[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\n\n# this allows you to use CircleCI's dynamic configuration feature\nsetup: true\n\n# the path-filtering orb is required to continue a pipeline based on\n# the path of an updated fileset\norbs:\n  path-filtering: circleci/path-filtering@0.1.2\n\nworkflows:\n  # the always-run workflow is always triggered, regardless of the pipeline parameters.\n  always-run:\n    jobs:\n      # the path-filtering/filter job determines which pipeline\n      # parameters to update.\n      - path-filtering/filter:\n          name: check-updated-files\n          # 3-column, whitespace-delimited mapping. One mapping per\n          # line:\n          # <regex path-to-test> <parameter-to-set> <value-of-pipeline-parameter>\n          mapping: |\n            mmdet/.* lint_only false\n            requirements/.* lint_only false\n            tests/.* lint_only false\n            tools/.* lint_only false\n            configs/.* lint_only false\n            .circleci/.* lint_only false\n          base-revision: dev-3.x\n          # this is the path of the configuration we should trigger once\n          # path filtering and pipeline parameter value updates are\n          # complete. In this case, we are using the parent dynamic\n          # configuration itself.\n          config-path: .circleci/test.yml\n"
  },
  {
    "path": ".circleci/docker/Dockerfile",
    "content": "ARG PYTORCH=\"1.8.1\"\nARG CUDA=\"10.2\"\nARG CUDNN=\"7\"\n\nFROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel\n\n# To fix GPG key error when running apt-get update\nRUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub\nRUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub\n\nRUN apt-get update && apt-get install -y ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx\n"
  },
  {
    "path": ".circleci/test.yml",
    "content": "version: 2.1\n\n# the default pipeline parameters, which will be updated according to\n# the results of the path-filtering orb\nparameters:\n  lint_only:\n    type: boolean\n    default: true\n\njobs:\n  lint:\n    docker:\n      - image: cimg/python:3.7.4\n    steps:\n      - checkout\n      - run:\n          name: Install pre-commit hook\n          command: |\n            pip install pre-commit\n            pre-commit install\n      - run:\n          name: Linting\n          command: pre-commit run --all-files\n      - run:\n          name: Check docstring coverage\n          command: |\n            pip install interrogate\n            interrogate -v --ignore-init-method --ignore-module --ignore-nested-functions --ignore-magic --ignore-regex \"__repr__\" --fail-under 85 mmdet\n\n  build_cpu:\n    parameters:\n      # The python version must match available image tags in\n      # https://circleci.com/developer/images/image/cimg/python\n      python:\n        type: string\n      torch:\n        type: string\n      torchvision:\n        type: string\n    docker:\n      - image: cimg/python:<< parameters.python >>\n    resource_class: large\n    steps:\n      - checkout\n      - run:\n          name: Install Libraries\n          command: |\n            sudo apt-get update\n            sudo apt-get install -y ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx libjpeg-dev zlib1g-dev libtinfo-dev libncurses5\n      - run:\n          name: Configure Python & pip\n          command: |\n            pip install --upgrade pip\n            pip install wheel\n      - run:\n          name: Install PyTorch\n          command: |\n            python -V\n            python -m pip install torch==<< parameters.torch >>+cpu torchvision==<< parameters.torchvision >>+cpu -f https://download.pytorch.org/whl/torch_stable.html\n      - when:\n          condition:\n            equal: [\"3.9.0\", << parameters.python >>]\n          steps:\n            - run: pip install \"protobuf <= 3.20.1\" && sudo apt-get update && sudo apt-get -y install libprotobuf-dev protobuf-compiler cmake\n            - run: pip install dsdl\n      - run:\n          name: Install mmdet dependencies\n          # numpy may be downgraded after building pycocotools, which causes `ImportError: numpy.core.multiarray failed to import`\n          # force reinstall pycocotools to ensure pycocotools being built under the currenct numpy\n          command: |\n            python -m pip install git+ssh://git@github.com/open-mmlab/mmengine.git@main\n            pip install -U openmim\n            mim install 'mmcv >= 2.0.0rc4'\n            pip install -r requirements/tests.txt -r requirements/optional.txt\n            pip install --force-reinstall pycocotools\n            pip install albumentations>=0.3.2 --no-binary imgaug,albumentations\n            pip install -r requirements/tracking.txt\n            pip install git+https://github.com/cocodataset/panopticapi.git\n            pip install git+https://github.com/JonathonLuiten/TrackEval.git\n      - run:\n          name: Build and install\n          command: |\n            pip install -e .\n      - run:\n          name: Run unittests\n          command: |\n            python -m coverage run --branch --source mmdet -m pytest tests/\n            python -m coverage xml\n            python -m coverage report -m\n\n  build_cuda:\n    parameters:\n      torch:\n        type: string\n      cuda:\n        type: enum\n        enum: [\"11.1\", \"11.7\", \"11.8\"]\n      cudnn:\n        type: integer\n        default: 8\n    machine:\n      image: linux-cuda-11:default\n      # docker_layer_caching: true\n    resource_class: gpu.nvidia.small.multi\n    steps:\n      - checkout\n      - run:\n          # CLoning repos in VM since Docker doesn't have access to the private key\n          name: Clone Repos\n          command: |\n            git clone -b main --depth 1 ssh://git@github.com/open-mmlab/mmengine.git /home/circleci/mmengine\n      - run:\n          name: Install nvidia-container-toolkit and Restart Docker\n          command: |\n            sudo apt-get update\n            sudo apt-get install -y nvidia-container-toolkit\n            sudo systemctl restart docker\n      - run:\n          name: Build Docker image\n          command: |\n            docker build .circleci/docker -t mmdetection:gpu --build-arg PYTORCH=<< parameters.torch >> --build-arg CUDA=<< parameters.cuda >> --build-arg CUDNN=<< parameters.cudnn >>\n            docker run --gpus all -t -d -v /home/circleci/project:/mmdetection -v /home/circleci/mmengine:/mmengine -w /mmdetection --name mmdetection mmdetection:gpu\n            docker exec mmdetection apt-get install -y git\n      - run:\n          name: Install mmdet dependencies\n          command: |\n            docker exec mmdetection pip install -e /mmengine\n            docker exec mmdetection pip install -U openmim\n            docker exec mmdetection mim install 'mmcv >= 2.0.0rc4'\n            docker exec mmdetection pip install -r requirements/tests.txt -r requirements/optional.txt\n            docker exec mmdetection pip install pycocotools\n            docker exec mmdetection pip install albumentations>=0.3.2 --no-binary imgaug,albumentations\n            docker exec mmdetection pip install -r requirements/tracking.txt\n            docker exec mmdetection pip install git+https://github.com/cocodataset/panopticapi.git\n            docker exec mmdetection pip install git+https://github.com/JonathonLuiten/TrackEval.git\n            docker exec mmdetection python -c 'import mmcv; print(mmcv.__version__)'\n      - run:\n          name: Build and install\n          command: |\n            docker exec mmdetection pip install -e .\n      - run:\n          name: Run unittests\n          command: |\n            docker exec mmdetection python -m pytest tests/\n\nworkflows:\n  pr_stage_lint:\n    when: << pipeline.parameters.lint_only >>\n    jobs:\n      - lint:\n          name: lint\n          filters:\n            branches:\n              ignore:\n                - dev-3.x\n  pr_stage_test:\n    when:\n      not: << pipeline.parameters.lint_only >>\n    jobs:\n      - lint:\n          name: lint\n          filters:\n            branches:\n              ignore:\n                - dev-3.x\n      - build_cpu:\n          name: minimum_version_cpu\n          torch: 1.8.0\n          torchvision: 0.9.0\n          python: 3.7.16\n          requires:\n            - lint\n      - build_cpu:\n          name: maximum_version_cpu\n          torch: 2.0.0\n          torchvision: 0.15.1\n          python: 3.9.0\n          requires:\n            - minimum_version_cpu\n      - hold:\n          type: approval\n          requires:\n            - maximum_version_cpu\n      - build_cuda:\n          name: mainstream_version_gpu\n          torch: 1.8.1\n          # Use double quotation mark to explicitly specify its type\n          # as string instead of number\n          cuda: \"11.1\"\n          requires:\n            - hold\n      - build_cuda:\n          name: maximum_version_gpu\n          torch: 2.0.0\n          cuda: \"11.7\"\n          cudnn: 8\n          requires:\n            - hold\n  merge_stage_test:\n    when:\n      not: << pipeline.parameters.lint_only >>\n    jobs:\n      - build_cuda:\n          name: minimum_version_gpu\n          torch: 1.8.0\n          cuda: \"11.1\"\n          filters:\n            branches:\n              only:\n                - dev-3.x\n"
  },
  {
    "path": ".dev_scripts/batch_test_list.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# missing wider_face/timm_example/strong_baselines/simple_copy_paste/\n# selfsup_pretrain/seesaw_loss/pascal_voc/openimages/lvis/ld/lad/cityscapes/deepfashion\n\n# yapf: disable\natss = dict(\n    config='configs/atss/atss_r50_fpn_1x_coco.py',\n    checkpoint='atss_r50_fpn_1x_coco_20200209-985f7bd0.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/atss/atss_r50_fpn_1x_coco/atss_r50_fpn_1x_coco_20200209-985f7bd0.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=39.4),\n)\nautoassign = dict(\n    config='configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py',\n    checkpoint='auto_assign_r50_fpn_1x_coco_20210413_115540-5e17991f.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/autoassign/auto_assign_r50_fpn_1x_coco/auto_assign_r50_fpn_1x_coco_20210413_115540-5e17991f.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.4),\n)\ncarafe = dict(\n    config='configs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py',\n    checkpoint='faster_rcnn_r50_fpn_carafe_1x_coco_bbox_mAP-0.386_20200504_175733-385a75b7.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/carafe/faster_rcnn_r50_fpn_carafe_1x_coco/faster_rcnn_r50_fpn_carafe_1x_coco_bbox_mAP-0.386_20200504_175733-385a75b7.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.6),\n)\ncascade_rcnn = [\n    dict(\n        config='configs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py',\n        checkpoint='cascade_rcnn_r50_fpn_1x_coco_20200316-3dc56deb.pth',\n        eval='bbox',\n        url='https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_fpn_1x_coco/cascade_rcnn_r50_fpn_1x_coco_20200316-3dc56deb.pth', # noqa\n        metric=dict(bbox_mAP=40.3),\n    ),\n    dict(\n        config='configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py',\n        checkpoint='cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth',\n        url='https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco/cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth', # noqa\n        eval=['bbox', 'segm'],\n        metric=dict(bbox_mAP=41.2, segm_mAP=35.9),\n    ),\n]\ncascade_rpn = dict(\n    config='configs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py', # noqa\n    checkpoint='crpn_faster_rcnn_r50_caffe_fpn_1x_coco-c8283cca.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/cascade_rpn/crpn_faster_rcnn_r50_caffe_fpn_1x_coco/crpn_faster_rcnn_r50_caffe_fpn_1x_coco-c8283cca.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.4),\n)\ncenternet = dict(\n    config='configs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py',\n    checkpoint='centernet_resnet18_dcnv2_140e_coco_20210702_155131-c8cd631f.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_dcnv2_140e_coco/centernet_resnet18_dcnv2_140e_coco_20210702_155131-c8cd631f.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=29.5),\n)\ncentripetalnet = dict(\n    config='configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py',  # noqa\n    checkpoint='centripetalnet_hourglass104_mstest_16x6_210e_coco_20200915_204804-3ccc61e5.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/centripetalnet/centripetalnet_hourglass104_mstest_16x6_210e_coco/centripetalnet_hourglass104_mstest_16x6_210e_coco_20200915_204804-3ccc61e5.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=44.7),\n)\nconvnext = dict(\n    config='configs/convnext/cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py', # noqa\n    checkpoint='cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220510_201004-3d24f5a4.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/convnext/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220510_201004-3d24f5a4.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=51.8, segm_mAP=44.8),\n)\ncornernet = dict(\n    config='configs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py',\n    checkpoint='cornernet_hourglass104_mstest_8x6_210e_coco_20200825_150618-79b44c30.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_8x6_210e_coco/cornernet_hourglass104_mstest_8x6_210e_coco_20200825_150618-79b44c30.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=41.2),\n)\ndcn = dict(\n    config='configs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py',\n    checkpoint='faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130-d68aed1e.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130-d68aed1e.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=41.3),\n)\ndcnv2 = dict(\n    config='configs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py',\n    checkpoint='faster_rcnn_r50_fpn_mdpool_1x_coco_20200307-c0df27ff.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdpool_1x_coco/faster_rcnn_r50_fpn_mdpool_1x_coco_20200307-c0df27ff.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.7),\n)\nddod = dict(\n    config='configs/ddod/ddod_r50_fpn_1x_coco.py',\n    checkpoint='ddod_r50_fpn_1x_coco_20220523_223737-29b2fc67.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/ddod/ddod_r50_fpn_1x_coco/ddod_r50_fpn_1x_coco_20220523_223737-29b2fc67.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=41.7),\n)\ndeformable_detr = dict(\n    config='configs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py',\n    checkpoint='deformable_detr_r50_16x2_50e_coco_20210419_220030-a12b9512.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/deformable_detr/deformable_detr_r50_16x2_50e_coco/deformable_detr_r50_16x2_50e_coco_20210419_220030-a12b9512.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=44.5),\n)\ndetectors = dict(\n    config='configs/detectors/detectors_htc-r50_1x_coco.py',\n    checkpoint='detectors_htc_r50_1x_coco-329b1453.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_htc_r50_1x_coco/detectors_htc_r50_1x_coco-329b1453.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=49.1, segm_mAP=42.6),\n)\ndetr = dict(\n    config='configs/detr/detr_r50_8xb2-150e_coco.py',\n    checkpoint='detr_r50_8x2_150e_coco_20201130_194835-2c4b8974.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/detr/detr_r50_8x2_150e_coco/detr_r50_8x2_150e_coco_20201130_194835-2c4b8974.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.1),\n)\ndouble_heads = dict(\n    config='configs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py',\n    checkpoint='dh_faster_rcnn_r50_fpn_1x_coco_20200130-586b67df.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/double_heads/dh_faster_rcnn_r50_fpn_1x_coco/dh_faster_rcnn_r50_fpn_1x_coco_20200130-586b67df.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.0),\n)\ndyhead = dict(\n    config='configs/dyhead/atss_r50_fpn_dyhead_1x_coco.py',\n    checkpoint='atss_r50_fpn_dyhead_4x4_1x_coco_20211219_023314-eaa620c6.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_r50_fpn_dyhead_4x4_1x_coco/atss_r50_fpn_dyhead_4x4_1x_coco_20211219_023314-eaa620c6.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=43.3),\n)\ndynamic_rcnn = dict(\n    config='configs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py',\n    checkpoint='dynamic_rcnn_r50_fpn_1x-62a3f276.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/dynamic_rcnn/dynamic_rcnn_r50_fpn_1x/dynamic_rcnn_r50_fpn_1x-62a3f276.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.9),\n)\nefficientnet = dict(\n    config='configs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py',\n    checkpoint='retinanet_effb3_fpn_crop896_8x4_1x_coco_20220322_234806-615a0dda.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/efficientnet/retinanet_effb3_fpn_crop896_8x4_1x_coco/retinanet_effb3_fpn_crop896_8x4_1x_coco_20220322_234806-615a0dda.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.5),\n)\nempirical_attention = dict(\n    config='configs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py',  # noqa\n    checkpoint='faster_rcnn_r50_fpn_attention_1111_1x_coco_20200130-403cccba.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_1111_1x_coco/faster_rcnn_r50_fpn_attention_1111_1x_coco_20200130-403cccba.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.0),\n)\nfaster_rcnn = dict(\n    config='configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py',\n    checkpoint='faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.4),\n)\nfcos = dict(\n    config='configs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py',  # noqa\n    checkpoint='fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco-0a0d75a8.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco-0a0d75a8.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.7),\n)\nfoveabox = dict(\n    config='configs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py',\n    checkpoint='fovea_align_r50_fpn_gn-head_4x4_2x_coco_20200203-8987880d.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r50_fpn_gn-head_4x4_2x_coco/fovea_align_r50_fpn_gn-head_4x4_2x_coco_20200203-8987880d.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.9),\n)\nfpg = dict(\n    config='configs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py',\n    checkpoint='mask_rcnn_r50_fpg_crop640_50e_coco_20220311_011857-233b8334.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpg_crop640_50e_coco/mask_rcnn_r50_fpg_crop640_50e_coco_20220311_011857-233b8334.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=43.0, segm_mAP=38.1),\n)\nfree_anchor = dict(\n    config='configs/free_anchor/freeanchor_r50_fpn_1x_coco.py',\n    checkpoint='retinanet_free_anchor_r50_fpn_1x_coco_20200130-0f67375f.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_r50_fpn_1x_coco/retinanet_free_anchor_r50_fpn_1x_coco_20200130-0f67375f.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.7),\n)\nfsaf = dict(\n    config='configs/fsaf/fsaf_r50_fpn_1x_coco.py',\n    checkpoint='fsaf_r50_fpn_1x_coco-94ccc51f.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_r50_fpn_1x_coco/fsaf_r50_fpn_1x_coco-94ccc51f.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.4),\n)\ngcnet = dict(\n    config='configs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py',  # noqa\n    checkpoint='mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200202-587b99aa.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200202-587b99aa.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=40.4, segm_mAP=36.2),\n)\ngfl = dict(\n    config='configs/gfl/gfl_r50_fpn_1x_coco.py',\n    checkpoint='gfl_r50_fpn_1x_coco_20200629_121244-25944287.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r50_fpn_1x_coco/gfl_r50_fpn_1x_coco_20200629_121244-25944287.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.2),\n)\nghm = dict(\n    config='configs/ghm/retinanet_r50_fpn_ghm-1x_coco.py',\n    checkpoint='retinanet_ghm_r50_fpn_1x_coco_20200130-a437fda3.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_r50_fpn_1x_coco/retinanet_ghm_r50_fpn_1x_coco_20200130-a437fda3.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.0),\n)\ngn = dict(\n    config='configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py',\n    checkpoint='mask_rcnn_r50_fpn_gn-all_2x_coco_20200206-8eee02a6.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_2x_coco/mask_rcnn_r50_fpn_gn-all_2x_coco_20200206-8eee02a6.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=40.1, segm_mAP=36.4),\n)\ngn_ws = dict(\n    config='configs/gn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py',\n    checkpoint='faster_rcnn_r50_fpn_gn_ws-all_1x_coco_20200130-613d9fe2.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_r50_fpn_gn_ws-all_1x_coco/faster_rcnn_r50_fpn_gn_ws-all_1x_coco_20200130-613d9fe2.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=39.7),\n)\ngrid_rcnn = dict(\n    config='configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py',\n    checkpoint='grid_rcnn_r50_fpn_gn-head_2x_coco_20200130-6cca8223.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_r50_fpn_gn-head_2x_coco/grid_rcnn_r50_fpn_gn-head_2x_coco_20200130-6cca8223.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.4),\n)\ngroie = dict(\n    config='configs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py',\n    checkpoint='faster_rcnn_r50_fpn_groie_1x_coco_20200604_211715-66ee9516.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/groie/faster_rcnn_r50_fpn_groie_1x_coco/faster_rcnn_r50_fpn_groie_1x_coco_20200604_211715-66ee9516.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.3),\n)\nguided_anchoring = dict(\n        config='configs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py',  # noqa\n        checkpoint='ga_retinanet_r50_caffe_fpn_1x_coco_20201020-39581c6f.pth',\n        url='https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_r50_caffe_fpn_1x_coco/ga_retinanet_r50_caffe_fpn_1x_coco_20201020-39581c6f.pth', # noqa\n        eval='bbox',\n        metric=dict(bbox_mAP=36.9),\n    )\nhrnet = dict(\n    config='configs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py',\n    checkpoint='faster_rcnn_hrnetv2p_w18_1x_coco_20200130-56651a6d.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w18_1x_coco/faster_rcnn_hrnetv2p_w18_1x_coco_20200130-56651a6d.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=36.9),\n)\nhtc = dict(\n    config='configs/htc/htc_r50_fpn_1x_coco.py',\n    checkpoint='htc_r50_fpn_1x_coco_20200317-7332cf16.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r50_fpn_1x_coco/htc_r50_fpn_1x_coco_20200317-7332cf16.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=42.3, segm_mAP=37.4),\n)\ninstaboost = dict(\n    config='configs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py',\n    checkpoint='mask_rcnn_r50_fpn_instaboost_4x_coco_20200307-d025f83a.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_r50_fpn_instaboost_4x_coco/mask_rcnn_r50_fpn_instaboost_4x_coco_20200307-d025f83a.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=40.6, segm_mAP=36.6),\n)\nlibra_rcnn = dict(\n    config='configs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py',\n    checkpoint='libra_faster_rcnn_r50_fpn_1x_coco_20200130-3afee3a9.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_r50_fpn_1x_coco/libra_faster_rcnn_r50_fpn_1x_coco_20200130-3afee3a9.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.3),\n)\nmask2former = dict(\n    config='configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py',\n    checkpoint='mask2former_r50_lsj_8x2_50e_coco-panoptic_20220326_224516-11a44721.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_r50_lsj_8x2_50e_coco-panoptic/mask2former_r50_lsj_8x2_50e_coco-panoptic_20220326_224516-11a44721.pth', # noqa\n    eval=['bbox', 'segm', 'PQ'],\n    metric=dict(PQ=51.9, bbox_mAP=44.8, segm_mAP=41.9),\n)\nmask_rcnn = dict(\n    config='configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py',\n    checkpoint='mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=38.2, segm_mAP=34.7),\n)\nmaskformer = dict(\n    config='configs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py',\n    checkpoint='maskformer_r50_mstrain_16x1_75e_coco_20220221_141956-bc2699cb.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/maskformer/maskformer_r50_mstrain_16x1_75e_coco/maskformer_r50_mstrain_16x1_75e_coco_20220221_141956-bc2699cb.pth', # noqa\n    eval='PQ',\n    metric=dict(PQ=46.9),\n)\nms_rcnn = dict(\n    config='configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py',\n    checkpoint='ms_rcnn_r50_caffe_fpn_1x_coco_20200702_180848-61c9355e.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r50_caffe_fpn_1x_coco/ms_rcnn_r50_caffe_fpn_1x_coco_20200702_180848-61c9355e.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=38.2, segm_mAP=36.0),\n)\nnas_fcos = dict(\n    config='configs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py',  # noqa\n    checkpoint='nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200520-1bdba3ce.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/nas_fcos/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200520-1bdba3ce.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=39.4),\n)\nnas_fpn = dict(\n    config='configs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py',\n    checkpoint='retinanet_r50_nasfpn_crop640_50e_coco-0ad1f644.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/nas_fpn/retinanet_r50_nasfpn_crop640_50e_coco/retinanet_r50_nasfpn_crop640_50e_coco-0ad1f644.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.5),\n)\npaa = dict(\n    config='configs/paa/paa_r50_fpn_1x_coco.py',\n    checkpoint='paa_r50_fpn_1x_coco_20200821-936edec3.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1x_coco/paa_r50_fpn_1x_coco_20200821-936edec3.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.4),\n)\npafpn = dict(\n    config='configs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py',\n    checkpoint='faster_rcnn_r50_pafpn_1x_coco_bbox_mAP-0.375_20200503_105836-b7b4b9bd.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/pafpn/faster_rcnn_r50_pafpn_1x_coco/faster_rcnn_r50_pafpn_1x_coco_bbox_mAP-0.375_20200503_105836-b7b4b9bd.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.5),\n)\npanoptic_fpn = dict(\n    config='configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py',\n    checkpoint='panoptic_fpn_r50_fpn_1x_coco_20210821_101153-9668fd13.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r50_fpn_1x_coco/panoptic_fpn_r50_fpn_1x_coco_20210821_101153-9668fd13.pth', # noqa\n    eval='PQ',\n    metric=dict(PQ=40.2),\n)\npisa = dict(\n    config='configs/pisa/faster-rcnn_r50_fpn_pisa_1x_coco.py',\n    checkpoint='pisa_faster_rcnn_r50_fpn_1x_coco-dea93523.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_faster_rcnn_r50_fpn_1x_coco/pisa_faster_rcnn_r50_fpn_1x_coco-dea93523.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=38.4),\n)\npoint_rend = dict(\n    config='configs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py',\n    checkpoint='point_rend_r50_caffe_fpn_mstrain_1x_coco-1bcb5fb4.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/point_rend/point_rend_r50_caffe_fpn_mstrain_1x_coco/point_rend_r50_caffe_fpn_mstrain_1x_coco-1bcb5fb4.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=38.4, segm_mAP=36.3),\n)\npvt = dict(\n    config='configs/pvt/retinanet_pvt-s_fpn_1x_coco.py',\n    checkpoint='retinanet_pvt-s_fpn_1x_coco_20210906_142921-b6c94a5b.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-s_fpn_1x_coco/retinanet_pvt-s_fpn_1x_coco_20210906_142921-b6c94a5b.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=40.4),\n)\nqueryinst = dict(\n    config='configs/queryinst/queryinst_r50_fpn_1x_coco.py',\n    checkpoint='queryinst_r50_fpn_1x_coco_20210907_084916-5a8f1998.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_1x_coco/queryinst_r50_fpn_1x_coco_20210907_084916-5a8f1998.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=42.0, segm_mAP=37.5),\n)\nregnet = dict(\n    config='configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py',\n    checkpoint='mask_rcnn_regnetx-3.2GF_fpn_1x_coco_20200520_163141-2a9d1814.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_1x_coco/mask_rcnn_regnetx-3.2GF_fpn_1x_coco_20200520_163141-2a9d1814.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=40.4, segm_mAP=36.7),\n)\nreppoints = dict(\n    config='configs/reppoints/reppoints-moment_r50_fpn_1x_coco.py',\n    checkpoint='reppoints_moment_r50_fpn_1x_coco_20200330-b73db8d1.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_1x_coco/reppoints_moment_r50_fpn_1x_coco_20200330-b73db8d1.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.0),\n)\nres2net = dict(\n    config='configs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py',\n    checkpoint='faster_rcnn_r2_101_fpn_2x_coco-175f1da6.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/res2net/faster_rcnn_r2_101_fpn_2x_coco/faster_rcnn_r2_101_fpn_2x_coco-175f1da6.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=43.0),\n)\nresnest = dict(\n    config='configs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py',  # noqa\n    checkpoint='faster_rcnn_s50_fpn_syncbn-backbone+head_mstrain-range_1x_coco_20200926_125502-20289c16.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/resnest/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20200926_125502-20289c16.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=42.0),\n)\nresnet_strikes_back = dict(\n    config='configs/resnet_strikes_back/mask-rcnn_r50-rsb-pre_fpn_1x_coco.py', # noqa\n    checkpoint='mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_174054-06ce8ba0.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_174054-06ce8ba0.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=41.2, segm_mAP=38.2),\n)\nretinanet = dict(\n    config='configs/retinanet/retinanet_r50_fpn_1x_coco.py',\n    checkpoint='retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=36.5),\n)\nrpn = dict(\n    config='configs/rpn/rpn_r50_fpn_1x_coco.py',\n    checkpoint='rpn_r50_fpn_1x_coco_20200218-5525fa2e.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_fpn_1x_coco/rpn_r50_fpn_1x_coco_20200218-5525fa2e.pth', # noqa\n    eval='proposal_fast',\n    metric=dict(AR_1000=58.2),\n)\nsabl = [\n    dict(\n        config='configs/sabl/sabl-retinanet_r50_fpn_1x_coco.py',\n        checkpoint='sabl_retinanet_r50_fpn_1x_coco-6c54fd4f.pth',\n        url='https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r50_fpn_1x_coco/sabl_retinanet_r50_fpn_1x_coco-6c54fd4f.pth', # noqa\n        eval='bbox',\n        metric=dict(bbox_mAP=37.7),\n    ),\n    dict(\n        config='configs/sabl/sabl-faster-rcnn_r50_fpn_1x_coco.py',\n        checkpoint='sabl_faster_rcnn_r50_fpn_1x_coco-e867595b.pth',\n        url='https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_faster_rcnn_r50_fpn_1x_coco/sabl_faster_rcnn_r50_fpn_1x_coco-e867595b.pth', # noqa\n        eval='bbox',\n        metric=dict(bbox_mAP=39.9),\n    ),\n]\nscnet = dict(\n    config='configs/scnet/scnet_r50_fpn_1x_coco.py',\n    checkpoint='scnet_r50_fpn_1x_coco-c3f09857.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r50_fpn_1x_coco/scnet_r50_fpn_1x_coco-c3f09857.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=43.5),\n)\nscratch = dict(\n    config='configs/scratch/mask-rcnn_r50-scratch_fpn_gn-all_6x_coco.py',\n    checkpoint='scratch_mask_rcnn_r50_fpn_gn_6x_bbox_mAP-0.412__segm_mAP-0.374_20200201_193051-1e190a40.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/scratch/mask_rcnn_r50_fpn_gn-all_scratch_6x_coco/scratch_mask_rcnn_r50_fpn_gn_6x_bbox_mAP-0.412__segm_mAP-0.374_20200201_193051-1e190a40.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=41.2, segm_mAP=37.4),\n)\nsolo = dict(\n    config='configs/solo/decoupled-solo_r50_fpn_1x_coco.py',\n    checkpoint='decoupled_solo_r50_fpn_1x_coco_20210820_233348-6337c589.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_r50_fpn_1x_coco/decoupled_solo_r50_fpn_1x_coco_20210820_233348-6337c589.pth', # noqa\n    eval='segm',\n    metric=dict(segm_mAP=33.9),\n)\nsolov2 = dict(\n    config='configs/solov2/solov2_r50_fpn_1x_coco.py',\n    checkpoint='solov2_r50_fpn_1x_coco_20220512_125858-a357fa23.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r50_fpn_1x_coco/solov2_r50_fpn_1x_coco_20220512_125858-a357fa23.pth', # noqa\n    eval='segm',\n    metric=dict(segm_mAP=34.8),\n)\nsparse_rcnn = dict(\n    config='configs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py',\n    checkpoint='sparse_rcnn_r50_fpn_1x_coco_20201222_214453-dc79b137.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_1x_coco/sparse_rcnn_r50_fpn_1x_coco_20201222_214453-dc79b137.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.9),\n)\nssd = [\n    dict(\n        config='configs/ssd/ssd300_coco.py',\n        checkpoint='ssd300_coco_20210803_015428-d231a06e.pth',\n        url='https://download.openmmlab.com/mmdetection/v2.0/ssd/ssd300_coco/ssd300_coco_20210803_015428-d231a06e.pth', # noqa\n        eval='bbox',\n        metric=dict(bbox_mAP=25.5),\n    ),\n    dict(\n        config='configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py',\n        checkpoint='ssdlite_mobilenetv2_scratch_600e_coco_20210629_110627-974d9307.pth',  # noqa\n        url='https://download.openmmlab.com/mmdetection/v2.0/ssd/ssdlite_mobilenetv2_scratch_600e_coco/ssdlite_mobilenetv2_scratch_600e_coco_20210629_110627-974d9307.pth', # noqa\n        eval='bbox',\n        metric=dict(bbox_mAP=21.3),\n    ),\n]\nswin = dict(\n    config='configs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py',\n    checkpoint='mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth', # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=42.7, segm_mAP=39.3),\n)\ntood = dict(\n    config='configs/tood/tood_r50_fpn_1x_coco.py',\n    checkpoint='tood_r50_fpn_1x_coco_20211210_103425-20e20746.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_1x_coco/tood_r50_fpn_1x_coco_20211210_103425-20e20746.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=42.4),\n)\ntridentnet = dict(\n    config='configs/tridentnet/tridentnet_r50-caffe_1x_coco.py',\n    checkpoint='tridentnet_r50_caffe_1x_coco_20201230_141838-2ec0b530.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_1x_coco/tridentnet_r50_caffe_1x_coco_20201230_141838-2ec0b530.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.6),\n)\nvfnet = dict(\n    config='configs/vfnet/vfnet_r50_fpn_1x_coco.py',\n    checkpoint='vfnet_r50_fpn_1x_coco_20201027-38db6f58.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_1x_coco/vfnet_r50_fpn_1x_coco_20201027-38db6f58.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=41.6),\n)\nyolact = dict(\n    config='configs/yolact/yolact_r50_1xb8-55e_coco.py',\n    checkpoint='yolact_r50_1x8_coco_20200908-f38d58df.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/yolact/yolact_r50_1x8_coco/yolact_r50_1x8_coco_20200908-f38d58df.pth', # noqa\n    eval=['bbox', 'segm'],\n    metric=dict(bbox_mAP=31.2, segm_mAP=29.0),\n)\nyolo = dict(\n    config='configs/yolo/yolov3_d53_8xb8-320-273e_coco.py',\n    checkpoint='yolov3_d53_320_273e_coco-421362b6.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_320_273e_coco/yolov3_d53_320_273e_coco-421362b6.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=27.9),\n)\nyolof = dict(\n    config='configs/yolof/yolof_r50-c5_8xb8-1x_coco.py',\n    checkpoint='yolof_r50_c5_8x8_1x_coco_20210425_024427-8e864411.pth',\n    url='https://download.openmmlab.com/mmdetection/v2.0/yolof/yolof_r50_c5_8x8_1x_coco/yolof_r50_c5_8x8_1x_coco_20210425_024427-8e864411.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=37.5),\n)\nyolox = dict(\n    config='configs/yolox/yolox_tiny_8xb8-300e_coco.py',\n    checkpoint='yolox_tiny_8x8_300e_coco_20211124_171234-b4047906.pth',  # noqa\n    url='https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_tiny_8x8_300e_coco/yolox_tiny_8x8_300e_coco_20211124_171234-b4047906.pth', # noqa\n    eval='bbox',\n    metric=dict(bbox_mAP=31.8),\n)\n# yapf: enable\n"
  },
  {
    "path": ".dev_scripts/batch_train_list.txt",
    "content": "configs/albu_example/mask-rcnn_r50_fpn_albu_1x_coco.py\nconfigs/atss/atss_r50_fpn_1x_coco.py\nconfigs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py\nconfigs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py\nconfigs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py\nconfigs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py\nconfigs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py\nconfigs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py\nconfigs/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco.py\nconfigs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py\nconfigs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py\nconfigs/convnext/mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco.py\nconfigs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py\nconfigs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py\nconfigs/ddod/ddod_r50_fpn_1x_coco.py\nconfigs/detectors/detectors_htc-r50_1x_coco.py\nconfigs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py\nconfigs/detr/detr_r50_8xb2-150e_coco.py\nconfigs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py\nconfigs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py\nconfigs/dyhead/atss_r50_fpn_dyhead_1x_coco.py\nconfigs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py\nconfigs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py\nconfigs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py\nconfigs/faster_rcnn/faster-rcnn_r50-caffe-dc5_ms-1x_coco.py\nconfigs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py\nconfigs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py\nconfigs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py\nconfigs/free_anchor/freeanchor_r50_fpn_1x_coco.py\nconfigs/fsaf/fsaf_r50_fpn_1x_coco.py\nconfigs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py\nconfigs/gfl/gfl_r50_fpn_1x_coco.py\nconfigs/ghm/retinanet_r50_fpn_ghm-1x_coco.py\nconfigs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py\nconfigs/gn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py\nconfigs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py\nconfigs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py\nconfigs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py\nconfigs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py\nconfigs/htc/htc_r50_fpn_1x_coco.py\nconfigs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py\nconfigs/lad/lad_r50-paa-r101_fpn_2xb8_coco_1x.py\nconfigs/ld/ld_r18-gflv1-r101_fpn_1x_coco.py\nconfigs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py\nconfigs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py\nconfigs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py\nconfigs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py\nconfigs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py\nconfigs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py\nconfigs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py\nconfigs/paa/paa_r50_fpn_1x_coco.py\nconfigs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py\nconfigs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py\nconfigs/pisa/mask-rcnn_r50_fpn_pisa_1x_coco.py\nconfigs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py\nconfigs/pvt/retinanet_pvt-t_fpn_1x_coco.py\nconfigs/queryinst/queryinst_r50_fpn_1x_coco.py\nconfigs/regnet/retinanet_regnetx-800MF_fpn_1x_coco.py\nconfigs/reppoints/reppoints-moment_r50_fpn_1x_coco.py\nconfigs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py\nconfigs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py\nconfigs/resnet_strikes_back/retinanet_r50-rsb-pre_fpn_1x_coco.py\nconfigs/retinanet/retinanet_r50-caffe_fpn_1x_coco.py\nconfigs/rpn/rpn_r50_fpn_1x_coco.py\nconfigs/sabl/sabl-retinanet_r50_fpn_1x_coco.py\nconfigs/scnet/scnet_r50_fpn_1x_coco.py\nconfigs/scratch/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py\nconfigs/solo/solo_r50_fpn_1x_coco.py\nconfigs/solov2/solov2_r50_fpn_1x_coco.py\nconfigs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py\nconfigs/ssd/ssd300_coco.py\nconfigs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py\nconfigs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py\nconfigs/tood/tood_r50_fpn_1x_coco.py\n'configs/tridentnet/tridentnet_r50-caffe_1x_coco.py\nconfigs/vfnet/vfnet_r50_fpn_1x_coco.py\nconfigs/yolact/yolact_r50_8xb8-55e_coco.py\nconfigs/yolo/yolov3_d53_8xb8-320-273e_coco.py\nconfigs/yolof/yolof_r50-c5_8xb8-1x_coco.py\nconfigs/yolox/yolox_tiny_8xb8-300e_coco.py\n"
  },
  {
    "path": ".dev_scripts/benchmark_filter.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Filter configs to train')\n    parser.add_argument(\n        '--basic-arch',\n        action='store_true',\n        help='to train models in basic arch')\n    parser.add_argument(\n        '--datasets', action='store_true', help='to train models in dataset')\n    parser.add_argument(\n        '--data-pipeline',\n        action='store_true',\n        help='to train models related to data pipeline, e.g. augmentations')\n    parser.add_argument(\n        '--nn-module',\n        action='store_true',\n        help='to train models related to neural network modules')\n    parser.add_argument(\n        '--model-options',\n        nargs='+',\n        help='custom options to special model benchmark')\n    parser.add_argument(\n        '--out',\n        type=str,\n        default='batch_train_list.txt',\n        help='output path of gathered metrics to be stored')\n    args = parser.parse_args()\n    return args\n\n\nbasic_arch_root = [\n    'atss', 'autoassign', 'cascade_rcnn', 'cascade_rpn', 'centripetalnet',\n    'cornernet', 'detectors', 'deformable_detr', 'detr', 'double_heads',\n    'dynamic_rcnn', 'faster_rcnn', 'fcos', 'foveabox', 'fp16', 'free_anchor',\n    'fsaf', 'gfl', 'ghm', 'grid_rcnn', 'guided_anchoring', 'htc', 'ld',\n    'libra_rcnn', 'mask_rcnn', 'ms_rcnn', 'nas_fcos', 'paa', 'pisa',\n    'point_rend', 'reppoints', 'retinanet', 'rpn', 'sabl', 'ssd', 'tridentnet',\n    'vfnet', 'yolact', 'yolo', 'sparse_rcnn', 'scnet', 'yolof', 'centernet'\n]\n\ndatasets_root = [\n    'wider_face', 'pascal_voc', 'cityscapes', 'lvis', 'deepfashion'\n]\n\ndata_pipeline_root = ['albu_example', 'instaboost']\n\nnn_module_root = [\n    'carafe', 'dcn', 'empirical_attention', 'gcnet', 'gn', 'gn+ws', 'hrnet',\n    'pafpn', 'nas_fpn', 'regnet', 'resnest', 'res2net', 'groie'\n]\n\nbenchmark_pool = [\n    'configs/albu_example/mask_rcnn_r50_fpn_albu_1x_coco.py',\n    'configs/atss/atss_r50_fpn_1x_coco.py',\n    'configs/autoassign/autoassign_r50_fpn_8x2_1x_coco.py',\n    'configs/carafe/mask_rcnn_r50_fpn_carafe_1x_coco.py',\n    'configs/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco.py',\n    'configs/cascade_rpn/crpn_faster_rcnn_r50_caffe_fpn_1x_coco.py',\n    'configs/centernet/centernet_resnet18_dcnv2_140e_coco.py',\n    'configs/centripetalnet/'\n    'centripetalnet_hourglass104_mstest_16x6_210e_coco.py',\n    'configs/cityscapes/mask_rcnn_r50_fpn_1x_cityscapes.py',\n    'configs/cornernet/'\n    'cornernet_hourglass104_mstest_8x6_210e_coco.py',\n    'configs/dcn/mask_rcnn_r50_fpn_mdconv_c3-c5_1x_coco.py',\n    'configs/dcn/faster_rcnn_r50_fpn_dpool_1x_coco.py',\n    'configs/dcn/faster_rcnn_r50_fpn_mdpool_1x_coco.py',\n    'configs/dcn/mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco.py',\n    'configs/deformable_detr/deformable_detr_r50_16x2_50e_coco.py',\n    'configs/detectors/detectors_htc_r50_1x_coco.py',\n    'configs/detr/detr_r50_8x2_150e_coco.py',\n    'configs/double_heads/dh_faster_rcnn_r50_fpn_1x_coco.py',\n    'configs/dynamic_rcnn/dynamic_rcnn_r50_fpn_1x_coco.py',\n    'configs/empirical_attention/faster_rcnn_r50_fpn_attention_1111_dcn_1x_coco.py',  # noqa\n    'configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py',\n    'configs/faster_rcnn/faster_rcnn_r50_fpn_ohem_1x_coco.py',\n    'configs/faster_rcnn/faster_rcnn_r50_caffe_fpn_1x_coco.py',\n    'configs/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco.py',\n    'configs/faster_rcnn/faster_rcnn_r50_caffe_dc5_mstrain_1x_coco.py',\n    'configs/fcos/fcos_center_r50_caffe_fpn_gn-head_4x4_1x_coco.py',\n    'configs/foveabox/fovea_align_r50_fpn_gn-head_4x4_2x_coco.py',\n    'configs/retinanet/retinanet_r50_fpn_fp16_1x_coco.py',\n    'configs/mask_rcnn/mask_rcnn_r50_fpn_fp16_1x_coco.py',\n    'configs/free_anchor/retinanet_free_anchor_r50_fpn_1x_coco.py',\n    'configs/fsaf/fsaf_r50_fpn_1x_coco.py',\n    'configs/gcnet/mask_rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco.py',\n    'configs/gfl/gfl_r50_fpn_1x_coco.py',\n    'configs/ghm/retinanet_ghm_r50_fpn_1x_coco.py',\n    'configs/gn/mask_rcnn_r50_fpn_gn-all_2x_coco.py',\n    'configs/gn+ws/mask_rcnn_r50_fpn_gn_ws-all_2x_coco.py',\n    'configs/grid_rcnn/grid_rcnn_r50_fpn_gn-head_2x_coco.py',\n    'configs/groie/faster_rcnn_r50_fpn_groie_1x_coco.py',\n    'configs/guided_anchoring/ga_faster_r50_caffe_fpn_1x_coco.py',\n    'configs/hrnet/mask_rcnn_hrnetv2p_w18_1x_coco.py',\n    'configs/htc/htc_r50_fpn_1x_coco.py',\n    'configs/instaboost/mask_rcnn_r50_fpn_instaboost_4x_coco.py',\n    'configs/ld/ld_r18_gflv1_r101_fpn_coco_1x.py',\n    'configs/libra_rcnn/libra_faster_rcnn_r50_fpn_1x_coco.py',\n    'configs/lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_1x_lvis_v1.py',\n    'configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco.py',\n    'configs/ms_rcnn/ms_rcnn_r50_caffe_fpn_1x_coco.py',\n    'configs/nas_fcos/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco.py',\n    'configs/nas_fpn/retinanet_r50_nasfpn_crop640_50e_coco.py',\n    'configs/paa/paa_r50_fpn_1x_coco.py',\n    'configs/pafpn/faster_rcnn_r50_pafpn_1x_coco.py',\n    'configs/pisa/pisa_mask_rcnn_r50_fpn_1x_coco.py',\n    'configs/point_rend/point_rend_r50_caffe_fpn_mstrain_1x_coco.py',\n    'configs/regnet/mask_rcnn_regnetx-3.2GF_fpn_1x_coco.py',\n    'configs/reppoints/reppoints_moment_r50_fpn_gn-neck+head_1x_coco.py',\n    'configs/res2net/faster_rcnn_r2_101_fpn_2x_coco.py',\n    'configs/resnest/'\n    'mask_rcnn_s50_fpn_syncbn-backbone+head_mstrain_1x_coco.py',\n    'configs/retinanet/retinanet_r50_caffe_fpn_1x_coco.py',\n    'configs/rpn/rpn_r50_fpn_1x_coco.py',\n    'configs/sabl/sabl_retinanet_r50_fpn_1x_coco.py',\n    'configs/ssd/ssd300_coco.py',\n    'configs/tridentnet/tridentnet_r50_caffe_1x_coco.py',\n    'configs/vfnet/vfnet_r50_fpn_1x_coco.py',\n    'configs/yolact/yolact_r50_1x8_coco.py',\n    'configs/yolo/yolov3_d53_320_273e_coco.py',\n    'configs/sparse_rcnn/sparse_rcnn_r50_fpn_1x_coco.py',\n    'configs/scnet/scnet_r50_fpn_1x_coco.py',\n    'configs/yolof/yolof_r50_c5_8x8_1x_coco.py',\n]\n\n\ndef main():\n    args = parse_args()\n\n    benchmark_type = []\n    if args.basic_arch:\n        benchmark_type += basic_arch_root\n    if args.datasets:\n        benchmark_type += datasets_root\n    if args.data_pipeline:\n        benchmark_type += data_pipeline_root\n    if args.nn_module:\n        benchmark_type += nn_module_root\n\n    special_model = args.model_options\n    if special_model is not None:\n        benchmark_type += special_model\n\n    config_dpath = 'configs/'\n    benchmark_configs = []\n    for cfg_root in benchmark_type:\n        cfg_dir = osp.join(config_dpath, cfg_root)\n        configs = os.scandir(cfg_dir)\n        for cfg in configs:\n            config_path = osp.join(cfg_dir, cfg.name)\n            if (config_path in benchmark_pool\n                    and config_path not in benchmark_configs):\n                benchmark_configs.append(config_path)\n\n    print(f'Totally found {len(benchmark_configs)} configs to benchmark')\n    with open(args.out, 'w') as f:\n        for config in benchmark_configs:\n            f.write(config + '\\n')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".dev_scripts/benchmark_full_models.txt",
    "content": "albu_example/mask-rcnn_r50_fpn_albu_1x_coco.py\natss/atss_r50_fpn_1x_coco.py\nautoassign/autoassign_r50-caffe_fpn_1x_coco.py\nboxinst/boxinst_r50_fpn_ms-90k_coco.py\ncarafe/faster-rcnn_r50_fpn-carafe_1x_coco.py\ncascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py\ncascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py\ncascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py\ncenternet/centernet-update_r50-caffe_fpn_ms-1x_coco.py\ncentripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py\ncondinst/condinst_r50_fpn_ms-poly-90k_coco_instance.py\nconditional_detr/conditional-detr_r50_8xb2-50e_coco.py\nconvnext/mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco.py\ncornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py\ndab_detr/dab-detr_r50_8xb2-50e_coco.py\ndcn/mask-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py\ndcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py\nddod/ddod_r50_fpn_1x_coco.py\ndeformable_detr/deformable-detr_r50_16xb2-50e_coco.py\ndetectors/detectors_htc-r50_1x_coco.py\ndetr/detr_r50_8xb2-150e_coco.py\ndino/dino-4scale_r50_8xb2-12e_coco.py\ndouble_heads/dh-faster-rcnn_r50_fpn_1x_coco.py\ndyhead/atss_r50_fpn_dyhead_1x_coco.py\ndynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py\nefficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py\nempirical_attention/faster-rcnn_r50-attn0010-dcn_fpn_1x_coco.py\nfaster_rcnn/faster-rcnn_r50_fpn_1x_coco.py\nfcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py\nfoveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py\nfpg/retinanet_r50_fpg_crop640_50e_coco.py\nfree_anchor/freeanchor_r50_fpn_1x_coco.py\nfsaf/fsaf_r50_fpn_1x_coco.py\ngcnet/mask-rcnn_r50-gcb-r4-c3-c5_fpn_1x_coco.py\ngfl/gfl_r50_fpn_1x_coco.py\nglip/glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py\nghm/retinanet_r50_fpn_ghm-1x_coco.py\ngn/mask-rcnn_r50_fpn_gn-all_2x_coco.py\ngn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py\ngrid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py\ngroie/faste-rcnn_r50_fpn_groie_1x_coco.py\nguided_anchoring/ga-faster-rcnn_r50-caffe_fpn_1x_coco.py\nhrnet/htc_hrnetv2p-w18_20e_coco.py\nhtc/htc_r50_fpn_1x_coco.py\ninstaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py\nlad/lad_r50-paa-r101_fpn_2xb8_coco_1x.py\nld/ld_r18-gflv1-r101_fpn_1x_coco.py\nlibra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py\nlvis/mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py\nmask2former/mask2former_r50_8xb2-lsj-50e_coco.py\nmask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py\nmask_rcnn/mask-rcnn_r50_fpn_1x_coco.py\nmaskformer/maskformer_r50_ms-16xb1-75e_coco.py\nms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py\nnas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py\nnas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py\npaa/paa_r50_fpn_1x_coco.py\npafpn/faster-rcnn_r50_pafpn_1x_coco.py\npanoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py\npisa/faster-rcnn_r50_fpn_pisa_1x_coco.py\npoint_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py\npvt/retinanet_pvtv2-b0_fpn_1x_coco.py\nqueryinst/queryinst_r50_fpn_1x_coco.py\nregnet/mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py\nreppoints/reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py\nres2net/faster-rcnn_res2net-101_fpn_2x_coco.py\nresnest/mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py\nresnet_strikes_back/faster-rcnn_r50-rsb-pre_fpn_1x_coco.py\nretinanet/retinanet_r50_fpn_1x_coco.py\nrpn/rpn_r50_fpn_1x_coco.py\nrtmdet/rtmdet_s_8xb32-300e_coco.py\nrtmdet/rtmdet-ins_s_8xb32-300e_coco.py\nsabl/sabl-retinanet_r50_fpn_1x_coco.py\nscnet/scnet_r50_fpn_1x_coco.py\nscratch/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py\nseesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py\nsimple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-90k_coco.py\nsoft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py\nsolo/solo_r50_fpn_1x_coco.py\nsolov2/solov2_r50_fpn_1x_coco.py\nsparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py\nssd/ssd300_coco.py\nswin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py\ntood/tood_r50_fpn_1x_coco.py\ntridentnet/tridentnet_r50-caffe_1x_coco.py\nvfnet/vfnet_r50_fpn_1x_coco.py\nyolact/yolact_r50_8xb8-55e_coco.py\nyolo/yolov3_d53_8xb8-320-273e_coco.py\nyolof/yolof_r50-c5_8xb8-1x_coco.py\nyolox/yolox_s_8xb8-300e_coco.py\ndeepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\nmask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py\nmasktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py\nocsort/ocsort_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py\nqdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\nstrongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py\n"
  },
  {
    "path": ".dev_scripts/benchmark_inference_fps.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.dist import init_dist\nfrom mmengine.fileio import dump\nfrom mmengine.utils import mkdir_or_exist\nfrom terminaltables import GithubFlavoredMarkdownTable\n\nfrom tools.analysis_tools.benchmark import repeat_measure_inference_speed\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMDet benchmark a model of FPS')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('checkpoint_root', help='Checkpoint file root path')\n    parser.add_argument(\n        '--round-num',\n        type=int,\n        default=1,\n        help='round a number to a given precision in decimal digits')\n    parser.add_argument(\n        '--repeat-num',\n        type=int,\n        default=1,\n        help='number of repeat times of measurement for averaging the results')\n    parser.add_argument(\n        '--out', type=str, help='output path of gathered fps to be stored')\n    parser.add_argument(\n        '--max-iter', type=int, default=2000, help='num of max iter')\n    parser.add_argument(\n        '--log-interval', type=int, default=50, help='interval of logging')\n    parser.add_argument(\n        '--fuse-conv-bn',\n        action='store_true',\n        help='Whether to fuse conv and bn, this will slightly increase'\n        'the inference speed')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--local_rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    return args\n\n\ndef results2markdown(result_dict):\n    table_data = []\n    is_multiple_results = False\n    for cfg_name, value in result_dict.items():\n        name = cfg_name.replace('configs/', '')\n        fps = value['fps']\n        ms_times_pre_image = value['ms_times_pre_image']\n        if isinstance(fps, list):\n            is_multiple_results = True\n            mean_fps = value['mean_fps']\n            mean_times_pre_image = value['mean_times_pre_image']\n            fps_str = ','.join([str(s) for s in fps])\n            ms_times_pre_image_str = ','.join(\n                [str(s) for s in ms_times_pre_image])\n            table_data.append([\n                name, fps_str, mean_fps, ms_times_pre_image_str,\n                mean_times_pre_image\n            ])\n        else:\n            table_data.append([name, fps, ms_times_pre_image])\n\n    if is_multiple_results:\n        table_data.insert(0, [\n            'model', 'fps', 'mean_fps', 'times_pre_image(ms)',\n            'mean_times_pre_image(ms)'\n        ])\n\n    else:\n        table_data.insert(0, ['model', 'fps', 'times_pre_image(ms)'])\n    table = GithubFlavoredMarkdownTable(table_data)\n    print(table.table, flush=True)\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    assert args.round_num >= 0\n    assert args.repeat_num >= 1\n\n    config = Config.fromfile(args.config)\n\n    if args.launcher == 'none':\n        raise NotImplementedError('Only supports distributed mode')\n    else:\n        init_dist(args.launcher)\n\n    result_dict = {}\n    for model_key in config:\n        model_infos = config[model_key]\n        if not isinstance(model_infos, list):\n            model_infos = [model_infos]\n        for model_info in model_infos:\n            record_metrics = model_info['metric']\n            cfg_path = model_info['config'].strip()\n            cfg = Config.fromfile(cfg_path)\n            checkpoint = osp.join(args.checkpoint_root,\n                                  model_info['checkpoint'].strip())\n            try:\n                fps = repeat_measure_inference_speed(cfg, checkpoint,\n                                                     args.max_iter,\n                                                     args.log_interval,\n                                                     args.fuse_conv_bn,\n                                                     args.repeat_num)\n                if args.repeat_num > 1:\n                    fps_list = [round(fps_, args.round_num) for fps_ in fps]\n                    times_pre_image_list = [\n                        round(1000 / fps_, args.round_num) for fps_ in fps\n                    ]\n                    mean_fps = round(\n                        sum(fps_list) / len(fps_list), args.round_num)\n                    mean_times_pre_image = round(\n                        sum(times_pre_image_list) / len(times_pre_image_list),\n                        args.round_num)\n                    print(\n                        f'{cfg_path} '\n                        f'Overall fps: {fps_list}[{mean_fps}] img / s, '\n                        f'times per image: '\n                        f'{times_pre_image_list}[{mean_times_pre_image}] '\n                        f'ms / img',\n                        flush=True)\n                    result_dict[cfg_path] = dict(\n                        fps=fps_list,\n                        mean_fps=mean_fps,\n                        ms_times_pre_image=times_pre_image_list,\n                        mean_times_pre_image=mean_times_pre_image)\n                else:\n                    print(\n                        f'{cfg_path} fps : {fps:.{args.round_num}f} img / s, '\n                        f'times per image: {1000 / fps:.{args.round_num}f} '\n                        f'ms / img',\n                        flush=True)\n                    result_dict[cfg_path] = dict(\n                        fps=round(fps, args.round_num),\n                        ms_times_pre_image=round(1000 / fps, args.round_num))\n            except Exception as e:\n                print(f'{cfg_path} error: {repr(e)}')\n                if args.repeat_num > 1:\n                    result_dict[cfg_path] = dict(\n                        fps=[0],\n                        mean_fps=0,\n                        ms_times_pre_image=[0],\n                        mean_times_pre_image=0)\n                else:\n                    result_dict[cfg_path] = dict(fps=0, ms_times_pre_image=0)\n\n    if args.out:\n        mkdir_or_exist(args.out)\n        dump(result_dict, osp.join(args.out, 'batch_inference_fps.json'))\n\n    results2markdown(result_dict)\n"
  },
  {
    "path": ".dev_scripts/benchmark_options.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nthird_part_libs = [\n    'pip install -r ../requirements/albu.txt',\n    'pip install instaboostfast',\n    'pip install git+https://github.com/cocodataset/panopticapi.git',\n    'pip install timm',\n    'pip install mmpretrain',\n    'pip install git+https://github.com/lvis-dataset/lvis-api.git',\n    'pip install -r ../requirements/multimodal.txt',\n    'pip install -r ../requirements/tracking.txt',\n    'pip install git+https://github.com/JonathonLuiten/TrackEval.git',\n]\n\ndefault_floating_range = 0.5\nmodel_floating_ranges = {'atss/atss_r50_fpn_1x_coco.py': 0.3}\n"
  },
  {
    "path": ".dev_scripts/benchmark_test.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport logging\nimport os\nimport os.path as osp\nfrom argparse import ArgumentParser\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.logging import MMLogger\nfrom mmengine.registry import RUNNERS\nfrom mmengine.runner import Runner\n\nfrom mmdet.testing import replace_to_ceph\nfrom mmdet.utils import register_all_modules, replace_cfg_vals\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('checkpoint_root', help='Checkpoint file root path')\n    parser.add_argument('--work-dir', help='the dir to save logs')\n    parser.add_argument('--ceph', action='store_true')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--local_rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    args = parser.parse_args()\n    return args\n\n\n# TODO: Need to refactor test.py so that it can be reused.\ndef fast_test_model(config_name, checkpoint, args, logger=None):\n    cfg = Config.fromfile(config_name)\n    cfg = replace_cfg_vals(cfg)\n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = osp.join(args.work_dir,\n                                osp.splitext(osp.basename(config_name))[0])\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(config_name))[0])\n\n    if args.ceph:\n        replace_to_ceph(cfg)\n\n    cfg.load_from = checkpoint\n\n    # TODO: temporary plan\n    if 'visualizer' in cfg:\n        if 'name' in cfg.visualizer:\n            del cfg.visualizer.name\n\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    runner.test()\n\n\n# Sample test whether the inference code is correct\ndef main(args):\n    # register all modules in mmdet into the registries\n    register_all_modules(init_default_scope=False)\n\n    config = Config.fromfile(args.config)\n\n    # test all model\n    logger = MMLogger.get_instance(\n        name='MMLogger',\n        log_file='benchmark_test.log',\n        log_level=logging.ERROR)\n\n    for model_key in config:\n        model_infos = config[model_key]\n        if not isinstance(model_infos, list):\n            model_infos = [model_infos]\n        for model_info in model_infos:\n            print('processing: ', model_info['config'], flush=True)\n            config_name = model_info['config'].strip()\n            checkpoint = osp.join(args.checkpoint_root,\n                                  model_info['checkpoint'].strip())\n            try:\n                fast_test_model(config_name, checkpoint, args, logger)\n            except Exception as e:\n                logger.error(f'{config_name} \" : {repr(e)}')\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    main(args)\n"
  },
  {
    "path": ".dev_scripts/benchmark_test_image.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport logging\nimport os.path as osp\nfrom argparse import ArgumentParser\n\nimport mmcv\nfrom mmengine.config import Config\nfrom mmengine.logging import MMLogger\nfrom mmengine.utils import mkdir_or_exist\n\nfrom mmdet.apis import inference_detector, init_detector\nfrom mmdet.registry import VISUALIZERS\nfrom mmdet.utils import register_all_modules\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('checkpoint_root', help='Checkpoint file root path')\n    parser.add_argument('--img', default='demo/demo.jpg', help='Image file')\n    parser.add_argument('--aug', action='store_true', help='aug test')\n    parser.add_argument('--model-name', help='model name to inference')\n    parser.add_argument('--show', action='store_true', help='show results')\n    parser.add_argument('--out-dir', default=None, help='Dir to output file')\n    parser.add_argument(\n        '--wait-time',\n        type=float,\n        default=1,\n        help='the interval of show (s), 0 is block')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--palette',\n        default='coco',\n        choices=['coco', 'voc', 'citys', 'random'],\n        help='Color palette used for visualization')\n    parser.add_argument(\n        '--score-thr', type=float, default=0.3, help='bbox score threshold')\n    args = parser.parse_args()\n    return args\n\n\ndef inference_model(config_name, checkpoint, visualizer, args, logger=None):\n    cfg = Config.fromfile(config_name)\n    if args.aug:\n        raise NotImplementedError()\n\n    model = init_detector(\n        cfg, checkpoint, palette=args.palette, device=args.device)\n    visualizer.dataset_meta = model.dataset_meta\n\n    # test a single image\n    result = inference_detector(model, args.img)\n\n    # show the results\n    if args.show or args.out_dir is not None:\n        img = mmcv.imread(args.img)\n        img = mmcv.imconvert(img, 'bgr', 'rgb')\n        out_file = None\n        if args.out_dir is not None:\n            out_dir = args.out_dir\n            mkdir_or_exist(out_dir)\n\n            out_file = osp.join(\n                out_dir,\n                config_name.split('/')[-1].replace('py', 'jpg'))\n\n        visualizer.add_datasample(\n            'result',\n            img,\n            data_sample=result,\n            draw_gt=False,\n            show=args.show,\n            wait_time=args.wait_time,\n            out_file=out_file,\n            pred_score_thr=args.score_thr)\n\n    return result\n\n\n# Sample test whether the inference code is correct\ndef main(args):\n    # register all modules in mmdet into the registries\n    register_all_modules()\n\n    config = Config.fromfile(args.config)\n\n    # init visualizer\n    visualizer_cfg = dict(type='DetLocalVisualizer', name='visualizer')\n    visualizer = VISUALIZERS.build(visualizer_cfg)\n\n    # test single model\n    if args.model_name:\n        if args.model_name in config:\n            model_infos = config[args.model_name]\n            if not isinstance(model_infos, list):\n                model_infos = [model_infos]\n            model_info = model_infos[0]\n            config_name = model_info['config'].strip()\n            print(f'processing: {config_name}', flush=True)\n            checkpoint = osp.join(args.checkpoint_root,\n                                  model_info['checkpoint'].strip())\n            # build the model from a config file and a checkpoint file\n            inference_model(config_name, checkpoint, visualizer, args)\n            return\n        else:\n            raise RuntimeError('model name input error.')\n\n    # test all model\n    logger = MMLogger.get_instance(\n        name='MMLogger',\n        log_file='benchmark_test_image.log',\n        log_level=logging.ERROR)\n\n    for model_key in config:\n        model_infos = config[model_key]\n        if not isinstance(model_infos, list):\n            model_infos = [model_infos]\n        for model_info in model_infos:\n            print('processing: ', model_info['config'], flush=True)\n            config_name = model_info['config'].strip()\n            checkpoint = osp.join(args.checkpoint_root,\n                                  model_info['checkpoint'].strip())\n            try:\n                # build the model from a config file and a checkpoint file\n                inference_model(config_name, checkpoint, visualizer, args,\n                                logger)\n            except Exception as e:\n                logger.error(f'{config_name} \" : {repr(e)}')\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    main(args)\n"
  },
  {
    "path": ".dev_scripts/benchmark_train.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport logging\nimport os\nimport os.path as osp\nfrom argparse import ArgumentParser\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.logging import MMLogger, print_log\nfrom mmengine.registry import RUNNERS\nfrom mmengine.runner import Runner\n\nfrom mmdet.testing import replace_to_ceph\nfrom mmdet.utils import register_all_modules, replace_cfg_vals\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('--work-dir', help='the dir to save logs and models')\n    parser.add_argument('--ceph', action='store_true')\n    parser.add_argument('--save-ckpt', action='store_true')\n    parser.add_argument(\n        '--amp',\n        action='store_true',\n        default=False,\n        help='enable automatic-mixed-precision training')\n    parser.add_argument(\n        '--auto-scale-lr',\n        action='store_true',\n        help='enable automatically scaling LR.')\n    parser.add_argument(\n        '--resume',\n        action='store_true',\n        help='resume from the latest checkpoint in the work_dir automatically')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--local_rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    args = parser.parse_args()\n    return args\n\n\n# TODO: Need to refactor train.py so that it can be reused.\ndef fast_train_model(config_name, args, logger=None):\n    cfg = Config.fromfile(config_name)\n    cfg = replace_cfg_vals(cfg)\n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = osp.join(args.work_dir,\n                                osp.splitext(osp.basename(config_name))[0])\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(config_name))[0])\n\n    ckpt_hook = cfg.default_hooks.checkpoint\n    by_epoch = ckpt_hook.get('by_epoch', True)\n    fast_stop_hook = dict(type='FastStopTrainingHook')\n    fast_stop_hook['by_epoch'] = by_epoch\n    if args.save_ckpt:\n        if by_epoch:\n            interval = 1\n            stop_iter_or_epoch = 2\n        else:\n            interval = 4\n            stop_iter_or_epoch = 10\n        fast_stop_hook['stop_iter_or_epoch'] = stop_iter_or_epoch\n        fast_stop_hook['save_ckpt'] = True\n        ckpt_hook.interval = interval\n\n    if 'custom_hooks' in cfg:\n        cfg.custom_hooks.append(fast_stop_hook)\n    else:\n        custom_hooks = [fast_stop_hook]\n        cfg.custom_hooks = custom_hooks\n\n    # TODO: temporary plan\n    if 'visualizer' in cfg:\n        if 'name' in cfg.visualizer:\n            del cfg.visualizer.name\n\n    # enable automatic-mixed-precision training\n    if args.amp is True:\n        optim_wrapper = cfg.optim_wrapper.type\n        if optim_wrapper == 'AmpOptimWrapper':\n            print_log(\n                'AMP training is already enabled in your config.',\n                logger='current',\n                level=logging.WARNING)\n        else:\n            assert optim_wrapper == 'OptimWrapper', (\n                '`--amp` is only supported when the optimizer wrapper type is '\n                f'`OptimWrapper` but got {optim_wrapper}.')\n            cfg.optim_wrapper.type = 'AmpOptimWrapper'\n            cfg.optim_wrapper.loss_scale = 'dynamic'\n\n    # enable automatically scaling LR\n    if args.auto_scale_lr:\n        if 'auto_scale_lr' in cfg and \\\n                'enable' in cfg.auto_scale_lr and \\\n                'base_batch_size' in cfg.auto_scale_lr:\n            cfg.auto_scale_lr.enable = True\n        else:\n            raise RuntimeError('Can not find \"auto_scale_lr\" or '\n                               '\"auto_scale_lr.enable\" or '\n                               '\"auto_scale_lr.base_batch_size\" in your'\n                               ' configuration file.')\n\n    if args.ceph:\n        replace_to_ceph(cfg)\n\n    cfg.resume = args.resume\n\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    runner.train()\n\n\n# Sample test whether the train code is correct\ndef main(args):\n    # register all modules in mmdet into the registries\n    register_all_modules(init_default_scope=False)\n\n    config = Config.fromfile(args.config)\n\n    # test all model\n    logger = MMLogger.get_instance(\n        name='MMLogger',\n        log_file='benchmark_train.log',\n        log_level=logging.ERROR)\n\n    for model_key in config:\n        model_infos = config[model_key]\n        if not isinstance(model_infos, list):\n            model_infos = [model_infos]\n        for model_info in model_infos:\n            print('processing: ', model_info['config'], flush=True)\n            config_name = model_info['config'].strip()\n            try:\n                fast_train_model(config_name, args, logger)\n            except RuntimeError as e:\n                # quick exit is the normal exit message\n                if 'quick exit' not in repr(e):\n                    logger.error(f'{config_name} \" : {repr(e)}')\n            except Exception as e:\n                logger.error(f'{config_name} \" : {repr(e)}')\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    main(args)\n"
  },
  {
    "path": ".dev_scripts/benchmark_train_models.txt",
    "content": "atss/atss_r50_fpn_1x_coco.py\nfaster_rcnn/faster-rcnn_r50_fpn_1x_coco.py\nmask_rcnn/mask-rcnn_r50_fpn_1x_coco.py\ncascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py\nconfigs/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco.py\nconfigs/glip/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco.py\nconfigs/ddq/ddq-detr-4scale_r50_8xb2-12e_coco.py\npanoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py\nretinanet/retinanet_r50_fpn_1x_coco.py\nrtmdet/rtmdet_s_8xb32-300e_coco.py\nrtmdet/rtmdet-ins_s_8xb32-300e_coco.py\nfcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py\ncenternet/centernet-update_r50-caffe_fpn_ms-1x_coco.py\ndino/dino-4scale_r50_8xb2-12e_coco.py\nhtc/htc_r50_fpn_1x_coco.py\nmask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py\nswin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py\ncondinst/condinst_r50_fpn_ms-poly-90k_coco_instance.py\nlvis/mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py\nmask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py\nmasktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py\nqdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n"
  },
  {
    "path": ".dev_scripts/benchmark_valid_flops.py",
    "content": "import logging\nimport re\nimport tempfile\nfrom argparse import ArgumentParser\nfrom collections import OrderedDict\nfrom functools import partial\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom mmengine import Config, DictAction\nfrom mmengine.analysis import get_model_complexity_info\nfrom mmengine.analysis.print_helper import _format_size\nfrom mmengine.fileio import FileClient\nfrom mmengine.logging import MMLogger\nfrom mmengine.model import revert_sync_batchnorm\nfrom mmengine.runner import Runner\nfrom modelindex.load_model_index import load\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.text import Text\nfrom tqdm import tqdm\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import register_all_modules\n\nconsole = Console()\nMMDET_ROOT = Path(__file__).absolute().parents[1]\n\n\ndef parse_args():\n    parser = ArgumentParser(description='Valid all models in model-index.yml')\n    parser.add_argument(\n        '--shape',\n        type=int,\n        nargs='+',\n        default=[1280, 800],\n        help='input image size')\n    parser.add_argument(\n        '--checkpoint_root',\n        help='Checkpoint file root path. If set, load checkpoint before test.')\n    parser.add_argument('--img', default='demo/demo.jpg', help='Image file')\n    parser.add_argument('--models', nargs='+', help='models name to inference')\n    parser.add_argument(\n        '--batch-size',\n        type=int,\n        default=1,\n        help='The batch size during the inference.')\n    parser.add_argument(\n        '--flops', action='store_true', help='Get Flops and Params of models')\n    parser.add_argument(\n        '--flops-str',\n        action='store_true',\n        help='Output FLOPs and params counts in a string form.')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--size_divisor',\n        type=int,\n        default=32,\n        help='Pad the input image, the minimum size that is divisible '\n        'by size_divisor, -1 means do not pad the image.')\n    args = parser.parse_args()\n    return args\n\n\ndef inference(config_file, checkpoint, work_dir, args, exp_name):\n    logger = MMLogger.get_instance(name='MMLogger')\n    logger.warning('if you want test flops, please make sure torch>=1.12')\n    cfg = Config.fromfile(config_file)\n    cfg.work_dir = work_dir\n    cfg.load_from = checkpoint\n    cfg.log_level = 'WARN'\n    cfg.experiment_name = exp_name\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # forward the model\n    result = {'model': config_file.stem}\n\n    if args.flops:\n\n        if len(args.shape) == 1:\n            h = w = args.shape[0]\n        elif len(args.shape) == 2:\n            h, w = args.shape\n        else:\n            raise ValueError('invalid input shape')\n        divisor = args.size_divisor\n        if divisor > 0:\n            h = int(np.ceil(h / divisor)) * divisor\n            w = int(np.ceil(w / divisor)) * divisor\n\n        input_shape = (3, h, w)\n        result['resolution'] = input_shape\n\n        try:\n            cfg = Config.fromfile(config_file)\n            if hasattr(cfg, 'head_norm_cfg'):\n                cfg['head_norm_cfg'] = dict(type='SyncBN', requires_grad=True)\n                cfg['model']['roi_head']['bbox_head']['norm_cfg'] = dict(\n                    type='SyncBN', requires_grad=True)\n                cfg['model']['roi_head']['mask_head']['norm_cfg'] = dict(\n                    type='SyncBN', requires_grad=True)\n\n            if args.cfg_options is not None:\n                cfg.merge_from_dict(args.cfg_options)\n\n            model = MODELS.build(cfg.model)\n            input = torch.rand(1, *input_shape)\n            if torch.cuda.is_available():\n                model.cuda()\n                input = input.cuda()\n            model = revert_sync_batchnorm(model)\n            inputs = (input, )\n            model.eval()\n            outputs = get_model_complexity_info(\n                model, input_shape, inputs, show_table=False, show_arch=False)\n            flops = outputs['flops']\n            params = outputs['params']\n            activations = outputs['activations']\n            result['Get Types'] = 'direct'\n        except:  # noqa 772\n            logger = MMLogger.get_instance(name='MMLogger')\n            logger.warning(\n                'Direct get flops failed, try to get flops with data')\n            cfg = Config.fromfile(config_file)\n            if hasattr(cfg, 'head_norm_cfg'):\n                cfg['head_norm_cfg'] = dict(type='SyncBN', requires_grad=True)\n                cfg['model']['roi_head']['bbox_head']['norm_cfg'] = dict(\n                    type='SyncBN', requires_grad=True)\n                cfg['model']['roi_head']['mask_head']['norm_cfg'] = dict(\n                    type='SyncBN', requires_grad=True)\n            data_loader = Runner.build_dataloader(cfg.val_dataloader)\n            data_batch = next(iter(data_loader))\n            model = MODELS.build(cfg.model)\n            if torch.cuda.is_available():\n                model = model.cuda()\n            model = revert_sync_batchnorm(model)\n            model.eval()\n            _forward = model.forward\n            data = model.data_preprocessor(data_batch)\n            del data_loader\n            model.forward = partial(\n                _forward, data_samples=data['data_samples'])\n            outputs = get_model_complexity_info(\n                model,\n                input_shape,\n                data['inputs'],\n                show_table=False,\n                show_arch=False)\n            flops = outputs['flops']\n            params = outputs['params']\n            activations = outputs['activations']\n            result['Get Types'] = 'dataloader'\n\n        if args.flops_str:\n            flops = _format_size(flops)\n            params = _format_size(params)\n            activations = _format_size(activations)\n\n        result['flops'] = flops\n        result['params'] = params\n\n    return result\n\n\ndef show_summary(summary_data, args):\n    table = Table(title='Validation Benchmark Regression Summary')\n    table.add_column('Model')\n    table.add_column('Validation')\n    table.add_column('Resolution (c, h, w)')\n    if args.flops:\n        table.add_column('Flops', justify='right', width=11)\n        table.add_column('Params', justify='right')\n\n    for model_name, summary in summary_data.items():\n        row = [model_name]\n        valid = summary['valid']\n        color = 'green' if valid == 'PASS' else 'red'\n        row.append(f'[{color}]{valid}[/{color}]')\n        if valid == 'PASS':\n            row.append(str(summary['resolution']))\n            if args.flops:\n                row.append(str(summary['flops']))\n                row.append(str(summary['params']))\n        table.add_row(*row)\n\n    console.print(table)\n    table_data = {\n        x.header: [Text.from_markup(y).plain for y in x.cells]\n        for x in table.columns\n    }\n    table_pd = pd.DataFrame(table_data)\n    table_pd.to_csv('./mmdetection_flops.csv')\n\n\n# Sample test whether the inference code is correct\ndef main(args):\n    register_all_modules()\n    model_index_file = MMDET_ROOT / 'model-index.yml'\n    model_index = load(str(model_index_file))\n    model_index.build_models_with_collections()\n    models = OrderedDict({model.name: model for model in model_index.models})\n\n    logger = MMLogger(\n        'validation',\n        logger_name='validation',\n        log_file='benchmark_test_image.log',\n        log_level=logging.INFO)\n\n    if args.models:\n        patterns = [\n            re.compile(pattern.replace('+', '_')) for pattern in args.models\n        ]\n        filter_models = {}\n        for k, v in models.items():\n            k = k.replace('+', '_')\n            if any([re.match(pattern, k) for pattern in patterns]):\n                filter_models[k] = v\n        if len(filter_models) == 0:\n            print('No model found, please specify models in:')\n            print('\\n'.join(models.keys()))\n            return\n        models = filter_models\n\n    summary_data = {}\n    tmpdir = tempfile.TemporaryDirectory()\n    for model_name, model_info in tqdm(models.items()):\n\n        if model_info.config is None:\n            continue\n\n        model_info.config = model_info.config.replace('%2B', '+')\n        config = Path(model_info.config)\n\n        try:\n            config.exists()\n        except:  # noqa 722\n            logger.error(f'{model_name}: {config} not found.')\n            continue\n\n        logger.info(f'Processing: {model_name}')\n\n        http_prefix = 'https://download.openmmlab.com/mmdetection/'\n        if args.checkpoint_root is not None:\n            root = args.checkpoint_root\n            if 's3://' in args.checkpoint_root:\n                from petrel_client.common.exception import AccessDeniedError\n                file_client = FileClient.infer_client(uri=root)\n                checkpoint = file_client.join_path(\n                    root, model_info.weights[len(http_prefix):])\n                try:\n                    exists = file_client.exists(checkpoint)\n                except AccessDeniedError:\n                    exists = False\n            else:\n                checkpoint = Path(root) / model_info.weights[len(http_prefix):]\n                exists = checkpoint.exists()\n            if exists:\n                checkpoint = str(checkpoint)\n            else:\n                print(f'WARNING: {model_name}: {checkpoint} not found.')\n                checkpoint = None\n        else:\n            checkpoint = None\n\n        try:\n            # build the model from a config file and a checkpoint file\n            result = inference(MMDET_ROOT / config, checkpoint, tmpdir.name,\n                               args, model_name)\n            result['valid'] = 'PASS'\n        except Exception:  # noqa 722\n            import traceback\n            logger.error(f'\"{config}\" :\\n{traceback.format_exc()}')\n            result = {'valid': 'FAIL'}\n\n        summary_data[model_name] = result\n\n    tmpdir.cleanup()\n    show_summary(summary_data, args)\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    main(args)\n"
  },
  {
    "path": ".dev_scripts/check_links.py",
    "content": "# Modified from:\n# https://github.com/allenai/allennlp/blob/main/scripts/check_links.py\n\nimport argparse\nimport logging\nimport os\nimport pathlib\nimport re\nimport sys\nfrom multiprocessing.dummy import Pool\nfrom typing import NamedTuple, Optional, Tuple\n\nimport requests\nfrom mmengine.logging import MMLogger\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Goes through all the inline-links '\n        'in markdown files and reports the breakages')\n    parser.add_argument(\n        '--num-threads',\n        type=int,\n        default=100,\n        help='Number of processes to confirm the link')\n    parser.add_argument('--https-proxy', type=str, help='https proxy')\n    parser.add_argument(\n        '--out',\n        type=str,\n        default='link_reports.txt',\n        help='output path of reports')\n    args = parser.parse_args()\n    return args\n\n\nOK_STATUS_CODES = (\n    200,\n    401,  # the resource exists but may require some sort of login.\n    403,  # ^ same\n    405,  # HEAD method not allowed.\n    # the resource exists, but our default 'Accept-' header may not\n    # match what the server can provide.\n    406,\n)\n\n\nclass MatchTuple(NamedTuple):\n    source: str\n    name: str\n    link: str\n\n\ndef check_link(\n        match_tuple: MatchTuple,\n        http_session: requests.Session,\n        logger: logging = None) -> Tuple[MatchTuple, bool, Optional[str]]:\n    reason: Optional[str] = None\n    if match_tuple.link.startswith('http'):\n        result_ok, reason = check_url(match_tuple, http_session)\n    else:\n        result_ok = check_path(match_tuple)\n    if logger is None:\n        print(f\"  {'✓' if result_ok else '✗'} {match_tuple.link}\")\n    else:\n        logger.info(f\"  {'✓' if result_ok else '✗'} {match_tuple.link}\")\n    return match_tuple, result_ok, reason\n\n\ndef check_url(match_tuple: MatchTuple,\n              http_session: requests.Session) -> Tuple[bool, str]:\n    \"\"\"Check if a URL is reachable.\"\"\"\n    try:\n        result = http_session.head(\n            match_tuple.link, timeout=5, allow_redirects=True)\n        return (\n            result.ok or result.status_code in OK_STATUS_CODES,\n            f'status code = {result.status_code}',\n        )\n    except (requests.ConnectionError, requests.Timeout):\n        return False, 'connection error'\n\n\ndef check_path(match_tuple: MatchTuple) -> bool:\n    \"\"\"Check if a file in this repository exists.\"\"\"\n    relative_path = match_tuple.link.split('#')[0]\n    full_path = os.path.join(\n        os.path.dirname(str(match_tuple.source)), relative_path)\n    return os.path.exists(full_path)\n\n\ndef main():\n    args = parse_args()\n\n    # setup logger\n    logger = MMLogger.get_instance(name='mmdet', log_file=args.out)\n\n    # setup https_proxy\n    if args.https_proxy:\n        os.environ['https_proxy'] = args.https_proxy\n\n    # setup http_session\n    http_session = requests.Session()\n    for resource_prefix in ('http://', 'https://'):\n        http_session.mount(\n            resource_prefix,\n            requests.adapters.HTTPAdapter(\n                max_retries=5,\n                pool_connections=20,\n                pool_maxsize=args.num_threads),\n        )\n\n    logger.info('Finding all markdown files in the current directory...')\n\n    project_root = (pathlib.Path(__file__).parent / '..').resolve()\n    markdown_files = project_root.glob('**/*.md')\n\n    all_matches = set()\n    url_regex = re.compile(r'\\[([^!][^\\]]+)\\]\\(([^)(]+)\\)')\n    for markdown_file in markdown_files:\n        with open(markdown_file) as handle:\n            for line in handle.readlines():\n                matches = url_regex.findall(line)\n                for name, link in matches:\n                    if 'localhost' not in link:\n                        all_matches.add(\n                            MatchTuple(\n                                source=str(markdown_file),\n                                name=name,\n                                link=link))\n\n    logger.info(f'  {len(all_matches)} markdown files found')\n    logger.info('Checking to make sure we can retrieve each link...')\n\n    with Pool(processes=args.num_threads) as pool:\n        results = pool.starmap(check_link, [(match, http_session, logger)\n                                            for match in list(all_matches)])\n\n    # collect unreachable results\n    unreachable_results = [(match_tuple, reason)\n                           for match_tuple, success, reason in results\n                           if not success]\n\n    if unreachable_results:\n        logger.info('================================================')\n        logger.info(f'Unreachable links ({len(unreachable_results)}):')\n        for match_tuple, reason in unreachable_results:\n            logger.info('  > Source: ' + match_tuple.source)\n            logger.info('    Name: ' + match_tuple.name)\n            logger.info('    Link: ' + match_tuple.link)\n            if reason is not None:\n                logger.info('    Reason: ' + reason)\n        sys.exit(1)\n    logger.info('No Unreachable link found.')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".dev_scripts/convert_test_benchmark_script.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\n\nfrom mmengine import Config\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert benchmark model list to script')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('--port', type=int, default=29666, help='dist port')\n    parser.add_argument(\n        '--run', action='store_true', help='run script directly')\n    parser.add_argument(\n        '--out', type=str, help='path to save model benchmark script')\n\n    args = parser.parse_args()\n    return args\n\n\ndef process_model_info(model_info, work_dir):\n    config = model_info['config'].strip()\n    fname, _ = osp.splitext(osp.basename(config))\n    job_name = fname\n    work_dir = '$WORK_DIR/' + fname\n    checkpoint = model_info['checkpoint'].strip()\n    return dict(\n        config=config,\n        job_name=job_name,\n        work_dir=work_dir,\n        checkpoint=checkpoint)\n\n\ndef create_test_bash_info(commands, model_test_dict, port, script_name,\n                          partition):\n    config = model_test_dict['config']\n    job_name = model_test_dict['job_name']\n    checkpoint = model_test_dict['checkpoint']\n    work_dir = model_test_dict['work_dir']\n\n    echo_info = f' \\necho \\'{config}\\' &'\n    commands.append(echo_info)\n    commands.append('\\n')\n\n    command_info = f'GPUS=8  GPUS_PER_NODE=8  ' \\\n                   f'CPUS_PER_TASK=$CPUS_PRE_TASK {script_name} '\n\n    command_info += f'{partition} '\n    command_info += f'{job_name} '\n    command_info += f'{config} '\n    command_info += f'$CHECKPOINT_DIR/{checkpoint} '\n    command_info += f'--work-dir {work_dir} '\n\n    command_info += f'--cfg-option env_cfg.dist_cfg.port={port} '\n    command_info += ' &'\n\n    commands.append(command_info)\n\n\ndef main():\n    args = parse_args()\n    if args.out:\n        out_suffix = args.out.split('.')[-1]\n        assert args.out.endswith('.sh'), \\\n            f'Expected out file path suffix is .sh, but get .{out_suffix}'\n    assert args.out or args.run, \\\n        ('Please specify at least one operation (save/run/ the '\n         'script) with the argument \"--out\" or \"--run\"')\n\n    commands = []\n    partition_name = 'PARTITION=$1 '\n    commands.append(partition_name)\n    commands.append('\\n')\n\n    checkpoint_root = 'CHECKPOINT_DIR=$2 '\n    commands.append(checkpoint_root)\n    commands.append('\\n')\n\n    work_dir = 'WORK_DIR=$3 '\n    commands.append(work_dir)\n    commands.append('\\n')\n\n    cpus_pre_task = 'CPUS_PER_TASK=${4:-2} '\n    commands.append(cpus_pre_task)\n    commands.append('\\n')\n\n    script_name = osp.join('tools', 'slurm_test.sh')\n    port = args.port\n\n    cfg = Config.fromfile(args.config)\n\n    for model_key in cfg:\n        model_infos = cfg[model_key]\n        if not isinstance(model_infos, list):\n            model_infos = [model_infos]\n        for model_info in model_infos:\n            print('processing: ', model_info['config'])\n            model_test_dict = process_model_info(model_info, work_dir)\n            create_test_bash_info(commands, model_test_dict, port, script_name,\n                                  '$PARTITION')\n            port += 1\n\n    command_str = ''.join(commands)\n    if args.out:\n        with open(args.out, 'w') as f:\n            f.write(command_str)\n    if args.run:\n        os.system(command_str)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".dev_scripts/convert_train_benchmark_script.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert benchmark model json to script')\n    parser.add_argument(\n        'txt_path', type=str, help='txt path output by benchmark_filter')\n    parser.add_argument(\n        '--run', action='store_true', help='run script directly')\n    parser.add_argument(\n        '--out', type=str, help='path to save model benchmark script')\n\n    args = parser.parse_args()\n    return args\n\n\ndef determine_gpus(cfg_name):\n    gpus = 8\n    gpus_pre_node = 8\n\n    if cfg_name.find('16x') >= 0:\n        gpus = 16\n    elif cfg_name.find('4xb4') >= 0:\n        gpus = 4\n        gpus_pre_node = 4\n    elif 'lad' in cfg_name:\n        gpus = 2\n        gpus_pre_node = 2\n\n    return gpus, gpus_pre_node\n\n\ndef main():\n    args = parse_args()\n    if args.out:\n        out_suffix = args.out.split('.')[-1]\n        assert args.out.endswith('.sh'), \\\n            f'Expected out file path suffix is .sh, but get .{out_suffix}'\n    assert args.out or args.run, \\\n        ('Please specify at least one operation (save/run/ the '\n         'script) with the argument \"--out\" or \"--run\"')\n\n    root_name = './tools'\n    train_script_name = osp.join(root_name, 'slurm_train.sh')\n\n    commands = []\n    partition_name = 'PARTITION=$1 '\n    commands.append(partition_name)\n    commands.append('\\n')\n\n    work_dir = 'WORK_DIR=$2 '\n    commands.append(work_dir)\n    commands.append('\\n')\n\n    cpus_pre_task = 'CPUS_PER_TASK=${3:-4} '\n    commands.append(cpus_pre_task)\n    commands.append('\\n')\n    commands.append('\\n')\n\n    with open(args.txt_path, 'r') as f:\n        model_cfgs = f.readlines()\n        for i, cfg in enumerate(model_cfgs):\n            cfg = cfg.strip()\n            if len(cfg) == 0:\n                continue\n            # print cfg name\n            echo_info = f'echo \\'{cfg}\\' &'\n            commands.append(echo_info)\n            commands.append('\\n')\n\n            fname, _ = osp.splitext(osp.basename(cfg))\n            out_fname = '$WORK_DIR/' + fname\n\n            gpus, gpus_pre_node = determine_gpus(cfg)\n            command_info = f'GPUS={gpus}  GPUS_PER_NODE={gpus_pre_node}  ' \\\n                           f'CPUS_PER_TASK=$CPUS_PRE_TASK {train_script_name} '\n            command_info += '$PARTITION '\n            command_info += f'{fname} '\n            command_info += f'{cfg} '\n            command_info += f'{out_fname} '\n\n            command_info += '--cfg-options default_hooks.checkpoint.' \\\n                            'max_keep_ckpts=1 '\n            command_info += '&'\n\n            commands.append(command_info)\n\n            if i < len(model_cfgs):\n                commands.append('\\n')\n\n        command_str = ''.join(commands)\n        if args.out:\n            with open(args.out, 'w') as f:\n                f.write(command_str)\n        if args.run:\n            os.system(command_str)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".dev_scripts/covignore.cfg",
    "content": "# Each line should be the relative path to the root directory\n# of this repo. Support regular expression as well.\n# For example:\n\n.*/__init__.py\n"
  },
  {
    "path": ".dev_scripts/diff_coverage_test.sh",
    "content": "#!/bin/bash\n\nreadarray -t IGNORED_FILES < $( dirname \"$0\" )/covignore.cfg\nREUSE_COVERAGE_REPORT=${REUSE_COVERAGE_REPORT:-0}\nREPO=${1:-\"origin\"}\nBRANCH=${2:-\"refactor_dev\"}\n\ngit fetch $REPO $BRANCH\n\nPY_FILES=\"\"\nfor FILE_NAME in $(git diff --name-only ${REPO}/${BRANCH}); do\n    # Only test python files in mmdet/ existing in current branch, and not ignored in covignore.cfg\n    if [ ${FILE_NAME: -3} == \".py\" ] && [ ${FILE_NAME:0:6} == \"mmdet/\" ] && [ -f \"$FILE_NAME\" ]; then\n        IGNORED=false\n        for IGNORED_FILE_NAME in \"${IGNORED_FILES[@]}\"; do\n            # Skip blank lines\n            if [ -z \"$IGNORED_FILE_NAME\" ]; then\n                continue\n            fi\n            if [ \"${IGNORED_FILE_NAME::1}\" != \"#\" ] && [[ \"$FILE_NAME\" =~ $IGNORED_FILE_NAME ]]; then\n                echo \"Ignoring $FILE_NAME\"\n                IGNORED=true\n                break\n            fi\n        done\n        if [ \"$IGNORED\" = false ]; then\n            PY_FILES=\"$PY_FILES $FILE_NAME\"\n        fi\n    fi\ndone\n\n# Only test the coverage when PY_FILES are not empty, otherwise they will test the entire project\nif [ ! -z \"${PY_FILES}\" ]\nthen\n    if [ \"$REUSE_COVERAGE_REPORT\" == \"0\" ]; then\n        coverage run --branch --source mmdet -m pytest tests/\n    fi\n    coverage report --fail-under 80 -m $PY_FILES\n    interrogate -v --ignore-init-method --ignore-module --ignore-nested-functions --ignore-magic --ignore-regex \"__repr__\" --fail-under 95 $PY_FILES\nfi\n"
  },
  {
    "path": ".dev_scripts/download_checkpoints.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nimport argparse\nimport math\nimport os\nimport os.path as osp\nfrom multiprocessing import Pool\n\nimport torch\nfrom mmengine.config import Config\nfrom mmengine.utils import mkdir_or_exist\n\n\ndef download(url, out_file, min_bytes=math.pow(1024, 2), progress=True):\n    # math.pow(1024, 2) is mean 1 MB\n    assert_msg = f\"Downloaded url '{url}' does not exist \" \\\n                 f'or size is < min_bytes={min_bytes}'\n    try:\n        print(f'Downloading {url} to {out_file}...')\n        torch.hub.download_url_to_file(url, str(out_file), progress=progress)\n        assert osp.exists(\n            out_file) and osp.getsize(out_file) > min_bytes, assert_msg\n    except Exception as e:\n        if osp.exists(out_file):\n            os.remove(out_file)\n        print(f'ERROR: {e}\\nRe-attempting {url} to {out_file} ...')\n        os.system(f\"curl -L '{url}' -o '{out_file}' --retry 3 -C -\"\n                  )  # curl download, retry and resume on fail\n    finally:\n        if osp.exists(out_file) and osp.getsize(out_file) < min_bytes:\n            os.remove(out_file)  # remove partial downloads\n\n        if not osp.exists(out_file):\n            print(f'ERROR: {assert_msg}\\n')\n        print('=========================================\\n')\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Download checkpoints')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument(\n        'out', type=str, help='output dir of checkpoints to be stored')\n    parser.add_argument(\n        '--nproc', type=int, default=16, help='num of Processes')\n    parser.add_argument(\n        '--intranet',\n        action='store_true',\n        help='switch to internal network url')\n    args = parser.parse_args()\n    return args\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    mkdir_or_exist(args.out)\n\n    cfg = Config.fromfile(args.config)\n\n    checkpoint_url_list = []\n    checkpoint_out_list = []\n\n    for model in cfg:\n        model_infos = cfg[model]\n        if not isinstance(model_infos, list):\n            model_infos = [model_infos]\n        for model_info in model_infos:\n            checkpoint = model_info['checkpoint']\n            out_file = osp.join(args.out, checkpoint)\n            if not osp.exists(out_file):\n\n                url = model_info['url']\n                if args.intranet is True:\n                    url = url.replace('.com', '.sensetime.com')\n                    url = url.replace('https', 'http')\n\n                checkpoint_url_list.append(url)\n                checkpoint_out_list.append(out_file)\n\n    if len(checkpoint_url_list) > 0:\n        pool = Pool(min(os.cpu_count(), args.nproc))\n        pool.starmap(download, zip(checkpoint_url_list, checkpoint_out_list))\n    else:\n        print('No files to download!')\n"
  },
  {
    "path": ".dev_scripts/gather_models.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport glob\nimport os\nimport os.path as osp\nimport shutil\nimport subprocess\nimport time\nfrom collections import OrderedDict\n\nimport torch\nimport yaml\nfrom mmengine.config import Config\nfrom mmengine.fileio import dump\nfrom mmengine.utils import digit_version, mkdir_or_exist, scandir\n\n\ndef ordered_yaml_dump(data, stream=None, Dumper=yaml.SafeDumper, **kwds):\n\n    class OrderedDumper(Dumper):\n        pass\n\n    def _dict_representer(dumper, data):\n        return dumper.represent_mapping(\n            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, data.items())\n\n    OrderedDumper.add_representer(OrderedDict, _dict_representer)\n    return yaml.dump(data, stream, OrderedDumper, **kwds)\n\n\ndef process_checkpoint(in_file, out_file):\n    checkpoint = torch.load(in_file, map_location='cpu')\n    # remove optimizer for smaller file size\n    if 'optimizer' in checkpoint:\n        del checkpoint['optimizer']\n    if 'ema_state_dict' in checkpoint:\n        del checkpoint['ema_state_dict']\n\n    # remove ema state_dict\n    for key in list(checkpoint['state_dict']):\n        if key.startswith('ema_'):\n            checkpoint['state_dict'].pop(key)\n        elif key.startswith('data_preprocessor'):\n            checkpoint['state_dict'].pop(key)\n\n    # if it is necessary to remove some sensitive data in checkpoint['meta'],\n    # add the code here.\n    if digit_version(torch.__version__) >= digit_version('1.6'):\n        torch.save(checkpoint, out_file, _use_new_zipfile_serialization=False)\n    else:\n        torch.save(checkpoint, out_file)\n    sha = subprocess.check_output(['sha256sum', out_file]).decode()\n    final_file = out_file.rstrip('.pth') + '-{}.pth'.format(sha[:8])\n    subprocess.Popen(['mv', out_file, final_file])\n    return final_file\n\n\ndef is_by_epoch(config):\n    cfg = Config.fromfile('./configs/' + config)\n    return cfg.train_cfg.type == 'EpochBasedTrainLoop'\n\n\ndef get_final_epoch_or_iter(config):\n    cfg = Config.fromfile('./configs/' + config)\n    if cfg.train_cfg.type == 'EpochBasedTrainLoop':\n        return cfg.train_cfg.max_epochs\n    else:\n        return cfg.train_cfg.max_iters\n\n\ndef get_best_epoch_or_iter(exp_dir):\n    best_epoch_iter_full_path = list(\n        sorted(glob.glob(osp.join(exp_dir, 'best_*.pth'))))[-1]\n    best_epoch_or_iter_model_path = best_epoch_iter_full_path.split('/')[-1]\n    best_epoch_or_iter = best_epoch_or_iter_model_path.\\\n        split('_')[-1].split('.')[0]\n    return best_epoch_or_iter_model_path, int(best_epoch_or_iter)\n\n\ndef get_real_epoch_or_iter(config):\n    cfg = Config.fromfile('./configs/' + config)\n    if cfg.train_cfg.type == 'EpochBasedTrainLoop':\n        epoch = cfg.train_cfg.max_epochs\n        return epoch\n    else:\n        return cfg.train_cfg.max_iters\n\n\ndef get_final_results(log_json_path,\n                      epoch_or_iter,\n                      results_lut='coco/bbox_mAP',\n                      by_epoch=True):\n    result_dict = dict()\n    with open(log_json_path) as f:\n        r = f.readlines()[-1]\n        last_metric = r.split(',')[0].split(': ')[-1].strip()\n    result_dict[results_lut] = last_metric\n    return result_dict\n\n\ndef get_dataset_name(config):\n    # If there are more dataset, add here.\n    name_map = dict(\n        CityscapesDataset='Cityscapes',\n        CocoDataset='COCO',\n        CocoPanopticDataset='COCO',\n        DeepFashionDataset='Deep Fashion',\n        LVISV05Dataset='LVIS v0.5',\n        LVISV1Dataset='LVIS v1',\n        VOCDataset='Pascal VOC',\n        WIDERFaceDataset='WIDER Face',\n        OpenImagesDataset='OpenImagesDataset',\n        OpenImagesChallengeDataset='OpenImagesChallengeDataset',\n        Objects365V1Dataset='Objects365 v1',\n        Objects365V2Dataset='Objects365 v2')\n    cfg = Config.fromfile('./configs/' + config)\n    return name_map[cfg.dataset_type]\n\n\ndef find_last_dir(model_dir):\n    dst_times = []\n    for time_stamp in os.scandir(model_dir):\n        if osp.isdir(time_stamp):\n            dst_time = time.mktime(\n                time.strptime(time_stamp.name, '%Y%m%d_%H%M%S'))\n            dst_times.append([dst_time, time_stamp.name])\n    return max(dst_times, key=lambda x: x[0])[1]\n\n\ndef convert_model_info_to_pwc(model_infos):\n    pwc_files = {}\n    for model in model_infos:\n        cfg_folder_name = osp.split(model['config'])[-2]\n        pwc_model_info = OrderedDict()\n        pwc_model_info['Name'] = osp.split(model['config'])[-1].split('.')[0]\n        pwc_model_info['In Collection'] = 'Please fill in Collection name'\n        pwc_model_info['Config'] = osp.join('configs', model['config'])\n\n        # get metadata\n        meta_data = OrderedDict()\n        if 'epochs' in model:\n            meta_data['Epochs'] = get_real_epoch_or_iter(model['config'])\n        else:\n            meta_data['Iterations'] = get_real_epoch_or_iter(model['config'])\n        pwc_model_info['Metadata'] = meta_data\n\n        # get dataset name\n        dataset_name = get_dataset_name(model['config'])\n\n        # get results\n        results = []\n        # if there are more metrics, add here.\n        if 'bbox_mAP' in model['results']:\n            metric = round(model['results']['bbox_mAP'] * 100, 1)\n            results.append(\n                OrderedDict(\n                    Task='Object Detection',\n                    Dataset=dataset_name,\n                    Metrics={'box AP': metric}))\n        if 'segm_mAP' in model['results']:\n            metric = round(model['results']['segm_mAP'] * 100, 1)\n            results.append(\n                OrderedDict(\n                    Task='Instance Segmentation',\n                    Dataset=dataset_name,\n                    Metrics={'mask AP': metric}))\n        if 'PQ' in model['results']:\n            metric = round(model['results']['PQ'], 1)\n            results.append(\n                OrderedDict(\n                    Task='Panoptic Segmentation',\n                    Dataset=dataset_name,\n                    Metrics={'PQ': metric}))\n        pwc_model_info['Results'] = results\n\n        link_string = 'https://download.openmmlab.com/mmdetection/v3.0/'\n        link_string += '{}/{}'.format(model['config'].rstrip('.py'),\n                                      osp.split(model['model_path'])[-1])\n        pwc_model_info['Weights'] = link_string\n        if cfg_folder_name in pwc_files:\n            pwc_files[cfg_folder_name].append(pwc_model_info)\n        else:\n            pwc_files[cfg_folder_name] = [pwc_model_info]\n    return pwc_files\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Gather benchmarked models')\n    parser.add_argument(\n        'root',\n        type=str,\n        default='work_dirs',\n        help='root path of benchmarked models to be gathered')\n    parser.add_argument(\n        '--out',\n        type=str,\n        default='gather',\n        help='output path of gathered models to be stored')\n    parser.add_argument(\n        '--best',\n        action='store_true',\n        help='whether to gather the best model.')\n\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    models_root = args.root\n    models_out = args.out\n    mkdir_or_exist(models_out)\n\n    # find all models in the root directory to be gathered\n    raw_configs = list(scandir('./configs', '.py', recursive=True))\n\n    # filter configs that is not trained in the experiments dir\n    used_configs = []\n    for raw_config in raw_configs:\n        if osp.exists(osp.join(models_root, raw_config)):\n            used_configs.append(raw_config)\n    print(f'Find {len(used_configs)} models to be gathered')\n\n    # find final_ckpt and log file for trained each config\n    # and parse the best performance\n    model_infos = []\n    for used_config in used_configs:\n        exp_dir = osp.join(models_root, used_config)\n        by_epoch = is_by_epoch(used_config)\n        # check whether the exps is finished\n        if args.best is True:\n            final_model, final_epoch_or_iter = get_best_epoch_or_iter(exp_dir)\n        else:\n            final_epoch_or_iter = get_final_epoch_or_iter(used_config)\n            final_model = '{}_{}.pth'.format('epoch' if by_epoch else 'iter',\n                                             final_epoch_or_iter)\n\n        model_path = osp.join(exp_dir, final_model)\n        # skip if the model is still training\n        if not osp.exists(model_path):\n            continue\n\n        # get the latest logs\n        latest_exp_name = find_last_dir(exp_dir)\n        latest_exp_json = osp.join(exp_dir, latest_exp_name, 'vis_data',\n                                   latest_exp_name + '.json')\n\n        model_performance = get_final_results(\n            latest_exp_json, final_epoch_or_iter, by_epoch=by_epoch)\n\n        if model_performance is None:\n            continue\n\n        model_info = dict(\n            config=used_config,\n            results=model_performance,\n            final_model=final_model,\n            latest_exp_json=latest_exp_json,\n            latest_exp_name=latest_exp_name)\n        model_info['epochs' if by_epoch else 'iterations'] =\\\n            final_epoch_or_iter\n        model_infos.append(model_info)\n\n    # publish model for each checkpoint\n    publish_model_infos = []\n    for model in model_infos:\n        model_publish_dir = osp.join(models_out, model['config'].rstrip('.py'))\n        mkdir_or_exist(model_publish_dir)\n\n        model_name = osp.split(model['config'])[-1].split('.')[0]\n\n        model_name += '_' + model['latest_exp_name']\n        publish_model_path = osp.join(model_publish_dir, model_name)\n        trained_model_path = osp.join(models_root, model['config'],\n                                      model['final_model'])\n\n        # convert model\n        final_model_path = process_checkpoint(trained_model_path,\n                                              publish_model_path)\n\n        # copy log\n        shutil.copy(model['latest_exp_json'],\n                    osp.join(model_publish_dir, f'{model_name}.log.json'))\n\n        # copy config to guarantee reproducibility\n        config_path = model['config']\n        config_path = osp.join(\n            'configs',\n            config_path) if 'configs' not in config_path else config_path\n        target_config_path = osp.split(config_path)[-1]\n        shutil.copy(config_path, osp.join(model_publish_dir,\n                                          target_config_path))\n\n        model['model_path'] = final_model_path\n        publish_model_infos.append(model)\n\n    models = dict(models=publish_model_infos)\n    print(f'Totally gathered {len(publish_model_infos)} models')\n    dump(models, osp.join(models_out, 'model_info.json'))\n\n    pwc_files = convert_model_info_to_pwc(publish_model_infos)\n    for name in pwc_files:\n        with open(osp.join(models_out, name + '_metafile.yml'), 'w') as f:\n            ordered_yaml_dump(pwc_files[name], f, encoding='utf-8')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".dev_scripts/gather_test_benchmark_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport glob\nimport os.path as osp\n\nfrom mmengine.config import Config\nfrom mmengine.fileio import dump, load\nfrom mmengine.utils import mkdir_or_exist\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Gather benchmarked models metric')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument(\n        'root',\n        type=str,\n        help='root path of benchmarked models to be gathered')\n    parser.add_argument(\n        '--out', type=str, help='output path of gathered metrics to be stored')\n    parser.add_argument(\n        '--not-show', action='store_true', help='not show metrics')\n    parser.add_argument(\n        '--show-all', action='store_true', help='show all model metrics')\n\n    args = parser.parse_args()\n    return args\n\n\nif __name__ == '__main__':\n    args = parse_args()\n\n    root_path = args.root\n    metrics_out = args.out\n    result_dict = {}\n\n    cfg = Config.fromfile(args.config)\n\n    for model_key in cfg:\n        model_infos = cfg[model_key]\n        if not isinstance(model_infos, list):\n            model_infos = [model_infos]\n        for model_info in model_infos:\n            record_metrics = model_info['metric']\n            config = model_info['config'].strip()\n            fname, _ = osp.splitext(osp.basename(config))\n            metric_json_dir = osp.join(root_path, fname)\n            if osp.exists(metric_json_dir):\n                json_list = glob.glob(osp.join(metric_json_dir, '*.json'))\n                if len(json_list) > 0:\n                    log_json_path = list(sorted(json_list))[-1]\n\n                    metric = load(log_json_path)\n                    if config in metric.get('config', {}):\n\n                        new_metrics = dict()\n                        for record_metric_key in record_metrics:\n                            record_metric_key_bk = record_metric_key\n                            old_metric = record_metrics[record_metric_key]\n                            if record_metric_key == 'AR_1000':\n                                record_metric_key = 'AR@1000'\n                            if record_metric_key not in metric['metric']:\n                                raise KeyError(\n                                    'record_metric_key not exist, please '\n                                    'check your config')\n                            new_metric = round(\n                                metric['metric'][record_metric_key] * 100, 1)\n                            new_metrics[record_metric_key_bk] = new_metric\n\n                        if args.show_all:\n                            result_dict[config] = dict(\n                                before=record_metrics, after=new_metrics)\n                        else:\n                            for record_metric_key in record_metrics:\n                                old_metric = record_metrics[record_metric_key]\n                                new_metric = new_metrics[record_metric_key]\n                                if old_metric != new_metric:\n                                    result_dict[config] = dict(\n                                        before=record_metrics,\n                                        after=new_metrics)\n                                    break\n                    else:\n                        print(f'{config} not included in: {log_json_path}')\n                else:\n                    print(f'{config} not exist file: {metric_json_dir}')\n            else:\n                print(f'{config} not exist dir: {metric_json_dir}')\n\n    if metrics_out:\n        mkdir_or_exist(metrics_out)\n        dump(result_dict, osp.join(metrics_out, 'batch_test_metric_info.json'))\n    if not args.not_show:\n        print('===================================')\n        for config_name, metrics in result_dict.items():\n            print(config_name, metrics)\n        print('===================================')\n"
  },
  {
    "path": ".dev_scripts/gather_train_benchmark_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport glob\nimport os.path as osp\n\nfrom gather_models import get_final_results\nfrom mmengine.config import Config\nfrom mmengine.fileio import dump\nfrom mmengine.utils import mkdir_or_exist\n\ntry:\n    import xlrd\nexcept ImportError:\n    xlrd = None\ntry:\n    import xlutils\n    from xlutils.copy import copy\nexcept ImportError:\n    xlutils = None\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Gather benchmarked models metric')\n    parser.add_argument(\n        'root',\n        type=str,\n        help='root path of benchmarked models to be gathered')\n    parser.add_argument(\n        'txt_path', type=str, help='txt path output by benchmark_filter')\n    parser.add_argument(\n        '--out', type=str, help='output path of gathered metrics to be stored')\n    parser.add_argument(\n        '--not-show', action='store_true', help='not show metrics')\n    parser.add_argument(\n        '--excel', type=str, help='input path of excel to be recorded')\n    parser.add_argument(\n        '--ncol', type=int, help='Number of column to be modified or appended')\n\n    args = parser.parse_args()\n    return args\n\n\nif __name__ == '__main__':\n    args = parse_args()\n\n    if args.excel:\n        assert args.ncol, 'Please specify \"--excel\" and \"--ncol\" ' \\\n                          'at the same time'\n        if xlrd is None:\n            raise RuntimeError(\n                'xlrd is not installed,'\n                'Please use “pip install xlrd==1.2.0” to install')\n        if xlutils is None:\n            raise RuntimeError(\n                'xlutils is not installed,'\n                'Please use “pip install xlutils==2.0.0” to install')\n        readbook = xlrd.open_workbook(args.excel)\n        sheet = readbook.sheet_by_name('Sheet1')\n        sheet_info = {}\n        total_nrows = sheet.nrows\n        for i in range(3, sheet.nrows):\n            sheet_info[sheet.row_values(i)[0]] = i\n        xlrw = copy(readbook)\n        table = xlrw.get_sheet(0)\n\n    root_path = args.root\n    metrics_out = args.out\n\n    result_dict = {}\n    with open(args.txt_path, 'r') as f:\n        model_cfgs = f.readlines()\n        for i, config in enumerate(model_cfgs):\n            config = config.strip()\n            if len(config) == 0:\n                continue\n\n            config_name = osp.split(config)[-1]\n            config_name = osp.splitext(config_name)[0]\n            result_path = osp.join(root_path, config_name)\n            if osp.exists(result_path):\n                # 1 read config\n                cfg = Config.fromfile(config)\n                total_epochs = cfg.runner.max_epochs\n                final_results = cfg.evaluation.metric\n                if not isinstance(final_results, list):\n                    final_results = [final_results]\n                final_results_out = []\n                for key in final_results:\n                    if 'proposal_fast' in key:\n                        final_results_out.append('AR@1000')  # RPN\n                    elif 'mAP' not in key:\n                        final_results_out.append(key + '_mAP')\n\n                # 2 determine whether total_epochs ckpt exists\n                ckpt_path = f'epoch_{total_epochs}.pth'\n                if osp.exists(osp.join(result_path, ckpt_path)):\n                    log_json_path = list(\n                        sorted(glob.glob(osp.join(result_path,\n                                                  '*.log.json'))))[-1]\n\n                    # 3 read metric\n                    model_performance = get_final_results(\n                        log_json_path, total_epochs, final_results_out)\n                    if model_performance is None:\n                        print(f'log file error: {log_json_path}')\n                        continue\n                    for performance in model_performance:\n                        if performance in ['AR@1000', 'bbox_mAP', 'segm_mAP']:\n                            metric = round(\n                                model_performance[performance] * 100, 1)\n                            model_performance[performance] = metric\n                    result_dict[config] = model_performance\n\n                    # update and append excel content\n                    if args.excel:\n                        if 'AR@1000' in model_performance:\n                            metrics = f'{model_performance[\"AR@1000\"]}' \\\n                                      f'(AR@1000)'\n                        elif 'segm_mAP' in model_performance:\n                            metrics = f'{model_performance[\"bbox_mAP\"]}/' \\\n                                      f'{model_performance[\"segm_mAP\"]}'\n                        else:\n                            metrics = f'{model_performance[\"bbox_mAP\"]}'\n\n                        row_num = sheet_info.get(config, None)\n                        if row_num:\n                            table.write(row_num, args.ncol, metrics)\n                        else:\n                            table.write(total_nrows, 0, config)\n                            table.write(total_nrows, args.ncol, metrics)\n                            total_nrows += 1\n\n                else:\n                    print(f'{config} not exist: {ckpt_path}')\n            else:\n                print(f'not exist: {config}')\n\n        # 4 save or print results\n        if metrics_out:\n            mkdir_or_exist(metrics_out)\n            dump(result_dict, osp.join(metrics_out, 'model_metric_info.json'))\n        if not args.not_show:\n            print('===================================')\n            for config_name, metrics in result_dict.items():\n                print(config_name, metrics)\n            print('===================================')\n        if args.excel:\n            filename, sufflx = osp.splitext(args.excel)\n            xlrw.save(f'{filename}_o{sufflx}')\n            print(f'>>> Output {filename}_o{sufflx}')\n"
  },
  {
    "path": ".dev_scripts/linter.sh",
    "content": "yapf -r -i mmdet/ configs/ tests/ tools/\nisort -rc mmdet/ configs/ tests/ tools/\nflake8 .\n"
  },
  {
    "path": ".dev_scripts/test_benchmark.sh",
    "content": "PARTITION=$1\nCHECKPOINT_DIR=$2\nWORK_DIR=$3\nCPUS_PER_TASK=${4:-2}\n\necho 'configs/atss/atss_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION atss_r50_fpn_1x_coco configs/atss/atss_r50_fpn_1x_coco.py $CHECKPOINT_DIR/atss_r50_fpn_1x_coco_20200209-985f7bd0.pth --work-dir $WORK_DIR/atss_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29666  &\necho 'configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION autoassign_r50-caffe_fpn_1x_coco configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py $CHECKPOINT_DIR/auto_assign_r50_fpn_1x_coco_20210413_115540-5e17991f.pth --work-dir $WORK_DIR/autoassign_r50-caffe_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29667  &\necho 'configs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50_fpn-carafe_1x_coco configs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_fpn_carafe_1x_coco_bbox_mAP-0.386_20200504_175733-385a75b7.pth --work-dir $WORK_DIR/faster-rcnn_r50_fpn-carafe_1x_coco --cfg-option env_cfg.dist_cfg.port=29668  &\necho 'configs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION cascade-rcnn_r50_fpn_1x_coco configs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/cascade_rcnn_r50_fpn_1x_coco_20200316-3dc56deb.pth --work-dir $WORK_DIR/cascade-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29669  &\necho 'configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION cascade-mask-rcnn_r50_fpn_1x_coco configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth --work-dir $WORK_DIR/cascade-mask-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29670  &\necho 'configs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco configs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py $CHECKPOINT_DIR/crpn_faster_rcnn_r50_caffe_fpn_1x_coco-c8283cca.pth --work-dir $WORK_DIR/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29671  &\necho 'configs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION centernet_r18-dcnv2_8xb16-crop512-140e_coco configs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py $CHECKPOINT_DIR/centernet_resnet18_dcnv2_140e_coco_20210702_155131-c8cd631f.pth --work-dir $WORK_DIR/centernet_r18-dcnv2_8xb16-crop512-140e_coco --cfg-option env_cfg.dist_cfg.port=29672  &\necho 'configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py $CHECKPOINT_DIR/centripetalnet_hourglass104_mstest_16x6_210e_coco_20200915_204804-3ccc61e5.pth --work-dir $WORK_DIR/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco --cfg-option env_cfg.dist_cfg.port=29673  &\necho 'configs/convnext/cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco configs/convnext/cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py $CHECKPOINT_DIR/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220510_201004-3d24f5a4.pth --work-dir $WORK_DIR/cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco --cfg-option env_cfg.dist_cfg.port=29674  &\necho 'configs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION cornernet_hourglass104_8xb6-210e-mstest_coco configs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py $CHECKPOINT_DIR/cornernet_hourglass104_mstest_8x6_210e_coco_20200825_150618-79b44c30.pth --work-dir $WORK_DIR/cornernet_hourglass104_8xb6-210e-mstest_coco --cfg-option env_cfg.dist_cfg.port=29675  &\necho 'configs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco configs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130-d68aed1e.pth --work-dir $WORK_DIR/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29676  &\necho 'configs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50_fpn_mdpool_1x_coco configs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_fpn_mdpool_1x_coco_20200307-c0df27ff.pth --work-dir $WORK_DIR/faster-rcnn_r50_fpn_mdpool_1x_coco --cfg-option env_cfg.dist_cfg.port=29677  &\necho 'configs/ddod/ddod_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION ddod_r50_fpn_1x_coco configs/ddod/ddod_r50_fpn_1x_coco.py $CHECKPOINT_DIR/ddod_r50_fpn_1x_coco_20220523_223737-29b2fc67.pth --work-dir $WORK_DIR/ddod_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29678  &\necho 'configs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION deformable-detr_r50_16xb2-50e_coco configs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py $CHECKPOINT_DIR/deformable_detr_r50_16x2_50e_coco_20210419_220030-a12b9512.pth --work-dir $WORK_DIR/deformable-detr_r50_16xb2-50e_coco --cfg-option env_cfg.dist_cfg.port=29679  &\necho 'configs/detectors/detectors_htc-r50_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION detectors_htc-r50_1x_coco configs/detectors/detectors_htc-r50_1x_coco.py $CHECKPOINT_DIR/detectors_htc_r50_1x_coco-329b1453.pth --work-dir $WORK_DIR/detectors_htc-r50_1x_coco --cfg-option env_cfg.dist_cfg.port=29680  &\necho 'configs/detr/detr_r50_8xb2-150e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION detr_r50_8xb2-150e_coco configs/detr/detr_r50_8xb2-150e_coco.py $CHECKPOINT_DIR/detr_r50_8x2_150e_coco_20201130_194835-2c4b8974.pth --work-dir $WORK_DIR/detr_r50_8xb2-150e_coco --cfg-option env_cfg.dist_cfg.port=29681  &\necho 'configs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION dh-faster-rcnn_r50_fpn_1x_coco configs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/dh_faster_rcnn_r50_fpn_1x_coco_20200130-586b67df.pth --work-dir $WORK_DIR/dh-faster-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29682  &\necho 'configs/dyhead/atss_r50_fpn_dyhead_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION atss_r50_fpn_dyhead_1x_coco configs/dyhead/atss_r50_fpn_dyhead_1x_coco.py $CHECKPOINT_DIR/atss_r50_fpn_dyhead_4x4_1x_coco_20211219_023314-eaa620c6.pth --work-dir $WORK_DIR/atss_r50_fpn_dyhead_1x_coco --cfg-option env_cfg.dist_cfg.port=29683  &\necho 'configs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION dynamic-rcnn_r50_fpn_1x_coco configs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/dynamic_rcnn_r50_fpn_1x-62a3f276.pth --work-dir $WORK_DIR/dynamic-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29684  &\necho 'configs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION retinanet_effb3_fpn_8xb4-crop896-1x_coco configs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py $CHECKPOINT_DIR/retinanet_effb3_fpn_crop896_8x4_1x_coco_20220322_234806-615a0dda.pth --work-dir $WORK_DIR/retinanet_effb3_fpn_8xb4-crop896-1x_coco --cfg-option env_cfg.dist_cfg.port=29685  &\necho 'configs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50-attn1111_fpn_1x_coco configs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_fpn_attention_1111_1x_coco_20200130-403cccba.pth --work-dir $WORK_DIR/faster-rcnn_r50-attn1111_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29686  &\necho 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50_fpn_1x_coco configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth --work-dir $WORK_DIR/faster-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29687  &\necho 'configs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco configs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py $CHECKPOINT_DIR/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco-0a0d75a8.pth --work-dir $WORK_DIR/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco --cfg-option env_cfg.dist_cfg.port=29688  &\necho 'configs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION fovea_r50_fpn_gn-head-align_4xb4-2x_coco configs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py $CHECKPOINT_DIR/fovea_align_r50_fpn_gn-head_4x4_2x_coco_20200203-8987880d.pth --work-dir $WORK_DIR/fovea_r50_fpn_gn-head-align_4xb4-2x_coco --cfg-option env_cfg.dist_cfg.port=29689  &\necho 'configs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_r50_fpg_crop640-50e_coco configs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py $CHECKPOINT_DIR/mask_rcnn_r50_fpg_crop640_50e_coco_20220311_011857-233b8334.pth --work-dir $WORK_DIR/mask-rcnn_r50_fpg_crop640-50e_coco --cfg-option env_cfg.dist_cfg.port=29690  &\necho 'configs/free_anchor/freeanchor_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION freeanchor_r50_fpn_1x_coco configs/free_anchor/freeanchor_r50_fpn_1x_coco.py $CHECKPOINT_DIR/retinanet_free_anchor_r50_fpn_1x_coco_20200130-0f67375f.pth --work-dir $WORK_DIR/freeanchor_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29691  &\necho 'configs/fsaf/fsaf_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION fsaf_r50_fpn_1x_coco configs/fsaf/fsaf_r50_fpn_1x_coco.py $CHECKPOINT_DIR/fsaf_r50_fpn_1x_coco-94ccc51f.pth --work-dir $WORK_DIR/fsaf_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29692  &\necho 'configs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco configs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py $CHECKPOINT_DIR/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200202-587b99aa.pth --work-dir $WORK_DIR/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29693  &\necho 'configs/gfl/gfl_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION gfl_r50_fpn_1x_coco configs/gfl/gfl_r50_fpn_1x_coco.py $CHECKPOINT_DIR/gfl_r50_fpn_1x_coco_20200629_121244-25944287.pth --work-dir $WORK_DIR/gfl_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29694  &\necho 'configs/ghm/retinanet_r50_fpn_ghm-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION retinanet_r50_fpn_ghm-1x_coco configs/ghm/retinanet_r50_fpn_ghm-1x_coco.py $CHECKPOINT_DIR/retinanet_ghm_r50_fpn_1x_coco_20200130-a437fda3.pth --work-dir $WORK_DIR/retinanet_r50_fpn_ghm-1x_coco --cfg-option env_cfg.dist_cfg.port=29695  &\necho 'configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_r50_fpn_gn-all_2x_coco configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py $CHECKPOINT_DIR/mask_rcnn_r50_fpn_gn-all_2x_coco_20200206-8eee02a6.pth --work-dir $WORK_DIR/mask-rcnn_r50_fpn_gn-all_2x_coco --cfg-option env_cfg.dist_cfg.port=29696  &\necho 'configs/gn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50_fpn_gn-ws-all_1x_coco configs/gn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_fpn_gn_ws-all_1x_coco_20200130-613d9fe2.pth --work-dir $WORK_DIR/faster-rcnn_r50_fpn_gn-ws-all_1x_coco --cfg-option env_cfg.dist_cfg.port=29697  &\necho 'configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION grid-rcnn_r50_fpn_gn-head_2x_coco configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py $CHECKPOINT_DIR/grid_rcnn_r50_fpn_gn-head_2x_coco_20200130-6cca8223.pth --work-dir $WORK_DIR/grid-rcnn_r50_fpn_gn-head_2x_coco --cfg-option env_cfg.dist_cfg.port=29698  &\necho 'configs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faste-rcnn_r50_fpn_groie_1x_coco configs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_fpn_groie_1x_coco_20200604_211715-66ee9516.pth --work-dir $WORK_DIR/faste-rcnn_r50_fpn_groie_1x_coco --cfg-option env_cfg.dist_cfg.port=29699  &\necho 'configs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION ga-retinanet_r50-caffe_fpn_1x_coco configs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py $CHECKPOINT_DIR/ga_retinanet_r50_caffe_fpn_1x_coco_20201020-39581c6f.pth --work-dir $WORK_DIR/ga-retinanet_r50-caffe_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29700  &\necho 'configs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_hrnetv2p-w18-1x_coco configs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py $CHECKPOINT_DIR/faster_rcnn_hrnetv2p_w18_1x_coco_20200130-56651a6d.pth --work-dir $WORK_DIR/faster-rcnn_hrnetv2p-w18-1x_coco --cfg-option env_cfg.dist_cfg.port=29701  &\necho 'configs/htc/htc_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION htc_r50_fpn_1x_coco configs/htc/htc_r50_fpn_1x_coco.py $CHECKPOINT_DIR/htc_r50_fpn_1x_coco_20200317-7332cf16.pth --work-dir $WORK_DIR/htc_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29702  &\necho 'configs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_r50_fpn_instaboost-4x_coco configs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py $CHECKPOINT_DIR/mask_rcnn_r50_fpn_instaboost_4x_coco_20200307-d025f83a.pth --work-dir $WORK_DIR/mask-rcnn_r50_fpn_instaboost-4x_coco --cfg-option env_cfg.dist_cfg.port=29703  &\necho 'configs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION libra-faster-rcnn_r50_fpn_1x_coco configs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/libra_faster_rcnn_r50_fpn_1x_coco_20200130-3afee3a9.pth --work-dir $WORK_DIR/libra-faster-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29704  &\necho 'configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask2former_r50_8xb2-lsj-50e_coco-panoptic configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py $CHECKPOINT_DIR/mask2former_r50_lsj_8x2_50e_coco-panoptic_20220326_224516-11a44721.pth --work-dir $WORK_DIR/mask2former_r50_8xb2-lsj-50e_coco-panoptic --cfg-option env_cfg.dist_cfg.port=29705  &\necho 'configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_r50_fpn_1x_coco configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth --work-dir $WORK_DIR/mask-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29706  &\necho 'configs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION maskformer_r50_ms-16xb1-75e_coco configs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py $CHECKPOINT_DIR/maskformer_r50_mstrain_16x1_75e_coco_20220221_141956-bc2699cb.pth --work-dir $WORK_DIR/maskformer_r50_ms-16xb1-75e_coco --cfg-option env_cfg.dist_cfg.port=29707  &\necho 'configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION ms-rcnn_r50-caffe_fpn_1x_coco configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py $CHECKPOINT_DIR/ms_rcnn_r50_caffe_fpn_1x_coco_20200702_180848-61c9355e.pth --work-dir $WORK_DIR/ms-rcnn_r50-caffe_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29708  &\necho 'configs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco configs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py $CHECKPOINT_DIR/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200520-1bdba3ce.pth --work-dir $WORK_DIR/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco --cfg-option env_cfg.dist_cfg.port=29709  &\necho 'configs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION retinanet_r50_nasfpn_crop640-50e_coco configs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py $CHECKPOINT_DIR/retinanet_r50_nasfpn_crop640_50e_coco-0ad1f644.pth --work-dir $WORK_DIR/retinanet_r50_nasfpn_crop640-50e_coco --cfg-option env_cfg.dist_cfg.port=29710  &\necho 'configs/paa/paa_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION paa_r50_fpn_1x_coco configs/paa/paa_r50_fpn_1x_coco.py $CHECKPOINT_DIR/paa_r50_fpn_1x_coco_20200821-936edec3.pth --work-dir $WORK_DIR/paa_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29711  &\necho 'configs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50_pafpn_1x_coco configs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py $CHECKPOINT_DIR/faster_rcnn_r50_pafpn_1x_coco_bbox_mAP-0.375_20200503_105836-b7b4b9bd.pth --work-dir $WORK_DIR/faster-rcnn_r50_pafpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29712  &\necho 'configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION panoptic-fpn_r50_fpn_1x_coco configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/panoptic_fpn_r50_fpn_1x_coco_20210821_101153-9668fd13.pth --work-dir $WORK_DIR/panoptic-fpn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29713  &\necho 'configs/pisa/faster-rcnn_r50_fpn_pisa_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_r50_fpn_pisa_1x_coco configs/pisa/faster-rcnn_r50_fpn_pisa_1x_coco.py $CHECKPOINT_DIR/pisa_faster_rcnn_r50_fpn_1x_coco-dea93523.pth --work-dir $WORK_DIR/faster-rcnn_r50_fpn_pisa_1x_coco --cfg-option env_cfg.dist_cfg.port=29714  &\necho 'configs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION point-rend_r50-caffe_fpn_ms-1x_coco configs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py $CHECKPOINT_DIR/point_rend_r50_caffe_fpn_mstrain_1x_coco-1bcb5fb4.pth --work-dir $WORK_DIR/point-rend_r50-caffe_fpn_ms-1x_coco --cfg-option env_cfg.dist_cfg.port=29715  &\necho 'configs/pvt/retinanet_pvt-s_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION retinanet_pvt-s_fpn_1x_coco configs/pvt/retinanet_pvt-s_fpn_1x_coco.py $CHECKPOINT_DIR/retinanet_pvt-s_fpn_1x_coco_20210906_142921-b6c94a5b.pth --work-dir $WORK_DIR/retinanet_pvt-s_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29716  &\necho 'configs/queryinst/queryinst_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION queryinst_r50_fpn_1x_coco configs/queryinst/queryinst_r50_fpn_1x_coco.py $CHECKPOINT_DIR/queryinst_r50_fpn_1x_coco_20210907_084916-5a8f1998.pth --work-dir $WORK_DIR/queryinst_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29717  &\necho 'configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_regnetx-3.2GF_fpn_1x_coco configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py $CHECKPOINT_DIR/mask_rcnn_regnetx-3.2GF_fpn_1x_coco_20200520_163141-2a9d1814.pth --work-dir $WORK_DIR/mask-rcnn_regnetx-3.2GF_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29718  &\necho 'configs/reppoints/reppoints-moment_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION reppoints-moment_r50_fpn_1x_coco configs/reppoints/reppoints-moment_r50_fpn_1x_coco.py $CHECKPOINT_DIR/reppoints_moment_r50_fpn_1x_coco_20200330-b73db8d1.pth --work-dir $WORK_DIR/reppoints-moment_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29719  &\necho 'configs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_res2net-101_fpn_2x_coco configs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py $CHECKPOINT_DIR/faster_rcnn_r2_101_fpn_2x_coco-175f1da6.pth --work-dir $WORK_DIR/faster-rcnn_res2net-101_fpn_2x_coco --cfg-option env_cfg.dist_cfg.port=29720  &\necho 'configs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco configs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py $CHECKPOINT_DIR/faster_rcnn_s50_fpn_syncbn-backbone+head_mstrain-range_1x_coco_20200926_125502-20289c16.pth --work-dir $WORK_DIR/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco --cfg-option env_cfg.dist_cfg.port=29721  &\necho 'configs/resnet_strikes_back/mask-rcnn_r50-rsb-pre_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_r50-rsb-pre_fpn_1x_coco configs/resnet_strikes_back/mask-rcnn_r50-rsb-pre_fpn_1x_coco.py $CHECKPOINT_DIR/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_174054-06ce8ba0.pth --work-dir $WORK_DIR/mask-rcnn_r50-rsb-pre_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29722  &\necho 'configs/retinanet/retinanet_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION retinanet_r50_fpn_1x_coco configs/retinanet/retinanet_r50_fpn_1x_coco.py $CHECKPOINT_DIR/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth --work-dir $WORK_DIR/retinanet_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29723  &\necho 'configs/rpn/rpn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION rpn_r50_fpn_1x_coco configs/rpn/rpn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/rpn_r50_fpn_1x_coco_20200218-5525fa2e.pth --work-dir $WORK_DIR/rpn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29724  &\necho 'configs/sabl/sabl-retinanet_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION sabl-retinanet_r50_fpn_1x_coco configs/sabl/sabl-retinanet_r50_fpn_1x_coco.py $CHECKPOINT_DIR/sabl_retinanet_r50_fpn_1x_coco-6c54fd4f.pth --work-dir $WORK_DIR/sabl-retinanet_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29725  &\necho 'configs/sabl/sabl-faster-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION sabl-faster-rcnn_r50_fpn_1x_coco configs/sabl/sabl-faster-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/sabl_faster_rcnn_r50_fpn_1x_coco-e867595b.pth --work-dir $WORK_DIR/sabl-faster-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29726  &\necho 'configs/scnet/scnet_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION scnet_r50_fpn_1x_coco configs/scnet/scnet_r50_fpn_1x_coco.py $CHECKPOINT_DIR/scnet_r50_fpn_1x_coco-c3f09857.pth --work-dir $WORK_DIR/scnet_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29727  &\necho 'configs/scratch/mask-rcnn_r50-scratch_fpn_gn-all_6x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_r50-scratch_fpn_gn-all_6x_coco configs/scratch/mask-rcnn_r50-scratch_fpn_gn-all_6x_coco.py $CHECKPOINT_DIR/scratch_mask_rcnn_r50_fpn_gn_6x_bbox_mAP-0.412__segm_mAP-0.374_20200201_193051-1e190a40.pth --work-dir $WORK_DIR/mask-rcnn_r50-scratch_fpn_gn-all_6x_coco --cfg-option env_cfg.dist_cfg.port=29728  &\necho 'configs/solo/decoupled-solo_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION decoupled-solo_r50_fpn_1x_coco configs/solo/decoupled-solo_r50_fpn_1x_coco.py $CHECKPOINT_DIR/decoupled_solo_r50_fpn_1x_coco_20210820_233348-6337c589.pth --work-dir $WORK_DIR/decoupled-solo_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29729  &\necho 'configs/solov2/solov2_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION solov2_r50_fpn_1x_coco configs/solov2/solov2_r50_fpn_1x_coco.py $CHECKPOINT_DIR/solov2_r50_fpn_1x_coco_20220512_125858-a357fa23.pth --work-dir $WORK_DIR/solov2_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29730  &\necho 'configs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION sparse-rcnn_r50_fpn_1x_coco configs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py $CHECKPOINT_DIR/sparse_rcnn_r50_fpn_1x_coco_20201222_214453-dc79b137.pth --work-dir $WORK_DIR/sparse-rcnn_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29731  &\necho 'configs/ssd/ssd300_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION ssd300_coco configs/ssd/ssd300_coco.py $CHECKPOINT_DIR/ssd300_coco_20210803_015428-d231a06e.pth --work-dir $WORK_DIR/ssd300_coco --cfg-option env_cfg.dist_cfg.port=29732  &\necho 'configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION ssdlite_mobilenetv2-scratch_8xb24-600e_coco configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py $CHECKPOINT_DIR/ssdlite_mobilenetv2_scratch_600e_coco_20210629_110627-974d9307.pth --work-dir $WORK_DIR/ssdlite_mobilenetv2-scratch_8xb24-600e_coco --cfg-option env_cfg.dist_cfg.port=29733  &\necho 'configs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION mask-rcnn_swin-t-p4-w7_fpn_1x_coco configs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py $CHECKPOINT_DIR/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth --work-dir $WORK_DIR/mask-rcnn_swin-t-p4-w7_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29734  &\necho 'configs/tood/tood_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION tood_r50_fpn_1x_coco configs/tood/tood_r50_fpn_1x_coco.py $CHECKPOINT_DIR/tood_r50_fpn_1x_coco_20211210_103425-20e20746.pth --work-dir $WORK_DIR/tood_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29735  &\necho 'configs/tridentnet/tridentnet_r50-caffe_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION tridentnet_r50-caffe_1x_coco configs/tridentnet/tridentnet_r50-caffe_1x_coco.py $CHECKPOINT_DIR/tridentnet_r50_caffe_1x_coco_20201230_141838-2ec0b530.pth --work-dir $WORK_DIR/tridentnet_r50-caffe_1x_coco --cfg-option env_cfg.dist_cfg.port=29736  &\necho 'configs/vfnet/vfnet_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION vfnet_r50_fpn_1x_coco configs/vfnet/vfnet_r50_fpn_1x_coco.py $CHECKPOINT_DIR/vfnet_r50_fpn_1x_coco_20201027-38db6f58.pth --work-dir $WORK_DIR/vfnet_r50_fpn_1x_coco --cfg-option env_cfg.dist_cfg.port=29737  &\necho 'configs/yolact/yolact_r50_1xb8-55e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION yolact_r50_1xb8-55e_coco configs/yolact/yolact_r50_1xb8-55e_coco.py $CHECKPOINT_DIR/yolact_r50_1x8_coco_20200908-f38d58df.pth --work-dir $WORK_DIR/yolact_r50_1xb8-55e_coco --cfg-option env_cfg.dist_cfg.port=29738  &\necho 'configs/yolo/yolov3_d53_8xb8-320-273e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION yolov3_d53_8xb8-320-273e_coco configs/yolo/yolov3_d53_8xb8-320-273e_coco.py $CHECKPOINT_DIR/yolov3_d53_320_273e_coco-421362b6.pth --work-dir $WORK_DIR/yolov3_d53_8xb8-320-273e_coco --cfg-option env_cfg.dist_cfg.port=29739  &\necho 'configs/yolof/yolof_r50-c5_8xb8-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION yolof_r50-c5_8xb8-1x_coco configs/yolof/yolof_r50-c5_8xb8-1x_coco.py $CHECKPOINT_DIR/yolof_r50_c5_8x8_1x_coco_20210425_024427-8e864411.pth --work-dir $WORK_DIR/yolof_r50-c5_8xb8-1x_coco --cfg-option env_cfg.dist_cfg.port=29740  &\necho 'configs/yolox/yolox_tiny_8xb8-300e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK tools/slurm_test.sh $PARTITION yolox_tiny_8xb8-300e_coco configs/yolox/yolox_tiny_8xb8-300e_coco.py $CHECKPOINT_DIR/yolox_tiny_8x8_300e_coco_20211124_171234-b4047906.pth --work-dir $WORK_DIR/yolox_tiny_8xb8-300e_coco --cfg-option env_cfg.dist_cfg.port=29741  &\n"
  },
  {
    "path": ".dev_scripts/test_init_backbone.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Check out backbone whether successfully load pretrained checkpoint.\"\"\"\nimport copy\nimport os\nfrom os.path import dirname, exists, join\n\nimport pytest\nfrom mmengine.config import Config\nfrom mmengine.runner import CheckpointLoader\nfrom mmengine.utils import ProgressBar\n\nfrom mmdet.registry import MODELS\n\n\ndef _get_config_directory():\n    \"\"\"Find the predefined detector config directory.\"\"\"\n    try:\n        # Assume we are running in the source mmdetection repo\n        repo_dpath = dirname(dirname(__file__))\n    except NameError:\n        # For IPython development when this __file__ is not defined\n        import mmdet\n        repo_dpath = dirname(dirname(mmdet.__file__))\n    config_dpath = join(repo_dpath, 'configs')\n    if not exists(config_dpath):\n        raise Exception('Cannot find config path')\n    return config_dpath\n\n\ndef _get_config_module(fname):\n    \"\"\"Load a configuration as a python module.\"\"\"\n    config_dpath = _get_config_directory()\n    config_fpath = join(config_dpath, fname)\n    config_mod = Config.fromfile(config_fpath)\n    return config_mod\n\n\ndef _get_detector_cfg(fname):\n    \"\"\"Grab configs necessary to create a detector.\n\n    These are deep copied to allow for safe modification of parameters without\n    influencing other tests.\n    \"\"\"\n    config = _get_config_module(fname)\n    model = copy.deepcopy(config.model)\n    return model\n\n\ndef _traversed_config_file():\n    \"\"\"We traversed all potential config files under the `config` file. If you\n    need to print details or debug code, you can use this function.\n\n    If the `backbone.init_cfg` is None (do not use `Pretrained` init way), you\n    need add the folder name in `ignores_folder` (if the config files in this\n    folder all set backbone.init_cfg is None) or add config name in\n    `ignores_file` (if the config file set backbone.init_cfg is None)\n    \"\"\"\n    config_path = _get_config_directory()\n    check_cfg_names = []\n\n    # `base`, `legacy_1.x` and `common` ignored by default.\n    ignores_folder = ['_base_', 'legacy_1.x', 'common']\n    # 'ld' need load teacher model, if want to check 'ld',\n    # please check teacher_config path first.\n    ignores_folder += ['ld']\n    # `selfsup_pretrain` need convert model, if want to check this model,\n    # need to convert the model first.\n    ignores_folder += ['selfsup_pretrain']\n\n    # the `init_cfg` in 'centripetalnet', 'cornernet', 'cityscapes',\n    # 'scratch' is None.\n    # the `init_cfg` in ssdlite(`ssdlite_mobilenetv2_scratch_600e_coco.py`)\n    # is None\n    # Please confirm `bockbone.init_cfg` is None first.\n    ignores_folder += ['centripetalnet', 'cornernet', 'cityscapes', 'scratch']\n    ignores_file = ['ssdlite_mobilenetv2_scratch_600e_coco.py']\n\n    for config_file_name in os.listdir(config_path):\n        if config_file_name not in ignores_folder:\n            config_file = join(config_path, config_file_name)\n            if os.path.isdir(config_file):\n                for config_sub_file in os.listdir(config_file):\n                    if config_sub_file.endswith('py') and \\\n                            config_sub_file not in ignores_file:\n                        name = join(config_file, config_sub_file)\n                        check_cfg_names.append(name)\n    return check_cfg_names\n\n\ndef _check_backbone(config, print_cfg=True):\n    \"\"\"Check out backbone whether successfully load pretrained model, by using\n    `backbone.init_cfg`.\n\n    First, using `CheckpointLoader.load_checkpoint` to load the checkpoint\n        without loading models.\n    Then, using `MODELS.build` to build models, and using\n        `model.init_weights()` to initialize the parameters.\n    Finally, assert weights and bias of each layer loaded from pretrained\n        checkpoint are equal to the weights and bias of original checkpoint.\n        For the convenience of comparison, we sum up weights and bias of\n        each loaded layer separately.\n\n    Args:\n        config (str): Config file path.\n        print_cfg (bool): Whether print logger and return the result.\n\n    Returns:\n        results (str or None): If backbone successfully load pretrained\n            checkpoint, return None; else, return config file path.\n    \"\"\"\n    if print_cfg:\n        print('-' * 15 + 'loading ', config)\n    cfg = Config.fromfile(config)\n    init_cfg = None\n    try:\n        init_cfg = cfg.model.backbone.init_cfg\n        init_flag = True\n    except AttributeError:\n        init_flag = False\n    if init_cfg is None or init_cfg.get('type') != 'Pretrained':\n        init_flag = False\n    if init_flag:\n        checkpoint = CheckpointLoader.load_checkpoint(init_cfg.checkpoint)\n        if 'state_dict' in checkpoint:\n            state_dict = checkpoint['state_dict']\n        else:\n            state_dict = checkpoint\n\n        model = MODELS.build(cfg.model)\n        model.init_weights()\n\n        checkpoint_layers = state_dict.keys()\n        for name, value in model.backbone.state_dict().items():\n            if name in checkpoint_layers:\n                assert value.equal(state_dict[name])\n\n        if print_cfg:\n            print('-' * 10 + 'Successfully load checkpoint' + '-' * 10 +\n                  '\\n', )\n            return None\n    else:\n        if print_cfg:\n            print(config + '\\n' + '-' * 10 +\n                  'config file do not have init_cfg' + '-' * 10 + '\\n')\n            return config\n\n\n@pytest.mark.parametrize('config', _traversed_config_file())\ndef test_load_pretrained(config):\n    \"\"\"Check out backbone whether successfully load pretrained model by using\n    `backbone.init_cfg`.\n\n    Details please refer to `_check_backbone`\n    \"\"\"\n    _check_backbone(config, print_cfg=False)\n\n\ndef _test_load_pretrained():\n    \"\"\"We traversed all potential config files under the `config` file. If you\n    need to print details or debug code, you can use this function.\n\n    Returns:\n        check_cfg_names (list[str]): Config files that backbone initialized\n        from pretrained checkpoint might be problematic. Need to recheck\n        the config file. The output including the config files that the\n        backbone.init_cfg is None\n    \"\"\"\n    check_cfg_names = _traversed_config_file()\n    need_check_cfg = []\n\n    prog_bar = ProgressBar(len(check_cfg_names))\n    for config in check_cfg_names:\n        init_cfg_name = _check_backbone(config)\n        if init_cfg_name is not None:\n            need_check_cfg.append(init_cfg_name)\n        prog_bar.update()\n    print('These config files need to be checked again')\n    print(need_check_cfg)\n"
  },
  {
    "path": ".dev_scripts/train_benchmark.sh",
    "content": "PARTITION=$1\nWORK_DIR=$2\nCPUS_PER_TASK=${3:-4}\n\necho 'configs/albu_example/mask-rcnn_r50_fpn_albu_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_r50_fpn_albu_1x_coco configs/albu_example/mask-rcnn_r50_fpn_albu_1x_coco.py $WORK_DIR/mask-rcnn_r50_fpn_albu_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/atss/atss_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION atss_r50_fpn_1x_coco configs/atss/atss_r50_fpn_1x_coco.py $WORK_DIR/atss_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION autoassign_r50-caffe_fpn_1x_coco configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py $WORK_DIR/autoassign_r50-caffe_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50_fpn-carafe_1x_coco configs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py $WORK_DIR/faster-rcnn_r50_fpn-carafe_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION cascade-rcnn_r50_fpn_1x_coco configs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py $WORK_DIR/cascade-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION cascade-mask-rcnn_r50_fpn_1x_coco configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py $WORK_DIR/cascade-mask-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco configs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py $WORK_DIR/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION centernet_r18-dcnv2_8xb16-crop512-140e_coco configs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py $WORK_DIR/centernet_r18-dcnv2_8xb16-crop512-140e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION centernet-update_r50-caffe_fpn_ms-1x_coco configs/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco.py $WORK_DIR/centernet-update_r50-caffe_fpn_ms-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py' &\nGPUS=16  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py $WORK_DIR/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION cornernet_hourglass104_8xb6-210e-mstest_coco configs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py $WORK_DIR/cornernet_hourglass104_8xb6-210e-mstest_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/convnext/mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco configs/convnext/mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco.py $WORK_DIR/mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco configs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py $WORK_DIR/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50_fpn_mdpool_1x_coco configs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py $WORK_DIR/faster-rcnn_r50_fpn_mdpool_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/ddod/ddod_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION ddod_r50_fpn_1x_coco configs/ddod/ddod_r50_fpn_1x_coco.py $WORK_DIR/ddod_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/detectors/detectors_htc-r50_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION detectors_htc-r50_1x_coco configs/detectors/detectors_htc-r50_1x_coco.py $WORK_DIR/detectors_htc-r50_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py' &\nGPUS=16  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION deformable-detr_r50_16xb2-50e_coco configs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py $WORK_DIR/deformable-detr_r50_16xb2-50e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/detr/detr_r50_8xb2-150e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION detr_r50_8xb2-150e_coco configs/detr/detr_r50_8xb2-150e_coco.py $WORK_DIR/detr_r50_8xb2-150e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION dh-faster-rcnn_r50_fpn_1x_coco configs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py $WORK_DIR/dh-faster-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION dynamic-rcnn_r50_fpn_1x_coco configs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py $WORK_DIR/dynamic-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/dyhead/atss_r50_fpn_dyhead_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION atss_r50_fpn_dyhead_1x_coco configs/dyhead/atss_r50_fpn_dyhead_1x_coco.py $WORK_DIR/atss_r50_fpn_dyhead_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION retinanet_effb3_fpn_8xb4-crop896-1x_coco configs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py $WORK_DIR/retinanet_effb3_fpn_8xb4-crop896-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50-attn1111_fpn_1x_coco configs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py $WORK_DIR/faster-rcnn_r50-attn1111_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50_fpn_1x_coco configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py $WORK_DIR/faster-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_ms-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50-caffe-dc5_ms-1x_coco configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_ms-1x_coco.py $WORK_DIR/faster-rcnn_r50-caffe-dc5_ms-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco configs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py $WORK_DIR/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py' &\nGPUS=4  GPUS_PER_NODE=4  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION fovea_r50_fpn_gn-head-align_4xb4-2x_coco configs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py $WORK_DIR/fovea_r50_fpn_gn-head-align_4xb4-2x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_r50_fpg_crop640-50e_coco configs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py $WORK_DIR/mask-rcnn_r50_fpg_crop640-50e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/free_anchor/freeanchor_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION freeanchor_r50_fpn_1x_coco configs/free_anchor/freeanchor_r50_fpn_1x_coco.py $WORK_DIR/freeanchor_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/fsaf/fsaf_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION fsaf_r50_fpn_1x_coco configs/fsaf/fsaf_r50_fpn_1x_coco.py $WORK_DIR/fsaf_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco configs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py $WORK_DIR/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/gfl/gfl_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION gfl_r50_fpn_1x_coco configs/gfl/gfl_r50_fpn_1x_coco.py $WORK_DIR/gfl_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/ghm/retinanet_r50_fpn_ghm-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION retinanet_r50_fpn_ghm-1x_coco configs/ghm/retinanet_r50_fpn_ghm-1x_coco.py $WORK_DIR/retinanet_r50_fpn_ghm-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_r50_fpn_gn-all_2x_coco configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py $WORK_DIR/mask-rcnn_r50_fpn_gn-all_2x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/gn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50_fpn_gn-ws-all_1x_coco configs/gn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py $WORK_DIR/faster-rcnn_r50_fpn_gn-ws-all_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION grid-rcnn_r50_fpn_gn-head_2x_coco configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py $WORK_DIR/grid-rcnn_r50_fpn_gn-head_2x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faste-rcnn_r50_fpn_groie_1x_coco configs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py $WORK_DIR/faste-rcnn_r50_fpn_groie_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION ga-retinanet_r50-caffe_fpn_1x_coco configs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py $WORK_DIR/ga-retinanet_r50-caffe_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_hrnetv2p-w18-1x_coco configs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py $WORK_DIR/faster-rcnn_hrnetv2p-w18-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/htc/htc_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION htc_r50_fpn_1x_coco configs/htc/htc_r50_fpn_1x_coco.py $WORK_DIR/htc_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_r50_fpn_instaboost-4x_coco configs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py $WORK_DIR/mask-rcnn_r50_fpn_instaboost-4x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/lad/lad_r50-paa-r101_fpn_2xb8_coco_1x.py' &\nGPUS=2  GPUS_PER_NODE=2  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION lad_r50-paa-r101_fpn_2xb8_coco_1x configs/lad/lad_r50-paa-r101_fpn_2xb8_coco_1x.py $WORK_DIR/lad_r50-paa-r101_fpn_2xb8_coco_1x --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/ld/ld_r18-gflv1-r101_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION ld_r18-gflv1-r101_fpn_1x_coco configs/ld/ld_r18-gflv1-r101_fpn_1x_coco.py $WORK_DIR/ld_r18-gflv1-r101_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION libra-faster-rcnn_r50_fpn_1x_coco configs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py $WORK_DIR/libra-faster-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask2former_r50_8xb2-lsj-50e_coco-panoptic configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py $WORK_DIR/mask2former_r50_8xb2-lsj-50e_coco-panoptic --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_r50_fpn_1x_coco configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py $WORK_DIR/mask-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py' &\nGPUS=16  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION maskformer_r50_ms-16xb1-75e_coco configs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py $WORK_DIR/maskformer_r50_ms-16xb1-75e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION ms-rcnn_r50-caffe_fpn_1x_coco configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py $WORK_DIR/ms-rcnn_r50-caffe_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py' &\nGPUS=4  GPUS_PER_NODE=4  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco configs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py $WORK_DIR/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION retinanet_r50_nasfpn_crop640-50e_coco configs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py $WORK_DIR/retinanet_r50_nasfpn_crop640-50e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/paa/paa_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION paa_r50_fpn_1x_coco configs/paa/paa_r50_fpn_1x_coco.py $WORK_DIR/paa_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50_pafpn_1x_coco configs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py $WORK_DIR/faster-rcnn_r50_pafpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION panoptic-fpn_r50_fpn_1x_coco configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py $WORK_DIR/panoptic-fpn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/pisa/mask-rcnn_r50_fpn_pisa_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_r50_fpn_pisa_1x_coco configs/pisa/mask-rcnn_r50_fpn_pisa_1x_coco.py $WORK_DIR/mask-rcnn_r50_fpn_pisa_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION point-rend_r50-caffe_fpn_ms-1x_coco configs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py $WORK_DIR/point-rend_r50-caffe_fpn_ms-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/pvt/retinanet_pvt-t_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION retinanet_pvt-t_fpn_1x_coco configs/pvt/retinanet_pvt-t_fpn_1x_coco.py $WORK_DIR/retinanet_pvt-t_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/queryinst/queryinst_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION queryinst_r50_fpn_1x_coco configs/queryinst/queryinst_r50_fpn_1x_coco.py $WORK_DIR/queryinst_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/regnet/retinanet_regnetx-800MF_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION retinanet_regnetx-800MF_fpn_1x_coco configs/regnet/retinanet_regnetx-800MF_fpn_1x_coco.py $WORK_DIR/retinanet_regnetx-800MF_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/reppoints/reppoints-moment_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION reppoints-moment_r50_fpn_1x_coco configs/reppoints/reppoints-moment_r50_fpn_1x_coco.py $WORK_DIR/reppoints-moment_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_res2net-101_fpn_2x_coco configs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py $WORK_DIR/faster-rcnn_res2net-101_fpn_2x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco configs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py $WORK_DIR/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/resnet_strikes_back/retinanet_r50-rsb-pre_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION retinanet_r50-rsb-pre_fpn_1x_coco configs/resnet_strikes_back/retinanet_r50-rsb-pre_fpn_1x_coco.py $WORK_DIR/retinanet_r50-rsb-pre_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/retinanet/retinanet_r50-caffe_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION retinanet_r50-caffe_fpn_1x_coco configs/retinanet/retinanet_r50-caffe_fpn_1x_coco.py $WORK_DIR/retinanet_r50-caffe_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/rpn/rpn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION rpn_r50_fpn_1x_coco configs/rpn/rpn_r50_fpn_1x_coco.py $WORK_DIR/rpn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/sabl/sabl-retinanet_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION sabl-retinanet_r50_fpn_1x_coco configs/sabl/sabl-retinanet_r50_fpn_1x_coco.py $WORK_DIR/sabl-retinanet_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/scnet/scnet_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION scnet_r50_fpn_1x_coco configs/scnet/scnet_r50_fpn_1x_coco.py $WORK_DIR/scnet_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/scratch/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION faster-rcnn_r50-scratch_fpn_gn-all_6x_coco configs/scratch/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py $WORK_DIR/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/solo/solo_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION solo_r50_fpn_1x_coco configs/solo/solo_r50_fpn_1x_coco.py $WORK_DIR/solo_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/solov2/solov2_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION solov2_r50_fpn_1x_coco configs/solov2/solov2_r50_fpn_1x_coco.py $WORK_DIR/solov2_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION sparse-rcnn_r50_fpn_1x_coco configs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py $WORK_DIR/sparse-rcnn_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/ssd/ssd300_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION ssd300_coco configs/ssd/ssd300_coco.py $WORK_DIR/ssd300_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION ssdlite_mobilenetv2-scratch_8xb24-600e_coco configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py $WORK_DIR/ssdlite_mobilenetv2-scratch_8xb24-600e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION mask-rcnn_swin-t-p4-w7_fpn_1x_coco configs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py $WORK_DIR/mask-rcnn_swin-t-p4-w7_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/tood/tood_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION tood_r50_fpn_1x_coco configs/tood/tood_r50_fpn_1x_coco.py $WORK_DIR/tood_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho ''configs/tridentnet/tridentnet_r50-caffe_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION tridentnet_r50-caffe_1x_coco 'configs/tridentnet/tridentnet_r50-caffe_1x_coco.py $WORK_DIR/tridentnet_r50-caffe_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/vfnet/vfnet_r50_fpn_1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION vfnet_r50_fpn_1x_coco configs/vfnet/vfnet_r50_fpn_1x_coco.py $WORK_DIR/vfnet_r50_fpn_1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/yolact/yolact_r50_8xb8-55e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION yolact_r50_8xb8-55e_coco configs/yolact/yolact_r50_8xb8-55e_coco.py $WORK_DIR/yolact_r50_8xb8-55e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/yolo/yolov3_d53_8xb8-320-273e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION yolov3_d53_8xb8-320-273e_coco configs/yolo/yolov3_d53_8xb8-320-273e_coco.py $WORK_DIR/yolov3_d53_8xb8-320-273e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/yolof/yolof_r50-c5_8xb8-1x_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION yolof_r50-c5_8xb8-1x_coco configs/yolof/yolof_r50-c5_8xb8-1x_coco.py $WORK_DIR/yolof_r50-c5_8xb8-1x_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\necho 'configs/yolox/yolox_tiny_8xb8-300e_coco.py' &\nGPUS=8  GPUS_PER_NODE=8  CPUS_PER_TASK=$CPUS_PRE_TASK ./tools/slurm_train.sh $PARTITION yolox_tiny_8xb8-300e_coco configs/yolox/yolox_tiny_8xb8-300e_coco.py $WORK_DIR/yolox_tiny_8xb8-300e_coco --cfg-options default_hooks.checkpoint.max_keep_ckpts=1 &\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at chenkaidev@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n\n[homepage]: https://www.contributor-covenant.org\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "We appreciate all contributions to improve MMDetection. Please refer to [CONTRIBUTING.md](https://github.com/open-mmlab/mmcv/blob/master/CONTRIBUTING.md) in MMCV for more details about the contributing guideline.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n\ncontact_links:\n  - name: Common Issues\n    url: https://mmdetection.readthedocs.io/en/latest/faq.html\n    about: Check if your issue already has solutions\n  - name: MMDetection Documentation\n    url: https://mmdetection.readthedocs.io/en/latest/\n    about: Check if your question is answered in docs\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/error-report.md",
    "content": "---\nname: Error report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\nThanks for your error report and we appreciate it a lot.\n\n**Checklist**\n\n1. I have searched related issues but cannot get the expected help.\n2. I have read the [FAQ documentation](https://mmdetection.readthedocs.io/en/latest/faq.html) but cannot get the expected help.\n3. The bug has not been fixed in the latest version.\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**Reproduction**\n\n1. What command or script did you run?\n\n```none\nA placeholder for the command.\n```\n\n2. Did you make any modifications on the code or config? Did you understand what you have modified?\n3. What dataset did you use?\n\n**Environment**\n\n1. Please run `python mmdet/utils/collect_env.py` to collect necessary environment information and paste it here.\n2. You may add addition that may be helpful for locating the problem, such as\n   - How you installed PyTorch \\[e.g., pip, conda, source\\]\n   - Other environment variables that may be related (such as `$PATH`, `$LD_LIBRARY_PATH`, `$PYTHONPATH`, etc.)\n\n**Error traceback**\nIf applicable, paste the error trackback here.\n\n```none\nA placeholder for trackback.\n```\n\n**Bug fix**\nIf you have already identified the reason, you can provide the information here. If you are willing to create a PR to fix it, please also leave a comment here and that would be much appreciated!\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n**Describe the feature**\n\n**Motivation**\nA clear and concise description of the motivation of the feature.\nEx1. It is inconvenient when \\[....\\].\nEx2. There is a recent paper \\[....\\], which is very helpful for \\[....\\].\n\n**Related resources**\nIf there is an official code release or third-party implementations, please also provide the information here, which would be very helpful.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\nIf you would like to implement the feature and create a PR, please leave a comment here and that would be much appreciated.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/general_questions.md",
    "content": "---\nname: General questions\nabout: Ask general questions to get help\ntitle: ''\nlabels: ''\nassignees: ''\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/reimplementation_questions.md",
    "content": "---\nname: Reimplementation Questions\nabout: Ask about questions during model reimplementation\ntitle: ''\nlabels: reimplementation\nassignees: ''\n---\n\n**Notice**\n\nThere are several common situations in the reimplementation issues as below\n\n1. Reimplement a model in the model zoo using the provided configs\n2. Reimplement a model in the model zoo on other dataset (e.g., custom datasets)\n3. Reimplement a custom model but all the components are implemented in MMDetection\n4. Reimplement a custom model with new modules implemented by yourself\n\nThere are several things to do for different cases as below.\n\n- For case 1 & 3, please follow the steps in the following sections thus we could help to quick identify the issue.\n- For case 2 & 4, please understand that we are not able to do much help here because we usually do not know the full code and the users should be responsible to the code they write.\n- One suggestion for case 2 & 4 is that the users should first check whether the bug lies in the self-implemented code or the original code. For example, users can first make sure that the same model runs well on supported datasets. If you still need help, please describe what you have done and what you obtain in the issue, and follow the steps in the following sections and try as clear as possible so that we can better help you.\n\n**Checklist**\n\n1. I have searched related issues but cannot get the expected help.\n2. The issue has not been fixed in the latest version.\n\n**Describe the issue**\n\nA clear and concise description of what the problem you meet and what have you done.\n\n**Reproduction**\n\n1. What command or script did you run?\n\n```none\nA placeholder for the command.\n```\n\n2. What config dir you run?\n\n```none\nA placeholder for the config.\n```\n\n3. Did you make any modifications on the code or config? Did you understand what you have modified?\n4. What dataset did you use?\n\n**Environment**\n\n1. Please run `python mmdet/utils/collect_env.py` to collect necessary environment information and paste it here.\n2. You may add addition that may be helpful for locating the problem, such as\n   1. How you installed PyTorch \\[e.g., pip, conda, source\\]\n   2. Other environment variables that may be related (such as `$PATH`, `$LD_LIBRARY_PATH`, `$PYTHONPATH`, etc.)\n\n**Results**\n\nIf applicable, paste the related results here, e.g., what you expect and what you get.\n\n```none\nA placeholder for results comparison\n```\n\n**Issue fix**\n\nIf you have already identified the reason, you can provide the information here. If you are willing to create a PR to fix it, please also leave a comment here and that would be much appreciated!\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "Thanks for your contribution and we appreciate it a lot. The following instructions would make your pull request more healthy and more easily get feedback. If you do not understand some items, don't worry, just make the pull request and seek help from maintainers.\n\n## Motivation\n\nPlease describe the motivation of this PR and the goal you want to achieve through this PR.\n\n## Modification\n\nPlease briefly describe what modification is made in this PR.\n\n## BC-breaking (Optional)\n\nDoes the modification introduce changes that break the backward-compatibility of the downstream repos?\nIf so, please describe how it breaks the compatibility and how the downstream projects should modify their code to keep compatibility with this PR.\n\n## Use cases (Optional)\n\nIf this PR introduces a new feature, it is better to list some use cases here, and update the documentation.\n\n## Checklist\n\n1. Pre-commit or other linting tools are used to fix the potential lint issues.\n2. The modification is covered by complete unit tests. If not, please add more unit test to ensure the correctness.\n3. If the modification has potential influence on downstream projects, this PR should be tested with downstream projects, like MMDet or MMPreTrain.\n4. The documentation has been modified accordingly, like docstring or example tutorials.\n"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "content": "name: deploy\n\non: push\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build-n-publish:\n    runs-on: ubuntu-latest\n    if: startsWith(github.event.ref, 'refs/tags')\n    steps:\n      - uses: actions/checkout@v2\n      - name: Set up Python 3.7\n        uses: actions/setup-python@v2\n        with:\n          python-version: 3.7\n      - name: Install torch\n        run: pip install torch\n      - name: Install wheel\n        run: pip install wheel\n      - name: Build MMDetection\n        run: python setup.py sdist bdist_wheel\n      - name: Publish distribution to PyPI\n        run: |\n          pip install twine\n          twine upload dist/* -u __token__ -p ${{ secrets.pypi_password }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/en/_build/\ndocs/zh_cn/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\ndata/\ndata\n.vscode\n.idea\n.DS_Store\n\n# custom\n*.pkl\n*.pkl.json\n*.log.json\ndocs/modelzoo_statistics.md\nmmdet/.mim\nwork_dirs/\n\n# Pytorch\n*.pth\n*.py~\n*.sh~\n"
  },
  {
    "path": ".owners.yml",
    "content": "assign:\n  strategy:\n    # random\n    daily-shift-based\n  scedule:\n    '*/1 * * * *'\n  assignees:\n    - Czm369\n    - hhaAndroid\n    - jbwang1997\n    - RangiLyu\n    - BIGWangYuDong\n    - chhluo\n    - ZwwWayne\n"
  },
  {
    "path": ".pre-commit-config-zh-cn.yaml",
    "content": "exclude: ^tests/data/\nrepos:\n  - repo: https://gitee.com/openmmlab/mirrors-flake8\n    rev: 5.0.4\n    hooks:\n      - id: flake8\n  - repo: https://gitee.com/openmmlab/mirrors-isort\n    rev: 5.11.5\n    hooks:\n      - id: isort\n  - repo: https://gitee.com/openmmlab/mirrors-yapf\n    rev: v0.32.0\n    hooks:\n      - id: yapf\n  - repo: https://gitee.com/openmmlab/mirrors-pre-commit-hooks\n    rev: v4.3.0\n    hooks:\n      - id: trailing-whitespace\n      - id: check-yaml\n      - id: end-of-file-fixer\n      - id: requirements-txt-fixer\n      - id: double-quote-string-fixer\n      - id: check-merge-conflict\n      - id: fix-encoding-pragma\n        args: [\"--remove\"]\n      - id: mixed-line-ending\n        args: [\"--fix=lf\"]\n  - repo: https://gitee.com/openmmlab/mirrors-mdformat\n    rev: 0.7.9\n    hooks:\n      - id: mdformat\n        args: [\"--number\"]\n        additional_dependencies:\n          - mdformat-openmmlab\n          - mdformat_frontmatter\n          - linkify-it-py\n  - repo: https://gitee.com/openmmlab/mirrors-codespell\n    rev: v2.2.1\n    hooks:\n      - id: codespell\n  - repo: https://gitee.com/openmmlab/mirrors-docformatter\n    rev: v1.3.1\n    hooks:\n      - id: docformatter\n        args: [\"--in-place\", \"--wrap-descriptions\", \"79\"]\n  - repo: https://gitee.com/openmmlab/mirrors-pyupgrade\n    rev: v3.0.0\n    hooks:\n      - id: pyupgrade\n        args: [\"--py36-plus\"]\n  - repo: https://gitee.com/open-mmlab/pre-commit-hooks\n    rev: v0.2.0\n    hooks:\n      - id: check-algo-readme\n      - id: check-copyright\n        args: [\"mmdet\"]\n#  - repo: https://gitee.com/openmmlab/mirrors-mypy\n#    rev: v0.812\n#    hooks:\n#      - id: mypy\n#        exclude: \"docs\"\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/PyCQA/flake8\n    rev: 5.0.4\n    hooks:\n      - id: flake8\n  - repo: https://github.com/PyCQA/isort\n    rev: 5.11.5\n    hooks:\n      - id: isort\n  - repo: https://github.com/pre-commit/mirrors-yapf\n    rev: v0.32.0\n    hooks:\n      - id: yapf\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.3.0\n    hooks:\n      - id: trailing-whitespace\n      - id: check-yaml\n      - id: end-of-file-fixer\n      - id: requirements-txt-fixer\n      - id: double-quote-string-fixer\n      - id: check-merge-conflict\n      - id: fix-encoding-pragma\n        args: [\"--remove\"]\n      - id: mixed-line-ending\n        args: [\"--fix=lf\"]\n  - repo: https://github.com/codespell-project/codespell\n    rev: v2.2.1\n    hooks:\n      - id: codespell\n  - repo: https://github.com/executablebooks/mdformat\n    rev: 0.7.9\n    hooks:\n      - id: mdformat\n        args: [\"--number\"]\n        additional_dependencies:\n          - mdformat-openmmlab\n          - mdformat_frontmatter\n          - linkify-it-py\n  - repo: https://github.com/myint/docformatter\n    rev: v1.3.1\n    hooks:\n      - id: docformatter\n        args: [\"--in-place\", \"--wrap-descriptions\", \"79\"]\n  - repo: https://github.com/open-mmlab/pre-commit-hooks\n    rev: v0.2.0  # Use the ref you want to point at\n    hooks:\n      - id: check-algo-readme\n      - id: check-copyright\n        args: [\"mmdet\"]  # replace the dir_to_check with your expected directory to check\n"
  },
  {
    "path": ".readthedocs.yml",
    "content": "version: 2\n\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.8\"\n\nformats:\n  - epub\n\npython:\n  install:\n    - requirements: requirements/docs.txt\n    - requirements: requirements/readthedocs.txt\n"
  },
  {
    "path": "CITATION.cff",
    "content": "cff-version: 1.2.0\nmessage: \"If you use this software, please cite it as below.\"\nauthors:\n  - name: \"MMDetection Contributors\"\ntitle: \"OpenMMLab Detection Toolbox and Benchmark\"\ndate-released: 2018-08-22\nurl: \"https://github.com/open-mmlab/mmdetection\"\nlicense: Apache-2.0\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2018-2023 OpenMMLab. All rights reserved.\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2018-2023 OpenMMLab.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include requirements/*.txt\ninclude mmdet/VERSION\ninclude mmdet/.mim/model-index.yml\ninclude mmdet/.mim/dataset-index.yml\ninclude mmdet/.mim/demo/*/*\nrecursive-include mmdet/.mim/configs *.py *.yml\nrecursive-include mmdet/.mim/tools *.sh *.py\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <img src=\"resources/mmdet-logo.png\" width=\"600\"/>\n  <div>&nbsp;</div>\n  <div align=\"center\">\n    <b><font size=\"5\">OpenMMLab website</font></b>\n    <sup>\n      <a href=\"https://openmmlab.com\">\n        <i><font size=\"4\">HOT</font></i>\n      </a>\n    </sup>\n    &nbsp;&nbsp;&nbsp;&nbsp;\n    <b><font size=\"5\">OpenMMLab platform</font></b>\n    <sup>\n      <a href=\"https://platform.openmmlab.com\">\n        <i><font size=\"4\">TRY IT OUT</font></i>\n      </a>\n    </sup>\n  </div>\n  <div>&nbsp;</div>\n\n[![PyPI](https://img.shields.io/pypi/v/mmdet)](https://pypi.org/project/mmdet)\n[![docs](https://img.shields.io/badge/docs-latest-blue)](https://mmdetection.readthedocs.io/en/latest/)\n[![badge](https://github.com/open-mmlab/mmdetection/workflows/build/badge.svg)](https://github.com/open-mmlab/mmdetection/actions)\n[![codecov](https://codecov.io/gh/open-mmlab/mmdetection/branch/main/graph/badge.svg)](https://codecov.io/gh/open-mmlab/mmdetection)\n[![license](https://img.shields.io/github/license/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/blob/main/LICENSE)\n[![open issues](https://isitmaintained.com/badge/open/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/issues)\n[![issue resolution](https://isitmaintained.com/badge/resolution/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/issues)\n[![Open in OpenXLab](https://cdn-static.openxlab.org.cn/app-center/openxlab_demo.svg)](https://openxlab.org.cn/apps?search=mmdet)\n\n[📘Documentation](https://mmdetection.readthedocs.io/en/latest/) |\n[🛠️Installation](https://mmdetection.readthedocs.io/en/latest/get_started.html) |\n[👀Model Zoo](https://mmdetection.readthedocs.io/en/latest/model_zoo.html) |\n[🆕Update News](https://mmdetection.readthedocs.io/en/latest/notes/changelog.html) |\n[🚀Ongoing Projects](https://github.com/open-mmlab/mmdetection/projects) |\n[🤔Reporting Issues](https://github.com/open-mmlab/mmdetection/issues/new/choose)\n\n</div>\n\n<div align=\"center\">\n\nEnglish | [简体中文](README_zh-CN.md)\n\n</div>\n\n<div align=\"center\">\n  <a href=\"https://openmmlab.medium.com/\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/219255827-67c1a27f-f8c5-46a9-811d-5e57448c61d1.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://discord.com/channels/1037617289144569886/1046608014234370059\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/218347213-c080267f-cbb6-443e-8532-8e1ed9a58ea9.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://twitter.com/OpenMMLab\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/218346637-d30c8a0f-3eba-4699-8131-512fb06d46db.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://www.youtube.com/openmmlab\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/218346691-ceb2116a-465a-40af-8424-9f30d2348ca9.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://space.bilibili.com/1293512903\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/219026751-d7d14cce-a7c9-4e82-9942-8375fca65b99.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://www.zhihu.com/people/openmmlab\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/219026120-ba71e48b-6e94-4bd4-b4e9-b7d175b5e362.png\" width=\"3%\" alt=\"\" /></a>\n</div>\n\n<div align=\"center\">\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/6c29886f-ae7a-4a55-8be4-352ee85b7d3e\"/>\n</div>\n\n## Introduction\n\nMMDetection is an open source object detection toolbox based on PyTorch. It is\na part of the [OpenMMLab](https://openmmlab.com/) project.\n\nThe main branch works with **PyTorch 1.8+**.\n\n<img src=\"https://user-images.githubusercontent.com/12907710/187674113-2074d658-f2fb-42d1-ac15-9c4a695e64d7.png\"/>\n\n<details open>\n<summary>Major features</summary>\n\n- **Modular Design**\n\n  We decompose the detection framework into different components and one can easily construct a customized object detection framework by combining different modules.\n\n- **Support of multiple tasks out of box**\n\n  The toolbox directly supports multiple detection tasks such as **object detection**, **instance segmentation**, **panoptic segmentation**, and **semi-supervised object detection**.\n\n- **High efficiency**\n\n  All basic bbox and mask operations run on GPUs. The training speed is faster than or comparable to other codebases, including [Detectron2](https://github.com/facebookresearch/detectron2), [maskrcnn-benchmark](https://github.com/facebookresearch/maskrcnn-benchmark) and [SimpleDet](https://github.com/TuSimple/simpledet).\n\n- **State of the art**\n\n  The toolbox stems from the codebase developed by the *MMDet* team, who won [COCO Detection Challenge](http://cocodataset.org/#detection-leaderboard) in 2018, and we keep pushing it forward.\n  The newly released [RTMDet](configs/rtmdet) also obtains new state-of-the-art results on real-time instance segmentation and rotated object detection tasks and the best parameter-accuracy trade-off on object detection.\n\n</details>\n\nApart from MMDetection, we also released [MMEngine](https://github.com/open-mmlab/mmengine) for model training and [MMCV](https://github.com/open-mmlab/mmcv) for computer vision research, which are heavily depended on by this toolbox.\n\n## What's New\n\n💎 **We have released the pre-trained weights for MM-Grounding-DINO Swin-B and Swin-L, welcome to try and give feedback.**\n\n### Highlight\n\n**v3.3.0** was released in 5/1/2024:\n\n**[MM-Grounding-DINO: An Open and Comprehensive Pipeline for Unified Object Grounding and Detection](https://arxiv.org/abs/2401.02361)**\n\nGrounding DINO is a grounding pre-training model that unifies 2d open vocabulary object detection and phrase grounding, with wide applications. However, its training part has not been open sourced. Therefore, we propose MM-Grounding-DINO, which not only serves as an open source replication version of Grounding DINO, but also achieves significant performance improvement based on reconstructed data types, exploring different dataset combinations and initialization strategies. Moreover, we conduct evaluations from multiple dimensions, including OOD, REC, Phrase Grounding, OVD, and Fine-tune, to fully excavate the advantages and disadvantages of Grounding pre-training, hoping to provide inspiration for future work.\n\ncode: [mm_grounding_dino/README.md](configs/mm_grounding_dino/README.md)\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/fb14d1ee-5469-44d2-b865-aac9850c429c\"/>\n</div>\n\nWe are excited to announce our latest work on real-time object recognition tasks, **RTMDet**, a family of fully convolutional single-stage detectors. RTMDet not only achieves the best parameter-accuracy trade-off on object detection from tiny to extra-large model sizes but also obtains new state-of-the-art performance on instance segmentation and rotated object detection tasks. Details can be found in the [technical report](https://arxiv.org/abs/2212.07784). Pre-trained models are [here](configs/rtmdet).\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/real-time-instance-segmentation-on-mscoco)](https://paperswithcode.com/sota/real-time-instance-segmentation-on-mscoco?p=rtmdet-an-empirical-study-of-designing-real)\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-dota-1)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-dota-1?p=rtmdet-an-empirical-study-of-designing-real)\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-hrsc2016)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-hrsc2016?p=rtmdet-an-empirical-study-of-designing-real)\n\n| Task                     | Dataset | AP                                   | FPS(TRT FP16 BS1 3090) |\n| ------------------------ | ------- | ------------------------------------ | ---------------------- |\n| Object Detection         | COCO    | 52.8                                 | 322                    |\n| Instance Segmentation    | COCO    | 44.6                                 | 188                    |\n| Rotated Object Detection | DOTA    | 78.9(single-scale)/81.3(multi-scale) | 121                    |\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/12907710/208044554-1e8de6b5-48d8-44e4-a7b5-75076c7ebb71.png\"/>\n</div>\n\n## Installation\n\nPlease refer to [Installation](https://mmdetection.readthedocs.io/en/latest/get_started.html) for installation instructions.\n\n## Getting Started\n\nPlease see [Overview](https://mmdetection.readthedocs.io/en/latest/get_started.html) for the general introduction of MMDetection.\n\nFor detailed user guides and advanced guides, please refer to our [documentation](https://mmdetection.readthedocs.io/en/latest/):\n\n- User Guides\n\n  <details>\n\n  - [Train & Test](https://mmdetection.readthedocs.io/en/latest/user_guides/index.html#train-test)\n    - [Learn about Configs](https://mmdetection.readthedocs.io/en/latest/user_guides/config.html)\n    - [Inference with existing models](https://mmdetection.readthedocs.io/en/latest/user_guides/inference.html)\n    - [Dataset Prepare](https://mmdetection.readthedocs.io/en/latest/user_guides/dataset_prepare.html)\n    - [Test existing models on standard datasets](https://mmdetection.readthedocs.io/en/latest/user_guides/test.html)\n    - [Train predefined models on standard datasets](https://mmdetection.readthedocs.io/en/latest/user_guides/train.html)\n    - [Train with customized datasets](https://mmdetection.readthedocs.io/en/latest/user_guides/train.html#train-with-customized-datasets)\n    - [Train with customized models and standard datasets](https://mmdetection.readthedocs.io/en/latest/user_guides/new_model.html)\n    - [Finetuning Models](https://mmdetection.readthedocs.io/en/latest/user_guides/finetune.html)\n    - [Test Results Submission](https://mmdetection.readthedocs.io/en/latest/user_guides/test_results_submission.html)\n    - [Weight initialization](https://mmdetection.readthedocs.io/en/latest/user_guides/init_cfg.html)\n    - [Use a single stage detector as RPN](https://mmdetection.readthedocs.io/en/latest/user_guides/single_stage_as_rpn.html)\n    - [Semi-supervised Object Detection](https://mmdetection.readthedocs.io/en/latest/user_guides/semi_det.html)\n  - [Useful Tools](https://mmdetection.readthedocs.io/en/latest/user_guides/index.html#useful-tools)\n\n  </details>\n\n- Advanced Guides\n\n  <details>\n\n  - [Basic Concepts](https://mmdetection.readthedocs.io/en/latest/advanced_guides/index.html#basic-concepts)\n  - [Component Customization](https://mmdetection.readthedocs.io/en/latest/advanced_guides/index.html#component-customization)\n  - [How to](https://mmdetection.readthedocs.io/en/latest/advanced_guides/index.html#how-to)\n\n  </details>\n\nWe also provide object detection colab tutorial [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](demo/MMDet_Tutorial.ipynb) and instance segmentation colab tutorial [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](demo/MMDet_InstanceSeg_Tutorial.ipynb).\n\nTo migrate from MMDetection 2.x, please refer to [migration](https://mmdetection.readthedocs.io/en/latest/migration.html).\n\n## Overview of Benchmark and Model Zoo\n\nResults and models are available in the [model zoo](docs/en/model_zoo.md).\n\n<div align=\"center\">\n  <b>Architectures</b>\n</div>\n<table align=\"center\">\n  <tbody>\n    <tr align=\"center\" valign=\"bottom\">\n      <td>\n        <b>Object Detection</b>\n      </td>\n      <td>\n        <b>Instance Segmentation</b>\n      </td>\n      <td>\n        <b>Panoptic Segmentation</b>\n      </td>\n      <td>\n        <b>Other</b>\n      </td>\n    </tr>\n    <tr valign=\"top\">\n      <td>\n        <ul>\n            <li><a href=\"configs/fast_rcnn\">Fast R-CNN (ICCV'2015)</a></li>\n            <li><a href=\"configs/faster_rcnn\">Faster R-CNN (NeurIPS'2015)</a></li>\n            <li><a href=\"configs/rpn\">RPN (NeurIPS'2015)</a></li>\n            <li><a href=\"configs/ssd\">SSD (ECCV'2016)</a></li>\n            <li><a href=\"configs/retinanet\">RetinaNet (ICCV'2017)</a></li>\n            <li><a href=\"configs/cascade_rcnn\">Cascade R-CNN (CVPR'2018)</a></li>\n            <li><a href=\"configs/yolo\">YOLOv3 (ArXiv'2018)</a></li>\n            <li><a href=\"configs/cornernet\">CornerNet (ECCV'2018)</a></li>\n            <li><a href=\"configs/grid_rcnn\">Grid R-CNN (CVPR'2019)</a></li>\n            <li><a href=\"configs/guided_anchoring\">Guided Anchoring (CVPR'2019)</a></li>\n            <li><a href=\"configs/fsaf\">FSAF (CVPR'2019)</a></li>\n            <li><a href=\"configs/centernet\">CenterNet (CVPR'2019)</a></li>\n            <li><a href=\"configs/libra_rcnn\">Libra R-CNN (CVPR'2019)</a></li>\n            <li><a href=\"configs/tridentnet\">TridentNet (ICCV'2019)</a></li>\n            <li><a href=\"configs/fcos\">FCOS (ICCV'2019)</a></li>\n            <li><a href=\"configs/reppoints\">RepPoints (ICCV'2019)</a></li>\n            <li><a href=\"configs/free_anchor\">FreeAnchor (NeurIPS'2019)</a></li>\n            <li><a href=\"configs/cascade_rpn\">CascadeRPN (NeurIPS'2019)</a></li>\n            <li><a href=\"configs/foveabox\">Foveabox (TIP'2020)</a></li>\n            <li><a href=\"configs/double_heads\">Double-Head R-CNN (CVPR'2020)</a></li>\n            <li><a href=\"configs/atss\">ATSS (CVPR'2020)</a></li>\n            <li><a href=\"configs/nas_fcos\">NAS-FCOS (CVPR'2020)</a></li>\n            <li><a href=\"configs/centripetalnet\">CentripetalNet (CVPR'2020)</a></li>\n            <li><a href=\"configs/autoassign\">AutoAssign (ArXiv'2020)</a></li>\n            <li><a href=\"configs/sabl\">Side-Aware Boundary Localization (ECCV'2020)</a></li>\n            <li><a href=\"configs/dynamic_rcnn\">Dynamic R-CNN (ECCV'2020)</a></li>\n            <li><a href=\"configs/detr\">DETR (ECCV'2020)</a></li>\n            <li><a href=\"configs/paa\">PAA (ECCV'2020)</a></li>\n            <li><a href=\"configs/vfnet\">VarifocalNet (CVPR'2021)</a></li>\n            <li><a href=\"configs/sparse_rcnn\">Sparse R-CNN (CVPR'2021)</a></li>\n            <li><a href=\"configs/yolof\">YOLOF (CVPR'2021)</a></li>\n            <li><a href=\"configs/yolox\">YOLOX (CVPR'2021)</a></li>\n            <li><a href=\"configs/deformable_detr\">Deformable DETR (ICLR'2021)</a></li>\n            <li><a href=\"configs/tood\">TOOD (ICCV'2021)</a></li>\n            <li><a href=\"configs/ddod\">DDOD (ACM MM'2021)</a></li>\n            <li><a href=\"configs/rtmdet\">RTMDet (ArXiv'2022)</a></li>\n            <li><a href=\"configs/conditional_detr\">Conditional DETR (ICCV'2021)</a></li>\n            <li><a href=\"configs/dab_detr\">DAB-DETR (ICLR'2022)</a></li>\n            <li><a href=\"configs/dino\">DINO (ICLR'2023)</a></li>\n            <li><a href=\"configs/glip\">GLIP (CVPR'2022)</a></li>\n            <li><a href=\"configs/ddq\">DDQ (CVPR'2023)</a></li>\n            <li><a href=\"projects/DiffusionDet\">DiffusionDet (ArXiv'2023)</a></li>\n            <li><a href=\"projects/EfficientDet\">EfficientDet (CVPR'2020)</a></li>\n            <li><a href=\"projects/ViTDet\">ViTDet (ECCV'2022)</a></li>\n            <li><a href=\"projects/Detic\">Detic (ECCV'2022)</a></li>\n            <li><a href=\"projects/CO-DETR\">CO-DETR (ICCV'2023)</a></li>\n      </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/mask_rcnn\">Mask R-CNN (ICCV'2017)</a></li>\n          <li><a href=\"configs/cascade_rcnn\">Cascade Mask R-CNN (CVPR'2018)</a></li>\n          <li><a href=\"configs/ms_rcnn\">Mask Scoring R-CNN (CVPR'2019)</a></li>\n          <li><a href=\"configs/htc\">Hybrid Task Cascade (CVPR'2019)</a></li>\n          <li><a href=\"configs/yolact\">YOLACT (ICCV'2019)</a></li>\n          <li><a href=\"configs/instaboost\">InstaBoost (ICCV'2019)</a></li>\n          <li><a href=\"configs/solo\">SOLO (ECCV'2020)</a></li>\n          <li><a href=\"configs/point_rend\">PointRend (CVPR'2020)</a></li>\n          <li><a href=\"configs/detectors\">DetectoRS (ArXiv'2020)</a></li>\n          <li><a href=\"configs/solov2\">SOLOv2 (NeurIPS'2020)</a></li>\n          <li><a href=\"configs/scnet\">SCNet (AAAI'2021)</a></li>\n          <li><a href=\"configs/queryinst\">QueryInst (ICCV'2021)</a></li>\n          <li><a href=\"configs/mask2former\">Mask2Former (ArXiv'2021)</a></li>\n          <li><a href=\"configs/condinst\">CondInst (ECCV'2020)</a></li>\n          <li><a href=\"projects/SparseInst\">SparseInst (CVPR'2022)</a></li>\n          <li><a href=\"configs/rtmdet\">RTMDet (ArXiv'2022)</a></li>\n          <li><a href=\"configs/boxinst\">BoxInst (CVPR'2021)</a></li>\n          <li><a href=\"projects/ConvNeXt-V2\">ConvNeXt-V2 (Arxiv'2023)</a></li>\n        </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/panoptic_fpn\">Panoptic FPN (CVPR'2019)</a></li>\n          <li><a href=\"configs/maskformer\">MaskFormer (NeurIPS'2021)</a></li>\n          <li><a href=\"configs/mask2former\">Mask2Former (ArXiv'2021)</a></li>\n          <li><a href=\"configs/XDecoder\">XDecoder (CVPR'2023)</a></li>\n        </ul>\n      </td>\n      <td>\n        </ul>\n          <li><b>Contrastive Learning</b></li>\n        <ul>\n        <ul>\n          <li><a href=\"configs/selfsup_pretrain\">SwAV (NeurIPS'2020)</a></li>\n          <li><a href=\"configs/selfsup_pretrain\">MoCo (CVPR'2020)</a></li>\n          <li><a href=\"configs/selfsup_pretrain\">MoCov2 (ArXiv'2020)</a></li>\n        </ul>\n        </ul>\n        </ul>\n          <li><b>Distillation</b></li>\n        <ul>\n        <ul>\n          <li><a href=\"configs/ld\">Localization Distillation (CVPR'2022)</a></li>\n          <li><a href=\"configs/lad\">Label Assignment Distillation (WACV'2022)</a></li>\n        </ul>\n        </ul>\n          <li><b>Semi-Supervised Object Detection</b></li>\n        <ul>\n        <ul>\n          <li><a href=\"configs/soft_teacher\">Soft Teacher (ICCV'2021)</a></li>\n        </ul>\n        </ul>\n      </ul>\n      </td>\n    </tr>\n</td>\n    </tr>\n  </tbody>\n</table>\n\n<div align=\"center\">\n  <b>Components</b>\n</div>\n<table align=\"center\">\n  <tbody>\n    <tr align=\"center\" valign=\"bottom\">\n      <td>\n        <b>Backbones</b>\n      </td>\n      <td>\n        <b>Necks</b>\n      </td>\n      <td>\n        <b>Loss</b>\n      </td>\n      <td>\n        <b>Common</b>\n      </td>\n    </tr>\n    <tr valign=\"top\">\n      <td>\n      <ul>\n        <li>VGG (ICLR'2015)</li>\n        <li>ResNet (CVPR'2016)</li>\n        <li>ResNeXt (CVPR'2017)</li>\n        <li>MobileNetV2 (CVPR'2018)</li>\n        <li><a href=\"configs/hrnet\">HRNet (CVPR'2019)</a></li>\n        <li><a href=\"configs/empirical_attention\">Generalized Attention (ICCV'2019)</a></li>\n        <li><a href=\"configs/gcnet\">GCNet (ICCVW'2019)</a></li>\n        <li><a href=\"configs/res2net\">Res2Net (TPAMI'2020)</a></li>\n        <li><a href=\"configs/regnet\">RegNet (CVPR'2020)</a></li>\n        <li><a href=\"configs/resnest\">ResNeSt (ArXiv'2020)</a></li>\n        <li><a href=\"configs/pvt\">PVT (ICCV'2021)</a></li>\n        <li><a href=\"configs/swin\">Swin (CVPR'2021)</a></li>\n        <li><a href=\"configs/pvt\">PVTv2 (ArXiv'2021)</a></li>\n        <li><a href=\"configs/resnet_strikes_back\">ResNet strikes back (ArXiv'2021)</a></li>\n        <li><a href=\"configs/efficientnet\">EfficientNet (ArXiv'2021)</a></li>\n        <li><a href=\"configs/convnext\">ConvNeXt (CVPR'2022)</a></li>\n        <li><a href=\"projects/ConvNeXt-V2\">ConvNeXtv2 (ArXiv'2023)</a></li>\n      </ul>\n      </td>\n      <td>\n      <ul>\n        <li><a href=\"configs/pafpn\">PAFPN (CVPR'2018)</a></li>\n        <li><a href=\"configs/nas_fpn\">NAS-FPN (CVPR'2019)</a></li>\n        <li><a href=\"configs/carafe\">CARAFE (ICCV'2019)</a></li>\n        <li><a href=\"configs/fpg\">FPG (ArXiv'2020)</a></li>\n        <li><a href=\"configs/groie\">GRoIE (ICPR'2020)</a></li>\n        <li><a href=\"configs/dyhead\">DyHead (CVPR'2021)</a></li>\n      </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/ghm\">GHM (AAAI'2019)</a></li>\n          <li><a href=\"configs/gfl\">Generalized Focal Loss (NeurIPS'2020)</a></li>\n          <li><a href=\"configs/seesaw_loss\">Seasaw Loss (CVPR'2021)</a></li>\n        </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/faster_rcnn/faster-rcnn_r50_fpn_ohem_1x_coco.py\">OHEM (CVPR'2016)</a></li>\n          <li><a href=\"configs/gn\">Group Normalization (ECCV'2018)</a></li>\n          <li><a href=\"configs/dcn\">DCN (ICCV'2017)</a></li>\n          <li><a href=\"configs/dcnv2\">DCNv2 (CVPR'2019)</a></li>\n          <li><a href=\"configs/gn+ws\">Weight Standardization (ArXiv'2019)</a></li>\n          <li><a href=\"configs/pisa\">Prime Sample Attention (CVPR'2020)</a></li>\n          <li><a href=\"configs/strong_baselines\">Strong Baselines (CVPR'2021)</a></li>\n          <li><a href=\"configs/resnet_strikes_back\">Resnet strikes back (ArXiv'2021)</a></li>\n        </ul>\n      </td>\n    </tr>\n</td>\n    </tr>\n  </tbody>\n</table>\n\nSome other methods are also supported in [projects using MMDetection](./docs/en/notes/projects.md).\n\n## FAQ\n\nPlease refer to [FAQ](docs/en/notes/faq.md) for frequently asked questions.\n\n## Contributing\n\nWe appreciate all contributions to improve MMDetection. Ongoing projects can be found in out [GitHub Projects](https://github.com/open-mmlab/mmdetection/projects). Welcome community users to participate in these projects. Please refer to [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the contributing guideline.\n\n## Acknowledgement\n\nMMDetection is an open source project that is contributed by researchers and engineers from various colleges and companies. We appreciate all the contributors who implement their methods or add new features, as well as users who give valuable feedbacks.\nWe wish that the toolbox and benchmark could serve the growing research community by providing a flexible toolkit to reimplement existing methods and develop their own new detectors.\n\n## Citation\n\nIf you use this toolbox or benchmark in your research, please cite this project.\n\n```\n@article{mmdetection,\n  title   = {{MMDetection}: Open MMLab Detection Toolbox and Benchmark},\n  author  = {Chen, Kai and Wang, Jiaqi and Pang, Jiangmiao and Cao, Yuhang and\n             Xiong, Yu and Li, Xiaoxiao and Sun, Shuyang and Feng, Wansen and\n             Liu, Ziwei and Xu, Jiarui and Zhang, Zheng and Cheng, Dazhi and\n             Zhu, Chenchen and Cheng, Tianheng and Zhao, Qijie and Li, Buyu and\n             Lu, Xin and Zhu, Rui and Wu, Yue and Dai, Jifeng and Wang, Jingdong\n             and Shi, Jianping and Ouyang, Wanli and Loy, Chen Change and Lin, Dahua},\n  journal= {arXiv preprint arXiv:1906.07155},\n  year={2019}\n}\n```\n\n## License\n\nThis project is released under the [Apache 2.0 license](LICENSE).\n\n## Projects in OpenMMLab\n\n- [MMEngine](https://github.com/open-mmlab/mmengine): OpenMMLab foundational library for training deep learning models.\n- [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab foundational library for computer vision.\n- [MMPreTrain](https://github.com/open-mmlab/mmpretrain): OpenMMLab pre-training toolbox and benchmark.\n- [MMagic](https://github.com/open-mmlab/mmagic): Open**MM**Lab **A**dvanced, **G**enerative and **I**ntelligent **C**reation toolbox.\n- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab detection toolbox and benchmark.\n- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab's next-generation platform for general 3D object detection.\n- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab rotated object detection toolbox and benchmark.\n- [MMYOLO](https://github.com/open-mmlab/mmyolo): OpenMMLab YOLO series toolbox and benchmark.\n- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab semantic segmentation toolbox and benchmark.\n- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab text detection, recognition, and understanding toolbox.\n- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab pose estimation toolbox and benchmark.\n- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 3D human parametric model toolbox and benchmark.\n- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab self-supervised learning toolbox and benchmark.\n- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab model compression toolbox and benchmark.\n- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab fewshot learning toolbox and benchmark.\n- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab's next-generation action understanding toolbox and benchmark.\n- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab video perception toolbox and benchmark.\n- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab optical flow toolbox and benchmark.\n- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab image and video editing toolbox.\n- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab image and video generative models toolbox.\n- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab model deployment framework.\n- [MIM](https://github.com/open-mmlab/mim): MIM installs OpenMMLab packages.\n- [MMEval](https://github.com/open-mmlab/mmeval): A unified evaluation library for multiple machine learning libraries.\n- [Playground](https://github.com/open-mmlab/playground): A central hub for gathering and showcasing amazing projects built upon OpenMMLab.\n"
  },
  {
    "path": "README_zh-CN.md",
    "content": "<div align=\"center\">\n  <img src=\"resources/mmdet-logo.png\" width=\"600\"/>\n  <div>&nbsp;</div>\n  <div align=\"center\">\n    <b><font size=\"5\">OpenMMLab 官网</font></b>\n    <sup>\n      <a href=\"https://openmmlab.com\">\n        <i><font size=\"4\">HOT</font></i>\n      </a>\n    </sup>\n    &nbsp;&nbsp;&nbsp;&nbsp;\n    <b><font size=\"5\">OpenMMLab 开放平台</font></b>\n    <sup>\n      <a href=\"https://platform.openmmlab.com\">\n        <i><font size=\"4\">TRY IT OUT</font></i>\n      </a>\n    </sup>\n  </div>\n  <div>&nbsp;</div>\n\n[![PyPI](https://img.shields.io/pypi/v/mmdet)](https://pypi.org/project/mmdet)\n[![docs](https://img.shields.io/badge/docs-latest-blue)](https://mmdetection.readthedocs.io/en/latest/)\n[![badge](https://github.com/open-mmlab/mmdetection/workflows/build/badge.svg)](https://github.com/open-mmlab/mmdetection/actions)\n[![codecov](https://codecov.io/gh/open-mmlab/mmdetection/branch/main/graph/badge.svg)](https://codecov.io/gh/open-mmlab/mmdetection)\n[![license](https://img.shields.io/github/license/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/blob/main/LICENSE)\n[![open issues](https://isitmaintained.com/badge/open/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/issues)\n[![issue resolution](https://isitmaintained.com/badge/resolution/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/issues)\n[![Open in OpenXLab](https://cdn-static.openxlab.org.cn/app-center/openxlab_demo.svg)](https://openxlab.org.cn/apps?search=mmdet)\n\n[📘使用文档](https://mmdetection.readthedocs.io/zh_CN/latest/) |\n[🛠️安装教程](https://mmdetection.readthedocs.io/zh_CN/latest/get_started.html) |\n[👀模型库](https://mmdetection.readthedocs.io/zh_CN/latest/model_zoo.html) |\n[🆕更新日志](https://mmdetection.readthedocs.io/en/latest/notes/changelog.html) |\n[🚀进行中的项目](https://github.com/open-mmlab/mmdetection/projects) |\n[🤔报告问题](https://github.com/open-mmlab/mmdetection/issues/new/choose)\n\n</div>\n\n<div align=\"center\">\n\n[English](README.md) | 简体中文\n\n</div>\n\n<div align=\"center\">\n  <a href=\"https://openmmlab.medium.com/\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/219255827-67c1a27f-f8c5-46a9-811d-5e57448c61d1.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://discord.com/channels/1037617289144569886/1046608014234370059\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/218347213-c080267f-cbb6-443e-8532-8e1ed9a58ea9.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://twitter.com/OpenMMLab\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/218346637-d30c8a0f-3eba-4699-8131-512fb06d46db.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://www.youtube.com/openmmlab\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/218346691-ceb2116a-465a-40af-8424-9f30d2348ca9.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://space.bilibili.com/1293512903\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/219026751-d7d14cce-a7c9-4e82-9942-8375fca65b99.png\" width=\"3%\" alt=\"\" /></a>\n  <img src=\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\" width=\"3%\" alt=\"\" />\n  <a href=\"https://www.zhihu.com/people/openmmlab\" style=\"text-decoration:none;\">\n    <img src=\"https://user-images.githubusercontent.com/25839884/219026120-ba71e48b-6e94-4bd4-b4e9-b7d175b5e362.png\" width=\"3%\" alt=\"\" /></a>\n</div>\n\n<div align=\"center\">\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/6c29886f-ae7a-4a55-8be4-352ee85b7d3e\"/>\n</div>\n\n## 简介\n\nMMDetection 是一个基于 PyTorch 的目标检测开源工具箱。它是 [OpenMMLab](https://openmmlab.com/) 项目的一部分。\n\n主分支代码目前支持 PyTorch 1.8 及其以上的版本。\n\n<img src=\"https://user-images.githubusercontent.com/12907710/187674113-2074d658-f2fb-42d1-ac15-9c4a695e64d7.png\"/>\n\n<details open>\n<summary>主要特性</summary>\n\n- **模块化设计**\n\n  MMDetection 将检测框架解耦成不同的模块组件，通过组合不同的模块组件，用户可以便捷地构建自定义的检测模型\n\n- **支持多种检测任务**\n\n  MMDetection 支持了各种不同的检测任务，包括**目标检测**，**实例分割**，**全景分割**，以及**半监督目标检测**。\n\n- **速度快**\n\n  基本的框和 mask 操作都实现了 GPU 版本，训练速度比其他代码库更快或者相当，包括 [Detectron2](https://github.com/facebookresearch/detectron2), [maskrcnn-benchmark](https://github.com/facebookresearch/maskrcnn-benchmark) 和 [SimpleDet](https://github.com/TuSimple/simpledet)。\n\n- **性能高**\n\n  MMDetection 这个算法库源自于 COCO 2018 目标检测竞赛的冠军团队 *MMDet* 团队开发的代码，我们在之后持续进行了改进和提升。\n  新发布的 [RTMDet](configs/rtmdet) 还在实时实例分割和旋转目标检测任务中取得了最先进的成果，同时也在目标检测模型中取得了最佳的的参数量和精度平衡。\n\n</details>\n\n除了 MMDetection 之外，我们还开源了深度学习训练库 [MMEngine](https://github.com/open-mmlab/mmengine) 和计算机视觉基础库 [MMCV](https://github.com/open-mmlab/mmcv)，它们是 MMDetection 的主要依赖。\n\n## 最新进展\n\n💎 **我们已经发布了 MM-Grounding-DINO Swin-B 和 Swin-L 预训练权重，欢迎试用和反馈.**\n\n### 亮点\n\n**v3.3.0** 版本已经在 2024.1.5 发布：\n\n**MM-Grounding-DINO: 轻松涨点，数据到评测全面开源**\n\nGrounding DINO 是一个统一了 2d 开放词汇目标检测和 Phrase Grounding 的检测预训练模型，应用广泛，但是其训练部分并未开源，为此提出了 MM-Grounding-DINO。其不仅作为 Grounding DINO 的开源复现版，MM-Grounding-DINO 基于重新构建的数据类型出发，在探索了不同数据集组合和初始化策略基础上实现了 Grounding DINO 的性能极大提升，并且从多个维度包括 OOD、REC、Phrase Grounding、OVD 和 Finetune 等方面进行评测，充分挖掘 Grounding 预训练优缺点，希望能为后续工作提供启发。\n\narxiv 技术报告：https://arxiv.org/abs/2401.02361\n\n代码地址: [mm_grounding_dino/README.md](configs/mm_grounding_dino/README.md)\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/fb14d1ee-5469-44d2-b865-aac9850c429c\"/>\n</div>\n\n我们很高兴向大家介绍我们在实时目标识别任务方面的最新成果 RTMDet，包含了一系列的全卷积单阶段检测模型。 RTMDet 不仅在从 tiny 到 extra-large 尺寸的目标检测模型上实现了最佳的参数量和精度的平衡，而且在实时实例分割和旋转目标检测任务上取得了最先进的成果。 更多细节请参阅[技术报告](https://arxiv.org/abs/2212.07784)。 预训练模型可以在[这里](configs/rtmdet)找到。\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/real-time-instance-segmentation-on-mscoco)](https://paperswithcode.com/sota/real-time-instance-segmentation-on-mscoco?p=rtmdet-an-empirical-study-of-designing-real)\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-dota-1)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-dota-1?p=rtmdet-an-empirical-study-of-designing-real)\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-hrsc2016)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-hrsc2016?p=rtmdet-an-empirical-study-of-designing-real)\n\n| Task                     | Dataset | AP                                   | FPS(TRT FP16 BS1 3090) |\n| ------------------------ | ------- | ------------------------------------ | ---------------------- |\n| Object Detection         | COCO    | 52.8                                 | 322                    |\n| Instance Segmentation    | COCO    | 44.6                                 | 188                    |\n| Rotated Object Detection | DOTA    | 78.9(single-scale)/81.3(multi-scale) | 121                    |\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/12907710/208044554-1e8de6b5-48d8-44e4-a7b5-75076c7ebb71.png\"/>\n</div>\n\n## 安装\n\n请参考[快速入门文档](https://mmdetection.readthedocs.io/zh_CN/latest/get_started.html)进行安装。\n\n## 教程\n\n请阅读[概述](https://mmdetection.readthedocs.io/zh_CN/latest/get_started.html)对 MMDetection 进行初步的了解。\n\n为了帮助用户更进一步了解 MMDetection，我们准备了用户指南和进阶指南，请阅读我们的[文档](https://mmdetection.readthedocs.io/zh_CN/latest/)：\n\n- 用户指南\n\n  <details>\n\n  - [训练 & 测试](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/index.html#train-test)\n    - [学习配置文件](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/config.html)\n    - [使用已有模型在标准数据集上进行推理](https://mmdetection.readthedocs.io/en/latest/user_guides/inference.html)\n    - [数据集准备](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/dataset_prepare.html)\n    - [测试现有模型](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/test.html)\n    - [在标准数据集上训练预定义的模型](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/train.html)\n    - [在自定义数据集上进行训练](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/train.html#train-with-customized-datasets)\n    - [在标准数据集上训练自定义模型](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/new_model.html)\n    - [模型微调](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/finetune.html)\n    - [提交测试结果](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/test_results_submission.html)\n    - [权重初始化](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/init_cfg.html)\n    - [将单阶段检测器作为 RPN](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/single_stage_as_rpn.html)\n    - [半监督目标检测](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/semi_det.html)\n  - [实用工具](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/index.html#useful-tools)\n\n  </details>\n\n- 进阶指南\n\n  <details>\n\n  - [基础概念](https://mmdetection.readthedocs.io/zh_CN/latest/advanced_guides/index.html#basic-concepts)\n  - [组件定制](https://mmdetection.readthedocs.io/zh_CN/latest/advanced_guides/index.html#component-customization)\n  - [How to](https://mmdetection.readthedocs.io/zh_CN/latest/advanced_guides/index.html#how-to)\n\n  </details>\n\n我们提供了检测的 colab 教程 [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](demo/MMDet_Tutorial.ipynb) 和 实例分割的 colab 教程 [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](demo/MMDet_Tutorial.ipynb)\n\n同时，我们还提供了 [MMDetection 中文解读文案汇总](docs/zh_cn/article.md)\n\n若需要将2.x版本的代码迁移至新版，请参考[迁移文档](https://mmdetection.readthedocs.io/en/latest/migration.html)。\n\n## 基准测试和模型库\n\n测试结果和模型可以在[模型库](docs/zh_cn/model_zoo.md)中找到。\n\n<div align=\"center\">\n  <b>算法架构</b>\n</div>\n<table align=\"center\">\n  <tbody>\n    <tr align=\"center\" valign=\"bottom\">\n      <td>\n        <b>Object Detection</b>\n      </td>\n      <td>\n        <b>Instance Segmentation</b>\n      </td>\n      <td>\n        <b>Panoptic Segmentation</b>\n      </td>\n      <td>\n        <b>Other</b>\n      </td>\n    </tr>\n    <tr valign=\"top\">\n      <td>\n        <ul>\n            <li><a href=\"configs/fast_rcnn\">Fast R-CNN (ICCV'2015)</a></li>\n            <li><a href=\"configs/faster_rcnn\">Faster R-CNN (NeurIPS'2015)</a></li>\n            <li><a href=\"configs/rpn\">RPN (NeurIPS'2015)</a></li>\n            <li><a href=\"configs/ssd\">SSD (ECCV'2016)</a></li>\n            <li><a href=\"configs/retinanet\">RetinaNet (ICCV'2017)</a></li>\n            <li><a href=\"configs/cascade_rcnn\">Cascade R-CNN (CVPR'2018)</a></li>\n            <li><a href=\"configs/yolo\">YOLOv3 (ArXiv'2018)</a></li>\n            <li><a href=\"configs/cornernet\">CornerNet (ECCV'2018)</a></li>\n            <li><a href=\"configs/grid_rcnn\">Grid R-CNN (CVPR'2019)</a></li>\n            <li><a href=\"configs/guided_anchoring\">Guided Anchoring (CVPR'2019)</a></li>\n            <li><a href=\"configs/fsaf\">FSAF (CVPR'2019)</a></li>\n            <li><a href=\"configs/centernet\">CenterNet (CVPR'2019)</a></li>\n            <li><a href=\"configs/libra_rcnn\">Libra R-CNN (CVPR'2019)</a></li>\n            <li><a href=\"configs/tridentnet\">TridentNet (ICCV'2019)</a></li>\n            <li><a href=\"configs/fcos\">FCOS (ICCV'2019)</a></li>\n            <li><a href=\"configs/reppoints\">RepPoints (ICCV'2019)</a></li>\n            <li><a href=\"configs/free_anchor\">FreeAnchor (NeurIPS'2019)</a></li>\n            <li><a href=\"configs/cascade_rpn\">CascadeRPN (NeurIPS'2019)</a></li>\n            <li><a href=\"configs/foveabox\">Foveabox (TIP'2020)</a></li>\n            <li><a href=\"configs/double_heads\">Double-Head R-CNN (CVPR'2020)</a></li>\n            <li><a href=\"configs/atss\">ATSS (CVPR'2020)</a></li>\n            <li><a href=\"configs/nas_fcos\">NAS-FCOS (CVPR'2020)</a></li>\n            <li><a href=\"configs/centripetalnet\">CentripetalNet (CVPR'2020)</a></li>\n            <li><a href=\"configs/autoassign\">AutoAssign (ArXiv'2020)</a></li>\n            <li><a href=\"configs/sabl\">Side-Aware Boundary Localization (ECCV'2020)</a></li>\n            <li><a href=\"configs/dynamic_rcnn\">Dynamic R-CNN (ECCV'2020)</a></li>\n            <li><a href=\"configs/detr\">DETR (ECCV'2020)</a></li>\n            <li><a href=\"configs/paa\">PAA (ECCV'2020)</a></li>\n            <li><a href=\"configs/vfnet\">VarifocalNet (CVPR'2021)</a></li>\n            <li><a href=\"configs/sparse_rcnn\">Sparse R-CNN (CVPR'2021)</a></li>\n            <li><a href=\"configs/yolof\">YOLOF (CVPR'2021)</a></li>\n            <li><a href=\"configs/yolox\">YOLOX (CVPR'2021)</a></li>\n            <li><a href=\"configs/deformable_detr\">Deformable DETR (ICLR'2021)</a></li>\n            <li><a href=\"configs/tood\">TOOD (ICCV'2021)</a></li>\n            <li><a href=\"configs/ddod\">DDOD (ACM MM'2021)</a></li>\n            <li><a href=\"configs/rtmdet\">RTMDet (ArXiv'2022)</a></li>\n            <li><a href=\"configs/conditional_detr\">Conditional DETR (ICCV'2021)</a></li>\n            <li><a href=\"configs/dab_detr\">DAB-DETR (ICLR'2022)</a></li>\n            <li><a href=\"configs/dino\">DINO (ICLR'2023)</a></li>\n            <li><a href=\"configs/glip\">GLIP (CVPR'2022)</a></li>\n            <li><a href=\"configs/ddq\">DDQ (CVPR'2023)</a></li>\n            <li><a href=\"projects/DiffusionDet\">DiffusionDet (ArXiv'2023)</a></li>\n            <li><a href=\"projects/EfficientDet\">EfficientDet (CVPR'2020)</a></li>\n            <li><a href=\"projects/ViTDet\">ViTDet (ECCV'2022)</a></li>\n            <li><a href=\"projects/Detic\">Detic (ECCV'2022)</a></li>\n            <li><a href=\"projects/CO-DETR\">CO-DETR (ICCV'2023)</a></li>\n      </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/mask_rcnn\">Mask R-CNN (ICCV'2017)</a></li>\n          <li><a href=\"configs/cascade_rcnn\">Cascade Mask R-CNN (CVPR'2018)</a></li>\n          <li><a href=\"configs/ms_rcnn\">Mask Scoring R-CNN (CVPR'2019)</a></li>\n          <li><a href=\"configs/htc\">Hybrid Task Cascade (CVPR'2019)</a></li>\n          <li><a href=\"configs/yolact\">YOLACT (ICCV'2019)</a></li>\n          <li><a href=\"configs/instaboost\">InstaBoost (ICCV'2019)</a></li>\n          <li><a href=\"configs/solo\">SOLO (ECCV'2020)</a></li>\n          <li><a href=\"configs/point_rend\">PointRend (CVPR'2020)</a></li>\n          <li><a href=\"configs/detectors\">DetectoRS (ArXiv'2020)</a></li>\n          <li><a href=\"configs/solov2\">SOLOv2 (NeurIPS'2020)</a></li>\n          <li><a href=\"configs/scnet\">SCNet (AAAI'2021)</a></li>\n          <li><a href=\"configs/queryinst\">QueryInst (ICCV'2021)</a></li>\n          <li><a href=\"configs/mask2former\">Mask2Former (ArXiv'2021)</a></li>\n          <li><a href=\"configs/condinst\">CondInst (ECCV'2020)</a></li>\n          <li><a href=\"projects/SparseInst\">SparseInst (CVPR'2022)</a></li>\n          <li><a href=\"configs/rtmdet\">RTMDet (ArXiv'2022)</a></li>\n          <li><a href=\"configs/boxinst\">BoxInst (CVPR'2021)</a></li>\n          <li><a href=\"projects/ConvNeXt-V2\">ConvNeXt-V2 (Arxiv'2023)</a></li>\n        </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/panoptic_fpn\">Panoptic FPN (CVPR'2019)</a></li>\n          <li><a href=\"configs/maskformer\">MaskFormer (NeurIPS'2021)</a></li>\n          <li><a href=\"configs/mask2former\">Mask2Former (ArXiv'2021)</a></li>\n          <li><a href=\"configs/XDecoder\">XDecoder (CVPR'2023)</a></li>\n        </ul>\n      </td>\n      <td>\n        </ul>\n          <li><b>Contrastive Learning</b></li>\n        <ul>\n        <ul>\n          <li><a href=\"configs/selfsup_pretrain\">SwAV (NeurIPS'2020)</a></li>\n          <li><a href=\"configs/selfsup_pretrain\">MoCo (CVPR'2020)</a></li>\n          <li><a href=\"configs/selfsup_pretrain\">MoCov2 (ArXiv'2020)</a></li>\n        </ul>\n        </ul>\n        </ul>\n          <li><b>Distillation</b></li>\n        <ul>\n        <ul>\n          <li><a href=\"configs/ld\">Localization Distillation (CVPR'2022)</a></li>\n          <li><a href=\"configs/lad\">Label Assignment Distillation (WACV'2022)</a></li>\n        </ul>\n        </ul>\n          <li><b>Semi-Supervised Object Detection</b></li>\n        <ul>\n        <ul>\n          <li><a href=\"configs/soft_teacher\">Soft Teacher (ICCV'2021)</a></li>\n        </ul>\n        </ul>\n      </ul>\n      </td>\n    </tr>\n</td>\n    </tr>\n  </tbody>\n</table>\n\n<div align=\"center\">\n  <b>模块组件</b>\n</div>\n<table align=\"center\">\n  <tbody>\n    <tr align=\"center\" valign=\"bottom\">\n      <td>\n        <b>Backbones</b>\n      </td>\n      <td>\n        <b>Necks</b>\n      </td>\n      <td>\n        <b>Loss</b>\n      </td>\n      <td>\n        <b>Common</b>\n      </td>\n    </tr>\n    <tr valign=\"top\">\n      <td>\n      <ul>\n        <li>VGG (ICLR'2015)</li>\n        <li>ResNet (CVPR'2016)</li>\n        <li>ResNeXt (CVPR'2017)</li>\n        <li>MobileNetV2 (CVPR'2018)</li>\n        <li><a href=\"configs/hrnet\">HRNet (CVPR'2019)</a></li>\n        <li><a href=\"configs/empirical_attention\">Generalized Attention (ICCV'2019)</a></li>\n        <li><a href=\"configs/gcnet\">GCNet (ICCVW'2019)</a></li>\n        <li><a href=\"configs/res2net\">Res2Net (TPAMI'2020)</a></li>\n        <li><a href=\"configs/regnet\">RegNet (CVPR'2020)</a></li>\n        <li><a href=\"configs/resnest\">ResNeSt (ArXiv'2020)</a></li>\n        <li><a href=\"configs/pvt\">PVT (ICCV'2021)</a></li>\n        <li><a href=\"configs/swin\">Swin (CVPR'2021)</a></li>\n        <li><a href=\"configs/pvt\">PVTv2 (ArXiv'2021)</a></li>\n        <li><a href=\"configs/resnet_strikes_back\">ResNet strikes back (ArXiv'2021)</a></li>\n        <li><a href=\"configs/efficientnet\">EfficientNet (ArXiv'2021)</a></li>\n        <li><a href=\"configs/convnext\">ConvNeXt (CVPR'2022)</a></li>\n        <li><a href=\"projects/ConvNeXt-V2\">ConvNeXtv2 (ArXiv'2023)</a></li>\n      </ul>\n      </td>\n      <td>\n      <ul>\n        <li><a href=\"configs/pafpn\">PAFPN (CVPR'2018)</a></li>\n        <li><a href=\"configs/nas_fpn\">NAS-FPN (CVPR'2019)</a></li>\n        <li><a href=\"configs/carafe\">CARAFE (ICCV'2019)</a></li>\n        <li><a href=\"configs/fpg\">FPG (ArXiv'2020)</a></li>\n        <li><a href=\"configs/groie\">GRoIE (ICPR'2020)</a></li>\n        <li><a href=\"configs/dyhead\">DyHead (CVPR'2021)</a></li>\n      </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/ghm\">GHM (AAAI'2019)</a></li>\n          <li><a href=\"configs/gfl\">Generalized Focal Loss (NeurIPS'2020)</a></li>\n          <li><a href=\"configs/seesaw_loss\">Seasaw Loss (CVPR'2021)</a></li>\n        </ul>\n      </td>\n      <td>\n        <ul>\n          <li><a href=\"configs/faster_rcnn/faster_rcnn_r50_fpn_ohem_1x_coco.py\">OHEM (CVPR'2016)</a></li>\n          <li><a href=\"configs/gn\">Group Normalization (ECCV'2018)</a></li>\n          <li><a href=\"configs/dcn\">DCN (ICCV'2017)</a></li>\n          <li><a href=\"configs/dcnv2\">DCNv2 (CVPR'2019)</a></li>\n          <li><a href=\"configs/gn+ws\">Weight Standardization (ArXiv'2019)</a></li>\n          <li><a href=\"configs/pisa\">Prime Sample Attention (CVPR'2020)</a></li>\n          <li><a href=\"configs/strong_baselines\">Strong Baselines (CVPR'2021)</a></li>\n          <li><a href=\"configs/resnet_strikes_back\">Resnet strikes back (ArXiv'2021)</a></li>\n        </ul>\n      </td>\n    </tr>\n</td>\n    </tr>\n  </tbody>\n</table>\n\n我们在[基于 MMDetection 的项目](./docs/zh_cn/notes/projects.md)中列举了一些其他的支持的算法。\n\n## 常见问题\n\n请参考 [FAQ](docs/zh_cn/notes/faq.md) 了解其他用户的常见问题。\n\n## 贡献指南\n\n我们感谢所有的贡献者为改进和提升 MMDetection 所作出的努力。我们将正在进行中的项目添加进了[GitHub Projects](https://github.com/open-mmlab/mmdetection/projects)页面，非常欢迎社区用户能参与进这些项目中来。请参考[贡献指南](.github/CONTRIBUTING.md)来了解参与项目贡献的相关指引。\n\n## 致谢\n\nMMDetection 是一款由来自不同高校和企业的研发人员共同参与贡献的开源项目。我们感谢所有为项目提供算法复现和新功能支持的贡献者，以及提供宝贵反馈的用户。 我们希望这个工具箱和基准测试可以为社区提供灵活的代码工具，供用户复现已有算法并开发自己的新模型，从而不断为开源社区提供贡献。\n\n## 引用\n\n如果你在研究中使用了本项目的代码或者性能基准，请参考如下 bibtex 引用 MMDetection。\n\n```\n@article{mmdetection,\n  title   = {{MMDetection}: Open MMLab Detection Toolbox and Benchmark},\n  author  = {Chen, Kai and Wang, Jiaqi and Pang, Jiangmiao and Cao, Yuhang and\n             Xiong, Yu and Li, Xiaoxiao and Sun, Shuyang and Feng, Wansen and\n             Liu, Ziwei and Xu, Jiarui and Zhang, Zheng and Cheng, Dazhi and\n             Zhu, Chenchen and Cheng, Tianheng and Zhao, Qijie and Li, Buyu and\n             Lu, Xin and Zhu, Rui and Wu, Yue and Dai, Jifeng and Wang, Jingdong\n             and Shi, Jianping and Ouyang, Wanli and Loy, Chen Change and Lin, Dahua},\n  journal= {arXiv preprint arXiv:1906.07155},\n  year={2019}\n}\n```\n\n## 开源许可证\n\n该项目采用 [Apache 2.0 开源许可证](LICENSE)。\n\n## OpenMMLab 的其他项目\n\n- [MMEngine](https://github.com/open-mmlab/mmengine): OpenMMLab 深度学习模型训练基础库\n- [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab 计算机视觉基础库\n- [MMPreTrain](https://github.com/open-mmlab/mmpretrain): OpenMMLab 深度学习预训练工具箱\n- [MMagic](https://github.com/open-mmlab/mmagic): OpenMMLab 新一代人工智能内容生成（AIGC）工具箱\n- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab 目标检测工具箱\n- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab 新一代通用 3D 目标检测平台\n- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab 旋转框检测工具箱与测试基准\n- [MMYOLO](https://github.com/open-mmlab/mmyolo): OpenMMLab YOLO 系列工具箱与测试基准\n- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab 语义分割工具箱\n- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab 全流程文字检测识别理解工具包\n- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab 姿态估计工具箱\n- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 人体参数化模型工具箱与测试基准\n- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab 自监督学习工具箱与测试基准\n- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab 模型压缩工具箱与测试基准\n- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab 少样本学习工具箱与测试基准\n- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab 新一代视频理解工具箱\n- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab 一体化视频目标感知平台\n- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab 光流估计工具箱与测试基准\n- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab 图像视频编辑工具箱\n- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab 图片视频生成模型工具箱\n- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab 模型部署框架\n- [MIM](https://github.com/open-mmlab/mim): OpenMMlab 项目、算法、模型的统一入口\n- [MMEval](https://github.com/open-mmlab/mmeval): 统一开放的跨框架算法评测库\n- [Playground](https://github.com/open-mmlab/playground): 收集和展示 OpenMMLab 相关的前沿、有趣的社区项目\n\n## 欢迎加入 OpenMMLab 社区\n\n扫描下方的二维码可关注 OpenMMLab 团队的 [知乎官方账号](https://www.zhihu.com/people/openmmlab)，扫描下方微信二维码添加喵喵好友，进入 MMDectection 微信交流社群。【加好友申请格式：研究方向+地区+学校/公司+姓名】\n\n<div align=\"center\">\n<img src=\"resources/zhihu_qrcode.jpg\" height=\"400\" />  <img src=\"resources/miaomiao_qrcode.jpg\" height=\"400\" />\n</div>\n\n我们会在 OpenMMLab 社区为大家\n\n- 📢 分享 AI 框架的前沿核心技术\n- 💻 解读 PyTorch 常用模块源码\n- 📰 发布 OpenMMLab 的相关新闻\n- 🚀 介绍 OpenMMLab 开发的前沿算法\n- 🏃 获取更高效的问题答疑和意见反馈\n- 🔥 提供与各行各业开发者充分交流的平台\n\n干货满满 📘，等你来撩 💗，OpenMMLab 社区期待您的加入 👬\n"
  },
  {
    "path": "configs/_base_/datasets/ade20k_instance.py",
    "content": "# dataset settings\ndataset_type = 'ADE20KInstanceDataset'\ndata_root = 'data/ADEChallengeData2016/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/ADEChallengeData2016/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(2560, 640), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='ade20k_instance_val.json',\n        data_prefix=dict(img='images/validation'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'ade20k_instance_val.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/ade20k_panoptic.py",
    "content": "# dataset settings\ndataset_type = 'ADE20KPanopticDataset'\ndata_root = 'data/ADEChallengeData2016/'\n\nbackend_args = None\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(2560, 640), keep_ratio=True),\n    dict(type='LoadPanopticAnnotations', backend_args=backend_args),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=0,\n    persistent_workers=False,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='ade20k_panoptic_val.json',\n        data_prefix=dict(img='images/validation/', seg='ade20k_panoptic_val/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoPanopticMetric',\n    ann_file=data_root + 'ade20k_panoptic_val.json',\n    seg_prefix=data_root + 'ade20k_panoptic_val/',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/ade20k_semantic.py",
    "content": "dataset_type = 'ADE20KSegDataset'\ndata_root = 'data/ADEChallengeData2016/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/ADEChallengeData2016/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(2048, 512), keep_ratio=True),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=False,\n        with_mask=False,\n        with_seg=True,\n        reduce_zero_label=True),\n    dict(\n        type='PackDetInputs', meta_keys=('img_path', 'ori_shape', 'img_shape'))\n]\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img_path='images/validation',\n            seg_map_path='annotations/validation'),\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type='SemSegMetric', iou_metrics=['mIoU'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/cityscapes_detection.py",
    "content": "# dataset settings\ndataset_type = 'CityscapesDataset'\ndata_root = 'data/cityscapes/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/segmentation/cityscapes/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/segmentation/',\n#          'data/': 's3://openmmlab/datasets/segmentation/'\n#      }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=[(2048, 800), (2048, 1024)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(2048, 1024), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',\n        times=8,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instancesonly_filtered_gtFine_train.json',\n            data_prefix=dict(img='leftImg8bit/train/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instancesonly_filtered_gtFine_val.json',\n        data_prefix=dict(img='leftImg8bit/val/'),\n        test_mode=True,\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=test_pipeline,\n        backend_args=backend_args))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instancesonly_filtered_gtFine_val.json',\n    metric='bbox',\n    backend_args=backend_args)\n\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/cityscapes_instance.py",
    "content": "# dataset settings\ndataset_type = 'CityscapesDataset'\ndata_root = 'data/cityscapes/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/segmentation/cityscapes/'\n\n# Method 2: Use backend_args, file_client_args in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/segmentation/',\n#          'data/': 's3://openmmlab/datasets/segmentation/'\n#      }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=[(2048, 800), (2048, 1024)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(2048, 1024), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',\n        times=8,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instancesonly_filtered_gtFine_train.json',\n            data_prefix=dict(img='leftImg8bit/train/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instancesonly_filtered_gtFine_val.json',\n        data_prefix=dict(img='leftImg8bit/val/'),\n        test_mode=True,\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=test_pipeline,\n        backend_args=backend_args))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = [\n    dict(\n        type='CocoMetric',\n        ann_file=data_root +\n        'annotations/instancesonly_filtered_gtFine_val.json',\n        metric=['bbox', 'segm'],\n        backend_args=backend_args),\n    dict(\n        type='CityScapesMetric',\n        seg_prefix=data_root + 'gtFine/val',\n        outfile_prefix='./work_dirs/cityscapes_metric/instance',\n        backend_args=backend_args)\n]\n\ntest_evaluator = val_evaluator\n\n# inference on test dataset and\n# format the output results for submission.\n# test_dataloader = dict(\n#     batch_size=1,\n#     num_workers=2,\n#     persistent_workers=True,\n#     drop_last=False,\n#     sampler=dict(type='DefaultSampler', shuffle=False),\n#     dataset=dict(\n#         type=dataset_type,\n#         data_root=data_root,\n#         ann_file='annotations/instancesonly_filtered_gtFine_test.json',\n#         data_prefix=dict(img='leftImg8bit/test/'),\n#         test_mode=True,\n#         filter_cfg=dict(filter_empty_gt=True, min_size=32),\n#         pipeline=test_pipeline))\n# test_evaluator = dict(\n#         type='CityScapesMetric',\n#         format_only=True,\n#         outfile_prefix='./work_dirs/cityscapes_metric/test')\n"
  },
  {
    "path": "configs/_base_/datasets/coco_caption.py",
    "content": "# data settings\n\ndataset_type = 'CocoCaptionDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        imdecode_backend='pillow',\n        backend_args=backend_args),\n    dict(\n        type='Resize',\n        scale=(224, 224),\n        interpolation='bicubic',\n        backend='pillow'),\n    dict(type='PackInputs', meta_keys=['image_id']),\n]\n\n# ann_file download from\n# train dataset: https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_train.json # noqa\n# val dataset: https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val.json # noqa\n# test dataset: https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test.json # noqa\n# val evaluator: https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val_gt.json # noqa\n# test evaluator: https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test_gt.json # noqa\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/coco_karpathy_val.json',\n        pipeline=test_pipeline,\n    ))\n\nval_evaluator = dict(\n    type='COCOCaptionMetric',\n    ann_file=data_root + 'annotations/coco_karpathy_val_gt.json',\n)\n\n# # If you want standard test, please manually configure the test dataset\ntest_dataloader = val_dataloader\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/coco_detection.py",
    "content": "# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# inference on test dataset and\n# format the output results for submission.\n# test_dataloader = dict(\n#     batch_size=1,\n#     num_workers=2,\n#     persistent_workers=True,\n#     drop_last=False,\n#     sampler=dict(type='DefaultSampler', shuffle=False),\n#     dataset=dict(\n#         type=dataset_type,\n#         data_root=data_root,\n#         ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#         data_prefix=dict(img='test2017/'),\n#         test_mode=True,\n#         pipeline=test_pipeline))\n# test_evaluator = dict(\n#     type='CocoMetric',\n#     metric='bbox',\n#     format_only=True,\n#     ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#     outfile_prefix='./work_dirs/coco_detection/test')\n"
  },
  {
    "path": "configs/_base_/datasets/coco_instance.py",
    "content": "# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# inference on test dataset and\n# format the output results for submission.\n# test_dataloader = dict(\n#     batch_size=1,\n#     num_workers=2,\n#     persistent_workers=True,\n#     drop_last=False,\n#     sampler=dict(type='DefaultSampler', shuffle=False),\n#     dataset=dict(\n#         type=dataset_type,\n#         data_root=data_root,\n#         ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#         data_prefix=dict(img='test2017/'),\n#         test_mode=True,\n#         pipeline=test_pipeline))\n# test_evaluator = dict(\n#     type='CocoMetric',\n#     metric=['bbox', 'segm'],\n#     format_only=True,\n#     ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#     outfile_prefix='./work_dirs/coco_instance/test')\n"
  },
  {
    "path": "configs/_base_/datasets/coco_instance_semantic.py",
    "content": "# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(\n        type='LoadAnnotations', with_bbox=True, with_mask=True, with_seg=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(\n        type='LoadAnnotations', with_bbox=True, with_mask=True, with_seg=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/', seg='stuffthingmaps/train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/coco_panoptic.py",
    "content": "# dataset settings\ndataset_type = 'CocoPanopticDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadPanopticAnnotations', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadPanopticAnnotations', backend_args=backend_args),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/panoptic_train2017.json',\n        data_prefix=dict(\n            img='train2017/', seg='annotations/panoptic_train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/panoptic_val2017.json',\n        data_prefix=dict(img='val2017/', seg='annotations/panoptic_val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoPanopticMetric',\n    ann_file=data_root + 'annotations/panoptic_val2017.json',\n    seg_prefix=data_root + 'annotations/panoptic_val2017/',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# inference on test dataset and\n# format the output results for submission.\n# test_dataloader = dict(\n#     batch_size=1,\n#     num_workers=1,\n#     persistent_workers=True,\n#     drop_last=False,\n#     sampler=dict(type='DefaultSampler', shuffle=False),\n#     dataset=dict(\n#         type=dataset_type,\n#         data_root=data_root,\n#         ann_file='annotations/panoptic_image_info_test-dev2017.json',\n#         data_prefix=dict(img='test2017/'),\n#         test_mode=True,\n#         pipeline=test_pipeline))\n# test_evaluator = dict(\n#     type='CocoPanopticMetric',\n#     format_only=True,\n#     ann_file=data_root + 'annotations/panoptic_image_info_test-dev2017.json',\n#     outfile_prefix='./work_dirs/coco_panoptic/test')\n"
  },
  {
    "path": "configs/_base_/datasets/coco_semantic.py",
    "content": "# dataset settings\ndataset_type = 'CocoSegDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=False,\n        with_label=False,\n        with_seg=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=False,\n        with_label=False,\n        with_seg=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_path', 'ori_shape', 'img_shape', 'scale_factor'))\n]\n\n# For stuffthingmaps_semseg, please refer to\n# `docs/en/user_guides/dataset_prepare.md`\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img_path='train2017/',\n            seg_map_path='stuffthingmaps_semseg/train2017/'),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img_path='val2017/',\n            seg_map_path='stuffthingmaps_semseg/val2017/'),\n        pipeline=test_pipeline))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type='SemSegMetric', iou_metrics=['mIoU'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/deepfashion.py",
    "content": "# dataset settings\ndataset_type = 'DeepFashionDataset'\ndata_root = 'data/DeepFashion/In-shop/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='Resize', scale=(750, 1101), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(750, 1101), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',\n        times=2,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='Anno/segmentation/DeepFashion_segmentation_train.json',\n            data_prefix=dict(img='Img/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='Anno/segmentation/DeepFashion_segmentation_query.json',\n        data_prefix=dict(img='Img/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='Anno/segmentation/DeepFashion_segmentation_gallery.json',\n        data_prefix=dict(img='Img/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root +\n    'Anno/segmentation/DeepFashion_segmentation_query.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root +\n    'Anno/segmentation/DeepFashion_segmentation_gallery.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\n"
  },
  {
    "path": "configs/_base_/datasets/dsdl.py",
    "content": "dataset_type = 'DSDLDetDataset'\ndata_root = 'path to dataset folder'\ntrain_ann = 'path to train yaml file'\nval_ann = 'path to val yaml file'\n\nbackend_args = None\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': \"s3://open_data/\",\n#         'data/': \"s3://open_data/\"\n#     }))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'instances'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file=train_ann,\n        filter_cfg=dict(filter_empty_gt=True, min_size=32, bbox_min_size=32),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file=val_ann,\n        test_mode=True,\n        pipeline=test_pipeline))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type='CocoMetric', metric='bbox')\n# val_evaluator = dict(type='VOCMetric', metric='mAP', eval_mode='11points')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/isaid_instance.py",
    "content": "# dataset settings\ndataset_type = 'iSAIDDataset'\ndata_root = 'data/iSAID/'\nbackend_args = None\n\n# Please see  `projects/iSAID/README.md` for data preparation\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='Resize', scale=(800, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(800, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='train/instancesonly_filtered_train.json',\n        data_prefix=dict(img='train/images/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='val/instancesonly_filtered_val.json',\n        data_prefix=dict(img='val/images/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'val/instancesonly_filtered_val.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/lvis_v0.5_instance.py",
    "content": "# dataset settings\ndataset_type = 'LVISV05Dataset'\ndata_root = 'data/lvis_v0.5/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/lvis_v0.5/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/lvis_v0.5_train.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/lvis_v0.5_val.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='LVISMetric',\n    ann_file=data_root + 'annotations/lvis_v0.5_val.json',\n    metric=['bbox', 'segm'],\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/lvis_v1_instance.py",
    "content": "# dataset settings\n_base_ = 'lvis_v0.5_instance.py'\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/lvis_v1/'\n\ntrain_dataloader = dict(\n    dataset=dict(\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/lvis_v1_train.json',\n            data_prefix=dict(img=''))))\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/lvis_v1_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(ann_file=data_root + 'annotations/lvis_v1_val.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/mot_challenge.py",
    "content": "# dataset settings\ndataset_type = 'MOTChallengeDataset'\ndata_root = 'data/MOT17/'\nimg_scale = (1088, 1088)\n\nbackend_args = None\n# data pipeline\ntrain_pipeline = [\n    dict(\n        type='UniformRefFrameSample',\n        num_ref_imgs=1,\n        frame_range=10,\n        filter_key_img=True),\n    dict(\n        type='TransformBroadcaster',\n        share_random_params=True,\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=backend_args),\n            dict(type='LoadTrackAnnotations'),\n            dict(\n                type='RandomResize',\n                scale=img_scale,\n                ratio_range=(0.8, 1.2),\n                keep_ratio=True,\n                clip_object_border=False),\n            dict(type='PhotoMetricDistortion')\n        ]),\n    dict(\n        type='TransformBroadcaster',\n        # different cropped positions for different frames\n        share_random_params=False,\n        transforms=[\n            dict(\n                type='RandomCrop', crop_size=img_scale, bbox_clip_border=False)\n        ]),\n    dict(\n        type='TransformBroadcaster',\n        share_random_params=True,\n        transforms=[\n            dict(type='RandomFlip', prob=0.5),\n        ]),\n    dict(type='PackTrackInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='TransformBroadcaster',\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=backend_args),\n            dict(type='Resize', scale=img_scale, keep_ratio=True),\n            dict(type='LoadTrackAnnotations')\n        ]),\n    dict(type='PackTrackInputs')\n]\n\n# dataloader\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='TrackImgSampler'),  # image-based sampling\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        visibility_thr=-1,\n        ann_file='annotations/half-train_cocoformat.json',\n        data_prefix=dict(img_path='train'),\n        metainfo=dict(classes=('pedestrian', )),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    # Now we support two ways to test, image_based and video_based\n    # if you want to use video_based sampling, you can use as follows\n    # sampler=dict(type='DefaultSampler', shuffle=False, round_up=False),\n    sampler=dict(type='TrackImgSampler'),  # image-based sampling\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/half-val_cocoformat.json',\n        data_prefix=dict(img_path='train'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# evaluator\nval_evaluator = dict(\n    type='MOTChallengeMetric', metric=['HOTA', 'CLEAR', 'Identity'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/mot_challenge_det.py",
    "content": "# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/MOT17/'\n\nbackend_args = None\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args, to_float32=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(1088, 1088),\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True,\n        clip_object_border=False),\n    dict(type='PhotoMetricDistortion'),\n    dict(type='RandomCrop', crop_size=(1088, 1088), bbox_clip_border=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1088, 1088), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/half-train_cocoformat.json',\n        data_prefix=dict(img='train/'),\n        metainfo=dict(classes=('pedestrian', )),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/half-val_cocoformat.json',\n        data_prefix=dict(img='train/'),\n        metainfo=dict(classes=('pedestrian', )),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/half-val_cocoformat.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/mot_challenge_reid.py",
    "content": "# dataset settings\ndataset_type = 'ReIDDataset'\ndata_root = 'data/MOT17/'\n\nbackend_args = None\n# data pipeline\ntrain_pipeline = [\n    dict(\n        type='TransformBroadcaster',\n        share_random_params=False,\n        transforms=[\n            dict(\n                type='LoadImageFromFile',\n                backend_args=backend_args,\n                to_float32=True),\n            dict(\n                type='Resize',\n                scale=(128, 256),\n                keep_ratio=False,\n                clip_object_border=False),\n            dict(type='RandomFlip', prob=0.5, direction='horizontal'),\n        ]),\n    dict(type='PackReIDInputs', meta_keys=('flip', 'flip_direction'))\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args, to_float32=True),\n    dict(type='Resize', scale=(128, 256), keep_ratio=False),\n    dict(type='PackReIDInputs')\n]\n\n# dataloader\ntrain_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        triplet_sampler=dict(num_ids=8, ins_per_id=4),\n        data_prefix=dict(img_path='reid/imgs'),\n        ann_file='reid/meta/train_80.txt',\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        triplet_sampler=None,\n        data_prefix=dict(img_path='reid/imgs'),\n        ann_file='reid/meta/val_20.txt',\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# evaluator\nval_evaluator = dict(type='ReIDMetrics', metric=['mAP', 'CMC'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/objects365v1_detection.py",
    "content": "# dataset settings\ndataset_type = 'Objects365V1Dataset'\ndata_root = 'data/Objects365/Obj365_v1/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/objects365_train.json',\n        data_prefix=dict(img='train/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/objects365_val.json',\n        data_prefix=dict(img='val/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/objects365_val.json',\n    metric='bbox',\n    sort_categories=True,\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/objects365v2_detection.py",
    "content": "# dataset settings\ndataset_type = 'Objects365V2Dataset'\ndata_root = 'data/Objects365/Obj365_v2/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/zhiyuan_objv2_train.json',\n        data_prefix=dict(img='train/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/zhiyuan_objv2_val.json',\n        data_prefix=dict(img='val/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/zhiyuan_objv2_val.json',\n    metric='bbox',\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/openimages_detection.py",
    "content": "# dataset settings\ndataset_type = 'OpenImagesDataset'\ndata_root = 'data/OpenImages/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1024, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1024, 800), keep_ratio=True),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    # TODO: find a better way to collect image_level_labels\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'instances', 'image_level_labels'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=0,  # workers_per_gpu > 0 may occur out of memory\n    persistent_workers=False,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/oidv6-train-annotations-bbox.csv',\n        data_prefix=dict(img='OpenImages/train/'),\n        label_file='annotations/class-descriptions-boxable.csv',\n        hierarchy_file='annotations/bbox_labels_600_hierarchy.json',\n        meta_file='annotations/train-image-metas.pkl',\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=0,\n    persistent_workers=False,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/validation-annotations-bbox.csv',\n        data_prefix=dict(img='OpenImages/validation/'),\n        label_file='annotations/class-descriptions-boxable.csv',\n        hierarchy_file='annotations/bbox_labels_600_hierarchy.json',\n        meta_file='annotations/validation-image-metas.pkl',\n        image_level_ann_file='annotations/validation-'\n        'annotations-human-imagelabels-boxable.csv',\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='OpenImagesMetric',\n    iou_thrs=0.5,\n    ioa_thrs=0.5,\n    use_group_of=True,\n    get_supercategory=True)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/refcoco+.py",
    "content": "# dataset settings\ndataset_type = 'RefCocoDataset'\ndata_root = 'data/coco/'\n\nbackend_args = None\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='LoadAnnotations',\n        with_mask=True,\n        with_bbox=False,\n        with_seg=False,\n        with_label=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'gt_masks', 'text'))\n]\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(img_path='train2014/'),\n        ann_file='refcoco+/instances.json',\n        split_file='refcoco+/refs(unc).p',\n        split='val',\n        text_mode='select_first',\n        pipeline=test_pipeline))\n\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(img_path='train2014/'),\n        ann_file='refcoco+/instances.json',\n        split_file='refcoco+/refs(unc).p',\n        split='testA',  # or 'testB'\n        text_mode='select_first',\n        pipeline=test_pipeline))\n\nval_evaluator = dict(type='RefSegMetric', metric=['cIoU', 'mIoU'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/refcoco.py",
    "content": "# dataset settings\ndataset_type = 'RefCocoDataset'\ndata_root = 'data/coco/'\n\nbackend_args = None\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='LoadAnnotations',\n        with_mask=True,\n        with_bbox=False,\n        with_seg=False,\n        with_label=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'gt_masks', 'text'))\n]\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(img_path='train2014/'),\n        ann_file='refcoco/instances.json',\n        split_file='refcoco/refs(unc).p',\n        split='val',\n        text_mode='select_first',\n        pipeline=test_pipeline))\n\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(img_path='train2014/'),\n        ann_file='refcoco/instances.json',\n        split_file='refcoco/refs(unc).p',\n        split='testA',  # or 'testB'\n        text_mode='select_first',\n        pipeline=test_pipeline))\n\nval_evaluator = dict(type='RefSegMetric', metric=['cIoU', 'mIoU'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/refcocog.py",
    "content": "# dataset settings\ndataset_type = 'RefCocoDataset'\ndata_root = 'data/coco/'\n\nbackend_args = None\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='LoadAnnotations',\n        with_mask=True,\n        with_bbox=False,\n        with_seg=False,\n        with_label=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'gt_masks', 'text'))\n]\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(img_path='train2014/'),\n        ann_file='refcocog/instances.json',\n        split_file='refcocog/refs(umd).p',\n        split='val',\n        text_mode='select_first',\n        pipeline=test_pipeline))\n\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(img_path='train2014/'),\n        ann_file='refcocog/instances.json',\n        split_file='refcocog/refs(umd).p',\n        split='test',\n        text_mode='select_first',\n        pipeline=test_pipeline))\n\nval_evaluator = dict(type='RefSegMetric', metric=['cIoU', 'mIoU'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/semi_coco_detection.py",
    "content": "# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ncolor_space = [\n    [dict(type='ColorTransform')],\n    [dict(type='AutoContrast')],\n    [dict(type='Equalize')],\n    [dict(type='Sharpness')],\n    [dict(type='Posterize')],\n    [dict(type='Solarize')],\n    [dict(type='Color')],\n    [dict(type='Contrast')],\n    [dict(type='Brightness')],\n]\n\ngeometric = [\n    [dict(type='Rotate')],\n    [dict(type='ShearX')],\n    [dict(type='ShearY')],\n    [dict(type='TranslateX')],\n    [dict(type='TranslateY')],\n]\n\nscale = [(1333, 400), (1333, 1200)]\n\nbranch_field = ['sup', 'unsup_teacher', 'unsup_student']\n# pipeline used to augment labeled data,\n# which will be sent to student model for supervised training.\nsup_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='RandAugment', aug_space=color_space, aug_num=1),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='MultiBranch',\n        branch_field=branch_field,\n        sup=dict(type='PackDetInputs'))\n]\n\n# pipeline used to augment unlabeled data weakly,\n# which will be sent to teacher model for predicting pseudo instances.\nweak_pipeline = [\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction',\n                   'homography_matrix')),\n]\n\n# pipeline used to augment unlabeled data strongly,\n# which will be sent to student model for unsupervised training.\nstrong_pipeline = [\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomOrder',\n        transforms=[\n            dict(type='RandAugment', aug_space=color_space, aug_num=1),\n            dict(type='RandAugment', aug_space=geometric, aug_num=1),\n        ]),\n    dict(type='RandomErasing', n_patches=(1, 5), ratio=(0, 0.2)),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction',\n                   'homography_matrix')),\n]\n\n# pipeline used to augment unlabeled data into different views\nunsup_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadEmptyAnnotations'),\n    dict(\n        type='MultiBranch',\n        branch_field=branch_field,\n        unsup_teacher=weak_pipeline,\n        unsup_student=strong_pipeline,\n    )\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nbatch_size = 5\nnum_workers = 5\n# There are two common semi-supervised learning settings on the coco dataset：\n# (1) Divide the train2017 into labeled and unlabeled datasets\n# by a fixed percentage, such as 1%, 2%, 5% and 10%.\n# The format of labeled_ann_file and unlabeled_ann_file are\n# instances_train2017.{fold}@{percent}.json, and\n# instances_train2017.{fold}@{percent}-unlabeled.json\n# `fold` is used for cross-validation, and `percent` represents\n# the proportion of labeled data in the train2017.\n# (2) Choose the train2017 as the labeled dataset\n# and unlabeled2017 as the unlabeled dataset.\n# The labeled_ann_file and unlabeled_ann_file are\n# instances_train2017.json and image_info_unlabeled2017.json\n# We use this configuration by default.\nlabeled_dataset = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='annotations/instances_train2017.json',\n    data_prefix=dict(img='train2017/'),\n    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n    pipeline=sup_pipeline,\n    backend_args=backend_args)\n\nunlabeled_dataset = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='annotations/instances_unlabeled2017.json',\n    data_prefix=dict(img='unlabeled2017/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=unsup_pipeline,\n    backend_args=backend_args)\n\ntrain_dataloader = dict(\n    batch_size=batch_size,\n    num_workers=num_workers,\n    persistent_workers=True,\n    sampler=dict(\n        type='GroupMultiSourceSampler',\n        batch_size=batch_size,\n        source_ratio=[1, 4]),\n    dataset=dict(\n        type='ConcatDataset', datasets=[labeled_dataset, unlabeled_dataset]))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/v3det.py",
    "content": "# dataset settings\ndataset_type = 'V3DetDataset'\ndata_root = 'data/V3Det/'\n\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/v3det_2023_v1_train.json',\n            data_prefix=dict(img=''),\n            filter_cfg=dict(filter_empty_gt=True, min_size=4),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/v3det_2023_v1_val.json',\n        data_prefix=dict(img=''),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/v3det_2023_v1_val.json',\n    metric='bbox',\n    format_only=False,\n    backend_args=backend_args,\n    use_mp_eval=True,\n    proposal_nums=[300])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/voc0712.py",
    "content": "# dataset settings\ndataset_type = 'VOCDataset'\ndata_root = 'data/VOCdevkit/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically Infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/segmentation/VOCdevkit/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/segmentation/',\n#         'data/': 's3://openmmlab/datasets/segmentation/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',\n        times=3,\n        dataset=dict(\n            type='ConcatDataset',\n            # VOCDataset will add different `dataset_type` in dataset.metainfo,\n            # which will get error if using ConcatDataset. Adding\n            # `ignore_keys` can avoid this error.\n            ignore_keys=['dataset_type'],\n            datasets=[\n                dict(\n                    type=dataset_type,\n                    data_root=data_root,\n                    ann_file='VOC2007/ImageSets/Main/trainval.txt',\n                    data_prefix=dict(sub_data_root='VOC2007/'),\n                    filter_cfg=dict(\n                        filter_empty_gt=True, min_size=32, bbox_min_size=32),\n                    pipeline=train_pipeline,\n                    backend_args=backend_args),\n                dict(\n                    type=dataset_type,\n                    data_root=data_root,\n                    ann_file='VOC2012/ImageSets/Main/trainval.txt',\n                    data_prefix=dict(sub_data_root='VOC2012/'),\n                    filter_cfg=dict(\n                        filter_empty_gt=True, min_size=32, bbox_min_size=32),\n                    pipeline=train_pipeline,\n                    backend_args=backend_args)\n            ])))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='VOC2007/ImageSets/Main/test.txt',\n        data_prefix=dict(sub_data_root='VOC2007/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\n# Pascal VOC2007 uses `11points` as default evaluate mode, while PASCAL\n# VOC2012 defaults to use 'area'.\nval_evaluator = dict(type='VOCMetric', metric='mAP', eval_mode='11points')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/wider_face.py",
    "content": "# dataset settings\ndataset_type = 'WIDERFaceDataset'\ndata_root = 'data/WIDERFace/'\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/cityscapes/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#          'data/': 's3://openmmlab/datasets/detection/'\n#      }))\nbackend_args = None\n\nimg_scale = (640, 640)  # VGA resolution\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=img_scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=img_scale, keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='train.txt',\n        data_prefix=dict(img='WIDER_train'),\n        filter_cfg=dict(filter_empty_gt=True, bbox_min_size=17, min_size=32),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='val.txt',\n        data_prefix=dict(img='WIDER_val'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    # TODO: support WiderFace-Evaluation for easy, medium, hard cases\n    type='VOCMetric',\n    metric='mAP',\n    eval_mode='11points')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/_base_/datasets/youtube_vis.py",
    "content": "dataset_type = 'YouTubeVISDataset'\ndata_root = 'data/youtube_vis_2019/'\ndataset_version = data_root[-5:-1]  # 2019 or 2021\n\nbackend_args = None\n\n# dataset settings\ntrain_pipeline = [\n    dict(\n        type='UniformRefFrameSample',\n        num_ref_imgs=1,\n        frame_range=100,\n        filter_key_img=True),\n    dict(\n        type='TransformBroadcaster',\n        share_random_params=True,\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=backend_args),\n            dict(type='LoadTrackAnnotations', with_mask=True),\n            dict(type='Resize', scale=(640, 360), keep_ratio=True),\n            dict(type='RandomFlip', prob=0.5),\n        ]),\n    dict(type='PackTrackInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='TransformBroadcaster',\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=backend_args),\n            dict(type='Resize', scale=(640, 360), keep_ratio=True),\n            dict(type='LoadTrackAnnotations', with_mask=True),\n        ]),\n    dict(type='PackTrackInputs')\n]\n\n# dataloader\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    # sampler=dict(type='TrackImgSampler'),  # image-based sampling\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='TrackAspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2019_train.json',\n        data_prefix=dict(img_path='train/JPEGImages'),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False, round_up=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2019_valid.json',\n        data_prefix=dict(img_path='valid/JPEGImages'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/_base_/default_runtime.py",
    "content": "default_scope = 'mmdet'\n\ndefault_hooks = dict(\n    timer=dict(type='IterTimerHook'),\n    logger=dict(type='LoggerHook', interval=50),\n    param_scheduler=dict(type='ParamSchedulerHook'),\n    checkpoint=dict(type='CheckpointHook', interval=1),\n    sampler_seed=dict(type='DistSamplerSeedHook'),\n    visualization=dict(type='DetVisualizationHook'))\n\nenv_cfg = dict(\n    cudnn_benchmark=False,\n    mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),\n    dist_cfg=dict(backend='nccl'),\n)\n\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=True)\n\nlog_level = 'INFO'\nload_from = None\nresume = False\n"
  },
  {
    "path": "configs/_base_/models/cascade-mask-rcnn_r50_fpn.py",
    "content": "# model settings\nmodel = dict(\n    type='CascadeRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        type='CascadeRoIHead',\n        num_stages=3,\n        stage_loss_weights=[1, 0.5, 0.25],\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=80,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.5,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)))\n"
  },
  {
    "path": "configs/_base_/models/cascade-rcnn_r50_fpn.py",
    "content": "# model settings\nmodel = dict(\n    type='CascadeRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        type='CascadeRoIHead',\n        num_stages=3,\n        stage_loss_weights=[1, 0.5, 0.25],\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ]),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.5,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)))\n"
  },
  {
    "path": "configs/_base_/models/fast-rcnn_r50_fpn.py",
    "content": "# model settings\nmodel = dict(\n    type='FastRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    roi_head=dict(\n        type='StandardRoIHead',\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rcnn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)))\n"
  },
  {
    "path": "configs/_base_/models/faster-rcnn_r50-caffe-c4.py",
    "content": "# model settings\nnorm_cfg = dict(type='BN', requires_grad=False)\nmodel = dict(\n    type='FasterRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=3,\n        strides=(1, 2, 2),\n        dilations=(1, 1, 1),\n        out_indices=(2, ),\n        frozen_stages=1,\n        norm_cfg=norm_cfg,\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=1024,\n        feat_channels=1024,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[2, 4, 8, 16, 32],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[16]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    roi_head=dict(\n        type='StandardRoIHead',\n        shared_head=dict(\n            type='ResLayer',\n            depth=50,\n            stage=3,\n            stride=2,\n            dilation=1,\n            style='caffe',\n            norm_cfg=norm_cfg,\n            norm_eval=True,\n            init_cfg=dict(\n                type='Pretrained',\n                checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=1024,\n            featmap_strides=[16]),\n        bbox_head=dict(\n            type='BBoxHead',\n            with_avg_pool=True,\n            roi_feat_size=7,\n            in_channels=2048,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=12000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=6000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)))\n"
  },
  {
    "path": "configs/_base_/models/faster-rcnn_r50-caffe-dc5.py",
    "content": "# model settings\nnorm_cfg = dict(type='BN', requires_grad=False)\nmodel = dict(\n    type='FasterRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        strides=(1, 2, 2, 1),\n        dilations=(1, 1, 1, 2),\n        out_indices=(3, ),\n        frozen_stages=1,\n        norm_cfg=norm_cfg,\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=2048,\n        feat_channels=2048,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[2, 4, 8, 16, 32],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[16]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    roi_head=dict(\n        type='StandardRoIHead',\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=2048,\n            featmap_strides=[16]),\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            in_channels=2048,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=12000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms=dict(type='nms', iou_threshold=0.7),\n            nms_pre=6000,\n            max_per_img=1000,\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)))\n"
  },
  {
    "path": "configs/_base_/models/faster-rcnn_r50_fpn.py",
    "content": "# model settings\nmodel = dict(\n    type='FasterRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    roi_head=dict(\n        type='StandardRoIHead',\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)\n        # soft-nms is also supported for rcnn testing\n        # e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05)\n    ))\n"
  },
  {
    "path": "configs/_base_/models/mask-rcnn_r50-caffe-c4.py",
    "content": "# model settings\nnorm_cfg = dict(type='BN', requires_grad=False)\nmodel = dict(\n    type='MaskRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=3,\n        strides=(1, 2, 2),\n        dilations=(1, 1, 1),\n        out_indices=(2, ),\n        frozen_stages=1,\n        norm_cfg=norm_cfg,\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=1024,\n        feat_channels=1024,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[2, 4, 8, 16, 32],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[16]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    roi_head=dict(\n        type='StandardRoIHead',\n        shared_head=dict(\n            type='ResLayer',\n            depth=50,\n            stage=3,\n            stride=2,\n            dilation=1,\n            style='caffe',\n            norm_cfg=norm_cfg,\n            norm_eval=True),\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=1024,\n            featmap_strides=[16]),\n        bbox_head=dict(\n            type='BBoxHead',\n            with_avg_pool=True,\n            roi_feat_size=7,\n            in_channels=2048,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n        mask_roi_extractor=None,\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=0,\n            in_channels=2048,\n            conv_out_channels=256,\n            num_classes=80,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=12000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            mask_size=14,\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=6000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            max_per_img=1000,\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)))\n"
  },
  {
    "path": "configs/_base_/models/mask-rcnn_r50_fpn.py",
    "content": "# model settings\nmodel = dict(\n    type='MaskRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    roi_head=dict(\n        type='StandardRoIHead',\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=80,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            mask_size=28,\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)))\n"
  },
  {
    "path": "configs/_base_/models/retinanet_r50_fpn.py",
    "content": "# model settings\nmodel = dict(\n    type='RetinaNet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_input',\n        num_outs=5),\n    bbox_head=dict(\n        type='RetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    # model training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        sampler=dict(\n            type='PseudoSampler'),  # Focal loss should use PseudoSampler\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.5),\n        max_per_img=100))\n"
  },
  {
    "path": "configs/_base_/models/rpn_r50-caffe-c4.py",
    "content": "# model settings\nmodel = dict(\n    type='RPN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=3,\n        strides=(1, 2, 2),\n        dilations=(1, 1, 1),\n        out_indices=(2, ),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    neck=None,\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=1024,\n        feat_channels=1024,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[2, 4, 8, 16, 32],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[16]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=12000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)))\n"
  },
  {
    "path": "configs/_base_/models/rpn_r50_fpn.py",
    "content": "# model settings\nmodel = dict(\n    type='RPN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=2000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)))\n"
  },
  {
    "path": "configs/_base_/models/ssd300.py",
    "content": "# model settings\ninput_size = 300\nmodel = dict(\n    type='SingleStageDetector',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[1, 1, 1],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='SSDVGG',\n        depth=16,\n        with_last_pool=False,\n        ceil_mode=True,\n        out_indices=(3, 4),\n        out_feature_indices=(22, 34),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://vgg16_caffe')),\n    neck=dict(\n        type='SSDNeck',\n        in_channels=(512, 1024),\n        out_channels=(512, 1024, 512, 256, 256, 256),\n        level_strides=(2, 2, 1, 1),\n        level_paddings=(1, 1, 0, 0),\n        l2_norm_scale=20),\n    bbox_head=dict(\n        type='SSDHead',\n        in_channels=(512, 1024, 512, 256, 256, 256),\n        num_classes=80,\n        anchor_generator=dict(\n            type='SSDAnchorGenerator',\n            scale_major=False,\n            input_size=input_size,\n            basesize_ratio_range=(0.15, 0.9),\n            strides=[8, 16, 32, 64, 100, 300],\n            ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2])),\n    # model training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0.,\n            ignore_iof_thr=-1,\n            gt_max_assign_all=False),\n        sampler=dict(type='PseudoSampler'),\n        smoothl1_beta=1.,\n        allowed_border=-1,\n        pos_weight=-1,\n        neg_pos_ratio=3,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        nms=dict(type='nms', iou_threshold=0.45),\n        min_bbox_size=0,\n        score_thr=0.02,\n        max_per_img=200))\ncudnn_benchmark = True\n"
  },
  {
    "path": "configs/_base_/schedules/schedule_1x.py",
    "content": "# training schedule for 1x\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/_base_/schedules/schedule_20e.py",
    "content": "# training schedule for 20e\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=20, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=20,\n        by_epoch=True,\n        milestones=[16, 19],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/_base_/schedules/schedule_2x.py",
    "content": "# training schedule for 2x\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=24, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/albu_example/README.md",
    "content": "# Albu Example\n\n> [Albumentations: fast and flexible image augmentations](https://arxiv.org/abs/1809.06839)\n\n<!-- [OTHERS] -->\n\n## Abstract\n\nData augmentation is a commonly used technique for increasing both the size and the diversity of labeled training sets by leveraging input transformations that preserve output labels. In computer vision domain, image augmentations have become a common implicit regularization technique to combat overfitting in deep convolutional neural networks and are ubiquitously used to improve performance. While most deep learning frameworks implement basic image transformations, the list is typically limited to some variations and combinations of flipping, rotating, scaling, and cropping. Moreover, the image processing speed varies in existing tools for image augmentation. We present Albumentations, a fast and flexible library for image augmentations with many various image transform operations available, that is also an easy-to-use wrapper around other augmentation libraries. We provide examples of image augmentations for different computer vision tasks and show that Albumentations is faster than other commonly used image augmentation tools on the most of commonly used image transformations.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143870703-74f3ea3f-ae23-4035-9856-746bc3f88464.png\" height=\"400\" />\n</div>\n\n## Results and Models\n\n| Backbone |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                    Config                     |                                                                                                                                                        Download                                                                                                                                                         |\n| :------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | pytorch |   1x    |   4.4    |      16.6      |  38.0  |  34.5   | [config](./mask-rcnn_r50_fpn_albu-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/albu_example/mask_rcnn_r50_fpn_albu_1x_coco/mask_rcnn_r50_fpn_albu_1x_coco_20200208-ab203bcd.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/albu_example/mask_rcnn_r50_fpn_albu_1x_coco/mask_rcnn_r50_fpn_albu_1x_coco_20200208_225520.log.json) |\n\n## Citation\n\n```latex\n@article{2018arXiv180906839B,\n  author = {A. Buslaev, A. Parinov, E. Khvedchenya, V.~I. Iglovikov and A.~A. Kalinin},\n  title = \"{Albumentations: fast and flexible image augmentations}\",\n  journal = {ArXiv e-prints},\n  eprint = {1809.06839},\n  year = 2018\n}\n```\n"
  },
  {
    "path": "configs/albu_example/mask-rcnn_r50_fpn_albu-1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\n\nalbu_train_transforms = [\n    dict(\n        type='ShiftScaleRotate',\n        shift_limit=0.0625,\n        scale_limit=0.0,\n        rotate_limit=0,\n        interpolation=1,\n        p=0.5),\n    dict(\n        type='RandomBrightnessContrast',\n        brightness_limit=[0.1, 0.3],\n        contrast_limit=[0.1, 0.3],\n        p=0.2),\n    dict(\n        type='OneOf',\n        transforms=[\n            dict(\n                type='RGBShift',\n                r_shift_limit=10,\n                g_shift_limit=10,\n                b_shift_limit=10,\n                p=1.0),\n            dict(\n                type='HueSaturationValue',\n                hue_shift_limit=20,\n                sat_shift_limit=30,\n                val_shift_limit=20,\n                p=1.0)\n        ],\n        p=0.1),\n    dict(type='JpegCompression', quality_lower=85, quality_upper=95, p=0.2),\n    dict(type='ChannelShuffle', p=0.1),\n    dict(\n        type='OneOf',\n        transforms=[\n            dict(type='Blur', blur_limit=3, p=1.0),\n            dict(type='MedianBlur', blur_limit=3, p=1.0)\n        ],\n        p=0.1),\n]\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='Albu',\n        transforms=albu_train_transforms,\n        bbox_params=dict(\n            type='BboxParams',\n            format='pascal_voc',\n            label_fields=['gt_bboxes_labels', 'gt_ignore_flags'],\n            min_visibility=0.0,\n            filter_lost_elements=True),\n        keymap={\n            'img': 'image',\n            'gt_masks': 'masks',\n            'gt_bboxes': 'bboxes'\n        },\n        skip_img_without_anno=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/albu_example/metafile.yml",
    "content": "Models:\n  - Name: mask-rcnn_r50_fpn_albu-1x_coco\n    In Collection: Mask R-CNN\n    Config: mask-rcnn_r50_fpn_albu-1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 34.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/albu_example/mask_rcnn_r50_fpn_albu_1x_coco/mask_rcnn_r50_fpn_albu_1x_coco_20200208-ab203bcd.pth\n"
  },
  {
    "path": "configs/atss/README.md",
    "content": "# ATSS\n\n> [Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection](https://arxiv.org/abs/1912.02424)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nObject detection has been dominated by anchor-based detectors for several years. Recently, anchor-free detectors have become popular due to the proposal of FPN and Focal Loss. In this paper, we first point out that the essential difference between anchor-based and anchor-free detection is actually how to define positive and negative training samples, which leads to the performance gap between them. If they adopt the same definition of positive and negative samples during training, there is no obvious difference in the final performance, no matter regressing from a box or a point. This shows that how to select positive and negative training samples is important for current object detectors. Then, we propose an Adaptive Training Sample Selection (ATSS) to automatically select positive and negative samples according to statistical characteristics of object. It significantly improves the performance of anchor-based and anchor-free detectors and bridges the gap between them. Finally, we discuss the necessity of tiling multiple anchors per location on the image to detect objects. Extensive experiments conducted on MS COCO support our aforementioned analysis and conclusions. With the newly introduced ATSS, we improve state-of-the-art detectors by a large margin to 50.7% AP without introducing any overhead.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143870776-c81168f5-e8b2-44ee-978b-509e4372c5c9.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                Config                |                                                                                                                            Download                                                                                                                             |\n| :------: | :-----: | :-----: | :------: | :------------: | :----: | :----------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | pytorch |   1x    |   3.7    |      19.7      |  39.4  | [config](./atss_r50_fpn_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/atss/atss_r50_fpn_1x_coco/atss_r50_fpn_1x_coco_20200209-985f7bd0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/atss/atss_r50_fpn_1x_coco/atss_r50_fpn_1x_coco_20200209_102539.log.json) |\n|  R-101   | pytorch |   1x    |   5.6    |      12.3      |  41.5  | [config](./atss_r101_fpn_1x_coco.py) |   [model](https://download.openmmlab.com/mmdetection/v2.0/atss/atss_r101_fpn_1x_coco/atss_r101_fpn_1x_20200825-dfcadd6f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/atss/atss_r101_fpn_1x_coco/atss_r101_fpn_1x_20200825-dfcadd6f.log.json)   |\n\n## Citation\n\n```latex\n@article{zhang2019bridging,\n  title   =  {Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection},\n  author  =  {Zhang, Shifeng and Chi, Cheng and Yao, Yongqiang and Lei, Zhen and Li, Stan Z.},\n  journal =  {arXiv preprint arXiv:1912.02424},\n  year    =  {2019}\n}\n```\n"
  },
  {
    "path": "configs/atss/atss_r101_fpn_1x_coco.py",
    "content": "_base_ = './atss_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/atss/atss_r101_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './atss_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/atss/atss_r18_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './atss_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/atss/atss_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\nmodel = dict(\n    type='ATSS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='ATSSHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/atss/atss_r50_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = '../common/lsj-200e_coco-detection.py'\n\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\nmodel = dict(\n    type='ATSS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='ATSSHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.01 * 4, momentum=0.9, weight_decay=0.00004))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/atss/metafile.yml",
    "content": "Collections:\n  - Name: ATSS\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ATSS\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1912.02424\n      Title: 'Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection'\n    README: configs/atss/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/atss.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: atss_r50_fpn_1x_coco\n    In Collection: ATSS\n    Config: configs/atss/atss_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.7\n      inference time (ms/im):\n        - value: 50.76\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/atss/atss_r50_fpn_1x_coco/atss_r50_fpn_1x_coco_20200209-985f7bd0.pth\n\n  - Name: atss_r101_fpn_1x_coco\n    In Collection: ATSS\n    Config: configs/atss/atss_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.6\n      inference time (ms/im):\n        - value: 81.3\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/atss/atss_r101_fpn_1x_coco/atss_r101_fpn_1x_20200825-dfcadd6f.pth\n"
  },
  {
    "path": "configs/autoassign/README.md",
    "content": "# AutoAssign\n\n> [AutoAssign: Differentiable Label Assignment for Dense Object Detection](https://arxiv.org/abs/2007.03496)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nDetermining 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.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143870875-33567e44-0584-4470-9a90-0df0fb6c1fe2.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone | Style | Lr schd | Mem (GB) | box AP |                     Config                      |                                                                                                                                                        Download                                                                                                                                                         |\n| :------: | :---: | :-----: | :------: | :----: | :---------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | caffe |   1x    |   4.08   |  40.4  | [config](./autoassign_r50-caffe_fpn_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) |\n\n**Note**:\n\n1. 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.\n2. 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.\n\n## Citation\n\n```latex\n@article{zhu2020autoassign,\n  title={AutoAssign: Differentiable Label Assignment for Dense Object Detection},\n  author={Zhu, Benjin and Wang, Jianfeng and Jiang, Zhengkai and Zong, Fuhang and Liu, Songtao and Li, Zeming and Sun, Jian},\n  journal={arXiv preprint arXiv:2007.03496},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py",
    "content": "# We follow the original implementation which\n# adopts the Caffe pre-trained backbone.\n_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    type='AutoAssign',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[102.9801, 115.9465, 122.7717],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs=True,\n        num_outs=5,\n        relu_before_extra_convs=True,\n        init_cfg=dict(type='Caffe2Xavier', layer='Conv2d')),\n    bbox_head=dict(\n        type='AutoAssignHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_bbox=dict(type='GIoULoss', loss_weight=5.0)),\n    train_cfg=None,\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.01), paramwise_cfg=dict(norm_decay_mult=0.))\n"
  },
  {
    "path": "configs/autoassign/metafile.yml",
    "content": "Collections:\n  - Name: AutoAssign\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - AutoAssign\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/2007.03496\n      Title: 'AutoAssign: Differentiable Label Assignment for Dense Object Detection'\n    README: configs/autoassign/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.12.0/mmdet/models/detectors/autoassign.py#L6\n      Version: v2.12.0\n\nModels:\n  - Name: autoassign_r50-caffe_fpn_1x_coco\n    In Collection: AutoAssign\n    Config: configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.08\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    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\n"
  },
  {
    "path": "configs/boxinst/README.md",
    "content": "# BoxInst\n\n> [BoxInst: High-Performance Instance Segmentation with Box Annotations](https://arxiv.org/pdf/2012.02310.pdf)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present a high-performance method that can achieve mask-level instance segmentation with only bounding-box annotations for training. While this setting has been studied in the literature, here we show significantly stronger performance with a simple design (e.g., dramatically improving previous best reported mask AP of 21.1% to 31.6% on the COCO dataset). Our core idea is to redesign the loss\nof learning masks in instance segmentation, with no modification to the segmentation network itself. The new loss functions can supervise the mask training without relying on mask annotations. This is made possible with two loss terms, namely, 1) a surrogate term that minimizes the discrepancy between the projections of the ground-truth box and the predicted mask; 2) a pairwise loss that can exploit the prior that proximal pixels with similar colors are very likely to have the same category label. Experiments demonstrate that the redesigned mask loss can yield surprisingly high-quality instance masks with only box annotations. For example, without using any mask annotations, with a ResNet-101 backbone and 3× training schedule, we achieve 33.2% mask AP on COCO test-dev split (vs. 39.1% of the fully supervised counterpart). Our excellent experiment results on COCO and Pascal VOC indicate that our method dramatically narrows the performance gap between weakly and fully supervised instance segmentation.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/57584090/209087723-756b76d7-5061-4000-a93c-df1194a439a0.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |  Style  | MS train | Lr schd | bbox AP | mask AP |                   Config                    |                                                                                                                                                  Download                                                                                                                                                   |\n| :------: | :-----: | :------: | :-----: | :-----: | :-----: | :-----------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | pytorch |    Y     |   1x    |  39.6   |  31.1   | [config](./boxinst_r50_fpn_ms-90k_coco.py)  |  [model](https://download.openmmlab.com/mmdetection/v3.0/boxinst/boxinst_r50_fpn_ms-90k_coco/boxinst_r50_fpn_ms-90k_coco_20221228_163052-6add751a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/boxinst/boxinst_r50_fpn_ms-90k_coco/boxinst_r50_fpn_ms-90k_coco_20221228_163052.log.json)   |\n|  R-101   | pytorch |    Y     |   1x    |  41.8   |  32.7   | [config](./boxinst_r101_fpn_ms-90k_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/boxinst/boxinst_r101_fpn_ms-90k_coco/boxinst_r101_fpn_ms-90k_coco_20221229_145106-facf375b.pth) \\|[log](https://download.openmmlab.com/mmdetection/v3.0/boxinst/boxinst_r101_fpn_ms-90k_coco/boxinst_r101_fpn_ms-90k_coco_20221229_145106.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{tian2020boxinst,\n  title     =  {{BoxInst}: High-Performance Instance Segmentation with Box Annotations},\n  author    =  {Tian, Zhi and Shen, Chunhua and Wang, Xinlong and Chen, Hao},\n  booktitle =  {Proc. IEEE Conf. Computer Vision and Pattern Recognition (CVPR)},\n  year      =  {2021}\n}\n```\n"
  },
  {
    "path": "configs/boxinst/boxinst_r101_fpn_ms-90k_coco.py",
    "content": "_base_ = './boxinst_r50_fpn_ms-90k_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/boxinst/boxinst_r50_fpn_ms-90k_coco.py",
    "content": "_base_ = '../common/ms-90k_coco.py'\n\n# model settings\nmodel = dict(\n    type='BoxInst',\n    data_preprocessor=dict(\n        type='BoxInstDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        mask_stride=4,\n        pairwise_size=3,\n        pairwise_dilation=2,\n        pairwise_color_thresh=0.3,\n        bottom_pixels_removed=10),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50'),\n        style='pytorch'),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        num_outs=5,\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='BoxInstBboxHead',\n        num_params=593,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        norm_on_bbox=True,\n        centerness_on_reg=True,\n        dcn_on_last_conv=False,\n        center_sampling=True,\n        conv_bias=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    mask_head=dict(\n        type='BoxInstMaskHead',\n        num_layers=3,\n        feat_channels=16,\n        size_of_interest=8,\n        mask_out_stride=4,\n        topk_masks_per_img=64,\n        mask_feature_head=dict(\n            in_channels=256,\n            feat_channels=128,\n            start_level=0,\n            end_level=2,\n            out_channels=16,\n            mask_stride=8,\n            num_stacked_convs=4,\n            norm_cfg=dict(type='BN', requires_grad=True)),\n        loss_mask=dict(\n            type='DiceLoss',\n            use_sigmoid=True,\n            activate=True,\n            eps=5e-6,\n            loss_weight=1.0)),\n    # model training and testing settings\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100,\n        mask_thr=0.5))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n\n# evaluator\nval_evaluator = dict(metric=['bbox', 'segm'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/boxinst/metafile.yml",
    "content": "Collections:\n  - Name: BoxInst\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ResNet\n        - FPN\n        - CondInst\n    Paper:\n      URL: https://arxiv.org/abs/2012.02310\n      Title: 'BoxInst: High-Performance Instance Segmentation with Box Annotations'\n    README: configs/boxinst/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v3.0.0rc6/mmdet/models/detectors/boxinst.py#L8\n      Version: v3.0.0rc6\n\nModels:\n  - Name: boxinst_r50_fpn_ms-90k_coco\n    In Collection: BoxInst\n    Config: configs/boxinst/boxinst_r50_fpn_ms-90k_coco.py\n    Metadata:\n      Iterations: 90000\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 30.8\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/boxinst/boxinst_r50_fpn_ms-90k_coco/boxinst_r50_fpn_ms-90k_coco_20221228_163052-6add751a.pth\n\n  - Name: boxinst_r101_fpn_ms-90k_coco\n    In Collection: BoxInst\n    Config: configs/boxinst/boxinst_r101_fpn_ms-90k_coco.py\n    Metadata:\n      Iterations: 90000\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 32.7\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/boxinst/boxinst_r101_fpn_ms-90k_coco/boxinst_r101_fpn_ms-90k_coco_20221229_145106-facf375b.pth\n"
  },
  {
    "path": "configs/bytetrack/README.md",
    "content": "# ByteTrack: Multi-Object Tracking by Associating Every Detection Box\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nMulti-object tracking (MOT) aims at estimating bounding boxes and identities of objects in videos. Most methods obtain identities by associating detection boxes whose scores are higher than a threshold. The objects with low detection scores, e.g. occluded objects, are simply thrown away, which brings non-negligible true object missing and fragmented trajectories. To solve this problem, we present a simple, effective and generic association method, tracking by associating every detection box instead of only the high score ones. For the low score detection boxes, we utilize their similarities with tracklets to recover true objects and filter out the background detections. When applied to 9 different state-of-the-art trackers, our method achieves consistent improvement on IDF1 score ranging from 1 to 10 points. To put forwards the state-of-the-art performance of MOT, we design a simple and strong tracker, named ByteTrack. For the first time, we achieve 80.3 MOTA, 77.3 IDF1 and 63.1 HOTA on the test set of MOT17 with 30 FPS running speed on a single V100 GPU.\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/26813582/147467498-b8d16d8c-8472-4830-8bac-b107c49f7c6f.png\"/>\n</div>\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@inproceedings{zhang2021bytetrack,\n  title={ByteTrack: Multi-Object Tracking by Associating Every Detection Box},\n  author={Zhang, Yifu and Sun, Peize and Jiang, Yi and Yu, Dongdong and Yuan, Zehuan and Luo, Ping and Liu, Wenyu and Wang, Xinggang},\n  journal={arXiv preprint arXiv:2110.06864},\n  year={2021}\n}\n```\n\n## Results and models on MOT17\n\nPlease note that the performance on `MOT17-half-val` is comparable with the performance reported in the manuscript, while the performance on `MOT17-test` is lower than the performance reported in the manuscript.\n\nThe reason is that ByteTrack tunes customized hyper-parameters (e.g., image resolution and the high threshold of detection score) for each video in `MOT17-test` set, while we use unified parameters.\n\n|  Method   | Detector |           Train Set           |    Test Set    | Public | Inf time (fps) | HOTA | MOTA | IDF1 |  FP   |  FN   | IDSw. |                                         Config                                          |                                                                                                                                                           Download                                                                                                                                                           |\n| :-------: | :------: | :---------------------------: | :------------: | :----: | :------------: | :--: | :--: | :--: | :---: | :---: | :---: | :-------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| ByteTrack | YOLOX-X  | CrowdHuman + MOT17-half-train | MOT17-half-val |   N    |       -        | 67.5 | 78.6 | 78.5 | 12852 | 21060 |  672  | [config](bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py) | [model](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500-1985c9f0.pth) \\| [log](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500.log.json) |\n| ByteTrack | YOLOX-X  | CrowdHuman + MOT17-half-train |   MOT17-test   |   N    |       -        | 61.7 | 78.1 | 74.8 | 36705 | 85032 | 2049  |  [config](bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17test.py)   | [model](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500-1985c9f0.pth) \\| [log](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500.log.json) |\n\n## Results and models on MOT20\n\nSince there are only 4 videos in `MOT20-train`, ByteTrack is validated on `MOT17-train` rather than `MOT20-half-train`.\n\nPlease note that the MOTA on `MOT20-test` is slightly lower than that reported in the manuscript, because we don't tune the threshold for each video.\n\n|  Method   | Detector |        Train Set         |  Test Set   | Public | Inf time (fps) | HOTA | MOTA | IDF1 |   FP   |   FN   | IDSw. |                                      Config                                      |                                                                                                                                                      Download                                                                                                                                                      |\n| :-------: | :------: | :----------------------: | :---------: | :----: | :------------: | :--: | :--: | :--: | :----: | :----: | :---: | :------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| ByteTrack | YOLOX-X  | CrowdHuman + MOT20-train | MOT17-train |   N    |       -        | 57.3 | 64.9 | 71.8 | 33,747 | 83,385 | 1,263 | [config](bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot20train_test-mot20test.py) | [model](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot20-private_20220506_101040-9ce38a60.pth) \\| [log](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot20-private_20220506_101040.log.json) |\n| ByteTrack | YOLOX-X  | CrowdHuman + MOT20-train | MOT20-test  |   N    |       -        | 61.5 | 77.0 | 75.4 | 33,083 | 84,433 | 1,345 | [config](bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot20train_test-mot20test.py) | [model](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot20-private_20220506_101040-9ce38a60.pth) \\| [log](https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot20-private_20220506_101040.log.json) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nDue to the influence of parameters such as learning rate in default configuration file, we recommend using 8 GPUs for training in order to reproduce accuracy. You can use the following command to start the training.\n\n**3.1 Joint training and tracking**\n\nSome algorithm like ByteTrack, OCSORT don't need reid model, so we provide joint training and tracking for convenient.\n\n```shell\n# Training Bytetrack on crowdhuman and mot17-half-train dataset with following command\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs\nbash tools/dist_train.sh configs/bytetrack/bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py 8\n```\n\n**3.2 Separate training and tracking**\n\nOf course, we provide train detector independently like SORT, DeepSORT, StrongSORT. Then use this detector to track.\n\n```shell\n# Training Bytetrack on crowdhuman and mot17-half-train dataset with following command\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs\nbash tools/dist_train.sh configs/bytetrack/yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py 8\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 4. Testing and evaluation\n\n### 4.1 Example on MOTxx-halfval dataset\n\n**4.1.1 use joint trained detector to evaluating and testing**\n\n```shell\nbash tools/dist_test_tracking.sh configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py 8 --checkpoint ${CHECKPOINT_FILE}\n```\n\n**4.1.2 use separate trained detector to evaluating and testing**\n\n```shell\nbash tools/dist_test_tracking.sh configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py 8 --detector ${CHECKPOINT_FILE}\n```\n\n**4.1.3 use video_baesd to evaluating and testing**\n\nwe also provide two_ways(img_based or video_based) to evaluating and testing.\nif you want to use video_based to evaluating and testing, you can modify config as follows\n\n```\nval_dataloader = dict(\n    sampler=dict(type='DefaultSampler', shuffle=False, round_up=False))\n```\n\n#### 4.2 Example on MOTxx-test dataset\n\nIf you want to get the results of the [MOT Challenge](https://motchallenge.net/) test set, please use the following command to generate result files that can be used for submission. It will be stored in `./mot_17_test_res`, you can modify the saved path in `test_evaluator` of the config.\n\n```shell\nbash tools/dist_test_tracking.sh configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17test.py 8 --checkpoint ${CHECKPOINT_FILE}\n```\n\nIf you want to know about more detailed usage of `test_tracking.py/dist_test_tracking.sh/slurm_test_tracking.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 5.Inference\n\nUse a single GPU to predict a video and save it as a video.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py --checkpoint ${CHECKPOINT_FILE} --out mot.mp4\n```\n\nIf you want to know about more detailed usage of `mot_demo.py`, please refer to this [document](../../docs/en/user_guides/tracking_inference.md).\n"
  },
  {
    "path": "configs/bytetrack/bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = ['../yolox/yolox_x_8xb8-300e_coco.py']\n\ndataset_type = 'MOTChallengeDataset'\ndata_root = 'data/MOT17/'\n\nimg_scale = (1440, 800)  # weight, height\nbatch_size = 4\n\ndetector = _base_.model\ndetector.pop('data_preprocessor')\ndetector.bbox_head.update(dict(num_classes=1))\ndetector.test_cfg.nms.update(dict(iou_threshold=0.7))\ndetector['init_cfg'] = dict(\n    type='Pretrained',\n    checkpoint=  # noqa: E251\n    'https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_x_8x8_300e_coco/yolox_x_8x8_300e_coco_20211126_140254-1ef88d67.pth'  # noqa: E501\n)\ndel _base_.model\n\nmodel = dict(\n    type='ByteTrack',\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        pad_size_divisor=32,\n        # in bytetrack, we provide joint train detector and evaluate tracking\n        # performance, use_det_processor means use independent detector\n        # data_preprocessor. of course, you can train detector independently\n        # like strongsort\n        use_det_processor=True,\n        batch_augments=[\n            dict(\n                type='BatchSyncRandomResize',\n                random_size_range=(576, 1024),\n                size_divisor=32,\n                interval=10)\n        ]),\n    detector=detector,\n    tracker=dict(\n        type='ByteTracker',\n        motion=dict(type='KalmanFilter'),\n        obj_score_thrs=dict(high=0.6, low=0.1),\n        init_track_thr=0.7,\n        weight_iou_with_det_scores=True,\n        match_iou_thrs=dict(high=0.1, low=0.5, tentative=0.3),\n        num_frames_retain=30))\n\ntrain_pipeline = [\n    dict(\n        type='Mosaic',\n        img_scale=img_scale,\n        pad_val=114.0,\n        bbox_clip_border=False),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        border=(-img_scale[0] // 2, -img_scale[1] // 2),\n        bbox_clip_border=False),\n    dict(\n        type='MixUp',\n        img_scale=img_scale,\n        ratio_range=(0.8, 1.6),\n        pad_val=114.0,\n        bbox_clip_border=False),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='Resize',\n        scale=img_scale,\n        keep_ratio=True,\n        clip_object_border=False),\n    dict(type='Pad', size_divisor=32, pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1), keep_empty=False),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='TransformBroadcaster',\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n            dict(type='Resize', scale=img_scale, keep_ratio=True),\n            dict(\n                type='Pad',\n                size_divisor=32,\n                pad_val=dict(img=(114.0, 114.0, 114.0))),\n            dict(type='LoadTrackAnnotations'),\n        ]),\n    dict(type='PackTrackInputs')\n]\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=batch_size,\n    num_workers=4,\n    persistent_workers=True,\n    pin_memory=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        type='MultiImageMixDataset',\n        dataset=dict(\n            type='ConcatDataset',\n            datasets=[\n                dict(\n                    type='CocoDataset',\n                    data_root='data/MOT17',\n                    ann_file='annotations/half-train_cocoformat.json',\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_train.json',\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_val.json',\n                    data_prefix=dict(img='val'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n            ]),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    _delete_=True,\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    pin_memory=True,\n    drop_last=False,\n    # video_based\n    # sampler=dict(type='DefaultSampler', shuffle=False, round_up=False),\n    sampler=dict(type='TrackImgSampler'),  # image_based\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/half-val_cocoformat.json',\n        data_prefix=dict(img_path='train'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\n# default 8 gpu\nbase_lr = 0.001 / 8 * batch_size\noptim_wrapper = dict(optimizer=dict(lr=base_lr))\n\n# some hyper parameters\n# training settings\nmax_epochs = 80\nnum_last_epochs = 10\ninterval = 5\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop',\n    max_epochs=max_epochs,\n    val_begin=70,\n    val_interval=1)\n\n# learning policy\nparam_scheduler = [\n    dict(\n        # use quadratic formula to warm up 1 epochs\n        type='QuadraticWarmupLR',\n        by_epoch=True,\n        begin=0,\n        end=1,\n        convert_to_iter_based=True),\n    dict(\n        # use cosine lr from 1 to 70 epoch\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=1,\n        T_max=max_epochs - num_last_epochs,\n        end=max_epochs - num_last_epochs,\n        by_epoch=True,\n        convert_to_iter_based=True),\n    dict(\n        # use fixed lr during last 10 epochs\n        type='ConstantLR',\n        by_epoch=True,\n        factor=1,\n        begin=max_epochs - num_last_epochs,\n        end=max_epochs,\n    )\n]\n\ncustom_hooks = [\n    dict(\n        type='YOLOXModeSwitchHook',\n        num_last_epochs=num_last_epochs,\n        priority=48),\n    dict(type='SyncNormHook', priority=48),\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0001,\n        update_buffers=True,\n        priority=49)\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        _delete_=True, type='CheckpointHook', interval=1, max_keep_ckpts=10),\n    visualization=dict(type='TrackVisualizationHook', draw=False))\n\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='TrackLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\n# evaluator\nval_evaluator = dict(\n    _delete_=True,\n    type='MOTChallengeMetric',\n    metric=['HOTA', 'CLEAR', 'Identity'],\n    postprocess_tracklet_cfg=[\n        dict(type='InterpolateTracklets', min_num_frames=5, max_num_frames=20)\n    ])\ntest_evaluator = val_evaluator\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (4 samples per GPU)\nauto_scale_lr = dict(base_batch_size=32)\n\ndel detector\ndel _base_.tta_model\ndel _base_.tta_pipeline\ndel _base_.train_dataset\n"
  },
  {
    "path": "configs/bytetrack/bytetrack_yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test.py",
    "content": "_base_ = [\n    './bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_'\n    'test-mot17halfval.py'\n]\n\ndataset_type = 'MOTChallengeDataset'\n\nimg_scale = (1600, 896)  # weight, height\n\nmodel = dict(\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        use_det_processor=True,\n        pad_size_divisor=32,\n        batch_augments=[\n            dict(type='BatchSyncRandomResize', random_size_range=(640, 1152))\n        ]),\n    tracker=dict(\n        weight_iou_with_det_scores=False,\n        match_iou_thrs=dict(high=0.3),\n    ))\n\ntrain_pipeline = [\n    dict(\n        type='Mosaic',\n        img_scale=img_scale,\n        pad_val=114.0,\n        bbox_clip_border=True),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        border=(-img_scale[0] // 2, -img_scale[1] // 2),\n        bbox_clip_border=True),\n    dict(\n        type='MixUp',\n        img_scale=img_scale,\n        ratio_range=(0.8, 1.6),\n        pad_val=114.0,\n        bbox_clip_border=True),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='Resize',\n        scale=img_scale,\n        keep_ratio=True,\n        clip_object_border=True),\n    dict(type='Pad', size_divisor=32, pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1), keep_empty=False),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='TransformBroadcaster',\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n            dict(type='Resize', scale=img_scale, keep_ratio=True),\n            dict(\n                type='Pad',\n                size_divisor=32,\n                pad_val=dict(img=(114.0, 114.0, 114.0))),\n            dict(type='LoadTrackAnnotations'),\n        ]),\n    dict(type='PackTrackInputs')\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        type='MultiImageMixDataset',\n        dataset=dict(\n            type='ConcatDataset',\n            datasets=[\n                dict(\n                    type='CocoDataset',\n                    data_root='data/MOT20',\n                    ann_file='annotations/train_cocoformat.json',\n                    # TODO: mmdet use img as key, but img_path is needed\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_train.json',\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_val.json',\n                    data_prefix=dict(img='val'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n            ]),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    dataset=dict(ann_file='annotations/train_cocoformat.json'))\n\ntest_dataloader = dict(\n    dataset=dict(\n        data_root='data/MOT20', ann_file='annotations/test_cocoformat.json'))\n\ntest_evaluator = dict(\n    type='MOTChallengeMetrics',\n    postprocess_tracklet_cfg=[\n        dict(type='InterpolateTracklets', min_num_frames=5, max_num_frames=20)\n    ],\n    format_only=True,\n    outfile_prefix='./mot_20_test_res')\n"
  },
  {
    "path": "configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    './bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_'\n    'test-mot17halfval.py'\n]\n\n# fp16 settings\noptim_wrapper = dict(type='AmpOptimWrapper', loss_scale='dynamic')\nval_cfg = dict(type='ValLoop', fp16=True)\ntest_cfg = dict(type='TestLoop', fp16=True)\n"
  },
  {
    "path": "configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17test.py",
    "content": "_base_ = [\n    './bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-'\n    'mot17halftrain_test-mot17halfval.py'\n]\n\ntest_dataloader = dict(\n    dataset=dict(\n        data_root='data/MOT17/',\n        ann_file='annotations/test_cocoformat.json',\n        data_prefix=dict(img_path='test')))\ntest_evaluator = dict(\n    type='MOTChallengeMetrics',\n    postprocess_tracklet_cfg=[\n        dict(type='InterpolateTracklets', min_num_frames=5, max_num_frames=20)\n    ],\n    format_only=True,\n    outfile_prefix='./mot_17_test_res')\n"
  },
  {
    "path": "configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot20train_test-mot20test.py",
    "content": "_base_ = [\n    './bytetrack_yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test.py'\n]\n\n# fp16 settings\noptim_wrapper = dict(type='AmpOptimWrapper', loss_scale='dynamic')\nval_cfg = dict(type='ValLoop', fp16=True)\ntest_cfg = dict(type='TestLoop', fp16=True)\n"
  },
  {
    "path": "configs/bytetrack/metafile.yml",
    "content": "Collections:\n  - Name: ByteTrack\n    Metadata:\n      Training Techniques:\n        - SGD with Momentum\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - YOLOX\n    Paper:\n      URL: https://arxiv.org/abs/2110.06864\n      Title: ByteTrack Multi-Object Tracking by Associating Every Detection Box\n    README: configs/bytetrack/README.md\n\nModels:\n  - Name: bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval\n    In Collection: ByteTrack\n    Config: configs/bytetrack/bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py\n    Metadata:\n      Training Data: CrowdHuman + MOT17-half-train\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT17-half-val\n        Metrics:\n          HOTA: 67.5\n          MOTA: 78.6\n          IDF1: 78.5\n    Weights: https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500-1985c9f0.pth\n\n  - Name: bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17test\n    In Collection: ByteTrack\n    Config: configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17test.py\n    Metadata:\n      Training Data: CrowdHuman + MOT17-half-train\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT17-test\n        Metrics:\n          MOTA: 78.1\n          IDF1: 74.8\n    Weights: https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500-1985c9f0.pth\n\n  - Name: bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot20train_test-mot20test\n    In Collection: ByteTrack\n    Config: configs/bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot20train_test-mot20test.py\n    Metadata:\n      Training Data: CrowdHuman + MOT20-train\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT20-test\n        Metrics:\n          MOTA: 77.0\n          IDF1: 75.4\n    Weights: https://download.openmmlab.com/mmtracking/mot/bytetrack/bytetrack_yolox_x/bytetrack_yolox_x_crowdhuman_mot20-private_20220506_101040-9ce38a60.pth\n"
  },
  {
    "path": "configs/bytetrack/yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    '../strongsort/yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py'  # noqa: E501\n]\n\n# fp16 settings\noptim_wrapper = dict(type='AmpOptimWrapper', loss_scale='dynamic')\n"
  },
  {
    "path": "configs/carafe/README.md",
    "content": "# CARAFE\n\n> [CARAFE: Content-Aware ReAssembly of FEatures](https://arxiv.org/abs/1905.02188)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nFeature upsampling is a key operation in a number of modern convolutional network architectures, e.g. feature pyramids. Its design is critical for dense prediction tasks such as object detection and semantic/instance segmentation. In this work, we propose Content-Aware ReAssembly of FEatures (CARAFE), a universal, lightweight and highly effective operator to fulfill this goal. CARAFE has several appealing properties: (1) Large field of view. Unlike previous works (e.g. bilinear interpolation) that only exploit sub-pixel neighborhood, CARAFE can aggregate contextual information within a large receptive field. (2) Content-aware handling. Instead of using a fixed kernel for all samples (e.g. deconvolution), CARAFE enables instance-specific content-aware handling, which generates adaptive kernels on-the-fly. (3) Lightweight and fast to compute. CARAFE introduces little computational overhead and can be readily integrated into modern network architectures. We conduct comprehensive evaluations on standard benchmarks in object detection, instance/semantic segmentation and inpainting. CARAFE shows consistent and substantial gains across all the tasks (1.2%, 1.3%, 1.8%, 1.1db respectively) with negligible computational overhead. It has great potential to serve as a strong building block for future research. It has great potential to serve as a strong building block for future research.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143872016-48225685-0e59-49cf-bd65-a50ee04ca8a2.png\"/>\n</div>\n\n## Results and Models\n\nThe results on COCO 2017 val is shown in the below table.\n\n|         Method         | Backbone |  Style  | Lr schd | Test Proposal Num | Inf time (fps) | Box AP | Mask AP |                      Config                       |                                                                                                                                                                         Download                                                                                                                                                                          |\n| :--------------------: | :------: | :-----: | :-----: | :---------------: | :------------: | :----: | :-----: | :-----------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Faster R-CNN w/ CARAFE | R-50-FPN | pytorch |   1x    |       1000        |      16.5      |  38.6  |  38.6   | [config](./faster-rcnn_r50_fpn-carafe_1x_coco.py) |     [model](https://download.openmmlab.com/mmdetection/v2.0/carafe/faster_rcnn_r50_fpn_carafe_1x_coco/faster_rcnn_r50_fpn_carafe_1x_coco_bbox_mAP-0.386_20200504_175733-385a75b7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/carafe/faster_rcnn_r50_fpn_carafe_1x_coco/faster_rcnn_r50_fpn_carafe_1x_coco_20200504_175733.log.json)     |\n|           -            |    -     |    -    |    -    |       2000        |                |        |         |                                                   |                                                                                                                                                                                                                                                                                                                                                           |\n|  Mask R-CNN w/ CARAFE  | R-50-FPN | pytorch |   1x    |       1000        |      14.0      |  39.3  |  35.8   |  [config](./mask-rcnn_r50_fpn-carafe_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/carafe/mask_rcnn_r50_fpn_carafe_1x_coco/mask_rcnn_r50_fpn_carafe_1x_coco_bbox_mAP-0.393__segm_mAP-0.358_20200503_135957-8687f195.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/carafe/mask_rcnn_r50_fpn_carafe_1x_coco/mask_rcnn_r50_fpn_carafe_1x_coco_20200503_135957.log.json) |\n|           -            |    -     |    -    |    -    |       2000        |                |        |         |                                                   |                                                                                                                                                                                                                                                                                                                                                           |\n\n## Implementation\n\nThe CUDA implementation of CARAFE can be find at https://github.com/myownskyW7/CARAFE.\n\n## Citation\n\nWe provide config files to reproduce the object detection & instance segmentation results in the ICCV 2019 Oral paper for [CARAFE: Content-Aware ReAssembly of FEatures](https://arxiv.org/abs/1905.02188).\n\n```latex\n@inproceedings{Wang_2019_ICCV,\n    title = {CARAFE: Content-Aware ReAssembly of FEatures},\n    author = {Wang, Jiaqi and Chen, Kai and Xu, Rui and Liu, Ziwei and Loy, Chen Change and Lin, Dahua},\n    booktitle = {The IEEE International Conference on Computer Vision (ICCV)},\n    month = {October},\n    year = {2019}\n}\n```\n"
  },
  {
    "path": "configs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(pad_size_divisor=64),\n    neck=dict(\n        type='FPN_CARAFE',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5,\n        start_level=0,\n        end_level=-1,\n        norm_cfg=None,\n        act_cfg=None,\n        order=('conv', 'norm', 'act'),\n        upsample_cfg=dict(\n            type='carafe',\n            up_kernel=5,\n            up_group=1,\n            encoder_kernel=3,\n            encoder_dilation=1,\n            compressed_channels=64)))\n"
  },
  {
    "path": "configs/carafe/mask-rcnn_r50_fpn-carafe_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(pad_size_divisor=64),\n    neck=dict(\n        type='FPN_CARAFE',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5,\n        start_level=0,\n        end_level=-1,\n        norm_cfg=None,\n        act_cfg=None,\n        order=('conv', 'norm', 'act'),\n        upsample_cfg=dict(\n            type='carafe',\n            up_kernel=5,\n            up_group=1,\n            encoder_kernel=3,\n            encoder_dilation=1,\n            compressed_channels=64)),\n    roi_head=dict(\n        mask_head=dict(\n            upsample_cfg=dict(\n                type='carafe',\n                scale_factor=2,\n                up_kernel=5,\n                up_group=1,\n                encoder_kernel=3,\n                encoder_dilation=1,\n                compressed_channels=64))))\n"
  },
  {
    "path": "configs/carafe/metafile.yml",
    "content": "Collections:\n  - Name: CARAFE\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RPN\n        - FPN_CARAFE\n        - ResNet\n        - RoIPool\n    Paper:\n      URL: https://arxiv.org/abs/1905.02188\n      Title: 'CARAFE: Content-Aware ReAssembly of FEatures'\n    README: configs/carafe/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.12.0/mmdet/models/necks/fpn_carafe.py#L11\n      Version: v2.12.0\n\nModels:\n  - Name: faster-rcnn_r50_fpn_carafe_1x_coco\n    In Collection: CARAFE\n    Config: configs/carafe/faster-rcnn_r50_fpn-carafe_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.26\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/carafe/faster_rcnn_r50_fpn_carafe_1x_coco/faster_rcnn_r50_fpn_carafe_1x_coco_bbox_mAP-0.386_20200504_175733-385a75b7.pth\n\n  - Name: mask-rcnn_r50_fpn_carafe_1x_coco\n    In Collection: CARAFE\n    Config: configs/carafe/mask-rcnn_r50_fpn-carafe_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.31\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 35.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/carafe/mask_rcnn_r50_fpn_carafe_1x_coco/mask_rcnn_r50_fpn_carafe_1x_coco_bbox_mAP-0.393__segm_mAP-0.358_20200503_135957-8687f195.pth\n"
  },
  {
    "path": "configs/cascade_rcnn/README.md",
    "content": "# Cascade R-CNN\n\n> [Cascade R-CNN: High Quality Object Detection and Instance Segmentation](https://arxiv.org/abs/1906.09756)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIn object detection, the intersection over union (IoU) threshold is frequently used to define positives/negatives. The threshold used to train a detector defines its quality. While the commonly used threshold of 0.5 leads to noisy (low-quality) detections, detection performance frequently degrades for larger thresholds. This paradox of high-quality detection has two causes: 1) overfitting, due to vanishing positive samples for large thresholds, and 2) inference-time quality mismatch between detector and test hypotheses. A multi-stage object detection architecture, the Cascade R-CNN, composed of a sequence of detectors trained with increasing IoU thresholds, is proposed to address these problems. The detectors are trained sequentially, using the output of a detector as training set for the next. This resampling progressively improves hypotheses quality, guaranteeing a positive training set of equivalent size for all detectors and minimizing overfitting. The same cascade is applied at inference, to eliminate quality mismatches between hypotheses and detectors. An implementation of the Cascade R-CNN without bells or whistles achieves state-of-the-art performance on the COCO dataset, and significantly improves high-quality detection on generic and specific object detection datasets, including VOC, KITTI, CityPerson, and WiderFace. Finally, the Cascade R-CNN is generalized to instance segmentation, with nontrivial improvements over the Mask R-CNN.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143872197-d99b90e4-4f05-4329-80a4-327ac862a051.png\"/>\n</div>\n\n## Results and Models\n\n### Cascade R-CNN\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                       Config                        |                                                                                                                                                                             Download                                                                                                                                                                              |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     |  caffe  |   1x    |   4.2    |                |  40.4  |  [config](./cascade-rcnn_r50-caffe_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_caffe_fpn_1x_coco/cascade_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.404_20200504_174853-b857be87.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_caffe_fpn_1x_coco/cascade_rcnn_r50_caffe_fpn_1x_coco_20200504_174853.log.json)   |\n|    R-50-FPN     | pytorch |   1x    |   4.4    |      16.1      |  40.3  |     [config](./cascade-rcnn_r50_fpn_1x_coco.py)     |                          [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_fpn_1x_coco/cascade_rcnn_r50_fpn_1x_coco_20200316-3dc56deb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_fpn_1x_coco/cascade_rcnn_r50_fpn_1x_coco_20200316_214748.log.json)                          |\n|    R-50-FPN     | pytorch |   20e   |    -     |       -        |  41.0  |    [config](./cascade-rcnn_r50_fpn_20e_coco.py)     |             [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_fpn_20e_coco/cascade_rcnn_r50_fpn_20e_coco_bbox_mAP-0.41_20200504_175131-e9872a90.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_fpn_20e_coco/cascade_rcnn_r50_fpn_20e_coco_20200504_175131.log.json)              |\n|    R-101-FPN    |  caffe  |   1x    |   6.2    |                |  42.3  | [config](./cascade-rcnn_r101-caffe_fpn_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_caffe_fpn_1x_coco/cascade_rcnn_r101_caffe_fpn_1x_coco_bbox_mAP-0.423_20200504_175649-cab8dbd5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_caffe_fpn_1x_coco/cascade_rcnn_r101_caffe_fpn_1x_coco_20200504_175649.log.json) |\n|    R-101-FPN    | pytorch |   1x    |   6.4    |      13.5      |  42.0  |    [config](./cascade-rcnn_r101_fpn_1x_coco.py)     |                        [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_fpn_1x_coco/cascade_rcnn_r101_fpn_1x_coco_20200317-0b6a2fbf.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_fpn_1x_coco/cascade_rcnn_r101_fpn_1x_coco_20200317_101744.log.json)                        |\n|    R-101-FPN    | pytorch |   20e   |    -     |       -        |  42.5  |    [config](./cascade-rcnn_r101_fpn_20e_coco.py)    |           [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_fpn_20e_coco/cascade_rcnn_r101_fpn_20e_coco_bbox_mAP-0.425_20200504_231812-5057dcc5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_fpn_20e_coco/cascade_rcnn_r101_fpn_20e_coco_20200504_231812.log.json)           |\n| X-101-32x4d-FPN | pytorch |   1x    |   7.6    |      10.9      |  43.7  | [config](./cascade-rcnn_x101-32x4d_fpn_1x_coco.py)  |            [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_32x4d_fpn_1x_coco/cascade_rcnn_x101_32x4d_fpn_1x_coco_20200316-95c2deb6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_32x4d_fpn_1x_coco/cascade_rcnn_x101_32x4d_fpn_1x_coco_20200316_055608.log.json)            |\n| X-101-32x4d-FPN | pytorch |   20e   |   7.6    |                |  43.7  | [config](./cascade-rcnn_x101-32x4d_fpn_20e_coco.py) |      [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_32x4d_fpn_20e_coco/cascade_rcnn_x101_32x4d_fpn_20e_coco_20200906_134608-9ae0a720.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_32x4d_fpn_20e_coco/cascade_rcnn_x101_32x4d_fpn_20e_coco_20200906_134608.log.json)       |\n| X-101-64x4d-FPN | pytorch |   1x    |   10.7   |                |  44.7  | [config](./cascade-rcnn_x101-64x4d_fpn_1x_coco.py)  |        [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_64x4d_fpn_1x_coco/cascade_rcnn_x101_64x4d_fpn_1x_coco_20200515_075702-43ce6a30.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_64x4d_fpn_1x_coco/cascade_rcnn_x101_64x4d_fpn_1x_coco_20200515_075702.log.json)         |\n| X-101-64x4d-FPN | pytorch |   20e   |   10.7   |                |  44.5  | [config](./cascade-rcnn_x101_64x4d_fpn_20e_coco.py) |      [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_64x4d_fpn_20e_coco/cascade_rcnn_x101_64x4d_fpn_20e_coco_20200509_224357-051557b1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_64x4d_fpn_20e_coco/cascade_rcnn_x101_64x4d_fpn_20e_coco_20200509_224357.log.json)       |\n\n### Cascade Mask R-CNN\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                          Config                          |                                                                                                                                                                                               Download                                                                                                                                                                                                |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     |  caffe  |   1x    |   5.9    |                |  41.2  |  36.0   |  [config](./cascade-mask-rcnn_r50-caffe_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_caffe_fpn_1x_coco/cascade_mask_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.412__segm_mAP-0.36_20200504_174659-5004b251.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_caffe_fpn_1x_coco/cascade_mask_rcnn_r50_caffe_fpn_1x_coco_20200504_174659.log.json)    |\n|    R-50-FPN     | pytorch |   1x    |   6.0    |      11.2      |  41.2  |  35.9   |     [config](./cascade-mask-rcnn_r50_fpn_1x_coco.py)     |                                  [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco/cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco/cascade_mask_rcnn_r50_fpn_1x_coco_20200203_170449.log.json)                                  |\n|    R-50-FPN     | pytorch |   20e   |    -     |       -        |  41.9  |  36.5   |    [config](./cascade-mask-rcnn_r50_fpn_20e_coco.py)     |             [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_20e_coco/cascade_mask_rcnn_r50_fpn_20e_coco_bbox_mAP-0.419__segm_mAP-0.365_20200504_174711-4af8e66e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_20e_coco/cascade_mask_rcnn_r50_fpn_20e_coco_20200504_174711.log.json)             |\n|    R-101-FPN    |  caffe  |   1x    |   7.8    |                |  43.2  |  37.6   | [config](./cascade-mask-rcnn_r101-caffe_fpn_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_caffe_fpn_1x_coco/cascade_mask_rcnn_r101_caffe_fpn_1x_coco_bbox_mAP-0.432__segm_mAP-0.376_20200504_174813-5c1e9599.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_caffe_fpn_1x_coco/cascade_mask_rcnn_r101_caffe_fpn_1x_coco_20200504_174813.log.json) |\n|    R-101-FPN    | pytorch |   1x    |   7.9    |      9.8       |  42.9  |  37.3   |    [config](./cascade-mask-rcnn_r101_fpn_1x_coco.py)     |                                [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_1x_coco/cascade_mask_rcnn_r101_fpn_1x_coco_20200203-befdf6ee.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_1x_coco/cascade_mask_rcnn_r101_fpn_1x_coco_20200203_092521.log.json)                                |\n|    R-101-FPN    | pytorch |   20e   |    -     |       -        |  43.4  |  37.8   |    [config](./cascade-mask-rcnn_r101_fpn_20e_coco.py)    |           [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_20e_coco/cascade_mask_rcnn_r101_fpn_20e_coco_bbox_mAP-0.434__segm_mAP-0.378_20200504_174836-005947da.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_20e_coco/cascade_mask_rcnn_r101_fpn_20e_coco_20200504_174836.log.json)           |\n| X-101-32x4d-FPN | pytorch |   1x    |   9.2    |      8.6       |  44.3  |  38.3   | [config](./cascade-mask-rcnn_x101-32x4d_fpn_1x_coco.py)  |                    [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_1x_coco_20200201-0f411b1f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_1x_coco_20200201_052416.log.json)                    |\n| X-101-32x4d-FPN | pytorch |   20e   |   9.2    |       -        |  45.0  |  39.0   | [config](./cascade-mask-rcnn_x101-32x4d_fpn_20e_coco.py) |              [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_20e_coco/cascade_mask_rcnn_x101_32x4d_fpn_20e_coco_20200528_083917-ed1f4751.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_20e_coco/cascade_mask_rcnn_x101_32x4d_fpn_20e_coco_20200528_083917.log.json)               |\n| X-101-64x4d-FPN | pytorch |   1x    |   12.2   |      6.7       |  45.3  |  39.2   | [config](./cascade-mask-rcnn_x101-64x4d_fpn_1x_coco.py)  |                    [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_1x_coco/cascade_mask_rcnn_x101_64x4d_fpn_1x_coco_20200203-9a2db89d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_1x_coco/cascade_mask_rcnn_x101_64x4d_fpn_1x_coco_20200203_044059.log.json)                    |\n| X-101-64x4d-FPN | pytorch |   20e   |   12.2   |                |  45.6  |  39.5   | [config](./cascade-mask-rcnn_x101-64x4d_fpn_20e_coco.py) |              [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_20e_coco/cascade_mask_rcnn_x101_64x4d_fpn_20e_coco_20200512_161033-bdb5126a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_20e_coco/cascade_mask_rcnn_x101_64x4d_fpn_20e_coco_20200512_161033.log.json)               |\n\n**Notes:**\n\n- The `20e` schedule in Cascade (Mask) R-CNN indicates decreasing the lr at 16 and 19 epochs, with a total of 20 epochs.\n\n## Pre-trained Models\n\nWe also train some models with longer schedules and multi-scale training for Cascade Mask R-CNN. The users could finetune them for downstream tasks.\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                           Config                           |                                                                                                                                                                                                Download                                                                                                                                                                                                |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :--------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     |  caffe  |   3x    |   5.7    |                |  44.0  |  38.1   | [config](./cascade-mask-rcnn_r50-caffe_fpn_ms-3x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_caffe_fpn_mstrain_3x_coco/cascade_mask_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210707_002651-6e29b3a6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_caffe_fpn_mstrain_3x_coco/cascade_mask_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210707_002651.log.json)   |\n|    R-50-FPN     | pytorch |   3x    |   5.9    |                |  44.3  |  38.5   |    [config](./cascade-mask-rcnn_r50_fpn_ms-3x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_mstrain_3x_coco/cascade_mask_rcnn_r50_fpn_mstrain_3x_coco_20210628_164719-5bdc3824.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_mstrain_3x_coco/cascade_mask_rcnn_r50_fpn_mstrain_3x_coco_20210628_164719.log.json)               |\n|    R-101-FPN    |  caffe  |   3x    |   7.7    |                |  45.4  |  39.5   | [config](./cascade-mask-rcnn_r101-caffe_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_caffe_fpn_mstrain_3x_coco/cascade_mask_rcnn_r101_caffe_fpn_mstrain_3x_coco_20210707_002620-a5bd2389.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_caffe_fpn_mstrain_3x_coco/cascade_mask_rcnn_r101_caffe_fpn_mstrain_3x_coco_20210707_002620.log.json) |\n|    R-101-FPN    | pytorch |   3x    |   7.8    |                |  45.5  |  39.6   |    [config](./cascade-mask-rcnn_r101_fpn_ms-3x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_mstrain_3x_coco/cascade_mask_rcnn_r101_fpn_mstrain_3x_coco_20210628_165236-51a2d363.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_mstrain_3x_coco/cascade_mask_rcnn_r101_fpn_mstrain_3x_coco_20210628_165236.log.json)             |\n| X-101-32x4d-FPN | pytorch |   3x    |   9.0    |                |  46.3  |  40.1   | [config](./cascade-mask-rcnn_x101-32x4d_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_32x4d_fpn_mstrain_3x_coco_20210706_225234-40773067.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_32x4d_fpn_mstrain_3x_coco_20210706_225234.log.json) |\n| X-101-32x8d-FPN | pytorch |   3x    |   12.1   |                |  46.1  |  39.9   | [config](./cascade-mask-rcnn_x101-32x8d_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x8d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_32x8d_fpn_mstrain_3x_coco_20210719_180640-9ff7e76f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x8d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_32x8d_fpn_mstrain_3x_coco_20210719_180640.log.json) |\n| X-101-64x4d-FPN | pytorch |   3x    |   12.0   |                |  46.6  |  40.3   | [config](./cascade-mask-rcnn_x101-64x4d_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_64x4d_fpn_mstrain_3x_coco_20210719_210311-d3e64ba0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_64x4d_fpn_mstrain_3x_coco_20210719_210311.log.json) |\n\n## Citation\n\n```latex\n@article{Cai_2019,\n   title={Cascade R-CNN: High Quality Object Detection and Instance Segmentation},\n   ISSN={1939-3539},\n   url={http://dx.doi.org/10.1109/tpami.2019.2956516},\n   DOI={10.1109/tpami.2019.2956516},\n   journal={IEEE Transactions on Pattern Analysis and Machine Intelligence},\n   publisher={Institute of Electrical and Electronics Engineers (IEEE)},\n   author={Cai, Zhaowei and Vasconcelos, Nuno},\n   year={2019},\n   pages={1–1}\n}\n```\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r101-caffe_fpn_ms-3x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50-caffe_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r101_fpn_20e_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r101_fpn_ms-3x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = ['./cascade-mask-rcnn_r50_fpn_1x_coco.py']\n\nmodel = dict(\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r50-caffe_fpn_ms-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms_3x_coco-instance.py',\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_20e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_20e.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_ms-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms_3x_coco-instance.py',\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py'\n]\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_20e_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_ms-3x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_x101-32x8d_fpn_ms-3x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_ms-3x_coco.py'\n\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_x101-64x4d_fpn_20e_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-mask-rcnn_x101-64x4d_fpn_ms-3x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './cascade-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r101_fpn_20e_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r101_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r18_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r50_fpn_20e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_20e.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../common/lsj-200e_coco-detection.py'\n]\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\n# disable allowed_border to avoid potential errors.\nmodel = dict(\n    data_preprocessor=dict(batch_augments=batch_augments),\n    train_cfg=dict(rpn=dict(allowed_border=-1)))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.02 * 4, momentum=0.9, weight_decay=0.00004))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_x101-32x4d_fpn_20e_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    type='CascadeRCNN',\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/cascade-rcnn_x101_64x4d_fpn_20e_coco.py",
    "content": "_base_ = './cascade-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    type='CascadeRCNN',\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/cascade_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Cascade R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Cascade R-CNN\n        - FPN\n        - RPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: http://dx.doi.org/10.1109/tpami.2019.2956516\n      Title: 'Cascade R-CNN: Delving into High Quality Object Detection'\n    README: configs/cascade_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/cascade_rcnn.py#L6\n      Version: v2.0.0\n  - Name: Cascade Mask R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Cascade R-CNN\n        - FPN\n        - RPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: http://dx.doi.org/10.1109/tpami.2019.2956516\n      Title: 'Cascade R-CNN: Delving into High Quality Object Detection'\n    README: configs/cascade_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/cascade_rcnn.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: cascade-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.2\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_caffe_fpn_1x_coco/cascade_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.404_20200504_174853-b857be87.pth\n\n  - Name: cascade-rcnn_r50_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      inference time (ms/im):\n        - value: 62.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_fpn_1x_coco/cascade_rcnn_r50_fpn_1x_coco_20200316-3dc56deb.pth\n\n  - Name: cascade-rcnn_r50_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_r50_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      inference time (ms/im):\n        - value: 62.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r50_fpn_20e_coco/cascade_rcnn_r50_fpn_20e_coco_bbox_mAP-0.41_20200504_175131-e9872a90.pth\n\n  - Name: cascade-rcnn_r101-caffe_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.2\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_caffe_fpn_1x_coco/cascade_rcnn_r101_caffe_fpn_1x_coco_bbox_mAP-0.423_20200504_175649-cab8dbd5.pth\n\n  - Name: cascade-rcnn_r101_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      inference time (ms/im):\n        - value: 74.07\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_fpn_1x_coco/cascade_rcnn_r101_fpn_1x_coco_20200317-0b6a2fbf.pth\n\n  - Name: cascade-rcnn_r101_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_r101_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      inference time (ms/im):\n        - value: 74.07\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_r101_fpn_20e_coco/cascade_rcnn_r101_fpn_20e_coco_bbox_mAP-0.425_20200504_231812-5057dcc5.pth\n\n  - Name: cascade-rcnn_x101-32x4d_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      inference time (ms/im):\n        - value: 91.74\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_32x4d_fpn_1x_coco/cascade_rcnn_x101_32x4d_fpn_1x_coco_20200316-95c2deb6.pth\n\n  - Name: cascade-rcnn_x101-32x4d_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_x101-32x4d_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_32x4d_fpn_20e_coco/cascade_rcnn_x101_32x4d_fpn_20e_coco_20200906_134608-9ae0a720.pth\n\n  - Name: cascade-rcnn_x101-64x4d_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.7\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_64x4d_fpn_1x_coco/cascade_rcnn_x101_64x4d_fpn_1x_coco_20200515_075702-43ce6a30.pth\n\n  - Name: cascade-rcnn_x101_64x4d_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-rcnn_x101_64x4d_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 10.7\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_rcnn_x101_64x4d_fpn_20e_coco/cascade_rcnn_x101_64x4d_fpn_20e_coco_20200509_224357-051557b1.pth\n\n  - Name: cascade-mask-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.9\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_caffe_fpn_1x_coco/cascade_mask_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.412__segm_mAP-0.36_20200504_174659-5004b251.pth\n\n  - Name: cascade-mask-rcnn_r50_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 89.29\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  35.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco/cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth\n\n  - Name: cascade-mask-rcnn_r50_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 89.29\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_20e_coco/cascade_mask_rcnn_r50_fpn_20e_coco_bbox_mAP-0.419__segm_mAP-0.365_20200504_174711-4af8e66e.pth\n\n  - Name: cascade-mask-rcnn_r101-caffe_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_caffe_fpn_1x_coco/cascade_mask_rcnn_r101_caffe_fpn_1x_coco_bbox_mAP-0.432__segm_mAP-0.376_20200504_174813-5c1e9599.pth\n\n  - Name: cascade-mask-rcnn_r101_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.9\n      inference time (ms/im):\n        - value: 102.04\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_1x_coco/cascade_mask_rcnn_r101_fpn_1x_coco_20200203-befdf6ee.pth\n\n  - Name: cascade-mask-rcnn_r101_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r101_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 7.9\n      inference time (ms/im):\n        - value: 102.04\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_20e_coco/cascade_mask_rcnn_r101_fpn_20e_coco_bbox_mAP-0.434__segm_mAP-0.378_20200504_174836-005947da.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.2\n      inference time (ms/im):\n        - value: 116.28\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_1x_coco_20200201-0f411b1f.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 9.2\n      inference time (ms/im):\n        - value: 116.28\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_20e_coco/cascade_mask_rcnn_x101_32x4d_fpn_20e_coco_20200528_083917-ed1f4751.pth\n\n  - Name: cascade-mask-rcnn_x101-64x4d_fpn_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 12.2\n      inference time (ms/im):\n        - value: 149.25\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  39.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_1x_coco/cascade_mask_rcnn_x101_64x4d_fpn_1x_coco_20200203-9a2db89d.pth\n\n  - Name: cascade-mask-rcnn_x101-64x4d_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_x101-64x4d_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 12.2\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_20e_coco/cascade_mask_rcnn_x101_64x4d_fpn_20e_coco_20200512_161033-bdb5126a.pth\n\n  - Name: cascade-mask-rcnn_r50-caffe_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r50-caffe_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.7\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_caffe_fpn_mstrain_3x_coco/cascade_mask_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210707_002651-6e29b3a6.pth\n\n  - Name: cascade-mask-rcnn_r50_fpn_mstrain_3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r50_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.9\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_mstrain_3x_coco/cascade_mask_rcnn_r50_fpn_mstrain_3x_coco_20210628_164719-5bdc3824.pth\n\n  - Name: cascade-mask-rcnn_r101-caffe_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r101-caffe_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.7\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_caffe_fpn_mstrain_3x_coco/cascade_mask_rcnn_r101_caffe_fpn_mstrain_3x_coco_20210707_002620-a5bd2389.pth\n\n  - Name: cascade-mask-rcnn_r101_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_r101_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r101_fpn_mstrain_3x_coco/cascade_mask_rcnn_r101_fpn_mstrain_3x_coco_20210628_165236-51a2d363.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 9.0\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x4d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_32x4d_fpn_mstrain_3x_coco_20210706_225234-40773067.pth\n\n  - Name: cascade-mask-rcnn_x101-32x8d_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_x101-32x8d_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 12.1\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_32x8d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_32x8d_fpn_mstrain_3x_coco_20210719_180640-9ff7e76f.pth\n\n  - Name: cascade-mask-rcnn_x101-64x4d_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/cascade_rcnn/cascade-mask-rcnn_x101-64x4d_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 12.0\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_x101_64x4d_fpn_mstrain_3x_coco/cascade_mask_rcnn_x101_64x4d_fpn_mstrain_3x_coco_20210719_210311-d3e64ba0.pth\n"
  },
  {
    "path": "configs/cascade_rpn/README.md",
    "content": "# Cascade RPN\n\n> [Cascade RPN: Delving into High-Quality Region Proposal Network with Adaptive Convolution](https://arxiv.org/abs/1909.06720)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThis paper considers an architecture referred to as Cascade Region Proposal Network (Cascade RPN) for improving the region-proposal quality and detection performance by systematically addressing the limitation of the conventional RPN that heuristically defines the anchors and aligns the features to the anchors. First, instead of using multiple anchors with predefined scales and aspect ratios, Cascade RPN relies on a single anchor per location and performs multi-stage refinement. Each stage is progressively more stringent in defining positive samples by starting out with an anchor-free metric followed by anchor-based metrics in the ensuing stages. Second, to attain alignment between the features and the anchors throughout the stages, adaptive convolution is proposed that takes the anchors in addition to the image features as its input and learns the sampled features guided by the anchors. A simple implementation of a two-stage Cascade RPN achieves AR 13.4 points higher than that of the conventional RPN, surpassing any existing region proposal methods. When adopting to Fast R-CNN and Faster R-CNN, Cascade RPN can improve the detection mAP by 3.1 and 3.5 points, respectively.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143872368-1580193a-d19c-4723-a579-c7ed2d5da4d1.png\"/>\n</div>\n\n## Results and Models\n\n### Region proposal performance\n\n| Method | Backbone | Style | Mem (GB) | Train time (s/iter) | Inf time (fps) | AR 1000 |                      Config                      |                                                                    Download                                                                    |\n| :----: | :------: | :---: | :------: | :-----------------: | :------------: | :-----: | :----------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------: |\n|  CRPN  | R-50-FPN | caffe |    -     |          -          |       -        |  72.0   | [config](./cascade-rpn_r50-caffe_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rpn/crpn_r50_caffe_fpn_1x_coco/cascade_rpn_r50_caffe_fpn_1x_coco-7aa93cef.pth) |\n\n### Detection performance\n\n|    Method    |  Proposal   | Backbone | Style | Schedule | Mem (GB) | Train time (s/iter) | Inf time (fps) | box AP |                            Config                            |                                                                            Download                                                                             |\n| :----------: | :---------: | :------: | :---: | :------: | :------: | :-----------------: | :------------: | :----: | :----------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  Fast R-CNN  | Cascade RPN | R-50-FPN | caffe |    1x    |    -     |          -          |       -        |  39.9  |  [config](./cascade-rpn_fast-rcnn_r50-caffe_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rpn/crpn_fast_rcnn_r50_caffe_fpn_1x_coco/crpn_fast_rcnn_r50_caffe_fpn_1x_coco-cb486e66.pth)   |\n| Faster R-CNN | Cascade RPN | R-50-FPN | caffe |    1x    |    -     |          -          |       -        |  40.4  | [config](./cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cascade_rpn/crpn_faster_rcnn_r50_caffe_fpn_1x_coco/crpn_faster_rcnn_r50_caffe_fpn_1x_coco-c8283cca.pth) |\n\n## Citation\n\nWe provide the code for reproducing experiment results of [Cascade RPN](https://arxiv.org/abs/1909.06720).\n\n```latex\n@inproceedings{vu2019cascade,\n  title={Cascade RPN: Delving into High-Quality Region Proposal Network with Adaptive Convolution},\n  author={Vu, Thang and Jang, Hyunjun and Pham, Trung X and Yoo, Chang D},\n  booktitle={Conference on Neural Information Processing Systems (NeurIPS)},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/cascade_rpn/cascade-rpn_fast-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../fast_rcnn/fast-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            bbox_coder=dict(target_stds=[0.04, 0.04, 0.08, 0.08]),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.5),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rcnn=dict(\n            assigner=dict(\n                pos_iou_thr=0.65, neg_iou_thr=0.65, min_pos_iou=0.65),\n            sampler=dict(num=256))),\n    test_cfg=dict(rcnn=dict(score_thr=1e-3)))\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# train_dataloader = dict(dataset=dict(proposal_file='proposals/crpn_r50_caffe_fpn_1x_train2017.pkl'))  # noqa\n_base_.train_dataloader.dataset.proposal_file = 'proposals/crpn_r50_caffe_fpn_1x_train2017.pkl'  # noqa\n\n# val_dataloader = dict(dataset=dict(proposal_file='proposals/crpn_r50_caffe_fpn_1x_val2017.pkl'))  # noqa\n# test_dataloader = val_dataloader\n_base_.val_dataloader.dataset.proposal_file = 'proposals/crpn_r50_caffe_fpn_1x_val2017.pkl'  # noqa\ntest_dataloader = _base_.val_dataloader\n\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50-caffe_fpn_1x_coco.py'\nrpn_weight = 0.7\nmodel = dict(\n    rpn_head=dict(\n        _delete_=True,\n        type='CascadeRPNHead',\n        num_stages=2,\n        stages=[\n            dict(\n                type='StageCascadeRPNHead',\n                in_channels=256,\n                feat_channels=256,\n                anchor_generator=dict(\n                    type='AnchorGenerator',\n                    scales=[8],\n                    ratios=[1.0],\n                    strides=[4, 8, 16, 32, 64]),\n                adapt_cfg=dict(type='dilation', dilation=3),\n                bridged_feature=True,\n                with_cls=False,\n                reg_decoded_bbox=True,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=(.0, .0, .0, .0),\n                    target_stds=(0.1, 0.1, 0.5, 0.5)),\n                loss_bbox=dict(\n                    type='IoULoss', linear=True,\n                    loss_weight=10.0 * rpn_weight)),\n            dict(\n                type='StageCascadeRPNHead',\n                in_channels=256,\n                feat_channels=256,\n                adapt_cfg=dict(type='offset'),\n                bridged_feature=False,\n                with_cls=True,\n                reg_decoded_bbox=True,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=(.0, .0, .0, .0),\n                    target_stds=(0.05, 0.05, 0.1, 0.1)),\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=True,\n                    loss_weight=1.0 * rpn_weight),\n                loss_bbox=dict(\n                    type='IoULoss', linear=True,\n                    loss_weight=10.0 * rpn_weight))\n        ]),\n    roi_head=dict(\n        bbox_head=dict(\n            bbox_coder=dict(target_stds=[0.04, 0.04, 0.08, 0.08]),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.5),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=[\n            dict(\n                assigner=dict(\n                    type='RegionAssigner', center_ratio=0.2, ignore_ratio=0.5),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.3,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=256,\n                    pos_fraction=0.5,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False)\n        ],\n        rpn_proposal=dict(max_per_img=300, nms=dict(iou_threshold=0.8)),\n        rcnn=dict(\n            assigner=dict(\n                pos_iou_thr=0.65, neg_iou_thr=0.65, min_pos_iou=0.65),\n            sampler=dict(type='RandomSampler', num=256))),\n    test_cfg=dict(\n        rpn=dict(max_per_img=300, nms=dict(iou_threshold=0.8)),\n        rcnn=dict(score_thr=1e-3)))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/cascade_rpn/cascade-rpn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../rpn/rpn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    rpn_head=dict(\n        _delete_=True,\n        type='CascadeRPNHead',\n        num_stages=2,\n        stages=[\n            dict(\n                type='StageCascadeRPNHead',\n                in_channels=256,\n                feat_channels=256,\n                anchor_generator=dict(\n                    type='AnchorGenerator',\n                    scales=[8],\n                    ratios=[1.0],\n                    strides=[4, 8, 16, 32, 64]),\n                adapt_cfg=dict(type='dilation', dilation=3),\n                bridged_feature=True,\n                sampling=False,\n                with_cls=False,\n                reg_decoded_bbox=True,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=(.0, .0, .0, .0),\n                    target_stds=(0.1, 0.1, 0.5, 0.5)),\n                loss_bbox=dict(type='IoULoss', linear=True, loss_weight=10.0)),\n            dict(\n                type='StageCascadeRPNHead',\n                in_channels=256,\n                feat_channels=256,\n                adapt_cfg=dict(type='offset'),\n                bridged_feature=False,\n                sampling=True,\n                with_cls=True,\n                reg_decoded_bbox=True,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=(.0, .0, .0, .0),\n                    target_stds=(0.05, 0.05, 0.1, 0.1)),\n                loss_cls=dict(\n                    type='CrossEntropyLoss', use_sigmoid=True,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='IoULoss', linear=True, loss_weight=10.0))\n        ]),\n    train_cfg=dict(rpn=[\n        dict(\n            assigner=dict(\n                type='RegionAssigner', center_ratio=0.2, ignore_ratio=0.5),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.7,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1,\n                iou_calculator=dict(type='BboxOverlaps2D')),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False)\n    ]),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.8),\n            min_bbox_size=0)))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/cascade_rpn/metafile.yml",
    "content": "Collections:\n  - Name: Cascade RPN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Cascade RPN\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1909.06720\n      Title: 'Cascade RPN: Delving into High-Quality Region Proposal Network with Adaptive Convolution'\n    README: configs/cascade_rpn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.8.0/mmdet/models/dense_heads/cascade_rpn_head.py#L538\n      Version: v2.8.0\n\nModels:\n  - Name: cascade-rpn_fast-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Cascade RPN\n    Config: configs/cascade_rpn/cascade-rpn_fast-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rpn/crpn_fast_rcnn_r50_caffe_fpn_1x_coco/crpn_fast_rcnn_r50_caffe_fpn_1x_coco-cb486e66.pth\n\n  - Name: cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Cascade RPN\n    Config: configs/cascade_rpn/cascade-rpn_faster-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cascade_rpn/crpn_faster_rcnn_r50_caffe_fpn_1x_coco/crpn_faster_rcnn_r50_caffe_fpn_1x_coco-c8283cca.pth\n"
  },
  {
    "path": "configs/centernet/README.md",
    "content": "# CenterNet\n\n> [Objects as Points](https://arxiv.org/abs/1904.07850)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nDetection identifies objects as axis-aligned boxes in an image. Most successful object detectors enumerate a nearly exhaustive list of potential object locations and classify each. This is wasteful, inefficient, and requires additional post-processing. In this paper, we take a different approach. We model an object as a single point --- the center point of its bounding box. Our detector uses keypoint estimation to find center points and regresses to all other object properties, such as size, 3D location, orientation, and even pose. Our center point based approach, CenterNet, is end-to-end differentiable, simpler, faster, and more accurate than corresponding bounding box based detectors. CenterNet achieves the best speed-accuracy trade-off on the MS COCO dataset, with 28.1% AP at 142 FPS, 37.4% AP at 52 FPS, and 45.1% AP with multi-scale testing at 1.4 FPS. We use the same approach to estimate 3D bounding box in the KITTI benchmark and human pose on the COCO keypoint dataset. Our method performs competitively with sophisticated multi-stage methods and runs in real-time.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143873810-85ffa6e7-915b-46a4-9b8f-709e5d7700bb.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone  | DCN | Mem (GB) | Box AP | Flip box AP |                           Config                           |                                                                                                                                                                 Download                                                                                                                                                                 |\n| :-------: | :-: | :------: | :----: | :---------: | :--------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| ResNet-18 |  N  |   3.45   |  25.9  |    27.3     |    [config](./centernet_r18_8xb16-crop512-140e_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_140e_coco/centernet_resnet18_140e_coco_20210705_093630-bb5b3bf7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_140e_coco/centernet_resnet18_140e_coco_20210705_093630.log.json)             |\n| ResNet-18 |  Y  |   3.47   |  29.5  |    30.9     | [config](./centernet_r18-dcnv2_8xb16-crop512-140e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_dcnv2_140e_coco/centernet_resnet18_dcnv2_140e_coco_20210702_155131-c8cd631f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_dcnv2_140e_coco/centernet_resnet18_dcnv2_140e_coco_20210702_155131.log.json) |\n\nNote:\n\n- Flip box AP setting is single-scale and `flip=True`.\n- Due to complex data enhancement, we find that the performance is unstable and may fluctuate by about 0.4 mAP. mAP 29.4 ~ 29.8 is acceptable in ResNet-18-DCNv2.\n- Compared to the source code, we refer to [CenterNet-Better](https://github.com/FateScript/CenterNet-better), and make the following changes\n  - fix wrong image mean and variance in image normalization to be compatible with the pre-trained backbone.\n  - Use SGD rather than ADAM optimizer and add warmup and grad clip.\n  - Use DistributedDataParallel as other models in MMDetection rather than using DataParallel.\n\n## CenterNet Update\n\n| Backbone  | Style | Lr schd | MS train | Mem (GB) | Box AP |                          Config                          |                                                                                                                                                                               Download                                                                                                                                                                               |\n| :-------: | :---: | :-----: | :------: | :------: | :----: | :------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| ResNet-50 | caffe |   1x    |   True   |   3.3    |  40.2  | [config](./centernet-update_r50-caffe_fpn_ms-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco/centernet-update_r50-caffe_fpn_ms-1x_coco_20230512_203845-8306baf2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco/centernet-update_r50-caffe_fpn_ms-1x_coco_20230512_203845.log.json) |\n\nCenterNet Update from the paper of [Probabilistic two-stage detection](https://arxiv.org/abs/2103.07461). The author has updated CenterNet to greatly improve performance and convergence speed.\nThe [Details](https://github.com/xingyizhou/CenterNet2/blob/master/docs/MODEL_ZOO.md) are as follows:\n\n- Using top-left-right-bottom box encoding and GIoU Loss\n- Adding regression loss to the center 3x3 region\n- Adding more positive pixels for the heatmap loss whose regression loss is small and is within the center3x3 region\n- Using RetinaNet-style optimizer (SGD), learning rate rule (0.01 for each batch size 16), and schedule (12 epochs)\n- Added FPN neck layers, and assigns objects to FPN levels based on a fixed size range.\n- Using standard NMS instead of max pooling\n\nNote: We found that the performance of the r50 model fluctuates greatly and sometimes it does not converge. If the model does not converge, you can try running it again or reduce the learning rate.\n\n## Citation\n\n```latex\n@article{zhou2019objects,\n  title={Objects as Points},\n  author={Zhou, Xingyi and Wang, Dequan and Kr{\\\"a}henb{\\\"u}hl, Philipp},\n  booktitle={arXiv preprint arXiv:1904.07850},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/centernet/centernet-update_r101_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './centernet-update_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/centernet/centernet-update_r18_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './centernet-update_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    type='CenterNet',\n    # use caffe img_norm\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5,\n        # There is a chance to get 40.3 after switching init_cfg,\n        # otherwise it is about 39.9~40.1\n        init_cfg=dict(type='Caffe2Xavier', layer='Conv2d'),\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='CenterNetUpdateHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        hm_min_radius=4,\n        hm_min_overlap=0.8,\n        more_pos_thresh=0.2,\n        more_pos_topk=9,\n        soft_weight_on_reg=False,\n        loss_cls=dict(\n            type='GaussianFocalLoss',\n            pos_weight=0.25,\n            neg_weight=0.75,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n    ),\n    train_cfg=None,\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# single-scale training is about 39.3\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.00025,\n        by_epoch=False,\n        begin=0,\n        end=4000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.01),\n    # Experiments show that there is no need to turn on clip_grad.\n    paramwise_cfg=dict(norm_decay_mult=0.))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/centernet/centernet-update_r50_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = '../common/lsj-200e_coco-detection.py'\n\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\nmodel = dict(\n    type='CenterNet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5,\n        init_cfg=dict(type='Caffe2Xavier', layer='Conv2d'),\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='CenterNetUpdateHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='GaussianFocalLoss',\n            pos_weight=0.25,\n            neg_weight=0.75,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n    ),\n    train_cfg=None,\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.01 * 4, momentum=0.9, weight_decay=0.00004),\n    paramwise_cfg=dict(norm_decay_mult=0.))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.00025,\n        by_epoch=False,\n        begin=0,\n        end=4000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=25,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py',\n    './centernet_tta.py'\n]\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# model settings\nmodel = dict(\n    type='CenterNet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True),\n    backbone=dict(\n        type='ResNet',\n        depth=18,\n        norm_eval=False,\n        norm_cfg=dict(type='BN'),\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(\n        type='CTResNetNeck',\n        in_channels=512,\n        num_deconv_filters=(256, 128, 64),\n        num_deconv_kernels=(4, 4, 4),\n        use_dcn=True),\n    bbox_head=dict(\n        type='CenterNetHead',\n        num_classes=80,\n        in_channels=64,\n        feat_channels=64,\n        loss_center_heatmap=dict(type='GaussianFocalLoss', loss_weight=1.0),\n        loss_wh=dict(type='L1Loss', loss_weight=0.1),\n        loss_offset=dict(type='L1Loss', loss_weight=1.0)),\n    train_cfg=None,\n    test_cfg=dict(topk=100, local_maximum_kernel=3, max_per_img=100))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(\n        type='RandomCenterCropPad',\n        # The cropped images are padded into squares during training,\n        # but may be less than crop_size.\n        crop_size=(512, 512),\n        ratios=(0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3),\n        mean=[0, 0, 0],\n        std=[1, 1, 1],\n        to_rgb=True,\n        test_pad_mode=None),\n    # Make sure the output is always crop_size.\n    dict(type='Resize', scale=(512, 512), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args={{_base_.backend_args}},\n        to_float32=True),\n    # don't need Resize\n    dict(\n        type='RandomCenterCropPad',\n        ratios=None,\n        border=None,\n        mean=[0, 0, 0],\n        std=[1, 1, 1],\n        to_rgb=True,\n        test_mode=True,\n        test_pad_mode=['logical_or', 31],\n        test_pad_add_pix=1),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'border'))\n]\n\n# Use RepeatDataset to speed up training\ntrain_dataloader = dict(\n    batch_size=16,\n    num_workers=4,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=5,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args={{_base_.backend_args}},\n        )))\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\n# Based on the default settings of modern detectors, the SGD effect is better\n# than the Adam in the source code, so we use SGD default settings and\n# if you use adam+lr5e-4, the map is 29.1.\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n\nmax_epochs = 28\n# learning policy\n# Based on the default settings of modern detectors, we added warmup settings.\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[18, 24],  # the real step is [18*5, 24*5]\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)  # the real epoch is 28*5=140\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (16 samples per GPU)\nauto_scale_lr = dict(base_batch_size=128)\n"
  },
  {
    "path": "configs/centernet/centernet_r18_8xb16-crop512-140e_coco.py",
    "content": "_base_ = './centernet_r18-dcnv2_8xb16-crop512-140e_coco.py'\n\nmodel = dict(neck=dict(use_dcn=False))\n"
  },
  {
    "path": "configs/centernet/centernet_tta.py",
    "content": "# This is different from the TTA of official CenterNet.\n\ntta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(type='nms', iou_threshold=0.5), max_per_img=100))\n\ntta_pipeline = [\n    dict(type='LoadImageFromFile', to_float32=True, backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[\n            [\n                # ``RandomFlip`` must be placed before ``RandomCenterCropPad``,\n                # otherwise bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type='RandomFlip', prob=1.),\n                dict(type='RandomFlip', prob=0.)\n            ],\n            [\n                dict(\n                    type='RandomCenterCropPad',\n                    ratios=None,\n                    border=None,\n                    mean=[0, 0, 0],\n                    std=[1, 1, 1],\n                    to_rgb=True,\n                    test_mode=True,\n                    test_pad_mode=['logical_or', 31],\n                    test_pad_add_pix=1),\n            ],\n            [dict(type='LoadAnnotations', with_bbox=True)],\n            [\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'flip', 'flip_direction', 'border'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "configs/centernet/metafile.yml",
    "content": "Collections:\n  - Name: CenterNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x TITANXP GPUs\n      Architecture:\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1904.07850\n      Title: 'Objects as Points'\n    README: configs/centernet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.13.0/mmdet/models/detectors/centernet.py#L10\n      Version: v2.13.0\n\nModels:\n  - Name: centernet_r18-dcnv2_8xb16-crop512-140e_coco\n    In Collection: CenterNet\n    Config: configs/centernet/centernet_r18-dcnv2_8xb16-crop512-140e_coco.py\n    Metadata:\n      Batch Size: 128\n      Training Memory (GB): 3.47\n      Epochs: 140\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 29.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_dcnv2_140e_coco/centernet_resnet18_dcnv2_140e_coco_20210702_155131-c8cd631f.pth\n\n  - Name: centernet_r18_8xb16-crop512-140e_coco\n    In Collection: CenterNet\n    Config: configs/centernet/centernet_r18_8xb16-crop512-140e_coco.py\n    Metadata:\n      Batch Size: 128\n      Training Memory (GB): 3.45\n      Epochs: 140\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 25.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/centernet/centernet_resnet18_140e_coco/centernet_resnet18_140e_coco_20210705_093630-bb5b3bf7.pth\n\n  - Name: centernet-update_r50-caffe_fpn_ms-1x_coco\n    In Collection: CenterNet\n    Config: configs/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco.py\n    Metadata:\n      Batch Size: 16\n      Training Memory (GB): 3.3\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.2\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/centernet/centernet-update_r50-caffe_fpn_ms-1x_coco/centernet-update_r50-caffe_fpn_ms-1x_coco_20230512_203845-8306baf2.pth\n"
  },
  {
    "path": "configs/centripetalnet/README.md",
    "content": "# CentripetalNet\n\n> [CentripetalNet: Pursuing High-quality Keypoint Pairs for Object Detection](https://arxiv.org/abs/2003.09119)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nKeypoint-based detectors have achieved pretty-well performance. However, incorrect keypoint matching is still widespread and greatly affects the performance of the detector. In this paper, we propose CentripetalNet which uses centripetal shift to pair corner keypoints from the same instance. CentripetalNet predicts the position and the centripetal shift of the corner points and matches corners whose shifted results are aligned. Combining position information, our approach matches corner points more accurately than the conventional embedding approaches do. Corner pooling extracts information inside the bounding boxes onto the border. To make this information more aware at the corners, we design a cross-star deformable convolution network to conduct feature adaption. Furthermore, we explore instance segmentation on anchor-free detectors by equipping our CentripetalNet with a mask prediction module. On MS-COCO test-dev, our CentripetalNet not only outperforms all existing anchor-free detectors with an AP of 48.0% but also achieves comparable performance to the state-of-the-art instance segmentation approaches with a 40.2% MaskAP.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143873955-42804e0e-3638-4c5b-8bf4-ac8133bbcdc8.png\"/>\n</div>\n\n## Results and Models\n\n|     Backbone     |                                Batch Size                                 | Step/Total Epochs | Mem (GB) | Inf time (fps) | box AP |                                  Config                                   |                                                                                                                                                                                                    Download                                                                                                                                                                                                    |\n| :--------------: | :-----------------------------------------------------------------------: | :---------------: | :------: | :------------: | :----: | :-----------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HourglassNet-104 | [16 x 6](./centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py) |      190/210      |   16.7   |      3.7       |  44.8  | [config](./centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/centripetalnet/centripetalnet_hourglass104_mstest_16x6_210e_coco/centripetalnet_hourglass104_mstest_16x6_210e_coco_20200915_204804-3ccc61e5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/centripetalnet/centripetalnet_hourglass104_mstest_16x6_210e_coco/centripetalnet_hourglass104_mstest_16x6_210e_coco_20200915_204804.log.json) |\n\nNote:\n\n- TTA setting is single-scale and `flip=True`. If you want to reproduce the TTA performance, please add `--tta` in the test command.\n- The model we released is the best checkpoint rather than the latest checkpoint (box AP 44.8 vs 44.6 in our experiment).\n\n## Citation\n\n```latex\n@InProceedings{Dong_2020_CVPR,\nauthor = {Dong, Zhiwei and Li, Guoxuan and Liao, Yue and Wang, Fei and Ren, Pengju and Qian, Chen},\ntitle = {CentripetalNet: Pursuing High-Quality Keypoint Pairs for Object Detection},\nbooktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},\nmonth = {June},\nyear = {2020}\n}\n```\n"
  },
  {
    "path": "configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py",
    "content": "_base_ = [\n    '../_base_/default_runtime.py', '../_base_/datasets/coco_detection.py'\n]\n\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True)\n\n# model settings\nmodel = dict(\n    type='CornerNet',\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type='HourglassNet',\n        downsample_times=5,\n        num_stacks=2,\n        stage_channels=[256, 256, 384, 384, 384, 512],\n        stage_blocks=[2, 2, 2, 2, 2, 4],\n        norm_cfg=dict(type='BN', requires_grad=True)),\n    neck=None,\n    bbox_head=dict(\n        type='CentripetalHead',\n        num_classes=80,\n        in_channels=256,\n        num_feat_levels=2,\n        corner_emb_channels=0,\n        loss_heatmap=dict(\n            type='GaussianFocalLoss', alpha=2.0, gamma=4.0, loss_weight=1),\n        loss_offset=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1),\n        loss_guiding_shift=dict(\n            type='SmoothL1Loss', beta=1.0, loss_weight=0.05),\n        loss_centripetal_shift=dict(\n            type='SmoothL1Loss', beta=1.0, loss_weight=1)),\n    # training and testing settings\n    train_cfg=None,\n    test_cfg=dict(\n        corner_topk=100,\n        local_maximum_kernel=3,\n        distance_threshold=0.5,\n        score_thr=0.05,\n        max_per_img=100,\n        nms=dict(type='soft_nms', iou_threshold=0.5, method='gaussian')))\n\n# data settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(\n        # The cropped images are padded into squares during training,\n        # but may be smaller than crop_size.\n        type='RandomCenterCropPad',\n        crop_size=(511, 511),\n        ratios=(0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3),\n        test_mode=False,\n        test_pad_mode=None,\n        mean=data_preprocessor['mean'],\n        std=data_preprocessor['std'],\n        # Image data is not converted to rgb.\n        to_rgb=data_preprocessor['bgr_to_rgb']),\n    dict(type='Resize', scale=(511, 511), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        to_float32=True,\n        backend_args=_base_.backend_args),\n    # don't need Resize\n    dict(\n        type='RandomCenterCropPad',\n        crop_size=None,\n        ratios=None,\n        border=None,\n        test_mode=True,\n        test_pad_mode=['logical_or', 127],\n        mean=data_preprocessor['mean'],\n        std=data_preprocessor['std'],\n        # Image data is not converted to rgb.\n        to_rgb=data_preprocessor['bgr_to_rgb']),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'border'))\n]\n\ntrain_dataloader = dict(\n    batch_size=6,\n    num_workers=3,\n    batch_sampler=None,\n    dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='Adam', lr=0.0005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\nmax_epochs = 210\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[190],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (16 GPUs) x (6 samples per GPU)\nauto_scale_lr = dict(base_batch_size=96)\n\ntta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(\n        nms=dict(type='soft_nms', iou_threshold=0.5, method='gaussian'),\n        max_per_img=100))\n\ntta_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        to_float32=True,\n        backend_args=_base_.backend_args),\n    dict(\n        type='TestTimeAug',\n        transforms=[\n            [\n                # ``RandomFlip`` must be placed before ``RandomCenterCropPad``,\n                # otherwise bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type='RandomFlip', prob=1.),\n                dict(type='RandomFlip', prob=0.)\n            ],\n            [\n                dict(\n                    type='RandomCenterCropPad',\n                    crop_size=None,\n                    ratios=None,\n                    border=None,\n                    test_mode=True,\n                    test_pad_mode=['logical_or', 127],\n                    mean=data_preprocessor['mean'],\n                    std=data_preprocessor['std'],\n                    # Image data is not converted to rgb.\n                    to_rgb=data_preprocessor['bgr_to_rgb'])\n            ],\n            [dict(type='LoadAnnotations', with_bbox=True)],\n            [\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'flip', 'flip_direction', 'border'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "configs/centripetalnet/metafile.yml",
    "content": "Collections:\n  - Name: CentripetalNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - Adam\n      Training Resources: 16x V100 GPUs\n      Architecture:\n        - Corner Pooling\n        - Stacked Hourglass Network\n    Paper:\n      URL: https://arxiv.org/abs/2003.09119\n      Title: 'CentripetalNet: Pursuing High-quality Keypoint Pairs for Object Detection'\n    README: configs/centripetalnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.5.0/mmdet/models/detectors/cornernet.py#L9\n      Version: v2.5.0\n\nModels:\n  - Name: centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco\n    In Collection: CentripetalNet\n    Config: configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py\n    Metadata:\n      Batch Size: 96\n      Training Memory (GB): 16.7\n      inference time (ms/im):\n        - value: 270.27\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 210\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/centripetalnet/centripetalnet_hourglass104_mstest_16x6_210e_coco/centripetalnet_hourglass104_mstest_16x6_210e_coco_20200915_204804-3ccc61e5.pth\n"
  },
  {
    "path": "configs/cityscapes/README.md",
    "content": "# Cityscapes\n\n> [The Cityscapes Dataset for Semantic Urban Scene Understanding](https://arxiv.org/abs/1604.01685)\n\n<!-- [DATASET] -->\n\n## Abstract\n\nVisual understanding of complex urban street scenes is an enabling factor for a wide range of applications. Object detection has benefited enormously from large-scale datasets, especially in the context of deep learning. For semantic urban scene understanding, however, no current dataset adequately captures the complexity of real-world urban scenes.\nTo address this, we introduce Cityscapes, a benchmark suite and large-scale dataset to train and test approaches for pixel-level and instance-level semantic labeling. Cityscapes is comprised of a large, diverse set of stereo video sequences recorded in streets from 50 different cities. 5000 of these images have high quality pixel-level annotations; 20000 additional images have coarse annotations to enable methods that leverage large volumes of weakly-labeled data. Crucially, our effort exceeds previous attempts in terms of dataset size, annotation richness, scene variability, and complexity. Our accompanying empirical study provides an in-depth analysis of the dataset characteristics, as well as a performance evaluation of several state-of-the-art approaches based on our benchmark.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143874154-db4484a5-9211-41f6-852a-b7f0a8c9ec26.png\"/>\n</div>\n\n## Common settings\n\n- All baselines were trained using 8 GPU with a batch size of 8 (1 images per GPU) using the [linear scaling rule](https://arxiv.org/abs/1706.02677) to scale the learning rate.\n- All models were trained on `cityscapes_train`, and tested on `cityscapes_val`.\n- 1x training schedule indicates 64 epochs which corresponds to slightly less than the 24k iterations reported in the original schedule from the [Mask R-CNN paper](https://arxiv.org/abs/1703.06870)\n- COCO pre-trained weights are used to initialize.\n- A conversion [script](../../tools/dataset_converters/cityscapes.py) is provided to convert Cityscapes into COCO format. Please refer to [install.md](../../docs/1_exist_data_model.md#prepare-datasets) for details.\n- `CityscapesDataset` implemented three evaluation methods. `bbox` and `segm` are standard COCO bbox/mask AP. `cityscapes` is the cityscapes dataset official evaluation, which may be slightly higher than COCO.\n\n### Faster R-CNN\n\n| Backbone |  Style  | Lr schd |  Scale   | Mem (GB) | Inf time (fps) | box AP |                      Config                      |                                                                                                                          Download                                                                                                                           |\n| :------: | :-----: | :-----: | :------: | :------: | :------------: | :----: | :----------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN | pytorch |   1x    | 800-1024 |   5.2    |       -        |  40.3  | [config](./faster-rcnn_r50_fpn_1x_cityscapes.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cityscapes/faster_rcnn_r50_fpn_1x_cityscapes_20200502-829424c0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cityscapes/faster_rcnn_r50_fpn_1x_cityscapes_20200502_114915.log.json) |\n\n### Mask R-CNN\n\n| Backbone |  Style  | Lr schd |  Scale   | Mem (GB) | Inf time (fps) | box AP | mask AP |                     Config                     |                                                                                                                                                            Download                                                                                                                                                            |\n| :------: | :-----: | :-----: | :------: | :------: | :------------: | :----: | :-----: | :--------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN | pytorch |   1x    | 800-1024 |   5.3    |       -        |  40.9  |  36.4   | [config](./mask-rcnn_r50_fpn_1x_cityscapes.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cityscapes/mask_rcnn_r50_fpn_1x_cityscapes/mask_rcnn_r50_fpn_1x_cityscapes_20201211_133733-d2858245.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cityscapes/mask_rcnn_r50_fpn_1x_cityscapes/mask_rcnn_r50_fpn_1x_cityscapes_20201211_133733.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{Cordts2016Cityscapes,\n   title={The Cityscapes Dataset for Semantic Urban Scene Understanding},\n   author={Cordts, Marius and Omran, Mohamed and Ramos, Sebastian and Rehfeld, Timo and Enzweiler, Markus and Benenson, Rodrigo and Franke, Uwe and Roth, Stefan and Schiele, Bernt},\n   booktitle={Proc. of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},\n   year={2016}\n}\n```\n"
  },
  {
    "path": "configs/cityscapes/faster-rcnn_r50_fpn_1x_cityscapes.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/cityscapes_detection.py',\n    '../_base_/default_runtime.py', '../_base_/schedules/schedule_1x.py'\n]\nmodel = dict(\n    backbone=dict(init_cfg=None),\n    roi_head=dict(\n        bbox_head=dict(\n            num_classes=8,\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))))\n\n# optimizer\n# lr is set for a batch size of 8\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=8,\n        by_epoch=True,\n        # [7] yields higher performance than [6]\n        milestones=[7],\n        gamma=0.1)\n]\n\n# actual epoch = 8 * 8 = 64\ntrain_cfg = dict(max_epochs=8)\n\n# For better, more stable performance initialize from COCO\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'  # noqa\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (1 samples per GPU)\n# TODO: support auto scaling lr\n# auto_scale_lr = dict(base_batch_size=8)\n"
  },
  {
    "path": "configs/cityscapes/mask-rcnn_r50_fpn_1x_cityscapes.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/cityscapes_instance.py',\n    '../_base_/default_runtime.py', '../_base_/schedules/schedule_1x.py'\n]\nmodel = dict(\n    backbone=dict(init_cfg=None),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            num_classes=8,\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n        mask_head=dict(num_classes=8)))\n\n# optimizer\n# lr is set for a batch size of 8\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=8,\n        by_epoch=True,\n        # [7] yields higher performance than [6]\n        milestones=[7],\n        gamma=0.1)\n]\n\n# actual epoch = 8 * 8 = 64\ntrain_cfg = dict(max_epochs=8)\n\n# For better, more stable performance initialize from COCO\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth'  # noqa\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (1 samples per GPU)\n# TODO: support auto scaling lr\n# auto_scale_lr = dict(base_batch_size=8)\n"
  },
  {
    "path": "configs/common/lsj-100e_coco-detection.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\nimage_size = (1024, 1024)\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\n# Use RepeatDataset to speed up training\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        type='RepeatDataset',\n        times=4,  # simply change this from 2 to 16 for 50e - 400e training.\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\nmax_epochs = 25\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=5)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# optimizer assumes bs=64\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.00004))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.067, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n\n# only keep latest 2 checkpoints\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=2))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/common/lsj-100e_coco-instance.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\nimage_size = (1024, 1024)\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\n# Use RepeatDataset to speed up training\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        type='RepeatDataset',\n        times=4,  # simply change this from 2 to 16 for 50e - 400e training.\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\nmax_epochs = 25\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=5)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# optimizer assumes bs=64\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.00004))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.067, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n\n# only keep latest 2 checkpoints\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=2))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/common/lsj-200e_coco-detection.py",
    "content": "_base_ = './lsj-100e_coco-detection.py'\n\n# 8x25=200e\ntrain_dataloader = dict(dataset=dict(times=8))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.067, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=25,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/common/lsj-200e_coco-instance.py",
    "content": "_base_ = './lsj-100e_coco-instance.py'\n\n# 8x25=200e\ntrain_dataloader = dict(dataset=dict(times=8))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.067, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=25,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/common/ms-90k_coco.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Align with Detectron2\nbackend = 'pillow'\ntrain_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True,\n        backend=backend),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True, backend=backend),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    pin_memory=True,\n    sampler=dict(type='InfiniteSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    pin_memory=True,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# training schedule for 90k\nmax_iter = 90000\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iter, val_interval=10000)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/common/ms-poly-90k_coco-instance.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Align with Detectron2\nbackend = 'pillow'\ntrain_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True,\n        backend=backend),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True, backend=backend),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    pin_memory=True,\n    sampler=dict(type='InfiniteSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    pin_memory=True,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# training schedule for 90k\nmax_iter = 90000\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iter, val_interval=10000)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/common/ms-poly_3x_coco-instance.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# In mstrain 3x config, img_scale=[(1333, 640), (1333, 800)],\n# multiscale_mode='range'\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',\n        times=3,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# training schedule for 3x with `RepeatDataset`\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\n# Experiments show that using milestones=[9, 11] has higher performance\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[9, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/common/ms_3x_coco-instance.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',\n        times=3,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# training schedule for 3x with `RepeatDataset`\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\n# Experiments show that using milestones=[9, 11] has higher performance\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[9, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/common/ms_3x_coco.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',\n        times=3,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# training schedule for 3x with `RepeatDataset`\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\n# Experiments show that using milestones=[9, 11] has higher performance\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[9, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/common/ssj_270k_coco-instance.py",
    "content": "_base_ = '../_base_/default_runtime.py'\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\nimage_size = (1024, 1024)\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Standard Scale Jittering (SSJ) resizes and crops an image\n# with a resize range of 0.8 to 1.25 of the original image size.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.8, 1.25),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='InfiniteSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# The model is trained by 270k iterations with batch_size 64,\n# which is roughly equivalent to 144 epochs.\n\nmax_iters = 270000\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iters, val_interval=10000)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# optimizer assumes bs=64\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.00004))\n\n# learning rate policy\n# lr steps at [0.9, 0.95, 0.975] of the maximum iterations\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=270000,\n        by_epoch=False,\n        milestones=[243000, 256500, 263250],\n        gamma=0.1)\n]\n\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\nlog_processor = dict(by_epoch=False)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/common/ssj_scp_270k_coco-instance.py",
    "content": "_base_ = 'ssj_270k_coco-instance.py'\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\nimage_size = (1024, 1024)\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Standard Scale Jittering (SSJ) resizes and crops an image\n# with a resize range of 0.8 to 1.25 of the original image size.\nload_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.8, 1.25),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=image_size),\n]\ntrain_pipeline = [\n    dict(type='CopyPaste', max_num_pasted=100),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='MultiImageMixDataset',\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=load_pipeline,\n            backend_args=backend_args),\n        pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/condinst/README.md",
    "content": "# CondInst\n\n> [CondInst: Conditional Convolutions for Instance\n> Segmentation](https://arxiv.org/pdf/2003.05664.pdf)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe propose a simple yet effective instance segmentation framework, termed CondInst (conditional convolutions for instance segmentation). Top-performing instance segmentation methods such as Mask\nR-CNN rely on ROI operations (typically ROIPool or ROIAlign) to\nobtain the final instance masks. In contrast, we propose to solve instance segmentation from a new perspective. Instead of using instancewise ROIs as inputs to a network of fixed weights, we employ dynamic\ninstance-aware networks, conditioned on instances. CondInst enjoys two\nadvantages: 1) Instance segmentation is solved by a fully convolutional\nnetwork, eliminating the need for ROI cropping and feature alignment.\n2\\) Due to the much improved capacity of dynamically-generated conditional convolutions, the mask head can be very compact (e.g., 3 conv.\nlayers, each having only 8 channels), leading to significantly faster inference. We demonstrate a simpler instance segmentation method that can\nachieve improved performance in both accuracy and inference speed. On\nthe COCO dataset, we outperform a few recent methods including welltuned Mask R-CNN baselines, without longer training schedules needed.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/57584090/203303488-3dbc36da-09a6-4dc8-be9d-d9af27bd1234.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |  Style  | MS train | Lr schd | bbox AP | mask AP |                          Config                           |                                                                                                                                                                              Download                                                                                                                                                                              |\n| :------: | :-----: | :------: | :-----: | :-----: | :-----: | :-------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | pytorch |    Y     |   1x    |  39.8   |  36.0   | [config](./condinst_r50_fpn_ms-poly-90k_coco_instance.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/condinst/condinst_r50_fpn_ms-poly-90k_coco_instance/condinst_r50_fpn_ms-poly-90k_coco_instance_20221129_125223-4c186406.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/condinst/condinst_r50_fpn_ms-poly-90k_coco_instance/condinst_r50_fpn_ms-poly-90k_coco_instance_20221129_125223.json) |\n\n## Citation\n\n```latex\n@inproceedings{tian2020conditional,\n  title     =  {Conditional Convolutions for Instance Segmentation},\n  author    =  {Tian, Zhi and Shen, Chunhua and Chen, Hao},\n  booktitle =  {Proc. Eur. Conf. Computer Vision (ECCV)},\n  year      =  {2020}\n}\n```\n"
  },
  {
    "path": "configs/condinst/condinst_r50_fpn_ms-poly-90k_coco_instance.py",
    "content": "_base_ = '../common/ms-poly-90k_coco-instance.py'\n\n# model settings\nmodel = dict(\n    type='CondInst',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50'),\n        style='pytorch'),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        num_outs=5,\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='CondInstBboxHead',\n        num_params=169,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        norm_on_bbox=True,\n        centerness_on_reg=True,\n        dcn_on_last_conv=False,\n        center_sampling=True,\n        conv_bias=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    mask_head=dict(\n        type='CondInstMaskHead',\n        num_layers=3,\n        feat_channels=8,\n        size_of_interest=8,\n        mask_out_stride=4,\n        max_masks_to_train=300,\n        mask_feature_head=dict(\n            in_channels=256,\n            feat_channels=128,\n            start_level=0,\n            end_level=2,\n            out_channels=8,\n            mask_stride=8,\n            num_stacked_convs=4,\n            norm_cfg=dict(type='BN', requires_grad=True)),\n        loss_mask=dict(\n            type='DiceLoss',\n            use_sigmoid=True,\n            activate=True,\n            eps=5e-6,\n            loss_weight=1.0)),\n    # model training and testing settings\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100,\n        mask_thr=0.5))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/condinst/metafile.yml",
    "content": "Collections:\n  - Name: CondInst\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - FPN\n        - FCOS\n        - ResNet\n    Paper: https://arxiv.org/abs/2003.05664\n    README: configs/condinst/README.md\n\nModels:\n  - Name: condinst_r50_fpn_ms-poly-90k_coco_instance\n    In Collection: CondInst\n    Config: configs/condinst/condinst_r50_fpn_ms-poly-90k_coco_instance.py\n    Metadata:\n      Training Memory (GB): 4.4\n      Iterations: 90000\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.0\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/condinst/condinst_r50_fpn_ms-poly-90k_coco_instance/condinst_r50_fpn_ms-poly-90k_coco_instance_20221129_125223-4c186406.pth\n"
  },
  {
    "path": "configs/conditional_detr/README.md",
    "content": "# Conditional DETR\n\n> [Conditional DETR for Fast Training Convergence](https://arxiv.org/abs/2108.06152)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe DETR approach applies the transformer encoder and decoder architecture to object detection and achieves promising performance. In this paper, we handle the critical issue, slow training convergence, and present a conditional cross-attention mechanism for fast DETR training. Our approach is motivated by that the cross-attention in DETR relies highly on the content embeddings and that the spatial embeddings make minor contributions, increasing the need for high-quality content embeddings and thus increasing the training difficulty.\n\n<div align=center>\n<img src=\"https://github.com/Atten4Vis/ConditionalDETR/blob/main/.github/attention-maps.png?raw=true\"/>\n</div>\n\nOur conditional DETR learns a conditional spatial query from the decoder embedding for decoder multi-head cross-attention. The benefit is that through the conditional spatial query, each cross-attention head is able to attend to a band containing a distinct region, e.g., one object extremity or a region inside the object box (Figure 1). This narrows down the spatial range for localizing the distinct regions for object classification and box regression, thus relaxing the dependence on the content embeddings and easing the training. Empirical results show that conditional DETR converges 6.7x faster for the backbones R50 and R101 and 10x faster for stronger backbones DC5-R50 and DC5-R101.\n\n<div align=center>\n<img src=\"https://github.com/Atten4Vis/ConditionalDETR/raw/main/.github/conditional-detr.png\" width=\"48%\"/>\n<img src=\"https://github.com/Atten4Vis/ConditionalDETR/raw/main/.github/convergence-curve.png\" width=\"48%\"/>\n</div>\n\n## Results and Models\n\nWe provide the config files and models for Conditional DETR: [Conditional DETR for Fast Training Convergence](https://arxiv.org/abs/2108.06152).\n\n| Backbone |      Model       | Lr schd | Mem (GB) | Inf time (fps) | box AP |                      Config                       |                                                                                                                                                                        Download                                                                                                                                                                        |\n| :------: | :--------------: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | Conditional DETR |   50e   |          |                |  41.1  | [config](./conditional-detr_r50_8xb2-50e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/conditional_detr/conditional-detr_r50_8xb2-50e_coco/conditional-detr_r50_8xb2-50e_coco_20221121_180202-c83a1dc0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/conditional_detr/conditional-detr_r50_8xb2-50e_coco/conditional-detr_r50_8xb2-50e_coco_20221121_180202.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{meng2021-CondDETR,\n  title       = {Conditional DETR for Fast Training Convergence},\n  author      = {Meng, Depu and Chen, Xiaokang and Fan, Zejia and Zeng, Gang and Li, Houqiang and Yuan, Yuhui and Sun, Lei and Wang, Jingdong},\n  booktitle   = {Proceedings of the IEEE International Conference on Computer Vision (ICCV)},\n  year        = {2021}\n}\n```\n"
  },
  {
    "path": "configs/conditional_detr/conditional-detr_r50_8xb2-50e_coco.py",
    "content": "_base_ = ['../detr/detr_r50_8xb2-150e_coco.py']\nmodel = dict(\n    type='ConditionalDETR',\n    num_queries=300,\n    decoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(\n                _delete_=True,\n                embed_dims=256,\n                num_heads=8,\n                attn_drop=0.1,\n                cross_attn=False),\n            cross_attn_cfg=dict(\n                _delete_=True,\n                embed_dims=256,\n                num_heads=8,\n                attn_drop=0.1,\n                cross_attn=True))),\n    bbox_head=dict(\n        type='ConditionalDETRHead',\n        loss_cls=dict(\n            _delete_=True,\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])))\n\n# learning policy\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=50, val_interval=1)\n\nparam_scheduler = [dict(type='MultiStepLR', end=50, milestones=[40])]\n"
  },
  {
    "path": "configs/conditional_detr/metafile.yml",
    "content": "Collections:\n  - Name: Conditional DETR\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ResNet\n        - Transformer\n    Paper:\n      URL: https://arxiv.org/abs/2108.06152\n      Title: 'Conditional DETR for Fast Training Convergence'\n    README: configs/conditional_detr/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/f4112c9e5611468ffbd57cfba548fd1289264b52/mmdet/models/detectors/conditional_detr.py#L14\n      Version: v3.0.0rc6\n\nModels:\n  - Name: conditional-detr_r50_8xb2-50e_coco\n    In Collection: Conditional DETR\n    Config: configs/conditional_detr/conditional-detr_r50_8xb2-50e_coco.py\n    Metadata:\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/conditional_detr/conditional-detr_r50_8xb2-50e_coco/conditional-detr_r50_8xb2-50e_coco_20221121_180202-c83a1dc0.pth\n"
  },
  {
    "path": "configs/convnext/README.md",
    "content": "# ConvNeXt\n\n> [A ConvNet for the 2020s](https://arxiv.org/abs/2201.03545)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe \"Roaring 20s\" of visual recognition began with the introduction of Vision Transformers (ViTs), which quickly superseded ConvNets as the state-of-the-art image classification model. A vanilla ViT, on the other hand, faces difficulties when applied to general computer vision tasks such as object detection and semantic segmentation. It is the hierarchical Transformers (e.g., Swin Transformers) that reintroduced several ConvNet priors, making Transformers practically viable as a generic vision backbone and demonstrating remarkable performance on a wide variety of vision tasks. However, the effectiveness of such hybrid approaches is still largely credited to the intrinsic superiority of Transformers, rather than the inherent inductive biases of convolutions. In this work, we reexamine the design spaces and test the limits of what a pure ConvNet can achieve. We gradually \"modernize\" a standard ResNet toward the design of a vision Transformer, and discover several key components that contribute to the performance difference along the way. The outcome of this exploration is a family of pure ConvNet models dubbed ConvNeXt. Constructed entirely from standard ConvNet modules, ConvNeXts compete favorably with Transformers in terms of accuracy and scalability, achieving 87.8% ImageNet top-1 accuracy and outperforming Swin Transformers on COCO detection and ADE20K segmentation, while maintaining the simplicity and efficiency of standard ConvNets.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/8370623/148624004-e9581042-ea4d-4e10-b3bd-42c92b02053b.png\" width=\"90%\"/>\n</div>\n\n## Results and models\n\n|       Method       |  Backbone  |  Pretrain   | Lr schd | Multi-scale crop | FP16 | Mem (GB) | box AP | mask AP |                                         Config                                          |                                                                                                                                                                                                                                            Download                                                                                                                                                                                                                                             |\n| :----------------: | :--------: | :---------: | :-----: | :--------------: | :--: | :------: | :----: | :-----: | :-------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     Mask R-CNN     | ConvNeXt-T | ImageNet-1K |   3x    |       yes        | yes  |   7.3    |  46.2  |  41.7   |            [config](./mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco.py)            |                                           [model](https://download.openmmlab.com/mmdetection/v2.0/convnext/mask_rcnn_convnext-t_p4_w7_fpn_fp16_ms-crop_3x_coco/mask_rcnn_convnext-t_p4_w7_fpn_fp16_ms-crop_3x_coco_20220426_154953-050731f4.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v2.0/convnext/mask_rcnn_convnext-t_p4_w7_fpn_fp16_ms-crop_3x_coco/mask_rcnn_convnext-t_p4_w7_fpn_fp16_ms-crop_3x_coco_20220426_154953.log.json)                                           |\n| Cascade Mask R-CNN | ConvNeXt-T | ImageNet-1K |   3x    |       yes        | yes  |   9.0    |  50.3  |  43.6   | [config](./cascade-mask-rcnn_convnext-t-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/convnext/cascade_mask_rcnn_convnext-t_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco/cascade_mask_rcnn_convnext-t_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220509_204200-8f07c40b.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v2.0/convnext/cascade_mask_rcnn_convnext-t_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco/cascade_mask_rcnn_convnext-t_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220509_204200.log.json) |\n| Cascade Mask R-CNN | ConvNeXt-S | ImageNet-1K |   3x    |       yes        | yes  |   12.3   |  51.8  |  44.8   | [config](./cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/convnext/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220510_201004-3d24f5a4.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v2.0/convnext/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220510_201004.log.json) |\n\n**Note**:\n\n- ConvNeXt backbone needs to install [MMPreTrain](https://github.com/open-mmlab/mmpretrain) first, which has abundant backbones for downstream tasks.\n\n```shell\npip install mmpretrain\n```\n\n- The performance is unstable. `Cascade Mask R-CNN` may fluctuate about 0.2 mAP.\n\n## Citation\n\n```bibtex\n@article{liu2022convnet,\n  title={A ConvNet for the 2020s},\n  author={Liu, Zhuang and Mao, Hanzi and Wu, Chao-Yuan and Feichtenhofer, Christoph and Darrell, Trevor and Xie, Saining},\n  journal={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},\n  year={2022}\n}\n```\n"
  },
  {
    "path": "configs/convnext/cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_convnext-t-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py'  # noqa\n\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(\n    imports=['mmpretrain.models'], allow_failed_imports=False)\ncheckpoint_file = 'https://download.openmmlab.com/mmclassification/v0/convnext/downstream/convnext-small_3rdparty_32xb128-noema_in1k_20220301-303e75e3.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='mmpretrain.ConvNeXt',\n        arch='small',\n        out_indices=[0, 1, 2, 3],\n        drop_path_rate=0.6,\n        layer_scale_init_value=1.0,\n        gap_before_final_norm=False,\n        init_cfg=dict(\n            type='Pretrained', checkpoint=checkpoint_file,\n            prefix='backbone.')))\n\noptim_wrapper = dict(paramwise_cfg={\n    'decay_rate': 0.7,\n    'decay_type': 'layer_wise',\n    'num_layers': 12\n})\n"
  },
  {
    "path": "configs/convnext/cascade-mask-rcnn_convnext-t-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(\n    imports=['mmpretrain.models'], allow_failed_imports=False)\ncheckpoint_file = 'https://download.openmmlab.com/mmclassification/v0/convnext/downstream/convnext-tiny_3rdparty_32xb128-noema_in1k_20220301-795e9634.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='mmpretrain.ConvNeXt',\n        arch='tiny',\n        out_indices=[0, 1, 2, 3],\n        drop_path_rate=0.4,\n        layer_scale_init_value=1.0,\n        gap_before_final_norm=False,\n        init_cfg=dict(\n            type='Pretrained', checkpoint=checkpoint_file,\n            prefix='backbone.')),\n    neck=dict(in_channels=[96, 192, 384, 768]),\n    roi_head=dict(bbox_head=[\n        dict(\n            type='ConvFCBBoxHead',\n            num_shared_convs=4,\n            num_shared_fcs=1,\n            in_channels=256,\n            conv_out_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            reg_decoded_bbox=True,\n            norm_cfg=dict(type='SyncBN', requires_grad=True),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),\n        dict(\n            type='ConvFCBBoxHead',\n            num_shared_convs=4,\n            num_shared_fcs=1,\n            in_channels=256,\n            conv_out_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.05, 0.05, 0.1, 0.1]),\n            reg_class_agnostic=False,\n            reg_decoded_bbox=True,\n            norm_cfg=dict(type='SyncBN', requires_grad=True),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),\n        dict(\n            type='ConvFCBBoxHead',\n            num_shared_convs=4,\n            num_shared_fcs=1,\n            in_channels=256,\n            conv_out_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.033, 0.033, 0.067, 0.067]),\n            reg_class_agnostic=False,\n            reg_decoded_bbox=True,\n            norm_cfg=dict(type='SyncBN', requires_grad=True),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=10.0))\n    ]))\n\n# augmentation strategy originates from DETR / Sparse RCNN\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    constructor='LearningRateDecayOptimizerConstructor',\n    paramwise_cfg={\n        'decay_rate': 0.7,\n        'decay_type': 'layer_wise',\n        'num_layers': 6\n    },\n    optimizer=dict(\n        _delete_=True,\n        type='AdamW',\n        lr=0.0002,\n        betas=(0.9, 0.999),\n        weight_decay=0.05))\n"
  },
  {
    "path": "configs/convnext/mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(\n    imports=['mmpretrain.models'], allow_failed_imports=False)\ncheckpoint_file = 'https://download.openmmlab.com/mmclassification/v0/convnext/downstream/convnext-tiny_3rdparty_32xb128-noema_in1k_20220301-795e9634.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='mmpretrain.ConvNeXt',\n        arch='tiny',\n        out_indices=[0, 1, 2, 3],\n        drop_path_rate=0.4,\n        layer_scale_init_value=1.0,\n        gap_before_final_norm=False,\n        init_cfg=dict(\n            type='Pretrained', checkpoint=checkpoint_file,\n            prefix='backbone.')),\n    neck=dict(in_channels=[96, 192, 384, 768]))\n\n# augmentation strategy originates from DETR / Sparse RCNN\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    constructor='LearningRateDecayOptimizerConstructor',\n    paramwise_cfg={\n        'decay_rate': 0.95,\n        'decay_type': 'layer_wise',\n        'num_layers': 6\n    },\n    optimizer=dict(\n        _delete_=True,\n        type='AdamW',\n        lr=0.0001,\n        betas=(0.9, 0.999),\n        weight_decay=0.05,\n    ))\n"
  },
  {
    "path": "configs/convnext/metafile.yml",
    "content": "Models:\n  - Name: mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/convnext/mask-rcnn_convnext-t-p4-w7_fpn_amp-ms-crop-3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.3\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Mixed Precision Training\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ConvNeXt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/convnext/mask_rcnn_convnext-t_p4_w7_fpn_fp16_ms-crop_3x_coco/mask_rcnn_convnext-t_p4_w7_fpn_fp16_ms-crop_3x_coco_20220426_154953-050731f4.pth\n    Paper:\n      URL: https://arxiv.org/abs/2201.03545\n      Title: 'A ConvNet for the 2020s'\n    README: configs/convnext/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/backbones/swin.py#L465\n      Version: v2.16.0\n\n  - Name: cascade-mask-rcnn_convnext-t-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco\n    In Collection: Cascade Mask R-CNN\n    Config: configs/convnext/cascade-mask-rcnn_convnext-t-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py\n    Metadata:\n      Training Memory (GB): 9.0\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Mixed Precision Training\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ConvNeXt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 43.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/convnext/cascade_mask_rcnn_convnext-t_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco/cascade_mask_rcnn_convnext-t_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220509_204200-8f07c40b.pth\n    Paper:\n      URL: https://arxiv.org/abs/2201.03545\n      Title: 'A ConvNet for the 2020s'\n    README: configs/convnext/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/backbones/swin.py#L465\n      Version: v2.25.0\n\n  - Name: cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco\n    In Collection: Cascade Mask R-CNN\n    Config: configs/convnext/cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco.py\n    Metadata:\n      Training Memory (GB): 12.3\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Mixed Precision Training\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ConvNeXt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 51.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 44.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/convnext/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco/cascade_mask_rcnn_convnext-s_p4_w7_fpn_giou_4conv1f_fp16_ms-crop_3x_coco_20220510_201004-3d24f5a4.pth\n    Paper:\n      URL: https://arxiv.org/abs/2201.03545\n      Title: 'A ConvNet for the 2020s'\n    README: configs/convnext/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/backbones/swin.py#L465\n      Version: v2.25.0\n"
  },
  {
    "path": "configs/cornernet/README.md",
    "content": "# CornerNet\n\n> [Cornernet: Detecting objects as paired keypoints](https://arxiv.org/abs/1808.01244)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe propose CornerNet, a new approach to object detection where we detect an object bounding box as a pair of keypoints, the top-left corner and the bottom-right corner, using a single convolution neural network. By detecting objects as paired keypoints, we eliminate the need for designing a set of anchor boxes commonly used in prior single-stage detectors. In addition to our novel formulation, we introduce corner pooling, a new type of pooling layer that helps the network better localize corners. Experiments show that CornerNet achieves a 42.2% AP on MS COCO, outperforming all existing one-stage detectors.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143876061-4de20768-c812-4b97-b089-944d8db91ca2.png\"/>\n</div>\n\n## Results and Models\n\n|     Backbone     |                              Batch Size                              | Step/Total Epochs | Mem (GB) | Inf time (fps) | box AP |                                Config                                |                                                                                                                                                                                     Download                                                                                                                                                                                     |\n| :--------------: | :------------------------------------------------------------------: | :---------------: | :------: | :------------: | :----: | :------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HourglassNet-104 | [10 x 5](./cornernet_hourglass104_10xb5-crop511-210e-mstest_coco.py) |      180/210      |   13.9   |      4.2       |  41.2  | [config](./cornernet_hourglass104_10xb5-crop511-210e-mstest_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_10x5_210e_coco/cornernet_hourglass104_mstest_10x5_210e_coco_20200824_185720-5fefbf1c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_10x5_210e_coco/cornernet_hourglass104_mstest_10x5_210e_coco_20200824_185720.log.json) |\n| HourglassNet-104 |      [8 x 6](./cornernet_hourglass104_8xb6-210e-mstest_coco.py)      |      180/210      |   15.9   |      4.2       |  41.2  |     [config](./cornernet_hourglass104_8xb6-210e-mstest_coco.py)      |   [model](https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_8x6_210e_coco/cornernet_hourglass104_mstest_8x6_210e_coco_20200825_150618-79b44c30.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_8x6_210e_coco/cornernet_hourglass104_mstest_8x6_210e_coco_20200825_150618.log.json)   |\n| HourglassNet-104 |     [32 x 3](./cornernet_hourglass104_32xb3-210e-mstest_coco.py)     |      180/210      |   9.5    |      3.9       |  40.4  |     [config](./cornernet_hourglass104_32xb3-210e-mstest_coco.py)     | [model](https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_32x3_210e_coco/cornernet_hourglass104_mstest_32x3_210e_coco_20200819_203110-1efaea91.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_32x3_210e_coco/cornernet_hourglass104_mstest_32x3_210e_coco_20200819_203110.log.json) |\n\nNote:\n\n- TTA setting is single-scale and `flip=True`. If you want to reproduce the TTA performance, please add `--tta` in the test command.\n- Experiments with `images_per_gpu=6` are conducted on Tesla V100-SXM2-32GB, `images_per_gpu=3` are conducted on GeForce GTX 1080 Ti.\n- Here are the descriptions of each experiment setting:\n  - 10 x 5: 10 GPUs with 5 images per gpu. This is the same setting as that reported in the original paper.\n  - 8 x 6: 8 GPUs with 6 images per gpu. The total batchsize is similar to paper and only need 1 node to train.\n  - 32 x 3: 32 GPUs with 3 images per gpu. The default setting for 1080TI and need 4 nodes to train.\n\n## Citation\n\n```latex\n@inproceedings{law2018cornernet,\n  title={Cornernet: Detecting objects as paired keypoints},\n  author={Law, Hei and Deng, Jia},\n  booktitle={15th European Conference on Computer Vision, ECCV 2018},\n  pages={765--781},\n  year={2018},\n  organization={Springer Verlag}\n}\n```\n"
  },
  {
    "path": "configs/cornernet/cornernet_hourglass104_10xb5-crop511-210e-mstest_coco.py",
    "content": "_base_ = './cornernet_hourglass104_8xb6-210e-mstest_coco.py'\n\ntrain_dataloader = dict(batch_size=5)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (10 GPUs) x (5 samples per GPU)\nauto_scale_lr = dict(base_batch_size=50)\n"
  },
  {
    "path": "configs/cornernet/cornernet_hourglass104_32xb3-210e-mstest_coco.py",
    "content": "_base_ = './cornernet_hourglass104_8xb6-210e-mstest_coco.py'\n\ntrain_dataloader = dict(batch_size=3)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (3 samples per GPU)\nauto_scale_lr = dict(base_batch_size=96)\n"
  },
  {
    "path": "configs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py",
    "content": "_base_ = [\n    '../_base_/default_runtime.py', '../_base_/datasets/coco_detection.py'\n]\n\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True)\n\n# model settings\nmodel = dict(\n    type='CornerNet',\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type='HourglassNet',\n        downsample_times=5,\n        num_stacks=2,\n        stage_channels=[256, 256, 384, 384, 384, 512],\n        stage_blocks=[2, 2, 2, 2, 2, 4],\n        norm_cfg=dict(type='BN', requires_grad=True)),\n    neck=None,\n    bbox_head=dict(\n        type='CornerHead',\n        num_classes=80,\n        in_channels=256,\n        num_feat_levels=2,\n        corner_emb_channels=1,\n        loss_heatmap=dict(\n            type='GaussianFocalLoss', alpha=2.0, gamma=4.0, loss_weight=1),\n        loss_embedding=dict(\n            type='AssociativeEmbeddingLoss',\n            pull_weight=0.10,\n            push_weight=0.10),\n        loss_offset=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1)),\n    # training and testing settings\n    train_cfg=None,\n    test_cfg=dict(\n        corner_topk=100,\n        local_maximum_kernel=3,\n        distance_threshold=0.5,\n        score_thr=0.05,\n        max_per_img=100,\n        nms=dict(type='soft_nms', iou_threshold=0.5, method='gaussian')))\n\n# data settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(\n        # The cropped images are padded into squares during training,\n        # but may be smaller than crop_size.\n        type='RandomCenterCropPad',\n        crop_size=(511, 511),\n        ratios=(0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3),\n        test_mode=False,\n        test_pad_mode=None,\n        mean=data_preprocessor['mean'],\n        std=data_preprocessor['std'],\n        # Image data is not converted to rgb.\n        to_rgb=data_preprocessor['bgr_to_rgb']),\n    # Make sure the output is always crop_size.\n    dict(type='Resize', scale=(511, 511), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        to_float32=True,\n        backend_args=_base_.backend_args,\n    ),\n    # don't need Resize\n    dict(\n        type='RandomCenterCropPad',\n        crop_size=None,\n        ratios=None,\n        border=None,\n        test_mode=True,\n        test_pad_mode=['logical_or', 127],\n        mean=data_preprocessor['mean'],\n        std=data_preprocessor['std'],\n        # Image data is not converted to rgb.\n        to_rgb=data_preprocessor['bgr_to_rgb']),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'border'))\n]\n\ntrain_dataloader = dict(\n    batch_size=6,\n    num_workers=3,\n    batch_sampler=None,\n    dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='Adam', lr=0.0005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\nmax_epochs = 210\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[180],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (6 samples per GPU)\nauto_scale_lr = dict(base_batch_size=48)\n\ntta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(\n        nms=dict(type='soft_nms', iou_threshold=0.5, method='gaussian'),\n        max_per_img=100))\n\ntta_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        to_float32=True,\n        backend_args=_base_.backend_args),\n    dict(\n        type='TestTimeAug',\n        transforms=[\n            [\n                # ``RandomFlip`` must be placed before ``RandomCenterCropPad``,\n                # otherwise bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type='RandomFlip', prob=1.),\n                dict(type='RandomFlip', prob=0.)\n            ],\n            [\n                dict(\n                    type='RandomCenterCropPad',\n                    crop_size=None,\n                    ratios=None,\n                    border=None,\n                    test_mode=True,\n                    test_pad_mode=['logical_or', 127],\n                    mean=data_preprocessor['mean'],\n                    std=data_preprocessor['std'],\n                    # Image data is not converted to rgb.\n                    to_rgb=data_preprocessor['bgr_to_rgb'])\n            ],\n            [dict(type='LoadAnnotations', with_bbox=True)],\n            [\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'flip', 'flip_direction', 'border'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "configs/cornernet/metafile.yml",
    "content": "Collections:\n  - Name: CornerNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - Adam\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Corner Pooling\n        - Stacked Hourglass Network\n    Paper:\n      URL: https://arxiv.org/abs/1808.01244\n      Title: 'CornerNet: Detecting Objects as Paired Keypoints'\n    README: configs/cornernet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.3.0/mmdet/models/detectors/cornernet.py#L9\n      Version: v2.3.0\n\nModels:\n  - Name: cornernet_hourglass104_10xb5-crop511-210e-mstest_coco\n    In Collection: CornerNet\n    Config: configs/cornernet/cornernet_hourglass104_10xb5-crop511-210e-mstest_coco.py\n    Metadata:\n      Training Resources: 10x V100 GPUs\n      Batch Size: 50\n      Training Memory (GB): 13.9\n      inference time (ms/im):\n        - value: 238.1\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 210\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_10x5_210e_coco/cornernet_hourglass104_mstest_10x5_210e_coco_20200824_185720-5fefbf1c.pth\n\n  - Name: cornernet_hourglass104_8xb6-210e-mstest_coco\n    In Collection: CornerNet\n    Config: configs/cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py\n    Metadata:\n      Batch Size: 48\n      Training Memory (GB): 15.9\n      inference time (ms/im):\n        - value: 238.1\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 210\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_8x6_210e_coco/cornernet_hourglass104_mstest_8x6_210e_coco_20200825_150618-79b44c30.pth\n\n  - Name: cornernet_hourglass104_32xb3-210e-mstest_coco\n    In Collection: CornerNet\n    Config: configs/cornernet/cornernet_hourglass104_32xb3-210e-mstest_coco.py\n    Metadata:\n      Training Resources: 32x V100 GPUs\n      Batch Size: 96\n      Training Memory (GB): 9.5\n      inference time (ms/im):\n        - value: 256.41\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 210\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/cornernet/cornernet_hourglass104_mstest_32x3_210e_coco/cornernet_hourglass104_mstest_32x3_210e_coco_20200819_203110-1efaea91.pth\n"
  },
  {
    "path": "configs/crowddet/README.md",
    "content": "# CrowdDet\n\n> [Detection in Crowded Scenes: One Proposal, Multiple Predictions](https://arxiv.org/abs/2003.09163)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe propose a simple yet effective proposal-based object detector, aiming at detecting highly-overlapped instances in crowded scenes. The key of our approach is to let each proposal predict a set of correlated instances rather than a single one in previous proposal-based frameworks. Equipped with new techniques such as EMD Loss and Set NMS, our detector can effectively handle the difficulty of detecting highly overlapped objects. On a FPN-Res50 baseline, our detector can obtain 4.9% AP gains on challenging CrowdHuman dataset and 1.0% MR^−2 improvements on CityPersons dataset, without bells and whistles. Moreover, on less crowed datasets like COCO, our approach can still achieve moderate improvement, suggesting the proposed method is robust to crowdedness. Code and pre-trained models will be released at https://github.com/megvii-model/CrowdDetection.\n\n<div align=center>\n<img src=\"https://github.com/Purkialo/images/blob/master/CrowdDet_arch.jpg\"/>\n</div>\n\n## Results and Models\n\n| Backbone |  RM   |  Style  | Mem (GB) | Inf time (fps) | box AP |                             Config                              |                                                                                                                                                                                            Download                                                                                                                                                                                            |\n| :------: | :---: | :-----: | :------: | :------------: | :----: | :-------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN | False | pytorch |   4.4    |       -        |  90.0  |    [config](./crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman.py)     |               [model](https://download.openmmlab.com/mmdetection/v3.0/crowddet/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman_20221023_174954-dc319c2d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/crowddet/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman_20221023_174954.log.json)               |\n| R-50-FPN | True  | pytorch |   4.8    |       -        | 90.32  | [config](./crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/crowddet/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman_20221024_215917-45602806.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/crowddet/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman_20221024_215917.log.json) |\n\nNote:\n\n- RM indicates whether to use the refine module.\n- The dataset for training and testing this model is `CrowdHuman`, and the metric of `box AP` is calculated by `mmdet/evaluation/metrics/crowdhuman_metric.py`.\n\n## Citation\n\n```latex\n@inproceedings{Chu_2020_CVPR,\n  title={Detection in Crowded Scenes: One Proposal, Multiple Predictions},\n  author={Chu, Xuangeng and Zheng, Anlin and Zhang, Xiangyu and Sun, Jian},\n  booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},\n  month = {June},\n  year = {2020}\n}\n```\n"
  },
  {
    "path": "configs/crowddet/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman.py",
    "content": "_base_ = ['../_base_/default_runtime.py']\n\nmodel = dict(\n    type='CrowdDet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False,\n        pad_size_divisor=64,\n        # This option is set according to https://github.com/Purkialo/CrowdDet/\n        # blob/master/lib/data/CrowdHuman.py The images in the entire batch are\n        # resize together.\n        batch_augments=[\n            dict(type='BatchResize', scale=(1400, 800), pad_size_divisor=64)\n        ]),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5,\n        upsample_cfg=dict(mode='bilinear', align_corners=False)),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[1.0, 2.0, 3.0],\n            strides=[4, 8, 16, 32, 64],\n            centers=[(8, 8), (8, 8), (8, 8), (8, 8), (8, 8)]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[0.0, 0.0, 0.0, 0.0],\n            target_stds=[1.0, 1.0, 1.0, 1.0],\n            clip_border=False),\n        loss_cls=dict(type='CrossEntropyLoss', loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n    roi_head=dict(\n        type='MultiInstanceRoIHead',\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=7,\n                sampling_ratio=-1,\n                aligned=True,\n                use_torchvision=True),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            type='MultiInstanceBBoxHead',\n            with_refine=False,\n            num_shared_fcs=2,\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=1,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss',\n                loss_weight=1.0,\n                use_sigmoid=False,\n                reduction='none'),\n            loss_bbox=dict(\n                type='SmoothL1Loss', loss_weight=1.0, reduction='none'))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=(0.3, 0.7),\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2400,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=2),\n        rcnn=dict(\n            assigner=dict(\n                type='MultiInstanceAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.3,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='MultiInsRandomSampler',\n                num=512,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1200,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=2),\n        rcnn=dict(\n            nms=dict(type='nms', iou_threshold=0.5),\n            score_thr=0.01,\n            max_per_img=500)))\n\ndataset_type = 'CrowdHumanDataset'\ndata_root = 'data/CrowdHuman/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/tracking/CrowdHuman/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/tracking/',\n#         'data/': 's3://openmmlab/datasets/tracking/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip',\n                   'flip_direction'))\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1400, 800), keep_ratio=True),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=4,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=None,  # The 'batch_sampler' may decrease the precision\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotation_train.odgt',\n        data_prefix=dict(img='Images/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotation_val.odgt',\n        data_prefix=dict(img='Images/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CrowdHumanMetric',\n    ann_file=data_root + 'annotation_val.odgt',\n    metric=['AP', 'MR', 'JI'],\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=30, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=800),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=30,\n        by_epoch=True,\n        milestones=[24, 27],\n        gamma=0.1)\n]\n\n# optimizer\nauto_scale_lr = dict(base_batch_size=16)\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.002, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/crowddet/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman.py",
    "content": "_base_ = './crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman.py'\n\nmodel = dict(roi_head=dict(bbox_head=dict(with_refine=True)))\n"
  },
  {
    "path": "configs/crowddet/metafile.yml",
    "content": "Collections:\n  - Name: CrowdDet\n    Metadata:\n      Training Data: CrowdHuman\n      Training Techniques:\n        - SGD\n        - EMD Loss\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - FPN\n        - RPN\n        - ResNet\n        - RoIPool\n    Paper:\n      URL: https://arxiv.org/abs/2003.09163\n      Title: 'Detection in Crowded Scenes: One Proposal, Multiple Predictions'\n    README: configs/crowddet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v3.0.0rc3/mmdet/models/detectors/crowddet.py\n      Version: v3.0.0rc3\n\nModels:\n  - Name: crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman\n    In Collection: CrowdDet\n    Config: configs/crowddet/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman.py\n    Metadata:\n      Training Memory (GB): 4.8\n      Epochs: 30\n    Results:\n      - Task: Object Detection\n        Dataset: CrowdHuman\n        Metrics:\n          box AP: 90.32\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/crowddet/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman/crowddet-rcnn_refine_r50_fpn_8xb2-30e_crowdhuman_20221024_215917-45602806.pth\n\n  - Name: crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman\n    In Collection: CrowdDet\n    Config: configs/crowddet/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman.py\n    Metadata:\n      Training Memory (GB): 4.4\n      Epochs: 30\n    Results:\n      - Task: Object Detection\n        Dataset: CrowdHuman\n        Metrics:\n          box AP: 90.0\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/crowddet/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman/crowddet-rcnn_r50_fpn_8xb2-30e_crowdhuman_20221023_174954-dc319c2d.pth\n"
  },
  {
    "path": "configs/dab_detr/README.md",
    "content": "# DAB-DETR\n\n> [DAB-DETR: Dynamic Anchor Boxes are Better Queries for DETR](https://arxiv.org/abs/2201.12329)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present in this paper a novel query formulation using dynamic anchor boxes for DETR (DEtection TRansformer) and offer a deeper understanding of the role of queries in DETR. This new formulation directly uses box coordinates as queries in Transformer decoders and dynamically updates them layer-by-layer. Using box coordinates not only helps using explicit positional priors to improve the query-to-feature similarity and eliminate the slow training convergence issue in DETR, but also allows us to modulate the positional attention map using the box width and height information. Such a design makes it clear that queries in DETR can be implemented as performing soft ROI pooling layer-by-layer in a cascade manner. As a result, it leads to the best performance on MS-COCO benchmark among the DETR-like detection models under the same setting, e.g., AP 45.7% using ResNet50-DC5 as backbone trained in 50 epochs. We also conducted extensive experiments to confirm our analysis and verify the effectiveness of our methods.\n\n<div align=center>\n<img src=\"https://github.com/IDEA-Research/DAB-DETR/blob/main/figure/arch.png?raw=true\"/>\n</div>\n<div align=center>\n<img src=\"https://github.com/IDEA-Research/DAB-DETR/blob/main/figure/model.png?raw=true\"/>\n</div>\n<div align=center>\n<img src=\"https://github.com/IDEA-Research/DAB-DETR/blob/main/figure/results.png?raw=true\"/>\n</div>\n\n## Results and Models\n\nWe provide the config files and models for DAB-DETR: [DAB-DETR: Dynamic Anchor Boxes are Better Queries for DETR](https://arxiv.org/abs/2201.12329).\n\n| Backbone |  Model   | Lr schd | Mem (GB) | Inf time (fps) | box AP |                  Config                   |                                                                                                                                                Download                                                                                                                                                |\n| :------: | :------: | :-----: | :------: | :------------: | :----: | :---------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | DAB-DETR |   50e   |          |                |  42.3  | [config](./dab-detr_r50_8xb2-50e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/dab_detr/dab-detr_r50_8xb2-50e_coco/dab-detr_r50_8xb2-50e_coco_20221122_120837-c1035c8c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/dab_detr/dab-detr_r50_8xb2-50e_coco/dab-detr_r50_8xb2-50e_coco_20221122_120837.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{\n  liu2022dabdetr,\n  title={{DAB}-{DETR}: Dynamic Anchor Boxes are Better Queries for {DETR}},\n  author={Shilong Liu and Feng Li and Hao Zhang and Xiao Yang and Xianbiao Qi and Hang Su and Jun Zhu and Lei Zhang},\n  booktitle={International Conference on Learning Representations},\n  year={2022},\n  url={https://openreview.net/forum?id=oMI9PjOb9Jl}\n}\n```\n"
  },
  {
    "path": "configs/dab_detr/dab-detr_r50_8xb2-50e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='DABDETR',\n    num_queries=300,\n    with_random_refpoints=False,\n    num_patterns=0,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(3, ),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=None,\n        num_outs=1),\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(\n                embed_dims=256, num_heads=8, dropout=0., batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,\n                num_fcs=2,\n                ffn_drop=0.,\n                act_cfg=dict(type='PReLU')))),\n    decoder=dict(\n        num_layers=6,\n        query_dim=4,\n        query_scale_type='cond_elewise',\n        with_modulated_hw_attn=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(\n                embed_dims=256,\n                num_heads=8,\n                attn_drop=0.,\n                proj_drop=0.,\n                cross_attn=False),\n            cross_attn_cfg=dict(\n                embed_dims=256,\n                num_heads=8,\n                attn_drop=0.,\n                proj_drop=0.,\n                cross_attn=True),\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,\n                num_fcs=2,\n                ffn_drop=0.,\n                act_cfg=dict(type='PReLU'))),\n        return_intermediate=True),\n    positional_encoding=dict(num_feats=128, temperature=20, normalize=True),\n    bbox_head=dict(\n        type='DABDETRHead',\n        num_classes=80,\n        embed_dims=256,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2., eps=1e-8),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={'backbone': dict(lr_mult=0.1, decay_mult=1.0)}))\n\n# learning policy\nmax_epochs = 50\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[40],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16, enable=False)\n"
  },
  {
    "path": "configs/dab_detr/metafile.yml",
    "content": "Collections:\n  - Name: DAB-DETR\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ResNet\n        - Transformer\n    Paper:\n      URL: https://arxiv.org/abs/2201.12329\n      Title: 'DAB-DETR: Dynamic Anchor Boxes are Better Queries for DETR'\n    README: configs/dab_detr/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/f4112c9e5611468ffbd57cfba548fd1289264b52/mmdet/models/detectors/dab_detr.py#L15\n      Version: v3.0.0rc6\n\nModels:\n  - Name: dab-detr_r50_8xb2-50e_coco\n    In Collection: DAB-DETR\n    Config: configs/dab_detr/dab-detr_r50_8xb2-50e_coco.py\n    Metadata:\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/dab_detr/dab-detr_r50_8xb2-50e_coco/dab-detr_r50_8xb2-50e_coco_20221122_120837-c1035c8c.pth\n"
  },
  {
    "path": "configs/dcn/README.md",
    "content": "# DCN\n\n> [Deformable Convolutional Networks](https://arxiv.org/abs/1703.06211)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nConvolutional neural networks (CNNs) are inherently limited to model geometric transformations due to the fixed geometric structures in its building modules. In this work, we introduce two new modules to enhance the transformation modeling capacity of CNNs, namely, deformable convolution and deformable RoI pooling. Both are based on the idea of augmenting the spatial sampling locations in the modules with additional offsets and learning the offsets from target tasks, without additional supervision. The new modules can readily replace their plain counterparts in existing CNNs and can be easily trained end-to-end by standard back-propagation, giving rise to deformable convolutional networks. Extensive experiments validate the effectiveness of our approach on sophisticated vision tasks of object detection and semantic segmentation.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143876246-c4985e25-e286-4511-9b7c-97af2857461e.png\"/>\n</div>\n\n## Results and Models\n\n|    Backbone     |    Model     |  Style  |     Conv     | Pool  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                               Config                                |                                                                                                                                                                                       Download                                                                                                                                                                                       |\n| :-------------: | :----------: | :-----: | :----------: | :---: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     |    Faster    | pytorch | dconv(c3-c5) |   -   |   1x    |   4.0    |      17.8      |  41.3  |         |       [config](./faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py)        |                      [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130-d68aed1e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130_212941.log.json)                       |\n|    R-50-FPN     |    Faster    | pytorch |      -       | dpool |   1x    |   5.0    |      17.2      |  38.9  |         |          [config](./faster-rcnn_r50_fpn_dpool_1x_coco.py)           |                                  [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_dpool_1x_coco/faster_rcnn_r50_fpn_dpool_1x_coco_20200307-90d3c01d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_dpool_1x_coco/faster_rcnn_r50_fpn_dpool_1x_coco_20200307_203250.log.json)                                   |\n|    R-101-FPN    |    Faster    | pytorch | dconv(c3-c5) |   -   |   1x    |   6.0    |      12.5      |  42.7  |         |       [config](./faster-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py)       |                    [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r101_fpn_dconv_c3-c5_1x_coco/faster_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200203-1377f13d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r101_fpn_dconv_c3-c5_1x_coco/faster_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200203_230019.log.json)                     |\n| X-101-32x4d-FPN |    Faster    | pytorch | dconv(c3-c5) |   -   |   1x    |   7.3    |      10.0      |  44.5  |         |    [config](./faster-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py)    |        [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco/faster_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco_20200203-4f85c69c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco/faster_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco_20200203_001325.log.json)         |\n|    R-50-FPN     |     Mask     | pytorch | dconv(c3-c5) |   -   |   1x    |   4.5    |      15.4      |  41.8  |  37.4   |        [config](./mask-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py)         |                          [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200203-4d9ad43b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200203_061339.log.json)                           |\n|    R-101-FPN    |     Mask     | pytorch | dconv(c3-c5) |   -   |   1x    |   6.5    |      11.7      |  43.5  |  38.9   |        [config](./mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py)        |                        [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco/mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200216-a71f5bce.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco/mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200216_191601.log.json)                         |\n|    R-50-FPN     |   Cascade    | pytorch | dconv(c3-c5) |   -   |   1x    |   4.5    |      14.6      |  43.8  |         |       [config](./cascade-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py)       |                    [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_rcnn_r50_fpn_dconv_c3-c5_1x_coco/cascade_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130-2f1fca44.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_rcnn_r50_fpn_dconv_c3-c5_1x_coco/cascade_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130_220843.log.json)                     |\n|    R-101-FPN    |   Cascade    | pytorch | dconv(c3-c5) |   -   |   1x    |   6.4    |      11.0      |  45.0  |         |      [config](./cascade-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py)       |                  [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_rcnn_r101_fpn_dconv_c3-c5_1x_coco/cascade_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200203-3b2f0594.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_rcnn_r101_fpn_dconv_c3-c5_1x_coco/cascade_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200203_224829.log.json)                   |\n|    R-50-FPN     | Cascade Mask | pytorch | dconv(c3-c5) |   -   |   1x    |   6.0    |      10.0      |  44.4  |  38.6   |    [config](./cascade-mask-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py)     |          [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200202-42e767a2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200202_010309.log.json)           |\n|    R-101-FPN    | Cascade Mask | pytorch | dconv(c3-c5) |   -   |   1x    |   8.0    |      8.6       |  45.8  |  39.7   |    [config](./cascade-mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py)    |        [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200204-df0c5f10.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200204_134006.log.json)         |\n| X-101-32x4d-FPN | Cascade Mask | pytorch | dconv(c3-c5) |   -   |   1x    |   9.2    |                |  47.3  |  41.1   | [config](./cascade-mask-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco-e75f90c8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco-20200606_183737.log.json) |\n| R-50-FPN (FP16) |     Mask     | pytorch | dconv(c3-c5) |   -   |   1x    |   3.0    |                |  41.9  |  37.5   |      [config](./mask-rcnn_r50-dconv-c3-c5_fpn_amp-1x_coco.py)       |            [model](https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_dconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_fp16_dconv_c3-c5_1x_coco_20210520_180247-c06429d2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_dconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_fp16_dconv_c3-c5_1x_coco_20210520_180247.log.json)            |\n\n**Notes:**\n\n- `dconv` denotes deformable convolution, `c3-c5` means adding dconv in resnet stage 3 to 5. `dpool` denotes deformable roi pooling.\n- The dcn ops are modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch, which should be more memory efficient and slightly faster.\n- (\\*) For R-50-FPN (dg=4), dg is short for deformable_group. This model is trained and tested on Amazon EC2 p3dn.24xlarge instance.\n- **Memory, Train/Inf time is outdated.**\n\n## Citation\n\n```latex\n@inproceedings{dai2017deformable,\n  title={Deformable Convolutional Networks},\n  author={Dai, Jifeng and Qi, Haozhi and Xiong, Yuwen and Li, Yi and Zhang, Guodong and Hu, Han and Wei, Yichen},\n  booktitle={Proceedings of the IEEE international conference on computer vision},\n  year={2017}\n}\n```\n"
  },
  {
    "path": "configs/dcn/cascade-mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/cascade-mask-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/cascade-mask-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/cascade-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/cascade-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/faster-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/faster-rcnn_r50_fpn_dpool_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                _delete_=True,\n                type='DeformRoIPoolPack',\n                output_size=7,\n                output_channels=256),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32])))\n"
  },
  {
    "path": "configs/dcn/faster-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/dcn/mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/mask-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcn/mask-rcnn_r50-dconv-c3-c5_fpn_amp-1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# optim_wrapper = dict(type='AmpOptimWrapper')\n_base_.optim_wrapper.type = 'AmpOptimWrapper'\n"
  },
  {
    "path": "configs/dcn/metafile.yml",
    "content": "Collections:\n  - Name: Deformable Convolutional Networks\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Deformable Convolution\n    Paper:\n      URL: https://arxiv.org/abs/1703.06211\n      Title: \"Deformable Convolutional Networks\"\n    README: configs/dcn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/ops/dcn/deform_conv.py#L15\n      Version: v2.0.0\n\nModels:\n  - Name: faster-rcnn_r50_fpn_dconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/faster-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.0\n      inference time (ms/im):\n        - value: 56.18\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco/faster_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130-d68aed1e.pth\n\n  - Name: faster-rcnn_r50_fpn_dpool_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/faster-rcnn_r50_fpn_dpool_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      inference time (ms/im):\n        - value: 58.14\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_dpool_1x_coco/faster_rcnn_r50_fpn_dpool_1x_coco_20200307-90d3c01d.pth\n\n  - Name: faster-rcnn_r101-dconv-c3-c5_fpn_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/faster-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 80\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r101_fpn_dconv_c3-c5_1x_coco/faster_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200203-1377f13d.pth\n\n  - Name: faster-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/faster-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.3\n      inference time (ms/im):\n        - value: 100\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco/faster_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco_20200203-4f85c69c.pth\n\n  - Name: mask-rcnn_r50_fpn_dconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/mask-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.5\n      inference time (ms/im):\n        - value: 64.94\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200203-4d9ad43b.pth\n\n  - Name: mask-rcnn_r50_fpn_fp16_dconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/mask-rcnn_r50-dconv-c3-c5_fpn_amp-1x_coco.py\n    Metadata:\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n        - Mixed Precision Training\n      Training Memory (GB): 3.0\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_dconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_fp16_dconv_c3-c5_1x_coco_20210520_180247-c06429d2.pth\n\n  - Name: mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.5\n      inference time (ms/im):\n        - value: 85.47\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco/mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200216-a71f5bce.pth\n\n  - Name: cascade-rcnn_r50_fpn_dconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/cascade-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.5\n      inference time (ms/im):\n        - value: 68.49\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_rcnn_r50_fpn_dconv_c3-c5_1x_coco/cascade_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200130-2f1fca44.pth\n\n  - Name: cascade-rcnn_r101-dconv-c3-c5_fpn_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/cascade-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      inference time (ms/im):\n        - value: 90.91\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_rcnn_r101_fpn_dconv_c3-c5_1x_coco/cascade_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200203-3b2f0594.pth\n\n  - Name: cascade-mask-rcnn_r50_fpn_dconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/cascade-mask-rcnn_r50-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 100\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_r50_fpn_dconv_c3-c5_1x_coco_20200202-42e767a2.pth\n\n  - Name: cascade-mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/cascade-mask-rcnn_r101-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.0\n      inference time (ms/im):\n        - value: 116.28\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_r101_fpn_dconv_c3-c5_1x_coco_20200204-df0c5f10.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco\n    In Collection: Deformable Convolutional Networks\n    Config: configs/dcn/cascade-mask-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.2\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/cascade_mask_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_dconv_c3-c5_1x_coco-e75f90c8.pth\n"
  },
  {
    "path": "configs/dcnv2/README.md",
    "content": "# DCNv2\n\n> [Deformable ConvNets v2: More Deformable, Better Results](https://arxiv.org/abs/1811.11168)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe superior performance of Deformable Convolutional Networks arises from its ability to adapt to the geometric variations of objects. Through an examination of its adaptive behavior, we observe that while the spatial support for its neural features conforms more closely than regular ConvNets to object structure, this support may nevertheless extend well beyond the region of interest, causing features to be influenced by irrelevant image content. To address this problem, we present a reformulation of Deformable ConvNets that improves its ability to focus on pertinent image regions, through increased modeling power and stronger training. The modeling power is enhanced through a more comprehensive integration of deformable convolution within the network, and by introducing a modulation mechanism that expands the scope of deformation modeling. To effectively harness this enriched modeling capability, we guide network training via a proposed feature mimicking scheme that helps the network to learn features that reflect the object focus and classification power of RCNN features. With the proposed contributions, this new version of Deformable ConvNets yields significant performance gains over the original model and produces leading results on the COCO benchmark for object detection and instance segmentation.\n\n## Results and Models\n\n|     Backbone      | Model  |  Style  |     Conv      |  Pool  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                             Config                             |                                                                                                                                                                                 Download                                                                                                                                                                                  |\n| :---------------: | :----: | :-----: | :-----------: | :----: | :-----: | :------: | :------------: | :----: | :-----: | :------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     R-50-FPN      | Faster | pytorch | mdconv(c3-c5) |   -    |   1x    |   4.1    |      17.6      |  41.4  |         |    [config](./faster-rcnn_r50-mdconv-c3-c5_fpn_1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdconv_c3-c5_1x_coco/faster_rcnn_r50_fpn_mdconv_c3-c5_1x_coco_20200130-d099253b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdconv_c3-c5_1x_coco/faster_rcnn_r50_fpn_mdconv_c3-c5_1x_coco_20200130_222144.log.json)               |\n| \\*R-50-FPN (dg=4) | Faster | pytorch | mdconv(c3-c5) |   -    |   1x    |   4.2    |      17.4      |  41.5  |         | [config](./faster-rcnn_r50-mdconv-group4-c3-c5_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdconv_c3-c5_group4_1x_coco/faster_rcnn_r50_fpn_mdconv_c3-c5_group4_1x_coco_20200130-01262257.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdconv_c3-c5_group4_1x_coco/faster_rcnn_r50_fpn_mdconv_c3-c5_group4_1x_coco_20200130_222058.log.json) |\n|     R-50-FPN      | Faster | pytorch |       -       | mdpool |   1x    |   5.8    |      16.6      |  38.7  |         |       [config](./faster-rcnn_r50_fpn_mdpool_1x_coco.py)        |                           [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdpool_1x_coco/faster_rcnn_r50_fpn_mdpool_1x_coco_20200307-c0df27ff.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdpool_1x_coco/faster_rcnn_r50_fpn_mdpool_1x_coco_20200307_203304.log.json)                           |\n|     R-50-FPN      |  Mask  | pytorch | mdconv(c3-c5) |   -    |   1x    |   4.5    |      15.1      |  41.5  |  37.1   |     [config](./mask-rcnn_r50-mdconv-c3-c5_fpn_1x_coco.py)      |                   [model](https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r50_fpn_mdconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_mdconv_c3-c5_1x_coco_20200203-ad97591f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r50_fpn_mdconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_mdconv_c3-c5_1x_coco_20200203_063443.log.json)                   |\n|  R-50-FPN (FP16)  |  Mask  | pytorch | mdconv(c3-c5) |   -    |   1x    |   3.1    |                |  42.0  |  37.6   |   [config](./mask-rcnn_r50-mdconv-c3-c5_fpn_amp-1x_coco.py)    |    [model](https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_mdconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_fp16_mdconv_c3-c5_1x_coco_20210520_180434-cf8fefa5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_mdconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_fp16_mdconv_c3-c5_1x_coco_20210520_180434.log.json)     |\n\n**Notes:**\n\n- `mdconv` denotes modulated deformable convolution, `c3-c5` means adding dconv in resnet stage 3 to 5. `mdpool` denotes modulated deformable roi pooling.\n- The dcn ops are modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch, which should be more memory efficient and slightly faster.\n- (\\*) For R-50-FPN (dg=4), dg is short for deformable_group. This model is trained and tested on Amazon EC2 p3dn.24xlarge instance.\n- **Memory, Train/Inf time is outdated.**\n\n## Citation\n\n```latex\n@article{zhu2018deformable,\n  title={Deformable ConvNets v2: More Deformable, Better Results},\n  author={Zhu, Xizhou and Hu, Han and Lin, Stephen and Dai, Jifeng},\n  journal={arXiv preprint arXiv:1811.11168},\n  year={2018}\n}\n```\n"
  },
  {
    "path": "configs/dcnv2/faster-rcnn_r50-mdconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcnv2/faster-rcnn_r50-mdconv-group4-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deform_groups=4, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                _delete_=True,\n                type='ModulatedDeformRoIPoolPack',\n                output_size=7,\n                output_channels=256),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32])))\n"
  },
  {
    "path": "configs/dcnv2/mask-rcnn_r50-mdconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/dcnv2/mask-rcnn_r50-mdconv-c3-c5_fpn_amp-1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# optim_wrapper = dict(type='AmpOptimWrapper')\n_base_.optim_wrapper.type = 'AmpOptimWrapper'\n"
  },
  {
    "path": "configs/dcnv2/metafile.yml",
    "content": "Collections:\n  - Name: Deformable Convolutional Networks v2\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Deformable Convolution\n    Paper:\n      URL: https://arxiv.org/abs/1811.11168\n      Title: \"Deformable ConvNets v2: More Deformable, Better Results\"\n    README: configs/dcnv2/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/ops/dcn/deform_conv.py#L15\n      Version: v2.0.0\n\nModels:\n  - Name: faster-rcnn_r50_fpn_mdconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks v2\n    Config: configs/dcnv2/faster-rcnn_r50-mdconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.1\n      inference time (ms/im):\n        - value: 56.82\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdconv_c3-c5_1x_coco/faster_rcnn_r50_fpn_mdconv_c3-c5_1x_coco_20200130-d099253b.pth\n\n  - Name: faster-rcnn_r50_fpn_mdconv_c3-c5_group4_1x_coco\n    In Collection: Deformable Convolutional Networks v2\n    Config: configs/dcnv2/faster-rcnn_r50-mdconv-group4-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.2\n      inference time (ms/im):\n        - value: 57.47\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdconv_c3-c5_group4_1x_coco/faster_rcnn_r50_fpn_mdconv_c3-c5_group4_1x_coco_20200130-01262257.pth\n\n  - Name: faster-rcnn_r50_fpn_mdpool_1x_coco\n    In Collection: Deformable Convolutional Networks v2\n    Config: configs/dcnv2/faster-rcnn_r50_fpn_mdpool_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.8\n      inference time (ms/im):\n        - value: 60.24\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/faster_rcnn_r50_fpn_mdpool_1x_coco/faster_rcnn_r50_fpn_mdpool_1x_coco_20200307-c0df27ff.pth\n\n  - Name: mask-rcnn_r50_fpn_mdconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks v2\n    Config: configs/dcnv2/mask-rcnn_r50-mdconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.5\n      inference time (ms/im):\n        - value: 66.23\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dcn/mask_rcnn_r50_fpn_mdconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_mdconv_c3-c5_1x_coco_20200203-ad97591f.pth\n\n  - Name: mask-rcnn_r50_fpn_fp16_mdconv_c3-c5_1x_coco\n    In Collection: Deformable Convolutional Networks v2\n    Config: configs/dcnv2/mask-rcnn_r50-mdconv-c3-c5_fpn_amp-1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.1\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n        - Mixed Precision Training\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_mdconv_c3-c5_1x_coco/mask_rcnn_r50_fpn_fp16_mdconv_c3-c5_1x_coco_20210520_180434-cf8fefa5.pth\n"
  },
  {
    "path": "configs/ddod/README.md",
    "content": "# DDOD\n\n> [Disentangle Your Dense Object Detector](https://arxiv.org/pdf/2107.02963.pdf)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nDeep learning-based dense object detectors have achieved great success in the past few years and have been applied to numerous multimedia applications such as video understanding. However, the current training pipeline for dense detectors is compromised to lots of conjunctions that may not hold. In this paper, we investigate three such important conjunctions: 1) only samples assigned as positive in classification head are used to train the regression head; 2) classification and regression share the same input feature and computational fields defined by the parallel head architecture; and 3) samples distributed in different feature pyramid layers are treated equally when computing the loss. We first carry out a series of pilot experiments to show disentangling such conjunctions can lead to persistent performance improvement. Then, based on these findings, we propose Disentangled Dense Object Detector(DDOD), in which simple and effective disentanglement mechanisms are designed and integrated into the current state-of-the-art dense object detectors. Extensive experiments on MS COCO benchmark show that our approach can lead to 2.0 mAP, 2.4 mAP and 2.2 mAP absolute improvements on RetinaNet, FCOS, and ATSS baselines with negligible extra overhead. Notably, our best model reaches 55.0 mAP on the COCO test-dev set and 93.5 AP on the hard subset of WIDER FACE, achieving new state-of-the-art performance on these two competitive benchmarks. Code is available at https://github.com/zehuichen123/DDOD.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/159212920-2e99d433-82c9-46cf-8f3a-32fdf3c566f5.png\"/>\n</div>\n\n## Results and Models\n\n|   Model   | Backbone |  Style  | Lr schd | Mem (GB) | box AP |               Config                |                                                                                                                                Download                                                                                                                                |\n| :-------: | :------: | :-----: | :-----: | :------: | :----: | :---------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| DDOD-ATSS |   R-50   | pytorch |   1x    |   3.4    |  41.7  | [config](./ddod_r50_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ddod/ddod_r50_fpn_1x_coco/ddod_r50_fpn_1x_coco_20220523_223737-29b2fc67.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ddod/ddod_r50_fpn_1x_coco/ddod_r50_fpn_1x_coco_20220523_223737.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{chen2021disentangle,\ntitle={Disentangle Your Dense Object Detector},\nauthor={Chen, Zehui and Yang, Chenhongyi and Li, Qiaofei and Zhao, Feng and Zha, Zheng-Jun and Wu, Feng},\nbooktitle={Proceedings of the 29th ACM International Conference on Multimedia},\npages={4939--4948},\nyear={2021}\n}\n```\n"
  },
  {
    "path": "configs/ddod/ddod_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    type='DDOD',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='DDODHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_iou=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    train_cfg=dict(\n        # assigner is mean cls_assigner\n        assigner=dict(type='ATSSAssigner', topk=9, alpha=0.8),\n        reg_assigner=dict(type='ATSSAssigner', topk=9, alpha=0.5),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/ddod/metafile.yml",
    "content": "Collections:\n  - Name: DDOD\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - DDOD\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/pdf/2107.02963.pdf\n      Title: 'Disentangle Your Dense Object Detector'\n    README: configs/ddod/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.25.0/mmdet/models/detectors/ddod.py#L6\n      Version: v2.25.0\n\nModels:\n  - Name: ddod_r50_fpn_1x_coco\n    In Collection: DDOD\n    Config: configs/ddod/ddod_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.4\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ddod/ddod_r50_fpn_1x_coco/ddod_r50_fpn_1x_coco_20220523_223737-29b2fc67.pth\n"
  },
  {
    "path": "configs/ddq/README.md",
    "content": "# DDQ\n\n> [Dense Distinct Query for End-to-End Object Detection](https://arxiv.org/abs/2303.12776)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nOne-to-one label assignment in object detection has successfully obviated the need for non-maximum suppression (NMS) as postprocessing and makes the pipeline end-to-end. However, it triggers a new dilemma as the widely used sparse queries cannot guarantee a high recall, while dense queries inevitably bring more similar queries and encounter optimization difficulties. As both sparse and dense queries are problematic, then what are the expected queries in end-to-end object detection? This paper shows that the solution should be Dense Distinct Queries (DDQ). Concretely, we first lay dense queries like traditional detectors and then select distinct ones for one-to-one assignments. DDQ blends the advantages of traditional and recent end-to-end detectors and significantly improves the performance of various detectors including FCN, R-CNN, and DETRs. Most impressively, DDQ-DETR achieves 52.1 AP on MS-COCO dataset within 12 epochs using a ResNet-50 backbone, outperforming all existing detectors in the same setting. DDQ also shares the benefit of end-to-end detectors in crowded scenes and achieves 93.8 AP on CrowdHuman. We hope DDQ can inspire researchers to consider the complementarity between traditional methods and end-to-end detectors.\n\n![ddq_arch](https://github.com/open-mmlab/mmdetection/assets/33146359/5ca9f11b-b6f3-454f-a2d1-3009ee337bbc)\n\n## Results and Models\n\n|       Model       | Backbone | Lr schd | Augmentation | box AP(val) |                       Config                       |                                                                                                                                                         Download                                                                                                                                                         |\n| :---------------: | :------: | :-----: | :----------: | :---------: | :------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  DDQ DETR-4scale  |   R-50   |   12e   |     DETR     |    51.4     |  [config](./ddq-detr-4scale_r50_8xb2-12e_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq-detr-4scale_r50_8xb2-12e_coco/ddq-detr-4scale_r50_8xb2-12e_coco_20230809_170711-42528127.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq-detr-4scale_r50_8xb2-12e_coco/ddq-detr-4scale_r50_8xb2-12e_coco_20230809_170711.log.json) |\n| DDQ DETR-5scale\\* |   R-50   |   12e   |     DETR     |    52.1     |  [config](./ddq-detr-5scale_r50_8xb2-12e_coco.py)  |                                                            [model](https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq_detr_5scale_coco_1x.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq_detr_5scale_coco_1x_20230319_103307.log)                                                            |\n| DDQ DETR-4scale\\* |  Swin-L  |   30e   |     DETR     |    58.7     | [config](./ddq-detr-4scale_swinl_8xb2-30e_coco.py) |                                                         [model](https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq_detr_swinl_30e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq_detr_swinl_30e_20230316_221721_20230318_143554.log)                                                         |\n\n**Note**\n\n- Models labeled * are not trained by us, but from [DDQ official website](https://github.com/jshilong/DDQ).\n- We find that the performance is unstable and may fluctuate by about 0.2 mAP.\n\n## Citation\n\n```latex\n@InProceedings{Zhang_2023_CVPR,\n    author    = {Zhang, Shilong and Wang, Xinjiang and Wang, Jiaqi and Pang, Jiangmiao and Lyu, Chengqi and Zhang, Wenwei and Luo, Ping and Chen, Kai},\n    title     = {Dense Distinct Query for End-to-End Object Detection},\n    booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},\n    month     = {June},\n    year      = {2023},\n    pages     = {7329-7338}\n}\n```\n"
  },
  {
    "path": "configs/ddq/ddq-detr-4scale_r50_8xb2-12e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='DDQDETR',\n    num_queries=900,  # num_matching_queries\n    # ratio of num_dense queries to num_queries\n    dense_topk_ratio=1.5,\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    # encoder class name: DeformableDetrTransformerEncoder\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0))),  # 0.1 for DeformDETR\n    # decoder class name: DDQTransformerDecoder\n    decoder=dict(\n        # `num_layers` >= 2, because attention masks of the last\n        #   `num_layers` - 1 layers are used for distinct query selection\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=4,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128,\n        normalize=True,\n        offset=0.0,  # -0.5 for DeformDETR\n        temperature=20),  # 10000 for DeformDETR\n    bbox_head=dict(\n        type='DDQDETRHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,\n        group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),\n    dqs_cfg=dict(type='nms', iou_threshold=0.8),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.05),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.1)}))\n\n# learning policy\nmax_epochs = 12\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=2000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/ddq/ddq-detr-4scale_swinl_8xb2-30e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa: E501\nmodel = dict(\n    type='DDQDETR',\n    num_queries=900,  # num_matching_queries\n    # ratio of num_dense queries to num_queries\n    dense_topk_ratio=1.5,\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[384, 768, 1536],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    # encoder class name: DeformableDetrTransformerEncoder\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0))),  # 0.1 for DeformDETR\n    # decoder class name: DDQTransformerDecoder\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=4,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128,\n        normalize=True,\n        offset=0.0,  # -0.5 for DeformDETR\n        temperature=20),  # 10000 for DeformDETR\n    bbox_head=dict(\n        type='DDQDETRHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,\n        group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),\n    dqs_cfg=dict(type='nms', iou_threshold=0.8),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.05),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.05)}))\n\n# learning policy\nmax_epochs = 30\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=2000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20, 26],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/ddq/ddq-detr-5scale_r50_8xb2-12e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='DDQDETR',\n    num_queries=900,  # num_matching_queries\n    # ratio of num_dense queries to num_queries\n    dense_topk_ratio=1.5,\n    with_box_refine=True,\n    as_two_stage=True,\n    num_feature_levels=5,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[256, 512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=5),\n    # encoder class name: DeformableDetrTransformerEncoder\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=5,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0))),  # 0.1 for DeformDETR\n    # decoder class name: DDQTransformerDecoder\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=5,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128,\n        normalize=True,\n        offset=0.0,  # -0.5 for DeformDETR\n        temperature=20),  # 10000 for DeformDETR\n    bbox_head=dict(\n        type='DDQDETRHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,\n        group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),\n    dqs_cfg=dict(type='nms', iou_threshold=0.8),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.05),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.1)}))\n\n# learning policy\nmax_epochs = 12\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=2000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/ddq/metafile.yml",
    "content": "Collections:\n  - Name: DDQ\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ResNet\n        - Transformer\n    Paper:\n      URL: https://arxiv.org/abs/2303.12776\n      Title: 'Dense Distinct Query for End-to-End Object Detection'\n    README: configs/ddq/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/dev-3.x/mmdet/models/detectors/ddq_detr.py#L21\n      Version: dev-3.x\n\nModels:\n  - Name: ddq-detr-4scale_r50_8xb2-12e_coco\n    In Collection: DDQ\n    Config: configs/ddq/ddq-detr-4scale_r50_8xb2-12e_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 51.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq-detr-4scale_r50_8xb2-12e_coco/ddq-detr-4scale_r50_8xb2-12e_coco_20230809_170711-42528127.pth\n\n  - Name: ddq-detr-5scale_r50_8xb2-12e_coco\n    In Collection: DDQ\n    Config: configs/dino/ddq-detr-5scale_r50_8xb2-12e_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 52.1\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq_detr_5scale_coco_1x.pth\n\n  - Name: ddq-detr-4scale_swinl_8xb2-30e_coco\n    In Collection: DDQ\n    Config: configs/dino/ddq-detr-4scale_swinl_8xb2-30e_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 58.7\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq_detr_swinl_30e.pth\n"
  },
  {
    "path": "configs/deepfashion/README.md",
    "content": "# DeepFashion\n\n> [DeepFashion: Powering Robust Clothes Recognition and Retrieval With Rich Annotations](https://openaccess.thecvf.com/content_cvpr_2016/html/Liu_DeepFashion_Powering_Robust_CVPR_2016_paper.html)\n\n<!-- [DATASET] -->\n\n## Abstract\n\nRecent advances in clothes recognition have been driven by the construction of clothes datasets. Existing datasets are limited in the amount of annotations and are difficult to cope with the various challenges in real-world applications. In this work, we introduce DeepFashion, a large-scale clothes dataset with comprehensive annotations. It contains over 800,000 images, which are richly annotated with massive attributes, clothing landmarks, and correspondence of images taken under different scenarios including store, street snapshot, and consumer. Such rich annotations enable the development of powerful algorithms in clothes recognition and facilitating future researches. To demonstrate the advantages of DeepFashion, we propose a new deep model, namely FashionNet, which learns clothing features by jointly predicting clothing attributes and landmarks. The estimated landmarks are then employed to pool or gate the learned features. It is optimized in an iterative manner. Extensive experiments demonstrate the effectiveness of FashionNet and the usefulness of DeepFashion.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143876310-08470a6a-ea3a-4ec1-a6f2-8ec5df36a8a0.png\"/>\n</div>\n\n## Introduction\n\n[MMFashion](https://github.com/open-mmlab/mmfashion) develops \"fashion parsing and segmentation\" module\nbased on the dataset\n[DeepFashion-Inshop](https://drive.google.com/drive/folders/0B7EVK8r0v71pVDZFQXRsMDZCX1E?usp=sharing).\nIts annotation follows COCO style.\nTo use it, you need to first download the data. Note that we only use \"img_highres\" in this task.\nThe file tree should be like this:\n\n```sh\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── DeepFashion\n│   │   ├── In-shop\n|   │   │   ├── Anno\n|   │   │   │   ├── segmentation\n|   │   │   │   |   ├── DeepFashion_segmentation_train.json\n|   │   │   │   |   ├── DeepFashion_segmentation_query.json\n|   │   │   │   |   ├── DeepFashion_segmentation_gallery.json\n|   │   │   │   ├── list_bbox_inshop.txt\n|   │   │   │   ├── list_description_inshop.json\n|   │   │   │   ├── list_item_inshop.txt\n|   │   │   │   └── list_landmarks_inshop.txt\n|   │   │   ├── Eval\n|   │   │   │   └── list_eval_partition.txt\n|   │   │   ├── Img\n|   │   │   │   ├── img\n|   │   │   │   │   ├──XXX.jpg\n|   │   │   │   ├── img_highres\n|   │   │   │   └── ├──XXX.jpg\n\n```\n\nAfter that you can train the Mask RCNN r50 on DeepFashion-In-shop dataset by launching training with the `mask_rcnn_r50_fpn_1x.py` config\nor creating your own config file.\n\n## Results and Models\n\n| Backbone | Model type |       Dataset       | bbox detection Average Precision | segmentation Average Precision |                      Config                      |                                                                                                                                       Download (Google)                                                                                                                                       |\n| :------: | :--------: | :-----------------: | :------------------------------: | :----------------------------: | :----------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| ResNet50 | Mask RCNN  | DeepFashion-In-shop |              0.599               |             0.584              | [config](./mask-rcnn_r50_fpn_15e_deepfashion.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/deepfashion/mask_rcnn_r50_fpn_15e_deepfashion/mask_rcnn_r50_fpn_15e_deepfashion_20200329_192752.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/deepfashion/mask_rcnn_r50_fpn_15e_deepfashion/20200329_192752.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{liuLQWTcvpr16DeepFashion,\n   author = {Liu, Ziwei and Luo, Ping and Qiu, Shi and Wang, Xiaogang and Tang, Xiaoou},\n   title = {DeepFashion: Powering Robust Clothes Recognition and Retrieval with Rich Annotations},\n   booktitle = {Proceedings of IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},\n   month = {June},\n   year = {2016}\n}\n```\n"
  },
  {
    "path": "configs/deepfashion/mask-rcnn_r50_fpn_15e_deepfashion.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/deepfashion.py', '../_base_/schedules/schedule_1x.py',\n    '../_base_/default_runtime.py'\n]\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(num_classes=15), mask_head=dict(num_classes=15)))\n# runtime settings\nmax_epochs = 15\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/deepsort/README.md",
    "content": "# Simple online and realtime tracking with a deep association metric\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nSimple Online and Realtime Tracking (SORT) is a pragmatic approach to multiple object tracking with a focus on simple, effective algorithms. In this paper, we integrate appearance information to improve the performance of SORT. Due to this extension we are able to track objects through longer periods of occlusions, effectively reducing the number of identity switches. In spirit of the original framework we place much of the computational complexity into an offline pre-training stage where we learn a deep association metric on a largescale person re-identification dataset. During online application, we establish measurement-to-track associations using nearest neighbor queries in visual appearance space. Experimental evaluation shows that our extensions reduce the number of identity switches by 45%, achieving overall competitive performance at high frame rates.\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/26813582/145542023-22950508-b35f-41b6-bc78-33d6a82bc3c3.png\"/>\n</div>\n\n## Results and models on MOT17\n\nCurrently we do not support training ReID models for DeepSORT.\nWe directly use the ReID model from [Tracktor](https://github.com/phil-bergmann/tracking_wo_bnw). These missed features will be supported in the future.\n\n|  Method  |      Detector      | ReID | Train Set  | Test Set | Public | Inf time (fps) | HOTA | MOTA | IDF1 |  FP   |  FN   | IDSw. |                                       Config                                       |                                                                                                         Download                                                                                                         |\n| :------: | :----------------: | :--: | :--------: | :------: | :----: | :------------: | :--: | :--: | :--: | :---: | :---: | :---: | :--------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| DeepSORT | R50-FasterRCNN-FPN | R50  | half-train | half-val |   N    |      13.8      | 57.0 | 63.7 | 69.5 | 15063 | 40323 | 3276  | [config](deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py) | [detector](https://download.openmmlab.com/mmtracking/mot/faster_rcnn/faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth) [reid](https://download.openmmlab.com/mmtracking/mot/reid/tracktor_reid_r50_iter25245-a452f51f.pth) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nWe implement DeepSORT with independent detector and ReID models.\nNote that, due to the influence of parameters such as learning rate in default configuration file,\nwe recommend using 8 GPUs for training in order to reproduce accuracy.\n\nYou can train the detector as follows.\n\n```shell script\n# Training Faster R-CNN on mot17-half-train dataset with following command.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_train.sh configs/sort/faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 4. Testing and evaluation\n\n### 4.1 Example on MOTxx-halfval dataset\n\n**4.1.1 use separate trained detector and reid model to evaluating and testing**\n\n```shell\n# Example 1: Test on motXX-half-val set.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_test_tracking.sh configs/deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8 --detector ${DETECTOR_CHECKPOINT_PATH} --reid ${REID_CHECKPOINT_PATH}\n```\n\n**4.1.2 use video_baesd to evaluating and testing**\n\nwe also provide two_ways(img_based or video_based) to evaluating and testing.\nif you want to use video_based to evaluating and testing, you can modify config as follows\n\n```\nval_dataloader = dict(\n    sampler=dict(type='DefaultSampler', shuffle=False, round_up=False))\n```\n\n### 4.2 Example on MOTxx-test dataset\n\nIf you want to get the results of the [MOT Challenge](https://motchallenge.net/) test set,\nplease use the following command to generate result files that can be used for submission.\nIt will be stored in `./mot_17_test_res`, you can modify the saved path in `test_evaluator` of the config.\n\n```shell script\n# Example 2: Test on motxx-test set\n# The number after config file represents the number of GPUs used\nbash tools/dist_test_tracking.sh configs/deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17train_test-mot17test 8 --detector ${DETECTOR_CHECKPOINT_PATH} --reid ${REID_CHECKPOINT_PATH}\n```\n\nIf you want to know about more detailed usage of `test_tracking.py/dist_test_tracking.sh/slurm_test_tracking.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 5.Inference\n\nUse a single GPU to predict a video and save it as a video.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 configs/deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17train_test-mot17test --detector ${DETECTOR_CHECKPOINT_PATH} --reid ${REID_CHECKPOINT_PATH} --out mot.mp4\n```\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@inproceedings{wojke2017simple,\n  title={Simple online and realtime tracking with a deep association metric},\n  author={Wojke, Nicolai and Bewley, Alex and Paulus, Dietrich},\n  booktitle={2017 IEEE international conference on image processing (ICIP)},\n  pages={3645--3649},\n  year={2017},\n  organization={IEEE}\n}\n```\n"
  },
  {
    "path": "configs/deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/mot_challenge.py', '../_base_/default_runtime.py'\n]\n\ndefault_hooks = dict(\n    logger=dict(type='LoggerHook', interval=1),\n    visualization=dict(type='TrackVisualizationHook', draw=False))\n\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='TrackLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n# custom hooks\ncustom_hooks = [\n    # Synchronize model buffers such as running_mean and running_var in BN\n    # at the end of each epoch\n    dict(type='SyncBuffersHook')\n]\n\ndetector = _base_.model\ndetector.pop('data_preprocessor')\ndetector.rpn_head.bbox_coder.update(dict(clip_border=False))\ndetector.roi_head.bbox_head.update(dict(num_classes=1))\ndetector.roi_head.bbox_head.bbox_coder.update(dict(clip_border=False))\ndetector['init_cfg'] = dict(\n    type='Pretrained',\n    checkpoint=  # noqa: E251\n    'https://download.openmmlab.com/mmtracking/mot/faster_rcnn/'\n    'faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth')\ndel _base_.model\n\nmodel = dict(\n    type='DeepSORT',\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    detector=detector,\n    reid=dict(\n        type='BaseReID',\n        data_preprocessor=dict(type='mmpretrain.ClsDataPreprocessor'),\n        backbone=dict(\n            type='mmpretrain.ResNet',\n            depth=50,\n            num_stages=4,\n            out_indices=(3, ),\n            style='pytorch'),\n        neck=dict(type='GlobalAveragePooling', kernel_size=(8, 4), stride=1),\n        head=dict(\n            type='LinearReIDHead',\n            num_fcs=1,\n            in_channels=2048,\n            fc_channels=1024,\n            out_channels=128,\n            num_classes=380,\n            loss_cls=dict(type='mmpretrain.CrossEntropyLoss', loss_weight=1.0),\n            loss_triplet=dict(type='TripletLoss', margin=0.3, loss_weight=1.0),\n            norm_cfg=dict(type='BN1d'),\n            act_cfg=dict(type='ReLU')),\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint=  # noqa: E251\n            'https://download.openmmlab.com/mmtracking/mot/reid/tracktor_reid_r50_iter25245-a452f51f.pth'  # noqa: E501\n        )),\n    tracker=dict(\n        type='SORTTracker',\n        motion=dict(type='KalmanFilter', center_only=False),\n        obj_score_thr=0.5,\n        reid=dict(\n            num_samples=10,\n            img_scale=(256, 128),\n            img_norm_cfg=None,\n            match_score_thr=2.0),\n        match_iou_thr=0.5,\n        momentums=None,\n        num_tentatives=2,\n        num_frames_retain=100))\n\ntrain_dataloader = None\n\ntrain_cfg = None\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n"
  },
  {
    "path": "configs/deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17train_test-mot17test.py",
    "content": "_base_ = [\n    './deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain'\n    '_test-mot17halfval.py'\n]\n\n# dataloader\nval_dataloader = dict(\n    dataset=dict(ann_file='annotations/train_cocoformat.json'))\ntest_dataloader = dict(\n    dataset=dict(\n        ann_file='annotations/test_cocoformat.json',\n        data_prefix=dict(img_path='test')))\n\n# evaluator\ntest_evaluator = dict(format_only=True, outfile_prefix='./mot_17_test_res')\n"
  },
  {
    "path": "configs/deepsort/metafile.yml",
    "content": "Collections:\n  - Name: DeepSORT\n    Metadata:\n      Training Techniques:\n        - SGD with Momentum\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n        - FPN\n    Paper:\n      URL: https://arxiv.org/abs/1703.07402\n      Title: Simple Online and Realtime Tracking with a Deep Association Metric\n    README: configs/deepsort/README.md\n\nModels:\n  - Name: deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval\n    In Collection: DeepSORT\n    Config: configs/deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n    Metadata:\n      Training Data: MOT17-half-train\n      inference time (ms/im):\n        - value: 72.5\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (640, 1088)\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT17-half-val\n        Metrics:\n          MOTA: 63.7\n          IDF1: 69.5\n          HOTA: 57.0\n    Weights:\n      - https://download.openmmlab.com/mmtracking/mot/faster_rcnn/faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth\n      - https://download.openmmlab.com/mmtracking/mot/reid/tracktor_reid_r50_iter25245-a452f51f.pth\n"
  },
  {
    "path": "configs/deformable_detr/README.md",
    "content": "# Deformable DETR\n\n> [Deformable DETR: Deformable Transformers for End-to-End Object Detection](https://arxiv.org/abs/2010.04159)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nDETR has been recently proposed to eliminate the need for many hand-designed components in object detection while demonstrating good performance. However, it suffers from slow convergence and limited feature spatial resolution, due to the limitation of Transformer attention modules in processing image feature maps. To mitigate these issues, we proposed Deformable DETR, whose attention modules only attend to a small set of key sampling points around a reference. Deformable DETR can achieve better performance than DETR (especially on small objects) with 10 times less training epochs. Extensive experiments on the COCO benchmark demonstrate the effectiveness of our approach.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143877617-ad9b24fd-77ce-46aa-9689-1a44b5594132.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |                Model                | Lr schd | box AP |                              Config                               |                                                                                                                                                                                                       Download                                                                                                                                                                                                       |\n| :------: | :---------------------------------: | :-----: | :----: | :---------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   |           Deformable DETR           |   50e   |  44.3  |         [config](./deformable-detr_r50_16xb2-50e_coco.py)         |                                 [model](https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr_r50_16xb2-50e_coco/deformable-detr_r50_16xb2-50e_coco_20221029_210934-6bc7d21b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr_r50_16xb2-50e_coco/deformable-detr_r50_16xb2-50e_coco_20221029_210934.log.json)                                 |\n|   R-50   | + iterative bounding box refinement |   50e   |  46.2  |     [config](./deformable-detr-refine_r50_16xb2-50e_coco.py)      |                   [model](https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr-refine_r50_16xb2-50e_coco/deformable-detr-refine_r50_16xb2-50e_coco_20221022_225303-844e0f93.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr-refine_r50_16xb2-50e_coco/deformable-detr-refine_r50_16xb2-50e_coco_20221022_225303.log.json)                   |\n|   R-50   |    ++ two-stage Deformable DETR     |   50e   |  47.0  | [config](./deformable-detr-refine-twostage_r50_16xb2-50e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr-refine-twostage_r50_16xb2-50e_coco/deformable-detr-refine-twostage_r50_16xb2-50e_coco_20221021_184714-acc8a5ff.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr-refine-twostage_r50_16xb2-50e_coco/deformable-detr-refine-twostage_r50_16xb2-50e_coco_20221021_184714.log.json) |\n\n### NOTE\n\n1. All models are trained with batch size 32.\n2. The performance is unstable. `Deformable DETR` and `iterative bounding box refinement` may fluctuate about 0.3 mAP. `two-stage Deformable DETR` may fluctuate about 0.2 mAP.\n\n## Citation\n\nWe provide the config files for Deformable DETR: [Deformable DETR: Deformable Transformers for End-to-End Object Detection](https://arxiv.org/abs/2010.04159).\n\n```latex\n@inproceedings{\nzhu2021deformable,\ntitle={Deformable DETR: Deformable Transformers for End-to-End Object Detection},\nauthor={Xizhou Zhu and Weijie Su and Lewei Lu and Bin Li and Xiaogang Wang and Jifeng Dai},\nbooktitle={International Conference on Learning Representations},\nyear={2021},\nurl={https://openreview.net/forum?id=gZ9hCDWe6ke}\n}\n```\n"
  },
  {
    "path": "configs/deformable_detr/deformable-detr-refine-twostage_r50_16xb2-50e_coco.py",
    "content": "_base_ = 'deformable-detr-refine_r50_16xb2-50e_coco.py'\nmodel = dict(as_two_stage=True)\n"
  },
  {
    "path": "configs/deformable_detr/deformable-detr-refine_r50_16xb2-50e_coco.py",
    "content": "_base_ = 'deformable-detr_r50_16xb2-50e_coco.py'\nmodel = dict(with_box_refine=True)\n"
  },
  {
    "path": "configs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='DeformableDETR',\n    num_queries=300,\n    num_feature_levels=4,\n    with_box_refine=False,\n    as_two_stage=False,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(  # DeformableDetrTransformerEncoder\n        num_layers=6,\n        layer_cfg=dict(  # DeformableDetrTransformerEncoderLayer\n            self_attn_cfg=dict(  # MultiScaleDeformableAttention\n                embed_dims=256,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.1))),\n    decoder=dict(  # DeformableDetrTransformerDecoder\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(  # DeformableDetrTransformerDecoderLayer\n            self_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            cross_attn_cfg=dict(  # MultiScaleDeformableAttention\n                embed_dims=256,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.1)),\n        post_norm_cfg=None),\n    positional_encoding=dict(num_feats=128, normalize=True, offset=-0.5),\n    bbox_head=dict(\n        type='DeformableDETRHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=2.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=100))\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1),\n            'sampling_offsets': dict(lr_mult=0.1),\n            'reference_points': dict(lr_mult=0.1)\n        }))\n\n# learning policy\nmax_epochs = 50\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[40],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (16 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=32)\n"
  },
  {
    "path": "configs/deformable_detr/metafile.yml",
    "content": "Collections:\n  - Name: Deformable DETR\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n        - Transformer\n    Paper:\n      URL: https://openreview.net/forum?id=gZ9hCDWe6ke\n      Title: 'Deformable DETR: Deformable Transformers for End-to-End Object Detection'\n    README: configs/deformable_detr/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.12.0/mmdet/models/detectors/deformable_detr.py#L6\n      Version: v2.12.0\n\nModels:\n  - Name: deformable-detr_r50_16xb2-50e_coco\n    In Collection: Deformable DETR\n    Config: configs/deformable_detr/deformable-detr_r50_16xb2-50e_coco.py\n    Metadata:\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr_r50_16xb2-50e_coco/deformable-detr_r50_16xb2-50e_coco_20221029_210934-6bc7d21b.pth\n\n  - Name: deformable-detr-refine_r50_16xb2-50e_coco\n    In Collection: Deformable DETR\n    Config: configs/deformable_detr/deformable-detr-refine_r50_16xb2-50e_coco.py\n    Metadata:\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.2\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr-refine_r50_16xb2-50e_coco/deformable-detr-refine_r50_16xb2-50e_coco_20221022_225303-844e0f93.pth\n\n  - Name: deformable-detr-refine-twostage_r50_16xb2-50e_coco\n    In Collection: Deformable DETR\n    Config: configs/deformable_detr/deformable-detr-refine-twostage_r50_16xb2-50e_coco.py\n    Metadata:\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.0\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/deformable_detr/deformable-detr-refine-twostage_r50_16xb2-50e_coco/deformable-detr-refine-twostage_r50_16xb2-50e_coco_20221021_184714-acc8a5ff.pth\n"
  },
  {
    "path": "configs/detectors/README.md",
    "content": "# DetectoRS\n\n> [DetectoRS: Detecting Objects with Recursive Feature Pyramid and Switchable Atrous Convolution](https://arxiv.org/abs/2006.02334)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nMany modern object detectors demonstrate outstanding performances by using the mechanism of looking and thinking twice. In this paper, we explore this mechanism in the backbone design for object detection. At the macro level, we propose Recursive Feature Pyramid, which incorporates extra feedback connections from Feature Pyramid Networks into the bottom-up backbone layers. At the micro level, we propose Switchable Atrous Convolution, which convolves the features with different atrous rates and gathers the results using switch functions. Combining them results in DetectoRS, which significantly improves the performances of object detection. On COCO test-dev, DetectoRS achieves state-of-the-art 55.7% box AP for object detection, 48.5% mask AP for instance segmentation, and 50.0% PQ for panoptic segmentation.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143877901-24451581-2c50-4a54-b000-c4cb111e29ad.png\"/>\n</div>\n\n## Introduction\n\nDetectoRS requires COCO and [COCO-stuff](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip) dataset for training. You need to download and extract it in the COCO dataset path.\nThe directory should be like this.\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n|   |   ├── stuffthingmaps\n```\n\n## Results and Models\n\nDetectoRS includes two major components:\n\n- Recursive Feature Pyramid (RFP).\n- Switchable Atrous Convolution (SAC).\n\nThey can be used independently.\nCombining them together results in DetectoRS.\nThe results on COCO 2017 val are shown in the below table.\n\n|  Method   |      Detector       | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                      Config                       |                                                                                                                                                         Download                                                                                                                                                         |\n| :-------: | :-----------------: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    RFP    | Cascade + ResNet-50 |   1x    |   7.5    |       -        |  44.8  |         |    [config](./cascade-rcnn_r50-rfp_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/detectors/cascade_rcnn_r50_rfp_1x_coco/cascade_rcnn_r50_rfp_1x_coco-8cf51bfd.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/detectors/cascade_rcnn_r50_rfp_1x_coco/cascade_rcnn_r50_rfp_1x_coco_20200624_104126.log.json)             |\n|    SAC    | Cascade + ResNet-50 |   1x    |   5.6    |       -        |  45.0  |         |    [config](./cascade-rcnn_r50-sac_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/detectors/cascade_rcnn_r50_sac_1x_coco/cascade_rcnn_r50_sac_1x_coco-24bfda62.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/detectors/cascade_rcnn_r50_sac_1x_coco/cascade_rcnn_r50_sac_1x_coco_20200624_104402.log.json)             |\n| DetectoRS | Cascade + ResNet-50 |   1x    |   9.9    |       -        |  47.4  |         | [config](./detectors_cascade-rcnn_r50_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_cascade_rcnn_r50_1x_coco/detectors_cascade_rcnn_r50_1x_coco-32a10ba0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_cascade_rcnn_r50_1x_coco/detectors_cascade_rcnn_r50_1x_coco_20200706_001203.log.json) |\n|    RFP    |   HTC + ResNet-50   |   1x    |   11.2   |       -        |  46.6  |  40.9   |        [config](./htc_r50-rfp_1x_coco.py)         |                               [model](https://download.openmmlab.com/mmdetection/v2.0/detectors/htc_r50_rfp_1x_coco/htc_r50_rfp_1x_coco-8ff87c51.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/detectors/htc_r50_rfp_1x_coco/htc_r50_rfp_1x_coco_20200624_103053.log.json)                               |\n|    SAC    |   HTC + ResNet-50   |   1x    |   9.3    |       -        |  46.4  |  40.9   |        [config](./htc_r50-sac_1x_coco.py)         |                               [model](https://download.openmmlab.com/mmdetection/v2.0/detectors/htc_r50_sac_1x_coco/htc_r50_sac_1x_coco-bfa60c54.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/detectors/htc_r50_sac_1x_coco/htc_r50_sac_1x_coco_20200624_103111.log.json)                               |\n| DetectoRS |   HTC + ResNet-50   |   1x    |   13.6   |       -        |  49.1  |  42.6   |     [config](./detectors_htc-r50_1x_coco.py)      |                   [model](https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_htc_r50_1x_coco/detectors_htc_r50_1x_coco-329b1453.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_htc_r50_1x_coco/detectors_htc_r50_1x_coco_20200624_103659.log.json)                   |\n| DetectoRS |  HTC + ResNet-101   |   20e   |   19.6   |                |  50.5  |  43.9   |    [config](./detectors_htc-r101_20e_coco.py)     |       [model](https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_htc_r101_20e_coco/detectors_htc_r101_20e_coco_20210419_203638-348d533b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_htc_r101_20e_coco/detectors_htc_r101_20e_coco_20210419_203638.log.json)       |\n\n*Note*: This is a re-implementation based on MMDetection-V2.\nThe original implementation is based on MMDetection-V1.\n\n## Citation\n\nWe provide the config files for [DetectoRS: Detecting Objects with Recursive Feature Pyramid and Switchable Atrous Convolution](https://arxiv.org/pdf/2006.02334.pdf).\n\n```latex\n@article{qiao2020detectors,\n  title={DetectoRS: Detecting Objects with Recursive Feature Pyramid and Switchable Atrous Convolution},\n  author={Qiao, Siyuan and Chen, Liang-Chieh and Yuille, Alan},\n  journal={arXiv preprint arXiv:2006.02334},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/detectors/cascade-rcnn_r50-rfp_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        type='DetectoRS_ResNet',\n        conv_cfg=dict(type='ConvAWS'),\n        output_img=True),\n    neck=dict(\n        type='RFP',\n        rfp_steps=2,\n        aspp_out_channels=64,\n        aspp_dilations=(1, 3, 6, 1),\n        rfp_backbone=dict(\n            rfp_inplanes=256,\n            type='DetectoRS_ResNet',\n            depth=50,\n            num_stages=4,\n            out_indices=(0, 1, 2, 3),\n            frozen_stages=1,\n            norm_cfg=dict(type='BN', requires_grad=True),\n            norm_eval=True,\n            conv_cfg=dict(type='ConvAWS'),\n            pretrained='torchvision://resnet50',\n            style='pytorch')))\n"
  },
  {
    "path": "configs/detectors/cascade-rcnn_r50-sac_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        type='DetectoRS_ResNet',\n        conv_cfg=dict(type='ConvAWS'),\n        sac=dict(type='SAC', use_deform=True),\n        stage_with_sac=(False, True, True, True)))\n"
  },
  {
    "path": "configs/detectors/detectors_cascade-rcnn_r50_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        type='DetectoRS_ResNet',\n        conv_cfg=dict(type='ConvAWS'),\n        sac=dict(type='SAC', use_deform=True),\n        stage_with_sac=(False, True, True, True),\n        output_img=True),\n    neck=dict(\n        type='RFP',\n        rfp_steps=2,\n        aspp_out_channels=64,\n        aspp_dilations=(1, 3, 6, 1),\n        rfp_backbone=dict(\n            rfp_inplanes=256,\n            type='DetectoRS_ResNet',\n            depth=50,\n            num_stages=4,\n            out_indices=(0, 1, 2, 3),\n            frozen_stages=1,\n            norm_cfg=dict(type='BN', requires_grad=True),\n            norm_eval=True,\n            conv_cfg=dict(type='ConvAWS'),\n            sac=dict(type='SAC', use_deform=True),\n            stage_with_sac=(False, True, True, True),\n            pretrained='torchvision://resnet50',\n            style='pytorch')))\n"
  },
  {
    "path": "configs/detectors/detectors_htc-r101_20e_coco.py",
    "content": "_base_ = '../htc/htc_r101_fpn_20e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        type='DetectoRS_ResNet',\n        conv_cfg=dict(type='ConvAWS'),\n        sac=dict(type='SAC', use_deform=True),\n        stage_with_sac=(False, True, True, True),\n        output_img=True),\n    neck=dict(\n        type='RFP',\n        rfp_steps=2,\n        aspp_out_channels=64,\n        aspp_dilations=(1, 3, 6, 1),\n        rfp_backbone=dict(\n            rfp_inplanes=256,\n            type='DetectoRS_ResNet',\n            depth=101,\n            num_stages=4,\n            out_indices=(0, 1, 2, 3),\n            frozen_stages=1,\n            norm_cfg=dict(type='BN', requires_grad=True),\n            norm_eval=True,\n            conv_cfg=dict(type='ConvAWS'),\n            sac=dict(type='SAC', use_deform=True),\n            stage_with_sac=(False, True, True, True),\n            pretrained='torchvision://resnet101',\n            style='pytorch')))\n"
  },
  {
    "path": "configs/detectors/detectors_htc-r50_1x_coco.py",
    "content": "_base_ = '../htc/htc_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        type='DetectoRS_ResNet',\n        conv_cfg=dict(type='ConvAWS'),\n        sac=dict(type='SAC', use_deform=True),\n        stage_with_sac=(False, True, True, True),\n        output_img=True),\n    neck=dict(\n        type='RFP',\n        rfp_steps=2,\n        aspp_out_channels=64,\n        aspp_dilations=(1, 3, 6, 1),\n        rfp_backbone=dict(\n            rfp_inplanes=256,\n            type='DetectoRS_ResNet',\n            depth=50,\n            num_stages=4,\n            out_indices=(0, 1, 2, 3),\n            frozen_stages=1,\n            norm_cfg=dict(type='BN', requires_grad=True),\n            norm_eval=True,\n            conv_cfg=dict(type='ConvAWS'),\n            sac=dict(type='SAC', use_deform=True),\n            stage_with_sac=(False, True, True, True),\n            pretrained='torchvision://resnet50',\n            style='pytorch')))\n"
  },
  {
    "path": "configs/detectors/htc_r50-rfp_1x_coco.py",
    "content": "_base_ = '../htc/htc_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        type='DetectoRS_ResNet',\n        conv_cfg=dict(type='ConvAWS'),\n        output_img=True),\n    neck=dict(\n        type='RFP',\n        rfp_steps=2,\n        aspp_out_channels=64,\n        aspp_dilations=(1, 3, 6, 1),\n        rfp_backbone=dict(\n            rfp_inplanes=256,\n            type='DetectoRS_ResNet',\n            depth=50,\n            num_stages=4,\n            out_indices=(0, 1, 2, 3),\n            frozen_stages=1,\n            norm_cfg=dict(type='BN', requires_grad=True),\n            norm_eval=True,\n            conv_cfg=dict(type='ConvAWS'),\n            pretrained='torchvision://resnet50',\n            style='pytorch')))\n"
  },
  {
    "path": "configs/detectors/htc_r50-sac_1x_coco.py",
    "content": "_base_ = '../htc/htc_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        type='DetectoRS_ResNet',\n        conv_cfg=dict(type='ConvAWS'),\n        sac=dict(type='SAC', use_deform=True),\n        stage_with_sac=(False, True, True, True)))\n"
  },
  {
    "path": "configs/detectors/metafile.yml",
    "content": "Collections:\n  - Name: DetectoRS\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ASPP\n        - FPN\n        - RFP\n        - RPN\n        - ResNet\n        - RoIAlign\n        - SAC\n    Paper:\n      URL: https://arxiv.org/abs/2006.02334\n      Title: 'DetectoRS: Detecting Objects with Recursive Feature Pyramid and Switchable Atrous Convolution'\n    README: configs/detectors/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.2.0/mmdet/models/backbones/detectors_resnet.py#L205\n      Version: v2.2.0\n\nModels:\n  - Name: cascade-rcnn_r50-rfp_1x_coco\n    In Collection: DetectoRS\n    Config: configs/detectors/cascade-rcnn_r50-rfp_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.5\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/detectors/cascade_rcnn_r50_rfp_1x_coco/cascade_rcnn_r50_rfp_1x_coco-8cf51bfd.pth\n\n  - Name: cascade-rcnn_r50-sac_1x_coco\n    In Collection: DetectoRS\n    Config: configs/detectors/cascade-rcnn_r50-sac_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.6\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/detectors/cascade_rcnn_r50_sac_1x_coco/cascade_rcnn_r50_sac_1x_coco-24bfda62.pth\n\n  - Name: detectors_cascade-rcnn_r50_1x_coco\n    In Collection: DetectoRS\n    Config: configs/detectors/detectors_cascade-rcnn_r50_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.9\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_cascade_rcnn_r50_1x_coco/detectors_cascade_rcnn_r50_1x_coco-32a10ba0.pth\n\n  - Name: htc_r50-rfp_1x_coco\n    In Collection: DetectoRS\n    Config: configs/detectors/htc_r50-rfp_1x_coco.py\n    Metadata:\n      Training Memory (GB): 11.2\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  40.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/detectors/htc_r50_rfp_1x_coco/htc_r50_rfp_1x_coco-8ff87c51.pth\n\n  - Name: htc_r50-sac_1x_coco\n    In Collection: DetectoRS\n    Config: configs/detectors/htc_r50-sac_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.3\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  40.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/detectors/htc_r50_sac_1x_coco/htc_r50_sac_1x_coco-bfa60c54.pth\n\n  - Name: detectors_htc-r50_1x_coco\n    In Collection: DetectoRS\n    Config: configs/detectors/detectors_htc-r50_1x_coco.py\n    Metadata:\n      Training Memory (GB): 13.6\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 49.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  42.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/detectors/detectors_htc_r50_1x_coco/detectors_htc_r50_1x_coco-329b1453.pth\n"
  },
  {
    "path": "configs/detr/README.md",
    "content": "# DETR\n\n> [End-to-End Object Detection with Transformers](https://arxiv.org/abs/2005.12872)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present a new method that views object detection as a direct set prediction problem. Our approach streamlines the detection pipeline, effectively removing the need for many hand-designed components like a non-maximum suppression procedure or anchor generation that explicitly encode our prior knowledge about the task. The main ingredients of the new framework, called DEtection TRansformer or DETR, are a set-based global loss that forces unique predictions via bipartite matching, and a transformer encoder-decoder architecture. Given a fixed small set of learned object queries, DETR reasons about the relations of the objects and the global image context to directly output the final set of predictions in parallel. The new model is conceptually simple and does not require a specialized library, unlike many other modern detectors. DETR demonstrates accuracy and run-time performance on par with the well-established and highly-optimized Faster RCNN baseline on the challenging COCO object detection dataset. Moreover, DETR can be easily generalized to produce panoptic segmentation in a unified manner. We show that it significantly outperforms competitive baselines.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143878072-0a7434e4-416b-4315-aeea-a8297f4d6453.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone | Model | Lr schd | Mem (GB) | Inf time (fps) | box AP |                 Config                 |                                                                                                                                      Download                                                                                                                                      |\n| :------: | :---: | :-----: | :------: | :------------: | :----: | :------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | DETR  |  150e   |   7.9    |                |  39.9  | [config](./detr_r50_8xb2-150e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/detr/detr_r50_8xb2-150e_coco/detr_r50_8xb2-150e_coco_20221023_153551-436d03e8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/detr/detr_r50_8xb2-150e_coco/detr_r50_8xb2-150e_coco_20221023_153551.log.json) |\n\n## Citation\n\nWe provide the config files for DETR: [End-to-End Object Detection with Transformers](https://arxiv.org/abs/2005.12872).\n\n```latex\n@inproceedings{detr,\n  author    = {Nicolas Carion and\n               Francisco Massa and\n               Gabriel Synnaeve and\n               Nicolas Usunier and\n               Alexander Kirillov and\n               Sergey Zagoruyko},\n  title     = {End-to-End Object Detection with Transformers},\n  booktitle = {ECCV},\n  year      = {2020}\n}\n```\n"
  },
  {
    "path": "configs/detr/detr_r101_8xb2-500e_coco.py",
    "content": "_base_ = './detr_r50_8xb2-500e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/detr/detr_r18_8xb2-500e_coco.py",
    "content": "_base_ = './detr_r50_8xb2-500e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[512]))\n"
  },
  {
    "path": "configs/detr/detr_r50_8xb2-150e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='DETR',\n    num_queries=100,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(3, ),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=None,\n        num_outs=1),\n    encoder=dict(  # DetrTransformerEncoder\n        num_layers=6,\n        layer_cfg=dict(  # DetrTransformerEncoderLayer\n            self_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,\n                num_fcs=2,\n                ffn_drop=0.1,\n                act_cfg=dict(type='ReLU', inplace=True)))),\n    decoder=dict(  # DetrTransformerDecoder\n        num_layers=6,\n        layer_cfg=dict(  # DetrTransformerDecoderLayer\n            self_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            cross_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,\n                num_fcs=2,\n                ffn_drop=0.1,\n                act_cfg=dict(type='ReLU', inplace=True))),\n        return_intermediate=True),\n    positional_encoding=dict(num_feats=128, normalize=True),\n    bbox_head=dict(\n        type='DETRHead',\n        num_classes=80,\n        embed_dims=256,\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            bg_cls_weight=0.1,\n            use_sigmoid=False,\n            loss_weight=1.0,\n            class_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='ClassificationCost', weight=1.),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=100))\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={'backbone': dict(lr_mult=0.1, decay_mult=1.0)}))\n\n# learning policy\nmax_epochs = 150\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[100],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/detr/detr_r50_8xb2-500e_coco.py",
    "content": "_base_ = './detr_r50_8xb2-150e_coco.py'\n\n# learning policy\nmax_epochs = 500\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=10)\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[334],\n        gamma=0.1)\n]\n\n# only keep latest 2 checkpoints\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=2))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/detr/metafile.yml",
    "content": "Collections:\n  - Name: DETR\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n        - Transformer\n    Paper:\n      URL: https://arxiv.org/abs/2005.12872\n      Title: 'End-to-End Object Detection with Transformers'\n    README: configs/detr/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/detectors/detr.py#L7\n      Version: v2.7.0\n\nModels:\n  - Name: detr_r50_8xb2-150e_coco\n    In Collection: DETR\n    Config: configs/detr/detr_r50_8xb2-150e_coco.py\n    Metadata:\n      Training Memory (GB): 7.9\n      Epochs: 150\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/detr/detr_r50_8xb2-150e_coco/detr_r50_8xb2-150e_coco_20221023_153551-436d03e8.pth\n"
  },
  {
    "path": "configs/dino/README.md",
    "content": "# DINO\n\n> [DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection](https://arxiv.org/abs/2203.03605)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present DINO (DETR with Improved deNoising anchOr boxes), a state-of-the-art end-to-end object detector. DINO improves over previous DETR-like models in performance and efficiency by using a contrastive way for denoising training, a mixed query selection method for anchor initialization, and a look forward twice scheme for box prediction. DINO achieves 49.4AP in 12 epochs and 51.3AP in 24 epochs on COCO with a ResNet-50 backbone and multi-scale features, yielding a significant improvement of +6.0AP and +2.7AP, respectively, compared to DN-DETR, the previous best DETR-like model. DINO scales well in both model size and data size. Without bells and whistles, after pre-training on the Objects365 dataset with a SwinL backbone, DINO obtains the best results on both COCO val2017 (63.2AP) and test-dev (63.3AP). Compared to other models on the leaderboard, DINO significantly reduces its model size and pre-training data size while achieving better results.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/79644233/207820666-099e6a85-59c4-45d6-a687-91b5781d11cd.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |    Model    | Lr schd | Better-Hyper | box AP |                        Config                         |                                                                                                                                                                    Download                                                                                                                                                                    |\n| :------: | :---------: | :-----: | :----------: | :----: | :---------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | DINO-4scale |   12e   |    False     |  49.0  |     [config](./dino-4scale_r50_8xb2-12e_coco.py)      |                   [model](https://download.openmmlab.com/mmdetection/v3.0/dino/dino-4scale_r50_8xb2-12e_coco/dino-4scale_r50_8xb2-12e_coco_20221202_182705-55b2bba2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/dino/dino-4scale_r50_8xb2-12e_coco/dino-4scale_r50_8xb2-12e_coco_20221202_182705.log.json)                   |\n|   R-50   | DINO-4scale |   12e   |     True     |  50.1  | [config](./dino-4scale_r50_improved_8xb2-12e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/dino/dino-4scale_r50_improved_8xb2-12e_coco/dino-4scale_r50_improved_8xb2-12e_coco_20230818_162607-6f47a913.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/dino/dino-4scale_r50_improved_8xb2-12e_coco/dino-4scale_r50_improved_8xb2-12e_coco_20230818_162607.log.json) |\n|  Swin-L  | DINO-5scale |   12e   |    False     |  57.2  |    [config](./dino-5scale_swin-l_8xb2-12e_coco.py)    |               [model](https://download.openmmlab.com/mmdetection/v3.0/dino/dino-5scale_swin-l_8xb2-12e_coco/dino-5scale_swin-l_8xb2-12e_coco_20230228_072924-a654145f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/dino/dino-5scale_swin-l_8xb2-12e_coco/dino-5scale_swin-l_8xb2-12e_coco_20230228_072924.log)                |\n|  Swin-L  | DINO-5scale |   36e   |    False     |  58.4  |    [config](./dino-5scale_swin-l_8xb2-36e_coco.py)    |                                                 [model](https://github.com/RistoranteRist/mmlab-weights/releases/download/dino-swinl/dino-5scale_swin-l_8xb2-36e_coco-5486e051.pth) \\| [log](https://github.com/RistoranteRist/mmlab-weights/releases/download/dino-swinl/20230307_032359.log)                                                 |\n\n### NOTE\n\nThe performance is unstable. `DINO-4scale` with `R-50` may fluctuate about 0.4 mAP.\n\n## Citation\n\nWe provide the config files for DINO: [DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection](https://arxiv.org/abs/2203.03605).\n\n```latex\n@misc{zhang2022dino,\n  title={DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection},\n  author={Hao Zhang and Feng Li and Shilong Liu and Lei Zhang and Hang Su and Jun Zhu and Lionel M. Ni and Heung-Yeung Shum},\n  year={2022},\n  eprint={2203.03605},\n  archivePrefix={arXiv},\n  primaryClass={cs.CV}}\n```\n"
  },
  {
    "path": "configs/dino/dino-4scale_r50_8xb2-12e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='DINO',\n    num_queries=900,  # num_matching_queries\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0))),  # 0.1 for DeformDETR\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=4,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128,\n        normalize=True,\n        offset=0.0,  # -0.5 for DeformDETR\n        temperature=20),  # 10000 for DeformDETR\n    bbox_head=dict(\n        type='DINOHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(  # TODO: Move to model.train_cfg ?\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None,\n                       num_dn_queries=100)),  # TODO: half num_dn_queries\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))  # 100 for DeformDETR\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,  # 0.0002 for DeformDETR\n        weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.1)})\n)  # custom_keys contains sampling_offsets and reference_points in DeformDETR  # noqa\n\n# learning policy\nmax_epochs = 12\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/dino/dino-4scale_r50_8xb2-24e_coco.py",
    "content": "_base_ = './dino-4scale_r50_8xb2-12e_coco.py'\nmax_epochs = 24\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/dino/dino-4scale_r50_8xb2-36e_coco.py",
    "content": "_base_ = './dino-4scale_r50_8xb2-12e_coco.py'\nmax_epochs = 36\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[30],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/dino/dino-4scale_r50_improved_8xb2-12e_coco.py",
    "content": "_base_ = ['dino-4scale_r50_8xb2-12e_coco.py']\n\n# from deformable detr hyper\nmodel = dict(\n    backbone=dict(frozen_stages=-1),\n    bbox_head=dict(loss_cls=dict(loss_weight=2.0)),\n    positional_encoding=dict(offset=-0.5, temperature=10000),\n    dn_cfg=dict(group_cfg=dict(num_dn_queries=300)))\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.0002),\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1),\n            'sampling_offsets': dict(lr_mult=0.1),\n            'reference_points': dict(lr_mult=0.1)\n        }))\n"
  },
  {
    "path": "configs/dino/dino-5scale_swin-l_8xb2-12e_coco.py",
    "content": "_base_ = './dino-4scale_r50_8xb2-12e_coco.py'\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\nnum_levels = 5\nmodel = dict(\n    num_feature_levels=num_levels,\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=True,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[192, 384, 768, 1536], num_outs=num_levels),\n    encoder=dict(layer_cfg=dict(self_attn_cfg=dict(num_levels=num_levels))),\n    decoder=dict(layer_cfg=dict(cross_attn_cfg=dict(num_levels=num_levels))))\n"
  },
  {
    "path": "configs/dino/dino-5scale_swin-l_8xb2-36e_coco.py",
    "content": "_base_ = './dino-5scale_swin-l_8xb2-12e_coco.py'\nmax_epochs = 36\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/dino/metafile.yml",
    "content": "Collections:\n  - Name: DINO\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - ResNet\n        - Transformer\n    Paper:\n      URL: https://arxiv.org/abs/2203.03605\n      Title: 'DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection'\n    README: configs/dino/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/f4112c9e5611468ffbd57cfba548fd1289264b52/mmdet/models/detectors/dino.py#L17\n      Version: v3.0.0rc6\n\nModels:\n  - Name: dino-4scale_r50_8xb2-12e_coco\n    In Collection: DINO\n    Config: configs/dino/dino-4scale_r50_8xb2-12e_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 49.0\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/dino/dino-4scale_r50_8xb2-12e_coco/dino-4scale_r50_8xb2-12e_coco_20221202_182705-55b2bba2.pth\n\n  - Name: dino-4scale_r50_8xb2-24e_coco\n    In Collection: DINO\n    Config: configs/dino/dino-4scale_r50_8xb2-24e_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n\n  - Name:  dino-4scale_r50_8xb2-36e_coco\n    In Collection: DINO\n    Config: configs/dino/dino-4scale_r50_8xb2-36e_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n\n  - Name: dino-5scale_swin-l_8xb2-12e_coco\n    In Collection: DINO\n    Config: configs/dino/dino-5scale_swin-l_8xb2-12e_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 57.2\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/dino/dino-5scale_swin-l_8xb2-12e_coco/dino-5scale_swin-l_8xb2-12e_coco_20230228_072924-a654145f.pth\n\n  - Name: dino-5scale_swin-l_8xb2-36e_coco\n    In Collection: DINO\n    Config: configs/dino/dino-5scale_swin-l_8xb2-36e_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 58.4\n    Weights: https://github.com/RistoranteRist/mmlab-weights/releases/download/dino-swinl/dino-5scale_swin-l_8xb2-36e_coco-5486e051.pth\n  - Name: dino-4scale_r50_improved_8xb2-12e_coco\n    In Collection: DINO\n    Config: configs/dino/dino-4scale_r50_improved_8xb2-12e_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.1\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/dino/dino-4scale_r50_improved_8xb2-12e_coco/dino-4scale_r50_improved_8xb2-12e_coco_20230818_162607-6f47a913.pth\n"
  },
  {
    "path": "configs/double_heads/README.md",
    "content": "# Double Heads\n\n> [Rethinking Classification and Localization for Object Detection](https://arxiv.org/abs/1904.06493)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nTwo head structures (i.e. fully connected head and convolution head) have been widely used in R-CNN based detectors for classification and localization tasks. However, there is a lack of understanding of how does these two head structures work for these two tasks. To address this issue, we perform a thorough analysis and find an interesting fact that the two head structures have opposite preferences towards the two tasks. Specifically, the fully connected head (fc-head) is more suitable for the classification task, while the convolution head (conv-head) is more suitable for the localization task. Furthermore, we examine the output feature maps of both heads and find that fc-head has more spatial sensitivity than conv-head. Thus, fc-head has more capability to distinguish a complete object from part of an object, but is not robust to regress the whole object. Based upon these findings, we propose a Double-Head method, which has a fully connected head focusing on classification and a convolution head for bounding box regression. Without bells and whistles, our method gains +3.5 and +2.8 AP on MS COCO dataset from Feature Pyramid Network (FPN) baselines with ResNet-50 and ResNet-101 backbones, respectively.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143879010-e30f654b-f93e-44b2-a186-c251fdca5bda.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                    Config                     |                                                                                                                                                        Download                                                                                                                                                         |\n| :------: | :-----: | :-----: | :------: | :------------: | :----: | :-------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN | pytorch |   1x    |   6.8    |      9.5       |  40.0  | [config](./dh-faster-rcnn_r50_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/double_heads/dh_faster_rcnn_r50_fpn_1x_coco/dh_faster_rcnn_r50_fpn_1x_coco_20200130-586b67df.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/double_heads/dh_faster_rcnn_r50_fpn_1x_coco/dh_faster_rcnn_r50_fpn_1x_coco_20200130_220238.log.json) |\n\n## Citation\n\n```latex\n@article{wu2019rethinking,\n    title={Rethinking Classification and Localization for Object Detection},\n    author={Yue Wu and Yinpeng Chen and Lu Yuan and Zicheng Liu and Lijuan Wang and Hongzhi Li and Yun Fu},\n    year={2019},\n    eprint={1904.06493},\n    archivePrefix={arXiv},\n    primaryClass={cs.CV}\n}\n```\n"
  },
  {
    "path": "configs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        type='DoubleHeadRoIHead',\n        reg_roi_scale_factor=1.3,\n        bbox_head=dict(\n            _delete_=True,\n            type='DoubleConvFCBBoxHead',\n            num_convs=4,\n            num_fcs=2,\n            in_channels=256,\n            conv_out_channels=1024,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=2.0),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=2.0))))\n"
  },
  {
    "path": "configs/double_heads/metafile.yml",
    "content": "Collections:\n  - Name: Rethinking Classification and Localization for Object Detection\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - RPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/pdf/1904.06493\n      Title: 'Rethinking Classification and Localization for Object Detection'\n    README: configs/double_heads/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/roi_heads/double_roi_head.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: dh-faster-rcnn_r50_fpn_1x_coco\n    In Collection: Rethinking Classification and Localization for Object Detection\n    Config: configs/double_heads/dh-faster-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.8\n      inference time (ms/im):\n        - value: 105.26\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/double_heads/dh_faster_rcnn_r50_fpn_1x_coco/dh_faster_rcnn_r50_fpn_1x_coco_20200130-586b67df.pth\n"
  },
  {
    "path": "configs/dsdl/README.md",
    "content": "# DSDL: Standard Description Language for DataSet\n\n<!-- [DATASET] -->\n\n## 1. Abstract\n\nData is the cornerstone of artificial intelligence. The efficiency of data acquisition, exchange, and application directly impacts the advances in technologies and applications. Over the long history of AI, a vast quantity of data sets have been developed and distributed. However, these datasets are defined in very different forms, which incurs significant overhead when it comes to exchange, integration, and utilization -- it is often the case that one needs to develop a new customized tool or script in order to incorporate a new dataset into a workflow.\n\nTo overcome such difficulties, we develop **Data Set Description Language (DSDL)**. More details please visit our [official documents](https://opendatalab.github.io/dsdl-docs/getting_started/overview/), dsdl datasets can be downloaded from our platform [OpenDataLab](https://opendatalab.com/).\n\n## 2. Steps\n\n- install dsdl:\n\n  install by pip:\n\n  ```\n  pip install dsdl\n  ```\n\n  install by source code:\n\n  ```\n  git clone https://github.com/opendatalab/dsdl-sdk.git -b schema-dsdl\n  cd dsdl-sdk\n  python setup.py install\n  ```\n\n- install mmdet and pytorch:\n  please refer this [installation documents](https://mmdetection.readthedocs.io/en/latest/get_started.html).\n\n- train:\n\n  - using single gpu:\n\n  ```\n  python tools/train.py {config_file}\n  ```\n\n  - using slurm:\n\n  ```\n  ./tools/slurm_train.sh {partition} {job_name} {config_file} {work_dir} {gpu_nums}\n  ```\n\n## 3. Test Results\n\n- detection task:\n\n  |  Datasets  |                                                                                         Model                                                                                          | box AP |           Config            |\n  | :--------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----: | :-------------------------: |\n  |  VOC07+12  |             [model](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/faster_rcnn_r50_fpn_1x_voc0712/faster_rcnn_r50_fpn_1x_voc0712_20220320_192712-54bef0f3.pth)             | 80.3\\* |   [config](./voc0712.py)    |\n  |    COCO    |                   [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth)                   |  37.4  |     [config](./coco.py)     |\n  | Objects365 |       [model](https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_16x4_1x_obj365v2/faster_rcnn_r50_fpn_16x4_1x_obj365v2_20221220_175040-5910b015.pth)       |  19.8  | [config](./objects365v2.py) |\n  | OpenImages | [model](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_20220306_202424-98c630e5.pth) | 59.9\\* | [config](./openimagesv6.py) |\n\n  \\*: box AP in voc metric and openimages metric, actually means AP_50.\n\n- instance segmentation task:\n\n  | Datasets |                                                                    Model                                                                     | box AP | mask AP |            Config            |\n  | :------: | :------------------------------------------------------------------------------------------------------------------------------------------: | :----: | :-----: | :--------------------------: |\n  |   COCO   | [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth) |  38.1  |  34.7   | [config](./coco_instance.py) |\n"
  },
  {
    "path": "configs/dsdl/coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/dsdl.py'\n]\n\n# dsdl dataset settings\n\n# please visit our platform [OpenDataLab](https://opendatalab.com/)\n# to downloaded dsdl dataset.\ndata_root = 'data/COCO2017'\nimg_prefix = 'original'\ntrain_ann = 'dsdl/set-train/train.yaml'\nval_ann = 'dsdl/set-val/val.yaml'\nspecific_key_path = dict(ignore_flag='./annotations/*/iscrowd')\n\ntrain_dataloader = dict(\n    dataset=dict(\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=train_ann,\n        data_prefix=dict(img_path=img_prefix),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32, bbox_min_size=32),\n    ))\n\nval_dataloader = dict(\n    dataset=dict(\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=val_ann,\n        data_prefix=dict(img_path=img_prefix),\n    ))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/dsdl/coco_instance.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/dsdl.py'\n]\n\n# dsdl dataset settings.\n\n# please visit our platform [OpenDataLab](https://opendatalab.com/)\n# to downloaded dsdl dataset.\ndata_root = 'data/COCO2017'\nimg_prefix = 'original'\ntrain_ann = 'dsdl/set-train/train.yaml'\nval_ann = 'dsdl/set-val/val.yaml'\nspecific_key_path = dict(ignore_flag='./annotations/*/iscrowd')\n\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'instances'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        with_polygon=True,\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=train_ann,\n        data_prefix=dict(img_path=img_prefix),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32, bbox_min_size=32),\n        pipeline=train_pipeline,\n    ))\n\nval_dataloader = dict(\n    dataset=dict(\n        with_polygon=True,\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=val_ann,\n        data_prefix=dict(img_path=img_prefix),\n        pipeline=test_pipeline,\n    ))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric', metric=['bbox', 'segm'], format_only=False)\n\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/dsdl/objects365v2.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/dsdl.py'\n]\n\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=365)))\n\n# dsdl dataset settings\n\n# please visit our platform [OpenDataLab](https://opendatalab.com/)\n# to downloaded dsdl dataset.\ndata_root = 'data/Objects365'\nimg_prefix = 'original'\ntrain_ann = 'dsdl/set-train/train.yaml'\nval_ann = 'dsdl/set-val/val.yaml'\nspecific_key_path = dict(ignore_flag='./annotations/*/iscrowd')\n\ntrain_dataloader = dict(\n    dataset=dict(\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=train_ann,\n        data_prefix=dict(img_path=img_prefix),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32, bbox_min_size=32),\n    ))\n\nval_dataloader = dict(\n    dataset=dict(\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=val_ann,\n        data_prefix=dict(img_path=img_prefix),\n        test_mode=True,\n    ))\ntest_dataloader = val_dataloader\n\ndefault_hooks = dict(logger=dict(type='LoggerHook', interval=1000), )\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=3, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[1, 2],\n        gamma=0.1)\n]\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/dsdl/openimagesv6.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/schedules/schedule_1x.py',\n    '../_base_/default_runtime.py',\n]\n\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=601)))\n\n# dsdl dataset settings\n\n# please visit our platform [OpenDataLab](https://opendatalab.com/)\n# to downloaded dsdl dataset.\ndataset_type = 'DSDLDetDataset'\ndata_root = 'data/OpenImages'\ntrain_ann = 'dsdl/set-train/train.yaml'\nval_ann = 'dsdl/set-val/val.yaml'\nspecific_key_path = dict(\n    image_level_labels='./image_labels/*/label',\n    Label='./objects/*/label',\n    is_group_of='./objects/*/isgroupof',\n)\n\nbackend_args = dict(\n    backend='petrel',\n    path_mapping=dict({'data/': 's3://open_dataset_original/'}))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1024, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1024, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'instances', 'image_level_labels'))\n]\n\ntrain_dataloader = dict(\n    sampler=dict(type='ClassAwareSampler', num_sample_class=1),\n    dataset=dict(\n        type=dataset_type,\n        with_imagelevel_label=True,\n        with_hierarchy=True,\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=train_ann,\n        filter_cfg=dict(filter_empty_gt=True, min_size=32, bbox_min_size=32),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        with_imagelevel_label=True,\n        with_hierarchy=True,\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=val_ann,\n        test_mode=True,\n        pipeline=test_pipeline))\n\ntest_dataloader = val_dataloader\n\ndefault_hooks = dict(logger=dict(type='LoggerHook', interval=1000), )\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=3, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[1, 2],\n        gamma=0.1)\n]\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\nval_evaluator = dict(\n    type='OpenImagesMetric',\n    iou_thrs=0.5,\n    ioa_thrs=0.5,\n    use_group_of=True,\n    get_supercategory=True)\n\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/dsdl/voc07.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/default_runtime.py'\n]\n\n# model setting\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=20)))\n\n# dsdl dataset settings\n\n# please visit our platform [OpenDataLab](https://opendatalab.com/)\n# to downloaded dsdl dataset.\ndataset_type = 'DSDLDetDataset'\ndata_root = 'data/VOC07-det'\nimg_prefix = 'original'\ntrain_ann = 'dsdl/set-train/train.yaml'\nval_ann = 'dsdl/set-test/test.yaml'\n\nspecific_key_path = dict(ignore_flag='./objects/*/difficult')\n\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'instances'))\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=train_ann,\n        data_prefix=dict(img_path=img_prefix),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32, bbox_min_size=32),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        specific_key_path=specific_key_path,\n        data_root=data_root,\n        ann_file=val_ann,\n        data_prefix=dict(img_path=img_prefix),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# Pascal VOC2007 uses `11points` as default evaluate mode, while PASCAL\n# VOC2012 defaults to use 'area'.\nval_evaluator = dict(type='VOCMetric', metric='mAP', eval_mode='11points')\n# val_evaluator = dict(type='CocoMetric', metric='bbox')\ntest_evaluator = val_evaluator\n\n# training schedule, voc dataset is repeated 3 times, in\n# `_base_/datasets/voc0712.py`, so the actual epoch = 4 * 3 = 12\nmax_epochs = 12\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=3)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[9],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/dsdl/voc0712.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/schedules/schedule_1x.py',\n    '../_base_/default_runtime.py',\n    # '../_base_/datasets/dsdl.py'\n]\n\n# model setting\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=20)))\n\n# dsdl dataset settings\n\n# please visit our platform [OpenDataLab](https://opendatalab.com/)\n# to downloaded dsdl dataset.\ndataset_type = 'DSDLDetDataset'\ndata_root_07 = 'data/VOC07-det'\ndata_root_12 = 'data/VOC12-det'\nimg_prefix = 'original'\n\ntrain_ann = 'dsdl/set-train/train.yaml'\nval_ann = 'dsdl/set-val/val.yaml'\ntest_ann = 'dsdl/set-test/test.yaml'\n\nbackend_args = None\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'instances'))\n]\n\nspecific_key_path = dict(ignore_flag='./objects/*/difficult', )\n\ntrain_dataloader = dict(\n    dataset=dict(\n        type='RepeatDataset',\n        times=3,\n        dataset=dict(\n            type='ConcatDataset',\n            datasets=[\n                dict(\n                    type=dataset_type,\n                    specific_key_path=specific_key_path,\n                    data_root=data_root_07,\n                    ann_file=train_ann,\n                    data_prefix=dict(img_path=img_prefix),\n                    filter_cfg=dict(\n                        filter_empty_gt=True, min_size=32, bbox_min_size=32),\n                    pipeline=train_pipeline),\n                dict(\n                    type=dataset_type,\n                    specific_key_path=specific_key_path,\n                    data_root=data_root_07,\n                    ann_file=val_ann,\n                    data_prefix=dict(img_path=img_prefix),\n                    filter_cfg=dict(\n                        filter_empty_gt=True, min_size=32, bbox_min_size=32),\n                    pipeline=train_pipeline),\n                dict(\n                    type=dataset_type,\n                    specific_key_path=specific_key_path,\n                    data_root=data_root_12,\n                    ann_file=train_ann,\n                    data_prefix=dict(img_path=img_prefix),\n                    filter_cfg=dict(\n                        filter_empty_gt=True, min_size=32, bbox_min_size=32),\n                    pipeline=train_pipeline),\n                dict(\n                    type=dataset_type,\n                    specific_key_path=specific_key_path,\n                    data_root=data_root_12,\n                    ann_file=val_ann,\n                    data_prefix=dict(img_path=img_prefix),\n                    filter_cfg=dict(\n                        filter_empty_gt=True, min_size=32, bbox_min_size=32),\n                    pipeline=train_pipeline),\n            ])))\n\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        specific_key_path=specific_key_path,\n        data_root=data_root_07,\n        ann_file=test_ann,\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type='CocoMetric', metric='bbox')\n# val_evaluator = dict(type='VOCMetric', metric='mAP', eval_mode='11points')\ntest_evaluator = val_evaluator\n\n# training schedule, voc dataset is repeated 3 times, in\n# `_base_/datasets/voc0712.py`, so the actual epoch = 4 * 3 = 12\nmax_epochs = 4\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/dyhead/README.md",
    "content": "# DyHead\n\n> [Dynamic Head: Unifying Object Detection Heads with Attentions](https://arxiv.org/abs/2106.08322)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe complex nature of combining localization and classification in object detection has resulted in the flourished development of methods. Previous works tried to improve the performance in various object detection heads but failed to present a unified view. In this paper, we present a novel dynamic head framework to unify object detection heads with attentions. By coherently combining multiple self-attention mechanisms between feature levels for scale-awareness, among spatial locations for spatial-awareness, and within output channels for task-awareness, the proposed approach significantly improves the representation ability of object detection heads without any computational overhead. Further experiments demonstrate that the effectiveness and efficiency of the proposed dynamic head on the COCO benchmark. With a standard ResNeXt-101-DCN backbone, we largely improve the performance over popular object detectors and achieve a new state-of-the-art at 54.0 AP. Furthermore, with latest transformer backbone and extra data, we can push current best COCO result to a new record at 60.6 AP.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/42844407/149169448-fcafb6d0-b866-41cc-9422-94de9f1e1761.png\" height=\"300\"/>\n</div>\n\n## Results and Models\n\n| Method | Backbone |  Style  |   Setting    | Lr schd | Mem (GB) | Inf time (fps) | box AP |                      Config                      |                                                                                                                                                                                      Download                                                                                                                                                                                      |\n| :----: | :------: | :-----: | :----------: | :-----: | :------: | :------------: | :----: | :----------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  ATSS  |   R-50   |  caffe  | reproduction |   1x    |   5.4    |      13.2      |  42.5  | [config](./atss_r50-caffe_fpn_dyhead_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_r50_fpn_dyhead_for_reproduction_1x_coco/atss_r50_fpn_dyhead_for_reproduction_4x4_1x_coco_20220107_213939-162888e6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_r50_fpn_dyhead_for_reproduction_1x_coco/atss_r50_fpn_dyhead_for_reproduction_4x4_1x_coco_20220107_213939.log.json) |\n|  ATSS  |   R-50   | pytorch |    simple    |   1x    |   4.9    |      13.7      |  43.3  |    [config](./atss_r50_fpn_dyhead_1x_coco.py)    |                               [model](https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_r50_fpn_dyhead_4x4_1x_coco/atss_r50_fpn_dyhead_4x4_1x_coco_20211219_023314-eaa620c6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_r50_fpn_dyhead_4x4_1x_coco/atss_r50_fpn_dyhead_4x4_1x_coco_20211219_023314.log.json)                               |\n\n- We trained the above models with 4 GPUs and 4 `samples_per_gpu`.\n- The `reproduction` setting aims to reproduce the official implementation based on Detectron2.\n- The `simple` setting serves as a minimum example to use DyHead in MMDetection. Specifically,\n  - it adds `DyHead` to `neck` after `FPN`\n  - it sets `stacked_convs=0` to `bbox_head`\n- The `simple` setting achieves higher AP than the original implementation.\n  We have not conduct ablation study between the two settings.\n  `dict(type='Pad', size_divisor=128)` may further improve AP by prefer spatial alignment across pyramid levels, although large padding reduces efficiency.\n\nWe also trained the model with Swin-L backbone. Results are as below.\n\n| Method | Backbone | Style |   Setting    | Lr schd | mstrain  | box AP |                         Config                          |                                                                                                                                                                                    Download                                                                                                                                                                                    |\n| :----: | :------: | :---: | :----------: | :-----: | :------: | :----: | :-----------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  ATSS  |  Swin-L  | caffe | reproduction |   2x    | 480~1200 |  56.2  | [config](./atss_swin-l-p4-w12_fpn_dyhead_ms-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_swin-l-p4-w12_fpn_dyhead_mstrain_2x_coco/atss_swin-l-p4-w12_fpn_dyhead_mstrain_2x_coco_20220509_100315-bc5b6516.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_swin-l-p4-w12_fpn_dyhead_mstrain_2x_coco/atss_swin-l-p4-w12_fpn_dyhead_mstrain_2x_coco_20220509_100315.log.json) |\n\n## Relation to Other Methods\n\n- DyHead can be regarded as an improved [SEPC](https://arxiv.org/abs/2005.03101) with [DyReLU modules](https://arxiv.org/abs/2003.10027) and simplified [SE blocks](https://arxiv.org/abs/1709.01507).\n- Xiyang Dai et al., the author team of DyHead, adopt it for [Dynamic DETR](https://openaccess.thecvf.com/content/ICCV2021/html/Dai_Dynamic_DETR_End-to-End_Object_Detection_With_Dynamic_Attention_ICCV_2021_paper.html).\n  The description of Dynamic Encoder in Sec. 3.2 will help you understand DyHead.\n\n## Citation\n\n```latex\n@inproceedings{DyHead_CVPR2021,\n  author    = {Dai, Xiyang and Chen, Yinpeng and Xiao, Bin and Chen, Dongdong and Liu, Mengchen and Yuan, Lu and Zhang, Lei},\n  title     = {Dynamic Head: Unifying Object Detection Heads With Attentions},\n  booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},\n  year      = {2021}\n}\n```\n"
  },
  {
    "path": "configs/dyhead/atss_r50-caffe_fpn_dyhead_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='ATSS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=128),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    neck=[\n        dict(\n            type='FPN',\n            in_channels=[256, 512, 1024, 2048],\n            out_channels=256,\n            start_level=1,\n            add_extra_convs='on_output',\n            num_outs=5),\n        dict(\n            type='DyHead',\n            in_channels=256,\n            out_channels=256,\n            num_blocks=6,\n            # disable zero_init_offset to follow official implementation\n            zero_init_offset=False)\n    ],\n    bbox_head=dict(\n        type='ATSSHead',\n        num_classes=80,\n        in_channels=256,\n        pred_kernel_size=1,  # follow DyHead official implementation\n        stacked_convs=0,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),  # follow DyHead official implementation\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True, backend='pillow'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True, backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/dyhead/atss_r50_fpn_dyhead_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='ATSS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=[\n        dict(\n            type='FPN',\n            in_channels=[256, 512, 1024, 2048],\n            out_channels=256,\n            start_level=1,\n            add_extra_convs='on_output',\n            num_outs=5),\n        dict(type='DyHead', in_channels=256, out_channels=256, num_blocks=6)\n    ],\n    bbox_head=dict(\n        type='ATSSHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=0,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/dyhead/atss_swin-l-p4-w12_fpn_dyhead_ms-2x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\nmodel = dict(\n    type='ATSS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=128),\n    backbone=dict(\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=[\n        dict(\n            type='FPN',\n            in_channels=[384, 768, 1536],\n            out_channels=256,\n            start_level=0,\n            add_extra_convs='on_output',\n            num_outs=5),\n        dict(\n            type='DyHead',\n            in_channels=256,\n            out_channels=256,\n            num_blocks=6,\n            # disable zero_init_offset to follow official implementation\n            zero_init_offset=False)\n    ],\n    bbox_head=dict(\n        type='ATSSHead',\n        num_classes=80,\n        in_channels=256,\n        pred_kernel_size=1,  # follow DyHead official implementation\n        stacked_convs=0,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),  # follow DyHead official implementation\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=[(2000, 480), (2000, 1200)],\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(2000, 1200), keep_ratio=True, backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=2,\n        dataset=dict(\n            type={{_base_.dataset_type}},\n            data_root={{_base_.data_root}},\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args={{_base_.backend_args}})))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW', lr=0.00005, betas=(0.9, 0.999), weight_decay=0.05),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'relative_position_bias_table': dict(decay_mult=0.),\n            'norm': dict(decay_mult=0.)\n        }),\n    clip_grad=None)\n"
  },
  {
    "path": "configs/dyhead/metafile.yml",
    "content": "Collections:\n  - Name: DyHead\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 4x T4 GPUs\n      Architecture:\n        - ATSS\n        - DyHead\n        - FPN\n        - ResNet\n        - Deformable Convolution\n        - Pyramid Convolution\n    Paper:\n      URL: https://arxiv.org/abs/2106.08322\n      Title: 'Dynamic Head: Unifying Object Detection Heads with Attentions'\n    README: configs/dyhead/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.22.0/mmdet/models/necks/dyhead.py#L130\n      Version: v2.22.0\n\nModels:\n  - Name: atss_r50-caffe_fpn_dyhead_1x_coco\n    In Collection: DyHead\n    Config: configs/dyhead/atss_r50-caffe_fpn_dyhead_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.4\n      inference time (ms/im):\n        - value: 75.7\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_r50_fpn_dyhead_for_reproduction_1x_coco/atss_r50_fpn_dyhead_for_reproduction_4x4_1x_coco_20220107_213939-162888e6.pth\n\n  - Name: atss_r50_fpn_dyhead_1x_coco\n    In Collection: DyHead\n    Config: configs/dyhead/atss_r50_fpn_dyhead_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.9\n      inference time (ms/im):\n        - value: 73.1\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_r50_fpn_dyhead_4x4_1x_coco/atss_r50_fpn_dyhead_4x4_1x_coco_20211219_023314-eaa620c6.pth\n\n  - Name: atss_swin-l-p4-w12_fpn_dyhead_ms-2x_coco\n    In Collection: DyHead\n    Config: configs/dyhead/atss_swin-l-p4-w12_fpn_dyhead_ms-2x_coco.py\n    Metadata:\n      Training Memory (GB): 58.4\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 56.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dyhead/atss_swin-l-p4-w12_fpn_dyhead_mstrain_2x_coco/atss_swin-l-p4-w12_fpn_dyhead_mstrain_2x_coco_20220509_100315-bc5b6516.pth\n"
  },
  {
    "path": "configs/dynamic_rcnn/README.md",
    "content": "# Dynamic R-CNN\n\n> [Dynamic R-CNN: Towards High Quality Object Detection via Dynamic Training](https://arxiv.org/abs/2004.06002)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nAlthough two-stage object detectors have continuously advanced the state-of-the-art performance in recent years, the training process itself is far from crystal. In this work, we first point out the inconsistency problem between the fixed network settings and the dynamic training procedure, which greatly affects the performance. For example, the fixed label assignment strategy and regression loss function cannot fit the distribution change of proposals and thus are harmful to training high quality detectors. Consequently, we propose Dynamic R-CNN to adjust the label assignment criteria (IoU threshold) and the shape of regression loss function (parameters of SmoothL1 Loss) automatically based on the statistics of proposals during training. This dynamic design makes better use of the training samples and pushes the detector to fit more high quality samples. Specifically, our method improves upon ResNet-50-FPN baseline with 1.9% AP and 5.5% AP90 on the MS COCO dataset with no extra overhead.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143879518-842f5bec-9f65-4454-93a1-9b3b0c42ec3c.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                   Config                    |                                                                                                                                      Download                                                                                                                                      |\n| :------: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | pytorch |   1x    |   3.8    |                |  38.9  | [config](./dynamic-rcnn_r50_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/dynamic_rcnn/dynamic_rcnn_r50_fpn_1x/dynamic_rcnn_r50_fpn_1x-62a3f276.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/dynamic_rcnn/dynamic_rcnn_r50_fpn_1x/dynamic_rcnn_r50_fpn_1x_20200618_095048.log.json) |\n\n## Citation\n\n```latex\n@article{DynamicRCNN,\n    author = {Hongkai Zhang and Hong Chang and Bingpeng Ma and Naiyan Wang and Xilin Chen},\n    title = {Dynamic {R-CNN}: Towards High Quality Object Detection via Dynamic Training},\n    journal = {arXiv preprint arXiv:2004.06002},\n    year = {2020}\n}\n```\n"
  },
  {
    "path": "configs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        type='DynamicRoIHead',\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    train_cfg=dict(\n        rpn_proposal=dict(nms=dict(iou_threshold=0.85)),\n        rcnn=dict(\n            dynamic_rcnn=dict(\n                iou_topk=75,\n                beta_topk=10,\n                update_iter_interval=100,\n                initial_iou=0.4,\n                initial_beta=1.0))),\n    test_cfg=dict(rpn=dict(nms=dict(iou_threshold=0.85))))\n"
  },
  {
    "path": "configs/dynamic_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Dynamic R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Dynamic R-CNN\n        - FPN\n        - RPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/pdf/2004.06002\n      Title: 'Dynamic R-CNN: Towards High Quality Object Detection via Dynamic Training'\n    README: configs/dynamic_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.2.0/mmdet/models/roi_heads/dynamic_roi_head.py#L11\n      Version: v2.2.0\n\nModels:\n  - Name: dynamic-rcnn_r50_fpn_1x_coco\n    In Collection: Dynamic R-CNN\n    Config: configs/dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.8\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/dynamic_rcnn/dynamic_rcnn_r50_fpn_1x/dynamic_rcnn_r50_fpn_1x-62a3f276.pth\n"
  },
  {
    "path": "configs/efficientnet/README.md",
    "content": "# EfficientNet\n\n> [EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks](https://arxiv.org/abs/1905.11946v5)\n\n<!-- [BACKBONE] -->\n\n## Introduction\n\nConvolutional Neural Networks (ConvNets) are commonly developed at a fixed resource budget, and then scaled up for better accuracy if more resources are available. In this paper, we systematically study model scaling and identify that carefully balancing network depth, width, and resolution can lead to better performance. Based on this observation, we propose a new scaling method that uniformly scales all dimensions of depth/width/resolution using a simple yet highly effective compound coefficient. We demonstrate the effectiveness of this method on scaling up MobileNets and ResNet.\n\nTo go even further, we use neural architecture search to design a new baseline network and scale it up to obtain a family of models, called EfficientNets, which achieve much better accuracy and efficiency than previous ConvNets. In particular, our EfficientNet-B7 achieves state-of-the-art 84.3% top-1 accuracy on ImageNet, while being 8.4x smaller and 6.1x faster on inference than the best existing ConvNet. Our EfficientNets also transfer well and achieve state-of-the-art accuracy on CIFAR-100 (91.7%), Flowers (98.8%), and 3 other transfer learning datasets, with an order of magnitude fewer parameters.\n\n## Results and Models\n\n### RetinaNet\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                         Config                          |                                                                                                                                                                              Download                                                                                                                                                                              |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Efficientnet-b3 | pytorch |   1x    |    -     |       -        |  40.5  | [config](./retinanet_effb3_fpn_8xb4-crop896-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/efficientnet/retinanet_effb3_fpn_crop896_8x4_1x_coco/retinanet_effb3_fpn_crop896_8x4_1x_coco_20220322_234806-615a0dda.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/efficientnet/retinanet_effb3_fpn_crop896_8x4_1x_coco/retinanet_effb3_fpn_crop896_8x4_1x_coco_20220322_234806.log.json) |\n\n## Citation\n\n```latex\n@article{tan2019efficientnet,\n  title={Efficientnet: Rethinking model scaling for convolutional neural networks},\n  author={Tan, Mingxing and Le, Quoc V},\n  journal={arXiv preprint arXiv:1905.11946},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/efficientnet/metafile.yml",
    "content": "Models:\n  - Name: retinanet_effb3_fpn_8xb4-crop896-1x_coco\n    In Collection: RetinaNet\n    Config: configs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/efficientnet/retinanet_effb3_fpn_crop896_8x4_1x_coco/retinanet_effb3_fpn_crop896_8x4_1x_coco_20220322_234806-615a0dda.pth\n    Paper:\n      URL: https://arxiv.org/abs/1905.11946v5\n      Title: 'EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks'\n    README: configs/efficientnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.23.0/mmdet/models/backbones/efficientnet.py#L159\n      Version: v2.23.0\n"
  },
  {
    "path": "configs/efficientnet/retinanet_effb3_fpn_8xb4-crop896-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/schedules/schedule_1x.py',\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\n\nimage_size = (896, 896)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\nnorm_cfg = dict(type='BN', requires_grad=True)\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/efficientnet/efficientnet-b3_3rdparty_8xb32-aa_in1k_20220119-5b4887a0.pth'  # noqa\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        _delete_=True,\n        type='EfficientNet',\n        arch='b3',\n        drop_path_rate=0.2,\n        out_indices=(3, 4, 5),\n        frozen_stages=0,\n        norm_cfg=dict(\n            type='SyncBN', requires_grad=True, eps=1e-3, momentum=0.01),\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone', checkpoint=checkpoint)),\n    neck=dict(\n        in_channels=[48, 136, 384],\n        start_level=0,\n        out_channels=256,\n        relu_before_extra_convs=True,\n        no_norm_on_lateral=True,\n        norm_cfg=norm_cfg),\n    bbox_head=dict(type='RetinaSepBNHead', num_ins=5, norm_cfg=norm_cfg),\n    # training and testing settings\n    train_cfg=dict(assigner=dict(neg_iou_thr=0.5)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=image_size),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=image_size, keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=4, num_workers=4, dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.04),\n    paramwise_cfg=dict(norm_decay_mult=0, bypass_duplicate=True))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# cudnn_benchmark=True can accelerate fix-size training\nenv_cfg = dict(cudnn_benchmark=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (4 samples per GPU)\nauto_scale_lr = dict(base_batch_size=32)\n"
  },
  {
    "path": "configs/empirical_attention/README.md",
    "content": "# Empirical Attention\n\n> [An Empirical Study of Spatial Attention Mechanisms in Deep Networks](https://arxiv.org/abs/1904.05873)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nAttention mechanisms have become a popular component in deep neural networks, yet there has been little examination of how different influencing factors and methods for computing attention from these factors affect performance. Toward a better general understanding of attention mechanisms, we present an empirical study that ablates various spatial attention elements within a generalized attention formulation, encompassing the dominant Transformer attention as well as the prevalent deformable convolution and dynamic convolution modules. Conducted on a variety of applications, the study yields significant findings about spatial attention in deep networks, some of which run counter to conventional understanding. For example, we find that the query and key content comparison in Transformer attention is negligible for self-attention, but vital for encoder-decoder attention. A proper combination of deformable convolution with key content only saliency achieves the best accuracy-efficiency tradeoff in self-attention. Our results suggest that there exists much room for improvement in the design of attention mechanisms.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143879619-f1817da9-1573-45c9-891d-cfe55ad54911.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone | Attention Component | DCN | Lr schd | Mem (GB) | Inf time (fps) | box AP |                         Config                          |                                                                                                                                                                                               Download                                                                                                                                                                                                |\n| :------: | :-----------------: | :-: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   |        1111         |  N  |   1x    |   8.0    |      13.8      |  40.0  |   [config](./faster-rcnn_r50-attn1111_fpn_1x_coco.py)   |         [model](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_1111_1x_coco/faster_rcnn_r50_fpn_attention_1111_1x_coco_20200130-403cccba.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_1111_1x_coco/faster_rcnn_r50_fpn_attention_1111_1x_coco_20200130_210344.log.json)         |\n|   R-50   |        0010         |  N  |   1x    |   4.2    |      18.4      |  39.1  |   [config](./faster-rcnn_r50-attn0010_fpn_1x_coco.py)   |         [model](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_0010_1x_coco/faster_rcnn_r50_fpn_attention_0010_1x_coco_20200130-7cb0c14d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_0010_1x_coco/faster_rcnn_r50_fpn_attention_0010_1x_coco_20200130_210125.log.json)         |\n|   R-50   |        1111         |  Y  |   1x    |   8.0    |      12.7      |  42.1  | [config](./faster-rcnn_r50-attn1111-dcn_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_1111_dcn_1x_coco/faster_rcnn_r50_fpn_attention_1111_dcn_1x_coco_20200130-8b2523a6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_1111_dcn_1x_coco/faster_rcnn_r50_fpn_attention_1111_dcn_1x_coco_20200130_204442.log.json) |\n|   R-50   |        0010         |  Y  |   1x    |   4.2    |      17.1      |  42.0  | [config](./faster-rcnn_r50-attn0010-dcn_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_0010_dcn_1x_coco/faster_rcnn_r50_fpn_attention_0010_dcn_1x_coco_20200130-1a2e831d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_0010_dcn_1x_coco/faster_rcnn_r50_fpn_attention_0010_dcn_1x_coco_20200130_210410.log.json) |\n\n## Citation\n\n```latex\n@article{zhu2019empirical,\n  title={An Empirical Study of Spatial Attention Mechanisms in Deep Networks},\n  author={Zhu, Xizhou and Cheng, Dazhi and Zhang, Zheng and Lin, Stephen and Dai, Jifeng},\n  journal={arXiv preprint arXiv:1904.05873},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/empirical_attention/faster-rcnn_r50-attn0010-dcn_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        plugins=[\n            dict(\n                cfg=dict(\n                    type='GeneralizedAttention',\n                    spatial_range=-1,\n                    num_heads=8,\n                    attention_type='0010',\n                    kv_stride=2),\n                stages=(False, False, True, True),\n                position='after_conv2')\n        ],\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/empirical_attention/faster-rcnn_r50-attn0010_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(plugins=[\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='0010',\n                kv_stride=2),\n            stages=(False, False, True, True),\n            position='after_conv2')\n    ]))\n"
  },
  {
    "path": "configs/empirical_attention/faster-rcnn_r50-attn1111-dcn_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        plugins=[\n            dict(\n                cfg=dict(\n                    type='GeneralizedAttention',\n                    spatial_range=-1,\n                    num_heads=8,\n                    attention_type='1111',\n                    kv_stride=2),\n                stages=(False, False, True, True),\n                position='after_conv2')\n        ],\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n"
  },
  {
    "path": "configs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(plugins=[\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='1111',\n                kv_stride=2),\n            stages=(False, False, True, True),\n            position='after_conv2')\n    ]))\n"
  },
  {
    "path": "configs/empirical_attention/metafile.yml",
    "content": "Collections:\n  - Name: Empirical Attention\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Deformable Convolution\n        - FPN\n        - RPN\n        - ResNet\n        - RoIAlign\n        - Spatial Attention\n    Paper:\n      URL: https://arxiv.org/pdf/1904.05873\n      Title: 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks'\n    README: configs/empirical_attention/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/ops/generalized_attention.py#L10\n      Version: v2.0.0\n\nModels:\n  - Name: faster-rcnn_r50_fpn_attention_1111_1x_coco\n    In Collection: Empirical Attention\n    Config: configs/empirical_attention/faster-rcnn_r50-attn1111_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.0\n      inference time (ms/im):\n        - value: 72.46\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_1111_1x_coco/faster_rcnn_r50_fpn_attention_1111_1x_coco_20200130-403cccba.pth\n\n  - Name: faster-rcnn_r50_fpn_attention_0010_1x_coco\n    In Collection: Empirical Attention\n    Config: configs/empirical_attention/faster-rcnn_r50-attn0010_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.2\n      inference time (ms/im):\n        - value: 54.35\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_0010_1x_coco/faster_rcnn_r50_fpn_attention_0010_1x_coco_20200130-7cb0c14d.pth\n\n  - Name: faster-rcnn_r50_fpn_attention_1111_dcn_1x_coco\n    In Collection: Empirical Attention\n    Config: configs/empirical_attention/faster-rcnn_r50-attn1111-dcn_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.0\n      inference time (ms/im):\n        - value: 78.74\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_1111_dcn_1x_coco/faster_rcnn_r50_fpn_attention_1111_dcn_1x_coco_20200130-8b2523a6.pth\n\n  - Name: faster-rcnn_r50_fpn_attention_0010_dcn_1x_coco\n    In Collection: Empirical Attention\n    Config: configs/empirical_attention/faster-rcnn_r50-attn0010-dcn_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.2\n      inference time (ms/im):\n        - value: 58.48\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/empirical_attention/faster_rcnn_r50_fpn_attention_0010_dcn_1x_coco/faster_rcnn_r50_fpn_attention_0010_dcn_1x_coco_20200130-1a2e831d.pth\n"
  },
  {
    "path": "configs/fast_rcnn/README.md",
    "content": "# Fast R-CNN\n\n> [Fast R-CNN](https://arxiv.org/abs/1504.08083)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThis paper proposes a Fast Region-based Convolutional Network method (Fast R-CNN) for object detection. Fast R-CNN builds on previous work to efficiently classify object proposals using deep convolutional networks. Compared to previous work, Fast R-CNN employs several innovations to improve training and testing speed while also increasing detection accuracy. Fast R-CNN trains the very deep VGG16 network 9x faster than R-CNN, is 213x faster at test-time, and achieves a higher mAP on PASCAL VOC 2012. Compared to SPPnet, Fast R-CNN trains VGG16 3x faster, tests 10x faster, and is more accurate.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143882189-6258c05c-f2a1-4320-9282-7e2f2d502eb2.png\"/>\n</div>\n\n## Introduction\n\nBefore training the Fast R-CNN, users should first train an [RPN](../rpn/README.md), and use the RPN to extract the region proposals.\nThe region proposals can be obtained by setting `DumpProposals` pseudo metric. The dumped results is a `dict(file_name: pred_instance)`.\nThe `pred_instance` is an `InstanceData` containing the sorted boxes and scores predicted by RPN. We provide example of dumping proposals in [RPN config](../rpn/rpn_r50_fpn_1x_coco.py).\n\n- First, it should be obtained the region proposals in both training and validation (or testing) set.\n  change the type of `test_evaluator` to `DumpProposals` in the RPN config to get the region proposals as below:\n\n  The config of get training image region proposals can be set as below:\n\n  ```python\n  # For training set\n  val_dataloader = dict(\n      dataset=dict(\n          ann_file='data/coco/annotations/instances_train2017.json',\n          data_prefix=dict(img='val2017/')))\n  val_dataloader = dict(\n      _delete_=True,\n      type='DumpProposals',\n      output_dir='data/coco/proposals/',\n      proposals_file='rpn_r50_fpn_1x_train2017.pkl')\n  test_dataloader = val_dataloader\n  test_evaluator = val_dataloader\n  ```\n\n  The config of get validation image region proposals can be set as below:\n\n  ```python\n  # For validation set\n  val_dataloader = dict(\n    _delete_=True,\n    type='DumpProposals',\n    output_dir='data/coco/proposals/',\n    proposals_file='rpn_r50_fpn_1x_val2017.pkl')\n  test_evaluator = val_dataloader\n  ```\n\n  Extract the region proposals command can be set as below:\n\n  ```bash\n  ./tools/dist_test.sh \\\n      configs/rpn_r50_fpn_1x_coco.py \\\n      checkpoints/rpn_r50_fpn_1x_coco_20200218-5525fa2e.pth \\\n      8\n  ```\n\n  Users can refer to [test tutorial](https://mmdetection.readthedocs.io/en/latest/user_guides/test.html) for more details.\n\n- Then, modify the path of `proposal_file` in the dataset and using `ProposalBroadcaster` to process both ground truth bounding boxes and region proposals in pipelines.\n  An example of Fast R-CNN important setting can be seen as below:\n\n  ```python\n  train_pipeline = [\n      dict(\n          type='LoadImageFromFile',\n          backend_args={{_base_.backend_args}}),\n      dict(type='LoadProposals', num_max_proposals=2000),\n      dict(type='LoadAnnotations', with_bbox=True),\n      dict(\n          type='ProposalBroadcaster',\n          transforms=[\n              dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n              dict(type='RandomFlip', prob=0.5),\n          ]),\n      dict(type='PackDetInputs')\n  ]\n  test_pipeline = [\n      dict(\n          type='LoadImageFromFile',\n          backend_args={{_base_.backend_args}}),\n      dict(type='LoadProposals', num_max_proposals=None),\n      dict(\n          type='ProposalBroadcaster',\n          transforms=[\n              dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n          ]),\n      dict(\n          type='PackDetInputs',\n          meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                     'scale_factor'))\n  ]\n  train_dataloader = dict(\n      dataset=dict(\n          proposal_file='proposals/rpn_r50_fpn_1x_train2017.pkl',\n          pipeline=train_pipeline))\n  val_dataloader = dict(\n      dataset=dict(\n          proposal_file='proposals/rpn_r50_fpn_1x_val2017.pkl',\n          pipeline=test_pipeline))\n  test_dataloader = val_dataloader\n  ```\n\n- Finally, users can start training the Fast R-CNN.\n\n## Results and Models\n\n## Citation\n\n```latex\n@inproceedings{girshick2015fast,\n  title={Fast r-cnn},\n  author={Girshick, Ross},\n  booktitle={Proceedings of the IEEE international conference on computer vision},\n  year={2015}\n}\n```\n"
  },
  {
    "path": "configs/fast_rcnn/fast-rcnn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './fast-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/fast_rcnn/fast-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = './fast-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/fast_rcnn/fast-rcnn_r101_fpn_2x_coco.py",
    "content": "_base_ = './fast-rcnn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/fast_rcnn/fast-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = './fast-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='caffe',\n        norm_eval=True,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/fast_rcnn/fast-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/fast-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadProposals', num_max_proposals=2000),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='ProposalBroadcaster',\n        transforms=[\n            dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n            dict(type='RandomFlip', prob=0.5),\n        ]),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadProposals', num_max_proposals=None),\n    dict(\n        type='ProposalBroadcaster',\n        transforms=[\n            dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        proposal_file='proposals/rpn_r50_fpn_1x_train2017.pkl',\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    dataset=dict(\n        proposal_file='proposals/rpn_r50_fpn_1x_val2017.pkl',\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/fast_rcnn/fast-rcnn_r50_fpn_2x_coco.py",
    "content": "_base_ = './fast-rcnn_r50_fpn_1x_coco.py'\n\ntrain_cfg = dict(max_epochs=24)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/faster_rcnn/README.md",
    "content": "# Faster R-CNN\n\n> [Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks](https://arxiv.org/abs/1506.01497)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nState-of-the-art object detection networks depend on region proposal algorithms to hypothesize object locations. Advances like SPPnet and Fast R-CNN have reduced the running time of these detection networks, exposing region proposal computation as a bottleneck. In this work, we introduce a Region Proposal Network (RPN) that shares full-image convolutional features with the detection network, thus enabling nearly cost-free region proposals. An RPN is a fully convolutional network that simultaneously predicts object bounds and objectness scores at each position. The RPN is trained end-to-end to generate high-quality region proposals, which are used by Fast R-CNN for detection. We further merge RPN and Fast R-CNN into a single network by sharing their convolutional features---using the recently popular terminology of neural networks with 'attention' mechanisms, the RPN component tells the unified network where to look. For the very deep VGG-16 model, our detection system has a frame rate of 5fps (including all steps) on a GPU, while achieving state-of-the-art object detection accuracy on PASCAL VOC 2007, 2012, and MS COCO datasets with only 300 proposals per image. In ILSVRC and COCO 2015 competitions, Faster R-CNN and RPN are the foundations of the 1st-place winning entries in several tracks.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143881188-ab87720f-5059-4b4e-a928-b540fb8fb84d.png\" height=\"300\"/>\n</div>\n\n## Results and Models\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                      Config                       |                                                                                                                                                    Download                                                                                                                                                     |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     R-50-C4     |  caffe  |   1x    |    -     |       -        |  35.6  |  [config](./faster-rcnn_r50-caffe_c4-1x_coco.py)  |                [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50-caffe-c4_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_c4_1x_coco/faster_rcnn_r50_caffe_c4_1x_coco_20220316_150152.log.json)                |\n|    R-50-DC5     |  caffe  |   1x    |    -     |       -        |  37.2  | [config](./faster-rcnn_r50-caffe-dc5_1x_coco.py)  |              [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50-caffe-dc5_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_1x_coco/faster_rcnn_r50_caffe_dc5_1x_coco_20201030_151909.log.json)               |\n|    R-50-FPN     |  caffe  |   1x    |   3.8    |                |  37.8  | [config](./faster-rcnn_r50-caffe_fpn_1x_coco.py)  |              [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50-caffe_fpn_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_1x_coco/faster_rcnn_r50_caffe_fpn_1x_coco_20200504_180032.log.json)               |\n|    R-50-FPN     | pytorch |   1x    |   4.0    |      21.4      |  37.4  |    [config](./faster-rcnn_r50_fpn_1x_coco.py)     |                       [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50_fpn_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130_204655.log.json)                        |\n| R-50-FPN (FP16) | pytorch |   1x    |   3.4    |      28.8      |  37.5  |  [config](./faster-rcnn_r50_fpn_amp-1x_coco.py)   | [model](https://download.openmmlab.com/mmdetection/v2.0/fp16/faster_rcnn_r50_fpn_fp16_1x_coco/faster_rcnn_r50_fpn_fp16_1x_coco_20200204-d4dc1471.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fp16/faster_rcnn_r50_fpn_fp16_1x_coco/faster_rcnn_r50_fpn_fp16_1x_coco_20200204_143530.log.json) |\n|    R-50-FPN     | pytorch |   2x    |    -     |       -        |  38.4  |    [config](./faster-rcnn_r50_fpn_2x_coco.py)     |                       [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50_fpn_2x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_2x_coco/faster_rcnn_r50_fpn_2x_coco_20200504_210434.log.json)                        |\n|    R-101-FPN    |  caffe  |   1x    |   5.7    |                |  39.8  | [config](./faster-rcnn_r101-caffe_fpn_1x_coco.py) |             [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r101-caffe_fpn_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_caffe_fpn_1x_coco/faster_rcnn_r101_caffe_fpn_1x_coco_20200504_180057.log.json)             |\n|    R-101-FPN    | pytorch |   1x    |   6.0    |      15.6      |  39.4  |    [config](./faster-rcnn_r101_fpn_1x_coco.py)    |                      [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r101_fpn_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_1x_coco/faster_rcnn_r101_fpn_1x_coco_20200130_204655.log.json)                      |\n|    R-101-FPN    | pytorch |   2x    |    -     |       -        |  39.8  |    [config](./faster-rcnn_r101_fpn_2x_coco.py)    |                      [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r101_fpn_2x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_2x_coco/faster_rcnn_r101_fpn_2x_coco_20200504_210455.log.json)                      |\n| X-101-32x4d-FPN | pytorch |   1x    |   7.2    |      13.8      |  41.2  | [config](./faster-rcnn_x101-32x4d_fpn_1x_coco.py) |             [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_x101-32x4d_fpn_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x4d_fpn_1x_coco/faster_rcnn_x101_32x4d_fpn_1x_coco_20200203_000520.log.json)             |\n| X-101-32x4d-FPN | pytorch |   2x    |    -     |       -        |  41.2  | [config](./faster-rcnn_x101-32x4d_fpn_2x_coco.py) |             [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_x101-32x4d_fpn_2x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x4d_fpn_2x_coco/faster_rcnn_x101_32x4d_fpn_2x_coco_20200506_041400.log.json)             |\n| X-101-64x4d-FPN | pytorch |   1x    |   10.3   |      9.4       |  42.1  | [config](./faster-rcnn_x101-64x4d_fpn_1x_coco.py) |             [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_x101-64x4d_fpn_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_64x4d_fpn_1x_coco/faster_rcnn_x101_64x4d_fpn_1x_coco_20200204_134340.log.json)             |\n| X-101-64x4d-FPN | pytorch |   2x    |    -     |       -        |  41.6  | [config](./faster-rcnn_x101-64x4d_fpn_2x_coco.py) |             [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_x101-64x4d_fpn_2x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_64x4d_fpn_2x_coco/faster_rcnn_x101_64x4d_fpn_2x_coco_20200512_161033.log.json)             |\n\n## Different regression loss\n\nWe trained with R-50-FPN pytorch style backbone for 1x schedule.\n\n| Backbone |   Loss type    | Mem (GB) | Inf time (fps) | box AP |                         Config                         |                                                                                                                                                         Download                                                                                                                                                         |\n| :------: | :------------: | :------: | :------------: | :----: | :----------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN |     L1Loss     |   4.0    |      21.4      |  37.4  |       [config](./faster-rcnn_r50_fpn_1x_coco.py)       |                            [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50_fpn_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130_204655.log.json)                            |\n| R-50-FPN |    IoULoss     |          |                |  37.9  |     [config](./faster-rcnn_r50_fpn_iou_1x_coco.py)     |                      [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50_fpn_iou_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_iou_1x_coco/faster_rcnn_r50_fpn_iou_1x_coco_20200506_095954.log.json)                      |\n| R-50-FPN |    GIoULoss    |          |                |  37.6  |    [config](./faster-rcnn_r50_fpn_giou_1x_coco.py)     |                       [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50_fpn_giou_1x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_giou_1x_coco_20200505_161120.log.json)                       |\n| R-50-FPN | BoundedIoULoss |          |                |  37.4  | [config](./faster-rcnn_r50_fpn_bounded-iou_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_bounded_iou_1x_coco-98ad993b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_bounded_iou_1x_coco_20200505_160738.log.json) |\n\n## Pre-trained Models\n\nWe also train some models with longer schedules and multi-scale training. The users could finetune them for downstream tasks.\n\n|                           Backbone                            |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                        Config                        |                                                                                                                                                                                 Download                                                                                                                                                                                 |\n| :-----------------------------------------------------------: | :-----: | :-----: | :------: | :------------: | :----: | :--------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|      [R-50-C4](./faster-rcnn_r50-caffe-c4_ms-1x_coco.py)      |  caffe  |   1x    |    -     |                |  35.9  |  [config](./faster-rcnn_r50-caffe-c4_ms-1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_c4_mstrain_1x_coco/faster_rcnn_r50_caffe_c4_mstrain_1x_coco_20220316_150527-db276fed.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_c4_mstrain_1x_coco/faster_rcnn_r50_caffe_c4_mstrain_1x_coco_20220316_150527.log.json)   |\n|     [R-50-DC5](./faster-rcnn_r50-caffe-dc5_ms-1x_coco.py)     |  caffe  |   1x    |    -     |                |  37.4  | [config](./faster-rcnn_r50-caffe-dc5_ms-1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_mstrain_1x_coco/faster_rcnn_r50_caffe_dc5_mstrain_1x_coco_20201028_233851-b33d21b9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_mstrain_1x_coco/faster_rcnn_r50_caffe_dc5_mstrain_1x_coco_20201028_233851.log.json) |\n|     [R-50-DC5](./faster-rcnn_r50-caffe-dc5_ms-3x_coco.py)     |  caffe  |   3x    |    -     |                |  38.7  | [config](./faster-rcnn_r50-caffe-dc5_ms-3x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_mstrain_3x_coco/faster_rcnn_r50_caffe_dc5_mstrain_3x_coco_20201028_002107-34a53b2c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_mstrain_3x_coco/faster_rcnn_r50_caffe_dc5_mstrain_3x_coco_20201028_002107.log.json) |\n|     [R-50-FPN](./faster-rcnn_r50-caffe_fpn_ms-2x_coco.py)     |  caffe  |   2x    |   3.7    |                |  39.7  | [config](./faster-rcnn_r50-caffe_fpn_ms-2x_coco.py)  |                                 [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50-caffe_fpn_ms-2x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_2x_coco/faster_rcnn_r50_caffe_fpn_mstrain_2x_coco_20200504_231813.log.json)                                  |\n|     [R-50-FPN](./faster-rcnn_r50-caffe_fpn_ms-3x_coco.py)     |  caffe  |   3x    |   3.7    |                |  39.9  | [config](./faster-rcnn_r50-caffe_fpn_ms-3x_coco.py)  |                                 [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r50-caffe_fpn_ms-3x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210526_095054.log.json)                                  |\n|        [R-50-FPN](./faster-rcnn_r50_fpn_ms-3x_coco.py)        | pytorch |   3x    |   3.9    |                |  40.3  |    [config](./faster-rcnn_r50_fpn_ms-3x_coco.py)     |             [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_mstrain_3x_coco/faster_rcnn_r50_fpn_mstrain_3x_coco_20210524_110822-e10bd31c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_mstrain_3x_coco/faster_rcnn_r50_fpn_mstrain_3x_coco_20210524_110822.log.json)             |\n|    [R-101-FPN](./faster-rcnn_r101-caffe_fpn_ms-3x_coco.py)    |  caffe  |   3x    |   5.6    |                |  42.0  | [config](./faster-rcnn_r101-caffe_fpn_ms-3x_coco.py) |                                [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r101-caffe_fpn_ms-3x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_caffe_fpn_mstrain_3x_coco/faster_rcnn_r101_caffe_fpn_mstrain_3x_coco_20210526_095742.log.json)                                |\n|       [R-101-FPN](./faster-rcnn_r101_fpn_ms-3x_coco.py)       | pytorch |   3x    |   5.8    |                |  41.8  |    [config](./faster-rcnn_r101_fpn_ms-3x_coco.py)    |                                         [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_r101_fpn_ms-3x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_mstrain_3x_coco/faster_rcnn_r101_fpn_mstrain_3x_coco_20210524_110822.log.json)                                         |\n| [X-101-32x4d-FPN](./faster-rcnn_x101-32x4d_fpn_ms-3x_coco.py) | pytorch |   3x    |   7.0    |                |  42.5  | [config](./faster-rcnn_x101-32x4d_fpn_ms-3x_coco.py) |                                [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_x101-32x4d_fpn_ms-3x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x4d_fpn_mstrain_3x_coco/faster_rcnn_x101_32x4d_fpn_mstrain_3x_coco_20210524_124151.log.json)                                |\n| [X-101-32x8d-FPN](./faster-rcnn_x101-32x8d_fpn_ms-3x_coco.py) | pytorch |   3x    |   10.1   |                |  42.4  | [config](./faster-rcnn_x101-32x8d_fpn_ms-3x_coco.py) |                                [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_x101-32x8d_fpn_ms-3x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x8d_fpn_mstrain_3x_coco/faster_rcnn_x101_32x8d_fpn_mstrain_3x_coco_20210604_182954.log.json)                                |\n| [X-101-64x4d-FPN](./faster-rcnn_x101-64x4d_fpn_ms-3x_coco.py) | pytorch |   3x    |   10.0   |                |  43.1  | [config](./faster-rcnn_x101-64x4d_fpn_ms-3x_coco.py) |                                [model](https://download.openxlab.org.cn/models/mmdetection/FasterR-CNN/weight/faster-rcnn_x101-64x4d_fpn_ms-3x_coco) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_64x4d_fpn_mstrain_3x_coco/faster_rcnn_x101_64x4d_fpn_mstrain_3x_coco_20210524_124528.log.json)                                |\n\nWe further finetune some pre-trained models on the COCO subsets, which only contain only a few of the 80 categories.\n\n| Backbone                                                                 | Style | Class name         | Pre-traind model                                               | Mem (GB) | box AP | Config                                                                 | Download                                                                                                                                                                                                                                                                                                                                                                                     |\n| ------------------------------------------------------------------------ | ----- | ------------------ | -------------------------------------------------------------- | -------- | ------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [R-50-FPN](./faster-rcnn_r50-caffe_fpn_ms-1x_coco-person.py)             | caffe | person             | [R-50-FPN-Caffe-3x](./faster-rcnn_r50-caffe_fpn_ms-3x_coco.py) | 3.7      | 55.8   | [config](./faster-rcnn_r50-caffe_fpn_ms-1x_coco-person.py)             | [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco-person/faster_rcnn_r50_fpn_1x_coco-person_20201216_175929-d022e227.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco-person/faster_rcnn_r50_fpn_1x_coco-person_20201216_175929.log.json)                                                 |\n| [R-50-FPN](./faster-rcnn_r50-caffe_fpn_ms-1x_coco-person-bicycle-car.py) | caffe | person-bicycle-car | [R-50-FPN-Caffe-3x](./faster-rcnn_r50-caffe_fpn_ms-3x_coco.py) | 3.7      | 44.1   | [config](./faster-rcnn_r50-caffe_fpn_ms-1x_coco-person-bicycle-car.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco-person-bicycle-car/faster_rcnn_r50_fpn_1x_coco-person-bicycle-car_20201216_173117-6eda6d92.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco-person-bicycle-car/faster_rcnn_r50_fpn_1x_coco-person-bicycle-car_20201216_173117.log.json) |\n\n## Torchvision New Receipe (TNR)\n\nTorchvision released its high-precision ResNet models. The training details can be found on the [Pytorch website](https://pytorch.org/blog/how-to-train-state-of-the-art-models-using-torchvision-latest-primitives/). Here, we have done grid searches on learning rate and weight decay and found the optimal hyper-parameter on the detection task.\n\n|                       Backbone                       |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                       Config                       |                                                                                                                                                                               Download                                                                                                                                                                               |\n| :--------------------------------------------------: | :-----: | :-----: | :------: | :------------: | :----: | :------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| [R-50-TNR](./faster-rcnn_r50-tnr-pre_fpn_1x_coco.py) | pytorch |   1x    |    -     |                |  40.2  | [config](./faster-rcnn_r50-tnr-pre_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_tnr-pretrain_1x_coco/faster_rcnn_r50_fpn_tnr-pretrain_1x_coco_20220320_085147-efedfda4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_tnr-pretrain_1x_coco/faster_rcnn_r50_fpn_tnr-pretrain_1x_coco_20220320_085147.log.json) |\n\n## Citation\n\n```latex\n@article{Ren_2017,\n   title={Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks},\n   journal={IEEE Transactions on Pattern Analysis and Machine Intelligence},\n   publisher={Institute of Electrical and Electronics Engineers (IEEE)},\n   author={Ren, Shaoqing and He, Kaiming and Girshick, Ross and Sun, Jian},\n   year={2017},\n   month={Jun},\n}\n```\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r101-caffe_fpn_ms-3x_coco.py",
    "content": "_base_ = 'faster-rcnn_r50_fpn_ms-3x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r101_fpn_2x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r101_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r101_fpn_ms-3x_coco.py",
    "content": "_base_ = 'faster-rcnn_r50_fpn_ms-3x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r18_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe-c4_ms-1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50-caffe_c4-1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n_base_.train_dataloader.dataset.pipeline = train_pipeline\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50-caffe-dc5.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_ms-1x_coco.py",
    "content": "_base_ = 'faster-rcnn_r50-caffe-dc5_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n_base_.train_dataloader.dataset.pipeline = train_pipeline\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_ms-3x_coco.py",
    "content": "_base_ = './faster-rcnn_r50-caffe-dc5_ms-1x_coco.py'\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# param_scheduler = [\n#     dict(\n#         type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500), # noqa\n#     dict(\n#         type='MultiStepLR',\n#         begin=0,\n#         end=12,\n#         by_epoch=True,\n#         milestones=[28, 34],\n#         gamma=0.1)\n# ]\n_base_.param_scheduler[1].milestones = [28, 34]\n\ntrain_cfg = dict(max_epochs=36)\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_c4-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50-caffe-c4.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_90k_coco.py",
    "content": "_base_ = 'faster-rcnn_r50-caffe_fpn_1x_coco.py'\nmax_iter = 90000\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=10000)\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-1x_coco-person-bicycle-car.py",
    "content": "_base_ = './faster-rcnn_r50-caffe_fpn_ms-1x_coco.py'\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=3)))\nmetainfo = {\n    'classes': ('person', 'bicycle', 'car'),\n    'palette': [\n        (220, 20, 60),\n        (119, 11, 32),\n        (0, 0, 142),\n    ]\n}\n\ntrain_dataloader = dict(dataset=dict(metainfo=metainfo))\nval_dataloader = dict(dataset=dict(metainfo=metainfo))\ntest_dataloader = dict(dataset=dict(metainfo=metainfo))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_bbox_mAP-0.398_20200504_163323-30042637.pth'  # noqa\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-1x_coco-person.py",
    "content": "_base_ = './faster-rcnn_r50-caffe_fpn_ms-1x_coco.py'\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=1)))\nmetainfo = {\n    'classes': ('person', ),\n    'palette': [\n        (220, 20, 60),\n    ]\n}\n\ntrain_dataloader = dict(dataset=dict(metainfo=metainfo))\nval_dataloader = dict(dataset=dict(metainfo=metainfo))\ntest_dataloader = dict(dataset=dict(metainfo=metainfo))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_bbox_mAP-0.398_20200504_163323-30042637.pth'  # noqa\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# train_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n_base_.train_dataloader.dataset.pipeline = train_pipeline\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-2x_coco.py",
    "content": "_base_ = './faster-rcnn_r50-caffe_fpn_ms-1x_coco.py'\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# param_scheduler = [\n#     dict(\n#         type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500), # noqa\n#     dict(\n#         type='MultiStepLR',\n#         begin=0,\n#         end=12,\n#         by_epoch=True,\n#         milestones=[16, 23],\n#         gamma=0.1)\n# ]\n_base_.param_scheduler[1].milestones = [16, 23]\n\ntrain_cfg = dict(max_epochs=24)\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-3x_coco.py",
    "content": "_base_ = 'faster-rcnn_r50_fpn_ms-3x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-90k_coco.py",
    "content": "_base_ = 'faster-rcnn_r50-caffe_fpn_ms-1x_coco.py'\n\nmax_iter = 90000\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=10000)\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50-tnr-pre_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\ncheckpoint = 'https://download.pytorch.org/models/resnet50-11ad3fa6.pth'\nmodel = dict(\n    backbone=dict(init_cfg=dict(type='Pretrained', checkpoint=checkpoint)))\n\n# `lr` and `weight_decay` have been searched to be optimal.\noptim_wrapper = dict(\n    optimizer=dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.1),\n    paramwise_cfg=dict(norm_decay_mult=0., bypass_duplicate=True))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_2x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../common/lsj-200e_coco-detection.py'\n]\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\nmodel = dict(data_preprocessor=dict(batch_augments=batch_augments))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.02 * 4, momentum=0.9, weight_decay=0.00004))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_amp-1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# optim_wrapper = dict(type='AmpOptimWrapper')\n_base_.optim_wrapper.type = 'AmpOptimWrapper'\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_bounded-iou_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            reg_decoded_bbox=True,\n            loss_bbox=dict(type='BoundedIoULoss', loss_weight=10.0))))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_ciou_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            reg_decoded_bbox=True,\n            loss_bbox=dict(type='CIoULoss', loss_weight=12.0))))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    # copied from configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py\n    neck=dict(\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        _delete_=True,  # ignore the unused old settings\n        type='FCOSHead',\n        # num_classes = 1 for rpn,\n        # if num_classes > 1, it will be set to 1 in\n        # TwoStageDetector automatically\n        num_classes=1,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    roi_head=dict(  # update featmap_strides\n        bbox_roi_extractor=dict(featmap_strides=[8, 16, 32, 64, 128])))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),  # Slowly increase lr, otherwise loss becomes NAN\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_giou_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            reg_decoded_bbox=True,\n            loss_bbox=dict(type='GIoULoss', loss_weight=10.0))))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_iou_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            reg_decoded_bbox=True,\n            loss_bbox=dict(type='IoULoss', loss_weight=10.0))))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_ms-3x_coco.py",
    "content": "_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_ohem_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(train_cfg=dict(rcnn=dict(sampler=dict(type='OHEMSampler'))))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_r50_fpn_soft-nms_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type='soft_nms', iou_threshold=0.5),\n            max_per_img=100)))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_x101-32x4d_fpn_2x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_x101-32x4d_fpn_ms-3x_coco.py",
    "content": "_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_x101-32x8d_fpn_ms-3x_coco.py",
    "content": "_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_x101-64x4d_fpn_2x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/faster_rcnn/faster-rcnn_x101-64x4d_fpn_ms-3x_coco.py",
    "content": "_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/faster_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Faster R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - RPN\n        - ResNet\n        - RoIPool\n    Paper:\n      URL: https://arxiv.org/abs/1506.01497\n      Title: \"Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks\"\n    README: configs/faster_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/faster_rcnn.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: faster-rcnn_r50-caffe-c4_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe_c4-1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 35.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_c4_1x_coco/faster_rcnn_r50_caffe_c4_1x_coco_20220316_150152-3f885b85.pth\n\n  - Name: faster-rcnn_r50-caffe-c4_mstrain_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe-c4_ms-1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 35.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_c4_mstrain_1x_coco/faster_rcnn_r50_caffe_c4_mstrain_1x_coco_20220316_150527-db276fed.pth\n\n  - Name: faster-rcnn_r50-caffe-dc5_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_1x_coco/faster_rcnn_r50_caffe_dc5_1x_coco_20201030_151909-531f0f43.pth\n\n  - Name: faster-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.8\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_1x_coco/faster_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.378_20200504_180032-c5925ee5.pth\n\n  - Name: faster-rcnn_r50_fpn_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.0\n      inference time (ms/im):\n        - value: 46.73\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth\n\n  - Name: faster-rcnn_r50_fpn_fp16_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50_fpn_amp-1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.4\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n        - Mixed Precision Training\n      inference time (ms/im):\n        - value: 34.72\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP16\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fp16/faster_rcnn_r50_fpn_fp16_1x_coco/faster_rcnn_r50_fpn_fp16_1x_coco_20200204-d4dc1471.pth\n\n  - Name: faster-rcnn_r50_fpn_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 4.0\n      inference time (ms/im):\n        - value: 46.73\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_2x_coco/faster_rcnn_r50_fpn_2x_coco_bbox_mAP-0.384_20200504_210434-a5d8aa15.pth\n\n  - Name: faster-rcnn_r101-caffe_fpn_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.7\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_caffe_fpn_1x_coco/faster_rcnn_r101_caffe_fpn_1x_coco_bbox_mAP-0.398_20200504_180057-b269e9dd.pth\n\n  - Name: faster-rcnn_r101_fpn_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 64.1\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_1x_coco/faster_rcnn_r101_fpn_1x_coco_20200130-f513f705.pth\n\n  - Name: faster-rcnn_r101_fpn_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r101_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 64.1\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_2x_coco/faster_rcnn_r101_fpn_2x_coco_bbox_mAP-0.398_20200504_210455-1d2dac9c.pth\n\n  - Name: faster-rcnn_x101-32x4d_fpn_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.2\n      inference time (ms/im):\n        - value: 72.46\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x4d_fpn_1x_coco/faster_rcnn_x101_32x4d_fpn_1x_coco_20200203-cff10310.pth\n\n  - Name: faster-rcnn_x101-32x4d_fpn_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_x101-32x4d_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.2\n      inference time (ms/im):\n        - value: 72.46\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x4d_fpn_2x_coco/faster_rcnn_x101_32x4d_fpn_2x_coco_bbox_mAP-0.412_20200506_041400-64a12c0b.pth\n\n  - Name: faster-rcnn_x101-64x4d_fpn_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.3\n      inference time (ms/im):\n        - value: 106.38\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_64x4d_fpn_1x_coco/faster_rcnn_x101_64x4d_fpn_1x_coco_20200204-833ee192.pth\n\n  - Name: faster-rcnn_x101-64x4d_fpn_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_x101-64x4d_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.3\n      inference time (ms/im):\n        - value: 106.38\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_64x4d_fpn_2x_coco/faster_rcnn_x101_64x4d_fpn_2x_coco_20200512_161033-5961fa95.pth\n\n  - Name: faster-rcnn_r50_fpn_iou_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50_fpn_iou_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_iou_1x_coco/faster_rcnn_r50_fpn_iou_1x_coco_20200506_095954-938e81f0.pth\n\n  - Name: faster-rcnn_r50_fpn_giou_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50_fpn_giou_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_giou_1x_coco-0eada910.pth\n\n  - Name: faster-rcnn_r50_fpn_bounded_iou_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50_fpn_bounded-iou_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_bounded_iou_1x_coco-98ad993b.pth\n\n  - Name: faster-rcnn_r50-caffe-dc5_mstrain_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_ms-1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_mstrain_1x_coco/faster_rcnn_r50_caffe_dc5_mstrain_1x_coco_20201028_233851-b33d21b9.pth\n\n  - Name: faster-rcnn_r50-caffe-dc5_mstrain_3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe-dc5_ms-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_dc5_mstrain_3x_coco/faster_rcnn_r50_caffe_dc5_mstrain_3x_coco_20201028_002107-34a53b2c.pth\n\n  - Name: faster-rcnn_r50-caffe_fpn_ms-2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-2x_coco.py\n    Metadata:\n      Training Memory (GB): 4.3\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_2x_coco/faster_rcnn_r50_caffe_fpn_mstrain_2x_coco_bbox_mAP-0.397_20200504_231813-10b2de58.pth\n\n  - Name: faster-rcnn_r50-caffe_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 3.7\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco/faster_rcnn_r50_caffe_fpn_mstrain_3x_coco_20210526_095054-1f77628b.pth\n\n  - Name: faster-rcnn_r50_fpn_mstrain_3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 3.9\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_mstrain_3x_coco/faster_rcnn_r50_fpn_mstrain_3x_coco_20210524_110822-e10bd31c.pth\n\n  - Name: faster-rcnn_r101-caffe_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r101-caffe_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.6\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_caffe_fpn_mstrain_3x_coco/faster_rcnn_r101_caffe_fpn_mstrain_3x_coco_20210526_095742-a7ae426d.pth\n\n  - Name: faster-rcnn_r101_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r101_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.8\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_mstrain_3x_coco/faster_rcnn_r101_fpn_mstrain_3x_coco_20210524_110822-4d4d2ca8.pth\n\n  - Name: faster-rcnn_x101-32x4d_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_x101-32x4d_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x4d_fpn_mstrain_3x_coco/faster_rcnn_x101_32x4d_fpn_mstrain_3x_coco_20210524_124151-16b9b260.pth\n\n  - Name: faster-rcnn_x101-32x8d_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_x101-32x8d_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 10.1\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_32x8d_fpn_mstrain_3x_coco/faster_rcnn_x101_32x8d_fpn_mstrain_3x_coco_20210604_182954-002e082a.pth\n\n  - Name: faster-rcnn_x101-64x4d_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_x101-64x4d_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 10.0\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_x101_64x4d_fpn_mstrain_3x_coco/faster_rcnn_x101_64x4d_fpn_mstrain_3x_coco_20210524_124528-26c63de6.pth\n\n  - Name: faster-rcnn_r50_fpn_tnr-pretrain_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/faster_rcnn/faster-rcnn_r50-tnr-pre_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.0\n      inference time (ms/im):\n        - value: 46.73\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_tnr-pretrain_1x_coco/faster_rcnn_r50_fpn_tnr-pretrain_1x_coco_20220320_085147-efedfda4.pth\n"
  },
  {
    "path": "configs/fcos/README.md",
    "content": "# FCOS\n\n> [FCOS: Fully Convolutional One-Stage Object Detection](https://arxiv.org/abs/1904.01355)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe propose a fully convolutional one-stage object detector (FCOS) to solve object detection in a per-pixel prediction fashion, analogue to semantic segmentation. Almost all state-of-the-art object detectors such as RetinaNet, SSD, YOLOv3, and Faster R-CNN rely on pre-defined anchor boxes. In contrast, our proposed detector FCOS is anchor box free, as well as proposal free. By eliminating the predefined set of anchor boxes, FCOS completely avoids the complicated computation related to anchor boxes such as calculating overlapping during training. More importantly, we also avoid all hyper-parameters related to anchor boxes, which are often very sensitive to the final detection performance. With the only post-processing non-maximum suppression (NMS), FCOS with ResNeXt-64x4d-101 achieves 44.7% in AP with single-model and single-scale testing, surpassing previous one-stage detectors with the advantage of being much simpler. For the first time, we demonstrate a much simpler and flexible detection framework achieving improved detection accuracy. We hope that the proposed FCOS framework can serve as a simple and strong alternative for many other instance-level tasks.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143882011-45b234bc-d04b-4bbe-a822-94bec057ac86.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone | Style | GN  | MS train | Tricks | DCN | Lr schd | Mem (GB) | Inf time (fps) | box AP |                                         Config                                         |                                                                                                                                                                                          Download                                                                                                                                                                                          |\n| :------: | :---: | :-: | :------: | :----: | :-: | :-----: | :------: | :------------: | :----: | :------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | caffe |  Y  |    N     |   N    |  N  |   1x    |   3.6    |      22.7      |  36.6  |                   [config](./fcos_r50-caffe_fpn_gn-head_1x_coco.py)                    |                                                        [model](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_1x_coco/fcos_r50_caffe_fpn_gn-head_1x_coco-821213aa.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_1x_coco/20201227_180009.log.json)                                                         |\n|   R-50   | caffe |  Y  |    N     |   Y    |  N  |   1x    |   3.7    |       -        |  38.7  |   [config](./fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco-0a0d75a8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco/20210105_135818.log.json)       |\n|   R-50   | caffe |  Y  |    N     |   Y    |  Y  |   1x    |   3.8    |       -        |  42.3  | [config](./fcos_r50-dcn-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_dcn_1x_coco/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_dcn_1x_coco-ae4d8b3d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_dcn_1x_coco/20210105_224556.log.json) |\n|  R-101   | caffe |  Y  |    N     |   N    |  N  |   1x    |   5.5    |      17.3      |  39.1  |                   [config](./fcos_r101-caffe_fpn_gn-head-1x_coco.py)                   |                                                       [model](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r101_caffe_fpn_gn-head_1x_coco/fcos_r101_caffe_fpn_gn-head_1x_coco-0e37b982.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r101_caffe_fpn_gn-head_1x_coco/20210103_155046.log.json)                                                       |\n\n| Backbone |  Style  | GN  | MS train | Lr schd | Mem (GB) | Inf time (fps) | box AP |                            Config                             |                                                                                                                                                            Download                                                                                                                                                            |\n| :------: | :-----: | :-: | :------: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   |  caffe  |  Y  |    Y     |   2x    |   2.6    |      22.9      |  38.5  | [config](./fcos_r50-caffe_fpn_gn-head_ms-640-800-2x_coco.py)  |  [model](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_mstrain_640-800_2x_coco/fcos_r50_caffe_fpn_gn-head_mstrain_640-800_2x_coco-d92ceeea.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_mstrain_640-800_2x_coco/20201227_161900.log.json)   |\n|  R-101   |  caffe  |  Y  |    Y     |   2x    |   5.5    |      17.3      |  40.8  | [config](./fcos_r101-caffe_fpn_gn-head_ms-640-800-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r101_caffe_fpn_gn-head_mstrain_640-800_2x_coco/fcos_r101_caffe_fpn_gn-head_mstrain_640-800_2x_coco-511424d6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r101_caffe_fpn_gn-head_mstrain_640-800_2x_coco/20210103_155046.log.json) |\n|  X-101   | pytorch |  Y  |    Y     |   2x    |   10.0   |      9.7       |  42.6  | [config](./fcos_x101-64x4d_fpn_gn-head_ms-640-800-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_x101_64x4d_fpn_gn-head_mstrain_640-800_2x_coco/fcos_x101_64x4d_fpn_gn-head_mstrain_640-800_2x_coco-ede514a8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_x101_64x4d_fpn_gn-head_mstrain_640-800_2x_coco/20210114_133041.log.json) |\n\n**Notes:**\n\n- The X-101 backbone is X-101-64x4d.\n- Tricks means setting `norm_on_bbox`, `centerness_on_reg`, `center_sampling` as `True`.\n- DCN means using `DCNv2` in both backbone and head.\n\n## Citation\n\n```latex\n@article{tian2019fcos,\n  title={FCOS: Fully Convolutional One-Stage Object Detection},\n  author={Tian, Zhi and Shen, Chunhua and Chen, Hao and He, Tong},\n  journal={arXiv preprint arXiv:1904.01355},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/fcos/fcos_r101-caffe_fpn_gn-head-1x_coco.py",
    "content": "_base_ = './fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron/resnet101_caffe')))\n"
  },
  {
    "path": "configs/fcos/fcos_r101-caffe_fpn_gn-head_ms-640-800-2x_coco.py",
    "content": "_base_ = './fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron/resnet101_caffe')))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# training schedule for 2x\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(type='ConstantLR', factor=1.0 / 3, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/fcos/fcos_r101_fpn_gn-head-center-normbbox-centeronreg-giou_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './fcos_r50_fpn_gn-head-center-normbbox-centeronreg-giou_8xb8-amp-lsj-200e_coco.py'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/fcos/fcos_r18_fpn_gn-head-center-normbbox-centeronreg-giou_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './fcos_r50_fpn_gn-head-center-normbbox-centeronreg-giou_8xb8-amp-lsj-200e_coco.py'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py",
    "content": "_base_ = 'fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# model setting\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    bbox_head=dict(\n        norm_on_bbox=True,\n        centerness_on_reg=True,\n        dcn_on_last_conv=False,\n        center_sampling=True,\n        conv_bias=True,\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.0)),\n    # training and testing settings\n    test_cfg=dict(nms=dict(type='nms', iou_threshold=0.6)))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3.0,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(clip_grad=None)\n"
  },
  {
    "path": "configs/fcos/fcos_r50-caffe_fpn_gn-head-center_1x_coco.py",
    "content": "_base_ = './fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# model settings\nmodel = dict(bbox_head=dict(center_sampling=True, center_sample_radius=1.5))\n"
  },
  {
    "path": "configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\nmodel = dict(\n    type='FCOS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[102.9801, 115.9465, 122.7717],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron/resnet50_caffe')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        num_outs=5,\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='FCOSHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # testing settings\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.5),\n        max_per_img=100))\n\n# learning rate\nparam_scheduler = [\n    dict(type='ConstantLR', factor=1.0 / 3, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.01),\n    paramwise_cfg=dict(bias_lr_mult=2., bias_decay_mult=0.),\n    clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/fcos/fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py",
    "content": "# TODO: Remove this config after benchmarking all related configs\n_base_ = 'fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# dataset settings\ntrain_dataloader = dict(batch_size=4, num_workers=4)\n"
  },
  {
    "path": "configs/fcos/fcos_r50-caffe_fpn_gn-head_ms-640-800-2x_coco.py",
    "content": "_base_ = './fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# training schedule for 2x\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(type='ConstantLR', factor=1.0 / 3, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/fcos/fcos_r50-dcn-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py",
    "content": "_base_ = 'fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# model settings\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    bbox_head=dict(\n        norm_on_bbox=True,\n        centerness_on_reg=True,\n        dcn_on_last_conv=True,\n        center_sampling=True,\n        conv_bias=True,\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.0)),\n    # training and testing settings\n    test_cfg=dict(nms=dict(type='nms', iou_threshold=0.6)))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3.0,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(clip_grad=None)\n"
  },
  {
    "path": "configs/fcos/fcos_r50_fpn_gn-head-center-normbbox-centeronreg-giou_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = '../common/lsj-200e_coco-detection.py'\n\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\n# model settings\nmodel = dict(\n    type='FCOS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        num_outs=5,\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='FCOSHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        norm_on_bbox=True,\n        centerness_on_reg=True,\n        dcn_on_last_conv=False,\n        center_sampling=True,\n        conv_bias=True,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # testing settings\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.01 * 4, momentum=0.9, weight_decay=0.00004),\n    paramwise_cfg=dict(bias_lr_mult=2., bias_decay_mult=0.),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/fcos/fcos_x101-64x4d_fpn_gn-head_ms-640-800-2x_coco.py",
    "content": "_base_ = './fcos_r50-caffe_fpn_gn-head_1x_coco.py'\n\n# model settings\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# training schedule for 2x\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(type='ConstantLR', factor=1.0 / 3, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/fcos/metafile.yml",
    "content": "Collections:\n  - Name: FCOS\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - Group Normalization\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1904.01355\n      Title: 'FCOS: Fully Convolutional One-Stage Object Detection'\n    README: configs/fcos/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/fcos.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: fcos_r50-caffe_fpn_gn-head_1x_coco\n    In Collection: FCOS\n    Config: configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.6\n      inference time (ms/im):\n        - value: 44.05\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_1x_coco/fcos_r50_caffe_fpn_gn-head_1x_coco-821213aa.pth\n\n  - Name: fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco\n    In Collection: FCOS\n    Config: configs/fcos/fcos_r50-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.7\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_1x_coco-0a0d75a8.pth\n\n  - Name: fcos_r50-dcn-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco\n    In Collection: FCOS\n    Config: configs/fcos/fcos_r50-dcn-caffe_fpn_gn-head-center-normbbox-centeronreg-giou_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.8\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_dcn_1x_coco/fcos_center-normbbox-centeronreg-giou_r50_caffe_fpn_gn-head_dcn_1x_coco-ae4d8b3d.pth\n\n  - Name: fcos_r101-caffe_fpn_gn-head-1x_coco\n    In Collection: FCOS\n    Config: configs/fcos/fcos_r101-caffe_fpn_gn-head-1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      inference time (ms/im):\n        - value: 57.8\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r101_caffe_fpn_gn-head_1x_coco/fcos_r101_caffe_fpn_gn-head_1x_coco-0e37b982.pth\n\n  - Name: fcos_r50-caffe_fpn_gn-head_ms-640-800-2x_coco\n    In Collection: FCOS\n    Config: configs/fcos/fcos_r50-caffe_fpn_gn-head_ms-640-800-2x_coco.py\n    Metadata:\n      Training Memory (GB): 2.6\n      inference time (ms/im):\n        - value: 43.67\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_mstrain_640-800_2x_coco/fcos_r50_caffe_fpn_gn-head_mstrain_640-800_2x_coco-d92ceeea.pth\n\n  - Name: fcos_r101-caffe_fpn_gn-head_ms-640-800-2x_coco\n    In Collection: FCOS\n    Config: configs/fcos/fcos_r101-caffe_fpn_gn-head_ms-640-800-2x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      inference time (ms/im):\n        - value: 57.8\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r101_caffe_fpn_gn-head_mstrain_640-800_2x_coco/fcos_r101_caffe_fpn_gn-head_mstrain_640-800_2x_coco-511424d6.pth\n\n  - Name: fcos_x101-64x4d_fpn_gn-head_ms-640-800-2x_coco\n    In Collection: FCOS\n    Config: configs/fcos/fcos_x101-64x4d_fpn_gn-head_ms-640-800-2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.0\n      inference time (ms/im):\n        - value: 103.09\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_x101_64x4d_fpn_gn-head_mstrain_640-800_2x_coco/fcos_x101_64x4d_fpn_gn-head_mstrain_640-800_2x_coco-ede514a8.pth\n"
  },
  {
    "path": "configs/foveabox/README.md",
    "content": "# FoveaBox\n\n> [FoveaBox: Beyond Anchor-based Object Detector](https://arxiv.org/abs/1904.03797)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present FoveaBox, an accurate, flexible, and completely anchor-free framework for object detection. While almost all state-of-the-art object detectors utilize predefined anchors to enumerate possible locations, scales and aspect ratios for the search of the objects, their performance and generalization ability are also limited to the design of anchors. Instead, FoveaBox directly learns the object existing possibility and the bounding box coordinates without anchor reference. This is achieved by: (a) predicting category-sensitive semantic maps for the object existing possibility, and (b) producing category-agnostic bounding box for each position that potentially contains an object. The scales of target boxes are naturally associated with feature pyramid representations. In FoveaBox, an instance is assigned to adjacent feature levels to make the model more accurate.We demonstrate its effectiveness on standard benchmarks and report extensive experimental analysis. Without bells and whistles, FoveaBox achieves state-of-the-art single model performance on the standard COCO and Pascal VOC object detection benchmark. More importantly, FoveaBox avoids all computation and hyper-parameters related to anchor boxes, which are often sensitive to the final detection performance. We believe the simple and effective approach will serve as a solid baseline and help ease future research for object detection.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143885497-332d38a7-b492-4f51-b9d2-ef9d4ad4412a.png\"/>\n</div>\n\n## Introduction\n\nFoveaBox is an accurate, flexible and completely anchor-free object detection system for object detection framework, as presented in our paper [https://arxiv.org/abs/1904.03797](https://arxiv.org/abs/1904.03797):\nDifferent from previous anchor-based methods, FoveaBox directly learns the object existing possibility and the bounding box coordinates without anchor reference. This is achieved by: (a) predicting category-sensitive semantic maps for the object existing possibility, and (b) producing category-agnostic bounding box for each position that potentially contains an object.\n\n## Results and Models\n\n### Results on R50/101-FPN\n\n| Backbone |  Style  | align | ms-train | Lr schd | Mem (GB) | Inf time (fps) | box AP |                               Config                                |                                                                                                                                                                                                        Download                                                                                                                                                                                                         |\n| :------: | :-----: | :---: | :------: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | pytorch |   N   |    N     |   1x    |   5.6    |      24.1      |  36.5  |              [config](./fovea_r50_fpn_4xb4-1x_coco.py)              |                                                               [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r50_fpn_4x4_1x_coco/fovea_r50_fpn_4x4_1x_coco_20200219-ee4d5303.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r50_fpn_4x4_1x_coco/fovea_r50_fpn_4x4_1x_coco_20200219_223025.log.json)                                                               |\n|   R-50   | pytorch |   N   |    N     |   2x    |   5.6    |       -        |  37.2  |              [config](./fovea_r50_fpn_4xb4-2x_coco.py)              |                                                               [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r50_fpn_4x4_2x_coco/fovea_r50_fpn_4x4_2x_coco_20200203-2df792b1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r50_fpn_4x4_2x_coco/fovea_r50_fpn_4x4_2x_coco_20200203_112043.log.json)                                                               |\n|   R-50   | pytorch |   Y   |    N     |   2x    |   8.1    |      19.4      |  37.9  |       [config](./fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py)       |                                   [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r50_fpn_gn-head_4x4_2x_coco/fovea_align_r50_fpn_gn-head_4x4_2x_coco_20200203-8987880d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r50_fpn_gn-head_4x4_2x_coco/fovea_align_r50_fpn_gn-head_4x4_2x_coco_20200203_134252.log.json)                                   |\n|   R-50   | pytorch |   Y   |    Y     |   2x    |   8.1    |      18.3      |  40.4  | [config](./fovea_r50_fpn_gn-head-align_ms-640-800-4xb4-2x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r50_fpn_gn-head_mstrain_640-800_4x4_2x_coco/fovea_align_r50_fpn_gn-head_mstrain_640-800_4x4_2x_coco_20200205-85ce26cb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r50_fpn_gn-head_mstrain_640-800_4x4_2x_coco/fovea_align_r50_fpn_gn-head_mstrain_640-800_4x4_2x_coco_20200205_112557.log.json)   |\n|  R-101   | pytorch |   N   |    N     |   1x    |   9.2    |      17.4      |  38.6  |             [config](./fovea_r101_fpn_4xb4-1x_coco.py)              |                                                             [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r101_fpn_4x4_1x_coco/fovea_r101_fpn_4x4_1x_coco_20200219-05e38f1c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r101_fpn_4x4_1x_coco/fovea_r101_fpn_4x4_1x_coco_20200219_011740.log.json)                                                             |\n|  R-101   | pytorch |   N   |    N     |   2x    |   11.7   |       -        |  40.0  |             [config](./fovea_r101_fpn_4xb4-2x_coco.py)              |                                                             [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r101_fpn_4x4_2x_coco/fovea_r101_fpn_4x4_2x_coco_20200208-02320ea4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r101_fpn_4x4_2x_coco/fovea_r101_fpn_4x4_2x_coco_20200208_202059.log.json)                                                             |\n|  R-101   | pytorch |   Y   |    N     |   2x    |   11.7   |      14.7      |  40.0  |      [config](./fovea_r101_fpn_gn-head-align_4xb4-2x_coco.py)       |                                 [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r101_fpn_gn-head_4x4_2x_coco/fovea_align_r101_fpn_gn-head_4x4_2x_coco_20200208-c39a027a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r101_fpn_gn-head_4x4_2x_coco/fovea_align_r101_fpn_gn-head_4x4_2x_coco_20200208_203337.log.json)                                 |\n|  R-101   | pytorch |   Y   |    Y     |   2x    |   11.7   |      14.7      |  42.0  | [config](./fovea_r101_fpn_gn-head-align_ms-640-800-4xb4-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r101_fpn_gn-head_mstrain_640-800_4x4_2x_coco/fovea_align_r101_fpn_gn-head_mstrain_640-800_4x4_2x_coco_20200208-649c5eb6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r101_fpn_gn-head_mstrain_640-800_4x4_2x_coco/fovea_align_r101_fpn_gn-head_mstrain_640-800_4x4_2x_coco_20200208_202124.log.json) |\n\n\\[1\\] *1x and 2x mean the model is trained for 12 and 24 epochs, respectively.* \\\n\\[2\\] *Align means utilizing deformable convolution to align the cls branch.* \\\n\\[3\\] *All results are obtained with a single model and without any test time data augmentation.*\\\n\\[4\\] *We use 4 GPUs for training.*\n\nAny pull requests or issues are welcome.\n\n## Citation\n\nPlease consider citing our paper in your publications if the project helps your research. BibTeX reference is as follows.\n\n```latex\n@article{kong2019foveabox,\n  title={FoveaBox: Beyond Anchor-based Object Detector},\n  author={Kong, Tao and Sun, Fuchun and Liu, Huaping and Jiang, Yuning and Shi, Jianbo},\n  journal={arXiv preprint arXiv:1904.03797},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/foveabox/fovea_r101_fpn_4xb4-1x_coco.py",
    "content": "_base_ = './fovea_r50_fpn_4xb4-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/foveabox/fovea_r101_fpn_4xb4-2x_coco.py",
    "content": "_base_ = './fovea_r50_fpn_4xb4-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/foveabox/fovea_r101_fpn_gn-head-align_4xb4-2x_coco.py",
    "content": "_base_ = './fovea_r50_fpn_4xb4-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    bbox_head=dict(\n        with_deform=True,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)))\n# learning policy\nmax_epochs = 24\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/foveabox/fovea_r101_fpn_gn-head-align_ms-640-800-4xb4-2x_coco.py",
    "content": "_base_ = './fovea_r50_fpn_4xb4-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    bbox_head=dict(\n        with_deform=True,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)))\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n# learning policy\nmax_epochs = 24\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/foveabox/fovea_r50_fpn_4xb4-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    type='FOVEA',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        num_outs=5,\n        add_extra_convs='on_input'),\n    bbox_head=dict(\n        type='FoveaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        base_edge_list=[16, 32, 64, 128, 256],\n        scale_ranges=((1, 64), (32, 128), (64, 256), (128, 512), (256, 2048)),\n        sigma=0.4,\n        with_deform=False,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=1.50,\n            alpha=0.4,\n            loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.11, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(),\n    test_cfg=dict(\n        nms_pre=1000,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.5),\n        max_per_img=100))\ntrain_dataloader = dict(batch_size=4, num_workers=4)\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/foveabox/fovea_r50_fpn_4xb4-2x_coco.py",
    "content": "_base_ = './fovea_r50_fpn_4xb4-1x_coco.py'\n# learning policy\nmax_epochs = 24\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py",
    "content": "_base_ = './fovea_r50_fpn_4xb4-1x_coco.py'\nmodel = dict(\n    bbox_head=dict(\n        with_deform=True,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)))\n# learning policy\nmax_epochs = 24\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/foveabox/fovea_r50_fpn_gn-head-align_ms-640-800-4xb4-2x_coco.py",
    "content": "_base_ = './fovea_r50_fpn_4xb4-1x_coco.py'\nmodel = dict(\n    bbox_head=dict(\n        with_deform=True,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)))\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n# learning policy\nmax_epochs = 24\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/foveabox/metafile.yml",
    "content": "Collections:\n  - Name: FoveaBox\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 4x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1904.03797\n      Title: 'FoveaBox: Beyond Anchor-based Object Detector'\n    README: configs/foveabox/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/fovea.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: fovea_r50_fpn_4xb4-1x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r50_fpn_4xb4-1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.6\n      inference time (ms/im):\n        - value: 41.49\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r50_fpn_4x4_1x_coco/fovea_r50_fpn_4x4_1x_coco_20200219-ee4d5303.pth\n\n  - Name: fovea_r50_fpn_4xb4-2x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r50_fpn_4xb4-2x_coco.py\n    Metadata:\n      Training Memory (GB): 5.6\n      inference time (ms/im):\n        - value: 41.49\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r50_fpn_4x4_2x_coco/fovea_r50_fpn_4x4_2x_coco_20200203-2df792b1.pth\n\n  - Name: fovea_r50_fpn_gn-head-align_4xb4-2x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r50_fpn_gn-head-align_4xb4-2x_coco.py\n    Metadata:\n      Training Memory (GB): 8.1\n      inference time (ms/im):\n        - value: 51.55\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r50_fpn_gn-head_4x4_2x_coco/fovea_align_r50_fpn_gn-head_4x4_2x_coco_20200203-8987880d.pth\n\n  - Name: fovea_r50_fpn_gn-head-align_ms-640-800-4xb4-2x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r50_fpn_gn-head-align_ms-640-800-4xb4-2x_coco.py\n    Metadata:\n      Training Memory (GB): 8.1\n      inference time (ms/im):\n        - value: 54.64\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r50_fpn_gn-head_mstrain_640-800_4x4_2x_coco/fovea_align_r50_fpn_gn-head_mstrain_640-800_4x4_2x_coco_20200205-85ce26cb.pth\n\n  - Name: fovea_r101_fpn_4xb4-1x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r101_fpn_4xb4-1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.2\n      inference time (ms/im):\n        - value: 57.47\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r101_fpn_4x4_1x_coco/fovea_r101_fpn_4x4_1x_coco_20200219-05e38f1c.pth\n\n  - Name: fovea_r101_fpn_4xb4-2x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r101_fpn_4xb4-2x_coco.py\n    Metadata:\n      Training Memory (GB): 11.7\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_r101_fpn_4x4_2x_coco/fovea_r101_fpn_4x4_2x_coco_20200208-02320ea4.pth\n\n  - Name: fovea_r101_fpn_gn-head-align_4xb4-2x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r101_fpn_gn-head-align_4xb4-2x_coco.py\n    Metadata:\n      Training Memory (GB): 11.7\n      inference time (ms/im):\n        - value: 68.03\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r101_fpn_gn-head_4x4_2x_coco/fovea_align_r101_fpn_gn-head_4x4_2x_coco_20200208-c39a027a.pth\n\n  - Name: fovea_r101_fpn_gn-head-align_ms-640-800-4xb4-2x_coco\n    In Collection: FoveaBox\n    Config: configs/foveabox/fovea_r101_fpn_gn-head-align_ms-640-800-4xb4-2x_coco.py\n    Metadata:\n      Training Memory (GB): 11.7\n      inference time (ms/im):\n        - value: 68.03\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/foveabox/fovea_align_r101_fpn_gn-head_mstrain_640-800_4x4_2x_coco/fovea_align_r101_fpn_gn-head_mstrain_640-800_4x4_2x_coco_20200208-649c5eb6.pth\n"
  },
  {
    "path": "configs/fpg/README.md",
    "content": "# FPG\n\n> [Feature Pyramid Grids](https://arxiv.org/abs/2004.03580)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nFeature pyramid networks have been widely adopted in the object detection literature to improve feature representations for better handling of variations in scale. In this paper, we present Feature Pyramid Grids (FPG), a deep multi-pathway feature pyramid, that represents the feature scale-space as a regular grid of parallel bottom-up pathways which are fused by multi-directional lateral connections. FPG can improve single-pathway feature pyramid networks by significantly increasing its performance at similar computation cost, highlighting importance of deep pyramid representations. In addition to its general and uniform structure, over complicated structures that have been found with neural architecture search, it also compares favorably against such approaches without relying on search. We hope that FPG with its uniform and effective nature can serve as a strong component for future work in object recognition.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143885611-85902399-2885-4a85-9126-9b9b7464ad08.png\"/>\n</div>\n\n## Results and Models\n\nWe benchmark the new training schedule (crop training, large batch, unfrozen BN, 50 epochs) introduced in NAS-FPN.\nAll backbones are Resnet-50 in pytorch style.\n\n|    Method    |    Neck    | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                           Config                           |                                                                                                                                                                             Download                                                                                                                                                                             |\n| :----------: | :--------: | :-----: | :------: | :------------: | :----: | :-----: | :--------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Faster R-CNN |    FPG     |   50e   |   20.0   |       -        |  42.3  |    -    |    [config](./faster-rcnn_r50_fpg_crop640-50e_coco.py)     |        [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpg_crop640_50e_coco/faster_rcnn_r50_fpg_crop640_50e_coco_20220311_011856-74109f42.pth) \\|               [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpg_crop640_50e_coco/faster_rcnn_r50_fpg_crop640_50e_coco_20220311_011856.log.json)        |\n| Faster R-CNN | FPG-chn128 |   50e   |   11.9   |       -        |  41.2  |    -    | [config](./faster-rcnn_r50_fpg-chn128_crop640-50e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpg-chn128_crop640_50e_coco/faster_rcnn_r50_fpg-chn128_crop640_50e_coco_20220311_011857-9376aa9d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpg-chn128_crop640_50e_coco/faster_rcnn_r50_fpg-chn128_crop640_50e_coco_20220311_011857.log.json) |\n| Faster R-CNN |    FPN     |   50e   |   20.0   |       -        |  38.9  |    -    |    [config](./faster-rcnn_r50_fpn_crop640-50e_coco.py)     |        [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpn_crop640_50e_coco/faster_rcnn_r50_fpn_crop640_50e_coco_20220311_011857-be7c9f42.pth) \\|               [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpn_crop640_50e_coco/faster_rcnn_r50_fpn_crop640_50e_coco_20220311_011857.log.json)        |\n|  Mask R-CNN  |    FPG     |   50e   |   23.2   |       -        |  43.0  |  38.1   |     [config](./mask-rcnn_r50_fpg_crop640-50e_coco.py)      |          [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpg_crop640_50e_coco/mask_rcnn_r50_fpg_crop640_50e_coco_20220311_011857-233b8334.pth) \\|                   [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpg_crop640_50e_coco/mask_rcnn_r50_fpg_crop640_50e_coco_20220311_011857.log.json)          |\n|  Mask R-CNN  | FPG-chn128 |   50e   |   15.3   |       -        |  41.7  |  37.1   |  [config](./mask-rcnn_r50_fpg-chn128_crop640-50e_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpg-chn128_crop640_50e_coco/mask_rcnn_r50_fpg-chn128_crop640_50e_coco_20220311_011859-043c9b4e.pth) \\|     [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpg-chn128_crop640_50e_coco/mask_rcnn_r50_fpg-chn128_crop640_50e_coco_20220311_011859.log.json)   |\n|  Mask R-CNN  |    FPN     |   50e   |   23.2   |       -        |  49.6  |  35.6   |     [config](./mask-rcnn_r50_fpn_crop640-50e_coco.py)      |          [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpn_crop640_50e_coco/mask_rcnn_r50_fpn_crop640_50e_coco_20220311_011855-a756664a.pth) \\|                   [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpn_crop640_50e_coco/mask_rcnn_r50_fpn_crop640_50e_coco_20220311_011855.log.json)          |\n|  RetinaNet   |    FPG     |   50e   |   20.8   |       -        |  40.5  |    -    |     [config](./retinanet_r50_fpg_crop640_50e_coco.py)      |          [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/retinanet_r50_fpg_crop640_50e_coco/retinanet_r50_fpg_crop640_50e_coco_20220311_110809-b0bcf5f4.pth) \\|                   [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/retinanet_r50_fpg_crop640_50e_coco/retinanet_r50_fpg_crop640_50e_coco_20220311_110809.log.json)          |\n|  RetinaNet   | FPG-chn128 |   50e   |   19.9   |       -        |  39.9  |    -    |  [config](./retinanet_r50_fpg-chn128_crop640_50e_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/fpg/retinanet_r50_fpg-chn128_crop640_50e_coco/retinanet_r50_fpg-chn128_crop640_50e_coco_20220313_104829-ee99a686.pth) \\|     [log](https://download.openmmlab.com/mmdetection/v2.0/fpg/retinanet_r50_fpg-chn128_crop640_50e_coco/retinanet_r50_fpg-chn128_crop640_50e_coco_20220313_104829.log.json)   |\n\n**Note**: Chn128 means to decrease the number of channels of features and convs from 256 (default) to 128 in\nNeck and BBox Head, which can greatly decrease memory consumption without sacrificing much precision.\n\n## Citation\n\n```latex\n@article{chen2020feature,\n  title={Feature pyramid grids},\n  author={Chen, Kai and Cao, Yuhang and Loy, Chen Change and Lin, Dahua and Feichtenhofer, Christoph},\n  journal={arXiv preprint arXiv:2004.03580},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/fpg/faster-rcnn_r50_fpg-chn128_crop640-50e_coco.py",
    "content": "_base_ = 'faster-rcnn_r50_fpg_crop640-50e_coco.py'\n\nnorm_cfg = dict(type='BN', requires_grad=True)\nmodel = dict(\n    neck=dict(out_channels=128, inter_channels=128),\n    rpn_head=dict(in_channels=128),\n    roi_head=dict(\n        bbox_roi_extractor=dict(out_channels=128),\n        bbox_head=dict(in_channels=128)))\n"
  },
  {
    "path": "configs/fpg/faster-rcnn_r50_fpg_crop640-50e_coco.py",
    "content": "_base_ = 'faster-rcnn_r50_fpn_crop640-50e_coco.py'\n\nnorm_cfg = dict(type='BN', requires_grad=True)\nmodel = dict(\n    neck=dict(\n        type='FPG',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        inter_channels=256,\n        num_outs=5,\n        stack_times=9,\n        paths=['bu'] * 9,\n        same_down_trans=None,\n        same_up_trans=dict(\n            type='conv',\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_lateral_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_down_trans=dict(\n            type='interpolation_conv',\n            mode='nearest',\n            kernel_size=3,\n            norm_cfg=norm_cfg,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        across_up_trans=None,\n        across_skip_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        output_trans=dict(\n            type='last_conv',\n            kernel_size=3,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        norm_cfg=norm_cfg,\n        skip_inds=[(0, 1, 2, 3), (0, 1, 2), (0, 1), (0, ), ()]))\n"
  },
  {
    "path": "configs/fpg/faster-rcnn_r50_fpn_crop640-50e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nnorm_cfg = dict(type='BN', requires_grad=True)\nimage_size = (640, 640)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\nmodel = dict(\n    data_preprocessor=dict(pad_size_divisor=64, batch_augments=batch_augments),\n    backbone=dict(norm_cfg=norm_cfg, norm_eval=False),\n    neck=dict(norm_cfg=norm_cfg),\n    roi_head=dict(bbox_head=dict(norm_cfg=norm_cfg)))\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        allow_negative_crop=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=image_size, keep_ratio=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8, num_workers=4, dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# learning policy\nmax_epochs = 50\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=2)\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[30, 40],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.08, momentum=0.9, weight_decay=0.0001),\n    paramwise_cfg=dict(norm_decay_mult=0, bypass_duplicate=True),\n    clip_grad=None)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/fpg/mask-rcnn_r50_fpg-chn128_crop640-50e_coco.py",
    "content": "_base_ = 'mask-rcnn_r50_fpg_crop640-50e_coco.py'\n\nmodel = dict(\n    neck=dict(out_channels=128, inter_channels=128),\n    rpn_head=dict(in_channels=128),\n    roi_head=dict(\n        bbox_roi_extractor=dict(out_channels=128),\n        bbox_head=dict(in_channels=128),\n        mask_roi_extractor=dict(out_channels=128),\n        mask_head=dict(in_channels=128)))\n"
  },
  {
    "path": "configs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py",
    "content": "_base_ = 'mask-rcnn_r50_fpn_crop640-50e_coco.py'\n\nnorm_cfg = dict(type='BN', requires_grad=True)\nmodel = dict(\n    neck=dict(\n        type='FPG',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        inter_channels=256,\n        num_outs=5,\n        stack_times=9,\n        paths=['bu'] * 9,\n        same_down_trans=None,\n        same_up_trans=dict(\n            type='conv',\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_lateral_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_down_trans=dict(\n            type='interpolation_conv',\n            mode='nearest',\n            kernel_size=3,\n            norm_cfg=norm_cfg,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        across_up_trans=None,\n        across_skip_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        output_trans=dict(\n            type='last_conv',\n            kernel_size=3,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        norm_cfg=norm_cfg,\n        skip_inds=[(0, 1, 2, 3), (0, 1, 2), (0, 1), (0, ), ()]))\n"
  },
  {
    "path": "configs/fpg/mask-rcnn_r50_fpn_crop640-50e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nnorm_cfg = dict(type='BN', requires_grad=True)\nimage_size = (640, 640)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\nmodel = dict(\n    data_preprocessor=dict(pad_size_divisor=64, batch_augments=batch_augments),\n    backbone=dict(norm_cfg=norm_cfg, norm_eval=False),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        norm_cfg=norm_cfg,\n        num_outs=5),\n    roi_head=dict(\n        bbox_head=dict(norm_cfg=norm_cfg), mask_head=dict(norm_cfg=norm_cfg)))\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        allow_negative_crop=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=image_size, keep_ratio=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8, num_workers=4, dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# learning policy\nmax_epochs = 50\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=2)\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[30, 40],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.08, momentum=0.9, weight_decay=0.0001),\n    paramwise_cfg=dict(norm_decay_mult=0, bypass_duplicate=True),\n    clip_grad=None)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/fpg/metafile.yml",
    "content": "Collections:\n  - Name: Feature Pyramid Grids\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Feature Pyramid Grids\n    Paper:\n      URL: https://arxiv.org/abs/2004.03580\n      Title: 'Feature Pyramid Grids'\n    README: configs/fpg/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.10.0/mmdet/models/necks/fpg.py#L101\n      Version: v2.10.0\n\nModels:\n  - Name: faster-rcnn_r50_fpg_crop640-50e_coco\n    In Collection: Feature Pyramid Grids\n    Config: configs/fpg/faster-rcnn_r50_fpg_crop640-50e_coco.py\n    Metadata:\n      Training Memory (GB): 20.0\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpg_crop640_50e_coco/faster_rcnn_r50_fpg_crop640_50e_coco_20220311_011856-74109f42.pth\n\n  - Name: faster-rcnn_r50_fpg-chn128_crop640-50e_coco\n    In Collection: Feature Pyramid Grids\n    Config: configs/fpg/faster-rcnn_r50_fpg-chn128_crop640-50e_coco.py\n    Metadata:\n      Training Memory (GB): 11.9\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fpg/faster_rcnn_r50_fpg-chn128_crop640_50e_coco/faster_rcnn_r50_fpg-chn128_crop640_50e_coco_20220311_011857-9376aa9d.pth\n\n  - Name: mask-rcnn_r50_fpg_crop640-50e_coco\n    In Collection: Feature Pyramid Grids\n    Config: configs/fpg/mask-rcnn_r50_fpg_crop640-50e_coco.py\n    Metadata:\n      Training Memory (GB): 23.2\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpg_crop640_50e_coco/mask_rcnn_r50_fpg_crop640_50e_coco_20220311_011857-233b8334.pth\n\n  - Name: mask-rcnn_r50_fpg-chn128_crop640-50e_coco\n    In Collection: Feature Pyramid Grids\n    Config: configs/fpg/mask-rcnn_r50_fpg-chn128_crop640-50e_coco.py\n    Metadata:\n      Training Memory (GB): 15.3\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fpg/mask_rcnn_r50_fpg-chn128_crop640_50e_coco/mask_rcnn_r50_fpg-chn128_crop640_50e_coco_20220311_011859-043c9b4e.pth\n\n  - Name: retinanet_r50_fpg_crop640_50e_coco\n    In Collection: Feature Pyramid Grids\n    Config: configs/fpg/retinanet_r50_fpg_crop640_50e_coco.py\n    Metadata:\n      Training Memory (GB): 20.8\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fpg/retinanet_r50_fpg_crop640_50e_coco/retinanet_r50_fpg_crop640_50e_coco_20220311_110809-b0bcf5f4.pth\n\n  - Name: retinanet_r50_fpg-chn128_crop640_50e_coco\n    In Collection: Feature Pyramid Grids\n    Config: configs/fpg/retinanet_r50_fpg-chn128_crop640_50e_coco.py\n    Metadata:\n      Training Memory (GB): 19.9\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n    Weights:  https://download.openmmlab.com/mmdetection/v2.0/fpg/retinanet_r50_fpg-chn128_crop640_50e_coco/retinanet_r50_fpg-chn128_crop640_50e_coco_20220313_104829-ee99a686.pth\n"
  },
  {
    "path": "configs/fpg/retinanet_r50_fpg-chn128_crop640_50e_coco.py",
    "content": "_base_ = 'retinanet_r50_fpg_crop640_50e_coco.py'\n\nmodel = dict(\n    neck=dict(out_channels=128, inter_channels=128),\n    bbox_head=dict(in_channels=128))\n"
  },
  {
    "path": "configs/fpg/retinanet_r50_fpg_crop640_50e_coco.py",
    "content": "_base_ = '../nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py'\n\nnorm_cfg = dict(type='BN', requires_grad=True)\nmodel = dict(\n    neck=dict(\n        _delete_=True,\n        type='FPG',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        inter_channels=256,\n        num_outs=5,\n        add_extra_convs=True,\n        start_level=1,\n        stack_times=9,\n        paths=['bu'] * 9,\n        same_down_trans=None,\n        same_up_trans=dict(\n            type='conv',\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_lateral_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_down_trans=dict(\n            type='interpolation_conv',\n            mode='nearest',\n            kernel_size=3,\n            norm_cfg=norm_cfg,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        across_up_trans=None,\n        across_skip_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        output_trans=dict(\n            type='last_conv',\n            kernel_size=3,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        norm_cfg=norm_cfg,\n        skip_inds=[(0, 1, 2, 3), (0, 1, 2), (0, 1), (0, ), ()]))\n\ntrain_cfg = dict(val_interval=2)\n"
  },
  {
    "path": "configs/free_anchor/README.md",
    "content": "# FreeAnchor\n\n> [FreeAnchor: Learning to Match Anchors for Visual Object Detection](https://arxiv.org/abs/1909.02466)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nModern CNN-based object detectors assign anchors for ground-truth objects under the restriction of object-anchor Intersection-over-Unit (IoU). In this study, we propose a learning-to-match approach to break IoU restriction, allowing objects to match anchors in a flexible manner. Our approach, referred to as FreeAnchor, updates hand-crafted anchor assignment to \"free\" anchor matching by formulating detector training as a maximum likelihood estimation (MLE) procedure. FreeAnchor targets at learning features which best explain a class of objects in terms of both classification and localization. FreeAnchor is implemented by optimizing detection customized likelihood and can be fused with CNN-based detectors in a plug-and-play manner. Experiments on COCO demonstrate that FreeAnchor consistently outperforms their counterparts with significant margins.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143886006-8374bf9b-28af-442e-9abf-eb16562bb7d3.png\"/>\n</div>\n\n## Results and Models\n\n|  Backbone   |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                      Config                      |                                                                                                                                                                                   Download                                                                                                                                                                                    |\n| :---------: | :-----: | :-----: | :------: | :------------: | :----: | :----------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50     | pytorch |   1x    |   4.9    |      18.4      |  38.7  |    [config](./freeanchor_r50_fpn_1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_r50_fpn_1x_coco/retinanet_free_anchor_r50_fpn_1x_coco_20200130-0f67375f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_r50_fpn_1x_coco/retinanet_free_anchor_r50_fpn_1x_coco_20200130_095625.log.json)               |\n|    R-101    | pytorch |   1x    |   6.8    |      14.9      |  40.3  |    [config](./freeanchor_r101_fpn_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_r101_fpn_1x_coco/retinanet_free_anchor_r101_fpn_1x_coco_20200130-358324e6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_r101_fpn_1x_coco/retinanet_free_anchor_r101_fpn_1x_coco_20200130_100723.log.json)             |\n| X-101-32x4d | pytorch |   1x    |   8.1    |      11.1      |  41.9  | [config](./freeanchor_x101-32x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_x101_32x4d_fpn_1x_coco/retinanet_free_anchor_x101_32x4d_fpn_1x_coco_20200130-d4846968.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_x101_32x4d_fpn_1x_coco/retinanet_free_anchor_x101_32x4d_fpn_1x_coco_20200130_095627.log.json) |\n\n**Notes:**\n\n- We use 8 GPUs with 2 images/GPU.\n- For more settings and models, please refer to the [official repo](https://github.com/zhangxiaosong18/FreeAnchor).\n\n## Citation\n\n```latex\n@inproceedings{zhang2019freeanchor,\n  title   =  {{FreeAnchor}: Learning to Match Anchors for Visual Object Detection},\n  author  =  {Zhang, Xiaosong and Wan, Fang and Liu, Chang and Ji, Rongrong and Ye, Qixiang},\n  booktitle =  {Neural Information Processing Systems},\n  year    =  {2019}\n}\n```\n"
  },
  {
    "path": "configs/free_anchor/freeanchor_r101_fpn_1x_coco.py",
    "content": "_base_ = './freeanchor_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/free_anchor/freeanchor_r50_fpn_1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    bbox_head=dict(\n        _delete_=True,\n        type='FreeAnchorRetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.11, loss_weight=0.75)))\n\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/free_anchor/freeanchor_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './freeanchor_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/free_anchor/metafile.yml",
    "content": "Collections:\n  - Name: FreeAnchor\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FreeAnchor\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1909.02466\n      Title: 'FreeAnchor: Learning to Match Anchors for Visual Object Detection'\n    README: configs/free_anchor/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/dense_heads/free_anchor_retina_head.py#L10\n      Version: v2.0.0\n\nModels:\n  - Name: freeanchor_r50_fpn_1x_coco\n    In Collection: FreeAnchor\n    Config: configs/free_anchor/freeanchor_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.9\n      inference time (ms/im):\n        - value: 54.35\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_r50_fpn_1x_coco/retinanet_free_anchor_r50_fpn_1x_coco_20200130-0f67375f.pth\n\n  - Name: freeanchor_r101_fpn_1x_coco\n    In Collection: FreeAnchor\n    Config: configs/free_anchor/freeanchor_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.8\n      inference time (ms/im):\n        - value: 67.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_r101_fpn_1x_coco/retinanet_free_anchor_r101_fpn_1x_coco_20200130-358324e6.pth\n\n  - Name: freeanchor_x101-32x4d_fpn_1x_coco\n    In Collection: FreeAnchor\n    Config: configs/free_anchor/freeanchor_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.1\n      inference time (ms/im):\n        - value: 90.09\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/free_anchor/retinanet_free_anchor_x101_32x4d_fpn_1x_coco/retinanet_free_anchor_x101_32x4d_fpn_1x_coco_20200130-d4846968.pth\n"
  },
  {
    "path": "configs/fsaf/README.md",
    "content": "# FSAF\n\n> [Feature Selective Anchor-Free Module for Single-Shot Object Detection](https://arxiv.org/abs/1903.00621)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe motivate and present feature selective anchor-free (FSAF) module, a simple and effective building block for single-shot object detectors. It can be plugged into single-shot detectors with feature pyramid structure. The FSAF module addresses two limitations brought up by the conventional anchor-based detection: 1) heuristic-guided feature selection; 2) overlap-based anchor sampling. The general concept of the FSAF module is online feature selection applied to the training of multi-level anchor-free branches. Specifically, an anchor-free branch is attached to each level of the feature pyramid, allowing box encoding and decoding in the anchor-free manner at an arbitrary level. During training, we dynamically assign each instance to the most suitable feature level. At the time of inference, the FSAF module can work jointly with anchor-based branches by outputting predictions in parallel. We instantiate this concept with simple implementations of anchor-free branches and online feature selection strategy. Experimental results on the COCO detection track show that our FSAF module performs better than anchor-based counterparts while being faster. When working jointly with anchor-based branches, the FSAF module robustly improves the baseline RetinaNet by a large margin under various settings, while introducing nearly free inference overhead. And the resulting best model can achieve a state-of-the-art 44.6% mAP, outperforming all existing single-shot detectors on COCO.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143887228-c790b542-c0f7-4113-8597-12250c50fe8f.png\"/>\n</div>\n\n## Introduction\n\nFSAF is an anchor-free method published in CVPR2019 ([https://arxiv.org/pdf/1903.00621.pdf](https://arxiv.org/pdf/1903.00621.pdf)).\nActually it is equivalent to the anchor-based method with only one anchor at each feature map position in each FPN level.\nAnd this is how we implemented it.\nOnly the anchor-free branch is released for its better compatibility with the current framework and less computational budget.\n\nIn the original paper, feature maps within the central 0.2-0.5 area of a gt box are tagged as ignored. However,\nit is empirically found that a hard threshold (0.2-0.2) gives a further gain on the performance. (see the table below)\n\n## Results and Models\n\n### Results on R50/R101/X101-FPN\n\n| Backbone | ignore range | ms-train | Lr schd | Train Mem (GB) | Train time (s/iter) | Inf time (fps) |   box AP    |                   Config                   |                                                                                                                                                                    Download                                                                                                                                                                     |\n| :------: | :----------: | :------: | :-----: | :------------: | :-----------------: | :------------: | :---------: | :----------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   |   0.2-0.5    |    N     |   1x    |      3.15      |        0.43         |      12.3      | 36.0 (35.9) |                                            | [model](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_pscale0.2_nscale0.5_r50_fpn_1x_coco/fsaf_pscale0.2_nscale0.5_r50_fpn_1x_coco_20200715-b555b0e0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_pscale0.2_nscale0.5_r50_fpn_1x_coco/fsaf_pscale0.2_nscale0.5_r50_fpn_1x_coco_20200715_094657.log.json) |\n|   R-50   |   0.2-0.2    |    N     |   1x    |      3.15      |        0.43         |      13.0      |    37.4     |    [config](./fsaf_r50_fpn_1x_coco.py)     |                                             [model](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_r50_fpn_1x_coco/fsaf_r50_fpn_1x_coco-94ccc51f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_r50_fpn_1x_coco/fsaf_r50_fpn_1x_coco_20200428_072327.log.json)                                              |\n|  R-101   |   0.2-0.2    |    N     |   1x    |      5.08      |        0.58         |      10.8      | 39.3 (37.9) |    [config](./fsaf_r101_fpn_1x_coco.py)    |                                           [model](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_r101_fpn_1x_coco/fsaf_r101_fpn_1x_coco-9e71098f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_r101_fpn_1x_coco/fsaf_r101_fpn_1x_coco_20200428_160348.log.json)                                            |\n|  X-101   |   0.2-0.2    |    N     |   1x    |      9.38      |        1.23         |      5.6       | 42.4 (41.0) | [config](./fsaf_x101-64x4d_fpn_1x_coco.py) |                               [model](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_x101_64x4d_fpn_1x_coco/fsaf_x101_64x4d_fpn_1x_coco-e3f6e6fd.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_x101_64x4d_fpn_1x_coco/fsaf_x101_64x4d_fpn_1x_coco_20200428_160424.log.json)                                |\n\n**Notes:**\n\n- *1x means the model is trained for 12 epochs.*\n- *AP values in the brackets represent those reported in the original paper.*\n- *All results are obtained with a single model and single-scale test.*\n- *X-101 backbone represents ResNext-101-64x4d.*\n- *All pretrained backbones use pytorch style.*\n- *All models are trained on 8 Titan-XP gpus and tested on a single gpu.*\n\n## Citation\n\nBibTeX reference is as follows.\n\n```latex\n@inproceedings{zhu2019feature,\n  title={Feature Selective Anchor-Free Module for Single-Shot Object Detection},\n  author={Zhu, Chenchen and He, Yihui and Savvides, Marios},\n  booktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition},\n  pages={840--849},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/fsaf/fsaf_r101_fpn_1x_coco.py",
    "content": "_base_ = './fsaf_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/fsaf/fsaf_r50_fpn_1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    type='FSAF',\n    bbox_head=dict(\n        type='FSAFHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        reg_decoded_bbox=True,\n        # Only anchor-free branch is implemented. The anchor generator only\n        #  generates 1 anchor at each feature point, as a substitute of the\n        #  grid of features.\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=1,\n            scales_per_octave=1,\n            ratios=[1.0],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(_delete_=True, type='TBLRBBoxCoder', normalizer=4.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0,\n            reduction='none'),\n        loss_bbox=dict(\n            _delete_=True,\n            type='IoULoss',\n            eps=1e-6,\n            loss_weight=1.0,\n            reduction='none')),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            _delete_=True,\n            type='CenterRegionAssigner',\n            pos_scale=0.2,\n            neg_scale=0.2,\n            min_pos_iof=0.01),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False))\n\noptim_wrapper = dict(clip_grad=dict(max_norm=10, norm_type=2))\n"
  },
  {
    "path": "configs/fsaf/fsaf_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './fsaf_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/fsaf/metafile.yml",
    "content": "Collections:\n  - Name: FSAF\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x Titan-XP GPUs\n      Architecture:\n        - FPN\n        - FSAF\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1903.00621\n      Title: 'Feature Selective Anchor-Free Module for Single-Shot Object Detection'\n    README: configs/fsaf/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/detectors/fsaf.py#L6\n      Version: v2.1.0\n\nModels:\n  - Name: fsaf_r50_fpn_1x_coco\n    In Collection: FSAF\n    Config: configs/fsaf/fsaf_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.15\n      inference time (ms/im):\n        - value: 76.92\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_r50_fpn_1x_coco/fsaf_r50_fpn_1x_coco-94ccc51f.pth\n\n  - Name: fsaf_r101_fpn_1x_coco\n    In Collection: FSAF\n    Config: configs/fsaf/fsaf_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.08\n      inference time (ms/im):\n        - value: 92.59\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_r101_fpn_1x_coco/fsaf_r101_fpn_1x_coco-9e71098f.pth\n\n  - Name: fsaf_x101-64x4d_fpn_1x_coco\n    In Collection: FSAF\n    Config: configs/fsaf/fsaf_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.38\n      inference time (ms/im):\n        - value: 178.57\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fsaf/fsaf_x101_64x4d_fpn_1x_coco/fsaf_x101_64x4d_fpn_1x_coco-e3f6e6fd.pth\n"
  },
  {
    "path": "configs/gcnet/README.md",
    "content": "# GCNet\n\n> [GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond](https://arxiv.org/abs/1904.11492)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe 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.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143887418-c5d2c906-d6c9-415d-bce7-c5355c20a10a.png\" height=\"300\"/>\n</div>\n\n## Introduction\n\nBy [Yue Cao](http://yue-cao.me), [Jiarui Xu](http://jerryxu.net), [Stephen Lin](https://scholar.google.com/citations?user=c3PYmxUAAAAJ&hl=en), Fangyun Wei, [Han Hu](https://sites.google.com/site/hanhushomepage/).\n\nWe provide config files to reproduce the results in the paper for\n[\"GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond\"](https://arxiv.org/abs/1904.11492) on COCO object detection.\n\n**GCNet** is initially described in [arxiv](https://arxiv.org/abs/1904.11492). Via absorbing advantages of Non-Local Networks (NLNet) and Squeeze-Excitation Networks (SENet),  GCNet provides a simple, fast and effective approach for global context modeling, which generally outperforms both NLNet and SENet on major benchmarks for various recognition tasks.\n\n## Results and Models\n\nThe results on COCO 2017val are shown in the below table.\n\n| Backbone  | Model |    Context     | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                         Config                          |                                                                                                                                                                       Download                                                                                                                                                                       |\n| :-------: | :---: | :------------: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN  | Mask  | GC(c3-c5, r16) |   1x    |   5.0    |                |  39.7  |  35.9   | [config](./mask-rcnn_r50-gcb-r16-c3-c5_fpn_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_r16_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_r16_gcb_c3-c5_1x_coco_20200515_211915-187da160.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_r16_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_r16_gcb_c3-c5_1x_coco_20200515_211915.log.json) |\n| R-50-FPN  | Mask  | GC(c3-c5, r4)  |   1x    |   5.1    |      15.0      |  39.9  |  36.0   |  [config](./mask-rcnn_r50-gcb-r4-c3-c5_fpn_1x_coco.py)  |      [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco_20200204-17235656.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco_20200204_024626.log.json)       |\n| R-101-FPN | Mask  | GC(c3-c5, r16) |   1x    |   7.6    |      11.4      |  41.3  |  37.2   | [config](./mask-rcnn_r101-gcb-r16-c3-c5_fpn_1x_coco.py) |  [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_r16_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_r16_gcb_c3-c5_1x_coco_20200205-e58ae947.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_r16_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_r16_gcb_c3-c5_1x_coco_20200205_192835.log.json)   |\n| R-101-FPN | Mask  | GC(c3-c5, r4)  |   1x    |   7.8    |      11.6      |  42.2  |  37.8   | [config](./mask-rcnn_r101-gcb-r4-c3-c5_fpn_1x_coco.py)  |    [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_r4_gcb_c3-c5_1x_coco_20200206-af22dc9d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_r4_gcb_c3-c5_1x_coco_20200206_112128.log.json)     |\n\n| Backbone  |      Model       |    Context     | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                                          Config                                          |                                                                                                                                                                                                                                                             Download                                                                                                                                                                                                                                                             |\n| :-------: | :--------------: | :------------: | :-----: | :------: | :------------: | :----: | :-----: | :--------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN  |       Mask       |       -        |   1x    |   4.4    |      16.6      |  38.4  |  34.6   |                     [config](./mask-rcnn_r50-syncbn_fpn_1x_coco.py)                      |                                                                                      [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_1x_coco_20200202-bb3eb55c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_1x_coco_20200202_214122.log.json)                                                                                       |\n| R-50-FPN  |       Mask       | GC(c3-c5, r16) |   1x    |   5.0    |      15.5      |  40.4  |  36.2   |              [config](./mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py)               |                                                          [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200202-587b99aa.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200202_174907.log.json)                                                           |\n| R-50-FPN  |       Mask       | GC(c3-c5, r4)  |   1x    |   5.1    |      15.1      |  40.7  |  36.5   |               [config](./mask-rcnn_r50-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py)               |                                                            [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200202-50b90e5c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200202_085547.log.json)                                                             |\n| R-101-FPN |       Mask       |       -        |   1x    |   6.4    |      13.3      |  40.5  |  36.3   |                     [config](./mask-rcnn_r101-syncbn_fpn_1x_coco.py)                     |                                                                                    [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_1x_coco_20200210-81658c8a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_1x_coco_20200210_220422.log.json)                                                                                     |\n| R-101-FPN |       Mask       | GC(c3-c5, r16) |   1x    |   7.6    |      12.0      |  42.2  |  37.8   |              [config](./mask-rcnn_r101-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py)              |                                                        [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200207-945e77ca.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200207_015330.log.json)                                                         |\n| R-101-FPN |       Mask       | GC(c3-c5, r4)  |   1x    |   7.8    |      11.8      |  42.2  |  37.8   |              [config](./mask-rcnn_r101-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py)               |                                                          [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200206-8407a3f0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200206_142508.log.json)                                                           |\n| X-101-FPN |       Mask       |       -        |   1x    |   7.6    |      11.3      |  42.4  |  37.7   |                  [config](./mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco.py)                  |                                                                        [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco_20200211-7584841c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco_20200211_054326.log.json)                                                                         |\n| X-101-FPN |       Mask       | GC(c3-c5, r16) |   1x    |   8.8    |      9.8       |  43.5  |  38.6   |           [config](./mask-rcnn_x101-32x4d-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py)           |                                            [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200211-cbed3d2c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200211_164715.log.json)                                             |\n| X-101-FPN |       Mask       | GC(c3-c5, r4)  |   1x    |   9.0    |      9.7       |  43.9  |  39.0   |           [config](./mask-rcnn_x101-32x4d-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py)            |                                              [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200212-68164964.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200212_070942.log.json)                                               |\n| X-101-FPN |   Cascade Mask   |       -        |   1x    |   9.2    |      8.4       |  44.7  |  38.6   |              [config](./cascade-mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco.py)              |                                                        [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco_20200310-d5ad2a5e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco_20200310_115217.log.json)                                                         |\n| X-101-FPN |   Cascade Mask   | GC(c3-c5, r16) |   1x    |   10.3   |      7.7       |  46.2  |  39.7   |       [config](./cascade-mask-rcnn_x101-32x4d-syncbn-r16-gcb-c3-c5_fpn_1x_coco.py)       |                            [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200211-10bf2463.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200211_184154.log.json)                             |\n| X-101-FPN |   Cascade Mask   | GC(c3-c5, r4)  |   1x    |   10.6   |                |  46.4  |  40.1   |       [config](./cascade-mask-rcnn_x101-32x4d-syncbn-r4-gcb-c3-c5_fpn_1x_coco.py)        |                           [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200703_180653-ed035291.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200703_180653.log.json)                           |\n| X-101-FPN | DCN Cascade Mask |       -        |   1x    |          |                |  47.5  |  40.9   |        [config](./cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5_fpn_1x_coco.py)        |                             [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_1x_coco_20210615_211019-abbc39ea.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_1x_coco_20210615_211019.log.json)                             |\n| X-101-FPN | DCN Cascade Mask | GC(c3-c5, r16) |   1x    |          |                |  48.0  |  41.3   | [config](./cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r16-gcb-c3-c5_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r16_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r16_gcb_c3-c5_1x_coco_20210615_215648-44aa598a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r16_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r16_gcb_c3-c5_1x_coco_20210615_215648.log.json) |\n| X-101-FPN | DCN Cascade Mask | GC(c3-c5, r4)  |   1x    |          |                |  47.9  |  41.1   | [config](./cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r4-gcb-c3-c5_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r4_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r4_gcb_c3-c5_1x_coco_20210615_161851-720338ec.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r4_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r4_gcb_c3-c5_1x_coco_20210615_161851.log.json)   |\n\n**Notes:**\n\n- The `SyncBN` is added in the backbone for all models in **Table 2**.\n- `GC` denotes Global Context (GC) block is inserted after 1x1 conv of backbone.\n- `DCN` denotes replace 3x3 conv with 3x3 Deformable Convolution in `c3-c5` stages of backbone.\n- `r4` and `r16` denote ratio 4 and ratio 16 in GC block respectively.\n\n## Citation\n\n```latex\n@article{cao2019GCNet,\n  title={GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond},\n  author={Cao, Yue and Xu, Jiarui and Lin, Stephen and Wei, Fangyun and Hu, Han},\n  journal={arXiv preprint arXiv:1904.11492},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r16-gcb-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../dcn/cascade-mask-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r4-gcb-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../dcn/cascade-mask-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 4),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../dcn/cascade-mask-rcnn_x101-32x4d-dconv-c3-c5_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True), norm_eval=False))\n"
  },
  {
    "path": "configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-r16-gcb-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-r4-gcb-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 4),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True), norm_eval=False))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r101-gcb-r16-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(plugins=[\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16),\n            stages=(False, True, True, True),\n            position='after_conv3')\n    ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r101-gcb-r4-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(plugins=[\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 4),\n            stages=(False, True, True, True),\n            position='after_conv3')\n    ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r101-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r101-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 4),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r101-syncbn_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True), norm_eval=False))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r50-gcb-r16-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(plugins=[\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16),\n            stages=(False, True, True, True),\n            position='after_conv3')\n    ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r50-gcb-r4-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(plugins=[\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 4),\n            stages=(False, True, True, True),\n            position='after_conv3')\n    ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r50-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 4),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_r50-syncbn_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True), norm_eval=False))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_x101-32x4d-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_x101-32x4d-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        plugins=[\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 4),\n                stages=(False, True, True, True),\n                position='after_conv3')\n        ]))\n"
  },
  {
    "path": "configs/gcnet/mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        norm_cfg=dict(type='SyncBN', requires_grad=True), norm_eval=False))\n"
  },
  {
    "path": "configs/gcnet/metafile.yml",
    "content": "Collections:\n  - Name: GCNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Global Context Block\n        - FPN\n        - RPN\n        - ResNet\n        - ResNeXt\n    Paper:\n      URL: https://arxiv.org/abs/1904.11492\n      Title: 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond'\n    README: configs/gcnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/ops/context_block.py#L13\n      Version: v2.0.0\n\nModels:\n  - Name: mask-rcnn_r50_fpn_r16_gcb_c3-c5_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r50-gcb-r16-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  35.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_r16_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_r16_gcb_c3-c5_1x_coco_20200515_211915-187da160.pth\n\n  - Name: mask-rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r50-gcb-r4-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.1\n      inference time (ms/im):\n        - value: 66.67\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_r4_gcb_c3-c5_1x_coco_20200204-17235656.pth\n\n  - Name: mask-rcnn_r101-gcb-r16-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r101-gcb-r16-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      inference time (ms/im):\n        - value: 87.72\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_r16_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_r16_gcb_c3-c5_1x_coco_20200205-e58ae947.pth\n\n  - Name: mask-rcnn_r101-gcb-r4-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r101-gcb-r4-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      inference time (ms/im):\n        - value: 86.21\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_r4_gcb_c3-c5_1x_coco_20200206-af22dc9d.pth\n\n  - Name: mask-rcnn_r50_fpn_syncbn-backbone_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r50-syncbn_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      inference time (ms/im):\n        - value: 60.24\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  34.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_1x_coco_20200202-bb3eb55c.pth\n\n  - Name: mask-rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r50-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      inference time (ms/im):\n        - value: 64.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200202-587b99aa.pth\n\n  - Name: mask-rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r50-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.1\n      inference time (ms/im):\n        - value: 66.23\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200202-50b90e5c.pth\n\n  - Name: mask-rcnn_r101-syncbn_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r101-syncbn_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      inference time (ms/im):\n        - value: 75.19\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_1x_coco_20200210-81658c8a.pth\n\n  - Name: mask-rcnn_r101-syncbn-gcb-r16-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r101-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      inference time (ms/im):\n        - value: 83.33\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200207-945e77ca.pth\n\n  - Name: mask-rcnn_r101-syncbn-gcb-r4-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_r101-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      inference time (ms/im):\n        - value: 84.75\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200206-8407a3f0.pth\n\n  - Name: mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      inference time (ms/im):\n        - value: 88.5\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco_20200211-7584841c.pth\n\n  - Name: mask-rcnn_x101-32x4d-syncbn-gcb-r16-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_x101-32x4d-syncbn-gcb-r16-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.8\n      inference time (ms/im):\n        - value: 102.04\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200211-cbed3d2c.pth\n\n  - Name: mask-rcnn_x101-32x4d-syncbn-gcb-r4-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/mask-rcnn_x101-32x4d-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.0\n      inference time (ms/im):\n        - value: 103.09\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200212-68164964.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.2\n      inference time (ms/im):\n        - value: 119.05\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_1x_coco_20200310-d5ad2a5e.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d-syncbn-r16-gcb-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-r16-gcb-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.3\n      inference time (ms/im):\n        - value: 129.87\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  39.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r16_gcb_c3-c5_1x_coco_20200211-10bf2463.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d-syncbn-r4-gcb-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-r4-gcb-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.6\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:    40.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200703_180653-ed035291.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  40.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_1x_coco_20210615_211019-abbc39ea.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r16-gcb-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r16-gcb-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 48.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r16_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r16_gcb_c3-c5_1x_coco_20210615_215648-44aa598a.pth\n\n  - Name: cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r4-gcb-c3-c5_fpn_1x_coco\n    In Collection: GCNet\n    Config: configs/gcnet/cascade-mask-rcnn_x101-32x4d-syncbn-dconv-c3-c5-r4-gcb-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:   41.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gcnet/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r4_gcb_c3-c5_1x_coco/cascade_mask_rcnn_x101_32x4d_fpn_syncbn-backbone_dconv_c3-c5_r4_gcb_c3-c5_1x_coco_20210615_161851-720338ec.pth\n"
  },
  {
    "path": "configs/gfl/README.md",
    "content": "# GFL\n\n> [Generalized Focal Loss: Learning Qualified and Distributed Bounding Boxes for Dense Object Detection](https://arxiv.org/abs/2006.04388)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nOne-stage detector basically formulates object detection as dense classification and localization. The classification is usually optimized by Focal Loss and the box location is commonly learned under Dirac delta distribution. A recent trend for one-stage detectors is to introduce an individual prediction branch to estimate the quality of localization, where the predicted quality facilitates the classification to improve detection performance. This paper delves into the representations of the above three fundamental elements: quality estimation, classification and localization. Two problems are discovered in existing practices, including (1) the inconsistent usage of the quality estimation and classification between training and inference and (2) the inflexible Dirac delta distribution for localization when there is ambiguity and uncertainty in complex scenes. To address the problems, we design new representations for these elements. Specifically, we merge the quality estimation into the class prediction vector to form a joint representation of localization quality and classification, and use a vector to represent arbitrary distribution of box locations. The improved representations eliminate the inconsistency risk and accurately depict the flexible distribution in real data, but contain continuous labels, which is beyond the scope of Focal Loss. We then propose Generalized Focal Loss (GFL) that generalizes Focal Loss from its discrete form to the continuous version for successful optimization. On COCO test-dev, GFL achieves 45.0% AP using ResNet-101 backbone, surpassing state-of-the-art SAPD (43.5%) and ATSS (43.6%) with higher or comparable inference speed, under the same backbone and training settings. Notably, our best model can achieve a single-model single-scale AP of 48.2%, at 10 FPS on a single 2080Ti GPU.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143887865-44dc384d-ba0d-44e8-b3d7-d5fa837838cf.png\"/>\n</div>\n\n## Results and Models\n\n|     Backbone      |  Style  | Lr schd | Multi-scale Training | Inf time (fps) | box AP |                          Config                          |                                                                                                                                                                                   Download                                                                                                                                                                                   |\n| :---------------: | :-----: | :-----: | :------------------: | :------------: | :----: | :------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|       R-50        | pytorch |   1x    |          No          |      19.5      |  40.2  |            [config](./gfl_r50_fpn_1x_coco.py)            |                                                       [model](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r50_fpn_1x_coco/gfl_r50_fpn_1x_coco_20200629_121244-25944287.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r50_fpn_1x_coco/gfl_r50_fpn_1x_coco_20200629_121244.log.json)                                                       |\n|       R-50        | pytorch |   2x    |         Yes          |      19.5      |  42.9  |          [config](./gfl_r50_fpn_ms-2x_coco.py)           |                                       [model](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r50_fpn_mstrain_2x_coco/gfl_r50_fpn_mstrain_2x_coco_20200629_213802-37bb1edc.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r50_fpn_mstrain_2x_coco/gfl_r50_fpn_mstrain_2x_coco_20200629_213802.log.json)                                       |\n|       R-101       | pytorch |   2x    |         Yes          |      14.7      |  44.7  |          [config](./gfl_r101_fpn_ms-2x_coco.py)          |                                     [model](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_mstrain_2x_coco/gfl_r101_fpn_mstrain_2x_coco_20200629_200126-dd12f847.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_mstrain_2x_coco/gfl_r101_fpn_mstrain_2x_coco_20200629_200126.log.json)                                     |\n|    R-101-dcnv2    | pytorch |   2x    |         Yes          |      12.9      |  47.1  |    [config](./gfl_r101-dconv-c3-c5_fpn_ms-2x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco_20200630_102002-134b07df.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco_20200630_102002.log.json)             |\n|    X-101-32x4d    | pytorch |   2x    |         Yes          |      12.1      |  45.9  |       [config](./gfl_x101-32x4d_fpn_ms-2x_coco.py)       |                         [model](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_x101_32x4d_fpn_mstrain_2x_coco/gfl_x101_32x4d_fpn_mstrain_2x_coco_20200630_102002-50c1ffdb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_x101_32x4d_fpn_mstrain_2x_coco/gfl_x101_32x4d_fpn_mstrain_2x_coco_20200630_102002.log.json)                         |\n| X-101-32x4d-dcnv2 | pytorch |   2x    |         Yes          |      10.7      |  48.1  | [config](./gfl_x101-32x4d-dconv-c4-c5_fpn_ms-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_x101_32x4d_fpn_dconv_c4-c5_mstrain_2x_coco/gfl_x101_32x4d_fpn_dconv_c4-c5_mstrain_2x_coco_20200630_102002-14a2bf25.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_x101_32x4d_fpn_dconv_c4-c5_mstrain_2x_coco/gfl_x101_32x4d_fpn_dconv_c4-c5_mstrain_2x_coco_20200630_102002.log.json) |\n\n\\[1\\] *1x and 2x mean the model is trained for 90K and 180K iterations, respectively.* \\\n\\[2\\] *All results are obtained with a single model and without any test time data augmentation such as multi-scale, flipping and etc..* \\\n\\[3\\] *`dcnv2` denotes deformable convolutional networks v2.* \\\n\\[4\\] *FPS is tested with a single GeForce RTX 2080Ti GPU, using a batch size of 1.*\n\n## Citation\n\nWe provide config files to reproduce the object detection results in the paper [Generalized Focal Loss: Learning Qualified and Distributed Bounding Boxes for Dense Object Detection](https://arxiv.org/abs/2006.04388)\n\n```latex\n@article{li2020generalized,\n  title={Generalized Focal Loss: Learning Qualified and Distributed Bounding Boxes for Dense Object Detection},\n  author={Li, Xiang and Wang, Wenhai and Wu, Lijun and Chen, Shuo and Hu, Xiaolin and Li, Jun and Tang, Jinhui and Yang, Jian},\n  journal={arXiv preprint arXiv:2006.04388},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/gfl/gfl_r101-dconv-c3-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './gfl_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNet',\n        depth=101,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/gfl/gfl_r101_fpn_ms-2x_coco.py",
    "content": "_base_ = './gfl_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNet',\n        depth=101,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/gfl/gfl_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='GFL',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='GFLHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        loss_cls=dict(\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            beta=2.0,\n            loss_weight=1.0),\n        loss_dfl=dict(type='DistributionFocalLoss', loss_weight=0.25),\n        reg_max=16,\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/gfl/gfl_r50_fpn_ms-2x_coco.py",
    "content": "_base_ = './gfl_r50_fpn_1x_coco.py'\nmax_epochs = 24\n\n# learning policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# multi-scale training\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 480), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/gfl/gfl_x101-32x4d-dconv-c4-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './gfl_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    type='GFL',\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, False, True, True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/gfl/gfl_x101-32x4d_fpn_ms-2x_coco.py",
    "content": "_base_ = './gfl_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    type='GFL',\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/gfl/metafile.yml",
    "content": "Collections:\n  - Name: Generalized Focal Loss\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Generalized Focal Loss\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/2006.04388\n      Title: 'Generalized Focal Loss: Learning Qualified and Distributed Bounding Boxes for Dense Object Detection'\n    README: configs/gfl/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.2.0/mmdet/models/detectors/gfl.py#L6\n      Version: v2.2.0\n\nModels:\n  - Name: gfl_r50_fpn_1x_coco\n    In Collection: Generalized Focal Loss\n    Config: configs/gfl/gfl_r50_fpn_1x_coco.py\n    Metadata:\n      inference time (ms/im):\n        - value: 51.28\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r50_fpn_1x_coco/gfl_r50_fpn_1x_coco_20200629_121244-25944287.pth\n\n  - Name: gfl_r50_fpn_ms-2x_coco\n    In Collection: Generalized Focal Loss\n    Config: configs/gfl/gfl_r50_fpn_ms-2x_coco.py\n    Metadata:\n      inference time (ms/im):\n        - value: 51.28\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r50_fpn_mstrain_2x_coco/gfl_r50_fpn_mstrain_2x_coco_20200629_213802-37bb1edc.pth\n\n  - Name: gfl_r101_fpn_ms-2x_coco\n    In Collection: Generalized Focal Loss\n    Config: configs/gfl/gfl_r101_fpn_ms-2x_coco.py\n    Metadata:\n      inference time (ms/im):\n        - value: 68.03\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_mstrain_2x_coco/gfl_r101_fpn_mstrain_2x_coco_20200629_200126-dd12f847.pth\n\n  - Name: gfl_r101-dconv-c3-c5_fpn_ms-2x_coco\n    In Collection: Generalized Focal Loss\n    Config: configs/gfl/gfl_r101-dconv-c3-c5_fpn_ms-2x_coco.py\n    Metadata:\n      inference time (ms/im):\n        - value: 77.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco_20200630_102002-134b07df.pth\n\n  - Name: gfl_x101-32x4d_fpn_ms-2x_coco\n    In Collection: Generalized Focal Loss\n    Config: configs/gfl/gfl_x101-32x4d_fpn_ms-2x_coco.py\n    Metadata:\n      inference time (ms/im):\n        - value: 82.64\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_x101_32x4d_fpn_mstrain_2x_coco/gfl_x101_32x4d_fpn_mstrain_2x_coco_20200630_102002-50c1ffdb.pth\n\n  - Name: gfl_x101-32x4d-dconv-c4-c5_fpn_ms-2x_coco\n    In Collection: Generalized Focal Loss\n    Config: configs/gfl/gfl_x101-32x4d-dconv-c4-c5_fpn_ms-2x_coco.py\n    Metadata:\n      inference time (ms/im):\n        - value: 93.46\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 48.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_x101_32x4d_fpn_dconv_c4-c5_mstrain_2x_coco/gfl_x101_32x4d_fpn_dconv_c4-c5_mstrain_2x_coco_20200630_102002-14a2bf25.pth\n"
  },
  {
    "path": "configs/ghm/README.md",
    "content": "# GHM\n\n> [Gradient Harmonized Single-stage Detector](https://arxiv.org/abs/1811.05181)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nDespite the great success of two-stage detectors, single-stage detector is still a more elegant and efficient way, yet suffers from the two well-known disharmonies during training, i.e. the huge difference in quantity between positive and negative examples as well as between easy and hard examples. In this work, we first point out that the essential effect of the two disharmonies can be summarized in term of the gradient. Further, we propose a novel gradient harmonizing mechanism (GHM) to be a hedging for the disharmonies. The philosophy behind GHM can be easily embedded into both classification loss function like cross-entropy (CE) and regression loss function like smooth-L1 (SL1) loss. To this end, two novel loss functions called GHM-C and GHM-R are designed to balancing the gradient flow for anchor classification and bounding box refinement, respectively. Ablation study on MS COCO demonstrates that without laborious hyper-parameter tuning, both GHM-C and GHM-R can bring substantial improvement for single-stage detector. Without any whistles and bells, our model achieves 41.6 mAP on COCO test-dev set which surpasses the state-of-the-art method, Focal Loss (FL) + SL1, by 0.8.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143889057-0341f32b-1291-4b9a-8444-52ad266ae709.png\"/>\n</div>\n\n## Results and Models\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                       Config                        |                                                                                                                                                           Download                                                                                                                                                            |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |   1x    |   4.0    |      3.3       |  37.0  |    [config](./retinanet_r50_fpn_ghm-1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_r50_fpn_1x_coco/retinanet_ghm_r50_fpn_1x_coco_20200130-a437fda3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_r50_fpn_1x_coco/retinanet_ghm_r50_fpn_1x_coco_20200130_004213.log.json)               |\n|    R-101-FPN    | pytorch |   1x    |   6.0    |      4.4       |  39.1  |    [config](./retinanet_r101_fpn_ghm-1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_r101_fpn_1x_coco/retinanet_ghm_r101_fpn_1x_coco_20200130-c148ee8f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_r101_fpn_1x_coco/retinanet_ghm_r101_fpn_1x_coco_20200130_145259.log.json)             |\n| X-101-32x4d-FPN | pytorch |   1x    |   7.2    |      5.1       |  40.7  | [config](./retinanet_x101-32x4d_fpn_ghm-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_x101_32x4d_fpn_1x_coco/retinanet_ghm_x101_32x4d_fpn_1x_coco_20200131-e4333bd0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_x101_32x4d_fpn_1x_coco/retinanet_ghm_x101_32x4d_fpn_1x_coco_20200131_113653.log.json) |\n| X-101-64x4d-FPN | pytorch |   1x    |   10.3   |      5.2       |  41.4  | [config](./retinanet_x101-64x4d_fpn_ghm-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_x101_64x4d_fpn_1x_coco/retinanet_ghm_x101_64x4d_fpn_1x_coco_20200131-dd381cef.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_x101_64x4d_fpn_1x_coco/retinanet_ghm_x101_64x4d_fpn_1x_coco_20200131_113723.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{li2019gradient,\n  title={Gradient Harmonized Single-stage Detector},\n  author={Li, Buyu and Liu, Yu and Wang, Xiaogang},\n  booktitle={AAAI Conference on Artificial Intelligence},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/ghm/metafile.yml",
    "content": "Collections:\n  - Name: GHM\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - GHM-C\n        - GHM-R\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1811.05181\n      Title: 'Gradient Harmonized Single-stage Detector'\n    README: configs/ghm/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/losses/ghm_loss.py#L21\n      Version: v2.0.0\n\nModels:\n  - Name: retinanet_r50_fpn_ghm-1x_coco\n    In Collection: GHM\n    Config: configs/ghm/retinanet_r50_fpn_ghm-1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.0\n      inference time (ms/im):\n        - value: 303.03\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_r50_fpn_1x_coco/retinanet_ghm_r50_fpn_1x_coco_20200130-a437fda3.pth\n\n  - Name: retinanet_r101_fpn_ghm-1x_coco\n    In Collection: GHM\n    Config: configs/ghm/retinanet_r101_fpn_ghm-1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 227.27\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_r101_fpn_1x_coco/retinanet_ghm_r101_fpn_1x_coco_20200130-c148ee8f.pth\n\n  - Name: retinanet_x101-32x4d_fpn_ghm-1x_coco\n    In Collection: GHM\n    Config: configs/ghm/retinanet_x101-32x4d_fpn_ghm-1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.2\n      inference time (ms/im):\n        - value: 196.08\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_x101_32x4d_fpn_1x_coco/retinanet_ghm_x101_32x4d_fpn_1x_coco_20200131-e4333bd0.pth\n\n  - Name: retinanet_x101-64x4d_fpn_ghm-1x_coco\n    In Collection: GHM\n    Config: configs/ghm/retinanet_x101-64x4d_fpn_ghm-1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.3\n      inference time (ms/im):\n        - value: 192.31\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ghm/retinanet_ghm_x101_64x4d_fpn_1x_coco/retinanet_ghm_x101_64x4d_fpn_1x_coco_20200131-dd381cef.pth\n"
  },
  {
    "path": "configs/ghm/retinanet_r101_fpn_ghm-1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_ghm-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/ghm/retinanet_r50_fpn_ghm-1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    bbox_head=dict(\n        loss_cls=dict(\n            _delete_=True,\n            type='GHMC',\n            bins=30,\n            momentum=0.75,\n            use_sigmoid=True,\n            loss_weight=1.0),\n        loss_bbox=dict(\n            _delete_=True,\n            type='GHMR',\n            mu=0.02,\n            bins=10,\n            momentum=0.7,\n            loss_weight=10.0)))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/ghm/retinanet_x101-32x4d_fpn_ghm-1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_ghm-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/ghm/retinanet_x101-64x4d_fpn_ghm-1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_ghm-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/glip/README.md",
    "content": "# GLIP: Grounded Language-Image Pre-training\n\n> [GLIP: Grounded Language-Image Pre-training](https://arxiv.org/abs/2112.03857)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThis paper presents a grounded language-image pre-training (GLIP) model for learning object-level, language-aware, and semantic-rich visual representations. GLIP unifies object detection and phrase grounding for pre-training. The unification brings two benefits: 1) it allows GLIP to learn from both detection and grounding data to improve both tasks and bootstrap a good grounding model; 2) GLIP can leverage massive image-text pairs by generating grounding boxes in a self-training fashion, making the learned representation semantic-rich. In our experiments, we pre-train GLIP on 27M grounding data, including 3M human-annotated and 24M web-crawled image-text pairs. The learned representations demonstrate strong zero-shot and few-shot transferability to various object-level recognition tasks. 1) When directly evaluated on COCO and LVIS (without seeing any images in COCO during pre-training), GLIP achieves 49.8 AP and 26.9 AP, respectively, surpassing many supervised baselines. 2) After fine-tuned on COCO, GLIP achieves 60.8 AP on val and 61.5 AP on test-dev, surpassing prior SoTA. 3) When transferred to 13 downstream object detection tasks, a 1-shot GLIP rivals with a fully-supervised Dynamic Head.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmyolo/assets/17425982/b87228d7-f000-4a5d-b103-fe535984417a\"/>\n</div>\n\n## Installation\n\n```shell\ncd $MMDETROOT\n\n# source installation\npip install -r requirements/multimodal.txt\n\n# or mim installation\nmim install mmdet[multimodal]\n```\n\n```shell\ncd $MMDETROOT\n\nwget https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth\n\npython demo/image_demo.py demo/demo.jpg \\\nconfigs/glip/glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py \\\n--weights glip_tiny_a_mmdet-b3654169.pth \\\n--texts 'bench. car'\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/7b450d96-81ac-462a-92bc-0d4ae7b8721c\" width=\"40%\"/>\n</div>\n\n## NOTE\n\nGLIP utilizes BERT as the language model, which requires access to https://huggingface.co/. If you encounter connection errors due to network access, you can download the required files on a computer with internet access and save them locally. Finally, modify the `lang_model_name` field in the config to the local path. Please refer to the following code:\n\n```python\nfrom transformers import BertConfig, BertModel\nfrom transformers import AutoTokenizer\n\nconfig = BertConfig.from_pretrained(\"bert-base-uncased\")\nmodel = BertModel.from_pretrained(\"bert-base-uncased\", add_pooling_layer=False, config=config)\ntokenizer = AutoTokenizer.from_pretrained(\"bert-base-uncased\")\n\nconfig.save_pretrained(\"your path/bert-base-uncased\")\nmodel.save_pretrained(\"your path/bert-base-uncased\")\ntokenizer.save_pretrained(\"your path/bert-base-uncased\")\n```\n\n## COCO Results and Models\n\n|   Model    | Zero-shot or Finetune | COCO mAP | Official COCO mAP |       Pre-Train Data       |                                 Config                                  |                                                                                                                                                                                                   Download                                                                                                                                                                                                    |\n| :--------: | :-------------------: | :------: | ----------------: | :------------------------: | :---------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| GLIP-T (A) |       Zero-shot       |   43.0   |              42.9 |            O365            |       [config](glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py)        |                                                                                                                                                         [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth)                                                                                                                                                          |\n| GLIP-T (A) |       Finetune        |   53.3   |              52.9 |            O365            |   [config](glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco.py)   | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_180419-e6addd96.pth)\\| [log](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_180419.log.json) |\n| GLIP-T (B) |       Zero-shot       |   44.9   |              44.9 |            O365            |       [config](glip_atss_swin-t_b_fpn_dyhead_pretrain_obj365.py)        |                                                                                                                                                         [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_b_mmdet-6dfbd102.pth)                                                                                                                                                          |\n| GLIP-T (B) |       Finetune        |   54.1   |              53.8 |            O365            |   [config](glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco.py)   | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230916_163538-650323ba.pth)\\| [log](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230916_163538.log.json) |\n| GLIP-T (C) |       Zero-shot       |   46.7   |              46.7 |         O365,GoldG         |    [config](glip_atss_swin-t_c_fpn_dyhead_pretrain_obj365-goldg.py)     |                                                                                                                                                         [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_c_mmdet-2fc427dd.pth)                                                                                                                                                          |\n| GLIP-T (C) |       Finetune        |   55.2   |              55.1 |         O365,GoldG         |   [config](glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco.py)   | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_182935-4ba3fc3b.pth)\\| [log](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_182935.log.json) |\n|   GLIP-T   |       Zero-shot       |   46.6   |              46.6 |    O365,GoldG,CC3M,SBU     | [config](glip_atss_swin-t_fpn_dyhead_pretrain_obj365-goldg-cc3m-sub.py) |                                                                                                                                                          [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_mmdet-c24ce662.pth)                                                                                                                                                           |\n|   GLIP-T   |       Finetune        |   55.4   |              55.2 |    O365,GoldG,CC3M,SBU     |    [config](glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco.py)    |     [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_224410-ba97be24.pth)\\| [log](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_224410.log.json)     |\n|   GLIP-L   |       Zero-shot       |   51.3   |              51.4 | FourODs,GoldG,CC3M+12M,SBU |       [config](glip_atss_swin-l_fpn_dyhead_pretrain_mixeddata.py)       |                                                                                                                                                            [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_l_mmdet-abfe026b.pth)                                                                                                                                                            |\n|   GLIP-L   |       Finetune        |   59.4   |                   | FourODs,GoldG,CC3M+12M,SBU |    [config](glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco.py)    |     [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230910_100800-e9be4274.pth)\\| [log](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230910_100800.log.json)     |\n\nNote:\n\n1. The weights corresponding to the zero-shot model are adopted from the official weights and converted using the [script](../../tools/model_converters/glip_to_mmdet.py). We have not retrained the model for the time being.\n2. Finetune refers to fine-tuning on the COCO 2017 dataset. The L model is trained using 16 A100 GPUs, while the remaining models are trained using 16 NVIDIA GeForce 3090 GPUs.\n3. Taking the GLIP-T(A) model as an example, I trained it twice using the official code, and the fine-tuning mAP were 52.5 and 52.6. Therefore, the mAP we achieved in our reproduction is higher than the official results. The main reason is that we modified the `weight_decay` parameter.\n4. Our experiments revealed that training for 24 epochs leads to overfitting. Therefore, we chose the best-performing model. If users want to train on a custom dataset, it is advisable to shorten the number of epochs and save the best-performing model.\n5. Due to the official absence of fine-tuning hyperparameters for the GLIP-L model, we have not yet reproduced the official accuracy. I have found that overfitting can also occur, so it may be necessary to consider custom modifications to data augmentation and model enhancement. Given the high cost of training, we have not conducted any research on this matter at the moment.\n\n## LVIS Results\n\n|   Model    | Official | MiniVal APr | MiniVal APc | MiniVal APf | MiniVal AP | Val1.0 APr | Val1.0 APc | Val1.0 APf | Val1.0 AP |       Pre-Train Data       |                                 Config                                  |                                           Download                                           |\n| :--------: | :------: | :---------: | :---------: | :---------: | :--------: | :--------: | :--------: | :--------: | :-------: | :------------------------: | :---------------------------------------------------------------------: | :------------------------------------------------------------------------------------------: |\n| GLIP-T (A) |    ✔     |             |             |             |            |            |            |            |           |            O365            | [config](lvis/glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_lvis.py)  | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth) |\n| GLIP-T (A) |          |    12.1     |    15.5     |    25.8     |    20.2    |    6.2     |    10.9    |    22.8    |   14.7    |            O365            | [config](lvis/glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_lvis.py)  | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth) |\n| GLIP-T (B) |    ✔     |             |             |             |            |            |            |            |           |            O365            | [config](lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_lvis.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_b_mmdet-6dfbd102.pth) |\n| GLIP-T (B) |          |     8.6     |    13.9     |    26.0     |    19.3    |    4.6     |    9.8     |    22.6    |   13.9    |            O365            | [config](lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_lvis.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_b_mmdet-6dfbd102.pth) |\n| GLIP-T (C) |    ✔     |    14.3     |    19.4     |    31.1     |    24.6    |            |            |            |           |         O365,GoldG         | [config](lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_lvis.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_c_mmdet-2fc427dd.pth) |\n| GLIP-T (C) |          |    14.4     |    19.8     |    31.9     |    25.2    |    8.3     |    13.2    |    28.1    |   18.2    |         O365,GoldG         | [config](lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_lvis.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_c_mmdet-2fc427dd.pth) |\n|   GLIP-T   |    ✔     |             |             |             |            |            |            |            |           |    O365,GoldG,CC3M,SBU     | [config](lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_lvis.py) |  [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_mmdet-c24ce662.pth)  |\n|   GLIP-T   |          |    18.1     |    21.2     |    33.1     |    26.7    |    10.8    |    14.7    |    29.0    |   19.6    |    O365,GoldG,CC3M,SBU     | [config](lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_lvis.py) |  [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_mmdet-c24ce662.pth)  |\n|   GLIP-L   |    ✔     |    29.2     |    34.9     |    42.1     |    37.9    |            |            |            |           | FourODs,GoldG,CC3M+12M,SBU |  [config](lvis/glip_atss_swin-l_fpn_dyhead_pretrain_zeroshot_lvis.py)   |   [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_l_mmdet-abfe026b.pth)    |\n|   GLIP-L   |          |    27.9     |    33.7     |    39.7     |    36.1    |    20.2    |    25.8    |    35.3    |   28.5    | FourODs,GoldG,CC3M+12M,SBU |  [config](lvis/glip_atss_swin-l_fpn_dyhead_pretrain_zeroshot_lvis.py)   |   [model](https://download.openmmlab.com/mmdetection/v3.0/glip/glip_l_mmdet-abfe026b.pth)    |\n\nNote:\n\n1. The above are zero-shot evaluation results.\n2. The evaluation metric we used is LVIS FixAP. For specific details, please refer to [Evaluating Large-Vocabulary Object Detectors: The Devil is in the Details](https://arxiv.org/pdf/2102.01066.pdf).\n3. We found that the performance on small models is better than the official results, but it is lower on large models. This is mainly due to the incomplete alignment of the GLIP post-processing.\n\n## ODinW (Object Detection in the Wild) Results\n\nLearning visual representations from natural language supervision has recently shown great promise in a number of pioneering works. In general, these language-augmented visual models demonstrate strong transferability to a variety of datasets and tasks. However, it remains challenging to evaluate the transferablity of these models due to the lack of easy-to-use evaluation toolkits and public benchmarks. To tackle this, we build ELEVATER 1 , the first benchmark and toolkit for evaluating (pre-trained) language-augmented visual models. ELEVATER is composed of three components. (i) Datasets. As downstream evaluation suites, it consists of 20 image classification datasets and 35 object detection datasets, each of which is augmented with external knowledge. (ii) Toolkit. An automatic hyper-parameter tuning toolkit is developed to facilitate model evaluation on downstream tasks. (iii) Metrics. A variety of evaluation metrics are used to measure sample-efficiency (zero-shot and few-shot) and parameter-efficiency (linear probing and full model fine-tuning). ELEVATER is platform for Computer Vision in the Wild (CVinW), and is publicly released at https://computer-vision-in-the-wild.github.io/ELEVATER/\n\n### Results and models of ODinW13\n\n| Method                | GLIP-T(A) | Official  | GLIP-T(B) | Official  | GLIP-T(C) | Official  | GroundingDINO-T | GroundingDINO-B |\n| --------------------- | --------- | --------- | --------- | --------- | --------- | --------- | --------------- | --------------- |\n| AerialMaritimeDrone   | 0.123     | 0.122     | 0.110     | 0.110     | 0.130     | 0.130     | 0.173           | 0.281           |\n| Aquarium              | 0.175     | 0.174     | 0.173     | 0.169     | 0.191     | 0.190     | 0.195           | 0.445           |\n| CottontailRabbits     | 0.686     | 0.686     | 0.688     | 0.688     | 0.744     | 0.744     | 0.799           | 0.808           |\n| EgoHands              | 0.013     | 0.013     | 0.003     | 0.004     | 0.314     | 0.315     | 0.608           | 0.764           |\n| NorthAmericaMushrooms | 0.502     | 0.502     | 0.367     | 0.367     | 0.297     | 0.296     | 0.507           | 0.675           |\n| Packages              | 0.589     | 0.589     | 0.083     | 0.083     | 0.699     | 0.699     | 0.687           | 0.670           |\n| PascalVOC             | 0.512     | 0.512     | 0.541     | 0.540     | 0.565     | 0.565     | 0.563           | 0.711           |\n| pistols               | 0.339     | 0.339     | 0.502     | 0.501     | 0.503     | 0.504     | 0.726           | 0.771           |\n| pothole               | 0.007     | 0.007     | 0.030     | 0.030     | 0.058     | 0.058     | 0.215           | 0.478           |\n| Raccoon               | 0.075     | 0.074     | 0.285     | 0.288     | 0.241     | 0.244     | 0.549           | 0.541           |\n| ShellfishOpenImages   | 0.253     | 0.253     | 0.337     | 0.338     | 0.300     | 0.302     | 0.393           | 0.650           |\n| thermalDogsAndPeople  | 0.372     | 0.372     | 0.475     | 0.475     | 0.510     | 0.510     | 0.657           | 0.633           |\n| VehiclesOpenImages    | 0.574     | 0.566     | 0.562     | 0.547     | 0.549     | 0.534     | 0.613           | 0.647           |\n| Average               | **0.325** | **0.324** | **0.320** | **0.318** | **0.392** | **0.392** | **0.514**       | **0.621**       |\n\n### Results and models of ODinW35\n\n| Method                      | GLIP-T(A) | Official  | GLIP-T(B) | Official  | GLIP-T(C) | Official  | GroundingDINO-T | GroundingDINO-B |\n| --------------------------- | --------- | --------- | --------- | --------- | --------- | --------- | --------------- | --------------- |\n| AerialMaritimeDrone_large   | 0.123     | 0.122     | 0.110     | 0.110     | 0.130     | 0.130     | 0.173           | 0.281           |\n| AerialMaritimeDrone_tiled   | 0.174     | 0.174     | 0.172     | 0.172     | 0.172     | 0.172     | 0.206           | 0.364           |\n| AmericanSignLanguageLetters | 0.001     | 0.001     | 0.003     | 0.003     | 0.009     | 0.009     | 0.002           | 0.096           |\n| Aquarium                    | 0.175     | 0.175     | 0.173     | 0.171     | 0.192     | 0.182     | 0.195           | 0.445           |\n| BCCD                        | 0.016     | 0.016     | 0.001     | 0.001     | 0.000     | 0.000     | 0.161           | 0.584           |\n| boggleBoards                | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000           | 0.134           |\n| brackishUnderwater          | 0.016     | 0..013    | 0.021     | 0.027     | 0.020     | 0.022     | 0.021           | 0.454           |\n| ChessPieces                 | 0.001     | 0.001     | 0.000     | 0.000     | 0.001     | 0.001     | 0.000           | 0.000           |\n| CottontailRabbits           | 0.710     | 0.709     | 0.683     | 0.683     | 0.752     | 0.752     | 0.806           | 0.797           |\n| dice                        | 0.005     | 0.005     | 0.004     | 0.004     | 0.004     | 0.004     | 0.004           | 0.082           |\n| DroneControl                | 0.016     | 0.017     | 0.006     | 0.008     | 0.005     | 0.007     | 0.042           | 0.638           |\n| EgoHands_generic            | 0.009     | 0.010     | 0.005     | 0.006     | 0.510     | 0.508     | 0.608           | 0.764           |\n| EgoHands_specific           | 0.001     | 0.001     | 0.004     | 0.006     | 0.003     | 0.004     | 0.002           | 0.687           |\n| HardHatWorkers              | 0.029     | 0.029     | 0.023     | 0.023     | 0.033     | 0.033     | 0.046           | 0.439           |\n| MaskWearing                 | 0.007     | 0.007     | 0.003     | 0.002     | 0.005     | 0.005     | 0.004           | 0.406           |\n| MountainDewCommercial       | 0.218     | 0.227     | 0.199     | 0.197     | 0.478     | 0.463     | 0.430           | 0.580           |\n| NorthAmericaMushrooms       | 0.502     | 0.502     | 0.450     | 0.450     | 0.497     | 0.497     | 0.471           | 0.501           |\n| openPoetryVision            | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000           | 0.051           |\n| OxfordPets_by_breed         | 0.001     | 0.002     | 0.002     | 0.004     | 0.001     | 0.002     | 0.003           | 0.799           |\n| OxfordPets_by_species       | 0.016     | 0.011     | 0.012     | 0.009     | 0.013     | 0.009     | 0.011           | 0.872           |\n| PKLot                       | 0.002     | 0.002     | 0.000     | 0.000     | 0.000     | 0.000     | 0.001           | 0.774           |\n| Packages                    | 0.569     | 0.569     | 0.279     | 0.279     | 0.712     | 0.712     | 0.695           | 0.728           |\n| PascalVOC                   | 0.512     | 0.512     | 0.541     | 0.540     | 0.565     | 0.565     | 0.563           | 0.711           |\n| pistols                     | 0.339     | 0.339     | 0.502     | 0.501     | 0.503     | 0.504     | 0.726           | 0.771           |\n| plantdoc                    | 0.002     | 0.002     | 0.007     | 0.007     | 0.009     | 0.009     | 0.005           | 0.376           |\n| pothole                     | 0.007     | 0.010     | 0.024     | 0.025     | 0.085     | 0.101     | 0.215           | 0.478           |\n| Raccoons                    | 0.075     | 0.074     | 0.285     | 0.288     | 0.241     | 0.244     | 0.549           | 0.541           |\n| selfdrivingCar              | 0.071     | 0.072     | 0.074     | 0.074     | 0.081     | 0.080     | 0.089           | 0.318           |\n| ShellfishOpenImages         | 0.253     | 0.253     | 0.337     | 0.338     | 0.300     | 0.302     | 0.393           | 0.650           |\n| ThermalCheetah              | 0.028     | 0.028     | 0.000     | 0.000     | 0.028     | 0.028     | 0.087           | 0.290           |\n| thermalDogsAndPeople        | 0.372     | 0.372     | 0.475     | 0.475     | 0.510     | 0.510     | 0.657           | 0.633           |\n| UnoCards                    | 0.000     | 0.000     | 0.000     | 0.001     | 0.002     | 0.003     | 0.006           | 0.754           |\n| VehiclesOpenImages          | 0.574     | 0.566     | 0.562     | 0.547     | 0.549     | 0.534     | 0.613           | 0.647           |\n| WildfireSmoke               | 0.000     | 0.000     | 0.000     | 0.000     | 0.017     | 0.017     | 0.134           | 0.410           |\n| websiteScreenshots          | 0.003     | 0.004     | 0.003     | 0.005     | 0.005     | 0.006     | 0.012           | 0.175           |\n| Average                     | **0.134** | **0.134** | **0.138** | **0.138** | **0.179** | **0.178** | **0.227**       | **0.492**       |\n\n### Results on Flickr30k\n\n| Model         | Official | Pre-Train Data      | Val R@1 | Val R@5 | Val R@10 | Test R@1 | Test R@5 | Test R@10 |\n| ------------- | -------- | ------------------- | ------- | ------- | -------- | -------- | -------- | --------- |\n| **GLIP-T(C)** | ✔        | O365, GoldG         | 84.8    | 94.9    | 96.3     | 85.5     | 95.4     | 96.6      |\n| **GLIP-T(C)** |          | O365, GoldG         | 84.9    | 94.9    | 96.3     | 85.6     | 95.4     | 96.7      |\n| **GLIP-T**    |          | O365,GoldG,CC3M,SBU | 85.3    | 95.5    | 96.9     | 86.0     | 95.9     | 97.2      |\n"
  },
  {
    "path": "configs/glip/flickr30k/glip_atss_swin-t_c_fpn_dyhead_pretrain_obj365-goldg_zeroshot_flickr30k.py",
    "content": "_base_ = '../glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'\n\nlang_model_name = 'bert-base-uncased'\n\nmodel = dict(bbox_head=dict(early_fuse=True))\n\ndataset_type = 'Flickr30kDataset'\ndata_root = 'data/flickr30k_entities/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive', 'phrase_ids', 'phrases'))\n]\n\ndataset_Flickr30k_val = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='final_flickr_separateGT_val.json',\n    data_prefix=dict(img='flickr30k_images/'),\n    pipeline=test_pipeline,\n)\n\ndataset_Flickr30k_test = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='final_flickr_separateGT_test.json',\n    data_prefix=dict(img='flickr30k_images/'),\n    pipeline=test_pipeline,\n)\n\nval_evaluator_Flickr30k = dict(type='Flickr30kMetric', )\n\ntest_evaluator_Flickr30k = dict(type='Flickr30kMetric', )\n\n# ----------Config---------- #\ndataset_prefixes = ['Flickr30kVal', 'Flickr30kTest']\ndatasets = [dataset_Flickr30k_val, dataset_Flickr30k_test]\nmetrics = [val_evaluator_Flickr30k, test_evaluator_Flickr30k]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco.py",
    "content": "_base_ = './glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        drop_path_rate=0.4,\n    ),\n    neck=dict(in_channels=[384, 768, 1536]),\n    bbox_head=dict(early_fuse=True, num_dyhead_blocks=8, use_checkpoint=True))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/glip/glip_l_mmdet-abfe026b.pth'  # noqa\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-l_fpn_dyhead_pretrain_mixeddata.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'\n\nmodel = dict(\n    backbone=dict(\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        drop_path_rate=0.4,\n    ),\n    neck=dict(in_channels=[384, 768, 1536]),\n    bbox_head=dict(early_fuse=True, num_dyhead_blocks=8))\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth'  # noqa\nlang_model_name = 'bert-base-uncased'\n\nmodel = dict(\n    type='GLIP',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False,\n        convert_weights=False),\n    neck=dict(\n        type='FPN_DropBlock',\n        in_channels=[192, 384, 768],\n        out_channels=256,\n        start_level=0,\n        relu_before_extra_convs=True,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='ATSSVLFusionHead',\n        lang_model_name=lang_model_name,\n        num_classes=80,\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoderForGLIP',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    language_model=dict(type='BertModel', name=lang_model_name),\n    train_cfg=dict(\n        assigner=dict(\n            type='ATSSAssigner',\n            topk=9,\n            iou_calculator=dict(type='BboxOverlaps2D_GLIP')),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# dataset settings\ntrain_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        imdecode_backend='pillow',\n        backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='GTBoxSubOne_GLIP'),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 480), (1333, 560), (1333, 640), (1333, 720),\n                (1333, 800)],\n        keep_ratio=True,\n        resize_type='FixScaleResize',\n        backend='pillow'),\n    dict(type='RandomFlip_GLIP', prob=0.5),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1)),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=_base_.backend_args,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=2,\n        dataset=dict(\n            type=_base_.dataset_type,\n            data_root=_base_.data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            return_classes=True,\n            backend_args=_base_.backend_args)))\n\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\ntest_dataloader = val_dataloader\n\n# We did not adopt the official 24e optimizer strategy\n# because the results indicate that the current strategy is superior.\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW', lr=0.00002, betas=(0.9, 0.999), weight_decay=0.05),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'relative_position_bias_table': dict(decay_mult=0.),\n            'norm': dict(decay_mult=0.)\n        }),\n    clip_grad=None)\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nlang_model_name = 'bert-base-uncased'\n\nmodel = dict(\n    type='GLIP',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False,\n        convert_weights=False),\n    neck=dict(\n        type='FPN',\n        in_channels=[192, 384, 768],\n        out_channels=256,\n        start_level=0,\n        relu_before_extra_convs=True,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='ATSSVLFusionHead',\n        lang_model_name=lang_model_name,\n        num_classes=80,\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoderForGLIP',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n    ),\n    language_model=dict(type='BertModel', name=lang_model_name),\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=_base_.backend_args,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities'))\n]\n\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco.py'\n\nmodel = dict(bbox_head=dict(early_fuse=True, use_checkpoint=True))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_b_mmdet-6dfbd102.pth'  # noqa\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.00001),\n    clip_grad=dict(_delete_=True, max_norm=1, norm_type=2))\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_b_fpn_dyhead_pretrain_obj365.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'\n\nmodel = dict(bbox_head=dict(early_fuse=True))\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco.py",
    "content": "_base_ = './glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco.py'\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_c_mmdet-2fc427dd.pth'  # noqa\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_c_fpn_dyhead_pretrain_obj365-goldg.py",
    "content": "_base_ = './glip_atss_swin-t_b_fpn_dyhead_pretrain_obj365.py'\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco.py",
    "content": "_base_ = './glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco.py'\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_mmdet-c24ce662.pth'  # noqa\n"
  },
  {
    "path": "configs/glip/glip_atss_swin-t_fpn_dyhead_pretrain_obj365-goldg-cc3m-sub.py",
    "content": "_base_ = './glip_atss_swin-t_b_fpn_dyhead_pretrain_obj365.py'\n"
  },
  {
    "path": "configs/glip/lvis/glip_atss_swin-l_fpn_dyhead_pretrain_zeroshot_lvis.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_lvis.py'\n\nmodel = dict(\n    backbone=dict(\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        drop_path_rate=0.4,\n    ),\n    neck=dict(in_channels=[384, 768, 1536]),\n    bbox_head=dict(early_fuse=True, num_dyhead_blocks=8))\n"
  },
  {
    "path": "configs/glip/lvis/glip_atss_swin-l_fpn_dyhead_pretrain_zeroshot_mini-lvis.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_mini-lvis.py'\n\nmodel = dict(\n    backbone=dict(\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        drop_path_rate=0.4,\n    ),\n    neck=dict(in_channels=[384, 768, 1536]),\n    bbox_head=dict(early_fuse=True, num_dyhead_blocks=8))\n"
  },
  {
    "path": "configs/glip/lvis/glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_lvis.py",
    "content": "_base_ = '../glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/coco/'\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type=dataset_type,\n        ann_file='annotations/lvis_od_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\n# numpy < 1.24.0\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root + 'annotations/lvis_od_val.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/glip/lvis/glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_mini-lvis.py",
    "content": "_base_ = '../glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/coco/'\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type=dataset_type,\n        ann_file='annotations/lvis_v1_minival_inserted_image_name.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\n# numpy < 1.24.0\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root +\n    'annotations/lvis_v1_minival_inserted_image_name.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/glip/lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_lvis.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_lvis.py'\n\nmodel = dict(bbox_head=dict(early_fuse=True))\n"
  },
  {
    "path": "configs/glip/lvis/glip_atss_swin-t_bc_fpn_dyhead_pretrain_zeroshot_mini-lvis.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_zeroshot_mini-lvis.py'\n\nmodel = dict(bbox_head=dict(early_fuse=True))\n"
  },
  {
    "path": "configs/glip/metafile.yml",
    "content": "Collections:\n  - Name: GLIP\n    Metadata:\n      Training Data: Objects365, GoldG, CC3M, SBU and COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: A100 GPUs\n      Architecture:\n        - Swin Transformer\n        - DYHead\n        - BERT\n    Paper:\n      URL: https://arxiv.org/abs/2112.03857\n      Title: 'GLIP: Grounded Language-Image Pre-training'\n    README: configs/glip/README.md\n    Code:\n      URL:\n      Version: v3.0.0\n\nModels:\n  - Name: glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.0\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth\n  - Name: glip_atss_swin-t_b_fpn_dyhead_pretrain_obj365\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_b_fpn_dyhead_pretrain_obj365.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_b_mmdet-6dfbd102.pth\n  - Name: glip_atss_swin-t_c_fpn_dyhead_pretrain_obj365-goldg\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_c_fpn_dyhead_pretrain_obj365-goldg.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.7\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_c_mmdet-2fc427dd.pth\n  - Name: glip_atss_swin-t_fpn_dyhead_pretrain_obj365-goldg-cc3m-sub\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_fpn_dyhead_pretrain_obj365-goldg-cc3m-sub.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_mmdet-c24ce662.pth\n  - Name: glip_atss_swin-l_fpn_dyhead_pretrain_mixeddata\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-l_fpn_dyhead_pretrain_mixeddata.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 51.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_l_mmdet-abfe026b.pth\n  - Name: glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 53.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_a_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_180419-e6addd96.pth\n  - Name: glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 54.1\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_b_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230916_163538-650323ba.pth\n  - Name: glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 55.2\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_c_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_182935-4ba3fc3b.pth\n  - Name: glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 55.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-t_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230914_224410-ba97be24.pth\n  - Name: glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco\n    In Collection: GLIP\n    Config: configs/glip/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 59.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/glip/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco/glip_atss_swin-l_fpn_dyhead_16xb2_ms-2x_funtune_coco_20230910_100800-e9be4274.pth\n"
  },
  {
    "path": "configs/glip/odinw/glip_atss_swin-t_a_fpn_dyhead_pretrain_odinw13.py",
    "content": "_base_ = '../glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    test_mode=True,\n    pipeline=base_test_pipeline,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'penguin': {\n#         'suffix': ', which is black and white'\n#     },\n#     'puffin': {\n#         'suffix': ' with orange beaks'\n#     },\n#     'stingray': {\n#         'suffix': ' which is flat and round'\n#     },\n# }\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 CottontailRabbits---------------------#\nclass_name = ('Cottontail-Rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\n\ncaption_prompt = None\n# caption_prompt = {'Cottontail-Rabbit': {'name': 'rabbit'}}\n\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 EgoHands---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\n\ncaption_prompt = None\n# caption_prompt = {'hand': {'suffix': ' of a person'}}\n\ndataset_EgoHands = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 NorthAmericaMushrooms---------------------#\nclass_name = ('CoW', 'chanterelle')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\n\ncaption_prompt = None\n# caption_prompt = {\n#     'CoW': {\n#         'name': 'flat mushroom'\n#     },\n#     'chanterelle': {\n#         'name': 'yellow mushroom'\n#     }\n# }\n\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'package': {\n#         'prefix': 'there is a ',\n#         'suffix': ' on the porch'\n#     }\n# }\n\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'pothole': {\n#         'prefix': 'there are some ',\n#         'name': 'holes',\n#         'suffix': ' on the road'\n#     }\n# }\n\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\ndataset_prefixes = [\n    'AerialMaritimeDrone', 'Aquarium', 'CottontailRabbits', 'EgoHands',\n    'NorthAmericaMushrooms', 'Packages', 'PascalVOC', 'pistols', 'pothole',\n    'Raccoon', 'ShellfishOpenImages', 'thermalDogsAndPeople',\n    'VehiclesOpenImages'\n]\ndatasets = [\n    dataset_AerialMaritimeDrone, dataset_Aquarium, dataset_CottontailRabbits,\n    dataset_EgoHands, dataset_NorthAmericaMushrooms, dataset_Packages,\n    dataset_PascalVOC, dataset_pistols, dataset_pothole, dataset_Raccoon,\n    dataset_ShellfishOpenImages, dataset_thermalDogsAndPeople,\n    dataset_VehiclesOpenImages\n]\nmetrics = [\n    val_evaluator_AerialMaritimeDrone, val_evaluator_Aquarium,\n    val_evaluator_CottontailRabbits, val_evaluator_EgoHands,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_Packages,\n    val_evaluator_PascalVOC, val_evaluator_pistols, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_ShellfishOpenImages,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_VehiclesOpenImages\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/glip/odinw/glip_atss_swin-t_a_fpn_dyhead_pretrain_odinw35.py",
    "content": "_base_ = '../glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone_large---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone_large = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_large = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 AerialMaritimeDrone_tiled---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/tiled/'\ndataset_AerialMaritimeDrone_tiled = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_tiled = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 AmericanSignLanguageLetters---------------------#\nclass_name = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AmericanSignLanguageLetters/American Sign Language Letters.v1-v1.coco/'  # noqa\ndataset_AmericanSignLanguageLetters = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AmericanSignLanguageLetters = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 BCCD---------------------#\nclass_name = ('Platelets', 'RBC', 'WBC')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'BCCD/BCCD.v3-raw.coco/'\ndataset_BCCD = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_BCCD = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 boggleBoards---------------------#\nclass_name = ('Q', 'a', 'an', 'b', 'c', 'd', 'e', 'er', 'f', 'g', 'h', 'he',\n              'i', 'in', 'j', 'k', 'l', 'm', 'n', 'o', 'o ', 'p', 'q', 'qu',\n              'r', 's', 't', 't\\\\', 'th', 'u', 'v', 'w', 'wild', 'x', 'y', 'z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'boggleBoards/416x416AutoOrient/export/'\ndataset_boggleBoards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_boggleBoards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 brackishUnderwater---------------------#\nclass_name = ('crab', 'fish', 'jellyfish', 'shrimp', 'small_fish', 'starfish')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'brackishUnderwater/960x540/'\ndataset_brackishUnderwater = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_brackishUnderwater = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 ChessPieces---------------------#\nclass_name = ('  ', 'black bishop', 'black king', 'black knight', 'black pawn',\n              'black queen', 'black rook', 'white bishop', 'white king',\n              'white knight', 'white pawn', 'white queen', 'white rook')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ChessPieces/Chess Pieces.v23-raw.coco/'\ndataset_ChessPieces = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ChessPieces = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 CottontailRabbits---------------------#\nclass_name = ('rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 dice---------------------#\nclass_name = ('1', '2', '3', '4', '5', '6')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'dice/mediumColor/export/'\ndataset_dice = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_dice = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 DroneControl---------------------#\nclass_name = ('follow', 'follow_hand', 'land', 'land_hand', 'null', 'object',\n              'takeoff', 'takeoff-hand')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'DroneControl/Drone Control.v3-raw.coco/'\ndataset_DroneControl = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_DroneControl = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 EgoHands_generic---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\ncaption_prompt = {'hand': {'suffix': ' of a person'}}\ndataset_EgoHands_generic = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_generic = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 EgoHands_specific---------------------#\nclass_name = ('myleft', 'myright', 'yourleft', 'yourright')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/specific/'\ndataset_EgoHands_specific = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_specific = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------14 HardHatWorkers---------------------#\nclass_name = ('head', 'helmet', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'HardHatWorkers/raw/'\ndataset_HardHatWorkers = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_HardHatWorkers = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------15 MaskWearing---------------------#\nclass_name = ('mask', 'no-mask')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MaskWearing/raw/'\ndataset_MaskWearing = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MaskWearing = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------16 MountainDewCommercial---------------------#\nclass_name = ('bottle', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MountainDewCommercial/'\ndataset_MountainDewCommercial = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MountainDewCommercial = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------17 NorthAmericaMushrooms---------------------#\nclass_name = ('flat mushroom', 'yellow mushroom')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------18 openPoetryVision---------------------#\nclass_name = ('American Typewriter', 'Andale Mono', 'Apple Chancery', 'Arial',\n              'Avenir', 'Baskerville', 'Big Caslon', 'Bradley Hand',\n              'Brush Script MT', 'Chalkboard', 'Comic Sans MS', 'Copperplate',\n              'Courier', 'Didot', 'Futura', 'Geneva', 'Georgia', 'Gill Sans',\n              'Helvetica', 'Herculanum', 'Impact', 'Kefa', 'Lucida Grande',\n              'Luminari', 'Marker Felt', 'Menlo', 'Monaco', 'Noteworthy',\n              'Optima', 'PT Sans', 'PT Serif', 'Palatino', 'Papyrus',\n              'Phosphate', 'Rockwell', 'SF Pro', 'SignPainter', 'Skia',\n              'Snell Roundhand', 'Tahoma', 'Times New Roman', 'Trebuchet MS',\n              'Verdana')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'openPoetryVision/512x512/'\ndataset_openPoetryVision = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_openPoetryVision = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------19 OxfordPets_by_breed---------------------#\nclass_name = ('cat-Abyssinian', 'cat-Bengal', 'cat-Birman', 'cat-Bombay',\n              'cat-British_Shorthair', 'cat-Egyptian_Mau', 'cat-Maine_Coon',\n              'cat-Persian', 'cat-Ragdoll', 'cat-Russian_Blue', 'cat-Siamese',\n              'cat-Sphynx', 'dog-american_bulldog',\n              'dog-american_pit_bull_terrier', 'dog-basset_hound',\n              'dog-beagle', 'dog-boxer', 'dog-chihuahua',\n              'dog-english_cocker_spaniel', 'dog-english_setter',\n              'dog-german_shorthaired', 'dog-great_pyrenees', 'dog-havanese',\n              'dog-japanese_chin', 'dog-keeshond', 'dog-leonberger',\n              'dog-miniature_pinscher', 'dog-newfoundland', 'dog-pomeranian',\n              'dog-pug', 'dog-saint_bernard', 'dog-samoyed',\n              'dog-scottish_terrier', 'dog-shiba_inu',\n              'dog-staffordshire_bull_terrier', 'dog-wheaten_terrier',\n              'dog-yorkshire_terrier')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-breed/'  # noqa\ndataset_OxfordPets_by_breed = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_breed = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------20 OxfordPets_by_species---------------------#\nclass_name = ('cat', 'dog')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-species/'  # noqa\ndataset_OxfordPets_by_species = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_species = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------21 PKLot---------------------#\nclass_name = ('space-empty', 'space-occupied')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PKLot/640/'  # noqa\ndataset_PKLot = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PKLot = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------22 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\ncaption_prompt = {\n    'package': {\n        'prefix': 'there is a ',\n        'suffix': ' on the porch'\n    }\n}\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------23 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------24 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------25 plantdoc---------------------#\nclass_name = ('Apple Scab Leaf', 'Apple leaf', 'Apple rust leaf',\n              'Bell_pepper leaf', 'Bell_pepper leaf spot', 'Blueberry leaf',\n              'Cherry leaf', 'Corn Gray leaf spot', 'Corn leaf blight',\n              'Corn rust leaf', 'Peach leaf', 'Potato leaf',\n              'Potato leaf early blight', 'Potato leaf late blight',\n              'Raspberry leaf', 'Soyabean leaf', 'Soybean leaf',\n              'Squash Powdery mildew leaf', 'Strawberry leaf',\n              'Tomato Early blight leaf', 'Tomato Septoria leaf spot',\n              'Tomato leaf', 'Tomato leaf bacterial spot',\n              'Tomato leaf late blight', 'Tomato leaf mosaic virus',\n              'Tomato leaf yellow virus', 'Tomato mold leaf',\n              'Tomato two spotted spider mites leaf', 'grape leaf',\n              'grape leaf black rot')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'plantdoc/416x416/'\ndataset_plantdoc = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_plantdoc = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------26 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\ncaption_prompt = {\n    'pothole': {\n        'name': 'holes',\n        'prefix': 'there are some ',\n        'suffix': ' on the road'\n    }\n}\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    caption_prompt=caption_prompt,\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------27 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------28 selfdrivingCar---------------------#\nclass_name = ('biker', 'car', 'pedestrian', 'trafficLight',\n              'trafficLight-Green', 'trafficLight-GreenLeft',\n              'trafficLight-Red', 'trafficLight-RedLeft',\n              'trafficLight-Yellow', 'trafficLight-YellowLeft', 'truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'selfdrivingCar/fixedLarge/export/'\ndataset_selfdrivingCar = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_selfdrivingCar = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------29 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------30 ThermalCheetah---------------------#\nclass_name = ('cheetah', 'human')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ThermalCheetah/'\ndataset_ThermalCheetah = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ThermalCheetah = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------31 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------32 UnoCards---------------------#\nclass_name = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11',\n              '12', '13', '14')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'UnoCards/raw/'\ndataset_UnoCards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_UnoCards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------33 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------34 WildfireSmoke---------------------#\nclass_name = ('smoke', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'WildfireSmoke/'\ndataset_WildfireSmoke = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_WildfireSmoke = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------35 websiteScreenshots---------------------#\nclass_name = ('button', 'field', 'heading', 'iframe', 'image', 'label', 'link',\n              'text')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'websiteScreenshots/'\ndataset_websiteScreenshots = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_websiteScreenshots = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\n\ndataset_prefixes = [\n    'AerialMaritimeDrone_large',\n    'AerialMaritimeDrone_tiled',\n    'AmericanSignLanguageLetters',\n    'Aquarium',\n    'BCCD',\n    'boggleBoards',\n    'brackishUnderwater',\n    'ChessPieces',\n    'CottontailRabbits',\n    'dice',\n    'DroneControl',\n    'EgoHands_generic',\n    'EgoHands_specific',\n    'HardHatWorkers',\n    'MaskWearing',\n    'MountainDewCommercial',\n    'NorthAmericaMushrooms',\n    'openPoetryVision',\n    'OxfordPets_by_breed',\n    'OxfordPets_by_species',\n    'PKLot',\n    'Packages',\n    'PascalVOC',\n    'pistols',\n    'plantdoc',\n    'pothole',\n    'Raccoons',\n    'selfdrivingCar',\n    'ShellfishOpenImages',\n    'ThermalCheetah',\n    'thermalDogsAndPeople',\n    'UnoCards',\n    'VehiclesOpenImages',\n    'WildfireSmoke',\n    'websiteScreenshots',\n]\n\ndatasets = [\n    dataset_AerialMaritimeDrone_large, dataset_AerialMaritimeDrone_tiled,\n    dataset_AmericanSignLanguageLetters, dataset_Aquarium, dataset_BCCD,\n    dataset_boggleBoards, dataset_brackishUnderwater, dataset_ChessPieces,\n    dataset_CottontailRabbits, dataset_dice, dataset_DroneControl,\n    dataset_EgoHands_generic, dataset_EgoHands_specific,\n    dataset_HardHatWorkers, dataset_MaskWearing, dataset_MountainDewCommercial,\n    dataset_NorthAmericaMushrooms, dataset_openPoetryVision,\n    dataset_OxfordPets_by_breed, dataset_OxfordPets_by_species, dataset_PKLot,\n    dataset_Packages, dataset_PascalVOC, dataset_pistols, dataset_plantdoc,\n    dataset_pothole, dataset_Raccoon, dataset_selfdrivingCar,\n    dataset_ShellfishOpenImages, dataset_ThermalCheetah,\n    dataset_thermalDogsAndPeople, dataset_UnoCards, dataset_VehiclesOpenImages,\n    dataset_WildfireSmoke, dataset_websiteScreenshots\n]\n\nmetrics = [\n    val_evaluator_AerialMaritimeDrone_large,\n    val_evaluator_AerialMaritimeDrone_tiled,\n    val_evaluator_AmericanSignLanguageLetters, val_evaluator_Aquarium,\n    val_evaluator_BCCD, val_evaluator_boggleBoards,\n    val_evaluator_brackishUnderwater, val_evaluator_ChessPieces,\n    val_evaluator_CottontailRabbits, val_evaluator_dice,\n    val_evaluator_DroneControl, val_evaluator_EgoHands_generic,\n    val_evaluator_EgoHands_specific, val_evaluator_HardHatWorkers,\n    val_evaluator_MaskWearing, val_evaluator_MountainDewCommercial,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_openPoetryVision,\n    val_evaluator_OxfordPets_by_breed, val_evaluator_OxfordPets_by_species,\n    val_evaluator_PKLot, val_evaluator_Packages, val_evaluator_PascalVOC,\n    val_evaluator_pistols, val_evaluator_plantdoc, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_selfdrivingCar,\n    val_evaluator_ShellfishOpenImages, val_evaluator_ThermalCheetah,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_UnoCards,\n    val_evaluator_VehiclesOpenImages, val_evaluator_WildfireSmoke,\n    val_evaluator_websiteScreenshots\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/glip/odinw/glip_atss_swin-t_bc_fpn_dyhead_pretrain_odinw13.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_odinw13.py'\n\nmodel = dict(bbox_head=dict(early_fuse=True))\n"
  },
  {
    "path": "configs/glip/odinw/glip_atss_swin-t_bc_fpn_dyhead_pretrain_odinw35.py",
    "content": "_base_ = './glip_atss_swin-t_a_fpn_dyhead_pretrain_odinw35.py'\n\nmodel = dict(bbox_head=dict(early_fuse=True))\n"
  },
  {
    "path": "configs/glip/odinw/override_category.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\n\nimport mmengine\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Override Category')\n    parser.add_argument('data_root')\n    return parser.parse_args()\n\n\ndef main():\n    args = parse_args()\n\n    ChessPieces = [{\n        'id': 1,\n        'name': '  ',\n        'supercategory': 'pieces'\n    }, {\n        'id': 2,\n        'name': 'black bishop',\n        'supercategory': 'pieces'\n    }, {\n        'id': 3,\n        'name': 'black king',\n        'supercategory': 'pieces'\n    }, {\n        'id': 4,\n        'name': 'black knight',\n        'supercategory': 'pieces'\n    }, {\n        'id': 5,\n        'name': 'black pawn',\n        'supercategory': 'pieces'\n    }, {\n        'id': 6,\n        'name': 'black queen',\n        'supercategory': 'pieces'\n    }, {\n        'id': 7,\n        'name': 'black rook',\n        'supercategory': 'pieces'\n    }, {\n        'id': 8,\n        'name': 'white bishop',\n        'supercategory': 'pieces'\n    }, {\n        'id': 9,\n        'name': 'white king',\n        'supercategory': 'pieces'\n    }, {\n        'id': 10,\n        'name': 'white knight',\n        'supercategory': 'pieces'\n    }, {\n        'id': 11,\n        'name': 'white pawn',\n        'supercategory': 'pieces'\n    }, {\n        'id': 12,\n        'name': 'white queen',\n        'supercategory': 'pieces'\n    }, {\n        'id': 13,\n        'name': 'white rook',\n        'supercategory': 'pieces'\n    }]\n\n    _data_root = args.data_root + 'ChessPieces/Chess Pieces.v23-raw.coco/'\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = ChessPieces\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n    CottontailRabbits = [{\n        'id': 1,\n        'name': 'rabbit',\n        'supercategory': 'Cottontail-Rabbit'\n    }]\n\n    _data_root = args.data_root + 'CottontailRabbits/'\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = CottontailRabbits\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n    NorthAmericaMushrooms = [{\n        'id': 1,\n        'name': 'flat mushroom',\n        'supercategory': 'mushroom'\n    }, {\n        'id': 2,\n        'name': 'yellow mushroom',\n        'supercategory': 'mushroom'\n    }]\n\n    _data_root = args.data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = NorthAmericaMushrooms\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "configs/gn/README.md",
    "content": "# GN\n\n> [Group Normalization](https://arxiv.org/abs/1803.08494)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nBatch Normalization (BN) is a milestone technique in the development of deep learning, enabling various networks to train. However, normalizing along the batch dimension introduces problems --- BN's error increases rapidly when the batch size becomes smaller, caused by inaccurate batch statistics estimation. This limits BN's usage for training larger models and transferring features to computer vision tasks including detection, segmentation, and video, which require small batches constrained by memory consumption. In this paper, we present Group Normalization (GN) as a simple alternative to BN. GN divides the channels into groups and computes within each group the mean and variance for normalization. GN's computation is independent of batch sizes, and its accuracy is stable in a wide range of batch sizes. On ResNet-50 trained in ImageNet, GN has 10.6% lower error than its BN counterpart when using a batch size of 2; when using typical batch sizes, GN is comparably good with BN and outperforms other normalization variants. Moreover, GN can be naturally transferred from pre-training to fine-tuning. GN can outperform its BN-based counterparts for object detection and segmentation in COCO, and for video classification in Kinetics, showing that GN can effectively replace the powerful BN in a variety of tasks. GN can be easily implemented by a few lines of code in modern libraries.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143889171-d4660307-3a4f-4ac2-a0ad-7d17c2f045b1.png\" height=\"200\"/>\n</div>\n\n## Results and Models\n\n|   Backbone    |   model    | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                         Config                          |                                                                                                                                                                  Download                                                                                                                                                                   |\n| :-----------: | :--------: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN (d)  | Mask R-CNN |   2x    |   7.1    |      11.0      |  40.2  |  36.4   |     [config](./mask-rcnn_r50_fpn_gn-all_2x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_2x_coco/mask_rcnn_r50_fpn_gn-all_2x_coco_20200206-8eee02a6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_2x_coco/mask_rcnn_r50_fpn_gn-all_2x_coco_20200206_050355.log.json)                 |\n| R-50-FPN (d)  | Mask R-CNN |   3x    |   7.1    |       -        |  40.5  |  36.7   |     [config](./mask-rcnn_r50_fpn_gn-all_3x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_3x_coco/mask_rcnn_r50_fpn_gn-all_3x_coco_20200214-8b23b1e5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_3x_coco/mask_rcnn_r50_fpn_gn-all_3x_coco_20200214_063512.log.json)                 |\n| R-101-FPN (d) | Mask R-CNN |   2x    |   9.9    |      9.0       |  41.9  |  37.6   |    [config](./mask-rcnn_r101_fpn_gn-all_2x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r101_fpn_gn-all_2x_coco/mask_rcnn_r101_fpn_gn-all_2x_coco_20200205-d96b1b50.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r101_fpn_gn-all_2x_coco/mask_rcnn_r101_fpn_gn-all_2x_coco_20200205_234402.log.json)               |\n| R-101-FPN (d) | Mask R-CNN |   3x    |   9.9    |                |  42.1  |  38.0   |    [config](./mask-rcnn_r101_fpn_gn-all_3x_coco.py)     |           [model](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r101_fpn_gn-all_3x_coco/mask_rcnn_r101_fpn_gn-all_3x_coco_20200513_181609-0df864f4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r101_fpn_gn-all_3x_coco/mask_rcnn_r101_fpn_gn-all_3x_coco_20200513_181609.log.json)            |\n| R-50-FPN (c)  | Mask R-CNN |   2x    |   7.1    |      10.9      |  40.0  |  36.1   | [config](./mask-rcnn_r50-contrib_fpn_gn-all_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_contrib_2x_coco/mask_rcnn_r50_fpn_gn-all_contrib_2x_coco_20200207-20d3e849.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_contrib_2x_coco/mask_rcnn_r50_fpn_gn-all_contrib_2x_coco_20200207_225832.log.json) |\n| R-50-FPN (c)  | Mask R-CNN |   3x    |   7.1    |       -        |  40.1  |  36.2   | [config](./mask-rcnn_r50-contrib_fpn_gn-all_3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_contrib_3x_coco/mask_rcnn_r50_fpn_gn-all_contrib_3x_coco_20200225-542aefbc.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_contrib_3x_coco/mask_rcnn_r50_fpn_gn-all_contrib_3x_coco_20200225_235135.log.json) |\n\n**Notes:**\n\n- (d) means pretrained model converted from Detectron, and (c) means the contributed model pretrained by [@thangvubk](https://github.com/thangvubk).\n- The `3x` schedule is epoch \\[28, 34, 36\\].\n- **Memory, Train/Inf time is outdated.**\n\n## Citation\n\n```latex\n@inproceedings{wu2018group,\n  title={Group Normalization},\n  author={Wu, Yuxin and He, Kaiming},\n  booktitle={Proceedings of the European Conference on Computer Vision (ECCV)},\n  year={2018}\n}\n```\n"
  },
  {
    "path": "configs/gn/mask-rcnn_r101_fpn_gn-all_2x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_gn-all_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron/resnet101_gn')))\n"
  },
  {
    "path": "configs/gn/mask-rcnn_r101_fpn_gn-all_3x_coco.py",
    "content": "_base_ = './mask-rcnn_r101_fpn_gn-all_2x_coco.py'\n\n# learning policy\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn/mask-rcnn_r50-contrib_fpn_gn-all_2x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://contrib/resnet50_gn')),\n    neck=dict(norm_cfg=norm_cfg),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=norm_cfg),\n        mask_head=dict(norm_cfg=norm_cfg)))\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn/mask-rcnn_r50-contrib_fpn_gn-all_3x_coco.py",
    "content": "_base_ = './mask-rcnn_r50-contrib_fpn_gn-all_2x_coco.py'\n\n# learning policy\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron/resnet50_gn')),\n    neck=dict(norm_cfg=norm_cfg),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=norm_cfg),\n        mask_head=dict(norm_cfg=norm_cfg)))\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn/mask-rcnn_r50_fpn_gn-all_3x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_gn-all_2x_coco.py'\n\n# learning policy\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn/metafile.yml",
    "content": "Collections:\n  - Name: Group Normalization\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Group Normalization\n    Paper:\n      URL: https://arxiv.org/abs/1803.08494\n      Title: 'Group Normalization'\n    README: configs/gn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py\n      Version: v2.0.0\n\nModels:\n  - Name: mask-rcnn_r50_fpn_gn-all_2x_coco\n    In Collection: Group Normalization\n    Config: configs/gn/mask-rcnn_r50_fpn_gn-all_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      inference time (ms/im):\n        - value: 90.91\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_2x_coco/mask_rcnn_r50_fpn_gn-all_2x_coco_20200206-8eee02a6.pth\n\n  - Name: mask-rcnn_r50_fpn_gn-all_3x_coco\n    In Collection: Group Normalization\n    Config: configs/gn/mask-rcnn_r50_fpn_gn-all_3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      inference time (ms/im):\n        - value: 90.91\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_3x_coco/mask_rcnn_r50_fpn_gn-all_3x_coco_20200214-8b23b1e5.pth\n\n  - Name: mask-rcnn_r101_fpn_gn-all_2x_coco\n    In Collection: Group Normalization\n    Config: configs/gn/mask-rcnn_r101_fpn_gn-all_2x_coco.py\n    Metadata:\n      Training Memory (GB): 9.9\n      inference time (ms/im):\n        - value: 111.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r101_fpn_gn-all_2x_coco/mask_rcnn_r101_fpn_gn-all_2x_coco_20200205-d96b1b50.pth\n\n  - Name: mask-rcnn_r101_fpn_gn-all_3x_coco\n    In Collection: Group Normalization\n    Config: configs/gn/mask-rcnn_r101_fpn_gn-all_3x_coco.py\n    Metadata:\n      Training Memory (GB): 9.9\n      inference time (ms/im):\n        - value: 111.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r101_fpn_gn-all_3x_coco/mask_rcnn_r101_fpn_gn-all_3x_coco_20200513_181609-0df864f4.pth\n\n  - Name: mask-rcnn_r50_fpn_gn-all_contrib_2x_coco\n    In Collection: Group Normalization\n    Config: configs/gn/mask-rcnn_r50-contrib_fpn_gn-all_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      inference time (ms/im):\n        - value: 91.74\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_contrib_2x_coco/mask_rcnn_r50_fpn_gn-all_contrib_2x_coco_20200207-20d3e849.pth\n\n  - Name: mask-rcnn_r50_fpn_gn-all_contrib_3x_coco\n    In Collection: Group Normalization\n    Config: configs/gn/mask-rcnn_r50-contrib_fpn_gn-all_3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      inference time (ms/im):\n        - value: 91.74\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn/mask_rcnn_r50_fpn_gn-all_contrib_3x_coco/mask_rcnn_r50_fpn_gn-all_contrib_3x_coco_20200225-542aefbc.pth\n"
  },
  {
    "path": "configs/gn+ws/README.md",
    "content": "# GN + WS\n\n> [Weight Standardization](https://arxiv.org/abs/1903.10520)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nBatch Normalization (BN) has become an out-of-box technique to improve deep network training. However, its effectiveness is limited for micro-batch training, i.e., each GPU typically has only 1-2 images for training, which is inevitable for many computer vision tasks, e.g., object detection and semantic segmentation, constrained by memory consumption. To address this issue, we propose Weight Standardization (WS) and Batch-Channel Normalization (BCN) to bring two success factors of BN into micro-batch training: 1) the smoothing effects on the loss landscape and 2) the ability to avoid harmful elimination singularities along the training trajectory. WS standardizes the weights in convolutional layers to smooth the loss landscape by reducing the Lipschitz constants of the loss and the gradients; BCN combines batch and channel normalizations and leverages estimated statistics of the activations in convolutional layers to keep networks away from elimination singularities. We validate WS and BCN on comprehensive computer vision tasks, including image classification, object detection, instance segmentation, video recognition and semantic segmentation. All experimental results consistently show that WS and BCN improve micro-batch training significantly. Moreover, using WS and BCN with micro-batch training is even able to match or outperform the performances of BN with large-batch training.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143889309-b1f3487d-abfe-4615-befc-79384bc8b65c.png\"/ height=\"200\">\n</div>\n\n## Results and Models\n\nFaster R-CNN\n\n|    Backbone     |  Style  | Normalization | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                           Config                            |                                                                                                                                                                               Download                                                                                                                                                                                |\n| :-------------: | :-----: | :-----------: | :-----: | :------: | :------------: | :----: | :-----: | :---------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |     GN+WS     |   1x    |   5.9    |      11.7      |  39.7  |    -    |    [config](./faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_r50_fpn_gn_ws-all_1x_coco/faster_rcnn_r50_fpn_gn_ws-all_1x_coco_20200130-613d9fe2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_r50_fpn_gn_ws-all_1x_coco/faster_rcnn_r50_fpn_gn_ws-all_1x_coco_20200130_210936.log.json)               |\n|    R-101-FPN    | pytorch |     GN+WS     |   1x    |   8.9    |      9.0       |  41.7  |    -    |    [config](./faster-rcnn_r101_fpn_gn-ws-all_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_r101_fpn_gn_ws-all_1x_coco/faster_rcnn_r101_fpn_gn_ws-all_1x_coco_20200205-a93b0d75.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_r101_fpn_gn_ws-all_1x_coco/faster_rcnn_r101_fpn_gn_ws-all_1x_coco_20200205_232146.log.json)             |\n| X-50-32x4d-FPN  | pytorch |     GN+WS     |   1x    |   7.0    |      10.3      |  40.7  |    -    | [config](./faster-rcnn_x50-32x4d_fpn_gn-ws-all_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_x50_32x4d_fpn_gn_ws-all_1x_coco/faster_rcnn_x50_32x4d_fpn_gn_ws-all_1x_coco_20200203-839c5d9d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_x50_32x4d_fpn_gn_ws-all_1x_coco/faster_rcnn_x50_32x4d_fpn_gn_ws-all_1x_coco_20200203_220113.log.json)   |\n| X-101-32x4d-FPN | pytorch |     GN+WS     |   1x    |   10.8   |      7.6       |  42.1  |    -    | [config](./faster-rcnn_x101-32x4d_fpn_gn-ws-all_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_x101_32x4d_fpn_gn_ws-all_1x_coco/faster_rcnn_x101_32x4d_fpn_gn_ws-all_1x_coco_20200212-27da1bc2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_x101_32x4d_fpn_gn_ws-all_1x_coco/faster_rcnn_x101_32x4d_fpn_gn_ws-all_1x_coco_20200212_195302.log.json) |\n\nMask R-CNN\n\n|    Backbone     |  Style  | Normalization |  Lr schd  | Mem (GB) | Inf time (fps) | box AP | mask AP |                              Config                              |                                                                                                                                                                                         Download                                                                                                                                                                                          |\n| :-------------: | :-----: | :-----------: | :-------: | :------: | :------------: | :----: | :-----: | :--------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |     GN+WS     |    2x     |   7.3    |      10.5      |  40.6  |  36.6   |        [config](./mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py)        |                             [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r50_fpn_gn_ws-all_2x_coco/mask_rcnn_r50_fpn_gn_ws-all_2x_coco_20200226-16acb762.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r50_fpn_gn_ws-all_2x_coco/mask_rcnn_r50_fpn_gn_ws-all_2x_coco_20200226_062128.log.json)                             |\n|    R-101-FPN    | pytorch |     GN+WS     |    2x     |   10.3   |      8.6       |  42.0  |  37.7   |       [config](./mask-rcnn_r101_fpn_gn-ws-all_2x_coco.py)        |                           [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r101_fpn_gn_ws-all_2x_coco/mask_rcnn_r101_fpn_gn_ws-all_2x_coco_20200212-ea357cd9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r101_fpn_gn_ws-all_2x_coco/mask_rcnn_r101_fpn_gn_ws-all_2x_coco_20200212_213627.log.json)                           |\n| X-50-32x4d-FPN  | pytorch |     GN+WS     |    2x     |   8.4    |      9.3       |  41.1  |  37.0   |     [config](./mask-rcnn_x50-32x4d_fpn_gn-ws-all_2x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x50_32x4d_fpn_gn_ws-all_2x_coco/mask_rcnn_x50_32x4d_fpn_gn_ws-all_2x_coco_20200216-649fdb6f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x50_32x4d_fpn_gn_ws-all_2x_coco/mask_rcnn_x50_32x4d_fpn_gn_ws-all_2x_coco_20200216_201500.log.json)                 |\n| X-101-32x4d-FPN | pytorch |     GN+WS     |    2x     |   12.2   |      7.1       |  42.1  |  37.9   |    [config](./mask-rcnn_x101-32x4d_fpn_gn-ws-all_2x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x101_32x4d_fpn_gn_ws-all_2x_coco/mask_rcnn_x101_32x4d_fpn_gn_ws-all_2x_coco_20200319-33fb95b5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x101_32x4d_fpn_gn_ws-all_2x_coco/mask_rcnn_x101_32x4d_fpn_gn_ws-all_2x_coco_20200319_104101.log.json)               |\n|    R-50-FPN     | pytorch |     GN+WS     | 20-23-24e |   7.3    |       -        |  41.1  |  37.1   |    [config](./mask-rcnn_r50_fpn_gn-ws-all_20-23-24e_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r50_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_r50_fpn_gn_ws-all_20_23_24e_coco_20200213-487d1283.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r50_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_r50_fpn_gn_ws-all_20_23_24e_coco_20200213_035123.log.json)               |\n|    R-101-FPN    | pytorch |     GN+WS     | 20-23-24e |   10.3   |       -        |  43.1  |  38.6   |    [config](./mask-rcnn_r101_fpn_gn-ws-all_20-23-24e_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r101_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_r101_fpn_gn_ws-all_20_23_24e_coco_20200213-57b5a50f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r101_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_r101_fpn_gn_ws-all_20_23_24e_coco_20200213_130142.log.json)             |\n| X-50-32x4d-FPN  | pytorch |     GN+WS     | 20-23-24e |   8.4    |       -        |  42.1  |  38.0   | [config](./mask-rcnn_x50-32x4d_fpn_gn-ws-all_20-23-24e_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x50_32x4d_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_x50_32x4d_fpn_gn_ws-all_20_23_24e_coco_20200226-969bcb2c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x50_32x4d_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_x50_32x4d_fpn_gn_ws-all_20_23_24e_coco_20200226_093732.log.json)   |\n| X-101-32x4d-FPN | pytorch |     GN+WS     | 20-23-24e |   12.2   |       -        |  42.7  |  38.5   | [config](./mask-rcnn_x101-32x4d_fpn_gn-ws-all_20-23-24e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x101_32x4d_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_x101_32x4d_fpn_gn_ws-all_20_23_24e_coco_20200316-e6cd35ef.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x101_32x4d_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_x101_32x4d_fpn_gn_ws-all_20_23_24e_coco_20200316_013741.log.json) |\n\nNote:\n\n- GN+WS requires about 5% more memory than GN, and it is only 5% slower than GN.\n- In the paper, a 20-23-24e lr schedule is used instead of 2x.\n- The X-50-GN and X-101-GN pretrained models are also shared by the authors.\n\n## Citation\n\n```latex\n@article{weightstandardization,\n  author    = {Siyuan Qiao and Huiyu Wang and Chenxi Liu and Wei Shen and Alan Yuille},\n  title     = {Weight Standardization},\n  journal   = {arXiv preprint arXiv:1903.10520},\n  year      = {2019},\n}\n```\n"
  },
  {
    "path": "configs/gn+ws/faster-rcnn_r101_fpn_gn-ws-all_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://jhu/resnet101_gn_ws')))\n"
  },
  {
    "path": "configs/gn+ws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nconv_cfg = dict(type='ConvWS')\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        conv_cfg=conv_cfg,\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://jhu/resnet50_gn_ws')),\n    neck=dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg)))\n"
  },
  {
    "path": "configs/gn+ws/faster-rcnn_x101-32x4d_fpn_gn-ws-all_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py'\nconv_cfg = dict(type='ConvWS')\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        style='pytorch',\n        conv_cfg=conv_cfg,\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://jhu/resnext101_32x4d_gn_ws')))\n"
  },
  {
    "path": "configs/gn+ws/faster-rcnn_x50-32x4d_fpn_gn-ws-all_1x_coco.py",
    "content": "_base_ = './faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py'\nconv_cfg = dict(type='ConvWS')\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=50,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        style='pytorch',\n        conv_cfg=conv_cfg,\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://jhu/resnext50_32x4d_gn_ws')))\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_r101_fpn_gn-ws-all_20-23-24e_coco.py",
    "content": "_base_ = './mask-rcnn_r101_fpn_gn-ws-all_2x_coco.py'\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20, 23],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_r101_fpn_gn-ws-all_2x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://jhu/resnet101_gn_ws')))\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_r50_fpn_gn-ws-all_20-23-24e_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py'\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20, 23],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nconv_cfg = dict(type='ConvWS')\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        conv_cfg=conv_cfg,\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://jhu/resnet50_gn_ws')),\n    neck=dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg),\n        mask_head=dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg)))\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_x101-32x4d_fpn_gn-ws-all_20-23-24e_coco.py",
    "content": "_base_ = './mask-rcnn_x101-32x4d_fpn_gn-ws-all_2x_coco.py'\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20, 23],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_x101-32x4d_fpn_gn-ws-all_2x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py'\n# model settings\nconv_cfg = dict(type='ConvWS')\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        style='pytorch',\n        conv_cfg=conv_cfg,\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://jhu/resnext101_32x4d_gn_ws')))\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_x50-32x4d_fpn_gn-ws-all_20-23-24e_coco.py",
    "content": "_base_ = './mask-rcnn_x50-32x4d_fpn_gn-ws-all_2x_coco.py'\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20, 23],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/gn+ws/mask-rcnn_x50-32x4d_fpn_gn-ws-all_2x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py'\n# model settings\nconv_cfg = dict(type='ConvWS')\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=50,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        style='pytorch',\n        conv_cfg=conv_cfg,\n        norm_cfg=norm_cfg,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://jhu/resnext50_32x4d_gn_ws')))\n"
  },
  {
    "path": "configs/gn+ws/metafile.yml",
    "content": "Collections:\n  - Name: Weight Standardization\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Group Normalization\n        - Weight Standardization\n    Paper:\n      URL: https://arxiv.org/abs/1903.10520\n      Title: 'Weight Standardization'\n    README: configs/gn+ws/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/configs/gn%2Bws/mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py\n      Version: v2.0.0\n\nModels:\n  - Name: faster-rcnn_r50_fpn_gn_ws-all_1x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/faster-rcnn_r50_fpn_gn-ws-all_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.9\n      inference time (ms/im):\n        - value: 85.47\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_r50_fpn_gn_ws-all_1x_coco/faster_rcnn_r50_fpn_gn_ws-all_1x_coco_20200130-613d9fe2.pth\n\n  - Name: faster-rcnn_r101_fpn_gn-ws-all_1x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/faster-rcnn_r101_fpn_gn-ws-all_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.9\n      inference time (ms/im):\n        - value: 111.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_r101_fpn_gn_ws-all_1x_coco/faster_rcnn_r101_fpn_gn_ws-all_1x_coco_20200205-a93b0d75.pth\n\n  - Name: faster-rcnn_x50-32x4d_fpn_gn-ws-all_1x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/faster-rcnn_x50-32x4d_fpn_gn-ws-all_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 97.09\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_x50_32x4d_fpn_gn_ws-all_1x_coco/faster_rcnn_x50_32x4d_fpn_gn_ws-all_1x_coco_20200203-839c5d9d.pth\n\n  - Name: faster-rcnn_x101-32x4d_fpn_gn-ws-all_1x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/faster-rcnn_x101-32x4d_fpn_gn-ws-all_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.8\n      inference time (ms/im):\n        - value: 131.58\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/faster_rcnn_x101_32x4d_fpn_gn_ws-all_1x_coco/faster_rcnn_x101_32x4d_fpn_gn_ws-all_1x_coco_20200212-27da1bc2.pth\n\n  - Name: mask-rcnn_r50_fpn_gn_ws-all_2x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_r50_fpn_gn-ws-all_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.3\n      inference time (ms/im):\n        - value: 95.24\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  36.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r50_fpn_gn_ws-all_2x_coco/mask_rcnn_r50_fpn_gn_ws-all_2x_coco_20200226-16acb762.pth\n\n  - Name: mask-rcnn_r101_fpn_gn-ws-all_2x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_r101_fpn_gn-ws-all_2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.3\n      inference time (ms/im):\n        - value: 116.28\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r101_fpn_gn_ws-all_2x_coco/mask_rcnn_r101_fpn_gn_ws-all_2x_coco_20200212-ea357cd9.pth\n\n  - Name: mask-rcnn_x50-32x4d_fpn_gn-ws-all_2x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_x50-32x4d_fpn_gn-ws-all_2x_coco.py\n    Metadata:\n      Training Memory (GB): 8.4\n      inference time (ms/im):\n        - value: 107.53\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x50_32x4d_fpn_gn_ws-all_2x_coco/mask_rcnn_x50_32x4d_fpn_gn_ws-all_2x_coco_20200216-649fdb6f.pth\n\n  - Name: mask-rcnn_x101-32x4d_fpn_gn-ws-all_2x_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_x101-32x4d_fpn_gn-ws-all_2x_coco.py\n    Metadata:\n      Training Memory (GB): 12.2\n      inference time (ms/im):\n        - value: 140.85\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x101_32x4d_fpn_gn_ws-all_2x_coco/mask_rcnn_x101_32x4d_fpn_gn_ws-all_2x_coco_20200319-33fb95b5.pth\n\n  - Name: mask-rcnn_r50_fpn_gn_ws-all_20_23_24e_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_r50_fpn_gn-ws-all_20-23-24e_coco.py\n    Metadata:\n      Training Memory (GB): 7.3\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r50_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_r50_fpn_gn_ws-all_20_23_24e_coco_20200213-487d1283.pth\n\n  - Name: mask-rcnn_r101_fpn_gn-ws-all_20-23-24e_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_r101_fpn_gn-ws-all_20-23-24e_coco.py\n    Metadata:\n      Training Memory (GB): 10.3\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_r101_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_r101_fpn_gn_ws-all_20_23_24e_coco_20200213-57b5a50f.pth\n\n  - Name: mask-rcnn_x50-32x4d_fpn_gn-ws-all_20-23-24e_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_x50-32x4d_fpn_gn-ws-all_20-23-24e_coco.py\n    Metadata:\n      Training Memory (GB): 8.4\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x50_32x4d_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_x50_32x4d_fpn_gn_ws-all_20_23_24e_coco_20200226-969bcb2c.pth\n\n  - Name: mask-rcnn_x101-32x4d_fpn_gn-ws-all_20-23-24e_coco\n    In Collection: Weight Standardization\n    Config: configs/gn%2Bws/mask-rcnn_x101-32x4d_fpn_gn-ws-all_20-23-24e_coco.py\n    Metadata:\n      Training Memory (GB): 12.2\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:  38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/gn%2Bws/mask_rcnn_x101_32x4d_fpn_gn_ws-all_20_23_24e_coco/mask_rcnn_x101_32x4d_fpn_gn_ws-all_20_23_24e_coco_20200316-e6cd35ef.pth\n"
  },
  {
    "path": "configs/grid_rcnn/README.md",
    "content": "# Grid R-CNN\n\n> [Grid R-CNN](https://arxiv.org/abs/1811.12030)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThis paper proposes a novel object detection framework named Grid R-CNN, which adopts a grid guided localization mechanism for accurate object detection. Different from the traditional regression based methods, the Grid R-CNN captures the spatial information explicitly and enjoys the position sensitive property of fully convolutional architecture. Instead of using only two independent points, we design a multi-point supervision formulation to encode more clues in order to reduce the impact of inaccurate prediction of specific points. To take the full advantage of the correlation of points in a grid, we propose a two-stage information fusion strategy to fuse feature maps of neighbor grid points. The grid guided localization approach is easy to be extended to different state-of-the-art detection frameworks. Grid R-CNN leads to high quality object localization, and experiments demonstrate that it achieves a 4.1% AP gain at IoU=0.8 and a 10.0% AP gain at IoU=0.9 on COCO benchmark compared to Faster R-CNN with Res50 backbone and FPN architecture.\n\nGrid R-CNN is a well-performed objection detection framework. It transforms the traditional box offset regression problem into a grid point estimation problem. With the guidance of the grid points, it can obtain high-quality localization results. However, the speed of Grid R-CNN is not so satisfactory. In this technical report we present Grid R-CNN Plus, a better and faster version of Grid R-CNN. We have made several updates that significantly speed up the framework and simultaneously improve the accuracy. On COCO dataset, the Res50-FPN based Grid R-CNN Plus detector achieves an mAP of 40.4%, outperforming the baseline on the same model by 3.0 points with similar inference time.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143890379-5d9e6233-0533-48b4-88b9-bc33abbd9f82.png\"/>\n</div>\n\n## Results and Models\n\n|  Backbone   | Lr schd | Mem (GB) | Inf time (fps) | box AP |                         Config                          |                                                                                                                                                                         Download                                                                                                                                                                          |\n| :---------: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50     |   2x    |   5.1    |      15.0      |  40.4  |    [config](./grid-rcnn_r50_fpn_gn-head_2x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_r50_fpn_gn-head_2x_coco/grid_rcnn_r50_fpn_gn-head_2x_coco_20200130-6cca8223.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_r50_fpn_gn-head_2x_coco/grid_rcnn_r50_fpn_gn-head_2x_coco_20200130_221140.log.json)               |\n|    R-101    |   2x    |   7.0    |      12.6      |  41.5  |    [config](./grid-rcnn_r101_fpn_gn-head_2x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_r101_fpn_gn-head_2x_coco/grid_rcnn_r101_fpn_gn-head_2x_coco_20200309-d6eca030.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_r101_fpn_gn-head_2x_coco/grid_rcnn_r101_fpn_gn-head_2x_coco_20200309_164224.log.json)             |\n| X-101-32x4d |   2x    |   8.3    |      10.8      |  42.9  | [config](./grid-rcnn_x101-32x4d_fpn_gn-head_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_x101_32x4d_fpn_gn-head_2x_coco/grid_rcnn_x101_32x4d_fpn_gn-head_2x_coco_20200130-d8f0e3ff.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_x101_32x4d_fpn_gn-head_2x_coco/grid_rcnn_x101_32x4d_fpn_gn-head_2x_coco_20200130_215413.log.json) |\n| X-101-64x4d |   2x    |   11.3   |      7.7       |  43.0  | [config](./grid-rcnn_x101-64x4d_fpn_gn-head_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_x101_64x4d_fpn_gn-head_2x_coco/grid_rcnn_x101_64x4d_fpn_gn-head_2x_coco_20200204-ec76a754.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_x101_64x4d_fpn_gn-head_2x_coco/grid_rcnn_x101_64x4d_fpn_gn-head_2x_coco_20200204_080641.log.json) |\n\n**Notes:**\n\n- All models are trained with 8 GPUs instead of 32 GPUs in the original paper.\n- The warming up lasts for 1 epoch and `2x` here indicates 25 epochs.\n\n## Citation\n\n```latex\n@inproceedings{lu2019grid,\n  title={Grid r-cnn},\n  author={Lu, Xin and Li, Buyu and Yue, Yuxin and Li, Quanquan and Yan, Junjie},\n  booktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition},\n  year={2019}\n}\n\n@article{lu2019grid,\n  title={Grid R-CNN Plus: Faster and Better},\n  author={Lu, Xin and Li, Buyu and Yue, Yuxin and Li, Quanquan and Yan, Junjie},\n  journal={arXiv preprint arXiv:1906.05688},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/grid_rcnn/grid-rcnn_r101_fpn_gn-head_2x_coco.py",
    "content": "_base_ = './grid-rcnn_r50_fpn_gn-head_2x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_1x_coco.py",
    "content": "_base_ = './grid-rcnn_r50_fpn_gn-head_2x_coco.py'\n\n# training schedule\nmax_epochs = 12\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.0001, by_epoch=False, begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    type='GridRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        type='GridRoIHead',\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            with_reg=False,\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False),\n        grid_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        grid_head=dict(\n            type='GridHead',\n            grid_points=9,\n            num_convs=8,\n            in_channels=256,\n            point_feat_channels=64,\n            norm_cfg=dict(type='GN', num_groups=36),\n            loss_grid=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=15))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            pos_radius=1,\n            pos_weight=-1,\n            max_num_grid=192,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.03,\n            nms=dict(type='nms', iou_threshold=0.3),\n            max_per_img=100)))\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# training schedule\nmax_epochs = 25\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 80,\n        by_epoch=False,\n        begin=0,\n        end=3665),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[17, 23],\n        gamma=0.1)\n]\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/grid_rcnn/grid-rcnn_x101-32x4d_fpn_gn-head_2x_coco.py",
    "content": "_base_ = './grid-rcnn_r50_fpn_gn-head_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/grid_rcnn/grid-rcnn_x101-64x4d_fpn_gn-head_2x_coco.py",
    "content": "_base_ = './grid-rcnn_x101-32x4d_fpn_gn-head_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/grid_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Grid R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RPN\n        - Dilated Convolution\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/abs/1906.05688\n      Title: 'Grid R-CNN'\n    README: configs/grid_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/grid_rcnn.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: grid-rcnn_r50_fpn_gn-head_2x_coco\n    In Collection: Grid R-CNN\n    Config: configs/grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py\n    Metadata:\n      Training Memory (GB): 5.1\n      inference time (ms/im):\n        - value: 66.67\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_r50_fpn_gn-head_2x_coco/grid_rcnn_r50_fpn_gn-head_2x_coco_20200130-6cca8223.pth\n\n  - Name: grid-rcnn_r101_fpn_gn-head_2x_coco\n    In Collection: Grid R-CNN\n    Config: configs/grid_rcnn/grid-rcnn_r101_fpn_gn-head_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 79.37\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_r101_fpn_gn-head_2x_coco/grid_rcnn_r101_fpn_gn-head_2x_coco_20200309-d6eca030.pth\n\n  - Name: grid-rcnn_x101-32x4d_fpn_gn-head_2x_coco\n    In Collection: Grid R-CNN\n    Config: configs/grid_rcnn/grid-rcnn_x101-32x4d_fpn_gn-head_2x_coco.py\n    Metadata:\n      Training Memory (GB): 8.3\n      inference time (ms/im):\n        - value: 92.59\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_x101_32x4d_fpn_gn-head_2x_coco/grid_rcnn_x101_32x4d_fpn_gn-head_2x_coco_20200130-d8f0e3ff.pth\n\n  - Name: grid-rcnn_x101-64x4d_fpn_gn-head_2x_coco\n    In Collection: Grid R-CNN\n    Config: configs/grid_rcnn/grid-rcnn_x101-64x4d_fpn_gn-head_2x_coco.py\n    Metadata:\n      Training Memory (GB): 11.3\n      inference time (ms/im):\n        - value: 129.87\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/grid_rcnn/grid_rcnn_x101_64x4d_fpn_gn-head_2x_coco/grid_rcnn_x101_64x4d_fpn_gn-head_2x_coco_20200204-ec76a754.pth\n"
  },
  {
    "path": "configs/groie/README.md",
    "content": "# GRoIE\n\n> [A novel Region of Interest Extraction Layer for Instance Segmentation](https://arxiv.org/abs/2004.13665)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nGiven the wide diffusion of deep neural network architectures for computer vision tasks, several new applications are nowadays more and more feasible. Among them, a particular attention has been recently given to instance segmentation, by exploiting the results achievable by two-stage networks (such as Mask R-CNN or Faster R-CNN), derived from R-CNN. In these complex architectures, a crucial role is played by the Region of Interest (RoI) extraction layer, devoted to extracting a coherent subset of features from a single Feature Pyramid Network (FPN) layer attached on top of a backbone.\nThis paper is motivated by the need to overcome the limitations of existing RoI extractors which select only one (the best) layer from FPN. Our intuition is that all the layers of FPN retain useful information. Therefore, the proposed layer (called Generic RoI Extractor - GRoIE) introduces non-local building blocks and attention mechanisms to boost the performance.\nA comprehensive ablation study at component level is conducted to find the best set of algorithms and parameters for the GRoIE layer. Moreover, GRoIE can be integrated seamlessly with every two-stage architecture for both object detection and instance segmentation tasks. Therefore, the improvements brought about by the use of GRoIE in different state-of-the-art architectures are also evaluated. The proposed layer leads up to gain a 1.1% AP improvement on bounding box detection and 1.7% AP improvement on instance segmentation.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143891453-afdcdaf4-a868-4a28-ad20-dc710a517a76.png\"/>\n</div>\n\n## Introduction\n\nBy Leonardo Rossi, Akbar Karimi and Andrea Prati from\n[IMPLab](http://implab.ce.unipr.it/).\n\nWe provide configs to reproduce the results in the paper for\n\"*A novel Region of Interest Extraction Layer for Instance Segmentation*\"\non COCO object detection.\n\nThis paper is motivated by the need to overcome to the limitations of existing\nRoI extractors which select only one (the best) layer from FPN.\n\nOur intuition is that all the layers of FPN retain useful information.\n\nTherefore, the proposed layer (called Generic RoI Extractor - **GRoIE**)\nintroduces non-local building blocks and attention mechanisms to boost the\nperformance.\n\n## Results and Models\n\nThe results on COCO 2017 minival (5k images) are shown in the below table.\n\n### Application of GRoIE to different architectures\n\n| Backbone  |     Method      | Lr schd | box AP | mask AP |                                Config                                |                                                                                                                                                                                                                   Download                                                                                                                                                                                                                   |\n| :-------: | :-------------: | :-----: | :----: | :-----: | :------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN  | Faster Original |   1x    |  37.4  |         |       [config](../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py)        |                                                                  [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130_204655.log.json)                                                                   |\n| R-50-FPN  |     + GRoIE     |   1x    |  38.3  |         |           [config](./faste-rcnn_r50_fpn_groie_1x_coco.py)            |                                                         [model](https://download.openmmlab.com/mmdetection/v2.0/groie/faster_rcnn_r50_fpn_groie_1x_coco/faster_rcnn_r50_fpn_groie_1x_coco_20200604_211715-66ee9516.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/groie/faster_rcnn_r50_fpn_groie_1x_coco/faster_rcnn_r50_fpn_groie_1x_coco_20200604_211715.log.json)                                                         |\n| R-50-FPN  |   Grid R-CNN    |   1x    |  39.1  |         |        [config](./grid-rcnn_r50_fpn_gn-head-groie_1x_coco.py)        |                                             [model](https://download.openmmlab.com/mmdetection/v2.0/groie/grid_rcnn_r50_fpn_gn-head_groie_1x_coco/grid_rcnn_r50_fpn_gn-head_groie_1x_coco_20200605_202059-4b75d86f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/groie/grid_rcnn_r50_fpn_gn-head_groie_1x_coco/grid_rcnn_r50_fpn_gn-head_groie_1x_coco_20200605_202059.log.json)                                             |\n| R-50-FPN  |     + GRoIE     |   1x    |        |         |        [config](./grid-rcnn_r50_fpn_gn-head-groie_1x_coco.py)        |                                                                                                                                                                                                                                                                                                                                                                                                                                              |\n| R-50-FPN  |   Mask R-CNN    |   1x    |  38.2  |  34.7   |         [config](../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py)          |                                                                        [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205_050542.log.json)                                                                         |\n| R-50-FPN  |     + GRoIE     |   1x    |  39.0  |  36.0   |            [config](./mask-rcnn_r50_fpn_groie_1x_coco.py)            |                                                             [model](https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r50_fpn_groie_1x_coco/mask_rcnn_r50_fpn_groie_1x_coco_20200604_211715-50d90c74.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r50_fpn_groie_1x_coco/mask_rcnn_r50_fpn_groie_1x_coco_20200604_211715.log.json)                                                             |\n| R-50-FPN  |     GC-Net      |   1x    |  40.7  |  36.5   | [config](../gcnet/mask-rcnn_r50-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py)  |                  [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200202-50b90e5c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200202_085547.log.json)                   |\n| R-50-FPN  |     + GRoIE     |   1x    |  41.0  |  37.8   |  [config](./mask-rcnn_r50_fpn_syncbn-r4-gcb-c3-c5-groie_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco_20200604_211715-42eb79e1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco_20200604_211715-42eb79e1.pth) |\n| R-101-FPN |     GC-Net      |   1x    |  42.2  |  37.8   | [config](../gcnet/mask-rcnn_r101-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py) |                [model](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200206-8407a3f0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/gcnet/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_1x_coco_20200206_142508.log.json)                 |\n| R-101-FPN |     + GRoIE     |   1x    |  42.6  |  38.7   | [config](./mask-rcnn_r101_fpn_syncbn-r4-gcb_c3-c5-groie_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco_20200607_224507-8daae01c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco_20200607_224507.log.json) |\n\n## Citation\n\nIf you use this work or benchmark in your research, please cite this project.\n\n```latex\n@inproceedings{rossi2021novel,\n  title={A novel region of interest extraction layer for instance segmentation},\n  author={Rossi, Leonardo and Karimi, Akbar and Prati, Andrea},\n  booktitle={2020 25th International Conference on Pattern Recognition (ICPR)},\n  pages={2203--2209},\n  year={2021},\n  organization={IEEE}\n}\n```\n\n## Contact\n\nThe implementation of GRoIE is currently maintained by\n[Leonardo Rossi](https://github.com/hachreak/).\n"
  },
  {
    "path": "configs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            aggregation='sum',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2))))\n"
  },
  {
    "path": "configs/groie/grid-rcnn_r50_fpn_gn-head-groie_1x_coco.py",
    "content": "_base_ = '../grid_rcnn/grid-rcnn_r50_fpn_gn-head_1x_coco.py'\n# model settings\nmodel = dict(\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            aggregation='sum',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2)),\n        grid_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2))))\n"
  },
  {
    "path": "configs/groie/mask-rcnn_r101_fpn_syncbn-r4-gcb_c3-c5-groie_1x_coco.py",
    "content": "_base_ = '../gcnet/mask-rcnn_r101-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            aggregation='sum',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2)),\n        mask_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2))))\n"
  },
  {
    "path": "configs/groie/mask-rcnn_r50_fpn_groie_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            aggregation='sum',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2)),\n        mask_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2))))\n"
  },
  {
    "path": "configs/groie/mask-rcnn_r50_fpn_syncbn-r4-gcb-c3-c5-groie_1x_coco.py",
    "content": "_base_ = '../gcnet/mask-rcnn_r50-syncbn-gcb-r4-c3-c5_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            aggregation='sum',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2)),\n        mask_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=256,\n                out_channels=256,\n                kernel_size=5,\n                padding=2,\n                inplace=False,\n            ),\n            post_cfg=dict(\n                type='GeneralizedAttention',\n                in_channels=256,\n                spatial_range=-1,\n                num_heads=6,\n                attention_type='0100',\n                kv_stride=2))))\n"
  },
  {
    "path": "configs/groie/metafile.yml",
    "content": "Collections:\n  - Name: GRoIE\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Generic RoI Extractor\n        - FPN\n        - RPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/abs/2004.13665\n      Title: 'A novel Region of Interest Extraction Layer for Instance Segmentation'\n    README: configs/groie/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/roi_heads/roi_extractors/groie.py#L15\n      Version: v2.1.0\n\nModels:\n  - Name: faster-rcnn_r50_fpn_groie_1x_coco\n    In Collection: GRoIE\n    Config: configs/groie/faste-rcnn_r50_fpn_groie_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/groie/faster_rcnn_r50_fpn_groie_1x_coco/faster_rcnn_r50_fpn_groie_1x_coco_20200604_211715-66ee9516.pth\n\n  - Name: grid-rcnn_r50_fpn_gn-head-groie_1x_coco\n    In Collection: GRoIE\n    Config: configs/groie/grid-rcnn_r50_fpn_gn-head-groie_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/groie/grid_rcnn_r50_fpn_gn-head_groie_1x_coco/grid_rcnn_r50_fpn_gn-head_groie_1x_coco_20200605_202059-4b75d86f.pth\n\n  - Name: mask-rcnn_r50_fpn_groie_1x_coco\n    In Collection: GRoIE\n    Config: configs/groie/mask-rcnn_r50_fpn_groie_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r50_fpn_groie_1x_coco/mask_rcnn_r50_fpn_groie_1x_coco_20200604_211715-50d90c74.pth\n\n  - Name: mask-rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco\n    In Collection: GRoIE\n    Config: configs/groie/mask-rcnn_r50_fpn_syncbn-r4-gcb-c3-c5-groie_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP:   37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco/mask_rcnn_r50_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco_20200604_211715-42eb79e1.pth\n\n  - Name: mask-rcnn_r101_fpn_syncbn-r4-gcb_c3-c5-groie_1x_coco\n    In Collection: GRoIE\n    Config: configs/groie/mask-rcnn_r101_fpn_syncbn-r4-gcb_c3-c5-groie_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/groie/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco/mask_rcnn_r101_fpn_syncbn-backbone_r4_gcb_c3-c5_groie_1x_coco_20200607_224507-8daae01c.pth\n"
  },
  {
    "path": "configs/grounding_dino/README.md",
    "content": "# Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection\n\n[Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection](https://arxiv.org/abs/2303.05499)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIn this paper, we present an open-set object detector, called Grounding DINO, by marrying Transformer-based detector DINO with grounded pre-training, which can detect arbitrary objects with human inputs such as category names or referring expressions. The key solution of open-set object detection is introducing language to a closed-set detector for open-set concept generalization. To effectively fuse language and vision modalities, we conceptually divide a closed-set detector into three phases and propose a tight fusion solution, which includes a feature enhancer, a language-guided query selection, and a cross-modality decoder for cross-modality fusion. While previous works mainly evaluate open-set object detection on novel categories, we propose to also perform evaluations on referring expression comprehension for objects specified with attributes. Grounding DINO performs remarkably well on all three settings, including benchmarks on COCO, LVIS, ODinW, and RefCOCO/+/g. Grounding DINO achieves a 52.5 AP on the COCO detection zero-shot transfer benchmark, i.e., without any training data from COCO. It sets a new record on the ODinW zero-shot benchmark with a mean 26.1 AP.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/42299757/0ed51aeb-3d53-42d8-8563-f6d21364ac95\"/>\n</div>\n\n## Installation\n\n```shell\ncd $MMDETROOT\n\n# source installation\npip install -r requirements/multimodal.txt\n\n# or mim installation\nmim install mmdet[multimodal]\n```\n\n## NOTE\n\nGrounding DINO utilizes BERT as the language model, which requires access to https://huggingface.co/. If you encounter connection errors due to network access, you can download the required files on a computer with internet access and save them locally. Finally, modify the `lang_model_name` field in the config to the local path. Please refer to the following code:\n\n```python\nfrom transformers import BertConfig, BertModel\nfrom transformers import AutoTokenizer\n\nconfig = BertConfig.from_pretrained(\"bert-base-uncased\")\nmodel = BertModel.from_pretrained(\"bert-base-uncased\", add_pooling_layer=False, config=config)\ntokenizer = AutoTokenizer.from_pretrained(\"bert-base-uncased\")\n\nconfig.save_pretrained(\"your path/bert-base-uncased\")\nmodel.save_pretrained(\"your path/bert-base-uncased\")\ntokenizer.save_pretrained(\"your path/bert-base-uncased\")\n```\n\n## Inference\n\n```\ncd $MMDETROOT\n\nwget https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth\n\npython demo/image_demo.py \\\n\tdemo/demo.jpg \\\n\tconfigs/grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py \\\n\t--weights groundingdino_swint_ogc_mmdet-822d7e9d.pth \\\n\t--texts 'bench . car .'\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/42299757/3a3bd6f1-e2ed-43d4-aa22-0bb07ee6f20b\"/>\n</div>\n\n## COCO Results and Models\n\n|       Model        | Backbone |   Style   |  COCO mAP  | Official COCO mAP |                  Pre-Train Data                  |                             Config                             |                                                                                                                                                                                                                                         Download                                                                                                                                                                                                                                          |\n| :----------------: | :------: | :-------: | :--------: | :---------------: | :----------------------------------------------: | :------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  Grounding DINO-T  |  Swin-T  | Zero-shot |    48.5    |       48.4        |                 O365,GoldG,Cap4M                 | [config](grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py) |                                                                                                                                                                                    [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth)                                                                                                                                                                                     |\n|  Grounding DINO-T  |  Swin-T  | Finetune  | 58.1(+0.9) |       57.2        |                 O365,GoldG,Cap4M                 |   [config](grounding_dino_swin-t_finetune_16xb2_1x_coco.py)    | [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco/grounding_dino_swin-t_finetune_16xb2_1x_coco_20230921_152544-5f234b20.pth)                                                                                                \\| [log](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco/grounding_dino_swin-t_finetune_16xb2_1x_coco_20230921_152544.log.json) |\n|  Grounding DINO-B  |  Swin-B  | Zero-shot |    56.9    |       56.7        | COCO,O365,GoldG,Cap4M,OpenImage,ODinW-35,RefCOCO |     [config](grounding_dino_swin-b_pretrain_mixeddata.py)      |                                                                                                                                                                                  [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swinb_cogcoor_mmdet-55949c9c.pth)                                                                                                                                                                                   |\n|  Grounding DINO-B  |  Swin-B  | Finetune  |    59.7    |                   | COCO,O365,GoldG,Cap4M,OpenImage,ODinW-35,RefCOCO |   [config](grounding_dino_swin-b_finetune_16xb2_1x_coco.py)    |                                               [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-b_finetune_16xb2_1x_coco/grounding_dino_swin-b_finetune_16xb2_1x_coco_20230921_153201-f219e0c0.pth)   \\| [log](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-b_finetune_16xb2_1x_coco/grounding_dino_swin-b_finetune_16xb2_1x_coco_20230921_153201.log.json)                                                |\n| Grounding DINO-R50 |   R50    |  Scratch  | 48.9(+0.8) |       48.1        |                                                  |      [config](grounding_dino_r50_scratch_8xb2_1x_coco.py)      |                                                                                          [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_r50_scratch_8xb2_1x_coco/grounding_dino_r50_scratch_1x_coco-fe0002f2.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_r50_scratch_8xb2_1x_coco/20230922_114218.json)                                                                                           |\n\nNote:\n\n1. The weights corresponding to the zero-shot model are adopted from the official weights and converted using the [script](../../tools/model_converters/groundingdino_to_mmdet.py). We have not retrained the model for the time being.\n2. Finetune refers to fine-tuning on the COCO 2017 dataset. The R50 model is trained using 8 NVIDIA GeForce 3090 GPUs, while the remaining models are trained using 16 NVIDIA GeForce 3090 GPUs. The GPU memory usage is approximately 8.5GB.\n3. Our performance is higher than the official model due to two reasons: we modified the initialization strategy and introduced a log scaler.\n\n## LVIS Results\n\n|      Model       | MiniVal APr | MiniVal APc | MiniVal APf | MiniVal AP | Val1.0 APr | Val1.0 APc | Val1.0 APf | Val1.0 AP |                  Pre-Train Data                  |                               Config                                |                                                        Download                                                        |\n| :--------------: | :---------: | :---------: | :---------: | :--------: | :--------: | :--------: | :--------: | :-------: | :----------------------------------------------: | :-----------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------: |\n| Grounding DINO-T |    18.8     |    24.2     |    34.7     |    28.8    |    10.1    |    15.3    |    29.9    |   20.1    |                 O365,GoldG,Cap4M                 | [config](lvis/grounding_dino_swin-t_pretrain_zeroshot_mini-lvis.py) |   [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth)   |\n| Grounding DINO-B |    27.9     |    33.4     |    37.2     |    34.7    |    19.0    |    24.1    |    32.9    |   26.7    | COCO,O365,GoldG,Cap4M,OpenImage,ODinW-35,RefCOCO | [config](lvis/grounding_dino_swin-b_pretrain_zeroshot_mini-lvis.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swinb_cogcoor_mmdet-55949c9c.pth) |\n\nNote:\n\n1. The above are zero-shot evaluation results.\n2. The evaluation metric we used is LVIS FixAP. For specific details, please refer to [Evaluating Large-Vocabulary Object Detectors: The Devil is in the Details](https://arxiv.org/pdf/2102.01066.pdf).\n\n## ODinW (Object Detection in the Wild) Results\n\nLearning visual representations from natural language supervision has recently shown great promise in a number of pioneering works. In general, these language-augmented visual models demonstrate strong transferability to a variety of datasets and tasks. However, it remains challenging to evaluate the transferablity of these models due to the lack of easy-to-use evaluation toolkits and public benchmarks. To tackle this, we build ELEVATER 1 , the first benchmark and toolkit for evaluating (pre-trained) language-augmented visual models. ELEVATER is composed of three components. (i) Datasets. As downstream evaluation suites, it consists of 20 image classification datasets and 35 object detection datasets, each of which is augmented with external knowledge. (ii) Toolkit. An automatic hyper-parameter tuning toolkit is developed to facilitate model evaluation on downstream tasks. (iii) Metrics. A variety of evaluation metrics are used to measure sample-efficiency (zero-shot and few-shot) and parameter-efficiency (linear probing and full model fine-tuning). ELEVATER is platform for Computer Vision in the Wild (CVinW), and is publicly released at https://computer-vision-in-the-wild.github.io/ELEVATER/\n\n### Results and models of ODinW13\n\n| Method                | GLIP-T(A) | Official  | GLIP-T(B) | Official  | GLIP-T(C) | Official  | GroundingDINO-T | GroundingDINO-B |\n| --------------------- | --------- | --------- | --------- | --------- | --------- | --------- | --------------- | --------------- |\n| AerialMaritimeDrone   | 0.123     | 0.122     | 0.110     | 0.110     | 0.130     | 0.130     | 0.173           | 0.281           |\n| Aquarium              | 0.175     | 0.174     | 0.173     | 0.169     | 0.191     | 0.190     | 0.195           | 0.445           |\n| CottontailRabbits     | 0.686     | 0.686     | 0.688     | 0.688     | 0.744     | 0.744     | 0.799           | 0.808           |\n| EgoHands              | 0.013     | 0.013     | 0.003     | 0.004     | 0.314     | 0.315     | 0.608           | 0.764           |\n| NorthAmericaMushrooms | 0.502     | 0.502     | 0.367     | 0.367     | 0.297     | 0.296     | 0.507           | 0.675           |\n| Packages              | 0.589     | 0.589     | 0.083     | 0.083     | 0.699     | 0.699     | 0.687           | 0.670           |\n| PascalVOC             | 0.512     | 0.512     | 0.541     | 0.540     | 0.565     | 0.565     | 0.563           | 0.711           |\n| pistols               | 0.339     | 0.339     | 0.502     | 0.501     | 0.503     | 0.504     | 0.726           | 0.771           |\n| pothole               | 0.007     | 0.007     | 0.030     | 0.030     | 0.058     | 0.058     | 0.215           | 0.478           |\n| Raccoon               | 0.075     | 0.074     | 0.285     | 0.288     | 0.241     | 0.244     | 0.549           | 0.541           |\n| ShellfishOpenImages   | 0.253     | 0.253     | 0.337     | 0.338     | 0.300     | 0.302     | 0.393           | 0.650           |\n| thermalDogsAndPeople  | 0.372     | 0.372     | 0.475     | 0.475     | 0.510     | 0.510     | 0.657           | 0.633           |\n| VehiclesOpenImages    | 0.574     | 0.566     | 0.562     | 0.547     | 0.549     | 0.534     | 0.613           | 0.647           |\n| Average               | **0.325** | **0.324** | **0.320** | **0.318** | **0.392** | **0.392** | **0.514**       | **0.621**       |\n\n### Results and models of ODinW35\n\n| Method                      | GLIP-T(A) | Official  | GLIP-T(B) | Official  | GLIP-T(C) | Official  | GroundingDINO-T | GroundingDINO-B |\n| --------------------------- | --------- | --------- | --------- | --------- | --------- | --------- | --------------- | --------------- |\n| AerialMaritimeDrone_large   | 0.123     | 0.122     | 0.110     | 0.110     | 0.130     | 0.130     | 0.173           | 0.281           |\n| AerialMaritimeDrone_tiled   | 0.174     | 0.174     | 0.172     | 0.172     | 0.172     | 0.172     | 0.206           | 0.364           |\n| AmericanSignLanguageLetters | 0.001     | 0.001     | 0.003     | 0.003     | 0.009     | 0.009     | 0.002           | 0.096           |\n| Aquarium                    | 0.175     | 0.175     | 0.173     | 0.171     | 0.192     | 0.182     | 0.195           | 0.445           |\n| BCCD                        | 0.016     | 0.016     | 0.001     | 0.001     | 0.000     | 0.000     | 0.161           | 0.584           |\n| boggleBoards                | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000           | 0.134           |\n| brackishUnderwater          | 0.016     | 0..013    | 0.021     | 0.027     | 0.020     | 0.022     | 0.021           | 0.454           |\n| ChessPieces                 | 0.001     | 0.001     | 0.000     | 0.000     | 0.001     | 0.001     | 0.000           | 0.000           |\n| CottontailRabbits           | 0.710     | 0.709     | 0.683     | 0.683     | 0.752     | 0.752     | 0.806           | 0.797           |\n| dice                        | 0.005     | 0.005     | 0.004     | 0.004     | 0.004     | 0.004     | 0.004           | 0.082           |\n| DroneControl                | 0.016     | 0.017     | 0.006     | 0.008     | 0.005     | 0.007     | 0.042           | 0.638           |\n| EgoHands_generic            | 0.009     | 0.010     | 0.005     | 0.006     | 0.510     | 0.508     | 0.608           | 0.764           |\n| EgoHands_specific           | 0.001     | 0.001     | 0.004     | 0.006     | 0.003     | 0.004     | 0.002           | 0.687           |\n| HardHatWorkers              | 0.029     | 0.029     | 0.023     | 0.023     | 0.033     | 0.033     | 0.046           | 0.439           |\n| MaskWearing                 | 0.007     | 0.007     | 0.003     | 0.002     | 0.005     | 0.005     | 0.004           | 0.406           |\n| MountainDewCommercial       | 0.218     | 0.227     | 0.199     | 0.197     | 0.478     | 0.463     | 0.430           | 0.580           |\n| NorthAmericaMushrooms       | 0.502     | 0.502     | 0.450     | 0.450     | 0.497     | 0.497     | 0.471           | 0.501           |\n| openPoetryVision            | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000     | 0.000           | 0.051           |\n| OxfordPets_by_breed         | 0.001     | 0.002     | 0.002     | 0.004     | 0.001     | 0.002     | 0.003           | 0.799           |\n| OxfordPets_by_species       | 0.016     | 0.011     | 0.012     | 0.009     | 0.013     | 0.009     | 0.011           | 0.872           |\n| PKLot                       | 0.002     | 0.002     | 0.000     | 0.000     | 0.000     | 0.000     | 0.001           | 0.774           |\n| Packages                    | 0.569     | 0.569     | 0.279     | 0.279     | 0.712     | 0.712     | 0.695           | 0.728           |\n| PascalVOC                   | 0.512     | 0.512     | 0.541     | 0.540     | 0.565     | 0.565     | 0.563           | 0.711           |\n| pistols                     | 0.339     | 0.339     | 0.502     | 0.501     | 0.503     | 0.504     | 0.726           | 0.771           |\n| plantdoc                    | 0.002     | 0.002     | 0.007     | 0.007     | 0.009     | 0.009     | 0.005           | 0.376           |\n| pothole                     | 0.007     | 0.010     | 0.024     | 0.025     | 0.085     | 0.101     | 0.215           | 0.478           |\n| Raccoons                    | 0.075     | 0.074     | 0.285     | 0.288     | 0.241     | 0.244     | 0.549           | 0.541           |\n| selfdrivingCar              | 0.071     | 0.072     | 0.074     | 0.074     | 0.081     | 0.080     | 0.089           | 0.318           |\n| ShellfishOpenImages         | 0.253     | 0.253     | 0.337     | 0.338     | 0.300     | 0.302     | 0.393           | 0.650           |\n| ThermalCheetah              | 0.028     | 0.028     | 0.000     | 0.000     | 0.028     | 0.028     | 0.087           | 0.290           |\n| thermalDogsAndPeople        | 0.372     | 0.372     | 0.475     | 0.475     | 0.510     | 0.510     | 0.657           | 0.633           |\n| UnoCards                    | 0.000     | 0.000     | 0.000     | 0.001     | 0.002     | 0.003     | 0.006           | 0.754           |\n| VehiclesOpenImages          | 0.574     | 0.566     | 0.562     | 0.547     | 0.549     | 0.534     | 0.613           | 0.647           |\n| WildfireSmoke               | 0.000     | 0.000     | 0.000     | 0.000     | 0.017     | 0.017     | 0.134           | 0.410           |\n| websiteScreenshots          | 0.003     | 0.004     | 0.003     | 0.005     | 0.005     | 0.006     | 0.012           | 0.175           |\n| Average                     | **0.134** | **0.134** | **0.138** | **0.138** | **0.179** | **0.178** | **0.227**       | **0.492**       |\n\n## Flickr30k Results\n\n|      Model       |  Pre-Train Data  | Val R@1 | Val R@5 | Val R@10 | Tesst R@1 | Test R@5 | Test R@10 |                          Config                           |                                                                                                                                                                                                                                         Download                                                                                                                                                                                                                                          |\n| :--------------: | :--------------: | ------- | ------- | -------- | --------- | -------- | --------- | :-------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Grounding DINO-T | O365,GoldG,Cap4M | 87.8    | 96.6    | 98.0     | 88.1      | 96.9     | 98.2      | [config](grounding_dino_swin-t_finetune_16xb2_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco/grounding_dino_swin-t_finetune_16xb2_1x_coco_20230921_152544-5f234b20.pth)                                                                                                \\| [log](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco/grounding_dino_swin-t_finetune_16xb2_1x_coco_20230921_152544.log.json) |\n\nNote:\n\n1. `@1,5,10` refers to precision at the top 1, 5, and 10 positions in a predicted ranked list.\n2. The pretraining data used by Grounding DINO-T is `O365,GoldG,Cap4M`, and the corresponding evaluation configuration is (grounding_dino_swin-t_pretrain_zeroshot_refcoco)\\[refcoco/grounding_dino_swin-t_pretrain_zeroshot_refcoco.py\\].\n\nTest Command\n\n```shell\ncd mmdetection\nbash tools/dist_test.sh configs/grounding_dino/flickr30k/grounding_dino_swin-t-pretrain_zeroshot_flickr30k.py checkpoints/groundingdino_swint_ogc_mmdet-822d7e9d.pth 8\n```\n\n## Referring Expression Comprehension Results\n\n| Method                                  | Grounding DINO-T <br/> (O365,GoldG,Cap4M) | Grounding DINO-B <br/> (COCO,O365,GoldG,Cap4M,OpenImage,ODinW-35,RefCOCO) |\n| --------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------- |\n| RefCOCO val @1,5,10                     | 50.77/89.45/94.86                         | 84.61/97.88/99.10                                                         |\n| RefCOCO testA @1,5,10                   | 57.45/91.29/95.62                         | 88.65/98.89/99.63                                                         |\n| RefCOCO testB @1,5,10                   | 44.97/86.54/92.88                         | 80.51/96.64/98.51                                                         |\n| RefCOCO+ val @1,5,10                    | 51.64/86.35/92.57                         | 73.67/96.60/98.65                                                         |\n| RefCOCO+ testA @1,5,10                  | 57.25/86.74/92.65                         | 82.19/97.92/99.09                                                         |\n| RefCOCO+ testB @1,5,10                  | 46.35/84.05/90.67                         | 64.10/94.25/97.46                                                         |\n| RefCOCOg val @1,5,10                    | 60.42/92.10/96.18                         | 78.33/97.28/98.57                                                         |\n| RefCOCOg test @1,5,10                   | 59.74/92.08/96.28                         | 78.11/97.06/98.65                                                         |\n| gRefCOCO val Pr@(F1=1, IoU≥0.5),N-acc   | 41.32/91.82                               | 46.18/81.44                                                               |\n| gRefCOCO testA Pr@(F1=1, IoU≥0.5),N-acc | 27.23/90.24                               | 38.60/76.06                                                               |\n| gRefCOCO testB Pr@(F1=1, IoU≥0.5),N-acc | 29.70/93.49                               | 35.87/80.58                                                               |\n\nNote:\n\n1. `@1,5,10` refers to precision at the top 1, 5, and 10 positions in a predicted ranked list.\n2. `Pr@(F1=1, IoU≥0.5),N-acc` from the paper [GREC: Generalized Referring Expression Comprehension](https://arxiv.org/pdf/2308.16182.pdf)\n3. The pretraining data used by Grounding DINO-T is `O365,GoldG,Cap4M`, and the corresponding evaluation configuration is (grounding_dino_swin-t_pretrain_zeroshot_refcoco)\\[refcoco/grounding_dino_swin-t_pretrain_zeroshot_refcoco.py\\].\n4. The pretraining data used by Grounding DINO-B is `COCO,O365,GoldG,Cap4M,OpenImage,ODinW-35,RefCOCO`, and the corresponding evaluation configuration is (grounding_dino_swin-t_pretrain_zeroshot_refcoco)\\[refcoco/grounding_dino_swin-b_pretrain_zeroshot_refcoco.py\\].\n\nTest Command\n\n```shell\ncd mmdetection\n./tools/dist_test.sh configs/grounding_dino/refcoco/grounding_dino_swin-t_pretrain_zeroshot_refexp.py https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth 8\n./tools/dist_test.sh configs/grounding_dino/refcoco/grounding_dino_swin-b_pretrain_zeroshot_refexp.py https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swinb_cogcoor_mmdet-55949c9c.pth 8\n```\n\n## Description Detection Dataset\n\n```shell\npip install ddd-dataset\n```\n\n| Method                           | mode     | Grounding DINO-T <br/> (O365,GoldG,Cap4M) | Grounding DINO-B <br/> (COCO,O365,GoldG,Cap4M,OpenImage,ODinW-35,RefCOCO) |\n| -------------------------------- | -------- | ----------------------------------------- | ------------------------------------------------------------------------- |\n| FULL/short/middle/long/very long | concat   | 17.2/18.0/18.7/14.8/16.3                  | 20.2/20.4/21.1/18.8/19.8                                                  |\n| FULL/short/middle/long/very long | parallel | 22.3/28.2/24.8/19.1/13.9                  | 25.0/26.4/27.2/23.5/19.7                                                  |\n| PRES/short/middle/long/very long | concat   | 17.8/18.3/19.2/15.2/17.3                  | 20.7/21.7/21.4/19.1/20.3                                                  |\n| PRES/short/middle/long/very long | parallel | 21.0/27.0/22.8/17.5/12.5                  | 23.7/25.8/25.1/21.9/19.3                                                  |\n| ABS/short/middle/long/very long  | concat   | 15.4/17.1/16.4/13.6/14.9                  | 18.6/16.1/19.7/18.1/19.1                                                  |\n| ABS/short/middle/long/very long  | parallel | 26.0/32.0/33.0/23.6/15.5                  | 28.8/28.1/35.8/28.2/20.2                                                  |\n\nNote:\n\n1. Considering that the evaluation time for Inter-scenario is very long and the performance is low, it is temporarily not supported. The mentioned metrics are for Intra-scenario.\n2. `concat` is the default inference mode for Grounding DINO, where it concatenates multiple sub-sentences with \".\" to form a single sentence for inference. On the other hand, \"parallel\" performs inference on each sub-sentence in a for-loop.\n\n## Custom Dataset\n\nTo facilitate fine-tuning on custom datasets, we use a simple cat dataset as an example, as shown in the following steps.\n\n### 1. Dataset Preparation\n\n```shell\ncd mmdetection\nwget https://download.openmmlab.com/mmyolo/data/cat_dataset.zip\nunzip cat_dataset.zip -d data/cat/\n```\n\ncat dataset is a single-category dataset with 144 images, which has been converted to coco format.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/25873202/205423220-c4b8f2fd-22ba-4937-8e47-1b3f6a8facd8.png\" alt=\"cat dataset\"/>\n</div>\n\n### 2. Config Preparation\n\nDue to the simplicity and small number of cat datasets, we use 8 cards to train 20 epochs, scale the learning rate accordingly, and do not train the language model, only the visual model.\n\nThe Details of the configuration can be found in [grounding_dino_swin-t_finetune_8xb2_20e_cat](grounding_dino_swin-t_finetune_8xb2_20e_cat.py)\n\n### 3. Visualization and Evaluation\n\nDue to the Grounding DINO is an open detection model, so it can be detected and evaluated even if it is not trained on the cat dataset.\n\nThe single image visualization is as follows:\n\n```shell\ncd mmdetection\npython demo/image_demo.py data/cat/images/IMG_20211205_120756.jpg configs/grounding_dino/grounding_dino_swin-t_finetune_8xb2_20e_cat.py --weights https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth --texts cat.\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/89173261-16f1-4fd9-ac63-8dc2dcda6616\" alt=\"cat dataset\"/>\n</div>\n\nThe test dataset evaluation on single card is as follows:\n\n```shell\npython tools/test.py configs/grounding_dino/grounding_dino_swin-t_finetune_8xb2_20e_cat.py https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth\n```\n\n```text\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.867\n Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 1.000\n Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.931\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.867\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.903\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.907\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.907\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.907\n```\n\n### 4. Model Training and Visualization\n\n```shell\n./tools/dist_train.sh configs/grounding_dino/grounding_dino_swin-t_finetune_8xb2_20e_cat.py 8 --work-dir cat_work_dir\n```\n\nThe model will be saved based on the best performance on the test set. The performance of the best model (at epoch 16) is as follows:\n\n```text\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.905\n Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 1.000\n Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.923\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.905\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.927\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.937\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.937\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.937\n```\n\nWe can find that after fine-tuning training, the training of the cat dataset is increased from 86.7 to 90.5.\n\nIf we do single image inference visualization again, the result is as follows:\n\n```shell\ncd mmdetection\npython demo/image_demo.py data/cat/images/IMG_20211205_120756.jpg configs/grounding_dino/grounding_dino_swin-t_finetune_8xb2_20e_cat.py --weights cat_work_dir/best_coco_bbox_mAP_epoch_16.pth --texts cat.\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/5a027b00-8adb-4283-a47b-2f7a0a2c96d4\" alt=\"cat dataset\"/>\n</div>\n"
  },
  {
    "path": "configs/grounding_dino/dod/grounding_dino_swin-b_pretrain_zeroshot_concat_dod.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_zeroshot_concat_dod.py'\n\nmodel = dict(\n    type='GroundingDINO',\n    backbone=dict(\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        drop_path_rate=0.3,\n        patch_norm=True),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n"
  },
  {
    "path": "configs/grounding_dino/dod/grounding_dino_swin-b_pretrain_zeroshot_parallel_dod.py",
    "content": "_base_ = 'grounding_dino_swin-b_pretrain_zeroshot_concat_dod.py'\n\nmodel = dict(test_cfg=dict(chunked_size=1))\n"
  },
  {
    "path": "configs/grounding_dino/dod/grounding_dino_swin-t_pretrain_zeroshot_concat_dod.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py'\n\ndata_root = 'data/d3/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities', 'sent_ids'))\n]\n\n# -------------------------------------------------#\nval_dataset_full = dict(\n    type='DODDataset',\n    data_root=data_root,\n    ann_file='d3_json/d3_full_annotations.json',\n    data_prefix=dict(img='d3_images/', anno='d3_pkl'),\n    pipeline=test_pipeline,\n    test_mode=True,\n    backend_args=None,\n    return_classes=True)\n\nval_evaluator_full = dict(\n    type='DODCocoMetric',\n    ann_file=data_root + 'd3_json/d3_full_annotations.json')\n\n# -------------------------------------------------#\nval_dataset_pres = dict(\n    type='DODDataset',\n    data_root=data_root,\n    ann_file='d3_json/d3_pres_annotations.json',\n    data_prefix=dict(img='d3_images/', anno='d3_pkl'),\n    pipeline=test_pipeline,\n    test_mode=True,\n    backend_args=None,\n    return_classes=True)\nval_evaluator_pres = dict(\n    type='DODCocoMetric',\n    ann_file=data_root + 'd3_json/d3_pres_annotations.json')\n\n# -------------------------------------------------#\nval_dataset_abs = dict(\n    type='DODDataset',\n    data_root=data_root,\n    ann_file='d3_json/d3_abs_annotations.json',\n    data_prefix=dict(img='d3_images/', anno='d3_pkl'),\n    pipeline=test_pipeline,\n    test_mode=True,\n    backend_args=None,\n    return_classes=True)\nval_evaluator_abs = dict(\n    type='DODCocoMetric',\n    ann_file=data_root + 'd3_json/d3_abs_annotations.json')\n\n# -------------------------------------------------#\ndatasets = [val_dataset_full, val_dataset_pres, val_dataset_abs]\ndataset_prefixes = ['FULL', 'PRES', 'ABS']\nmetrics = [val_evaluator_full, val_evaluator_pres, val_evaluator_abs]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/dod/grounding_dino_swin-t_pretrain_zeroshot_parallel_dod.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_zeroshot_concat_dod.py'\n\nmodel = dict(test_cfg=dict(chunked_size=1))\n"
  },
  {
    "path": "configs/grounding_dino/flickr30k/grounding_dino_swin-t-pretrain_zeroshot_flickr30k.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py'\n\ndataset_type = 'Flickr30kDataset'\ndata_root = 'data/flickr30k_entities/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive', 'phrase_ids', 'phrases'))\n]\n\ndataset_Flickr30k_val = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='final_flickr_separateGT_val.json',\n    data_prefix=dict(img='flickr30k_images/'),\n    pipeline=test_pipeline,\n)\n\ndataset_Flickr30k_test = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='final_flickr_separateGT_test.json',\n    data_prefix=dict(img='flickr30k_images/'),\n    pipeline=test_pipeline,\n)\n\nval_evaluator_Flickr30k = dict(type='Flickr30kMetric')\n\ntest_evaluator_Flickr30k = dict(type='Flickr30kMetric')\n\n# ----------Config---------- #\ndataset_prefixes = ['Flickr30kVal', 'Flickr30kTest']\ndatasets = [dataset_Flickr30k_val, dataset_Flickr30k_test]\nmetrics = [val_evaluator_Flickr30k, test_evaluator_Flickr30k]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/grounding_dino_r50_scratch_8xb2_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nlang_model_name = 'bert-base-uncased'\n\nmodel = dict(\n    type='GroundingDINO',\n    num_queries=900,\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=False,\n    ),\n    language_model=dict(\n        type='BertModel',\n        name=lang_model_name,\n        pad_to_max=False,\n        use_sub_sentence_represent=True,\n        special_tokens_list=['[CLS]', '[SEP]', '.', '?'],\n        add_pooling_layer=False,\n    ),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        bias=True,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        num_cp=6,\n        # visual layer config\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        # text layer config\n        text_layer_cfg=dict(\n            self_attn_cfg=dict(num_heads=4, embed_dims=256, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.0)),\n        # fusion layer config\n        fusion_layer_cfg=dict(\n            v_dim=256,\n            l_dim=256,\n            embed_dim=1024,\n            num_heads=4,\n            init_values=1e-4),\n    ),\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            # query self attention layer\n            self_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to text\n            cross_attn_text_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to image\n            cross_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128, normalize=True, offset=0.0, temperature=20),\n    bbox_head=dict(\n        type='GroundingDINOHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        contrastive_cfg=dict(max_text_len=256, log_scale='auto', bias=True),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(  # TODO: Move to model.train_cfg ?\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None,\n                       num_dn_queries=100)),  # TODO: half num_dn_queries\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='BinaryFocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='FixScaleResize', scale=(800, 1333), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=train_pipeline,\n        return_classes=True))\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\ntest_dataloader = val_dataloader\n\n# We did not adopt the official 24e optimizer strategy\n# because the results indicate that the current strategy is superior.\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,  # 0.0002 for DeformDETR\n        weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={\n        'absolute_pos_embed': dict(decay_mult=0.),\n        'backbone': dict(lr_mult=0.1)\n    }))\n# learning policy\nmax_epochs = 12\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/grounding_dino/grounding_dino_swin-b_finetune_16xb2_1x_coco.py",
    "content": "_base_ = [\n    './grounding_dino_swin-t_finetune_16xb2_1x_coco.py',\n]\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swinb_cogcoor_mmdet-55949c9c.pth'  # noqa\nmodel = dict(\n    type='GroundingDINO',\n    backbone=dict(\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        drop_path_rate=0.3,\n        patch_norm=True),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n"
  },
  {
    "path": "configs/grounding_dino/grounding_dino_swin-b_pretrain_mixeddata.py",
    "content": "_base_ = [\n    './grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py',\n]\n\nmodel = dict(\n    type='GroundingDINO',\n    backbone=dict(\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        drop_path_rate=0.3,\n        patch_norm=True),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n"
  },
  {
    "path": "configs/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth'  # noqa\nlang_model_name = 'bert-base-uncased'\n\nmodel = dict(\n    type='GroundingDINO',\n    num_queries=900,\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=False,\n    ),\n    language_model=dict(\n        type='BertModel',\n        name=lang_model_name,\n        pad_to_max=False,\n        use_sub_sentence_represent=True,\n        special_tokens_list=['[CLS]', '[SEP]', '.', '?'],\n        add_pooling_layer=False,\n    ),\n    backbone=dict(\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=True,\n        convert_weights=False),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[192, 384, 768],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        bias=True,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        num_cp=6,\n        # visual layer config\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        # text layer config\n        text_layer_cfg=dict(\n            self_attn_cfg=dict(num_heads=4, embed_dims=256, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.0)),\n        # fusion layer config\n        fusion_layer_cfg=dict(\n            v_dim=256,\n            l_dim=256,\n            embed_dim=1024,\n            num_heads=4,\n            init_values=1e-4),\n    ),\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            # query self attention layer\n            self_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to text\n            cross_attn_text_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to image\n            cross_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128, normalize=True, offset=0.0, temperature=20),\n    bbox_head=dict(\n        type='GroundingDINOHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        contrastive_cfg=dict(max_text_len=256, log_scale=0.0, bias=False),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(  # TODO: Move to model.train_cfg ?\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None,\n                       num_dn_queries=100)),  # TODO: half num_dn_queries\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='BinaryFocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='FixScaleResize', scale=(800, 1333), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=train_pipeline,\n        return_classes=True))\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\ntest_dataloader = val_dataloader\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={\n        'absolute_pos_embed': dict(decay_mult=0.),\n        'backbone': dict(lr_mult=0.1)\n    }))\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (16 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=32)\n"
  },
  {
    "path": "configs/grounding_dino/grounding_dino_swin-t_finetune_8xb2_20e_cat.py",
    "content": "_base_ = 'grounding_dino_swin-t_finetune_16xb2_1x_coco.py'\n\ndata_root = 'data/cat/'\nclass_name = ('cat', )\nnum_classes = len(class_name)\nmetainfo = dict(classes=class_name, palette=[(220, 20, 60)])\n\nmodel = dict(bbox_head=dict(num_classes=num_classes))\n\ntrain_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        metainfo=metainfo,\n        ann_file='annotations/trainval.json',\n        data_prefix=dict(img='images/')))\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=metainfo,\n        data_root=data_root,\n        ann_file='annotations/test.json',\n        data_prefix=dict(img='images/')))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(ann_file=data_root + 'annotations/test.json')\ntest_evaluator = val_evaluator\n\nmax_epoch = 20\n\ndefault_hooks = dict(\n    checkpoint=dict(interval=1, max_keep_ckpts=1, save_best='auto'),\n    logger=dict(type='LoggerHook', interval=5))\ntrain_cfg = dict(max_epochs=max_epoch, val_interval=1)\n\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=30),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epoch,\n        by_epoch=True,\n        milestones=[15],\n        gamma=0.1)\n]\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.00005),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            'language_model': dict(lr_mult=0),\n        }))\n\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nlang_model_name = 'bert-base-uncased'\n\nmodel = dict(\n    type='GroundingDINO',\n    num_queries=900,\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=False,\n    ),\n    language_model=dict(\n        type='BertModel',\n        name=lang_model_name,\n        pad_to_max=False,\n        use_sub_sentence_represent=True,\n        special_tokens_list=['[CLS]', '[SEP]', '.', '?'],\n        add_pooling_layer=True,\n    ),\n    backbone=dict(\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False,\n        convert_weights=False),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[192, 384, 768],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        bias=True,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        # visual layer config\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        # text layer config\n        text_layer_cfg=dict(\n            self_attn_cfg=dict(num_heads=4, embed_dims=256, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.0)),\n        # fusion layer config\n        fusion_layer_cfg=dict(\n            v_dim=256,\n            l_dim=256,\n            embed_dim=1024,\n            num_heads=4,\n            init_values=1e-4),\n    ),\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            # query self attention layer\n            self_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to text\n            cross_attn_text_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to image\n            cross_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128, normalize=True, offset=0.0, temperature=20),\n    bbox_head=dict(\n        type='GroundingDINOHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        contrastive_cfg=dict(max_text_len=256),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0)),\n    dn_cfg=dict(  # TODO: Move to model.train_cfg ?\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None,\n                       num_dn_queries=100)),  # TODO: half num_dn_queries\n    # training and testing settings\n    train_cfg=None,\n    test_cfg=dict(max_per_img=300))\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive'))\n]\n\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/grounding_dino/lvis/grounding_dino_swin-b_pretrain_zeroshot_lvis.py",
    "content": "_base_ = './grounding_dino_swin-t_pretrain_zeroshot_lvis.py'\n\nmodel = dict(\n    type='GroundingDINO',\n    backbone=dict(\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        drop_path_rate=0.3,\n        patch_norm=True),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n"
  },
  {
    "path": "configs/grounding_dino/lvis/grounding_dino_swin-b_pretrain_zeroshot_mini-lvis.py",
    "content": "_base_ = './grounding_dino_swin-t_pretrain_zeroshot_mini-lvis.py'\n\nmodel = dict(\n    type='GroundingDINO',\n    backbone=dict(\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        drop_path_rate=0.3,\n        patch_norm=True),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n"
  },
  {
    "path": "configs/grounding_dino/lvis/grounding_dino_swin-t_pretrain_zeroshot_lvis.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/coco/'\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type=dataset_type,\n        ann_file='annotations/lvis_od_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\n# numpy < 1.24.0\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root + 'annotations/lvis_od_val.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/lvis/grounding_dino_swin-t_pretrain_zeroshot_mini-lvis.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/coco/'\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type=dataset_type,\n        ann_file='annotations/lvis_v1_minival_inserted_image_name.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\n# numpy < 1.24.0\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root +\n    'annotations/lvis_v1_minival_inserted_image_name.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/metafile.yml",
    "content": "Collections:\n  - Name: Grounding DINO\n    Metadata:\n      Training Data: Objects365, GoldG, CC3M and COCO\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 3090 GPUs\n      Architecture:\n        - Swin Transformer\n        - BERT\n    Paper:\n      URL: https://arxiv.org/abs/2303.05499\n      Title: 'Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection\n'\n    README: configs/grounding_dino/README.md\n    Code:\n      URL:\n      Version: v3.0.0\n\nModels:\n  - Name: grounding_dino_swin-t_pretrain_obj365_goldg_cap4m\n    In Collection: Grounding DINO\n    Config: configs/grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 48.5\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth\n  - Name: grounding_dino_swin-b_pretrain_mixeddata\n    In Collection: Grounding DINO\n    Config: configs/grounding_dino/grounding_dino_swin-b_pretrain_mixeddata.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 56.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swinb_cogcoor_mmdet-55949c9c.pth\n  - Name: grounding_dino_swin-t_finetune_16xb2_1x_coco\n    In Collection: Grounding DINO\n    Config: configs/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 58.1\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-t_finetune_16xb2_1x_coco/grounding_dino_swin-t_finetune_16xb2_1x_coco_20230921_152544-5f234b20.pth\n  - Name: grounding_dino_swin-b_finetune_16xb2_1x_coco\n    In Collection: Grounding DINO\n    Config: configs/grounding_dino/grounding_dino_swin-b_finetune_16xb2_1x_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 59.7\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_swin-b_finetune_16xb2_1x_coco/grounding_dino_swin-b_finetune_16xb2_1x_coco_20230921_153201-f219e0c0.pth\n  - Name: grounding_dino_r50_scratch_8xb2_1x_coco\n    In Collection: Grounding DINO\n    Config: configs/grounding_dino/grounding_dino_r50_scratch_8xb2_1x_coco.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 48.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/grounding_dino_r50_scratch_8xb2_1x_coco/grounding_dino_r50_scratch_1x_coco-fe0002f2.pth\n"
  },
  {
    "path": "configs/grounding_dino/odinw/grounding_dino_swin-b_pretrain_odinw13.py",
    "content": "_base_ = '../grounding_dino_swin-b_pretrain_mixeddata.py'\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    test_mode=True,\n    pipeline=base_test_pipeline,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'penguin': {\n#         'suffix': ', which is black and white'\n#     },\n#     'puffin': {\n#         'suffix': ' with orange beaks'\n#     },\n#     'stingray': {\n#         'suffix': ' which is flat and round'\n#     },\n# }\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 CottontailRabbits---------------------#\nclass_name = ('Cottontail-Rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\n\ncaption_prompt = None\n# caption_prompt = {'Cottontail-Rabbit': {'name': 'rabbit'}}\n\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 EgoHands---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\n\ncaption_prompt = None\n# caption_prompt = {'hand': {'suffix': ' of a person'}}\n\ndataset_EgoHands = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 NorthAmericaMushrooms---------------------#\nclass_name = ('CoW', 'chanterelle')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\n\ncaption_prompt = None\n# caption_prompt = {\n#     'CoW': {\n#         'name': 'flat mushroom'\n#     },\n#     'chanterelle': {\n#         'name': 'yellow mushroom'\n#     }\n# }\n\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'package': {\n#         'prefix': 'there is a ',\n#         'suffix': ' on the porch'\n#     }\n# }\n\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'pothole': {\n#         'prefix': 'there are some ',\n#         'name': 'holes',\n#         'suffix': ' on the road'\n#     }\n# }\n\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\ndataset_prefixes = [\n    'AerialMaritimeDrone', 'Aquarium', 'CottontailRabbits', 'EgoHands',\n    'NorthAmericaMushrooms', 'Packages', 'PascalVOC', 'pistols', 'pothole',\n    'Raccoon', 'ShellfishOpenImages', 'thermalDogsAndPeople',\n    'VehiclesOpenImages'\n]\ndatasets = [\n    dataset_AerialMaritimeDrone, dataset_Aquarium, dataset_CottontailRabbits,\n    dataset_EgoHands, dataset_NorthAmericaMushrooms, dataset_Packages,\n    dataset_PascalVOC, dataset_pistols, dataset_pothole, dataset_Raccoon,\n    dataset_ShellfishOpenImages, dataset_thermalDogsAndPeople,\n    dataset_VehiclesOpenImages\n]\nmetrics = [\n    val_evaluator_AerialMaritimeDrone, val_evaluator_Aquarium,\n    val_evaluator_CottontailRabbits, val_evaluator_EgoHands,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_Packages,\n    val_evaluator_PascalVOC, val_evaluator_pistols, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_ShellfishOpenImages,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_VehiclesOpenImages\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/odinw/grounding_dino_swin-b_pretrain_odinw35.py",
    "content": "_base_ = '../grounding_dino_swin-b_pretrain_mixeddata.py'\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone_large---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone_large = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_large = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 AerialMaritimeDrone_tiled---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/tiled/'\ndataset_AerialMaritimeDrone_tiled = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_tiled = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 AmericanSignLanguageLetters---------------------#\nclass_name = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AmericanSignLanguageLetters/American Sign Language Letters.v1-v1.coco/'  # noqa\ndataset_AmericanSignLanguageLetters = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AmericanSignLanguageLetters = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 BCCD---------------------#\nclass_name = ('Platelets', 'RBC', 'WBC')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'BCCD/BCCD.v3-raw.coco/'\ndataset_BCCD = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_BCCD = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 boggleBoards---------------------#\nclass_name = ('Q', 'a', 'an', 'b', 'c', 'd', 'e', 'er', 'f', 'g', 'h', 'he',\n              'i', 'in', 'j', 'k', 'l', 'm', 'n', 'o', 'o ', 'p', 'q', 'qu',\n              'r', 's', 't', 't\\\\', 'th', 'u', 'v', 'w', 'wild', 'x', 'y', 'z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'boggleBoards/416x416AutoOrient/export/'\ndataset_boggleBoards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_boggleBoards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 brackishUnderwater---------------------#\nclass_name = ('crab', 'fish', 'jellyfish', 'shrimp', 'small_fish', 'starfish')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'brackishUnderwater/960x540/'\ndataset_brackishUnderwater = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_brackishUnderwater = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 ChessPieces---------------------#\nclass_name = ('  ', 'black bishop', 'black king', 'black knight', 'black pawn',\n              'black queen', 'black rook', 'white bishop', 'white king',\n              'white knight', 'white pawn', 'white queen', 'white rook')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ChessPieces/Chess Pieces.v23-raw.coco/'\ndataset_ChessPieces = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ChessPieces = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 CottontailRabbits---------------------#\nclass_name = ('rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 dice---------------------#\nclass_name = ('1', '2', '3', '4', '5', '6')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'dice/mediumColor/export/'\ndataset_dice = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_dice = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 DroneControl---------------------#\nclass_name = ('follow', 'follow_hand', 'land', 'land_hand', 'null', 'object',\n              'takeoff', 'takeoff-hand')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'DroneControl/Drone Control.v3-raw.coco/'\ndataset_DroneControl = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_DroneControl = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 EgoHands_generic---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\ncaption_prompt = {'hand': {'suffix': ' of a person'}}\ndataset_EgoHands_generic = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    # NOTE w. prompt 0.548; wo. prompt 0.764\n    # caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_generic = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 EgoHands_specific---------------------#\nclass_name = ('myleft', 'myright', 'yourleft', 'yourright')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/specific/'\ndataset_EgoHands_specific = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_specific = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------14 HardHatWorkers---------------------#\nclass_name = ('head', 'helmet', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'HardHatWorkers/raw/'\ndataset_HardHatWorkers = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_HardHatWorkers = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------15 MaskWearing---------------------#\nclass_name = ('mask', 'no-mask')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MaskWearing/raw/'\ndataset_MaskWearing = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MaskWearing = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------16 MountainDewCommercial---------------------#\nclass_name = ('bottle', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MountainDewCommercial/'\ndataset_MountainDewCommercial = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MountainDewCommercial = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------17 NorthAmericaMushrooms---------------------#\nclass_name = ('flat mushroom', 'yellow mushroom')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------18 openPoetryVision---------------------#\nclass_name = ('American Typewriter', 'Andale Mono', 'Apple Chancery', 'Arial',\n              'Avenir', 'Baskerville', 'Big Caslon', 'Bradley Hand',\n              'Brush Script MT', 'Chalkboard', 'Comic Sans MS', 'Copperplate',\n              'Courier', 'Didot', 'Futura', 'Geneva', 'Georgia', 'Gill Sans',\n              'Helvetica', 'Herculanum', 'Impact', 'Kefa', 'Lucida Grande',\n              'Luminari', 'Marker Felt', 'Menlo', 'Monaco', 'Noteworthy',\n              'Optima', 'PT Sans', 'PT Serif', 'Palatino', 'Papyrus',\n              'Phosphate', 'Rockwell', 'SF Pro', 'SignPainter', 'Skia',\n              'Snell Roundhand', 'Tahoma', 'Times New Roman', 'Trebuchet MS',\n              'Verdana')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'openPoetryVision/512x512/'\ndataset_openPoetryVision = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_openPoetryVision = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------19 OxfordPets_by_breed---------------------#\nclass_name = ('cat-Abyssinian', 'cat-Bengal', 'cat-Birman', 'cat-Bombay',\n              'cat-British_Shorthair', 'cat-Egyptian_Mau', 'cat-Maine_Coon',\n              'cat-Persian', 'cat-Ragdoll', 'cat-Russian_Blue', 'cat-Siamese',\n              'cat-Sphynx', 'dog-american_bulldog',\n              'dog-american_pit_bull_terrier', 'dog-basset_hound',\n              'dog-beagle', 'dog-boxer', 'dog-chihuahua',\n              'dog-english_cocker_spaniel', 'dog-english_setter',\n              'dog-german_shorthaired', 'dog-great_pyrenees', 'dog-havanese',\n              'dog-japanese_chin', 'dog-keeshond', 'dog-leonberger',\n              'dog-miniature_pinscher', 'dog-newfoundland', 'dog-pomeranian',\n              'dog-pug', 'dog-saint_bernard', 'dog-samoyed',\n              'dog-scottish_terrier', 'dog-shiba_inu',\n              'dog-staffordshire_bull_terrier', 'dog-wheaten_terrier',\n              'dog-yorkshire_terrier')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-breed/'  # noqa\ndataset_OxfordPets_by_breed = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_breed = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------20 OxfordPets_by_species---------------------#\nclass_name = ('cat', 'dog')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-species/'  # noqa\ndataset_OxfordPets_by_species = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_species = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------21 PKLot---------------------#\nclass_name = ('space-empty', 'space-occupied')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PKLot/640/'  # noqa\ndataset_PKLot = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PKLot = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------22 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\ncaption_prompt = {\n    'package': {\n        'prefix': 'there is a ',\n        'suffix': ' on the porch'\n    }\n}\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,  # NOTE w. prompt 0.728; wo. prompt 0.670\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------23 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------24 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------25 plantdoc---------------------#\nclass_name = ('Apple Scab Leaf', 'Apple leaf', 'Apple rust leaf',\n              'Bell_pepper leaf', 'Bell_pepper leaf spot', 'Blueberry leaf',\n              'Cherry leaf', 'Corn Gray leaf spot', 'Corn leaf blight',\n              'Corn rust leaf', 'Peach leaf', 'Potato leaf',\n              'Potato leaf early blight', 'Potato leaf late blight',\n              'Raspberry leaf', 'Soyabean leaf', 'Soybean leaf',\n              'Squash Powdery mildew leaf', 'Strawberry leaf',\n              'Tomato Early blight leaf', 'Tomato Septoria leaf spot',\n              'Tomato leaf', 'Tomato leaf bacterial spot',\n              'Tomato leaf late blight', 'Tomato leaf mosaic virus',\n              'Tomato leaf yellow virus', 'Tomato mold leaf',\n              'Tomato two spotted spider mites leaf', 'grape leaf',\n              'grape leaf black rot')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'plantdoc/416x416/'\ndataset_plantdoc = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_plantdoc = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------26 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\ncaption_prompt = {\n    'pothole': {\n        'name': 'holes',\n        'prefix': 'there are some ',\n        'suffix': ' on the road'\n    }\n}\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    # NOTE w. prompt 0.221; wo. prompt 0.478\n    # caption_prompt=caption_prompt,\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------27 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------28 selfdrivingCar---------------------#\nclass_name = ('biker', 'car', 'pedestrian', 'trafficLight',\n              'trafficLight-Green', 'trafficLight-GreenLeft',\n              'trafficLight-Red', 'trafficLight-RedLeft',\n              'trafficLight-Yellow', 'trafficLight-YellowLeft', 'truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'selfdrivingCar/fixedLarge/export/'\ndataset_selfdrivingCar = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_selfdrivingCar = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------29 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------30 ThermalCheetah---------------------#\nclass_name = ('cheetah', 'human')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ThermalCheetah/'\ndataset_ThermalCheetah = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ThermalCheetah = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------31 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------32 UnoCards---------------------#\nclass_name = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11',\n              '12', '13', '14')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'UnoCards/raw/'\ndataset_UnoCards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_UnoCards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------33 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------34 WildfireSmoke---------------------#\nclass_name = ('smoke', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'WildfireSmoke/'\ndataset_WildfireSmoke = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_WildfireSmoke = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------35 websiteScreenshots---------------------#\nclass_name = ('button', 'field', 'heading', 'iframe', 'image', 'label', 'link',\n              'text')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'websiteScreenshots/'\ndataset_websiteScreenshots = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_websiteScreenshots = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\n\ndataset_prefixes = [\n    'AerialMaritimeDrone_large',\n    'AerialMaritimeDrone_tiled',\n    'AmericanSignLanguageLetters',\n    'Aquarium',\n    'BCCD',\n    'boggleBoards',\n    'brackishUnderwater',\n    'ChessPieces',\n    'CottontailRabbits',\n    'dice',\n    'DroneControl',\n    'EgoHands_generic',\n    'EgoHands_specific',\n    'HardHatWorkers',\n    'MaskWearing',\n    'MountainDewCommercial',\n    'NorthAmericaMushrooms',\n    'openPoetryVision',\n    'OxfordPets_by_breed',\n    'OxfordPets_by_species',\n    'PKLot',\n    'Packages',\n    'PascalVOC',\n    'pistols',\n    'plantdoc',\n    'pothole',\n    'Raccoons',\n    'selfdrivingCar',\n    'ShellfishOpenImages',\n    'ThermalCheetah',\n    'thermalDogsAndPeople',\n    'UnoCards',\n    'VehiclesOpenImages',\n    'WildfireSmoke',\n    'websiteScreenshots',\n]\n\ndatasets = [\n    dataset_AerialMaritimeDrone_large, dataset_AerialMaritimeDrone_tiled,\n    dataset_AmericanSignLanguageLetters, dataset_Aquarium, dataset_BCCD,\n    dataset_boggleBoards, dataset_brackishUnderwater, dataset_ChessPieces,\n    dataset_CottontailRabbits, dataset_dice, dataset_DroneControl,\n    dataset_EgoHands_generic, dataset_EgoHands_specific,\n    dataset_HardHatWorkers, dataset_MaskWearing, dataset_MountainDewCommercial,\n    dataset_NorthAmericaMushrooms, dataset_openPoetryVision,\n    dataset_OxfordPets_by_breed, dataset_OxfordPets_by_species, dataset_PKLot,\n    dataset_Packages, dataset_PascalVOC, dataset_pistols, dataset_plantdoc,\n    dataset_pothole, dataset_Raccoon, dataset_selfdrivingCar,\n    dataset_ShellfishOpenImages, dataset_ThermalCheetah,\n    dataset_thermalDogsAndPeople, dataset_UnoCards, dataset_VehiclesOpenImages,\n    dataset_WildfireSmoke, dataset_websiteScreenshots\n]\n\nmetrics = [\n    val_evaluator_AerialMaritimeDrone_large,\n    val_evaluator_AerialMaritimeDrone_tiled,\n    val_evaluator_AmericanSignLanguageLetters, val_evaluator_Aquarium,\n    val_evaluator_BCCD, val_evaluator_boggleBoards,\n    val_evaluator_brackishUnderwater, val_evaluator_ChessPieces,\n    val_evaluator_CottontailRabbits, val_evaluator_dice,\n    val_evaluator_DroneControl, val_evaluator_EgoHands_generic,\n    val_evaluator_EgoHands_specific, val_evaluator_HardHatWorkers,\n    val_evaluator_MaskWearing, val_evaluator_MountainDewCommercial,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_openPoetryVision,\n    val_evaluator_OxfordPets_by_breed, val_evaluator_OxfordPets_by_species,\n    val_evaluator_PKLot, val_evaluator_Packages, val_evaluator_PascalVOC,\n    val_evaluator_pistols, val_evaluator_plantdoc, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_selfdrivingCar,\n    val_evaluator_ShellfishOpenImages, val_evaluator_ThermalCheetah,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_UnoCards,\n    val_evaluator_VehiclesOpenImages, val_evaluator_WildfireSmoke,\n    val_evaluator_websiteScreenshots\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw13.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py'  # noqa\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    test_mode=True,\n    pipeline=base_test_pipeline,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'penguin': {\n#         'suffix': ', which is black and white'\n#     },\n#     'puffin': {\n#         'suffix': ' with orange beaks'\n#     },\n#     'stingray': {\n#         'suffix': ' which is flat and round'\n#     },\n# }\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 CottontailRabbits---------------------#\nclass_name = ('Cottontail-Rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\n\ncaption_prompt = None\n# caption_prompt = {'Cottontail-Rabbit': {'name': 'rabbit'}}\n\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 EgoHands---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\n\ncaption_prompt = None\n# caption_prompt = {'hand': {'suffix': ' of a person'}}\n\ndataset_EgoHands = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 NorthAmericaMushrooms---------------------#\nclass_name = ('CoW', 'chanterelle')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\n\ncaption_prompt = None\n# caption_prompt = {\n#     'CoW': {\n#         'name': 'flat mushroom'\n#     },\n#     'chanterelle': {\n#         'name': 'yellow mushroom'\n#     }\n# }\n\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'package': {\n#         'prefix': 'there is a ',\n#         'suffix': ' on the porch'\n#     }\n# }\n\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'pothole': {\n#         'prefix': 'there are some ',\n#         'name': 'holes',\n#         'suffix': ' on the road'\n#     }\n# }\n\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\ndataset_prefixes = [\n    'AerialMaritimeDrone', 'Aquarium', 'CottontailRabbits', 'EgoHands',\n    'NorthAmericaMushrooms', 'Packages', 'PascalVOC', 'pistols', 'pothole',\n    'Raccoon', 'ShellfishOpenImages', 'thermalDogsAndPeople',\n    'VehiclesOpenImages'\n]\ndatasets = [\n    dataset_AerialMaritimeDrone, dataset_Aquarium, dataset_CottontailRabbits,\n    dataset_EgoHands, dataset_NorthAmericaMushrooms, dataset_Packages,\n    dataset_PascalVOC, dataset_pistols, dataset_pothole, dataset_Raccoon,\n    dataset_ShellfishOpenImages, dataset_thermalDogsAndPeople,\n    dataset_VehiclesOpenImages\n]\nmetrics = [\n    val_evaluator_AerialMaritimeDrone, val_evaluator_Aquarium,\n    val_evaluator_CottontailRabbits, val_evaluator_EgoHands,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_Packages,\n    val_evaluator_PascalVOC, val_evaluator_pistols, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_ShellfishOpenImages,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_VehiclesOpenImages\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw35.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py'  # noqa\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone_large---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone_large = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_large = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 AerialMaritimeDrone_tiled---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/tiled/'\ndataset_AerialMaritimeDrone_tiled = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_tiled = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 AmericanSignLanguageLetters---------------------#\nclass_name = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AmericanSignLanguageLetters/American Sign Language Letters.v1-v1.coco/'  # noqa\ndataset_AmericanSignLanguageLetters = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AmericanSignLanguageLetters = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 BCCD---------------------#\nclass_name = ('Platelets', 'RBC', 'WBC')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'BCCD/BCCD.v3-raw.coco/'\ndataset_BCCD = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_BCCD = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 boggleBoards---------------------#\nclass_name = ('Q', 'a', 'an', 'b', 'c', 'd', 'e', 'er', 'f', 'g', 'h', 'he',\n              'i', 'in', 'j', 'k', 'l', 'm', 'n', 'o', 'o ', 'p', 'q', 'qu',\n              'r', 's', 't', 't\\\\', 'th', 'u', 'v', 'w', 'wild', 'x', 'y', 'z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'boggleBoards/416x416AutoOrient/export/'\ndataset_boggleBoards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_boggleBoards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 brackishUnderwater---------------------#\nclass_name = ('crab', 'fish', 'jellyfish', 'shrimp', 'small_fish', 'starfish')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'brackishUnderwater/960x540/'\ndataset_brackishUnderwater = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_brackishUnderwater = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 ChessPieces---------------------#\nclass_name = ('  ', 'black bishop', 'black king', 'black knight', 'black pawn',\n              'black queen', 'black rook', 'white bishop', 'white king',\n              'white knight', 'white pawn', 'white queen', 'white rook')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ChessPieces/Chess Pieces.v23-raw.coco/'\ndataset_ChessPieces = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ChessPieces = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 CottontailRabbits---------------------#\nclass_name = ('rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 dice---------------------#\nclass_name = ('1', '2', '3', '4', '5', '6')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'dice/mediumColor/export/'\ndataset_dice = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_dice = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 DroneControl---------------------#\nclass_name = ('follow', 'follow_hand', 'land', 'land_hand', 'null', 'object',\n              'takeoff', 'takeoff-hand')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'DroneControl/Drone Control.v3-raw.coco/'\ndataset_DroneControl = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_DroneControl = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 EgoHands_generic---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\ncaption_prompt = {'hand': {'suffix': ' of a person'}}\ndataset_EgoHands_generic = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    # NOTE w. prompt 0.526, wo. prompt 0.608\n    # caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_generic = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 EgoHands_specific---------------------#\nclass_name = ('myleft', 'myright', 'yourleft', 'yourright')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/specific/'\ndataset_EgoHands_specific = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_specific = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------14 HardHatWorkers---------------------#\nclass_name = ('head', 'helmet', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'HardHatWorkers/raw/'\ndataset_HardHatWorkers = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_HardHatWorkers = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------15 MaskWearing---------------------#\nclass_name = ('mask', 'no-mask')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MaskWearing/raw/'\ndataset_MaskWearing = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MaskWearing = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------16 MountainDewCommercial---------------------#\nclass_name = ('bottle', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MountainDewCommercial/'\ndataset_MountainDewCommercial = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MountainDewCommercial = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------17 NorthAmericaMushrooms---------------------#\nclass_name = ('flat mushroom', 'yellow mushroom')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------18 openPoetryVision---------------------#\nclass_name = ('American Typewriter', 'Andale Mono', 'Apple Chancery', 'Arial',\n              'Avenir', 'Baskerville', 'Big Caslon', 'Bradley Hand',\n              'Brush Script MT', 'Chalkboard', 'Comic Sans MS', 'Copperplate',\n              'Courier', 'Didot', 'Futura', 'Geneva', 'Georgia', 'Gill Sans',\n              'Helvetica', 'Herculanum', 'Impact', 'Kefa', 'Lucida Grande',\n              'Luminari', 'Marker Felt', 'Menlo', 'Monaco', 'Noteworthy',\n              'Optima', 'PT Sans', 'PT Serif', 'Palatino', 'Papyrus',\n              'Phosphate', 'Rockwell', 'SF Pro', 'SignPainter', 'Skia',\n              'Snell Roundhand', 'Tahoma', 'Times New Roman', 'Trebuchet MS',\n              'Verdana')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'openPoetryVision/512x512/'\ndataset_openPoetryVision = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_openPoetryVision = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------19 OxfordPets_by_breed---------------------#\nclass_name = ('cat-Abyssinian', 'cat-Bengal', 'cat-Birman', 'cat-Bombay',\n              'cat-British_Shorthair', 'cat-Egyptian_Mau', 'cat-Maine_Coon',\n              'cat-Persian', 'cat-Ragdoll', 'cat-Russian_Blue', 'cat-Siamese',\n              'cat-Sphynx', 'dog-american_bulldog',\n              'dog-american_pit_bull_terrier', 'dog-basset_hound',\n              'dog-beagle', 'dog-boxer', 'dog-chihuahua',\n              'dog-english_cocker_spaniel', 'dog-english_setter',\n              'dog-german_shorthaired', 'dog-great_pyrenees', 'dog-havanese',\n              'dog-japanese_chin', 'dog-keeshond', 'dog-leonberger',\n              'dog-miniature_pinscher', 'dog-newfoundland', 'dog-pomeranian',\n              'dog-pug', 'dog-saint_bernard', 'dog-samoyed',\n              'dog-scottish_terrier', 'dog-shiba_inu',\n              'dog-staffordshire_bull_terrier', 'dog-wheaten_terrier',\n              'dog-yorkshire_terrier')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-breed/'  # noqa\ndataset_OxfordPets_by_breed = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_breed = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------20 OxfordPets_by_species---------------------#\nclass_name = ('cat', 'dog')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-species/'  # noqa\ndataset_OxfordPets_by_species = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_species = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------21 PKLot---------------------#\nclass_name = ('space-empty', 'space-occupied')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PKLot/640/'  # noqa\ndataset_PKLot = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PKLot = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------22 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\ncaption_prompt = {\n    'package': {\n        'prefix': 'there is a ',\n        'suffix': ' on the porch'\n    }\n}\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,  # NOTE w. prompt 0.695; wo. prompt 0.687\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------23 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------24 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------25 plantdoc---------------------#\nclass_name = ('Apple Scab Leaf', 'Apple leaf', 'Apple rust leaf',\n              'Bell_pepper leaf', 'Bell_pepper leaf spot', 'Blueberry leaf',\n              'Cherry leaf', 'Corn Gray leaf spot', 'Corn leaf blight',\n              'Corn rust leaf', 'Peach leaf', 'Potato leaf',\n              'Potato leaf early blight', 'Potato leaf late blight',\n              'Raspberry leaf', 'Soyabean leaf', 'Soybean leaf',\n              'Squash Powdery mildew leaf', 'Strawberry leaf',\n              'Tomato Early blight leaf', 'Tomato Septoria leaf spot',\n              'Tomato leaf', 'Tomato leaf bacterial spot',\n              'Tomato leaf late blight', 'Tomato leaf mosaic virus',\n              'Tomato leaf yellow virus', 'Tomato mold leaf',\n              'Tomato two spotted spider mites leaf', 'grape leaf',\n              'grape leaf black rot')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'plantdoc/416x416/'\ndataset_plantdoc = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_plantdoc = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------26 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\ncaption_prompt = {\n    'pothole': {\n        'name': 'holes',\n        'prefix': 'there are some ',\n        'suffix': ' on the road'\n    }\n}\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    # NOTE w. prompt 0.137; wo. prompt 0.215\n    # caption_prompt=caption_prompt,\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------27 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------28 selfdrivingCar---------------------#\nclass_name = ('biker', 'car', 'pedestrian', 'trafficLight',\n              'trafficLight-Green', 'trafficLight-GreenLeft',\n              'trafficLight-Red', 'trafficLight-RedLeft',\n              'trafficLight-Yellow', 'trafficLight-YellowLeft', 'truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'selfdrivingCar/fixedLarge/export/'\ndataset_selfdrivingCar = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_selfdrivingCar = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------29 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------30 ThermalCheetah---------------------#\nclass_name = ('cheetah', 'human')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ThermalCheetah/'\ndataset_ThermalCheetah = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ThermalCheetah = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------31 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------32 UnoCards---------------------#\nclass_name = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11',\n              '12', '13', '14')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'UnoCards/raw/'\ndataset_UnoCards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_UnoCards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------33 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------34 WildfireSmoke---------------------#\nclass_name = ('smoke', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'WildfireSmoke/'\ndataset_WildfireSmoke = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_WildfireSmoke = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------35 websiteScreenshots---------------------#\nclass_name = ('button', 'field', 'heading', 'iframe', 'image', 'label', 'link',\n              'text')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'websiteScreenshots/'\ndataset_websiteScreenshots = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_websiteScreenshots = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\n\ndataset_prefixes = [\n    'AerialMaritimeDrone_large',\n    'AerialMaritimeDrone_tiled',\n    'AmericanSignLanguageLetters',\n    'Aquarium',\n    'BCCD',\n    'boggleBoards',\n    'brackishUnderwater',\n    'ChessPieces',\n    'CottontailRabbits',\n    'dice',\n    'DroneControl',\n    'EgoHands_generic',\n    'EgoHands_specific',\n    'HardHatWorkers',\n    'MaskWearing',\n    'MountainDewCommercial',\n    'NorthAmericaMushrooms',\n    'openPoetryVision',\n    'OxfordPets_by_breed',\n    'OxfordPets_by_species',\n    'PKLot',\n    'Packages',\n    'PascalVOC',\n    'pistols',\n    'plantdoc',\n    'pothole',\n    'Raccoons',\n    'selfdrivingCar',\n    'ShellfishOpenImages',\n    'ThermalCheetah',\n    'thermalDogsAndPeople',\n    'UnoCards',\n    'VehiclesOpenImages',\n    'WildfireSmoke',\n    'websiteScreenshots',\n]\n\ndatasets = [\n    dataset_AerialMaritimeDrone_large, dataset_AerialMaritimeDrone_tiled,\n    dataset_AmericanSignLanguageLetters, dataset_Aquarium, dataset_BCCD,\n    dataset_boggleBoards, dataset_brackishUnderwater, dataset_ChessPieces,\n    dataset_CottontailRabbits, dataset_dice, dataset_DroneControl,\n    dataset_EgoHands_generic, dataset_EgoHands_specific,\n    dataset_HardHatWorkers, dataset_MaskWearing, dataset_MountainDewCommercial,\n    dataset_NorthAmericaMushrooms, dataset_openPoetryVision,\n    dataset_OxfordPets_by_breed, dataset_OxfordPets_by_species, dataset_PKLot,\n    dataset_Packages, dataset_PascalVOC, dataset_pistols, dataset_plantdoc,\n    dataset_pothole, dataset_Raccoon, dataset_selfdrivingCar,\n    dataset_ShellfishOpenImages, dataset_ThermalCheetah,\n    dataset_thermalDogsAndPeople, dataset_UnoCards, dataset_VehiclesOpenImages,\n    dataset_WildfireSmoke, dataset_websiteScreenshots\n]\n\nmetrics = [\n    val_evaluator_AerialMaritimeDrone_large,\n    val_evaluator_AerialMaritimeDrone_tiled,\n    val_evaluator_AmericanSignLanguageLetters, val_evaluator_Aquarium,\n    val_evaluator_BCCD, val_evaluator_boggleBoards,\n    val_evaluator_brackishUnderwater, val_evaluator_ChessPieces,\n    val_evaluator_CottontailRabbits, val_evaluator_dice,\n    val_evaluator_DroneControl, val_evaluator_EgoHands_generic,\n    val_evaluator_EgoHands_specific, val_evaluator_HardHatWorkers,\n    val_evaluator_MaskWearing, val_evaluator_MountainDewCommercial,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_openPoetryVision,\n    val_evaluator_OxfordPets_by_breed, val_evaluator_OxfordPets_by_species,\n    val_evaluator_PKLot, val_evaluator_Packages, val_evaluator_PascalVOC,\n    val_evaluator_pistols, val_evaluator_plantdoc, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_selfdrivingCar,\n    val_evaluator_ShellfishOpenImages, val_evaluator_ThermalCheetah,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_UnoCards,\n    val_evaluator_VehiclesOpenImages, val_evaluator_WildfireSmoke,\n    val_evaluator_websiteScreenshots\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/grounding_dino/odinw/override_category.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\n\nimport mmengine\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Override Category')\n    parser.add_argument('data_root')\n    return parser.parse_args()\n\n\ndef main():\n    args = parse_args()\n\n    ChessPieces = [{\n        'id': 1,\n        'name': '  ',\n        'supercategory': 'pieces'\n    }, {\n        'id': 2,\n        'name': 'black bishop',\n        'supercategory': 'pieces'\n    }, {\n        'id': 3,\n        'name': 'black king',\n        'supercategory': 'pieces'\n    }, {\n        'id': 4,\n        'name': 'black knight',\n        'supercategory': 'pieces'\n    }, {\n        'id': 5,\n        'name': 'black pawn',\n        'supercategory': 'pieces'\n    }, {\n        'id': 6,\n        'name': 'black queen',\n        'supercategory': 'pieces'\n    }, {\n        'id': 7,\n        'name': 'black rook',\n        'supercategory': 'pieces'\n    }, {\n        'id': 8,\n        'name': 'white bishop',\n        'supercategory': 'pieces'\n    }, {\n        'id': 9,\n        'name': 'white king',\n        'supercategory': 'pieces'\n    }, {\n        'id': 10,\n        'name': 'white knight',\n        'supercategory': 'pieces'\n    }, {\n        'id': 11,\n        'name': 'white pawn',\n        'supercategory': 'pieces'\n    }, {\n        'id': 12,\n        'name': 'white queen',\n        'supercategory': 'pieces'\n    }, {\n        'id': 13,\n        'name': 'white rook',\n        'supercategory': 'pieces'\n    }]\n\n    _data_root = args.data_root + 'ChessPieces/Chess Pieces.v23-raw.coco/'\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = ChessPieces\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n    CottontailRabbits = [{\n        'id': 1,\n        'name': 'rabbit',\n        'supercategory': 'Cottontail-Rabbit'\n    }]\n\n    _data_root = args.data_root + 'CottontailRabbits/'\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = CottontailRabbits\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n    NorthAmericaMushrooms = [{\n        'id': 1,\n        'name': 'flat mushroom',\n        'supercategory': 'mushroom'\n    }, {\n        'id': 2,\n        'name': 'yellow mushroom',\n        'supercategory': 'mushroom'\n    }]\n\n    _data_root = args.data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = NorthAmericaMushrooms\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "configs/grounding_dino/refcoco/grounding_dino_swin-b_pretrain_zeroshot_refexp.py",
    "content": "_base_ = './grounding_dino_swin-t_pretrain_zeroshot_refexp.py'\n\nmodel = dict(\n    type='GroundingDINO',\n    backbone=dict(\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        drop_path_rate=0.3,\n        patch_norm=True),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n"
  },
  {
    "path": "configs/grounding_dino/refcoco/grounding_dino_swin-t_pretrain_zeroshot_refexp.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py'\n\n# 30 is an empirical value, just set it to the maximum value\n# without affecting the evaluation result\nmodel = dict(test_cfg=dict(max_per_img=30))\n\ndata_root = 'data/coco/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive'))\n]\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/final_refexp_val.json'\nval_dataset_all_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\nval_evaluator_all_val = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco_testA.json'\nval_dataset_refcoco_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testA = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco_testB.json'\nval_dataset_refcoco_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testB = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco+_testA.json'\nval_dataset_refcoco_plus_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_plus_testA = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco+_testB.json'\nval_dataset_refcoco_plus_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_plus_testB = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcocog_test.json'\nval_dataset_refcocog_test = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcocog_test = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_val.json'\nval_dataset_grefcoco_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_grefcoco_val = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_testA.json'\nval_dataset_grefcoco_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_grefcoco_testA = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_testB.json'\nval_dataset_grefcoco_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_grefcoco_testB = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\ndatasets = [\n    val_dataset_all_val, val_dataset_refcoco_testA, val_dataset_refcoco_testB,\n    val_dataset_refcoco_plus_testA, val_dataset_refcoco_plus_testB,\n    val_dataset_refcocog_test, val_dataset_grefcoco_val,\n    val_dataset_grefcoco_testA, val_dataset_grefcoco_testB\n]\ndataset_prefixes = [\n    'val', 'refcoco_testA', 'refcoco_testB', 'refcoco+_testA',\n    'refcoco+_testB', 'refcocog_test', 'grefcoco_val', 'grefcoco_testA',\n    'grefcoco_testB'\n]\nmetrics = [\n    val_evaluator_all_val, val_evaluator_refcoco_testA,\n    val_evaluator_refcoco_testB, val_evaluator_refcoco_plus_testA,\n    val_evaluator_refcoco_plus_testB, val_evaluator_refcocog_test,\n    val_evaluator_grefcoco_val, val_evaluator_grefcoco_testA,\n    val_evaluator_grefcoco_testB\n]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/guided_anchoring/README.md",
    "content": "# Guided Anchoring\n\n> [Region Proposal by Guided Anchoring](https://arxiv.org/abs/1901.03278)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nRegion anchors are the cornerstone of modern object detection techniques. State-of-the-art detectors mostly rely on a dense anchoring scheme, where anchors are sampled uniformly over the spatial domain with a predefined set of scales and aspect ratios. In this paper, we revisit this foundational stage. Our study shows that it can be done much more effectively and efficiently. Specifically, we present an alternative scheme, named Guided Anchoring, which leverages semantic features to guide the anchoring. The proposed method jointly predicts the locations where the center of objects of interest are likely to exist as well as the scales and aspect ratios at different locations. On top of predicted anchor shapes, we mitigate the feature inconsistency with a feature adaption module. We also study the use of high-quality proposals to improve detection performance. The anchoring scheme can be seamlessly integrated into proposal methods and detectors. With Guided Anchoring, we achieve 9.1% higher recall on MS COCO with 90% fewer anchors than the RPN baseline. We also adopt Guided Anchoring in Fast R-CNN, Faster R-CNN and RetinaNet, respectively improving the detection mAP by 2.2%, 2.7% and 1.2%.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143891529-4c178948-c3fd-4543-ae6e-bb2aa3c8147e.png\"/>\n</div>\n\n## Results and Models\n\nThe results on COCO 2017 val is shown in the below table. (results on test-dev are usually slightly higher than val).\n\n| Method |    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | AR 1000 |                    Config                    |                                                                                                                                                          Download                                                                                                                                                           |\n| :----: | :-------------: | :-----: | :-----: | :------: | :------------: | :-----: | :------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| GA-RPN |    R-50-FPN     |  caffe  |   1x    |   5.3    |      15.8      |  68.4   | [config](./ga-rpn_r50-caffe_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_r50_caffe_fpn_1x_coco/ga_rpn_r50_caffe_fpn_1x_coco_20200531-899008a6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_r50_caffe_fpn_1x_coco/ga_rpn_r50_caffe_fpn_1x_coco_20200531_011819.log.json)   |\n| GA-RPN |    R-101-FPN    |  caffe  |   1x    |   7.3    |      13.0      |  69.5   | [config](./ga-rpn_r101-caffe_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_r101_caffe_fpn_1x_coco/ga_rpn_r101_caffe_fpn_1x_coco_20200531-ca9ba8fb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_r101_caffe_fpn_1x_coco/ga_rpn_r101_caffe_fpn_1x_coco_20200531_011812.log.json) |\n| GA-RPN | X-101-32x4d-FPN | pytorch |   1x    |   8.5    |      10.0      |  70.6   | [config](./ga-rpn_x101-32x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_x101_32x4d_fpn_1x_coco/ga_rpn_x101_32x4d_fpn_1x_coco_20200220-c28d1b18.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_x101_32x4d_fpn_1x_coco/ga_rpn_x101_32x4d_fpn_1x_coco_20200220_221326.log.json) |\n| GA-RPN | X-101-64x4d-FPN | pytorch |   1x    |   7.1    |      7.5       |  71.2   | [config](./ga-rpn_x101-64x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_x101_64x4d_fpn_1x_coco/ga_rpn_x101_64x4d_fpn_1x_coco_20200225-3c6e1aa2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_x101_64x4d_fpn_1x_coco/ga_rpn_x101_64x4d_fpn_1x_coco_20200225_152704.log.json) |\n\n|     Method     |    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                        Config                        |                                                                                                                                                                           Download                                                                                                                                                                            |\n| :------------: | :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :--------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| GA-Faster RCNN |    R-50-FPN     |  caffe  |   1x    |   5.5    |                |  39.6  | [config](./ga-faster-rcnn_r50-caffe_fpn_1x_coco.py)  |          [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_r50_caffe_fpn_1x_coco/ga_faster_r50_caffe_fpn_1x_coco_20200702_000718-a11ccfe6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_r50_caffe_fpn_1x_coco/ga_faster_r50_caffe_fpn_1x_coco_20200702_000718.log.json)           |\n| GA-Faster RCNN |    R-101-FPN    |  caffe  |   1x    |   7.5    |                |  41.5  | [config](./ga-faster-rcnn_r101-caffe_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_r101_caffe_fpn_1x_coco/ga_faster_r101_caffe_fpn_1x_coco_bbox_mAP-0.415_20200505_115528-fb82e499.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_r101_caffe_fpn_1x_coco/ga_faster_r101_caffe_fpn_1x_coco_20200505_115528.log.json) |\n| GA-Faster RCNN | X-101-32x4d-FPN | pytorch |   1x    |   8.7    |      9.7       |  43.0  | [config](./ga-faster-rcnn_x101-32x4d_fpn_1x_coco.py) |            [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_x101_32x4d_fpn_1x_coco/ga_faster_x101_32x4d_fpn_1x_coco_20200215-1ded9da3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_x101_32x4d_fpn_1x_coco/ga_faster_x101_32x4d_fpn_1x_coco_20200215_184547.log.json)            |\n| GA-Faster RCNN | X-101-64x4d-FPN | pytorch |   1x    |   11.8   |      7.3       |  43.9  | [config](./ga-faster-rcnn_x101-64x4d_fpn_1x_coco.py) |            [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_x101_64x4d_fpn_1x_coco/ga_faster_x101_64x4d_fpn_1x_coco_20200215-0fa7bde7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_x101_64x4d_fpn_1x_coco/ga_faster_x101_64x4d_fpn_1x_coco_20200215_104455.log.json)            |\n|  GA-RetinaNet  |    R-50-FPN     |  caffe  |   1x    |   3.5    |      16.8      |  36.9  |  [config](./ga-retinanet_r50-caffe_fpn_1x_coco.py)   |        [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_r50_caffe_fpn_1x_coco/ga_retinanet_r50_caffe_fpn_1x_coco_20201020-39581c6f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_r50_caffe_fpn_1x_coco/ga_retinanet_r50_caffe_fpn_1x_coco_20201020_225450.log.json)        |\n|  GA-RetinaNet  |    R-101-FPN    |  caffe  |   1x    |   5.5    |      12.9      |  39.0  |  [config](./ga-retinanet_r101-caffe_fpn_1x_coco.py)  |      [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_r101_caffe_fpn_1x_coco/ga_retinanet_r101_caffe_fpn_1x_coco_20200531-6266453c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_r101_caffe_fpn_1x_coco/ga_retinanet_r101_caffe_fpn_1x_coco_20200531_012847.log.json)      |\n|  GA-RetinaNet  | X-101-32x4d-FPN | pytorch |   1x    |   6.9    |      10.6      |  40.5  |  [config](./ga-retinanet_x101-32x4d_fpn_1x_coco.py)  |      [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_x101_32x4d_fpn_1x_coco/ga_retinanet_x101_32x4d_fpn_1x_coco_20200219-40c56caa.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_x101_32x4d_fpn_1x_coco/ga_retinanet_x101_32x4d_fpn_1x_coco_20200219_223025.log.json)      |\n|  GA-RetinaNet  | X-101-64x4d-FPN | pytorch |   1x    |   9.9    |      7.7       |  41.3  |  [config](./ga-retinanet_x101-64x4d_fpn_1x_coco.py)  |      [model](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_x101_64x4d_fpn_1x_coco/ga_retinanet_x101_64x4d_fpn_1x_coco_20200226-ef9f7f1f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_x101_64x4d_fpn_1x_coco/ga_retinanet_x101_64x4d_fpn_1x_coco_20200226_221123.log.json)      |\n\n- In the Guided Anchoring paper, `score_thr` is set to 0.001 in Fast/Faster RCNN and 0.05 in RetinaNet for both baselines and Guided Anchoring.\n\n- Performance on COCO test-dev benchmark are shown as follows.\n\n|     Method     | Backbone  | Style | Lr schd | Aug Train | Score thr | AP  | AP_50 | AP_75 | AP_small | AP_medium | AP_large | Download |\n| :------------: | :-------: | :---: | :-----: | :-------: | :-------: | :-: | :---: | :---: | :------: | :-------: | :------: | :------: |\n| GA-Faster RCNN | R-101-FPN | caffe |   1x    |     F     |   0.05    |     |       |       |          |           |          |          |\n| GA-Faster RCNN | R-101-FPN | caffe |   1x    |     F     |   0.001   |     |       |       |          |           |          |          |\n|  GA-RetinaNet  | R-101-FPN | caffe |   1x    |     F     |   0.05    |     |       |       |          |           |          |          |\n|  GA-RetinaNet  | R-101-FPN | caffe |   2x    |     T     |   0.05    |     |       |       |          |           |          |          |\n\n## Citation\n\nWe provide config files to reproduce the results in the CVPR 2019 paper for [Region Proposal by Guided Anchoring](https://arxiv.org/abs/1901.03278).\n\n```latex\n@inproceedings{wang2019region,\n    title={Region Proposal by Guided Anchoring},\n    author={Jiaqi Wang and Kai Chen and Shuo Yang and Chen Change Loy and Dahua Lin},\n    booktitle={IEEE Conference on Computer Vision and Pattern Recognition},\n    year={2019}\n}\n```\n"
  },
  {
    "path": "configs/guided_anchoring/ga-fast-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../fast_rcnn/fast-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    roi_head=dict(\n        bbox_head=dict(bbox_coder=dict(target_stds=[0.05, 0.05, 0.1, 0.1]))),\n    # model training and testing settings\n    train_cfg=dict(\n        rcnn=dict(\n            assigner=dict(pos_iou_thr=0.6, neg_iou_thr=0.6, min_pos_iou=0.6),\n            sampler=dict(num=256))),\n    test_cfg=dict(rcnn=dict(score_thr=1e-3)))\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\nimg_norm_cfg = dict(\n    mean=[103.530, 116.280, 123.675], std=[1.0, 1.0, 1.0], to_rgb=False)\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadProposals', num_max_proposals=300),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', flip_ratio=0.5),\n    dict(type='Normalize', **img_norm_cfg),\n    dict(type='Pad', size_divisor=32),\n    dict(type='DefaultFormatBundle'),\n    dict(type='Collect', keys=['img', 'proposals', 'gt_bboxes', 'gt_labels']),\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadProposals', num_max_proposals=None),\n    dict(\n        type='MultiScaleFlipAug',\n        img_scale=(1333, 800),\n        flip=False,\n        transforms=[\n            dict(type='Resize', keep_ratio=True),\n            dict(type='RandomFlip'),\n            dict(type='Normalize', **img_norm_cfg),\n            dict(type='Pad', size_divisor=32),\n            dict(type='ImageToTensor', keys=['img']),\n            dict(type='Collect', keys=['img', 'proposals']),\n        ])\n]\n# TODO: support loading proposals\ndata = dict(\n    train=dict(\n        proposal_file=data_root + 'proposals/ga_rpn_r50_fpn_1x_train2017.pkl',\n        pipeline=train_pipeline),\n    val=dict(\n        proposal_file=data_root + 'proposals/ga_rpn_r50_fpn_1x_val2017.pkl',\n        pipeline=test_pipeline),\n    test=dict(\n        proposal_file=data_root + 'proposals/ga_rpn_r50_fpn_1x_val2017.pkl',\n        pipeline=test_pipeline))\noptimizer_config = dict(\n    _delete_=True, grad_clip=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-faster-rcnn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './ga-faster-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-faster-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    rpn_head=dict(\n        _delete_=True,\n        type='GARPNHead',\n        in_channels=256,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=8,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[8],\n            strides=[4, 8, 16, 32, 64]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.14, 0.14]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.11, 0.11]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n    roi_head=dict(\n        bbox_head=dict(bbox_coder=dict(target_stds=[0.05, 0.05, 0.1, 0.1]))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            ga_assigner=dict(\n                type='ApproxMaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            ga_sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            center_ratio=0.2,\n            ignore_ratio=0.5),\n        rpn_proposal=dict(nms_post=1000, max_per_img=300),\n        rcnn=dict(\n            assigner=dict(pos_iou_thr=0.6, neg_iou_thr=0.6, min_pos_iou=0.6),\n            sampler=dict(type='RandomSampler', num=256))),\n    test_cfg=dict(\n        rpn=dict(nms_post=1000, max_per_img=300), rcnn=dict(score_thr=1e-3)))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-faster-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    rpn_head=dict(\n        _delete_=True,\n        type='GARPNHead',\n        in_channels=256,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=8,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[8],\n            strides=[4, 8, 16, 32, 64]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.14, 0.14]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.11, 0.11]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n    roi_head=dict(\n        bbox_head=dict(bbox_coder=dict(target_stds=[0.05, 0.05, 0.1, 0.1]))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            ga_assigner=dict(\n                type='ApproxMaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            ga_sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            center_ratio=0.2,\n            ignore_ratio=0.5),\n        rpn_proposal=dict(nms_post=1000, max_per_img=300),\n        rcnn=dict(\n            assigner=dict(pos_iou_thr=0.6, neg_iou_thr=0.6, min_pos_iou=0.6),\n            sampler=dict(type='RandomSampler', num=256))),\n    test_cfg=dict(\n        rpn=dict(nms_post=1000, max_per_img=300), rcnn=dict(score_thr=1e-3)))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-faster-rcnn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './ga-faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-faster-rcnn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './ga-faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-retinanet_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './ga-retinanet_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-retinanet_r101-caffe_fpn_ms-2x.py",
    "content": "_base_ = './ga-retinanet_r101-caffe_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 480), (1333, 960)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3.0,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    bbox_head=dict(\n        _delete_=True,\n        type='GARetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.04, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        ga_assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.4,\n            ignore_iof_thr=-1),\n        ga_sampler=dict(\n            type='RandomSampler',\n            num=256,\n            pos_fraction=0.5,\n            neg_pos_ub=-1,\n            add_gt_as_proposals=False),\n        assigner=dict(neg_iou_thr=0.5, min_pos_iou=0.0),\n        center_ratio=0.2,\n        ignore_ratio=0.5))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-retinanet_r50_fpn_1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    bbox_head=dict(\n        _delete_=True,\n        type='GARetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.04, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        ga_assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.4,\n            ignore_iof_thr=-1),\n        ga_sampler=dict(\n            type='RandomSampler',\n            num=256,\n            pos_fraction=0.5,\n            neg_pos_ub=-1,\n            add_gt_as_proposals=False),\n        assigner=dict(neg_iou_thr=0.5, min_pos_iou=0.0),\n        center_ratio=0.2,\n        ignore_ratio=0.5))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-retinanet_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './ga-retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-retinanet_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './ga-retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-rpn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './ga-rpn_r50-caffe_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-rpn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../rpn/rpn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    rpn_head=dict(\n        _delete_=True,\n        type='GARPNHead',\n        in_channels=256,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=8,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[8],\n            strides=[4, 8, 16, 32, 64]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.14, 0.14]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.11, 0.11]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            ga_assigner=dict(\n                type='ApproxMaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            ga_sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            center_ratio=0.2,\n            ignore_ratio=0.5)),\n    test_cfg=dict(rpn=dict(nms_post=1000)))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-rpn_r50_fpn_1x_coco.py",
    "content": "_base_ = '../rpn/rpn_r50_fpn_1x_coco.py'\nmodel = dict(\n    rpn_head=dict(\n        _delete_=True,\n        type='GARPNHead',\n        in_channels=256,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=8,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[8],\n            strides=[4, 8, 16, 32, 64]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.14, 0.14]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.11, 0.11]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            ga_assigner=dict(\n                type='ApproxMaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            ga_sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            center_ratio=0.2,\n            ignore_ratio=0.5)),\n    test_cfg=dict(rpn=dict(nms_post=1000)))\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-rpn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './ga-rpn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/guided_anchoring/ga-rpn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './ga-rpn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/guided_anchoring/metafile.yml",
    "content": "Collections:\n  - Name: Guided Anchoring\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - Guided Anchoring\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1901.03278\n      Title: 'Region Proposal by Guided Anchoring'\n    README: configs/guided_anchoring/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/dense_heads/ga_retina_head.py#L10\n      Version: v2.0.0\n\nModels:\n  - Name: ga-rpn_r50-caffe_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-rpn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.3\n      inference time (ms/im):\n        - value: 63.29\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Region Proposal\n        Dataset: COCO\n        Metrics:\n          AR@1000: 68.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_r50_caffe_fpn_1x_coco/ga_rpn_r50_caffe_fpn_1x_coco_20200531-899008a6.pth\n\n  - Name: ga-rpn_r101-caffe_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-rpn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.3\n      inference time (ms/im):\n        - value: 76.92\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Region Proposal\n        Dataset: COCO\n        Metrics:\n          AR@1000: 69.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_r101_caffe_fpn_1x_coco/ga_rpn_r101_caffe_fpn_1x_coco_20200531-ca9ba8fb.pth\n\n  - Name: ga-rpn_x101-32x4d_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-rpn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.5\n      inference time (ms/im):\n        - value: 100\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Region Proposal\n        Dataset: COCO\n        Metrics:\n          AR@1000: 70.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_x101_32x4d_fpn_1x_coco/ga_rpn_x101_32x4d_fpn_1x_coco_20200220-c28d1b18.pth\n\n  - Name: ga-rpn_x101-64x4d_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-rpn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      inference time (ms/im):\n        - value: 133.33\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Region Proposal\n        Dataset: COCO\n        Metrics:\n          AR@1000: 70.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_rpn_x101_64x4d_fpn_1x_coco/ga_rpn_x101_64x4d_fpn_1x_coco_20200225-3c6e1aa2.pth\n\n  - Name: ga-faster-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-faster-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_r50_caffe_fpn_1x_coco/ga_faster_r50_caffe_fpn_1x_coco_20200702_000718-a11ccfe6.pth\n\n  - Name: ga-faster-rcnn_r101-caffe_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-faster-rcnn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.5\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_r101_caffe_fpn_1x_coco/ga_faster_r101_caffe_fpn_1x_coco_bbox_mAP-0.415_20200505_115528-fb82e499.pth\n\n  - Name: ga-faster-rcnn_x101-32x4d_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-faster-rcnn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.7\n      inference time (ms/im):\n        - value: 103.09\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_x101_32x4d_fpn_1x_coco/ga_faster_x101_32x4d_fpn_1x_coco_20200215-1ded9da3.pth\n\n  - Name: ga-faster-rcnn_x101-64x4d_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-faster-rcnn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 11.8\n      inference time (ms/im):\n        - value: 136.99\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_faster_x101_64x4d_fpn_1x_coco/ga_faster_x101_64x4d_fpn_1x_coco_20200215-0fa7bde7.pth\n\n  - Name: ga-retinanet_r50-caffe_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-retinanet_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.5\n      inference time (ms/im):\n        - value: 59.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_r50_caffe_fpn_1x_coco/ga_retinanet_r50_caffe_fpn_1x_coco_20201020-39581c6f.pth\n\n  - Name: ga-retinanet_r101-caffe_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-retinanet_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      inference time (ms/im):\n        - value: 77.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_r101_caffe_fpn_1x_coco/ga_retinanet_r101_caffe_fpn_1x_coco_20200531-6266453c.pth\n\n  - Name: ga-retinanet_x101-32x4d_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-retinanet_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.9\n      inference time (ms/im):\n        - value: 94.34\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_x101_32x4d_fpn_1x_coco/ga_retinanet_x101_32x4d_fpn_1x_coco_20200219-40c56caa.pth\n\n  - Name: ga-retinanet_x101-64x4d_fpn_1x_coco\n    In Collection: Guided Anchoring\n    Config: configs/guided_anchoring/ga-retinanet_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.9\n      inference time (ms/im):\n        - value: 129.87\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/guided_anchoring/ga_retinanet_x101_64x4d_fpn_1x_coco/ga_retinanet_x101_64x4d_fpn_1x_coco_20200226-ef9f7f1f.pth\n"
  },
  {
    "path": "configs/hrnet/README.md",
    "content": "# HRNet\n\n> [Deep High-Resolution Representation Learning for Human Pose Estimation](https://arxiv.org/abs/1902.09212)\n\n<!-- [BACKBONE] -->\n\n## Abstract\n\nThis is an official pytorch implementation of Deep High-Resolution Representation Learning for Human Pose Estimation. In this work, we are interested in the human pose estimation problem with a focus on learning reliable high-resolution representations. Most existing methods recover high-resolution representations from low-resolution representations produced by a high-to-low resolution network. Instead, our proposed network maintains high-resolution representations through the whole process. We start from a high-resolution subnetwork as the first stage, gradually add high-to-low resolution subnetworks one by one to form more stages, and connect the mutli-resolution subnetworks in parallel. We conduct repeated multi-scale fusions such that each of the high-to-low resolution representations receives information from other parallel representations over and over, leading to rich high-resolution representations. As a result, the predicted keypoint heatmap is potentially more accurate and spatially more precise. We empirically demonstrate the effectiveness of our network through the superior pose estimation results over two benchmark datasets: the COCO keypoint detection dataset and the MPII Human Pose dataset.\n\nHigh-resolution representation learning plays an essential role in many vision problems, e.g., pose estimation and semantic segmentation. The high-resolution network (HRNet), recently developed for human pose estimation, maintains high-resolution representations through the whole process by connecting high-to-low resolution convolutions in parallel and produces strong high-resolution representations by repeatedly conducting fusions across parallel convolutions.\nIn this paper, we conduct a further study on high-resolution representations by introducing a simple yet effective modification and apply it to a wide range of vision tasks. We augment the high-resolution representation by aggregating the (upsampled) representations from all the parallel convolutions rather than only the representation from the high-resolution convolution as done in HRNet. This simple modification leads to stronger representations, evidenced by superior results. We show top results in semantic segmentation on Cityscapes, LIP, and PASCAL Context, and facial landmark detection on AFLW, COFW, 300W, and WFLW. In addition, we build a multi-level representation from the high-resolution representation and apply it to the Faster R-CNN object detection framework and the extended frameworks. The proposed approach achieves superior results to existing single-model networks on COCO object detection.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143892740-a4e9743e-a323-4ace-8025-50e251ef43ff.png\"/>\n</div>\n\n## Results and Models\n\n### Faster R-CNN\n\n|   Backbone   |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                     Config                      |                                                                                                                                                         Download                                                                                                                                                         |\n| :----------: | :-----: | :-----: | :------: | :------------: | :----: | :---------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HRNetV2p-W18 | pytorch |   1x    |   6.6    |      13.4      |  36.9  | [config](./faster-rcnn_hrnetv2p-w18-1x_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w18_1x_coco/faster_rcnn_hrnetv2p_w18_1x_coco_20200130-56651a6d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w18_1x_coco/faster_rcnn_hrnetv2p_w18_1x_coco_20200130_211246.log.json)     |\n| HRNetV2p-W18 | pytorch |   2x    |   6.6    |       -        |  38.9  | [config](./faster-rcnn_hrnetv2p-w18-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w18_2x_coco/faster_rcnn_hrnetv2p_w18_2x_coco_20200702_085731-a4ec0611.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w18_2x_coco/faster_rcnn_hrnetv2p_w18_2x_coco_20200702_085731.log.json) |\n| HRNetV2p-W32 | pytorch |   1x    |   9.0    |      12.4      |  40.2  | [config](./faster-rcnn_hrnetv2p-w32-1x_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w32_1x_coco/faster_rcnn_hrnetv2p_w32_1x_coco_20200130-6e286425.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w32_1x_coco/faster_rcnn_hrnetv2p_w32_1x_coco_20200130_204442.log.json)     |\n| HRNetV2p-W32 | pytorch |   2x    |   9.0    |       -        |  41.4  | [config](./faster-rcnn_hrnetv2p-w32_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w32_2x_coco/faster_rcnn_hrnetv2p_w32_2x_coco_20200529_015927-976a9c15.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w32_2x_coco/faster_rcnn_hrnetv2p_w32_2x_coco_20200529_015927.log.json) |\n| HRNetV2p-W40 | pytorch |   1x    |   10.4   |      10.5      |  41.2  | [config](./faster-rcnn_hrnetv2p-w40-1x_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w40_1x_coco/faster_rcnn_hrnetv2p_w40_1x_coco_20200210-95c1f5ce.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w40_1x_coco/faster_rcnn_hrnetv2p_w40_1x_coco_20200210_125315.log.json)     |\n| HRNetV2p-W40 | pytorch |   2x    |   10.4   |       -        |  42.1  | [config](./faster-rcnn_hrnetv2p-w40_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w40_2x_coco/faster_rcnn_hrnetv2p_w40_2x_coco_20200512_161033-0f236ef4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w40_2x_coco/faster_rcnn_hrnetv2p_w40_2x_coco_20200512_161033.log.json) |\n\n### Mask R-CNN\n\n|   Backbone   |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                    Config                     |                                                                                                                                                     Download                                                                                                                                                     |\n| :----------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HRNetV2p-W18 | pytorch |   1x    |   7.0    |      11.7      |  37.7  |  34.2   | [config](./mask-rcnn_hrnetv2p-w18-1x_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w18_1x_coco/mask_rcnn_hrnetv2p_w18_1x_coco_20200205-1c3d78ed.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w18_1x_coco/mask_rcnn_hrnetv2p_w18_1x_coco_20200205_232523.log.json)     |\n| HRNetV2p-W18 | pytorch |   2x    |   7.0    |       -        |  39.8  |  36.0   | [config](./mask-rcnn_hrnetv2p-w18-2x_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w18_2x_coco/mask_rcnn_hrnetv2p_w18_2x_coco_20200212-b3c825b1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w18_2x_coco/mask_rcnn_hrnetv2p_w18_2x_coco_20200212_134222.log.json)     |\n| HRNetV2p-W32 | pytorch |   1x    |   9.4    |      11.3      |  41.2  |  37.1   | [config](./mask-rcnn_hrnetv2p-w32-1x_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w32_1x_coco/mask_rcnn_hrnetv2p_w32_1x_coco_20200207-b29f616e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w32_1x_coco/mask_rcnn_hrnetv2p_w32_1x_coco_20200207_055017.log.json)     |\n| HRNetV2p-W32 | pytorch |   2x    |   9.4    |       -        |  42.5  |  37.8   | [config](./mask-rcnn_hrnetv2p-w32-2x_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w32_2x_coco/mask_rcnn_hrnetv2p_w32_2x_coco_20200213-45b75b4d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w32_2x_coco/mask_rcnn_hrnetv2p_w32_2x_coco_20200213_150518.log.json)     |\n| HRNetV2p-W40 | pytorch |   1x    |   10.9   |                |  42.1  |  37.5   | [config](./mask-rcnn_hrnetv2p-w40_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w40_1x_coco/mask_rcnn_hrnetv2p_w40_1x_coco_20200511_015646-66738b35.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w40_1x_coco/mask_rcnn_hrnetv2p_w40_1x_coco_20200511_015646.log.json) |\n| HRNetV2p-W40 | pytorch |   2x    |   10.9   |                |  42.8  |  38.2   | [config](./mask-rcnn_hrnetv2p-w40-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w40_2x_coco/mask_rcnn_hrnetv2p_w40_2x_coco_20200512_163732-aed5e4ab.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w40_2x_coco/mask_rcnn_hrnetv2p_w40_2x_coco_20200512_163732.log.json) |\n\n### Cascade R-CNN\n\n|   Backbone   |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                      Config                       |                                                                                                                                                             Download                                                                                                                                                             |\n| :----------: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HRNetV2p-W18 | pytorch |   20e   |   7.0    |      11.0      |  41.2  | [config](./cascade-rcnn_hrnetv2p-w18-20e_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w18_20e_coco/cascade_rcnn_hrnetv2p_w18_20e_coco_20200210-434be9d7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w18_20e_coco/cascade_rcnn_hrnetv2p_w18_20e_coco_20200210_105632.log.json)     |\n| HRNetV2p-W32 | pytorch |   20e   |   9.4    |      11.0      |  43.3  | [config](./cascade-rcnn_hrnetv2p-w32-20e_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w32_20e_coco/cascade_rcnn_hrnetv2p_w32_20e_coco_20200208-928455a4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w32_20e_coco/cascade_rcnn_hrnetv2p_w32_20e_coco_20200208_160511.log.json)     |\n| HRNetV2p-W40 | pytorch |   20e   |   10.8   |                |  43.8  | [config](./cascade-rcnn_hrnetv2p-w40-20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w40_20e_coco/cascade_rcnn_hrnetv2p_w40_20e_coco_20200512_161112-75e47b04.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w40_20e_coco/cascade_rcnn_hrnetv2p_w40_20e_coco_20200512_161112.log.json) |\n\n### Cascade Mask R-CNN\n\n|   Backbone   |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                         Config                         |                                                                                                                                                                       Download                                                                                                                                                                       |\n| :----------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :----------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HRNetV2p-W18 | pytorch |   20e   |   8.5    |      8.5       |  41.6  |  36.4   | [config](./cascade-mask-rcnn_hrnetv2p-w18_20e_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w18_20e_coco/cascade_mask_rcnn_hrnetv2p_w18_20e_coco_20200210-b543cd2b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w18_20e_coco/cascade_mask_rcnn_hrnetv2p_w18_20e_coco_20200210_093149.log.json)     |\n| HRNetV2p-W32 | pytorch |   20e   |          |      8.3       |  44.3  |  38.6   | [config](./cascade-mask-rcnn_hrnetv2p-w32_20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w32_20e_coco/cascade_mask_rcnn_hrnetv2p_w32_20e_coco_20200512_154043-39d9cf7b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w32_20e_coco/cascade_mask_rcnn_hrnetv2p_w32_20e_coco_20200512_154043.log.json) |\n| HRNetV2p-W40 | pytorch |   20e   |   12.5   |                |  45.1  |  39.3   | [config](./cascade-mask-rcnn_hrnetv2p-w40-20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w40_20e_coco/cascade_mask_rcnn_hrnetv2p_w40_20e_coco_20200527_204922-969c4610.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w40_20e_coco/cascade_mask_rcnn_hrnetv2p_w40_20e_coco_20200527_204922.log.json) |\n\n### Hybrid Task Cascade (HTC)\n\n|   Backbone   |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                  Config                  |                                                                                                                                           Download                                                                                                                                           |\n| :----------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :--------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HRNetV2p-W18 | pytorch |   20e   |   10.8   |      4.7       |  42.8  |  37.9   | [config](./htc_hrnetv2p-w18_20e_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w18_20e_coco/htc_hrnetv2p_w18_20e_coco_20200210-b266988c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w18_20e_coco/htc_hrnetv2p_w18_20e_coco_20200210_182735.log.json)     |\n| HRNetV2p-W32 | pytorch |   20e   |   13.1   |      4.9       |  45.4  |  39.9   | [config](./htc_hrnetv2p-w32_20e_coco.py) |    [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w32_20e_coco/htc_hrnetv2p_w32_20e_coco_20200207-7639fa12.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w32_20e_coco/htc_hrnetv2p_w32_20e_coco_20200207_193153.log.json)     |\n| HRNetV2p-W40 | pytorch |   20e   |   14.6   |                |  46.4  |  40.8   | [config](./htc_hrnetv2p-w40_20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w40_20e_coco/htc_hrnetv2p_w40_20e_coco_20200529_183411-417c4d5b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w40_20e_coco/htc_hrnetv2p_w40_20e_coco_20200529_183411.log.json) |\n\n### FCOS\n\n|   Backbone   |  Style  | GN  | MS train | Lr schd | Mem (GB) | Inf time (fps) | box AP |                              Config                              |                                                                                                                                                                                                   Download                                                                                                                                                                                                   |\n| :----------: | :-----: | :-: | :------: | :-----: | :------: | :------------: | :----: | :--------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| HRNetV2p-W18 | pytorch |  Y  |    N     |   1x    |   13.0   |      12.9      |  35.3  |      [config](./fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py)       |                                 [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_4x4_1x_coco/fcos_hrnetv2p_w18_gn-head_4x4_1x_coco_20201212_100710-4ad151de.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_4x4_1x_coco/fcos_hrnetv2p_w18_gn-head_4x4_1x_coco_20201212_100710.log.json)                                 |\n| HRNetV2p-W18 | pytorch |  Y  |    N     |   2x    |   13.0   |       -        |  38.2  |      [config](./fcos_hrnetv2p-w18-gn-head_4xb4-2x_coco.py)       |                                 [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_4x4_2x_coco/fcos_hrnetv2p_w18_gn-head_4x4_2x_coco_20201212_101110-5c575fa5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_4x4_2x_coco/fcos_hrnetv2p_w18_gn-head_4x4_2x_coco_20201212_101110.log.json)                                 |\n| HRNetV2p-W32 | pytorch |  Y  |    N     |   1x    |   17.5   |      12.9      |  39.5  |      [config](./fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py)       |                                 [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_4x4_1x_coco/fcos_hrnetv2p_w32_gn-head_4x4_1x_coco_20201211_134730-cb8055c0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_4x4_1x_coco/fcos_hrnetv2p_w32_gn-head_4x4_1x_coco_20201211_134730.log.json)                                 |\n| HRNetV2p-W32 | pytorch |  Y  |    N     |   2x    |   17.5   |       -        |  40.8  |      [config](./fcos_hrnetv2p-w32-gn-head_4xb4-2x_coco.py)       |                                 [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_4x4_2x_coco/fcos_hrnetv2p_w32_gn-head_4x4_2x_coco_20201212_112133-77b6b9bb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_4x4_2x_coco/fcos_hrnetv2p_w32_gn-head_4x4_2x_coco_20201212_112133.log.json)                                 |\n| HRNetV2p-W18 | pytorch |  Y  |    Y     |   2x    |   13.0   |      12.9      |  38.3  | [config](./fcos_hrnetv2p-w18-gn-head_ms-640-800-4xb4-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w18_gn-head_mstrain_640-800_4x4_2x_coco_20201212_111651-441e9d9f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w18_gn-head_mstrain_640-800_4x4_2x_coco_20201212_111651.log.json) |\n| HRNetV2p-W32 | pytorch |  Y  |    Y     |   2x    |   17.5   |      12.4      |  41.9  | [config](./fcos_hrnetv2p-w32-gn-head_ms-640-800-4xb4-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w32_gn-head_mstrain_640-800_4x4_2x_coco_20201212_090846-b6f2b49f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w32_gn-head_mstrain_640-800_4x4_2x_coco_20201212_090846.log.json) |\n| HRNetV2p-W48 | pytorch |  Y  |    Y     |   2x    |   20.3   |      10.8      |  42.7  | [config](./fcos_hrnetv2p-w40-gn-head_ms-640-800-4xb4-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w40_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w40_gn-head_mstrain_640-800_4x4_2x_coco_20201212_124752-f22d2ce5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w40_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w40_gn-head_mstrain_640-800_4x4_2x_coco_20201212_124752.log.json) |\n\n**Note:**\n\n- The `28e` schedule in HTC indicates decreasing the lr at 24 and 27 epochs, with a total of 28 epochs.\n- HRNetV2 ImageNet pretrained models are in [HRNets for Image Classification](https://github.com/HRNet/HRNet-Image-Classification).\n\n## Citation\n\n```latex\n@inproceedings{SunXLW19,\n  title={Deep High-Resolution Representation Learning for Human Pose Estimation},\n  author={Ke Sun and Bin Xiao and Dong Liu and Jingdong Wang},\n  booktitle={CVPR},\n  year={2019}\n}\n\n@article{SunZJCXLMWLW19,\n  title={High-Resolution Representations for Labeling Pixels and Regions},\n  author={Ke Sun and Yang Zhao and Borui Jiang and Tianheng Cheng and Bin Xiao\n  and Dong Liu and Yadong Mu and Xinggang Wang and Wenyu Liu and Jingdong Wang},\n  journal   = {CoRR},\n  volume    = {abs/1904.04514},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/hrnet/cascade-mask-rcnn_hrnetv2p-w18_20e_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_hrnetv2p-w32_20e_coco.py'\n# model settings\nmodel = dict(\n    backbone=dict(\n        extra=dict(\n            stage2=dict(num_channels=(18, 36)),\n            stage3=dict(num_channels=(18, 36, 72)),\n            stage4=dict(num_channels=(18, 36, 72, 144))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')),\n    neck=dict(type='HRFPN', in_channels=[18, 36, 72, 144], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/cascade-mask-rcnn_hrnetv2p-w32_20e_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(\n        _delete_=True,\n        type='HRFPN',\n        in_channels=[32, 64, 128, 256],\n        out_channels=256))\n# learning policy\nmax_epochs = 20\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 19],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/cascade-mask-rcnn_hrnetv2p-w40-20e_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_hrnetv2p-w32_20e_coco.py'\n# model settings\nmodel = dict(\n    backbone=dict(\n        type='HRNet',\n        extra=dict(\n            stage2=dict(num_channels=(40, 80)),\n            stage3=dict(num_channels=(40, 80, 160)),\n            stage4=dict(num_channels=(40, 80, 160, 320))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w40')),\n    neck=dict(type='HRFPN', in_channels=[40, 80, 160, 320], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/cascade-rcnn_hrnetv2p-w18-20e_coco.py",
    "content": "_base_ = './cascade-rcnn_hrnetv2p-w32-20e_coco.py'\n# model settings\nmodel = dict(\n    backbone=dict(\n        extra=dict(\n            stage2=dict(num_channels=(18, 36)),\n            stage3=dict(num_channels=(18, 36, 72)),\n            stage4=dict(num_channels=(18, 36, 72, 144))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')),\n    neck=dict(type='HRFPN', in_channels=[18, 36, 72, 144], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/cascade-rcnn_hrnetv2p-w32-20e_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(\n        _delete_=True,\n        type='HRFPN',\n        in_channels=[32, 64, 128, 256],\n        out_channels=256))\n# learning policy\nmax_epochs = 20\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 19],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/cascade-rcnn_hrnetv2p-w40-20e_coco.py",
    "content": "_base_ = './cascade-rcnn_hrnetv2p-w32-20e_coco.py'\n# model settings\nmodel = dict(\n    backbone=dict(\n        type='HRNet',\n        extra=dict(\n            stage2=dict(num_channels=(40, 80)),\n            stage3=dict(num_channels=(40, 80, 160)),\n            stage4=dict(num_channels=(40, 80, 160, 320))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w40')),\n    neck=dict(type='HRFPN', in_channels=[40, 80, 160, 320], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py",
    "content": "_base_ = './faster-rcnn_hrnetv2p-w32-1x_coco.py'\n# model settings\nmodel = dict(\n    backbone=dict(\n        extra=dict(\n            stage2=dict(num_channels=(18, 36)),\n            stage3=dict(num_channels=(18, 36, 72)),\n            stage4=dict(num_channels=(18, 36, 72, 144))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')),\n    neck=dict(type='HRFPN', in_channels=[18, 36, 72, 144], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/faster-rcnn_hrnetv2p-w18-2x_coco.py",
    "content": "_base_ = './faster-rcnn_hrnetv2p-w18-1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/faster-rcnn_hrnetv2p-w32-1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(\n        _delete_=True,\n        type='HRFPN',\n        in_channels=[32, 64, 128, 256],\n        out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/faster-rcnn_hrnetv2p-w32_2x_coco.py",
    "content": "_base_ = './faster-rcnn_hrnetv2p-w32-1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/faster-rcnn_hrnetv2p-w40-1x_coco.py",
    "content": "_base_ = './faster-rcnn_hrnetv2p-w32-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='HRNet',\n        extra=dict(\n            stage2=dict(num_channels=(40, 80)),\n            stage3=dict(num_channels=(40, 80, 160)),\n            stage4=dict(num_channels=(40, 80, 160, 320))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w40')),\n    neck=dict(type='HRFPN', in_channels=[40, 80, 160, 320], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/faster-rcnn_hrnetv2p-w40_2x_coco.py",
    "content": "_base_ = './faster-rcnn_hrnetv2p-w40-1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py",
    "content": "_base_ = './fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        extra=dict(\n            stage2=dict(num_channels=(18, 36)),\n            stage3=dict(num_channels=(18, 36, 72)),\n            stage4=dict(num_channels=(18, 36, 72, 144))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')),\n    neck=dict(type='HRFPN', in_channels=[18, 36, 72, 144], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/fcos_hrnetv2p-w18-gn-head_4xb4-2x_coco.py",
    "content": "_base_ = './fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/fcos_hrnetv2p-w18-gn-head_ms-640-800-4xb4-2x_coco.py",
    "content": "_base_ = './fcos_hrnetv2p-w32-gn-head_ms-640-800-4xb4-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        extra=dict(\n            stage2=dict(num_channels=(18, 36)),\n            stage3=dict(num_channels=(18, 36, 72)),\n            stage4=dict(num_channels=(18, 36, 72, 144))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')),\n    neck=dict(type='HRFPN', in_channels=[18, 36, 72, 144], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py",
    "content": "_base_ = '../fcos/fcos_r50-caffe_fpn_gn-head_4xb4-1x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(\n        _delete_=True,\n        type='HRFPN',\n        in_channels=[32, 64, 128, 256],\n        out_channels=256,\n        stride=2,\n        num_outs=5))\n"
  },
  {
    "path": "configs/hrnet/fcos_hrnetv2p-w32-gn-head_4xb4-2x_coco.py",
    "content": "_base_ = './fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/fcos_hrnetv2p-w32-gn-head_ms-640-800-4xb4-2x_coco.py",
    "content": "_base_ = './fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py'\n\nmodel = dict(\n    data_preprocessor=dict(\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/fcos_hrnetv2p-w40-gn-head_ms-640-800-4xb4-2x_coco.py",
    "content": "_base_ = './fcos_hrnetv2p-w32-gn-head_ms-640-800-4xb4-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='HRNet',\n        extra=dict(\n            stage2=dict(num_channels=(40, 80)),\n            stage3=dict(num_channels=(40, 80, 160)),\n            stage4=dict(num_channels=(40, 80, 160, 320))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w40')),\n    neck=dict(type='HRFPN', in_channels=[40, 80, 160, 320], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/htc_hrnetv2p-w18_20e_coco.py",
    "content": "_base_ = './htc_hrnetv2p-w32_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        extra=dict(\n            stage2=dict(num_channels=(18, 36)),\n            stage3=dict(num_channels=(18, 36, 72)),\n            stage4=dict(num_channels=(18, 36, 72, 144))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')),\n    neck=dict(type='HRFPN', in_channels=[18, 36, 72, 144], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/htc_hrnetv2p-w32_20e_coco.py",
    "content": "_base_ = '../htc/htc_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(\n        _delete_=True,\n        type='HRFPN',\n        in_channels=[32, 64, 128, 256],\n        out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/htc_hrnetv2p-w40_20e_coco.py",
    "content": "_base_ = './htc_hrnetv2p-w32_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='HRNet',\n        extra=dict(\n            stage2=dict(num_channels=(40, 80)),\n            stage3=dict(num_channels=(40, 80, 160)),\n            stage4=dict(num_channels=(40, 80, 160, 320))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w40')),\n    neck=dict(type='HRFPN', in_channels=[40, 80, 160, 320], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/htc_hrnetv2p-w40_28e_coco.py",
    "content": "_base_ = './htc_hrnetv2p-w40_20e_coco.py'\n\n# learning policy\nmax_epochs = 28\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[24, 27],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/htc_x101-64x4d_fpn_16xb1-28e_coco.py",
    "content": "_base_ = '../htc/htc_x101-64x4d_fpn_16xb1-20e_coco.py'\n\n# learning policy\nmax_epochs = 28\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[24, 27],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/mask-rcnn_hrnetv2p-w18-1x_coco.py",
    "content": "_base_ = './mask-rcnn_hrnetv2p-w32-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        extra=dict(\n            stage2=dict(num_channels=(18, 36)),\n            stage3=dict(num_channels=(18, 36, 72)),\n            stage4=dict(num_channels=(18, 36, 72, 144))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')),\n    neck=dict(type='HRFPN', in_channels=[18, 36, 72, 144], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/mask-rcnn_hrnetv2p-w18-2x_coco.py",
    "content": "_base_ = './mask-rcnn_hrnetv2p-w18-1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/mask-rcnn_hrnetv2p-w32-1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(\n        _delete_=True,\n        type='HRFPN',\n        in_channels=[32, 64, 128, 256],\n        out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/mask-rcnn_hrnetv2p-w32-2x_coco.py",
    "content": "_base_ = './mask-rcnn_hrnetv2p-w32-1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/mask-rcnn_hrnetv2p-w40-2x_coco.py",
    "content": "_base_ = './mask-rcnn_hrnetv2p-w40_1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/hrnet/mask-rcnn_hrnetv2p-w40_1x_coco.py",
    "content": "_base_ = './mask-rcnn_hrnetv2p-w18-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='HRNet',\n        extra=dict(\n            stage2=dict(num_channels=(40, 80)),\n            stage3=dict(num_channels=(40, 80, 160)),\n            stage4=dict(num_channels=(40, 80, 160, 320))),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w40')),\n    neck=dict(type='HRFPN', in_channels=[40, 80, 160, 320], out_channels=256))\n"
  },
  {
    "path": "configs/hrnet/metafile.yml",
    "content": "Models:\n  - Name: faster-rcnn_hrnetv2p-w18-1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/hrnet/faster-rcnn_hrnetv2p-w18-1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.6\n      inference time (ms/im):\n        - value: 74.63\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w18_1x_coco/faster_rcnn_hrnetv2p_w18_1x_coco_20200130-56651a6d.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: faster-rcnn_hrnetv2p-w18-2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/hrnet/faster-rcnn_hrnetv2p-w18-2x_coco.py\n    Metadata:\n      Training Memory (GB): 6.6\n      inference time (ms/im):\n        - value: 74.63\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w18_2x_coco/faster_rcnn_hrnetv2p_w18_2x_coco_20200702_085731-a4ec0611.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: faster-rcnn_hrnetv2p-w32-1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/hrnet/faster-rcnn_hrnetv2p-w32-1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.0\n      inference time (ms/im):\n        - value: 80.65\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w32_1x_coco/faster_rcnn_hrnetv2p_w32_1x_coco_20200130-6e286425.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: faster-rcnn_hrnetv2p-w32_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/hrnet/faster-rcnn_hrnetv2p-w32_2x_coco.py\n    Metadata:\n      Training Memory (GB): 9.0\n      inference time (ms/im):\n        - value: 80.65\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w32_2x_coco/faster_rcnn_hrnetv2p_w32_2x_coco_20200529_015927-976a9c15.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: faster-rcnn_hrnetv2p-w40-1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/hrnet/faster-rcnn_hrnetv2p-w40-1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.4\n      inference time (ms/im):\n        - value: 95.24\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w40_1x_coco/faster_rcnn_hrnetv2p_w40_1x_coco_20200210-95c1f5ce.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: faster-rcnn_hrnetv2p-w40_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/hrnet/faster-rcnn_hrnetv2p-w40_2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.4\n      inference time (ms/im):\n        - value: 95.24\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/faster_rcnn_hrnetv2p_w40_2x_coco/faster_rcnn_hrnetv2p_w40_2x_coco_20200512_161033-0f236ef4.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: mask-rcnn_hrnetv2p-w18-1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/hrnet/mask-rcnn_hrnetv2p-w18-1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 85.47\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 34.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w18_1x_coco/mask_rcnn_hrnetv2p_w18_1x_coco_20200205-1c3d78ed.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: mask-rcnn_hrnetv2p-w18-2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/hrnet/mask-rcnn_hrnetv2p-w18-2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 85.47\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w18_2x_coco/mask_rcnn_hrnetv2p_w18_2x_coco_20200212-b3c825b1.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: mask-rcnn_hrnetv2p-w32-1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/hrnet/mask-rcnn_hrnetv2p-w32-1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.4\n      inference time (ms/im):\n        - value: 88.5\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w32_1x_coco/mask_rcnn_hrnetv2p_w32_1x_coco_20200207-b29f616e.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: mask-rcnn_hrnetv2p-w32-2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/hrnet/mask-rcnn_hrnetv2p-w32-2x_coco.py\n    Metadata:\n      Training Memory (GB): 9.4\n      inference time (ms/im):\n        - value: 88.5\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w32_2x_coco/mask_rcnn_hrnetv2p_w32_2x_coco_20200213-45b75b4d.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: mask-rcnn_hrnetv2p-w40_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/hrnet/mask-rcnn_hrnetv2p-w40_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.9\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w40_1x_coco/mask_rcnn_hrnetv2p_w40_1x_coco_20200511_015646-66738b35.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: mask-rcnn_hrnetv2p-w40-2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/hrnet/mask-rcnn_hrnetv2p-w40-2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.9\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/mask_rcnn_hrnetv2p_w40_2x_coco/mask_rcnn_hrnetv2p_w40_2x_coco_20200512_163732-aed5e4ab.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: cascade-rcnn_hrnetv2p-w18-20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/hrnet/cascade-rcnn_hrnetv2p-w18-20e_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 90.91\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w18_20e_coco/cascade_rcnn_hrnetv2p_w18_20e_coco_20200210-434be9d7.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: cascade-rcnn_hrnetv2p-w32-20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/hrnet/cascade-rcnn_hrnetv2p-w32-20e_coco.py\n    Metadata:\n      Training Memory (GB): 9.4\n      inference time (ms/im):\n        - value: 90.91\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w32_20e_coco/cascade_rcnn_hrnetv2p_w32_20e_coco_20200208-928455a4.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: cascade-rcnn_hrnetv2p-w40-20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/hrnet/cascade-rcnn_hrnetv2p-w40-20e_coco.py\n    Metadata:\n      Training Memory (GB): 10.8\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_rcnn_hrnetv2p_w40_20e_coco/cascade_rcnn_hrnetv2p_w40_20e_coco_20200512_161112-75e47b04.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: cascade-mask-rcnn_hrnetv2p-w18_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/hrnet/cascade-mask-rcnn_hrnetv2p-w18_20e_coco.py\n    Metadata:\n      Training Memory (GB): 8.5\n      inference time (ms/im):\n        - value: 117.65\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w18_20e_coco/cascade_mask_rcnn_hrnetv2p_w18_20e_coco_20200210-b543cd2b.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: cascade-mask-rcnn_hrnetv2p-w32_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/hrnet/cascade-mask-rcnn_hrnetv2p-w32_20e_coco.py\n    Metadata:\n      inference time (ms/im):\n        - value: 120.48\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w32_20e_coco/cascade_mask_rcnn_hrnetv2p_w32_20e_coco_20200512_154043-39d9cf7b.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: cascade-mask-rcnn_hrnetv2p-w40-20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/hrnet/cascade-mask-rcnn_hrnetv2p-w40-20e_coco.py\n    Metadata:\n      Training Memory (GB): 12.5\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/cascade_mask_rcnn_hrnetv2p_w40_20e_coco/cascade_mask_rcnn_hrnetv2p_w40_20e_coco_20200527_204922-969c4610.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: htc_hrnetv2p-w18_20e_coco\n    In Collection: HTC\n    Config: configs/hrnet/htc_hrnetv2p-w18_20e_coco.py\n    Metadata:\n      Training Memory (GB): 10.8\n      inference time (ms/im):\n        - value: 212.77\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w18_20e_coco/htc_hrnetv2p_w18_20e_coco_20200210-b266988c.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: htc_hrnetv2p-w32_20e_coco\n    In Collection: HTC\n    Config: configs/hrnet/htc_hrnetv2p-w32_20e_coco.py\n    Metadata:\n      Training Memory (GB): 13.1\n      inference time (ms/im):\n        - value: 204.08\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w32_20e_coco/htc_hrnetv2p_w32_20e_coco_20200207-7639fa12.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: htc_hrnetv2p-w40_20e_coco\n    In Collection: HTC\n    Config: configs/hrnet/htc_hrnetv2p-w40_20e_coco.py\n    Metadata:\n      Training Memory (GB): 14.6\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/htc_hrnetv2p_w40_20e_coco/htc_hrnetv2p_w40_20e_coco_20200529_183411-417c4d5b.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco\n    In Collection: FCOS\n    Config: configs/hrnet/fcos_hrnetv2p-w18-gn-head_4xb4-1x_coco.py\n    Metadata:\n      Training Resources: 4x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 13.0\n      inference time (ms/im):\n        - value: 77.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 35.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_4x4_1x_coco/fcos_hrnetv2p_w18_gn-head_4x4_1x_coco_20201212_100710-4ad151de.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: fcos_hrnetv2p-w18-gn-head_4xb4-2x_coco\n    In Collection: FCOS\n    Config: configs/hrnet/fcos_hrnetv2p-w18-gn-head_4xb4-2x_coco.py\n    Metadata:\n      Training Resources: 4x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 13.0\n      inference time (ms/im):\n        - value: 77.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_4x4_2x_coco/fcos_hrnetv2p_w18_gn-head_4x4_2x_coco_20201212_101110-5c575fa5.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco\n    In Collection: FCOS\n    Config: configs/hrnet/fcos_hrnetv2p-w32-gn-head_4xb4-1x_coco.py\n    Metadata:\n      Training Resources: 4x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 17.5\n      inference time (ms/im):\n        - value: 77.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_4x4_1x_coco/fcos_hrnetv2p_w32_gn-head_4x4_1x_coco_20201211_134730-cb8055c0.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: fcos_hrnetv2p-w32-gn-head_4xb4-2x_coco\n    In Collection: FCOS\n    Config: configs/hrnet/fcos_hrnetv2p-w32-gn-head_4xb4-2x_coco.py\n    Metadata:\n      Training Resources: 4x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 17.5\n      inference time (ms/im):\n        - value: 77.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_4x4_2x_coco/fcos_hrnetv2p_w32_gn-head_4x4_2x_coco_20201212_112133-77b6b9bb.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: fcos_hrnetv2p-w18-gn-head_ms-640-800-4xb4-2x_coco\n    In Collection: FCOS\n    Config: configs/hrnet/fcos_hrnetv2p-w18-gn-head_ms-640-800-4xb4-2x_coco.py\n    Metadata:\n      Training Resources: 4x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 13.0\n      inference time (ms/im):\n        - value: 77.52\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w18_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w18_gn-head_mstrain_640-800_4x4_2x_coco_20201212_111651-441e9d9f.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: fcos_hrnetv2p-w32-gn-head_ms-640-800-4xb4-2x_coco\n    In Collection: FCOS\n    Config: configs/hrnet/fcos_hrnetv2p-w32-gn-head_ms-640-800-4xb4-2x_coco.py\n    Metadata:\n      Training Resources: 4x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 17.5\n      inference time (ms/im):\n        - value: 80.65\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w32_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w32_gn-head_mstrain_640-800_4x4_2x_coco_20201212_090846-b6f2b49f.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n\n  - Name: fcos_hrnetv2p-w40-gn-head_ms-640-800-4xb4-2x_coco\n    In Collection: FCOS\n    Config: configs/hrnet/fcos_hrnetv2p-w40-gn-head_ms-640-800-4xb4-2x_coco.py\n    Metadata:\n      Training Resources: 4x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 20.3\n      inference time (ms/im):\n        - value: 92.59\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Architecture:\n        - HRNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/hrnet/fcos_hrnetv2p_w40_gn-head_mstrain_640-800_4x4_2x_coco/fcos_hrnetv2p_w40_gn-head_mstrain_640-800_4x4_2x_coco_20201212_124752-f22d2ce5.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.04514\n      Title: 'Deep High-Resolution Representation Learning for Visual Recognition'\n    README: configs/hrnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/backbones/hrnet.py#L195\n      Version: v2.0.0\n"
  },
  {
    "path": "configs/htc/README.md",
    "content": "# HTC\n\n> [Hybrid Task Cascade for Instance Segmentation](https://arxiv.org/abs/1901.07518)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nCascade is a classic yet powerful architecture that has boosted performance on various tasks. However, how to introduce cascade to instance segmentation remains an open question. A simple combination of Cascade R-CNN and Mask R-CNN only brings limited gain. In exploring a more effective approach, we find that the key to a successful instance segmentation cascade is to fully leverage the reciprocal relationship between detection and segmentation. In this work, we propose a new framework, Hybrid Task Cascade (HTC), which differs in two important aspects: (1) instead of performing cascaded refinement on these two tasks separately, it interweaves them for a joint multi-stage processing; (2) it adopts a fully convolutional branch to provide spatial context, which can help distinguishing hard foreground from cluttered background. Overall, this framework can learn more discriminative features progressively while integrating complementary features together in each stage. Without bells and whistles, a single HTC obtains 38.4 and 1.5 improvement over a strong Cascade Mask R-CNN baseline on MSCOCO dataset. Moreover, our overall system achieves 48.6 mask AP on the test-challenge split, ranking 1st in the COCO 2018 Challenge Object Detection Task.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143893906-e05acaa6-d46f-4c11-84e8-bb9940a95b44.png\"/>\n</div>\n\n## Introduction\n\nHTC requires COCO and [COCO-stuff](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip) dataset for training. You need to download and extract it in the COCO dataset path.\nThe directory should be like this.\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n|   |   ├── stuffthingmaps\n```\n\n## Results and Models\n\nThe results on COCO 2017val are shown in the below table. (results on test-dev are usually slightly higher than val)\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                      Config                      |                                                                                                                                                   Download                                                                                                                                                    |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :----------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |   1x    |   8.2    |      5.8       |  42.3  |  37.4   |        [config](./htc_r50_fpn_1x_coco.py)        |                           [model](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r50_fpn_1x_coco/htc_r50_fpn_1x_coco_20200317-7332cf16.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r50_fpn_1x_coco/htc_r50_fpn_1x_coco_20200317_070435.log.json)                           |\n|    R-50-FPN     | pytorch |   20e   |   8.2    |       -        |  43.3  |  38.3   |       [config](./htc_r50_fpn_20e_coco.py)        |                         [model](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r50_fpn_20e_coco/htc_r50_fpn_20e_coco_20200319-fe28c577.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r50_fpn_20e_coco/htc_r50_fpn_20e_coco_20200319_070313.log.json)                         |\n|    R-101-FPN    | pytorch |   20e   |   10.2   |      5.5       |  44.8  |  39.6   |       [config](./htc_r101_fpn_20e_coco.py)       |                       [model](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r101_fpn_20e_coco/htc_r101_fpn_20e_coco_20200317-9b41b48f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r101_fpn_20e_coco/htc_r101_fpn_20e_coco_20200317_153107.log.json)                       |\n| X-101-32x4d-FPN | pytorch |   20e   |   11.4   |      5.0       |  46.1  |  40.5   | [config](./htc_x101-32x4d_fpn_16xb1-20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_32x4d_fpn_16x1_20e_coco/htc_x101_32x4d_fpn_16x1_20e_coco_20200318-de97ae01.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_32x4d_fpn_16x1_20e_coco/htc_x101_32x4d_fpn_16x1_20e_coco_20200318_034519.log.json) |\n| X-101-64x4d-FPN | pytorch |   20e   |   14.5   |      4.4       |  47.0  |  41.4   | [config](./htc_x101-64x4d_fpn_16xb1-20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_64x4d_fpn_16x1_20e_coco/htc_x101_64x4d_fpn_16x1_20e_coco_20200318-b181fd7a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_64x4d_fpn_16x1_20e_coco/htc_x101_64x4d_fpn_16x1_20e_coco_20200318_081711.log.json) |\n\n- In the HTC paper and COCO 2018 Challenge, `score_thr` is set to 0.001 for both baselines and HTC.\n- We use 8 GPUs with 2 images/GPU for R-50 and R-101 models, and 16 GPUs with 1 image/GPU for X-101 models.\n  If you would like to train X-101 HTC with 8 GPUs, you need to change the lr from 0.02 to 0.01.\n\nWe also provide a powerful HTC with DCN and multi-scale training model. No testing augmentation is used.\n\n|    Backbone     |  Style  |  DCN  | training scales | Lr schd | box AP | mask AP |                                  Config                                  |                                                                                                                                                                                                             Download                                                                                                                                                                                                              |\n| :-------------: | :-----: | :---: | :-------------: | :-----: | :----: | :-----: | :----------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| X-101-64x4d-FPN | pytorch | c3-c5 |    400~1400     |   20e   |  50.4  |  43.8   | [config](./htc_x101-64x4d-dconv-c3-c5_fpn_ms-400-1400-16xb1-20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_64x4d_fpn_dconv_c3-c5_mstrain_400_1400_16x1_20e_coco/htc_x101_64x4d_fpn_dconv_c3-c5_mstrain_400_1400_16x1_20e_coco_20200312-946fd751.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_64x4d_fpn_dconv_c3-c5_mstrain_400_1400_16x1_20e_coco/htc_x101_64x4d_fpn_dconv_c3-c5_mstrain_400_1400_16x1_20e_coco_20200312_203410.log.json) |\n\n## Citation\n\nWe provide config files to reproduce the results in the CVPR 2019 paper for [Hybrid Task Cascade](https://arxiv.org/abs/1901.07518).\n\n```latex\n@inproceedings{chen2019hybrid,\n  title={Hybrid task cascade for instance segmentation},\n  author={Chen, Kai and Pang, Jiangmiao and Wang, Jiaqi and Xiong, Yu and Li, Xiaoxiao and Sun, Shuyang and Feng, Wansen and Liu, Ziwei and Shi, Jianping and Ouyang, Wanli and Chen Change Loy and Dahua Lin},\n  booktitle={IEEE Conference on Computer Vision and Pattern Recognition},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/htc/htc-without-semantic_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    type='HybridTaskCascade',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        type='HybridTaskCascadeRoIHead',\n        interleaved=True,\n        mask_info_flow=True,\n        num_stages=3,\n        stage_loss_weights=[1, 0.5, 0.25],\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        mask_head=[\n            dict(\n                type='HTCMaskHead',\n                with_conv_res=False,\n                num_convs=4,\n                in_channels=256,\n                conv_out_channels=256,\n                num_classes=80,\n                loss_mask=dict(\n                    type='CrossEntropyLoss', use_mask=True, loss_weight=1.0)),\n            dict(\n                type='HTCMaskHead',\n                num_convs=4,\n                in_channels=256,\n                conv_out_channels=256,\n                num_classes=80,\n                loss_mask=dict(\n                    type='CrossEntropyLoss', use_mask=True, loss_weight=1.0)),\n            dict(\n                type='HTCMaskHead',\n                num_convs=4,\n                in_channels=256,\n                conv_out_channels=256,\n                num_classes=80,\n                loss_mask=dict(\n                    type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))\n        ]),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.5,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.001,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)))\n"
  },
  {
    "path": "configs/htc/htc_r101_fpn_20e_coco.py",
    "content": "_base_ = './htc_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/htc/htc_r50_fpn_1x_coco.py",
    "content": "_base_ = './htc-without-semantic_r50_fpn_1x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(pad_seg=True),\n    roi_head=dict(\n        semantic_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[8]),\n        semantic_head=dict(\n            type='FusedSemanticHead',\n            num_ins=5,\n            fusion_level=1,\n            seg_scale_factor=1 / 8,\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=183,\n            loss_seg=dict(\n                type='CrossEntropyLoss', ignore_index=255, loss_weight=0.2))))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations', with_bbox=True, with_mask=True, with_seg=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        data_prefix=dict(img='train2017/', seg='stuffthingmaps/train2017/'),\n        pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/htc/htc_r50_fpn_20e_coco.py",
    "content": "_base_ = './htc_r50_fpn_1x_coco.py'\n\n# learning policy\nmax_epochs = 20\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 19],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/htc/htc_x101-32x4d_fpn_16xb1-20e_coco.py",
    "content": "_base_ = './htc_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n\ntrain_dataloader = dict(batch_size=1, num_workers=1)\n\n# learning policy\nmax_epochs = 20\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 19],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/htc/htc_x101-64x4d-dconv-c3-c5_fpn_ms-400-1400-16xb1-20e_coco.py",
    "content": "_base_ = './htc_x101-64x4d_fpn_16xb1-20e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(\n        type='LoadAnnotations', with_bbox=True, with_mask=True, with_seg=True),\n    dict(\n        type='RandomResize',\n        scale=[(1600, 400), (1600, 1400)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/htc/htc_x101-64x4d_fpn_16xb1-20e_coco.py",
    "content": "_base_ = './htc_x101-32x4d_fpn_16xb1-20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        groups=64,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/htc/metafile.yml",
    "content": "Collections:\n  - Name: HTC\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - HTC\n        - RPN\n        - ResNet\n        - ResNeXt\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/abs/1901.07518\n      Title: 'Hybrid Task Cascade for Instance Segmentation'\n    README: configs/htc/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/htc.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: htc_r50_fpn_1x_coco\n    In Collection: HTC\n    Config: configs/htc/htc_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.2\n      inference time (ms/im):\n        - value: 172.41\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r50_fpn_1x_coco/htc_r50_fpn_1x_coco_20200317-7332cf16.pth\n\n  - Name: htc_r50_fpn_20e_coco\n    In Collection: HTC\n    Config: configs/htc/htc_r50_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 8.2\n      inference time (ms/im):\n        - value: 172.41\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r50_fpn_20e_coco/htc_r50_fpn_20e_coco_20200319-fe28c577.pth\n\n  - Name: htc_r101_fpn_20e_coco\n    In Collection: HTC\n    Config: configs/htc/htc_r101_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 10.2\n      inference time (ms/im):\n        - value: 181.82\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/htc/htc_r101_fpn_20e_coco/htc_r101_fpn_20e_coco_20200317-9b41b48f.pth\n\n  - Name: htc_x101-32x4d_fpn_16xb1-20e_coco\n    In Collection: HTC\n    Config: configs/htc/htc_x101-32x4d_fpn_16xb1-20e_coco.py\n    Metadata:\n      Training Resources: 16x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 11.4\n      inference time (ms/im):\n        - value: 200\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_32x4d_fpn_16x1_20e_coco/htc_x101_32x4d_fpn_16x1_20e_coco_20200318-de97ae01.pth\n\n  - Name: htc_x101-64x4d_fpn_16xb1-20e_coco\n    In Collection: HTC\n    Config: configs/htc/htc_x101-64x4d_fpn_16xb1-20e_coco.py\n    Metadata:\n      Training Resources: 16x V100 GPUs\n      Batch Size: 16\n      Training Memory (GB): 14.5\n      inference time (ms/im):\n        - value: 227.27\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_64x4d_fpn_16x1_20e_coco/htc_x101_64x4d_fpn_16x1_20e_coco_20200318-b181fd7a.pth\n\n  - Name: htc_x101-64x4d-dconv-c3-c5_fpn_ms-400-1400-16xb1-20e_coco\n    In Collection: HTC\n    Config: configs/htc/htc_x101-64x4d-dconv-c3-c5_fpn_ms-400-1400-16xb1-20e_coco.py\n    Metadata:\n      Training Resources: 16x V100 GPUs\n      Batch Size: 16\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 43.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/htc/htc_x101_64x4d_fpn_dconv_c3-c5_mstrain_400_1400_16x1_20e_coco/htc_x101_64x4d_fpn_dconv_c3-c5_mstrain_400_1400_16x1_20e_coco_20200312-946fd751.pth\n"
  },
  {
    "path": "configs/instaboost/README.md",
    "content": "# Instaboost\n\n> [Instaboost: Boosting instance segmentation via probability map guided copy-pasting](https://arxiv.org/abs/1908.07801)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nInstance segmentation requires a large number of training samples to achieve satisfactory performance and benefits from proper data augmentation. To enlarge the training set and increase the diversity, previous methods have investigated using data annotation from other domain (e.g. bbox, point) in a weakly supervised mechanism. In this paper, we present a simple, efficient and effective method to augment the training set using the existing instance mask annotations. Exploiting the pixel redundancy of the background, we are able to improve the performance of Mask R-CNN for 1.7 mAP on COCO dataset and 3.3 mAP on Pascal VOC dataset by simply introducing random jittering to objects. Furthermore, we propose a location probability map based approach to explore the feasible locations that objects can be placed based on local appearance similarity. With the guidance of such map, we boost the performance of R101-Mask R-CNN on instance segmentation from 35.7 mAP to 37.9 mAP without modifying the backbone or network structure. Our method is simple to implement and does not increase the computational complexity. It can be integrated into the training pipeline of any instance segmentation model without affecting the training and inference efficiency.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143894053-ecfe8406-1a05-461b-953c-efeef22d7a60.png\" height=\"300\"/>\n</div>\n\n## Introduction\n\nConfigs in this directory is the implementation for ICCV2019 paper \"InstaBoost: Boosting Instance Segmentation Via Probability Map Guided Copy-Pasting\" and provided by the authors of the paper. InstaBoost is a data augmentation method for object detection and instance segmentation. The paper has been released on [`arXiv`](https://arxiv.org/abs/1908.07801).\n\n## Usage\n\n### Requirements\n\nYou need to install `instaboostfast` before using it.\n\n```shell\npip install instaboostfast\n```\n\nThe code and more details can be found [here](https://github.com/GothicAi/Instaboost).\n\n### Integration with MMDetection\n\nInstaBoost have been already integrated in the data pipeline, thus all you need is to add or change **InstaBoost** configurations after **LoadImageFromFile**. We have provided examples like [this](mask_rcnn_r50_fpn_instaboost_4x#L121). You can refer to [`InstaBoostConfig`](https://github.com/GothicAi/InstaBoost-pypi#instaboostconfig) for more details.\n\n## Results and Models\n\n- All models were trained on `coco_2017_train` and tested on `coco_2017_val` for convenience of evaluation and comparison. In the paper, the results are obtained from `test-dev`.\n- To balance accuracy and training time when using InstaBoost, models released in this page are all trained for 48 Epochs. Other training and testing configs strictly follow the original framework.\n- For results and models in MMDetection V1.x, please refer to [Instaboost](https://github.com/GothicAi/Instaboost).\n\n|    Network    |    Backbone     | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                           Config                            |                                                                                                                                                                                    Download                                                                                                                                                                                    |\n| :-----------: | :-------------: | :-----: | :------: | :------------: | :----: | :-----: | :---------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  Mask R-CNN   |    R-50-FPN     |   4x    |   4.4    |      17.5      |  40.6  |  36.6   |     [config](./mask-rcnn_r50_fpn_instaboost-4x_coco.py)     |                  [model](https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_r50_fpn_instaboost_4x_coco/mask_rcnn_r50_fpn_instaboost_4x_coco_20200307-d025f83a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_r50_fpn_instaboost_4x_coco/mask_rcnn_r50_fpn_instaboost_4x_coco_20200307_223635.log.json)                   |\n|  Mask R-CNN   |    R-101-FPN    |   4x    |   6.4    |                |  42.5  |  38.0   |    [config](./mask-rcnn_r101_fpn_instaboost-4x_coco.py)     |             [model](https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_r101_fpn_instaboost_4x_coco/mask_rcnn_r101_fpn_instaboost_4x_coco_20200703_235738-f23f3a5f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_r101_fpn_instaboost_4x_coco/mask_rcnn_r101_fpn_instaboost_4x_coco_20200703_235738.log.json)             |\n|  Mask R-CNN   | X-101-64x4d-FPN |   4x    |   10.7   |                |  44.7  |  39.7   | [config](./mask-rcnn_x101-64x4d_fpn_instaboost-4x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_x101_64x4d_fpn_instaboost_4x_coco/mask_rcnn_x101_64x4d_fpn_instaboost_4x_coco_20200515_080947-8ed58c1b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_x101_64x4d_fpn_instaboost_4x_coco/mask_rcnn_x101_64x4d_fpn_instaboost_4x_coco_20200515_080947.log.json) |\n| Cascade R-CNN |    R-101-FPN    |   4x    |   6.0    |      12.0      |  43.7  |  38.0   | [config](./cascade-mask-rcnn_r50_fpn_instaboost-4x_coco.py) |  [model](https://download.openmmlab.com/mmdetection/v2.0/instaboost/cascade_mask_rcnn_r50_fpn_instaboost_4x_coco/cascade_mask_rcnn_r50_fpn_instaboost_4x_coco_20200307-c19d98d9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/instaboost/cascade_mask_rcnn_r50_fpn_instaboost_4x_coco/cascade_mask_rcnn_r50_fpn_instaboost_4x_coco_20200307_223646.log.json)   |\n\n## Citation\n\n```latex\n@inproceedings{fang2019instaboost,\n  title={Instaboost: Boosting instance segmentation via probability map guided copy-pasting},\n  author={Fang, Hao-Shu and Sun, Jianhua and Wang, Runzhong and Gou, Minghao and Li, Yong-Lu and Lu, Cewu},\n  booktitle={Proceedings of the IEEE International Conference on Computer Vision},\n  pages={682--691},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/instaboost/cascade-mask-rcnn_r101_fpn_instaboost-4x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_instaboost-4x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/instaboost/cascade-mask-rcnn_r50_fpn_instaboost-4x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='InstaBoost',\n        action_candidate=('normal', 'horizontal', 'skip'),\n        action_prob=(1, 0, 0),\n        scale=(0.8, 1.2),\n        dx=15,\n        dy=15,\n        theta=(-1, 1),\n        color_prob=0.5,\n        hflag=False,\n        aug_ratio=0.5),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\nmax_epochs = 48\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[32, 44],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# only keep latest 3 checkpoints\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=3))\n"
  },
  {
    "path": "configs/instaboost/cascade-mask-rcnn_x101-64x4d_fpn_instaboost-4x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_r50_fpn_instaboost-4x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/instaboost/mask-rcnn_r101_fpn_instaboost-4x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_instaboost-4x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='InstaBoost',\n        action_candidate=('normal', 'horizontal', 'skip'),\n        action_prob=(1, 0, 0),\n        scale=(0.8, 1.2),\n        dx=15,\n        dy=15,\n        theta=(-1, 1),\n        color_prob=0.5,\n        hflag=False,\n        aug_ratio=0.5),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\nmax_epochs = 48\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[32, 44],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# only keep latest 3 checkpoints\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=3))\n"
  },
  {
    "path": "configs/instaboost/mask-rcnn_x101-64x4d_fpn_instaboost-4x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_instaboost-4x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/instaboost/metafile.yml",
    "content": "Collections:\n  - Name: InstaBoost\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - InstaBoost\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n    Paper:\n      URL: https://arxiv.org/abs/1908.07801\n      Title: 'Instaboost: Boosting instance segmentation via probability map guided copy-pasting'\n    README: configs/instaboost/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/datasets/pipelines/instaboost.py#L7\n      Version: v2.0.0\n\nModels:\n  - Name: mask-rcnn_r50_fpn_instaboost_4x_coco\n    In Collection: InstaBoost\n    Config: configs/instaboost/mask-rcnn_r50_fpn_instaboost-4x_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      inference time (ms/im):\n        - value: 57.14\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 48\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_r50_fpn_instaboost_4x_coco/mask_rcnn_r50_fpn_instaboost_4x_coco_20200307-d025f83a.pth\n\n  - Name: mask-rcnn_r101_fpn_instaboost-4x_coco\n    In Collection: InstaBoost\n    Config: configs/instaboost/mask-rcnn_r101_fpn_instaboost-4x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      Epochs: 48\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_r101_fpn_instaboost_4x_coco/mask_rcnn_r101_fpn_instaboost_4x_coco_20200703_235738-f23f3a5f.pth\n\n  - Name: mask-rcnn_x101-64x4d_fpn_instaboost-4x_coco\n    In Collection: InstaBoost\n    Config: configs/instaboost/mask-rcnn_x101-64x4d_fpn_instaboost-4x_coco.py\n    Metadata:\n      Training Memory (GB): 10.7\n      Epochs: 48\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/instaboost/mask_rcnn_x101_64x4d_fpn_instaboost_4x_coco/mask_rcnn_x101_64x4d_fpn_instaboost_4x_coco_20200515_080947-8ed58c1b.pth\n\n  - Name: cascade-mask-rcnn_r50_fpn_instaboost_4x_coco\n    In Collection: InstaBoost\n    Config: configs/instaboost/cascade-mask-rcnn_r50_fpn_instaboost-4x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      inference time (ms/im):\n        - value: 83.33\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 48\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/instaboost/cascade_mask_rcnn_r50_fpn_instaboost_4x_coco/cascade_mask_rcnn_r50_fpn_instaboost_4x_coco_20200307-c19d98d9.pth\n"
  },
  {
    "path": "configs/lad/README.md",
    "content": "# LAD\n\n> [Improving Object Detection by Label Assignment Distillation](https://arxiv.org/abs/2108.10520)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nLabel assignment in object detection aims to assign targets, foreground or background, to sampled regions in an image. Unlike labeling for image classification, this problem is not well defined due to the object's bounding box. In this paper, we investigate the problem from a perspective of distillation, hence we call Label Assignment Distillation (LAD). Our initial motivation is very simple, we use a teacher network to generate labels for the student. This can be achieved in two ways: either using the teacher's prediction as the direct targets (soft label), or through the hard labels dynamically assigned by the teacher (LAD). Our experiments reveal that: (i) LAD is more effective than soft-label, but they are complementary. (ii) Using LAD, a smaller teacher can also improve a larger student significantly, while soft-label can't. We then introduce Co-learning LAD, in which two networks simultaneously learn from scratch and the role of teacher and student are dynamically interchanged. Using PAA-ResNet50 as a teacher, our LAD techniques can improve detectors PAA-ResNet101 and PAA-ResNeXt101 to 46AP and 47.5AP on the COCO test-dev set. With a stronger teacher PAA-SwinB, we improve the students PAA-ResNet50 to 43.7AP by only 1x schedule training and standard setting, and PAA-ResNet101 to 47.9AP, significantly surpassing the current methods.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143894499-c2a3a243-988f-4604-915b-17918732bf03.png\"/>\n</div>\n\n## Results and Models\n\nWe provide config files to reproduce the object detection results in the\nWACV 2022 paper for Improving Object Detection by Label Assignment\nDistillation.\n\n### PAA with LAD\n\n| Teacher | Student | Training schedule | AP (val) |                      Config                      |                                                                                                                                               Download                                                                                                                                               |\n| :-----: | :-----: | :---------------: | :------: | :----------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   --    |  R-50   |        1x         |   40.4   |     [config](../paa/paa_r50_fpn_1x_coco.py)      |                     [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1x_coco/paa_r50_fpn_1x_coco_20200821-936edec3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1x_coco/paa_r50_fpn_1x_coco_20200821-936edec3.log.json)                      |\n|   --    |  R-101  |        1x         |   42.6   |     [config](../paa/paa_r101_fpn_1x_coco.py)     |                   [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_1x_coco/paa_r101_fpn_1x_coco_20200821-0a1825a4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_1x_coco/paa_r101_fpn_1x_coco_20200821-0a1825a4.log.json)                    |\n|  R-101  |  R-50   |        1x         |   41.4   | [config](./lad_r50-paa-r101_fpn_2xb8_coco_1x.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/lad/lad_r50_paa_r101_fpn_coco_1x/lad_r50_paa_r101_fpn_coco_1x_20220708_124246-74c76ff0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lad/lad_r50_paa_r101_fpn_coco_1x/lad_r50_paa_r101_fpn_coco_1x_20220708_124246.log.json) |\n|  R-50   |  R-101  |        1x         |   43.2   | [config](./lad_r101-paa-r50_fpn_2xb8_coco_1x.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/lad/lad_r101_paa_r50_fpn_coco_1x/lad_r101_paa_r50_fpn_coco_1x_20220708_124357-9407ac54.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lad/lad_r101_paa_r50_fpn_coco_1x/lad_r101_paa_r50_fpn_coco_1x_20220708_124357.log.json) |\n\n## Note\n\n- Meaning of Config name: lad_r50(student model)\\_paa(based on paa)\\_r101(teacher model)\\_fpn(neck)\\_coco(dataset)\\_1x(12 epoch).py\n- Results may fluctuate by about 0.2 mAP.\n- 2 GPUs are used, 8 samples per GPU.\n\n## Citation\n\n```latex\n@inproceedings{nguyen2021improving,\n  title={Improving Object Detection by Label Assignment Distillation},\n  author={Chuong H. Nguyen and Thuy C. Nguyen and Tuan N. Tang and Nam L. H. Phan},\n  booktitle = {WACV},\n  year={2022}\n}\n```\n"
  },
  {
    "path": "configs/lad/lad_r101-paa-r50_fpn_2xb8_coco_1x.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nteacher_ckpt = 'https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1x_coco/paa_r50_fpn_1x_coco_20200821-936edec3.pth'  # noqa\n\nmodel = dict(\n    type='LAD',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    # student\n    backbone=dict(\n        type='ResNet',\n        depth=101,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='LADHead',\n        reg_decoded_bbox=True,\n        score_voting=True,\n        topk=9,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5)),\n    # teacher\n    teacher_ckpt=teacher_ckpt,\n    teacher_backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch'),\n    teacher_neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    teacher_bbox_head=dict(\n        type='LADHead',\n        reg_decoded_bbox=True,\n        score_voting=True,\n        topk=9,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.1,\n            neg_iou_thr=0.1,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        score_voting=True,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\ntrain_dataloader = dict(batch_size=8, num_workers=4)\noptim_wrapper = dict(type='AmpOptimWrapper', optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/lad/lad_r50-paa-r101_fpn_2xb8_coco_1x.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nteacher_ckpt = 'http://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_1x_coco/paa_r101_fpn_1x_coco_20200821-0a1825a4.pth'  # noqa\n\nmodel = dict(\n    type='LAD',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    # student\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='LADHead',\n        reg_decoded_bbox=True,\n        score_voting=True,\n        topk=9,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5)),\n    # teacher\n    teacher_ckpt=teacher_ckpt,\n    teacher_backbone=dict(\n        type='ResNet',\n        depth=101,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch'),\n    teacher_neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    teacher_bbox_head=dict(\n        type='LADHead',\n        reg_decoded_bbox=True,\n        score_voting=True,\n        topk=9,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.1,\n            neg_iou_thr=0.1,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        score_voting=True,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\ntrain_dataloader = dict(batch_size=8, num_workers=4)\noptim_wrapper = dict(type='AmpOptimWrapper', optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/lad/metafile.yml",
    "content": "Collections:\n  - Name: Label Assignment Distillation\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - Label Assignment Distillation\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 2x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/2108.10520\n      Title: 'Improving Object Detection by Label Assignment Distillation'\n    README: configs/lad/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.19.0/mmdet/models/detectors/lad.py#L10\n      Version: v2.19.0\n\nModels:\n  - Name: lad_r101-paa-r50_fpn_2xb8_coco_1x\n    In Collection: Label Assignment Distillation\n    Config: configs/lad/lad_r101-paa-r50_fpn_2xb8_coco_1x.py\n    Metadata:\n      Training Memory (GB): 12.4\n      Epochs: 12\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 43.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lad/lad_r101_paa_r50_fpn_coco_1x/lad_r101_paa_r50_fpn_coco_1x_20220708_124357-9407ac54.pth\n  - Name: lad_r50-paa-r101_fpn_2xb8_coco_1x\n    In Collection: Label Assignment Distillation\n    Config: configs/lad/lad_r50-paa-r101_fpn_2xb8_coco_1x.py\n    Metadata:\n      Training Memory (GB): 8.9\n      Epochs: 12\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 41.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lad/lad_r50_paa_r101_fpn_coco_1x/lad_r50_paa_r101_fpn_coco_1x_20220708_124246-74c76ff0.pth\n"
  },
  {
    "path": "configs/ld/README.md",
    "content": "# LD\n\n> [Localization Distillation for Dense Object Detection](https://arxiv.org/abs/2102.12252)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nKnowledge distillation (KD) has witnessed its powerful capability in learning compact models in object detection. Previous KD methods for object detection mostly focus on imitating deep features within the imitation regions instead of mimicking classification logits due to its inefficiency in distilling localization information. In this paper, by reformulating the knowledge distillation process on localization, we present a novel localization distillation (LD) method which can efficiently transfer the localization knowledge from the teacher to the student. Moreover, we also heuristically introduce the concept of valuable localization region that can aid to selectively distill the semantic and localization knowledge for a certain region. Combining these two new components, for the first time, we show that logit mimicking can outperform feature imitation and localization knowledge distillation is more important and efficient than semantic knowledge for distilling object detectors. Our distillation scheme is simple as well as effective and can be easily applied to different dense object detectors. Experiments show that our LD can boost the AP score of GFocal-ResNet-50 with a single-scale 1× training schedule from 40.1 to 42.1 on the COCO benchmark without any sacrifice on the inference speed.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143966265-48a03668-8585-4525-8a86-afa2209d1602.png\"/>\n</div>\n\n## Results and Models\n\n### GFocalV1 with LD\n\n|  Teacher  | Student | Training schedule | Mini-batch size | AP (val) |                      Config                       |                                                                                                                                                        Download                                                                                                                                                        |\n| :-------: | :-----: | :---------------: | :-------------: | :------: | :-----------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    --     |  R-18   |        1x         |        6        |   35.8   |                                                   |                                                                                                                                                                                                                                                                                                                        |\n|   R-101   |  R-18   |        1x         |        6        |   36.5   |   [config](./ld_r18-gflv1-r101_fpn_1x_coco.py)    |         [model](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r18_gflv1_r101_fpn_coco_1x/ld_r18_gflv1_r101_fpn_coco_1x_20220702_062206-330e6332.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r18_gflv1_r101_fpn_coco_1x/ld_r18_gflv1_r101_fpn_coco_1x_20220702_062206.log.json)         |\n|    --     |  R-34   |        1x         |        6        |   38.9   |                                                   |                                                                                                                                                                                                                                                                                                                        |\n|   R-101   |  R-34   |        1x         |        6        |   39.9   |   [config](./ld_r34-gflv1-r101_fpn_1x_coco.py)    |         [model](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r34_gflv1_r101_fpn_coco_1x/ld_r34_gflv1_r101_fpn_coco_1x_20220630_134007-9bc69413.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r34_gflv1_r101_fpn_coco_1x/ld_r34_gflv1_r101_fpn_coco_1x_20220630_134007.log.json)         |\n|    --     |  R-50   |        1x         |        6        |   40.1   |                                                   |                                                                                                                                                                                                                                                                                                                        |\n|   R-101   |  R-50   |        1x         |        6        |   41.0   |   [config](./ld_r50-gflv1-r101_fpn_1x_coco.py)    |         [model](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r50_gflv1_r101_fpn_coco_1x/ld_r50_gflv1_r101_fpn_coco_1x_20220629_145355-8dc5bad8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r50_gflv1_r101_fpn_coco_1x/ld_r50_gflv1_r101_fpn_coco_1x_20220629_145355.log.json)         |\n|    --     |  R-101  |        2x         |        6        |   44.6   |                                                   |                                                                                                                                                                                                                                                                                                                        |\n| R-101-DCN |  R-101  |        2x         |        6        |   45.5   | [config](./ld_r101-gflv1-r101-dcn_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r101_gflv1_r101dcn_fpn_coco_2x/ld_r101_gflv1_r101dcn_fpn_coco_2x_20220629_185920-9e658426.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r101_gflv1_r101dcn_fpn_coco_2x/ld_r101_gflv1_r101dcn_fpn_coco_2x_20220629_185920.log.json) |\n\n## Note\n\n- Meaning of Config name: ld_r18(student model)\\_gflv1(based on gflv1)\\_r101(teacher model)\\_fpn(neck)\\_coco(dataset)\\_1x(12 epoch).py\n\n## Citation\n\n```latex\n@Inproceedings{zheng2022LD,\n  title={Localization Distillation for Dense Object Detection},\n  author= {Zheng, Zhaohui and Ye, Rongguang and Wang, Ping and Ren, Dongwei and Zuo, Wangmeng and Hou, Qibin and Cheng, Mingming},\n  booktitle={CVPR},\n  year={2022}\n}\n```\n"
  },
  {
    "path": "configs/ld/ld_r101-gflv1-r101-dcn_fpn_2x_coco.py",
    "content": "_base_ = ['./ld_r18-gflv1-r101_fpn_1x_coco.py']\nteacher_ckpt = 'https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco/gfl_r101_fpn_dconv_c3-c5_mstrain_2x_coco_20200630_102002-134b07df.pth'  # noqa\nmodel = dict(\n    teacher_config='configs/gfl/gfl_r101-dconv-c3-c5_fpn_ms-2x_coco.py',\n    teacher_ckpt=teacher_ckpt,\n    backbone=dict(\n        type='ResNet',\n        depth=101,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5))\n\nmax_epochs = 24\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# multi-scale training\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 480), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/ld/ld_r18-gflv1-r101_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nteacher_ckpt = 'https://download.openmmlab.com/mmdetection/v2.0/gfl/gfl_r101_fpn_mstrain_2x_coco/gfl_r101_fpn_mstrain_2x_coco_20200629_200126-dd12f847.pth'  # noqa\nmodel = dict(\n    type='KnowledgeDistillationSingleStageDetector',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    teacher_config='configs/gfl/gfl_r101_fpn_ms-2x_coco.py',\n    teacher_ckpt=teacher_ckpt,\n    backbone=dict(\n        type='ResNet',\n        depth=18,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(\n        type='FPN',\n        in_channels=[64, 128, 256, 512],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='LDHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        loss_cls=dict(\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            beta=2.0,\n            loss_weight=1.0),\n        loss_dfl=dict(type='DistributionFocalLoss', loss_weight=0.25),\n        loss_ld=dict(\n            type='KnowledgeDistillationKLDivLoss', loss_weight=0.25, T=10),\n        reg_max=16,\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/ld/ld_r34-gflv1-r101_fpn_1x_coco.py",
    "content": "_base_ = ['./ld_r18-gflv1-r101_fpn_1x_coco.py']\nmodel = dict(\n    backbone=dict(\n        type='ResNet',\n        depth=34,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet34')),\n    neck=dict(\n        type='FPN',\n        in_channels=[64, 128, 256, 512],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5))\n"
  },
  {
    "path": "configs/ld/ld_r50-gflv1-r101_fpn_1x_coco.py",
    "content": "_base_ = ['./ld_r18-gflv1-r101_fpn_1x_coco.py']\nmodel = dict(\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5))\n"
  },
  {
    "path": "configs/ld/metafile.yml",
    "content": "Collections:\n  - Name: Localization Distillation\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - Localization Distillation\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/2102.12252\n      Title: 'Localization Distillation for Dense Object Detection'\n    README: configs/ld/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.11.0/mmdet/models/dense_heads/ld_head.py#L11\n      Version: v2.11.0\n\nModels:\n  - Name: ld_r18-gflv1-r101_fpn_1x_coco\n    In Collection: Localization Distillation\n    Config: configs/ld/ld_r18-gflv1-r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 1.8\n      Epochs: 12\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 36.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r18_gflv1_r101_fpn_coco_1x/ld_r18_gflv1_r101_fpn_coco_1x_20220702_062206-330e6332.pth\n  - Name: ld_r34-gflv1-r101_fpn_1x_coco\n    In Collection: Localization Distillation\n    Config: configs/ld/ld_r34-gflv1-r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 2.2\n      Epochs: 12\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r34_gflv1_r101_fpn_coco_1x/ld_r34_gflv1_r101_fpn_coco_1x_20220630_134007-9bc69413.pth\n  - Name: ld_r50-gflv1-r101_fpn_1x_coco\n    In Collection: Localization Distillation\n    Config: configs/ld/ld_r50-gflv1-r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.6\n      Epochs: 12\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 41.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r50_gflv1_r101_fpn_coco_1x/ld_r50_gflv1_r101_fpn_coco_1x_20220629_145355-8dc5bad8.pth\n  - Name: ld_r101-gflv1-r101-dcn_fpn_2x_coco\n    In Collection: Localization Distillation\n    Config: configs/ld/ld_r101-gflv1-r101-dcn_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      Epochs: 24\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 45.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ld/ld_r101_gflv1_r101dcn_fpn_coco_2x/ld_r101_gflv1_r101dcn_fpn_coco_2x_20220629_185920-9e658426.pth\n"
  },
  {
    "path": "configs/legacy_1.x/README.md",
    "content": "# Legacy Configs in MMDetection V1.x\n\n<!-- [OTHERS] -->\n\nConfigs in this directory implement the legacy configs used by MMDetection V1.x and its model zoos.\n\nTo help users convert their models from V1.x to MMDetection V2.0, we provide v1.x configs to inference the converted v1.x models.\nDue to the BC-breaking changes in MMDetection V2.0 from MMDetection V1.x, running inference with the same model weights in these two version will produce different results. The difference will cause within 1% AP absolute difference as can be found in the following table.\n\n## Usage\n\nTo upgrade the model version, the users need to do the following steps.\n\n### 1. Convert model weights\n\nThere are three main difference in the model weights between V1.x and V2.0 codebases.\n\n1. Since the class order in all the detector's classification branch is reordered, all the legacy model weights need to go through the conversion process.\n2. The regression and segmentation head no longer contain the background channel. Weights in these background channels should be removed to fix in the current codebase.\n3. For two-stage detectors, their wegihts need to be upgraded since MMDetection V2.0 refactors all the two-stage detectors with `RoIHead`.\n\nThe users can do the same modification as mentioned above for the self-implemented\ndetectors. We provide a scripts `tools/model_converters/upgrade_model_version.py` to convert the model weights in the V1.x model zoo.\n\n```bash\npython tools/model_converters/upgrade_model_version.py ${OLD_MODEL_PATH} ${NEW_MODEL_PATH} --num-classes ${NUM_CLASSES}\n\n```\n\n- OLD_MODEL_PATH: the path to load the model weights in 1.x version.\n- NEW_MODEL_PATH: the path to save the converted model weights in 2.0 version.\n- NUM_CLASSES: number of classes of the original model weights. Usually it is 81 for COCO dataset, 21 for VOC dataset.\n  The number of classes in V2.0 models should be equal to that in V1.x models - 1.\n\n### 2. Use configs with legacy settings\n\nAfter converting the model weights, checkout to the v1.2 release to find the corresponding config file that uses the legacy settings.\nThe V1.x models usually need these three legacy modules: `LegacyAnchorGenerator`, `LegacyDeltaXYWHBBoxCoder`, and `RoIAlign(align=False)`.\nFor models using ResNet Caffe backbones, they also need to change the pretrain name and the corresponding `img_norm_cfg`.\nAn example is in [`retinanet_r50-caffe_fpn_1x_coco_v1.py`](retinanet_r50-caffe_fpn_1x_coco_v1.py)\nThen use the config to test the model weights. For most models, the obtained results should be close to that in V1.x.\nWe provide configs of some common structures in this directory.\n\n## Performance\n\nThe performance change after converting the models in this directory are listed as the following.\n\n|           Method            |  Style  | Lr schd | V1.x box AP | V1.x mask AP | V2.0 box AP | V2.0 mask AP |                       Config                        |                                                             Download                                                              |\n| :-------------------------: | :-----: | :-----: | :---------: | :----------: | :---------: | :----------: | :-------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: |\n|     Mask R-CNN R-50-FPN     | pytorch |   1x    |    37.3     |     34.2     |    36.8     |     33.9     |     [config](./mask-rcnn_r50_fpn_1x_coco_v1.py)     |     [model](https://s3.ap-northeast-2.amazonaws.com/open-mmlab/mmdetection/models/mask_rcnn_r50_fpn_1x_20181010-069fa190.pth)     |\n|     RetinaNet R-50-FPN      |  caffe  |   1x    |    35.8     |      -       |    35.4     |      -       |  [config](./retinanet_r50-caffe_fpn_1x_coco_v1.py)  |                                                                                                                                   |\n|     RetinaNet R-50-FPN      | pytorch |   1x    |    35.6     |      -       |    35.2     |      -       |     [config](./retinanet_r50_fpn_1x_coco_v1.py)     |     [model](https://s3.ap-northeast-2.amazonaws.com/open-mmlab/mmdetection/models/retinanet_r50_fpn_1x_20181125-7b0c2548.pth)     |\n| Cascade Mask R-CNN R-50-FPN | pytorch |   1x    |    41.2     |     35.7     |    40.8     |     35.6     | [config](./cascade-mask-rcnn_r50_fpn_1x_coco_v1.py) | [model](https://s3.ap-northeast-2.amazonaws.com/open-mmlab/mmdetection/models/cascade_mask_rcnn_r50_fpn_1x_20181123-88b170c9.pth) |\n|        SSD300-VGG16         |  caffe  |  120e   |    25.7     |      -       |    25.4     |      -       |            [config](./ssd300_coco_v1.py)            | [model](https://s3.ap-northeast-2.amazonaws.com/open-mmlab/mmdetection/models/ssd300_coco_vgg16_caffe_120e_20181221-84d7110b.pth) |\n"
  },
  {
    "path": "configs/legacy_1.x/cascade-mask-rcnn_r50_fpn_1x_coco_v1.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='CascadeRCNN',\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        anchor_generator=dict(type='LegacyAnchorGenerator', center_offset=0.5),\n        bbox_coder=dict(\n            type='LegacyDeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0])),\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=7,\n                sampling_ratio=2,\n                aligned=False)),\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                reg_class_agnostic=True,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='LegacyDeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2])),\n            dict(\n                type='Shared2FCBBoxHead',\n                reg_class_agnostic=True,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='LegacyDeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1])),\n            dict(\n                type='Shared2FCBBoxHead',\n                reg_class_agnostic=True,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='LegacyDeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067])),\n        ],\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=14,\n                sampling_ratio=2,\n                aligned=False))))\n"
  },
  {
    "path": "configs/legacy_1.x/faster-rcnn_r50_fpn_1x_coco_v1.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    type='FasterRCNN',\n    backbone=dict(\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    rpn_head=dict(\n        type='RPNHead',\n        anchor_generator=dict(\n            type='LegacyAnchorGenerator',\n            center_offset=0.5,\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(type='LegacyDeltaXYWHBBoxCoder'),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        type='StandardRoIHead',\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=7,\n                sampling_ratio=2,\n                aligned=False),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            bbox_coder=dict(type='LegacyDeltaXYWHBBoxCoder'),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn_proposal=dict(max_per_img=2000),\n        rcnn=dict(assigner=dict(match_low_quality=True))))\n"
  },
  {
    "path": "configs/legacy_1.x/mask-rcnn_r50_fpn_1x_coco_v1.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    rpn_head=dict(\n        anchor_generator=dict(type='LegacyAnchorGenerator', center_offset=0.5),\n        bbox_coder=dict(type='LegacyDeltaXYWHBBoxCoder'),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=7,\n                sampling_ratio=2,\n                aligned=False)),\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=14,\n                sampling_ratio=2,\n                aligned=False)),\n        bbox_head=dict(\n            bbox_coder=dict(type='LegacyDeltaXYWHBBoxCoder'),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n\n    # model training and testing settings\n    train_cfg=dict(\n        rpn_proposal=dict(max_per_img=2000),\n        rcnn=dict(assigner=dict(match_low_quality=True))))\n"
  },
  {
    "path": "configs/legacy_1.x/retinanet_r50-caffe_fpn_1x_coco_v1.py",
    "content": "_base_ = './retinanet_r50_fpn_1x_coco_v1.py'\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        # use caffe img_norm\n        mean=[102.9801, 115.9465, 122.7717],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron/resnet50_caffe')))\n"
  },
  {
    "path": "configs/legacy_1.x/retinanet_r50_fpn_1x_coco_v1.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    bbox_head=dict(\n        type='RetinaHead',\n        anchor_generator=dict(\n            type='LegacyAnchorGenerator',\n            center_offset=0.5,\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(type='LegacyDeltaXYWHBBoxCoder'),\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.11, loss_weight=1.0)))\n"
  },
  {
    "path": "configs/legacy_1.x/ssd300_coco_v1.py",
    "content": "_base_ = [\n    '../_base_/models/ssd300.py', '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n# model settings\ninput_size = 300\nmodel = dict(\n    bbox_head=dict(\n        type='SSDHead',\n        anchor_generator=dict(\n            type='LegacySSDAnchorGenerator',\n            scale_major=False,\n            input_size=input_size,\n            basesize_ratio_range=(0.15, 0.9),\n            strides=[8, 16, 32, 64, 100, 300],\n            ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]]),\n        bbox_coder=dict(\n            type='LegacyDeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2])))\n"
  },
  {
    "path": "configs/libra_rcnn/README.md",
    "content": "# Libra R-CNN\n\n> [Libra R-CNN: Towards Balanced Learning for Object Detection](https://arxiv.org/abs/1904.02701)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nCompared with model architectures, the training process, which is also crucial to the success of detectors, has received relatively less attention in object detection. In this work, we carefully revisit the standard training practice of detectors, and find that the detection performance is often limited by the imbalance during the training process, which generally consists in three levels - sample level, feature level, and objective level. To mitigate the adverse effects caused thereby, we propose Libra R-CNN, a simple but effective framework towards balanced learning for object detection. It integrates three novel components: IoU-balanced sampling, balanced feature pyramid, and balanced L1 loss, respectively for reducing the imbalance at sample, feature, and objective level. Benefitted from the overall balanced design, Libra R-CNN significantly improves the detection performance. Without bells and whistles, it achieves 2.5 points and 2.0 points higher Average Precision (AP) than FPN Faster R-CNN and RetinaNet respectively on MSCOCO.\n\nInstance recognition is rapidly advanced along with the developments of various deep convolutional neural networks. Compared to the architectures of networks, the training process, which is also crucial to the success of detectors, has received relatively less attention. In this work, we carefully revisit the standard training practice of detectors, and find that the detection performance is often limited by the imbalance during the training process, which generally consists in three levels - sample level, feature level, and objective level. To mitigate the adverse effects caused thereby, we propose Libra R-CNN, a simple yet effective framework towards balanced learning for instance recognition. It integrates IoU-balanced sampling, balanced feature pyramid, and objective re-weighting, respectively for reducing the imbalance at sample, feature, and objective level. Extensive experiments conducted on MS COCO, LVIS and Pascal VOC datasets prove the effectiveness of the overall balanced design.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143966392-2633684c-a67a-4269-b71b-afe945c67bcd.png\"/>\n</div>\n\n## Results and Models\n\nThe results on COCO 2017val are shown in the below table. (results on test-dev are usually slightly higher than val)\n\n| Architecture |    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                         Config                          |                                                                                                                                                                          Download                                                                                                                                                                           |\n| :----------: | :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Faster R-CNN |    R-50-FPN     | pytorch |   1x    |   4.6    |      19.0      |  38.3  |    [config](./libra-faster-rcnn_r50_fpn_1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_r50_fpn_1x_coco/libra_faster_rcnn_r50_fpn_1x_coco_20200130-3afee3a9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_r50_fpn_1x_coco/libra_faster_rcnn_r50_fpn_1x_coco_20200130_204655.log.json)               |\n|  Fast R-CNN  |    R-50-FPN     | pytorch |   1x    |          |                |        |                                                         |                                                                                                                                                                                                                                                                                                                                                             |\n| Faster R-CNN |    R-101-FPN    | pytorch |   1x    |   6.5    |      14.4      |  40.1  |    [config](./libra-faster-rcnn_r101_fpn_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_r101_fpn_1x_coco/libra_faster_rcnn_r101_fpn_1x_coco_20200203-8dba6a5a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_r101_fpn_1x_coco/libra_faster_rcnn_r101_fpn_1x_coco_20200203_001405.log.json)             |\n| Faster R-CNN | X-101-64x4d-FPN | pytorch |   1x    |   10.8   |      8.5       |  42.7  | [config](./libra-faster-rcnn_x101-64x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_x101_64x4d_fpn_1x_coco/libra_faster_rcnn_x101_64x4d_fpn_1x_coco_20200315-3a7d0488.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_x101_64x4d_fpn_1x_coco/libra_faster_rcnn_x101_64x4d_fpn_1x_coco_20200315_231625.log.json) |\n|  RetinaNet   |    R-50-FPN     | pytorch |   1x    |   4.2    |      17.7      |  37.6  |     [config](./libra-retinanet_r50_fpn_1x_coco.py)      |                   [model](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_retinanet_r50_fpn_1x_coco/libra_retinanet_r50_fpn_1x_coco_20200205-804d94ce.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_retinanet_r50_fpn_1x_coco/libra_retinanet_r50_fpn_1x_coco_20200205_112757.log.json)                   |\n\n## Citation\n\nWe provide config files to reproduce the results in the CVPR 2019 paper [Libra R-CNN](https://arxiv.org/pdf/1904.02701.pdf).\n\nThe extended version of [Libra R-CNN](https://arxiv.org/pdf/2108.10175.pdf) is accpeted by IJCV.\n\n```latex\n@inproceedings{pang2019libra,\n  title={Libra R-CNN: Towards Balanced Learning for Object Detection},\n  author={Pang, Jiangmiao and Chen, Kai and Shi, Jianping and Feng, Huajun and Ouyang, Wanli and Dahua Lin},\n  booktitle={IEEE Conference on Computer Vision and Pattern Recognition},\n  year={2019}\n}\n\n@article{pang2021towards,\n  title={Towards Balanced Learning for Instance Recognition},\n  author={Pang, Jiangmiao and Chen, Kai and Li, Qi and Xu, Zhihai and Feng, Huajun and Shi, Jianping and Ouyang, Wanli and Lin, Dahua},\n  journal={International Journal of Computer Vision},\n  volume={129},\n  number={5},\n  pages={1376--1393},\n  year={2021},\n  publisher={Springer}\n}\n```\n"
  },
  {
    "path": "configs/libra_rcnn/libra-fast-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = '../fast_rcnn/fast-rcnn_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    neck=[\n        dict(\n            type='FPN',\n            in_channels=[256, 512, 1024, 2048],\n            out_channels=256,\n            num_outs=5),\n        dict(\n            type='BFP',\n            in_channels=256,\n            num_levels=5,\n            refine_level=2,\n            refine_type='non_local')\n    ],\n    roi_head=dict(\n        bbox_head=dict(\n            loss_bbox=dict(\n                _delete_=True,\n                type='BalancedL1Loss',\n                alpha=0.5,\n                gamma=1.5,\n                beta=1.0,\n                loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rcnn=dict(\n            sampler=dict(\n                _delete_=True,\n                type='CombinedSampler',\n                num=512,\n                pos_fraction=0.25,\n                add_gt_as_proposals=True,\n                pos_sampler=dict(type='InstanceBalancedPosSampler'),\n                neg_sampler=dict(\n                    type='IoUBalancedNegSampler',\n                    floor_thr=-1,\n                    floor_fraction=0,\n                    num_bins=3)))))\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# _base_.train_dataloader.dataset.proposal_file = 'libra_proposals/rpn_r50_fpn_1x_train2017.pkl'  # noqa\ntrain_dataloader = dict(\n    dataset=dict(proposal_file='libra_proposals/rpn_r50_fpn_1x_train2017.pkl'))\n\n# _base_.val_dataloader.dataset.proposal_file = 'libra_proposals/rpn_r50_fpn_1x_val2017.pkl'  # noqa\n# test_dataloader = _base_.val_dataloader\nval_dataloader = dict(\n    dataset=dict(proposal_file='libra_proposals/rpn_r50_fpn_1x_val2017.pkl'))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/libra_rcnn/libra-faster-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = './libra-faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    neck=[\n        dict(\n            type='FPN',\n            in_channels=[256, 512, 1024, 2048],\n            out_channels=256,\n            num_outs=5),\n        dict(\n            type='BFP',\n            in_channels=256,\n            num_levels=5,\n            refine_level=2,\n            refine_type='non_local')\n    ],\n    roi_head=dict(\n        bbox_head=dict(\n            loss_bbox=dict(\n                _delete_=True,\n                type='BalancedL1Loss',\n                alpha=0.5,\n                gamma=1.5,\n                beta=1.0,\n                loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(sampler=dict(neg_pos_ub=5), allowed_border=-1),\n        rcnn=dict(\n            sampler=dict(\n                _delete_=True,\n                type='CombinedSampler',\n                num=512,\n                pos_fraction=0.25,\n                add_gt_as_proposals=True,\n                pos_sampler=dict(type='InstanceBalancedPosSampler'),\n                neg_sampler=dict(\n                    type='IoUBalancedNegSampler',\n                    floor_thr=-1,\n                    floor_fraction=0,\n                    num_bins=3)))))\n"
  },
  {
    "path": "configs/libra_rcnn/libra-faster-rcnn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './libra-faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/libra_rcnn/libra-retinanet_r50_fpn_1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    neck=[\n        dict(\n            type='FPN',\n            in_channels=[256, 512, 1024, 2048],\n            out_channels=256,\n            start_level=1,\n            add_extra_convs='on_input',\n            num_outs=5),\n        dict(\n            type='BFP',\n            in_channels=256,\n            num_levels=5,\n            refine_level=1,\n            refine_type='non_local')\n    ],\n    bbox_head=dict(\n        loss_bbox=dict(\n            _delete_=True,\n            type='BalancedL1Loss',\n            alpha=0.5,\n            gamma=1.5,\n            beta=0.11,\n            loss_weight=1.0)))\n"
  },
  {
    "path": "configs/libra_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Libra R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - IoU-Balanced Sampling\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Balanced Feature Pyramid\n    Paper:\n      URL: https://arxiv.org/abs/1904.02701\n      Title: 'Libra R-CNN: Towards Balanced Learning for Object Detection'\n    README: configs/libra_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/necks/bfp.py#L10\n      Version: v2.0.0\n\nModels:\n  - Name: libra-faster-rcnn_r50_fpn_1x_coco\n    In Collection: Libra R-CNN\n    Config: configs/libra_rcnn/libra-faster-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.6\n      inference time (ms/im):\n        - value: 52.63\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_r50_fpn_1x_coco/libra_faster_rcnn_r50_fpn_1x_coco_20200130-3afee3a9.pth\n\n  - Name: libra-faster-rcnn_r101_fpn_1x_coco\n    In Collection: Libra R-CNN\n    Config: configs/libra_rcnn/libra-faster-rcnn_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.5\n      inference time (ms/im):\n        - value: 69.44\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_r101_fpn_1x_coco/libra_faster_rcnn_r101_fpn_1x_coco_20200203-8dba6a5a.pth\n\n  - Name: libra-faster-rcnn_x101-64x4d_fpn_1x_coco\n    In Collection: Libra R-CNN\n    Config: configs/libra_rcnn/libra-faster-rcnn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.8\n      inference time (ms/im):\n        - value: 117.65\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_faster_rcnn_x101_64x4d_fpn_1x_coco/libra_faster_rcnn_x101_64x4d_fpn_1x_coco_20200315-3a7d0488.pth\n\n  - Name: libra-retinanet_r50_fpn_1x_coco\n    In Collection: Libra R-CNN\n    Config: configs/libra_rcnn/libra-retinanet_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.2\n      inference time (ms/im):\n        - value: 56.5\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/libra_rcnn/libra_retinanet_r50_fpn_1x_coco/libra_retinanet_r50_fpn_1x_coco_20200205-804d94ce.pth\n"
  },
  {
    "path": "configs/lvis/README.md",
    "content": "# LVIS\n\n> [LVIS: A Dataset for Large Vocabulary Instance Segmentation](https://arxiv.org/abs/1908.03195)\n\n<!-- [DATASET] -->\n\n## Abstract\n\nProgress on object detection is enabled by datasets that focus the research community's attention on open challenges. This process led us from simple images to complex scenes and from bounding boxes to segmentation masks. In this work, we introduce LVIS (pronounced \\`el-vis'): a new dataset for Large Vocabulary Instance Segmentation. We plan to collect ~2 million high-quality instance segmentation masks for over 1000 entry-level object categories in 164k images. Due to the Zipfian distribution of categories in natural images, LVIS naturally has a long tail of categories with few training samples. Given that state-of-the-art deep learning methods for object detection perform poorly in the low-sample regime, we believe that our dataset poses an important and exciting new scientific challenge.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143967423-85b9b705-05ea-4bbc-9a41-eccc14240c7a.png\" height=\"300\"/>\n</div>\n\n## Common Setting\n\n- Please follow [install guide](../../docs/get_started.md#install-mmdetection) to install open-mmlab forked cocoapi first.\n\n- Run following scripts to install our forked lvis-api.\n\n  ```shell\n  pip install git+https://github.com/lvis-dataset/lvis-api.git\n  ```\n\n- All experiments use oversample strategy [here](../../docs/tutorials/customize_dataset.md#class-balanced-dataset) with oversample threshold `1e-3`.\n\n- The size of LVIS v0.5 is half of COCO, so schedule `2x` in LVIS is roughly the same iterations as `1x` in COCO.\n\n## Results and models of LVIS v0.5\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                               Config                               |                                                                                                                                                                                      Download                                                                                                                                                                                      |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :----------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |   2x    |    -     |       -        |  26.1  |  25.9   |    [config](./mask-rcnn_r50_fpn_sample1e-3_ms-2x_lvis-v0.5.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_2x_lvis-dbd06831.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_2x_lvis_20200531_160435.log.json)               |\n|    R-101-FPN    | pytorch |   2x    |    -     |       -        |  27.1  |  27.0   |    [config](./mask-rcnn_r101_fpn_sample1e-3_ms-2x_lvis-v0.5.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_2x_lvis-54582ee2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_2x_lvis_20200601_134748.log.json)             |\n| X-101-32x4d-FPN | pytorch |   2x    |    -     |       -        |  26.7  |  26.9   | [config](./mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-2x_lvis-v0.5.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_2x_lvis-3cf55ea2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_2x_lvis_20200531_221749.log.json) |\n| X-101-64x4d-FPN | pytorch |   2x    |    -     |       -        |  26.4  |  26.0   | [config](./mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-2x_lvis-v0.5.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_2x_lvis-1c99a5ad.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_2x_lvis_20200601_194651.log.json) |\n\n## Results and models of LVIS v1\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                              Config                              |                                                                                                                                                                                            Download                                                                                                                                                                                            |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :--------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |   1x    |   9.1    |       -        |  22.5  |  21.7   |    [config](./mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_r50_fpn_sample1e-3_mstrain_1x_lvis_v1-aa78ac3d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_r50_fpn_sample1e-3_mstrain_1x_lvis_v1-20200829_061305.log.json)               |\n|    R-101-FPN    | pytorch |   1x    |   10.8   |       -        |  24.6  |  23.6   |    [config](./mask-rcnn_r101_fpn_sample1e-3_ms-1x_lvis-v1.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_r101_fpn_sample1e-3_mstrain_1x_lvis_v1-ec55ce32.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_r101_fpn_sample1e-3_mstrain_1x_lvis_v1-20200829_070959.log.json)             |\n| X-101-32x4d-FPN | pytorch |   1x    |   11.8   |       -        |  26.7  |  25.5   | [config](./mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-1x_lvis-v1.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_1x_lvis_v1-ebbc5c81.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_1x_lvis_v1-20200829_071317.log.json) |\n| X-101-64x4d-FPN | pytorch |   1x    |   14.6   |       -        |  27.2  |  25.8   | [config](./mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-1x_lvis-v1.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_1x_lvis_v1-43d9edfe.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_1x_lvis_v1-20200830_060206.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{gupta2019lvis,\n  title={{LVIS}: A Dataset for Large Vocabulary Instance Segmentation},\n  author={Gupta, Agrim and Dollar, Piotr and Girshick, Ross},\n  booktitle={Proceedings of the {IEEE} Conference on Computer Vision and Pattern Recognition},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_r101_fpn_sample1e-3_ms-1x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_r101_fpn_sample1e-3_ms-2x_lvis-v0.5.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_sample1e-3_ms-2x_lvis-v0.5.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/lvis_v1_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(num_classes=1203), mask_head=dict(num_classes=1203)),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            # LVIS allows up to 300\n            max_per_img=300)))\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_r50_fpn_sample1e-3_ms-2x_lvis-v0.5.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/lvis_v0.5_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(num_classes=1230), mask_head=dict(num_classes=1230)),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            # LVIS allows up to 300\n            max_per_img=300)))\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-1x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-2x_lvis-v0.5.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_sample1e-3_ms-2x_lvis-v0.5.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-1x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/lvis/mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-2x_lvis-v0.5.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_sample1e-3_ms-2x_lvis-v0.5.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/lvis/metafile.yml",
    "content": "Models:\n  - Name: mask-rcnn_r50_fpn_sample1e-3_ms-2x_lvis-v0.5\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_r50_fpn_sample1e-3_ms-2x_lvis-v0.5.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v0.5\n        Metrics:\n          box AP: 26.1\n      - Task: Instance Segmentation\n        Dataset: LVIS v0.5\n        Metrics:\n          mask AP: 25.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_2x_lvis-dbd06831.pth\n\n  - Name: mask-rcnn_r101_fpn_sample1e-3_ms-2x_lvis-v0.5\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_r101_fpn_sample1e-3_ms-2x_lvis-v0.5.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v0.5\n        Metrics:\n          box AP: 27.1\n      - Task: Instance Segmentation\n        Dataset: LVIS v0.5\n        Metrics:\n          mask AP: 27.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_2x_lvis-54582ee2.pth\n\n  - Name: mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-2x_lvis-v0.5\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-2x_lvis-v0.5.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v0.5\n        Metrics:\n          box AP: 26.7\n      - Task: Instance Segmentation\n        Dataset: LVIS v0.5\n        Metrics:\n          mask AP: 26.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_2x_lvis-3cf55ea2.pth\n\n  - Name: mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-2x_lvis-v0.5\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-2x_lvis-v0.5.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v0.5\n        Metrics:\n          box AP: 26.4\n      - Task: Instance Segmentation\n        Dataset: LVIS v0.5\n        Metrics:\n          mask AP: 26.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_2x_lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_2x_lvis-1c99a5ad.pth\n\n  - Name: mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_r50_fpn_sample1e-3_ms-1x_lvis-v1.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 22.5\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 21.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r50_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_r50_fpn_sample1e-3_mstrain_1x_lvis_v1-aa78ac3d.pth\n\n  - Name: mask-rcnn_r101_fpn_sample1e-3_ms-1x_lvis-v1\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_r101_fpn_sample1e-3_ms-1x_lvis-v1.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 24.6\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 23.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_r101_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_r101_fpn_sample1e-3_mstrain_1x_lvis_v1-ec55ce32.pth\n\n  - Name: mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-1x_lvis-v1\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_x101-32x4d_fpn_sample1e-3_ms-1x_lvis-v1.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 26.7\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 25.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_x101_32x4d_fpn_sample1e-3_mstrain_1x_lvis_v1-ebbc5c81.pth\n\n  - Name: mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-1x_lvis-v1\n    In Collection: Mask R-CNN\n    Config: configs/lvis/mask-rcnn_x101-64x4d_fpn_sample1e-3_ms-1x_lvis-v1.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 27.2\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 25.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/lvis/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_1x_lvis_v1/mask_rcnn_x101_64x4d_fpn_sample1e-3_mstrain_1x_lvis_v1-43d9edfe.pth\n"
  },
  {
    "path": "configs/mask2former/README.md",
    "content": "# Mask2Former\n\n> [Masked-attention Mask Transformer for Universal Image Segmentation](http://arxiv.org/abs/2112.01527)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nImage segmentation is about grouping pixels with different semantics, e.g., category or instance membership, where each choice of semantics defines a task. While only the semantics of each task differ, current research focuses on designing specialized architectures for each task. We present Masked-attention Mask Transformer (Mask2Former), a new architecture capable of addressing any image segmentation task (panoptic, instance or semantic). Its key components include masked attention, which extracts localized features by constraining cross-attention within predicted mask regions. In addition to reducing the research effort by at least three times, it outperforms the best specialized architectures by a significant margin on four popular datasets. Most notably, Mask2Former sets a new state-of-the-art for panoptic segmentation (57.8 PQ on COCO), instance segmentation (50.1 AP on COCO) and semantic segmentation (57.7 mIoU on ADE20K).\n\n<div align=center>\n<img src=\"https://camo.githubusercontent.com/455d3116845b1d580b1f8a8542334b9752fdf39364deee2951cdd231524c7725/68747470733a2f2f626f77656e63303232312e6769746875622e696f2f696d616765732f6d61736b666f726d657276325f7465617365722e706e67\" height=\"300\"/>\n</div>\n\n## Introduction\n\nMask2Former requires COCO and [COCO-panoptic](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip) dataset for training and evaluation. You need to download and extract it in the COCO dataset path.\nThe directory should be like this.\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n|   |   |   ├── instances_train2017.json\n|   |   |   ├── instances_val2017.json\n│   │   │   ├── panoptic_train2017.json\n│   │   │   ├── panoptic_train2017\n│   │   │   ├── panoptic_val2017.json\n│   │   │   ├── panoptic_val2017\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n```\n\n## Results and Models\n\n### Panoptic segmentation\n\n| Backbone | style   | Pretrain     | Lr schd | Mem (GB) | Inf time (fps) | PQ   | box mAP | mask mAP | Config                                                                          | Download                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n| -------- | ------- | ------------ | ------- | -------- | -------------- | ---- | ------- | -------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| R-50     | pytorch | ImageNet-1K  | 50e     | 13.9     | -              | 52.0 | 44.5    | 41.8     | [config](./mask2former_r50_8xb2-lsj-50e_coco-panoptic.py)                       | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic/mask2former_r50_8xb2-lsj-50e_coco-panoptic_20230118_125535-54df384a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic/mask2former_r50_8xb2-lsj-50e_coco-panoptic_20230118_125535.log.json)                                                                                       |\n| R-101    | pytorch | ImageNet-1K  | 50e     | 16.1     | -              | 52.4 | 45.3    | 42.4     | [config](./mask2former_r101_8xb2-lsj-50e_coco-panoptic.py)                      | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r101_8xb2-lsj-50e_coco-panoptic/mask2former_r101_8xb2-lsj-50e_coco-panoptic_20220329_225104-c74d4d71.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_r101_lsj_8x2_50e_coco-panoptic/mask2former_r101_lsj_8x2_50e_coco-panoptic_20220329_225104.log.json)                                                                                     |\n| Swin-T   | -       | ImageNet-1K  | 50e     | 15.9     | -              | 53.4 | 46.3    | 43.4     | [config](./mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py)          | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic_20220326_224553-3ec9e0ae.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_swin-t-p4-w7-224_lsj_8x2_50e_coco-panoptic/mask2former_swin-t-p4-w7-224_lsj_8x2_50e_coco-panoptic_20220326_224553.log.json)                                     |\n| Swin-S   | -       | ImageNet-1K  | 50e     | 19.1     | -              | 54.5 | 47.8    | 44.5     | [config](./mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py)          | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic_20220329_225200-4a16ded7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_swin-s-p4-w7-224_lsj_8x2_50e_coco-panoptic/mask2former_swin-s-p4-w7-224_lsj_8x2_50e_coco-panoptic_20220329_225200.log.json)                                     |\n| Swin-B   | -       | ImageNet-1K  | 50e     | 26.0     | -              | 55.1 | 48.2    | 44.9     | [config](./mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic.py)         | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic/mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic_20220331_002244-8a651d82.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_swin-b-p4-w12-384_lsj_8x2_50e_coco-panoptic/mask2former_swin-b-p4-w12-384_lsj_8x2_50e_coco-panoptic_20220331_002244.log.json)                                 |\n| Swin-B   | -       | ImageNet-21K | 50e     | 25.8     | -              | 56.3 | 50.0    | 46.3     | [config](./mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic.py)   | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic/mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic_20220329_230021-05ec7315.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_swin-b-p4-w12-384-in21k_lsj_8x2_50e_coco-panoptic/mask2former_swin-b-p4-w12-384-in21k_lsj_8x2_50e_coco-panoptic_20220329_230021.log.json)         |\n| Swin-L   | -       | ImageNet-21K | 100e    | 21.1     | -              | 57.6 | 52.2    | 48.5     | [config](./mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic/mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic_20220407_104949-82f8d28d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_swin-l-p4-w12-384-in21k_lsj_16x1_100e_coco-panoptic/mask2former_swin-l-p4-w12-384-in21k_lsj_16x1_100e_coco-panoptic_20220407_104949.log.json) |\n\n### Instance segmentation\n\n| Backbone | style   | Pretrain    | Lr schd | Mem (GB) | Inf time (fps) | box mAP | mask mAP | Config                                                        | Download                                                                                                                                                                                                                                                                                                                                                                                   |\n| -------- | ------- | ----------- | ------- | -------- | -------------- | ------- | -------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| R-50     | pytorch | ImageNet-1K | 50e     | 13.7     | -              | 45.7    | 42.9     | [config](./mask2former_r50_8xb2-lsj-50e_coco.py)              | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r50_8xb2-lsj-50e_coco/mask2former_r50_8xb2-lsj-50e_coco_20220506_191028-41b088b6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_r50_lsj_8x2_50e_coco/mask2former_r50_lsj_8x2_50e_coco_20220506_191028.log.json)                                                     |\n| R-101    | pytorch | ImageNet-1K | 50e     | 15.5     | -              | 46.7    | 44.0     | [config](./mask2former_r101_8xb2-lsj-50e_coco.py)             | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r101_8xb2-lsj-50e_coco/mask2former_r101_8xb2-lsj-50e_coco_20220426_100250-ecf181e2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_r101_lsj_8x2_50e_coco/mask2former_r101_lsj_8x2_50e_coco_20220426_100250.log.json)                                                 |\n| Swin-T   | -       | ImageNet-1K | 50e     | 15.3     | -              | 47.7    | 44.7     | [config](./mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco_20220508_091649-01b0f990.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_swin-t-p4-w7-224_lsj_8x2_50e_coco/mask2former_swin-t-p4-w7-224_lsj_8x2_50e_coco_20220508_091649.log.json) |\n| Swin-S   | -       | ImageNet-1K | 50e     | 18.8     | -              | 49.3    | 46.1     | [config](./mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco_20220504_001756-c9d0c4f2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask2former/mask2former_swin-s-p4-w7-224_lsj_8x2_50e_coco/mask2former_swin-s-p4-w7-224_lsj_8x2_50e_coco_20220504_001756.log.json) |\n\n### Note\n\n1. The performance is unstable. The `Mask2Former-R50-coco-panoptic` may fluctuate about 0.2 PQ. The models other than `Mask2Former-R50-coco-panoptic` were trained with mmdet 2.x and have been converted for mmdet 3.x.\n2. We have trained the instance segmentation models many times (see more details in [PR 7571](https://github.com/open-mmlab/mmdetection/pull/7571)). The results of the trained models are relatively stable (+- 0.2), and have a certain gap (about 0.2 AP) in comparison with the results in the [paper](http://arxiv.org/abs/2112.01527). However, the performance of the model trained with the official code is unstable and may also be slightly lower than the reported results as mentioned in the [issue](https://github.com/facebookresearch/Mask2Former/issues/46).\n\n## Citation\n\n```latex\n@article{cheng2021mask2former,\n  title={Masked-attention Mask Transformer for Universal Image Segmentation},\n  author={Bowen Cheng and Ishan Misra and Alexander G. Schwing and Alexander Kirillov and Rohit Girdhar},\n  journal={arXiv},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/mask2former/mask2former_r101_8xb2-lsj-50e_coco-panoptic.py",
    "content": "_base_ = './mask2former_r50_8xb2-lsj-50e_coco-panoptic.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/mask2former/mask2former_r101_8xb2-lsj-50e_coco.py",
    "content": "_base_ = ['./mask2former_r50_8xb2-lsj-50e_coco.py']\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_panoptic.py', '../_base_/default_runtime.py'\n]\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(\n        type='BatchFixedSizePad',\n        size=image_size,\n        img_pad_value=0,\n        pad_mask=True,\n        mask_pad_value=0,\n        pad_seg=True,\n        seg_pad_value=255)\n]\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True,\n    pad_size_divisor=32,\n    pad_mask=True,\n    mask_pad_value=0,\n    pad_seg=True,\n    seg_pad_value=255,\n    batch_augments=batch_augments)\n\nnum_things_classes = 80\nnum_stuff_classes = 53\nnum_classes = num_things_classes + num_stuff_classes\nmodel = dict(\n    type='Mask2Former',\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=-1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    panoptic_head=dict(\n        type='Mask2FormerHead',\n        in_channels=[256, 512, 1024, 2048],  # pass to pixel_decoder inside\n        strides=[4, 8, 16, 32],\n        feat_channels=256,\n        out_channels=256,\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes,\n        num_queries=100,\n        num_transformer_feat_level=3,\n        pixel_decoder=dict(\n            type='MSDeformAttnPixelDecoder',\n            num_outs=3,\n            norm_cfg=dict(type='GN', num_groups=32),\n            act_cfg=dict(type='ReLU'),\n            encoder=dict(  # DeformableDetrTransformerEncoder\n                num_layers=6,\n                layer_cfg=dict(  # DeformableDetrTransformerEncoderLayer\n                    self_attn_cfg=dict(  # MultiScaleDeformableAttention\n                        embed_dims=256,\n                        num_heads=8,\n                        num_levels=3,\n                        num_points=4,\n                        dropout=0.0,\n                        batch_first=True),\n                    ffn_cfg=dict(\n                        embed_dims=256,\n                        feedforward_channels=1024,\n                        num_fcs=2,\n                        ffn_drop=0.0,\n                        act_cfg=dict(type='ReLU', inplace=True)))),\n            positional_encoding=dict(num_feats=128, normalize=True)),\n        enforce_decoder_input_project=False,\n        positional_encoding=dict(num_feats=128, normalize=True),\n        transformer_decoder=dict(  # Mask2FormerTransformerDecoder\n            return_intermediate=True,\n            num_layers=9,\n            layer_cfg=dict(  # Mask2FormerTransformerDecoderLayer\n                self_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.0,\n                    batch_first=True),\n                cross_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.0,\n                    batch_first=True),\n                ffn_cfg=dict(\n                    embed_dims=256,\n                    feedforward_channels=2048,\n                    num_fcs=2,\n                    ffn_drop=0.0,\n                    act_cfg=dict(type='ReLU', inplace=True))),\n            init_cfg=None),\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=False,\n            loss_weight=2.0,\n            reduction='mean',\n            class_weight=[1.0] * num_classes + [0.1]),\n        loss_mask=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='mean',\n            loss_weight=5.0),\n        loss_dice=dict(\n            type='DiceLoss',\n            use_sigmoid=True,\n            activate=True,\n            reduction='mean',\n            naive_dice=True,\n            eps=1.0,\n            loss_weight=5.0)),\n    panoptic_fusion_head=dict(\n        type='MaskFormerFusionHead',\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes,\n        loss_panoptic=None,\n        init_cfg=None),\n    train_cfg=dict(\n        num_points=12544,\n        oversample_ratio=3.0,\n        importance_sample_ratio=0.75,\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='ClassificationCost', weight=2.0),\n                dict(\n                    type='CrossEntropyLossCost', weight=5.0, use_sigmoid=True),\n                dict(type='DiceCost', weight=5.0, pred_act=True, eps=1.0)\n            ]),\n        sampler=dict(type='MaskPseudoSampler')),\n    test_cfg=dict(\n        panoptic_on=True,\n        # For now, the dataset does not support\n        # evaluating semantic segmentation metric.\n        semantic_on=False,\n        instance_on=True,\n        # max_per_image is for instance segmentation.\n        max_per_image=100,\n        iou_thr=0.8,\n        # In Mask2Former's panoptic postprocessing,\n        # it will filter mask area where score is less than 0.5 .\n        filter_low_score=True),\n    init_cfg=None)\n\n# dataset settings\ndata_root = 'data/coco/'\ntrain_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        to_float32=True,\n        backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadPanopticAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        with_seg=True,\n        backend_args={{_base_.backend_args}}),\n    dict(type='RandomFlip', prob=0.5),\n    # large scale jittering\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_size=image_size,\n        crop_type='absolute',\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\nval_evaluator = [\n    dict(\n        type='CocoPanopticMetric',\n        ann_file=data_root + 'annotations/panoptic_val2017.json',\n        seg_prefix=data_root + 'annotations/panoptic_val2017/',\n        backend_args={{_base_.backend_args}}),\n    dict(\n        type='CocoMetric',\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        metric=['bbox', 'segm'],\n        backend_args={{_base_.backend_args}})\n]\ntest_evaluator = val_evaluator\n\n# optimizer\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,\n        weight_decay=0.05,\n        eps=1e-8,\n        betas=(0.9, 0.999)),\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n            'query_embed': embed_multi,\n            'query_feat': embed_multi,\n            'level_embed': embed_multi,\n        },\n        norm_decay_mult=0.0),\n    clip_grad=dict(max_norm=0.01, norm_type=2))\n\n# learning policy\nmax_iters = 368750\nparam_scheduler = dict(\n    type='MultiStepLR',\n    begin=0,\n    end=max_iters,\n    by_epoch=False,\n    milestones=[327778, 355092],\n    gamma=0.1)\n\n# Before 365001th iteration, we do evaluation every 5000 iterations.\n# After 365000th iteration, we do evaluation every 368750 iterations,\n# which means that we do evaluation at the end of training.\ninterval = 5000\ndynamic_intervals = [(max_iters // interval * interval + 1, max_iters)]\ntrain_cfg = dict(\n    type='IterBasedTrainLoop',\n    max_iters=max_iters,\n    val_interval=interval,\n    dynamic_intervals=dynamic_intervals)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        by_epoch=False,\n        save_last=True,\n        max_keep_ckpts=3,\n        interval=interval))\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco.py",
    "content": "_base_ = ['./mask2former_r50_8xb2-lsj-50e_coco-panoptic.py']\n\nnum_things_classes = 80\nnum_stuff_classes = 0\nnum_classes = num_things_classes + num_stuff_classes\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(\n        type='BatchFixedSizePad',\n        size=image_size,\n        img_pad_value=0,\n        pad_mask=True,\n        mask_pad_value=0,\n        pad_seg=False)\n]\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True,\n    pad_size_divisor=32,\n    pad_mask=True,\n    mask_pad_value=0,\n    pad_seg=False,\n    batch_augments=batch_augments)\nmodel = dict(\n    data_preprocessor=data_preprocessor,\n    panoptic_head=dict(\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes,\n        loss_cls=dict(class_weight=[1.0] * num_classes + [0.1])),\n    panoptic_fusion_head=dict(\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes),\n    test_cfg=dict(panoptic_on=False))\n\n# dataset settings\ntrain_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        to_float32=True,\n        backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='RandomFlip', prob=0.5),\n    # large scale jittering\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        resize_type='Resize',\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_size=image_size,\n        crop_type='absolute',\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-5, 1e-5), by_mask=True),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        to_float32=True,\n        backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\ntrain_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args={{_base_.backend_args}})\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mask2former/mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic.py",
    "content": "_base_ = ['./mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic.py']\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window12_384_22k.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(init_cfg=dict(type='Pretrained', checkpoint=pretrained)))\n"
  },
  {
    "path": "configs/mask2former/mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic.py",
    "content": "_base_ = ['./mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py']\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window12_384.pth'  # noqa\n\ndepths = [2, 2, 18, 2]\nmodel = dict(\n    backbone=dict(\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=depths,\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    panoptic_head=dict(in_channels=[128, 256, 512, 1024]))\n\n# set all layers in backbone to lr_mult=0.1\n# set all norm layers, position_embeding,\n# query_embeding, level_embeding to decay_multi=0.0\nbackbone_norm_multi = dict(lr_mult=0.1, decay_mult=0.0)\nbackbone_embed_multi = dict(lr_mult=0.1, decay_mult=0.0)\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n    'backbone.patch_embed.norm': backbone_norm_multi,\n    'backbone.norm': backbone_norm_multi,\n    'absolute_pos_embed': backbone_embed_multi,\n    'relative_position_bias_table': backbone_embed_multi,\n    'query_embed': embed_multi,\n    'query_feat': embed_multi,\n    'level_embed': embed_multi\n}\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.blocks.{block_id}.norm': backbone_norm_multi\n    for stage_id, num_blocks in enumerate(depths)\n    for block_id in range(num_blocks)\n})\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.downsample.norm': backbone_norm_multi\n    for stage_id in range(len(depths) - 1)\n})\n# optimizer\noptim_wrapper = dict(\n    paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0))\n"
  },
  {
    "path": "configs/mask2former/mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic.py",
    "content": "_base_ = ['./mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic.py']\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        embed_dims=192,\n        num_heads=[6, 12, 24, 48],\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    panoptic_head=dict(num_queries=200, in_channels=[192, 384, 768, 1536]))\n\ntrain_dataloader = dict(batch_size=1, num_workers=1)\n\n# learning policy\nmax_iters = 737500\nparam_scheduler = dict(end=max_iters, milestones=[655556, 710184])\n\n# Before 735001th iteration, we do evaluation every 5000 iterations.\n# After 735000th iteration, we do evaluation every 737500 iterations,\n# which means that we do evaluation at the end of training.'\ninterval = 5000\ndynamic_intervals = [(max_iters // interval * interval + 1, max_iters)]\ntrain_cfg = dict(\n    max_iters=max_iters,\n    val_interval=interval,\n    dynamic_intervals=dynamic_intervals)\n"
  },
  {
    "path": "configs/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py",
    "content": "_base_ = ['./mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py']\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_small_patch4_window7_224.pth'  # noqa\n\ndepths = [2, 2, 18, 2]\nmodel = dict(\n    backbone=dict(\n        depths=depths, init_cfg=dict(type='Pretrained',\n                                     checkpoint=pretrained)))\n\n# set all layers in backbone to lr_mult=0.1\n# set all norm layers, position_embeding,\n# query_embeding, level_embeding to decay_multi=0.0\nbackbone_norm_multi = dict(lr_mult=0.1, decay_mult=0.0)\nbackbone_embed_multi = dict(lr_mult=0.1, decay_mult=0.0)\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n    'backbone.patch_embed.norm': backbone_norm_multi,\n    'backbone.norm': backbone_norm_multi,\n    'absolute_pos_embed': backbone_embed_multi,\n    'relative_position_bias_table': backbone_embed_multi,\n    'query_embed': embed_multi,\n    'query_feat': embed_multi,\n    'level_embed': embed_multi\n}\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.blocks.{block_id}.norm': backbone_norm_multi\n    for stage_id, num_blocks in enumerate(depths)\n    for block_id in range(num_blocks)\n})\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.downsample.norm': backbone_norm_multi\n    for stage_id in range(len(depths) - 1)\n})\n# optimizer\noptim_wrapper = dict(\n    paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0))\n"
  },
  {
    "path": "configs/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco.py",
    "content": "_base_ = ['./mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco.py']\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_small_patch4_window7_224.pth'  # noqa\n\ndepths = [2, 2, 18, 2]\nmodel = dict(\n    backbone=dict(\n        depths=depths, init_cfg=dict(type='Pretrained',\n                                     checkpoint=pretrained)))\n\n# set all layers in backbone to lr_mult=0.1\n# set all norm layers, position_embeding,\n# query_embeding, level_embeding to decay_multi=0.0\nbackbone_norm_multi = dict(lr_mult=0.1, decay_mult=0.0)\nbackbone_embed_multi = dict(lr_mult=0.1, decay_mult=0.0)\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n    'backbone.patch_embed.norm': backbone_norm_multi,\n    'backbone.norm': backbone_norm_multi,\n    'absolute_pos_embed': backbone_embed_multi,\n    'relative_position_bias_table': backbone_embed_multi,\n    'query_embed': embed_multi,\n    'query_feat': embed_multi,\n    'level_embed': embed_multi\n}\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.blocks.{block_id}.norm': backbone_norm_multi\n    for stage_id, num_blocks in enumerate(depths)\n    for block_id in range(num_blocks)\n})\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.downsample.norm': backbone_norm_multi\n    for stage_id in range(len(depths) - 1)\n})\n# optimizer\noptim_wrapper = dict(\n    paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0))\n"
  },
  {
    "path": "configs/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py",
    "content": "_base_ = ['./mask2former_r50_8xb2-lsj-50e_coco-panoptic.py']\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth'  # noqa\n\ndepths = [2, 2, 6, 2]\nmodel = dict(\n    type='Mask2Former',\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=depths,\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    panoptic_head=dict(\n        type='Mask2FormerHead', in_channels=[96, 192, 384, 768]),\n    init_cfg=None)\n\n# set all layers in backbone to lr_mult=0.1\n# set all norm layers, position_embeding,\n# query_embeding, level_embeding to decay_multi=0.0\nbackbone_norm_multi = dict(lr_mult=0.1, decay_mult=0.0)\nbackbone_embed_multi = dict(lr_mult=0.1, decay_mult=0.0)\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n    'backbone.patch_embed.norm': backbone_norm_multi,\n    'backbone.norm': backbone_norm_multi,\n    'absolute_pos_embed': backbone_embed_multi,\n    'relative_position_bias_table': backbone_embed_multi,\n    'query_embed': embed_multi,\n    'query_feat': embed_multi,\n    'level_embed': embed_multi\n}\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.blocks.{block_id}.norm': backbone_norm_multi\n    for stage_id, num_blocks in enumerate(depths)\n    for block_id in range(num_blocks)\n})\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.downsample.norm': backbone_norm_multi\n    for stage_id in range(len(depths) - 1)\n})\n\n# optimizer\noptim_wrapper = dict(\n    paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0))\n"
  },
  {
    "path": "configs/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco.py",
    "content": "_base_ = ['./mask2former_r50_8xb2-lsj-50e_coco.py']\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth'  # noqa\ndepths = [2, 2, 6, 2]\nmodel = dict(\n    type='Mask2Former',\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=depths,\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    panoptic_head=dict(\n        type='Mask2FormerHead', in_channels=[96, 192, 384, 768]),\n    init_cfg=None)\n\n# set all layers in backbone to lr_mult=0.1\n# set all norm layers, position_embeding,\n# query_embeding, level_embeding to decay_multi=0.0\nbackbone_norm_multi = dict(lr_mult=0.1, decay_mult=0.0)\nbackbone_embed_multi = dict(lr_mult=0.1, decay_mult=0.0)\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n    'backbone.patch_embed.norm': backbone_norm_multi,\n    'backbone.norm': backbone_norm_multi,\n    'absolute_pos_embed': backbone_embed_multi,\n    'relative_position_bias_table': backbone_embed_multi,\n    'query_embed': embed_multi,\n    'query_feat': embed_multi,\n    'level_embed': embed_multi\n}\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.blocks.{block_id}.norm': backbone_norm_multi\n    for stage_id, num_blocks in enumerate(depths)\n    for block_id in range(num_blocks)\n})\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.downsample.norm': backbone_norm_multi\n    for stage_id in range(len(depths) - 1)\n})\n# optimizer\noptim_wrapper = dict(\n    paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0))\n"
  },
  {
    "path": "configs/mask2former/metafile.yml",
    "content": "Collections:\n  - Name: Mask2Former\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Weight Decay\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - Mask2Former\n    Paper:\n      URL: https://arxiv.org/pdf/2112.01527\n      Title: 'Masked-attention Mask Transformer for Universal Image Segmentation'\n    README: configs/mask2former/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.23.0/mmdet/models/detectors/mask2former.py#L7\n      Version: v2.23.0\n\nModels:\n- Name: mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py\n  Metadata:\n    Training Memory (GB): 19.1\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 47.8\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 44.5\n  - Task: Panoptic Segmentation\n    Dataset: COCO\n    Metrics:\n      PQ: 54.5\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic_20220329_225200-4a16ded7.pth\n- Name: mask2former_r101_8xb2-lsj-50e_coco\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_r101_8xb2-lsj-50e_coco.py\n  Metadata:\n    Training Memory (GB): 15.5\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 46.7\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 44.0\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r101_8xb2-lsj-50e_coco/mask2former_r101_8xb2-lsj-50e_coco_20220426_100250-ecf181e2.pth\n- Name: mask2former_r101_8xb2-lsj-50e_coco-panoptic\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_r101_8xb2-lsj-50e_coco-panoptic.py\n  Metadata:\n    Training Memory (GB): 16.1\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 45.3\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 42.4\n  - Task: Panoptic Segmentation\n    Dataset: COCO\n    Metrics:\n      PQ: 52.4\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r101_8xb2-lsj-50e_coco-panoptic/mask2former_r101_8xb2-lsj-50e_coco-panoptic_20220329_225104-c74d4d71.pth\n- Name: mask2former_r50_8xb2-lsj-50e_coco-panoptic\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py\n  Metadata:\n    Training Memory (GB): 13.9\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 44.5\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 41.8\n  - Task: Panoptic Segmentation\n    Dataset: COCO\n    Metrics:\n      PQ: 52.0\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic/mask2former_r50_8xb2-lsj-50e_coco-panoptic_20230118_125535-54df384a.pth\n- Name: mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic.py\n  Metadata:\n    Training Memory (GB): 15.9\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 46.3\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 43.4\n  - Task: Panoptic Segmentation\n    Dataset: COCO\n    Metrics:\n      PQ: 53.4\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco-panoptic_20220326_224553-3ec9e0ae.pth\n- Name: mask2former_r50_8xb2-lsj-50e_coco\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_r50_8xb2-lsj-50e_coco.py\n  Metadata:\n    Training Memory (GB): 13.7\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 45.7\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 42.9\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_r50_8xb2-lsj-50e_coco/mask2former_r50_8xb2-lsj-50e_coco_20220506_191028-41b088b6.pth\n- Name: mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic.py\n  Metadata:\n    Training Memory (GB): 21.1\n    Iterations: 737500\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 52.2\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 48.5\n  - Task: Panoptic Segmentation\n    Dataset: COCO\n    Metrics:\n      PQ: 57.6\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic/mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic_20220407_104949-82f8d28d.pth\n- Name: mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic.py\n  Metadata:\n    Training Memory (GB): 25.8\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 50.0\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 46.3\n  - Task: Panoptic Segmentation\n    Dataset: COCO\n    Metrics:\n      PQ: 56.3\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic/mask2former_swin-b-p4-w12-384-in21k_8xb2-lsj-50e_coco-panoptic_20220329_230021-05ec7315.pth\n- Name: mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic.py\n  Metadata:\n    Training Memory (GB): 26.0\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 48.2\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 44.9\n  - Task: Panoptic Segmentation\n    Dataset: COCO\n    Metrics:\n      PQ: 55.1\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic/mask2former_swin-b-p4-w12-384_8xb2-lsj-50e_coco-panoptic_20220331_002244-8a651d82.pth\n- Name: mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco.py\n  Metadata:\n    Training Memory (GB): 15.3\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 47.7\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 44.7\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco/mask2former_swin-t-p4-w7-224_8xb2-lsj-50e_coco_20220508_091649-01b0f990.pth\n- Name: mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco\n  In Collection: Mask2Former\n  Config: configs/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco.py\n  Metadata:\n    Training Memory (GB): 18.8\n    Iterations: 368750\n  Results:\n  - Task: Object Detection\n    Dataset: COCO\n    Metrics:\n      box AP: 49.3\n  - Task: Instance Segmentation\n    Dataset: COCO\n    Metrics:\n      mask AP: 46.1\n  Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco/mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco_20220504_001756-c9d0c4f2.pth\n"
  },
  {
    "path": "configs/mask2former_vis/README.md",
    "content": "# Mask2Former for Video Instance Segmentation\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nWe find Mask2Former also achieves state-of-the-art performance on video instance segmentation without modifying the architecture, the loss or even the training pipeline. In this report, we show universal image segmentation architectures trivially generalize to video segmentation by directly predicting 3D segmentation volumes. Specifically, Mask2Former sets a new state-of-the-art of 60.4 AP on YouTubeVIS-2019 and 52.6 AP on YouTubeVIS-2021. We believe Mask2Former is also capable of handling video semantic and panoptic segmentation, given its versatility in image segmentation. We hope this will make state-of-theart video segmentation research more accessible and bring more attention to designing universal image and video segmentation architectures.\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/46072190/188271377-164634a5-4d65-4161-8a69-2d0eaf2791f8.png\"/>\n</div>\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@inproceedings{cheng2021mask2former,\n  title={Masked-attention Mask Transformer for Universal Image Segmentation},\n  author={Bowen Cheng and Ishan Misra and Alexander G. Schwing and Alexander Kirillov and Rohit Girdhar},\n  journal={CVPR},\n  year={2022}\n}\n```\n\n## Results and models of Mask2Former on YouTube-VIS 2021 validation dataset\n\nNote: Codalab has closed the evaluation portal of `YouTube-VIS 2019`, so we do not provide the results of `YouTube-VIS 2019` at present. If you want to evaluate the results of `YouTube-VIS 2021`, at present, you can submit the result to the evaluation portal of `YouTube-VIS 2022`. The value of `AP_S` is the result of `YouTube-VIS 2021`.\n\n|          Method          | Backbone |  Style  | Lr schd | Mem (GB) | Inf time (fps) |  AP  |                                 Config                                  |                                                                                                                                                                                    Download                                                                                                                                                                                     |\n| :----------------------: | :------: | :-----: | :-----: | :------: | :------------: | :--: | :---------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|       Mask2Former        |   R-50   | pytorch |   8e    |   6.0    |       -        | 41.3 |           [config](mask2former_r50_8xb2-8e_youtubevis2021.py)           |        [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021/mask2former_r50_8xb2-8e_youtubevis2021_20230426_131833-5d215283.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021/mask2former_r50_8xb2-8e_youtubevis2021_20230426_131833.json)         |\n|       Mask2Former        |  R-101   | pytorch |   8e    |   7.5    |       -        | 42.3 |          [config](mask2former_r101_8xb2-8e_youtubevis2021.py)           |                             [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former_vis/mask2former_r101_8xb2-8e_youtubevis2021/mask2former_r101_8xb2-8e_youtubevis2021_20220823_092747-8077d115.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/mask2former/mask2former_r101_8xb2-8e_youtubevis2021_20220823_092747.json)                              |\n| Mask2Former(200 queries) |  Swin-L  | pytorch |   8e    |   18.5   |       -        | 52.3 | [config](mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/mask2former_vis/mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021/mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021_20220907_124752-48252603.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/mask2former/mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021_20220907_124752.json) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nDue to the influence of parameters such as learning rate in default configuration file, we recommend using 8 GPUs for training in order to reproduce accuracy. You can use the following command to start the training.\n\n```shell\n# Training Mask2Former on YouTube-VIS-2021 dataset with following command.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_train.sh configs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py 8\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 4. Testing and evaluation\n\nIf you want to get the results of the [YouTube-VOS](https://youtube-vos.org/dataset/vis/) val/test set, please use the following command to generate result files that can be used for submission. It will be stored in `./youtube_vis_results.submission_file.zip`, you can modify the saved path in `test_evaluator` of the config.\n\n```shell\n# The number after config file represents the number of GPUs used.\nbash tools/dist_test_tracking.sh configs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py --checkpoint ${CHECKPOINT_PATH}\n```\n\nIf you want to know about more detailed usage of `test_tracking.py/dist_test_tracking.sh/slurm_test_tracking.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 5.Inference\n\nUse a single GPU to predict a video and save it as a video.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 configs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py  --checkpoint {CHECKPOINT_PATH} --out vis.mp4\n```\n\nIf you want to know about more detailed usage of `mot_demo.py`, please refer to this [document](../../docs/en/user_guides/tracking_inference.md).\n"
  },
  {
    "path": "configs/mask2former_vis/mask2former_r101_8xb2-8e_youtubevis2019.py",
    "content": "_base_ = './mask2former_r50_8xb2-8e_youtubevis2019.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint='https://download.openmmlab.com/mmdetection/v3.0/'\n        'mask2former/mask2former_r101_8xb2-lsj-50e_coco/'\n        'mask2former_r101_8xb2-lsj-50e_coco_20220426_100250-ecf181e2.pth'))\n"
  },
  {
    "path": "configs/mask2former_vis/mask2former_r101_8xb2-8e_youtubevis2021.py",
    "content": "_base_ = './mask2former_r50_8xb2-8e_youtubevis2021.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint='https://download.openmmlab.com/mmdetection/v3.0/'\n        'mask2former/mask2former_r101_8xb2-lsj-50e_coco/'\n        'mask2former_r101_8xb2-lsj-50e_coco_20220426_100250-ecf181e2.pth'))\n"
  },
  {
    "path": "configs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2019.py",
    "content": "_base_ = ['../_base_/datasets/youtube_vis.py', '../_base_/default_runtime.py']\n\nnum_classes = 40\nnum_frames = 2\nmodel = dict(\n    type='Mask2FormerVideo',\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=-1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    track_head=dict(\n        type='Mask2FormerTrackHead',\n        in_channels=[256, 512, 1024, 2048],  # pass to pixel_decoder inside\n        strides=[4, 8, 16, 32],\n        feat_channels=256,\n        out_channels=256,\n        num_classes=num_classes,\n        num_queries=100,\n        num_frames=num_frames,\n        num_transformer_feat_level=3,\n        pixel_decoder=dict(\n            type='MSDeformAttnPixelDecoder',\n            num_outs=3,\n            norm_cfg=dict(type='GN', num_groups=32),\n            act_cfg=dict(type='ReLU'),\n            encoder=dict(  # DeformableDetrTransformerEncoder\n                num_layers=6,\n                layer_cfg=dict(  # DeformableDetrTransformerEncoderLayer\n                    self_attn_cfg=dict(  # MultiScaleDeformableAttention\n                        embed_dims=256,\n                        num_heads=8,\n                        num_levels=3,\n                        num_points=4,\n                        im2col_step=128,\n                        dropout=0.0,\n                        batch_first=True),\n                    ffn_cfg=dict(\n                        embed_dims=256,\n                        feedforward_channels=1024,\n                        num_fcs=2,\n                        ffn_drop=0.0,\n                        act_cfg=dict(type='ReLU', inplace=True)))),\n            positional_encoding=dict(num_feats=128, normalize=True)),\n        enforce_decoder_input_project=False,\n        positional_encoding=dict(\n            type='SinePositionalEncoding3D', num_feats=128, normalize=True),\n        transformer_decoder=dict(  # Mask2FormerTransformerDecoder\n            return_intermediate=True,\n            num_layers=9,\n            layer_cfg=dict(  # Mask2FormerTransformerDecoderLayer\n                self_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.0,\n                    batch_first=True),\n                cross_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.0,\n                    batch_first=True),\n                ffn_cfg=dict(\n                    embed_dims=256,\n                    feedforward_channels=2048,\n                    num_fcs=2,\n                    ffn_drop=0.0,\n                    act_cfg=dict(type='ReLU', inplace=True))),\n            init_cfg=None),\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=False,\n            loss_weight=2.0,\n            reduction='mean',\n            class_weight=[1.0] * num_classes + [0.1]),\n        loss_mask=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='mean',\n            loss_weight=5.0),\n        loss_dice=dict(\n            type='DiceLoss',\n            use_sigmoid=True,\n            activate=True,\n            reduction='mean',\n            naive_dice=True,\n            eps=1.0,\n            loss_weight=5.0),\n        train_cfg=dict(\n            num_points=12544,\n            oversample_ratio=3.0,\n            importance_sample_ratio=0.75,\n            assigner=dict(\n                type='HungarianAssigner',\n                match_costs=[\n                    dict(type='ClassificationCost', weight=2.0),\n                    dict(\n                        type='CrossEntropyLossCost',\n                        weight=5.0,\n                        use_sigmoid=True),\n                    dict(type='DiceCost', weight=5.0, pred_act=True, eps=1.0)\n                ]),\n            sampler=dict(type='MaskPseudoSampler'))),\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint='https://download.openmmlab.com/mmdetection/v3.0/'\n        'mask2former/mask2former_r50_8xb2-lsj-50e_coco/'\n        'mask2former_r50_8xb2-lsj-50e_coco_20220506_191028-41b088b6.pth'))\n\n# optimizer\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,\n        weight_decay=0.05,\n        eps=1e-8,\n        betas=(0.9, 0.999)),\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n            'query_embed': embed_multi,\n            'query_feat': embed_multi,\n            'level_embed': embed_multi,\n        },\n        norm_decay_mult=0.0),\n    clip_grad=dict(max_norm=0.01, norm_type=2))\n\n# learning policy\nmax_iters = 6000\nparam_scheduler = dict(\n    type='MultiStepLR',\n    begin=0,\n    end=max_iters,\n    by_epoch=False,\n    milestones=[\n        4000,\n    ],\n    gamma=0.1)\n# runtime settings\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iters, val_interval=6001)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='TrackLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook', by_epoch=False, save_last=True, interval=2000),\n    visualization=dict(type='TrackVisualizationHook', draw=False))\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n\n# evaluator\nval_evaluator = dict(\n    type='YouTubeVISMetric',\n    metric='youtube_vis_ap',\n    outfile_prefix='./youtube_vis_results',\n    format_only=True)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py",
    "content": "_base_ = './mask2former_r50_8xb2-8e_youtubevis2019.py'\n\ndataset_type = 'YouTubeVISDataset'\ndata_root = 'data/youtube_vis_2021/'\ndataset_version = data_root[-5:-1]  # 2019 or 2021\n\ntrain_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_train.json'))\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_valid.json'))\ntest_dataloader = val_dataloader\n\n# learning policy\nmax_iters = 8000\nparam_scheduler = dict(\n    type='MultiStepLR',\n    begin=0,\n    end=max_iters,\n    by_epoch=False,\n    milestones=[\n        5500,\n    ],\n    gamma=0.1)\n# runtime settings\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iters, val_interval=8001)\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook', by_epoch=False, save_last=True, interval=500))\n"
  },
  {
    "path": "configs/mask2former_vis/mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021.py",
    "content": "_base_ = ['./mask2former_r50_8xb2-8e_youtubevis2021.py']\ndepths = [2, 2, 18, 2]\nmodel = dict(\n    type='Mask2FormerVideo',\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=depths,\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=None),\n    track_head=dict(\n        type='Mask2FormerTrackHead',\n        in_channels=[192, 384, 768, 1536],\n        num_queries=200),\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint=  # noqa: E251\n        'https://download.openmmlab.com/mmdetection/v3.0/mask2former/'\n        'mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic/'\n        'mask2former_swin-l-p4-w12-384-in21k_16xb1-lsj-100e_coco-panoptic_'\n        '20220407_104949-82f8d28d.pth'))\n\n# set all layers in backbone to lr_mult=0.1\n# set all norm layers, position_embeding,\n# query_embeding, level_embeding to decay_multi=0.0\nbackbone_norm_multi = dict(lr_mult=0.1, decay_mult=0.0)\nbackbone_embed_multi = dict(lr_mult=0.1, decay_mult=0.0)\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n    'backbone.patch_embed.norm': backbone_norm_multi,\n    'backbone.norm': backbone_norm_multi,\n    'absolute_pos_embed': backbone_embed_multi,\n    'relative_position_bias_table': backbone_embed_multi,\n    'query_embed': embed_multi,\n    'query_feat': embed_multi,\n    'level_embed': embed_multi\n}\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.blocks.{block_id}.norm': backbone_norm_multi\n    for stage_id, num_blocks in enumerate(depths)\n    for block_id in range(num_blocks)\n})\ncustom_keys.update({\n    f'backbone.stages.{stage_id}.downsample.norm': backbone_norm_multi\n    for stage_id in range(len(depths) - 1)\n})\n# optimizer\noptim_wrapper = dict(\n    paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0))\n"
  },
  {
    "path": "configs/mask2former_vis/metafile.yml",
    "content": "Collections:\n  - Name: Mask2Former\n    Metadata:\n      Training Techniques:\n        - AdamW\n        - Weight Decay\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - Mask2Former\n    Paper:\n      URL: https://arxiv.org/pdf/2112.10764.pdf\n      Title: Mask2Former for Video Instance Segmentation\n    README: configs/mask2former/README.md\n\nModels:\n  - Name: mask2former_r50_8xb2-8e_youtubevis2021\n    In Collection: Mask2Former\n    Config: configs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py\n    Metadata:\n      Training Data: YouTube-VIS 2021\n      Training Memory (GB): 6.0\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2021\n        Metrics:\n          AP: 41.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021/mask2former_r50_8xb2-8e_youtubevis2021_20230426_131833-5d215283.pth\n\n  - Name: mask2former_r101_8xb2-8e_youtubevis2021\n    In Collection: Mask2Former\n    Config: configs/mask2former_vis/mask2former_r101_8xb2-8e_youtubevis2021.py\n    Metadata:\n      Training Data: YouTube-VIS 2021\n      Training Memory (GB): 7.5\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2021\n        Metrics:\n          AP: 42.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former_vis/mask2former_r101_8xb2-8e_youtubevis2021/mask2former_r101_8xb2-8e_youtubevis2021_20220823_092747-8077d115.pth\n\n  - Name: mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021.py\n    In Collection: Mask2Former\n    Config: configs/mask2former_vis/mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021.py\n    Metadata:\n      Training Data: YouTube-VIS 2021\n      Training Memory (GB): 18.5\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2021\n        Metrics:\n          AP: 52.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mask2former_vis/mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021/mask2former_swin-l-p4-w12-384-in21k_8xb2-8e_youtubevis2021_20220907_124752-48252603.pth\n"
  },
  {
    "path": "configs/mask_rcnn/README.md",
    "content": "# Mask R-CNN\n\n> [Mask R-CNN](https://arxiv.org/abs/1703.06870)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present a conceptually simple, flexible, and general framework for object instance segmentation. Our approach efficiently detects objects in an image while simultaneously generating a high-quality segmentation mask for each instance. The method, called Mask R-CNN, extends Faster R-CNN by adding a branch for predicting an object mask in parallel with the existing branch for bounding box recognition. Mask R-CNN is simple to train and adds only a small overhead to Faster R-CNN, running at 5 fps. Moreover, Mask R-CNN is easy to generalize to other tasks, e.g., allowing us to estimate human poses in the same framework. We show top results in all three tracks of the COCO suite of challenges, including instance segmentation, bounding-box object detection, and person keypoint detection. Without bells and whistles, Mask R-CNN outperforms all existing, single-model entries on every task, including the COCO 2016 challenge winners. We hope our simple and effective approach will serve as a solid baseline and help ease future research in instance-level recognition.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143967081-c2552bed-9af2-46c4-ae44-5b3b74e5679f.png\"/>\n</div>\n\n## Results and Models\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                     Config                      |                                                                                                                                                                            Download                                                                                                                                                                             |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :---------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     |  caffe  |   1x    |   4.3    |                |  38.0  |  34.4   | [config](./mask-rcnn_r50-caffe_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_1x_coco/mask_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.38__segm_mAP-0.344_20200504_231812-0ebd1859.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_1x_coco/mask_rcnn_r50_caffe_fpn_1x_coco_20200504_231812.log.json)    |\n|    R-50-FPN     | pytorch |   1x    |   4.4    |      16.1      |  38.2  |  34.7   |    [config](./mask-rcnn_r50_fpn_1x_coco.py)     |                                  [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205_050542.log.json)                                  |\n| R-50-FPN (FP16) | pytorch |   1x    |   3.6    |      24.1      |  38.1  |  34.7   |  [config](./mask-rcnn_r50_fpn_amp-1x_coco.py)   |                             [model](https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_1x_coco/mask_rcnn_r50_fpn_fp16_1x_coco_20200205-59faf7e4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_1x_coco/mask_rcnn_r50_fpn_fp16_1x_coco_20200205_130539.log.json)                             |\n|    R-50-FPN     | pytorch |   2x    |    -     |       -        |  39.2  |  35.4   |    [config](./mask-rcnn_r50_fpn_2x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_2x_coco/mask_rcnn_r50_fpn_2x_coco_bbox_mAP-0.392__segm_mAP-0.354_20200505_003907-3e542a40.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_2x_coco/mask_rcnn_r50_fpn_2x_coco_20200505_003907.log.json)               |\n|    R-101-FPN    |  caffe  |   1x    |          |                |  40.4  |  36.4   | [config](./mask-rcnn_r101-caffe_fpn_1x_coco.py) |                [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_caffe_fpn_1x_coco/mask_rcnn_r101_caffe_fpn_1x_coco_20200601_095758-805e06c1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_caffe_fpn_1x_coco/mask_rcnn_r101_caffe_fpn_1x_coco_20200601_095758.log.json)                 |\n|    R-101-FPN    | pytorch |   1x    |   6.4    |      13.5      |  40.0  |  36.1   |    [config](./mask-rcnn_r101_fpn_1x_coco.py)    |                                [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_1x_coco/mask_rcnn_r101_fpn_1x_coco_20200204-1efe0ed5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_1x_coco/mask_rcnn_r101_fpn_1x_coco_20200204_144809.log.json)                                |\n|    R-101-FPN    | pytorch |   2x    |    -     |       -        |  40.8  |  36.6   |    [config](./mask-rcnn_r101_fpn_2x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_2x_coco/mask_rcnn_r101_fpn_2x_coco_bbox_mAP-0.408__segm_mAP-0.366_20200505_071027-14b391c7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_2x_coco/mask_rcnn_r101_fpn_2x_coco_20200505_071027.log.json)             |\n| X-101-32x4d-FPN | pytorch |   1x    |   7.6    |      11.3      |  41.9  |  37.5   | [config](./mask-rcnn_x101-32x4d_fpn_1x_coco.py) |                    [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_1x_coco/mask_rcnn_x101_32x4d_fpn_1x_coco_20200205-478d0b67.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_1x_coco/mask_rcnn_x101_32x4d_fpn_1x_coco_20200205_034906.log.json)                    |\n| X-101-32x4d-FPN | pytorch |   2x    |    -     |       -        |  42.2  |  37.8   | [config](./mask-rcnn_x101-32x4d_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_2x_coco/mask_rcnn_x101_32x4d_fpn_2x_coco_bbox_mAP-0.422__segm_mAP-0.378_20200506_004702-faef898c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_2x_coco/mask_rcnn_x101_32x4d_fpn_2x_coco_20200506_004702.log.json) |\n| X-101-64x4d-FPN | pytorch |   1x    |   10.7   |      8.0       |  42.8  |  38.4   | [config](./mask-rcnn_x101-64x4d_fpn_1x_coco.py) |                    [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_1x_coco/mask_rcnn_x101_64x4d_fpn_1x_coco_20200201-9352eb0d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_1x_coco/mask_rcnn_x101_64x4d_fpn_1x_coco_20200201_124310.log.json)                    |\n| X-101-64x4d-FPN | pytorch |   2x    |    -     |       -        |  42.7  |  38.1   | [config](./mask-rcnn_x101-64x4d_fpn_2x_coco.py) |                [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_2x_coco/mask_rcnn_x101_64x4d_fpn_2x_coco_20200509_224208-39d6f70c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_2x_coco/mask_rcnn_x101_64x4d_fpn_2x_coco_20200509_224208.log.json)                 |\n| X-101-32x8d-FPN | pytorch |   1x    |   10.6   |       -        |  42.8  |  38.3   | [config](./mask-rcnn_x101-32x8d_fpn_1x_coco.py) |                [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_1x_coco/mask_rcnn_x101_32x8d_fpn_1x_coco_20220630_173841-0aaf329e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_1x_coco/mask_rcnn_x101_32x8d_fpn_1x_coco_20220630_173841.log.json)                 |\n\n## Pre-trained Models\n\nWe also train some models with longer schedules and multi-scale training. The users could finetune them for downstream tasks.\n\n|                             Backbone                             |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                         Config                          |                                                                                                                                                                                                    Download                                                                                                                                                                                                     |\n| :--------------------------------------------------------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     [R-50-FPN](./mask-rcnn_r50-caffe_fpn_ms-poly-2x_coco.py)     |  caffe  |   2x    |   4.3    |                |  40.3  |  36.5   | [config](./mask-rcnn_r50-caffe_fpn_ms-poly-2x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_2x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_2x_coco_bbox_mAP-0.403__segm_mAP-0.365_20200504_231822-a75c98ce.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_2x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_2x_coco_20200504_231822.log.json) |\n|     [R-50-FPN](./mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco.py)     |  caffe  |   3x    |   4.3    |                |  40.8  |  37.0   | [config](./mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_20200504_163245.log.json)  |\n|        [R-50-FPN](./mask-rcnn_r50_fpn_ms-poly-3x_coco.py)        | pytorch |   3x    |   4.1    |                |  40.9  |  37.1   |    [config](./mask-rcnn_r50_fpn_ms-poly-3x_coco.py)     |                            [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_fpn_mstrain-poly_3x_coco_20210524_201154-21b550bb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_fpn_mstrain-poly_3x_coco_20210524_201154.log.json)                             |\n|    [R-101-FPN](./mask-rcnn_r101-caffe_fpn_ms-poly-3x_coco.py)    |  caffe  |   3x    |   5.9    |                |  42.9  |  38.5   | [config](./mask-rcnn_r101-caffe_fpn_ms-poly-3x_coco.py) |                   [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r101_caffe_fpn_mstrain-poly_3x_coco_20210526_132339-3c33ce02.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn_r101_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r101_caffe_fpn_mstrain-poly_3x_coco_20210526_132339.log.json)                    |\n|       [R-101-FPN](./mask-rcnn_r101_fpn_ms-poly-3x_coco.py)       | pytorch |   3x    |   6.1    |                |  42.7  |  38.5   |    [config](./mask-rcnn_r101_fpn_ms-poly-3x_coco.py)    |                          [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_mstrain-poly_3x_coco/mask_rcnn_r101_fpn_mstrain-poly_3x_coco_20210524_200244-5675c317.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_mstrain-poly_3x_coco/mask_rcnn_r101_fpn_mstrain-poly_3x_coco_20210524_200244.log.json)                           |\n| [x101-32x4d-FPN](./mask-rcnn_x101-32x4d_fpn_ms-poly-3x_coco.py)  | pytorch |   3x    |   7.3    |                |  43.6  |  39.0   | [config](./mask-rcnn_x101-32x4d_fpn_ms-poly-3x_coco.py) |              [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_32x4d_fpn_mstrain-poly_3x_coco_20210524_201410-abcd7859.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_32x4d_fpn_mstrain-poly_3x_coco_20210524_201410.log.json)               |\n| [X-101-32x8d-FPN](./mask-rcnn_x101-32x8d_fpn_ms-poly-3x_coco.py) | pytorch |   1x    |   10.4   |                |  43.4  |  39.0   | [config](./mask-rcnn_x101-32x8d_fpn_ms-poly-1x_coco.py) |              [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_mstrain-poly_1x_coco/mask_rcnn_x101_32x8d_fpn_mstrain-poly_1x_coco_20220630_170346-b4637974.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_mstrain-poly_1x_coco/mask_rcnn_x101_32x8d_fpn_mstrain-poly_1x_coco_20220630_170346.log.json)               |\n| [X-101-32x8d-FPN](./mask-rcnn_x101-32x8d_fpn_ms-poly-3x_coco.py) | pytorch |   3x    |   10.3   |                |  44.3  |  39.5   | [config](./mask-rcnn_x101-32x8d_fpn_ms-poly-3x_coco.py) |              [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_32x8d_fpn_mstrain-poly_3x_coco_20210607_161042-8bd2c639.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_32x8d_fpn_mstrain-poly_3x_coco_20210607_161042.log.json)               |\n| [X-101-64x4d-FPN](./mask-rcnn_x101-64x4d_fpn_ms-poly_3x_coco.py) | pytorch |   3x    |   10.4   |                |  44.5  |  39.7   | [config](./mask-rcnn_x101-64x4d_fpn_ms-poly_3x_coco.py) |              [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco_20210526_120447-c376f129.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco_20210526_120447.log.json)               |\n\n## Citation\n\n```latex\n@article{He_2017,\n   title={Mask R-CNN},\n   journal={2017 IEEE International Conference on Computer Vision (ICCV)},\n   publisher={IEEE},\n   author={He, Kaiming and Gkioxari, Georgia and Dollar, Piotr and Girshick, Ross},\n   year={2017},\n   month={Oct}\n}\n```\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r101-caffe_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        depth=101,\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r101_fpn_2x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r101_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r101_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r18_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50-caffe-c4_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50-caffe-c4.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-1x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-2x_coco.py",
    "content": "_base_ = './mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'\n\ntrain_cfg = dict(max_epochs=24)\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = './mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'\n\ntrain_cfg = dict(max_epochs=36)\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_poly-1x_coco_v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    rpn_head=dict(\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=7,\n                sampling_ratio=2,\n                aligned=False)),\n        bbox_head=dict(\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n        mask_roi_extractor=dict(\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=14,\n                sampling_ratio=2,\n                aligned=False))))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50_fpn_1x-wandb_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nvis_backends = [dict(type='LocalVisBackend'), dict(type='WandbVisBackend')]\nvisualizer = dict(vis_backends=vis_backends)\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# default_hooks = dict(checkpoint=dict(interval=4))\n_base_.default_hooks.checkpoint.interval = 4\n\n# train_cfg = dict(val_interval=2)\n_base_.train_cfg.val_interval = 2\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50_fpn_2x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../common/lsj-100e_coco-instance.py'\n]\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\n\nmodel = dict(data_preprocessor=dict(batch_augments=batch_augments))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.02 * 4, momentum=0.9, weight_decay=0.00004))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50_fpn_amp-1x_coco.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(type='AmpOptimWrapper')\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_r50_fpn_poly-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_r101_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-32x4d_fpn_2x_coco.py",
    "content": "_base_ = './mask-rcnn_r101_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-32x4d_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-32x8d_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_r101_fpn_1x_coco.py'\n\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-32x8d_fpn_ms-poly-1x_coco.py",
    "content": "_base_ = './mask-rcnn_r101_fpn_1x_coco.py'\n\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-32x8d_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_x101-32x4d_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-64x4d_fpn_2x_coco.py",
    "content": "_base_ = './mask-rcnn_x101-32x4d_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/mask_rcnn/mask-rcnn_x101-64x4d_fpn_ms-poly_3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/mask_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Mask R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Softmax\n        - RPN\n        - Convolution\n        - Dense Connections\n        - FPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/abs/1703.06870v3\n      Title: \"Mask R-CNN\"\n    README: configs/mask_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/mask_rcnn.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: mask-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.3\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 34.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_1x_coco/mask_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.38__segm_mAP-0.344_20200504_231812-0ebd1859.pth\n\n  - Name: mask-rcnn_r50_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      inference time (ms/im):\n        - value: 62.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 34.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth\n\n  - Name: mask-rcnn_r50_fpn_fp16_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r50_fpn_amp-1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.6\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n        - Mixed Precision Training\n      inference time (ms/im):\n        - value: 41.49\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP16\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 34.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fp16/mask_rcnn_r50_fpn_fp16_1x_coco/mask_rcnn_r50_fpn_fp16_1x_coco_20200205-59faf7e4.pth\n\n  - Name: mask-rcnn_r50_fpn_2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r50_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      inference time (ms/im):\n        - value: 62.11\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 35.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_2x_coco/mask_rcnn_r50_fpn_2x_coco_bbox_mAP-0.392__segm_mAP-0.354_20200505_003907-3e542a40.pth\n\n  - Name: mask-rcnn_r101-caffe_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_caffe_fpn_1x_coco/mask_rcnn_r101_caffe_fpn_1x_coco_20200601_095758-805e06c1.pth\n\n  - Name: mask-rcnn_r101_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      inference time (ms/im):\n        - value: 74.07\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_1x_coco/mask_rcnn_r101_fpn_1x_coco_20200204-1efe0ed5.pth\n\n  - Name: mask-rcnn_r101_fpn_2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r101_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      inference time (ms/im):\n        - value: 74.07\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_2x_coco/mask_rcnn_r101_fpn_2x_coco_bbox_mAP-0.408__segm_mAP-0.366_20200505_071027-14b391c7.pth\n\n  - Name: mask-rcnn_x101-32x4d_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      inference time (ms/im):\n        - value: 88.5\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_1x_coco/mask_rcnn_x101_32x4d_fpn_1x_coco_20200205-478d0b67.pth\n\n  - Name: mask-rcnn_x101-32x4d_fpn_2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-32x4d_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      inference time (ms/im):\n        - value: 88.5\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_2x_coco/mask_rcnn_x101_32x4d_fpn_2x_coco_bbox_mAP-0.422__segm_mAP-0.378_20200506_004702-faef898c.pth\n\n  - Name: mask-rcnn_x101-64x4d_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.7\n      inference time (ms/im):\n        - value: 125\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_1x_coco/mask_rcnn_x101_64x4d_fpn_1x_coco_20200201-9352eb0d.pth\n\n  - Name: mask-rcnn_x101-64x4d_fpn_2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-64x4d_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.7\n      inference time (ms/im):\n        - value: 125\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_2x_coco/mask_rcnn_x101_64x4d_fpn_2x_coco_20200509_224208-39d6f70c.pth\n\n  - Name: mask-rcnn_x101-32x8d_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-32x8d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.6\n      Epochs: 12\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 42.8\n    - Task: Instance Segmentation\n      Dataset: COCO\n      Metrics:\n        mask AP: 38.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_1x_coco/mask_rcnn_x101_32x8d_fpn_1x_coco_20220630_173841-0aaf329e.pth\n\n  - Name: mask-rcnn_r50-caffe_fpn_ms-poly-2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-2x_coco.py\n    Metadata:\n      Training Memory (GB): 4.3\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_2x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_2x_coco_bbox_mAP-0.403__segm_mAP-0.365_20200504_231822-a75c98ce.pth\n\n  - Name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.3\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth\n\n  - Name: mask-rcnn_r50_fpn_mstrain-poly_3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r50_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.1\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_fpn_mstrain-poly_3x_coco_20210524_201154-21b550bb.pth\n\n  - Name: mask-rcnn_r101_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r101_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 6.1\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_mstrain-poly_3x_coco/mask_rcnn_r101_fpn_mstrain-poly_3x_coco_20210524_200244-5675c317.pth\n\n  - Name: mask-rcnn_r101-caffe_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_r101-caffe_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.9\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r101_caffe_fpn_mstrain-poly_3x_coco_20210526_132339-3c33ce02.pth\n\n  - Name: mask-rcnn_x101-32x4d_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-32x4d_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.3\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_32x4d_fpn_mstrain-poly_3x_coco_20210524_201410-abcd7859.pth\n\n  - Name: mask-rcnn_x101-32x8d_fpn_ms-poly-1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-32x8d_fpn_ms-poly-1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.4\n      Epochs: 12\n    Results:\n    - Task: Object Detection\n      Dataset: COCO\n      Metrics:\n        box AP: 43.4\n    - Task: Instance Segmentation\n      Dataset: COCO\n      Metrics:\n        mask AP: 39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_mstrain-poly_1x_coco/mask_rcnn_x101_32x8d_fpn_mstrain-poly_1x_coco_20220630_170346-b4637974.pth\n\n  - Name: mask-rcnn_x101-32x8d_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-32x8d_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 10.3\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x8d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_32x8d_fpn_mstrain-poly_3x_coco_20210607_161042-8bd2c639.pth\n\n  - Name: mask-rcnn_x101-64x4d_fpn_ms-poly_3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/mask_rcnn/mask-rcnn_x101-64x4d_fpn_ms-poly_3x_coco.py\n    Metadata:\n      Epochs: 36\n      Training Memory (GB): 10.4\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco/mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco_20210526_120447-c376f129.pth\n"
  },
  {
    "path": "configs/maskformer/README.md",
    "content": "# MaskFormer\n\n> [Per-Pixel Classification is Not All You Need for Semantic Segmentation](https://arxiv.org/abs/2107.06278)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nModern approaches typically formulate semantic segmentation as a per-pixel classification task, while instance-level segmentation is handled with an alternative mask classification. Our key insight: mask classification is sufficiently general to solve both semantic- and instance-level segmentation tasks in a unified manner using the exact same model, loss, and training procedure. Following this observation, we propose MaskFormer, a simple mask classification model which predicts a set of binary masks, each associated with a single global class label prediction. Overall, the proposed mask classification-based method simplifies the landscape of effective approaches to semantic and panoptic  segmentation tasks and shows excellent empirical results. In particular, we observe that MaskFormer outperforms per-pixel classification baselines when the number of classes is large. Our mask classification-based method outperforms both current state-of-the-art semantic (55.6 mIoU on ADE20K) and panoptic segmentation (52.7 PQ on COCO) models.\n\n<div align=center>\n<img src=\"https://camo.githubusercontent.com/29fb22298d506ce176caad3006a7b05ef2603ca12cece6c788b7e73c046e8bc9/68747470733a2f2f626f77656e63303232312e6769746875622e696f2f696d616765732f6d61736b666f726d65722e706e67\" height=\"300\"/>\n</div>\n\n## Introduction\n\nMaskFormer requires COCO and [COCO-panoptic](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip) dataset for training and evaluation. You need to download and extract it in the COCO dataset path.\nThe directory should be like this.\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── panoptic_train2017.json\n│   │   │   ├── panoptic_train2017\n│   │   │   ├── panoptic_val2017.json\n│   │   │   ├── panoptic_val2017\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n```\n\n## Results and Models\n\n| Backbone |  style  | Lr schd | Mem (GB) | Inf time (fps) |   PQ   |   SQ   |   RQ   | PQ_th  | SQ_th  | RQ_th  | PQ_st  | SQ_st  | RQ_st  |                           Config                           |                                                                                                                                                                                        Download                                                                                                                                                                                        |\n| :------: | :-----: | :-----: | :------: | :------------: | :----: | :----: | :----: | :----: | :----: | :----: | :----: | :----: | :----: | :--------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | pytorch |   75e   |   16.2   |       -        | 46.757 | 80.297 | 57.176 | 50.829 | 81.125 | 61.798 | 40.610 | 79.048 | 50.199 |      [config](./maskformer_r50_ms-16xb1-75e_coco.py)       |                           [model](https://download.openmmlab.com/mmdetection/v3.0/maskformer/maskformer_r50_ms-16xb1-75e_coco/maskformer_r50_ms-16xb1-75e_coco_20230116_095226-baacd858.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/maskformer/maskformer_r50_ms-16xb1-75e_coco/maskformer_r50_ms-16xb1-75e_coco_20230116_095226.log.json)                           |\n|  Swin-L  | pytorch |  300e   |   27.2   |       -        | 53.249 | 81.704 | 64.231 | 58.798 | 82.923 | 70.282 | 44.874 | 79.863 | 55.097 | [config](./maskformer_swin-l-p4-w12_64xb1-ms-300e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/maskformer/maskformer_swin-l-p4-w12_64xb1-ms-300e_coco/maskformer_swin-l-p4-w12_64xb1-ms-300e_coco_20220326_221612-c63ab967.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/maskformer/maskformer_swin-l-p4-w12_mstrain_64x1_300e_coco/maskformer_swin-l-p4-w12_mstrain_64x1_300e_coco_20220326_221612.log.json) |\n\n### Note\n\n1. The `R-50` version was mentioned in Table XI, in paper [Masked-attention Mask Transformer for Universal Image Segmentation](https://arxiv.org/abs/2112.01527).\n2. The models were trained with mmdet 2.x and have been converted for mmdet 3.x.\n\n## Citation\n\n```latex\n@inproceedings{cheng2021maskformer,\n  title={Per-Pixel Classification is Not All You Need for Semantic Segmentation},\n  author={Bowen Cheng and Alexander G. Schwing and Alexander Kirillov},\n  journal={NeurIPS},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_panoptic.py', '../_base_/default_runtime.py'\n]\n\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True,\n    pad_size_divisor=1,\n    pad_mask=True,\n    mask_pad_value=0,\n    pad_seg=True,\n    seg_pad_value=255)\n\nnum_things_classes = 80\nnum_stuff_classes = 53\nnum_classes = num_things_classes + num_stuff_classes\nmodel = dict(\n    type='MaskFormer',\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=-1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    panoptic_head=dict(\n        type='MaskFormerHead',\n        in_channels=[256, 512, 1024, 2048],  # pass to pixel_decoder inside\n        feat_channels=256,\n        out_channels=256,\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes,\n        num_queries=100,\n        pixel_decoder=dict(\n            type='TransformerEncoderPixelDecoder',\n            norm_cfg=dict(type='GN', num_groups=32),\n            act_cfg=dict(type='ReLU'),\n            encoder=dict(  # DetrTransformerEncoder\n                num_layers=6,\n                layer_cfg=dict(  # DetrTransformerEncoderLayer\n                    self_attn_cfg=dict(  # MultiheadAttention\n                        embed_dims=256,\n                        num_heads=8,\n                        dropout=0.1,\n                        batch_first=True),\n                    ffn_cfg=dict(\n                        embed_dims=256,\n                        feedforward_channels=2048,\n                        num_fcs=2,\n                        ffn_drop=0.1,\n                        act_cfg=dict(type='ReLU', inplace=True)))),\n            positional_encoding=dict(num_feats=128, normalize=True)),\n        enforce_decoder_input_project=False,\n        positional_encoding=dict(num_feats=128, normalize=True),\n        transformer_decoder=dict(  # DetrTransformerDecoder\n            num_layers=6,\n            layer_cfg=dict(  # DetrTransformerDecoderLayer\n                self_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.1,\n                    batch_first=True),\n                cross_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.1,\n                    batch_first=True),\n                ffn_cfg=dict(\n                    embed_dims=256,\n                    feedforward_channels=2048,\n                    num_fcs=2,\n                    ffn_drop=0.1,\n                    act_cfg=dict(type='ReLU', inplace=True))),\n            return_intermediate=True),\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=False,\n            loss_weight=1.0,\n            reduction='mean',\n            class_weight=[1.0] * num_classes + [0.1]),\n        loss_mask=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            reduction='mean',\n            loss_weight=20.0),\n        loss_dice=dict(\n            type='DiceLoss',\n            use_sigmoid=True,\n            activate=True,\n            reduction='mean',\n            naive_dice=True,\n            eps=1.0,\n            loss_weight=1.0)),\n    panoptic_fusion_head=dict(\n        type='MaskFormerFusionHead',\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes,\n        loss_panoptic=None,\n        init_cfg=None),\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='ClassificationCost', weight=1.0),\n                dict(type='FocalLossCost', weight=20.0, binary_input=True),\n                dict(type='DiceCost', weight=1.0, pred_act=True, eps=1.0)\n            ]),\n        sampler=dict(type='MaskPseudoSampler')),\n    test_cfg=dict(\n        panoptic_on=True,\n        # For now, the dataset does not support\n        # evaluating semantic segmentation metric.\n        semantic_on=False,\n        instance_on=False,\n        # max_per_image is for instance segmentation.\n        max_per_image=100,\n        object_mask_thr=0.8,\n        iou_thr=0.8,\n        # In MaskFormer's panoptic postprocessing,\n        # it will not filter masks whose score is smaller than 0.5 .\n        filter_low_score=False),\n    init_cfg=None)\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(\n        type='LoadPanopticAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        with_seg=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    batch_size=1, num_workers=1, dataset=dict(pipeline=train_pipeline))\n\nval_dataloader = dict(batch_size=1, num_workers=1)\n\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,\n        weight_decay=0.0001,\n        eps=1e-8,\n        betas=(0.9, 0.999)),\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n            'query_embed': dict(lr_mult=1.0, decay_mult=0.0)\n        },\n        norm_decay_mult=0.0),\n    clip_grad=dict(max_norm=0.01, norm_type=2))\n\nmax_epochs = 75\n\n# learning rate\nparam_scheduler = dict(\n    type='MultiStepLR',\n    begin=0,\n    end=max_epochs,\n    by_epoch=True,\n    milestones=[50],\n    gamma=0.1)\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (16 GPUs) x (1 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/maskformer/maskformer_swin-l-p4-w12_64xb1-ms-300e_coco.py",
    "content": "_base_ = './maskformer_r50_ms-16xb1-75e_coco.py'\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\ndepths = [2, 2, 18, 2]\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        patch_size=4,\n        window_size=12,\n        mlp_ratio=4,\n        depths=depths,\n        num_heads=[6, 12, 24, 48],\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    panoptic_head=dict(\n        in_channels=[192, 384, 768, 1536],  # pass to pixel_decoder inside\n        pixel_decoder=dict(\n            _delete_=True,\n            type='PixelDecoder',\n            norm_cfg=dict(type='GN', num_groups=32),\n            act_cfg=dict(type='ReLU')),\n        enforce_decoder_input_project=True))\n\n# optimizer\n\n# weight_decay = 0.01\n# norm_weight_decay = 0.0\n# embed_weight_decay = 0.0\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\nnorm_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'norm': norm_multi,\n    'absolute_pos_embed': embed_multi,\n    'relative_position_bias_table': embed_multi,\n    'query_embed': embed_multi\n}\n\noptim_wrapper = dict(\n    optimizer=dict(lr=6e-5, weight_decay=0.01),\n    paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0))\n\nmax_epochs = 300\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=1e-6, by_epoch=False, begin=0, end=1500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[250],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (64 GPUs) x (1 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/maskformer/metafile.yml",
    "content": "Collections:\n  - Name: MaskFormer\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Weight Decay\n      Training Resources: 16x V100 GPUs\n      Architecture:\n        - MaskFormer\n    Paper:\n      URL: https://arxiv.org/pdf/2107.06278\n      Title: 'Per-Pixel Classification is Not All You Need for Semantic Segmentation'\n    README: configs/maskformer/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.22.0/mmdet/models/detectors/maskformer.py#L7\n      Version: v2.22.0\n\nModels:\n  - Name: maskformer_r50_ms-16xb1-75e_coco\n    In Collection: MaskFormer\n    Config: configs/maskformer/maskformer_r50_ms-16xb1-75e_coco.py\n    Metadata:\n      Training Memory (GB): 16.2\n      Epochs: 75\n    Results:\n    - Task: Panoptic Segmentation\n      Dataset: COCO\n      Metrics:\n        PQ: 46.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/maskformer/maskformer_r50_ms-16xb1-75e_coco/maskformer_r50_ms-16xb1-75e_coco_20230116_095226-baacd858.pth\n  - Name: maskformer_swin-l-p4-w12_64xb1-ms-300e_coco\n    In Collection: MaskFormer\n    Config: configs/maskformer/maskformer_swin-l-p4-w12_64xb1-ms-300e_coco.py\n    Metadata:\n      Training Memory (GB): 27.2\n      Epochs: 300\n    Results:\n    - Task: Panoptic Segmentation\n      Dataset: COCO\n      Metrics:\n        PQ: 53.2\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/maskformer/maskformer_swin-l-p4-w12_64xb1-ms-300e_coco/maskformer_swin-l-p4-w12_64xb1-ms-300e_coco_20220326_221612-c63ab967.pth\n"
  },
  {
    "path": "configs/masktrack_rcnn/README.md",
    "content": "# Video Instance Segmentation\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nIn this paper we present a new computer vision task, named video instance segmentation. The goal of this new task is simultaneous detection, segmentation and tracking of instances in videos. In words, it is the first time that the image instance segmentation problem is extended to the video domain. To facilitate research on this new task, we propose a large-scale benchmark called YouTube-VIS, which consists of 2883 high-resolution YouTube videos, a 40-category label set and 131k high-quality instance masks. In addition, we propose a novel algorithm called MaskTrack R-CNN for this task. Our new method introduces a new tracking branch to Mask R-CNN to jointly perform the detection, segmentation and tracking tasks simultaneously. Finally, we evaluate the proposed method and several strong baselines on our new dataset. Experimental results clearly demonstrate the advantages of the proposed algorithm and reveal insight for future improvement. We believe the video instance segmentation task will motivate the community along the line of research for video understanding.\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/34888372/142986554-4f6a2630-92bc-43b4-8509-5173be00402d.png\"/>\n</div>\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@inproceedings{yang2019video,\n  title={Video instance segmentation},\n  author={Yang, Linjie and Fan, Yuchen and Xu, Ning},\n  booktitle={Proceedings of the IEEE/CVF International Conference on Computer Vision},\n  pages={5188--5197},\n  year={2019}\n}\n```\n\n## Results and models of MaskTrack R-CNN on YouTube-VIS 2019 validation dataset\n\nAs mentioned in [Issues #6](https://github.com/youtubevos/MaskTrackRCNN/issues/6#issuecomment-502503505) in MaskTrack R-CNN, the result is kind of unstable for different trials, which ranges from 28 AP to 31 AP when using R-50-FPN as backbone.\nThe checkpoint provided below is the best one from two experiments.\n\n|     Method      | Base detector | Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) |  AP  |                                 Config                                 |                                                                                                                                                                                    Download                                                                                                                                                                                    |\n| :-------------: | :-----------: | :-------: | :-----: | :-----: | :------: | :------------: | :--: | :--------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| MaskTrack R-CNN |  Mask R-CNN   | R-50-FPN  | pytorch |   12e   |   1.61   |       -        | 30.2 | [config](masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py)  |   [model](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r50_fpn_12e_youtubevis2019/masktrack_rcnn_r50_fpn_12e_youtubevis2019_20211022_194830-6ca6b91e.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r50_fpn_12e_youtubevis2019/masktrack_rcnn_r50_fpn_12e_youtubevis2019_20211022_194830.log.json)   |\n| MaskTrack R-CNN |  Mask R-CNN   | R-101-FPN | pytorch |   12e   |   2.27   |       -        | 32.2 | [config](masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2019.py) | [model](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r101_fpn_12e_youtubevis2019/masktrack_rcnn_r101_fpn_12e_youtubevis2019_20211023_150038-454dc48b.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r101_fpn_12e_youtubevis2019/masktrack_rcnn_r101_fpn_12e_youtubevis2019_20211023_150038.log.json) |\n| MaskTrack R-CNN |  Mask R-CNN   | X-101-FPN | pytorch |   12e   |   3.69   |       -        | 34.7 | [config](masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2019.py) | [model](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_x101_fpn_12e_youtubevis2019/masktrack_rcnn_x101_fpn_12e_youtubevis2019_20211023_153205-fff7a102.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_x101_fpn_12e_youtubevis2019/masktrack_rcnn_x101_fpn_12e_youtubevis2019_20211023_153205.log.json) |\n\n## Results and models of MaskTrack R-CNN on YouTube-VIS 2021 validation dataset\n\nThe checkpoint provided below is the best one from two experiments.\n\n|     Method      | Base detector | Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) |  AP  |                                 Config                                 |                                                                                                                                                                                    Download                                                                                                                                                                                    |\n| :-------------: | :-----------: | :-------: | :-----: | :-----: | :------: | :------------: | :--: | :--------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| MaskTrack R-CNN |  Mask R-CNN   | R-50-FPN  | pytorch |   12e   |   1.61   |       -        | 28.7 | [config](masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py)  |   [model](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r50_fpn_12e_youtubevis2021/masktrack_rcnn_r50_fpn_12e_youtubevis2021_20211026_044948-10da90d9.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r50_fpn_12e_youtubevis2021/masktrack_rcnn_r50_fpn_12e_youtubevis2021_20211026_044948.log.json)   |\n| MaskTrack R-CNN |  Mask R-CNN   | R-101-FPN | pytorch |   12e   |   2.27   |       -        | 31.3 | [config](masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2021.py) | [model](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r101_fpn_12e_youtubevis2021/masktrack_rcnn_r101_fpn_12e_youtubevis2021_20211026_045509-3c49e4f3.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r101_fpn_12e_youtubevis2021/masktrack_rcnn_r101_fpn_12e_youtubevis2021_20211026_045509.log.json) |\n| MaskTrack R-CNN |  Mask R-CNN   | X-101-FPN | pytorch |   12e   |   3.69   |       -        | 33.5 | [config](masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2021.py) | [model](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_x101_fpn_12e_youtubevis2021/masktrack_rcnn_x101_fpn_12e_youtubevis2021_20211026_095943-90831df4.pth) \\| [log](https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_x101_fpn_12e_youtubevis2021/masktrack_rcnn_x101_fpn_12e_youtubevis2021_20211026_095943.log.json) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nDue to the influence of parameters such as learning rate in default configuration file, we recommend using 8 GPUs for training in order to reproduce accuracy. You can use the following command to start the training.\n\n```shell\n# Training MaskTrack R-CNN on YouTube-VIS-2021 dataset with following command.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_train.sh configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py 8\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 4. Testing and evaluation\n\nIf you want to get the results of the [YouTube-VOS](https://youtube-vos.org/dataset/vis/) val/test set, please use the following command to generate result files that can be used for submission. It will be stored in `./youtube_vis_results.submission_file.zip`, you can modify the saved path in `test_evaluator` of the config.\n\n```shell\n# The number after config file represents the number of GPUs used.\nbash tools/dist_test_tracking.sh configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py 8 --checkpoint ${CHECKPOINT_PATH}\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 5.Inference\n\nUse a single GPU to predict a video and save it as a video.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py  --checkpoint {CHECKPOINT_PATH} --out vis.mp4\n```\n\nIf you want to know about more detailed usage of `mot_demo.py`, please refer to this [document](../../docs/en/user_guides/tracking_inference.md).\n"
  },
  {
    "path": "configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2019.py",
    "content": "_base_ = ['./masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py']\nmodel = dict(\n    detector=dict(\n        backbone=dict(\n            depth=101,\n            init_cfg=dict(\n                type='Pretrained', checkpoint='torchvision://resnet101')),\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint=  # noqa: E251\n            'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_1x_coco/mask_rcnn_r101_fpn_1x_coco_20200204-1efe0ed5.pth'  # noqa: E501\n        )))\n"
  },
  {
    "path": "configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2021.py",
    "content": "_base_ = ['./masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py']\nmodel = dict(\n    detector=dict(\n        backbone=dict(\n            depth=101,\n            init_cfg=dict(\n                type='Pretrained', checkpoint='torchvision://resnet101')),\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint=  # noqa: E251\n            'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_1x_coco/mask_rcnn_r101_fpn_1x_coco_20200204-1efe0ed5.pth'  # noqa: E501\n        )))\n\ndata_root = 'data/youtube_vis_2021/'\ndataset_version = data_root[-5:-1]\n\n# dataloader\ntrain_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_train.json'))\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_valid.json'))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/youtube_vis.py', '../_base_/default_runtime.py'\n]\n\ndetector = _base_.model\ndetector.pop('data_preprocessor')\ndetector.roi_head.bbox_head.update(dict(num_classes=40))\ndetector.roi_head.mask_head.update(dict(num_classes=40))\ndetector.train_cfg.rpn.sampler.update(dict(num=64))\ndetector.train_cfg.rpn_proposal.update(dict(nms_pre=200, max_per_img=200))\ndetector.train_cfg.rcnn.sampler.update(dict(num=128))\ndetector.test_cfg.rpn.update(dict(nms_pre=200, max_per_img=200))\ndetector.test_cfg.rcnn.update(dict(score_thr=0.01))\ndetector['init_cfg'] = dict(\n    type='Pretrained',\n    checkpoint=  # noqa: E251\n    'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth'  # noqa: E501\n)\ndel _base_.model\n\nmodel = dict(\n    type='MaskTrackRCNN',\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    detector=detector,\n    track_head=dict(\n        type='RoITrackHead',\n        roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        embed_head=dict(\n            type='RoIEmbedHead',\n            num_fcs=2,\n            roi_feat_size=7,\n            in_channels=256,\n            fc_out_channels=1024),\n        train_cfg=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=128,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            pos_weight=-1,\n            debug=False)),\n    tracker=dict(\n        type='MaskTrackRCNNTracker',\n        match_weights=dict(det_score=1.0, iou=2.0, det_label=10.0),\n        num_frames_retain=20))\n\ndataset_type = 'YouTubeVISDataset'\ndata_root = 'data/youtube_vis_2019/'\ndataset_version = data_root[-5:-1]  # 2019 or 2021\n\n# train_dataloader\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='TrackImgSampler'),  # image-based sampling\n    batch_sampler=dict(type='TrackAspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2019_train.json',\n        data_prefix=dict(img_path='train/JPEGImages'),\n        pipeline=_base_.train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.00125, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning policy\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3.0,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# visualizer\ndefault_hooks = dict(\n    visualization=dict(type='TrackVisualizationHook', draw=False))\n\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='TrackLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\n# runtime settings\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_begin=13)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# evaluator\nval_evaluator = dict(\n    type='YouTubeVISMetric',\n    metric='youtube_vis_ap',\n    outfile_prefix='./youtube_vis_results',\n    format_only=True)\ntest_evaluator = val_evaluator\n\ndel detector\n"
  },
  {
    "path": "configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py",
    "content": "_base_ = ['./masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py']\n\ndata_root = 'data/youtube_vis_2021/'\ndataset_version = data_root[-5:-1]\n\n# dataloader\ntrain_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_train.json'))\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_valid.json'))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2019.py",
    "content": "_base_ = ['./masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py']\nmodel = dict(\n    detector=dict(\n        backbone=dict(\n            type='ResNeXt',\n            depth=101,\n            groups=64,\n            base_width=4,\n            init_cfg=dict(\n                type='Pretrained',\n                checkpoint='open-mmlab://resnext101_64x4d')),\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint=  # noqa: E251\n            'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_1x_coco/mask_rcnn_x101_64x4d_fpn_1x_coco_20200201-9352eb0d.pth'  # noqa: E501\n        )))\n"
  },
  {
    "path": "configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2021.py",
    "content": "_base_ = ['./masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py']\nmodel = dict(\n    detector=dict(\n        backbone=dict(\n            type='ResNeXt',\n            depth=101,\n            groups=64,\n            base_width=4,\n            init_cfg=dict(\n                type='Pretrained',\n                checkpoint='open-mmlab://resnext101_64x4d')),\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint=  # noqa: E251\n            'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_64x4d_fpn_1x_coco/mask_rcnn_x101_64x4d_fpn_1x_coco_20200201-9352eb0d.pth'  # noqa: E501\n        )))\n\ndata_root = 'data/youtube_vis_2021/'\ndataset_version = data_root[-5:-1]\n\n# dataloader\ntrain_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_train.json'))\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        dataset_version=dataset_version,\n        ann_file='annotations/youtube_vis_2021_valid.json'))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/masktrack_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: MaskTrack R-CNN\n    Metadata:\n      Training Techniques:\n        - SGD with Momentum\n      Training Resources: 8x TiTanXP GPUs\n      Architecture:\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/pdf/1905.04804.pdf\n      Title: Video Instance Segmentation\n    README: configs/masktrack_rcnn/README.md\n\nModels:\n  - Name: masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019\n    In Collection: MaskTrack R-CNN\n    Config: configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py\n    Metadata:\n      Training Data: YouTube-VIS 2019\n      Training Memory (GB): 1.16\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2019\n        Metrics:\n          AP: 30.2\n    Weights: https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r50_fpn_12e_youtubevis2019/masktrack_rcnn_r50_fpn_12e_youtubevis2019_20211022_194830-6ca6b91e.pth\n\n  - Name: masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2019\n    In Collection: MaskTrack R-CNN\n    Config: configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2019.py\n    Metadata:\n      Training Data: YouTube-VIS 2019\n      Training Memory (GB): 2.27\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2019\n        Metrics:\n          AP: 32.2\n    Weights: https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r101_fpn_12e_youtubevis2019/masktrack_rcnn_r101_fpn_12e_youtubevis2019_20211023_150038-454dc48b.pth\n\n  - Name: masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2019\n    In Collection: MaskTrack R-CNN\n    Config: configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2019.py\n    Metadata:\n      Training Data: YouTube-VIS 2019\n      Training Memory (GB): 3.69\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2019\n        Metrics:\n          AP: 34.7\n    Weights: https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_x101_fpn_12e_youtubevis2019/masktrack_rcnn_x101_fpn_12e_youtubevis2019_20211023_153205-fff7a102.pth\n\n  - Name: masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021\n    In Collection: MaskTrack R-CNN\n    Config: configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2021.py\n    Metadata:\n      Training Data: YouTube-VIS 2021\n      Training Memory (GB): 1.16\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2021\n        Metrics:\n          AP: 28.7\n    Weights: https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r50_fpn_12e_youtubevis2021/masktrack_rcnn_r50_fpn_12e_youtubevis2021_20211026_044948-10da90d9.pth\n\n  - Name: masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2021\n    In Collection: MaskTrack R-CNN\n    Config: configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_r101_fpn_8xb1-12e_youtubevis2021.py\n    Metadata:\n      Training Data: YouTube-VIS 2021\n      Training Memory (GB): 2.27\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2021\n        Metrics:\n          AP: 31.3\n    Weights: https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_r101_fpn_12e_youtubevis2021/masktrack_rcnn_r101_fpn_12e_youtubevis2021_20211026_045509-3c49e4f3.pth\n\n  - Name: masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2021\n    In Collection: MaskTrack R-CNN\n    Config: configs/masktrack_rcnn/masktrack-rcnn_mask-rcnn_x101_fpn_8xb1-12e_youtubevis2021.py\n    Metadata:\n      Training Data: YouTube-VIS 2021\n      Training Memory (GB): 3.69\n    Results:\n      - Task: Video Instance Segmentation\n        Dataset: YouTube-VIS 2021\n        Metrics:\n          AP: 33.5\n    Weights: https://download.openmmlab.com/mmtracking/vis/masktrack_rcnn/masktrack_rcnn_x101_fpn_12e_youtubevis2021/masktrack_rcnn_x101_fpn_12e_youtubevis2021_20211026_095943-90831df4.pth\n"
  },
  {
    "path": "configs/misc/d2_faster-rcnn_r50-caffe_fpn_ms-90k_coco.py",
    "content": "_base_ = '../common/ms-90k_coco.py'\n\n# model settings\nmodel = dict(\n    type='Detectron2Wrapper',\n    bgr_to_rgb=False,\n    detector=dict(\n        # The settings in `d2_detector` will merged into default settings\n        # in detectron2. More details please refer to\n        # https://github.com/facebookresearch/detectron2/blob/main/detectron2/config/defaults.py    # noqa\n        meta_architecture='GeneralizedRCNN',\n        # If you want to finetune the detector, you can use the\n        # checkpoint released by detectron2, for example:\n        # weights='detectron2://COCO-Detection/faster_rcnn_R_50_FPN_1x/137257794/model_final_b275ba.pkl'     # noqa\n        weights='detectron2://ImageNetPretrained/MSRA/R-50.pkl',\n        mask_on=False,\n        pixel_mean=[103.530, 116.280, 123.675],\n        pixel_std=[1.0, 1.0, 1.0],\n        backbone=dict(name='build_resnet_fpn_backbone', freeze_at=2),\n        resnets=dict(\n            depth=50,\n            out_features=['res2', 'res3', 'res4', 'res5'],\n            num_groups=1,\n            norm='FrozenBN'),\n        fpn=dict(\n            in_features=['res2', 'res3', 'res4', 'res5'], out_channels=256),\n        anchor_generator=dict(\n            name='DefaultAnchorGenerator',\n            sizes=[[32], [64], [128], [256], [512]],\n            aspect_ratios=[[0.5, 1.0, 2.0]],\n            angles=[[-90, 0, 90]]),\n        proposal_generator=dict(name='RPN'),\n        rpn=dict(\n            head_name='StandardRPNHead',\n            in_features=['p2', 'p3', 'p4', 'p5', 'p6'],\n            iou_thresholds=[0.3, 0.7],\n            iou_labels=[0, -1, 1],\n            batch_size_per_image=256,\n            positive_fraction=0.5,\n            bbox_reg_loss_type='smooth_l1',\n            bbox_reg_loss_weight=1.0,\n            bbox_reg_weights=(1.0, 1.0, 1.0, 1.0),\n            smooth_l1_beta=0.0,\n            loss_weight=1.0,\n            boundary_thresh=-1,\n            pre_nms_topk_train=2000,\n            post_nms_topk_train=1000,\n            pre_nms_topk_test=1000,\n            post_nms_topk_test=1000,\n            nms_thresh=0.7,\n            conv_dims=[-1]),\n        roi_heads=dict(\n            name='StandardROIHeads',\n            num_classes=80,\n            in_features=['p2', 'p3', 'p4', 'p5'],\n            iou_thresholds=[0.5],\n            iou_labels=[0, 1],\n            batch_size_per_image=512,\n            positive_fraction=0.25,\n            score_thresh_test=0.05,\n            nms_thresh_test=0.5,\n            proposal_append_gt=True),\n        roi_box_head=dict(\n            name='FastRCNNConvFCHead',\n            num_fc=2,\n            fc_dim=1024,\n            conv_dim=256,\n            pooler_type='ROIAlignV2',\n            pooler_resolution=7,\n            pooler_sampling_ratio=0,\n            bbox_reg_loss_type='smooth_l1',\n            bbox_reg_loss_weight=1.0,\n            bbox_reg_weights=(10.0, 10.0, 5.0, 5.0),\n            smooth_l1_beta=0.0,\n            cls_agnostic_bbox_reg=False)))\n"
  },
  {
    "path": "configs/misc/d2_mask-rcnn_r50-caffe_fpn_ms-90k_coco.py",
    "content": "_base_ = '../common/ms-poly-90k_coco-instance.py'\n\n# model settings\nmodel = dict(\n    type='Detectron2Wrapper',\n    bgr_to_rgb=False,\n    detector=dict(\n        # The settings in `d2_detector` will merged into default settings\n        # in detectron2. More details please refer to\n        # https://github.com/facebookresearch/detectron2/blob/main/detectron2/config/defaults.py    # noqa\n        meta_architecture='GeneralizedRCNN',\n        # If you want to finetune the detector, you can use the\n        # checkpoint released by detectron2, for example:\n        # weights='detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x/137260431/model_final_a54504.pkl'  # noqa\n        weights='detectron2://ImageNetPretrained/MSRA/R-50.pkl',\n        mask_on=True,\n        pixel_mean=[103.530, 116.280, 123.675],\n        pixel_std=[1.0, 1.0, 1.0],\n        backbone=dict(name='build_resnet_fpn_backbone', freeze_at=2),\n        resnets=dict(\n            depth=50,\n            out_features=['res2', 'res3', 'res4', 'res5'],\n            num_groups=1,\n            norm='FrozenBN'),\n        fpn=dict(\n            in_features=['res2', 'res3', 'res4', 'res5'], out_channels=256),\n        anchor_generator=dict(\n            name='DefaultAnchorGenerator',\n            sizes=[[32], [64], [128], [256], [512]],\n            aspect_ratios=[[0.5, 1.0, 2.0]],\n            angles=[[-90, 0, 90]]),\n        proposal_generator=dict(name='RPN'),\n        rpn=dict(\n            head_name='StandardRPNHead',\n            in_features=['p2', 'p3', 'p4', 'p5', 'p6'],\n            iou_thresholds=[0.3, 0.7],\n            iou_labels=[0, -1, 1],\n            batch_size_per_image=256,\n            positive_fraction=0.5,\n            bbox_reg_loss_type='smooth_l1',\n            bbox_reg_loss_weight=1.0,\n            bbox_reg_weights=(1.0, 1.0, 1.0, 1.0),\n            smooth_l1_beta=0.0,\n            loss_weight=1.0,\n            boundary_thresh=-1,\n            pre_nms_topk_train=2000,\n            post_nms_topk_train=1000,\n            pre_nms_topk_test=1000,\n            post_nms_topk_test=1000,\n            nms_thresh=0.7,\n            conv_dims=[-1]),\n        roi_heads=dict(\n            name='StandardROIHeads',\n            num_classes=80,\n            in_features=['p2', 'p3', 'p4', 'p5'],\n            iou_thresholds=[0.5],\n            iou_labels=[0, 1],\n            batch_size_per_image=512,\n            positive_fraction=0.25,\n            score_thresh_test=0.05,\n            nms_thresh_test=0.5,\n            proposal_append_gt=True),\n        roi_box_head=dict(\n            name='FastRCNNConvFCHead',\n            num_fc=2,\n            fc_dim=1024,\n            conv_dim=256,\n            pooler_type='ROIAlignV2',\n            pooler_resolution=7,\n            pooler_sampling_ratio=0,\n            bbox_reg_loss_type='smooth_l1',\n            bbox_reg_loss_weight=1.0,\n            bbox_reg_weights=(10.0, 10.0, 5.0, 5.0),\n            smooth_l1_beta=0.0,\n            cls_agnostic_bbox_reg=False),\n        roi_mask_head=dict(\n            name='MaskRCNNConvUpsampleHead',\n            conv_dim=256,\n            num_conv=4,\n            pooler_type='ROIAlignV2',\n            pooler_resolution=14,\n            pooler_sampling_ratio=0,\n            cls_agnostic_mask=False)))\n"
  },
  {
    "path": "configs/misc/d2_retinanet_r50-caffe_fpn_ms-90k_coco.py",
    "content": "_base_ = '../common/ms-90k_coco.py'\n\n# model settings\nmodel = dict(\n    type='Detectron2Wrapper',\n    bgr_to_rgb=False,\n    detector=dict(\n        # The settings in `d2_detector` will merged into default settings\n        # in detectron2. More details please refer to\n        # https://github.com/facebookresearch/detectron2/blob/main/detectron2/config/defaults.py    # noqa\n        meta_architecture='RetinaNet',\n        # If you want to finetune the detector, you can use the\n        # checkpoint released by detectron2, for example:\n        # weights='detectron2://COCO-Detection/retinanet_R_50_FPN_1x/190397773/model_final_bfca0b.pkl'     # noqa\n        weights='detectron2://ImageNetPretrained/MSRA/R-50.pkl',\n        mask_on=False,\n        pixel_mean=[103.530, 116.280, 123.675],\n        pixel_std=[1.0, 1.0, 1.0],\n        backbone=dict(name='build_retinanet_resnet_fpn_backbone', freeze_at=2),\n        resnets=dict(\n            depth=50,\n            out_features=['res3', 'res4', 'res5'],\n            num_groups=1,\n            norm='FrozenBN'),\n        fpn=dict(in_features=['res3', 'res4', 'res5'], out_channels=256),\n        anchor_generator=dict(\n            name='DefaultAnchorGenerator',\n            sizes=[[x, x * 2**(1.0 / 3), x * 2**(2.0 / 3)]\n                   for x in [32, 64, 128, 256, 512]],\n            aspect_ratios=[[0.5, 1.0, 2.0]],\n            angles=[[-90, 0, 90]]),\n        retinanet=dict(\n            num_classes=80,\n            in_features=['p3', 'p4', 'p5', 'p6', 'p7'],\n            num_convs=4,\n            iou_thresholds=[0.4, 0.5],\n            iou_labels=[0, -1, 1],\n            bbox_reg_weights=(1.0, 1.0, 1.0, 1.0),\n            bbox_reg_loss_type='smooth_l1',\n            smooth_l1_loss_beta=0.0,\n            focal_loss_gamma=2.0,\n            focal_loss_alpha=0.25,\n            prior_prob=0.01,\n            score_thresh_test=0.05,\n            topk_candidates_test=1000,\n            nms_thresh_test=0.5)))\n\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/mm_grounding_dino/README.md",
    "content": "# MM Grounding DINO\n\n> [An Open and Comprehensive Pipeline for Unified Object Grounding and Detection](https://arxiv.org/abs/2401.02361)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nGrounding-DINO is a state-of-the-art open-set detection model that tackles multiple vision tasks including Open-Vocabulary Detection (OVD), Phrase Grounding (PG), and Referring Expression Comprehension (REC). Its effectiveness has led to its widespread adoption as a mainstream architecture for various downstream applications. However, despite its significance, the original Grounding-DINO model lacks comprehensive public technical details due to the unavailability of its training code. To bridge this gap, we present MM-Grounding-DINO, an open-source, comprehensive, and user-friendly baseline, which is built with the MMDetection toolbox. It adopts abundant vision datasets for pre-training and various detection and grounding datasets for fine-tuning. We give a comprehensive analysis of each reported result and detailed settings for reproduction. The extensive experiments on the benchmarks mentioned demonstrate that our MM-Grounding-DINO-Tiny outperforms the Grounding-DINO-Tiny baseline. We release all our models to the research community.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/4214e282-a553-4abf-b8a4-84ea566851c9\"/>\n</div>\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/fb14d1ee-5469-44d2-b865-aac9850c429c\"/>\n</div>\n\n## Dataset Preparation\n\nPlease refer to [dataset_prepare.md](dataset_prepare.md) or [中文版数据准备](dataset_prepare_zh-CN.md)\n\n## ✨ What's New\n\n💎 **We have released the pre-trained weights for Swin-B and Swin-L, welcome to try and give feedback.**\n\n## Usage\n\nPlease refer to [usage.md](usage.md) or [中文版用法说明](usage_zh-CN.md)\n\n## Zero-Shot COCO Results and Models\n\n|    Model     | Backbone |   Style   |  COCO mAP  |      Pre-Train Data      |                                      Config                                      |                                                                                                                                                                                                                     Download                                                                                                                                                                                                                      |\n| :----------: | :------: | :-------: | :--------: | :----------------------: | :------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   GDINO-T    |  Swin-T  | Zero-shot |    46.7    |           O365           |                                                                                  |                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\n|   GDINO-T    |  Swin-T  | Zero-shot |    48.1    |        O365,GoldG        |                                                                                  |                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\n|   GDINO-T    |  Swin-T  | Zero-shot |    48.4    |     O365,GoldG,Cap4M     | [config](../grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_cap4m.py) |                                                                                                                                                                [model](https://download.openmmlab.com/mmdetection/v3.0/grounding_dino/groundingdino_swint_ogc_mmdet-822d7e9d.pth)                                                                                                                                                                 |\n|  MM-GDINO-T  |  Swin-T  | Zero-shot | 48.5(+1.8) |           O365           |                [config](grounding_dino_swin-t_pretrain_obj365.py)                |                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\n|  MM-GDINO-T  |  Swin-T  | Zero-shot | 50.4(+2.3) |        O365,GoldG        |             [config](grounding_dino_swin-t_pretrain_obj365_goldg.py)             |                           [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg/grounding_dino_swin-t_pretrain_obj365_goldg_20231122_132602-4ea751ce.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg/grounding_dino_swin-t_pretrain_obj365_goldg_20231122_132602.log.json)                            |\n|  MM-GDINO-T  |  Swin-T  | Zero-shot | 50.5(+2.1) |     O365,GoldG,GRIT      |         [config](grounding_dino_swin-t_pretrain_obj365_goldg_grit9m.py)          |             [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_20231128_200818-169cc352.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_20231128_200818.log.json)              |\n|  MM-GDINO-T  |  Swin-T  | Zero-shot | 50.6(+2.2) |     O365,GoldG,V3Det     |          [config](grounding_dino_swin-t_pretrain_obj365_goldg_v3det.py)          |             [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_v3det_20231218_095741-e316e297.pth)      \\| [log](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_v3det_20231218_095741.log.json)             |\n|  MM-GDINO-T  |  Swin-T  | Zero-shot | 50.4(+2.0) |  O365,GoldG,GRIT,V3Det   |      [config](grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det.py)       | [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047.log.json) |\n|  MM-GDINO-B  |  Swin-B  | Zero-shot |    52.5    |     O365,GoldG,V3Det     |          [config](grounding_dino_swin-b_pretrain_obj365_goldg_v3det.py)          |                                                                                                                     [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-b_pretrain_obj365_goldg_v3det/grounding_dino_swin-b_pretrain_obj365_goldg_v3de-f83eef00.pth)  \\| [log](<>)                                                                                                                      |\n| MM-GDINO-B\\* |  Swin-B  |     -     |    59.5    |         O365,ALL         |                 [config](grounding_dino_swin-b_pretrain_all.py)                  |                                                                                                                                    [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-b_pretrain_all/grounding_dino_swin-b_pretrain_all-f9818a7c.pth)  \\| [log](<>)                                                                                                                                    |\n|  MM-GDINO-L  |  Swin-L  | Zero-shot |    53.0    | O365V2,OpenImageV6,GoldG |             [config](grounding_dino_swin-l_pretrain_obj365_goldg.py)             |                                                                                                                           [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-l_pretrain_obj365_goldg/grounding_dino_swin-l_pretrain_obj365_goldg-34dcdc53.pth)  \\| [log](<>)                                                                                                                           |\n| MM-GDINO-L\\* |  Swin-L  |     -     |    60.3    |  O365V2,OpenImageV6,ALL  |                 [config](grounding_dino_swin-l_pretrain_all.py)                  |                                                                                                                                    [model](https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-l_pretrain_all/grounding_dino_swin-l_pretrain_all-56d69e78.pth)  \\| [log](<>)                                                                                                                                    |\n\n- This * indicates that the model has not been fully trained yet. We will release the final weights in the future.\n- ALL: GoldG,V3det,COCO2017,LVISV1,COCO2014,GRIT,RefCOCO,RefCOCO+,RefCOCOg,gRefCOCO.\n\n## Zero-Shot LVIS Results\n\n|   Model    | MiniVal APr | MiniVal APc | MiniVal APf | MiniVal AP  | Val1.0 APr | Val1.0 APc | Val1.0 APf |  Val1.0 AP  |    Pre-Train Data     |\n| :--------: | :---------: | :---------: | :---------: | :---------: | :--------: | :--------: | :--------: | :---------: | :-------------------: |\n|  GDINO-T   |    18.8     |    24.2     |    34.7     |    28.8     |    10.1    |    15.3    |    29.9    |    20.1     |   O365,GoldG,Cap4M    |\n| MM-GDINO-T |    28.1     |    30.2     |    42.0     | 35.7(+6.9)  |    17.1    |    22.4    |    36.5    | 27.0(+6.9)  |      O365,GoldG       |\n| MM-GDINO-T |    26.6     |    32.4     |    41.8     | 36.5(+7.7)  |    17.3    |    22.6    |    36.4    | 27.1(+7.0)  |    O365,GoldG,GRIT    |\n| MM-GDINO-T |    33.0     |    36.0     |    45.9     | 40.5(+11.7) |    21.5    |    25.5    |    40.2    | 30.6(+10.5) |   O365,GoldG,V3Det    |\n| MM-GDINO-T |    34.2     |    37.4     |    46.2     | 41.4(+12.6) |    23.6    |    27.6    |    40.5    | 31.9(+11.8) | O365,GoldG,GRIT,V3Det |\n\n- The MM-GDINO-T config file is [mini-lvis](lvis/grounding_dino_swin-t_pretrain_zeroshot_mini-lvis.py) and [lvis 1.0](lvis/grounding_dino_swin-t_pretrain_zeroshot_lvis.py)\n\n## Zero-Shot ODinW (Object Detection in the Wild) Results\n\n### Results and models of ODinW13\n\n| Method                | GDINO-T <br/> (O365,GoldG,Cap4M) | MM-GDINO-T <br/> (O365,GoldG) | MM-GDINO-T <br/> (O365,GoldG,GRIT) | MM-GDINO-T <br/> (O365,GoldG,V3Det) | MM-GDINO-T <br/> (O365,GoldG,GRIT,V3Det) |\n| --------------------- | -------------------------------- | ----------------------------- | ---------------------------------- | ----------------------------------- | ---------------------------------------- |\n| AerialMaritimeDrone   | 0.173                            | 0.133                         | 0.155                              | 0.177                               | 0.151                                    |\n| Aquarium              | 0.195                            | 0.252                         | 0.261                              | 0.266                               | 0.283                                    |\n| CottontailRabbits     | 0.799                            | 0.771                         | 0.810                              | 0.778                               | 0.786                                    |\n| EgoHands              | 0.608                            | 0.499                         | 0.537                              | 0.506                               | 0.519                                    |\n| NorthAmericaMushrooms | 0.507                            | 0.331                         | 0.462                              | 0.669                               | 0.767                                    |\n| Packages              | 0.687                            | 0.707                         | 0.687                              | 0.710                               | 0.706                                    |\n| PascalVOC             | 0.563                            | 0.565                         | 0.580                              | 0.556                               | 0.566                                    |\n| pistols               | 0.726                            | 0.585                         | 0.709                              | 0.671                               | 0.729                                    |\n| pothole               | 0.215                            | 0.136                         | 0.285                              | 0.199                               | 0.243                                    |\n| Raccoon               | 0.549                            | 0.469                         | 0.511                              | 0.553                               | 0.535                                    |\n| ShellfishOpenImages   | 0.393                            | 0.321                         | 0.437                              | 0.519                               | 0.488                                    |\n| thermalDogsAndPeople  | 0.657                            | 0.556                         | 0.603                              | 0.493                               | 0.542                                    |\n| VehiclesOpenImages    | 0.613                            | 0.566                         | 0.603                              | 0.614                               | 0.615                                    |\n| Average               | **0.514**                        | **0.453**                     | **0.511**                          | **0.516**                           | **0.533**                                |\n\n- The MM-GDINO-T config file is [odinw13](odinw/grounding_dino_swin-t_pretrain_odinw13.py)\n\n### Results and models of ODinW35\n\n| Method                      | GDINO-T <br/> (O365,GoldG,Cap4M) | MM-GDINO-T <br/> (O365,GoldG) | MM-GDINO-T <br/> (O365,GoldG,GRIT) | MM-GDINO-T <br/> (O365,GoldG,V3Det) | MM-GDINO-T <br/> (O365,GoldG,GRIT,V3Det) |\n| --------------------------- | -------------------------------- | ----------------------------- | ---------------------------------- | ----------------------------------- | ---------------------------------------- |\n| AerialMaritimeDrone_large   | 0.173                            | 0.133                         | 0.155                              | 0.177                               | 0.151                                    |\n| AerialMaritimeDrone_tiled   | 0.206                            | 0.170                         | 0.225                              | 0.184                               | 0.206                                    |\n| AmericanSignLanguageLetters | 0.002                            | 0.016                         | 0.020                              | 0.011                               | 0.007                                    |\n| Aquarium                    | 0.195                            | 0.252                         | 0.261                              | 0.266                               | 0.283                                    |\n| BCCD                        | 0.161                            | 0.069                         | 0.118                              | 0.083                               | 0.077                                    |\n| boggleBoards                | 0.000                            | 0.002                         | 0.001                              | 0.001                               | 0.002                                    |\n| brackishUnderwater          | 0.021                            | 0.033                         | 0.021                              | 0.025                               | 0.025                                    |\n| ChessPieces                 | 0.000                            | 0.000                         | 0.000                              | 0.000                               | 0.000                                    |\n| CottontailRabbits           | 0.806                            | 0.771                         | 0.810                              | 0.778                               | 0.786                                    |\n| dice                        | 0.004                            | 0.002                         | 0.005                              | 0.001                               | 0.001                                    |\n| DroneControl                | 0.042                            | 0.047                         | 0.097                              | 0.088                               | 0.074                                    |\n| EgoHands_generic            | 0.608                            | 0.527                         | 0.537                              | 0.506                               | 0.519                                    |\n| EgoHands_specific           | 0.002                            | 0.001                         | 0.005                              | 0.007                               | 0.003                                    |\n| HardHatWorkers              | 0.046                            | 0.048                         | 0.070                              | 0.070                               | 0.108                                    |\n| MaskWearing                 | 0.004                            | 0.009                         | 0.004                              | 0.011                               | 0.009                                    |\n| MountainDewCommercial       | 0.430                            | 0.453                         | 0.465                              | 0.194                               | 0.430                                    |\n| NorthAmericaMushrooms       | 0.471                            | 0.331                         | 0.462                              | 0.669                               | 0.767                                    |\n| openPoetryVision            | 0.000                            | 0.001                         | 0.000                              | 0.000                               | 0.000                                    |\n| OxfordPets_by_breed         | 0.003                            | 0.002                         | 0.004                              | 0.006                               | 0.004                                    |\n| OxfordPets_by_species       | 0.011                            | 0.019                         | 0.016                              | 0.020                               | 0.015                                    |\n| PKLot                       | 0.001                            | 0.004                         | 0.002                              | 0.008                               | 0.007                                    |\n| Packages                    | 0.695                            | 0.707                         | 0.687                              | 0.710                               | 0.706                                    |\n| PascalVOC                   | 0.563                            | 0.565                         | 0.580                              | 0.566                               | 0.566                                    |\n| pistols                     | 0.726                            | 0.585                         | 0.709                              | 0.671                               | 0.729                                    |\n| plantdoc                    | 0.005                            | 0.005                         | 0.007                              | 0.008                               | 0.011                                    |\n| pothole                     | 0.215                            | 0.136                         | 0.219                              | 0.077                               | 0.168                                    |\n| Raccoons                    | 0.549                            | 0.469                         | 0.511                              | 0.553                               | 0.535                                    |\n| selfdrivingCar              | 0.089                            | 0.091                         | 0.076                              | 0.094                               | 0.083                                    |\n| ShellfishOpenImages         | 0.393                            | 0.321                         | 0.437                              | 0.519                               | 0.488                                    |\n| ThermalCheetah              | 0.087                            | 0.063                         | 0.081                              | 0.030                               | 0.045                                    |\n| thermalDogsAndPeople        | 0.657                            | 0.556                         | 0.603                              | 0.493                               | 0.543                                    |\n| UnoCards                    | 0.006                            | 0.012                         | 0.010                              | 0.009                               | 0.005                                    |\n| VehiclesOpenImages          | 0.613                            | 0.566                         | 0.603                              | 0.614                               | 0.615                                    |\n| WildfireSmoke               | 0.134                            | 0.106                         | 0.154                              | 0.042                               | 0.127                                    |\n| websiteScreenshots          | 0.012                            | 0.02                          | 0.016                              | 0.016                               | 0.016                                    |\n| Average                     | **0.227**                        | **0.202**                     | **0.228**                          | **0.214**                           | **0.284**                                |\n\n- The MM-GDINO-T config file is [odinw35](odinw/grounding_dino_swin-t_pretrain_odinw35.py)\n\n## Zero-Shot Referring Expression Comprehension Results\n\n| Method                 | GDINO-T <br/> (O365,GoldG,Cap4M) | MM-GDINO-T <br/> (O365,GoldG) | MM-GDINO-T <br/> (O365,GoldG,GRIT) | MM-GDINO-T <br/> (O365,GoldG,V3Det) | MM-GDINO-T <br/> (O365,GoldG,GRIT,V3Det) |\n| ---------------------- | -------------------------------- | ----------------------------- | ---------------------------------- | ----------------------------------- | ---------------------------------------- |\n| RefCOCO val @1,5,10    | 50.8/89.5/94.9                   | 53.1/89.9/94.7                | 53.4/90.3/95.5                     | 52.1/89.8/95.0                      | 53.1/89.7/95.1                           |\n| RefCOCO testA @1,5,10  | 57.4/91.3/95.6                   | 59.7/91.5/95.9                | 58.8/91.70/96.2                    | 58.4/86.8/95.6                      | 59.1/91.0/95.5                           |\n| RefCOCO testB @1,5,10  | 45.0/86.5/92.9                   | 46.4/86.9/92.2                | 46.8/87.7/93.3                     | 45.4/86.2/92.6                      | 46.8/87.8/93.6                           |\n| RefCOCO+ val @1,5,10   | 51.6/86.4/92.6                   | 53.1/87.0/92.8                | 53.5/88.0/93.7                     | 52.5/86.8/93.2                      | 52.7/87.7/93.5                           |\n| RefCOCO+ testA @1,5,10 | 57.3/86.7/92.7                   | 58.9/87.3/92.9                | 59.0/88.1/93.7                     | 58.1/86.7/93.5                      | 58.7/87.2/93.1                           |\n| RefCOCO+ testB @1,5,10 | 46.4/84.1/90.7                   | 47.9/84.3/91.0                | 47.9/85.5/92.7                     | 46.9/83.7/91.5                      | 48.4/85.8/92.1                           |\n| RefCOCOg val @1,5,10   | 60.4/92.1/96.2                   | 61.2/92.6/96.1                | 62.7/93.3/97.0                     | 61.7/92.9/96.6                      | 62.9/93.3/97.2                           |\n| RefCOCOg test @1,5,10  | 59.7/92.1/96.3                   | 61.1/93.3/96.7                | 62.6/94.9/97.1                     | 61.0/93.1/96.8                      | 62.9/93.9/97.4                           |\n\n| Method                                  | thresh_score | GDINO-T <br/> (O365,GoldG,Cap4M) | MM-GDINO-T <br/> (O365,GoldG) | MM-GDINO-T <br/> (O365,GoldG,GRIT) | MM-GDINO-T <br/> (O365,GoldG,V3Det) | MM-GDINO-T <br/> (O365,GoldG,GRIT,V3Det) |\n| --------------------------------------- | ------------ | -------------------------------- | ----------------------------- | ---------------------------------- | ----------------------------------- | ---------------------------------------- |\n| gRefCOCO val Pr@(F1=1, IoU≥0.5),N-acc   | 0.5          | 39.3/70.4                        |                               |                                    |                                     | 39.4/67.5                                |\n| gRefCOCO val Pr@(F1=1, IoU≥0.5),N-acc   | 0.6          | 40.5/83.8                        |                               |                                    |                                     | 40.6/83.1                                |\n| gRefCOCO val Pr@(F1=1, IoU≥0.5),N-acc   | 0.7          | 41.3/91.8                        | 39.8/84.7                     | 40.7/89.7                          | 40.3/88.8                           | 41.0/91.3                                |\n| gRefCOCO val Pr@(F1=1, IoU≥0.5),N-acc   | 0.8          | 41.5/96.8                        |                               |                                    |                                     | 41.1/96.4                                |\n| gRefCOCO testA Pr@(F1=1, IoU≥0.5),N-acc | 0.5          | 31.9/70.4                        |                               |                                    |                                     | 33.1/69.5                                |\n| gRefCOCO testA Pr@(F1=1, IoU≥0.5),N-acc | 0.6          | 29.3/82.9                        |                               |                                    |                                     | 29.2/84.3                                |\n| gRefCOCO testA Pr@(F1=1, IoU≥0.5),N-acc | 0.7          | 27.2/90.2                        | 26.3/89.0                     | 26.0/91.9                          | 25.4/91.8                           | 26.1/93.0                                |\n| gRefCOCO testA Pr@(F1=1, IoU≥0.5),N-acc | 0.8          | 25.1/96.3                        |                               |                                    |                                     | 23.8/97.2                                |\n| gRefCOCO testB Pr@(F1=1, IoU≥0.5),N-acc | 0.5          | 30.9/72.5                        |                               |                                    |                                     | 33.0/69.6                                |\n| gRefCOCO testB Pr@(F1=1, IoU≥0.5),N-acc | 0.6          | 30.0/86.1                        |                               |                                    |                                     | 31.6/96.7                                |\n| gRefCOCO testB Pr@(F1=1, IoU≥0.5),N-acc | 0.7          | 29.7/93.5                        | 31.3/84.8                     | 30.6/90.2                          | 30.7/89.9                           | 30.4/92.3                                |\n| gRefCOCO testB Pr@(F1=1, IoU≥0.5),N-acc | 0.8          | 29.1/97.4                        |                               |                                    |                                     | 29.5/84.2                                |\n\n- The MM-GDINO-T config file is [here](refcoco/grounding_dino_swin-t_pretrain_zeroshot_refexp.py)\n\n## Zero-Shot Description Detection Dataset(DOD)\n\n```shell\npip install ddd-dataset\n```\n\n| Method                           | mode     | GDINO-T <br/> (O365,GoldG,Cap4M) | MM-GDINO-T <br/> (O365,GoldG) | MM-GDINO-T <br/> (O365,GoldG,GRIT) | MM-GDINO-T <br/> (O365,GoldG,V3Det) | MM-GDINO-T <br/> (O365,GoldG,GRIT,V3Det) |\n| -------------------------------- | -------- | -------------------------------- | ----------------------------- | ---------------------------------- | ----------------------------------- | ---------------------------------------- |\n| FULL/short/middle/long/very long | concat   | 17.2/18.0/18.7/14.8/16.3         | 15.6/17.3/16.7/14.3/13.1      | 17.0/17.7/18.0/15.7/15.7           | 16.2/17.4/16.8/14.9/15.4            | 17.5/23.4/18.3/14.7/13.8                 |\n| FULL/short/middle/long/very long | parallel | 22.3/28.2/24.8/19.1/13.9         | 21.7/24.7/24.0/20.2/13.7      | 22.5/25.6/25.1/20.5/14.9           | 22.3/25.6/24.5/20.6/14.7            | 22.9/28.1/25.4/20.4/14.4                 |\n| PRES/short/middle/long/very long | concat   | 17.8/18.3/19.2/15.2/17.3         | 16.4/18.4/17.3/14.5/14.2      | 17.9/19.0/18.3/16.5/17.5           | 16.6/18.8/17.1/15.1/15.0            | 18.0/23.7/18.6/15.4/13.3                 |\n| PRES/short/middle/long/very long | parallel | 21.0/27.0/22.8/17.5/12.5         | 21.3/25.5/22.8/19.2/12.9      | 21.5/25.2/23.0/19.0/15.0           | 21.6/25.7/23.0/19.5/14.8            | 21.9/27.4/23.2/19.1/14.2                 |\n| ABS/short/middle/long/very long  | concat   | 15.4/17.1/16.4/13.6/14.9         | 13.4/13.4/14.5/13.5/11.9      | 14.5/13.1/16.7/13.6/13.3           | 14.8/12.5/15.6/14.3/15.8            | 15.9/22.2/17.1/12.5/14.4                 |\n| ABS/short/middle/long/very long  | parallel | 26.0/32.0/33.0/23.6/15.5         | 22.8/22.2/28.7/22.9/14.7      | 25.6/26.8/33.9/24.5/14.7           | 24.1/24.9/30.7/23.8/14.7            | 26.0/30.3/34.1/23.9/14.6                 |\n\nNote:\n\n1. Considering that the evaluation time for Inter-scenario is very long and the performance is low, it is temporarily not supported. The mentioned metrics are for Intra-scenario.\n2. `concat` is the default inference mode for Grounding DINO, where it concatenates multiple sub-sentences with \".\" to form a single sentence for inference. On the other hand, \"parallel\" performs inference on each sub-sentence in a for-loop.\n3. The MM-GDINO-T config file is [concat_dod](dod/grounding_dino_swin-t_pretrain_zeroshot_concat_dod.py) and [parallel_dod](dod/grounding_dino_swin-t_pretrain_zeroshot_parallel_dod.py)\n\n## Pretrain Flickr30k Results\n\n|   Model    |    Pre-Train Data     | Val R@1 | Val R@5 | Val R@10 | Test R@1 | Test R@5 | Test R@10 |\n| :--------: | :-------------------: | ------- | ------- | -------- | -------- | -------- | --------- |\n|   GLIP-T   |      O365,GoldG       | 84.9    | 94.9    | 96.3     | 85.6     | 95.4     | 96.7      |\n|   GLIP-T   |  O365,GoldG,CC3M,SBU  | 85.3    | 95.5    | 96.9     | 86.0     | 95.9     | 97.2      |\n|  GDINO-T   |   O365,GoldG,Cap4M    | 87.8    | 96.6    | 98.0     | 88.1     | 96.9     | 98.2      |\n| MM-GDINO-T |      O365,GoldG       | 85.5    | 95.6    | 97.2     | 86.2     | 95.7     | 97.4      |\n| MM-GDINO-T |    O365,GoldG,GRIT    | 86.7    | 95.8    | 97.6     | 87.0     | 96.2     | 97.7      |\n| MM-GDINO-T |   O365,GoldG,V3Det    | 85.9    | 95.7    | 97.4     | 86.3     | 95.7     | 97.4      |\n| MM-GDINO-T | O365,GoldG,GRIT,V3Det | 86.7    | 96.0    | 97.6     | 87.2     | 96.2     | 97.7      |\n\nNote:\n\n1. `@1,5,10` refers to precision at the top 1, 5, and 10 positions in a predicted ranked list.\n2. The MM-GDINO-T config file is [here](flickr30k/grounding_dino_swin-t-pretrain_flickr30k.py)\n\n## Validating the generalization of a pre-trained model through fine-tuning\n\n### RTTS\n\n|    Architecture     | Backbone | Lr schd | box AP   |\n| :-----------------: | :------: | ------- | -------- |\n|    Faster R-CNN     |   R-50   | 1x      | 48.1     |\n|    Cascade R-CNN    |   R-50   | 1x      | 50.8     |\n|        ATSS         |   R-50   | 1x      | 48.2     |\n|        TOOD         |   R-50   | 1X      | 50.8     |\n| MM-GDINO(zero-shot) |  Swin-T  |         | 49.8     |\n|      MM-GDINO       |  Swin-T  | 1x      | **69.1** |\n\n- The reference metrics come from https://github.com/BIGWangYuDong/lqit/tree/main/configs/detection/rtts_dataset\n- The MM-GDINO-T config file is [here](rtts/grounding_dino_swin-t_finetune_8xb4_1x_rtts.py)\n\n### RUOD\n\n|    Architecture     | Backbone | Lr schd | box AP   |\n| :-----------------: | :------: | ------- | -------- |\n|    Faster R-CNN     |   R-50   | 1x      | 52.4     |\n|    Cascade R-CNN    |   R-50   | 1x      | 55.3     |\n|        ATSS         |   R-50   | 1x      | 55.7     |\n|        TOOD         |   R-50   | 1X      | 57.4     |\n| MM-GDINO(zero-shot) |  Swin-T  |         | 29.8     |\n|      MM-GDINO       |  Swin-T  | 1x      | **65.5** |\n\n- The reference metrics come from https://github.com/BIGWangYuDong/lqit/tree/main/configs/detection/ruod_dataset\n- The MM-GDINO-T config file is [here](ruod/grounding_dino_swin-t_finetune_8xb4_1x_ruod.py)\n\n### Brain Tumor\n\n| Architecture  | Backbone | Lr schd | box AP |\n| :-----------: | :------: | ------- | ------ |\n| Faster R-CNN  |   R-50   | 50e     | 43.5   |\n| Cascade R-CNN |   R-50   | 50e     | 46.2   |\n|     DINO      |   R-50   | 50e     | 46.4   |\n| Cascade-DINO  |   R-50   | 50e     | 48.6   |\n|   MM-GDINO    |  Swin-T  | 50e     | 47.5   |\n\n- The reference metrics come from https://arxiv.org/abs/2307.11035\n- The MM-GDINO-T config file is [here](brain_tumor/grounding_dino_swin-t_finetune_8xb4_50e_brain_tumor.py)\n\n### Cityscapes\n\n|    Architecture     | Backbone | Lr schd | box AP   |\n| :-----------------: | :------: | ------- | -------- |\n|    Faster R-CNN     |   R-50   | 50e     | 30.1     |\n|    Cascade R-CNN    |   R-50   | 50e     | 31.8     |\n|        DINO         |   R-50   | 50e     | 34.5     |\n|    Cascade-DINO     |   R-50   | 50e     | 34.8     |\n| MM-GDINO(zero-shot) |  Swin-T  |         | 34.2     |\n|      MM-GDINO       |  Swin-T  | 50e     | **51.5** |\n\n- The reference metrics come from https://arxiv.org/abs/2307.11035\n- The MM-GDINO-T config file is [here](cityscapes/grounding_dino_swin-t_finetune_8xb4_50e_cityscapes.py)\n\n### People in Painting\n\n|    Architecture     | Backbone | Lr schd | box AP   |\n| :-----------------: | :------: | ------- | -------- |\n|    Faster R-CNN     |   R-50   | 50e     | 17.0     |\n|    Cascade R-CNN    |   R-50   | 50e     | 18.0     |\n|        DINO         |   R-50   | 50e     | 12.0     |\n|    Cascade-DINO     |   R-50   | 50e     | 13.4     |\n| MM-GDINO(zero-shot) |  Swin-T  |         | 23.1     |\n|      MM-GDINO       |  Swin-T  | 50e     | **38.9** |\n\n- The reference metrics come from https://arxiv.org/abs/2307.11035\n- The MM-GDINO-T config file is [here](people_in_painting/grounding_dino_swin-t_finetune_8xb4_50e_people_in_painting.py)\n\n### COCO\n\n**(1) Closed-set performance**\n\n|    Architecture     | Backbone | Lr schd | box AP |\n| :-----------------: | :------: | ------- | ------ |\n|    Faster R-CNN     |   R-50   | 1x      | 37.4   |\n|    Cascade R-CNN    |   R-50   | 1x      | 40.3   |\n|        ATSS         |   R-50   | 1x      | 39.4   |\n|        TOOD         |   R-50   | 1X      | 42.4   |\n|        DINO         |   R-50   | 1X      | 50.1   |\n|   GLIP(zero-shot)   |  Swin-T  |         | 46.6   |\n|  GDINO(zero-shot)   |  Swin-T  |         | 48.5   |\n| MM-GDINO(zero-shot) |  Swin-T  |         | 50.4   |\n|        GLIP         |  Swin-T  | 1x      | 55.4   |\n|        GDINO        |  Swin-T  | 1x      | 58.1   |\n|      MM-GDINO       |  Swin-T  | 1x      | 58.2   |\n\n- The MM-GDINO-T config file is [here](coco/grounding_dino_swin-t_finetune_16xb4_1x_coco.py)\n\n**(2) Open-set continuing pretraining performance**\n\n|    Architecture     | Backbone | Lr schd | box AP |\n| :-----------------: | :------: | :-----: | :----: |\n|   GLIP(zero-shot)   |  Swin-T  |         |  46.7  |\n|  GDINO(zero-shot)   |  Swin-T  |         |  48.5  |\n| MM-GDINO(zero-shot) |  Swin-T  |         |  50.4  |\n|      MM-GDINO       |  Swin-T  |   1x    |  54.7  |\n\n- The MM-GDINO-T config file is [here](coco/grounding_dino_swin-t_finetune_16xb4_1x_sft_coco.py)\n- Due to the small size of the COCO dataset, continuing pretraining solely on COCO can easily lead to overfitting. The results shown above are from the third epoch. I do not recommend you train using this approach.\n\n**(3) Open vocabulary performance**\n\n|    Architecture     | Backbone | Lr schd | box AP | Base box AP | Novel box AP | box AP@50 | Base box AP@50 | Novel box AP@50 |\n| :-----------------: | :------: | :-----: | :----: | :---------: | :----------: | :-------: | :------------: | :-------------: |\n| MM-GDINO(zero-shot) |  Swin-T  |         |  51.1  |    48.4     |     58.9     |   66.7    |      64.0      |      74.2       |\n|      MM-GDINO       |  Swin-T  |   1x    |  57.2  |    56.1     |     60.4     |   73.6    |      73.0      |      75.3       |\n\n- The MM-GDINO-T config file is [here](coco/grounding_dino_swin-t_finetune_16xb4_1x_coco_48_17.py)\n\n### LVIS 1.0\n\n**(1) Open-set continuing pretraining performance**\n\n|    Architecture     | Backbone | Lr schd | MiniVal APr | MiniVal APc | MiniVal APf | MiniVal AP | Val1.0 APr | Val1.0 APc | Val1.0 APf | Val1.0 AP |\n| :-----------------: | :------: | :-----: | :---------: | :---------: | :---------: | :--------: | :--------: | :--------: | :--------: | :-------: |\n|   GLIP(zero-shot)   |  Swin-T  |         |    18.1     |    21.2     |    33.1     |    26.7    |    10.8    |    14.7    |    29.0    |   19.6    |\n|  GDINO(zero-shot)   |  Swin-T  |         |    18.8     |    24.2     |    34.7     |    28.8    |    10.1    |    15.3    |    29.9    |   20.1    |\n| MM-GDINO(zero-shot) |  Swin-T  |         |    34.2     |    37.4     |    46.2     |    41.4    |    23.6    |    27.6    |    40.5    |   31.9    |\n|      MM-GDINO       |  Swin-T  |   1x    |    50.7     |    58.8     |    60.1     |    58.7    |    45.2    |    50.2    |    56.1    |   51.7    |\n\n- The MM-GDINO-T config file is [here](lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis.py)\n\n**(2) Open vocabulary performance**\n\n|    Architecture     | Backbone | Lr schd | MiniVal APr | MiniVal APc | MiniVal APf | MiniVal AP |\n| :-----------------: | :------: | :-----: | :---------: | :---------: | :---------: | :--------: |\n| MM-GDINO(zero-shot) |  Swin-T  |         |    34.2     |    37.4     |    46.2     |    41.4    |\n|      MM-GDINO       |  Swin-T  |   1x    |    43.2     |    57.4     |    59.3     |    57.1    |\n\n- The MM-GDINO-T config file is [here](lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis_866_337.py)\n\n### RefEXP\n\n#### RefCOCO\n\n|    Architecture     | Backbone | Lr schd | val @1 | val @5 | val @10 | testA @1 | testA @5 | testA @10 | testB @1 | testB @5 | testB @10 |\n| :-----------------: | :------: | :-----: | :----: | :----: | :-----: | :------: | :------: | :-------: | :------: | :------: | :-------: |\n|  GDINO(zero-shot)   |  Swin-T  |         |  50.8  |  89.5  |  94.9   |   57.5   |   91.3   |   95.6    |   45.0   |   86.5   |   92.9    |\n| MM-GDINO(zero-shot) |  Swin-T  |         |  53.1  |  89.7  |  95.1   |   59.1   |   91.0   |   95.5    |   46.8   |   87.8   |   93.6    |\n|        GDINO        |  Swin-T  |   UNK   |  89.2  |        |         |   91.9   |          |           |   86.0   |          |           |\n|      MM-GDINO       |  Swin-T  |   5e    |  89.5  |  98.6  |  99.4   |   91.4   |   99.2   |   99.8    |   86.6   |   97.9   |   99.1    |\n\n- The MM-GDINO-T config file is [here](refcoco/grounding_dino_swin-t_finetune_8xb4_5e_refcoco.py)\n\n#### RefCOCO+\n\n|    Architecture     | Backbone | Lr schd | val @1 | val @5 | val @10 | testA @1 | testA @5 | testA @10 | testB @1 | testB @5 | testB @10 |\n| :-----------------: | :------: | :-----: | :----: | :----: | :-----: | :------: | :------: | :-------: | :------: | :------: | :-------: |\n|  GDINO(zero-shot)   |  Swin-T  |         |  51.6  |  86.4  |  92.6   |   57.3   |   86.7   |   92.7    |   46.4   |   84.1   |   90.7    |\n| MM-GDINO(zero-shot) |  Swin-T  |         |  52.7  |  87.7  |  93.5   |   58.7   |   87.2   |   93.1    |   48.4   |   85.8   |   92.1    |\n|        GDINO        |  Swin-T  |   UNK   |  81.1  |        |         |   87.4   |          |           |   74.7   |          |           |\n|      MM-GDINO       |  Swin-T  |   5e    |  82.1  |  97.8  |  99.2   |   87.5   |   99.2   |   99.7    |   74.0   |   96.3   |   96.4    |\n\n- The MM-GDINO-T config file is [here](refcoco/grounding_dino_swin-t_finetune_8xb4_5e_refcoco_plus.py)\n\n#### RefCOCOg\n\n|    Architecture     | Backbone | Lr schd | val @1 | val @5 | val @10 | test @1 | test @5 | test @10 |\n| :-----------------: | :------: | :-----: | :----: | :----: | :-----: | :-----: | :-----: | :------: |\n|  GDINO(zero-shot)   |  Swin-T  |         |  60.4  |  92.1  |  96.2   |  59.7   |  92.1   |   96.3   |\n| MM-GDINO(zero-shot) |  Swin-T  |         |  62.9  |  93.3  |  97.2   |  62.9   |  93.9   |   97.4   |\n|        GDINO        |  Swin-T  |   UNK   |  84.2  |        |         |  84.9   |         |          |\n|      MM-GDINO       |  Swin-T  |   5e    |  85.5  |  98.4  |  99.4   |  85.8   |  98.6   |   99.4   |\n\n- The MM-GDINO-T config file is [here](refcoco/grounding_dino_swin-t_finetune_8xb4_5e_refcocog.py)\n\n#### gRefCOCO\n\n|    Architecture     | Backbone | Lr schd | val Pr@(F1=1, IoU≥0.5) | val N-acc | testA Pr@(F1=1, IoU≥0.5) | testA N-acc | testB Pr@(F1=1, IoU≥0.5) | testB N-acc |\n| :-----------------: | :------: | :-----: | :--------------------: | :-------: | :----------------------: | :---------: | :----------------------: | :---------: |\n|  GDINO(zero-shot)   |  Swin-T  |         |          41.3          |   91.8    |           27.2           |    90.2     |           29.7           |    93.5     |\n| MM-GDINO(zero-shot) |  Swin-T  |         |          41.0          |   91.3    |           26.1           |    93.0     |           30.4           |    92.3     |\n|      MM-GDINO       |  Swin-T  |   5e    |          45.1          |   64.7    |           42.5           |    65.5     |           40.3           |    63.2     |\n\n- The MM-GDINO-T config file is [here](refcoco/grounding_dino_swin-t_finetune_8xb4_5e_grefcoco.py)\n\n## Citation\n\nIf you find this project useful in your research, please consider citing:\n\n```latex\n@article{zhao2024open,\n  title={An Open and Comprehensive Pipeline for Unified Object Grounding and Detection},\n  author={Zhao, Xiangyu and Chen, Yicheng and Xu, Shilin and Li, Xiangtai and Wang, Xinjiang and Li, Yining and Huang, Haian},\n  journal={arXiv preprint arXiv:2401.02361},\n  year={2024}\n}\n```\n"
  },
  {
    "path": "configs/mm_grounding_dino/brain_tumor/grounding_dino_swin-t_finetune_8xb4_50e_brain_tumor.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\n# https://universe.roboflow.com/roboflow-100/brain-tumor-m2pbp/dataset/2\ndata_root = 'data/brain_tumor_v2/'\nclass_name = ('label0', 'label1', 'label2')\nlabel_name = '_annotations.coco.json'\n\npalette = [(220, 20, 60), (255, 0, 0), (0, 0, 142)]\n\nmetainfo = dict(classes=class_name, palette=palette)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    sampler=dict(_delete_=True, type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=10,\n        dataset=dict(\n            type='CocoDataset',\n            data_root=data_root,\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            pipeline=train_pipeline,\n            return_classes=True,\n            data_prefix=dict(img='train/'),\n            ann_file='train/' + label_name)))\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=metainfo,\n        data_root=data_root,\n        return_classes=True,\n        ann_file='valid/' + label_name,\n        data_prefix=dict(img='valid/')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'valid/' + label_name,\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={\n        'absolute_pos_embed': dict(decay_mult=0.),\n        'backbone': dict(lr_mult=0.1)\n    }))\n\n# learning policy\nmax_epochs = 5\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[4],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/cityscapes/grounding_dino_swin-t_finetune_8xb4_50e_cityscapes.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/cityscapes/'\nclass_name = ('person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle',\n              'bicycle')\npalette = [(220, 20, 60), (255, 0, 0), (0, 0, 142), (0, 0, 70), (0, 60, 100),\n           (0, 80, 100), (0, 0, 230), (119, 11, 32)]\n\nmetainfo = dict(classes=class_name, palette=palette)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    sampler=dict(_delete_=True, type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=10,\n        dataset=dict(\n            type='CocoDataset',\n            data_root=data_root,\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            pipeline=train_pipeline,\n            return_classes=True,\n            data_prefix=dict(img='leftImg8bit/train/'),\n            ann_file='annotations/instancesonly_filtered_gtFine_train.json')))\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=metainfo,\n        data_root=data_root,\n        return_classes=True,\n        ann_file='annotations/instancesonly_filtered_gtFine_val.json',\n        data_prefix=dict(img='leftImg8bit/val/')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instancesonly_filtered_gtFine_val.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={\n        'absolute_pos_embed': dict(decay_mult=0.),\n        'backbone': dict(lr_mult=0.1)\n    }))\n\n# learning policy\nmax_epochs = 5\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[4],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/coco/grounding_dino_swin-t_finetune_16xb4_1x_coco.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='CocoDataset',\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        return_classes=True,\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        pipeline=train_pipeline))\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            'language_model': dict(lr_mult=0.1),\n        }))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/coco/grounding_dino_swin-t_finetune_16xb4_1x_coco_48_17.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\nbase_classes = ('person', 'bicycle', 'car', 'motorcycle', 'train', 'truck',\n                'boat', 'bench', 'bird', 'horse', 'sheep', 'bear', 'zebra',\n                'giraffe', 'backpack', 'handbag', 'suitcase', 'frisbee',\n                'skis', 'kite', 'surfboard', 'bottle', 'fork', 'spoon', 'bowl',\n                'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot',\n                'pizza', 'donut', 'chair', 'bed', 'toilet', 'tv', 'laptop',\n                'mouse', 'remote', 'microwave', 'oven', 'toaster',\n                'refrigerator', 'book', 'clock', 'vase', 'toothbrush')  # 48\nnovel_classes = ('airplane', 'bus', 'cat', 'dog', 'cow', 'elephant',\n                 'umbrella', 'tie', 'snowboard', 'skateboard', 'cup', 'knife',\n                 'cake', 'couch', 'keyboard', 'sink', 'scissors')  # 17\nall_classes = (\n    'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n    'truck', 'boat', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',\n    'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag',\n    'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'kite', 'skateboard',\n    'surfboard', 'bottle', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana',\n    'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'pizza', 'donut',\n    'cake', 'chair', 'couch', 'bed', 'toilet', 'tv', 'laptop', 'mouse',\n    'remote', 'keyboard', 'microwave', 'oven', 'toaster', 'sink',\n    'refrigerator', 'book', 'clock', 'vase', 'scissors', 'toothbrush')  # 65\n\ntrain_metainfo = dict(classes=base_classes)\ntest_metainfo = dict(\n    classes=all_classes,\n    base_classes=base_classes,\n    novel_classes=novel_classes)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='CocoDataset',\n        metainfo=train_metainfo,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017_seen_2.json',\n        data_prefix=dict(img='train2017/'),\n        return_classes=True,\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type='CocoDataset',\n        metainfo=test_metainfo,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017_all_2.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        return_classes=True,\n    ))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='OVCocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017_all_2.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.00005, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            # 'language_model': dict(lr_mult=0),\n        }))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        max_keep_ckpts=1, save_best='coco/novel_ap50', rule='greater'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/coco/grounding_dino_swin-t_finetune_16xb4_1x_sft_coco.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=20,  # ======= important =====\n        label_map_file='data/coco/annotations/coco2017_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='ODVGDataset',\n        need_text=False,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017_od.json',\n        label_map_file='annotations/coco2017_label_map.json',\n        data_prefix=dict(img='train2017/'),\n        return_classes=True,\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        pipeline=train_pipeline))\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.00005, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            'language_model': dict(lr_mult=0.0),\n        }))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/dataset_prepare.md",
    "content": "# Data Prepare and Process\n\n## MM-GDINO-T Pre-train Dataset\n\nFor the MM-GDINO-T model, we provide a total of 5 different data combination pre-training configurations. The data is trained in a progressive accumulation manner, so users can prepare it according to their actual needs.\n\n### 1 Objects365v1\n\nThe corresponding training config is [grounding_dino_swin-t_pretrain_obj365](./grounding_dino_swin-t_pretrain_obj365.py)\n\nObjects365v1 can be downloaded from [opendatalab](https://opendatalab.com/OpenDataLab/Objects365_v1). It offers two methods of download: CLI and SDK.\n\nAfter downloading and unzipping, place the dataset or create a symbolic link to the `data/objects365v1` directory. The directory structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v1\n│   │   ├── objects365_train.json\n│   │   ├── objects365_val.json\n│   │   ├── train\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── test\n```\n\nThen, use [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) to convert it into the ODVG format required for training.\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/objects365v1/objects365_train.json -d o365v1\n```\n\nAfter the program runs successfully, it will create two new files, `o365v1_train_od.json` and `o365v1_label_map.json`, in the `data/objects365v1` directory. The complete structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v1\n│   │   ├── objects365_train.json\n│   │   ├── objects365_val.json\n│   │   ├── o365v1_train_od.json\n│   │   ├── o365v1_label_map.json\n│   │   ├── train\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── test\n```\n\n### 2 COCO 2017\n\nThe above configuration will evaluate the performance on the COCO 2017 dataset during the training process. Therefore, it is necessary to prepare the COCO 2017 dataset. You can download it from the [COCO](https://cocodataset.org/) official website or from [opendatalab](https://opendatalab.com/OpenDataLab/COCO_2017).\n\nAfter downloading and unzipping, place the dataset or create a symbolic link to the `data/coco` directory. The directory structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 3 GoldG\n\nAfter downloading the dataset, you can start training with the [grounding_dino_swin-t_pretrain_obj365_goldg](./grounding_dino_swin-t_pretrain_obj365_goldg.py) configuration.\n\nThe GoldG dataset includes the `GQA` and `Flickr30k` datasets, which are part of the MixedGrounding dataset mentioned in the GLIP paper, excluding the COCO dataset. The download links are [mdetr_annotations](https://huggingface.co/GLIPModel/GLIP/tree/main/mdetr_annotations), and the specific files currently needed are `mdetr_annotations/final_mixed_train_no_coco.json` and `mdetr_annotations/final_flickr_separateGT_train.json`.\n\nThen download the [GQA images](https://nlp.stanford.edu/data/gqa/images.zip). After downloading and unzipping, place the dataset or create a symbolic link to them in the `data/gqa` directory, with the following directory structure:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── gqa\n|   |   ├── final_mixed_train_no_coco.json\n│   │   ├── images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nThen download the [Flickr30k images](http://shannon.cs.illinois.edu/DenotationGraph/). You need to apply for access to this dataset and then download it using the provided link. After downloading and unzipping, place the dataset or create a symbolic link to them in the `data/flickr30k_entities` directory, with the following directory structure:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nFor the GQA dataset, you need to use [goldg2odvg.py](../../tools/dataset_converters/goldg2odvg.py) to convert it into the ODVG format required for training:\n\n```shell\npython tools/dataset_converters/goldg2odvg.py data/gqa/final_mixed_train_no_coco.json\n```\n\nAfter the program has run, a new file `final_mixed_train_no_coco_vg.json` will be created in the `data/gqa` directory, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── gqa\n|   |   ├── final_mixed_train_no_coco.json\n|   |   ├── final_mixed_train_no_coco_vg.json\n│   │   ├── images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nFor the Flickr30k dataset, you need to use [goldg2odvg.py](../../tools/dataset_converters/goldg2odvg.py) to convert it into the ODVG format required for training:\n\n```shell\npython tools/dataset_converters/goldg2odvg.py data/flickr30k_entities/final_flickr_separateGT_train.json\n```\n\nAfter the program has run, a new file `final_flickr_separateGT_train_vg.json` will be created in the `data/flickr30k_entities` directory, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── final_flickr_separateGT_train_vg.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 4 GRIT-20M\n\nThe corresponding training configuration is [grounding_dino_swin-t_pretrain_obj365_goldg_grit9m](./grounding_dino_swin-t_pretrain_obj365_goldg_grit9m.py).\n\nThe GRIT dataset can be downloaded using the img2dataset package from [GRIT](https://huggingface.co/datasets/zzliang/GRIT#download-image). By default, the dataset size is 1.1T, and downloading and processing it may require at least 2T of disk space, depending on your available storage capacity. After downloading, the dataset is in its original format, which includes:\n\n```text\nmmdetection\n├── configs\n├── data\n│    ├── grit_raw\n│    │    ├── 00000_stats.json\n│    │    ├── 00000.parquet\n│    │    ├── 00000.tar\n│    │    ├── 00001_stats.json\n│    │    ├── 00001.parquet\n│    │    ├── 00001.tar\n│    │    ├── ...\n```\n\nAfter downloading, further format processing is required:\n\n```shell\npython tools/dataset_converters/grit_processing.py data/grit_raw data/grit_processed\n```\n\nThe processed format is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│    ├── grit_processed\n│    │    ├── annotations\n│    │    │   ├── 00000.json\n│    │    │   ├── 00001.json\n│    │    │   ├── ...\n│    │    ├── images\n│    │    │   ├── 00000\n│    │    │   │   ├── 000000000.jpg\n│    │    │   │   ├── 000000003.jpg\n│    │    │   │   ├── 000000004.jpg\n│    │    │   │   ├── ...\n│    │    │   ├── 00001\n│    │    │   ├── ...\n```\n\nAs for the GRIT dataset, you need to use [grit2odvg.py](../../tools/dataset_converters/grit2odvg.py) to convert it to the format of ODVG:\n\n```shell\npython tools/dataset_converters/grit2odvg.py data/grit_processed/\n```\n\nAfter the program has run, a new file `grit20m_vg.json` will be created in the `data/grit_processed` directory, which has about 9M data, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│    ├── grit_processed\n|    |    ├── grit20m_vg.json\n│    │    ├── annotations\n│    │    │   ├── 00000.json\n│    │    │   ├── 00001.json\n│    │    │   ├── ...\n│    │    ├── images\n│    │    │   ├── 00000\n│    │    │   │   ├── 000000000.jpg\n│    │    │   │   ├── 000000003.jpg\n│    │    │   │   ├── 000000004.jpg\n│    │    │   │   ├── ...\n│    │    │   ├── 00001\n│    │    │   ├── ...\n```\n\n### 5 V3Det\n\nThe corresponding training configurations are:\n\n- [grounding_dino_swin-t_pretrain_obj365_goldg_v3det](./grounding_dino_swin-t_pretrain_obj365_goldg_v3det.py)\n- [grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det](./grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det.py)\n\nThe V3Det dataset can be downloaded from [opendatalab](https://opendatalab.com/V3Det/V3Det). After downloading and unzipping, place the dataset or create a symbolic link to it in the `data/v3det` directory, with the following directory structure:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── v3det\n│   │   ├── annotations\n│   │   |   ├── v3det_2023_v1_train.json\n│   │   ├── images\n│   │   │   ├── a00000066\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nThen use [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) to convert it into the ODVG format required for training:\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/v3det/annotations/v3det_2023_v1_train.json -d v3det\n```\n\nAfter the program has run, two new files `v3det_2023_v1_train_od.json` and `v3det_2023_v1_label_map.json` will be created in the `data/v3det/annotations` directory, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── v3det\n│   │   ├── annotations\n│   │   |   ├── v3det_2023_v1_train.json\n│   │   |   ├── v3det_2023_v1_train_od.json\n│   │   |   ├── v3det_2023_v1_label_map.json\n│   │   ├── images\n│   │   │   ├── a00000066\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 6 Data Splitting and Visualization\n\nConsidering that users need to prepare many datasets, which is inconvenient for confirming images and annotations before training, we provide a data splitting and visualization tool. This tool can split the dataset into a tiny version and then use a visualization script to check the correctness of the images and labels.\n\n1. Splitting the Dataset\n\nThe script is located [here](../../tools/misc/split_odvg.py). Taking `Object365 v1` as an example, the command to split the dataset is as follows:\n\n```shell\npython tools/misc/split_odvg.py data/object365_v1/ o365v1_train_od.json train your_output_dir --label-map-file o365v1_label_map.json -n 200\n```\n\nAfter running the above script, it will create a folder structure in the `your_output_dir` directory identical to `data/object365_v1/`, but it will only save 200 training images and their corresponding json files for convenient user review.\n\n2. Visualizing the Original Dataset\n\nThe script is located [here](../../tools/analysis_tools/browse_grounding_raw.py). Taking `Object365 v1` as an example, the command to visualize the dataset is as follows:\n\n```shell\npython tools/analysis_tools/browse_grounding_raw.py data/object365_v1/ o365v1_train_od.json train --label-map-file o365v1_label_map.json -o your_output_dir --not-show\n```\n\nAfter running the above script, it will generate images in the `your_output_dir` directory that include both the pictures and their labels, making it convenient for users to review.\n\n3. Visualizing the Output Dataset\n\nThe script is located [here](../../tools/analysis_tools/browse_grounding_dataset.py). Users can use this script to view the results of the dataset output, including the results of data augmentation. Taking `Object365 v1` as an example, the command to visualize the dataset is as follows:\n\n```shell\npython tools/analysis_tools/browse_grounding_dataset.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py  -o your_output_dir --not-show\n```\n\nAfter running the above script, it will generate images in the `your_output_dir` directory that include both the pictures and their labels, making it convenient for users to review.\n\n## MM-GDINO-L Pre-training Data Preparation and Processing\n\n### 1 Object365 v2\n\nObjects365_v2 can be downloaded from [opendatalab](https://opendatalab.com/OpenDataLab/Objects365). It offers two download methods: CLI and SDK.\n\nAfter downloading and unzipping, place the dataset or create a symbolic link to it in the `data/objects365v2` directory, with the following directory structure:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v2\n│   │   ├── annotations\n│   │   │   ├── zhiyuan_objv2_train.json\n│   │   ├── train\n│   │   │   ├── patch0\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nSince some category names in Objects365v2 are incorrect, it is necessary to correct them first.\n\n```shell\npython tools/dataset_converters/fix_o365_names.py\n```\n\nA new annotation file `zhiyuan_objv2_train_fixname.json` will be generated in the `data/objects365v2/annotations` directory.\n\nThen use [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) to convert it into the ODVG format required for training:\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/objects365v2/annotations/zhiyuan_objv2_train_fixname.json -d o365v2\n```\n\nAfter the program has run, two new files `zhiyuan_objv2_train_fixname_od.json` and `o365v2_label_map.json` will be created in the `data/objects365v2` directory, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v2\n│   │   ├── annotations\n│   │   │   ├── zhiyuan_objv2_train.json\n│   │   │   ├── zhiyuan_objv2_train_fixname.json\n│   │   │   ├── zhiyuan_objv2_train_fixname_od.json\n│   │   │   ├── o365v2_label_map.json\n│   │   ├── train\n│   │   │   ├── patch0\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 2 OpenImages v6\n\nOpenImages v6 can be downloaded from the [official website](https://storage.googleapis.com/openimages/web/download_v6.html). Due to the large size of the dataset, it may take some time to download. After completion, the file structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── OpenImages\n│   │   ├── annotations\n|   │   │   ├── oidv6-train-annotations-bbox.csv\n|   │   │   ├── class-descriptions-boxable.csv\n│   │   ├── OpenImages\n│   │   │   ├── train\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nThen use [openimages2odvg.py](../../tools/dataset_converters/openimages2odvg.py) to convert it into the ODVG format required for training:\n\n```shell\npython tools/dataset_converters/openimages2odvg.py data/OpenImages/annotations\n```\n\nAfter the program has run, two new files `oidv6-train-annotation_od.json` and `openimages_label_map.json` will be created in the `data/OpenImages/annotations` directory, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── OpenImages\n│   │   ├── annotations\n|   │   │   ├── oidv6-train-annotations-bbox.csv\n|   │   │   ├── class-descriptions-boxable.csv\n|   │   │   ├── oidv6-train-annotations_od.json\n|   │   │   ├── openimages_label_map.json\n│   │   ├── OpenImages\n│   │   │   ├── train\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 3 V3Det\n\nReferring to the data preparation section of the previously mentioned MM-GDINO-T pre-training data preparation and processing, the complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── v3det\n│   │   ├── annotations\n│   │   |   ├── v3det_2023_v1_train.json\n│   │   |   ├── v3det_2023_v1_train_od.json\n│   │   |   ├── v3det_2023_v1_label_map.json\n│   │   ├── images\n│   │   │   ├── a00000066\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 4 LVIS 1.0\n\nPlease refer to the `2 LVIS 1.0` section of the later `Fine-tuning Dataset Preparation`. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── lvis_v1_train_od.json\n│   │   │   ├── lvis_v1_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 5 COCO2017 OD\n\nYou can refer to the earlier section `MM-GDINO-T Pre-training Data Preparation and Processing` for data preparation. For convenience in subsequent processing, please create a symbolic link or move the downloaded [mdetr_annotations](https://huggingface.co/GLIPModel/GLIP/tree/main/mdetr_annotations) folder to the `data/coco` path. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\nDue to some overlap between COCO2017 train and RefCOCO/RefCOCO+/RefCOCOg/gRefCOCO val, if not removed in advance, there will be data leakage when evaluating RefExp.\n\n```shell\npython tools/dataset_converters/remove_cocotrain2017_from_refcoco.py data/coco/mdetr_annotations data/coco/annotations/instances_train2017.json\n```\n\nA new file `instances_train2017_norefval.json` will be created in the `data/coco/annotations` directory. Finally, use [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) to convert it into the ODVG format required for training:\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/coco/annotations/instances_train2017_norefval.json -d coco\n```\n\nTwo new files `instances_train2017_norefval_od.json` and `coco_label_map.json` will be created in the `data/coco/annotations` directory, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2017_norefval_od.json\n│   │   │   ├── coco_label_map.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\nNote: There are 15,000 images that overlap between the COCO2017 train and LVIS 1.0 val datasets. Therefore, if the COCO2017 train dataset is used in training, the evaluation results of LVIS 1.0 val will have a data leakage issue. However, LVIS 1.0 minival does not have this problem.\n\n### 6 GoldG\n\nPlease refer to the section on `MM-GDINO-T Pre-training Data Preparation and Processing`.\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── final_flickr_separateGT_train_vg.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   ├── gqa\n|   |   ├── final_mixed_train_no_coco.json\n|   |   ├── final_mixed_train_no_coco_vg.json\n│   │   ├── images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 7 COCO2014 VG\n\nMDetr provides a Phrase Grounding version of the COCO2014 train annotations. The original annotation file is named `final_mixed_train.json`, and similar to the previous structure, the file structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_mixed_train.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nWe can extract the COCO portion of the data from `final_mixed_train.json`.\n\n```shell\npython tools/dataset_converters/extract_coco_from_mixed.py data/coco/mdetr_annotations/final_mixed_train.json\n```\n\nA new file named `final_mixed_train_only_coco.json` will be created in the `data/coco/mdetr_annotations` directory. Finally, use [goldg2odvg.py](../../tools/dataset_converters/goldg2odvg.py) to convert it into the ODVG format required for training:\n\n```shell\npython tools/dataset_converters/goldg2odvg.py data/coco/mdetr_annotations/final_mixed_train_only_coco.json\n```\n\nA new file named `final_mixed_train_only_coco_vg.json` will be created in the `data/coco/mdetr_annotations` directory, with the complete structure as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_mixed_train.json\n│   │   │   ├── final_mixed_train_only_coco.json\n│   │   │   ├── final_mixed_train_only_coco_vg.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\nNote: COCO2014 train and COCO2017 val do not have duplicate images, so there is no need to worry about data leakage issues in COCO evaluation.\n\n### 8 Referring Expression Comprehension\n\nThere are a total of 4 datasets included. For data preparation, please refer to the `Fine-tuning Dataset Preparation` section.\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcoco_train_vg.json\n│   │   │   ├── finetune_refcoco+_train_vg.json\n│   │   │   ├── finetune_refcocog_train_vg.json\n│   │   │   ├── finetune_grefcoco_train_vg.json\n```\n\n### 9 GRIT-20M\n\nPlease refer to the `MM-GDINO-T Pre-training Data Preparation and Processing` section.\n\n## Preparation of Evaluation Dataset\n\n### 1 COCO 2017\n\nThe data preparation process is consistent with the previous descriptions, and the final structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 2 LVIS 1.0\n\nThe LVIS 1.0 val dataset includes both mini and full versions. The significance of the mini version is:\n\n1. The full LVIS val evaluation dataset is quite large, and conducting an evaluation with it can take a significant amount of time.\n2. In the full LVIS val dataset, there are 15,000 images from the COCO2017 train dataset. If a user has used the COCO2017 data for training, there can be a data leakage issue when evaluating on the full LVIS val dataset\n\nThe LVIS 1.0 dataset contains images that are exactly the same as the COCO2017 dataset, with the addition of new annotations. You can download the minival annotation file from [here](https://huggingface.co/GLIPModel/GLIP/blob/main/lvis_v1_minival_inserted_image_name.json), and the val 1.0 annotation file from [here](https://huggingface.co/GLIPModel/GLIP/blob/main/lvis_od_val.json). The final structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 3 ODinW\n\nODinW, which stands for Object Detection in the Wild, is a dataset used to evaluate the generalization capability of grounding pre-trained models in different real-world scenarios. It consists of two subsets, ODinW13 and ODinW35, representing datasets composed of 13 and 35 different datasets, respectively. You can download it from [here](https://huggingface.co/GLIPModel/GLIP/tree/main/odinw_35), and then unzip each file. The final structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── odinw\n│   │   ├── AerialMaritimeDrone\n│   │   |   |── large\n│   │   |   |   ├── test\n│   │   |   |   ├── train\n│   │   |   |   ├── valid\n│   │   |   |── tiled\n│   │   ├── AmericanSignLanguageLetters\n│   │   ├── Aquarium\n│   │   ├── BCCD\n│   │   ├── ...\n```\n\nWhen evaluating ODinW35, custom prompts are required. Therefore, it's necessary to preprocess the annotated JSON files in advance. You can use the [override_category.py](./odinw/override_category.py) script for this purpose. After processing, it will generate new annotation files without overwriting the original ones.\n\n```shell\npython configs/mm_grounding_dino/odinw/override_category.py data/odinw/\n```\n\n### 4 DOD\n\nDOD stands for Described Object Detection, and it is introduced in the paper titled [Described Object Detection: Liberating Object Detection with Flexible Expressions](https://arxiv.org/abs/2307.12813). You can download the dataset from [here](https://github.com/shikras/d-cube?tab=readme-ov-file). The final structure of the dataset is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── d3\n│   │   ├── d3_images\n│   │   ├── d3_json\n│   │   ├── d3_pkl\n```\n\n### 5 Flickr30k Entities\n\nIn the previous GoldG data preparation section, we downloaded the necessary files for training with Flickr30k. For evaluation, you will need 2 JSON files, which you can download from [here](https://huggingface.co/GLIPModel/GLIP/blob/main/mdetr_annotations/final_flickr_separateGT_val.json) and [here](https://huggingface.co/GLIPModel/GLIP/blob/main/mdetr_annotations/final_flickr_separateGT_test.json). The final structure of the dataset is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── final_flickr_separateGT_val.json\n│   │   ├── final_flickr_separateGT_test.json\n│   │   ├── final_flickr_separateGT_train_vg.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 6 Referring Expression Comprehension\n\nReferential Expression Comprehension includes 4 datasets: RefCOCO, RefCOCO+, RefCOCOg, and gRefCOCO. The images used in these 4 datasets are from COCO2014 train, similar to COCO2017. You can download the images from the official COCO website or opendatalab. The annotations can be directly downloaded from [here](https://huggingface.co/GLIPModel/GLIP/tree/main/mdetr_annotations). The mdetr_annotations folder contains a large number of annotations, so you can choose to download only the JSON files you need. The final structure of the dataset is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcocog_test.json\n```\n\nPlease note that gRefCOCO is introduced in [GREC: Generalized Referring Expression Comprehension](https://arxiv.org/abs/2308.16182) and is not available in the `mdetr_annotations` folder. You will need to handle it separately. Here are the specific steps:\n\n1. Download [gRefCOCO](https://github.com/henghuiding/gRefCOCO?tab=readme-ov-file) and unzip it into the `data/coco/` folder.\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   ├── grefs\n│   │   │   ├── grefs(unc).json\n│   │   │   ├── instances.json\n```\n\n2. Convert to COCO format\n\nYou can use the official [conversion script](https://github.com/henghuiding/gRefCOCO/blob/b4b1e55b4d3a41df26d6b7d843ea011d581127d4/mdetr/scripts/fine-tuning/grefexp_coco_format.py) provided by gRefCOCO. Please note that you need to uncomment line 161 and comment out line 160 in the script to obtain the full JSON file.\n\n```shell\n# you need to clone the official repo\ngit clone https://github.com/henghuiding/gRefCOCO.git\ncd gRefCOCO/mdetr\npython scripts/fine-tuning/grefexp_coco_format.py --data_path ../../data/coco/grefs --out_path ../../data/coco/mdetr_annotations/ --coco_path ../../data/coco\n```\n\nFour JSON files will be generated in the `data/coco/mdetr_annotations/` folder. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_grefcoco_train.json\n│   │   │   ├── finetune_grefcoco_val.json\n│   │   │   ├── finetune_grefcoco_testA.json\n│   │   │   ├── finetune_grefcoco_testB.json\n```\n\n## Fine-Tuning Dataset Preparation\n\n### 1 COCO 2017\n\nCOCO is the most commonly used dataset in the field of object detection, and we aim to explore its fine-tuning modes more comprehensively. From current developments, there are a total of three fine-tuning modes:\n\n1. Closed-set fine-tuning, where the description on the text side cannot be modified after fine-tuning, transforms into a closed-set algorithm. This approach maximizes performance on COCO but loses generality.\n2. Open-set continued pretraining fine-tuning involves using pretraining methods consistent with the COCO dataset. There are two approaches to this: the first is to reduce the learning rate and fix certain modules, fine-tuning only on the COCO dataset; the second is to mix COCO data with some of the pre-trained data. The goal of both approaches is to improve performance on the COCO dataset as much as possible without compromising generalization.\n3. Open-vocabulary fine-tuning involves adopting a common practice in the OVD (Open-Vocabulary Detection) domain. It divides COCO categories into base classes and novel classes. During training, fine-tuning is performed only on the base classes, while evaluation is conducted on both base and novel classes. This approach allows for the assessment of COCO OVD capabilities, with the goal of improving COCO dataset performance without compromising generalization as much as possible.\n\n\\*\\*(1) Closed-set Fine-tuning \\*\\*\n\nThis section does not require data preparation; you can directly use the data you have prepared previously.\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n**(2) Open-set Continued Pretraining Fine-tuning**\nTo use this approach, you need to convert the COCO training data into ODVG format. You can use the following command for conversion:\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/coco/annotations/instances_train2017.json -d coco\n```\n\nThis will generate new files, `instances_train2017_od.json` and `coco2017_label_map.json`, in the `data/coco/annotations/` directory. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_train2017_od.json\n│   │   │   ├── coco2017_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\nOnce you have obtained the data, you can choose whether to perform individual pretraining or mixed pretraining.\n\n**(3) Open-vocabulary Fine-tuning**\nFor this approach, you need to convert the COCO training data into OVD (Open-Vocabulary Detection) format. You can use the following command for conversion:\n\n```shell\npython tools/dataset_converters/coco2ovd.py data/coco/\n```\n\nThis will generate new files, `instances_val2017_all_2.json` and `instances_val2017_seen_2.json`, in the `data/coco/annotations/` directory. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_train2017_od.json\n│   │   │   ├── instances_val2017_all_2.json\n│   │   │   ├── instances_val2017_seen_2.json\n│   │   │   ├── coco2017_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\nYou can then proceed to train and test directly using the [configuration](coco/grounding_dino_swin-t_finetune_16xb4_1x_coco_48_17.py).\n\n### 2 LVIS 1.0\n\nLVIS is a dataset that includes 1,203 classes, making it a valuable dataset for fine-tuning. Due to its large number of classes, it's not feasible to perform closed-set fine-tuning. Therefore, we can only use open-set continued pretraining fine-tuning and open-vocabulary fine-tuning on LVIS.\n\nYou need to prepare the LVIS training JSON files first, which you can download from [here](https://www.lvisdataset.org/dataset). We only need `lvis_v1_train.json` and `lvis_v1_val.json`. After downloading them, place them in the `data/coco/annotations/` directory, and then run the following command:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n(1) Open-set continued pretraining fine-tuning\n\nConvert to ODVG format using the following command:\n\n```shell\npython tools/dataset_converters/lvis2odvg.py data/coco/annotations/lvis_v1_train.json\n```\n\nIt will generate new files, `lvis_v1_train_od.json` and `lvis_v1_label_map.json`, in the `data/coco/annotations/` directory, and the complete dataset structure will look like this:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── lvis_v1_train_od.json\n│   │   │   ├── lvis_v1_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\nYou can directly use the provided [configuration](lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis.py) for training and testing, or you can modify the configuration to mix it with some of the pretraining datasets as needed.\n\n**(2) Open Vocabulary Fine-tuning**\n\nConvert to OVD format using the following command:\n\n```shell\npython tools/dataset_converters/lvis2ovd.py data/coco/\n```\n\nNew `lvis_v1_train_od_norare.json` and `lvis_v1_label_map_norare.json` will be generated under `data/coco/annotations/`, and the complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── lvis_v1_train_od.json\n│   │   │   ├── lvis_v1_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   │   ├── lvis_v1_train_od_norare.json\n│   │   │   ├── lvis_v1_label_map_norare.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n然Then you can directly use the [configuration](lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis_866_337.py) for training and testing.\n\n### 3 RTTS\n\nRTTS is a foggy weather dataset, which contains 4,322 foggy images, including five classes: bicycle, bus, car, motorbike, and person. It can be downloaded from [here](https://drive.google.com/file/d/15Ei1cHGVqR1mXFep43BO7nkHq1IEGh1e/view), and then extracted to the `data/RTTS/` folder. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── RTTS\n│   │   ├── annotations_json\n│   │   ├── annotations_xml\n│   │   ├── ImageSets\n│   │   ├── JPEGImages\n```\n\n### 4 RUOD\n\nRUOD is an underwater object detection dataset. You can download it from [here](https://drive.google.com/file/d/1hxtbdgfVveUm_DJk5QXkNLokSCTa_E5o/view), and then extract it to the `data/RUOD/` folder. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── RUOD\n│   │   ├── Environment_pic\n│   │   ├── Environmet_ANN\n│   │   ├── RUOD_ANN\n│   │   ├── RUOD_pic\n```\n\n### 5 Brain Tumor\n\nBrain Tumor is a 2D detection dataset in the medical field. You can download it from [here](https://universe.roboflow.com/roboflow-100/brain-tumor-m2pbp/dataset/2), please make sure to choose the `COCO JSON` format. Then extract it to the `data/brain_tumor_v2/` folder. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── brain_tumor_v2\n│   │   ├── test\n│   │   ├── train\n│   │   ├── valid\n```\n\n### 6 Cityscapes\n\nCityscapes is an urban street scene dataset. You can download it from [here](https://www.cityscapes-dataset.com/) or from opendatalab, and then extract it to the `data/cityscapes/` folder. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n```\n\nAfter downloading, you can use the [cityscapes.py](../../tools/dataset_converters/cityscapes.py) script to generate the required JSON format.\n\n```shell\npython tools/dataset_converters/cityscapes.py data/cityscapes/\n```\n\nThree new JSON files will be generated in the annotations directory. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   │   ├── instancesonly_filtered_gtFine_train.json\n│   │   │   ├── instancesonly_filtered_gtFine_val.json\n│   │   │   ├── instancesonly_filtered_gtFine_test.json\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n```\n\n### 7 People in Painting\n\nPeople in Painting is an oil painting dataset that you can download from [here](https://universe.roboflow.com/roboflow-100/people-in-paintings/dataset/2). Please make sure to choose the `COCO JSON` format. After downloading, unzip the dataset to the `data/people_in_painting_v2/` folder. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── people_in_painting_v2\n│   │   ├── test\n│   │   ├── train\n│   │   ├── valid\n```\n\n### 8 Referring Expression Comprehension\n\nFine-tuning for Referential Expression Comprehension is similar to what was described earlier and includes four datasets. The dataset preparation for evaluation has already been organized. The complete dataset structure is as follows:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcocog_test.json\n```\n\nThen we need to convert it to the required ODVG format. Please use the [refcoco2odvg.py](../../tools/dataset_converters/refcoco2odvg.py) script to perform the conversion.\n\n```shell\npython tools/dataset_converters/refcoco2odvg.py data/coco/mdetr_annotations\n```\n\nThe converted dataset structure will include 4 new JSON files in the `data/coco/mdetr_annotations` directory. Here is the structure of the converted dataset:\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcoco_train_vg.json\n│   │   │   ├── finetune_refcoco+_train_vg.json\n│   │   │   ├── finetune_refcocog_train_vg.json\n│   │   │   ├── finetune_grefcoco_train_vg.json\n```\n"
  },
  {
    "path": "configs/mm_grounding_dino/dataset_prepare_zh-CN.md",
    "content": "# 数据准备和处理\n\n## MM-GDINO-T 预训练数据准备和处理\n\nMM-GDINO-T 模型中我们一共提供了 5 种不同数据组合的预训练配置，数据采用逐步累加的方式进行训练，因此用户可以根据自己的实际需求准备数据。\n\n### 1 Objects365 v1\n\n对应的训练配置为 [grounding_dino_swin-t_pretrain_obj365](./grounding_dino_swin-t_pretrain_obj365.py)\n\nObjects365_v1 可以从 [opendatalab](https://opendatalab.com/OpenDataLab/Objects365_v1) 下载，其提供了 CLI 和 SDK 两者下载方式。\n\n下载并解压后，将其放置或者软链接到 `data/objects365v1` 目录下，目录结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v1\n│   │   ├── objects365_train.json\n│   │   ├── objects365_val.json\n│   │   ├── train\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── test\n```\n\n然后使用 [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/objects365v1/objects365_train.json -d o365v1\n```\n\n程序运行完成后会在 `data/objects365v1` 目录下创建 `o365v1_train_od.json` 和 `o365v1_label_map.json` 两个新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v1\n│   │   ├── objects365_train.json\n│   │   ├── objects365_val.json\n│   │   ├── o365v1_train_od.json\n│   │   ├── o365v1_label_map.json\n│   │   ├── train\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── test\n```\n\n### 2 COCO 2017\n\n上述配置在训练过程中会评估 COCO 2017 数据集的性能，因此需要准备 COCO 2017 数据集。你可以从 [COCO](https://cocodataset.org/) 官网下载或者从 [opendatalab](https://opendatalab.com/OpenDataLab/COCO_2017) 下载\n\n下载并解压后，将其放置或者软链接到 `data/coco` 目录下，目录结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 3 GoldG\n\n下载该数据集后就可以训练 [grounding_dino_swin-t_pretrain_obj365_goldg](./grounding_dino_swin-t_pretrain_obj365_goldg.py) 配置了。\n\nGoldG 数据集包括 `GQA` 和 `Flickr30k` 两个数据集，来自 GLIP 论文中提到的 MixedGrounding 数据集，其排除了 COCO 数据集。下载链接为 [mdetr_annotations](https://huggingface.co/GLIPModel/GLIP/tree/main/mdetr_annotations)，我们目前需要的是 `mdetr_annotations/final_mixed_train_no_coco.json` 和 `mdetr_annotations/final_flickr_separateGT_train.json` 文件。\n\n然后下载 [GQA images](https://nlp.stanford.edu/data/gqa/images.zip) 图片。下载并解压后，将其放置或者软链接到 `data/gqa` 目录下，目录结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── gqa\n|   |   ├── final_mixed_train_no_coco.json\n│   │   ├── images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n然后下载 [Flickr30k images](http://shannon.cs.illinois.edu/DenotationGraph/) 图片。这个数据下载需要先申请，再获得下载链接后才可以下载。下载并解压后，将其放置或者软链接到 `data/flickr30k_entities` 目录下，目录结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n对于 GQA 数据集，你需要使用 [goldg2odvg.py](../../tools/dataset_converters/goldg2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/goldg2odvg.py data/gqa/final_mixed_train_no_coco.json\n```\n\n程序运行完成后会在 `data/gqa` 目录下创建 `final_mixed_train_no_coco_vg.json` 新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── gqa\n|   |   ├── final_mixed_train_no_coco.json\n|   |   ├── final_mixed_train_no_coco_vg.json\n│   │   ├── images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n对于 Flickr30k 数据集，你需要使用 [goldg2odvg.py](../../tools/dataset_converters/goldg2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/goldg2odvg.py data/flickr30k_entities/final_flickr_separateGT_train.json\n```\n\n程序运行完成后会在 `data/flickr30k_entities` 目录下创建 `final_flickr_separateGT_train_vg.json` 新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── final_flickr_separateGT_train_vg.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 4 GRIT-20M\n\n对应的训练配置为 [grounding_dino_swin-t_pretrain_obj365_goldg_grit9m](./grounding_dino_swin-t_pretrain_obj365_goldg_grit9m.py)\n\nGRIT数据集可以从 [GRIT](https://huggingface.co/datasets/zzliang/GRIT#download-image) 中使用 img2dataset 包下载，默认指令下载后数据集大小为 1.1T，下载和处理预估需要至少 2T 硬盘空间，可根据硬盘容量酌情下载。下载后原始格式为：\n\n```text\nmmdetection\n├── configs\n├── data\n│    ├── grit_raw\n│    │    ├── 00000_stats.json\n│    │    ├── 00000.parquet\n│    │    ├── 00000.tar\n│    │    ├── 00001_stats.json\n│    │    ├── 00001.parquet\n│    │    ├── 00001.tar\n│    │    ├── ...\n```\n\n下载后需要对格式进行进一步处理:\n\n```shell\npython tools/dataset_converters/grit_processing.py data/grit_raw data/grit_processed\n```\n\n处理后的格式为：\n\n```text\nmmdetection\n├── configs\n├── data\n│    ├── grit_processed\n│    │    ├── annotations\n│    │    │   ├── 00000.json\n│    │    │   ├── 00001.json\n│    │    │   ├── ...\n│    │    ├── images\n│    │    │   ├── 00000\n│    │    │   │   ├── 000000000.jpg\n│    │    │   │   ├── 000000003.jpg\n│    │    │   │   ├── 000000004.jpg\n│    │    │   │   ├── ...\n│    │    │   ├── 00001\n│    │    │   ├── ...\n```\n\n对于 GRIT 数据集，你需要使用 [grit2odvg.py](../../tools/dataset_converters/grit2odvg.py) 转化成需要的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/grit2odvg.py data/grit_processed/\n```\n\n程序运行完成后会在 `data/grit_processed` 目录下创建 `grit20m_vg.json` 新文件，大概包含 9M 条数据，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│    ├── grit_processed\n|    |    ├── grit20m_vg.json\n│    │    ├── annotations\n│    │    │   ├── 00000.json\n│    │    │   ├── 00001.json\n│    │    │   ├── ...\n│    │    ├── images\n│    │    │   ├── 00000\n│    │    │   │   ├── 000000000.jpg\n│    │    │   │   ├── 000000003.jpg\n│    │    │   │   ├── 000000004.jpg\n│    │    │   │   ├── ...\n│    │    │   ├── 00001\n│    │    │   ├── ...\n```\n\n### 5 V3Det\n\n对应的训练配置为\n\n- [grounding_dino_swin-t_pretrain_obj365_goldg_v3det](./grounding_dino_swin-t_pretrain_obj365_goldg_v3det.py)\n- [grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det](./grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det.py)\n\nV3Det 数据集下载可以从 [opendatalab](https://opendatalab.com/V3Det/V3Det) 下载，下载并解压后，将其放置或者软链接到 `data/v3det` 目录下，目录结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── v3det\n│   │   ├── annotations\n│   │   |   ├── v3det_2023_v1_train.json\n│   │   ├── images\n│   │   │   ├── a00000066\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n然后使用 [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/v3det/annotations/v3det_2023_v1_train.json -d v3det\n```\n\n程序运行完成后会在 `data/v3det/annotations` 目录下创建目录下创建 `v3det_2023_v1_train_od.json` 和 `v3det_2023_v1_label_map.json` 两个新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── v3det\n│   │   ├── annotations\n│   │   |   ├── v3det_2023_v1_train.json\n│   │   |   ├── v3det_2023_v1_train_od.json\n│   │   |   ├── v3det_2023_v1_label_map.json\n│   │   ├── images\n│   │   │   ├── a00000066\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 6 数据切分和可视化\n\n考虑到用户需要准备的数据集过多，不方便对图片和标注进行训练前确认，因此我们提供了一个数据切分和可视化的工具，可以将数据集切分为 tiny 版本，然后使用可视化脚本查看图片和标签正确性。\n\n1. 切分数据集\n\n脚本位于 [这里](../../tools/misc/split_odvg.py), 以 `Object365 v1` 为例，切分数据集的命令如下：\n\n```shell\npython tools/misc/split_odvg.py data/object365_v1/ o365v1_train_od.json train your_output_dir --label-map-file o365v1_label_map.json -n 200\n```\n\n上述脚本运行后会在 `your_output_dir` 目录下创建和 `data/object365_v1/` 一样的文件夹结构，但是只会保存 200 张训练图片和对应的 json，方便用户查看。\n\n2. 可视化原始数据集\n\n脚本位于 [这里](../../tools/analysis_tools/browse_grounding_raw.py), 以 `Object365 v1` 为例，可视化数据集的命令如下：\n\n```shell\npython tools/analysis_tools/browse_grounding_raw.py data/object365_v1/ o365v1_train_od.json train --label-map-file o365v1_label_map.json -o your_output_dir --not-show\n```\n\n上述脚本运行后会在 `your_output_dir` 目录下生成同时包括图片和标签的图片，方便用户查看。\n\n3. 可视化 dataset 输出的数据集\n\n脚本位于 [这里](../../tools/analysis_tools/browse_grounding_dataset.py), 用户可以通过该脚本查看 dataset 输出的结果即包括了数据增强的结果。 以 `Object365 v1` 为例，可视化数据集的命令如下：\n\n```shell\npython tools/analysis_tools/browse_grounding_dataset.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py  -o your_output_dir --not-show\n```\n\n上述脚本运行后会在 `your_output_dir` 目录下生成同时包括图片和标签的图片，方便用户查看。\n\n## MM-GDINO-L 预训练数据准备和处理\n\n### 1 Object365 v2\n\nObjects365_v2 可以从 [opendatalab](https://opendatalab.com/OpenDataLab/Objects365) 下载，其提供了 CLI 和 SDK 两者下载方式。\n\n下载并解压后，将其放置或者软链接到 `data/objects365v2` 目录下，目录结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v2\n│   │   ├── annotations\n│   │   │   ├── zhiyuan_objv2_train.json\n│   │   ├── train\n│   │   │   ├── patch0\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n由于 objects365v2 类别中有部分类名是错误的，因此需要先进行修正。\n\n```shell\npython tools/dataset_converters/fix_o365_names.py\n```\n\n会在 `data/objects365v2/annotations` 下生成新的标注文件 `zhiyuan_objv2_train_fixname.json`。\n\n然后使用 [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/objects365v2/annotations/zhiyuan_objv2_train_fixname.json -d o365v2\n```\n\n程序运行完成后会在 `data/objects365v2` 目录下创建 `zhiyuan_objv2_train_fixname_od.json` 和 `o365v2_label_map.json` 两个新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── objects365v2\n│   │   ├── annotations\n│   │   │   ├── zhiyuan_objv2_train.json\n│   │   │   ├── zhiyuan_objv2_train_fixname.json\n│   │   │   ├── zhiyuan_objv2_train_fixname_od.json\n│   │   │   ├── o365v2_label_map.json\n│   │   ├── train\n│   │   │   ├── patch0\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 2 OpenImages v6\n\nOpenImages v6 可以从 [官网](https://storage.googleapis.com/openimages/web/download_v6.html) 下载，由于数据集比较大，需要花费一定的时间，下载完成后文件结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── OpenImages\n│   │   ├── annotations\n|   │   │   ├── oidv6-train-annotations-bbox.csv\n|   │   │   ├── class-descriptions-boxable.csv\n│   │   ├── OpenImages\n│   │   │   ├── train\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n然后使用 [openimages2odvg.py](../../tools/dataset_converters/openimages2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/openimages2odvg.py data/OpenImages/annotations\n```\n\n程序运行完成后会在 `data/OpenImages/annotations` 目录下创建 `oidv6-train-annotation_od.json` 和 `openimages_label_map.json` 两个新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── OpenImages\n│   │   ├── annotations\n|   │   │   ├── oidv6-train-annotations-bbox.csv\n|   │   │   ├── class-descriptions-boxable.csv\n|   │   │   ├── oidv6-train-annotations_od.json\n|   │   │   ├── openimages_label_map.json\n│   │   ├── OpenImages\n│   │   │   ├── train\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 3 V3Det\n\n参见前面的 MM-GDINO-T 预训练数据准备和处理 数据准备部分，完整数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── v3det\n│   │   ├── annotations\n│   │   |   ├── v3det_2023_v1_train.json\n│   │   |   ├── v3det_2023_v1_train_od.json\n│   │   |   ├── v3det_2023_v1_label_map.json\n│   │   ├── images\n│   │   │   ├── a00000066\n│   │   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 4 LVIS 1.0\n\n参见后面的 `微调数据集准备` 的 `2 LVIS 1.0` 部分。完整数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── lvis_v1_train_od.json\n│   │   │   ├── lvis_v1_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 5 COCO2017 OD\n\n数据准备可以参考前面的 `MM-GDINO-T 预训练数据准备和处理` 部分。为了方便后续处理，请将下载的 [mdetr_annotations](https://huggingface.co/GLIPModel/GLIP/tree/main/mdetr_annotations) 文件夹软链接或者移动到 `data/coco` 路径下\n完整数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n由于 COCO2017 train 和 RefCOCO/RefCOCO+/RefCOCOg/gRefCOCO val 中存在部分重叠，如果不提前移除，在评测 RefExp 时候会存在数据泄露。\n\n```shell\npython tools/dataset_converters/remove_cocotrain2017_from_refcoco.py data/coco/mdetr_annotations data/coco/annotations/instances_train2017.json\n```\n\n会在 `data/coco/annotations` 目录下创建 `instances_train2017_norefval.json` 新文件。最后使用 [coco2odvg.py](../../tools/dataset_converters/coco2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/coco/annotations/instances_train2017_norefval.json -d coco\n```\n\n会在 `data/coco/annotations` 目录下创建 `instances_train2017_norefval_od.json` 和 `coco_label_map.json` 两个新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2017_norefval_od.json\n│   │   │   ├── coco_label_map.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n注意： COCO2017 train 和 LVIS 1.0 val 数据集有 15000 张图片重复，因此一旦在训练中使用了 COCO2017 train，那么 LVIS 1.0 val 的评测结果就存在数据泄露问题，LVIS 1.0 minival 没有这个问题。\n\n### 6 GoldG\n\n参见 MM-GDINO-T 预训练数据准备和处理 部分\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── final_flickr_separateGT_train_vg.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   ├── gqa\n|   |   ├── final_mixed_train_no_coco.json\n|   |   ├── final_mixed_train_no_coco_vg.json\n│   │   ├── images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 7 COCO2014 VG\n\nMDetr 中提供了 COCO2014 train 的 Phrase Grounding 版本标注， 最原始标注文件为 `final_mixed_train.json`，和之前类似，文件结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_mixed_train.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n我们可以从 `final_mixed_train.json` 中提取出 COCO 部分数据\n\n```shell\npython tools/dataset_converters/extract_coco_from_mixed.py data/coco/mdetr_annotations/final_mixed_train.json\n```\n\n会在 `data/coco/mdetr_annotations` 目录下创建 `final_mixed_train_only_coco.json` 新文件，最后使用 [goldg2odvg.py](../../tools/dataset_converters/goldg2odvg.py) 转换为训练所需的 ODVG 格式：\n\n```shell\npython tools/dataset_converters/goldg2odvg.py data/coco/mdetr_annotations/final_mixed_train_only_coco.json\n```\n\n会在 `data/coco/mdetr_annotations` 目录下创建 `final_mixed_train_only_coco_vg.json` 新文件，完整结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_mixed_train.json\n│   │   │   ├── final_mixed_train_only_coco.json\n│   │   │   ├── final_mixed_train_only_coco_vg.json\n│   │   │   ├── ...\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n注意： COCO2014 train 和 COCO2017 val 没有重复图片，因此不用担心 COCO 评测的数据泄露问题。\n\n### 8 Referring Expression Comprehension\n\n其一共包括 4 个数据集。数据准备部分请参见 微调数据集准备 部分。\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcoco_train_vg.json\n│   │   │   ├── finetune_refcoco+_train_vg.json\n│   │   │   ├── finetune_refcocog_train_vg.json\n│   │   │   ├── finetune_grefcoco_train_vg.json\n```\n\n### 9 GRIT-20M\n\n参见 MM-GDINO-T 预训练数据准备和处理 部分\n\n## 评测数据集准备\n\n### 1 COCO 2017\n\n数据准备流程和前面描述一致，最终结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 2 LVIS 1.0\n\nLVIS 1.0 val 数据集包括 mini 和全量两个版本，mini 版本存在的意义是：\n\n1. LVIS val 全量评测数据集比较大，评测一次需要比较久的时间\n2. LVIS val 全量数据集中包括了 15000 张 COCO2017 train, 如果用户使用了 COCO2017 数据进行训练，那么将存在数据泄露问题\n\nLVIS 1.0 图片和 COCO2017 数据集图片完全一样，只是提供了新的标注而已，minival 标注文件可以从 [这里](https://huggingface.co/GLIPModel/GLIP/blob/main/lvis_v1_minival_inserted_image_name.json)下载， val 1.0 标注文件可以从 [这里](https://huggingface.co/GLIPModel/GLIP/blob/main/lvis_od_val.json) 下载。 最终结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n### 3 ODinW\n\nODinw 全称为 Object Detection in the Wild，是用于验证 grounding 预训练模型在不同实际场景中的泛化能力的数据集，其包括两个子集，分别是 ODinW13 和 ODinW35，代表是由 13 和 35 个数据集组成的。你可以从 [这里](https://huggingface.co/GLIPModel/GLIP/tree/main/odinw_35)下载，然后对每个文件进行解压，最终结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── odinw\n│   │   ├── AerialMaritimeDrone\n│   │   |   |── large\n│   │   |   |   ├── test\n│   │   |   |   ├── train\n│   │   |   |   ├── valid\n│   │   |   |── tiled\n│   │   ├── AmericanSignLanguageLetters\n│   │   ├── Aquarium\n│   │   ├── BCCD\n│   │   ├── ...\n```\n\n在评测 ODinW3535 时候由于需要自定义 prompt，因此需要提前对标注的 json 文件进行处理，你可以使用 [override_category.py](./odinw/override_category.py) 脚本进行处理，处理后会生成新的标注文件，不会覆盖原先的标注文件。\n\n```shell\npython configs/mm_grounding_dino/odinw/override_category.py data/odinw/\n```\n\n### 4 DOD\n\nDOD 来自 [Described Object Detection: Liberating Object Detection with Flexible Expressions](https://arxiv.org/abs/2307.12813)。其数据集可以从 [这里](https://github.com/shikras/d-cube?tab=readme-ov-file#download)下载，最终的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── d3\n│   │   ├── d3_images\n│   │   ├── d3_json\n│   │   ├── d3_pkl\n```\n\n### 5 Flickr30k Entities\n\n在前面 GoldG 数据准备章节中我们已经下载了 Flickr30k 训练所需文件，评估所需的文件是 2 个 json 文件，你可以从 [这里](https://huggingface.co/GLIPModel/GLIP/blob/main/mdetr_annotations/final_flickr_separateGT_val.json) 和 [这里](https://huggingface.co/GLIPModel/GLIP/blob/main/mdetr_annotations/final_flickr_separateGT_test.json)下载，最终的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── flickr30k_entities\n│   │   ├── final_flickr_separateGT_train.json\n│   │   ├── final_flickr_separateGT_val.json\n│   │   ├── final_flickr_separateGT_test.json\n│   │   ├── final_flickr_separateGT_train_vg.json\n│   │   ├── flickr30k_images\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n```\n\n### 6 Referring Expression Comprehension\n\n指代性表达式理解包括 4 个数据集： RefCOCO, RefCOCO+, RefCOCOg, gRefCOCO。这 4 个数据集所采用的图片都来自于 COCO2014 train，和 COCO2017 类似，你可以从 COCO 官方或者 opendatalab 中下载，而标注可以直接从 [这里](https://huggingface.co/GLIPModel/GLIP/tree/main/mdetr_annotations) 下载，mdetr_annotations 文件夹里面包括了其他大量的标注，你如果觉得数量过多，可以只下载所需要的几个 json 文件即可。最终的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcocog_test.json\n```\n\n注意 gRefCOCO 是在 [GREC: Generalized Referring Expression Comprehension](https://arxiv.org/abs/2308.16182) 被提出，并不在 `mdetr_annotations` 文件夹中，需要自行处理。具体步骤为：\n\n1. 下载 [gRefCOCO](https://github.com/henghuiding/gRefCOCO?tab=readme-ov-file#grefcoco-dataset-download)，并解压到 data/coco/ 文件夹中\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   ├── grefs\n│   │   │   ├── grefs(unc).json\n│   │   │   ├── instances.json\n```\n\n2. 转换为 coco 格式\n\n你可以使用 gRefCOCO 官方提供的[转换脚本](https://github.com/henghuiding/gRefCOCO/blob/b4b1e55b4d3a41df26d6b7d843ea011d581127d4/mdetr/scripts/fine-tuning/grefexp_coco_format.py)。注意需要将被注释的 161 行打开，并注释 160 行才可以得到全量的 json 文件。\n\n```shell\n# 需要克隆官方 repo\ngit clone https://github.com/henghuiding/gRefCOCO.git\ncd gRefCOCO/mdetr\npython scripts/fine-tuning/grefexp_coco_format.py --data_path ../../data/coco/grefs --out_path ../../data/coco/mdetr_annotations/ --coco_path ../../data/coco\n```\n\n会在 `data/coco/mdetr_annotations/` 文件夹中生成 4 个 json 文件，完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_grefcoco_train.json\n│   │   │   ├── finetune_grefcoco_val.json\n│   │   │   ├── finetune_grefcoco_testA.json\n│   │   │   ├── finetune_grefcoco_testB.json\n```\n\n## 微调数据集准备\n\n### 1 COCO 2017\n\nCOCO 是检测领域最常用的数据集，我们希望能够更充分探索其微调模式。从目前发展来看，一共有 3 种微调方式：\n\n1. 闭集微调，即微调后文本端将无法修改描述，转变为闭集算法，在 COCO 上性能能够最大化，但是失去了通用性。\n2. 开集继续预训练微调，即对 COCO 数据集采用和预训练一致的预训练手段。此时有两种做法，第一种是降低学习率并固定某些模块，仅仅在 COCO 数据上预训练，第二种是将 COCO 数据和部分预训练数据混合一起训练，两种方式的目的都是在尽可能不降低泛化性时提高 COCO 数据集性能\n3. 开放词汇微调，即采用 OVD 领域常用做法，将 COCO 类别分成 base 类和 novel 类，训练时候仅仅在 base 类上进行，评测在 base 和 novel 类上进行。这种方式可以验证 COCO OVD 能力，目的也是在尽可能不降低泛化性时提高 COCO 数据集性能\n\n**(1) 闭集微调**\n\n这个部分无需准备数据，直接用之前的数据即可。\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n**(2) 开集继续预训练微调**\n这种方式需要将 COCO 训练数据转换为 ODVG 格式，你可以使用如下命令转换：\n\n```shell\npython tools/dataset_converters/coco2odvg.py data/coco/annotations/instances_train2017.json -d coco\n```\n\n会在 `data/coco/annotations/` 下生成新的 `instances_train2017_od.json` 和 `coco2017_label_map.json`，完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_train2017_od.json\n│   │   │   ├── coco2017_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n在得到数据后，你可以自行选择单独预习还是混合预训练方式。\n\n**(3) 开放词汇微调**\n这种方式需要将 COCO 训练数据转换为 OVD 格式，你可以使用如下命令转换：\n\n```shell\npython tools/dataset_converters/coco2ovd.py data/coco/\n```\n\n会在 `data/coco/annotations/` 下生成新的 `instances_val2017_all_2.json` 和 `instances_val2017_seen_2.json`，完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_train2017_od.json\n│   │   │   ├── instances_val2017_all_2.json\n│   │   │   ├── instances_val2017_seen_2.json\n│   │   │   ├── coco2017_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n然后可以直接使用 [配置](coco/grounding_dino_swin-t_finetune_16xb4_1x_coco_48_17.py) 进行训练和测试。\n\n### 2 LVIS 1.0\n\nLVIS 是一个包括 1203 类的数据集，同时也是一个长尾联邦数据集，对其进行微调很有意义。 由于其类别过多，我们无法对其进行闭集微调，因此只能采用开集继续预训练微调和开放词汇微调。\n\n你需要先准备好 LVIS 训练 JSON 文件，你可以从 [这里](https://www.lvisdataset.org/dataset) 下载，我们只需要 `lvis_v1_train.json` 和 `lvis_v1_val.json`，然后将其放到 `data/coco/annotations/` 下，然后运行如下命令：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n(1) 开集继续预训练微调\n\n使用如下命令转换为 ODVG 格式：\n\n```shell\npython tools/dataset_converters/lvis2odvg.py data/coco/annotations/lvis_v1_train.json\n```\n\n会在 `data/coco/annotations/` 下生成新的 `lvis_v1_train_od.json` 和 `lvis_v1_label_map.json`，完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── lvis_v1_train_od.json\n│   │   │   ├── lvis_v1_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n然后可以直接使用 [配置](lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis.py) 进行训练测试，或者你修改配置将其和部分预训练数据集混合使用。\n\n**(2) 开放词汇微调**\n\n使用如下命令转换为 OVD 格式：\n\n```shell\npython tools/dataset_converters/lvis2ovd.py data/coco/\n```\n\n会在 `data/coco/annotations/` 下生成新的 `lvis_v1_train_od_norare.json` 和 `lvis_v1_label_map_norare.json`，完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── lvis_v1_train.json\n│   │   │   ├── lvis_v1_val.json\n│   │   │   ├── lvis_v1_train_od.json\n│   │   │   ├── lvis_v1_label_map.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── lvis_v1_minival_inserted_image_name.json\n│   │   │   ├── lvis_od_val.json\n│   │   │   ├── lvis_v1_train_od_norare.json\n│   │   │   ├── lvis_v1_label_map_norare.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n```\n\n然后可以直接使用 [配置](lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis_866_337.py) 进行训练测试\n\n### 3 RTTS\n\nRTTS 是一个浓雾天气数据集，该数据集包含 4,322 张雾天图像，包含五个类：自行车 (bicycle)、公共汽车 (bus)、汽车 (car)、摩托车 (motorbike) 和人 (person)。可以从 [这里](https://drive.google.com/file/d/15Ei1cHGVqR1mXFep43BO7nkHq1IEGh1e/view)下载, 然后解压到 `data/RTTS/` 文件夹中。完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── RTTS\n│   │   ├── annotations_json\n│   │   ├── annotations_xml\n│   │   ├── ImageSets\n│   │   ├── JPEGImages\n```\n\n### 4 RUOD\n\nRUOD 是一个水下目标检测数据集，你可以从 [这里](https://drive.google.com/file/d/1hxtbdgfVveUm_DJk5QXkNLokSCTa_E5o/view)下载, 然后解压到 `data/RUOD/` 文件夹中。完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── RUOD\n│   │   ├── Environment_pic\n│   │   ├── Environmet_ANN\n│   │   ├── RUOD_ANN\n│   │   ├── RUOD_pic\n```\n\n### 5 Brain Tumor\n\nBrain Tumor 是一个医学领域的 2d 检测数据集，你可以从 [这里](https://universe.roboflow.com/roboflow-100/brain-tumor-m2pbp/dataset/2)下载, 请注意选择 `COCO JSON` 格式。然后解压到 `data/brain_tumor_v2/` 文件夹中。完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── brain_tumor_v2\n│   │   ├── test\n│   │   ├── train\n│   │   ├── valid\n```\n\n### 6 Cityscapes\n\nCityscapes 是一个城市街景数据集，你可以从 [这里](https://www.cityscapes-dataset.com/) 或者 opendatalab 中下载, 然后解压到 `data/cityscapes/` 文件夹中。完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n```\n\n在下载后，然后使用 [cityscapes.py](../../tools/dataset_converters/cityscapes.py) 脚本生成我们所需要的 json 格式\n\n```shell\npython tools/dataset_converters/cityscapes.py data/cityscapes/\n```\n\n会在 annotations 中生成 3 个新的 json 文件。完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   │   ├── instancesonly_filtered_gtFine_train.json\n│   │   │   ├── instancesonly_filtered_gtFine_val.json\n│   │   │   ├── instancesonly_filtered_gtFine_test.json\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n```\n\n### 7 People in Painting\n\nPeople in Painting 是一个油画数据集，你可以从 [这里](https://universe.roboflow.com/roboflow-100/people-in-paintings/dataset/2), 请注意选择 `COCO JSON` 格式。然后解压到 `data/people_in_painting_v2/` 文件夹中。完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── people_in_painting_v2\n│   │   ├── test\n│   │   ├── train\n│   │   ├── valid\n```\n\n### 8 Referring Expression Comprehension\n\n指代性表达式理解的微调和前面一样，也是包括 4 个数据集，在评测数据准备阶段已经全部整理好了，完整的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcocog_test.json\n```\n\n然后我们需要将其转换为所需的 ODVG 格式，请使用 [refcoco2odvg.py](../../tools/dataset_converters/refcoco2odvg.py) 脚本转换，\n\n```shell\npython tools/dataset_converters/refcoco2odvg.py data/coco/mdetr_annotations\n```\n\n会在 `data/coco/mdetr_annotations` 中生成新的 4 个 json 文件。 转换后的数据集结构如下：\n\n```text\nmmdetection\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   │   ├── instances_train2014.json\n│   │   ├── train2017\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── val2017\n│   │   │   ├── xxxx.jpg\n│   │   │   ├── ...\n│   │   ├── train2014\n│   │   │   ├── xxx.jpg\n│   │   │   ├── ...\n│   │   ├── mdetr_annotations\n│   │   │   ├── final_refexp_val.json\n│   │   │   ├── finetune_refcoco_testA.json\n│   │   │   ├── finetune_refcoco_testB.json\n│   │   │   ├── finetune_refcoco+_testA.json\n│   │   │   ├── finetune_refcoco+_testB.json\n│   │   │   ├── finetune_refcocog_test.json\n│   │   │   ├── finetune_refcoco_train_vg.json\n│   │   │   ├── finetune_refcoco+_train_vg.json\n│   │   │   ├── finetune_refcocog_train_vg.json\n│   │   │   ├── finetune_grefcoco_train_vg.json\n```\n"
  },
  {
    "path": "configs/mm_grounding_dino/dod/grounding_dino_swin-t_pretrain_zeroshot_concat_dod.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/d3/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities', 'sent_ids'))\n]\n\n# -------------------------------------------------#\nval_dataset_full = dict(\n    type='DODDataset',\n    data_root=data_root,\n    ann_file='d3_json/d3_full_annotations.json',\n    data_prefix=dict(img='d3_images/', anno='d3_pkl'),\n    pipeline=test_pipeline,\n    test_mode=True,\n    backend_args=None,\n    return_classes=True)\n\nval_evaluator_full = dict(\n    type='DODCocoMetric',\n    ann_file=data_root + 'd3_json/d3_full_annotations.json')\n\n# -------------------------------------------------#\nval_dataset_pres = dict(\n    type='DODDataset',\n    data_root=data_root,\n    ann_file='d3_json/d3_pres_annotations.json',\n    data_prefix=dict(img='d3_images/', anno='d3_pkl'),\n    pipeline=test_pipeline,\n    test_mode=True,\n    backend_args=None,\n    return_classes=True)\nval_evaluator_pres = dict(\n    type='DODCocoMetric',\n    ann_file=data_root + 'd3_json/d3_pres_annotations.json')\n\n# -------------------------------------------------#\nval_dataset_abs = dict(\n    type='DODDataset',\n    data_root=data_root,\n    ann_file='d3_json/d3_abs_annotations.json',\n    data_prefix=dict(img='d3_images/', anno='d3_pkl'),\n    pipeline=test_pipeline,\n    test_mode=True,\n    backend_args=None,\n    return_classes=True)\nval_evaluator_abs = dict(\n    type='DODCocoMetric',\n    ann_file=data_root + 'd3_json/d3_abs_annotations.json')\n\n# -------------------------------------------------#\ndatasets = [val_dataset_full, val_dataset_pres, val_dataset_abs]\ndataset_prefixes = ['FULL', 'PRES', 'ABS']\nmetrics = [val_evaluator_full, val_evaluator_pres, val_evaluator_abs]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/dod/grounding_dino_swin-t_pretrain_zeroshot_parallel_dod.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_zeroshot_concat_dod.py'\n\nmodel = dict(test_cfg=dict(chunked_size=1))\n"
  },
  {
    "path": "configs/mm_grounding_dino/flickr30k/grounding_dino_swin-t-pretrain_flickr30k.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndataset_type = 'Flickr30kDataset'\ndata_root = 'data/flickr30k_entities/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive', 'phrase_ids', 'phrases'))\n]\n\ndataset_Flickr30k_val = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='final_flickr_separateGT_val.json',\n    data_prefix=dict(img='flickr30k_images/'),\n    pipeline=test_pipeline,\n)\n\ndataset_Flickr30k_test = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='final_flickr_separateGT_test.json',\n    data_prefix=dict(img='flickr30k_images/'),\n    pipeline=test_pipeline,\n)\n\nval_evaluator_Flickr30k = dict(type='Flickr30kMetric')\n\ntest_evaluator_Flickr30k = dict(type='Flickr30kMetric')\n\n# ----------Config---------- #\ndataset_prefixes = ['Flickr30kVal', 'Flickr30kTest']\ndatasets = [dataset_Flickr30k_val, dataset_Flickr30k_test]\nmetrics = [val_evaluator_Flickr30k, test_evaluator_Flickr30k]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-b_pretrain_all.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-b_pretrain_obj365_goldg_v3det/grounding_dino_swin-b_pretrain_obj365_goldg_v3de-f83eef00.pth'  # noqa\n\nmodel = dict(\n    use_autocast=True,\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=True,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=None),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n\no365v1_od_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v1/',\n    ann_file='o365v1_train_odvg.json',\n    label_map_file='o365v1_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None,\n)\n\nflickr30k_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/flickr30k_entities/',\n    ann_file='final_flickr_separateGT_train_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='flickr30k_images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\nv3d_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/V3Det/annotations/v3det_2023_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\nv3det_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/V3Det/',\n    ann_file='annotations/v3det_2023_v1_train_od.json',\n    label_map_file='annotations/v3det_2023_v1_label_map.json',\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    need_text=False,  # change this\n    pipeline=v3d_train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngrit_dataset = dict(\n    type='ODVGDataset',\n    data_root='grit_processed/',\n    ann_file='grit20m_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\n# --------------------------- lvis od dataset---------------------------\nlvis_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/coco/annotations/lvis_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\nlvis_dataset = dict(\n    type='ClassBalancedDataset',\n    oversample_thr=1e-3,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='annotations/lvis_v1_train_od.json',\n        label_map_file='annotations/lvis_v1_label_map.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=False),\n        need_text=False,  # change this\n        pipeline=lvis_train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- coco2017 od dataset---------------------------\ncoco2017_train_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='annotations/instance_train2017_norefval_od.json',\n        label_map_file='annotations/coco2017_label_map.json',\n        data_prefix=dict(img='train2017'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- coco2014 vg dataset---------------------------\ncoco2014_vg_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/coco/',\n    ann_file='mdetr_annotations/final_mixed_train_only_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='train2014/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\n# --------------------------- refcoco vg dataset---------------------------\nrefcoco_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_refcoco_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- refcoco+ vg dataset---------------------------\nrefcoco_plus_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_refcoco+_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- refcocog vg dataset---------------------------\nrefcocog_dataset = dict(\n    type='RepeatDataset',\n    times=3,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_refcocog_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- grefcoco vg dataset---------------------------\ngrefcoco_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_grefcoco_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- dataloader---------------------------\ntrain_dataloader = dict(\n    batch_size=4,\n    num_workers=4,\n    sampler=dict(\n        _delete_=True,\n        type='CustomSampleSizeSampler',\n        ratio_mode=True,\n        dataset_size=[-1, -1, 0.07, -1, -1, -1, -1, -1, -1, -1, -1, -1]),\n    dataset=dict(datasets=[\n        o365v1_od_dataset,  # 1.74M\n        v3det_dataset,  #\n        grit_dataset,\n        lvis_dataset,\n        coco2017_train_dataset,  # 0.12M\n        flickr30k_dataset,  # 0.15M\n        gqa_dataset,  # 0.62M\n        coco2014_vg_dataset,  # 0.49M\n        refcoco_dataset,  # 0.12M\n        refcoco_plus_dataset,  # 0.12M\n        refcocog_dataset,  # 0.08M\n        grefcoco_dataset,  # 0.19M\n    ]))\n\noptim_wrapper = dict(optimizer=dict(lr=0.0001))\n\n# learning policy\nmax_iter = 304680\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=10000)\n\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[228510],\n        gamma=0.1)\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=False, interval=10000, max_keep_ckpts=20))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-b_pretrain_obj365_goldg_v3det.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window12_384_22k.pth'  # noqa\nmodel = dict(\n    use_autocast=True,\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=True,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n\no365v1_od_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v1/',\n    ann_file='o365v1_train_odvg.json',\n    label_map_file='o365v1_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None,\n)\n\nflickr30k_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/flickr30k_entities/',\n    ann_file='final_flickr_separateGT_train_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='flickr30k_images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\nv3d_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/V3Det/annotations/v3det_2023_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\nv3det_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/V3Det/',\n    ann_file='annotations/v3det_2023_v1_train_od.json',\n    label_map_file='annotations/v3det_2023_v1_label_map.json',\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    need_text=False,  # change this\n    pipeline=v3d_train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ntrain_dataloader = dict(\n    dataset=dict(datasets=[\n        o365v1_od_dataset, flickr30k_dataset, gqa_dataset, v3det_dataset\n    ]))\n\n# learning policy\nmax_epochs = 18\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[13, 16],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-l_pretrain_all.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-l_pretrain_obj365_goldg/grounding_dino_swin-l_pretrain_obj365_goldg-34dcdc53.pth'  # noqa\n\nnum_levels = 5\nmodel = dict(\n    use_autocast=True,\n    num_feature_levels=num_levels,\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=True,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=None),\n    neck=dict(in_channels=[192, 384, 768, 1536], num_outs=num_levels),\n    encoder=dict(layer_cfg=dict(self_attn_cfg=dict(num_levels=num_levels))),\n    decoder=dict(layer_cfg=dict(cross_attn_cfg=dict(num_levels=num_levels))))\n\n# --------------------------- object365v2 od dataset---------------------------\n# objv2_backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/objects365v2/': 'yudong:s3://wangyudong/obj365_v2/',\n#         'data/objects365v2/': 'yudong:s3://wangyudong/obj365_v2/'\n#     }))\nobjv2_backend_args = None\n\nobjv2_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=objv2_backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/objects365v2/annotations/o365v2_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\no365v2_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v2/',\n    ann_file='annotations/zhiyuan_objv2_train_od.json',\n    label_map_file='annotations/o365v2_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=objv2_train_pipeline,\n    return_classes=True,\n    need_text=False,\n    backend_args=None,\n)\n\n# --------------------------- openimagev6 od dataset---------------------------\n# oi_backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\noi_backend_args = None\n\noi_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=oi_backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/OpenImages/annotations/openimages_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\noiv6_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/OpenImages/',\n    ann_file='annotations/oidv6-train-annotations_od.json',\n    label_map_file='annotations/openimages_label_map.json',\n    data_prefix=dict(img='OpenImages/train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    need_text=False,\n    pipeline=oi_train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\n# --------------------------- v3det od dataset---------------------------\nv3d_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/V3Det/annotations/v3det_2023_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\nv3det_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/V3Det/',\n        ann_file='annotations/v3det_2023_v1_train_od.json',\n        label_map_file='annotations/v3det_2023_v1_label_map.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=False),\n        need_text=False,\n        pipeline=v3d_train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- lvis od dataset---------------------------\nlvis_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/coco/annotations/lvis_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\nlvis_dataset = dict(\n    type='ClassBalancedDataset',\n    oversample_thr=1e-3,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='annotations/lvis_v1_train_od.json',\n        label_map_file='annotations/lvis_v1_label_map.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=False),\n        need_text=False,  # change this\n        pipeline=lvis_train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- coco2017 od dataset---------------------------\ncoco2017_train_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='annotations/instance_train2017_norefval_od.json',\n        label_map_file='annotations/coco2017_label_map.json',\n        data_prefix=dict(img='train2017'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- flickr30k vg dataset---------------------------\nflickr30k_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/flickr30k_entities/',\n        ann_file='final_flickr_separateGT_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='flickr30k_images/'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- gqa vg dataset---------------------------\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\n# --------------------------- coco2014 vg dataset---------------------------\ncoco2014_vg_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/coco/',\n    ann_file='mdetr_annotations/final_mixed_train_only_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='train2014/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\n# --------------------------- refcoco vg dataset---------------------------\nrefcoco_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_refcoco_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- refcoco+ vg dataset---------------------------\nrefcoco_plus_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_refcoco+_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- refcocog vg dataset---------------------------\nrefcocog_dataset = dict(\n    type='RepeatDataset',\n    times=3,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_refcocog_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- grefcoco vg dataset---------------------------\ngrefcoco_dataset = dict(\n    type='RepeatDataset',\n    times=2,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root='data/coco/',\n        ann_file='mdetr_annotations/finetune_grefcoco_train_vg.json',\n        label_map_file=None,\n        data_prefix=dict(img='train2014'),\n        filter_cfg=dict(filter_empty_gt=False),\n        pipeline=_base_.train_pipeline,\n        return_classes=True,\n        backend_args=None))\n\n# --------------------------- grit vg dataset---------------------------\n# grit_backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/grit/': 'yichen:s3://chenyicheng/grit/',\n#         'data/grit/': 'yichen:s3://chenyicheng/grit/'\n#     }))\ngrit_backend_args = None\n\ngrit_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=grit_backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ngrit_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/grit/',\n    ann_file='grit20m_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=grit_train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\n# --------------------------- dataloader---------------------------\ntrain_dataloader = dict(\n    batch_size=4,\n    num_workers=4,\n    sampler=dict(\n        _delete_=True,\n        type='CustomSampleSizeSampler',\n        ratio_mode=True,\n        # OD ~ 1.74+1.67*0.5+0.18*2+0.12*2+0.1=3.2\n        # vg ~ 0.15*2+0.62*1+0.49*1+0.12*2+0.12*2+0.08*3+0.19*2+9*0.09=3.3\n        dataset_size=[-1, 0.5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0.09]),\n    dataset=dict(datasets=[\n        o365v2_dataset,  # 1.74M\n        oiv6_dataset,  # 1.67M\n        v3det_dataset,  # 0.18M\n        coco2017_train_dataset,  # 0.12M\n        lvis_dataset,  # 0.1M\n        flickr30k_dataset,  # 0.15M\n        gqa_dataset,  # 0.62M\n        coco2014_vg_dataset,  # 0.49M\n        refcoco_dataset,  # 0.12M\n        refcoco_plus_dataset,  # 0.12M\n        refcocog_dataset,  # 0.08M\n        grefcoco_dataset,  # 0.19M\n        grit_dataset  # 9M\n    ]))\n\n# 4NODES * 8GPU\noptim_wrapper = dict(optimizer=dict(lr=0.0001))\n\nmax_iter = 250000\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=13000)\n\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[210000],\n        gamma=0.1)\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=False, interval=13000, max_keep_ckpts=30))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-l_pretrain_obj365_goldg.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\nnum_levels = 5\nmodel = dict(\n    use_autocast=True,\n    num_feature_levels=num_levels,\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=True,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[192, 384, 768, 1536], num_outs=num_levels),\n    encoder=dict(layer_cfg=dict(self_attn_cfg=dict(num_levels=num_levels))),\n    decoder=dict(layer_cfg=dict(cross_attn_cfg=dict(num_levels=num_levels))))\n\n# --------------------------- object365v2 od dataset---------------------------\n# objv2_backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/objects365v2/': 'yudong:s3://wangyudong/obj365_v2/',\n#         'data/objects365v2/': 'yudong:s3://wangyudong/obj365_v2/'\n#     }))\nobjv2_backend_args = None\n\nobjv2_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=objv2_backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/objects365v2/annotations/o365v2_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\no365v2_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v2/',\n    ann_file='annotations/zhiyuan_objv2_train_od.json',\n    label_map_file='annotations/o365v2_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=objv2_train_pipeline,\n    return_classes=True,\n    need_text=False,\n    backend_args=None,\n)\n\n# --------------------------- openimagev6 od dataset---------------------------\n# oi_backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\noi_backend_args = None\n\noi_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=oi_backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/OpenImages/annotations/openimages_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\noiv6_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/OpenImages/',\n    ann_file='annotations/oidv6-train-annotations_od.json',\n    label_map_file='annotations/openimages_label_map.json',\n    data_prefix=dict(img='OpenImages/train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    need_text=False,\n    pipeline=oi_train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\nflickr30k_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/flickr30k_entities/',\n    ann_file='final_flickr_separateGT_train_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='flickr30k_images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ntrain_dataloader = dict(\n    dataset=dict(datasets=[\n        o365v2_dataset, oiv6_dataset, flickr30k_dataset, gqa_dataset\n    ]))\n\n# 4Nodex8GPU\noptim_wrapper = dict(optimizer=dict(lr=0.0002))\n\nmax_iter = 200000\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=13000)\n\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[156100],\n        gamma=0.5)\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=False, interval=13000, max_keep_ckpts=30))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_finetune_8xb4_20e_cat.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/cat/'\nclass_name = ('cat', )\nnum_classes = len(class_name)\nmetainfo = dict(classes=class_name, palette=[(220, 20, 60)])\n\nmodel = dict(bbox_head=dict(num_classes=num_classes))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='CocoDataset',\n        data_root=data_root,\n        metainfo=metainfo,\n        return_classes=True,\n        pipeline=train_pipeline,\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        ann_file='annotations/trainval.json',\n        data_prefix=dict(img='images/')))\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=metainfo,\n        data_root=data_root,\n        ann_file='annotations/test.json',\n        data_prefix=dict(img='images/')))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(ann_file=data_root + 'annotations/test.json')\ntest_evaluator = val_evaluator\n\nmax_epoch = 20\n\ndefault_hooks = dict(\n    checkpoint=dict(interval=1, max_keep_ckpts=1, save_best='auto'),\n    logger=dict(type='LoggerHook', interval=5))\ntrain_cfg = dict(max_epochs=max_epoch, val_interval=1)\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epoch,\n        by_epoch=True,\n        milestones=[15],\n        gamma=0.1)\n]\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.0001),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.0),\n            'language_model': dict(lr_mult=0.0)\n        }))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth'  # noqa\nlang_model_name = 'bert-base-uncased'\n\nmodel = dict(\n    type='GroundingDINO',\n    num_queries=900,\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=False,\n    ),\n    language_model=dict(\n        type='BertModel',\n        name=lang_model_name,\n        max_tokens=256,\n        pad_to_max=False,\n        use_sub_sentence_represent=True,\n        special_tokens_list=['[CLS]', '[SEP]', '.', '?'],\n        add_pooling_layer=False,\n    ),\n    backbone=dict(\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=True,\n        convert_weights=True,\n        frozen_stages=-1,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[192, 384, 768],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        bias=True,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        num_cp=6,\n        # visual layer config\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        # text layer config\n        text_layer_cfg=dict(\n            self_attn_cfg=dict(num_heads=4, embed_dims=256, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.0)),\n        # fusion layer config\n        fusion_layer_cfg=dict(\n            v_dim=256,\n            l_dim=256,\n            embed_dim=1024,\n            num_heads=4,\n            init_values=1e-4),\n    ),\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            # query self attention layer\n            self_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to text\n            cross_attn_text_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            # cross attention layer query to image\n            cross_attn_cfg=dict(embed_dims=256, num_heads=8, dropout=0.0),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=2048, ffn_drop=0.0)),\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128, normalize=True, offset=0.0, temperature=20),\n    bbox_head=dict(\n        type='GroundingDINOHead',\n        num_classes=256,\n        sync_cls_avg_factor=True,\n        contrastive_cfg=dict(max_text_len=256, log_scale='auto', bias=True),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0)),\n    dn_cfg=dict(  # TODO: Move to model.train_cfg ?\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None,\n                       num_dn_queries=100)),  # TODO: half num_dn_queries\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='BinaryFocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=lang_model_name,\n        num_sample_negative=85,\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive'))\n]\n\ndataset_type = 'ODVGDataset'\ndata_root = 'data/objects365v1/'\n\ncoco_od_dataset = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='o365v1_train_odvg.json',\n    label_map_file='o365v1_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=4,\n    num_workers=4,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(type='ConcatDataset', datasets=[coco_od_dataset]))\n\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\ntest_dataloader = val_dataloader\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0004,\n                   weight_decay=0.0001),  # bs=16 0.0001\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            'language_model': dict(lr_mult=0.1),\n        }))\n\n# learning policy\nmax_epochs = 30\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[19, 26],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (16 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n\ndefault_hooks = dict(visualization=dict(type='GroundingVisualizationHook'))\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\no365v1_od_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v1/',\n    ann_file='o365v1_train_odvg.json',\n    label_map_file='o365v1_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None,\n)\n\nflickr30k_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/flickr30k_entities/',\n    ann_file='final_flickr_separateGT_train_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='flickr30k_images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ntrain_dataloader = dict(\n    dataset=dict(datasets=[o365v1_od_dataset, flickr30k_dataset, gqa_dataset]))\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\no365v1_od_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v1/',\n    ann_file='o365v1_train_odvg.json',\n    label_map_file='o365v1_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None,\n)\n\nflickr30k_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/flickr30k_entities/',\n    ann_file='final_flickr_separateGT_train_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='flickr30k_images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngrit_dataset = dict(\n    type='ODVGDataset',\n    data_root='grit_processed/',\n    ann_file='grit20m_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ntrain_dataloader = dict(\n    sampler=dict(\n        _delete_=True,\n        type='CustomSampleSizeSampler',\n        dataset_size=[-1, -1, -1, 500000]),\n    dataset=dict(datasets=[\n        o365v1_od_dataset, flickr30k_dataset, gqa_dataset, grit_dataset\n    ]))\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\no365v1_od_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v1/',\n    ann_file='o365v1_train_odvg.json',\n    label_map_file='o365v1_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None,\n)\n\nflickr30k_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/flickr30k_entities/',\n    ann_file='final_flickr_separateGT_train_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='flickr30k_images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\nv3d_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/V3Det/annotations/v3det_2023_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\nv3det_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/V3Det/',\n    ann_file='annotations/v3det_2023_v1_train_od.json',\n    label_map_file='annotations/v3det_2023_v1_label_map.json',\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    need_text=False,  # change this\n    pipeline=v3d_train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngrit_dataset = dict(\n    type='ODVGDataset',\n    data_root='grit_processed/',\n    ann_file='grit20m_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ntrain_dataloader = dict(\n    sampler=dict(\n        _delete_=True,\n        type='CustomSampleSizeSampler',\n        dataset_size=[-1, -1, -1, -1, 500000]),\n    dataset=dict(datasets=[\n        o365v1_od_dataset, flickr30k_dataset, gqa_dataset, v3det_dataset,\n        grit_dataset\n    ]))\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_v3det.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\no365v1_od_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/objects365v1/',\n    ann_file='o365v1_train_odvg.json',\n    label_map_file='o365v1_label_map.json',\n    data_prefix=dict(img='train/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None,\n)\n\nflickr30k_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/flickr30k_entities/',\n    ann_file='final_flickr_separateGT_train_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='flickr30k_images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ngqa_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/gqa/',\n    ann_file='final_mixed_train_no_coco_vg.json',\n    label_map_file=None,\n    data_prefix=dict(img='images/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=_base_.train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\nv3d_train_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/V3Det/annotations/v3det_2023_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\nv3det_dataset = dict(\n    type='ODVGDataset',\n    data_root='data/V3Det/',\n    ann_file='annotations/v3det_2023_v1_train_od.json',\n    label_map_file='annotations/v3det_2023_v1_label_map.json',\n    data_prefix=dict(img=''),\n    filter_cfg=dict(filter_empty_gt=False),\n    need_text=False,  # change this\n    pipeline=v3d_train_pipeline,\n    return_classes=True,\n    backend_args=None)\n\ntrain_dataloader = dict(\n    dataset=dict(datasets=[\n        o365v1_od_dataset, flickr30k_dataset, gqa_dataset, v3det_dataset\n    ]))\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_pseudo-labeling_cat.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadTextAnnotations'),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive'))\n]\n\ndata_root = 'data/cat/'\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=False,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root=data_root,\n        label_map_file='cat_label_map.json',\n        ann_file='cat_train_od.json',\n        data_prefix=dict(img='images/'),\n        pipeline=test_pipeline,\n        return_classes=True))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    outfile_path=data_root + 'cat_train_od_v1.json',\n    img_prefix=data_root + 'images/',\n    score_thr=0.7,\n    nms_thr=0.5,\n    type='DumpODVGResults')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_pseudo-labeling_flickr30k.py",
    "content": "_base_ = 'grounding_dino_swin-t_pretrain_obj365.py'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadTextAnnotations'),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive'))\n]\n\ndata_root = 'data/flickr30k_entities/'\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=False,\n    dataset=dict(\n        type='ODVGDataset',\n        data_root=data_root,\n        ann_file='flickr_simple_train_vg.json',\n        data_prefix=dict(img='flickr30k_images/'),\n        pipeline=test_pipeline,\n        return_classes=True))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    outfile_path=data_root + 'flickr_simple_train_vg_v1.json',\n    img_prefix=data_root + 'flickr30k_images/',\n    score_thr=0.4,\n    nms_thr=0.5,\n    type='DumpODVGResults')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/coco/annotations/lvis_v1_label_map.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(\n            type='ODVGDataset',\n            data_root=data_root,\n            need_text=False,\n            label_map_file='annotations/lvis_v1_label_map.json',\n            ann_file='annotations/lvis_v1_train_od.json',\n            data_prefix=dict(img=''),\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            return_classes=True,\n            pipeline=train_pipeline)))\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type='LVISV1Dataset',\n        ann_file='annotations/lvis_v1_minival_inserted_image_name.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root +\n    'annotations/lvis_v1_minival_inserted_image_name.json')\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            # 'language_model': dict(lr_mult=0),\n        }))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=3)\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        max_keep_ckpts=1, save_best='lvis_fixed_ap/AP', rule='greater'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/lvis/grounding_dino_swin-t_finetune_16xb4_1x_lvis_866_337.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        # change this\n        label_map_file='data/coco/annotations/lvis_v1_label_map_norare.json',\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(\n            type='ODVGDataset',\n            data_root=data_root,\n            need_text=False,\n            label_map_file='annotations/lvis_v1_label_map_norare.json',\n            ann_file='annotations/lvis_v1_train_od_norare.json',\n            data_prefix=dict(img=''),\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            return_classes=True,\n            pipeline=train_pipeline)))\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type='LVISV1Dataset',\n        ann_file='annotations/lvis_v1_minival_inserted_image_name.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root +\n    'annotations/lvis_v1_minival_inserted_image_name.json')\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.00005, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            # 'language_model': dict(lr_mult=0),\n        }))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=3)\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        max_keep_ckpts=3, save_best='lvis_fixed_ap/AP', rule='greater'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/lvis/grounding_dino_swin-t_pretrain_zeroshot_lvis.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/coco/'\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type=dataset_type,\n        ann_file='annotations/lvis_od_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\n# numpy < 1.24.0\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root + 'annotations/lvis_od_val.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/lvis/grounding_dino_swin-t_pretrain_zeroshot_mini-lvis.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\nmodel = dict(test_cfg=dict(\n    max_per_img=300,\n    chunked_size=40,\n))\n\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/coco/'\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        type=dataset_type,\n        ann_file='annotations/lvis_v1_minival_inserted_image_name.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\n# numpy < 1.24.0\nval_evaluator = dict(\n    _delete_=True,\n    type='LVISFixedAPMetric',\n    ann_file=data_root +\n    'annotations/lvis_v1_minival_inserted_image_name.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/metafile.yml",
    "content": "Collections:\n  - Name: MM Grounding DINO\n    Metadata:\n      Training Data: Objects365, GoldG, GRIT and V3Det\n      Training Techniques:\n        - AdamW\n        - Multi Scale Train\n        - Gradient Clip\n      Training Resources: 3090 GPUs\n      Architecture:\n        - Swin Transformer\n        - BERT\n    README: configs/mm_grounding_dino/README.md\n    Code:\n      URL:\n      Version: v3.0.0\n\nModels:\n  - Name: grounding_dino_swin-t_pretrain_obj365_goldg\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg/grounding_dino_swin-t_pretrain_obj365_goldg_20231122_132602-4ea751ce.pth\n  - Name: grounding_dino_swin-t_pretrain_obj365_goldg_grit9m\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.5\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_20231128_200818-169cc352.pth\n  - Name: grounding_dino_swin-t_pretrain_obj365_goldg_v3det\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_v3det.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.6\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_v3det_20231218_095741-e316e297.pth\n  - Name: grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n  - Name: grounding_dino_swin-b_pretrain_obj365_goldg_v3det\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-b_pretrain_obj365_goldg_v3det.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 52.5\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-b_pretrain_obj365_goldg_v3det/grounding_dino_swin-b_pretrain_obj365_goldg_v3de-f83eef00.pth\n  - Name: grounding_dino_swin-b_pretrain_all\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-b_pretrain_all.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 59.5\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-b_pretrain_all/grounding_dino_swin-b_pretrain_all-f9818a7c.pth\n  - Name: grounding_dino_swin-l_pretrain_obj365_goldg\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-l_pretrain_obj365_goldg.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 53.0\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-l_pretrain_obj365_goldg/grounding_dino_swin-l_pretrain_obj365_goldg-34dcdc53.pth\n  - Name: grounding_dino_swin-l_pretrain_all\n    In Collection: MM Grounding DINO\n    Config: configs/mm_grounding_dino/grounding_dino_swin-l_pretrain_all.py\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 60.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-l_pretrain_all/grounding_dino_swin-l_pretrain_all-56d69e78.pth\n"
  },
  {
    "path": "configs/mm_grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw13.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'  # noqa\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    test_mode=True,\n    pipeline=base_test_pipeline,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\n\ncaption_prompt = None\n# caption_prompt = {\n#     'penguin': {\n#         'suffix': ', which is black and white'\n#     },\n#     'puffin': {\n#         'suffix': ' with orange beaks'\n#     },\n#     'stingray': {\n#         'suffix': ' which is flat and round'\n#     },\n# }\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 CottontailRabbits---------------------#\nclass_name = ('Cottontail-Rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\n\n# caption_prompt = None\ncaption_prompt = {'Cottontail-Rabbit': {'name': 'rabbit'}}\n\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 EgoHands---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\n\n# caption_prompt = None\ncaption_prompt = {'hand': {'suffix': ' of a person'}}\n\ndataset_EgoHands = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 NorthAmericaMushrooms---------------------#\nclass_name = ('CoW', 'chanterelle')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\n\n# caption_prompt = None\ncaption_prompt = {\n    'CoW': {\n        'name': 'flat mushroom'\n    },\n    'chanterelle': {\n        'name': 'yellow mushroom'\n    }\n}\n\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\n\n# caption_prompt = None\ncaption_prompt = {\n    'package': {\n        'prefix': 'there is a ',\n        'suffix': ' on the porch'\n    }\n}\n\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\n\n# caption_prompt = None\ncaption_prompt = {\n    'pothole': {\n        'prefix': 'there are some ',\n        'name': 'holes',\n        'suffix': ' on the road'\n    }\n}\n\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\ndataset_prefixes = [\n    'AerialMaritimeDrone', 'Aquarium', 'CottontailRabbits', 'EgoHands',\n    'NorthAmericaMushrooms', 'Packages', 'PascalVOC', 'pistols', 'pothole',\n    'Raccoon', 'ShellfishOpenImages', 'thermalDogsAndPeople',\n    'VehiclesOpenImages'\n]\ndatasets = [\n    dataset_AerialMaritimeDrone, dataset_Aquarium, dataset_CottontailRabbits,\n    dataset_EgoHands, dataset_NorthAmericaMushrooms, dataset_Packages,\n    dataset_PascalVOC, dataset_pistols, dataset_pothole, dataset_Raccoon,\n    dataset_ShellfishOpenImages, dataset_thermalDogsAndPeople,\n    dataset_VehiclesOpenImages\n]\nmetrics = [\n    val_evaluator_AerialMaritimeDrone, val_evaluator_Aquarium,\n    val_evaluator_CottontailRabbits, val_evaluator_EgoHands,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_Packages,\n    val_evaluator_PascalVOC, val_evaluator_pistols, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_ShellfishOpenImages,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_VehiclesOpenImages\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw35.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'  # noqa\n\ndataset_type = 'CocoDataset'\ndata_root = 'data/odinw/'\n\nbase_test_pipeline = _base_.test_pipeline\nbase_test_pipeline[-1]['meta_keys'] = ('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'text',\n                                       'custom_entities', 'caption_prompt')\n\n# ---------------------1 AerialMaritimeDrone_large---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/large/'\ndataset_AerialMaritimeDrone_large = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_large = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------2 AerialMaritimeDrone_tiled---------------------#\nclass_name = ('boat', 'car', 'dock', 'jetski', 'lift')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AerialMaritimeDrone/tiled/'\ndataset_AerialMaritimeDrone_tiled = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AerialMaritimeDrone_tiled = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------3 AmericanSignLanguageLetters---------------------#\nclass_name = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'AmericanSignLanguageLetters/American Sign Language Letters.v1-v1.coco/'  # noqa\ndataset_AmericanSignLanguageLetters = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_AmericanSignLanguageLetters = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------4 Aquarium---------------------#\nclass_name = ('fish', 'jellyfish', 'penguin', 'puffin', 'shark', 'starfish',\n              'stingray')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Aquarium/Aquarium Combined.v2-raw-1024.coco/'\ndataset_Aquarium = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Aquarium = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------5 BCCD---------------------#\nclass_name = ('Platelets', 'RBC', 'WBC')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'BCCD/BCCD.v3-raw.coco/'\ndataset_BCCD = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_BCCD = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------6 boggleBoards---------------------#\nclass_name = ('Q', 'a', 'an', 'b', 'c', 'd', 'e', 'er', 'f', 'g', 'h', 'he',\n              'i', 'in', 'j', 'k', 'l', 'm', 'n', 'o', 'o ', 'p', 'q', 'qu',\n              'r', 's', 't', 't\\\\', 'th', 'u', 'v', 'w', 'wild', 'x', 'y', 'z')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'boggleBoards/416x416AutoOrient/export/'\ndataset_boggleBoards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_boggleBoards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------7 brackishUnderwater---------------------#\nclass_name = ('crab', 'fish', 'jellyfish', 'shrimp', 'small_fish', 'starfish')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'brackishUnderwater/960x540/'\ndataset_brackishUnderwater = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_brackishUnderwater = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------8 ChessPieces---------------------#\nclass_name = ('  ', 'black bishop', 'black king', 'black knight', 'black pawn',\n              'black queen', 'black rook', 'white bishop', 'white king',\n              'white knight', 'white pawn', 'white queen', 'white rook')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ChessPieces/Chess Pieces.v23-raw.coco/'\ndataset_ChessPieces = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ChessPieces = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------9 CottontailRabbits---------------------#\nclass_name = ('rabbit', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'CottontailRabbits/'\ndataset_CottontailRabbits = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_CottontailRabbits = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------10 dice---------------------#\nclass_name = ('1', '2', '3', '4', '5', '6')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'dice/mediumColor/export/'\ndataset_dice = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_dice = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------11 DroneControl---------------------#\nclass_name = ('follow', 'follow_hand', 'land', 'land_hand', 'null', 'object',\n              'takeoff', 'takeoff-hand')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'DroneControl/Drone Control.v3-raw.coco/'\ndataset_DroneControl = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_DroneControl = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------12 EgoHands_generic---------------------#\nclass_name = ('hand', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/generic/'\ncaption_prompt = {'hand': {'suffix': ' of a person'}}\ndataset_EgoHands_generic = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_generic = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------13 EgoHands_specific---------------------#\nclass_name = ('myleft', 'myright', 'yourleft', 'yourright')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'EgoHands/specific/'\ndataset_EgoHands_specific = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_EgoHands_specific = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------14 HardHatWorkers---------------------#\nclass_name = ('head', 'helmet', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'HardHatWorkers/raw/'\ndataset_HardHatWorkers = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_HardHatWorkers = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------15 MaskWearing---------------------#\nclass_name = ('mask', 'no-mask')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MaskWearing/raw/'\ndataset_MaskWearing = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MaskWearing = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------16 MountainDewCommercial---------------------#\nclass_name = ('bottle', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'MountainDewCommercial/'\ndataset_MountainDewCommercial = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_MountainDewCommercial = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------17 NorthAmericaMushrooms---------------------#\nclass_name = ('flat mushroom', 'yellow mushroom')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\ndataset_NorthAmericaMushrooms = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/new_annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_NorthAmericaMushrooms = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/new_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------18 openPoetryVision---------------------#\nclass_name = ('American Typewriter', 'Andale Mono', 'Apple Chancery', 'Arial',\n              'Avenir', 'Baskerville', 'Big Caslon', 'Bradley Hand',\n              'Brush Script MT', 'Chalkboard', 'Comic Sans MS', 'Copperplate',\n              'Courier', 'Didot', 'Futura', 'Geneva', 'Georgia', 'Gill Sans',\n              'Helvetica', 'Herculanum', 'Impact', 'Kefa', 'Lucida Grande',\n              'Luminari', 'Marker Felt', 'Menlo', 'Monaco', 'Noteworthy',\n              'Optima', 'PT Sans', 'PT Serif', 'Palatino', 'Papyrus',\n              'Phosphate', 'Rockwell', 'SF Pro', 'SignPainter', 'Skia',\n              'Snell Roundhand', 'Tahoma', 'Times New Roman', 'Trebuchet MS',\n              'Verdana')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'openPoetryVision/512x512/'\ndataset_openPoetryVision = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_openPoetryVision = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------19 OxfordPets_by_breed---------------------#\nclass_name = ('cat-Abyssinian', 'cat-Bengal', 'cat-Birman', 'cat-Bombay',\n              'cat-British_Shorthair', 'cat-Egyptian_Mau', 'cat-Maine_Coon',\n              'cat-Persian', 'cat-Ragdoll', 'cat-Russian_Blue', 'cat-Siamese',\n              'cat-Sphynx', 'dog-american_bulldog',\n              'dog-american_pit_bull_terrier', 'dog-basset_hound',\n              'dog-beagle', 'dog-boxer', 'dog-chihuahua',\n              'dog-english_cocker_spaniel', 'dog-english_setter',\n              'dog-german_shorthaired', 'dog-great_pyrenees', 'dog-havanese',\n              'dog-japanese_chin', 'dog-keeshond', 'dog-leonberger',\n              'dog-miniature_pinscher', 'dog-newfoundland', 'dog-pomeranian',\n              'dog-pug', 'dog-saint_bernard', 'dog-samoyed',\n              'dog-scottish_terrier', 'dog-shiba_inu',\n              'dog-staffordshire_bull_terrier', 'dog-wheaten_terrier',\n              'dog-yorkshire_terrier')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-breed/'  # noqa\ndataset_OxfordPets_by_breed = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_breed = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------20 OxfordPets_by_species---------------------#\nclass_name = ('cat', 'dog')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'OxfordPets/by-species/'  # noqa\ndataset_OxfordPets_by_species = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_OxfordPets_by_species = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------21 PKLot---------------------#\nclass_name = ('space-empty', 'space-occupied')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PKLot/640/'  # noqa\ndataset_PKLot = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PKLot = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------22 Packages---------------------#\nclass_name = ('package', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Packages/Raw/'\ncaption_prompt = {\n    'package': {\n        'prefix': 'there is a ',\n        'suffix': ' on the porch'\n    }\n}\ndataset_Packages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=base_test_pipeline,\n    caption_prompt=caption_prompt,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Packages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------23 PascalVOC---------------------#\nclass_name = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',\n              'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',\n              'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',\n              'tvmonitor')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'PascalVOC/'\ndataset_PascalVOC = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_PascalVOC = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------24 pistols---------------------#\nclass_name = ('pistol', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pistols/export/'\ndataset_pistols = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pistols = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------25 plantdoc---------------------#\nclass_name = ('Apple Scab Leaf', 'Apple leaf', 'Apple rust leaf',\n              'Bell_pepper leaf', 'Bell_pepper leaf spot', 'Blueberry leaf',\n              'Cherry leaf', 'Corn Gray leaf spot', 'Corn leaf blight',\n              'Corn rust leaf', 'Peach leaf', 'Potato leaf',\n              'Potato leaf early blight', 'Potato leaf late blight',\n              'Raspberry leaf', 'Soyabean leaf', 'Soybean leaf',\n              'Squash Powdery mildew leaf', 'Strawberry leaf',\n              'Tomato Early blight leaf', 'Tomato Septoria leaf spot',\n              'Tomato leaf', 'Tomato leaf bacterial spot',\n              'Tomato leaf late blight', 'Tomato leaf mosaic virus',\n              'Tomato leaf yellow virus', 'Tomato mold leaf',\n              'Tomato two spotted spider mites leaf', 'grape leaf',\n              'grape leaf black rot')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'plantdoc/416x416/'\ndataset_plantdoc = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_plantdoc = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------26 pothole---------------------#\nclass_name = ('pothole', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'pothole/'\ncaption_prompt = {\n    'pothole': {\n        'name': 'holes',\n        'prefix': 'there are some ',\n        'suffix': ' on the road'\n    }\n}\ndataset_pothole = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    caption_prompt=caption_prompt,\n    pipeline=base_test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_pothole = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------27 Raccoon---------------------#\nclass_name = ('raccoon', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'Raccoon/Raccoon.v2-raw.coco/'\ndataset_Raccoon = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_Raccoon = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------28 selfdrivingCar---------------------#\nclass_name = ('biker', 'car', 'pedestrian', 'trafficLight',\n              'trafficLight-Green', 'trafficLight-GreenLeft',\n              'trafficLight-Red', 'trafficLight-RedLeft',\n              'trafficLight-Yellow', 'trafficLight-YellowLeft', 'truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'selfdrivingCar/fixedLarge/export/'\ndataset_selfdrivingCar = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='val_annotations_without_background.json',\n    data_prefix=dict(img=''),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_selfdrivingCar = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'val_annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------29 ShellfishOpenImages---------------------#\nclass_name = ('Crab', 'Lobster', 'Shrimp')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ShellfishOpenImages/raw/'\ndataset_ShellfishOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ShellfishOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------30 ThermalCheetah---------------------#\nclass_name = ('cheetah', 'human')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'ThermalCheetah/'\ndataset_ThermalCheetah = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_ThermalCheetah = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------31 thermalDogsAndPeople---------------------#\nclass_name = ('dog', 'person')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'thermalDogsAndPeople/'\ndataset_thermalDogsAndPeople = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_thermalDogsAndPeople = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------32 UnoCards---------------------#\nclass_name = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11',\n              '12', '13', '14')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'UnoCards/raw/'\ndataset_UnoCards = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_UnoCards = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------33 VehiclesOpenImages---------------------#\nclass_name = ('Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'VehiclesOpenImages/416x416/'\ndataset_VehiclesOpenImages = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_VehiclesOpenImages = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------34 WildfireSmoke---------------------#\nclass_name = ('smoke', )\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'WildfireSmoke/'\ndataset_WildfireSmoke = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_WildfireSmoke = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# ---------------------35 websiteScreenshots---------------------#\nclass_name = ('button', 'field', 'heading', 'iframe', 'image', 'label', 'link',\n              'text')\nmetainfo = dict(classes=class_name)\n_data_root = data_root + 'websiteScreenshots/'\ndataset_websiteScreenshots = dict(\n    type=dataset_type,\n    metainfo=metainfo,\n    data_root=_data_root,\n    ann_file='valid/annotations_without_background.json',\n    data_prefix=dict(img='valid/'),\n    pipeline=_base_.test_pipeline,\n    test_mode=True,\n    return_classes=True)\nval_evaluator_websiteScreenshots = dict(\n    type='CocoMetric',\n    ann_file=_data_root + 'valid/annotations_without_background.json',\n    metric='bbox')\n\n# --------------------- Config---------------------#\n\ndataset_prefixes = [\n    'AerialMaritimeDrone_large',\n    'AerialMaritimeDrone_tiled',\n    'AmericanSignLanguageLetters',\n    'Aquarium',\n    'BCCD',\n    'boggleBoards',\n    'brackishUnderwater',\n    'ChessPieces',\n    'CottontailRabbits',\n    'dice',\n    'DroneControl',\n    'EgoHands_generic',\n    'EgoHands_specific',\n    'HardHatWorkers',\n    'MaskWearing',\n    'MountainDewCommercial',\n    'NorthAmericaMushrooms',\n    'openPoetryVision',\n    'OxfordPets_by_breed',\n    'OxfordPets_by_species',\n    'PKLot',\n    'Packages',\n    'PascalVOC',\n    'pistols',\n    'plantdoc',\n    'pothole',\n    'Raccoons',\n    'selfdrivingCar',\n    'ShellfishOpenImages',\n    'ThermalCheetah',\n    'thermalDogsAndPeople',\n    'UnoCards',\n    'VehiclesOpenImages',\n    'WildfireSmoke',\n    'websiteScreenshots',\n]\n\ndatasets = [\n    dataset_AerialMaritimeDrone_large, dataset_AerialMaritimeDrone_tiled,\n    dataset_AmericanSignLanguageLetters, dataset_Aquarium, dataset_BCCD,\n    dataset_boggleBoards, dataset_brackishUnderwater, dataset_ChessPieces,\n    dataset_CottontailRabbits, dataset_dice, dataset_DroneControl,\n    dataset_EgoHands_generic, dataset_EgoHands_specific,\n    dataset_HardHatWorkers, dataset_MaskWearing, dataset_MountainDewCommercial,\n    dataset_NorthAmericaMushrooms, dataset_openPoetryVision,\n    dataset_OxfordPets_by_breed, dataset_OxfordPets_by_species, dataset_PKLot,\n    dataset_Packages, dataset_PascalVOC, dataset_pistols, dataset_plantdoc,\n    dataset_pothole, dataset_Raccoon, dataset_selfdrivingCar,\n    dataset_ShellfishOpenImages, dataset_ThermalCheetah,\n    dataset_thermalDogsAndPeople, dataset_UnoCards, dataset_VehiclesOpenImages,\n    dataset_WildfireSmoke, dataset_websiteScreenshots\n]\n\nmetrics = [\n    val_evaluator_AerialMaritimeDrone_large,\n    val_evaluator_AerialMaritimeDrone_tiled,\n    val_evaluator_AmericanSignLanguageLetters, val_evaluator_Aquarium,\n    val_evaluator_BCCD, val_evaluator_boggleBoards,\n    val_evaluator_brackishUnderwater, val_evaluator_ChessPieces,\n    val_evaluator_CottontailRabbits, val_evaluator_dice,\n    val_evaluator_DroneControl, val_evaluator_EgoHands_generic,\n    val_evaluator_EgoHands_specific, val_evaluator_HardHatWorkers,\n    val_evaluator_MaskWearing, val_evaluator_MountainDewCommercial,\n    val_evaluator_NorthAmericaMushrooms, val_evaluator_openPoetryVision,\n    val_evaluator_OxfordPets_by_breed, val_evaluator_OxfordPets_by_species,\n    val_evaluator_PKLot, val_evaluator_Packages, val_evaluator_PascalVOC,\n    val_evaluator_pistols, val_evaluator_plantdoc, val_evaluator_pothole,\n    val_evaluator_Raccoon, val_evaluator_selfdrivingCar,\n    val_evaluator_ShellfishOpenImages, val_evaluator_ThermalCheetah,\n    val_evaluator_thermalDogsAndPeople, val_evaluator_UnoCards,\n    val_evaluator_VehiclesOpenImages, val_evaluator_WildfireSmoke,\n    val_evaluator_websiteScreenshots\n]\n\n# -------------------------------------------------#\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/odinw/override_category.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\n\nimport mmengine\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Override Category')\n    parser.add_argument('data_root')\n    return parser.parse_args()\n\n\ndef main():\n    args = parse_args()\n\n    ChessPieces = [{\n        'id': 1,\n        'name': '  ',\n        'supercategory': 'pieces'\n    }, {\n        'id': 2,\n        'name': 'black bishop',\n        'supercategory': 'pieces'\n    }, {\n        'id': 3,\n        'name': 'black king',\n        'supercategory': 'pieces'\n    }, {\n        'id': 4,\n        'name': 'black knight',\n        'supercategory': 'pieces'\n    }, {\n        'id': 5,\n        'name': 'black pawn',\n        'supercategory': 'pieces'\n    }, {\n        'id': 6,\n        'name': 'black queen',\n        'supercategory': 'pieces'\n    }, {\n        'id': 7,\n        'name': 'black rook',\n        'supercategory': 'pieces'\n    }, {\n        'id': 8,\n        'name': 'white bishop',\n        'supercategory': 'pieces'\n    }, {\n        'id': 9,\n        'name': 'white king',\n        'supercategory': 'pieces'\n    }, {\n        'id': 10,\n        'name': 'white knight',\n        'supercategory': 'pieces'\n    }, {\n        'id': 11,\n        'name': 'white pawn',\n        'supercategory': 'pieces'\n    }, {\n        'id': 12,\n        'name': 'white queen',\n        'supercategory': 'pieces'\n    }, {\n        'id': 13,\n        'name': 'white rook',\n        'supercategory': 'pieces'\n    }]\n\n    _data_root = args.data_root + 'ChessPieces/Chess Pieces.v23-raw.coco/'\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = ChessPieces\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n    CottontailRabbits = [{\n        'id': 1,\n        'name': 'rabbit',\n        'supercategory': 'Cottontail-Rabbit'\n    }]\n\n    _data_root = args.data_root + 'CottontailRabbits/'\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = CottontailRabbits\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n    NorthAmericaMushrooms = [{\n        'id': 1,\n        'name': 'flat mushroom',\n        'supercategory': 'mushroom'\n    }, {\n        'id': 2,\n        'name': 'yellow mushroom',\n        'supercategory': 'mushroom'\n    }]\n\n    _data_root = args.data_root + 'NorthAmericaMushrooms/North American Mushrooms.v1-416x416.coco/'  # noqa\n    json_data = mmengine.load(_data_root +\n                              'valid/annotations_without_background.json')\n    json_data['categories'] = NorthAmericaMushrooms\n    mmengine.dump(json_data,\n                  _data_root + 'valid/new_annotations_without_background.json')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "configs/mm_grounding_dino/people_in_painting/grounding_dino_swin-t_finetune_8xb4_50e_people_in_painting.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\n# https://universe.roboflow.com/roboflow-100/people-in-paintings/dataset/2\ndata_root = 'data/people_in_painting_v2/'\nclass_name = ('Human', )\npalette = [(220, 20, 60)]\n\nmetainfo = dict(classes=class_name, palette=palette)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    sampler=dict(_delete_=True, type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=10,\n        dataset=dict(\n            type='CocoDataset',\n            data_root=data_root,\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            pipeline=train_pipeline,\n            return_classes=True,\n            data_prefix=dict(img='train/'),\n            ann_file='train/_annotations.coco.json')))\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=metainfo,\n        data_root=data_root,\n        return_classes=True,\n        ann_file='valid/_annotations.coco.json',\n        data_prefix=dict(img='valid/')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'valid/_annotations.coco.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={\n        'absolute_pos_embed': dict(decay_mult=0.),\n        'backbone': dict(lr_mult=0.1)\n    }))\n\n# learning policy\nmax_epochs = 5\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[4],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/refcoco/grounding_dino_swin-t_finetune_8xb4_5e_grefcoco.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    # change this\n    dict(type='RandomFlip', prob=0.0),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='ODVGDataset',\n        data_root=data_root,\n        ann_file='mdetr_annotations/finetune_grefcoco_train_vg.json',\n        data_prefix=dict(img='train2014/'),\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        return_classes=True,\n        pipeline=train_pipeline))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_val.json'\nval_dataset_all_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\nval_evaluator_all_val = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_testA.json'\nval_dataset_refcoco_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testA = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_testB.json'\nval_dataset_refcoco_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testB = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\ndatasets = [\n    val_dataset_all_val, val_dataset_refcoco_testA, val_dataset_refcoco_testB\n]\ndataset_prefixes = ['grefcoco_val', 'grefcoco_testA', 'grefcoco_testB']\nmetrics = [\n    val_evaluator_all_val, val_evaluator_refcoco_testA,\n    val_evaluator_refcoco_testB\n]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            # 'language_model': dict(lr_mult=0),\n        }))\n\n# learning policy\nmax_epochs = 5\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/refcoco/grounding_dino_swin-t_finetune_8xb4_5e_refcoco.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    # change this\n    dict(type='RandomFlip', prob=0.0),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='ODVGDataset',\n        data_root=data_root,\n        ann_file='mdetr_annotations/finetune_refcoco_train_vg.json',\n        data_prefix=dict(img='train2014/'),\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        return_classes=True,\n        pipeline=train_pipeline))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco_val.json'\nval_dataset_all_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\nval_evaluator_all_val = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco_testA.json'\nval_dataset_refcoco_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testA = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco_testB.json'\nval_dataset_refcoco_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testB = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\ndatasets = [\n    val_dataset_all_val, val_dataset_refcoco_testA, val_dataset_refcoco_testB\n]\ndataset_prefixes = ['refcoco_val', 'refcoco_testA', 'refcoco_testB']\nmetrics = [\n    val_evaluator_all_val, val_evaluator_refcoco_testA,\n    val_evaluator_refcoco_testB\n]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            # 'language_model': dict(lr_mult=0),\n        }))\n\n# learning policy\nmax_epochs = 5\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/refcoco/grounding_dino_swin-t_finetune_8xb4_5e_refcoco_plus.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    # change this\n    dict(type='RandomFlip', prob=0.0),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='ODVGDataset',\n        data_root=data_root,\n        ann_file='mdetr_annotations/finetune_refcoco+_train_vg.json',\n        data_prefix=dict(img='train2014/'),\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        return_classes=True,\n        pipeline=train_pipeline))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco+_val.json'\nval_dataset_all_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\nval_evaluator_all_val = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco+_testA.json'\nval_dataset_refcoco_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testA = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco+_testB.json'\nval_dataset_refcoco_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testB = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\ndatasets = [\n    val_dataset_all_val, val_dataset_refcoco_testA, val_dataset_refcoco_testB\n]\ndataset_prefixes = ['refcoco+_val', 'refcoco+_testA', 'refcoco+_testB']\nmetrics = [\n    val_evaluator_all_val, val_evaluator_refcoco_testA,\n    val_evaluator_refcoco_testB\n]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            # 'language_model': dict(lr_mult=0),\n        }))\n\n# learning policy\nmax_epochs = 5\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/refcoco/grounding_dino_swin-t_finetune_8xb4_5e_refcocog.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/coco/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    # change this\n    dict(type='RandomFlip', prob=0.0),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='RandomSamplingNegPos',\n        tokenizer_name=_base_.lang_model_name,\n        num_sample_negative=85,\n        max_tokens=256),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities', 'tokens_positive', 'dataset_mode'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type='ODVGDataset',\n        data_root=data_root,\n        ann_file='mdetr_annotations/finetune_refcocog_train_vg.json',\n        data_prefix=dict(img='train2014/'),\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        return_classes=True,\n        pipeline=train_pipeline))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcocog_val.json'\nval_dataset_all_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\nval_evaluator_all_val = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcocog_test.json'\nval_dataset_refcoco_test = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=_base_.test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_test = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\ndatasets = [val_dataset_all_val, val_dataset_refcoco_test]\ndataset_prefixes = ['refcocog_val', 'refcocog_test']\nmetrics = [val_evaluator_all_val, val_evaluator_refcoco_test]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'backbone': dict(lr_mult=0.1),\n            # 'language_model': dict(lr_mult=0),\n        }))\n\n# learning policy\nmax_epochs = 5\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/refcoco/grounding_dino_swin-t_pretrain_zeroshot_refexp.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\n# 30 is an empirical value, just set it to the maximum value\n# without affecting the evaluation result\nmodel = dict(test_cfg=dict(max_per_img=30))\n\ndata_root = 'data/coco/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', backend_args=None,\n        imdecode_backend='pillow'),\n    dict(\n        type='FixScaleResize',\n        scale=(800, 1333),\n        keep_ratio=True,\n        backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities',\n                   'tokens_positive'))\n]\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/final_refexp_val.json'\nval_dataset_all_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\nval_evaluator_all_val = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco_testA.json'\nval_dataset_refcoco_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testA = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco_testB.json'\nval_dataset_refcoco_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_testB = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco+_testA.json'\nval_dataset_refcoco_plus_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_plus_testA = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcoco+_testB.json'\nval_dataset_refcoco_plus_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcoco_plus_testB = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_refcocog_test.json'\nval_dataset_refcocog_test = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_refcocog_test = dict(\n    type='RefExpMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    topk=(1, 5, 10))\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_val.json'\nval_dataset_grefcoco_val = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_grefcoco_val = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_testA.json'\nval_dataset_grefcoco_testA = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_grefcoco_testA = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\nann_file = 'mdetr_annotations/finetune_grefcoco_testB.json'\nval_dataset_grefcoco_testB = dict(\n    type='MDETRStyleRefCocoDataset',\n    data_root=data_root,\n    ann_file=ann_file,\n    data_prefix=dict(img='train2014/'),\n    test_mode=True,\n    return_classes=True,\n    pipeline=test_pipeline,\n    backend_args=None)\n\nval_evaluator_grefcoco_testB = dict(\n    type='gRefCOCOMetric',\n    ann_file=data_root + ann_file,\n    metric='bbox',\n    iou_thrs=0.5,\n    thresh_score=0.7,\n    thresh_f1=1.0)\n\n# -------------------------------------------------#\ndatasets = [\n    val_dataset_all_val, val_dataset_refcoco_testA, val_dataset_refcoco_testB,\n    val_dataset_refcoco_plus_testA, val_dataset_refcoco_plus_testB,\n    val_dataset_refcocog_test, val_dataset_grefcoco_val,\n    val_dataset_grefcoco_testA, val_dataset_grefcoco_testB\n]\ndataset_prefixes = [\n    'val', 'refcoco_testA', 'refcoco_testB', 'refcoco+_testA',\n    'refcoco+_testB', 'refcocog_test', 'grefcoco_val', 'grefcoco_testA',\n    'grefcoco_testB'\n]\nmetrics = [\n    val_evaluator_all_val, val_evaluator_refcoco_testA,\n    val_evaluator_refcoco_testB, val_evaluator_refcoco_plus_testA,\n    val_evaluator_refcoco_plus_testB, val_evaluator_refcocog_test,\n    val_evaluator_grefcoco_val, val_evaluator_grefcoco_testA,\n    val_evaluator_grefcoco_testB\n]\n\nval_dataloader = dict(\n    dataset=dict(_delete_=True, type='ConcatDataset', datasets=datasets))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    _delete_=True,\n    type='MultiDatasetsEvaluator',\n    metrics=metrics,\n    dataset_prefixes=dataset_prefixes)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/mm_grounding_dino/rtts/grounding_dino_swin-t_finetune_8xb4_1x_rtts.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/RTTS/'\nclass_name = ('bicycle', 'bus', 'car', 'motorbike', 'person')\npalette = [(255, 97, 0), (0, 201, 87), (176, 23, 31), (138, 43, 226),\n           (30, 144, 255)]\n\nmetainfo = dict(classes=class_name, palette=palette)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    sampler=dict(_delete_=True, type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        _delete_=True,\n        type='CocoDataset',\n        data_root=data_root,\n        metainfo=metainfo,\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        pipeline=train_pipeline,\n        return_classes=True,\n        ann_file='annotations_json/rtts_train.json',\n        data_prefix=dict(img='')))\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=metainfo,\n        data_root=data_root,\n        return_classes=True,\n        ann_file='annotations_json/rtts_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations_json/rtts_val.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={\n        'absolute_pos_embed': dict(decay_mult=0.),\n        'backbone': dict(lr_mult=0.1)\n    }))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/ruod/grounding_dino_swin-t_finetune_8xb4_1x_ruod.py",
    "content": "_base_ = '../grounding_dino_swin-t_pretrain_obj365.py'\n\ndata_root = 'data/RUOD/'\nclass_name = ('holothurian', 'echinus', 'scallop', 'starfish', 'fish',\n              'corals', 'diver', 'cuttlefish', 'turtle', 'jellyfish')\npalette = [(235, 211, 70), (106, 90, 205), (160, 32, 240), (176, 23, 31),\n           (142, 0, 0), (230, 0, 0), (106, 0, 228), (60, 100, 0), (80, 100, 0),\n           (70, 0, 0)]\n\nmetainfo = dict(classes=class_name, palette=palette)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction', 'text',\n                   'custom_entities'))\n]\n\ntrain_dataloader = dict(\n    sampler=dict(_delete_=True, type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        _delete_=True,\n        type='CocoDataset',\n        data_root=data_root,\n        metainfo=metainfo,\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        pipeline=train_pipeline,\n        return_classes=True,\n        ann_file='RUOD_ANN/instances_train.json',\n        data_prefix=dict(img='RUOD_pic/train/')))\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=metainfo,\n        data_root=data_root,\n        return_classes=True,\n        ann_file='RUOD_ANN/instances_test.json',\n        data_prefix=dict(img='RUOD_pic/test/')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'RUOD_ANN/instances_test.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={\n        'absolute_pos_embed': dict(decay_mult=0.),\n        'backbone': dict(lr_mult=0.1)\n    }))\n\n# learning policy\nmax_epochs = 12\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1, save_best='auto'))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth'  # noqa\n"
  },
  {
    "path": "configs/mm_grounding_dino/usage.md",
    "content": "# Usage\n\n## Install\n\nAfter installing MMDet according to the instructions in the [get_started](../../docs/zh_cn/get_started.md) section, you need to install additional dependency packages:\n\n```shell\ncd $MMDETROOT\n\npip install -r requirements/multimodal.txt\npip install emoji ddd-dataset\npip install git+https://github.com/lvis-dataset/lvis-api.git\"\n```\n\nPlease note that since the LVIS third-party library does not currently support numpy 1.24, ensure that your numpy version meets the requirements. It is recommended to install numpy version 1.23.\n\n## Instructions\n\n### Download BERT Weight\n\nMM Grounding DINO uses BERT as its language model and requires access to https://huggingface.co/. If you encounter connection errors due to network access issues, you can download the necessary files on a computer with network access and save them locally. Finally, modify the `lang_model_name` field in the configuration file to the local path. For specific instructions, please refer to the following code:\n\n```python\nfrom transformers import BertConfig, BertModel\nfrom transformers import AutoTokenizer\n\nconfig = BertConfig.from_pretrained(\"bert-base-uncased\")\nmodel = BertModel.from_pretrained(\"bert-base-uncased\", add_pooling_layer=False, config=config)\ntokenizer = AutoTokenizer.from_pretrained(\"bert-base-uncased\")\n\nconfig.save_pretrained(\"your path/bert-base-uncased\")\nmodel.save_pretrained(\"your path/bert-base-uncased\")\ntokenizer.save_pretrained(\"your path/bert-base-uncased\")\n```\n\n### Download NLTK Weight\n\nWhen MM Grounding DINO performs Phrase Grounding inference, it may extract noun phrases. Although it downloads specific models at runtime, considering that some users' running environments cannot connect to the internet, it is possible to download them in advance to the `~/nltk_data` path.\n\n```python\nimport nltk\nnltk.download('punkt', download_dir='~/nltk_data')\nnltk.download('averaged_perceptron_tagger', download_dir='~/nltk_data')\n```\n\n### Download MM Grounding DINO-T Weight\n\nFor convenience in demonstration, you can download the MM Grounding DINO-T model weights in advance to the current path.\n\n```shell\nwget load_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth' # noqa\n```\n\n## Inference\n\nBefore inference, for a better experience of the inference effects on different images, it is recommended that you first download [these images](https://github.com/microsoft/X-Decoder/tree/main/inference_demo/images) to the current path.\n\nMM Grounding DINO supports four types of inference methods: Closed-Set Object Detection, Open Vocabulary Object Detection, Phrase Grounding, and Referential Expression Comprehension. The details are explained below.\n\n**(1) Closed-Set Object Detection**\n\nSince MM Grounding DINO is a pretrained model, it can theoretically be applied to any closed-set detection dataset. Currently, we support commonly used datasets such as coco/voc/cityscapes/objects365v1/lvis, etc. Below, we will use coco as an example.\n\n```shell\npython demo/image_demo.py images/animals.png \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts '$: coco'\n```\n\nThe predictions for `outputs/vis/animals.png` will be generated in the current directory, as shown in the following image.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/1659211c-c117-4097-a659-84ab26efa2d3\" width=\"70%\"/>\n</div>\n\nSince ostrich is not one of the 80 classes in COCO, it will not be detected.\n\nIt's important to note that Objects365v1 and LVIS have a large number of categories. If you try to input all category names directly into the network, it may exceed 256 tokens, leading to poor model predictions. In such cases, you can use the `--chunked-size` parameter to perform chunked predictions. However, please be aware that chunked predictions may take longer to complete due to the large number of categories.\n\n```shell\npython demo/image_demo.py images/animals.png \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts '$: lvis'  --chunked-size 70 \\\n        --palette random\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/93554cf5-a1c5-4318-8e16-615cd2270fb6\" width=\"70%\"/>\n</div>\n\nDifferent `--chunked-size` values can lead to different prediction results. You can experiment with different chunked sizes to find the one that works best for your specific task and dataset.\n\n**(2) Open Vocabulary Object Detection**\n\nOpen vocabulary object detection refers to the ability to input arbitrary class names during inference.\n\n```shell\npython demo/image_demo.py images/animals.png \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'zebra. giraffe' -c\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/75e4a81f-4644-4306-8f66-60e684ac32db\" width=\"70%\"/>\n</div>\n\n**(3) Phrase Grounding**\n\nPhrase Grounding refers to the process where a user inputs a natural language description, and the model automatically detects the corresponding bounding boxes for the mentioned noun phrases. It can be used in two ways:\n\n1. Automatically extracting noun phrases using the NLTK library and then performing detection.\n\n```shell\npython demo/image_demo.py images/apples.jpg \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'There are many apples here.'\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/7c5839d2-3266-49e1-8be6-012f258d710b\" width=\"70%\"/>\n</div>\n\nThe program will automatically split `many apples` as a noun phrase and then detect the corresponding objects. Different input descriptions can have a significant impact on the prediction results.\n\n2. Users can manually specify which parts of the sentence are noun phrases to avoid errors in NLTK extraction.\n\n```shell\npython demo/image_demo.py images/fruit.jpg \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'The picture contains watermelon, flower, and a white bottle.' \\\n        --tokens-positive \"[[[21,31]], [[45,59]]]\"  --pred-score-thr 0.12\n```\n\nThe noun phrase corresponding to positions 21-31 is `watermelon`, and the noun phrase corresponding to positions 45-59 is `a white bottle`.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/15080faf-048d-4201-a126-a9c773580f5e\" width=\"70%\"/>\n</div>\n\n**(4) Referential Expression Comprehension**\n\nReferential expression understanding refers to the model automatically comprehending the referential expressions involved in a user's language description without the need for noun phrase extraction.\n\n```shell\npython demo/image_demo.py images/apples.jpg \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'red apple.' \\\n        --tokens-positive -1\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/40b970c3-60cd-4c78-a2cb-2c41b0442932\" width=\"70%\"/>\n</div>\n\n## Evaluation\n\nOur provided evaluation scripts are unified, and you only need to prepare the data in advance and then run the relevant configuration.\n\n(1) Zero-Shot COCO2017 val\n\n```shell\n# single GPU\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n\n# 8 GPUs\n./tools/dist_test.sh configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth 8\n```\n\n(2) Zero-Shot ODinW13\n\n```shell\n# single GPU\npython tools/test.py configs/mm_grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw13.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n\n# 8 GPUs\n./tools/dist_test.sh configs/mm_grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw13.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth 8\n```\n\n## Visualization of Evaluation Results\n\nFor the convenience of visualizing and analyzing model prediction results, we provide support for visualizing evaluation dataset prediction results. Taking referential expression understanding as an example, the usage is as follows:\n\n```shell\npython tools/test.py configs/mm_grounding_dino/refcoco/grounding_dino_swin-t_pretrain_zeroshot_refexp \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth --work-dir refcoco_result --show-dir save_path\n```\n\nDuring the inference process, it will save the visualization results to the `refcoco_result/{current_timestamp}/save_path` directory. For other evaluation dataset visualizations, you only need to replace the configuration file.\n\nHere are some visualization results for various datasets. The left image represents the Ground Truth (GT). The right image represents the Predicted Result.\n\n1. COCO2017 val Results：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/3a0fa894-c0a5-4c1f-bdf0-1c6fd17abafa\" width=\"70%\"/>\n</div>\n\n2. Flickr30k Entities Results：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/e9f2667f-9dca-464b-b995-599aa2731b34\" width=\"70%\"/>\n</div>\n\n3. DOD Results：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/c71a306b-1055-4344-ba1d-ae4c57f2cb2f\" width=\"70%\"/>\n</div>\n\n4. RefCOCO val Results：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/b175959d-d788-4b5e-8b11-e8e34753457f\" width=\"70%\"/>\n</div>\n\n5. RefCOCO testA Results：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/c087f889-f96c-4355-8a15-7dc2738b4223\" width=\"70%\"/>\n</div>\n\n6. gRefCOCO val Results：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/96c2e783-17da-462e-a7cf-937555e26c90\" width=\"70%\"/>\n</div>\n\n## Training\n\nIf you want to reproduce our results, you can train the model by using the following command after preparing the dataset:\n\n```shell\n# Training on a single machine with 8 GPUs for obj365v1 dataset\n./tools/dist_train.sh configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py 8\n# Training on a single machine with 8 GPUs for datasets like obj365v1, goldg, grit, v3det, and other datasets is similar.\n./tools/dist_train.sh configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det.py 8\n```\n\nFor multi-machine training, please refer to [train.md](../../docs/zh_cn/user_guides/train.md). The MM-Grounding-DINO T model is designed to work with 32 GPUs (specifically, 3090Ti GPUs). If your total batch size is not 32x4=128, you will need to manually adjust the learning rate accordingly.\n\n### Pretraining Custom Format Explanation\n\nIn order to standardize the pretraining formats for different datasets, we refer to the format design proposed by [Open-GroundingDino](https://github.com/longzw1997/Open-GroundingDino). Specifically, it is divided into two formats.\n\n**(1) Object Detection Format (OD)**\n\n```text\n{\"filename\": \"obj365_train_000000734304.jpg\",\n \"height\": 512,\n \"width\": 769,\n \"detection\": {\n    \"instances\": [\n          {\"bbox\": [109.4768676992, 346.0190429696, 135.1918335098, 365.3641967616], \"label\": 2, \"category\": \"chair\"},\n          {\"bbox\": [58.612365705900004, 323.2281494016, 242.6005859067, 451.4166870016], \"label\": 8, \"category\": \"car\"}\n                ]\n      }\n}\n```\n\nThe numerical values corresponding to labels in the label dictionary should match the respective label_map. Each item in the instances list corresponds to a bounding box (in the format x1y1x2y2).\n\n**(2) Phrase Grounding Format (VG)**\n\n```text\n{\"filename\": \"2405116.jpg\",\n \"height\": 375,\n \"width\": 500,\n \"grounding\":\n     {\"caption\": \"Two surfers walking down the shore. sand on the beach.\",\n      \"regions\": [\n            {\"bbox\": [206, 156, 282, 248], \"phrase\": \"Two surfers\", \"tokens_positive\": [[0, 3], [4, 11]]},\n            {\"bbox\": [303, 338, 443, 343], \"phrase\": \"sand\", \"tokens_positive\": [[36, 40]]},\n            {\"bbox\": [[327, 223, 421, 282], [300, 200, 400, 210]], \"phrase\": \"beach\", \"tokens_positive\": [[48, 53]]}\n               ]\n      }\n```\n\nThe `tokens_positive` field indicates the character positions of the current phrase within the caption.\n\n## Example of Fine-tuning Custom Dataset\n\nIn order to facilitate downstream fine-tuning on custom datasets, we have provided a fine-tuning example using the simple \"cat\" dataset as an illustration.\n\n### 1 Data Preparation\n\n```shell\ncd mmdetection\nwget https://download.openmmlab.com/mmyolo/data/cat_dataset.zip\nunzip cat_dataset.zip -d data/cat/\n```\n\nThe \"cat\" dataset is a single-category dataset consisting of 144 images, already converted to the COCO format.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/25873202/205423220-c4b8f2fd-22ba-4937-8e47-1b3f6a8facd8.png\" alt=\"cat dataset\"/>\n</div>\n\n### 2 Configuration Preparation\n\nDue to the simplicity and small size of the \"cat\" dataset, we trained it for 20 epochs using 8 GPUs, with corresponding learning rate scaling. We did not train the language model, only the visual model.\n\nDetailed configuration information can be found in [grounding_dino_swin-t_finetune_8xb4_20e_cat](grounding_dino_swin-t_finetune_8xb4_20e_cat.py).\n\n### 3 Visualization and Evaluation of Zero-Shot Results\n\nDue to MM Grounding DINO being an open-set detection model, you can perform detection and evaluation even if it was not trained on the cat dataset.\n\nVisualization of a single image:\n\n```shell\ncd mmdetection\npython demo/image_demo.py data/cat/images/IMG_20211205_120756.jpg configs/mm_grounding_dino/grounding_dino_swin-t_finetune_8xb4_20e_cat.py --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth --texts cat.\n```\n\nEvaluation results of Zero-shot on test dataset：\n\n```shell\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_finetune_8xb4_20e_cat.py grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n```\n\n```text\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.881\n Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 1.000\n Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.929\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.881\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.913\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.913\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.913\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.913\n```\n\n### 4 Fine-tuning\n\n```shell\n./tools/dist_train.sh configs/mm_grounding_dino/grounding_dino_swin-t_finetune_8xb4_20e_cat.py 8 --work-dir cat_work_dir\n```\n\nThe model will save the best-performing checkpoint. It achieved its best performance at the 16th epoch, with the following results:\n\n```text\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.901\n Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 1.000\n Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.930\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.901\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.967\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.967\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.967\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.967\n```\n\nWe can observe that after fine-tuning, the training performance on the cat dataset improved from 88.1 to 90.1. However, due to the small dataset size, the evaluation metrics show some fluctuations.\n\n## Iterative Generation and Optimization Pipeline of Model Self-training Pseduo Label\n\nTo facilitate users in creating their own datasets from scratch or those who want to leverage the model's inference capabilities for iterative pseudo-label generation and optimization, continuously modifying pseudo-labels to improve model performance, we have provided relevant pipelines.\n\nSince we have defined two data formats, we will provide separate explanations for demonstration purposes.\n\n### 1 Object Detection Format\n\nHere, we continue to use the aforementioned cat dataset as an example. Let's assume that we currently have a series of images and predefined categories but no annotations.\n\n1. Generate initial `odvg` format file\n\n```python\nimport os\nimport cv2\nimport json\nimport jsonlines\n\ndata_root = 'data/cat'\nimages_path = os.path.join(data_root, 'images')\nout_path = os.path.join(data_root, 'cat_train_od.json')\nmetas = []\nfor files in os.listdir(images_path):\n    img = cv2.imread(os.path.join(images_path, files))\n    height, width, _ = img.shape\n    metas.append({\"filename\": files, \"height\": height, \"width\": width})\n\nwith jsonlines.open(out_path, mode='w') as writer:\n    writer.write_all(metas)\n\n# 生成 label_map.json，由于只有一个类别，所以只需要写一个 cat 即可\nlabel_map_path = os.path.join(data_root, 'cat_label_map.json')\nwith open(label_map_path, 'w') as f:\n    json.dump({'0': 'cat'}, f)\n```\n\nTwo files, `cat_train_od.json` and `cat_label_map.json`, will be generated in the `data/cat` directory.\n\n2. Inference with pre-trained model and save the results\n\nWe provide a readily usable [configuration](grounding_dino_swin-t_pretrain_pseudo-labeling_cat.py). If you are using a different dataset, you can refer to this configuration for modifications.\n\n```shell\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_pseudo-labeling_cat.py \\\n    grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n```\n\nA new file `cat_train_od_v1.json` will be generated in the `data/cat` directory. You can manually open it to confirm or use the provided [script](../../tools/analysis_tools/browse_grounding_raw.py) to visualize the results.\n\n```shell\npython tools/analysis_tools/browse_grounding_raw.py data/cat/ cat_train_od_v1.json images --label-map-file cat_label_map.json -o your_output_dir --not-show\n```\n\nThe visualization results will be generated in the `your_output_dir` directory.\n\n3. Continue training to boost performance\n\nAfter obtaining pseudo-labels, you can mix them with some pre-training data for further pre-training to improve the model's performance on the current dataset. Then, you can repeat step 2 to obtain more accurate pseudo-labels, and continue this iterative process.\n\n### 2 Phrase Grounding Format\n\n1. Generate initial `odvg` format file\n\nThe bootstrapping process of Phrase Grounding requires providing captions corresponding to each image and pre-segmented phrase information initially. Taking flickr30k entities images as an example, the generated typical file should look like this:\n\n```text\n[\n{\"filename\": \"3028766968.jpg\",\n \"height\": 375,\n \"width\": 500,\n \"grounding\":\n     {\"caption\": \"Man with a black shirt on sit behind a desk sorting threw a giant stack of people work with a smirk on his face .\",\n      \"regions\": [\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a giant stack of people\", \"tokens_positive\": [[58, 81]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a black shirt\", \"tokens_positive\": [[9, 22]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a desk\", \"tokens_positive\": [[37, 43]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"his face\", \"tokens_positive\": [[103, 111]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"Man\", \"tokens_positive\": [[0, 3]]}]}}\n{\"filename\": \"6944134083.jpg\",\n \"height\": 319,\n \"width\": 500,\n \"grounding\":\n    {\"caption\": \"Two men are competing in a horse race .\",\n    \"regions\": [\n                {\"bbox\": [0, 0, 1, 1], \"phrase\": \"Two men\", \"tokens_positive\": [[0, 7]]}]}}\n]\n```\n\nBbox needs to be set to `[0, 0, 1, 1]` for initialization to make sure the programme could run, but this value would not be utilized.\n\n```text\n{\"filename\": \"3028766968.jpg\", \"height\": 375, \"width\": 500, \"grounding\": {\"caption\": \"Man with a black shirt on sit behind a desk sorting threw a giant stack of people work with a smirk on his face .\", \"regions\": [{\"bbox\": [0, 0, 1, 1], \"phrase\": \"a giant stack of people\", \"tokens_positive\": [[58, 81]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a black shirt\", \"tokens_positive\": [[9, 22]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a desk\", \"tokens_positive\": [[37, 43]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"his face\", \"tokens_positive\": [[103, 111]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"Man\", \"tokens_positive\": [[0, 3]]}]}}\n{\"filename\": \"6944134083.jpg\", \"height\": 319, \"width\": 500, \"grounding\": {\"caption\": \"Two men are competing in a horse race .\", \"regions\": [{\"bbox\": [0, 0, 1, 1], \"phrase\": \"Two men\", \"tokens_positive\": [[0, 7]]}]}}\n```\n\nYou can directly copy the text above, and assume that the text content is pasted into a file named `flickr_simple_train_vg.json`, which is placed in the pre-prepared `data/flickr30k_entities` dataset directory, as detailed in the data preparation document.\n\n2. Inference with pre-trained model and save the results\n\nWe provide a directly usable [configuration](https://chat.openai.com/c/grounding_dino_swin-t_pretrain_pseudo-labeling_flickr30k.py). If you are using a different dataset, you can refer to this configuration for modifications.\n\n```shell\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_pseudo-labeling_flickr30k.py \\\n    grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n```\n\nThe translation of your text from Chinese to English is: \"A new file `flickr_simple_train_vg_v1.json` will be generated in the `data/flickr30k_entities` directory. You can manually open it to confirm or use the [script](../../tools/analysis_tools/browse_grounding_raw.py) to visualize the effects\n\n```shell\npython tools/analysis_tools/browse_grounding_raw.py data/flickr30k_entities/ flickr_simple_train_vg_v1.json flickr30k_images -o your_output_dir --not-show\n```\n\nThe visualization results will be generated in the `your_output_dir` directory, as shown in the following image:\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/a1c72d52-fa52-4ebe-b793-716d34e7b83f\" width=\"50%\"/>\n</div>\n\n3. Continue training to boost performance\n\nAfter obtaining the pseudo-labels, you can mix some pre-training data to continue pre-training jointly, which enhances the model's performance on the current dataset. Then, rerun step 2 to obtain more accurate pseudo-labels, and repeat this cycle iteratively.\n"
  },
  {
    "path": "configs/mm_grounding_dino/usage_zh-CN.md",
    "content": "# 用法说明\n\n## 安装\n\n在按照 [get_started](../../docs/zh_cn/get_started.md) 一节的说明安装好 MMDet 之后，需要安装额外的依赖包：\n\n```shell\ncd $MMDETROOT\n\npip install -r requirements/multimodal.txt\npip install emoji ddd-dataset\npip install git+https://github.com/lvis-dataset/lvis-api.git\"\n```\n\n请注意由于 LVIS 第三方库暂时不支持 numpy 1.24，因此请确保您的 numpy 版本符合要求。建议安装 numpy 1.23 版本。\n\n## 说明\n\n### BERT 权重下载\n\nMM Grounding DINO 采用了 BERT 作为语言模型，需要访问 https://huggingface.co/, 如果您因为网络访问问题遇到连接错误，可以在有网络访问权限的电脑上下载所需文件并保存在本地。最后，修改配置文件中的 `lang_model_name` 字段为本地路径即可。具体请参考以下代码：\n\n```python\nfrom transformers import BertConfig, BertModel\nfrom transformers import AutoTokenizer\n\nconfig = BertConfig.from_pretrained(\"bert-base-uncased\")\nmodel = BertModel.from_pretrained(\"bert-base-uncased\", add_pooling_layer=False, config=config)\ntokenizer = AutoTokenizer.from_pretrained(\"bert-base-uncased\")\n\nconfig.save_pretrained(\"your path/bert-base-uncased\")\nmodel.save_pretrained(\"your path/bert-base-uncased\")\ntokenizer.save_pretrained(\"your path/bert-base-uncased\")\n```\n\n### NLTK 权重下载\n\nMM Grounding DINO 在进行 Phrase Grounding 推理时候可能会进行名词短语提取，虽然会在运行时候下载特定的模型，但是考虑到有些用户运行环境无法联网，因此可以提前下载到 `~/nltk_data` 路径下\n\n```python\nimport nltk\nnltk.download('punkt', download_dir='~/nltk_data')\nnltk.download('averaged_perceptron_tagger', download_dir='~/nltk_data')\n```\n\n### MM Grounding DINO-T 模型权重下载\n\n为了方便演示，您可以提前下载 MM Grounding DINO-T 模型权重到当前路径下\n\n```shell\nwget load_from = 'https://download.openmmlab.com/mmdetection/v3.0/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth' # noqa\n```\n\n## 推理\n\n在推理前，为了更好的体验不同图片的推理效果，建议您先下载 [这些图片](https://github.com/microsoft/X-Decoder/tree/main/inference_demo/images) 到当前路径下\n\nMM Grounding DINO 支持了闭集目标检测，开放词汇目标检测，Phrase Grounding 和指代性表达式理解 4 种推理方式，下面详细说明。\n\n**(1) 闭集目标检测**\n\n由于 MM Grounding DINO 是预训练模型，理论上可以应用于任何闭集检测数据集，目前我们支持了常用的 coco/voc/cityscapes/objects365v1/lvis 等，下面以 coco 为例\n\n```shell\npython demo/image_demo.py images/animals.png \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts '$: coco'\n```\n\n会在当前路径下生成 `outputs/vis/animals.png` 的预测结果，如下图所示\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/1659211c-c117-4097-a659-84ab26efa2d3\" width=\"70%\"/>\n</div>\n\n由于鸵鸟并不在 COCO 80 类中, 因此不会检测出来。\n\n需要注意，由于 objects365v1 和 lvis 类别很多，如果直接将类别名全部输入到网络中，会超过 256 个 token 导致模型预测效果极差，此时我们需要通过 `--chunked-size` 参数进行截断预测, 同时预测时间会比较长。\n\n```shell\npython demo/image_demo.py images/animals.png \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts '$: lvis'  --chunked-size 70 \\\n        --palette random\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/93554cf5-a1c5-4318-8e16-615cd2270fb6\" width=\"70%\"/>\n</div>\n\n不同的 `--chunked-size` 会导致不同的预测效果，您可以自行尝试。\n\n**(2) 开放词汇目标检测**\n\n开放词汇目标检测是指在推理时候，可以输入任意的类别名\n\n```shell\npython demo/image_demo.py images/animals.png \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'zebra. giraffe' -c\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/75e4a81f-4644-4306-8f66-60e684ac32db\" width=\"70%\"/>\n</div>\n\n**(3) Phrase Grounding**\n\nPhrase Grounding 是指的用户输入一句语言描述，模型自动对其涉及到的名词短语想对应的 bbox 进行检测，有两种用法\n\n1. 通过 NLTK 库自动提取名词短语，然后进行检测\n\n```shell\npython demo/image_demo.py images/apples.jpg \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'There are many apples here.'\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/7c5839d2-3266-49e1-8be6-012f258d710b\" width=\"70%\"/>\n</div>\n\n程序内部会自动切分出 `many apples` 作为名词短语，然后检测出对应物体。不同的输入描述对预测结果影响很大。\n\n2. 用户自己指定句子中哪些为名词短语，避免 NLTK 提取错误的情况\n\n```shell\npython demo/image_demo.py images/fruit.jpg \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'The picture contains watermelon, flower, and a white bottle.' \\\n        --tokens-positive \"[[[21,31]], [[45,59]]]\"  --pred-score-thr 0.12\n```\n\n21,31 对应的名词短语为 `watermelon`，45,59 对应的名词短语为 `a white bottle`。\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/15080faf-048d-4201-a126-a9c773580f5e\" width=\"70%\"/>\n</div>\n\n**(4) 指代性表达式理解**\n\n指代性表达式理解是指的用户输入一句语言描述，模型自动对其涉及到的指代性表达式进行理解, 不需要进行名词短语提取。\n\n```shell\npython demo/image_demo.py images/apples.jpg \\\n        configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth \\\n        --texts 'red apple.' \\\n        --tokens-positive -1\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/40b970c3-60cd-4c78-a2cb-2c41b0442932\" width=\"70%\"/>\n</div>\n\n## 评测\n\n我们所提供的评测脚本都是统一的，你只需要提前准备好数据，然后运行相关配置就可以了\n\n(1) Zero-Shot COCO2017 val\n\n```shell\n# 单卡\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n\n# 8 卡\n./tools/dist_test.sh configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth 8\n```\n\n(2) Zero-Shot ODinW13\n\n```shell\n# 单卡\npython tools/test.py configs/mm_grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw13.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n\n# 8 卡\n./tools/dist_test.sh configs/mm_grounding_dino/odinw/grounding_dino_swin-t_pretrain_odinw13.py \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth 8\n```\n\n## 评测数据集结果可视化\n\n为了方便大家对模型预测结果进行可视化和分析，我们支持了评测数据集预测结果可视化，以指代性表达式理解为例用法如下：\n\n```shell\npython tools/test.py configs/mm_grounding_dino/refcoco/grounding_dino_swin-t_pretrain_zeroshot_refexp \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth --work-dir refcoco_result --show-dir save_path\n```\n\n模型在推理过程中会将可视化结果保存到  `refcoco_result/{当前时间戳}/save_path` 路径下。其余评测数据集可视化只需要替换配置文件即可。\n\n下面展示一些数据集的可视化结果： 左图为 GT，右图为预测结果\n\n1. COCO2017 val 结果：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/3a0fa894-c0a5-4c1f-bdf0-1c6fd17abafa\" width=\"70%\"/>\n</div>\n\n2. Flickr30k Entities 结果：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/e9f2667f-9dca-464b-b995-599aa2731b34\" width=\"70%\"/>\n</div>\n\n3. DOD 结果：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/c71a306b-1055-4344-ba1d-ae4c57f2cb2f\" width=\"70%\"/>\n</div>\n\n4. RefCOCO val 结果：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/b175959d-d788-4b5e-8b11-e8e34753457f\" width=\"70%\"/>\n</div>\n\n5. RefCOCO testA 结果：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/c087f889-f96c-4355-8a15-7dc2738b4223\" width=\"70%\"/>\n</div>\n\n6. gRefCOCO val 结果：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/96c2e783-17da-462e-a7cf-937555e26c90\" width=\"70%\"/>\n</div>\n\n## 模型训练\n\n如果想复现我们的结果，你可以在准备好数据集后，直接通过如下命令进行训练\n\n```shell\n# 单机 8 卡训练仅包括 obj365v1 数据集\n./tools/dist_train.sh configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365.py 8\n# 单机 8 卡训练包括 obj365v1/goldg/grit/v3det 数据集，其余数据集类似\n./tools/dist_train.sh configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det.py 8\n```\n\n多机训练的用法请参考 [train.md](../../docs/zh_cn/user_guides/train.md)。MM-Grounding-DINO T 模型默认采用的是 32 张 3090Ti，如果你的总 bs 数不是 32x4=128，那么你需要手动的线性调整学习率。\n\n### 预训练自定义格式说明\n\n为了统一不同数据集的预训练格式，我们参考 [Open-GroundingDino](https://github.com/longzw1997/Open-GroundingDino) 所设计的格式。具体来说分成 2 种格式\n\n**(1) 目标检测数据格式 OD**\n\n```text\n{\"filename\": \"obj365_train_000000734304.jpg\",\n \"height\": 512,\n \"width\": 769,\n \"detection\": {\n    \"instances\": [\n          {\"bbox\": [109.4768676992, 346.0190429696, 135.1918335098, 365.3641967616], \"label\": 2, \"category\": \"chair\"},\n          {\"bbox\": [58.612365705900004, 323.2281494016, 242.6005859067, 451.4166870016], \"label\": 8, \"category\": \"car\"}\n                ]\n      }\n}\n```\n\nlabel字典中所对应的数值需要和相应的 label_map 一致。 instances 列表中的每一项都对应一个 bbox (x1y1x2y2 格式)。\n\n**(2) phrase grounding 数据格式 VG**\n\n```text\n{\"filename\": \"2405116.jpg\",\n \"height\": 375,\n \"width\": 500,\n \"grounding\":\n     {\"caption\": \"Two surfers walking down the shore. sand on the beach.\",\n      \"regions\": [\n            {\"bbox\": [206, 156, 282, 248], \"phrase\": \"Two surfers\", \"tokens_positive\": [[0, 3], [4, 11]]},\n            {\"bbox\": [303, 338, 443, 343], \"phrase\": \"sand\", \"tokens_positive\": [[36, 40]]},\n            {\"bbox\": [[327, 223, 421, 282], [300, 200, 400, 210]], \"phrase\": \"beach\", \"tokens_positive\": [[48, 53]]}\n               ]\n      }\n```\n\ntokens_positive 表示当前 phrase 在 caption 中的字符位置。\n\n## 自定义数据集微调训练案例\n\n为了方便用户针对自定义数据集进行下游微调，我们特意提供了以简单的 cat 数据集为例的微调训练案例。\n\n### 1 数据准备\n\n```shell\ncd mmdetection\nwget https://download.openmmlab.com/mmyolo/data/cat_dataset.zip\nunzip cat_dataset.zip -d data/cat/\n```\n\ncat 数据集是一个单类别数据集，包含 144 张图片，已经转换为 coco 格式。\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/25873202/205423220-c4b8f2fd-22ba-4937-8e47-1b3f6a8facd8.png\" alt=\"cat dataset\"/>\n</div>\n\n### 2 配置准备\n\n由于 cat 数据集的简单性和数量较少，我们使用 8 卡训练 20 个 epoch，相应的缩放学习率，不训练语言模型，只训练视觉模型。\n\n详细的配置信息可以在 [grounding_dino_swin-t_finetune_8xb4_20e_cat](grounding_dino_swin-t_finetune_8xb4_20e_cat.py) 中找到。\n\n### 3 可视化和 Zero-Shot 评估\n\n由于 MM Grounding DINO 是一个开放的检测模型，所以即使没有在 cat 数据集上训练，也可以进行检测和评估。\n\n单张图片的可视化结果如下：\n\n```shell\ncd mmdetection\npython demo/image_demo.py data/cat/images/IMG_20211205_120756.jpg configs/mm_grounding_dino/grounding_dino_swin-t_finetune_8xb4_20e_cat.py --weights grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth --texts cat.\n```\n\n测试集上的 Zero-Shot 评估结果如下：\n\n```shell\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_finetune_8xb4_20e_cat.py grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n```\n\n```text\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.881\n Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 1.000\n Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.929\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.881\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.913\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.913\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.913\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.913\n```\n\n### 4 模型训练\n\n```shell\n./tools/dist_train.sh configs/mm_grounding_dino/grounding_dino_swin-t_finetune_8xb4_20e_cat.py 8 --work-dir cat_work_dir\n```\n\n模型将会保存性能最佳的模型。在第 16 epoch 时候达到最佳，性能如下所示：\n\n```text\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.901\n Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 1.000\n Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.930\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.901\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.967\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.967\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.967\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = -1.000\n Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.967\n```\n\n我们可以发现，经过微调训练后，cat 数据集的训练性能从 88.1 提升到了 90.1。同时由于数据集比较小，评估指标波动比较大。\n\n## 模型自训练伪标签迭代生成和优化 pipeline\n\n为了方便用户从头构建自己的数据集或者希望利用模型推理能力进行自举式伪标签迭代生成和优化，不断修改伪标签来提升模型性能，我们特意提供了相关的 pipeline。\n\n由于我们定义了两种数据格式，为了演示我们也将分别进行说明。\n\n### 1 目标检测格式\n\n此处我们依然采用上述的 cat 数据集为例，假设我们目前只有一系列图片和预定义的类别，并不存在标注。\n\n1. 生成初始 odvg 格式文件\n\n```python\nimport os\nimport cv2\nimport json\nimport jsonlines\n\ndata_root = 'data/cat'\nimages_path = os.path.join(data_root, 'images')\nout_path = os.path.join(data_root, 'cat_train_od.json')\nmetas = []\nfor files in os.listdir(images_path):\n    img = cv2.imread(os.path.join(images_path, files))\n    height, width, _ = img.shape\n    metas.append({\"filename\": files, \"height\": height, \"width\": width})\n\nwith jsonlines.open(out_path, mode='w') as writer:\n    writer.write_all(metas)\n\n# 生成 label_map.json，由于只有一个类别，所以只需要写一个 cat 即可\nlabel_map_path = os.path.join(data_root, 'cat_label_map.json')\nwith open(label_map_path, 'w') as f:\n    json.dump({'0': 'cat'}, f)\n```\n\n会在 `data/cat` 目录下生成 `cat_train_od.json` 和 `cat_label_map.json` 两个文件。\n\n2. 使用预训练模型进行推理，并保存结果\n\n我们提供了直接可用的 [配置](grounding_dino_swin-t_pretrain_pseudo-labeling_cat.py), 如果你是其他数据集可以参考这个配置进行修改。\n\n```shell\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_pseudo-labeling_cat.py \\\n    grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n```\n\n会在 `data/cat` 目录下新生成 `cat_train_od_v1.json` 文件，你可以手动打开确认或者使用 [脚本](../../tools/analysis_tools/browse_grounding_raw.py) 可视化效果\n\n```shell\npython tools/analysis_tools/browse_grounding_raw.py data/cat/ cat_train_od_v1.json images --label-map-file cat_label_map.json -o your_output_dir --not-show\n```\n\n会在 your_output_dir 目录下生成可视化结果\n\n3. 继续训练提高性能\n\n在得到伪标签后，你可以混合一些预训练数据联合进行继续预训练，提升模型在当前数据集上的性能，然后重新运行 2 步骤，得到更准确的伪标签，如此循环迭代即可。\n\n### 2 Phrase Grounding 格式\n\n1. 生成初始 odvg 格式文件\n\nPhrase Grounding 的自举流程要求初始时候提供每张图片对应的 caption 和提前切割好的 phrase 信息。以 flickr30k entities 图片为例，生成的典型的文件应该如下所示：\n\n```text\n[\n{\"filename\": \"3028766968.jpg\",\n \"height\": 375,\n \"width\": 500,\n \"grounding\":\n     {\"caption\": \"Man with a black shirt on sit behind a desk sorting threw a giant stack of people work with a smirk on his face .\",\n      \"regions\": [\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a giant stack of people\", \"tokens_positive\": [[58, 81]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a black shirt\", \"tokens_positive\": [[9, 22]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a desk\", \"tokens_positive\": [[37, 43]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"his face\", \"tokens_positive\": [[103, 111]]},\n                 {\"bbox\": [0, 0, 1, 1], \"phrase\": \"Man\", \"tokens_positive\": [[0, 3]]}]}}\n{\"filename\": \"6944134083.jpg\",\n \"height\": 319,\n \"width\": 500,\n \"grounding\":\n    {\"caption\": \"Two men are competing in a horse race .\",\n    \"regions\": [\n                {\"bbox\": [0, 0, 1, 1], \"phrase\": \"Two men\", \"tokens_positive\": [[0, 7]]}]}}\n]\n```\n\n初始时候 bbox 必须要设置为 `[0, 0, 1, 1]`，因为这能确保程序正常运行，但是 bbox 的值并不会被使用。\n\n```text\n{\"filename\": \"3028766968.jpg\", \"height\": 375, \"width\": 500, \"grounding\": {\"caption\": \"Man with a black shirt on sit behind a desk sorting threw a giant stack of people work with a smirk on his face .\", \"regions\": [{\"bbox\": [0, 0, 1, 1], \"phrase\": \"a giant stack of people\", \"tokens_positive\": [[58, 81]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a black shirt\", \"tokens_positive\": [[9, 22]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"a desk\", \"tokens_positive\": [[37, 43]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"his face\", \"tokens_positive\": [[103, 111]]}, {\"bbox\": [0, 0, 1, 1], \"phrase\": \"Man\", \"tokens_positive\": [[0, 3]]}]}}\n{\"filename\": \"6944134083.jpg\", \"height\": 319, \"width\": 500, \"grounding\": {\"caption\": \"Two men are competing in a horse race .\", \"regions\": [{\"bbox\": [0, 0, 1, 1], \"phrase\": \"Two men\", \"tokens_positive\": [[0, 7]]}]}}\n```\n\n你可直接复制上面的文本，并假设将文本内容粘贴到命名为 `flickr_simple_train_vg.json` 文件中，并放置于提前准备好的 `data/flickr30k_entities` 数据集目录下，具体见数据准备文档。\n\n2. 使用预训练模型进行推理，并保存结果\n\n我们提供了直接可用的 [配置](grounding_dino_swin-t_pretrain_pseudo-labeling_flickr30k.py), 如果你是其他数据集可以参考这个配置进行修改。\n\n```shell\npython tools/test.py configs/mm_grounding_dino/grounding_dino_swin-t_pretrain_pseudo-labeling_flickr30k.py \\\n    grounding_dino_swin-t_pretrain_obj365_goldg_grit9m_v3det_20231204_095047-b448804b.pth\n```\n\n会在 `data/flickr30k_entities` 目录下新生成 `flickr_simple_train_vg_v1.json` 文件，你可以手动打开确认或者使用 [脚本](../../tools/analysis_tools/browse_grounding_raw.py) 可视化效果\n\n```shell\npython tools/analysis_tools/browse_grounding_raw.py data/flickr30k_entities/ flickr_simple_train_vg_v1.json flickr30k_images -o your_output_dir --not-show\n```\n\n会在 `your_output_dir` 目录下生成可视化结果，如下图所示：\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/a1c72d52-fa52-4ebe-b793-716d34e7b83f\" width=\"50%\"/>\n</div>\n\n3. 继续训练提高性能\n\n在得到伪标签后，你可以混合一些预训练数据联合进行继续预训练，提升模型在当前数据集上的性能，然后重新运行 2 步骤，得到更准确的伪标签，如此循环迭代即可。\n"
  },
  {
    "path": "configs/ms_rcnn/README.md",
    "content": "# MS R-CNN\n\n> [Mask Scoring R-CNN](https://arxiv.org/abs/1903.00241)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nLetting a deep network be aware of the quality of its own predictions is an interesting yet important problem. In the task of instance segmentation, the confidence of instance classification is used as mask quality score in most instance segmentation frameworks. However, the mask quality, quantified as the IoU between the instance mask and its ground truth, is usually not well correlated with classification score. In this paper, we study this problem and propose Mask Scoring R-CNN which contains a network block to learn the quality of the predicted instance masks. The proposed network block takes the instance feature and the corresponding predicted mask together to regress the mask IoU. The mask scoring strategy calibrates the misalignment between mask quality and mask score, and improves instance segmentation performance by prioritizing more accurate mask predictions during COCO AP evaluation. By extensive evaluations on the COCO dataset, Mask Scoring R-CNN brings consistent and noticeable gain with different models, and outperforms the state-of-the-art Mask R-CNN. We hope our simple and effective approach will provide a new direction for improving instance segmentation.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143967239-3a95ae92-6443-4181-9cbc-dfe16e81b969.png\"/>\n</div>\n\n## Results and Models\n\n|   Backbone   |  style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                    Config                     |                                                                                                                                                                      Download                                                                                                                                                                       |\n| :----------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50-FPN   |  caffe  |   1x    |   4.5    |                |  38.2  |  36.0   | [config](./ms-rcnn_r50-caffe_fpn_1x_coco.py)  |                  [model](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r50_caffe_fpn_1x_coco/ms_rcnn_r50_caffe_fpn_1x_coco_20200702_180848-61c9355e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r50_caffe_fpn_1x_coco/ms_rcnn_r50_caffe_fpn_1x_coco_20200702_180848.log.json)                   |\n|   R-50-FPN   |  caffe  |   2x    |    -     |       -        |  38.8  |  36.3   | [config](./ms-rcnn_r50-caffe_fpn_2x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r50_caffe_fpn_2x_coco/ms_rcnn_r50_caffe_fpn_2x_coco_bbox_mAP-0.388__segm_mAP-0.363_20200506_004738-ee87b137.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r50_caffe_fpn_2x_coco/ms_rcnn_r50_caffe_fpn_2x_coco_20200506_004738.log.json)   |\n|  R-101-FPN   |  caffe  |   1x    |   6.5    |                |  40.4  |  37.6   | [config](./ms-rcnn_r101-caffe_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r101_caffe_fpn_1x_coco/ms_rcnn_r101_caffe_fpn_1x_coco_bbox_mAP-0.404__segm_mAP-0.376_20200506_004755-b9b12a37.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r101_caffe_fpn_1x_coco/ms_rcnn_r101_caffe_fpn_1x_coco_20200506_004755.log.json) |\n|  R-101-FPN   |  caffe  |   2x    |    -     |       -        |  41.1  |  38.1   | [config](./ms-rcnn_r101-caffe_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r101_caffe_fpn_2x_coco/ms_rcnn_r101_caffe_fpn_2x_coco_bbox_mAP-0.411__segm_mAP-0.381_20200506_011134-5f3cc74f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r101_caffe_fpn_2x_coco/ms_rcnn_r101_caffe_fpn_2x_coco_20200506_011134.log.json) |\n| R-X101-32x4d | pytorch |   2x    |   7.9    |      11.0      |  41.8  |  38.7   | [config](./ms-rcnn_x101-32x4d_fpn_1x_coco.py) |                    [model](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_32x4d_fpn_1x_coco/ms_rcnn_x101_32x4d_fpn_1x_coco_20200206-81fd1740.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_32x4d_fpn_1x_coco/ms_rcnn_x101_32x4d_fpn_1x_coco_20200206_100113.log.json)                    |\n| R-X101-64x4d | pytorch |   1x    |   11.0   |      8.0       |  43.0  |  39.5   | [config](./ms-rcnn_x101-64x4d_fpn_1x_coco.py) |                    [model](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_64x4d_fpn_1x_coco/ms_rcnn_x101_64x4d_fpn_1x_coco_20200206-86ba88d2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_64x4d_fpn_1x_coco/ms_rcnn_x101_64x4d_fpn_1x_coco_20200206_091744.log.json)                    |\n| R-X101-64x4d | pytorch |   2x    |   11.0   |      8.0       |  42.6  |  39.5   | [config](./ms-rcnn_x101-64x4d_fpn_2x_coco.py) |                    [model](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_64x4d_fpn_2x_coco/ms_rcnn_x101_64x4d_fpn_2x_coco_20200308-02a445e2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_64x4d_fpn_2x_coco/ms_rcnn_x101_64x4d_fpn_2x_coco_20200308_012247.log.json)                    |\n\n## Citation\n\n```latex\n@inproceedings{huang2019msrcnn,\n    title={Mask Scoring R-CNN},\n    author={Zhaojin Huang and Lichao Huang and Yongchao Gong and Chang Huang and Xinggang Wang},\n    booktitle={IEEE Conference on Computer Vision and Pattern Recognition},\n    year={2019},\n}\n```\n"
  },
  {
    "path": "configs/ms_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Mask Scoring R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RPN\n        - FPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/abs/1903.00241\n      Title: 'Mask Scoring R-CNN'\n    README: configs/ms_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/mask_scoring_rcnn.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: ms-rcnn_r50-caffe_fpn_1x_coco\n    In Collection: Mask Scoring R-CNN\n    Config: configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.5\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r50_caffe_fpn_1x_coco/ms_rcnn_r50_caffe_fpn_1x_coco_20200702_180848-61c9355e.pth\n\n  - Name: ms-rcnn_r50-caffe_fpn_2x_coco\n    In Collection: Mask Scoring R-CNN\n    Config: configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r50_caffe_fpn_2x_coco/ms_rcnn_r50_caffe_fpn_2x_coco_bbox_mAP-0.388__segm_mAP-0.363_20200506_004738-ee87b137.pth\n\n  - Name: ms-rcnn_r101-caffe_fpn_1x_coco\n    In Collection: Mask Scoring R-CNN\n    Config: configs/ms_rcnn/ms-rcnn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.5\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r101_caffe_fpn_1x_coco/ms_rcnn_r101_caffe_fpn_1x_coco_bbox_mAP-0.404__segm_mAP-0.376_20200506_004755-b9b12a37.pth\n\n  - Name: ms-rcnn_r101-caffe_fpn_2x_coco\n    In Collection: Mask Scoring R-CNN\n    Config: configs/ms_rcnn/ms-rcnn_r101-caffe_fpn_2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_r101_caffe_fpn_2x_coco/ms_rcnn_r101_caffe_fpn_2x_coco_bbox_mAP-0.411__segm_mAP-0.381_20200506_011134-5f3cc74f.pth\n\n  - Name: ms-rcnn_x101-32x4d_fpn_1x_coco\n    In Collection: Mask Scoring R-CNN\n    Config: configs/ms_rcnn/ms-rcnn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.9\n      inference time (ms/im):\n        - value: 90.91\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_32x4d_fpn_1x_coco/ms_rcnn_x101_32x4d_fpn_1x_coco_20200206-81fd1740.pth\n\n  - Name: ms-rcnn_x101-64x4d_fpn_1x_coco\n    In Collection: Mask Scoring R-CNN\n    Config: configs/ms_rcnn/ms-rcnn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 11.0\n      inference time (ms/im):\n        - value: 125\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_64x4d_fpn_1x_coco/ms_rcnn_x101_64x4d_fpn_1x_coco_20200206-86ba88d2.pth\n\n  - Name: ms-rcnn_x101-64x4d_fpn_2x_coco\n    In Collection: Mask Scoring R-CNN\n    Config: configs/ms_rcnn/ms-rcnn_x101-64x4d_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 11.0\n      inference time (ms/im):\n        - value: 125\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ms_rcnn/ms_rcnn_x101_64x4d_fpn_2x_coco/ms_rcnn_x101_64x4d_fpn_2x_coco_20200308-02a445e2.pth\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './ms-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_r101-caffe_fpn_2x_coco.py",
    "content": "_base_ = './ms-rcnn_r101-caffe_fpn_1x_coco.py'\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    type='MaskScoringRCNN',\n    roi_head=dict(\n        type='MaskScoringRoIHead',\n        mask_iou_head=dict(\n            type='MaskIoUHead',\n            num_convs=4,\n            num_fcs=2,\n            roi_feat_size=14,\n            in_channels=256,\n            conv_out_channels=256,\n            fc_out_channels=1024,\n            num_classes=80)),\n    # model training and testing settings\n    train_cfg=dict(rcnn=dict(mask_thr_binary=0.5)))\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_r50-caffe_fpn_2x_coco.py",
    "content": "_base_ = './ms-rcnn_r50-caffe_fpn_1x_coco.py'\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    type='MaskScoringRCNN',\n    roi_head=dict(\n        type='MaskScoringRoIHead',\n        mask_iou_head=dict(\n            type='MaskIoUHead',\n            num_convs=4,\n            num_fcs=2,\n            roi_feat_size=14,\n            in_channels=256,\n            conv_out_channels=256,\n            fc_out_channels=1024,\n            num_classes=80)),\n    # model training and testing settings\n    train_cfg=dict(rcnn=dict(mask_thr_binary=0.5)))\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './ms-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './ms-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/ms_rcnn/ms-rcnn_x101-64x4d_fpn_2x_coco.py",
    "content": "_base_ = './ms-rcnn_x101-64x4d_fpn_1x_coco.py'\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/nas_fcos/README.md",
    "content": "# NAS-FCOS\n\n> [NAS-FCOS: Fast Neural Architecture Search for Object Detection](https://arxiv.org/abs/1906.04423)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe success of deep neural networks relies on significant architecture engineering. Recently neural architecture search (NAS) has emerged as a promise to greatly reduce manual effort in network design by automatically searching for optimal architectures, although typically such algorithms need an excessive amount of computational resources, e.g., a few thousand GPU-days. To date, on challenging vision tasks such as object detection, NAS, especially fast versions of NAS, is less studied. Here we propose to search for the decoder structure of object detectors with search efficiency being taken into consideration. To be more specific, we aim to efficiently search for the feature pyramid network (FPN) as well as the prediction head of a simple anchor-free object detector, namely FCOS, using a tailored reinforcement learning paradigm. With carefully designed search space, search algorithms and strategies for evaluating network quality, we are able to efficiently search a top-performing detection architecture within 4 days using 8 V100 GPUs. The discovered architecture surpasses state-of-the-art object detection models (such as Faster R-CNN, RetinaNet and FCOS) by 1.5 to 3.5 points in AP on the COCO dataset, with comparable computation complexity and memory footprint, demonstrating the efficacy of the proposed NAS for object detection.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143967900-1c8a65b9-c58d-4b03-8900-96af8f9768e8.png\"/>\n</div>\n\n## Results and Models\n\n|     Head     | Backbone | Style | GN-head | Lr schd | Mem (GB) | Inf time (fps) | box AP |                               Config                                |                                                                                                                                                                                           Download                                                                                                                                                                                           |\n| :----------: | :------: | :---: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| NAS-FCOSHead |   R-50   | caffe |    Y    |   1x    |          |                |  39.4  | [config](./nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/nas_fcos/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200520-1bdba3ce.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/nas_fcos/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200520.log.json)   |\n|   FCOSHead   |   R-50   | caffe |    Y    |   1x    |          |                |  38.5  | [config](./nas-fcos_r50-caffe_fpn_fcoshead-gn-head_4xb4-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/nas_fcos/nas_fcos_fcoshead_r50_caffe_fpn_gn-head_4x4_1x_coco/nas_fcos_fcoshead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200521-7fdcbce0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/nas_fcos/nas_fcos_fcoshead_r50_caffe_fpn_gn-head_4x4_1x_coco/nas_fcos_fcoshead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200521.log.json) |\n\n**Notes:**\n\n- To be consistent with the author's implementation, we use 4 GPUs with 4 images/GPU.\n\n## Citation\n\n```latex\n@article{wang2019fcos,\n  title={Nas-fcos: Fast neural architecture search for object detection},\n  author={Wang, Ning and Gao, Yang and Chen, Hao and Wang, Peng and Tian, Zhi and Shen, Chunhua},\n  journal={arXiv preprint arXiv:1906.04423},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/nas_fcos/metafile.yml",
    "content": "Collections:\n  - Name: NAS-FCOS\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 4x V100 GPUs\n      Architecture:\n        - FPN\n        - NAS-FCOS\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1906.04423\n      Title: 'NAS-FCOS: Fast Neural Architecture Search for Object Detection'\n    README: configs/nas_fcos/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/detectors/nasfcos.py#L6\n      Version: v2.1.0\n\nModels:\n  - Name: nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco\n    In Collection: NAS-FCOS\n    Config: configs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/nas_fcos/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco/nas_fcos_nashead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200520-1bdba3ce.pth\n\n  - Name: nas-fcos_r50-caffe_fpn_fcoshead-gn-head_4xb4-1x_coco\n    In Collection: NAS-FCOS\n    Config: configs/nas_fcos/nas-fcos_r50-caffe_fpn_fcoshead-gn-head_4xb4-1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/nas_fcos/nas_fcos_fcoshead_r50_caffe_fpn_gn-head_4x4_1x_coco/nas_fcos_fcoshead_r50_caffe_fpn_gn-head_4x4_1x_coco_20200521-7fdcbce0.pth\n"
  },
  {
    "path": "configs/nas_fcos/nas-fcos_r50-caffe_fpn_fcoshead-gn-head_4xb4-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\nmodel = dict(\n    type='NASFCOS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False, eps=0),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    neck=dict(\n        type='NASFCOS_FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs=True,\n        num_outs=5,\n        norm_cfg=dict(type='BN'),\n        conv_cfg=dict(type='DCNv2', deform_groups=2)),\n    bbox_head=dict(\n        type='FCOSHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        norm_cfg=dict(type='GN', num_groups=32),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# dataset settings\ntrain_dataloader = dict(batch_size=4, num_workers=2)\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.01),\n    paramwise_cfg=dict(bias_lr_mult=2., bias_decay_mult=0.))\n"
  },
  {
    "path": "configs/nas_fcos/nas-fcos_r50-caffe_fpn_nashead-gn-head_4xb4-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\nmodel = dict(\n    type='NASFCOS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False, eps=0),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    neck=dict(\n        type='NASFCOS_FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs=True,\n        num_outs=5,\n        norm_cfg=dict(type='BN'),\n        conv_cfg=dict(type='DCNv2', deform_groups=2)),\n    bbox_head=dict(\n        type='NASFCOSHead',\n        num_classes=80,\n        in_channels=256,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        norm_cfg=dict(type='GN', num_groups=32),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# dataset settings\ntrain_dataloader = dict(batch_size=4, num_workers=2)\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.01),\n    paramwise_cfg=dict(bias_lr_mult=2., bias_decay_mult=0.))\n"
  },
  {
    "path": "configs/nas_fpn/README.md",
    "content": "# NAS-FPN\n\n> [NAS-FPN: Learning Scalable Feature Pyramid Architecture for Object Detection](https://arxiv.org/abs/1904.07392)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nCurrent state-of-the-art convolutional architectures for object detection are manually designed. Here we aim to learn a better architecture of feature pyramid network for object detection. We adopt Neural Architecture Search and discover a new feature pyramid architecture in a novel scalable search space covering all cross-scale connections. The discovered architecture, named NAS-FPN, consists of a combination of top-down and bottom-up connections to fuse features across scales. NAS-FPN, combined with various backbone models in the RetinaNet framework, achieves better accuracy and latency tradeoff compared to state-of-the-art object detection models. NAS-FPN improves mobile detection accuracy by 2 AP compared to state-of-the-art SSDLite with MobileNetV2 model in \\[32\\] and achieves 48.3 AP which surpasses Mask R-CNN \\[10\\] detection accuracy with less computation time.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143968037-cedd76e9-1ae7-4869-bd34-c9d8611d630c.png\"/>\n</div>\n\n## Results and Models\n\nWe benchmark the new training schedule (crop training, large batch, unfrozen BN, 50 epochs) introduced in NAS-FPN. RetinaNet is used in the paper.\n\n|  Backbone   | Lr schd | Mem (GB) | Inf time (fps) | box AP |                        Config                        |                                                                                                                                                             Download                                                                                                                                                             |\n| :---------: | :-----: | :------: | :------------: | :----: | :--------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  R-50-FPN   |   50e   |   12.9   |      22.9      |  37.9  |  [config](./retinanet_r50_fpn_crop640-50e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/nas_fpn/retinanet_r50_fpn_crop640_50e_coco/retinanet_r50_fpn_crop640_50e_coco-9b953d76.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/nas_fpn/retinanet_r50_fpn_crop640_50e_coco/retinanet_r50_fpn_crop640_50e_coco_20200529_095329.log.json)       |\n| R-50-NASFPN |   50e   |   13.2   |      23.0      |  40.5  | [config](./retinanet_r50_nasfpn_crop640-50e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/nas_fpn/retinanet_r50_nasfpn_crop640_50e_coco/retinanet_r50_nasfpn_crop640_50e_coco-0ad1f644.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/nas_fpn/retinanet_r50_nasfpn_crop640_50e_coco/retinanet_r50_nasfpn_crop640_50e_coco_20200528_230008.log.json) |\n\n**Note**: We find that it is unstable to train NAS-FPN and there is a small chance that results can be 3% mAP lower.\n\n## Citation\n\n```latex\n@inproceedings{ghiasi2019fpn,\n  title={Nas-fpn: Learning scalable feature pyramid architecture for object detection},\n  author={Ghiasi, Golnaz and Lin, Tsung-Yi and Le, Quoc V},\n  booktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition},\n  pages={7036--7045},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/nas_fpn/metafile.yml",
    "content": "Collections:\n  - Name: NAS-FPN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - NAS-FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1904.07392\n      Title: 'NAS-FPN: Learning Scalable Feature Pyramid Architecture for Object Detection'\n    README: configs/nas_fpn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/necks/nas_fpn.py#L67\n      Version: v2.0.0\n\nModels:\n  - Name: retinanet_r50_fpn_crop640-50e_coco\n    In Collection: NAS-FPN\n    Config: configs/nas_fpn/retinanet_r50_fpn_crop640-50e_coco.py\n    Metadata:\n      Training Memory (GB): 12.9\n      inference time (ms/im):\n        - value: 43.67\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/nas_fpn/retinanet_r50_fpn_crop640_50e_coco/retinanet_r50_fpn_crop640_50e_coco-9b953d76.pth\n\n  - Name: retinanet_r50_nasfpn_crop640-50e_coco\n    In Collection: NAS-FPN\n    Config: configs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py\n    Metadata:\n      Training Memory (GB): 13.2\n      inference time (ms/im):\n        - value: 43.48\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 50\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/nas_fpn/retinanet_r50_nasfpn_crop640_50e_coco/retinanet_r50_nasfpn_crop640_50e_coco-0ad1f644.pth\n"
  },
  {
    "path": "configs/nas_fpn/retinanet_r50_fpn_crop640-50e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nnorm_cfg = dict(type='BN', requires_grad=True)\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=64,\n        batch_augments=[dict(type='BatchFixedSizePad', size=(640, 640))]),\n    backbone=dict(norm_eval=False),\n    neck=dict(\n        relu_before_extra_convs=True,\n        no_norm_on_lateral=True,\n        norm_cfg=norm_cfg),\n    bbox_head=dict(type='RetinaSepBNHead', num_ins=5, norm_cfg=norm_cfg),\n    # training and testing settings\n    train_cfg=dict(assigner=dict(neg_iou_thr=0.5)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(640, 640),\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(640, 640)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(640, 640), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=8, num_workers=4, dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# training schedule for 50e\nmax_epochs = 50\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[30, 40],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.08, momentum=0.9, weight_decay=0.0001),\n    paramwise_cfg=dict(norm_decay_mult=0, bypass_duplicate=True))\n\nenv_cfg = dict(cudnn_benchmark=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/nas_fpn/retinanet_r50_nasfpn_crop640-50e_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_crop640-50e_coco.py'\n\n# model settings\nmodel = dict(\n    # `pad_size_divisor=128` ensures the feature maps sizes\n    # in `NAS_FPN` won't mismatch.\n    data_preprocessor=dict(pad_size_divisor=128),\n    neck=dict(\n        _delete_=True,\n        type='NASFPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5,\n        stack_times=7,\n        start_level=1,\n        norm_cfg=dict(type='BN', requires_grad=True)))\n"
  },
  {
    "path": "configs/objects365/README.md",
    "content": "# Objects365 Dataset\n\n> [Objects365 Dataset](https://openaccess.thecvf.com/content_ICCV_2019/papers/Shao_Objects365_A_Large-Scale_High-Quality_Dataset_for_Object_Detection_ICCV_2019_paper.pdf)\n\n<!-- [DATASET] -->\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\n#### Objects365 Dataset V1\n\n[Objects365 Dataset V1](http://www.objects365.org/overview.html) is a brand new dataset,\ndesigned to spur object detection research with a focus on diverse objects in the Wild.\nIt has 365 object categories over 600K training images. More than 10 million, high-quality bounding boxes are manually labeled through a three-step, carefully designed annotation pipeline. It is the largest object detection dataset (with full annotation) so far and establishes a more challenging benchmark for the community. Objects365 can serve as a better feature learning dataset for localization-sensitive tasks like object detection\nand semantic segmentation.\n\n<!-- [IMAGE] -->\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/48282753/208368046-b7573022-06c9-4a99-af17-a6ac7407e3d8.png\" height=\"400\"/>\n</div>\n\n#### Objects365 Dataset V2\n\n[Objects365 Dataset V2](http://www.objects365.org/overview.html) is based on the V1 release of the Objects365 dataset.\nObjects 365 annotated 365 object classes on more than 1800k images, with more than 29 million bounding boxes in the training set, surpassing PASCAL VOC, ImageNet, and COCO datasets.\nObjects 365 includes 11 categories of people, clothing, living room, bathroom, kitchen, office/medical, electrical appliances, transportation, food, animals, sports/musical instruments, and each category has dozens of subcategories.\n\n## Citation\n\n```\n@inproceedings{shao2019objects365,\n  title={Objects365: A large-scale, high-quality dataset for object detection},\n  author={Shao, Shuai and Li, Zeming and Zhang, Tianyuan and Peng, Chao and Yu, Gang and Zhang, Xiangyu and Li, Jing and Sun, Jian},\n  booktitle={Proceedings of the IEEE/CVF international conference on computer vision},\n  pages={8430--8439},\n  year={2019}\n}\n```\n\n## Prepare Dataset\n\n1. You need to download and extract Objects365 dataset. Users can download Objects365 V2 by using `tools/misc/download_dataset.py`.\n\n   **Usage**\n\n   ```shell\n   python tools/misc/download_dataset.py --dataset-name objects365v2 \\\n   --save-dir ${SAVING PATH} \\\n   --unzip \\\n   --delete  # Optional, delete the download zip file\n   ```\n\n   **Note:** There is no download link for Objects365 V1 right now. If you would like to download Objects365-V1, please visit [official website](http://www.objects365.org/) to concat the author.\n\n2. The directory should be like this:\n\n   ```none\n   mmdetection\n   ├── mmdet\n   ├── tools\n   ├── configs\n   ├── data\n   │   ├── Objects365\n   │   │   ├── Obj365_v1\n   │   │   │   ├── annotations\n   │   │   │   │   ├── objects365_train.json\n   │   │   │   │   ├── objects365_val.json\n   │   │   │   ├── train        # training images\n   │   │   │   ├── val          # validation images\n   │   │   ├── Obj365_v2\n   │   │   │   ├── annotations\n   │   │   │   │   ├── zhiyuan_objv2_train.json\n   │   │   │   │   ├── zhiyuan_objv2_val.json\n   │   │   │   ├── train        # training images\n   │   │   │   │   ├── patch0\n   │   │   │   │   ├── patch1\n   │   │   │   │   ├── ...\n   │   │   │   ├── val          # validation images\n   │   │   │   │   ├── patch0\n   │   │   │   │   ├── patch1\n   │   │   │   │   ├── ...\n   ```\n\n## Results and Models\n\n### Objects365 V1\n\n| Architecture | Backbone |  Style  | Lr schd | Mem (GB) | box AP |                                                              Config                                                               |                                                                                                                                                                                Download                                                                                                                                                                                |\n| :----------: | :------: | :-----: | :-----: | :------: | :----: | :-------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Faster R-CNN |   R-50   | pytorch |   1x    |    -     |  19.6  |   [config](https://github.com/open-mmlab/mmdetection/tree/main/configs/objects365/faster-rcnn_r50_fpn_16xb4-1x_objects365v1.py)   |           [model](https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_16x4_1x_obj365v1/faster_rcnn_r50_fpn_16x4_1x_obj365v1_20221219_181226-9ff10f95.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_16x4_1x_obj365v1/faster_rcnn_r50_fpn_16x4_1x_obj365v1_20221219_181226.log.json)           |\n| Faster R-CNN |   R-50   | pytorch |  1350K  |    -     |  22.3  | [config](https://github.com/open-mmlab/mmdetection/tree/main/configs/objects365/faster-rcnn_r50-syncbn_fpn_1350k_objects365v1.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_syncbn_1350k_obj365v1/faster_rcnn_r50_fpn_syncbn_1350k_obj365v1_20220510_142457-337d8965.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_syncbn_1350k_obj365v1/faster_rcnn_r50_fpn_syncbn_1350k_obj365v1_20220510_142457.log.json) |\n|  Retinanet   |   R-50   | pytorch |   1x    |    -     |  14.8  |       [config](https://github.com/open-mmlab/mmdetection/tree/main/configs/objects365/retinanet_r50_fpn_1x_objects365v1.py)       |                         [model](https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_1x_obj365v1/retinanet_r50_fpn_1x_obj365v1_20221219_181859-ba3e3dd5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_1x_obj365v1/retinanet_r50_fpn_1x_obj365v1_20221219_181859.log.json)                         |\n|  Retinanet   |   R-50   | pytorch |  1350K  |    -     |  18.0  |  [config](https://github.com/open-mmlab/mmdetection/tree/main/configs/objects365/retinanet_r50-syncbn_fpn_1350k_objects365v1.py)  |     [model](https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_syncbn_1350k_obj365v1/retinanet_r50_fpn_syncbn_1350k_obj365v1_20220513_111237-7517c576.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_syncbn_1350k_obj365v1/retinanet_r50_fpn_syncbn_1350k_obj365v1_20220513_111237.log.json)     |\n\n### Objects365 V2\n\n| Architecture | Backbone |  Style  | Lr schd | Mem (GB) | box AP |                                                            Config                                                             |                                                                                                                                                                      Download                                                                                                                                                                      |\n| :----------: | :------: | :-----: | :-----: | :------: | :----: | :---------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Faster R-CNN |   R-50   | pytorch |   1x    |    -     |  19.8  | [config](https://github.com/open-mmlab/mmdetection/tree/main/configs/objects365/faster-rcnn_r50_fpn_16xb4-1x_objects365v2.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_16x4_1x_obj365v2/faster_rcnn_r50_fpn_16x4_1x_obj365v2_20221220_175040-5910b015.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_16x4_1x_obj365v2/faster_rcnn_r50_fpn_16x4_1x_obj365v2_20221220_175040.log.json) |\n|  Retinanet   |   R-50   | pytorch |   1x    |    -     |  16.7  |     [config](https://github.com/open-mmlab/mmdetection/tree/main/configs/objects365/retinanet_r50_fpn_1x_objects365v2.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_1x_obj365v2/retinanet_r50_fpn_1x_obj365v2_20221223_122105-d9b191f1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_1x_obj365v2/retinanet_r50_fpn_1x_obj365v2_20221223_122105.log.json)               |\n"
  },
  {
    "path": "configs/objects365/faster-rcnn_r50-syncbn_fpn_1350k_objects365v1.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/objects365v2_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(norm_cfg=dict(type='SyncBN', requires_grad=True)),\n    roi_head=dict(bbox_head=dict(num_classes=365)))\n\n# training schedule for 1350K\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=1350000,  # 36 epochs\n    val_interval=150000)\n\n# Using 8 GPUS while training\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 1000,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=1350000,\n        by_epoch=False,\n        milestones=[900000, 1200000],\n        gamma=0.1)\n]\n\ntrain_dataloader = dict(sampler=dict(type='InfiniteSampler'))\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=150000))\n\nlog_processor = dict(by_epoch=False)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/objects365/faster-rcnn_r50_fpn_16xb4-1x_objects365v1.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/objects365v1_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=365)))\n\ntrain_dataloader = dict(\n    batch_size=4,  # using 16 GPUS while training. total batch size is 16 x 4)\n)\n\n# Using 32 GPUS while training\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.08, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 1000,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/objects365/faster-rcnn_r50_fpn_16xb4-1x_objects365v2.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/objects365v2_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=365)))\n\ntrain_dataloader = dict(\n    batch_size=4,  # using 16 GPUS while training. total batch size is 16 x 4)\n)\n\n# Using 32 GPUS while training\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.08, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 1000,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/objects365/metafile.yml",
    "content": "- Name: retinanet_r50_fpn_1x_objects365v1\n  In Collection: RetinaNet\n  Config: configs/objects365/retinanet_r50_fpn_1x_objects365v1.py\n  Metadata:\n    Training Memory (GB): 7.4\n    Epochs: 12\n    Training Data: Objects365 v1\n    Training Techniques:\n      - SGD with Momentum\n      - Weight Decay\n  Results:\n  - Task: Object Detection\n    Dataset: Objects365 v1\n    Metrics:\n      box AP: 14.8\n  Weights: https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_1x_obj365v1/retinanet_r50_fpn_1x_obj365v1_20221219_181859-ba3e3dd5.pth\n\n- Name: retinanet_r50-syncbn_fpn_1350k_objects365v1\n  In Collection: RetinaNet\n  Config: configs/objects365/retinanet_r50-syncbn_fpn_1350k_objects365v1.py\n  Metadata:\n    Training Memory (GB): 7.6\n    Iterations: 1350000\n    Training Data: Objects365 v1\n    Training Techniques:\n      - SGD with Momentum\n      - Weight Decay\n  Results:\n  - Task: Object Detection\n    Dataset: Objects365 v1\n    Metrics:\n      box AP: 18.0\n  Weights: https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_syncbn_1350k_obj365v1/retinanet_r50_fpn_syncbn_1350k_obj365v1_20220513_111237-7517c576.pth\n\n- Name: retinanet_r50_fpn_1x_objects365v2\n  In Collection: RetinaNet\n  Config: configs/objects365/retinanet_r50_fpn_1x_objects365v2.py\n  Metadata:\n    Training Memory (GB): 7.2\n    Epochs: 12\n    Training Data: Objects365 v2\n    Training Techniques:\n      - SGD with Momentum\n      - Weight Decay\n  Results:\n  - Task: Object Detection\n    Dataset: Objects365 v2\n    Metrics:\n      box AP: 16.7\n  Weights: https://download.openmmlab.com/mmdetection/v2.0/objects365/retinanet_r50_fpn_1x_obj365v2/retinanet_r50_fpn_1x_obj365v2_20221223_122105-d9b191f1.pth\n\n- Name: faster-rcnn_r50_fpn_16xb4-1x_objects365v1\n  In Collection: Faster R-CNN\n  Config: configs/objects365/faster-rcnn_r50_fpn_16xb4-1x_objects365v1.py\n  Metadata:\n    Training Memory (GB): 11.4\n    Epochs: 12\n    Training Data: Objects365 v1\n    Training Techniques:\n      - SGD with Momentum\n      - Weight Decay\n  Results:\n  - Task: Object Detection\n    Dataset: Objects365 v1\n    Metrics:\n      box AP: 19.6\n  Weights: https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_16x4_1x_obj365v1/faster_rcnn_r50_fpn_16x4_1x_obj365v1_20221219_181226-9ff10f95.pth\n\n- Name: faster-rcnn_r50-syncbn_fpn_1350k_objects365v1\n  In Collection: Faster R-CNN\n  Config: configs/objects365/faster-rcnn_r50-syncbn_fpn_1350k_objects365v1.py\n  Metadata:\n    Training Memory (GB): 8.6\n    Iterations: 1350000\n    Training Data: Objects365 v1\n    Training Techniques:\n      - SGD with Momentum\n      - Weight Decay\n  Results:\n  - Task: Object Detection\n    Dataset: Objects365 v1\n    Metrics:\n      box AP: 22.3\n  Weights: https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_syncbn_1350k_obj365v1/faster_rcnn_r50_fpn_syncbn_1350k_obj365v1_20220510_142457-337d8965.pth\n\n- Name: faster-rcnn_r50_fpn_16xb4-1x_objects365v2\n  In Collection: Faster R-CNN\n  Config: configs/objects365/faster-rcnn_r50_fpn_16xb4-1x_objects365v2.py\n  Metadata:\n    Training Memory (GB): 10.8\n    Epochs: 12\n    Training Data: Objects365 v1\n    Training Techniques:\n      - SGD with Momentum\n      - Weight Decay\n  Results:\n  - Task: Object Detection\n    Dataset: Objects365 v2\n    Metrics:\n      box AP: 19.8\n  Weights: https://download.openmmlab.com/mmdetection/v2.0/objects365/faster_rcnn_r50_fpn_16x4_1x_obj365v2/faster_rcnn_r50_fpn_16x4_1x_obj365v2_20221220_175040-5910b015.pth\n"
  },
  {
    "path": "configs/objects365/retinanet_r50-syncbn_fpn_1350k_objects365v1.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/objects365v2_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(norm_cfg=dict(type='SyncBN', requires_grad=True)),\n    bbox_head=dict(num_classes=365))\n\n# training schedule for 1350K\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=1350000,  # 36 epochs\n    val_interval=150000)\n\n# Using 8 GPUS while training\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 1000,\n        by_epoch=False,\n        begin=0,\n        end=10000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=1350000,\n        by_epoch=False,\n        milestones=[900000, 1200000],\n        gamma=0.1)\n]\n\ntrain_dataloader = dict(sampler=dict(type='InfiniteSampler'))\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=150000))\n\nlog_processor = dict(by_epoch=False)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/objects365/retinanet_r50_fpn_1x_objects365v1.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/objects365v1_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(bbox_head=dict(num_classes=365))\n\n# Using 8 GPUS while training\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 1000,\n        by_epoch=False,\n        begin=0,\n        end=10000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/objects365/retinanet_r50_fpn_1x_objects365v2.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/objects365v2_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(bbox_head=dict(num_classes=365))\n\n# Using 8 GPUS while training\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 1000,\n        by_epoch=False,\n        begin=0,\n        end=10000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/ocsort/README.md",
    "content": "# Observation-Centric SORT: Rethinking SORT for Robust Multi-Object Tracking\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nMulti-Object Tracking (MOT) has rapidly progressed with the development of object detection and re-identification. However, motion modeling, which facilitates object association by forecasting short-term trajec- tories with past observations, has been relatively under-explored in recent years. Current motion models in MOT typically assume that the object motion is linear in a small time window and needs continuous observations, so these methods are sensitive to occlusions and non-linear motion and require high frame-rate videos. In this work, we show that a simple motion model can obtain state-of-the-art tracking performance without other cues like appearance. We emphasize the role of “observation” when recovering tracks from being lost and reducing the error accumulated by linear motion models during the lost period. We thus name the proposed method as Observation-Centric SORT, OC-SORT for short. It remains simple, online, and real-time but improves robustness over occlusion and non-linear motion. It achieves 63.2 and 62.1 HOTA on MOT17 and MOT20, respectively, surpassing all published methods. It also sets new states of the art on KITTI Pedestrian Tracking and DanceTrack where the object motion is highly non-linear\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/17743251/168193097-b3ad1a94-b18c-4b14-b7b1-5f8c6ed842f0.png\"/>\n</div>\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@article{cao2022observation,\n  title={Observation-Centric SORT: Rethinking SORT for Robust Multi-Object Tracking},\n  author={Cao, Jinkun and Weng, Xinshuo and Khirodkar, Rawal and Pang, Jiangmiao and Kitani, Kris},\n  journal={arXiv preprint arXiv:2203.14360},\n  year={2022}\n}\n```\n\n## Results and models on MOT17\n\nThe performance on `MOT17-half-val` is comparable with the performance from [the OC-SORT official implementation](https://github.com/noahcao/OC_SORT). We use the same YOLO-X detector weights as in [ByteTrack](https://github.com/open-mmlab/mmtracking/tree/master/configs/mot/bytetrack).\n\n| Method  | Detector |        Train Set        | Test Set | Public | Inf time (fps) | HOTA | MOTA | IDF1 |  FP   |  FN   | IDSw. |                          Config                           |                                                                                                                                               Download                                                                                                                                               |\n| :-----: | :------: | :---------------------: | :------: | :----: | :------------: | :--: | :--: | :--: | :---: | :---: | :---: | :-------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| OC-SORT | YOLOX-X  | CrowdHuman + half-train | half-val |   N    |       -        | 67.5 | 77.5 | 78.2 | 15987 | 19590 |  855  | [config](ocsort_yolox_x_crowdhuman_mot17-private-half.py) | [model](https://download.openmmlab.com/mmtracking/mot/ocsort/mot_dataset/ocsort_yolox_x_crowdhuman_mot17-private-half_20220813_101618-fe150582.pth) \\| [log](https://download.openmmlab.com/mmtracking/mot/ocsort/mot_dataset/ocsort_yolox_x_crowdhuman_mot17-private-half_20220813_101618.log.json) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nOCSORT training is same as Bytetrack, please refer to [document](../../configs/bytetrack/README.md).\n\n### 4. Testing and evaluation\n\nOCSORT evaluation and test are same as Bytetrack, please refer to [document](../../configs/bytetrack/README.md).\n\n### 5.Inference\n\nOCSORT inference is same as Bytetrack, please refer to [document](../../configs/bytetrack/README.md).\n"
  },
  {
    "path": "configs/ocsort/metafile.yml",
    "content": "Collections:\n  - Name: OCSORT\n    Metadata:\n      Training Techniques:\n        - SGD with Momentum\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - YOLOX\n    Paper:\n        URL: https://arxiv.org/abs/2203.14360\n        Title: Observation-Centric SORT Rethinking SORT for Robust Multi-Object Tracking\n    README: configs/ocsort/README.md\n\nModels:\n  - Name: ocsort_yolox_x_crowdhuman_mot17-private-half\n    In Collection: OCSORT\n    Config: configs/ocsort/ocsort_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py\n    Metadata:\n      Training Data: CrowdHuman + MOT17-half-train\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT17-half-val\n        Metrics:\n          HOTA: 67.5\n          MOTA: 77.5\n          IDF1: 78.2\n    Weights: https://download.openmmlab.com/mmtracking/mot/ocsort/mot_dataset/ocsort_yolox_x_crowdhuman_mot17-private-half_20220813_101618-fe150582.pth\n"
  },
  {
    "path": "configs/ocsort/ocsort_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    '../bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py',  # noqa: E501\n]\n\nmodel = dict(\n    type='OCSORT',\n    tracker=dict(\n        _delete_=True,\n        type='OCSORTTracker',\n        motion=dict(type='KalmanFilter'),\n        obj_score_thr=0.3,\n        init_track_thr=0.7,\n        weight_iou_with_det_scores=True,\n        match_iou_thr=0.3,\n        num_tentatives=3,\n        vel_consist_weight=0.2,\n        vel_delta_t=3,\n        num_frames_retain=30))\n"
  },
  {
    "path": "configs/ocsort/ocsort_yolox_x_8xb4-amp-80e_crowdhuman-mot20train_test-mot20test.py",
    "content": "_base_ = [\n    '../bytetrack/bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py',  # noqa: E501\n]\n\nmodel = dict(\n    type='OCSORT',\n    tracker=dict(\n        _delete_=True,\n        type='OCSORTTracker',\n        motion=dict(type='KalmanFilter'),\n        obj_score_thr=0.3,\n        init_track_thr=0.7,\n        weight_iou_with_det_scores=True,\n        match_iou_thr=0.3,\n        num_tentatives=3,\n        vel_consist_weight=0.2,\n        vel_delta_t=3,\n        num_frames_retain=30))\n"
  },
  {
    "path": "configs/openimages/README.md",
    "content": "# Open Images Dataset\n\n> [Open Images Dataset](https://arxiv.org/abs/1811.00982)\n\n<!-- [DATASET] -->\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\n#### Open Images v6\n\n[Open Images](https://storage.googleapis.com/openimages/web/index.html) is a dataset of ~9M images annotated with image-level labels,\nobject bounding boxes, object segmentation masks, visual relationships,\nand localized narratives:\n\n- It contains a total of 16M bounding boxes for 600 object classes on\n  1.9M images, making it the largest existing dataset with object location\n  annotations. The boxes have been largely manually drawn by professional\n  annotators to ensure accuracy and consistency. The images are very diverse\n  and often contain complex scenes with several objects (8.3 per image on\n  average).\n\n- Open Images also offers visual relationship annotations, indicating pairs\n  of objects in particular relations (e.g. \"woman playing guitar\", \"beer on\n  table\"), object properties (e.g. \"table is wooden\"), and human actions (e.g.\n  \"woman is jumping\"). In total it has 3.3M annotations from 1,466 distinct\n  relationship triplets.\n\n- In V5 we added segmentation masks for 2.8M object instances in 350 classes.\n  Segmentation masks mark the outline of objects, which characterizes their\n  spatial extent to a much higher level of detail.\n\n- In V6 we added 675k localized narratives: multimodal descriptions of images\n  consisting of synchronized voice, text, and mouse traces over the objects being\n  described. (Note we originally launched localized narratives only on train in V6,\n  but since July 2020 we also have validation and test covered.)\n\n- Finally, the dataset is annotated with 59.9M image-level labels spanning 19,957\n  classes.\n\nWe believe that having a single dataset with unified annotations for image\nclassification, object detection, visual relationship detection, instance\nsegmentation, and multimodal image descriptions will enable to study these\ntasks jointly and stimulate progress towards genuine scene understanding.\n\n<!-- [IMAGE] -->\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/48282753/147199750-23e17230-c0cf-49a0-a13c-0d014d49107e.png\" height=\"400\"/>\n</div>\n\n#### Open Images Challenge 2019\n\n[Open Images Challenges 2019](https://storage.googleapis.com/openimages/web/challenge2019.html) is based on the V5 release of the Open\nImages dataset. The images of the dataset are very varied and\noften contain complex scenes with several objects (explore the dataset).\n\n## Citation\n\n```\n@article{OpenImages,\n  author = {Alina Kuznetsova and Hassan Rom and Neil Alldrin and Jasper Uijlings and Ivan Krasin and Jordi Pont-Tuset and Shahab Kamali and Stefan Popov and Matteo Malloci and Alexander Kolesnikov and Tom Duerig and Vittorio Ferrari},\n  title = {The Open Images Dataset V4: Unified image classification, object detection, and visual relationship detection at scale},\n  year = {2020},\n  journal = {IJCV}\n}\n```\n\n## Prepare Dataset\n\n1. You need to download and extract Open Images dataset.\n\n2. The Open Images dataset does not have image metas (width and height of the image),\n   which will be used during training and testing (evaluation). We suggest to get test image metas before\n   training/testing by using `tools/misc/get_image_metas.py`.\n\n   **Usage**\n\n   ```shell\n   python tools/misc/get_image_metas.py ${CONFIG} \\\n   --dataset ${DATASET TYPE} \\  # train or val or test\n   --out ${OUTPUT FILE NAME}\n   ```\n\n3. The directory should be like this:\n\n   ```none\n   mmdetection\n   ├── mmdet\n   ├── tools\n   ├── configs\n   ├── data\n   │   ├── OpenImages\n   │   │   ├── annotations\n   │   │   │   ├── bbox_labels_600_hierarchy.json\n   │   │   │   ├── class-descriptions-boxable.csv\n   │   │   │   ├── oidv6-train-annotations-bbox.scv\n   │   │   │   ├── validation-annotations-bbox.csv\n   │   │   │   ├── validation-annotations-human-imagelabels-boxable.csv\n   │   │   │   ├── validation-image-metas.pkl      # get from script\n   │   │   ├── challenge2019\n   │   │   │   ├── challenge-2019-train-detection-bbox.txt\n   │   │   │   ├── challenge-2019-validation-detection-bbox.txt\n   │   │   │   ├── class_label_tree.np\n   │   │   │   ├── class_sample_train.pkl\n   │   │   │   ├── challenge-2019-validation-detection-human-imagelabels.csv       # download from official website\n   │   │   │   ├── challenge-2019-validation-metas.pkl     # get from script\n   │   │   ├── OpenImages\n   │   │   │   ├── train           # training images\n   │   │   │   ├── test            # testing images\n   │   │   │   ├── validation      # validation images\n   ```\n\n**Note**:\n\n1. The training and validation images of Open Images Challenge dataset are based on\n   Open Images v6, but the test images are different.\n2. The Open Images Challenges annotations are obtained from [TSD](https://github.com/Sense-X/TSD).\n   You can also download the annotations from [official website](https://storage.googleapis.com/openimages/web/challenge2019_downloads.html),\n   and set data.train.type=OpenImagesDataset, data.val.type=OpenImagesDataset, and data.test.type=OpenImagesDataset in the config\n3. If users do not want to use `validation-annotations-human-imagelabels-boxable.csv` and `challenge-2019-validation-detection-human-imagelabels.csv`\n   users can set `test_dataloader.dataset.image_level_ann_file=None` and `test_dataloader.dataset.image_level_ann_file=None` in the config.\n   Please note that loading image-levels label is the default of Open Images evaluation metric.\n   More details please refer to the [official website](https://storage.googleapis.com/openimages/web/evaluation.html)\n\n## Results and Models\n\n|         Architecture          | Backbone |  Style  | Lr schd |       Sampler       | Mem (GB) | Inf time (fps) | box AP |                                Config                                |                                                                                                                                                                                                      Download                                                                                                                                                                                                      |\n| :---------------------------: | :------: | :-----: | :-----: | :-----------------: | :------: | :------------: | :----: | :------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|         Faster R-CNN          |   R-50   | pytorch |   1x    |    Group Sampler    |   7.7    |       -        |  51.6  |        [config](./faster-rcnn_r50_fpn_32xb2-1x_openimages.py)        |                             [model](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_1x_openimages/faster_rcnn_r50_fpn_32x2_1x_openimages_20211130_231159-e87ab7ce.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_1x_openimages/faster_rcnn_r50_fpn_32x2_1x_openimages_20211130_231159.log.json)                             |\n|         Faster R-CNN          |   R-50   | pytorch |   1x    | Class Aware Sampler |   7.7    |       -        |  60.0  |      [config](./faster-rcnn_r50_fpn_32xb2-cas-1x_openimages.py)      |                     [model](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_20220306_202424-98c630e5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_20220306_202424.log.json)                     |\n| Faster R-CNN (Challenge 2019) |   R-50   | pytorch |   1x    |    Group Sampler    |   7.7    |       -        |  54.9  |   [config](./faster-rcnn_r50_fpn_32xb2-1x_openimages-challenge.py)   |         [model](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_1x_openimages_challenge/faster_rcnn_r50_fpn_32x2_1x_openimages_challenge_20220114_045100-0e79e5df.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_1x_openimages_challenge/faster_rcnn_r50_fpn_32x2_1x_openimages_challenge_20220114_045100.log.json)         |\n| Faster R-CNN (Challenge 2019) |   R-50   | pytorch |   1x    | Class Aware Sampler |   7.1    |       -        |  65.0  | [config](./faster-rcnn_r50_fpn_32xb2-cas-1x_openimages-challenge.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_challenge/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_challenge_20220221_192021-34c402d9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_challenge/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_challenge_20220221_192021.log.json) |\n|           Retinanet           |   R-50   | pytorch |   1x    |    Group Sampler    |   6.6    |       -        |  61.5  |         [config](./retinanet_r50_fpn_32xb2-1x_openimages.py)         |                                 [model](https://download.openmmlab.com/mmdetection/v2.0/openimages/retinanet_r50_fpn_32x2_1x_openimages/retinanet_r50_fpn_32x2_1x_openimages_20211223_071954-d2ae5462.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/openimages/retinanet_r50_fpn_32x2_1x_openimages/retinanet_r50_fpn_32x2_1x_openimages_20211223_071954.log.json)                                 |\n|              SSD              |  VGG16   | pytorch |   36e   |    Group Sampler    |   10.8   |       -        |  35.4  |              [config](./ssd300_32xb8-36e_openimages.py)              |                                                     [model](https://download.openmmlab.com/mmdetection/v2.0/openimages/ssd300_32x8_36e_openimages/ssd300_32x8_36e_openimages_20211224_000232-dce93846.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/openimages/ssd300_32x8_36e_openimages/ssd300_32x8_36e_openimages_20211224_000232.log.json)                                                     |\n\n**Notes:**\n\n- 'cas' is short for 'Class Aware Sampler'\n\n### Results of consider image level labels\n\n|           Architecture            |       Sampler       | Consider Image Level Labels | box AP |\n| :-------------------------------: | :-----------------: | :-------------------------: | :----: |\n| Faster R-CNN r50 (Challenge 2019) |    Group Sampler    |             w/o             | 62.19  |\n| Faster R-CNN r50 (Challenge 2019) |    Group Sampler    |             w/              | 54.87  |\n| Faster R-CNN r50 (Challenge 2019) | Class Aware Sampler |             w/o             | 71.77  |\n| Faster R-CNN r50 (Challenge 2019) | Class Aware Sampler |             w/              | 64.98  |\n"
  },
  {
    "path": "configs/openimages/faster-rcnn_r50_fpn_32xb2-1x_openimages-challenge.py",
    "content": "_base_ = ['faster-rcnn_r50_fpn_32xb2-1x_openimages.py']\n\nmodel = dict(\n    roi_head=dict(bbox_head=dict(num_classes=500)),\n    test_cfg=dict(rcnn=dict(score_thr=0.01)))\n\n# dataset settings\ndataset_type = 'OpenImagesChallengeDataset'\ntrain_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        ann_file='challenge2019/challenge-2019-train-detection-bbox.txt',\n        label_file='challenge2019/cls-label-description.csv',\n        hierarchy_file='challenge2019/class_label_tree.np',\n        meta_file='challenge2019/challenge-2019-train-metas.pkl'))\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        ann_file='challenge2019/challenge-2019-validation-detection-bbox.txt',\n        data_prefix=dict(img='OpenImages/'),\n        label_file='challenge2019/cls-label-description.csv',\n        hierarchy_file='challenge2019/class_label_tree.np',\n        meta_file='challenge2019/challenge-2019-validation-metas.pkl',\n        image_level_ann_file='challenge2019/challenge-2019-validation-'\n        'detection-human-imagelabels.csv'))\ntest_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        ann_file='challenge2019/challenge-2019-validation-detection-bbox.txt',\n        label_file='challenge2019/cls-label-description.csv',\n        hierarchy_file='challenge2019/class_label_tree.np',\n        meta_file='challenge2019/challenge-2019-validation-metas.pkl',\n        image_level_ann_file='challenge2019/challenge-2019-validation-'\n        'detection-human-imagelabels.csv'))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/openimages/faster-rcnn_r50_fpn_32xb2-1x_openimages.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/openimages_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=601)))\n\n# Using 32 GPUS while training\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.08, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 64,\n        by_epoch=False,\n        begin=0,\n        end=26000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/openimages/faster-rcnn_r50_fpn_32xb2-cas-1x_openimages-challenge.py",
    "content": "_base_ = ['faster-rcnn_r50_fpn_32xb2-1x_openimages-challenge.py']\n\n# Use ClassAwareSampler\ntrain_dataloader = dict(\n    sampler=dict(_delete_=True, type='ClassAwareSampler', num_sample_class=1))\n"
  },
  {
    "path": "configs/openimages/faster-rcnn_r50_fpn_32xb2-cas-1x_openimages.py",
    "content": "_base_ = ['faster-rcnn_r50_fpn_32xb2-1x_openimages.py']\n\n# Use ClassAwareSampler\ntrain_dataloader = dict(\n    sampler=dict(_delete_=True, type='ClassAwareSampler', num_sample_class=1))\n"
  },
  {
    "path": "configs/openimages/metafile.yml",
    "content": "Models:\n  - Name: faster-rcnn_r50_fpn_32x2_1x_openimages\n    In Collection: Faster R-CNN\n    Config: configs/openimages/faster-rcnn_r50_fpn_32xb2-1x_openimages.py\n    Metadata:\n      Training Memory (GB): 7.7\n      Epochs: 12\n      Training Data: Open Images v6\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n    Results:\n      - Task: Object Detection\n        Dataset: Open Images v6\n        Metrics:\n          box AP: 51.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_1x_openimages/faster_rcnn_r50_fpn_32x2_1x_openimages_20211130_231159-e87ab7ce.pth\n\n  - Name: retinanet_r50_fpn_32xb2-1x_openimages\n    In Collection: RetinaNet\n    Config: configs/openimages/retinanet_r50_fpn_32xb2-1x_openimages.py\n    Metadata:\n      Training Memory (GB): 6.6\n      Epochs: 12\n      Training Data: Open Images v6\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n    Results:\n      - Task: Object Detection\n        Dataset: Open Images v6\n        Metrics:\n          box AP: 61.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/openimages/retinanet_r50_fpn_32x2_1x_openimages/retinanet_r50_fpn_32x2_1x_openimages_20211223_071954-d2ae5462.pth\n\n  - Name: ssd300_32xb8-36e_openimages\n    In Collection: SSD\n    Config: configs/openimages/ssd300_32xb8-36e_openimages.py\n    Metadata:\n      Training Memory (GB): 10.8\n      Epochs: 36\n      Training Data: Open Images v6\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n    Results:\n      - Task: Object Detection\n        Dataset: Open Images v6\n        Metrics:\n          box AP: 35.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/openimages/ssd300_32x8_36e_openimages/ssd300_32x8_36e_openimages_20211224_000232-dce93846.pth\n\n  - Name: faster-rcnn_r50_fpn_32x2_1x_openimages_challenge\n    In Collection: Faster R-CNN\n    Config: configs/openimages/faster-rcnn_r50_fpn_32xb2-1x_openimages-challenge.py\n    Metadata:\n      Training Memory (GB): 7.7\n      Epochs: 12\n      Training Data: Open Images Challenge 2019\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n    Results:\n      - Task: Object Detection\n        Dataset: Open Images Challenge 2019\n        Metrics:\n          box AP: 54.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_1x_openimages_challenge/faster_rcnn_r50_fpn_32x2_1x_openimages_challenge_20220114_045100-0e79e5df.pth\n\n  - Name: faster-rcnn_r50_fpn_32x2_cas_1x_openimages\n    In Collection: Faster R-CNN\n    Config: configs/openimages/faster-rcnn_r50_fpn_32xb2-cas-1x_openimages.py\n    Metadata:\n      Training Memory (GB): 7.7\n      Epochs: 12\n      Training Data: Open Images Challenge 2019\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n    Results:\n      - Task: Object Detection\n        Dataset: Open Images Challenge 2019\n        Metrics:\n          box AP: 60.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_20220306_202424-98c630e5.pth\n\n  - Name: faster-rcnn_r50_fpn_32x2_cas_1x_openimages_challenge\n    In Collection: Faster R-CNN\n    Config: configs/openimages/faster-rcnn_r50_fpn_32xb2-cas-1x_openimages-challenge.py\n    Metadata:\n      Training Memory (GB): 7.1\n      Epochs: 12\n      Training Data: Open Images Challenge 2019\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n    Results:\n      - Task: Object Detection\n        Dataset: Open Images Challenge 2019\n        Metrics:\n          box AP: 65.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_challenge/faster_rcnn_r50_fpn_32x2_cas_1x_openimages_challenge_20220221_192021-34c402d9.pth\n"
  },
  {
    "path": "configs/openimages/retinanet_r50_fpn_32xb2-1x_openimages.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/openimages_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(bbox_head=dict(num_classes=601))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 64,\n        by_epoch=False,\n        begin=0,\n        end=26000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.08, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/openimages/ssd300_32xb8-36e_openimages.py",
    "content": "_base_ = [\n    '../_base_/models/ssd300.py', '../_base_/datasets/openimages_detection.py',\n    '../_base_/default_runtime.py', '../_base_/schedules/schedule_1x.py'\n]\nmodel = dict(\n    bbox_head=dict(\n        num_classes=601,\n        anchor_generator=dict(basesize_ratio_range=(0.2, 0.9))))\n# dataset settings\ndataset_type = 'OpenImagesDataset'\ndata_root = 'data/OpenImages/'\ninput_size = 300\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(\n        type='Expand',\n        mean={{_base_.model.data_preprocessor.mean}},\n        to_rgb={{_base_.model.data_preprocessor.bgr_to_rgb}},\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'instances'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,  # using 32 GPUS while training. total batch size is 32 x 8\n    batch_sampler=None,\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=3,  # repeat 3 times, total epochs are 12 x 3\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/oidv6-train-annotations-bbox.csv',\n            data_prefix=dict(img='OpenImages/train/'),\n            label_file='annotations/class-descriptions-boxable.csv',\n            hierarchy_file='annotations/bbox_labels_600_hierarchy.json',\n            meta_file='annotations/train-image-metas.pkl',\n            pipeline=train_pipeline)))\nval_dataloader = dict(batch_size=8, dataset=dict(pipeline=test_pipeline))\ntest_dataloader = dict(batch_size=8, dataset=dict(pipeline=test_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.04, momentum=0.9, weight_decay=5e-4))\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.001,\n        by_epoch=False,\n        begin=0,\n        end=20000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=256)\n"
  },
  {
    "path": "configs/paa/README.md",
    "content": "# PAA\n\n> [Probabilistic Anchor Assignment with IoU Prediction for Object Detection](https://arxiv.org/abs/2007.08103)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIn object detection, determining which anchors to assign as positive or negative samples, known as anchor assignment, has been revealed as a core procedure that can significantly affect a model's performance. In this paper we propose a novel anchor assignment strategy that adaptively separates anchors into positive and negative samples for a ground truth bounding box according to the model's learning status such that it is able to reason about the separation in a probabilistic manner. To do so we first calculate the scores of anchors conditioned on the model and fit a probability distribution to these scores. The model is then trained with anchors separated into positive and negative samples according to their probabilities. Moreover, we investigate the gap between the training and testing objectives and propose to predict the Intersection-over-Unions of detected boxes as a measure of localization quality to reduce the discrepancy. The combined score of classification and localization qualities serving as a box selection metric in non-maximum suppression well aligns with the proposed anchor assignment strategy and leads significant performance improvements. The proposed methods only add a single convolutional layer to RetinaNet baseline and does not require multiple anchors per location, so are efficient. Experimental results verify the effectiveness of the proposed methods. Especially, our models set new records for single-stage detectors on MS COCO test-dev dataset with various backbones.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143968195-519a116a-de29-437e-b4c8-30aef43dcb15.png\"/>\n</div>\n\n## Results and Models\n\nWe provide config files to reproduce the object detection results in the\nECCV 2020 paper for Probabilistic Anchor Assignment with IoU\nPrediction for Object Detection.\n\n| Backbone  | Lr schd | Mem (GB) | Score voting | box AP |                 Config                 |                                                                                                                                               Download                                                                                                                                               |\n| :-------: | :-----: | :------: | :----------: | :----: | :------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN  |   12e   |   3.7    |     True     |  40.4  |   [config](./paa_r50_fpn_1x_coco.py)   |                     [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1x_coco/paa_r50_fpn_1x_coco_20200821-936edec3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1x_coco/paa_r50_fpn_1x_coco_20200821-936edec3.log.json)                      |\n| R-50-FPN  |   12e   |   3.7    |    False     |  40.2  |                   -                    |                                                                                                                                                                                                                                                                                                      |\n| R-50-FPN  |   18e   |   3.7    |     True     |  41.4  |  [config](./paa_r50_fpn_1.5x_coco.py)  |                 [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1.5x_coco/paa_r50_fpn_1.5x_coco_20200823-805d6078.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1.5x_coco/paa_r50_fpn_1.5x_coco_20200823-805d6078.log.json)                  |\n| R-50-FPN  |   18e   |   3.7    |    False     |  41.2  |                   -                    |                                                                                                                                                                                                                                                                                                      |\n| R-50-FPN  |   24e   |   3.7    |     True     |  41.6  |   [config](./paa_r50_fpn_2x_coco.py)   |                     [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_2x_coco/paa_r50_fpn_2x_coco_20200821-c98bfc4e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_2x_coco/paa_r50_fpn_2x_coco_20200821-c98bfc4e.log.json)                      |\n| R-50-FPN  |   36e   |   3.7    |     True     |  43.3  | [config](./paa_r50_fpn_ms-3x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_mstrain_3x_coco/paa_r50_fpn_mstrain_3x_coco_20210121_145722-06a6880b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_mstrain_3x_coco/paa_r50_fpn_mstrain_3x_coco_20210121_145722.log.json)   |\n| R-101-FPN |   12e   |   6.2    |     True     |  42.6  |  [config](./paa_r101_fpn_1x_coco.py)   |                   [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_1x_coco/paa_r101_fpn_1x_coco_20200821-0a1825a4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_1x_coco/paa_r101_fpn_1x_coco_20200821-0a1825a4.log.json)                    |\n| R-101-FPN |   12e   |   6.2    |    False     |  42.4  |                   -                    |                                                                                                                                                                                                                                                                                                      |\n| R-101-FPN |   24e   |   6.2    |     True     |  43.5  |  [config](./paa_r101_fpn_2x_coco.py)   |                   [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_2x_coco/paa_r101_fpn_2x_coco_20200821-6829f96b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_2x_coco/paa_r101_fpn_2x_coco_20200821-6829f96b.log.json)                    |\n| R-101-FPN |   36e   |   6.2    |     True     |  45.1  | [config](./paa_r101_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_mstrain_3x_coco/paa_r101_fpn_mstrain_3x_coco_20210122_084202-83250d22.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_mstrain_3x_coco/paa_r101_fpn_mstrain_3x_coco_20210122_084202.log.json) |\n\n**Note**:\n\n1. We find that the performance is unstable with 1x setting and may fluctuate by about 0.2 mAP. We report the best results.\n\n## Citation\n\n```latex\n@inproceedings{paa-eccv2020,\n  title={Probabilistic Anchor Assignment with IoU Prediction for Object Detection},\n  author={Kim, Kang and Lee, Hee Seok},\n  booktitle = {ECCV},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/paa/metafile.yml",
    "content": "Collections:\n  - Name: PAA\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - Probabilistic Anchor Assignment\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/2007.08103\n      Title: 'Probabilistic Anchor Assignment with IoU Prediction for Object Detection'\n    README: configs/paa/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.4.0/mmdet/models/detectors/paa.py#L6\n      Version: v2.4.0\n\nModels:\n  - Name: paa_r50_fpn_1x_coco\n    In Collection: PAA\n    Config: configs/paa/paa_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.7\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1x_coco/paa_r50_fpn_1x_coco_20200821-936edec3.pth\n\n  - Name: paa_r50_fpn_1.5x_coco\n    In Collection: PAA\n    Config: configs/paa/paa_r50_fpn_1.5x_coco.py\n    Metadata:\n      Training Memory (GB): 3.7\n      Epochs: 18\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_1.5x_coco/paa_r50_fpn_1.5x_coco_20200823-805d6078.pth\n\n  - Name: paa_r50_fpn_2x_coco\n    In Collection: PAA\n    Config: configs/paa/paa_r50_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 3.7\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_2x_coco/paa_r50_fpn_2x_coco_20200821-c98bfc4e.pth\n\n  - Name: paa_r50_fpn_mstrain_3x_coco\n    In Collection: PAA\n    Config: configs/paa/paa_r50_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 3.7\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r50_fpn_mstrain_3x_coco/paa_r50_fpn_mstrain_3x_coco_20210121_145722-06a6880b.pth\n\n  - Name: paa_r101_fpn_1x_coco\n    In Collection: PAA\n    Config: configs/paa/paa_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.2\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_1x_coco/paa_r101_fpn_1x_coco_20200821-0a1825a4.pth\n\n  - Name: paa_r101_fpn_2x_coco\n    In Collection: PAA\n    Config: configs/paa/paa_r101_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 6.2\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_2x_coco/paa_r101_fpn_2x_coco_20200821-6829f96b.pth\n\n  - Name: paa_r101_fpn_mstrain_3x_coco\n    In Collection: PAA\n    Config: configs/paa/paa_r101_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 6.2\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/paa/paa_r101_fpn_mstrain_3x_coco/paa_r101_fpn_mstrain_3x_coco_20210122_084202-83250d22.pth\n"
  },
  {
    "path": "configs/paa/paa_r101_fpn_1x_coco.py",
    "content": "_base_ = './paa_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/paa/paa_r101_fpn_2x_coco.py",
    "content": "_base_ = './paa_r101_fpn_1x_coco.py'\nmax_epochs = 24\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\n# training schedule for 2x\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/paa/paa_r101_fpn_ms-3x_coco.py",
    "content": "_base_ = './paa_r50_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/paa/paa_r50_fpn_1.5x_coco.py",
    "content": "_base_ = './paa_r50_fpn_1x_coco.py'\nmax_epochs = 18\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[12, 16],\n        gamma=0.1)\n]\n\n# training schedule for 1.5x\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/paa/paa_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\nmodel = dict(\n    type='PAA',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='PAAHead',\n        reg_decoded_bbox=True,\n        score_voting=True,\n        topk=9,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.1,\n            neg_iou_thr=0.1,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/paa/paa_r50_fpn_2x_coco.py",
    "content": "_base_ = './paa_r50_fpn_1x_coco.py'\nmax_epochs = 24\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\n# training schedule for 2x\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/paa/paa_r50_fpn_ms-3x_coco.py",
    "content": "_base_ = './paa_r50_fpn_1x_coco.py'\nmax_epochs = 36\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n\n# training schedule for 3x\ntrain_cfg = dict(max_epochs=max_epochs)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/pafpn/README.md",
    "content": "# PAFPN\n\n> [Path Aggregation Network for Instance Segmentation](https://arxiv.org/abs/1803.01534)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe way that information propagates in neural networks is of great importance. In this paper, we propose Path Aggregation Network (PANet) aiming at boosting information flow in proposal-based instance segmentation framework. Specifically, we enhance the entire feature hierarchy with accurate localization signals in lower layers by bottom-up path augmentation, which shortens the information path between lower layers and topmost feature. We present adaptive feature pooling, which links feature grid and all feature levels to make useful information in each feature level propagate directly to following proposal subnetworks. A complementary branch capturing different views for each proposal is created to further improve mask prediction. These improvements are simple to implement, with subtle extra computational overhead. Our PANet reaches the 1st place in the COCO 2017 Challenge Instance Segmentation task and the 2nd place in Object Detection task without large-batch training. It is also state-of-the-art on MVD and Cityscapes.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143968947-5f2d7e8a-a236-4d59-8f2d-7fbb12764845.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone |  style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                    Config                    |                                                                                                                                                          Download                                                                                                                                                           |\n| :------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN | pytorch |   1x    |   4.0    |      17.2      |  37.5  |         | [config](./faster-rcnn_r50_pafpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pafpn/faster_rcnn_r50_pafpn_1x_coco/faster_rcnn_r50_pafpn_1x_coco_bbox_mAP-0.375_20200503_105836-b7b4b9bd.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pafpn/faster_rcnn_r50_pafpn_1x_coco/faster_rcnn_r50_pafpn_1x_coco_20200503_105836.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{liu2018path,\n  author = {Shu Liu and\n            Lu Qi and\n            Haifang Qin and\n            Jianping Shi and\n            Jiaya Jia},\n  title = {Path Aggregation Network for Instance Segmentation},\n  booktitle = {Proceedings of IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},\n  year = {2018}\n}\n```\n"
  },
  {
    "path": "configs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    neck=dict(\n        type='PAFPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/pafpn/metafile.yml",
    "content": "Collections:\n  - Name: PAFPN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - PAFPN\n    Paper:\n      URL: https://arxiv.org/abs/1803.01534\n      Title: 'Path Aggregation Network for Instance Segmentation'\n    README: configs/pafpn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/necks/pafpn.py#L11\n      Version: v2.0.0\n\nModels:\n  - Name: faster-rcnn_r50_pafpn_1x_coco\n    In Collection: PAFPN\n    Config: configs/pafpn/faster-rcnn_r50_pafpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.0\n      inference time (ms/im):\n        - value: 58.14\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pafpn/faster_rcnn_r50_pafpn_1x_coco/faster_rcnn_r50_pafpn_1x_coco_bbox_mAP-0.375_20200503_105836-b7b4b9bd.pth\n"
  },
  {
    "path": "configs/panoptic_fpn/README.md",
    "content": "# Panoptic FPN\n\n> [Panoptic feature pyramid networks](https://arxiv.org/abs/1901.02446)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe recently introduced panoptic segmentation task has renewed our community's interest in unifying the tasks of instance segmentation (for thing classes) and semantic segmentation (for stuff classes). However, current state-of-the-art methods for this joint task use separate and dissimilar networks for instance and semantic segmentation, without performing any shared computation. In this work, we aim to unify these methods at the architectural level, designing a single network for both tasks. Our approach is to endow Mask R-CNN, a popular instance segmentation method, with a semantic segmentation branch using a shared Feature Pyramid Network (FPN) backbone. Surprisingly, this simple baseline not only remains effective for instance segmentation, but also yields a lightweight, top-performing method for semantic segmentation. In this work, we perform a detailed study of this minimally extended version of Mask R-CNN with FPN, which we refer to as Panoptic FPN, and show it is a robust and accurate baseline for both tasks. Given its effectiveness and conceptual simplicity, we hope our method can serve as a strong baseline and aid future research in panoptic segmentation.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143968979-a1593758-c9d7-44a6-a3b8-d9686ef19ce8.png\" height=\"300\"/>\n</div>\n\n## Dataset\n\nPanopticFPN requires COCO and [COCO-panoptic](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip) dataset for training and evaluation. You need to download and extract it in the COCO dataset path.\nThe directory should be like this.\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── panoptic_train2017.json\n│   │   │   ├── panoptic_train2017\n│   │   │   ├── panoptic_val2017.json\n│   │   │   ├── panoptic_val2017\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n```\n\n## Results and Models\n\n| Backbone  |  style  | Lr schd | Mem (GB) | Inf time (fps) |  PQ  |  SQ  |  RQ  | PQ_th | SQ_th | RQ_th | PQ_st | SQ_st | RQ_st |                     Config                      |                                                                                                                                                                          Download                                                                                                                                                                          |\n| :-------: | :-----: | :-----: | :------: | :------------: | :--: | :--: | :--: | :---: | :---: | :---: | :---: | :---: | :---: | :---------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN  | pytorch |   1x    |   4.7    |                | 40.2 | 77.8 | 49.3 | 47.8  | 80.9  | 57.5  | 28.9  | 73.1  | 37.0  |   [config](./panoptic-fpn_r50_fpn_1x_coco.py)   |                   [model](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r50_fpn_1x_coco/panoptic_fpn_r50_fpn_1x_coco_20210821_101153-9668fd13.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r50_fpn_1x_coco/panoptic_fpn_r50_fpn_1x_coco_20210821_101153.log.json)                   |\n| R-50-FPN  | pytorch |   3x    |    -     |       -        | 42.5 | 78.1 | 51.7 | 50.3  | 81.5  | 60.3  | 30.7  | 73.0  | 38.8  | [config](./panoptic-fpn_r50_fpn_ms-3x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r50_fpn_mstrain_3x_coco/panoptic_fpn_r50_fpn_mstrain_3x_coco_20210824_171155-5650f98b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r50_fpn_mstrain_3x_coco/panoptic_fpn_r50_fpn_mstrain_3x_coco_20210824_171155.log.json)   |\n| R-101-FPN | pytorch |   1x    |   6.7    |                | 42.2 | 78.3 | 51.4 | 50.1  | 81.4  | 59.9  | 30.3  | 73.6  | 38.5  |  [config](./panoptic-fpn_r101_fpn_1x_coco.py)   |                 [model](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r101_fpn_1x_coco/panoptic_fpn_r101_fpn_1x_coco_20210820_193950-ab9157a2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r101_fpn_1x_coco/panoptic_fpn_r101_fpn_1x_coco_20210820_193950.log.json)                 |\n| R-101-FPN | pytorch |   3x    |    -     |       -        | 44.1 | 78.9 | 53.6 | 52.1  | 81.7  | 62.3  | 32.0  | 74.6  | 40.3  | [config](./panoptic-fpn_r101_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r101_fpn_mstrain_3x_coco/panoptic_fpn_r101_fpn_mstrain_3x_coco_20210823_114712-9c99acc4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r101_fpn_mstrain_3x_coco/panoptic_fpn_r101_fpn_mstrain_3x_coco_20210823_114712.log.json) |\n\n## Citation\n\nThe base method for panoptic segmentation task.\n\n```latex\n@inproceedings{kirillov2018panopticfpn,\n  author = {\n    Alexander Kirillov,\n    Ross Girshick,\n    Kaiming He,\n    Piotr Dollar,\n  },\n  title = {Panoptic Feature Pyramid Networks},\n  booktitle = {Proceedings of IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},\n  year = {2019}\n}\n```\n"
  },
  {
    "path": "configs/panoptic_fpn/metafile.yml",
    "content": "Collections:\n  - Name: PanopticFPN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - PanopticFPN\n    Paper:\n      URL: https://arxiv.org/pdf/1901.02446\n      Title: 'Panoptic feature pyramid networks'\n    README: configs/panoptic_fpn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/detectors/panoptic_fpn.py#L7\n      Version: v2.16.0\n\nModels:\n  - Name: panoptic_fpn_r50_fpn_1x_coco\n    In Collection: PanopticFPN\n    Config: configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.6\n      Epochs: 12\n    Results:\n    - Task: Panoptic Segmentation\n      Dataset: COCO\n      Metrics:\n        PQ: 40.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r50_fpn_1x_coco/panoptic_fpn_r50_fpn_1x_coco_20210821_101153-9668fd13.pth\n\n  - Name: panoptic_fpn_r50_fpn_mstrain_3x_coco\n    In Collection: PanopticFPN\n    Config: configs/panoptic_fpn/panoptic-fpn_r50_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.6\n      Epochs: 36\n    Results:\n    - Task: Panoptic Segmentation\n      Dataset: COCO\n      Metrics:\n        PQ: 42.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r50_fpn_mstrain_3x_coco/panoptic_fpn_r50_fpn_mstrain_3x_coco_20210824_171155-5650f98b.pth\n\n  - Name: panoptic_fpn_r101_fpn_1x_coco\n    In Collection: PanopticFPN\n    Config: configs/panoptic_fpn/panoptic-fpn_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.5\n      Epochs: 12\n    Results:\n    - Task: Panoptic Segmentation\n      Dataset: COCO\n      Metrics:\n        PQ: 42.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r101_fpn_1x_coco/panoptic_fpn_r101_fpn_1x_coco_20210820_193950-ab9157a2.pth\n\n  - Name: panoptic_fpn_r101_fpn_mstrain_3x_coco\n    In Collection: PanopticFPN\n    Config: configs/panoptic_fpn/panoptic-fpn_r101_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 6.5\n      Epochs: 36\n    Results:\n    - Task: Panoptic Segmentation\n      Dataset: COCO\n      Metrics:\n        PQ: 44.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/panoptic_fpn/panoptic_fpn_r101_fpn_mstrain_3x_coco/panoptic_fpn_r101_fpn_mstrain_3x_coco_20210823_114712-9c99acc4.pth\n"
  },
  {
    "path": "configs/panoptic_fpn/panoptic-fpn_r101_fpn_1x_coco.py",
    "content": "_base_ = './panoptic-fpn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/panoptic_fpn/panoptic-fpn_r101_fpn_ms-3x_coco.py",
    "content": "_base_ = './panoptic-fpn_r50_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_panoptic.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    type='PanopticFPN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        pad_mask=True,\n        mask_pad_value=0,\n        pad_seg=True,\n        seg_pad_value=255),\n    semantic_head=dict(\n        type='PanopticFPNHead',\n        num_things_classes=80,\n        num_stuff_classes=53,\n        in_channels=256,\n        inner_channels=128,\n        start_level=0,\n        end_level=4,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True),\n        conv_cfg=None,\n        loss_seg=dict(\n            type='CrossEntropyLoss', ignore_index=255, loss_weight=0.5)),\n    panoptic_fusion_head=dict(\n        type='HeuristicFusionHead',\n        num_things_classes=80,\n        num_stuff_classes=53),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.6,\n            nms=dict(type='nms', iou_threshold=0.5, class_agnostic=True),\n            max_per_img=100,\n            mask_thr_binary=0.5),\n        # used in HeuristicFusionHead\n        panoptic=dict(mask_overlap=0.5, stuff_area_limit=4096)))\n\n# Forced to remove NumClassCheckHook\ncustom_hooks = []\n"
  },
  {
    "path": "configs/panoptic_fpn/panoptic-fpn_r50_fpn_ms-3x_coco.py",
    "content": "_base_ = './panoptic-fpn_r50_fpn_1x_coco.py'\n\n# In mstrain 3x config, img_scale=[(1333, 640), (1333, 800)],\n# multiscale_mode='range'\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(\n        type='LoadPanopticAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        with_seg=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# TODO: Use RepeatDataset to speed up training\n# training schedule for 3x\ntrain_cfg = dict(max_epochs=36, val_interval=3)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=36,\n        by_epoch=True,\n        milestones=[24, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/pascal_voc/README.md",
    "content": "# Pascal VOC\n\n> [The Pascal Visual Object Classes (VOC) Challenge](https://link.springer.com/article/10.1007/s11263-009-0275-4)\n\n<!-- [DATASET] -->\n\n## Abstract\n\nThe Pascal Visual Object Classes (VOC) challenge is a benchmark in visual object category recognition and detection, providing the vision and machine learning communities with a standard dataset of images and annotation, and standard evaluation procedures. Organised annually from 2005 to present, the challenge and its associated dataset has become accepted as the benchmark for object detection.\n\nThis paper describes the dataset and evaluation procedure. We review the state-of-the-art in evaluated methods for both classification and detection, analyse whether the methods are statistically different, what they are learning from the images (e.g. the object or its context), and what the methods find easy or confuse. The paper concludes with lessons learnt in the three year history of the challenge, and proposes directions for future improvement and extension.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143969235-6bb4d665-0470-4bae-825c-492eb4582127.png\" height=\"600\"/>\n</div>\n\n## Results and Models\n\n|  Architecture   | Backbone |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                         Config                         |                                                                                                                                                                                                                                                       Download                                                                                                                                                                                                                                                       |\n| :-------------: | :------: | :-----: | :-----: | :------: | :------------: | :----: | :----------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Faster R-CNN C4 |   R-50   |  caffe  |   18k   |          |       -        |  80.9  | [config](./faster-rcnn_r50-caffe-c4_ms-18k_voc0712.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/faster_rcnn_r50_caffe_c4_mstrain_18k_voc0712//home/dong/code_sensetime/2022Q1/mmdetection/work_dirs/prepare_voc/gather/pascal_voc/faster_rcnn_r50_caffe_c4_mstrain_18k_voc0712/faster_rcnn_r50_caffe_c4_mstrain_18k_voc0712_20220314_234327-847a14d2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/faster_rcnn_r50_caffe_c4_mstrain_18k_voc0712/faster_rcnn_r50_caffe_c4_mstrain_18k_voc0712_20220314_234327.log.json) |\n|  Faster R-CNN   |   R-50   | pytorch |   1x    |   2.6    |       -        |  80.4  |     [config](./faster-rcnn_r50_fpn_1x_voc0712.py)      |                                                                                              [model](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/faster_rcnn_r50_fpn_1x_voc0712/faster_rcnn_r50_fpn_1x_voc0712_20220320_192712-54bef0f3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/faster_rcnn_r50_fpn_1x_voc0712/faster_rcnn_r50_fpn_1x_voc0712_20220320_192712.log.json)                                                                                              |\n|    Retinanet    |   R-50   | pytorch |   1x    |   2.1    |       -        |  77.3  |      [config](./retinanet_r50_fpn_1x_voc0712.py)       |                                                                                                     [model](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/retinanet_r50_fpn_1x_voc0712/retinanet_r50_fpn_1x_voc0712_20200617-47cbdd0e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/retinanet_r50_fpn_1x_voc0712/retinanet_r50_fpn_1x_voc0712_20200616_014642.log.json)                                                                                                      |\n|     SSD300      |  VGG16   |    -    |  120e   |    -     |       -        |  76.5  |             [config](./ssd300_voc0712.py)              |                                                                                                                              [model](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/ssd300_voc0712/ssd300_voc0712_20220320_194658-17edda1b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/ssd300_voc0712/ssd300_voc0712_20220320_194658.log.json)                                                                                                                              |\n|     SSD512      |  VGG16   |    -    |  120e   |    -     |       -        |  79.5  |             [config](./ssd512_voc0712.py)              |                                                                                                                              [model](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/ssd512_voc0712/ssd512_voc0712_20220320_194717-03cefefe.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pascal_voc/ssd512_voc0712/ssd512_voc0712_20220320_194717.log.json)                                                                                                                              |\n\n## Citation\n\n```latex\n@Article{Everingham10,\n   author = \"Everingham, M. and Van~Gool, L. and Williams, C. K. I. and Winn, J. and Zisserman, A.\",\n   title = \"The Pascal Visual Object Classes (VOC) Challenge\",\n   journal = \"International Journal of Computer Vision\",\n   volume = \"88\",\n   year = \"2010\",\n   number = \"2\",\n   month = jun,\n   pages = \"303--338\",\n}\n```\n"
  },
  {
    "path": "configs/pascal_voc/faster-rcnn_r50-caffe-c4_ms-18k_voc0712.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50-caffe-c4.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/datasets/voc0712.py',\n    '../_base_/default_runtime.py'\n]\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=20)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 480), (1333, 512), (1333, 544), (1333, 576),\n                (1333, 608), (1333, 640), (1333, 672), (1333, 704),\n                (1333, 736), (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    sampler=dict(type='InfiniteSampler', shuffle=True),\n    dataset=dict(\n        _delete_=True,\n        type='ConcatDataset',\n        datasets=[\n            dict(\n                type='VOCDataset',\n                data_root={{_base_.data_root}},\n                ann_file='VOC2007/ImageSets/Main/trainval.txt',\n                data_prefix=dict(sub_data_root='VOC2007/'),\n                filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                pipeline=train_pipeline,\n                backend_args={{_base_.backend_args}}),\n            dict(\n                type='VOCDataset',\n                data_root={{_base_.data_root}},\n                ann_file='VOC2012/ImageSets/Main/trainval.txt',\n                data_prefix=dict(sub_data_root='VOC2012/'),\n                filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                pipeline=train_pipeline,\n                backend_args={{_base_.backend_args}})\n        ]))\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# training schedule for 18k\nmax_iter = 18000\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=3000)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=100),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[12000, 16000],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=3000))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/pascal_voc/faster-rcnn_r50_fpn_1x_voc0712-cocofmt.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/datasets/voc0712.py',\n    '../_base_/default_runtime.py'\n]\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=20)))\n\nMETAINFO = {\n    'classes':\n    ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat',\n     'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person',\n     'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'),\n    # palette is a list of color tuples, which is used for visualization.\n    'palette': [(106, 0, 228), (119, 11, 32), (165, 42, 42), (0, 0, 192),\n                (197, 226, 255), (0, 60, 100), (0, 0, 142), (255, 77, 255),\n                (153, 69, 1), (120, 166, 157), (0, 182, 199), (0, 226, 252),\n                (182, 182, 255), (0, 0, 230), (220, 20, 60), (163, 255, 0),\n                (0, 82, 0), (3, 95, 161), (0, 80, 100), (183, 130, 88)]\n}\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/VOCdevkit/'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1000, 600), keep_ratio=True),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        type='RepeatDataset',\n        times=3,\n        dataset=dict(\n            _delete_=True,\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/voc0712_trainval.json',\n            data_prefix=dict(img=''),\n            metainfo=METAINFO,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args={{_base_.backend_args}})))\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        ann_file='annotations/voc07_test.json',\n        data_prefix=dict(img=''),\n        metainfo=METAINFO,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/voc07_test.json',\n    metric='bbox',\n    format_only=False,\n    backend_args={{_base_.backend_args}})\ntest_evaluator = val_evaluator\n\n# training schedule, the dataset is repeated 3 times, so the\n# actual epoch = 4 * 3 = 12\nmax_epochs = 4\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/pascal_voc/faster-rcnn_r50_fpn_1x_voc0712.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/datasets/voc0712.py',\n    '../_base_/default_runtime.py'\n]\nmodel = dict(roi_head=dict(bbox_head=dict(num_classes=20)))\n\n# training schedule, voc dataset is repeated 3 times, in\n# `_base_/datasets/voc0712.py`, so the actual epoch = 4 * 3 = 12\nmax_epochs = 4\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/pascal_voc/retinanet_r50_fpn_1x_voc0712.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py', '../_base_/datasets/voc0712.py',\n    '../_base_/default_runtime.py'\n]\nmodel = dict(bbox_head=dict(num_classes=20))\n\n# training schedule, voc dataset is repeated 3 times, in\n# `_base_/datasets/voc0712.py`, so the actual epoch = 4 * 3 = 12\nmax_epochs = 4\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "configs/pascal_voc/ssd300_voc0712.py",
    "content": "_base_ = [\n    '../_base_/models/ssd300.py', '../_base_/datasets/voc0712.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    bbox_head=dict(\n        num_classes=20, anchor_generator=dict(basesize_ratio_range=(0.2,\n                                                                    0.9))))\n# dataset settings\ndataset_type = 'VOCDataset'\ndata_root = 'data/VOCdevkit/'\ninput_size = 300\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='Expand',\n        mean={{_base_.model.data_preprocessor.mean}},\n        to_rgb={{_base_.model.data_preprocessor.bgr_to_rgb}},\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=3,\n    dataset=dict(  # RepeatDataset\n        # the dataset is repeated 10 times, and the training schedule is 2x,\n        # so the actual epoch = 12 * 10 = 120.\n        times=10,\n        dataset=dict(  # ConcatDataset\n            # VOCDataset will add different `dataset_type` in dataset.metainfo,\n            # which will get error if using ConcatDataset. Adding\n            # `ignore_keys` can avoid this error.\n            ignore_keys=['dataset_type'],\n            datasets=[\n                dict(\n                    type=dataset_type,\n                    data_root=data_root,\n                    ann_file='VOC2007/ImageSets/Main/trainval.txt',\n                    data_prefix=dict(sub_data_root='VOC2007/'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    pipeline=train_pipeline),\n                dict(\n                    type=dataset_type,\n                    data_root=data_root,\n                    ann_file='VOC2012/ImageSets/Main/trainval.txt',\n                    data_prefix=dict(sub_data_root='VOC2012/'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    pipeline=train_pipeline)\n            ])))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\ncustom_hooks = [\n    dict(type='NumClassCheckHook'),\n    dict(type='CheckInvalidLossHook', interval=50, priority='VERY_LOW')\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=1e-3, momentum=0.9, weight_decay=5e-4))\n\n# learning policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 20],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/pascal_voc/ssd512_voc0712.py",
    "content": "_base_ = 'ssd300_voc0712.py'\n\ninput_size = 512\nmodel = dict(\n    neck=dict(\n        out_channels=(512, 1024, 512, 256, 256, 256, 256),\n        level_strides=(2, 2, 2, 2, 1),\n        level_paddings=(1, 1, 1, 1, 1),\n        last_kernel_size=4),\n    bbox_head=dict(\n        in_channels=(512, 1024, 512, 256, 256, 256, 256),\n        anchor_generator=dict(\n            input_size=input_size,\n            strides=[8, 16, 32, 64, 128, 256, 512],\n            basesize_ratio_range=(0.15, 0.9),\n            ratios=([2], [2, 3], [2, 3], [2, 3], [2, 3], [2], [2]))))\n\n# dataset settings\ndataset_type = 'VOCDataset'\ndata_root = 'data/VOCdevkit/'\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='Expand',\n        mean={{_base_.model.data_preprocessor.mean}},\n        to_rgb={{_base_.model.data_preprocessor.bgr_to_rgb}},\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    # avoid bboxes being resized\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=3,\n    dataset=dict(  # RepeatDataset\n        # the dataset is repeated 10 times, and the training schedule is 2x,\n        # so the actual epoch = 12 * 10 = 120.\n        times=10,\n        dataset=dict(  # ConcatDataset\n            # VOCDataset will add different `dataset_type` in dataset.metainfo,\n            # which will get error if using ConcatDataset. Adding\n            # `ignore_keys` can avoid this error.\n            ignore_keys=['dataset_type'],\n            datasets=[\n                dict(\n                    type=dataset_type,\n                    data_root=data_root,\n                    ann_file='VOC2007/ImageSets/Main/trainval.txt',\n                    data_prefix=dict(sub_data_root='VOC2007/'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    pipeline=train_pipeline),\n                dict(\n                    type=dataset_type,\n                    data_root=data_root,\n                    ann_file='VOC2012/ImageSets/Main/trainval.txt',\n                    data_prefix=dict(sub_data_root='VOC2012/'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    pipeline=train_pipeline)\n            ])))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/pisa/README.md",
    "content": "# PISA\n\n> [Prime Sample Attention in Object Detection](https://arxiv.org/abs/1904.04821)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIt is a common paradigm in object detection frameworks to treat all samples equally and target at maximizing the performance on average. In this work, we revisit this paradigm through a careful study on how different samples contribute to the overall performance measured in terms of mAP. Our study suggests that the samples in each mini-batch are neither independent nor equally important, and therefore a better classifier on average does not necessarily mean higher mAP. Motivated by this study, we propose the notion of Prime Samples, those that play a key role in driving the detection performance. We further develop a simple yet effective sampling and learning strategy called PrIme Sample Attention (PISA) that directs the focus of the training process towards such samples. Our experiments demonstrate that it is often more effective to focus on prime samples than hard samples when training a detector. Particularly, On the MSCOCO dataset, PISA outperforms the random sampling baseline and hard mining schemes, e.g., OHEM and Focal Loss, consistently by around 2% on both single-stage and two-stage detectors, even with a strong backbone ResNeXt-101.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143970710-5cfd5960-fcf9-4e32-860a-acd46ce5d274.png\"/>\n</div>\n\n## Results and Models\n\n| PISA |   Network    |    Backbone    | Lr schd | box AP | mask AP |                         Config                         |                                                                                                                                                              Download                                                                                                                                                              |\n| :--: | :----------: | :------------: | :-----: | :----: | :-----: | :----------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  ×   | Faster R-CNN |    R-50-FPN    |   1x    |  36.4  |         |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   | Faster R-CNN |    R-50-FPN    |   1x    |  38.4  |         |    [config](./faster-rcnn_r50_fpn_pisa_1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_faster_rcnn_r50_fpn_1x_coco/pisa_faster_rcnn_r50_fpn_1x_coco-dea93523.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_faster_rcnn_r50_fpn_1x_coco/pisa_faster_rcnn_r50_fpn_1x_coco_20200506_185619.log.json)               |\n|  ×   | Faster R-CNN | X101-32x4d-FPN |   1x    |  40.1  |         |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   | Faster R-CNN | X101-32x4d-FPN |   1x    |  41.9  |         | [config](./faster-rcnn_x101-32x4d_fpn_pisa_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_faster_rcnn_x101_32x4d_fpn_1x_coco/pisa_faster_rcnn_x101_32x4d_fpn_1x_coco-e4accec4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_faster_rcnn_x101_32x4d_fpn_1x_coco/pisa_faster_rcnn_x101_32x4d_fpn_1x_coco_20200505_181503.log.json) |\n|  ×   | Mask   R-CNN |    R-50-FPN    |   1x    |  37.3  |  34.2   |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   | Mask   R-CNN |    R-50-FPN    |   1x    |  39.1  |  35.2   |     [config](./mask-rcnn_r50_fpn_pisa_1x_coco.py)      |                   [model](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_mask_rcnn_r50_fpn_1x_coco/pisa_mask_rcnn_r50_fpn_1x_coco-dfcedba6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_mask_rcnn_r50_fpn_1x_coco/pisa_mask_rcnn_r50_fpn_1x_coco_20200508_150500.log.json)                   |\n|  ×   | Mask   R-CNN | X101-32x4d-FPN |   1x    |  41.1  |  37.1   |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   | Mask   R-CNN | X101-32x4d-FPN |   1x    |        |         |                                                        |                                                                                                                                                                                                                                                                                                                                    |\n|  ×   |  RetinaNet   |    R-50-FPN    |   1x    |  35.6  |         |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   |  RetinaNet   |    R-50-FPN    |   1x    |  36.9  |         |     [config](./retinanet-r50_fpn_pisa_1x_coco.py)      |                   [model](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_retinanet_r50_fpn_1x_coco/pisa_retinanet_r50_fpn_1x_coco-76409952.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_retinanet_r50_fpn_1x_coco/pisa_retinanet_r50_fpn_1x_coco_20200504_014311.log.json)                   |\n|  ×   |  RetinaNet   | X101-32x4d-FPN |   1x    |  39.0  |         |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   |  RetinaNet   | X101-32x4d-FPN |   1x    |  40.7  |         |  [config](./retinanet_x101-32x4d_fpn_pisa_1x_coco.py)  |     [model](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_retinanet_x101_32x4d_fpn_1x_coco/pisa_retinanet_x101_32x4d_fpn_1x_coco-a0c13c73.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_retinanet_x101_32x4d_fpn_1x_coco/pisa_retinanet_x101_32x4d_fpn_1x_coco_20200505_001404.log.json)     |\n|  ×   |    SSD300    |     VGG16      |   1x    |  25.6  |         |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   |    SSD300    |     VGG16      |   1x    |  27.6  |         |            [config](./ssd300_pisa_coco.py)             |                                               [model](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_ssd300_coco/pisa_ssd300_coco-710e3ac9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_ssd300_coco/pisa_ssd300_coco_20200504_144325.log.json)                                               |\n|  ×   |    SSD512    |     VGG16      |   1x    |  29.3  |         |                           -                            |                                                                                                                                                                                                                                                                                                                                    |\n|  √   |    SSD512    |     VGG16      |   1x    |  31.8  |         |            [config](./ssd512_pisa_coco.py)             |                                               [model](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_ssd512_coco/pisa_ssd512_coco-247addee.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_ssd512_coco/pisa_ssd512_coco_20200508_131030.log.json)                                               |\n\n**Notes:**\n\n- In the original paper, all models are trained and tested on mmdet v1.x, thus results may not be exactly the same with this release on v2.0.\n- It is noted PISA only modifies the training pipeline so the inference time remains the same with the baseline.\n\n## Citation\n\n```latex\n@inproceedings{cao2019prime,\n  title={Prime sample attention in object detection},\n  author={Cao, Yuhang and Chen, Kai and Loy, Chen Change and Lin, Dahua},\n  booktitle={IEEE Conference on Computer Vision and Pattern Recognition},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/pisa/faster-rcnn_r50_fpn_pisa_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    roi_head=dict(\n        type='PISARoIHead',\n        bbox_head=dict(\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    train_cfg=dict(\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            sampler=dict(\n                type='ScoreHLRSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True,\n                k=0.5,\n                bias=0.),\n            isr=dict(k=2, bias=0),\n            carl=dict(k=1, bias=0.2))),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)))\n"
  },
  {
    "path": "configs/pisa/faster-rcnn_x101-32x4d_fpn_pisa_1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_x101-32x4d_fpn_1x_coco.py'\n\nmodel = dict(\n    roi_head=dict(\n        type='PISARoIHead',\n        bbox_head=dict(\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    train_cfg=dict(\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            sampler=dict(\n                type='ScoreHLRSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True,\n                k=0.5,\n                bias=0.),\n            isr=dict(k=2, bias=0),\n            carl=dict(k=1, bias=0.2))),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)))\n"
  },
  {
    "path": "configs/pisa/mask-rcnn_r50_fpn_pisa_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    roi_head=dict(\n        type='PISARoIHead',\n        bbox_head=dict(\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    train_cfg=dict(\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            sampler=dict(\n                type='ScoreHLRSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True,\n                k=0.5,\n                bias=0.),\n            isr=dict(k=2, bias=0),\n            carl=dict(k=1, bias=0.2))),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)))\n"
  },
  {
    "path": "configs/pisa/mask-rcnn_x101-32x4d_fpn_pisa_1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py'\n\nmodel = dict(\n    roi_head=dict(\n        type='PISARoIHead',\n        bbox_head=dict(\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),\n    train_cfg=dict(\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            sampler=dict(\n                type='ScoreHLRSampler',\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True,\n                k=0.5,\n                bias=0.),\n            isr=dict(k=2, bias=0),\n            carl=dict(k=1, bias=0.2))),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)))\n"
  },
  {
    "path": "configs/pisa/metafile.yml",
    "content": "Collections:\n  - Name: PISA\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - PISA\n        - RPN\n        - ResNet\n        - RoIPool\n    Paper:\n      URL: https://arxiv.org/abs/1904.04821\n      Title: 'Prime Sample Attention in Object Detection'\n    README: configs/pisa/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/roi_heads/pisa_roi_head.py#L8\n      Version: v2.1.0\n\nModels:\n  - Name: pisa_faster_rcnn_r50_fpn_1x_coco\n    In Collection: PISA\n    Config: configs/pisa/faster-rcnn_r50_fpn_pisa_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_faster_rcnn_r50_fpn_1x_coco/pisa_faster_rcnn_r50_fpn_1x_coco-dea93523.pth\n\n  - Name: pisa_faster_rcnn_x101_32x4d_fpn_1x_coco\n    In Collection: PISA\n    Config: configs/pisa/faster-rcnn_x101-32x4d_fpn_pisa_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_faster_rcnn_x101_32x4d_fpn_1x_coco/pisa_faster_rcnn_x101_32x4d_fpn_1x_coco-e4accec4.pth\n\n  - Name: pisa_mask_rcnn_r50_fpn_1x_coco\n    In Collection: PISA\n    Config: configs/pisa/mask-rcnn_r50_fpn_pisa_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 35.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_mask_rcnn_r50_fpn_1x_coco/pisa_mask_rcnn_r50_fpn_1x_coco-dfcedba6.pth\n\n  - Name: pisa_retinanet_r50_fpn_1x_coco\n    In Collection: PISA\n    Config: configs/pisa/retinanet-r50_fpn_pisa_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_retinanet_r50_fpn_1x_coco/pisa_retinanet_r50_fpn_1x_coco-76409952.pth\n\n  - Name: pisa_retinanet_x101_32x4d_fpn_1x_coco\n    In Collection: PISA\n    Config: configs/pisa/retinanet_x101-32x4d_fpn_pisa_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_retinanet_x101_32x4d_fpn_1x_coco/pisa_retinanet_x101_32x4d_fpn_1x_coco-a0c13c73.pth\n\n  - Name: pisa_ssd300_coco\n    In Collection: PISA\n    Config: configs/pisa/ssd300_pisa_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 27.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_ssd300_coco/pisa_ssd300_coco-710e3ac9.pth\n\n  - Name: pisa_ssd512_coco\n    In Collection: PISA\n    Config: configs/pisa/ssd512_pisa_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 31.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pisa/pisa_ssd512_coco/pisa_ssd512_coco-247addee.pth\n"
  },
  {
    "path": "configs/pisa/retinanet-r50_fpn_pisa_1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_r50_fpn_1x_coco.py'\n\nmodel = dict(\n    bbox_head=dict(\n        type='PISARetinaHead',\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.11, loss_weight=1.0)),\n    train_cfg=dict(isr=dict(k=2., bias=0.), carl=dict(k=1., bias=0.2)))\n"
  },
  {
    "path": "configs/pisa/retinanet_x101-32x4d_fpn_pisa_1x_coco.py",
    "content": "_base_ = '../retinanet/retinanet_x101-32x4d_fpn_1x_coco.py'\n\nmodel = dict(\n    bbox_head=dict(\n        type='PISARetinaHead',\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.11, loss_weight=1.0)),\n    train_cfg=dict(isr=dict(k=2., bias=0.), carl=dict(k=1., bias=0.2)))\n"
  },
  {
    "path": "configs/pisa/ssd300_pisa_coco.py",
    "content": "_base_ = '../ssd/ssd300_coco.py'\n\nmodel = dict(\n    bbox_head=dict(type='PISASSDHead'),\n    train_cfg=dict(isr=dict(k=2., bias=0.), carl=dict(k=1., bias=0.2)))\n\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/pisa/ssd512_pisa_coco.py",
    "content": "_base_ = '../ssd/ssd512_coco.py'\n\nmodel = dict(\n    bbox_head=dict(type='PISASSDHead'),\n    train_cfg=dict(isr=dict(k=2., bias=0.), carl=dict(k=1., bias=0.2)))\n\noptim_wrapper = dict(clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/point_rend/README.md",
    "content": "# PointRend\n\n> [PointRend: Image Segmentation as Rendering](https://arxiv.org/abs/1912.08193)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present a new method for efficient high-quality image segmentation of objects and scenes. By analogizing classical computer graphics methods for efficient rendering with over- and undersampling challenges faced in pixel labeling tasks, we develop a unique perspective of image segmentation as a rendering problem. From this vantage, we present the PointRend (Point-based Rendering) neural network module: a module that performs point-based segmentation predictions at adaptively selected locations based on an iterative subdivision algorithm. PointRend can be flexibly applied to both instance and semantic segmentation tasks by building on top of existing state-of-the-art models. While many concrete implementations of the general idea are possible, we show that a simple design already achieves excellent results. Qualitatively, PointRend outputs crisp object boundaries in regions that are over-smoothed by previous methods. Quantitatively, PointRend yields significant gains on COCO and Cityscapes, for both instance and semantic segmentation. PointRend's efficiency enables output resolutions that are otherwise impractical in terms of memory or computation compared to existing approaches.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143970097-d38b6801-d3c8-468f-b8b0-639be3689907.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone | Style | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                       Config                       |                                                                                                                                                                      Download                                                                                                                                                                      |\n| :------: | :---: | :-----: | :------: | :------------: | :----: | :-----: | :------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-FPN | caffe |   1x    |   4.6    |                |  38.4  |  36.3   | [config](./point-rend_r50-caffe_fpn_ms-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/point_rend/point_rend_r50_caffe_fpn_mstrain_1x_coco/point_rend_r50_caffe_fpn_mstrain_1x_coco-1bcb5fb4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/point_rend/point_rend_r50_caffe_fpn_mstrain_1x_coco/point_rend_r50_caffe_fpn_mstrain_1x_coco_20200612_161407.log.json) |\n| R-50-FPN | caffe |   3x    |   4.6    |                |  41.0  |  38.0   | [config](./point-rend_r50-caffe_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/point_rend/point_rend_r50_caffe_fpn_mstrain_3x_coco/point_rend_r50_caffe_fpn_mstrain_3x_coco-e0ebb6b7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/point_rend/point_rend_r50_caffe_fpn_mstrain_3x_coco/point_rend_r50_caffe_fpn_mstrain_3x_coco_20200614_002632.log.json) |\n\nNote: All models are trained with multi-scale, the input image shorter side is randomly scaled to one of (640, 672, 704, 736, 768, 800).\n\n## Citation\n\n```latex\n@InProceedings{kirillov2019pointrend,\n  title={{PointRend}: Image Segmentation as Rendering},\n  author={Alexander Kirillov and Yuxin Wu and Kaiming He and Ross Girshick},\n  journal={ArXiv:1912.08193},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/point_rend/metafile.yml",
    "content": "Collections:\n  - Name: PointRend\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - PointRend\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1912.08193\n      Title: 'PointRend: Image Segmentation as Rendering'\n    README: configs/point_rend/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.2.0/mmdet/models/detectors/point_rend.py#L6\n      Version: v2.2.0\n\nModels:\n  - Name: point_rend_r50_caffe_fpn_mstrain_1x_coco\n    In Collection: PointRend\n    Config: configs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.6\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/point_rend/point_rend_r50_caffe_fpn_mstrain_1x_coco/point_rend_r50_caffe_fpn_mstrain_1x_coco-1bcb5fb4.pth\n\n  - Name: point_rend_r50_caffe_fpn_mstrain_3x_coco\n    In Collection: PointRend\n    Config: configs/point_rend/point-rend_r50-caffe_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.6\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/point_rend/point_rend_r50_caffe_fpn_mstrain_3x_coco/point_rend_r50_caffe_fpn_mstrain_3x_coco-e0ebb6b7.pth\n"
  },
  {
    "path": "configs/point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-1x_coco.py'\n# model settings\nmodel = dict(\n    type='PointRend',\n    roi_head=dict(\n        type='PointRendRoIHead',\n        mask_roi_extractor=dict(\n            type='GenericRoIExtractor',\n            aggregation='concat',\n            roi_layer=dict(\n                _delete_=True, type='SimpleRoIAlign', output_size=14),\n            out_channels=256,\n            featmap_strides=[4]),\n        mask_head=dict(\n            _delete_=True,\n            type='CoarseMaskHead',\n            num_fcs=2,\n            in_channels=256,\n            conv_out_channels=256,\n            fc_out_channels=1024,\n            num_classes=80,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0)),\n        point_head=dict(\n            type='MaskPointHead',\n            num_fcs=3,\n            in_channels=256,\n            fc_channels=256,\n            num_classes=80,\n            coarse_pred_each_layer=True,\n            loss_point=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rcnn=dict(\n            mask_size=7,\n            num_points=14 * 14,\n            oversample_ratio=3,\n            importance_sample_ratio=0.75)),\n    test_cfg=dict(\n        rcnn=dict(\n            subdivision_steps=5,\n            subdivision_num_points=28 * 28,\n            scale_factor=2)))\n"
  },
  {
    "path": "configs/point_rend/point-rend_r50-caffe_fpn_ms-3x_coco.py",
    "content": "_base_ = './point-rend_r50-caffe_fpn_ms-1x_coco.py'\n\nmax_epochs = 36\n\n# learning policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/pvt/README.md",
    "content": "# PVT\n\n> [Pyramid vision transformer: A versatile backbone for dense prediction without convolutions](https://arxiv.org/abs/2102.12122)\n\n<!-- [BACKBONE] -->\n\n## Abstract\n\nAlthough using convolutional neural networks (CNNs) as backbones achieves great successes in computer vision, this work investigates a simple backbone network useful for many dense prediction tasks without convolutions. Unlike the recently-proposed Transformer model (e.g., ViT) that is specially designed for image classification, we propose Pyramid Vision Transformer~(PVT), which overcomes the difficulties of porting Transformer to various dense prediction tasks. PVT has several merits compared to prior arts. (1) Different from ViT that typically has low-resolution outputs and high computational and memory cost, PVT can be not only trained on dense partitions of the image to achieve high output resolution, which is important for dense predictions but also using a progressive shrinking pyramid to reduce computations of large feature maps. (2) PVT inherits the advantages from both CNN and Transformer, making it a unified backbone in various vision tasks without convolutions by simply replacing CNN backbones. (3) We validate PVT by conducting extensive experiments, showing that it boosts the performance of many downstream tasks, e.g., object detection, semantic, and instance segmentation. For example, with a comparable number of parameters, RetinaNet+PVT achieves 40.4 AP on the COCO dataset, surpassing RetinNet+ResNet50 (36.3 AP) by 4.1 absolute AP. We hope PVT could serve as an alternative and useful backbone for pixel-level predictions and facilitate future researches.\n\nTransformer recently has shown encouraging progresses in computer vision. In this work, we present new baselines by improving the original Pyramid Vision Transformer (abbreviated as PVTv1) by adding three designs, including (1) overlapping patch embedding, (2) convolutional feed-forward networks, and (3) linear complexity attention layers.\nWith these modifications, our PVTv2 significantly improves PVTv1 on three tasks e.g., classification, detection, and segmentation. Moreover, PVTv2 achieves comparable or better performances than recent works such as Swin Transformer. We hope this work will facilitate state-of-the-art Transformer researches in computer vision.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143969989-6f94e695-23b1-4f8f-b406-d589fdc3cfb2.png\"/>\n</div>\n\n## Results and Models\n\n### RetinaNet (PVTv1)\n\n|  Backbone  | Lr schd | Mem (GB) | box AP |                   Config                   |                                                                                                                                             Download                                                                                                                                             |\n| :--------: | :-----: | :------: | :----: | :----------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  PVT-Tiny  |   12e   |   8.5    |  36.6  | [config](./retinanet_pvt-t_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-t_fpn_1x_coco/retinanet_pvt-t_fpn_1x_coco_20210831_103110-17b566bd.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-t_fpn_1x_coco/retinanet_pvt-t_fpn_1x_coco_20210831_103110.log.json) |\n| PVT-Small  |   12e   |   14.5   |  40.4  | [config](./retinanet_pvt-s_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-s_fpn_1x_coco/retinanet_pvt-s_fpn_1x_coco_20210906_142921-b6c94a5b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-s_fpn_1x_coco/retinanet_pvt-s_fpn_1x_coco_20210906_142921.log.json) |\n| PVT-Medium |   12e   |   20.9   |  41.7  | [config](./retinanet_pvt-m_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-m_fpn_1x_coco/retinanet_pvt-m_fpn_1x_coco_20210831_103243-55effa1b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-m_fpn_1x_coco/retinanet_pvt-m_fpn_1x_coco_20210831_103243.log.json) |\n\n### RetinaNet (PVTv2)\n\n| Backbone | Lr schd | Mem (GB) | box AP |                    Config                     |                                                                                                                                                   Download                                                                                                                                                   |\n| :------: | :-----: | :------: | :----: | :-------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| PVTv2-B0 |   12e   |   7.4    |  37.1  | [config](./retinanet_pvtv2-b0_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b0_fpn_1x_coco/retinanet_pvtv2-b0_fpn_1x_coco_20210831_103157-13e9aabe.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b0_fpn_1x_coco/retinanet_pvtv2-b0_fpn_1x_coco_20210831_103157.log.json) |\n| PVTv2-B1 |   12e   |   9.5    |  41.2  | [config](./retinanet_pvtv2-b1_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b1_fpn_1x_coco/retinanet_pvtv2-b1_fpn_1x_coco_20210831_103318-7e169a7d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b1_fpn_1x_coco/retinanet_pvtv2-b1_fpn_1x_coco_20210831_103318.log.json) |\n| PVTv2-B2 |   12e   |   16.2   |  44.6  | [config](./retinanet_pvtv2-b2_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b2_fpn_1x_coco/retinanet_pvtv2-b2_fpn_1x_coco_20210901_174843-529f0b9a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b2_fpn_1x_coco/retinanet_pvtv2-b2_fpn_1x_coco_20210901_174843.log.json) |\n| PVTv2-B3 |   12e   |   23.0   |  46.0  | [config](./retinanet_pvtv2-b3_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b3_fpn_1x_coco/retinanet_pvtv2-b3_fpn_1x_coco_20210903_151512-8357deff.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b3_fpn_1x_coco/retinanet_pvtv2-b3_fpn_1x_coco_20210903_151512.log.json) |\n| PVTv2-B4 |   12e   |   17.0   |  46.3  | [config](./retinanet_pvtv2-b4_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b4_fpn_1x_coco/retinanet_pvtv2-b4_fpn_1x_coco_20210901_170151-83795c86.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b4_fpn_1x_coco/retinanet_pvtv2-b4_fpn_1x_coco_20210901_170151.log.json) |\n| PVTv2-B5 |   12e   |   18.7   |  46.1  | [config](./retinanet_pvtv2-b5_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b5_fpn_1x_coco/retinanet_pvtv2-b5_fpn_1x_coco_20210902_201800-3420eb57.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b5_fpn_1x_coco/retinanet_pvtv2-b5_fpn_1x_coco_20210902_201800.log.json) |\n\n## Citation\n\n```latex\n@article{wang2021pyramid,\n  title={Pyramid vision transformer: A versatile backbone for dense prediction without convolutions},\n  author={Wang, Wenhai and Xie, Enze and Li, Xiang and Fan, Deng-Ping and Song, Kaitao and Liang, Ding and Lu, Tong and Luo, Ping and Shao, Ling},\n  journal={arXiv preprint arXiv:2102.12122},\n  year={2021}\n}\n```\n\n```latex\n@article{wang2021pvtv2,\n  title={PVTv2: Improved Baselines with Pyramid Vision Transformer},\n  author={Wang, Wenhai and Xie, Enze and Li, Xiang and Fan, Deng-Ping and Song, Kaitao and Liang, Ding and Lu, Tong and Luo, Ping and Shao, Ling},\n  journal={arXiv preprint arXiv:2106.13797},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/pvt/metafile.yml",
    "content": "Models:\n  - Name: retinanet_pvt-t_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvt-t_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformer\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-t_fpn_1x_coco/retinanet_pvt-t_fpn_1x_coco_20210831_103110-17b566bd.pth\n    Paper:\n      URL: https://arxiv.org/abs/2102.12122\n      Title: \"Pyramid Vision Transformer: A Versatile Backbone for Dense Prediction without Convolutions\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L315\n      Version: 2.17.0\n\n  - Name: retinanet_pvt-s_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvt-s_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 14.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformer\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-s_fpn_1x_coco/retinanet_pvt-s_fpn_1x_coco_20210906_142921-b6c94a5b.pth\n    Paper:\n      URL: https://arxiv.org/abs/2102.12122\n      Title: \"Pyramid Vision Transformer: A Versatile Backbone for Dense Prediction without Convolutions\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L315\n      Version: 2.17.0\n\n  - Name: retinanet_pvt-m_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvt-m_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 20.9\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformer\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvt-m_fpn_1x_coco/retinanet_pvt-m_fpn_1x_coco_20210831_103243-55effa1b.pth\n    Paper:\n      URL: https://arxiv.org/abs/2102.12122\n      Title: \"Pyramid Vision Transformer: A Versatile Backbone for Dense Prediction without Convolutions\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L315\n      Version: 2.17.0\n\n  - Name: retinanet_pvtv2-b0_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvtv2-b0_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.4\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformerV2\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b0_fpn_1x_coco/retinanet_pvtv2-b0_fpn_1x_coco_20210831_103157-13e9aabe.pth\n    Paper:\n      URL: https://arxiv.org/abs/2106.13797\n      Title: \"PVTv2: Improved Baselines with Pyramid Vision Transformer\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L543\n      Version: 2.17.0\n\n  - Name: retinanet_pvtv2-b1_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvtv2-b1_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 9.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformerV2\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b1_fpn_1x_coco/retinanet_pvtv2-b1_fpn_1x_coco_20210831_103318-7e169a7d.pth\n    Paper:\n      URL: https://arxiv.org/abs/2106.13797\n      Title: \"PVTv2: Improved Baselines with Pyramid Vision Transformer\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L543\n      Version: 2.17.0\n\n  - Name: retinanet_pvtv2-b2_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvtv2-b2_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 16.2\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformerV2\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b2_fpn_1x_coco/retinanet_pvtv2-b2_fpn_1x_coco_20210901_174843-529f0b9a.pth\n    Paper:\n      URL: https://arxiv.org/abs/2106.13797\n      Title: \"PVTv2: Improved Baselines with Pyramid Vision Transformer\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L543\n      Version: 2.17.0\n\n  - Name: retinanet_pvtv2-b3_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvtv2-b3_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 23.0\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformerV2\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b3_fpn_1x_coco/retinanet_pvtv2-b3_fpn_1x_coco_20210903_151512-8357deff.pth\n    Paper:\n      URL: https://arxiv.org/abs/2106.13797\n      Title: \"PVTv2: Improved Baselines with Pyramid Vision Transformer\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L543\n      Version: 2.17.0\n\n  - Name: retinanet_pvtv2-b4_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvtv2-b4_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 17.0\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformerV2\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b4_fpn_1x_coco/retinanet_pvtv2-b4_fpn_1x_coco_20210901_170151-83795c86.pth\n    Paper:\n      URL: https://arxiv.org/abs/2106.13797\n      Title: \"PVTv2: Improved Baselines with Pyramid Vision Transformer\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L543\n      Version: 2.17.0\n\n  - Name: retinanet_pvtv2-b5_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/pvt/retinanet_pvtv2-b5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 18.7\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x NVIDIA V100 GPUs\n      Architecture:\n        - PyramidVisionTransformerV2\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/pvt/retinanet_pvtv2-b5_fpn_1x_coco/retinanet_pvtv2-b5_fpn_1x_coco_20210902_201800-3420eb57.pth\n    Paper:\n      URL: https://arxiv.org/abs/2106.13797\n      Title: \"PVTv2: Improved Baselines with Pyramid Vision Transformer\"\n    README: configs/pvt/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.17.0/mmdet/models/backbones/pvt.py#L543\n      Version: 2.17.0\n"
  },
  {
    "path": "configs/pvt/retinanet_pvt-l_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvt-t_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        num_layers=[3, 8, 27, 3],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_large.pth')))\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(type='AmpOptimWrapper')\n"
  },
  {
    "path": "configs/pvt/retinanet_pvt-m_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvt-t_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        num_layers=[3, 4, 18, 3],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_medium.pth')))\n"
  },
  {
    "path": "configs/pvt/retinanet_pvt-s_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvt-t_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        num_layers=[3, 4, 6, 3],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_small.pth')))\n"
  },
  {
    "path": "configs/pvt/retinanet_pvt-t_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='RetinaNet',\n    backbone=dict(\n        _delete_=True,\n        type='PyramidVisionTransformer',\n        num_layers=[2, 2, 2, 2],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_tiny.pth')),\n    neck=dict(in_channels=[64, 128, 320, 512]))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=0.0001, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/pvt/retinanet_pvtv2-b0_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='RetinaNet',\n    backbone=dict(\n        _delete_=True,\n        type='PyramidVisionTransformerV2',\n        embed_dims=32,\n        num_layers=[2, 2, 2, 2],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_v2_b0.pth')),\n    neck=dict(in_channels=[32, 64, 160, 256]))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=0.0001, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/pvt/retinanet_pvtv2-b1_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvtv2-b0_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        embed_dims=64,\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_v2_b1.pth')),\n    neck=dict(in_channels=[64, 128, 320, 512]))\n"
  },
  {
    "path": "configs/pvt/retinanet_pvtv2-b2_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvtv2-b0_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        embed_dims=64,\n        num_layers=[3, 4, 6, 3],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_v2_b2.pth')),\n    neck=dict(in_channels=[64, 128, 320, 512]))\n"
  },
  {
    "path": "configs/pvt/retinanet_pvtv2-b3_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvtv2-b0_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        embed_dims=64,\n        num_layers=[3, 4, 18, 3],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_v2_b3.pth')),\n    neck=dict(in_channels=[64, 128, 320, 512]))\n"
  },
  {
    "path": "configs/pvt/retinanet_pvtv2-b4_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvtv2-b0_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        embed_dims=64,\n        num_layers=[3, 8, 27, 3],\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_v2_b4.pth')),\n    neck=dict(in_channels=[64, 128, 320, 512]))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=0.0001 / 1.4, weight_decay=0.0001))\n\n# dataset settings\ntrain_dataloader = dict(batch_size=1, num_workers=1)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (1 samples per GPU)\nauto_scale_lr = dict(base_batch_size=8)\n"
  },
  {
    "path": "configs/pvt/retinanet_pvtv2-b5_fpn_1x_coco.py",
    "content": "_base_ = 'retinanet_pvtv2-b0_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        embed_dims=64,\n        num_layers=[3, 6, 40, 3],\n        mlp_ratios=(4, 4, 4, 4),\n        init_cfg=dict(checkpoint='https://github.com/whai362/PVT/'\n                      'releases/download/v2/pvt_v2_b5.pth')),\n    neck=dict(in_channels=[64, 128, 320, 512]))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=0.0001 / 1.4, weight_decay=0.0001))\n\n# dataset settings\ntrain_dataloader = dict(batch_size=1, num_workers=1)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (1 samples per GPU)\nauto_scale_lr = dict(base_batch_size=8)\n"
  },
  {
    "path": "configs/qdtrack/README.md",
    "content": "# Quasi-Dense Similarity Learning for Multiple Object Tracking\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nSimilarity learning has been recognized as a crucial step for object tracking. However, existing multiple object tracking methods only use sparse ground truth matching as the training objective, while ignoring the majority of the informative regions on the images. In this paper, we present Quasi-Dense Similarity Learning, which densely samples hundreds of region proposals on a pair of images for contrastive learning. We can directly combine this similarity learning with existing detection methods to build Quasi-Dense Tracking (QDTrack) without turning to displacementregression or motion priors. We also find that the resulting distinctive feature space admits a simple nearest neighbor search at the inference time. Despite its simplicity, QD-Track outperforms all existing methods on MOT, BDD100K, Waymo, and TAO tracking benchmarks. It achieves 68.7 MOTA at 20.3 FPS on MOT17 without using external training data. Compared to methods with similar detectors, it boosts almost 10 points of MOTA and significantly decreases the number of ID switches on BDD100K and Waymo datasets.\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/48645550/158332287-79fb379b-d817-4aa8-8530-5f9d172b3ca7.png\"/>\n  <img src=\"https://user-images.githubusercontent.com/48645550/158332524-8ccaab0e-d379-4c6b-83e5-d75398af02bf.png\"/>\n</div>\n\n## Results and models on MOT17\n\n| Method  |   Detector   | Train Set  | Test Set | Public | Inf time (fps) | HOTA | MOTA | IDF1 |  FP  |  FN   | IDSw. |                                      Config                                       |                                                                                                                                        Download                                                                                                                                        |\n| :-----: | :----------: | :--------: | :------: | :----: | :------------: | :--: | :--: | :--: | :--: | :---: | :---: | :-------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| QDTrack | Faster R-CNN | half-train | half-val |   N    |       -        | 57.1 | 68.1 | 68.6 | 7707 | 42732 | 1083  | [config](qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py) | [model](https://download.openmmlab.com/mmtracking/mot/qdtrack/mot_dataset/qdtrack_faster-rcnn_r50_fpn_4e_mot17_20220315_145635-76f295ef.pth) \\| [log](https://download.openmmlab.com/mmtracking/mot/qdtrack/mot_dataset/qdtrack_faster-rcnn_r50_fpn_4e_mot17_20220315_145635.log.json) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nDue to the influence of parameters such as learning rate in default configuration file, we recommend using 8 GPUs for training in order to reproduce accuracy. You can use the following command to start the training.\n\n```shell\n# Training QDTrack on mot17-half-train dataset with following command.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_train.sh configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 4. Testing and evaluation\n\n**4.1 Example on MOTxx-halfval dataset**\n\n```shell\n# Example 1: Test on motXX-half-val set\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_test_tracking.sh configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8 --checkpoint ${CHECKPOINT_PATH}\n```\n\n**4.2 use video_baesd to evaluating and testing**\nwe also provide two_ways(img_based or video_based) to evaluating and testing.\nif you want to use video_based to evaluating and testing, you can modify config as follows\n\n```\nval_dataloader = dict(\n    sampler=dict(type='DefaultSampler', shuffle=False, round_up=False))\n```\n\nIf you want to know about more detailed usage of `test_tracking.py/dist_test_tracking.sh/slurm_test_tracking.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 5.Inference\n\nUse a single GPU to predict a video and save it as a video.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py --checkpoint ${CHECKPOINT_PATH} --out mot.mp4\n```\n\nIf you want to know about more detailed usage of `mot_demo.py`, please refer to this [document](../../docs/en/user_guides/tracking_inference.md).\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@inproceedings{pang2021quasi,\n  title={Quasi-dense similarity learning for multiple object tracking},\n  author={Pang, Jiangmiao and Qiu, Linlu and Li, Xia and Chen, Haofeng and Li, Qi and Darrell, Trevor and Yu, Fisher},\n  booktitle={Proceedings of the IEEE/CVF conference on computer vision and pattern recognition},\n  pages={164--173},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/qdtrack/metafile.yml",
    "content": "Collections:\n  - Name: QDTrack\n    Metadata:\n      Training Data: MOT17, crowdhuman\n      Training Techniques:\n        - SGD\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/pdf/2006.06664.pdf\n      Title: Quasi-Dense Similarity Learning for Multiple Object Tracking\n    README: configs/qdtrack/README.md\n\nModels:\n  - Name: qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval\n    In Collection: QDTrack\n    Config: configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n    Metadata:\n      Training Data: MOT17\n      Training Memory (GB): 5.83\n      Epochs: 4\n    Results:\n      - Task: Multi-object Tracking\n        Dataset: MOT17\n        Metrics:\n          HOTA: 57.1\n          MOTA: 68.1\n          IDF1: 68.6\n    Weights: https://download.openmmlab.com/mmtracking/mot/qdtrack/mot_dataset/qdtrack_faster-rcnn_r50_fpn_4e_mot17_20220315_145635-76f295ef.pth\n"
  },
  {
    "path": "configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_4e_base.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/default_runtime.py'\n]\n\ndetector = _base_.model\ndetector.pop('data_preprocessor')\n\ndetector['backbone'].update(\n    dict(\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\ndetector.rpn_head.loss_bbox.update(\n    dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0))\ndetector.rpn_head.bbox_coder.update(dict(clip_border=False))\ndetector.roi_head.bbox_head.update(dict(num_classes=1))\ndetector.roi_head.bbox_head.bbox_coder.update(dict(clip_border=False))\ndetector['init_cfg'] = dict(\n    type='Pretrained',\n    checkpoint=  # noqa: E251\n    'https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/'\n    'faster_rcnn_r50_fpn_1x_coco-person/'\n    'faster_rcnn_r50_fpn_1x_coco-person_20201216_175929-d022e227.pth'\n    # noqa: E501\n)\ndel _base_.model\n\nmodel = dict(\n    type='QDTrack',\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    detector=detector,\n    track_head=dict(\n        type='QuasiDenseTrackHead',\n        roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        embed_head=dict(\n            type='QuasiDenseEmbedHead',\n            num_convs=4,\n            num_fcs=1,\n            embed_channels=256,\n            norm_cfg=dict(type='GN', num_groups=32),\n            loss_track=dict(type='MultiPosCrossEntropyLoss', loss_weight=0.25),\n            loss_track_aux=dict(\n                type='MarginL2Loss',\n                neg_pos_ub=3,\n                pos_margin=0,\n                neg_margin=0.1,\n                hard_mining=True,\n                loss_weight=1.0)),\n        loss_bbox=dict(type='L1Loss', loss_weight=1.0),\n        train_cfg=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='CombinedSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=3,\n                add_gt_as_proposals=True,\n                pos_sampler=dict(type='InstanceBalancedPosSampler'),\n                neg_sampler=dict(type='RandomSampler')))),\n    tracker=dict(\n        type='QuasiDenseTracker',\n        init_score_thr=0.9,\n        obj_score_thr=0.5,\n        match_score_thr=0.5,\n        memo_tracklet_frames=30,\n        memo_backdrop_frames=1,\n        memo_momentum=0.8,\n        nms_conf_thr=0.5,\n        nms_backdrop_iou_thr=0.3,\n        nms_class_iou_thr=0.7,\n        with_cats=True,\n        match_metric='bisoftmax'))\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n# learning policy\nparam_scheduler = [\n    dict(type='MultiStepLR', begin=0, end=4, by_epoch=True, milestones=[3])\n]\n\n# runtime settings\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=4, val_interval=4)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\ndefault_hooks = dict(\n    logger=dict(type='LoggerHook', interval=50),\n    visualization=dict(type='TrackVisualizationHook', draw=False))\n\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='TrackLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\n# custom hooks\ncustom_hooks = [\n    # Synchronize model buffers such as running_mean and running_var in BN\n    # at the end of each epoch\n    dict(type='SyncBuffersHook')\n]\n"
  },
  {
    "path": "configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    './qdtrack_faster-rcnn_r50_fpn_4e_base.py',\n    '../_base_/datasets/mot_challenge.py',\n]\n\n# evaluator\nval_evaluator = [\n    dict(type='CocoVideoMetric', metric=['bbox'], classwise=True),\n    dict(type='MOTChallengeMetric', metric=['HOTA', 'CLEAR', 'Identity'])\n]\n\ntest_evaluator = val_evaluator\n# The fluctuation of HOTA is about +-1.\nrandomness = dict(seed=6)\n"
  },
  {
    "path": "configs/queryinst/README.md",
    "content": "# QueryInst\n\n> [Instances as Queries](https://openaccess.thecvf.com/content/ICCV2021/html/Fang_Instances_As_Queries_ICCV_2021_paper.html)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present QueryInst, a new perspective for instance segmentation. QueryInst is a multi-stage end-to-end system that treats instances of interest as learnable queries, enabling query based object detectors, e.g., Sparse R-CNN, to have strong instance segmentation performance. The attributes of instances such as categories, bounding boxes, instance masks, and instance association embeddings are represented by queries in a unified manner. In QueryInst, a query is shared by both detection and segmentation via dynamic convolutions and driven by parallelly-supervised multi-stage learning. We conduct extensive experiments on three challenging benchmarks, i.e., COCO, CityScapes, and YouTube-VIS to evaluate the effectiveness of QueryInst in object detection, instance segmentation, and video instance segmentation tasks. For the first time, we demonstrate that a simple end-to-end query based framework can achieve the state-of-the-art performance in various instance-level recognition tasks.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143971527-c1b7ff78-e95f-4edb-9d5e-3d6d7d902999.png\"/>\n</div>\n\n## Results and Models\n\n|   Model   | Backbone  |  Style  | Lr schd | Number of Proposals | Multi-Scale | RandomCrop | box AP | mask AP |                                 Config                                  |                                                                                                                                                                                                                       Download                                                                                                                                                                                                                       |\n| :-------: | :-------: | :-----: | :-----: | :-----------------: | :---------: | :--------: | :----: | :-----: | :---------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| QueryInst | R-50-FPN  | pytorch |   1x    |         100         |    False    |   False    |  42.0  |  37.5   |                [config](./queryinst_r50_fpn_1x_coco.py)                 |                                                                         [model](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_1x_coco/queryinst_r50_fpn_1x_coco_20210907_084916-5a8f1998.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_1x_coco/queryinst_r50_fpn_1x_coco_20210907_084916.log.json)                                                                         |\n| QueryInst | R-50-FPN  | pytorch |   3x    |         100         |    True     |   False    |  44.8  |  39.8   |           [config](./queryinst_r50_fpn_ms-480-800-3x_coco.py)           |                                         [model](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_mstrain_480-800_3x_coco/queryinst_r50_fpn_mstrain_480-800_3x_coco_20210901_103643-7837af86.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_mstrain_480-800_3x_coco/queryinst_r50_fpn_mstrain_480-800_3x_coco_20210901_103643.log.json)                                         |\n| QueryInst | R-50-FPN  | pytorch |   3x    |         300         |    True     |    True    |  47.5  |  41.7   | [config](./queryinst_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco/queryinst_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20210904_101802-85cffbd8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco/queryinst_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20210904_101802.log.json)   |\n| QueryInst | R-101-FPN | pytorch |   3x    |         100         |    True     |   False    |  46.4  |  41.0   |          [config](./queryinst_r101_fpn_ms-480-800-3x_coco.py)           |                                       [model](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r101_fpn_mstrain_480-800_3x_coco/queryinst_r101_fpn_mstrain_480-800_3x_coco_20210904_104048-91f9995b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r101_fpn_mstrain_480-800_3x_coco/queryinst_r101_fpn_mstrain_480-800_3x_coco_20210904_104048.log.json)                                       |\n| QueryInst | R-101-FPN | pytorch |   3x    |         300         |    True     |    True    |  49.0  |  42.9   | [config](./queryinst_r101_fpn_300-proposals_crop-ms-480-800-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco/queryinst_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20210904_153621-76cce59f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco/queryinst_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20210904_153621.log.json) |\n\n## Citation\n\n```latex\n@InProceedings{Fang_2021_ICCV,\n    author    = {Fang, Yuxin and Yang, Shusheng and Wang, Xinggang and Li, Yu and Fang, Chen and Shan, Ying and Feng, Bin and Liu, Wenyu},\n    title     = {Instances As Queries},\n    booktitle = {Proceedings of the IEEE/CVF International Conference on Computer Vision (ICCV)},\n    month     = {October},\n    year      = {2021},\n    pages     = {6910-6919}\n}\n```\n"
  },
  {
    "path": "configs/queryinst/metafile.yml",
    "content": "Collections:\n  - Name: QueryInst\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n        - QueryInst\n    Paper:\n      URL: https://openaccess.thecvf.com/content/ICCV2021/papers/Fang_Instances_As_Queries_ICCV_2021_paper.pdf\n      Title: 'Instances as Queries'\n    README: configs/queryinst/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/detectors/queryinst.py\n      Version: v2.18.0\n\nModels:\n  - Name: queryinst_r50_fpn_1x_coco\n    In Collection: QueryInst\n    Config: configs/queryinst/queryinst_r50_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_1x_coco/queryinst_r50_fpn_1x_coco_20210907_084916-5a8f1998.pth\n\n  - Name: queryinst_r50_fpn_ms-480-800-3x_coco\n    In Collection: QueryInst\n    Config: configs/queryinst/queryinst_r50_fpn_ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_mstrain_480-800_3x_coco/queryinst_r50_fpn_mstrain_480-800_3x_coco_20210901_103643-7837af86.pth\n\n  - Name: queryinst_r50_fpn_300-proposals_crop-ms-480-800-3x_coco\n    In Collection: QueryInst\n    Config: configs/queryinst/queryinst_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco/queryinst_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20210904_101802-85cffbd8.pth\n\n  - Name: queryinst_r101_fpn_ms-480-800-3x_coco\n    In Collection: QueryInst\n    Config: configs/queryinst/queryinst_r101_fpn_ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r101_fpn_mstrain_480-800_3x_coco/queryinst_r101_fpn_mstrain_480-800_3x_coco_20210904_104048-91f9995b.pth\n\n  - Name: queryinst_r101_fpn_300-proposals_crop-ms-480-800-3x_coco\n    In Collection: QueryInst\n    Config: configs/queryinst/queryinst_r101_fpn_300-proposals_crop-ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 49.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 42.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/queryinst/queryinst_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco/queryinst_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20210904_153621-76cce59f.pth\n"
  },
  {
    "path": "configs/queryinst/queryinst_r101_fpn_300-proposals_crop-ms-480-800-3x_coco.py",
    "content": "_base_ = './queryinst_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/queryinst/queryinst_r101_fpn_ms-480-800-3x_coco.py",
    "content": "_base_ = './queryinst_r50_fpn_ms-480-800-3x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/queryinst/queryinst_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nnum_stages = 6\nnum_proposals = 100\nmodel = dict(\n    type='QueryInst',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=0,\n        add_extra_convs='on_input',\n        num_outs=4),\n    rpn_head=dict(\n        type='EmbeddingRPNHead',\n        num_proposals=num_proposals,\n        proposal_feature_channel=256),\n    roi_head=dict(\n        type='SparseRoIHead',\n        num_stages=num_stages,\n        stage_loss_weights=[1] * num_stages,\n        proposal_feature_channel=256,\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type='DIIHead',\n                num_classes=80,\n                num_ffn_fcs=2,\n                num_heads=8,\n                num_cls_fcs=1,\n                num_reg_fcs=3,\n                feedforward_channels=2048,\n                in_channels=256,\n                dropout=0.0,\n                ffn_act_cfg=dict(type='ReLU', inplace=True),\n                dynamic_conv_cfg=dict(\n                    type='DynamicConv',\n                    in_channels=256,\n                    feat_channels=64,\n                    out_channels=256,\n                    input_feat_shape=7,\n                    act_cfg=dict(type='ReLU', inplace=True),\n                    norm_cfg=dict(type='LN')),\n                loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n                loss_iou=dict(type='GIoULoss', loss_weight=2.0),\n                loss_cls=dict(\n                    type='FocalLoss',\n                    use_sigmoid=True,\n                    gamma=2.0,\n                    alpha=0.25,\n                    loss_weight=2.0),\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    clip_border=False,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.5, 0.5, 1., 1.])) for _ in range(num_stages)\n        ],\n        mask_head=[\n            dict(\n                type='DynamicMaskHead',\n                dynamic_conv_cfg=dict(\n                    type='DynamicConv',\n                    in_channels=256,\n                    feat_channels=64,\n                    out_channels=256,\n                    input_feat_shape=14,\n                    with_proj=False,\n                    act_cfg=dict(type='ReLU', inplace=True),\n                    norm_cfg=dict(type='LN')),\n                num_convs=4,\n                num_classes=80,\n                roi_feat_size=14,\n                in_channels=256,\n                conv_kernel_size=3,\n                conv_out_channels=256,\n                class_agnostic=False,\n                norm_cfg=dict(type='BN'),\n                upsample_cfg=dict(type='deconv', scale_factor=2),\n                loss_mask=dict(\n                    type='DiceLoss',\n                    loss_weight=8.0,\n                    use_sigmoid=True,\n                    activate=False,\n                    eps=1e-5)) for _ in range(num_stages)\n        ]),\n    # training and testing settings\n    train_cfg=dict(\n        rpn=None,\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='HungarianAssigner',\n                    match_costs=[\n                        dict(type='FocalLossCost', weight=2.0),\n                        dict(type='BBoxL1Cost', weight=5.0, box_format='xyxy'),\n                        dict(type='IoUCost', iou_mode='giou', weight=2.0)\n                    ]),\n                sampler=dict(type='PseudoSampler'),\n                pos_weight=1,\n                mask_size=28,\n            ) for _ in range(num_stages)\n        ]),\n    test_cfg=dict(\n        rpn=None, rcnn=dict(max_per_img=num_proposals, mask_thr_binary=0.5)))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=0.0001, weight_decay=0.0001),\n    paramwise_cfg=dict(\n        custom_keys={'backbone': dict(lr_mult=0.1, decay_mult=1.0)}),\n    clip_grad=dict(max_norm=0.1, norm_type=2))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/queryinst/queryinst_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py",
    "content": "_base_ = './queryinst_r50_fpn_ms-480-800-3x_coco.py'\nnum_proposals = 300\nmodel = dict(\n    rpn_head=dict(num_proposals=num_proposals),\n    test_cfg=dict(\n        _delete_=True,\n        rpn=None,\n        rcnn=dict(max_per_img=num_proposals, mask_thr_binary=0.5)))\n\n# augmentation strategy originates from DETR.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/queryinst/queryinst_r50_fpn_ms-480-800-3x_coco.py",
    "content": "_base_ = './queryinst_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                (736, 1333), (768, 1333), (800, 1333)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# learning policy\nmax_epochs = 36\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=max_epochs)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/regnet/README.md",
    "content": "# RegNet\n\n> [Designing Network Design Spaces](https://arxiv.org/abs/2003.13678)\n\n<!-- [BACKBONE] -->\n\n## Abstract\n\nIn this work, we present a new network design paradigm. Our goal is to help advance the understanding of network design and discover design principles that generalize across settings. Instead of focusing on designing individual network instances, we design network design spaces that parametrize populations of networks. The overall process is analogous to classic manual design of networks, but elevated to the design space level. Using our methodology we explore the structure aspect of network design and arrive at a low-dimensional design space consisting of simple, regular networks that we call RegNet. The core insight of the RegNet parametrization is surprisingly simple: widths and depths of good networks can be explained by a quantized linear function. We analyze the RegNet design space and arrive at interesting findings that do not match the current practice of network design. The RegNet design space provides simple and fast networks that work well across a wide range of flop regimes. Under comparable training settings and flops, the RegNet models outperform the popular EfficientNet models while being up to 5x faster on GPUs.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143971942-da50f719-61e9-43bd-9468-0dbfbe80284e.png\"/>\n</div>\n\n## Introduction\n\nWe implement RegNetX and RegNetY models in detection systems and provide their first results on Mask R-CNN, Faster R-CNN and RetinaNet.\n\nThe pre-trained models are converted from [model zoo of pycls](https://github.com/facebookresearch/pycls/blob/master/MODEL_ZOO.md).\n\n## Usage\n\nTo use a regnet model, there are two steps to do:\n\n1. Convert the model to ResNet-style supported by MMDetection\n2. Modify backbone and neck in config accordingly\n\n### Convert model\n\nWe already prepare models of FLOPs from 400M to 12G in our model zoo.\n\nFor more general usage, we also provide script `regnet2mmdet.py` in the tools directory to convert the key of models pretrained by [pycls](https://github.com/facebookresearch/pycls/) to\nResNet-style checkpoints used in MMDetection.\n\n```bash\npython -u tools/model_converters/regnet2mmdet.py ${PRETRAIN_PATH} ${STORE_PATH}\n```\n\nThis script convert model from `PRETRAIN_PATH` and store the converted model in `STORE_PATH`.\n\n### Modify config\n\nThe users can modify the config's `depth` of backbone and corresponding keys in `arch` according to the configs in the [pycls model zoo](https://github.com/facebookresearch/pycls/blob/master/MODEL_ZOO.md).\nThe parameter `in_channels` in FPN can be found in the Figure 15 & 16 of the paper (`wi` in the legend).\nThis directory already provides some configs with their performance, using RegNetX from 800MF to 12GF level.\nFor other pre-trained models or self-implemented regnet models, the users are responsible to check these parameters by themselves.\n\n**Note**: Although Fig. 15 & 16 also provide `w0`, `wa`, `wm`, `group_w`, and `bot_mul` for `arch`, they are quantized thus inaccurate, using them sometimes produces different backbone that does not match the key in the pre-trained model.\n\n## Results and Models\n\n### Mask R-CNN\n\n|                                       Backbone                                       |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                             Config                              |                                                                                                                                                                                          Download                                                                                                                                                                                          |\n| :----------------------------------------------------------------------------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|                [R-50-FPN](../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py)                 | pytorch |   1x    |   4.4    |      12.0      |  38.2  |  34.7   |       [config](../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py)       |                                               [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205_050542.log.json)                                                |\n|            [RegNetX-3.2GF-FPN](./mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py)             | pytorch |   1x    |   5.0    |                |  40.3  |  36.6   |       [config](./mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py)        |                           [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_1x_coco/mask_rcnn_regnetx-3.2GF_fpn_1x_coco_20200520_163141-2a9d1814.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_1x_coco/mask_rcnn_regnetx-3.2GF_fpn_1x_coco_20200520_163141.log.json)                           |\n|             [RegNetX-4.0GF-FPN](./mask-rcnn_regnetx-4GF_fpn_1x_coco.py)              | pytorch |   1x    |   5.5    |                |  41.5  |  37.4   |        [config](./mask-rcnn_regnetx-4GF_fpn_1x_coco.py)         |                               [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-4GF_fpn_1x_coco/mask_rcnn_regnetx-4GF_fpn_1x_coco_20200517_180217-32e9c92d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-4GF_fpn_1x_coco/mask_rcnn_regnetx-4GF_fpn_1x_coco_20200517_180217.log.json)                               |\n|               [R-101-FPN](../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py)                | pytorch |   1x    |   6.4    |      10.3      |  40.0  |  36.1   |      [config](../mask_rcnn/mask-rcnn_r101_fpn_1x_coco.py)       |                                             [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_1x_coco/mask_rcnn_r101_fpn_1x_coco_20200204-1efe0ed5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r101_fpn_1x_coco/mask_rcnn_r101_fpn_1x_coco_20200204_144809.log.json)                                              |\n|            [RegNetX-6.4GF-FPN](./mask-rcnn_regnetx-6.4GF_fpn_1x_coco.py)             | pytorch |   1x    |   6.1    |                |  41.0  |  37.1   |       [config](./mask-rcnn_regnetx-6.4GF_fpn_1x_coco.py)        |                           [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-6.4GF_fpn_1x_coco/mask_rcnn_regnetx-6.4GF_fpn_1x_coco_20200517_180439-3a7aae83.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-6.4GF_fpn_1x_coco/mask_rcnn_regnetx-6.4GF_fpn_1x_coco_20200517_180439.log.json)                           |\n|         [X-101-32x4d-FPN](../mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py)          | pytorch |   1x    |   7.6    |      9.4       |  41.9  |  37.5   |   [config](../mask_rcnn/mask-rcnn_x101-32x4d_fpn_1x_coco.py)    |                                 [model](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_1x_coco/mask_rcnn_x101_32x4d_fpn_1x_coco_20200205-478d0b67.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_x101_32x4d_fpn_1x_coco/mask_rcnn_x101_32x4d_fpn_1x_coco_20200205_034906.log.json)                                  |\n|             [RegNetX-8.0GF-FPN](./mask-rcnn_regnetx-8GF_fpn_1x_coco.py)              | pytorch |   1x    |   6.4    |                |  41.7  |  37.5   |        [config](./mask-rcnn_regnetx-8GF_fpn_1x_coco.py)         |                               [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-8GF_fpn_1x_coco/mask_rcnn_regnetx-8GF_fpn_1x_coco_20200517_180515-09daa87e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-8GF_fpn_1x_coco/mask_rcnn_regnetx-8GF_fpn_1x_coco_20200517_180515.log.json)                               |\n|             [RegNetX-12GF-FPN](./mask-rcnn_regnetx-12GF_fpn_1x_coco.py)              | pytorch |   1x    |   7.4    |                |  42.2  |   38    |        [config](./mask-rcnn_regnetx-12GF_fpn_1x_coco.py)        |                             [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-12GF_fpn_1x_coco/mask_rcnn_regnetx-12GF_fpn_1x_coco_20200517_180552-b538bd8b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-12GF_fpn_1x_coco/mask_rcnn_regnetx-12GF_fpn_1x_coco_20200517_180552.log.json)                             |\n| [RegNetX-3.2GF-FPN-DCN-C3-C5](./mask-rcnn_regnetx-3.2GF-mdconv-c3-c5_fpn_1x_coco.py) | pytorch |   1x    |   5.0    |                |  40.3  |  36.6   | [config](./mask-rcnn_regnetx-3.2GF-mdconv-c3-c5_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_mdconv_c3-c5_1x_coco/mask_rcnn_regnetx-3.2GF_fpn_mdconv_c3-c5_1x_coco_20200520_172726-75f40794.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_mdconv_c3-c5_1x_coco/mask_rcnn_regnetx-3.2GF_fpn_mdconv_c3-c5_1x_coco_20200520_172726.log.json) |\n\n### Faster R-CNN\n\n|                            Backbone                             |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                         Config                          |                                                                                                                                                                    Download                                                                                                                                                                    |\n| :-------------------------------------------------------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    [R-50-FPN](../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py)    | pytorch |   1x    |   4.0    |      18.2      |  37.4  | [config](../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py) |                   [model](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130_204655.log.json)                    |\n| [RegNetX-3.2GF-FPN](./faster-rcnn_regnetx-3.2GF_fpn_1x_coco.py) | pytorch |   1x    |   4.5    |                |  39.9  |  [config](./faster-rcnn_regnetx-3.2GF_fpn_1x_coco.py)   | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_1x_coco/faster_rcnn_regnetx-3.2GF_fpn_1x_coco_20200517_175927-126fd9bf.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_1x_coco/faster_rcnn_regnetx-3.2GF_fpn_1x_coco_20200517_175927.log.json) |\n| [RegNetX-3.2GF-FPN](./faster-rcnn_regnetx-3.2GF_fpn_2x_coco.py) | pytorch |   2x    |   4.5    |                |  41.1  |  [config](./faster-rcnn_regnetx-3.2GF_fpn_2x_coco.py)   | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_2x_coco/faster_rcnn_regnetx-3.2GF_fpn_2x_coco_20200520_223955-e2081918.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_2x_coco/faster_rcnn_regnetx-3.2GF_fpn_2x_coco_20200520_223955.log.json) |\n\n### RetinaNet\n\n|                           Backbone                            |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                       Config                        |                                                                                                                                                                Download                                                                                                                                                                |\n| :-----------------------------------------------------------: | :-----: | :-----: | :------: | :------------: | :----: | :-------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     [R-50-FPN](../retinanet/retinanet_r50_fpn_1x_coco.py)     | pytorch |   1x    |   3.8    |      16.6      |  36.5  | [config](../retinanet/retinanet_r50_fpn_1x_coco.py) |                     [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130_002941.log.json)                      |\n| [RegNetX-800MF-FPN](./retinanet_regnetx-800MF_fpn_1x_coco.py) | pytorch |   1x    |   2.5    |                |  35.6  | [config](./retinanet_regnetx-800MF_fpn_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-800MF_fpn_1x_coco/retinanet_regnetx-800MF_fpn_1x_coco_20200517_191403-f6f91d10.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-800MF_fpn_1x_coco/retinanet_regnetx-800MF_fpn_1x_coco_20200517_191403.log.json) |\n| [RegNetX-1.6GF-FPN](./retinanet_regnetx-1.6GF_fpn_1x_coco.py) | pytorch |   1x    |   3.3    |                |  37.3  | [config](./retinanet_regnetx-1.6GF_fpn_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-1.6GF_fpn_1x_coco/retinanet_regnetx-1.6GF_fpn_1x_coco_20200517_191403-37009a9d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-1.6GF_fpn_1x_coco/retinanet_regnetx-1.6GF_fpn_1x_coco_20200517_191403.log.json) |\n| [RegNetX-3.2GF-FPN](./retinanet_regnetx-3.2GF_fpn_1x_coco.py) | pytorch |   1x    |   4.2    |                |  39.1  | [config](./retinanet_regnetx-3.2GF_fpn_1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-3.2GF_fpn_1x_coco/retinanet_regnetx-3.2GF_fpn_1x_coco_20200520_163141-cb1509e8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-3.2GF_fpn_1x_coco/retinanet_regnetx-3.2GF_fpn_1x_coco_20200520_163141.log.json) |\n\n### Pre-trained models\n\nWe also train some models with longer schedules and multi-scale training. The users could finetune them for downstream tasks.\n\n|      Method       |                                 Backbone                                 |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                            Config                             |                                                                                                                                                                                                Download                                                                                                                                                                                                |\n| :---------------: | :----------------------------------------------------------------------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    Faster RCNN    |    [RegNetX-400MF-FPN](./faster-rcnn_regnetx-400MF_fpn_ms-3x_coco.py)    | pytorch |   3x    |   2.3    |                |  37.1  |    -    |    [config](./faster-rcnn_regnetx-400MF_fpn_ms-3x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-400MF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-400MF_fpn_mstrain_3x_coco_20210526_095112-e1967c37.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-400MF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-400MF_fpn_mstrain_3x_coco_20210526_095112.log.json)             |\n|    Faster RCNN    |    [RegNetX-800MF-FPN](./faster-rcnn_regnetx-800MF_fpn_ms-3x_coco.py)    | pytorch |   3x    |   2.8    |                |  38.8  |    -    |    [config](./faster-rcnn_regnetx-800MF_fpn_ms-3x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-800MF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-800MF_fpn_mstrain_3x_coco_20210526_095118-a2c70b20.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-800MF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-800MF_fpn_mstrain_3x_coco_20210526_095118.log.json)             |\n|    Faster RCNN    |    [RegNetX-1.6GF-FPN](./faster-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py)    | pytorch |   3x    |   3.4    |                |  40.5  |    -    |    [config](./faster-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py)    |                                     [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-1.6GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-1_20210526_095325-94aa46cc.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-1.6GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-1_20210526_095325.log.json)                                     |\n|    Faster RCNN    |    [RegNetX-3.2GF-FPN](./faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py)    | pytorch |   3x    |   4.4    |                |  42.3  |    -    |    [config](./faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py)    |                                     [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-3_20210526_095152-e16a5227.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-3_20210526_095152.log.json)                                     |\n|    Faster RCNN    |      [RegNetX-4GF-FPN](./faster-rcnn_regnetx-4GF_fpn_ms-3x_coco.py)      | pytorch |   3x    |   4.9    |                |  42.8  |    -    |     [config](./faster-rcnn_regnetx-4GF_fpn_ms-3x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-4GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-4GF_fpn_mstrain_3x_coco_20210526_095201-65eaf841.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-4GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-4GF_fpn_mstrain_3x_coco_20210526_095201.log.json)                 |\n|     Mask RCNN     |  [RegNetX-400MF-FPN](./mask-rcnn_regnetx-400MF_fpn_ms-poly-3x_coco.py)   | pytorch |   3x    |   2.5    |                |  37.6  |  34.4   |  [config](./mask-rcnn_regnetx-400MF_fpn_ms-poly-3x_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-400MF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-400MF_fpn_mstrain-poly_3x_coco_20210601_235443-8aac57a4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-400MF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-400MF_fpn_mstrain-poly_3x_coco_20210601_235443.log.json)       |\n|     Mask RCNN     |  [RegNetX-800MF-FPN](./mask-rcnn_regnetx-800MF_fpn_ms-poly-3x_coco.py)   | pytorch |   3x    |   2.9    |                |  39.5  |  36.1   |  [config](./mask-rcnn_regnetx-800MF_fpn_ms-poly-3x_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-800MF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-800MF_fpn_mstrain-poly_3x_coco_20210602_210641-715d51f5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-800MF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-800MF_fpn_mstrain-poly_3x_coco_20210602_210641.log.json)       |\n|     Mask RCNN     |  [RegNetX-1.6GF-FPN](./mask-rcnn_regnetx-1.6GF_fpn_ms-poly-3x_coco.py)   | pytorch |   3x    |   3.6    |                |  40.9  |  37.5   |  [config](./mask-rcnn_regnetx-1.6GF_fpn_ms-poly-3x_coco.py)   |                                    [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-1.6GF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-1_20210602_210641-6764cff5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-1.6GF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-1_20210602_210641.log.json)                                    |\n|     Mask RCNN     |     [RegNetX-3.2GF-FPN](./mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py)     | pytorch |   3x    |   5.0    |                |  43.1  |  38.7   |     [config](./mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco_20200521_202221-99879813.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco_20200521_202221.log.json)                 |\n|     Mask RCNN     |    [RegNetX-4GF-FPN](./mask-rcnn_regnetx-4GF_fpn_ms-poly-3x_coco.py)     | pytorch |   3x    |   5.1    |                |  43.4  |  39.2   |   [config](./mask-rcnn_regnetx-4GF_fpn_ms-poly-3x_coco.py)    |           [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-4GF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-4GF_fpn_mstrain-poly_3x_coco_20210602_032621-00f0331c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-4GF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-4GF_fpn_mstrain-poly_3x_coco_20210602_032621.log.json)           |\n| Cascade Mask RCNN | [RegNetX-400MF-FPN](./cascade-mask-rcnn_regnetx-400MF_fpn_ms-3x_coco.py) | pytorch |   3x    |   4.3    |                |  41.6  |  36.4   | [config](./cascade-mask-rcnn_regnetx-400MF_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-400MF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-400MF_fpn_mstrain_3x_coco_20210715_211619-5142f449.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-400MF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-400MF_fpn_mstrain_3x_coco_20210715_211619.log.json) |\n| Cascade Mask RCNN | [RegNetX-800MF-FPN](./cascade-mask-rcnn_regnetx-800MF_fpn_ms-3x_coco.py) | pytorch |   3x    |   4.8    |                |  42.8  |  37.6   | [config](./cascade-mask-rcnn_regnetx-800MF_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-800MF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-800MF_fpn_mstrain_3x_coco_20210715_211616-dcbd13f4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-800MF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-800MF_fpn_mstrain_3x_coco_20210715_211616.log.json) |\n| Cascade Mask RCNN | [RegNetX-1.6GF-FPN](./cascade-mask-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py) | pytorch |   3x    |   5.4    |                |  44.5  |  39.0   | [config](./cascade-mask-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py) |                         [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-1.6GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-1_20210715_211616-75f29a61.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-1.6GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-1_20210715_211616.log.json)                         |\n| Cascade Mask RCNN | [RegNetX-3.2GF-FPN](./cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py) | pytorch |   3x    |   6.4    |                |  45.8  |  40.0   | [config](./cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py) |                         [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-3_20210715_211616-b9c2c58b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-3_20210715_211616.log.json)                         |\n| Cascade Mask RCNN |   [RegNetX-4GF-FPN](./cascade-mask-rcnn_regnetx-4GF_fpn_ms-3x_coco.py)   | pytorch |   3x    |   6.9    |                |  45.8  |  40.0   |  [config](./cascade-mask-rcnn_regnetx-4GF_fpn_ms-3x_coco.py)  |     [model](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-4GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-4GF_fpn_mstrain_3x_coco_20210715_212034-cbb1be4c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-4GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-4GF_fpn_mstrain_3x_coco_20210715_212034.log.json)     |\n\n### Notice\n\n1. The models are trained using a different weight decay, i.e., `weight_decay=5e-5` according to the setting in ImageNet training. This brings improvement of at least 0.7 AP absolute but does not improve the model using ResNet-50.\n2. RetinaNets using RegNets are trained with learning rate 0.02 with gradient clip. We find that using learning rate 0.02 could improve the results by at least 0.7 AP absolute and gradient clip is necessary to stabilize the training. However, this does not improve the performance of ResNet-50-FPN RetinaNet.\n\n## Citation\n\n```latex\n@article{radosavovic2020designing,\n    title={Designing Network Design Spaces},\n    author={Ilija Radosavovic and Raj Prateek Kosaraju and Ross Girshick and Kaiming He and Piotr Dollár},\n    year={2020},\n    eprint={2003.13678},\n    archivePrefix={arXiv},\n    primaryClass={cs.CV}\n}\n```\n"
  },
  {
    "path": "configs/regnet/cascade-mask-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_1.6gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_1.6gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[72, 168, 408, 912],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms_3x_coco-instance.py',\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py'\n]\nmodel = dict(\n    data_preprocessor=dict(\n        # The mean and std are used in PyCls when training RegNets\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_3.2gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_3.2gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[96, 192, 432, 1008],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(optimizer=dict(weight_decay=0.00005))\n"
  },
  {
    "path": "configs/regnet/cascade-mask-rcnn_regnetx-400MF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_400mf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_400mf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[32, 64, 160, 384],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/cascade-mask-rcnn_regnetx-4GF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_4.0gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_4.0gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[80, 240, 560, 1360],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/cascade-mask-rcnn_regnetx-800MF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_800mf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_800mf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[64, 128, 288, 672],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/faster-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_1.6gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_1.6gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[72, 168, 408, 912],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/faster-rcnn_regnetx-3.2GF_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    data_preprocessor=dict(\n        # The mean and std are used in PyCls when training RegNets\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_3.2gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_3.2gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[96, 192, 432, 1008],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005))\n"
  },
  {
    "path": "configs/regnet/faster-rcnn_regnetx-3.2GF_fpn_2x_coco.py",
    "content": "_base_ = './faster-rcnn_regnetx-3.2GF_fpn_1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/regnet/faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py",
    "content": "_base_ = ['../common/ms_3x_coco.py', '../_base_/models/faster-rcnn_r50_fpn.py']\nmodel = dict(\n    data_preprocessor=dict(\n        # The mean and std are used in PyCls when training RegNets\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_3.2gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_3.2gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[96, 192, 432, 1008],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(optimizer=dict(weight_decay=0.00005))\n"
  },
  {
    "path": "configs/regnet/faster-rcnn_regnetx-400MF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_400mf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_400mf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[32, 64, 160, 384],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/faster-rcnn_regnetx-4GF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_4.0gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_4.0gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[80, 240, 560, 1360],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/faster-rcnn_regnetx-800MF_fpn_ms-3x_coco.py",
    "content": "_base_ = 'faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_800mf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_800mf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[64, 128, 288, 672],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-1.6GF_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_1.6gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_1.6gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[72, 168, 408, 912],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-12GF_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_12gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_12gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[224, 448, 896, 2240],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-3.2GF-mdconv-c3-c5_fpn_1x_coco.py",
    "content": "_base_ = 'mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_3.2gf')))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    data_preprocessor=dict(\n        # The mean and std are used in PyCls when training RegNets\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_3.2gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_3.2gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[96, 192, 432, 1008],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    data_preprocessor=dict(\n        # The mean and std are used in PyCls when training RegNets\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_3.2gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_3.2gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[96, 192, 432, 1008],\n        out_channels=256,\n        num_outs=5))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning policy\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-400MF_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_400mf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_400mf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[32, 64, 160, 384],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-4GF_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_4.0gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_4.0gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[80, 240, 560, 1360],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-4GF_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_4.0gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_4.0gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[80, 240, 560, 1360],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-6.4GF_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_6.4gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_6.4gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[168, 392, 784, 1624],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-800MF_fpn_ms-poly-3x_coco.py",
    "content": "_base_ = [\n    '../common/ms-poly_3x_coco-instance.py',\n    '../_base_/models/mask-rcnn_r50_fpn.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_800mf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_800mf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[64, 128, 288, 672],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/regnet/mask-rcnn_regnetx-8GF_fpn_1x_coco.py",
    "content": "_base_ = './mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_8.0gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_8.0gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[80, 240, 720, 1920],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/metafile.yml",
    "content": "Models:\n  - Name: mask-rcnn_regnetx-3.2GF_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_1x_coco/mask_rcnn_regnetx-3.2GF_fpn_1x_coco_20200520_163141-2a9d1814.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-4GF_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-4GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-4GF_fpn_1x_coco/mask_rcnn_regnetx-4GF_fpn_1x_coco_20200517_180217-32e9c92d.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-6.4GF_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-6.4GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.1\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-6.4GF_fpn_1x_coco/mask_rcnn_regnetx-6.4GF_fpn_1x_coco_20200517_180439-3a7aae83.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-8GF_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-8GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-8GF_fpn_1x_coco/mask_rcnn_regnetx-8GF_fpn_1x_coco_20200517_180515-09daa87e.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-12GF_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-12GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.4\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-12GF_fpn_1x_coco/mask_rcnn_regnetx-12GF_fpn_1x_coco_20200517_180552-b538bd8b.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-3.2GF-mdconv-c3-c5_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-3.2GF-mdconv-c3-c5_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_mdconv_c3-c5_1x_coco/mask_rcnn_regnetx-3.2GF_fpn_mdconv_c3-c5_1x_coco_20200520_172726-75f40794.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: faster-rcnn_regnetx-3.2GF_fpn_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/regnet/faster-rcnn_regnetx-3.2GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_1x_coco/faster_rcnn_regnetx-3.2GF_fpn_1x_coco_20200517_175927-126fd9bf.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: faster-rcnn_regnetx-3.2GF_fpn_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/regnet/faster-rcnn_regnetx-3.2GF_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 4.5\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_2x_coco/faster_rcnn_regnetx-3.2GF_fpn_2x_coco_20200520_223955-e2081918.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: retinanet_regnetx-800MF_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/regnet/retinanet_regnetx-800MF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 2.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 35.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-800MF_fpn_1x_coco/retinanet_regnetx-800MF_fpn_1x_coco_20200517_191403-f6f91d10.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: retinanet_regnetx-1.6GF_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/regnet/retinanet_regnetx-1.6GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.3\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-1.6GF_fpn_1x_coco/retinanet_regnetx-1.6GF_fpn_1x_coco_20200517_191403-37009a9d.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: retinanet_regnetx-3.2GF_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/regnet/retinanet_regnetx-3.2GF_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.2\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/retinanet_regnetx-3.2GF_fpn_1x_coco/retinanet_regnetx-3.2GF_fpn_1x_coco_20200520_163141-cb1509e8.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: faster-rcnn_regnetx-400MF_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/regnet/faster-rcnn_regnetx-400MF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 2.3\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-400MF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-400MF_fpn_mstrain_3x_coco_20210526_095112-e1967c37.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: faster-rcnn_regnetx-800MF_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/regnet/faster-rcnn_regnetx-800MF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 2.8\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-800MF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-800MF_fpn_mstrain_3x_coco_20210526_095118-a2c70b20.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: faster-rcnn_regnetx-1.6GF_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/regnet/faster-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 3.4\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-1.6GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-1_20210526_095325-94aa46cc.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/regnet/faster-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.4\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-3_20210526_095152-e16a5227.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: faster-rcnn_regnetx-4GF_fpn_ms-3x_coco\n    In Collection: Faster R-CNN\n    Config: configs/regnet/faster-rcnn_regnetx-4GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.9\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/faster_rcnn_regnetx-4GF_fpn_mstrain_3x_coco/faster_rcnn_regnetx-4GF_fpn_mstrain_3x_coco_20210526_095201-65eaf841.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco_20200521_202221-99879813.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-400MF_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-400MF_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 2.5\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 34.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-400MF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-400MF_fpn_mstrain-poly_3x_coco_20210601_235443-8aac57a4.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-800MF_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-800MF_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 2.9\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-800MF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-800MF_fpn_mstrain-poly_3x_coco_20210602_210641-715d51f5.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-1.6GF_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-1.6GF_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 3.6\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.9\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-1.6GF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-1_20210602_210641-6764cff5.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco_20200521_202221-99879813.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: mask-rcnn_regnetx-4GF_fpn_ms-poly-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/regnet/mask-rcnn_regnetx-4GF_fpn_ms-poly-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.1\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/mask_rcnn_regnetx-4GF_fpn_mstrain-poly_3x_coco/mask_rcnn_regnetx-4GF_fpn_mstrain-poly_3x_coco_20210602_032621-00f0331c.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: cascade-mask-rcnn_regnetx-400MF_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/regnet/cascade-mask-rcnn_regnetx-400MF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.3\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-400MF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-400MF_fpn_mstrain_3x_coco_20210715_211619-5142f449.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: cascade-mask-rcnn_regnetx-800MF_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/regnet/cascade-mask-rcnn_regnetx-800MF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 4.8\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-800MF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-800MF_fpn_mstrain_3x_coco_20210715_211616-dcbd13f4.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: cascade-mask-rcnn_regnetx-1.6GF_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/regnet/cascade-mask-rcnn_regnetx-1.6GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.4\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-1.6GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-1_20210715_211616-75f29a61.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/regnet/cascade-mask-rcnn_regnetx-3.2GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 6.4\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-3.2GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-3_20210715_211616-b9c2c58b.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n\n  - Name: cascade-mask-rcnn_regnetx-4GF_fpn_ms-3x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/regnet/cascade-mask-rcnn_regnetx-4GF_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 6.9\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - RegNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/regnet/cascade_mask_rcnn_regnetx-4GF_fpn_mstrain_3x_coco/cascade_mask_rcnn_regnetx-4GF_fpn_mstrain_3x_coco_20210715_212034-cbb1be4c.pth\n    Paper:\n      URL: https://arxiv.org/abs/2003.13678\n      Title: 'Designing Network Design Spaces'\n    README: configs/regnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/regnet.py#L11\n      Version: v2.1.0\n"
  },
  {
    "path": "configs/regnet/retinanet_regnetx-1.6GF_fpn_1x_coco.py",
    "content": "_base_ = './retinanet_regnetx-3.2GF_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_1.6gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_1.6gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[72, 168, 408, 912],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/regnet/retinanet_regnetx-3.2GF_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    data_preprocessor=dict(\n        # The mean and std are used in PyCls when training RegNets\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        _delete_=True,\n        type='RegNet',\n        arch='regnetx_3.2gf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_3.2gf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[96, 192, 432, 1008],\n        out_channels=256,\n        num_outs=5))\n\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.00005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n"
  },
  {
    "path": "configs/regnet/retinanet_regnetx-800MF_fpn_1x_coco.py",
    "content": "_base_ = './retinanet_regnetx-3.2GF_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='RegNet',\n        arch='regnetx_800mf',\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://regnetx_800mf')),\n    neck=dict(\n        type='FPN',\n        in_channels=[64, 128, 288, 672],\n        out_channels=256,\n        num_outs=5))\n"
  },
  {
    "path": "configs/reid/README.md",
    "content": "# Training a ReID Model\n\nYou may want to train a ReID model for multiple object tracking or other applications. We support ReID model training in MMDetection, which is built upon [MMPretrain](https://github.com/open-mmlab/mmpretrain).\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Preparation\n\nThis section will show how to train a ReID model on standard datasets i.e. MOT17.\n\nWe need to download datasets following docs. We use [ReIDDataset](mmdet/datasets/reid_dataset.py) to maintain standard datasets. In this case, you need to convert the official dataset to this style. We provide scripts and the usages as follow:\n\n```python\npython tools/dataset_converters/mot2reid.py -i ./data/MOT17/ -o ./data/MOT17/reid --val-split 0.2 --vis-threshold 0.3\n```\n\nArguments:\n\n- `--val-split`: Proportion of the validation dataset to the whole ReID dataset.\n- `--vis-threshold`: Threshold of visibility for each person.\n\nThe directory of the converted datasets is as follows:\n\n```\nMOT17\n├── train\n├── test\n├── reid\n│   ├── imgs\n│   │   ├── MOT17-02-FRCNN_000002\n│   │   │   ├── 000000.jpg\n│   │   │   ├── 000001.jpg\n│   │   │   ├── ...\n│   │   ├── MOT17-02-FRCNN_000003\n│   │   │   ├── 000000.jpg\n│   │   │   ├── 000001.jpg\n│   │   │   ├── ...\n│   ├── meta\n│   │   ├── train_80.txt\n│   │   ├── val_20.txt\n```\n\nNote: `80` in `train_80.txt` means the proportion of the training dataset to the whole ReID dataset is eighty percent. While the proportion of the validation dataset is twenty percent.\n\nFor training, we provide a annotation list `train_80.txt`. Each line of the list constraints a filename and its corresponding ground-truth labels. The format is as follows:\n\n```\nMOT17-05-FRCNN_000110/000018.jpg 0\nMOT17-13-FRCNN_000146/000014.jpg 1\nMOT17-05-FRCNN_000088/000004.jpg 2\nMOT17-02-FRCNN_000009/000081.jpg 3\n```\n\nFor validation, The annotation list `val_20.txt` remains the same as format above.\n\nNote: Images in `MOT17/reid/imgs` are cropped from raw images in `MOT17/train` by the corresponding `gt.txt`. The value of ground-truth labels should fall in range `[0, num_classes - 1]`.\n\n### 3. Training\n\n#### Training on a single GPU\n\n```shell\npython tools/train.py configs/reid/reid_r50_8xb32-6e_mot17train80_test-mot17val20.py\n```\n\n#### Training on multiple GPUs\n\nWe provide `tools/dist_train.sh` to launch training on multiple GPUs.\nThe basic usage is as follows.\n\n```shell\nbash tools/dist_train.sh configs/reid/reid_r50_8xb32-6e_mot17train80_test-mot17val20.py 8\n```\n\n### 4. Customize Dataset\n\nThis section will show how to train a ReID model on customize datasets.\n\n### 4.1 Dataset Preparation\n\nYou need to convert your customize datasets to existing dataset format.\n\n#### An example of customized dataset\n\nAssume we are going to implement a `Filelist` dataset, which takes filelists for both training and testing. The directory of the dataset is as follows:\n\n```\nFilelist\n├── imgs\n│   ├── person1\n│   │   ├── 000000.jpg\n│   │   ├── 000001.jpg\n│   │   ├── ...\n│   ├── person2\n│   │   ├── 000000.jpg\n│   │   ├── 000001.jpg\n│   │   ├── ...\n├── meta\n│   ├── train.txt\n│   ├── val.txt\n```\n\nThe format of annotation list is as follows:\n\n```\nperson1/000000.jpg 0\nperson1/000001.jpg 0\nperson2/000000.jpg 1\nperson2/000001.jpg 1\n```\n\nYou can directly use [ReIDDataset](mmdet/datasets/reid_dataset.py). In this case, you only need to modify the config as follows:\n\n```python\n# modify the path of annotation files and the image path prefix\ndata = dict(\n    train=dict(\n        data_prefix='data/Filelist/imgs',\n        ann_file='data/Filelist/meta/train.txt'),\n    val=dict(\n        data_prefix='data/Filelist/imgs',\n        ann_file='data/Filelist/meta/val.txt'),\n    test=dict(\n        data_prefix='data/Filelist/imgs',\n        ann_file='data/Filelist/meta/val.txt'),\n)\n# modify the number of classes, assume your training set has 100 classes\nmodel = dict(reid=dict(head=dict(num_classes=100)))\n```\n\n### 4.2 Training\n\nThe training stage is the same as `Standard Dataset`.\n"
  },
  {
    "path": "configs/reid/reid_r50_8xb32-6e_mot15train80_test-mot15val20.py",
    "content": "_base_ = ['./reid_r50_8xb32-6e_mot17train80_test-mot17val20.py']\nmodel = dict(head=dict(num_classes=368))\n# data\ndata_root = 'data/MOT15/'\ntrain_dataloader = dict(dataset=dict(data_root=data_root))\nval_dataloader = dict(dataset=dict(data_root=data_root))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/reid/reid_r50_8xb32-6e_mot16train80_test-mot16val20.py",
    "content": "_base_ = ['./reid_r50_8xb32-6e_mot17train80_test-mot17val20.py']\nmodel = dict(head=dict(num_classes=371))\n# data\ndata_root = 'data/MOT16/'\ntrain_dataloader = dict(dataset=dict(data_root=data_root))\nval_dataloader = dict(dataset=dict(data_root=data_root))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/reid/reid_r50_8xb32-6e_mot17train80_test-mot17val20.py",
    "content": "_base_ = [\n    '../_base_/datasets/mot_challenge_reid.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='BaseReID',\n    data_preprocessor=dict(\n        type='ReIDDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        to_rgb=True),\n    backbone=dict(\n        type='mmpretrain.ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(3, ),\n        style='pytorch'),\n    neck=dict(type='GlobalAveragePooling', kernel_size=(8, 4), stride=1),\n    head=dict(\n        type='LinearReIDHead',\n        num_fcs=1,\n        in_channels=2048,\n        fc_channels=1024,\n        out_channels=128,\n        num_classes=380,\n        loss_cls=dict(type='mmpretrain.CrossEntropyLoss', loss_weight=1.0),\n        loss_triplet=dict(type='TripletLoss', margin=0.3, loss_weight=1.0),\n        norm_cfg=dict(type='BN1d'),\n        act_cfg=dict(type='ReLU')),\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint=  # noqa: E251\n        'https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth'  # noqa: E501\n    ))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    clip_grad=None,\n    optimizer=dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001))\n\n# learning policy\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 1000,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=6,\n        by_epoch=True,\n        milestones=[5],\n        gamma=0.1)\n]\n\n# train, val, test setting\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=6, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n"
  },
  {
    "path": "configs/reid/reid_r50_8xb32-6e_mot20train80_test-mot20val20.py",
    "content": "_base_ = ['./reid_r50_8xb32-6e_mot17train80_test-mot17val20.py']\nmodel = dict(head=dict(num_classes=1701))\n# data\ndata_root = 'data/MOT20/'\ntrain_dataloader = dict(dataset=dict(data_root=data_root))\nval_dataloader = dict(dataset=dict(data_root=data_root))\ntest_dataloader = val_dataloader\n\n# train, val, test setting\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=6, val_interval=7)\n"
  },
  {
    "path": "configs/reppoints/README.md",
    "content": "# RepPoints\n\n> [RepPoints: Point Set Representation for Object Detection](https://arxiv.org/abs/1904.11490)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nModern object detectors rely heavily on rectangular bounding boxes, such as anchors, proposals and the final predictions, to represent objects at various recognition stages. The bounding box is convenient to use but provides only a coarse localization of objects and leads to a correspondingly coarse extraction of object features. In this paper, we present RepPoints(representative points), a new finer representation of objects as a set of sample points useful for both localization and recognition. Given ground truth localization and recognition targets for training, RepPoints learn to automatically arrange themselves in a manner that bounds the spatial extent of an object and indicates semantically significant local areas. They furthermore do not require the use of anchors to sample a space of bounding boxes. We show that an anchor-free object detector based on RepPoints can be as effective as the state-of-the-art anchor-based detection methods, with 46.5 AP and 67.4 AP50 on the COCO test-dev detection benchmark, using ResNet-101 model.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143972514-93247220-4dad-4eb3-a51b-a1115dc7d449.png\"/>\n</div>\n\n## Introdution\n\nBy [Ze Yang](https://yangze.tech/), [Shaohui Liu](http://b1ueber2y.me/), and [Han Hu](https://ancientmooner.github.io/).\n\nWe provide code support and configuration files to reproduce the results in the paper for\n[\"RepPoints: Point Set Representation for Object Detection\"](https://arxiv.org/abs/1904.11490) on COCO object detection.\n\n**RepPoints**, initially described in [arXiv](https://arxiv.org/abs/1904.11490), is a new representation method for visual objects, on which visual understanding tasks are typically centered. Visual object representation, aiming at both geometric description and appearance feature extraction, is conventionally achieved by `bounding box + RoIPool (RoIAlign)`. The bounding box representation is convenient to use; however, it provides only a rectangular localization of objects that lacks geometric precision and may consequently degrade feature quality. Our new representation, RepPoints, models objects by a `point set` instead of a `bounding box`, which learns to adaptively position themselves over an object in a manner that circumscribes the object’s `spatial extent` and enables `semantically aligned feature extraction`. This richer and more flexible representation maintains the convenience of bounding boxes while facilitating various visual understanding applications. This repo demonstrated the effectiveness of RepPoints for COCO object detection.\n\nAnother feature of this repo is the demonstration of an `anchor-free detector`, which can be as effective as state-of-the-art anchor-based detection methods. The anchor-free detector can utilize either `bounding box` or `RepPoints` as the basic object representation.\n\n## Results and Models\n\nThe results on COCO 2017val are shown in the table below.\n\n|  Method   |   Backbone    | GN  | Anchor | convert func | Lr schd | Mem (GB) | Inf time (fps) | box AP |                                 Config                                  |                                                                                                                                                                                                                 Download                                                                                                                                                                                                                  |\n| :-------: | :-----------: | :-: | :----: | :----------: | :-----: | :------: | :------------: | :----: | :---------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   BBox    |   R-50-FPN    |  Y  | single |      -       |   1x    |   3.9    |      15.9      |  36.4  |      [config](./reppoints-bbox_r50_fpn-gn_head-gn-grid_1x_coco.py)      |                                     [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco_20200329_145916-0eedf8d1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco_20200329_145916.log.json)                                      |\n|   BBox    |   R-50-FPN    |  Y  |  none  |      -       |   1x    |   3.9    |      15.4      |  37.4  |  [config](./reppoints-bbox_r50-center_fpn-gn_head-gn-grid_1x_coco.py)   |                                     [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco_20200329_145916-0eedf8d1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco_20200329_145916.log.json)                                      |\n| RepPoints |   R-50-FPN    |  N  |  none  |    moment    |   1x    |   3.3    |      18.5      |  37.0  |             [config](./reppoints-moment_r50_fpn_1x_coco.py)             |                                                         [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_1x_coco/reppoints_moment_r50_fpn_1x_coco_20200330-b73db8d1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_1x_coco/reppoints_moment_r50_fpn_1x_coco_20200330_233609.log.json)                                                         |\n| RepPoints |   R-50-FPN    |  Y  |  none  |    moment    |   1x    |   3.9    |      17.5      |  38.1  |       [config](./reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py)        |                       [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_gn-neck%2Bhead_1x_coco/reppoints_moment_r50_fpn_gn-neck%2Bhead_1x_coco_20200329_145952-3e51b550.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_gn-neck%2Bhead_1x_coco/reppoints_moment_r50_fpn_gn-neck%2Bhead_1x_coco_20200329_145952.log.json)                        |\n| RepPoints |   R-50-FPN    |  Y  |  none  |    moment    |   2x    |   3.9    |       -        |  38.6  |       [config](./reppoints-moment_r50_fpn-gn_head-gn_2x_coco.py)        |                           [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_gn-neck%2Bhead_2x_coco/reppoints_moment_r50_fpn_gn-neck%2Bhead_2x_coco_20200329-91babaa2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_gn-neck%2Bhead_2x_coco/reppoints_moment_r50_fpn_gn-neck%2Bhead_2x_coco_20200329_150020.log.json)                           |\n| RepPoints |   R-101-FPN   |  Y  |  none  |    moment    |   2x    |   5.8    |      13.7      |  40.5  |       [config](./reppoints-moment_r101_fpn-gn_head-gn_2x_coco.py)       |                         [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r101_fpn_gn-neck%2Bhead_2x_coco/reppoints_moment_r101_fpn_gn-neck%2Bhead_2x_coco_20200329-4fbc7310.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r101_fpn_gn-neck%2Bhead_2x_coco/reppoints_moment_r101_fpn_gn-neck%2Bhead_2x_coco_20200329_132205.log.json)                         |\n| RepPoints | R-101-FPN-DCN |  Y  |  none  |    moment    |   2x    |   5.9    |      12.1      |  42.9  | [config](./reppoints-moment_r101-dconv-c3-c5_fpn-gn_head-gn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco/reppoints_moment_r101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco_20200329-3309fbf2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco/reppoints_moment_r101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco_20200329_132134.log.json) |\n| RepPoints | X-101-FPN-DCN |  Y  |  none  |    moment    |   2x    |   7.1    |      9.3       |  44.2  | [config](./reppoints-moment_x101-dconv-c3-c5_fpn-gn_head-gn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_x101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco/reppoints_moment_x101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco_20200329-f87da1ea.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_x101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco/reppoints_moment_x101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco_20200329_132201.log.json) |\n\n**Notes:**\n\n- `R-xx`, `X-xx` denote the ResNet and ResNeXt architectures, respectively.\n- `DCN` denotes replacing 3x3 conv with the 3x3 deformable convolution in `c3-c5` stages of backbone.\n- `none` in the `anchor` column means 2-d `center point` (x,y) is used to represent the initial object hypothesis. `single` denotes one 4-d anchor box (x,y,w,h) with IoU based label assign criterion is adopted.\n- `moment`, `partial MinMax`, `MinMax` in the `convert func` column are three functions to convert a point set to a pseudo box.\n- Note the results here are slightly different from those reported in the paper, due to framework change. While the original paper uses an [MXNet](https://mxnet.apache.org/) implementation, we re-implement the method in [PyTorch](https://pytorch.org/) based on mmdetection.\n\n## Citation\n\n```latex\n@inproceedings{yang2019reppoints,\n  title={RepPoints: Point Set Representation for Object Detection},\n  author={Yang, Ze and Liu, Shaohui and Hu, Han and Wang, Liwei and Lin, Stephen},\n  booktitle={The IEEE International Conference on Computer Vision (ICCV)},\n  month={Oct},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/reppoints/metafile.yml",
    "content": "Collections:\n  - Name: RepPoints\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Group Normalization\n        - FPN\n        - RepPoints\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1904.11490\n      Title: 'RepPoints: Point Set Representation for Object Detection'\n    README: configs/reppoints/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/reppoints_detector.py#L9\n      Version: v2.0.0\n\nModels:\n  - Name: reppoints-bbox_r50_fpn-gn_head-gn-grid_1x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-bbox_r50_fpn-gn_head-gn-grid_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.9\n      inference time (ms/im):\n        - value: 62.89\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco_20200329_145916-0eedf8d1.pth\n\n  - Name: reppoints-bbox_r50-center_fpn-gn_head-gn-grid_1x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-bbox_r50-center_fpn-gn_head-gn-grid_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.9\n      inference time (ms/im):\n        - value: 64.94\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco/bbox_r50_grid_fpn_gn-neck%2Bhead_1x_coco_20200329_145916-0eedf8d1.pth\n\n  - Name: reppoints-moment_r50_fpn_1x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-moment_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.3\n      inference time (ms/im):\n        - value: 54.05\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_1x_coco/reppoints_moment_r50_fpn_1x_coco_20200330-b73db8d1.pth\n\n  - Name: reppoints-moment_r50_fpn-gn_head-gn_1x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.9\n      inference time (ms/im):\n        - value: 57.14\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_gn-neck%2Bhead_1x_coco/reppoints_moment_r50_fpn_gn-neck%2Bhead_1x_coco_20200329_145952-3e51b550.pth\n\n  - Name: reppoints-moment_r50_fpn-gn_head-gn_2x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-moment_r50_fpn-gn_head-gn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 3.9\n      inference time (ms/im):\n        - value: 57.14\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r50_fpn_gn-neck%2Bhead_2x_coco/reppoints_moment_r50_fpn_gn-neck%2Bhead_2x_coco_20200329-91babaa2.pth\n\n  - Name: reppoints-moment_r101_fpn-gn_head-gn_2x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-moment_r101_fpn-gn_head-gn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 5.8\n      inference time (ms/im):\n        - value: 72.99\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r101_fpn_gn-neck%2Bhead_2x_coco/reppoints_moment_r101_fpn_gn-neck%2Bhead_2x_coco_20200329-4fbc7310.pth\n\n  - Name: reppoints-moment_r101-dconv-c3-c5_fpn-gn_head-gn_2x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-moment_r101-dconv-c3-c5_fpn-gn_head-gn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 5.9\n      inference time (ms/im):\n        - value: 82.64\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_r101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco/reppoints_moment_r101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco_20200329-3309fbf2.pth\n\n  - Name: reppoints-moment_x101-dconv-c3-c5_fpn-gn_head-gn_2x_coco\n    In Collection: RepPoints\n    Config: configs/reppoints/reppoints-moment_x101-dconv-c3-c5_fpn-gn_head-gn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      inference time (ms/im):\n        - value: 107.53\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/reppoints/reppoints_moment_x101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco/reppoints_moment_x101_fpn_dconv_c3-c5_gn-neck%2Bhead_2x_coco_20200329-f87da1ea.pth\n"
  },
  {
    "path": "configs/reppoints/reppoints-bbox_r50-center_fpn-gn_head-gn-grid_1x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py'\nmodel = dict(bbox_head=dict(transform_method='minmax', use_grid_points=True))\n"
  },
  {
    "path": "configs/reppoints/reppoints-bbox_r50_fpn-gn_head-gn-grid_1x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py'\nmodel = dict(\n    bbox_head=dict(transform_method='minmax', use_grid_points=True),\n    # training and testing settings\n    train_cfg=dict(\n        init=dict(\n            assigner=dict(\n                _delete_=True,\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.4,\n                min_pos_iou=0,\n                ignore_iof_thr=-1))))\n"
  },
  {
    "path": "configs/reppoints/reppoints-minmax_r50_fpn-gn_head-gn_1x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py'\nmodel = dict(bbox_head=dict(transform_method='minmax'))\n"
  },
  {
    "path": "configs/reppoints/reppoints-moment_r101-dconv-c3-c5_fpn-gn_head-gn_2x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/reppoints/reppoints-moment_r101_fpn-gn_head-gn_2x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/reppoints/reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(neck=dict(norm_cfg=norm_cfg), bbox_head=dict(norm_cfg=norm_cfg))\n"
  },
  {
    "path": "configs/reppoints/reppoints-moment_r50_fpn-gn_head-gn_2x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py'\n\nmax_epochs = 24\n\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/reppoints/reppoints-moment_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='RepPointsDetector',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_input',\n        num_outs=5),\n    bbox_head=dict(\n        type='RepPointsHead',\n        num_classes=80,\n        in_channels=256,\n        feat_channels=256,\n        point_feat_channels=256,\n        stacked_convs=3,\n        num_points=9,\n        gradient_mul=0.1,\n        point_strides=[8, 16, 32, 64, 128],\n        point_base_scale=4,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_init=dict(type='SmoothL1Loss', beta=0.11, loss_weight=0.5),\n        loss_bbox_refine=dict(type='SmoothL1Loss', beta=0.11, loss_weight=1.0),\n        transform_method='moment'),\n    # training and testing settings\n    train_cfg=dict(\n        init=dict(\n            assigner=dict(type='PointAssigner', scale=4, pos_num=1),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        refine=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.4,\n                min_pos_iou=0,\n                ignore_iof_thr=-1),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.5),\n        max_per_img=100))\n\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/reppoints/reppoints-moment_x101-dconv-c3-c5_fpn-gn_head-gn_2x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/reppoints/reppoints-partial-minmax_r50_fpn-gn_head-gn_1x_coco.py",
    "content": "_base_ = './reppoints-moment_r50_fpn-gn_head-gn_1x_coco.py'\nmodel = dict(bbox_head=dict(transform_method='partial_minmax'))\n"
  },
  {
    "path": "configs/res2net/README.md",
    "content": "# Res2Net\n\n> [Res2Net: A New Multi-scale Backbone Architecture](https://arxiv.org/abs/1904.01169)\n\n<!-- [BACKBONE] -->\n\n## Abstract\n\nRepresenting features at multiple scales is of great importance for numerous vision tasks. Recent advances in backbone convolutional neural networks (CNNs) continually demonstrate stronger multi-scale representation ability, leading to consistent performance gains on a wide range of applications. However, most existing methods represent the multi-scale features in a layer-wise manner. In this paper, we propose a novel building block for CNNs, namely Res2Net, by constructing hierarchical residual-like connections within one single residual block. The Res2Net represents multi-scale features at a granular level and increases the range of receptive fields for each network layer. The proposed Res2Net block can be plugged into the state-of-the-art backbone CNN models, e.g., ResNet, ResNeXt, and DLA. We evaluate the Res2Net block on all these models and demonstrate consistent performance gains over baseline models on widely-used datasets, e.g., CIFAR-100 and ImageNet. Further ablation studies and experimental results on representative computer vision tasks, i.e., object detection, class activation mapping, and salient object detection, further verify the superiority of the Res2Net over the state-of-the-art baseline methods.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143972411-8d08113f-9fce-4d24-a138-4fadf2c54f9a.png\" height=\"300\"/>\n</div>\n\n## Introduction\n\nWe propose a novel building block for CNNs, namely Res2Net, by constructing hierarchical residual-like connections within one single residual block. The Res2Net represents multi-scale features at a granular level and increases the range of receptive fields for each network layer.\n\n|     Backbone      | Params. | GFLOPs | top-1 err. | top-5 err. |\n| :---------------: | :-----: | :----: | :--------: | :--------: |\n|    ResNet-101     | 44.6 M  |  7.8   |   22.63    |    6.44    |\n| ResNeXt-101-64x4d |  83.5M  |  15.5  |   20.40    |     -      |\n|   HRNetV2p-W48    |  77.5M  |  16.1  |   20.70    |    5.50    |\n|    Res2Net-101    |  45.2M  |  8.3   |   18.77    |    4.64    |\n\nCompared with other backbone networks, Res2Net requires fewer parameters and FLOPs.\n\n**Note:**\n\n- GFLOPs for classification are calculated with image size (224x224).\n\n## Results and Models\n\n### Faster R-CNN\n\n|  Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                       Config                       |                                                                                                                                               Download                                                                                                                                               |\n| :--------: | :-----: | :-----: | :------: | :------------: | :----: | :------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R2-101-FPN | pytorch |   2x    |   7.4    |       -        |  43.0  | [config](./faster-rcnn_res2net-101_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/res2net/faster_rcnn_r2_101_fpn_2x_coco/faster_rcnn_r2_101_fpn_2x_coco-175f1da6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/res2net/faster_rcnn_r2_101_fpn_2x_coco/faster_rcnn_r2_101_fpn_2x_coco_20200514_231734.log.json) |\n\n### Mask R-CNN\n\n|  Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                      Config                      |                                                                                                                                           Download                                                                                                                                           |\n| :--------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :----------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R2-101-FPN | pytorch |   2x    |   7.9    |       -        |  43.6  |  38.7   | [config](./mask-rcnn_res2net-101_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/res2net/mask_rcnn_r2_101_fpn_2x_coco/mask_rcnn_r2_101_fpn_2x_coco-17f061e8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/res2net/mask_rcnn_r2_101_fpn_2x_coco/mask_rcnn_r2_101_fpn_2x_coco_20200515_002413.log.json) |\n\n### Cascade R-CNN\n\n|  Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                        Config                        |                                                                                                                                                   Download                                                                                                                                                   |\n| :--------: | :-----: | :-----: | :------: | :------------: | :----: | :--------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R2-101-FPN | pytorch |   20e   |   7.8    |       -        |  45.7  | [config](./cascade-rcnn_res2net-101_fpn_20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/res2net/cascade_rcnn_r2_101_fpn_20e_coco/cascade_rcnn_r2_101_fpn_20e_coco-f4b7b7db.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/res2net/cascade_rcnn_r2_101_fpn_20e_coco/cascade_rcnn_r2_101_fpn_20e_coco_20200515_091644.log.json) |\n\n### Cascade Mask R-CNN\n\n|  Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                          Config                           |                                                                                                                                                             Download                                                                                                                                                             |\n| :--------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R2-101-FPN | pytorch |   20e   |   9.5    |       -        |  46.4  |  40.0   | [config](./cascade-mask-rcnn_res2net-101_fpn_20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/res2net/cascade_mask_rcnn_r2_101_fpn_20e_coco/cascade_mask_rcnn_r2_101_fpn_20e_coco-8a7b41e1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/res2net/cascade_mask_rcnn_r2_101_fpn_20e_coco/cascade_mask_rcnn_r2_101_fpn_20e_coco_20200515_091645.log.json) |\n\n### Hybrid Task Cascade (HTC)\n\n|  Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                   Config                    |                                                                                                                                 Download                                                                                                                                 |\n| :--------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R2-101-FPN | pytorch |   20e   |    -     |       -        |  47.5  |  41.6   | [config](./htc_res2net-101_fpn_20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/res2net/htc_r2_101_fpn_20e_coco/htc_r2_101_fpn_20e_coco-3a8d2112.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/res2net/htc_r2_101_fpn_20e_coco/htc_r2_101_fpn_20e_coco_20200515_150029.log.json) |\n\n- Res2Net ImageNet pretrained models are in [Res2Net-PretrainedModels](https://github.com/Res2Net/Res2Net-PretrainedModels).\n- More applications of Res2Net are in [Res2Net-Github](https://github.com/Res2Net/).\n\n## Citation\n\n```latex\n@article{gao2019res2net,\n  title={Res2Net: A New Multi-scale Backbone Architecture},\n  author={Gao, Shang-Hua and Cheng, Ming-Ming and Zhao, Kai and Zhang, Xin-Yu and Yang, Ming-Hsuan and Torr, Philip},\n  journal={IEEE TPAMI},\n  year={2020},\n  doi={10.1109/TPAMI.2019.2938758},\n}\n```\n"
  },
  {
    "path": "configs/res2net/cascade-mask-rcnn_res2net-101_fpn_20e_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='Res2Net',\n        depth=101,\n        scales=4,\n        base_width=26,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://res2net101_v1d_26w_4s')))\n"
  },
  {
    "path": "configs/res2net/cascade-rcnn_res2net-101_fpn_20e_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-rcnn_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='Res2Net',\n        depth=101,\n        scales=4,\n        base_width=26,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://res2net101_v1d_26w_4s')))\n"
  },
  {
    "path": "configs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='Res2Net',\n        depth=101,\n        scales=4,\n        base_width=26,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://res2net101_v1d_26w_4s')))\n"
  },
  {
    "path": "configs/res2net/htc_res2net-101_fpn_20e_coco.py",
    "content": "_base_ = '../htc/htc_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='Res2Net',\n        depth=101,\n        scales=4,\n        base_width=26,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://res2net101_v1d_26w_4s')))\n"
  },
  {
    "path": "configs/res2net/mask-rcnn_res2net-101_fpn_2x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='Res2Net',\n        depth=101,\n        scales=4,\n        base_width=26,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://res2net101_v1d_26w_4s')))\n"
  },
  {
    "path": "configs/res2net/metafile.yml",
    "content": "Models:\n  - Name: faster-rcnn_res2net-101_fpn_2x_coco\n    In Collection: Faster R-CNN\n    Config: configs/res2net/faster-rcnn_res2net-101_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.4\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Res2Net\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/res2net/faster_rcnn_r2_101_fpn_2x_coco/faster_rcnn_r2_101_fpn_2x_coco-175f1da6.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.01169\n      Title: 'Res2Net for object detection and instance segmentation'\n    README: configs/res2net/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/res2net.py#L239\n      Version: v2.1.0\n\n  - Name: mask-rcnn_res2net-101_fpn_2x_coco\n    In Collection: Mask R-CNN\n    Config: configs/res2net/mask-rcnn_res2net-101_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.9\n      Epochs: 24\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Res2Net\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/res2net/mask_rcnn_r2_101_fpn_2x_coco/mask_rcnn_r2_101_fpn_2x_coco-17f061e8.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.01169\n      Title: 'Res2Net for object detection and instance segmentation'\n    README: configs/res2net/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/res2net.py#L239\n      Version: v2.1.0\n\n  - Name: cascade-rcnn_res2net-101_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/res2net/cascade-rcnn_res2net-101_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Res2Net\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/res2net/cascade_rcnn_r2_101_fpn_20e_coco/cascade_rcnn_r2_101_fpn_20e_coco-f4b7b7db.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.01169\n      Title: 'Res2Net for object detection and instance segmentation'\n    README: configs/res2net/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/res2net.py#L239\n      Version: v2.1.0\n\n  - Name: cascade-mask-rcnn_res2net-101_fpn_20e_coco\n    In Collection: Cascade R-CNN\n    Config: configs/res2net/cascade-mask-rcnn_res2net-101_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 9.5\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Res2Net\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/res2net/cascade_mask_rcnn_r2_101_fpn_20e_coco/cascade_mask_rcnn_r2_101_fpn_20e_coco-8a7b41e1.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.01169\n      Title: 'Res2Net for object detection and instance segmentation'\n    README: configs/res2net/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/res2net.py#L239\n      Version: v2.1.0\n\n  - Name: htc_res2net-101_fpn_20e_coco\n    In Collection: HTC\n    Config: configs/res2net/htc_res2net-101_fpn_20e_coco.py\n    Metadata:\n      Epochs: 20\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Res2Net\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/res2net/htc_r2_101_fpn_20e_coco/htc_r2_101_fpn_20e_coco-3a8d2112.pth\n    Paper:\n      URL: https://arxiv.org/abs/1904.01169\n      Title: 'Res2Net for object detection and instance segmentation'\n    README: configs/res2net/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.1.0/mmdet/models/backbones/res2net.py#L239\n      Version: v2.1.0\n"
  },
  {
    "path": "configs/resnest/README.md",
    "content": "# ResNeSt\n\n> [ResNeSt: Split-Attention Networks](https://arxiv.org/abs/2004.08955)\n\n<!-- [BACKBONE] -->\n\n## Abstract\n\nIt is well known that featuremap attention and multi-path representation are important for visual recognition. In this paper, we present a modularized architecture, which applies the channel-wise attention on different network branches to leverage their success in capturing cross-feature interactions and learning diverse representations. Our design results in a simple and unified computation block, which can be parameterized using only a few variables. Our model, named ResNeSt, outperforms EfficientNet in accuracy and latency trade-off on image classification. In addition, ResNeSt has achieved superior transfer learning results on several public benchmarks serving as the backbone, and has been adopted by the winning entries of COCO-LVIS challenge.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143973475-b5b33b15-ed04-4fc6-890a-521f1a62bc52.png\"/>\n</div>\n\n## Results and Models\n\n### Faster R-CNN\n\n| Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                                  Config                                   |                                                                                                                                                                                                                             Download                                                                                                                                                                                                                             |\n| :-------: | :-----: | :-----: | :------: | :------------: | :----: | :-----------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| S-50-FPN  | pytorch |   1x    |   4.8    |       -        |  42.0  | [config](./faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20200926_125502-20289c16.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco-20200926_125502.log.json)   |\n| S-101-FPN | pytorch |   1x    |   7.1    |       -        |  44.5  | [config](./faster-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/faster_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/faster_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20201006_021058-421517f1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/faster_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/faster_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco-20201006_021058.log.json) |\n\n### Mask R-CNN\n\n| Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                              Config                               |                                                                                                                                                                                                             Download                                                                                                                                                                                                             |\n| :-------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :---------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| S-50-FPN  | pytorch |   1x    |   5.5    |       -        |  42.6  |  38.1   | [config](./mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20200926_125503-8a2c3d47.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco-20200926_125503.log.json)   |\n| S-101-FPN | pytorch |   1x    |   7.8    |       -        |  45.2  |  40.2   | [config](./mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20201005_215831-af60cdf9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco-20201005_215831.log.json) |\n\n### Cascade R-CNN\n\n| Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                                   Config                                   |                                                                                                                                                                                                                              Download                                                                                                                                                                                                                              |\n| :-------: | :-----: | :-----: | :------: | :------------: | :----: | :------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| S-50-FPN  | pytorch |   1x    |    -     |       -        |  44.5  | [config](./cascade-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/cascade_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20201122_213640-763cc7b5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/cascade_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco-20201005_113242.log.json) |\n| S-101-FPN | pytorch |   1x    |   8.4    |       -        |  46.8  | [config](./cascade-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/cascade_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20201005_113242-b9459f8f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/cascade_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco-20201122_213640.log.json) |\n\n### Cascade Mask R-CNN\n\n| Backbone  |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                                  Config                                   |                                                                                                                                                                                                                             Download                                                                                                                                                                                                                             |\n| :-------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| S-50-FPN  | pytorch |   1x    |    -     |       -        |  45.4  |  39.5   | [config](./cascade-mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/cascade_mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20201122_104428-99eca4c7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/cascade_mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco-20201122_104428.log.json)   |\n| S-101-FPN | pytorch |   1x    |   10.5   |       -        |  47.7  |  41.4   | [config](./cascade-mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/cascade_mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20201005_113243-42607475.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/cascade_mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco-20201005_113243.log.json) |\n\n## Citation\n\n```latex\n@article{zhang2020resnest,\ntitle={ResNeSt: Split-Attention Networks},\nauthor={Zhang, Hang and Wu, Chongruo and Zhang, Zhongyue and Zhu, Yi and Zhang, Zhi and Lin, Haibin and Sun, Yue and He, Tong and Muller, Jonas and Manmatha, R. and Li, Mu and Smola, Alexander},\njournal={arXiv preprint arXiv:2004.08955},\nyear={2020}\n}\n```\n"
  },
  {
    "path": "configs/resnest/cascade-mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco.py",
    "content": "_base_ = './cascade-mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        stem_channels=128,\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='open-mmlab://resnest101')))\n"
  },
  {
    "path": "configs/resnest/cascade-mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\n\nmodel = dict(\n    # use ResNeSt img_norm\n    data_preprocessor=dict(\n        mean=[123.68, 116.779, 103.939],\n        std=[58.393, 57.12, 57.375],\n        bgr_to_rgb=True),\n    backbone=dict(\n        type='ResNeSt',\n        stem_channels=64,\n        depth=50,\n        radix=2,\n        reduction_factor=4,\n        avg_down_stride=True,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://resnest50')),\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared4Conv1FCBBoxHead',\n                in_channels=256,\n                conv_out_channels=256,\n                fc_out_channels=1024,\n                norm_cfg=norm_cfg,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared4Conv1FCBBoxHead',\n                in_channels=256,\n                conv_out_channels=256,\n                fc_out_channels=1024,\n                norm_cfg=norm_cfg,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared4Conv1FCBBoxHead',\n                in_channels=256,\n                conv_out_channels=256,\n                fc_out_channels=1024,\n                norm_cfg=norm_cfg,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_head=dict(norm_cfg=norm_cfg)))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/resnest/cascade-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco.py",
    "content": "_base_ = './cascade-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        stem_channels=128,\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='open-mmlab://resnest101')))\n"
  },
  {
    "path": "configs/resnest/cascade-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py",
    "content": "_base_ = '../cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\nmodel = dict(\n    # use ResNeSt img_norm\n    data_preprocessor=dict(\n        mean=[123.68, 116.779, 103.939],\n        std=[58.393, 57.12, 57.375],\n        bgr_to_rgb=True),\n    backbone=dict(\n        type='ResNeSt',\n        stem_channels=64,\n        depth=50,\n        radix=2,\n        reduction_factor=4,\n        avg_down_stride=True,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://resnest50')),\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared4Conv1FCBBoxHead',\n                in_channels=256,\n                conv_out_channels=256,\n                fc_out_channels=1024,\n                norm_cfg=norm_cfg,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared4Conv1FCBBoxHead',\n                in_channels=256,\n                conv_out_channels=256,\n                fc_out_channels=1024,\n                norm_cfg=norm_cfg,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared4Conv1FCBBoxHead',\n                in_channels=256,\n                conv_out_channels=256,\n                fc_out_channels=1024,\n                norm_cfg=norm_cfg,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ], ))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/resnest/faster-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco.py",
    "content": "_base_ = './faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        stem_channels=128,\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='open-mmlab://resnest101')))\n"
  },
  {
    "path": "configs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py",
    "content": "_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\nmodel = dict(\n    # use ResNeSt img_norm\n    data_preprocessor=dict(\n        mean=[123.68, 116.779, 103.939],\n        std=[58.393, 57.12, 57.375],\n        bgr_to_rgb=True),\n    backbone=dict(\n        type='ResNeSt',\n        stem_channels=64,\n        depth=50,\n        radix=2,\n        reduction_factor=4,\n        avg_down_stride=True,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://resnest50')),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=norm_cfg)))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/resnest/mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco.py",
    "content": "_base_ = './mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        stem_channels=128,\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='open-mmlab://resnest101')))\n"
  },
  {
    "path": "configs/resnest/mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py",
    "content": "_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\nmodel = dict(\n    # use ResNeSt img_norm\n    data_preprocessor=dict(\n        mean=[123.68, 116.779, 103.939],\n        std=[58.393, 57.12, 57.375],\n        bgr_to_rgb=True),\n    backbone=dict(\n        type='ResNeSt',\n        stem_channels=64,\n        depth=50,\n        radix=2,\n        reduction_factor=4,\n        avg_down_stride=True,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://resnest50')),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=norm_cfg),\n        mask_head=dict(norm_cfg=norm_cfg)))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/resnest/metafile.yml",
    "content": "Models:\n  - Name: faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/resnest/faster-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.8\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/faster_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20200926_125502-20289c16.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n\n  - Name: faster-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/resnest/faster-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/faster_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/faster_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20201006_021058-421517f1.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n\n  - Name: mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/resnest/mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.6\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20200926_125503-8a2c3d47.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n\n  - Name: mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/resnest/mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20201005_215831-af60cdf9.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n\n  - Name: cascade-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/resnest/cascade-rcnn_s50_fpn_syncbn-backbone+head_ms-range-1x_coco.py\n    Metadata:\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/cascade_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20201122_213640-763cc7b5.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n\n  - Name: cascade-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/resnest/cascade-rcnn_s101_fpn_syncbn-backbone+head_ms-range-1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.4\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco/cascade_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain-range_1x_coco_20201005_113242-b9459f8f.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n\n  - Name: cascade-mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/resnest/cascade-mask-rcnn_s50_fpn_syncbn-backbone+head_ms-1x_coco.py\n    Metadata:\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/cascade_mask_rcnn_s50_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20201122_104428-99eca4c7.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n\n  - Name: cascade-mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/resnest/cascade-mask-rcnn_s101_fpn_syncbn-backbone+head_ms-1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNeSt\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnest/cascade_mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco/cascade_mask_rcnn_s101_fpn_syncbn-backbone%2Bhead_mstrain_1x_coco_20201005_113243-42607475.pth\n    Paper:\n      URL: https://arxiv.org/abs/2004.08955\n      Title: 'ResNeSt: Split-Attention Networks'\n    README: configs/resnest/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.7.0/mmdet/models/backbones/resnest.py#L273\n      Version: v2.7.0\n"
  },
  {
    "path": "configs/resnet_strikes_back/README.md",
    "content": "# ResNet strikes back\n\n> [ResNet strikes back: An improved training procedure in timm](https://arxiv.org/abs/2110.00476)\n\n<!-- [OTHERS] -->\n\n## Abstract\n\nThe influential Residual Networks designed by He et al. remain the gold-standard architecture in numerous scientific publications. They typically serve as the default architecture in studies, or as baselines when new architectures are proposed. Yet there has been significant progress on best practices for training neural networks since the inception of the ResNet architecture in 2015. Novel optimization & dataaugmentation have increased the effectiveness of the training recipes.\n\nIn this paper, we re-evaluate the performance of the vanilla ResNet-50 when trained with a procedure that integrates such advances. We share competitive training settings and pre-trained models in the timm open-source library, with the hope that they will serve as better baselines for future work. For instance, with our more demanding training setting, a vanilla ResNet-50 reaches 80.4% top-1 accuracy at resolution 224×224 on ImageNet-val without extra data or distillation. We also report the performance achieved with popular models with our training procedure.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/12907710/149324625-4546a5a7-704f-406c-982f-0376a20d03d8.png\"/>\n</div>\n\n## Results and Models\n\n|       Method       | Backbone | Lr schd | Mem (GB) | Inf time (fps) |   box AP    |   mask AP   |                          Config                          |                                                                                                                                                                                                   Download                                                                                                                                                                                                   |\n| :----------------: | :------: | :-----: | :------: | :------------: | :---------: | :---------: | :------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    Faster R-CNN    | R-50 rsb |   1x    |   3.9    |       -        | 40.8 (+3.4) |      -      |    [Config](./faster-rcnn_r50-rsb-pre_fpn_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/faster_rcnn_r50_fpn_rsb-pretrain_1x_coco/faster_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_162229-32ae82a9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/faster_rcnn_r50_fpn_rsb-pretrain_1x_coco/faster_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_162229.log.json)             |\n|     Mask R-CNN     | R-50 rsb |   1x    |   4.5    |       -        | 41.2 (+3.0) | 38.2 (+3.0) |     [Config](./mask-rcnn_r50-rsb-pre_fpn_1x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_174054-06ce8ba0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_174054.log.json)                 |\n| Cascade Mask R-CNN | R-50 rsb |   1x    |   6.2    |       -        | 44.8 (+3.6) | 39.9 (+3.6) | [Config](./cascade-mask-rcnn_r50-rsb-pre_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/cascade_mask_rcnn_r50_fpn_rsb-pretrain_1x_coco/cascade_mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_193636-8b9ad50f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/cascade_mask_rcnn_r50_fpn_rsb-pretrain_1x_coco/cascade_mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_193636.log.json) |\n|     RetinaNet      | R-50 rsb |   1x    |   3.8    |       -        | 39.0 (+2.5) |      -      |     [Config](./retinanet_r50-rsb-pre_fpn_1x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/retinanet_r50_fpn_rsb-pretrain_1x_coco/retinanet_r50_fpn_rsb-pretrain_1x_coco_20220113_175432-bd24aae9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/retinanet_r50_fpn_rsb-pretrain_1x_coco/retinanet_r50_fpn_rsb-pretrain_1x_coco_20220113_175432.log.json)                 |\n\n**Notes:**\n\n- 'rsb' is short for 'resnet strikes back'\n- We have done some grid searches on learning rate and weight decay and get these optimal hyper-parameters.\n\n## Citation\n\n```latex\n@article{wightman2021resnet,\ntitle={Resnet strikes back: An improved training procedure in timm},\nauthor={Ross Wightman, Hugo Touvron, Hervé Jégou},\njournal={arXiv preprint arXiv:2110.00476},\nyear={2021}\n}\n```\n"
  },
  {
    "path": "configs/resnet_strikes_back/cascade-mask-rcnn_r50-rsb-pre_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb256-rsb-a1-600e_in1k_20211228-20e21305.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)))\n\noptim_wrapper = dict(\n    optimizer=dict(_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.05),\n    paramwise_cfg=dict(norm_decay_mult=0., bypass_duplicate=True))\n"
  },
  {
    "path": "configs/resnet_strikes_back/faster-rcnn_r50-rsb-pre_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb256-rsb-a1-600e_in1k_20211228-20e21305.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)))\n\noptim_wrapper = dict(\n    optimizer=dict(_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.05),\n    paramwise_cfg=dict(norm_decay_mult=0., bypass_duplicate=True))\n"
  },
  {
    "path": "configs/resnet_strikes_back/mask-rcnn_r50-rsb-pre_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb256-rsb-a1-600e_in1k_20211228-20e21305.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)))\n\noptim_wrapper = dict(\n    optimizer=dict(_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.05),\n    paramwise_cfg=dict(norm_decay_mult=0., bypass_duplicate=True))\n"
  },
  {
    "path": "configs/resnet_strikes_back/metafile.yml",
    "content": "Models:\n  - Name: faster-rcnn_r50_fpn_rsb-pretrain_1x_coco\n    In Collection: Faster R-CNN\n    Config: configs/resnet_strikes_back/faster-rcnn_r50-rsb-pre_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.9\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/faster_rcnn_r50_fpn_rsb-pretrain_1x_coco/faster_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_162229-32ae82a9.pth\n    Paper:\n      URL: https://arxiv.org/abs/2110.00476\n      Title: 'ResNet strikes back: An improved training procedure in timm'\n    README: configs/resnet_strikes_back/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.22.0/configs/resnet_strikes_back/README.md\n      Version: v2.22.0\n\n  - Name: cascade-mask-rcnn_r50_fpn_rsb-pretrain_1x_coco\n    In Collection: Cascade R-CNN\n    Config: configs/resnet_strikes_back/cascade-mask-rcnn_r50-rsb-pre_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 6.2\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/cascade_mask_rcnn_r50_fpn_rsb-pretrain_1x_coco/cascade_mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_193636-8b9ad50f.pth\n    Paper:\n      URL: https://arxiv.org/abs/2110.00476\n      Title: 'ResNet strikes back: An improved training procedure in timm'\n    README: configs/resnet_strikes_back/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.22.0/configs/resnet_strikes_back/README.md\n      Version: v2.22.0\n\n  - Name: retinanet_r50-rsb-pre_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/resnet_strikes_back/retinanet_r50-rsb-pre_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.8\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/retinanet_r50_fpn_rsb-pretrain_1x_coco/retinanet_r50_fpn_rsb-pretrain_1x_coco_20220113_175432-bd24aae9.pth\n    Paper:\n      URL: https://arxiv.org/abs/2110.00476\n      Title: 'ResNet strikes back: An improved training procedure in timm'\n    README: configs/resnet_strikes_back/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.22.0/configs/resnet_strikes_back/README.md\n      Version: v2.22.0\n\n  - Name: mask-rcnn_r50_fpn_rsb-pretrain_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/resnet_strikes_back/mask-rcnn_r50-rsb-pre_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.5\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/resnet_strikes_back/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco/mask_rcnn_r50_fpn_rsb-pretrain_1x_coco_20220113_174054-06ce8ba0.pth\n    Paper:\n      URL: https://arxiv.org/abs/2110.00476\n      Title: 'ResNet strikes back: An improved training procedure in timm'\n    README: configs/resnet_strikes_back/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.22.0/configs/resnet_strikes_back/README.md\n      Version: v2.22.0\n"
  },
  {
    "path": "configs/resnet_strikes_back/retinanet_r50-rsb-pre_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb256-rsb-a1-600e_in1k_20211228-20e21305.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)))\n\noptim_wrapper = dict(\n    optimizer=dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05),\n    paramwise_cfg=dict(norm_decay_mult=0., bypass_duplicate=True))\n"
  },
  {
    "path": "configs/retinanet/README.md",
    "content": "# RetinaNet\n\n> [Focal Loss for Dense Object Detection](https://arxiv.org/abs/1708.02002)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThe highest accuracy object detectors to date are based on a two-stage approach popularized by R-CNN, where a classifier is applied to a sparse set of candidate object locations. In contrast, one-stage detectors that are applied over a regular, dense sampling of possible object locations have the potential to be faster and simpler, but have trailed the accuracy of two-stage detectors thus far. In this paper, we investigate why this is the case. We discover that the extreme foreground-background class imbalance encountered during training of dense detectors is the central cause. We propose to address this class imbalance by reshaping the standard cross entropy loss such that it down-weights the loss assigned to well-classified examples. Our novel Focal Loss focuses training on a sparse set of hard examples and prevents the vast number of easy negatives from overwhelming the detector during training. To evaluate the effectiveness of our loss, we design and train a simple dense detector we call RetinaNet. Our results show that when trained with the focal loss, RetinaNet is able to match the speed of previous one-stage detectors while surpassing the accuracy of all existing state-of-the-art two-stage detectors.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143973551-2b8e766a-1677-4f6d-953d-2e6d2a3c67b5.png\" height=\"300\"/>\n</div>\n\n## Results and Models\n\n|    Backbone     |  Style  |   Lr schd    | Mem (GB) | Inf time (fps) | box AP |                     Config                      |                                                                                                                                                         Download                                                                                                                                                          |\n| :-------------: | :-----: | :----------: | :------: | :------------: | :----: | :---------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-18-FPN     | pytorch |      1x      |   1.7    |                |  31.7  |    [config](./retinanet_r18_fpn_1x_coco.py)     |           [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r18_fpn_1x_coco/retinanet_r18_fpn_1x_coco_20220407_171055-614fd399.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r18_fpn_1x_coco/retinanet_r18_fpn_1x_coco_20220407_171055.log.json)            |\n|    R-18-FPN     | pytorch | 1x(1 x 8 BS) |   5.0    |                |  31.7  |  [config](./retinanet_r18_fpn_1xb8-1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r18_fpn_1x8_1x_coco/retinanet_r18_fpn_1x8_1x_coco_20220407_171255-4ea310d7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r18_fpn_1x8_1x_coco/retinanet_r18_fpn_1x8_1x_coco_20220407_171255.log.json)    |\n|    R-50-FPN     |  caffe  |      1x      |   3.5    |      18.6      |  36.3  | [config](./retinanet_r50-caffe_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_caffe_fpn_1x_coco/retinanet_r50_caffe_fpn_1x_coco_20200531-f11027c5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_caffe_fpn_1x_coco/retinanet_r50_caffe_fpn_1x_coco_20200531_012518.log.json)   |\n|    R-50-FPN     | pytorch |      1x      |   3.8    |      19.0      |  36.5  |    [config](./retinanet_r50_fpn_1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130_002941.log.json)               |\n| R-50-FPN (FP16) | pytorch |      1x      |   2.8    |      31.6      |  36.4  |  [config](./retinanet_r50_fpn_amp-1x_coco.py)   |          [model](https://download.openmmlab.com/mmdetection/v2.0/fp16/retinanet_r50_fpn_fp16_1x_coco/retinanet_r50_fpn_fp16_1x_coco_20200702-0dbfb212.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/fp16/retinanet_r50_fpn_fp16_1x_coco/retinanet_r50_fpn_fp16_1x_coco_20200702_020127.log.json)          |\n|    R-50-FPN     | pytorch |      2x      |    -     |       -        |  37.4  |    [config](./retinanet_r50_fpn_2x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_2x_coco/retinanet_r50_fpn_2x_coco_20200131-fdb43119.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_2x_coco/retinanet_r50_fpn_2x_coco_20200131_114738.log.json)               |\n|    R-101-FPN    |  caffe  |      1x      |   5.5    |      14.7      |  38.5  | [config](./retinanet_r101-caffe_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_caffe_fpn_1x_coco/retinanet_r101_caffe_fpn_1x_coco_20200531-b428fa0f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_caffe_fpn_1x_coco/retinanet_r101_caffe_fpn_1x_coco_20200531_012536.log.json) |\n|    R-101-FPN    | pytorch |      1x      |   5.7    |      15.0      |  38.5  |    [config](./retinanet_r101_fpn_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_1x_coco/retinanet_r101_fpn_1x_coco_20200130-7a93545f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_1x_coco/retinanet_r101_fpn_1x_coco_20200130_003055.log.json)             |\n|    R-101-FPN    | pytorch |      2x      |    -     |       -        |  38.9  |    [config](./retinanet_r101_fpn_2x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_2x_coco/retinanet_r101_fpn_2x_coco_20200131-5560aee8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_2x_coco/retinanet_r101_fpn_2x_coco_20200131_114859.log.json)             |\n| X-101-32x4d-FPN | pytorch |      1x      |   7.0    |      12.1      |  39.9  | [config](./retinanet_x101-32x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_32x4d_fpn_1x_coco/retinanet_x101_32x4d_fpn_1x_coco_20200130-5c8b7ec4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_32x4d_fpn_1x_coco/retinanet_x101_32x4d_fpn_1x_coco_20200130_003004.log.json) |\n| X-101-32x4d-FPN | pytorch |      2x      |    -     |       -        |  40.1  | [config](./retinanet_x101-32x4d_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_32x4d_fpn_2x_coco/retinanet_x101_32x4d_fpn_2x_coco_20200131-237fc5e1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_32x4d_fpn_2x_coco/retinanet_x101_32x4d_fpn_2x_coco_20200131_114812.log.json) |\n| X-101-64x4d-FPN | pytorch |      1x      |   10.0   |      8.7       |  41.0  | [config](./retinanet_x101-64x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_1x_coco/retinanet_x101_64x4d_fpn_1x_coco_20200130-366f5af1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_1x_coco/retinanet_x101_64x4d_fpn_1x_coco_20200130_003008.log.json) |\n| X-101-64x4d-FPN | pytorch |      2x      |    -     |       -        |  40.8  | [config](./retinanet_x101-64x4d_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_2x_coco/retinanet_x101_64x4d_fpn_2x_coco_20200131-bca068ab.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_2x_coco/retinanet_x101_64x4d_fpn_2x_coco_20200131_114833.log.json) |\n\n## Pre-trained Models\n\nWe also train some models with longer schedules and multi-scale training. The users could finetune them for downstream tasks.\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | box AP |                           Config                           |                                                                                                                                                                                 Download                                                                                                                                                                                  |\n| :-------------: | :-----: | :-----: | :------: | :----: | :--------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |   3x    |   3.5    |  39.5  |    [config](./retinanet_r50_fpn_ms-640-800-3x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_mstrain_3x_coco/retinanet_r50_fpn_mstrain_3x_coco_20210718_220633-88476508.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_mstrain_3x_coco/retinanet_r50_fpn_mstrain_3x_coco_20210718_220633-88476508.log.json)               |\n|    R-101-FPN    |  caffe  |   3x    |   5.4    |  40.7  |     [config](./retinanet_r101-caffe_fpn_ms-3x_coco.py)     | [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_caffe_fpn_mstrain_3x_coco/retinanet_r101_caffe_fpn_mstrain_3x_coco_20210721_063439-88a8a944.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_caffe_fpn_mstrain_3x_coco/retinanet_r101_caffe_fpn_mstrain_3x_coco_20210721_063439-88a8a944.log.json) |\n|    R-101-FPN    | pytorch |   3x    |   5.4    |   41   |    [config](./retinanet_r101_fpn_ms-640-800-3x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_mstrain_3x_coco/retinanet_r101_fpn_mstrain_3x_coco_20210720_214650-7ee888e0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_mstrain_3x_coco/retinanet_r101_fpn_mstrain_3x_coco_20210720_214650-7ee888e0.log.json)             |\n| X-101-64x4d-FPN | pytorch |   3x    |   9.8    |  41.6  | [config](./retinanet_x101-64x4d_fpn_ms-640-800-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_mstrain_3x_coco/retinanet_x101_64x4d_fpn_mstrain_3x_coco_20210719_051838-022c2187.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_mstrain_3x_coco/retinanet_x101_64x4d_fpn_mstrain_3x_coco_20210719_051838-022c2187.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{lin2017focal,\n  title={Focal loss for dense object detection},\n  author={Lin, Tsung-Yi and Goyal, Priya and Girshick, Ross and He, Kaiming and Doll{\\'a}r, Piotr},\n  booktitle={Proceedings of the IEEE international conference on computer vision},\n  year={2017}\n}\n```\n"
  },
  {
    "path": "configs/retinanet/metafile.yml",
    "content": "Collections:\n  - Name: RetinaNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Focal Loss\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1708.02002\n      Title: \"Focal Loss for Dense Object Detection\"\n    README: configs/retinanet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/retinanet.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: retinanet_r18_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r18_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 1.7\n      Training Resources: 8x V100 GPUs\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 31.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r18_fpn_1x_coco/retinanet_r18_fpn_1x_coco_20220407_171055-614fd399.pth\n\n  - Name: retinanet_r18_fpn_1xb8-1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r18_fpn_1xb8-1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.0\n      Training Resources:  1x V100 GPUs\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 31.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r18_fpn_1x8_1x_coco/retinanet_r18_fpn_1x8_1x_coco_20220407_171255-4ea310d7.pth\n\n  - Name: retinanet_r50-caffe_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.5\n      inference time (ms/im):\n        - value: 53.76\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_caffe_fpn_1x_coco/retinanet_r50_caffe_fpn_1x_coco_20200531-f11027c5.pth\n\n  - Name: retinanet_r50_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.8\n      inference time (ms/im):\n        - value: 52.63\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth\n\n  - Name: retinanet_r50_fpn_amp-1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r50_fpn_amp-1x_coco.py\n    Metadata:\n      Training Memory (GB): 2.8\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n        - Mixed Precision Training\n      inference time (ms/im):\n        - value: 31.65\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP16\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 36.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/fp16/retinanet_r50_fpn_fp16_1x_coco/retinanet_r50_fpn_fp16_1x_coco_20200702-0dbfb212.pth\n\n  - Name: retinanet_r50_fpn_2x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r50_fpn_2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_2x_coco/retinanet_r50_fpn_2x_coco_20200131-fdb43119.pth\n\n  - Name: retinanet_r50_fpn_ms-640-800-3x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r50_fpn_ms-640-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_mstrain_3x_coco/retinanet_r50_fpn_mstrain_3x_coco_20210718_220633-88476508.pth\n\n  - Name: retinanet_r101-caffe_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.5\n      inference time (ms/im):\n        - value: 68.03\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_caffe_fpn_1x_coco/retinanet_r101_caffe_fpn_1x_coco_20200531-b428fa0f.pth\n\n  - Name: retinanet_r101-caffe_fpn_ms-3x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r101-caffe_fpn_ms-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_caffe_fpn_mstrain_3x_coco/retinanet_r101_caffe_fpn_mstrain_3x_coco_20210721_063439-88a8a944.pth\n\n  - Name: retinanet_r101_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r101_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.7\n      inference time (ms/im):\n        - value: 66.67\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_1x_coco/retinanet_r101_fpn_1x_coco_20200130-7a93545f.pth\n\n  - Name: retinanet_r101_fpn_2x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r101_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 5.7\n      inference time (ms/im):\n        - value: 66.67\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_2x_coco/retinanet_r101_fpn_2x_coco_20200131-5560aee8.pth\n\n  - Name: retinanet_r101_fpn_ms-640-800-3x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_r101_fpn_ms-640-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r101_fpn_mstrain_3x_coco/retinanet_r101_fpn_mstrain_3x_coco_20210720_214650-7ee888e0.pth\n\n  - Name: retinanet_x101-32x4d_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 82.64\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_32x4d_fpn_1x_coco/retinanet_x101_32x4d_fpn_1x_coco_20200130-5c8b7ec4.pth\n\n  - Name: retinanet_x101-32x4d_fpn_2x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_x101-32x4d_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 82.64\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_32x4d_fpn_2x_coco/retinanet_x101_32x4d_fpn_2x_coco_20200131-237fc5e1.pth\n\n  - Name: retinanet_x101-64x4d_fpn_1x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.0\n      inference time (ms/im):\n        - value: 114.94\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_1x_coco/retinanet_x101_64x4d_fpn_1x_coco_20200130-366f5af1.pth\n\n  - Name: retinanet_x101-64x4d_fpn_2x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_x101-64x4d_fpn_2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.0\n      inference time (ms/im):\n        - value: 114.94\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_2x_coco/retinanet_x101_64x4d_fpn_2x_coco_20200131-bca068ab.pth\n\n  - Name: retinanet_x101-64x4d_fpn_ms-640-800-3x_coco\n    In Collection: RetinaNet\n    Config: configs/retinanet/retinanet_x101-64x4d_fpn_ms-640-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_x101_64x4d_fpn_mstrain_3x_coco/retinanet_x101_64x4d_fpn_mstrain_3x_coco_20210719_051838-022c2187.pth\n"
  },
  {
    "path": "configs/retinanet/retinanet_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './retinanet_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r101-caffe_fpn_ms-3x_coco.py",
    "content": "_base_ = './retinanet_r50-caffe_fpn_ms-3x_coco.py'\n# learning policy\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r101_fpn_1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r101_fpn_2x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r101_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r101_fpn_ms-640-800-3x_coco.py",
    "content": "_base_ = ['../_base_/models/retinanet_r50_fpn.py', '../common/ms_3x_coco.py']\n# optimizer\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r18_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# TODO: support auto scaling lr\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\n# auto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "configs/retinanet/retinanet_r18_fpn_1xb8-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# data\ntrain_dataloader = dict(batch_size=8)\n\n# model\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n\n# Note: If the learning rate is set to 0.0025, the mAP will be 32.4.\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.005, momentum=0.9, weight_decay=0.0001))\n# TODO: support auto scaling lr\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (1 GPUs) x (8 samples per GPU)\n# auto_scale_lr = dict(base_batch_size=8)\n"
  },
  {
    "path": "configs/retinanet/retinanet_r18_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_8xb8-amp-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        # use caffe img_norm\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50-caffe_fpn_ms-1x_coco.py",
    "content": "_base_ = './retinanet_r50-caffe_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50-caffe_fpn_ms-2x_coco.py",
    "content": "_base_ = './retinanet_r50-caffe_fpn_ms-1x_coco.py'\n# training schedule for 2x\ntrain_cfg = dict(max_epochs=24)\n\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50-caffe_fpn_ms-3x_coco.py",
    "content": "_base_ = './retinanet_r50-caffe_fpn_ms-1x_coco.py'\n\n# training schedule for 2x\ntrain_cfg = dict(max_epochs=36)\n\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=36,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py',\n    './retinanet_tta.py'\n]\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50_fpn_2x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# training schedule for 2x\ntrain_cfg = dict(max_epochs=24)\n\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50_fpn_8xb8-amp-lsj-200e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../common/lsj-200e_coco-detection.py'\n]\n\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\nmodel = dict(data_preprocessor=dict(batch_augments=batch_augments))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.01 * 4, momentum=0.9, weight_decay=0.00004))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50_fpn_90k_coco.py",
    "content": "_base_ = 'retinanet_r50_fpn_1x_coco.py'\n\n# training schedule for 90k\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=90000,\n    val_interval=10000)\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=90000,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\ntrain_dataloader = dict(sampler=dict(type='InfiniteSampler'))\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\n\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50_fpn_amp-1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_1x_coco.py'\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# optim_wrapper = dict(type='AmpOptimWrapper')\n_base_.optim_wrapper.type = 'AmpOptimWrapper'\n"
  },
  {
    "path": "configs/retinanet/retinanet_r50_fpn_ms-640-800-3x_coco.py",
    "content": "_base_ = ['../_base_/models/retinanet_r50_fpn.py', '../common/ms_3x_coco.py']\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/retinanet/retinanet_tta.py",
    "content": "tta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(type='nms', iou_threshold=0.5), max_per_img=100))\n\nimg_scales = [(1333, 800), (666, 400), (2000, 1200)]\ntta_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[[\n            dict(type='Resize', scale=s, keep_ratio=True) for s in img_scales\n        ], [\n            dict(type='RandomFlip', prob=1.),\n            dict(type='RandomFlip', prob=0.)\n        ], [dict(type='LoadAnnotations', with_bbox=True)],\n                    [\n                        dict(\n                            type='PackDetInputs',\n                            meta_keys=('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'flip',\n                                       'flip_direction'))\n                    ]])\n]\n"
  },
  {
    "path": "configs/retinanet/retinanet_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_x101-32x4d_fpn_2x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_x101-64x4d_fpn_2x_coco.py",
    "content": "_base_ = './retinanet_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/retinanet/retinanet_x101-64x4d_fpn_ms-640-800-3x_coco.py",
    "content": "_base_ = ['../_base_/models/retinanet_r50_fpn.py', '../common/ms_3x_coco.py']\n# optimizer\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\noptim_wrapper = dict(optimizer=dict(type='SGD', lr=0.01))\n"
  },
  {
    "path": "configs/rpn/README.md",
    "content": "# RPN\n\n> [Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks](https://arxiv.org/abs/1506.01497)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nState-of-the-art object detection networks depend on region proposal algorithms to hypothesize object locations. Advances like SPPnet and Fast R-CNN have reduced the running time of these detection networks, exposing region proposal computation as a bottleneck. In this work, we introduce a Region Proposal Network (RPN) that shares full-image convolutional features with the detection network, thus enabling nearly cost-free region proposals. An RPN is a fully convolutional network that simultaneously predicts object bounds and objectness scores at each position. The RPN is trained end-to-end to generate high-quality region proposals, which are used by Fast R-CNN for detection. We further merge RPN and Fast R-CNN into a single network by sharing their convolutional features---using the recently popular terminology of neural networks with 'attention' mechanisms, the RPN component tells the unified network where to look. For the very deep VGG-16 model, our detection system has a frame rate of 5fps (including all steps) on a GPU, while achieving state-of-the-art object detection accuracy on PASCAL VOC 2007, 2012, and MS COCO datasets with only 300 proposals per image. In ILSVRC and COCO 2015 competitions, Faster R-CNN and RPN are the foundations of the 1st-place winning entries in several tracks.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143973617-387c7561-82f4-40b2-b78e-4776394b1b8b.png\" height=\"300\"/>\n</div>\n\n## Results and Models\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | AR1000 |                  Config                   |                                                                                                                                       Download                                                                                                                                        |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :---------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     |  caffe  |   1x    |   3.5    |      22.6      |  58.7  | [config](./rpn_r50-caffe_fpn_1x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_caffe_fpn_1x_coco/rpn_r50_caffe_fpn_1x_coco_20200531-5b903a37.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_caffe_fpn_1x_coco/rpn_r50_caffe_fpn_1x_coco_20200531_012334.log.json)   |\n|    R-50-FPN     | pytorch |   1x    |   3.8    |      22.3      |  58.2  |    [config](./rpn_r50_fpn_1x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_fpn_1x_coco/rpn_r50_fpn_1x_coco_20200218-5525fa2e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_fpn_1x_coco/rpn_r50_fpn_1x_coco_20200218_151240.log.json)               |\n|    R-50-FPN     | pytorch |   2x    |    -     |       -        |  58.6  |    [config](./rpn_r50_fpn_2x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_fpn_2x_coco/rpn_r50_fpn_2x_coco_20200131-0728c9b3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_fpn_2x_coco/rpn_r50_fpn_2x_coco_20200131_190631.log.json)               |\n|    R-101-FPN    |  caffe  |   1x    |   5.4    |      17.3      |  60.0  | [config](./rpn_r101-caffe_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r101_caffe_fpn_1x_coco/rpn_r101_caffe_fpn_1x_coco_20200531-0629a2e2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r101_caffe_fpn_1x_coco/rpn_r101_caffe_fpn_1x_coco_20200531_012345.log.json) |\n|    R-101-FPN    | pytorch |   1x    |   5.8    |      16.5      |  59.7  |    [config](./rpn_r101_fpn_1x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r101_fpn_1x_coco/rpn_r101_fpn_1x_coco_20200131-2ace2249.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r101_fpn_1x_coco/rpn_r101_fpn_1x_coco_20200131_191000.log.json)             |\n|    R-101-FPN    | pytorch |   2x    |    -     |       -        |  60.2  |    [config](./rpn_r101_fpn_2x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r101_fpn_2x_coco/rpn_r101_fpn_2x_coco_20200131-24e3db1a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r101_fpn_2x_coco/rpn_r101_fpn_2x_coco_20200131_191106.log.json)             |\n| X-101-32x4d-FPN | pytorch |   1x    |   7.0    |      13.0      |  60.6  | [config](./rpn_x101-32x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_32x4d_fpn_1x_coco/rpn_x101_32x4d_fpn_1x_coco_20200219-b02646c6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_32x4d_fpn_1x_coco/rpn_x101_32x4d_fpn_1x_coco_20200219_012037.log.json) |\n| X-101-32x4d-FPN | pytorch |   2x    |    -     |       -        |  61.1  | [config](./rpn_x101-32x4d_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_32x4d_fpn_2x_coco/rpn_x101_32x4d_fpn_2x_coco_20200208-d22bd0bb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_32x4d_fpn_2x_coco/rpn_x101_32x4d_fpn_2x_coco_20200208_200752.log.json) |\n| X-101-64x4d-FPN | pytorch |   1x    |   10.1   |      9.1       |  61.0  | [config](./rpn_x101-64x4d_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_64x4d_fpn_1x_coco/rpn_x101_64x4d_fpn_1x_coco_20200208-cde6f7dd.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_64x4d_fpn_1x_coco/rpn_x101_64x4d_fpn_1x_coco_20200208_200752.log.json) |\n| X-101-64x4d-FPN | pytorch |   2x    |    -     |       -        |  61.5  | [config](./rpn_x101-64x4d_fpn_2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_64x4d_fpn_2x_coco/rpn_x101_64x4d_fpn_2x_coco_20200208-c65f524f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_64x4d_fpn_2x_coco/rpn_x101_64x4d_fpn_2x_coco_20200208_200752.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{ren2015faster,\n  title={Faster r-cnn: Towards real-time object detection with region proposal networks},\n  author={Ren, Shaoqing and He, Kaiming and Girshick, Ross and Sun, Jian},\n  booktitle={Advances in neural information processing systems},\n  year={2015}\n}\n```\n"
  },
  {
    "path": "configs/rpn/metafile.yml",
    "content": "Collections:\n  - Name: RPN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1506.01497\n      Title: \"Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks\"\n    README: configs/rpn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/mmdet/models/detectors/rpn.py#L6\n      Version: v2.0.0\n\nModels:\n  - Name: rpn_r50-caffe_fpn_1x_coco\n    In Collection: RPN\n    Config: configs/rpn/rpn_r50-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.5\n      Training Resources: 8x V100 GPUs\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 58.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_caffe_fpn_1x_coco/rpn_r50_caffe_fpn_1x_coco_20200531-5b903a37.pth\n\n  - Name: rpn_r50_fpn_1x_coco\n    In Collection: RPN\n    Config: configs/rpn/rpn_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 3.8\n      Training Resources: 8x V100 GPUs\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 58.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_fpn_1x_coco/rpn_r50_fpn_1x_coco_20200218-5525fa2e.pth\n\n  - Name: rpn_r50_fpn_2x_coco\n    In Collection: RPN\n    Config: rpn_r50_fpn_2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 58.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r50_fpn_2x_coco/rpn_r50_fpn_2x_coco_20200131-0728c9b3.pth\n\n  - Name: rpn_r101-caffe_fpn_1x_coco\n    In Collection: RPN\n    Config: configs/rpn/rpn_r101-caffe_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.4\n      Training Resources: 8x V100 GPUs\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 60.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_r101_caffe_fpn_1x_coco/rpn_r101_caffe_fpn_1x_coco_20200531-0629a2e2.pth\n\n  - Name: rpn_x101-32x4d_fpn_1x_coco\n    In Collection: RPN\n    Config: configs/rpn/rpn_x101-32x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      Training Resources: 8x V100 GPUs\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 60.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_32x4d_fpn_1x_coco/rpn_x101_32x4d_fpn_1x_coco_20200219-b02646c6.pth\n\n  - Name: rpn_x101-32x4d_fpn_2x_coco\n    In Collection: RPN\n    Config: configs/rpn/rpn_x101-32x4d_fpn_2x_coco.py\n    Metadata:\n      Training Resources: 8x V100 GPUs\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 61.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_32x4d_fpn_2x_coco/rpn_x101_32x4d_fpn_2x_coco_20200208-d22bd0bb.pth\n\n  - Name: rpn_x101-64x4d_fpn_1x_coco\n    In Collection: RPN\n    Config: configs/rpn/rpn_x101-64x4d_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 10.1\n      Training Resources: 8x V100 GPUs\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 61.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_64x4d_fpn_1x_coco/rpn_x101_64x4d_fpn_1x_coco_20200208-cde6f7dd.pth\n\n  - Name: rpn_x101-64x4d_fpn_2x_coco\n    In Collection: RPN\n    Config: configs/rpn/rpn_x101-64x4d_fpn_2x_coco.py\n    Metadata:\n      Training Resources: 8x V100 GPUs\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          AR@1000: 61.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/rpn/rpn_x101_64x4d_fpn_2x_coco/rpn_x101_64x4d_fpn_2x_coco_20200208-c65f524f.pth\n"
  },
  {
    "path": "configs/rpn/rpn_r101-caffe_fpn_1x_coco.py",
    "content": "_base_ = './rpn_r50-caffe_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "configs/rpn/rpn_r101_fpn_1x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/rpn/rpn_r101_fpn_2x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/rpn/rpn_r50-caffe-c4_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/rpn_r50-caffe-c4.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nval_evaluator = dict(metric='proposal_fast')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/rpn/rpn_r50-caffe_fpn_1x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_1x_coco.py'\n# use caffe img_norm\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "configs/rpn/rpn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/rpn_r50_fpn.py', '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nval_evaluator = dict(metric='proposal_fast')\ntest_evaluator = val_evaluator\n\n# inference on val dataset and dump the proposals with evaluate metric\n# data_root = 'data/coco/'\n# test_evaluator = [\n#     dict(\n#         type='DumpProposals',\n#         output_dir=data_root + 'proposals/',\n#         proposals_file='rpn_r50_fpn_1x_val2017.pkl'),\n#     dict(\n#         type='CocoMetric',\n#         ann_file=data_root + 'annotations/instances_val2017.json',\n#         metric='proposal_fast',\n#         backend_args={{_base_.backend_args}},\n#         format_only=False)\n# ]\n\n# inference on training dataset and dump the proposals without evaluate metric\n# data_root = 'data/coco/'\n# test_dataloader = dict(\n#     dataset=dict(\n#         ann_file='annotations/instances_train2017.json',\n#         data_prefix=dict(img='train2017/')))\n#\n# test_evaluator = [\n#     dict(\n#         type='DumpProposals',\n#         output_dir=data_root + 'proposals/',\n#         proposals_file='rpn_r50_fpn_1x_train2017.pkl'),\n# ]\n"
  },
  {
    "path": "configs/rpn/rpn_r50_fpn_2x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_1x_coco.py'\n\n# learning policy\nmax_epochs = 24\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/rpn/rpn_x101-32x4d_fpn_1x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/rpn/rpn_x101-32x4d_fpn_2x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/rpn/rpn_x101-64x4d_fpn_1x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/rpn/rpn_x101-64x4d_fpn_2x_coco.py",
    "content": "_base_ = './rpn_r50_fpn_2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/rtmdet/README.md",
    "content": "# RTMDet: An Empirical Study of Designing Real-Time Object Detectors\n\n> [RTMDet: An Empirical Study of Designing Real-Time Object Detectors](https://arxiv.org/abs/2212.07784)\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/real-time-instance-segmentation-on-mscoco)](https://paperswithcode.com/sota/real-time-instance-segmentation-on-mscoco?p=rtmdet-an-empirical-study-of-designing-real)\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-dota-1)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-dota-1?p=rtmdet-an-empirical-study-of-designing-real)\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-hrsc2016)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-hrsc2016?p=rtmdet-an-empirical-study-of-designing-real)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIn this paper, we aim to design an efficient real-time object detector that exceeds the YOLO series and is easily extensible for many object recognition tasks such as instance segmentation and rotated object detection. To obtain a more efficient model architecture, we explore an architecture that has compatible capacities in the backbone and neck, constructed by a basic building block that consists of large-kernel depth-wise convolutions. We further introduce soft labels when calculating matching costs in the dynamic label assignment to improve accuracy. Together with better training techniques, the resulting object detector, named RTMDet, achieves 52.8% AP on COCO with 300+ FPS on an NVIDIA 3090 GPU, outperforming the current mainstream industrial detectors. RTMDet achieves the best parameter-accuracy trade-off with tiny/small/medium/large/extra-large model sizes for various application scenarios, and obtains new state-of-the-art performance on real-time instance segmentation and rotated object detection. We hope the experimental results can provide new insights into designing versatile real-time object detectors for many object recognition tasks.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/12907710/208070055-7233a3d8-955f-486a-82da-b714b3c3bbd6.png\"/>\n</div>\n\n## Results and Models\n\n### Object Detection\n\n|        Model        | size | box AP | Params(M) | FLOPS(G) | TRT-FP16-Latency(ms)<br>RTX3090 | TRT-FP16-Latency(ms)<br>T4 |                       Config                       |                                                                                                                                                Download                                                                                                                                                |\n| :-----------------: | :--: | :----: | :-------: | :------: | :-----------------------------: | :------------------------: | :------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     RTMDet-tiny     | 640  |  41.1  |    4.8    |   8.1    |              0.98               |            2.34            |     [config](./rtmdet_tiny_8xb32-300e_coco.py)     | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_tiny_8xb32-300e_coco/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_tiny_8xb32-300e_coco/rtmdet_tiny_8xb32-300e_coco_20220902_112414.log.json) |\n|      RTMDet-s       | 640  |  44.6  |   8.89    |   14.8   |              1.22               |            2.96            |      [config](./rtmdet_s_8xb32-300e_coco.py)       |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_s_8xb32-300e_coco/rtmdet_s_8xb32-300e_coco_20220905_161602-387a891e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_s_8xb32-300e_coco/rtmdet_s_8xb32-300e_coco_20220905_161602.log.json)       |\n|      RTMDet-m       | 640  |  49.4  |   24.71   |  39.27   |              1.62               |            6.41            |      [config](./rtmdet_m_8xb32-300e_coco.py)       |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_m_8xb32-300e_coco/rtmdet_m_8xb32-300e_coco_20220719_112220-229f527c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_m_8xb32-300e_coco/rtmdet_m_8xb32-300e_coco_20220719_112220.log.json)       |\n|      RTMDet-l       | 640  |  51.5  |   52.3    |  80.23   |              2.44               |           10.32            |      [config](./rtmdet_l_8xb32-300e_coco.py)       |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_l_8xb32-300e_coco/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_l_8xb32-300e_coco/rtmdet_l_8xb32-300e_coco_20220719_112030.log.json)       |\n|      RTMDet-x       | 640  |  52.8  |   94.86   |  141.67  |              3.10               |           18.80            |      [config](./rtmdet_x_8xb32-300e_coco.py)       |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_x_8xb32-300e_coco/rtmdet_x_8xb32-300e_coco_20220715_230555-cc79b9ae.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_x_8xb32-300e_coco/rtmdet_x_8xb32-300e_coco_20220715_230555.log.json)       |\n|     RTMDet-x-P6     | 1280 |  54.9  |           |          |                                 |                            |     [config](./rtmdet_x_p6_4xb8-300e_coco.py)      |                                                                                    [model](https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-p6/rtmdet_x_p6_4xb8-300e_coco-bf32be58.pth)                                                                                     |\n| RTMDet-l-ConvNeXt-B | 640  |  53.1  |           |          |                                 |                            | [config](./rtmdet_l_convnext_b_4xb32-100e_coco.py) |                                                                          [model](https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-swin-convnext/rtmdet_l_convnext_b_4xb32-100e_coco-d4731b3d.pth)                                                                           |\n|   RTMDet-l-Swin-B   | 640  |  52.4  |           |          |                                 |                            |   [config](./rtmdet_l_swin_b_4xb32-100e_coco.py)   |                                                                            [model](https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-swin-convnext/rtmdet_l_swin_b_4xb32-100e_coco-0828ce5d.pth)                                                                             |\n| RTMDet-l-Swin-B-P6  | 1280 |  56.4  |           |          |                                 |                            | [config](./rtmdet_l_swin_b_p6_4xb16-100e_coco.py)  |                                                                           [model](https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-swin-convnext/rtmdet_l_swin_b_p6_4xb16-100e_coco-a1486b6f.pth)                                                                           |\n\n**Note**:\n\n1. We implement a fast training version of RTMDet in [MMYOLO](https://github.com/open-mmlab/mmyolo). Its training speed is **2.6 times faster** and memory requirement is lower! Try it [here](https://github.com/open-mmlab/mmyolo/tree/main/configs/rtmdet)!\n2. The inference speed of RTMDet is measured with TensorRT 8.4.3, cuDNN 8.2.0, FP16, batch size=1, and without NMS.\n3. For a fair comparison, the config of bbox postprocessing is changed to be consistent with YOLOv5/6/7 after [PR#9494](https://github.com/open-mmlab/mmdetection/pull/9494), bringing about 0.1~0.3% AP improvement.\n\n### Instance Segmentation\n\nRTMDet-Ins is the state-of-the-art real-time instance segmentation on coco dataset:\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/real-time-instance-segmentation-on-mscoco)](https://paperswithcode.com/sota/real-time-instance-segmentation-on-mscoco?p=rtmdet-an-empirical-study-of-designing-real)\n\n|      Model      | size | box AP | mask AP | Params(M) | FLOPS(G) | TRT-FP16-Latency(ms) |                     Config                     |                                                                                                                                                        Download                                                                                                                                                        |\n| :-------------: | :--: | :----: | :-----: | :-------: | :------: | :------------------: | :--------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| RTMDet-Ins-tiny | 640  |  40.5  |  35.4   |    5.6    |   11.8   |         1.70         | [config](./rtmdet-ins_tiny_8xb32-300e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_tiny_8xb32-300e_coco/rtmdet-ins_tiny_8xb32-300e_coco_20221130_151727-ec670f7e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_tiny_8xb32-300e_coco/rtmdet-ins_tiny_8xb32-300e_coco_20221130_151727.log.json) |\n|  RTMDet-Ins-s   | 640  |  44.0  |  38.7   |   10.18   |   21.5   |         1.93         |  [config](./rtmdet-ins_s_8xb32-300e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_s_8xb32-300e_coco/rtmdet-ins_s_8xb32-300e_coco_20221121_212604-fdc5d7ec.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_s_8xb32-300e_coco/rtmdet-ins_s_8xb32-300e_coco_20221121_212604.log.json)       |\n|  RTMDet-Ins-m   | 640  |  48.8  |  42.1   |   27.58   |  54.13   |         2.69         |  [config](./rtmdet-ins_m_8xb32-300e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_m_8xb32-300e_coco/rtmdet-ins_m_8xb32-300e_coco_20221123_001039-6eba602e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_m_8xb32-300e_coco/rtmdet-ins_m_8xb32-300e_coco_20221123_001039.log.json)       |\n|  RTMDet-Ins-l   | 640  |  51.2  |  43.7   |   57.37   |  106.56  |         3.68         |  [config](./rtmdet-ins_l_8xb32-300e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_l_8xb32-300e_coco/rtmdet-ins_l_8xb32-300e_coco_20221124_103237-78d1d652.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_l_8xb32-300e_coco/rtmdet-ins_l_8xb32-300e_coco_20221124_103237.log.json)       |\n|  RTMDet-Ins-x   | 640  |  52.4  |  44.6   |   102.7   |  182.7   |         5.31         |  [config](./rtmdet-ins_x_8xb16-300e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_x_8xb16-300e_coco/rtmdet-ins_x_8xb16-300e_coco_20221124_111313-33d4595b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_x_8xb16-300e_coco/rtmdet-ins_x_8xb16-300e_coco_20221124_111313.log.json)       |\n\n**Note**:\n\n1. The inference speed of RTMDet-Ins is measured on an NVIDIA 3090 GPU with TensorRT 8.4.3, cuDNN 8.2.0, FP16, batch size=1. Top 100 masks are kept and the post process latency is included.\n\n### Rotated Object Detection\n\nRTMDet-R achieves state-of-the-art on various remote sensing datasets.\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-dota-1)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-dota-1?p=rtmdet-an-empirical-study-of-designing-real)\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/one-stage-anchor-free-oriented-object-1)](https://paperswithcode.com/sota/one-stage-anchor-free-oriented-object-1?p=rtmdet-an-empirical-study-of-designing-real)\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/object-detection-in-aerial-images-on-hrsc2016)](https://paperswithcode.com/sota/object-detection-in-aerial-images-on-hrsc2016?p=rtmdet-an-empirical-study-of-designing-real)\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rtmdet-an-empirical-study-of-designing-real/one-stage-anchor-free-oriented-object-3)](https://paperswithcode.com/sota/one-stage-anchor-free-oriented-object-3?p=rtmdet-an-empirical-study-of-designing-real)\n\nModels and configs of RTMDet-R are available in [MMRotate](https://github.com/open-mmlab/mmrotate/tree/1.x/configs/rotated_rtmdet).\n\n|  Backbone   | pretrain |  Aug  | mmAP  | mAP50 | mAP75 | Params(M) | FLOPS(G) | TRT-FP16-Latency(ms) |                                                            Config                                                             |                                                                                                                                                                       Download                                                                                                                                                                       |\n| :---------: | :------: | :---: | :---: | :---: | :---: | :-------: | :------: | :------------------: | :---------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| RTMDet-tiny |    IN    |  RR   | 47.37 | 75.36 | 50.64 |   4.88    |  20.45   |         4.40         |        [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_tiny-3x-dota.py)        |                             [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_tiny-3x-dota/rotated_rtmdet_tiny-3x-dota-9d821076.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_tiny-3x-dota/rotated_rtmdet_tiny-3x-dota_20221201_120814.json)                             |\n| RTMDet-tiny |    IN    | MS+RR | 53.59 | 79.82 | 58.87 |   4.88    |  20.45   |         4.40         |      [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_tiny-3x-dota_ms.py)       |                       [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_tiny-3x-dota_ms/rotated_rtmdet_tiny-3x-dota_ms-f12286ff.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_tiny-3x-dota_ms/rotated_rtmdet_tiny-3x-dota_ms_20221113_201235.log)                        |\n|  RTMDet-s   |    IN    |  RR   | 48.16 | 76.93 | 50.59 |   8.86    |  37.62   |         4.86         |         [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_s-3x-dota.py)          |                                   [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_s-3x-dota/rotated_rtmdet_s-3x-dota-11f6ccf5.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_s-3x-dota/rotated_rtmdet_s-3x-dota_20221124_081442.json)                                   |\n|  RTMDet-s   |    IN    | MS+RR | 54.43 | 79.98 | 60.07 |   8.86    |  37.62   |         4.86         |        [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_s-3x-dota_ms.py)        |                             [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_s-3x-dota_ms/rotated_rtmdet_s-3x-dota_ms-20ead048.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_s-3x-dota_ms/rotated_rtmdet_s-3x-dota_ms_20221113_201055.json)                             |\n|  RTMDet-m   |    IN    |  RR   | 50.56 | 78.24 | 54.47 |   24.67   |  99.76   |         7.82         |         [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_m-3x-dota.py)          |                                   [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_m-3x-dota/rotated_rtmdet_m-3x-dota-beeadda6.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_m-3x-dota/rotated_rtmdet_m-3x-dota_20221122_011234.json)                                   |\n|  RTMDet-m   |    IN    | MS+RR | 55.00 | 80.26 | 61.26 |   24.67   |  99.76   |         7.82         |        [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_m-3x-dota_ms.py)        |                             [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_m-3x-dota_ms/rotated_rtmdet_m-3x-dota_ms-c71eb375.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_m-3x-dota_ms/rotated_rtmdet_m-3x-dota_ms_20221122_011234.json)                             |\n|  RTMDet-l   |    IN    |  RR   | 51.01 | 78.85 | 55.21 |   52.27   |  204.21  |        10.82         |         [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_l-3x-dota.py)          |                                   [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_l-3x-dota/rotated_rtmdet_l-3x-dota-23992372.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_l-3x-dota/rotated_rtmdet_l-3x-dota_20221122_011241.json)                                   |\n|  RTMDet-l   |    IN    | MS+RR | 55.52 | 80.54 | 61.47 |   52.27   |  204.21  |        10.82         |        [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_l-3x-dota_ms.py)        |                             [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_l-3x-dota_ms/rotated_rtmdet_l-3x-dota_ms-2738da34.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_l-3x-dota_ms/rotated_rtmdet_l-3x-dota_ms_20221122_011241.json)                             |\n|  RTMDet-l   |   COCO   | MS+RR | 56.74 | 81.33 | 63.45 |   52.27   |  204.21  |        10.82         | [config](https://github.com/open-mmlab/mmrotate/edit/1.x/configs/rotated_rtmdet/rotated_rtmdet_l-coco_pretrain-3x-dota_ms.py) | [model](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_l-coco_pretrain-3x-dota_ms/rotated_rtmdet_l-coco_pretrain-3x-dota_ms-06d248a2.pth) \\| [log](https://download.openmmlab.com/mmrotate/v1.0/rotated_rtmdet/rotated_rtmdet_l-coco_pretrain-3x-dota_ms/rotated_rtmdet_l-coco_pretrain-3x-dota_ms_20221113_202010.json) |\n\n### Classification\n\nWe also provide the imagenet classification configs of the RTMDet backbone. Find more details in the [classification folder](./classification).\n\n|    Model     | resolution | Params(M) | Flops(G) | Top-1 (%) | Top-5 (%) |                                                              Download                                                               |\n| :----------: | :--------: | :-------: | :------: | :-------: | :-------: | :---------------------------------------------------------------------------------------------------------------------------------: |\n| CSPNeXt-tiny |  224x224   |   2.73    |   0.34   |   69.44   |   89.45   |    [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-tiny_imagenet_600e-3a2dd350.pth)     |\n|  CSPNeXt-s   |  224x224   |   4.89    |   0.66   |   74.41   |   92.23   |      [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-s_imagenet_600e-ea671761.pth)      |\n|  CSPNeXt-m   |  224x224   |   13.05   |   1.93   |   79.27   |   94.79   | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-m_8xb256-rsb-a1-600e_in1k-ecb3bbd9.pth) |\n|  CSPNeXt-l   |  224x224   |   27.16   |   4.19   |   81.30   |   95.62   | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-l_8xb256-rsb-a1-600e_in1k-6a760974.pth) |\n|  CSPNeXt-x   |  224x224   |   48.85   |   7.76   |   82.10   |   95.69   | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-x_8xb256-rsb-a1-600e_in1k-b3f78edd.pth) |\n\n## Citation\n\n```latex\n@misc{lyu2022rtmdet,\n      title={RTMDet: An Empirical Study of Designing Real-Time Object Detectors},\n      author={Chengqi Lyu and Wenwei Zhang and Haian Huang and Yue Zhou and Yudong Wang and Yanyi Liu and Shilong Zhang and Kai Chen},\n      year={2022},\n      eprint={2212.07784},\n      archivePrefix={arXiv},\n      primaryClass={cs.CV}\n}\n```\n\n## Visualization\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/12907710/208044554-1e8de6b5-48d8-44e4-a7b5-75076c7ebb71.png\"/>\n</div>\n\n## Deployment Tutorial\n\nHere is a basic example of deploy RTMDet with [MMDeploy-1.x](https://github.com/open-mmlab/mmdeploy/tree/1.x).\n\n### Step1. Install MMDeploy\n\nBefore starting the deployment, please make sure you install MMDetection and MMDeploy-1.x correctly.\n\n- Install MMDetection, please refer to the [MMDetection installation guide](https://mmdetection.readthedocs.io/en/latest/get_started.html).\n- Install MMDeploy-1.x, please refer to the [MMDeploy-1.x installation guide](https://mmdeploy.readthedocs.io/en/1.x/get_started.html#installation).\n\nIf you want to deploy RTMDet with ONNXRuntime, TensorRT, or other inference engine,\nplease make sure you have installed the corresponding dependencies and MMDeploy precompiled packages.\n\n### Step2. Convert Model\n\nAfter the installation, you can enjoy the model deployment journey starting from converting PyTorch model to backend model by running MMDeploy's `tools/deploy.py`.\n\nThe detailed model conversion tutorial please refer to the [MMDeploy document](https://mmdeploy.readthedocs.io/en/1.x/02-how-to-run/convert_model.html).\nHere we only give the example of converting RTMDet.\n\nMMDeploy supports converting dynamic and static models. Dynamic models support different input shape, but the inference speed is slower than static models.\nTo achieve the best performance, we suggest converting RTMDet with static setting.\n\n- If you only want to use ONNX, please use [`configs/mmdet/detection/detection_onnxruntime_static.py`](https://github.com/open-mmlab/mmdeploy/blob/1.x/configs/mmdet/detection/detection_onnxruntime_static.py) as the deployment config.\n- If you want to use TensorRT, please use [`configs/mmdet/detection/detection_tensorrt_static-640x640.py`](https://github.com/open-mmlab/mmdeploy/blob/1.x/configs/mmdet/detection/detection_tensorrt_static-640x640.py).\n\nIf you want to customize the settings in the deployment config for your requirements, please refer to [MMDeploy config tutorial](https://mmdeploy.readthedocs.io/en/1.x/02-how-to-run/write_config.html).\n\nAfter preparing the deployment config, you can run the `tools/deploy.py` script to convert your model.\nHere we take converting RTMDet-s to TensorRT as an example:\n\n```shell\n# go to the mmdeploy folder\ncd ${PATH_TO_MMDEPLOY}\n\n# download RTMDet-s checkpoint\nwget -P checkpoint https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_s_8xb32-300e_coco/rtmdet_s_8xb32-300e_coco_20220905_161602-387a891e.pth\n\n# run the command to start model conversion\npython tools/deploy.py \\\n  configs/mmdet/detection/detection_tensorrt_static-640x640.py \\\n  ${PATH_TO_MMDET}/configs/rtmdet/rtmdet_s_8xb32-300e_coco.py \\\n  checkpoint/rtmdet_s_8xb32-300e_coco_20220905_161602-387a891e.pth \\\n  demo/resources/det.jpg \\\n  --work-dir ./work_dirs/rtmdet \\\n  --device cuda:0 \\\n  --show\n```\n\nIf the script runs successfully, you will see the following files:\n\n```\n|----work_dirs\n     |----rtmdet\n          |----end2end.onnx  # ONNX model\n          |----end2end.engine  # TensorRT engine file\n```\n\nAfter this, you can check the inference results with MMDeploy Model Converter API:\n\n```python\nfrom mmdeploy.apis import inference_model\n\nresult = inference_model(\n  model_cfg='${PATH_TO_MMDET}/configs/rtmdet/rtmdet_s_8xb32-300e_coco.py',\n  deploy_cfg='${PATH_TO_MMDEPLOY}/configs/mmdet/detection/detection_tensorrt_static-640x640.py',\n  backend_files=['work_dirs/rtmdet/end2end.engine'],\n  img='demo/resources/det.jpg',\n  device='cuda:0')\n```\n\n#### Advanced Setting\n\nTo convert the model with TRT-FP16, you can enable the fp16 mode in your deploy config:\n\n```python\n# in MMDeploy config\nbackend_config = dict(\n    type='tensorrt',\n    common_config=dict(\n        fp16_mode=True  # enable fp16\n    ))\n```\n\nTo reduce the end to end inference speed with the inference engine, we suggest you to adjust the post-processing setting of the model.\nWe set a very low score threshold during training and testing to achieve better COCO mAP.\nHowever, in actual usage scenarios, a relatively high score threshold (e.g. 0.3) is usually used.\n\nYou can adjust the score threshold and the number of detection boxes in your model config according to the actual usage to reduce the time-consuming of post-processing.\n\n```python\n# in MMDetection config\nmodel = dict(\n    test_cfg=dict(\n        nms_pre=1000,  # keep top-k score bboxes before nms\n        min_bbox_size=0,\n        score_thr=0.3,  # score threshold to filter bboxes\n        nms=dict(type='nms', iou_threshold=0.65),\n        max_per_img=100)  # only keep top-100 as the final results.\n)\n```\n\n### Step3. Inference with SDK\n\nWe provide both Python and C++ inference API with MMDeploy SDK.\n\nTo use SDK, you need to dump the required info during converting the model. Just add `--dump-info` to the model conversion command:\n\n```shell\npython tools/deploy.py \\\n  configs/mmdet/detection/detection_tensorrt_static-640x640.py \\\n  ${PATH_TO_MMDET}/configs/rtmdet/rtmdet_s_8xb32-300e_coco.py \\\n  checkpoint/rtmdet_s_8xb32-300e_coco_20220905_161602-387a891e.pth \\\n  demo/resources/det.jpg \\\n  --work-dir ./work_dirs/rtmdet-sdk \\\n  --device cuda:0 \\\n  --show \\\n  --dump-info  # dump sdk info\n```\n\nAfter running the command, it will dump 3 json files additionally for the SDK:\n\n```\n|----work_dirs\n     |----rtmdet-sdk\n          |----end2end.onnx  # ONNX model\n          |----end2end.engine  # TensorRT engine file\n          # json files for the SDK\n          |----pipeline.json\n          |----deploy.json\n          |----detail.json\n```\n\n#### Python API\n\nHere is a basic example of SDK Python API:\n\n```python\nfrom mmdeploy_python import Detector\nimport cv2\n\nimg = cv2.imread('demo/resources/det.jpg')\n\n# create a detector\ndetector = Detector(model_path='work_dirs/rtmdet-sdk', device_name='cuda', device_id=0)\n# run the inference\nbboxes, labels, _ = detector(img)\n# Filter the result according to threshold\nindices = [i for i in range(len(bboxes))]\nfor index, bbox, label_id in zip(indices, bboxes, labels):\n  [left, top, right, bottom], score = bbox[0:4].astype(int),  bbox[4]\n  if score < 0.3:\n      continue\n  # draw bbox\n  cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0))\n\ncv2.imwrite('output_detection.png', img)\n```\n\n#### C++ API\n\nHere is a basic example of SDK C++ API:\n\n```C++\n#include <cstdlib>\n#include <opencv2/opencv.hpp>\n#include \"mmdeploy/detector.hpp\"\n\nint main() {\n  const char* device_name = \"cuda\";\n  int device_id = 0;\n  std::string model_path = \"work_dirs/rtmdet-sdk\";\n  std::string image_path = \"demo/resources/det.jpg\";\n\n  // 1. load model\n  mmdeploy::Model model(model_path);\n  // 2. create predictor\n  mmdeploy::Detector detector(model, mmdeploy::Device{device_name, device_id});\n  // 3. read image\n  cv::Mat img = cv::imread(image_path);\n  // 4. inference\n  auto dets = detector.Apply(img);\n  // 5. deal with the result. Here we choose to visualize it\n  for (int i = 0; i < dets.size(); ++i) {\n    const auto& box = dets[i].bbox;\n    fprintf(stdout, \"box %d, left=%.2f, top=%.2f, right=%.2f, bottom=%.2f, label=%d, score=%.4f\\n\",\n            i, box.left, box.top, box.right, box.bottom, dets[i].label_id, dets[i].score);\n    if (bboxes[i].score < 0.3) {\n      continue;\n    }\n    cv::rectangle(img, cv::Point{(int)box.left, (int)box.top},\n                  cv::Point{(int)box.right, (int)box.bottom}, cv::Scalar{0, 255, 0});\n  }\n  cv::imwrite(\"output_detection.png\", img);\n  return 0;\n}\n```\n\nTo build C++ example, please add MMDeploy package in your CMake project as following:\n\n```cmake\nfind_package(MMDeploy REQUIRED)\ntarget_link_libraries(${name} PRIVATE mmdeploy ${OpenCV_LIBS})\n```\n\n#### Other languages\n\n- [C# API Examples](https://github.com/open-mmlab/mmdeploy/tree/1.x/demo/csharp)\n- [JAVA API Examples](https://github.com/open-mmlab/mmdeploy/tree/1.x/demo/java)\n\n### Deploy RTMDet Instance Segmentation Model\n\nWe support RTMDet-Ins ONNXRuntime and TensorRT deployment after [MMDeploy v1.0.0rc2](https://github.com/open-mmlab/mmdeploy/tree/v1.0.0rc2). And its deployment process is almost consistent with the detection model.\n\n#### Step1. Install MMDeploy >= v1.0.0rc2\n\nPlease refer to the [MMDeploy-1.x installation guide](https://mmdeploy.readthedocs.io/en/1.x/get_started.html#installation) to install the latest version.\nPlease remember to replace the pre-built package with the latest version.\nThe v1.0.0rc2 package can be downloaded from [v1.0.0rc2 release page](https://github.com/open-mmlab/mmdeploy/releases/tag/v1.0.0rc2).\n\nStep2. Convert Model\n\nThis step has no difference with the previous tutorial. The only thing you need to change is switching to the RTMDet-Ins deploy config:\n\n- If you want to use ONNXRuntime, please use [`configs/mmdet/instance-seg/instance-seg_rtmdet-ins_onnxruntime_static-640x640.py`](https://github.com/open-mmlab/mmdeploy/blob/dev-1.x/configs/mmdet/instance-seg/instance-seg_rtmdet-ins_onnxruntime_static-640x640.py) as the deployment config.\n- If you want to use TensorRT, please use [`configs/mmdet/instance-seg/instance-seg_rtmdet-ins_tensorrt_static-640x640.py`](https://github.com/open-mmlab/mmdeploy/blob/dev-1.x/configs/mmdet/instance-seg/instance-seg_rtmdet-ins_tensorrt_static-640x640.py).\n\nHere we take converting RTMDet-Ins-s to TensorRT as an example:\n\n```shell\n# go to the mmdeploy folder\ncd ${PATH_TO_MMDEPLOY}\n\n# download RTMDet-s checkpoint\nwget -P checkpoint https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_s_8xb32-300e_coco/rtmdet-ins_s_8xb32-300e_coco_20221121_212604-fdc5d7ec.pth\n\n# run the command to start model conversion\npython tools/deploy.py \\\n  configs/mmdet/instance-seg/instance-seg_rtmdet-ins_tensorrt_static-640x640.py \\\n  ${PATH_TO_MMDET}/configs/rtmdet/rtmdet-ins_s_8xb32-300e_coco.py \\\n  checkpoint/rtmdet-ins_s_8xb32-300e_coco_20221121_212604-fdc5d7ec.pth \\\n  demo/resources/det.jpg \\\n  --work-dir ./work_dirs/rtmdet-ins \\\n  --device cuda:0 \\\n  --show\n```\n\nIf the script runs successfully, you will see the following files:\n\n```\n|----work_dirs\n     |----rtmdet-ins\n          |----end2end.onnx  # ONNX model\n          |----end2end.engine  # TensorRT engine file\n```\n\nAfter this, you can check the inference results with MMDeploy Model Converter API:\n\n```python\nfrom mmdeploy.apis import inference_model\n\nresult = inference_model(\n  model_cfg='${PATH_TO_MMDET}/configs/rtmdet/rtmdet-ins_s_8xb32-300e_coco.py',\n  deploy_cfg='${PATH_TO_MMDEPLOY}/configs/mmdet/instance-seg/instance-seg_rtmdet-ins_tensorrt_static-640x640.py',\n  backend_files=['work_dirs/rtmdet-ins/end2end.engine'],\n  img='demo/resources/det.jpg',\n  device='cuda:0')\n```\n\n### Model Config\n\nIn MMDetection's config, we use `model` to set up detection algorithm components. In addition to neural network components such as `backbone`, `neck`, etc, it also requires `data_preprocessor`, `train_cfg`, and `test_cfg`. `data_preprocessor` is responsible for processing a batch of data output by dataloader. `train_cfg`, and `test_cfg` in the model config are for training and testing hyperparameters of the components.Taking RTMDet as an example, we will introduce each field in the config according to different function modules:\n\n```python\nmodel = dict(\n    type='RTMDet',  # The name of detector\n    data_preprocessor=dict(  # The config of data preprocessor, usually includes image normalization and padding\n        type='DetDataPreprocessor',  # The type of the data preprocessor. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.data_preprocessors.DetDataPreprocessor\n        mean=[103.53, 116.28, 123.675],  # Mean values used to pre-training the pre-trained backbone models, ordered in R, G, B\n        std=[57.375, 57.12, 58.395],  # Standard variance used to pre-training the pre-trained backbone models, ordered in R, G, B\n        bgr_to_rgb=False,  # whether to convert image from BGR to RGB\n        batch_augments=None),  # Batch-level augmentations\n    backbone=dict(  # The config of backbone\n        type='CSPNeXt',  # The type of backbone network. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.backbones.CSPNeXt\n        arch='P5',  # Architecture of CSPNeXt, from {P5, P6}. Defaults to P5\n        expand_ratio=0.5,  # Ratio to adjust the number of channels of the hidden layer. Defaults to 0.5\n        deepen_factor=1,  # Depth multiplier, multiply number of blocks in CSP layer by this amount. Defaults to 1.0\n        widen_factor=1,  # Width multiplier, multiply number of channels in each layer by this amount. Defaults to 1.0\n        channel_attention=True,  # Whether to add channel attention in each stage. Defaults to True\n        norm_cfg=dict(type='SyncBN'),  # Dictionary to construct and config norm layer. Defaults to dict(type=’BN’, requires_grad=True)\n        act_cfg=dict(type='SiLU', inplace=True)),  # Config dict for activation layer. Defaults to dict(type=’SiLU’)\n    neck=dict(\n        type='CSPNeXtPAFPN',  # The type of neck is CSPNeXtPAFPN. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.necks.CSPNeXtPAFPN\n        in_channels=[256, 512, 1024],  # Number of input channels per scale\n        out_channels=256,  # Number of output channels (used at each scale)\n        num_csp_blocks=3,  # Number of bottlenecks in CSPLayer. Defaults to 3\n        expand_ratio=0.5,  # Ratio to adjust the number of channels of the hidden layer. Default: 0.5\n        norm_cfg=dict(type='SyncBN'),  # Config dict for normalization layer. Default: dict(type=’BN’)\n        act_cfg=dict(type='SiLU', inplace=True)),  # Config dict for activation layer. Default: dict(type=’Swish’)\n    bbox_head=dict(\n        type='RTMDetSepBNHead',  # The type of bbox_head is RTMDetSepBNHead. RTMDetHead with separated BN layers and shared conv layers. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.dense_heads.RTMDetSepBNHead\n        num_classes=80,  # Number of categories excluding the background category\n        in_channels=256,  # Number of channels in the input feature map\n        stacked_convs=2,  # Whether to share conv layers between stages. Defaults to True\n        feat_channels=256,  # Feature channels of convolutional layers in the head\n        anchor_generator=dict(  # The config of anchor generator\n            type='MlvlPointGenerator',  # The methods use MlvlPointGenerator. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/prior_generators/point_generator.py#L92\n            offset=0,  # The offset of points, the value is normalized with corresponding stride. Defaults to 0.5\n            strides=[8, 16, 32]),  # Strides of anchors in multiple feature levels in order (w, h)\n        bbox_coder=dict(type='DistancePointBBoxCoder'),  # 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. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/coders/distance_point_bbox_coder.py#L9\n        loss_cls=dict(  # Config of loss function for the classification branch\n            type='QualityFocalLoss',  # Type of loss for classification branch. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.losses.QualityFocalLoss\n            use_sigmoid=True,  # Whether sigmoid operation is conducted in QFL. Defaults to True\n            beta=2.0,  # The beta parameter for calculating the modulating factor. Defaults to 2.0\n            loss_weight=1.0),  #  Loss weight of current loss\n        loss_bbox=dict(  # Config of loss function for the regression branch\n            type='GIoULoss',  # Type of loss. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.losses.GIoULoss\n            loss_weight=2.0),  # Loss weight of the regression branch\n        with_objectness=False,  # Whether to add an objectness branch. Defaults to True\n        exp_on_reg=True,  # Whether to use .exp() in regression\n        share_conv=True,  # Whether to share conv layers between stages. Defaults to True\n        pred_kernel_size=1,  # Kernel size of prediction layer. Defaults to 1\n        norm_cfg=dict(type='SyncBN'),  # Config dict for normalization layer. Defaults to dict(type='BN', momentum=0.03, eps=0.001)\n        act_cfg=dict(type='SiLU', inplace=True)),  # Config dict for activation layer. Defaults to dict(type='SiLU')\n    train_cfg=dict(  # Config of training hyperparameters for ATSS\n        assigner=dict(  # Config of assigner\n            type='DynamicSoftLabelAssigner',   # Type of assigner. DynamicSoftLabelAssigner computes matching between predictions and ground truth with dynamic soft label assignment. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/assigners/dynamic_soft_label_assigner.py#L40\n            topk=13),  # Select top-k predictions to calculate dynamic k best matches for each gt. Defaults to 13\n        allowed_border=-1,  # The border allowed after padding for valid anchors\n        pos_weight=-1,  # The weight of positive samples during training\n        debug=False),  # Whether to set the debug mode\n    test_cfg=dict(  # Config for testing hyperparameters for ATSS\n        nms_pre=30000,  # The number of boxes before NMS\n        min_bbox_size=0,  # The allowed minimal box size\n        score_thr=0.001,  # Threshold to filter out boxes\n        nms=dict(  # Config of NMS in the second stage\n            type='nms',  # Type of NMS\n            iou_threshold=0.65),  # NMS threshold\n        max_per_img=300),  # Max number of detections of each image\n)\n```\n"
  },
  {
    "path": "configs/rtmdet/classification/README.md",
    "content": "# CSPNeXt ImageNet Pre-training\n\nIn this folder, we provide the imagenet pre-training config of RTMDet's backbone CSPNeXt.\n\n## Requirements\n\nTo train with these configs, please install [MMPreTrain](https://github.com/open-mmlab/mmpretrain) first.\n\nInstall by MIM:\n\n```shell\nmim install mmpretrain\n```\n\nor install by pip:\n\n```shell\npip install mmpretrain\n```\n\n## Prepare Dataset\n\nTo pre-train on ImageNet, you need to prepare the dataset first. Please refer to the [guide](https://mmpretrain.readthedocs.io/en/latest/user_guides/dataset_prepare.html#imagenet).\n\n## How to Train\n\nYou can use the classification config in the same way as the detection config.\n\nFor single-GPU training, run:\n\n```shell\npython tools/train.py \\\n    ${CONFIG_FILE} \\\n    [optional arguments]\n```\n\nFor multi-GPU training, run:\n\n```shell\nbash ./tools/dist_train.sh \\\n    ${CONFIG_FILE} \\\n    ${GPU_NUM} \\\n    [optional arguments]\n```\n\nMore details can be found in [user guides](https://mmdetection.readthedocs.io/en/latest/user_guides/train.html).\n\n## Results and Models\n\n|    Model     | resolution | Params(M) | Flops(G) | Top-1 (%) | Top-5 (%) |                                                              Download                                                               |\n| :----------: | :--------: | :-------: | :------: | :-------: | :-------: | :---------------------------------------------------------------------------------------------------------------------------------: |\n| CSPNeXt-tiny |  224x224   |   2.73    |   0.34   |   69.44   |   89.45   |    [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-tiny_imagenet_600e-3a2dd350.pth)     |\n|  CSPNeXt-s   |  224x224   |   4.89    |   0.66   |   74.41   |   92.23   |      [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-s_imagenet_600e-ea671761.pth)      |\n|  CSPNeXt-m   |  224x224   |   13.05   |   1.93   |   79.27   |   94.79   | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-m_8xb256-rsb-a1-600e_in1k-ecb3bbd9.pth) |\n|  CSPNeXt-l   |  224x224   |   27.16   |   4.19   |   81.30   |   95.62   | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-l_8xb256-rsb-a1-600e_in1k-6a760974.pth) |\n|  CSPNeXt-x   |  224x224   |   48.85   |   7.76   |   82.10   |   95.69   | [model](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-x_8xb256-rsb-a1-600e_in1k-b3f78edd.pth) |\n"
  },
  {
    "path": "configs/rtmdet/classification/cspnext-l_8xb256-rsb-a1-600e_in1k.py",
    "content": "_base_ = './cspnext-s_8xb256-rsb-a1-600e_in1k.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=1, widen_factor=1),\n    head=dict(in_channels=1024))\n"
  },
  {
    "path": "configs/rtmdet/classification/cspnext-m_8xb256-rsb-a1-600e_in1k.py",
    "content": "_base_ = './cspnext-s_8xb256-rsb-a1-600e_in1k.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=0.67, widen_factor=0.75),\n    head=dict(in_channels=768))\n"
  },
  {
    "path": "configs/rtmdet/classification/cspnext-s_8xb256-rsb-a1-600e_in1k.py",
    "content": "_base_ = [\n    'mmpretrain::_base_/datasets/imagenet_bs256_rsb_a12.py',\n    'mmpretrain::_base_/schedules/imagenet_bs2048_rsb.py',\n    'mmpretrain::_base_/default_runtime.py'\n]\n\nmodel = dict(\n    type='ImageClassifier',\n    backbone=dict(\n        type='mmdet.CSPNeXt',\n        arch='P5',\n        out_indices=(4, ),\n        expand_ratio=0.5,\n        deepen_factor=0.33,\n        widen_factor=0.5,\n        channel_attention=True,\n        norm_cfg=dict(type='BN'),\n        act_cfg=dict(type='mmdet.SiLU')),\n    neck=dict(type='GlobalAveragePooling'),\n    head=dict(\n        type='LinearClsHead',\n        num_classes=1000,\n        in_channels=512,\n        loss=dict(\n            type='LabelSmoothLoss',\n            label_smooth_val=0.1,\n            mode='original',\n            loss_weight=1.0),\n        topk=(1, 5)),\n    train_cfg=dict(augments=[\n        dict(type='Mixup', alpha=0.2),\n        dict(type='CutMix', alpha=1.0)\n    ]))\n\n# dataset settings\ntrain_dataloader = dict(sampler=dict(type='RepeatAugSampler', shuffle=True))\n\n# schedule settings\noptim_wrapper = dict(\n    optimizer=dict(weight_decay=0.01),\n    paramwise_cfg=dict(bias_decay_mult=0., norm_decay_mult=0.),\n)\n\nparam_scheduler = [\n    # warm up learning rate scheduler\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=True,\n        begin=0,\n        end=5,\n        # update by iter\n        convert_to_iter_based=True),\n    # main learning rate scheduler\n    dict(\n        type='CosineAnnealingLR',\n        T_max=595,\n        eta_min=1.0e-6,\n        by_epoch=True,\n        begin=5,\n        end=600)\n]\n\ntrain_cfg = dict(by_epoch=True, max_epochs=600)\n"
  },
  {
    "path": "configs/rtmdet/classification/cspnext-tiny_8xb256-rsb-a1-600e_in1k.py",
    "content": "_base_ = './cspnext-s_8xb256-rsb-a1-600e_in1k.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=0.167, widen_factor=0.375),\n    head=dict(in_channels=384))\n"
  },
  {
    "path": "configs/rtmdet/classification/cspnext-x_8xb256-rsb-a1-600e_in1k.py",
    "content": "_base_ = './cspnext-s_8xb256-rsb-a1-600e_in1k.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=1.33, widen_factor=1.25),\n    head=dict(in_channels=1280))\n"
  },
  {
    "path": "configs/rtmdet/metafile.yml",
    "content": "Collections:\n  - Name: RTMDet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n        - Flat Cosine Annealing\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - CSPNeXt\n        - CSPNeXtPAFPN\n    README: configs/rtmdet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v3.0.0rc1/mmdet/models/detectors/rtmdet.py#L6\n      Version: v3.0.0rc1\n\nModels:\n  - Name: rtmdet_tiny_8xb32-300e_coco\n    Alias:\n      - rtmdet-t\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 11.7\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_tiny_8xb32-300e_coco/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth\n\n  - Name: rtmdet_s_8xb32-300e_coco\n    Alias:\n      - rtmdet-s\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_s_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 15.9\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_s_8xb32-300e_coco/rtmdet_s_8xb32-300e_coco_20220905_161602-387a891e.pth\n\n  - Name: rtmdet_m_8xb32-300e_coco\n    Alias:\n      - rtmdet-m\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_m_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 27.8\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 49.1\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_m_8xb32-300e_coco/rtmdet_m_8xb32-300e_coco_20220719_112220-229f527c.pth\n\n  - Name: rtmdet_l_8xb32-300e_coco\n    Alias:\n      - rtmdet-l\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_l_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 43.2\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 51.3\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_l_8xb32-300e_coco/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth\n\n  - Name: rtmdet_x_8xb32-300e_coco\n    Alias:\n      - rtmdet-x\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_x_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 61.1\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 52.6\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_x_8xb32-300e_coco/rtmdet_x_8xb32-300e_coco_20220715_230555-cc79b9ae.pth\n\n  - Name: rtmdet_x_p6_4xb8-300e_coco\n    Alias:\n      - rtmdet-x_p6\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_x_p6_4xb8-300e_coco.py\n    Metadata:\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 54.9\n    Weights: https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-p6/rtmdet_x_p6_4xb8-300e_coco-bf32be58.pth\n\n  - Name: rtmdet_l_convnext_b_4xb32-100e_coco\n    Alias:\n      - rtmdet-l_convnext_b\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_l_convnext_b_4xb32-100e_coco.py\n    Metadata:\n      Epochs: 100\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 53.1\n    Weights: https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-swin-convnext/rtmdet_l_convnext_b_4xb32-100e_coco-d4731b3d.pth\n\n  - Name: rtmdet_l_swin_b_4xb32-100e_coco\n    Alias:\n      - rtmdet-l_swin_b\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_l_swin_b_4xb32-100e_coco.py\n    Metadata:\n      Epochs: 100\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 52.4\n    Weights: https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-swin-convnext/rtmdet_l_swin_b_4xb32-100e_coco-0828ce5d.pth\n\n  - Name: rtmdet_l_swin_b_p6_4xb16-100e_coco\n    Alias:\n      - rtmdet-l_swin_b_p6\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet_l_swin_b_p6_4xb16-100e_coco.py\n    Metadata:\n      Epochs: 100\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 56.4\n    Weights: https://github.com/orange0-jp/orange-weights/releases/download/v0.1.0rtmdet-swin-convnext/rtmdet_l_swin_b_p6_4xb16-100e_coco-a1486b6f.pth\n\n  - Name: rtmdet-ins_tiny_8xb32-300e_coco\n    Alias:\n      - rtmdet-ins-t\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet-ins_tiny_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 18.4\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 35.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_tiny_8xb32-300e_coco/rtmdet-ins_tiny_8xb32-300e_coco_20221130_151727-ec670f7e.pth\n\n  - Name: rtmdet-ins_s_8xb32-300e_coco\n    Alias:\n      - rtmdet-ins-s\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet-ins_s_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 27.6\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 38.7\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_s_8xb32-300e_coco/rtmdet-ins_s_8xb32-300e_coco_20221121_212604-fdc5d7ec.pth\n\n  - Name: rtmdet-ins_m_8xb32-300e_coco\n    Alias:\n      - rtmdet-ins-m\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet-ins_m_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 42.5\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 48.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 42.1\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_m_8xb32-300e_coco/rtmdet-ins_m_8xb32-300e_coco_20221123_001039-6eba602e.pth\n\n  - Name: rtmdet-ins_l_8xb32-300e_coco\n    Alias:\n      - rtmdet-ins-l\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet-ins_l_8xb32-300e_coco.py\n    Metadata:\n      Training Memory (GB): 59.8\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 51.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 43.7\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_l_8xb32-300e_coco/rtmdet-ins_l_8xb32-300e_coco_20221124_103237-78d1d652.pth\n\n  - Name: rtmdet-ins_x_8xb16-300e_coco\n    Alias:\n      - rtmdet-ins-x\n    In Collection: RTMDet\n    Config: configs/rtmdet/rtmdet-ins_x_8xb16-300e_coco.py\n    Metadata:\n      Training Memory (GB): 33.7\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 52.4\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 44.6\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet-ins_x_8xb16-300e_coco/rtmdet-ins_x_8xb16-300e_coco_20221124_111313-33d4595b.pth\n"
  },
  {
    "path": "configs/rtmdet/rtmdet-ins_l_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet_l_8xb32-300e_coco.py'\nmodel = dict(\n    bbox_head=dict(\n        _delete_=True,\n        type='RTMDetInsSepBNHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=2,\n        share_conv=True,\n        pred_kernel_size=1,\n        feat_channels=256,\n        act_cfg=dict(type='SiLU', inplace=True),\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        anchor_generator=dict(\n            type='MlvlPointGenerator', offset=0, strides=[8, 16, 32]),\n        bbox_coder=dict(type='DistancePointBBoxCoder'),\n        loss_cls=dict(\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            beta=2.0,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        loss_mask=dict(\n            type='DiceLoss', loss_weight=2.0, eps=5e-6, reduction='mean')),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100,\n        mask_thr_binary=0.5),\n)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(type='CachedMosaic', img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1)),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(pin_memory=True, dataset=dict(pipeline=train_pipeline))\n\ntrain_pipeline_stage2 = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomResize',\n        scale=(640, 640),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type='PackDetInputs')\n]\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=280,\n        switch_pipeline=train_pipeline_stage2)\n]\n\nval_evaluator = dict(metric=['bbox', 'segm'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/rtmdet/rtmdet-ins_m_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet-ins_l_8xb32-300e_coco.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=0.67, widen_factor=0.75),\n    neck=dict(in_channels=[192, 384, 768], out_channels=192, num_csp_blocks=2),\n    bbox_head=dict(in_channels=192, feat_channels=192))\n"
  },
  {
    "path": "configs/rtmdet/rtmdet-ins_s_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet-ins_l_8xb32-300e_coco.py'\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-s_imagenet_600e.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        deepen_factor=0.33,\n        widen_factor=0.5,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n    neck=dict(in_channels=[128, 256, 512], out_channels=128, num_csp_blocks=1),\n    bbox_head=dict(in_channels=128, feat_channels=128))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(type='CachedMosaic', img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1)),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_stage2 = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomResize',\n        scale=(640, 640),\n        ratio_range=(0.5, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=280,\n        switch_pipeline=train_pipeline_stage2)\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet-ins_tiny_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet-ins_s_8xb32-300e_coco.py'\n\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-tiny_imagenet_600e.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        deepen_factor=0.167,\n        widen_factor=0.375,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n    neck=dict(in_channels=[96, 192, 384], out_channels=96, num_csp_blocks=1),\n    bbox_head=dict(in_channels=96, feat_channels=96))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='CachedMosaic',\n        img_scale=(640, 640),\n        pad_val=114.0,\n        max_cached_images=20,\n        random_pop=False),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(640, 640)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=10,\n        random_pop=False,\n        pad_val=(114, 114, 114),\n        prob=0.5),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1)),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/rtmdet/rtmdet-ins_x_8xb16-300e_coco.py",
    "content": "_base_ = './rtmdet-ins_l_8xb32-300e_coco.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=1.33, widen_factor=1.25),\n    neck=dict(\n        in_channels=[320, 640, 1280], out_channels=320, num_csp_blocks=4),\n    bbox_head=dict(in_channels=320, feat_channels=320))\n\nbase_lr = 0.002\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=base_lr))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0e-5,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        # use cosine lr from 150 to 300 epoch\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=_base_.max_epochs // 2,\n        end=_base_.max_epochs,\n        T_max=_base_.max_epochs // 2,\n        by_epoch=True,\n        convert_to_iter_based=True),\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_l_8xb32-300e_coco.py",
    "content": "_base_ = [\n    '../_base_/default_runtime.py', '../_base_/schedules/schedule_1x.py',\n    '../_base_/datasets/coco_detection.py', './rtmdet_tta.py'\n]\nmodel = dict(\n    type='RTMDet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False,\n        batch_augments=None),\n    backbone=dict(\n        type='CSPNeXt',\n        arch='P5',\n        expand_ratio=0.5,\n        deepen_factor=1,\n        widen_factor=1,\n        channel_attention=True,\n        norm_cfg=dict(type='SyncBN'),\n        act_cfg=dict(type='SiLU', inplace=True)),\n    neck=dict(\n        type='CSPNeXtPAFPN',\n        in_channels=[256, 512, 1024],\n        out_channels=256,\n        num_csp_blocks=3,\n        expand_ratio=0.5,\n        norm_cfg=dict(type='SyncBN'),\n        act_cfg=dict(type='SiLU', inplace=True)),\n    bbox_head=dict(\n        type='RTMDetSepBNHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=2,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='MlvlPointGenerator', offset=0, strides=[8, 16, 32]),\n        bbox_coder=dict(type='DistancePointBBoxCoder'),\n        loss_cls=dict(\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            beta=2.0,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        with_objectness=False,\n        exp_on_reg=True,\n        share_conv=True,\n        pred_kernel_size=1,\n        norm_cfg=dict(type='SyncBN'),\n        act_cfg=dict(type='SiLU', inplace=True)),\n    train_cfg=dict(\n        assigner=dict(type='DynamicSoftLabelAssigner', topk=13),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=30000,\n        min_bbox_size=0,\n        score_thr=0.001,\n        nms=dict(type='nms', iou_threshold=0.65),\n        max_per_img=300),\n)\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='CachedMosaic', img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(640, 640)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_stage2 = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(640, 640),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(640, 640)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(640, 640), keep_ratio=True),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=32,\n    num_workers=10,\n    batch_sampler=None,\n    pin_memory=True,\n    dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(\n    batch_size=5, num_workers=10, dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nmax_epochs = 300\nstage2_num_epochs = 20\nbase_lr = 0.004\ninterval = 10\n\ntrain_cfg = dict(\n    max_epochs=max_epochs,\n    val_interval=interval,\n    dynamic_intervals=[(max_epochs - stage2_num_epochs, 1)])\n\nval_evaluator = dict(proposal_nums=(100, 1, 10))\ntest_evaluator = val_evaluator\n\n# optimizer\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=base_lr, weight_decay=0.05),\n    paramwise_cfg=dict(\n        norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0e-5,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        # use cosine lr from 150 to 300 epoch\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=max_epochs // 2,\n        end=max_epochs,\n        T_max=max_epochs // 2,\n        by_epoch=True,\n        convert_to_iter_based=True),\n]\n\n# hooks\ndefault_hooks = dict(\n    checkpoint=dict(\n        interval=interval,\n        max_keep_ckpts=3  # only keep latest 3 checkpoints\n    ))\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=max_epochs - stage2_num_epochs,\n        switch_pipeline=train_pipeline_stage2)\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_l_convnext_b_4xb32-100e_coco.py",
    "content": "_base_ = './rtmdet_l_8xb32-300e_coco.py'\n\ncustom_imports = dict(\n    imports=['mmpretrain.models'], allow_failed_imports=False)\n\nnorm_cfg = dict(type='GN', num_groups=32)\ncheckpoint_file = 'https://download.openmmlab.com/mmclassification/v0/convnext/convnext-base_in21k-pre-3rdparty_in1k-384px_20221219-4570f792.pth'  # noqa\nmodel = dict(\n    type='RTMDet',\n    data_preprocessor=dict(\n        _delete_=True,\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        batch_augments=None),\n    backbone=dict(\n        _delete_=True,\n        type='mmpretrain.ConvNeXt',\n        arch='base',\n        out_indices=[1, 2, 3],\n        drop_path_rate=0.7,\n        layer_scale_init_value=1.0,\n        gap_before_final_norm=False,\n        with_cp=True,\n        init_cfg=dict(\n            type='Pretrained', checkpoint=checkpoint_file,\n            prefix='backbone.')),\n    neck=dict(in_channels=[256, 512, 1024], norm_cfg=norm_cfg),\n    bbox_head=dict(norm_cfg=norm_cfg))\n\nmax_epochs = 100\nstage2_num_epochs = 10\ninterval = 10\nbase_lr = 0.001\n\ntrain_cfg = dict(\n    max_epochs=max_epochs,\n    val_interval=interval,\n    dynamic_intervals=[(max_epochs - stage2_num_epochs, 1)])\n\noptim_wrapper = dict(\n    constructor='LearningRateDecayOptimizerConstructor',\n    paramwise_cfg={\n        'decay_rate': 0.8,\n        'decay_type': 'layer_wise',\n        'num_layers': 12\n    },\n    optimizer=dict(lr=base_lr))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0e-5,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        # use cosine lr from 50 to 100 epoch\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=max_epochs // 2,\n        end=max_epochs,\n        T_max=max_epochs // 2,\n        by_epoch=True,\n        convert_to_iter_based=True),\n]\n\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=max_epochs - stage2_num_epochs,\n        switch_pipeline={{_base_.train_pipeline_stage2}})\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_l_swin_b_4xb32-100e_coco.py",
    "content": "_base_ = './rtmdet_l_8xb32-300e_coco.py'\n\nnorm_cfg = dict(type='GN', num_groups=32)\ncheckpoint = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window12_384_22k.pth'  # noqa\nmodel = dict(\n    type='RTMDet',\n    data_preprocessor=dict(\n        _delete_=True,\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        batch_augments=None),\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=True,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=checkpoint)),\n    neck=dict(in_channels=[256, 512, 1024], norm_cfg=norm_cfg),\n    bbox_head=dict(norm_cfg=norm_cfg))\n\nmax_epochs = 100\nstage2_num_epochs = 10\ninterval = 10\nbase_lr = 0.001\n\ntrain_cfg = dict(\n    max_epochs=max_epochs,\n    val_interval=interval,\n    dynamic_intervals=[(max_epochs - stage2_num_epochs, 1)])\n\noptim_wrapper = dict(optimizer=dict(lr=base_lr))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0e-5,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        # use cosine lr from 50 to 100 epoch\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=max_epochs // 2,\n        end=max_epochs,\n        T_max=max_epochs // 2,\n        by_epoch=True,\n        convert_to_iter_based=True),\n]\n\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=max_epochs - stage2_num_epochs,\n        switch_pipeline={{_base_.train_pipeline_stage2}})\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_l_swin_b_p6_4xb16-100e_coco.py",
    "content": "_base_ = './rtmdet_l_swin_b_4xb32-100e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depths=[2, 2, 18, 2, 1],\n        num_heads=[4, 8, 16, 32, 64],\n        strides=(4, 2, 2, 2, 2),\n        out_indices=(1, 2, 3, 4)),\n    neck=dict(in_channels=[256, 512, 1024, 2048]),\n    bbox_head=dict(\n        anchor_generator=dict(\n            type='MlvlPointGenerator', offset=0, strides=[8, 16, 32, 64])))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='CachedMosaic', img_scale=(1280, 1280), pad_val=114.0),\n    dict(\n        type='RandomResize',\n        scale=(2560, 2560),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(1280, 1280)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(1280, 1280), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(1280, 1280),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_stage2 = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(1280, 1280)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(1280, 1280), pad_val=dict(img=(114, 114, 114))),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1280, 1280), keep_ratio=True),\n    dict(type='Pad', size=(1280, 1280), pad_val=dict(img=(114, 114, 114))),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=16, num_workers=20, dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(num_workers=20, dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nmax_epochs = 100\nstage2_num_epochs = 10\n\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=max_epochs - stage2_num_epochs,\n        switch_pipeline=train_pipeline_stage2)\n]\n\nimg_scales = [(1280, 1280), (640, 640), (1920, 1920)]\ntta_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[\n            [\n                dict(type='Resize', scale=s, keep_ratio=True)\n                for s in img_scales\n            ],\n            [\n                # ``RandomFlip`` must be placed before ``Pad``, otherwise\n                # bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type='RandomFlip', prob=1.),\n                dict(type='RandomFlip', prob=0.)\n            ],\n            [\n                dict(\n                    type='Pad',\n                    size=(1920, 1920),\n                    pad_val=dict(img=(114, 114, 114))),\n            ],\n            [dict(type='LoadAnnotations', with_bbox=True)],\n            [\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'scale_factor', 'flip', 'flip_direction'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_m_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet_l_8xb32-300e_coco.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=0.67, widen_factor=0.75),\n    neck=dict(in_channels=[192, 384, 768], out_channels=192, num_csp_blocks=2),\n    bbox_head=dict(in_channels=192, feat_channels=192))\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_s_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet_l_8xb32-300e_coco.py'\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-s_imagenet_600e.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        deepen_factor=0.33,\n        widen_factor=0.5,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n    neck=dict(in_channels=[128, 256, 512], out_channels=128, num_csp_blocks=1),\n    bbox_head=dict(in_channels=128, feat_channels=128, exp_on_reg=False))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='CachedMosaic', img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(640, 640)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_stage2 = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(640, 640),\n        ratio_range=(0.5, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(640, 640)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=280,\n        switch_pipeline=train_pipeline_stage2)\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet_s_8xb32-300e_coco.py'\n\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-tiny_imagenet_600e.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        deepen_factor=0.167,\n        widen_factor=0.375,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n    neck=dict(in_channels=[96, 192, 384], out_channels=96, num_csp_blocks=1),\n    bbox_head=dict(in_channels=96, feat_channels=96, exp_on_reg=False))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='CachedMosaic',\n        img_scale=(640, 640),\n        pad_val=114.0,\n        max_cached_images=20,\n        random_pop=False),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(640, 640)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=10,\n        random_pop=False,\n        pad_val=(114, 114, 114),\n        prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_tta.py",
    "content": "tta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(type='nms', iou_threshold=0.6), max_per_img=100))\n\nimg_scales = [(640, 640), (320, 320), (960, 960)]\ntta_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[\n            [\n                dict(type='Resize', scale=s, keep_ratio=True)\n                for s in img_scales\n            ],\n            [\n                # ``RandomFlip`` must be placed before ``Pad``, otherwise\n                # bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type='RandomFlip', prob=1.),\n                dict(type='RandomFlip', prob=0.)\n            ],\n            [\n                dict(\n                    type='Pad',\n                    size=(960, 960),\n                    pad_val=dict(img=(114, 114, 114))),\n            ],\n            [dict(type='LoadAnnotations', with_bbox=True)],\n            [\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'scale_factor', 'flip', 'flip_direction'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_x_8xb32-300e_coco.py",
    "content": "_base_ = './rtmdet_l_8xb32-300e_coco.py'\n\nmodel = dict(\n    backbone=dict(deepen_factor=1.33, widen_factor=1.25),\n    neck=dict(\n        in_channels=[320, 640, 1280], out_channels=320, num_csp_blocks=4),\n    bbox_head=dict(in_channels=320, feat_channels=320))\n"
  },
  {
    "path": "configs/rtmdet/rtmdet_x_p6_4xb8-300e_coco.py",
    "content": "_base_ = './rtmdet_x_8xb32-300e_coco.py'\n\nmodel = dict(\n    backbone=dict(arch='P6', out_indices=(2, 3, 4, 5)),\n    neck=dict(in_channels=[320, 640, 960, 1280]),\n    bbox_head=dict(\n        anchor_generator=dict(\n            type='MlvlPointGenerator', offset=0, strides=[8, 16, 32, 64])))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='CachedMosaic', img_scale=(1280, 1280), pad_val=114.0),\n    dict(\n        type='RandomResize',\n        scale=(2560, 2560),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(1280, 1280)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(1280, 1280), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type='CachedMixUp',\n        img_scale=(1280, 1280),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_stage2 = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(1280, 1280),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(1280, 1280)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=(1280, 1280), pad_val=dict(img=(114, 114, 114))),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1280, 1280), keep_ratio=True),\n    dict(type='Pad', size=(1280, 1280), pad_val=dict(img=(114, 114, 114))),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8, num_workers=20, dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(\n    batch_size=5, num_workers=20, dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nmax_epochs = 300\nstage2_num_epochs = 20\n\nbase_lr = 0.004 * 32 / 256\noptim_wrapper = dict(optimizer=dict(lr=base_lr))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0e-5,\n        by_epoch=False,\n        begin=0,\n        end=1000),\n    dict(\n        # use cosine lr from 150 to 300 epoch\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=max_epochs // 2,\n        end=max_epochs,\n        T_max=max_epochs // 2,\n        by_epoch=True,\n        convert_to_iter_based=True),\n]\n\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type='PipelineSwitchHook',\n        switch_epoch=max_epochs - stage2_num_epochs,\n        switch_pipeline=train_pipeline_stage2)\n]\n\nimg_scales = [(1280, 1280), (640, 640), (1920, 1920)]\ntta_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[\n            [\n                dict(type='Resize', scale=s, keep_ratio=True)\n                for s in img_scales\n            ],\n            [\n                # ``RandomFlip`` must be placed before ``Pad``, otherwise\n                # bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type='RandomFlip', prob=1.),\n                dict(type='RandomFlip', prob=0.)\n            ],\n            [\n                dict(\n                    type='Pad',\n                    size=(1920, 1920),\n                    pad_val=dict(img=(114, 114, 114))),\n            ],\n            [dict(type='LoadAnnotations', with_bbox=True)],\n            [\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'scale_factor', 'flip', 'flip_direction'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "configs/sabl/README.md",
    "content": "# SABL\n\n> [Side-Aware Boundary Localization for More Precise Object Detection](https://arxiv.org/abs/1912.04260)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nCurrent object detection frameworks mainly rely on bounding box regression to localize objects. Despite the remarkable progress in recent years, the precision of bounding box regression remains unsatisfactory, hence limiting performance in object detection. We observe that precise localization requires careful placement of each side of the bounding box. However, the mainstream approach, which focuses on predicting centers and sizes, is not the most effective way to accomplish this task, especially when there exists displacements with large variance between the anchors and the targets. In this paper, we propose an alternative approach, named as Side-Aware Boundary Localization (SABL), where each side of the bounding box is respectively localized with a dedicated network branch. To tackle the difficulty of precise localization in the presence of displacements with large variance, we further propose a two-step localization scheme, which first predicts a range of movement through bucket prediction and then pinpoints the precise position within the predicted bucket. We test the proposed method on both two-stage and single-stage detection frameworks. Replacing the standard bounding box regression branch with the proposed design leads to significant improvements on Faster R-CNN, RetinaNet, and Cascade R-CNN, by 3.0%, 1.7%, and 0.9%, respectively.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143973698-3dfaea91-4415-4818-9781-5017183e7489.png\"/>\n</div>\n\n## Results and Models\n\nThe results on COCO 2017 val is shown in the below table. (results on test-dev are usually slightly higher than val).\nSingle-scale testing (1333x800) is adopted in all results.\n\n|       Method       | Backbone  | Lr schd | ms-train | box AP |                      Config                       |                                                                                                                                  Download                                                                                                                                   |\n| :----------------: | :-------: | :-----: | :------: | :----: | :-----------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| SABL Faster R-CNN  | R-50-FPN  |   1x    |    N     |  39.9  |  [config](./sabl-faster-rcnn_r50_fpn_1x_coco.py)  |    [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_faster_rcnn_r50_fpn_1x_coco/sabl_faster_rcnn_r50_fpn_1x_coco-e867595b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_faster_rcnn_r50_fpn_1x_coco/20200830_130324.log.json)    |\n| SABL Faster R-CNN  | R-101-FPN |   1x    |    N     |  41.7  | [config](./sabl-faster-rcnn_r101_fpn_1x_coco.py)  |  [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_faster_rcnn_r101_fpn_1x_coco/sabl_faster_rcnn_r101_fpn_1x_coco-f804c6c1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_faster_rcnn_r101_fpn_1x_coco/20200830_183949.log.json)   |\n| SABL Cascade R-CNN | R-50-FPN  |   1x    |    N     |  41.6  | [config](./sabl-cascade-rcnn_r50_fpn_1x_coco.py)  |  [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_cascade_rcnn_r50_fpn_1x_coco/sabl_cascade_rcnn_r50_fpn_1x_coco-e1748e5e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_cascade_rcnn_r50_fpn_1x_coco/20200831_033726.log.json)   |\n| SABL Cascade R-CNN | R-101-FPN |   1x    |    N     |  43.0  | [config](./sabl-cascade-rcnn_r101_fpn_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_cascade_rcnn_r101_fpn_1x_coco/sabl_cascade_rcnn_r101_fpn_1x_coco-2b83e87c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_cascade_rcnn_r101_fpn_1x_coco/20200831_141745.log.json) |\n\n|     Method     | Backbone  | GN  | Lr schd |  ms-train   | box AP |                            Config                            |                                                                                                                                                   Download                                                                                                                                                   |\n| :------------: | :-------: | :-: | :-----: | :---------: | :----: | :----------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| SABL RetinaNet | R-50-FPN  |  N  |   1x    |      N      |  37.7  |        [config](./sabl-retinanet_r50_fpn_1x_coco.py)         |                       [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r50_fpn_1x_coco/sabl_retinanet_r50_fpn_1x_coco-6c54fd4f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r50_fpn_1x_coco/20200830_053451.log.json)                        |\n| SABL RetinaNet | R-50-FPN  |  Y  |   1x    |      N      |  38.8  |       [config](./sabl-retinanet_r50-gn_fpn_1x_coco.py)       |                   [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r50_fpn_gn_1x_coco/sabl_retinanet_r50_fpn_gn_1x_coco-e16dfcf1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r50_fpn_gn_1x_coco/20200831_141955.log.json)                   |\n| SABL RetinaNet | R-101-FPN |  N  |   1x    |      N      |  39.7  |        [config](./sabl-retinanet_r101_fpn_1x_coco.py)        |                      [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_1x_coco/sabl_retinanet_r101_fpn_1x_coco-42026904.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_1x_coco/20200831_034256.log.json)                      |\n| SABL RetinaNet | R-101-FPN |  Y  |   1x    |      N      |  40.5  |      [config](./sabl-retinanet_r101-gn_fpn_1x_coco.py)       |                 [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_1x_coco/sabl_retinanet_r101_fpn_gn_1x_coco-40a893e8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_1x_coco/20200830_201422.log.json)                  |\n| SABL RetinaNet | R-101-FPN |  Y  |   2x    | Y (640~800) |  42.9  | [config](./sabl-retinanet_r101-gn_fpn_ms-640-800-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_2x_ms_640_800_coco/sabl_retinanet_r101_fpn_gn_2x_ms_640_800_coco-1e63382c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_2x_ms_640_800_coco/20200830_144807.log.json) |\n| SABL RetinaNet | R-101-FPN |  Y  |   2x    | Y (480~960) |  43.6  | [config](./sabl-retinanet_r101-gn_fpn_ms-480-960-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_2x_ms_480_960_coco/sabl_retinanet_r101_fpn_gn_2x_ms_480_960_coco-5342f857.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_2x_ms_480_960_coco/20200830_164537.log.json) |\n\n## Citation\n\nWe provide config files to reproduce the object detection results in the ECCV 2020 Spotlight paper for [Side-Aware Boundary Localization for More Precise Object Detection](https://arxiv.org/abs/1912.04260).\n\n```latex\n@inproceedings{Wang_2020_ECCV,\n    title = {Side-Aware Boundary Localization for More Precise Object Detection},\n    author = {Jiaqi Wang and Wenwei Zhang and Yuhang Cao and Kai Chen and Jiangmiao Pang and Tao Gong and Jianping Shi and Chen Change Loy and Dahua Lin},\n    booktitle = {ECCV},\n    year = {2020}\n}\n```\n"
  },
  {
    "path": "configs/sabl/metafile.yml",
    "content": "Collections:\n  - Name: SABL\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n        - SABL\n    Paper:\n      URL: https://arxiv.org/abs/1912.04260\n      Title: 'Side-Aware Boundary Localization for More Precise Object Detection'\n    README: configs/sabl/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.4.0/mmdet/models/roi_heads/bbox_heads/sabl_head.py#L14\n      Version: v2.4.0\n\nModels:\n  - Name: sabl-faster-rcnn_r50_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-faster-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_faster_rcnn_r50_fpn_1x_coco/sabl_faster_rcnn_r50_fpn_1x_coco-e867595b.pth\n\n  - Name: sabl-faster-rcnn_r101_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-faster-rcnn_r101_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_faster_rcnn_r101_fpn_1x_coco/sabl_faster_rcnn_r101_fpn_1x_coco-f804c6c1.pth\n\n  - Name: sabl-cascade-rcnn_r50_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-cascade-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_cascade_rcnn_r50_fpn_1x_coco/sabl_cascade_rcnn_r50_fpn_1x_coco-e1748e5e.pth\n\n  - Name: sabl-cascade-rcnn_r101_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-cascade-rcnn_r101_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_cascade_rcnn_r101_fpn_1x_coco/sabl_cascade_rcnn_r101_fpn_1x_coco-2b83e87c.pth\n\n  - Name: sabl-retinanet_r50_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-retinanet_r50_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r50_fpn_1x_coco/sabl_retinanet_r50_fpn_1x_coco-6c54fd4f.pth\n\n  - Name: sabl-retinanet_r50-gn_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-retinanet_r50-gn_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 38.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r50_fpn_gn_1x_coco/sabl_retinanet_r50_fpn_gn_1x_coco-e16dfcf1.pth\n\n  - Name: sabl-retinanet_r101_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-retinanet_r101_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 39.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_1x_coco/sabl_retinanet_r101_fpn_1x_coco-42026904.pth\n\n  - Name: sabl-retinanet_r101-gn_fpn_1x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-retinanet_r101-gn_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_1x_coco/sabl_retinanet_r101_fpn_gn_1x_coco-40a893e8.pth\n\n  - Name: sabl-retinanet_r101-gn_fpn_ms-640-800-2x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-retinanet_r101-gn_fpn_ms-640-800-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_2x_ms_640_800_coco/sabl_retinanet_r101_fpn_gn_2x_ms_640_800_coco-1e63382c.pth\n\n  - Name: sabl-retinanet_r101-gn_fpn_ms-480-960-2x_coco\n    In Collection: SABL\n    Config: configs/sabl/sabl-retinanet_r101-gn_fpn_ms-480-960-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sabl/sabl_retinanet_r101_fpn_gn_2x_ms_480_960_coco/sabl_retinanet_r101_fpn_gn_2x_ms_480_960_coco-5342f857.pth\n"
  },
  {
    "path": "configs/sabl/sabl-cascade-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    roi_head=dict(bbox_head=[\n        dict(\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.7),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0)),\n        dict(\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.5),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0)),\n        dict(\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.3),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1, loss_weight=1.0))\n    ]))\n"
  },
  {
    "path": "configs/sabl/sabl-cascade-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    roi_head=dict(bbox_head=[\n        dict(\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.7),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0)),\n        dict(\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.5),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0)),\n        dict(\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.3),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1, loss_weight=1.0))\n    ]))\n"
  },
  {
    "path": "configs/sabl/sabl-faster-rcnn_r101_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    roi_head=dict(\n        bbox_head=dict(\n            _delete_=True,\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.7),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0))))\n"
  },
  {
    "path": "configs/sabl/sabl-faster-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            _delete_=True,\n            type='SABLHead',\n            num_classes=80,\n            cls_in_channels=256,\n            reg_in_channels=256,\n            roi_feat_size=7,\n            reg_feat_up_ratio=2,\n            reg_pre_kernel=3,\n            reg_post_kernel=3,\n            reg_pre_num=2,\n            reg_post_num=1,\n            cls_out_channels=1024,\n            reg_offset_out_channels=256,\n            reg_cls_out_channels=256,\n            num_cls_fcs=1,\n            num_reg_fcs=0,\n            reg_class_agnostic=True,\n            norm_cfg=None,\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=1.7),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox_reg=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0))))\n"
  },
  {
    "path": "configs/sabl/sabl-retinanet_r101-gn_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    bbox_head=dict(\n        _delete_=True,\n        type='SABLRetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        norm_cfg=norm_cfg,\n        bbox_coder=dict(\n            type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n        loss_bbox_reg=dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/sabl/sabl-retinanet_r101-gn_fpn_ms-480-960-2x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n# model settings\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    bbox_head=dict(\n        _delete_=True,\n        type='SABLRetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        norm_cfg=norm_cfg,\n        bbox_coder=dict(\n            type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n        loss_bbox_reg=dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False))\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 480), (1333, 960)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/sabl/sabl-retinanet_r101-gn_fpn_ms-640-800-2x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n# model settings\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    bbox_head=dict(\n        _delete_=True,\n        type='SABLRetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        norm_cfg=norm_cfg,\n        bbox_coder=dict(\n            type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n        loss_bbox_reg=dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False))\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 480), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/sabl/sabl-retinanet_r101_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    bbox_head=dict(\n        _delete_=True,\n        type='SABLRetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n        loss_bbox_reg=dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/sabl/sabl-retinanet_r50-gn_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    bbox_head=dict(\n        _delete_=True,\n        type='SABLRetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        norm_cfg=norm_cfg,\n        bbox_coder=dict(\n            type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n        loss_bbox_reg=dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/sabl/sabl-retinanet_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    bbox_head=dict(\n        _delete_=True,\n        type='SABLRetinaHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n        loss_bbox_reg=dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='ApproxMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.0,\n            ignore_iof_thr=-1),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/scnet/README.md",
    "content": "# SCNet\n\n> [SCNet: Training Inference Sample Consistency for Instance Segmentation](https://arxiv.org/abs/2012.10150)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nCascaded architectures have brought significant performance improvement in object detection and instance segmentation. However, there are lingering issues regarding the disparity in the Intersection-over-Union (IoU) distribution of the samples between training and inference. This disparity can potentially exacerbate detection accuracy. This paper proposes an architecture referred to as Sample Consistency Network (SCNet) to ensure that the IoU distribution of the samples at training time is close to that at inference time. Furthermore, SCNet incorporates feature relay and utilizes global contextual information to further reinforce the reciprocal relationships among classifying, detecting, and segmenting sub-tasks. Extensive experiments on the standard COCO dataset reveal the effectiveness of the proposed method over multiple evaluation metrics, including box AP, mask AP, and inference speed. In particular, while running 38% faster, the proposed SCNet improves the AP of the box and mask predictions by respectively 1.3 and 2.3 points compared to the strong Cascade Mask R-CNN baseline.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143974840-8fed75f3-661e-4e2a-a210-acf4ab5f42a3.png\"/>\n</div>\n\n## Dataset\n\nSCNet requires COCO and [COCO-stuff](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip) dataset for training. You need to download and extract it in the COCO dataset path.\nThe directory should be like this.\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n|   |   ├── stuffthingmaps\n```\n\n## Results and Models\n\nThe results on COCO 2017val are shown in the below table. (results on test-dev are usually slightly higher than val)\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf speed (fps) | box AP | mask AP | TTA box AP | TTA mask AP |                    Config                    |                                                                                                                                           Download                                                                                                                                           |\n| :-------------: | :-----: | :-----: | :------: | :-------------: | :----: | :-----: | :--------: | :---------: | :------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50-FPN     | pytorch |   1x    |   7.0    |       6.2       |  43.5  |  39.2   |    44.8    |    40.9     |     [config](./scnet_r50_fpn_1x_coco.py)     |                 [model](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r50_fpn_1x_coco/scnet_r50_fpn_1x_coco-c3f09857.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r50_fpn_1x_coco/scnet_r50_fpn_1x_coco_20210117_192725.log.json)                 |\n|    R-50-FPN     | pytorch |   20e   |   7.0    |       6.2       |  44.5  |  40.0   |    45.8    |    41.5     |    [config](./scnet_r50_fpn_20e_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r50_fpn_20e_coco/scnet_r50_fpn_20e_coco-a569f645.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r50_fpn_20e_coco/scnet_r50_fpn_20e_coco_20210116_060148.log.json)               |\n|    R-101-FPN    | pytorch |   20e   |   8.9    |       5.8       |  45.8  |  40.9   |    47.3    |    42.7     |    [config](./scnet_r101_fpn_20e_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r101_fpn_20e_coco/scnet_r101_fpn_20e_coco-294e312c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r101_fpn_20e_coco/scnet_r101_fpn_20e_coco_20210118_175824.log.json)             |\n| X-101-64x4d-FPN | pytorch |   20e   |   13.2   |       4.9       |  47.5  |  42.3   |    48.9    |    44.0     | [config](./scnet_x101-64x4d_fpn_20e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_x101_64x4d_fpn_20e_coco/scnet_x101_64x4d_fpn_20e_coco-fb09dec9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_x101_64x4d_fpn_20e_coco/scnet_x101_64x4d_fpn_20e_coco_20210120_045959.log.json) |\n\n### Notes\n\n- Training hyper-parameters are identical to those of [HTC](https://github.com/open-mmlab/mmdetection/tree/main/configs/htc).\n- TTA means Test Time Augmentation, which applies horizontal flip and multi-scale testing. Refer to [config](./scnet_r50_fpn_1x_coco.py).\n\n## Citation\n\nWe provide the code for reproducing experiment results of [SCNet](https://arxiv.org/abs/2012.10150).\n\n```latex\n@inproceedings{vu2019cascade,\n  title={SCNet: Training Inference Sample Consistency for Instance Segmentation},\n  author={Vu, Thang and Haeyong, Kang and Yoo, Chang D},\n  booktitle={AAAI},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/scnet/metafile.yml",
    "content": "Collections:\n  - Name: SCNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n        - SCNet\n    Paper:\n      URL: https://arxiv.org/abs/2012.10150\n      Title: 'SCNet: Training Inference Sample Consistency for Instance Segmentation'\n    README: configs/scnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.9.0/mmdet/models/detectors/scnet.py#L6\n      Version: v2.9.0\n\nModels:\n  - Name: scnet_r50_fpn_1x_coco\n    In Collection: SCNet\n    Config: configs/scnet/scnet_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 161.29\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r50_fpn_1x_coco/scnet_r50_fpn_1x_coco-c3f09857.pth\n\n  - Name: scnet_r50_fpn_20e_coco\n    In Collection: SCNet\n    Config: configs/scnet/scnet_r50_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 7.0\n      inference time (ms/im):\n        - value: 161.29\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r50_fpn_20e_coco/scnet_r50_fpn_20e_coco-a569f645.pth\n\n  - Name: scnet_r101_fpn_20e_coco\n    In Collection: SCNet\n    Config: configs/scnet/scnet_r101_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 8.9\n      inference time (ms/im):\n        - value: 172.41\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_r101_fpn_20e_coco/scnet_r101_fpn_20e_coco-294e312c.pth\n\n  - Name: scnet_x101-64x4d_fpn_20e_coco\n    In Collection: SCNet\n    Config: configs/scnet/scnet_x101-64x4d_fpn_20e_coco.py\n    Metadata:\n      Training Memory (GB): 13.2\n      inference time (ms/im):\n        - value: 204.08\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (800, 1333)\n      Epochs: 20\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 42.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/scnet/scnet_x101_64x4d_fpn_20e_coco/scnet_x101_64x4d_fpn_20e_coco-fb09dec9.pth\n"
  },
  {
    "path": "configs/scnet/scnet_r101_fpn_20e_coco.py",
    "content": "_base_ = './scnet_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/scnet/scnet_r50_fpn_1x_coco.py",
    "content": "_base_ = '../htc/htc_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    type='SCNet',\n    roi_head=dict(\n        _delete_=True,\n        type='SCNetRoIHead',\n        num_stages=3,\n        stage_loss_weights=[1, 0.5, 0.25],\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type='SCNetBBoxHead',\n                num_shared_fcs=2,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='SCNetBBoxHead',\n                num_shared_fcs=2,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='SCNetBBoxHead',\n                num_shared_fcs=2,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        mask_head=dict(\n            type='SCNetMaskHead',\n            num_convs=12,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=80,\n            conv_to_res=True,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0)),\n        semantic_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[8]),\n        semantic_head=dict(\n            type='SCNetSemanticHead',\n            num_ins=5,\n            fusion_level=1,\n            seg_scale_factor=1 / 8,\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=183,\n            loss_seg=dict(\n                type='CrossEntropyLoss', ignore_index=255, loss_weight=0.2),\n            conv_to_res=True),\n        glbctx_head=dict(\n            type='GlobalContextHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=80,\n            loss_weight=3.0,\n            conv_to_res=True),\n        feat_relay_head=dict(\n            type='FeatureRelayHead',\n            in_channels=1024,\n            out_conv_channels=256,\n            roi_feat_size=7,\n            scale_factor=2)))\n\n# TODO\n# uncomment below code to enable test time augmentations\n# img_norm_cfg = dict(\n#     mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)\n# test_pipeline = [\n#     dict(type='LoadImageFromFile'),\n#     dict(\n#         type='MultiScaleFlipAug',\n#         img_scale=[(600, 900), (800, 1200), (1000, 1500), (1200, 1800),\n#                    (1400, 2100)],\n#         flip=True,\n#         transforms=[\n#             dict(type='Resize', keep_ratio=True),\n#             dict(type='RandomFlip', flip_ratio=0.5),\n#             dict(type='Normalize', **img_norm_cfg),\n#             dict(type='Pad', size_divisor=32),\n#             dict(type='ImageToTensor', keys=['img']),\n#             dict(type='Collect', keys=['img']),\n#         ])\n# ]\n# data = dict(\n#     val=dict(pipeline=test_pipeline),\n#     test=dict(pipeline=test_pipeline))\n"
  },
  {
    "path": "configs/scnet/scnet_r50_fpn_20e_coco.py",
    "content": "_base_ = './scnet_r50_fpn_1x_coco.py'\n# learning policy\nmax_epochs = 20\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 19],\n        gamma=0.1)\n]\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/scnet/scnet_x101-64x4d_fpn_20e_coco.py",
    "content": "_base_ = './scnet_r50_fpn_20e_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/scnet/scnet_x101-64x4d_fpn_8xb1-20e_coco.py",
    "content": "_base_ = './scnet_x101-64x4d_fpn_20e_coco.py'\ntrain_dataloader = dict(batch_size=1, num_workers=1)\n\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (1 samples per GPU)\nauto_scale_lr = dict(base_batch_size=8)\n"
  },
  {
    "path": "configs/scratch/README.md",
    "content": "# Scratch\n\n> [Rethinking ImageNet Pre-training](https://arxiv.org/abs/1811.08883)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe report competitive results on object detection and instance segmentation on the COCO dataset using standard models trained from random initialization. The results are no worse than their ImageNet pre-training counterparts even when using the hyper-parameters of the baseline system (Mask R-CNN) that were optimized for fine-tuning pre-trained models, with the sole exception of increasing the number of training iterations so the randomly initialized models may converge. Training from random initialization is surprisingly robust; our results hold even when: (i) using only 10% of the training data, (ii) for deeper and wider models, and (iii) for multiple tasks and metrics. Experiments show that ImageNet pre-training speeds up convergence early in training, but does not necessarily provide regularization or improve final target task accuracy. To push the envelope we demonstrate 50.9 AP on COCO object detection without using any external data---a result on par with the top COCO 2017 competition results that used ImageNet pre-training. These observations challenge the conventional wisdom of ImageNet pre-training for dependent tasks and we expect these discoveries will encourage people to rethink the current de facto paradigm of \\`pre-training and fine-tuning' in computer vision.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143974572-69c4f57d-0d6d-4f56-ba91-23f8a65a2a77.png\" height=\"300\"/>\n</div>\n\n## Results and Models\n\n|    Model     | Backbone |  Style  | Lr schd | box AP | mask AP |                          Config                           |                                                                                                                                                                                 Download                                                                                                                                                                                  |\n| :----------: | :------: | :-----: | :-----: | :----: | :-----: | :-------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Faster R-CNN | R-50-FPN | pytorch |   6x    |  40.7  |         | [config](./faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py) |     [model](https://download.openmmlab.com/mmdetection/v2.0/scratch/faster_rcnn_r50_fpn_gn-all_scratch_6x_coco/scratch_faster_rcnn_r50_fpn_gn_6x_bbox_mAP-0.407_20200201_193013-90813d01.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/scratch/faster_rcnn_r50_fpn_gn-all_scratch_6x_coco/scratch_faster_rcnn_r50_fpn_gn_6x_20200201_193013.log.json)     |\n|  Mask R-CNN  | R-50-FPN | pytorch |   6x    |  41.2  |  37.4   |  [config](./mask-rcnn_r50-scratch_fpn_gn-all_6x_coco.py)  | [model](https://download.openmmlab.com/mmdetection/v2.0/scratch/mask_rcnn_r50_fpn_gn-all_scratch_6x_coco/scratch_mask_rcnn_r50_fpn_gn_6x_bbox_mAP-0.412__segm_mAP-0.374_20200201_193051-1e190a40.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/scratch/mask_rcnn_r50_fpn_gn-all_scratch_6x_coco/scratch_mask_rcnn_r50_fpn_gn_6x_20200201_193051.log.json) |\n\nNote:\n\n- The above models are trained with 16 GPUs.\n\n## Citation\n\n```latex\n@article{he2018rethinking,\n  title={Rethinking imagenet pre-training},\n  author={He, Kaiming and Girshick, Ross and Doll{\\'a}r, Piotr},\n  journal={arXiv preprint arXiv:1811.08883},\n  year={2018}\n}\n```\n"
  },
  {
    "path": "configs/scratch/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        frozen_stages=-1,\n        zero_init_residual=False,\n        norm_cfg=norm_cfg,\n        init_cfg=None),\n    neck=dict(norm_cfg=norm_cfg),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=norm_cfg)))\n\noptim_wrapper = dict(paramwise_cfg=dict(norm_decay_mult=0.))\n\nmax_epochs = 73\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[65, 71],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# only keep latest 3 checkpoints\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=3))\n"
  },
  {
    "path": "configs/scratch/mask-rcnn_r50-scratch_fpn_gn-all_6x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nnorm_cfg = dict(type='GN', num_groups=32, requires_grad=True)\nmodel = dict(\n    backbone=dict(\n        frozen_stages=-1,\n        zero_init_residual=False,\n        norm_cfg=norm_cfg,\n        init_cfg=None),\n    neck=dict(norm_cfg=norm_cfg),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=norm_cfg),\n        mask_head=dict(norm_cfg=norm_cfg)))\n\noptim_wrapper = dict(paramwise_cfg=dict(norm_decay_mult=0.))\n\nmax_epochs = 73\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[65, 71],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# only keep latest 3 checkpoints\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=3))\n"
  },
  {
    "path": "configs/scratch/metafile.yml",
    "content": "Collections:\n  - Name: Rethinking ImageNet Pre-training\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - RPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1811.08883\n      Title: 'Rethinking ImageNet Pre-training'\n    README: configs/scratch/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.0.0/configs/scratch/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py\n      Version: v2.0.0\n\nModels:\n  - Name: faster-rcnn_r50_fpn_gn-all_scratch_6x_coco\n    In Collection: Rethinking ImageNet Pre-training\n    Config: configs/scratch/faster-rcnn_r50-scratch_fpn_gn-all_6x_coco.py\n    Metadata:\n      Epochs: 72\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/scratch/faster_rcnn_r50_fpn_gn-all_scratch_6x_coco/scratch_faster_rcnn_r50_fpn_gn_6x_bbox_mAP-0.407_20200201_193013-90813d01.pth\n\n  - Name: mask-rcnn_r50_fpn_gn-all_scratch_6x_coco\n    In Collection: Rethinking ImageNet Pre-training\n    Config: configs/scratch/mask-rcnn_r50-scratch_fpn_gn-all_6x_coco.py\n    Metadata:\n      Epochs: 72\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/scratch/mask_rcnn_r50_fpn_gn-all_scratch_6x_coco/scratch_mask_rcnn_r50_fpn_gn_6x_bbox_mAP-0.412__segm_mAP-0.374_20200201_193051-1e190a40.pth\n"
  },
  {
    "path": "configs/seesaw_loss/README.md",
    "content": "# Seesaw Loss\n\n> [Seesaw Loss for Long-Tailed Instance Segmentation](https://arxiv.org/abs/2008.10032)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nInstance segmentation has witnessed a remarkable progress on class-balanced benchmarks. However, they fail to perform as accurately in real-world scenarios, where the category distribution of objects naturally comes with a long tail. Instances of head classes dominate a long-tailed dataset and they serve as negative samples of tail categories. The overwhelming gradients of negative samples on tail classes lead to a biased learning process for classifiers. Consequently, objects of tail categories are more likely to be misclassified as backgrounds or head categories. To tackle this problem, we propose Seesaw Loss to dynamically re-balance gradients of positive and negative samples for each category, with two complementary factors, i.e., mitigation factor and compensation factor. The mitigation factor reduces punishments to tail categories w.r.t. the ratio of cumulative training instances between different categories. Meanwhile, the compensation factor increases the penalty of misclassified instances to avoid false positives of tail categories. We conduct extensive experiments on Seesaw Loss with mainstream frameworks and different data sampling strategies. With a simple end-to-end training pipeline, Seesaw Loss obtains significant gains over Cross-Entropy Loss, and achieves state-of-the-art performance on LVIS dataset without bells and whistles.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143974715-d181abe5-d0a2-40d3-a2bd-17d8c60b89b8.png\"/>\n</div>\n\n- Please setup [LVIS dataset](../lvis/README.md) for MMDetection.\n\n- RFS indicates to use oversample strategy [here](../../docs/tutorials/customipredataset.md#class-balanced-dataset) with oversample threshold `1e-3`.\n\n## Results and models of Seasaw Loss on LVIS v1 dataset\n\n|       Method       | Backbone  |  Style  | Lr schd | Data Sampler | Norm Mask | box AP | mask AP |                                           Config                                           |                                                                                                                                                              Download                                                                                                                                                              |\n| :----------------: | :-------: | :-----: | :-----: | :----------: | :-------: | :----: | :-----: | :----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     Mask R-CNN     | R-50-FPN  | pytorch |   2x    |    random    |     N     |  25.6  |  25.0   |             [config](./mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py)              |                          [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_random_seesaw_loss_mstrain_2x_lvis_v1-a698dd3d.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_random_seesaw_loss_mstrain_2x_lvis_v1.log.json)                          |\n|     Mask R-CNN     | R-50-FPN  | pytorch |   2x    |    random    |     Y     |  25.6  |  25.4   |       [config](./mask-rcnn_r50_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py)        |              [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-a1c11314.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1.log.json)              |\n|     Mask R-CNN     | R-101-FPN | pytorch |   2x    |    random    |     N     |  27.4  |  26.7   |             [config](./mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.py)             |                         [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_random_seesaw_loss_mstrain_2x_lvis_v1-8e6e6dd5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_random_seesaw_loss_mstrain_2x_lvis_v1.log.json)                         |\n|     Mask R-CNN     | R-101-FPN | pytorch |   2x    |    random    |     Y     |  27.2  |  27.3   |       [config](./mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py)       |             [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-a0b59c42.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1.log.json)             |\n|     Mask R-CNN     | R-50-FPN  | pytorch |   2x    |     RFS      |     N     |  27.6  |  26.4   |           [config](./mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py)            |                      [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1-392a804b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1.log.json)                      |\n|     Mask R-CNN     | R-50-FPN  | pytorch |   2x    |     RFS      |     Y     |  27.6  |  26.8   |     [config](./mask-rcnn_r50_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py)      |          [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-cd0f6a12.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1.log.json)          |\n|     Mask R-CNN     | R-101-FPN | pytorch |   2x    |     RFS      |     N     |  28.9  |  27.6   |           [config](./mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py)           |                     [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1-e68eb464.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1.log.json)                     |\n|     Mask R-CNN     | R-101-FPN | pytorch |   2x    |     RFS      |     Y     |  28.9  |  28.2   |     [config](./mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py)     |         [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-1d817139.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1.log.json)         |\n| Cascade Mask R-CNN | R-101-FPN | pytorch |   2x    |    random    |     N     |  33.1  |  29.2   |         [config](./cascade-mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.py)         |                 [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_random_seesaw_loss_mstrain_2x_lvis_v1-71e2215e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_random_seesaw_loss_mstrain_2x_lvis_v1.log.json)                 |\n| Cascade Mask R-CNN | R-101-FPN | pytorch |   2x    |    random    |     Y     |  33.0  |  30.0   |   [config](./cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py)   |     [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-8b5a6745.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1.log.json)     |\n| Cascade Mask R-CNN | R-101-FPN | pytorch |   2x    |     RFS      |     N     |  30.0  |  29.3   |       [config](./cascade-mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py)       |             [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1-5d8ca2a4.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1.log.json)             |\n| Cascade Mask R-CNN | R-101-FPN | pytorch |   2x    |     RFS      |     Y     |  32.8  |  30.1   | [config](./cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-c8551505.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1.log.json) |\n\n## Citation\n\nWe provide config files to reproduce the instance segmentation performance in the CVPR 2021 paper for [Seesaw Loss for Long-Tailed Instance Segmentation](https://arxiv.org/abs/2008.10032).\n\n```latex\n@inproceedings{wang2021seesaw,\n  title={Seesaw Loss for Long-Tailed Instance Segmentation},\n  author={Jiaqi Wang and Wenwei Zhang and Yuhang Zang and Yuhang Cao and Jiangmiao Pang and Tao Gong and Kai Chen and Ziwei Liu and Chen Change Loy and Dahua Lin},\n  booktitle={Proceedings of the {IEEE} Conference on Computer Vision and Pattern Recognition},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py",
    "content": "_base_ = './cascade-mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.py'  # noqa: E501\nmodel = dict(\n    roi_head=dict(\n        mask_head=dict(\n            predictor_cfg=dict(type='NormedConv2d', tempearture=20))))\n"
  },
  {
    "path": "configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py",
    "content": "_base_ = './cascade-mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py'  # noqa: E501\nmodel = dict(\n    roi_head=dict(\n        mask_head=dict(\n            predictor_cfg=dict(type='NormedConv2d', tempearture=20))))\n"
  },
  {
    "path": "configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=1203,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n                loss_cls=dict(\n                    type='SeesawLoss',\n                    p=0.8,\n                    q=2.0,\n                    num_classes=1203,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=1203,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n                loss_cls=dict(\n                    type='SeesawLoss',\n                    p=0.8,\n                    q=2.0,\n                    num_classes=1203,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=1203,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n                loss_cls=dict(\n                    type='SeesawLoss',\n                    p=0.8,\n                    q=2.0,\n                    num_classes=1203,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_head=dict(num_classes=1203)),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            # LVIS allows up to 300\n            max_per_img=300)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/lvis_v1/'\ntrain_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/lvis_v1_train.json',\n        data_prefix=dict(img=''),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/lvis_v1_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='LVISMetric',\n    ann_file=data_root + 'annotations/lvis_v1_val.json',\n    metric=['bbox', 'segm'])\ntest_evaluator = val_evaluator\n\ntrain_cfg = dict(val_interval=24)\n"
  },
  {
    "path": "configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/lvis_v1_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')),\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=1203,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n                loss_cls=dict(\n                    type='SeesawLoss',\n                    p=0.8,\n                    q=2.0,\n                    num_classes=1203,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=1203,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n                loss_cls=dict(\n                    type='SeesawLoss',\n                    p=0.8,\n                    q=2.0,\n                    num_classes=1203,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=1203,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n                loss_cls=dict(\n                    type='SeesawLoss',\n                    p=0.8,\n                    q=2.0,\n                    num_classes=1203,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_head=dict(num_classes=1203)),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            # LVIS allows up to 300\n            max_per_img=300)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(dataset=dict(pipeline=train_pipeline)))\n\ntrain_cfg = dict(val_interval=24)\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py'  # noqa: E501\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py'  # noqa: E501\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py'\nmodel = dict(\n    roi_head=dict(\n        mask_head=dict(\n            predictor_cfg=dict(type='NormedConv2d', tempearture=20))))\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py",
    "content": "_base_ = './mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py'\nmodel = dict(\n    roi_head=dict(\n        mask_head=dict(\n            predictor_cfg=dict(type='NormedConv2d', tempearture=20))))\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            num_classes=1203,\n            cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n            loss_cls=dict(\n                type='SeesawLoss',\n                p=0.8,\n                q=2.0,\n                num_classes=1203,\n                loss_weight=1.0)),\n        mask_head=dict(num_classes=1203)),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            # LVIS allows up to 300\n            max_per_img=300)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ndataset_type = 'LVISV1Dataset'\ndata_root = 'data/lvis_v1/'\ntrain_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/lvis_v1_train.json',\n        data_prefix=dict(img=''),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/lvis_v1_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='LVISMetric',\n    ann_file=data_root + 'annotations/lvis_v1_val.json',\n    metric=['bbox', 'segm'])\ntest_evaluator = val_evaluator\n\ntrain_cfg = dict(val_interval=24)\n"
  },
  {
    "path": "configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/lvis_v1_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            num_classes=1203,\n            cls_predictor_cfg=dict(type='NormedLinear', tempearture=20),\n            loss_cls=dict(\n                type='SeesawLoss',\n                p=0.8,\n                q=2.0,\n                num_classes=1203,\n                loss_weight=1.0)),\n        mask_head=dict(num_classes=1203)),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            # LVIS allows up to 300\n            max_per_img=300)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(dataset=dict(pipeline=train_pipeline)))\n\ntrain_cfg = dict(val_interval=24)\n"
  },
  {
    "path": "configs/seesaw_loss/metafile.yml",
    "content": "Collections:\n  - Name: Seesaw Loss\n    Metadata:\n      Training Data: LVIS\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Softmax\n        - RPN\n        - Convolution\n        - Dense Connections\n        - FPN\n        - ResNet\n        - RoIAlign\n        - Seesaw Loss\n    Paper:\n      URL: https://arxiv.org/abs/2008.10032\n      Title: 'Seesaw Loss for Long-Tailed Instance Segmentation'\n    README: configs/seesaw_loss/README.md\n\nModels:\n  - Name: mask-rcnn_r50_fpn_random_seesaw_loss_mstrain_2x_lvis_v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 25.6\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 25.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_random_seesaw_loss_mstrain_2x_lvis_v1-a698dd3d.pth\n  - Name: mask-rcnn_r50_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 25.6\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 25.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-a1c11314.pth\n  - Name: mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 27.4\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 26.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_random_seesaw_loss_mstrain_2x_lvis_v1-8e6e6dd5.pth\n  - Name: mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 27.2\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 27.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-a0b59c42.pth\n  - Name: mask-rcnn_r50_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 27.6\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 26.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1-392a804b.pth\n  - Name: mask-rcnn_r50_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 27.6\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 26.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r50_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-cd0f6a12.pth\n  - Name: mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 28.9\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 27.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1-e68eb464.pth\n  - Name: mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 28.9\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 28.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-1d817139.pth\n  - Name: cascade-mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 33.1\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 29.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_random_seesaw_loss_mstrain_2x_lvis_v1-71e2215e.pth\n  - Name: cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 33.0\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 30.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_random_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-8b5a6745.pth\n  - Name: cascade-mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 30.0\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 29.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_mstrain_2x_lvis_v1-5d8ca2a4.pth\n  - Name: cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1\n    In Collection: Seesaw Loss\n    Config: configs/seesaw_loss/cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: LVIS v1\n        Metrics:\n          box AP: 32.8\n      - Task: Instance Segmentation\n        Dataset: LVIS v1\n        Metrics:\n          mask AP: 30.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/seesaw_loss/cascade_mask_rcnn_r101_fpn_sample1e-3_seesaw_loss_normed_mask_mstrain_2x_lvis_v1-c8551505.pth\n"
  },
  {
    "path": "configs/selfsup_pretrain/README.md",
    "content": "# Backbones Trained by Self-Supervise Algorithms\n\n<!-- [OTHERS] -->\n\n## Abstract\n\nUnsupervised image representations have significantly reduced the gap with supervised pretraining, notably with the recent achievements of contrastive learning methods. These contrastive methods typically work online and rely on a large number of explicit pairwise feature comparisons, which is computationally challenging. In this paper, we propose an online algorithm, SwAV, that takes advantage of contrastive methods without requiring to compute pairwise comparisons. Specifically, our method simultaneously clusters the data while enforcing consistency between cluster assignments produced for different augmentations (or views) of the same image, instead of comparing features directly as in contrastive learning. Simply put, we use a swapped prediction mechanism where we predict the cluster assignment of a view from the representation of another view. Our method can be trained with large and small batches and can scale to unlimited amounts of data. Compared to previous contrastive methods, our method is more memory efficient since it does not require a large memory bank or a special momentum network. In addition, we also propose a new data augmentation strategy, multi-crop, that uses a mix of views with different resolutions in place of two full-resolution views, without increasing the memory or compute requirements much. We validate our findings by achieving 75.3% top-1 accuracy on ImageNet with ResNet-50, as well as surpassing supervised pretraining on all the considered transfer tasks.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143997246-ac40fd8a-9b48-4ff5-a0d9-ba10e1e333d2.png\"/>\n</div>\n\nWe present Momentum Contrast (MoCo) for unsupervised visual representation learning. From a perspective on contrastive learning as dictionary look-up, we build a dynamic dictionary with a queue and a moving-averaged encoder. This enables building a large and consistent dictionary on-the-fly that facilitates contrastive unsupervised learning. MoCo provides competitive results under the common linear protocol on ImageNet classification. More importantly, the representations learned by MoCo transfer well to downstream tasks. MoCo can outperform its supervised pre-training counterpart in 7 detection/segmentation tasks on PASCAL VOC, COCO, and other datasets, sometimes surpassing it by large margins. This suggests that the gap between unsupervised and supervised representation learning has been largely closed in many vision tasks.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143997315-5ff824d4-1007-4b59-8952-bc5a2c0bfd78.png\" height=\"300\"/>\n</div>\n\n## Usage\n\nTo use a self-supervisely pretrained backbone, there are two steps to do:\n\n1. Download and convert the model to PyTorch-style supported by MMDetection\n2. Modify the config and change the training setting accordingly\n\n### Convert model\n\nFor more general usage, we also provide script `selfsup2mmdet.py` in the tools directory to convert the key of models pretrained by different self-supervised methods to PyTorch-style checkpoints used in MMDetection.\n\n```bash\npython -u tools/model_converters/selfsup2mmdet.py ${PRETRAIN_PATH} ${STORE_PATH} --selfsup ${method}\n```\n\nThis script convert model from `PRETRAIN_PATH` and store the converted model in `STORE_PATH`.\n\nFor example, to use a ResNet-50 backbone released by MoCo, you can download it from [here](https://dl.fbaipublicfiles.com/moco/moco_checkpoints/moco_v2_800ep/moco_v2_800ep_pretrain.pth.tar) and use the following command\n\n```bash\npython -u tools/model_converters/selfsup2mmdet.py ./moco_v2_800ep_pretrain.pth.tar mocov2_r50_800ep_pretrain.pth --selfsup moco\n```\n\nTo use the ResNet-50 backbone released by SwAV, you can download it from [here](https://dl.fbaipublicfiles.com/deepcluster/swav_800ep_pretrain.pth.tar)\n\n### Modify config\n\nThe backbone requires SyncBN and the `frozen_stages` need to be changed. A config that use the moco backbone is as below\n\n```python\n_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    pretrained='./mocov2_r50_800ep_pretrain.pth',\n    backbone=dict(\n        frozen_stages=0,\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False))\n\n```\n\n## Results and Models\n\n|  Method   |                            Backbone                            |  Style  |    Lr schd     | Mem (GB) | Inf time (fps) | box AP | mask AP |                         Config                         |                                                                                                                                                                                            Download                                                                                                                                                                                            |\n| :-------: | :------------------------------------------------------------: | :-----: | :------------: | :------: | :------------: | :----: | :-----: | :----------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Mask RCNN |  [R50 by MoCo v2](./mask-rcnn_r50-mocov2-pre_fpn_1x_coco.py)   | pytorch |       1x       |          |                |  38.0  |  34.3   |  [config](./mask-rcnn_r50-mocov2-pre_fpn_1x_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_mocov2-pretrain_1x_coco/mask_rcnn_r50_fpn_mocov2-pretrain_1x_coco_20210604_114614-a8b63483.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_mocov2-pretrain_1x_coco/mask_rcnn_r50_fpn_mocov2-pretrain_1x_coco_20210604_114614.log.json)       |\n| Mask RCNN | [R50 by MoCo v2](./mask-rcnn_r50-mocov2-pre_fpn_ms-2x_coco.py) | pytorch | multi-scale 2x |          |                |  40.8  |  36.8   | [config](./mask-rcnn_r50-mocov2-pre_fpn_ms-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_mocov2-pretrain_ms-2x_coco/mask_rcnn_r50_fpn_mocov2-pretrain_ms-2x_coco_20210605_163717-d95df20a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_mocov2-pretrain_ms-2x_coco/mask_rcnn_r50_fpn_mocov2-pretrain_ms-2x_coco_20210605_163717.log.json) |\n| Mask RCNN |     [R50 by SwAV](./mask-rcnn_r50-swav-pre_fpn_1x_coco.py)     | pytorch |       1x       |          |                |  39.1  |  35.7   |   [config](./mask-rcnn_r50-swav-pre_fpn_1x_coco.py)    |           [model](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_swav-pretrain_1x_coco/mask_rcnn_r50_fpn_swav-pretrain_1x_coco_20210604_114640-7b9baf28.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_swav-pretrain_1x_coco/mask_rcnn_r50_fpn_swav-pretrain_1x_coco_20210604_114640.log.json)           |\n| Mask RCNN |   [R50 by SwAV](./mask-rcnn_r50-swav-pre_fpn_ms-2x_coco.py)    | pytorch | multi-scale 2x |          |                |  41.3  |  37.3   |  [config](./mask-rcnn_r50-swav-pre_fpn_ms-2x_coco.py)  |     [model](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_swav-pretrain_ms-2x_coco/mask_rcnn_r50_fpn_swav-pretrain_ms-2x_coco_20210605_163717-08e26fca.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/selfsup_pretrain/mask_rcnn_r50_fpn_swav-pretrain_ms-2x_coco/mask_rcnn_r50_fpn_swav-pretrain_ms-2x_coco_20210605_163717.log.json)     |\n\n### Notice\n\n1. We only provide single-scale 1x and multi-scale 2x configs as examples to show how to use backbones trained by self-supervised algorithms. We will try to reproduce the results in their corresponding paper using the released backbone in the future. Please stay tuned.\n\n## Citation\n\nWe support to apply the backbone models pre-trained by different self-supervised methods in detection systems and provide their results on Mask R-CNN.\n\nThe pre-trained models are converted from [MoCo](https://github.com/facebookresearch/moco) and downloaded from [SwAV](https://github.com/facebookresearch/swav).\n\nFor SwAV, please cite\n\n```latex\n@article{caron2020unsupervised,\n  title={Unsupervised Learning of Visual Features by Contrasting Cluster Assignments},\n  author={Caron, Mathilde and Misra, Ishan and Mairal, Julien and Goyal, Priya and Bojanowski, Piotr and Joulin, Armand},\n  booktitle={Proceedings of Advances in Neural Information Processing Systems (NeurIPS)},\n  year={2020}\n}\n```\n\nFor MoCo, please cite\n\n```latex\n@Article{he2019moco,\n  author  = {Kaiming He and Haoqi Fan and Yuxin Wu and Saining Xie and Ross Girshick},\n  title   = {Momentum Contrast for Unsupervised Visual Representation Learning},\n  journal = {arXiv preprint arXiv:1911.05722},\n  year    = {2019},\n}\n@Article{chen2020mocov2,\n  author  = {Xinlei Chen and Haoqi Fan and Ross Girshick and Kaiming He},\n  title   = {Improved Baselines with Momentum Contrastive Learning},\n  journal = {arXiv preprint arXiv:2003.04297},\n  year    = {2020},\n}\n```\n"
  },
  {
    "path": "configs/selfsup_pretrain/mask-rcnn_r50-mocov2-pre_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        frozen_stages=0,\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='./mocov2_r50_800ep_pretrain.pth')))\n"
  },
  {
    "path": "configs/selfsup_pretrain/mask-rcnn_r50-mocov2-pre_fpn_ms-2x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        frozen_stages=0,\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='./mocov2_r50_800ep_pretrain.pth')))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/selfsup_pretrain/mask-rcnn_r50-swav-pre_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        frozen_stages=0,\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='./swav_800ep_pretrain.pth.tar')))\n"
  },
  {
    "path": "configs/selfsup_pretrain/mask-rcnn_r50-swav-pre_fpn_ms-2x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    backbone=dict(\n        frozen_stages=0,\n        norm_cfg=dict(type='SyncBN', requires_grad=True),\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='./swav_800ep_pretrain.pth.tar')))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/simple_copy_paste/README.md",
    "content": "# SimpleCopyPaste\n\n> [Simple Copy-Paste is a Strong Data Augmentation Method for Instance Segmentation](https://arxiv.org/abs/2012.07177)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nBuilding instance segmentation models that are data-efficient and can handle rare object categories is an important challenge in computer vision. Leveraging data augmentations is a promising direction towards addressing this challenge. Here, we perform a systematic study of the Copy-Paste augmentation (\\[13, 12\\]) for instance segmentation where we randomly paste objects onto an image. Prior studies on Copy-Paste relied on modeling the surrounding visual context for pasting the objects. However, we find that the simple mechanism of pasting objects randomly is good enough and can provide solid gains on top of strong baselines. Furthermore, we show Copy-Paste is additive with semi-supervised methods that leverage extra data through pseudo labeling (e.g. self-training). On COCO instance segmentation, we achieve 49.1 mask AP and 57.3 box AP, an improvement of +0.6 mask AP and +1.5 box AP over the previous state-of-the-art. We further demonstrate that Copy-Paste can lead to significant improvements on the LVIS benchmark. Our baseline model outperforms the LVIS 2020 Challenge winning entry by +3.6 mask AP on rare categories.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/161843866-c5b769da-58b2-4c1f-8078-db4a4ded3881.png\"/>\n</div>\n\n## Results and Models\n\n### Mask R-CNN with Standard Scale Jittering (SSJ) and Simple Copy-Paste(SCP)\n\nStandard Scale Jittering(SSJ) resizes and crops an image with a resize range of 0.8 to 1.25 of the original image size, and Simple Copy-Paste(SCP) selects a random subset of objects from one of the images and pastes them onto the other image.\n\n| Backbone | Training schedule | Augmentation | batch size | box AP | mask AP |                                         Config                                         |                                                                                                                                                                                                                               Download                                                                                                                                                                                                                               |\n| :------: | :---------------: | :----------: | :--------: | :----: | :-----: | :------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   |        90k        |     SSJ      |     64     |  43.3  |  39.0   |   [config](./mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-90k_coco.py)    |           [model](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_90k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_90k_coco_20220316_181409-f79c84c5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_90k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_90k_coco_20220316_181409.log.json)           |\n|   R-50   |        90k        |   SSJ+SCP    |     64     |  43.8  |  39.2   | [config](./mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-90k_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_90k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_90k_coco_20220316_181307-6bc5726f.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_90k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_90k_coco_20220316_181307.log.json)   |\n|   R-50   |       270k        |     SSJ      |     64     |  43.5  |  39.1   |   [config](./mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-270k_coco.py)   |         [model](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_270k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_270k_coco_20220324_182940-33a100c5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_270k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_270k_coco_20220324_182940.log.json)         |\n|   R-50   |       270k        |   SSJ+SCP    |     64     |  45.1  |  40.3   | [config](./mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-270k_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco_20220324_201229-80ee90b7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco_20220324_201229.log.json) |\n\n## Citation\n\n```latex\n@inproceedings{ghiasi2021simple,\n  title={Simple copy-paste is a strong data augmentation method for instance segmentation},\n  author={Ghiasi, Golnaz and Cui, Yin and Srinivas, Aravind and Qian, Rui and Lin, Tsung-Yi and Cubuk, Ekin D and Le, Quoc V and Zoph, Barret},\n  booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},\n  pages={2918--2928},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-270k_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    # 270k iterations with batch_size 64 is roughly equivalent to 144 epochs\n    '../common/ssj_270k_coco-instance.py',\n]\n\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\n# Use MMSyncBN that handles empty tensor in head. It can be changed to\n# SyncBN after https://github.com/pytorch/pytorch/issues/36530 is fixed\nhead_norm_cfg = dict(type='MMSyncBN', requires_grad=True)\nmodel = dict(\n    # the model is trained from scratch, so init_cfg is None\n    data_preprocessor=dict(\n        # pad_size_divisor=32 is unnecessary in training but necessary\n        # in testing.\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        frozen_stages=-1, norm_eval=False, norm_cfg=norm_cfg, init_cfg=None),\n    neck=dict(norm_cfg=norm_cfg),\n    rpn_head=dict(num_convs=2),  # leads to 0.1+ mAP\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=head_norm_cfg),\n        mask_head=dict(norm_cfg=head_norm_cfg)))\n"
  },
  {
    "path": "configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-90k_coco.py",
    "content": "_base_ = 'mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-270k_coco.py'  # noqa\n\n# training schedule for 90k\nmax_iters = 90000\n\n# learning rate policy\n# lr steps at [0.9, 0.95, 0.975] of the maximum iterations\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.067, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=90000,\n        by_epoch=False,\n        milestones=[81000, 85500, 87750],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-270k_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    # 270k iterations with batch_size 64 is roughly equivalent to 144 epochs\n    '../common/ssj_scp_270k_coco-instance.py'\n]\n\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\n# Use MMSyncBN that handles empty tensor in head. It can be changed to\n# SyncBN after https://github.com/pytorch/pytorch/issues/36530 is fixed\nhead_norm_cfg = dict(type='MMSyncBN', requires_grad=True)\nmodel = dict(\n    # the model is trained from scratch, so init_cfg is None\n    data_preprocessor=dict(\n        # pad_size_divisor=32 is unnecessary in training but necessary\n        # in testing.\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        frozen_stages=-1, norm_eval=False, norm_cfg=norm_cfg, init_cfg=None),\n    neck=dict(norm_cfg=norm_cfg),\n    rpn_head=dict(num_convs=2),  # leads to 0.1+ mAP\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=head_norm_cfg),\n        mask_head=dict(norm_cfg=head_norm_cfg)))\n"
  },
  {
    "path": "configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-90k_coco.py",
    "content": "_base_ = 'mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-270k_coco.py'  # noqa\n\n# training schedule for 90k\nmax_iters = 90000\n\n# learning rate policy\n# lr steps at [0.9, 0.95, 0.975] of the maximum iterations\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.067, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=90000,\n        by_epoch=False,\n        milestones=[81000, 85500, 87750],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/simple_copy_paste/metafile.yml",
    "content": "Collections:\n  - Name: SimpleCopyPaste\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 32x A100 GPUs\n      Architecture:\n        - Softmax\n        - RPN\n        - Convolution\n        - Dense Connections\n        - FPN\n        - ResNet\n        - RoIAlign\n    Paper:\n      URL: https://arxiv.org/abs/2012.07177\n      Title: \"Simple Copy-Paste is a Strong Data Augmentation Method for Instance Segmentation\"\n    README: configs/simple_copy_paste/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.25.0/mmdet/datasets/pipelines/transforms.py#L2762\n      Version: v2.25.0\n\nModels:\n  - Name: mask-rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_270k_coco\n    In Collection: SimpleCopyPaste\n    Config: configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-270k_coco.py\n    Metadata:\n      Training Memory (GB): 7.2\n      Iterations: 270000\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.5\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_270k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_270k_coco_20220324_182940-33a100c5.pth\n\n  - Name: mask-rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_90k_coco\n    In Collection: SimpleCopyPaste\n    Config: configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-90k_coco.py\n    Metadata:\n      Training Memory (GB): 7.2\n      Iterations: 90000\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.3\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_90k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_32x2_90k_coco_20220316_181409-f79c84c5.pth\n\n  - Name: mask-rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco\n    In Collection: SimpleCopyPaste\n    Config: configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-270k_coco.py\n    Metadata:\n      Training Memory (GB): 7.2\n      Iterations: 270000\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.1\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 40.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco_20220324_201229-80ee90b7.pth\n\n  - Name: mask-rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_90k_coco\n    In Collection: SimpleCopyPaste\n    Config: configs/simple_copy_paste/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_32xb2-ssj-scp-90k_coco.py\n    Metadata:\n      Training Memory (GB): 7.2\n      Iterations: 90000\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.8\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_90k_coco/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_90k_coco_20220316_181307-6bc5726f.pth\n"
  },
  {
    "path": "configs/soft_teacher/README.md",
    "content": "# SoftTeacher\n\n> [End-to-End Semi-Supervised Object Detection with Soft Teacher](https://arxiv.org/abs/2106.09018)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThis paper presents an end-to-end semi-supervised object detection approach, in contrast to previous more complex multi-stage methods. The end-to-end training gradually improves pseudo label qualities during the curriculum, and the more and more accurate pseudo labels in turn benefit object detection training. We also propose two simple yet effective techniques within this framework: a soft teacher mechanism where the classification loss of each unlabeled bounding box is weighed by the classification score produced by the teacher network; a box jittering approach to select reliable pseudo boxes for the learning of box regression. On the COCO benchmark, the proposed approach outperforms previous methods by a large margin under various labeling ratios, i.e. 1%, 5% and 10%. Moreover, our approach proves to perform also well when the amount of labeled data is relatively large. For example, it can improve a 40.9 mAP baseline detector trained using the full COCO training set by +3.6 mAP, reaching 44.5 mAP, by leveraging the 123K unlabeled images of COCO. On the state-of-the-art Swin Transformer based object detector (58.9 mAP on test-dev), it can still significantly improve the detection accuracy by +1.5 mAP, reaching 60.4 mAP, and improve the instance segmentation accuracy by +1.2 mAP, reaching 52.4 mAP. Further incorporating with the Object365 pre-trained model, the detection accuracy reaches 61.3 mAP and the instance segmentation accuracy reaches 53.0 mAP, pushing the new state-of-the-art.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/186086683-f8a69813-d09c-4c3f-a86a-e233a708cd38.png\"/>\n</div>\n\n## Results and Models\n\n|    Model    |   Detector   | Labeled Dataset | Iteration | box AP |                                  Config                                   |                                                                                                                                                                                                            Download                                                                                                                                                                                                            |\n| :---------: | :----------: | :-------------: | :-------: | :----: | :-----------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| SoftTeacher | Faster R-CNN |     COCO-1%     |   180k    |  19.9  | [config](./soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.01-coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.01-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230330_233412-3c8f6d4a.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.01-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230330_233412.log.json) |\n| SoftTeacher | Faster R-CNN |     COCO-2%     |   180k    |  24.9  | [config](./soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.02-coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.02-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230331_020244-c0d2c3aa.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.02-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230331_020244.log.json) |\n| SoftTeacher | Faster R-CNN |     COCO-5%     |   180k    |  30.4  | [config](./soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.05-coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.05-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230331_070656-308798ad.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.05-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230331_070656.log.json) |\n| SoftTeacher | Faster R-CNN |    COCO-10%     |   180k    |  33.8  | [config](./soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py)  |  [model](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230330_232113-b46f78d0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230330_232113.log.json)  |\n\n## Citation\n\n```latex\n@article{xu2021end,\n  title={End-to-End Semi-Supervised Object Detection with Soft Teacher},\n  author={Xu, Mengde and Zhang, Zheng and Hu, Han and Wang, Jianfeng and Wang, Lijuan and Wei, Fangyun and Bai, Xiang and Liu, Zicheng},\n  journal={Proceedings of the IEEE/CVF International Conference on Computer Vision (ICCV)},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/soft_teacher/metafile.yml",
    "content": "Collections:\n  - Name: SoftTeacher\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/2106.09018\n      Title: \"End-to-End Semi-Supervised Object Detection with Soft Teacher\"\n    README: configs/soft_teacher/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v3.0.0rc1/mmdet/models/detectors/soft_teacher.py#L20\n      Version: v3.0.0rc1\n\nModels:\n  - Name: soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.01-coco.py\n    In Collection: SoftTeacher\n    Config: configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.01-coco.py\n    Metadata:\n      Iterations: 180000\n    Results:\n      - Task: Semi-Supervised Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 19.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.01-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230330_233412-3c8f6d4a.pth\n\n  - Name: soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.02-coco.py\n    In Collection: SoftTeacher\n    Config: configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.02-coco.py\n    Metadata:\n      Iterations: 180000\n    Results:\n      - Task: Semi-Supervised Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 24.9\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.02-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230331_020244-c0d2c3aa.pth\n\n  - Name: soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.05-coco.py\n    In Collection: SoftTeacher\n    Config: configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.05-coco.py\n    Metadata:\n      Iterations: 180000\n    Results:\n      - Task: Semi-Supervised Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 30.4\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.05-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230331_070656-308798ad.pth\n\n  - Name: soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py\n    In Collection: SoftTeacher\n    Config: configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py\n    Metadata:\n      Iterations: 180000\n    Results:\n      - Task: Semi-Supervised Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 33.8\n    Weights: https://download.openmmlab.com/mmdetection/v3.0/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0_20230330_232113-b46f78d0.pth\n"
  },
  {
    "path": "configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.01-coco.py",
    "content": "_base_ = ['soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py']\n\n# 1% coco train2017 is set as labeled dataset\nlabeled_dataset = _base_.labeled_dataset\nunlabeled_dataset = _base_.unlabeled_dataset\nlabeled_dataset.ann_file = 'semi_anns/instances_train2017.1@1.json'\nunlabeled_dataset.ann_file = 'semi_anns/instances_train2017.1@1-unlabeled.json'\ntrain_dataloader = dict(\n    dataset=dict(datasets=[labeled_dataset, unlabeled_dataset]))\n"
  },
  {
    "path": "configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.02-coco.py",
    "content": "_base_ = ['soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py']\n\n# 2% coco train2017 is set as labeled dataset\nlabeled_dataset = _base_.labeled_dataset\nunlabeled_dataset = _base_.unlabeled_dataset\nlabeled_dataset.ann_file = 'semi_anns/instances_train2017.1@2.json'\nunlabeled_dataset.ann_file = 'semi_anns/instances_train2017.1@2-unlabeled.json'\ntrain_dataloader = dict(\n    dataset=dict(datasets=[labeled_dataset, unlabeled_dataset]))\n"
  },
  {
    "path": "configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.05-coco.py",
    "content": "_base_ = ['soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py']\n\n# 5% coco train2017 is set as labeled dataset\nlabeled_dataset = _base_.labeled_dataset\nunlabeled_dataset = _base_.unlabeled_dataset\nlabeled_dataset.ann_file = 'semi_anns/instances_train2017.1@5.json'\nunlabeled_dataset.ann_file = 'semi_anns/instances_train2017.1@5-unlabeled.json'\ntrain_dataloader = dict(\n    dataset=dict(datasets=[labeled_dataset, unlabeled_dataset]))\n"
  },
  {
    "path": "configs/soft_teacher/soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/semi_coco_detection.py'\n]\n\ndetector = _base_.model\ndetector.data_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[103.530, 116.280, 123.675],\n    std=[1.0, 1.0, 1.0],\n    bgr_to_rgb=False,\n    pad_size_divisor=32)\ndetector.backbone = dict(\n    type='ResNet',\n    depth=50,\n    num_stages=4,\n    out_indices=(0, 1, 2, 3),\n    frozen_stages=1,\n    norm_cfg=dict(type='BN', requires_grad=False),\n    norm_eval=True,\n    style='caffe',\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint='open-mmlab://detectron2/resnet50_caffe'))\n\nmodel = dict(\n    _delete_=True,\n    type='SoftTeacher',\n    detector=detector,\n    data_preprocessor=dict(\n        type='MultiBranchDataPreprocessor',\n        data_preprocessor=detector.data_preprocessor),\n    semi_train_cfg=dict(\n        freeze_teacher=True,\n        sup_weight=1.0,\n        unsup_weight=4.0,\n        pseudo_label_initial_score_thr=0.5,\n        rpn_pseudo_thr=0.9,\n        cls_pseudo_thr=0.9,\n        reg_pseudo_thr=0.02,\n        jitter_times=10,\n        jitter_scale=0.06,\n        min_pseudo_bbox_wh=(1e-2, 1e-2)),\n    semi_test_cfg=dict(predict_on='teacher'))\n\n# 10% coco train2017 is set as labeled dataset\nlabeled_dataset = _base_.labeled_dataset\nunlabeled_dataset = _base_.unlabeled_dataset\nlabeled_dataset.ann_file = 'semi_anns/instances_train2017.1@10.json'\nunlabeled_dataset.ann_file = 'semi_anns/' \\\n                             'instances_train2017.1@10-unlabeled.json'\nunlabeled_dataset.data_prefix = dict(img='train2017/')\ntrain_dataloader = dict(\n    dataset=dict(datasets=[labeled_dataset, unlabeled_dataset]))\n\n# training schedule for 180k\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=180000, val_interval=5000)\nval_cfg = dict(type='TeacherStudentValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=180000,\n        by_epoch=False,\n        milestones=[120000, 160000],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=False, interval=10000, max_keep_ckpts=2))\nlog_processor = dict(by_epoch=False)\n\ncustom_hooks = [dict(type='MeanTeacherHook')]\n"
  },
  {
    "path": "configs/solo/README.md",
    "content": "# SOLO\n\n> [SOLO: Segmenting Objects by Locations](https://arxiv.org/abs/1912.04488)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe 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.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143998371-10e6f14b-4506-481d-91a7-5f8f58213307.png\"/>\n</div>\n\n## Results and Models\n\n### SOLO\n\n| Backbone |  Style  | MS train | Lr schd | Mem (GB) | Inf time (fps) | mask AP |                                                                                                                                Download                                                                                                                                |\n| :------: | :-----: | :------: | :-----: | :------: | :------------: | :-----: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   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) |\n|   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) |\n\n### Decoupled SOLO\n\n| Backbone |  Style  | MS train | Lr schd | Mem (GB) | Inf time (fps) | mask AP |                                                                                                                                                    Download                                                                                                                                                    |\n| :------: | :-----: | :------: | :-----: | :------: | :------------: | :-----: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   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) |\n|   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) |\n\n- Decoupled SOLO has a decoupled head which is different from SOLO head.\n  Decoupled SOLO serves as an efficient and equivalent variant in accuracy\n  of SOLO. Please refer to the corresponding config files for details.\n\n### Decoupled Light SOLO\n\n| Backbone |  Style  | MS train | Lr schd | Mem (GB) | Inf time (fps) | mask AP |                                                                                                                                                                Download                                                                                                                                                                |\n| :------: | :-----: | :------: | :-----: | :------: | :------------: | :-----: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   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) |\n\n- Decoupled Light SOLO using decoupled structure similar to Decoupled\n  SOLO head, with light-weight head and smaller input size, Please refer\n  to the corresponding config files for details.\n\n## Citation\n\n```latex\n@inproceedings{wang2020solo,\n  title     =  {{SOLO}: Segmenting Objects by Locations},\n  author    =  {Wang, Xinlong and Kong, Tao and Shen, Chunhua and Jiang, Yuning and Li, Lei},\n  booktitle =  {Proc. Eur. Conf. Computer Vision (ECCV)},\n  year      =  {2020}\n}\n```\n"
  },
  {
    "path": "configs/solo/decoupled-solo-light_r50_fpn_3x_coco.py",
    "content": "_base_ = './decoupled-solo_r50_fpn_3x_coco.py'\n\n# model settings\nmodel = dict(\n    mask_head=dict(\n        type='DecoupledSOLOLightHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 8, 16, 32, 32],\n        scale_ranges=((1, 64), (32, 128), (64, 256), (128, 512), (256, 2048)),\n        pos_scale=0.2,\n        num_grids=[40, 36, 24, 16, 12],\n        cls_down_index=0,\n        loss_mask=dict(\n            type='DiceLoss', use_sigmoid=True, activate=False,\n            loss_weight=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(852, 512), (852, 480), (852, 448), (852, 416), (852, 384),\n                (852, 352)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(852, 512), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/solo/decoupled-solo_r50_fpn_1x_coco.py",
    "content": "_base_ = './solo_r50_fpn_1x_coco.py'\n# model settings\nmodel = dict(\n    mask_head=dict(\n        type='DecoupledSOLOHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=7,\n        feat_channels=256,\n        strides=[8, 8, 16, 32, 32],\n        scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)),\n        pos_scale=0.2,\n        num_grids=[40, 36, 24, 16, 12],\n        cls_down_index=0,\n        loss_mask=dict(\n            type='DiceLoss', use_sigmoid=True, activate=False,\n            loss_weight=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)))\n"
  },
  {
    "path": "configs/solo/decoupled-solo_r50_fpn_3x_coco.py",
    "content": "_base_ = './solo_r50_fpn_3x_coco.py'\n\n# model settings\nmodel = dict(\n    mask_head=dict(\n        type='DecoupledSOLOHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=7,\n        feat_channels=256,\n        strides=[8, 8, 16, 32, 32],\n        scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)),\n        pos_scale=0.2,\n        num_grids=[40, 36, 24, 16, 12],\n        cls_down_index=0,\n        loss_mask=dict(\n            type='DiceLoss', use_sigmoid=True, activate=False,\n            loss_weight=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)))\n"
  },
  {
    "path": "configs/solo/metafile.yml",
    "content": "Collections:\n  - Name: SOLO\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - Convolution\n        - ResNet\n    Paper: https://arxiv.org/abs/1912.04488\n    README: configs/solo/README.md\n\nModels:\n  - Name: decoupled-solo_r50_fpn_1x_coco\n    In Collection: SOLO\n    Config: configs/solo/decoupled-solo_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      Epochs: 12\n    inference time (ms/im):\n      - value: 116.4\n        hardware: V100\n        backend: PyTorch\n        batch size: 1\n        mode: FP32\n        resolution: (1333, 800)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 33.9\n    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\n\n  - Name: decoupled-solo_r50_fpn_3x_coco\n    In Collection: SOLO\n    Config: configs/solo/decoupled-solo_r50_fpn_3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.9\n      Epochs: 36\n    inference time (ms/im):\n      - value: 117.2\n        hardware: V100\n        backend: PyTorch\n        batch size: 1\n        mode: FP32\n        resolution: (1333, 800)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 36.7\n    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\n\n  - Name: decoupled-solo-light_r50_fpn_3x_coco\n    In Collection: SOLO\n    Config: configs/solo/decoupled-solo-light_r50_fpn_3x_coco.py\n    Metadata:\n      Training Memory (GB): 2.2\n      Epochs: 36\n    inference time (ms/im):\n      - value: 35.0\n        hardware: V100\n        backend: PyTorch\n        batch size: 1\n        mode: FP32\n        resolution: (852, 512)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 32.9\n    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\n\n  - Name: solo_r50_fpn_3x_coco\n    In Collection: SOLO\n    Config: configs/solo/solo_r50_fpn_3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.4\n      Epochs: 36\n    inference time (ms/im):\n      - value: 94.2\n        hardware: V100\n        backend: PyTorch\n        batch size: 1\n        mode: FP32\n        resolution: (1333, 800)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 35.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_3x_coco/solo_r50_fpn_3x_coco_20210901_012353-11d224d7.pth\n\n  - Name: solo_r50_fpn_1x_coco\n    In Collection: SOLO\n    Config: configs/solo/solo_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.0\n      Epochs: 12\n    inference time (ms/im):\n      - value: 95.1\n        hardware: V100\n        backend: PyTorch\n        batch size: 1\n        mode: FP32\n        resolution: (1333, 800)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 33.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_1x_coco/solo_r50_fpn_1x_coco_20210821_035055-2290a6b8.pth\n"
  },
  {
    "path": "configs/solo/solo_r101_fpn_8xb8-lsj-200e_coco.py",
    "content": "_base_ = './solo_r50_fpn_8xb8-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/solo/solo_r18_fpn_8xb8-lsj-200e_coco.py",
    "content": "_base_ = './solo_r50_fpn_8xb8-lsj-200e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/solo/solo_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    type='SOLO',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50'),\n        style='pytorch'),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=0,\n        num_outs=5),\n    mask_head=dict(\n        type='SOLOHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=7,\n        feat_channels=256,\n        strides=[8, 8, 16, 32, 32],\n        scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)),\n        pos_scale=0.2,\n        num_grids=[40, 36, 24, 16, 12],\n        cls_down_index=0,\n        loss_mask=dict(type='DiceLoss', use_sigmoid=True, loss_weight=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)),\n    # model training and testing settings\n    test_cfg=dict(\n        nms_pre=500,\n        score_thr=0.1,\n        mask_thr=0.5,\n        filter_thr=0.05,\n        kernel='gaussian',  # gaussian/linear\n        sigma=2.0,\n        max_per_img=100))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n\nval_evaluator = dict(metric='segm')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/solo/solo_r50_fpn_3x_coco.py",
    "content": "_base_ = './solo_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 800), (1333, 768), (1333, 736), (1333, 704),\n                (1333, 672), (1333, 640)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# training schedule for 3x\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=36,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/solo/solo_r50_fpn_8xb8-lsj-200e_coco.py",
    "content": "_base_ = '../common/lsj-200e_coco-instance.py'\n\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\n# model settings\nmodel = dict(\n    type='SOLO',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50'),\n        style='pytorch'),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=0,\n        num_outs=5),\n    mask_head=dict(\n        type='SOLOHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=7,\n        feat_channels=256,\n        strides=[8, 8, 16, 32, 32],\n        scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)),\n        pos_scale=0.2,\n        num_grids=[40, 36, 24, 16, 12],\n        cls_down_index=0,\n        loss_mask=dict(type='DiceLoss', use_sigmoid=True, loss_weight=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)),\n    # model training and testing settings\n    test_cfg=dict(\n        nms_pre=500,\n        score_thr=0.1,\n        mask_thr=0.5,\n        filter_thr=0.05,\n        kernel='gaussian',  # gaussian/linear\n        sigma=2.0,\n        max_per_img=100))\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.01 * 4, momentum=0.9, weight_decay=0.00004),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/solov2/README.md",
    "content": "# SOLOv2\n\n> [SOLOv2: Dynamic and Fast Instance Segmentation](https://arxiv.org/abs/2003.10152)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIn this work, we aim at building a simple, direct, and fast instance segmentation\nframework with strong performance. We follow the principle of the SOLO method of\nWang et al. \"SOLO: segmenting objects by locations\". Importantly, we take one\nstep further by dynamically learning the mask head of the object segmenter such\nthat the mask head is conditioned on the location. Specifically, the mask branch\nis decoupled into a mask kernel branch and mask feature branch, which are\nresponsible for learning the convolution kernel and the convolved features\nrespectively. Moreover, we propose Matrix NMS (non maximum suppression) to\nsignificantly reduce the inference time overhead due to NMS of masks. Our\nMatrix NMS performs NMS with parallel matrix operations in one shot, and\nyields better results. We demonstrate a simple direct instance segmentation\nsystem, outperforming a few state-of-the-art methods in both speed and accuracy.\nA light-weight version of SOLOv2 executes at 31.3 FPS and yields 37.1% AP.\nMoreover, our state-of-the-art results in object detection (from our mask byproduct)\nand panoptic segmentation show the potential to serve as a new strong baseline\nfor many instance-level recognition tasks besides instance segmentation.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/48282753/167235090-f20dab74-43a5-44ed-9f11-4e5f08866f45.png\"/>\n</div>\n\n## Results and Models\n\n### SOLOv2\n\n|  Backbone  |  Style  | MS train | Lr schd | Mem (GB) | mask AP |                    Config                     |                                                                                                                                                Download                                                                                                                                                |\n| :--------: | :-----: | :------: | :-----: | :------: | :-----: | :-------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50    | pytorch |    N     |   1x    |   5.1    |  34.8   |     [config](./solov2_r50_fpn_1x_coco.py)     |      [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r50_fpn_1x_coco/solov2_r50_fpn_1x_coco_20220512_125858-a357fa23.pth)           \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r50_fpn_1x_coco/solov2_r50_fpn_1x_coco_20220512_125858.log.json)      |\n|    R-50    | pytorch |    Y     |   3x    |   5.1    |  37.5   |   [config](./solov2_r50_fpn_ms-3x_coco.py)    |      [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r50_fpn_3x_coco/solov2_r50_fpn_3x_coco_20220512_125856-fed092d4.pth)           \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r50_fpn_3x_coco/solov2_r50_fpn_3x_coco_20220512_125856.log.json)      |\n|   R-101    | pytorch |    Y     |   3x    |   6.9    |  39.1   |   [config](./solov2_r101_fpn_ms-3x_coco.py)   |     [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r101_fpn_3x_coco/solov2_r101_fpn_3x_coco_20220511_095119-c559a076.pth)         \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r101_fpn_3x_coco/solov2_r101_fpn_3x_coco_20220511_095119.log.json)     |\n| R-101(DCN) | pytorch |    Y     |   3x    |   7.1    |  41.2   | [config](./solov2_r101-dcn_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r101_dcn_fpn_3x_coco/solov2_r101_dcn_fpn_3x_coco_20220513_214734-16c966cb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r101_dcn_fpn_3x_coco/solov2_r101_dcn_fpn_3x_coco_20220513_214734.log.json) |\n| X-101(DCN) | pytorch |    Y     |   3x    |   11.3   |  42.4   | [config](./solov2_x101-dcn_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_x101_dcn_fpn_3x_coco/solov2_x101_dcn_fpn_3x_coco_20220513_214337-aef41095.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_x101_dcn_fpn_3x_coco/solov2_x101_dcn_fpn_3x_coco_20220513_214337.log.json) |\n\n### Light SOLOv2\n\n| Backbone |  Style  | MS train | Lr schd | Mem (GB) | mask AP |                     Config                     |                                                                                                                                                  Download                                                                                                                                                  |\n| :------: | :-----: | :------: | :-----: | :------: | :-----: | :--------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-18   | pytorch |    Y     |   3x    |   9.1    |  29.7   | [config](./solov2-light_r18_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r18_fpn_3x_coco/solov2_light_r18_fpn_3x_coco_20220511_083717-75fa355b.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r18_fpn_3x_coco/solov2_light_r18_fpn_3x_coco_20220511_083717.log.json) |\n|   R-34   | pytorch |    Y     |   3x    |   9.3    |  31.9   | [config](./solov2-light_r34_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r34_fpn_3x_coco/solov2_light_r34_fpn_3x_coco_20220511_091839-e51659d3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r34_fpn_3x_coco/solov2_light_r34_fpn_3x_coco_20220511_091839.log.json) |\n|   R-50   | pytorch |    Y     |   3x    |   9.9    |  33.7   | [config](./solov2-light_r50_fpn_ms-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r50_fpn_3x_coco/solov2_light_r50_fpn_3x_coco_20220512_165256-c93a6074.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r50_fpn_3x_coco/solov2_light_r50_fpn_3x_coco_20220512_165256.log.json) |\n\n## Citation\n\n```latex\n@article{wang2020solov2,\n  title={SOLOv2: Dynamic and Fast Instance Segmentation},\n  author={Wang, Xinlong and Zhang, Rufeng and  Kong, Tao and Li, Lei and Shen, Chunhua},\n  journal={Proc. Advances in Neural Information Processing Systems (NeurIPS)},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/solov2/metafile.yml",
    "content": "Collections:\n  - Name: SOLOv2\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x A100 GPUs\n      Architecture:\n        - FPN\n        - Convolution\n        - ResNet\n    Paper: https://arxiv.org/abs/2003.10152\n    README: configs/solov2/README.md\n\nModels:\n  - Name: solov2_r50_fpn_1x_coco\n    In Collection: SOLOv2\n    Config: configs/solov2/solov2_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 5.1\n      Epochs: 12\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 34.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r50_fpn_1x_coco/solov2_r50_fpn_1x_coco_20220512_125858-a357fa23.pth\n\n  - Name: solov2_r50_fpn_ms-3x_coco\n    In Collection: SOLOv2\n    Config: configs/solov2/solov2_r50_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 5.1\n      Epochs: 36\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r50_fpn_3x_coco/solov2_r50_fpn_3x_coco_20220512_125856-fed092d4.pth\n\n  - Name: solov2_r101-dcn_fpn_ms-3x_coco\n    In Collection: SOLOv2\n    Config: configs/solov2/solov2_r101-dcn_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.1\n      Epochs: 36\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_r101_dcn_fpn_3x_coco/solov2_r101_dcn_fpn_3x_coco_20220513_214734-16c966cb.pth\n\n  - Name: solov2_x101-dcn_fpn_ms-3x_coco\n    In Collection: SOLOv2\n    Config: configs/solov2/solov2_x101-dcn_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 11.3\n      Epochs: 36\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 42.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_x101_dcn_fpn_3x_coco/solov2_x101_dcn_fpn_3x_coco_20220513_214337-aef41095.pth\n\n  - Name: solov2-light_r18_fpn_ms-3x_coco\n    In Collection: SOLOv2\n    Config: configs/solov2/solov2-light_r18_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 9.1\n      Epochs: 36\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 29.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r18_fpn_3x_coco/solov2_light_r18_fpn_3x_coco_20220511_083717-75fa355b.pth\n\n  - Name: solov2-light_r50_fpn_ms-3x_coco\n    In Collection: SOLOv2\n    Config: configs/solov2/solov2-light_r50_fpn_ms-3x_coco.py\n    Metadata:\n      Training Memory (GB): 9.9\n      Epochs: 36\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 33.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/solov2/solov2_light_r50_fpn_3x_coco/solov2_light_r50_fpn_3x_coco_20220512_165256-c93a6074.pth\n"
  },
  {
    "path": "configs/solov2/solov2-light_r18_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2-light_r50_fpn_ms-3x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=18, init_cfg=dict(checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/solov2/solov2-light_r34_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2-light_r50_fpn_ms-3x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=34, init_cfg=dict(checkpoint='torchvision://resnet34')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "configs/solov2/solov2-light_r50-dcn_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2-light_r50_fpn_ms-3x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deformable_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)),\n    mask_head=dict(\n        feat_channels=256,\n        stacked_convs=3,\n        scale_ranges=((1, 64), (32, 128), (64, 256), (128, 512), (256, 2048)),\n        mask_feature_head=dict(out_channels=128),\n        dcn_cfg=dict(type='DCNv2'),\n        dcn_apply_to_all_conv=False))  # light solov2 head\n"
  },
  {
    "path": "configs/solov2/solov2-light_r50_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2_r50_fpn_1x_coco.py'\n\n# model settings\nmodel = dict(\n    mask_head=dict(\n        stacked_convs=2,\n        feat_channels=256,\n        scale_ranges=((1, 56), (28, 112), (56, 224), (112, 448), (224, 896)),\n        mask_feature_head=dict(out_channels=128)))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(768, 512), (768, 480), (768, 448), (768, 416), (768, 384),\n                (768, 352)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(448, 768), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# training schedule for 3x\nmax_epochs = 36\ntrain_cfg = dict(by_epoch=True, max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=36,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/solov2/solov2_r101-dcn_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2_r50_fpn_ms-3x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(checkpoint='torchvision://resnet101'),\n        dcn=dict(type='DCNv2', deformable_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)),\n    mask_head=dict(\n        mask_feature_head=dict(conv_cfg=dict(type='DCNv2')),\n        dcn_cfg=dict(type='DCNv2'),\n        dcn_apply_to_all_conv=True))\n"
  },
  {
    "path": "configs/solov2/solov2_r101_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2_r50_fpn_ms-3x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        depth=101, init_cfg=dict(checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/solov2/solov2_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\nmodel = dict(\n    type='SOLOv2',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50'),\n        style='pytorch'),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=0,\n        num_outs=5),\n    mask_head=dict(\n        type='SOLOV2Head',\n        num_classes=80,\n        in_channels=256,\n        feat_channels=512,\n        stacked_convs=4,\n        strides=[8, 8, 16, 32, 32],\n        scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)),\n        pos_scale=0.2,\n        num_grids=[40, 36, 24, 16, 12],\n        cls_down_index=0,\n        mask_feature_head=dict(\n            feat_channels=128,\n            start_level=0,\n            end_level=3,\n            out_channels=256,\n            mask_stride=4,\n            norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)),\n        loss_mask=dict(type='DiceLoss', use_sigmoid=True, loss_weight=3.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0)),\n    # model training and testing settings\n    test_cfg=dict(\n        nms_pre=500,\n        score_thr=0.1,\n        mask_thr=0.5,\n        filter_thr=0.05,\n        kernel='gaussian',  # gaussian/linear\n        sigma=2.0,\n        max_per_img=100))\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.01), clip_grad=dict(max_norm=35, norm_type=2))\n\nval_evaluator = dict(metric='segm')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/solov2/solov2_r50_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 800), (1333, 768), (1333, 736), (1333, 704),\n                (1333, 672), (1333, 640)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# training schedule for 3x\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 3,\n        by_epoch=False,\n        begin=0,\n        end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=36,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/solov2/solov2_x101-dcn_fpn_ms-3x_coco.py",
    "content": "_base_ = './solov2_r50_fpn_ms-3x_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        dcn=dict(type='DCNv2', deformable_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')),\n    mask_head=dict(\n        mask_feature_head=dict(conv_cfg=dict(type='DCNv2')),\n        dcn_cfg=dict(type='DCNv2'),\n        dcn_apply_to_all_conv=True))\n"
  },
  {
    "path": "configs/sort/README.md",
    "content": "# Simple online and realtime tracking\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nThis paper explores a pragmatic approach to multiple object tracking where the main focus is to associate objects efficiently for online and realtime applications. To this end, detection quality is identified as a key factor influencing tracking performance, where changing the detector can improve tracking by up to 18.9%. Despite only using a rudimentary combination of familiar techniques such as the Kalman Filter and Hungarian algorithm for the tracking components, this approach achieves an accuracy comparable to state-of-the-art online trackers. Furthermore, due to the simplicity of our tracking method, the tracker updates at a rate of 260 Hz which is over 20x faster than other state-of-the-art trackers.\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/99722489/176848133-d6621813-7b8f-4b25-96cd-2fbcc87983ce.png\"/>\n</div>\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@inproceedings{bewley2016simple,\n  title={Simple online and realtime tracking},\n  author={Bewley, Alex and Ge, Zongyuan and Ott, Lionel and Ramos, Fabio and Upcroft, Ben},\n  booktitle={2016 IEEE International Conference on Image Processing (ICIP)},\n  pages={3464--3468},\n  year={2016},\n  organization={IEEE}\n}\n```\n\n## Results and models on MOT17\n\n| Method |      Detector      | ReID | Train Set  | Test Set | Public | Inf time (fps) | HOTA | MOTA | IDF1 |  FP   |  FN   | IDSw. |                                     Config                                     |                                                       Download                                                       |\n| :----: | :----------------: | :--: | :--------: | :------: | :----: | :------------: | :--: | :--: | :--: | :---: | :---: | :---: | :----------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------: |\n|  SORT  | R50-FasterRCNN-FPN |  -   | half-train | half-val |   N    |      18.6      | 52.0 | 62.0 | 57.8 | 15150 | 40410 | 5847  | [config](sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py) | [detector](https://download.openmmlab.com/mmtracking/mot/faster_rcnn/faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nWe implement SORT with independent detector models.\nNote that, due to the influence of parameters such as learning rate in default configuration file,\nwe recommend using 8 GPUs for training in order to reproduce accuracy.\n\nYou can train the detector as follows.\n\n```shell script\n# Training Faster R-CNN on mot17-half-train dataset with following command.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_train.sh configs/sort/faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 4. Testing and evaluation\n\n### 4.1 Example on MOTxx-halfval dataset\n\n**4.1.1 use separate trained detector model to evaluating and testing**\\*\n\n```shell script\n# Example 1: Test on motXX-half-val set.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_test_tracking.sh configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8 --detector ${DETECTOR_CHECKPOINT_PATH}\n```\n\n**4.1.2 use video_baesd to evaluating and testing**\n\nwe also provide two_ways(img_based or video_based) to evaluating and testing.\nif you want to use video_based to evaluating and testing, you can modify config as follows\n\n```\nval_dataloader = dict(\n    sampler=dict(type='DefaultSampler', shuffle=False, round_up=False))\n```\n\n### 4.2 Example on MOTxx-test dataset\n\nIf you want to get the results of the [MOT Challenge](https://motchallenge.net/) test set,\nplease use the following command to generate result files that can be used for submission.\nIt will be stored in `./mot_17_test_res`, you can modify the saved path in `test_evaluator` of the config.\n\n```shell script\n# Example 2: Test on motxx-test set\n# The number after config file represents the number of GPUs used\nbash tools/dist_test_tracking.sh configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17train_test-mot17test.py 8 --detector ${DETECTOR_CHECKPOINT_PATH}\n```\n\nIf you want to know about more detailed usage of `test_tracking.py/dist_test_tracking.sh/slurm_test_tracking.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 5.Inference\n\nUse a single GPU to predict a video and save it as a video.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py --detector ${DETECTOR_CHECKPOINT_PATH}  --out mot.mp4\n```\n\nIf you want to know about more detailed usage of `mot_demo.py`, please refer to this [document](../../docs/en/user_guides/tracking_inference.md).\n"
  },
  {
    "path": "configs/sort/faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/mot_challenge_det.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    rpn_head=dict(\n        bbox_coder=dict(clip_border=False),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        bbox_head=dict(\n            num_classes=1,\n            bbox_coder=dict(clip_border=False),\n            loss_bbox=dict(type='SmoothL1Loss', loss_weight=1.0))),\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint=  # noqa: E251\n        'http://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_2x_coco/faster_rcnn_r50_fpn_2x_coco_bbox_mAP-0.384_20200504_210434-a5d8aa15.pth'  # noqa: E501\n    ))\n\n# training schedule for 4e\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=4, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.01, by_epoch=False, begin=0, end=100),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=4,\n        by_epoch=True,\n        milestones=[3],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/sort/faster-rcnn_r50_fpn_8xb2-4e_mot17train_test-mot17train.py",
    "content": "_base_ = ['./faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval']\n# data\ndata_root = 'data/MOT17/'\ntrain_dataloader = dict(\n    dataset=dict(ann_file='annotations/train_cocoformat.json'))\nval_dataloader = dict(\n    dataset=dict(ann_file='annotations/train_cocoformat.json'))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(ann_file=data_root + 'annotations/train_cocoformat.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/sort/faster-rcnn_r50_fpn_8xb2-8e_mot20halftrain_test-mot20halfval.py",
    "content": "_base_ = ['./faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval']\nmodel = dict(\n    rpn_head=dict(bbox_coder=dict(clip_border=True)),\n    roi_head=dict(\n        bbox_head=dict(bbox_coder=dict(clip_border=True), num_classes=1)))\n# data\ndata_root = 'data/MOT20/'\ntrain_dataloader = dict(dataset=dict(data_root=data_root))\nval_dataloader = dict(dataset=dict(data_root=data_root))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(ann_file=data_root +\n                     'annotations/half-val_cocoformat.json')\ntest_evaluator = val_evaluator\n\n# training schedule for 8e\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=8, val_interval=1)\n\n# learning rate\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.01, by_epoch=False, begin=0, end=100),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=8,\n        by_epoch=True,\n        milestones=[6],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/sort/faster-rcnn_r50_fpn_8xb2-8e_mot20train_test-mot20train.py",
    "content": "_base_ = ['./faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval']\nmodel = dict(\n    rpn_head=dict(bbox_coder=dict(clip_border=True)),\n    roi_head=dict(\n        bbox_head=dict(bbox_coder=dict(clip_border=True), num_classes=1)))\n# data\ndata_root = 'data/MOT20/'\ntrain_dataloader = dict(\n    dataset=dict(\n        data_root=data_root, ann_file='annotations/train_cocoformat.json'))\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root, ann_file='annotations/train_cocoformat.json'))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(ann_file=data_root + 'annotations/train_cocoformat.json')\ntest_evaluator = val_evaluator\n\n# training schedule for 8e\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=8, val_interval=1)\n\n# learning rate\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.01, by_epoch=False, begin=0, end=100),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=8,\n        by_epoch=True,\n        milestones=[6],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/sort/metafile.yml",
    "content": "Collections:\n  - Name: SORT\n    Metadata:\n      Training Techniques:\n        - SGD with Momentum\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n        - FPN\n    Paper:\n      URL: https://arxiv.org/abs/1602.00763\n      Title: Simple Online and Realtime Tracking\n    README: configs/sort/README.md\n\nModels:\n  - Name: sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval\n    In Collection: SORT\n    Config: configs/mot/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n    Metadata:\n      Training Data: MOT17-half-train\n      inference time (ms/im):\n        - value: 53.8\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (640, 1088)\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT17-half-val\n        Metrics:\n          MOTA: 62.0\n          IDF1: 57.8\n          HOTA: 52.0\n    Weights: https://download.openmmlab.com/mmtracking/mot/faster_rcnn/faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth\n"
  },
  {
    "path": "configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/mot_challenge.py', '../_base_/default_runtime.py'\n]\n\ndefault_hooks = dict(\n    logger=dict(type='LoggerHook', interval=1),\n    visualization=dict(type='TrackVisualizationHook', draw=False))\n\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='TrackLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\n# custom hooks\ncustom_hooks = [\n    # Synchronize model buffers such as running_mean and running_var in BN\n    # at the end of each epoch\n    dict(type='SyncBuffersHook')\n]\n\ndetector = _base_.model\ndetector.pop('data_preprocessor')\ndetector.rpn_head.bbox_coder.update(dict(clip_border=False))\ndetector.roi_head.bbox_head.update(dict(num_classes=1))\ndetector.roi_head.bbox_head.bbox_coder.update(dict(clip_border=False))\ndetector['init_cfg'] = dict(\n    type='Pretrained',\n    checkpoint=  # noqa: E251\n    'https://download.openmmlab.com/mmtracking/mot/'\n    'faster_rcnn/faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth')  # noqa: E501\ndel _base_.model\n\nmodel = dict(\n    type='DeepSORT',\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        rgb_to_bgr=False,\n        pad_size_divisor=32),\n    detector=detector,\n    tracker=dict(\n        type='SORTTracker',\n        motion=dict(type='KalmanFilter', center_only=False),\n        obj_score_thr=0.5,\n        match_iou_thr=0.5,\n        reid=None))\n\ntrain_dataloader = None\n\ntrain_cfg = None\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n"
  },
  {
    "path": "configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17train_test-mot17test.py",
    "content": "_base_ = [\n    './sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain'\n    '_test-mot17halfval.py'\n]\n\n# dataloader\nval_dataloader = dict(\n    dataset=dict(ann_file='annotations/train_cocoformat.json'))\ntest_dataloader = dict(\n    dataset=dict(\n        ann_file='annotations/test_cocoformat.json',\n        data_prefix=dict(img_path='test')))\n\n# evaluator\ntest_evaluator = dict(format_only=True, outfile_prefix='./mot_17_test_res')\n"
  },
  {
    "path": "configs/sparse_rcnn/README.md",
    "content": "# Sparse R-CNN\n\n> [Sparse R-CNN: End-to-End Object Detection with Learnable Proposals](https://arxiv.org/abs/2011.12450)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present Sparse R-CNN, a purely sparse method for object detection in images. Existing works on object detection heavily rely on dense object candidates, such as k anchor boxes pre-defined on all grids of image feature map of size H×W. In our method, however, a fixed sparse set of learned object proposals, total length of N, are provided to object recognition head to perform classification and location. By eliminating HWk (up to hundreds of thousands) hand-designed object candidates to N (e.g. 100) learnable proposals, Sparse R-CNN completely avoids all efforts related to object candidates design and many-to-one label assignment. More importantly, final predictions are directly output without non-maximum suppression post-procedure. Sparse R-CNN demonstrates accuracy, run-time and training convergence performance on par with the well-established detector baselines on the challenging COCO dataset, e.g., achieving 45.0 AP in standard 3× training schedule and running at 22 fps using ResNet-50 FPN model. We hope our work could inspire re-thinking the convention of dense prior in object detectors.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143998489-8a5a687d-ceec-4590-8347-708e427e7dfe.png\" height=\"300\"/>\n</div>\n\n## Results and Models\n\n|    Model     | Backbone  |  Style  | Lr schd | Number of Proposals | Multi-Scale | RandomCrop | box AP |                                  Config                                   |                                                                                                                                                                                                                                 Download                                                                                                                                                                                                                                  |\n| :----------: | :-------: | :-----: | :-----: | :-----------------: | :---------: | :--------: | :----: | :-----------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Sparse R-CNN | R-50-FPN  | pytorch |   1x    |         100         |    False    |   False    |  37.9  |                [config](./sparse-rcnn_r50_fpn_1x_coco.py)                 |                                                                         [model](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_1x_coco/sparse_rcnn_r50_fpn_1x_coco_20201222_214453-dc79b137.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_1x_coco/sparse_rcnn_r50_fpn_1x_coco_20201222_214453-dc79b137.log.json)                                                                         |\n| Sparse R-CNN | R-50-FPN  | pytorch |   3x    |         100         |    True     |   False    |  42.8  |           [config](./sparse-rcnn_r50_fpn_ms-480-800-3x_coco.py)           |                                         [model](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_mstrain_480-800_3x_coco/sparse_rcnn_r50_fpn_mstrain_480-800_3x_coco_20201218_154234-7bc5c054.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_mstrain_480-800_3x_coco/sparse_rcnn_r50_fpn_mstrain_480-800_3x_coco_20201218_154234-7bc5c054.log.json)                                         |\n| Sparse R-CNN | R-50-FPN  | pytorch |   3x    |         300         |    True     |    True    |  45.0  | [config](./sparse-rcnn_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py)  |   [model](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco/sparse_rcnn_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20201223_024605-9fe92701.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco/sparse_rcnn_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20201223_024605-9fe92701.log.json)   |\n| Sparse R-CNN | R-101-FPN | pytorch |   3x    |         100         |    True     |   False    |  44.2  |          [config](./sparse-rcnn_r101_fpn_ms-480-800-3x_coco.py)           |                                       [model](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r101_fpn_mstrain_480-800_3x_coco/sparse_rcnn_r101_fpn_mstrain_480-800_3x_coco_20201223_121552-6c46c9d6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r101_fpn_mstrain_480-800_3x_coco/sparse_rcnn_r101_fpn_mstrain_480-800_3x_coco_20201223_121552-6c46c9d6.log.json)                                       |\n| Sparse R-CNN | R-101-FPN | pytorch |   3x    |         300         |    True     |    True    |  46.2  | [config](./sparse-rcnn_r101_fpn_300-proposals_crop-ms-480-800-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco/sparse_rcnn_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20201223_023452-c23c3564.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco/sparse_rcnn_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20201223_023452-c23c3564.log.json) |\n\n### Notes\n\nWe observe about 0.3 AP noise especially when using ResNet-101 as the backbone.\n\n## Citation\n\n```latex\n@article{peize2020sparse,\n  title   =  {{SparseR-CNN}: End-to-End Object Detection with Learnable Proposals},\n  author  =  {Peize Sun and Rufeng Zhang and Yi Jiang and Tao Kong and Chenfeng Xu and Wei Zhan and Masayoshi Tomizuka and Lei Li and Zehuan Yuan and Changhu Wang and Ping Luo},\n  journal =  {arXiv preprint arXiv:2011.12450},\n  year    =  {2020}\n}\n```\n"
  },
  {
    "path": "configs/sparse_rcnn/metafile.yml",
    "content": "Collections:\n  - Name: Sparse R-CNN\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n        - Sparse R-CNN\n    Paper:\n      URL: https://arxiv.org/abs/2011.12450\n      Title: 'Sparse R-CNN: End-to-End Object Detection with Learnable Proposals'\n    README: configs/sparse_rcnn/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.9.0/mmdet/models/detectors/sparse_rcnn.py#L6\n      Version: v2.9.0\n\nModels:\n  - Name: sparse-rcnn_r50_fpn_1x_coco\n    In Collection: Sparse R-CNN\n    Config: configs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_1x_coco/sparse_rcnn_r50_fpn_1x_coco_20201222_214453-dc79b137.pth\n\n  - Name: sparse-rcnn_r50_fpn_ms-480-800-3x_coco\n    In Collection: Sparse R-CNN\n    Config: configs/sparse_rcnn/sparse-rcnn_r50_fpn_ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_mstrain_480-800_3x_coco/sparse_rcnn_r50_fpn_mstrain_480-800_3x_coco_20201218_154234-7bc5c054.pth\n\n  - Name: sparse-rcnn_r50_fpn_300-proposals_crop-ms-480-800-3x_coco\n    In Collection: Sparse R-CNN\n    Config: configs/sparse_rcnn/sparse-rcnn_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 45.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco/sparse_rcnn_r50_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20201223_024605-9fe92701.pth\n\n  - Name: sparse-rcnn_r101_fpn_ms-480-800-3x_coco\n    In Collection: Sparse R-CNN\n    Config: configs/sparse_rcnn/sparse-rcnn_r101_fpn_ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r101_fpn_mstrain_480-800_3x_coco/sparse_rcnn_r101_fpn_mstrain_480-800_3x_coco_20201223_121552-6c46c9d6.pth\n\n  - Name: sparse-rcnn_r101_fpn_300-proposals_crop-ms-480-800-3x_coco\n    In Collection: Sparse R-CNN\n    Config: configs/sparse_rcnn/sparse-rcnn_r101_fpn_300-proposals_crop-ms-480-800-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/sparse_rcnn/sparse_rcnn_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco/sparse_rcnn_r101_fpn_300_proposals_crop_mstrain_480-800_3x_coco_20201223_023452-c23c3564.pth\n"
  },
  {
    "path": "configs/sparse_rcnn/sparse-rcnn_r101_fpn_300-proposals_crop-ms-480-800-3x_coco.py",
    "content": "_base_ = './sparse-rcnn_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/sparse_rcnn/sparse-rcnn_r101_fpn_ms-480-800-3x_coco.py",
    "content": "_base_ = './sparse-rcnn_r50_fpn_ms-480-800-3x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nnum_stages = 6\nnum_proposals = 100\nmodel = dict(\n    type='SparseRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=0,\n        add_extra_convs='on_input',\n        num_outs=4),\n    rpn_head=dict(\n        type='EmbeddingRPNHead',\n        num_proposals=num_proposals,\n        proposal_feature_channel=256),\n    roi_head=dict(\n        type='SparseRoIHead',\n        num_stages=num_stages,\n        stage_loss_weights=[1] * num_stages,\n        proposal_feature_channel=256,\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type='DIIHead',\n                num_classes=80,\n                num_ffn_fcs=2,\n                num_heads=8,\n                num_cls_fcs=1,\n                num_reg_fcs=3,\n                feedforward_channels=2048,\n                in_channels=256,\n                dropout=0.0,\n                ffn_act_cfg=dict(type='ReLU', inplace=True),\n                dynamic_conv_cfg=dict(\n                    type='DynamicConv',\n                    in_channels=256,\n                    feat_channels=64,\n                    out_channels=256,\n                    input_feat_shape=7,\n                    act_cfg=dict(type='ReLU', inplace=True),\n                    norm_cfg=dict(type='LN')),\n                loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n                loss_iou=dict(type='GIoULoss', loss_weight=2.0),\n                loss_cls=dict(\n                    type='FocalLoss',\n                    use_sigmoid=True,\n                    gamma=2.0,\n                    alpha=0.25,\n                    loss_weight=2.0),\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    clip_border=False,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.5, 0.5, 1., 1.])) for _ in range(num_stages)\n        ]),\n    # training and testing settings\n    train_cfg=dict(\n        rpn=None,\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='HungarianAssigner',\n                    match_costs=[\n                        dict(type='FocalLossCost', weight=2.0),\n                        dict(type='BBoxL1Cost', weight=5.0, box_format='xyxy'),\n                        dict(type='IoUCost', iou_mode='giou', weight=2.0)\n                    ]),\n                sampler=dict(type='PseudoSampler'),\n                pos_weight=1) for _ in range(num_stages)\n        ]),\n    test_cfg=dict(rpn=None, rcnn=dict(max_per_img=num_proposals)))\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=0.000025, weight_decay=0.0001),\n    clip_grad=dict(max_norm=1, norm_type=2))\n"
  },
  {
    "path": "configs/sparse_rcnn/sparse-rcnn_r50_fpn_300-proposals_crop-ms-480-800-3x_coco.py",
    "content": "_base_ = './sparse-rcnn_r50_fpn_ms-480-800-3x_coco.py'\nnum_proposals = 300\nmodel = dict(\n    rpn_head=dict(num_proposals=num_proposals),\n    test_cfg=dict(\n        _delete_=True, rpn=None, rcnn=dict(max_per_img=num_proposals)))\n\n# augmentation strategy originates from DETR.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/sparse_rcnn/sparse-rcnn_r50_fpn_ms-480-800-3x_coco.py",
    "content": "_base_ = './sparse-rcnn_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                (736, 1333), (768, 1333), (800, 1333)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\n# learning policy\nmax_epochs = 36\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=max_epochs)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/ssd/README.md",
    "content": "# SSD\n\n> [SSD: Single Shot MultiBox Detector](https://arxiv.org/abs/1512.02325)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present a method for detecting objects in images using a single deep neural network. Our approach, named SSD, discretizes the output space of bounding boxes into a set of default boxes over different aspect ratios and scales per feature map location. At prediction time, the network generates scores for the presence of each object category in each default box and produces adjustments to the box to better match the object shape. Additionally, the network combines predictions from multiple feature maps with different resolutions to naturally handle objects of various sizes. Our SSD model is simple relative to methods that require object proposals because it completely eliminates proposal generation and subsequent pixel or feature resampling stage and encapsulates all computation in a single network. This makes SSD easy to train and straightforward to integrate into systems that require a detection component. Experimental results on the PASCAL VOC, MS COCO, and ILSVRC datasets confirm that SSD has comparable accuracy to methods that utilize an additional object proposal step and is much faster, while providing a unified framework for both training and inference. Compared to other single stage methods, SSD has much better accuracy, even with a smaller input image size. For 300×300 input, SSD achieves 72.1% mAP on VOC2007 test at 58 FPS on a Nvidia Titan X and for 500×500 input, SSD achieves 75.1% mAP, outperforming a comparable state of the art Faster R-CNN model.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143998553-4e12f681-6025-46b4-8410-9e2e1e53a8ec.png\"/>\n</div>\n\n## Results and models of SSD\n\n| Backbone | Size | Style | Lr schd | Mem (GB) | Inf time (fps) | box AP |           Config           |                                                                                                             Download                                                                                                             |\n| :------: | :--: | :---: | :-----: | :------: | :------------: | :----: | :------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  VGG16   | 300  | caffe |  120e   |   9.9    |      43.7      |  25.5  | [config](./ssd300_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ssd/ssd300_coco/ssd300_coco_20210803_015428-d231a06e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ssd/ssd300_coco/ssd300_coco_20210803_015428.log.json) |\n|  VGG16   | 512  | caffe |  120e   |   19.4   |      30.7      |  29.5  | [config](./ssd512_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ssd/ssd512_coco/ssd512_coco_20210803_022849-0a47a1ca.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ssd/ssd512_coco/ssd512_coco_20210803_022849.log.json) |\n\n## Results and models of SSD-Lite\n\n|  Backbone   | Size | Training from scratch | Lr schd | Mem (GB) | Inf time (fps) | box AP |                           Config                           |                                                                                                                                                                 Download                                                                                                                                                                 |\n| :---------: | :--: | :-------------------: | :-----: | :------: | :------------: | :----: | :--------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| MobileNetV2 | 320  |          yes          |  600e   |   4.0    |      69.9      |  21.3  | [config](./ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/ssd/ssdlite_mobilenetv2_scratch_600e_coco/ssdlite_mobilenetv2_scratch_600e_coco_20210629_110627-974d9307.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/ssd/ssdlite_mobilenetv2_scratch_600e_coco/ssdlite_mobilenetv2_scratch_600e_coco_20210629_110627.log.json) |\n\n## Notice\n\n### Compatibility\n\nIn v2.14.0, [PR5291](https://github.com/open-mmlab/mmdetection/pull/5291) refactored SSD neck and head for more\nflexible usage. If users want to use the SSD checkpoint trained in the older versions, we provide a scripts\n`tools/model_converters/upgrade_ssd_version.py` to convert the model weights.\n\n```bash\npython tools/model_converters/upgrade_ssd_version.py ${OLD_MODEL_PATH} ${NEW_MODEL_PATH}\n\n```\n\n- OLD_MODEL_PATH: the path to load the old version SSD model.\n- NEW_MODEL_PATH: the path to save the converted model weights.\n\n### SSD-Lite training settings\n\nThere are some differences between our implementation of MobileNetV2 SSD-Lite and the one in [TensorFlow 1.x detection model zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1_detection_zoo.md) .\n\n1. Use 320x320 as input size instead of 300x300.\n2. The anchor sizes are different.\n3. The C4 feature map is taken from the last layer of stage 4 instead of the middle of the block.\n4. The model in TensorFlow1.x is trained on coco 2014 and validated on coco minival2014, but we trained and validated the model on coco 2017. The mAP on val2017 is usually a little lower than minival2014 (refer to the results in TensorFlow Object Detection API, e.g., MobileNetV2 SSD gets 22 mAP on minival2014 but 20.2 mAP on val2017).\n\n## Citation\n\n```latex\n@article{Liu_2016,\n   title={SSD: Single Shot MultiBox Detector},\n   journal={ECCV},\n   author={Liu, Wei and Anguelov, Dragomir and Erhan, Dumitru and Szegedy, Christian and Reed, Scott and Fu, Cheng-Yang and Berg, Alexander C.},\n   year={2016},\n}\n```\n"
  },
  {
    "path": "configs/ssd/metafile.yml",
    "content": "Collections:\n  - Name: SSD\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - VGG\n    Paper:\n      URL: https://arxiv.org/abs/1512.02325\n      Title: 'SSD: Single Shot MultiBox Detector'\n    README: configs/ssd/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.14.0/mmdet/models/dense_heads/ssd_head.py#L16\n      Version: v2.14.0\n\nModels:\n  - Name: ssd300_coco\n    In Collection: SSD\n    Config: configs/ssd/ssd300_coco.py\n    Metadata:\n      Training Memory (GB): 9.9\n      inference time (ms/im):\n        - value: 22.88\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (300, 300)\n      Epochs: 120\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 25.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ssd/ssd300_coco/ssd300_coco_20210803_015428-d231a06e.pth\n\n  - Name: ssd512_coco\n    In Collection: SSD\n    Config: configs/ssd/ssd512_coco.py\n    Metadata:\n      Training Memory (GB): 19.4\n      inference time (ms/im):\n        - value: 32.57\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (512, 512)\n      Epochs: 120\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 29.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ssd/ssd512_coco/ssd512_coco_20210803_022849-0a47a1ca.pth\n\n  - Name: ssdlite_mobilenetv2-scratch_8xb24-600e_coco\n    In Collection: SSD\n    Config: configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py\n    Metadata:\n      Training Memory (GB): 4.0\n      inference time (ms/im):\n        - value: 14.3\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (320, 320)\n      Epochs: 600\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 21.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/ssd/ssdlite_mobilenetv2_scratch_600e_coco/ssdlite_mobilenetv2_scratch_600e_coco_20210629_110627-974d9307.pth\n"
  },
  {
    "path": "configs/ssd/ssd300_coco.py",
    "content": "_base_ = [\n    '../_base_/models/ssd300.py', '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n\n# dataset settings\ninput_size = 300\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='Expand',\n        mean={{_base_.model.data_preprocessor.mean}},\n        to_rgb={{_base_.model.data_preprocessor.bgr_to_rgb}},\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=2,\n    batch_sampler=None,\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=5,\n        dataset=dict(\n            type={{_base_.dataset_type}},\n            data_root={{_base_.data_root}},\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args={{_base_.backend_args}})))\nval_dataloader = dict(batch_size=8, dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=2e-3, momentum=0.9, weight_decay=5e-4))\n\ncustom_hooks = [\n    dict(type='NumClassCheckHook'),\n    dict(type='CheckInvalidLossHook', interval=50, priority='VERY_LOW')\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/ssd/ssd512_coco.py",
    "content": "_base_ = 'ssd300_coco.py'\n\n# model settings\ninput_size = 512\nmodel = dict(\n    neck=dict(\n        out_channels=(512, 1024, 512, 256, 256, 256, 256),\n        level_strides=(2, 2, 2, 2, 1),\n        level_paddings=(1, 1, 1, 1, 1),\n        last_kernel_size=4),\n    bbox_head=dict(\n        in_channels=(512, 1024, 512, 256, 256, 256, 256),\n        anchor_generator=dict(\n            type='SSDAnchorGenerator',\n            scale_major=False,\n            input_size=input_size,\n            basesize_ratio_range=(0.1, 0.9),\n            strides=[8, 16, 32, 64, 128, 256, 512],\n            ratios=[[2], [2, 3], [2, 3], [2, 3], [2, 3], [2], [2]])))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='Expand',\n        mean={{_base_.model.data_preprocessor.mean}},\n        to_rgb={{_base_.model.data_preprocessor.bgr_to_rgb}},\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(dataset=dict(pipeline=train_pipeline)))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/ssd/ssdlite_mobilenetv2-scratch_8xb24-600e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True,\n    pad_size_divisor=1)\nmodel = dict(\n    type='SingleStageDetector',\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type='MobileNetV2',\n        out_indices=(4, 7),\n        norm_cfg=dict(type='BN', eps=0.001, momentum=0.03),\n        init_cfg=dict(type='TruncNormal', layer='Conv2d', std=0.03)),\n    neck=dict(\n        type='SSDNeck',\n        in_channels=(96, 1280),\n        out_channels=(96, 1280, 512, 256, 256, 128),\n        level_strides=(2, 2, 2, 2),\n        level_paddings=(1, 1, 1, 1),\n        l2_norm_scale=None,\n        use_depthwise=True,\n        norm_cfg=dict(type='BN', eps=0.001, momentum=0.03),\n        act_cfg=dict(type='ReLU6'),\n        init_cfg=dict(type='TruncNormal', layer='Conv2d', std=0.03)),\n    bbox_head=dict(\n        type='SSDHead',\n        in_channels=(96, 1280, 512, 256, 256, 128),\n        num_classes=80,\n        use_depthwise=True,\n        norm_cfg=dict(type='BN', eps=0.001, momentum=0.03),\n        act_cfg=dict(type='ReLU6'),\n        init_cfg=dict(type='Normal', layer='Conv2d', std=0.001),\n\n        # set anchor size manually instead of using the predefined\n        # SSD300 setting.\n        anchor_generator=dict(\n            type='SSDAnchorGenerator',\n            scale_major=False,\n            strides=[16, 32, 64, 107, 160, 320],\n            ratios=[[2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]],\n            min_sizes=[48, 100, 150, 202, 253, 304],\n            max_sizes=[100, 150, 202, 253, 304, 320]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2])),\n    # model training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0.,\n            ignore_iof_thr=-1,\n            gt_max_assign_all=False),\n        sampler=dict(type='PseudoSampler'),\n        smoothl1_beta=1.,\n        allowed_border=-1,\n        pos_weight=-1,\n        neg_pos_ratio=3,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        nms=dict(type='nms', iou_threshold=0.45),\n        min_bbox_size=0,\n        score_thr=0.02,\n        max_per_img=200))\nenv_cfg = dict(cudnn_benchmark=True)\n\n# dataset settings\ninput_size = 320\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='Expand',\n        mean=data_preprocessor['mean'],\n        to_rgb=data_preprocessor['bgr_to_rgb'],\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=24,\n    num_workers=4,\n    batch_sampler=None,\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=5,\n        dataset=dict(\n            type={{_base_.dataset_type}},\n            data_root={{_base_.data_root}},\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline)))\nval_dataloader = dict(batch_size=8, dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# training schedule\nmax_epochs = 120\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=5)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='CosineAnnealingLR',\n        begin=0,\n        T_max=max_epochs,\n        end=max_epochs,\n        by_epoch=True,\n        eta_min=0)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.015, momentum=0.9, weight_decay=4.0e-5))\n\ncustom_hooks = [\n    dict(type='NumClassCheckHook'),\n    dict(type='CheckInvalidLossHook', interval=50, priority='VERY_LOW')\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (24 samples per GPU)\nauto_scale_lr = dict(base_batch_size=192)\n"
  },
  {
    "path": "configs/strong_baselines/README.md",
    "content": "# Strong Baselines\n\n<!-- [OTHERS] -->\n\nWe train Mask R-CNN with large-scale jitter and longer schedule as strong baselines.\nThe modifications follow those in [Detectron2](https://github.com/facebookresearch/detectron2/tree/master/configs/new_baselines).\n\n## Results and Models\n\n| Backbone |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP | mask AP |                                       Config                                       |         Download         |\n| :------: | :-----: | :-----: | :------: | :------------: | :----: | :-----: | :--------------------------------------------------------------------------------: | :----------------------: |\n| R-50-FPN | pytorch |   50e   |          |                |        |         |    [config](./mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-50e_coco.py)     | [model](<>) \\| [log](<>) |\n| R-50-FPN | pytorch |  100e   |          |                |        |         |    [config](./mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py)    | [model](<>) \\| [log](<>) |\n| R-50-FPN |  caffe  |  100e   |          |                |  44.7  |  40.4   | [config](./mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py) | [model](<>) \\| [log](<>) |\n| R-50-FPN |  caffe  |  400e   |          |                |        |         | [config](./mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-400e_coco.py) | [model](<>) \\| [log](<>) |\n\n## Notice\n\nWhen using large-scale jittering, there are sometimes empty proposals in the box and mask heads during training.\nThis requires MMSyncBN that allows empty tensors. Therefore, please use mmcv-full>=1.3.14 to train models supported in this directory.\n"
  },
  {
    "path": "configs/strong_baselines/mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_amp-lsj-100e_coco.py",
    "content": "_base_ = 'mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py'  # noqa\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(type='AmpOptimWrapper')\n"
  },
  {
    "path": "configs/strong_baselines/mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../common/lsj-100e_coco-instance.py'\n]\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\n# Use MMSyncBN that handles empty tensor in head. It can be changed to\n# SyncBN after https://github.com/pytorch/pytorch/issues/36530 is fixed\nhead_norm_cfg = dict(type='MMSyncBN', requires_grad=True)\nmodel = dict(\n    # use caffe norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n\n        # pad_size_divisor=32 is unnecessary in training but necessary\n        # in testing.\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        frozen_stages=-1,\n        norm_eval=False,\n        norm_cfg=norm_cfg,\n        init_cfg=None,\n        style='caffe'),\n    neck=dict(norm_cfg=norm_cfg),\n    rpn_head=dict(num_convs=2),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=head_norm_cfg),\n        mask_head=dict(norm_cfg=head_norm_cfg)))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\n# Use RepeatDataset to speed up training\ntrain_dataloader = dict(dataset=dict(dataset=dict(pipeline=train_pipeline)))\n"
  },
  {
    "path": "configs/strong_baselines/mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-400e_coco.py",
    "content": "_base_ = './mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py'  # noqa\n\n# Use RepeatDataset to speed up training\n# change repeat time from 4 (for 100 epochs) to 16 (for 400 epochs)\ntrain_dataloader = dict(dataset=dict(times=4 * 4))\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.067,\n        by_epoch=False,\n        begin=0,\n        end=500 * 4),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/strong_baselines/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_amp-lsj-100e_coco.py",
    "content": "_base_ = 'mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py'\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(type='AmpOptimWrapper')\n"
  },
  {
    "path": "configs/strong_baselines/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../common/lsj-100e_coco-instance.py'\n]\n\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\nnorm_cfg = dict(type='SyncBN', requires_grad=True)\n# Use MMSyncBN that handles empty tensor in head. It can be changed to\n# SyncBN after https://github.com/pytorch/pytorch/issues/36530 is fixed\nhead_norm_cfg = dict(type='MMSyncBN', requires_grad=True)\nmodel = dict(\n    # the model is trained from scratch, so init_cfg is None\n    data_preprocessor=dict(\n        # pad_size_divisor=32 is unnecessary in training but necessary\n        # in testing.\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        frozen_stages=-1, norm_eval=False, norm_cfg=norm_cfg, init_cfg=None),\n    neck=dict(norm_cfg=norm_cfg),\n    rpn_head=dict(num_convs=2),  # leads to 0.1+ mAP\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=head_norm_cfg),\n        mask_head=dict(norm_cfg=head_norm_cfg)))\n"
  },
  {
    "path": "configs/strong_baselines/mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-50e_coco.py",
    "content": "_base_ = 'mask-rcnn_r50_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py'\n\n# Use RepeatDataset to speed up training\n# change repeat time from 4 (for 100 epochs) to 2 (for 50 epochs)\ntrain_dataloader = dict(dataset=dict(times=2))\n"
  },
  {
    "path": "configs/strong_baselines/metafile.yml",
    "content": "Models:\n  - Name: mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco\n    In Collection: Mask R-CNN\n    Config: configs/strong_baselines/mask-rcnn_r50-caffe_fpn_rpn-2conv_4conv1fc_syncbn-all_lsj-100e_coco.py\n    Metadata:\n      Epochs: 100\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n        - LSJ\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n        - FPN\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          box AP: 40.4\n"
  },
  {
    "path": "configs/strongsort/README.md",
    "content": "# StrongSORT: Make DeepSORT Great Again\n\n## Abstract\n\n<!-- [ABSTRACT] -->\n\nExisting Multi-Object Tracking (MOT) methods can be roughly classified as tracking-by-detection and joint-detection-association paradigms. Although the latter has elicited more attention and demonstrates comparable performance relative to the former, we claim that the tracking-by-detection paradigm is still the optimal solution in terms of tracking accuracy. In this paper, we revisit the classic tracker DeepSORT and upgrade it from various aspects, i.e., detection, embedding and association. The resulting tracker, called StrongSORT, sets new HOTA and IDF1 records on MOT17 and MOT20. We also present two lightweight and plug-and-play algorithms to further refine the tracking results. Firstly, an appearance-free link model (AFLink) is proposed to associate short tracklets into complete trajectories. To the best of our knowledge, this is the first global link model without appearance information. Secondly, we propose Gaussian-smoothed interpolation (GSI) to compensate for missing detections. Instead of ignoring motion information like linear interpolation, GSI is based on the Gaussian process regression algorithm and can achieve more accurate localizations. Moreover, AFLink and GSI can be plugged into various trackers with a negligible extra computational cost (591.9 and 140.9 Hz, respectively, on MOT17). By integrating StrongSORT with the two algorithms, the final tracker StrongSORT++ ranks first on MOT17 and MOT20 in terms of HOTA and IDF1 metrics and surpasses the second-place one by 1.3 - 2.2. Code will be released soon.\n\n<!-- [IMAGE] -->\n\n<div align=\"center\">\n  <img src=\"https://user-images.githubusercontent.com/99722489/185282811-ec82bdf6-8889-4f01-9c4d-a8e104f775b7.png\"/>\n</div>\n\n## Citation\n\n<!-- [ALGORITHM] -->\n\n```latex\n@article{du2022strongsort,\n  title={Strongsort: Make deepsort great again},\n  author={Du, Yunhao and Song, Yang and Yang, Bo and Zhao, Yanyun},\n  journal={arXiv preprint arXiv:2202.13514},\n  year={2022}\n}\n```\n\n## Results and models on MOT17\n\n|    Method    | Detector | ReID |           Train Set           |    Test Set    | Public | Inf time (fps) | HOTA | MOTA | IDF1 |  FP   |  FN   | IDSw. |                                        Config                                        |                                                                                                                                                                                   Download                                                                                                                                                                                    |\n| :----------: | :------: | :--: | :---------------------------: | :------------: | :----: | :------------: | :--: | :--: | :--: | :---: | :---: | :---: | :----------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| StrongSORT++ | YOLOX-X  | R50  | CrowdHuman + MOT17-half-train | MOT17-half-val |   N    |       -        | 70.9 | 78.4 | 83.3 | 15237 | 19035 |  582  | [config](strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py) | [detector](https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/yolox_x_crowdhuman_mot17-private-half_20220812_192036-b6c9ce9a.pth) [reid](https://download.openmmlab.com/mmtracking/mot/reid/reid_r50_6e_mot17-4bf6b63d.pth) [AFLink](https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/aflink_motchallenge_20220812_190310-a7578ad3.pth) |\n\n## Results and models on MOT20\n\n|    Method    | Detector | ReID |        Train Set         |  Test Set  | Public | Inf time (fps) | HOTA | MOTA | IDF1 |  FP   |  FN   | IDSw. |                                    Config                                     |                                                                                                                                                                                         Download                                                                                                                                                                                         |\n| :----------: | :------: | :--: | :----------------------: | :--------: | :----: | :------------: | :--: | :--: | :--: | :---: | :---: | :---: | :---------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| StrongSORT++ | YOLOX-X  | R50  | CrowdHuman + MOT20-train | MOT20-test |   N    |       -        | 62.9 | 75.5 | 77.3 | 29043 | 96155 | 1640  | [config](strongsort_yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test.py) | [detector](https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/yolox_x_crowdhuman_mot20-private_20220812_192123-77c014de.pth) [reid](https://download.openmmlab.com/mmtracking/mot/reid/reid_r50_6e_mot20_20210803_212426-c83b1c01.pth) [AFLink](https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/aflink_motchallenge_20220812_190310-a7578ad3.pth) |\n\n## Get started\n\n### 1. Development Environment Setup\n\nTracking Development Environment Setup can refer to this [document](../../docs/en/get_started.md).\n\n### 2. Dataset Prepare\n\nTracking Dataset Prepare can refer to this [document](../../docs/en/user_guides/tracking_dataset_prepare.md).\n\n### 3. Training\n\nWe implement StrongSORT with independent detector and ReID models.\nNote that, due to the influence of parameters such as learning rate in default configuration file,\nwe recommend using 8 GPUs for training in order to reproduce accuracy.\n\nYou can train the detector as follows.\n\n```shell script\n# Training YOLOX-X on crowdhuman and mot17-half-train dataset with following command.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_train.sh configs/det/yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py 8\n```\n\nAnd you can train the ReID model as follows.\n\n```shell script\n# Training ReID model on mot17-train80 dataset with following command.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_train.sh configs/reid/reid_r50_8xb32-6e_mot17train80_test-mot17val20.py 8\n```\n\nIf you want to know about more detailed usage of `train.py/dist_train.sh/slurm_train.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 4. Testing and evaluation\n\n**2.1 Example on MOTxx-halfval dataset**\n\n```shell script\n# Example 1: Test on motXX-half-val set.\n# The number after config file represents the number of GPUs used. Here we use 8 GPUs.\nbash tools/dist_test_tracking.sh configs/strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py 8 --detector ${CHECKPOINT_PATH} --reid ${CHECKPOINT_PATH}\n```\n\n**2.2 Example on MOTxx-test dataset**\n\nIf you want to get the results of the [MOT Challenge](https://motchallenge.net/) test set,\nplease use the following command to generate result files that can be used for submission.\nIt will be stored in `./mot_20_test_res`, you can modify the saved path in `test_evaluator` of the config.\n\n```shell script\n# Example 2: Test on motxx-test set\n# The number after config file represents the number of GPUs used\nbash tools/dist_test_tracking.sh configs/strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test.py 8 --detector ${CHECKPOINT_PATH} --reid ${CHECKPOINT_PATH}\n```\n\nIf you want to know about more detailed usage of `test_tracking.py/dist_test_tracking.sh/slurm_test_tracking.sh`,\nplease refer to this [document](../../docs/en/user_guides/tracking_train_test.md).\n\n### 3.Inference\n\nUse a single GPU to predict a video and save it as a video.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 configs/strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py --detector ${CHECKPOINT_FILE} --reid ${CHECKPOINT_PATH} --out mot.mp4\n```\n\nIf you want to know about more detailed usage of `mot_demo.py`, please refer to this [document](../../docs/en/user_guides/tracking_inference.md).\n"
  },
  {
    "path": "configs/strongsort/metafile.yml",
    "content": "Collections:\n  - Name: StrongSORT++\n    Metadata:\n      Training Techniques:\n        - SGD with Momentum\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n        - YOLOX\n    Paper:\n      URL: https://arxiv.org/abs/2202.13514\n      Title: \"StrongSORT: Make DeepSORT Great Again\"\n    README: configs/strongsort/README.md\n\nModels:\n  - Name: strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval\n    In Collection: StrongSORT++\n    Config: configs/strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py\n    Metadata:\n      Training Data: CrowdHuman + MOT17-half-train\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT17-half-val\n        Metrics:\n          MOTA: 78.3\n          IDF1: 83.2\n          HOTA: 70.9\n    Weights:\n      - https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/yolox_x_crowdhuman_mot17-private-half_20220812_192036-b6c9ce9a.pth\n      - https://download.openmmlab.com/mmtracking/mot/reid/reid_r50_6e_mot17-4bf6b63d.pth\n      - https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/aflink_motchallenge_20220812_190310-a7578ad3.pth\n\n  - Name: strongsort_yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test\n    In Collection: StrongSORT++\n    Config: configs/strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test.py\n    Metadata:\n      Training Data: CrowdHuman + MOT20-train\n    Results:\n      - Task: Multiple Object Tracking\n        Dataset: MOT20-test\n        Metrics:\n          MOTA: 75.5\n          IDF1: 77.3\n          HOTA: 62.9\n    Weights:\n      - https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/yolox_x_crowdhuman_mot20-private_20220812_192123-77c014de.pth\n      - https://download.openmmlab.com/mmtracking/mot/reid/reid_r50_6e_mot20_20210803_212426-c83b1c01.pth\n      - https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/aflink_motchallenge_20220812_190310-a7578ad3.pth\n"
  },
  {
    "path": "configs/strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = [\n    './yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py',  # noqa: E501\n]\n\ndataset_type = 'MOTChallengeDataset'\ndetector = _base_.model\ndetector.pop('data_preprocessor')\ndel _base_.model\n\nmodel = dict(\n    type='StrongSORT',\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        pad_size_divisor=32,\n        batch_augments=[\n            dict(\n                type='BatchSyncRandomResize',\n                random_size_range=(576, 1024),\n                size_divisor=32,\n                interval=10)\n        ]),\n    detector=detector,\n    reid=dict(\n        type='BaseReID',\n        data_preprocessor=dict(type='mmpretrain.ClsDataPreprocessor'),\n        backbone=dict(\n            type='mmpretrain.ResNet',\n            depth=50,\n            num_stages=4,\n            out_indices=(3, ),\n            style='pytorch'),\n        neck=dict(type='GlobalAveragePooling', kernel_size=(8, 4), stride=1),\n        head=dict(\n            type='LinearReIDHead',\n            num_fcs=1,\n            in_channels=2048,\n            fc_channels=1024,\n            out_channels=128,\n            num_classes=380,\n            loss_cls=dict(type='mmpretrain.CrossEntropyLoss', loss_weight=1.0),\n            loss_triplet=dict(type='TripletLoss', margin=0.3, loss_weight=1.0),\n            norm_cfg=dict(type='BN1d'),\n            act_cfg=dict(type='ReLU'))),\n    cmc=dict(\n        type='CameraMotionCompensation',\n        warp_mode='cv2.MOTION_EUCLIDEAN',\n        num_iters=100,\n        stop_eps=0.00001),\n    tracker=dict(\n        type='StrongSORTTracker',\n        motion=dict(type='KalmanFilter', center_only=False, use_nsa=True),\n        obj_score_thr=0.6,\n        reid=dict(\n            num_samples=None,\n            img_scale=(256, 128),\n            img_norm_cfg=dict(\n                mean=[123.675, 116.28, 103.53],\n                std=[58.395, 57.12, 57.375],\n                to_rgb=True),\n            match_score_thr=0.3,\n            motion_weight=0.02,\n        ),\n        match_iou_thr=0.7,\n        momentums=dict(embeds=0.1, ),\n        num_tentatives=2,\n        num_frames_retain=100),\n    postprocess_model=dict(\n        type='AppearanceFreeLink',\n        checkpoint=  # noqa: E251\n        'https://download.openmmlab.com/mmtracking/mot/strongsort/mot_dataset/aflink_motchallenge_20220812_190310-a7578ad3.pth',  # noqa: E501\n        temporal_threshold=(0, 30),\n        spatial_threshold=50,\n        confidence_threshold=0.95,\n    ))\n\ntrain_pipeline = None\ntest_pipeline = [\n    dict(\n        type='TransformBroadcaster',\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n            dict(type='Resize', scale=_base_.img_scale, keep_ratio=True),\n            dict(\n                type='Pad',\n                size_divisor=32,\n                pad_val=dict(img=(114.0, 114.0, 114.0))),\n            dict(type='LoadTrackAnnotations'),\n        ]),\n    dict(type='PackTrackInputs')\n]\n\ntrain_dataloader = None\nval_dataloader = dict(\n    # Now StrongSORT only support video_based sampling\n    sampler=dict(type='DefaultSampler', shuffle=False, round_up=False),\n    dataset=dict(\n        _delete_=True,\n        type=dataset_type,\n        data_root=_base_.data_root,\n        ann_file='annotations/half-val_cocoformat.json',\n        data_prefix=dict(img_path='train'),\n        # when you evaluate track performance, you need to remove metainfo\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\ntrain_cfg = None\noptim_wrapper = None\n\n# evaluator\nval_evaluator = dict(\n    _delete_=True,\n    type='MOTChallengeMetric',\n    metric=['HOTA', 'CLEAR', 'Identity'],\n    # use_postprocess to support AppearanceFreeLink in val_evaluator\n    use_postprocess=True,\n    postprocess_tracklet_cfg=[\n        dict(\n            type='InterpolateTracklets',\n            min_num_frames=5,\n            max_num_frames=20,\n            use_gsi=True,\n            smooth_tau=10)\n    ])\ntest_evaluator = val_evaluator\n\ndefault_hooks = dict(logger=dict(type='LoggerHook', interval=1))\n\ndel _base_.param_scheduler\ndel _base_.custom_hooks\n"
  },
  {
    "path": "configs/strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test.py",
    "content": "_base_ = [\n    './strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain'\n    '_test-mot17halfval.py'\n]\n\nimg_scale = (1600, 896)  # width, height\n\nmodel = dict(\n    data_preprocessor=dict(\n        type='TrackDataPreprocessor',\n        pad_size_divisor=32,\n        batch_augments=[\n            dict(type='BatchSyncRandomResize', random_size_range=(640, 1152))\n        ]))\n\ntest_pipeline = [\n    dict(\n        type='TransformBroadcaster',\n        transforms=[\n            dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n            dict(type='Resize', scale=img_scale, keep_ratio=True),\n            dict(\n                type='Pad',\n                size_divisor=32,\n                pad_val=dict(img=(114.0, 114.0, 114.0))),\n            dict(type='LoadTrackAnnotations'),\n        ]),\n    dict(type='PackTrackInputs')\n]\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root='data/MOT17',\n        ann_file='annotations/train_cocoformat.json',\n        data_prefix=dict(img_path='train'),\n        pipeline=test_pipeline))\ntest_dataloader = dict(\n    dataset=dict(\n        data_root='data/MOT20',\n        ann_file='annotations/test_cocoformat.json',\n        data_prefix=dict(img_path='test'),\n        pipeline=test_pipeline))\n\ntest_evaluator = dict(format_only=True, outfile_prefix='./mot_20_test_res')\n"
  },
  {
    "path": "configs/strongsort/yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py",
    "content": "_base_ = ['../yolox/yolox_x_8xb8-300e_coco.py']\n\ndata_root = 'data/MOT17/'\n\nimg_scale = (1440, 800)  # width, height\nbatch_size = 4\n\n# model settings\nmodel = dict(\n    bbox_head=dict(num_classes=1),\n    test_cfg=dict(nms=dict(iou_threshold=0.7)),\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint=  # noqa: E251\n        'https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_x_8x8_300e_coco/yolox_x_8x8_300e_coco_20211126_140254-1ef88d67.pth'  # noqa: E501\n    ))\n\ntrain_pipeline = [\n    dict(\n        type='Mosaic',\n        img_scale=img_scale,\n        pad_val=114.0,\n        bbox_clip_border=False),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        border=(-img_scale[0] // 2, -img_scale[1] // 2),\n        bbox_clip_border=False),\n    dict(\n        type='MixUp',\n        img_scale=img_scale,\n        ratio_range=(0.8, 1.6),\n        pad_val=114.0,\n        bbox_clip_border=False),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='Resize',\n        scale=img_scale,\n        keep_ratio=True,\n        clip_object_border=False),\n    dict(type='Pad', size_divisor=32, pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1), keep_empty=False),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='Resize', scale=img_scale, keep_ratio=True),\n    dict(type='Pad', size_divisor=32, pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=batch_size,\n    num_workers=4,\n    persistent_workers=True,\n    pin_memory=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        type='MultiImageMixDataset',\n        dataset=dict(\n            type='ConcatDataset',\n            datasets=[\n                dict(\n                    type='CocoDataset',\n                    data_root=data_root,\n                    ann_file='annotations/half-train_cocoformat.json',\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_train.json',\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_val.json',\n                    data_prefix=dict(img='val'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n            ]),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    dataset=dict(\n        data_root=data_root,\n        ann_file='annotations/half-val_cocoformat.json',\n        data_prefix=dict(img='train'),\n        metainfo=dict(classes=('pedestrian', )),\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# training settings\nmax_epochs = 80\nnum_last_epochs = 10\ninterval = 5\n\ntrain_cfg = dict(max_epochs=max_epochs, val_begin=75, val_interval=1)\n\n# optimizer\n# default 8 gpu\nbase_lr = 0.001 / 8 * batch_size\noptim_wrapper = dict(optimizer=dict(lr=base_lr))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='QuadraticWarmupLR',\n        by_epoch=True,\n        begin=0,\n        end=1,\n        convert_to_iter_based=True),\n    dict(\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=1,\n        T_max=max_epochs - num_last_epochs,\n        end=max_epochs - num_last_epochs,\n        by_epoch=True,\n        convert_to_iter_based=True),\n    dict(\n        type='ConstantLR',\n        by_epoch=True,\n        factor=1,\n        begin=max_epochs - num_last_epochs,\n        end=max_epochs,\n    )\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        interval=1,\n        max_keep_ckpts=5  # only keep latest 5 checkpoints\n    ))\n\ncustom_hooks = [\n    dict(\n        type='YOLOXModeSwitchHook',\n        num_last_epochs=num_last_epochs,\n        priority=48),\n    dict(type='SyncNormHook', priority=48),\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0001,\n        update_buffers=True,\n        priority=49)\n]\n\n# evaluator\nval_evaluator = dict(\n    ann_file=data_root + 'annotations/half-val_cocoformat.json',\n    format_only=False)\ntest_evaluator = val_evaluator\n\ndel _base_.tta_model\ndel _base_.tta_pipeline\ndel _base_.train_dataset\n"
  },
  {
    "path": "configs/strongsort/yolox_x_8xb4-80e_crowdhuman-mot20train_test-mot20test.py",
    "content": "_base_ = ['./yolox_x_8xb4-80e_crowdhuman-mot17halftrain_test-mot17halfval.py']\n\ndata_root = 'data/MOT20/'\n\nimg_scale = (1600, 896)  # width, height\n\n# model settings\nmodel = dict(\n    data_preprocessor=dict(batch_augments=[\n        dict(type='BatchSyncRandomResize', random_size_range=(640, 1152))\n    ]))\n\ntrain_pipeline = [\n    dict(\n        type='Mosaic',\n        img_scale=img_scale,\n        pad_val=114.0,\n        bbox_clip_border=True),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        border=(-img_scale[0] // 2, -img_scale[1] // 2),\n        bbox_clip_border=True),\n    dict(\n        type='MixUp',\n        img_scale=img_scale,\n        ratio_range=(0.8, 1.6),\n        pad_val=114.0,\n        bbox_clip_border=True),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='Resize',\n        scale=img_scale,\n        keep_ratio=True,\n        clip_object_border=True),\n    dict(type='Pad', size_divisor=32, pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1), keep_empty=False),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='Resize', scale=img_scale, keep_ratio=True),\n    dict(type='Pad', size_divisor=32, pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        type='MultiImageMixDataset',\n        dataset=dict(\n            type='ConcatDataset',\n            datasets=[\n                dict(\n                    type='CocoDataset',\n                    data_root=data_root,\n                    ann_file='annotations/train_cocoformat.json',\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_train.json',\n                    data_prefix=dict(img='train'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n                dict(\n                    type='CocoDataset',\n                    data_root='data/crowdhuman',\n                    ann_file='annotations/crowdhuman_val.json',\n                    data_prefix=dict(img='val'),\n                    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                    metainfo=dict(classes=('pedestrian', )),\n                    pipeline=[\n                        dict(\n                            type='LoadImageFromFile',\n                            backend_args=_base_.backend_args),\n                        dict(type='LoadAnnotations', with_bbox=True),\n                    ]),\n            ]),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    dataset=dict(\n        data_root='data/MOT17', ann_file='annotations/train_cocoformat.json'))\ntest_dataloader = val_dataloader\n\n# evaluator\nval_evaluator = dict(ann_file='data/MOT17/annotations/train_cocoformat.json')\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "configs/swin/README.md",
    "content": "# Swin\n\n> [Swin Transformer: Hierarchical Vision Transformer using Shifted Windows](https://arxiv.org/abs/2103.14030)\n\n<!-- [BACKBONE] -->\n\n## Abstract\n\nThis paper presents a new vision Transformer, called Swin Transformer, that capably serves as a general-purpose backbone for computer vision. Challenges in adapting Transformer from language to vision arise from differences between the two domains, such as large variations in the scale of visual entities and the high resolution of pixels in images compared to words in text. To address these differences, we propose a hierarchical Transformer whose representation is computed with Shifted windows. The shifted windowing scheme brings greater efficiency by limiting self-attention computation to non-overlapping local windows while also allowing for cross-window connection. This hierarchical architecture has the flexibility to model at various scales and has linear computational complexity with respect to image size. These qualities of Swin Transformer make it compatible with a broad range of vision tasks, including image classification (87.3 top-1 accuracy on ImageNet-1K) and dense prediction tasks such as object detection (58.7 box AP and 51.1 mask AP on COCO test-dev) and semantic segmentation (53.5 mIoU on ADE20K val). Its performance surpasses the previous state-of-the-art by a large margin of +2.7 box AP and +2.6 mask AP on COCO, and +3.2 mIoU on ADE20K, demonstrating the potential of Transformer-based models as vision backbones. The hierarchical design and the shifted window approach also prove beneficial for all-MLP architectures.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143999551-6a527048-de38-485c-a1b6-3133ffa5bfaa.png\"/>\n</div>\n\n## Results and Models\n\n### Mask R-CNN\n\n| Backbone |  Pretrain   | Lr schd | Multi-scale crop | FP16 | Mem (GB) | Inf time (fps) | box AP | mask AP |                            Config                             |                                                                                                                                                                                      Download                                                                                                                                                                                       |\n| :------: | :---------: | :-----: | :--------------: | :--: | :------: | :------------: | :----: | :-----: | :-----------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  Swin-T  | ImageNet-1K |   1x    |        no        |  no  |   7.6    |                |  42.7  |  39.3   |       [config](./mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py)       |                           [model](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937.log.json)                           |\n|  Swin-T  | ImageNet-1K |   3x    |       yes        |  no  |   10.2   |                |  46.0  |  41.6   |   [config](./mask-rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco.py)   |           [model](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco/mask_rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco_20210906_131725-bacf6f7b.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco/mask_rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco_20210906_131725.log.json)           |\n|  Swin-T  | ImageNet-1K |   3x    |       yes        | yes  |   7.8    |                |  46.0  |  41.7   | [config](./mask-rcnn_swin-t-p4-w7_fpn_amp-ms-crop-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_fp16_ms-crop-3x_coco/mask_rcnn_swin-t-p4-w7_fpn_fp16_ms-crop-3x_coco_20210908_165006-90a4008c.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_fp16_ms-crop-3x_coco/mask_rcnn_swin-t-p4-w7_fpn_fp16_ms-crop-3x_coco_20210908_165006.log.json) |\n|  Swin-S  | ImageNet-1K |   3x    |       yes        | yes  |   11.9   |                |  48.2  |  43.2   | [config](./mask-rcnn_swin-s-p4-w7_fpn_amp-ms-crop-3x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco_20210903_104808-b92c91f1.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco_20210903_104808.log.json) |\n\n### Notice\n\nPlease follow the example\nof `retinanet_swin-t-p4-w7_fpn_1x_coco.py` when you want to combine Swin Transformer with\nthe one-stage detector. Because there is a layer norm at the outs of Swin Transformer, you must set `start_level` as 0 in FPN, so we have to set the `out_indices` of backbone as `[1,2,3]`.\n\n## Citation\n\n```latex\n@article{liu2021Swin,\n    title={Swin Transformer: Hierarchical Vision Transformer using Shifted Windows},\n    author={Liu, Ze and Lin, Yutong and Cao, Yue and Hu, Han and Wei, Yixuan and Zhang, Zheng and Lin, Stephen and Guo, Baining},\n    journal={arXiv preprint arXiv:2103.14030},\n    year={2021}\n}\n```\n"
  },
  {
    "path": "configs/swin/mask-rcnn_swin-s-p4-w7_fpn_amp-ms-crop-3x_coco.py",
    "content": "_base_ = './mask-rcnn_swin-t-p4-w7_fpn_amp-ms-crop-3x_coco.py'\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_small_patch4_window7_224.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        depths=[2, 2, 18, 2],\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)))\n"
  },
  {
    "path": "configs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth'  # noqa\nmodel = dict(\n    type='MaskRCNN',\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[96, 192, 384, 768]))\n\nmax_epochs = 12\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'relative_position_bias_table': dict(decay_mult=0.),\n            'norm': dict(decay_mult=0.)\n        }),\n    optimizer=dict(\n        _delete_=True,\n        type='AdamW',\n        lr=0.0001,\n        betas=(0.9, 0.999),\n        weight_decay=0.05))\n"
  },
  {
    "path": "configs/swin/mask-rcnn_swin-t-p4-w7_fpn_amp-ms-crop-3x_coco.py",
    "content": "_base_ = './mask-rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco.py'\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(type='AmpOptimWrapper')\n"
  },
  {
    "path": "configs/swin/mask-rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth'  # noqa\n\nmodel = dict(\n    type='MaskRCNN',\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[96, 192, 384, 768]))\n\n# augmentation strategy originates from DETR / Sparse RCNN\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    paramwise_cfg=dict(\n        custom_keys={\n            'absolute_pos_embed': dict(decay_mult=0.),\n            'relative_position_bias_table': dict(decay_mult=0.),\n            'norm': dict(decay_mult=0.)\n        }),\n    optimizer=dict(\n        _delete_=True,\n        type='AdamW',\n        lr=0.0001,\n        betas=(0.9, 0.999),\n        weight_decay=0.05))\n"
  },
  {
    "path": "configs/swin/metafile.yml",
    "content": "Models:\n  - Name: mask-rcnn_swin-s-p4-w7_fpn_amp-ms-crop-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/swin/mask-rcnn_swin-s-p4-w7_fpn_amp-ms-crop-3x_coco.py\n    Metadata:\n      Training Memory (GB): 11.9\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Swin Transformer\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 48.2\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 43.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco_20210903_104808-b92c91f1.pth\n    Paper:\n      URL: https://arxiv.org/abs/2107.08430\n      Title: 'Swin Transformer: Hierarchical Vision Transformer using Shifted Windows'\n    README: configs/swin/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/backbones/swin.py#L465\n      Version: v2.16.0\n\n  - Name: mask-rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/swin/mask-rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco.py\n    Metadata:\n      Training Memory (GB): 10.2\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Swin Transformer\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco/mask_rcnn_swin-t-p4-w7_fpn_ms-crop-3x_coco_20210906_131725-bacf6f7b.pth\n    Paper:\n      URL: https://arxiv.org/abs/2107.08430\n      Title: 'Swin Transformer: Hierarchical Vision Transformer using Shifted Windows'\n    README: configs/swin/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/backbones/swin.py#L465\n      Version: v2.16.0\n\n  - Name: mask-rcnn_swin-t-p4-w7_fpn_1x_coco\n    In Collection: Mask R-CNN\n    Config: configs/swin/mask-rcnn_swin-t-p4-w7_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      Epochs: 12\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Swin Transformer\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.7\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 39.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth\n    Paper:\n      URL: https://arxiv.org/abs/2107.08430\n      Title: 'Swin Transformer: Hierarchical Vision Transformer using Shifted Windows'\n    README: configs/swin/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/backbones/swin.py#L465\n      Version: v2.16.0\n\n  - Name: mask-rcnn_swin-t-p4-w7_fpn_amp-ms-crop-3x_coco\n    In Collection: Mask R-CNN\n    Config: configs/swin/mask-rcnn_swin-t-p4-w7_fpn_amp-ms-crop-3x_coco.py\n    Metadata:\n      Training Memory (GB): 7.8\n      Epochs: 36\n      Training Data: COCO\n      Training Techniques:\n        - AdamW\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Swin Transformer\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.0\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 41.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_fp16_ms-crop-3x_coco/mask_rcnn_swin-t-p4-w7_fpn_fp16_ms-crop-3x_coco_20210908_165006-90a4008c.pth\n    Paper:\n      URL: https://arxiv.org/abs/2107.08430\n      Title: 'Swin Transformer: Hierarchical Vision Transformer using Shifted Windows'\n    README: configs/swin/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.16.0/mmdet/models/backbones/swin.py#L465\n      Version: v2.16.0\n"
  },
  {
    "path": "configs/swin/retinanet_swin-t-p4-w7_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth'  # noqa\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=96,\n        depths=[2, 2, 6, 2],\n        num_heads=[3, 6, 12, 24],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.2,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[192, 384, 768], start_level=0, num_outs=5))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/timm_example/README.md",
    "content": "# Timm Example\n\n> [PyTorch Image Models](https://github.com/rwightman/pytorch-image-models)\n\n<!-- [OTHERS] -->\n\n## Abstract\n\nPy**T**orch **Im**age **M**odels (`timm`) is a collection of image models, layers, utilities, optimizers, schedulers, data-loaders / augmentations, and reference training / validation scripts that aim to pull together a wide variety of SOTA models with ability to reproduce ImageNet training results.\n\n<!--\n<div align=center>\n<img src=\"\" height=\"400\" />\n</div>\n-->\n\n## Results and Models\n\n### RetinaNet\n\n|    Backbone     |  Style  | Lr schd | Mem (GB) | Inf time (fps) | box AP |                          Config                           | Download |\n| :-------------: | :-----: | :-----: | :------: | :------------: | :----: | :-------------------------------------------------------: | :------: |\n|      R-50       | pytorch |   1x    |          |                |        |   [config](./retinanet_timm-tv-resnet50_fpn_1x_coco.py)   |          |\n| EfficientNet-B1 |    -    |   1x    |          |                |        | [config](./retinanet_timm-efficientnet-b1_fpn_1x_coco.py) |          |\n\n## Usage\n\n### Install additional requirements\n\nMMDetection supports timm backbones via `TIMMBackbone`, a wrapper class in MMPretrain.\nThus, you need to install `mmpretrain` in addition to timm.\nIf you have already installed requirements for mmdet, run\n\n```shell\npip install 'dataclasses; python_version<\"3.7\"'\npip install timm\npip install mmpretrain\n```\n\nSee [this document](https://mmpretrain.readthedocs.io/en/latest/get_started.html#installation) for the details of MMPretrain installation.\n\n### Edit config\n\n- See example configs for basic usage.\n- See the documents of [timm feature extraction](https://rwightman.github.io/pytorch-image-models/feature_extraction/#multi-scale-feature-maps-feature-pyramid) and [TIMMBackbone](https://mmpretrain.readthedocs.io/en/latest/api/generated/mmpretrain.models.backbones.TIMMBackbone.html#mmpretrain.models.backbones.TIMMBackbone) for details.\n- Which feature map is output depends on the backbone.\n  Please check `backbone out_channels` and `backbone out_strides` in your log, and modify `model.neck.in_channels` and `model.backbone.out_indices` if necessary.\n- If you use Vision Transformer models that do not support `features_only=True`, add `custom_hooks = []` to your config to disable `NumClassCheckHook`.\n\n## Citation\n\n```latex\n@misc{rw2019timm,\n  author = {Ross Wightman},\n  title = {PyTorch Image Models},\n  year = {2019},\n  publisher = {GitHub},\n  journal = {GitHub repository},\n  doi = {10.5281/zenodo.4414861},\n  howpublished = {\\url{https://github.com/rwightman/pytorch-image-models}}\n}\n```\n"
  },
  {
    "path": "configs/timm_example/retinanet_timm-efficientnet-b1_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(\n    imports=['mmpretrain.models'], allow_failed_imports=False)\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='mmpretrain.TIMMBackbone',\n        model_name='efficientnet_b1',\n        features_only=True,\n        pretrained=True,\n        out_indices=(1, 2, 3, 4)),\n    neck=dict(in_channels=[24, 40, 112, 320]))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/timm_example/retinanet_timm-tv-resnet50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(\n    imports=['mmpretrain.models'], allow_failed_imports=False)\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='mmpretrain.TIMMBackbone',\n        model_name='tv_resnet50',  # ResNet-50 with torchvision weights\n        features_only=True,\n        pretrained=True,\n        out_indices=(1, 2, 3, 4)))\n\n# optimizer\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n"
  },
  {
    "path": "configs/tood/README.md",
    "content": "# TOOD\n\n> [TOOD: Task-aligned One-stage Object Detection](https://arxiv.org/abs/2108.07755)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nOne-stage object detection is commonly implemented by optimizing two sub-tasks: object classification and localization, using heads with two parallel branches, which might lead to a certain level of spatial misalignment in predictions between the two tasks. In this work, we propose a Task-aligned One-stage Object Detection (TOOD) that explicitly aligns the two tasks in a learning-based manner. First, we design a novel Task-aligned Head (T-Head) which offers a better balance between learning task-interactive and task-specific features, as well as a greater flexibility to learn the alignment via a task-aligned predictor. Second, we propose Task Alignment Learning (TAL) to explicitly pull closer (or even unify) the optimal anchors for the two tasks during training via a designed sample assignment scheme and a task-aligned loss. Extensive experiments are conducted on MS-COCO, where TOOD achieves a 51.1 AP at single-model single-scale testing. This surpasses the recent one-stage detectors by a large margin, such as ATSS (47.7 AP), GFL (48.2 AP), and PAA (49.0 AP), with fewer parameters and FLOPs. Qualitative results also demonstrate the effectiveness of TOOD for better aligning the tasks of object classification and localization.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/12907710/145400075-e08191f5-8afa-4335-9b3b-27926fc9a26e.png\"/>\n</div>\n\n## Results and Models\n\n|     Backbone      |  Style  | Anchor Type  | Lr schd | Multi-scale Training | Mem (GB) | Inf time (fps) | box AP |                          Config                           |                                                                                                                                                                       Download                                                                                                                                                                        |\n| :---------------: | :-----: | :----------: | :-----: | :------------------: | :------: | :------------: | :----: | :-------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|       R-50        | pytorch | Anchor-free  |   1x    |          N           |   4.1    |                |  42.4  |            [config](./tood_r50_fpn_1x_coco.py)            |                                           [model](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_1x_coco/tood_r50_fpn_1x_coco_20211210_103425-20e20746.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_1x_coco/tood_r50_fpn_1x_coco_20211210_103425.log)                                           |\n|       R-50        | pytorch | Anchor-based |   1x    |          N           |   4.1    |                |  42.4  |     [config](./tood_r50_fpn_anchor-based_1x_coco.py)      |                 [model](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_anchor_based_1x_coco/tood_r50_fpn_anchor_based_1x_coco_20211214_100105-b776c134.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_anchor_based_1x_coco/tood_r50_fpn_anchor_based_1x_coco_20211214_100105.log)                 |\n|       R-50        | pytorch | Anchor-free  |   2x    |          Y           |   4.1    |                |  44.5  |          [config](./tood_r50_fpn_ms-2x_coco.py)           |                           [model](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_mstrain_2x_coco/tood_r50_fpn_mstrain_2x_coco_20211210_144231-3b23174c.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_mstrain_2x_coco/tood_r50_fpn_mstrain_2x_coco_20211210_144231.log)                           |\n|       R-101       | pytorch | Anchor-free  |   2x    |          Y           |   6.0    |                |  46.1  |          [config](./tood_r101_fpn_ms-2x_coco.py)          |                         [model](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r101_fpn_mstrain_2x_coco/tood_r101_fpn_mstrain_2x_coco_20211210_144232-a18f53c8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r101_fpn_mstrain_2x_coco/tood_r101_fpn_mstrain_2x_coco_20211210_144232.log)                         |\n|    R-101-dcnv2    | pytorch | Anchor-free  |   2x    |          Y           |   6.2    |                |  49.3  |    [config](./tood_r101-dconv-c3-c5_fpn_ms-2x_coco.py)    | [model](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r101_fpn_dconv_c3-c5_mstrain_2x_coco/tood_r101_fpn_dconv_c3-c5_mstrain_2x_coco_20211210_213728-4a824142.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r101_fpn_dconv_c3-c5_mstrain_2x_coco/tood_r101_fpn_dconv_c3-c5_mstrain_2x_coco_20211210_213728.log) |\n|    X-101-64x4d    | pytorch | Anchor-free  |   2x    |          Y           |   10.2   |                |  47.6  |       [config](./tood_x101-64x4d_fpn_ms-2x_coco.py)       |             [model](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_x101_64x4d_fpn_mstrain_2x_coco/tood_x101_64x4d_fpn_mstrain_2x_coco_20211211_003519-a4f36113.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tood/tood_x101_64x4d_fpn_mstrain_2x_coco/tood_x101_64x4d_fpn_mstrain_2x_coco_20211211_003519.log)             |\n| X-101-64x4d-dcnv2 | pytorch | Anchor-free  |   2x    |          Y           |          |                |        | [config](./tood_x101-64x4d-dconv-c4-c5_fpn_ms-2x_coco.py) |                                                                                                                                                               [model](<>) \\| [log](<>)                                                                                                                                                                |\n\n\\[1\\] *1x and 2x mean the model is trained for 90K and 180K iterations, respectively.* \\\n\\[2\\] *All results are obtained with a single model and without any test time data augmentation such as multi-scale, flipping and etc..* \\\n\\[3\\] *`dcnv2` denotes deformable convolutional networks v2.* \\\\\n\n## Citation\n\n```latex\n@inproceedings{feng2021tood,\n    title={TOOD: Task-aligned One-stage Object Detection},\n    author={Feng, Chengjian and Zhong, Yujie and Gao, Yu and Scott, Matthew R and Huang, Weilin},\n    booktitle={ICCV},\n    year={2021}\n}\n```\n"
  },
  {
    "path": "configs/tood/metafile.yml",
    "content": "Collections:\n  - Name: TOOD\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - TOOD\n    Paper:\n      URL: https://arxiv.org/abs/2108.07755\n      Title: 'TOOD: Task-aligned One-stage Object Detection'\n    README: configs/tood/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.20.0/mmdet/models/detectors/tood.py#L7\n      Version: v2.20.0\n\nModels:\n  - Name: tood_r101_fpn_ms-2x_coco\n    In Collection: TOOD\n    Config: configs/tood/tood_r101_fpn_ms-2x_coco.py\n    Metadata:\n      Training Memory (GB): 6.0\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.1\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r101_fpn_mstrain_2x_coco/tood_r101_fpn_mstrain_2x_coco_20211210_144232-a18f53c8.pth\n\n  - Name: tood_x101-64x4d_fpn_ms-2x_coco\n    In Collection: TOOD\n    Config: configs/tood/tood_x101-64x4d_fpn_ms-2x_coco.py\n    Metadata:\n      Training Memory (GB): 10.2\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 47.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tood/tood_x101_64x4d_fpn_mstrain_2x_coco/tood_x101_64x4d_fpn_mstrain_2x_coco_20211211_003519-a4f36113.pth\n\n  - Name: tood_r101-dconv-c3-c5_fpn_ms-2x_coco\n    In Collection: TOOD\n    Config: configs/tood/tood_r101-dconv-c3-c5_fpn_ms-2x_coco.py\n    Metadata:\n      Training Memory (GB): 6.2\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 49.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r101_fpn_dconv_c3-c5_mstrain_2x_coco/tood_r101_fpn_dconv_c3-c5_mstrain_2x_coco_20211210_213728-4a824142.pth\n\n  - Name: tood_r50_fpn_anchor-based_1x_coco\n    In Collection: TOOD\n    Config: configs/tood/tood_r50_fpn_anchor-based_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.1\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_anchor_based_1x_coco/tood_r50_fpn_anchor_based_1x_coco_20211214_100105-b776c134.pth\n\n  - Name: tood_r50_fpn_1x_coco\n    In Collection: TOOD\n    Config: configs/tood/tood_r50_fpn_1x_coco.py\n    Metadata:\n      Training Memory (GB): 4.1\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 42.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_1x_coco/tood_r50_fpn_1x_coco_20211210_103425-20e20746.pth\n\n  - Name: tood_r50_fpn_ms-2x_coco\n    In Collection: TOOD\n    Config: configs/tood/tood_r50_fpn_ms-2x_coco.py\n    Metadata:\n      Training Memory (GB): 4.1\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_mstrain_2x_coco/tood_r50_fpn_mstrain_2x_coco_20211210_144231-3b23174c.pth\n"
  },
  {
    "path": "configs/tood/tood_r101-dconv-c3-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './tood_r101_fpn_ms-2x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deformable_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)),\n    bbox_head=dict(num_dcn=2))\n"
  },
  {
    "path": "configs/tood/tood_r101_fpn_ms-2x_coco.py",
    "content": "_base_ = './tood_r50_fpn_ms-2x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/tood/tood_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# model settings\nmodel = dict(\n    type='TOOD',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',\n        num_outs=5),\n    bbox_head=dict(\n        type='TOODHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=6,\n        feat_channels=256,\n        anchor_type='anchor_free',\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        initial_loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            activated=True,  # use probability instead of logit as input\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_cls=dict(\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            activated=True,  # use probability instead of logit as input\n            beta=2.0,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0)),\n    train_cfg=dict(\n        initial_epoch=4,\n        initial_assigner=dict(type='ATSSAssigner', topk=9),\n        assigner=dict(type='TaskAlignedAssigner', topk=13),\n        alpha=1,\n        beta=6,\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/tood/tood_r50_fpn_anchor-based_1x_coco.py",
    "content": "_base_ = './tood_r50_fpn_1x_coco.py'\nmodel = dict(bbox_head=dict(anchor_type='anchor_based'))\n"
  },
  {
    "path": "configs/tood/tood_r50_fpn_ms-2x_coco.py",
    "content": "_base_ = './tood_r50_fpn_1x_coco.py'\nmax_epochs = 24\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\n# training schedule for 2x\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# multi-scale training\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 480), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/tood/tood_x101-64x4d-dconv-c4-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './tood_x101-64x4d_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deformable_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, False, True, True),\n    ),\n    bbox_head=dict(num_dcn=2))\n"
  },
  {
    "path": "configs/tood/tood_x101-64x4d_fpn_ms-2x_coco.py",
    "content": "_base_ = './tood_r50_fpn_ms-2x_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/tridentnet/README.md",
    "content": "# TridentNet\n\n> [Scale-Aware Trident Networks for Object Detection](https://arxiv.org/abs/1901.01892)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nScale variation is one of the key challenges in object detection. In this work, we first present a controlled experiment to investigate the effect of receptive fields for scale variation in object detection. Based on the findings from the exploration experiments, we propose a novel Trident Network (TridentNet) aiming to generate scale-specific feature maps with a uniform representational power. We construct a parallel multi-branch architecture in which each branch shares the same transformation parameters but with different receptive fields. Then, we adopt a scale-aware training scheme to specialize each branch by sampling object instances of proper scales for training. As a bonus, a fast approximation version of TridentNet could achieve significant improvements without any additional parameters and computational cost compared with the vanilla detector. On the COCO dataset, our TridentNet with ResNet-101 backbone achieves state-of-the-art single-model results of 48.4 mAP.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/143999668-0927922e-efc2-45fa-8bfc-1e3df18720f5.png\"/>\n</div>\n\n## Results and Models\n\nWe reports the test results using only one branch for inference.\n\n| Backbone | Style | mstrain | Lr schd | Mem (GB) | Inf time (fps) | box AP |                                                                                                                                                                      Download                                                                                                                                                                      |\n| :------: | :---: | :-----: | :-----: | :------: | :------------: | :----: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | caffe |    N    |   1x    |          |                |  37.7  |                 [model](https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_1x_coco/tridentnet_r50_caffe_1x_coco_20201230_141838-2ec0b530.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_1x_coco/tridentnet_r50_caffe_1x_coco_20201230_141838.log.json)                 |\n|   R-50   | caffe |    Y    |   1x    |          |                |  37.6  | [model](https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_mstrain_1x_coco/tridentnet_r50_caffe_mstrain_1x_coco_20201230_141839-6ce55ccb.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_mstrain_1x_coco/tridentnet_r50_caffe_mstrain_1x_coco_20201230_141839.log.json) |\n|   R-50   | caffe |    Y    |   3x    |          |                |  40.3  | [model](https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_mstrain_3x_coco/tridentnet_r50_caffe_mstrain_3x_coco_20201130_100539-46d227ba.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_mstrain_3x_coco/tridentnet_r50_caffe_mstrain_3x_coco_20201130_100539.log.json) |\n\n**Note**\n\nSimilar to [Detectron2](https://github.com/facebookresearch/detectron2/tree/master/projects/TridentNet), we haven't implemented the Scale-aware Training Scheme in section 4.2 of the paper.\n\n## Citation\n\n```latex\n@InProceedings{li2019scale,\n  title={Scale-Aware Trident Networks for Object Detection},\n  author={Li, Yanghao and Chen, Yuntao and Wang, Naiyan and Zhang, Zhaoxiang},\n  journal={The International Conference on Computer Vision (ICCV)},\n  year={2019}\n}\n```\n"
  },
  {
    "path": "configs/tridentnet/metafile.yml",
    "content": "Collections:\n  - Name: TridentNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - ResNet\n        - TridentNet Block\n    Paper:\n      URL: https://arxiv.org/abs/1901.01892\n      Title: 'Scale-Aware Trident Networks for Object Detection'\n    README: configs/tridentnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.8.0/mmdet/models/detectors/trident_faster_rcnn.py#L6\n      Version: v2.8.0\n\nModels:\n  - Name: tridentnet_r50-caffe_1x_coco\n    In Collection: TridentNet\n    Config: configs/tridentnet/tridentnet_r50-caffe_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_1x_coco/tridentnet_r50_caffe_1x_coco_20201230_141838-2ec0b530.pth\n\n  - Name: tridentnet_r50-caffe_ms-1x_coco\n    In Collection: TridentNet\n    Config: configs/tridentnet/tridentnet_r50-caffe_ms-1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_mstrain_1x_coco/tridentnet_r50_caffe_mstrain_1x_coco_20201230_141839-6ce55ccb.pth\n\n  - Name: tridentnet_r50-caffe_ms-3x_coco\n    In Collection: TridentNet\n    Config: configs/tridentnet/tridentnet_r50-caffe_ms-3x_coco.py\n    Metadata:\n      Epochs: 36\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.3\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/tridentnet/tridentnet_r50_caffe_mstrain_3x_coco/tridentnet_r50_caffe_mstrain_3x_coco_20201130_100539-46d227ba.pth\n"
  },
  {
    "path": "configs/tridentnet/tridentnet_r50-caffe_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50-caffe-c4.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    type='TridentFasterRCNN',\n    backbone=dict(\n        type='TridentResNet',\n        trident_dilations=(1, 2, 3),\n        num_branch=3,\n        test_branch_idx=1,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    roi_head=dict(type='TridentRoIHead', num_branch=3, test_branch_idx=1),\n    train_cfg=dict(\n        rpn_proposal=dict(max_per_img=500),\n        rcnn=dict(\n            sampler=dict(num=128, pos_fraction=0.5,\n                         add_gt_as_proposals=False))))\n"
  },
  {
    "path": "configs/tridentnet/tridentnet_r50-caffe_ms-1x_coco.py",
    "content": "_base_ = 'tridentnet_r50-caffe_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "configs/tridentnet/tridentnet_r50-caffe_ms-3x_coco.py",
    "content": "_base_ = 'tridentnet_r50-caffe_ms-1x_coco.py'\n\n# learning rate\nmax_epochs = 36\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "configs/v3det/README.md",
    "content": "<p>\n<div align=\"center\">\n\n# <img src=\"v3det_icon.jpg\" height=\"25\"> V3Det: Vast Vocabulary Visual Detection Dataset\n\n<div>\n    <a href='https://myownskyw7.github.io/' target='_blank'>Jiaqi Wang</a>*,\n    <a href='https://panzhang0212.github.io/' target='_blank'>Pan Zhang</a>*,\n    Tao Chu*,\n    Yuhang Cao*, </br>\n    Yujie Zhou,\n    <a href='https://wutong16.github.io/' target='_blank'>Tong Wu</a>,\n    Bin Wang,\n    Conghui He,\n    <a href='http://dahua.site/' target='_blank'>Dahua Lin</a></br>\n    (* equal contribution)</br>\n    <strong>Accepted to ICCV 2023 (Oral)</strong>\n</div>\n</p>\n<p>\n<div>\n    <strong>\n        <a href='https://arxiv.org/pdf/2304.03752.pdf' target='_blank'>Paper</a>,\n        <a href='https://v3det.openxlab.org.cn/' target='_blank'>Dataset</a></br>\n    </strong>\n</div>\n</div>\n</p>\n\n<div align=center>\n    <img width=960 src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/9c216387-02be-46e6-b0f2-b856f80f6d84\"/>\n</div>\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nRecent advances in detecting arbitrary objects in the real world are trained and evaluated on object detection datasets with a relatively restricted vocabulary. To facilitate the development of more general visual object detection, we propose V3Det, a vast vocabulary visual detection dataset with precisely annotated bounding boxes on massive images. V3Det has several appealing properties: 1) Vast Vocabulary: It contains bounding boxes of objects from 13,204 categories on real-world images, which is 10 times larger than the existing large vocabulary object detection dataset, e.g., LVIS. 2) Hierarchical Category Organization: The vast vocabulary of V3Det is organized by a hierarchical category tree which annotates the inclusion relationship among categories, encouraging the exploration of category relationships in vast and open vocabulary object detection. 3) Rich Annotations: V3Det comprises precisely annotated objects in 243k images and professional descriptions of each category written by human experts and a powerful chatbot. By offering a vast exploration space, V3Det enables extensive benchmarks on both vast and open vocabulary object detection, leading to new observations, practices, and insights for future research. It has the potential to serve as a cornerstone dataset for developing more general visual perception systems. V3Det is available at https://v3det.openxlab.org.cn/.\n\n## Prepare Dataset\n\nPlease download and prepare V3Det Dataset at [V3Det Homepage](https://v3det.openxlab.org.cn/) and [V3Det Github](https://github.com/V3Det/V3Det).\n\nThe data includes a training set, a validation set, comprising 13,204 categories. The training set consists of 183,354 images, while the validation set has 29,821 images. The data organization is:\n\n```\ndata/\n    V3Det/\n        images/\n            <category_node>/\n                |────<image_name>.png\n                ...\n            ...\n        annotations/\n            |────v3det_2023_v1_category_tree.json       # Category tree\n            |────category_name_13204_v3det_2023_v1.txt  # Category name\n            |────v3det_2023_v1_train.json               # Train set\n            |────v3det_2023_v1_val.json                 # Validation set\n```\n\n## Results and Models\n\n| Backbone |      Model      | Lr schd | box AP |                                     Config                                     |                                                          Download                                                           |\n| :------: | :-------------: | :-----: | :----: | :----------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   |  Faster R-CNN   |   2x    |  25.4  |       [config](./faster_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py)       |  [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight//faster_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x)   |\n|   R-50   |  Cascade R-CNN  |   2x    |  31.6  |      [config](./cascade_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py)       |  [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight//cascade_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x)  |\n|   R-50   |      FCOS       |   2x    |  9.4   |          [config](./fcos_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py)           |      [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight//fcos_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x)      |\n|   R-50   | Deformable-DETR |   50e   |  34.4  |  [config](./deformable-detr-refine-twostage_r50_8xb4_sample1e-3_v3det_50e.py)  |                [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight/Deformable_DETR_V3Det_R50)                |\n|   R-50   |      DINO       |   36e   |  33.5  |            [config](./dino-4scale_r50_8xb2_sample1e-3_v3det_36e.py)            |                     [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight/DINO_V3Det_R50)                      |\n|  Swin-B  |  Faster R-CNN   |   2x    |  37.6  |      [config](./faster_rcnn_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x.py)      | [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight//faster_rcnn_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x)  |\n|  Swin-B  |  Cascade R-CNN  |   2x    |  42.5  |     [config](./cascade_rcnn_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x.py)      | [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight//cascade_rcnn_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x) |\n|  Swin-B  |      FCOS       |   2x    |  21.0  |         [config](./fcos_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x.py)          |     [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight//fcos_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x)     |\n|  Swin-B  | Deformable-DETR |   50e   |  42.5  | [config](./deformable-detr-refine-twostage_swin_16xb2_sample1e-3_v3det_50e.py) |               [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight/Deformable_DETR_V3Det_SwinB)               |\n|  Swin-B  |      DINO       |   36e   |  42.0  |           [config](./dino-4scale_swin_16xb1_sample1e-3_v3det_36e.py)           |                    [model](https://download.openxlab.org.cn/models/V3Det/V3Det/weight/DINO_V3Det_SwinB)                     |\n\n## Citation\n\n```latex\n@inproceedings{wang2023v3det,\n      title = {V3Det: Vast Vocabulary Visual Detection Dataset},\n      author = {Wang, Jiaqi and Zhang, Pan and Chu, Tao and Cao, Yuhang and Zhou, Yujie and Wu, Tong and Wang, Bin and He, Conghui and Lin, Dahua},\n      booktitle = {The IEEE International Conference on Computer Vision (ICCV)},\n      month = {October},\n      year = {2023}\n}\n```\n"
  },
  {
    "path": "configs/v3det/cascade_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py",
    "content": "_base_ = [\n    '../_base_/models/cascade-rcnn_r50_fpn.py', '../_base_/datasets/v3det.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    rpn_head=dict(\n        loss_bbox=dict(_delete_=True, type='L1Loss', loss_weight=1.0)),\n    roi_head=dict(bbox_head=[\n        dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=13204,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=True,\n            cls_predictor_cfg=dict(\n                type='NormedLinear', tempearture=50, bias=True),\n            loss_cls=dict(\n                type='CrossEntropyCustomLoss',\n                num_classes=13204,\n                use_sigmoid=True,\n                loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n        dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=13204,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.05, 0.05, 0.1, 0.1]),\n            reg_class_agnostic=True,\n            cls_predictor_cfg=dict(\n                type='NormedLinear', tempearture=50, bias=True),\n            loss_cls=dict(\n                type='CrossEntropyCustomLoss',\n                num_classes=13204,\n                use_sigmoid=True,\n                loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0)),\n        dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=13204,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.033, 0.033, 0.067, 0.067]),\n            reg_class_agnostic=True,\n            cls_predictor_cfg=dict(\n                type='NormedLinear', tempearture=50, bias=True),\n            loss_cls=dict(\n                type='CrossEntropyCustomLoss',\n                num_classes=13204,\n                use_sigmoid=True,\n                loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0))\n    ]),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn_proposal=dict(nms_pre=4000, max_per_img=2000),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.5,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1,\n                    perm_repeat_gt_cfg=dict(iou_thr=0.7, perm_range=0.01)),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1,\n                    perm_repeat_gt_cfg=dict(iou_thr=0.7, perm_range=0.01)),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1,\n                    perm_repeat_gt_cfg=dict(iou_thr=0.7, perm_range=0.01)),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            nms=dict(type='nms', iou_threshold=0.6),\n            max_per_img=300)))\n# dataset settings\ntrain_dataloader = dict(batch_size=4, num_workers=8)\n\n# training schedule for 1x\nmax_iter = 68760 * 2\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=max_iter)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 2048,\n        by_epoch=False,\n        begin=0,\n        end=5000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[45840 * 2, 63030 * 2],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(_delete_=True, type='AdamW', lr=1e-4 * 1, weight_decay=0.1),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=32)\n\ndefault_hooks = dict(\n    checkpoint=dict(type='CheckpointHook', by_epoch=False, interval=5730 * 2))\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n"
  },
  {
    "path": "configs/v3det/cascade_rcnn_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x.py",
    "content": "_base_ = [\n    './cascade_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py',\n]\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window7_224.pth'  # noqa\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[128, 256, 512, 1024]))\n"
  },
  {
    "path": "configs/v3det/deformable-detr-refine-twostage_r50_8xb4_sample1e-3_v3det_50e.py",
    "content": "_base_ = '../deformable_detr/deformable-detr-refine-twostage_r50_16xb2-50e_coco.py'  # noqa\n\nmodel = dict(\n    bbox_head=dict(num_classes=13204),\n    test_cfg=dict(max_per_img=300),\n)\n\ndata_root = 'data/V3Det/'\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=4,\n    num_workers=4,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(\n            type='V3DetDataset',\n            data_root=data_root,\n            ann_file='annotations/v3det_2023_v1_train.json',\n            data_prefix=dict(img=''),\n            filter_cfg=dict(filter_empty_gt=False),\n            pipeline=train_pipeline,\n            backend_args=None)))\nval_dataloader = dict(\n    dataset=dict(\n        type='V3DetDataset',\n        data_root=data_root,\n        ann_file='annotations/v3det_2023_v1_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    ann_file=data_root + 'annotations/v3det_2023_v1_val.json',\n    use_mp_eval=True,\n    proposal_nums=[300])\ntest_evaluator = val_evaluator\n\n# training schedule for 50e\n# when using RFS, bs32, each epoch ~ 5730 iter\nmax_iter = 286500\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=max_iter / 5)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[229200],  # 40e\n        gamma=0.1)\n]\n\ndefault_hooks = dict(\n    timer=dict(type='IterTimerHook'),\n    param_scheduler=dict(type='ParamSchedulerHook'),\n    checkpoint=dict(\n        type='CheckpointHook', by_epoch=False, interval=5730,\n        max_keep_ckpts=3))\n\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n"
  },
  {
    "path": "configs/v3det/deformable-detr-refine-twostage_swin_16xb2_sample1e-3_v3det_50e.py",
    "content": "_base_ = 'deformable-detr-refine-twostage_r50_8xb4_sample1e-3_v3det_50e.py'\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window7_224.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n\ntrain_dataloader = dict(batch_size=2, num_workers=2)\n"
  },
  {
    "path": "configs/v3det/dino-4scale_r50_8xb2_sample1e-3_v3det_36e.py",
    "content": "_base_ = '../dino/dino-4scale_r50_8xb2-36e_coco.py'\n\nmodel = dict(\n    bbox_head=dict(num_classes=13204),\n    test_cfg=dict(max_per_img=300),\n)\n\ndata_root = 'data/V3Det/'\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(\n            type='V3DetDataset',\n            data_root=data_root,\n            ann_file='annotations/v3det_2023_v1_train.json',\n            data_prefix=dict(img=''),\n            filter_cfg=dict(filter_empty_gt=False),\n            pipeline=train_pipeline,\n            backend_args=None)))\nval_dataloader = dict(\n    dataset=dict(\n        type='V3DetDataset',\n        data_root=data_root,\n        ann_file='annotations/v3det_2023_v1_val.json',\n        data_prefix=dict(img='')))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    ann_file=data_root + 'annotations/v3det_2023_v1_val.json',\n    use_mp_eval=True,\n    proposal_nums=[300])\ntest_evaluator = val_evaluator\n\n# training schedule for 36e\n# when using RFS, bs16, each epoch ~ 11460 iter\nmax_iter = 412560\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=max_iter / 5)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[343800],  # 30e\n        gamma=0.1)\n]\n\ndefault_hooks = dict(\n    timer=dict(type='IterTimerHook'),\n    param_scheduler=dict(type='ParamSchedulerHook'),\n    checkpoint=dict(\n        type='CheckpointHook',\n        by_epoch=False,\n        interval=11460,\n        max_keep_ckpts=3))\n\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n"
  },
  {
    "path": "configs/v3det/dino-4scale_swin_16xb1_sample1e-3_v3det_36e.py",
    "content": "_base_ = 'dino-4scale_r50_8xb2_sample1e-3_v3det_36e.py'\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window7_224.pth'  # noqa\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[256, 512, 1024]),\n)\n\ntrain_dataloader = dict(batch_size=1)\n"
  },
  {
    "path": "configs/v3det/faster_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py",
    "content": "_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/datasets/v3det.py',\n    '../_base_/schedules/schedule_2x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            num_classes=13204,\n            reg_class_agnostic=True,\n            cls_predictor_cfg=dict(\n                type='NormedLinear', tempearture=50, bias=True),\n            loss_cls=dict(\n                type='CrossEntropyCustomLoss',\n                num_classes=13204,\n                use_sigmoid=True,\n                loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn_proposal=dict(nms_pre=4000, max_per_img=2000),\n        rcnn=dict(\n            assigner=dict(\n                perm_repeat_gt_cfg=dict(iou_thr=0.7, perm_range=0.01)))),\n    test_cfg=dict(\n        rcnn=dict(\n            score_thr=0.0001,\n            nms=dict(type='nms', iou_threshold=0.6),\n            max_per_img=300)))\n# dataset settings\ntrain_dataloader = dict(batch_size=4, num_workers=8)\n\n# training schedule for 2x\nmax_iter = 68760 * 2\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=max_iter)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 2048,\n        by_epoch=False,\n        begin=0,\n        end=5000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[45840 * 2, 63030 * 2],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(_delete_=True, type='AdamW', lr=1e-4 * 1, weight_decay=0.1),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=32)\n\ndefault_hooks = dict(\n    checkpoint=dict(type='CheckpointHook', by_epoch=False, interval=5730 * 2))\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n"
  },
  {
    "path": "configs/v3det/faster_rcnn_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x.py",
    "content": "_base_ = [\n    './faster_rcnn_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py',\n]\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window7_224.pth'  # noqa\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[128, 256, 512, 1024]))\n"
  },
  {
    "path": "configs/v3det/fcos_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py",
    "content": "_base_ = [\n    '../_base_/datasets/v3det.py', '../_base_/schedules/schedule_2x.py',\n    '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    type='FCOS',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        num_outs=5,\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='FCOSHead',\n        num_classes=13204,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        cls_predictor_cfg=dict(type='NormedLinear', tempearture=50, bias=True),\n        loss_cls=dict(\n            type='FocalCustomLoss',\n            use_sigmoid=True,\n            num_classes=13204,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # model training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0,\n            ignore_iof_thr=-1,\n            perm_repeat_gt_cfg=dict(iou_thr=0.7, perm_range=0.01)),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.0001,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=300))\n# dataset settings\n\nbackend_args = None\n\ntrain_dataloader = dict(batch_size=2, num_workers=8)\n\n# training schedule for 2x\nmax_iter = 68760 * 2 * 2\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=max_iter,\n    val_interval=max_iter)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=1.0 / 2048,\n        by_epoch=False,\n        begin=0,\n        end=5000 * 2),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[45840 * 2 * 2, 63030 * 2 * 2],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=1e-4 * 0.25, weight_decay=0.1),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=32)\n\ndefault_hooks = dict(\n    checkpoint=dict(type='CheckpointHook', by_epoch=False, interval=5730 * 2))\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n\nfind_unused_parameters = True\n"
  },
  {
    "path": "configs/v3det/fcos_swinb_fpn_8x4_sample1e-3_mstrain_v3det_2x.py",
    "content": "_base_ = [\n    './fcos_r50_fpn_8x4_sample1e-3_mstrain_v3det_2x.py',\n]\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window7_224.pth'  # noqa\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.0,\n        attn_drop_rate=0.0,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[128, 256, 512, 1024], force_grad_on_level=True))\n"
  },
  {
    "path": "configs/vfnet/README.md",
    "content": "# VarifocalNet\n\n> [VarifocalNet: An IoU-aware Dense Object Detector](https://arxiv.org/abs/2008.13367)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nAccurately ranking the vast number of candidate detections is crucial for dense object detectors to achieve high performance. Prior work uses the classification score or a combination of classification and predicted localization scores to rank candidates. However, neither option results in a reliable ranking, thus degrading detection performance. In this paper, we propose to learn an Iou-aware Classification Score (IACS) as a joint representation of object presence confidence and localization accuracy. We show that dense object detectors can achieve a more accurate ranking of candidate detections based on the IACS. We design a new loss function, named Varifocal Loss, to train a dense object detector to predict the IACS, and propose a new star-shaped bounding box feature representation for IACS prediction and bounding box refinement. Combining these two new components and a bounding box refinement branch, we build an IoU-aware dense object detector based on the FCOS+ATSS architecture, that we call VarifocalNet or VFNet for short. Extensive experiments on MS COCO show that our VFNet consistently surpasses the strong baseline by ∼2.0 AP with different backbones. Our best model VFNet-X-1200 with Res2Net-101-DCN achieves a single-model single-scale AP of 55.1 on COCO test-dev, which is state-of-the-art among various object detectors.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/9102141/97464778-4b9ab000-197c-11eb-9283-ab2907ee0252.png\"/>\n</div>\n\n## Introduction\n\n**VarifocalNet (VFNet)** learns to predict the IoU-aware classification score which mixes the object presence confidence and localization accuracy together as the detection score for a bounding box. The learning is supervised by the proposed Varifocal Loss (VFL), based on a new star-shaped bounding box feature representation (the features at nine yellow sampling points). Given the new representation, the object localization accuracy is further improved by refining the initially regressed bounding box. The full paper is available at: [https://arxiv.org/abs/2008.13367](https://arxiv.org/abs/2008.13367).\n\n## Results and Models\n\n|  Backbone   |  Style  | DCN | MS train | Lr schd | Inf time (fps) | box AP (val) | box AP (test-dev) |                           Config                            |                                                                                                                                                                               Download                                                                                                                                                                               |\n| :---------: | :-----: | :-: | :------: | :-----: | :------------: | :----------: | :---------------: | :---------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|    R-50     | pytorch |  N  |    N     |   1x    |       -        |     41.6     |       41.6        |            [config](./vfnet_r50_fpn_1x_coco.py)             |                                                          [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_1x_coco/vfnet_r50_fpn_1x_coco_20201027-38db6f58.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_1x_coco/vfnet_r50_fpn_1x_coco.json)                                                           |\n|    R-50     | pytorch |  N  |    Y     |   2x    |       -        |     44.5     |       44.8        |           [config](./vfnet_r50_fpn_ms-2x_coco.py)           |                                          [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mstrain_2x_coco/vfnet_r50_fpn_mstrain_2x_coco_20201027-7cc75bd2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mstrain_2x_coco/vfnet_r50_fpn_mstrain_2x_coco.json)                                           |\n|    R-50     | pytorch |  Y  |    Y     |   2x    |       -        |     47.8     |       48.0        |    [config](./vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco.py)     |               [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-6879c318.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco.json)               |\n|    R-101    | pytorch |  N  |    N     |   1x    |       -        |     43.0     |       43.6        |            [config](./vfnet_r101_fpn_1x_coco.py)            |                                                       [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_1x_coco/vfnet_r101_fpn_1x_coco_20201027pth-c831ece7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_1x_coco/vfnet_r101_fpn_1x_coco.json)                                                       |\n|    R-101    | pytorch |  N  |    Y     |   2x    |       -        |     46.2     |       46.7        |          [config](./vfnet_r101_fpn_ms-2x_coco.py)           |                                       [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_mstrain_2x_coco/vfnet_r101_fpn_mstrain_2x_coco_20201027pth-4a5d53f1.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_mstrain_2x_coco/vfnet_r101_fpn_mstrain_2x_coco.json)                                       |\n|    R-101    | pytorch |  Y  |    Y     |   2x    |       -        |     49.0     |       49.2        |    [config](./vfnet_r101-mdconv-c3-c5_fpn_ms-2x_coco.py)    |             [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_r101_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-7729adb5.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_r101_fpn_mdconv_c3-c5_mstrain_2x_coco.json)             |\n| X-101-32x4d | pytorch |  Y  |    Y     |   2x    |       -        |     49.7     |       50.0        | [config](./vfnet_x101-32x4d-mdconv-c3-c5_fpn_ms-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_x101_32x4d_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_x101_32x4d_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-d300a6fc.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_x101_32x4d_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_x101_32x4d_fpn_mdconv_c3-c5_mstrain_2x_coco.json) |\n| X-101-64x4d | pytorch |  Y  |    Y     |   2x    |       -        |     50.4     |       50.8        | [config](./vfnet_x101-64x4d-mdconv-c3-c5_fpn_ms-2x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_x101_64x4d_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_x101_64x4d_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-b5f6da5e.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_x101_64x4d_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_x101_64x4d_fpn_mdconv_c3-c5_mstrain_2x_coco.json) |\n\n**Notes:**\n\n- The MS-train scale range is 1333x\\[480:960\\] (`range` mode) and the inference scale keeps 1333x800.\n- DCN means using `DCNv2` in both backbone and head.\n- Inference time will be updated soon.\n- More results and pre-trained models can be found in [VarifocalNet-Github](https://github.com/hyz-xmaster/VarifocalNet)\n\n## Citation\n\n```latex\n@article{zhang2020varifocalnet,\n  title={VarifocalNet: An IoU-aware Dense Object Detector},\n  author={Zhang, Haoyang and Wang, Ying and Dayoub, Feras and S{\\\"u}nderhauf, Niko},\n  journal={arXiv preprint arXiv:2008.13367},\n  year={2020}\n}\n```\n"
  },
  {
    "path": "configs/vfnet/metafile.yml",
    "content": "Collections:\n  - Name: VFNet\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n        - Varifocal Loss\n    Paper:\n      URL: https://arxiv.org/abs/2008.13367\n      Title: 'VarifocalNet: An IoU-aware Dense Object Detector'\n    README: configs/vfnet/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.6.0/mmdet/models/detectors/vfnet.py#L6\n      Version: v2.6.0\n\nModels:\n  - Name: vfnet_r50_fpn_1x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_r50_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 41.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_1x_coco/vfnet_r50_fpn_1x_coco_20201027-38db6f58.pth\n\n  - Name: vfnet_r50_fpn_ms-2x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_r50_fpn_ms-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 44.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mstrain_2x_coco/vfnet_r50_fpn_mstrain_2x_coco_20201027-7cc75bd2.pth\n\n  - Name: vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 48.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-6879c318.pth\n\n  - Name: vfnet_r101_fpn_1x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_r101_fpn_1x_coco.py\n    Metadata:\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 43.6\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_1x_coco/vfnet_r101_fpn_1x_coco_20201027pth-c831ece7.pth\n\n  - Name: vfnet_r101_fpn_ms-2x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_r101_fpn_ms-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 46.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_mstrain_2x_coco/vfnet_r101_fpn_mstrain_2x_coco_20201027pth-4a5d53f1.pth\n\n  - Name: vfnet_r101-mdconv-c3-c5_fpn_ms-2x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_r101-mdconv-c3-c5_fpn_ms-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 49.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_r101_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_r101_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-7729adb5.pth\n\n  - Name: vfnet_x101-32x4d-mdconv-c3-c5_fpn_ms-2x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_x101-32x4d-mdconv-c3-c5_fpn_ms-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_x101_32x4d_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_x101_32x4d_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-d300a6fc.pth\n\n  - Name: vfnet_x101-64x4d-mdconv-c3-c5_fpn_ms-2x_coco\n    In Collection: VFNet\n    Config: configs/vfnet/vfnet_x101-64x4d-mdconv-c3-c5_fpn_ms-2x_coco.py\n    Metadata:\n      Epochs: 24\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/vfnet/vfnet_x101_64x4d_fpn_mdconv_c3-c5_mstrain_2x_coco/vfnet_x101_64x4d_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-b5f6da5e.pth\n"
  },
  {
    "path": "configs/vfnet/vfnet_r101-mdconv-c3-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNet',\n        depth=101,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_r101_fpn_1x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_r101_fpn_2x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n# learning policy\nmax_epochs = 24\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/vfnet/vfnet_r101_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True)),\n    bbox_head=dict(dcn_on_last_conv=True))\n"
  },
  {
    "path": "configs/vfnet/vfnet_r50_fpn_1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(\n    type='VFNet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        num_outs=5,\n        relu_before_extra_convs=True),\n    bbox_head=dict(\n        type='VFNetHead',\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=3,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        center_sampling=False,\n        dcn_on_last_conv=False,\n        use_atss=True,\n        use_vfl=True,\n        loss_cls=dict(\n            type='VarifocalLoss',\n            use_sigmoid=True,\n            alpha=0.75,\n            gamma=2.0,\n            iou_weighted=True,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.5),\n        loss_bbox_refine=dict(type='GIoULoss', loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(type='ATSSAssigner', topk=9),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n\n# data setting\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.01),\n    paramwise_cfg=dict(bias_lr_mult=2., bias_decay_mult=0.),\n    clip_grad=None)\n# learning rate\nmax_epochs = 12\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/vfnet/vfnet_r50_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_1x_coco.py'\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize', scale=[(1333, 480), (1333, 960)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n# learning policy\nmax_epochs = 24\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(max_epochs=max_epochs)\n"
  },
  {
    "path": "configs/vfnet/vfnet_res2net-101_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='Res2Net',\n        depth=101,\n        scales=4,\n        base_width=26,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://res2net101_v1d_26w_4s')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_res2net101-mdconv-c3-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='Res2Net',\n        depth=101,\n        scales=4,\n        base_width=26,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://res2net101_v1d_26w_4s')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_x101-32x4d-mdconv-c3-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_x101-32x4d_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_x101-64x4d-mdconv-c3-c5_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50-mdconv-c3-c5_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),\n        stage_with_dcn=(False, True, True, True),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/vfnet/vfnet_x101-64x4d_fpn_ms-2x_coco.py",
    "content": "_base_ = './vfnet_r50_fpn_ms-2x_coco.py'\nmodel = dict(\n    backbone=dict(\n        type='ResNeXt',\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "configs/wider_face/README.md",
    "content": "# WIDER FACE\n\n> [WIDER FACE: A Face Detection Benchmark](https://arxiv.org/abs/1511.06523)\n\n<!-- [DATASET] -->\n\n## Abstract\n\nFace detection is one of the most studied topics in the computer vision community. Much of the progresses have been made by the availability of face detection benchmark datasets. We show that there is a gap between current face detection performance and the real world requirements. To facilitate future face detection research, we introduce the WIDER FACE dataset, which is 10 times larger than existing datasets. The dataset contains rich annotations, including occlusions, poses, event categories, and face bounding boxes. Faces in the proposed dataset are extremely challenging due to large variations in scale, pose and occlusion, as shown in Fig. 1. Furthermore, we show that WIDER FACE dataset is an effective training source for face detection. We benchmark several representative detection systems, providing an overview of state-of-the-art performance and propose a solution to deal with large scale variation. Finally, we discuss common failure cases that worth to be further investigated.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/144000364-3320de79-34fc-40a6-938f-bb512f05a4bb.png\" height=\"400\"/>\n</div>\n\n## Introduction\n\nTo use the WIDER Face dataset you need to download it\nand extract to the `data/WIDERFace` folder. Annotation in the VOC format\ncan be found in this [repo](https://github.com/sovrasov/wider-face-pascal-voc-annotations.git).\nYou should move the annotation files from `WIDER_train_annotations` and `WIDER_val_annotations` folders\nto the `Annotation` folders inside the corresponding directories `WIDER_train` and `WIDER_val`.\nAlso annotation lists `val.txt` and `train.txt` should be copied to `data/WIDERFace` from `WIDER_train_annotations` and `WIDER_val_annotations`.\nThe directory should be like this:\n\n```\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── WIDERFace\n│   │   ├── WIDER_train\n│   |   │   ├──0--Parade\n│   |   │   ├── ...\n│   |   │   ├── Annotations\n│   │   ├── WIDER_val\n│   |   │   ├──0--Parade\n│   |   │   ├── ...\n│   |   │   ├── Annotations\n│   │   ├── val.txt\n│   │   ├── train.txt\n\n```\n\nAfter that you can train the SSD300 on WIDER by launching training with the `ssd300_wider_face.py` config or\ncreate your own config based on the presented one.\n\n## Citation\n\n```latex\n@inproceedings{yang2016wider,\n   Author = {Yang, Shuo and Luo, Ping and Loy, Chen Change and Tang, Xiaoou},\n   Booktitle = {IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},\n   Title = {WIDER FACE: A Face Detection Benchmark},\n   Year = {2016}\n}\n```\n"
  },
  {
    "path": "configs/wider_face/retinanet_r50_fpn_1x_widerface.py",
    "content": "_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/wider_face.py', '../_base_/schedules/schedule_1x.py',\n    '../_base_/default_runtime.py'\n]\n# model settings\nmodel = dict(bbox_head=dict(num_classes=1))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n"
  },
  {
    "path": "configs/wider_face/ssd300_8xb32-24e_widerface.py",
    "content": "_base_ = [\n    '../_base_/models/ssd300.py', '../_base_/datasets/wider_face.py',\n    '../_base_/default_runtime.py', '../_base_/schedules/schedule_2x.py'\n]\nmodel = dict(bbox_head=dict(num_classes=1))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(\n        type='Expand',\n        mean={{_base_.model.data_preprocessor.mean}},\n        to_rgb={{_base_.model.data_preprocessor.bgr_to_rgb}},\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(300, 300), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='Resize', scale=(300, 300), keep_ratio=False),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ndataset_type = 'WIDERFaceDataset'\ndata_root = 'data/WIDERFace/'\ntrain_dataloader = dict(\n    batch_size=32, num_workers=8, dataset=dict(pipeline=train_pipeline))\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(type='MultiStepLR', by_epoch=True, milestones=[16, 20], gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(lr=0.012, momentum=0.9, weight_decay=5e-4),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (32 samples per GPU)\nauto_scale_lr = dict(base_batch_size=256)\n"
  },
  {
    "path": "configs/yolact/README.md",
    "content": "# YOLACT\n\n> [YOLACT: Real-time Instance Segmentation](https://arxiv.org/abs/1904.02689)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present a simple, fully-convolutional model for real-time instance segmentation that achieves 29.8 mAP on MS COCO at 33.5 fps evaluated on a single Titan Xp, which is significantly faster than any previous competitive approach. Moreover, we obtain this result after training on only one GPU. We accomplish this by breaking instance segmentation into two parallel subtasks: (1) generating a set of prototype masks and (2) predicting per-instance mask coefficients. Then we produce instance masks by linearly combining the prototypes with the mask coefficients. We find that because this process doesn't depend on repooling, this approach produces very high-quality masks and exhibits temporal stability for free. Furthermore, we analyze the emergent behavior of our prototypes and show they learn to localize instances on their own in a translation variant manner, despite being fully-convolutional. Finally, we also propose Fast NMS, a drop-in 12 ms faster replacement for standard NMS that only has a marginal performance penalty.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/144001225-8c62cad7-a330-4f60-873f-61aa17e99223.png\"/>\n</div>\n\n## Introduction\n\nA simple, fully convolutional model for real-time instance segmentation. This is the code for our paper:\n\n- [YOLACT: Real-time Instance Segmentation](https://arxiv.org/abs/1904.02689)\n\n<!-- - [YOLACT++: Better Real-time Instance Segmentation](https://arxiv.org/abs/1912.06218) -->\n\nFor a real-time demo, check out our ICCV video:\n[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/0pMfmo8qfpQ/0.jpg)](https://www.youtube.com/watch?v=0pMfmo8qfpQ)\n\n## Evaluation\n\nHere are our YOLACT models along with their FPS on a Titan Xp and mAP on COCO's `val`:\n\n| Image Size | GPU x BS |   Backbone    | \\*FPS | mAP  | Weights |                 Configs                  |                                                            Download                                                             |\n| :--------: | :------: | :-----------: | :---: | :--: | :-----: | :--------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------: |\n|    550     |   1x8    | Resnet50-FPN  | 42.5  | 29.0 |         | [config](./yolact_r50_1xb8-55e_coco.py)  |  [model](https://download.openmmlab.com/mmdetection/v2.0/yolact/yolact_r50_1x8_coco/yolact_r50_1x8_coco_20200908-f38d58df.pth)  |\n|    550     |   8x8    | Resnet50-FPN  | 42.5  | 28.4 |         | [config](./yolact_r50_8xb8-55e_coco.py)  |  [model](https://download.openmmlab.com/mmdetection/v2.0/yolact/yolact_r50_8x8_coco/yolact_r50_8x8_coco_20200908-ca34f5db.pth)  |\n|    550     |   1x8    | Resnet101-FPN | 33.5  | 30.4 |         | [config](./yolact_r101_1xb8-55e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/yolact/yolact_r101_1x8_coco/yolact_r101_1x8_coco_20200908-4cbe9101.pth) |\n\n\\*Note: The FPS is evaluated by the [original implementation](https://github.com/dbolya/yolact). When calculating FPS, only the model inference time is taken into account. Data loading and post-processing operations such as converting masks to RLE code, generating COCO JSON results, image rendering are not included.\n\n## Training\n\nAll the aforementioned models are trained with a single GPU. It typically takes ~12GB VRAM when using resnet-101 as the backbone. If you want to try multiple GPUs training, you may have to modify the configuration files accordingly, such as adjusting the training schedule and freezing batch norm.\n\n```Shell\n# Trains using the resnet-101 backbone with a batch size of 8 on a single GPU.\n./tools/dist_train.sh configs/yolact/yolact_r101.py 1\n```\n\n## Testing\n\nPlease refer to [mmdetection/docs/getting_started.md](https://mmdetection.readthedocs.io/en/latest/1_exist_data_model.html#test-existing-models).\n\n## Citation\n\nIf you use YOLACT or this code base in your work, please cite\n\n```latex\n@inproceedings{yolact-iccv2019,\n  author    = {Daniel Bolya and Chong Zhou and Fanyi Xiao and Yong Jae Lee},\n  title     = {YOLACT: {Real-time} Instance Segmentation},\n  booktitle = {ICCV},\n  year      = {2019},\n}\n```\n\n<!-- For YOLACT++, please cite\n\n```latex\n@misc{yolact-plus-arxiv2019,\n  title         = {YOLACT++: Better Real-time Instance Segmentation},\n  author        = {Daniel Bolya and Chong Zhou and Fanyi Xiao and Yong Jae Lee},\n  year          = {2019},\n  eprint        = {1912.06218},\n  archivePrefix = {arXiv},\n  primaryClass  = {cs.CV}\n}\n``` -->\n"
  },
  {
    "path": "configs/yolact/metafile.yml",
    "content": "Collections:\n  - Name: YOLACT\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - FPN\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/1904.02689\n      Title: 'YOLACT: Real-time Instance Segmentation'\n    README: configs/yolact/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.5.0/mmdet/models/detectors/yolact.py#L9\n      Version: v2.5.0\n\nModels:\n  - Name: yolact_r50_1x8_coco\n    In Collection: YOLACT\n    Config: configs/yolact/yolact_r50_1xb8-55e_coco.py\n    Metadata:\n      Training Resources: 1x V100 GPU\n      Batch Size: 8\n      Epochs: 55\n      inference time (ms/im):\n        - value: 23.53\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (550, 550)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 29.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolact/yolact_r50_1x8_coco/yolact_r50_1x8_coco_20200908-f38d58df.pth\n\n  - Name: yolact_r50_8x8_coco\n    In Collection: YOLACT\n    Config: configs/yolact/yolact_r50_8xb8-55e_coco.py\n    Metadata:\n      Batch Size: 64\n      Epochs: 55\n      inference time (ms/im):\n        - value: 23.53\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (550, 550)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 28.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolact/yolact_r50_8x8_coco/yolact_r50_8x8_coco_20200908-ca34f5db.pth\n\n  - Name: yolact_r101_1x8_coco\n    In Collection: YOLACT\n    Config: configs/yolact/yolact_r101_1xb8-55e_coco.py\n    Metadata:\n      Training Resources: 1x V100 GPU\n      Batch Size: 8\n      Epochs: 55\n      inference time (ms/im):\n        - value: 29.85\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (550, 550)\n    Results:\n      - Task: Instance Segmentation\n        Dataset: COCO\n        Metrics:\n          mask AP: 30.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolact/yolact_r101_1x8_coco/yolact_r101_1x8_coco_20200908-4cbe9101.pth\n"
  },
  {
    "path": "configs/yolact/yolact_r101_1xb8-55e_coco.py",
    "content": "_base_ = './yolact_r50_1xb8-55e_coco.py'\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(type='Pretrained',\n                      checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "configs/yolact/yolact_r50_1xb8-55e_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_instance.py', '../_base_/default_runtime.py'\n]\nimg_norm_cfg = dict(\n    mean=[123.68, 116.78, 103.94], std=[58.40, 57.12, 57.38], to_rgb=True)\n# model settings\ninput_size = 550\nmodel = dict(\n    type='YOLACT',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=img_norm_cfg['mean'],\n        std=img_norm_cfg['std'],\n        bgr_to_rgb=img_norm_cfg['to_rgb'],\n        pad_mask=True),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=-1,  # do not freeze stem\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=False,  # update the statistics of bn\n        zero_init_residual=False,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_input',\n        num_outs=5,\n        upsample_cfg=dict(mode='bilinear')),\n    bbox_head=dict(\n        type='YOLACTHead',\n        num_classes=80,\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=3,\n            scales_per_octave=1,\n            base_sizes=[8, 16, 32, 64, 128],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[550.0 / x for x in [69, 35, 18, 9, 5]],\n            centers=[(550 * 0.5 / x, 550 * 0.5 / x)\n                     for x in [69, 35, 18, 9, 5]]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=False,\n            reduction='none',\n            loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.5),\n        num_head_convs=1,\n        num_protos=32,\n        use_ohem=True),\n    mask_head=dict(\n        type='YOLACTProtonet',\n        in_channels=256,\n        num_protos=32,\n        num_classes=80,\n        max_masks_to_train=100,\n        loss_mask_weight=6.125,\n        with_seg_branch=True,\n        loss_segm=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0.,\n            ignore_iof_thr=-1,\n            gt_max_assign_all=False),\n        sampler=dict(type='PseudoSampler'),  # YOLACT should use PseudoSampler\n        # smoothl1_beta=1.,\n        allowed_border=-1,\n        pos_weight=-1,\n        neg_pos_ratio=3,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        mask_thr=0.5,\n        iou_thr=0.5,\n        top_k=200,\n        max_per_img=100,\n        mask_thr_binary=0.5))\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(4.0, 4.0)),\n    dict(\n        type='Expand',\n        mean=img_norm_cfg['mean'],\n        to_rgb=img_norm_cfg['to_rgb'],\n        ratio_range=(1, 4)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.1, 0.3, 0.5, 0.7, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PhotoMetricDistortion',\n        brightness_delta=32,\n        contrast_range=(0.5, 1.5),\n        saturation_range=(0.5, 1.5),\n        hue_delta=18),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(input_size, input_size), keep_ratio=False),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=4,\n    batch_sampler=None,\n    dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nmax_epochs = 55\n# training schedule for 55e\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# learning rate\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20, 42, 49, 52],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=1e-3, momentum=0.9, weight_decay=5e-4))\n\ncustom_hooks = [\n    dict(type='CheckInvalidLossHook', interval=50, priority='VERY_LOW')\n]\n\nenv_cfg = dict(cudnn_benchmark=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (1 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=8)\n"
  },
  {
    "path": "configs/yolact/yolact_r50_8xb8-55e_coco.py",
    "content": "_base_ = 'yolact_r50_1xb8-55e_coco.py'\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(lr=8e-3),\n    clip_grad=dict(max_norm=35, norm_type=2))\n# learning rate\nmax_epochs = 55\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20, 42, 49, 52],\n        gamma=0.1)\n]\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/yolo/README.md",
    "content": "# YOLOv3\n\n> [YOLOv3: An Incremental Improvement](https://arxiv.org/abs/1804.02767)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present some updates to YOLO! We made a bunch of little design changes to make it better. We also trained this new network that's pretty swell. It's a little bigger than last time but more accurate. It's still fast though, don't worry. At 320x320 YOLOv3 runs in 22 ms at 28.2 mAP, as accurate as SSD but three times faster. When we look at the old .5 IOU mAP detection metric YOLOv3 is quite good. It achieves 57.9 mAP@50 in 51 ms on a Titan X, compared to 57.5 mAP@50 in 198 ms by RetinaNet, similar performance but 3.8x faster.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/144001433-b4f7fb5e-3b7a-414b-b949-93733213b670.png\" height=\"300\"/>\n</div>\n\n## Results and Models\n\n|  Backbone  | Scale | Lr schd | Mem (GB) | Inf time (fps) | box AP |                     Config                      |                                                                                                                                                        Download                                                                                                                                                        |\n| :--------: | :---: | :-----: | :------: | :------------: | :----: | :---------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| DarkNet-53 |  320  |  273e   |   2.7    |      63.9      |  27.9  |  [config](./yolov3_d53_8xb8-320-273e_coco.py)   |                         [model](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_320_273e_coco/yolov3_d53_320_273e_coco-421362b6.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_320_273e_coco/yolov3_d53_320_273e_coco-20200819_172101.log.json)                         |\n| DarkNet-53 |  416  |  273e   |   3.8    |      61.2      |  30.9  | [config](./yolov3_d53_8xb8-ms-416-273e_coco.py) |         [model](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_mstrain-416_273e_coco/yolov3_d53_mstrain-416_273e_coco-2b60fcd9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_mstrain-416_273e_coco/yolov3_d53_mstrain-416_273e_coco-20200819_173424.log.json)         |\n| DarkNet-53 |  608  |  273e   |   7.4    |      48.1      |  33.7  | [config](./yolov3_d53_8xb8-ms-608-273e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_mstrain-608_273e_coco/yolov3_d53_mstrain-608_273e_coco_20210518_115020-a2c3acb8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_mstrain-608_273e_coco/yolov3_d53_mstrain-608_273e_coco_20210518_115020.log.json) |\n\n## Mixed Precision Training\n\nWe also train YOLOv3 with mixed precision training.\n\n|  Backbone  | Scale | Lr schd | Mem (GB) | Inf time (fps) | box AP |                       Config                        |                                                                                                                                                                  Download                                                                                                                                                                  |\n| :--------: | :---: | :-----: | :------: | :------------: | :----: | :-------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| DarkNet-53 |  608  |  273e   |   4.7    |      48.1      |  33.8  | [config](./yolov3_d53_8xb8-amp-ms-608-273e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_fp16_mstrain-608_273e_coco/yolov3_d53_fp16_mstrain-608_273e_coco_20210517_213542-4bc34944.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_fp16_mstrain-608_273e_coco/yolov3_d53_fp16_mstrain-608_273e_coco_20210517_213542.log.json) |\n\n## Lightweight models\n\n|  Backbone   | Scale | Lr schd | Mem (GB) | Inf time (fps) | box AP |                          Config                          |                                                                                                                                                                        Download                                                                                                                                                                        |\n| :---------: | :---: | :-----: | :------: | :------------: | :----: | :------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| MobileNetV2 |  416  |  300e   |   5.3    |                |  23.9  | [config](./yolov3_mobilenetv2_8xb24-ms-416-300e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_mobilenetv2_mstrain-416_300e_coco/yolov3_mobilenetv2_mstrain-416_300e_coco_20210718_010823-f68a07b3.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_mobilenetv2_mstrain-416_300e_coco/yolov3_mobilenetv2_mstrain-416_300e_coco_20210718_010823.log.json) |\n| MobileNetV2 |  320  |  300e   |   3.2    |                |  22.2  |  [config](./yolov3_mobilenetv2_8xb24-320-300e_coco.py)   |                 [model](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_mobilenetv2_320_300e_coco/yolov3_mobilenetv2_320_300e_coco_20210719_215349-d18dff72.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_mobilenetv2_320_300e_coco/yolov3_mobilenetv2_320_300e_coco_20210719_215349.log.json)                 |\n\nNotice: We reduce the number of channels to 96 in both head and neck. It can reduce the flops and parameters, which makes these models more suitable for edge devices.\n\n## Credit\n\nThis implementation originates from the project of Haoyu Wu(@wuhy08) at Western Digital.\n\n## Citation\n\n```latex\n@misc{redmon2018yolov3,\n    title={YOLOv3: An Incremental Improvement},\n    author={Joseph Redmon and Ali Farhadi},\n    year={2018},\n    eprint={1804.02767},\n    archivePrefix={arXiv},\n    primaryClass={cs.CV}\n}\n```\n"
  },
  {
    "path": "configs/yolo/metafile.yml",
    "content": "Collections:\n  - Name: YOLOv3\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - DarkNet\n    Paper:\n      URL: https://arxiv.org/abs/1804.02767\n      Title: 'YOLOv3: An Incremental Improvement'\n    README: configs/yolo/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.4.0/mmdet/models/detectors/yolo.py#L8\n      Version: v2.4.0\n\nModels:\n  - Name: yolov3_d53_320_273e_coco\n    In Collection: YOLOv3\n    Config: configs/yolo/yolov3_d53_8xb8-320-273e_coco.py\n    Metadata:\n      Training Memory (GB): 2.7\n      inference time (ms/im):\n        - value: 15.65\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (320, 320)\n      Epochs: 273\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 27.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_320_273e_coco/yolov3_d53_320_273e_coco-421362b6.pth\n\n  - Name: yolov3_d53_mstrain-416_273e_coco\n    In Collection: YOLOv3\n    Config: configs/yolo/yolov3_d53_8xb8-ms-416-273e_coco.py\n    Metadata:\n      Training Memory (GB): 3.8\n      inference time (ms/im):\n        - value: 16.34\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (416, 416)\n      Epochs: 273\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 30.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_mstrain-416_273e_coco/yolov3_d53_mstrain-416_273e_coco-2b60fcd9.pth\n\n  - Name: yolov3_d53_mstrain-608_273e_coco\n    In Collection: YOLOv3\n    Config: configs/yolo/yolov3_d53_8xb8-ms-608-273e_coco.py\n    Metadata:\n      Training Memory (GB): 7.4\n      inference time (ms/im):\n        - value: 20.79\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP32\n          resolution: (608, 608)\n      Epochs: 273\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 33.7\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_mstrain-608_273e_coco/yolov3_d53_mstrain-608_273e_coco_20210518_115020-a2c3acb8.pth\n\n  - Name: yolov3_d53_fp16_mstrain-608_273e_coco\n    In Collection: YOLOv3\n    Config: configs/yolo/yolov3_d53_8xb8-amp-ms-608-273e_coco.py\n    Metadata:\n      Training Memory (GB): 4.7\n      inference time (ms/im):\n        - value: 20.79\n          hardware: V100\n          backend: PyTorch\n          batch size: 1\n          mode: FP16\n          resolution: (608, 608)\n      Epochs: 273\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 33.8\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_d53_fp16_mstrain-608_273e_coco/yolov3_d53_fp16_mstrain-608_273e_coco_20210517_213542-4bc34944.pth\n\n  - Name: yolov3_mobilenetv2_8xb24-320-300e_coco\n    In Collection: YOLOv3\n    Config: configs/yolo/yolov3_mobilenetv2_8xb24-320-300e_coco.py\n    Metadata:\n      Training Memory (GB): 3.2\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 22.2\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_mobilenetv2_320_300e_coco/yolov3_mobilenetv2_320_300e_coco_20210719_215349-d18dff72.pth\n\n  - Name: yolov3_mobilenetv2_8xb24-ms-416-300e_coco\n    In Collection: YOLOv3\n    Config: configs/yolo/yolov3_mobilenetv2_8xb24-ms-416-300e_coco.py\n    Metadata:\n      Training Memory (GB): 5.3\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 23.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolo/yolov3_mobilenetv2_mstrain-416_300e_coco/yolov3_mobilenetv2_mstrain-416_300e_coco_20210718_010823-f68a07b3.pth\n"
  },
  {
    "path": "configs/yolo/yolov3_d53_8xb8-320-273e_coco.py",
    "content": "_base_ = './yolov3_d53_8xb8-ms-608-273e_coco.py'\n\ninput_size = (320, 320)\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    # `mean` and `to_rgb` should be the same with the `preprocess_cfg`\n    dict(type='Expand', mean=[0, 0, 0], to_rgb=True, ratio_range=(1, 2)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.4, 0.5, 0.6, 0.7, 0.8, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=input_size, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PhotoMetricDistortion'),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=input_size, keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/yolo/yolov3_d53_8xb8-amp-ms-608-273e_coco.py",
    "content": "_base_ = './yolov3_d53_8xb8-ms-608-273e_coco.py'\n# fp16 settings\noptim_wrapper = dict(type='AmpOptimWrapper', loss_scale='dynamic')\n"
  },
  {
    "path": "configs/yolo/yolov3_d53_8xb8-ms-416-273e_coco.py",
    "content": "_base_ = './yolov3_d53_8xb8-ms-608-273e_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    # `mean` and `to_rgb` should be the same with the `preprocess_cfg`\n    dict(type='Expand', mean=[0, 0, 0], to_rgb=True, ratio_range=(1, 2)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.4, 0.5, 0.6, 0.7, 0.8, 0.9),\n        min_crop_size=0.3),\n    dict(type='RandomResize', scale=[(320, 320), (416, 416)], keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PhotoMetricDistortion'),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(416, 416), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/yolo/yolov3_d53_8xb8-ms-608-273e_coco.py",
    "content": "_base_ = ['../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py']\n# model settings\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[0, 0, 0],\n    std=[255., 255., 255.],\n    bgr_to_rgb=True,\n    pad_size_divisor=32)\nmodel = dict(\n    type='YOLOV3',\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type='Darknet',\n        depth=53,\n        out_indices=(3, 4, 5),\n        init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://darknet53')),\n    neck=dict(\n        type='YOLOV3Neck',\n        num_scales=3,\n        in_channels=[1024, 512, 256],\n        out_channels=[512, 256, 128]),\n    bbox_head=dict(\n        type='YOLOV3Head',\n        num_classes=80,\n        in_channels=[512, 256, 128],\n        out_channels=[1024, 512, 256],\n        anchor_generator=dict(\n            type='YOLOAnchorGenerator',\n            base_sizes=[[(116, 90), (156, 198), (373, 326)],\n                        [(30, 61), (62, 45), (59, 119)],\n                        [(10, 13), (16, 30), (33, 23)]],\n            strides=[32, 16, 8]),\n        bbox_coder=dict(type='YOLOBBoxCoder'),\n        featmap_strides=[32, 16, 8],\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            loss_weight=1.0,\n            reduction='sum'),\n        loss_conf=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            loss_weight=1.0,\n            reduction='sum'),\n        loss_xy=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            loss_weight=2.0,\n            reduction='sum'),\n        loss_wh=dict(type='MSELoss', loss_weight=2.0, reduction='sum')),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='GridAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0)),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        conf_thr=0.005,\n        nms=dict(type='nms', iou_threshold=0.45),\n        max_per_img=100))\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='Expand',\n        mean=data_preprocessor['mean'],\n        to_rgb=data_preprocessor['bgr_to_rgb'],\n        ratio_range=(1, 2)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.4, 0.5, 0.6, 0.7, 0.8, 0.9),\n        min_crop_size=0.3),\n    dict(type='RandomResize', scale=[(320, 320), (608, 608)], keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PhotoMetricDistortion'),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(608, 608), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=4,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\ntrain_cfg = dict(max_epochs=273, val_interval=7)\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.001, momentum=0.9, weight_decay=0.0005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning policy\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=2000),\n    dict(type='MultiStepLR', by_epoch=True, milestones=[218, 246], gamma=0.1)\n]\n\ndefault_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=7))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/yolo/yolov3_mobilenetv2_8xb24-320-300e_coco.py",
    "content": "_base_ = ['./yolov3_mobilenetv2_8xb24-ms-416-300e_coco.py']\n\n# yapf:disable\nmodel = dict(\n    bbox_head=dict(\n        anchor_generator=dict(\n            base_sizes=[[(220, 125), (128, 222), (264, 266)],\n                        [(35, 87), (102, 96), (60, 170)],\n                        [(10, 15), (24, 36), (72, 42)]])))\n# yapf:enable\n\ninput_size = (320, 320)\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    # `mean` and `to_rgb` should be the same with the `preprocess_cfg`\n    dict(\n        type='Expand',\n        mean=[123.675, 116.28, 103.53],\n        to_rgb=True,\n        ratio_range=(1, 2)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.4, 0.5, 0.6, 0.7, 0.8, 0.9),\n        min_crop_size=0.3),\n    dict(type='Resize', scale=input_size, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PhotoMetricDistortion'),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=input_size, keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(dataset=dict(pipeline=train_pipeline)))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/yolo/yolov3_mobilenetv2_8xb24-ms-416-300e_coco.py",
    "content": "_base_ = ['../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py']\n# model settings\ndata_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True,\n    pad_size_divisor=32)\nmodel = dict(\n    type='YOLOV3',\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type='MobileNetV2',\n        out_indices=(2, 4, 6),\n        act_cfg=dict(type='LeakyReLU', negative_slope=0.1),\n        init_cfg=dict(\n            type='Pretrained', checkpoint='open-mmlab://mmdet/mobilenet_v2')),\n    neck=dict(\n        type='YOLOV3Neck',\n        num_scales=3,\n        in_channels=[320, 96, 32],\n        out_channels=[96, 96, 96]),\n    bbox_head=dict(\n        type='YOLOV3Head',\n        num_classes=80,\n        in_channels=[96, 96, 96],\n        out_channels=[96, 96, 96],\n        anchor_generator=dict(\n            type='YOLOAnchorGenerator',\n            base_sizes=[[(116, 90), (156, 198), (373, 326)],\n                        [(30, 61), (62, 45), (59, 119)],\n                        [(10, 13), (16, 30), (33, 23)]],\n            strides=[32, 16, 8]),\n        bbox_coder=dict(type='YOLOBBoxCoder'),\n        featmap_strides=[32, 16, 8],\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            loss_weight=1.0,\n            reduction='sum'),\n        loss_conf=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            loss_weight=1.0,\n            reduction='sum'),\n        loss_xy=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            loss_weight=2.0,\n            reduction='sum'),\n        loss_wh=dict(type='MSELoss', loss_weight=2.0, reduction='sum')),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='GridAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0)),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        conf_thr=0.005,\n        nms=dict(type='nms', iou_threshold=0.45),\n        max_per_img=100))\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='Expand',\n        mean=data_preprocessor['mean'],\n        to_rgb=data_preprocessor['bgr_to_rgb'],\n        ratio_range=(1, 2)),\n    dict(\n        type='MinIoURandomCrop',\n        min_ious=(0.4, 0.5, 0.6, 0.7, 0.8, 0.9),\n        min_crop_size=0.3),\n    dict(type='RandomResize', scale=[(320, 320), (416, 416)], keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PhotoMetricDistortion'),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=(416, 416), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=24,\n    num_workers=4,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='RepeatDataset',  # use RepeatDataset to speed up training\n        times=10,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=24,\n    num_workers=4,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\ntrain_cfg = dict(max_epochs=30)\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.003, momentum=0.9, weight_decay=0.0005),\n    clip_grad=dict(max_norm=35, norm_type=2))\n\n# learning policy\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=4000),\n    dict(type='MultiStepLR', by_epoch=True, milestones=[24, 28], gamma=0.1)\n]\n\nfind_unused_parameters = True\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (24 samples per GPU)\nauto_scale_lr = dict(base_batch_size=192)\n"
  },
  {
    "path": "configs/yolof/README.md",
    "content": "# YOLOF\n\n> [You Only Look One-level Feature](https://arxiv.org/abs/2103.09460)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nThis paper revisits feature pyramids networks (FPN) for one-stage detectors and points out that the success of FPN is due to its divide-and-conquer solution to the optimization problem in object detection rather than multi-scale feature fusion. From the perspective of optimization, we introduce an alternative way to address the problem instead of adopting the complex feature pyramids - {\\\\em utilizing only one-level feature for detection}. Based on the simple and efficient solution, we present You Only Look One-level Feature (YOLOF). In our method, two key components, Dilated Encoder and Uniform Matching, are proposed and bring considerable improvements. Extensive experiments on the COCO benchmark prove the effectiveness of the proposed model. Our YOLOF achieves comparable results with its feature pyramids counterpart RetinaNet while being 2.5× faster. Without transformer layers, YOLOF can match the performance of DETR in a single-level feature manner with 7× less training epochs. With an image size of 608×608, YOLOF achieves 44.3 mAP running at 60 fps on 2080Ti, which is 13% faster than YOLOv4.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/144001639-257374ef-7d4f-412b-a783-88abdd22f277.png\"/>\n</div>\n\n## Results and Models\n\n| Backbone | Style | Epoch | Lr schd | Mem (GB) | box AP |                  Config                  |                                                                                                                                         Download                                                                                                                                         |\n| :------: | :---: | :---: | :-----: | :------: | :----: | :--------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| R-50-C5  | caffe |   Y   |   1x    |   8.3    |  37.5  | [config](./yolof_r50-c5_8xb8-1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/yolof/yolof_r50_c5_8x8_1x_coco/yolof_r50_c5_8x8_1x_coco_20210425_024427-8e864411.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolof/yolof_r50_c5_8x8_1x_coco/yolof_r50_c5_8x8_1x_coco_20210425_024427.log.json) |\n\n**Note**:\n\n1. We find that the performance is unstable and may fluctuate by about 0.3 mAP. mAP 37.4 ~ 37.7 is acceptable in YOLOF_R_50_C5_1x. Such fluctuation can also be found in the [original implementation](https://github.com/chensnathan/YOLOF).\n2. In addition to instability issues, sometimes there are large loss fluctuations and NAN, so there may still be problems with this project, which will be improved subsequently.\n\n## Citation\n\n```latex\n@inproceedings{chen2021you,\n  title={You Only Look One-level Feature},\n  author={Chen, Qiang and Wang, Yingming and Yang, Tong and Zhang, Xiangyu and Cheng, Jian and Sun, Jian},\n  booktitle={IEEE Conference on Computer Vision and Pattern Recognition},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/yolof/metafile.yml",
    "content": "Collections:\n  - Name: YOLOF\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Momentum\n        - Weight Decay\n      Training Resources: 8x V100 GPUs\n      Architecture:\n        - Dilated Encoder\n        - ResNet\n    Paper:\n      URL: https://arxiv.org/abs/2103.09460\n      Title: 'You Only Look One-level Feature'\n    README: configs/yolof/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.12.0/mmdet/models/detectors/yolof.py#L6\n      Version: v2.12.0\n\nModels:\n  - Name: yolof_r50_c5_8x8_1x_coco\n    In Collection: YOLOF\n    Config: configs/yolof/yolof_r50-c5_8xb8-1x_coco.py\n    Metadata:\n      Training Memory (GB): 8.3\n      Epochs: 12\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 37.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolof/yolof_r50_c5_8x8_1x_coco/yolof_r50_c5_8x8_1x_coco_20210425_024427-8e864411.pth\n"
  },
  {
    "path": "configs/yolof/yolof_r50-c5_8xb8-1x_coco.py",
    "content": "_base_ = [\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    type='YOLOF',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(3, ),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='open-mmlab://detectron/resnet50_caffe')),\n    neck=dict(\n        type='DilatedEncoder',\n        in_channels=2048,\n        out_channels=512,\n        block_mid_channels=128,\n        num_residual_blocks=4,\n        block_dilations=[2, 4, 6, 8]),\n    bbox_head=dict(\n        type='YOLOFHead',\n        num_classes=80,\n        in_channels=512,\n        reg_decoded_bbox=True,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[1, 2, 4, 8, 16],\n            strides=[32]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1., 1., 1., 1.],\n            add_ctr_clamp=True,\n            ctr_clamp=32),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=1.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='UniformAssigner', pos_ignore_thr=0.15, neg_ignore_thr=0.7),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type='nms', iou_threshold=0.6),\n        max_per_img=100))\n# optimizer\noptim_wrapper = dict(\n    optimizer=dict(type='SGD', lr=0.12, momentum=0.9, weight_decay=0.0001),\n    paramwise_cfg=dict(\n        norm_decay_mult=0., custom_keys={'backbone': dict(lr_mult=1. / 3)}))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.00066667,\n        by_epoch=False,\n        begin=0,\n        end=1500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='RandomShift', prob=0.5, max_shift_px=32),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=8, num_workers=8, dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/yolof/yolof_r50-c5_8xb8-iter-1x_coco.py",
    "content": "_base_ = './yolof_r50-c5_8xb8-1x_coco.py'\n\n# We implemented the iter-based config according to the source code.\n# COCO dataset has 117266 images after filtering. We use 8 gpu and\n# 8 batch size training, so 22500 is equivalent to\n# 22500/(117266/(8x8))=12.3 epoch, 15000 is equivalent to 8.2 epoch,\n# 20000 is equivalent to 10.9 epoch. Due to lr(0.12) is large,\n# the iter-based and epoch-based setting have about 0.2 difference on\n# the mAP evaluation value.\n\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=22500,\n    val_interval=4500)\n\n# learning rate policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=22500,\n        by_epoch=False,\n        milestones=[15000, 20000],\n        gamma=0.1)\n]\ntrain_dataloader = dict(sampler=dict(type='InfiniteSampler'))\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=2500))\n\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "configs/yolox/README.md",
    "content": "# YOLOX\n\n> [YOLOX: Exceeding YOLO Series in 2021](https://arxiv.org/abs/2107.08430)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIn this report, we present some experienced improvements to YOLO series, forming a new high-performance detector -- YOLOX. We switch the YOLO detector to an anchor-free manner and conduct other advanced detection techniques, i.e., a decoupled head and the leading label assignment strategy SimOTA to achieve state-of-the-art results across a large scale range of models: For YOLO-Nano with only 0.91M parameters and 1.08G FLOPs, we get 25.3% AP on COCO, surpassing NanoDet by 1.8% AP; for YOLOv3, one of the most widely used detectors in industry, we boost it to 47.3% AP on COCO, outperforming the current best practice by 3.0% AP; for YOLOX-L with roughly the same amount of parameters as YOLOv4-CSP, YOLOv5-L, we achieve 50.0% AP on COCO at a speed of 68.9 FPS on Tesla V100, exceeding YOLOv5-L by 1.8% AP. Further, we won the 1st Place on Streaming Perception Challenge (Workshop on Autonomous Driving at CVPR 2021) using a single YOLOX-L model. We hope this report can provide useful experience for developers and researchers in practical scenes, and we also provide deploy versions with ONNX, TensorRT, NCNN, and Openvino supported.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/144001736-9fb303dd-eac7-46b0-ad45-214cfa51e928.png\"/>\n</div>\n\n## Results and Models\n\n|  Backbone  | size | Mem (GB) | box AP |                  Config                  |                                                                                                                                         Download                                                                                                                                         |\n| :--------: | :--: | :------: | :----: | :--------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| YOLOX-tiny | 416  |   3.5    |  32.0  | [config](./yolox_tiny_8xb8-300e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_tiny_8x8_300e_coco/yolox_tiny_8x8_300e_coco_20211124_171234-b4047906.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_tiny_8x8_300e_coco/yolox_tiny_8x8_300e_coco_20211124_171234.log.json) |\n|  YOLOX-s   | 640  |   7.6    |  40.5  |  [config](./yolox_s_8xb8-300e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_s_8x8_300e_coco/yolox_s_8x8_300e_coco_20211121_095711-4592a793.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_s_8x8_300e_coco/yolox_s_8x8_300e_coco_20211121_095711.log.json)       |\n|  YOLOX-l   | 640  |   19.9   |  49.4  |  [config](./yolox_l_8xb8-300e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_l_8x8_300e_coco/yolox_l_8x8_300e_coco_20211126_140236-d3bd2b23.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_l_8x8_300e_coco/yolox_l_8x8_300e_coco_20211126_140236.log.json)       |\n|  YOLOX-x   | 640  |   28.1   |  50.9  |  [config](./yolox_x_8xb8-300e_coco.py)   |       [model](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_x_8x8_300e_coco/yolox_x_8x8_300e_coco_20211126_140254-1ef88d67.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_x_8x8_300e_coco/yolox_x_8x8_300e_coco_20211126_140254.log.json)       |\n\n**Note**:\n\n1. The test score threshold is 0.001, and the box AP indicates the best AP.\n2. Due to the need for pre-training weights, we cannot reproduce the performance of the `yolox-nano` model. Please refer to https://github.com/Megvii-BaseDetection/YOLOX/issues/674 for more information.\n3. We also trained the model by the official release of YOLOX based on [Megvii-BaseDetection/YOLOX#735](https://github.com/Megvii-BaseDetection/YOLOX/issues/735) with commit ID [38c633](https://github.com/Megvii-BaseDetection/YOLOX/tree/38c633bf176462ee42b110c70e4ffe17b5753208). We found that the best AP of `YOLOX-tiny`, `YOLOX-s`, `YOLOX-l`, and `YOLOX-x` is 31.8, 40.3, 49.2, and 50.9, respectively. The performance is consistent with that of our re-implementation (see Table above) but still has a gap (0.3~0.8 AP) in comparison with the reported performance in their [README](https://github.com/Megvii-BaseDetection/YOLOX/blob/38c633bf176462ee42b110c70e4ffe17b5753208/README.md#benchmark).\n\n## Citation\n\n```latex\n@article{yolox2021,\n  title={{YOLOX}: Exceeding YOLO Series in 2021},\n  author={Ge, Zheng and Liu, Songtao and Wang, Feng and Li, Zeming and Sun, Jian},\n  journal={arXiv preprint arXiv:2107.08430},\n  year={2021}\n}\n```\n"
  },
  {
    "path": "configs/yolox/metafile.yml",
    "content": "Collections:\n  - Name: YOLOX\n    Metadata:\n      Training Data: COCO\n      Training Techniques:\n        - SGD with Nesterov\n        - Weight Decay\n        - Cosine Annealing Lr Updater\n      Training Resources: 8x TITANXp GPUs\n      Architecture:\n        - CSPDarkNet\n        - PAFPN\n    Paper:\n      URL: https://arxiv.org/abs/2107.08430\n      Title: 'YOLOX: Exceeding YOLO Series in 2021'\n    README: configs/yolox/README.md\n    Code:\n      URL: https://github.com/open-mmlab/mmdetection/blob/v2.15.1/mmdet/models/detectors/yolox.py#L6\n      Version: v2.15.1\n\n\nModels:\n  - Name: yolox_s_8x8_300e_coco\n    In Collection: YOLOX\n    Config: configs/yolox/yolox_s_8xb8-300e_coco.py\n    Metadata:\n      Training Memory (GB): 7.6\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 40.5\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_s_8x8_300e_coco/yolox_s_8x8_300e_coco_20211121_095711-4592a793.pth\n  - Name: yolox_l_8x8_300e_coco\n    In Collection: YOLOX\n    Config: configs/yolox/yolox_l_8xb8-300e_coco.py\n    Metadata:\n      Training Memory (GB): 19.9\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 49.4\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_l_8x8_300e_coco/yolox_l_8x8_300e_coco_20211126_140236-d3bd2b23.pth\n  - Name: yolox_x_8x8_300e_coco\n    In Collection: YOLOX\n    Config: configs/yolox/yolox_x_8xb8-300e_coco.py\n    Metadata:\n      Training Memory (GB): 28.1\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 50.9\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_x_8x8_300e_coco/yolox_x_8x8_300e_coco_20211126_140254-1ef88d67.pth\n  - Name: yolox_tiny_8x8_300e_coco\n    In Collection: YOLOX\n    Config: configs/yolox/yolox_tiny_8xb8-300e_coco.py\n    Metadata:\n      Training Memory (GB): 3.5\n      Epochs: 300\n    Results:\n      - Task: Object Detection\n        Dataset: COCO\n        Metrics:\n          box AP: 32.0\n    Weights: https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_tiny_8x8_300e_coco/yolox_tiny_8x8_300e_coco_20211124_171234-b4047906.pth\n"
  },
  {
    "path": "configs/yolox/yolox_l_8xb8-300e_coco.py",
    "content": "_base_ = './yolox_s_8xb8-300e_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(deepen_factor=1.0, widen_factor=1.0),\n    neck=dict(\n        in_channels=[256, 512, 1024], out_channels=256, num_csp_blocks=3),\n    bbox_head=dict(in_channels=256, feat_channels=256))\n"
  },
  {
    "path": "configs/yolox/yolox_m_8xb8-300e_coco.py",
    "content": "_base_ = './yolox_s_8xb8-300e_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(deepen_factor=0.67, widen_factor=0.75),\n    neck=dict(in_channels=[192, 384, 768], out_channels=192, num_csp_blocks=2),\n    bbox_head=dict(in_channels=192, feat_channels=192),\n)\n"
  },
  {
    "path": "configs/yolox/yolox_nano_8xb8-300e_coco.py",
    "content": "_base_ = './yolox_tiny_8xb8-300e_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(deepen_factor=0.33, widen_factor=0.25, use_depthwise=True),\n    neck=dict(\n        in_channels=[64, 128, 256],\n        out_channels=64,\n        num_csp_blocks=1,\n        use_depthwise=True),\n    bbox_head=dict(in_channels=64, feat_channels=64, use_depthwise=True))\n"
  },
  {
    "path": "configs/yolox/yolox_s_8xb8-300e_coco.py",
    "content": "_base_ = [\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py',\n    './yolox_tta.py'\n]\n\nimg_scale = (640, 640)  # width, height\n\n# model settings\nmodel = dict(\n    type='YOLOX',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        pad_size_divisor=32,\n        batch_augments=[\n            dict(\n                type='BatchSyncRandomResize',\n                random_size_range=(480, 800),\n                size_divisor=32,\n                interval=10)\n        ]),\n    backbone=dict(\n        type='CSPDarknet',\n        deepen_factor=0.33,\n        widen_factor=0.5,\n        out_indices=(2, 3, 4),\n        use_depthwise=False,\n        spp_kernal_sizes=(5, 9, 13),\n        norm_cfg=dict(type='BN', momentum=0.03, eps=0.001),\n        act_cfg=dict(type='Swish'),\n    ),\n    neck=dict(\n        type='YOLOXPAFPN',\n        in_channels=[128, 256, 512],\n        out_channels=128,\n        num_csp_blocks=1,\n        use_depthwise=False,\n        upsample_cfg=dict(scale_factor=2, mode='nearest'),\n        norm_cfg=dict(type='BN', momentum=0.03, eps=0.001),\n        act_cfg=dict(type='Swish')),\n    bbox_head=dict(\n        type='YOLOXHead',\n        num_classes=80,\n        in_channels=128,\n        feat_channels=128,\n        stacked_convs=2,\n        strides=(8, 16, 32),\n        use_depthwise=False,\n        norm_cfg=dict(type='BN', momentum=0.03, eps=0.001),\n        act_cfg=dict(type='Swish'),\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='sum',\n            loss_weight=1.0),\n        loss_bbox=dict(\n            type='IoULoss',\n            mode='square',\n            eps=1e-16,\n            reduction='sum',\n            loss_weight=5.0),\n        loss_obj=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='sum',\n            loss_weight=1.0),\n        loss_l1=dict(type='L1Loss', reduction='sum', loss_weight=1.0)),\n    train_cfg=dict(assigner=dict(type='SimOTAAssigner', center_radius=2.5)),\n    # In order to align the source code, the threshold of the val phase is\n    # 0.01, and the threshold of the test phase is 0.001.\n    test_cfg=dict(score_thr=0.01, nms=dict(type='nms', iou_threshold=0.65)))\n\n# dataset settings\ndata_root = 'data/coco/'\ndataset_type = 'CocoDataset'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='Mosaic', img_scale=img_scale, pad_val=114.0),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        # img_scale is (width, height)\n        border=(-img_scale[0] // 2, -img_scale[1] // 2)),\n    dict(\n        type='MixUp',\n        img_scale=img_scale,\n        ratio_range=(0.8, 1.6),\n        pad_val=114.0),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    # According to the official implementation, multi-scale\n    # training is not considered here but in the\n    # 'mmdet/models/detectors/yolox.py'.\n    # Resize and Pad are for the last 15 epochs when Mosaic,\n    # RandomAffine, and MixUp are closed by YOLOXModeSwitchHook.\n    dict(type='Resize', scale=img_scale, keep_ratio=True),\n    dict(\n        type='Pad',\n        pad_to_square=True,\n        # If the image is three-channel, the pad value needs\n        # to be set separately for each channel.\n        pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1), keep_empty=False),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataset = dict(\n    # use MultiImageMixDataset wrapper to support mosaic and mixup\n    type='MultiImageMixDataset',\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        pipeline=[\n            dict(type='LoadImageFromFile', backend_args=backend_args),\n            dict(type='LoadAnnotations', with_bbox=True)\n        ],\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        backend_args=backend_args),\n    pipeline=train_pipeline)\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=img_scale, keep_ratio=True),\n    dict(\n        type='Pad',\n        pad_to_square=True,\n        pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=4,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=train_dataset)\nval_dataloader = dict(\n    batch_size=8,\n    num_workers=4,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# training settings\nmax_epochs = 300\nnum_last_epochs = 15\ninterval = 10\n\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=interval)\n\n# optimizer\n# default 8 gpu\nbase_lr = 0.01\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=base_lr, momentum=0.9, weight_decay=5e-4,\n        nesterov=True),\n    paramwise_cfg=dict(norm_decay_mult=0., bias_decay_mult=0.))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        # use quadratic formula to warm up 5 epochs\n        # and lr is updated by iteration\n        # TODO: fix default scope in get function\n        type='mmdet.QuadraticWarmupLR',\n        by_epoch=True,\n        begin=0,\n        end=5,\n        convert_to_iter_based=True),\n    dict(\n        # use cosine lr from 5 to 285 epoch\n        type='CosineAnnealingLR',\n        eta_min=base_lr * 0.05,\n        begin=5,\n        T_max=max_epochs - num_last_epochs,\n        end=max_epochs - num_last_epochs,\n        by_epoch=True,\n        convert_to_iter_based=True),\n    dict(\n        # use fixed lr during last 15 epochs\n        type='ConstantLR',\n        by_epoch=True,\n        factor=1,\n        begin=max_epochs - num_last_epochs,\n        end=max_epochs,\n    )\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(\n        interval=interval,\n        max_keep_ckpts=3  # only keep latest 3 checkpoints\n    ))\n\ncustom_hooks = [\n    dict(\n        type='YOLOXModeSwitchHook',\n        num_last_epochs=num_last_epochs,\n        priority=48),\n    dict(type='SyncNormHook', priority=48),\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0001,\n        update_buffers=True,\n        priority=49)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "configs/yolox/yolox_tiny_8xb8-300e_coco.py",
    "content": "_base_ = './yolox_s_8xb8-300e_coco.py'\n\n# model settings\nmodel = dict(\n    data_preprocessor=dict(batch_augments=[\n        dict(\n            type='BatchSyncRandomResize',\n            random_size_range=(320, 640),\n            size_divisor=32,\n            interval=10)\n    ]),\n    backbone=dict(deepen_factor=0.33, widen_factor=0.375),\n    neck=dict(in_channels=[96, 192, 384], out_channels=96),\n    bbox_head=dict(in_channels=96, feat_channels=96))\n\nimg_scale = (640, 640)  # width, height\n\ntrain_pipeline = [\n    dict(type='Mosaic', img_scale=img_scale, pad_val=114.0),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.5, 1.5),\n        # img_scale is (width, height)\n        border=(-img_scale[0] // 2, -img_scale[1] // 2)),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', prob=0.5),\n    # Resize and Pad are for the last 15 epochs when Mosaic and\n    # RandomAffine are closed by YOLOXModeSwitchHook.\n    dict(type='Resize', scale=img_scale, keep_ratio=True),\n    dict(\n        type='Pad',\n        pad_to_square=True,\n        pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1), keep_empty=False),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(416, 416), keep_ratio=True),\n    dict(\n        type='Pad',\n        pad_to_square=True,\n        pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "configs/yolox/yolox_tta.py",
    "content": "tta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(type='nms', iou_threshold=0.65), max_per_img=100))\n\nimg_scales = [(640, 640), (320, 320), (960, 960)]\ntta_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[\n            [\n                dict(type='Resize', scale=s, keep_ratio=True)\n                for s in img_scales\n            ],\n            [\n                # ``RandomFlip`` must be placed before ``Pad``, otherwise\n                # bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type='RandomFlip', prob=1.),\n                dict(type='RandomFlip', prob=0.)\n            ],\n            [\n                dict(\n                    type='Pad',\n                    pad_to_square=True,\n                    pad_val=dict(img=(114.0, 114.0, 114.0))),\n            ],\n            [dict(type='LoadAnnotations', with_bbox=True)],\n            [\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'scale_factor', 'flip', 'flip_direction'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "configs/yolox/yolox_x_8xb8-300e_coco.py",
    "content": "_base_ = './yolox_s_8xb8-300e_coco.py'\n\n# model settings\nmodel = dict(\n    backbone=dict(deepen_factor=1.33, widen_factor=1.25),\n    neck=dict(\n        in_channels=[320, 640, 1280], out_channels=320, num_csp_blocks=4),\n    bbox_head=dict(in_channels=320, feat_channels=320))\n"
  },
  {
    "path": "dataset-index.yml",
    "content": "openxlab: true\nvoc2007:\n  dataset: OpenDataLab/PASCAL_VOC2007\n  download_root: data\n  data_root: data\n  script: tools/dataset_converters/scripts/preprocess_voc2007.sh\n\nvoc2012:\n  dataset: OpenDataLab/PASCAL_VOC2012\n  download_root: data\n  data_root: data\n  script: tools/dataset_converters/scripts/preprocess_voc2012.sh\n\ncoco2017:\n  dataset: OpenDataLab/COCO_2017\n  download_root: data\n  data_root: data/coco\n  script: tools/dataset_converters/scripts/preprocess_coco2017.sh\n"
  },
  {
    "path": "demo/MMDet_InstanceSeg_Tutorial.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"aGYwt_UjIrqp\"\n   },\n   \"source\": [\n    \"# Instance Segmentation\\n\",\n    \"\\n\",\n    \"In this tutorial, you will learn:\\n\",\n    \"- the basic structure of Mask R-CNN.\\n\",\n    \"- to perform inference with a MMDetection detector.\\n\",\n    \"- to train a new instance segmentation model with a new dataset.\\n\",\n    \"\\n\",\n    \"Let's start!\\n\",\n    \"\\n\",\n    \"<a href=\\\"https://colab.research.google.com/drive/11MqMCyF_V7Rkw6b9CCShGfNTP59OJ905?usp=sharing\\\"><img src=\\\"https://colab.research.google.com/assets/colab-badge.svg\\\" alt=\\\"Open In Colab\\\"></a>\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"cCk6uTQrdUUn\"\n   },\n   \"source\": [\n    \"If you are running the tutorial files on the colab platform or a new virtual environment, please run the following code first to configure the runtime environment.\\n\",\n    \"```python\\n\",\n    \"!pip install -U openmim\\n\",\n    \"!mim install \\\"mmengine>=0.7.0\\\"\\n\",\n    \"!mim install \\\"mmcv>=2.0.0rc4\\\"\\n\",\n    \"\\n\",\n    \"# Install mmdetection\\n\",\n    \"!rm -rf mmdetection\\n\",\n    \"!git clone https://github.com/open-mmlab/mmdetection.git\\n\",\n    \"%cd mmdetection\\n\",\n    \"\\n\",\n    \"!pip install -e .\\n\",\n    \"```\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"6hD0mmMixT0p\",\n    \"outputId\": \"221dad3c-5ef8-4094-e07e-289f333f7bb9\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"torch version: 2.0.1+cu118 cuda: True\\n\",\n      \"mmdetection: 3.1.0\\n\",\n      \"mmcv: 2.0.1\\n\",\n      \"mmengine: 0.8.4\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"# Check Pytorch installation\\n\",\n    \"import torch, torchvision\\n\",\n    \"print(\\\"torch version:\\\",torch.__version__, \\\"cuda:\\\",torch.cuda.is_available())\\n\",\n    \"\\n\",\n    \"# Check MMDetection installation\\n\",\n    \"import mmdet\\n\",\n    \"print(\\\"mmdetection:\\\",mmdet.__version__)\\n\",\n    \"\\n\",\n    \"# Check mmcv installation\\n\",\n    \"import mmcv\\n\",\n    \"print(\\\"mmcv:\\\",mmcv.__version__)\\n\",\n    \"\\n\",\n    \"# Check mmengine installation\\n\",\n    \"import mmengine\\n\",\n    \"print(\\\"mmengine:\\\",mmengine.__version__)\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"gi9zw03oM4CH\"\n   },\n   \"source\": [\n    \"## Perform Inference with An MMDetection Detector\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"3pFYLerc0we1\"\n   },\n   \"source\": [\n    \"### A two-stage detector\\n\",\n    \"\\n\",\n    \"In this tutorial, we use Mask R-CNN, a simple two-stage detector as an example.\\n\",\n    \"\\n\",\n    \"The high-level architecture of Mask R-CNN is shown in the following picture. More details can be found in the [paper](https://arxiv.org/abs/1703.06870).\\n\",\n    \"\\n\",\n    \"<img src=\\\"https://user-images.githubusercontent.com/40661020/143967081-c2552bed-9af2-46c4-ae44-5b3b74e5679f.png\\\" alt=\\\"mask rcnn\\\" align=\\\"bottom\\\" />\\n\",\n    \"\\n\",\n    \"Mask R-CNN adds a mask branch based on the original Faster R-CNN. It also uses RoIAlign, a more precise version of RoIPooling for RoI feature extraction to improve the performance.\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"sWI-nX5yRYYQ\",\n    \"outputId\": \"fd91e337-27cb-492c-a948-98adcbcfca27\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"processing mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco...\\n\",\n      \"\\u001b[2Kdownloading \\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[32m169.6/169.6 MiB\\u001b[0m \\u001b[31m9.4 MB/s\\u001b[0m eta \\u001b[36m0:00:00\\u001b[0m\\n\",\n      \"\\u001b[?25h\\u001b[32mSuccessfully downloaded mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth to /content/mmdetection/checkpoints\\u001b[0m\\n\",\n      \"\\u001b[32mSuccessfully dumped mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco.py to /content/mmdetection/checkpoints\\u001b[0m\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"!mim download mmdet --config mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco --dest ./checkpoints\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"8M5KUnX7Np3h\",\n    \"outputId\": \"71de79c0-9f7e-4cae-f810-5c0a20fe9be8\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Loads checkpoint by local backend from path: checkpoints/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import mmcv\\n\",\n    \"import mmengine\\n\",\n    \"from mmdet.apis import init_detector, inference_detector\\n\",\n    \"from mmdet.utils import register_all_modules\\n\",\n    \"# Choose to use a config and initialize the detector\\n\",\n    \"config_file = 'configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-3x_coco.py'\\n\",\n    \"# Setup a checkpoint file to load\\n\",\n    \"checkpoint_file = 'checkpoints/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'\\n\",\n    \"\\n\",\n    \"# register all modules in mmdet into the registries\\n\",\n    \"register_all_modules()\\n\",\n    \"\\n\",\n    \"# build the model from a config file and a checkpoint file\\n\",\n    \"model = init_detector(config_file, checkpoint_file, device='cuda:0')  # or device='cuda:0'\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"pVqDQAOiKkJK\"\n   },\n   \"source\": [\n    \"From the printed model, we will find that the model does consist of the components that we described earlier. It uses ResNet as its CNN backbone, and has a RPN head and RoI Head.\\n\",\n    \"The RoI Head includes box head and mask head. In addition, the model has a neural network module, named neck, directly after the CNN backbone. It is a [feature pyramid network (FPN)](https://arxiv.org/abs/1612.03144) for enhancing the multi-scale features.\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"### Inference with the detector\\n\",\n    \"\\n\",\n    \"The model is successfully created and loaded, let's see how good it is. We use the high-level API `inference_detector` implemented in the MMDetection. This API is created to ease the inference process. The details of the codes can be found [here](https://github.com/open-mmlab/mmdetection/blob/master/mmdet/apis/inference.py#L15).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"Wi6DRpsQPEmV\",\n    \"outputId\": \"42a9dd39-edcb-49f1-e318-a3cd77f89eee\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"<DetDataSample(\\n\",\n      \"\\n\",\n      \"    META INFORMATION\\n\",\n      \"    batch_input_shape: (800, 1216)\\n\",\n      \"    pad_shape: (800, 1216)\\n\",\n      \"    scale_factor: (1.8734375, 1.873536299765808)\\n\",\n      \"    img_id: 0\\n\",\n      \"    ori_shape: (427, 640)\\n\",\n      \"    img_shape: (800, 1199)\\n\",\n      \"    img_path: None\\n\",\n      \"\\n\",\n      \"    DATA FIELDS\\n\",\n      \"    gt_instances: <InstanceData(\\n\",\n      \"        \\n\",\n      \"            META INFORMATION\\n\",\n      \"        \\n\",\n      \"            DATA FIELDS\\n\",\n      \"            labels: tensor([], device='cuda:0', dtype=torch.int64)\\n\",\n      \"            bboxes: tensor([], device='cuda:0', size=(0, 4))\\n\",\n      \"            masks: BitmapMasks(num_masks=0, height=427, width=640)\\n\",\n      \"        ) at 0x79a3a9cfa0b0>\\n\",\n      \"    ignored_instances: <InstanceData(\\n\",\n      \"        \\n\",\n      \"            META INFORMATION\\n\",\n      \"        \\n\",\n      \"            DATA FIELDS\\n\",\n      \"            labels: tensor([], device='cuda:0', dtype=torch.int64)\\n\",\n      \"            bboxes: tensor([], device='cuda:0', size=(0, 4))\\n\",\n      \"            masks: BitmapMasks(num_masks=0, height=427, width=640)\\n\",\n      \"        ) at 0x79a3a9cfa0e0>\\n\",\n      \"    pred_instances: <InstanceData(\\n\",\n      \"        \\n\",\n      \"            META INFORMATION\\n\",\n      \"        \\n\",\n      \"            DATA FIELDS\\n\",\n      \"            labels: tensor([13,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 13, 56,  2,  2,\\n\",\n      \"                         2,  0,  0,  2,  2,  2,  0,  2,  2,  2,  0,  2,  0,  2,  2,  2,  2,  2,\\n\",\n      \"                         0,  7,  7,  7,  2,  7,  7, 25,  2,  0,  2,  7,  7,  2,  2,  7],\\n\",\n      \"                       device='cuda:0')\\n\",\n      \"            bboxes: tensor([[219.8702, 177.0791, 458.3700, 382.1367],\\n\",\n      \"                        [480.5608, 110.4768, 523.1904, 130.3331],\\n\",\n      \"                        [167.2974, 108.4704, 219.3721, 139.7081],\\n\",\n      \"                        [432.7696, 104.8358, 482.8209, 132.2976],\\n\",\n      \"                        [293.9358, 116.1538, 381.3969, 150.1965],\\n\",\n      \"                        [189.7066, 107.6951, 296.3842, 154.8887],\\n\",\n      \"                        [608.6334, 111.2579, 635.6981, 136.8759],\\n\",\n      \"                        [  0.0000, 112.1323,  64.1121, 145.1977],\\n\",\n      \"                        [396.6710, 110.8235, 434.0617, 133.4602],\\n\",\n      \"                        [558.0748, 109.3840, 593.6632, 127.6792],\\n\",\n      \"                        [590.7142, 109.8773, 620.7469, 126.5424],\\n\",\n      \"                        [269.1886, 105.4429, 327.0177, 127.3188],\\n\",\n      \"                        [ 85.7339,  89.2483,  99.3820,  98.0990],\\n\",\n      \"                        [ 98.4353,  89.3797, 118.6270, 100.8068],\\n\",\n      \"                        [372.5294, 136.1234, 432.7725, 187.9528],\\n\",\n      \"                        [377.2267, 135.4557, 432.1877, 187.9861],\\n\",\n      \"                        [553.8447, 110.4809, 562.0096, 126.6809],\\n\",\n      \"                        [604.7581, 109.7503, 630.5582, 127.8616],\\n\",\n      \"                        [186.7537, 109.0132, 228.5729, 145.0153],\\n\",\n      \"                        [535.8760, 108.3876, 545.3403, 124.3354],\\n\",\n      \"                        [254.0453, 103.2393, 263.8707, 113.5147],\\n\",\n      \"                        [177.0499, 105.5779, 257.9825, 144.5236],\\n\",\n      \"                        [375.6868, 119.4991, 382.5288, 132.6492],\\n\",\n      \"                        [609.1486, 113.8924, 624.6574, 125.1476],\\n\",\n      \"                        [375.7215, 119.5242, 382.4901, 133.1054],\\n\",\n      \"                        [222.0856,  98.0710, 251.4666, 107.5862],\\n\",\n      \"                        [100.2685,  88.5259, 117.7641,  95.0929],\\n\",\n      \"                        [614.0423, 102.3213, 634.2089, 112.2706],\\n\",\n      \"                        [532.6091, 108.7414, 541.8567, 125.0906],\\n\",\n      \"                        [541.7024, 111.1429, 561.5145, 126.8495],\\n\",\n      \"                        [533.5267, 108.6170, 545.8909, 136.2182],\\n\",\n      \"                        [135.4252,  89.6805, 152.0995,  96.3841],\\n\",\n      \"                        [ 98.6447,  92.1244, 110.0710, 100.7798],\\n\",\n      \"                        [168.5464,  96.7550, 182.6953, 105.4320],\\n\",\n      \"                        [373.9429, 112.1656, 415.0832, 133.9632],\\n\",\n      \"                        [555.3735, 110.0341, 574.9896, 127.4099],\\n\",\n      \"                        [227.8013, 115.6852, 237.1723, 123.8744],\\n\",\n      \"                        [430.8856, 104.9880, 484.4790, 131.9029],\\n\",\n      \"                        [294.1423, 116.5301, 378.7433, 149.9078],\\n\",\n      \"                        [  7.9151, 101.6727, 121.0091, 142.4919],\\n\",\n      \"                        [364.8131, 108.6062, 437.1151, 133.0383],\\n\",\n      \"                        [392.4191, 111.1939, 434.2920, 133.6734],\\n\",\n      \"                        [187.0622, 108.6223, 300.6056, 155.1926],\\n\",\n      \"                        [ 13.0601,  93.1385,  25.3213, 126.1826],\\n\",\n      \"                        [ 80.9476,  89.6317,  92.6563,  97.9697],\\n\",\n      \"                        [241.6709, 116.3163, 252.0889, 125.3810],\\n\",\n      \"                        [598.0154, 103.6475, 634.9665, 124.7287],\\n\",\n      \"                        [ 82.1116,  89.2348,  99.1320,  97.9058],\\n\",\n      \"                        [ 98.8386,  89.3510, 119.2133, 101.0519],\\n\",\n      \"                        [  9.4168, 106.1699, 107.8712, 144.0217],\\n\",\n      \"                        [134.5102,  90.4127, 152.7260, 100.8205],\\n\",\n      \"                        [ 43.4677, 105.9956, 114.2341, 128.5489]], device='cuda:0')\\n\",\n      \"            masks: tensor([[[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        ...,\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]]], device='cuda:0')\\n\",\n      \"            scores: tensor([0.9990, 0.9938, 0.9878, 0.9854, 0.9836, 0.9829, 0.9781, 0.9779, 0.9731,\\n\",\n      \"                        0.9605, 0.9476, 0.9367, 0.6866, 0.6654, 0.6534, 0.5688, 0.4980, 0.3680,\\n\",\n      \"                        0.3513, 0.2965, 0.2664, 0.2557, 0.2344, 0.2336, 0.2222, 0.2082, 0.2000,\\n\",\n      \"                        0.1945, 0.1809, 0.1675, 0.1653, 0.1504, 0.1208, 0.1143, 0.1067, 0.1060,\\n\",\n      \"                        0.0810, 0.0800, 0.0776, 0.0751, 0.0732, 0.0700, 0.0686, 0.0653, 0.0638,\\n\",\n      \"                        0.0591, 0.0583, 0.0573, 0.0539, 0.0538, 0.0528, 0.0516],\\n\",\n      \"                       device='cuda:0')\\n\",\n      \"        ) at 0x79a3a9cfb550>\\n\",\n      \") at 0x79a3c999fc10>\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"# Use the detector to do inference\\n\",\n    \"image = mmcv.imread('demo/demo.jpg',channel_order='rgb')\\n\",\n    \"result = inference_detector(model, image)\\n\",\n    \"print(result)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"4pFVhKeQRYYS\"\n   },\n   \"source\": [\n    \"### Let's plot the result\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"YinmJV1dRYYT\",\n    \"outputId\": \"e6c9059f-55b3-481b-edef-b21befcbcf2e\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"/usr/local/lib/python3.10/dist-packages/mmengine/visualization/visualizer.py:196: UserWarning: Failed to add <class 'mmengine.visualization.vis_backend.LocalVisBackend'>, please provide the `save_dir` argument.\\n\",\n      \"  warnings.warn(f'Failed to add {vis_backend.__class__}, '\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from mmdet.registry import VISUALIZERS\\n\",\n    \"# init visualizer(run the block only once in jupyter notebook)\\n\",\n    \"visualizer = VISUALIZERS.build(model.cfg.visualizer)\\n\",\n    \"# the dataset_meta is loaded from the checkpoint and\\n\",\n    \"# then pass to the model in init_detector\\n\",\n    \"visualizer.dataset_meta = model.dataset_meta\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 7,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\",\n     \"height\": 464\n    },\n    \"id\": \"z6qT6pG1RYYT\",\n    \"outputId\": \"089b652b-061f-480d-f9de-ffa06b7d385a\"\n   },\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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q6++wne/+z323ntvVq5cievGrhnJZIJisRxnt7QYkRYC/CAkjJgU4O6angZoa2ujt7cXpRQ9PT1s2xYHlLG/tV579uqBczxYc7kcf/rTn8jn83R3d3PFFVfw17/+lUWLFvHBDx7PD374Q1zHJZlMMXNmDzuvQf+qve+A0vVj3kfV9WJF7r9ijk4JGkOE8OPdazSBRNY5lMcc/UE+/vFTCcKAe+/5K48++igf/ODRHH98rCyaN28ev/jFz3nmmWcbnTJr1gzOP//8hqXNxRdfzMjIMFL2kkzGSryqU8I0NQJfUiq6fPr0kzjuhP14+vF3KJZ3cMgH9kUa7RTLHmYiw6lfWsCmt/L85aZ/kExZfPq042mePsyyp2dzxx8m4OXOPVro3tfkoI8sZGRsnGoJypXY7xERIyKu40AkiHw/VmULnVALiMIA3QLHd5BYVDwH29BJJHQKbgkV1wxAjyRWwkATsepPavECZBgGUkqSySSO4+AGcSpYKIiiMpomKRaLRKZNEAowFIYeYZsa6ebplMoO0tcYGagyMtpPNl1gW98YTa0ZnJKDUgojbcQTp4q96aKYh4Ch23hOMabehRpSajHHR4Xouo20JH++9R/kx0+ipzvH9HktDIwWqQoPU9fxRYiyNEpBlcj10XULHx8vdAl8EHVTeh0MFH4ULzi6YcaorKdQmkDqEEQhbuQSYjfuS11+0t09jTVr3qm9FidxjzrqaI499liEEHR2dpJMpDAMg61bt9LT04Oq2ygArhdiWxpKwejoGB/60L9hmibnnnsumUyGKIrYvn07f//7gySTCa699lreeustmpqaGB8f59e//jXNzc1cdtlljbT4ZPuab/7nNzju2GP52/33M62rk7HREb7xn/9Jb28fv7nictatXc9rr71OS0uOXDbLeH58yqOVLxTI54v0Dwxz9NGLefzx5xtQvtAFaHpsOO164Pu18SEII4VmaGimQVBfzIVE0zVMXSMIPVQUB3RC1hceP06TGyamGdudmMk0YajYPlziinse4K7nX+aTRx7G0fvFu11N08AOufDCxVN2+0OjJS659HdTvsuNt97FjbfeVb+BIOD6m/5CHKjE5y694spdppdb/nI3t/zlbmKysQAl+Ou9j/HXex+DRr3tGGle+ss/YtkWba1JHvvHw3F6vTPXeK+kYWLq2k7ZkKlzWzJpkErp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\",\n      \"text/plain\": [\n       \"<Figure size 640x480 with 1 Axes>\"\n      ]\n     },\n     \"execution_count\": 7,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"# show the results\\n\",\n    \"visualizer.add_datasample(\\n\",\n    \"    'result',\\n\",\n    \"    image,\\n\",\n    \"    data_sample=result,\\n\",\n    \"    draw_gt = None,\\n\",\n    \"    wait_time=0,\\n\",\n    \")\\n\",\n    \"visualizer.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"7GrWIJywLV-V\"\n   },\n   \"source\": [\n    \"## Train a Detector on A Customized Dataset\\n\",\n    \"\\n\",\n    \"To train a new detector, there are usually three things to do:\\n\",\n    \"1. Support a new dataset\\n\",\n    \"2. Modify the config\\n\",\n    \"3. Train a new detector\\n\",\n    \"\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"E73y5Lru-wBx\"\n   },\n   \"source\": [\n    \"### Support a new dataset\\n\",\n    \"\\n\",\n    \"There are three ways to support a new dataset in MMDetection:\\n\",\n    \"  1. Reorganize the dataset into a COCO format\\n\",\n    \"  2. Reorganize the dataset into a middle format\\n\",\n    \"  3. Implement a new dataset\\n\",\n    \"\\n\",\n    \"We recommend the first two methods, as they are usually easier than the third.\\n\",\n    \"\\n\",\n    \"In this tutorial, we give an example that converts the data into COCO format because MMDetection **only support evaluating mask AP of dataset in COCO format for now**. Other methods and more advanced usages can be found in the [doc](https://mmdetection.readthedocs.io/en/latest/advanced_guides/customize_dataset.html).\\n\",\n    \"\\n\",\n    \"First, let's download the [the balloon dataset](https://github.com/matterport/Mask_RCNN/tree/master/samples/balloon).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"id\": \"rHnw5Q_nARXq\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# download and unzip the data\\n\",\n    \"!wget -c https://github.com/matterport/Mask_RCNN/releases/download/v2.1/balloon_dataset.zip\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"id\": \"ucSfn1U_RYYW\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"!unzip balloon_dataset.zip -d ./ballondatasets/\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"Ro6JhfBVRYYX\"\n   },\n   \"source\": [\n    \"# Check the directory structure of the tiny data\\n\",\n    \"\\n\",\n    \"# Install tree first in your terminal(linux)\\n\",\n    \"sudo apt-get -q install tree\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 10,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"Wuwxw1oZRtVZ\",\n    \"outputId\": \"2e472cfa-2e2f-41ea-ddec-5a9d49fe71cf\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"/bin/bash: line 1: tree: command not found\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"!tree ballondatasets\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 11,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\",\n     \"height\": 836\n    },\n    \"id\": \"YnQQqzOWzE91\",\n    \"outputId\": \"ff7d3804-638c-461f-aef6-8c496a4b69c8\"\n   },\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\",\n      \"text/plain\": [\n       \"<Figure size 1500x1000 with 1 Axes>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"# Let's take a look at the dataset image\\n\",\n    \"import mmcv\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"\\n\",\n    \"img = mmcv.imread('ballondatasets/balloon/train/10464445726_6f1e3bbe6a_k.jpg')\\n\",\n    \"plt.figure(figsize=(15, 10))\\n\",\n    \"plt.imshow(mmcv.bgr2rgb(img))\\n\",\n    \"plt.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"PMZvtSIl71qi\"\n   },\n   \"source\": [\n    \"After downloading the data, we need to implement a function to convert the annotation format into the COCO format. Then we can use implemented `COCODataset` to load the data and perform training and evaluation.\\n\",\n    \"Let's take a look at the annotation json file.\\n\",\n    \"\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 12,\n   \"metadata\": {\n    \"id\": \"n7rwalnPd6e1\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Check the label of a single image\\n\",\n    \"import mmengine\\n\",\n    \"\\n\",\n    \"annotation = mmengine.load('./ballondatasets/balloon/train/via_region_data.json')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 13,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"keLW7uqJM54Y\",\n    \"outputId\": \"8bdf087e-5ec0-4f8a-ee1d-5692986ac87d\"\n   },\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"{'fileref': '',\\n\",\n       \" 'size': 1115004,\\n\",\n       \" 'filename': '34020010494_e5cb88e1c4_k.jpg',\\n\",\n       \" 'base64_img_data': '',\\n\",\n       \" 'file_attributes': {},\\n\",\n       \" 'regions': {'0': {'shape_attributes': {'name': 'polygon',\\n\",\n       \"    'all_points_x': [1020,\\n\",\n       \"     1000,\\n\",\n       \"     994,\\n\",\n       \"     1003,\\n\",\n       \"     1023,\\n\",\n       \"     1050,\\n\",\n       \"     1089,\\n\",\n       \"     1134,\\n\",\n       \"     1190,\\n\",\n       \"     1265,\\n\",\n       \"     1321,\\n\",\n       \"     1361,\\n\",\n       \"     1403,\\n\",\n       \"     1428,\\n\",\n       \"     1442,\\n\",\n       \"     1445,\\n\",\n       \"     1441,\\n\",\n       \"     1427,\\n\",\n       \"     1400,\\n\",\n       \"     1361,\\n\",\n       \"     1316,\\n\",\n       \"     1269,\\n\",\n       \"     1228,\\n\",\n       \"     1198,\\n\",\n       \"     1207,\\n\",\n       \"     1210,\\n\",\n       \"     1190,\\n\",\n       \"     1177,\\n\",\n       \"     1172,\\n\",\n       \"     1174,\\n\",\n       \"     1170,\\n\",\n       \"     1153,\\n\",\n       \"     1127,\\n\",\n       \"     1104,\\n\",\n       \"     1061,\\n\",\n       \"     1032,\\n\",\n       \"     1020],\\n\",\n       \"    'all_points_y': [963,\\n\",\n       \"     899,\\n\",\n       \"     841,\\n\",\n       \"     787,\\n\",\n       \"     738,\\n\",\n       \"     700,\\n\",\n       \"     663,\\n\",\n       \"     638,\\n\",\n       \"     621,\\n\",\n       \"     619,\\n\",\n       \"     643,\\n\",\n       \"     672,\\n\",\n       \"     720,\\n\",\n       \"     765,\\n\",\n       \"     800,\\n\",\n       \"     860,\\n\",\n       \"     896,\\n\",\n       \"     942,\\n\",\n       \"     990,\\n\",\n       \"     1035,\\n\",\n       \"     1079,\\n\",\n       \"     1112,\\n\",\n       \"     1129,\\n\",\n       \"     1134,\\n\",\n       \"     1144,\\n\",\n       \"     1153,\\n\",\n       \"     1166,\\n\",\n       \"     1166,\\n\",\n       \"     1150,\\n\",\n       \"     1136,\\n\",\n       \"     1129,\\n\",\n       \"     1122,\\n\",\n       \"     1112,\\n\",\n       \"     1084,\\n\",\n       \"     1037,\\n\",\n       \"     989,\\n\",\n       \"     963]},\\n\",\n       \"   'region_attributes': {}}}}\"\n      ]\n     },\n     \"execution_count\": 13,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"# The annotation is a dict, and its values looks like the following\\n\",\n    \"annotation['34020010494_e5cb88e1c4_k.jpg1115004']\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"QA1pFg-FeO3l\"\n   },\n   \"source\": [\n    \"According to the above observation, each single image has a corresponding annotation containing keys `filename` and `regions` that are necessary for training.\\n\",\n    \"We need to read annotations of each image and convert them into COCO format as below:\\n\",\n    \"\\n\",\n    \"```python\\n\",\n    \"{\\n\",\n    \"    \\\"images\\\": [image],\\n\",\n    \"    \\\"annotations\\\": [annotation],\\n\",\n    \"    \\\"categories\\\": [category]\\n\",\n    \"}\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"image = {\\n\",\n    \"    \\\"id\\\": int,\\n\",\n    \"    \\\"width\\\": int,\\n\",\n    \"    \\\"height\\\": int,\\n\",\n    \"    \\\"file_name\\\": str,\\n\",\n    \"}\\n\",\n    \"\\n\",\n    \"annotation = {\\n\",\n    \"    \\\"id\\\": int,\\n\",\n    \"    \\\"image_id\\\": int,\\n\",\n    \"    \\\"category_id\\\": int,\\n\",\n    \"    \\\"segmentation\\\": RLE or [polygon],\\n\",\n    \"    \\\"area\\\": float,\\n\",\n    \"    \\\"bbox\\\": [x,y,width,height],\\n\",\n    \"    \\\"iscrowd\\\": 0 or 1,\\n\",\n    \"}\\n\",\n    \"\\n\",\n    \"categories = [{\\n\",\n    \"    \\\"id\\\": int,\\n\",\n    \"    \\\"name\\\": str,\\n\",\n    \"    \\\"supercategory\\\": str,\\n\",\n    \"}]\\n\",\n    \"```\\n\",\n    \"**Note**: We only list the necessary keys for training, as shown above. For a full COCO format, please see [here](https://cocodataset.org/#format-data).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 14,\n   \"metadata\": {\n    \"id\": \"GdSaB2ad0EdX\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"import os.path as osp\\n\",\n    \"\\n\",\n    \"def convert_balloon_to_coco(ann_file, out_file, image_prefix):\\n\",\n    \"    data_infos = mmengine.load(ann_file)\\n\",\n    \"\\n\",\n    \"    annotations = []\\n\",\n    \"    images = []\\n\",\n    \"    obj_count = 0\\n\",\n    \"    for idx, v in enumerate(mmengine.track_iter_progress(list(data_infos.values()))):\\n\",\n    \"        filename = v['filename']\\n\",\n    \"        img_path = osp.join(image_prefix, filename)\\n\",\n    \"        height, width = mmcv.imread(img_path).shape[:2]\\n\",\n    \"\\n\",\n    \"        images.append(dict(\\n\",\n    \"            id=idx,\\n\",\n    \"            file_name=filename,\\n\",\n    \"            height=height,\\n\",\n    \"            width=width))\\n\",\n    \"\\n\",\n    \"        bboxes = []\\n\",\n    \"        labels = []\\n\",\n    \"        masks = []\\n\",\n    \"        for _, obj in v['regions'].items():\\n\",\n    \"            assert not obj['region_attributes']\\n\",\n    \"            obj = obj['shape_attributes']\\n\",\n    \"            px = obj['all_points_x']\\n\",\n    \"            py = obj['all_points_y']\\n\",\n    \"            poly = [(x + 0.5, y + 0.5) for x, y in zip(px, py)]\\n\",\n    \"            poly = [p for x in poly for p in x]\\n\",\n    \"\\n\",\n    \"            x_min, y_min, x_max, y_max = (\\n\",\n    \"                min(px), min(py), max(px), max(py))\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"            data_anno = dict(\\n\",\n    \"                image_id=idx,\\n\",\n    \"                id=obj_count,\\n\",\n    \"                category_id=0,\\n\",\n    \"                bbox=[x_min, y_min, x_max - x_min, y_max - y_min],\\n\",\n    \"                area=(x_max - x_min) * (y_max - y_min),\\n\",\n    \"                segmentation=[poly],\\n\",\n    \"                iscrowd=0)\\n\",\n    \"            annotations.append(data_anno)\\n\",\n    \"            obj_count += 1\\n\",\n    \"\\n\",\n    \"    coco_format_json = dict(\\n\",\n    \"        images=images,\\n\",\n    \"        annotations=annotations,\\n\",\n    \"        categories=[{'id':0, 'name': 'balloon'}])\\n\",\n    \"    mmengine.dump(coco_format_json, out_file)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 15,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"G3xV5ktqlpFu\",\n    \"outputId\": \"2d97137b-34e6-42e5-c8d6-0a4fe7d2c7cf\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 61/61, 19.7 task/s, elapsed: 3s, ETA:     0s\\n\",\n      \"[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 13/13, 20.1 task/s, elapsed: 1s, ETA:     0s\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"convert_balloon_to_coco(\\n\",\n    \"    './ballondatasets/balloon/train/via_region_data.json',\\n\",\n    \"    './ballondatasets/balloon/train/annotation_coco.json',\\n\",\n    \"    './ballondatasets/balloon/train/')\\n\",\n    \"convert_balloon_to_coco(\\n\",\n    \"    './ballondatasets/balloon/val/via_region_data.json',\\n\",\n    \"    './ballondatasets/balloon/val/annotation_coco.json',\\n\",\n    \"    './ballondatasets/balloon/val/')\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"h85AtunjRvx4\"\n   },\n   \"source\": [\n    \"Checking the label corresponding to the instance split ID after the data format conversion is complete\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 16,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"zaYkWbxORwZq\",\n    \"outputId\": \"02ad1ff6-f138-49af-b733-1d23c51557f5\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"loading annotations into memory...\\n\",\n      \"Done (t=0.01s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Category ID: 0, Category Name: balloon\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from pycocotools.coco import COCO\\n\",\n    \"\\n\",\n    \"# Path to load the COCO annotation file\\n\",\n    \"annotation_file = './ballondatasets/balloon/train/annotation_coco.json'\\n\",\n    \"\\n\",\n    \"# Initialise the COCO object\\n\",\n    \"coco = COCO(annotation_file)\\n\",\n    \"\\n\",\n    \"# Get all category tags and corresponding category IDs\\n\",\n    \"categories = coco.loadCats(coco.getCatIds())\\n\",\n    \"category_id_to_name = {cat['id']: cat['name'] for cat in categories}\\n\",\n    \"\\n\",\n    \"# Print all category IDs and corresponding category names\\n\",\n    \"for category_id, category_name in category_id_to_name.items():\\n\",\n    \"    print(f\\\"Category ID: {category_id}, Category Name: {category_name}\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"PwqJOpBe-bMj\"\n   },\n   \"source\": [\n    \"### Modify the config\\n\",\n    \"\\n\",\n    \"In the next step, we need to modify the config for the training.\\n\",\n    \"To accelerate the process, we finetune a detector using a pre-trained detector.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 17,\n   \"metadata\": {\n    \"id\": \"hamZrlnH-YDD\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from mmengine import Config\\n\",\n    \"cfg = Config.fromfile('./configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py')\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"HntziLGq-92Z\"\n   },\n   \"source\": [\n    \"Given a config that trains a Mask R-CNN on COCO dataset, we need to modify some values to use it for training on the balloon dataset.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 18,\n   \"metadata\": {\n    \"id\": \"pUbwD8uV0PR8\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"from mmengine.runner import set_random_seed\\n\",\n    \"\\n\",\n    \"# Modify dataset classes and color\\n\",\n    \"cfg.metainfo = {\\n\",\n    \"    'classes': ('balloon', ),\\n\",\n    \"    'palette': [\\n\",\n    \"        (220, 20, 60),\\n\",\n    \"    ]\\n\",\n    \"}\\n\",\n    \"\\n\",\n    \"# Modify dataset type and path\\n\",\n    \"cfg.data_root = './ballondatasets/balloon'\\n\",\n    \"\\n\",\n    \"cfg.train_dataloader.dataset.ann_file = 'train/annotation_coco.json'\\n\",\n    \"cfg.train_dataloader.dataset.data_root = cfg.data_root\\n\",\n    \"cfg.train_dataloader.dataset.data_prefix.img = 'train/'\\n\",\n    \"cfg.train_dataloader.dataset.metainfo = cfg.metainfo\\n\",\n    \"\\n\",\n    \"cfg.val_dataloader.dataset.ann_file = 'val/annotation_coco.json'\\n\",\n    \"cfg.val_dataloader.dataset.data_root = cfg.data_root\\n\",\n    \"cfg.val_dataloader.dataset.data_prefix.img = 'val/'\\n\",\n    \"cfg.val_dataloader.dataset.metainfo = cfg.metainfo\\n\",\n    \"\\n\",\n    \"cfg.test_dataloader = cfg.val_dataloader\\n\",\n    \"\\n\",\n    \"# Modify metric config\\n\",\n    \"cfg.val_evaluator.ann_file = cfg.data_root+'/'+'val/annotation_coco.json'\\n\",\n    \"cfg.test_evaluator = cfg.val_evaluator\\n\",\n    \"\\n\",\n    \"# Modify num classes of the model in box head and mask head\\n\",\n    \"cfg.model.roi_head.bbox_head.num_classes = 1\\n\",\n    \"cfg.model.roi_head.mask_head.num_classes = 1\\n\",\n    \"\\n\",\n    \"# We can still the pre-trained Mask RCNN model to obtain a higher performance\\n\",\n    \"cfg.load_from = 'checkpoints/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'\\n\",\n    \"\\n\",\n    \"# Set up working dir to save files and logs.\\n\",\n    \"cfg.work_dir = './tutorial_exps'\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"# We can set the evaluation interval to reduce the evaluation times\\n\",\n    \"cfg.train_cfg.val_interval = 3\\n\",\n    \"# We can set the checkpoint saving interval to reduce the storage cost\\n\",\n    \"cfg.default_hooks.checkpoint.interval = 3\\n\",\n    \"\\n\",\n    \"# The original learning rate (LR) is set for 8-GPU training.\\n\",\n    \"# We divide it by 8 since we only use one GPU.\\n\",\n    \"cfg.optim_wrapper.optimizer.lr = 0.02 / 8\\n\",\n    \"cfg.default_hooks.logger.interval = 10\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"# Set seed thus the results are more reproducible\\n\",\n    \"# cfg.seed = 0\\n\",\n    \"set_random_seed(0, deterministic=False)\\n\",\n    \"\\n\",\n    \"# We can also use tensorboard to log the training process\\n\",\n    \"cfg.visualizer.vis_backends.append({\\\"type\\\":'TensorboardVisBackend'})\\n\",\n    \"\\n\",\n    \"#------------------------------------------------------\\n\",\n    \"config=f'./configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon.py'\\n\",\n    \"with open(config, 'w') as f:\\n\",\n    \"    f.write(cfg.pretty_text)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"111W_oZV_3wa\"\n   },\n   \"source\": [\n    \"### Train a new detector\\n\",\n    \"\\n\",\n    \"Finally, lets initialize the dataset and detector, then train a new detector!\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 19,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"JiqDnPdAMGyg\",\n    \"outputId\": \"0de25679-3541-488e-eceb-5b5400f92745\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"08/15 04:31:57 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - \\n\",\n      \"------------------------------------------------------------\\n\",\n      \"System environment:\\n\",\n      \"    sys.platform: linux\\n\",\n      \"    Python: 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0]\\n\",\n      \"    CUDA available: True\\n\",\n      \"    numpy_random_seed: 1186080067\\n\",\n      \"    GPU 0: Tesla T4\\n\",\n      \"    CUDA_HOME: /usr/local/cuda\\n\",\n      \"    NVCC: Cuda compilation tools, release 11.8, V11.8.89\\n\",\n      \"    GCC: x86_64-linux-gnu-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0\\n\",\n      \"    PyTorch: 2.0.1+cu118\\n\",\n      \"    PyTorch compiling details: PyTorch built with:\\n\",\n      \"  - GCC 9.3\\n\",\n      \"  - C++ Version: 201703\\n\",\n      \"  - Intel(R) oneAPI Math Kernel Library Version 2022.2-Product Build 20220804 for Intel(R) 64 architecture applications\\n\",\n      \"  - Intel(R) MKL-DNN v2.7.3 (Git Hash 6dbeffbae1f23cbbeae17adb7b5b13f1f37c080e)\\n\",\n      \"  - OpenMP 201511 (a.k.a. OpenMP 4.5)\\n\",\n      \"  - LAPACK is enabled (usually provided by MKL)\\n\",\n      \"  - NNPACK is enabled\\n\",\n      \"  - CPU capability usage: AVX2\\n\",\n      \"  - CUDA Runtime 11.8\\n\",\n      \"  - NVCC architecture flags: -gencode;arch=compute_37,code=sm_37;-gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_80,code=sm_80;-gencode;arch=compute_86,code=sm_86;-gencode;arch=compute_90,code=sm_90\\n\",\n      \"  - CuDNN 8.7\\n\",\n      \"  - Magma 2.6.1\\n\",\n      \"  - Build settings: BLAS_INFO=mkl, BUILD_TYPE=Release, CUDA_VERSION=11.8, CUDNN_VERSION=8.7.0, CXX_COMPILER=/opt/rh/devtoolset-9/root/usr/bin/c++, CXX_FLAGS= -D_GLIBCXX_USE_CXX11_ABI=0 -fabi-version=11 -Wno-deprecated -fvisibility-inlines-hidden -DUSE_PTHREADPOOL -DNDEBUG -DUSE_KINETO -DLIBKINETO_NOROCTRACER -DUSE_FBGEMM -DUSE_QNNPACK -DUSE_PYTORCH_QNNPACK -DUSE_XNNPACK -DSYMBOLICATE_MOBILE_DEBUG_HANDLE -O2 -fPIC -Wall -Wextra -Werror=return-type -Werror=non-virtual-dtor -Werror=bool-operation -Wnarrowing -Wno-missing-field-initializers -Wno-type-limits -Wno-array-bounds -Wno-unknown-pragmas -Wunused-local-typedefs -Wno-unused-parameter -Wno-unused-function -Wno-unused-result -Wno-strict-overflow -Wno-strict-aliasing -Wno-error=deprecated-declarations -Wno-stringop-overflow -Wno-psabi -Wno-error=pedantic -Wno-error=redundant-decls -Wno-error=old-style-cast -fdiagnostics-color=always -faligned-new -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-math-errno -fno-trapping-math -Werror=format -Werror=cast-function-type -Wno-stringop-overflow, LAPACK_INFO=mkl, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, TORCH_DISABLE_GPU_ASSERTS=ON, TORCH_VERSION=2.0.1, USE_CUDA=ON, USE_CUDNN=ON, USE_EXCEPTION_PTR=1, USE_GFLAGS=OFF, USE_GLOG=OFF, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=1, USE_NNPACK=ON, USE_OPENMP=ON, USE_ROCM=OFF, \\n\",\n      \"\\n\",\n      \"    TorchVision: 0.15.2+cu118\\n\",\n      \"    OpenCV: 4.8.0\\n\",\n      \"    MMEngine: 0.8.4\\n\",\n      \"\\n\",\n      \"Runtime environment:\\n\",\n      \"    cudnn_benchmark: False\\n\",\n      \"    dist_cfg: {'backend': 'nccl'}\\n\",\n      \"    mp_cfg: {'mp_start_method': 'fork', 'opencv_num_threads': 0}\\n\",\n      \"    seed: 1186080067\\n\",\n      \"    Distributed launcher: none\\n\",\n      \"    Distributed training: False\\n\",\n      \"    GPU number: 1\\n\",\n      \"------------------------------------------------------------\\n\",\n      \"\\n\",\n      \"08/15 04:31:57 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Config:\\n\",\n      \"auto_scale_lr = dict(base_batch_size=16, enable=False)\\n\",\n      \"backend_args = None\\n\",\n      \"data_root = './ballondatasets/balloon'\\n\",\n      \"dataset_type = 'CocoDataset'\\n\",\n      \"default_hooks = dict(\\n\",\n      \"    checkpoint=dict(interval=3, type='CheckpointHook'),\\n\",\n      \"    logger=dict(interval=10, type='LoggerHook'),\\n\",\n      \"    param_scheduler=dict(type='ParamSchedulerHook'),\\n\",\n      \"    sampler_seed=dict(type='DistSamplerSeedHook'),\\n\",\n      \"    timer=dict(type='IterTimerHook'),\\n\",\n      \"    visualization=dict(type='DetVisualizationHook'))\\n\",\n      \"default_scope = 'mmdet'\\n\",\n      \"env_cfg = dict(\\n\",\n      \"    cudnn_benchmark=False,\\n\",\n      \"    dist_cfg=dict(backend='nccl'),\\n\",\n      \"    mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0))\\n\",\n      \"launcher = 'none'\\n\",\n      \"load_from = 'checkpoints/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'\\n\",\n      \"log_level = 'INFO'\\n\",\n      \"log_processor = dict(by_epoch=True, type='LogProcessor', window_size=50)\\n\",\n      \"metainfo = dict(\\n\",\n      \"    classes=('balloon', ), palette=[\\n\",\n      \"        (\\n\",\n      \"            220,\\n\",\n      \"            20,\\n\",\n      \"            60,\\n\",\n      \"        ),\\n\",\n      \"    ])\\n\",\n      \"model = dict(\\n\",\n      \"    backbone=dict(\\n\",\n      \"        depth=50,\\n\",\n      \"        frozen_stages=1,\\n\",\n      \"        init_cfg=dict(\\n\",\n      \"            checkpoint='open-mmlab://detectron2/resnet50_caffe',\\n\",\n      \"            type='Pretrained'),\\n\",\n      \"        norm_cfg=dict(requires_grad=False, type='BN'),\\n\",\n      \"        norm_eval=True,\\n\",\n      \"        num_stages=4,\\n\",\n      \"        out_indices=(\\n\",\n      \"            0,\\n\",\n      \"            1,\\n\",\n      \"            2,\\n\",\n      \"            3,\\n\",\n      \"        ),\\n\",\n      \"        style='caffe',\\n\",\n      \"        type='ResNet'),\\n\",\n      \"    data_preprocessor=dict(\\n\",\n      \"        bgr_to_rgb=False,\\n\",\n      \"        mean=[\\n\",\n      \"            103.53,\\n\",\n      \"            116.28,\\n\",\n      \"            123.675,\\n\",\n      \"        ],\\n\",\n      \"        pad_mask=True,\\n\",\n      \"        pad_size_divisor=32,\\n\",\n      \"        std=[\\n\",\n      \"            1.0,\\n\",\n      \"            1.0,\\n\",\n      \"            1.0,\\n\",\n      \"        ],\\n\",\n      \"        type='DetDataPreprocessor'),\\n\",\n      \"    neck=dict(\\n\",\n      \"        in_channels=[\\n\",\n      \"            256,\\n\",\n      \"            512,\\n\",\n      \"            1024,\\n\",\n      \"            2048,\\n\",\n      \"        ],\\n\",\n      \"        num_outs=5,\\n\",\n      \"        out_channels=256,\\n\",\n      \"        type='FPN'),\\n\",\n      \"    roi_head=dict(\\n\",\n      \"        bbox_head=dict(\\n\",\n      \"            bbox_coder=dict(\\n\",\n      \"                target_means=[\\n\",\n      \"                    0.0,\\n\",\n      \"                    0.0,\\n\",\n      \"                    0.0,\\n\",\n      \"                    0.0,\\n\",\n      \"                ],\\n\",\n      \"                target_stds=[\\n\",\n      \"                    0.1,\\n\",\n      \"                    0.1,\\n\",\n      \"                    0.2,\\n\",\n      \"                    0.2,\\n\",\n      \"                ],\\n\",\n      \"                type='DeltaXYWHBBoxCoder'),\\n\",\n      \"            fc_out_channels=1024,\\n\",\n      \"            in_channels=256,\\n\",\n      \"            loss_bbox=dict(loss_weight=1.0, type='L1Loss'),\\n\",\n      \"            loss_cls=dict(\\n\",\n      \"                loss_weight=1.0, type='CrossEntropyLoss', use_sigmoid=False),\\n\",\n      \"            num_classes=1,\\n\",\n      \"            reg_class_agnostic=False,\\n\",\n      \"            roi_feat_size=7,\\n\",\n      \"            type='Shared2FCBBoxHead'),\\n\",\n      \"        bbox_roi_extractor=dict(\\n\",\n      \"            featmap_strides=[\\n\",\n      \"                4,\\n\",\n      \"                8,\\n\",\n      \"                16,\\n\",\n      \"                32,\\n\",\n      \"            ],\\n\",\n      \"            out_channels=256,\\n\",\n      \"            roi_layer=dict(output_size=7, sampling_ratio=0, type='RoIAlign'),\\n\",\n      \"            type='SingleRoIExtractor'),\\n\",\n      \"        mask_head=dict(\\n\",\n      \"            conv_out_channels=256,\\n\",\n      \"            in_channels=256,\\n\",\n      \"            loss_mask=dict(\\n\",\n      \"                loss_weight=1.0, type='CrossEntropyLoss', use_mask=True),\\n\",\n      \"            num_classes=1,\\n\",\n      \"            num_convs=4,\\n\",\n      \"            type='FCNMaskHead'),\\n\",\n      \"        mask_roi_extractor=dict(\\n\",\n      \"            featmap_strides=[\\n\",\n      \"                4,\\n\",\n      \"                8,\\n\",\n      \"                16,\\n\",\n      \"                32,\\n\",\n      \"            ],\\n\",\n      \"            out_channels=256,\\n\",\n      \"            roi_layer=dict(output_size=14, sampling_ratio=0, type='RoIAlign'),\\n\",\n      \"            type='SingleRoIExtractor'),\\n\",\n      \"        type='StandardRoIHead'),\\n\",\n      \"    rpn_head=dict(\\n\",\n      \"        anchor_generator=dict(\\n\",\n      \"            ratios=[\\n\",\n      \"                0.5,\\n\",\n      \"                1.0,\\n\",\n      \"                2.0,\\n\",\n      \"            ],\\n\",\n      \"            scales=[\\n\",\n      \"                8,\\n\",\n      \"            ],\\n\",\n      \"            strides=[\\n\",\n      \"                4,\\n\",\n      \"                8,\\n\",\n      \"                16,\\n\",\n      \"                32,\\n\",\n      \"                64,\\n\",\n      \"            ],\\n\",\n      \"            type='AnchorGenerator'),\\n\",\n      \"        bbox_coder=dict(\\n\",\n      \"            target_means=[\\n\",\n      \"                0.0,\\n\",\n      \"                0.0,\\n\",\n      \"                0.0,\\n\",\n      \"                0.0,\\n\",\n      \"            ],\\n\",\n      \"            target_stds=[\\n\",\n      \"                1.0,\\n\",\n      \"                1.0,\\n\",\n      \"                1.0,\\n\",\n      \"                1.0,\\n\",\n      \"            ],\\n\",\n      \"            type='DeltaXYWHBBoxCoder'),\\n\",\n      \"        feat_channels=256,\\n\",\n      \"        in_channels=256,\\n\",\n      \"        loss_bbox=dict(loss_weight=1.0, type='L1Loss'),\\n\",\n      \"        loss_cls=dict(\\n\",\n      \"            loss_weight=1.0, type='CrossEntropyLoss', use_sigmoid=True),\\n\",\n      \"        type='RPNHead'),\\n\",\n      \"    test_cfg=dict(\\n\",\n      \"        rcnn=dict(\\n\",\n      \"            mask_thr_binary=0.5,\\n\",\n      \"            max_per_img=100,\\n\",\n      \"            nms=dict(iou_threshold=0.5, type='nms'),\\n\",\n      \"            score_thr=0.05),\\n\",\n      \"        rpn=dict(\\n\",\n      \"            max_per_img=1000,\\n\",\n      \"            min_bbox_size=0,\\n\",\n      \"            nms=dict(iou_threshold=0.7, type='nms'),\\n\",\n      \"            nms_pre=1000)),\\n\",\n      \"    train_cfg=dict(\\n\",\n      \"        rcnn=dict(\\n\",\n      \"            assigner=dict(\\n\",\n      \"                ignore_iof_thr=-1,\\n\",\n      \"                match_low_quality=True,\\n\",\n      \"                min_pos_iou=0.5,\\n\",\n      \"                neg_iou_thr=0.5,\\n\",\n      \"                pos_iou_thr=0.5,\\n\",\n      \"                type='MaxIoUAssigner'),\\n\",\n      \"            debug=False,\\n\",\n      \"            mask_size=28,\\n\",\n      \"            pos_weight=-1,\\n\",\n      \"            sampler=dict(\\n\",\n      \"                add_gt_as_proposals=True,\\n\",\n      \"                neg_pos_ub=-1,\\n\",\n      \"                num=512,\\n\",\n      \"                pos_fraction=0.25,\\n\",\n      \"                type='RandomSampler')),\\n\",\n      \"        rpn=dict(\\n\",\n      \"            allowed_border=-1,\\n\",\n      \"            assigner=dict(\\n\",\n      \"                ignore_iof_thr=-1,\\n\",\n      \"                match_low_quality=True,\\n\",\n      \"                min_pos_iou=0.3,\\n\",\n      \"                neg_iou_thr=0.3,\\n\",\n      \"                pos_iou_thr=0.7,\\n\",\n      \"                type='MaxIoUAssigner'),\\n\",\n      \"            debug=False,\\n\",\n      \"            pos_weight=-1,\\n\",\n      \"            sampler=dict(\\n\",\n      \"                add_gt_as_proposals=False,\\n\",\n      \"                neg_pos_ub=-1,\\n\",\n      \"                num=256,\\n\",\n      \"                pos_fraction=0.5,\\n\",\n      \"                type='RandomSampler')),\\n\",\n      \"        rpn_proposal=dict(\\n\",\n      \"            max_per_img=1000,\\n\",\n      \"            min_bbox_size=0,\\n\",\n      \"            nms=dict(iou_threshold=0.7, type='nms'),\\n\",\n      \"            nms_pre=2000)),\\n\",\n      \"    type='MaskRCNN')\\n\",\n      \"optim_wrapper = dict(\\n\",\n      \"    optimizer=dict(lr=0.0025, momentum=0.9, type='SGD', weight_decay=0.0001),\\n\",\n      \"    type='OptimWrapper')\\n\",\n      \"param_scheduler = [\\n\",\n      \"    dict(\\n\",\n      \"        begin=0, by_epoch=False, end=500, start_factor=0.001, type='LinearLR'),\\n\",\n      \"    dict(\\n\",\n      \"        begin=0,\\n\",\n      \"        by_epoch=True,\\n\",\n      \"        end=12,\\n\",\n      \"        gamma=0.1,\\n\",\n      \"        milestones=[\\n\",\n      \"            8,\\n\",\n      \"            11,\\n\",\n      \"        ],\\n\",\n      \"        type='MultiStepLR'),\\n\",\n      \"]\\n\",\n      \"resume = False\\n\",\n      \"test_cfg = dict(type='TestLoop')\\n\",\n      \"test_dataloader = dict(\\n\",\n      \"    batch_size=1,\\n\",\n      \"    dataset=dict(\\n\",\n      \"        ann_file='val/annotation_coco.json',\\n\",\n      \"        backend_args=None,\\n\",\n      \"        data_prefix=dict(img='val/'),\\n\",\n      \"        data_root='./ballondatasets/balloon',\\n\",\n      \"        metainfo=dict(classes=('balloon', ), palette=[\\n\",\n      \"            (\\n\",\n      \"                220,\\n\",\n      \"                20,\\n\",\n      \"                60,\\n\",\n      \"            ),\\n\",\n      \"        ]),\\n\",\n      \"        pipeline=[\\n\",\n      \"            dict(backend_args=None, type='LoadImageFromFile'),\\n\",\n      \"            dict(keep_ratio=True, scale=(\\n\",\n      \"                1333,\\n\",\n      \"                800,\\n\",\n      \"            ), type='Resize'),\\n\",\n      \"            dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\\n\",\n      \"            dict(\\n\",\n      \"                meta_keys=(\\n\",\n      \"                    'img_id',\\n\",\n      \"                    'img_path',\\n\",\n      \"                    'ori_shape',\\n\",\n      \"                    'img_shape',\\n\",\n      \"                    'scale_factor',\\n\",\n      \"                ),\\n\",\n      \"                type='PackDetInputs'),\\n\",\n      \"        ],\\n\",\n      \"        test_mode=True,\\n\",\n      \"        type='CocoDataset'),\\n\",\n      \"    drop_last=False,\\n\",\n      \"    num_workers=2,\\n\",\n      \"    persistent_workers=True,\\n\",\n      \"    sampler=dict(shuffle=False, type='DefaultSampler'))\\n\",\n      \"test_evaluator = dict(\\n\",\n      \"    ann_file='./ballondatasets/balloon/val/annotation_coco.json',\\n\",\n      \"    backend_args=None,\\n\",\n      \"    format_only=False,\\n\",\n      \"    metric=[\\n\",\n      \"        'bbox',\\n\",\n      \"        'segm',\\n\",\n      \"    ],\\n\",\n      \"    type='CocoMetric')\\n\",\n      \"test_pipeline = [\\n\",\n      \"    dict(backend_args=None, type='LoadImageFromFile'),\\n\",\n      \"    dict(keep_ratio=True, scale=(\\n\",\n      \"        1333,\\n\",\n      \"        800,\\n\",\n      \"    ), type='Resize'),\\n\",\n      \"    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\\n\",\n      \"    dict(\\n\",\n      \"        meta_keys=(\\n\",\n      \"            'img_id',\\n\",\n      \"            'img_path',\\n\",\n      \"            'ori_shape',\\n\",\n      \"            'img_shape',\\n\",\n      \"            'scale_factor',\\n\",\n      \"        ),\\n\",\n      \"        type='PackDetInputs'),\\n\",\n      \"]\\n\",\n      \"train_cfg = dict(max_epochs=12, type='EpochBasedTrainLoop', val_interval=3)\\n\",\n      \"train_dataloader = dict(\\n\",\n      \"    batch_sampler=dict(type='AspectRatioBatchSampler'),\\n\",\n      \"    batch_size=2,\\n\",\n      \"    dataset=dict(\\n\",\n      \"        ann_file='train/annotation_coco.json',\\n\",\n      \"        backend_args=None,\\n\",\n      \"        data_prefix=dict(img='train/'),\\n\",\n      \"        data_root='./ballondatasets/balloon',\\n\",\n      \"        filter_cfg=dict(filter_empty_gt=True, min_size=32),\\n\",\n      \"        metainfo=dict(classes=('balloon', ), palette=[\\n\",\n      \"            (\\n\",\n      \"                220,\\n\",\n      \"                20,\\n\",\n      \"                60,\\n\",\n      \"            ),\\n\",\n      \"        ]),\\n\",\n      \"        pipeline=[\\n\",\n      \"            dict(backend_args=None, type='LoadImageFromFile'),\\n\",\n      \"            dict(\\n\",\n      \"                poly2mask=False,\\n\",\n      \"                type='LoadAnnotations',\\n\",\n      \"                with_bbox=True,\\n\",\n      \"                with_mask=True),\\n\",\n      \"            dict(\\n\",\n      \"                keep_ratio=True,\\n\",\n      \"                scales=[\\n\",\n      \"                    (\\n\",\n      \"                        1333,\\n\",\n      \"                        640,\\n\",\n      \"                    ),\\n\",\n      \"                    (\\n\",\n      \"                        1333,\\n\",\n      \"                        672,\\n\",\n      \"                    ),\\n\",\n      \"                    (\\n\",\n      \"                        1333,\\n\",\n      \"                        704,\\n\",\n      \"                    ),\\n\",\n      \"                    (\\n\",\n      \"                        1333,\\n\",\n      \"                        736,\\n\",\n      \"                    ),\\n\",\n      \"                    (\\n\",\n      \"                        1333,\\n\",\n      \"                        768,\\n\",\n      \"                    ),\\n\",\n      \"                    (\\n\",\n      \"                        1333,\\n\",\n      \"                        800,\\n\",\n      \"                    ),\\n\",\n      \"                ],\\n\",\n      \"                type='RandomChoiceResize'),\\n\",\n      \"            dict(prob=0.5, type='RandomFlip'),\\n\",\n      \"            dict(type='PackDetInputs'),\\n\",\n      \"        ],\\n\",\n      \"        type='CocoDataset'),\\n\",\n      \"    num_workers=2,\\n\",\n      \"    persistent_workers=True,\\n\",\n      \"    sampler=dict(shuffle=True, type='DefaultSampler'))\\n\",\n      \"train_pipeline = [\\n\",\n      \"    dict(backend_args=None, type='LoadImageFromFile'),\\n\",\n      \"    dict(\\n\",\n      \"        poly2mask=False,\\n\",\n      \"        type='LoadAnnotations',\\n\",\n      \"        with_bbox=True,\\n\",\n      \"        with_mask=True),\\n\",\n      \"    dict(\\n\",\n      \"        keep_ratio=True,\\n\",\n      \"        scales=[\\n\",\n      \"            (\\n\",\n      \"                1333,\\n\",\n      \"                640,\\n\",\n      \"            ),\\n\",\n      \"            (\\n\",\n      \"                1333,\\n\",\n      \"                672,\\n\",\n      \"            ),\\n\",\n      \"            (\\n\",\n      \"                1333,\\n\",\n      \"                704,\\n\",\n      \"            ),\\n\",\n      \"            (\\n\",\n      \"                1333,\\n\",\n      \"                736,\\n\",\n      \"            ),\\n\",\n      \"            (\\n\",\n      \"                1333,\\n\",\n      \"                768,\\n\",\n      \"            ),\\n\",\n      \"            (\\n\",\n      \"                1333,\\n\",\n      \"                800,\\n\",\n      \"            ),\\n\",\n      \"        ],\\n\",\n      \"        type='RandomChoiceResize'),\\n\",\n      \"    dict(prob=0.5, type='RandomFlip'),\\n\",\n      \"    dict(type='PackDetInputs'),\\n\",\n      \"]\\n\",\n      \"val_cfg = dict(type='ValLoop')\\n\",\n      \"val_dataloader = dict(\\n\",\n      \"    batch_size=1,\\n\",\n      \"    dataset=dict(\\n\",\n      \"        ann_file='val/annotation_coco.json',\\n\",\n      \"        backend_args=None,\\n\",\n      \"        data_prefix=dict(img='val/'),\\n\",\n      \"        data_root='./ballondatasets/balloon',\\n\",\n      \"        metainfo=dict(classes=('balloon', ), palette=[\\n\",\n      \"            (\\n\",\n      \"                220,\\n\",\n      \"                20,\\n\",\n      \"                60,\\n\",\n      \"            ),\\n\",\n      \"        ]),\\n\",\n      \"        pipeline=[\\n\",\n      \"            dict(backend_args=None, type='LoadImageFromFile'),\\n\",\n      \"            dict(keep_ratio=True, scale=(\\n\",\n      \"                1333,\\n\",\n      \"                800,\\n\",\n      \"            ), type='Resize'),\\n\",\n      \"            dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\\n\",\n      \"            dict(\\n\",\n      \"                meta_keys=(\\n\",\n      \"                    'img_id',\\n\",\n      \"                    'img_path',\\n\",\n      \"                    'ori_shape',\\n\",\n      \"                    'img_shape',\\n\",\n      \"                    'scale_factor',\\n\",\n      \"                ),\\n\",\n      \"                type='PackDetInputs'),\\n\",\n      \"        ],\\n\",\n      \"        test_mode=True,\\n\",\n      \"        type='CocoDataset'),\\n\",\n      \"    drop_last=False,\\n\",\n      \"    num_workers=2,\\n\",\n      \"    persistent_workers=True,\\n\",\n      \"    sampler=dict(shuffle=False, type='DefaultSampler'))\\n\",\n      \"val_evaluator = dict(\\n\",\n      \"    ann_file='./ballondatasets/balloon/val/annotation_coco.json',\\n\",\n      \"    backend_args=None,\\n\",\n      \"    format_only=False,\\n\",\n      \"    metric=[\\n\",\n      \"        'bbox',\\n\",\n      \"        'segm',\\n\",\n      \"    ],\\n\",\n      \"    type='CocoMetric')\\n\",\n      \"vis_backends = [\\n\",\n      \"    dict(type='LocalVisBackend'),\\n\",\n      \"]\\n\",\n      \"visualizer = dict(\\n\",\n      \"    name='visualizer',\\n\",\n      \"    type='DetLocalVisualizer',\\n\",\n      \"    vis_backends=[\\n\",\n      \"        dict(type='LocalVisBackend'),\\n\",\n      \"        dict(type='TensorboardVisBackend'),\\n\",\n      \"    ])\\n\",\n      \"work_dir = './tutorial_exps'\\n\",\n      \"\\n\",\n      \"2023-08-15 04:31:59.033157: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\\n\",\n      \"To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\\n\",\n      \"2023-08-15 04:32:00.371943: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT\\n\",\n      \"08/15 04:32:04 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Distributed training is not used, all SyncBatchNorm (SyncBN) layers in the model will be automatically reverted to BatchNormXd layers if they are used.\\n\",\n      \"08/15 04:32:04 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Hooks will be executed in the following order:\\n\",\n      \"before_run:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(BELOW_NORMAL) LoggerHook                         \\n\",\n      \" -------------------- \\n\",\n      \"before_train:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(VERY_LOW    ) CheckpointHook                     \\n\",\n      \" -------------------- \\n\",\n      \"before_train_epoch:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(NORMAL      ) DistSamplerSeedHook                \\n\",\n      \" -------------------- \\n\",\n      \"before_train_iter:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \" -------------------- \\n\",\n      \"after_train_iter:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(BELOW_NORMAL) LoggerHook                         \\n\",\n      \"(LOW         ) ParamSchedulerHook                 \\n\",\n      \"(VERY_LOW    ) CheckpointHook                     \\n\",\n      \" -------------------- \\n\",\n      \"after_train_epoch:\\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(LOW         ) ParamSchedulerHook                 \\n\",\n      \"(VERY_LOW    ) CheckpointHook                     \\n\",\n      \" -------------------- \\n\",\n      \"before_val:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \" -------------------- \\n\",\n      \"before_val_epoch:\\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \" -------------------- \\n\",\n      \"before_val_iter:\\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \" -------------------- \\n\",\n      \"after_val_iter:\\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(NORMAL      ) DetVisualizationHook               \\n\",\n      \"(BELOW_NORMAL) LoggerHook                         \\n\",\n      \" -------------------- \\n\",\n      \"after_val_epoch:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(BELOW_NORMAL) LoggerHook                         \\n\",\n      \"(LOW         ) ParamSchedulerHook                 \\n\",\n      \"(VERY_LOW    ) CheckpointHook                     \\n\",\n      \" -------------------- \\n\",\n      \"after_val:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \" -------------------- \\n\",\n      \"after_train:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(VERY_LOW    ) CheckpointHook                     \\n\",\n      \" -------------------- \\n\",\n      \"before_test:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \" -------------------- \\n\",\n      \"before_test_epoch:\\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \" -------------------- \\n\",\n      \"before_test_iter:\\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \" -------------------- \\n\",\n      \"after_test_iter:\\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(NORMAL      ) DetVisualizationHook               \\n\",\n      \"(BELOW_NORMAL) LoggerHook                         \\n\",\n      \" -------------------- \\n\",\n      \"after_test_epoch:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \"(NORMAL      ) IterTimerHook                      \\n\",\n      \"(BELOW_NORMAL) LoggerHook                         \\n\",\n      \" -------------------- \\n\",\n      \"after_test:\\n\",\n      \"(VERY_HIGH   ) RuntimeInfoHook                    \\n\",\n      \" -------------------- \\n\",\n      \"after_run:\\n\",\n      \"(BELOW_NORMAL) LoggerHook                         \\n\",\n      \" -------------------- \\n\",\n      \"loading annotations into memory...\\n\",\n      \"Done (t=0.01s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"loading annotations into memory...\\n\",\n      \"Done (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"loading annotations into memory...\\n\",\n      \"Done (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"08/15 04:32:05 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - load model from: open-mmlab://detectron2/resnet50_caffe\\n\",\n      \"08/15 04:32:05 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Loads checkpoint by openmmlab backend from path: open-mmlab://detectron2/resnet50_caffe\\n\",\n      \"Downloading: \\\"https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth\\\" to /root/.cache/torch/hub/checkpoints/resnet50_msra-5891d200.pth\\n\",\n      \"100% 89.9M/89.9M [00:12<00:00, 7.43MB/s]\\n\",\n      \"08/15 04:32:19 - mmengine - \\u001b[5m\\u001b[4m\\u001b[33mWARNING\\u001b[0m - The model and loaded state dict do not match exactly\\n\",\n      \"\\n\",\n      \"unexpected key in source state_dict: conv1.bias\\n\",\n      \"\\n\",\n      \"Loads checkpoint by local backend from path: checkpoints/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth\\n\",\n      \"The model and loaded state dict do not match exactly\\n\",\n      \"\\n\",\n      \"size mismatch for roi_head.bbox_head.fc_cls.weight: copying a param with shape torch.Size([81, 1024]) from checkpoint, the shape in current model is torch.Size([2, 1024]).\\n\",\n      \"size mismatch for roi_head.bbox_head.fc_cls.bias: copying a param with shape torch.Size([81]) from checkpoint, the shape in current model is torch.Size([2]).\\n\",\n      \"size mismatch for roi_head.bbox_head.fc_reg.weight: copying a param with shape torch.Size([320, 1024]) from checkpoint, the shape in current model is torch.Size([4, 1024]).\\n\",\n      \"size mismatch for roi_head.bbox_head.fc_reg.bias: copying a param with shape torch.Size([320]) from checkpoint, the shape in current model is torch.Size([4]).\\n\",\n      \"size mismatch for roi_head.mask_head.conv_logits.weight: copying a param with shape torch.Size([80, 256, 1, 1]) from checkpoint, the shape in current model is torch.Size([1, 256, 1, 1]).\\n\",\n      \"size mismatch for roi_head.mask_head.conv_logits.bias: copying a param with shape torch.Size([80]) from checkpoint, the shape in current model is torch.Size([1]).\\n\",\n      \"08/15 04:32:19 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Load checkpoint from checkpoints/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth\\n\",\n      \"08/15 04:32:19 - mmengine - \\u001b[5m\\u001b[4m\\u001b[33mWARNING\\u001b[0m - \\\"FileClient\\\" will be deprecated in future. Please use io functions in https://mmengine.readthedocs.io/en/latest/api/fileio.html#file-io\\n\",\n      \"08/15 04:32:19 - mmengine - \\u001b[5m\\u001b[4m\\u001b[33mWARNING\\u001b[0m - \\\"HardDiskBackend\\\" is the alias of \\\"LocalBackend\\\" and the former will be deprecated in future.\\n\",\n      \"08/15 04:32:19 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Checkpoints will be saved to /content/mmdetection/tutorial_exps.\\n\",\n      \"08/15 04:32:25 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [1][10/31]  lr: 4.7545e-05  eta: 0:03:33  time: 0.5898  data_time: 0.0359  memory: 3283  loss: 16.7169  loss_rpn_cls: 0.1373  loss_rpn_bbox: 0.0201  loss_cls: 0.6155  acc: 83.3984  loss_bbox: 0.3727  loss_mask: 15.5713\\n\",\n      \"08/15 04:32:30 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [1][20/31]  lr: 9.7595e-05  eta: 0:03:05  time: 0.5271  data_time: 0.0214  memory: 3283  loss: 10.8499  loss_rpn_cls: 0.0836  loss_rpn_bbox: 0.0148  loss_cls: 0.5401  acc: 84.6680  loss_bbox: 0.2949  loss_mask: 9.9164\\n\",\n      \"08/15 04:32:35 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [1][30/31]  lr: 1.4765e-04  eta: 0:03:02  time: 0.5348  data_time: 0.0180  memory: 3283  loss: 7.7485  loss_rpn_cls: 0.0823  loss_rpn_bbox: 0.0183  loss_cls: 0.4752  acc: 95.7031  loss_bbox: 0.2886  loss_mask: 6.8839\\n\",\n      \"08/15 04:32:35 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:32:41 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [2][10/31]  lr: 2.0270e-04  eta: 0:02:53  time: 0.5254  data_time: 0.0198  memory: 3449  loss: 5.9555  loss_rpn_cls: 0.0705  loss_rpn_bbox: 0.0176  loss_cls: 0.4270  acc: 93.2617  loss_bbox: 0.3093  loss_mask: 5.1312\\n\",\n      \"08/15 04:32:46 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [2][20/31]  lr: 2.5275e-04  eta: 0:02:49  time: 0.5077  data_time: 0.0126  memory: 3282  loss: 4.6321  loss_rpn_cls: 0.0661  loss_rpn_bbox: 0.0176  loss_cls: 0.3947  acc: 84.6680  loss_bbox: 0.3221  loss_mask: 3.8315\\n\",\n      \"08/15 04:32:52 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [2][30/31]  lr: 3.0280e-04  eta: 0:02:45  time: 0.5237  data_time: 0.0137  memory: 3283  loss: 1.8118  loss_rpn_cls: 0.0470  loss_rpn_bbox: 0.0193  loss_cls: 0.3295  acc: 85.7422  loss_bbox: 0.3360  loss_mask: 1.0801\\n\",\n      \"08/15 04:32:52 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:32:57 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [3][10/31]  lr: 3.5786e-04  eta: 0:02:37  time: 0.5224  data_time: 0.0146  memory: 3282  loss: 1.1179  loss_rpn_cls: 0.0411  loss_rpn_bbox: 0.0203  loss_cls: 0.2903  acc: 96.0938  loss_bbox: 0.3861  loss_mask: 0.3801\\n\",\n      \"08/15 04:33:02 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [3][20/31]  lr: 4.0791e-04  eta: 0:02:31  time: 0.5148  data_time: 0.0105  memory: 3283  loss: 0.9559  loss_rpn_cls: 0.0353  loss_rpn_bbox: 0.0189  loss_cls: 0.2542  acc: 95.4102  loss_bbox: 0.3753  loss_mask: 0.2723\\n\",\n      \"08/15 04:33:08 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [3][30/31]  lr: 4.5796e-04  eta: 0:02:27  time: 0.5319  data_time: 0.0118  memory: 3283  loss: 0.9498  loss_rpn_cls: 0.0316  loss_rpn_bbox: 0.0194  loss_cls: 0.2459  acc: 86.4258  loss_bbox: 0.4030  loss_mask: 0.2500\\n\",\n      \"08/15 04:33:08 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:33:08 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Saving checkpoint at 3 epochs\\n\",\n      \"08/15 04:33:20 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val)  [3][10/13]    eta: 0:00:03  time: 1.0153  data_time: 0.0759  memory: 2785  \\n\",\n      \"08/15 04:33:21 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating bbox...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *bbox*\\n\",\n      \"DONE (t=0.16s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.06s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.515\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.736\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.597\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.266\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.621\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.644\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.644\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.644\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.525\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.719\\n\",\n      \"08/15 04:33:21 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - bbox_mAP_copypaste: 0.515 0.736 0.597 0.000 0.266 0.621\\n\",\n      \"08/15 04:33:21 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating segm...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.04s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *segm*\\n\",\n      \"DONE (t=0.21s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.06s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.622\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.733\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.729\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.001\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.331\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.734\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.764\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.764\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.764\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.150\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.750\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.803\\n\",\n      \"08/15 04:33:22 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - segm_mAP_copypaste: 0.622 0.733 0.729 0.001 0.331 0.734\\n\",\n      \"08/15 04:33:22 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val) [3][13/13]    coco/bbox_mAP: 0.5150  coco/bbox_mAP_50: 0.7360  coco/bbox_mAP_75: 0.5970  coco/bbox_mAP_s: 0.0000  coco/bbox_mAP_m: 0.2660  coco/bbox_mAP_l: 0.6210  coco/segm_mAP: 0.6220  coco/segm_mAP_50: 0.7330  coco/segm_mAP_75: 0.7290  coco/segm_mAP_s: 0.0010  coco/segm_mAP_m: 0.3310  coco/segm_mAP_l: 0.7340  data_time: 0.0597  time: 0.8604\\n\",\n      \"08/15 04:33:27 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [4][10/31]  lr: 5.1301e-04  eta: 0:02:22  time: 0.5251  data_time: 0.0119  memory: 3283  loss: 0.8807  loss_rpn_cls: 0.0273  loss_rpn_bbox: 0.0187  loss_cls: 0.2192  acc: 95.1172  loss_bbox: 0.4036  loss_mask: 0.2119\\n\",\n      \"08/15 04:33:32 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [4][20/31]  lr: 5.6306e-04  eta: 0:02:16  time: 0.5211  data_time: 0.0099  memory: 3004  loss: 0.8362  loss_rpn_cls: 0.0217  loss_rpn_bbox: 0.0173  loss_cls: 0.2001  acc: 97.4609  loss_bbox: 0.4115  loss_mask: 0.1856\\n\",\n      \"08/15 04:33:38 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [4][30/31]  lr: 6.1311e-04  eta: 0:02:11  time: 0.5334  data_time: 0.0108  memory: 3284  loss: 0.7356  loss_rpn_cls: 0.0198  loss_rpn_bbox: 0.0148  loss_cls: 0.1705  acc: 99.1211  loss_bbox: 0.3685  loss_mask: 0.1621\\n\",\n      \"08/15 04:33:38 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:33:44 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [5][10/31]  lr: 6.6817e-04  eta: 0:02:05  time: 0.5305  data_time: 0.0137  memory: 3283  loss: 0.6874  loss_rpn_cls: 0.0174  loss_rpn_bbox: 0.0127  loss_cls: 0.1545  acc: 95.1172  loss_bbox: 0.3607  loss_mask: 0.1421\\n\",\n      \"08/15 04:33:49 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [5][20/31]  lr: 7.1822e-04  eta: 0:01:59  time: 0.5216  data_time: 0.0115  memory: 3283  loss: 0.6122  loss_rpn_cls: 0.0162  loss_rpn_bbox: 0.0123  loss_cls: 0.1331  acc: 93.5547  loss_bbox: 0.3217  loss_mask: 0.1289\\n\",\n      \"08/15 04:33:54 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [5][30/31]  lr: 7.6827e-04  eta: 0:01:55  time: 0.5289  data_time: 0.0125  memory: 3283  loss: 0.5432  loss_rpn_cls: 0.0145  loss_rpn_bbox: 0.0122  loss_cls: 0.1175  acc: 93.7500  loss_bbox: 0.2771  loss_mask: 0.1219\\n\",\n      \"08/15 04:33:55 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:34:00 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [6][10/31]  lr: 8.2332e-04  eta: 0:01:49  time: 0.5335  data_time: 0.0137  memory: 3284  loss: 0.4541  loss_rpn_cls: 0.0139  loss_rpn_bbox: 0.0115  loss_cls: 0.0988  acc: 94.2383  loss_bbox: 0.2158  loss_mask: 0.1141\\n\",\n      \"08/15 04:34:06 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [6][20/31]  lr: 8.7337e-04  eta: 0:01:44  time: 0.5318  data_time: 0.0095  memory: 3283  loss: 0.4245  loss_rpn_cls: 0.0130  loss_rpn_bbox: 0.0122  loss_cls: 0.0944  acc: 99.4141  loss_bbox: 0.1922  loss_mask: 0.1127\\n\",\n      \"08/15 04:34:11 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [6][30/31]  lr: 9.2342e-04  eta: 0:01:39  time: 0.5465  data_time: 0.0102  memory: 3497  loss: 0.3810  loss_rpn_cls: 0.0133  loss_rpn_bbox: 0.0126  loss_cls: 0.0883  acc: 98.1445  loss_bbox: 0.1617  loss_mask: 0.1052\\n\",\n      \"08/15 04:34:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:34:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Saving checkpoint at 6 epochs\\n\",\n      \"08/15 04:34:17 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val)  [6][10/13]    eta: 0:00:01  time: 0.6382  data_time: 0.0735  memory: 1810  \\n\",\n      \"08/15 04:34:18 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating bbox...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *bbox*\\n\",\n      \"DONE (t=0.03s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.02s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.709\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.854\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.799\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.474\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.795\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.776\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.776\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.776\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.733\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.833\\n\",\n      \"08/15 04:34:18 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - bbox_mAP_copypaste: 0.709 0.854 0.799 0.000 0.474 0.795\\n\",\n      \"08/15 04:34:18 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating segm...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *segm*\\n\",\n      \"DONE (t=0.03s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.01s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.762\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.834\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.834\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.453\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.860\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.822\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.822\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.822\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.758\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.889\\n\",\n      \"08/15 04:34:18 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - segm_mAP_copypaste: 0.762 0.834 0.834 0.000 0.453 0.860\\n\",\n      \"08/15 04:34:18 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val) [6][13/13]    coco/bbox_mAP: 0.7090  coco/bbox_mAP_50: 0.8540  coco/bbox_mAP_75: 0.7990  coco/bbox_mAP_s: 0.0000  coco/bbox_mAP_m: 0.4740  coco/bbox_mAP_l: 0.7950  coco/segm_mAP: 0.7620  coco/segm_mAP_50: 0.8340  coco/segm_mAP_75: 0.8340  coco/segm_mAP_s: 0.0000  coco/segm_mAP_m: 0.4530  coco/segm_mAP_l: 0.8600  data_time: 0.0661  time: 0.3035\\n\",\n      \"08/15 04:34:23 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [7][10/31]  lr: 9.7848e-04  eta: 0:01:33  time: 0.5453  data_time: 0.0117  memory: 3283  loss: 0.3427  loss_rpn_cls: 0.0123  loss_rpn_bbox: 0.0119  loss_cls: 0.0819  acc: 98.7305  loss_bbox: 0.1376  loss_mask: 0.0989\\n\",\n      \"08/15 04:34:29 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [7][20/31]  lr: 1.0285e-03  eta: 0:01:28  time: 0.5473  data_time: 0.0103  memory: 3282  loss: 0.2958  loss_rpn_cls: 0.0098  loss_rpn_bbox: 0.0095  loss_cls: 0.0710  acc: 97.1680  loss_bbox: 0.1132  loss_mask: 0.0922\\n\",\n      \"08/15 04:34:35 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [7][30/31]  lr: 1.0786e-03  eta: 0:01:23  time: 0.5543  data_time: 0.0105  memory: 3448  loss: 0.2984  loss_rpn_cls: 0.0092  loss_rpn_bbox: 0.0104  loss_cls: 0.0723  acc: 91.1133  loss_bbox: 0.1114  loss_mask: 0.0952\\n\",\n      \"08/15 04:34:35 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:34:41 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [8][10/31]  lr: 1.1336e-03  eta: 0:01:17  time: 0.5499  data_time: 0.0125  memory: 3283  loss: 0.2648  loss_rpn_cls: 0.0083  loss_rpn_bbox: 0.0094  loss_cls: 0.0634  acc: 97.7539  loss_bbox: 0.0944  loss_mask: 0.0894\\n\",\n      \"08/15 04:34:47 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [8][20/31]  lr: 1.1837e-03  eta: 0:01:12  time: 0.5652  data_time: 0.0119  memory: 3283  loss: 0.2537  loss_rpn_cls: 0.0073  loss_rpn_bbox: 0.0086  loss_cls: 0.0605  acc: 99.2188  loss_bbox: 0.0873  loss_mask: 0.0900\\n\",\n      \"08/15 04:34:52 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [8][30/31]  lr: 1.2337e-03  eta: 0:01:07  time: 0.5620  data_time: 0.0117  memory: 3283  loss: 0.2575  loss_rpn_cls: 0.0073  loss_rpn_bbox: 0.0099  loss_cls: 0.0642  acc: 95.4102  loss_bbox: 0.0891  loss_mask: 0.0870\\n\",\n      \"08/15 04:34:52 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:34:58 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [9][10/31]  lr: 1.2888e-04  eta: 0:01:01  time: 0.5621  data_time: 0.0160  memory: 3283  loss: 0.2680  loss_rpn_cls: 0.0079  loss_rpn_bbox: 0.0108  loss_cls: 0.0709  acc: 95.8008  loss_bbox: 0.0934  loss_mask: 0.0851\\n\",\n      \"08/15 04:35:04 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [9][20/31]  lr: 1.3388e-04  eta: 0:00:56  time: 0.5631  data_time: 0.0150  memory: 3283  loss: 0.2295  loss_rpn_cls: 0.0063  loss_rpn_bbox: 0.0082  loss_cls: 0.0603  acc: 99.1211  loss_bbox: 0.0787  loss_mask: 0.0760\\n\",\n      \"08/15 04:35:09 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train)  [9][30/31]  lr: 1.3889e-04  eta: 0:00:50  time: 0.5553  data_time: 0.0133  memory: 3091  loss: 0.2444  loss_rpn_cls: 0.0064  loss_rpn_bbox: 0.0094  loss_cls: 0.0650  acc: 96.5820  loss_bbox: 0.0847  loss_mask: 0.0789\\n\",\n      \"08/15 04:35:09 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:35:09 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Saving checkpoint at 9 epochs\\n\",\n      \"08/15 04:35:16 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val)  [9][10/13]    eta: 0:00:01  time: 0.5437  data_time: 0.0917  memory: 1693  \\n\",\n      \"08/15 04:35:16 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating bbox...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *bbox*\\n\",\n      \"DONE (t=0.04s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.02s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.741\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.869\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.807\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.473\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.833\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.794\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.794\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.794\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.717\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.864\\n\",\n      \"08/15 04:35:16 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - bbox_mAP_copypaste: 0.741 0.869 0.807 0.000 0.473 0.833\\n\",\n      \"08/15 04:35:16 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating segm...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *segm*\\n\",\n      \"DONE (t=0.04s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.02s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.779\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.847\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.847\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.476\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.877\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.830\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.830\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.830\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.767\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.897\\n\",\n      \"08/15 04:35:16 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - segm_mAP_copypaste: 0.779 0.847 0.847 0.000 0.476 0.877\\n\",\n      \"08/15 04:35:16 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val) [9][13/13]    coco/bbox_mAP: 0.7410  coco/bbox_mAP_50: 0.8690  coco/bbox_mAP_75: 0.8070  coco/bbox_mAP_s: 0.0000  coco/bbox_mAP_m: 0.4730  coco/bbox_mAP_l: 0.8330  coco/segm_mAP: 0.7790  coco/segm_mAP_50: 0.8470  coco/segm_mAP_75: 0.8470  coco/segm_mAP_s: 0.0000  coco/segm_mAP_m: 0.4760  coco/segm_mAP_l: 0.8770  data_time: 0.1157  time: 0.3601\\n\",\n      \"08/15 04:35:22 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [10][10/31]  lr: 1.4439e-04  eta: 0:00:44  time: 0.5374  data_time: 0.0169  memory: 3284  loss: 0.2415  loss_rpn_cls: 0.0063  loss_rpn_bbox: 0.0093  loss_cls: 0.0644  acc: 98.3398  loss_bbox: 0.0832  loss_mask: 0.0784\\n\",\n      \"08/15 04:35:27 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [10][20/31]  lr: 1.4940e-04  eta: 0:00:39  time: 0.5342  data_time: 0.0130  memory: 3283  loss: 0.2161  loss_rpn_cls: 0.0048  loss_rpn_bbox: 0.0080  loss_cls: 0.0573  acc: 99.4141  loss_bbox: 0.0721  loss_mask: 0.0738\\n\",\n      \"08/15 04:35:33 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [10][30/31]  lr: 1.5440e-04  eta: 0:00:33  time: 0.5374  data_time: 0.0129  memory: 3428  loss: 0.2210  loss_rpn_cls: 0.0054  loss_rpn_bbox: 0.0086  loss_cls: 0.0584  acc: 99.8047  loss_bbox: 0.0737  loss_mask: 0.0749\\n\",\n      \"08/15 04:35:33 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:35:38 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [11][10/31]  lr: 1.5991e-04  eta: 0:00:27  time: 0.5344  data_time: 0.0149  memory: 3067  loss: 0.2405  loss_rpn_cls: 0.0052  loss_rpn_bbox: 0.0099  loss_cls: 0.0639  acc: 98.8281  loss_bbox: 0.0807  loss_mask: 0.0808\\n\",\n      \"08/15 04:35:44 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [11][20/31]  lr: 1.6491e-04  eta: 0:00:22  time: 0.5326  data_time: 0.0105  memory: 3283  loss: 0.2297  loss_rpn_cls: 0.0047  loss_rpn_bbox: 0.0094  loss_cls: 0.0606  acc: 96.7773  loss_bbox: 0.0768  loss_mask: 0.0781\\n\",\n      \"08/15 04:35:49 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [11][30/31]  lr: 1.6992e-04  eta: 0:00:17  time: 0.5409  data_time: 0.0115  memory: 3283  loss: 0.2221  loss_rpn_cls: 0.0048  loss_rpn_bbox: 0.0092  loss_cls: 0.0595  acc: 95.3125  loss_bbox: 0.0753  loss_mask: 0.0733\\n\",\n      \"08/15 04:35:50 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:35:55 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [12][10/31]  lr: 1.7543e-05  eta: 0:00:11  time: 0.5311  data_time: 0.0129  memory: 3200  loss: 0.2094  loss_rpn_cls: 0.0054  loss_rpn_bbox: 0.0084  loss_cls: 0.0555  acc: 96.9727  loss_bbox: 0.0689  loss_mask: 0.0711\\n\",\n      \"08/15 04:36:00 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [12][20/31]  lr: 1.8043e-05  eta: 0:00:05  time: 0.5275  data_time: 0.0098  memory: 3448  loss: 0.2108  loss_rpn_cls: 0.0047  loss_rpn_bbox: 0.0089  loss_cls: 0.0560  acc: 99.5117  loss_bbox: 0.0698  loss_mask: 0.0713\\n\",\n      \"08/15 04:36:06 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(train) [12][30/31]  lr: 1.8544e-05  eta: 0:00:00  time: 0.5425  data_time: 0.0111  memory: 3282  loss: 0.2048  loss_rpn_cls: 0.0043  loss_rpn_bbox: 0.0080  loss_cls: 0.0540  acc: 98.2422  loss_bbox: 0.0695  loss_mask: 0.0690\\n\",\n      \"08/15 04:36:06 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Exp name: mask-rcnn_r50-caffe_fpn_ms-poly-3x_balloon_20230815_043154\\n\",\n      \"08/15 04:36:06 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Saving checkpoint at 12 epochs\\n\",\n      \"08/15 04:36:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val) [12][10/13]    eta: 0:00:01  time: 0.4801  data_time: 0.0931  memory: 1589  \\n\",\n      \"08/15 04:36:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating bbox...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *bbox*\\n\",\n      \"DONE (t=0.02s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.01s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.740\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.868\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.804\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.506\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.828\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.798\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.798\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.798\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.742\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.861\\n\",\n      \"08/15 04:36:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - bbox_mAP_copypaste: 0.740 0.868 0.804 0.000 0.506 0.828\\n\",\n      \"08/15 04:36:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Evaluating segm...\\n\",\n      \"Loading and preparing results...\\n\",\n      \"DONE (t=0.00s)\\n\",\n      \"creating index...\\n\",\n      \"index created!\\n\",\n      \"Running per image evaluation...\\n\",\n      \"Evaluate annotation type *segm*\\n\",\n      \"DONE (t=0.02s).\\n\",\n      \"Accumulating evaluation results...\\n\",\n      \"DONE (t=0.01s).\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.776\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.845\\n\",\n      \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.845\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.481\\n\",\n      \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.868\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.832\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.832\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.832\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.000\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.775\\n\",\n      \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.897\\n\",\n      \"08/15 04:36:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - segm_mAP_copypaste: 0.776 0.845 0.845 0.000 0.481 0.868\\n\",\n      \"08/15 04:36:12 - mmengine - \\u001b[4m\\u001b[97mINFO\\u001b[0m - Epoch(val) [12][13/13]    coco/bbox_mAP: 0.7400  coco/bbox_mAP_50: 0.8680  coco/bbox_mAP_75: 0.8040  coco/bbox_mAP_s: 0.0000  coco/bbox_mAP_m: 0.5060  coco/bbox_mAP_l: 0.8280  coco/segm_mAP: 0.7760  coco/segm_mAP_50: 0.8450  coco/segm_mAP_75: 0.8450  coco/segm_mAP_s: 0.0000  coco/segm_mAP_m: 0.4810  coco/segm_mAP_l: 0.8680  data_time: 0.0903  time: 0.2892\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"!python tools/train.py {config}\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"_vYQF5K2NqqI\"\n   },\n   \"source\": [\n    \"### Understand the log\\n\",\n    \"From the log, we can have a basic understanding on the training process and know how well the detector is trained.\\n\",\n    \"\\n\",\n    \"First, since the dataset we are using is small, we loaded a Mask R-CNN model and finetune it for detection. Because the original Mask R-CNN is trained on COCO dataset that contains 80 classes but KITTI Tiny dataset only have 3 classes. Therefore, the last FC layers of the pre-trained Mask R-CNN for classification and regression have different weight shape and are not used. The pre-trained weights of mask prediction layer `mask_head.conv_logits` also does not matches the current model and is not used due to similar reason.\\n\",\n    \"\\n\",\n    \"Third, after training, the detector is evaluated by the default COCO-style evaluation. The results show that the detector achieves 79.6 bbox AP and 81.5 mask AP on the val dataset, not bad!\\n\",\n    \"\\n\",\n    \" We can also check the tensorboard to see the curves.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"id\": \"gbLNlJR-RYYd\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"%pip install tensorboard  -i https://mirrors.ustc.edu.cn/pypi/web/simple\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 21,\n   \"metadata\": {\n    \"id\": \"PW2NAam_7irv\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# load tensorboard in jupyter notebook\\n\",\n    \"%load_ext tensorboard\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"id\": \"4G9MCbL2RYYd\"\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# see curves in tensorboard\\n\",\n    \"# if you see <IPython.core.display.HTML object> please run it again\\n\",\n    \"%tensorboard --logdir tutorial_exps/\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"MfQ-yspZLuuI\"\n   },\n   \"source\": [\n    \"## Test the Trained Detector\\n\",\n    \"\\n\",\n    \"After finetuning the detector, let's visualize the prediction results!\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 23,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\"\n    },\n    \"id\": \"_MuZurfGLq0p\",\n    \"outputId\": \"4b25759c-8e22-405e-a061-3abc44e38043\"\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Loads checkpoint by local backend from path: tutorial_exps/epoch_12.pth\\n\",\n      \"<DetDataSample(\\n\",\n      \"\\n\",\n      \"    META INFORMATION\\n\",\n      \"    batch_input_shape: (800, 1216)\\n\",\n      \"    pad_shape: (800, 1216)\\n\",\n      \"    scale_factor: (0.58837890625, 0.5882352941176471)\\n\",\n      \"    img_id: 0\\n\",\n      \"    ori_shape: (1360, 2048)\\n\",\n      \"    img_shape: (800, 1205)\\n\",\n      \"    img_path: None\\n\",\n      \"\\n\",\n      \"    DATA FIELDS\\n\",\n      \"    gt_instances: <InstanceData(\\n\",\n      \"        \\n\",\n      \"            META INFORMATION\\n\",\n      \"        \\n\",\n      \"            DATA FIELDS\\n\",\n      \"            labels: tensor([], dtype=torch.int64)\\n\",\n      \"            bboxes: tensor([], size=(0, 4))\\n\",\n      \"            masks: BitmapMasks(num_masks=0, height=1360, width=2048)\\n\",\n      \"        ) at 0x79a3a96e32b0>\\n\",\n      \"    ignored_instances: <InstanceData(\\n\",\n      \"        \\n\",\n      \"            META INFORMATION\\n\",\n      \"        \\n\",\n      \"            DATA FIELDS\\n\",\n      \"            labels: tensor([], dtype=torch.int64)\\n\",\n      \"            bboxes: tensor([], size=(0, 4))\\n\",\n      \"            masks: BitmapMasks(num_masks=0, height=1360, width=2048)\\n\",\n      \"        ) at 0x79a3a96e2e90>\\n\",\n      \"    pred_instances: <InstanceData(\\n\",\n      \"        \\n\",\n      \"            META INFORMATION\\n\",\n      \"        \\n\",\n      \"            DATA FIELDS\\n\",\n      \"            labels: tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])\\n\",\n      \"            bboxes: tensor([[ 669.8222,  242.3854, 1406.3090,  999.6241],\\n\",\n      \"                        [1284.7911,  415.3238, 1697.2852,  925.6313],\\n\",\n      \"                        [ 589.4117,  419.8082,  729.0321,  839.9910],\\n\",\n      \"                        [1034.3956,    8.1391, 1430.1029,  165.9097],\\n\",\n      \"                        [1030.5734, 1166.0953, 1149.0190, 1229.6295],\\n\",\n      \"                        [1017.3596, 1311.2710, 1193.4612, 1359.9869],\\n\",\n      \"                        [ 724.5475, 1260.3674,  835.1842, 1339.4836],\\n\",\n      \"                        [ 491.2525,  405.2336,  684.7927,  529.9492],\\n\",\n      \"                        [ 980.2676,   15.9298, 1496.0459,  413.1231],\\n\",\n      \"                        [ 635.1721,  399.2910,  786.5412,  847.4308],\\n\",\n      \"                        [ 994.9816,    3.6709, 1188.3881,  169.8953],\\n\",\n      \"                        [1786.0100,  203.3728, 1813.8008,  230.7004],\\n\",\n      \"                        [ 416.1780,  117.8048,  559.7723,  383.0736],\\n\",\n      \"                        [ 183.7183,  849.5600,  332.8814,  895.9375],\\n\",\n      \"                        [ 310.0583,  784.4355,  378.9562,  896.0558],\\n\",\n      \"                        [1136.9861,  912.4040, 1391.0818, 1344.7209],\\n\",\n      \"                        [1300.2130, 1108.3888, 1587.3857, 1348.6622]])\\n\",\n      \"            masks: tensor([[[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        ...,\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]],\\n\",\n      \"                \\n\",\n      \"                        [[False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         ...,\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False],\\n\",\n      \"                         [False, False, False,  ..., False, False, False]]])\\n\",\n      \"            scores: tensor([0.9977, 0.9940, 0.9682, 0.8142, 0.6556, 0.3539, 0.3009, 0.2788, 0.1958,\\n\",\n      \"                        0.1624, 0.1309, 0.1179, 0.1106, 0.0928, 0.0888, 0.0737, 0.0584])\\n\",\n      \"        ) at 0x79a3a96cb070>\\n\",\n      \") at 0x79a3a96e3130>\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import mmcv\\n\",\n    \"from mmdet.apis import init_detector, inference_detector\\n\",\n    \"img = mmcv.imread('./ballondatasets/balloon/train/7178882742_f090f3ce56_k.jpg',channel_order='rgb')\\n\",\n    \"checkpoint_file = 'tutorial_exps/epoch_12.pth'\\n\",\n    \"model = init_detector(cfg, checkpoint_file, device='cpu')\\n\",\n    \"new_result = inference_detector(model, img)\\n\",\n    \"print(new_result)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 24,\n   \"metadata\": {\n    \"colab\": {\n     \"base_uri\": \"https://localhost:8080/\",\n     \"height\": 461\n    },\n    \"id\": \"7SSTauCURYYe\",\n    \"outputId\": \"3becb5ea-cb4e-44f6-d93d-c10194a2263b\"\n   },\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"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\",\n      \"text/plain\": [\n       \"<Figure size 640x480 with 1 Axes>\"\n      ]\n     },\n     \"execution_count\": 24,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"from mmengine.visualization import Visualizer\\n\",\n    \"# get built visualizer\\n\",\n    \"visualizer_now = Visualizer.get_current_instance()\\n\",\n    \"# the dataset_meta is loaded from the checkpoint and\\n\",\n    \"# then pass to the model in init_detector\\n\",\n    \"visualizer_now.dataset_meta = model.dataset_meta\\n\",\n    \"# show the results\\n\",\n    \"visualizer_now.add_datasample(\\n\",\n    \"    'new_result',\\n\",\n    \"    img,\\n\",\n    \"    data_sample=new_result,\\n\",\n    \"    draw_gt=False,\\n\",\n    \"    wait_time=0,\\n\",\n    \"    out_file=None,\\n\",\n    \"    pred_score_thr=0.5\\n\",\n    \")\\n\",\n    \"visualizer_now.show()\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"id\": \"6rzruCwFgPXm\"\n   },\n   \"source\": [\n    \"## What to Do Next?\\n\",\n    \"\\n\",\n    \"So far, we have learnt how to test and train Mask R-CNN. To further explore the segmentation task, you could do several other things as shown below:\\n\",\n    \"\\n\",\n    \"- Try cascade methods, e.g., [Cascade Mask R-CNN](https://github.com/open-mmlab/mmdetection/tree/master/configs/cascade_rcnn) and [HTC](https://github.com/open-mmlab/mmdetection/tree/master/configs/htc) in [MMDetection model zoo](https://github.com/open-mmlab/mmdetection/blob/master/docs/en/model_zoo.md). They are powerful detectors that are ranked high in many benchmarks, e.g., COCO dataset.\\n\",\n    \"- Try single-stage methods, e.g., [K-Net](https://github.com/ZwwWayne/K-Net) and [Dense-RepPoints](https://github.com/justimyhxu/Dense-RepPoints). These two algorithms are based on MMDetection. Box-free instance segmentation is a new trend in the instance segmentation community.\\n\",\n    \"- Try semantic segmentation. Semantic segmentation is also a popular task with wide applications. You can explore [MMSegmentation](https://github.com/open-mmlab/mmsegmentation/); we also provide a [colab tutorial](https://github.com/open-mmlab/mmsegmentation/blob/master/demo/MMSegmentation_Tutorial.ipynb) for semantic segmentation using MMSegmentation.\\n\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"accelerator\": \"GPU\",\n  \"colab\": {\n   \"provenance\": []\n  },\n  \"kernelspec\": {\n   \"display_name\": \"Python 3 (ipykernel)\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.9.15\"\n  },\n  \"vscode\": {\n   \"interpreter\": {\n    \"hash\": \"8868640c17582ff5a3e06365ba2fb344ce697cf42d4745ae8b85a9738303c037\"\n   }\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 1\n}\n"
  },
  {
    "path": "demo/create_result_gif.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\n\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nimport mmcv\nimport numpy as np\nfrom mmengine.utils import scandir\n\ntry:\n    import imageio\nexcept ImportError:\n    imageio = None\n\n\n# TODO verify after refactoring analyze_results.py\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Create GIF for demo')\n    parser.add_argument(\n        'image_dir',\n        help='directory where result '\n        'images save path generated by ‘analyze_results.py’')\n    parser.add_argument(\n        '--out',\n        type=str,\n        default='result.gif',\n        help='gif path where will be saved')\n    args = parser.parse_args()\n    return args\n\n\ndef _generate_batch_data(sampler, batch_size):\n    batch = []\n    for idx in sampler:\n        batch.append(idx)\n        if len(batch) == batch_size:\n            yield batch\n            batch = []\n    if len(batch) > 0:\n        yield batch\n\n\ndef create_gif(frames, gif_name, duration=2):\n    \"\"\"Create gif through imageio.\n\n    Args:\n        frames (list[ndarray]): Image frames\n        gif_name (str): Saved gif name\n        duration (int): Display interval (s),\n            Default: 2\n    \"\"\"\n    if imageio is None:\n        raise RuntimeError('imageio is not installed,'\n                           'Please use “pip install imageio” to install')\n    imageio.mimsave(gif_name, frames, 'GIF', duration=duration)\n\n\ndef create_frame_by_matplotlib(image_dir,\n                               nrows=1,\n                               fig_size=(300, 300),\n                               font_size=15):\n    \"\"\"Create gif frame image through matplotlib.\n\n    Args:\n        image_dir (str): Root directory of result images\n        nrows (int): Number of rows displayed, Default: 1\n        fig_size (tuple): Figure size of the pyplot figure.\n           Default: (300, 300)\n        font_size (int): Font size of texts. Default: 15\n\n    Returns:\n        list[ndarray]: image frames\n    \"\"\"\n\n    result_dir_names = os.listdir(image_dir)\n    assert len(result_dir_names) == 2\n    # Longer length has higher priority\n    result_dir_names.reverse()\n\n    images_list = []\n    for dir_names in result_dir_names:\n        images_list.append(scandir(osp.join(image_dir, dir_names)))\n\n    frames = []\n    for paths in _generate_batch_data(zip(*images_list), nrows):\n\n        fig, axes = plt.subplots(nrows=nrows, ncols=2)\n        fig.suptitle('Good/bad case selected according '\n                     'to the COCO mAP of the single image')\n\n        det_patch = mpatches.Patch(color='salmon', label='prediction')\n        gt_patch = mpatches.Patch(color='royalblue', label='ground truth')\n        # bbox_to_anchor may need to be finetuned\n        plt.legend(\n            handles=[det_patch, gt_patch],\n            bbox_to_anchor=(1, -0.18),\n            loc='lower right',\n            borderaxespad=0.)\n\n        if nrows == 1:\n            axes = [axes]\n\n        dpi = fig.get_dpi()\n        # set fig size and margin\n        fig.set_size_inches(\n            (fig_size[0] * 2 + fig_size[0] // 20) / dpi,\n            (fig_size[1] * nrows + fig_size[1] // 3) / dpi,\n        )\n\n        fig.tight_layout()\n        # set subplot margin\n        plt.subplots_adjust(\n            hspace=.05,\n            wspace=0.05,\n            left=0.02,\n            right=0.98,\n            bottom=0.02,\n            top=0.98)\n\n        for i, (path_tuple, ax_tuple) in enumerate(zip(paths, axes)):\n            image_path_left = osp.join(\n                osp.join(image_dir, result_dir_names[0], path_tuple[0]))\n            image_path_right = osp.join(\n                osp.join(image_dir, result_dir_names[1], path_tuple[1]))\n            image_left = mmcv.imread(image_path_left)\n            image_left = mmcv.rgb2bgr(image_left)\n            image_right = mmcv.imread(image_path_right)\n            image_right = mmcv.rgb2bgr(image_right)\n\n            if i == 0:\n                ax_tuple[0].set_title(\n                    result_dir_names[0], fontdict={'size': font_size})\n                ax_tuple[1].set_title(\n                    result_dir_names[1], fontdict={'size': font_size})\n            ax_tuple[0].imshow(\n                image_left, extent=(0, *fig_size, 0), interpolation='bilinear')\n            ax_tuple[0].axis('off')\n            ax_tuple[1].imshow(\n                image_right,\n                extent=(0, *fig_size, 0),\n                interpolation='bilinear')\n            ax_tuple[1].axis('off')\n\n        canvas = fig.canvas\n        s, (width, height) = canvas.print_to_buffer()\n        buffer = np.frombuffer(s, dtype='uint8')\n        img_rgba = buffer.reshape(height, width, 4)\n        rgb, alpha = np.split(img_rgba, [3], axis=2)\n        img = rgb.astype('uint8')\n\n        frames.append(img)\n\n    return frames\n\n\ndef main():\n    args = parse_args()\n    frames = create_frame_by_matplotlib(args.image_dir)\n    create_gif(frames, args.out)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "demo/demo_multi_model.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Support for multi-model fusion, and currently only the Weighted Box Fusion\n(WBF) fusion method is supported.\n\nReferences: https://github.com/ZFTurbo/Weighted-Boxes-Fusion\n\nExample:\n\n     python demo/demo_multi_model.py demo/demo.jpg \\\n         ./configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_1x_coco.py \\\n         ./configs/retinanet/retinanet_r50-caffe_fpn_1x_coco.py \\\n         --checkpoints \\\n         https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_caffe_fpn_1x_coco/faster_rcnn_r50_caffe_fpn_1x_coco_bbox_mAP-0.378_20200504_180032-c5925ee5.pth \\  # noqa\n         https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_caffe_fpn_1x_coco/retinanet_r50_caffe_fpn_1x_coco_20200531-f11027c5.pth \\\n         --weights 1 2\n\"\"\"\n\nimport argparse\nimport os.path as osp\n\nimport mmcv\nimport mmengine\nfrom mmengine.fileio import isdir, join_path, list_dir_or_file\nfrom mmengine.logging import print_log\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.apis import DetInferencer\nfrom mmdet.models.utils import weighted_boxes_fusion\nfrom mmdet.registry import VISUALIZERS\nfrom mmdet.structures import DetDataSample\n\nIMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif',\n                  '.tiff', '.webp')\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMDetection multi-model inference demo')\n    parser.add_argument(\n        'inputs', type=str, help='Input image file or folder path.')\n    parser.add_argument(\n        'config',\n        type=str,\n        nargs='*',\n        help='Config file(s), support receive multiple files')\n    parser.add_argument(\n        '--checkpoints',\n        type=str,\n        nargs='*',\n        help='Checkpoint file(s), support receive multiple files, '\n        'remember to correspond to the above config',\n    )\n    parser.add_argument(\n        '--weights',\n        type=float,\n        nargs='*',\n        default=None,\n        help='weights for each model, remember to '\n        'correspond to the above config')\n    parser.add_argument(\n        '--fusion-iou-thr',\n        type=float,\n        default=0.55,\n        help='IoU value for boxes to be a match in wbf')\n    parser.add_argument(\n        '--skip-box-thr',\n        type=float,\n        default=0.0,\n        help='exclude boxes with score lower than this variable in wbf')\n    parser.add_argument(\n        '--conf-type',\n        type=str,\n        default='avg',  # avg, max, box_and_model_avg, absent_model_aware_avg\n        help='how to calculate confidence in weighted boxes in wbf')\n    parser.add_argument(\n        '--out-dir',\n        type=str,\n        default='outputs',\n        help='Output directory of images or prediction results.')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--pred-score-thr',\n        type=float,\n        default=0.3,\n        help='bbox score threshold')\n    parser.add_argument(\n        '--batch-size', type=int, default=1, help='Inference batch size.')\n    parser.add_argument(\n        '--show',\n        action='store_true',\n        help='Display the image in a popup window.')\n    parser.add_argument(\n        '--no-save-vis',\n        action='store_true',\n        help='Do not save detection vis results')\n    parser.add_argument(\n        '--no-save-pred',\n        action='store_true',\n        help='Do not save detection json results')\n    parser.add_argument(\n        '--palette',\n        default='none',\n        choices=['coco', 'voc', 'citys', 'random', 'none'],\n        help='Color palette used for visualization')\n\n    args = parser.parse_args()\n\n    if args.no_save_vis and args.no_save_pred:\n        args.out_dir = ''\n\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    results = []\n    cfg_visualizer = None\n    dataset_meta = None\n\n    inputs = []\n    filename_list = []\n    if isdir(args.inputs):\n        dir = list_dir_or_file(\n            args.inputs, list_dir=False, suffix=IMG_EXTENSIONS)\n        for filename in dir:\n            img = mmcv.imread(join_path(args.inputs, filename))\n            inputs.append(img)\n            filename_list.append(filename)\n    else:\n        img = mmcv.imread(args.inputs)\n        inputs.append(img)\n        img_name = osp.basename(args.inputs)\n        filename_list.append(img_name)\n\n    for i, (config,\n            checkpoint) in enumerate(zip(args.config, args.checkpoints)):\n        inferencer = DetInferencer(\n            config, checkpoint, device=args.device, palette=args.palette)\n\n        result_raw = inferencer(\n            inputs=inputs,\n            batch_size=args.batch_size,\n            no_save_vis=True,\n            pred_score_thr=args.pred_score_thr)\n\n        if i == 0:\n            cfg_visualizer = inferencer.cfg.visualizer\n            dataset_meta = inferencer.model.dataset_meta\n            results = [{\n                'bboxes_list': [],\n                'scores_list': [],\n                'labels_list': []\n            } for _ in range(len(result_raw['predictions']))]\n\n        for res, raw in zip(results, result_raw['predictions']):\n            res['bboxes_list'].append(raw['bboxes'])\n            res['scores_list'].append(raw['scores'])\n            res['labels_list'].append(raw['labels'])\n\n    visualizer = VISUALIZERS.build(cfg_visualizer)\n    visualizer.dataset_meta = dataset_meta\n\n    for i in range(len(results)):\n        bboxes, scores, labels = weighted_boxes_fusion(\n            results[i]['bboxes_list'],\n            results[i]['scores_list'],\n            results[i]['labels_list'],\n            weights=args.weights,\n            iou_thr=args.fusion_iou_thr,\n            skip_box_thr=args.skip_box_thr,\n            conf_type=args.conf_type)\n\n        pred_instances = InstanceData()\n        pred_instances.bboxes = bboxes\n        pred_instances.scores = scores\n        pred_instances.labels = labels\n\n        fusion_result = DetDataSample(pred_instances=pred_instances)\n\n        img_name = filename_list[i]\n\n        if not args.no_save_pred:\n            out_json_path = (\n                args.out_dir + '/preds/' + img_name.split('.')[0] + '.json')\n            mmengine.dump(\n                {\n                    'labels': labels.tolist(),\n                    'scores': scores.tolist(),\n                    'bboxes': bboxes.tolist()\n                }, out_json_path)\n\n        out_file = osp.join(args.out_dir, 'vis',\n                            img_name) if not args.no_save_vis else None\n\n        visualizer.add_datasample(\n            img_name,\n            inputs[i][..., ::-1],\n            data_sample=fusion_result,\n            show=args.show,\n            draw_gt=False,\n            wait_time=0,\n            pred_score_thr=args.pred_score_thr,\n            out_file=out_file)\n\n    if not args.no_save_vis:\n        print_log(f'results have been saved at {args.out_dir}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "demo/image_demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Image Demo.\n\nThis script adopts a new infenence class, currently supports image path,\nnp.array and folder input formats, and will support video and webcam\nin the future.\n\nExample:\n    Save visualizations and predictions results::\n\n        python demo/image_demo.py demo/demo.jpg rtmdet-s\n\n        python demo/image_demo.py demo/demo.jpg \\\n        configs/rtmdet/rtmdet_s_8xb32-300e_coco.py \\\n        --weights rtmdet_s_8xb32-300e_coco_20220905_161602-387a891e.pth\n\n        python demo/image_demo.py demo/demo.jpg \\\n        glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365 --texts bench\n\n        python demo/image_demo.py demo/demo.jpg \\\n        glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365 --texts 'bench . car .'\n\n        python demo/image_demo.py demo/demo.jpg \\\n        glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365\n        --texts 'bench . car .' -c\n\n        python demo/image_demo.py demo/demo.jpg \\\n        glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365 \\\n        --texts 'There are a lot of cars here.'\n\n        python demo/image_demo.py demo/demo.jpg \\\n        glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365 \\\n        --texts '$: coco'\n\n        python demo/image_demo.py demo/demo.jpg \\\n        glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365 \\\n        --texts '$: lvis' --pred-score-thr 0.7 \\\n        --palette random --chunked-size 80\n\n        python demo/image_demo.py demo/demo.jpg \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_cap4m \\\n        --texts '$: lvis' --pred-score-thr 0.4 \\\n        --palette random --chunked-size 80\n\n        python demo/image_demo.py demo/demo.jpg \\\n        grounding_dino_swin-t_pretrain_obj365_goldg_cap4m \\\n        --texts \"a red car in the upper right corner\" \\\n        --tokens-positive -1\n\n    Visualize prediction results::\n\n        python demo/image_demo.py demo/demo.jpg rtmdet-ins-s --show\n\n        python demo/image_demo.py demo/demo.jpg rtmdet-ins_s_8xb32-300e_coco \\\n        --show\n\"\"\"\n\nimport ast\nfrom argparse import ArgumentParser\n\nfrom mmengine.logging import print_log\n\nfrom mmdet.apis import DetInferencer\nfrom mmdet.evaluation import get_classes\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument(\n        'inputs', type=str, help='Input image file or folder path.')\n    parser.add_argument(\n        'model',\n        type=str,\n        help='Config or checkpoint .pth file or the model name '\n        'and alias defined in metafile. The model configuration '\n        'file will try to read from .pth if the parameter is '\n        'a .pth weights file.')\n    parser.add_argument('--weights', default=None, help='Checkpoint file')\n    parser.add_argument(\n        '--out-dir',\n        type=str,\n        default='outputs',\n        help='Output directory of images or prediction results.')\n    # Once you input a format similar to $: xxx, it indicates that\n    # the prompt is based on the dataset class name.\n    # support $: coco, $: voc, $: cityscapes, $: lvis, $: imagenet_det.\n    # detail to `mmdet/evaluation/functional/class_names.py`\n    parser.add_argument(\n        '--texts', help='text prompt, such as \"bench . car .\", \"$: coco\"')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--pred-score-thr',\n        type=float,\n        default=0.3,\n        help='bbox score threshold')\n    parser.add_argument(\n        '--batch-size', type=int, default=1, help='Inference batch size.')\n    parser.add_argument(\n        '--show',\n        action='store_true',\n        help='Display the image in a popup window.')\n    parser.add_argument(\n        '--no-save-vis',\n        action='store_true',\n        help='Do not save detection vis results')\n    parser.add_argument(\n        '--no-save-pred',\n        action='store_true',\n        help='Do not save detection json results')\n    parser.add_argument(\n        '--print-result',\n        action='store_true',\n        help='Whether to print the results.')\n    parser.add_argument(\n        '--palette',\n        default='none',\n        choices=['coco', 'voc', 'citys', 'random', 'none'],\n        help='Color palette used for visualization')\n    # only for GLIP and Grounding DINO\n    parser.add_argument(\n        '--custom-entities',\n        '-c',\n        action='store_true',\n        help='Whether to customize entity names? '\n        'If so, the input text should be '\n        '\"cls_name1 . cls_name2 . cls_name3 .\" format')\n    parser.add_argument(\n        '--chunked-size',\n        '-s',\n        type=int,\n        default=-1,\n        help='If the number of categories is very large, '\n        'you can specify this parameter to truncate multiple predictions.')\n    # only for Grounding DINO\n    parser.add_argument(\n        '--tokens-positive',\n        '-p',\n        type=str,\n        help='Used to specify which locations in the input text are of '\n        'interest to the user. -1 indicates that no area is of interest, '\n        'None indicates ignoring this parameter. '\n        'The two-dimensional array represents the start and end positions.')\n\n    call_args = vars(parser.parse_args())\n\n    if call_args['no_save_vis'] and call_args['no_save_pred']:\n        call_args['out_dir'] = ''\n\n    if call_args['model'].endswith('.pth'):\n        print_log('The model is a weight file, automatically '\n                  'assign the model to --weights')\n        call_args['weights'] = call_args['model']\n        call_args['model'] = None\n\n    if call_args['texts'] is not None:\n        if call_args['texts'].startswith('$:'):\n            dataset_name = call_args['texts'][3:].strip()\n            class_names = get_classes(dataset_name)\n            call_args['texts'] = [tuple(class_names)]\n\n    if call_args['tokens_positive'] is not None:\n        call_args['tokens_positive'] = ast.literal_eval(\n            call_args['tokens_positive'])\n\n    init_kws = ['model', 'weights', 'device', 'palette']\n    init_args = {}\n    for init_kw in init_kws:\n        init_args[init_kw] = call_args.pop(init_kw)\n\n    return init_args, call_args\n\n\ndef main():\n    init_args, call_args = parse_args()\n    # TODO: Video and Webcam are currently not supported and\n    #  may consume too much memory if your input folder has a lot of images.\n    #  We will be optimized later.\n    inferencer = DetInferencer(**init_args)\n\n    chunked_size = call_args.pop('chunked_size')\n    inferencer.model.test_cfg.chunked_size = chunked_size\n\n    inferencer(**call_args)\n\n    if call_args['out_dir'] != '' and not (call_args['no_save_vis']\n                                           and call_args['no_save_pred']):\n        print_log(f'results have been saved at {call_args[\"out_dir\"]}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "demo/inference_demo.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gCMycQ_2U8SA\"\n      },\n      \"source\": [\n        \"<div align=\\\"center\\\">\\n\",\n        \"  <img src=\\\"https://github.com/open-mmlab/mmdetection/raw/3.x/resources/mmdet-logo.png\\\" width=\\\"600\\\"/>\\n\",\n        \"  <div>&nbsp;</div>\\n\",\n        \"  <div align=\\\"center\\\">\\n\",\n        \"    <b><font size=\\\"5\\\">OpenMMLab website</font></b>\\n\",\n        \"    <sup>\\n\",\n        \"      <a href=\\\"https://openmmlab.com\\\">\\n\",\n        \"        <i><font size=\\\"4\\\">HOT</font></i>\\n\",\n        \"      </a>\\n\",\n        \"    </sup>\\n\",\n        \"    &nbsp;&nbsp;&nbsp;&nbsp;\\n\",\n        \"    <b><font size=\\\"5\\\">OpenMMLab platform</font></b>\\n\",\n        \"    <sup>\\n\",\n        \"      <a href=\\\"https://platform.openmmlab.com\\\">\\n\",\n        \"        <i><font size=\\\"4\\\">TRY IT OUT</font></i>\\n\",\n        \"      </a>\\n\",\n        \"    </sup>\\n\",\n        \"  </div>\\n\",\n        \"  <div>&nbsp;</div>\\n\",\n        \"\\n\",\n        \"<a href=\\\"https://colab.research.google.com/github/open-mmlab/mmdetection/blob/dev-3.x/demo/inference_demo.ipynb\\\"><img src=\\\"https://colab.research.google.com/assets/colab-badge.svg\\\" alt=\\\"Open In Colab\\\"></a>\\n\",\n        \"\\n\",\n        \"[![PyPI](https://img.shields.io/pypi/v/mmdet)](https://pypi.org/project/mmdet)\\n\",\n        \"[![docs](https://img.shields.io/badge/docs-latest-blue)](https://mmdetection.readthedocs.io/en/latest/)\\n\",\n        \"[![badge](https://github.com/open-mmlab/mmdetection/workflows/build/badge.svg)](https://github.com/open-mmlab/mmdetection/actions)\\n\",\n        \"[![codecov](https://codecov.io/gh/open-mmlab/mmdetection/branch/master/graph/badge.svg)](https://codecov.io/gh/open-mmlab/mmdetection)\\n\",\n        \"[![license](https://img.shields.io/github/license/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/blob/master/LICENSE)\\n\",\n        \"[![open issues](https://isitmaintained.com/badge/open/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/issues)\\n\",\n        \"[![issue resolution](https://isitmaintained.com/badge/resolution/open-mmlab/mmdetection.svg)](https://github.com/open-mmlab/mmdetection/issues)\\n\",\n        \"\\n\",\n        \"[📘Documentation](https://mmdetection.readthedocs.io/en/3.x/) |\\n\",\n        \"[🛠️Installation](https://mmdetection.readthedocs.io/en/3.x/get_started.html) |\\n\",\n        \"[👀Model Zoo](https://mmdetection.readthedocs.io/en/3.x/model_zoo.html) |\\n\",\n        \"[🆕Update News](https://mmdetection.readthedocs.io/en/3.x/notes/changelog.html) |\\n\",\n        \"[🚀Ongoing Projects](https://github.com/open-mmlab/mmdetection/projects) |\\n\",\n        \"[🤔Reporting Issues](https://github.com/open-mmlab/mmdetection/issues/new/choose)\\n\",\n        \"\\n\",\n        \"</div>\\n\",\n        \"\\n\",\n        \"<div align=\\\"center\\\">\\n\",\n        \"  <a href=\\\"https://openmmlab.medium.com/\\\" style=\\\"text-decoration:none;\\\">\\n\",\n        \"    <img src=\\\"https://user-images.githubusercontent.com/25839884/219255827-67c1a27f-f8c5-46a9-811d-5e57448c61d1.png\\\" width=\\\"3%\\\" alt=\\\"\\\" /></a>\\n\",\n        \"  <img src=\\\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\\\" width=\\\"3%\\\" alt=\\\"\\\" />\\n\",\n        \"  <a href=\\\"https://discord.com/channels/1037617289144569886/1046608014234370059\\\" style=\\\"text-decoration:none;\\\">\\n\",\n        \"    <img src=\\\"https://user-images.githubusercontent.com/25839884/218347213-c080267f-cbb6-443e-8532-8e1ed9a58ea9.png\\\" width=\\\"3%\\\" alt=\\\"\\\" /></a>\\n\",\n        \"  <img src=\\\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\\\" width=\\\"3%\\\" alt=\\\"\\\" />\\n\",\n        \"  <a href=\\\"https://twitter.com/OpenMMLab\\\" style=\\\"text-decoration:none;\\\">\\n\",\n        \"    <img src=\\\"https://user-images.githubusercontent.com/25839884/218346637-d30c8a0f-3eba-4699-8131-512fb06d46db.png\\\" width=\\\"3%\\\" alt=\\\"\\\" /></a>\\n\",\n        \"  <img src=\\\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\\\" width=\\\"3%\\\" alt=\\\"\\\" />\\n\",\n        \"  <a href=\\\"https://www.youtube.com/openmmlab\\\" style=\\\"text-decoration:none;\\\">\\n\",\n        \"    <img src=\\\"https://user-images.githubusercontent.com/25839884/218346691-ceb2116a-465a-40af-8424-9f30d2348ca9.png\\\" width=\\\"3%\\\" alt=\\\"\\\" /></a>\\n\",\n        \"  <img src=\\\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\\\" width=\\\"3%\\\" alt=\\\"\\\" />\\n\",\n        \"  <a href=\\\"https://space.bilibili.com/1293512903\\\" style=\\\"text-decoration:none;\\\">\\n\",\n        \"    <img src=\\\"https://user-images.githubusercontent.com/25839884/219026751-d7d14cce-a7c9-4e82-9942-8375fca65b99.png\\\" width=\\\"3%\\\" alt=\\\"\\\" /></a>\\n\",\n        \"  <img src=\\\"https://user-images.githubusercontent.com/25839884/218346358-56cc8e2f-a2b8-487f-9088-32480cceabcf.png\\\" width=\\\"3%\\\" alt=\\\"\\\" />\\n\",\n        \"  <a href=\\\"https://www.zhihu.com/people/openmmlab\\\" style=\\\"text-decoration:none;\\\">\\n\",\n        \"    <img src=\\\"https://user-images.githubusercontent.com/25839884/219026120-ba71e48b-6e94-4bd4-b4e9-b7d175b5e362.png\\\" width=\\\"3%\\\" alt=\\\"\\\" /></a>\\n\",\n        \"</div>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"aGYwt_UjIrqp\"\n      },\n      \"source\": [\n        \"# Inferencer\\n\",\n        \"\\n\",\n        \"In this tutorial, you will learn how to perform inference with a MMDetection `DetInferencer`.\\n\",\n        \"\\n\",\n        \"Let's start!\\n\",\n        \"\\n\",\n        \"```{note}\\n\",\n        \"The commands in this tutorial are mainly for Colab.\\n\",\n        \"You can click the button above, `Open in Colab`, to run this notebook in Colab.\\n\",\n        \"```\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tJxJHruNLb7Y\"\n      },\n      \"source\": [\n        \"## Install MMDetection\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"Wi4LPmsR66sy\",\n        \"outputId\": \"0077a9a3-0183-4002-fe7a-2a12f020cf69\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Check nvcc version\\n\",\n        \"!nvcc -V\\n\",\n        \"# Check GCC version\\n\",\n        \"!gcc --version\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"gkGnB9WyHSXB\",\n        \"outputId\": \"5945fe0b-13a5-4f1b-dff9-8beb1df67ab0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# install dependencies\\n\",\n        \"%pip install -U openmim\\n\",\n        \"!mim install \\\"mmengine>=0.7.0\\\"\\n\",\n        \"!mim install \\\"mmcv>=2.0.0rc4\\\"\\n\",\n        \"\\n\",\n        \"# Install mmdetection\\n\",\n        \"!rm -rf mmdetection\\n\",\n        \"!git clone https://github.com/open-mmlab/mmdetection.git -b dev-3.x\\n\",\n        \"%cd mmdetection\\n\",\n        \"\\n\",\n        \"%pip install -e .\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"_YeUiqAoCaoV\",\n        \"outputId\": \"06e4c803-ac46-49e6-b8fa-1a85c23fa482\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from mmengine.utils import get_git_hash\\n\",\n        \"from mmengine.utils.dl_utils import collect_env as collect_base_env\\n\",\n        \"\\n\",\n        \"import mmdet\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"def collect_env():\\n\",\n        \"    \\\"\\\"\\\"Collect the information of the running environments.\\\"\\\"\\\"\\n\",\n        \"    env_info = collect_base_env()\\n\",\n        \"    env_info['MMDetection'] = f'{mmdet.__version__}+{get_git_hash()[:7]}'\\n\",\n        \"    return env_info\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"if __name__ == '__main__':\\n\",\n        \"    for name, val in collect_env().items():\\n\",\n        \"        print(f'{name}: {val}')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fLgFRMtP91ue\"\n      },\n      \"source\": [\n        \"## `DetInferencer`\\n\",\n        \"\\n\",\n        \"### Basic Usage\\n\",\n        \"\\n\",\n        \"We use the high-level API `DetInferencer` implemented in the MMDetection. This API is created to ease the inference process. The details of the codes can be found [here](https://github.com/open-mmlab/mmdetection/blob/dev-3.x/mmdet/apis/det_inferencer.py).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000,\n          \"referenced_widgets\": [\n            \"6fa2cda48fda43f9bf53a0f533392eba\",\n            \"0226fedc26044ab2abdccc4fcbe226f8\"\n          ]\n        },\n        \"id\": \"WJHpC402p2w9\",\n        \"outputId\": \"c2326326-d198-4fce-ec0e-a9cc2e35ba09\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from mmdet.apis import DetInferencer\\n\",\n        \"\\n\",\n        \"# Initialize the DetInferencer\\n\",\n        \"inferencer = DetInferencer('rtmdet_tiny_8xb32-300e_coco')\\n\",\n        \"\\n\",\n        \"# Perform inference\\n\",\n        \"inferencer('demo/demo.jpg', out_dir='./output')\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 444\n        },\n        \"id\": \"34JfPWRRSlNh\",\n        \"outputId\": \"8eec8bc4-4824-47ac-b10f-41538422fb28\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Show the output image\\n\",\n        \"from PIL import Image\\n\",\n        \"Image.open('./output/vis/demo.jpg')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-53WPeyBqRHe\"\n      },\n      \"source\": [\n        \"### Initialization\\n\",\n        \"\\n\",\n        \"Each Inferencer must be initialized with a model. You can also choose the inference device during initialization.\\n\",\n        \"\\n\",\n        \"#### Model Initialization\\n\",\n        \"\\n\",\n        \"- To infer with MMDetection's pre-trained model, passing its name to the argument `model` can work. The weights will be automatically downloaded and loaded from OpenMMLab's model zoo.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"bbMu3IPtv-cX\",\n        \"outputId\": \"2bceb594-06c8-4c18-e8c6-b1816b0acb23\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AwKtnol3TQlM\"\n      },\n      \"source\": [\n        \"There is a very easy to list all model names in MMDetection.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"3kYfK3ssTIQE\",\n        \"outputId\": \"88c4fbb9-bb92-42af-baaa-14cee5b5bdc1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# models is a list of model names, and them will print automatically\\n\",\n        \"models = DetInferencer.list_models('mmdet')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"G-25HR9HTZvr\"\n      },\n      \"source\": [\n        \"You can load another weight by passing its path/url to `weights`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"j4doHX4exvS1\",\n        \"outputId\": \"54ac0be2-835f-4390-aa0e-3be5236d8cc9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!mkdir ./checkpoints\\n\",\n        \"!mim download mmdet --config rtmdet_tiny_8xb32-300e_coco --dest ./checkpoints\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"8LQB4EC-Tako\",\n        \"outputId\": \"2cc0960e-d5b5-4c3a-8a0c-cec23989f6a0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Setup a checkpoint file to load\\n\",\n        \"checkpoint = './checkpoints/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth'\\n\",\n        \"\\n\",\n        \"# Initialize the DetInferencer\\n\",\n        \"inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', weights=checkpoint)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Atft9tjcwgeD\"\n      },\n      \"source\": [\n        \"- To load custom config and weight, you can pass the path to the config file to `model` and the path to the weight to `weights`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"eukDD4Rzwp9P\",\n        \"outputId\": \"0a34392c-0544-4a90-c844-7628d184efc0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Choose to use a config\\n\",\n        \"config_path = './configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py'\\n\",\n        \"\\n\",\n        \"# Setup a checkpoint file to load\\n\",\n        \"checkpoint = './checkpoints/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth'\\n\",\n        \"\\n\",\n        \"# Initialize the DetInferencer\\n\",\n        \"inferencer = DetInferencer(model=config_path, weights=checkpoint)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FC1je9iiTuMS\"\n      },\n      \"source\": [\n        \"- By default, [MMEngine](https://github.com/open-mmlab/mmengine/) dumps config to the weight. If you have a weight trained on MMEngine, you can also pass the path to the weight file to `weights` without specifying `model`:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"Kenyo80RTx63\",\n        \"outputId\": \"46fe8219-d1a7-4e45-b5a1-b6d21c30be42\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# It will raise an error if the config file cannot be found in the weight. Currently, within the MMDetection model repository, only the weights of ddq-detr-4scale_r50 can be loaded in this manner.\\n\",\n        \"inferencer = DetInferencer(weights='https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq-detr-4scale_r50_8xb2-12e_coco/ddq-detr-4scale_r50_8xb2-12e_coco_20230809_170711-42528127.pth')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"b-AlYOw4T3AO\"\n      },\n      \"source\": [\n        \"- Passing config file to `model` without specifying `weight` will result in a randomly initialized model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"quFQ8abYT6As\"\n      },\n      \"source\": [\n        \"### Device\\n\",\n        \"\\n\",\n        \"Each Inferencer instance is bound to a device.\\n\",\n        \"By default, the best device is automatically decided by [MMEngine](https://github.com/open-mmlab/mmengine/). You can also alter the device by specifying the `device` argument. For example, you can use the following code to create an Inferencer on GPU 0.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"Wi6DRpsQPEmV\",\n        \"outputId\": \"9eac2017-cce6-491a-ef51-3e7e2560f107\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', device='cuda:0')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"h3pgIACHUXEv\"\n      },\n      \"source\": [\n        \"To create an Inferencer on CPU:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"JsAotaiRUXWH\",\n        \"outputId\": \"531b1cb0-e986-4e0a-91c9-6d3ad65544e7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', device='cpu')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"0a4Zw5plUisX\"\n      },\n      \"source\": [\n        \"### Inference\\n\",\n        \"\\n\",\n        \"Once the Inferencer is initialized, you can directly pass in the raw data to be inferred and get the inference results from return values.\\n\",\n        \"\\n\",\n        \"#### Input\\n\",\n        \"\\n\",\n        \"Input can be either of these types:\\n\",\n        \"\\n\",\n        \"- str: Path/URL to the image.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000,\n          \"referenced_widgets\": [\n            \"2abd7eef6f1f4b9c865a466b3dd5ef24\",\n            \"8951ec1ee7164f7ca7239a37e80e98ea\"\n          ]\n        },\n        \"id\": \"C4McAmYdUnCL\",\n        \"outputId\": \"50bea3e2-a912-497e-cee9-26109dccdc12\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', device='cuda:0')\\n\",\n        \"inferencer('demo/demo.jpg')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"3G_TPKrMUp2T\"\n      },\n      \"source\": [\n        \"- array: Image in numpy array. It should be in BGR order.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000,\n          \"referenced_widgets\": [\n            \"59bfd22c751f4ed4baefa466e7653315\",\n            \"0164804ae2f842fe8d2a4c5414c4a0c2\"\n          ]\n        },\n        \"id\": \"-M1qGlfaUpha\",\n        \"outputId\": \"5a06cfe8-e056-4d56-c8e9-489e8f6633a0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import mmcv\\n\",\n        \"array = mmcv.imread('demo/demo.jpg')\\n\",\n        \"inferencer(array)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"I45B_CtzUuh2\"\n      },\n      \"source\": [\n        \"- list: A list of basic types above. Each element in the list will be processed separately.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000,\n          \"referenced_widgets\": [\n            \"a64a6eb038c44236b80579b2bfc4b8e3\",\n            \"eef25a0509854f98883395a2c0fc2134\",\n            \"f87f0b153b0342ad99dcd320a1302c92\",\n            \"f6634888109048069b6844e9f9b4ec13\"\n          ]\n        },\n        \"id\": \"k1IXIWXHUwKP\",\n        \"outputId\": \"0af73b0b-d703-4cbc-91ad-052f0b521d50\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"inferencer(['tests/data/color.jpg', 'tests/data/gray.jpg'])\\n\",\n        \"# You can even mix the types\\n\",\n        \"inferencer(['tests/data/color.jpg', array])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hUGrTtxrVBAS\"\n      },\n      \"source\": [\n        \"- str: Path to the directory. All images in the directory will be processed.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000,\n          \"referenced_widgets\": [\n            \"07ed8efcd87a40059af36f0c43ef5147\",\n            \"69ce7e58e27f4e1186ab0afcb99d37c3\"\n          ]\n        },\n        \"id\": \"JWK10ZD6VDDE\",\n        \"outputId\": \"91418597-d9ea-4613-b141-16bc8bcc8caf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"inferencer('tests/data/')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BQxEVr2pVGen\"\n      },\n      \"source\": [\n        \"### Output\\n\",\n        \"\\n\",\n        \"By default, each `Inferencer` returns the prediction results in a dictionary format.\\n\",\n        \"\\n\",\n        \"- `visualization` contains the visualized predictions.\\n\",\n        \"\\n\",\n        \"- `predictions` contains the predictions results in a json-serializable format. But it's an empty list by default unless `return_vis=True`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 306,\n          \"referenced_widgets\": [\n            \"95674a6baa1842d2981fe60b31ab6cad\",\n            \"7e62816d1f6c441fb98c1f8e942fff1d\"\n          ]\n        },\n        \"id\": \"m6a8T4goU8Sq\",\n        \"outputId\": \"6f74098f-a3d3-4897-c58f-68fae88889af\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Show the structure of result dict\\n\",\n        \"from rich.pretty import pprint\\n\",\n        \"\\n\",\n        \"result = inferencer('demo/demo.jpg')\\n\",\n        \"pprint(result, max_length=4)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a93hFT0jVkrR\"\n      },\n      \"source\": [\n        \"If you wish to get the raw outputs from the model, you can set `return_datasamples` to `True` to get the original `DataSample`, which will be stored in `predictions`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000,\n          \"referenced_widgets\": [\n            \"060f510b5bda498583d7212060bb528c\",\n            \"1bb724cb12c240a18f651dd99842e5b0\"\n          ]\n        },\n        \"id\": \"U5DFI7QAVbnP\",\n        \"outputId\": \"effaf3ec-2476-4b64-dcbd-802a18a26479\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"result = inferencer('demo/demo.jpg', return_datasamples=True)\\n\",\n        \"pprint(result, max_length=4)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JHdcUnGzVsk1\"\n      },\n      \"source\": [\n        \"#### Dumping Results\\n\",\n        \"\\n\",\n        \"Apart from obtaining predictions from the return value, you can also export the predictions/visualizations to files by setting `out_dir` and `no_save_pred`/`no_save_vis` arguments.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000,\n          \"referenced_widgets\": [\n            \"38083c2f29604d1d9a7dcf9845dfbf33\",\n            \"54cdfe55e0f04df9ab844961a089fe2f\"\n          ]\n        },\n        \"id\": \"0dr-ixmfVtng\",\n        \"outputId\": \"af22d458-9aed-41e2-f675-e017a0cb588b\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"inferencer('demo/demo.jpg', out_dir='outputs/', no_save_pred=False)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"provenance\": []\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"language\": \"python\",\n      \"name\": \"python3\"\n    },\n    \"language_info\": {\n      \"codemirror_mode\": {\n        \"name\": \"ipython\",\n        \"version\": 3\n      },\n      \"file_extension\": \".py\",\n      \"mimetype\": \"text/x-python\",\n      \"name\": \"python\",\n      \"nbconvert_exporter\": \"python\",\n      \"pygments_lexer\": \"ipython3\",\n      \"version\": \"3.8.13\"\n    },\n    \"widgets\": {\n      \"application/vnd.jupyter.widget-state+json\": {\n        \"0164804ae2f842fe8d2a4c5414c4a0c2\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"0226fedc26044ab2abdccc4fcbe226f8\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"060f510b5bda498583d7212060bb528c\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_1bb724cb12c240a18f651dd99842e5b0\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span>  <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m  \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"07ed8efcd87a40059af36f0c43ef5147\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_69ce7e58e27f4e1186ab0afcb99d37c3\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span> <span style=\\\"color: #800080; text-decoration-color: #800080\\\">13.5 it/s</span> <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m \\u001b[35m13.5 it/s\\u001b[0m \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"1bb724cb12c240a18f651dd99842e5b0\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"2abd7eef6f1f4b9c865a466b3dd5ef24\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_8951ec1ee7164f7ca7239a37e80e98ea\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span>  <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m  \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"38083c2f29604d1d9a7dcf9845dfbf33\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_54cdfe55e0f04df9ab844961a089fe2f\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span>  <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m  \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"54cdfe55e0f04df9ab844961a089fe2f\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"59bfd22c751f4ed4baefa466e7653315\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_0164804ae2f842fe8d2a4c5414c4a0c2\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span>  <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m  \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"69ce7e58e27f4e1186ab0afcb99d37c3\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"6fa2cda48fda43f9bf53a0f533392eba\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_0226fedc26044ab2abdccc4fcbe226f8\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span>  <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m  \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"7e62816d1f6c441fb98c1f8e942fff1d\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"8951ec1ee7164f7ca7239a37e80e98ea\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"95674a6baa1842d2981fe60b31ab6cad\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_7e62816d1f6c441fb98c1f8e942fff1d\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span>  <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m  \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"a64a6eb038c44236b80579b2bfc4b8e3\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_eef25a0509854f98883395a2c0fc2134\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span> <span style=\\\"color: #800080; text-decoration-color: #800080\\\">9.7 it/s</span> <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m \\u001b[35m9.7 it/s\\u001b[0m \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        },\n        \"eef25a0509854f98883395a2c0fc2134\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"f6634888109048069b6844e9f9b4ec13\": {\n          \"model_module\": \"@jupyter-widgets/base\",\n          \"model_module_version\": \"1.2.0\",\n          \"model_name\": \"LayoutModel\",\n          \"state\": {\n            \"_model_module\": \"@jupyter-widgets/base\",\n            \"_model_module_version\": \"1.2.0\",\n            \"_model_name\": \"LayoutModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/base\",\n            \"_view_module_version\": \"1.2.0\",\n            \"_view_name\": \"LayoutView\",\n            \"align_content\": null,\n            \"align_items\": null,\n            \"align_self\": null,\n            \"border\": null,\n            \"bottom\": null,\n            \"display\": null,\n            \"flex\": null,\n            \"flex_flow\": null,\n            \"grid_area\": null,\n            \"grid_auto_columns\": null,\n            \"grid_auto_flow\": null,\n            \"grid_auto_rows\": null,\n            \"grid_column\": null,\n            \"grid_gap\": null,\n            \"grid_row\": null,\n            \"grid_template_areas\": null,\n            \"grid_template_columns\": null,\n            \"grid_template_rows\": null,\n            \"height\": null,\n            \"justify_content\": null,\n            \"justify_items\": null,\n            \"left\": null,\n            \"margin\": null,\n            \"max_height\": null,\n            \"max_width\": null,\n            \"min_height\": null,\n            \"min_width\": null,\n            \"object_fit\": null,\n            \"object_position\": null,\n            \"order\": null,\n            \"overflow\": null,\n            \"overflow_x\": null,\n            \"overflow_y\": null,\n            \"padding\": null,\n            \"right\": null,\n            \"top\": null,\n            \"visibility\": null,\n            \"width\": null\n          }\n        },\n        \"f87f0b153b0342ad99dcd320a1302c92\": {\n          \"model_module\": \"@jupyter-widgets/output\",\n          \"model_module_version\": \"1.0.0\",\n          \"model_name\": \"OutputModel\",\n          \"state\": {\n            \"_dom_classes\": [],\n            \"_model_module\": \"@jupyter-widgets/output\",\n            \"_model_module_version\": \"1.0.0\",\n            \"_model_name\": \"OutputModel\",\n            \"_view_count\": null,\n            \"_view_module\": \"@jupyter-widgets/output\",\n            \"_view_module_version\": \"1.0.0\",\n            \"_view_name\": \"OutputView\",\n            \"layout\": \"IPY_MODEL_f6634888109048069b6844e9f9b4ec13\",\n            \"msg_id\": \"\",\n            \"outputs\": [\n              {\n                \"data\": {\n                  \"text/html\": \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">Inference <span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #f92672; text-decoration-color: #f92672\\\">━</span><span style=\\\"color: #f42670; text-decoration-color: #f42670\\\">━</span><span style=\\\"color: #e6276c; text-decoration-color: #e6276c\\\">━</span><span style=\\\"color: #d12a66; text-decoration-color: #d12a66\\\">━</span><span style=\\\"color: #b72c5e; text-decoration-color: #b72c5e\\\">━</span><span style=\\\"color: #993056; text-decoration-color: #993056\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #3a3a3a; text-decoration-color: #3a3a3a\\\">━</span><span style=\\\"color: #3e393b; text-decoration-color: #3e393b\\\">━</span><span style=\\\"color: #4c383f; text-decoration-color: #4c383f\\\">━</span><span style=\\\"color: #613545; text-decoration-color: #613545\\\">━</span><span style=\\\"color: #7b334d; text-decoration-color: #7b334d\\\">━</span> <span style=\\\"color: #800080; text-decoration-color: #800080\\\">9.0 it/s</span> <span style=\\\"color: #008080; text-decoration-color: #008080\\\"> </span>\\n</pre>\\n\",\n                  \"text/plain\": \"Inference \\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;249;38;114m━\\u001b[0m\\u001b[38;2;244;38;112m━\\u001b[0m\\u001b[38;2;230;39;108m━\\u001b[0m\\u001b[38;2;209;42;102m━\\u001b[0m\\u001b[38;2;183;44;94m━\\u001b[0m\\u001b[38;2;153;48;86m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;58;58;58m━\\u001b[0m\\u001b[38;2;62;57;59m━\\u001b[0m\\u001b[38;2;76;56;63m━\\u001b[0m\\u001b[38;2;97;53;69m━\\u001b[0m\\u001b[38;2;123;51;77m━\\u001b[0m \\u001b[35m9.0 it/s\\u001b[0m \\u001b[36m \\u001b[0m\\n\"\n                },\n                \"metadata\": {},\n                \"output_type\": \"display_data\"\n              }\n            ]\n          }\n        }\n      }\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "demo/large_image_demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Perform MMDET inference on large images (as satellite imagery) as:\n\n```shell\nwget -P checkpoint https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_2x_coco/faster_rcnn_r101_fpn_2x_coco_bbox_mAP-0.398_20200504_210455-1d2dac9c.pth # noqa: E501, E261.\n\npython demo/large_image_demo.py \\\n    demo/large_image.jpg \\\n    configs/faster_rcnn/faster-rcnn_r101_fpn_2x_coco.py \\\n    checkpoint/faster_rcnn_r101_fpn_2x_coco_bbox_mAP-0.398_20200504_210455-1d2dac9c.pth\n```\n\"\"\"\n\nimport os\nimport random\nfrom argparse import ArgumentParser\nfrom pathlib import Path\n\nimport mmcv\nimport numpy as np\nfrom mmengine.config import Config, ConfigDict\nfrom mmengine.logging import print_log\nfrom mmengine.utils import ProgressBar\n\nfrom mmdet.apis import inference_detector, init_detector\n\ntry:\n    from sahi.slicing import slice_image\nexcept ImportError:\n    raise ImportError('Please run \"pip install -U sahi\" '\n                      'to install sahi first for large image inference.')\n\nfrom mmdet.registry import VISUALIZERS\nfrom mmdet.utils.large_image import merge_results_by_nms, shift_predictions\nfrom mmdet.utils.misc import get_file_list\n\n\ndef parse_args():\n    parser = ArgumentParser(\n        description='Perform MMDET inference on large images.')\n    parser.add_argument(\n        'img', help='Image path, include image file, dir and URL.')\n    parser.add_argument('config', help='Config file')\n    parser.add_argument('checkpoint', help='Checkpoint file')\n    parser.add_argument(\n        '--out-dir', default='./output', help='Path to output file')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--show', action='store_true', help='Show the detection results')\n    parser.add_argument(\n        '--tta',\n        action='store_true',\n        help='Whether to use test time augmentation')\n    parser.add_argument(\n        '--score-thr', type=float, default=0.3, help='Bbox score threshold')\n    parser.add_argument(\n        '--patch-size', type=int, default=640, help='The size of patches')\n    parser.add_argument(\n        '--patch-overlap-ratio',\n        type=float,\n        default=0.25,\n        help='Ratio of overlap between two patches')\n    parser.add_argument(\n        '--merge-iou-thr',\n        type=float,\n        default=0.25,\n        help='IoU threshould for merging results')\n    parser.add_argument(\n        '--merge-nms-type',\n        type=str,\n        default='nms',\n        help='NMS type for merging results')\n    parser.add_argument(\n        '--batch-size',\n        type=int,\n        default=1,\n        help='Batch size, must greater than or equal to 1')\n    parser.add_argument(\n        '--debug',\n        action='store_true',\n        help='Export debug results before merging')\n    parser.add_argument(\n        '--save-patch',\n        action='store_true',\n        help='Save the results of each patch. '\n        'The `--debug` must be enabled.')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    config = args.config\n\n    if isinstance(config, (str, Path)):\n        config = Config.fromfile(config)\n    elif not isinstance(config, Config):\n        raise TypeError('config must be a filename or Config object, '\n                        f'but got {type(config)}')\n    if 'init_cfg' in config.model.backbone:\n        config.model.backbone.init_cfg = None\n\n    if args.tta:\n        assert 'tta_model' in config, 'Cannot find ``tta_model`` in config.' \\\n                                      \" Can't use tta !\"\n        assert 'tta_pipeline' in config, 'Cannot find ``tta_pipeline`` ' \\\n                                         \"in config. Can't use tta !\"\n        config.model = ConfigDict(**config.tta_model, module=config.model)\n        test_data_cfg = config.test_dataloader.dataset\n        while 'dataset' in test_data_cfg:\n            test_data_cfg = test_data_cfg['dataset']\n\n        test_data_cfg.pipeline = config.tta_pipeline\n\n    # TODO: TTA mode will error if cfg_options is not set.\n    #  This is an mmdet issue and needs to be fixed later.\n    # build the model from a config file and a checkpoint file\n    model = init_detector(\n        config, args.checkpoint, device=args.device, cfg_options={})\n\n    if not os.path.exists(args.out_dir) and not args.show:\n        os.mkdir(args.out_dir)\n\n    # init visualizer\n    visualizer = VISUALIZERS.build(model.cfg.visualizer)\n    visualizer.dataset_meta = model.dataset_meta\n\n    # get file list\n    files, source_type = get_file_list(args.img)\n\n    # start detector inference\n    print(f'Performing inference on {len(files)} images.... '\n          'This may take a while.')\n    progress_bar = ProgressBar(len(files))\n    for file in files:\n        # read image\n        img = mmcv.imread(file)\n\n        # arrange slices\n        height, width = img.shape[:2]\n        sliced_image_object = slice_image(\n            img,\n            slice_height=args.patch_size,\n            slice_width=args.patch_size,\n            auto_slice_resolution=False,\n            overlap_height_ratio=args.patch_overlap_ratio,\n            overlap_width_ratio=args.patch_overlap_ratio,\n        )\n        # perform sliced inference\n        slice_results = []\n        start = 0\n        while True:\n            # prepare batch slices\n            end = min(start + args.batch_size, len(sliced_image_object))\n            images = []\n            for sliced_image in sliced_image_object.images[start:end]:\n                images.append(sliced_image)\n\n            # forward the model\n            slice_results.extend(inference_detector(model, images))\n\n            if end >= len(sliced_image_object):\n                break\n            start += args.batch_size\n\n        if source_type['is_dir']:\n            filename = os.path.relpath(file, args.img).replace('/', '_')\n        else:\n            filename = os.path.basename(file)\n\n        img = mmcv.imconvert(img, 'bgr', 'rgb')\n        out_file = None if args.show else os.path.join(args.out_dir, filename)\n\n        # export debug images\n        if args.debug:\n            # export sliced image results\n            name, suffix = os.path.splitext(filename)\n\n            shifted_instances = shift_predictions(\n                slice_results,\n                sliced_image_object.starting_pixels,\n                src_image_shape=(height, width))\n            merged_result = slice_results[0].clone()\n            merged_result.pred_instances = shifted_instances\n\n            debug_file_name = name + '_debug' + suffix\n            debug_out_file = None if args.show else os.path.join(\n                args.out_dir, debug_file_name)\n            visualizer.set_image(img.copy())\n\n            debug_grids = []\n            for starting_point in sliced_image_object.starting_pixels:\n                start_point_x = starting_point[0]\n                start_point_y = starting_point[1]\n                end_point_x = start_point_x + args.patch_size\n                end_point_y = start_point_y + args.patch_size\n                debug_grids.append(\n                    [start_point_x, start_point_y, end_point_x, end_point_y])\n            debug_grids = np.array(debug_grids)\n            debug_grids[:, 0::2] = np.clip(debug_grids[:, 0::2], 1,\n                                           img.shape[1] - 1)\n            debug_grids[:, 1::2] = np.clip(debug_grids[:, 1::2], 1,\n                                           img.shape[0] - 1)\n\n            palette = np.random.randint(0, 256, size=(len(debug_grids), 3))\n            palette = [tuple(c) for c in palette]\n            line_styles = random.choices(['-', '-.', ':'], k=len(debug_grids))\n            visualizer.draw_bboxes(\n                debug_grids,\n                edge_colors=palette,\n                alpha=1,\n                line_styles=line_styles)\n            visualizer.draw_bboxes(\n                debug_grids, face_colors=palette, alpha=0.15)\n\n            visualizer.draw_texts(\n                list(range(len(debug_grids))),\n                debug_grids[:, :2] + 5,\n                colors='w')\n\n            visualizer.add_datasample(\n                debug_file_name,\n                visualizer.get_image(),\n                data_sample=merged_result,\n                draw_gt=False,\n                show=args.show,\n                wait_time=0,\n                out_file=debug_out_file,\n                pred_score_thr=args.score_thr,\n            )\n\n            if args.save_patch:\n                debug_patch_out_dir = os.path.join(args.out_dir,\n                                                   f'{name}_patch')\n                for i, slice_result in enumerate(slice_results):\n                    patch_out_file = os.path.join(\n                        debug_patch_out_dir,\n                        f'{filename}_slice_{i}_result.jpg')\n                    image = mmcv.imconvert(sliced_image_object.images[i],\n                                           'bgr', 'rgb')\n\n                    visualizer.add_datasample(\n                        'patch_result',\n                        image,\n                        data_sample=slice_result,\n                        draw_gt=False,\n                        show=False,\n                        wait_time=0,\n                        out_file=patch_out_file,\n                        pred_score_thr=args.score_thr,\n                    )\n\n        image_result = merge_results_by_nms(\n            slice_results,\n            sliced_image_object.starting_pixels,\n            src_image_shape=(height, width),\n            nms_cfg={\n                'type': args.merge_nms_type,\n                'iou_threshold': args.merge_iou_thr\n            })\n\n        visualizer.add_datasample(\n            filename,\n            img,\n            data_sample=image_result,\n            draw_gt=False,\n            show=args.show,\n            wait_time=0,\n            out_file=out_file,\n            pred_score_thr=args.score_thr,\n        )\n        progress_bar.update()\n\n    if not args.show or (args.debug and args.save_patch):\n        print_log(\n            f'\\nResults have been saved at {os.path.abspath(args.out_dir)}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "demo/mot_demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport os.path as osp\nimport tempfile\nfrom argparse import ArgumentParser\n\nimport mmcv\nimport mmengine\nfrom mmengine.registry import init_default_scope\n\nfrom mmdet.apis import inference_mot, init_track_model\nfrom mmdet.registry import VISUALIZERS\n\nIMG_EXTENSIONS = ('.jpg', '.jpeg', '.png')\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument(\n        'inputs', type=str, help='Input image file or folder path.')\n    parser.add_argument('config', help='config file')\n    parser.add_argument('--checkpoint', help='checkpoint file')\n    parser.add_argument('--detector', help='det checkpoint file')\n    parser.add_argument('--reid', help='reid checkpoint file')\n    parser.add_argument(\n        '--device', default='cuda:0', help='device used for inference')\n    parser.add_argument(\n        '--score-thr',\n        type=float,\n        default=0.0,\n        help='The threshold of score to filter bboxes.')\n    parser.add_argument(\n        '--out', help='output video file (mp4 format) or folder')\n    parser.add_argument(\n        '--show',\n        action='store_true',\n        help='whether show the results on the fly')\n    parser.add_argument('--fps', help='FPS of the output video')\n    args = parser.parse_args()\n    return args\n\n\ndef main(args):\n    assert args.out or args.show\n    # load images\n    if osp.isdir(args.inputs):\n        imgs = sorted(\n            filter(lambda x: x.endswith(IMG_EXTENSIONS),\n                   os.listdir(args.inputs)),\n            key=lambda x: int(x.split('.')[0]))\n        in_video = False\n    else:\n        imgs = mmcv.VideoReader(args.inputs)\n        in_video = True\n\n    # define output\n    out_video = False\n    if args.out is not None:\n        if args.out.endswith('.mp4'):\n            out_video = True\n            out_dir = tempfile.TemporaryDirectory()\n            out_path = out_dir.name\n            _out = args.out.rsplit(os.sep, 1)\n            if len(_out) > 1:\n                os.makedirs(_out[0], exist_ok=True)\n        else:\n            out_path = args.out\n            os.makedirs(out_path, exist_ok=True)\n\n    fps = args.fps\n    if args.show or out_video:\n        if fps is None and in_video:\n            fps = imgs.fps\n        if not fps:\n            raise ValueError('Please set the FPS for the output video.')\n        fps = int(fps)\n\n    init_default_scope('mmdet')\n\n    # build the model from a config file and a checkpoint file\n    model = init_track_model(\n        args.config,\n        args.checkpoint,\n        args.detector,\n        args.reid,\n        device=args.device)\n\n    # build the visualizer\n    visualizer = VISUALIZERS.build(model.cfg.visualizer)\n    visualizer.dataset_meta = model.dataset_meta\n\n    prog_bar = mmengine.ProgressBar(len(imgs))\n    # test and show/save the images\n    for i, img in enumerate(imgs):\n        if isinstance(img, str):\n            img_path = osp.join(args.inputs, img)\n            img = mmcv.imread(img_path)\n        # result [TrackDataSample]\n        result = inference_mot(model, img, frame_id=i, video_len=len(imgs))\n        if args.out is not None:\n            if in_video or out_video:\n                out_file = osp.join(out_path, f'{i:06d}.jpg')\n            else:\n                out_file = osp.join(out_path, img.rsplit(os.sep, 1)[-1])\n        else:\n            out_file = None\n\n        # show the results\n        visualizer.add_datasample(\n            'mot',\n            img[..., ::-1],\n            data_sample=result[0],\n            show=args.show,\n            draw_gt=False,\n            out_file=out_file,\n            wait_time=float(1 / int(fps)) if fps else 0,\n            pred_score_thr=args.score_thr,\n            step=i)\n\n        prog_bar.update()\n\n    if args.out and out_video:\n        print(f'making the output video at {args.out} with a FPS of {fps}')\n        mmcv.frames2video(out_path, args.out, fps=fps, fourcc='mp4v')\n        out_dir.cleanup()\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    main(args)\n"
  },
  {
    "path": "demo/video_demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\n\nimport cv2\nimport mmcv\nfrom mmcv.transforms import Compose\nfrom mmengine.utils import track_iter_progress\n\nfrom mmdet.apis import inference_detector, init_detector\nfrom mmdet.registry import VISUALIZERS\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='MMDetection video demo')\n    parser.add_argument('video', help='Video file')\n    parser.add_argument('config', help='Config file')\n    parser.add_argument('checkpoint', help='Checkpoint file')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--score-thr', type=float, default=0.3, help='Bbox score threshold')\n    parser.add_argument('--out', type=str, help='Output video file')\n    parser.add_argument('--show', action='store_true', help='Show video')\n    parser.add_argument(\n        '--wait-time',\n        type=float,\n        default=1,\n        help='The interval of show (s), 0 is block')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    assert args.out or args.show, \\\n        ('Please specify at least one operation (save/show the '\n         'video) with the argument \"--out\" or \"--show\"')\n\n    # build the model from a config file and a checkpoint file\n    model = init_detector(args.config, args.checkpoint, device=args.device)\n\n    # build test pipeline\n    model.cfg.test_dataloader.dataset.pipeline[\n        0].type = 'mmdet.LoadImageFromNDArray'\n    test_pipeline = Compose(model.cfg.test_dataloader.dataset.pipeline)\n\n    # init visualizer\n    visualizer = VISUALIZERS.build(model.cfg.visualizer)\n    # the dataset_meta is loaded from the checkpoint and\n    # then pass to the model in init_detector\n    visualizer.dataset_meta = model.dataset_meta\n\n    video_reader = mmcv.VideoReader(args.video)\n    video_writer = None\n    if args.out:\n        fourcc = cv2.VideoWriter_fourcc(*'mp4v')\n        video_writer = cv2.VideoWriter(\n            args.out, fourcc, video_reader.fps,\n            (video_reader.width, video_reader.height))\n\n    for frame in track_iter_progress((video_reader, len(video_reader))):\n        result = inference_detector(model, frame, test_pipeline=test_pipeline)\n        visualizer.add_datasample(\n            name='video',\n            image=frame,\n            data_sample=result,\n            draw_gt=False,\n            show=False,\n            pred_score_thr=args.score_thr)\n        frame = visualizer.get_image()\n\n        if args.show:\n            cv2.namedWindow('video', 0)\n            mmcv.imshow(frame, 'video', args.wait_time)\n        if args.out:\n            video_writer.write(frame)\n\n    if video_writer:\n        video_writer.release()\n    cv2.destroyAllWindows()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "demo/video_gpuaccel_demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nfrom typing import Tuple\n\nimport cv2\nimport mmcv\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom mmcv.transforms import Compose\nfrom mmengine.utils import track_iter_progress\n\nfrom mmdet.apis import init_detector\nfrom mmdet.registry import VISUALIZERS\nfrom mmdet.structures import DetDataSample\n\ntry:\n    import ffmpegcv\nexcept ImportError:\n    raise ImportError(\n        'Please install ffmpegcv with:\\n\\n    pip install ffmpegcv')\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMDetection video demo with GPU acceleration')\n    parser.add_argument('video', help='Video file')\n    parser.add_argument('config', help='Config file')\n    parser.add_argument('checkpoint', help='Checkpoint file')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--score-thr', type=float, default=0.3, help='Bbox score threshold')\n    parser.add_argument('--out', type=str, help='Output video file')\n    parser.add_argument('--show', action='store_true', help='Show video')\n    parser.add_argument(\n        '--nvdecode', action='store_true', help='Use NVIDIA decoder')\n    parser.add_argument(\n        '--wait-time',\n        type=float,\n        default=1,\n        help='The interval of show (s), 0 is block')\n    args = parser.parse_args()\n    return args\n\n\ndef prefetch_batch_input_shape(model: nn.Module, ori_wh: Tuple[int,\n                                                               int]) -> dict:\n    cfg = model.cfg\n    w, h = ori_wh\n    cfg.test_dataloader.dataset.pipeline[0].type = 'LoadImageFromNDArray'\n    test_pipeline = Compose(cfg.test_dataloader.dataset.pipeline)\n    data = {'img': np.zeros((h, w, 3), dtype=np.uint8), 'img_id': 0}\n    data = test_pipeline(data)\n    data['inputs'] = [data['inputs']]\n    data['data_samples'] = [data['data_samples']]\n    data_sample = model.data_preprocessor(data, False)['data_samples']\n    batch_input_shape = data_sample[0].batch_input_shape\n    return batch_input_shape\n\n\ndef pack_data(frame_resize: np.ndarray, batch_input_shape: Tuple[int, int],\n              ori_shape: Tuple[int, int]) -> dict:\n    assert frame_resize.shape[:2] == batch_input_shape\n    data_sample = DetDataSample()\n    data_sample.set_metainfo({\n        'img_shape':\n        batch_input_shape,\n        'ori_shape':\n        ori_shape,\n        'scale_factor': (batch_input_shape[0] / ori_shape[0],\n                         batch_input_shape[1] / ori_shape[1])\n    })\n    frame_resize = torch.from_numpy(frame_resize).permute((2, 0, 1)).cuda()\n    data = {'inputs': [frame_resize], 'data_samples': [data_sample]}\n    return data\n\n\ndef main():\n    args = parse_args()\n    assert args.out or args.show, \\\n        ('Please specify at least one operation (save/show the '\n         'video) with the argument \"--out\" or \"--show\"')\n\n    model = init_detector(args.config, args.checkpoint, device=args.device)\n\n    # init visualizer\n    visualizer = VISUALIZERS.build(model.cfg.visualizer)\n    # the dataset_meta is loaded from the checkpoint and\n    # then pass to the model in init_detector\n    visualizer.dataset_meta = model.dataset_meta\n\n    if args.nvdecode:\n        VideoCapture = ffmpegcv.VideoCaptureNV\n    else:\n        VideoCapture = ffmpegcv.VideoCapture\n    video_origin = VideoCapture(args.video)\n\n    batch_input_shape = prefetch_batch_input_shape(\n        model, (video_origin.width, video_origin.height))\n    ori_shape = (video_origin.height, video_origin.width)\n    resize_wh = batch_input_shape[::-1]\n    video_resize = VideoCapture(\n        args.video,\n        resize=resize_wh,\n        resize_keepratio=True,\n        resize_keepratioalign='topleft')\n\n    video_writer = None\n    if args.out:\n        video_writer = ffmpegcv.VideoWriter(args.out, fps=video_origin.fps)\n\n    with torch.no_grad():\n        for i, (frame_resize, frame_origin) in enumerate(\n                zip(track_iter_progress(video_resize), video_origin)):\n            data = pack_data(frame_resize, batch_input_shape, ori_shape)\n            result = model.test_step(data)[0]\n\n            visualizer.add_datasample(\n                name='video',\n                image=frame_origin,\n                data_sample=result,\n                draw_gt=False,\n                show=False,\n                pred_score_thr=args.score_thr)\n\n            frame_mask = visualizer.get_image()\n\n            if args.show:\n                cv2.namedWindow('video', 0)\n                mmcv.imshow(frame_mask, 'video', args.wait_time)\n            if args.out:\n                video_writer.write(frame_mask)\n\n    if video_writer:\n        video_writer.release()\n    video_origin.release()\n    video_resize.release()\n\n    cv2.destroyAllWindows()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "demo/webcam_demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\n\nimport cv2\nimport mmcv\nimport torch\n\nfrom mmdet.apis import inference_detector, init_detector\nfrom mmdet.registry import VISUALIZERS\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='MMDetection webcam demo')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('checkpoint', help='checkpoint file')\n    parser.add_argument(\n        '--device', type=str, default='cuda:0', help='CPU/CUDA device option')\n    parser.add_argument(\n        '--camera-id', type=int, default=0, help='camera device id')\n    parser.add_argument(\n        '--score-thr', type=float, default=0.5, help='bbox score threshold')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    # build the model from a config file and a checkpoint file\n    device = torch.device(args.device)\n    model = init_detector(args.config, args.checkpoint, device=device)\n\n    # init visualizer\n    visualizer = VISUALIZERS.build(model.cfg.visualizer)\n    # the dataset_meta is loaded from the checkpoint and\n    # then pass to the model in init_detector\n    visualizer.dataset_meta = model.dataset_meta\n\n    camera = cv2.VideoCapture(args.camera_id)\n\n    print('Press \"Esc\", \"q\" or \"Q\" to exit.')\n    while True:\n        ret_val, img = camera.read()\n        result = inference_detector(model, img)\n\n        img = mmcv.imconvert(img, 'bgr', 'rgb')\n        visualizer.add_datasample(\n            name='result',\n            image=img,\n            data_sample=result,\n            draw_gt=False,\n            pred_score_thr=args.score_thr,\n            show=False)\n\n        img = visualizer.get_image()\n        img = mmcv.imconvert(img, 'bgr', 'rgb')\n        cv2.imshow('result', img)\n\n        ch = cv2.waitKey(1)\n        if ch == 27 or ch == ord('q') or ch == ord('Q'):\n            break\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "ARG PYTORCH=\"1.9.0\"\nARG CUDA=\"11.1\"\nARG CUDNN=\"8\"\n\nFROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel\n\nENV TORCH_CUDA_ARCH_LIST=\"6.0 6.1 7.0 7.5 8.0 8.6+PTX\" \\\n    TORCH_NVCC_FLAGS=\"-Xfatbin -compress-all\" \\\n    CMAKE_PREFIX_PATH=\"$(dirname $(which conda))/../\" \\\n    FORCE_CUDA=\"1\"\n\n# Avoid Public GPG key error\n# https://github.com/NVIDIA/nvidia-docker/issues/1631\nRUN rm /etc/apt/sources.list.d/cuda.list \\\n    && rm /etc/apt/sources.list.d/nvidia-ml.list \\\n    && apt-key del 7fa2af80 \\\n    && apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub \\\n    && apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub\n\n# (Optional, use Mirror to speed up downloads)\n# RUN sed -i 's/http:\\/\\/archive.ubuntu.com\\/ubuntu\\//http:\\/\\/mirrors.aliyun.com\\/ubuntu\\//g' /etc/apt/sources.list && \\\n#    pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple\n\n# Install the required packages\nRUN apt-get update \\\n    && apt-get install -y ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \\\n    && apt-get clean \\\n    && rm -rf /var/lib/apt/lists/*\n\n# Install MMEngine and MMCV\nRUN pip install openmim && \\\n    mim install \"mmengine>=0.7.1\" \"mmcv>=2.0.0rc4\"\n\n# Install MMDetection\nRUN conda clean --all \\\n    && git clone https://github.com/open-mmlab/mmdetection.git /mmdetection \\\n    && cd /mmdetection \\\n    && pip install --no-cache-dir -e .\n\nWORKDIR /mmdetection\n"
  },
  {
    "path": "docker/serve/Dockerfile",
    "content": "ARG PYTORCH=\"1.9.0\"\nARG CUDA=\"11.1\"\nARG CUDNN=\"8\"\nFROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel\n\nARG MMCV=\"2.0.0rc4\"\nARG MMDET=\"3.3.0\"\n\nENV PYTHONUNBUFFERED TRUE\n\n# Avoid Public GPG key error\n# https://github.com/NVIDIA/nvidia-docker/issues/1631\nRUN rm /etc/apt/sources.list.d/cuda.list \\\n    && rm /etc/apt/sources.list.d/nvidia-ml.list \\\n    && apt-key del 7fa2af80 \\\n    && apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub \\\n    && apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub\n\n# (Optional, use Mirror to speed up downloads)\n# RUN sed -i 's/http:\\/\\/archive.ubuntu.com\\/ubuntu\\//http:\\/\\/mirrors.aliyun.com\\/ubuntu\\//g' /etc/apt/sources.list\n\n# Install the required packages\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \\\n    ca-certificates \\\n    g++ \\\n    openjdk-11-jre-headless \\\n    # MMDet Requirements\n    ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \\\n    && rm -rf /var/lib/apt/lists/*\n\nENV PATH=\"/opt/conda/bin:$PATH\" \\\n    FORCE_CUDA=\"1\"\n\n# TORCHSEVER\nRUN pip install torchserve torch-model-archiver\n\n# MMLAB\nARG PYTORCH\nARG CUDA\nRUN pip install mmengine\nRUN [\"/bin/bash\", \"-c\", \"pip install mmcv==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html\"]\nRUN pip install mmdet==${MMDET}\n\nRUN useradd -m model-server \\\n    && mkdir -p /home/model-server/tmp\n\nCOPY entrypoint.sh /usr/local/bin/entrypoint.sh\n\nRUN chmod +x /usr/local/bin/entrypoint.sh \\\n    && chown -R model-server /home/model-server\n\nCOPY config.properties /home/model-server/config.properties\nRUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store\n\nEXPOSE 8080 8081 8082\n\nUSER model-server\nWORKDIR /home/model-server\nENV TEMP=/home/model-server/tmp\nENTRYPOINT [\"/usr/local/bin/entrypoint.sh\"]\nCMD [\"serve\"]\n"
  },
  {
    "path": "docker/serve/config.properties",
    "content": "inference_address=http://0.0.0.0:8080\nmanagement_address=http://0.0.0.0:8081\nmetrics_address=http://0.0.0.0:8082\nmodel_store=/home/model-server/model-store\nload_models=all\n"
  },
  {
    "path": "docker/serve/entrypoint.sh",
    "content": "#!/bin/bash\nset -e\n\nif [[ \"$1\" = \"serve\" ]]; then\n    shift 1\n    torchserve --start --ts-config /home/model-server/config.properties\nelse\n    eval \"$@\"\nfi\n\n# prevent docker exit\ntail -f /dev/null\n"
  },
  {
    "path": "docker/serve_cn/Dockerfile",
    "content": "ARG PYTORCH=\"1.9.0\"\nARG CUDA=\"11.1\"\nARG CUDNN=\"8\"\nFROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel\n\nARG MMCV=\"2.0.0rc4\"\nARG MMDET=\"3.3.0\"\n\nENV PYTHONUNBUFFERED TRUE\n\n# Avoid Public GPG key error\n# - https://github.com/NVIDIA/nvidia-docker/issues/1631\nRUN rm /etc/apt/sources.list.d/cuda.list \\\n  && rm /etc/apt/sources.list.d/nvidia-ml.list \\\n  && apt-get update \\\n  && apt-get install -y wget \\\n  && rm -rf /var/lib/apt/lists/* \\\n  && apt-key del 7fa2af80 \\\n  && apt-get update && apt-get install -y --no-install-recommends wget \\\n  && wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-keyring_1.0-1_all.deb \\\n  && dpkg -i cuda-keyring_1.0-1_all.deb\n# (Optional, use Mirror to speed up downloads)\n# RUN sed -i 's/http:\\/\\/archive.ubuntu.com\\/ubuntu\\//http:\\/\\/mirrors.aliyun.com\\/ubuntu\\//g' /etc/apt/sources.list\n\n# Install the required packages\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \\\n    ca-certificates \\\n    g++ \\\n    openjdk-11-jre-headless \\\n    # MMDet Requirements\n    ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \\\n    && rm -rf /var/lib/apt/lists/*\n\nENV PATH=\"/opt/conda/bin:$PATH\" \\\n    FORCE_CUDA=\"1\"\n\n# TORCHSEVER\nRUN pip install torchserve torch-model-archiver nvgpu -i https://pypi.mirrors.ustc.edu.cn/simple/\n\n# MMLAB\nARG PYTORCH\nARG CUDA\nRUN pip install mmengine -i https://pypi.mirrors.ustc.edu.cn/simple/\nRUN [\"/bin/bash\", \"-c\", \"pip install mmcv==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html\"]\nRUN pip install mmdet==${MMDET} -i https://pypi.mirrors.ustc.edu.cn/simple/\n\nRUN useradd -m model-server \\\n    && mkdir -p /home/model-server/tmp\n\nCOPY entrypoint.sh /usr/local/bin/entrypoint.sh\n\nRUN chmod +x /usr/local/bin/entrypoint.sh \\\n    && chown -R model-server /home/model-server\n\nCOPY config.properties /home/model-server/config.properties\nRUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store\n\nEXPOSE 8080 8081 8082\n\nUSER model-server\nWORKDIR /home/model-server\nENV TEMP=/home/model-server/tmp\nENTRYPOINT [\"/usr/local/bin/entrypoint.sh\"]\nCMD [\"serve\"]\n"
  },
  {
    "path": "docs/en/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    ?=\nSPHINXBUILD   ?= sphinx-build\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "docs/en/_static/css/readthedocs.css",
    "content": ".header-logo {\n    background-image: url(\"../image/mmdet-logo.png\");\n    background-size: 156px 40px;\n    height: 40px;\n    width: 156px;\n}\n"
  },
  {
    "path": "docs/en/advanced_guides/conventions.md",
    "content": "# Conventions\n\nPlease check the following conventions if you would like to modify MMDetection as your own project.\n\n## About the order of image shape\n\nIn OpenMMLab 2.0, to be consistent with the input argument of OpenCV, the argument about image shape in the data transformation pipeline is always in the `(width, height)` order. On the contrary, for computation convenience, the order of the field going through the data pipeline and the model is `(height, width)`. Specifically, in the results processed by each data transform pipeline, the fields and their value meaning is as below:\n\n- img_shape: (height, width)\n- ori_shape: (height, width)\n- pad_shape: (height, width)\n- batch_input_shape: (height, width)\n\nAs an example, the initialization arguments of `Mosaic` are as below:\n\n```python\n@TRANSFORMS.register_module()\nclass Mosaic(BaseTransform):\n    def __init__(self,\n                img_scale: Tuple[int, int] = (640, 640),\n                center_ratio_range: Tuple[float, float] = (0.5, 1.5),\n                bbox_clip_border: bool = True,\n                pad_val: float = 114.0,\n                prob: float = 1.0) -> None:\n       ...\n\n       # img_scale order should be (width, height)\n       self.img_scale = img_scale\n\n    def transform(self, results: dict) -> dict:\n        ...\n\n        results['img'] = mosaic_img\n        # (height, width)\n        results['img_shape'] = mosaic_img.shape[:2]\n```\n\n## Loss\n\nIn MMDetection, a `dict` containing losses and metrics will be returned by `model(**data)`.\n\nFor example, in bbox head,\n\n```python\nclass BBoxHead(nn.Module):\n    ...\n    def loss(self, ...):\n        losses = dict()\n        # classification loss\n        losses['loss_cls'] = self.loss_cls(...)\n        # classification accuracy\n        losses['acc'] = accuracy(...)\n        # bbox regression loss\n        losses['loss_bbox'] = self.loss_bbox(...)\n        return losses\n```\n\n`bbox_head.loss()` will be called during model forward.\nThe returned dict contains `'loss_bbox'`, `'loss_cls'`, `'acc'` .\nOnly `'loss_bbox'`, `'loss_cls'` will be used during back propagation,\n`'acc'` will only be used as a metric to monitor training process.\n\nBy default, only values whose keys contain `'loss'` will be back propagated.\nThis behavior could be changed by modifying `BaseDetector.train_step()`.\n\n## Empty Proposals\n\nIn MMDetection, We have added special handling and unit test for empty proposals of two-stage. We need to deal with the empty proposals of the entire batch and single image at the same time. For example, in CascadeRoIHead,\n\n```python\n# simple_test method\n...\n# There is no proposal in the whole batch\nif rois.shape[0] == 0:\n    bbox_results = [[\n        np.zeros((0, 5), dtype=np.float32)\n        for _ in range(self.bbox_head[-1].num_classes)\n    ]] * num_imgs\n    if self.with_mask:\n        mask_classes = self.mask_head[-1].num_classes\n        segm_results = [[[] for _ in range(mask_classes)]\n                        for _ in range(num_imgs)]\n        results = list(zip(bbox_results, segm_results))\n    else:\n        results = bbox_results\n    return results\n...\n\n# There is no proposal in the single image\nfor i in range(self.num_stages):\n    ...\n    if i < self.num_stages - 1:\n          for j in range(num_imgs):\n                # Handle empty proposal\n                if rois[j].shape[0] > 0:\n                    bbox_label = cls_score[j][:, :-1].argmax(dim=1)\n                    refine_roi = self.bbox_head[i].regress_by_class(\n                         rois[j], bbox_label, bbox_pred[j], img_metas[j])\n                    refine_roi_list.append(refine_roi)\n```\n\nIf you have customized `RoIHead`, you can refer to the above method to deal with empty proposals.\n\n## Coco Panoptic Dataset\n\nIn MMDetection, we have supported COCO Panoptic dataset. We clarify a few conventions about the implementation of `CocoPanopticDataset` here.\n\n1. For mmdet\\<=2.16.0, the range of foreground and background labels in semantic segmentation are different from the default setting of MMDetection. The label `0` stands for `VOID` label and the category labels start from `1`.\n   Since mmdet=2.17.0, the category labels of semantic segmentation start from `0` and label `255` stands for `VOID` for consistency with labels of bounding boxes.\n   To achieve that, the `Pad` pipeline supports setting the padding value for `seg`.\n2. In the evaluation, the panoptic result is a map with the same shape as the original image. Each value in the result map has the format of `instance_id * INSTANCE_OFFSET + category_id`.\n"
  },
  {
    "path": "docs/en/advanced_guides/customize_dataset.md",
    "content": "# Customize Datasets\n\n## Support new data format\n\nTo support a new data format, you can either convert them to existing formats (COCO format or PASCAL format) or directly convert them to the middle format. You could also choose to convert them offline (before training by a script) or online (implement a new dataset and do the conversion at training). In MMDetection, we recommend to convert the data into COCO formats and do the conversion offline, thus you only need to modify the config's data annotation paths and classes after the conversion of your data.\n\n### Reorganize new data formats to existing format\n\nThe simplest way is to convert your dataset to existing dataset formats (COCO or PASCAL VOC).\n\nThe annotation JSON files in COCO format has the following necessary keys:\n\n```python\n'images': [\n    {\n        'file_name': 'COCO_val2014_000000001268.jpg',\n        'height': 427,\n        'width': 640,\n        'id': 1268\n    },\n    ...\n],\n\n'annotations': [\n    {\n        'segmentation': [[192.81,\n            247.09,\n            ...\n            219.03,\n            249.06]],  # If you have mask labels, and it is in polygon XY point coordinate format, you need to ensure that at least 3 point coordinates are included. Otherwise, it is an invalid polygon.\n        'area': 1035.749,\n        'iscrowd': 0,\n        'image_id': 1268,\n        'bbox': [192.81, 224.8, 74.73, 33.43],\n        'category_id': 16,\n        'id': 42986\n    },\n    ...\n],\n\n'categories': [\n    {'id': 0, 'name': 'car'},\n ]\n```\n\nThere are three necessary keys in the JSON file:\n\n- `images`: contains a list of images with their information like `file_name`, `height`, `width`, and `id`.\n- `annotations`: contains the list of instance annotations.\n- `categories`: contains the list of categories names and their ID.\n\nAfter the data pre-processing, there are two steps for users to train the customized new dataset with existing format (e.g. COCO format):\n\n1. Modify the config file for using the customized dataset.\n2. Check the annotations of the customized dataset.\n\nHere we give an example to show the above two steps, which uses a customized dataset of 5 classes with COCO format to train an existing Cascade Mask R-CNN R50-FPN detector.\n\n#### 1. Modify the config file for using the customized dataset\n\nThere are two aspects involved in the modification of config file:\n\n1. The `data` field. Specifically, you need to explicitly add the `metainfo=dict(classes=classes)` fields in `train_dataloader.dataset`, `val_dataloader.dataset` and `test_dataloader.dataset` and `classes` must be a tuple type.\n2. The `num_classes` field in the `model` part. Explicitly over-write all the `num_classes` from default value (e.g. 80 in COCO) to your classes number.\n\nIn `configs/my_custom_config.py`:\n\n```python\n\n# the new config inherits the base configs to highlight the necessary modification\n_base_ = './cascade_mask_rcnn_r50_fpn_1x_coco.py'\n\n# 1. dataset settings\ndataset_type = 'CocoDataset'\nclasses = ('a', 'b', 'c', 'd', 'e')\ndata_root='path/to/your/'\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    dataset=dict(\n        type=dataset_type,\n        # explicitly add your class names to the field `metainfo`\n        metainfo=dict(classes=classes),\n        data_root=data_root,\n        ann_file='train/annotation_data',\n        data_prefix=dict(img='train/image_data')\n        )\n    )\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    dataset=dict(\n        type=dataset_type,\n        test_mode=True,\n        # explicitly add your class names to the field `metainfo`\n        metainfo=dict(classes=classes),\n        data_root=data_root,\n        ann_file='val/annotation_data',\n        data_prefix=dict(img='val/image_data')\n        )\n    )\n\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    dataset=dict(\n        type=dataset_type,\n        test_mode=True,\n        # explicitly add your class names to the field `metainfo`\n        metainfo=dict(classes=classes),\n        data_root=data_root,\n        ann_file='test/annotation_data',\n        data_prefix=dict(img='test/image_data')\n        )\n    )\n\n# 2. model settings\n\n# explicitly over-write all the `num_classes` field from default 80 to 5.\nmodel = dict(\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                # explicitly over-write all the `num_classes` field from default 80 to 5.\n                num_classes=5),\n            dict(\n                type='Shared2FCBBoxHead',\n                # explicitly over-write all the `num_classes` field from default 80 to 5.\n                num_classes=5),\n            dict(\n                type='Shared2FCBBoxHead',\n                # explicitly over-write all the `num_classes` field from default 80 to 5.\n                num_classes=5)],\n    # explicitly over-write all the `num_classes` field from default 80 to 5.\n    mask_head=dict(num_classes=5)))\n```\n\n#### 2. Check the annotations of the customized dataset\n\nAssuming your customized dataset is COCO format, make sure you have the correct annotations in the customized dataset:\n\n1. The length for `categories` field in annotations should exactly equal the tuple length of `classes` fields in your config, meaning the number of classes (e.g. 5 in this example).\n2. The `classes` fields in your config file should have exactly the same elements and the same order with the `name` in `categories` of annotations. MMDetection automatically maps the uncontinuous `id` in `categories` to the continuous label indices, so the string order of `name` in `categories` field affects the order of label indices. Meanwhile, the string order of `classes` in config affects the label text during visualization of predicted bounding boxes.\n3. The `category_id` in `annotations` field should be valid, i.e., all values in `category_id` should belong to `id` in `categories`.\n\nHere is a valid example of annotations:\n\n```python\n\n'annotations': [\n    {\n        'segmentation': [[192.81,\n            247.09,\n            ...\n            219.03,\n            249.06]],  # if you have mask labels\n        'area': 1035.749,\n        'iscrowd': 0,\n        'image_id': 1268,\n        'bbox': [192.81, 224.8, 74.73, 33.43],\n        'category_id': 16,\n        'id': 42986\n    },\n    ...\n],\n\n# MMDetection automatically maps the uncontinuous `id` to the continuous label indices.\n'categories': [\n    {'id': 1, 'name': 'a'}, {'id': 3, 'name': 'b'}, {'id': 4, 'name': 'c'}, {'id': 16, 'name': 'd'}, {'id': 17, 'name': 'e'},\n ]\n```\n\nWe use this way to support CityScapes dataset. The script is in [cityscapes.py](../../../tools/dataset_converters/cityscapes.py) and we also provide the finetuning [configs](../../../configs/cityscapes).\n\n**Note**\n\n1. For instance segmentation datasets, **MMDetection only supports evaluating mask AP of dataset in COCO format for now**.\n2. It is recommended to convert the data offline before training, thus you can still use `CocoDataset` and only need to modify the path of annotations and the training classes.\n\n### Reorganize new data format to middle format\n\nIt is also fine if you do not want to convert the annotation format to COCO or PASCAL format.\nActually, we define a simple annotation format in MMEninge's [BaseDataset](https://github.com/open-mmlab/mmengine/blob/main/mmengine/dataset/base_dataset.py#L116) and all existing datasets are\nprocessed to be compatible with it, either online or offline.\n\nThe annotation of the dataset must be in `json` or `yaml`, `yml` or `pickle`, `pkl` format; the dictionary stored in the annotation file must contain two fields `metainfo` and `data_list`.  The `metainfo` is a dictionary, which contains the metadata of the dataset, such as class information; `data_list` is a list, each element in the list is a dictionary, the dictionary defines the raw data of one image, and each raw data contains a or several training/testing samples.\n\nHere is an example.\n\n```python\n{\n    'metainfo':\n        {\n            'classes': ('person', 'bicycle', 'car', 'motorcycle'),\n            ...\n        },\n    'data_list':\n        [\n            {\n                \"img_path\": \"xxx/xxx_1.jpg\",\n                \"height\": 604,\n                \"width\": 640,\n                \"instances\":\n                [\n                  {\n                    \"bbox\": [0, 0, 10, 20],\n                    \"bbox_label\": 1,\n                    \"ignore_flag\": 0\n                  },\n                  {\n                    \"bbox\": [10, 10, 110, 120],\n                    \"bbox_label\": 2,\n                    \"ignore_flag\": 0\n                  }\n                ]\n              },\n            {\n                \"img_path\": \"xxx/xxx_2.jpg\",\n                \"height\": 320,\n                \"width\": 460,\n                \"instances\":\n                [\n                  {\n                    \"bbox\": [10, 0, 20, 20],\n                    \"bbox_label\": 3,\n                    \"ignore_flag\": 1,\n                  }\n                ]\n              },\n            ...\n        ]\n}\n```\n\nSome datasets may provide annotations like crowd/difficult/ignored bboxes, we use `ignore_flag`to cover them.\n\nAfter obtaining the above standard data annotation format, you can directly use [BaseDetDataset](../../../mmdet/datasets/base_det_dataset.py#L13) of MMDetection in the configuration , without conversion.\n\n### An example of customized dataset\n\nAssume the annotation is in a new format in text files.\nThe bounding boxes annotations are stored in text file `annotation.txt` as the following\n\n```\n#\n000001.jpg\n1280 720\n2\n10 20 40 60 1\n20 40 50 60 2\n#\n000002.jpg\n1280 720\n3\n50 20 40 60 2\n20 40 30 45 2\n30 40 50 60 3\n```\n\nWe can create a new dataset in `mmdet/datasets/my_dataset.py` to load the data.\n\n```python\nimport mmengine\n\nfrom mmdet.base_det_dataset import BaseDetDataset\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass MyDataset(BaseDetDataset):\n\n    METAINFO = {\n       'classes': ('person', 'bicycle', 'car', 'motorcycle'),\n        'palette': [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230)]\n    }\n\n    def load_data_list(self, ann_file):\n        ann_list = mmengine.list_from_file(ann_file)\n\n        data_infos = []\n        for i, ann_line in enumerate(ann_list):\n            if ann_line != '#':\n                continue\n\n            img_shape = ann_list[i + 2].split(' ')\n            width = int(img_shape[0])\n            height = int(img_shape[1])\n            bbox_number = int(ann_list[i + 3])\n\n            instances = []\n            for anns in ann_list[i + 4:i + 4 + bbox_number]:\n                instance = {}\n                instance['bbox'] = [float(ann) for ann in anns.split(' ')[:4]]\n                instance['bbox_label']=int(anns[4])\n \t\t\t\tinstances.append(instance)\n\n            data_infos.append(\n                dict(\n                    img_path=ann_list[i + 1],\n                    img_id=i,\n                    width=width,\n                    height=height,\n                    instances=instances\n                ))\n\n        return data_infos\n```\n\nThen in the config, to use `MyDataset` you can modify the config as the following\n\n```python\ndataset_A_train = dict(\n    type='MyDataset',\n    ann_file = 'image_list.txt',\n    pipeline=train_pipeline\n)\n```\n\n## Customize datasets by dataset wrappers\n\nMMEngine also supports many dataset wrappers to mix the dataset or modify the dataset distribution for training.\nCurrently it supports to three dataset wrappers as below:\n\n- `RepeatDataset`: simply repeat the whole dataset.\n- `ClassBalancedDataset`: repeat dataset in a class balanced manner.\n- `ConcatDataset`: concat datasets.\n\nFor detailed usage, see [MMEngine Dataset Wrapper](#TODO).\n\n## Modify Dataset Classes\n\nWith existing dataset types, we can modify the metainfo of them to train subset of the annotations.\nFor example, if you want to train only three classes of the current dataset,\nyou can modify the classes of dataset.\nThe dataset will filter out the ground truth boxes of other classes automatically.\n\n```python\nclasses = ('person', 'bicycle', 'car')\ntrain_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(classes=classes))\n    )\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(classes=classes))\n    )\ntest_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(classes=classes))\n    )\n```\n\n**Note**:\n\n- Before MMDetection v2.5.0, the dataset will filter out the empty GT images automatically if the classes are set and there is no way to disable that through config. This is an undesirable behavior and introduces confusion because if the classes are not set, the dataset only filter the empty GT images when `filter_empty_gt=True` and `test_mode=False`. After MMDetection v2.5.0, we decouple the image filtering process and the classes modification, i.e., the dataset will only filter empty GT images when `filter_cfg=dict(filter_empty_gt=True)` and `test_mode=False`, no matter whether the classes are set. Thus, setting the classes only influences the annotations of classes used for training and users could decide whether to filter empty GT images by themselves.\n- When directly using `BaseDataset` in MMEngine or `BaseDetDataset` in MMDetection, users cannot filter images without GT by modifying the configuration, but it can be solved in an offline way.\n- Please remember to modify the `num_classes` in the head when specifying `classes` in dataset. We implemented [NumClassCheckHook](../../../mmdet/engine/hooks/num_class_check_hook.py) to check whether the numbers are consistent since v2.9.0(after PR#4508).\n\n## COCO Panoptic Dataset\n\nNow we support COCO Panoptic Dataset, the format of panoptic annotations is different from COCO format.\nBoth the foreground and the background will exist in the annotation file.\nThe annotation json files in COCO Panoptic format has the following necessary keys:\n\n```python\n'images': [\n    {\n        'file_name': '000000001268.jpg',\n        'height': 427,\n        'width': 640,\n        'id': 1268\n    },\n    ...\n]\n\n'annotations': [\n    {\n        'filename': '000000001268.jpg',\n        'image_id': 1268,\n        'segments_info': [\n            {\n                'id':8345037,  # One-to-one correspondence with the id in the annotation map.\n                'category_id': 51,\n                'iscrowd': 0,\n                'bbox': (x1, y1, w, h),  # The bbox of the background is the outer rectangle of its mask.\n                'area': 24315\n            },\n            ...\n        ]\n    },\n    ...\n]\n\n'categories': [  # including both foreground categories and background categories\n    {'id': 0, 'name': 'person'},\n    ...\n ]\n```\n\nMoreover, the `seg` must be set to the path of the panoptic annotation images.\n\n```python\ndataset_type = 'CocoPanopticDataset'\ndata_root='path/to/your/'\n\ntrain_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img='train/image_data/', seg='train/panoptic/image_annotation_data/')\n    )\n)\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img='val/image_data/', seg='val/panoptic/image_annotation_data/')\n    )\n)\ntest_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img='test/image_data/', seg='test/panoptic/image_annotation_data/')\n    )\n)\n```\n"
  },
  {
    "path": "docs/en/advanced_guides/customize_losses.md",
    "content": "# Customize Losses\n\nMMDetection provides users with different loss functions. But the default configuration may be not applicable for different datasets or models, so users may want to modify a specific loss to adapt the new situation.\n\nThis tutorial first elaborate the computation pipeline of losses, then give some instructions about how to modify each step. The modification can be categorized as tweaking and weighting.\n\n## Computation pipeline of a loss\n\nGiven the input prediction and target, as well as the weights, a loss function maps the input tensor to the final loss scalar. The mapping can be divided into five steps:\n\n1. Set the sampling method to sample positive and negative samples.\n\n2. Get **element-wise** or **sample-wise** loss by the loss kernel function.\n\n3. Weighting the loss with a weight tensor **element-wisely**.\n\n4. Reduce the loss tensor to a **scalar**.\n\n5. Weighting the loss with a **scalar**.\n\n## Set sampling method (step 1)\n\nFor some loss functions, sampling strategies are needed to avoid imbalance between positive and negative samples.\n\nFor example, when using `CrossEntropyLoss` in RPN head, we need to set `RandomSampler` in `train_cfg`\n\n```python\ntrain_cfg=dict(\n    rpn=dict(\n        sampler=dict(\n            type='RandomSampler',\n            num=256,\n            pos_fraction=0.5,\n            neg_pos_ub=-1,\n            add_gt_as_proposals=False))\n```\n\nFor some other losses which have positive and negative sample balance mechanism such as Focal Loss, GHMC, and QualityFocalLoss, the sampler is no more necessary.\n\n## Tweaking loss\n\nTweaking a loss is more related with step 2, 4, 5, and most modifications can be specified in the config.\nHere we take [Focal Loss (FL)](../../../mmdet/models/losses/focal_loss.py) as an example.\nThe following code sniper are the construction method and config of FL respectively, they are actually one to one correspondence.\n\n```python\n@LOSSES.register_module()\nclass FocalLoss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid=True,\n                 gamma=2.0,\n                 alpha=0.25,\n                 reduction='mean',\n                 loss_weight=1.0):\n```\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=2.0,\n    alpha=0.25,\n    loss_weight=1.0)\n```\n\n### Tweaking hyper-parameters (step 2)\n\n`gamma` and `beta` are two hyper-parameters in the Focal Loss. Say if we want to change the value of `gamma` to be 1.5 and `alpha` to be 0.5, then we can specify them in the config as follows:\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=1.5,\n    alpha=0.5,\n    loss_weight=1.0)\n```\n\n### Tweaking the way of reduction (step 3)\n\nThe default way of reduction is `mean` for FL. Say if we want to change the reduction from `mean` to `sum`, we can specify it in the config as follows:\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=2.0,\n    alpha=0.25,\n    loss_weight=1.0,\n    reduction='sum')\n```\n\n### Tweaking loss weight (step 5)\n\nThe loss weight here is a scalar which controls the weight of different losses in multi-task learning, e.g. classification loss and regression loss. Say if we want to change to loss weight of classification loss to be 0.5, we can specify it in the config as follows:\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=2.0,\n    alpha=0.25,\n    loss_weight=0.5)\n```\n\n## Weighting loss (step 3)\n\nWeighting loss means we re-weight the loss element-wisely. To be more specific, we multiply the loss tensor with a weight tensor which has the same shape. As a result, different entries of the loss can be scaled differently, and so called element-wisely.\nThe loss weight varies across different models and highly context related, but overall there are two kinds of loss weights, `label_weights` for classification loss and `bbox_weights` for bbox regression loss. You can find them in the `get_target` method of the corresponding head. Here we take [ATSSHead](../../../mmdet/models/dense_heads/atss_head.py#L322) as an example, which inherit [AnchorHead](../../../mmdet/models/dense_heads/anchor_head.py) but overwrite its `get_targets` method which yields different `label_weights` and `bbox_weights`.\n\n```\nclass ATSSHead(AnchorHead):\n\n    ...\n\n    def get_targets(self,\n                    anchor_list,\n                    valid_flag_list,\n                    gt_bboxes_list,\n                    img_metas,\n                    gt_bboxes_ignore_list=None,\n                    gt_labels_list=None,\n                    label_channels=1,\n                    unmap_outputs=True):\n```\n"
  },
  {
    "path": "docs/en/advanced_guides/customize_models.md",
    "content": "# Customize Models\n\nWe basically categorize model components into 5 types.\n\n- backbone: usually an FCN network to extract feature maps, e.g., ResNet, MobileNet.\n- neck: the component between backbones and heads, e.g., FPN, PAFPN.\n- head: the component for specific tasks, e.g., bbox prediction and mask prediction.\n- roi extractor: the part for extracting RoI features from feature maps, e.g., RoI Align.\n- loss: the component in head for calculating losses, e.g., FocalLoss, L1Loss, and GHMLoss.\n\n## Develop new components\n\n### Add a new backbone\n\nHere we show how to develop new components with an example of MobileNet.\n\n#### 1. Define a new backbone (e.g. MobileNet)\n\nCreate a new file `mmdet/models/backbones/mobilenet.py`.\n\n```python\nimport torch.nn as nn\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass MobileNet(nn.Module):\n\n    def __init__(self, arg1, arg2):\n        pass\n\n    def forward(self, x):  # should return a tuple\n        pass\n```\n\n#### 2. Import the module\n\nYou can either add the following line to `mmdet/models/backbones/__init__.py`\n\n```python\nfrom .mobilenet import MobileNet\n```\n\nor alternatively add\n\n```python\ncustom_imports = dict(\n    imports=['mmdet.models.backbones.mobilenet'],\n    allow_failed_imports=False)\n```\n\nto the config file to avoid modifying the original code.\n\n#### 3. Use the backbone in your config file\n\n```python\nmodel = dict(\n    ...\n    backbone=dict(\n        type='MobileNet',\n        arg1=xxx,\n        arg2=xxx),\n    ...\n```\n\n### Add new necks\n\n#### 1. Define a neck (e.g. PAFPN)\n\nCreate a new file `mmdet/models/necks/pafpn.py`.\n\n```python\nimport torch.nn as nn\n\nfrom mmdet.registry import MODELS\n\n@MODELS.register_module()\nclass PAFPN(nn.Module):\n\n    def __init__(self,\n                in_channels,\n                out_channels,\n                num_outs,\n                start_level=0,\n                end_level=-1,\n                add_extra_convs=False):\n        pass\n\n    def forward(self, inputs):\n        # implementation is ignored\n        pass\n```\n\n#### 2. Import the module\n\nYou can either add the following line to `mmdet/models/necks/__init__.py`,\n\n```python\nfrom .pafpn import PAFPN\n```\n\nor alternatively add\n\n```python\ncustom_imports = dict(\n    imports=['mmdet.models.necks.pafpn'],\n    allow_failed_imports=False)\n```\n\nto the config file and avoid modifying the original code.\n\n#### 3. Modify the config file\n\n```python\nneck=dict(\n    type='PAFPN',\n    in_channels=[256, 512, 1024, 2048],\n    out_channels=256,\n    num_outs=5)\n```\n\n### Add new heads\n\nHere we show how to develop a new head with the example of [Double Head R-CNN](https://arxiv.org/abs/1904.06493) as the following.\n\nFirst, add a new bbox head in `mmdet/models/roi_heads/bbox_heads/double_bbox_head.py`.\nDouble Head R-CNN implements a new bbox head for object detection.\nTo implement a bbox head, basically we need to implement three functions of the new module as the following.\n\n```python\nfrom typing import Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule, ModuleList\nfrom torch import Tensor\n\nfrom mmdet.models.backbones.resnet import Bottleneck\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, MultiConfig, OptConfigType, OptMultiConfig\nfrom .bbox_head import BBoxHead\n\n@MODELS.register_module()\nclass DoubleConvFCBBoxHead(BBoxHead):\n    r\"\"\"Bbox head used in Double-Head R-CNN\n\n    .. code-block:: none\n\n                                          /-> cls\n                      /-> shared convs ->\n                                          \\-> reg\n        roi features\n                                          /-> cls\n                      \\-> shared fc    ->\n                                          \\-> reg\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 num_convs: int = 0,\n                 num_fcs: int = 0,\n                 conv_out_channels: int = 1024,\n                 fc_out_channels: int = 1024,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     override=[\n                         dict(type='Normal', name='fc_cls', std=0.01),\n                         dict(type='Normal', name='fc_reg', std=0.001),\n                         dict(\n                             type='Xavier',\n                             name='fc_branch',\n                             distribution='uniform')\n                     ]),\n                 **kwargs) -> None:\n        kwargs.setdefault('with_avg_pool', True)\n        super().__init__(init_cfg=init_cfg, **kwargs)\n\n    def forward(self, x_cls: Tensor, x_reg: Tensor) -> Tuple[Tensor]:\n\n```\n\nSecond, implement a new RoI Head if it is necessary. We plan to inherit the new `DoubleHeadRoIHead` from `StandardRoIHead`. We can find that a `StandardRoIHead` already implements the following functions.\n\n```python\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils import empty_instances, unpack_gt_instances\nfrom .base_roi_head import BaseRoIHead\n\n\n@MODELS.register_module()\nclass StandardRoIHead(BaseRoIHead):\n    \"\"\"Simplest base roi head including one bbox head and one mask head.\"\"\"\n\n    def init_assigner_sampler(self) -> None:\n\n    def init_bbox_head(self, bbox_roi_extractor: ConfigType,\n                       bbox_head: ConfigType) -> None:\n\n    def init_mask_head(self, mask_roi_extractor: ConfigType,\n                       mask_head: ConfigType) -> None:\n\n    def forward(self, x: Tuple[Tensor],\n                rpn_results_list: InstanceList) -> tuple:\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: List[DetDataSample]) -> dict:\n\n    def _bbox_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n\n    def bbox_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult]) -> dict:\n\n    def mask_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult], bbox_feats: Tensor,\n                  batch_gt_instances: InstanceList) -> dict:\n\n    def _mask_forward(self,\n                      x: Tuple[Tensor],\n                      rois: Tensor = None,\n                      pos_inds: Optional[Tensor] = None,\n                      bbox_feats: Optional[Tensor] = None) -> dict:\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False) -> InstanceList:\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: InstanceList,\n                     rescale: bool = False) -> InstanceList:\n\n```\n\nDouble Head's modification is mainly in the `bbox_forward` logic, and it inherits other logics from the `StandardRoIHead`. In the `mmdet/models/roi_heads/double_roi_head.py`, we implement the new RoI Head as the following:\n\n```python\nfrom typing import Tuple\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass DoubleHeadRoIHead(StandardRoIHead):\n    \"\"\"RoI head for `Double Head RCNN <https://arxiv.org/abs/1904.06493>`_.\n\n    Args:\n        reg_roi_scale_factor (float): The scale factor to extend the rois\n            used to extract the regression features.\n    \"\"\"\n\n    def __init__(self, reg_roi_scale_factor: float, **kwargs):\n        super().__init__(**kwargs)\n        self.reg_roi_scale_factor = reg_roi_scale_factor\n\n    def _bbox_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        bbox_cls_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs], rois)\n        bbox_reg_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs],\n            rois,\n            roi_scale_factor=self.reg_roi_scale_factor)\n        if self.with_shared_head:\n            bbox_cls_feats = self.shared_head(bbox_cls_feats)\n            bbox_reg_feats = self.shared_head(bbox_reg_feats)\n        cls_score, bbox_pred = self.bbox_head(bbox_cls_feats, bbox_reg_feats)\n\n        bbox_results = dict(\n            cls_score=cls_score,\n            bbox_pred=bbox_pred,\n            bbox_feats=bbox_cls_feats)\n        return bbox_results\n```\n\nLast, the users need to add the module in\n`mmdet/models/bbox_heads/__init__.py` and `mmdet/models/roi_heads/__init__.py` thus the corresponding registry could find and load them.\n\nAlternatively, the users can add\n\n```python\ncustom_imports=dict(\n    imports=['mmdet.models.roi_heads.double_roi_head', 'mmdet.models.roi_heads.bbox_heads.double_bbox_head'])\n```\n\nto the config file and achieve the same goal.\n\nThe config file of Double Head R-CNN is as the following\n\n```python\n_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        type='DoubleHeadRoIHead',\n        reg_roi_scale_factor=1.3,\n        bbox_head=dict(\n            _delete_=True,\n            type='DoubleConvFCBBoxHead',\n            num_convs=4,\n            num_fcs=2,\n            in_channels=256,\n            conv_out_channels=1024,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=2.0),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=2.0))))\n\n```\n\nSince MMDetection 2.0, the config system supports to inherit configs such that the users can focus on the modification.\nThe Double Head R-CNN mainly uses a new `DoubleHeadRoIHead` and a new `DoubleConvFCBBoxHead `, the arguments are set according to the `__init__` function of each module.\n\n### Add new loss\n\nAssume you want to add a new loss as `MyLoss`, for bounding box regression.\nTo add a new loss function, the users need implement it in `mmdet/models/losses/my_loss.py`.\nThe decorator `weighted_loss` enable the loss to be weighted for each element.\n\n```python\nimport torch\nimport torch.nn as nn\n\nfrom mmdet.registry import MODELS\nfrom .utils import weighted_loss\n\n@weighted_loss\ndef my_loss(pred, target):\n    assert pred.size() == target.size() and target.numel() > 0\n    loss = torch.abs(pred - target)\n    return loss\n\n@MODELS.register_module()\nclass MyLoss(nn.Module):\n\n    def __init__(self, reduction='mean', loss_weight=1.0):\n        super(MyLoss, self).__init__()\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None):\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_bbox = self.loss_weight * my_loss(\n            pred, target, weight, reduction=reduction, avg_factor=avg_factor)\n        return loss_bbox\n```\n\nThen the users need to add it in the `mmdet/models/losses/__init__.py`.\n\n```python\nfrom .my_loss import MyLoss, my_loss\n\n```\n\nAlternatively, you can add\n\n```python\ncustom_imports=dict(\n    imports=['mmdet.models.losses.my_loss'])\n```\n\nto the config file and achieve the same goal.\n\nTo use it, modify the `loss_xxx` field.\nSince MyLoss is for regression, you need to modify the `loss_bbox` field in the head.\n\n```python\nloss_bbox=dict(type='MyLoss', loss_weight=1.0))\n```\n"
  },
  {
    "path": "docs/en/advanced_guides/customize_runtime.md",
    "content": "# Customize Runtime Settings\n\n## Customize optimization settings\n\nOptimization related configuration is now all managed by `optim_wrapper` which usually has three fields: `optimizer`, `paramwise_cfg`, `clip_grad`, refer to [OptimWrapper](https://mmengine.readthedocs.io/en/latest/tutorials/optim_wrapper.md) for more detail. See the example below, where `Adamw` is used as an `optimizer`, the learning rate of the backbone is reduced by a factor of 10, and gradient clipping is added.\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    # optimizer\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,\n        weight_decay=0.05,\n        eps=1e-8,\n        betas=(0.9, 0.999)),\n\n    # Parameter-level learning rate and weight decay settings\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n        },\n        norm_decay_mult=0.0),\n\n    # gradient clipping\n    clip_grad=dict(max_norm=0.01, norm_type=2))\n```\n\n### Customize optimizer supported by Pytorch\n\nWe already support to use all the optimizers implemented by PyTorch, and the only modification is to change the `optimizer` field in `optim_wrapper` field of config files. For example, if you want to use `ADAM` (note that the performance could drop a lot), the modification could be as the following.\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='Adam', lr=0.0003, weight_decay=0.0001))\n```\n\nTo modify the learning rate of the model, the users only need to modify the `lr` in `optimizer`. The users can directly set arguments following the [API doc](https://pytorch.org/docs/stable/optim.html?highlight=optim#module-torch.optim) of PyTorch.\n\n### Customize self-implemented optimizer\n\n#### 1. Define a new optimizer\n\nA customized optimizer could be defined as following.\n\nAssume you want to add a optimizer named `MyOptimizer`, which has arguments `a`, `b`, and `c`.\nYou need to create a new directory named `mmdet/engine/optimizers`. And then implement the new optimizer in a file, e.g., in `mmdet/engine/optimizers/my_optimizer.py`:\n\n```python\nfrom mmdet.registry import OPTIMIZERS\nfrom torch.optim import Optimizer\n\n\n@OPTIMIZERS.register_module()\nclass MyOptimizer(Optimizer):\n\n    def __init__(self, a, b, c)\n\n```\n\n#### 2. Add the optimizer to registry\n\nTo find the above module defined above, this module should be imported into the main namespace at first. There are two options to achieve it.\n\n- Modify `mmdet/engine/optimizers/__init__.py` to import it.\n\n  The newly defined module should be imported in `mmdet/engine/optimizers/__init__.py` so that the registry will find the new module and add it:\n\n```python\nfrom .my_optimizer import MyOptimizer\n```\n\n- Use `custom_imports` in the config to manually import it\n\n```python\ncustom_imports = dict(imports=['mmdet.engine.optimizers.my_optimizer'], allow_failed_imports=False)\n```\n\nThe module `mmdet.engine.optimizers.my_optimizer` will be imported at the beginning of the program and the class `MyOptimizer` is then automatically registered.\nNote that only the package containing the class `MyOptimizer` should be imported.\n`mmdet.engine.optimizers.my_optimizer.MyOptimizer` **cannot** be imported directly.\n\nActually users can use a totally different file directory structure using this importing method, as long as the module root can be located in `PYTHONPATH`.\n\n#### 3. Specify the optimizer in the config file\n\nThen you can use `MyOptimizer` in `optimizer` field in `optim_wrapper` field of config files. In the configs, the optimizers are defined by the field `optimizer` like the following:\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n```\n\nTo use your own optimizer, the field can be changed to\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='MyOptimizer', a=a_value, b=b_value, c=c_value))\n```\n\n### Customize optimizer wrapper constructor\n\nSome models may have some parameter-specific settings for optimization, e.g. weight decay for BatchNorm layers.\nThe users can do those fine-grained parameter tuning through customizing optimizer wrapper constructor.\n\n```python\nfrom mmengine.optim import DefaultOptiWrapperConstructor\n\nfrom mmdet.registry import OPTIM_WRAPPER_CONSTRUCTORS\nfrom .my_optimizer import MyOptimizer\n\n\n@OPTIM_WRAPPER_CONSTRUCTORS.register_module()\nclass MyOptimizerWrapperConstructor(DefaultOptimWrapperConstructor):\n\n    def __init__(self,\n                 optim_wrapper_cfg: dict,\n                 paramwise_cfg: Optional[dict] = None):\n\n    def __call__(self, model: nn.Module) -> OptimWrapper:\n\n        return optim_wrapper\n\n```\n\nThe default optimizer wrapper constructor is implemented [here](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/optimizer/default_constructor.py#L18), which could also serve as a template for the new optimizer wrapper constructor.\n\n### Additional settings\n\nTricks not implemented by the optimizer should be implemented through optimizer wrapper constructor (e.g., set parameter-wise learning rates) or hooks. We list some common settings that could stabilize the training or accelerate the training. Feel free to create PR, issue for more settings.\n\n- __Use gradient clip to stabilize training__:\n  Some models need gradient clip to clip the gradients to stabilize the training process. An example is as below:\n\n  ```python\n  optim_wrapper = dict(\n      _delete_=True, clip_grad=dict(max_norm=35, norm_type=2))\n  ```\n\n  If your config inherits the base config which already sets the `optim_wrapper`, you might need `_delete_=True` to override the unnecessary settings. See the [config documentation](../user_guides/config.md) for more details.\n\n- __Use momentum schedule to accelerate model convergence__:\n  We support momentum scheduler to modify model's momentum according to learning rate, which could make the model converge in a faster way.\n  Momentum scheduler is usually used with LR scheduler, for example, the following config is used in [3D detection](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/_base_/schedules/cyclic-20e.py) to accelerate convergence.\n  For more details, please refer to the implementation of [CosineAnnealingLR](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py#L43) and [CosineAnnealingMomentum](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/momentum_scheduler.py#L71).\n\n  ```python\n  param_scheduler = [\n      # learning rate scheduler\n      # During the first 8 epochs, learning rate increases from 0 to lr * 10\n      # during the next 12 epochs, learning rate decreases from lr * 10 to lr * 1e-4\n      dict(\n          type='CosineAnnealingLR',\n          T_max=8,\n          eta_min=lr * 10,\n          begin=0,\n          end=8,\n          by_epoch=True,\n          convert_to_iter_based=True),\n      dict(\n          type='CosineAnnealingLR',\n          T_max=12,\n          eta_min=lr * 1e-4,\n          begin=8,\n          end=20,\n          by_epoch=True,\n          convert_to_iter_based=True),\n      # momentum scheduler\n      # During the first 8 epochs, momentum increases from 0 to 0.85 / 0.95\n      # during the next 12 epochs, momentum increases from 0.85 / 0.95 to 1\n      dict(\n          type='CosineAnnealingMomentum',\n          T_max=8,\n          eta_min=0.85 / 0.95,\n          begin=0,\n          end=8,\n          by_epoch=True,\n          convert_to_iter_based=True),\n      dict(\n          type='CosineAnnealingMomentum',\n          T_max=12,\n          eta_min=1,\n          begin=8,\n          end=20,\n          by_epoch=True,\n          convert_to_iter_based=True)\n  ]\n  ```\n\n## Customize training schedules\n\nBy default we use step learning rate with 1x schedule, this calls [MultiStepLR](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py#L139) in MMEngine.\nWe support many other learning rate schedule [here](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py), such as `CosineAnnealingLR` and `PolyLR` schedule. Here are some examples\n\n- Poly schedule:\n\n  ```python\n  param_scheduler = [\n      dict(\n          type='PolyLR',\n          power=0.9,\n          eta_min=1e-4,\n          begin=0,\n          end=8,\n          by_epoch=True)]\n  ```\n\n- ConsineAnnealing schedule:\n\n  ```python\n  param_scheduler = [\n      dict(\n          type='CosineAnnealingLR',\n          T_max=8,\n          eta_min=lr * 1e-5,\n          begin=0,\n          end=8,\n          by_epoch=True)]\n\n  ```\n\n## Customize train loop\n\nBy default, `EpochBasedTrainLoop` is used in `train_cfg` and validation is done after every train epoch, as follows.\n\n```python\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_begin=1, val_interval=1)\n```\n\nActually, both [`IterBasedTrainLoop`](https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py#L183%5D) and [`EpochBasedTrainLoop`](https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py#L18) support dynamical interval, see the following example.\n\n```python\n# Before 365001th iteration, we do evaluation every 5000 iterations.\n# After 365000th iteration, we do evaluation every 368750 iterations,\n# which means that we do evaluation at the end of training.\n\ninterval = 5000\nmax_iters = 368750\ndynamic_intervals = [(max_iters // interval * interval + 1, max_iters)]\ntrain_cfg = dict(\n    type='IterBasedTrainLoop',\n    max_iters=max_iters,\n    val_interval=interval,\n    dynamic_intervals=dynamic_intervals)\n```\n\n## Customize hooks\n\n### Customize self-implemented hooks\n\n#### 1. Implement a new hook\n\nMMEngine provides many useful [hooks](https://mmengine.readthedocs.io/en/latest/tutorials/hooks.html), but there are some occasions when the users might need to implement a new hook. MMDetection supports customized hooks in training in v3.0 . Thus the users could implement a hook directly in mmdet or their mmdet-based codebases and use the hook by only modifying the config in training.\nHere we give an example of creating a new hook in mmdet and using it in training.\n\n```python\nfrom mmengine.hooks import Hook\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass MyHook(Hook):\n\n    def __init__(self, a, b):\n\n    def before_run(self, runner) -> None:\n\n    def after_run(self, runner) -> None:\n\n    def before_train(self, runner) -> None:\n\n    def after_train(self, runner) -> None:\n\n    def before_train_epoch(self, runner) -> None:\n\n    def after_train_epoch(self, runner) -> None:\n\n    def before_train_iter(self,\n                          runner,\n                          batch_idx: int,\n                          data_batch: DATA_BATCH = None) -> None:\n\n    def after_train_iter(self,\n                         runner,\n                         batch_idx: int,\n                         data_batch: DATA_BATCH = None,\n                         outputs: Optional[dict] = None) -> None:\n```\n\nDepending on the functionality of the hook, the users need to specify what the hook will do at each stage of the training in `before_run`, `after_run`, `before_train`, `after_train` , `before_train_epoch`, `after_train_epoch`, `before_train_iter`, and `after_train_iter`.  There are more points where hooks can be inserted, refer to [base hook class](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/hook.py#L9) for more detail.\n\n#### 2. Register the new hook\n\nThen we need to make `MyHook` imported. Assuming the file is in `mmdet/engine/hooks/my_hook.py` there are two ways to do that:\n\n- Modify `mmdet/engine/hooks/__init__.py` to import it.\n\n  The newly defined module should be imported in `mmdet/engine/hooks/__init__.py` so that the registry will find the new module and add it:\n\n```python\nfrom .my_hook import MyHook\n```\n\n- Use `custom_imports` in the config to manually import it\n\n```python\ncustom_imports = dict(imports=['mmdet.engine.hooks.my_hook'], allow_failed_imports=False)\n```\n\n#### 3. Modify the config\n\n```python\ncustom_hooks = [\n    dict(type='MyHook', a=a_value, b=b_value)\n]\n```\n\nYou can also set the priority of the hook by adding key `priority` to `'NORMAL'` or `'HIGHEST'` as below\n\n```python\ncustom_hooks = [\n    dict(type='MyHook', a=a_value, b=b_value, priority='NORMAL')\n]\n```\n\nBy default the hook's priority is set as `NORMAL` during registration.\n\n### Use hooks implemented in MMDetection\n\nIf the hook is already implemented in MMDectection, you can directly modify the config to use the hook as below\n\n#### Example: `NumClassCheckHook`\n\nWe implement a customized hook named [NumClassCheckHook](../../../mmdet/engine/hooks/num_class_check_hook.py) to check whether the `num_classes` in head matches the length of `classes` in the metainfo of `dataset`.\n\nWe set it in [default_runtime.py](../../../configs/_base_/default_runtime.py).\n\n```python\ncustom_hooks = [dict(type='NumClassCheckHook')]\n```\n\n### Modify default runtime hooks\n\nThere are some common hooks that are registered through `default_hooks`, they are\n\n- `IterTimerHook`: A hook that logs 'data_time' for loading data and 'time' for a model train step.\n- `LoggerHook`: A hook that Collect logs from different components of `Runner` and write them to terminal, JSON file, tensorboard and wandb .etc.\n- `ParamSchedulerHook`: A hook to update some hyper-parameters in optimizer, e.g., learning rate and momentum.\n- `CheckpointHook`: A hook that saves checkpoints periodically.\n- `DistSamplerSeedHook`: A hook that sets the seed for sampler and batch_sampler.\n- `DetVisualizationHook`: A hook used to visualize validation and testing process prediction results.\n\n`IterTimerHook`, `ParamSchedulerHook` and `DistSamplerSeedHook` are simple and no need to be modified usually, so here we reveals how what we can do with `LoggerHook`, `CheckpointHook` and `DetVisualizationHook`.\n\n#### CheckpointHook\n\nExcept saving checkpoints periodically, [`CheckpointHook`](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/checkpoint_hook.py#L19) provides other options such as `max_keep_ckpts`, `save_optimizer` and etc. The users could set `max_keep_ckpts` to only save small number of checkpoints or decide whether to store state dict of optimizer by `save_optimizer`. More details of the arguments are [here](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/checkpoint_hook.py#L19)\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        interval=1,\n        max_keep_ckpts=3,\n        save_optimizer=True))\n```\n\n#### LoggerHook\n\nThe `LoggerHook` enables to set intervals. And the detail usages can be found in the [docstring](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/logger_hook.py#L18).\n\n```python\ndefault_hooks = dict(logger=dict(type='LoggerHook', interval=50))\n```\n\n#### DetVisualizationHook\n\n`DetVisualizationHook` use `DetLocalVisualizer` to visualize prediction results, and `DetLocalVisualizer` current supports different backends, e.g., `TensorboardVisBackend` and `WandbVisBackend` (see [docstring](https://github.com/open-mmlab/mmengine/blob/main/mmengine/visualization/vis_backend.py) for more detail). The users could add multi backbends to do visualization, as follows.\n\n```python\ndefault_hooks = dict(\n    visualization=dict(type='DetVisualizationHook', draw=True))\n\nvis_backends = [dict(type='LocalVisBackend'),\n                dict(type='TensorboardVisBackend')]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n```\n"
  },
  {
    "path": "docs/en/advanced_guides/customize_transforms.md",
    "content": "# Customize Data Pipelines\n\n1. Write a new transform in a file, e.g., in `my_pipeline.py`. It takes a dict as input and returns a dict.\n\n   ```python\n   import random\n   from mmcv.transforms import BaseTransform\n   from mmdet.registry import TRANSFORMS\n\n\n   @TRANSFORMS.register_module()\n   class MyTransform(BaseTransform):\n       \"\"\"Add your transform\n\n       Args:\n           p (float): Probability of shifts. Default 0.5.\n       \"\"\"\n\n       def __init__(self, prob=0.5):\n           self.prob = prob\n\n       def transform(self, results):\n           if random.random() > self.prob:\n               results['dummy'] = True\n           return results\n   ```\n\n2. Import and use the pipeline in your config file.\n   Make sure the import is relative to where your train script is located.\n\n   ```python\n   custom_imports = dict(imports=['path.to.my_pipeline'], allow_failed_imports=False)\n\n   train_pipeline = [\n       dict(type='LoadImageFromFile'),\n       dict(type='LoadAnnotations', with_bbox=True),\n       dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n       dict(type='RandomFlip', prob=0.5),\n       dict(type='MyTransform', prob=0.2),\n       dict(type='PackDetInputs')\n   ]\n   ```\n\n3. Visualize the output of your transforms pipeline\n\n   To visualize the output of your transforms pipeline, `tools/misc/browse_dataset.py`\n   can help the user to browse a detection dataset (both images and bounding box annotations)\n   visually, or save the image to a designated directory. More details can refer to\n   [visualization documentation](../user_guides/visualization.md)\n"
  },
  {
    "path": "docs/en/advanced_guides/data_flow.md",
    "content": "# Data Flow\n"
  },
  {
    "path": "docs/en/advanced_guides/datasets.md",
    "content": "# Datasets\n"
  },
  {
    "path": "docs/en/advanced_guides/engine.md",
    "content": "# Engine\n"
  },
  {
    "path": "docs/en/advanced_guides/evaluation.md",
    "content": "# Evaluation\n"
  },
  {
    "path": "docs/en/advanced_guides/how_to.md",
    "content": "This tutorial collects answers to any `How to xxx with MMDetection`. Feel free to update this doc if you meet new questions about `How to` and find the answers!\n\n# Use backbone network through MMPretrain\n\nThe model registry in MMDet, MMPreTrain, MMSeg all inherit from the root registry in MMEngine. This allows these repositories to directly use the modules already implemented by each other. Therefore, users can use backbone networks from MMPretrain in MMDetection without implementing a network that already exists in MMPretrain.\n\n## Use backbone network implemented in MMPretrain\n\nSuppose you want to use `MobileNetV3-small` as the backbone network of `RetinaNet`, the example config is as the following.\n\n```python\n_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(imports=['mmpretrain.models'], allow_failed_imports=False)\npretrained = 'https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth'\nmodel = dict(\n    backbone=dict(\n        _delete_=True, # Delete the backbone field in _base_\n        type='mmpretrain.MobileNetV3', # Using MobileNetV3 from mmpretrain\n        arch='small',\n        out_indices=(3, 8, 11), # Modify out_indices\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint=pretrained,\n            prefix='backbone.')), # The pre-trained weights of backbone network in mmpretrain have prefix='backbone.'. The prefix in the keys will be removed so that these weights can be normally loaded.\n    # Modify in_channels\n    neck=dict(in_channels=[24, 48, 96], start_level=0))\n```\n\n## Use backbone network in TIMM through MMPretrain\n\nMMPretrain also provides a wrapper for the PyTorch Image Models (timm) backbone network, users can directly use the backbone network in timm through MMPretrain. Suppose you want to use [EfficientNet-B1](../../../configs/timm_example/retinanet_timm-efficientnet-b1_fpn_1x_coco.py) as the backbone network of RetinaNet, the example config is as the following.\n\n```python\n# https://github.com/open-mmlab/mmdetection/blob/main/configs/timm_example/retinanet_timm-efficientnet-b1_fpn_1x_coco.py\n\n_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(imports=['mmpretrain.models'], allow_failed_imports=False)\nmodel = dict(\n    backbone=dict(\n        _delete_=True, # Delete the backbone field in _base_\n        type='mmpretrain.TIMMBackbone', # Using timm from mmpretrain\n        model_name='efficientnet_b1',\n        features_only=True,\n        pretrained=True,\n        out_indices=(1, 2, 3, 4)), # Modify out_indices\n    neck=dict(in_channels=[24, 40, 112, 320])) # Modify in_channels\n\noptimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)\n```\n\n`type='mmpretrain.TIMMBackbone'` means use the `TIMMBackbone` class from MMPretrain in MMDetection, and the model used is `EfficientNet-B1`, where `mmpretrain` means the MMPretrain repo and `TIMMBackbone` means the TIMMBackbone wrapper implemented in MMPretrain.\n\nFor the principle of the Hierarchy Registry, please refer to the [MMEngine document](https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/config.md). For how to use other backbones in MMPretrain, you can refer to the [MMPretrain document](https://mmpretrain.readthedocs.io/en/latest/user_guides/config.html).\n\n# Use Mosaic augmentation\n\nIf you want to use `Mosaic` in training, please make sure that you use `MultiImageMixDataset` at the same time. Taking the 'Faster R-CNN' algorithm as an example, you should modify the values of `train_pipeline` and `train_dataset` in the config as below:\n\n```python\n# Open configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py directly and add the following fields\ndata_root = 'data/coco/'\ndataset_type = 'CocoDataset'\nimg_scale=(1333, 800)\n\ntrain_pipeline = [\n    dict(type='Mosaic', img_scale=img_scale, pad_val=114.0),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        border=(-img_scale[0] // 2, -img_scale[1] // 2)), # The image will be enlarged by 4 times after Mosaic processing,so we use affine transformation to restore the image size.\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataset = dict(\n    _delete_ = True, # remove unnecessary Settings\n    type='MultiImageMixDataset',\n    dataset=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_train2017.json',\n        img_prefix=data_root + 'train2017/',\n        pipeline=[\n            dict(type='LoadImageFromFile'),\n            dict(type='LoadAnnotations', with_bbox=True)\n        ],\n        filter_empty_gt=False,\n    ),\n    pipeline=train_pipeline\n    )\n\ndata = dict(\n    train=train_dataset\n    )\n```\n\n# Unfreeze backbone network after freezing the backbone in the config\n\nIf you have freezed the backbone network in the config and want to unfreeze it after some epoches, you can write a hook function to do it.  Taking the Faster R-CNN with the resnet backbone as an example, you can freeze one stage of the backbone network and  add a `custom_hooks` in the config as below:\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    # freeze one stage of the backbone network.\n    backbone=dict(frozen_stages=1),\n)\ncustom_hooks = [dict(type=\"UnfreezeBackboneEpochBasedHook\", unfreeze_epoch=1)]\n```\n\nMeanwhile write the hook class `UnfreezeBackboneEpochBasedHook` in `mmdet/core/hook/unfreeze_backbone_epoch_based_hook.py`\n\n```python\nfrom mmengine.model import is_model_wrapper\nfrom mmengine.hooks import Hook\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass UnfreezeBackboneEpochBasedHook(Hook):\n    \"\"\"Unfreeze backbone network Hook.\n\n    Args:\n        unfreeze_epoch (int): The epoch unfreezing the backbone network.\n    \"\"\"\n\n    def __init__(self, unfreeze_epoch=1):\n        self.unfreeze_epoch = unfreeze_epoch\n\n    def before_train_epoch(self, runner):\n        # Unfreeze the backbone network.\n        # Only valid for resnet.\n        if runner.epoch == self.unfreeze_epoch:\n            model = runner.model\n            if is_model_wrapper(model):\n                model = model.module\n            backbone = model.backbone\n            if backbone.frozen_stages >= 0:\n                if backbone.deep_stem:\n                    backbone.stem.train()\n                    for param in backbone.stem.parameters():\n                        param.requires_grad = True\n                else:\n                    backbone.norm1.train()\n                    for m in [backbone.conv1, backbone.norm1]:\n                        for param in m.parameters():\n                            param.requires_grad = True\n\n            for i in range(1, backbone.frozen_stages + 1):\n                m = getattr(backbone, f'layer{i}')\n                m.train()\n                for param in m.parameters():\n                    param.requires_grad = True\n```\n\n# Get the channels of a new backbone\n\nIf you want to get the channels of a new backbone, you can build this backbone alone and input a pseudo image to get each stage output.\n\nTake `ResNet` as an example:\n\n```python\nfrom mmdet.models import ResNet\nimport torch\nself = ResNet(depth=18)\nself.eval()\ninputs = torch.rand(1, 3, 32, 32)\nlevel_outputs = self.forward(inputs)\nfor level_out in level_outputs:\n    print(tuple(level_out.shape))\n\n```\n\nOutput of the above script is as below:\n\n```python\n(1, 64, 8, 8)\n(1, 128, 4, 4)\n(1, 256, 2, 2)\n(1, 512, 1, 1)\n```\n\nUsers can get the channels of the new backbone by Replacing the `ResNet(depth=18)` in this script with their customized backbone.\n\n# Use Detectron2 Model in MMDetection\n\nUsers can use Detectron2Wrapper to run Detectron2's model in MMDetection. We provide examples of [Faster R-CNN](../../../configs/misc/d2_faster-rcnn_r50-caffe_fpn_ms-90k_coco.py),\n[Mask R-CNN](../../../configs/misc/d2_mask-rcnn_r50-caffe_fpn_ms-90k_coco.py), and [RetinaNet](../../../configs/misc/d2_retinanet_r50-caffe_fpn_ms-90k_coco.py) in MMDetection.\n\nThe algorithm components in config file should be the same as those of in Detectron2. During setup, we will first initialize the default settings, which can be found in [Detectron2](https://github.com/facebookresearch/detectron2/blob/main/detectron2/config/defaults.py).\nThen, the settings in config file will overwrite the default settings and the model will be built with these settings.\nThe input data will first convert to Detectron2's type and feed into Detectron2's model.\nDuring inference the results calculate from Detectron2's model will reconvert back to the MMDetection's type.\n\n## Use Detectron2's pre-trained weights\n\nThe weight initialization in `Detectron2Wrapper` will not use the logic of MMDetection. Users can set `model.d2_detector.weights=xxx` to load pre-trained weights.\nFor example, we can use `model.d2_detector.weights='detectron2://ImageNetPretrained/MSRA/R-50.pkl'` to load the pre-trained ResNet-50 or use\n`model.d2_detector.weights='detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x/137260431/model_final_a54504.pkl'` to load the pre-trained Mask R-CNN weights proposed in Detectron2.\n\n**Note:** Detectron2's pretrained model cannot be loaded directly by using `load_from`, it should be first converted via `tools/model_converters/detectron2_to_mmdet.py`\n\nFor inference of released detectron2 checkpoints, users should first use `tools/model_converters/detectron2_to_mmdet.py` to convert Detectron2 checkpoint to MMDetection.\n\n```shell\npython tools/model_converters/detectron2_to_mmdet.py ${Detectron2 ckpt path} ${MMDetectron ckpt path}\n```\n"
  },
  {
    "path": "docs/en/advanced_guides/index.rst",
    "content": "Basic Concepts\n***************\n\n.. toctree::\n   :maxdepth: 1\n\n   data_flow.md\n   structures.md\n   models.md\n   datasets.md\n   transforms.md\n   evaluation.md\n   engine.md\n   conventions.md\n\nComponent Customization\n************************\n\n.. toctree::\n   :maxdepth: 1\n\n   customize_models.md\n   customize_losses.md\n   customize_dataset.md\n   customize_transforms.md\n   customize_runtime.md\n\nHow to\n************************\n\n.. toctree::\n   :maxdepth: 1\n\n   how_to.md\n"
  },
  {
    "path": "docs/en/advanced_guides/models.md",
    "content": "# Models\n"
  },
  {
    "path": "docs/en/advanced_guides/structures.md",
    "content": "# Structures\n"
  },
  {
    "path": "docs/en/advanced_guides/transforms.md",
    "content": "# Data Transforms (Need to update)\n\n## Design of Data transforms pipeline\n\nFollowing typical conventions, we use `Dataset` and `DataLoader` for data loading\nwith multiple workers. `Dataset` returns a dict of data items corresponding\nthe arguments of models' forward method.\n\nThe data transforms pipeline and the dataset is decomposed. Usually a dataset\ndefines how to process the annotations and a data transforms pipeline defines all the steps to prepare a data dict.\nA pipeline consists of a sequence of data transforms. Each operation takes a dict as input and also output a dict for the next transform.\n\nWe present a classical pipeline in the following figure. The blue blocks are pipeline operations. With the pipeline going on, each operator can add new keys (marked as green) to the result dict or update the existing keys (marked as orange).\n![pipeline figure](../../../resources/data_pipeline.png)\n\nHere is a pipeline example for Faster R-CNN.\n\n```python\ntrain_pipeline = [  # Training data processing pipeline\n    dict(type='LoadImageFromFile', backend_args=backend_args),  # First pipeline to load images from file path\n    dict(\n        type='LoadAnnotations',  # Second pipeline to load annotations for current image\n        with_bbox=True),  # Whether to use bounding box, True for detection\n    dict(\n        type='Resize',  # Pipeline that resize the images and their annotations\n        scale=(1333, 800),  # The largest scale of image\n        keep_ratio=True  # Whether to keep the ratio between height and width\n        ),\n    dict(\n        type='RandomFlip',  # Augmentation pipeline that flip the images and their annotations\n        prob=0.5),  # The probability to flip\n    dict(type='PackDetInputs')  # Pipeline that formats the annotation data and decides which keys in the data should be packed into data_samples\n]\ntest_pipeline = [  # Testing data processing pipeline\n    dict(type='LoadImageFromFile', backend_args=backend_args),  # First pipeline to load images from file path\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),  # Pipeline that resize the images\n    dict(\n        type='PackDetInputs',  # Pipeline that formats the annotation data and decides which keys in the data should be packed into data_samples\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n```\n"
  },
  {
    "path": "docs/en/api.rst",
    "content": "mmdet.apis\n--------------\n.. automodule:: mmdet.apis\n    :members:\n\nmmdet.datasets\n--------------\n\ndatasets\n^^^^^^^^^^\n.. automodule:: mmdet.datasets\n    :members:\n\napi_wrappers\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.datasets.api_wrappers\n    :members:\n\nsamplers\n^^^^^^^^^^\n.. automodule:: mmdet.datasets.samplers\n    :members:\n\ntransforms\n^^^^^^^^^^^^\n.. automodule:: mmdet.datasets.transforms\n    :members:\n\nmmdet.engine\n--------------\n\nhooks\n^^^^^^^^^^\n.. automodule:: mmdet.engine.hooks\n    :members:\n\noptimizers\n^^^^^^^^^^^^^^^\n.. automodule:: mmdet.engine.optimizers\n    :members:\n\nrunner\n^^^^^^^^^^\n.. automodule:: mmdet.engine.runner\n    :members:\n\nschedulers\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.engine.schedulers\n    :members:\n\nmmdet.evaluation\n--------------------\n\nfunctional\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.evaluation.functional\n    :members:\n\nmetrics\n^^^^^^^^^^\n.. automodule:: mmdet.evaluation.metrics\n    :members:\n\n\nmmdet.models\n--------------\n\nbackbones\n^^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.backbones\n    :members:\n\ndata_preprocessors\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.data_preprocessors\n    :members:\n\ndense_heads\n^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.dense_heads\n    :members:\n\ndetectors\n^^^^^^^^^^\n.. automodule:: mmdet.models.detectors\n    :members:\n\nlayers\n^^^^^^^^^^\n.. automodule:: mmdet.models.layers\n    :members:\n\nlosses\n^^^^^^^^^^\n.. automodule:: mmdet.models.losses\n    :members:\n\nnecks\n^^^^^^^^^^^^\n.. automodule:: mmdet.models.necks\n    :members:\n\nroi_heads\n^^^^^^^^^^^^^\n.. automodule:: mmdet.models.roi_heads\n    :members:\n\nseg_heads\n^^^^^^^^^^^^^\n.. automodule:: mmdet.models.seg_heads\n    :members:\n\ntask_modules\n^^^^^^^^^^^^^\n.. automodule:: mmdet.models.task_modules\n    :members:\n\ntest_time_augs\n^^^^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.test_time_augs\n    :members:\n\nutils\n^^^^^^^^^^\n.. automodule:: mmdet.models.utils\n    :members:\n\n\nmmdet.structures\n--------------------\n\nstructures\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.structures\n    :members:\n\nbbox\n^^^^^^^^^^\n.. automodule:: mmdet.structures.bbox\n    :members:\n\nmask\n^^^^^^^^^^\n.. automodule:: mmdet.structures.mask\n    :members:\n\nmmdet.testing\n----------------\n.. automodule:: mmdet.testing\n    :members:\n\nmmdet.visualization\n--------------------\n.. automodule:: mmdet.visualization\n    :members:\n\nmmdet.utils\n--------------\n.. automodule:: mmdet.utils\n    :members:\n"
  },
  {
    "path": "docs/en/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common options. For a full\n# list see the documentation:\n# https://www.sphinx-doc.org/en/main/usage/configuration.html\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport subprocess\nimport sys\n\nimport pytorch_sphinx_theme\n\nsys.path.insert(0, os.path.abspath('../..'))\n\n# -- Project information -----------------------------------------------------\n\nproject = 'MMDetection'\ncopyright = '2018-2021, OpenMMLab'\nauthor = 'MMDetection Authors'\nversion_file = '../../mmdet/version.py'\n\n\ndef get_version():\n    with open(version_file, 'r') as f:\n        exec(compile(f.read(), version_file, 'exec'))\n    return locals()['__version__']\n\n\n# The full version, including alpha/beta/rc tags\nrelease = get_version()\n\n# -- General configuration ---------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.napoleon',\n    'sphinx.ext.viewcode',\n    'myst_parser',\n    'sphinx_markdown_tables',\n    'sphinx_copybutton',\n]\n\nmyst_enable_extensions = ['colon_fence']\nmyst_heading_anchors = 3\n\nautodoc_mock_imports = [\n    'matplotlib', 'pycocotools', 'terminaltables', 'mmdet.version', 'mmcv.ops'\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\nsource_suffix = {\n    '.rst': 'restructuredtext',\n    '.md': 'markdown',\n}\n\n# The main toctree document.\nmaster_doc = 'index'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\n# html_theme = 'sphinx_rtd_theme'\nhtml_theme = 'pytorch_sphinx_theme'\nhtml_theme_path = [pytorch_sphinx_theme.get_html_theme_path()]\n\nhtml_theme_options = {\n    'menu': [\n        {\n            'name': 'GitHub',\n            'url': 'https://github.com/open-mmlab/mmdetection'\n        },\n    ],\n    # Specify the language of shared menu\n    'menu_lang':\n    'en'\n}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\nhtml_css_files = ['css/readthedocs.css']\n\n# -- Extension configuration -------------------------------------------------\n# Ignore >>> when copying code\ncopybutton_prompt_text = r'>>> |\\.\\.\\. '\ncopybutton_prompt_is_regexp = True\n\n\ndef builder_inited_handler(app):\n    subprocess.run(['./stat.py'])\n\n\ndef setup(app):\n    app.connect('builder-inited', builder_inited_handler)\n"
  },
  {
    "path": "docs/en/dataset_zoo.md",
    "content": "# Dataset Zoo\n"
  },
  {
    "path": "docs/en/get_started.md",
    "content": "# GET STARTED\n\n## Prerequisites\n\nIn this section, we demonstrate how to prepare an environment with PyTorch.\n\nMMDetection works on Linux, Windows, and macOS. It requires Python 3.7+, CUDA 9.2+, and PyTorch 1.8+.\n\n```{note}\nIf you are experienced with PyTorch and have already installed it, just skip this part and jump to the [next section](#installation). Otherwise, you can follow these steps for the preparation.\n```\n\n**Step 0.** Download and install Miniconda from the [official website](https://docs.conda.io/en/latest/miniconda.html).\n\n**Step 1.** Create a conda environment and activate it.\n\n```shell\nconda create --name openmmlab python=3.8 -y\nconda activate openmmlab\n```\n\n**Step 2.** Install PyTorch following [official instructions](https://pytorch.org/get-started/locally/), e.g.\n\nOn GPU platforms:\n\n```shell\nconda install pytorch torchvision -c pytorch\n```\n\nOn CPU platforms:\n\n```shell\nconda install pytorch torchvision cpuonly -c pytorch\n```\n\n## Installation\n\nWe recommend that users follow our best practices to install MMDetection. However, the whole process is highly customizable. See [Customize Installation](#customize-installation) section for more information.\n\n### Best Practices\n\n**Step 0.** Install [MMEngine](https://github.com/open-mmlab/mmengine) and [MMCV](https://github.com/open-mmlab/mmcv) using [MIM](https://github.com/open-mmlab/mim).\n\n```shell\npip install -U openmim\nmim install mmengine\nmim install \"mmcv>=2.0.0\"\n```\n\n**Note:** In MMCV-v2.x, `mmcv-full` is rename to `mmcv`, if you want to install `mmcv` without CUDA ops, you can use `mim install \"mmcv-lite>=2.0.0rc1\"` to install the lite version.\n\n**Step 1.** Install MMDetection.\n\nCase a: If you develop and run mmdet directly, install it from source:\n\n```shell\ngit clone https://github.com/open-mmlab/mmdetection.git\ncd mmdetection\npip install -v -e .\n# \"-v\" means verbose, or more output\n# \"-e\" means installing a project in editable mode,\n# thus any local modifications made to the code will take effect without reinstallation.\n```\n\nCase b: If you use mmdet as a dependency or third-party package, install it with MIM:\n\n```shell\nmim install mmdet\n```\n\n## Verify the installation\n\nTo verify whether MMDetection is installed correctly, we provide some sample codes to run an inference demo.\n\n**Step 1.** We need to download config and checkpoint files.\n\n```shell\nmim download mmdet --config rtmdet_tiny_8xb32-300e_coco --dest .\n```\n\nThe downloading will take several seconds or more, depending on your network environment. When it is done, you will find two files `rtmdet_tiny_8xb32-300e_coco.py` and `rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth` in your current folder.\n\n**Step 2.** Verify the inference demo.\n\nCase a: If you install MMDetection from source, just run the following command.\n\n```shell\npython demo/image_demo.py demo/demo.jpg rtmdet_tiny_8xb32-300e_coco.py --weights rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth --device cpu\n```\n\nYou will see a new image `demo.jpg` on your `./outputs/vis` folder, where bounding boxes are plotted on cars, benches, etc.\n\nCase b: If you install MMDetection with MIM, open your python interpreter and copy&paste the following codes.\n\n```python\nfrom mmdet.apis import init_detector, inference_detector\n\nconfig_file = 'rtmdet_tiny_8xb32-300e_coco.py'\ncheckpoint_file = 'rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth'\nmodel = init_detector(config_file, checkpoint_file, device='cpu')  # or device='cuda:0'\ninference_detector(model, 'demo/demo.jpg')\n```\n\nYou will see a list of `DetDataSample`, and the predictions are in the `pred_instance`, indicating the detected bounding boxes, labels, and scores.\n\n## Tracking Installation\n\nWe recommend that users follow our best practices to install MMDetection for tracking task.\n\n### Best Practices\n\n**Step 0.** Install [MMEngine](https://github.com/open-mmlab/mmengine) and [MMCV](https://github.com/open-mmlab/mmcv) using [MIM](https://github.com/open-mmlab/mim).\n\n```shell\npip install -U openmim\nmim install mmengine\nmim install \"mmcv>=2.0.0\"\n```\n\n**Step 1.** Install MMDetection.\n\nCase a: If you develop and run mmdet directly, install it from source:\n\n```shell\ngit clone https://github.com/open-mmlab/mmdetection.git\ncd mmdetection\npip install -v -e . -r requirements/tracking.txt\n# \"-v\" means verbose, or more output\n# \"-e\" means installing a project in editable mode,\n# thus any local modifications made to the code will take effect without reinstallation.\n```\n\nCase b: If you use mmdet as a dependency or third-party package, install it with MIM:\n\n```shell\nmim install mmdet[tracking]\n```\n\n**Step 2.** Install TrackEval.\n\n```shell\npip install git+https://github.com/JonathonLuiten/TrackEval.git\n```\n\n## Verify the installation\n\nTo verify whether MMDetection is installed correctly, we provide some sample codes to run an inference demo.\n\n**Step 1.** We need to download config and checkpoint files.\n\n```shell\nmim download mmdet --config bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval --dest .\n```\n\nThe downloading will take several seconds or more, depending on your network environment. When it is done, you will find two files `bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py` and `bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500-1985c9f0.pth` in your current folder.\n\n**Step 2.** Verify the inference demo.\n\nCase a: If you install MMDetection from source, just run the following command.\n\n```shell\npython demo/mot_demo.py demo/demo_mot.mp4 bytetrack_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_test-mot17halfval.py --checkpoint bytetrack_yolox_x_crowdhuman_mot17-private-half_20211218_205500-1985c9f0.pth --out mot.mp4\n```\n\nYou will see a new video `mot.mp4` on your folder, where bounding boxes are plotted on person.\n\nCase b: If you install MMDetection with MIM, open your python interpreter and demo/mot_demo.py, then run it like Case a.\n\n### Customize Installation\n\n#### CUDA versions\n\nWhen installing PyTorch, you need to specify the version of CUDA. If you are not clear on which to choose, follow our recommendations:\n\n- For Ampere-based NVIDIA GPUs, such as GeForce 30 series and NVIDIA A100, CUDA 11 is a must.\n- For older NVIDIA GPUs, CUDA 11 is backward compatible, but CUDA 10.2 offers better compatibility and is more lightweight.\n\nPlease make sure the GPU driver satisfies the minimum version requirements. See [this table](https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html#cuda-major-component-versions__table-cuda-toolkit-driver-versions) for more information.\n\n```{note}\nInstalling CUDA runtime libraries is enough if you follow our best practices, because no CUDA code will be compiled locally. However, if you hope to compile MMCV from source or develop other CUDA operators, you need to install the complete CUDA toolkit from NVIDIA's [website](https://developer.nvidia.com/cuda-downloads), and its version should match the CUDA version of PyTorch. i.e., the specified version of cudatoolkit in the `conda install` command.\n```\n\n#### Install MMEngine without MIM\n\nTo install MMEngine with pip instead of MIM, please follow [MMEngine installation guides](https://mmengine.readthedocs.io/en/latest/get_started/installation.html).\n\nFor example, you can install MMEngine by the following command.\n\n```shell\npip install mmengine\n```\n\n#### Install MMCV without MIM\n\nMMCV contains C++ and CUDA extensions, thus depending on PyTorch in a complex way. MIM solves such dependencies automatically and makes the installation easier. However, it is not a must.\n\nTo install MMCV with pip instead of MIM, please follow [MMCV installation guides](https://mmcv.readthedocs.io/en/2.x/get_started/installation.html). This requires manually specifying a find-url based on the PyTorch version and its CUDA version.\n\nFor example, the following command installs MMCV built for PyTorch 1.12.x and CUDA 11.6.\n\n```shell\npip install \"mmcv>=2.0.0\" -f https://download.openmmlab.com/mmcv/dist/cu116/torch1.12.0/index.html\n```\n\n#### Install on CPU-only platforms\n\nMMDetection can be built for CPU-only environments. In CPU mode you can train (requires MMCV version >= 2.0.0rc1), test, or infer a model.\n\nHowever, some functionalities are gone in this mode:\n\n- Deformable Convolution\n- Modulated Deformable Convolution\n- ROI pooling\n- Deformable ROI pooling\n- CARAFE\n- SyncBatchNorm\n- CrissCrossAttention\n- MaskedConv2d\n- Temporal Interlace Shift\n- nms_cuda\n- sigmoid_focal_loss_cuda\n- bbox_overlaps\n\nIf you try to train/test/infer a model containing the above ops, an error will be raised.\nThe following table lists affected algorithms.\n\n|                        Operator                         |                                          Model                                           |\n| :-----------------------------------------------------: | :--------------------------------------------------------------------------------------: |\n| Deformable Convolution/Modulated Deformable Convolution | DCN, Guided Anchoring, RepPoints, CentripetalNet, VFNet, CascadeRPN, NAS-FCOS, DetectoRS |\n|                      MaskedConv2d                       |                                     Guided Anchoring                                     |\n|                         CARAFE                          |                                          CARAFE                                          |\n|                      SyncBatchNorm                      |                                         ResNeSt                                          |\n\n#### Install on Google Colab\n\n[Google Colab](https://colab.research.google.com/) usually has PyTorch installed,\nthus we only need to install MMEngine, MMCV, and MMDetection with the following commands.\n\n**Step 1.** Install [MMEngine](https://github.com/open-mmlab/mmengine) and [MMCV](https://github.com/open-mmlab/mmcv) using [MIM](https://github.com/open-mmlab/mim).\n\n```shell\n!pip3 install openmim\n!mim install mmengine\n!mim install \"mmcv>=2.0.0,<2.1.0\"\n```\n\n**Step 2.** Install MMDetection from the source.\n\n```shell\n!git clone https://github.com/open-mmlab/mmdetection.git\n%cd mmdetection\n!pip install -e .\n```\n\n**Step 3.** Verification.\n\n```python\nimport mmdet\nprint(mmdet.__version__)\n# Example output: 3.0.0, or an another version.\n```\n\n```{note}\nWithin Jupyter, the exclamation mark `!` is used to call external executables and `%cd` is a [magic command](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-cd) to change the current working directory of Python.\n```\n\n#### Use MMDetection with Docker\n\nWe provide a [Dockerfile](../../docker/Dockerfile) to build an image. Ensure that your [docker version](https://docs.docker.com/engine/install/) >=19.03.\n\n```shell\n# build an image with PyTorch 1.9, CUDA 11.1\n# If you prefer other versions, just modified the Dockerfile\ndocker build -t mmdetection docker/\n```\n\nRun it with\n\n```shell\ndocker run --gpus all --shm-size=8g -it -v {DATA_DIR}:/mmdetection/data mmdetection\n```\n\n### Troubleshooting\n\nIf you have some issues during the installation, please first view the [FAQ](notes/faq.md) page.\nYou may [open an issue](https://github.com/open-mmlab/mmdetection/issues/new/choose) on GitHub if no solution is found.\n\n### Use Multiple Versions of MMDetection in Development\n\nTraining and testing scripts have already been modified in `PYTHONPATH` in order to make sure the scripts are using their own versions of MMDetection.\n\nTo install the default version of MMDetection in your environment, you can exclude the follow code in the relative scripts:\n\n```shell\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH\n```\n"
  },
  {
    "path": "docs/en/index.rst",
    "content": "Welcome to MMDetection's documentation!\n=======================================\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Get Started\n\n   overview.md\n   get_started.md\n\n.. toctree::\n   :maxdepth: 2\n   :caption: User Guides\n\n   user_guides/index.rst\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Advanced Guides\n\n   advanced_guides/index.rst\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Migration\n\n   migration/migration.md\n\n.. toctree::\n   :maxdepth: 1\n   :caption: API Reference\n\n   api.rst\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Model Zoo\n\n   model_zoo.md\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Notes\n\n   notes/contribution_guide.md\n   notes/projects.md\n   notes/changelog.md\n   notes/changelog_v2.x.md\n   notes/faq.md\n   notes/compatibility.md\n\n.. toctree::\n   :caption: Switch Language\n\n   switch_language.md\n\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/en/make.bat",
    "content": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset SOURCEDIR=.\nset BUILDDIR=_build\n\nif \"%1\" == \"\" goto help\n\n%SPHINXBUILD% >NUL 2>NUL\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.http://sphinx-doc.org/\n\texit /b 1\n)\n\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\ngoto end\n\n:help\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\n\n:end\npopd\n"
  },
  {
    "path": "docs/en/migration/api_and_registry_migration.md",
    "content": "# Migrate API and Registry from MMDetection 2.x to 3.x\n"
  },
  {
    "path": "docs/en/migration/config_migration.md",
    "content": "# Migrate Configuration File from MMDetection 2.x to 3.x\n\nThe configuration file of MMDetection 3.x has undergone significant changes in comparison to the 2.x version. This document explains how to migrate 2.x configuration files to 3.x.\n\nIn the previous tutorial [Learn about Configs](../user_guides/config.md), we used Mask R-CNN as an example to introduce the configuration file structure of MMDetection 3.x. Here, we will follow the same structure to demonstrate how to migrate 2.x configuration files to 3.x.\n\n## Model Configuration\n\nThere have been no major changes to the model configuration in 3.x compared to 2.x. For the model's backbone, neck, head, as well as train_cfg and test_cfg, the parameters remain the same as in version 2.x.\n\nOn the other hand, we have added the `DataPreprocessor` module in MMDetection 3.x. The configuration for the `DataPreprocessor` module is located in `model.data_preprocessor`. It is used to preprocess the input data, such as normalizing input images and padding images of different sizes into batches, and loading images from memory to VRAM. This configuration replaces the `Normalize` and `Pad` modules in `train_pipeline` and `test_pipeline` of the earlier version.\n\n<table class=\"docutils\">\n<tr>\n<td>2.x Config</td>\n<td>\n\n```python\n# Image normalization parameters\nimg_norm_cfg = dict(\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    to_rgb=True)\npipeline=[\n    ...,\n    dict(type='Normalize', **img_norm_cfg),\n    dict(type='Pad', size_divisor=32),  # Padding the image to multiples of 32\n    ...\n]\n```\n\n</td>\n<tr>\n<td>3.x Config</td>\n<td>\n\n```python\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        # Image normalization parameters\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        # Image padding parameters\n        pad_mask=True,  # In instance segmentation, the mask needs to be padded\n        pad_size_divisor=32)  # Padding the image to multiples of 32\n)\n\n```\n\n</td>\n</tr>\n</table>\n\n## Dataset and Evaluator Configuration\n\nThe dataset and evaluator configurations have undergone major changes compared to version 2.x. We will introduce how to migrate from version 2.x to version 3.x from three aspects: Dataloader and Dataset, Data transform pipeline, and Evaluator configuration.\n\n### Dataloader and Dataset Configuration\n\nIn the new version, we set the data loading settings consistent with PyTorch's official DataLoader,\nmaking it easier for users to understand and get started with.\nWe put the data loading settings for training, validation, and testing separately in `train_dataloader`, `val_dataloader`, and `test_dataloader`.\nUsers can set different parameters for these dataloaders.\nThe input parameters are basically the same as those required by [PyTorch DataLoader](https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader).\n\nThis way, we put the unconfigurable parameters in version 2.x, such as `sampler`, `batch_sampler`, and `persistent_workers`, in the configuration file, so that users can set dataloader parameters more flexibly.\n\nUsers can set the dataset configuration through `train_dataloader.dataset`, `val_dataloader.dataset`, and `test_dataloader.dataset`, which correspond to `data.train`, `data.val`, and `data.test` in version 2.x.\n\n<table class=\"docutils\">\n<tr>\n<td>2.x Config</td>\n<td>\n\n```python\ndata = dict(\n    samples_per_gpu=2,\n    workers_per_gpu=2,\n    train=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_train2017.json',\n        img_prefix=data_root + 'train2017/',\n        pipeline=train_pipeline),\n    val=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        img_prefix=data_root + 'val2017/',\n        pipeline=test_pipeline),\n    test=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        img_prefix=data_root + 'val2017/',\n        pipeline=test_pipeline))\n```\n\n</td>\n<tr>\n<td>3.x Config</td>\n<td>\n\n```python\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,  # Avoid recreating subprocesses after each iteration\n    sampler=dict(type='DefaultSampler', shuffle=True),  # Default sampler, supports both distributed and non-distributed training\n    batch_sampler=dict(type='AspectRatioBatchSampler'),  # Default batch_sampler, used to ensure that images in the batch have similar aspect ratios, so as to better utilize graphics memory\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline))\n# In version 3.x, validation and test dataloaders can be configured independently\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader  # The configuration of the testing dataloader is the same as that of the validation dataloader, which is omitted here\n\n```\n\n</td>\n</tr>\n</table>\n\n### Data Transform Pipeline Configuration\n\nAs mentioned earlier, we have separated the normalization and padding configurations for images from the `train_pipeline` and `test_pipeline`, and have placed them in `model.data_preprocessor` instead. Hence, in the 3.x version of the pipeline, we no longer require the `Normalize` and `Pad` transforms.\n\nAt the same time, we have also refactored the transform responsible for packing the data format, and have merged the `Collect` and `DefaultFormatBundle` transforms into `PackDetInputs`. This transform is responsible for packing the data from the data pipeline into the input format of the model. For more details on the input format conversion, please refer to the [data flow documentation](../advanced_guides/data_flow.md).\n\nBelow, we will use the `train_pipeline` of Mask R-CNN as an example, to demonstrate how to migrate from the 2.x configuration to the 3.x configuration:\n\n<table class=\"docutils\">\n<tr>\n<td>2.x Config</td>\n<td>\n\n```python\nimg_norm_cfg = dict(\n    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', flip_ratio=0.5),\n    dict(type='Normalize', **img_norm_cfg),\n    dict(type='Pad', size_divisor=32),\n    dict(type='DefaultFormatBundle'),\n    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),\n]\n```\n\n</td>\n<tr>\n<td>3.x Config</td>\n<td>\n\n```python\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n```\n\n</td>\n</tr>\n</table>\n\nFor the `test_pipeline`, apart from removing the `Normalize` and `Pad` transforms, we have also separated the data augmentation for testing (TTA) from the normal testing process, and have removed `MultiScaleFlipAug`. For more information on how to use the new TTA version, please refer to the [TTA documentation](../advanced_guides/tta.md).\n\nBelow, we will again use the `test_pipeline` of Mask R-CNN as an example, to demonstrate how to migrate from the 2.x configuration to the 3.x configuration:\n\n<table class=\"docutils\">\n<tr>\n<td>2.x Config</td>\n<td>\n\n```python\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(\n        type='MultiScaleFlipAug',\n        img_scale=(1333, 800),\n        flip=False,\n        transforms=[\n            dict(type='Resize', keep_ratio=True),\n            dict(type='RandomFlip'),\n            dict(type='Normalize', **img_norm_cfg),\n            dict(type='Pad', size_divisor=32),\n            dict(type='ImageToTensor', keys=['img']),\n            dict(type='Collect', keys=['img']),\n        ])\n]\n```\n\n</td>\n<tr>\n<td>3.x Config</td>\n<td>\n\n```python\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n```\n\n</td>\n</tr>\n</table>\n\nIn addition, we have also refactored some data augmentation transforms. The following table lists the mapping between the transforms used in the 2.x version and the 3.x version:\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>Name</th>\n    <th>2.x Config</th>\n    <th>3.x Config</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>Resize</td>\n<td>\n\n```python\ndict(type='Resize',\n     img_scale=(1333, 800),\n     keep_ratio=True)\n```\n\n</td>\n\n<td>\n\n```python\ndict(type='Resize',\n     scale=(1333, 800),\n     keep_ratio=True)\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>RandomResize</td>\n<td>\n\n```python\ndict(\n    type='Resize',\n    img_scale=[\n        (1333, 640), (1333, 800)],\n    multiscale_mode='range',\n    keep_ratio=True)\n```\n\n</td>\n<td>\n\n```python\ndict(\n    type='RandomResize',\n    scale=[\n        (1333, 640), (1333, 800)],\n    keep_ratio=True)\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>RandomChoiceResize</td>\n<td>\n\n```python\ndict(\n    type='Resize',\n    img_scale=[\n        (1333, 640), (1333, 672),\n        (1333, 704), (1333, 736),\n        (1333, 768), (1333, 800)],\n    multiscale_mode='value',\n    keep_ratio=True)\n```\n\n</td>\n<td>\n\n```python\ndict(\n    type='RandomChoiceResize',\n    scales=[\n        (1333, 640), (1333, 672),\n        (1333, 704), (1333, 736),\n        (1333, 768), (1333, 800)],\n    keep_ratio=True)\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>RandomFlip</td>\n<td>\n\n```python\ndict(type='RandomFlip', flip_ratio=0.5)\n```\n\n</td>\n<td>\n\n```python\ndict(type='RandomFlip', prob=0.5)\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\n### 评测器配置\n\nIn version 3.x, model accuracy evaluation is no longer tied to the dataset, but is instead accomplished through the use of an Evaluator.\nThe Evaluator configuration is divided into two parts: `val_evaluator` and `test_evaluator`. The `val_evaluator` is used for validation dataset evaluation, while the `test_evaluator` is used for testing dataset evaluation.\nThis corresponds to the `evaluation` field in version 2.x.\n\nThe following table shows the corresponding relationship between Evaluators in version 2.x and 3.x.\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>Metric Name</th>\n    <th>2.x Config</th>\n    <th>3.x Config</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>COCO</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type='CocoDataset',\n        ann_file=data_root + 'annotations/instances_val2017.json'))\nevaluation = dict(metric=['bbox', 'segm'])\n```\n\n</td>\n\n<td>\n\n```python\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False)\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>Pascal VOC</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type=dataset_type,\n        ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt'))\nevaluation = dict(metric='mAP')\n```\n\n</td>\n<td>\n\n```python\nval_evaluator = dict(\n    type='VOCMetric',\n    metric='mAP',\n    eval_mode='11points')\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>OpenImages</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type='OpenImagesDataset',\n        ann_file=data_root + 'annotations/validation-annotations-bbox.csv',\n        img_prefix=data_root + 'OpenImages/validation/',\n        label_file=data_root + 'annotations/class-descriptions-boxable.csv',\n        hierarchy_file=data_root +\n        'annotations/bbox_labels_600_hierarchy.json',\n        meta_file=data_root + 'annotations/validation-image-metas.pkl',\n        image_level_ann_file=data_root +\n        'annotations/validation-annotations-human-imagelabels-boxable.csv'))\nevaluation = dict(interval=1, metric='mAP')\n```\n\n</td>\n<td>\n\n```python\nval_evaluator = dict(\n    type='OpenImagesMetric',\n    iou_thrs=0.5,\n    ioa_thrs=0.5,\n    use_group_of=True,\n    get_supercategory=True)\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>CityScapes</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type='CityScapesDataset',\n        ann_file=data_root +\n        'annotations/instancesonly_filtered_gtFine_val.json',\n        img_prefix=data_root + 'leftImg8bit/val/',\n        pipeline=test_pipeline))\nevaluation = dict(metric=['bbox', 'segm'])\n```\n\n</td>\n<td>\n\n```python\nval_evaluator = [\n    dict(\n        type='CocoMetric',\n        ann_file=data_root +\n        'annotations/instancesonly_filtered_gtFine_val.json',\n        metric=['bbox', 'segm']),\n    dict(\n        type='CityScapesMetric',\n        ann_file=data_root +\n        'annotations/instancesonly_filtered_gtFine_val.json',\n        seg_prefix=data_root + '/gtFine/val',\n        outfile_prefix='./work_dirs/cityscapes_metric/instance')\n]\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\n## Configuration for Training and Testing\n\n<table class=\"docutils\">\n<tr>\n<td>2.x Config</td>\n<td>\n\n```python\nrunner = dict(\n    type='EpochBasedRunner',  # Type of training loop\n    max_epochs=12)  # Maximum number of training epochs\nevaluation = dict(interval=2)  # Interval for evaluation, check the performance every 2 epochs\n```\n\n</td>\n<tr>\n<td>3.x Config</td>\n<td>\n\n```python\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop',  # Type of training loop, please refer to https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py\n    max_epochs=12,  # Maximum number of training epochs\n    val_interval=2)  # Interval for validation, check the performance every 2 epochs\nval_cfg = dict(type='ValLoop')  # Type of validation loop\ntest_cfg = dict(type='TestLoop')  # Type of testing loop\n```\n\n</td>\n</tr>\n</table>\n\n## Optimization Configuration\n\nThe configuration for optimizer and gradient clipping is moved to the `optim_wrapper` field.\nThe following table shows the correspondences for optimizer configuration between 2.x version and 3.x version:\n\n<table class=\"docutils\">\n<tr>\n<td>2.x Config</td>\n<td>\n\n```python\noptimizer = dict(\n    type='SGD',  # Optimizer: Stochastic Gradient Descent\n    lr=0.02,  # Base learning rate\n    momentum=0.9,  # SGD with momentum\n    weight_decay=0.0001)  # Weight decay\noptimizer_config = dict(grad_clip=None)  # Configuration for gradient clipping, set to None to disable\n```\n\n</td>\n<tr>\n<td>3.x Config</td>\n<td>\n\n```python\noptim_wrapper = dict(  # Configuration for the optimizer wrapper\n    type='OptimWrapper',  # Type of optimizer wrapper, you can switch to AmpOptimWrapper to enable mixed precision training\n    optimizer=dict(  # Optimizer configuration, supports various PyTorch optimizers, please refer to https://pytorch.org/docs/stable/optim.html#algorithms\n        type='SGD',  # SGD\n        lr=0.02,  # Base learning rate\n        momentum=0.9,  # SGD with momentum\n        weight_decay=0.0001),  # Weight decay\n    clip_grad=None,  # Configuration for gradient clipping, set to None to disable. For usage, please see https://mmengine.readthedocs.io/en/latest/tutorials/optimizer.html\n    )\n```\n\n</td>\n</tr>\n</table>\n\nThe configuration for learning rate is also moved from the `lr_config` field to the `param_scheduler` field. The `param_scheduler` configuration is more similar to PyTorch's learning rate scheduler and more flexible. The following table shows the correspondences for learning rate configuration between 2.x version and 3.x version:\n\n<table class=\"docutils\">\n<tr>\n<td>2.x Config</td>\n<td>\n\n```python\nlr_config = dict(\n    policy='step',  # Use multi-step learning rate strategy during training\n    warmup='linear',  # Use linear learning rate warmup\n    warmup_iters=500,  # End warmup at iteration 500\n    warmup_ratio=0.001,  # Coefficient for learning rate warmup\n    step=[8, 11],  # Learning rate decay at which epochs\n    gamma=0.1)  # Learning rate decay coefficient\n\n```\n\n</td>\n<tr>\n<td>3.x Config</td>\n<td>\n\n```python\nparam_scheduler = [\n    dict(\n        type='LinearLR',  # Use linear learning rate warmup\n        start_factor=0.001, # Coefficient for learning rate warmup\n        by_epoch=False,  # Update the learning rate during warmup at each iteration\n        begin=0,  # Starting from the first iteration\n        end=500),  # End at the 500th iteration\n    dict(\n        type='MultiStepLR',  # Use multi-step learning rate strategy during training\n        by_epoch=True,  # Update the learning rate at each epoch\n        begin=0,   # Starting from the first epoch\n        end=12,  # Ending at the 12th epoch\n        milestones=[8, 11],  # Learning rate decay at which epochs\n        gamma=0.1)  # Learning rate decay coefficient\n]\n\n```\n\n</td>\n</tr>\n</table>\n\nFor information on how to migrate other learning rate adjustment policies, please refer to the [learning rate migration document of MMEngine](https://mmengine.readthedocs.io/zh_CN/latest/migration/param_scheduler.html).\n\n## Migration of Other Configurations\n\n### Configuration for Saving Checkpoints\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>Function</th>\n    <th>2.x Config</th>\n    <th>3.x Config</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>Set Save Interval</td>\n<td>\n\n```python\ncheckpoint_config = dict(\n    interval=1)\n```\n\n</td>\n\n<td>\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        interval=1))\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>Save Best Model</td>\n<td>\n\n```python\nevaluation = dict(\n    save_best='auto')\n```\n\n</td>\n<td>\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        save_best='auto'))\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>Keep Latest Model</td>\n<td>\n\n```python\ncheckpoint_config = dict(\n    max_keep_ckpts=3)\n```\n\n</td>\n<td>\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        max_keep_ckpts=3))\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\n### Logging Configuration\n\nIn MMDetection 3.x, the logging and visualization of the log are carried out respectively by the logger and visualizer in MMEngine. The following table shows the comparison between the configuration of printing logs and visualizing logs in MMDetection 2.x and 3.x.\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>Function</th>\n    <th>2.x Config</th>\n    <th>3.x Config</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>Set Log Printing Interval</td>\n<td>\n\n```python\nlog_config = dict(interval=50)\n```\n\n</td>\n\n<td>\n\n```python\ndefault_hooks = dict(\n    logger=dict(type='LoggerHook', interval=50))\n# Optional: set moving average window size\nlog_processor = dict(\n    type='LogProcessor', window_size=50)\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>Use TensorBoard or WandB to visualize logs</td>\n<td>\n\n```python\nlog_config = dict(\n    interval=50,\n    hooks=[\n        dict(type='TextLoggerHook'),\n        dict(type='TensorboardLoggerHook'),\n        dict(type='MMDetWandbHook',\n             init_kwargs={\n                'project': 'mmdetection',\n                'group': 'maskrcnn-r50-fpn-1x-coco'\n             },\n             interval=50,\n             log_checkpoint=True,\n             log_checkpoint_metadata=True,\n             num_eval_images=100)\n    ])\n```\n\n</td>\n<td>\n\n```python\nvis_backends = [\n    dict(type='LocalVisBackend'),\n    dict(type='TensorboardVisBackend'),\n    dict(type='WandbVisBackend',\n         init_kwargs={\n            'project': 'mmdetection',\n            'group': 'maskrcnn-r50-fpn-1x-coco'\n         })\n]\nvisualizer = dict(\n    type='DetLocalVisualizer',\n    vis_backends=vis_backends,\n    name='visualizer')\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\nFor visualization-related tutorials, please refer to [Visualization Tutorial](../user_guides/visualization.md) of MMDetection.\n\n### Runtime Configuration\n\nThe runtime configuration fields in version 3.x have been adjusted, and the specific correspondence is as follows:\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>2.x Config</th>\n    <th>3.x Config</th>\n  </tr>\n</thead>\n<tbody>\n\n<tr>\n<td>\n\n```python\ncudnn_benchmark = False\nopencv_num_threads = 0\nmp_start_method = 'fork'\ndist_params = dict(backend='nccl')\nlog_level = 'INFO'\nload_from = None\nresume_from = None\n\n\n```\n\n</td>\n<td>\n\n```python\nenv_cfg = dict(\n    cudnn_benchmark=False,\n    mp_cfg=dict(mp_start_method='fork',\n                opencv_num_threads=0),\n    dist_cfg=dict(backend='nccl'))\nlog_level = 'INFO'\nload_from = None\nresume = False\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n"
  },
  {
    "path": "docs/en/migration/dataset_migration.md",
    "content": "# Migrate dataset from MMDetection 2.x to 3.x\n"
  },
  {
    "path": "docs/en/migration/migration.md",
    "content": "# Migrating from MMDetection 2.x to 3.x\n\nMMDetection 3.x is a significant update that includes many changes to API and configuration files. This document aims to help users migrate from MMDetection 2.x to 3.x.\nWe divided the migration guide into the following sections:\n\n- [Configuration file migration](./config_migration.md)\n- [API and Registry migration](./api_and_registry_migration.md)\n- [Dataset migration](./dataset_migration.md)\n- [Model migration](./model_migration.md)\n- [Frequently Asked Questions](./migration_faq.md)\n\nIf you encounter any problems during the migration process, feel free to raise an issue. We also welcome contributions to this document.\n"
  },
  {
    "path": "docs/en/migration/migration_faq.md",
    "content": "# Migration FAQ\n"
  },
  {
    "path": "docs/en/migration/model_migration.md",
    "content": "# Migrate models from MMDetection 2.x to 3.x\n"
  },
  {
    "path": "docs/en/migration.md",
    "content": "# Migration\n"
  },
  {
    "path": "docs/en/model_zoo.md",
    "content": "# Benchmark and Model Zoo\n\n## Mirror sites\n\nWe only use aliyun to maintain the model zoo since MMDetection V2.0. The model zoo of V1.x has been deprecated.\n\n## Common settings\n\n- All models were trained on `coco_2017_train`, and tested on the `coco_2017_val`.\n- We use distributed training.\n- All pytorch-style pretrained backbones on ImageNet are from PyTorch model zoo, caffe-style pretrained backbones are converted from the newly released model from detectron2.\n- For fair comparison with other codebases, we report the GPU memory as the maximum value of `torch.cuda.max_memory_allocated()` for all 8 GPUs. Note that this value is usually less than what `nvidia-smi` shows.\n- We report the inference time as the total time of network forwarding and post-processing, excluding the data loading time. Results are obtained with the script [benchmark.py](https://github.com/open-mmlab/mmdetection/blob/main/tools/analysis_tools/benchmark.py) which computes the average time on 2000 images.\n\n## ImageNet Pretrained Models\n\nIt is common to initialize from backbone models pre-trained on ImageNet classification task. All pre-trained  model links can be found at [open_mmlab](https://github.com/open-mmlab/mmcv/blob/master/mmcv/model_zoo/open_mmlab.json).  According to `img_norm_cfg` and source of weight, we can divide all the ImageNet  pre-trained  model weights into some cases:\n\n- TorchVision:  Corresponding to torchvision weight, including ResNet50, ResNet101. The `img_norm_cfg` is `dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)`.\n- Pycls:  Corresponding to [pycls](https://github.com/facebookresearch/pycls) weight, including RegNetX. The `img_norm_cfg` is `dict(   mean=[103.530, 116.280, 123.675], std=[57.375, 57.12, 58.395], to_rgb=False)`.\n- MSRA styles: Corresponding to [MSRA](https://github.com/KaimingHe/deep-residual-networks) weights, including ResNet50_Caffe and ResNet101_Caffe. The `img_norm_cfg` is `dict(   mean=[103.530, 116.280, 123.675], std=[1.0, 1.0, 1.0], to_rgb=False)`.\n- Caffe2 styles:  Currently only contains ResNext101_32x8d. The `img_norm_cfg` is `dict(mean=[103.530, 116.280, 123.675], std=[57.375, 57.120, 58.395], to_rgb=False)`.\n- Other styles: E.g SSD which corresponds to `img_norm_cfg` is `dict(mean=[123.675, 116.28, 103.53], std=[1, 1, 1], to_rgb=True)` and YOLOv3 which corresponds to `img_norm_cfg` is `dict(mean=[0, 0, 0], std=[255., 255., 255.], to_rgb=True)`.\n\nThe detailed table of the commonly used backbone models in MMDetection is listed below :\n\n| model            | source      | link                                                                                                                                                                                                   | description                                                                                                                                                                                                                                      |\n| ---------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| ResNet50         | TorchVision | [torchvision's ResNet-50](https://download.pytorch.org/models/resnet50-19c8e357.pth)                                                                                                                   | From [torchvision's ResNet-50](https://download.pytorch.org/models/resnet50-19c8e357.pth).                                                                                                                                                       |\n| ResNet101        | TorchVision | [torchvision's ResNet-101](https://download.pytorch.org/models/resnet101-5d3b4d8f.pth)                                                                                                                 | From [torchvision's ResNet-101](https://download.pytorch.org/models/resnet101-5d3b4d8f.pth).                                                                                                                                                     |\n| RegNetX          | Pycls       | [RegNetX_3.2gf](https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth), [RegNetX_800mf](https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth). etc. | From [pycls](https://github.com/facebookresearch/pycls).                                                                                                                                                                                         |\n| ResNet50_Caffe   | MSRA        | [MSRA's ResNet-50](https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth)                                                                                                    | Converted copy of [Detectron2's R-50.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/MSRA/R-50.pkl) model. The original weight comes from [MSRA's original ResNet-50](https://github.com/KaimingHe/deep-residual-networks).    |\n| ResNet101_Caffe  | MSRA        | [MSRA's ResNet-101](https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth)                                                                                                  | Converted copy of [Detectron2's R-101.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/MSRA/R-101.pkl) model. The original weight comes from [MSRA's original ResNet-101](https://github.com/KaimingHe/deep-residual-networks). |\n| ResNext101_32x8d | Caffe2      | [Caffe2 ResNext101_32x8d](https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth)                                                                                           | Converted copy of [Detectron2's X-101-32x8d.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/FAIR/X-101-32x8d.pkl) model. The ResNeXt-101-32x8d model trained with Caffe2 at FB.                                                |\n\n## Baselines\n\n### RPN\n\nPlease refer to [RPN](https://github.com/open-mmlab/mmdetection/blob/main/configs/rpn) for details.\n\n### Faster R-CNN\n\nPlease refer to [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn) for details.\n\n### Mask R-CNN\n\nPlease refer to [Mask R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn) for details.\n\n### Fast R-CNN (with pre-computed proposals)\n\nPlease refer to [Fast R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/fast_rcnn) for details.\n\n### RetinaNet\n\nPlease refer to [RetinaNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/retinanet) for details.\n\n### Cascade R-CNN and Cascade Mask R-CNN\n\nPlease refer to [Cascade R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/cascade_rcnn) for details.\n\n### Hybrid Task Cascade (HTC)\n\nPlease refer to [HTC](https://github.com/open-mmlab/mmdetection/blob/main/configs/htc) for details.\n\n### SSD\n\nPlease refer to [SSD](https://github.com/open-mmlab/mmdetection/blob/main/configs/ssd) for details.\n\n### Group Normalization (GN)\n\nPlease refer to [Group Normalization](https://github.com/open-mmlab/mmdetection/blob/main/configs/gn) for details.\n\n### Weight Standardization\n\nPlease refer to [Weight Standardization](https://github.com/open-mmlab/mmdetection/blob/main/configs/gn+ws) for details.\n\n### Deformable Convolution v2\n\nPlease refer to [Deformable Convolutional Networks](https://github.com/open-mmlab/mmdetection/blob/main/configs/dcn) for details.\n\n### CARAFE: Content-Aware ReAssembly of FEatures\n\nPlease refer to [CARAFE](https://github.com/open-mmlab/mmdetection/blob/main/configs/carafe) for details.\n\n### Instaboost\n\nPlease refer to [Instaboost](https://github.com/open-mmlab/mmdetection/blob/main/configs/instaboost) for details.\n\n### Libra R-CNN\n\nPlease refer to [Libra R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/libra_rcnn) for details.\n\n### Guided Anchoring\n\nPlease refer to [Guided Anchoring](https://github.com/open-mmlab/mmdetection/blob/main/configs/guided_anchoring) for details.\n\n### FCOS\n\nPlease refer to [FCOS](https://github.com/open-mmlab/mmdetection/blob/main/configs/fcos) for details.\n\n### FoveaBox\n\nPlease refer to [FoveaBox](https://github.com/open-mmlab/mmdetection/blob/main/configs/foveabox) for details.\n\n### RepPoints\n\nPlease refer to [RepPoints](https://github.com/open-mmlab/mmdetection/blob/main/configs/reppoints) for details.\n\n### FreeAnchor\n\nPlease refer to [FreeAnchor](https://github.com/open-mmlab/mmdetection/blob/main/configs/free_anchor) for details.\n\n### Grid R-CNN (plus)\n\nPlease refer to [Grid R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/grid_rcnn) for details.\n\n### GHM\n\nPlease refer to [GHM](https://github.com/open-mmlab/mmdetection/blob/main/configs/ghm) for details.\n\n### GCNet\n\nPlease refer to [GCNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/gcnet) for details.\n\n### HRNet\n\nPlease refer to [HRNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/hrnet) for details.\n\n### Mask Scoring R-CNN\n\nPlease refer to [Mask Scoring R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/ms_rcnn) for details.\n\n### Train from Scratch\n\nPlease refer to [Rethinking ImageNet Pre-training](https://github.com/open-mmlab/mmdetection/blob/main/configs/scratch) for details.\n\n### NAS-FPN\n\nPlease refer to [NAS-FPN](https://github.com/open-mmlab/mmdetection/blob/main/configs/nas_fpn) for details.\n\n### ATSS\n\nPlease refer to [ATSS](https://github.com/open-mmlab/mmdetection/blob/main/configs/atss) for details.\n\n### FSAF\n\nPlease refer to [FSAF](https://github.com/open-mmlab/mmdetection/blob/main/configs/fsaf) for details.\n\n### RegNetX\n\nPlease refer to [RegNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/regnet) for details.\n\n### Res2Net\n\nPlease refer to [Res2Net](https://github.com/open-mmlab/mmdetection/blob/main/configs/res2net) for details.\n\n### GRoIE\n\nPlease refer to [GRoIE](https://github.com/open-mmlab/mmdetection/blob/main/configs/groie) for details.\n\n### Dynamic R-CNN\n\nPlease refer to [Dynamic R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/dynamic_rcnn) for details.\n\n### PointRend\n\nPlease refer to [PointRend](https://github.com/open-mmlab/mmdetection/blob/main/configs/point_rend) for details.\n\n### DetectoRS\n\nPlease refer to [DetectoRS](https://github.com/open-mmlab/mmdetection/blob/main/configs/detectors) for details.\n\n### Generalized Focal Loss\n\nPlease refer to [Generalized Focal Loss](https://github.com/open-mmlab/mmdetection/blob/main/configs/gfl) for details.\n\n### CornerNet\n\nPlease refer to [CornerNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/cornernet) for details.\n\n### YOLOv3\n\nPlease refer to [YOLOv3](https://github.com/open-mmlab/mmdetection/blob/main/configs/yolo) for details.\n\n### PAA\n\nPlease refer to [PAA](https://github.com/open-mmlab/mmdetection/blob/main/configs/paa) for details.\n\n### SABL\n\nPlease refer to [SABL](https://github.com/open-mmlab/mmdetection/blob/main/configs/sabl) for details.\n\n### CentripetalNet\n\nPlease refer to [CentripetalNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/centripetalnet) for details.\n\n### ResNeSt\n\nPlease refer to [ResNeSt](https://github.com/open-mmlab/mmdetection/blob/main/configs/resnest) for details.\n\n### DETR\n\nPlease refer to [DETR](https://github.com/open-mmlab/mmdetection/blob/main/configs/detr) for details.\n\n### Deformable DETR\n\nPlease refer to [Deformable DETR](https://github.com/open-mmlab/mmdetection/blob/main/configs/deformable_detr) for details.\n\n### AutoAssign\n\nPlease refer to [AutoAssign](https://github.com/open-mmlab/mmdetection/blob/main/configs/autoassign) for details.\n\n### YOLOF\n\nPlease refer to [YOLOF](https://github.com/open-mmlab/mmdetection/blob/main/configs/yolof) for details.\n\n### Seesaw Loss\n\nPlease refer to [Seesaw Loss](https://github.com/open-mmlab/mmdetection/blob/main/configs/seesaw_loss) for details.\n\n### CenterNet\n\nPlease refer to [CenterNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/centernet) for details.\n\n### YOLOX\n\nPlease refer to [YOLOX](https://github.com/open-mmlab/mmdetection/blob/main/configs/yolox) for details.\n\n### PVT\n\nPlease refer to [PVT](https://github.com/open-mmlab/mmdetection/blob/main/configs/pvt) for details.\n\n### SOLO\n\nPlease refer to [SOLO](https://github.com/open-mmlab/mmdetection/blob/main/configs/solo) for details.\n\n### QueryInst\n\nPlease refer to [QueryInst](https://github.com/open-mmlab/mmdetection/blob/main/configs/queryinst) for details.\n\n### PanopticFPN\n\nPlease refer to [PanopticFPN](https://github.com/open-mmlab/mmdetection/blob/main/configs/panoptic_fpn) for details.\n\n### MaskFormer\n\nPlease refer to [MaskFormer](https://github.com/open-mmlab/mmdetection/blob/main/configs/maskformer) for details.\n\n### DyHead\n\nPlease refer to [DyHead](https://github.com/open-mmlab/mmdetection/blob/main/configs/dyhead) for details.\n\n### Mask2Former\n\nPlease refer to [Mask2Former](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask2former) for details.\n\n### Efficientnet\n\nPlease refer to [Efficientnet](https://github.com/open-mmlab/mmdetection/blob/main/configs/efficientnet) for details.\n\n### Other datasets\n\nWe also benchmark some methods on [PASCAL VOC](https://github.com/open-mmlab/mmdetection/blob/main/configs/pascal_voc), [Cityscapes](https://github.com/open-mmlab/mmdetection/blob/main/configs/cityscapes), [OpenImages](https://github.com/open-mmlab/mmdetection/blob/main/configs/openimages) and [WIDER FACE](https://github.com/open-mmlab/mmdetection/blob/main/configs/wider_face).\n\n### Pre-trained Models\n\nWe also train [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn) and [Mask R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn) using ResNet-50 and [RegNetX-3.2G](https://github.com/open-mmlab/mmdetection/blob/main/configs/regnet) with multi-scale training and longer schedules. These models serve as strong pre-trained models for downstream tasks for convenience.\n\n## Speed benchmark\n\n### Training Speed benchmark\n\nWe provide [analyze_logs.py](https://github.com/open-mmlab/mmdetection/blob/main/tools/analysis_tools/analyze_logs.py) to get average time of iteration in training. You can find examples in [Log Analysis](https://mmdetection.readthedocs.io/en/latest/useful_tools.html#log-analysis).\n\nWe compare the training speed of Mask R-CNN with some other popular frameworks (The data is copied from [detectron2](https://github.com/facebookresearch/detectron2/blob/main/docs/notes/benchmarks.md/)).\nFor mmdetection, we benchmark with [mask-rcnn_r50-caffe_fpn_poly-1x_coco_v1.py](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_poly-1x_coco_v1.py), which should have the same setting with [mask_rcnn_R_50_FPN_noaug_1x.yaml](https://github.com/facebookresearch/detectron2/blob/main/configs/Detectron1-Comparisons/mask_rcnn_R_50_FPN_noaug_1x.yaml) of detectron2.\nWe also provide the [checkpoint](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug_compare_20200518-10127928.pth) and [training log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug_20200518_105755.log.json) for reference. The throughput is computed as the average throughput in iterations 100-500 to skip GPU warmup time.\n\n| Implementation                                                                         | Throughput (img/s) |\n| -------------------------------------------------------------------------------------- | ------------------ |\n| [Detectron2](https://github.com/facebookresearch/detectron2)                           | 62                 |\n| [MMDetection](https://github.com/open-mmlab/mmdetection)                               | 61                 |\n| [maskrcnn-benchmark](https://github.com/facebookresearch/maskrcnn-benchmark/)          | 53                 |\n| [tensorpack](https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN) | 50                 |\n| [simpledet](https://github.com/TuSimple/simpledet/)                                    | 39                 |\n| [Detectron](https://github.com/facebookresearch/Detectron)                             | 19                 |\n| [matterport/Mask_RCNN](https://github.com/matterport/Mask_RCNN/)                       | 14                 |\n\n### Inference Speed Benchmark\n\nWe provide [benchmark.py](https://github.com/open-mmlab/mmdetection/blob/main/tools/analysis_tools/benchmark.py) to benchmark the inference latency.\nThe script benchmarkes the model with 2000 images and calculates the average time ignoring first 5 times. You can change the output log interval (defaults: 50) by setting `LOG-INTERVAL`.\n\n```shell\npython tools/benchmark.py ${CONFIG} ${CHECKPOINT} [--log-interval $[LOG-INTERVAL]] [--fuse-conv-bn]\n```\n\nThe latency of all models in our model zoo is benchmarked without setting `fuse-conv-bn`, you can get a lower latency by setting it.\n\n## Comparison with Detectron2\n\nWe compare mmdetection with [Detectron2](https://github.com/facebookresearch/detectron2.git) in terms of speed and performance.\nWe use the commit id [185c27e](https://github.com/facebookresearch/detectron2/tree/185c27e4b4d2d4c68b5627b3765420c6d7f5a659)(30/4/2020) of detectron.\nFor fair comparison, we install and run both frameworks on the same machine.\n\n### Hardware\n\n- 8 NVIDIA Tesla V100 (32G) GPUs\n- Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz\n\n### Software environment\n\n- Python 3.7\n- PyTorch 1.4\n- CUDA 10.1\n- CUDNN 7.6.03\n- NCCL 2.4.08\n\n### Performance\n\n| Type                                                                                                                            | Lr schd | Detectron2                                                                                                                           | mmdetection | Download                                                                                                                                                                                                                                                                                                                                                         |\n| ------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-1x_coco.py) | 1x      | [37.9](https://github.com/facebookresearch/detectron2/blob/main/configs/COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml)                 | 38.0        | [model](https://download.openmmlab.com/mmdetection/v2.0/benchmark/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco-5324cff8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco_20200429_234554.log.json)             |\n| [Mask R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py)  | 1x      | [38.6 & 35.2](https://github.com/facebookresearch/detectron2/blob/main/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml) | 38.8 & 35.4 | [model](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco-dbecf295.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco_20200430_054239.log.json) |\n| [Retinanet](https://github.com/open-mmlab/mmdetection/blob/main/configs/retinanet/retinanet_r50-caffe_fpn_ms-1x_coco.py)        | 1x      | [36.5](https://github.com/facebookresearch/detectron2/blob/master/configs/COCO-Detection/retinanet_R_50_FPN_1x.yaml)                 | 37.0        | [model](https://download.openmmlab.com/mmdetection/v2.0/benchmark/retinanet_r50_caffe_fpn_mstrain_1x_coco/retinanet_r50_caffe_fpn_mstrain_1x_coco-586977a0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/retinanet_r50_caffe_fpn_mstrain_1x_coco/retinanet_r50_caffe_fpn_mstrain_1x_coco_20200430_014748.log.json)                     |\n\n### Training Speed\n\nThe training speed is measure with s/iter. The lower, the better.\n\n| Type         | Detectron2 | mmdetection |\n| ------------ | ---------- | ----------- |\n| Faster R-CNN | 0.210      | 0.216       |\n| Mask R-CNN   | 0.261      | 0.265       |\n| Retinanet    | 0.200      | 0.205       |\n\n### Inference Speed\n\nThe inference speed is measured with fps (img/s) on a single GPU, the higher, the better.\nTo be consistent with Detectron2, we report the pure inference speed (without the time of data loading).\nFor Mask R-CNN, we exclude the time of RLE encoding in post-processing.\nWe also include the officially reported speed in the parentheses, which is slightly higher\nthan the results tested on our server due to differences of hardwares.\n\n| Type         | Detectron2  | mmdetection |\n| ------------ | ----------- | ----------- |\n| Faster R-CNN | 25.6 (26.3) | 22.2        |\n| Mask R-CNN   | 22.5 (23.3) | 19.6        |\n| Retinanet    | 17.8 (18.2) | 20.6        |\n\n### Training memory\n\n| Type         | Detectron2 | mmdetection |\n| ------------ | ---------- | ----------- |\n| Faster R-CNN | 3.0        | 3.8         |\n| Mask R-CNN   | 3.4        | 3.9         |\n| Retinanet    | 3.9        | 3.4         |\n"
  },
  {
    "path": "docs/en/notes/changelog.md",
    "content": "# Changelog of v3.x\n\n## v3.3.0 (05/01/2024)\n\n### Highlights\n\nGrounding-DINO is a state-of-the-art open-set detection model that tackles multiple vision tasks including Open-Vocabulary Detection (OVD), Phrase Grounding (PG), and Referring Expression Comprehension (REC). Its effectiveness has led to its widespread adoption as a mainstream architecture for various downstream applications. However, despite its significance, the original Grounding-DINO model lacks comprehensive public technical details due to the unavailability of its training code. To bridge this gap, we present MM-Grounding-DINO, an open-source, comprehensive, and user-friendly baseline, which is built with the MMDetection toolbox. It adopts abundant vision datasets for pre-training and various detection and grounding datasets for fine-tuning. We give a comprehensive analysis of each reported result and detailed settings for reproduction. The extensive experiments on the benchmarks mentioned demonstrate that our MM-Grounding-DINO-Tiny outperforms the Grounding-DINO-Tiny baseline. We release all our models to the research community.\n\n### New Features\n\n- Add RTMDet Swin / ConvNeXt backbone and results (#11259)\n- Add `odinw` configs and evaluation results of `GLIP` (#11175)\n- Add optional score threshold option to `coco_error_analysis.py` (#11117)\n- Add new configs for `panoptic_fpn` (#11109)\n- Replace partially weighted download links with OpenXLab for the `Faster-RCNN` (#11173)\n\n### Bug Fixes\n\n- Fix `Grounding DINO` nan when class tokens exceeds 256 (#11066)\n- Fix the `CO-DETR` config files error (#11325)\n- Fix `CO-DETR` load_from url in config (#11220)\n- Fixed mask shape after Albu postprocess (#11280)\n- Fix bug in `convert_coco_format`  and `youtubevis2coco` (#11251, #11086)\n\n### Contributors\n\nA total of 15 developers contributed to this release.\n\nThank @adnan-mujagic, @Cycyes, @ilcopione, @returnL, @honeybadger78, @okotaku, @xushilin1, @keyhsw, @guyleaf, @Crescent-Saturn, @LRJKD, @aaronzs, @Divadi, @AwePhD, @hhaAndroid\n\n## v3.2.0 (12/10/2023)\n\n### Highlights\n\n**(1) Detection Transformer SOTA Model Collection**\n\n- Supported four updated and stronger SOTA Transformer models: DDQ, CO-DETR, AlignDETR, and H-DINO.\n- Based on CO-DETR, MMDet released a model with a COCO performance of 64.1 mAP.\n- Algorithms such as DINO support AMP/Checkpoint/FrozenBN, which can effectively reduce memory usage.\n\n**(2) Comprehensive Performance Comparison between CNN and Transformer**\n\nRF100 consists of a dataset collection of 100 real-world datasets, including 7 domains. It can be used to assess the performance differences of Transformer models like DINO and CNN-based algorithms under different scenarios and data volumes. Users can utilize this benchmark to quickly evaluate the robustness of their algorithms in various scenarios.\n\n**(3) Support for GLIP and Grounding DINO fine-tuning, the only algorithm library that supports Grounding DINO fine-tuning**\n\nThe Grounding DINO algorithm in MMDet is the only library that supports fine-tuning. Its performance is one point higher than the official version, and of course, GLIP also outperforms the official version.\nWe also provide a detailed process for training and evaluating Grounding DINO on custom datasets. Everyone is welcome to give it a try.\n\n**(4) Support for the open-vocabulary detection algorithm Detic and multi-dataset joint training.**\n\n**(5) Training detection models using FSDP and DeepSpeed.**\n\n**(6) Support for the V3Det dataset, a large-scale detection dataset with over 13,000 categories.**\n\n### New Features\n\n- Support CO-DETR/DDQ/AlignDETR/H-DINO\n- Support GLIP and Grounding DINO fine-tuning\n- Support Detic and Multi-Datasets training (#10926)\n- Support V3Det and benchmark (#10938)\n- Support Roboflow 100 Benchmark (#10915)\n- Add custom dataset of grounding dino (#11012)\n- Release RTMDet-X p6 (#10993)\n- Support AMP of DINO (#10827)\n- Support FrozenBN (#10845)\n- Add new configuration files for `QDTrack/DETR/RTMDet/MaskRCNN/DINO/DeformableDETR/MaskFormer` algorithm\n- Add a new script to support the WBF (#10808)\n- Add `large_image_demo` (#10719)\n- Support download dataset from OpenXLab (#10799)\n- Update to support torch2onnx for DETR series models (#10910)\n- Translation into Chinese of an English document (#10744, #10756, #10805, #10848)\n\n### Bug Fixes\n\n- Fix name error in DETR metafile.yml (#10595)\n- Fix device of the tensors in `set_nms` (#10574)\n- Remove some unicode chars from `en/` docs (#10648)\n- Fix download dataset with mim script. (#10727)\n- Fix export to torchserve (#10694)\n- Fix typo in `mask-rcnn_r50_fpn_1x-wandb_coco` (#10757)\n- Fix `eval_recalls` error in `voc_metric` (#10770)\n- Fix torch version comparison (#10934)\n- Fix incorrect behavior to access train pipeline from ConcatDataset in `analyze_results.py` (#11004)\n\n### Improvements\n\n- Update `useful_tools.md` (#10587)\n- Update Instance segmentation Tutorial (#10711)\n- Update `train.py` to compat with new config (#11025)\n- Support `torch2onnx` for maskformer series (#10782)\n\n### Contributors\n\nA total of 36 developers contributed to this release.\n\nThank @YQisme, @nskostas, @max-unfinity, @evdcush, @Xiangxu-0103, @ZhaoCake, @RangeKing, @captainIT, @ODAncona, @aaronzs, @zeyuanyin, @gotjd709, @Musiyuan, @YanxingLiu, @RunningLeon, @ytzfhqs, @zhangzhidaSunny, @yeungkong, @crazysteeaam, @timerring, @okotaku, @apatsekin, @Morty-Xu, @Markson-Young, @ZhaoQiiii, @Kuro96, @PhoenixZ810, @yhcao6, @myownskyW7, @jiongjiongli, @Johnson-Wang, @ryylcc, @guyleaf, @agpeshal, @SimonGuoNjust, @hhaAndroid\n\n## v3.1.0 (30/6/2023)\n\n### Highlights\n\n- Supports tracking algorithms including multi-object tracking (MOT) algorithms SORT, DeepSORT, StrongSORT, OCSORT, ByteTrack, QDTrack, and video instance segmentation (VIS) algorithm MaskTrackRCNN, Mask2Former-VIS.\n- Support [ViTDet](../../../projects/ViTDet)\n- Supports inference and evaluation of multimodal algorithms [GLIP](../../../configs/glip) and [XDecoder](../../../projects/XDecoder), and also supports datasets such as COCO semantic segmentation, COCO Caption, ADE20k general segmentation, and RefCOCO. GLIP fine-tuning will be supported in the future.\n- Provides a [gradio demo](https://github.com/open-mmlab/mmdetection/blob/dev-3.x/projects/gradio_demo/README.md) for image type tasks of MMDetection, making it easy for users to experience.\n\n### New Features\n\n- Support DSDL Dataset (#9801)\n- Support iSAID dataset (#10028)\n- Support VISION dataset (#10530)\n- Release SoftTeacher checkpoints (#10119)\n- Release `centernet-update_r50-caffe_fpn_ms-1x_coco` checkpoints  (#10327)\n- Support SIoULoss (#10290)\n- Support Eqlv2 loss (#10120)\n- Support CopyPaste when mask is not available (#10509)\n- Support MIM to download ODL dataset (#10460)\n- Support new config (#10566)\n\n### Bug Fixes\n\n- Fix benchmark scripts error in windows (#10128)\n- Fix error of `YOLOXModeSwitchHook` does not switch the mode when resumed from the checkpoint after switched (#10116)\n- Fix pred and weight dims unmatch in SmoothL1Loss (#10423)\n\n### Improvements\n\n- Update MMDet_Tutorial.ipynb (#10081)\n- Support to hide inference progress (#10519)\n- Replace mmcls with mmpretrain  (#10545)\n\n### Contributors\n\nA total of 29 developers contributed to this release.\n\nThanks @lovelykite, @minato-ellie, @freepoet, @wufan-tb, @yalibian, @keyakiluo, @gihanjayatilaka, @i-aki-y, @xin-li-67, @RangeKing, @JingweiZhang12, @MambaWong, @lucianovk, @tall-josh, @xiuqhou, @jamiechoi1995, @YQisme, @yechenzhi, @bjzhb666, @xiexinch, @jamiechoi1995, @yarkable, @Renzhihan, @nijkah, @amaizr, @Lum1104, @zwhus, @Czm369, @hhaAndroid\n\n## v3.0.0 (6/4/2023)\n\n### Highlights\n\n- Support Semi-automatic annotation Base [Label-Studio](../../../projects/LabelStudio) (#10039)\n- Support [EfficientDet](../../../projects/EfficientDet) in projects (#9810)\n\n### New Features\n\n- File I/O migration and reconstruction (#9709)\n- Release DINO Swin-L 36e model (#9927)\n\n### Bug Fixes\n\n- Fix benchmark script (#9865)\n- Fix the crop method of PolygonMasks (#9858)\n- Fix Albu augmentation with the mask shape (#9918)\n- Fix `RTMDetIns` prior generator device error (#9964)\n- Fix `img_shape` in data pipeline (#9966)\n- Fix cityscapes import error (#9984)\n- Fix `solov2_r50_fpn_ms-3x_coco.py` config error (#10030)\n- Fix Conditional DETR AP and Log (#9889)\n- Fix accepting an unexpected argument local-rank in PyTorch 2.0 (#10050)\n- Fix `common/ms_3x_coco-instance.py` config error (#10056)\n- Fix compute flops error (#10051)\n- Delete `data_root` in `CocoOccludedSeparatedMetric` to fix bug (#9969)\n- Unifying metafile.yml (#9849)\n\n### Improvements\n\n- Added BoxInst r101 config (#9967)\n- Added config migration guide (#9960)\n- Added more social networking links (#10021)\n- Added RTMDet config introduce (#10042)\n- Added visualization docs (#9938, #10058)\n- Refined data_prepare docs (#9935)\n- Added support for setting the cache_size_limit parameter of dynamo in PyTorch 2.0 (#10054)\n- Updated coco_metric.py (#10033)\n- Update type hint (#10040)\n\n### Contributors\n\nA total of 19 developers contributed to this release.\n\nThanks @IRONICBo, @vansin, @RangeKing, @Ghlerrix, @okotaku, @JosonChan1998, @zgzhengSE, @bobo0810, @yechenzh, @Zheng-LinXiao, @LYMDLUT, @yarkable, @xiejiajiannb, @chhluo, @BIGWangYuDong, @RangiLy, @zwhus, @hhaAndroid, @ZwwWayne\n\n## v3.0.0rc6 (24/2/2023)\n\n### Highlights\n\n- Support [Boxinst](../../../configs/boxinst), [Objects365 Dataset](../../../configs/objects365), and [Separated and Occluded COCO metric](../user_guides/useful_tools.md#COCO-Separated-&-Occluded-Mask-Metric)\n- Support [ConvNeXt-V2](../../../projects/ConvNeXt-V2), [DiffusionDet](../../../projects/DiffusionDet), and inference of [EfficientDet](../../../projects/EfficientDet) and [Detic](../../../projects/Detic) in `Projects`\n- Refactor [DETR](../../../configs/detr) series and support [Conditional-DETR](../../../configs/conditional_detr), [DAB-DETR](../../../configs/dab_detr), and [DINO](../../../configs/detr)\n- Support `DetInferencer` for inference, Test Time Augmentation, and automatically importing modules from registry\n- Support RTMDet-Ins ONNXRuntime and TensorRT [deployment](../../../configs/rtmdet/README.md#deployment-tutorial)\n- Support [calculating FLOPs of detectors](../user_guides/useful_tools.md#Model-Complexity)\n\n### New Features\n\n- Support [Boxinst](https://arxiv.org/abs/2012.02310) (#9525)\n- Support [Objects365 Dataset](https://openaccess.thecvf.com/content_ICCV_2019/papers/Shao_Objects365_A_Large-Scale_High-Quality_Dataset_for_Object_Detection_ICCV_2019_paper.pdf) (#9600)\n- Support [ConvNeXt-V2](http://arxiv.org/abs/2301.00808) in `Projects` (#9619)\n- Support [DiffusionDet](https://arxiv.org/abs/2211.09788) in `Projects` (#9639, #9768)\n- Support [Detic](http://arxiv.org/abs/2201.02605) inference in `Projects` (#9645)\n- Support [EfficientDet](https://arxiv.org/abs/1911.09070) inference in `Projects` (#9645)\n- Support [Separated and Occluded COCO metric](https://arxiv.org/abs/2210.10046) (#9710)\n- Support auto import modules from registry (#9143)\n- Refactor DETR series and support Conditional-DETR, DAB-DETR and DINO (#9646)\n- Support `DetInferencer` for inference (#9561)\n- Support Test Time Augmentation (#9452)\n- Support calculating FLOPs of detectors (#9777)\n\n### Bug Fixes\n\n- Fix deprecating old type alias due to new version of numpy (#9625, #9537)\n- Fix VOC metrics (#9784)\n- Fix the wrong link of RTMDet-x log (#9549)\n- Fix RTMDet link in README (#9575)\n- Fix MMDet get flops error (#9589)\n- Fix `use_depthwise` in RTMDet (#9624)\n- Fix `albumentations` augmentation post process with masks (#9551)\n- Fix DETR series Unit Test (#9647)\n- Fix `LoadPanopticAnnotations` bug (#9703)\n- Fix `isort` CI (#9680)\n- Fix amp pooling overflow (#9670)\n- Fix docstring about noise in DINO (#9747)\n- Fix potential bug in `MultiImageMixDataset` (#9764)\n\n### Improvements\n\n- Replace NumPy transpose with PyTorch permute to speed-up (#9762)\n- Deprecate `sklearn` (#9725)\n- Add RTMDet-Ins deployment guide (#9823)\n- Update RTMDet config and README (#9603)\n- Replace the models used in the tutorial document with RTMDet (#9843)\n- Adjust the minimum supported python version to 3.7 (#9602)\n- Support modifying palette through configuration (#9445)\n- Update README document in `Project` (#9599)\n- Replace `github` with `gitee` in `.pre-commit-config-zh-cn.yaml` file (#9586)\n- Use official `isort` in `.pre-commit-config.yaml` file (#9701)\n- Change MMCV minimum version to `2.0.0rc4` for `dev-3.x` (#9695)\n- Add Chinese version of single_stage_as_rpn.md and test_results_submission.md (#9434)\n- Add OpenDataLab download link (#9605, #9738)\n- Add type hints of several layers (#9346)\n- Add typehint for `DarknetBottleneck` (#9591)\n- Add dockerfile (#9659)\n- Add twitter, discord, medium, and youtube link (#9775)\n- Prepare for merging refactor-detr (#9656)\n- Add metafile to ConditionalDETR, DABDETR and DINO (#9715)\n- Support to modify `non_blocking` parameters (#9723)\n- Comment repeater visualizer register (#9740)\n- Update user guide: `finetune.md` and `inference.md` (#9578)\n\n### New Contributors\n\n- @NoFish-528 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9346>\n- @137208 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9434>\n- @lyviva made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9625>\n- @zwhus made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9589>\n- @zylo117 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9670>\n- @chg0901 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9740>\n- @DanShouzhu made their first contribution in https://github.com/open-mmlab/mmdetection/pull/9578\n\n### Contributors\n\nA total of 27 developers contributed to this release.\n\nThanks @JosonChan1998, @RangeKing, @NoFish-528, @likyoo, @Xiangxu-0103, @137208, @PeterH0323, @tianleiSHI, @wufan-tb, @lyviva, @zwhus, @jshilong, @Li-Qingyun, @sanbuphy, @zylo117, @triple-Mu, @KeiChiTse, @LYMDLUT, @nijkah, @chg0901, @DanShouzhu, @zytx121, @vansin, @BIGWangYuDong, @hhaAndroid, @RangiLyu, @ZwwWayne\n\n## v3.0.0rc5 (26/12/2022)\n\n### Highlights\n\n- Support [RTMDet](https://arxiv.org/abs/2212.07784) instance segmentation models. The technical report of RTMDet is on [arxiv](https://arxiv.org/abs/2212.07784)\n- Support SSHContextModule in paper [SSH: Single Stage Headless Face Detector](https://arxiv.org/abs/1708.03979).\n\n### New Features\n\n- Support [RTMDet](https://arxiv.org/abs/2212.07784) instance segmentation models and improve RTMDet test config (#9494)\n- Support SSHContextModule in paper [SSH: Single Stage Headless Face Detector](https://arxiv.org/abs/1708.03979) (#8953)\n- Release [CondInst](https://arxiv.org/abs/2003.05664) pre-trained model (#9406)\n\n### Bug Fixes\n\n- Fix CondInst predict error when `batch_size` is greater than 1 in inference (#9400)\n- Fix the bug of visualization when the dtype of the pipeline output image is not uint8 in browse dataset (#9401)\n- Fix `analyze_logs.py` to plot mAP and calculate train time correctly (#9409)\n- Fix backward inplace error with `PAFPN` (#9450)\n- Fix config import links in model converters (#9441)\n- Fix `DeformableDETRHead` object has no attribute `loss_single` (#9477)\n- Fix the logic of pseudo bboxes predicted by teacher model in SemiBaseDetector (#9414)\n- Fix demo API in instance segmentation tutorial (#9226)\n- Fix `analyze_results` (#9380)\n- Fix the error that Readthedocs API cannot be displayed (#9510)\n- Fix the error when there are no prediction results and support visualize the groundtruth of TTA (#9840)\n\n### Improvements\n\n- Remove legacy `builder.py` (#9479)\n- Make sure the pipeline argument shape is in `(width, height)` order (#9324)\n- Add `.pre-commit-config-zh-cn.yaml` file (#9388)\n- Refactor dataset metainfo to lowercase (#9469)\n- Add PyTorch 1.13 checking in CI (#9478)\n- Adjust `FocalLoss` and `QualityFocalLoss` to allow different kinds of targets (#9481)\n- Refactor `setup.cfg` (#9370)\n- Clip saturation value to valid range `[0, 1]` (#9391)\n- Only keep meta and state_dict when publishing model (#9356)\n- Add segm evaluator in ms-poly_3x_coco_instance config (#9524)\n- Update deployment guide (#9527)\n- Update zh_cn `faq.md` (#9396)\n- Update `get_started` (#9480)\n- Update the zh_cn user_guides of `useful_tools.md` and `useful_hooks.md` (#9453)\n- Add type hints for `bfp` and `channel_mapper` (#9410)\n- Add type hints of several losses (#9397)\n- Add type hints and update docstring for task modules (#9468)\n\n### New Contributors\n\n- @lihua199710 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9388>\n- @twmht made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9450>\n- @tianleiSHI made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9453>\n- @kitecats made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9481>\n- @QJC123654 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9468>\n\n### Contributors\n\nA total of 20 developers contributed to this release.\n\nThanks @liuyanyi, @RangeKing, @lihua199710, @MambaWong, @sanbuphy, @Xiangxu-0103, @twmht, @JunyaoHu, @Chan-Sun, @tianleiSHI, @zytx121, @kitecats, @QJC123654, @JosonChan1998, @lvhan028, @Czm369, @BIGWangYuDong, @RangiLyu, @hhaAndroid, @ZwwWayne\n\n## v3.0.0rc4 (23/11/2022)\n\n### Highlights\n\n- Support [CondInst](https://arxiv.org/abs/2003.05664)\n- Add `projects/` folder, which will be a place for some experimental models/features.\n- Support [SparseInst](https://arxiv.org/abs/2203.12827) in [`projects`](./projects/SparseInst/README.md)\n\n### New Features\n\n- Support [CondInst](https://arxiv.org/abs/2003.05664) (#9223)\n- Add `projects/` folder, which will be a place for some experimental models/features (#9341)\n- Support [SparseInst](https://arxiv.org/abs/2203.12827) in [`projects`](./projects/SparseInst/README.md) (#9377)\n\n### Bug Fixes\n\n- Fix `pixel_decoder_type` discrimination in MaskFormer Head. (#9176)\n- Fix wrong padding value in cached MixUp (#9259)\n- Rename `utils/typing.py` to `utils/typing_utils.py` to fix `collect_env` error (#9265)\n- Fix resume arg conflict (#9287)\n- Fix the configs of Faster R-CNN with caffe backbone (#9319)\n- Fix torchserve and update related documentation (#9343)\n- Fix bbox refine bug with sigmooid activation (#9538)\n\n### Improvements\n\n- Update the docs of GIoU Loss in README (#8810)\n- Handle dataset wrapper in `inference_detector` (#9144)\n- Update the type of `counts` in COCO's compressed RLE (#9274)\n- Support saving config file in `print_config` (#9276)\n- Update docs about video inference (#9305)\n- Update guide about model deployment (#9344)\n- Fix doc typos of useful tools (#9177)\n- Allow to resume from specific checkpoint in CLI (#9284)\n- Update FAQ about windows installation issues of pycocotools (#9292)\n\n### New Contributors\n\n- @Daa98 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9274>\n- @lvhan028 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9344>\n\n### Contributors\n\nA total of 12 developers contributed to this release.\n\nThanks @sanbuphy, @Czm369, @Daa98, @jbwang1997, @BIGWangYuDong, @JosonChan1998, @lvhan028, @RunningLeon, @RangiLyu, @Daa98, @ZwwWayne, @hhaAndroid\n\n## v3.0.0rc3 (4/11/2022)\n\nUpgrade the minimum version requirement of MMEngine to 0.3.0 to use `ignore_key` of `ConcatDataset` for training VOC datasets (#9058)\n\n### Highlights\n\n- Support [CrowdDet](https://arxiv.org/abs/2003.09163) and [EIoU Loss](https://ieeexplore.ieee.org/document/9429909)\n- Support training detection models in Detectron2\n- Refactor Fast R-CNN\n\n### New Features\n\n- Support [CrowdDet](https://arxiv.org/abs/2003.09163) (#8744)\n- Support training detection models in Detectron2 with examples of Mask R-CNN, Faster R-CNN, and RetinaNet (#8672)\n- Support [EIoU Loss](https://ieeexplore.ieee.org/document/9429909) (#9086)\n\n### Bug Fixes\n\n- Fix `XMLDataset` image size error (#9216)\n- Fix bugs of empty_instances when predicting without nms in roi_head (#9015)\n- Fix the config file of DETR (#9158)\n- Fix SOLOv2 cannot dealing with empty gt image (#9192)\n- Fix inference demo (#9153)\n- Add `ignore_key` in VOC `ConcatDataset` (#9058)\n- Fix dumping results issue in test scripts. (#9241)\n- Fix configs of training coco subsets on MMDet 3.x (#9225)\n- Fix corner2hbox of HorizontalBoxes for supporting empty bboxes (#9140)\n\n### Improvements\n\n- Refactor Fast R-CNN (#9132)\n- Clean requirements of mmcv-full due to SyncBN (#9207)\n- Support training detection models in detectron2 (#8672)\n- Add `box_type` support for `DynamicSoftLabelAssigner` (#9179)\n- Make scipy as a default dependency in runtime (#9187)\n- Update eval_metric (#9062)\n- Add `seg_map_suffix` in `BaseDetDataset` (#9088)\n\n### New Contributors\n\n- @Wwupup made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9086>\n- @sanbuphy made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9153>\n- @cxiang26 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9158>\n- @JosonChan1998 made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9225>\n\n### Contributors\n\nA total of 13 developers contributed to this release.\n\nThanks @wanghonglie, @Wwupup, @sanbuphy, @BIGWangYuDong, @liuyanyi, @cxiang26, @jbwang1997, @ZwwWayne, @yuyoujiang, @RangiLyu, @hhaAndroid, @JosonChan1998, @Czm369\n\n## v3.0.0rc2 (21/10/2022)\n\n### Highlights\n\n- Support [imagenet pre-training](configs/rtmdet/cspnext_imagenet_pretrain) for RTMDet's backbone\n\n### New Features\n\n- Support [imagenet pre-training](configs/rtmdet/cspnext_imagenet_pretrain) for RTMDet's backbone (#8887)\n- Add `CrowdHumanDataset` and Metric (#8430)\n- Add `FixShapeResize` to support resize of fixed shape (#8665)\n\n### Bug Fixes\n\n- Fix `ConcatDataset` Import Error (#8909)\n- Fix `CircleCI` and `readthedoc` build failed (#8980, #8963)\n- Fix bitmap mask translate when `out_shape` is different (#8993)\n- Fix inconsistency in `Conv2d` weight channels (#8948)\n- Fix bugs when plotting loss curve by analyze_logs.py (#8944)\n- Fix type change of labels in `albumentations` (#9074)\n- Fix some docs and types error (#8818)\n- Update memory occupation of `RTMDet` in metafile (#9098)\n- Fix wrong arguments of `OpenImageMetrics` in the config (#9061)\n\n### Improvements\n\n- Refactor standard roi head with `box type` (#8658)\n- Support mask concatenation in `BitmapMasks` and `PolygonMasks` (#9006)\n- Update PyTorch and dependencies' version in dockerfile (#8845)\n- Update `robustness_eval.py` and `print_config` (#8452)\n- Make compatible with `ConfigDict` and `dict` in `dense_heads` (#8942)\n- Support logging coco metric copypaste (#9012)\n- Remove `Normalize` transform (#8913)\n- Support jittering the color of different instances of the same class (#8988)\n- Add assertion for missing key in `PackDetInputs` (#8982)\n\n### New Contributors\n\n- @Chan-Sun made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/8909>\n- @MambaWong made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/8913>\n- @yuyoujiang made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/8437>\n- @sltlls made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/8944>\n- @Nioolek made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/8845>\n- @wufan-tb made their first contribution in <https://github.com/open-mmlab/mmdetection/pull/9061>\n\n### Contributors\n\nA total of 13 developers contributed to this release.\n\nThanks @RangiLyu, @jbwang1997, @wanghonglie, @Chan-Sun, @RangeKing, @chhluo, @MambaWong, @yuyoujiang, @hhaAndroid, @sltlls, @Nioolek, @ZwwWayne, @wufan-tb\n\n## v3.0.0rc1 (26/9/2022)\n\n### Highlights\n\n- Release a high-precision, low-latency single-stage object detector [RTMDet](configs/rtmdet).\n\n### Bug Fixes\n\n- Fix UT to be compatible with PyTorch 1.6 (#8707)\n- Fix `NumClassCheckHook` bug when model is wrapped (#8794)\n- Update the right URL of R-50-FPN with BoundedIoULoss (#8805)\n- Fix potential bug of indices in RandAugment (#8826)\n- Fix some types and links (#8839, #8820, #8793, #8868)\n- Fix incorrect background fill values in `FSAF` and `RepPoints` Head (#8813)\n\n### Improvements\n\n- Refactored anchor head and base head with `box type` (#8625)\n- Refactored `SemiBaseDetector` and `SoftTeacher` (#8786)\n- Add list to dict keys to avoid modify loss dict (#8828)\n- Update `analyze_results.py` , `analyze_logs.py` and `loading.py` (#8430, #8402, #8784)\n- Support dump results in `test.py` (#8814)\n- Check empty predictions in `DetLocalVisualizer._draw_instances` (#8830)\n- Fix `floordiv` warning in `SOLO` (#8738)\n\n### Contributors\n\nA total of 16 developers contributed to this release.\n\nThanks @ZwwWayne, @jbwang1997, @Czm369, @ice-tong, @Zheng-LinXiao, @chhluo, @RangiLyu, @liuyanyi, @wanghonglie, @levan92, @JiayuXu0, @nye0, @hhaAndroid, @xin-li-67, @shuxp, @zytx121\n\n## v3.0.0rc0 (31/8/2022)\n\nWe are excited to announce the release of MMDetection 3.0.0rc0. MMDet 3.0.0rc0 is the first version of MMDetection 3.x, a part of the OpenMMLab 2.0 projects. Built upon the new [training engine](https://github.com/open-mmlab/mmengine), MMDet 3.x unifies the interfaces of the dataset, models, evaluation, and visualization with faster training and testing speed. It also provides a general semi-supervised object detection framework and strong baselines.\n\n### Highlights\n\n1. **New engine**. MMDet 3.x is based on [MMEngine](https://github.com/open-mmlab/mmengine), which provides a universal and powerful runner that allows more flexible customizations and significantly simplifies the entry points of high-level interfaces.\n\n2. **Unified interfaces**. As a part of the OpenMMLab 2.0 projects, MMDet 3.x unifies and refactors the interfaces and internal logic of training, testing, datasets, models, evaluation, and visualization. All the OpenMMLab 2.0 projects share the same design in those interfaces and logic to allow the emergence of multi-task/modality algorithms.\n\n3. **Faster speed**. We optimize the training and inference speed for common models and configurations, achieving a faster or similar speed than [Detection2](https://github.com/facebookresearch/detectron2/). Model details of benchmark will be updated in [this note](./benchmark.md#comparison-with-detectron2).\n\n4. **General semi-supervised object detection**. Benefitting from the unified interfaces, we support a general semi-supervised learning framework that works with all the object detectors supported in MMDet 3.x. Please refer to [semi-supervised object detection](../user_guides/semi_det.md) for details.\n\n5. **Strong baselines**. We release strong baselines of many popular models to enable fair comparisons among state-of-the-art models.\n\n6. **New features and algorithms**:\n\n   - Enable all the single-stage detectors to serve as region proposal networks\n   - [SoftTeacher](https://arxiv.org/abs/2106.09018)\n   - [the updated CenterNet](https://arxiv.org/abs/2103.07461)\n\n7. **More documentation and tutorials**. We add a bunch of documentation and tutorials to help users get started more smoothly. Read it [here](https://mmdetection.readthedocs.io/en/3.x/).\n\n### Breaking Changes\n\nMMDet 3.x has undergone significant changes for better design, higher efficiency, more flexibility, and more unified interfaces.\nBesides the changes in API, we briefly list the major breaking changes in this section.\nWe will update the [migration guide](../migration.md) to provide complete details and migration instructions.\nUsers can also refer to the [API doc](https://mmdetection.readthedocs.io/en/3.x/) for more details.\n\n#### Dependencies\n\n- MMDet 3.x runs on PyTorch>=1.6. We have deprecated the support of PyTorch 1.5 to embrace mixed precision training and other new features since PyTorch 1.6. Some models can still run on PyTorch 1.5, but the full functionality of MMDet 3.x is not guaranteed.\n- MMDet 3.x relies on MMEngine to run. MMEngine is a new foundational library for training deep learning models of OpenMMLab and is the core dependency of OpenMMLab 2.0 projects. The dependencies of file IO and training are migrated from MMCV 1.x to MMEngine.\n- MMDet 3.x relies on MMCV>=2.0.0rc0. Although MMCV no longer maintains the training functionalities since 2.0.0rc0, MMDet 3.x relies on the data transforms, CUDA operators, and image processing interfaces in MMCV. Note that the package `mmcv` is the version that provides pre-built CUDA operators and `mmcv-lite` does not since MMCV 2.0.0rc0, while `mmcv-full` has been deprecated since 2.0.0rc0.\n\n#### Training and testing\n\n- MMDet 3.x uses Runner in [MMEngine](https://github.com/open-mmlab/mmengine) rather than that in MMCV. The new Runner implements and unifies the building logic of the dataset, model, evaluation, and visualizer. Therefore, MMDet 3.x no longer maintains the building logic of those modules in `mmdet.train.apis` and `tools/train.py`. Those codes have been migrated into [MMEngine](https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/runner.py). Please refer to the [migration guide of Runner in MMEngine](https://mmengine.readthedocs.io/en/latest/migration/runner.html) for more details.\n- The Runner in MMEngine also supports testing and validation. The testing scripts are also simplified, which has similar logic to that in training scripts to build the runner.\n- The execution points of hooks in the new Runner have been enriched to allow more flexible customization. Please refer to the [migration guide of Hook in MMEngine](https://mmengine.readthedocs.io/en/latest/migration/hook.html) for more details.\n- Learning rate and momentum schedules have been migrated from Hook to [Parameter Scheduler in MMEngine](https://mmengine.readthedocs.io/en/latest/tutorials/param_scheduler.html). Please refer to the [migration guide of Parameter Scheduler in MMEngine](https://mmengine.readthedocs.io/en/latest/migration/param_scheduler.html) for more details.\n\n#### Configs\n\n- The [Runner in MMEngine](https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/runner.py) uses a different config structure to ease the understanding of the components in the runner. Users can read the [config example of MMDet 3.x](../user_guides/config.md) or refer to the [migration guide in MMEngine](https://mmengine.readthedocs.io/en/latest/migration/runner.html) for migration details.\n- The file names of configs and models are also refactored to follow the new rules unified across OpenMMLab 2.0 projects. The names of checkpoints are not updated for now as there is no BC-breaking of model weights between MMDet 3.x and 2.x. We will progressively replace all the model weights with those trained in MMDet 3.x. Please refer to the [user guides of config](../user_guides/config.md) for more details.\n\n#### Dataset\n\nThe Dataset classes implemented in MMDet 3.x all inherit from the `BaseDetDataset`, which inherits from the [BaseDataset in MMEngine](https://mmengine.readthedocs.io/en/latest/advanced_tutorials/basedataset.html). In addition to the changes in interfaces, there are several changes in Dataset in MMDet 3.x.\n\n- All the datasets support serializing the internal data list to reduce the memory when multiple workers are built for data loading.\n- The internal data structure in the dataset is changed to be self-contained (without losing information like class names in MMDet 2.x) while keeping simplicity.\n- The evaluation functionality of each dataset has been removed from the dataset so that some specific evaluation metrics like COCO AP can be used to evaluate the prediction on other datasets.\n\n#### Data Transforms\n\nThe data transforms in MMDet 3.x all inherits from `BaseTransform` in MMCV>=2.0.0rc0, which defines a new convention in OpenMMLab 2.0 projects.\nBesides the interface changes, there are several changes listed below:\n\n- The functionality of some data transforms (e.g., `Resize`) are decomposed into several transforms to simplify and clarify the usages.\n- The format of data dict processed by each data transform is changed according to the new data structure of dataset.\n- Some inefficient data transforms (e.g., normalization and padding) are moved into data preprocessor of model to improve data loading and training speed.\n- The same data transforms in different OpenMMLab 2.0 libraries have the same augmentation implementation and the logic given the same arguments, i.e., `Resize` in MMDet 3.x and MMSeg 1.x will resize the image in the exact same manner given the same arguments.\n\n#### Model\n\nThe models in MMDet 3.x all inherit from `BaseModel` in MMEngine, which defines a new convention of models in OpenMMLab 2.0 projects.\nUsers can refer to [the tutorial of the model in MMengine](https://mmengine.readthedocs.io/en/latest/tutorials/model.html) for more details.\nAccordingly, there are several changes as the following:\n\n- The model interfaces, including the input and output formats, are significantly simplified and unified following the new convention in MMDet 3.x.\n  Specifically, all the input data in training and testing are packed into `inputs` and `data_samples`, where `inputs` contains model inputs like a list of image tensors, and `data_samples` contains other information of the current data sample such as ground truths, region proposals, and model predictions. In this way, different tasks in MMDet 3.x can share the same input arguments, which makes the models more general and suitable for multi-task learning and some flexible training paradigms like semi-supervised learning.\n- The model has a data preprocessor module, which is used to pre-process the input data of the model. In MMDet 3.x, the data preprocessor usually does the necessary steps to form the input images into a batch, such as padding. It can also serve as a place for some special data augmentations or more efficient data transformations like normalization.\n- The internal logic of the model has been changed. In MMdet 2.x, model uses `forward_train`, `forward_test`, `simple_test`, and `aug_test` to deal with different model forward logics. In MMDet 3.x and OpenMMLab 2.0, the forward function has three modes: 'loss', 'predict', and 'tensor' for training, inference, and tracing or other purposes, respectively.\n  The forward function calls `self.loss`, `self.predict`, and `self._forward` given the modes 'loss', 'predict', and 'tensor', respectively.\n\n#### Evaluation\n\nThe evaluation in MMDet 2.x strictly binds with the dataset. In contrast, MMDet 3.x decomposes the evaluation from dataset so that all the detection datasets can evaluate with COCO AP and other metrics implemented in MMDet 3.x.\nMMDet 3.x mainly implements corresponding metrics for each dataset, which are manipulated by [Evaluator](https://mmengine.readthedocs.io/en/latest/design/evaluator.html) to complete the evaluation.\nUsers can build an evaluator in MMDet 3.x to conduct offline evaluation, i.e., evaluate predictions that may not produce in MMDet 3.x with the dataset as long as the dataset and the prediction follow the dataset conventions. More details can be found in the [tutorial in mmengine](https://mmengine.readthedocs.io/en/latest/tutorials/evaluation.html).\n\n#### Visualization\n\nThe functions of visualization in MMDet 2.x are removed. Instead, in OpenMMLab 2.0 projects, we use [Visualizer](https://mmengine.readthedocs.io/en/latest/design/visualization.html) to visualize data. MMDet 3.x implements `DetLocalVisualizer` to allow visualization of ground truths, model predictions, feature maps, etc., at any place. It also supports sending the visualization data to any external visualization backends such as Tensorboard.\n\n### Improvements\n\n- Optimized training and testing speed of FCOS, RetinaNet, Faster R-CNN, Mask R-CNN, and Cascade R-CNN. The training speed of those models with some common training strategies is also optimized, including those with synchronized batch normalization and mixed precision training.\n- Support mixed precision training of all the models. However, some models may get undesirable performance due to some numerical issues. We will update the documentation and list the results (accuracy of failure) of mixed precision training.\n- Release strong baselines of some popular object detectors. Their accuracy and pre-trained checkpoints will be released.\n\n### Bug Fixes\n\n- DeepFashion dataset: the config and results have been updated.\n\n### New Features\n\n1. Support a general semi-supervised learning framework that works with all the object detectors supported in MMDet 3.x. Please refer to [semi-supervised object detection](../user_guides/semi_det.md) for details.\n2. Enable all the single-stage detectors to serve as region proposal networks. We give [an example of using FCOS as RPN](../user_guides/single_stage_as_rpn.md).\n3. Support a semi-supervised object detection algorithm: [SoftTeacher](https://arxiv.org/abs/2106.09018).\n4. Support [the updated CenterNet](https://arxiv.org/abs/2103.07461).\n5. Support data structures `HorizontalBoxes` and `BaseBoxes` to encapsulate different kinds of bounding boxes. We are migrating to use data structures of boxes to replace the use of pure tensor boxes. This will unify the usages of different kinds of bounding boxes in MMDet 3.x and MMRotate 1.x to simplify the implementation and reduce redundant codes.\n\n### Planned changes\n\nWe list several planned changes of MMDet 3.0.0rc0 so that the community could more comprehensively know the progress of MMDet 3.x. Feel free to create a PR, issue, or discussion if you are interested, have any suggestions and feedback, or want to participate.\n\n1. Test-time augmentation: which is supported in MMDet 2.x, is not implemented in this version due to the limited time slot. We will support it in the following releases with a new and simplified design.\n2. Inference interfaces: unified inference interfaces will be supported in the future to ease the use of released models.\n3. Interfaces of useful tools that can be used in Jupyter Notebook or Colab: more useful tools that are implemented in the `tools` directory will have their python interfaces so that they can be used in Jupyter Notebook, Colab, and downstream libraries.\n4. Documentation: we will add more design docs, tutorials, and migration guidance so that the community can deep dive into our new design, participate the future development, and smoothly migrate downstream libraries to MMDet 3.x.\n5. Wandb visualization: MMDet 2.x supports data visualization since v2.25.0, which has not been migrated to MMDet 3.x for now. Since WandB provides strong visualization and experiment management capabilities, a `DetWandBVisualizer` and maybe a hook are planned to fully migrate those functionalities from MMDet 2.x.\n6. Full support of WiderFace dataset (#8508) and Fast R-CNN: we are verifying their functionalities and will fix related issues soon.\n7. Migrate DETR-series algorithms (#8655, #8533) and YOLOv3 on IPU (#8552) from MMDet 2.x.\n\n### Contributors\n\nA total of 11 developers contributed to this release.\nThanks @shuxp, @wanghonglie, @Czm369, @BIGWangYuDong, @zytx121, @jbwang1997, @chhluo, @jshilong, @RangiLyu, @hhaAndroid, @ZwwWayne\n"
  },
  {
    "path": "docs/en/notes/changelog_v2.x.md",
    "content": "# Changelog v2.x\n\n### v2.25.0 (31/5/2022)\n\n#### Highlights\n\n- Support dedicated `WandbLogger` hook\n- Support [ConvNeXt](configs/convnext), [DDOD](configs/ddod), [SOLOv2](configs/solov2)\n- Support [Mask2Former](configs/mask2former) for instance segmentation\n- Rename [config files of Mask2Former](configs/mask2former)\n\n#### Backwards incompatible changes\n\n- Rename [config files of Mask2Former](configs/mask2former) (#7571)\n\n  <table align=\"center\">\n    <thead>\n        <tr align='center'>\n            <td>before v2.25.0</td>\n            <td>after v2.25.0</td>\n        </tr>\n    </thead>\n    <tbody><tr valign='top'>\n    <th>\n\n  - `mask2former_xxx_coco.py` represents config files for **panoptic segmentation**.\n\n  </th>\n    <th>\n\n  - `mask2former_xxx_coco.py` represents config files for **instance segmentation**.\n  - `mask2former_xxx_coco-panoptic.py` represents config files for **panoptic segmentation**.\n\n  </th></tr>\n  </tbody></table>\n\n#### New Features\n\n- Support [ConvNeXt](https://arxiv.org/abs/2201.03545) (#7281)\n- Support [DDOD](https://arxiv.org/abs/2107.02963) (#7279)\n- Support [SOLOv2](https://arxiv.org/abs/2003.10152) (#7441)\n- Support [Mask2Former](https://arxiv.org/abs/2112.01527) for instance segmentation (#7571, #8032)\n\n#### Bug Fixes\n\n- Enable YOLOX training on different devices (#7912)\n- Fix the log plot error when evaluation with `interval != 1` (#7784)\n- Fix RuntimeError of HTC (#8083)\n\n#### Improvements\n\n- Support dedicated `WandbLogger` hook (#7459)\n\n  Users can set\n\n  ```python\n  cfg.log_config.hooks = [\n    dict(type='MMDetWandbHook',\n         init_kwargs={'project': 'MMDetection-tutorial'},\n         interval=10,\n         log_checkpoint=True,\n         log_checkpoint_metadata=True,\n         num_eval_images=10)]\n  ```\n\n  in the config to use `MMDetWandbHook`. Example can be found in this [colab tutorial](https://colab.research.google.com/drive/1RCSXHZwDZvakFh3eo9RuNrJbCGqD0dru?usp=sharing#scrollTo=WTEdPDRaBz2C)\n\n- Add `AvoidOOM` to avoid OOM (#7434, #8091)\n\n  Try to use `AvoidCUDAOOM` to avoid GPU out of memory. It will first retry after calling `torch.cuda.empty_cache()`. If it still fails, it will then retry by converting the type of inputs to FP16 format. If it still fails, it will try to copy inputs from GPUs to CPUs to continue computing. Try AvoidOOM in code to make the code continue to run when GPU memory runs out:\n\n  ```python\n  from mmdet.utils import AvoidCUDAOOM\n\n  output = AvoidCUDAOOM.retry_if_cuda_oom(some_function)(input1, input2)\n  ```\n\n  Users can also try `AvoidCUDAOOM` as a decorator to make the code continue to run when GPU memory runs out:\n\n  ```python\n  from mmdet.utils import AvoidCUDAOOM\n\n  @AvoidCUDAOOM.retry_if_cuda_oom\n  def function(*args, **kwargs):\n      ...\n      return xxx\n  ```\n\n- Support reading `gpu_collect` from `cfg.evaluation.gpu_collect` (#7672)\n\n- Speedup the Video Inference by Accelerating data-loading Stage (#7832)\n\n- Support replacing the `${key}` with the value of `cfg.key` (#7492)\n\n- Accelerate result analysis in `analyze_result.py`. The evaluation time is speedup by 10 ~ 15 times and only tasks 10 ~ 15 minutes now. (#7891)\n\n- Support to set `block_dilations` in `DilatedEncoder` (#7812)\n\n- Support panoptic segmentation result analysis (#7922)\n\n- Release DyHead with Swin-Large backbone (#7733)\n\n- Documentations updating and adding\n\n  - Fix wrong default type of `act_cfg` in `SwinTransformer` (#7794)\n  - Fix text errors in the tutorials (#7959)\n  - Rewrite the [installation guide](docs/en/get_started.md) (#7897)\n  - [Useful hooks](docs/en/tutorials/useful_hooks.md) (#7810)\n  - Fix heading anchor in documentation  (#8006)\n  - Replace `markdownlint` with `mdformat` for avoiding installing ruby (#8009)\n\n#### Contributors\n\nA total of 20 developers contributed to this release.\n\nThanks @ZwwWayne, @DarthThomas, @solyaH, @LutingWang, @chenxinfeng4, @Czm369, @Chenastron, @chhluo, @austinmw, @Shanyaliux @hellock, @Y-M-Y, @jbwang1997, @hhaAndroid, @Irvingao, @zhanggefan, @BIGWangYuDong, @Keiku, @PeterVennerstrom, @ayulockin\n\n### v2.24.0 (26/4/2022)\n\n#### Highlights\n\n- Support [Simple Copy-Paste is a Strong Data Augmentation Method for Instance Segmentation](https://arxiv.org/abs/2012.07177)\n- Support automatically scaling LR according to GPU number and samples per GPU\n- Support Class Aware Sampler that improves performance on OpenImages Dataset\n\n#### New Features\n\n- Support [Simple Copy-Paste is a Strong Data Augmentation Method for Instance Segmentation](https://arxiv.org/abs/2012.07177), see [example configs](configs/simple_copy_paste/mask_rcnn_r50_fpn_syncbn-all_rpn-2conv_ssj_scp_32x2_270k_coco.py) (#7501)\n\n- Support Class Aware Sampler, users can set\n\n  ```python\n  data=dict(train_dataloader=dict(class_aware_sampler=dict(num_sample_class=1))))\n  ```\n\n  in the config to use `ClassAwareSampler`. Examples can be found in [the configs of OpenImages Dataset](https://github.com/open-mmlab/mmdetection/tree/main/configs/openimages/faster_rcnn_r50_fpn_32x2_cas_1x_openimages.py).  (#7436)\n\n- Support automatically scaling LR according to GPU number and samples per GPU. (#7482)\n  In each config, there is a corresponding config of auto-scaling LR as below,\n\n  ```python\n  auto_scale_lr = dict(enable=True, base_batch_size=N)\n  ```\n\n  where `N` is the batch size used for the current learning rate in the config (also equals to `samples_per_gpu` * gpu number to train this config).\n  By default, we set `enable=False` so that the original usages will not be affected. Users can set `enable=True` in each config or add `--auto-scale-lr` after the command line to enable this feature and should check the correctness of `base_batch_size` in customized configs.\n\n- Support setting dataloader arguments in config and add functions to handle config compatibility. (#7668)\n  The comparison between the old and new usages is as below.\n\n  <table align=\"center\">\n    <thead>\n        <tr align='center'>\n            <td>v2.23.0</td>\n            <td>v2.24.0</td>\n        </tr>\n    </thead>\n    <tbody><tr valign='top'>\n    <th>\n\n  ```python\n  data = dict(\n      samples_per_gpu=64, workers_per_gpu=4,\n      train=dict(type='xxx', ...),\n      val=dict(type='xxx', samples_per_gpu=4, ...),\n      test=dict(type='xxx', ...),\n  )\n  ```\n\n  </th>\n    <th>\n\n  ```python\n  # A recommended config that is clear\n  data = dict(\n      train=dict(type='xxx', ...),\n      val=dict(type='xxx', ...),\n      test=dict(type='xxx', ...),\n      # Use different batch size during inference.\n      train_dataloader=dict(samples_per_gpu=64, workers_per_gpu=4),\n      val_dataloader=dict(samples_per_gpu=8, workers_per_gpu=2),\n      test_dataloader=dict(samples_per_gpu=8, workers_per_gpu=2),\n  )\n\n  # Old style still works but allows to set more arguments about data loaders\n  data = dict(\n      samples_per_gpu=64,  # only works for train_dataloader\n      workers_per_gpu=4,  # only works for train_dataloader\n      train=dict(type='xxx', ...),\n      val=dict(type='xxx', ...),\n      test=dict(type='xxx', ...),\n      # Use different batch size during inference.\n      val_dataloader=dict(samples_per_gpu=8, workers_per_gpu=2),\n      test_dataloader=dict(samples_per_gpu=8, workers_per_gpu=2),\n  )\n  ```\n\n  </th></tr>\n  </tbody></table>\n\n- Support memory profile hook. Users can use it to monitor the memory usages during training as below (#7560)\n\n  ```python\n  custom_hooks = [\n      dict(type='MemoryProfilerHook', interval=50)\n  ]\n  ```\n\n- Support to run on PyTorch with MLU chip (#7578)\n\n- Support re-spliting data batch with tag (#7641)\n\n- Support the `DiceCost` used by [K-Net](https://arxiv.org/abs/2106.14855) in `MaskHungarianAssigner` (#7716)\n\n- Support splitting COCO data for Semi-supervised object detection (#7431)\n\n- Support Pathlib for Config.fromfile (#7685)\n\n- Support to use file client in OpenImages dataset (#7433)\n\n- Add a probability parameter to Mosaic transformation (#7371)\n\n- Support specifying interpolation mode in `Resize` pipeline (#7585)\n\n#### Bug Fixes\n\n- Avoid invalid bbox after deform_sampling (#7567)\n- Fix the issue that argument color_theme does not take effect when exporting confusion matrix (#7701)\n- Fix the `end_level` in Necks, which should be the index of the end input backbone level (#7502)\n- Fix the bug that `mix_results` may be None in `MultiImageMixDataset` (#7530)\n- Fix the bug in ResNet plugin when two plugins are used (#7797)\n\n#### Improvements\n\n- Enhance `load_json_logs` of analyze_logs.py for resumed training logs (#7732)\n- Add argument `out_file` in image_demo.py (#7676)\n- Allow mixed precision training with `SimOTAAssigner` (#7516)\n- Updated INF to 100000.0 to be the same as that in the official YOLOX (#7778)\n- Add documentations of:\n  - how to get channels of a new backbone (#7642)\n  - how to unfreeze the backbone network (#7570)\n  - how to train fast_rcnn model (#7549)\n  - proposals in Deformable DETR (#7690)\n  - from-scratch install script in get_started.md (#7575)\n- Release pre-trained models of\n  - [Mask2Former](configs/mask2former) (#7595, #7709)\n  - RetinaNet with ResNet-18 and release models (#7387)\n  - RetinaNet with EfficientNet backbone (#7646)\n\n#### Contributors\n\nA total of 27 developers contributed to this release.\nThanks @jovialio, @zhangsanfeng2022, @HarryZJ, @jamiechoi1995, @nestiank, @PeterH0323, @RangeKing, @Y-M-Y, @mattcasey02, @weiji14, @Yulv-git, @xiefeifeihu, @FANG-MING, @meng976537406, @nijkah, @sudz123, @CCODING04, @SheffieldCao, @Czm369, @BIGWangYuDong, @zytx121, @jbwang1997, @chhluo, @jshilong, @RangiLyu, @hhaAndroid, @ZwwWayne\n\n### v2.23.0 (28/3/2022)\n\n#### Highlights\n\n- Support Mask2Former: [Masked-attention Mask Transformer for Universal Image Segmentation](https://arxiv.org/abs/2112.01527)\n- Support EfficientNet: [EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks](https://arxiv.org/abs/1905.11946)\n- Support setting data root through environment variable `MMDET_DATASETS`, users don't have to modify the corresponding path in config files anymore.\n- Find a good recipe for fine-tuning high precision ResNet backbone pre-trained by Torchvision.\n\n#### New Features\n\n- Support [Mask2Former](configs/mask2former)(#6938)(#7466)(#7471)\n- Support [EfficientNet](configs/efficientnet) (#7514)\n- Support setting data root through environment variable `MMDET_DATASETS`, users don't have to modify the corresponding path in config files anymore. (#7386)\n- Support setting different seeds to different ranks (#7432)\n- Update the `dist_train.sh` so that the script can be used to support launching multi-node training on machines without slurm (#7415)\n- Find a good recipe for fine-tuning high precision ResNet backbone pre-trained by Torchvision (#7489)\n\n#### Bug Fixes\n\n- Fix bug in VOC unit test which removes the data directory (#7270)\n- Adjust the order of `get_classes` and `FileClient` (#7276)\n- Force the inputs of `get_bboxes` in yolox_head to float32 (#7324)\n- Fix misplaced arguments in LoadPanopticAnnotations (#7388)\n- Fix reduction=mean in CELoss. (#7449)\n- Update unit test of CrossEntropyCost (#7537)\n- Fix memory leaking in panpotic segmentation evaluation (#7538)\n- Fix the bug of shape broadcast in YOLOv3 (#7551)\n\n#### Improvements\n\n- Add Chinese version of onnx2tensorrt.md (#7219)\n- Update colab tutorials (#7310)\n- Update information about Localization Distillation (#7350)\n- Add Chinese version of `finetune.md` (#7178)\n- Update YOLOX log for non square input (#7235)\n- Add `nproc` in `coco_panoptic.py` for panoptic quality computing (#7315)\n- Allow to set channel_order in LoadImageFromFile (#7258)\n- Take point sample related functions out of mask_point_head (#7353)\n- Add instance evaluation for coco_panoptic (#7313)\n- Enhance the robustness of analyze_logs.py (#7407)\n- Supplementary notes of sync_random_seed (#7440)\n- Update docstring of cross entropy loss (#7472)\n- Update pascal voc result (#7503)\n- We create How-to documentation to record any questions about How to xxx. In this version, we added\n  - How to use Mosaic augmentation (#7507)\n  - How to use backbone in mmcls (#7438)\n  - How to produce and submit the prediction results of panoptic segmentation models on COCO test-dev set (#7430))\n\n#### Contributors\n\nA total of 27 developers contributed to this release.\nThanks @ZwwWayne, @haofanwang, @shinya7y, @chhluo, @yangrisheng, @triple-Mu, @jbwang1997, @HikariTJU, @imflash217, @274869388, @zytx121, @matrixgame2018, @jamiechoi1995, @BIGWangYuDong, @JingweiZhang12, @Xiangxu-0103, @hhaAndroid, @jshilong, @osbm, @ceroytres, @bunge-bedstraw-herb, @Youth-Got, @daavoo, @jiangyitong, @RangiLyu, @CCODING04, @yarkable\n\n### v2.22.0 (24/2/2022)\n\n#### Highlights\n\n- Support MaskFormer: [Per-Pixel Classification is Not All You Need for Semantic Segmentation](https://arxiv.org/abs/2107.06278) (#7212)\n- Support DyHead: [Dynamic Head: Unifying Object Detection Heads with Attentions](https://arxiv.org/abs/2106.08322) (#6823)\n- Release a good recipe of using ResNet in object detectors pre-trained by [ResNet Strikes Back](https://arxiv.org/abs/2110.00476), which consistently brings about 3~4 mAP improvements over RetinaNet, Faster/Mask/Cascade Mask R-CNN (#7001)\n- Support [Open Images Dataset](https://storage.googleapis.com/openimages/web/index.html) (#6331)\n- Support TIMM backbone: [PyTorch Image Models](https://github.com/rwightman/pytorch-image-models) (#7020)\n\n#### New Features\n\n- Support [MaskFormer](configs/maskformer) (#7212)\n- Support [DyHead](configs/dyhead) (#6823)\n- Support [ResNet Strikes Back](configs/resnet_strikes_back) (#7001)\n- Support [OpenImages Dataset](configs/openimages) (#6331)\n- Support [TIMM backbone](configs/timm_example) (#7020)\n- Support visualization for Panoptic Segmentation (#7041)\n\n#### Breaking Changes\n\nIn order to support the visualization for Panoptic Segmentation, the `num_classes` can not be `None` when using the `get_palette` function to determine whether to use the panoptic palette.\n\n#### Bug Fixes\n\n- Fix bug for the best checkpoints can not be saved when the `key_score` is None (#7101)\n- Fix MixUp transform filter boxes failing case (#7080)\n- Add missing properties in SABLHead (#7091)\n- Fix bug when NaNs exist in confusion matrix (#7147)\n- Fix PALETTE AttributeError in downstream task (#7230)\n\n#### Improvements\n\n- Speed up SimOTA matching (#7098)\n- Add Chinese translation of `docs_zh-CN/tutorials/init_cfg.md` (#7188)\n\n#### Contributors\n\nA total of 20 developers contributed to this release.\nThanks @ZwwWayne, @hhaAndroid, @RangiLyu, @AronLin, @BIGWangYuDong, @jbwang1997, @zytx121, @chhluo, @shinya7y, @LuooChen, @dvansa, @siatwangmin, @del-zhenwu, @vikashranjan26, @haofanwang, @jamiechoi1995, @HJoonKwon, @yarkable, @zhijian-liu, @RangeKing\n\n### v2.21.0 (8/2/2022)\n\n### Breaking Changes\n\nTo standardize the contents in config READMEs and meta files of OpenMMLab projects, the READMEs and meta files in each config directory have been significantly changed. The template will be released in the future, for now, you can refer to the examples of README for [algorithm](https://github.com/open-mmlab/mmdetection/blob/master/configs/faster_rcnn/README.md), [dataset](https://github.com/open-mmlab/mmdetection/blob/master/configs/deepfashion/README.md) and [backbone](https://github.com/open-mmlab/mmdetection/blob/master/configs/regnet/README.md). To align with the standard, the configs in dcn are put into to two directories named `dcn` and `dcnv2`.\n\n#### New Features\n\n- Allow to customize colors of different classes during visualization (#6716)\n- Support CPU training (#7016)\n- Add download script of COCO, LVIS, and VOC dataset (#7015)\n\n#### Bug Fixes\n\n- Fix weight conversion issue of RetinaNet with Swin-S (#6973)\n- Update `__repr__` of `Compose` (#6951)\n- Fix BadZipFile Error when build docker (#6966)\n- Fix bug in non-distributed multi-gpu training/testing (#7019)\n- Fix bbox clamp in PyTorch 1.10 (#7074)\n- Relax the requirement of PALETTE in dataset wrappers (#7085)\n- Keep the same weights before reassign in the PAA head (#7032)\n- Update code demo in doc (#7092)\n\n#### Improvements\n\n- Speed-up training by allow to set variables of multi-processing (#6974, #7036)\n- Add links of Chinese tutorials in readme (#6897)\n- Disable cv2 multiprocessing by default for acceleration (#6867)\n- Deprecate the support for \"python setup.py test\" (#6998)\n- Re-organize metafiles and config readmes (#7051)\n- Fix None grad problem during training TOOD by adding `SigmoidGeometricMean` (#7090)\n\n#### Contributors\n\nA total of 26 developers contributed to this release.\nThanks @del-zhenwu, @zimoqingfeng, @srishilesh, @imyhxy, @jenhaoyang, @jliu-ac, @kimnamu, @ShengliLiu, @garvan2021, @ciusji, @DIYer22, @kimnamu, @q3394101, @zhouzaida, @gaotongxiao, @topsy404, @AntoAndGar, @jbwang1997, @nijkah, @ZwwWayne, @Czm369, @jshilong, @RangiLyu, @BIGWangYuDong, @hhaAndroid, @AronLin\n\n### v2.20.0 (27/12/2021)\n\n#### New Features\n\n- Support [TOOD](configs/tood/README.md): Task-aligned One-stage Object Detection (ICCV 2021 Oral) (#6746)\n- Support resuming from the latest checkpoint automatically (#6727)\n\n#### Bug Fixes\n\n- Fix wrong bbox `loss_weight` of the PAA head (#6744)\n- Fix the padding value of `gt_semantic_seg` in batch collating (#6837)\n- Fix test error of lvis when using `classwise` (#6845)\n- Avoid BC-breaking of `get_local_path`  (#6719)\n- Fix bug in `sync_norm_hook` when the BN layer does not exist (#6852)\n- Use pycocotools directly no matter what platform it is (#6838)\n\n#### Improvements\n\n- Add unit test for SimOTA with no valid bbox (#6770)\n- Use precommit to check readme (#6802)\n- Support selecting GPU-ids in non-distributed testing time (#6781)\n\n#### Contributors\n\nA total of 16 developers contributed to this release.\nThanks @ZwwWayne, @Czm369, @jshilong, @RangiLyu, @BIGWangYuDong, @hhaAndroid, @jamiechoi1995, @AronLin, @Keiku, @gkagkos, @fcakyon, @www516717402, @vansin, @zactodd, @kimnamu, @jenhaoyang\n\n### v2.19.1 (14/12/2021)\n\n#### New Features\n\n- Release [YOLOX](configs/yolox/README.md) COCO pretrained models (#6698)\n\n#### Bug Fixes\n\n- Fix DCN initialization in DenseHead (#6625)\n- Fix initialization of ConvFCHead (#6624)\n- Fix PseudoSampler in RCNN (#6622)\n- Fix weight initialization in Swin and PVT (#6663)\n- Fix dtype bug in BaseDenseHead (#6767)\n- Fix SimOTA with no valid bbox (#6733)\n\n#### Improvements\n\n- Add an example of combining swin and one-stage models (#6621)\n- Add `get_ann_info` to dataset_wrappers (#6526)\n- Support keeping image ratio in the multi-scale training of YOLOX (#6732)\n- Support `bbox_clip_border` for the augmentations of YOLOX (#6730)\n\n#### Documents\n\n- Update metafile (#6717)\n- Add mmhuman3d in readme (#6699)\n- Update FAQ docs (#6587)\n- Add doc for `detect_anomalous_params` (#6697)\n\n#### Contributors\n\nA total of 11 developers contributed to this release.\nThanks @ZwwWayne, @LJoson, @Czm369, @jshilong, @ZCMax, @RangiLyu, @BIGWangYuDong, @hhaAndroid, @zhaoxin111, @GT9505, @shinya7y\n\n### v2.19.0 (29/11/2021)\n\n#### Highlights\n\n- Support [Label Assignment Distillation](https://arxiv.org/abs/2108.10520)\n- Support `persistent_workers` for Pytorch >= 1.7\n- Align accuracy to the updated official YOLOX\n\n#### New Features\n\n- Support [Label Assignment Distillation](https://arxiv.org/abs/2108.10520) (#6342)\n- Support `persistent_workers` for Pytorch >= 1.7 (#6435)\n\n#### Bug Fixes\n\n- Fix repeatedly output warning message (#6584)\n- Avoid infinite GPU waiting in dist training (#6501)\n- Fix SSD512 config error (#6574)\n- Fix MMDetection model to ONNX command (#6558)\n\n#### Improvements\n\n- Refactor configs of FP16 models (#6592)\n- Align accuracy to the updated official YOLOX (#6443)\n- Speed up training and reduce memory cost when using PhotoMetricDistortion. (#6442)\n- Make OHEM work with seesaw loss (#6514)\n\n#### Documents\n\n- Update README.md (#6567)\n\n#### Contributors\n\nA total of 11 developers contributed to this release.\nThanks @FloydHsiu, @RangiLyu, @ZwwWayne, @AndreaPi, @st9007a, @hachreak, @BIGWangYuDong, @hhaAndroid, @AronLin, @chhluo, @vealocia, @HarborYuan, @st9007a, @jshilong\n\n### v2.18.1 (15/11/2021)\n\n#### Highlights\n\n- Release [QueryInst](http://arxiv.org/abs/2105.01928) pre-trained weights (#6460)\n- Support plot confusion matrix (#6344)\n\n#### New Features\n\n- Release [QueryInst](http://arxiv.org/abs/2105.01928) pre-trained weights (#6460)\n- Support plot confusion matrix (#6344)\n\n#### Bug Fixes\n\n- Fix aug test error when the number of prediction bboxes is 0 (#6398)\n- Fix SpatialReductionAttention in PVT (#6488)\n- Fix wrong use of `trunc_normal_init` in PVT and Swin-Transformer (#6432)\n\n#### Improvements\n\n- Save the printed AP information of COCO API to logger (#6505)\n- Always map location to cpu when load checkpoint (#6405)\n- Set a random seed when the user does not set a seed (#6457)\n\n#### Documents\n\n- Chinese version of [Corruption Benchmarking](robustness_benchmarking.md) (#6375)\n- Fix config path in docs (#6396)\n- Update GRoIE readme (#6401)\n\n#### Contributors\n\nA total of 11 developers contributed to this release.\nThanks @st9007a, @hachreak, @HarborYuan, @vealocia, @chhluo, @AndreaPi, @AronLin, @BIGWangYuDong, @hhaAndroid, @RangiLyu, @ZwwWayne\n\n### v2.18.0 (27/10/2021)\n\n#### Highlights\n\n- Support [QueryInst](http://arxiv.org/abs/2105.01928) (#6050)\n- Refactor dense heads to decouple onnx export logics from `get_bboxes` and speed up inference (#5317, #6003, #6369, #6268, #6315)\n\n#### New Features\n\n- Support [QueryInst](http://arxiv.org/abs/2105.01928) (#6050)\n- Support infinite sampler (#5996)\n\n#### Bug Fixes\n\n- Fix init_weight in fcn_mask_head (#6378)\n- Fix type error in imshow_bboxes of RPN (#6386)\n- Fix broken colab link in MMDetection Tutorial (#6382)\n- Make sure the device and dtype of scale_factor are the same as bboxes (#6374)\n- Remove sampling hardcode (#6317)\n- Fix RandomAffine bbox coordinate recorrection (#6293)\n- Fix init bug of final cls/reg layer in convfc head (#6279)\n- Fix img_shape broken in auto_augment (#6259)\n- Fix kwargs parameter missing error in two_stage (#6256)\n\n#### Improvements\n\n- Unify the interface of stuff head and panoptic head (#6308)\n- Polish readme (#6243)\n- Add code-spell pre-commit hook and fix a typo (#6306)\n- Fix typo (#6245, #6190)\n- Fix sampler unit test (#6284)\n- Fix `forward_dummy` of YOLACT to enable `get_flops` (#6079)\n- Fix link error in the config documentation (#6252)\n- Adjust the order to beautify the document (#6195)\n\n#### Refactors\n\n- Refactor one-stage get_bboxes logic (#5317)\n- Refactor ONNX export of One-Stage models (#6003, #6369)\n- Refactor dense_head and speedup (#6268)\n- Migrate to use prior_generator in training of dense heads (#6315)\n\n#### Contributors\n\nA total of 18 developers contributed to this release.\nThanks @Boyden, @onnkeat, @st9007a, @vealocia, @yhcao6, @DapangpangX, @yellowdolphin, @cclauss, @kennymckormick,\n@pingguokiller, @collinzrj, @AndreaPi, @AronLin, @BIGWangYuDong, @hhaAndroid, @jshilong, @RangiLyu, @ZwwWayne\n\n### v2.17.0 (28/9/2021)\n\n#### Highlights\n\n- Support [PVT](https://arxiv.org/abs/2102.12122) and [PVTv2](https://arxiv.org/abs/2106.13797)\n- Support [SOLO](https://arxiv.org/abs/1912.04488)\n- Support large scale jittering and New Mask R-CNN baselines\n- Speed up `YOLOv3` inference\n\n#### New Features\n\n- Support [PVT](https://arxiv.org/abs/2102.12122) and [PVTv2](https://arxiv.org/abs/2106.13797) (#5780)\n- Support [SOLO](https://arxiv.org/abs/1912.04488) (#5832)\n- Support large scale jittering and New Mask R-CNN baselines (#6132)\n- Add a general data structure for the results of models (#5508)\n- Added a base class for one-stage instance segmentation (#5904)\n- Speed up `YOLOv3` inference (#5991)\n- Release Swin Transformer pre-trained models (#6100)\n- Support mixed precision training in `YOLOX` (#5983)\n- Support `val` workflow in `YOLACT` (#5986)\n- Add script to test `torchserve` (#5936)\n- Support `onnxsim` with dynamic input shape (#6117)\n\n#### Bug Fixes\n\n- Fix the function naming errors in `model_wrappers` (#5975)\n- Fix regression loss bug when the input is an empty tensor (#5976)\n- Fix scores not contiguous error in `centernet_head` (#6016)\n- Fix missing parameters bug in `imshow_bboxes` (#6034)\n- Fix bug in `aug_test` of `HTC` when the length of `det_bboxes` is 0 (#6088)\n- Fix empty proposal errors in the training of some two-stage models (#5941)\n- Fix `dynamic_axes` parameter error in `ONNX` dynamic shape export (#6104)\n- Fix `dynamic_shape` bug of `SyncRandomSizeHook` (#6144)\n- Fix the Swin Transformer config link error in the configuration (#6172)\n\n#### Improvements\n\n- Add filter rules in `Mosaic` transform (#5897)\n- Add size divisor in get flops to avoid some potential bugs (#6076)\n- Add Chinese translation of `docs_zh-CN/tutorials/customize_dataset.md` (#5915)\n- Add Chinese translation of `conventions.md` (#5825)\n- Add description of the output of data pipeline (#5886)\n- Add dataset information in the README file for `PanopticFPN` (#5996)\n- Add `extra_repr` for `DropBlock` layer to get details in the model printing (#6140)\n- Fix CI out of memory and add PyTorch1.9 Python3.9 unit tests (#5862)\n- Fix download links error of some model (#6069)\n- Improve the generalization of XML dataset (#5943)\n- Polish assertion error messages (#6017)\n- Remove `opencv-python-headless` dependency by `albumentations` (#5868)\n- Check dtype in transform unit tests (#5969)\n- Replace the default theme of documentation with PyTorch Sphinx Theme (#6146)\n- Update the paper and code fields in the metafile (#6043)\n- Support to customize padding value of segmentation map (#6152)\n- Support to resize multiple segmentation maps (#5747)\n\n#### Contributors\n\nA total of 24 developers contributed to this release.\nThanks @morkovka1337, @HarborYuan, @guillaumefrd, @guigarfr, @www516717402, @gaotongxiao, @ypwhs, @MartaYang, @shinya7y, @justiceeem, @zhaojinjian0000, @VVsssssk, @aravind-anantha, @wangbo-zhao, @czczup, @whai362, @czczup, @marijnl, @AronLin, @BIGWangYuDong, @hhaAndroid, @jshilong, @RangiLyu, @ZwwWayne\n\n### v2.16.0 (30/8/2021)\n\n#### Highlights\n\n- Support [Panoptic FPN](https://arxiv.org/abs/1901.02446) and [Swin Transformer](https://arxiv.org/abs/2103.14030)\n\n#### New Features\n\n- Support [Panoptic FPN](https://arxiv.org/abs/1901.02446) and release models (#5577, #5902)\n- Support Swin Transformer backbone (#5748)\n- Release RetinaNet models pre-trained with multi-scale 3x schedule (#5636)\n- Add script to convert unlabeled image list to coco format (#5643)\n- Add hook to check whether the loss value is valid (#5674)\n- Add YOLO anchor optimizing tool (#5644)\n- Support export onnx models without post process. (#5851)\n- Support classwise evaluation in CocoPanopticDataset (#5896)\n- Adapt browse_dataset for concatenated datasets. (#5935)\n- Add `PatchEmbed` and `PatchMerging` with `AdaptivePadding` (#5952)\n\n#### Bug Fixes\n\n- Fix unit tests of YOLOX (#5859)\n- Fix lose randomness in `imshow_det_bboxes` (#5845)\n- Make output result of `ImageToTensor` contiguous (#5756)\n- Fix inference bug when calling `regress_by_class` in RoIHead in some cases (#5884)\n- Fix bug in CIoU loss where alpha should not have gradient. (#5835)\n- Fix the bug that `multiscale_output` is defined but not used in HRNet (#5887)\n- Set the priority of EvalHook to LOW. (#5882)\n- Fix a YOLOX bug when applying bbox rescaling in test mode (#5899)\n- Fix mosaic coordinate error (#5947)\n- Fix dtype of bbox in RandomAffine. (#5930)\n\n#### Improvements\n\n- Add Chinese version of `data_pipeline` and  (#5662)\n- Support to remove state dicts of EMA when publishing models. (#5858)\n- Refactor the loss function in HTC and SCNet (#5881)\n- Use warnings instead of logger.warning (#5540)\n- Use legacy coordinate in metric of VOC (#5627)\n- Add Chinese version of customize_losses (#5826)\n- Add Chinese version of model_zoo (#5827)\n\n#### Contributors\n\nA total of 19 developers contributed to this release.\nThanks @ypwhs, @zywvvd, @collinzrj, @OceanPang, @ddonatien, @@haotian-liu, @viibridges, @Muyun99, @guigarfr, @zhaojinjian0000, @jbwang1997,@wangbo-zhao, @xvjiarui, @RangiLyu, @jshilong, @AronLin, @BIGWangYuDong, @hhaAndroid, @ZwwWayne\n\n### v2.15.1 (11/8/2021)\n\n#### Highlights\n\n- Support [YOLOX](https://arxiv.org/abs/2107.08430)\n\n#### New Features\n\n- Support [YOLOX](https://arxiv.org/abs/2107.08430)(#5756, #5758, #5760, #5767, #5770, #5774, #5777, #5808, #5828, #5848)\n\n#### Bug Fixes\n\n- Update correct SSD models. (#5789)\n- Fix casting error in mask structure (#5820)\n- Fix MMCV deployment documentation links. (#5790)\n\n#### Improvements\n\n- Use dynamic MMCV download link in TorchServe dockerfile (#5779)\n- Rename the function `upsample_like` to `interpolate_as` for more general usage (#5788)\n\n#### Contributors\n\nA total of 14 developers contributed to this release.\nThanks @HAOCHENYE, @xiaohu2015, @HsLOL, @zhiqwang, @Adamdad, @shinya7y, @Johnson-Wang, @RangiLyu, @jshilong, @mmeendez8, @AronLin, @BIGWangYuDong, @hhaAndroid, @ZwwWayne\n\n### v2.15.0 (02/8/2021)\n\n#### Highlights\n\n- Support adding [MIM](https://github.com/open-mmlab/mim) dependencies during pip installation\n- Support MobileNetV2 for SSD-Lite and YOLOv3\n- Support Chinese Documentation\n\n#### New Features\n\n- Add function `upsample_like` (#5732)\n- Support to output pdf and epub format documentation (#5738)\n- Support and release Cascade Mask R-CNN 3x pre-trained models (#5645)\n- Add `ignore_index` to CrossEntropyLoss (#5646)\n- Support adding [MIM](https://github.com/open-mmlab/mim) dependencies during pip installation (#5676)\n- Add MobileNetV2 config and models for YOLOv3 (#5510)\n- Support COCO Panoptic Dataset (#5231)\n- Support ONNX export of cascade models (#5486)\n- Support DropBlock with RetinaNet (#5544)\n- Support MobileNetV2 SSD-Lite (#5526)\n\n#### Bug Fixes\n\n- Fix the device of label in multiclass_nms (#5673)\n- Fix error of backbone initialization from pre-trained checkpoint in config file (#5603, #5550)\n- Fix download links of RegNet pretrained weights (#5655)\n- Fix two-stage runtime error given empty proposal (#5559)\n- Fix flops count error in DETR (#5654)\n- Fix unittest for `NumClassCheckHook` when it is not used. (#5626)\n- Fix description bug of using custom dataset (#5546)\n- Fix bug of `multiclass_nms` that returns the global indices (#5592)\n- Fix `valid_mask` logic error in RPNHead (#5562)\n- Fix unit test error of pretrained configs (#5561)\n- Fix typo error in anchor_head.py (#5555)\n- Fix bug when using dataset wrappers (#5552)\n- Fix a typo error in demo/MMDet_Tutorial.ipynb (#5511)\n- Fixing crash in `get_root_logger` when `cfg.log_level` is not None (#5521)\n- Fix docker version (#5502)\n- Fix optimizer parameter error when using `IterBasedRunner` (#5490)\n\n#### Improvements\n\n- Add unit tests for MMTracking (#5620)\n- Add Chinese translation of documentation (#5718, #5618, #5558, #5423, #5593, #5421, #5408. #5369, #5419, #5530, #5531)\n- Update resource limit (#5697)\n- Update docstring for InstaBoost (#5640)\n- Support key `reduction_override` in all loss functions (#5515)\n- Use repeatdataset to accelerate CenterNet training (#5509)\n- Remove unnecessary code in autoassign (#5519)\n- Add documentation about `init_cfg` (#5273)\n\n#### Contributors\n\nA total of 18 developers contributed to this release.\nThanks @OceanPang, @AronLin, @hellock, @Outsider565, @RangiLyu, @ElectronicElephant, @likyoo, @BIGWangYuDong, @hhaAndroid, @noobying, @yyz561, @likyoo,\n@zeakey, @ZwwWayne, @ChenyangLiu, @johnson-magic, @qingswu, @BuxianChen\n\n### v2.14.0 (29/6/2021)\n\n#### Highlights\n\n- Add `simple_test` to dense heads to improve the consistency of single-stage and two-stage detectors\n- Revert the `test_mixins` to single image test to improve efficiency and readability\n- Add Faster R-CNN and Mask R-CNN config using multi-scale training with 3x schedule\n\n#### New Features\n\n- Support pretrained models from MoCo v2 and SwAV (#5286)\n- Add Faster R-CNN and Mask R-CNN config using multi-scale training with 3x schedule (#5179, #5233)\n- Add `reduction_override` in MSELoss (#5437)\n- Stable support of exporting DETR to ONNX with dynamic shapes and batch inference (#5168)\n- Stable support of exporting PointRend to ONNX with dynamic shapes and batch inference (#5440)\n\n#### Bug Fixes\n\n- Fix size mismatch bug in `multiclass_nms` (#4980)\n- Fix the import path of `MultiScaleDeformableAttention` (#5338)\n- Fix errors in config of GCNet ResNext101 models (#5360)\n- Fix Grid-RCNN error when there is no bbox result (#5357)\n- Fix errors in `onnx_export` of bbox_head when setting reg_class_agnostic (#5468)\n- Fix type error of AutoAssign in the document (#5478)\n- Fix web links ending with `.md` (#5315)\n\n#### Improvements\n\n- Add `simple_test` to dense heads to improve the consistency of single-stage and two-stage detectors (#5264)\n- Add support for mask diagonal flip in TTA (#5403)\n- Revert the `test_mixins` to single image test to improve efficiency and readability (#5249)\n- Make YOLOv3 Neck more flexible (#5218)\n- Refactor SSD to make it more general (#5291)\n- Refactor `anchor_generator` and `point_generator` (#5349)\n- Allow to configure out the `mask_head` of the HTC algorithm (#5389)\n- Delete deprecated warning in FPN (#5311)\n- Move `model.pretrained` to `model.backbone.init_cfg` (#5370)\n- Make deployment tools more friendly to use (#5280)\n- Clarify installation documentation (#5316)\n- Add ImageNet Pretrained Models docs (#5268)\n- Add FAQ about training loss=nan solution and COCO AP or AR =-1 (# 5312, #5313)\n- Change all weight links of http to https (#5328)\n\n### v2.13.0 (01/6/2021)\n\n#### Highlights\n\n- Support new methods: [CenterNet](https://arxiv.org/abs/1904.07850), [Seesaw Loss](https://arxiv.org/abs/2008.10032), [MobileNetV2](https://arxiv.org/abs/1801.04381)\n\n#### New Features\n\n- Support paper [Objects as Points](https://arxiv.org/abs/1904.07850) (#4602)\n- Support paper [Seesaw Loss for Long-Tailed Instance Segmentation (CVPR 2021)](https://arxiv.org/abs/2008.10032) (#5128)\n- Support [MobileNetV2](https://arxiv.org/abs/1801.04381) backbone and inverted residual block (#5122)\n- Support [MIM](https://github.com/open-mmlab/mim) (#5143)\n- ONNX exportation with dynamic shapes of CornerNet (#5136)\n- Add `mask_soft` config option to allow non-binary masks (#4615)\n- Add PWC metafile (#5135)\n\n#### Bug Fixes\n\n- Fix YOLOv3 FP16 training error (#5172)\n- Fix Cacscade R-CNN TTA test error when `det_bboxes` length is 0  (#5221)\n- Fix `iou_thr` variable naming errors in VOC recall calculation function (#5195)\n- Fix Faster R-CNN performance dropped in ONNX Runtime (#5197)\n- Fix DETR dict changed error when using python 3.8 during iteration  (#5226)\n\n#### Improvements\n\n- Refactor ONNX export of two stage detector (#5205)\n- Replace MMDetection's EvalHook with MMCV's EvalHook for consistency  (#4806)\n- Update RoI extractor for ONNX (#5194)\n- Use better parameter initialization in YOLOv3 head for higher performance (#5181)\n- Release new DCN models of Mask R-CNN by mixed-precision training (#5201)\n- Update YOLOv3 model weights (#5229)\n- Add DetectoRS ResNet-101 model weights (#4960)\n- Discard bboxes with sizes equals to `min_bbox_size` (#5011)\n- Remove duplicated code in DETR head (#5129)\n- Remove unnecessary object in class definition (#5180)\n- Fix doc link (#5192)\n\n### v2.12.0 (01/5/2021)\n\n#### Highlights\n\n- Support new methods: [AutoAssign](https://arxiv.org/abs/2007.03496), [YOLOF](https://arxiv.org/abs/2103.09460), and [Deformable DETR](https://arxiv.org/abs/2010.04159)\n- Stable support of exporting models to ONNX with batched images and dynamic shape (#5039)\n\n#### Backwards Incompatible Changes\n\nMMDetection is going through big refactoring for more general and convenient usages during the releases from v2.12.0 to v2.15.0 (maybe longer).\nIn v2.12.0 MMDetection inevitably brings some BC-breakings, including the MMCV dependency, model initialization, model registry, and mask AP evaluation.\n\n- MMCV version. MMDetection v2.12.0 relies on the newest features in MMCV 1.3.3, including `BaseModule` for unified parameter initialization, model registry, and the CUDA operator `MultiScaleDeformableAttn` for [Deformable DETR](https://arxiv.org/abs/2010.04159). Note that MMCV 1.3.2 already contains all the features used by MMDet but has known issues. Therefore, we recommend users skip MMCV v1.3.2 and use v1.3.3, though v1.3.2 might work for most cases.\n- Unified model initialization (#4750). To unify the parameter initialization in OpenMMLab projects, MMCV supports `BaseModule` that accepts `init_cfg` to allow the modules' parameters initialized in a flexible and unified manner. Now the users need to explicitly call `model.init_weights()` in the training script to initialize the model (as in [here](https://github.com/open-mmlab/mmdetection/blob/master/tools/train.py#L162), previously this was handled by the detector. The models in MMDetection have been re-benchmarked to ensure accuracy based on PR #4750. __The downstream projects should update their code accordingly to use MMDetection v2.12.0__.\n- Unified model registry (#5059). To easily use backbones implemented in other OpenMMLab projects, MMDetection migrates to inherit the model registry created in MMCV (#760). In this way, as long as the backbone is supported in an OpenMMLab project and that project also uses the registry in MMCV, users can use that backbone in MMDetection by simply modifying the config without copying the code of that backbone into MMDetection.\n- Mask AP evaluation (#4898). Previous versions calculate the areas of masks through the bounding boxes when calculating the mask AP of small, medium, and large instances. To indeed use the areas of masks, we pop the key `bbox` during mask AP calculation. This change does not affect the overall mask AP evaluation and aligns the mask AP of similar models in other projects like Detectron2.\n\n#### New Features\n\n- Support paper [AutoAssign: Differentiable Label Assignment for Dense Object Detection](https://arxiv.org/abs/2007.03496) (#4295)\n- Support paper [You Only Look One-level Feature](https://arxiv.org/abs/2103.09460) (#4295)\n- Support paper [Deformable DETR: Deformable Transformers for End-to-End Object Detection](https://arxiv.org/abs/2010.04159) (#4778)\n- Support calculating IoU with FP16 tensor in `bbox_overlaps` to save memory and keep speed (#4889)\n- Add `__repr__` in custom dataset to count the number of instances (#4756)\n- Add windows support by updating requirements.txt (#5052)\n- Stable support of exporting models to ONNX with batched images and dynamic shape, including SSD, FSAF,FCOS, YOLOv3, RetinaNet, Faster R-CNN, and Mask R-CNN (#5039)\n\n#### Improvements\n\n- Use MMCV `MODEL_REGISTRY` (#5059)\n- Unified parameter initialization for more flexible usage (#4750)\n- Rename variable names and fix docstring in anchor head (#4883)\n- Support training with empty GT in Cascade RPN (#4928)\n- Add more details of usage of `test_robustness` in documentation (#4917)\n- Changing to use `pycocotools` instead of `mmpycocotools` to fully support Detectron2 and MMDetection in one environment (#4939)\n- Update torch serve dockerfile to support dockers of more versions (#4954)\n- Add check for training with single class dataset (#4973)\n- Refactor transformer and DETR Head (#4763)\n- Update FPG model zoo (#5079)\n- More accurate mask AP of small/medium/large instances (#4898)\n\n#### Bug Fixes\n\n- Fix bug in mean_ap.py when calculating mAP by 11 points (#4875)\n- Fix error when key `meta` is not in old checkpoints (#4936)\n- Fix hanging bug when training with empty GT in VFNet, GFL, and FCOS by changing the place of `reduce_mean` (#4923, #4978, #5058)\n- Fix asyncronized inference error and provide related demo (#4941)\n- Fix IoU losses dimensionality unmatch error (#4982)\n- Fix torch.randperm whtn using PyTorch 1.8 (#5014)\n- Fix empty bbox error in `mask_head` when using CARAFE (#5062)\n- Fix `supplement_mask` bug when there are zero-size RoIs (#5065)\n- Fix testing with empty rois in RoI Heads (#5081)\n\n### v2.11.0 (01/4/2021)\n\n__Highlights__\n\n- Support new method: [Localization Distillation for Object Detection](https://arxiv.org/pdf/2102.12252.pdf)\n- Support Pytorch2ONNX with batch inference and dynamic shape\n\n__New Features__\n\n- Support [Localization Distillation for Object Detection](https://arxiv.org/pdf/2102.12252.pdf) (#4758)\n- Support Pytorch2ONNX with batch inference and dynamic shape for Faster-RCNN and mainstream one-stage detectors (#4796)\n\n__Improvements__\n\n- Support batch inference in head of RetinaNet (#4699)\n- Add batch dimension in second stage of Faster-RCNN (#4785)\n- Support batch inference in bbox coder (#4721)\n- Add check for `ann_ids` in `COCODataset` to ensure it is unique (#4789)\n- support for showing the FPN results (#4716)\n- support dynamic shape for grid_anchor (#4684)\n- Move pycocotools version check to when it is used (#4880)\n\n__Bug Fixes__\n\n- Fix a bug of TridentNet when doing the batch inference (#4717)\n- Fix a bug of Pytorch2ONNX in FASF (#4735)\n- Fix a bug when show the image with float type (#4732)\n\n### v2.10.0 (01/03/2021)\n\n#### Highlights\n\n- Support new methods: [FPG](https://arxiv.org/abs/2004.03580)\n- Support ONNX2TensorRT for SSD, FSAF, FCOS, YOLOv3, and Faster R-CNN.\n\n#### New Features\n\n- Support ONNX2TensorRT for SSD, FSAF, FCOS, YOLOv3, and Faster R-CNN (#4569)\n- Support [Feature Pyramid Grids (FPG)](https://arxiv.org/abs/2004.03580) (#4645)\n- Support video demo (#4420)\n- Add seed option for sampler (#4665)\n- Support to customize type of runner (#4570, #4669)\n- Support synchronizing BN buffer in `EvalHook` (#4582)\n- Add script for GIF demo (#4573)\n\n#### Bug Fixes\n\n- Fix ConfigDict AttributeError and add Colab link (#4643)\n- Avoid crash in empty gt training of GFL head (#4631)\n- Fix `iou_thrs` bug in RPN evaluation (#4581)\n- Fix syntax error of config when upgrading model version (#4584)\n\n#### Improvements\n\n- Refactor unit test file structures (#4600)\n- Refactor nms config (#4636)\n- Get loading pipeline by checking the class directly rather than through config strings (#4619)\n- Add doctests for mask target generation and mask structures (#4614)\n- Use deep copy when copying pipeline arguments (#4621)\n- Update documentations (#4642, #4650, #4620, #4630)\n- Remove redundant code calling `import_modules_from_strings` (#4601)\n- Clean deprecated FP16 API (#4571)\n- Check whether `CLASSES` is correctly initialized in the initialization of `XMLDataset` (#4555)\n- Support batch inference in the inference API (#4462, #4526)\n- Clean deprecated warning and fix 'meta' error (#4695)\n\n### v2.9.0 (01/02/2021)\n\n#### Highlights\n\n- Support new methods: [SCNet](https://arxiv.org/abs/2012.10150), [Sparse R-CNN](https://arxiv.org/abs/2011.12450)\n- Move `train_cfg` and `test_cfg` into model in configs\n- Support to visualize results based on prediction quality\n\n#### New Features\n\n- Support [SCNet](https://arxiv.org/abs/2012.10150) (#4356)\n- Support [Sparse R-CNN](https://arxiv.org/abs/2011.12450) (#4219)\n- Support evaluate mAP by multiple IoUs (#4398)\n- Support concatenate dataset for testing (#4452)\n- Support to visualize results based on prediction quality (#4441)\n- Add ONNX simplify option to Pytorch2ONNX script (#4468)\n- Add hook for checking compatibility of class numbers in heads and datasets (#4508)\n\n#### Bug Fixes\n\n- Fix CPU inference bug of Cascade RPN (#4410)\n- Fix NMS error of CornerNet when there is no prediction box (#4409)\n- Fix TypeError in CornerNet inference (#4411)\n- Fix bug of PAA when training with background images (#4391)\n- Fix the error that the window data is not destroyed when `out_file is not None` and `show==False` (#4442)\n- Fix order of NMS `score_factor` that will decrease the performance of YOLOv3 (#4473)\n- Fix bug in HTC TTA when the number of detection boxes is 0 (#4516)\n- Fix resize error in mask data structures (#4520)\n\n#### Improvements\n\n- Allow to customize classes in LVIS dataset (#4382)\n- Add tutorials for building new models with existing datasets (#4396)\n- Add CPU compatibility information in documentation (#4405)\n- Add documentation of deprecated `ImageToTensor` for batch inference (#4408)\n- Add more details in documentation for customizing dataset (#4430)\n- Switch `imshow_det_bboxes` visualization backend from OpenCV to Matplotlib (#4389)\n- Deprecate `ImageToTensor` in `image_demo.py` (#4400)\n- Move train_cfg/test_cfg into model (#4347, #4489)\n- Update docstring for `reg_decoded_bbox` option in bbox heads (#4467)\n- Update dataset information in documentation (#4525)\n- Release pre-trained R50 and R101 PAA detectors with multi-scale 3x training schedules (#4495)\n- Add guidance for speed benchmark (#4537)\n\n### v2.8.0 (04/01/2021)\n\n#### Highlights\n\n- Support new methods: [Cascade RPN](https://arxiv.org/abs/1909.06720), [TridentNet](https://arxiv.org/abs/1901.01892)\n\n#### New Features\n\n- Support [Cascade RPN](https://arxiv.org/abs/1909.06720) (#1900)\n- Support [TridentNet](https://arxiv.org/abs/1901.01892) (#3313)\n\n#### Bug Fixes\n\n- Fix bug of show result in async_benchmark (#4367)\n- Fix scale factor in MaskTestMixin (#4366)\n- Fix but when returning indices in `multiclass_nms` (#4362)\n- Fix bug of empirical attention in resnext backbone error (#4300)\n- Fix bug of `img_norm_cfg` in FCOS-HRNet models with updated performance and models (#4250)\n- Fix invalid checkpoint and log in Mask R-CNN models on Cityscapes dataset (#4287)\n- Fix bug in distributed sampler when dataset is too small (#4257)\n- Fix bug of 'PAFPN has no attribute extra_convs_on_inputs' (#4235)\n\n#### Improvements\n\n- Update model url from aws to aliyun (#4349)\n- Update ATSS for PyTorch 1.6+ (#4359)\n- Update script to install ruby in pre-commit installation (#4360)\n- Delete deprecated `mmdet.ops` (#4325)\n- Refactor hungarian assigner for more general usage in Sparse R-CNN (#4259)\n- Handle scipy import in DETR to reduce package dependencies (#4339)\n- Update documentation of usages for config options after MMCV (1.2.3) supports overriding list in config (#4326)\n- Update pre-train models of faster rcnn trained on COCO subsets (#4307)\n- Avoid zero or too small value for beta in Dynamic R-CNN (#4303)\n- Add doccumentation for Pytorch2ONNX (#4271)\n- Add deprecated warning FPN arguments (#4264)\n- Support returning indices of kept bboxes when using nms (#4251)\n- Update type and device requirements when creating tensors `GFLHead` (#4210)\n- Update device requirements when creating tensors in `CrossEntropyLoss` (#4224)\n\n### v2.7.0 (30/11/2020)\n\n- Support new method: [DETR](https://arxiv.org/abs/2005.12872), [ResNest](https://arxiv.org/abs/2004.08955), Faster R-CNN DC5.\n- Support YOLO, Mask R-CNN, and Cascade R-CNN models exportable to ONNX.\n\n#### New Features\n\n- Support [DETR](https://arxiv.org/abs/2005.12872) (#4201, #4206)\n- Support to link the best checkpoint in training (#3773)\n- Support to override config through options in inference.py (#4175)\n- Support YOLO, Mask R-CNN, and Cascade R-CNN models exportable to ONNX (#4087, #4083)\n- Support [ResNeSt](https://arxiv.org/abs/2004.08955) backbone (#2959)\n- Support unclip border bbox regression (#4076)\n- Add tpfp func in evaluating AP (#4069)\n- Support mixed precision training of SSD detector with other backbones (#4081)\n- Add Faster R-CNN DC5 models (#4043)\n\n#### Bug Fixes\n\n- Fix bug of `gpu_id` in distributed training mode (#4163)\n- Support Albumentations with version higher than 0.5 (#4032)\n- Fix num_classes bug in faster rcnn config (#4088)\n- Update code in docs/2_new_data_model.md (#4041)\n\n#### Improvements\n\n- Ensure DCN offset to have similar type as features in VFNet (#4198)\n- Add config links in README files of models (#4190)\n- Add tutorials for loss conventions (#3818)\n- Add solution to installation issues in 30-series GPUs (#4176)\n- Update docker version in get_started.md (#4145)\n- Add model statistics and polish some titles in configs README (#4140)\n- Clamp neg probability in FreeAnchor (#4082)\n- Speed up expanding large images (#4089)\n- Fix Pytorch 1.7 incompatibility issues (#4103)\n- Update trouble shooting page to resolve segmentation fault (#4055)\n- Update aLRP-Loss in project page (#4078)\n- Clean duplicated `reduce_mean` function (#4056)\n- Refactor Q&A (#4045)\n\n### v2.6.0 (1/11/2020)\n\n- Support new method: [VarifocalNet](https://arxiv.org/abs/2008.13367).\n- Refactored documentation with more tutorials.\n\n#### New Features\n\n- Support GIoU calculation in `BboxOverlaps2D`, and re-implement `giou_loss` using `bbox_overlaps` (#3936)\n- Support random sampling in CPU mode (#3948)\n- Support VarifocalNet (#3666, #4024)\n\n#### Bug Fixes\n\n- Fix SABL validating bug in Cascade R-CNN (#3913)\n- Avoid division by zero in PAA head when num_pos=0 (#3938)\n- Fix temporary directory bug of multi-node testing error (#4034, #4017)\n- Fix `--show-dir` option in test script (#4025)\n- Fix GA-RetinaNet r50 model url (#3983)\n- Update code in docs and fix broken urls (#3947)\n\n#### Improvements\n\n- Refactor pytorch2onnx API into `mmdet.core.export` and use `generate_inputs_and_wrap_model` for pytorch2onnx (#3857, #3912)\n- Update RPN upgrade scripts for v2.5.0 compatibility (#3986)\n- Use mmcv `tensor2imgs` (#4010)\n- Update test robustness (#4000)\n- Update trouble shooting page (#3994)\n- Accelerate PAA training speed (#3985)\n- Support batch_size > 1 in validation (#3966)\n- Use RoIAlign implemented in MMCV for inference in CPU mode (#3930)\n- Documentation refactoring (#4031)\n\n### v2.5.0 (5/10/2020)\n\n#### Highlights\n\n- Support new methods: [YOLACT](https://arxiv.org/abs/1904.02689), [CentripetalNet](https://arxiv.org/abs/2003.09119).\n- Add more documentations for easier and more clear usage.\n\n#### Backwards Incompatible Changes\n\n__FP16 related methods are imported from mmcv instead of mmdet. (#3766, #3822)__\nMixed precision training utils in `mmdet.core.fp16` are moved to `mmcv.runner`, including `force_fp32`, `auto_fp16`, `wrap_fp16_model`, and `Fp16OptimizerHook`. A deprecation warning will be raised if users attempt to import those methods from `mmdet.core.fp16`, and will be finally removed in V2.10.0.\n\n__\\[0, N-1\\] represents foreground classes and N indicates background classes for all models. (#3221)__\nBefore v2.5.0, the background label for RPN is 0, and N for other heads. Now the behavior is consistent for all models. Thus `self.background_labels` in `dense_heads` is removed and all heads use `self.num_classes` to indicate the class index of background labels.\nThis change has no effect on the pre-trained models in the v2.x model zoo, but will affect the training of all models with RPN heads. Two-stage detectors whose RPN head uses softmax will be affected because the order of categories is changed.\n\n**Only call `get_subset_by_classes` when `test_mode=True` and `self.filter_empty_gt=True` (#3695)**\nFunction `get_subset_by_classes` in dataset is refactored and only filters out images when `test_mode=True` and `self.filter_empty_gt=True`.\nIn the original implementation, `get_subset_by_classes` is not related to the flag `self.filter_empty_gt` and will only be called when the classes is set during initialization no matter `test_mode` is `True` or `False`. This brings ambiguous behavior and potential bugs in many cases. After v2.5.0, if `filter_empty_gt=False`, no matter whether the classes are specified in a dataset, the dataset will use all the images in the annotations. If `filter_empty_gt=True` and `test_mode=True`, no matter whether the classes are specified, the dataset will call \\`\\`get_subset_by_classes\\` to check the images and filter out images containing no GT boxes. Therefore, the users should be responsible for the data filtering/cleaning process for the test dataset.\n\n#### New Features\n\n- Test time augmentation for single stage detectors (#3844, #3638)\n- Support to show the name of experiments during training (#3764)\n- Add `Shear`, `Rotate`, `Translate` Augmentation (#3656, #3619, #3687)\n- Add image-only transformations including `Constrast`, `Equalize`, `Color`, and `Brightness`. (#3643)\n- Support [YOLACT](https://arxiv.org/abs/1904.02689) (#3456)\n- Support [CentripetalNet](https://arxiv.org/abs/2003.09119) (#3390)\n- Support PyTorch 1.6 in docker (#3905)\n\n#### Bug Fixes\n\n- Fix the bug of training ATSS when there is no ground truth boxes (#3702)\n- Fix the bug of using Focal Loss when there is `num_pos` is 0 (#3702)\n- Fix the label index mapping in dataset browser (#3708)\n- Fix Mask R-CNN training stuck problem when their is no positive rois (#3713)\n- Fix the bug of `self.rpn_head.test_cfg` in `RPNTestMixin` by using `self.rpn_head` in rpn head (#3808)\n- Fix deprecated `Conv2d` from mmcv.ops (#3791)\n- Fix device bug in RepPoints (#3836)\n- Fix SABL validating bug (#3849)\n- Use `https://download.openmmlab.com/mmcv/dist/index.html` for installing MMCV (#3840)\n- Fix nonzero in NMS for PyTorch 1.6.0 (#3867)\n- Fix the API change bug of PAA (#3883)\n- Fix typo in bbox_flip (#3886)\n- Fix cv2 import error of ligGL.so.1 in Dockerfile (#3891)\n\n#### Improvements\n\n- Change to use `mmcv.utils.collect_env` for collecting environment information to avoid duplicate codes (#3779)\n- Update checkpoint file names to v2.0 models in documentation (#3795)\n- Update tutorials for changing runtime settings (#3778), modifying loss (#3777)\n- Improve the function of `simple_test_bboxes` in SABL (#3853)\n- Convert mask to bool before using it as img's index for robustness and speedup (#3870)\n- Improve documentation of modules and dataset customization (#3821)\n\n### v2.4.0 (5/9/2020)\n\n__Highlights__\n\n- Fix lots of issues/bugs and reorganize the trouble shooting page\n- Support new methods [SABL](https://arxiv.org/abs/1912.04260), [YOLOv3](https://arxiv.org/abs/1804.02767), and [PAA Assign](https://arxiv.org/abs/2007.08103)\n- Support Batch Inference\n- Start to publish `mmdet` package to PyPI since v2.3.0\n- Switch model zoo to download.openmmlab.com\n\n__Backwards Incompatible Changes__\n\n- Support Batch Inference (#3564, #3686, #3705): Since v2.4.0, MMDetection could inference model with multiple images in a single GPU.\n  This change influences all the test APIs in MMDetection and downstream codebases. To help the users migrate their code, we use `replace_ImageToTensor` (#3686) to convert legacy test data pipelines during dataset initialization.\n- Support RandomFlip with horizontal/vertical/diagonal direction (#3608): Since v2.4.0, MMDetection supports horizontal/vertical/diagonal flip in the data augmentation. This influences bounding box, mask, and image transformations in data augmentation process and the process that will map those data back to the original format.\n- Migrate to use `mmlvis` and `mmpycocotools` for COCO and LVIS dataset (#3727). The APIs are fully compatible with the original `lvis` and `pycocotools`. Users need to uninstall the existing pycocotools and lvis packages in their environment first and install `mmlvis` & `mmpycocotools`.\n\n__Bug Fixes__\n\n- Fix default mean/std for onnx (#3491)\n- Fix coco evaluation and add metric items (#3497)\n- Fix typo for install.md (#3516)\n- Fix atss when sampler per gpu is 1 (#3528)\n- Fix import of fuse_conv_bn (#3529)\n- Fix bug of gaussian_target, update unittest of heatmap (#3543)\n- Fixed VOC2012 evaluate (#3553)\n- Fix scale factor bug of rescale (#3566)\n- Fix with_xxx_attributes in base detector (#3567)\n- Fix boxes scaling when number is 0 (#3575)\n- Fix rfp check when neck config is a list (#3591)\n- Fix import of fuse conv bn in benchmark.py (#3606)\n- Fix webcam demo (#3634)\n- Fix typo and itemize issues in tutorial (#3658)\n- Fix error in distributed training when some levels of FPN are not assigned with bounding boxes (#3670)\n- Fix the width and height orders of stride in valid flag generation (#3685)\n- Fix weight initialization bug in Res2Net DCN (#3714)\n- Fix bug in OHEMSampler (#3677)\n\n__New Features__\n\n- Support Cutout augmentation (#3521)\n- Support evaluation on multiple datasets through ConcatDataset (#3522)\n- Support [PAA assign](https://arxiv.org/abs/2007.08103) #(3547)\n- Support eval metric with pickle results (#3607)\n- Support [YOLOv3](https://arxiv.org/abs/1804.02767) (#3083)\n- Support [SABL](https://arxiv.org/abs/1912.04260) (#3603)\n- Support to publish to Pypi in github-action (#3510)\n- Support custom imports (#3641)\n\n__Improvements__\n\n- Refactor common issues in documentation (#3530)\n- Add pytorch 1.6 to CI config (#3532)\n- Add config to runner meta (#3534)\n- Add eval-option flag for testing (#3537)\n- Add init_eval to evaluation hook (#3550)\n- Add include_bkg in ClassBalancedDataset (#3577)\n- Using config's loading in inference_detector (#3611)\n- Add ATSS ResNet-101 models in model zoo (#3639)\n- Update urls to download.openmmlab.com (#3665)\n- Support non-mask training for CocoDataset (#3711)\n\n### v2.3.0 (5/8/2020)\n\n__Highlights__\n\n- The CUDA/C++ operators have been moved to `mmcv.ops`. For backward compatibility `mmdet.ops` is kept as warppers of `mmcv.ops`.\n- Support new methods [CornerNet](https://arxiv.org/abs/1808.01244), [DIOU](https://arxiv.org/abs/1911.08287)/[CIOU](https://arxiv.org/abs/2005.03572) loss, and new dataset: [LVIS V1](https://arxiv.org/abs/1908.03195)\n- Provide more detailed colab training tutorials and more complete documentation.\n- Support to convert RetinaNet from Pytorch to ONNX.\n\n__Bug Fixes__\n\n- Fix the model initialization bug of DetectoRS (#3187)\n- Fix the bug of module names in NASFCOSHead (#3205)\n- Fix the filename bug in publish_model.py (#3237)\n- Fix the dimensionality bug when `inside_flags.any()` is `False` in dense heads (#3242)\n- Fix the bug of forgetting to pass flip directions in `MultiScaleFlipAug` (#3262)\n- Fixed the bug caused by default value of `stem_channels` (#3333)\n- Fix the bug of model checkpoint loading for CPU inference (#3318, #3316)\n- Fix topk bug when box number is smaller than the expected topk number in ATSSAssigner (#3361)\n- Fix the gt priority bug in center_region_assigner.py (#3208)\n- Fix NaN issue of iou calculation in iou_loss.py (#3394)\n- Fix the bug that `iou_thrs` is not actually used during evaluation in coco.py (#3407)\n- Fix test-time augmentation of RepPoints (#3435)\n- Fix runtimeError caused by incontiguous tensor in Res2Net+DCN (#3412)\n\n__New Features__\n\n- Support [CornerNet](https://arxiv.org/abs/1808.01244) (#3036)\n- Support [DIOU](https://arxiv.org/abs/1911.08287)/[CIOU](https://arxiv.org/abs/2005.03572) loss (#3151)\n- Support [LVIS V1](https://arxiv.org/abs/1908.03195) dataset (#)\n- Support customized hooks in training (#3395)\n- Support fp16 training of generalized focal loss (#3410)\n- Support to convert RetinaNet from Pytorch to ONNX (#3075)\n\n__Improvements__\n\n- Support to process ignore boxes in ATSS assigner (#3082)\n- Allow to crop images without ground truth in `RandomCrop` (#3153)\n- Enable the the `Accuracy` module to set threshold (#3155)\n- Refactoring unit tests (#3206)\n- Unify the training settings of `to_float32` and `norm_cfg` in RegNets configs (#3210)\n- Add colab training tutorials for beginners (#3213, #3273)\n- Move CUDA/C++ operators into `mmcv.ops` and keep `mmdet.ops` as warppers for backward compatibility (#3232)(#3457)\n- Update installation scripts in documentation (#3290) and dockerfile (#3320)\n- Support to set image resize backend (#3392)\n- Remove git hash in version file (#3466)\n- Check mmcv version to force version compatibility (#3460)\n\n### v2.2.0 (1/7/2020)\n\n__Highlights__\n\n- Support new methods: [DetectoRS](https://arxiv.org/abs/2006.02334), [PointRend](https://arxiv.org/abs/1912.08193), [Generalized Focal Loss](https://arxiv.org/abs/2006.04388), [Dynamic R-CNN](https://arxiv.org/abs/2004.06002)\n\n__Bug Fixes__\n\n- Fix FreeAnchor when no gt in image (#3176)\n- Clean up deprecated usage of `register_module()` (#3092, #3161)\n- Fix pretrain bug in NAS FCOS (#3145)\n- Fix `num_classes` in SSD (#3142)\n- Fix FCOS warmup (#3119)\n- Fix `rstrip` in `tools/publish_model.py`\n- Fix `flip_ratio` default value in RandomFLip pipeline (#3106)\n- Fix cityscapes eval with ms_rcnn (#3112)\n- Fix RPN softmax (#3056)\n- Fix filename of LVIS@v0.5 (#2998)\n- Fix nan loss by filtering out-of-frame gt_bboxes in COCO (#2999)\n- Fix bug in FSAF (#3018)\n- Add FocalLoss `num_classes` check (#2964)\n- Fix PISA Loss when there are no gts (#2992)\n- Avoid nan in `iou_calculator` (#2975)\n- Prevent possible bugs in loading and transforms caused by shallow copy (#2967)\n\n__New Features__\n\n- Add DetectoRS (#3064)\n- Support Generalize Focal Loss (#3097)\n- Support PointRend (#2752)\n- Support Dynamic R-CNN (#3040)\n- Add DeepFashion dataset (#2968)\n- Implement FCOS training tricks (#2935)\n- Use BaseDenseHead as base class for anchor-base heads (#2963)\n- Add `with_cp` for BasicBlock (#2891)\n- Add `stem_channels` argument for ResNet (#2954)\n\n__Improvements__\n\n- Add anchor free base head (#2867)\n- Migrate to github action (#3137)\n- Add docstring for datasets, pipelines, core modules and methods (#3130, #3125, #3120)\n- Add VOC benchmark (#3060)\n- Add `concat` mode in GRoI (#3098)\n- Remove cmd arg `autorescale-lr` (#3080)\n- Use `len(data['img_metas'])` to indicate `num_samples` (#3073, #3053)\n- Switch to EpochBasedRunner (#2976)\n\n### v2.1.0 (8/6/2020)\n\n__Highlights__\n\n- Support new backbones: [RegNetX](https://arxiv.org/abs/2003.13678), [Res2Net](https://arxiv.org/abs/1904.01169)\n- Support new methods: [NASFCOS](https://arxiv.org/abs/1906.04423), [PISA](https://arxiv.org/abs/1904.04821), [GRoIE](https://arxiv.org/abs/2004.13665)\n- Support new dataset: [LVIS](https://arxiv.org/abs/1908.03195)\n\n__Bug Fixes__\n\n- Change the CLI argument `--validate` to `--no-validate` to enable validation after training epochs by default. (#2651)\n- Add missing cython to docker file (#2713)\n- Fix bug in nms cpu implementation (#2754)\n- Fix bug when showing mask results (#2763)\n- Fix gcc requirement (#2806)\n- Fix bug in async test (#2820)\n- Fix mask encoding-decoding bugs in test API (#2824)\n- Fix bug in test time augmentation (#2858, #2921, #2944)\n- Fix a typo in comment of apis/train (#2877)\n- Fix the bug of returning None when no gt bboxes are in the original image in `RandomCrop`. Fix the bug that misses to handle `gt_bboxes_ignore`, `gt_label_ignore`, and `gt_masks_ignore` in `RandomCrop`, `MinIoURandomCrop` and `Expand` modules. (#2810)\n- Fix bug of `base_channels` of regnet (#2917)\n- Fix the bug of logger when loading pre-trained weights in base detector (#2936)\n\n__New Features__\n\n- Add IoU models (#2666)\n- Add colab demo for inference\n- Support class agnostic nms (#2553)\n- Add benchmark gathering scripts for development only (#2676)\n- Add mmdet-based project links (#2736, #2767, #2895)\n- Add config dump in training (#2779)\n- Add ClassBalancedDataset (#2721)\n- Add res2net backbone (#2237)\n- Support RegNetX models (#2710)\n- Use `mmcv.FileClient` to support different storage backends (#2712)\n- Add ClassBalancedDataset (#2721)\n- Code Release: Prime Sample Attention in Object Detection (CVPR 2020) (#2626)\n- Implement NASFCOS (#2682)\n- Add class weight in CrossEntropyLoss (#2797)\n- Support LVIS dataset (#2088)\n- Support GRoIE (#2584)\n\n__Improvements__\n\n- Allow different x and y strides in anchor heads. (#2629)\n- Make FSAF loss more robust to no gt (#2680)\n- Compute pure inference time instead (#2657) and update inference speed (#2730)\n- Avoided the possibility that a patch with 0 area is cropped. (#2704)\n- Add warnings when deprecated `imgs_per_gpu` is used. (#2700)\n- Add a mask rcnn example for config (#2645)\n- Update model zoo (#2762, #2866, #2876, #2879, #2831)\n- Add `ori_filename` to img_metas and use it in test show-dir (#2612)\n- Use `img_fields` to handle multiple images during image transform (#2800)\n- Add upsample_cfg support in FPN (#2787)\n- Add `['img']` as default `img_fields` for back compatibility (#2809)\n- Rename the pretrained model from `open-mmlab://resnet50_caffe` and `open-mmlab://resnet50_caffe_bgr` to `open-mmlab://detectron/resnet50_caffe` and `open-mmlab://detectron2/resnet50_caffe`. (#2832)\n- Added sleep(2) in test.py to reduce hanging problem (#2847)\n- Support `c10::half` in CARAFE (#2890)\n- Improve documentations (#2918, #2714)\n- Use optimizer constructor in mmcv and clean the original implementation in `mmdet.core.optimizer` (#2947)\n\n### v2.0.0 (6/5/2020)\n\nIn this release, we made lots of major refactoring and modifications.\n\n1. __Faster speed__. We optimize the training and inference speed for common models, achieving up to 30% speedup for training and 25% for inference. Please refer to [model zoo](model_zoo.md#comparison-with-detectron2) for details.\n\n2. __Higher performance__. We change some default hyperparameters with no additional cost, which leads to a gain of performance for most models. Please refer to [compatibility](compatibility.md#training-hyperparameters) for details.\n\n3. __More documentation and tutorials__. We add a bunch of documentation and tutorials to help users get started more smoothly. Read it [here](https://mmdetection.readthedocs.io/en/latest/).\n\n4. __Support PyTorch 1.5__. The support for 1.1 and 1.2 is dropped, and we switch to some new APIs.\n\n5. __Better configuration system__. Inheritance is supported to reduce the redundancy of configs.\n\n6. __Better modular design__. Towards the goal of simplicity and flexibility, we simplify some encapsulation while add more other configurable modules like BBoxCoder, IoUCalculator, OptimizerConstructor, RoIHead. Target computation is also included in heads and the call hierarchy is simpler.\n\n7. Support new methods: [FSAF](https://arxiv.org/abs/1903.00621) and PAFPN (part of [PAFPN](https://arxiv.org/abs/1803.01534)).\n\n__Breaking Changes__\nModels training with MMDetection 1.x are not fully compatible with 2.0, please refer to the [compatibility doc](compatibility.md) for the details and how to migrate to the new version.\n\n__Improvements__\n\n- Unify cuda and cpp API for custom ops. (#2277)\n- New config files with inheritance. (#2216)\n- Encapsulate the second stage into RoI heads. (#1999)\n- Refactor GCNet/EmpericalAttention into plugins. (#2345)\n- Set low quality match as an option in IoU-based bbox assigners. (#2375)\n- Change the codebase's coordinate system. (#2380)\n- Refactor the category order in heads. 0 means the first positive class instead of background now. (#2374)\n- Add bbox sampler and assigner registry. (#2419)\n- Speed up the inference of RPN. (#2420)\n- Add `train_cfg` and `test_cfg` as class members in all anchor heads. (#2422)\n- Merge target computation methods into heads. (#2429)\n- Add bbox coder to support different bbox encoding and losses. (#2480)\n- Unify the API for regression loss. (#2156)\n- Refactor Anchor Generator. (#2474)\n- Make `lr` an optional argument for optimizers. (#2509)\n- Migrate to modules and methods in MMCV. (#2502, #2511, #2569, #2572)\n- Support PyTorch 1.5. (#2524)\n- Drop the support for Python 3.5 and use F-string in the codebase. (#2531)\n\n__Bug Fixes__\n\n- Fix the scale factors for resized images without keep the aspect ratio. (#2039)\n- Check if max_num > 0 before slicing in NMS. (#2486)\n- Fix Deformable RoIPool when there is no instance. (#2490)\n- Fix the default value of assigned labels. (#2536)\n- Fix the evaluation of Cityscapes. (#2578)\n\n__New Features__\n\n- Add deep_stem and avg_down option to ResNet, i.e., support ResNetV1d. (#2252)\n- Add L1 loss. (#2376)\n- Support both polygon and bitmap for instance masks. (#2353, #2540)\n- Support CPU mode for inference. (#2385)\n- Add optimizer constructor for complicated configuration of optimizers. (#2397, #2488)\n- Implement PAFPN. (#2392)\n- Support empty tensor input for some modules. (#2280)\n- Support for custom dataset classes without overriding it. (#2408, #2443)\n- Support to train subsets of coco dataset. (#2340)\n- Add iou_calculator to potentially support more IoU calculation methods. (2405)\n- Support class wise mean AP (was removed in the last version). (#2459)\n- Add option to save the testing result images. (#2414)\n- Support MomentumUpdaterHook. (#2571)\n- Add a demo to inference a single image. (#2605)\n\n### v1.1.0 (24/2/2020)\n\n__Highlights__\n\n- Dataset evaluation is rewritten with a unified api, which is used by both evaluation hooks and test scripts.\n- Support new methods: [CARAFE](https://arxiv.org/abs/1905.02188).\n\n__Breaking Changes__\n\n- The new MMDDP inherits from the official DDP, thus the `__init__` api is changed to be the same as official DDP.\n- The `mask_head` field in HTC config files is modified.\n- The evaluation and testing script is updated.\n- In all transforms, instance masks are stored as a numpy array shaped (n, h, w) instead of a list of (h, w) arrays, where n is the number of instances.\n\n__Bug Fixes__\n\n- Fix IOU assigners when ignore_iof_thr > 0 and there is no pred boxes. (#2135)\n- Fix mAP evaluation when there are no ignored boxes. (#2116)\n- Fix the empty RoI input for Deformable RoI Pooling. (#2099)\n- Fix the dataset settings for multiple workflows. (#2103)\n- Fix the warning related to `torch.uint8` in PyTorch 1.4. (#2105)\n- Fix the inference demo on devices other than gpu:0. (#2098)\n- Fix Dockerfile. (#2097)\n- Fix the bug that `pad_val` is unused in Pad transform. (#2093)\n- Fix the albumentation transform when there is no ground truth bbox. (#2032)\n\n__Improvements__\n\n- Use torch instead of numpy for random sampling. (#2094)\n- Migrate to the new MMDDP implementation in MMCV v0.3. (#2090)\n- Add meta information in logs. (#2086)\n- Rewrite Soft NMS with pytorch extension and remove cython as a dependency. (#2056)\n- Rewrite dataset evaluation. (#2042, #2087, #2114, #2128)\n- Use numpy array for masks in transforms. (#2030)\n\n__New Features__\n\n- Implement \"CARAFE: Content-Aware ReAssembly of FEatures\". (#1583)\n- Add `worker_init_fn()` in data_loader when seed is set. (#2066, #2111)\n- Add logging utils. (#2035)\n\n### v1.0.0 (30/1/2020)\n\nThis release mainly improves the code quality and add more docstrings.\n\n__Highlights__\n\n- Documentation is online now: <https://mmdetection.readthedocs.io>.\n- Support new models: [ATSS](https://arxiv.org/abs/1912.02424).\n- DCN is now available with the api `build_conv_layer` and `ConvModule` like the normal conv layer.\n- A tool to collect environment information is available for trouble shooting.\n\n__Bug Fixes__\n\n- Fix the incompatibility of the latest numpy and pycocotools. (#2024)\n- Fix the case when distributed package is unavailable, e.g., on Windows. (#1985)\n- Fix the dimension issue for `refine_bboxes()`. (#1962)\n- Fix the typo when `seg_prefix` is a list. (#1906)\n- Add segmentation map cropping to RandomCrop. (#1880)\n- Fix the return value of `ga_shape_target_single()`. (#1853)\n- Fix the loaded shape of empty proposals. (#1819)\n- Fix the mask data type when using albumentation. (#1818)\n\n__Improvements__\n\n- Enhance AssignResult and SamplingResult. (#1995)\n- Add ability to overwrite existing module in Registry. (#1982)\n- Reorganize requirements and make albumentations and imagecorruptions optional. (#1969)\n- Check NaN in `SSDHead`. (#1935)\n- Encapsulate the DCN in ResNe(X)t into a ConvModule & Conv_layers. (#1894)\n- Refactoring for mAP evaluation and support multiprocessing and logging. (#1889)\n- Init the root logger before constructing Runner to log more information. (#1865)\n- Split `SegResizeFlipPadRescale` into different existing transforms. (#1852)\n- Move `init_dist()` to MMCV. (#1851)\n- Documentation and docstring improvements. (#1971, #1938, #1869, #1838)\n- Fix the color of the same class for mask visualization. (#1834)\n- Remove the option `keep_all_stages` in HTC and Cascade R-CNN. (#1806)\n\n__New Features__\n\n- Add two test-time options `crop_mask` and `rle_mask_encode` for mask heads. (#2013)\n- Support loading grayscale images as single channel. (#1975)\n- Implement \"Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection\". (#1872)\n- Add sphinx generated docs. (#1859, #1864)\n- Add GN support for flops computation. (#1850)\n- Collect env info for trouble shooting. (#1812)\n\n### v1.0rc1 (13/12/2019)\n\nThe RC1 release mainly focuses on improving the user experience, and fixing bugs.\n\n__Highlights__\n\n- Support new models: [FoveaBox](https://arxiv.org/abs/1904.03797), [RepPoints](https://arxiv.org/abs/1904.11490) and [FreeAnchor](https://arxiv.org/abs/1909.02466).\n- Add a Dockerfile.\n- Add a jupyter notebook demo and a webcam demo.\n- Setup the code style and CI.\n- Add lots of docstrings and unit tests.\n- Fix lots of bugs.\n\n__Breaking Changes__\n\n- There was a bug for computing COCO-style mAP w.r.t different scales (AP_s, AP_m, AP_l), introduced by #621. (#1679)\n\n__Bug Fixes__\n\n- Fix a sampling interval bug in Libra R-CNN. (#1800)\n- Fix the learning rate in SSD300 WIDER FACE. (#1781)\n- Fix the scaling issue when `keep_ratio=False`. (#1730)\n- Fix typos. (#1721, #1492, #1242, #1108, #1107)\n- Fix the shuffle argument in `build_dataloader`. (#1693)\n- Clip the proposal when computing mask targets. (#1688)\n- Fix the \"index out of range\" bug for samplers in some corner cases. (#1610, #1404)\n- Fix the NMS issue on devices other than GPU:0. (#1603)\n- Fix SSD Head and GHM Loss on CPU. (#1578)\n- Fix the OOM error when there are too many gt bboxes. (#1575)\n- Fix the wrong keyword argument `nms_cfg` in HTC. (#1573)\n- Process masks and semantic segmentation in Expand and MinIoUCrop transforms. (#1550, #1361)\n- Fix a scale bug in the Non Local op. (#1528)\n- Fix a bug in transforms when `gt_bboxes_ignore` is None. (#1498)\n- Fix a bug when `img_prefix` is None. (#1497)\n- Pass the device argument to `grid_anchors` and `valid_flags`. (#1478)\n- Fix the data pipeline for test_robustness. (#1476)\n- Fix the argument type of deformable pooling. (#1390)\n- Fix the coco_eval when there are only two classes. (#1376)\n- Fix a bug in Modulated DeformableConv when deformable_group>1. (#1359)\n- Fix the mask cropping in RandomCrop. (#1333)\n- Fix zero outputs in DeformConv when not running on cuda:0. (#1326)\n- Fix the type issue in Expand. (#1288)\n- Fix the inference API. (#1255)\n- Fix the inplace operation in Expand. (#1249)\n- Fix the from-scratch training config. (#1196)\n- Fix inplace add in RoIExtractor which cause an error in PyTorch 1.2. (#1160)\n- Fix FCOS when input images has no positive sample. (#1136)\n- Fix recursive imports. (#1099)\n\n__Improvements__\n\n- Print the config file and mmdet version in the log. (#1721)\n- Lint the code before compiling in travis CI. (#1715)\n- Add a probability argument for the `Expand` transform. (#1651)\n- Update the PyTorch and CUDA version in the docker file. (#1615)\n- Raise a warning when specifying `--validate` in non-distributed training. (#1624, #1651)\n- Beautify the mAP printing. (#1614)\n- Add pre-commit hook. (#1536)\n- Add the argument `in_channels` to backbones. (#1475)\n- Add lots of docstrings and unit tests, thanks to [@Erotemic](https://github.com/Erotemic). (#1603, #1517, #1506, #1505, #1491, #1479, #1477, #1475, #1474)\n- Add support for multi-node distributed test when there is no shared storage. (#1399)\n- Optimize Dockerfile to reduce the image size. (#1306)\n- Update new results of HRNet. (#1284, #1182)\n- Add an argument `no_norm_on_lateral` in FPN. (#1240)\n- Test the compiling in CI. (#1235)\n- Move docs to a separate folder. (#1233)\n- Add a jupyter notebook demo. (#1158)\n- Support different type of dataset for training. (#1133)\n- Use int64_t instead of long in cuda kernels. (#1131)\n- Support unsquare RoIs for bbox and mask heads. (#1128)\n- Manually add type promotion to make compatible to PyTorch 1.2. (#1114)\n- Allowing validation dataset for computing validation loss. (#1093)\n- Use `.scalar_type()` instead of `.type()` to suppress some warnings. (#1070)\n\n__New Features__\n\n- Add an option `--with_ap` to compute the AP for each class. (#1549)\n- Implement \"FreeAnchor: Learning to Match Anchors for Visual Object Detection\". (#1391)\n- Support [Albumentations](https://github.com/albumentations-team/albumentations) for augmentations in the data pipeline. (#1354)\n- Implement \"FoveaBox: Beyond Anchor-based Object Detector\". (#1339)\n- Support horizontal and vertical flipping. (#1273, #1115)\n- Implement \"RepPoints: Point Set Representation for Object Detection\". (#1265)\n- Add test-time augmentation to HTC and Cascade R-CNN. (#1251)\n- Add a COCO result analysis tool. (#1228)\n- Add Dockerfile. (#1168)\n- Add a webcam demo. (#1155, #1150)\n- Add FLOPs counter. (#1127)\n- Allow arbitrary layer order for ConvModule. (#1078)\n\n### v1.0rc0 (27/07/2019)\n\n- Implement lots of new methods and components (Mixed Precision Training, HTC, Libra R-CNN, Guided Anchoring, Empirical Attention, Mask Scoring R-CNN, Grid R-CNN (Plus), GHM, GCNet, FCOS, HRNet, Weight Standardization, etc.). Thank all collaborators!\n- Support two additional datasets: WIDER FACE and Cityscapes.\n- Refactoring for loss APIs and make it more flexible to adopt different losses and related hyper-parameters.\n- Speed up multi-gpu testing.\n- Integrate all compiling and installing in a single script.\n\n### v0.6.0 (14/04/2019)\n\n- Up to 30% speedup compared to the model zoo.\n- Support both PyTorch stable and nightly version.\n- Replace NMS and SigmoidFocalLoss with Pytorch CUDA extensions.\n\n### v0.6rc0(06/02/2019)\n\n- Migrate to PyTorch 1.0.\n\n### v0.5.7 (06/02/2019)\n\n- Add support for Deformable ConvNet v2. (Many thanks to the authors and [@chengdazhi](https://github.com/chengdazhi))\n- This is the last release based on PyTorch 0.4.1.\n\n### v0.5.6 (17/01/2019)\n\n- Add support for Group Normalization.\n- Unify RPNHead and single stage heads (RetinaHead, SSDHead) with AnchorHead.\n\n### v0.5.5 (22/12/2018)\n\n- Add SSD for COCO and PASCAL VOC.\n- Add ResNeXt backbones and detection models.\n- Refactoring for Samplers/Assigners and add OHEM.\n- Add VOC dataset and evaluation scripts.\n\n### v0.5.4 (27/11/2018)\n\n- Add SingleStageDetector and RetinaNet.\n\n### v0.5.3 (26/11/2018)\n\n- Add Cascade R-CNN and Cascade Mask R-CNN.\n- Add support for Soft-NMS in config files.\n\n### v0.5.2 (21/10/2018)\n\n- Add support for custom datasets.\n- Add a script to convert PASCAL VOC annotations to the expected format.\n\n### v0.5.1 (20/10/2018)\n\n- Add BBoxAssigner and BBoxSampler, the `train_cfg` field in config files are restructured.\n- `ConvFCRoIHead` / `SharedFCRoIHead` are renamed to `ConvFCBBoxHead` / `SharedFCBBoxHead` for consistency.\n"
  },
  {
    "path": "docs/en/notes/compatibility.md",
    "content": "# Compatibility of MMDetection 2.x\n\n## MMDetection 2.25.0\n\nIn order to support Mask2Former for instance segmentation, the original config files of Mask2Former for panpotic segmentation need to be renamed [PR #7571](https://github.com/open-mmlab/mmdetection/pull/7571).\n\n<table align=\"center\">\n    <thead>\n        <tr align='center'>\n            <td>before v2.25.0</td>\n            <td>after v2.25.0</td>\n        </tr>\n    </thead>\n    <tbody><tr valign='top'>\n    <th>\n\n```\n'mask2former_xxx_coco.py' represents config files for **panoptic segmentation**.\n```\n\n</th>\n    <th>\n\n```\n'mask2former_xxx_coco.py' represents config files for **instance segmentation**.\n'mask2former_xxx_coco-panoptic.py' represents config files for **panoptic segmentation**.\n```\n\n</th></tr>\n  </tbody></table>\n\n## MMDetection 2.21.0\n\nIn order to support CPU training, the logic of scatter in batch collating has been changed. We recommend to use\nMMCV v1.4.4 or higher. For more details, please refer to [MMCV PR #1621](https://github.com/open-mmlab/mmcv/pull/1621).\n\n## MMDetection 2.18.1\n\n### MMCV compatibility\n\nIn order to fix the wrong weight reference bug in BaseTransformerLayer, the logic in batch first mode of MultiheadAttention has been changed.\nWe recommend to use MMCV v1.3.17 or higher. For more details, please refer to [MMCV PR #1418](https://github.com/open-mmlab/mmcv/pull/1418).\n\n## MMDetection 2.18.0\n\n### DIIHead compatibility\n\nIn order to support QueryInst, attn_feats is added into the returned tuple of DIIHead.\n\n## MMDetection 2.14.0\n\n### MMCV Version\n\nIn order to fix the problem that the priority of EvalHook is too low, all hook priorities have been re-adjusted in 1.3.8, so MMDetection 2.14.0 needs to rely on the latest MMCV 1.3.8 version. For related information, please refer to [#1120](https://github.com/open-mmlab/mmcv/pull/1120), for related issues, please refer to [#5343](https://github.com/open-mmlab/mmdetection/issues/5343).\n\n### SSD compatibility\n\nIn v2.14.0, to make SSD more flexible to use, [PR5291](https://github.com/open-mmlab/mmdetection/pull/5291) refactored its backbone, neck and head. The users can use the script `tools/model_converters/upgrade_ssd_version.py` to convert their models.\n\n```bash\npython tools/model_converters/upgrade_ssd_version.py ${OLD_MODEL_PATH} ${NEW_MODEL_PATH}\n```\n\n- OLD_MODEL_PATH: the path to load the old version SSD model.\n- NEW_MODEL_PATH: the path to save the converted model weights.\n\n## MMDetection 2.12.0\n\nMMDetection is going through big refactoring for more general and convenient usages during the releases from v2.12.0 to v2.18.0 (maybe longer).\nIn v2.12.0 MMDetection inevitably brings some BC-breakings, including the MMCV dependency, model initialization, model registry, and mask AP evaluation.\n\n### MMCV Version\n\nMMDetection v2.12.0 relies on the newest features in MMCV 1.3.3, including `BaseModule` for unified parameter initialization, model registry, and the CUDA operator `MultiScaleDeformableAttn` for [Deformable DETR](https://arxiv.org/abs/2010.04159). Note that MMCV 1.3.2 already contains all the features used by MMDet but has known issues. Therefore, we recommend users to skip MMCV v1.3.2 and use v1.3.2, though v1.3.2 might work for most of the cases.\n\n### Unified model initialization\n\nTo unify the parameter initialization in OpenMMLab projects, MMCV supports `BaseModule` that accepts `init_cfg` to allow the modules' parameters initialized in a flexible and unified manner. Now the users need to explicitly call `model.init_weights()` in the training script to initialize the model (as in [here](https://github.com/open-mmlab/mmdetection/blob/main/tools/train.py#L162), previously this was handled by the detector. **The downstream projects must update their model initialization accordingly to use MMDetection v2.12.0**. Please refer to PR #4750 for details.\n\n### Unified model registry\n\nTo easily use backbones implemented in other OpenMMLab projects, MMDetection v2.12.0 inherits the model registry created in MMCV (#760). In this way, as long as the backbone is supported in an OpenMMLab project and that project also uses the registry in MMCV, users can use that backbone in MMDetection by simply modifying the config without copying the code of that backbone into MMDetection. Please refer to PR #5059 for more details.\n\n### Mask AP evaluation\n\nBefore [PR 4898](https://github.com/open-mmlab/mmdetection/pull/4898) and V2.12.0, the mask AP of small, medium, and large instances is calculated based on the bounding box area rather than the real mask area. This leads to higher `APs` and `APm` but lower `APl` but will not affect the overall mask AP. [PR 4898](https://github.com/open-mmlab/mmdetection/pull/4898) change it to use mask areas by deleting `bbox` in mask AP calculation.\nThe new calculation does not affect the overall mask AP evaluation and is consistent with [Detectron2](https://github.com/facebookresearch/detectron2/).\n\n## Compatibility with MMDetection 1.x\n\nMMDetection 2.0 goes through a big refactoring and addresses many legacy issues. It is not compatible with the 1.x version, i.e., running inference with the same model weights in these two versions will produce different results. Thus, MMDetection 2.0 re-benchmarks all the models and provides their links and logs in the model zoo.\n\nThe major differences are in four folds: coordinate system, codebase conventions, training hyperparameters, and modular design.\n\n### Coordinate System\n\nThe new coordinate system is consistent with [Detectron2](https://github.com/facebookresearch/detectron2/) and treats the center of the most left-top pixel as (0, 0) rather than the left-top corner of that pixel.\nAccordingly, the system interprets the coordinates in COCO bounding box and segmentation annotations as coordinates in range `[0, width]` or `[0, height]`.\nThis modification affects all the computation related to the bbox and pixel selection,\nwhich is more natural and accurate.\n\n- The height and width of a box with corners (x1, y1) and (x2, y2) in the new coordinate system is computed as `width = x2 - x1` and `height = y2 - y1`.\n  In MMDetection 1.x and previous version, a \"+ 1\" was added both height and width.\n  This modification are in three folds:\n\n  1. Box transformation and encoding/decoding in regression.\n  2. IoU calculation. This affects the matching process between ground truth and bounding box and the NMS process. The effect to compatibility is very negligible, though.\n  3. The corners of bounding box is in float type and no longer quantized. This should provide more accurate bounding box results. This also makes the bounding box and RoIs not required to have minimum size of 1, whose effect is small, though.\n\n- The anchors are center-aligned to feature grid points and in float type.\n  In MMDetection 1.x and previous version, the anchors are in `int` type and not center-aligned.\n  This affects the anchor generation in RPN and all the anchor-based methods.\n\n- ROIAlign is better aligned with the image coordinate system. The new implementation is adopted from [Detectron2](https://github.com/facebookresearch/detectron2/tree/master/detectron2/layers/csrc/ROIAlign).\n  The RoIs are shifted by half a pixel by default when they are used to cropping RoI features, compared to MMDetection 1.x.\n  The old behavior is still available by setting `aligned=False` instead of `aligned=True`.\n\n- Mask cropping and pasting are more accurate.\n\n  1. We use the new RoIAlign to crop mask targets. In MMDetection 1.x, the bounding box is quantized before it is used to crop mask target, and the crop process is implemented by numpy. In new implementation, the bounding box for crop is not quantized and sent to RoIAlign. This implementation accelerates the training speed by a large margin (~0.1s per iter, ~2 hour when training Mask R50 for 1x schedule) and should be more accurate.\n\n  2. In MMDetection 2.0, the \"`paste_mask()`\" function is different and should be more accurate than those in previous versions. This change follows the modification in [Detectron2](https://github.com/facebookresearch/detectron2/blob/master/detectron2/structures/masks.py) and can improve mask AP on COCO by ~0.5% absolute.\n\n### Codebase Conventions\n\n- MMDetection 2.0 changes the order of class labels to reduce unused parameters in regression and mask branch more naturally (without +1 and -1).\n  This effect all the classification layers of the model to have a different ordering of class labels. The final layers of regression branch and mask head no longer keep K+1 channels for K categories, and their class orders are consistent with the classification branch.\n\n  - In MMDetection 2.0, label \"K\" means background, and labels \\[0, K-1\\] correspond to the K = num_categories object categories.\n\n  - In MMDetection 1.x and previous version, label \"0\" means background, and labels \\[1, K\\] correspond to the K categories.\n\n  - **Note**: The class order of softmax RPN is still the same as that in 1.x in versions\\<=2.4.0 while sigmoid RPN is not affected. The class orders in all heads are unified since MMDetection v2.5.0.\n\n- Low quality matching in R-CNN is not used. In MMDetection 1.x and previous versions, the `max_iou_assigner` will match low quality boxes for each ground truth box in both RPN and R-CNN training. We observe this sometimes does not assign the most perfect GT box to some bounding boxes,\n  thus MMDetection 2.0 do not allow low quality matching by default in R-CNN training in the new system. This sometimes may slightly improve the box AP (~0.1% absolute).\n\n- Separate scale factors for width and height. In MMDetection 1.x and previous versions, the scale factor is a single float in mode `keep_ratio=True`. This is slightly inaccurate because the scale factors for width and height have slight difference. MMDetection 2.0 adopts separate scale factors for width and height, the improvement on AP ~0.1% absolute.\n\n- Configs name conventions are changed. MMDetection V2.0 adopts the new name convention to maintain the gradually growing model zoo as the following:\n\n  ```shell\n  [model]_(model setting)_[backbone]_[neck]_(norm setting)_(misc)_(gpu x batch)_[schedule]_[dataset].py,\n  ```\n\n  where the (`misc`) includes DCN and GCBlock, etc. More details are illustrated in the [documentation for config](tutorials/config)\n\n- MMDetection V2.0 uses new ResNet Caffe backbones to reduce warnings when loading pre-trained models. Most of the new backbones' weights are the same as the former ones but do not have `conv.bias`, except that they use a different `img_norm_cfg`. Thus, the new backbone will not cause warning of unexpected keys.\n\n### Training Hyperparameters\n\nThe change in training hyperparameters does not affect\nmodel-level compatibility but slightly improves the performance. The major ones are:\n\n- The number of proposals after nms is changed from 2000 to 1000 by setting `nms_post=1000` and `max_num=1000`.\n  This slightly improves both mask AP and bbox AP by ~0.2% absolute.\n\n- The default box regression losses for Mask R-CNN, Faster R-CNN and RetinaNet are changed from smooth L1 Loss to L1 loss. This leads to an overall improvement in box AP (~0.6% absolute). However, using L1-loss for other methods such as Cascade R-CNN and HTC does not improve the performance, so we keep the original settings for these methods.\n\n- The sample num of RoIAlign layer is set to be 0 for simplicity. This leads to slightly improvement on mask AP (~0.2% absolute).\n\n- The default setting does not use gradient clipping anymore during training for faster training speed. This does not degrade performance of the most of models. For some models such as RepPoints we keep using gradient clipping to stabilize the training process and to obtain better performance.\n\n- The default warmup ratio is changed from 1/3 to 0.001 for a more smooth warming up process since the gradient clipping is usually not used. The effect is found negligible during our re-benchmarking, though.\n\n### Upgrade Models from 1.x to 2.0\n\nTo convert the models trained by MMDetection V1.x to MMDetection V2.0, the users can use the script `tools/model_converters/upgrade_model_version.py` to convert\ntheir models. The converted models can be run in MMDetection V2.0 with slightly dropped performance (less than 1% AP absolute).\nDetails can be found in `configs/legacy`.\n\n## pycocotools compatibility\n\n`mmpycocotools` is the OpenMMlab's fork of official `pycocotools`, which works for both MMDetection and Detectron2.\nBefore [PR 4939](https://github.com/open-mmlab/mmdetection/pull/4939), since `pycocotools` and `mmpycocotool` have the same package name, if users already installed `pycocotools` (installed Detectron2 first under the same environment), then the setup of MMDetection will skip installing `mmpycocotool`. Thus MMDetection fails due to the missing `mmpycocotools`.\nIf MMDetection is installed before Detectron2, they could work under the same environment.\n[PR 4939](https://github.com/open-mmlab/mmdetection/pull/4939) deprecates mmpycocotools in favor of official pycocotools.\nUsers may install MMDetection and Detectron2 under the same environment after [PR 4939](https://github.com/open-mmlab/mmdetection/pull/4939), no matter what the installation order is.\n"
  },
  {
    "path": "docs/en/notes/contribution_guide.md",
    "content": "# Contribution\n"
  },
  {
    "path": "docs/en/notes/faq.md",
    "content": "# Frequently Asked Questions\n\nWe list some common troubles faced by many users and their corresponding solutions here. Feel free to enrich the list if you find any frequent issues and have ways to help others to solve them. If the contents here do not cover your issue, please create an issue using the [provided templates](https://github.com/open-mmlab/mmdetection/blob/main/.github/ISSUE_TEMPLATE/error-report.md/) and make sure you fill in all required information in the template.\n\n## PyTorch 2.0 Support\n\nThe vast majority of algorithms in MMDetection now support PyTorch 2.0 and its `torch.compile` function. Users only need to install MMDetection 3.0.0rc7 or later versions to enjoy this feature. If any unsupported algorithms are found during use, please feel free to give us feedback. We also welcome contributions from the community to benchmark the speed improvement brought by using the `torch.compile` function.\n\nTo enable the `torch.compile` function, simply add `--cfg-options compile=True` after `train.py` or `test.py`. For example, to enable `torch.compile` for RTMDet, you can use the following command:\n\n```shell\n# Single GPU\npython tools/train.py configs/rtmdet/rtmdet_s_8xb32-300e_coco.py  --cfg-options compile=True\n\n# Single node multiple GPUs\n./tools/dist_train.sh configs/rtmdet/rtmdet_s_8xb32-300e_coco.py 8 --cfg-options compile=True\n\n# Single node multiple GPUs + AMP\n./tools/dist_train.sh configs/rtmdet/rtmdet_s_8xb32-300e_coco.py 8 --cfg-options compile=True --amp\n```\n\nIt is important to note that PyTorch 2.0's support for dynamic shapes is not yet fully developed. In most object detection algorithms, not only are the input shapes dynamic, but the loss calculation and post-processing parts are also dynamic. This can lead to slower training speeds when using the `torch.compile` function. Therefore, if you wish to enable the `torch.compile` function, you should follow these principles:\n\n1. Input images to the network are fixed shape, not multi-scale\n2. set `torch._dynamo.config.cache_size_limit` parameter. TorchDynamo will convert and cache the Python bytecode, and the compiled functions will be stored in the cache. When the next check finds that the function needs to be recompiled, the function will be recompiled and cached. However, if the number of recompilations exceeds the maximum value set (64), the function will no longer be cached or recompiled. As mentioned above, the loss calculation and post-processing parts of the object detection algorithm are also dynamically calculated, and these functions need to be recompiled every time. Therefore, setting the `torch._dynamo.config.cache_size_limit` parameter to a smaller value can effectively reduce the compilation time\n\nIn MMDetection, you can set the `torch._dynamo.config.cache_size_limit` parameter through the environment variable `DYNAMO_CACHE_SIZE_LIMIT`. For example, the command is as follows:\n\n```shell\n# Single GPU\nexport DYNAMO_CACHE_SIZE_LIMIT = 4\npython tools/train.py configs/rtmdet/rtmdet_s_8xb32-300e_coco.py  --cfg-options compile=True\n\n# Single node multiple GPUs\nexport DYNAMO_CACHE_SIZE_LIMIT = 4\n./tools/dist_train.sh configs/rtmdet/rtmdet_s_8xb32-300e_coco.py 8 --cfg-options compile=True\n```\n\nAbout the common questions about PyTorch 2.0's dynamo, you can refer to [here](https://pytorch.org/docs/stable/dynamo/faq.html)\n\n## Installation\n\nCompatibility issue between MMCV and MMDetection; \"ConvWS is already registered in conv layer\"; \"AssertionError: MMCV==xxx is used but incompatible. Please install mmcv>=xxx, \\<=xxx.\"\n\nCompatible MMDetection, MMEngine, and MMCV versions are shown as below. Please choose the correct version of MMCV to avoid installation issues.\n\n| MMDetection version |      MMCV version       |     MMEngine version     |\n| :-----------------: | :---------------------: | :----------------------: |\n|        main         |  mmcv>=2.0.0, \\<2.2.0   | mmengine>=0.7.1, \\<1.0.0 |\n|        3.3.0        |  mmcv>=2.0.0, \\<2.2.0   | mmengine>=0.7.1, \\<1.0.0 |\n|        3.2.0        |  mmcv>=2.0.0, \\<2.2.0   | mmengine>=0.7.1, \\<1.0.0 |\n|        3.1.0        |  mmcv>=2.0.0, \\<2.1.0   | mmengine>=0.7.1, \\<1.0.0 |\n|        3.0.0        |  mmcv>=2.0.0, \\<2.1.0   | mmengine>=0.7.1, \\<1.0.0 |\n|      3.0.0rc6       | mmcv>=2.0.0rc4, \\<2.1.0 | mmengine>=0.6.0, \\<1.0.0 |\n|      3.0.0rc5       | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.3.0, \\<1.0.0 |\n|      3.0.0rc4       | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.3.0, \\<1.0.0 |\n|      3.0.0rc3       | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.3.0, \\<1.0.0 |\n|      3.0.0rc2       | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.1.0, \\<1.0.0 |\n|      3.0.0rc1       | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.1.0, \\<1.0.0 |\n|      3.0.0rc0       | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.1.0, \\<1.0.0 |\n\n**Note:**\n\n1. If you want to install mmdet-v2.x, the compatible MMDetection and MMCV versions table can be found at [here](https://mmdetection.readthedocs.io/en/stable/faq.html#installation). Please choose the correct version of MMCV to avoid installation issues.\n2. In MMCV-v2.x, `mmcv-full` is rename to `mmcv`, if you want to install `mmcv` without CUDA ops, you can install `mmcv-lite`.\n\n- \"No module named 'mmcv.ops'\"; \"No module named 'mmcv.\\_ext'\".\n\n  1. Uninstall existing `mmcv-lite` in the environment using `pip uninstall mmcv-lite`.\n  2. Install `mmcv` following the [installation instruction](https://mmcv.readthedocs.io/en/2.x/get_started/installation.html).\n\n- \"Microsoft Visual C++ 14.0 or graeter is required\" during installation on Windows.\n\n  This error happens when building the 'pycocotools.\\_mask' extension of pycocotools and the environment lacks corresponding C++ compilation dependencies. You need to download it at Microsoft officials [visual-cpp-build-tools](https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/),  select the \"Use C ++ Desktop Development\" option to install the minimum dependencies, and then reinstall pycocotools.\n\n- Using Albumentations\n\n  If you would like to use `albumentations`, we suggest using `pip install -r requirements/albu.txt` or\n  `pip install -U albumentations --no-binary qudida,albumentations`.\n  If you simply use `pip install albumentations>=0.3.2`, it will install `opencv-python-headless` simultaneously (even though you have already installed `opencv-python`).\n  Please refer to the [official documentation](https://albumentations.ai/docs/getting_started/installation/#note-on-opencv-dependencies) for details.\n\n- ModuleNotFoundError is raised when using some algorithms\n\n  Some extra dependencies are required for Instaboost, Panoptic Segmentation, LVIS dataset, etc. Please note the error message and install corresponding packages, e.g.,\n\n  ```shell\n  # for instaboost\n  pip install instaboostfast\n  # for panoptic segmentation\n  pip install git+https://github.com/cocodataset/panopticapi.git\n  # for LVIS dataset\n  pip install git+https://github.com/lvis-dataset/lvis-api.git\n  ```\n\n## Coding\n\n- Do I need to reinstall mmdet after some code modifications\n\n  If you follow the best practice and install mmdet with `pip install -e .`, any local modifications made to the code will take effect without reinstallation.\n\n- How to develop with multiple MMDetection versions\n\n  You can have multiple folders like mmdet-3.0, mmdet-3.1.\n  When you run the train or test script, it will adopt the mmdet package in the current folder.\n\n  To use the default MMDetection installed in the environment rather than the one you are working with, you can remove the following line in those scripts:\n\n  ```shell\n  PYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH\n  ```\n\n## PyTorch/CUDA Environment\n\n- \"RTX 30 series card fails when building MMCV or MMDet\"\n\n  1. Temporary work-around: do `MMCV_WITH_OPS=1 MMCV_CUDA_ARGS='-gencode=arch=compute_80,code=sm_80' pip install -e .`.\n     The common issue is `nvcc fatal : Unsupported gpu architecture 'compute_86'`. This means that the compiler should optimize for sm_86, i.e., nvidia 30 series card, but such optimizations have not been supported by CUDA toolkit 11.0.\n     This work-around modifies the compile flag by adding `MMCV_CUDA_ARGS='-gencode=arch=compute_80,code=sm_80'`, which tells `nvcc` to optimize for **sm_80**, i.e., Nvidia A100. Although A100 is different from the 30 series card, they use similar ampere architecture. This may hurt the performance but it works.\n  2. PyTorch developers have updated that the default compiler flags should be fixed by [pytorch/pytorch#47585](https://github.com/pytorch/pytorch/pull/47585). So using PyTorch-nightly may also be able to solve the problem, though we have not tested it yet.\n\n- \"invalid device function\" or \"no kernel image is available for execution\".\n\n  1. Check if your cuda runtime version (under `/usr/local/`), `nvcc --version` and `conda list cudatoolkit` version match.\n  2. Run `python mmdet/utils/collect_env.py` to check whether PyTorch, torchvision, and MMCV are built for the correct GPU architecture.\n     You may need to set `TORCH_CUDA_ARCH_LIST` to reinstall MMCV.\n     The GPU arch table could be found [here](https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#gpu-feature-list),\n     i.e. run `TORCH_CUDA_ARCH_LIST=7.0 pip install mmcv` to build MMCV for Volta GPUs.\n     The compatibility issue could happen when using old GPUS, e.g., Tesla K80 (3.7) on colab.\n  3. Check whether the running environment is the same as that when mmcv/mmdet has compiled.\n     For example, you may compile mmcv using CUDA 10.0 but run it on CUDA 9.0 environments.\n\n- \"undefined symbol\" or \"cannot open xxx.so\".\n\n  1. If those symbols are CUDA/C++ symbols (e.g., libcudart.so or GLIBCXX), check whether the CUDA/GCC runtimes are the same as those used for compiling mmcv,\n     i.e. run `python mmdet/utils/collect_env.py` to see if `\"MMCV Compiler\"`/`\"MMCV CUDA Compiler\"` is the same as `\"GCC\"`/`\"CUDA_HOME\"`.\n  2. If those symbols are PyTorch symbols (e.g., symbols containing caffe, aten, and TH), check whether the PyTorch version is the same as that used for compiling mmcv.\n  3. Run `python mmdet/utils/collect_env.py` to check whether PyTorch, torchvision, and MMCV are built by and running on the same environment.\n\n- setuptools.sandbox.UnpickleableException: DistutilsSetupError(\"each element of 'ext_modules' option must be an Extension instance or 2-tuple\")\n\n  1. If you are using miniconda rather than anaconda, check whether Cython is installed as indicated in [#3379](https://github.com/open-mmlab/mmdetection/issues/3379).\n     You need to manually install Cython first and then run command `pip install -r requirements.txt`.\n  2. You may also need to check the compatibility between the `setuptools`, `Cython`, and `PyTorch` in your environment.\n\n- \"Segmentation fault\".\n\n  1. Check you GCC version and use GCC 5.4. This usually caused by the incompatibility between PyTorch and the environment (e.g., GCC \\< 4.9 for PyTorch). We also recommend the users to avoid using GCC 5.5 because many feedbacks report that GCC 5.5 will cause \"segmentation fault\" and simply changing it to GCC 5.4 could solve the problem.\n\n  2. Check whether PyTorch is correctly installed and could use CUDA op, e.g. type the following command in your terminal.\n\n     ```shell\n     python -c 'import torch; print(torch.cuda.is_available())'\n     ```\n\n     And see whether they could correctly output results.\n\n  3. If Pytorch is correctly installed, check whether MMCV is correctly installed.\n\n     ```shell\n     python -c 'import mmcv; import mmcv.ops'\n     ```\n\n     If MMCV is correctly installed, then there will be no issue of the above two commands.\n\n  4. If MMCV and Pytorch is correctly installed, you man use `ipdb`, `pdb` to set breakpoints or directly add 'print' in mmdetection code and see which part leads the segmentation fault.\n\n## Training\n\n- \"Loss goes Nan\"\n\n  1. Check if the dataset annotations are valid: zero-size bounding boxes will cause the regression loss to be Nan due to the commonly used transformation for box regression. Some small size (width or height are smaller than 1) boxes will also cause this problem after data augmentation (e.g., instaboost). So check the data and try to filter out those zero-size boxes and skip some risky augmentations on the small-size boxes when you face the problem.\n  2. Reduce the learning rate: the learning rate might be too large due to some reasons, e.g., change of batch size. You can rescale them to the value that could stably train the model.\n  3. Extend the warmup iterations: some models are sensitive to the learning rate at the start of the training. You can extend the warmup iterations, e.g., change the `warmup_iters` from 500 to 1000 or 2000.\n  4. Add gradient clipping: some models requires gradient clipping to stabilize the training process. The default of `grad_clip` is `None`, you can add gradient clippint to avoid gradients that are too large, i.e., set `optim_wrapper=dict(clip_grad=dict(max_norm=35, norm_type=2))` in your config file.\n\n- \"GPU out of memory\"\n\n  1. There are some scenarios when there are large amount of ground truth boxes, which may cause OOM during target assignment. You can set `gpu_assign_thr=N` in the config of assigner thus the assigner will calculate box overlaps through CPU when there are more than N GT boxes.\n\n  2. Set `with_cp=True` in the backbone. This uses the sublinear strategy in PyTorch to reduce GPU memory cost in the backbone.\n\n  3. Try mixed precision training using following the examples in `config/fp16`. The `loss_scale` might need further tuning for different models.\n\n  4. Try to use `AvoidCUDAOOM` to avoid GPU out of memory. It will first retry after calling `torch.cuda.empty_cache()`. If it still fails, it will then retry by converting the type of inputs to FP16 format. If it still fails, it will try to copy inputs from GPUs to CPUs to continue computing. Try AvoidOOM in you code to make the code continue to run when GPU memory runs out:\n\n     ```python\n     from mmdet.utils import AvoidCUDAOOM\n\n     output = AvoidCUDAOOM.retry_if_cuda_oom(some_function)(input1, input2)\n     ```\n\n     You can also try `AvoidCUDAOOM` as a decorator to make the code continue to run when GPU memory runs out:\n\n     ```python\n     from mmdet.utils import AvoidCUDAOOM\n\n     @AvoidCUDAOOM.retry_if_cuda_oom\n     def function(*args, **kwargs):\n         ...\n         return xxx\n     ```\n\n- \"RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one\"\n\n  1. This error indicates that your module has parameters that were not used in producing loss. This phenomenon may be caused by running different branches in your code in DDP mode.\n  2. You can set `find_unused_parameters = True` in the config to solve the above problems, but this will slow down the training speed.\n  3. You can set `detect_anomalous_params = True` in the config or `model_wrapper_cfg = dict(type='MMDistributedDataParallel', detect_anomalous_params=True)` (More details please refer to [MMEngine](https://github.com/open-mmlab/mmengine/blob/main/mmengine/model/wrappers/distributed.py#L91)) to get the name of those unused parameters. Note `detect_anomalous_params = True` will slow down the training speed, so it is recommended for debugging only.\n\n- Save the best model\n\n  It can be turned on by configuring `default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=1, save_best='auto'),`. In the case of the `auto` parameter, the first key in the returned evaluation result will be used as the basis for selecting the best model. You can also directly set the key in the evaluation result to manually set it, for example, `save_best='coco/bbox_mAP'`.\n\n## Evaluation\n\n- COCO Dataset, AP or AR = -1\n  1. According to the definition of COCO dataset, the small and medium areas in an image are less than 1024 (32\\*32), 9216 (96\\*96), respectively.\n  2. If the corresponding area has no object, the result of AP and AR will set to -1.\n\n## Model\n\n- `style` in ResNet\n\n  The `style` parameter in ResNet allows either `pytorch` or `caffe` style. It indicates the difference in the Bottleneck module. Bottleneck is a stacking structure of `1x1-3x3-1x1` convolutional layers. In the case of `caffe` mode, the convolution layer with `stride=2` is the first `1x1` convolution, while in `pyorch` mode, it is the second `3x3` convolution has `stride=2`. A sample code is as below:\n\n  ```python\n  if self.style == 'pytorch':\n        self.conv1_stride = 1\n        self.conv2_stride = stride\n  else:\n        self.conv1_stride = stride\n        self.conv2_stride = 1\n  ```\n\n- ResNeXt parameter description\n\n  ResNeXt comes from the paper [`Aggregated Residual Transformations for Deep Neural Networks`](https://arxiv.org/abs/1611.05431). It introduces  group and uses “cardinality” to control the number of groups to achieve a balance between accuracy and complexity. It controls the basic width and grouping parameters of the internal Bottleneck module through two hyperparameters `baseWidth` and `cardinality`. An example configuration name in MMDetection is `mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco.py`, where `mask_rcnn` represents the algorithm using Mask R-CNN, `x101` represents the backbone network using ResNeXt-101, and `64x4d` represents that the bottleneck block has 64 group and each group has basic width of 4.\n\n- `norm_eval` in backbone\n\n  Since the detection model is usually large and the input image resolution is high, this will result in a small batch of the detection model, which will make the variance of the statistics calculated by BatchNorm during the training process very large and not as stable as the statistics obtained during the pre-training of the backbone network . Therefore, the `norm_eval=True` mode is generally used in training, and the BatchNorm statistics in the pre-trained backbone network are directly used. The few algorithms that use large batches are the `norm_eval=False` mode, such as NASFPN. For the backbone network without ImageNet pre-training and the batch is relatively small, you can consider using `SyncBN`.\n"
  },
  {
    "path": "docs/en/notes/projects.md",
    "content": "# Projects based on MMDetection\n\nThere are many projects built upon MMDetection.\nWe list some of them as examples of how to extend MMDetection for your own projects.\nAs the page might not be completed, please feel free to create a PR to update this page.\n\n## Projects as an extension\n\nSome projects extend the boundary of MMDetection for deployment or other research fields.\nThey reveal the potential of what MMDetection can do. We list several of them as below.\n\n- [OTEDetection](https://github.com/opencv/mmdetection): OpenVINO training extensions for object detection.\n- [MMDetection3d](https://github.com/open-mmlab/mmdetection3d): OpenMMLab's next-generation platform for general 3D object detection.\n\n## Projects of papers\n\nThere are also projects released with papers.\nSome of the papers are published in top-tier conferences (CVPR, ICCV, and ECCV), the others are also highly influential.\nTo make this list also a reference for the community to develop and compare new object detection algorithms, we list them following the time order of top-tier conferences.\nMethods already supported and maintained by MMDetection are not listed.\n\n- Involution: Inverting the Inherence of Convolution for Visual Recognition, CVPR21. [\\[paper\\]](https://arxiv.org/abs/2103.06255)[\\[github\\]](https://github.com/d-li14/involution)\n- Multiple Instance Active Learning for Object Detection, CVPR 2021. [\\[paper\\]](https://openaccess.thecvf.com/content/CVPR2021/papers/Yuan_Multiple_Instance_Active_Learning_for_Object_Detection_CVPR_2021_paper.pdf)[\\[github\\]](https://github.com/yuantn/MI-AOD)\n- Adaptive Class Suppression Loss for Long-Tail Object Detection, CVPR 2021. [\\[paper\\]](https://arxiv.org/abs/2104.00885)[\\[github\\]](https://github.com/CASIA-IVA-Lab/ACSL)\n- Generalizable Pedestrian Detection: The Elephant In The Room, CVPR2021. [\\[paper\\]](https://arxiv.org/abs/2003.08799)[\\[github\\]](https://github.com/hasanirtiza/Pedestron)\n- Group Fisher Pruning for Practical Network Compression, ICML2021. [\\[paper\\]](https://github.com/jshilong/FisherPruning/blob/main/resources/paper.pdf)[\\[github\\]](https://github.com/jshilong/FisherPruning)\n- Overcoming Classifier Imbalance for Long-tail Object Detection with Balanced Group Softmax, CVPR2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/papers/Li_Overcoming_Classifier_Imbalance_for_Long-Tail_Object_Detection_With_Balanced_Group_CVPR_2020_paper.pdf)[\\[github\\]](https://github.com/FishYuLi/BalancedGroupSoftmax)\n- Coherent Reconstruction of Multiple Humans from a Single Image, CVPR2020. [\\[paper\\]](https://jiangwenpl.github.io/multiperson/)[\\[github\\]](https://github.com/JiangWenPL/multiperson)\n- Look-into-Object: Self-supervised Structure Modeling for Object Recognition, CVPR 2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/papers/Zhou_Look-Into-Object_Self-Supervised_Structure_Modeling_for_Object_Recognition_CVPR_2020_paper.pdf)[\\[github\\]](https://github.com/JDAI-CV/LIO)\n- Video Panoptic Segmentation, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2006.11339)[\\[github\\]](https://github.com/mcahny/vps)\n- D2Det: Towards High Quality Object Detection and Instance Segmentation, CVPR2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/html/Cao_D2Det_Towards_High_Quality_Object_Detection_and_Instance_Segmentation_CVPR_2020_paper.html)[\\[github\\]](https://github.com/JialeCao001/D2Det)\n- CentripetalNet: Pursuing High-quality Keypoint Pairs for Object Detection, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2003.09119)[\\[github\\]](https://github.com/KiveeDong/CentripetalNet)\n- Learning a Unified Sample Weighting Network for Object Detection, CVPR 2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/html/Cai_Learning_a_Unified_Sample_Weighting_Network_for_Object_Detection_CVPR_2020_paper.html)[\\[github\\]](https://github.com/caiqi/sample-weighting-network)\n- Scale-equalizing Pyramid Convolution for Object Detection, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2005.03101) [\\[github\\]](https://github.com/jshilong/SEPC)\n- Revisiting the Sibling Head in Object Detector, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2003.07540)[\\[github\\]](https://github.com/Sense-X/TSD)\n- PolarMask: Single Shot Instance Segmentation with Polar Representation, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/1909.13226)[\\[github\\]](https://github.com/xieenze/PolarMask)\n- Hit-Detector: Hierarchical Trinity Architecture Search for Object Detection, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2003.11818)[\\[github\\]](https://github.com/ggjy/HitDet.pytorch)\n- ZeroQ: A Novel Zero Shot Quantization Framework, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2001.00281)[\\[github\\]](https://github.com/amirgholami/ZeroQ)\n- CBNet: A Novel Composite Backbone Network Architecture for Object Detection, AAAI2020. [\\[paper\\]](https://aaai.org/Papers/AAAI/2020GB/AAAI-LiuY.1833.pdf)[\\[github\\]](https://github.com/VDIGPKU/CBNet)\n- RDSNet: A New Deep Architecture for Reciprocal Object Detection and Instance Segmentation, AAAI2020. [\\[paper\\]](https://arxiv.org/abs/1912.05070)[\\[github\\]](https://github.com/wangsr126/RDSNet)\n- Training-Time-Friendly Network for Real-Time Object Detection, AAAI2020. [\\[paper\\]](https://arxiv.org/abs/1909.00700)[\\[github\\]](https://github.com/ZJULearning/ttfnet)\n- Cascade RPN: Delving into High-Quality Region Proposal Network with Adaptive Convolution, NeurIPS 2019. [\\[paper\\]](https://arxiv.org/abs/1909.06720)[\\[github\\]](https://github.com/thangvubk/Cascade-RPN)\n- Reasoning R-CNN: Unifying Adaptive Global Reasoning into Large-scale Object Detection, CVPR2019. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2019/papers/Xu_Reasoning-RCNN_Unifying_Adaptive_Global_Reasoning_Into_Large-Scale_Object_Detection_CVPR_2019_paper.pdf)[\\[github\\]](https://github.com/chanyn/Reasoning-RCNN)\n- Learning RoI Transformer for Oriented Object Detection in Aerial Images, CVPR2019. [\\[paper\\]](https://arxiv.org/abs/1812.00155)[\\[github\\]](https://github.com/dingjiansw101/AerialDetection)\n- SOLO: Segmenting Objects by Locations. [\\[paper\\]](https://arxiv.org/abs/1912.04488)[\\[github\\]](https://github.com/WXinlong/SOLO)\n- SOLOv2: Dynamic, Faster and Stronger. [\\[paper\\]](https://arxiv.org/abs/2003.10152)[\\[github\\]](https://github.com/WXinlong/SOLO)\n- Dense Peppoints: Representing Visual Objects with Dense Point Sets. [\\[paper\\]](https://arxiv.org/abs/1912.11473)[\\[github\\]](https://github.com/justimyhxu/Dense-RepPoints)\n- IterDet: Iterative Scheme for Object Detection in Crowded Environments. [\\[paper\\]](https://arxiv.org/abs/2005.05708)[\\[github\\]](https://github.com/saic-vul/iterdet)\n- Cross-Iteration Batch Normalization. [\\[paper\\]](https://arxiv.org/abs/2002.05712)[\\[github\\]](https://github.com/Howal/Cross-iterationBatchNorm)\n- A Ranking-based, Balanced Loss Function Unifying Classification and Localisation in Object Detection, NeurIPS2020 [\\[paper\\]](https://arxiv.org/abs/2009.13592)[\\[github\\]](https://github.com/kemaloksuz/aLRPLoss)\n- RelationNet++: Bridging Visual Representations for Object Detection via Transformer Decoder, NeurIPS2020 [\\[paper\\]](https://arxiv.org/abs/2010.15831)[\\[github\\]](https://github.com/microsoft/RelationNet2)\n- Generalized Focal Loss V2: Learning Reliable Localization Quality Estimation for Dense Object Detection, CVPR2021[\\[paper\\]](https://arxiv.org/abs/2011.12885)[\\[github\\]](https://github.com/implus/GFocalV2)\n- Swin Transformer: Hierarchical Vision Transformer using Shifted Windows, ICCV2021[\\[paper\\]](https://arxiv.org/abs/2103.14030)[\\[github\\]](https://github.com/SwinTransformer/)\n- Focal Transformer: Focal Self-attention for Local-Global Interactions in Vision Transformers, NeurIPS2021[\\[paper\\]](https://arxiv.org/abs/2107.00641)[\\[github\\]](https://github.com/microsoft/Focal-Transformer)\n- End-to-End Semi-Supervised Object Detection with Soft Teacher, ICCV2021[\\[paper\\]](https://arxiv.org/abs/2106.09018)[\\[github\\]](https://github.com/microsoft/SoftTeacher)\n- CBNetV2: A Novel Composite Backbone Network Architecture for Object Detection [\\[paper\\]](http://arxiv.org/abs/2107.00420)[\\[github\\]](https://github.com/VDIGPKU/CBNetV2)\n- Instances as Queries, ICCV2021 [\\[paper\\]](https://openaccess.thecvf.com/content/ICCV2021/papers/Fang_Instances_As_Queries_ICCV_2021_paper.pdf)[\\[github\\]](https://github.com/hustvl/QueryInst)\n"
  },
  {
    "path": "docs/en/overview.md",
    "content": "# OVERVIEW\n\nThis chapter introduces you to the framework of MMDetection, and provides links to detailed tutorials about MMDetection.\n\n## What is MMDetection\n\n![image](https://user-images.githubusercontent.com/12907710/137271636-56ba1cd2-b110-4812-8221-b4c120320aa9.png)\n\nMMDetection is an object detection toolbox that contains a rich set of object detection, instance segmentation, and panoptic segmentation methods as well as related components and modules, and below is its whole framework:\n\nMMDetection consists of 7 main parts, apis, structures, datasets, models, engine, evaluation and visualization.\n\n- **apis** provides high-level APIs for model inference.\n- **structures** provides data structures like bbox, mask, and DetDataSample.\n- **datasets** supports various dataset for object detection, instance segmentation, and panoptic segmentation.\n  - **transforms** contains a lot of useful data augmentation transforms.\n  - **samplers** defines different data loader sampling strategy.\n- **models** is the most vital part for detectors and contains different components of a detector.\n  - **detectors** defines all of the detection model classes.\n  - **data_preprocessors** is for preprocessing the input data of the model.\n  - **backbones** contains various backbone networks.\n  - **necks** contains various neck components.\n  - **dense_heads** contains various detection heads that perform dense predictions.\n  - **roi_heads** contains various detection heads that predict from RoIs.\n  - **seg_heads** contains various segmentation heads.\n  - **losses** contains various loss functions.\n  - **task_modules** provides modules for detection tasks. E.g. assigners, samplers, box coders, and prior generators.\n  - **layers** provides some basic neural network layers.\n- **engine** is a part for runtime components.\n  - **runner** provides extensions for [MMEngine's runner](https://mmengine.readthedocs.io/en/latest/tutorials/runner.html).\n  - **schedulers** provides schedulers for adjusting optimization hyperparameters.\n  - **optimizers** provides optimizers and optimizer wrappers.\n  - **hooks** provides various hooks of the runner.\n- **evaluation** provides different metrics for evaluating model performance.\n- **visualization** is for visualizing detection results.\n\n## How to Use this Guide\n\nHere is a detailed step-by-step guide to learn more about MMDetection:\n\n1. For installation instructions, please see [get_started](get_started.md).\n\n2. Refer to the below tutorials for the basic usage of MMDetection.\n\n   - [Train and Test](https://mmdetection.readthedocs.io/en/latest/user_guides/index.html#train-test)\n\n   - [Useful Tools](https://mmdetection.readthedocs.io/en/latest/user_guides/index.html#useful-tools)\n\n3. Refer to the below tutorials to dive deeper:\n\n   - [Basic Concepts](https://mmdetection.readthedocs.io/en/latest/advanced_guides/index.html#basic-concepts)\n   - [Component Customization](https://mmdetection.readthedocs.io/en/latest/advanced_guides/index.html#component-customization)\n\n4. For users of MMDetection 2.x version, we provide a guide to help you adapt to the new version. You can find it in the [migration guide](./migration/migration.md).\n"
  },
  {
    "path": "docs/en/stat.py",
    "content": "#!/usr/bin/env python\nimport functools as func\nimport glob\nimport os.path as osp\nimport re\n\nimport numpy as np\n\nurl_prefix = 'https://github.com/open-mmlab/mmdetection/blob/main/configs'\n\nfiles = sorted(glob.glob('../../configs/*/README.md'))\n\nstats = []\ntitles = []\nnum_ckpts = 0\n\nfor f in files:\n    url = osp.dirname(f.replace('../../configs', url_prefix))\n\n    with open(f, 'r') as content_file:\n        content = content_file.read()\n\n    title = content.split('\\n')[0].replace('# ', '').strip()\n    ckpts = set(x.lower().strip()\n                for x in re.findall(r'\\[model\\]\\((https?.*)\\)', content))\n\n    if len(ckpts) == 0:\n        continue\n\n    _papertype = [x for x in re.findall(r'\\[([A-Z]+)\\]', content)]\n    assert len(_papertype) > 0\n    papertype = _papertype[0]\n\n    paper = set([(papertype, title)])\n\n    titles.append(title)\n    num_ckpts += len(ckpts)\n\n    statsmsg = f\"\"\"\n\\t* [{papertype}] [{title}]({url}) ({len(ckpts)} ckpts)\n\"\"\"\n    stats.append((paper, ckpts, statsmsg))\n\nallpapers = func.reduce(lambda a, b: a.union(b), [p for p, _, _ in stats])\nmsglist = '\\n'.join(x for _, _, x in stats)\n\npapertypes, papercounts = np.unique([t for t, _ in allpapers],\n                                    return_counts=True)\ncountstr = '\\n'.join(\n    [f'   - {t}: {c}' for t, c in zip(papertypes, papercounts)])\n\nmodelzoo = f\"\"\"\n# Model Zoo Statistics\n\n* Number of papers: {len(set(titles))}\n{countstr}\n\n* Number of checkpoints: {num_ckpts}\n\n{msglist}\n\"\"\"\n\nwith open('modelzoo_statistics.md', 'w') as f:\n    f.write(modelzoo)\n"
  },
  {
    "path": "docs/en/switch_language.md",
    "content": "## <a href='https://mmdetection.readthedocs.io/en/latest/'>English</a>\n\n## <a href='https://mmdetection.readthedocs.io/zh_CN/latest/'>简体中文</a>\n"
  },
  {
    "path": "docs/en/user_guides/config.md",
    "content": "# Learn about Configs\n\nMMDetection and other OpenMMLab repositories use [MMEngine's config system](https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html). It has a modular and inheritance design, which is convenient to conduct various experiments.\n\n## Config file content\n\nMMDetection uses a modular design, all modules with different functions can be configured through the config. Taking Mask R-CNN as an example, we will introduce each field in the config according to different function modules:\n\n### Model config\n\nIn MMDetection's config, we use `model` to set up detection algorithm components. In addition to neural network components such as `backbone`, `neck`, etc, it also requires `data_preprocessor`, `train_cfg`, and `test_cfg`. `data_preprocessor` is responsible for processing a batch of data output by dataloader. `train_cfg`, and `test_cfg` in the model config are for training and testing hyperparameters of the components.\n\n```python\nmodel = dict(\n    type='MaskRCNN',  # The name of detector\n    data_preprocessor=dict(  # The config of data preprocessor, usually includes image normalization and padding\n        type='DetDataPreprocessor',  # The type of the data preprocessor, refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.data_preprocessors.DetDataPreprocessor\n        mean=[123.675, 116.28, 103.53],  # Mean values used to pre-training the pre-trained backbone models, ordered in R, G, B\n        std=[58.395, 57.12, 57.375],  # Standard variance used to pre-training the pre-trained backbone models, ordered in R, G, B\n        bgr_to_rgb=True,  # whether to convert image from BGR to RGB\n        pad_mask=True,  # whether to pad instance masks\n        pad_size_divisor=32),  # The size of padded image should be divisible by ``pad_size_divisor``\n    backbone=dict(  # The config of backbone\n        type='ResNet',  # The type of backbone network. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.backbones.ResNet\n        depth=50,  # The depth of backbone, usually it is 50 or 101 for ResNet and ResNext backbones.\n        num_stages=4,  # Number of stages of the backbone.\n        out_indices=(0, 1, 2, 3),  # The index of output feature maps produced in each stage\n        frozen_stages=1,  # The weights in the first stage are frozen\n        norm_cfg=dict(  # The config of normalization layers.\n            type='BN',  # Type of norm layer, usually it is BN or GN\n            requires_grad=True),  # Whether to train the gamma and beta in BN\n        norm_eval=True,  # Whether to freeze the statistics in BN\n        style='pytorch', # The style of backbone, 'pytorch' means that stride 2 layers are in 3x3 Conv, 'caffe' means stride 2 layers are in 1x1 Convs.\n    \tinit_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),  # The ImageNet pretrained backbone to be loaded\n    neck=dict(\n        type='FPN',  # The neck of detector is FPN. We also support 'NASFPN', 'PAFPN', etc. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.necks.FPN for more details.\n        in_channels=[256, 512, 1024, 2048],  # The input channels, this is consistent with the output channels of backbone\n        out_channels=256,  # The output channels of each level of the pyramid feature map\n        num_outs=5),  # The number of output scales\n    rpn_head=dict(\n        type='RPNHead',  # The type of RPN head is 'RPNHead', we also support 'GARPNHead', etc. Refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.dense_heads.RPNHead for more details.\n        in_channels=256,  # The input channels of each input feature map, this is consistent with the output channels of neck\n        feat_channels=256,  # Feature channels of convolutional layers in the head.\n        anchor_generator=dict(  # The config of anchor generator\n            type='AnchorGenerator',  # Most of methods use AnchorGenerator, SSD Detectors uses `SSDAnchorGenerator`. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/prior_generators/anchor_generator.py#L18 for more details\n            scales=[8],  # Basic scale of the anchor, the area of the anchor in one position of a feature map will be scale * base_sizes\n            ratios=[0.5, 1.0, 2.0],  # The ratio between height and width.\n            strides=[4, 8, 16, 32, 64]),  # The strides of the anchor generator. This is consistent with the FPN feature strides. The strides will be taken as base_sizes if base_sizes is not set.\n        bbox_coder=dict(  # Config of box coder to encode and decode the boxes during training and testing\n            type='DeltaXYWHBBoxCoder',  # Type of box coder. 'DeltaXYWHBBoxCoder' is applied for most of the methods. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/coders/delta_xywh_bbox_coder.py#L13 for more details.\n            target_means=[0.0, 0.0, 0.0, 0.0],  # The target means used to encode and decode boxes\n            target_stds=[1.0, 1.0, 1.0, 1.0]),  # The standard variance used to encode and decode boxes\n        loss_cls=dict(  # Config of loss function for the classification branch\n            type='CrossEntropyLoss',  # Type of loss for classification branch, we also support FocalLoss etc. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/losses/cross_entropy_loss.py#L201 for more details\n            use_sigmoid=True,  # RPN usually performs two-class classification, so it usually uses the sigmoid function.\n            loss_weight=1.0),  # Loss weight of the classification branch.\n        loss_bbox=dict(  # Config of loss function for the regression branch.\n            type='L1Loss',  # Type of loss, we also support many IoU Losses and smooth L1-loss, etc. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/losses/smooth_l1_loss.py#L56 for implementation.\n            loss_weight=1.0)),  # Loss weight of the regression branch.\n    roi_head=dict(  # RoIHead encapsulates the second stage of two-stage/cascade detectors.\n        type='StandardRoIHead',\n        bbox_roi_extractor=dict(  # RoI feature extractor for bbox regression.\n            type='SingleRoIExtractor',  # Type of the RoI feature extractor, most of methods uses SingleRoIExtractor. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/roi_heads/roi_extractors/single_level_roi_extractor.py#L13 for details.\n            roi_layer=dict(  # Config of RoI Layer\n                type='RoIAlign',  # Type of RoI Layer, DeformRoIPoolingPack and ModulatedDeformRoIPoolingPack are also supported. Refer to https://mmcv.readthedocs.io/en/latest/api.html#mmcv.ops.RoIAlign for details.\n                output_size=7,  # The output size of feature maps.\n                sampling_ratio=0),  # Sampling ratio when extracting the RoI features. 0 means adaptive ratio.\n            out_channels=256,  # output channels of the extracted feature.\n            featmap_strides=[4, 8, 16, 32]),  # Strides of multi-scale feature maps. It should be consistent with the architecture of the backbone.\n        bbox_head=dict(  # Config of box head in the RoIHead.\n            type='Shared2FCBBoxHead',  # Type of the bbox head, Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/roi_heads/bbox_heads/convfc_bbox_head.py#L220 for implementation details.\n            in_channels=256,  # Input channels for bbox head. This is consistent with the out_channels in roi_extractor\n            fc_out_channels=1024,  # Output feature channels of FC layers.\n            roi_feat_size=7,  # Size of RoI features\n            num_classes=80,  # Number of classes for classification\n            bbox_coder=dict(  # Box coder used in the second stage.\n                type='DeltaXYWHBBoxCoder',  # Type of box coder. 'DeltaXYWHBBoxCoder' is applied for most of the methods.\n                target_means=[0.0, 0.0, 0.0, 0.0],  # Means used to encode and decode box\n                target_stds=[0.1, 0.1, 0.2, 0.2]),  # Standard variance for encoding and decoding. It is smaller since the boxes are more accurate. [0.1, 0.1, 0.2, 0.2] is a conventional setting.\n            reg_class_agnostic=False,  # Whether the regression is class agnostic.\n            loss_cls=dict(  # Config of loss function for the classification branch\n                type='CrossEntropyLoss',  # Type of loss for classification branch, we also support FocalLoss etc.\n                use_sigmoid=False,  # Whether to use sigmoid.\n                loss_weight=1.0),  # Loss weight of the classification branch.\n            loss_bbox=dict(  # Config of loss function for the regression branch.\n                type='L1Loss',  # Type of loss, we also support many IoU Losses and smooth L1-loss, etc.\n                loss_weight=1.0)),  # Loss weight of the regression branch.\n        mask_roi_extractor=dict(  # RoI feature extractor for mask generation.\n            type='SingleRoIExtractor',  # Type of the RoI feature extractor, most of methods uses SingleRoIExtractor.\n            roi_layer=dict(  # Config of RoI Layer that extracts features for instance segmentation\n                type='RoIAlign',  # Type of RoI Layer, DeformRoIPoolingPack and ModulatedDeformRoIPoolingPack are also supported\n                output_size=14,  # The output size of feature maps.\n                sampling_ratio=0),  # Sampling ratio when extracting the RoI features.\n            out_channels=256,  # Output channels of the extracted feature.\n            featmap_strides=[4, 8, 16, 32]),  # Strides of multi-scale feature maps.\n        mask_head=dict(  # Mask prediction head\n            type='FCNMaskHead',  # Type of mask head, refer to https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.roi_heads.FCNMaskHead for implementation details.\n            num_convs=4,  # Number of convolutional layers in mask head.\n            in_channels=256,  # Input channels, should be consistent with the output channels of mask roi extractor.\n            conv_out_channels=256,  # Output channels of the convolutional layer.\n            num_classes=80,  # Number of class to be segmented.\n            loss_mask=dict(  # Config of loss function for the mask branch.\n                type='CrossEntropyLoss',  # Type of loss used for segmentation\n                use_mask=True,  # Whether to only train the mask in the correct class.\n                loss_weight=1.0))),  # Loss weight of mask branch.\n    train_cfg = dict(  # Config of training hyperparameters for rpn and rcnn\n        rpn=dict(  # Training config of rpn\n            assigner=dict(  # Config of assigner\n                type='MaxIoUAssigner',  # Type of assigner, MaxIoUAssigner is used for many common detectors. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/assigners/max_iou_assigner.py#L14 for more details.\n                pos_iou_thr=0.7,  # IoU >= threshold 0.7 will be taken as positive samples\n                neg_iou_thr=0.3,  # IoU < threshold 0.3 will be taken as negative samples\n                min_pos_iou=0.3,  # The minimal IoU threshold to take boxes as positive samples\n                match_low_quality=True,  # Whether to match the boxes under low quality (see API doc for more details).\n                ignore_iof_thr=-1),  # IoF threshold for ignoring bboxes\n            sampler=dict(  # Config of positive/negative sampler\n                type='RandomSampler',  # Type of sampler, PseudoSampler and other samplers are also supported. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/samplers/random_sampler.py#L14 for implementation details.\n                num=256,  # Number of samples\n                pos_fraction=0.5,  # The ratio of positive samples in the total samples.\n                neg_pos_ub=-1,  # The upper bound of negative samples based on the number of positive samples.\n                add_gt_as_proposals=False),  # Whether add GT as proposals after sampling.\n            allowed_border=-1,  # The border allowed after padding for valid anchors.\n            pos_weight=-1,  # The weight of positive samples during training.\n            debug=False),  # Whether to set the debug mode\n        rpn_proposal=dict(  # The config to generate proposals during training\n            nms_across_levels=False,  # Whether to do NMS for boxes across levels. Only work in `GARPNHead`, naive rpn does not support do nms cross levels.\n            nms_pre=2000,  # The number of boxes before NMS\n            nms_post=1000,  # The number of boxes to be kept by NMS. Only work in `GARPNHead`.\n            max_per_img=1000,  # The number of boxes to be kept after NMS.\n            nms=dict( # Config of NMS\n                type='nms',  # Type of NMS\n                iou_threshold=0.7 # NMS threshold\n                ),\n            min_bbox_size=0),  # The allowed minimal box size\n        rcnn=dict(  # The config for the roi heads.\n            assigner=dict(  # Config of assigner for second stage, this is different for that in rpn\n                type='MaxIoUAssigner',  # Type of assigner, MaxIoUAssigner is used for all roi_heads for now. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/assigners/max_iou_assigner.py#L14 for more details.\n                pos_iou_thr=0.5,  # IoU >= threshold 0.5 will be taken as positive samples\n                neg_iou_thr=0.5,  # IoU < threshold 0.5 will be taken as negative samples\n                min_pos_iou=0.5,  # The minimal IoU threshold to take boxes as positive samples\n                match_low_quality=False,  # Whether to match the boxes under low quality (see API doc for more details).\n                ignore_iof_thr=-1),  # IoF threshold for ignoring bboxes\n            sampler=dict(\n                type='RandomSampler',  # Type of sampler, PseudoSampler and other samplers are also supported. Refer to https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/samplers/random_sampler.py#L14 for implementation details.\n                num=512,  # Number of samples\n                pos_fraction=0.25,  # The ratio of positive samples in the total samples.\n                neg_pos_ub=-1,  # The upper bound of negative samples based on the number of positive samples.\n                add_gt_as_proposals=True\n            ),  # Whether add GT as proposals after sampling.\n            mask_size=28,  # Size of mask\n            pos_weight=-1,  # The weight of positive samples during training.\n            debug=False)),  # Whether to set the debug mode\n    test_cfg = dict(  # Config for testing hyperparameters for rpn and rcnn\n        rpn=dict(  # The config to generate proposals during testing\n            nms_across_levels=False,  # Whether to do NMS for boxes across levels. Only work in `GARPNHead`, naive rpn does not support do nms cross levels.\n            nms_pre=1000,  # The number of boxes before NMS\n            nms_post=1000,  # The number of boxes to be kept by NMS. Only work in `GARPNHead`.\n            max_per_img=1000,  # The number of boxes to be kept after NMS.\n            nms=dict( # Config of NMS\n                type='nms',  #Type of NMS\n                iou_threshold=0.7 # NMS threshold\n                ),\n            min_bbox_size=0),  # The allowed minimal box size\n        rcnn=dict(  # The config for the roi heads.\n            score_thr=0.05,  # Threshold to filter out boxes\n            nms=dict(  # Config of NMS in the second stage\n                type='nms',  # Type of NMS\n                iou_thr=0.5),  # NMS threshold\n            max_per_img=100,  # Max number of detections of each image\n            mask_thr_binary=0.5)))  # Threshold of mask prediction\n```\n\n### Dataset and evaluator config\n\n[Dataloaders](https://mmengine.readthedocs.io/en/latest/tutorials/dataset.html) are required for the training, validation, and testing of the [runner](https://mmengine.readthedocs.io/en/latest/tutorials/runner.html). Dataset and data pipeline need to be set to build the dataloader. Due to the complexity of this part, we use intermediate variables to simplify the writing of dataloader configs.\n\n```python\ndataset_type = 'CocoDataset'  # Dataset type, this will be used to define the dataset\ndata_root = 'data/coco/'  # Root path of data\nbackend_args = None # Arguments to instantiate the corresponding file backend\n\ntrain_pipeline = [  # Training data processing pipeline\n    dict(type='LoadImageFromFile', backend_args=backend_args),  # First pipeline to load images from file path\n    dict(\n        type='LoadAnnotations',  # Second pipeline to load annotations for current image\n        with_bbox=True,  # Whether to use bounding box, True for detection\n        with_mask=True,  # Whether to use instance mask, True for instance segmentation\n        poly2mask=True),  # Whether to convert the polygon mask to instance mask, set False for acceleration and to save memory\n    dict(\n        type='Resize',  # Pipeline that resizes the images and their annotations\n        scale=(1333, 800),  # The largest scale of the images\n        keep_ratio=True  # Whether to keep the ratio between height and width\n        ),\n    dict(\n        type='RandomFlip',  # Augmentation pipeline that flips the images and their annotations\n        prob=0.5),  # The probability to flip\n    dict(type='PackDetInputs')  # Pipeline that formats the annotation data and decides which keys in the data should be packed into data_samples\n]\ntest_pipeline = [  # Testing data processing pipeline\n    dict(type='LoadImageFromFile', backend_args=backend_args),  # First pipeline to load images from file path\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),  # Pipeline that resizes the images\n    dict(\n        type='PackDetInputs',  # Pipeline that formats the annotation data and decides which keys in the data should be packed into data_samples\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(   # Train dataloader config\n    batch_size=2,  # Batch size of a single GPU\n    num_workers=2,  # Worker to pre-fetch data for each single GPU\n    persistent_workers=True,  # If ``True``, the dataloader will not shut down the worker processes after an epoch end, which can accelerate training speed.\n    sampler=dict(  # training data sampler\n        type='DefaultSampler',  # DefaultSampler which supports both distributed and non-distributed training. Refer to https://mmengine.readthedocs.io/en/latest/api/generated/mmengine.dataset.DefaultSampler.html#mmengine.dataset.DefaultSampler\n        shuffle=True),  # randomly shuffle the training data in each epoch\n    batch_sampler=dict(type='AspectRatioBatchSampler'),  # Batch sampler for grouping images with similar aspect ratio into a same batch. It can reduce GPU memory cost.\n    dataset=dict(  # Train dataset config\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',  # Path of annotation file\n        data_prefix=dict(img='train2017/'),  # Prefix of image path\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),  # Config of filtering images and annotations\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(  # Validation dataloader config\n    batch_size=1,  # Batch size of a single GPU. If batch-size > 1, the extra padding area may influence the performance.\n    num_workers=2,  # Worker to pre-fetch data for each single GPU\n    persistent_workers=True,  # If ``True``, the dataloader will not shut down the worker processes after an epoch end, which can accelerate training speed.\n    drop_last=False,  # Whether to drop the last incomplete batch, if the dataset size is not divisible by the batch size\n    sampler=dict(\n        type='DefaultSampler',\n        shuffle=False),  # not shuffle during validation and testing\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,  # Turn on the test mode of the dataset to avoid filtering annotations or images\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader  # Testing dataloader config\n```\n\n[Evaluators](https://mmengine.readthedocs.io/en/latest/tutorials/evaluation.html) are used to compute the metrics of the trained model on the validation and testing datasets. The config of evaluators consists of one or a list of metric configs:\n\n```python\nval_evaluator = dict(  # Validation evaluator config\n    type='CocoMetric',  # The coco metric used to evaluate AR, AP, and mAP for detection and instance segmentation\n    ann_file=data_root + 'annotations/instances_val2017.json',  # Annotation file path\n    metric=['bbox', 'segm'],  # Metrics to be evaluated, `bbox` for detection and `segm` for instance segmentation\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator  # Testing evaluator config\n```\n\nSince the test dataset has no annotation files, the test_dataloader and test_evaluator config in MMDetection are generally equal to the val's. If you want to save the detection results on the test dataset, you can write the config like this:\n\n```python\n# inference on test dataset and\n# format the output results for submission.\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n        data_prefix=dict(img='test2017/'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n    metric=['bbox', 'segm'],  # Metrics to be evaluated\n    format_only=True,  # Only format and save the results to coco json file\n    outfile_prefix='./work_dirs/coco_detection/test')  # The prefix of output json files\n```\n\n### Training and testing config\n\nMMEngine's runner uses Loop to control the training, validation, and testing processes.\nUsers can set the maximum training epochs and validation intervals with these fields.\n\n```python\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop',  # The training loop type. Refer to https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py\n    max_epochs=12,  # Maximum training epochs\n    val_interval=1)  # Validation intervals. Run validation every epoch.\nval_cfg = dict(type='ValLoop')  # The validation loop type\ntest_cfg = dict(type='TestLoop')  # The testing loop type\n```\n\n### Optimization config\n\n`optim_wrapper` is the field to configure optimization-related settings. The optimizer wrapper not only provides the functions of the optimizer, but also supports functions such as gradient clipping, mixed precision training, etc. Find more in [optimizer wrapper tutorial](https://mmengine.readthedocs.io/en/latest/tutorials/optim_wrapper.html).\n\n```python\noptim_wrapper = dict(  # Optimizer wrapper config\n    type='OptimWrapper',  # Optimizer wrapper type, switch to AmpOptimWrapper to enable mixed precision training.\n    optimizer=dict(  # Optimizer config. Support all kinds of optimizers in PyTorch. Refer to https://pytorch.org/docs/stable/optim.html#algorithms\n        type='SGD',  # Stochastic gradient descent optimizer\n        lr=0.02,  # The base learning rate\n        momentum=0.9,  # Stochastic gradient descent with momentum\n        weight_decay=0.0001),  # Weight decay of SGD\n    clip_grad=None,  # Gradient clip option. Set None to disable gradient clip. Find usage in https://mmengine.readthedocs.io/en/latest/tutorials/optimizer.html\n    )\n```\n\n`param_scheduler` is a field that configures methods of adjusting optimization hyperparameters such as learning rate and momentum. Users can combine multiple schedulers to create a desired parameter adjustment strategy. Find more in [parameter scheduler tutorial](https://mmengine.readthedocs.io/en/latest/tutorials/param_scheduler.html) and [parameter scheduler API documents](https://mmengine.readthedocs.io/en/latest/api/generated/mmengine.optim._ParamScheduler.html#mmengine.optim._ParamScheduler)\n\n```python\nparam_scheduler = [\n    # Linear learning rate warm-up scheduler\n    dict(\n        type='LinearLR',  # Use linear policy to warmup learning rate\n        start_factor=0.001, # The ratio of the starting learning rate used for warmup\n        by_epoch=False,  # The warmup learning rate is updated by iteration\n        begin=0,  # Start from the first iteration\n        end=500),  # End the warmup at the 500th iteration\n    # The main LRScheduler\n    dict(\n        type='MultiStepLR',  # Use multi-step learning rate policy during training\n        by_epoch=True,  # The learning rate is updated by epoch\n        begin=0,   # Start from the first epoch\n        end=12,  # End at the 12th epoch\n        milestones=[8, 11],  # Epochs to decay the learning rate\n        gamma=0.1)  # The learning rate decay ratio\n]\n```\n\n### Hook config\n\nUsers can attach Hooks to training, validation, and testing loops to insert some operations during running. There are two different hook fields, one is `default_hooks` and the other is `custom_hooks`.\n\n`default_hooks` is a dict of hook configs, and they are the hooks must be required at the runtime. They have default priority which should not be modified. If not set, runner will use the default values. To disable a default hook, users can set its config to `None`. Find more in [HOOK](https://mmengine.readthedocs.io/en/latest/tutorials/hook.html).\n\n```python\ndefault_hooks = dict(\n    timer=dict(type='IterTimerHook'),  # Update the time spent during iteration into message hub\n    logger=dict(type='LoggerHook', interval=50),  # Collect logs from different components of Runner and write them to terminal, JSON file, tensorboard and wandb .etc\n    param_scheduler=dict(type='ParamSchedulerHook'), # update some hyper-parameters of optimizer\n    checkpoint=dict(type='CheckpointHook', interval=1), # Save checkpoints periodically\n    sampler_seed=dict(type='DistSamplerSeedHook'),  # Ensure distributed Sampler shuffle is active\n    visualization=dict(type='DetVisualizationHook'))  # Detection Visualization Hook. Used to visualize validation and testing process prediction results\n```\n\n`custom_hooks` is a list of all other hook configs. Users can develop their own hooks and insert them in this field.\n\n```python\ncustom_hooks = []\n```\n\n### Runtime config\n\n```python\ndefault_scope = 'mmdet'  # The default registry scope to find modules. Refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/registry.html\n\nenv_cfg = dict(\n    cudnn_benchmark=False,  # Whether to enable cudnn benchmark\n    mp_cfg=dict(  # Multi-processing config\n        mp_start_method='fork',  # Use fork to start multi-processing threads. 'fork' usually faster than 'spawn' but maybe unsafe. See discussion in https://github.com/pytorch/pytorch/issues/1355\n        opencv_num_threads=0),  # Disable opencv multi-threads to avoid system being overloaded\n    dist_cfg=dict(backend='nccl'),  # Distribution configs\n)\n\nvis_backends = [dict(type='LocalVisBackend')]  # Visualization backends. Refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/visualization.html\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\nlog_processor = dict(\n    type='LogProcessor',  # Log processor to process runtime logs\n    window_size=50,  # Smooth interval of log values\n    by_epoch=True)  # Whether to format logs with epoch type. Should be consistent with the train loop's type.\n\nlog_level = 'INFO'  # The level of logging.\nload_from = None  # Load model checkpoint as a pre-trained model from a given path. This will not resume training.\nresume = False  # Whether to resume from the checkpoint defined in `load_from`. If `load_from` is None, it will resume the latest checkpoint in the `work_dir`.\n```\n\n## Iter-based config\n\nMMEngine's Runner also provides an iter-based training loop except for epoch-based.\nTo use iter-based training, users should modify the `train_cfg`, `param_scheduler`, `train_dataloader`, `default_hooks`, and `log_processor`.\nHere is an example of changing an epoch-based RetinaNet config to iter-based: `configs/retinanet/retinanet_r50_fpn_90k_coco.py`\n\n```python\n# Iter-based training config\ntrain_cfg = dict(\n    _delete_=True,  # Ignore the base config setting (optional)\n    type='IterBasedTrainLoop',  # Use iter-based training loop\n    max_iters=90000,  # Maximum iterations\n    val_interval=10000)  # Validation interval\n\n\n# Change the scheduler to iter-based\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=90000,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\n# Switch to InfiniteSampler to avoid dataloader restart\ntrain_dataloader = dict(sampler=dict(type='InfiniteSampler'))\n\n# Change the checkpoint saving interval to iter-based\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\n\n# Change the log format to iter-based\nlog_processor = dict(by_epoch=False)\n```\n\n## Config file inheritance\n\nThere are 4 basic component types under `config/_base_`, dataset, model, schedule, default_runtime.\nMany methods could be easily constructed with one of these models like Faster R-CNN, Mask R-CNN, Cascade R-CNN, RPN, SSD.\nThe configs that are composed by components from `_base_` are called the _primitive_.\n\nFor all configs under the same folder, it is recommended to have only **one** _primitive_ config. All other configs should inherit from the _primitive_ config. In this way, the maximum of inheritance level is 3.\n\nFor easy understanding, we recommend contributors to inherit from existing methods.\nFor example, if some modification is made based on Faster R-CNN, users may first inherit the basic Faster R-CNN structure by specifying `_base_ = ../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py`, then modify the necessary fields in the config files.\n\nIf you are building an entirely new method that does not share the structure with any of the existing methods, you may create a folder `xxx_rcnn` under `configs`,\n\nPlease refer to [mmengine config tutorial](https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html) for detailed documentation.\n\nBy setting the `_base_` field, we can set which files the current configuration file inherits from.\n\nWhen `_base_` is a string of a file path, it means inheriting the contents from one config file.\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n```\n\nWhen `_base_` is a list of multiple file paths, it means inheriting from multiple files.\n\n```python\n_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n```\n\nIf you wish to inspect the config file, you may run `python tools/misc/print_config.py /PATH/TO/CONFIG` to see the complete config.\n\n### Ignore some fields in the base configs\n\nSometimes, you may set `_delete_=True` to ignore some of the fields in base configs.\nYou may refer to [mmengine config tutorial](https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html) for a simple illustration.\n\nIn MMDetection, for example, to change the backbone of Mask R-CNN with the following config.\n\n```python\nmodel = dict(\n    type='MaskRCNN',\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(...),\n    rpn_head=dict(...),\n    roi_head=dict(...))\n```\n\n`ResNet` and `HRNet` use different keywords to construct.\n\n```python\n_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(...))\n```\n\nThe `_delete_=True` would replace all old keys in `backbone` field with new keys.\n\n### Use intermediate variables in configs\n\nSome intermediate variables are used in the configs files, like `train_pipeline`/`test_pipeline` in datasets.\nIt's worth noting that when modifying intermediate variables in the children configs, users need to pass the intermediate variables into corresponding fields again.\nFor example, we would like to use a multi-scale strategy to train a Mask R-CNN. `train_pipeline`/`test_pipeline` are intermediate variables we would like to modify.\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = dict(dataset=dict(pipeline=test_pipeline))\n```\n\nWe first define the new `train_pipeline`/`test_pipeline` and pass them into dataloader fields.\n\nSimilarly, if we would like to switch from `SyncBN` to `BN` or `MMSyncBN`, we need to substitute every `norm_cfg` in the config.\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='BN', requires_grad=True)\nmodel = dict(\n    backbone=dict(norm_cfg=norm_cfg),\n    neck=dict(norm_cfg=norm_cfg),\n    ...)\n```\n\n### Reuse variables in \\_base\\_ file\n\nIf the users want to reuse the variables in the base file, they can get a copy of the corresponding variable by using `{{_base_.xxx}}`. E.g:\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\na = {{_base_.model}} # Variable `a` is equal to the `model` defined in `_base_`\n```\n\n## Modify config through script arguments\n\nWhen submitting jobs using `tools/train.py` or `tools/test.py`, you may specify `--cfg-options` to in-place modify the config.\n\n- Update config keys of dict chains.\n\n  The config options can be specified following the order of the dict keys in the original config.\n  For example, `--cfg-options model.backbone.norm_eval=False` changes the all BN modules in model backbones to `train` mode.\n\n- Update keys inside a list of configs.\n\n  Some config dicts are composed as a list in your config. For example, the training pipeline `train_dataloader.dataset.pipeline` is normally a list\n  e.g. `[dict(type='LoadImageFromFile'), ...]`. If you want to change `'LoadImageFromFile'` to `'LoadImageFromNDArray'` in the pipeline,\n  you may specify `--cfg-options data.train.pipeline.0.type=LoadImageFromNDArray`.\n\n- Update values of list/tuples.\n\n  If the value to be updated is a list or a tuple. For example, the config file normally sets `model.data_preprocessor.mean=[123.675, 116.28, 103.53]`. If you want to\n  change the mean values, you may specify `--cfg-options model.data_preprocessor.mean=\"[127,127,127]\"`. Note that the quotation mark `\"` is necessary to\n  support list/tuple data types, and **NO** white space is allowed inside the quotation marks in the specified value.\n\n## Config name style\n\nWe follow the below style to name config files. Contributors are advised to follow the same style.\n\n```\n{algorithm name}_{model component names [component1]_[component2]_[...]}_{training settings}_{training dataset information}_{testing dataset information}.py\n```\n\nThe file name is divided into five parts. All parts and components are connected with `_` and words of each part or component should be connected with `-`.\n\n- `{algorithm name}`: The name of the algorithm. It can be a detector name such as `faster-rcnn`, `mask-rcnn`, etc. Or can be a semi-supervised or knowledge-distillation algorithm such as `soft-teacher`, `lad`. etc.\n- `{model component names}`: Names of the components used in the algorithm such as backbone, neck, etc. For example, `r50-caffe_fpn_gn-head` means using caffe-style ResNet50, FPN and detection head with Group Norm in the algorithm.\n- `{training settings}`: Information of training settings such as batch size, augmentations, loss trick, scheduler, and epochs/iterations. For example: `4xb4-mixup-giou-coslr-100e` means using 8-gpus x 4-images-per-gpu, mixup augmentation, GIoU loss, cosine annealing learning rate, and train 100 epochs.\n  Some abbreviations:\n  - `{gpu x batch_per_gpu}`: GPUs and samples per GPU. `bN` indicates N batch size per GPU. E.g. `4xb4` is the short term of 4-GPUs x 4-images-per-GPU. And `8xb2` is used by default if not mentioned.\n  - `{schedule}`: training schedule, options are `1x`, `2x`, `20e`, etc.\n    `1x` and `2x` means 12 epochs and 24 epochs respectively.\n    `20e` is adopted in cascade models, which denotes 20 epochs.\n    For `1x`/`2x`, the initial learning rate decays by a factor of 10 at the 8/16th and 11/22th epochs.\n    For `20e`, the initial learning rate decays by a factor of 10 at the 16th and 19th epochs.\n- `{training dataset information}`: Training dataset names like `coco`, `coco-panoptic`, `cityscapes`, `voc-0712`, `wider-face`.\n- `{testing dataset information}` (optional): Testing dataset name for models trained on one dataset but tested on another. If not mentioned, it means the model was trained and tested on the same dataset type.\n"
  },
  {
    "path": "docs/en/user_guides/dataset_prepare.md",
    "content": "# Dataset Prepare\n\n### Basic Detection Dataset Preparation\n\nMMDetection supports multiple public datasets including COCO, Pascal VOC, CityScapes, and [more](../../../configs/_base_/datasets).\n\nPublic datasets like [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/index.html) or mirror and [COCO](https://cocodataset.org/#download) are available from official websites or mirrors. Note: In the detection task, Pascal VOC 2012 is an extension of Pascal VOC 2007 without overlap, and we usually use them together.\nIt is recommended to download and extract the dataset somewhere outside the project directory and symlink the dataset root to `$MMDETECTION/data` as below.\nIf your folder structure is different, you may need to change the corresponding paths in config files.\n\nWe provide a script to download datasets such as COCO, you can run `python tools/misc/download_dataset.py --dataset-name coco2017` to download COCO dataset.\nFor users in China, more datasets can be downloaded from the opensource dataset platform: [OpenDataLab](https://opendatalab.com/?source=OpenMMLab%20GitHub).\n\nFor more usage please refer to [dataset-download](./useful_tools.md#dataset-download)\n\n```text\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n│   ├── VOCdevkit\n│   │   ├── VOC2007\n│   │   ├── VOC2012\n```\n\nSome models require additional [COCO-stuff](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip) datasets, such as HTC, DetectoRS and SCNet, you can download, unzip, and then move them to the coco folder. The directory should be like this.\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── stuffthingmaps\n```\n\nPanoptic segmentation models like PanopticFPN require additional [COCO Panoptic](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip) datasets, you can download, unzip, and then move them to the coco annotation folder. The directory should be like this.\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── panoptic_train2017.json\n│   │   │   ├── panoptic_train2017\n│   │   │   ├── panoptic_val2017.json\n│   │   │   ├── panoptic_val2017\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n```\n\nThe [cityscapes](https://www.cityscapes-dataset.com/) annotations need to be converted into the coco format using `tools/dataset_converters/cityscapes.py`:\n\n```shell\npip install cityscapesscripts\n\npython tools/dataset_converters/cityscapes.py \\\n    ./data/cityscapes \\\n    --nproc 8 \\\n    --out-dir ./data/cityscapes/annotations\n```\n\n### COCO Caption Dataset Preparation\n\nCOCO Caption uses the COCO2014 dataset image and uses the annotation of karpathy.\n\nAt first, you need to download the COCO2014 dataset.\n\n```shell\npython tools/misc/download_dataset.py --dataset-name coco2014 --unzip\n```\n\nThe dataset will be downloaded to `data/coco` under the current path. Then download the annotation of karpathy.\n\n```shell\ncd data/coco/annotations\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_train.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val_gt.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test_gt.json\n```\n\nThe final directory structure of the dataset folder that can be directly used for training and testing is as follows:\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── coco_karpathy_train.json\n│   │   │   ├── coco_karpathy_test.json\n│   │   │   ├── coco_karpathy_val.json\n│   │   │   ├── coco_karpathy_val_gt.json\n│   │   │   ├── coco_karpathy_test_gt.json\n│   │   ├── train2014\n│   │   ├── val2014\n│   │   ├── test2014\n```\n\n### COCO Semantic Dataset Preparation\n\nThere are two types of annotations for COCO semantic segmentation, which differ mainly in the definition of category names, so there are two ways to handle them. The first is to directly use the stuffthingmaps dataset, and the second is to use the panoptic dataset.\n\n**(1) Use stuffthingmaps dataset**\n\nThe download link for this dataset is [stuffthingmaps_trainval2017](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip). Please download and extract it to the `data/coco` folder.\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── stuffthingmaps\n```\n\nThis dataset is different from the standard COCO category annotation in that it includes 172 classes: 80 \"thing\" classes, 91 \"stuff\" classes, and 1 \"unlabeled\" class. The description of each class can be found at https://github.com/nightrome/cocostuff/blob/master/labels.md.\n\nAlthough only 172 categories are annotated, the maximum label ID in `stuffthingmaps` is 182, and some categories in the middle are not annotated. In addition, the \"unlabeled\" category of class 0 is removed. Therefore, the relationship between the value at each position in the final `stuffthingmaps` image can be found at https://github.com/kazuto1011/deeplab-pytorch/blob/master/data/datasets/cocostuff/labels.txt.\n\nTo train efficiently and conveniently for users, we need to remove 12 unannotated classes before starting training or evaluation. The names of these 12 classes are: `street sign, hat, shoe, eye glasses, plate, mirror, window, desk, door, blender, hair brush`. The category information that can be used for training and evaluation can be found in `mmdet/datasets/coco_semantic.py`.\n\nYou can use `tools/dataset_converters/coco_stuff164k.py` to convert the downloaded `stuffthingmaps` to a dataset that can be directly used for training and evaluation. The directory structure of the converted dataset is as follows:\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── stuffthingmaps\n│   │   ├── stuffthingmaps_semseg\n```\n\n`stuffthingmaps_semseg` is the newly generated COCO semantic segmentation dataset that can be directly used for training and testing.\n\n**(2) use panoptic dataset**\n\nThe number of categories in the semantic segmentation dataset generated through panoptic annotation will be less than that generated using the `stuffthingmaps` dataset. First, you need to prepare the panoptic segmentation annotations, and then use the following script to complete the conversion.\n\n```shell\npython tools/dataset_converters/prepare_coco_semantic_annos_from_panoptic_annos.py data/coco\n```\n\nThe directory structure of the converted dataset is as follows:\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── panoptic_train2017.json\n│   │   │   ├── panoptic_train2017\n│   │   │   ├── panoptic_val2017.json\n│   │   │   ├── panoptic_val2017\n│   │   │   ├── panoptic_semseg_train2017\n│   │   │   ├── panoptic_semseg_val2017\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n```\n\n`panoptic_semseg_train2017` and `panoptic_semseg_val2017` are the newly generated COCO semantic segmentation datasets that can be directly used for training and testing. Note that their category information is the same as that of COCO panoptic segmentation, including both \"thing\" and \"stuff\" categories.\n\n### RefCOCO Dataset Preparation\n\nThe images and annotations of [RefCOCO](https://github.com/lichengunc/refer) series datasets can be download by running `tools/misc/download_dataset.py`:\n\n```shell\npython tools/misc/download_dataset.py --dataset-name refcoco --save-dir data/coco --unzip\n```\n\nThen the directory should be like this:\n\n```text\ndata\n├── coco\n│   ├── refcoco\n│   │   ├── instances.json\n│   │   ├── refs(google).p\n│   │   └── refs(unc).p\n│   ├── refcoco+\n│   │   ├── instances.json\n│   │   └── refs(unc).p\n│   ├── refcocog\n│   │   ├── instances.json\n│   │   ├── refs(google).p\n│   │   └── refs(umd).p\n│   │── train2014\n```\n\n### ADE20K 2016 Dataset Preparation\n\nThe images and annotations of [ADE20K](https://groups.csail.mit.edu/vision/datasets/ADE20K/) dataset can be download by running `tools/misc/download_dataset.py`:\n\n```shell\npython tools/misc/download_dataset.py --dataset-name ade20k_2016 --save-dir data --unzip\n```\n\nThen move the annotations to the `data/ADEChallengeData2016` directory and run the preprocess script to produce the coco format annotations:\n\n```shell\nmv data/annotations_instance data/ADEChallengeData2016/\nmv data/categoryMapping.txt data/ADEChallengeData2016/\nmv data/imgCatIds.json data/ADEChallengeData2016/\npython tools/dataset_converters/ade20k2coco.py data/ADEChallengeData2016 --task panoptic\npython tools/dataset_converters/ade20k2coco.py data/ADEChallengeData2016 --task instance\n```\n\nThe directory should be like this.\n\n```text\ndata\n├── ADEChallengeData2016\n│   ├── ade20k_instance_train.json\n│   ├── ade20k_instance_val.json\n│   ├── ade20k_panoptic_train\n│   │   ├── ADE_train_00000001.png\n│   │   ├── ADE_train_00000002.png\n│   │   ├── ...\n│   ├── ade20k_panoptic_train.json\n│   ├── ade20k_panoptic_val\n│   │   ├── ADE_val_00000001.png\n│   │   ├── ADE_val_00000002.png\n│   │   ├── ...\n│   ├── ade20k_panoptic_val.json\n│   ├── annotations\n│   │   ├── training\n│   │   │   ├── ADE_train_00000001.png\n│   │   │   ├── ADE_train_00000002.png\n│   │   │   ├── ...\n│   │   ├── validation\n│   │   │   ├── ADE_val_00000001.png\n│   │   │   ├── ADE_val_00000002.png\n│   │   │   ├── ...\n│   ├── annotations_instance\n│   │   ├── training\n│   │   │   ├── ADE_train_00000001.png\n│   │   │   ├── ADE_train_00000002.png\n│   │   │   ├── ...\n│   │   ├── validation\n│   │   │   ├── ADE_val_00000001.png\n│   │   │   ├── ADE_val_00000002.png\n│   │   │   ├── ...\n│   ├── categoryMapping.txt\n│   ├── images\n│   │   ├── training\n│   │   │   ├── ADE_train_00000001.jpg\n│   │   │   ├── ADE_train_00000002.jpg\n│   │   │   ├── ...\n│   │   ├── validation\n│   │   │   ├── ADE_val_00000001.jpg\n│   │   │   ├── ADE_val_00000002.jpg\n│   │   │   ├── ...\n│   ├── imgCatIds.json\n│   ├── objectInfo150.txt\n│   │── sceneCategories.txt\n```\n\nThe above folders include all data of ADE20K's semantic segmentation, instance segmentation, and panoptic segmentation.\n\n### Download from OpenDataLab\n\nBy using [OpenDataLab](https://opendatalab.com/), researchers can obtain free formatted datasets in various fields. Through the search function of the platform, researchers may address the dataset they look for quickly and easily. Using the formatted datasets from the platform, researchers can efficiently conduct tasks across datasets.\n\nCurrently, MIM supports downloading VOC and COCO datasets from OpenDataLab with one command line. More datasets will be supported in the future. You can also directly download the datasets you need from the OpenDataLab platform and then convert them to the format required by MMDetection.\n\nIf you use MIM to download, make sure that the version is greater than v0.3.8. You can use the following command to update:\n\n```Bash\npip install -U openmim\n```\n\n```Bash\n# install OpenXLab CLI tools\npip install -U openxlab\n# log in OpenXLab, registry\nopenxlab login\n\n# download voc2007 and preprocess by MIM\nmim download mmdet --dataset voc2007\n\n# download voc2012 and preprocess by MIM\nmim download mmdet --dataset voc2012\n\n# download coco2017 and preprocess by MIM\nmim download mmdet --dataset coco2017\n```\n"
  },
  {
    "path": "docs/en/user_guides/deploy.md",
    "content": "# Model Deployment\n\nThe deployment of OpenMMLab codebases, including MMDetection, MMPretrain and so on are supported by [MMDeploy](https://github.com/open-mmlab/mmdeploy).\nThe latest deployment guide for MMDetection can be found from [here](https://mmdeploy.readthedocs.io/en/dev-1.x/04-supported-codebases/mmdet.html).\n\nThis tutorial is organized as follows:\n\n- [Installation](#installation)\n- [Convert model](#convert-model)\n- [Model specification](#model-specification)\n- [Model inference](#model-inference)\n  - [Backend model inference](#backend-model-inference)\n  - [SDK model inference](#sdk-model-inference)\n- [Supported models](#supported-models)\n\n## Installation\n\nPlease follow the [guide](https://mmdetection.readthedocs.io/en/latest/get_started.html) to install mmdet. And then install mmdeploy from source by following [this](https://mmdeploy.readthedocs.io/en/1.x/get_started.html#installation) guide.\n\n```{note}\nIf you install mmdeploy prebuilt package, please also clone its repository by 'git clone https://github.com/open-mmlab/mmdeploy.git --depth=1' to get the deployment config files.\n```\n\n## Convert model\n\nSuppose mmdetection and mmdeploy repositories are in the same directory, and the working directory is the root path of mmdetection.\n\nTake [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py) model as an example. You can download its checkpoint from [here](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth), and then convert it to onnx model as follows:\n\n```python\nfrom mmdeploy.apis import torch2onnx\nfrom mmdeploy.backend.sdk.export_info import export2SDK\n\nimg = 'demo/demo.jpg'\nwork_dir = 'mmdeploy_models/mmdet/onnx'\nsave_file = 'end2end.onnx'\ndeploy_cfg = '../mmdeploy/configs/mmdet/detection/detection_onnxruntime_dynamic.py'\nmodel_cfg = 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel_checkpoint = 'faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'\ndevice = 'cpu'\n\n# 1. convert model to onnx\ntorch2onnx(img, work_dir, save_file, deploy_cfg, model_cfg,\n           model_checkpoint, device)\n\n# 2. extract pipeline info for inference by MMDeploy SDK\nexport2SDK(deploy_cfg, model_cfg, work_dir, pth=model_checkpoint,\n           device=device)\n```\n\nIt is crucial to specify the correct deployment config during model conversion. MMDeploy has already provided builtin deployment config [files](https://github.com/open-mmlab/mmdeploy/tree/1.x/configs/mmdet) of all supported backends for mmdetection, under which the config file path follows the pattern:\n\n```\n{task}/{task}_{backend}-{precision}_{static | dynamic}_{shape}.py\n```\n\n- **{task}:** task in mmdetection.\n\n  There are two of them. One is `detection` and the other is `instance-seg`, indicating instance segmentation.\n\n  mmdet models like `RetinaNet`, `Faster R-CNN` and `DETR` and so on belongs to `detection` task. While `Mask R-CNN` is one of `instance-seg` models.\n\n  **DO REMEMBER TO USE** `detection/detection_*.py` deployment config file when trying to convert detection models and use `instance-seg/instance-seg_*.py` to deploy instance segmentation models.\n\n- **{backend}:** inference backend, such as onnxruntime, tensorrt, pplnn, ncnn, openvino, coreml etc.\n\n- **{precision}:** fp16, int8. When it's empty, it means fp32\n\n- **{static | dynamic}:** static shape or dynamic shape\n\n- **{shape}:** input shape or shape range of a model\n\nTherefore, in the above example, you can also convert `Faster R-CNN` to tensorrt-fp16 model by `detection_tensorrt-fp16_dynamic-320x320-1344x1344.py`.\n\n```{tip}\nWhen converting mmdet models to tensorrt models, --device should be set to \"cuda\"\n```\n\n## Model specification\n\nBefore moving on to model inference chapter, let's know more about the converted model structure which is very important for model inference.\n\nThe converted model locates in the working directory like `mmdeploy_models/mmdet/onnx` in the previous example. It includes:\n\n```\nmmdeploy_models/mmdet/onnx\n├── deploy.json\n├── detail.json\n├── end2end.onnx\n└── pipeline.json\n```\n\nin which,\n\n- **end2end.onnx**: backend model which can be inferred by ONNX Runtime\n- ***xxx*.json**: the necessary information for mmdeploy SDK\n\nThe whole package **mmdeploy_models/mmdet/onnx** is defined as **mmdeploy SDK model**, i.e., **mmdeploy SDK model** includes both backend model and inference meta information.\n\n## Model inference\n\n### Backend model inference\n\nTake the previous converted `end2end.onnx` model as an example, you can use the following code to inference the model and visualize the results.\n\n```python\nfrom mmdeploy.apis.utils import build_task_processor\nfrom mmdeploy.utils import get_input_shape, load_config\nimport torch\n\ndeploy_cfg = '../mmdeploy/configs/mmdet/detection/detection_onnxruntime_dynamic.py'\nmodel_cfg = 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\ndevice = 'cpu'\nbackend_model = ['mmdeploy_models/mmdet/onnx/end2end.onnx']\nimage = 'demo/demo.jpg'\n\n# read deploy_cfg and model_cfg\ndeploy_cfg, model_cfg = load_config(deploy_cfg, model_cfg)\n\n# build task and backend model\ntask_processor = build_task_processor(model_cfg, deploy_cfg, device)\nmodel = task_processor.build_backend_model(backend_model)\n\n# process input image\ninput_shape = get_input_shape(deploy_cfg)\nmodel_inputs, _ = task_processor.create_input(image, input_shape)\n\n# do model inference\nwith torch.no_grad():\n    result = model.test_step(model_inputs)\n\n# visualize results\ntask_processor.visualize(\n    image=image,\n    model=model,\n    result=result[0],\n    window_name='visualize',\n    output_file='output_detection.png')\n```\n\n### SDK model inference\n\nYou can also perform SDK model inference like following,\n\n```python\nfrom mmdeploy_python import Detector\nimport cv2\n\nimg = cv2.imread('demo/demo.jpg')\n\n# create a detector\ndetector = Detector(model_path='mmdeploy_models/mmdet/onnx',\n                    device_name='cpu', device_id=0)\n# perform inference\nbboxes, labels, masks = detector(img)\n\n# visualize inference result\nindices = [i for i in range(len(bboxes))]\nfor index, bbox, label_id in zip(indices, bboxes, labels):\n    [left, top, right, bottom], score = bbox[0:4].astype(int), bbox[4]\n    if score < 0.3:\n        continue\n\n    cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0))\n\ncv2.imwrite('output_detection.png', img)\n```\n\nBesides python API, mmdeploy SDK also provides other FFI (Foreign Function Interface), such as C, C++, C#, Java and so on. You can learn their usage from [demos](https://github.com/open-mmlab/mmdeploy/tree/1.x/demo).\n\n## Supported models\n\nPlease refer to [here](https://mmdeploy.readthedocs.io/en/1.x/04-supported-codebases/mmdet.html#supported-models) for the supported model list.\n"
  },
  {
    "path": "docs/en/user_guides/finetune.md",
    "content": "# Finetuning Models\n\nDetectors pre-trained on the COCO dataset can serve as a good pre-trained model for other datasets, e.g., CityScapes and KITTI Dataset.\nThis tutorial provides instructions for users to use the models provided in the [Model Zoo](../model_zoo.md) for other datasets to obtain better performance.\n\nThere are two steps to finetune a model on a new dataset.\n\n- Add support for the new dataset following [Customize Datasets](../advanced_guides/customize_dataset.md).\n- Modify the configs as will be discussed in this tutorial.\n\nTake the finetuning process on Cityscapes Dataset as an example, the users need to modify five parts in the config.\n\n## Inherit base configs\n\nTo release the burden and reduce bugs in writing the whole configs, MMDetection V3.0 support inheriting configs from multiple existing configs. To finetune a Mask RCNN model, the new config needs to inherit\n`_base_/models/mask-rcnn_r50_fpn.py` to build the basic structure of the model. To use the Cityscapes Dataset, the new config can also simply inherit `_base_/datasets/cityscapes_instance.py`. For runtime settings such as logger settings, the new config needs to inherit `_base_/default_runtime.py`. For training schedules, the new config can to inherit `_base_/schedules/schedule_1x.py`. These configs are in the `configs` directory and the users can also choose to write the whole contents rather than use inheritance.\n\n```python\n_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/cityscapes_instance.py', '../_base_/default_runtime.py',\n    '../_base_/schedules/schedule_1x.py'\n]\n```\n\n## Modify head\n\nThen the new config needs to modify the head according to the class numbers of the new datasets. By only changing `num_classes` in the roi_head, the weights of the pre-trained models are mostly reused except for the final prediction head.\n\n```python\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=8,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=8,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))))\n```\n\n## Modify dataset\n\nThe users may also need to prepare the dataset and write the configs about dataset, refer to [Customize Datasets](../advanced_guides/customize_dataset.md) for more detail. MMDetection V3.0 already supports VOC, WIDERFACE, COCO, LIVS, OpenImages, DeepFashion, Objects365, and Cityscapes Dataset.\n\n## Modify training schedule\n\nThe finetuning hyperparameters vary from the default schedule. It usually requires a smaller learning rate and fewer training epochs\n\n```python\n# optimizer\n# lr is set for a batch size of 8\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=8,\n        by_epoch=True,\n        milestones=[7],\n        gamma=0.1)\n]\n\n# max_epochs\ntrain_cfg = dict(max_epochs=8)\n\n# log config\ndefault_hooks = dict(logger=dict(interval=100)),\n```\n\n## Use pre-trained model\n\nTo use the pre-trained model, the new config adds the link of pre-trained models in the `load_from`. The users might need to download the model weights before training to avoid the download time during training.\n\n```python\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'  # noqa\n```\n"
  },
  {
    "path": "docs/en/user_guides/index.rst",
    "content": "Train & Test\n**************\n\nMMDetection provides hundreds of pretrained detection models in `Model Zoo <https://mmdetection.readthedocs.io/en/latest/model_zoo.html>`_,\nand supports multiple standard datasets, including Pascal VOC, COCO, CityScapes, LVIS, etc. This note will show how to perform common tasks on these existing models and standard datasets:\n\n\n.. toctree::\n   :maxdepth: 1\n\n   config.md\n   inference.md\n   dataset_prepare.md\n   test.md\n   train.md\n   new_model.md\n   finetune.md\n   test_results_submission.md\n   init_cfg.md\n   single_stage_as_rpn.md\n   semi_det.md\n\n\nUseful Tools\n************\n\n.. toctree::\n   :maxdepth: 1\n\n   useful_tools.md\n   useful_hooks.md\n   visualization.md\n   robustness_benchmarking.md\n   deploy.md\n   label_studio.md\n   tracking_analysis_tools.md\n   tracking_config.md\n   tracking_dataset_prepare.md\n   tracking_inference.md\n   tracking_train_test.md\n   tracking_visualization.md\n"
  },
  {
    "path": "docs/en/user_guides/inference.md",
    "content": "# Inference with existing models\n\nMMDetection provides hundreds of pre-trained detection models in [Model Zoo](https://mmdetection.readthedocs.io/en/latest/model_zoo.html).\nThis note will show how to inference, which means using trained models to detect objects on images.\n\nIn MMDetection, a model is defined by a [configuration file](https://mmdetection.readthedocs.io/en/latest/user_guides/config.html) and existing model parameters are saved in a checkpoint file.\n\nTo start with, we recommend [RTMDet](https://github.com/open-mmlab/mmdetection/tree/main/configs/rtmdet) with this [configuration file](https://github.com/open-mmlab/mmdetection/blob/main/configs/rtmdet/rtmdet_l_8xb32-300e_coco.py) and this [checkpoint file](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_l_8xb32-300e_coco/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth). It is recommended to download the checkpoint file to `checkpoints` directory.\n\n## High-level APIs for inference - `Inferencer`\n\nIn OpenMMLab, all the inference operations are unified into a new interface - Inferencer. Inferencer is designed to expose a neat and simple API to users, and shares very similar interface across different OpenMMLab libraries.\nA notebook demo can be found in [demo/inference_demo.ipynb](https://github.com/open-mmlab/mmdetection/blob/main/demo/inference_demo.ipynb).\n\n### Basic Usage\n\nYou can get inference results for an image with only 3 lines of code.\n\n```python\nfrom mmdet.apis import DetInferencer\n\n# Initialize the DetInferencer\ninferencer = DetInferencer('rtmdet_tiny_8xb32-300e_coco')\n\n# Perform inference\ninferencer('demo/demo.jpg', show=True)\n```\n\nThe resulting output will be displayed in a new window:.\n\n<div align=\"center\">\n    <img src='https://github.com/open-mmlab/mmdetection/assets/27466624/311df42d-640a-4a5b-9ad9-9ba7f3ec3a2f' />\n</div>\n\n```{note}\nIf you are running MMDetection on a server without GUI or via SSH tunnel with X11 forwarding disabled, the `show` option will not work. However, you can still save visualizations to files by setting `out_dir` arguments. Read [Dumping Results](#dumping-results) for details.\n```\n\n### Initialization\n\nEach Inferencer must be initialized with a model. You can also choose the inference device during initialization.\n\n#### Model Initialization\n\n- To infer with MMDetection's pre-trained model, passing its name to the argument `model` can work. The weights will be automatically downloaded and loaded from OpenMMLab's model zoo.\n\n  ```python\n  inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco')\n  ```\n\n  There is a very easy to list all model names in MMDetection.\n\n  ```python\n  # models is a list of model names, and them will print automatically\n  models = DetInferencer.list_models('mmdet')\n  ```\n\n  You can load another weight by passing its path/url to `weights`.\n\n  ```python\n  inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', weights='path/to/rtmdet.pth')\n  ```\n\n- To load custom config and weight, you can pass the path to the config file to `model` and the path to the weight to `weights`.\n\n  ```python\n  inferencer = DetInferencer(model='path/to/rtmdet_config.py', weights='path/to/rtmdet.pth')\n  ```\n\n- By default, [MMEngine](https://github.com/open-mmlab/mmengine/) dumps config to the weight. If you have a weight trained on MMEngine, you can also pass the path to the weight file to `weights` without specifying `model`:\n\n  ```python\n  # It will raise an error if the config file cannot be found in the weight. Currently, within the MMDetection model repository, only the weights of ddq-detr-4scale_r50 can be loaded in this manner.\n  inferencer = DetInferencer(weights='https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq-detr-4scale_r50_8xb2-12e_coco/ddq-detr-4scale_r50_8xb2-12e_coco_20230809_170711-42528127.pth')\n  ```\n\n- Passing config file to `model` without specifying `weight` will result in a randomly initialized model.\n\n### Device\n\nEach Inferencer instance is bound to a device.\nBy default, the best device is automatically decided by [MMEngine](https://github.com/open-mmlab/mmengine/). You can also alter the device by specifying the `device` argument. For example, you can use the following code to create an Inferencer on GPU 1.\n\n```python\ninferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', device='cuda:1')\n```\n\nTo create an Inferencer on CPU:\n\n```python\ninferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', device='cpu')\n```\n\nRefer to [torch.device](https://pytorch.org/docs/stable/tensor_attributes.html#torch.device) for all the supported forms.\n\n### Inference\n\nOnce the Inferencer is initialized, you can directly pass in the raw data to be inferred and get the inference results from return values.\n\n#### Input\n\nInput can be either of these types:\n\n- str: Path/URL to the image.\n\n  ```python\n  inferencer('demo/demo.jpg')\n  ```\n\n- array: Image in numpy array. It should be in BGR order.\n\n  ```python\n  import mmcv\n  array = mmcv.imread('demo/demo.jpg')\n  inferencer(array)\n  ```\n\n- list: A list of basic types above. Each element in the list will be processed separately.\n\n  ```python\n  inferencer(['img_1.jpg', 'img_2.jpg])\n  # You can even mix the types\n  inferencer(['img_1.jpg', array])\n  ```\n\n- str: Path to the directory. All images in the directory will be processed.\n\n  ```python\n  inferencer('path/to/your_imgs/')\n  ```\n\n### Output\n\nBy default, each `Inferencer` returns the prediction results in a dictionary format.\n\n- `visualization` contains the visualized predictions.\n\n- `predictions` contains the predictions results in a json-serializable format. But it's an empty list by default unless `return_vis=True`.\n\n```python\n{\n      'predictions' : [\n        # Each instance corresponds to an input image\n        {\n          'labels': [...],  # int list of length (N, )\n          'scores': [...],  # float list of length (N, )\n          'bboxes': [...],  # 2d list of shape (N, 4), format: [min_x, min_y, max_x, max_y]\n        },\n        ...\n      ],\n      'visualization' : [\n        array(..., dtype=uint8),\n      ]\n  }\n```\n\nIf you wish to get the raw outputs from the model, you can set `return_datasamples` to `True` to get the original [DataSample](advanced_guides/structures.md), which will be stored in `predictions`.\n\n#### Dumping Results\n\nApart from obtaining predictions from the return value, you can also export the predictions/visualizations to files by setting `out_dir` and `no_save_pred`/`no_save_vis` arguments.\n\n```python\ninferencer('demo/demo.jpg', out_dir='outputs/', no_save_pred=False)\n```\n\nResults in the directory structure like:\n\n```text\noutputs\n├── preds\n│   └── demo.json\n└── vis\n    └── demo.jpg\n```\n\nThe filename of each file is the same as the corresponding input image filename. If the input image is an array, the filename will be a number starting from 0.\n\n#### Batch Inference\n\nYou can customize the batch size by setting `batch_size`. The default batch size is 1.\n\n### API\n\nHere are extensive lists of parameters that you can use.\n\n- **DetInferencer.\\_\\_init\\_\\_():**\n\n| Arguments       | Type          | Type    | Description                                                                                                                                                                                                                                                                                              |\n| --------------- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `model`         | str, optional | None    | Path to the config file or the model name defined in metafile. For example, it could be 'rtmdet-s' or 'rtmdet_s_8xb32-300e_coco' or 'configs/rtmdet/rtmdet_s_8xb32-300e_coco.py'. If the model is not specified, the user must provide the `weights` saved by MMEngine which contains the config string. |\n| `weights`       | str, optional | None    | Path to the checkpoint. If it is not specified and `model` is a model name of metafile, the weights will be loaded from metafile.                                                                                                                                                                        |\n| `device`        | str, optional | None    | Device used for inference, accepting all allowed strings by `torch.device`. E.g., 'cuda:0' or 'cpu'. If None, the available device will be automatically used.                                                                                                                                           |\n| `scope`         | str, optional | 'mmdet' | The scope of the model.                                                                                                                                                                                                                                                                                  |\n| `palette`       | str           | 'none'  | Color palette used for visualization. The order of priority is palette -> config -> checkpoint.                                                                                                                                                                                                          |\n| `show_progress` | bool          | True    | Control whether to display the progress bar during the inference process.                                                                                                                                                                                                                                |\n\n- **DetInferencer.\\_\\_call\\_\\_()**\n\n| Arguments            | Type                      | Default      | Description                                                                                                                                                                                                                                                    |\n| -------------------- | ------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `inputs`             | str/list/tuple/np.array   | **required** | It can be a path to an image/a folder, an np array or a list/tuple (with img paths or np arrays)                                                                                                                                                               |\n| `batch_size`         | int                       | 1            | Inference batch size.                                                                                                                                                                                                                                          |\n| `print_result`       | bool                      | False        | Whether to print the inference result to the console.                                                                                                                                                                                                          |\n| `show`               | bool                      | False        | Whether to display the visualization results in a popup window.                                                                                                                                                                                                |\n| `wait_time`          | float                     | 0            | The interval of show(s).                                                                                                                                                                                                                                       |\n| `no_save_vis`        | bool                      | False        | Whether to force not to save prediction vis results.                                                                                                                                                                                                           |\n| `draw_pred`          | bool                      | True         | Whether to draw predicted bounding boxes.                                                                                                                                                                                                                      |\n| `pred_score_thr`     | float                     | 0.3          | Minimum score of bboxes to draw.                                                                                                                                                                                                                               |\n| `return_datasamples` | bool                      | False        | Whether to return results as DataSamples. If False, the results will be packed into a dict.                                                                                                                                                                    |\n| `print_result`       | bool                      | False        | Whether to print the inference result to the console.                                                                                                                                                                                                          |\n| `no_save_pred`       | bool                      | True         | Whether to force not to save prediction results.                                                                                                                                                                                                               |\n| `out_dir`            | str                       | ''           | Output directory of results.                                                                                                                                                                                                                                   |\n| `texts`              | str/list\\[str\\], optional | None         | Text prompts.                                                                                                                                                                                                                                                  |\n| `stuff_texts`        | str/list\\[str\\], optional | None         | Stuff text prompts of open panoptic task.                                                                                                                                                                                                                      |\n| `custom_entities`    | bool                      | False        | Whether to use custom entities. Only used in GLIP.                                                                                                                                                                                                             |\n| \\*\\*kwargs           |                           |              | Other keyword arguments passed to :meth:`preprocess`, :meth:`forward`, :meth:`visualize` and :meth:`postprocess`. Each key in kwargs should be in the corresponding set of `preprocess_kwargs`, `forward_kwargs`, `visualize_kwargs` and `postprocess_kwargs`. |\n\n## Demos\n\nWe also provide four demo scripts, implemented with high-level APIs and supporting functionality codes.\nSource codes are available [here](https://github.com/open-mmlab/mmdetection/blob/main/demo).\n\n### Image demo\n\nThis script performs inference on a single image.\n\n```shell\npython demo/image_demo.py \\\n    ${IMAGE_FILE} \\\n    ${CONFIG_FILE} \\\n    [--weights ${WEIGHTS}] \\\n    [--device ${GPU_ID}] \\\n    [--pred-score-thr ${SCORE_THR}]\n```\n\nExamples:\n\n```shell\npython demo/image_demo.py demo/demo.jpg \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    --weights checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n    --device cpu\n```\n\n### Webcam demo\n\nThis is a live demo from a webcam.\n\n```shell\npython demo/webcam_demo.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--device ${GPU_ID}] \\\n    [--camera-id ${CAMERA-ID}] \\\n    [--score-thr ${SCORE_THR}]\n```\n\nExamples:\n\n```shell\npython demo/webcam_demo.py \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth\n```\n\n### Video demo\n\nThis script performs inference on a video.\n\n```shell\npython demo/video_demo.py \\\n    ${VIDEO_FILE} \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--device ${GPU_ID}] \\\n    [--score-thr ${SCORE_THR}] \\\n    [--out ${OUT_FILE}] \\\n    [--show] \\\n    [--wait-time ${WAIT_TIME}]\n```\n\nExamples:\n\n```shell\npython demo/video_demo.py demo/demo.mp4 \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n    --out result.mp4\n```\n\n#### Video demo with GPU acceleration\n\nThis script performs inference on a video with GPU acceleration.\n\n```shell\npython demo/video_gpuaccel_demo.py \\\n    ${VIDEO_FILE} \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--device ${GPU_ID}] \\\n    [--score-thr ${SCORE_THR}] \\\n    [--nvdecode] \\\n    [--out ${OUT_FILE}] \\\n    [--show] \\\n    [--wait-time ${WAIT_TIME}]\n```\n\nExamples:\n\n```shell\npython demo/video_gpuaccel_demo.py demo/demo.mp4 \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n    --nvdecode --out result.mp4\n```\n\n### Large-image inference demo\n\nThis is a script for slicing inference on large images.\n\n```\npython demo/large_image_demo.py \\\n\t${IMG_PATH} \\\n\t${CONFIG_FILE} \\\n\t${CHECKPOINT_FILE} \\\n\t--device ${GPU_ID}  \\\n\t--show \\\n\t--tta  \\\n\t--score-thr ${SCORE_THR} \\\n\t--patch-size ${PATCH_SIZE} \\\n\t--patch-overlap-ratio ${PATCH_OVERLAP_RATIO} \\\n\t--merge-iou-thr ${MERGE_IOU_THR} \\\n\t--merge-nms-type ${MERGE_NMS_TYPE} \\\n\t--batch-size ${BATCH_SIZE} \\\n\t--debug \\\n\t--save-patch\n```\n\nExamples:\n\n```shell\n# inferecnce without tta\nwget -P checkpoint https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_2x_coco/faster_rcnn_r101_fpn_2x_coco_bbox_mAP-0.398_20200504_210455-1d2dac9c.pth\n\npython demo/large_image_demo.py \\\n    demo/large_image.jpg \\\n    configs/faster_rcnn/faster-rcnn_r101_fpn_2x_coco.py \\\n    checkpoint/faster_rcnn_r101_fpn_2x_coco_bbox_mAP-0.398_20200504_210455-1d2dac9c.pth\n\n# inference with tta\nwget -P checkpoint https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth\n\npython demo/large_image_demo.py \\\n    demo/large_image.jpg \\\n    configs/retinanet/retinanet_r50_fpn_1x_coco.py \\\n    checkpoint/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth --tta\n\n```\n\n## Multi-modal algorithm inference demo and evaluation\n\nAs multimodal vision algorithms continue to evolve, MMDetection has also supported such algorithms. This section demonstrates how to use the demo and eval scripts corresponding to multimodal algorithms using the GLIP algorithm and model as the example. Moreover, MMDetection integrated a [gradio_demo project](../../../projects/gradio_demo/), which allows developers to quickly play with all image input tasks in MMDetection on their local devices. Check the [document](../../../projects/gradio_demo/README.md) for more details.\n\n### Preparation\n\nPlease first make sure that you have the correct dependencies installed:\n\n```shell\n# if source\npip install -r requirements/multimodal.txt\n\n# if wheel\nmim install mmdet[multimodal]\n```\n\nMMDetection has already implemented GLIP algorithms and provided the weights, you can download directly from urls:\n\n```shell\ncd mmdetection\nwget https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth\n```\n\n### Inference\n\nOnce the model is successfully downloaded, you can use the `demo/image_demo.py` script to run the inference.\n\n```shell\npython demo/image_demo.py demo/demo.jpg glip_tiny_a_mmdet-b3654169.pth --texts bench\n```\n\nDemo result will be similar to this:\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/234547841-266476c8-f987-4832-8642-34357be621c6.png\" height=\"300\"/>\n</div>\n\nIf users would like to detect multiple targets, please declare them in the format of `xx. xx` after the `--texts`.\n\n```shell\npython demo/image_demo.py demo/demo.jpg glip_tiny_a_mmdet-b3654169.pth --texts 'bench. car'\n```\n\nAnd the result will be like this one:\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/234548156-ef9bbc2e-7605-4867-abe6-048b8578893d.png\" height=\"300\"/>\n</div>\n\nYou can also use a sentence as the input prompt for the `--texts` field, for example:\n\n```shell\npython demo/image_demo.py demo/demo.jpg glip_tiny_a_mmdet-b3654169.pth --texts 'There are a lot of cars here.'\n```\n\nThe result will be similar to this:\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/234548490-d2e0a16d-1aad-4708-aea0-c829634fabbd.png\" height=\"300\"/>\n</div>\n\n### Evaluation\n\nThe GLIP implementation in MMDetection does not have any performance degradation, our benchmark is as follows:\n\n| Model                   | official mAP | mmdet mAP |\n| ----------------------- | :----------: | :-------: |\n| glip_A_Swin_T_O365.yaml |     42.9     |   43.0    |\n| glip_Swin_T_O365.yaml   |     44.9     |   44.9    |\n| glip_Swin_L.yaml        |     51.4     |   51.3    |\n\nUsers can use the test script we provided to run evaluation as well. Here is a basic example:\n\n```shell\n# 1 gpu\npython tools/test.py configs/glip/glip_atss_swin-t_fpn_dyhead_pretrain_obj365.py glip_tiny_a_mmdet-b3654169.pth\n\n# 8 GPU\n./tools/dist_test.sh configs/glip/glip_atss_swin-t_fpn_dyhead_pretrain_obj365.py glip_tiny_a_mmdet-b3654169.pth 8\n```\n"
  },
  {
    "path": "docs/en/user_guides/init_cfg.md",
    "content": "# Weight initialization\n\nDuring training, a proper initialization strategy is beneficial to speeding up the training or obtaining a higher performance. [MMCV](https://github.com/open-mmlab/mmcv/blob/master/mmcv/cnn/utils/weight_init.py) provide some commonly used methods for initializing modules like `nn.Conv2d`. Model initialization in MMdetection mainly uses `init_cfg`. Users can initialize models with following two steps:\n\n1. Define `init_cfg` for a model or its components in `model_cfg`,  but `init_cfg` of children components have higher priority and will override `init_cfg` of parents modules.\n2. Build model as usual, but call `model.init_weights()` method explicitly, and model parameters will be initialized as configuration.\n\nThe high-level workflow of initialization in MMdetection is :\n\nmodel_cfg(init_cfg) -> build_from_cfg -> model -> init_weight() -> initialize(self, self.init_cfg) -> children's init_weight()\n\n### Description\n\nIt is dict or list\\[dict\\], and contains the following keys and values:\n\n- `type` (str), containing the initializer name in `INTIALIZERS`, and followed by arguments of the initializer.\n- `layer` (str or list\\[str\\]), containing the names of basic layers in Pytorch or MMCV with learnable parameters that will be initialized, e.g. `'Conv2d'`,`'DeformConv2d'`.\n- `override` (dict or list\\[dict\\]),  containing the sub-modules that not inherit from BaseModule and whose initialization configuration is different from other layers' which are in `'layer'` key. Initializer defined in `type` will work for all layers defined in `layer`, so if sub-modules are not derived Classes of `BaseModule` but can be initialized as same ways of layers in `layer`, it does not need to use `override`. `override` contains:\n  - `type` followed by arguments of initializer;\n  - `name` to indicate sub-module which will be initialized.\n\n### Initialize parameters\n\nInherit a new model from `mmcv.runner.BaseModule` or `mmdet.models`  Here we show an example of FooModel.\n\n```python\nimport torch.nn as nn\nfrom mmcv.runner import BaseModule\n\nclass FooModel(BaseModule)\n\tdef __init__(self,\n                 arg1,\n                 arg2,\n                 init_cfg=None):\n    \tsuper(FooModel, self).__init__(init_cfg)\n\t\t...\n```\n\n- Initialize model by using `init_cfg` directly in code\n\n  ```python\n  import torch.nn as nn\n  from mmcv.runner import BaseModule\n  # or directly inherit mmdet models\n\n  class FooModel(BaseModule)\n  \tdef __init__(self,\n                  arg1,\n                  arg2,\n                  init_cfg=XXX):\n  \t\tsuper(FooModel, self).__init__(init_cfg)\n  \t    ...\n  ```\n\n- Initialize model by using `init_cfg` directly in `mmcv.Sequential` or `mmcv.ModuleList` code\n\n  ```python\n  from mmcv.runner import BaseModule, ModuleList\n\n  class FooModel(BaseModule)\n  \tdef __init__(self,\n              \targ1,\n              \targ2,\n              \tinit_cfg=None):\n  \t\tsuper(FooModel, self).__init__(init_cfg)\n      \t...\n      \tself.conv1 = ModuleList(init_cfg=XXX)\n  ```\n\n- Initialize model by using `init_cfg` in config file\n\n  ```python\n  model = dict(\n  \t...\n  \tmodel = dict(\n      \ttype='FooModel',\n      \targ1=XXX,\n      \targ2=XXX,\n      \tinit_cfg=XXX),\n          ...\n  ```\n\n### Usage of init_cfg\n\n1. Initialize model by `layer` key\n\n   If we only define `layer`, it just initialize the layer in `layer` key.\n\n   NOTE: Value of `layer` key is the class name with attributes weights and bias of Pytorch, (so such as  `MultiheadAttention layer` is not supported).\n\n- Define `layer` key for initializing module with same configuration.\n\n  ```python\n  init_cfg = dict(type='Constant', layer=['Conv1d', 'Conv2d', 'Linear'], val=1)\n  # initialize whole module with same configuration\n  ```\n\n- Define `layer` key for initializing layer with different configurations.\n\n```python\ninit_cfg = [dict(type='Constant', layer='Conv1d', val=1),\n            dict(type='Constant', layer='Conv2d', val=2),\n            dict(type='Constant', layer='Linear', val=3)]\n# nn.Conv1d will be initialized with dict(type='Constant', val=1)\n# nn.Conv2d will be initialized with dict(type='Constant', val=2)\n# nn.Linear will be initialized with dict(type='Constant', val=3)\n```\n\n2. Initialize model by `override` key\n\n- When initializing some specific part with its attribute name, we can use `override` key, and the value in `override` will ignore the value in init_cfg.\n\n  ```python\n  # layers:\n  # self.feat = nn.Conv1d(3, 1, 3)\n  # self.reg = nn.Conv2d(3, 3, 3)\n  # self.cls = nn.Linear(1,2)\n\n  init_cfg = dict(type='Constant',\n                  layer=['Conv1d','Conv2d'], val=1, bias=2,\n                  override=dict(type='Constant', name='reg', val=3, bias=4))\n  # self.feat and self.cls will be initialized with \tdict(type='Constant', val=1, bias=2)\n  # The module called 'reg' will be initialized with dict(type='Constant', val=3, bias=4)\n  ```\n\n- If `layer` is None in init_cfg, only sub-module with the name in override will be initialized, and type and other args in override can be omitted.\n\n  ```python\n  # layers:\n  # self.feat = nn.Conv1d(3, 1, 3)\n  # self.reg = nn.Conv2d(3, 3, 3)\n  # self.cls = nn.Linear(1,2)\n\n  init_cfg = dict(type='Constant', val=1, bias=2, \toverride=dict(name='reg'))\n\n  # self.feat and self.cls will be initialized by Pytorch\n  # The module called 'reg' will be initialized with dict(type='Constant', val=1, bias=2)\n  ```\n\n- If we don't define `layer` key or `override` key, it will not initialize anything.\n\n- Invalid usage\n\n  ```python\n  # It is invalid that override don't have name key\n  init_cfg = dict(type='Constant', layer=['Conv1d','Conv2d'], val=1, bias=2,\n              \toverride=dict(type='Constant', val=3, bias=4))\n\n  # It is also invalid that override has name and other args except type\n  init_cfg = dict(type='Constant', layer=['Conv1d','Conv2d'], val=1, bias=2,\n                  override=dict(name='reg', val=3, bias=4))\n  ```\n\n3. Initialize model with the pretrained model\n\n   ```python\n   init_cfg = dict(type='Pretrained',\n               checkpoint='torchvision://resnet50')\n   ```\n\nMore details can refer to the documentation in [MMEngine](https://mmengine.readthedocs.io/en/latest/advanced_tutorials/initialize.html)\n"
  },
  {
    "path": "docs/en/user_guides/label_studio.md",
    "content": "# Semi-automatic Object Detection Annotation with MMDetection and Label-Studio\n\nAnnotation data is a time-consuming and laborious task. This article introduces how to perform semi-automatic annotation using the RTMDet algorithm in MMDetection in conjunction with Label-Studio software. Specifically, using RTMDet to predict image annotations and then refining the annotations with Label-Studio. Community users can refer to this process and methodology and apply it to other fields.\n\n- RTMDet: RTMDet is a high-precision single-stage object detection algorithm developed by OpenMMLab, open-sourced in the MMDetection object detection toolbox. Its open-source license is Apache 2.0, and it can be used freely without restrictions by industrial users.\n\n- [Label Studio](https://github.com/heartexlabs/label-studio) is an excellent annotation software covering the functionality of dataset annotation in areas such as image classification, object detection, and segmentation.\n\nIn this article, we will use [cat](https://download.openmmlab.com/mmyolo/data/cat_dataset.zip) images for semi-automatic annotation.\n\n## Environment Configuration\n\nTo begin with, you need to create a virtual environment and then install PyTorch and MMCV. In this article, we will specify the versions of PyTorch and MMCV. Next, you can install MMDetection, Label-Studio, and label-studio-ml-backend using the following steps:\n\nCreate a virtual environment:\n\n```shell\nconda create -n rtmdet python=3.9 -y\nconda activate rtmdet\n```\n\nInstall PyTorch:\n\n```shell\n# Linux and Windows CPU only\npip install torch==1.10.1+cpu torchvision==0.11.2+cpu torchaudio==0.10.1 -f https://download.pytorch.org/whl/cpu/torch_stable.html\n# Linux and Windows CUDA 11.3\npip install torch==1.10.1+cu113 torchvision==0.11.2+cu113 torchaudio==0.10.1 -f https://download.pytorch.org/whl/cu113/torch_stable.html\n# OSX\npip install torch==1.10.1 torchvision==0.11.2 torchaudio==0.10.1\n```\n\nInstall MMCV:\n\n```shell\npip install -U openmim\nmim install \"mmcv>=2.0.0\"\n# Installing mmcv will automatically install mmengine\n```\n\nInstall MMDetection:\n\n```shell\ngit clone https://github.com/open-mmlab/mmdetection\ncd mmdetection\npip install -v -e .\n```\n\nInstall Label-Studio and label-studio-ml-backend:\n\n```shell\n# Installing Label-Studio may take some time, if the version is not found, please use the official source\npip install label-studio==1.7.2\npip install label-studio-ml==1.0.9\n```\n\nDownload the rtmdet weights:\n\n```shell\ncd path/to/mmetection\nmkdir work_dirs\ncd work_dirs\nwget https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_m_8xb32-300e_coco/rtmdet_m_8xb32-300e_coco_20220719_112220-229f527c.pth\n```\n\n## Start the Service\n\nStart the RTMDet backend inference service:\n\n```shell\ncd path/to/mmetection\n\nlabel-studio-ml start projects/LabelStudio/backend_template --with \\\nconfig_file=configs/rtmdet/rtmdet_m_8xb32-300e_coco.py \\\ncheckpoint_file=./work_dirs/rtmdet_m_8xb32-300e_coco_20220719_112220-229f527c.pth \\\ndevice=cpu \\\n--port 8003\n# Set device=cpu to use CPU inference, and replace cpu with cuda:0 to use GPU inference.\n```\n\n![](https://cdn.vansin.top/picgo20230330131601.png)\n\nThe RTMDet backend inference service has now been started. To configure it in the Label-Studio web system, use http://localhost:8003 as the backend inference service.\n\nNow, start the Label-Studio web service:\n\n```shell\nlabel-studio start\n```\n\n![](https://cdn.vansin.top/picgo20230330132913.png)\n\nOpen your web browser and go to http://localhost:8080/ to see the Label-Studio interface.\n\n![](https://cdn.vansin.top/picgo20230330133118.png)\n\nRegister a user and then create an RTMDet-Semiautomatic-Label project.\n\n![](https://cdn.vansin.top/picgo20230330133333.png)\n\nDownload the example cat images by running the following command and import them using the Data Import button:\n\n```shell\ncd path/to/mmetection\nmkdir data && cd data\n\nwget https://download.openmmlab.com/mmyolo/data/cat_dataset.zip && unzip cat_dataset.zip\n```\n\n![](https://cdn.vansin.top/picgo20230330133628.png)\n\n![](https://cdn.vansin.top/picgo20230330133715.png)\n\nThen, select the Object Detection With Bounding Boxes template.\n\n![](https://cdn.vansin.top/picgo20230330133807.png)\n\n```shell\nairplane\napple\nbackpack\nbanana\nbaseball_bat\nbaseball_glove\nbear\nbed\nbench\nbicycle\nbird\nboat\nbook\nbottle\nbowl\nbroccoli\nbus\ncake\ncar\ncarrot\ncat\ncell_phone\nchair\nclock\ncouch\ncow\ncup\ndining_table\ndog\ndonut\nelephant\nfire_hydrant\nfork\nfrisbee\ngiraffe\nhair_drier\nhandbag\nhorse\nhot_dog\nkeyboard\nkite\nknife\nlaptop\nmicrowave\nmotorcycle\nmouse\norange\noven\nparking_meter\nperson\npizza\npotted_plant\nrefrigerator\nremote\nsandwich\nscissors\nsheep\nsink\nskateboard\nskis\nsnowboard\nspoon\nsports_ball\nstop_sign\nsuitcase\nsurfboard\nteddy_bear\ntennis_racket\ntie\ntoaster\ntoilet\ntoothbrush\ntraffic_light\ntrain\ntruck\ntv\numbrella\nvase\nwine_glass\nzebra\n```\n\nThen, copy and add the above categories to Label-Studio and click Save.\n\n![](https://cdn.vansin.top/picgo20230330134027.png)\n\nIn the Settings, click Add Model to add the RTMDet backend inference service.\n\n![](https://cdn.vansin.top/picgo20230330134320.png)\n\nClick Validate and Save, and then click Start Labeling.\n\n![](https://cdn.vansin.top/picgo20230330134424.png)\n\nIf you see Connected as shown below, the backend inference service has been successfully added.\n\n![](https://cdn.vansin.top/picgo20230330134554.png)\n\n## Start Semi-Automatic Labeling\n\nClick on Label to start labeling.\n\n![](https://cdn.vansin.top/picgo20230330134804.png)\n\nWe can see that the RTMDet backend inference service has successfully returned the predicted results and displayed them on the image. However, we noticed that the predicted bounding boxes for the cats are a bit too large and not very accurate.\n\n![](https://cdn.vansin.top/picgo20230403104419.png)\n\nWe manually adjust the position of the cat bounding box, and then click Submit to complete the annotation of this image.\n\n![](https://cdn.vansin.top/picgo/20230403105923.png)\n\nAfter submitting all images, click export to export the labeled dataset in COCO format.\n\n![](https://cdn.vansin.top/picgo20230330135921.png)\n\nUse VS Code to open the unzipped folder to see the labeled dataset, which includes the images and the annotation files in JSON format.\n\n![](https://cdn.vansin.top/picgo20230330140321.png)\n\nAt this point, the semi-automatic labeling is complete. We can use this dataset to train a more accurate model in MMDetection and then continue semi-automatic labeling on newly collected images with this model. This way, we can iteratively expand the high-quality dataset and improve the accuracy of the model.\n\n## Use MMYOLO as the Backend Inference Service\n\nIf you want to use Label-Studio in MMYOLO, you can refer to replacing the config_file and checkpoint_file with the configuration file and weight file of MMYOLO when starting the backend inference service.\n\n```shell\ncd path/to/mmetection\n\nlabel-studio-ml start projects/LabelStudio/backend_template --with \\\nconfig_file= path/to/mmyolo_config.py \\\ncheckpoint_file= path/to/mmyolo_weights.pth \\\ndevice=cpu \\\n--port 8003\n# device=cpu is for using CPU inference. If using GPU inference, replace cpu with cuda:0.\n```\n\nRotation object detection and instance segmentation are still under development, please stay tuned.\n"
  },
  {
    "path": "docs/en/user_guides/new_model.md",
    "content": "# Train with customized models and standard datasets\n\nIn this note, you will know how to train, test and inference your own customized models under standard datasets. We use the cityscapes dataset to train a customized Cascade Mask R-CNN R50 model as an example to demonstrate the whole process, which using [`AugFPN`](https://github.com/Gus-Guo/AugFPN) to replace the default `FPN` as neck, and add `Rotate` or `TranslateX` as training-time auto augmentation.\n\nThe basic steps are as below:\n\n1. Prepare the standard dataset\n2. Prepare your own customized model\n3. Prepare a config\n4. Train, test, and inference models on the standard dataset.\n\n## Prepare the standard dataset\n\nIn this note, as we use the standard cityscapes dataset as an example.\n\nIt is recommended to symlink the dataset root to `$MMDETECTION/data`.\nIf your folder structure is different, you may need to change the corresponding paths in config files.\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n│   ├── VOCdevkit\n│   │   ├── VOC2007\n│   │   ├── VOC2012\n\n```\n\nOr you can set your dataset root through\n\n```bash\nexport MMDET_DATASETS=$data_root\n```\n\nWe will replace dataset root with `$MMDET_DATASETS`, so you don't have to modify the corresponding path in config files.\n\nThe cityscapes annotations have to be converted into the coco format using `tools/dataset_converters/cityscapes.py`:\n\n```shell\npip install cityscapesscripts\npython tools/dataset_converters/cityscapes.py ./data/cityscapes --nproc 8 --out-dir ./data/cityscapes/annotations\n```\n\nCurrently, the config files in `cityscapes` use COCO pre-trained weights to initialize.\nYou could download the pre-trained models in advance if the network is unavailable or slow, otherwise, it would cause errors at the beginning of training.\n\n## Prepare your own customized model\n\nThe second step is to use your own module or training setting. Assume that we want to implement a new neck called `AugFPN` to replace with the default `FPN` under the existing detector Cascade Mask R-CNN R50. The following implements `AugFPN` under MMDetection.\n\n### 1. Define a new neck (e.g. AugFPN)\n\nFirstly create a new file `mmdet/models/necks/augfpn.py`.\n\n```python\nimport torch.nn as nn\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass AugFPN(nn.Module):\n\n    def __init__(self,\n                in_channels,\n                out_channels,\n                num_outs,\n                start_level=0,\n                end_level=-1,\n                add_extra_convs=False):\n        pass\n\n    def forward(self, inputs):\n        # implementation is ignored\n        pass\n```\n\n### 2. Import the module\n\nYou can either add the following line to `mmdet/models/necks/__init__.py`,\n\n```python\nfrom .augfpn import AugFPN\n```\n\nor alternatively add\n\n```python\ncustom_imports = dict(\n    imports=['mmdet.models.necks.augfpn'],\n    allow_failed_imports=False)\n```\n\nto the config file and avoid modifying the original code.\n\n### 3. Modify the config file\n\n```python\nneck=dict(\n    type='AugFPN',\n    in_channels=[256, 512, 1024, 2048],\n    out_channels=256,\n    num_outs=5)\n```\n\nFor more detailed usages about customizing your own models (e.g. implement a new backbone, head, loss, etc) and runtime training settings (e.g. define a new optimizer, use gradient clip, customize training schedules and hooks, etc), please refer to the guideline [Customize Models](../advanced_guides/customize_models.md) and [Customize Runtime Settings](../advanced_guides/customize_runtime.md) respectively.\n\n## Prepare a config\n\nThe third step is to prepare a config for your own training setting. Assume that we want to add `AugFPN` and `Rotate` or `Translate` augmentation to existing Cascade Mask R-CNN R50 to train the cityscapes dataset, and assume the config is under directory `configs/cityscapes/` and named as `cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py`, the config is as below.\n\n```python\n# The new config inherits the base configs to highlight the necessary modification\n_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/cityscapes_instance.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    # set None to avoid loading ImageNet pre-trained backbone,\n    # instead here we set `load_from` to load from COCO pre-trained detectors.\n    backbone=dict(init_cfg=None),\n    # replace neck from defaultly `FPN` to our new implemented module `AugFPN`\n    neck=dict(\n        type='AugFPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    # We also need to change the num_classes in head from 80 to 8, to match the\n    # cityscapes dataset's annotation. This modification involves `bbox_head` and `mask_head`.\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                # change the number of classes from defaultly COCO to cityscapes\n                num_classes=8,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                # change the number of classes from defaultly COCO to cityscapes\n                num_classes=8,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                # change the number of classes from defaultly COCO to cityscapes\n                num_classes=8,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            # change the number of classes from default COCO to cityscapes\n            num_classes=8,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))))\n\n# over-write `train_pipeline` for new added `AutoAugment` training setting\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='AutoAugment',\n        policies=[\n            [dict(\n                 type='Rotate',\n                 level=5,\n                 img_border_value=(124, 116, 104),\n                 prob=0.5)\n            ],\n            [dict(type='Rotate', level=7, img_border_value=(124, 116, 104)),\n             dict(\n                 type='TranslateX',\n                 level=5,\n                 prob=0.5,\n                 img_border_value=(124, 116, 104))\n            ],\n        ]),\n    dict(\n        type='RandomResize',\n        scale=[(2048, 800), (2048, 1024)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\n\n# set batch_size per gpu, and set new training pipeline\ntrain_dataloader = dict(\n    batch_size=1,\n    num_workers=3,\n    # over-write `pipeline` with new training pipeline setting\n    dataset=dict(pipeline=train_pipeline))\n\n# Set optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# Set customized learning policy\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=10,\n        by_epoch=True,\n        milestones=[8],\n        gamma=0.1)\n]\n\n# train, val, test loop config\ntrain_cfg = dict(max_epochs=10, val_interval=1)\n\n# We can use the COCO pre-trained Cascade Mask R-CNN R50 model for a more stable performance initialization\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco/cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth'\n```\n\n## Train a new model\n\nTo train a model with the new config, you can simply run\n\n```shell\npython tools/train.py configs/cityscapes/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py\n```\n\nFor more detailed usages, please refer to the [training guide](train.md).\n\n## Test and inference\n\nTo test the trained model, you can simply run\n\n```shell\npython tools/test.py configs/cityscapes/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py work_dirs/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes/epoch_10.pth\n```\n\nFor more detailed usages, please refer to the [testing guide](test.md).\n"
  },
  {
    "path": "docs/en/user_guides/robustness_benchmarking.md",
    "content": "# Corruption Benchmarking\n\n## Introduction\n\nWe provide tools to test object detection and instance segmentation models on the image corruption benchmark defined in [Benchmarking Robustness in Object Detection: Autonomous Driving when Winter is Coming](https://arxiv.org/abs/1907.07484).\nThis page provides basic tutorials how to use the benchmark.\n\n```latex\n@article{michaelis2019winter,\n  title={Benchmarking Robustness in Object Detection:\n    Autonomous Driving when Winter is Coming},\n  author={Michaelis, Claudio and Mitzkus, Benjamin and\n    Geirhos, Robert and Rusak, Evgenia and\n    Bringmann, Oliver and Ecker, Alexander S. and\n    Bethge, Matthias and Brendel, Wieland},\n  journal={arXiv:1907.07484},\n  year={2019}\n}\n```\n\n![image corruption example](../../../resources/corruptions_sev_3.png)\n\n## About the benchmark\n\nTo submit results to the benchmark please visit the [benchmark homepage](https://github.com/bethgelab/robust-detection-benchmark)\n\nThe benchmark is modelled after the [imagenet-c benchmark](https://github.com/hendrycks/robustness) which was originally\npublished in [Benchmarking Neural Network Robustness to Common Corruptions and Perturbations](https://arxiv.org/abs/1903.12261) (ICLR 2019) by Dan Hendrycks and Thomas Dietterich.\n\nThe image corruption functions are included in this library but can be installed separately using:\n\n```shell\npip install imagecorruptions\n```\n\nCompared to imagenet-c a few changes had to be made to handle images of arbitrary size and greyscale images.\nWe also modified the 'motion blur' and 'snow' corruptions to remove dependency from a linux specific library,\nwhich would have to be installed separately otherwise. For details please refer to the [imagecorruptions repository](https://github.com/bethgelab/imagecorruptions).\n\n## Inference with pretrained models\n\nWe provide a testing script to evaluate a models performance on any combination of the corruptions provided in the benchmark.\n\n### Test a dataset\n\n- [x] single GPU testing\n- [ ] multiple GPU testing\n- [ ] visualize detection results\n\nYou can use the following commands to test a models performance under the 15 corruptions used in the benchmark.\n\n```shell\n# single-gpu testing\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}]\n```\n\nAlternatively different group of corruptions can be selected.\n\n```shell\n# noise\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] --corruptions noise\n\n# blur\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] --corruptions blur\n\n# wetaher\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] --corruptions weather\n\n# digital\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] --corruptions digital\n```\n\nOr a costom set of corruptions e.g.:\n\n```shell\n# gaussian noise, zoom blur and snow\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --corruptions gaussian_noise zoom_blur snow\n```\n\nFinally the corruption severities to evaluate can be chosen.\nSeverity 0 corresponds to clean data and the effect increases from 1 to 5.\n\n```shell\n# severity 1\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --severities 1\n\n# severities 0,2,4\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --severities 0 2 4\n```\n\n## Results for modelzoo models\n\nThe results on COCO 2017val are shown in the below table.\n\n|        Model        |      Backbone       |  Style  | Lr schd | box AP clean | box AP corr. | box % | mask AP clean | mask AP corr. | mask % |\n| :-----------------: | :-----------------: | :-----: | :-----: | :----------: | :----------: | :---: | :-----------: | :-----------: | :----: |\n|    Faster R-CNN     |      R-50-FPN       | pytorch |   1x    |     36.3     |     18.2     | 50.2  |       -       |       -       |   -    |\n|    Faster R-CNN     |      R-101-FPN      | pytorch |   1x    |     38.5     |     20.9     | 54.2  |       -       |       -       |   -    |\n|    Faster R-CNN     |   X-101-32x4d-FPN   | pytorch |   1x    |     40.1     |     22.3     | 55.5  |       -       |       -       |   -    |\n|    Faster R-CNN     |   X-101-64x4d-FPN   | pytorch |   1x    |     41.3     |     23.4     | 56.6  |       -       |       -       |   -    |\n|    Faster R-CNN     |    R-50-FPN-DCN     | pytorch |   1x    |     40.0     |     22.4     | 56.1  |       -       |       -       |   -    |\n|    Faster R-CNN     | X-101-32x4d-FPN-DCN | pytorch |   1x    |     43.4     |     26.7     | 61.6  |       -       |       -       |   -    |\n|     Mask R-CNN      |      R-50-FPN       | pytorch |   1x    |     37.3     |     18.7     | 50.1  |     34.2      |     16.8      |  49.1  |\n|     Mask R-CNN      |    R-50-FPN-DCN     | pytorch |   1x    |     41.1     |     23.3     | 56.7  |     37.2      |     20.7      |  55.7  |\n|    Cascade R-CNN    |      R-50-FPN       | pytorch |   1x    |     40.4     |     20.1     | 49.7  |       -       |       -       |   -    |\n| Cascade Mask R-CNN  |      R-50-FPN       | pytorch |   1x    |     41.2     |     20.7     | 50.2  |     35.7      |     17.6      |  49.3  |\n|      RetinaNet      |      R-50-FPN       | pytorch |   1x    |     35.6     |     17.8     | 50.1  |       -       |       -       |   -    |\n| Hybrid Task Cascade | X-101-64x4d-FPN-DCN | pytorch |   1x    |     50.6     |     32.7     | 64.7  |     43.8      |     28.1      |  64.0  |\n\nResults may vary slightly due to the stochastic application of the corruptions.\n"
  },
  {
    "path": "docs/en/user_guides/semi_det.md",
    "content": "# Semi-supervised Object Detection\n\nSemi-supervised object detection uses both labeled data and unlabeled data for training. It not only reduces the annotation burden for training high-performance object detectors but also further improves the object detector by using a large number of unlabeled data.\n\nA typical procedure to train a semi-supervised object detector is as below:\n\n- [Semi-supervised Object Detection](#semi-supervised-object-detection)\n  - [Prepare and split dataset](#prepare-and-split-dataset)\n  - [Configure multi-branch pipeline](#configure-multi-branch-pipeline)\n  - [Configure semi-supervised dataloader](#configure-semi-supervised-dataloader)\n  - [Configure semi-supervised model](#configure-semi-supervised-model)\n  - [Configure MeanTeacherHook](#configure-meanteacherhook)\n  - [Configure TeacherStudentValLoop](#configure-teacherstudentvalloop)\n\n## Prepare and split dataset\n\nWe provide a dataset download script, which downloads the coco2017 dataset by default and decompresses it automatically.\n\n```shell\npython tools/misc/download_dataset.py\n```\n\nThe decompressed dataset directory structure is as below:\n\n```plain\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── image_info_unlabeled2017.json\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── test2017\n│   │   ├── train2017\n│   │   ├── unlabeled2017\n│   │   ├── val2017\n```\n\nThere are two common experimental settings for semi-supervised object detection on the coco2017 dataset:\n\n(1) Split `train2017` according to a fixed percentage (1%, 2%, 5% and 10%) as a labeled dataset, and the rest of `train2017` as an unlabeled dataset. Because the different splits of `train2017` as labeled datasets will cause significant fluctuation on the accuracy of the semi-supervised detectors, five-fold cross-validation is used in practice to evaluate the algorithm. We provide the dataset split script:\n\n```shell\npython tools/misc/split_coco.py\n```\n\nBy default, the script will split `train2017` according to the labeled data ratio  1%, 2%, 5% and 10%, and each split will be randomly repeated 5 times for cross-validation. The generated semi-supervised annotation file name format is as below:\n\n- the name format of labeled dataset: `instances_train2017.{fold}@{percent}.json`\n- the name format of unlabeled dataset: `instances_train2017.{fold}@{percent}-unlabeled.json`\n\nHere, `fold` is used for cross-validation, and `percent` represents the ratio of labeled data. The directory structure of the divided dataset is as below:\n\n```plain\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── image_info_unlabeled2017.json\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── semi_anns\n│   │   │   ├── instances_train2017.1@1.json\n│   │   │   ├── instances_train2017.1@1-unlabeled.json\n│   │   │   ├── instances_train2017.1@2.json\n│   │   │   ├── instances_train2017.1@2-unlabeled.json\n│   │   │   ├── instances_train2017.1@5.json\n│   │   │   ├── instances_train2017.1@5-unlabeled.json\n│   │   │   ├── instances_train2017.1@10.json\n│   │   │   ├── instances_train2017.1@10-unlabeled.json\n│   │   │   ├── instances_train2017.2@1.json\n│   │   │   ├── instances_train2017.2@1-unlabeled.json\n│   │   ├── test2017\n│   │   ├── train2017\n│   │   ├── unlabeled2017\n│   │   ├── val2017\n```\n\n(2) Use `train2017` as the labeled dataset and `unlabeled2017` as the unlabeled dataset. Since `image_info_unlabeled2017.json` does not contain `categories` information, the `CocoDataset` cannot be initialized, so you need to write the `categories` of `instances_train2017.json` into `image_info_unlabeled2017.json` and save it as `instances_unlabeled2017.json`, the relevant script is as below:\n\n```python\nfrom mmengine.fileio import load, dump\n\nanns_train = load('instances_train2017.json')\nanns_unlabeled = load('image_info_unlabeled2017.json')\nanns_unlabeled['categories'] = anns_train['categories']\ndump(anns_unlabeled, 'instances_unlabeled2017.json')\n```\n\nThe processed dataset directory is as below:\n\n```plain\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── image_info_unlabeled2017.json\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_unlabeled2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── test2017\n│   │   ├── train2017\n│   │   ├── unlabeled2017\n│   │   ├── val2017\n```\n\n## Configure multi-branch pipeline\n\nThere are two main approaches to semi-supervised learning,\n[consistency regularization](https://research.nvidia.com/sites/default/files/publications/laine2017iclr_paper.pdf)\nand [pseudo label](https://www.researchgate.net/profile/Dong-Hyun-Lee/publication/280581078_Pseudo-Label_The_Simple_and_Efficient_Semi-Supervised_Learning_Method_for_Deep_Neural_Networks/links/55bc4ada08ae092e9660b776/Pseudo-Label-The-Simple-and-Efficient-Semi-Supervised-Learning-Method-for-Deep-Neural-Networks.pdf).\nConsistency regularization often requires some careful design, while pseudo label have a simpler form and are easier to extend to downstream tasks.\nWe adopt a teacher-student joint training semi-supervised object detection framework based on pseudo label, so labeled data and unlabeled data need to configure different data pipeline:\n\n(1) Pipeline for labeled data:\n\n```python\n# pipeline used to augment labeled data,\n# which will be sent to student model for supervised training.\nsup_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='RandAugment', aug_space=color_space, aug_num=1),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='MultiBranch', sup=dict(type='PackDetInputs'))\n]\n```\n\n(2) Pipeline for unlabeled data:\n\n```python\n# pipeline used to augment unlabeled data weakly,\n# which will be sent to teacher model for predicting pseudo instances.\nweak_pipeline = [\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction',\n                   'homography_matrix')),\n]\n\n# pipeline used to augment unlabeled data strongly,\n# which will be sent to student model for unsupervised training.\nstrong_pipeline = [\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomOrder',\n        transforms=[\n            dict(type='RandAugment', aug_space=color_space, aug_num=1),\n            dict(type='RandAugment', aug_space=geometric, aug_num=1),\n        ]),\n    dict(type='RandomErasing', n_patches=(1, 5), ratio=(0, 0.2)),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction',\n                   'homography_matrix')),\n]\n\n# pipeline used to augment unlabeled data into different views\nunsup_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadEmptyAnnotations'),\n    dict(\n        type='MultiBranch',\n        unsup_teacher=weak_pipeline,\n        unsup_student=strong_pipeline,\n    )\n]\n```\n\n## Configure semi-supervised dataloader\n\n(1) Build a semi-supervised dataset. Use `ConcatDataset` to concatenate labeled and unlabeled datasets.\n\n```python\nlabeled_dataset = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='annotations/instances_train2017.json',\n    data_prefix=dict(img='train2017/'),\n    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n    pipeline=sup_pipeline)\n\nunlabeled_dataset = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='annotations/instances_unlabeled2017.json',\n    data_prefix=dict(img='unlabeled2017/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=unsup_pipeline)\n\ntrain_dataloader = dict(\n    batch_size=batch_size,\n    num_workers=num_workers,\n    persistent_workers=True,\n    sampler=dict(\n        type='GroupMultiSourceSampler',\n        batch_size=batch_size,\n        source_ratio=[1, 4]),\n    dataset=dict(\n        type='ConcatDataset', datasets=[labeled_dataset, unlabeled_dataset]))\n```\n\n(2) Use multi-source dataset sampler. Use `GroupMultiSourceSampler` to sample data form batches from `labeled_dataset` and `labeled_dataset`, `source_ratio` controls the proportion of labeled data and unlabeled data in the batch. `GroupMultiSourceSampler` also ensures that the images in the same batch have similar aspect ratios. If you don't need to guarantee the aspect ratio of the images in the batch, you can use `MultiSourceSampler`. The sampling diagram of `GroupMultiSourceSampler` is as below:\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/186149261-8cf28e92-de5c-4c8c-96e1-13558b2e27f7.jpg\"/>\n</div>\n\n`sup=1000` indicates that the scale of the labeled dataset is 1000, `sup_h=200` indicates that the scale of the images with an aspect ratio greater than or equal to 1 in the labeled dataset is 200, and `sup_w=800` indicates that the scale of the images with an aspect ratio less than 1 in the labeled dataset is 800,\n`unsup=9000` indicates that the scale of the unlabeled dataset is 9000, `unsup_h=1800` indicates that the scale of the images with an aspect ratio greater than or equal to 1 in the unlabeled dataset is 1800, and `unsup_w=7200` indicates the scale of the images with an aspect ratio less than 1 in the unlabeled dataset is 7200.\n`GroupMultiSourceSampler` randomly selects a group according to the overall aspect ratio distribution of the images in the labeled dataset and the unlabeled dataset, and then sample data to form batches from the two datasets according to `source_ratio`, so labeled datasets and unlabeled datasets have different repetitions.\n\n## Configure semi-supervised model\n\nWe choose `Faster R-CNN` as `detector` for semi-supervised training. Take the semi-supervised object detection algorithm `SoftTeacher` as an example,\nthe model configuration can be inherited from `_base_/models/faster-rcnn_r50_fpn.py`, replacing the backbone network of the detector with `caffe` style.\nNote that unlike the supervised training configs, `Faster R-CNN` as `detector` is an attribute of `model`, not `model` .\nIn addition, `data_preprocessor` needs to be set to `MultiBranchDataPreprocessor`, which is used to pad and normalize images from different pipelines.\nFinally, parameters required for semi-supervised training and testing can be configured via `semi_train_cfg` and `semi_test_cfg`.\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/semi_coco_detection.py'\n]\n\ndetector = _base_.model\ndetector.data_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[103.530, 116.280, 123.675],\n    std=[1.0, 1.0, 1.0],\n    bgr_to_rgb=False,\n    pad_size_divisor=32)\ndetector.backbone = dict(\n    type='ResNet',\n    depth=50,\n    num_stages=4,\n    out_indices=(0, 1, 2, 3),\n    frozen_stages=1,\n    norm_cfg=dict(type='BN', requires_grad=False),\n    norm_eval=True,\n    style='caffe',\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint='open-mmlab://detectron2/resnet50_caffe'))\n\nmodel = dict(\n    _delete_=True,\n    type='SoftTeacher',\n    detector=detector,\n    data_preprocessor=dict(\n        type='MultiBranchDataPreprocessor',\n        data_preprocessor=detector.data_preprocessor),\n    semi_train_cfg=dict(\n        freeze_teacher=True,\n        sup_weight=1.0,\n        unsup_weight=4.0,\n        pseudo_label_initial_score_thr=0.5,\n        rpn_pseudo_thr=0.9,\n        cls_pseudo_thr=0.9,\n        reg_pseudo_thr=0.02,\n        jitter_times=10,\n        jitter_scale=0.06,\n        min_pseudo_bbox_wh=(1e-2, 1e-2)),\n    semi_test_cfg=dict(predict_on='teacher'))\n```\n\nIn addition, we also support semi-supervised training for other detection models, such as `RetinaNet` and `Cascade R-CNN`. Since `SoftTeacher` only supports `Faster R-CNN`, it needs to be replaced with `SemiBaseDetector`, example is as below:\n\n```python\n_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/semi_coco_detection.py'\n]\n\ndetector = _base_.model\n\nmodel = dict(\n    _delete_=True,\n    type='SemiBaseDetector',\n    detector=detector,\n    data_preprocessor=dict(\n        type='MultiBranchDataPreprocessor',\n        data_preprocessor=detector.data_preprocessor),\n    semi_train_cfg=dict(\n        freeze_teacher=True,\n        sup_weight=1.0,\n        unsup_weight=1.0,\n        cls_pseudo_thr=0.9,\n        min_pseudo_bbox_wh=(1e-2, 1e-2)),\n    semi_test_cfg=dict(predict_on='teacher'))\n```\n\nFollowing the semi-supervised training configuration of `SoftTeacher`, change `batch_size` to 2 and `source_ratio` to `[1, 1]`, the experimental results of supervised and semi-supervised training of `RetinaNet`, `Faster R-CNN`, `Cascade R-CNN` and `SoftTeacher` on the 10% coco `train2017` are as below:\n\n|      Model       |   Detector    | BackBone | Style | sup-0.1-coco mAP | semi-0.1-coco mAP |\n| :--------------: | :-----------: | :------: | :---: | :--------------: | :---------------: |\n| SemiBaseDetector |   RetinaNet   | R-50-FPN | caffe |       23.5       |       27.7        |\n| SemiBaseDetector | Faster R-CNN  | R-50-FPN | caffe |       26.7       |       28.4        |\n| SemiBaseDetector | Cascade R-CNN | R-50-FPN | caffe |       28.0       |       29.7        |\n|   SoftTeacher    | Faster R-CNN  | R-50-FPN | caffe |       26.7       |       31.1        |\n\n## Configure MeanTeacherHook\n\nUsually, the teacher model is updated by Exponential Moving Average (EMA) the student model, and then the teacher model is optimized with the optimization of the student model, which can be achieved by configuring `custom_hooks`:\n\n```python\ncustom_hooks = [dict(type='MeanTeacherHook')]\n```\n\n## Configure TeacherStudentValLoop\n\nSince there are two models in the teacher-student joint training framework, we can replace `ValLoop` with `TeacherStudentValLoop` to test the accuracy of both models during the training process.\n\n```python\nval_cfg = dict(type='TeacherStudentValLoop')\n```\n"
  },
  {
    "path": "docs/en/user_guides/single_stage_as_rpn.md",
    "content": "# Use a single stage detector as RPN\n\nRegion proposal network (RPN) is a submodule in [Faster R-CNN](https://arxiv.org/abs/1506.01497), which generates proposals for the second stage of Faster R-CNN. Most two-stage detectors in MMDetection use [`RPNHead`](../../../mmdet/models/dense_heads/rpn_head.py) to generate proposals as RPN. However, any single-stage detector can serve as an RPN since their bounding box predictions can also be regarded as region proposals and thus be refined in the R-CNN. Therefore, MMDetection v3.0 supports that.\n\nTo illustrate the whole process, here we give an example of how to use an anchor-free single-stage model [FCOS](../../../configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py) as an RPN in [Faster R-CNN](../../../configs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py).\n\nThe outline of this tutorial is as below:\n\n1. Use `FCOSHead` as an `RPNHead` in Faster R-CNN\n2. Evaluate proposals\n3. Train the customized Faster R-CNN with pre-trained FCOS\n\n## Use `FCOSHead` as an `RPNHead` in Faster R-CNN\n\nTo set `FCOSHead` as an `RPNHead` in Faster R-CNN, we should create a new config file named `configs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py`, and replace with the setting of `rpn_head` with the setting of `bbox_head` in `configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py`. Besides, we still use the neck setting of FCOS with strides of `[8, 16, 32, 64, 128]`, and update `featmap_strides` of `bbox_roi_extractor` to `[8, 16, 32, 64, 128]`. To avoid loss goes NAN, we apply warmup during the first 1000 iterations instead of the first 500 iterations, which means that the lr increases more slowly. The config is as follows:\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    # copied from configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py\n    neck=dict(\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        _delete_=True,  # ignore the unused old settings\n        type='FCOSHead',\n        num_classes=1,  # num_classes = 1 for rpn, if num_classes > 1, it will be set to 1 in TwoStageDetector automatically\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    roi_head=dict(  # update featmap_strides due to the strides in neck\n        bbox_roi_extractor=dict(featmap_strides=[8, 16, 32, 64, 128])))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),  # Slowly increase lr, otherwise loss becomes NAN\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n```\n\nThen, we could use the following command to train our customized model. For more training commands, please refer to [here](train.md).\n\n```python\n# training with 8 GPUS\nbash tools/dist_train.sh configs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py \\\n    8 \\\n    --work-dir ./work_dirs/faster-rcnn_r50_fpn_fcos-rpn_1x_coco\n```\n\n## Evaluate proposals\n\nThe quality of proposals is of great importance to the performance of detector, therefore, we also provide a way to evaluate proposals. Same as above, create a new config file named `configs/rpn/fcos-rpn_r50_fpn_1x_coco.py`, and replace with setting of `rpn_head` with the setting of `bbox_head` in `configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py`.\n\n```python\n_base_ = [\n    '../_base_/models/rpn_r50_fpn.py', '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nval_evaluator = dict(metric='proposal_fast')\ntest_evaluator = val_evaluator\n\nmodel = dict(\n    # copied from configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py\n    neck=dict(\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        _delete_=True,  # ignore the unused old settings\n        type='FCOSHead',\n        num_classes=1,  # num_classes = 1 for rpn, if num_classes > 1, it will be set to 1 in RPN automatically\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)))\n```\n\nSuppose we have the checkpoint `./work_dirs/faster-rcnn_r50_fpn_fcos-rpn_1x_coco/epoch_12.pth` after training, then we can evaluate the quality of proposals with the following command.\n\n```python\n# testing with 8 GPUs\nbash tools/dist_test.sh \\\n    configs/rpn/fcos-rpn_r50_fpn_1x_coco.py \\\n    ./work_dirs/faster-rcnn_r50_fpn_fcos-rpn_1x_coco/epoch_12.pth \\\n    8\n```\n\n## Train the customized Faster R-CNN with pre-trained FCOS\n\nPre-training not only speeds up convergence of training, but also improves the performance of the detector. Therefore, here we give an example to illustrate how to do use a pre-trained FCOS as an RPN to accelerate training and improve the accuracy. Suppose we want to use `FCOSHead` as an rpn head in Faster R-CNN and train with the pre-trained [`fcos_r50-caffe_fpn_gn-head_1x_coco`](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_1x_coco/fcos_r50_caffe_fpn_gn-head_1x_coco-821213aa.pth). The content of config file named `configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_fcos-rpn_1x_coco.py` is as the following. Note that `fcos_r50-caffe_fpn_gn-head_1x_coco` uses a caffe version of ResNet50, the pixel mean and std in `data_preprocessor` thus need to be updated.\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='caffe',\n        init_cfg=None),  # the checkpoint in ``load_from`` contains the weights of backbone\n    neck=dict(\n        start_level=1,\n        add_extra_convs='on_output',  # use P5\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        _delete_=True,  # ignore the unused old settings\n        type='FCOSHead',\n        num_classes=1,  # num_classes = 1 for rpn, if num_classes > 1, it will be set to 1 in TwoStageDetector automatically\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    roi_head=dict(  # update featmap_strides due to the strides in neck\n        bbox_roi_extractor=dict(featmap_strides=[8, 16, 32, 64, 128])))\n\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_1x_coco/fcos_r50_caffe_fpn_gn-head_1x_coco-821213aa.pth'\n```\n\nThe command for training is as below.\n\n```python\nbash tools/dist_train.sh \\\n    configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_fcos-rpn_1x_coco.py \\\n    8 \\\n    --work-dir ./work_dirs/faster-rcnn_r50-caffe_fpn_fcos-rpn_1x_coco\n```\n"
  },
  {
    "path": "docs/en/user_guides/test.md",
    "content": "# Test existing models on standard datasets\n\nTo evaluate a model's accuracy, one usually tests the model on some standard datasets, please refer to [dataset prepare guide](dataset_prepare.md) to prepare the dataset.\n\nThis section will show how to test existing models on supported datasets.\n\n## Test existing models\n\nWe provide testing scripts for evaluating an existing model on the whole dataset (COCO, PASCAL VOC, Cityscapes, etc.).\nThe following testing environments are supported:\n\n- single GPU\n- CPU\n- single node multiple GPUs\n- multiple nodes\n\nChoose the proper script to perform testing depending on the testing environment.\n\n```shell\n# Single-gpu testing\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--show]\n\n# CPU: disable GPUs and run single-gpu testing script\nexport CUDA_VISIBLE_DEVICES=-1\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--show]\n\n# Multi-gpu testing\nbash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    ${GPU_NUM} \\\n    [--out ${RESULT_FILE}]\n```\n\n`tools/dist_test.sh` also supports multi-node testing, but relies on PyTorch's [launch utility](https://pytorch.org/docs/stable/distributed.html#launch-utility).\n\nOptional arguments:\n\n- `RESULT_FILE`: Filename of the output results in pickle format. If not specified, the results will not be saved to a file.\n- `--show`: If specified, detection results will be plotted on the images and shown in a new window. It is only applicable to single GPU testing and used for debugging and visualization. Please make sure that GUI is available in your environment. Otherwise, you may encounter an error like `cannot connect to X server`.\n- `--show-dir`: If specified, detection results will be plotted on the images and saved to the specified directory. It is only applicable to single GPU testing and used for debugging and visualization. You do NOT need a GUI available in your environment for using this option.\n- `--work-dir`: If specified, detection results containing evaluation metrics will be saved to the specified directory.\n- `--cfg-options`:  If specified, the key-value pair optional cfg will be merged into config file\n\n## Examples\n\nAssuming that you have already downloaded the checkpoints to the directory `checkpoints/`.\n\n1. Test RTMDet and visualize the results. Press any key for the next image.\n   Config and checkpoint files are available [here](https://github.com/open-mmlab/mmdetection/tree/main/configs/rtmdet).\n\n   ```shell\n   python tools/test.py \\\n       configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n       checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n       --show\n   ```\n\n2. Test RTMDet and save the painted images for future visualization.\n   Config and checkpoint files are available [here](https://github.com/open-mmlab/mmdetection/tree/main/configs/rtmdet).\n\n   ```shell\n   python tools/test.py \\\n       configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n       checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n       --show-dir faster_rcnn_r50_fpn_1x_results\n   ```\n\n3. Test Faster R-CNN on PASCAL VOC (without saving the test results).\n   Config and checkpoint files are available [here](../../../configs/pascal_voc).\n\n   ```shell\n   python tools/test.py \\\n       configs/pascal_voc/faster-rcnn_r50_fpn_1x_voc0712.py \\\n       checkpoints/faster_rcnn_r50_fpn_1x_voc0712_20200624-c9895d40.pth\n   ```\n\n4. Test Mask R-CNN with 8 GPUs, and evaluate.\n   Config and checkpoint files are available [here](../../../configs/mask_rcnn).\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/mask-rcnn_r50_fpn_1x_coco.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n       8 \\\n       --out results.pkl\n   ```\n\n5. Test Mask R-CNN with 8 GPUs, and evaluate the metric **class-wise**.\n   Config and checkpoint files are available [here](../../../configs/mask_rcnn).\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n       8 \\\n       --out results.pkl \\\n       --cfg-options test_evaluator.classwise=True\n   ```\n\n6. Test Mask R-CNN on COCO test-dev with 8 GPUs, and generate JSON files for submitting to the official evaluation server.\n   Config and checkpoint files are available [here](../../../configs/mask_rcnn).\n\n   Replace the original test_evaluator and test_dataloader with test_evaluator and test_dataloader in the comment in [config](../../../configs/_base_/datasets/coco_instance.py) and run:\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n       8\n   ```\n\n   This command generates two JSON files `./work_dirs/coco_instance/test.bbox.json` and `./work_dirs/coco_instance/test.segm.json`.\n\n7. Test Mask R-CNN on Cityscapes test with 8 GPUs, and generate txt and png files for submitting to the official evaluation server.\n   Config and checkpoint files are available [here](../../../configs/cityscapes).\n\n   Replace the original test_evaluator and test_dataloader with test_evaluator and test_dataloader in the comment in [config](../../../configs/_base_/datasets/cityscapes_instance.py) and run:\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/cityscapes/mask-rcnn_r50_fpn_1x_cityscapes.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_cityscapes_20200227-afe51d5a.pth \\\n       8\n   ```\n\n   The generated png and txt would be under `./work_dirs/cityscapes_metric/` directory.\n\n## Test without Ground Truth Annotations\n\nMMDetection supports to test models without ground-truth annotations using `CocoDataset`. If your dataset format is not in COCO format, please convert them to COCO format. For example, if your dataset format is VOC, you can directly convert it to COCO format by the [script in tools.](../../../tools/dataset_converters/pascal_voc.py) If your dataset format is Cityscapes, you can directly convert it to COCO format by the [script in tools.](../../../tools/dataset_converters/cityscapes.py) The rest of the formats can be converted using [this script](../../../tools/dataset_converters/images2coco.py).\n\n```shell\npython tools/dataset_converters/images2coco.py \\\n    ${IMG_PATH} \\\n    ${CLASSES} \\\n    ${OUT} \\\n    [--exclude-extensions]\n```\n\narguments:\n\n- `IMG_PATH`: The root path of images.\n- `CLASSES`: The text file with a list of categories.\n- `OUT`: The output annotation json file name. The save dir is in the same directory as `IMG_PATH`.\n- `exclude-extensions`: The suffix of images to be excluded, such as 'png' and 'bmp'.\n\nAfter the conversion is complete, you need to replace the original test_evaluator and test_dataloader with test_evaluator and test_dataloader in the comment in [config](../../../configs/_base_/datasets/coco_detection.py)(find which dataset in 'configs/_base_/datasets' the current config corresponds to) and run:\n\n```shell\n# Single-gpu testing\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--show]\n\n# CPU: disable GPUs and run single-gpu testing script\nexport CUDA_VISIBLE_DEVICES=-1\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--show]\n\n# Multi-gpu testing\nbash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    ${GPU_NUM} \\\n    [--show]\n```\n\nAssuming that the checkpoints in the [model zoo](https://mmdetection.readthedocs.io/en/latest/modelzoo_statistics.html) have been downloaded to the directory `checkpoints/`, we can test Mask R-CNN on COCO test-dev with 8 GPUs, and generate JSON files using the following command.\n\n```sh\n./tools/dist_test.sh \\\n    configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py \\\n    checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n    8\n```\n\nThis command generates two JSON files `./work_dirs/coco_instance/test.bbox.json` and `./work_dirs/coco_instance/test.segm.json`.\n\n## Batch Inference\n\nMMDetection supports inference with a single image or batched images in test mode. By default, we use single-image inference and you can use batch inference by modifying `samples_per_gpu` in the config of test data. You can do that either by modifying the config as below.\n\n```shell\ndata = dict(train_dataloader=dict(...), val_dataloader=dict(...), test_dataloader=dict(batch_size=2, ...))\n```\n\nOr you can set it through `--cfg-options` as `--cfg-options test_dataloader.batch_size=2`\n\n## Test Time Augmentation (TTA)\n\nTest time augmentation (TTA) is a data augmentation strategy used during the test phase. It applies different augmentations, such as flipping and scaling, to the same image for model inference, and then merges the predictions of each augmented image to obtain more accurate predictions. To make it easier for users to use TTA, MMEngine provides [BaseTTAModel](https://mmengine.readthedocs.io/en/latest/api/generated/mmengine.model.BaseTTAModel.html#mmengine.model.BaseTTAModel) class, which allows users to implement different TTA strategies by simply extending the BaseTTAModel class according to their needs.\n\nIn MMDetection, we provides [DetTTAModel](../../../mmdet/models/test_time_augs/det_tta.py) class, which inherits from BaseTTAModel.\n\n### Use case\n\nUsing TTA requires two steps. First, you need to add `tta_model` and `tta_pipeline` in the configuration file:\n\n```shell\ntta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(\n                   type='nms',\n                   iou_threshold=0.5),\n                   max_per_img=100))\n\ntta_pipeline = [\n    dict(type='LoadImageFromFile',\n        backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[[\n            dict(type='Resize', scale=(1333, 800), keep_ratio=True)\n        ], [ # It uses 2 flipping transformations (flipping and not flipping).\n            dict(type='RandomFlip', prob=1.),\n            dict(type='RandomFlip', prob=0.)\n        ], [\n            dict(\n               type='PackDetInputs',\n               meta_keys=('img_id', 'img_path', 'ori_shape',\n                       'img_shape', 'scale_factor', 'flip',\n                       'flip_direction'))\n       ]])]\n```\n\nSecond, set `--tta` when running the test scripts as examples below:\n\n```shell\n# Single-gpu testing\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--tta]\n\n# CPU: disable GPUs and run single-gpu testing script\nexport CUDA_VISIBLE_DEVICES=-1\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--tta]\n\n# Multi-gpu testing\nbash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    ${GPU_NUM} \\\n    [--tta]\n```\n\nYou can also modify the TTA config by yourself, such as adding scaling enhancement:\n\n```shell\ntta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(\n                   type='nms',\n                   iou_threshold=0.5),\n                   max_per_img=100))\n\nimg_scales = [(1333, 800), (666, 400), (2000, 1200)]\ntta_pipeline = [\n    dict(type='LoadImageFromFile',\n         backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[[\n            dict(type='Resize', scale=s, keep_ratio=True) for s in img_scales\n        ], [\n            dict(type='RandomFlip', prob=1.),\n            dict(type='RandomFlip', prob=0.)\n        ], [\n            dict(\n               type='PackDetInputs',\n               meta_keys=('img_id', 'img_path', 'ori_shape',\n                       'img_shape', 'scale_factor', 'flip',\n                       'flip_direction'))\n       ]])]\n```\n\nThe above data augmentation pipeline will first perform 3 multi-scaling transformations on the image, followed by 2 flipping transformations (flipping and not flipping). Finally, the image is packaged into the final result using PackDetInputs.\n\nHere are more TTA use cases for your reference:\n\n- [RetinaNet](../../../configs/retinanet/retinanet_tta.py)\n- [CenterNet](../../../configs/centernet/centernet_tta.py)\n- [YOLOX](../../../configs/rtmdet/rtmdet_tta.py)\n- [RTMDet](../../../configs/yolox/yolox_tta.py)\n\nFor more advanced usage and data flow of TTA, please refer to [MMEngine](https://mmengine.readthedocs.io/en/latest/advanced_tutorials/test_time_augmentation.html#data-flow). We will support instance segmentation TTA latter.\n"
  },
  {
    "path": "docs/en/user_guides/test_results_submission.md",
    "content": "# Test Results Submission\n\n## Panoptic segmentation test results submission\n\nThe following sections introduce how to produce the prediction results of panoptic segmentation models on the COCO test-dev set and submit the predictions to [COCO evaluation server](https://competitions.codalab.org/competitions/19507).\n\n### Prerequisites\n\n- Download [COCO test dataset images](http://images.cocodataset.org/zips/test2017.zip), [testing image info](http://images.cocodataset.org/annotations/image_info_test2017.zip), and [panoptic train/val annotations](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip), then unzip them, put 'test2017' to `data/coco/`, put json files and annotation files to `data/coco/annotations/`.\n\n```shell\n# suppose data/coco/ does not exist\nmkdir -pv data/coco/\n\n# download test2017\nwget -P data/coco/ http://images.cocodataset.org/zips/test2017.zip\nwget -P data/coco/ http://images.cocodataset.org/annotations/image_info_test2017.zip\nwget -P data/coco/ http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip\n\n# unzip them\nunzip data/coco/test2017.zip -d data/coco/\nunzip data/coco/image_info_test2017.zip -d data/coco/\nunzip data/coco/panoptic_annotations_trainval2017.zip -d data/coco/\n\n# remove zip files (optional)\nrm -rf data/coco/test2017.zip data/coco/image_info_test2017.zip data/coco/panoptic_annotations_trainval2017.zip\n```\n\n- Run the following code to update category information in testing image info. Since the attribute `isthing` is missing in category information of 'image_info_test-dev2017.json', we need to update it with the category information in 'panoptic_val2017.json'.\n\n```shell\npython tools/misc/gen_coco_panoptic_test_info.py data/coco/annotations\n```\n\nAfter completing the above preparations, your directory structure of `data` should be like this:\n\n```text\ndata\n`-- coco\n    |-- annotations\n    |   |-- image_info_test-dev2017.json\n    |   |-- image_info_test2017.json\n    |   |-- panoptic_image_info_test-dev2017.json\n    |   |-- panoptic_train2017.json\n    |   |-- panoptic_train2017.zip\n    |   |-- panoptic_val2017.json\n    |   `-- panoptic_val2017.zip\n    `-- test2017\n```\n\n### Inference on coco test-dev\n\nTo do inference on coco test-dev, we should update the setting of `test_dataloder` and `test_evaluator` first. There two ways to do this: 1. update them in config file; 2. update them in command line.\n\n#### Update them in config file\n\nThe relevant settings are provided at the end of `configs/_base_/datasets/coco_panoptic.py`, as below.\n\n```python\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=1,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/panoptic_image_info_test-dev2017.json',\n        data_prefix=dict(img='test2017/'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_evaluator = dict(\n    type='CocoPanopticMetric',\n    format_only=True,\n    ann_file=data_root + 'annotations/panoptic_image_info_test-dev2017.json',\n    outfile_prefix='./work_dirs/coco_panoptic/test')\n```\n\nAny of the following way can be used to update the setting for inference on coco test-dev set.\n\nCase 1: Directly uncomment the setting in `configs/_base_/datasets/coco_panoptic.py`.\n\nCase 2: Copy the following setting to the config file you used now.\n\n```python\ntest_dataloader = dict(\n    dataset=dict(\n        ann_file='annotations/panoptic_image_info_test-dev2017.json',\n        data_prefix=dict(img='test2017/', _delete_=True)))\ntest_evaluator = dict(\n    format_only=True,\n    ann_file=data_root + 'annotations/panoptic_image_info_test-dev2017.json',\n    outfile_prefix='./work_dirs/coco_panoptic/test')\n```\n\nThen infer on coco test-dev et by the following command.\n\n```shell\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE}\n```\n\n#### Update them in command line\n\nThe command for update of the related settings and inference on coco test-dev are as below.\n\n```shell\n# test with single gpu\nCUDA_VISIBLE_DEVICES=0 python tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=${WORK_DIR}/results\n\n# test with four gpus\nCUDA_VISIBLE_DEVICES=0,1,3,4 bash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    8 \\  # eights gpus\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=${WORK_DIR}/results\n\n# test with slurm\nGPUS=8 tools/slurm_test.sh \\\n    ${Partition} \\\n    ${JOB_NAME} \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=${WORK_DIR}/results\n```\n\nExample\n\nSuppose we perform inference on `test2017` using pretrained MaskFormer with ResNet-50 backbone.\n\n```shell\n# test with single gpu\nCUDA_VISIBLE_DEVICES=0 python tools/test.py \\\n    configs/maskformer/maskformer_r50_mstrain_16x1_75e_coco.py \\\n    checkpoints/maskformer_r50_mstrain_16x1_75e_coco_20220221_141956-bc2699cb.pth \\\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=work_dirs/maskformer/results\n```\n\n### Rename files and zip results\n\nAfter inference, the panoptic segmentation results (a json file and a directory where the masks are stored) will be in `WORK_DIR`. We should rename them according to the naming convention described on [COCO's Website](https://cocodataset.org/#upload). Finally, we need to compress the json and the directory where the masks are stored into a zip file, and rename the zip file according to the naming convention. Note that the zip file should **directly** contains the above two files.\n\nThe commands to rename files and zip results:\n\n```shell\n# In WORK_DIR, we have panoptic segmentation results: 'panoptic' and 'results.panoptic.json'.\ncd ${WORK_DIR}\n\n# replace '[algorithm_name]' with the name of algorithm you used.\nmv ./panoptic ./panoptic_test-dev2017_[algorithm_name]_results\nmv ./results.panoptic.json ./panoptic_test-dev2017_[algorithm_name]_results.json\nzip panoptic_test-dev2017_[algorithm_name]_results.zip -ur panoptic_test-dev2017_[algorithm_name]_results panoptic_test-dev2017_[algorithm_name]_results.json\n```\n"
  },
  {
    "path": "docs/en/user_guides/tracking_analysis_tools.md",
    "content": "**We provide lots of useful tools under the `tools/` directory.**\n\n## MOT Test-time Parameter Search\n\n`tools/analysis_tools/mot/mot_param_search.py` can search the parameters of the `tracker` in MOT models.\nIt is used as the same manner with `tools/test.py` but **different** in the configs.\n\nHere is an example that shows how to modify the configs:\n\n1. Define the desirable evaluation metrics to record.\n\n   For example, you can define the `evaluator` as\n\n   ```python\n   test_evaluator=dict(type='MOTChallengeMetrics', metric=['HOTA', 'CLEAR', 'Identity'])\n   ```\n\n   Of course, you can also customize the content of `metric` in `test_evaluator`. You are free to choose one or more of `['HOTA', 'CLEAR', 'Identity']`.\n\n2. Define the parameters and the values to search.\n\n   Assume you have a tracker like\n\n   ```python\n   model=dict(\n       tracker=dict(\n           type='BaseTracker',\n           obj_score_thr=0.5,\n           match_iou_thr=0.5\n       )\n   )\n   ```\n\n   If you want to search the parameters of the tracker, just change the value to a list as follow\n\n   ```python\n   model=dict(\n       tracker=dict(\n           type='BaseTracker',\n           obj_score_thr=[0.4, 0.5, 0.6],\n           match_iou_thr=[0.4, 0.5, 0.6, 0.7]\n       )\n   )\n   ```\n\n   Then the script will test the totally 12 cases and log the results.\n\n## MOT Error Visualize\n\n`tools/analysis_tools/mot/mot_error_visualize.py` can visualize errors for multiple object tracking.\nThis script needs the result of inference. By Default, the **red** bounding box denotes false positive, the **yellow** bounding box denotes the false negative and the **blue** bounding box denotes ID switch.\n\n```\npython tools/analysis_tools/mot/mot_error_visualize.py \\\n    ${CONFIG_FILE}\\\n    --input ${INPUT} \\\n    --result-dir ${RESULT_DIR} \\\n    [--output-dir ${OUTPUT}] \\\n    [--fps ${FPS}] \\\n    [--show] \\\n    [--backend ${BACKEND}]\n```\n\nThe `RESULT_DIR` contains the inference results of all videos and the inference result is a `txt` file.\n\nOptional arguments:\n\n- `OUTPUT`: Output of the visualized demo. If not specified, the `--show` is obligate to show the video on the fly.\n- `FPS`: FPS of the output video.\n- `--show`: Whether show the video on the fly.\n- `BACKEND`: The backend to visualize the boxes. Options are `cv2` and `plt`.\n\n## Browse dataset\n\n`tools/analysis_tools/mot/browse_dataset.py` can visualize the training dataset to check whether the dataset configuration is correct.\n\n**Examples:**\n\n```shell\npython tools/analysis_tools/browse_dataset.py ${CONFIG_FILE} [--show-interval ${SHOW_INTERVAL}]\n```\n\nOptional arguments:\n\n- `SHOW_INTERVAL`: The interval of show (s).\n- `--show`: Whether show the images on the fly.\n"
  },
  {
    "path": "docs/en/user_guides/tracking_config.md",
    "content": "# Learn about Configs\n\nWe use python files as our config system. You can find all the provided configs under $MMDetection/configs.\n\nWe incorporate modular and inheritance design into our config system,\nwhich is convenient to conduct various experiments.\nIf you wish to inspect the config file,\nyou may run `python tools/misc/print_config.py /PATH/TO/CONFIG` to see the complete config.\n\n## A brief description of a complete config\n\nA complete config usually contains the following primary fields:\n\n- `model`: the basic config of model, which may contain `data_preprocessor`, modules (e.g., `detector`, `motion`),`train_cfg`, `test_cfg`, etc.\n- `train_dataloader`: the config of training dataloader, which usually contains `batch_size`, `num_workers`, `sampler`, `dataset`, etc.\n- `val_dataloader`: the config of validation dataloader, which is similar with `train_dataloader`.\n- `test_dataloader`: the config of testing dataloader, which is similar with `train_dataloader`.\n- `val_evaluator`: the config of validation evaluator. For example,`type='MOTChallengeMetrics'` for MOT task on the MOTChallenge benchmarks.\n- `test_evaluator`: the config of testing evaluator, which is similar with `val_evaluator`.\n- `train_cfg`: the config of training loop. For example, `type='EpochBasedTrainLoop'`.\n- `val_cfg`: the config of validation loop. For example, `type='VideoValLoop'`.\n- `test_cfg`: the config of testing loop. For example, `type='VideoTestLoop'`.\n- `default_hooks`: the config of default hooks, which may include hooks for timer, logger, param_scheduler, checkpoint, sampler_seed, visualization, etc.\n- `vis_backends`: the config of visualization backends, which uses `type='LocalVisBackend'` as default.\n- `visualizer`: the config of visualizer.  `type='TrackLocalVisualizer'` for MOT tasks.\n- `param_scheduler`: the config of parameter scheduler, which usually sets the learning rate scheduler.\n- `optim_wrapper`: the config of optimizer wrapper, which contains optimization-related information, for example optimizer, gradient clipping, etc.\n- `load_from`: load models as a pre-trained model from a given path.\n- `resume`: If `True`, resume checkpoints from `load_from`, and the training will be resumed from the epoch when the checkpoint is saved.\n\n## Modify config through script arguments\n\nWhen submitting jobs using `tools/train.py` or `tools/test_tracking.py`,\nyou may specify `--cfg-options` to in-place modify the config.\nWe present several examples as follows.\nFor more details, please refer to [MMEngine](https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/config.md).\n\n- **Update config keys of dict chains.**\n\n  The config options can be specified following the order of the dict keys in the original config.\n  For example, `--cfg-options model.detector.backbone.norm_eval=False` changes the all BN modules in model backbones to train mode.\n\n- **Update keys inside a list of configs.**\n\n  Some config dicts are composed as a list in your config.\n  For example, the testing pipeline `test_dataloader.dataset.pipeline` is normally a list e.g. `[dict(type='LoadImageFromFile'), ...]`.\n  If you want to change `LoadImageFromFile` to `LoadImageFromWebcam` in the pipeline,\n  you may specify `--cfg-options test_dataloader.dataset.pipeline.0.type=LoadImageFromWebcam`.\n\n- **Update values of list/tuples.**\n\n  Maybe the value to be updated is a list or a tuple.\n  For example, you can change the key `mean` of `data_preprocessor` by specifying `--cfg-options model.data_preprocessor.mean=[0,0,0]`.\n  Note that **NO** white space is allowed inside the specified value.\n\n## Config File Structure\n\nThere are 3 basic component types under `config/_base_`, i.e., dataset, model and default_runtime.\nMany methods could be easily constructed with one of each like SORT, DeepSORT.\nThe configs that are composed by components from `_base_` are called *primitive*.\n\nFor all configs under the same folder, it is recommended to have only **one** *primitive* config.\nAll other configs should inherit from the *primitive* config.\nIn this way, the maximum of inheritance level is 3.\n\nFor easy understanding, we recommend contributors to inherit from exiting methods.\nFor example, if some modification is made base on Faster R-CNN,\nuser may first inherit the basic Faster R-CNN structure\nby specifying `_base_ = ../_base_/models/faster-rcnn_r50-dc5.py`,\nthen modify the necessary fields in the config files.\n\nIf you are building an entirely new method that does not share the structure with any of the existing methods,\nyou may create a folder `method_name` under `configs`.\n\nPlease refer to [MMEngine](https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/config.md) for detailed documentation.\n\n## Config Name Style\n\nWe follow the below style to name config files. Contributors are advised to follow the same style.\n\n```shell\n{method}_{module}_{train_cfg}_{train_data}_{test_data}\n```\n\n- `{method}`: method name, like `sort`.\n- `{module}`: basic modules of the method, like `faster-rcnn_r50_fpn`.\n- `{train_cfg}`: training config which usually contains batch size, epochs, etc, like `8xb4-80e`.\n- `{train_data}`: training data, like `mot17halftrain`.\n- `{test_data}`: testing data, like `test-mot17halfval`.\n\n## FAQ\n\n**Ignore some fields in the base configs**\n\nSometimes, you may set `_delete_=True` to ignore some of fields in base configs.\nYou may refer to [MMEngine](https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/config.md) for simple illustration.\n\n## Tracking Data Structure Introduction\n\n### Advantages and new features\n\nIn mmdetection tracking task, we employ videos to organize the dataset and use\nTrackDataSample to descirbe dataset info.\n\n- Based on video organization, we provide transform `UniformRefFrameSample` to sample key frames and ref frames and use `TransformBroadcaster` for for clip training.\n- TrackDataSample can be viewd as a wrapper of multiple DetDataSample to some extent. It contains a property `video_data_samples` which is a list of DetDataSample, each of which corresponds to a single frame. In addition, it's metainfo includes key_frames_inds and ref_frames_inds to apply clip training way.\n- Thanks to video-based data organization, the entire video can be directly tested. This way is more concise and intuitive. We also provide image_based test method, if your GPU mmemory cannot fit the entire video.\n\n### TODO\n\n- Some algorithms like StrongSORT, Mask2Former can not support video_based testing. These algorithms pose a challenge to GPU memory. we will optimize this problem in the future.\n- Now we do not support joint training of video_based dataset like MOT Challenge Dataset and image_based dataset like Crowdhuman for the algorithm QDTrack. we will optimize this problem in the future.\n"
  },
  {
    "path": "docs/en/user_guides/tracking_dataset_prepare.md",
    "content": "## Dataset Preparation\n\nThis page provides the instructions for dataset preparation on existing benchmarks, include\n\n- Multiple Object Tracking\n\n  - [MOT Challenge](https://motchallenge.net/)\n  - [CrowdHuman](https://www.crowdhuman.org/)\n\n- Video Instance Segmentation\n\n  - [YouTube-VIS](https://youtube-vos.org/dataset/vis/)\n\n### 1. Download Datasets\n\nPlease download the datasets from the official websites. It is recommended to symlink the root of the datasets to `$MMDETECTION/data`.\n\n#### 1.1 Multiple Object Tracking\n\n- For the training and testing of multi object tracking task, one of the MOT Challenge datasets (e.g. MOT17, MOT20) are needed, CrowdHuman can be served as comlementary dataset.\n\n- For users in China, the following datasets can be downloaded from [OpenDataLab](https://opendatalab.com/) with high speed:\n\n  - [MOT17](https://opendatalab.com/MOT17/download)\n  - [MOT20](https://opendatalab.com/MOT20/download)\n  - [CrowdHuman](https://opendatalab.com/CrowdHuman/download)\n\n#### 1.2 Video Instance Segmentation\n\n- For the training and testing of video instance segmetatioon task, only one of YouTube-VIS datasets (e.g. YouTube-VIS 2019, YouTube-VIS 2021) is needed.\n\n- YouTube-VIS 2019 dataset can be download from [YouTubeVOS](https://codalab.lisn.upsaclay.fr/competitions/6064)\n\n- YouTube-VIS 2021 dataset can be download from [YouTubeVOS](https://codalab.lisn.upsaclay.fr/competitions/7680)\n\n#### 1.3 Data Structure\n\nIf your folder structure is different from the following, you may need to change the corresponding paths in config files.\n\n```\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── annotations\n│   │\n|   ├── MOT15/MOT16/MOT17/MOT20\n|   |   ├── train\n|   |   |   ├── MOT17-02-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── gt\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n|   |   ├── test\n|   |   |   ├── MOT17-01-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n│   │\n│   ├── crowdhuman\n│   │   ├── annotation_train.odgt\n│   │   ├── annotation_val.odgt\n│   │   ├── train\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_train01.zip\n│   │   │   ├── CrowdHuman_train02.zip\n│   │   │   ├── CrowdHuman_train03.zip\n│   │   ├── val\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_val.zip\n│   │\n```\n\n### 2. Convert Annotations\n\nIn this case, you need to convert the official annotations to coco style. We provide scripts and the usages are as following:\n\n```shell\n# MOT17\n# The processing of other MOT Challenge dataset is the same as MOT17\npython ./tools/dataset_converters/mot2coco.py -i ./data/MOT17/ -o ./data/MOT17/annotations --split-train --convert-det\npython ./tools/dataset_converters/mot2reid.py -i ./data/MOT17/ -o ./data/MOT17/reid --val-split 0.2 --vis-threshold 0.3\n\n# CrowdHuman\npython ./tools/dataset_converters/crowdhuman2coco.py -i ./data/crowdhuman -o ./data/crowdhuman/annotations\n\n# YouTube-VIS 2019\npython ./tools/dataset_converters/youtubevis2coco.py -i ./data/youtube_vis_2019 -o ./data/youtube_vis_2019/annotations --version 2019\n\n# YouTube-VIS 2021\npython ./tools/dataset_converters/youtubevis2coco.py -i ./data/youtube_vis_2021 -o ./data/youtube_vis_2021/annotations --version 2021\n\n```\n\nThe folder structure will be as following after your run these scripts:\n\n```\nmmdetection\n├── mmtrack\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── annotations\n│   │\n|   ├── MOT15/MOT16/MOT17/MOT20\n|   |   ├── train\n|   |   |   ├── MOT17-02-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── gt\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n|   |   ├── test\n|   |   |   ├── MOT17-01-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n|   |   ├── annotations\n|   |   ├── reid\n│   │   │   ├── imgs\n│   │   │   ├── meta\n│   │\n│   ├── crowdhuman\n│   │   ├── annotation_train.odgt\n│   │   ├── annotation_val.odgt\n│   │   ├── train\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_train01.zip\n│   │   │   ├── CrowdHuman_train02.zip\n│   │   │   ├── CrowdHuman_train03.zip\n│   │   ├── val\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_val.zip\n│   │   ├── annotations\n│   │   │   ├── crowdhuman_train.json\n│   │   │   ├── crowdhuman_val.json\n│   │\n│   ├── youtube_vis_2019\n│   │   │── train\n│   │   │   │── JPEGImages\n│   │   │   │── ......\n│   │   │── valid\n│   │   │   │── JPEGImages\n│   │   │   │── ......\n│   │   │── test\n│   │   │   │── JPEGImages\n│   │   │   │── ......\n│   │   │── train.json (the official annotation files)\n│   │   │── valid.json (the official annotation files)\n│   │   │── test.json (the official annotation files)\n│   │   │── annotations (the converted annotation file)\n│   │\n│   ├── youtube_vis_2021\n│   │   │── train\n│   │   │   │── JPEGImages\n│   │   │   │── instances.json (the official annotation files)\n│   │   │   │── ......\n│   │   │── valid\n│   │   │   │── JPEGImages\n│   │   │   │── instances.json (the official annotation files)\n│   │   │   │── ......\n│   │   │── test\n│   │   │   │── JPEGImages\n│   │   │   │── instances.json (the official annotation files)\n│   │   │   │── ......\n│   │   │── annotations (the converted annotation file)\n```\n\n#### The folder of annotations and reid in MOT15/MOT16/MOT17/MOT20\n\nWe take MOT17 dataset as examples, the other datasets share similar structure.\n\nThere are 8 JSON files in `data/MOT17/annotations`:\n\n`train_cocoformat.json`: JSON file containing the annotations information of the training set in MOT17 dataset.\n\n`train_detections.pkl`: Pickle file containing the public detections of the training set in MOT17 dataset.\n\n`test_cocoformat.json`: JSON file containing the annotations information of the testing set in MOT17 dataset.\n\n`test_detections.pkl`: Pickle file containing the public detections of the testing set in MOT17 dataset.\n\n`half-train_cocoformat.json`, `half-train_detections.pkl`, `half-val_cocoformat.json`and `half-val_detections.pkl` share similar meaning with `train_cocoformat.json` and `train_detections.pkl`. The `half` means we split each video in the training set into half. The first half videos are denoted as `half-train` set, and the second half videos are denoted as`half-val` set.\n\nThe structure of `data/MOT17/reid` is as follows:\n\n```\nreid\n├── imgs\n│   ├── MOT17-02-FRCNN_000002\n│   │   ├── 000000.jpg\n│   │   ├── 000001.jpg\n│   │   ├── ...\n│   ├── MOT17-02-FRCNN_000003\n│   │   ├── 000000.jpg\n│   │   ├── 000001.jpg\n│   │   ├── ...\n├── meta\n│   ├── train_80.txt\n│   ├── val_20.txt\n```\n\nThe `80` in `train_80.txt` means the proportion of the training dataset to the whole ReID dataset is 80%. While the proportion of the validation dataset is 20%.\n\nFor training, we provide a annotation list `train_80.txt`. Each line of the list contains a filename and its corresponding ground-truth labels. The format is as follows:\n\n```\nMOT17-05-FRCNN_000110/000018.jpg 0\nMOT17-13-FRCNN_000146/000014.jpg 1\nMOT17-05-FRCNN_000088/000004.jpg 2\nMOT17-02-FRCNN_000009/000081.jpg 3\n```\n\n`MOT17-05-FRCNN_000110` denotes the 110-th person in `MOT17-05-FRCNN` video.\n\nFor validation, The annotation list `val_20.txt` remains the same as format above.\n\nImages in `reid/imgs` are cropped from raw images in `MOT17/train` by the corresponding `gt.txt`. The value of ground-truth labels should fall in range `[0, num_classes - 1]`.\n\n#### The folder of annotations in crowdhuman\n\nThere are 2 JSON files in `data/crowdhuman/annotations`:\n\n`crowdhuman_train.json`:  JSON file containing the annotations information of the training set in CrowdHuman dataset.\n`crowdhuman_val.json`:  JSON file containing the annotations information of the validation set in CrowdHuman dataset.\n\n#### The folder of annotations in youtube_vis_2019/youtube_vis2021\n\nThere are 3 JSON files in `data/youtube_vis_2019/annotations` or `data/youtube_vis_2021/annotations`:\n\n`youtube_vis_2019_train.json`/`youtube_vis_2021_train.json`: JSON file containing the annotations information of the training set in youtube_vis_2019/youtube_vis2021 dataset.\n\n`youtube_vis_2019_valid.json`/`youtube_vis_2021_valid.json`: JSON file containing the annotations information of the validation set in youtube_vis_2019/youtube_vis2021 dataset.\n\n`youtube_vis_2019_test.json`/`youtube_vis_2021_test.json`: JSON file containing the annotations information of the testing set in youtube_vis_2019/youtube_vis2021 dataset.\n"
  },
  {
    "path": "docs/en/user_guides/tracking_inference.md",
    "content": "# Inference\n\nWe provide demo scripts to inference a given video or a folder that contains continuous images. The source codes are available [here](https://github.com/open-mmlab/mmdetection/tree/tracking/demo).\n\nNote that if you use a folder as the input, the image names there must be  **sortable** , which means we can re-order the images according to the numbers contained in the filenames. We now only support reading the images whose filenames end with `.jpg`, `.jpeg` and `.png`.\n\n## Inference MOT models\n\nThis script can inference an input video / images with a multiple object tracking or video instance segmentation model.\n\n```shell\npython demo/mot_demo.py \\\n    ${INPUTS}\n    ${CONFIG_FILE} \\\n    [--checkpoint ${CHECKPOINT_FILE}] \\\n    [--detector ${DETECTOR_FILE}] \\\n    [--reid ${REID_FILE}] \\\n    [--score-thr ${SCORE_THR}] \\\n    [--device ${DEVICE}] \\\n    [--out ${OUTPUT}] \\\n    [--show]\n```\n\nThe `INPUT` and `OUTPUT` support both _mp4 video_ format and the _folder_ format.\n\n**Important:** For `DeepSORT`, `SORT`, `StrongSORT`, they need load the weight of the `reid` and the weight of the `detector` separately. Therefore, we use `--detector` and `--reid` to load weights. Other algorithms such as `ByteTrack`, `OCSORT` `QDTrack` `MaskTrackRCNN` and `Mask2Former` use `--checkpoint` to load weights.\n\nOptional arguments:\n\n- `CHECKPOINT_FILE`: The checkpoint is optional.\n- `DETECTOR_FILE`: The detector is optional.\n- `REID_FILE`: The reid is optional.\n- `SCORE_THR`: The threshold of score to filter bboxes.\n- `DEVICE`: The device for inference. Options are `cpu` or `cuda:0`, etc.\n- `OUTPUT`: Output of the visualized demo. If not specified, the `--show` is obligate to show the video on the fly.\n- `--show`: Whether show the video on the fly.\n\n**Examples of running mot model:**\n\n```shell\n# Example 1: do not specify --checkpoint to use --detector\npython demo/mot_demo.py \\\n    demo/demo_mot.mp4 \\\n    configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py \\\n    --detector \\\n    https://download.openmmlab.com/mmtracking/mot/faster_rcnn/faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth \\\n    --out mot.mp4\n\n# Example 2: use --checkpoint\npython demo/mot_demo.py \\\n    demo/demo_mot.mp4 \\\n    configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py \\\n    --checkpoint https://download.openmmlab.com/mmtracking/mot/qdtrack/mot_dataset/qdtrack_faster-rcnn_r50_fpn_4e_mot17_20220315_145635-76f295ef.pth \\\n    --out mot.mp4\n```\n"
  },
  {
    "path": "docs/en/user_guides/tracking_train_test.md",
    "content": "# Learn to train and test\n\n## Train\n\nThis section will show how to train existing models on supported datasets.\nThe following training environments are supported:\n\n- CPU\n- single GPU\n- single node multiple GPUs\n- multiple nodes\n\nYou can also manage jobs with Slurm.\n\nImportant:\n\n- You can change the evaluation interval during training by modifying the `train_cfg` as\n  `train_cfg = dict(val_interval=10)`. That means evaluating the model every 10 epochs.\n- The default learning rate in all config files is for 8 GPUs.\n  According to the [Linear Scaling Rule](https://arxiv.org/abs/1706.02677),\n  you need to set the learning rate proportional to the batch size if you use different GPUs or images per GPU,\n  e.g., `lr=0.01` for 8 GPUs * 1 img/gpu and lr=0.04 for 16 GPUs * 2 imgs/gpu.\n- During training, log files and checkpoints will be saved to the working directory,\n  which is specified by CLI argument `--work-dir`. It uses `./work_dirs/CONFIG_NAME` as default.\n- If you want the mixed precision training, simply specify CLI argument `--amp`.\n\n#### 1. Train on CPU\n\nThe model is default put on cuda device.\nOnly if there are no cuda devices, the model will be put on cpu.\nSo if you want to train the model on CPU, you need to `export CUDA_VISIBLE_DEVICES=-1` to disable GPU visibility first.\nMore details in [MMEngine](https://github.com/open-mmlab/mmengine/blob/ca282aee9e402104b644494ca491f73d93a9544f/mmengine/runner/runner.py#L849-L850).\n\n```shell script\nCUDA_VISIBLE_DEVICES=-1 python tools/train.py ${CONFIG_FILE} [optional arguments]\n```\n\nAn example of training the MOT model QDTrack on CPU:\n\n```shell script\nCUDA_VISIBLE_DEVICES=-1 python tools/train.py configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n```\n\n#### 2. Train on single GPU\n\nIf you want to train the model on single GPU, you can directly use the `tools/train.py` as follows.\n\n```shell script\npython tools/train.py ${CONFIG_FILE} [optional arguments]\n```\n\nYou can use `export CUDA_VISIBLE_DEVICES=$GPU_ID` to select the GPU.\n\nAn example of training the MOT model QDTrack on single GPU:\n\n```shell script\nCUDA_VISIBLE_DEVICES=2 python tools/train.py configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n```\n\n#### 3. Train on single node multiple GPUs\n\nWe provide `tools/dist_train.sh` to launch training on multiple GPUs.\nThe basic usage is as follows.\n\n```shell script\nbash ./tools/dist_train.sh ${CONFIG_FILE} ${GPU_NUM} [optional arguments]\n```\n\nIf you would like to launch multiple jobs on a single machine,\ne.g., 2 jobs of 4-GPU training on a machine with 8 GPUs,\nyou need to specify different ports (29500 by default) for each job to avoid communication conflict.\n\nFor example, you can set the port in commands as follows.\n\n```shell script\nCUDA_VISIBLE_DEVICES=0,1,2,3 PORT=29500 ./tools/dist_train.sh ${CONFIG_FILE} 4\nCUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4\n```\n\nAn example of training the MOT model QDTrack on single node multiple GPUs:\n\n```shell script\nbash ./tools/dist_train.sh configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8\n```\n\n#### 4. Train on multiple nodes\n\nIf you launch with multiple machines simply connected with ethernet, you can simply run following commands:\n\nOn the first machine:\n\n```shell script\nNNODES=2 NODE_RANK=0 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR bash tools/dist_train.sh $CONFIG $GPUS\n```\n\nOn the second machine:\n\n```shell script\nNNODES=2 NODE_RANK=1 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR bash tools/dist_train.sh $CONFIG $GPUS\n```\n\nUsually it is slow if you do not have high speed networking like InfiniBand.\n\n#### 5. Train with Slurm\n\n[Slurm](https://slurm.schedmd.com/) is a good job scheduling system for computing clusters.\nOn a cluster managed by Slurm, you can use `slurm_train.sh` to spawn training jobs.\nIt supports both single-node and multi-node training.\n\nThe basic usage is as follows.\n\n```shell script\nbash ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR} ${GPUS}\n```\n\nAn example of training the MOT model QDTrack with Slurm:\n\n```shell script\nPORT=29501 \\\nGPUS_PER_NODE=8 \\\nSRUN_ARGS=\"--quotatype=reserved\" \\\nbash ./tools/slurm_train.sh \\\nmypartition \\\nmottrack\nconfigs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n./work_dirs/QDTrack \\\n8\n```\n\n## Test\n\nThis section will show how to test existing models on supported datasets.\nThe following testing environments are supported:\n\n- CPU\n- single GPU\n- single node multiple GPUs\n- multiple nodes\n\nYou can also manage jobs with Slurm.\n\nImportant:\n\n- In MOT, some algorithms like `DeepSORT`, `SORT`, `StrongSORT` need load the weight of the `reid` and the weight of the `detector` separately.\n  Other algorithms such as `ByteTrack`, `OCSORT` and `QDTrack` don't need. So we provide `--checkpoint`, `--detector` and `--reid` to load weights.\n- We provide two ways to evaluate and test models, video_basede test and image_based test. some algorithms like `StrongSORT`, `Mask2former` only support\n  video_based test. if your GPU memory can't fit the entire video, you can switch test way by set sampler type.\n  For example:\n  video_based test: `sampler=dict(type='DefaultSampler', shuffle=False, round_up=False)`\n  image_based test: `sampler=dict(type='TrackImgSampler')`\n- You can set the results saving path by modifying the key `outfile_prefix` in evaluator.\n  For example, `val_evaluator = dict(outfile_prefix='results/sort_mot17')`.\n  Otherwise, a temporal file will be created and will be removed after evaluation.\n- If you just want the formatted results without evaluation, you can set `format_only=True`.\n  For example, `test_evaluator = dict(type='MOTChallengeMetric', metric=['HOTA', 'CLEAR', 'Identity'], outfile_prefix='sort_mot17_results', format_only=True)`\n\n#### 1. Test on CPU\n\nThe model is default put on cuda device.\nOnly if there are no cuda devices, the model will be put on cpu.\nSo if you want to test the model on CPU, you need to `export CUDA_VISIBLE_DEVICES=-1` to disable GPU visibility first.\nMore details in [MMEngine](https://github.com/open-mmlab/mmengine/blob/ca282aee9e402104b644494ca491f73d93a9544f/mmengine/runner/runner.py#L849-L850).\n\n```shell script\nCUDA_VISIBLE_DEVICES=-1 python tools/test_tracking.py ${CONFIG_FILE} [optional arguments]\n```\n\nAn example of testing the MOT model SORT on CPU:\n\n```shell script\nCUDA_VISIBLE_DEVICES=-1 python tools/test_tracking.py configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py --detector ${CHECKPOINT_FILE}\n```\n\n#### 2. Test on single GPU\n\nIf you want to test the model on single GPU, you can directly use the `tools/test_tracking.py` as follows.\n\n```shell script\npython tools/test_tracking.py ${CONFIG_FILE} [optional arguments]\n```\n\nYou can use `export CUDA_VISIBLE_DEVICES=$GPU_ID` to select the GPU.\n\nAn example of testing the MOT model QDTrack on single GPU:\n\n```shell script\nCUDA_VISIBLE_DEVICES=2 python tools/test_tracking.py configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py --detector ${CHECKPOINT_FILE}\n```\n\n#### 3. Test on single node multiple GPUs\n\nWe provide `tools/dist_test_tracking.sh` to launch testing on multiple GPUs.\nThe basic usage is as follows.\n\n```shell script\nbash ./tools/dist_test_tracking.sh ${CONFIG_FILE} ${GPU_NUM} [optional arguments]\n```\n\nAn example of testing the MOT model DeepSort on single node multiple GPUs:\n\n```shell script\nbash ./tools/dist_test_tracking.sh configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8 --detector ${CHECKPOINT_FILE} --reid ${CHECKPOINT_FILE}\n```\n\n#### 4. Test on multiple nodes\n\nYou can test on multiple nodes, which is similar with \"Train on multiple nodes\".\n\n#### 5. Test with Slurm\n\nOn a cluster managed by Slurm, you can use `slurm_test_tracking.sh` to spawn testing jobs.\nIt supports both single-node and multi-node testing.\n\nThe basic usage is as follows.\n\n```shell script\n[GPUS=${GPUS}] bash tools/slurm_test_tracking.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} [optional arguments]\n```\n\nAn example of testing the VIS model Mask2former with Slurm:\n\n```shell script\nGPUS=8\nbash tools/slurm_test_tracking.sh \\\nmypartition \\\nvis \\\nconfigs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py \\\n--checkpoint ${CHECKPOINT_FILE}\n```\n"
  },
  {
    "path": "docs/en/user_guides/tracking_visualization.md",
    "content": "# Learn about Visualization\n\n## Local Visualization\n\nThis section will present how to visualize the detection/tracking results with local visualizer.\n\nIf you want to draw prediction results, you can turn this feature on by setting `draw=True` in `TrackVisualizationHook` as follows.\n\n```shell script\ndefault_hooks = dict(visualization=dict(type='TrackVisualizationHook', draw=True))\n```\n\nSpecifically, the `TrackVisualizationHook` has the following arguments:\n\n- `draw`: whether to draw prediction results. If it is False, it means that no drawing will be done. Defaults to False.\n- `interval`: The interval of visualization. Defaults to 30.\n- `score_thr`: The threshold to visualize the bboxes and masks. Defaults to 0.3.\n- `show`: Whether to display the drawn image. Default to False.\n- `wait_time`: The interval of show (s). Defaults to 0.\n- `test_out_dir`: directory where painted images will be saved in testing process.\n- `backend_args`: Arguments to instantiate a file client. Defaults to `None`.\n\nIn the `TrackVisualizationHook`, `TrackLocalVisualizer` will be called to implement visualization for MOT and VIS tasks.\nWe will present the details below.\nYou can refer to MMEngine for more details about [Visualization](https://github.com/open-mmlab/mmengine/blob/main/docs/en/advanced_tutorials/visualization.md) and [Hook](https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/hook.md).\n\n#### Tracking Visualization\n\nWe realize the tracking visualization with class `TrackLocalVisualizer`.\nYou can call it as follows.\n\n```python\nvisualizer = dict(type='TrackLocalVisualizer')\n```\n\nIt has the following arguments:\n\n- `name`: Name of the instance. Defaults to 'visualizer'.\n- `image`: The origin image to draw. The format should be RGB. Defaults to None.\n- `vis_backends`: Visual backend config list. Defaults to None.\n- `save_dir`: Save file dir for all storage backends. If it is None, the backend storage will not save any data.\n- `line_width`: The linewidth of lines. Defaults to 3.\n- `alpha`: The transparency of bboxes or mask. Defaults to 0.8.\n\nHere is a visualization example of DeepSORT:\n\n![test_img_89](https://user-images.githubusercontent.com/99722489/186062929-6d0e4663-0d8e-4045-9ec8-67e0e41da876.png)\n"
  },
  {
    "path": "docs/en/user_guides/train.md",
    "content": "# Train predefined models on standard datasets\n\nMMDetection also provides out-of-the-box tools for training detection models.\nThis section will show how to train _predefined_ models (under [configs](../../../configs)) on standard datasets i.e. COCO.\n\n## Prepare datasets\n\nPreparing datasets is also necessary for training. See section [Prepare datasets](#prepare-datasets) above for details.\n\n**Note**:\nCurrently, the config files under `configs/cityscapes` use COCO pre-trained weights to initialize.\nIf your network connection is slow or unavailable, it's advisable to download existing models before beginning training to avoid errors.\n\n## Learning rate auto scaling\n\n**Important**: The default learning rate in config files is for 8 GPUs and 2 sample per GPU (batch size = 8 * 2 = 16). And it had been set to `auto_scale_lr.base_batch_size` in `config/_base_/schedules/schedule_1x.py`. The learning rate will be automatically scaled based on the value at a batch size of 16. Meanwhile, to avoid affecting other codebases that use mmdet, the default setting for the `auto_scale_lr.enable` flag is `False`.\n\nIf you want to enable this feature, you need to add argument `--auto-scale-lr`. And you need to check the config name which you want to use before you process the command, because the config name indicates the default batch size.\nBy default, it is `8 x 2 = 16 batch size`, like `faster_rcnn_r50_caffe_fpn_90k_coco.py` or `pisa_faster_rcnn_x101_32x4d_fpn_1x_coco.py`. In other cases, you will see the config file name have `_NxM_` in dictating, like `cornernet_hourglass104_mstest_32x3_210e_coco.py` which batch size is `32 x 3 = 96`, or `scnet_x101_64x4d_fpn_8x1_20e_coco.py` which batch size is `8 x 1 = 8`.\n\n**Please remember to check the bottom of the specific config file you want to use, it will have `auto_scale_lr.base_batch_size` if the batch size is not `16`. If you can't find those values, check the config file which in `_base_=[xxx]` and you will find it. Please do not modify its values if you want to automatically scale the LR.**\n\nThe basic usage of learning rate auto scaling is as follows.\n\n```shell\npython tools/train.py \\\n    ${CONFIG_FILE} \\\n    --auto-scale-lr \\\n    [optional arguments]\n```\n\nIf you enabled this feature, the learning rate will be automatically scaled according to the number of GPUs on the machine and the batch size of training. See [linear scaling rule](https://arxiv.org/abs/1706.02677) for details. For example, If there are 4 GPUs and 2 pictures on each GPU, `lr = 0.01`, then if there are 16 GPUs and 4 pictures on each GPU, it will automatically scale to `lr = 0.08`.\n\nIf you don't want to use it, you need to calculate the learning rate according to the [linear scaling rule](https://arxiv.org/abs/1706.02677) manually then change `optimizer.lr` in specific config file.\n\n## Training on a single GPU\n\nWe provide `tools/train.py` to launch training jobs on a single GPU.\nThe basic usage is as follows.\n\n```shell\npython tools/train.py \\\n    ${CONFIG_FILE} \\\n    [optional arguments]\n```\n\nDuring training, log files and checkpoints will be saved to the working directory, which is specified by `work_dir` in the config file or via CLI argument `--work-dir`.\n\nBy default, the model is evaluated on the validation set every epoch, the evaluation interval can be specified in the config file as shown below.\n\n```python\n# evaluate the model every 12 epochs.\ntrain_cfg = dict(val_interval=12)\n```\n\nThis tool accepts several optional arguments, including:\n\n- `--work-dir ${WORK_DIR}`: Override the working directory.\n- `--resume`: resume from the latest checkpoint in the work_dir automatically.\n- `--resume ${CHECKPOINT_FILE}`: resume from the specific checkpoint.\n- `--cfg-options 'Key=value'`: Overrides other settings in the used config.\n\n**Note:**\n\nThere is a difference between `resume` and `load-from`:\n\n`resume` loads both the weights of the model and the state of the optimizer, and it inherits the iteration number from the specified checkpoint, so training does not start again from scratch. `load-from`, on the other hand, only loads the weights of the model, and its training starts from scratch. It is often used for fine-tuning a model. `load-from` needs to be written in the config file, while `resume` is passed as a command line argument.\n\n## Training on CPU\n\nThe process of training on the CPU is consistent with single GPU training. We just need to disable GPUs before the training process.\n\n```shell\nexport CUDA_VISIBLE_DEVICES=-1\n```\n\nAnd then run the script [above](#training-on-a-single-GPU).\n\n**Note**:\n\nWe do not recommend users to use the CPU for training because it is too slow. We support this feature to allow users to debug on machines without GPU for convenience.\n\n## Training on multiple GPUs\n\nWe provide `tools/dist_train.sh` to launch training on multiple GPUs.\nThe basic usage is as follows.\n\n```shell\nbash ./tools/dist_train.sh \\\n    ${CONFIG_FILE} \\\n    ${GPU_NUM} \\\n    [optional arguments]\n```\n\nOptional arguments remain the same as stated [above](#training-on-a-single-GPU).\n\n### Launch multiple jobs simultaneously\n\nIf you would like to launch multiple jobs on a single machine, e.g., 2 jobs of 4-GPU training on a machine with 8 GPUs,\nyou need to specify different ports (29500 by default) for each job to avoid communication conflict.\n\nIf you use `dist_train.sh` to launch training jobs, you can set the port in the commands.\n\n```shell\nCUDA_VISIBLE_DEVICES=0,1,2,3 PORT=29500 ./tools/dist_train.sh ${CONFIG_FILE} 4\nCUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4\n```\n\n## Train with multiple machines\n\nIf you launch with multiple machines simply connected with ethernet, you can simply run the following commands:\n\nOn the first machine:\n\n```shell\nNNODES=2 NODE_RANK=0 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR sh tools/dist_train.sh $CONFIG $GPUS\n```\n\nOn the second machine:\n\n```shell\nNNODES=2 NODE_RANK=1 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR sh tools/dist_train.sh $CONFIG $GPUS\n```\n\nUsually, it is slow if you do not have high-speed networking like InfiniBand.\n\n## Manage jobs with Slurm\n\n[Slurm](https://slurm.schedmd.com/) is a good job scheduling system for computing clusters.\nOn a cluster managed by Slurm, you can use `slurm_train.sh` to spawn training jobs. It supports both single-node and multi-node training.\n\nThe basic usage is as follows.\n\n```shell\n[GPUS=${GPUS}] ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR}\n```\n\nBelow is an example of using 16 GPUs to train Mask R-CNN on a Slurm partition named _dev_, and set the work-dir to some shared file systems.\n\n```shell\nGPUS=16 ./tools/slurm_train.sh dev mask_r50_1x configs/mask-rcnn_r50_fpn_1x_coco.py /nfs/xxxx/mask_rcnn_r50_fpn_1x\n```\n\nYou can check [the source code](../../../tools/slurm_train.sh) to review full arguments and environment variables.\n\nWhen using Slurm, the port option needs to be set in one of the following ways:\n\n1. Set the port through `--options`. This is more recommended since it does not change the original configs.\n\n   ```shell\n   CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR} --cfg-options 'dist_params.port=29500'\n   CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR} --cfg-options 'dist_params.port=29501'\n   ```\n\n2. Modify the config files to set different communication ports.\n\n   In `config1.py`, set\n\n   ```python\n   dist_params = dict(backend='nccl', port=29500)\n   ```\n\n   In `config2.py`, set\n\n   ```python\n   dist_params = dict(backend='nccl', port=29501)\n   ```\n\n   Then you can launch two jobs with `config1.py` and `config2.py`.\n\n   ```shell\n   CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR}\n   CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR}\n   ```\n\n# Train with customized datasets\n\nIn this part, you will know how to train predefined models with customized datasets and then test it. We use the [balloon dataset](https://github.com/matterport/Mask_RCNN/tree/master/samples/balloon) as an example to describe the whole process.\n\nThe basic steps are as below:\n\n1. Prepare the customized dataset\n2. Prepare a config\n3. Train, test, and infer models on the customized dataset.\n\n## Prepare the customized dataset\n\nThere are three ways to support a new dataset in MMDetection:\n\n1. Reorganize the dataset into COCO format.\n2. Reorganize the dataset into a middle format.\n3. Implement a new dataset.\n\nUsually, we recommend using the first two methods which are usually easier than the third.\n\nIn this note, we give an example of converting the data into COCO format.\n\n**Note**: Datasets and metrics have been decoupled except CityScapes since MMDetection 3.0. Therefore, users can use any kind of evaluation metrics for any format of datasets during validation. For example: evaluate on COCO dataset with VOC metric, or evaluate on OpenImages dataset with both VOC and COCO metrics.\n\n### COCO annotation format\n\nThe necessary keys of COCO format for instance segmentation are as below, for the complete details, please refer [here](https://cocodataset.org/#format-data).\n\n```json\n{\n    \"images\": [image],\n    \"annotations\": [annotation],\n    \"categories\": [category]\n}\n\nimage = {\n    \"id\": int,\n    \"width\": int,\n    \"height\": int,\n    \"file_name\": str,\n}\n\nannotation = {\n    \"id\": int,\n    \"image_id\": int,\n    \"category_id\": int,\n    \"segmentation\": RLE or [polygon],\n    \"area\": float,\n    \"bbox\": [x,y,width,height], # (x, y) are the coordinates of the upper left corner of the bbox\n    \"iscrowd\": 0 or 1,\n}\n\ncategories = [{\n    \"id\": int,\n    \"name\": str,\n    \"supercategory\": str,\n}]\n```\n\nAssume we use the balloon dataset.\nAfter downloading the data, we need to implement a function to convert the annotation format into the COCO format. Then we can use implemented `CocoDataset` to load the data and perform training and evaluation.\n\nIf you take a look at the dataset, you will find the dataset format is as below:\n\n```json\n{'base64_img_data': '',\n 'file_attributes': {},\n 'filename': '34020010494_e5cb88e1c4_k.jpg',\n 'fileref': '',\n 'regions': {'0': {'region_attributes': {},\n   'shape_attributes': {'all_points_x': [1020,\n     1000,\n     994,\n     1003,\n     1023,\n     1050,\n     1089,\n     1134,\n     1190,\n     1265,\n     1321,\n     1361,\n     1403,\n     1428,\n     1442,\n     1445,\n     1441,\n     1427,\n     1400,\n     1361,\n     1316,\n     1269,\n     1228,\n     1198,\n     1207,\n     1210,\n     1190,\n     1177,\n     1172,\n     1174,\n     1170,\n     1153,\n     1127,\n     1104,\n     1061,\n     1032,\n     1020],\n    'all_points_y': [963,\n     899,\n     841,\n     787,\n     738,\n     700,\n     663,\n     638,\n     621,\n     619,\n     643,\n     672,\n     720,\n     765,\n     800,\n     860,\n     896,\n     942,\n     990,\n     1035,\n     1079,\n     1112,\n     1129,\n     1134,\n     1144,\n     1153,\n     1166,\n     1166,\n     1150,\n     1136,\n     1129,\n     1122,\n     1112,\n     1084,\n     1037,\n     989,\n     963],\n    'name': 'polygon'}}},\n 'size': 1115004}\n```\n\nThe annotation is a JSON file where each key indicates an image's all annotations.\nThe code to convert the balloon dataset into coco format is as below.\n\n```python\nimport os.path as osp\n\nimport mmcv\n\nfrom mmengine.fileio import dump, load\nfrom mmengine.utils import track_iter_progress\n\n\ndef convert_balloon_to_coco(ann_file, out_file, image_prefix):\n    data_infos = load(ann_file)\n\n    annotations = []\n    images = []\n    obj_count = 0\n    for idx, v in enumerate(track_iter_progress(data_infos.values())):\n        filename = v['filename']\n        img_path = osp.join(image_prefix, filename)\n        height, width = mmcv.imread(img_path).shape[:2]\n\n        images.append(\n            dict(id=idx, file_name=filename, height=height, width=width))\n\n        for _, obj in v['regions'].items():\n            assert not obj['region_attributes']\n            obj = obj['shape_attributes']\n            px = obj['all_points_x']\n            py = obj['all_points_y']\n            poly = [(x + 0.5, y + 0.5) for x, y in zip(px, py)]\n            poly = [p for x in poly for p in x]\n\n            x_min, y_min, x_max, y_max = (min(px), min(py), max(px), max(py))\n\n            data_anno = dict(\n                image_id=idx,\n                id=obj_count,\n                category_id=0,\n                bbox=[x_min, y_min, x_max - x_min, y_max - y_min],\n                area=(x_max - x_min) * (y_max - y_min),\n                segmentation=[poly],\n                iscrowd=0)\n            annotations.append(data_anno)\n            obj_count += 1\n\n    coco_format_json = dict(\n        images=images,\n        annotations=annotations,\n        categories=[{\n            'id': 0,\n            'name': 'balloon'\n        }])\n    dump(coco_format_json, out_file)\n\n\nif __name__ == '__main__':\n    convert_balloon_to_coco(ann_file='data/balloon/train/via_region_data.json',\n                            out_file='data/balloon/train/annotation_coco.json',\n                            image_prefix='data/balloon/train')\n    convert_balloon_to_coco(ann_file='data/balloon/val/via_region_data.json',\n                            out_file='data/balloon/val/annotation_coco.json',\n                            image_prefix='data/balloon/val')\n\n```\n\nUsing the function above, users can successfully convert the annotation file into json format, then we can use `CocoDataset` to train and evaluate the model with `CocoMetric`.\n\n## Prepare a config\n\nThe second step is to prepare a config thus the dataset could be successfully loaded. Assume that we want to use Mask R-CNN with FPN, the config to train the detector on balloon dataset is as below. Assume the config is under directory `configs/balloon/` and named as `mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py`, the config is as below. Please refer [Learn about Configs - MMDetection 3.0.0 documentation](https://mmdetection.readthedocs.io/en/latest/user_guides/config.html) to get detailed information about config files.\n\n```python\n# The new config inherits a base config to highlight the necessary modification\n_base_ = '../mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'\n\n# We also need to change the num_classes in head to match the dataset's annotation\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(num_classes=1), mask_head=dict(num_classes=1)))\n\n# Modify dataset related settings\ndata_root = 'data/balloon/'\nmetainfo = {\n    'classes': ('balloon', ),\n    'palette': [\n        (220, 20, 60),\n    ]\n}\ntrain_dataloader = dict(\n    batch_size=1,\n    dataset=dict(\n        data_root=data_root,\n        metainfo=metainfo,\n        ann_file='train/annotation_coco.json',\n        data_prefix=dict(img='train/')))\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        metainfo=metainfo,\n        ann_file='val/annotation_coco.json',\n        data_prefix=dict(img='val/')))\ntest_dataloader = val_dataloader\n\n# Modify metric related settings\nval_evaluator = dict(ann_file=data_root + 'val/annotation_coco.json')\ntest_evaluator = val_evaluator\n\n# We can use the pre-trained Mask RCNN model to obtain higher performance\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'\n\n```\n\n## Train a new model\n\nTo train a model with the new config, you can simply run\n\n```shell\npython tools/train.py configs/balloon/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py\n```\n\nFor more detailed usages, please refer to the [training guide](https://mmdetection.readthedocs.io/en/latest/user_guides/train.html#train-predefined-models-on-standard-datasets).\n\n## Test and inference\n\nTo test the trained model, you can simply run\n\n```shell\npython tools/test.py configs/balloon/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py work_dirs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon/epoch_12.pth\n```\n\nFor more detailed usages, please refer to the [testing guide](https://mmdetection.readthedocs.io/en/latest/user_guides/test.html).\n"
  },
  {
    "path": "docs/en/user_guides/useful_hooks.md",
    "content": "# Useful Hooks\n\nMMDetection and MMEngine provide users with various useful hooks including log hooks, `NumClassCheckHook`, etc. This tutorial introduces the functionalities and usages of hooks implemented in MMDetection. For using hooks in MMEngine, please read the [API documentation in MMEngine](https://github.com/open-mmlab/mmengine/tree/main/docs/en/tutorials/hook.md).\n\n## CheckInvalidLossHook\n\n## NumClassCheckHook\n\n## MemoryProfilerHook\n\n[Memory profiler hook](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/engine/hooks/memory_profiler_hook.py) records memory information including virtual memory, swap memory, and the memory of the current process. This hook helps grasp the memory usage of the system and discover potential memory leak bugs. To use this hook, users should install `memory_profiler` and `psutil` by `pip install memory_profiler psutil` first.\n\n### Usage\n\nTo use this hook, users should add the following code to the config file.\n\n```python\ncustom_hooks = [\n    dict(type='MemoryProfilerHook', interval=50)\n]\n```\n\n### Result\n\nDuring training, you can see the messages in the log recorded by `MemoryProfilerHook` as below.\n\n```text\nThe system has 250 GB (246360 MB + 9407 MB) of memory and 8 GB (5740 MB + 2452 MB) of swap memory in total. Currently 9407 MB (4.4%) of memory and 5740 MB (29.9%) of swap memory were consumed. And the current training process consumed 5434 MB of memory.\n```\n\n```text\n2022-04-21 08:49:56,881 - mmengine - INFO - Memory information available_memory: 246360 MB, used_memory: 9407 MB, memory_utilization: 4.4 %, available_swap_memory: 5740 MB, used_swap_memory: 2452 MB, swap_memory_utilization: 29.9 %, current_process_memory: 5434 MB\n```\n\n## SetEpochInfoHook\n\n## SyncNormHook\n\n## SyncRandomSizeHook\n\n## YOLOXLrUpdaterHook\n\n## YOLOXModeSwitchHook\n\n## How to implement a custom hook\n\nIn general, there are 20 points where hooks can be inserted from the beginning to the end of model training. The users can implement custom hooks and insert them at different points in the process of training to do what they want.\n\n- global points: `before_run`, `after_run`\n- points in training: `before_train`, `before_train_epoch`, `before_train_iter`, `after_train_iter`, `after_train_epoch`, `after_train`\n- points in validation: `before_val`, `before_val_epoch`, `before_val_iter`, `after_val_iter`, `after_val_epoch`, `after_val`\n- points at testing: `before_test`, `before_test_epoch`, `before_test_iter`, `after_test_iter`, `after_test_epoch`,  `after_test`\n- other points: `before_save_checkpoint`, `after_save_checkpoint`\n\nFor example, users can implement a hook to check loss and terminate training when loss goes NaN. To achieve that, there are three steps to go:\n\n1. Implement a new hook that inherits the `Hook` class in MMEngine, and implement `after_train_iter` method which checks whether loss goes NaN after every `n` training iterations.\n2. The implemented hook should be registered in `HOOKS` by `@HOOKS.register_module()` as shown in the code below.\n3. Add `custom_hooks = [dict(type='MemoryProfilerHook', interval=50)]` in the config file.\n\n```python\nfrom typing import Optional\n\nimport torch\nfrom mmengine.hooks import Hook\nfrom mmengine.runner import Runner\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass CheckInvalidLossHook(Hook):\n    \"\"\"Check invalid loss hook.\n\n    This hook will regularly check whether the loss is valid\n    during training.\n\n    Args:\n        interval (int): Checking interval (every k iterations).\n            Default: 50.\n    \"\"\"\n\n    def __init__(self, interval: int = 50) -> None:\n        self.interval = interval\n\n    def after_train_iter(self,\n                         runner: Runner,\n                         batch_idx: int,\n                         data_batch: Optional[dict] = None,\n                         outputs: Optional[dict] = None) -> None:\n        \"\"\"Regularly check whether the loss is valid every n iterations.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training process.\n            batch_idx (int): The index of the current batch in the train loop.\n            data_batch (dict, Optional): Data from dataloader.\n                Defaults to None.\n            outputs (dict, Optional): Outputs from model. Defaults to None.\n        \"\"\"\n        if self.every_n_train_iters(runner, self.interval):\n            assert torch.isfinite(outputs['loss']), \\\n                runner.logger.info('loss become infinite or NaN!')\n```\n\nPlease read [customize_runtime](../advanced_guides/customize_runtime.md) for more about implementing a custom hook.\n"
  },
  {
    "path": "docs/en/user_guides/useful_tools.md",
    "content": "Apart from training/testing scripts, We provide lots of useful tools under the\n`tools/` directory.\n\n## Log Analysis\n\n`tools/analysis_tools/analyze_logs.py` plots loss/mAP curves given a training\nlog file. Run `pip install seaborn` first to install the dependency.\n\n```shell\npython tools/analysis_tools/analyze_logs.py plot_curve [--keys ${KEYS}] [--eval-interval ${EVALUATION_INTERVAL}] [--title ${TITLE}] [--legend ${LEGEND}] [--backend ${BACKEND}] [--style ${STYLE}] [--out ${OUT_FILE}]\n```\n\n![loss curve image](../../../resources/loss_curve.png)\n\nExamples:\n\n- Plot the classification loss of some run.\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py plot_curve log.json --keys loss_cls --legend loss_cls\n  ```\n\n- Plot the classification and regression loss of some run, and save the figure to a pdf.\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py plot_curve log.json --keys loss_cls loss_bbox --out losses.pdf\n  ```\n\n- Compare the bbox mAP of two runs in the same figure.\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py plot_curve log1.json log2.json --keys bbox_mAP --legend run1 run2\n  ```\n\n- Compute the average training speed.\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py cal_train_time log.json [--include-outliers]\n  ```\n\n  The output is expected to be like the following.\n\n  ```text\n  -----Analyze train time of work_dirs/some_exp/20190611_192040.log.json-----\n  slowest epoch 11, average time is 1.2024\n  fastest epoch 1, average time is 1.1909\n  time std over epochs is 0.0028\n  average iter time: 1.1959 s/iter\n  ```\n\n## Result Analysis\n\n`tools/analysis_tools/analyze_results.py` calculates single image mAP and saves or shows the topk images with the highest and lowest scores based on prediction results.\n\n**Usage**\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n      ${CONFIG} \\\n      ${PREDICTION_PATH} \\\n      ${SHOW_DIR} \\\n      [--show] \\\n      [--wait-time ${WAIT_TIME}] \\\n      [--topk ${TOPK}] \\\n      [--show-score-thr ${SHOW_SCORE_THR}] \\\n      [--cfg-options ${CFG_OPTIONS}]\n```\n\nDescription of all arguments:\n\n- `config` : The path of a model config file.\n- `prediction_path`:  Output result file in pickle format from `tools/test.py`\n- `show_dir`: Directory where painted GT and detection images will be saved\n- `--show`: Determines whether to show painted images, If not specified, it will be set to `False`\n- `--wait-time`: The interval of show (s), 0 is block\n- `--topk`: The number of saved images that have the highest and lowest `topk` scores after sorting. If not specified, it will be set to `20`.\n- `--show-score-thr`:  Show score threshold. If not specified, it will be set to `0`.\n- `--cfg-options`: If specified, the key-value pair optional cfg will be merged into config file\n\n**Examples**:\n\nAssume that you have got result file in pickle format from `tools/test.py`  in the path './result.pkl'.\n\n1. Test Faster R-CNN and visualize the results, save images to the directory `results/`\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       result.pkl \\\n       results \\\n       --show\n```\n\n2. Test Faster R-CNN and specified topk to 50, save images to the directory `results/`\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       result.pkl \\\n       results \\\n       --topk 50\n```\n\n3. If you want to filter the low score prediction results, you can specify the `show-score-thr` parameter\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       result.pkl \\\n       results \\\n       --show-score-thr 0.3\n```\n\n## Fusing results from multiple models\n\n`tools/analysis_tools/fusion_results.py` can fusing predictions using Weighted Boxes Fusion(WBF) from different object detection models. (Currently support coco format only)\n\n**Usage**\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ${PRED_RESULTS} \\\n       [--annotation ${ANNOTATION}] \\\n       [--weights ${WEIGHTS}] \\\n       [--fusion-iou-thr ${FUSION_IOU_THR}] \\\n       [--skip-box-thr ${SKIP_BOX_THR}] \\\n       [--conf-type ${CONF_TYPE}] \\\n       [--eval-single ${EVAL_SINGLE}] \\\n       [--save-fusion-results ${SAVE_FUSION_RESULTS}] \\\n       [--out-dir ${OUT_DIR}]\n```\n\nDescription of all arguments:\n\n- `pred-results`: Paths of detection results from different models.(Currently support coco format only)\n- `--annotation`: Path of ground-truth.\n- `--weights`: List of weights for each model. Default: `None`, which means weight == 1 for each model.\n- `--fusion-iou-thr`: IoU value for boxes to be a match。Default: `0.55`。\n- `--skip-box-thr`: The confidence threshold that needs to be excluded in the WBF algorithm. bboxes whose confidence is less than this value will be excluded.。Default: `0`。\n- `--conf-type`: How to calculate confidence in weighted boxes.\n  - `avg`: average value，default.\n  - `max`: maximum value.\n  - `box_and_model_avg`: box and model wise hybrid weighted average.\n  - `absent_model_aware_avg`: weighted average that takes into account the absent model.\n- `--eval-single`: Whether evaluate every single model. Default: `False`.\n- `--save-fusion-results`: Whether save fusion results. Default: `False`.\n- `--out-dir`: Path of fusion results.\n\n**Examples**:\nAssume that you have got 3 result files from corresponding models through `tools/test.py`, which paths are './faster-rcnn_r50-caffe_fpn_1x_coco.json', './retinanet_r50-caffe_fpn_1x_coco.json', './cascade-rcnn_r50-caffe_fpn_1x_coco.json' respectively. The ground-truth file path is './annotation.json'.\n\n1. Fusion of predictions from three models and evaluation of their effectiveness\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ./faster-rcnn_r50-caffe_fpn_1x_coco.json \\\n       ./retinanet_r50-caffe_fpn_1x_coco.json \\\n       ./cascade-rcnn_r50-caffe_fpn_1x_coco.json \\\n       --annotation ./annotation.json \\\n       --weights 1 2 3 \\\n```\n\n2. Simultaneously evaluate each single model and fusion results\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ./faster-rcnn_r50-caffe_fpn_1x_coco.json \\\n       ./retinanet_r50-caffe_fpn_1x_coco.json \\\n       ./cascade-rcnn_r50-caffe_fpn_1x_coco.json \\\n       --annotation ./annotation.json \\\n       --weights 1 2 3 \\\n       --eval-single\n```\n\n3. Fusion of prediction results from three models and save\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ./faster-rcnn_r50-caffe_fpn_1x_coco.json \\\n       ./retinanet_r50-caffe_fpn_1x_coco.json \\\n       ./cascade-rcnn_r50-caffe_fpn_1x_coco.json \\\n       --annotation ./annotation.json \\\n       --weights 1 2 3 \\\n       --save-fusion-results \\\n       --out-dir outputs/fusion\n```\n\n## Visualization\n\n### Visualize Datasets\n\n`tools/analysis_tools/browse_dataset.py` helps the user to browse a detection dataset (both\nimages and bounding box annotations) visually, or save the image to a\ndesignated directory.\n\n```shell\npython tools/analysis_tools/browse_dataset.py ${CONFIG} [-h] [--skip-type ${SKIP_TYPE[SKIP_TYPE...]}] [--output-dir ${OUTPUT_DIR}] [--not-show] [--show-interval ${SHOW_INTERVAL}]\n```\n\n### Visualize Models\n\nFirst, convert the model to ONNX as described\n[here](#convert-mmdetection-model-to-onnx-experimental).\nNote that currently only RetinaNet is supported, support for other models\nwill be coming in later versions.\nThe converted model could be visualized by tools like [Netron](https://github.com/lutzroeder/netron).\n\n### Visualize Predictions\n\nIf you need a lightweight GUI for visualizing the detection results, you can refer [DetVisGUI project](https://github.com/Chien-Hung/DetVisGUI/tree/mmdetection).\n\n## Error Analysis\n\n`tools/analysis_tools/coco_error_analysis.py` analyzes COCO results per category and by\ndifferent criterion. It can also make a plot to provide useful information.\n\n```shell\npython tools/analysis_tools/coco_error_analysis.py ${RESULT} ${OUT_DIR} [-h] [--ann ${ANN}] [--types ${TYPES[TYPES...]}]\n```\n\nExample:\n\nAssume that you have got [Mask R-CNN checkpoint file](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth) in the path 'checkpoint'. For other checkpoints, please refer to our [model zoo](./model_zoo.md).\n\nYou can modify the test_evaluator to save the results bbox by:\n\n1. Find which dataset in 'configs/base/datasets' the current config corresponds to.\n2. Replace the original test_evaluator and test_dataloader with test_evaluator and test_dataloader in the comment in dataset config.\n3. Use the following command to get the results bbox and segmentation json file.\n\n```shell\npython tools/test.py \\\n       configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py \\\n       checkpoint/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n```\n\n1. Get COCO bbox error results per category , save analyze result images to the directory(In  [config](../../../configs/_base_/datasets/coco_instance.py) the default directory is './work_dirs/coco_instance/test')\n\n```shell\npython tools/analysis_tools/coco_error_analysis.py \\\n       results.bbox.json \\\n       results \\\n       --ann=data/coco/annotations/instances_val2017.json \\\n```\n\n2. Get COCO segmentation error results per category , save analyze result images to the directory\n\n```shell\npython tools/analysis_tools/coco_error_analysis.py \\\n       results.segm.json \\\n       results \\\n       --ann=data/coco/annotations/instances_val2017.json \\\n       --types='segm'\n```\n\n## Model Serving\n\nIn order to serve an `MMDetection` model with [`TorchServe`](https://pytorch.org/serve/), you can follow the steps:\n\n### 1. Install TorchServe\n\nSuppose you have a `Python` environment with `PyTorch` and `MMDetection` successfully installed,\nthen you could run the following command to install `TorchServe` and its dependencies.\nFor more other installation options, please refer to the [quick start](https://github.com/pytorch/serve/blob/master/README.md#serve-a-model).\n\n```shell\npython -m pip install torchserve torch-model-archiver torch-workflow-archiver nvgpu\n```\n\n**Note**: Please refer to [torchserve docker](https://github.com/pytorch/serve/blob/master/docker/README.md) if you want to use `TorchServe` in docker.\n\n### 2. Convert model from MMDetection to TorchServe\n\n```shell\npython tools/deployment/mmdet2torchserve.py ${CONFIG_FILE} ${CHECKPOINT_FILE} \\\n--output-folder ${MODEL_STORE} \\\n--model-name ${MODEL_NAME}\n```\n\n### 3. Start `TorchServe`\n\n```shell\ntorchserve --start --ncs \\\n  --model-store ${MODEL_STORE} \\\n  --models  ${MODEL_NAME}.mar\n```\n\n### 4. Test deployment\n\n```shell\ncurl -O curl -O https://raw.githubusercontent.com/pytorch/serve/master/docs/images/3dogs.jpg\ncurl http://127.0.0.1:8080/predictions/${MODEL_NAME} -T 3dogs.jpg\n```\n\nYou should obtain a response similar to:\n\n```json\n[\n  {\n    \"class_label\": 16,\n    \"class_name\": \"dog\",\n    \"bbox\": [\n      294.63409423828125,\n      203.99111938476562,\n      417.048583984375,\n      281.62744140625\n    ],\n    \"score\": 0.9987992644309998\n  },\n  {\n    \"class_label\": 16,\n    \"class_name\": \"dog\",\n    \"bbox\": [\n      404.26019287109375,\n      126.0080795288086,\n      574.5091552734375,\n      293.6662292480469\n    ],\n    \"score\": 0.9979367256164551\n  },\n  {\n    \"class_label\": 16,\n    \"class_name\": \"dog\",\n    \"bbox\": [\n      197.2144775390625,\n      93.3067855834961,\n      307.8505554199219,\n      276.7560119628906\n    ],\n    \"score\": 0.993338406085968\n  }\n]\n```\n\n#### Compare results\n\nAnd you can use `test_torchserver.py` to compare result of `TorchServe` and `PyTorch`, and visualize them.\n\n```shell\npython tools/deployment/test_torchserver.py ${IMAGE_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} ${MODEL_NAME}\n[--inference-addr ${INFERENCE_ADDR}] [--device ${DEVICE}] [--score-thr ${SCORE_THR}] [--work-dir ${WORK_DIR}]\n```\n\nExample:\n\n```shell\npython tools/deployment/test_torchserver.py \\\ndemo/demo.jpg \\\nconfigs/yolo/yolov3_d53_8xb8-320-273e_coco.py \\\ncheckpoint/yolov3_d53_320_273e_coco-421362b6.pth \\\nyolov3 \\\n--work-dir ./work-dir\n```\n\n### 5. Stop `TorchServe`\n\n```shell\ntorchserve --stop\n```\n\n## Model Complexity\n\n`tools/analysis_tools/get_flops.py` is a script adapted from [flops-counter.pytorch](https://github.com/sovrasov/flops-counter.pytorch) to compute the FLOPs and params of a given model.\n\n```shell\npython tools/analysis_tools/get_flops.py ${CONFIG_FILE} [--shape ${INPUT_SHAPE}]\n```\n\nYou will get the results like this.\n\n```text\n==============================\nInput shape: (3, 1280, 800)\nFlops: 239.32 GFLOPs\nParams: 37.74 M\n==============================\n```\n\n**Note**: This tool is still experimental and we do not guarantee that the\nnumber is absolutely correct. You may well use the result for simple\ncomparisons, but double check it before you adopt it in technical reports or papers.\n\n1. FLOPs are related to the input shape while parameters are not. The default\n   input shape is (1, 3, 1280, 800).\n2. Some operators are not counted into FLOPs like GN and custom operators. Refer to [`mmcv.cnn.get_model_complexity_info()`](https://github.com/open-mmlab/mmcv/blob/2.x/mmcv/cnn/utils/flops_counter.py) for details.\n3. The FLOPs of two-stage detectors is dependent on the number of proposals.\n\n## Model conversion\n\n### MMDetection model to ONNX\n\nWe provide a script to convert model to [ONNX](https://github.com/onnx/onnx) format. We also support comparing the output results between Pytorch and ONNX model for verification. More details can refer to [mmdeploy](https://github.com/open-mmlab/mmdeploy)\n\n### MMDetection 1.x model to MMDetection 2.x\n\n`tools/model_converters/upgrade_model_version.py` upgrades a previous MMDetection checkpoint\nto the new version. Note that this script is not guaranteed to work as some\nbreaking changes are introduced in the new version. It is recommended to\ndirectly use the new checkpoints.\n\n```shell\npython tools/model_converters/upgrade_model_version.py ${IN_FILE} ${OUT_FILE} [-h] [--num-classes NUM_CLASSES]\n```\n\n### RegNet model to MMDetection\n\n`tools/model_converters/regnet2mmdet.py` convert keys in pycls pretrained RegNet models to\nMMDetection style.\n\n```shell\npython tools/model_converters/regnet2mmdet.py ${SRC} ${DST} [-h]\n```\n\n### Detectron ResNet to Pytorch\n\n`tools/model_converters/detectron2pytorch.py` converts keys in the original detectron pretrained\nResNet models to PyTorch style.\n\n```shell\npython tools/model_converters/detectron2pytorch.py ${SRC} ${DST} ${DEPTH} [-h]\n```\n\n### Prepare a model for publishing\n\n`tools/model_converters/publish_model.py` helps users to prepare their model for publishing.\n\nBefore you upload a model to AWS, you may want to\n\n1. convert model weights to CPU tensors\n2. delete the optimizer states and\n3. compute the hash of the checkpoint file and append the hash id to the\n   filename.\n\n```shell\npython tools/model_converters/publish_model.py ${INPUT_FILENAME} ${OUTPUT_FILENAME}\n```\n\nE.g.,\n\n```shell\npython tools/model_converters/publish_model.py work_dirs/faster_rcnn/latest.pth faster_rcnn_r50_fpn_1x_20190801.pth\n```\n\nThe final output filename will be `faster_rcnn_r50_fpn_1x_20190801-{hash id}.pth`.\n\n## Dataset Conversion\n\n`tools/data_converters/` contains tools to convert the Cityscapes dataset\nand Pascal VOC dataset to the COCO format.\n\n```shell\npython tools/dataset_converters/cityscapes.py ${CITYSCAPES_PATH} [-h] [--img-dir ${IMG_DIR}] [--gt-dir ${GT_DIR}] [-o ${OUT_DIR}] [--nproc ${NPROC}]\npython tools/dataset_converters/pascal_voc.py ${DEVKIT_PATH} [-h] [-o ${OUT_DIR}]\n```\n\n## Dataset Download\n\n`tools/misc/download_dataset.py` supports downloading datasets such as COCO, VOC, and LVIS.\n\n```shell\npython tools/misc/download_dataset.py --dataset-name coco2017\npython tools/misc/download_dataset.py --dataset-name voc2007\npython tools/misc/download_dataset.py --dataset-name lvis\n```\n\nFor users in China, these datasets can also be downloaded from [OpenDataLab](https://opendatalab.com/?source=OpenMMLab%20GitHub) with high speed:\n\n- [COCO2017](https://opendatalab.com/COCO_2017/download?source=OpenMMLab%20GitHub)\n- [VOC2007](https://opendatalab.com/PASCAL_VOC2007/download?source=OpenMMLab%20GitHub)\n- [VOC2012](https://opendatalab.com/PASCAL_VOC2012/download?source=OpenMMLab%20GitHub)\n- [LVIS](https://opendatalab.com/LVIS/download?source=OpenMMLab%20GitHub)\n\n## Benchmark\n\n### Robust Detection Benchmark\n\n`tools/analysis_tools/test_robustness.py` and`tools/analysis_tools/robustness_eval.py`  helps users to evaluate model robustness. The core idea comes from [Benchmarking Robustness in Object Detection: Autonomous Driving when Winter is Coming](https://arxiv.org/abs/1907.07484). For more information how to evaluate models on corrupted images and results for a set of standard models please refer to [robustness_benchmarking.md](robustness_benchmarking.md).\n\n### FPS Benchmark\n\n`tools/analysis_tools/benchmark.py` helps users to calculate FPS. The FPS value includes model forward and post-processing. In order to get a more accurate value, currently only supports single GPU distributed startup mode.\n\n```shell\npython -m torch.distributed.launch --nproc_per_node=1 --master_port=${PORT} tools/analysis_tools/benchmark.py \\\n    ${CONFIG} \\\n    [--checkpoint ${CHECKPOINT}] \\\n    [--repeat-num ${REPEAT_NUM}] \\\n    [--max-iter ${MAX_ITER}] \\\n    [--log-interval ${LOG_INTERVAL}] \\\n    --launcher pytorch\n```\n\nExamples: Assuming that you have already downloaded the `Faster R-CNN` model checkpoint to the directory `checkpoints/`.\n\n```shell\npython -m torch.distributed.launch --nproc_per_node=1 --master_port=29500 tools/analysis_tools/benchmark.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth \\\n       --launcher pytorch\n```\n\n## Miscellaneous\n\n### Evaluating a metric\n\n`tools/analysis_tools/eval_metric.py` evaluates certain metrics of a pkl result file\naccording to a config file.\n\n```shell\npython tools/analysis_tools/eval_metric.py ${CONFIG} ${PKL_RESULTS} [-h] [--format-only] [--eval ${EVAL[EVAL ...]}]\n                      [--cfg-options ${CFG_OPTIONS [CFG_OPTIONS ...]}]\n                      [--eval-options ${EVAL_OPTIONS [EVAL_OPTIONS ...]}]\n```\n\n### Print the entire config\n\n`tools/misc/print_config.py` prints the whole config verbatim, expanding all its\nimports.\n\n```shell\npython tools/misc/print_config.py ${CONFIG} [-h] [--options ${OPTIONS [OPTIONS...]}]\n```\n\n## Hyper-parameter Optimization\n\n### YOLO Anchor Optimization\n\n`tools/analysis_tools/optimize_anchors.py` provides two method to optimize YOLO anchors.\n\nOne is k-means anchor cluster which refers from [darknet](https://github.com/AlexeyAB/darknet/blob/master/src/detector.c#L1421).\n\n```shell\npython tools/analysis_tools/optimize_anchors.py ${CONFIG} --algorithm k-means --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} --output-dir ${OUTPUT_DIR}\n```\n\nAnother is using differential evolution to optimize anchors.\n\n```shell\npython tools/analysis_tools/optimize_anchors.py ${CONFIG} --algorithm differential_evolution --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} --output-dir ${OUTPUT_DIR}\n```\n\nE.g.,\n\n```shell\npython tools/analysis_tools/optimize_anchors.py configs/yolo/yolov3_d53_8xb8-320-273e_coco.py --algorithm differential_evolution --input-shape 608 608 --device cuda --output-dir work_dirs\n```\n\nYou will get:\n\n```\nloading annotations into memory...\nDone (t=9.70s)\ncreating index...\nindex created!\n2021-07-19 19:37:20,951 - mmdet - INFO - Collecting bboxes from annotation...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 117266/117266, 15874.5 task/s, elapsed: 7s, ETA:     0s\n\n2021-07-19 19:37:28,753 - mmdet - INFO - Collected 849902 bboxes.\ndifferential_evolution step 1: f(x)= 0.506055\ndifferential_evolution step 2: f(x)= 0.506055\n......\n\ndifferential_evolution step 489: f(x)= 0.386625\n2021-07-19 19:46:40,775 - mmdet - INFO Anchor evolution finish. Average IOU: 0.6133754253387451\n2021-07-19 19:46:40,776 - mmdet - INFO Anchor differential evolution result:[[10, 12], [15, 30], [32, 22], [29, 59], [61, 46], [57, 116], [112, 89], [154, 198], [349, 336]]\n2021-07-19 19:46:40,798 - mmdet - INFO Result saved in work_dirs/anchor_optimize_result.json\n```\n\n## Confusion Matrix\n\nA confusion matrix is a summary of prediction results.\n\n`tools/analysis_tools/confusion_matrix.py` can analyze the prediction results and plot a confusion matrix table.\n\nFirst, run `tools/test.py` to save the `.pkl` detection results.\n\nThen, run\n\n```\npython tools/analysis_tools/confusion_matrix.py ${CONFIG}  ${DETECTION_RESULTS}  ${SAVE_DIR} --show\n```\n\nAnd you will get a confusion matrix like this:\n\n![confusion_matrix_example](https://user-images.githubusercontent.com/12907710/140513068-994cdbf4-3a4a-48f0-8fd8-2830d93fd963.png)\n\n## COCO Separated & Occluded Mask Metric\n\nDetecting occluded objects still remains a challenge for state-of-the-art object detectors.\nWe implemented the metric presented in paper [A Tri-Layer Plugin to Improve Occluded Detection](https://arxiv.org/abs/2210.10046) to calculate the recall of separated and occluded masks.\n\nThere are two ways to use this metric:\n\n### Offline evaluation\n\nWe provide a script to calculate the metric with a dumped prediction file.\n\nFirst, use the `tools/test.py` script to dump the detection results:\n\n```shell\npython tools/test.py ${CONFIG} ${MODEL_PATH} --out results.pkl\n```\n\nThen, run the `tools/analysis_tools/coco_occluded_separated_recall.py` script to get the recall of separated and occluded masks:\n\n```shell\npython tools/analysis_tools/coco_occluded_separated_recall.py results.pkl --out occluded_separated_recall.json\n```\n\nThe output should be like this:\n\n```\nloading annotations into memory...\nDone (t=0.51s)\ncreating index...\nindex created!\nprocessing detection results...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 5000/5000, 109.3 task/s, elapsed: 46s, ETA:     0s\ncomputing occluded mask recall...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 5550/5550, 780.5 task/s, elapsed: 7s, ETA:     0s\nCOCO occluded mask recall: 58.79%\nCOCO occluded mask success num: 3263\ncomputing separated mask recall...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 3522/3522, 778.3 task/s, elapsed: 5s, ETA:     0s\nCOCO separated mask recall: 31.94%\nCOCO separated mask success num: 1125\n\n+-----------+--------+-------------+\n| mask type | recall | num correct |\n+-----------+--------+-------------+\n| occluded  | 58.79% | 3263        |\n| separated | 31.94% | 1125        |\n+-----------+--------+-------------+\nEvaluation results have been saved to occluded_separated_recall.json.\n```\n\n### Online evaluation\n\nWe implement `CocoOccludedSeparatedMetric` which inherits from the `CocoMetic`.\nTo evaluate the recall of separated and occluded masks during training, just replace the evaluator metric type with `'CocoOccludedSeparatedMetric'` in your config:\n\n```python\nval_evaluator = dict(\n    type='CocoOccludedSeparatedMetric',  # modify this\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False)\ntest_evaluator = val_evaluator\n```\n\nPlease cite the paper if you use this metric:\n\n```latex\n@article{zhan2022triocc,\n    title={A Tri-Layer Plugin to Improve Occluded Detection},\n    author={Zhan, Guanqi and Xie, Weidi and Zisserman, Andrew},\n    journal={British Machine Vision Conference},\n    year={2022}\n}\n```\n"
  },
  {
    "path": "docs/en/user_guides/visualization.md",
    "content": "# Visualization\n\nBefore reading this tutorial, it is recommended to read MMEngine's [Visualization](https://github.com/open-mmlab/mmengine/blob/main/docs/en/advanced_tutorials/visualization.md) documentation to get a first glimpse of the `Visualizer` definition and usage.\n\nIn brief, the [`Visualizer`](mmengine.visualization.Visualizer) is implemented in MMEngine to meet the daily visualization needs, and contains three main functions:\n\n- Implement common drawing APIs, such as [`draw_bboxes`](mmengine.visualization.Visualizer.draw_bboxes) which implements bounding box drawing functions, [`draw_lines`](mmengine.visualization.Visualizer.draw_lines) implements the line drawing function.\n- Support writing visualization results, learning rate curves, loss function curves, and verification accuracy curves to various backends, including local disks and common deep learning training logging tools such as [TensorBoard](https://www.tensorflow.org/tensorboard) and [Wandb](https://wandb.ai/site).\n- Support calling anywhere in the code to visualize or record intermediate states of the model during training or testing, such as feature maps and validation results.\n\nBased on MMEngine's Visualizer, MMDet comes with a variety of pre-built visualization tools that can be used by the user by simply modifying the following configuration files.\n\n- The `tools/analysis_tools/browse_dataset.py` script provides a dataset visualization function that draws images and corresponding annotations after Data Transforms, as described in [`browse_dataset.py`](useful_tools.md#Visualization).\n- MMEngine implements `LoggerHook`, which uses `Visualizer` to write the learning rate, loss and evaluation results to the backend set by `Visualizer`. Therefore, by modifying the `Visualizer` backend in the configuration file, for example to ` TensorBoardVISBackend` or `WandbVISBackend`, you can implement logging to common training logging tools such as `TensorBoard` or `WandB`, thus making it easy for users to use these visualization tools to analyze and monitor the training process.\n- The `VisualizerHook` is implemented in MMDet, which uses the `Visualizer` to visualize or store the prediction results of the validation or prediction phase into the backend set by the `Visualizer`, so by modifying the `Visualizer` backend in the configuration file, for example, to ` TensorBoardVISBackend` or `WandbVISBackend`, you can implement storing the predicted images to `TensorBoard` or `Wandb`.\n\n## Configuration\n\nThanks to the use of the registration mechanism, in MMDet we can set the behavior of the `Visualizer` by modifying the configuration file. Usually, we define the default configuration for the visualizer in `configs/_base_/default_runtime.py`, see [configuration tutorial](config.md) for details.\n\n```Python\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='DetLocalVisualizer',\n    vis_backends=vis_backends,\n    name='visualizer')\n```\n\nBased on the above example, we can see that the configuration of `Visualizer` consists of two main parts, namely, the type of `Visualizer` and the visualization backend `vis_backends` it uses.\n\n- Users can directly use `DetLocalVisualizer` to visualize labels or predictions for support tasks.\n- MMDet sets the visualization backend `vis_backend` to the local visualization backend `LocalVisBackend` by default, saving all visualization results and other training information in a local folder.\n\n## Storage\n\nMMDet uses the local visualization backend [`LocalVisBackend`](mmengine.visualization.LocalVisBackend) by default, and the model loss, learning rate, model evaluation accuracy and visualization The information stored in `VisualizerHook` and `LoggerHook`, including loss, learning rate, evaluation accuracy will be saved to the `{work_dir}/{config_name}/{time}/{vis_data}` folder by default. In addition, MMDet also supports other common visualization backends, such as `TensorboardVisBackend` and `WandbVisBackend`, and you only need to change the `vis_backends` type in the configuration file to the corresponding visualization backend. For example, you can store data to `TensorBoard` and `Wandb` by simply inserting the following code block into the configuration file.\n\n```Python\n# https://mmengine.readthedocs.io/en/latest/api/visualization.html\n_base_.visualizer.vis_backends = [\n    dict(type='LocalVisBackend'), #\n    dict(type='TensorboardVisBackend'),\n    dict(type='WandbVisBackend'),]\n```\n\n## Plot\n\n### Plot the prediction results\n\nMMDet mainly uses [`DetVisualizationHook`](mmdet.engine.hooks.DetVisualizationHook) to plot the prediction results of validation and test, by default `DetVisualizationHook` is off, and the default configuration is as follows.\n\n```Python\nvisualization=dict( # user visualization of validation and test results\n    type='DetVisualizationHook',\n    draw=False,\n    interval=1,\n    show=False)\n```\n\nThe following table shows the parameters supported by `DetVisualizationHook`.\n\n| Parameters |                                                  Description                                                  |\n| :--------: | :-----------------------------------------------------------------------------------------------------------: |\n|    draw    |      The DetVisualizationHook is turned on and off by the enable parameter, which is the default state.       |\n|  interval  | Controls how much iteration to store or display the results of a val or test if VisualizationHook is enabled. |\n|    show    |                           Controls whether to visualize the results of val or test.                           |\n\nIf you want to enable `DetVisualizationHook` related functions and configurations during training or testing, you only need to modify the configuration, take `configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py` as an example, draw annotations and predictions at the same time, and display the images, the configuration can be modified as follows\n\n```Python\nvisualization = _base_.default_hooks.visualization\nvisualization.update(dict(draw=True, show=True))\n```\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/224883427-1294a7ba-14ab-4d93-9152-55a7b270b1f1.png\" height=\"300\"/>\n</div>\n\nThe `test.py` procedure is further simplified by providing the  `--show` and `--show-dir` parameters to visualize the annotation and prediction results during the test without modifying the configuration.\n\n```Shell\n# Show test results\npython tools/test.py configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_tiny_8xb32-300e_coco/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth --show\n\n# Specify where to store the prediction results\npython tools/test.py configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_tiny_8xb32-300e_coco/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth --show-dir imgs/\n```\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/224883427-1294a7ba-14ab-4d93-9152-55a7b270b1f1.png\" height=\"300\"/>\n</div>\n"
  },
  {
    "path": "docs/zh_cn/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    ?=\nSPHINXBUILD   ?= sphinx-build\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "docs/zh_cn/_static/css/readthedocs.css",
    "content": ".header-logo {\n    background-image: url(\"../image/mmdet-logo.png\");\n    background-size: 156px 40px;\n    height: 40px;\n    width: 156px;\n}\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/conventions.md",
    "content": "# 默认约定\n\n如果你想把 MMDetection 修改为自己的项目，请遵循下面的约定。\n\n## 关于图片 shape 顺序的说明\n\n在OpenMMLab 2.0中， 为了与 OpenCV 的输入参数相一致，图片处理 pipeline 中关于图像 shape 的输入参数总是以 `(width, height)` 的顺序排列。\n相反，为了计算方便，经过 pipeline 和 model 的字段的顺序是 `(height, width)`。具体来说在每个数据 pipeline 处理的结果中，字段和它们的值含义如下：\n\n- img_shape: (height, width)\n- ori_shape: (height, width)\n- pad_shape: (height, width)\n- batch_input_shape: (height, width)\n\n以 `Mosaic` 为例，其初始化参数如下所示：\n\n```python\n@TRANSFORMS.register_module()\nclass Mosaic(BaseTransform):\n    def __init__(self,\n                img_scale: Tuple[int, int] = (640, 640),\n                center_ratio_range: Tuple[float, float] = (0.5, 1.5),\n                bbox_clip_border: bool = True,\n                pad_val: float = 114.0,\n                prob: float = 1.0) -> None:\n       ...\n\n       # img_scale 顺序应该是 (width, height)\n       self.img_scale = img_scale\n\n    def transform(self, results: dict) -> dict:\n        ...\n\n        results['img'] = mosaic_img\n        # (height, width)\n        results['img_shape'] = mosaic_img.shape[:2]\n```\n\n## 损失\n\n在 MMDetection 中，`model(**data)` 的返回值是一个字典，包含着所有的损失和评价指标，他们将会由 `model(**data)` 返回。\n\n例如，在 bbox head 中，\n\n```python\nclass BBoxHead(nn.Module):\n    ...\n    def loss(self, ...):\n        losses = dict()\n        # 分类损失\n        losses['loss_cls'] = self.loss_cls(...)\n        # 分类准确率\n        losses['acc'] = accuracy(...)\n        # 边界框损失\n        losses['loss_bbox'] = self.loss_bbox(...)\n        return losses\n```\n\n`'bbox_head.loss()'` 在模型 forward 阶段会被调用。返回的字典中包含了 `'loss_bbox'`,`'loss_cls'`,`'acc'`。只有 `'loss_bbox'`, `'loss_cls'` 会被用于反向传播，`'acc'` 只会被作为评价指标来监控训练过程。\n\n我们默认，只有那些键的名称中包含 `'loss'` 的值会被用于反向传播。这个行为可以通过修改 `BaseDetector.train_step()` 来改变。\n\n## 空 proposals\n\n在 MMDetection 中，我们为两阶段方法中空 proposals 的情况增加了特殊处理和单元测试。我们同时需要处理整个 batch 和单一图片中空 proposals 的情况。例如，在 CascadeRoIHead 中，\n\n```python\n# 简单的测试\n...\n\n# 在整个 batch中 都没有 proposals\nif rois.shape[0] == 0:\n    bbox_results = [[\n        np.zeros((0, 5), dtype=np.float32)\n        for _ in range(self.bbox_head[-1].num_classes)\n    ]] * num_imgs\n    if self.with_mask:\n        mask_classes = self.mask_head[-1].num_classes\n        segm_results = [[[] for _ in range(mask_classes)]\n                        for _ in range(num_imgs)]\n        results = list(zip(bbox_results, segm_results))\n    else:\n        results = bbox_results\n    return results\n...\n\n# 在单张图片中没有 proposals\nfor i in range(self.num_stages):\n    ...\n    if i < self.num_stages - 1:\n          for j in range(num_imgs):\n                # 处理空 proposals\n                if rois[j].shape[0] > 0:\n                    bbox_label = cls_score[j][:, :-1].argmax(dim=1)\n                    refine_roi = self.bbox_head[i].regress_by_class(\n                         rois[j], bbox_label[j], bbox_pred[j], img_metas[j])\n                    refine_roi_list.append(refine_roi)\n```\n\n如果你有自定义的 `RoIHead`, 你可以参考上面的方法来处理空 proposals 的情况。\n\n## 全景分割数据集\n\n在 MMDetection 中，我们支持了 COCO 全景分割数据集 `CocoPanopticDataset`。对于它的实现，我们在这里声明一些默认约定。\n\n1. 在 mmdet\\<=2.16.0 时，语义分割标注中的前景和背景标签范围与 MMDetection 中的默认规定有所不同。标签 `0` 代表 `VOID` 标签。\n   从 mmdet=2.17.0 开始，为了和框的类别标注保持一致，语义分割标注的类别标签也改为从 `0` 开始，标签 `255` 代表 `VOID` 类。\n   为了达成这一目标，我们在流程 `Pad` 里支持了设置 `seg` 的填充值的功能。\n2. 在评估中，全景分割结果必须是一个与原图大小相同的图。结果图中每个像素的值有如此形式：`instance_id * INSTANCE_OFFSET + category_id`。\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/customize_dataset.md",
    "content": "# 自定义数据集\n\n## 支持新的数据格式\n\n为了支持新的数据格式，可以选择将数据转换成现成的格式（COCO 或者 PASCAL）或将其转换成中间格式。当然也可以选择以离线的形式（在训练之前使用脚本转换）或者在线的形式（实现一个新的 dataset 在训练中进行转换）来转换数据。\n\n在 MMDetection 中，建议将数据转换成 COCO 格式并以离线的方式进行，因此在完成数据转换后只需修改配置文件中的标注数据的路径和类别即可。\n\n### 将新的数据格式转换为现有的数据格式\n\n最简单的方法就是将你的数据集转换成现有的数据格式（COCO 或者 PASCAL VOC）\n\nCOCO 格式的 JSON 标注文件有如下必要的字段：\n\n```python\n'images': [\n    {\n        'file_name': 'COCO_val2014_000000001268.jpg',\n        'height': 427,\n        'width': 640,\n        'id': 1268\n    },\n    ...\n],\n\n'annotations': [\n    {\n        'segmentation': [[192.81,\n            247.09,\n            ...\n            219.03,\n            249.06]],  # 如果有 mask 标签且为多边形 XY 点坐标格式，则需要保证至少包括 3 个点坐标，否则为无效多边形\n        'area': 1035.749,\n        'iscrowd': 0,\n        'image_id': 1268,\n        'bbox': [192.81, 224.8, 74.73, 33.43],\n        'category_id': 16,\n        'id': 42986\n    },\n    ...\n],\n\n'categories': [\n    {'id': 0, 'name': 'car'},\n ]\n```\n\n在 JSON 文件中有三个必要的键：\n\n- `images`: 包含多个图片以及它们的信息的数组，例如 `file_name`、`height`、`width` 和 `id`。\n- `annotations`: 包含多个实例标注信息的数组。\n- `categories`: 包含多个类别名字和 ID 的数组。\n\n在数据预处理之后，使用现有的数据格式来训练自定义的新数据集有如下两步（以 COCO 为例）：\n\n1. 为自定义数据集修改配置文件。\n2. 检查自定义数据集的标注。\n\n这里我们举一个例子来展示上面的两个步骤，这个例子使用包括 5 个类别的 COCO 格式的数据集来训练一个现有的 Cascade Mask R-CNN R50-FPN 检测器\n\n#### 1. 为自定义数据集修改配置文件\n\n配置文件的修改涉及两个方面：\n\n1. `dataloaer` 部分。需要在 `train_dataloader.dataset`、`val_dataloader.dataset` 和 `test_dataloader.dataset` 中添加 `metainfo=dict(classes=classes)`, 其中 classes 必须是 tuple 类型。\n2. `model` 部分中的 `num_classes`。需要将默认值（COCO 数据集中为 80）修改为自定义数据集中的类别数。\n\n`configs/my_custom_config.py` 内容如下：\n\n```python\n\n# 新的配置来自基础的配置以更好地说明需要修改的地方\n_base_ = './cascade_mask_rcnn_r50_fpn_1x_coco.py'\n\n# 1. 数据集设定\ndataset_type = 'CocoDataset'\nclasses = ('a', 'b', 'c', 'd', 'e')\ndata_root='path/to/your/'\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    dataset=dict(\n        type=dataset_type,\n        # 将类别名字添加至 `metainfo` 字段中\n        metainfo=dict(classes=classes),\n        data_root=data_root,\n        ann_file='train/annotation_data',\n        data_prefix=dict(img='train/image_data')\n        )\n    )\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    dataset=dict(\n        type=dataset_type,\n        test_mode=True,\n        # 将类别名字添加至 `metainfo` 字段中\n        metainfo=dict(classes=classes),\n        data_root=data_root,\n        ann_file='val/annotation_data',\n        data_prefix=dict(img='val/image_data')\n    )\n\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    dataset=dict(\n        type=dataset_type,\n        test_mode=True,\n        # 将类别名字添加至 `metainfo` 字段中\n        metainfo=dict(classes=classes),\n        data_root=data_root,\n        ann_file='test/annotation_data',\n        data_prefix=dict(img='test/image_data')\n        )\n    )\n\n# 2. 模型设置\n\n# 将所有的 `num_classes` 默认值修改为 5（原来为80）\nmodel = dict(\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                # 将所有的 `num_classes` 默认值修改为 5（原来为 80）\n                num_classes=5),\n            dict(\n                type='Shared2FCBBoxHead',\n                # 将所有的 `num_classes` 默认值修改为 5（原来为 80）\n                num_classes=5),\n            dict(\n                type='Shared2FCBBoxHead',\n                # 将所有的 `num_classes` 默认值修改为 5（原来为 80）\n                num_classes=5)],\n    # 将所有的 `num_classes` 默认值修改为 5（原来为 80）\n    mask_head=dict(num_classes=5)))\n```\n\n#### 2. 检查自定义数据集的标注\n\n假设你自己的数据集是 COCO 格式，那么需要保证数据的标注没有问题：\n\n1. 标注文件中 `categories` 的长度要与配置中的 `classes` 元组长度相匹配，它们都表示有几类。（如例子中有 5 个类别）\n2. 配置文件中 `classes` 字段应与标注文件里 `categories` 下的 `name` 有相同的元素且顺序一致。MMDetection 会自动将 `categories` 中不连续的 `id` 映射成连续的索引，因此 `categories` 下的 `name`的字符串顺序会影响标签的索引。同时，配置文件中的 `classes` 的字符串顺序也会影响到预测框可视化时的标签。\n3. `annotations` 中的 `category_id` 必须是有效的值。比如所有 `category_id` 的值都应该属于 `categories` 中的 `id`。\n\n下面是一个有效标注的例子：\n\n```python\n\n'annotations': [\n    {\n        'segmentation': [[192.81,\n            247.09,\n            ...\n            219.03,\n            249.06]],  # 如果有 mask 标签。\n        'area': 1035.749,\n        'iscrowd': 0,\n        'image_id': 1268,\n        'bbox': [192.81, 224.8, 74.73, 33.43],\n        'category_id': 16,\n        'id': 42986\n    },\n    ...\n],\n\n# MMDetection 会自动将 `categories` 中不连续的 `id` 映射成连续的索引。\n'categories': [\n    {'id': 1, 'name': 'a'}, {'id': 3, 'name': 'b'}, {'id': 4, 'name': 'c'}, {'id': 16, 'name': 'd'}, {'id': 17, 'name': 'e'},\n ]\n```\n\n我们使用这种方式来支持 CityScapes 数据集。脚本在 [cityscapes.py](https://github.com/open-mmlab/mmdetection/blob/main/tools/dataset_converters/cityscapes.py) 并且我们提供了微调的 [configs](https://github.com/open-mmlab/mmdetection/blob/main/configs/cityscapes).\n\n**注意**\n\n1. 对于实例分割数据集, **MMDetection 目前只支持评估 COCO 格式的 mask AP**.\n2. 推荐训练之前进行离线转换，这样就可以继续使用 `CocoDataset` 且只需修改标注文件的路径以及训练的种类。\n\n### 调整新的数据格式为中间格式\n\n如果不想将标注格式转换为 COCO 或者 PASCAL 格式也是可行的。实际上，我们在 MMEngine 的 [BaseDataset](https://github.com/open-mmlab/mmengine/blob/main/mmengine/dataset/base_dataset.py#L116) 中定义了一种简单的标注格式并且与所有现有的数据格式兼容，也能进行离线或者在线转换。\n\n数据集的标注必须为 `json` 或 `yaml`，`yml` 或 `pickle`，`pkl` 格式；标注文件中存储的字典必须包含 `metainfo` 和 `data_list` 两个字段。其中 `metainfo` 是一个字典，里面包含数据集的元信息，例如类别信息；`data_list` 是一个列表，列表中每个元素是一个字典，该字典定义了一个原始数据（raw data），每个原始数据包含一个或若干个训练/测试样本。\n\n以下是一个 JSON 标注文件的例子:\n\n```json\n{\n    'metainfo':\n        {\n            'classes': ('person', 'bicycle', 'car', 'motorcycle'),\n            ...\n        },\n    'data_list':\n        [\n            {\n                \"img_path\": \"xxx/xxx_1.jpg\",\n                \"height\": 604,\n                \"width\": 640,\n                \"instances\":\n                [\n                  {\n                    \"bbox\": [0, 0, 10, 20],\n                    \"bbox_label\": 1,\n                    \"ignore_flag\": 0\n                  },\n                  {\n                    \"bbox\": [10, 10, 110, 120],\n                    \"bbox_label\": 2,\n                    \"ignore_flag\": 0\n                  }\n                ]\n              },\n            {\n                \"img_path\": \"xxx/xxx_2.jpg\",\n                \"height\": 320,\n                \"width\": 460,\n                \"instances\":\n                [\n                  {\n                    \"bbox\": [10, 0, 20, 20],\n                    \"bbox_label\": 3,\n                    \"ignore_flag\": 1\n                  }\n                ]\n              },\n            ...\n        ]\n}\n```\n\n有些数据集可能会提供如：crowd/difficult/ignored bboxes 标注，那么我们使用 `ignore_flag`来包含它们。\n\n在得到上述标准的数据标注格式后，可以直接在配置中使用 MMDetection 的 [BaseDetDataset](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/datasets/base_det_dataset.py#L13) ，而无需进行转换。\n\n### 自定义数据集例子\n\n假设文本文件中表示的是一种全新的标注格式。边界框的标注信息保存在 `annotation.txt` 中，内容如下：\n\n```\n#\n000001.jpg\n1280 720\n2\n10 20 40 60 1\n20 40 50 60 2\n#\n000002.jpg\n1280 720\n3\n50 20 40 60 2\n20 40 30 45 2\n30 40 50 60 3\n```\n\n我们可以在 `mmdet/datasets/my_dataset.py` 中创建一个新的 dataset 用以加载数据。\n\n```python\nimport mmengine\nfrom mmdet.base_det_dataset import BaseDetDataset\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass MyDataset(BaseDetDataset):\n\n    METAINFO = {\n       'classes': ('person', 'bicycle', 'car', 'motorcycle'),\n        'palette': [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230)]\n    }\n\n    def load_data_list(self, ann_file):\n        ann_list = mmengine.list_from_file(ann_file)\n\n        data_infos = []\n        for i, ann_line in enumerate(ann_list):\n            if ann_line != '#':\n                continue\n\n            img_shape = ann_list[i + 2].split(' ')\n            width = int(img_shape[0])\n            height = int(img_shape[1])\n            bbox_number = int(ann_list[i + 3])\n\n            instances = []\n            for anns in ann_list[i + 4:i + 4 + bbox_number]:\n                instance = {}\n                instance['bbox'] = [float(ann) for ann in anns.split(' ')[:4]]\n                instance['bbox_label']=int(anns[4])\n \t\t\t\tinstances.append(instance)\n\n            data_infos.append(\n                dict(\n                    img_path=ann_list[i + 1],\n                    img_id=i,\n                    width=width,\n                    height=height,\n                    instances=instances\n                ))\n\n        return data_infos\n```\n\n配置文件中，可以使用 `MyDataset` 进行如下修改\n\n```python\ndataset_A_train = dict(\n    type='MyDataset',\n    ann_file = 'image_list.txt',\n    pipeline=train_pipeline\n)\n```\n\n## 使用 dataset 包装器自定义数据集\n\nMMEngine 也支持非常多的数据集包装器（wrapper）来混合数据集或在训练时修改数据集的分布，其支持如下三种数据集包装：\n\n- `RepeatDataset`：将整个数据集简单地重复。\n- `ClassBalancedDataset`：以类别均衡的方式重复数据集。\n- `ConcatDataset`：合并数据集。\n\n具体使用方式见 [MMEngine 数据集包装器](#TODO)。\n\n## 修改数据集的类别\n\n根据现有数据集的类型，我们可以修改它们的类别名称来训练其标注的子集。\n例如，如果只想训练当前数据集中的三个类别，那么就可以修改数据集的 `metainfo` 字典，数据集就会自动屏蔽掉其他类别的真实框。\n\n```python\nclasses = ('person', 'bicycle', 'car')\ntrain_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(classes=classes))\n    )\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(classes=classes))\n    )\ntest_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(classes=classes))\n    )\n```\n\n**注意**\n\n- 在 MMDetection v2.5.0 之前，如果类别为集合时数据集将自动过滤掉不包含 GT 的图片，且没办法通过修改配置将其关闭。这是一种不可取的行为而且会引起混淆，因为当类别不是集合时数据集时，只有在 `filter_empty_gt=True` 以及 `test_mode=False` 的情况下才会过滤掉不包含 GT 的图片。在 MMDetection v2.5.0 之后，我们将图片的过滤以及类别的修改进行解耦，数据集只有在 `filter_cfg=dict(filter_empty_gt=True)` 和 `test_mode=False` 的情况下才会过滤掉不包含 GT 的图片，无论类别是否为集合。设置类别只会影响用于训练的标注类别，用户可以自行决定是否过滤不包含 GT 的图片。\n- 直接使用 MMEngine 中的 `BaseDataset` 或者 MMDetection 中的 `BaseDetDataset` 时用户不能通过修改配置来过滤不含 GT 的图片，但是可以通过离线的方式来解决。\n- 当设置数据集中的 `classes` 时，记得修改 `num_classes`。从 v2.9.0 (PR#4508) 之后，我们实现了 [NumClassCheckHook](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/engine/hooks/num_class_check_hook.py) 来检查类别数是否一致。\n\n## COCO 全景分割数据集\n\n现在我们也支持 COCO Panoptic Dataset，全景注释的格式与 COCO 格式不同，其前景和背景都将存在于注释文件中。COCO Panoptic 格式的注释 JSON 文件具有以下必要的键：\n\n```python\n'images': [\n    {\n        'file_name': '000000001268.jpg',\n        'height': 427,\n        'width': 640,\n        'id': 1268\n    },\n    ...\n]\n\n'annotations': [\n    {\n        'filename': '000000001268.jpg',\n        'image_id': 1268,\n        'segments_info': [\n            {\n                'id':8345037,  # One-to-one correspondence with the id in the annotation map.\n                'category_id': 51,\n                'iscrowd': 0,\n                'bbox': (x1, y1, w, h),  # The bbox of the background is the outer rectangle of its mask.\n                'area': 24315\n            },\n            ...\n        ]\n    },\n    ...\n]\n\n'categories': [  # including both foreground categories and background categories\n    {'id': 0, 'name': 'person'},\n    ...\n ]\n```\n\n此外，`seg` 必须设置为全景注释图像的路径。\n\n```python\ndataset_type = 'CocoPanopticDataset'\ndata_root='path/to/your/'\n\ntrain_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img='train/image_data/', seg='train/panoptic/image_annotation_data/')\n    )\n)\nval_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img='val/image_data/', seg='val/panoptic/image_annotation_data/')\n    )\n)\ntest_dataloader = dict(\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        data_prefix=dict(\n            img='test/image_data/', seg='test/panoptic/image_annotation_data/')\n    )\n)\n```\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/customize_losses.md",
    "content": "# 自定义损失函数\n\nMMDetection 为用户提供了不同的损失函数。但是默认的配置可能无法适应不同的数据和模型，所以用户可能会希望修改某一个损失函数来适应新的情况。\n\n本教程首先详细的解释计算损失的过程然后给出一些关于如何修改每一个步骤的指导。对损失的修改可以被分为微调和加权。\n\n## 一个损失的计算过程\n\n给定输入（包括预测和目标，以及权重），损失函数会把输入的张量映射到最后的损失标量。映射过程可以分为下面五个步骤：\n\n1. 设置采样方法为对正负样本进行采样。\n\n2. 通过损失核函数获取**元素**或者**样本**损失。\n\n3. 通过权重张量来给损失**逐元素**权重。\n\n4. 把损失张量归纳为一个**标量**。\n\n5. 用一个**张量**给当前损失一个权重。\n\n## 设置采样方法（步骤 1）\n\n对于一些损失函数，需要采样策略来避免正负样本之间的不平衡。\n\n例如，在RPN head中使用`CrossEntropyLoss`时，我们需要在`train_cfg`中设置`RandomSampler`\n\n```python\ntrain_cfg=dict(\n    rpn=dict(\n        sampler=dict(\n            type='RandomSampler',\n            num=256,\n            pos_fraction=0.5,\n            neg_pos_ub=-1,\n            add_gt_as_proposals=False))\n```\n\n对于其他一些具有正负样本平衡机制的损失，例如 Focal Loss、GHMC 和 QualityFocalLoss，不再需要进行采样。\n\n## 微调损失\n\n微调一个损失主要与步骤 2，4，5 有关，大部分的修改可以在配置文件中指定。这里我们用 [Focal Loss (FL)](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/losses/focal_loss.py) 作为例子。\n下面的代码分别是构建 FL 的方法和它的配置文件，他们是一一对应的。\n\n```python\n@LOSSES.register_module()\nclass FocalLoss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid=True,\n                 gamma=2.0,\n                 alpha=0.25,\n                 reduction='mean',\n                 loss_weight=1.0):\n```\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=2.0,\n    alpha=0.25,\n    loss_weight=1.0)\n```\n\n### 微调超参数（步骤2）\n\n`gamma` 和 `beta` 是 Focal Loss 中的两个超参数。如果我们想把 `gamma` 的值设为 1.5，把 `alpha` 的值设为 0.5，我们可以在配置文件中按照如下指定：\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=1.5,\n    alpha=0.5,\n    loss_weight=1.0)\n```\n\n### 微调归纳方式（步骤4）\n\nFocal Loss 默认的归纳方式是 `mean`。如果我们想把归纳方式从 `mean` 改成 `sum`，我们可以在配置文件中按照如下指定：\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=2.0,\n    alpha=0.25,\n    loss_weight=1.0,\n    reduction='sum')\n```\n\n### 微调损失权重（步骤5）\n\n这里的损失权重是一个标量，他用来控制多任务学习中不同损失的重要程度，例如，分类损失和回归损失。如果我们想把分类损失的权重设为 0.5，我们可以在配置文件中如下指定：\n\n```python\nloss_cls=dict(\n    type='FocalLoss',\n    use_sigmoid=True,\n    gamma=2.0,\n    alpha=0.25,\n    loss_weight=0.5)\n```\n\n## 加权损失（步骤3）\n\n加权损失就是我们逐元素修改损失权重。更具体来说，我们给损失张量乘以一个与他有相同形状的权重张量。所以，损失中不同的元素可以被赋予不同的比例，所以这里叫做逐元素。损失的权重在不同模型中变化很大，而且与上下文相关，但是总的来说主要有两种损失权重：分类损失的 `label_weights` 和边界框的 `bbox_weights`。你可以在相应的头中的 `get_target` 方法中找到他们。这里我们使用 [ATSSHead](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/dense_heads/atss_head.py#L322) 作为一个例子。它继承了 [AnchorHead](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/dense_heads/anchor_head.py) ，但是我们重写它的\n`get_targets` 方法来产生不同的 `label_weights` 和 `bbox_weights`。\n\n```\nclass ATSSHead(AnchorHead):\n\n    ...\n\n    def get_targets(self,\n                    anchor_list,\n                    valid_flag_list,\n                    gt_bboxes_list,\n                    img_metas,\n                    gt_bboxes_ignore_list=None,\n                    gt_labels_list=None,\n                    label_channels=1,\n                    unmap_outputs=True):\n```\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/customize_models.md",
    "content": "# 自定义模型\n\n我们简单地把模型的各个组件分为五类：\n\n- 主干网络 (backbone)：通常是一个用来提取特征图 (feature map) 的全卷积网络 (FCN network)，例如：ResNet, MobileNet。\n- Neck：主干网络和 Head 之间的连接部分，例如：FPN, PAFPN。\n- Head：用于具体任务的组件，例如：边界框预测和掩码预测。\n- 区域提取器 (roi extractor)：从特征图中提取 RoI 特征，例如：RoI Align。\n- 损失 (loss)：在 Head 组件中用于计算损失的部分，例如：FocalLoss, L1Loss, GHMLoss.\n\n## 开发新的组件\n\n### 添加一个新的主干网络\n\n这里，我们以 MobileNet 为例来展示如何开发新组件。\n\n#### 1. 定义一个新的主干网络（以 MobileNet 为例）\n\n新建一个文件 `mmdet/models/backbones/mobilenet.py`\n\n```python\nimport torch.nn as nn\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass MobileNet(nn.Module):\n\n    def __init__(self, arg1, arg2):\n        pass\n\n    def forward(self, x):  # should return a tuple\n        pass\n```\n\n#### 2. 导入该模块\n\n你可以添加下述代码到 `mmdet/models/backbones/__init__.py`\n\n```python\nfrom .mobilenet import MobileNet\n```\n\n或添加：\n\n```python\ncustom_imports = dict(\n    imports=['mmdet.models.backbones.mobilenet'],\n    allow_failed_imports=False)\n```\n\n到配置文件以避免原始代码被修改。\n\n#### 3. 在你的配置文件中使用该主干网络\n\n```python\nmodel = dict(\n    ...\n    backbone=dict(\n        type='MobileNet',\n        arg1=xxx,\n        arg2=xxx),\n    ...\n```\n\n### 添加新的 Neck\n\n#### 1. 定义一个 Neck（以 PAFPN 为例）\n\n新建一个文件 `mmdet/models/necks/pafpn.py`\n\n```python\nimport torch.nn as nn\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass PAFPN(nn.Module):\n\n    def __init__(self,\n                in_channels,\n                out_channels,\n                num_outs,\n                start_level=0,\n                end_level=-1,\n                add_extra_convs=False):\n        pass\n\n    def forward(self, inputs):\n        # implementation is ignored\n        pass\n```\n\n#### 2. 导入该模块\n\n你可以添加下述代码到 `mmdet/models/necks/__init__.py`\n\n```python\nfrom .pafpn import PAFPN\n```\n\n或添加：\n\n```python\ncustom_imports = dict(\n    imports=['mmdet.models.necks.pafpn'],\n    allow_failed_imports=False)\n```\n\n到配置文件以避免原始代码被修改。\n\n#### 3. 修改配置文件\n\n```python\nneck=dict(\n    type='PAFPN',\n    in_channels=[256, 512, 1024, 2048],\n    out_channels=256,\n    num_outs=5)\n```\n\n### 添加新的 Head\n\n我们以 [Double Head R-CNN](https://arxiv.org/abs/1904.06493) 为例来展示如何添加一个新的 Head。\n\n首先，添加一个新的 bbox head 到 `mmdet/models/roi_heads/bbox_heads/double_bbox_head.py`。\nDouble Head R-CNN 在目标检测上实现了一个新的 bbox head。为了实现 bbox head，我们需要使用如下的新模块中三个函数。\n\n```python\nfrom typing import Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule, ModuleList\nfrom torch import Tensor\n\nfrom mmdet.models.backbones.resnet import Bottleneck\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, MultiConfig, OptConfigType, OptMultiConfig\nfrom .bbox_head import BBoxHead\n\n\n@MODELS.register_module()\nclass DoubleConvFCBBoxHead(BBoxHead):\n    r\"\"\"Bbox head used in Double-Head R-CNN\n\n    .. code-block:: none\n\n                                          /-> cls\n                      /-> shared convs ->\n                                          \\-> reg\n        roi features\n                                          /-> cls\n                      \\-> shared fc    ->\n                                          \\-> reg\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 num_convs: int = 0,\n                 num_fcs: int = 0,\n                 conv_out_channels: int = 1024,\n                 fc_out_channels: int = 1024,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     override=[\n                         dict(type='Normal', name='fc_cls', std=0.01),\n                         dict(type='Normal', name='fc_reg', std=0.001),\n                         dict(\n                             type='Xavier',\n                             name='fc_branch',\n                             distribution='uniform')\n                     ]),\n                 **kwargs) -> None:\n        kwargs.setdefault('with_avg_pool', True)\n        super().__init__(init_cfg=init_cfg, **kwargs)\n\n    def forward(self, x_cls: Tensor, x_reg: Tensor) -> Tuple[Tensor]:\n\n```\n\n然后，如有必要，实现一个新的 bbox head。我们打算从 `StandardRoIHead` 来继承新的 `DoubleHeadRoIHead`。我们可以发现 `StandardRoIHead` 已经实现了下述函数。\n\n```python\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils import empty_instances, unpack_gt_instances\nfrom .base_roi_head import BaseRoIHead\n\n\n@MODELS.register_module()\nclass StandardRoIHead(BaseRoIHead):\n    \"\"\"Simplest base roi head including one bbox head and one mask head.\"\"\"\n\n    def init_assigner_sampler(self) -> None:\n\n    def init_bbox_head(self, bbox_roi_extractor: ConfigType,\n                       bbox_head: ConfigType) -> None:\n\n    def init_mask_head(self, mask_roi_extractor: ConfigType,\n                       mask_head: ConfigType) -> None:\n\n    def forward(self, x: Tuple[Tensor],\n                rpn_results_list: InstanceList) -> tuple:\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: List[DetDataSample]) -> dict:\n\n    def _bbox_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n\n    def bbox_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult]) -> dict:\n\n    def mask_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult], bbox_feats: Tensor,\n                  batch_gt_instances: InstanceList) -> dict:\n\n    def _mask_forward(self,\n                      x: Tuple[Tensor],\n                      rois: Tensor = None,\n                      pos_inds: Optional[Tensor] = None,\n                      bbox_feats: Optional[Tensor] = None) -> dict:\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False) -> InstanceList:\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: InstanceList,\n                     rescale: bool = False) -> InstanceList:\n\n```\n\nDouble Head 的修改主要在 bbox_forward 的逻辑中，且它从 `StandardRoIHead` 中继承了其他逻辑。在 `mmdet/models/roi_heads/double_roi_head.py` 中，我们用下述代码实现新的 bbox head：\n\n```python\nfrom typing import Tuple\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass DoubleHeadRoIHead(StandardRoIHead):\n    \"\"\"RoI head for `Double Head RCNN <https://arxiv.org/abs/1904.06493>`_.\n\n    Args:\n        reg_roi_scale_factor (float): The scale factor to extend the rois\n            used to extract the regression features.\n    \"\"\"\n\n    def __init__(self, reg_roi_scale_factor: float, **kwargs):\n        super().__init__(**kwargs)\n        self.reg_roi_scale_factor = reg_roi_scale_factor\n\n    def _bbox_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        bbox_cls_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs], rois)\n        bbox_reg_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs],\n            rois,\n            roi_scale_factor=self.reg_roi_scale_factor)\n        if self.with_shared_head:\n            bbox_cls_feats = self.shared_head(bbox_cls_feats)\n            bbox_reg_feats = self.shared_head(bbox_reg_feats)\n        cls_score, bbox_pred = self.bbox_head(bbox_cls_feats, bbox_reg_feats)\n\n        bbox_results = dict(\n            cls_score=cls_score,\n            bbox_pred=bbox_pred,\n            bbox_feats=bbox_cls_feats)\n        return bbox_results\n```\n\n最终，用户需要把该模块添加到 `mmdet/models/bbox_heads/__init__.py` 和 `mmdet/models/roi_heads/__init__.py` 以使相关的注册表可以找到并加载他们。\n\n或者，用户可以添加：\n\n```python\ncustom_imports=dict(\n    imports=['mmdet.models.roi_heads.double_roi_head', 'mmdet.models.roi_heads.bbox_heads.double_bbox_head'])\n```\n\n到配置文件并实现相同的目的。\n\nDouble Head R-CNN 的配置文件如下：\n\n```python\n_base_ = '../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    roi_head=dict(\n        type='DoubleHeadRoIHead',\n        reg_roi_scale_factor=1.3,\n        bbox_head=dict(\n            _delete_=True,\n            type='DoubleConvFCBBoxHead',\n            num_convs=4,\n            num_fcs=2,\n            in_channels=256,\n            conv_out_channels=1024,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=2.0),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=2.0))))\n\n```\n\n从 MMDetection 2.0 版本起，配置系统支持继承配置以使用户可以专注于修改。\nDouble Head R-CNN 主要使用了一个新的 `DoubleHeadRoIHead` 和一个新的 `DoubleConvFCBBoxHead`，参数需要根据每个模块的 `__init__` 函数来设置。\n\n### 添加新的损失\n\n假设你想添加一个新的损失 `MyLoss` 用于边界框回归。\n为了添加一个新的损失函数，用户需要在 `mmdet/models/losses/my_loss.py` 中实现。\n装饰器 `weighted_loss` 可以使损失每个部分加权。\n\n```python\nimport torch\nimport torch.nn as nn\n\nfrom mmdet.registry import LOSSES\nfrom .utils import weighted_loss\n\n\n@weighted_loss\ndef my_loss(pred, target):\n    assert pred.size() == target.size() and target.numel() > 0\n    loss = torch.abs(pred - target)\n    return loss\n\n@LOSSES.register_module()\nclass MyLoss(nn.Module):\n\n    def __init__(self, reduction='mean', loss_weight=1.0):\n        super(MyLoss, self).__init__()\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None):\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_bbox = self.loss_weight * my_loss(\n            pred, target, weight, reduction=reduction, avg_factor=avg_factor)\n        return loss_bbox\n```\n\n然后，用户需要把它加到 `mmdet/models/losses/__init__.py`。\n\n```python\nfrom .my_loss import MyLoss, my_loss\n```\n\n或者，你可以添加：\n\n```python\ncustom_imports=dict(\n    imports=['mmdet.models.losses.my_loss'])\n```\n\n到配置文件来实现相同的目的。\n\n如使用，请修改 `loss_xxx` 字段。\n因为 MyLoss 是用于回归的，你需要在 Head 中修改 `loss_xxx` 字段。\n\n```python\nloss_bbox=dict(type='MyLoss', loss_weight=1.0))\n```\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/customize_runtime.md",
    "content": "# 自定义训练配置\n\n## 自定义优化相关的配置\n\n优化相关的配置现在已全部集成到 `optim_wrapper` 中，通常包含三个域：`optimizer`, `paramwise_cfg`，`clip_grad`，具体细节见 [OptimWrapper](https://mmengine.readthedocs.io/en/latest/tutorials/optim_wrapper.md)。下面这个例子中，使用了 `AdamW` 作为优化器，主干部分的学习率缩小到原来的十分之一，以及添加了梯度裁剪。\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    # 优化器\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,\n        weight_decay=0.05,\n        eps=1e-8,\n        betas=(0.9, 0.999)),\n\n    # 参数层面的学习率和正则化设置\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n        },\n        norm_decay_mult=0.0),\n\n    # 梯度裁剪\n    clip_grad=dict(max_norm=0.01, norm_type=2))\n```\n\n### 自定义 Pytorch 中优化器设置\n\n我们已经支持了 Pytorch 中实现的所有优化器，要使用这些优化器唯一要做就是修改配置文件中的 `optimi_wrapper` 中的 `optimzer` 域。比如，如果想要使用 `ADAM` 作为优化器（可能会导致性能下降），所需要做的修改如下。\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='Adam', lr=0.0003, weight_decay=0.0001))\n```\n\n要修改模型的学习率，用户只需要修改 `optimizer` 中的 `lr` 域。用户可以直接参考 PyToch 的 [API doc](https://pytorch.org/docs/stable/optim.html?highlight=optim#module-torch.optim) 来进行参数的设置。\n\n### 自定义优化器\n\n#### 1. 定义一个新优化器\n\n自定义优化器可以定义的方式如下：\n\n假设你想要添加一个名为 `MyOptimizer` 的优化器，它包含三个参数 `a`，`b`，`c`。你需要新建一个名为\n`mmdet/engine/optimizers` 的文件夹。然后在文件（比如，`mmdet/engine/optimizers/my_optimizer.py`）实现一个新的优化器。\n\n```python\nfrom mmdet.registry import OPTIMIZERS\nfrom torch.optim import Optimizer\n\n\n@OPTIMIZERS.register_module()\nclass MyOptimizer(Optimizer):\n\n    def __init__(self, a, b, c)\n\n```\n\n#### 2. 导入自定义的优化器\n\n为了能找到上面的所定义的模块，这个模块必须要先导入到主命名空间中。有两种方式可以实现这一点。\n\n- 修改 `mmdet/engine/optimizers/__init__.py` 来导入模块。\n\n  新定义的模块必须导入到 `mmdet/engine/optimizers/__init__.py`，这样注册器才能找到该模块并添加它。\n\n```python\nfrom .my_optimizer import MyOptimizer\n```\n\n- 在配置文件使用 `custom_imports` 来手动导入模块。\n\n```python\ncustom_imports = dict(imports=['mmdet.engine.optimizers.my_optimizer'], allow_failed_imports=False)\n```\n\n`mmdet.engine.optimizers.my_optimizer` 模块将在程序开始时导入，之后 `MyOptimizer` 类会被自动注册。注意：应该导入 `MyOptimizer` 所在的文件，即 `mmdet.engine.optimizers.my_optimizer`，而不是 `mmdet.engine.optimizers.my_optimizer.MyOptimizer`。\n\n实际上，用户也可以在别的目录结构下来进行导入模块，只要改模块可以在 `PYTHONPATH` 中找到。\n\n#### 3. 在配置文件中指定优化器\n\n接下来，你可以在配置文件中的 `optim_wrapper` 域中的中 `optimizer` 域中设置你实现的优化器 `MyOptimizer`。在配置文件中，优化器在 `optimizer` 域中的配置方式如下：\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001))\n```\n\n为了使用你的优化器，可以进行如下修改\n\n```python\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='MyOptimizer', a=a_value, b=b_value, c=c_value))\n```\n\n### 自定义优化器包装构造类\n\n一些模型可能存在一些特定参数的优化设置，比如，BN 层的权重衰减。用户可以通过自定义优化器包装构造类来实现这些精细化的参数调整。\n\n```python\nfrom mmengine.optim import DefaultOptiWrapperConstructor\n\nfrom mmdet.registry import OPTIM_WRAPPER_CONSTRUCTORS\nfrom .my_optimizer import MyOptimizer\n\n\n@OPTIM_WRAPPER_CONSTRUCTORS.register_module()\nclass MyOptimizerWrapperConstructor(DefaultOptimWrapperConstructor):\n\n    def __init__(self,\n                 optim_wrapper_cfg: dict,\n                 paramwise_cfg: Optional[dict] = None):\n\n    def __call__(self, model: nn.Module) -> OptimWrapper:\n\n        return optim_wrapper\n\n```\n\n优化器包装构造类的具体实现见[这里](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/optimizer/default_constructor.py#L18)，用户以它为模板，来实现新的优化器包装构造类。\n\n### 额外的设置\n\n一些没有被优化器实现的技巧（比如，参数层面的学习率设置）应该通过优化器包装构造类来实现或者钩子。我们列出了一些常用的设置用于稳定训练或者加速训练。请随意创建 PR，发布更多设置。\n\n- __使用梯度裁剪来稳定训练__:\n  一些模型需要进行梯度裁剪来稳定训练过程，例子如下：\n\n  ```python\n  optim_wrapper = dict(\n      _delete_=True, clip_grad=dict(max_norm=35, norm_type=2))\n  ```\n\n  如果你的配置已经集成了基础配置（包含了 `optim_wrapper` 的配置），那么你需要添加 `_delete_=True` 来覆盖掉不需要的设置。具体见[配置相关的文档](https://mmdetection.readthedocs.io/en/latest/tutorials/config.html)。\n\n- __使用动量调度加速模型收敛__:\n  我们支持动量调度器根据学习率修改模型的动量，这可以使模型以更快的方式收敛。动量调度器通常与学习率调度器一起使用，例如 [3D 检测](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/_base_/schedules/cyclic-20e.py) 中使用以下配置以加速收敛。\n  更多细节请参考 [CosineAnnealingLR](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py#L43) 和 [CosineAnnealingMomentum](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/momentum_scheduler.py#L71) 的具体实现。\n\n  ```python\n  param_scheduler = [\n      # 学习率调度器\n      # 在前 8 个 epoch, 学习率从 0 增大到 lr * 10\n      # 在接下来 12 个 epoch, 学习率从 lr * 10 减小到 lr * 1e-4\n      dict(\n          type='CosineAnnealingLR',\n          T_max=8,\n          eta_min=lr * 10,\n          begin=0,\n          end=8,\n          by_epoch=True,\n          convert_to_iter_based=True),\n      dict(\n          type='CosineAnnealingLR',\n          T_max=12,\n          eta_min=lr * 1e-4,\n          begin=8,\n          end=20,\n          by_epoch=True,\n          convert_to_iter_based=True),\n      # 动量调度器\n      # 在前 8 个 epoch, 动量从 0 增大到 0.85 / 0.95\n      # 在接下来 12 个 epoch, 学习率从 0.85 / 0.95 增大到 1\n      dict(\n          type='CosineAnnealingMomentum',\n          T_max=8,\n          eta_min=0.85 / 0.95,\n          begin=0,\n          end=8,\n          by_epoch=True,\n          convert_to_iter_based=True),\n      dict(\n          type='CosineAnnealingMomentum',\n          T_max=12,\n          eta_min=1,\n          begin=8,\n          end=20,\n          by_epoch=True,\n          convert_to_iter_based=True)\n  ]\n  ```\n\n## 自定义训练策略\n\n默认情况下，我们使用 1x 的学习率调整策略，这会条用 MMEngine 中的 [MultiStepLR](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py#L139)。\n我们支持许多其他学习率调整策略，具体见[这里](https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py)，例如 `CosineAnnealingLR` 和 `PolyLR` 策略。下面有些例子\n\n- 多项式学习率调整策略:\n\n  ```python\n  param_scheduler = [\n      dict(\n          type='PolyLR',\n          power=0.9,\n          eta_min=1e-4,\n          begin=0,\n          end=8,\n          by_epoch=True)]\n  ```\n\n- 余弦退火学习率调整策略\n\n  ```python\n  param_scheduler = [\n      dict(\n          type='CosineAnnealingLR',\n          T_max=8,\n          eta_min=lr * 1e-5,\n          begin=0,\n          end=8,\n          by_epoch=True)]\n\n  ```\n\n## 自定义训练循环\n\n默认情况下，在 `train_cfg` 中使用 `EpochBasedTrainLoop`，并且在每个 epoch 训练之后进行验证，如下所示。\n\n```python\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=12, val_begin=1, val_interval=1)\n```\n\n实际上，[`IterBasedTrainLoop`](https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py#L183%5D) 和\\[`EpochBasedTrainLoop`\\](https:// github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py#L18) 支持动态区间的方式进行验证，见下例。\n\n```python\n# 在第 365001 次迭代之前，我们每 5000 次迭代进行一次评估。\n# 在第 365000 次迭代后，我们每 368750 次迭代进行一次评估，\n# 这意味着我们在训练结束时进行评估。\n\ninterval = 5000\nmax_iters = 368750\ndynamic_intervals = [(max_iters // interval * interval + 1, max_iters)]\ntrain_cfg = dict(\n    type='IterBasedTrainLoop',\n    max_iters=max_iters,\n    val_interval=interval,\n    dynamic_intervals=dynamic_intervals)\n```\n\n## 自定义钩子\n\n### 自定义自行实现的钩子\n\n#### 1. 实现一个新的钩子\n\nMMEngine 提供了许多有用的[钩子](https://mmdetection.readthedocs.io/en/latest/tutorials/hooks.html)，但在某些情况下用户可能需要实现新的钩子。MMDetection 在 v3.0 中支持自定义钩子。因此，用户可以直接在 mmdet 或其基于 mmdet 的代码库中实现钩子，并通过仅在训练中修改配置来使用钩子。\n这里我们给出一个在 mmdet 中创建一个新的钩子并在训练中使用它的例子。\n\n```python\nfrom mmengine.hooks import Hook\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass MyHook(Hook):\n\n    def __init__(self, a, b):\n\n    def before_run(self, runner) -> None:\n\n    def after_run(self, runner) -> None:\n\n    def before_train(self, runner) -> None:\n\n    def after_train(self, runner) -> None:\n\n    def before_train_epoch(self, runner) -> None:\n\n    def after_train_epoch(self, runner) -> None:\n\n    def before_train_iter(self,\n                          runner,\n                          batch_idx: int,\n                          data_batch: DATA_BATCH = None) -> None:\n\n    def after_train_iter(self,\n                         runner,\n                         batch_idx: int,\n                         data_batch: DATA_BATCH = None,\n                         outputs: Optional[dict] = None) -> None:\n```\n\n根据钩子的功能，用户需要在 `before_run`、`after_run`、`before_train`、`after_train`、`before_train_epoch`、`after_train_epoch`、`before_train_iter` 和 `after_train_iter`。还有更多可以插入钩子的点，更多细节请参考 [base hook class](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/hook.py#L9)。\n\n#### 2. 注册新钩子\n\n然后我们需要导入 `MyHook`。假设该文件位于 `mmdet/engine/hooks/my_hook.py` 中，有两种方法可以做到这一点：\n\n- 修改 `mmdet/engine/hooks/__init__.py` 以导入它。\n\n  新定义的模块应该在 `mmdet/engine/hooks/__init__.py` 中导入，以便注册表找到新模块并添加它：\n\n```python\nfrom .my_hook import MyHook\n```\n\n- 在配置中使用 `custom_imports` 手动导入它\n\n```python\ncustom_imports = dict(imports=['mmdet.engine.hooks.my_hook'], allow_failed_imports=False)\n```\n\n#### 3. 修改配置\n\n```python\ncustom_hooks = [\n    dict(type='MyHook', a=a_value, b=b_value)\n]\n```\n\n你还可以通过修改键 `priority` 的值为 `NORMAL` 或 `HIGHEST` 来设置挂钩的优先级，如下所示\n\n```python\ncustom_hooks = [\n    dict(type='MyHook', a=a_value, b=b_value, priority='NORMAL')\n]\n```\n\n默认情况下，钩子的优先级在注册期间设置为 `NORMAL`。\n\n### 使用 MMDetection 中实现的钩子\n\n如果 MMDetection 中已经实现了该钩子，你可以直接修改配置以使用该钩子，如下所示\n\n#### 例子: `NumClassCheckHook`\n\n我们实现了一个名为 [NumClassCheckHook](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/engine/hooks/num_class_check_hook.py) 的自定义钩子来检查 `num_classes` 是否在 head 中和 `dataset` 中的 `classes` 的长度相匹配。\n\n我们在 [default_runtime.py](https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/default_runtime.py) 中设置它。\n\n```python\ncustom_hooks = [dict(type='NumClassCheckHook')]\n```\n\n### 修改默认运行时钩子\n\n有一些常见的钩子是通过 `default_hooks` 注册的，它们是\n\n- `IterTimerHook`：记录 “data_time” 用于加载数据和 “time” 用于模型训练步骤的钩子。\n- `LoggerHook`：从`Runner`的不同组件收集日志并将它们写入终端、JSON文件、tensorboard和 wandb 等的钩子。\n- `ParamSchedulerHook`：更新优化器中一些超参数的钩子，例如学习率和动量。\n- `CheckpointHook`：定期保存检查点的钩子。\n- `DistSamplerSeedHook`：为采样器和批处理采样器设置种子的钩子。\n- `DetVisualizationHook`：用于可视化验证和测试过程预测结果的钩子。\n\n`IterTimerHook`、`ParamSchedulerHook` 和 `DistSamplerSeedHook` 很简单，通常不需要修改，所以这里我们将展示如何使用 `LoggerHook`、`CheckpointHook` 和 `DetVisualizationHook`。\n\n#### CheckpointHook\n\n除了定期保存检查点，[`CheckpointHook`](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/checkpoint_hook.py#L19) 提供了其他选项，例如`max_keep_ckpts`、`save_optimizer ` 等。用户可以设置 `max_keep_ckpts` 只保存少量检查点或通过 `save_optimizer` 决定是否存储优化器的状态字典。参数的更多细节在[这里](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/checkpoint_hook.py#L19)可以找到。\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        interval=1,\n        max_keep_ckpts=3,\n        save_optimizer=True))\n```\n\n#### LoggerHook\n\n`LoggerHook` 可以设置间隔。详细用法可以在 [docstring](https://github.com/open-mmlab/mmengine/blob/main/mmengine/hooks/logger_hook.py#L18) 中找到。\n\n```python\ndefault_hooks = dict(logger=dict(type='LoggerHook', interval=50))\n```\n\n#### DetVisualizationHook\n\n`DetVisualizationHook` 使用 `DetLocalVisualizer` 来可视化预测结果，`DetLocalVisualizer` 支持不同的后端，例如 `TensorboardVisBackend` 和 `WandbVisBackend` （见 [docstring](https://github.com/open-mmlab/mmengine/blob/main/mmengine/visualization/vis_backend.py) 了解更多细节）。用户可以添加多个后端来进行可视化，如下所示。\n\n```python\ndefault_hooks = dict(\n    visualization=dict(type='DetVisualizationHook', draw=True))\n\nvis_backends = [dict(type='LocalVisBackend'),\n                dict(type='TensorboardVisBackend')]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n```\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/customize_transforms.md",
    "content": "# 自定义数据预处理流程\n\n1. 在任意文件里写一个新的流程，例如在 `my_pipeline.py`，它以一个字典作为输入并且输出一个字典：\n\n   ```python\n   import random\n   from mmcv.transforms import BaseTransform\n   from mmdet.registry import TRANSFORMS\n\n\n   @TRANSFORMS.register_module()\n   class MyTransform(BaseTransform):\n       \"\"\"Add your transform\n\n       Args:\n           p (float): Probability of shifts. Default 0.5.\n       \"\"\"\n\n       def __init__(self, prob=0.5):\n           self.prob = prob\n\n       def transform(self, results):\n           if random.random() > self.prob:\n               results['dummy'] = True\n           return results\n   ```\n\n2. 在配置文件里调用并使用你写的数据处理流程，需要确保你的训练脚本能够正确导入新增模块：\n\n   ```python\n   custom_imports = dict(imports=['path.to.my_pipeline'], allow_failed_imports=False)\n\n   train_pipeline = [\n       dict(type='LoadImageFromFile'),\n       dict(type='LoadAnnotations', with_bbox=True),\n       dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n       dict(type='RandomFlip', prob=0.5),\n       dict(type='MyTransform', prob=0.2),\n       dict(type='PackDetInputs')\n   ]\n   ```\n\n3. 可视化数据增强处理流程的结果\n\n   如果想要可视化数据增强处理流程的结果，可以使用 `tools/misc/browse_dataset.py` 直观\n   地浏览检测数据集（图像和标注信息），或将图像保存到指定目录。\n   使用方法请参考[可视化文档](../user_guides/visualization.md)\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/data_flow.md",
    "content": "# 数据流（待更新）\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/datasets.md",
    "content": "# 数据集（待更新）\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/engine.md",
    "content": "# 执行引擎（待更新）\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/evaluation.md",
    "content": "# 精度评测（待更新）\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/how_to.md",
    "content": "本教程收集了任何如何使用 MMDetection 进行 xxx 的答案。 如果您遇到有关`如何做`的问题及答案，请随时更新此文档！\n\n## 使用 MMPretrain 的骨干网络\n\nMMDet、MMPretrain、MMSeg 中的模型注册表都继承自 MMEngine 中的根注册表，允许这些存储库直接使用彼此已经实现的模块。 因此用户可以在 MMDetection 中使用来自 MMPretrain 的骨干网络，而无需实现MMPretrain 中已经存在的网络。\n\n### 使用在 MMPretrain 中实现的骨干网络\n\n假设想将 `MobileNetV3-small` 作为 `RetinaNet` 的骨干网络，则配置文件如下。\n\n```python\n_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(imports=['mmpretrain.models'], allow_failed_imports=False)\npretrained = 'https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth'\nmodel = dict(\n    backbone=dict(\n        _delete_=True, # 将 _base_ 中关于 backbone 的字段删除\n        type='mmpretrain.MobileNetV3', # 使用 mmpretrain 中的 MobileNetV3\n        arch='small',\n        out_indices=(3, 8, 11), # 修改 out_indices\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint=pretrained,\n            prefix='backbone.')), # mmpretrain 中骨干网络的预训练权重含义 prefix='backbone.'，为了正常加载权重，需要把这个 prefix 去掉。\n    # 修改 in_channels\n    neck=dict(in_channels=[24, 48, 96], start_level=0))\n```\n\n### 通过 MMPretrain 使用 TIMM 中实现的骨干网络\n\n由于 MMPretrain 提供了 Py**T**orch **Im**age **M**odels (`timm`) 骨干网络的封装，用户也可以通过 MMPretrain 直接使用 `timm` 中的骨干网络。假设想将 [`EfficientNet-B1`](../../../configs/timm_example/retinanet_timm-efficientnet-b1_fpn_1x_coco.py) 作为 `RetinaNet` 的骨干网络，则配置文件如下。\n\n```python\n# https://github.com/open-mmlab/mmdetection/blob/main/configs/timm_example/retinanet_timm_efficientnet_b1_fpn_1x_coco.py\n_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n\n# please install mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(imports=['mmpretrain.models'], allow_failed_imports=False)\nmodel = dict(\n    backbone=dict(\n        _delete_=True, # 将 _base_ 中关于 backbone 的字段删除\n        type='mmpretrain.TIMMBackbone', # 使用 mmpretrain 中 timm 骨干网络\n        model_name='efficientnet_b1',\n        features_only=True,\n        pretrained=True,\n        out_indices=(1, 2, 3, 4)), # 修改 out_indices\n    neck=dict(in_channels=[24, 40, 112, 320])) # 修改 in_channels\n\noptimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)\n```\n\n`type='mmpretrain.TIMMBackbone'` 表示在 MMDetection 中使用 MMPretrain 中的 `TIMMBackbone` 类，并且使用的模型为` EfficientNet-B1`，其中 `mmpretrain` 表示 MMPretrain 库，而 `TIMMBackbone ` 表示 MMPretrain 中实现的 TIMMBackbone 包装器。\n\n关于层次注册器的具体原理可以参考 [MMEngine 文档](https://mmengine.readthedocs.io/zh_cn/latest/tutorials/config.md#跨项目继承配置文件)，关于如何使用 MMPretrain 中的其他 backbone，可以参考 [MMPretrain 文档](https://mmpretrain.readthedocs.io/en/latest/user_guides/config.html)。\n\n## 使用马赛克数据增强\n\n如果你想在训练中使用 `Mosaic`，那么请确保你同时使用 `MultiImageMixDataset`。以 `Faster R-CNN` 算法为例，你可以通过如下做法实现：\n\n```python\n# 直接打开 configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py ,增添如下字段\ndata_root = 'data/coco/'\ndataset_type = 'CocoDataset'\nimg_scale=(1333, 800)\n\ntrain_pipeline = [\n    dict(type='Mosaic', img_scale=img_scale, pad_val=114.0),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        border=(-img_scale[0] // 2, -img_scale[1] // 2)), # 图像经过马赛克处理后会放大4倍，所以我们使用仿射变换来恢复图像的大小。\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'))\n]\n\ntrain_dataset = dict(\n    _delete_ = True, # 删除不必要的设置\n    type='MultiImageMixDataset',\n    dataset=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_train2017.json',\n        img_prefix=data_root + 'train2017/',\n        pipeline=[\n            dict(type='LoadImageFromFile'),\n            dict(type='LoadAnnotations', with_bbox=True)\n        ],\n        filter_empty_gt=False,\n    ),\n    pipeline=train_pipeline\n    )\n\ndata = dict(\n    train=train_dataset\n    )\n```\n\n## 在配置文件中冻结骨干网络后在训练中解冻骨干网络\n\n如果你在配置文件中已经冻结了骨干网络并希望在几个训练周期后解冻它，你可以通过 hook 来实现这个功能。以用 ResNet 为骨干网络的 Faster R-CNN 为例，你可以冻结一个骨干网络的一个层并在配置文件中添加如下 `custom_hooks`:\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    # freeze one stage of the backbone network.\n    backbone=dict(frozen_stages=1),\n)\ncustom_hooks = [dict(type=\"UnfreezeBackboneEpochBasedHook\", unfreeze_epoch=1)]\n```\n\n同时在 `mmdet/core/hook/unfreeze_backbone_epoch_based_hook.py` 当中书写 `UnfreezeBackboneEpochBasedHook` 类\n\n```python\nfrom mmengine.model import is_model_wrapper\nfrom mmengine.hooks import Hook\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass UnfreezeBackboneEpochBasedHook(Hook):\n    \"\"\"Unfreeze backbone network Hook.\n\n    Args:\n        unfreeze_epoch (int): The epoch unfreezing the backbone network.\n    \"\"\"\n\n    def __init__(self, unfreeze_epoch=1):\n        self.unfreeze_epoch = unfreeze_epoch\n\n    def before_train_epoch(self, runner):\n        # Unfreeze the backbone network.\n        # Only valid for resnet.\n        if runner.epoch == self.unfreeze_epoch:\n            model = runner.model\n            if is_module_wrapper(model):\n                model = model.module\n            backbone = model.backbone\n            if backbone.frozen_stages >= 0:\n                if backbone.deep_stem:\n                    backbone.stem.train()\n                    for param in backbone.stem.parameters():\n                        param.requires_grad = True\n                else:\n                    backbone.norm1.train()\n                    for m in [backbone.conv1, backbone.norm1]:\n                        for param in m.parameters():\n                            param.requires_grad = True\n\n            for i in range(1, backbone.frozen_stages + 1):\n                m = getattr(backbone, f'layer{i}')\n                m.train()\n                for param in m.parameters():\n                    param.requires_grad = True\n```\n\n## 获得新的骨干网络的通道数\n\n如果你想获得一个新骨干网络的通道数，你可以单独构建这个骨干网络并输入一个伪造的图片来获取每一个阶段的输出。\n\n以 `ResNet` 为例：\n\n```python\nfrom mmdet.models import ResNet\nimport torch\nself = ResNet(depth=18)\nself.eval()\ninputs = torch.rand(1, 3, 32, 32)\nlevel_outputs = self.forward(inputs)\nfor level_out in level_outputs:\n    print(tuple(level_out.shape))\n\n```\n\n以上脚本的输出为:\n\n```python\n(1, 64, 8, 8)\n(1, 128, 4, 4)\n(1, 256, 2, 2)\n(1, 512, 1, 1)\n```\n\n用户可以通过将脚本中的 `ResNet(depth=18)` 替换为自己的骨干网络配置来得到新的骨干网络的通道数。\n\n# MMDetection 中训练 Detectron2 的模型\n\n用户可以使用 `Detectron2Wrapper` 从而在 MMDetection 中使用 Detectron2 的模型。\n我们提供了 [Faster R-CNN](../../../configs/misc/d2_faster-rcnn_r50-caffe_fpn_ms-90k_coco.py),\n[Mask R-CNN](../../../configs/misc/d2_mask-rcnn_r50-caffe_fpn_ms-90k_coco.py) 和 [RetinaNet](../../../configs/misc/d2_retinanet_r50-caffe_fpn_ms-90k_coco.py) 的示例来在 MMDetection 中训练/测试 Detectron2 的模型。\n\n使用过程中需要注意配置文件中算法组件要和 Detectron2 中的相同。模型初始化时，我们首先初始化 [Detectron2](https://github.com/facebookresearch/detectron2/blob/main/detectron2/config/defaults.py) 的默认设置，然后配置文件中的设置将覆盖默认设置，模型将基于更新过的设置来建立。\n输入数据首先转换成 Detectron2 的类型并输入进 Detectron2 的模型中。在推理阶段，Detectron2 的模型结果将会转换回 MMDetection 的类型。\n\n## 使用 Detectron2 的预训练权重\n\n`Detectron2Wrapper` 中的权重初始化将不使用 MMDetection 的逻辑。用户可以设置 `model.d2_detector.weights=xxx` 来加载预训练的权重。\n例如，我们可以使用 `model.d2_detector.weights='detectron2://ImageNetPretrained/MSRA/R-50.pkl'` 来加载 ResNet-50 的预训练权重，或者使用\n`model.d2_detector.weights='detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x/137260431/model_final_a54504.pkl'` 来加载 Detectron2 中提出的预训练的Mask R-CNN权重。\n\n**注意：** 不能直接使用 `load_from` 来加载 Detectron2 的预训练模型，但可以通过 `tools/model_converters/detectron2_to_mmdet.py` 先对该预训练模型进行转换。\n\n在测试时，用户应该首先使用 `tools/model_converters/detectron2_to_mmdet.py` 将 Detectron2 的预训练权重转换为 MMDetection 可读取的结构。\n\n```shell\npython tools/model_converters/detectron2_to_mmdet.py ${Detectron2 ckpt path} ${MMDetectron ckpt path}。\n```\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/index.rst",
    "content": "基础概念\n***************\n\n.. toctree::\n   :maxdepth: 1\n\n   data_flow.md\n   structures.md\n   models.md\n   datasets.md\n   transforms.md\n   evaluation.md\n   engine.md\n   conventions.md\n\n组件定制\n************************\n\n.. toctree::\n   :maxdepth: 1\n\n   customize_models.md\n   customize_losses.md\n   customize_dataset.md\n   customize_transforms.md\n   customize_runtime.md\n\nHow to\n************************\n\n.. toctree::\n   :maxdepth: 1\n\n   how_to.md\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/models.md",
    "content": "# 模型（待更新）\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/structures.md",
    "content": "# 数据结构（待更新）\n"
  },
  {
    "path": "docs/zh_cn/advanced_guides/transforms.md",
    "content": "# 数据变换（待更新）\n\n按照惯例，我们使用 `Dataset` 和 `DataLoader` 进行多进程的数据加载。`Dataset` 返回字典类型的数据，数据内容为模型 `forward` 方法的各个参数。由于在目标检测中，输入的图像数据具有不同的大小，我们在 `MMCV` 里引入一个新的 `DataContainer` 类去收集和分发不同大小的输入数据。更多细节请参考[这里](https://github.com/open-mmlab/mmcv/blob/master/mmcv/parallel/data_container.py)。\n\n数据的准备流程和数据集是解耦的。通常一个数据集定义了如何处理标注数据（annotations）信息，而一个数据流程定义了准备一个数据字典的所有步骤。一个流程包括一系列的操作，每个操作都把一个字典作为输入，然后再输出一个新的字典给下一个变换操作。\n\n我们在下图展示了一个经典的数据处理流程。蓝色块是数据处理操作，随着数据流程的处理，每个操作都可以在结果字典中加入新的键（标记为绿色）或更新现有的键（标记为橙色）。\n\n![pipeline figure](../../../resources/data_pipeline.png)\n\n这些操作可以分为数据加载（data loading）、预处理（pre-processing）、格式变化（formatting）和测试时数据增强（test-time augmentation）。\n\n下面的例子是 `Faster R-CNN` 的一个流程：\n\n```python\nimg_norm_cfg = dict(\n    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', flip_ratio=0.5),\n    dict(type='Normalize', **img_norm_cfg),\n    dict(type='Pad', size_divisor=32),\n    dict(type='DefaultFormatBundle'),\n    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(\n        type='MultiScaleFlipAug',\n        img_scale=(1333, 800),\n        flip=False,\n        transforms=[\n            dict(type='Resize', keep_ratio=True),\n            dict(type='RandomFlip'),\n            dict(type='Normalize', **img_norm_cfg),\n            dict(type='Pad', size_divisor=32),\n            dict(type='ImageToTensor', keys=['img']),\n            dict(type='Collect', keys=['img']),\n        ])\n]\n```\n"
  },
  {
    "path": "docs/zh_cn/api.rst",
    "content": "mmdet.apis\n--------------\n.. automodule:: mmdet.apis\n    :members:\n\nmmdet.datasets\n--------------\n\ndatasets\n^^^^^^^^^^\n.. automodule:: mmdet.datasets\n    :members:\n\napi_wrappers\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.datasets.api_wrappers\n    :members:\n\nsamplers\n^^^^^^^^^^\n.. automodule:: mmdet.datasets.samplers\n    :members:\n\ntransforms\n^^^^^^^^^^^^\n.. automodule:: mmdet.datasets.transforms\n    :members:\n\nmmdet.engine\n--------------\n\nhooks\n^^^^^^^^^^\n.. automodule:: mmdet.engine.hooks\n    :members:\n\noptimizers\n^^^^^^^^^^^^^^^\n.. automodule:: mmdet.engine.optimizers\n    :members:\n\nrunner\n^^^^^^^^^^\n.. automodule:: mmdet.engine.runner\n    :members:\n\nschedulers\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.engine.schedulers\n    :members:\n\nmmdet.evaluation\n--------------------\n\nfunctional\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.evaluation.functional\n    :members:\n\nmetrics\n^^^^^^^^^^\n.. automodule:: mmdet.evaluation.metrics\n    :members:\n\n\nmmdet.models\n--------------\n\nbackbones\n^^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.backbones\n    :members:\n\ndata_preprocessors\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.data_preprocessors\n    :members:\n\ndense_heads\n^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.dense_heads\n    :members:\n\ndetectors\n^^^^^^^^^^\n.. automodule:: mmdet.models.detectors\n    :members:\n\nlayers\n^^^^^^^^^^\n.. automodule:: mmdet.models.layers\n    :members:\n\nlosses\n^^^^^^^^^^\n.. automodule:: mmdet.models.losses\n    :members:\n\nnecks\n^^^^^^^^^^^^\n.. automodule:: mmdet.models.necks\n    :members:\n\nroi_heads\n^^^^^^^^^^^^^\n.. automodule:: mmdet.models.roi_heads\n    :members:\n\nseg_heads\n^^^^^^^^^^^^^\n.. automodule:: mmdet.models.seg_heads\n    :members:\n\ntask_modules\n^^^^^^^^^^^^^\n.. automodule:: mmdet.models.task_modules\n    :members:\n\ntest_time_augs\n^^^^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.models.test_time_augs\n    :members:\n\nutils\n^^^^^^^^^^\n.. automodule:: mmdet.models.utils\n    :members:\n\n\nmmdet.structures\n--------------------\n\nstructures\n^^^^^^^^^^^^^^^^^\n.. automodule:: mmdet.structures\n    :members:\n\nbbox\n^^^^^^^^^^\n.. automodule:: mmdet.structures.bbox\n    :members:\n\nmask\n^^^^^^^^^^\n.. automodule:: mmdet.structures.mask\n    :members:\n\nmmdet.testing\n----------------\n.. automodule:: mmdet.testing\n    :members:\n\nmmdet.visualization\n--------------------\n.. automodule:: mmdet.visualization\n    :members:\n\nmmdet.utils\n--------------\n.. automodule:: mmdet.utils\n    :members:\n"
  },
  {
    "path": "docs/zh_cn/article.md",
    "content": "## 中文解读文案汇总（待更新）\n\n### 1 官方解读文案（v2.x）\n\n#### 1.1 框架解读\n\n- **[轻松掌握 MMDetection 整体构建流程(一)](https://zhuanlan.zhihu.com/p/337375549)**\n- **[轻松掌握 MMDetection 整体构建流程(二)](https://zhuanlan.zhihu.com/p/341954021)**\n- **[轻松掌握 MMDetection 中 Head 流程](https://zhuanlan.zhihu.com/p/343433169)**\n\n#### 1.2 算法解读\n\n- **[轻松掌握 MMDetection 中常用算法(一)：RetinaNet 及配置详解](https://zhuanlan.zhihu.com/p/346198300)**\n- **[轻松掌握 MMDetection 中常用算法(二)：Faster R-CNN|Mask R-CNN](https://zhuanlan.zhihu.com/p/349807581)**\n- [轻松掌握 MMDetection 中常用算法(三)：FCOS](https://zhuanlan.zhihu.com/p/358056615)\n- [轻松掌握 MMDetection 中常用算法(四)：ATSS](https://zhuanlan.zhihu.com/p/358125611)\n- [轻松掌握 MMDetection 中常用算法(五)：Cascade R-CNN](https://zhuanlan.zhihu.com/p/360952172)\n- [轻松掌握 MMDetection 中常用算法(六)：YOLOF](https://zhuanlan.zhihu.com/p/370758213)\n- [轻松掌握 MMDetection 中常用算法(七)：CenterNet](https://zhuanlan.zhihu.com/p/374891478)\n- [轻松掌握 MMDetection 中常用算法(八)：YOLACT](https://zhuanlan.zhihu.com/p/376347955)\n- [轻松掌握 MMDetection 中常用算法(九)：AutoAssign](https://zhuanlan.zhihu.com/p/378581552)\n- [YOLOX 在 MMDetection 中复现全流程解析](https://zhuanlan.zhihu.com/p/398545304)\n- [喂喂喂！你可以减重了！小模型 - MMDetection 新增SSDLite 、 MobileNetV2YOLOV3 两大经典算法](https://zhuanlan.zhihu.com/p/402781143)\n\n#### 1.3 工具解读\n\n- [OpenMMLab 中混合精度训练 AMP 的正确打开方式](https://zhuanlan.zhihu.com/p/375224982)\n- [小白都能看懂！手把手教你使用混淆矩阵分析目标检测](https://zhuanlan.zhihu.com/p/443499860)\n- [MMDetection 图像缩放 Resize 详细说明 OpenMMLab](https://zhuanlan.zhihu.com/p/381117525)\n- [拿什么拯救我的 4G 显卡](https://zhuanlan.zhihu.com/p/430123077)\n- [MMDet居然能用MMCls的Backbone？论配置文件的打开方式](https://zhuanlan.zhihu.com/p/436865195)\n\n#### 1.4 知乎问答\n\n- [COCO数据集上1x模式下为什么不采用多尺度训练?](https://www.zhihu.com/question/462170786/answer/1915119662)\n- [MMDetection中SOTA论文源码中将训练过程中BN层的eval打开?](https://www.zhihu.com/question/471189603/answer/2195540892)\n- [基于PyTorch的MMDetection中训练的随机性来自何处？](https://www.zhihu.com/question/453511684/answer/1839683634)\n- [单阶段、双阶段、anchor-based、anchor-free 这四者之间有什么联系吗？](https://www.zhihu.com/question/428972054/answer/1619925296)\n- [目标检测的深度学习方法，有推荐的书籍或资料吗？](https://www.zhihu.com/question/391577080/answer/1612593817)\n- [大佬们，刚入学研究生，想入门目标检测，有什么学习路线可以入门的？](https://www.zhihu.com/question/343768934/answer/1612580715)\n- [目标检测领域还有什么可以做的？](https://www.zhihu.com/question/280703314/answer/1627885518)\n- [如何看待Transformer在CV上的应用前景，未来有可能替代CNN吗？](https://www.zhihu.com/question/437495132/answer/1686380553)\n- [MMDetection如何学习源码？](https://www.zhihu.com/question/451585041/answer/1832498963)\n- [如何具体上手实现目标检测呢？](https://www.zhihu.com/question/341401981/answer/1848561187)\n\n#### 1.5 其他\n\n- **[不得不知的 MMDetection 学习路线(个人经验版)](https://zhuanlan.zhihu.com/p/369826931)**\n- [OpenMMLab 社区专访之 YOLOX 复现篇](https://zhuanlan.zhihu.com/p/405913343)\n\n### 2 社区解读文案（v2.x）\n\n- [手把手带你实现经典检测网络 Mask R-CNN 的推理](https://zhuanlan.zhihu.com/p/414082071)\n"
  },
  {
    "path": "docs/zh_cn/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common options. For a full\n# list see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport subprocess\nimport sys\n\nimport pytorch_sphinx_theme\n\nsys.path.insert(0, os.path.abspath('../../'))\n\n# -- Project information -----------------------------------------------------\n\nproject = 'MMDetection'\ncopyright = '2018-2021, OpenMMLab'\nauthor = 'MMDetection Authors'\nversion_file = '../../mmdet/version.py'\n\n\ndef get_version():\n    with open(version_file, 'r') as f:\n        exec(compile(f.read(), version_file, 'exec'))\n    return locals()['__version__']\n\n\n# The full version, including alpha/beta/rc tags\nrelease = get_version()\n\n# -- General configuration ---------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.napoleon',\n    'sphinx.ext.viewcode',\n    'myst_parser',\n    'sphinx_markdown_tables',\n    'sphinx_copybutton',\n]\n\nmyst_enable_extensions = ['colon_fence']\nmyst_heading_anchors = 3\n\nautodoc_mock_imports = [\n    'matplotlib', 'pycocotools', 'terminaltables', 'mmdet.version', 'mmcv.ops'\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\nsource_suffix = {\n    '.rst': 'restructuredtext',\n    '.md': 'markdown',\n}\n\n# The main toctree document.\nmaster_doc = 'index'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\n# html_theme = 'sphinx_rtd_theme'\nhtml_theme = 'pytorch_sphinx_theme'\nhtml_theme_path = [pytorch_sphinx_theme.get_html_theme_path()]\n\nhtml_theme_options = {\n    'menu': [\n        {\n            'name': 'GitHub',\n            'url': 'https://github.com/open-mmlab/mmdetection'\n        },\n    ],\n    # Specify the language of shared menu\n    'menu_lang':\n    'cn',\n}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\nhtml_css_files = ['css/readthedocs.css']\n\nlanguage = 'zh_CN'\n\n# -- Extension configuration -------------------------------------------------\n# Ignore >>> when copying code\ncopybutton_prompt_text = r'>>> |\\.\\.\\. '\ncopybutton_prompt_is_regexp = True\n\n\ndef builder_inited_handler(app):\n    subprocess.run(['./stat.py'])\n\n\ndef setup(app):\n    app.connect('builder-inited', builder_inited_handler)\n"
  },
  {
    "path": "docs/zh_cn/get_started.md",
    "content": "# 开始你的第一步\n\n## 依赖\n\n本节中，我们将演示如何用 PyTorch 准备一个环境。\n\nMMDetection 支持在 Linux，Windows 和 macOS 上运行。它需要 Python 3.7 以上，CUDA 9.2 以上和 PyTorch 1.8 及其以上。\n\n```{note}\n如果你对 PyTorch 有经验并且已经安装了它，你可以直接跳转到[下一小节](#安装流程)。否则，你可以按照下述步骤进行准备。\n```\n\n**步骤 0.** 从[官方网站](https://docs.conda.io/en/latest/miniconda.html)下载并安装 Miniconda。\n\n**步骤 1.** 创建并激活一个 conda 环境。\n\n```shell\nconda create --name openmmlab python=3.8 -y\nconda activate openmmlab\n```\n\n**步骤 2.** 基于 [PyTorch 官方说明](https://pytorch.org/get-started/locally/)安装 PyTorch。\n\n在 GPU 平台上：\n\n```shell\nconda install pytorch torchvision -c pytorch\n```\n\n在 CPU 平台上：\n\n```shell\nconda install pytorch torchvision cpuonly -c pytorch\n```\n\n## 安装流程\n\n我们推荐用户参照我们的最佳实践安装 MMDetection。不过，整个过程也是可定制化的，更多信息请参考[自定义安装](#自定义安装)章节。\n\n### 最佳实践\n\n**步骤 0.** 使用 [MIM](https://github.com/open-mmlab/mim) 安装 [MMEngine](https://github.com/open-mmlab/mmengine) 和 [MMCV](https://github.com/open-mmlab/mmcv)。\n\n```shell\npip install -U openmim\nmim install mmengine\nmim install \"mmcv>=2.0.0\"\n```\n\n**注意：** 在 MMCV-v2.x 中，`mmcv-full` 改名为 `mmcv`，如果你想安装不包含 CUDA 算子精简版，可以通过 `mim install \"mmcv-lite>=2.0.0rc1\"` 来安装。\n\n**步骤 1.** 安装 MMDetection。\n\n方案 a：如果你开发并直接运行 mmdet，从源码安装它：\n\n```shell\ngit clone https://github.com/open-mmlab/mmdetection.git\ncd mmdetection\npip install -v -e .\n# \"-v\" 指详细说明，或更多的输出\n# \"-e\" 表示在可编辑模式下安装项目，因此对代码所做的任何本地修改都会生效，从而无需重新安装。\n```\n\n方案 b：如果你将 mmdet 作为依赖或第三方 Python 包，使用 MIM 安装：\n\n```shell\nmim install mmdet\n```\n\n## 验证安装\n\n为了验证 MMDetection 是否安装正确，我们提供了一些示例代码来执行模型推理。\n\n**步骤 1.** 我们需要下载配置文件和模型权重文件。\n\n```shell\nmim download mmdet --config rtmdet_tiny_8xb32-300e_coco --dest .\n```\n\n下载将需要几秒钟或更长时间，这取决于你的网络环境。完成后，你会在当前文件夹中发现两个文件 `rtmdet_tiny_8xb32-300e_coco.py` 和 `rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth`。\n\n**步骤 2.** 推理验证。\n\n方案 a：如果你通过源码安装的 MMDetection，那么直接运行以下命令进行验证：\n\n```shell\npython demo/image_demo.py demo/demo.jpg rtmdet_tiny_8xb32-300e_coco.py --weights rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth --device cpu\n```\n\n你会在当前文件夹中的 `outputs/vis` 文件夹中看到一个新的图像 `demo.jpg`，图像中包含有网络预测的检测框。\n\n方案 b：如果你通过 MIM 安装的 MMDetection，那么可以打开你的 Python 解析器，复制并粘贴以下代码：\n\n```python\nfrom mmdet.apis import init_detector, inference_detector\n\nconfig_file = 'rtmdet_tiny_8xb32-300e_coco.py'\ncheckpoint_file = 'rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth'\nmodel = init_detector(config_file, checkpoint_file, device='cpu')  # or device='cuda:0'\ninference_detector(model, 'demo/demo.jpg')\n```\n\n你将会看到一个包含 `DetDataSample` 的列表，预测结果在 `pred_instance` 里，包含有检测框，类别和得分。\n\n### 自定义安装\n\n#### CUDA 版本\n\n在安装 PyTorch 时，你需要指定 CUDA 的版本。如果你不清楚应该选择哪一个，请遵循我们的建议：\n\n- 对于 Ampere 架构的 NVIDIA GPU，例如 GeForce 30 系列以及 NVIDIA A100，CUDA 11 是必需的。\n- 对于更早的 NVIDIA GPU，CUDA 11 是向后兼容 (backward compatible) 的，但 CUDA 10.2 能够提供更好的兼容性，也更加轻量。\n\n请确保你的 GPU 驱动版本满足最低的版本需求，参阅 NVIDIA 官方的 [CUDA 工具箱和相应的驱动版本关系表](https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html#cuda-major-component-versions__table-cuda-toolkit-driver-versions)。\n\n```{note}\n如果按照我们的最佳实践，安装 CUDA 运行时库就足够了，这是因为不需要在本地编译 CUDA 代码。但如果你希望从源码编译 MMCV，或是开发其他 CUDA 算子，那么就必须安装完整的 CUDA 工具链，参见 [NVIDIA 官网](https://developer.nvidia.com/cuda-downloads)，另外还需要确保该 CUDA 工具链的版本与 PyTorch 安装时的配置相匹配（如用 `conda install` 安装 PyTorch 时指定的 cudatoolkit 版本）。\n```\n\n#### 不使用 MIM 安装 MMEngine\n\n要使用 pip 而不是 MIM 来安装 MMEngine，请遵照 [MMEngine 安装指南](https://mmengine.readthedocs.io/zh_CN/latest/get_started/installation.html)。\n\n例如，你可以通过以下命令安装 MMEngine。\n\n```shell\npip install mmengine\n```\n\n#### 不使用 MIM 安装 MMCV\n\nMMCV 包含 C++ 和 CUDA 扩展，因此其对 PyTorch 的依赖比较复杂。MIM 会自动解析这些依赖，选择合适的 MMCV 预编译包，使安装更简单，但它并不是必需的。\n\n要使用 pip 而不是 MIM 来安装 MMCV，请遵照 [MMCV 安装指南](https://mmcv.readthedocs.io/zh_CN/2.x/get_started/installation.html)。它需要您用指定 url 的形式手动指定对应的 PyTorch 和 CUDA 版本。\n\n例如，下述命令将会安装基于 PyTorch 1.12.x 和 CUDA 11.6 编译的 MMCV。\n\n```shell\npip install \"mmcv>=2.0.0\" -f https://download.openmmlab.com/mmcv/dist/cu116/torch1.12.0/index.html\n```\n\n#### 在 CPU 环境中安装\n\nMMDetection 可以在 CPU 环境中构建。在 CPU 模式下，可以进行模型训练（需要 MMCV 版本 >= 2.0.0rc1）、测试或者推理。\n\n但是，以下功能在该模式下不能使用：\n\n- Deformable Convolution\n- Modulated Deformable Convolution\n- ROI pooling\n- Deformable ROI pooling\n- CARAFE\n- SyncBatchNorm\n- CrissCrossAttention\n- MaskedConv2d\n- Temporal Interlace Shift\n- nms_cuda\n- sigmoid_focal_loss_cuda\n- bbox_overlaps\n\n因此，如果尝试训练/测试/推理包含上述算子的模型，将会报错。下表列出了将会受影响的相关算法。\n\n|                          操作                           |                                           模型                                           |\n| :-----------------------------------------------------: | :--------------------------------------------------------------------------------------: |\n| Deformable Convolution/Modulated Deformable Convolution | DCN、Guided Anchoring、RepPoints、CentripetalNet、VFNet、CascadeRPN、NAS-FCOS、DetectoRS |\n|                      MaskedConv2d                       |                                     Guided Anchoring                                     |\n|                         CARAFE                          |                                          CARAFE                                          |\n|                      SyncBatchNorm                      |                                         ResNeSt                                          |\n\n#### 在 Google Colab 中安装\n\n[Google Colab](https://colab.research.google.com/) 通常已经包含了 PyTorch 环境，因此我们只需要安装 MMEngine，MMCV 和 MMDetection 即可，命令如下：\n\n**步骤 1.** 使用 [MIM](https://github.com/open-mmlab/mim) 安装 [MMEngine](https://github.com/open-mmlab/mmengine) 和 [MMCV](https://github.com/open-mmlab/mmcv)。\n\n```shell\n!pip3 install openmim\n!mim install mmengine\n!mim install \"mmcv>=2.0.0,<2.1.0\"\n```\n\n**步骤 2.** 使用源码安装 MMDetection。\n\n```shell\n!git clone https://github.com/open-mmlab/mmdetection.git\n%cd mmdetection\n!pip install -e .\n```\n\n**步骤 3.** 验证安装是否成功。\n\n```python\nimport mmdet\nprint(mmdet.__version__)\n# 预期输出：3.0.0 或其他版本号\n```\n\n```{note}\n在 Jupyter Notebook 中，感叹号 `!` 用于执行外部命令，而 `%cd` 是一个[魔术命令](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-cd)，用于切换 Python 的工作路径。\n```\n\n#### 通过 Docker 使用 MMDetection\n\n我们提供了一个 [Dockerfile](../../docker/Dockerfile) 来构建一个镜像。请确保你的 [docker 版本](https://docs.docker.com/engine/install/) >=19.03。\n\n```shell\n# 基于 PyTorch 1.9，CUDA 11.1 构建镜像\n# 如果你想要其他版本，只需要修改 Dockerfile\ndocker build -t mmdetection docker/\n```\n\n用以下命令运行 Docker 镜像：\n\n```shell\ndocker run --gpus all --shm-size=8g -it -v {DATA_DIR}:/mmdetection/data mmdetection\n```\n\n### 排除故障\n\n如果你在安装过程中遇到一些问题，请先查看 [FAQ](notes/faq.md) 页面。如果没有找到解决方案，你也可以在 GitHub 上[提出一个问题](https://github.com/open-mmlab/mmdetection/issues/new/choose)。\n\n### 使用多个 MMDetection 版本进行开发\n\n训练和测试的脚本已经在 `PYTHONPATH` 中进行了修改，以确保脚本使用当前目录中的 MMDetection。\n\n要使环境中安装默认版本的 MMDetection 而不是当前正在使用的，可以删除出现在相关脚本中的代码：\n\n```shell\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH\n```\n"
  },
  {
    "path": "docs/zh_cn/index.rst",
    "content": "Welcome to MMDetection's documentation!\n=======================================\n\n.. toctree::\n   :maxdepth: 1\n   :caption: 开始你的第一步\n\n   overview.md\n   get_started.md\n\n.. toctree::\n   :maxdepth: 2\n   :caption: 使用指南\n\n   user_guides/index.rst\n\n.. toctree::\n   :maxdepth: 2\n   :caption: 进阶教程\n\n   advanced_guides/index.rst\n\n.. toctree::\n   :maxdepth: 1\n   :caption: 迁移版本\n\n   migration/migration.md\n\n.. toctree::\n   :maxdepth: 1\n   :caption: 接口文档（英文）\n\n   api.rst\n\n.. toctree::\n   :maxdepth: 1\n   :caption: 模型仓库\n\n   model_zoo.md\n\n.. toctree::\n   :maxdepth: 1\n   :caption: 说明\n\n   notes/contribution_guide.md\n   notes/projects.md\n   notes/faq.md\n   notes/compatibility.md\n\n.. toctree::\n   :maxdepth: 1\n   :caption: 文章\n\n   article.md\n\n.. toctree::\n   :caption: 语言切换\n\n   switch_language.md\n\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/zh_cn/make.bat",
    "content": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset SOURCEDIR=.\nset BUILDDIR=_build\n\nif \"%1\" == \"\" goto help\n\n%SPHINXBUILD% >NUL 2>NUL\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.http://sphinx-doc.org/\n\texit /b 1\n)\n\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\ngoto end\n\n:help\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\n\n:end\npopd\n"
  },
  {
    "path": "docs/zh_cn/migration/api_and_registry_migration.md",
    "content": "# 将 API 和注册器从 MMDetection 2.x 迁移至 3.x\n"
  },
  {
    "path": "docs/zh_cn/migration/config_migration.md",
    "content": "# 将配置文件从 MMDetection 2.x 迁移至 3.x\n\nMMDetection 3.x 的配置文件与 2.x 相比有较大变化，这篇文档将介绍如何将 2.x 的配置文件迁移到 3.x。\n\n在前面的[配置文件教程](../user_guides/config.md)中，我们以 Mask R-CNN 为例介绍了 MMDetection 3.x 的配置文件结构，这里我们将按同样的结构介绍如何将 2.x 的配置文件迁移至 3.x。\n\n## 模型配置\n\n模型的配置与 2.x 相比并没有太大变化，对于模型的 backbone，neck，head，以及 train_cfg 和 test_cfg，它们的参数与 2.x 版本的参数保持一致。\n\n不同的是，我们在 3.x 版本的模型中新增了 `DataPreprocessor` 模块。\n`DataPreprocessor` 模块的配置位于 `model.data_preprocessor` 中，它用于对输入数据进行预处理，例如对输入图像进行归一化，将不同大小的图片进行 padding 从而组成 batch，将图像从内存中读取到显存中等。这部分配置取代了原本存在于 train_pipeline 和 test_pipeline 中的 `Normalize` 和 `Pad`。\n\n<table class=\"docutils\">\n<tr>\n<td>原配置</td>\n<td>\n\n```python\n# 图像归一化参数\nimg_norm_cfg = dict(\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    to_rgb=True)\npipeline=[\n    ...,\n    dict(type='Normalize', **img_norm_cfg),\n    dict(type='Pad', size_divisor=32),  # 图像 padding 到 32 的倍数\n    ...\n]\n```\n\n</td>\n<tr>\n<td>新配置</td>\n<td>\n\n```python\nmodel = dict(\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        # 图像归一化参数\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        # 图像 padding 参数\n        pad_mask=True,  # 在实例分割中，需要将 mask 也进行 padding\n        pad_size_divisor=32)  # 图像 padding 到 32 的倍数\n)\n```\n\n</td>\n</tr>\n</table>\n\n## 数据集和评测器配置\n\n数据集和评测部分的配置相比 2.x 版本有较大的变化。我们将从 Dataloader 和 Dataset，Data transform pipeline，以及评测器配置三个方面介绍如何将 2.x 版本的配置迁移到 3.x 版本。\n\n### Dataloader 和 Dataset 配置\n\n在新版本中，我们将数据加载的设置与 PyTorch 官方的 DataLoader 保持一致，这样可以使用户更容易理解和上手。\n我们将训练、验证和测试的数据加载设置分别放在 `train_dataloader`，`val_dataloader` 和 `test_dataloader` 中，用户可以分别对这些 dataloader 设置不同的参数，其输入参数与 [PyTorch 的 Dataloader](https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader) 所需要的参数基本一致。\n\n通过这种方式，我们将 2.x 版本中不可配置的 `sampler`，`batch_sampler`，`persistent_workers` 等参数都放到了配置文件中，使得用户可以更加灵活地设置数据加载的参数。\n\n用户可以通过 `train_dataloader.dataset`，`val_dataloader.dataset` 和 `test_dataloader.dataset` 来设置数据集的配置，它们分别对应 2.x 版本中的 `data.train`，`data.val` 和 `data.test`。\n\n<table class=\"docutils\">\n<tr>\n<td>原配置</td>\n<td>\n\n```python\ndata = dict(\n    samples_per_gpu=2,\n    workers_per_gpu=2,\n    train=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_train2017.json',\n        img_prefix=data_root + 'train2017/',\n        pipeline=train_pipeline),\n    val=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        img_prefix=data_root + 'val2017/',\n        pipeline=test_pipeline),\n    test=dict(\n        type=dataset_type,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        img_prefix=data_root + 'val2017/',\n        pipeline=test_pipeline))\n```\n\n</td>\n<tr>\n<td>新配置</td>\n<td>\n\n```python\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,  # 避免每次迭代后 dataloader 重新创建子进程\n    sampler=dict(type='DefaultSampler', shuffle=True),  # 默认的 sampler，同时支持分布式训练和非分布式训练\n    batch_sampler=dict(type='AspectRatioBatchSampler'),  # 默认的 batch_sampler，用于保证 batch 中的图片具有相似的长宽比，从而可以更好地利用显存\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline))\n# 在 3.x 版本中可以独立配置验证和测试的 dataloader\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader  # 测试 dataloader 的配置与验证 dataloader 的配置相同，这里省略\n```\n\n</td>\n</tr>\n</table>\n\n### Data transform pipeline 配置\n\n上文中提到，我们将图像 normalize 和 padding 的配置从 `train_pipeline` 和 `test_pipeline` 中独立出来，放到了 `model.data_preprocessor` 中，因此在 3.x 版本的 pipeline 中，我们不再需要 `Normalize` 和 `Pad` 这两个 transform。\n\n同时，我们也对负责数据格式打包的 transform 进行了重构，将 `Collect` 和 `DefaultFormatBundle` 这两个 transform 合并为了 `PackDetInputs`，它负责将 data pipeline 中的数据打包成模型的输入格式，关于输入格式的转换，详见[数据流文档](../advanced_guides/data_flow.md)。\n\n下面以 Mask R-CNN 1x 的 train_pipeline 为例，介绍如何将 2.x 版本的配置迁移到 3.x 版本：\n\n<table class=\"docutils\">\n<tr>\n<td>原配置</td>\n<td>\n\n```python\nimg_norm_cfg = dict(\n    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', flip_ratio=0.5),\n    dict(type='Normalize', **img_norm_cfg),\n    dict(type='Pad', size_divisor=32),\n    dict(type='DefaultFormatBundle'),\n    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),\n]\n```\n\n</td>\n<tr>\n<td>新配置</td>\n<td>\n\n```python\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n```\n\n</td>\n</tr>\n</table>\n\n对于 test_pipeline，除了将 `Normalize` 和 `Pad` 这两个 transform 去掉之外，我们也将测试时的数据增强（TTA）与普通的测试流程分开，移除了 `MultiScaleFlipAug`。关于新版的 TTA 如何使用，详见[TTA 文档](../advanced_guides/tta.md)。\n\n下面同样以 Mask R-CNN 1x 的 test_pipeline 为例，介绍如何将 2.x 版本的配置迁移到 3.x 版本：\n\n<table class=\"docutils\">\n<tr>\n<td>原配置</td>\n<td>\n\n```python\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(\n        type='MultiScaleFlipAug',\n        img_scale=(1333, 800),\n        flip=False,\n        transforms=[\n            dict(type='Resize', keep_ratio=True),\n            dict(type='RandomFlip'),\n            dict(type='Normalize', **img_norm_cfg),\n            dict(type='Pad', size_divisor=32),\n            dict(type='ImageToTensor', keys=['img']),\n            dict(type='Collect', keys=['img']),\n        ])\n]\n```\n\n</td>\n<tr>\n<td>新配置</td>\n<td>\n\n```python\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n```\n\n</td>\n</tr>\n</table>\n\n除此之外，我们还对一些数据增强进行了重构，下表列出了 2.x 版本中的 transform 与 3.x 版本中的 transform 的对应关系：\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>名称</th>\n    <th>原配置</th>\n    <th>新配置</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>Resize</td>\n<td>\n\n```python\ndict(type='Resize',\n     img_scale=(1333, 800),\n     keep_ratio=True)\n```\n\n</td>\n\n<td>\n\n```python\ndict(type='Resize',\n     scale=(1333, 800),\n     keep_ratio=True)\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>RandomResize</td>\n<td>\n\n```python\ndict(\n    type='Resize',\n    img_scale=[\n        (1333, 640), (1333, 800)],\n    multiscale_mode='range',\n    keep_ratio=True)\n```\n\n</td>\n<td>\n\n```python\ndict(\n    type='RandomResize',\n    scale=[\n        (1333, 640), (1333, 800)],\n    keep_ratio=True)\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>RandomChoiceResize</td>\n<td>\n\n```python\ndict(\n    type='Resize',\n    img_scale=[\n        (1333, 640), (1333, 672),\n        (1333, 704), (1333, 736),\n        (1333, 768), (1333, 800)],\n    multiscale_mode='value',\n    keep_ratio=True)\n```\n\n</td>\n<td>\n\n```python\ndict(\n    type='RandomChoiceResize',\n    scales=[\n        (1333, 640), (1333, 672),\n        (1333, 704), (1333, 736),\n        (1333, 768), (1333, 800)],\n    keep_ratio=True)\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>RandomFlip</td>\n<td>\n\n```python\ndict(type='RandomFlip',\n     flip_ratio=0.5)\n```\n\n</td>\n<td>\n\n```python\ndict(type='RandomFlip',\n     prob=0.5)\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\n### 评测器配置\n\n在 3.x 版本中，模型精度评测不再与数据集绑定，而是通过评测器（Evaluator）来完成。\n评测器配置分为 val_evaluator 和 test_evaluator 两部分，其中 val_evaluator 用于验证集评测，test_evaluator 用于测试集评测，对应 2.x 版本中的 evaluation 字段。\n下表列出了 2.x 版本与 3.x 版本中的评测器的对应关系：\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>评测指标名称</th>\n    <th>原配置</th>\n    <th>新配置</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>COCO</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type='CocoDataset',\n        ann_file=data_root + 'annotations/instances_val2017.json'))\nevaluation = dict(metric=['bbox', 'segm'])\n```\n\n</td>\n\n<td>\n\n```python\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False)\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>Pascal VOC</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type=dataset_type,\n        ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt'))\nevaluation = dict(metric='mAP')\n```\n\n</td>\n<td>\n\n```python\nval_evaluator = dict(\n    type='VOCMetric',\n    metric='mAP',\n    eval_mode='11points')\n```\n\n</td>\n  </tr>\n  <tr>\n    <td>OpenImages</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type='OpenImagesDataset',\n        ann_file=data_root + 'annotations/validation-annotations-bbox.csv',\n        img_prefix=data_root + 'OpenImages/validation/',\n        label_file=data_root + 'annotations/class-descriptions-boxable.csv',\n        hierarchy_file=data_root +\n        'annotations/bbox_labels_600_hierarchy.json',\n        meta_file=data_root + 'annotations/validation-image-metas.pkl',\n        image_level_ann_file=data_root +\n        'annotations/validation-annotations-human-imagelabels-boxable.csv'))\nevaluation = dict(interval=1, metric='mAP')\n```\n\n</td>\n<td>\n\n```python\nval_evaluator = dict(\n    type='OpenImagesMetric',\n    iou_thrs=0.5,\n    ioa_thrs=0.5,\n    use_group_of=True,\n    get_supercategory=True)\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>CityScapes</td>\n<td>\n\n```python\ndata = dict(\n    val=dict(\n        type='CityScapesDataset',\n        ann_file=data_root +\n        'annotations/instancesonly_filtered_gtFine_val.json',\n        img_prefix=data_root + 'leftImg8bit/val/',\n        pipeline=test_pipeline))\nevaluation = dict(metric=['bbox', 'segm'])\n```\n\n</td>\n<td>\n\n```python\nval_evaluator = [\n    dict(\n        type='CocoMetric',\n        ann_file=data_root +\n        'annotations/instancesonly_filtered_gtFine_val.json',\n        metric=['bbox', 'segm']),\n    dict(\n        type='CityScapesMetric',\n        ann_file=data_root +\n        'annotations/instancesonly_filtered_gtFine_val.json',\n        seg_prefix=data_root + '/gtFine/val',\n        outfile_prefix='./work_dirs/cityscapes_metric/instance')\n]\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\n## 训练和测试的配置\n\n<table class=\"docutils\">\n<tr>\n<td>原配置</td>\n<td>\n\n```python\nrunner = dict(\n    type='EpochBasedRunner',  # 训练循环的类型\n    max_epochs=12)  # 最大训练轮次\nevaluation = dict(interval=2)  # 验证间隔。每 2 个 epoch 验证一次\n```\n\n</td>\n<tr>\n<td>新配置</td>\n<td>\n\n```python\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop',  # 训练循环的类型，请参考 https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py\n    max_epochs=12,  # 最大训练轮次\n    val_interval=2)  # 验证间隔。每 2 个 epoch 验证一次\nval_cfg = dict(type='ValLoop')  # 验证循环的类型\ntest_cfg = dict(type='TestLoop')  # 测试循环的类型\n```\n\n</td>\n</tr>\n</table>\n\n## 优化相关配置\n\n优化器以及梯度裁剪的配置都移至 optim_wrapper 字段中。下表列出了 2.x 版本与 3.x 版本中的优化器配置的对应关系：\n\n<table class=\"docutils\">\n<tr>\n<td>原配置</td>\n<td>\n\n```python\noptimizer = dict(\n    type='SGD',  # 随机梯度下降优化器\n    lr=0.02,  # 基础学习率\n    momentum=0.9,  # 带动量的随机梯度下降\n    weight_decay=0.0001)  # 权重衰减\noptimizer_config = dict(grad_clip=None)  # 梯度裁剪的配置，设置为 None 关闭梯度裁剪\n```\n\n</td>\n<tr>\n<td>新配置</td>\n<td>\n\n```python\noptim_wrapper = dict(  # 优化器封装的配置\n    type='OptimWrapper',  # 优化器封装的类型。可以切换至 AmpOptimWrapper 来启用混合精度训练\n    optimizer=dict(  # 优化器配置。支持 PyTorch 的各种优化器。请参考 https://pytorch.org/docs/stable/optim.html#algorithms\n        type='SGD',  # 随机梯度下降优化器\n        lr=0.02,  # 基础学习率\n        momentum=0.9,  # 带动量的随机梯度下降\n        weight_decay=0.0001),  # 权重衰减\n    clip_grad=None,  # 梯度裁剪的配置，设置为 None 关闭梯度裁剪。使用方法请见 https://mmengine.readthedocs.io/en/latest/tutorials/optimizer.html\n    )\n```\n\n</td>\n</tr>\n</table>\n\n学习率的配置也从 lr_config 字段中移至 param_scheduler 字段中。param_scheduler 的配置更贴近 PyTorch 的学习率调整策略，更加灵活。下表列出了 2.x 版本与 3.x 版本中的学习率配置的对应关系：\n\n<table class=\"docutils\">\n<tr>\n<td>原配置</td>\n<td>\n\n```python\nlr_config = dict(\n    policy='step',  # 在训练过程中使用 multi step 学习率策略\n    warmup='linear',  # 使用线性学习率预热\n    warmup_iters=500,  # 到第 500 个 iteration 结束预热\n    warmup_ratio=0.001,  # 学习率预热的系数\n    step=[8, 11],  # 在哪几个 epoch 进行学习率衰减\n    gamma=0.1)  # 学习率衰减系数\n```\n\n</td>\n<tr>\n<td>新配置</td>\n<td>\n\n```python\nparam_scheduler = [\n    dict(\n        type='LinearLR',  # 使用线性学习率预热\n        start_factor=0.001, # 学习率预热的系数\n        by_epoch=False,  # 按 iteration 更新预热学习率\n        begin=0,  # 从第一个 iteration 开始\n        end=500),  # 到第 500 个 iteration 结束\n    dict(\n        type='MultiStepLR',  # 在训练过程中使用 multi step 学习率策略\n        by_epoch=True,  # 按 epoch 更新学习率\n        begin=0,   # 从第一个 epoch 开始\n        end=12,  # 到第 12 个 epoch 结束\n        milestones=[8, 11],  # 在哪几个 epoch 进行学习率衰减\n        gamma=0.1)  # 学习率衰减系数\n]\n```\n\n</td>\n</tr>\n</table>\n\n关于其他的学习率调整策略的迁移，请参考 MMEngine 的[学习率迁移文档](https://mmengine.readthedocs.io/zh_CN/latest/migration/param_scheduler.html)。\n\n## 其他配置的迁移\n\n### 保存 checkpoint 的配置\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>功能</th>\n    <th>原配置</th>\n    <th>新配置</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>设置保存间隔</td>\n<td>\n\n```python\ncheckpoint_config = dict(\n    interval=1)\n```\n\n</td>\n\n<td>\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        interval=1))\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>保存最佳模型</td>\n<td>\n\n```python\nevaluation = dict(\n    save_best='auto')\n```\n\n</td>\n<td>\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        save_best='auto'))\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>只保留最新的几个模型</td>\n<td>\n\n```python\ncheckpoint_config = dict(\n    max_keep_ckpts=3)\n```\n\n</td>\n<td>\n\n```python\ndefault_hooks = dict(\n    checkpoint=dict(\n        type='CheckpointHook',\n        max_keep_ckpts=3))\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\n### 日志的配置\n\n3.x 版本中，日志的打印和可视化由 MMEngine 中的 logger 和 visualizer 分别完成。下表列出了 2.x 版本与 3.x 版本中的日志配置的对应关系：\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>功能</th>\n    <th>原配置</th>\n    <th>新配置</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>设置日志打印间隔</td>\n<td>\n\n```python\nlog_config = dict(\n    interval=50)\n```\n\n</td>\n\n<td>\n\n```python\ndefault_hooks = dict(\n    logger=dict(\n        type='LoggerHook',\n        interval=50))\n# 可选： 配置日志打印数值的平滑窗口大小\nlog_processor = dict(\n    type='LogProcessor',\n    window_size=50)\n```\n\n</td>\n  </tr>\n\n<tr>\n    <td>使用 TensorBoard 或 WandB 可视化日志</td>\n<td>\n\n```python\nlog_config = dict(\n    interval=50,\n    hooks=[\n        dict(type='TextLoggerHook'),\n        dict(type='TensorboardLoggerHook'),\n        dict(type='MMDetWandbHook',\n             init_kwargs={\n                'project': 'mmdetection',\n                'group': 'maskrcnn-r50-fpn-1x-coco'\n             },\n             interval=50,\n             log_checkpoint=True,\n             log_checkpoint_metadata=True,\n             num_eval_images=100)\n    ])\n```\n\n</td>\n<td>\n\n```python\nvis_backends = [\n    dict(type='LocalVisBackend'),\n    dict(type='TensorboardVisBackend'),\n    dict(type='WandbVisBackend',\n         init_kwargs={\n            'project': 'mmdetection',\n            'group': 'maskrcnn-r50-fpn-1x-coco'\n         })\n]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n\n关于可视化相关的教程，请参考 MMDetection 的[可视化教程](../user_guides/visualization.md)。\n\n### Runtime 的配置\n\n3.x 版本中 runtime 的配置字段有所调整，具体的对应关系如下：\n\n<table class=\"docutils\">\n<thead>\n  <tr>\n    <th>原配置</th>\n    <th>新配置</th>\n  </tr>\n</thead>\n<tbody>\n\n<tr>\n<td>\n\n```python\ncudnn_benchmark = False\nopencv_num_threads = 0\nmp_start_method = 'fork'\ndist_params = dict(backend='nccl')\nlog_level = 'INFO'\nload_from = None\nresume_from = None\n\n\n```\n\n</td>\n<td>\n\n```python\nenv_cfg = dict(\n    cudnn_benchmark=False,\n    mp_cfg=dict(mp_start_method='fork',\n                opencv_num_threads=0),\n    dist_cfg=dict(backend='nccl'))\nlog_level = 'INFO'\nload_from = None\nresume = False\n```\n\n</td>\n  </tr>\n\n</tbody>\n</table>\n"
  },
  {
    "path": "docs/zh_cn/migration/dataset_migration.md",
    "content": "# 将数据集从 MMDetection 2.x 迁移至 3.x\n"
  },
  {
    "path": "docs/zh_cn/migration/migration.md",
    "content": "# 从 MMDetection 2.x 迁移至 3.x\n\nMMDetection 3.x 版本是一个重大更新，包含了许多 API 和配置文件的变化。本文档旨在帮助用户从 MMDetection 2.x 版本迁移到 3.x 版本。\n我们将迁移指南分为以下几个部分：\n\n- [配置文件迁移](./config_migration.md)\n- [API 和 Registry 迁移](./api_and_registry_migration.md)\n- [数据集迁移](./dataset_migration.md)\n- [模型迁移](./model_migration.md)\n- [常见问题](./migration_faq.md)\n\n如果您在迁移过程中遇到任何问题，欢迎在 issue 中提出。我们也欢迎您为本文档做出贡献。\n"
  },
  {
    "path": "docs/zh_cn/migration/migration_faq.md",
    "content": "# 迁移 FAQ\n"
  },
  {
    "path": "docs/zh_cn/migration/model_migration.md",
    "content": "# 将模型从 MMDetection 2.x 迁移至 3.x\n"
  },
  {
    "path": "docs/zh_cn/model_zoo.md",
    "content": "# 模型库\n\n## 镜像地址\n\n从 MMDetection V2.0 起，我们只通过阿里云维护模型库。V1.x 版本的模型已经弃用。\n\n## 共同设置\n\n- 所有模型都是在 `coco_2017_train` 上训练，在 `coco_2017_val` 上测试。\n- 我们使用分布式训练。\n- 所有 pytorch-style 的 ImageNet 预训练主干网络来自 PyTorch 的模型库，caffe-style 的预训练主干网络来自 detectron2 最新开源的模型。\n- 为了与其他代码库公平比较，文档中所写的 GPU 内存是8个 GPU 的 `torch.cuda.max_memory_allocated()` 的最大值，此值通常小于 nvidia-smi 显示的值。\n- 我们以网络 forward 和后处理的时间加和作为推理时间，不包含数据加载时间。所有结果通过 [benchmark.py](https://github.com/open-mmlab/mmdetection/blob/main/tools/analysis_tools/benchmark.py) 脚本计算所得。该脚本会计算推理 2000 张图像的平均时间。\n\n## ImageNet 预训练模型\n\n通过 ImageNet 分类任务预训练的主干网络进行初始化是很常见的操作。所有预训练模型的链接都可以在 [open_mmlab](https://github.com/open-mmlab/mmcv/blob/master/mmcv/model_zoo/open_mmlab.json) 中找到。根据 `img_norm_cfg` 和原始权重，我们可以将所有 ImageNet 预训练模型分为以下几种情况：\n\n- TorchVision：torchvision 模型权重，包含 ResNet50, ResNet101。`img_norm_cfg` 为 `dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)`。\n- Pycls：[pycls](https://github.com/facebookresearch/pycls) 模型权重，包含 RegNetX。`img_norm_cfg` 为 `dict(   mean=[103.530, 116.280, 123.675], std=[57.375, 57.12, 58.395], to_rgb=False)`。\n- MSRA styles：[MSRA](https://github.com/KaimingHe/deep-residual-networks) 模型权重，包含 ResNet50_Caffe，ResNet101_Caffe。`img_norm_cfg` 为 `dict(   mean=[103.530, 116.280, 123.675], std=[1.0, 1.0, 1.0], to_rgb=False)`。\n- Caffe2 styles：现阶段只包含 ResNext101_32x8d。`img_norm_cfg` 为 `dict(mean=[103.530, 116.280, 123.675], std=[57.375, 57.120, 58.395], to_rgb=False)`。\n- Other styles: SSD 的 `img_norm_cfg` 为 `dict(mean=[123.675, 116.28, 103.53], std=[1, 1, 1], to_rgb=True)`，YOLOv3 的 `img_norm_cfg` 为 `dict(mean=[0, 0, 0], std=[255., 255., 255.], to_rgb=True)`。\n\nMMdetection 常用到的主干网络细节如下表所示：\n\n| 模型             | 来源        | 链接                                                                                                                                                                                                | 描述                                                                                                                                                                                                                       |\n| ---------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| ResNet50         | TorchVision | [torchvision 中的 ResNet-50](https://download.pytorch.org/models/resnet50-19c8e357.pth)                                                                                                             | 来自 [torchvision 中的 ResNet-50](https://download.pytorch.org/models/resnet50-19c8e357.pth)。                                                                                                                             |\n| ResNet101        | TorchVision | [torchvision 中的 ResNet-101](https://download.pytorch.org/models/resnet101-5d3b4d8f.pth)                                                                                                           | 来自 [torchvision 中的 ResNet-101](https://download.pytorch.org/models/resnet101-5d3b4d8f.pth)。                                                                                                                           |\n| RegNetX          | Pycls       | [RegNetX_3.2gf](https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth)，[RegNetX_800mf](https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth) 等 | 来自 [pycls](https://github.com/facebookresearch/pycls)。                                                                                                                                                                  |\n| ResNet50_Caffe   | MSRA        | [MSRA 中的 ResNet-50](https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth)                                                                                              | 由 [Detectron2 中的 R-50.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/MSRA/R-50.pkl) 转化的副本。原始权重文件来自 [MSRA 中的原始 ResNet-50](https://github.com/KaimingHe/deep-residual-networks)。    |\n| ResNet101_Caffe  | MSRA        | [MSRA 中的 ResNet-101](https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth)                                                                                            | 由 [Detectron2 中的 R-101.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/MSRA/R-101.pkl) 转化的副本。原始权重文件来自 [MSRA 中的原始 ResNet-101](https://github.com/KaimingHe/deep-residual-networks)。 |\n| ResNext101_32x8d | Caffe2      | [Caffe2 ResNext101_32x8d](https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth)                                                                                        | 由 [Detectron2 中的 X-101-32x8d.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/FAIR/X-101-32x8d.pkl) 转化的副本。原始 ResNeXt-101-32x8d 由 FB 使用 Caffe2 训练。                                        |\n\n## Baselines\n\n### RPN\n\n请参考 [RPN](https://github.com/open-mmlab/mmdetection/blob/main/configs/rpn)。\n\n### Faster R-CNN\n\n请参考 [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn)。\n\n### Mask R-CNN\n\n请参考 [Mask R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn)。\n\n### Fast R-CNN (使用提前计算的 proposals)\n\n请参考 [Fast R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/fast_rcnn)。\n\n### RetinaNet\n\n请参考 [RetinaNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/retinanet)。\n\n### Cascade R-CNN and Cascade Mask R-CNN\n\n请参考 [Cascade R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/cascade_rcnn)。\n\n### Hybrid Task Cascade (HTC)\n\n请参考 [HTC](https://github.com/open-mmlab/mmdetection/blob/main/configs/htc)。\n\n### SSD\n\n请参考 [SSD](https://github.com/open-mmlab/mmdetection/blob/main/configs/ssd)。\n\n### Group Normalization (GN)\n\n请参考 [Group Normalization](https://github.com/open-mmlab/mmdetection/blob/main/configs/gn)。\n\n### Weight Standardization\n\n请参考 [Weight Standardization](https://github.com/open-mmlab/mmdetection/blob/main/configs/gn+ws)。\n\n### Deformable Convolution v2\n\n请参考 [Deformable Convolutional Networks](https://github.com/open-mmlab/mmdetection/blob/main/configs/dcn)。\n\n### CARAFE: Content-Aware ReAssembly of FEatures\n\n请参考 [CARAFE](https://github.com/open-mmlab/mmdetection/blob/main/configs/carafe)。\n\n### Instaboost\n\n请参考 [Instaboost](https://github.com/open-mmlab/mmdetection/blob/main/configs/instaboost)。\n\n### Libra R-CNN\n\n请参考 [Libra R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/libra_rcnn)。\n\n### Guided Anchoring\n\n请参考 [Guided Anchoring](https://github.com/open-mmlab/mmdetection/blob/main/configs/guided_anchoring)。\n\n### FCOS\n\n请参考 [FCOS](https://github.com/open-mmlab/mmdetection/blob/main/configs/fcos)。\n\n### FoveaBox\n\n请参考 [FoveaBox](https://github.com/open-mmlab/mmdetection/blob/main/configs/foveabox)。\n\n### RepPoints\n\n请参考 [RepPoints](https://github.com/open-mmlab/mmdetection/blob/main/configs/reppoints)。\n\n### FreeAnchor\n\n请参考 [FreeAnchor](https://github.com/open-mmlab/mmdetection/blob/main/configs/free_anchor)。\n\n### Grid R-CNN (plus)\n\n请参考 [Grid R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/grid_rcnn)。\n\n### GHM\n\n请参考 [GHM](https://github.com/open-mmlab/mmdetection/blob/main/configs/ghm)。\n\n### GCNet\n\n请参考 [GCNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/gcnet)。\n\n### HRNet\n\n请参考 [HRNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/hrnet)。\n\n### Mask Scoring R-CNN\n\n请参考 [Mask Scoring R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/ms_rcnn)。\n\n### Train from Scratch\n\n请参考 [Rethinking ImageNet Pre-training](https://github.com/open-mmlab/mmdetection/blob/main/configs/scratch)。\n\n### NAS-FPN\n\n请参考 [NAS-FPN](https://github.com/open-mmlab/mmdetection/blob/main/configs/nas_fpn)。\n\n### ATSS\n\n请参考 [ATSS](https://github.com/open-mmlab/mmdetection/blob/main/configs/atss)。\n\n### FSAF\n\n请参考 [FSAF](https://github.com/open-mmlab/mmdetection/blob/main/configs/fsaf)。\n\n### RegNetX\n\n请参考 [RegNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/regnet)。\n\n### Res2Net\n\n请参考 [Res2Net](https://github.com/open-mmlab/mmdetection/blob/main/configs/res2net)。\n\n### GRoIE\n\n请参考 [GRoIE](https://github.com/open-mmlab/mmdetection/blob/main/configs/groie)。\n\n### Dynamic R-CNN\n\n请参考 [Dynamic R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/dynamic_rcnn)。\n\n### PointRend\n\n请参考 [PointRend](https://github.com/open-mmlab/mmdetection/blob/main/configs/point_rend)。\n\n### DetectoRS\n\n请参考 [DetectoRS](https://github.com/open-mmlab/mmdetection/blob/main/configs/detectors)。\n\n### Generalized Focal Loss\n\n请参考 [Generalized Focal Loss](https://github.com/open-mmlab/mmdetection/blob/main/configs/gfl)。\n\n### CornerNet\n\n请参考 [CornerNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/cornernet)。\n\n### YOLOv3\n\n请参考 [YOLOv3](https://github.com/open-mmlab/mmdetection/blob/main/configs/yolo)。\n\n### PAA\n\n请参考 [PAA](https://github.com/open-mmlab/mmdetection/blob/main/configs/paa)。\n\n### SABL\n\n请参考 [SABL](https://github.com/open-mmlab/mmdetection/blob/main/configs/sabl)。\n\n### CentripetalNet\n\n请参考 [CentripetalNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/centripetalnet)。\n\n### ResNeSt\n\n请参考 [ResNeSt](https://github.com/open-mmlab/mmdetection/blob/main/configs/resnest)。\n\n### DETR\n\n请参考 [DETR](https://github.com/open-mmlab/mmdetection/blob/main/configs/detr)。\n\n### Deformable DETR\n\n请参考 [Deformable DETR](https://github.com/open-mmlab/mmdetection/blob/main/configs/deformable_detr)。\n\n### AutoAssign\n\n请参考 [AutoAssign](https://github.com/open-mmlab/mmdetection/blob/main/configs/autoassign)。\n\n### YOLOF\n\n请参考 [YOLOF](https://github.com/open-mmlab/mmdetection/blob/main/configs/yolof)。\n\n### Seesaw Loss\n\n请参考 [Seesaw Loss](https://github.com/open-mmlab/mmdetection/blob/main/configs/seesaw_loss)。\n\n### CenterNet\n\n请参考 [CenterNet](https://github.com/open-mmlab/mmdetection/blob/main/configs/centernet)。\n\n### YOLOX\n\n请参考 [YOLOX](https://github.com/open-mmlab/mmdetection/blob/main/configs/yolox)。\n\n### PVT\n\n请参考 [PVT](https://github.com/open-mmlab/mmdetection/blob/main/configs/pvt)。\n\n### SOLO\n\n请参考 [SOLO](https://github.com/open-mmlab/mmdetection/blob/main/configs/solo)。\n\n### QueryInst\n\n请参考 [QueryInst](https://github.com/open-mmlab/mmdetection/blob/main/configs/queryinst)。\n\n### Other datasets\n\n我们还在 [PASCAL VOC](https://github.com/open-mmlab/mmdetection/blob/main/configs/pascal_voc)，[Cityscapes](https://github.com/open-mmlab/mmdetection/blob/main/configs/cityscapes) 和 [WIDER FACE](https://github.com/open-mmlab/mmdetection/blob/main/configs/wider_face) 上对一些方法进行了基准测试。\n\n### Pre-trained Models\n\n我们还通过多尺度训练和更长的训练策略来训练用 ResNet-50 和 [RegNetX-3.2G](https://github.com/open-mmlab/mmdetection/blob/main/configs/regnet) 作为主干网络的 [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn) 和 [Mask R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn)。这些模型可以作为下游任务的预训练模型。\n\n## 速度基准\n\n### 训练速度基准\n\n我们提供 [analyze_logs.py](https://github.com/open-mmlab/mmdetection/blob/main/tools/analysis_tools/analyze_logs.py) 来得到训练中每一次迭代的平均时间。示例请参考 [Log Analysis](https://mmdetection.readthedocs.io/en/latest/useful_tools.html#log-analysis)。\n\n我们与其他流行框架的 Mask R-CNN 训练速度进行比较（数据是从 [detectron2](https://github.com/facebookresearch/detectron2/blob/main/docs/notes/benchmarks.md/) 复制而来）。在 mmdetection 中，我们使用 [mask-rcnn_r50-caffe_fpn_poly-1x_coco_v1.py](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_poly-1x_coco_v1.py) 进行基准测试。它与 detectron2 的 [mask_rcnn_R_50_FPN_noaug_1x.yaml](https://github.com/facebookresearch/detectron2/blob/main/configs/Detectron1-Comparisons/mask_rcnn_R_50_FPN_noaug_1x.yaml) 设置完全一样。同时，我们还提供了[模型权重](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug_compare_20200518-10127928.pth)和[训练 log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug/mask_rcnn_r50_caffe_fpn_poly_1x_coco_no_aug_20200518_105755.log.json) 作为参考。为了跳过 GPU 预热时间，吞吐量按照100-500次迭代之间的平均吞吐量来计算。\n\n| 框架                                                                                   | 吞吐量 (img/s) |\n| -------------------------------------------------------------------------------------- | -------------- |\n| [Detectron2](https://github.com/facebookresearch/detectron2)                           | 62             |\n| [MMDetection](https://github.com/open-mmlab/mmdetection)                               | 61             |\n| [maskrcnn-benchmark](https://github.com/facebookresearch/maskrcnn-benchmark/)          | 53             |\n| [tensorpack](https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN) | 50             |\n| [simpledet](https://github.com/TuSimple/simpledet/)                                    | 39             |\n| [Detectron](https://github.com/facebookresearch/Detectron)                             | 19             |\n| [matterport/Mask_RCNN](https://github.com/matterport/Mask_RCNN/)                       | 14             |\n\n### 推理时间基准\n\n我们提供 [benchmark.py](https://github.com/open-mmlab/mmdetection/blob/main/tools/analysis_tools/benchmark.py) 对推理时间进行基准测试。此脚本将推理 2000 张图片并计算忽略前 5 次推理的平均推理时间。可以通过设置 `LOG-INTERVAL` 来改变 log 输出间隔（默认为 50）。\n\n```shell\npython tools/benchmark.py ${CONFIG} ${CHECKPOINT} [--log-interval $[LOG-INTERVAL]] [--fuse-conv-bn]\n```\n\n模型库中，所有模型在基准测量推理时间时都没设置 `fuse-conv-bn`, 此设置可以使推理时间更短。\n\n## 与 Detectron2 对比\n\n我们在速度和精度方面对 mmdetection 和 [Detectron2](https://github.com/facebookresearch/detectron2.git) 进行对比。对比所使用的 detectron2 的 commit id 为 [185c27e](https://github.com/facebookresearch/detectron2/tree/185c27e4b4d2d4c68b5627b3765420c6d7f5a659)(30/4/2020)。\n为了公平对比，我们所有的实验都在同一机器下进行。\n\n### 硬件\n\n- 8 NVIDIA Tesla V100 (32G) GPUs\n- Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz\n\n### 软件环境\n\n- Python 3.7\n- PyTorch 1.4\n- CUDA 10.1\n- CUDNN 7.6.03\n- NCCL 2.4.08\n\n### 精度\n\n| 模型                                                                                                                            | 训练策略 | Detectron2                                                                                                                             | mmdetection | 下载                                                                                                                                                                                                                                                                                                                                                             |\n| ------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_ms-1x_coco.py) | 1x       | [37.9](https://github.com/facebookresearch/detectron2/blob/main/configs/COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml)                   | 38.0        | [model](https://download.openmmlab.com/mmdetection/v2.0/benchmark/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco-5324cff8.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco/faster_rcnn_r50_caffe_fpn_mstrain_1x_coco_20200429_234554.log.json)             |\n| [Mask R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py)  | 1x       | [38.6 & 35.2](https://github.com/facebookresearch/detectron2/blob/master/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml) | 38.8 & 35.4 | [model](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco-dbecf295.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_1x_coco_20200430_054239.log.json) |\n| [Retinanet](https://github.com/open-mmlab/mmdetection/blob/main/configs/retinanet/retinanet_r50-caffe_fpn_ms-1x_coco.py)        | 1x       | [36.5](https://github.com/facebookresearch/detectron2/blob/master/configs/COCO-Detection/retinanet_R_50_FPN_1x.yaml)                   | 37.0        | [model](https://download.openmmlab.com/mmdetection/v2.0/benchmark/retinanet_r50_caffe_fpn_mstrain_1x_coco/retinanet_r50_caffe_fpn_mstrain_1x_coco-586977a0.pth) \\| [log](https://download.openmmlab.com/mmdetection/v2.0/benchmark/retinanet_r50_caffe_fpn_mstrain_1x_coco/retinanet_r50_caffe_fpn_mstrain_1x_coco_20200430_014748.log.json)                     |\n\n### 训练速度\n\n训练速度使用 s/iter 来度量。结果越低越好。\n\n| 模型         | Detectron2 | mmdetection |\n| ------------ | ---------- | ----------- |\n| Faster R-CNN | 0.210      | 0.216       |\n| Mask R-CNN   | 0.261      | 0.265       |\n| Retinanet    | 0.200      | 0.205       |\n\n### 推理速度\n\n推理速度通过单张 GPU 下的 fps(img/s) 来度量，越高越好。\n为了与 Detectron2 保持一致，我们所写的推理时间除去了数据加载时间。\n对于 Mask RCNN，我们去除了后处理中 RLE 编码的时间。\n我们在括号中给出了官方给出的速度。由于硬件差异，官方给出的速度会比我们所测试得到的速度快一些。\n\n| 模型         | Detectron2  | mmdetection |\n| ------------ | ----------- | ----------- |\n| Faster R-CNN | 25.6 (26.3) | 22.2        |\n| Mask R-CNN   | 22.5 (23.3) | 19.6        |\n| Retinanet    | 17.8 (18.2) | 20.6        |\n\n### 训练内存\n\n| 模型         | Detectron2 | mmdetection |\n| ------------ | ---------- | ----------- |\n| Faster R-CNN | 3.0        | 3.8         |\n| Mask R-CNN   | 3.4        | 3.9         |\n| Retinanet    | 3.9        | 3.4         |\n"
  },
  {
    "path": "docs/zh_cn/notes/compatibility.md",
    "content": "# MMDetection v2.x 兼容性说明\n\n## MMDetection 2.25.0\n\n为了加入 Mask2Former 实例分割模型，对 Mask2Former 的配置文件进行了重命名 [PR #7571](https://github.com/open-mmlab/mmdetection/pull/7571)：\n\n<table align=\"center\">\n    <thead>\n        <tr align='center'>\n            <td>在 v2.25.0 之前</td>\n            <td>v2.25.0 及之后</td>\n        </tr>\n    </thead>\n    <tbody><tr valign='top'>\n    <th>\n\n```\n'mask2former_xxx_coco.py' 代表全景分割的配置文件\n```\n\n</th>\n    <th>\n\n```\n'mask2former_xxx_coco.py' 代表实例分割的配置文件\n'mask2former_xxx_coco-panoptic.py' 代表全景分割的配置文件\n```\n\n</th></tr>\n  </tbody></table>\n\n## MMDetection 2.21.0\n\n为了支持 CPU 训练，MMCV 中进行批处理的 scatter 的代码逻辑已经被修改。我们推荐使用 MMCV v1.4.4 或更高版本，\n更多信息请参考 [MMCV PR #1621](https://github.com/open-mmlab/mmcv/pull/1621).\n\n## MMDetection 2.18.1\n\n### MMCV compatibility\n\n为了修复 BaseTransformerLayer 中的权重引用问题, MultiheadAttention 中 batch first 的逻辑有所改变。\n我们推荐使用 MMCV v1.3.17 或更高版本。 更多信息请参考 [MMCV PR #1418](https://github.com/open-mmlab/mmcv/pull/1418) 。\n\n## MMDetection 2.18.0\n\n### DIIHead 兼容性\n\n为了支持 QueryInst，在 DIIHead 的返回元组中加入了 attn_feats。\n\n## MMDetection v2.14.0\n\n### MMCV 版本\n\n为了修复 EvalHook 优先级过低的问题，MMCV v1.3.8 中所有 hook 的优先级都重新进行了调整，因此 MMDetection v2.14.0 需要依赖最新的 MMCV v1.3.8 版本。 相关信息请参考[PR #1120](https://github.com/open-mmlab/mmcv/pull/1120) ，相关问题请参考[#5343](https://github.com/open-mmlab/mmdetection/issues/5343) 。\n\n### SSD 兼容性\n\n在 v2.14.0 中，为了使 SSD 能够被更灵活地使用，[PR #5291](https://github.com/open-mmlab/mmdetection/pull/5291) 重构了 SSD 的 backbone、neck 和 head。用户可以使用 tools/model_converters/upgrade_ssd_version.py 转换旧版本训练的模型。\n\n```shell\npython tools/model_converters/upgrade_ssd_version.py ${OLD_MODEL_PATH} ${NEW_MODEL_PATH}\n\n```\n\n- OLD_MODEL_PATH：旧版 SSD 模型的路径。\n- NEW_MODEL_PATH：保存转换后模型权重的路径。\n\n## MMDetection v2.12.0\n\n在 v2.12.0 到 v2.18.0（或以上）版本的这段时间，为了提升通用性和便捷性，MMDetection 正在进行大规模重构。在升级到 v2.12.0 后 MMDetection 不可避免地带来了一些 BC Breaking，包括 MMCV 的版本依赖、模型初始化方式、模型 registry 和 mask AP 的评估。\n\n### MMCV 版本\n\nMMDetection v2.12.0 依赖 MMCV v1.3.3 中新增加的功能，包括：使用 `BaseModule` 统一参数初始化，模型 registry，以及[Deformable DETR](https://arxiv.org/abs/2010.04159) 中的 `MultiScaleDeformableAttn` CUDA 算子。\n注意，尽管 MMCV v1.3.2 已经包含了 MMDet 所需的功能，但是存在一些已知的问题。我们建议用户跳过 MMCV v1.3.2 使用 v1.3.3 版本。\n\n### 统一模型初始化\n\n为了统一 OpenMMLab 项目中的参数初始化方式，MMCV 新增加了 `BaseModule` 类，使用 `init_cfg` 参数对模块进行统一且灵活的初始化配置管理。\n现在用户需要在训练脚本中显式调用 `model.init_weights()` 来初始化模型（例如 [这行代码](https://github.com/open-mmlab/mmdetection/blob/master/tools/train.py#L162) ，在这之前则是在 detector 中进行处理的。\n**下游项目必须相应地更新模型初始化方式才能使用 MMDetection v2.12.0**。请参阅 [PR #4750](https://github.com/open-mmlab/mmdetection/pull/4750) 了解详情。\n\n### 统一模型 registry\n\n为了能够使用在其他 OpenMMLab 项目中实现的 backbone，MMDetection v2.12.0 继承了在 MMCV (#760) 中创建的模型 registry。\n这样，只要 OpenMMLab 项目实现了某个 backbone，并且该项目也使用 MMCV 中的 registry，那么用户只需修改配置即可在 MMDetection 中使用该 backbone，不再需要将代码复制到 MMDetection 中。 更多详细信息，请参阅 [PR #5059](https://github.com/open-mmlab/mmdetection/pull/5059) 。\n\n### Mask AP 评估\n\n在 [PR #4898](https://github.com/open-mmlab/mmdetection/pull/4898) 和 v2.12.0 之前，对小、中、大目标的 mask AP 的评估是基于其边界框区域而不是真正的 mask 区域。\n这导致 `APs` 和 `APm` 变得更高但 `APl` 变得更低，但是不会影响整体的 mask AP。 [PR #4898](https://github.com/open-mmlab/mmdetection/pull/4898) 删除了 mask AP 计算中的 `bbox` ，改为使用 mask 区域。\n新的计算方式不会影响整体的 mask AP 评估，与 [Detectron2](https://github.com/facebookresearch/detectron2/)一致。\n\n## 与 MMDetection v1.x 的兼容性\n\nMMDetection v2.0 经过了大规模重构并解决了许多遗留问题。 MMDetection v2.0 不兼容 v1.x 版本，在这两个版本中使用相同的模型权重运行推理会产生不同的结果。 因此，MMDetection v2.0 重新对所有模型进行了 benchmark，并在 model zoo 中提供了新模型的权重和训练记录。\n\n新旧版本的主要的区别有四方面：坐标系、代码库约定、训练超参和模块设计。\n\n### 坐标系\n\n新坐标系与 [Detectron2](https://github.com/facebookresearch/detectron2/) 一致，\n将最左上角的像素的中心视为坐标原点 (0, 0) 而不是最左上角像素的左上角。 因此 COCO 边界框和分割标注中的坐标被解析为范围 `[0，width]` 和 `[0，height]` 中的坐标。 这个修改影响了所有与 bbox 及像素选择相关的计算，变得更加自然且更加准确。\n\n- 在新坐标系中，左上角和右下角为 (x1, y1) (x2, y2) 的框的宽度及高度计算公式为 `width = x2 - x1` 和 `height = y2 - y1`。\n  在 MMDetection v1.x 和之前的版本中，高度和宽度都多了 `+ 1` 的操作。\n  本次修改包括三部分：\n\n  1. box 回归中的检测框变换以及编码/解码。\n  2. IoU 计算。这会影响 ground truth 和检测框之间的匹配以及 NMS 。但对兼容性的影响可以忽略不计。\n  3. Box 的角点坐标为浮点型，不再取整。这能使得检测结果更为准确，也使得检测框和 RoI 的最小尺寸不再为 1，但影响很小。\n\n- Anchor 的中心与特征图的网格点对齐，类型变为 float。\n  在 MMDetection v1.x 和之前的版本中，anchors 是 `int` 类型且没有居中对齐。\n  这会影响 RPN 中的 Anchor 生成和所有基于 Anchor 的方法。\n\n- ROIAlign 更好地与图像坐标系对齐。新的实现来自 [Detectron2](https://github.com/facebookresearch/detectron2/tree/master/detectron2/layers/csrc/ROIAlign) 。\n  当 RoI 用于提取 RoI 特征时，与 MMDetection v1.x 相比默认情况下相差半个像素。\n  能够通过设置 `aligned=False` 而不是 `aligned=True` 来维持旧版本的设置。\n\n- Mask 的裁剪和粘贴更准确。\n\n  1. 我们使用新的 RoIAlign 来提取 mask 目标。 在 MMDetection v1.x 中，bounding box 在提取 mask 目标之前被取整，裁剪过程是 numpy 实现的。 而在新版本中，裁剪的边界框不经过取整直接输入 RoIAlign。 此实现大大加快了训练速度（每次迭代约加速 0.1 秒，1x schedule 训练 Mask R50 时加速约 2 小时）并且理论上会更准确。\n  2. 在 MMDetection v2.0 中，修改后的 `paste_mask()` 函数应该比之前版本更准确。 此更改参考了 [Detectron2](https://github.com/facebookresearch/detectron2/blob/master/detectron2/structures/masks.py) 中的修改，可以将 COCO 上的 mask AP 提高约 0.5%。\n\n### 代码库约定\n\n- MMDetection v2.0 更改了类别标签的顺序，减少了回归和 mask 分支里的无用参数并使得顺序更加自然（没有 +1 和 -1）。\n  这会影响模型的所有分类层，使其输出的类别标签顺序发生改变。回归分支和 mask head 的最后一层不再为 K 个类别保留 K+1 个通道，类别顺序与分类分支一致。\n\n  - 在 MMDetection v2.0 中，标签 “K” 表示背景，标签 \\[0, K-1\\] 对应于 K = num_categories 个对象类别。\n\n  - 在 MMDetection v1.x 及之前的版本中，标签 “0” 表示背景，标签 \\[1, K\\] 对应 K 个类别。\n\n  - **注意**：softmax RPN 的类顺序在 version\\<=2.4.0 中仍然和 1.x 中的一样，而 sigmoid RPN 不受影响。从 MMDetection v2.5.0 开始，所有 head 中的类顺序是统一的。\n\n- 不使用 R-CNN 中的低质量匹配。在 MMDetection v1.x 和之前的版本中，`max_iou_assigner` 会在 RPN 和 R-CNN 训练时给每个 ground truth 匹配低质量框。我们发现这会导致最佳的 GT 框不会被分配给某些边界框，\n  因此，在MMDetection v2.0 的 R-CNN 训练中默认不允许低质量匹配。这有时可能会稍微改善 box AP（约为 0.1%）。\n\n- 单独的宽高比例系数。在 MMDetection v1.x 和以前的版本中，`keep_ratio=True` 时比例系数是单个浮点数，这并不准确，因为宽度和高度的比例系数会有一定的差异。 MMDetection v2.0 对宽度和高度使用单独的比例系数，对 AP 的提升约为 0.1%。\n\n- 修改了 config 文件名称的规范。 由于 model zoo 中模型不断增多， MMDetection v2.0 采用新的命名规则：\n\n  ```shell\n  [model]_(model setting)_[backbone]_[neck]_(norm setting)_(misc)_(gpu x batch)_[schedule]_[dataset].py\n  ```\n\n  其中 (`misc`) 包括 DCN 和 GCBlock 等。更多详细信息在 [配置文件说明文档](config.md) 中说明\n\n- MMDetection v2.0 使用新的 ResNet Caffe backbone 来减少加载预训练模型时的警告。新 backbone 中的大部分权重与以前的相同，但没有 `conv.bias`，且它们使用不同的 `img_norm_cfg`。因此，新的 backbone 不会报 `unexpected keys` 的警告。\n\n### 训练超参\n\n训练超参的调整不会影响模型的兼容性，但会略微提高性能。主要有：\n\n- 通过设置 `nms_post=1000` 和 `max_num=1000`，将 nms 之后的 proposal 数量从 2000 更改为 1000。使 mask AP 和 bbox AP 提高了约 0.2%。\n\n- Mask R-CNN、Faster R-CNN 和 RetinaNet 的默认回归损失从 smooth L1 损失更改为 L1 损失，使得 box AP 整体上都有所提升（约 0.6%）。但是，将 L1-loss 用在 Cascade R-CNN 和 HTC 等其他方法上并不能提高性能，因此我们保留这些方法的原始设置。\n\n- 为简单起见，RoIAlign 层的 `sampling_ratio` 设置为 0。略微提升了 AP（约 0.2% 绝对值）。\n\n- 为了提升训练速度，默认设置在训练过程中不再使用梯度裁剪。大多数模型的性能不会受到影响。对于某些模型（例如 RepPoints），我们依旧使用梯度裁剪来稳定训练过程从而获得更好的性能。\n\n- 因为不再默认使用梯度裁剪，默认 warmup 比率从 1/3 更改为 0.001，以使模型训练预热更加平缓。不过我们重新进行基准测试时发现这种影响可以忽略不计。\n\n### 将模型从 v1.x 升级至 v2.0\n\n用户可以使用脚本 `tools/model_converters/upgrade_model_version.py` 来将 MMDetection 1.x 训练的模型转换为 MMDetection v2.0。转换后的模型可以在 MMDetection v2.0 中运行，但性能略有下降（小于 1% AP）。\n详细信息可以在 `configs/legacy` 中找到。\n\n## pycocotools 兼容性\n\n`mmpycocotools` 是 OpenMMLab 维护的 `pycocotools` 的复刻版，适用于 MMDetection 和 Detectron2。\n在 [PR #4939](https://github.com/open-mmlab/mmdetection/pull/4939) 之前，由于 `pycocotools` 和 `mmpycocotool` 具有相同的包名，如果用户已经安装了 `pyccocotools`（在相同环境下先安装了 Detectron2 ），那么 MMDetection 的安装过程会跳过安装 `mmpycocotool`。 导致 MMDetection 缺少 `mmpycocotools` 而报错。\n但如果在 Detectron2 之前安装 MMDetection，则可以在相同的环境下工作。\n[PR #4939](https://github.com/open-mmlab/mmdetection/pull/4939) 弃用 mmpycocotools，使用官方 pycocotools。\n在 [PR #4939](https://github.com/open-mmlab/mmdetection/pull/4939) 之后，用户能够在相同环境下安装 MMDetection 和 Detectron2，不再需要关注安装顺序。\n"
  },
  {
    "path": "docs/zh_cn/notes/faq.md",
    "content": "# 常见问题解答\n\n我们在这里列出了使用时的一些常见问题及其相应的解决方案。 如果您发现有一些问题被遗漏，请随时提 PR 丰富这个列表。 如果您无法在此获得帮助，请使用 [issue模板](https://github.com/open-mmlab/mmdetection/blob/main/.github/ISSUE_TEMPLATE/error-report.md/)创建问题，但是请在模板中填写所有必填信息，这有助于我们更快定位问题。\n\n## PyTorch 2.0 支持\n\nMMDetection 目前绝大部分算法已经支持了 PyTorch 2.0 及其 `torch.compile` 功能, 用户只需要安装 MMDetection 3.0.0rc7 及其以上版本即可。如果你在使用中发现有不支持的算法，欢迎给我们反馈。我们也非常欢迎社区贡献者来 benchmark 对比 `torch.compile` 功能所带来的速度提升。\n\n如果你想启动 `torch.compile` 功能，只需要在 `train.py` 或者 `test.py` 后面加上 `--cfg-options compile=True`。 以 RTMDet 为例，你可以使用以下命令启动 `torch.compile` 功能：\n\n```shell\n# 单卡\npython tools/train.py configs/rtmdet/rtmdet_s_8xb32-300e_coco.py  --cfg-options compile=True\n\n# 单机 8 卡\n./tools/dist_train.sh configs/rtmdet/rtmdet_s_8xb32-300e_coco.py 8 --cfg-options compile=True\n\n# 单机 8 卡 + AMP 混合精度训练\n./tools/dist_train.sh configs/rtmdet/rtmdet_s_8xb32-300e_coco.py 8 --cfg-options compile=True --amp\n```\n\n需要特别注意的是，PyTorch 2.0 对于动态 shape 支持不是非常完善，目标检测算法中大部分不仅输入 shape 是动态的，而且 loss 计算和后处理过程中也是动态的，这会导致在开启 `torch.compile` 功能后训练速度会变慢。基于此，如果你想启动 `torch.compile` 功能，则应该遵循如下原则：\n\n1. 输入到网络的图片是固定 shape 的，而非多尺度的\n2. 设置 `torch._dynamo.config.cache_size_limit` 参数。TorchDynamo 会将 Python 字节码转换并缓存，已编译的函数会被存入缓存中。当下一次检查发现需要重新编译时，该函数会被重新编译并缓存。但是如果重编译次数超过预设的最大值（64），则该函数将不再被缓存或重新编译。前面说过目标检测算法中的 loss 计算和后处理部分也是动态计算的，这些函数需要在每次迭代中重新编译。因此将 `torch._dynamo.config.cache_size_limit` 参数设置得更小一些可以有效减少编译时间\n\n在 MMDetection 中可以通过环境变量 `DYNAMO_CACHE_SIZE_LIMIT` 设置 `torch._dynamo.config.cache_size_limit` 参数，以 RTMDet 为例，命令如下所示：\n\n```shell\n# 单卡\nexport DYNAMO_CACHE_SIZE_LIMIT = 4\npython tools/train.py configs/rtmdet/rtmdet_s_8xb32-300e_coco.py  --cfg-options compile=True\n\n# 单机 8 卡\nexport DYNAMO_CACHE_SIZE_LIMIT = 4\n./tools/dist_train.sh configs/rtmdet/rtmdet_s_8xb32-300e_coco.py 8 --cfg-options compile=True\n```\n\n关于 PyTorch 2.0 的 dynamo 常见问题，可以参考 [这里](https://pytorch.org/docs/stable/dynamo/faq.html)\n\n## 安装\n\n- MMCV 与 MMDetection 的兼容问题: \"ConvWS is already registered in conv layer\"; \"AssertionError: MMCV==xxx is used but incompatible. Please install mmcv>=xxx, \\<=xxx.\"\n\n  MMDetection，MMEngine 和 MMCV 的版本兼容关系如下。请选择合适的版本避免安装错误 。\n\n  | MMDetection 版本 |        MMCV 版本        |      MMEngine 版本       |\n  | :--------------: | :---------------------: | :----------------------: |\n  |       main       |  mmcv>=2.0.0, \\<2.2.0   | mmengine>=0.7.1, \\<1.0.0 |\n  |      3.3.0       |  mmcv>=2.0.0, \\<2.2.0   | mmengine>=0.7.1, \\<1.0.0 |\n  |      3.2.0       |  mmcv>=2.0.0, \\<2.2.0   | mmengine>=0.7.1, \\<1.0.0 |\n  |      3.1.0       |  mmcv>=2.0.0, \\<2.1.0   | mmengine>=0.7.1, \\<1.0.0 |\n  |      3.0.0       |  mmcv>=2.0.0, \\<2.1.0   | mmengine>=0.7.1, \\<1.0.0 |\n  |     3.0.0rc6     | mmcv>=2.0.0rc4, \\<2.1.0 | mmengine>=0.6.0, \\<1.0.0 |\n  |     3.0.0rc5     | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.3.0, \\<1.0.0 |\n  |     3.0.0rc4     | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.3.0, \\<1.0.0 |\n  |     3.0.0rc3     | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.3.0, \\<1.0.0 |\n  |     3.0.0rc2     | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.1.0, \\<1.0.0 |\n  |     3.0.0rc1     | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.1.0, \\<1.0.0 |\n  |     3.0.0rc0     | mmcv>=2.0.0rc1, \\<2.1.0 | mmengine>=0.1.0, \\<1.0.0 |\n\n  **注意：**\n\n  1. 如果你希望安装 mmdet-v2.x, MMDetection 和 MMCV 版本兼容表可以在 [这里](https://mmdetection.readthedocs.io/en/stable/faq.html#installation) 找到，请选择合适的版本避免安装错误。\n  2. 在 MMCV-v2.x 中，`mmcv-full` 改名为 `mmcv`，如果你想安装不包含 CUDA 算子的版本，可以选择安装 MMCV 精简版 `mmcv-lite`。\n\n- \"No module named 'mmcv.ops'\"; \"No module named 'mmcv.\\_ext'\".\n\n  原因是安装了 `mmcv-lite` 而不是 `mmcv`。\n\n  1. `pip uninstall mmcv-lite` 卸载安装的 `mmcv-lite`\n\n  2. 安装 `mmcv` 根据 [安装说明](https://mmcv.readthedocs.io/zh_CN/2.x/get_started/installation.html)。\n\n- 在 Windows 环境下安装过程中遇到 \"Microsoft Visual C++ 14.0 or graeter is required\" error .\n\n  这个错误发生在 pycotools 的 'pycocotools.\\_mask' 扩展构建过程，其原因是缺少了对应 C++ 环境依赖。你需要到微软官方下载[对应工具](https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/)，选择“使用 C++ 的桌面开发”选项安装最小依赖，随后重新安装 pycocotools。\n\n- 使用 albumentations\n\n如果你希望使用 `albumentations`，我们建议使用 `pip install -r requirements/albu.txt`\n或者 `pip install -U albumentations --no-binary qudida,albumentations` 进行安装。\n如果简单地使用 `pip install albumentations>=0.3.2` 进行安装，\n则会同时安装 `opencv-python-headless`（即便已经安装了 `opencv-python` 也会再次安装）。\n我们建议在安装 `albumentations` 后检查环境，以确保没有同时安装 `opencv-python` 和 `opencv-python-headless`，\n因为同时安装可能会导致一些问题。更多细节请参考[官方文档](https://albumentations.ai/docs/getting_started/installation/#note-on-opencv-dependencies) 。\n\n- 在某些算法中出现 ModuleNotFoundError 错误\n\n一些算法或者数据需要额外的依赖，例如 Instaboost、 Panoptic Segmentation、 LVIS dataset 等。请注意错误信息并安装相应的包，例如：\n\n```shell\n# 安装 instaboost 依赖\npip install instaboostfast\n# 安装 panoptic segmentation 依赖\npip install git+https://github.com/cocodataset/panopticapi.git\n# 安装 LVIS dataset 依赖\npip install git+https://github.com/lvis-dataset/lvis-api.git\n```\n\n## 代码\n\n- 修改一些代码后是否需要重新安装 mmdet\n\n如果你遵循最佳实践，即使用 `pip install -v -e .` 安装的 mmdet，则对本地代码所作的任何修改都会生效，无需重新安装\n\n- 如何使用多个 MMDetection 版本进行开发\n\n你可以拥有多个文件夹，例如 mmdet-3.0，mmdet-3.1。\n\n要使环境中安装默认的 MMDetection 而不是当前正在在使用的，可以删除出现在相关脚本中的代码：\n\n```shell\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH\n```\n\n## PyTorch/CUDA 环境相关\n\n- \"RTX 30 series card fails when building MMCV or MMDet\"\n\n  1. 临时解决方案为使用命令 `MMCV_WITH_OPS=1 MMCV_CUDA_ARGS='-gencode=arch=compute_80,code=sm_80' pip install -e .` 进行编译。 常见报错信息为 `nvcc fatal : Unsupported gpu architecture 'compute_86'` 意思是你的编译器不支持 sm_86 架构(包括英伟达 30 系列的显卡)的优化，至 CUDA toolkit 11.0 依旧未支持. 这个命令是通过增加宏 `MMCV_CUDA_ARGS='-gencode=arch=compute_80,code=sm_80` 让 nvcc 编译器为英伟达 30 系列显卡进行 `sm_80` 的优化，虽然这有可能会无法发挥出显卡所有性能。\n\n  2. 有开发者已经在 [pytorch/pytorch#47585](https://github.com/pytorch/pytorch/pull/47585) 更新了 PyTorch 默认的编译 flag， 但是我们对此并没有进行测试。\n\n- \"invalid device function\" 或者 \"no kernel image is available for execution\".\n\n  1. 检查您正常安装了 CUDA runtime (一般在`/usr/local/`)，或者使用 `nvcc --version` 检查本地版本，有时安装 PyTorch 会顺带安装一个 CUDA runtime，并且实际优先使用 conda 环境中的版本，你可以使用 `conda list cudatoolkit` 查看其版本。\n\n  2. 编译 extension 的 CUDA Toolkit 版本与运行时的 CUDA Toolkit 版本是否相符，\n\n     - 如果您从源码自己编译的，使用 `python mmdet/utils/collect_env.py` 检查编译编译 extension 的 CUDA Toolkit 版本，然后使用 `conda list cudatoolkit` 检查当前 conda 环境是否有 CUDA Toolkit，若有检查版本是否匹配， 如不匹配，更换 conda 环境的 CUDA Toolkit，或者使用匹配的 CUDA Toolkit 中的 nvcc 编译即可，如环境中无 CUDA Toolkit，可以使用 `nvcc -V`。\n\n       等命令查看当前使用的 CUDA runtime。\n\n     - 如果您是通过 pip 下载的预编译好的版本，请确保与当前 CUDA runtime 一致。\n\n  3. 运行 `python mmdet/utils/collect_env.py` 检查是否为正确的 GPU 架构编译的 PyTorch， torchvision， 与 MMCV。 你或许需要设置 `TORCH_CUDA_ARCH_LIST` 来重新安装 MMCV，可以参考 [GPU 架构表](https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#gpu-feature-list),\n     例如， 运行 `TORCH_CUDA_ARCH_LIST=7.0 pip install mmcv` 为 Volta GPU 编译 MMCV。这种架构不匹配的问题一般会出现在使用一些旧型号的 GPU 时候出现， 例如， Tesla K80。\n\n- \"undefined symbol\" 或者 \"cannot open xxx.so\".\n\n  1. 如果这些 symbol 属于 CUDA/C++ (如 libcudart.so 或者 GLIBCXX)，使用 `python mmdet/utils/collect_env.py`检查 CUDA/GCC runtime 与编译 MMCV 的 CUDA 版本是否相同。\n  2. 如果这些 symbols 属于 PyTorch，(例如, symbols containing caffe, aten, and TH), 检查当前 Pytorch 版本是否与编译 MMCV 的版本一致。\n  3. 运行 `python mmdet/utils/collect_env.py` 检查 PyTorch， torchvision， MMCV 等的编译环境与运行环境一致。\n\n- setuptools.sandbox.UnpickleableException: DistutilsSetupError(\"each element of 'ext_modules' option must be an Extension instance or 2-tuple\")\n\n  1. 如果你在使用 miniconda 而不是 anaconda，检查是否正确的安装了 Cython 如 [#3379](https://github.com/open-mmlab/mmdetection/issues/3379).\n  2. 检查环境中的 `setuptools`, `Cython`, and `PyTorch` 相互之间版本是否匹配。\n\n- \"Segmentation fault\".\n\n  1. 检查 GCC 的版本，通常是因为 PyTorch 版本与 GCC 版本不匹配 （例如 GCC \\< 4.9 )，我们推荐用户使用 GCC 5.4，我们也不推荐使用 GCC 5.5， 因为有反馈 GCC 5.5 会导致 \"segmentation fault\" 并且切换到 GCC 5.4 就可以解决问题。\n\n  2. 检查是否正确安装了 CUDA 版本的 PyTorch 。\n\n     ```shell\n     python -c 'import torch; print(torch.cuda.is_available())'\n     ```\n\n     是否返回True。\n\n  3. 如果 `torch` 的安装是正确的，检查是否正确编译了 MMCV。\n\n     ```shell\n     python -c 'import mmcv; import mmcv.ops'\n     ```\n\n  4. 如果 MMCV 与 PyTorch 都被正确安装了，则使用 `ipdb`, `pdb` 设置断点，直接查找哪一部分的代码导致了 `segmentation fault`。\n\n## Training 相关\n\n- \"Loss goes Nan\"\n\n  1. 检查数据的标注是否正常， 长或宽为 0 的框可能会导致回归 loss 变为 nan，一些小尺寸（宽度或高度小于 1）的框在数据增强（例如，instaboost）后也会导致此问题。 因此，可以检查标注并过滤掉那些特别小甚至面积为 0 的框，并关闭一些可能会导致 0 面积框出现数据增强。\n  2. 降低学习率：由于某些原因，例如 batch size 大小的变化， 导致当前学习率可能太大。 您可以降低为可以稳定训练模型的值。\n  3. 延长 warm up 的时间：一些模型在训练初始时对学习率很敏感，您可以把 `warmup_iters` 从 500 更改为 1000 或 2000。\n  4. 添加 gradient clipping: 一些模型需要梯度裁剪来稳定训练过程。 默认的 `grad_clip` 是 `None`,  你可以在 config 设置 `optimizer_config=dict(_delete_=True, grad_clip=dict(max_norm=35, norm_type=2))`  如果你的 config 没有继承任何包含 `optimizer_config=dict(grad_clip=None)`,  你可以直接设置`optimizer_config=dict(grad_clip=dict(max_norm=35, norm_type=2))`.\n\n- \"GPU out of memory\"\n\n  1. 存在大量 ground truth boxes 或者大量 anchor 的场景，可能在 assigner 会 OOM。 您可以在 assigner 的配置中设置 `gpu_assign_thr=N`，这样当超过 N 个 GT boxes 时，assigner 会通过 CPU 计算 IOU。\n\n  2. 在 backbone 中设置 `with_cp=True`。 这使用 PyTorch 中的 `sublinear strategy` 来降低 backbone 占用的 GPU 显存。\n\n  3. 使用 `config/fp16` 中的示例尝试混合精度训练。`loss_scale` 可能需要针对不同模型进行调整。\n\n  4. 你也可以尝试使用 `AvoidCUDAOOM` 来避免该问题。首先它将尝试调用 `torch.cuda.empty_cache()`。如果失败，将会尝试把输入类型转换到 FP16。如果仍然失败，将会把输入从 GPUs 转换到 CPUs 进行计算。这里提供了两个使用的例子：\n\n     ```python\n     from mmdet.utils import AvoidCUDAOOM\n\n     output = AvoidCUDAOOM.retry_if_cuda_oom(some_function)(input1, input2)\n     ```\n\n     你也可也使用 `AvoidCUDAOOM` 作为装饰器让代码遇到 OOM 的时候继续运行：\n\n     ```python\n     from mmdet.utils import AvoidCUDAOOM\n\n     @AvoidCUDAOOM.retry_if_cuda_oom\n     def function(*args, **kwargs):\n         ...\n         return xxx\n     ```\n\n- \"RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one\"\n\n  1. 这个错误出现在存在参数没有在 forward 中使用，容易在 DDP 中运行不同分支时发生。\n  2. 你可以在 config 设置 `find_unused_parameters = True` 进行训练 (会降低训练速度)。\n  3. 你也可以通过在 config 中的 `optimizer_config` 里设置 `detect_anomalous_params=True` 查找哪些参数没有用到，但是需要 MMCV 的版本 >= 1.4.1。\n\n- 训练中保存最好模型\n\n  可以通过配置 `default_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=1, save_best='auto')`开启。在 `auto` 参数情况下会根据返回的验证结果中的第一个 key 作为选择最优模型的依据，你也可以直接设置评估结果中的 key 来手动设置，例如 `save_best='coco/bbox_mAP'`。\n\n- 在 Resume 训练中使用 `ExpMomentumEMAHook`\n\n  如果在训练中使用了 `ExpMomentumEMAHook`，那么 resume 时候不能仅仅通过命令行参数 `--resume-from` 或 `--cfg-options resume_from` 实现恢复模型参数功能例如 `python tools/train.py configs/yolox/yolox_s_8x8_300e_coco.py --resume-from ./work_dir/yolox_s_8x8_300e_coco/epoch_x.pth`。以 `yolox_s` 算法为例，由于 `ExpMomentumEMAHook` 需要重新加载权重，你可以通过如下做法实现：\n\n  ```python\n  # 直接打开 configs/yolox/yolox_s_8x8_300e_coco.py 修改所有 resume_from 字段\n  resume_from=./work_dir/yolox_s_8x8_300e_coco/epoch_x.pth\n  custom_hooks=[...\n      dict(\n          type='ExpMomentumEMAHook',\n          resume_from=./work_dir/yolox_s_8x8_300e_coco/epoch_x.pth,\n          momentum=0.0001,\n          priority=49)\n      ]\n  ```\n\n## Evaluation 相关\n\n- 使用 COCO Dataset 的测评接口时, 测评结果中 AP 或者 AR = -1\n  1. 根据COCO数据集的定义，一张图像中的中等物体与小物体面积的阈值分别为 9216（96\\*96）与 1024（32\\*32）。\n  2. 如果在某个区间没有检测框 AP 与 AR 认定为 -1.\n\n## Model 相关\n\n- **ResNet style 参数说明**\n\n  ResNet style 可选参数允许 `pytorch` 和 `caffe`，其差别在于 Bottleneck 模块。Bottleneck 是 `1x1-3x3-1x1` 堆叠结构，在 `caffe` 模式模式下 stride=2 参数放置在第一个 `1x1` 卷积处，而 `pyorch` 模式下 stride=2 放在第二个 `3x3` 卷积处。一个简单示例如下：\n\n  ```python\n  if self.style == 'pytorch':\n        self.conv1_stride = 1\n        self.conv2_stride = stride\n  else:\n        self.conv1_stride = stride\n        self.conv2_stride = 1\n  ```\n\n- **ResNeXt 参数说明**\n\n  ResNeXt 来自论文 [`Aggregated Residual Transformations for Deep Neural Networks`](https://arxiv.org/abs/1611.05431). 其引入分组卷积，并且通过变量基数来控制组的数量达到精度和复杂度的平衡，其有两个超参 `baseWidth` 和 `cardinality `来控制内部 Bottleneck 模块的基本宽度和分组数参数。以 MMDetection 中配置名为 `mask_rcnn_x101_64x4d_fpn_mstrain-poly_3x_coco.py` 为例，其中 `mask_rcnn` 代表算法采用 Mask R-CNN，`x101` 代表骨架网络采用 ResNeXt-101，`64x4d`代表 Bottleneck 一共分成 64 组，每组的基本宽度是 4。\n\n- **骨架网络 eval 模式说明**\n\n  因为检测模型通常比较大且输入图片分辨率很高，这会导致检测模型的 batch 很小，通常是 2，这会使得 BatchNorm 在训练过程计算的统计量方差非常大，不如主干网络预训练时得到的统计量稳定，因此在训练是一般都会使用 `norm_eval=True` 模式，直接使用预训练主干网络中的 BatchNorm 统计量，少数使用大 batch 的算法是 `norm_eval=False` 模式，例如 NASFPN。对于没有 ImageNet 预训练的骨架网络，如果 batch 比较小，可以考虑使用 `SyncBN`。\n"
  },
  {
    "path": "docs/zh_cn/notes/projects.md",
    "content": "# 基于 MMDetection 的项目\n\n有许多开源项目都是基于 MMDetection 搭建的，我们在这里列举一部分作为样例，展示如何基于 MMDetection 搭建您自己的项目。\n由于这个页面列举的项目并不完全，我们欢迎社区提交 Pull Request 来更新这个文档。\n\n## MMDetection 的拓展项目\n\n一些项目拓展了 MMDetection 的边界，如将 MMDetection 拓展支持 3D 检测或者将 MMDetection 用于部署。\n它们展示了 MMDetection 的许多可能性，所以我们在这里也列举一些。\n\n- [OTEDetection](https://github.com/opencv/mmdetection): OpenVINO training extensions for object detection.\n- [MMDetection3d](https://github.com/open-mmlab/mmdetection3d): OpenMMLab's next-generation platform for general 3D object detection.\n\n## 研究项目\n\n同样有许多研究论文是基于 MMDetection 进行的。许多论文都发表在了顶级的会议或期刊上，或者对社区产生了深远的影响。\n为了向社区提供一个可以参考的论文列表，帮助大家开发或者比较新的前沿算法，我们在这里也遵循会议的时间顺序列举了一些论文。\nMMDetection 中已经支持的算法不在此列。\n\n- Involution: Inverting the Inherence of Convolution for Visual Recognition, CVPR21. [\\[paper\\]](https://arxiv.org/abs/2103.06255)[\\[github\\]](https://github.com/d-li14/involution)\n- Multiple Instance Active Learning for Object Detection, CVPR 2021. [\\[paper\\]](https://openaccess.thecvf.com/content/CVPR2021/papers/Yuan_Multiple_Instance_Active_Learning_for_Object_Detection_CVPR_2021_paper.pdf)[\\[github\\]](https://github.com/yuantn/MI-AOD)\n- Adaptive Class Suppression Loss for Long-Tail Object Detection, CVPR 2021. [\\[paper\\]](https://arxiv.org/abs/2104.00885)[\\[github\\]](https://github.com/CASIA-IVA-Lab/ACSL)\n- Generalizable Pedestrian Detection: The Elephant In The Room, CVPR2021. [\\[paper\\]](https://arxiv.org/abs/2003.08799)[\\[github\\]](https://github.com/hasanirtiza/Pedestron)\n- Group Fisher Pruning for Practical Network Compression, ICML2021. [\\[paper\\]](https://github.com/jshilong/FisherPruning/blob/main/resources/paper.pdf)[\\[github\\]](https://github.com/jshilong/FisherPruning)\n- Overcoming Classifier Imbalance for Long-tail Object Detection with Balanced Group Softmax, CVPR2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/papers/Li_Overcoming_Classifier_Imbalance_for_Long-Tail_Object_Detection_With_Balanced_Group_CVPR_2020_paper.pdf)[\\[github\\]](https://github.com/FishYuLi/BalancedGroupSoftmax)\n- Coherent Reconstruction of Multiple Humans from a Single Image, CVPR2020. [\\[paper\\]](https://jiangwenpl.github.io/multiperson/)[\\[github\\]](https://github.com/JiangWenPL/multiperson)\n- Look-into-Object: Self-supervised Structure Modeling for Object Recognition, CVPR 2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/papers/Zhou_Look-Into-Object_Self-Supervised_Structure_Modeling_for_Object_Recognition_CVPR_2020_paper.pdf)[\\[github\\]](https://github.com/JDAI-CV/LIO)\n- Video Panoptic Segmentation, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2006.11339)[\\[github\\]](https://github.com/mcahny/vps)\n- D2Det: Towards High Quality Object Detection and Instance Segmentation, CVPR2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/html/Cao_D2Det_Towards_High_Quality_Object_Detection_and_Instance_Segmentation_CVPR_2020_paper.html)[\\[github\\]](https://github.com/JialeCao001/D2Det)\n- CentripetalNet: Pursuing High-quality Keypoint Pairs for Object Detection, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2003.09119)[\\[github\\]](https://github.com/KiveeDong/CentripetalNet)\n- Learning a Unified Sample Weighting Network for Object Detection, CVPR 2020. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2020/html/Cai_Learning_a_Unified_Sample_Weighting_Network_for_Object_Detection_CVPR_2020_paper.html)[\\[github\\]](https://github.com/caiqi/sample-weighting-network)\n- Scale-equalizing Pyramid Convolution for Object Detection, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2005.03101) [\\[github\\]](https://github.com/jshilong/SEPC)\n- Revisiting the Sibling Head in Object Detector, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2003.07540)[\\[github\\]](https://github.com/Sense-X/TSD)\n- PolarMask: Single Shot Instance Segmentation with Polar Representation, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/1909.13226)[\\[github\\]](https://github.com/xieenze/PolarMask)\n- Hit-Detector: Hierarchical Trinity Architecture Search for Object Detection, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2003.11818)[\\[github\\]](https://github.com/ggjy/HitDet.pytorch)\n- ZeroQ: A Novel Zero Shot Quantization Framework, CVPR2020. [\\[paper\\]](https://arxiv.org/abs/2001.00281)[\\[github\\]](https://github.com/amirgholami/ZeroQ)\n- CBNet: A Novel Composite Backbone Network Architecture for Object Detection, AAAI2020. [\\[paper\\]](https://aaai.org/Papers/AAAI/2020GB/AAAI-LiuY.1833.pdf)[\\[github\\]](https://github.com/VDIGPKU/CBNet)\n- RDSNet: A New Deep Architecture for Reciprocal Object Detection and Instance Segmentation, AAAI2020. [\\[paper\\]](https://arxiv.org/abs/1912.05070)[\\[github\\]](https://github.com/wangsr126/RDSNet)\n- Training-Time-Friendly Network for Real-Time Object Detection, AAAI2020. [\\[paper\\]](https://arxiv.org/abs/1909.00700)[\\[github\\]](https://github.com/ZJULearning/ttfnet)\n- Cascade RPN: Delving into High-Quality Region Proposal Network with Adaptive Convolution, NeurIPS 2019. [\\[paper\\]](https://arxiv.org/abs/1909.06720)[\\[github\\]](https://github.com/thangvubk/Cascade-RPN)\n- Reasoning R-CNN: Unifying Adaptive Global Reasoning into Large-scale Object Detection, CVPR2019. [\\[paper\\]](http://openaccess.thecvf.com/content_CVPR_2019/papers/Xu_Reasoning-RCNN_Unifying_Adaptive_Global_Reasoning_Into_Large-Scale_Object_Detection_CVPR_2019_paper.pdf)[\\[github\\]](https://github.com/chanyn/Reasoning-RCNN)\n- Learning RoI Transformer for Oriented Object Detection in Aerial Images, CVPR2019. [\\[paper\\]](https://arxiv.org/abs/1812.00155)[\\[github\\]](https://github.com/dingjiansw101/AerialDetection)\n- SOLO: Segmenting Objects by Locations. [\\[paper\\]](https://arxiv.org/abs/1912.04488)[\\[github\\]](https://github.com/WXinlong/SOLO)\n- SOLOv2: Dynamic, Faster and Stronger. [\\[paper\\]](https://arxiv.org/abs/2003.10152)[\\[github\\]](https://github.com/WXinlong/SOLO)\n- Dense Peppoints: Representing Visual Objects with Dense Point Sets. [\\[paper\\]](https://arxiv.org/abs/1912.11473)[\\[github\\]](https://github.com/justimyhxu/Dense-RepPoints)\n- IterDet: Iterative Scheme for Object Detection in Crowded Environments. [\\[paper\\]](https://arxiv.org/abs/2005.05708)[\\[github\\]](https://github.com/saic-vul/iterdet)\n- Cross-Iteration Batch Normalization. [\\[paper\\]](https://arxiv.org/abs/2002.05712)[\\[github\\]](https://github.com/Howal/Cross-iterationBatchNorm)\n- A Ranking-based, Balanced Loss Function Unifying Classification and Localisation in Object Detection, NeurIPS2020 [\\[paper\\]](https://arxiv.org/abs/2009.13592)[\\[github\\]](https://github.com/kemaloksuz/aLRPLoss)\n"
  },
  {
    "path": "docs/zh_cn/overview.md",
    "content": "# 概述\n\n本章向您介绍 MMDetection 的整体框架，并提供详细的教程链接。\n\n## 什么是 MMDetection\n\n![图片](https://user-images.githubusercontent.com/12907710/137271636-56ba1cd2-b110-4812-8221-b4c120320aa9.png)\n\nMMDetection 是一个目标检测工具箱，包含了丰富的目标检测、实例分割、全景分割算法以及相关的组件和模块，下面是它的整体框架：\n\nMMDetection 由 7 个主要部分组成，apis、structures、datasets、models、engine、evaluation 和 visualization。\n\n- **apis** 为模型推理提供高级 API。\n- **structures** 提供 bbox、mask 和 DetDataSample 等数据结构。\n- **datasets** 支持用于目标检测、实例分割和全景分割的各种数据集。\n  - **transforms** 包含各种数据增强变换。\n  - **samplers** 定义了不同的数据加载器采样策略。\n- **models** 是检测器最重要的部分，包含检测器的不同组件。\n  - **detectors** 定义所有检测模型类。\n  - **data_preprocessors** 用于预处理模型的输入数据。\n  - **backbones** 包含各种骨干网络。\n  - **necks** 包含各种模型颈部组件。\n  - **dense_heads** 包含执行密集预测的各种检测头。\n  - **roi_heads** 包含从 RoI 预测的各种检测头。\n  - **seg_heads** 包含各种分割头。\n  - **losses** 包含各种损失函数。\n  - **task_modules** 为检测任务提供模块，例如 assigners、samplers、box coders 和 prior generators。\n  - **layers** 提供了一些基本的神经网络层。\n- **engine** 是运行时组件的一部分。\n  - **runner** 为 [MMEngine 的执行器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/runner.html)提供扩展。\n  - **schedulers** 提供用于调整优化超参数的调度程序。\n  - **optimizers** 提供优化器和优化器封装。\n  - **hooks** 提供执行器的各种钩子。\n- **evaluation** 为评估模型性能提供不同的指标。\n- **visualization** 用于可视化检测结果。\n\n## 如何使用本指南\n\n以下是 MMDetection 的详细指南：\n\n1. 安装说明见[开始你的第一步](get_started.md)。\n\n2. MMDetection 的基本使用方法请参考以下教程。\n\n   - [训练和测试](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/index.html#train-test)\n\n   - [实用工具](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/index.html#useful-tools)\n\n3. 参考以下教程深入了解：\n\n   - [基础概念](https://mmdetection.readthedocs.io/zh_CN/latest/advanced_guides/index.html#basic-concepts)\n   - [组件定制](https://mmdetection.readthedocs.io/zh_CN/latest/advanced_guides/index.html#component-customization)\n\n4. 对于 MMDetection 2.x 版本的用户，我们提供了[迁移指南](./migration/migration.md)，帮助您完成新版本的适配。\n"
  },
  {
    "path": "docs/zh_cn/stat.py",
    "content": "#!/usr/bin/env python\nimport functools as func\nimport glob\nimport os.path as osp\nimport re\n\nimport numpy as np\n\nurl_prefix = 'https://github.com/open-mmlab/mmdetection/blob/main/'\n\nfiles = sorted(glob.glob('../configs/*/README.md'))\n\nstats = []\ntitles = []\nnum_ckpts = 0\n\nfor f in files:\n    url = osp.dirname(f.replace('../', url_prefix))\n\n    with open(f, 'r') as content_file:\n        content = content_file.read()\n\n    title = content.split('\\n')[0].replace('# ', '').strip()\n    ckpts = set(x.lower().strip()\n                for x in re.findall(r'\\[model\\]\\((https?.*)\\)', content))\n\n    if len(ckpts) == 0:\n        continue\n\n    _papertype = [x for x in re.findall(r'\\[([A-Z]+)\\]', content)]\n    assert len(_papertype) > 0\n    papertype = _papertype[0]\n\n    paper = set([(papertype, title)])\n\n    titles.append(title)\n    num_ckpts += len(ckpts)\n\n    statsmsg = f\"\"\"\n\\t* [{papertype}] [{title}]({url}) ({len(ckpts)} ckpts)\n\"\"\"\n    stats.append((paper, ckpts, statsmsg))\n\nallpapers = func.reduce(lambda a, b: a.union(b), [p for p, _, _ in stats])\nmsglist = '\\n'.join(x for _, _, x in stats)\n\npapertypes, papercounts = np.unique([t for t, _ in allpapers],\n                                    return_counts=True)\ncountstr = '\\n'.join(\n    [f'   - {t}: {c}' for t, c in zip(papertypes, papercounts)])\n\nmodelzoo = f\"\"\"\n# Model Zoo Statistics\n\n* Number of papers: {len(set(titles))}\n{countstr}\n\n* Number of checkpoints: {num_ckpts}\n\n{msglist}\n\"\"\"\n\nwith open('modelzoo_statistics.md', 'w') as f:\n    f.write(modelzoo)\n"
  },
  {
    "path": "docs/zh_cn/switch_language.md",
    "content": "## <a href='https://mmdetection.readthedocs.io/en/latest/'>English</a>\n\n## <a href='https://mmdetection.readthedocs.io/zh_CN/latest/'>简体中文</a>\n"
  },
  {
    "path": "docs/zh_cn/user_guides/config.md",
    "content": "# 学习配置文件\n\nMMDetection 和其他 OpenMMLab 仓库使用 [MMEngine 的配置文件系统](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/config.html)。 配置文件使用了模块化和继承设计，以便于进行各类实验。\n\n## 配置文件的内容\n\nMMDetection 采用模块化设计，所有功能的模块都可以通过配置文件进行配置。 以 Mask R-CNN 为例，我们将根据不同的功能模块介绍配置文件中的各个字段：\n\n### 模型配置\n\n在 mmdetection 的配置中，我们使用 `model` 字段来配置检测算法的组件。 除了 `backbone`、`neck` 等神经网络组件外，还需要 `data_preprocessor`、`train_cfg` 和 `test_cfg`。 `data_preprocessor` 负责对 dataloader 输出的每一批数据进行预处理。 模型配置中的 `train_cfg` 和 `test_cfg` 用于设置训练和测试组件的超参数。\n\n```python\nmodel = dict(\n    type='MaskRCNN',  # 检测器名\n    data_preprocessor=dict(  # 数据预处理器的配置，通常包括图像归一化和 padding\n        type='DetDataPreprocessor',  # 数据预处理器的类型，参考 https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.data_preprocessors.DetDataPreprocessor\n        mean=[123.675, 116.28, 103.53],  # 用于预训练骨干网络的图像归一化通道均值，按 R、G、B 排序\n        std=[58.395, 57.12, 57.375],  # 用于预训练骨干网络的图像归一化通道标准差，按 R、G、B 排序\n        bgr_to_rgb=True,  # 是否将图片通道从 BGR 转为 RGB\n        pad_mask=True,  # 是否填充实例分割掩码\n        pad_size_divisor=32),  # padding 后的图像的大小应该可以被 ``pad_size_divisor`` 整除\n    backbone=dict(  # 主干网络的配置文件\n        type='ResNet',  # 主干网络的类别，可用选项请参考 https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.backbones.ResNet\n        depth=50,  # 主干网络的深度，对于 ResNet 和 ResNext 通常设置为 50 或 101\n        num_stages=4,  # 主干网络状态(stages)的数目，这些状态产生的特征图作为后续的 head 的输入\n        out_indices=(0, 1, 2, 3),  # 每个状态产生的特征图输出的索引\n        frozen_stages=1,  # 第一个状态的权重被冻结\n        norm_cfg=dict(  # 归一化层(norm layer)的配置项\n            type='BN',  # 归一化层的类别，通常是 BN 或 GN\n            requires_grad=True),  # 是否训练归一化里的 gamma 和 beta\n        norm_eval=True,  # 是否冻结 BN 里的统计项\n        style='pytorch',  # 主干网络的风格，'pytorch' 意思是步长为2的层为 3x3 卷积， 'caffe' 意思是步长为2的层为 1x1 卷积\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),  # 加载通过 ImageNet 预训练的模型\n    neck=dict(\n        type='FPN',  # 检测器的 neck 是 FPN，我们同样支持 'NASFPN', 'PAFPN' 等，更多细节可以参考 https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.necks.FPN\n        in_channels=[256, 512, 1024, 2048],  # 输入通道数，这与主干网络的输出通道一致\n        out_channels=256,  # 金字塔特征图每一层的输出通道\n        num_outs=5),  # 输出的范围(scales)\n    rpn_head=dict(\n        type='RPNHead',  # rpn_head 的类型是 'RPNHead', 我们也支持 'GARPNHead' 等，更多细节可以参考 https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.dense_heads.RPNHead\n        in_channels=256,  # 每个输入特征图的输入通道，这与 neck 的输出通道一致\n        feat_channels=256,  # head 卷积层的特征通道\n        anchor_generator=dict(  # 锚点(Anchor)生成器的配置\n            type='AnchorGenerator',  # 大多数方法使用 AnchorGenerator 作为锚点生成器, SSD 检测器使用 `SSDAnchorGenerator`。更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/prior_generators/anchor_generator.py#L18\n            scales=[8],  # 锚点的基本比例，特征图某一位置的锚点面积为 scale * base_sizes\n            ratios=[0.5, 1.0, 2.0],  # 高度和宽度之间的比率\n            strides=[4, 8, 16, 32, 64]),  # 锚生成器的步幅。这与 FPN 特征步幅一致。 如果未设置 base_sizes，则当前步幅值将被视为 base_sizes\n        bbox_coder=dict(  # 在训练和测试期间对框进行编码和解码\n            type='DeltaXYWHBBoxCoder',  # 框编码器的类别，'DeltaXYWHBBoxCoder' 是最常用的，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/coders/delta_xywh_bbox_coder.py#L13\n            target_means=[0.0, 0.0, 0.0, 0.0],  # 用于编码和解码框的目标均值\n            target_stds=[1.0, 1.0, 1.0, 1.0]),  # 用于编码和解码框的标准差\n        loss_cls=dict(  # 分类分支的损失函数配置\n            type='CrossEntropyLoss',  # 分类分支的损失类型，我们也支持 FocalLoss 等，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/losses/cross_entropy_loss.py#L201\n            use_sigmoid=True,  # RPN 通常进行二分类，所以通常使用 sigmoid 函数\n            los_weight=1.0),  # 分类分支的损失权重\n        loss_bbox=dict(  # 回归分支的损失函数配置\n            type='L1Loss',  # 损失类型，我们还支持许多 IoU Losses 和 Smooth L1-loss 等，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/losses/smooth_l1_loss.py#L56\n            loss_weight=1.0)),  # 回归分支的损失权重\n    roi_head=dict(  # RoIHead 封装了两步(two-stage)/级联(cascade)检测器的第二步\n        type='StandardRoIHead',  # RoI head 的类型，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/roi_heads/standard_roi_head.py#L17\n        bbox_roi_extractor=dict(  # 用于 bbox 回归的 RoI 特征提取器\n            type='SingleRoIExtractor',  # RoI 特征提取器的类型，大多数方法使用 SingleRoIExtractor，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/roi_heads/roi_extractors/single_level_roi_extractor.py#L13\n            roi_layer=dict(  # RoI 层的配置\n                type='RoIAlign',  # RoI 层的类别, 也支持 DeformRoIPoolingPack 和 ModulatedDeformRoIPoolingPack，更多细节请参考 https://mmcv.readthedocs.io/en/latest/api.html#mmcv.ops.RoIAlign\n                output_size=7,  # 特征图的输出大小\n                sampling_ratio=0),  # 提取 RoI 特征时的采样率。0 表示自适应比率\n            out_channels=256,  # 提取特征的输出通道\n            featmap_strides=[4, 8, 16, 32]),  # 多尺度特征图的步幅，应该与主干的架构保持一致\n        bbox_head=dict(  # RoIHead 中 box head 的配置\n            type='Shared2FCBBoxHead',  # bbox head 的类别，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/roi_heads/bbox_heads/convfc_bbox_head.py#L220\n            in_channels=256,  # bbox head 的输入通道。 这与 roi_extractor 中的 out_channels 一致\n            fc_out_channels=1024,  # FC 层的输出特征通道\n            roi_feat_size=7,  # 候选区域(Region of Interest)特征的大小\n            num_classes=80,  # 分类的类别数量\n            bbox_coder=dict(  # 第二阶段使用的框编码器\n                type='DeltaXYWHBBoxCoder',  # 框编码器的类别，大多数情况使用 'DeltaXYWHBBoxCoder'\n                target_means=[0.0, 0.0, 0.0, 0.0],  # 用于编码和解码框的均值\n                target_stds=[0.1, 0.1, 0.2, 0.2]),  # 编码和解码的标准差。因为框更准确，所以值更小，常规设置时 [0.1, 0.1, 0.2, 0.2]。\n            reg_class_agnostic=False,  # 回归是否与类别无关\n            loss_cls=dict(  # 分类分支的损失函数配\n                type='CrossEntropyLoss',  # 分类分支的损失类型，我们也支持 FocalLoss 等\n                use_sigmoid=False,  # 是否使用 sigmoid\n                loss_weight=1.0),  # 分类分支的损失权重\n            loss_bbox=dict(  # 回归分支的损失函数配置\n                type='L1Loss',  # 损失类型，我们还支持许多 IoU Losses 和 Smooth L1-loss 等\n                loss_weight=1.0)),  # 回归分支的损失权重\n        mask_roi_extractor=dict(  # 用于 mask 生成的 RoI 特征提取器\n            type='SingleRoIExtractor',  # RoI 特征提取器的类型，大多数方法使用 SingleRoIExtractor\n            roi_layer=dict(  # 提取实例分割特征的 RoI 层配置\n                type='RoIAlign',  # RoI 层的类型，也支持 DeformRoIPoolingPack 和 ModulatedDeformRoIPoolingPack\n                output_size=14,  # 特征图的输出大小\n                sampling_ratio=0),  # 提取 RoI 特征时的采样率\n            out_channels=256,  # 提取特征的输出通道\n            featmap_strides=[4, 8, 16, 32]),  # 多尺度特征图的步幅\n        mask_head=dict(  # mask 预测 head 模型\n            type='FCNMaskHead',  # mask head 的类型，更多细节请参考 https://mmdetection.readthedocs.io/en/latest/api.html#mmdet.models.roi_heads.FCNMaskHead\n            num_convs=4,  # mask head 中的卷积层数\n            in_channels=256,  # 输入通道，应与 mask roi extractor 的输出通道一致\n            conv_out_channels=256,  # 卷积层的输出通道\n            num_classes=80,  # 要分割的类别数\n            loss_mask=dict(  # mask 分支的损失函数配置\n                type='CrossEntropyLoss',  # 用于分割的损失类型\n                use_mask=True,  # 是否只在正确的类中训练 mask\n                loss_weight=1.0))),  # mask 分支的损失权重\n    train_cfg = dict(  # rpn 和 rcnn 训练超参数的配置\n        rpn=dict(  # rpn 的训练配置\n            assigner=dict(  # 分配器(assigner)的配置\n                type='MaxIoUAssigner',  # 分配器的类型，MaxIoUAssigner 用于许多常见的检测器，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/assigners/max_iou_assigner.py#L14\n                pos_iou_thr=0.7,  # IoU >= 0.7(阈值) 被视为正样本\n                neg_iou_thr=0.3,  # IoU < 0.3(阈值) 被视为负样本\n                min_pos_iou=0.3,  # 将框作为正样本的最小 IoU 阈值\n                match_low_quality=True,  # 是否匹配低质量的框(更多细节见 API 文档)\n                ignore_iof_thr=-1),  # 忽略 bbox 的 IoF 阈值\n            sampler=dict(  # 正/负采样器(sampler)的配置\n                type='RandomSampler',  # 采样器类型，还支持 PseudoSampler 和其他采样器，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/samplers/random_sampler.py#L14\n                num=256,  # 样本数量。\n                pos_fraction=0.5,  # 正样本占总样本的比例\n                neg_pos_ub=-1,  # 基于正样本数量的负样本上限\n                add_gt_as_proposals=False),  # 采样后是否添加 GT 作为 proposal\n            allowed_border=-1,  # 填充有效锚点后允许的边框\n            pos_weight=-1,  # 训练期间正样本的权重\n            debug=False),  # 是否设置调试(debug)模式\n        rpn_proposal=dict(  # 在训练期间生成 proposals 的配置\n            nms_across_levels=False,  # 是否对跨层的 box 做 NMS。仅适用于 `GARPNHead` ，naive rpn 不支持 nms cross levels\n            nms_pre=2000,  # NMS 前的 box 数\n            nms_post=1000,  # NMS 要保留的 box 的数量，只在 GARPNHHead 中起作用\n            max_per_img=1000,  # NMS 后要保留的 box 数量\n            nms=dict( # NMS 的配置\n                type='nms',  # NMS 的类别\n                iou_threshold=0.7 # NMS 的阈值\n                ),\n            min_bbox_size=0),  # 允许的最小 box 尺寸\n        rcnn=dict(  # roi head 的配置。\n            assigner=dict(  # 第二阶段分配器的配置，这与 rpn 中的不同\n                type='MaxIoUAssigner',  # 分配器的类型，MaxIoUAssigner 目前用于所有 roi_heads。更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/assigners/max_iou_assigner.py#L14\n                pos_iou_thr=0.5,  # IoU >= 0.5(阈值)被认为是正样本\n                neg_iou_thr=0.5,  # IoU < 0.5(阈值)被认为是负样本\n                min_pos_iou=0.5,  # 将 box 作为正样本的最小 IoU 阈值\n                match_low_quality=False,  # 是否匹配低质量下的 box(有关更多详细信息，请参阅 API 文档)\n                ignore_iof_thr=-1),  # 忽略 bbox 的 IoF 阈值\n            sampler=dict(\n                type='RandomSampler',  # 采样器的类型，还支持 PseudoSampler 和其他采样器，更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/main/mmdet/models/task_modules/samplers/random_sampler.py#L14\n                num=512,  # 样本数量\n                pos_fraction=0.25,  # 正样本占总样本的比例\n                neg_pos_ub=-1,  # 基于正样本数量的负样本上限\n                add_gt_as_proposals=True\n            ),  # 采样后是否添加 GT 作为 proposal\n            mask_size=28,  # mask 的大小\n            pos_weight=-1,  # 训练期间正样本的权重\n            debug=False)),  # 是否设置调试模式\n    test_cfg = dict(  # 用于测试 rpn 和 rcnn 超参数的配置\n        rpn=dict(  # 测试阶段生成 proposals 的配置\n            nms_across_levels=False,  # 是否对跨层的 box 做 NMS。仅适用于 `GARPNHead`，naive rpn 不支持做 NMS cross levels\n            nms_pre=1000,  # NMS 前的 box 数\n            nms_post=1000,  # NMS 要保留的 box 的数量，只在 `GARPNHHead` 中起作用\n            max_per_img=1000,  # NMS 后要保留的 box 数量\n            nms=dict( # NMS 的配置\n                type='nms',  # NMS 的类型\n                iou_threshold=0.7 # NMS 阈值\n                ),\n            min_bbox_size=0),  # box 允许的最小尺寸\n        rcnn=dict(  # roi heads 的配置\n            score_thr=0.05,  # bbox 的分数阈值\n            nms=dict(  # 第二步的 NMS 配置\n                type='nms',  # NMS 的类型\n                iou_thr=0.5),  # NMS 的阈值\n            max_per_img=100,  # 每张图像的最大检测次数\n            mask_thr_binary=0.5)))  # mask 预处的阈值\n```\n\n### 数据集和评测器配置\n\n在使用[执行器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/runner.html) 进行训练、测试、验证时，我们需要配置 [Dataloader](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/dataset.html)。构建数据 dataloader 需要设置数据集（dataset）和数据处理流程（data pipeline）。 由于这部分的配置较为复杂，我们使用中间变量来简化 dataloader 配置的编写。\n\n```python\ndataset_type = 'CocoDataset'  # 数据集类型，这将被用来定义数据集。\ndata_root = 'data/coco/'  # 数据的根路径。\n\ntrain_pipeline = [  # 训练数据处理流程\n    dict(type='LoadImageFromFile'),  # 第 1 个流程，从文件路径里加载图像。\n    dict(\n        type='LoadAnnotations',  # 第 2 个流程，对于当前图像，加载它的注释信息。\n        with_bbox=True,  # 是否使用标注框(bounding box)， 目标检测需要设置为 True。\n        with_mask=True,  # 是否使用 instance mask，实例分割需要设置为 True。\n        poly2mask=False),  # 是否将 polygon mask 转化为 instance mask, 设置为 False 以加速和节省内存。\n    dict(\n        type='Resize',  # 变化图像和其标注大小的流程。\n        scale=(1333, 800),  # 图像的最大尺寸\n        keep_ratio=True  # 是否保持图像的长宽比。\n        ),\n    dict(\n        type='RandomFlip',  # 翻转图像和其标注的数据增广流程。\n        prob=0.5),  # 翻转图像的概率。\n    dict(type='PackDetInputs')  # 将数据转换为检测器输入格式的流程\n]\ntest_pipeline = [  # 测试数据处理流程\n    dict(type='LoadImageFromFile'),  # 第 1 个流程，从文件路径里加载图像。\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),  # 变化图像大小的流程。\n    dict(\n        type='PackDetInputs',  # 将数据转换为检测器输入格式的流程\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(  # 训练 dataloader 配置\n    batch_size=2,  # 单个 GPU 的 batch size\n    num_workers=2,  # 单个 GPU 分配的数据加载线程数\n    persistent_workers=True,  # 如果设置为 True，dataloader 在迭代完一轮之后不会关闭数据读取的子进程，可以加速训练\n    sampler=dict(  # 训练数据的采样器\n        type='DefaultSampler',  # 默认的采样器，同时支持分布式和非分布式训练。请参考 https://mmengine.readthedocs.io/zh_CN/latest/api/generated/mmengine.dataset.DefaultSampler.html#mmengine.dataset.DefaultSampler\n        shuffle=True),  # 随机打乱每个轮次训练数据的顺序\n    batch_sampler=dict(type='AspectRatioBatchSampler'),  # 批数据采样器，用于确保每一批次内的数据拥有相似的长宽比，可用于节省显存\n    dataset=dict(  # 训练数据集的配置\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',  # 标注文件路径\n        data_prefix=dict(img='train2017/'),  # 图片路径前缀\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),  # 图片和标注的过滤配置\n        pipeline=train_pipeline))  # 这是由之前创建的 train_pipeline 定义的数据处理流程。\nval_dataloader = dict(  # 验证 dataloader 配置\n    batch_size=1,  # 单个 GPU 的 Batch size。如果 batch-szie > 1，组成 batch 时的额外填充会影响模型推理精度\n    num_workers=2,  # 单个 GPU 分配的数据加载线程数\n    persistent_workers=True,  # 如果设置为 True，dataloader 在迭代完一轮之后不会关闭数据读取的子进程，可以加速训练\n    drop_last=False,  # 是否丢弃最后未能组成一个批次的数据\n    sampler=dict(\n        type='DefaultSampler',\n        shuffle=False),  # 验证和测试时不打乱数据顺序\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,  # 开启测试模式，避免数据集过滤图片和标注\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader  # 测试 dataloader 配置\n```\n\n[评测器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/evaluation.html) 用于计算训练模型在验证和测试数据集上的指标。评测器的配置由一个或一组评价指标（Metric）配置组成：\n\n```python\nval_evaluator = dict(  # 验证过程使用的评测器\n    type='CocoMetric',  # 用于评估检测和实例分割的 AR、AP 和 mAP 的 coco 评价指标\n    ann_file=data_root + 'annotations/instances_val2017.json',  # 标注文件路径\n    metric=['bbox', 'segm'],  # 需要计算的评价指标，`bbox` 用于检测，`segm` 用于实例分割\n    format_only=False)\ntest_evaluator = val_evaluator  # 测试过程使用的评测器\n```\n\n由于测试数据集没有标注文件，因此 MMDetection 中的 test_dataloader 和 test_evaluator 配置通常等于val。 如果要保存在测试数据集上的检测结果，则可以像这样编写配置：\n\n```python\n# 在测试集上推理，\n# 并将检测结果转换格式以用于提交结果\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n        data_prefix=dict(img='test2017/'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n    metric=['bbox', 'segm'],\n    format_only=True,  # 只将模型输出转换为 coco 的 JSON 格式并保存\n    outfile_prefix='./work_dirs/coco_detection/test')  # 要保存的 JSON 文件的前缀\n```\n\n### 训练和测试的配置\n\nMMEngine 的 Runner 使用 Loop 来控制训练，验证和测试过程。\n用户可以使用这些字段设置最大训练轮次和验证间隔。\n\n```python\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop',  # 训练循环的类型，请参考 https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py\n    max_epochs=12,  # 最大训练轮次\n    val_interval=1)  # 验证间隔。每个 epoch 验证一次\nval_cfg = dict(type='ValLoop')  # 验证循环的类型\ntest_cfg = dict(type='TestLoop')  # 测试循环的类型\n```\n\n### 优化相关配置\n\n`optim_wrapper` 是配置优化相关设置的字段。优化器封装（OptimWrapper）不仅提供了优化器的功能，还支持梯度裁剪、混合精度训练等功能。更多内容请看[优化器封装教程](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/optim_wrapper.html) 。\n\n```python\noptim_wrapper = dict(  # 优化器封装的配置\n    type='OptimWrapper',  # 优化器封装的类型。可以切换至 AmpOptimWrapper 来启用混合精度训练\n    optimizer=dict(  # 优化器配置。支持 PyTorch 的各种优化器。请参考 https://pytorch.org/docs/stable/optim.html#algorithms\n        type='SGD',  # 随机梯度下降优化器\n        lr=0.02,  # 基础学习率\n        momentum=0.9,  # 带动量的随机梯度下降\n        weight_decay=0.0001),  # 权重衰减\n    clip_grad=None,  # 梯度裁剪的配置，设置为 None 关闭梯度裁剪。使用方法请见 https://mmengine.readthedocs.io/en/latest/tutorials/optimizer.html\n    )\n```\n\n`param_scheduler` 字段用于配置参数调度器（Parameter Scheduler）来调整优化器的超参数（例如学习率和动量）。 用户可以组合多个调度器来创建所需的参数调整策略。 在 [参数调度器教程](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/param_scheduler.html) 和 [参数调度器 API 文档](https://mmengine.readthedocs.io/zh_CN/latest/api/generated/mmengine.optim._ParamScheduler.html#mmengine.optim._ParamScheduler) 中查找更多信息。\n\n```python\nparam_scheduler = [\n    dict(\n        type='LinearLR',  # 使用线性学习率预热\n        start_factor=0.001, # 学习率预热的系数\n        by_epoch=False,  # 按 iteration 更新预热学习率\n        begin=0,  # 从第一个 iteration 开始\n        end=500),  # 到第 500 个 iteration 结束\n    dict(\n        type='MultiStepLR',  # 在训练过程中使用 multi step 学习率策略\n        by_epoch=True,  # 按 epoch 更新学习率\n        begin=0,   # 从第一个 epoch 开始\n        end=12,  # 到第 12 个 epoch 结束\n        milestones=[8, 11],  # 在哪几个 epoch 进行学习率衰减\n        gamma=0.1)  # 学习率衰减系数\n]\n```\n\n### 钩子配置\n\n用户可以在训练、验证和测试循环上添加钩子，以便在运行期间插入一些操作。配置中有两种不同的钩子字段，一种是 `default_hooks`，另一种是 `custom_hooks`。\n\n`default_hooks` 是一个字典，用于配置运行时必须使用的钩子。这些钩子具有默认优先级，如果未设置，runner 将使用默认值。如果要禁用默认钩子，用户可以将其配置设置为 `None`。更多内容请看 [钩子教程](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/hook.html) 。\n\n```python\ndefault_hooks = dict(\n    timer=dict(type='IterTimerHook'),\n    logger=dict(type='LoggerHook', interval=50),\n    param_scheduler=dict(type='ParamSchedulerHook'),\n    checkpoint=dict(type='CheckpointHook', interval=1),\n    sampler_seed=dict(type='DistSamplerSeedHook'),\n    visualization=dict(type='DetVisualizationHook'))\n```\n\n`custom_hooks` 是一个列表。用户可以在这个字段中加入自定义的钩子。\n\n```python\ncustom_hooks = []\n```\n\n### 运行相关配置\n\n```python\ndefault_scope = 'mmdet'  # 默认的注册器域名，默认从此注册器域中寻找模块。请参考 https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/registry.html\n\nenv_cfg = dict(\n    cudnn_benchmark=False,  # 是否启用 cudnn benchmark\n    mp_cfg=dict(  # 多进程设置\n        mp_start_method='fork',  # 使用 fork 来启动多进程。'fork' 通常比 'spawn' 更快，但可能存在隐患。请参考 https://github.com/pytorch/pytorch/issues/1355\n        opencv_num_threads=0),  # 关闭 opencv 的多线程以避免系统超负荷\n    dist_cfg=dict(backend='nccl'),  # 分布式相关设置\n)\n\nvis_backends = [dict(type='LocalVisBackend')]  # 可视化后端，请参考 https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/visualization.html\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\nlog_processor = dict(\n    type='LogProcessor',  # 日志处理器用于处理运行时日志\n    window_size=50,  # 日志数值的平滑窗口\n    by_epoch=True)  # 是否使用 epoch 格式的日志。需要与训练循环的类型保存一致。\n\nlog_level = 'INFO'  # 日志等级\nload_from = None  # 从给定路径加载模型检查点作为预训练模型。这不会恢复训练。\nresume = False  # 是否从 `load_from` 中定义的检查点恢复。 如果 `load_from` 为 None，它将恢复 `work_dir` 中的最新检查点。\n```\n\n## Iter-based 配置\n\nMMEngine 的 Runner 除了基于轮次的训练循环（epoch）外，还提供了基于迭代（iteration）的训练循环。\n要使用基于迭代的训练，用户应该修改 `train_cfg`、`param_scheduler`、`train_dataloader`、`default_hooks` 和 `log_processor`。\n以下是将基于 epoch 的 RetinaNet 配置更改为基于 iteration 的示例：configs/retinanet/retinanet_r50_fpn_90k_coco.py\n\n```python\n# iter-based 训练配置\ntrain_cfg = dict(\n    _delete_=True,  # 忽略继承的配置文件中的值（可选）\n    type='IterBasedTrainLoop',  # iter-based 训练循环\n    max_iters=90000,  # 最大迭代次数\n    val_interval=10000)  # 每隔多少次进行一次验证\n\n\n# 将参数调度器修改为 iter-based\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=90000,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\n# 切换至 InfiniteSampler 来避免 dataloader 重启\ntrain_dataloader = dict(sampler=dict(type='InfiniteSampler'))\n\n# 将模型检查点保存间隔设置为按 iter 保存\ndefault_hooks = dict(checkpoint=dict(by_epoch=False, interval=10000))\n\n# 将日志格式修改为 iter-based\nlog_processor = dict(by_epoch=False)\n```\n\n## 配置文件继承\n\n在 `config/_base_` 文件夹下有 4 个基本组件类型，分别是：数据集(dataset)，模型(model)，训练策略(schedule)和运行时的默认设置(default runtime)。许多方法，例如 Faster R-CNN、Mask R-CNN、Cascade R-CNN、RPN、SSD 能够很容易地构建出来。由 `_base_` 下的组件组成的配置，被我们称为 _原始配置(primitive)_。\n\n对于同一文件夹下的所有配置，推荐**只有一个**对应的**原始配置**文件。所有其他的配置文件都应该继承自这个**原始配置**文件。这样就能保证配置文件的最大继承深度为 3。\n\n为了便于理解，我们建议贡献者继承现有方法。例如，如果在 Faster R-CNN 的基础上做了一些修改，用户首先可以通过指定 `_base_ = ../faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py` 来继承基础的 Faster R-CNN 结构，然后修改配置文件中的必要参数以完成继承。\n\n如果你在构建一个与任何现有方法不共享结构的全新方法，那么可以在 `configs` 文件夹下创建一个新的例如 `xxx_rcnn` 文件夹。\n\n更多细节请参考 [MMEngine 配置文件教程](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/config.html) 。\n\n通过设置 `_base_` 字段，我们可以设置当前配置文件继承自哪些文件。\n\n当 `_base_` 为文件路径字符串时，表示继承一个配置文件的内容。\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n```\n\n当 `_base_` 是多个文件路径的列表时，表示继承多个文件。\n\n```python\n_base_ = [\n    '../_base_/models/mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_instance.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\n```\n\n如果需要检查配置文件，可以通过运行 `python tools/misc/print_config.py /PATH/TO/CONFIG` 来查看完整的配置。\n\n### 忽略基础配置文件里的部分内容\n\n有时，您也许会设置 `_delete_=True` 去忽略基础配置文件里的一些域内容。 您也许可以参照 [MMEngine 配置文件教程](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/config.html) 来获得一些简单的指导。\n\n在 MMDetection 里，例如为了改变  Mask R-CNN 的主干网络的某些内容：\n\n```python\nmodel = dict(\n    type='MaskRCNN',\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(...),\n    rpn_head=dict(...),\n    roi_head=dict(...))\n```\n\n基础配置的 `Mask R-CNN` 使用 `ResNet-50`，在需要将主干网络改成 `HRNet` 的时候，因为 `HRNet` 和 `ResNet` 中有不同的字段，需要使用 `_delete_=True` 将新的键去替换 `backbone` 域内所有老的键。\n\n```python\n_base_ = '../mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py'\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='HRNet',\n        extra=dict(\n            stage1=dict(\n                num_modules=1,\n                num_branches=1,\n                block='BOTTLENECK',\n                num_blocks=(4, ),\n                num_channels=(64, )),\n            stage2=dict(\n                num_modules=1,\n                num_branches=2,\n                block='BASIC',\n                num_blocks=(4, 4),\n                num_channels=(32, 64)),\n            stage3=dict(\n                num_modules=4,\n                num_branches=3,\n                block='BASIC',\n                num_blocks=(4, 4, 4),\n                num_channels=(32, 64, 128)),\n            stage4=dict(\n                num_modules=3,\n                num_branches=4,\n                block='BASIC',\n                num_blocks=(4, 4, 4, 4),\n                num_channels=(32, 64, 128, 256))),\n        init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w32')),\n    neck=dict(...))\n```\n\n### 使用配置文件里的中间变量\n\n配置文件里会使用一些中间变量，例如数据集里的 `train_pipeline`/`test_pipeline`。我们在定义新的 `train_pipeline`/`test_pipeline` 之后，需要将它们传递到 `data` 里。例如，我们想在训练或测试时，改变 Mask R-CNN 的多尺度策略 (multi scale strategy)，`train_pipeline`/`test_pipeline` 是我们想要修改的中间变量。\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = dict(dataset=dict(pipeline=test_pipeline))\n```\n\n我们首先定义新的 `train_pipeline`/`test_pipeline` 然后传递到 `data` 里。\n\n同样的，如果我们想从 `SyncBN` 切换到 `BN` 或者 `MMSyncBN`，我们需要修改配置文件里的每一个  `norm_cfg`。\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\nnorm_cfg = dict(type='BN', requires_grad=True)\nmodel = dict(\n    backbone=dict(norm_cfg=norm_cfg),\n    neck=dict(norm_cfg=norm_cfg),\n    ...)\n```\n\n### 复用 \\_base\\_ 文件中的变量\n\n如果用户希望在当前配置中复用 base 文件中的变量，则可以通过使用 `{{_base_.xxx}}` 的方式来获取对应变量的拷贝。例如：\n\n```python\n_base_ = './mask-rcnn_r50_fpn_1x_coco.py'\n\na = {{_base_.model}}  # 变量 a 等于 _base_ 中定义的 model\n```\n\n## 通过脚本参数修改配置\n\n当运行 `tools/train.py` 和 `tools/test.py` 时，可以通过 `--cfg-options` 来修改配置文件。\n\n- 更新字典链中的配置\n\n  可以按照原始配置文件中的 dict 键顺序地指定配置预选项。例如，使用 `--cfg-options model.backbone.norm_eval=False` 将模型主干网络中的所有 BN 模块都改为 `train` 模式。\n\n- 更新配置列表中的键\n\n  在配置文件里，一些字典型的配置被包含在列表中。例如，数据训练流程 `data.train.pipeline` 通常是一个列表，比如 `[dict(type='LoadImageFromFile'), ...]`。如果需要将 `'LoadImageFromFile'` 改成 `'LoadImageFromWebcam'`，需要写成下述形式： `--cfg-options data.train.pipeline.0.type=LoadImageFromNDArray`.\n\n- 更新列表或元组的值\n\n  如果要更新的值是列表或元组。例如，配置文件通常设置 `model.data_preprocessor.mean=[123.675, 116.28, 103.53]`. 如果需要改变这个键，可以通过 `--cfg-options model.data_preprocessor.mean=\"[127,127,127]\"` 来重新设置。需要注意，引号 \" 是支持列表或元组数据类型所必需的，并且在指定值的引号内**不允许**有空格。\n\n## 配置文件名称风格\n\n我们遵循以下样式来命名配置文件。建议贡献者遵循相同的风格。\n\n```\n{algorithm name}_{model component names [component1]_[component2]_[...]}_{training settings}_{training dataset information}_{testing dataset information}.py\n```\n\n文件名分为五个部分。 每个部分用`_`连接，每个部分内的单词应该用`-`连接。\n\n- `{algorithm name}`: 算法的名称。 它可以是检测器名称，例如 `faster-rcnn`、`mask-rcnn` 等。也可以是半监督或知识蒸馏算法，例如 `soft-teacher`、`lad` 等等\n- `{component names}`: 算法中使用的组件名称，如 backbone、neck 等。例如 `r50-caffe_fpn_gn-head` 表示在算法中使用 caffe 版本的 ResNet50、FPN 和 使用了 Group Norm 的检测头。\n- `{training settings}`: 训练设置的信息，例如 batch 大小、数据增强、损失、参数调度方式和训练最大轮次/迭代。 例如：`4xb4-mixup-giou-coslr-100e` 表示使用 8 个 gpu 每个 gpu 4 张图、mixup 数据增强、GIoU loss、余弦退火学习率，并训练 100 个 epoch。\n  缩写介绍:\n  - `{gpu x batch_per_gpu}`: GPU 数和每个 GPU 的样本数。`bN` 表示每个 GPU 上的 batch 大小为 N。例如 `4x4b` 是 4 个 GPU 每个 GPU 4 张图的缩写。如果没有注明，默认为 8 卡每卡 2 张图。\n  - `{schedule}`: 训练方案，选项是 `1x`、 `2x`、 `20e` 等。`1x`  和 `2x` 分别代表 12 epoch 和 24 epoch，`20e` 在级联模型中使用，表示 20 epoch。对于 `1x`/`2x`，初始学习率在第 8/16 和第 11/22 epoch 衰减 10 倍；对于 `20e` ，初始学习率在第 16 和第 19 epoch 衰减 10 倍。\n- `{training dataset information}`: 训练数据集，例如 `coco`, `coco-panoptic`, `cityscapes`, `voc-0712`, `wider-face`。\n- `{testing dataset information}` (可选): 测试数据集，用于训练和测试在不同数据集上的模型配置。 如果没有注明，则表示训练和测试的数据集类型相同。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/dataset_prepare.md",
    "content": "## 数据集准备\n\n### 基础检测数据集准备\n\nMMDetection 支持多个公共数据集，包括 [COCO](https://cocodataset.org/)， [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC)， [Cityscapes](https://www.cityscapes-dataset.com/) 和 [其他更多数据集](https://github.com/open-mmlab/mmdetection/tree/main/configs/_base_/datasets)。\n\n一些公共数据集，比如 Pascal VOC 及其镜像数据集，或者 COCO 等数据集都可以从官方网站或者镜像网站获取。注意：在检测任务中，Pascal VOC 2012 是 Pascal VOC 2007 的无交集扩展，我们通常将两者一起使用。 我们建议将数据集下载，然后解压到项目外部的某个文件夹内，然后通过符号链接的方式，将数据集根目录链接到 `$MMDETECTION/data` 文件夹下， 如果你的文件夹结构和下方不同的话，你需要在配置文件中改变对应的路径。\n\n我们提供了下载 COCO 等数据集的脚本，你可以运行 `python tools/misc/download_dataset.py --dataset-name coco2017` 下载 COCO 数据集。 对于中国境内的用户，我们也推荐通过开源数据平台 [OpenDataLab](https://opendatalab.com/?source=OpenMMLab%20GitHub) 来下载数据，以获得更好的下载体验。\n\n更多用法请参考[数据集下载](./useful_tools.md#dataset-download)\n\n```text\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n│   ├── VOCdevkit\n│   │   ├── VOC2007\n│   │   ├── VOC2012\n```\n\n有些模型需要额外的 [COCO-stuff](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip) 数据集，比如 HTC，DetectoRS 和 SCNet，你可以下载并解压它们到 `coco` 文件夹下。文件夹会是如下结构：\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── stuffthingmaps\n```\n\nPanopticFPN 等全景分割模型需要额外的 [COCO Panoptic](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip) 数据集，你可以下载并解压它们到 `coco/annotations` 文件夹下。文件夹会是如下结构：\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── panoptic_train2017.json\n│   │   │   ├── panoptic_train2017\n│   │   │   ├── panoptic_val2017.json\n│   │   │   ├── panoptic_val2017\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n```\n\nCityscape 数据集的标注格式需要转换，以与 COCO 数据集标注格式保持一致，使用 `tools/dataset_converters/cityscapes.py` 来完成转换：\n\n```shell\npip install cityscapesscripts\n\npython tools/dataset_converters/cityscapes.py \\\n    ./data/cityscapes \\\n    --nproc 8 \\\n    --out-dir ./data/cityscapes/annotations\n```\n\n### COCO Caption 数据集准备\n\nCOCO Caption 采用的是 COCO2014 数据集作为图片，并且使用了 karpathy 的标注，\n\n首先你需要下载 COCO2014 数据集\n\n```shell\npython tools/misc/download_dataset.py --dataset-name coco2014 --unzip\n```\n\n数据集会下载到当前路径的 `data/coco` 下。然后下载 karpathy 的标注\n\n```shell\ncd data/coco/annotations\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_train.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val_gt.json\nwget https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test_gt.json\n```\n\n最终直接可用于训练和测试的数据集文件夹结构如下：\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── coco_karpathy_train.json\n│   │   │   ├── coco_karpathy_test.json\n│   │   │   ├── coco_karpathy_val.json\n│   │   │   ├── coco_karpathy_val_gt.json\n│   │   │   ├── coco_karpathy_test_gt.json\n│   │   ├── train2014\n│   │   ├── val2014\n│   │   ├── test2014\n```\n\n### COCO semantic 数据集准备\n\nCOCO 语义分割有两种类型标注，主要差别在于类别名定义不一样，因此处理方式也有两种，第一种是直接使用 stuffthingmaps 数据集，第二种是使用 panoptic 数据集。\n\n**(1) 使用 stuffthingmaps 数据集**\n\n该数据集的下载地址为 [stuffthingmaps_trainval2017](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip)，请下载后解压到 `data/coco` 文件夹下。\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── stuffthingmaps\n```\n\n该数据集不同于标准的 COCO 类别标注，其包括 172 个类： 80 thing 类、91 stuff 类和 1 个 'unlabeled'，其每个类别的说明见 https://github.com/nightrome/cocostuff/blob/master/labels.md\n\n虽然只标注了 172 个类别，但是 `stuffthingmaps` 中最大标签 id 是 182，中间有些类别是没有标注的，并且第 0 类的 `unlabeled` 类别被移除。因此最终的 `stuffthingmaps` 图片中每个位置的值对应的类别关系见 https://github.com/kazuto1011/deeplab-pytorch/blob/master/data/datasets/cocostuff/labels.txt\n\n考虑到训练高效和方便用户，在开启训练或者评估前，我们需要将没有标注的 12 个类移除，这 12 个类的名字为： `street sign、hat、shoe、eye glasses、plate、mirror、window、desk、door、blender、hair brush`，最终可用于训练和评估的类别信息见 `mmdet/datasets/coco_semantic.py`\n\n你可以使用 `tools/dataset_converters/coco_stuff164k.py` 来完成将下载的 `stuffthingmaps` 转换为直接可以训练和评估的数据集，转换后的数据集文件夹结构如下：\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── stuffthingmaps\n│   │   ├── stuffthingmaps_semseg\n```\n\n`stuffthingmaps_semseg` 即为新生成的可以直接训练和测试的 COCO 语义分割数据集。\n\n**(2) 使用 panoptic 数据集**\n\n通过 panoptic 标注生成的语义分割数据集类别数相比使用 `stuffthingmaps` 数据集生成的会少一些。首先你需要准备全景分割标注，然后使用如下脚本完成转换\n\n```shell\npython tools/dataset_converters/prepare_coco_semantic_annos_from_panoptic_annos.py data/coco\n```\n\n转换后的数据集文件夹结构如下：\n\n```text\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── panoptic_train2017.json\n│   │   │   ├── panoptic_train2017\n│   │   │   ├── panoptic_val2017.json\n│   │   │   ├── panoptic_val2017\n│   │   │   ├── panoptic_semseg_train2017\n│   │   │   ├── panoptic_semseg_val2017\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n```\n\n`panoptic_semseg_train2017` 和 `panoptic_semseg_val2017` 即为新生成的可以直接训练和测试的 COCO 语义分割数据集。注意其类别信息就是 COCO 全景分割的类别信息，包括 thing 和 stuff。\n\n### RefCOCO 数据集准备\n\n[RefCOCO](https://github.com/lichengunc/refer)系列数据集的图像和注释可以通过运行 `tools/misc/download_dataset.py` 下载:\n\n```shell\npython tools/misc/download_dataset.py --dataset-name refcoco --save-dir data/coco --unzip\n```\n\n然后，目录应该是这样的：\n\n```text\ndata\n├── coco\n│   ├── refcoco\n│   │   ├── instances.json\n│   │   ├── refs(google).p\n│   │   └── refs(unc).p\n│   ├── refcoco+\n│   │   ├── instances.json\n│   │   └── refs(unc).p\n│   ├── refcocog\n│   │   ├── instances.json\n│   │   ├── refs(google).p\n│   │   └── refs(umd).p\n|   |── train2014\n```\n\n### ADE20K 数据集准备\n\n[ADE20K](http://groups.csail.mit.edu/vision/datasets/ADE20K/)数据集的图像和注释可以通过运行 `tools/misc/download_dataset.py` 下载:\n\n```shell\npython tools/misc/download_dataset.py --dataset-name ade20k_2016 --save-dir data --unzip\n```\n\n然后将注释移至`data/ADEChallengeData2016`目录，并运行预处理脚本以产生coco格式注释：\n\n```shell\nmv data/annotations_instance data/ADEChallengeData2016/\nmv data/categoryMapping.txt data/ADEChallengeData2016/\nmv data/imgCatIds.json data/ADEChallengeData2016/\npython tools/dataset_converters/ade20k2coco.py data/ADEChallengeData2016 --task panoptic\npython tools/dataset_converters/ade20k2coco.py data/ADEChallengeData2016 --task instance\n```\n\n然后，目录应该是这样的：\n\n```text\ndata\n├── ADEChallengeData2016\n│   ├── ade20k_instance_train.json\n│   ├── ade20k_instance_val.json\n│   ├── ade20k_panoptic_train\n|   |   ├── ADE_train_00000001.png\n|   |   ├── ADE_train_00000002.png\n|   |   ├── ...\n│   ├── ade20k_panoptic_train.json\n│   ├── ade20k_panoptic_val\n|   |   ├── ADE_val_00000001.png\n|   |   ├── ADE_val_00000002.png\n|   |   ├── ...\n│   ├── ade20k_panoptic_val.json\n│   ├── annotations\n|   |   ├── training\n|   |   |   ├── ADE_train_00000001.png\n|   |   |   ├── ADE_train_00000002.png\n|   |   |   ├── ...\n|   |   ├── validation\n|   |   |   ├── ADE_val_00000001.png\n|   |   |   ├── ADE_val_00000002.png\n|   |   |   ├── ...\n│   ├── annotations_instance\n|   |   ├── training\n|   |   |   ├── ADE_train_00000001.png\n|   |   |   ├── ADE_train_00000002.png\n|   |   |   ├── ...\n|   |   ├── validation\n|   |   |   ├── ADE_val_00000001.png\n|   |   |   ├── ADE_val_00000002.png\n|   |   |   ├── ...\n│   ├── categoryMapping.txt\n│   ├── images\n│   |   ├── training\n|   |   |   ├── ADE_train_00000001.jpg\n|   |   |   ├── ADE_train_00000002.jpg\n|   |   |   ├── ...\n|   |   ├── validation\n|   |   |   ├── ADE_val_00000001.jpg\n|   |   |   ├── ADE_val_00000002.jpg\n|   |   |   ├── ...\n│   ├── imgCatIds.json\n│   ├── objectInfo150.txt\n|   |── sceneCategories.txt\n```\n\n上述文件夹包括ADE20K的语义分割、实例分割和泛在分割的所有数据。\n\n### 从 OpenDataLab 中下载\n\n[OpenDataLab](https://opendatalab.com/) 为人工智能研究者提供免费开源的数据集，通过 OpenDataLab，研究者可以获得格式统一的各领域经典数据集。通过平台的搜索功能，研究者可以迅速便捷地找到自己所需数据集；通过平台的统一格式，研究者可以便捷地对跨数据集任务进行开发。\n\n目前，MIM 支持使用一条命令行从 OpenDataLab 中下载 VOC 和 COCO 数据集，后续将支持更多数据集。你也可以直接访问 OpenDataLab 平台下载你所需的数据集，然后将其转化为 MMDetection 所要求的格式。\n\n如果使用 MIM 下载，请确保版本大于 v0.3.8，你可以使用如下命令更新:\n\n```Bash\npip install -U openmim\n```\n\n```Bash\n# install OpenXLab CLI tools\npip install -U openxlab\n# log in OpenXLab, registry\nopenxlab login\n\n# download voc2007 and preprocess by MIM\nmim download mmdet --dataset voc2007\n\n# download voc2012 and preprocess by MIM\nmim download mmdet --dataset voc2012\n\n# download coco2017 and preprocess by MIM\nmim download mmdet --dataset coco2017\n```\n\n### ODinW 数据集准备\n\nODinW 数据集来自 GLIP 论文，用于评估预训练模型泛化性能。一共包括 ODinW-13 和 ODinW-35 两个版本，其中 ODinW-35 包括了 ODinW-13 的所有数据。 目前数据托管在 [huggingface](https://huggingface.co/GLIPModel/GLIP)\n\n请确保你提前安装好了 [git lfs](https://git-lfs.com), 然后按照如下命令下载\n\n```shell\ncd mmdetection\n\ngit lfs install\n# 我们不需要下载权重\nGIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/GLIPModel/GLIP\n\ncd GLIP\ngit lfs pull --include=\"odinw_35\"\n```\n\n下载完成后，目录结构如下所示：\n\n```text\nmmdetection\n├── GLIP\n|     ├── odinw_35\n|     |   ├── AerialMaritimeDrone.zip\n|     |   ├── AmericanSignLanguageLetters.zip\n...\n```\n\n你可以采用如下命令全部解压并移动到 `mmdetection/data` 路径下：\n\n```shell\n#!/bin/bash\n\nfolder=\"./GLIP/odinw_35/\"\n\nfind \"$folder\" -type f -name \"*.zip\" | while read -r file; do\n    unzip \"$file\" -d \"$(dirname \"$file\")\"\ndone\n\nmv GLIP/odinw_35 data/\n```\n\n最终结构如下所示：\n\n```text\nmmdetection\n├── tools\n├── configs\n├── data\n|   ├── odinw_35\n|   |   ├── AerialMaritimeDrone\n...\n│   ├── coco\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/deploy.md",
    "content": "# 模型部署\n\n[MMDeploy](https://github.com/open-mmlab/mmdeploy) 是 OpenMMLab 的部署仓库，负责包括 MMPretrain、MMDetection 等在内的各算法库的部署工作。\n你可以从[这里](https://mmdeploy.readthedocs.io/zh_CN/1.x/04-supported-codebases/mmdet.html)获取 MMDeploy 对 MMDetection 部署支持的最新文档。\n\n本文的结构如下：\n\n- [安装](#安装)\n- [模型转换](#模型转换)\n- [模型规范](#模型规范)\n- [模型推理](#模型推理)\n  - [后端模型推理](#后端模型推理)\n  - [SDK 模型推理](#sdk-模型推理)\n- [模型支持列表](#模型支持列表)\n-\n\n## 安装\n\n请参考[此处](https://mmdetection.readthedocs.io/en/latest/get_started.html)安装 mmdet。然后，按照[说明](https://mmdeploy.readthedocs.io/zh_CN/1.x/get_started.html#mmdeploy)安装 mmdeploy。\n\n```{note}\n如果安装的是 mmdeploy 预编译包，那么也请通过 'git clone https://github.com/open-mmlab/mmdeploy.git --depth=1' 下载 mmdeploy 源码。因为它包含了部署时要用到的配置文件\n```\n\n## 模型转换\n\n假设在安装步骤中，mmdetection 和 mmdeploy 代码库在同级目录下，并且当前的工作目录为 mmdetection 的根目录，那么以 [Faster R-CNN](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py) 模型为例，你可以从[此处](https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth)下载对应的 checkpoint，并使用以下代码将之转换为 onnx 模型：\n\n```python\nfrom mmdeploy.apis import torch2onnx\nfrom mmdeploy.backend.sdk.export_info import export2SDK\n\nimg = 'demo/demo.jpg'\nwork_dir = 'mmdeploy_models/mmdet/onnx'\nsave_file = 'end2end.onnx'\ndeploy_cfg = '../mmdeploy/configs/mmdet/detection/detection_onnxruntime_dynamic.py'\nmodel_cfg = 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\nmodel_checkpoint = 'faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth'\ndevice = 'cpu'\n\n# 1. convert model to onnx\ntorch2onnx(img, work_dir, save_file, deploy_cfg, model_cfg,\n           model_checkpoint, device)\n\n# 2. extract pipeline info for inference by MMDeploy SDK\nexport2SDK(deploy_cfg, model_cfg, work_dir, pth=model_checkpoint,\n           device=device)\n```\n\n转换的关键之一是使用正确的配置文件。项目中已内置了各后端部署[配置文件](https://github.com/open-mmlab/mmdeploy/tree/1.x/configs/mmdet)。\n文件的命名模式是：\n\n```\n{task}/{task}_{backend}-{precision}_{static | dynamic}_{shape}.py\n```\n\n其中：\n\n- **{task}:** mmdet 中的任务\n\n  mmdet 任务有2种：物体检测（detection）、实例分割（instance-seg）。例如，`RetinaNet`、`Faster R-CNN`、`DETR`等属于前者。`Mask R-CNN`、`SOLO`等属于后者。更多`模型-任务`的划分，请参考章节[模型支持列表](#模型支持列表)。\n\n  **请务必**使用 `detection/detection_*.py` 转换检测模型，使用 `instance-seg/instance-seg_*.py` 转换实例分割模型。\n\n- **{backend}:** 推理后端名称。比如，onnxruntime、tensorrt、pplnn、ncnn、openvino、coreml 等等\n\n- **{precision}:** 推理精度。比如，fp16、int8。不填表示 fp32\n\n- **{static | dynamic}:** 动态、静态 shape\n\n- **{shape}:** 模型输入的 shape 或者 shape 范围\n\n在上例中，你也可以把 `Faster R-CNN` 转为其他后端模型。比如使用`detection_tensorrt-fp16_dynamic-320x320-1344x1344.py`，把模型转为 tensorrt-fp16 模型。\n\n```{tip}\n当转 tensorrt 模型时, --device 需要被设置为 \"cuda\"\n```\n\n## 模型规范\n\n在使用转换后的模型进行推理之前，有必要了解转换结果的结构。 它存放在 `--work-dir` 指定的路路径下。\n\n上例中的`mmdeploy_models/mmdet/onnx`，结构如下：\n\n```\nmmdeploy_models/mmdet/onnx\n├── deploy.json\n├── detail.json\n├── end2end.onnx\n└── pipeline.json\n```\n\n重要的是：\n\n- **end2end.onnx**: 推理引擎文件。可用 ONNX Runtime 推理\n- ***xxx*.json**:  mmdeploy SDK 推理所需的 meta 信息\n\n整个文件夹被定义为**mmdeploy SDK model**。换言之，**mmdeploy SDK model**既包括推理引擎，也包括推理 meta 信息。\n\n## 模型推理\n\n## 后端模型推理\n\n以上述模型转换后的 `end2end.onnx` 为例，你可以使用如下代码进行推理：\n\n```python\nfrom mmdeploy.apis.utils import build_task_processor\nfrom mmdeploy.utils import get_input_shape, load_config\nimport torch\n\ndeploy_cfg = '../mmdeploy/configs/mmdet/detection/detection_onnxruntime_dynamic.py'\nmodel_cfg = 'configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\ndevice = 'cpu'\nbackend_model = ['mmdeploy_models/mmdet/onnx/end2end.onnx']\nimage = 'demo/demo.jpg'\n\n# read deploy_cfg and model_cfg\ndeploy_cfg, model_cfg = load_config(deploy_cfg, model_cfg)\n\n# build task and backend model\ntask_processor = build_task_processor(model_cfg, deploy_cfg, device)\nmodel = task_processor.build_backend_model(backend_model)\n\n# process input image\ninput_shape = get_input_shape(deploy_cfg)\nmodel_inputs, _ = task_processor.create_input(image, input_shape)\n\n# do model inference\nwith torch.no_grad():\n    result = model.test_step(model_inputs)\n\n# visualize results\ntask_processor.visualize(\n    image=image,\n    model=model,\n    result=result[0],\n    window_name='visualize',\n    output_file='output_detection.png')\n```\n\n## SDK 模型推理\n\n你也可以参考如下代码，对 SDK model 进行推理：\n\n```python\nfrom mmdeploy_python import Detector\nimport cv2\n\nimg = cv2.imread('demo/demo.jpg')\n\n# create a detector\ndetector = Detector(model_path='mmdeploy_models/mmdet/onnx',\n                    device_name='cpu', device_id=0)\n# perform inference\nbboxes, labels, masks = detector(img)\n\n# visualize inference result\nindices = [i for i in range(len(bboxes))]\nfor index, bbox, label_id in zip(indices, bboxes, labels):\n    [left, top, right, bottom], score = bbox[0:4].astype(int), bbox[4]\n    if score < 0.3:\n        continue\n\n    cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0))\n\ncv2.imwrite('output_detection.png', img)\n```\n\n除了python API，mmdeploy SDK 还提供了诸如 C、C++、C#、Java等多语言接口。\n你可以参考[样例](https://github.com/open-mmlab/mmdeploy/tree/1.x/demo)学习其他语言接口的使用方法。\n\n## 模型支持列表\n\n请参考[这里](https://mmdeploy.readthedocs.io/zh_CN/1.x/04-supported-codebases/mmdet.html#id6)\n"
  },
  {
    "path": "docs/zh_cn/user_guides/finetune.md",
    "content": "# 模型微调\n\n在 COCO 数据集上预训练的检测器可以作为其他数据集（例如 CityScapes 和 KITTI 数据集）优质的预训练模型。\n本教程将指导用户如何把 [ModelZoo](../model_zoo.md) 中提供的模型用于其他数据集中并使得当前所训练的模型获得更好性能。\n\n以下是在新数据集中微调模型需要的两个步骤。\n\n- 按 [教程2：自定义数据集](../advanced_guides/customize_dataset.md) 中的方法对新数据集添加支持中的方法对新数据集添加支持\n- 按照本教程中所讨论方法，修改配置信息\n\n接下来将会以 Cityscapes Dataset 上的微调过程作为例子，具体讲述用户需要在配置中修改的五个部分。\n\n## 继承基础配置\n\n为了减轻编写整个配置的负担并减少漏洞的数量， MMDetection V3.0 支持从多个现有配置中继承配置信息。微调 MaskRCNN 模型的时候，新的配置信息需要使用从 `_base_/models/mask_rcnn_r50_fpn.py` 中继承的配置信息来构建模型的基本结构。当使用 Cityscapes 数据集时，新的配置信息可以简便地从`_base_/datasets/cityscapes_instance.py` 中继承。对于训练过程的运行设置部分，例如 `logger settings`，配置文件可以从 `_base_/default_runtime.py` 中继承。对于训练计划的配置则可以从`_base_/schedules/schedule_1x.py` 中继承。这些配置文件存放于 `configs` 目录下，用户可以选择全部内容的重新编写而不是使用继承方法。\n\n```python\n_base_ = [\n    '../_base_/models/mask_rcnn_r50_fpn.py',\n    '../_base_/datasets/cityscapes_instance.py', '../_base_/default_runtime.py',\n    '../_base_/schedules/schedule_1x.py'\n]\n```\n\n## Head 的修改\n\n接下来新的配置还需要根据新数据集的类别数量对 Head 进行修改。只需要对 roi_head 中的 `num_classes`进行修改。修改后除了最后的预测模型的 Head 之外，预训练模型的权重的大部分都会被重新使用。\n\n```python\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared2FCBBoxHead',\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=8,\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=8,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))))\n```\n\n## 数据集的修改\n\n用户可能还需要准备数据集并编写有关数据集的配置，可在 [Customize Datasets](../advanced_guides/customize_dataset.md) 中获取更多信息。目前 MMDetection V3.0 的配置文件已经支持 VOC、WIDERFACE、COCO、LIVS、OpenImages、DeepFashion、Objects365 和 Cityscapes Dataset 的数据集信息。\n\n## 训练策略的修改\n\n微调超参数与默认的训练策略不同。它通常需要更小的学习率和更少的训练回合。\n\n```python\n# 优化器\n# batch size 为 8 时的 lr 配置\noptim_wrapper = dict(optimizer=dict(lr=0.01))\n\n# 学习率\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=8,\n        by_epoch=True,\n        milestones=[7],\n        gamma=0.1)\n]\n\n# 设置 max epoch\ntrain_cfg = dict(max_epochs=8)\n\n# 设置 log config\ndefault_hooks = dict(logger=dict(interval=100)),\n\n```\n\n## 使用预训练模型\n\n如果要使用预训练模型，可以在 `load_from` 中查阅新的配置信息，用户需要在训练开始之前下载好需要的模型权重，从而避免在训练过程中浪费了宝贵时间。\n\n```python\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'  # noqa\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/index.rst",
    "content": "训练 & 测试\n**************\n\nMMDetection 在 `Model Zoo <https://mmdetection.readthedocs.io/en/latest/model_zoo.html>`_ 中提供了数百个预训练的检测模型，\n并支持多种标准数据集格式，包括 Pascal VOC、COCO、CityScapes、LVIS 等。本文档将展示如何使用这些模型和数据集来执行常见的训练和测试任务：\n\n.. toctree::\n   :maxdepth: 1\n\n   config.md\n   inference.md\n   dataset_prepare.md\n   test.md\n   train.md\n   new_model.md\n   finetune.md\n   test_results_submission.md\n   init_cfg.md\n   single_stage_as_rpn.md\n   semi_det.md\n\n\n实用工具\n************\n\n.. toctree::\n   :maxdepth: 1\n\n   useful_tools.md\n   useful_hooks.md\n   visualization.md\n   robustness_benchmarking.md\n   deploy.md\n   label_studio.md\n"
  },
  {
    "path": "docs/zh_cn/user_guides/inference.md",
    "content": "# 使用已有模型在标准数据集上进行推理\n\nMMDetection 提供了许多预训练好的检测模型，可以在 [Model Zoo](https://mmdetection.readthedocs.io/zh_CN/latest/model_zoo.html) 查看具体有哪些模型。\n\n推理具体指使用训练好的模型来检测图像上的目标，本文将会展示具体步骤。\n\n在 MMDetection 中，一个模型被定义为一个[配置文件](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/config.html) 和对应被存储在 checkpoint 文件内的模型参数的集合。\n\n首先，我们建议从 [RTMDet](https://github.com/open-mmlab/mmdetection/tree/main/configs/rtmdet) 开始，其 [配置](https://github.com/open-mmlab/mmdetection/blob/main/configs/rtmdet/rtmdet_l_8xb32-300e_coco.py) 文件和 [checkpoint](https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_l_8xb32-300e_coco/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth) 文件在此。\n我们建议将 checkpoint 文件下载到 `checkpoints` 文件夹内。\n\n## 推理的高层编程接口——推理器\n\n在 OpenMMLab 中，所有的推理操作都被统一到了推理器 `Inferencer` 中。推理器被设计成为一个简洁易用的 API，它在不同的 OpenMMLab 库中都有着非常相似的接口。\n下面介绍的演示样例都放在 [demo/inference_demo.ipynb](https://github.com/open-mmlab/mmdetection/blob/main/demo/inference_demo.ipynb) 中方便大家尝试。\n\n### 基础用法\n\n使用 `DetInferencer`，您只需 3 行代码就可以获得推理结果。\n\n```python\nfrom mmdet.apis import DetInferencer\n\n# 初始化模型\ninferencer = DetInferencer('rtmdet_tiny_8xb32-300e_coco')\n\n# 推理示例图片\ninferencer('demo/demo.jpg', show=True)\n```\n\n可视化结果将被显示在一个新窗口中：\n\n<div align=\"center\">\n    <img src='https://github.com/open-mmlab/mmdetection/assets/27466624/311df42d-640a-4a5b-9ad9-9ba7f3ec3a2f' />\n</div>\n\n```{note}\n如果你在没有 GUI 的服务器上，或者通过禁用 X11 转发的 SSH 隧道运行以上命令，`show` 选项将不起作用。然而，你仍然可以通过设置 `out_dir` 参数将可视化数据保存到文件。阅读 [储存结果](#储存结果) 了解详情。\n```\n\n### 初始化\n\n每个推理器必须使用一个模型进行初始化。初始化时，可以手动选择推理设备。\n\n#### 模型初始化\n\n- 要用 MMDetection 的预训练模型进行推理，只需要把它的名字传给参数 `model`，权重将自动从 OpenMMLab 的模型库中下载和加载。\n\n  ```python\n  inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco')\n  ```\n\n  在 MMDetection 中有一个非常容易的方法，可以列出所有模型名称。\n\n  ```python\n  # models 是一个模型名称列表，它们将自动打印\n  models = DetInferencer.list_models('mmdet')\n  ```\n\n  你可以通过将权重的路径或 URL 传递给 `weights` 来让推理器加载自定义的权重。\n\n  ```python\n  inferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', weights='path/to/rtmdet.pth')\n  ```\n\n- 要加载自定义的配置和权重，你可以把配置文件的路径传给 `model`，把权重的路径传给 `weights`。\n\n  ```python\n  inferencer = DetInferencer(model='path/to/rtmdet_config.py', weights='path/to/rtmdet.pth')\n  ```\n\n- 默认情况下，[MMEngine](https://github.com/open-mmlab/mmengine/) 会在训练模型时自动将配置文件转储到权重文件中。如果你有一个在 MMEngine 上训练的权重，你也可以将权重文件的路径传递给 `weights`，而不需要指定 `model`：\n\n  ```python\n  # 如果无法在权重中找到配置文件，则会引发错误。目前 MMDetection 模型库中只有 ddq-detr-4scale_r50 的权重可以这样加载。\n  inferencer = DetInferencer(weights='https://download.openmmlab.com/mmdetection/v3.0/ddq/ddq-detr-4scale_r50_8xb2-12e_coco/ddq-detr-4scale_r50_8xb2-12e_coco_20230809_170711-42528127.pth')\n  ```\n\n- 传递配置文件到 `model` 而不指定 `weights` 则会产生一个随机初始化的模型。\n\n#### 推理设备\n\n每个推理器实例都会跟一个设备绑定。默认情况下，最佳设备是由 [MMEngine](https://github.com/open-mmlab/mmengine/) 自动决定的。你也可以通过指定 `device` 参数来改变设备。例如，你可以使用以下代码在 GPU 1 上创建一个推理器。\n\n```python\ninferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', device='cuda:1')\n```\n\n如要在 CPU 上创建一个推理器：\n\n```python\ninferencer = DetInferencer(model='rtmdet_tiny_8xb32-300e_coco', device='cpu')\n```\n\n请参考 [torch.device](https://pytorch.org/docs/stable/tensor_attributes.html#torch.device) 了解 `device` 参数支持的所有形式。\n\n### 推理\n\n当推理器初始化后，你可以直接传入要推理的原始数据，从返回值中获取推理结果。\n\n#### 输入\n\n输入可以是以下任意一种格式：\n\n- str: 图像的路径/URL。\n\n  ```python\n  inferencer('demo/demo.jpg')\n  ```\n\n- array: 图像的 numpy 数组。它应该是 BGR 格式。\n\n  ```python\n  import mmcv\n  array = mmcv.imread('demo/demo.jpg')\n  inferencer(array)\n  ```\n\n- list: 基本类型的列表。列表中的每个元素都将单独处理。\n\n  ```python\n  inferencer(['img_1.jpg', 'img_2.jpg])\n  # 列表内混合类型也是允许的\n  inferencer(['img_1.jpg', array])\n  ```\n\n- str: 目录的路径。目录中的所有图像都将被处理。\n\n  ```python\n  inferencer('path/to/your_imgs/')\n  ```\n\n#### 输出\n\n默认情况下，每个推理器都以字典格式返回预测结果。\n\n- `visualization` 包含可视化的预测结果。但默认情况下，它是一个空列表，除非 `return_vis=True`。\n\n- `predictions` 包含以 json-可序列化格式返回的预测结果。\n\n```python\n{\n      'predictions' : [\n        # 每个实例都对应于一个输入图像\n        {\n          'labels': [...],  # 整数列表，长度为 (N, )\n          'scores': [...],  # 浮点列表，长度为 (N, )\n          'bboxes': [...],  # 2d 列表，形状为 (N, 4)，格式为 [min_x, min_y, max_x, max_y]\n        },\n        ...\n      ],\n      'visualization' : [\n        array(..., dtype=uint8),\n      ]\n  }\n```\n\n如果你想要从模型中获取原始输出，可以将 `return_datasamples` 设置为 `True` 来获取原始的 [DataSample](advanced_guides/structures.md)，它将存储在 `predictions` 中。\n\n#### 储存结果\n\n除了从返回值中获取预测结果，你还可以通过设置 `out_dir` 和 `no_save_pred`/`no_save_vis` 参数将预测结果和可视化结果导出到文件中。\n\n```python\ninferencer('demo/demo.jpg', out_dir='outputs/', no_save_pred=False)\n```\n\n结果目录结构如下：\n\n```text\noutputs\n├── preds\n│   └── demo.json\n└── vis\n    └── demo.jpg\n```\n\n#### 批量推理\n\n你可以通过设置 `batch_size` 来自定义批量推理的批大小。默认批大小为 1。\n\n### API\n\n这里列出了推理器详尽的参数列表。\n\n- **DetInferencer.\\_\\_init\\_\\_():**\n\n| 参数            | 类型       | 默认值  | 描述                                                                                                                                                                                                                        |\n| --------------- | ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `model`         | str , 可选 | None    | 配置文件的路径或 metafile 中定义的模型名称。例如，可以是 'rtmdet-s' 或 'rtmdet_s_8xb32-300e_coco' 或 'configs/rtmdet/rtmdet_s_8xb32-300e_coco.py'。如果未指定模型，用户必须提供 MMEngine 保存的包含配置字符串的 \"weights\"。 |\n| `weights`       | str, 可选  | None    | 模型权重文件的路径。如果未指定且 `model` 是 metafile 中的模型名称，权重将从 metafile 中加载。                                                                                                                               |\n| `device`        | str, 可选  | None    | 推理使用的设备，接受 `torch.device` 允许的所有字符串。例如，'cuda:0' 或 'cpu'。如果为 None，将自动使用可用设备。 默认为 None。                                                                                              |\n| `scope`         | str, 可选  | 'mmdet' | 模型的”域名“。                                                                                                                                                                                                              |\n| `palette`       | str        | 'none'  | 用于可视化的配色。优先顺序为 palette -> config -> checkpoint。                                                                                                                                                              |\n| `show_progress` | bool       | True    | 控制是否在推理过程中显示进度条。                                                                                                                                                                                            |\n\n- **DetInferencer.\\_\\_call\\_\\_()**\n\n| 参数                 | 类型                    | 默认值   | 描述                                                                                                                                                                                                                            |\n| -------------------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `inputs`             | str/list/tuple/np.array | **必需** | 它可以是一个图片/文件夹的路径，一个 numpy 数组，或者是一个包含图片路径或 numpy 数组的列表/元组                                                                                                                                  |\n| `batch_size`         | int                     | 1        | 推理的批大小。                                                                                                                                                                                                                  |\n| `return_vis`         | bool                    | False    | 是否返回可视化结果。                                                                                                                                                                                                            |\n| `show`               | bool                    | False    | 是否在弹出窗口中显示可视化结果。                                                                                                                                                                                                |\n| `wait_time`          | float                   | 0        | 弹窗展示可视化结果的时间间隔。                                                                                                                                                                                                  |\n| `no_save_vis`        | bool                    | False    | 是否将可视化结果保存到 `out_dir`。默认为保存。                                                                                                                                                                                  |\n| `draw_pred`          | bool                    | True     | 是否绘制预测的边界框。                                                                                                                                                                                                          |\n| `pred_score_thr`     | float                   | 0.3      | 显示预测框的最低置信度。                                                                                                                                                                                                        |\n| `return_datasamples` | bool                    | False    | 是否将结果作为 `DetDataSample` 返回。 如果为 False，则结果将被打包到一个 dict 中。                                                                                                                                              |\n| `print_result`       | bool                    | False    | 是否将推理结果打印到控制台。                                                                                                                                                                                                    |\n| `no_save_pred`       | bool                    | True     | 是否将推理结果保存到 `out_dir`。默认为不保存。                                                                                                                                                                                  |\n| `out_dir`            | str                     | ''       | 结果的输出目录。                                                                                                                                                                                                                |\n| `texts`              | str/list\\[str\\]，可选   | None     | 文本提示词。                                                                                                                                                                                                                    |\n| `stuff_texts`        | str/list\\[str\\]，可选   | None     | 物体文本提示词。                                                                                                                                                                                                                |\n| `custom_entities`    | bool                    | False    | 是否使用自定义实体。只用于 GLIP 算法。                                                                                                                                                                                          |\n| \\*\\*kwargs           |                         |          | 传递给 :meth:`preprocess`、:meth:`forward`、:meth:`visualize` 和 :meth:`postprocess` 的其他关键字参数。kwargs 中的每个关键字都应在相应的 `preprocess_kwargs`、`forward_kwargs`、`visualize_kwargs` 和 `postprocess_kwargs` 中。 |\n\n## 演示脚本样例\n\n我们还提供了四个演示脚本，它们是使用高层编程接口实现的。[源码在此](https://github.com/open-mmlab/mmdetection/blob/main/demo) 。\n\n### 图片样例\n\n这是在单张图片上进行推理的脚本。\n\n```shell\npython demo/image_demo.py \\\n    ${IMAGE_FILE} \\\n    ${CONFIG_FILE} \\\n    [--weights ${WEIGHTS}] \\\n    [--device ${GPU_ID}] \\\n    [--pred-score-thr ${SCORE_THR}]\n```\n\n运行样例：\n\n```shell\npython demo/image_demo.py demo/demo.jpg \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    --weights checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n    --device cpu\n```\n\n### 摄像头样例\n\n这是使用摄像头实时图片的推理脚本。\n\n```shell\npython demo/webcam_demo.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--device ${GPU_ID}] \\\n    [--camera-id ${CAMERA-ID}] \\\n    [--score-thr ${SCORE_THR}]\n```\n\n运行样例：\n\n```shell\npython demo/webcam_demo.py \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth\n```\n\n### 视频样例\n\n这是在视频样例上进行推理的脚本。\n\n```shell\npython demo/video_demo.py \\\n    ${VIDEO_FILE} \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--device ${GPU_ID}] \\\n    [--score-thr ${SCORE_THR}] \\\n    [--out ${OUT_FILE}] \\\n    [--show] \\\n    [--wait-time ${WAIT_TIME}]\n```\n\n运行样例：\n\n```shell\npython demo/video_demo.py demo/demo.mp4 \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n    --out result.mp4\n```\n\n#### 视频样例，显卡加速版本\n\n这是在视频样例上进行推理的脚本，使用显卡加速。\n\n```shell\npython demo/video_gpuaccel_demo.py \\\n     ${VIDEO_FILE} \\\n     ${CONFIG_FILE} \\\n     ${CHECKPOINT_FILE} \\\n     [--device ${GPU_ID}] \\\n     [--score-thr ${SCORE_THR}] \\\n     [--nvdecode] \\\n     [--out ${OUT_FILE}] \\\n     [--show] \\\n     [--wait-time ${WAIT_TIME}]\n\n```\n\n运行样例：\n\n```shell\npython demo/video_gpuaccel_demo.py demo/demo.mp4 \\\n    configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n    checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n    --nvdecode --out result.mp4\n```\n\n### 大图推理样例\n\n这是在大图上进行切片推理的脚本。\n\n```shell\npython demo/large_image_demo.py \\\n\t${IMG_PATH} \\\n\t${CONFIG_FILE} \\\n\t${CHECKPOINT_FILE} \\\n\t--device ${GPU_ID}  \\\n\t--show \\\n\t--tta  \\\n\t--score-thr ${SCORE_THR} \\\n\t--patch-size ${PATCH_SIZE} \\\n\t--patch-overlap-ratio ${PATCH_OVERLAP_RATIO} \\\n\t--merge-iou-thr ${MERGE_IOU_THR} \\\n\t--merge-nms-type ${MERGE_NMS_TYPE} \\\n\t--batch-size ${BATCH_SIZE} \\\n\t--debug \\\n\t--save-patch\n```\n\n运行样例:\n\n```shell\n# inferecnce without tta\nwget -P checkpoint https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r101_fpn_2x_coco/faster_rcnn_r101_fpn_2x_coco_bbox_mAP-0.398_20200504_210455-1d2dac9c.pth\n\npython demo/large_image_demo.py \\\n    demo/large_image.jpg \\\n    configs/faster_rcnn/faster-rcnn_r101_fpn_2x_coco.py \\\n    checkpoint/faster_rcnn_r101_fpn_2x_coco_bbox_mAP-0.398_20200504_210455-1d2dac9c.pth\n\n# inference with tta\nwget -P checkpoint https://download.openmmlab.com/mmdetection/v2.0/retinanet/retinanet_r50_fpn_1x_coco/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth\n\npython demo/large_image_demo.py \\\n    demo/large_image.jpg \\\n    configs/retinanet/retinanet_r50_fpn_1x_coco.py \\\n    checkpoint/retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth --tta\n```\n\n## 多模态算法的推理和验证\n\n随着多模态视觉算法的不断发展，MMDetection 也完成了对这类算法的支持。这一小节我们通过 GLIP 算法和模型来演示如何使用对应多模态算法的 demo 和 eval 脚本。同时 MMDetection 也在 projects 下完成了 [gradio_demo 项目](../../../projects/gradio_demo/)，用户可以参照[文档](../../../projects/gradio_demo/README.md)在本地快速体验 MMDetection 中支持的各类图片输入的任务。\n\n### 模型准备\n\n首先需要安装多模态依赖：\n\n```shell\n# if source\npip install -r requirements/multimodal.txt\n\n# if wheel\nmim install mmdet[multimodal]\n```\n\nMMDetection 已经集成了 glip 算法和模型，可以直接使用链接下载使用：\n\n```shell\ncd mmdetection\nwget https://download.openmmlab.com/mmdetection/v3.0/glip/glip_tiny_a_mmdet-b3654169.pth\n```\n\n### 推理演示\n\n下载完成后我们就可以利用 `demo` 下的多模态推理脚本完成推理：\n\n```shell\npython demo/image_demo.py demo/demo.jpg glip_tiny_a_mmdet-b3654169.pth --texts bench\n```\n\ndemo 效果如下图所示：\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/234547841-266476c8-f987-4832-8642-34357be621c6.png\" height=\"300\"/>\n</div>\n\n如果想进行多种类型的识别，需要使用 `xx. xx` 的格式在 `--texts` 字段后声明目标类型:\n\n```shell\npython demo/image_demo.py demo/demo.jpg glip_tiny_a_mmdet-b3654169.pth --texts 'bench. car'\n```\n\n结果如下图所示：\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/234548156-ef9bbc2e-7605-4867-abe6-048b8578893d.png\" height=\"300\"/>\n</div>\n\n推理脚本还支持输入一个句子作为 `--texts` 字段的输入：\n\n```shell\npython demo/image_demo.py demo/demo.jpg glip_tiny_a_mmdet-b3654169.pth --texts 'There are a lot of cars here.'\n```\n\n结果可以参考下图：\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/234548490-d2e0a16d-1aad-4708-aea0-c829634fabbd.png\" height=\"300\"/>\n</div>\n\n### 验证演示\n\nMMDetection 支持后的 GLIP 算法对比官方版本没有精度上的损失， benchmark 如下所示：\n\n| Model                   | official mAP | mmdet mAP |\n| ----------------------- | :----------: | :-------: |\n| glip_A_Swin_T_O365.yaml |     42.9     |   43.0    |\n| glip_Swin_T_O365.yaml   |     44.9     |   44.9    |\n| glip_Swin_L.yaml        |     51.4     |   51.3    |\n\n用户可以使用 `test.py` 脚本对模型精度进行验证，使用如下所示：\n\n```shell\n# 1 gpu\npython tools/test.py configs/glip/glip_atss_swin-t_fpn_dyhead_pretrain_obj365.py glip_tiny_a_mmdet-b3654169.pth\n\n# 8 GPU\n./tools/dist_test.sh configs/glip/glip_atss_swin-t_fpn_dyhead_pretrain_obj365.py glip_tiny_a_mmdet-b3654169.pth 8\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/init_cfg.md",
    "content": "# 权重初始化\n\n在训练过程中，适当的初始化策略有利于加快训练速度或获得更⾼的性能。 [MMCV](https://github.com/open-mmlab/mmcv/blob/master/mmcv/cnn/utils/weight_init.py) 提供了一些常⽤的初始化模块的⽅法，如 `nn.Conv2d`。 MMdetection 中的模型初始化主要使⽤ `init_cfg`。⽤⼾可以通过以下两个步骤来初始化模型：\n\n1. 在 `model_cfg` 中为模型或其组件定义 `init_cfg`，但⼦组件的 `init_cfg` 优先级更⾼，会覆盖⽗模块的 `init_cfg` 。\n2. 像往常一样构建模型，然后显式调⽤ `model.init_weights()` ⽅法，此时模型参数将会被按照配置文件写法进行初始化。\n\nMMdetection 初始化工作流的高层 API 调用流程是：\n\nmodel_cfg(init_cfg) -> build_from_cfg -> model -> init_weight() -> initialize(self, self.init_cfg) -> children's init_weight()\n\n### 描述\n\n它的数据类型是 dict 或者 list\\[dict\\]，包含了下列键值:\n\n- `type` (str)，包含 `INTIALIZERS` 中的初始化器名称，后面跟着初始化器的参数。\n- `layer`（str 或 list\\[str\\]），包含 Pytorch 或 MMCV 中基本层的名称，以及将被初始化的可学习参数，例如 `'Conv2d'`，`'DeformConv2d'`。\n- `override` (dict 或 list\\[dict\\])，包含不继承⾃ `BaseModule` 且其初始化配置与 `layer` 键中的其他层不同的⼦模块。 `type` 中定义的初始化器将适⽤于 `layer` 中定义的所有层，因此如果⼦模块不是 `BaseModule` 的派⽣类但可以与 `layer` 中的层相同的⽅式初始化，则不需要使⽤ `override`。`override` 包含了：\n  - `type` 后跟初始化器的参数；\n  - `name` 用以指⽰将被初始化的⼦模块。\n\n### 初始化参数\n\n从 `mmcv.runner.BaseModule` 或 `mmdet.models` 继承一个新模型。这里我们用 FooModel 来举个例子。\n\n```python\nimport torch.nn as nn\nfrom mmcv.runner import BaseModule\n\nclass FooModel(BaseModule)\n\tdef __init__(self,\n                 arg1,\n                 arg2,\n                 init_cfg=None):\n    \tsuper(FooModel, self).__init__(init_cfg)\n\t\t...\n```\n\n- 直接在代码中使⽤ `init_cfg` 初始化模型\n\n  ```python\n  import torch.nn as nn\n  from mmcv.runner import BaseModule\n  # or directly inherit mmdet models\n\n  class FooModel(BaseModule)\n  \tdef __init__(self,\n                  arg1,\n                  arg2,\n                  init_cfg=XXX):\n    \t\tsuper(FooModel, self).__init__(init_cfg)\n    \t    ...\n  ```\n\n- 在 `mmcv.Sequential` 或 `mmcv.ModuleList` 代码中直接使⽤ `init_cfg` 初始化模型\n\n  ```python\n  from mmcv.runner import BaseModule, ModuleList\n\n  class FooModel(BaseModule)\n  \tdef __init__(self,\n                \targ1,\n                \targ2,\n                \tinit_cfg=None):\n    \t\tsuper(FooModel, self).__init__(init_cfg)\n        \t...\n        \tself.conv1 = ModuleList(init_cfg=XXX)\n  ```\n\n- 使⽤配置⽂件中的 `init_cfg` 初始化模型\n\n  ```python\n  model = dict(\n  \t...\n    \tmodel = dict(\n        \ttype='FooModel',\n        \targ1=XXX,\n        \targ2=XXX,\n        \tinit_cfg=XXX),\n            ...\n  ```\n\n### init_cfg 的使用\n\n1. 用 `layer` 键初始化模型\n\n   如果我们只定义了 `layer`, 它只会在 `layer` 键中初始化网络层。\n\n   注意： `layer` 键对应的值是 Pytorch 的带有 weights 和 bias 属性的类名（因此不⽀持 `MultiheadAttention` 层）。\n\n- 定义⽤于初始化具有相同配置的模块的 `layer` 键。\n\n  ```python\n  init_cfg = dict(type='Constant', layer=['Conv1d', 'Conv2d', 'Linear'], val=1)\n  # ⽤相同的配置初始化整个模块\n  ```\n\n- 定义⽤于初始化具有不同配置的层的 `layer` 键。\n\n  ```python\n  init_cfg = [dict(type='Constant', layer='Conv1d', val=1),\n              dict(type='Constant', layer='Conv2d', val=2),\n              dict(type='Constant', layer='Linear', val=3)]\n  # nn.Conv1d 将被初始化为 dict(type='Constant', val=1)\n  # nn.Conv2d 将被初始化为 dict(type='Constant', val=2)\n  # nn.Linear 将被初始化为 dict(type='Constant', val=3)\n  ```\n\n2. 使⽤ `override` 键初始化模型\n\n- 当使⽤属性名初始化某些特定部分时，我们可以使⽤ `override` 键， `override` 中的值将忽略 init_cfg 中的值。\n\n  ```python\n  # layers：\n  # self.feat = nn.Conv1d(3, 1, 3)\n  # self.reg = nn.Conv2d(3, 3, 3)\n  # self.cls = nn.Linear(1,2)\n\n  init_cfg = dict(type='Constant',\n                  layer=['Conv1d','Conv2d'], val=1, bias=2,\n                  override=dict(type='Constant', name='reg', val=3, bias=4))\n  # self.feat and self.cls 将被初始化为 dict(type='Constant', val=1, bias=2)\n  # 叫 'reg' 的模块将被初始化为 dict(type='Constant', val=3, bias=4)\n  ```\n\n- 如果 init_cfg 中的 `layer` 为 None，则只会初始化 override 中有 name 的⼦模块，⽽ override 中的 type 和其他参数可以省略。\n\n  ```python\n  # layers：\n  # self.feat = nn.Conv1d(3, 1, 3)\n  # self.reg = nn.Conv2d(3, 3, 3)\n  # self.cls = nn.Linear(1,2)\n\n  init_cfg = dict(type='Constant', val=1, bias=2, \toverride=dict(name='reg'))\n\n  # self.feat and self.cls 将被 Pytorch 初始化\n  # 叫 'reg' 的模块将被 dict(type='Constant', val=1, bias=2) 初始化\n  ```\n\n- 如果我们不定义 `layer` 或 `override` 键，它不会初始化任何东西。\n\n- 无效的使用\n\n  ```python\n  # override 没有 name 键的话是无效的\n  init_cfg = dict(type='Constant', layer=['Conv1d','Conv2d'], val=1, bias=2,\n              \toverride=dict(type='Constant', val=3, bias=4))\n\n  # override 有 name 键和其他参数但是没有 type 键也是无效的\n  init_cfg = dict(type='Constant', layer=['Conv1d','Conv2d'], val=1, bias=2,\n                  override=dict(name='reg', val=3, bias=4))\n  ```\n\n3. 使⽤预训练模型初始化模型\n\n   ```python\n   init_cfg = dict(type='Pretrained',\n                checkpoint='torchvision://resnet50')\n   ```\n\n更多细节可以参考 [MMEngine](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/initialize.html) 的文档\n"
  },
  {
    "path": "docs/zh_cn/user_guides/label_studio.md",
    "content": "# 使用 MMDetection 和 Label-Studio 进行半自动化目标检测标注\n\n标注数据是一个费时费力的任务，本文介绍了如何使用 MMDetection 中的 RTMDet 算法联合 Label-Studio 软件进行半自动化标注。具体来说，使用 RTMDet 预测图片生成标注，然后使用 Label-Studio 进行微调标注，社区用户可以参考此流程和方法，将其应用到其他领域。\n\n- RTMDet：RTMDet 是 OpenMMLab 自研的高精度单阶段的目标检测算法，开源于 MMDetection 目标检测工具箱中，其开源协议为 Apache 2.0，工业界的用户可以不受限的免费使用。\n- [Label Studio](https://github.com/heartexlabs/label-studio) 是一款优秀的标注软件，覆盖图像分类、目标检测、分割等领域数据集标注的功能。\n\n本文将使用[喵喵数据集](https://download.openmmlab.com/mmyolo/data/cat_dataset.zip)的图片，进行半自动化标注。\n\n## 环境配置\n\n首先需要创建一个虚拟环境，然后安装 PyTorch 和 MMCV。在本文中，我们将指定 PyTorch 和 MMCV 的版本。接下来安装 MMDetection、Label-Studio 和 label-studio-ml-backend，具体步骤如下：\n\n创建虚拟环境：\n\n```shell\nconda create -n rtmdet python=3.9 -y\nconda activate rtmdet\n```\n\n安装 PyTorch\n\n```shell\n# Linux and Windows CPU only\npip install torch==1.10.1+cpu torchvision==0.11.2+cpu torchaudio==0.10.1 -f https://download.pytorch.org/whl/cpu/torch_stable.html\n# Linux and Windows CUDA 11.3\npip install torch==1.10.1+cu113 torchvision==0.11.2+cu113 torchaudio==0.10.1 -f https://download.pytorch.org/whl/cu113/torch_stable.html\n# OSX\npip install torch==1.10.1 torchvision==0.11.2 torchaudio==0.10.1\n```\n\n安装 MMCV\n\n```shell\npip install -U openmim\nmim install \"mmcv>=2.0.0\"\n# 安装 mmcv 的过程中会自动安装 mmengine\n```\n\n安装 MMDetection\n\n```shell\ngit clone https://github.com/open-mmlab/mmdetection\ncd mmdetection\npip install -v -e .\n```\n\n安装 Label-Studio 和 label-studio-ml-backend\n\n```shell\n# 安装 label-studio 需要一段时间,如果找不到版本请使用官方源\npip install label-studio==1.7.2\npip install label-studio-ml==1.0.9\n```\n\n下载rtmdet权重\n\n```shell\ncd path/to/mmetection\nmkdir work_dirs\ncd work_dirs\nwget https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_m_8xb32-300e_coco/rtmdet_m_8xb32-300e_coco_20220719_112220-229f527c.pth\n```\n\n## 启动服务\n\n启动 RTMDet 后端推理服务：\n\n```shell\ncd path/to/mmetection\n\nlabel-studio-ml start projects/LabelStudio/backend_template --with \\\nconfig_file=configs/rtmdet/rtmdet_m_8xb32-300e_coco.py \\\ncheckpoint_file=./work_dirs/rtmdet_m_8xb32-300e_coco_20220719_112220-229f527c.pth \\\ndevice=cpu \\\n--port 8003\n# device=cpu 为使用 CPU 推理，如果使用 GPU 推理，将 cpu 替换为 cuda:0\n```\n\n![](https://cdn.vansin.top/picgo20230330131601.png)\n\n此时，RTMDet 后端推理服务已经启动，后续在 Label-Studio Web 系统中配置 http://localhost:8003 后端推理服务即可。\n\n现在启动 Label-Studio 网页服务：\n\n```shell\nlabel-studio start\n```\n\n![](https://cdn.vansin.top/picgo20230330132913.png)\n\n打开浏览器访问 [http://localhost:8080/](http://localhost:8080/) 即可看到 Label-Studio 的界面。\n\n![](https://cdn.vansin.top/picgo20230330133118.png)\n\n我们注册一个用户，然后创建一个 RTMDet-Semiautomatic-Label 项目。\n\n![](https://cdn.vansin.top/picgo20230330133333.png)\n\n我们通过下面的方式下载好示例的喵喵图片，点击 Data Import 导入需要标注的猫图片。\n\n```shell\ncd path/to/mmetection\nmkdir data && cd data\n\nwget https://download.openmmlab.com/mmyolo/data/cat_dataset.zip && unzip cat_dataset.zip\n```\n\n![](https://cdn.vansin.top/picgo20230330133628.png)\n\n![](https://cdn.vansin.top/picgo20230330133715.png)\n\n然后选择 Object Detection With Bounding Boxes 模板\n\n![](https://cdn.vansin.top/picgo20230330133807.png)\n\n```shell\nairplane\napple\nbackpack\nbanana\nbaseball_bat\nbaseball_glove\nbear\nbed\nbench\nbicycle\nbird\nboat\nbook\nbottle\nbowl\nbroccoli\nbus\ncake\ncar\ncarrot\ncat\ncell_phone\nchair\nclock\ncouch\ncow\ncup\ndining_table\ndog\ndonut\nelephant\nfire_hydrant\nfork\nfrisbee\ngiraffe\nhair_drier\nhandbag\nhorse\nhot_dog\nkeyboard\nkite\nknife\nlaptop\nmicrowave\nmotorcycle\nmouse\norange\noven\nparking_meter\nperson\npizza\npotted_plant\nrefrigerator\nremote\nsandwich\nscissors\nsheep\nsink\nskateboard\nskis\nsnowboard\nspoon\nsports_ball\nstop_sign\nsuitcase\nsurfboard\nteddy_bear\ntennis_racket\ntie\ntoaster\ntoilet\ntoothbrush\ntraffic_light\ntrain\ntruck\ntv\numbrella\nvase\nwine_glass\nzebra\n```\n\n然后将上述类别复制添加到 Label-Studio，然后点击 Save。\n\n![](https://cdn.vansin.top/picgo20230330134027.png)\n\n然后在设置中点击 Add Model 添加 RTMDet 后端推理服务。\n\n![](https://cdn.vansin.top/picgo20230330134320.png)\n\n点击 Validate and Save，然后点击 Start Labeling。\n\n![](https://cdn.vansin.top/picgo20230330134424.png)\n\n看到如下 Connected 就说明后端推理服务添加成功。\n\n![](https://cdn.vansin.top/picgo20230330134554.png)\n\n## 开始半自动化标注\n\n点击 Label 开始标注\n\n![](https://cdn.vansin.top/picgo20230330134804.png)\n\n我们可以看到 RTMDet 后端推理服务已经成功返回了预测结果并显示在图片上，我们可以发现这个喵喵预测的框有点大。\n\n![](https://cdn.vansin.top/picgo20230403104419.png)\n\n我们手工拖动框，修正一下框的位置，得到以下修正过后的标注，然后点击 Submit，本张图片就标注完毕了。\n\n![](https://cdn.vansin.top/picgo/20230403105923.png)\n\n我们 submit 完毕所有图片后，点击 exprot 导出 COCO 格式的数据集，就能把标注好的数据集的压缩包导出来了。\n\n![](https://cdn.vansin.top/picgo20230330135921.png)\n\n用 vscode 打开解压后的文件夹，可以看到标注好的数据集，包含了图片和 json 格式的标注文件。\n\n![](https://cdn.vansin.top/picgo20230330140321.png)\n\n到此半自动化标注就完成了，我们可以用这个数据集在 MMDetection 训练精度更高的模型了，训练出更好的模型，然后再用这个模型继续半自动化标注新采集的图片，这样就可以不断迭代，扩充高质量数据集，提高模型的精度。\n\n## 使用 MMYOLO 作为后端推理服务\n\n如果想在 MMYOLO 中使用 Label-Studio，可以参考在启动后端推理服务时，将 config_file 和 checkpoint_file 替换为 MMYOLO 的配置文件和权重文件即可。\n\n```shell\ncd path/to/mmetection\n\nlabel-studio-ml start projects/LabelStudio/backend_template --with \\\nconfig_file= path/to/mmyolo_config.py \\\ncheckpoint_file= path/to/mmyolo_weights.pth \\\ndevice=cpu \\\n--port 8003\n# device=cpu 为使用 CPU 推理，如果使用 GPU 推理，将 cpu 替换为 cuda:0\n```\n\n旋转目标检测和实例分割还在支持中，敬请期待。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/new_model.md",
    "content": "# 在标准数据集上训练自定义模型（待更新）\n\n在本文中，你将知道如何在标准数据集上训练、测试和推理自定义模型。我们将在 cityscapes 数据集上以自定义 Cascade Mask R-CNN R50 模型为例演示整个过程，为了方便说明，我们将 neck 模块中的 `FPN` 替换为 `AugFPN`，并且在训练中的自动增强类中增加 `Rotate` 或 `TranslateX`。\n\n基本步骤如下所示：\n\n1. 准备标准数据集\n2. 准备你的自定义模型\n3. 准备配置文件\n4. 在标准数据集上对模型进行训练、测试和推理\n\n## 准备标准数据集\n\n在本文中，我们使用 cityscapes 标准数据集为例进行说明。\n\n推荐将数据集根路径采用符号链接方式链接到 `$MMDETECTION/data`。\n\n如果你的文件结构不同，你可能需要在配置文件中进行相应的路径更改。标准的文件组织格式如下所示：\n\n```none\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   ├── cityscapes\n│   │   ├── annotations\n│   │   ├── leftImg8bit\n│   │   │   ├── train\n│   │   │   ├── val\n│   │   ├── gtFine\n│   │   │   ├── train\n│   │   │   ├── val\n│   ├── VOCdevkit\n│   │   ├── VOC2007\n│   │   ├── VOC2012\n```\n\n你也可以通过如下方式设定数据集根路径\n\n```bash\nexport MMDET_DATASETS=$data_root\n```\n\n我们将会使用环境便变量 `$MMDET_DATASETS` 作为数据集的根目录，因此你无需再修改相应配置文件的路径信息。\n\n你需要使用脚本 `tools/dataset_converters/cityscapes.py` 将 cityscapes 标注转化为 coco 标注格式。\n\n```shell\npip install cityscapesscripts\npython tools/dataset_converters/cityscapes.py ./data/cityscapes --nproc 8 --out-dir ./data/cityscapes/annotations\n```\n\n目前在 `cityscapes `文件夹中的配置文件所对应模型是采用 COCO 预训练权重进行初始化的。\n\n如果你的网络不可用或者比较慢，建议你先手动下载对应的预训练权重，否则可能在训练开始时候出现错误。\n\n## 准备你的自定义模型\n\n第二步是准备你的自定义模型或者训练相关配置。假设你想在已有的  Cascade Mask R-CNN R50 检测模型基础上，新增一个新的 neck 模块 `AugFPN` 去代替默认的 `FPN`，以下是具体实现：\n\n### 1 定义新的 neck (例如 AugFPN)\n\n首先创建新文件  `mmdet/models/necks/augfpn.py`.\n\n```python\nimport torch.nn as nn\nfrom mmdet.registry import MODELS\n\n@MODELS.register_module()\nclass AugFPN(nn.Module):\n\n    def __init__(self,\n                in_channels,\n                out_channels,\n                num_outs,\n                start_level=0,\n                end_level=-1,\n                add_extra_convs=False):\n        pass\n\n    def forward(self, inputs):\n        # implementation is ignored\n        pass\n```\n\n### 2 导入模块\n\n你可以采用两种方式导入模块，第一种是在  `mmdet/models/necks/__init__.py` 中添加如下内容\n\n```python\nfrom .augfpn import AugFPN\n```\n\n第二种是增加如下代码到对应配置中，这种方式的好处是不需要改动代码\n\n```python\ncustom_imports = dict(\n    imports=['mmdet.models.necks.augfpn'],\n    allow_failed_imports=False)\n```\n\n### 3 修改配置\n\n```python\nneck=dict(\n    type='AugFPN',\n    in_channels=[256, 512, 1024, 2048],\n    out_channels=256,\n    num_outs=5)\n```\n\n关于自定义模型其余相关细节例如实现新的骨架网络，头部网络、损失函数，以及运行时训练配置例如定义新的优化器、使用梯度裁剪、定制训练调度策略和钩子等，请参考文档 [自定义模型](tutorials/customize_models.md) 和 [自定义运行时训练配置](tutorials/customize_runtime.md)。\n\n## 准备配置文件\n\n第三步是准备训练配置所需要的配置文件。假设你打算基于 cityscapes 数据集，在 Cascade Mask R-CNN R50 中新增 `AugFPN` 模块，同时增加 `Rotate` 或者 `Translate` 数据增强策略，假设你的配置文件位于 `configs/cityscapes/` 目录下，并且取名为 `cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py`，则配置信息如下：\n\n```python\n# 继承 base 配置，然后进行针对性修改\n_base_ = [\n    '../_base_/models/cascade-mask-rcnn_r50_fpn.py',\n    '../_base_/datasets/cityscapes_instance.py', '../_base_/default_runtime.py'\n]\n\nmodel = dict(\n    # 设置 `init_cfg` 为 None，表示不加载 ImageNet 预训练权重，\n    # 后续可以设置 `load_from` 参数用来加载 COCO 预训练权重\n    backbone=dict(init_cfg=None),\n    # 使用新增的 `AugFPN` 模块代替默认的 `FPN`\n    neck=dict(\n        type='AugFPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    # 我们也需要将 num_classes 从 80 修改为 8 来匹配 cityscapes 数据集标注\n    # 这个修改包括 `bbox_head` 和 `mask_head`.\n    roi_head=dict(\n        bbox_head=[\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                # 将 COCO 类别修改为 cityscapes 类别\n                num_classes=8,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                # 将 COCO 类别修改为 cityscapes 类别\n                num_classes=8,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                # 将 COCO 类别修改为 cityscapes 类别\n                num_classes=8,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            # 将 COCO 类别修改为 cityscapes 类别\n            num_classes=8,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))))\n\n# 覆写 `train_pipeline`，然后新增 `AutoAugment` 训练配置\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='AutoAugment',\n        policies=[\n            [dict(\n                 type='Rotate',\n                 level=5,\n                 img_border_value=(124, 116, 104),\n                 prob=0.5)\n            ],\n            [dict(type='Rotate', level=7, img_border_value=(124, 116, 104)),\n             dict(\n                 type='TranslateX',\n                 level=5,\n                 prob=0.5,\n                 img_border_value=(124, 116, 104))\n            ],\n        ]),\n    dict(\n        type='RandomResize',\n        scale=[(2048, 800), (2048, 1024)],\n        keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs'),\n]\n\n# 设置每张显卡的批处理大小，同时设置新的训练 pipeline\ndata = dict(\n    samples_per_gpu=1,\n    workers_per_gpu=3,\n    train=dict(dataset=dict(pipeline=train_pipeline)))\n\n# 设置优化器\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))\n\n# 设置定制的学习率策略\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=10,\n        by_epoch=True,\n        milestones=[8],\n        gamma=0.1)\n]\n\n# 训练，验证，测试配置\ntrain_cfg = dict(type='EpochBasedTrainLoop', max_epochs=10, val_interval=1)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# 我们采用 COCO 预训练过的 Cascade Mask R-CNN R50 模型权重作为初始化权重，可以得到更加稳定的性能\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco/cascade_mask_rcnn_r50_fpn_1x_coco_20200203-9d4dcb24.pth'\n```\n\n## 训练新模型\n\n为了能够使用新增配置来训练模型，你可以运行如下命令：\n\n```shell\npython tools/train.py configs/cityscapes/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py\n```\n\n如果想了解更多用法，可以参考 [例子1](1_exist_data_model.md)。\n\n## 测试和推理\n\n为了能够测试训练好的模型，你可以运行如下命令：\n\n```shell\npython tools/test.py configs/cityscapes/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes.py work_dirs/cascade-mask-rcnn_r50_augfpn_autoaug-10e_cityscapes/epoch_10.pth\n```\n\n如果想了解更多用法，可以参考 [例子1](1_exist_data_model.md)。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/robustness_benchmarking.md",
    "content": "# 检测器鲁棒性检查\n\n## 介绍\n\n我们提供了在 [Benchmarking Robustness in Object Detection: Autonomous Driving when Winter is Coming](https://arxiv.org/abs/1907.07484) 中定义的「图像损坏基准测试」上测试目标检测和实例分割模型的工具。\n此页面提供了如何使用该基准测试的基本教程。\n\n```latex\n@article{michaelis2019winter,\n  title={Benchmarking Robustness in Object Detection:\n    Autonomous Driving when Winter is Coming},\n  author={Michaelis, Claudio and Mitzkus, Benjamin and\n    Geirhos, Robert and Rusak, Evgenia and\n    Bringmann, Oliver and Ecker, Alexander S. and\n    Bethge, Matthias and Brendel, Wieland},\n  journal={arXiv:1907.07484},\n  year={2019}\n}\n```\n\n![image corruption example](../../../resources/corruptions_sev_3.png)\n\n## 关于基准测试\n\n要将结果提交到基准测试，请访问[基准测试主页](https://github.com/bethgelab/robust-detection-benchmark)\n\n基准测试是仿照 [imagenet-c 基准测试](https://github.com/hendrycks/robustness)，由 Dan Hendrycks 和 Thomas Dietterich 在[Benchmarking Neural Network Robustness to Common Corruptions and Perturbations](https://arxiv.org/abs/1903.12261)(ICLR 2019)中发表。\n\n图像损坏变换功能包含在此库中，但可以使用以下方法单独安装：\n\n```shell\npip install imagecorruptions\n```\n\n与 imagenet-c 相比，我们必须进行一些更改以处理任意大小的图像和灰度图像。\n我们还修改了“运动模糊”和“雪”损坏，以解除对于 linux 特定库的依赖，\n否则必须单独安装这些库。有关详细信息，请参阅 [imagecorruptions](https://github.com/bethgelab/imagecorruptions)。\n\n## 使用预训练模型进行推理\n\n我们提供了一个测试脚本来评估模型在基准测试中提供的各种损坏变换组合下的性能。\n\n### 在数据集上测试\n\n- [x] 单张 GPU 测试\n- [ ] 多张 GPU 测试\n- [ ] 可视化检测结果\n\n您可以使用以下命令在基准测试中使用 15 种损坏变换来测试模型性能。\n\n```shell\n# single-gpu testing\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}]\n```\n\n也可以选择其它不同类型的损坏变换。\n\n```shell\n# noise\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --corruptions noise\n\n# blur\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --corruptions blur\n\n# wetaher\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --corruptions weather\n\n# digital\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --corruptions digital\n```\n\n或者使用一组自定义的损坏变换，例如：\n\n```shell\n# gaussian noise, zoom blur and snow\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --corruptions gaussian_noise zoom_blur snow\n```\n\n最后，我们也可以选择施加在图像上的损坏变换的严重程度。\n严重程度从 1 到 5 逐级增强，0 表示不对图像施加损坏变换，即原始图像数据。\n\n```shell\n# severity 1\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --severities 1\n\n# severities 0,2,4\npython tools/analysis_tools/test_robustness.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] --severities 0 2 4\n```\n\n## 模型测试结果\n\n下表是各模型在 COCO 2017val 上的测试结果。\n\n|        Model        |      Backbone       |  Style  | Lr schd | box AP clean | box AP corr. | box % | mask AP clean | mask AP corr. | mask % |\n| :-----------------: | :-----------------: | :-----: | :-----: | :----------: | :----------: | :---: | :-----------: | :-----------: | :----: |\n|    Faster R-CNN     |      R-50-FPN       | pytorch |   1x    |     36.3     |     18.2     | 50.2  |       -       |       -       |   -    |\n|    Faster R-CNN     |      R-101-FPN      | pytorch |   1x    |     38.5     |     20.9     | 54.2  |       -       |       -       |   -    |\n|    Faster R-CNN     |   X-101-32x4d-FPN   | pytorch |   1x    |     40.1     |     22.3     | 55.5  |       -       |       -       |   -    |\n|    Faster R-CNN     |   X-101-64x4d-FPN   | pytorch |   1x    |     41.3     |     23.4     | 56.6  |       -       |       -       |   -    |\n|    Faster R-CNN     |    R-50-FPN-DCN     | pytorch |   1x    |     40.0     |     22.4     | 56.1  |       -       |       -       |   -    |\n|    Faster R-CNN     | X-101-32x4d-FPN-DCN | pytorch |   1x    |     43.4     |     26.7     | 61.6  |       -       |       -       |   -    |\n|     Mask R-CNN      |      R-50-FPN       | pytorch |   1x    |     37.3     |     18.7     | 50.1  |     34.2      |     16.8      |  49.1  |\n|     Mask R-CNN      |    R-50-FPN-DCN     | pytorch |   1x    |     41.1     |     23.3     | 56.7  |     37.2      |     20.7      |  55.7  |\n|    Cascade R-CNN    |      R-50-FPN       | pytorch |   1x    |     40.4     |     20.1     | 49.7  |       -       |       -       |   -    |\n| Cascade Mask R-CNN  |      R-50-FPN       | pytorch |   1x    |     41.2     |     20.7     | 50.2  |     35.7      |     17.6      |  49.3  |\n|      RetinaNet      |      R-50-FPN       | pytorch |   1x    |     35.6     |     17.8     | 50.1  |       -       |       -       |   -    |\n| Hybrid Task Cascade | X-101-64x4d-FPN-DCN | pytorch |   1x    |     50.6     |     32.7     | 64.7  |     43.8      |     28.1      |  64.0  |\n\n由于对图像的损坏变换存在随机性，测试结果可能略有不同。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/semi_det.md",
    "content": "# 半监督目标检测\n\n半监督目标检测同时利用标签数据和无标签数据进行训练，一方面可以减少模型对检测框数量的依赖，另一方面也可以利用大量的未标记数据进一步提高模型。\n\n按照以下流程进行半监督目标检测：\n\n- [半监督目标检测](#半监督目标检测)\n  - [准备和拆分数据集](#准备和拆分数据集)\n  - [配置多分支数据流程](#配置多分支数据流程)\n  - [配置半监督数据加载](#配置半监督数据加载)\n  - [配置半监督模型](#配置半监督模型)\n  - [配置MeanTeacherHook](#配置meanteacherhook)\n  - [配置TeacherStudentValLoop](#配置teacherstudentvalloop)\n\n## 准备和拆分数据集\n\n我们提供了数据集下载脚本，默认下载 coco2017 数据集，并且自动解压。\n\n```shell\npython tools/misc/download_dataset.py\n```\n\n解压后的数据集目录如下：\n\n```plain\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── image_info_unlabeled2017.json\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── test2017\n│   │   ├── train2017\n│   │   ├── unlabeled2017\n│   │   ├── val2017\n```\n\n半监督目标检测在 coco 数据集上有两种比较通用的实验设置：\n\n（1）将 `train2017` 按照固定百分比（1%，2%，5% 和 10%）划分出一部分数据作为标签数据集，剩余的训练集数据作为无标签数据集，同时考虑划分不同的训练集数据作为标签数据集对半监督训练的结果影响较大，所以采用五折交叉验证来评估算法性能。我们提供了数据集划分脚本：\n\n```shell\npython tools/misc/split_coco.py\n```\n\n该脚本默认会按照 1%，2%，5% 和 10% 的标签数据占比划分 `train2017`，每一种划分会随机重复 5 次，用于交叉验证。生成的半监督标注文件名称格式如下：\n\n- 标签数据集标注名称格式：`instances_train2017.{fold}@{percent}.json`\n\n- 无标签数据集名称标注：`instances_train2017.{fold}@{percent}-unlabeled.json`\n\n其中，`fold` 用于交叉验证，`percent` 表示标签数据的占比。 划分后的数据集目录结构如下：\n\n```plain\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── image_info_unlabeled2017.json\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── semi_anns\n│   │   │   ├── instances_train2017.1@1.json\n│   │   │   ├── instances_train2017.1@1-unlabeled.json\n│   │   │   ├── instances_train2017.1@2.json\n│   │   │   ├── instances_train2017.1@2-unlabeled.json\n│   │   │   ├── instances_train2017.1@5.json\n│   │   │   ├── instances_train2017.1@5-unlabeled.json\n│   │   │   ├── instances_train2017.1@10.json\n│   │   │   ├── instances_train2017.1@10-unlabeled.json\n│   │   │   ├── instances_train2017.2@1.json\n│   │   │   ├── instances_train2017.2@1-unlabeled.json\n│   │   ├── test2017\n│   │   ├── train2017\n│   │   ├── unlabeled2017\n│   │   ├── val2017\n```\n\n（2）将 `train2017` 作为标签数据集，`unlabeled2017` 作为无标签数据集。由于 `image_info_unlabeled2017.json` 没有 `categories` 信息，无法初始化 `CocoDataset` ，所以需要将 `instances_train2017.json` 的 `categories` 写入 `image_info_unlabeled2017.json` ，另存为 `instances_unlabeled2017.json`，相关脚本如下：\n\n```python\nfrom mmengine.fileio import load, dump\n\nanns_train = load('instances_train2017.json')\nanns_unlabeled = load('image_info_unlabeled2017.json')\nanns_unlabeled['categories'] = anns_train['categories']\ndump(anns_unlabeled, 'instances_unlabeled2017.json')\n```\n\n处理后的数据集目录如下：\n\n```plain\nmmdetection\n├── data\n│   ├── coco\n│   │   ├── annotations\n│   │   │   ├── image_info_unlabeled2017.json\n│   │   │   ├── instances_train2017.json\n│   │   │   ├── instances_unlabeled2017.json\n│   │   │   ├── instances_val2017.json\n│   │   ├── test2017\n│   │   ├── train2017\n│   │   ├── unlabeled2017\n│   │   ├── val2017\n```\n\n## 配置多分支数据流程\n\n半监督学习有两个主要的方法，分别是\n[一致性正则化](https://research.nvidia.com/sites/default/files/publications/laine2017iclr_paper.pdf)\n和[伪标签](https://www.researchgate.net/profile/Dong-Hyun-Lee/publication/280581078_Pseudo-Label_The_Simple_and_Efficient_Semi-Supervised_Learning_Method_for_Deep_Neural_Networks/links/55bc4ada08ae092e9660b776/Pseudo-Label-The-Simple-and-Efficient-Semi-Supervised-Learning-Method-for-Deep-Neural-Networks.pdf) 。\n一致性正则化往往需要一些精心的设计，而伪标签的形式比较简单，更容易拓展到下游任务。我们主要采用了基于伪标签的教师学生联合训练的半监督目标检测框架，对于标签数据和无标签数据需要配置不同的数据流程：\n（1）标签数据的数据流程：\n\n```python\n# pipeline used to augment labeled data,\n# which will be sent to student model for supervised training.\nsup_pipeline = [\n    dict(type='LoadImageFromFile',backend_args = backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='RandAugment', aug_space=color_space, aug_num=1),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='MultiBranch', sup=dict(type='PackDetInputs'))\n]\n```\n\n（2）无标签的数据流程：\n\n```python\n# pipeline used to augment unlabeled data weakly,\n# which will be sent to teacher model for predicting pseudo instances.\nweak_pipeline = [\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction',\n                   'homography_matrix')),\n]\n\n# pipeline used to augment unlabeled data strongly,\n# which will be sent to student model for unsupervised training.\nstrong_pipeline = [\n    dict(type='RandomResize', scale=scale, keep_ratio=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomOrder',\n        transforms=[\n            dict(type='RandAugment', aug_space=color_space, aug_num=1),\n            dict(type='RandAugment', aug_space=geometric, aug_num=1),\n        ]),\n    dict(type='RandomErasing', n_patches=(1, 5), ratio=(0, 0.2)),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'flip', 'flip_direction',\n                   'homography_matrix')),\n]\n\n# pipeline used to augment unlabeled data into different views\nunsup_pipeline = [\n    dict(type='LoadImageFromFile', backend_args = backend_args),\n    dict(type='LoadEmptyAnnotations'),\n    dict(\n        type='MultiBranch',\n        unsup_teacher=weak_pipeline,\n        unsup_student=strong_pipeline,\n    )\n]\n```\n\n## 配置半监督数据加载\n\n（1）构建半监督数据集。使用 `ConcatDataset` 拼接标签数据集和无标签数据集。\n\n```python\nlabeled_dataset = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='annotations/instances_train2017.json',\n    data_prefix=dict(img='train2017/'),\n    filter_cfg=dict(filter_empty_gt=True, min_size=32),\n    pipeline=sup_pipeline)\n\nunlabeled_dataset = dict(\n    type=dataset_type,\n    data_root=data_root,\n    ann_file='annotations/instances_unlabeled2017.json',\n    data_prefix=dict(img='unlabeled2017/'),\n    filter_cfg=dict(filter_empty_gt=False),\n    pipeline=unsup_pipeline)\n\ntrain_dataloader = dict(\n    batch_size=batch_size,\n    num_workers=num_workers,\n    persistent_workers=True,\n    sampler=dict(\n        type='GroupMultiSourceSampler',\n        batch_size=batch_size,\n        source_ratio=[1, 4]),\n    dataset=dict(\n        type='ConcatDataset', datasets=[labeled_dataset, unlabeled_dataset]))\n```\n\n（2）使用多源数据集采样器。 使用 `GroupMultiSourceSampler` 从 `labeled_dataset` 和 `labeled_dataset` 采样数据组成 batch , `source_ratio` 控制 batch 中标签数据和无标签数据的占比。`GroupMultiSourceSampler` 还保证了同一个 batch 中的图片具有相近的长宽比例，如果不需要保证batch内图片的长宽比例，可以使用 `MultiSourceSampler`。`GroupMultiSourceSampler` 采样示意图如下：\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/40661020/186149261-8cf28e92-de5c-4c8c-96e1-13558b2e27f7.jpg\"/>\n</div>\n\n`sup=1000` 表示标签数据集的规模为 1000 ，`sup_h=200` 表示标签数据集中长宽比大于等于1的图片规模为 200，`sup_w=800` 表示标签数据集中长宽比小于1的图片规模为 800 ，`unsup=9000` 表示无标签数据集的规模为 9000 ，`unsup_h=1800` 表示无标签数据集中长宽比大于等于1的图片规模为 1800，`unsup_w=7200` 表示标签数据集中长宽比小于1的图片规模为 7200 ，`GroupMultiSourceSampler` 每次按照标签数据集和无标签数据集的图片的总体长宽比分布随机选择一组，然后按照 `source_ratio` 从两个数据集中采样组成 batch ，因此标签数据集和无标签数据集重复采样次数不同。\n\n## 配置半监督模型\n\n我们选择 `Faster R-CNN` 作为 `detector` 进行半监督训练，以半监督目标检测算法 `SoftTeacher` 为例，模型的配置可以继承 `_base_/models/faster-rcnn_r50_fpn.py`，将检测器的骨干网络替换成 `caffe` 风格。\n注意，与监督训练的配置文件不同的是，`Faster R-CNN` 作为 `detector`，是作为 `model`的一个属性，而不是 `model` 。此外，还需要将`data_preprocessor`设置为`MultiBranchDataPreprocessor`，用于处理不同数据流程图片的填充和归一化。\n最后，可以通过 `semi_train_cfg` 和 `semi_test_cfg` 配置半监督训练和测试需要的参数。\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/semi_coco_detection.py'\n]\n\ndetector = _base_.model\ndetector.data_preprocessor = dict(\n    type='DetDataPreprocessor',\n    mean=[103.530, 116.280, 123.675],\n    std=[1.0, 1.0, 1.0],\n    bgr_to_rgb=False,\n    pad_size_divisor=32)\ndetector.backbone = dict(\n    type='ResNet',\n    depth=50,\n    num_stages=4,\n    out_indices=(0, 1, 2, 3),\n    frozen_stages=1,\n    norm_cfg=dict(type='BN', requires_grad=False),\n    norm_eval=True,\n    style='caffe',\n    init_cfg=dict(\n        type='Pretrained',\n        checkpoint='open-mmlab://detectron2/resnet50_caffe'))\n\nmodel = dict(\n    _delete_=True,\n    type='SoftTeacher',\n    detector=detector,\n    data_preprocessor=dict(\n        type='MultiBranchDataPreprocessor',\n        data_preprocessor=detector.data_preprocessor),\n    semi_train_cfg=dict(\n        freeze_teacher=True,\n        sup_weight=1.0,\n        unsup_weight=4.0,\n        pseudo_label_initial_score_thr=0.5,\n        rpn_pseudo_thr=0.9,\n        cls_pseudo_thr=0.9,\n        reg_pseudo_thr=0.02,\n        jitter_times=10,\n        jitter_scale=0.06,\n        min_pseudo_bbox_wh=(1e-2, 1e-2)),\n    semi_test_cfg=dict(predict_on='teacher'))\n```\n\n此外，我们也支持其他检测模型进行半监督训练，比如，`RetinaNet` 和 `Cascade R-CNN`。由于 `SoftTeacher` 仅支持 `Faster R-CNN`，所以需要将其替换为 `SemiBaseDetector`，示例如下：\n\n```python\n_base_ = [\n    '../_base_/models/retinanet_r50_fpn.py', '../_base_/default_runtime.py',\n    '../_base_/datasets/semi_coco_detection.py'\n]\n\ndetector = _base_.model\n\nmodel = dict(\n    _delete_=True,\n    type='SemiBaseDetector',\n    detector=detector,\n    data_preprocessor=dict(\n        type='MultiBranchDataPreprocessor',\n        data_preprocessor=detector.data_preprocessor),\n    semi_train_cfg=dict(\n        freeze_teacher=True,\n        sup_weight=1.0,\n        unsup_weight=1.0,\n        cls_pseudo_thr=0.9,\n        min_pseudo_bbox_wh=(1e-2, 1e-2)),\n    semi_test_cfg=dict(predict_on='teacher'))\n```\n\n沿用 `SoftTeacher` 的半监督训练配置，将 `batch_size` 改为 2 ，`source_ratio` 改为 `[1, 1]`，`RetinaNet`，`Faster R-CNN`， `Cascade R-CNN` 以及 `SoftTeacher` 在 10% coco 训练集上的监督训练和半监督训练的实验结果如下：\n\n|      Model       |   Detector    | BackBone | Style | sup-0.1-coco mAP | semi-0.1-coco mAP |\n| :--------------: | :-----------: | :------: | :---: | :--------------: | :---------------: |\n| SemiBaseDetector |   RetinaNet   | R-50-FPN | caffe |       23.5       |       27.7        |\n| SemiBaseDetector | Faster R-CNN  | R-50-FPN | caffe |       26.7       |       28.4        |\n| SemiBaseDetector | Cascade R-CNN | R-50-FPN | caffe |       28.0       |       29.7        |\n|   SoftTeacher    | Faster R-CNN  | R-50-FPN | caffe |       26.7       |       31.1        |\n\n## 配置MeanTeacherHook\n\n通常，教师模型采用对学生模型指数滑动平均（EMA）的方式进行更新，进而教师模型随着学生模型的优化而优化，可以通过配置 `custom_hooks` 实现：\n\n```python\ncustom_hooks = [dict(type='MeanTeacherHook')]\n```\n\n## 配置TeacherStudentValLoop\n\n由于教师学生联合训练框架存在两个模型，我们可以用 `TeacherStudentValLoop` 替换 `ValLoop`，在训练的过程中同时检验两个模型的精度。\n\n```python\nval_cfg = dict(type='TeacherStudentValLoop')\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/single_stage_as_rpn.md",
    "content": "# 将单阶段检测器作为 RPN\n\n候选区域网络 (Region Proposal Network, RPN) 作为 [Faster R-CNN](https://arxiv.org/abs/1506.01497) 的一个子模块，将为 Faster R-CNN 的第二阶段产生候选区域。在 MMDetection 里大多数的二阶段检测器使用 [`RPNHead`](../../../mmdet/models/dense_heads/rpn_head.py)作为候选区域网络来产生候选区域。然而，任何的单阶段检测器都可以作为候选区域网络，是因为他们对边界框的预测可以被视为是一种候选区域，并且因此能够在 R-CNN 中得到改进。因此在 MMDetection v3.0 中会支持将单阶段检测器作为 RPN 使用。\n\n接下来我们通过一个例子，即如何在 [Faster R-CNN](../../../configs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py) 中使用一个无锚框的单阶段的检测器模型 [FCOS](../../../configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py) 作为 RPN ，详细阐述具体的全部流程。\n\n主要流程如下:\n\n1. 在 Faster R-CNN 中使用 `FCOSHead` 作为 `RPNHead`\n2. 评估候选区域\n3. 用预先训练的 FCOS 训练定制的 Faster R-CNN\n\n## 在 Faster R-CNN 中使用 `FCOSHead` 作为` RPNHead`\n\n为了在 Faster R-CNN 中使用 `FCOSHead` 作为 `RPNHead` ，我们应该创建一个名为 `configs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py` 的配置文件，并且在 `configs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py` 中将 `rpn_head` 的设置替换为 `bbox_head` 的设置，此外我们仍然使用 FCOS 的瓶颈设置，步幅为`[8,16,32,64,128]`，并且更新 `bbox_roi_extractor` 的 `featmap_stride` 为 ` [8,16,32,64,128]`。为了避免损失变慢，我们在前1000次迭代而不是前500次迭代中应用预热，这意味着 lr 增长得更慢。相关配置如下:\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    # 从 configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py 复制\n    neck=dict(\n        start_level=1,\n        add_extra_convs='on_output',  # 使用 P5\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        _delete_=True,  # 忽略未使用的旧设置\n        type='FCOSHead',\n        num_classes=1,  # 对于 rpn, num_classes = 1，如果 num_classes > 1，它将在 TwoStageDetector 中自动设置为1\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    roi_head=dict(  # featmap_strides 的更新取决于于颈部的步伐\n        bbox_roi_extractor=dict(featmap_strides=[8, 16, 32, 64, 128])))\n# 学习率\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),  # 慢慢增加 lr，否则损失变成 NAN\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n```\n\n然后，我们可以使用下面的命令来训练我们的定制模型。更多训练命令，请参考[这里](train.md)。\n\n```python\n# 使用8个 GPU 进行训练\nbash\ntools/dist_train.sh\nconfigs/faster_rcnn/faster-rcnn_r50_fpn_fcos-rpn_1x_coco.py\n--work-dir  /work_dirs/faster-rcnn_r50_fpn_fcos-rpn_1x_coco\n```\n\n## 评估候选区域\n\n候选区域的质量对检测器的性能有重要影响，因此，我们也提供了一种评估候选区域的方法。和上面一样创建一个新的名为 `configs/rpn/fcos-rpn_r50_fpn_1x_coco.py` 的配置文件，并且在 `configs/rpn/fcos-rpn_r50_fpn_1x_coco.py` 中将 `rpn_head` 的设置替换为 `bbox_head` 的设置。\n\n```python\n_base_ = [\n    '../_base_/models/rpn_r50_fpn.py', '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nval_evaluator = dict(metric='proposal_fast')\ntest_evaluator = val_evaluator\nmodel = dict(\n    # 从 configs/fcos/fcos_r50-caffe_fpn_gn-head_1x_coco.py 复制\n    neck=dict(\n        start_level=1,\n        add_extra_convs='on_output',  # 使用 P5\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        _delete_=True,  # 忽略未使用的旧设置\n        type='FCOSHead',\n        num_classes=1,  # 对于 rpn, num_classes = 1，如果 num_classes >为1，它将在 rpn 中自动设置为1\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)))\n```\n\n假设我们在训练之后有检查点 `./work_dirs/faster-rcnn_r50_fpn_fcos-rpn_1x_coco/epoch_12.pth` ，然后，我们可以使用下面的命令来评估建议的质量。\n\n```python\n# 使用8个 GPU 进行测试\nbash\ntools/dist_test.sh\nconfigs/rpn/fcos-rpn_r50_fpn_1x_coco.py\n--work_dirs /faster-rcnn_r50_fpn_fcos-rpn_1x_coco/epoch_12.pth\n```\n\n## 用预先训练的 FCOS 训练定制的 Faster R-CNN\n\n预训练不仅加快了训练的收敛速度，而且提高了检测器的性能。因此，我们在这里给出一个例子来说明如何使用预先训练的 FCOS 作为 RPN 来加速训练和提高精度。假设我们想在 Faster R-CNN 中使用 `FCOSHead` 作为 `rpn_head`，并加载预先训练权重来进行训练 [`fcos_r50-caffe_fpn_gn-head_1x_coco`](https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_1x_coco/fcos_r50_caffe_fpn_gn-head_1x_coco-821213aa.pth)。 配置文件 `configs/faster_rcnn/faster-rcnn_r50-caffe_fpn_fcos- rpn_1x_copy .py` 的内容如下所示。注意，`fcos_r50-caffe_fpn_gn-head_1x_coco` 使用 ResNet50 的 caffe 版本，因此需要更新 `data_preprocessor` 中的像素平均值和 std。\n\n```python\n_base_ = [\n    '../_base_/models/faster-rcnn_r50_fpn.py',\n    '../_base_/datasets/coco_detection.py',\n    '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'\n]\nmodel = dict(\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(type='BN', requires_grad=False),\n        style='caffe',\n        init_cfg=None),  # the checkpoint in ``load_from`` contains the weights of backbone\n    neck=dict(\n        start_level=1,\n        add_extra_convs='on_output',  # 使用 P5\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        _delete_=True,  # 忽略未使用的旧设置\n        type='FCOSHead',\n        num_classes=1,  # 对于 rpn, num_classes = 1，如果 num_classes > 1，它将在 TwoStageDetector 中自动设置为1\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='IoULoss', loss_weight=1.0),\n        loss_centerness=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)),\n    roi_head=dict(  # update featmap_strides due to the strides in neck\n        bbox_roi_extractor=dict(featmap_strides=[8, 16, 32, 64, 128])))\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/fcos/fcos_r50_caffe_fpn_gn-head_1x_coco/fcos_r50_caffe_fpn_gn-head_1x_coco-821213aa.pth'\n```\n\n训练命令如下。\n\n```python\nbash\ntools/dist_train.sh\nconfigs/faster_rcnn/faster-rcnn_r50-caffe_fpn_fcos-rpn_1x_coco.py  \\\n--work-dir /work_dirs/faster-rcnn_r50-caffe_fpn_fcos-rpn_1x_coco\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/test.md",
    "content": "# 测试现有模型\n\n我们提供了测试脚本，能够测试一个现有模型在所有数据集（COCO，Pascal VOC，Cityscapes 等）上的性能。我们支持在如下环境下测试：\n\n- 单 GPU 测试\n- CPU 测试\n- 单节点多 GPU 测试\n- 多节点测试\n\n根据以上测试环境，选择合适的脚本来执行测试过程。\n\n```shell\n# 单 GPU 测试\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--show]\n\n# CPU 测试：禁用 GPU 并运行单 GPU 测试脚本\nexport CUDA_VISIBLE_DEVICES=-1\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--show]\n\n# 单节点多 GPU 测试\nbash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    ${GPU_NUM} \\\n    [--out ${RESULT_FILE}]\n```\n\n`tools/dist_test.sh` 也支持多节点测试，不过需要依赖 PyTorch 的 [启动工具](https://pytorch.org/docs/stable/distributed.html#launch-utility) 。\n\n可选参数：\n\n- `RESULT_FILE`: 结果文件名称，需以 .pkl 形式存储。如果没有声明，则不将结果存储到文件。\n- `--show`: 如果开启，检测结果将被绘制在图像上，以一个新窗口的形式展示。它只适用于单 GPU 的测试，是用于调试和可视化的。请确保使用此功能时，你的 GUI 可以在环境中打开。否则，你可能会遇到这么一个错误 `cannot connect to X server`。\n- `--show-dir`: 如果指明，检测结果将会被绘制在图像上并保存到指定目录。它只适用于单 GPU 的测试，是用于调试和可视化的。即使你的环境中没有 GUI，这个选项也可使用。\n- `--cfg-options`:  如果指明，这里的键值对将会被合并到配置文件中。\n\n### 样例\n\n假设你已经下载了 checkpoint 文件到 `checkpoints/` 文件下了。\n\n1. 测试 RTMDet 并可视化其结果。按任意键继续下张图片的测试。配置文件和 checkpoint 文件 [在此](https://github.com/open-mmlab/mmdetection/tree/main/configs/rtmdet) 。\n\n   ```shell\n   python tools/test.py \\\n       configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n       checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n       --show\n   ```\n\n2. 测试 RTMDet，并为了之后的可视化保存绘制的图像。配置文件和 checkpoint 文件 [在此](https://github.com/open-mmlab/mmdetection/tree/main/configs/rtmdet) 。\n\n   ```shell\n   python tools/test.py \\\n       configs/rtmdet/rtmdet_l_8xb32-300e_coco.py \\\n       checkpoints/rtmdet_l_8xb32-300e_coco_20220719_112030-5a0be7c4.pth \\\n       --show-dir rtmdet_l_8xb32-300e_coco_results\n   ```\n\n3. 在 Pascal VOC 数据集上测试 Faster R-CNN，不保存测试结果，测试 `mAP`。配置文件和 checkpoint 文件 [在此](../../../configs/pascal_voc) 。\n\n   ```shell\n   python tools/test.py \\\n       configs/pascal_voc/faster-rcnn_r50_fpn_1x_voc0712.py \\\n       checkpoints/faster_rcnn_r50_fpn_1x_voc0712_20200624-c9895d40.pth\n   ```\n\n4. 使用 8 块 GPU 测试 Mask R-CNN，测试 `bbox` 和 `mAP` 。配置文件和 checkpoint 文件 [在此](../../../configs/mask_rcnn) 。\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/mask-rcnn_r50_fpn_1x_coco.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n       8 \\\n       --out results.pkl\n   ```\n\n5. 使用 8 块 GPU 测试 Mask R-CNN，测试**每类**的 `bbox` 和 `mAP`。配置文件和 checkpoint 文件 [在此](../../../configs/mask_rcnn) 。\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n       8\n   ```\n\n   该命令生成两个JSON文件 `./work_dirs/coco_instance/test.bbox.json` 和 `./work_dirs/coco_instance/test.segm.json`。\n\n6. 在 COCO test-dev 数据集上，使用 8 块 GPU 测试 Mask R-CNN，并生成 JSON 文件提交到官方评测服务器，配置文件和 checkpoint 文件 [在此](../../../configs/mask_rcnnn) 。你可以在 [config](./././configs/_base_/datasets/coco_instance.py) 的注释中用 test_evaluator 和 test_dataloader 替换原来的 test_evaluator 和 test_dataloader，然后运行：\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/cityscapes/mask-rcnn_r50_fpn_1x_cityscapes.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_cityscapes_20200227-afe51d5a.pth \\\n       8\n   ```\n\n   这行命令生成两个 JSON 文件 `mask_rcnn_test-dev_results.bbox.json` 和 `mask_rcnn_test-dev_results.segm.json`。\n\n7. 在 Cityscapes 数据集上，使用 8 块 GPU 测试 Mask R-CNN，生成 txt 和 png 文件，并上传到官方评测服务器。配置文件和 checkpoint 文件 [在此](../../../configs/cityscapes) 。 你可以在 [config](./././configs/_base_/datasets/cityscapes_instance.py) 的注释中用 test_evaluator 和 test_dataloader 替换原来的 test_evaluator 和 test_dataloader，然后运行：\n\n   ```shell\n   ./tools/dist_test.sh \\\n       configs/cityscapes/mask-rcnn_r50_fpn_1x_cityscapes.py \\\n       checkpoints/mask_rcnn_r50_fpn_1x_cityscapes_20200227-afe51d5a.pth \\\n       8\n   ```\n\n   生成的 png 和 txt 文件在 `./work_dirs/cityscapes_metric` 文件夹下。\n\n### 不使用 Ground Truth 标注进行测试\n\nMMDetection 支持在不使用 ground-truth 标注的情况下对模型进行测试，这需要用到 `CocoDataset`。如果你的数据集格式不是 COCO 格式的，请将其转化成 COCO 格式。如果你的数据集格式是 VOC 或者 Cityscapes，你可以使用 [tools/dataset_converters](https://github.com/open-mmlab/mmdetection/tree/main/tools/dataset_converters) 内的脚本直接将其转化成 COCO 格式。如果是其他格式，可以使用 [images2coco 脚本](https://github.com/open-mmlab/mmdetection/tree/master/tools/dataset_converters/images2coco.py) 进行转换。\n\n```shell\npython tools/dataset_converters/images2coco.py \\\n    ${IMG_PATH} \\\n    ${CLASSES} \\\n    ${OUT} \\\n    [--exclude-extensions]\n```\n\n参数：\n\n- `IMG_PATH`: 图片根路径。\n- `CLASSES`: 类列表文本文件名。文本中每一行存储一个类别。\n- `OUT`: 输出 json 文件名。 默认保存目录和 `IMG_PATH` 在同一级。\n- `exclude-extensions`: 待排除的文件后缀名。\n\n在转换完成后，使用如下命令进行测试\n\n```shell\n# 单 GPU 测试\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--show]\n\n# CPU 测试：禁用 GPU 并运行单 GPU 测试脚本\nexport CUDA_VISIBLE_DEVICES=-1\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--show]\n\n# 单节点多 GPU 测试\nbash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    ${GPU_NUM} \\\n    [--show]\n```\n\n假设 [model zoo](https://mmdetection.readthedocs.io/en/latest/modelzoo_statistics.html) 中的 checkpoint 文件被下载到了 `checkpoints/` 文件夹下，\n我们可以使用以下命令，用 8 块 GPU 在 COCO test-dev 数据集上测试 Mask R-CNN，并且生成 JSON 文件。\n\n```sh\n./tools/dist_test.sh \\\n    configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py \\\n    checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n    8\n```\n\n这行命令生成两个 JSON 文件 `./work_dirs/coco_instance/test.bbox.json` 和 `./work_dirs/coco_instance/test.segm.json`。\n\n### 批量推理\n\nMMDetection 在测试模式下，既支持单张图片的推理，也支持对图像进行批量推理。默认情况下，我们使用单张图片的测试，你可以通过修改测试数据配置文件中的 `samples_per_gpu` 来开启批量测试。\n开启批量推理的配置文件修改方法为：\n\n```shell\ndata = dict(train_dataloader=dict(...), val_dataloader=dict(...), test_dataloader=dict(batch_size=2, ...))\n```\n\n或者你可以通过将 `--cfg-options` 设置为 `--cfg-options test_dataloader.batch_size=` 来开启它。\n\n## 测试时增强 (TTA)\n\n测试时增强 (TTA) 是一种在测试阶段使用的数据增强策略。它对同一张图片应用不同的增强，例如翻转和缩放，用于模型推理，然后将每个增强后的图像的预测结果合并，以获得更准确的预测结果。为了让用户更容易使用 TTA，MMEngine 提供了 [BaseTTAModel](https://mmengine.readthedocs.io/en/latest/api/generated/mmengine.model.BaseTTAModel.html#mmengine.model.BaseTTAModel) 类，允许用户根据自己的需求通过简单地扩展 BaseTTAModel 类来实现不同的 TTA 策略。\n\n在 MMDetection 中，我们提供了 [DetTTAModel](../../../mmdet/models/test_time_augs/det_tta.py) 类，它继承自 BaseTTAModel。\n\n### 使用案例\n\n使用 TTA 需要两个步骤。首先，你需要在配置文件中添加 `tta_model` 和 `tta_pipeline`：\n\n```shell\ntta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(\n                   type='nms',\n                   iou_threshold=0.5),\n                   max_per_img=100))\n\ntta_pipeline = [\n    dict(type='LoadImageFromFile',\n        backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[[\n            dict(type='Resize', scale=(1333, 800), keep_ratio=True)\n        ], [ # It uses 2 flipping transformations (flipping and not flipping).\n            dict(type='RandomFlip', prob=1.),\n            dict(type='RandomFlip', prob=0.)\n        ], [\n            dict(\n               type='PackDetInputs',\n               meta_keys=('img_id', 'img_path', 'ori_shape',\n                       'img_shape', 'scale_factor', 'flip',\n                       'flip_direction'))\n       ]])]\n```\n\n第二步，运行测试脚本时，设置 `--tta` 参数，如下所示：\n\n```shell\n# 单 GPU 测试\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--tta]\n\n# CPU 测试：禁用 GPU 并运行单 GPU 测试脚本\nexport CUDA_VISIBLE_DEVICES=-1\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    [--out ${RESULT_FILE}] \\\n    [--tta]\n\n# 多 GPU 测试\nbash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    ${GPU_NUM} \\\n    [--tta]\n```\n\n你也可以自己修改 TTA 配置，例如添加缩放增强：\n\n```shell\ntta_model = dict(\n    type='DetTTAModel',\n    tta_cfg=dict(nms=dict(\n                   type='nms',\n                   iou_threshold=0.5),\n                   max_per_img=100))\n\nimg_scales = [(1333, 800), (666, 400), (2000, 1200)]\ntta_pipeline = [\n    dict(type='LoadImageFromFile',\n         backend_args=None),\n    dict(\n        type='TestTimeAug',\n        transforms=[[\n            dict(type='Resize', scale=s, keep_ratio=True) for s in img_scales\n        ], [\n            dict(type='RandomFlip', prob=1.),\n            dict(type='RandomFlip', prob=0.)\n        ], [\n            dict(\n               type='PackDetInputs',\n               meta_keys=('img_id', 'img_path', 'ori_shape',\n                       'img_shape', 'scale_factor', 'flip',\n                       'flip_direction'))\n       ]])]\n```\n\n以上数据增强管道将首先对图像执行 3 个多尺度转换，然后执行 2 个翻转转换（翻转和不翻转），最后使用 PackDetInputs 将图像打包到最终结果中。\n这里有更多的 TTA 使用案例供您参考：\n\n- [RetinaNet](../../../configs/retinanet/retinanet_tta.py)\n- [CenterNet](../../../configs/centernet/centernet_tta.py)\n- [YOLOX](../../../configs/rtmdet/rtmdet_tta.py)\n- [RTMDet](../../../configs/yolox/yolox_tta.py)\n\n更多高级用法和 TTA 的数据流，请参考 [MMEngine](https://mmengine.readthedocs.io/en/latest/advanced_tutorials/test_time_augmentation.html#data-flow)。我们将在后续支持实例分割 TTA。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/test_results_submission.md",
    "content": "# 提交测试结果\n\n## 全景分割测试结果提交\n\n下面几节介绍如何在 COCO 测试开发集上生成泛视分割模型的预测结果，并将预测提交到 [COCO评估服务器](https://competitions.codalab.org/competitions/19507)\n\n### 前提条件\n\n- 下载 [COCO测试数据集图像](http://images.cocodataset.org/zips/test2017.zip)，[测试图像信息](http://images.cocodataset.org/annotations/image_info_test2017.zip)，和[全景训练/相关注释](http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip)，然后解压缩它们，把 `test2017` 放到 `data/coco/`，把 json 文件和注释文件放到 `data/coco/annotations/` 。\n\n```shell\n# 假设 data/coco/ 不存在\nmkdir -pv data/coco/\n# 下载 test2017\nwget -P data/coco/ http://images.cocodataset.org/zips/test2017.zip\nwget -P data/coco/ http://images.cocodataset.org/annotations/image_info_test2017.zip\nwget -P data/coco/ http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip\n# 解压缩它们\nunzip data/coco/test2017.zip -d data/coco/\nunzip data/coco/image_info_test2017.zip -d data/coco/\nunzip data/coco/panoptic_annotations_trainval2017.zip -d data/coco/\n# 删除 zip 文件(可选)\nrm -rf data/coco/test2017.zip data/coco/image_info_test2017.zip data/coco/panoptic_annotations_trainval2017.zip\n```\n\n- 运行以下代码更新测试图像信息中的类别信息。由于 `image_info_test-dev2017.json` 的类别信息中缺少属性 `isthing` ，我们需要用 `panoptic_val2017.json` 中的类别信息更新它。\n\n```shell\npython tools/misc/gen_coco_panoptic_test_info.py data/coco/annotations\n```\n\n在完成上述准备之后，你的 `data` 目录结构应该是这样:\n\n```text\ndata\n`-- coco\n    |-- annotations\n    |   |-- image_info_test-dev2017.json\n    |   |-- image_info_test2017.json\n    |   |-- panoptic_image_info_test-dev2017.json\n    |   |-- panoptic_train2017.json\n    |   |-- panoptic_train2017.zip\n    |   |-- panoptic_val2017.json\n    |   `-- panoptic_val2017.zip\n    `-- test2017\n```\n\n### coco 测试开发的推理\n\n要在 coco test-dev 上进行推断，我们应该首先更新 `test_dataloder` 和 `test_evaluator` 的设置。有两种方法可以做到这一点:1. 在配置文件中更新它们;2. 在命令行中更新它们。\n\n#### 在配置文件中更新它们\n\n相关的设置在 `configs/_base_/datasets/ coco_panoptical .py` 的末尾，如下所示。\n\n```python\ntest_dataloader = dict(\n    batch_size=1,\n    num_workers=1,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/panoptic_image_info_test-dev2017.json',\n        data_prefix=dict(img='test2017/'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_evaluator = dict(\n    type='CocoPanopticMetric',\n    format_only=True,\n    ann_file=data_root + 'annotations/panoptic_image_info_test-dev2017.json',\n    outfile_prefix='./work_dirs/coco_panoptic/test')\n```\n\n以下任何一种方法都可以用于更新 coco test-dev 集上的推理设置\n\n情况1:直接取消注释 `configs/_base_/datasets/ coco_panoptical .py` 中的设置。\n\n情况2:将以下设置复制到您现在使用的配置文件中。\n\n```python\ntest_dataloader = dict(\n    dataset=dict(\n        ann_file='annotations/panoptic_image_info_test-dev2017.json',\n        data_prefix=dict(img='test2017/', _delete_=True)))\ntest_evaluator = dict(\n    format_only=True,\n    ann_file=data_root + 'annotations/panoptic_image_info_test-dev2017.json',\n    outfile_prefix='./work_dirs/coco_panoptic/test')\n```\n\n然后通过以下命令对 coco test-dev et 进行推断。\n\n```shell\npython tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE}\n```\n\n#### 在命令行中更新它们\n\ncoco test-dev 上更新相关设置和推理的命令如下所示。\n\n```shell\n# 用一个 gpu 测试\nCUDA_VISIBLE_DEVICES=0 python tools/test.py \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=${WORK_DIR}/results\n# 用四个 gpu 测试\nCUDA_VISIBLE_DEVICES=0,1,3,4 bash tools/dist_test.sh \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    8 \\  # eights gpus\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=${WORK_DIR}/results\n# 用 slurm 测试\nGPUS=8 tools/slurm_test.sh \\\n    ${Partition} \\\n    ${JOB_NAME} \\\n    ${CONFIG_FILE} \\\n    ${CHECKPOINT_FILE} \\\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=${WORK_DIR}/results\n```\n\n例子:假设我们使用预先训练的带有 ResNet-50 骨干网的 MaskFormer 对 `test2017` 执行推断。\n\n```shell\n# 单 gpu 测试\nCUDA_VISIBLE_DEVICES=0 python tools/test.py \\\n    configs/maskformer/maskformer_r50_mstrain_16x1_75e_coco.py \\\n    checkpoints/maskformer_r50_mstrain_16x1_75e_coco_20220221_141956-bc2699cb.pth \\\n    --cfg-options \\\n    test_dataloader.dataset.ann_file=annotations/panoptic_image_info_test-dev2017.json \\\n    test_dataloader.dataset.data_prefix.img=test2017 \\\n    test_dataloader.dataset.data_prefix._delete_=True \\\n    test_evaluator.format_only=True \\\n    test_evaluator.ann_file=data/coco/annotations/panoptic_image_info_test-dev2017.json \\\n    test_evaluator.outfile_prefix=work_dirs/maskformer/results\n```\n\n### 重命名文件并压缩结果\n\n推理之后，全景分割结果(一个 json 文件和一个存储掩码的目录)将在 `WORK_DIR` 中。我们应该按照 [COCO's Website](https://cocodataset.org/#upload)上的命名约定重新命名它们。最后，我们需要将 json 和存储掩码的目录压缩到 zip 文件中，并根据命名约定重命名该 zip 文件。注意， zip 文件应该**直接**包含上述两个文件。\n\n重命名文件和压缩结果的命令:\n\n```shell\n# 在 WORK_DIR 中，我们有 panoptic 分割结果: 'panoptic' 和 'results. panoptical .json'。\ncd ${WORK_DIR}\n# 将 '[algorithm_name]' 替换为您使用的算法名称\nmv ./panoptic ./panoptic_test-dev2017_[algorithm_name]_results\nmv ./results.panoptic.json ./panoptic_test-dev2017_[algorithm_name]_results.json\nzip panoptic_test-dev2017_[algorithm_name]_results.zip -ur panoptic_test-dev2017_[algorithm_name]_results panoptic_test-dev2017_[algorithm_name]_results.json\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/tracking_analysis_tools.md",
    "content": "**我们在 `tools/` 目录下提供了很多有用的工具。**\n\n## MOT 测试时参数搜索\n\n`tools/analysis_tools/mot/mot_param_search.py` 可以搜索 MOT 模型中 `tracker` 的参数。\n它与 `tools/test.py` 的使用方式相同，但配置上**有所不同**。\n\n下面是修改配置的示例：\n\n1. 定义要记录的期望评估指标。\n\n   例如，你可以将 `evaluator` 定义为：\n\n   ```python\n   test_evaluator=dict(type='MOTChallengeMetrics', metric=['HOTA', 'CLEAR', 'Identity'])\n   ```\n\n   当然，你也可以自定义 `test_evaluator` 中 `metric` 的内容。你可以自由选择 `['HOTA', 'CLEAR', 'Identity']` 中的一个或多个指标。\n\n2. 定义要搜索的参数及其取值。\n\n   假设你有一个 `tracker` 的配置如下：\n\n   ```python\n   model=dict(\n       tracker=dict(\n           type='BaseTracker',\n           obj_score_thr=0.5,\n           match_iou_thr=0.5\n       )\n   )\n   ```\n\n   如果你想要搜索 `tracker` 的参数，只需将其值改为一个列表，如下所示：\n\n   ```python\n   model=dict(\n       tracker=dict(\n           type='BaseTracker',\n           obj_score_thr=[0.4, 0.5, 0.6],\n           match_iou_thr=[0.4, 0.5, 0.6, 0.7]\n       )\n   )\n   ```\n\n   然后，脚本将测试一共12种情况并且记录结果。\n\n## MOT 误差可视化\n\n`tools/analysis_tools/mot/mot_error_visualize.py` 可以为多目标跟踪可视化错误。\n\n该脚本需要推断的结果作为输入。默认情况下，**红色**边界框表示误检（false positive），**黄色**边界框表示漏检（false negative），**蓝色**边界框表示ID切换（ID switch）。\n\n```\npython tools/analysis_tools/mot/mot_error_visualize.py \\\n    ${CONFIG_FILE}\\\n    --input ${INPUT} \\\n    --result-dir ${RESULT_DIR} \\\n    [--output-dir ${OUTPUT}] \\\n    [--fps ${FPS}] \\\n    [--show] \\\n    [--backend ${BACKEND}]\n```\n\n`RESULT_DIR` 中包含了所有视频的推断结果，推断结果是一个 `txt` 文件。\n\n可选参数：\n\n- `OUTPUT`：可视化演示的输出。如果未指定，`--show` 是必选的，用于即时显示视频。\n- `FPS`：输出视频的帧率。\n- `--show`：是否即时显示视频。\n- `BACKEND`：用于可视化边界框的后端。选项包括 `cv2` 和 `plt`。\n\n## 浏览数据集\n\n`tools/analysis_tools/mot/browse_dataset.py` 可以可视化训练数据集，以检查数据集配置是否正确。\n\n**示例：**\n\n```shell\npython tools/analysis_tools/browse_dataset.py ${CONFIG_FILE} [--show-interval ${SHOW_INTERVAL}]\n```\n\n可选参数：\n\n- `SHOW_INTERVAL`: 显示的间隔时间（秒）。\n- `--show`: 是否即时显示图像。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/tracking_config.md",
    "content": "# 学习更多与配置相关的事\n\n我们用 python 文档作为我们的配置系统。你可以在 `MMDetection/configs` 底下找到所有已提供的配置文件。\n\n我们把模块化和继承化设计融入我们的配置系统，这使我们很方便去进行各种实验。如果你想查看相关的配置文件，你可以跑 `python tools/misc/print_config.py /PATH/TO/CONFIG` 去看完整的详细配置。\n\n## 完整配置的简要说明\n\n一个完整的配置通常包含以下主要的字段：\n\n`model`：一个模型的基本配置，包含 `data_preprocessor`、`detector`、`motion` 之类的模块，还有 `train_cfg`、`test_cfg` 等等；\n\n`train_dataloader`：训练数据集的配置，通常包含 `batch_size`、 `num_workers`、 `sampler`、 `dataset` 等等；\n\n`val_dataloader`：验证数据集的配置，与训练数据集的配置类似；\n\n`test_dataloader`：测试数据集的配置，与训练数据集的配置类似；\n\n`val_evaluator`：验证评估器的配置，例如 `type='MOTChallengeMetrics'` 是 MOT 任务里面的测量标准；\n\n`test_evaluator`：测试评估器的配置，与验证评估器的配置类似；\n\n`train_cfg`：训练循环的配置，例如 `type='EpochBasedTrainLoop'` ；\n\n`val_cfg`：验证循环的配置，例如 `type='VideoValLoop'` ；\n\n`test_cfg`：测试循环的配置，例如 `type='VideoTestLoop'` ；\n\n`default_hooks`：默认鱼钩的配置，包含计时器、日志、参数调度程序、检查点、样本种子、可视化；\n\n`vis_backends`：可视化后端的配置，默认使用 `type='LocalVisBackend'` ；\n\n`visualizer`：可视化工具的配置，例如MOT任务使用 `type='TrackLocalVisualizer'` ；\n\n`param_scheduler`：参数调度程序的配置，通常里面设置学习率调度程序；\n\n`optim_wrapper`：优化器封装的配置，包含优化相关的信息，例如优化器、梯度剪裁等；\n\n`load_from`：加载预训练模型的路径；\n\n`resume`：布尔值，如果是 `True` ，会从 `load_from` 加载模型的检查点，训练会恢复至检查点的迭代次数。\n\n## 通过脚本参数修改配置\n\n当使用 `tools/train.py` 或 `tools/test_trackin.py` 执行任务时，可以指定 `--cfg-options` 来就地修改配置。我们举几个例子如下。有关更多详细信息，请参阅[MMEngine](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/config.html)。\n\n### 更新 dict 链的配置键\n\n可以按照原始配置中 `dict` 键的顺序指定配置选项，例如，设置 `--cfg-options model.detector.backbone.norm_eval=False` 会将模型主干中的所有 `BN` 模块更改为训练模式。\n\n### 更新配置列表中的关键字\n\n一些配置的 `dict` 关键字会以列表的形式组成，例如，测试管道中的 `test_dataloader.dataset.pipeline` 以列表形式出现，即 `[dict(type='LoadImageFromFile'), ...]`。如果你想在测试管道中将 `LoadImageFromFile` 更改为 `LoadImageFromWebcam`，可以设置 `--cfg-options test_dataloader.dataset.pipeline.0.type=LoadImageFromWebcam`。\n\n### 更新列表/元组的值\n\n要被更新的可能是一个列表或一个元组，例如，你可以通过指定 `--cfg options model.data_processor.mean=[0,0,0]` 来更改 `data_preprocessor` 的平均值的关键字。请注意，指定值内不允许有空格。\n\n## 配置文件结构\n\n`config/_base_` 下有三种基本组件类型，即数据集、模型和默认运行时间。可以用它们来轻松构建许多方法，例如 `SORT`，`DeepSORT`。由 `_base_` 中的组件组成的配置称为基元。\n\n对于同一文件夹下的配置文件，建议只有一个基元配置文件。其他配置文件都应该从基元配置文件继承基本结构，这样，继承级别的最大值为 3。\n\n为了便于理解，我们建议贡献者继承现有的方法。例如，如果在 `Faster R-CNN` 的基础上进行了一些修改，用户可以首先通过指定 `_base_ = ../_base_/models/faster-rcnn_r50-dc5.py` 来继承基本的 `Faster R-CNN` 结构，然后修改配置文件中的必要字段。\n\n如果你正在构建一个与任何现有方法都不共享结构的全新方法，则可以在 `configs` 下创建一个新文件夹 method_name。\n\n有关详细文档，请参阅[MMEngine](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/config.html)。\n\n## 配置命名风格\n\n我们根据以下风格去命名配置文件，建议贡献者遵从相同风格。\n\n`{method}_{module}_{train_cfg}_{train_data}_{test_data}`\n\n`{method}`: 方法名称，例如 `sort`；\n\n`{module}`: 方法的基本模块，例如 `faster-rcnn_r50_fpn`；\n\n`{train_cfg}`: 训练配置通常包含批量大小、迭代次数等，例如 `8xb4-80e`；\n\n`{train_data}`: 训练数据集，例如 `mot17halftrain`；\n\n`{test_data}`: 测试数据集，例如 `test-mot17halfval`。\n\n## 常问问题\n\n### 忽略基本配置中的某些字段\n\n有时候你可以设置 `_delete_=True` 去忽略基本配置中的一些字段，你可以参考[MMEngine](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/config.html)进行简单说明。\n\n### 跟踪数据结构介绍\n\n#### 优点和新功能\n\n在 `mmdetection` 跟踪任务中，我们使用视频来组织数据集，并使用 `TrackDataSample` 来描述数据集信息。\n\n基于视频组织，我们提供了 `transform UniformRefFrameSample` 来对关键帧和参考帧进行采样，并使用 `TransformBroadcaster` 进行剪辑训练。\n\n在某种程度上，`TrackDataSample` 可以被视为多个 `DetDataSample` 的包装器。它包含一个 `video_data_samples`，这是一个以 `DetDataSample` 组成的列表，里面每个 `DetDataSample` 对应一个帧。此外，它的元信息包括关键帧的索引和参考帧的索引，用与剪辑训练。\n\n得益于基于视频的数据组织，整个视频可以直接被测试。这种方式更简洁直观。如果你的 GPU 内存无法容纳整个视频，我们还提供基于图像的测试方法。\n\n## 要做的事\n\n`StrongSORT`、`Mask2Former` 等算法不支持基于视频的测试，这些算法对 GPU 内存提出了挑战，我们将来会优化这个问题。\n\n现在，我们不支持像 `MOT Challenge dataset` 这样的基于视频的数据集和像 `Crowdhuman` 用于 `QDTrack` 算法这样的基于图像的数据集进行联合训练。我们将来会优化这个问题。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/tracking_dataset_prepare.md",
    "content": "## 数据集准备\n\n本页面提供了现有基准数据集的准备说明，包括：\n\n- 多目标跟踪\n\n  - [MOT Challenge](https://motchallenge.net/)\n  - [CrowdHuman](https://www.crowdhuman.org/)\n\n- 视频实例分割\n\n  - [YouTube-VIS](https://youtube-vos.org/dataset/vis/)\n\n### 1. 下载数据集\n\n请从官方网站下载数据集，并将数据集的根目录建立软链接到 `$MMDETECTION/data` 目录下。\n\n#### 1.1 多目标跟踪\n\n- 对于多目标跟踪任务的训练和测试，需要下载MOT Challenge数据集之一（例如MOT17、MOT20），CrowdHuman数据集可以作为补充数据集。\n\n- 对于中国的用户，可以从 [OpenDataLab](https://opendatalab.com/) 上高速下载如下数据集：\n\n  - [MOT17](https://opendatalab.com/MOT17/download)\n  - [MOT20](https://opendatalab.com/MOT20/download)\n  - [CrowdHuman](https://opendatalab.com/CrowdHuman/download)\n\n#### 1.2 视频实例分割\n\n- 对于视频实例分割任务的训练和测试，只需要选择一个YouTube-VIS数据集（例如YouTube-VIS 2019、YouTube-VIS 2021）即可。\n- 可以从 [YouTubeVOS](https://codalab.lisn.upsaclay.fr/competitions/6064) 上下载YouTube-VIS 2019数据集。\n- 可以从 [YouTubeVOS](https://codalab.lisn.upsaclay.fr/competitions/7680) 上下载YouTube-VIS 2021数据集。\n\n#### 1.3 数据结构\n\n如果您的文件夹结构与以下结构不同，则可能需要在配置文件中更改相应的路径。\n\n```\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── annotations\n│   │\n|   ├── MOT15/MOT16/MOT17/MOT20\n|   |   ├── train\n|   |   |   ├── MOT17-02-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── gt\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n|   |   ├── test\n|   |   |   ├── MOT17-01-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n│   │\n│   ├── crowdhuman\n│   │   ├── annotation_train.odgt\n│   │   ├── annotation_val.odgt\n│   │   ├── train\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_train01.zip\n│   │   │   ├── CrowdHuman_train02.zip\n│   │   │   ├── CrowdHuman_train03.zip\n│   │   ├── val\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_val.zip\n│   │\n```\n\n### 2. 转换注释\n\n在这种情况下，您需要将官方注释（Annotations）转换为COCO格式。我们提供了相应的脚本，使用方法如下：\n\n```shell\n# MOT17\n# 其他 MOT Challenge 数据集的处理方式与 MOT17 相同。\npython ./tools/dataset_converters/mot2coco.py -i ./data/MOT17/ -o ./data/MOT17/annotations --split-train --convert-det\npython ./tools/dataset_converters/mot2reid.py -i ./data/MOT17/ -o ./data/MOT17/reid --val-split 0.2 --vis-threshold 0.3\n\n# CrowdHuman\npython ./tools/dataset_converters/crowdhuman2coco.py -i ./data/crowdhuman -o ./data/crowdhuman/annotations\n\n# YouTube-VIS 2019\npython ./tools/dataset_converters/youtubevis2coco.py -i ./data/youtube_vis_2019 -o ./data/youtube_vis_2019/annotations --version 2019\n\n# YouTube-VIS 2021\npython ./tools/dataset_converters/youtubevis2coco.py -i ./data/youtube_vis_2021 -o ./data/youtube_vis_2021/annotations --version 2021\n\n```\n\n运行这些脚本后，文件夹结构将如下所示：\n\n```\nmmdetection\n├── mmtrack\n├── tools\n├── configs\n├── data\n│   ├── coco\n│   │   ├── train2017\n│   │   ├── val2017\n│   │   ├── test2017\n│   │   ├── annotations\n│   │\n|   ├── MOT15/MOT16/MOT17/MOT20\n|   |   ├── train\n|   |   |   ├── MOT17-02-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── gt\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n|   |   ├── test\n|   |   |   ├── MOT17-01-DPM\n|   |   |   |   ├── det\n|   │   │   │   ├── img1\n|   │   │   │   ├── seqinfo.ini\n│   │   │   ├── ......\n|   |   ├── annotations\n|   |   ├── reid\n│   │   │   ├── imgs\n│   │   │   ├── meta\n│   │\n│   ├── crowdhuman\n│   │   ├── annotation_train.odgt\n│   │   ├── annotation_val.odgt\n│   │   ├── train\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_train01.zip\n│   │   │   ├── CrowdHuman_train02.zip\n│   │   │   ├── CrowdHuman_train03.zip\n│   │   ├── val\n│   │   │   ├── Images\n│   │   │   ├── CrowdHuman_val.zip\n│   │   ├── annotations\n│   │   │   ├── crowdhuman_train.json\n│   │   │   ├── crowdhuman_val.json\n│   │\n│   ├── youtube_vis_2019\n│   │   │── train\n│   │   │   │── JPEGImages\n│   │   │   │── ......\n│   │   │── valid\n│   │   │   │── JPEGImages\n│   │   │   │── ......\n│   │   │── test\n│   │   │   │── JPEGImages\n│   │   │   │── ......\n│   │   │── train.json (the official annotation files)\n│   │   │── valid.json (the official annotation files)\n│   │   │── test.json (the official annotation files)\n│   │   │── annotations (the converted annotation file)\n│   │\n│   ├── youtube_vis_2021\n│   │   │── train\n│   │   │   │── JPEGImages\n│   │   │   │── instances.json (the official annotation files)\n│   │   │   │── ......\n│   │   │── valid\n│   │   │   │── JPEGImages\n│   │   │   │── instances.json (the official annotation files)\n│   │   │   │── ......\n│   │   │── test\n│   │   │   │── JPEGImages\n│   │   │   │── instances.json (the official annotation files)\n│   │   │   │── ......\n│   │   │── annotations (the converted annotation file)\n```\n\n#### MOT15/MOT16/MOT17/MOT20中的注释和reid文件夹\n\n以 MOT17 数据集为例，其他数据集的结构类似。\n\n在 `data/MOT17/annotations` 文件夹中有8个JSON文件：\n\n`train_cocoformat.json`: 包含MOT17数据集训练集的注释信息的JSON文件。\n\n`train_detections.pkl`: 包含MOT17数据集训练集的公共检测结果的Pickle文件。\n\n`test_cocoformat.json`: 包含MOT17数据集测试集的注释信息的JSON文件。\n\n`test_detections.pkl`: 包含MOT17数据集测试集的公共检测结果的Pickle文件。\n\n`half-train_cocoformat.json`、`half-train_detections.pkl`、`half-val_cocoformat.json` 和 `half-val_detections.pkl` 与 `train_cocoformat.json` 和 `train_detections.pkl` 具有类似的含义。`half` 表示将训练集中的每个视频分成两半。前一半的视频被标记为 `half-train` 集，后一半的视频被标记为 `half-val` 集。\n\n`data/MOT17/reid` 文件夹的结构如下所示：\n\n```\nreid\n├── imgs\n│   ├── MOT17-02-FRCNN_000002\n│   │   ├── 000000.jpg\n│   │   ├── 000001.jpg\n│   │   ├── ...\n│   ├── MOT17-02-FRCNN_000003\n│   │   ├── 000000.jpg\n│   │   ├── 000001.jpg\n│   │   ├── ...\n├── meta\n│   ├── train_80.txt\n│   ├── val_20.txt\n```\n\n`train_80.txt` 中的 `80` 表示训练数据集在整个ReID数据集中的比例为80%。而验证数据集的比例为20%。\n\n关于训练，我们提供了一个注释列表 `train_80.txt`。列表中的每一行包含一个文件名及其对应的真实标签。格式如下所示：\n\n```\nMOT17-05-FRCNN_000110/000018.jpg 0\nMOT17-13-FRCNN_000146/000014.jpg 1\nMOT17-05-FRCNN_000088/000004.jpg 2\nMOT17-02-FRCNN_000009/000081.jpg 3\n```\n\n`MOT17-05-FRCNN_000110` 表示 `MOT17-05-FRCNN` 视频中的第110个人。\n\n对于验证集，注释列表 `val_20.txt` 的格式与上述相同。\n\n`reid/imgs` 中的图像是通过相应的 `gt.txt` 从 `MOT17/train` 中的原始图像中裁剪而来。真实标签的值应在 `[0, num_classes - 1]` 的范围内。\n\n#### CrowdHuman 中的 annotations 文件夹\n\n`data/crowdhuman/annotations` 文件夹下有两个JSON文件：\n\n`crowdhuman_train.json`：包含 CrowdHuman 数据集训练集的注释信息的JSON文件。\n`crowdhuman_val.json`：包含 CrowdHuman 数据集验证集的注释信息的JSON文件。\n\n#### youtube_vis_2019/youtube_vis2021 中的 annotations 文件夹\n\nThere are 3 JSON files in `data/youtube_vis_2019/annotations` or `data/youtube_vis_2021/annotations`：\n\n`youtube_vis_2019_train.json`/`youtube_vis_2021_train.json`：包含 youtube_vis_2019/youtube_vis2021 数据集训练集的注释信息的JSON文件。\n\n`youtube_vis_2019_valid.json`/`youtube_vis_2021_valid.json`：包含 youtube_vis_2019/youtube_vis2021 数据集验证集的注释信息的JSON文件。\n\n`youtube_vis_2019_test.json`/`youtube_vis_2021_test.json`：包含 youtube_vis_2019/youtube_vis2021 数据集测试集的注释信息的JSON文件。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/tracking_interference.md",
    "content": "# 推理\n\n我们提供了一些演示脚本去推理一个给出的视频，或者是推理包含一系列连续照片的文件夹。想要获取该代码资源，请点击 [这里](https://github.com/open-mmlab/mmdetection/tree/tracking/demo)。\n\n若输入为文件夹格式，你需要标明这点。并且，图片命名应该**易于整理**，以便于你根据文件名字中包含的数字信息来重新调整图片的顺序。我们现在只支持 `.jpg`，`.jpeg` 和 `.png` 格式的图片。\n\n## MOT models 的推理\n\n该脚本能够使用多任务跟踪或者视频实例分割方法来推理一段输入的视频/一张图片。\n\n```shell\npython demo/mot_demo.py \\\n    ${INPUTS}\n    ${CONFIG_FILE} \\\n    [--checkpoint ${CHECKPOINT_FILE}] \\\n    [--detector ${DETECTOR_FILE}] \\\n    [--reid ${REID_FILE}] \\\n    [--score-thr ${SCORE_THR}] \\\n    [--device ${DEVICE}] \\\n    [--out ${OUTPUT}] \\\n    [--show]\n```\n\n`INPUTS` 和 `OUTPUT` 参数支持 _mp4 视频_ 格式和_文件夹_格式。\n\n**特别注意**：对于 `DeepSORT`、`SORT`、`StrongSORT`，他们需要单独加载 `reid` 和 `detector` 的权重。因此，我们会使用 `--detector` 和 `--reid` 来加载权重参数。其他的例如 `ByteTrack`、`OCSORT`、`QDTrack`、`MaskTrackRCNN` 以及 `Mask2Former` 这样的算法则使用 `--checkpoint` 来加载权重参数。\n\n可选参数：\n\n- `CHECKPOINT_FILE`： 可选择 checkpoint。\n- `DETECTOR_FILE`： 可选择 detector。\n- `REID_FILE`： 可选择 reid。\n- `SCORE_THR`： bboxes 的得分阈值。\n- `DEVICE`： 推理所需配置。可以选择 `cpu`，`cuda:0`,或者其他。\n- `OUTPUT`： 输出结果可视化的示例。如果未指定， `--show` 将强制显示动态视频。\n- `--show`： 是否即时显示视频。\n\n**运行 mot model 的示例:**\n\n```shell\n# 示例 1：不指定 --checkpoint 使用 --detector\npython demo/mot_demo.py \\\n    demo/demo_mot.mp4 \\\n    configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py \\\n    --detector \\\n    https://download.openmmlab.com/mmtracking/mot/faster_rcnn/faster-rcnn_r50_fpn_4e_mot17-half-64ee2ed4.pth \\\n    --out mot.mp4\n\n# 示例 2：使用 --checkpoint\npython demo/mot_demo.py \\\n    demo/demo_mot.mp4 \\\n    configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py \\\n    --checkpoint https://download.openmmlab.com/mmtracking/mot/qdtrack/mot_dataset/qdtrack_faster-rcnn_r50_fpn_4e_mot17_20220315_145635-76f295ef.pth \\\n    --out mot.mp4\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/tracking_train_test_zh_cn.md",
    "content": "# 学习训练和测试\n\n## 训练\n\n本节将介绍如何在支持的数据集上训练现有模型。\n支持以下训练环境：\n\n- CPU\n- 单 GPU\n- 单节点多 GPU\n- 多节点\n\n您还可以使用 Slurm 管理作业。\n\n重要：\n\n- 在训练过程中，您可以通过修改 `train_cfg` 来改变评估间隔。\n  `train_cfg = dict(val_interval=10)`。这意味着每 10 个 epoch 对模型进行一次评估。\n- 所有配置文件中的默认学习率为 8 个 GPU。\n  根据[线性扩展规则](https://arxiv.org/abs/1706.02677)、\n  如果在每个 GPU 上使用不同的 GPU 或图像，则需要设置与批次大小成比例的学习率、\n  例如，8 个 GPU * 1 个图像/GPU 的学习率为 `lr=0.01`，16 个 GPU * 2 个图像/GPU 的学习率为 lr=0.04。\n- 在训练过程中，日志文件和检查点将保存到工作目录、\n  该目录由 CLI 参数 `--work-dir`指定。它默认使用 `./work_dirs/CONFIG_NAME`。\n- 如果需要混合精度训练，只需指定 CLI 参数 `--amp`。\n\n#### 1.在 CPU 上训练\n\n该模型默认放在 cuda 设备上。\n仅当没有 cuda 设备时，该模型才会放在 CPU 上。\n因此，如果要在 CPU 上训练模型，则需要先 `export CUDA_VISIBLE_DEVICES=-1` 以禁用 GPU 可见性。\n更多细节参见 [MMEngine](https://github.com/open-mmlab/mmengine/blob/ca282aee9e402104b644494ca491f73d93a9544f/mmengine/runner/runner.py#L849-L850).\n\n```shell 脚本\nCUDA_VISIBLE_DEVICES=-1 python tools/train.py ${CONFIG_FILE} [optional arguments]\n```\n\n在 CPU 上训练 MOT 模型 QDTrack 的示例：\n\n```shell 脚本\nCUDA_VISIBLE_DEVICES=-1 python tools/train.py configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n```\n\n#### 2. 在单 GPU 上训练\n\n如果您想在单 GPU 上训练模型, 您可以按照如下方法直接使用 `tools/train.py`.\n\n```shell 脚本\npython tools/train.py ${CONFIG_FILE} [optional arguments]\n```\n\n您可以使用 `export CUDA_VISIBLE_DEVICES=$GPU_ID` 命令选择GPU.\n\n在单 GPU 上训练 MOT 模型 QDTrack 的示例：\n\n```shell 脚本\nCUDA_VISIBLE_DEVICES=2 python tools/train.py configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n```\n\n#### 3. 在单节点多 GPU 上进行训练\n\n我们提供了 `tools/dist_train.sh`，用于在多个 GPU 上启动训练。\n基本用法如下。\n\n```shell 脚本\nbash ./tools/dist_train.sh ${CONFIG_FILE} ${GPU_NUM} [optional arguments]\n```\n\n如果您想在一台机器上启动多个作业、\n例如，在拥有 8 个 GPU 的机器上启动 2 个 4-GPU 训练作业、\n需要为每个作业指定不同的端口（默认为 29500），以避免通信冲突。\n\n例如，可以在命令中设置端口如下。\n\n```shell 脚本\nCUDA_VISIBLE_DEVICES=0,1,2,3 PORT=29500 ./tools/dist_train.sh ${CONFIG_FILE} 4\nCUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4\n```\n\n在单节点多 GPU 上训练 MOT 模型 QDTrack 的示例：\n\n```shell脚本\nbash ./tools/dist_train.sh configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8\n```\n\n#### 4. 在多个节点上训练\n\n如果使用以太网连接多台机器，只需运行以下命令即可：\n\n在第一台机器上\n\n```shell 脚本\nNNODES=2 NODE_RANK=0 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR bash tools/dist_train.sh $CONFIG $GPUS\n```\n\n在第二台机器上:\n\n```shell script\nNNODES=2 NODE_RANK=1 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR bash tools/dist_train.sh $CONFIG $GPUS\n```\n\n如果没有 InfiniBand 等高速网络，速度通常会很慢。\n\n#### 5. 使用 Slurm 进行训练\n\n[Slurm](https://slurm.schedmd.com/)是一个用于计算集群的优秀作业调度系统。\n在 Slurm 管理的集群上，您可以使用 `slurm_train.sh` 生成训练作业。\n它支持单节点和多节点训练。\n\n基本用法如下。\n\n```shell 脚本\nbash ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR} ${GPUS}\n```\n\n使用 Slurm 训练 MOT 模型 QDTrack 的示例：\n\n```shell脚本\nPORT=29501 \\\nGPUS_PER_NODE=8 \\\nSRUN_ARGS=\"--quotatype=reserved\" \\\nbash ./tools/slurm_train.sh \\\nmypartition \\\nmottrack\nconfigs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py\n./work_dirs/QDTrack \\\n8\n```\n\n## 测试\n\n本节将介绍如何在支持的数据集上测试现有模型。\n支持以下测试环境：\n\n- CPU\n- 单 GPU\n- 单节点多 GPU\n- 多节点\n\n您还可以使用 Slurm 管理作业。\n\n重要：\n\n- 在 MOT 中，某些算法（如 `DeepSORT`、`SORT`、`StrongSORT`）需要分别加载 `reid` 的权重和 `detector` 的权重。\n  其他算法，如`ByteTrack`、`OCSORT`和`QDTrack`则不需要。因此，我们提供了 `--checkpoint`、`--detector` 和 `--reid`来加载权重。\n- 我们提供了两种评估和测试模型的方法，即基于视频的测试和基于图像的测试。 有些算法如 `StrongSORT`, `Mask2former` 只支持基于视频的测试. 如果您的 GPU 内存无法容纳整个视频，您可以通过设置采样器类型来切换测试方式。\n  例如\n  基于视频的测试：`sampler=dict(type='DefaultSampler', shuffle=False, round_up=False)`\n  基于图像的测试：`sampler=dict（type='TrackImgSampler'）`\n- 您可以通过修改 evaluator 中的关键字 `outfile_prefix` 来设置结果保存路径。\n  例如，`val_evaluator = dict(outfile_prefix='results/sort_mot17')`。\n  否则，将创建一个临时文件，并在评估后删除。\n- 如果您只想要格式化的结果而不需要评估，可以设置 `format_only=True`。\n  例如，`test_evaluator = dict(type='MOTChallengeMetric', metric=['HOTA', 'CLEAR', 'Identity'], outfile_prefix='sort_mot17_results', format_only=True)`\n\n#### 1. 在 CPU 上测试\n\n模型默认在 cuda 设备上运行。\n只有在没有 cuda 设备的情况下，模型才会在 CPU 上运行。\n因此，如果要在 CPU 上测试模型，您需要 `export CUDA_VISIBLE_DEVICES=-1` 先禁用 GPU 可见性。\n\n更多细节请参考[MMEngine](https://github.com/open-mmlab/mmengine/blob/ca282aee9e402104b644494ca491f73d93a9544f/mmengine/runner/runner.py#L849-L850).\n\n```shell 脚本\nCUDA_VISIBLE_DEVICES=-1 python tools/test_tracking.py ${CONFIG_FILE} [optional arguments]\n```\n\n在 CPU 上测试 MOT 模型 SORT 的示例：\n\n```shell 脚本\nCUDA_VISIBLE_DEVICES=-1 python tools/test_tracking.py configs/sort/sort_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py --detector ${CHECKPOINT_FILE}\n```\n\n#### 2. 在单 GPU 上测试\n\n如果您想在单 GPU 上测试模型，可以直接使用 `tools/test_tracking.py`，如下所示。\n\n```shell 脚本\npython tools/test_tracking.py ${CONFIG_FILE} [optional arguments]\n```\n\n您可以使用 `export CUDA_VISIBLE_DEVICES=$GPU_ID` 来选择 GPU。\n\n在单 GPU 上测试 MOT 模型 QDTrack 的示例：\n\n```shell 脚本\nCUDA_VISIBLE_DEVICES=2 python tools/test_tracking.py configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py --detector ${CHECKPOINT_FILE}\n```\n\n#### 3. 在单节点多 GPU 上进行测试\n\n我们提供了 `tools/dist_test_tracking.sh`，用于在多个 GPU 上启动测试。\n基本用法如下。\n\n```shell 脚本\nbash ./tools/dist_test_tracking.sh ${CONFIG_FILE} ${GPU_NUM} [optional arguments]\n```\n\n在单节点多 GPU 上测试 MOT 模型 DeepSort 的示例：\n\n```shell 脚本\nbash ./tools/dist_test_tracking.sh configs/qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py 8 --detector ${CHECKPOINT_FILE} --reid ${CHECKPOINT_FILE}\n```\n\n#### 4. 在多个节点上测试\n\n您可以在多个节点上进行测试，这与 \"在多个节点上进行训练 \"类似。\n\n#### 5. 使用 Slurm 进行测试\n\n在 Slurm 管理的集群上，您可以使用 `slurm_test_tracking.sh` 生成测试作业。\n它支持单节点和多节点测试。\n\n基本用法如下。\n\n```shell 脚本\n[GPUS=${GPUS}] bash tools/slurm_test_tracking.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} [optional arguments]\n```\n\n使用 Slurm 测试 VIS 模型 Mask2former 的示例：\n\n```shell 脚本\nGPUS=8\nbash tools/slurm_test_tracking.sh \\\nmypartition \\\nvis \\\nconfigs/mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py \\\n--checkpoint ${CHECKPOINT_FILE}\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/tracking_visualization.md",
    "content": "# 了解可视化\n\n## 本地的可视化\n\n这一节将会展示如何使用本地的工具可视化 detection/tracking 的运行结果。\n\n如果你想要画出预测结果的图像，你可以如下示例，将 `TrackVisualizationHook` 中的 draw 的参数设置为 `draw=True`。\n\n```shell\ndefault_hooks = dict(visualization=dict(type='TrackVisualizationHook', draw=True))\n```\n\n`TrackVisualizationHook` 共有如下参数：\n\n- `draw`： 是否绘制预测结果。如果选择 False，将不会显示图像。该参数默认设置为 False。\n- `interval`： 可视化的间隔。默认值为 30。\n- `score_thr`： 确定是否可视化边界框和掩码的阈值。默认值是 0.3。\n- `show`： 是否展示绘制的图像。默认不显示。\n- `wait_time`： 展示的时间间隔(秒)。默认为 0。\n- `test_out_dir`： 测试过程中绘制图像保存的目录。\n- `backend_args`： 用于实例化文件客户端的参数。默认值为 `None `。\n\n在 `TrackVisualizationHook` 中，将调用 `TrackLocalVisualizer` 来实现 MOT 和 VIS 任务的可视化。具体细节如下。\n\n你可以通过 MMEngine 获取  [Visualization](https://github.com/open-mmlab/mmengine/blob/main/docs/zh_cn/advanced_tutorials/visualization.md) 和  [Hook](https://github.com/open-mmlab/mmengine/blob/main/docs/zh_cn/tutorials/hook.md) 的更多细节。\n\n### Tracking 的可视化\n\n我们使用 `TrackLocalVisualizer` 这个类以实现跟踪任务可视化。调用方式如下：\n\n```python\nvisualizer = dict(type='TrackLocalVisualizer')\n```\n\nvisualizer 共有如下的参数：\n\n- `name`： 所选实例的名称。默认值为 ‘visualizer’。\n\n- `image`： 用于绘制的原始图像。格式需要为 RGB。默认为 None。\n\n- `vis_backends`： 可视化后端配置列表。默认为 None。\n\n- `save_dir`： 所有后端存储的保存文件目录。如果为 None，后端将不会保存任何数据。\n\n- `line_width`： 边框宽度。默认值为 3。\n\n- `alpha`： 边界框和掩码的透明度。默认为 0.8。\n\n这里提供了一个 DeepSORT 的可视化示例：\n\n![test_img_89](https://user-images.githubusercontent.com/99722489/186062929-6d0e4663-0d8e-4045-9ec8-67e0e41da876.png)\n"
  },
  {
    "path": "docs/zh_cn/user_guides/train.md",
    "content": "# 在标准数据集上训练预定义的模型\n\nMMDetection 也为训练检测模型提供了开盖即食的工具。本节将展示在标准数据集（比如 COCO）上如何训练一个预定义的模型。\n\n### 数据集\n\n训练需要准备好数据集，细节请参考 [数据集准备](#%E6%95%B0%E6%8D%AE%E9%9B%86%E5%87%86%E5%A4%87) 。\n\n**注意**：\n目前，`configs/cityscapes` 文件夹下的配置文件都是使用 COCO 预训练权值进行初始化的。如果网络连接不可用或者速度很慢，你可以提前下载现存的模型。否则可能在训练的开始会有错误发生。\n\n### 学习率自动缩放\n\n**注意**：在配置文件中的学习率是在 8 块 GPU，每块 GPU 有 2 张图像（批大小为 8\\*2=16）的情况下设置的。其已经设置在 `config/_base_/schedules/schedule_1x.py` 中的 `auto_scale_lr.base_batch_size`。学习率会基于批次大小为 `16`时的值进行自动缩放。同时，为了不影响其他基于 mmdet 的 codebase，启用自动缩放标志 `auto_scale_lr.enable` 默认设置为 `False`。\n\n如果要启用此功能，需在命令添加参数 `--auto-scale-lr`。并且在启动命令之前，请检查下即将使用的配置文件的名称，因为配置名称指示默认的批处理大小。\n在默认情况下，批次大小是 `8 x 2 = 16`，例如：`faster_rcnn_r50_caffe_fpn_90k_coco.py` 或者 `pisa_faster_rcnn_x101_32x4d_fpn_1x_coco.py`；若不是默认批次，你可以在配置文件看到像 `_NxM_` 字样的，例如：`cornernet_hourglass104_mstest_32x3_210e_coco.py` 的批次大小是 `32 x 3 = 96`, 或者 `scnet_x101_64x4d_fpn_8x1_20e_coco.py` 的批次大小是 `8 x 1 = 8`。\n\n**请记住：如果使用不是默认批次大小为 `16`的配置文件，请检查配置文件中的底部，会有 `auto_scale_lr.base_batch_size`。如果找不到，可以在其继承的 `_base_=[xxx]` 文件中找到。另外，如果想使用自动缩放学习率的功能，请不要修改这些值。**\n\n学习率自动缩放基本用法如下：\n\n```shell\npython tools/train.py \\\n    ${CONFIG_FILE} \\\n    --auto-scale-lr \\\n    [optional arguments]\n```\n\n执行命令之后，会根据机器的GPU数量和训练的批次大小对学习率进行自动缩放，缩放方式详见 [线性扩展规则](https://arxiv.org/abs/1706.02677) ，比如：在 4 块 GPU 并且每张 GPU 上有 2 张图片的情况下 `lr=0.01`，那么在 16 块 GPU 并且每张 GPU 上有 4 张图片的情况下, LR 会自动缩放至 `lr=0.08`。\n\n如果不启用该功能，则需要根据 [线性扩展规则](https://arxiv.org/abs/1706.02677) 来手动计算并修改配置文件里面 `optimizer.lr` 的值。\n\n### 使用单 GPU 训练\n\n我们提供了 `tools/train.py` 来开启在单张 GPU 上的训练任务。基本使用如下：\n\n```shell\npython tools/train.py \\\n    ${CONFIG_FILE} \\\n    [optional arguments]\n```\n\n在训练期间，日志文件和 checkpoint 文件将会被保存在工作目录下，它需要通过配置文件中的 `work_dir` 或者 CLI 参数中的 `--work-dir` 来指定。\n\n默认情况下，模型将在每轮训练之后在 validation 集上进行测试，测试的频率可以通过设置配置文件来指定：\n\n```python\n# 每 12 轮迭代进行一次测试评估\ntrain_cfg = dict(val_interval=12)\n```\n\n这个工具接受以下参数：\n\n- `--work-dir ${WORK_DIR}`: 覆盖工作目录.\n- `--resume`：自动从work_dir中的最新检查点恢复.\n- `--resume ${CHECKPOINT_FILE}`: 从某个 checkpoint 文件继续训练.\n- `--cfg-options 'Key=value'`: 覆盖使用的配置文件中的其他设置.\n\n**注意**：\n`resume` 和 `load-from` 的区别：\n\n`resume` 既加载了模型的权重和优化器的状态，也会继承指定 checkpoint 的迭代次数，不会重新开始训练。`load-from` 则是只加载模型的权重，它的训练是从头开始的，经常被用于微调模型。其中load-from需要写入配置文件中，而resume作为命令行参数传入。\n\n### 使用 CPU 训练\n\n使用 CPU 训练的流程和使用单 GPU 训练的流程一致，我们仅需要在训练流程开始前禁用 GPU。\n\n```shell\nexport CUDA_VISIBLE_DEVICES=-1\n```\n\n之后运行单 GPU 训练脚本即可。\n\n**注意**：\n\n我们不推荐用户使用 CPU 进行训练，这太过缓慢。我们支持这个功能是为了方便用户在没有 GPU 的机器上进行调试。\n\n### 在多 GPU 上训练\n\n我们提供了 `tools/dist_train.sh` 来开启在多 GPU 上的训练。基本使用如下：\n\n```shell\nbash ./tools/dist_train.sh \\\n    ${CONFIG_FILE} \\\n    ${GPU_NUM} \\\n    [optional arguments]\n```\n\n可选参数和单 GPU 训练的可选参数一致。\n\n#### 同时启动多个任务\n\n如果你想在一台机器上启动多个任务的话，比如在一个有 8 块 GPU 的机器上启动 2 个需要 4 块GPU的任务，你需要给不同的训练任务指定不同的端口（默认为 29500）来避免冲突。\n\n如果你使用 `dist_train.sh` 来启动训练任务，你可以使用命令来设置端口。\n\n```shell\nCUDA_VISIBLE_DEVICES=0,1,2,3 PORT=29500 ./tools/dist_train.sh ${CONFIG_FILE} 4\nCUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4\n```\n\n### 使用多台机器训练\n\n如果您想使用由 ethernet 连接起来的多台机器， 您可以使用以下命令:\n\n在第一台机器上:\n\n```shell\nNNODES=2 NODE_RANK=0 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR sh tools/dist_train.sh $CONFIG $GPUS\n```\n\n在第二台机器上:\n\n```shell\nNNODES=2 NODE_RANK=1 PORT=$MASTER_PORT MASTER_ADDR=$MASTER_ADDR sh tools/dist_train.sh $CONFIG $GPUS\n```\n\n但是，如果您不使用高速网路连接这几台机器的话，训练将会非常慢。\n\n### 使用 Slurm 来管理任务\n\nSlurm 是一个常见的计算集群调度系统。在 Slurm 管理的集群上，你可以使用 `slurm.sh` 来开启训练任务。它既支持单节点训练也支持多节点训练。\n\n基本使用如下：\n\n```shell\n[GPUS=${GPUS}] ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR}\n```\n\n以下是在一个名称为 _dev_ 的 Slurm 分区上，使用 16 块 GPU 来训练 Mask R-CNN 的例子，并且将 `work-dir` 设置在了某些共享文件系统下。\n\n```shell\nGPUS=16 ./tools/slurm_train.sh dev mask_r50_1x configs/mask_rcnn_r50_fpn_1x_coco.py /nfs/xxxx/mask_rcnn_r50_fpn_1x\n```\n\n你可以查看 [源码](https://github.com/open-mmlab/mmdetection/blob/main/tools/slurm_train.sh) 来检查全部的参数和环境变量.\n\n在使用 Slurm 时，端口需要以下方的某个方法之一来设置。\n\n1. 通过 `--options` 来设置端口。我们非常建议用这种方法，因为它无需改变原始的配置文件。\n\n   ```shell\n   CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR} --cfg-options 'dist_params.port=29500'\n   CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR} --cfg-options 'dist_params.port=29501'\n   ```\n\n2. 修改配置文件来设置不同的交流端口。\n\n   在 `config1.py` 中，设置：\n\n   ```python\n   dist_params = dict(backend='nccl', port=29500)\n   ```\n\n   在 `config2.py` 中，设置：\n\n   ```python\n   dist_params = dict(backend='nccl', port=29501)\n   ```\n\n   然后你可以使用 `config1.py` 和 `config2.py` 来启动两个任务了。\n\n   ```shell\n   CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR}\n   CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR}\n   ```\n\n# 在自定义数据集上进行训练\n\n通过本文档，你将会知道如何使用自定义数据集对预先定义好的模型进行推理，测试以及训练。我们使用 [balloon dataset](https://github.com/matterport/Mask_RCNN/tree/master/samples/balloon) 作为例子来描述整个过程。\n\n基本步骤如下：\n\n1. 准备自定义数据集\n2. 准备配置文件\n3. 在自定义数据集上进行训练，测试和推理。\n\n## 准备自定义数据集\n\nMMDetection 一共支持三种形式应用新数据集：\n\n1. 将数据集重新组织为 COCO 格式。\n2. 将数据集重新组织为一个中间格式。\n3. 实现一个新的数据集。\n\n我们通常建议使用前面两种方法，因为它们通常来说比第三种方法要简单。\n\n在本文档中，我们展示一个例子来说明如何将数据转化为 COCO 格式。\n\n**注意**：在 MMDetection 3.0 之后，数据集和指标已经解耦（除了 CityScapes）。因此，用户在验证阶段使用任意的评价指标来评价模型在任意数据集上的性能。比如，用 VOC 评价指标来评价模型在 COCO 数据集的性能，或者同时使用 VOC 评价指标和 COCO 评价指标来评价模型在 OpenImages 数据集上的性能。\n\n### COCO标注格式\n\n用于实例分割的 COCO 数据集格式如下所示，其中的键（key）都是必要的，参考[这里](https://cocodataset.org/#format-data)来获取更多细节。\n\n```json\n{\n    \"images\": [image],\n    \"annotations\": [annotation],\n    \"categories\": [category]\n}\n\n\nimage = {\n    \"id\": int,\n    \"width\": int,\n    \"height\": int,\n    \"file_name\": str,\n}\n\nannotation = {\n    \"id\": int,\n    \"image_id\": int,\n    \"category_id\": int,\n    \"segmentation\": RLE or [polygon],\n    \"area\": float,\n    \"bbox\": [x,y,width,height], # (x, y) 为 bbox 左上角的坐标\n    \"iscrowd\": 0 or 1,\n}\n\ncategories = [{\n    \"id\": int,\n    \"name\": str,\n    \"supercategory\": str,\n}]\n```\n\n现在假设我们使用 balloon dataset。\n\n下载了数据集之后，我们需要实现一个函数将标注格式转化为 COCO 格式。然后我们就可以使用已经实现的 `CocoDataset` 类来加载数据并进行训练以及评测。\n\n如果你浏览过新数据集，你会发现格式如下：\n\n```json\n{'base64_img_data': '',\n 'file_attributes': {},\n 'filename': '34020010494_e5cb88e1c4_k.jpg',\n 'fileref': '',\n 'regions': {'0': {'region_attributes': {},\n   'shape_attributes': {'all_points_x': [1020,\n     1000,\n     994,\n     1003,\n     1023,\n     1050,\n     1089,\n     1134,\n     1190,\n     1265,\n     1321,\n     1361,\n     1403,\n     1428,\n     1442,\n     1445,\n     1441,\n     1427,\n     1400,\n     1361,\n     1316,\n     1269,\n     1228,\n     1198,\n     1207,\n     1210,\n     1190,\n     1177,\n     1172,\n     1174,\n     1170,\n     1153,\n     1127,\n     1104,\n     1061,\n     1032,\n     1020],\n    'all_points_y': [963,\n     899,\n     841,\n     787,\n     738,\n     700,\n     663,\n     638,\n     621,\n     619,\n     643,\n     672,\n     720,\n     765,\n     800,\n     860,\n     896,\n     942,\n     990,\n     1035,\n     1079,\n     1112,\n     1129,\n     1134,\n     1144,\n     1153,\n     1166,\n     1166,\n     1150,\n     1136,\n     1129,\n     1122,\n     1112,\n     1084,\n     1037,\n     989,\n     963],\n    'name': 'polygon'}}},\n 'size': 1115004}\n```\n\n标注文件时是 JSON 格式的，其中所有键（key）组成了一张图片的所有标注。\n\n其中将 balloon dataset 转化为 COCO 格式的代码如下所示。\n\n```python\nimport os.path as osp\n\nimport mmcv\n\nfrom mmengine.fileio import dump, load\nfrom mmengine.utils import track_iter_progress\n\n\ndef convert_balloon_to_coco(ann_file, out_file, image_prefix):\n    data_infos = load(ann_file)\n\n    annotations = []\n    images = []\n    obj_count = 0\n    for idx, v in enumerate(track_iter_progress(data_infos.values())):\n        filename = v['filename']\n        img_path = osp.join(image_prefix, filename)\n        height, width = mmcv.imread(img_path).shape[:2]\n\n        images.append(\n            dict(id=idx, file_name=filename, height=height, width=width))\n\n        for _, obj in v['regions'].items():\n            assert not obj['region_attributes']\n            obj = obj['shape_attributes']\n            px = obj['all_points_x']\n            py = obj['all_points_y']\n            poly = [(x + 0.5, y + 0.5) for x, y in zip(px, py)]\n            poly = [p for x in poly for p in x]\n\n            x_min, y_min, x_max, y_max = (min(px), min(py), max(px), max(py))\n\n            data_anno = dict(\n                image_id=idx,\n                id=obj_count,\n                category_id=0,\n                bbox=[x_min, y_min, x_max - x_min, y_max - y_min],\n                area=(x_max - x_min) * (y_max - y_min),\n                segmentation=[poly],\n                iscrowd=0)\n            annotations.append(data_anno)\n            obj_count += 1\n\n    coco_format_json = dict(\n        images=images,\n        annotations=annotations,\n        categories=[{\n            'id': 0,\n            'name': 'balloon'\n        }])\n    dump(coco_format_json, out_file)\n\n\nif __name__ == '__main__':\n    convert_balloon_to_coco(ann_file='data/balloon/train/via_region_data.json',\n                            out_file='data/balloon/train/annotation_coco.json',\n                            image_prefix='data/balloon/train')\n    convert_balloon_to_coco(ann_file='data/balloon/val/via_region_data.json',\n                            out_file='data/balloon/val/annotation_coco.json',\n                            image_prefix='data/balloon/val')\n```\n\n使用如上的函数，用户可以成功将标注文件转化为 JSON 格式，之后可以使用 `CocoDataset` 对模型进行训练，并用 `CocoMetric` 评测。\n\n## 准备配置文件\n\n第二步需要准备一个配置文件来成功加载数据集。假设我们想要用 balloon dataset 来训练配备了 FPN 的 Mask R-CNN ，如下是我们的配置文件。假设配置文件命名为 `mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py`，相应保存路径为 `configs/balloon/`，配置文件内容如下所示。详细的配置文件方法可以参考[学习配置文件 — MMDetection 3.0.0 文档](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/config.html#base)。\n\n```python\n# 新配置继承了基本配置，并做了必要的修改\n_base_ = '../mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'\n\n# 我们还需要更改 head 中的 num_classes 以匹配数据集中的类别数\nmodel = dict(\n    roi_head=dict(\n        bbox_head=dict(num_classes=1), mask_head=dict(num_classes=1)))\n\n# 修改数据集相关配置\ndata_root = 'data/balloon/'\nmetainfo = {\n    'classes': ('balloon', ),\n    'palette': [\n        (220, 20, 60),\n    ]\n}\ntrain_dataloader = dict(\n    batch_size=1,\n    dataset=dict(\n        data_root=data_root,\n        metainfo=metainfo,\n        ann_file='train/annotation_coco.json',\n        data_prefix=dict(img='train/')))\nval_dataloader = dict(\n    dataset=dict(\n        data_root=data_root,\n        metainfo=metainfo,\n        ann_file='val/annotation_coco.json',\n        data_prefix=dict(img='val/')))\ntest_dataloader = val_dataloader\n\n# 修改评价指标相关配置\nval_evaluator = dict(ann_file=data_root + 'val/annotation_coco.json')\ntest_evaluator = val_evaluator\n\n# 使用预训练的 Mask R-CNN 模型权重来做初始化，可以提高模型性能\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'\n\n```\n\n## 训练一个新的模型\n\n为了使用新的配置方法来对模型进行训练，你只需要运行如下命令。\n\n```shell\npython tools/train.py configs/balloon/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py\n```\n\n参考 [在标准数据集上训练预定义的模型](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/train.html#id1) 来获取更多详细的使用方法。\n\n## 测试以及推理\n\n为了测试训练完毕的模型，你只需要运行如下命令。\n\n```shell\npython tools/test.py configs/balloon/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon.py work_dirs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_balloon/epoch_12.pth\n```\n\n参考 [测试现有模型](https://mmdetection.readthedocs.io/zh_CN/latest/user_guides/test.html) 来获取更多详细的使用方法。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/useful_hooks.md",
    "content": "# 实用的钩子\n\nMMDetection 和 MMEngine 为用户提供了多种多样实用的钩子（Hook），包括 `MemoryProfilerHook`、`NumClassCheckHook` 等等。\n这篇教程介绍了 MMDetection 中实现的钩子功能及使用方式。若使用 MMEngine 定义的钩子请参考 [MMEngine 的钩子API文档](https://github.com/open-mmlab/mmengine/tree/main/docs/en/tutorials/hook.md).\n\n## CheckInvalidLossHook\n\n## NumClassCheckHook\n\n## MemoryProfilerHook\n\n[内存分析钩子](https://github.com/open-mmlab/mmdetection/blob/main/mmdet/engine/hooks/memory_profiler_hook.py)\n记录了包括虚拟内存、交换内存、当前进程在内的所有内存信息，它能够帮助捕捉系统的使用状况与发现隐藏的内存泄露问题。为了使用这个钩子，你需要先通过 `pip install memory_profiler psutil` 命令安装 `memory_profiler` 和 `psutil`。\n\n### 使用\n\n为了使用这个钩子，使用者需要添加如下代码至 config 文件\n\n```python\ncustom_hooks = [\n    dict(type='MemoryProfilerHook', interval=50)\n]\n```\n\n### 结果\n\n在训练中，你会看到 `MemoryProfilerHook` 记录的如下信息：\n\n```text\nThe system has 250 GB (246360 MB + 9407 MB) of memory and 8 GB (5740 MB + 2452 MB) of swap memory in total. Currently 9407 MB (4.4%) of memory and 5740 MB (29.9%) of swap memory were consumed. And the current training process consumed 5434 MB of memory.\n```\n\n```text\n2022-04-21 08:49:56,881 - mmengine - INFO - Memory information available_memory: 246360 MB, used_memory: 9407 MB, memory_utilization: 4.4 %, available_swap_memory: 5740 MB, used_swap_memory: 2452 MB, swap_memory_utilization: 29.9 %, current_process_memory: 5434 MB\n```\n\n## SetEpochInfoHook\n\n## SyncNormHook\n\n## SyncRandomSizeHook\n\n## YOLOXLrUpdaterHook\n\n## YOLOXModeSwitchHook\n\n## 如何实现自定义钩子\n\n通常，从模型训练的开始到结束，共有20个点位可以执行钩子。我们可以实现自定义钩子在不同点位执行，以便在训练中实现自定义操作。\n\n- global points: `before_run`, `after_run`\n- points in training: `before_train`, `before_train_epoch`, `before_train_iter`, `after_train_iter`, `after_train_epoch`, `after_train`\n- points in validation: `before_val`, `before_val_epoch`, `before_val_iter`, `after_val_iter`, `after_val_epoch`, `after_val`\n- points at testing: `before_test`, `before_test_epoch`, `before_test_iter`, `after_test_iter`, `after_test_epoch`,  `after_test`\n- other points: `before_save_checkpoint`, `after_save_checkpoint`\n\n比如，我们要实现一个检查 loss 的钩子，当损失为 NaN 时自动结束训练。我们可以把这个过程分为三步：\n\n1. 在 MMEngine 实现一个继承于 `Hook` 类的新钩子，并实现 `after_train_iter` 方法用于检查每 `n` 次训练迭代后损失是否变为 NaN 。\n2. 使用 `@HOOKS.register_module()` 注册实现好了的自定义钩子，如下列代码所示。\n3. 在配置文件中添加 `custom_hooks = [dict(type='MemoryProfilerHook', interval=50)]`\n\n```python\nfrom typing import Optional\n\nimport torch\nfrom mmengine.hooks import Hook\nfrom mmengine.runner import Runner\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass CheckInvalidLossHook(Hook):\n    \"\"\"Check invalid loss hook.\n\n    This hook will regularly check whether the loss is valid\n    during training.\n\n    Args:\n        interval (int): Checking interval (every k iterations).\n            Default: 50.\n    \"\"\"\n\n    def __init__(self, interval: int = 50) -> None:\n        self.interval = interval\n\n    def after_train_iter(self,\n                         runner: Runner,\n                         batch_idx: int,\n                         data_batch: Optional[dict] = None,\n                         outputs: Optional[dict] = None) -> None:\n        \"\"\"Regularly check whether the loss is valid every n iterations.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training process.\n            batch_idx (int): The index of the current batch in the train loop.\n            data_batch (dict, Optional): Data from dataloader.\n                Defaults to None.\n            outputs (dict, Optional): Outputs from model. Defaults to None.\n        \"\"\"\n        if self.every_n_train_iters(runner, self.interval):\n            assert torch.isfinite(outputs['loss']), \\\n                runner.logger.info('loss become infinite or NaN!')\n```\n\n请参考 [自定义训练配置](../advanced_guides/customize_runtime.md) 了解更多与自定义钩子相关的内容。\n"
  },
  {
    "path": "docs/zh_cn/user_guides/useful_tools.md",
    "content": "除了训练和测试脚本，我们还在 `tools/` 目录下提供了许多有用的工具。\n\n## 日志分析\n\n`tools/analysis_tools/analyze_logs.py` 可利用指定的训练 log 文件绘制 loss/mAP 曲线图，\n第一次运行前请先运行 `pip install seaborn` 安装必要依赖.\n\n```shell\npython tools/analysis_tools/analyze_logs.py plot_curve [--keys ${KEYS}] [--eval-interval ${EVALUATION_INTERVAL}] [--title ${TITLE}] [--legend ${LEGEND}] [--backend ${BACKEND}] [--style ${STYLE}] [--out ${OUT_FILE}]\n```\n\n![loss curve image](../../../resources/loss_curve.png)\n\n样例:\n\n- 绘制分类损失曲线图\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py plot_curve log.json --keys loss_cls --legend loss_cls\n  ```\n\n- 绘制分类损失、回归损失曲线图，保存图片为对应的 pdf 文件\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py plot_curve log.json --keys loss_cls loss_bbox --out losses.pdf\n  ```\n\n- 在相同图像中比较两次运行结果的 bbox mAP\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py plot_curve log1.json log2.json --keys bbox_mAP --legend run1 run2\n  ```\n\n- 计算平均训练速度\n\n  ```shell\n  python tools/analysis_tools/analyze_logs.py cal_train_time log.json [--include-outliers]\n  ```\n\n  输出以如下形式展示\n\n  ```text\n  -----Analyze train time of work_dirs/some_exp/20190611_192040.log.json-----\n  slowest epoch 11, average time is 1.2024\n  fastest epoch 1, average time is 1.1909\n  time std over epochs is 0.0028\n  average iter time: 1.1959 s/iter\n  ```\n\n## 结果分析\n\n使用 `tools/analysis_tools/analyze_results.py` 可计算每个图像 mAP，随后根据真实标注框与预测框的比较结果，展示或保存最高与最低 top-k 得分的预测图像。\n\n**使用方法**\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n      ${CONFIG} \\\n      ${PREDICTION_PATH} \\\n      ${SHOW_DIR} \\\n      [--show] \\\n      [--wait-time ${WAIT_TIME}] \\\n      [--topk ${TOPK}] \\\n      [--show-score-thr ${SHOW_SCORE_THR}] \\\n      [--cfg-options ${CFG_OPTIONS}]\n```\n\n各个参数选项的作用:\n\n- `config`: model config 文件的路径。\n- `prediction_path`:  使用 `tools/test.py` 输出的 pickle 格式结果文件。\n- `show_dir`: 绘制真实标注框与预测框的图像存放目录。\n- `--show`：决定是否展示绘制 box 后的图片，默认值为 `False`。\n- `--wait-time`: show 时间的间隔，若为 0 表示持续显示。\n- `--topk`: 根据最高或最低 `topk` 概率排序保存的图片数量，若不指定，默认设置为 `20`。\n- `--show-score-thr`: 能够展示的概率阈值，默认为 `0`。\n- `--cfg-options`: 如果指定，可根据指定键值对覆盖更新配置文件的对应选项\n\n**样例**:\n假设你已经通过 `tools/test.py` 得到了 pickle 格式的结果文件，路径为 './result.pkl'。\n\n1. 测试 Faster R-CNN 并可视化结果，保存图片至 `results/`\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       result.pkl \\\n       results \\\n       --show\n```\n\n2. 测试 Faster R-CNN 并指定 top-k 参数为 50，保存结果图片至 `results/`\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       result.pkl \\\n       results \\\n       --topk 50\n```\n\n3. 如果你想过滤低概率的预测结果，指定 `show-score-thr` 参数\n\n```shell\npython tools/analysis_tools/analyze_results.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       result.pkl \\\n       results \\\n       --show-score-thr 0.3\n```\n\n## 多模型检测结果融合\n\n`tools/analysis_tools/fuse_results.py` 可使用 Weighted Boxes Fusion(WBF) 方法将多个模型的检测结果进行融合。（当前仅支持 COCO 格式）\n\n**使用方法**\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ${PRED_RESULTS} \\\n       [--annotation ${ANNOTATION}] \\\n       [--weights ${WEIGHTS}] \\\n       [--fusion-iou-thr ${FUSION_IOU_THR}] \\\n       [--skip-box-thr ${SKIP_BOX_THR}] \\\n       [--conf-type ${CONF_TYPE}] \\\n       [--eval-single ${EVAL_SINGLE}] \\\n       [--save-fusion-results ${SAVE_FUSION_RESULTS}] \\\n       [--out-dir ${OUT_DIR}]\n```\n\n各个参数选项的作用:\n\n- `pred-results`: 多模型测试结果的保存路径。（目前仅支持 json 格式）\n- `--annotation`: 真实标注框的保存路径。\n- `--weights`: 模型融合权重。默认设置下，每个模型的权重均为1。\n- `--fusion-iou-thr`: 在WBF算法中，匹配成功的 IoU 阈值，默认值为`0.55`。\n- `--skip-box-thr`: WBF算法中需剔除的置信度阈值，置信度小于该值的 bbox 会被剔除，默认值为`0`。\n- `--conf-type`: 如何计算融合后 bbox 的置信度。有以下四种选项：\n  - `avg`: 取平均值，默认为此选项。\n  - `max`: 取最大值。\n  - `box_and_model_avg`: box和模型尺度的加权平均值。\n  - `absent_model_aware_avg`: 考虑缺失模型的加权平均值。\n- `--eval-single`: 是否评估每个单一模型，默认值为`False`。\n- `--save-fusion-results`: 是否保存融合结果，默认值为`False`。\n- `--out-dir`: 融合结果保存的路径。\n\n**样例**:\n假设你已经通过 `tools/test.py` 得到了3个模型的 json 格式的结果文件，路径分别为 './faster-rcnn_r50-caffe_fpn_1x_coco.json', './retinanet_r50-caffe_fpn_1x_coco.json', './cascade-rcnn_r50-caffe_fpn_1x_coco.json'，真实标注框的文件路径为'./annotation.json'。\n\n1. 融合三个模型的预测结果并评估其效果\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ./faster-rcnn_r50-caffe_fpn_1x_coco.json \\\n       ./retinanet_r50-caffe_fpn_1x_coco.json \\\n       ./cascade-rcnn_r50-caffe_fpn_1x_coco.json \\\n       --annotation ./annotation.json \\\n       --weights 1 2 3 \\\n```\n\n2. 同时评估每个单一模型与融合结果\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ./faster-rcnn_r50-caffe_fpn_1x_coco.json \\\n       ./retinanet_r50-caffe_fpn_1x_coco.json \\\n       ./cascade-rcnn_r50-caffe_fpn_1x_coco.json \\\n       --annotation ./annotation.json \\\n       --weights 1 2 3 \\\n       --eval-single\n```\n\n3. 融合三个模型的预测结果并保存\n\n```shell\npython tools/analysis_tools/fuse_results.py \\\n       ./faster-rcnn_r50-caffe_fpn_1x_coco.json \\\n       ./retinanet_r50-caffe_fpn_1x_coco.json \\\n       ./cascade-rcnn_r50-caffe_fpn_1x_coco.json \\\n       --annotation ./annotation.json \\\n       --weights 1 2 3 \\\n       --save-fusion-results \\\n       --out-dir outputs/fusion\n```\n\n## 可视化\n\n### 可视化数据集\n\n`tools/analysis_tools/browse_dataset.py` 可帮助使用者检查所使用的检测数据集（包括图像和标注），或保存图像至指定目录。\n\n```shell\npython tools/analysis_tools/browse_dataset.py ${CONFIG} [-h] [--skip-type ${SKIP_TYPE[SKIP_TYPE...]}] [--output-dir ${OUTPUT_DIR}] [--not-show] [--show-interval ${SHOW_INTERVAL}]\n```\n\n### 可视化模型\n\n在可视化之前，需要先转换模型至 ONNX 格式，[可参考此处](#convert-mmdetection-model-to-onnx-experimental)。\n注意，现在只支持 RetinaNet，之后的版本将会支持其他模型\n转换后的模型可以被其他工具可视化[Netron](https://github.com/lutzroeder/netron)。\n\n### 可视化预测结果\n\n如果你想要一个轻量 GUI 可视化检测结果，你可以参考 [DetVisGUI project](https://github.com/Chien-Hung/DetVisGUI/tree/mmdetection)。\n\n## 误差分析\n\n`tools/analysis_tools/coco_error_analysis.py` 使用不同标准分析每个类别的 COCO 评估结果。同时将一些有帮助的信息体现在图表上。\n\n```shell\npython tools/analysis_tools/coco_error_analysis.py ${RESULT} ${OUT_DIR} [-h] [--ann ${ANN}] [--types ${TYPES[TYPES...]}]\n```\n\n样例:\n\n假设你已经把 [Mask R-CNN checkpoint file](https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth) 放置在文件夹 'checkpoint' 中（其他模型请在 [model zoo](./model_zoo.md) 中获取）。\n\n为了保存 bbox 结果信息，我们需要用下列方式修改 `test_evaluator` :\n\n1. 查找当前 config 文件相对应的  'configs/base/datasets' 数据集信息。\n2. 用当前数据集 config 中的 test_evaluator 以及 test_dataloader 替换原始文件的 test_evaluator 以及 test_dataloader。\n3. 使用以下命令得到 bbox 或 segmentation 的 json 格式文件。\n\n```shell\npython tools/test.py \\\n       configs/mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py \\\n       checkpoint/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth \\\n```\n\n1. 得到每一类的 COCO bbox 误差结果，并保存分析结果图像至指定目录。（在 [config](../../../configs/_base_/datasets/coco_instance.py) 中默认目录是 './work_dirs/coco_instance/test')\n\n```shell\npython tools/analysis_tools/coco_error_analysis.py \\\n       results.bbox.json \\\n       results \\\n       --ann=data/coco/annotations/instances_val2017.json \\\n```\n\n2. 得到每一类的 COCO 分割误差结果，并保存分析结果图像至指定目录。\n\n```shell\npython tools/analysis_tools/coco_error_analysis.py \\\n       results.segm.json \\\n       results \\\n       --ann=data/coco/annotations/instances_val2017.json \\\n       --types='segm'\n```\n\n## 模型服务部署\n\n如果你想使用 [`TorchServe`](https://pytorch.org/serve/) 搭建一个 `MMDetection` 模型服务，可以参考以下步骤：\n\n### 1. 安装 TorchServe\n\n假设你已经成功安装了包含 `PyTorch` 和 `MMDetection` 的 `Python` 环境，那么你可以运行以下命令来安装 `TorchServe` 及其依赖项。有关更多其他安装选项，请参考[快速入门](https://github.com/pytorch/serve/blob/master/README.md#serve-a-model)。\n\n```shell\npython -m pip install torchserve torch-model-archiver torch-workflow-archiver nvgpu\n```\n\n**注意**: 如果你想在 docker 中使用`TorchServe`，请参考[torchserve docker](https://github.com/pytorch/serve/blob/master/docker/README.md)。\n\n### 2. 把 MMDetection 模型转换至 TorchServe\n\n```shell\npython tools/deployment/mmdet2torchserve.py ${CONFIG_FILE} ${CHECKPOINT_FILE} \\\n--output-folder ${MODEL_STORE} \\\n--model-name ${MODEL_NAME}\n```\n\n### 3. 启动 `TorchServe`\n\n```shell\ntorchserve --start --ncs \\\n  --model-store ${MODEL_STORE} \\\n  --models  ${MODEL_NAME}.mar\n```\n\n### 4. 测试部署效果\n\n```shell\ncurl -O curl -O https://raw.githubusercontent.com/pytorch/serve/master/docs/images/3dogs.jpg\ncurl http://127.0.0.1:8080/predictions/${MODEL_NAME} -T 3dogs.jpg\n```\n\n你可以得到下列 json 信息：\n\n```json\n[\n  {\n    \"class_label\": 16,\n    \"class_name\": \"dog\",\n    \"bbox\": [\n      294.63409423828125,\n      203.99111938476562,\n      417.048583984375,\n      281.62744140625\n    ],\n    \"score\": 0.9987992644309998\n  },\n  {\n    \"class_label\": 16,\n    \"class_name\": \"dog\",\n    \"bbox\": [\n      404.26019287109375,\n      126.0080795288086,\n      574.5091552734375,\n      293.6662292480469\n    ],\n    \"score\": 0.9979367256164551\n  },\n  {\n    \"class_label\": 16,\n    \"class_name\": \"dog\",\n    \"bbox\": [\n      197.2144775390625,\n      93.3067855834961,\n      307.8505554199219,\n      276.7560119628906\n    ],\n    \"score\": 0.993338406085968\n  }\n]\n```\n\n#### 结果对比\n\n你也可以使用 `test_torchserver.py` 来比较 `TorchServe` 和 `PyTorch` 的结果，并可视化：\n\n```shell\npython tools/deployment/test_torchserver.py ${IMAGE_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} ${MODEL_NAME}\n[--inference-addr ${INFERENCE_ADDR}] [--device ${DEVICE}] [--score-thr ${SCORE_THR}] [--work-dir ${WORK_DIR}]\n```\n\n样例:\n\n```shell\npython tools/deployment/test_torchserver.py \\\ndemo/demo.jpg \\\nconfigs/yolo/yolov3_d53_8xb8-320-273e_coco.py \\\ncheckpoint/yolov3_d53_320_273e_coco-421362b6.pth \\\nyolov3 \\\n--work-dir ./work-dir\n```\n\n### 5. 停止 `TorchServe`\n\n```shell\ntorchserve --stop\n```\n\n## 模型复杂度\n\n`tools/analysis_tools/get_flops.py` 工具可用于计算指定模型的 FLOPs、参数量大小（改编自 [flops-counter.pytorch](https://github.com/sovrasov/flops-counter.pytorch) ）。\n\n```shell\npython tools/analysis_tools/get_flops.py ${CONFIG_FILE} [--shape ${INPUT_SHAPE}]\n```\n\n获得的结果如下：\n\n```text\n==============================\nInput shape: (3, 1280, 800)\nFlops: 239.32 GFLOPs\nParams: 37.74 M\n==============================\n```\n\n**注意**：这个工具还只是实验性质，我们不保证这个数值是绝对正确的。你可以将他用于简单的比较，但如果用于科技论文报告需要再三检查确认。\n\n1. FLOPs 与输入的形状大小相关，参数量没有这个关系，默认的输入形状大小为 (1, 3, 1280, 800) 。\n2. 一些算子并不计入 FLOPs，比如 GN 或其他自定义的算子。你可以参考 [`mmcv.cnn.get_model_complexity_info()`](https://github.com/open-mmlab/mmcv/blob/2.x/mmcv/cnn/utils/flops_counter.py) 查看更详细的说明。\n3. 两阶段检测的 FLOPs 大小取决于 proposal 的数量。\n\n## 模型转换\n\n### MMDetection 模型转换至 ONNX 格式\n\n我们提供了一个脚本用于转换模型至 [ONNX](https://github.com/onnx/onnx) 格式。同时还支持比较 Pytorch 与 ONNX 模型的输出结果以便对照。更详细的内容可以参考 [mmdeploy](https://github.com/open-mmlab/mmdeploy)。\n\n### MMDetection 1.x 模型转换至 MMDetection 2.x 模型\n\n`tools/model_converters/upgrade_model_version.py` 可将旧版本的 MMDetection checkpoints 转换至新版本。但要注意此脚本不保证在新版本加入非兼容更新后还能正常转换，建议您直接使用新版本的 checkpoints。\n\n```shell\npython tools/model_converters/upgrade_model_version.py ${IN_FILE} ${OUT_FILE} [-h] [--num-classes NUM_CLASSES]\n```\n\n### RegNet 模型转换至 MMDetection 模型\n\n`tools/model_converters/regnet2mmdet.py` 将 pycls 编码的预训练 RegNet 模型转换为 MMDetection 风格。\n\n```shell\npython tools/model_converters/regnet2mmdet.py ${SRC} ${DST} [-h]\n```\n\n### Detectron ResNet 模型转换至 Pytorch 模型\n\n`tools/model_converters/detectron2pytorch.py` 将 detectron 的原始预训练 RegNet 模型转换为 MMDetection 风格。\n\n```shell\npython tools/model_converters/detectron2pytorch.py ${SRC} ${DST} ${DEPTH} [-h]\n```\n\n### 制作发布用模型\n\n`tools/model_converters/publish_model.py` 可用来制作一个发布用的模型。\n\n在发布模型至 AWS 之前，你可能需要：\n\n1. 将模型转换至 CPU 张量\n2. 删除优化器状态\n3. 计算 checkpoint 文件的 hash 值，并将 hash 号码记录至文件名。\n\n```shell\npython tools/model_converters/publish_model.py ${INPUT_FILENAME} ${OUTPUT_FILENAME}\n```\n\n样例：\n\n```shell\npython tools/model_converters/publish_model.py work_dirs/faster_rcnn/latest.pth faster_rcnn_r50_fpn_1x_20190801.pth\n```\n\n最后输出的文件名如下所示： `faster_rcnn_r50_fpn_1x_20190801-{hash id}.pth`.\n\n## 数据集转换\n\n`tools/data_converters/` 提供了将 Cityscapes 数据集与 Pascal VOC 数据集转换至 COCO 数据集格式的工具\n\n```shell\npython tools/dataset_converters/cityscapes.py ${CITYSCAPES_PATH} [-h] [--img-dir ${IMG_DIR}] [--gt-dir ${GT_DIR}] [-o ${OUT_DIR}] [--nproc ${NPROC}]\npython tools/dataset_converters/pascal_voc.py ${DEVKIT_PATH} [-h] [-o ${OUT_DIR}]\n```\n\n## 数据集下载\n\n`tools/misc/download_dataset.py` 可以下载各类形如 COCO， VOC， LVIS 数据集。\n\n```shell\npython tools/misc/download_dataset.py --dataset-name coco2017\npython tools/misc/download_dataset.py --dataset-name voc2007\npython tools/misc/download_dataset.py --dataset-name lvis\n```\n\n对于中国境内的用户，我们也推荐使用开源数据平台 [OpenDataLab](https://opendatalab.com/?source=OpenMMLab%20GitHub) 来获取这些数据集，以获得更好的下载体验:\n\n- [COCO2017](https://opendatalab.com/COCO_2017/download?source=OpenMMLab%20GitHub)\n- [VOC2007](https://opendatalab.com/PASCAL_VOC2007/download?source=OpenMMLab%20GitHub)\n- [VOC2012](https://opendatalab.com/PASCAL_VOC2012/download?source=OpenMMLab%20GitHub)\n- [LVIS](https://opendatalab.com/LVIS/download?source=OpenMMLab%20GitHub)\n\n## 基准测试\n\n### 鲁棒性测试基准\n\n`tools/analysis_tools/test_robustness.py` 及 `tools/analysis_tools/robustness_eval.py` 帮助使用者衡量模型的鲁棒性。其核心思想来源于 [Benchmarking Robustness in Object Detection: Autonomous Driving when Winter is Coming](https://arxiv.org/abs/1907.07484)。如果你想了解如何在污损图像上评估模型的效果，以及参考该基准的一组标准模型，请参照 [robustness_benchmarking.md](robustness_benchmarking.md)。\n\n### FPS 测试基准\n\n`tools/analysis_tools/benchmark.py` 可帮助使用者计算 FPS，FPS 计算包括了模型向前传播与后处理过程。为了得到更精确的计算值，现在的分布式计算模式只支持一个 GPU。\n\n```shell\npython -m torch.distributed.launch --nproc_per_node=1 --master_port=${PORT} tools/analysis_tools/benchmark.py \\\n    ${CONFIG} \\\n    [--checkpoint ${CHECKPOINT}] \\\n    [--repeat-num ${REPEAT_NUM}] \\\n    [--max-iter ${MAX_ITER}] \\\n    [--log-interval ${LOG_INTERVAL}] \\\n    --launcher pytorch\n```\n\n样例：假设你已经下载了 `Faster R-CNN` 模型 checkpoint 并放置在 `checkpoints/` 目录下。\n\n```shell\npython -m torch.distributed.launch --nproc_per_node=1 --master_port=29500 tools/analysis_tools/benchmark.py \\\n       configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py \\\n       checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth \\\n       --launcher pytorch\n```\n\n## 更多工具\n\n### 以某个评估标准进行评估\n\n`tools/analysis_tools/eval_metric.py` 根据配置文件中的评估方式对 pkl 结果文件进行评估。\n\n```shell\npython tools/analysis_tools/eval_metric.py ${CONFIG} ${PKL_RESULTS} [-h] [--format-only] [--eval ${EVAL[EVAL ...]}]\n                      [--cfg-options ${CFG_OPTIONS [CFG_OPTIONS ...]}]\n                      [--eval-options ${EVAL_OPTIONS [EVAL_OPTIONS ...]}]\n```\n\n### 打印全部 config\n\n`tools/misc/print_config.py` 可将所有配置继承关系展开，完全打印相应的配置文件。\n\n```shell\npython tools/misc/print_config.py ${CONFIG} [-h] [--options ${OPTIONS [OPTIONS...]}]\n```\n\n## 超参数优化\n\n### YOLO Anchor 优化\n\n`tools/analysis_tools/optimize_anchors.py` 提供了两种方法优化 YOLO 的 anchors。\n\n其中一种方法使用 K 均值 anchor 聚类（k-means anchor cluster），源自 [darknet](https://github.com/AlexeyAB/darknet/blob/master/src/detector.c#L1421)。\n\n```shell\npython tools/analysis_tools/optimize_anchors.py ${CONFIG} --algorithm k-means --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} --output-dir ${OUTPUT_DIR}\n```\n\n另一种方法使用差分进化算法优化 anchors。\n\n```shell\npython tools/analysis_tools/optimize_anchors.py ${CONFIG} --algorithm differential_evolution --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} --output-dir ${OUTPUT_DIR}\n```\n\n样例：\n\n```shell\npython tools/analysis_tools/optimize_anchors.py configs/yolo/yolov3_d53_8xb8-320-273e_coco.py --algorithm differential_evolution --input-shape 608 608 --device cuda --output-dir work_dirs\n```\n\n你可能会看到如下结果：\n\n```\nloading annotations into memory...\nDone (t=9.70s)\ncreating index...\nindex created!\n2021-07-19 19:37:20,951 - mmdet - INFO - Collecting bboxes from annotation...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 117266/117266, 15874.5 task/s, elapsed: 7s, ETA:     0s\n\n2021-07-19 19:37:28,753 - mmdet - INFO - Collected 849902 bboxes.\ndifferential_evolution step 1: f(x)= 0.506055\ndifferential_evolution step 2: f(x)= 0.506055\n......\n\ndifferential_evolution step 489: f(x)= 0.386625\n2021-07-19 19:46:40,775 - mmdet - INFO Anchor evolution finish. Average IOU: 0.6133754253387451\n2021-07-19 19:46:40,776 - mmdet - INFO Anchor differential evolution result:[[10, 12], [15, 30], [32, 22], [29, 59], [61, 46], [57, 116], [112, 89], [154, 198], [349, 336]]\n2021-07-19 19:46:40,798 - mmdet - INFO Result saved in work_dirs/anchor_optimize_result.json\n```\n\n## 混淆矩阵\n\n混淆矩阵是对检测结果的概览。\n`tools/analysis_tools/confusion_matrix.py` 可对预测结果进行分析，绘制成混淆矩阵表。\n首先，运行 `tools/test.py` 保存 `.pkl` 预测结果。\n之后再运行：\n\n```\npython tools/analysis_tools/confusion_matrix.py ${CONFIG}  ${DETECTION_RESULTS}  ${SAVE_DIR} --show\n```\n\n最后你可以得到如图的混淆矩阵：\n\n![confusion_matrix_example](https://user-images.githubusercontent.com/12907710/140513068-994cdbf4-3a4a-48f0-8fd8-2830d93fd963.png)\n\n## COCO 分离和遮挡实例分割性能评估\n\n对于最先进的目标检测器来说，检测被遮挡的物体仍然是一个挑战。\n我们实现了论文 [A Tri-Layer Plugin to Improve Occluded Detection](https://arxiv.org/abs/2210.10046) 中提出的指标来计算分离和遮挡目标的召回率。\n\n使用此评价指标有两种方法：\n\n### 离线评测\n\n我们提供了一个脚本对存储后的检测结果文件计算指标。\n\n首先，使用 `tools/test.py` 脚本存储检测结果：\n\n```shell\npython tools/test.py ${CONFIG} ${MODEL_PATH} --out results.pkl\n```\n\n然后，运行 `tools/analysis_tools/coco_occluded_separated_recall.py` 脚本来计算分离和遮挡目标的掩码的召回率:\n\n```shell\npython tools/analysis_tools/coco_occluded_separated_recall.py results.pkl --out occluded_separated_recall.json\n```\n\n输出如下：\n\n```\nloading annotations into memory...\nDone (t=0.51s)\ncreating index...\nindex created!\nprocessing detection results...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 5000/5000, 109.3 task/s, elapsed: 46s, ETA:     0s\ncomputing occluded mask recall...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 5550/5550, 780.5 task/s, elapsed: 7s, ETA:     0s\nCOCO occluded mask recall: 58.79%\nCOCO occluded mask success num: 3263\ncomputing separated mask recall...\n[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 3522/3522, 778.3 task/s, elapsed: 5s, ETA:     0s\nCOCO separated mask recall: 31.94%\nCOCO separated mask success num: 1125\n\n+-----------+--------+-------------+\n| mask type | recall | num correct |\n+-----------+--------+-------------+\n| occluded  | 58.79% | 3263        |\n| separated | 31.94% | 1125        |\n+-----------+--------+-------------+\nEvaluation results have been saved to occluded_separated_recall.json.\n```\n\n### 在线评测\n\n我们实现继承自 `CocoMetic` 的 `CocoOccludedSeparatedMetric`。\n要在训练期间评估分离和遮挡掩码的召回率，只需在配置中将 evaluator 类型替换为 `CocoOccludedSeparatedMetric`：\n\n```python\nval_evaluator = dict(\n    type='CocoOccludedSeparatedMetric',  # 修改此处\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False)\ntest_evaluator = val_evaluator\n```\n\n如果您使用了此指标，请引用论文：\n\n```latex\n@article{zhan2022triocc,\n    title={A Tri-Layer Plugin to Improve Occluded Detection},\n    author={Zhan, Guanqi and Xie, Weidi and Zisserman, Andrew},\n    journal={British Machine Vision Conference},\n    year={2022}\n}\n```\n"
  },
  {
    "path": "docs/zh_cn/user_guides/visualization.md",
    "content": "# 可视化\n\n在阅读本教程之前，建议先阅读 MMEngine 的 [Visualization](https://github.com/open-mmlab/mmengine/blob/main/docs/en/advanced_tutorials/visualization.md) 文档，以对 `Visualizer` 的定义和用法有一个初步的了解。\n\n简而言之，`Visualizer` 在 MMEngine 中实现以满足日常可视化需求，并包含以下三个主要功能：\n\n- 实现通用的绘图 API，例如 [`draw_bboxes`](mmengine.visualization.Visualizer.draw_bboxes) 实现了绘制边界框的功能，[`draw_lines`](mmengine.visualization.Visualizer.draw_lines) 实现了绘制线条的功能。\n- 支持将可视化结果、学习率曲线、损失函数曲线以及验证精度曲线写入到各种后端中，包括本地磁盘以及常见的深度学习训练日志工具，例如 [TensorBoard](https://www.tensorflow.org/tensorboard) 和 [Wandb](https://wandb.ai/site)。\n- 支持在代码的任何位置调用以可视化或记录模型在训练或测试期间的中间状态，例如特征图和验证结果。\n\n基于 MMEngine 的 `Visualizer`，MMDet 提供了各种预构建的可视化工具，用户可以通过简单地修改以下配置文件来使用它们。\n\n- `tools/analysis_tools/browse_dataset.py` 脚本提供了一个数据集可视化功能，可以在数据经过数据转换后绘制图像和相应的注释，具体描述请参见[`browse_dataset.py`](useful_tools.md#Visualization)。\n\n- MMEngine实现了`LoggerHook`，使用`Visualizer`将学习率、损失和评估结果写入由`Visualizer`设置的后端。因此，通过修改配置文件中的`Visualizer`后端，例如修改为`TensorBoardVISBackend`或`WandbVISBackend`，可以实现日志记录到常用的训练日志工具，如`TensorBoard`或`WandB`，从而方便用户使用这些可视化工具来分析和监控训练过程。\n\n- 在MMDet中实现了`VisualizerHook`，它使用`Visualizer`将验证或预测阶段的预测结果可视化或存储到由`Visualizer`设置的后端。因此，通过修改配置文件中的`Visualizer`后端，例如修改为`TensorBoardVISBackend`或`WandbVISBackend`，可以将预测图像存储到`TensorBoard`或`Wandb`中。\n\n## 配置\n\n由于使用了注册机制，在MMDet中我们可以通过修改配置文件来设置`Visualizer`的行为。通常，我们会在`configs/_base_/default_runtime.py`中为可视化器定义默认配置，详细信息请参见[配置教程](config.md)。\n\n```Python\nvis_backends = [dict(type='LocalVisBackend')]\nvisualizer = dict(\n    type='DetLocalVisualizer',\n    vis_backends=vis_backends,\n    name='visualizer')\n```\n\n基于上面的例子，我们可以看到`Visualizer`的配置由两个主要部分组成，即`Visualizer`类型和其使用的可视化后端`vis_backends`。\n\n- 用户可直接使用`DetLocalVisualizer`来可视化支持任务的标签或预测结果。\n- MMDet默认将可视化后端`vis_backend`设置为本地可视化后端`LocalVisBackend`，将所有可视化结果和其他训练信息保存在本地文件夹中。\n\n## 存储\n\nMMDet默认使用本地可视化后端[`LocalVisBackend`](mmengine.visualization.LocalVisBackend)，`VisualizerHook`和`LoggerHook`中存储的模型损失、学习率、模型评估精度和可视化信息，包括损失、学习率、评估精度将默认保存到`{work_dir}/{config_name}/{time}/{vis_data}`文件夹中。此外，MMDet还支持其他常见的可视化后端，例如`TensorboardVisBackend`和`WandbVisBackend`，您只需要在配置文件中更改`vis_backends`类型为相应的可视化后端即可。例如，只需在配置文件中插入以下代码块即可将数据存储到`TensorBoard`和`Wandb`中。\n\n```Python\n# https://mmengine.readthedocs.io/en/latest/api/visualization.html\n_base_.visualizer.vis_backends = [\n    dict(type='LocalVisBackend'), #\n    dict(type='TensorboardVisBackend'),\n    dict(type='WandbVisBackend'),]\n```\n\n## 绘图\n\n### 绘制预测结果\n\nMMDet主要使用[`DetVisualizationHook`](mmdet.engine.hooks.DetVisualizationHook)来绘制验证和测试的预测结果，默认情况下`DetVisualizationHook`是关闭的，其默认配置如下。\n\n```Python\nvisualization=dict( #用户可视化验证和测试结果\n    type='DetVisualizationHook',\n    draw=False,\n    interval=1,\n    show=False)\n```\n\n以下表格展示了`DetVisualizationHook`支持的参数。\n\n|   参数   |                                       描述                                       |\n| :------: | :------------------------------------------------------------------------------: |\n|   draw   |          DetVisualizationHook通过enable参数打开和关闭，默认状态为关闭。          |\n| interval | 控制在DetVisualizationHook启用时存储或显示验证或测试结果的间隔，单位为迭代次数。 |\n|   show   |                         控制是否可视化验证或测试的结果。                         |\n\n如果您想在训练或测试期间启用 `DetVisualizationHook` 相关功能和配置，您只需要修改配置文件，以 `configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py` 为例，同时绘制注释和预测，并显示图像，配置文件可以修改如下：\n\n```Python\nvisualization = _base_.default_hooks.visualization\nvisualization.update(dict(draw=True, show=True))\n```\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/224883427-1294a7ba-14ab-4d93-9152-55a7b270b1f1.png\" height=\"300\"/>\n</div>\n\n`test.py`程序提供了`--show`和`--show-dir`参数，可以在测试过程中可视化注释和预测结果，而不需要修改配置文件，从而进一步简化了测试过程。\n\n```Shell\n# 展示测试结果\npython tools/test.py configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_tiny_8xb32-300e_coco/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth --show\n\n# 指定存储预测结果的位置\npython tools/test.py configs/rtmdet/rtmdet_tiny_8xb32-300e_coco.py https://download.openmmlab.com/mmdetection/v3.0/rtmdet/rtmdet_tiny_8xb32-300e_coco/rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth --show-dir imgs/\n```\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/17425982/224883427-1294a7ba-14ab-4d93-9152-55a7b270b1f1.png\" height=\"300\"/>\n</div>\n"
  },
  {
    "path": "mmdet/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport mmcv\nimport mmengine\nfrom mmengine.utils import digit_version\n\nfrom .version import __version__, version_info\n\nmmcv_minimum_version = '2.0.0rc4'\nmmcv_maximum_version = '2.2.0'\nmmcv_version = digit_version(mmcv.__version__)\n\nmmengine_minimum_version = '0.7.1'\nmmengine_maximum_version = '1.0.0'\nmmengine_version = digit_version(mmengine.__version__)\n\nassert (mmcv_version >= digit_version(mmcv_minimum_version)\n        and mmcv_version < digit_version(mmcv_maximum_version)), \\\n    f'MMCV=={mmcv.__version__} is used but incompatible. ' \\\n    f'Please install mmcv>={mmcv_minimum_version}, <{mmcv_maximum_version}.'\n\nassert (mmengine_version >= digit_version(mmengine_minimum_version)\n        and mmengine_version < digit_version(mmengine_maximum_version)), \\\n    f'MMEngine=={mmengine.__version__} is used but incompatible. ' \\\n    f'Please install mmengine>={mmengine_minimum_version}, ' \\\n    f'<{mmengine_maximum_version}.'\n\n__all__ = ['__version__', 'version_info', 'digit_version']\n"
  },
  {
    "path": "mmdet/apis/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .det_inferencer import DetInferencer\nfrom .inference import (async_inference_detector, inference_detector,\n                        inference_mot, init_detector, init_track_model)\n\n__all__ = [\n    'init_detector', 'async_inference_detector', 'inference_detector',\n    'DetInferencer', 'inference_mot', 'init_track_model'\n]\n"
  },
  {
    "path": "mmdet/apis/det_inferencer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nimport warnings\nfrom typing import Dict, Iterable, List, Optional, Sequence, Tuple, Union\n\nimport mmcv\nimport mmengine\nimport numpy as np\nimport torch.nn as nn\nfrom mmcv.transforms import LoadImageFromFile\nfrom mmengine.dataset import Compose\nfrom mmengine.fileio import (get_file_backend, isdir, join_path,\n                             list_dir_or_file)\nfrom mmengine.infer.infer import BaseInferencer, ModelType\nfrom mmengine.model.utils import revert_sync_batchnorm\nfrom mmengine.registry import init_default_scope\nfrom mmengine.runner.checkpoint import _load_checkpoint_to_model\nfrom mmengine.visualization import Visualizer\nfrom rich.progress import track\n\nfrom mmdet.evaluation import INSTANCE_OFFSET\nfrom mmdet.registry import DATASETS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.mask import encode_mask_results, mask2bbox\nfrom mmdet.utils import ConfigType\nfrom ..evaluation import get_classes\n\ntry:\n    from panopticapi.evaluation import VOID\n    from panopticapi.utils import id2rgb\nexcept ImportError:\n    id2rgb = None\n    VOID = None\n\nInputType = Union[str, np.ndarray]\nInputsType = Union[InputType, Sequence[InputType]]\nPredType = List[DetDataSample]\nImgType = Union[np.ndarray, Sequence[np.ndarray]]\n\nIMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif',\n                  '.tiff', '.webp')\n\n\nclass DetInferencer(BaseInferencer):\n    \"\"\"Object Detection Inferencer.\n\n    Args:\n        model (str, optional): Path to the config file or the model name\n            defined in metafile. For example, it could be\n            \"rtmdet-s\" or 'rtmdet_s_8xb32-300e_coco' or\n            \"configs/rtmdet/rtmdet_s_8xb32-300e_coco.py\".\n            If model is not specified, user must provide the\n            `weights` saved by MMEngine which contains the config string.\n            Defaults to None.\n        weights (str, optional): Path to the checkpoint. If it is not specified\n            and model is a model name of metafile, the weights will be loaded\n            from metafile. Defaults to None.\n        device (str, optional): Device to run inference. If None, the available\n            device will be automatically used. Defaults to None.\n        scope (str, optional): The scope of the model. Defaults to mmdet.\n        palette (str): Color palette used for visualization. The order of\n            priority is palette -> config -> checkpoint. Defaults to 'none'.\n        show_progress (bool): Control whether to display the progress\n            bar during the inference process. Defaults to True.\n    \"\"\"\n\n    preprocess_kwargs: set = set()\n    forward_kwargs: set = set()\n    visualize_kwargs: set = {\n        'return_vis',\n        'show',\n        'wait_time',\n        'draw_pred',\n        'pred_score_thr',\n        'img_out_dir',\n        'no_save_vis',\n    }\n    postprocess_kwargs: set = {\n        'print_result',\n        'pred_out_dir',\n        'return_datasamples',\n        'no_save_pred',\n    }\n\n    def __init__(self,\n                 model: Optional[Union[ModelType, str]] = None,\n                 weights: Optional[str] = None,\n                 device: Optional[str] = None,\n                 scope: Optional[str] = 'mmdet',\n                 palette: str = 'none',\n                 show_progress: bool = True) -> None:\n        # A global counter tracking the number of images processed, for\n        # naming of the output images\n        self.num_visualized_imgs = 0\n        self.num_predicted_imgs = 0\n        self.palette = palette\n        init_default_scope(scope)\n        super().__init__(\n            model=model, weights=weights, device=device, scope=scope)\n        self.model = revert_sync_batchnorm(self.model)\n        self.show_progress = show_progress\n\n    def _load_weights_to_model(self, model: nn.Module,\n                               checkpoint: Optional[dict],\n                               cfg: Optional[ConfigType]) -> None:\n        \"\"\"Loading model weights and meta information from cfg and checkpoint.\n\n        Args:\n            model (nn.Module): Model to load weights and meta information.\n            checkpoint (dict, optional): The loaded checkpoint.\n            cfg (Config or ConfigDict, optional): The loaded config.\n        \"\"\"\n\n        if checkpoint is not None:\n            _load_checkpoint_to_model(model, checkpoint)\n            checkpoint_meta = checkpoint.get('meta', {})\n            # save the dataset_meta in the model for convenience\n            if 'dataset_meta' in checkpoint_meta:\n                # mmdet 3.x, all keys should be lowercase\n                model.dataset_meta = {\n                    k.lower(): v\n                    for k, v in checkpoint_meta['dataset_meta'].items()\n                }\n            elif 'CLASSES' in checkpoint_meta:\n                # < mmdet 3.x\n                classes = checkpoint_meta['CLASSES']\n                model.dataset_meta = {'classes': classes}\n            else:\n                warnings.warn(\n                    'dataset_meta or class names are not saved in the '\n                    'checkpoint\\'s meta data, use COCO classes by default.')\n                model.dataset_meta = {'classes': get_classes('coco')}\n        else:\n            warnings.warn('Checkpoint is not loaded, and the inference '\n                          'result is calculated by the randomly initialized '\n                          'model!')\n            warnings.warn('weights is None, use COCO classes by default.')\n            model.dataset_meta = {'classes': get_classes('coco')}\n\n        # Priority:  args.palette -> config -> checkpoint\n        if self.palette != 'none':\n            model.dataset_meta['palette'] = self.palette\n        else:\n            test_dataset_cfg = copy.deepcopy(cfg.test_dataloader.dataset)\n            # lazy init. We only need the metainfo.\n            test_dataset_cfg['lazy_init'] = True\n            metainfo = DATASETS.build(test_dataset_cfg).metainfo\n            cfg_palette = metainfo.get('palette', None)\n            if cfg_palette is not None:\n                model.dataset_meta['palette'] = cfg_palette\n            else:\n                if 'palette' not in model.dataset_meta:\n                    warnings.warn(\n                        'palette does not exist, random is used by default. '\n                        'You can also set the palette to customize.')\n                    model.dataset_meta['palette'] = 'random'\n\n    def _init_pipeline(self, cfg: ConfigType) -> Compose:\n        \"\"\"Initialize the test pipeline.\"\"\"\n        pipeline_cfg = cfg.test_dataloader.dataset.pipeline\n\n        # For inference, the key of ``img_id`` is not used.\n        if 'meta_keys' in pipeline_cfg[-1]:\n            pipeline_cfg[-1]['meta_keys'] = tuple(\n                meta_key for meta_key in pipeline_cfg[-1]['meta_keys']\n                if meta_key != 'img_id')\n\n        load_img_idx = self._get_transform_idx(\n            pipeline_cfg, ('LoadImageFromFile', LoadImageFromFile))\n        if load_img_idx == -1:\n            raise ValueError(\n                'LoadImageFromFile is not found in the test pipeline')\n        pipeline_cfg[load_img_idx]['type'] = 'mmdet.InferencerLoader'\n        return Compose(pipeline_cfg)\n\n    def _get_transform_idx(self, pipeline_cfg: ConfigType,\n                           name: Union[str, Tuple[str, type]]) -> int:\n        \"\"\"Returns the index of the transform in a pipeline.\n\n        If the transform is not found, returns -1.\n        \"\"\"\n        for i, transform in enumerate(pipeline_cfg):\n            if transform['type'] in name:\n                return i\n        return -1\n\n    def _init_visualizer(self, cfg: ConfigType) -> Optional[Visualizer]:\n        \"\"\"Initialize visualizers.\n\n        Args:\n            cfg (ConfigType): Config containing the visualizer information.\n\n        Returns:\n            Visualizer or None: Visualizer initialized with config.\n        \"\"\"\n        visualizer = super()._init_visualizer(cfg)\n        visualizer.dataset_meta = self.model.dataset_meta\n        return visualizer\n\n    def _inputs_to_list(self, inputs: InputsType) -> list:\n        \"\"\"Preprocess the inputs to a list.\n\n        Preprocess inputs to a list according to its type:\n\n        - list or tuple: return inputs\n        - str:\n            - Directory path: return all files in the directory\n            - other cases: return a list containing the string. The string\n              could be a path to file, a url or other types of string according\n              to the task.\n\n        Args:\n            inputs (InputsType): Inputs for the inferencer.\n\n        Returns:\n            list: List of input for the :meth:`preprocess`.\n        \"\"\"\n        if isinstance(inputs, str):\n            backend = get_file_backend(inputs)\n            if hasattr(backend, 'isdir') and isdir(inputs):\n                # Backends like HttpsBackend do not implement `isdir`, so only\n                # those backends that implement `isdir` could accept the inputs\n                # as a directory\n                filename_list = list_dir_or_file(\n                    inputs, list_dir=False, suffix=IMG_EXTENSIONS)\n                inputs = [\n                    join_path(inputs, filename) for filename in filename_list\n                ]\n\n        if not isinstance(inputs, (list, tuple)):\n            inputs = [inputs]\n\n        return list(inputs)\n\n    def preprocess(self, inputs: InputsType, batch_size: int = 1, **kwargs):\n        \"\"\"Process the inputs into a model-feedable format.\n\n        Customize your preprocess by overriding this method. Preprocess should\n        return an iterable object, of which each item will be used as the\n        input of ``model.test_step``.\n\n        ``BaseInferencer.preprocess`` will return an iterable chunked data,\n        which will be used in __call__ like this:\n\n        .. code-block:: python\n\n            def __call__(self, inputs, batch_size=1, **kwargs):\n                chunked_data = self.preprocess(inputs, batch_size, **kwargs)\n                for batch in chunked_data:\n                    preds = self.forward(batch, **kwargs)\n\n        Args:\n            inputs (InputsType): Inputs given by user.\n            batch_size (int): batch size. Defaults to 1.\n\n        Yields:\n            Any: Data processed by the ``pipeline`` and ``collate_fn``.\n        \"\"\"\n        chunked_data = self._get_chunk_data(inputs, batch_size)\n        yield from map(self.collate_fn, chunked_data)\n\n    def _get_chunk_data(self, inputs: Iterable, chunk_size: int):\n        \"\"\"Get batch data from inputs.\n\n        Args:\n            inputs (Iterable): An iterable dataset.\n            chunk_size (int): Equivalent to batch size.\n\n        Yields:\n            list: batch data.\n        \"\"\"\n        inputs_iter = iter(inputs)\n        while True:\n            try:\n                chunk_data = []\n                for _ in range(chunk_size):\n                    inputs_ = next(inputs_iter)\n                    if isinstance(inputs_, dict):\n                        if 'img' in inputs_:\n                            ori_inputs_ = inputs_['img']\n                        else:\n                            ori_inputs_ = inputs_['img_path']\n                        chunk_data.append(\n                            (ori_inputs_,\n                             self.pipeline(copy.deepcopy(inputs_))))\n                    else:\n                        chunk_data.append((inputs_, self.pipeline(inputs_)))\n                yield chunk_data\n            except StopIteration:\n                if chunk_data:\n                    yield chunk_data\n                break\n\n    # TODO: Video and Webcam are currently not supported and\n    #  may consume too much memory if your input folder has a lot of images.\n    #  We will be optimized later.\n    def __call__(\n            self,\n            inputs: InputsType,\n            batch_size: int = 1,\n            return_vis: bool = False,\n            show: bool = False,\n            wait_time: int = 0,\n            no_save_vis: bool = False,\n            draw_pred: bool = True,\n            pred_score_thr: float = 0.3,\n            return_datasamples: bool = False,\n            print_result: bool = False,\n            no_save_pred: bool = True,\n            out_dir: str = '',\n            # by open image task\n            texts: Optional[Union[str, list]] = None,\n            # by open panoptic task\n            stuff_texts: Optional[Union[str, list]] = None,\n            # by GLIP and Grounding DINO\n            custom_entities: bool = False,\n            # by Grounding DINO\n            tokens_positive: Optional[Union[int, list]] = None,\n            **kwargs) -> dict:\n        \"\"\"Call the inferencer.\n\n        Args:\n            inputs (InputsType): Inputs for the inferencer.\n            batch_size (int): Inference batch size. Defaults to 1.\n            show (bool): Whether to display the visualization results in a\n                popup window. Defaults to False.\n            wait_time (float): The interval of show (s). Defaults to 0.\n            no_save_vis (bool): Whether to force not to save prediction\n                vis results. Defaults to False.\n            draw_pred (bool): Whether to draw predicted bounding boxes.\n                Defaults to True.\n            pred_score_thr (float): Minimum score of bboxes to draw.\n                Defaults to 0.3.\n            return_datasamples (bool): Whether to return results as\n                :obj:`DetDataSample`. Defaults to False.\n            print_result (bool): Whether to print the inference result w/o\n                visualization to the console. Defaults to False.\n            no_save_pred (bool): Whether to force not to save prediction\n                results. Defaults to True.\n            out_dir: Dir to save the inference results or\n                visualization. If left as empty, no file will be saved.\n                Defaults to ''.\n            texts (str | list[str]): Text prompts. Defaults to None.\n            stuff_texts (str | list[str]): Stuff text prompts of open\n                panoptic task. Defaults to None.\n            custom_entities (bool): Whether to use custom entities.\n                Defaults to False. Only used in GLIP and Grounding DINO.\n            **kwargs: Other keyword arguments passed to :meth:`preprocess`,\n                :meth:`forward`, :meth:`visualize` and :meth:`postprocess`.\n                Each key in kwargs should be in the corresponding set of\n                ``preprocess_kwargs``, ``forward_kwargs``, ``visualize_kwargs``\n                and ``postprocess_kwargs``.\n\n        Returns:\n            dict: Inference and visualization results.\n        \"\"\"\n        (\n            preprocess_kwargs,\n            forward_kwargs,\n            visualize_kwargs,\n            postprocess_kwargs,\n        ) = self._dispatch_kwargs(**kwargs)\n\n        ori_inputs = self._inputs_to_list(inputs)\n\n        if texts is not None and isinstance(texts, str):\n            texts = [texts] * len(ori_inputs)\n        if stuff_texts is not None and isinstance(stuff_texts, str):\n            stuff_texts = [stuff_texts] * len(ori_inputs)\n\n        # Currently only supports bs=1\n        tokens_positive = [tokens_positive] * len(ori_inputs)\n\n        if texts is not None:\n            assert len(texts) == len(ori_inputs)\n            for i in range(len(texts)):\n                if isinstance(ori_inputs[i], str):\n                    ori_inputs[i] = {\n                        'text': texts[i],\n                        'img_path': ori_inputs[i],\n                        'custom_entities': custom_entities,\n                        'tokens_positive': tokens_positive[i]\n                    }\n                else:\n                    ori_inputs[i] = {\n                        'text': texts[i],\n                        'img': ori_inputs[i],\n                        'custom_entities': custom_entities,\n                        'tokens_positive': tokens_positive[i]\n                    }\n        if stuff_texts is not None:\n            assert len(stuff_texts) == len(ori_inputs)\n            for i in range(len(stuff_texts)):\n                ori_inputs[i]['stuff_text'] = stuff_texts[i]\n\n        inputs = self.preprocess(\n            ori_inputs, batch_size=batch_size, **preprocess_kwargs)\n\n        results_dict = {'predictions': [], 'visualization': []}\n        for ori_imgs, data in (track(inputs, description='Inference')\n                               if self.show_progress else inputs):\n            preds = self.forward(data, **forward_kwargs)\n            visualization = self.visualize(\n                ori_imgs,\n                preds,\n                return_vis=return_vis,\n                show=show,\n                wait_time=wait_time,\n                draw_pred=draw_pred,\n                pred_score_thr=pred_score_thr,\n                no_save_vis=no_save_vis,\n                img_out_dir=out_dir,\n                **visualize_kwargs)\n            results = self.postprocess(\n                preds,\n                visualization,\n                return_datasamples=return_datasamples,\n                print_result=print_result,\n                no_save_pred=no_save_pred,\n                pred_out_dir=out_dir,\n                **postprocess_kwargs)\n            results_dict['predictions'].extend(results['predictions'])\n            if results['visualization'] is not None:\n                results_dict['visualization'].extend(results['visualization'])\n        return results_dict\n\n    def visualize(self,\n                  inputs: InputsType,\n                  preds: PredType,\n                  return_vis: bool = False,\n                  show: bool = False,\n                  wait_time: int = 0,\n                  draw_pred: bool = True,\n                  pred_score_thr: float = 0.3,\n                  no_save_vis: bool = False,\n                  img_out_dir: str = '',\n                  **kwargs) -> Union[List[np.ndarray], None]:\n        \"\"\"Visualize predictions.\n\n        Args:\n            inputs (List[Union[str, np.ndarray]]): Inputs for the inferencer.\n            preds (List[:obj:`DetDataSample`]): Predictions of the model.\n            return_vis (bool): Whether to return the visualization result.\n                Defaults to False.\n            show (bool): Whether to display the image in a popup window.\n                Defaults to False.\n            wait_time (float): The interval of show (s). Defaults to 0.\n            draw_pred (bool): Whether to draw predicted bounding boxes.\n                Defaults to True.\n            pred_score_thr (float): Minimum score of bboxes to draw.\n                Defaults to 0.3.\n            no_save_vis (bool): Whether to force not to save prediction\n                vis results. Defaults to False.\n            img_out_dir (str): Output directory of visualization results.\n                If left as empty, no file will be saved. Defaults to ''.\n\n        Returns:\n            List[np.ndarray] or None: Returns visualization results only if\n            applicable.\n        \"\"\"\n        if no_save_vis is True:\n            img_out_dir = ''\n\n        if not show and img_out_dir == '' and not return_vis:\n            return None\n\n        if self.visualizer is None:\n            raise ValueError('Visualization needs the \"visualizer\" term'\n                             'defined in the config, but got None.')\n\n        results = []\n\n        for single_input, pred in zip(inputs, preds):\n            if isinstance(single_input, str):\n                img_bytes = mmengine.fileio.get(single_input)\n                img = mmcv.imfrombytes(img_bytes)\n                img = img[:, :, ::-1]\n                img_name = osp.basename(single_input)\n            elif isinstance(single_input, np.ndarray):\n                img = single_input.copy()\n                img_num = str(self.num_visualized_imgs).zfill(8)\n                img_name = f'{img_num}.jpg'\n            else:\n                raise ValueError('Unsupported input type: '\n                                 f'{type(single_input)}')\n\n            out_file = osp.join(img_out_dir, 'vis',\n                                img_name) if img_out_dir != '' else None\n\n            self.visualizer.add_datasample(\n                img_name,\n                img,\n                pred,\n                show=show,\n                wait_time=wait_time,\n                draw_gt=False,\n                draw_pred=draw_pred,\n                pred_score_thr=pred_score_thr,\n                out_file=out_file,\n            )\n            results.append(self.visualizer.get_image())\n            self.num_visualized_imgs += 1\n\n        return results\n\n    def postprocess(\n        self,\n        preds: PredType,\n        visualization: Optional[List[np.ndarray]] = None,\n        return_datasamples: bool = False,\n        print_result: bool = False,\n        no_save_pred: bool = False,\n        pred_out_dir: str = '',\n        **kwargs,\n    ) -> Dict:\n        \"\"\"Process the predictions and visualization results from ``forward``\n        and ``visualize``.\n\n        This method should be responsible for the following tasks:\n\n        1. Convert datasamples into a json-serializable dict if needed.\n        2. Pack the predictions and visualization results and return them.\n        3. Dump or log the predictions.\n\n        Args:\n            preds (List[:obj:`DetDataSample`]): Predictions of the model.\n            visualization (Optional[np.ndarray]): Visualized predictions.\n            return_datasamples (bool): Whether to use Datasample to store\n                inference results. If False, dict will be used.\n            print_result (bool): Whether to print the inference result w/o\n                visualization to the console. Defaults to False.\n            no_save_pred (bool): Whether to force not to save prediction\n                results. Defaults to False.\n            pred_out_dir: Dir to save the inference results w/o\n                visualization. If left as empty, no file will be saved.\n                Defaults to ''.\n\n        Returns:\n            dict: Inference and visualization results with key ``predictions``\n            and ``visualization``.\n\n            - ``visualization`` (Any): Returned by :meth:`visualize`.\n            - ``predictions`` (dict or DataSample): Returned by\n                :meth:`forward` and processed in :meth:`postprocess`.\n                If ``return_datasamples=False``, it usually should be a\n                json-serializable dict containing only basic data elements such\n                as strings and numbers.\n        \"\"\"\n        if no_save_pred is True:\n            pred_out_dir = ''\n\n        result_dict = {}\n        results = preds\n        if not return_datasamples:\n            results = []\n            for pred in preds:\n                result = self.pred2dict(pred, pred_out_dir)\n                results.append(result)\n        elif pred_out_dir != '':\n            warnings.warn('Currently does not support saving datasample '\n                          'when return_datasamples is set to True. '\n                          'Prediction results are not saved!')\n        # Add img to the results after printing and dumping\n        result_dict['predictions'] = results\n        if print_result:\n            print(result_dict)\n        result_dict['visualization'] = visualization\n        return result_dict\n\n    # TODO: The data format and fields saved in json need further discussion.\n    #  Maybe should include model name, timestamp, filename, image info etc.\n    def pred2dict(self,\n                  data_sample: DetDataSample,\n                  pred_out_dir: str = '') -> Dict:\n        \"\"\"Extract elements necessary to represent a prediction into a\n        dictionary.\n\n        It's better to contain only basic data elements such as strings and\n        numbers in order to guarantee it's json-serializable.\n\n        Args:\n            data_sample (:obj:`DetDataSample`): Predictions of the model.\n            pred_out_dir: Dir to save the inference results w/o\n                visualization. If left as empty, no file will be saved.\n                Defaults to ''.\n\n        Returns:\n            dict: Prediction results.\n        \"\"\"\n        is_save_pred = True\n        if pred_out_dir == '':\n            is_save_pred = False\n\n        if is_save_pred and 'img_path' in data_sample:\n            img_path = osp.basename(data_sample.img_path)\n            img_path = osp.splitext(img_path)[0]\n            out_img_path = osp.join(pred_out_dir, 'preds',\n                                    img_path + '_panoptic_seg.png')\n            out_json_path = osp.join(pred_out_dir, 'preds', img_path + '.json')\n        elif is_save_pred:\n            out_img_path = osp.join(\n                pred_out_dir, 'preds',\n                f'{self.num_predicted_imgs}_panoptic_seg.png')\n            out_json_path = osp.join(pred_out_dir, 'preds',\n                                     f'{self.num_predicted_imgs}.json')\n            self.num_predicted_imgs += 1\n\n        result = {}\n        if 'pred_instances' in data_sample:\n            masks = data_sample.pred_instances.get('masks')\n            pred_instances = data_sample.pred_instances.numpy()\n            result = {\n                'labels': pred_instances.labels.tolist(),\n                'scores': pred_instances.scores.tolist()\n            }\n            if 'bboxes' in pred_instances:\n                result['bboxes'] = pred_instances.bboxes.tolist()\n            if masks is not None:\n                if 'bboxes' not in pred_instances or pred_instances.bboxes.sum(\n                ) == 0:\n                    # Fake bbox, such as the SOLO.\n                    bboxes = mask2bbox(masks.cpu()).numpy().tolist()\n                    result['bboxes'] = bboxes\n                encode_masks = encode_mask_results(pred_instances.masks)\n                for encode_mask in encode_masks:\n                    if isinstance(encode_mask['counts'], bytes):\n                        encode_mask['counts'] = encode_mask['counts'].decode()\n                result['masks'] = encode_masks\n\n        if 'pred_panoptic_seg' in data_sample:\n            if VOID is None:\n                raise RuntimeError(\n                    'panopticapi is not installed, please install it by: '\n                    'pip install git+https://github.com/cocodataset/'\n                    'panopticapi.git.')\n\n            pan = data_sample.pred_panoptic_seg.sem_seg.cpu().numpy()[0]\n            pan[pan % INSTANCE_OFFSET == len(\n                self.model.dataset_meta['classes'])] = VOID\n            pan = id2rgb(pan).astype(np.uint8)\n\n            if is_save_pred:\n                mmcv.imwrite(pan[:, :, ::-1], out_img_path)\n                result['panoptic_seg_path'] = out_img_path\n            else:\n                result['panoptic_seg'] = pan\n\n        if is_save_pred:\n            mmengine.dump(result, out_json_path)\n\n        return result\n"
  },
  {
    "path": "mmdet/apis/inference.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport warnings\nfrom pathlib import Path\nfrom typing import Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom mmcv.ops import RoIPool\nfrom mmcv.transforms import Compose\nfrom mmengine.config import Config\nfrom mmengine.dataset import default_collate\nfrom mmengine.model.utils import revert_sync_batchnorm\nfrom mmengine.registry import init_default_scope\nfrom mmengine.runner import load_checkpoint\n\nfrom mmdet.registry import DATASETS\nfrom mmdet.utils import ConfigType\nfrom ..evaluation import get_classes\nfrom ..registry import MODELS\nfrom ..structures import DetDataSample, SampleList\nfrom ..utils import get_test_pipeline_cfg\n\n\ndef init_detector(\n    config: Union[str, Path, Config],\n    checkpoint: Optional[str] = None,\n    palette: str = 'none',\n    device: str = 'cuda:0',\n    cfg_options: Optional[dict] = None,\n) -> nn.Module:\n    \"\"\"Initialize a detector from config file.\n\n    Args:\n        config (str, :obj:`Path`, or :obj:`mmengine.Config`): Config file path,\n            :obj:`Path`, or the config object.\n        checkpoint (str, optional): Checkpoint path. If left as None, the model\n            will not load any weights.\n        palette (str): Color palette used for visualization. If palette\n            is stored in checkpoint, use checkpoint's palette first, otherwise\n            use externally passed palette. Currently, supports 'coco', 'voc',\n            'citys' and 'random'. Defaults to none.\n        device (str): The device where the anchors will be put on.\n            Defaults to cuda:0.\n        cfg_options (dict, optional): Options to override some settings in\n            the used config.\n\n    Returns:\n        nn.Module: The constructed detector.\n    \"\"\"\n    if isinstance(config, (str, Path)):\n        config = Config.fromfile(config)\n    elif not isinstance(config, Config):\n        raise TypeError('config must be a filename or Config object, '\n                        f'but got {type(config)}')\n    if cfg_options is not None:\n        config.merge_from_dict(cfg_options)\n    elif 'init_cfg' in config.model.backbone:\n        config.model.backbone.init_cfg = None\n\n    scope = config.get('default_scope', 'mmdet')\n    if scope is not None:\n        init_default_scope(config.get('default_scope', 'mmdet'))\n\n    model = MODELS.build(config.model)\n    model = revert_sync_batchnorm(model)\n    if checkpoint is None:\n        warnings.simplefilter('once')\n        warnings.warn('checkpoint is None, use COCO classes by default.')\n        model.dataset_meta = {'classes': get_classes('coco')}\n    else:\n        checkpoint = load_checkpoint(model, checkpoint, map_location='cpu')\n        # Weights converted from elsewhere may not have meta fields.\n        checkpoint_meta = checkpoint.get('meta', {})\n\n        # save the dataset_meta in the model for convenience\n        if 'dataset_meta' in checkpoint_meta:\n            # mmdet 3.x, all keys should be lowercase\n            model.dataset_meta = {\n                k.lower(): v\n                for k, v in checkpoint_meta['dataset_meta'].items()\n            }\n        elif 'CLASSES' in checkpoint_meta:\n            # < mmdet 3.x\n            classes = checkpoint_meta['CLASSES']\n            model.dataset_meta = {'classes': classes}\n        else:\n            warnings.simplefilter('once')\n            warnings.warn(\n                'dataset_meta or class names are not saved in the '\n                'checkpoint\\'s meta data, use COCO classes by default.')\n            model.dataset_meta = {'classes': get_classes('coco')}\n\n    # Priority:  args.palette -> config -> checkpoint\n    if palette != 'none':\n        model.dataset_meta['palette'] = palette\n    else:\n        test_dataset_cfg = copy.deepcopy(config.test_dataloader.dataset)\n        # lazy init. We only need the metainfo.\n        test_dataset_cfg['lazy_init'] = True\n        metainfo = DATASETS.build(test_dataset_cfg).metainfo\n        cfg_palette = metainfo.get('palette', None)\n        if cfg_palette is not None:\n            model.dataset_meta['palette'] = cfg_palette\n        else:\n            if 'palette' not in model.dataset_meta:\n                warnings.warn(\n                    'palette does not exist, random is used by default. '\n                    'You can also set the palette to customize.')\n                model.dataset_meta['palette'] = 'random'\n\n    model.cfg = config  # save the config in the model for convenience\n    model.to(device)\n    model.eval()\n    return model\n\n\nImagesType = Union[str, np.ndarray, Sequence[str], Sequence[np.ndarray]]\n\n\ndef inference_detector(\n    model: nn.Module,\n    imgs: ImagesType,\n    test_pipeline: Optional[Compose] = None,\n    text_prompt: Optional[str] = None,\n    custom_entities: bool = False,\n) -> Union[DetDataSample, SampleList]:\n    \"\"\"Inference image(s) with the detector.\n\n    Args:\n        model (nn.Module): The loaded detector.\n        imgs (str, ndarray, Sequence[str/ndarray]):\n           Either image files or loaded images.\n        test_pipeline (:obj:`Compose`): Test pipeline.\n\n    Returns:\n        :obj:`DetDataSample` or list[:obj:`DetDataSample`]:\n        If imgs is a list or tuple, the same length list type results\n        will be returned, otherwise return the detection results directly.\n    \"\"\"\n\n    if isinstance(imgs, (list, tuple)):\n        is_batch = True\n    else:\n        imgs = [imgs]\n        is_batch = False\n\n    cfg = model.cfg\n\n    if test_pipeline is None:\n        cfg = cfg.copy()\n        test_pipeline = get_test_pipeline_cfg(cfg)\n        if isinstance(imgs[0], np.ndarray):\n            # Calling this method across libraries will result\n            # in module unregistered error if not prefixed with mmdet.\n            test_pipeline[0].type = 'mmdet.LoadImageFromNDArray'\n\n        test_pipeline = Compose(test_pipeline)\n\n    if model.data_preprocessor.device.type == 'cpu':\n        for m in model.modules():\n            assert not isinstance(\n                m, RoIPool\n            ), 'CPU inference with RoIPool is not supported currently.'\n\n    result_list = []\n    for i, img in enumerate(imgs):\n        # prepare data\n        if isinstance(img, np.ndarray):\n            # TODO: remove img_id.\n            data_ = dict(img=img, img_id=0)\n        else:\n            # TODO: remove img_id.\n            data_ = dict(img_path=img, img_id=0)\n\n        if text_prompt:\n            data_['text'] = text_prompt\n            data_['custom_entities'] = custom_entities\n\n        # build the data pipeline\n        data_ = test_pipeline(data_)\n\n        data_['inputs'] = [data_['inputs']]\n        data_['data_samples'] = [data_['data_samples']]\n\n        # forward the model\n        with torch.no_grad():\n            results = model.test_step(data_)[0]\n\n        result_list.append(results)\n\n    if not is_batch:\n        return result_list[0]\n    else:\n        return result_list\n\n\n# TODO: Awaiting refactoring\nasync def async_inference_detector(model, imgs):\n    \"\"\"Async inference image(s) with the detector.\n\n    Args:\n        model (nn.Module): The loaded detector.\n        img (str | ndarray): Either image files or loaded images.\n\n    Returns:\n        Awaitable detection results.\n    \"\"\"\n    if not isinstance(imgs, (list, tuple)):\n        imgs = [imgs]\n\n    cfg = model.cfg\n\n    if isinstance(imgs[0], np.ndarray):\n        cfg = cfg.copy()\n        # set loading pipeline type\n        cfg.data.test.pipeline[0].type = 'LoadImageFromNDArray'\n\n    # cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline)\n    test_pipeline = Compose(cfg.data.test.pipeline)\n\n    datas = []\n    for img in imgs:\n        # prepare data\n        if isinstance(img, np.ndarray):\n            # directly add img\n            data = dict(img=img)\n        else:\n            # add information into dict\n            data = dict(img_info=dict(filename=img), img_prefix=None)\n        # build the data pipeline\n        data = test_pipeline(data)\n        datas.append(data)\n\n    for m in model.modules():\n        assert not isinstance(\n            m,\n            RoIPool), 'CPU inference with RoIPool is not supported currently.'\n\n    # We don't restore `torch.is_grad_enabled()` value during concurrent\n    # inference since execution can overlap\n    torch.set_grad_enabled(False)\n    results = await model.aforward_test(data, rescale=True)\n    return results\n\n\ndef build_test_pipeline(cfg: ConfigType) -> ConfigType:\n    \"\"\"Build test_pipeline for mot/vis demo. In mot/vis infer, original\n    test_pipeline should remove the \"LoadImageFromFile\" and\n    \"LoadTrackAnnotations\".\n\n    Args:\n         cfg (ConfigDict): The loaded config.\n    Returns:\n         ConfigType: new test_pipeline\n    \"\"\"\n    # remove the \"LoadImageFromFile\" and \"LoadTrackAnnotations\" in pipeline\n    transform_broadcaster = cfg.test_dataloader.dataset.pipeline[0].copy()\n    for transform in transform_broadcaster['transforms']:\n        if transform['type'] == 'Resize':\n            transform_broadcaster['transforms'] = transform\n    pack_track_inputs = cfg.test_dataloader.dataset.pipeline[-1].copy()\n    test_pipeline = Compose([transform_broadcaster, pack_track_inputs])\n\n    return test_pipeline\n\n\ndef inference_mot(model: nn.Module, img: np.ndarray, frame_id: int,\n                  video_len: int) -> SampleList:\n    \"\"\"Inference image(s) with the mot model.\n\n    Args:\n        model (nn.Module): The loaded mot model.\n        img (np.ndarray): Loaded image.\n        frame_id (int): frame id.\n        video_len (int): demo video length\n    Returns:\n        SampleList: The tracking data samples.\n    \"\"\"\n    cfg = model.cfg\n    data = dict(\n        img=[img.astype(np.float32)],\n        frame_id=[frame_id],\n        ori_shape=[img.shape[:2]],\n        img_id=[frame_id + 1],\n        ori_video_length=[video_len])\n\n    test_pipeline = build_test_pipeline(cfg)\n    data = test_pipeline(data)\n\n    if not next(model.parameters()).is_cuda:\n        for m in model.modules():\n            assert not isinstance(\n                m, RoIPool\n            ), 'CPU inference with RoIPool is not supported currently.'\n\n    # forward the model\n    with torch.no_grad():\n        data = default_collate([data])\n        result = model.test_step(data)[0]\n    return result\n\n\ndef init_track_model(config: Union[str, Config],\n                     checkpoint: Optional[str] = None,\n                     detector: Optional[str] = None,\n                     reid: Optional[str] = None,\n                     device: str = 'cuda:0',\n                     cfg_options: Optional[dict] = None) -> nn.Module:\n    \"\"\"Initialize a model from config file.\n\n    Args:\n        config (str or :obj:`mmengine.Config`): Config file path or the config\n            object.\n        checkpoint (Optional[str], optional): Checkpoint path. Defaults to\n            None.\n        detector (Optional[str], optional): Detector Checkpoint path, use in\n            some tracking algorithms like sort.  Defaults to None.\n        reid (Optional[str], optional): Reid checkpoint path. use in\n            some tracking algorithms like sort. Defaults to None.\n        device (str, optional): The device that the model inferences on.\n            Defaults to `cuda:0`.\n        cfg_options (Optional[dict], optional): Options to override some\n            settings in the used config. Defaults to None.\n\n    Returns:\n        nn.Module: The constructed model.\n    \"\"\"\n    if isinstance(config, str):\n        config = Config.fromfile(config)\n    elif not isinstance(config, Config):\n        raise TypeError('config must be a filename or Config object, '\n                        f'but got {type(config)}')\n    if cfg_options is not None:\n        config.merge_from_dict(cfg_options)\n\n    model = MODELS.build(config.model)\n\n    if checkpoint is not None:\n        checkpoint = load_checkpoint(model, checkpoint, map_location='cpu')\n        # Weights converted from elsewhere may not have meta fields.\n        checkpoint_meta = checkpoint.get('meta', {})\n        # save the dataset_meta in the model for convenience\n        if 'dataset_meta' in checkpoint_meta:\n            if 'CLASSES' in checkpoint_meta['dataset_meta']:\n                value = checkpoint_meta['dataset_meta'].pop('CLASSES')\n                checkpoint_meta['dataset_meta']['classes'] = value\n            model.dataset_meta = checkpoint_meta['dataset_meta']\n\n    if detector is not None:\n        assert not (checkpoint and detector), \\\n            'Error: checkpoint and detector checkpoint cannot both exist'\n        load_checkpoint(model.detector, detector, map_location='cpu')\n\n    if reid is not None:\n        assert not (checkpoint and reid), \\\n            'Error: checkpoint and reid checkpoint cannot both exist'\n        load_checkpoint(model.reid, reid, map_location='cpu')\n\n    # Some methods don't load checkpoints or checkpoints don't contain\n    # 'dataset_meta'\n    # VIS need dataset_meta, MOT don't need dataset_meta\n    if not hasattr(model, 'dataset_meta'):\n        warnings.warn('dataset_meta or class names are missed, '\n                      'use None by default.')\n        model.dataset_meta = {'classes': None}\n\n    model.cfg = config  # save the config in the model for convenience\n    model.to(device)\n    model.eval()\n    return model\n"
  },
  {
    "path": "mmdet/configs/_base_/datasets/coco_detection.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms import LoadImageFromFile\nfrom mmengine.dataset.sampler import DefaultSampler\n\nfrom mmdet.datasets import AspectRatioBatchSampler, CocoDataset\nfrom mmdet.datasets.transforms import (LoadAnnotations, PackDetInputs,\n                                       RandomFlip, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type=DefaultSampler, shuffle=True),\n    batch_sampler=dict(type=AspectRatioBatchSampler),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type=DefaultSampler, shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type=CocoMetric,\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# inference on test dataset and\n# format the output results for submission.\n# test_dataloader = dict(\n#     batch_size=1,\n#     num_workers=2,\n#     persistent_workers=True,\n#     drop_last=False,\n#     sampler=dict(type=DefaultSampler, shuffle=False),\n#     dataset=dict(\n#         type=dataset_type,\n#         data_root=data_root,\n#         ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#         data_prefix=dict(img='test2017/'),\n#         test_mode=True,\n#         pipeline=test_pipeline))\n# test_evaluator = dict(\n#     type=CocoMetric,\n#     metric='bbox',\n#     format_only=True,\n#     ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#     outfile_prefix='./work_dirs/coco_detection/test')\n"
  },
  {
    "path": "mmdet/configs/_base_/datasets/coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmengine.dataset.sampler import DefaultSampler\n\nfrom mmdet.datasets.coco import CocoDataset\nfrom mmdet.datasets.samplers.batch_sampler import AspectRatioBatchSampler\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.datasets.transforms.transforms import RandomFlip, Resize\nfrom mmdet.evaluation.metrics.coco_metric import CocoMetric\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type=DefaultSampler, shuffle=True),\n    batch_sampler=dict(type=AspectRatioBatchSampler),\n    dataset=dict(\n        type=CocoDataset,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type=DefaultSampler, shuffle=False),\n    dataset=dict(\n        type=CocoDataset,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type=CocoMetric,\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# inference on test dataset and\n# format the output results for submission.\n# test_dataloader = dict(\n#     batch_size=1,\n#     num_workers=2,\n#     persistent_workers=True,\n#     drop_last=False,\n#     sampler=dict(type=DefaultSampler, shuffle=False),\n#     dataset=dict(\n#         type=CocoDataset,\n#         data_root=data_root,\n#         ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#         data_prefix=dict(img='test2017/'),\n#         test_mode=True,\n#         pipeline=test_pipeline))\n# test_evaluator = dict(\n#     type=CocoMetric,\n#     metric=['bbox', 'segm'],\n#     format_only=True,\n#     ann_file=data_root + 'annotations/image_info_test-dev2017.json',\n#     outfile_prefix='./work_dirs/coco_instance/test')\n"
  },
  {
    "path": "mmdet/configs/_base_/datasets/coco_instance_semantic.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmengine.dataset.sampler import DefaultSampler\n\nfrom mmdet.datasets.coco import CocoDataset\nfrom mmdet.datasets.samplers.batch_sampler import AspectRatioBatchSampler\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.datasets.transforms.transforms import RandomFlip, Resize\nfrom mmdet.evaluation.metrics.coco_metric import CocoMetric\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True, with_seg=True),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True, with_seg=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type=DefaultSampler, shuffle=True),\n    batch_sampler=dict(type=AspectRatioBatchSampler),\n    dataset=dict(\n        type=CocoDataset,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/', seg='stuffthingmaps/train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type=DefaultSampler, shuffle=False),\n    dataset=dict(\n        type=CocoDataset,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type=CocoMetric,\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "mmdet/configs/_base_/datasets/coco_panoptic.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmengine.dataset.sampler import DefaultSampler\n\nfrom mmdet.datasets.coco_panoptic import CocoPanopticDataset\nfrom mmdet.datasets.samplers.batch_sampler import AspectRatioBatchSampler\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadPanopticAnnotations\nfrom mmdet.datasets.transforms.transforms import RandomFlip, Resize\nfrom mmdet.evaluation.metrics.coco_panoptic_metric import CocoPanopticMetric\n\n# dataset settings\ndataset_type = 'CocoPanopticDataset'\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadPanopticAnnotations, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=LoadPanopticAnnotations, backend_args=backend_args),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type=DefaultSampler, shuffle=True),\n    batch_sampler=dict(type=AspectRatioBatchSampler),\n    dataset=dict(\n        type=CocoPanopticDataset,\n        data_root=data_root,\n        ann_file='annotations/panoptic_train2017.json',\n        data_prefix=dict(\n            img='train2017/', seg='annotations/panoptic_train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=backend_args))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type=DefaultSampler, shuffle=False),\n    dataset=dict(\n        type=CocoPanopticDataset,\n        data_root=data_root,\n        ann_file='annotations/panoptic_val2017.json',\n        data_prefix=dict(img='val2017/', seg='annotations/panoptic_val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type=CocoPanopticMetric,\n    ann_file=data_root + 'annotations/panoptic_val2017.json',\n    seg_prefix=data_root + 'annotations/panoptic_val2017/',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# inference on test dataset and\n# format the output results for submission.\n# test_dataloader = dict(\n#     batch_size=1,\n#     num_workers=1,\n#     persistent_workers=True,\n#     drop_last=False,\n#     sampler=dict(type=DefaultSampler, shuffle=False),\n#     dataset=dict(\n#         type=CocoPanopticDataset,\n#         data_root=data_root,\n#         ann_file='annotations/panoptic_image_info_test-dev2017.json',\n#         data_prefix=dict(img='test2017/'),\n#         test_mode=True,\n#         pipeline=test_pipeline))\n# test_evaluator = dict(\n#     type=CocoPanopticMetric,\n#     format_only=True,\n#     ann_file=data_root + 'annotations/panoptic_image_info_test-dev2017.json',\n#     outfile_prefix='./work_dirs/coco_panoptic/test')\n"
  },
  {
    "path": "mmdet/configs/_base_/datasets/mot_challenge.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms import (LoadImageFromFile, RandomResize,\n                             TransformBroadcaster)\n\nfrom mmdet.datasets import MOTChallengeDataset\nfrom mmdet.datasets.samplers import TrackImgSampler\nfrom mmdet.datasets.transforms import (LoadTrackAnnotations, PackTrackInputs,\n                                       PhotoMetricDistortion, RandomCrop,\n                                       RandomFlip, Resize,\n                                       UniformRefFrameSample)\nfrom mmdet.evaluation import MOTChallengeMetric\n\n# dataset settings\ndataset_type = MOTChallengeDataset\ndata_root = 'data/MOT17/'\nimg_scale = (1088, 1088)\n\nbackend_args = None\n# data pipeline\ntrain_pipeline = [\n    dict(\n        type=UniformRefFrameSample,\n        num_ref_imgs=1,\n        frame_range=10,\n        filter_key_img=True),\n    dict(\n        type=TransformBroadcaster,\n        share_random_params=True,\n        transforms=[\n            dict(type=LoadImageFromFile, backend_args=backend_args),\n            dict(type=LoadTrackAnnotations),\n            dict(\n                type=RandomResize,\n                scale=img_scale,\n                ratio_range=(0.8, 1.2),\n                keep_ratio=True,\n                clip_object_border=False),\n            dict(type=PhotoMetricDistortion)\n        ]),\n    dict(\n        type=TransformBroadcaster,\n        # different cropped positions for different frames\n        share_random_params=False,\n        transforms=[\n            dict(type=RandomCrop, crop_size=img_scale, bbox_clip_border=False)\n        ]),\n    dict(\n        type=TransformBroadcaster,\n        share_random_params=True,\n        transforms=[\n            dict(type=RandomFlip, prob=0.5),\n        ]),\n    dict(type=PackTrackInputs)\n]\n\ntest_pipeline = [\n    dict(\n        type=TransformBroadcaster,\n        transforms=[\n            dict(type=LoadImageFromFile, backend_args=backend_args),\n            dict(type=Resize, scale=img_scale, keep_ratio=True),\n            dict(type=LoadTrackAnnotations)\n        ]),\n    dict(type=PackTrackInputs)\n]\n\n# dataloader\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type=TrackImgSampler),  # image-based sampling\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        visibility_thr=-1,\n        ann_file='annotations/half-train_cocoformat.json',\n        data_prefix=dict(img_path='train'),\n        metainfo=dict(classes=('pedestrian', )),\n        pipeline=train_pipeline))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    # Now we support two ways to test, image_based and video_based\n    # if you want to use video_based sampling, you can use as follows\n    # sampler=dict(type='DefaultSampler', shuffle=False, round_up=False),\n    sampler=dict(type=TrackImgSampler),  # image-based sampling\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/half-val_cocoformat.json',\n        data_prefix=dict(img_path='train'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# evaluator\nval_evaluator = dict(\n    type=MOTChallengeMetric, metric=['HOTA', 'CLEAR', 'Identity'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "mmdet/configs/_base_/default_runtime.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.hooks import (CheckpointHook, DistSamplerSeedHook, IterTimerHook,\n                            LoggerHook, ParamSchedulerHook)\nfrom mmengine.runner import LogProcessor\nfrom mmengine.visualization import LocalVisBackend\n\nfrom mmdet.engine.hooks import DetVisualizationHook\nfrom mmdet.visualization import DetLocalVisualizer\n\ndefault_scope = None\n\ndefault_hooks = dict(\n    timer=dict(type=IterTimerHook),\n    logger=dict(type=LoggerHook, interval=50),\n    param_scheduler=dict(type=ParamSchedulerHook),\n    checkpoint=dict(type=CheckpointHook, interval=1),\n    sampler_seed=dict(type=DistSamplerSeedHook),\n    visualization=dict(type=DetVisualizationHook))\n\nenv_cfg = dict(\n    cudnn_benchmark=False,\n    mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),\n    dist_cfg=dict(backend='nccl'),\n)\n\nvis_backends = [dict(type=LocalVisBackend)]\nvisualizer = dict(\n    type=DetLocalVisualizer, vis_backends=vis_backends, name='visualizer')\nlog_processor = dict(type=LogProcessor, window_size=50, by_epoch=True)\n\nlog_level = 'INFO'\nload_from = None\nresume = False\n"
  },
  {
    "path": "mmdet/configs/_base_/models/cascade_mask_rcnn_r50_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.ops import RoIAlign, nms\nfrom torch.nn import BatchNorm2d\n\nfrom mmdet.models.backbones.resnet import ResNet\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.dense_heads.rpn_head import RPNHead\nfrom mmdet.models.detectors.cascade_rcnn import CascadeRCNN\nfrom mmdet.models.losses.cross_entropy_loss import CrossEntropyLoss\nfrom mmdet.models.losses.smooth_l1_loss import SmoothL1Loss\nfrom mmdet.models.necks.fpn import FPN\nfrom mmdet.models.roi_heads.bbox_heads.convfc_bbox_head import \\\n    Shared2FCBBoxHead\nfrom mmdet.models.roi_heads.cascade_roi_head import CascadeRoIHead\nfrom mmdet.models.roi_heads.mask_heads.fcn_mask_head import FCNMaskHead\nfrom mmdet.models.roi_heads.roi_extractors.single_level_roi_extractor import \\\n    SingleRoIExtractor\nfrom mmdet.models.task_modules.assigners.max_iou_assigner import MaxIoUAssigner\nfrom mmdet.models.task_modules.coders.delta_xywh_bbox_coder import \\\n    DeltaXYWHBBoxCoder\nfrom mmdet.models.task_modules.prior_generators.anchor_generator import \\\n    AnchorGenerator\nfrom mmdet.models.task_modules.samplers.random_sampler import RandomSampler\n\n# model settings\nmodel = dict(\n    type=CascadeRCNN,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=FPN,\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type=RPNHead,\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type=AnchorGenerator,\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type=DeltaXYWHBBoxCoder,\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type=CrossEntropyLoss, use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type=SmoothL1Loss, beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        type=CascadeRoIHead,\n        num_stages=3,\n        stage_loss_weights=[1, 0.5, 0.25],\n        bbox_roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type=Shared2FCBBoxHead,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type=DeltaXYWHBBoxCoder,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n                loss_bbox=dict(type=SmoothL1Loss, beta=1.0, loss_weight=1.0)),\n            dict(\n                type=Shared2FCBBoxHead,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type=DeltaXYWHBBoxCoder,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n                loss_bbox=dict(type=SmoothL1Loss, beta=1.0, loss_weight=1.0)),\n            dict(\n                type=Shared2FCBBoxHead,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type=DeltaXYWHBBoxCoder,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n                loss_bbox=dict(type=SmoothL1Loss, beta=1.0, loss_weight=1.0))\n        ],\n        mask_roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        mask_head=dict(\n            type=FCNMaskHead,\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=80,\n            loss_mask=dict(\n                type=CrossEntropyLoss, use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type=MaxIoUAssigner,\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.5,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type=RandomSampler,\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type=MaxIoUAssigner,\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type=RandomSampler,\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type=MaxIoUAssigner,\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type=RandomSampler,\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type=nms, iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)))\n"
  },
  {
    "path": "mmdet/configs/_base_/models/cascade_rcnn_r50_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.ops import RoIAlign, nms\nfrom torch.nn import BatchNorm2d\n\nfrom mmdet.models.backbones.resnet import ResNet\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.dense_heads.rpn_head import RPNHead\nfrom mmdet.models.detectors.cascade_rcnn import CascadeRCNN\nfrom mmdet.models.losses.cross_entropy_loss import CrossEntropyLoss\nfrom mmdet.models.losses.smooth_l1_loss import SmoothL1Loss\nfrom mmdet.models.necks.fpn import FPN\nfrom mmdet.models.roi_heads.bbox_heads.convfc_bbox_head import \\\n    Shared2FCBBoxHead\nfrom mmdet.models.roi_heads.cascade_roi_head import CascadeRoIHead\nfrom mmdet.models.roi_heads.roi_extractors.single_level_roi_extractor import \\\n    SingleRoIExtractor\nfrom mmdet.models.task_modules.assigners.max_iou_assigner import MaxIoUAssigner\nfrom mmdet.models.task_modules.coders.delta_xywh_bbox_coder import \\\n    DeltaXYWHBBoxCoder\nfrom mmdet.models.task_modules.prior_generators.anchor_generator import \\\n    AnchorGenerator\nfrom mmdet.models.task_modules.samplers.random_sampler import RandomSampler\n\n# model settings\nmodel = dict(\n    type=CascadeRCNN,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=FPN,\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type=RPNHead,\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type=AnchorGenerator,\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type=DeltaXYWHBBoxCoder,\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type=CrossEntropyLoss, use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type=SmoothL1Loss, beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        type=CascadeRoIHead,\n        num_stages=3,\n        stage_loss_weights=[1, 0.5, 0.25],\n        bbox_roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=[\n            dict(\n                type=Shared2FCBBoxHead,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type=DeltaXYWHBBoxCoder,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n                loss_bbox=dict(type=SmoothL1Loss, beta=1.0, loss_weight=1.0)),\n            dict(\n                type=Shared2FCBBoxHead,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type=DeltaXYWHBBoxCoder,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n                loss_bbox=dict(type=SmoothL1Loss, beta=1.0, loss_weight=1.0)),\n            dict(\n                type=Shared2FCBBoxHead,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=80,\n                bbox_coder=dict(\n                    type=DeltaXYWHBBoxCoder,\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n                loss_bbox=dict(type=SmoothL1Loss, beta=1.0, loss_weight=1.0))\n        ]),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type=MaxIoUAssigner,\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.5,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type=RandomSampler,\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type=MaxIoUAssigner,\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type=RandomSampler,\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type=MaxIoUAssigner,\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type=RandomSampler,\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type=nms, iou_threshold=0.5),\n            max_per_img=100)))\n"
  },
  {
    "path": "mmdet/configs/_base_/models/faster_rcnn_r50_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.ops import RoIAlign, nms\nfrom torch.nn import BatchNorm2d\n\nfrom mmdet.models.backbones.resnet import ResNet\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.dense_heads.rpn_head import RPNHead\nfrom mmdet.models.detectors.faster_rcnn import FasterRCNN\nfrom mmdet.models.losses.cross_entropy_loss import CrossEntropyLoss\nfrom mmdet.models.losses.smooth_l1_loss import L1Loss\nfrom mmdet.models.necks.fpn import FPN\nfrom mmdet.models.roi_heads.bbox_heads.convfc_bbox_head import \\\n    Shared2FCBBoxHead\nfrom mmdet.models.roi_heads.roi_extractors.single_level_roi_extractor import \\\n    SingleRoIExtractor\nfrom mmdet.models.roi_heads.standard_roi_head import StandardRoIHead\nfrom mmdet.models.task_modules.assigners.max_iou_assigner import MaxIoUAssigner\nfrom mmdet.models.task_modules.coders.delta_xywh_bbox_coder import \\\n    DeltaXYWHBBoxCoder\nfrom mmdet.models.task_modules.prior_generators.anchor_generator import \\\n    AnchorGenerator\nfrom mmdet.models.task_modules.samplers.random_sampler import RandomSampler\n\n# model settings\nmodel = dict(\n    type=FasterRCNN,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=FPN,\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type=RPNHead,\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type=AnchorGenerator,\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type=DeltaXYWHBBoxCoder,\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type=CrossEntropyLoss, use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type=L1Loss, loss_weight=1.0)),\n    roi_head=dict(\n        type=StandardRoIHead,\n        bbox_roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            type=Shared2FCBBoxHead,\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type=DeltaXYWHBBoxCoder,\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type=L1Loss, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=1000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type=nms, iou_threshold=0.5),\n            max_per_img=100)\n        # soft-nms is also supported for rcnn testing\n        # e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05)\n    ))\n"
  },
  {
    "path": "mmdet/configs/_base_/models/mask_rcnn_r50_caffe_c4.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.ops import RoIAlign, nms\nfrom mmengine.model.weight_init import PretrainedInit\nfrom torch.nn import BatchNorm2d\n\nfrom mmdet.models.backbones.resnet import ResNet\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.dense_heads.rpn_head import RPNHead\nfrom mmdet.models.detectors.mask_rcnn import MaskRCNN\nfrom mmdet.models.layers import ResLayer\nfrom mmdet.models.losses.cross_entropy_loss import CrossEntropyLoss\nfrom mmdet.models.losses.smooth_l1_loss import L1Loss\nfrom mmdet.models.roi_heads.bbox_heads.bbox_head import BBoxHead\nfrom mmdet.models.roi_heads.mask_heads.fcn_mask_head import FCNMaskHead\nfrom mmdet.models.roi_heads.roi_extractors.single_level_roi_extractor import \\\n    SingleRoIExtractor\nfrom mmdet.models.roi_heads.standard_roi_head import StandardRoIHead\nfrom mmdet.models.task_modules.assigners.max_iou_assigner import MaxIoUAssigner\nfrom mmdet.models.task_modules.coders.delta_xywh_bbox_coder import \\\n    DeltaXYWHBBoxCoder\nfrom mmdet.models.task_modules.prior_generators.anchor_generator import \\\n    AnchorGenerator\nfrom mmdet.models.task_modules.samplers.random_sampler import RandomSampler\n\n# model settings\nnorm_cfg = dict(type=BatchNorm2d, requires_grad=False)\n# model settings\nmodel = dict(\n    type=MaskRCNN,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=3,\n        strides=(1, 2, 2),\n        dilations=(1, 1, 1),\n        out_indices=(2, ),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    rpn_head=dict(\n        type=RPNHead,\n        in_channels=1024,\n        feat_channels=1024,\n        anchor_generator=dict(\n            type=AnchorGenerator,\n            scales=[2, 4, 8, 16, 32],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[16]),\n        bbox_coder=dict(\n            type=DeltaXYWHBBoxCoder,\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type=CrossEntropyLoss, use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type=L1Loss, loss_weight=1.0)),\n    roi_head=dict(\n        type=StandardRoIHead,\n        shared_head=dict(\n            type=ResLayer,\n            depth=50,\n            stage=3,\n            stride=2,\n            dilation=1,\n            style='caffe',\n            norm_cfg=norm_cfg,\n            norm_eval=True),\n        bbox_roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=14, sampling_ratio=0),\n            out_channels=1024,\n            featmap_strides=[16]),\n        bbox_head=dict(\n            type=BBoxHead,\n            with_avg_pool=True,\n            roi_feat_size=7,\n            in_channels=2048,\n            num_classes=80,\n            bbox_coder=dict(\n                type=DeltaXYWHBBoxCoder,\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type=L1Loss, loss_weight=1.0)),\n        mask_roi_extractor=None,\n        mask_head=dict(\n            type=FCNMaskHead,\n            num_convs=0,\n            in_channels=2048,\n            conv_out_channels=256,\n            num_classes=80,\n            loss_mask=dict(\n                type=CrossEntropyLoss, use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=12000,\n            max_per_img=2000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            mask_size=14,\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=6000,\n            max_per_img=1000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type=nms, iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)))\n"
  },
  {
    "path": "mmdet/configs/_base_/models/mask_rcnn_r50_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.ops import RoIAlign, nms\nfrom mmengine.model.weight_init import PretrainedInit\nfrom torch.nn import BatchNorm2d\n\nfrom mmdet.models.backbones.resnet import ResNet\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.dense_heads.rpn_head import RPNHead\nfrom mmdet.models.detectors.mask_rcnn import MaskRCNN\nfrom mmdet.models.losses.cross_entropy_loss import CrossEntropyLoss\nfrom mmdet.models.losses.smooth_l1_loss import L1Loss\nfrom mmdet.models.necks.fpn import FPN\nfrom mmdet.models.roi_heads.bbox_heads.convfc_bbox_head import \\\n    Shared2FCBBoxHead\nfrom mmdet.models.roi_heads.mask_heads.fcn_mask_head import FCNMaskHead\nfrom mmdet.models.roi_heads.roi_extractors.single_level_roi_extractor import \\\n    SingleRoIExtractor\nfrom mmdet.models.roi_heads.standard_roi_head import StandardRoIHead\nfrom mmdet.models.task_modules.assigners.max_iou_assigner import MaxIoUAssigner\nfrom mmdet.models.task_modules.coders.delta_xywh_bbox_coder import \\\n    DeltaXYWHBBoxCoder\nfrom mmdet.models.task_modules.prior_generators.anchor_generator import \\\n    AnchorGenerator\nfrom mmdet.models.task_modules.samplers.random_sampler import RandomSampler\n\n# model settings\nmodel = dict(\n    type=MaskRCNN,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=FPN,\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=5),\n    rpn_head=dict(\n        type=RPNHead,\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type=AnchorGenerator,\n            scales=[8],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder=dict(\n            type=DeltaXYWHBBoxCoder,\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type=CrossEntropyLoss, use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type=L1Loss, loss_weight=1.0)),\n    roi_head=dict(\n        type=StandardRoIHead,\n        bbox_roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        bbox_head=dict(\n            type=Shared2FCBBoxHead,\n            in_channels=256,\n            fc_out_channels=1024,\n            roi_feat_size=7,\n            num_classes=80,\n            bbox_coder=dict(\n                type=DeltaXYWHBBoxCoder,\n                target_means=[0., 0., 0., 0.],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            reg_class_agnostic=False,\n            loss_cls=dict(\n                type=CrossEntropyLoss, use_sigmoid=False, loss_weight=1.0),\n            loss_bbox=dict(type=L1Loss, loss_weight=1.0)),\n        mask_roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        mask_head=dict(\n            type=FCNMaskHead,\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            num_classes=80,\n            loss_mask=dict(\n                type=CrossEntropyLoss, use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=1000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=RandomSampler,\n                num=512,\n                pos_fraction=0.25,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=True),\n            mask_size=28,\n            pos_weight=-1,\n            debug=False)),\n    test_cfg=dict(\n        rpn=dict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type=nms, iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.05,\n            nms=dict(type=nms, iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)))\n"
  },
  {
    "path": "mmdet/configs/_base_/models/retinanet_r50_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.ops import nms\nfrom torch.nn import BatchNorm2d\n\nfrom mmdet.models import (FPN, DetDataPreprocessor, FocalLoss, L1Loss, ResNet,\n                          RetinaHead, RetinaNet)\nfrom mmdet.models.task_modules import (AnchorGenerator, DeltaXYWHBBoxCoder,\n                                       MaxIoUAssigner, PseudoSampler)\n\n# model settings\nmodel = dict(\n    type=RetinaNet,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=FPN,\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        start_level=1,\n        add_extra_convs='on_input',\n        num_outs=5),\n    bbox_head=dict(\n        type=RetinaHead,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        anchor_generator=dict(\n            type=AnchorGenerator,\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type=DeltaXYWHBBoxCoder,\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type=FocalLoss,\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type=L1Loss, loss_weight=1.0)),\n    # model training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type=MaxIoUAssigner,\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.4,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        sampler=dict(\n            type=PseudoSampler),  # Focal loss should use PseudoSampler\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(type=nms, iou_threshold=0.5),\n        max_per_img=100))\n"
  },
  {
    "path": "mmdet/configs/_base_/schedules/schedule_1x.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.optim.optimizer.optimizer_wrapper import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim.sgd import SGD\n\n# training schedule for 1x\ntrain_cfg = dict(type=EpochBasedTrainLoop, max_epochs=12, val_interval=1)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=12,\n        by_epoch=True,\n        milestones=[8, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "mmdet/configs/_base_/schedules/schedule_2x.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.optim.optimizer.optimizer_wrapper import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim.sgd import SGD\n\n# training schedule for 1x\ntrain_cfg = dict(type=EpochBasedTrainLoop, max_epochs=24, val_interval=1)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "mmdet/configs/cascade_rcnn/cascade_mask_rcnn_r50_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_instance import *\n    from .._base_.default_runtime import *\n    from .._base_.models.cascade_mask_rcnn_r50_fpn import *\n    from .._base_.schedules.schedule_1x import *\n"
  },
  {
    "path": "mmdet/configs/cascade_rcnn/cascade_rcnn_r50_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_detection import *\n    from .._base_.default_runtime import *\n    from .._base_.models.cascade_rcnn_r50_fpn import *\n    from .._base_.schedules.schedule_1x import *\n"
  },
  {
    "path": "mmdet/configs/common/lsj_100e_coco_detection.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmengine.dataset.sampler import DefaultSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import CocoDataset, RepeatDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  RandomResize, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\nimage_size = (1024, 1024)\n\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=RandomResize,\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type=RandomCrop,\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type=FilterAnnotations, min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\n# Use RepeatDataset to speed up training\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type=DefaultSampler, shuffle=True),\n    dataset=dict(\n        type=RepeatDataset,\n        times=4,  # simply change this from 2 to 16 for 50e - 400e training.\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type=DefaultSampler, shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type=CocoMetric,\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\nmax_epochs = 25\n\ntrain_cfg = dict(\n    type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=5)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\n# optimizer assumes bs=64\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=SGD, lr=0.1, momentum=0.9, weight_decay=0.00004))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.067, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n\n# only keep latest 2 checkpoints\ndefault_hooks.update(dict(checkpoint=dict(max_keep_ckpts=2)))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "mmdet/configs/common/lsj_100e_coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmengine.dataset.sampler import DefaultSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import CocoDataset, RepeatDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  RandomResize, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\nimage_size = (1024, 1024)\n\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=RandomResize,\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type=RandomCrop,\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type=FilterAnnotations, min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\n# Use RepeatDataset to speed up training\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type=DefaultSampler, shuffle=True),\n    dataset=dict(\n        type=RepeatDataset,\n        times=4,  # simply change this from 2 to 16 for 50e - 400e training.\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type=DefaultSampler, shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type=CocoMetric,\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\nmax_epochs = 25\n\ntrain_cfg = dict(\n    type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=5)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\n# optimizer assumes bs=64\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=SGD, lr=0.1, momentum=0.9, weight_decay=0.00004))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.067, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n\n# only keep latest 2 checkpoints\ndefault_hooks.update(dict(checkpoint=dict(max_keep_ckpts=2)))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (32 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "mmdet/configs/common/lsj_200e_coco_detection.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .lsj_100e_coco_detection import *\n\n# 8x25=200e\ntrain_dataloader.update(dict(dataset=dict(times=8)))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.067, by_epoch=False, begin=0, end=1000),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=25,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "mmdet/configs/common/lsj_200e_coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .lsj_100e_coco_instance import *\n\n# 8x25=200e\ntrain_dataloader.update(dict(dataset=dict(times=8)))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.067, by_epoch=False, begin=0, end=1000),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=25,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "mmdet/configs/common/ms_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmcv.transforms import RandomResize\nfrom mmengine.dataset import RepeatDataset\nfrom mmengine.dataset.sampler import DefaultSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import AspectRatioBatchSampler, CocoDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import RandomFlip, Resize\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# In mstrain 3x config, img_scale=[(1333, 640), (1333, 800)],\n# multiscale_mode='range'\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(type=RandomResize, scale=[(1333, 640), (1333, 800)], keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    batch_size=2,\n    num_workers=2,\n    persistent_workers=True,\n    pin_memory=True,\n    sampler=dict(type=DefaultSampler, shuffle=True),\n    batch_sampler=dict(type=AspectRatioBatchSampler),\n    dataset=dict(\n        type=RepeatDataset,\n        times=3,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type=DefaultSampler, shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline,\n        backend_args=backend_args))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type=CocoMetric,\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric='bbox',\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# training schedule for 3x with `RepeatDataset`\ntrain_cfg = dict(type=EpochBasedTrainLoop, max_iters=12, val_interval=1)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=12,\n        by_epoch=False,\n        milestones=[9, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "mmdet/configs/common/ms_3x_coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmcv.transforms import RandomChoiceResize\nfrom mmengine.dataset import RepeatDataset\nfrom mmengine.dataset.sampler import DefaultSampler, InfiniteSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import IterBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import AspectRatioBatchSampler, CocoDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  RandomResize, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\n\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader.update(\n    dict(\n        batch_size=2,\n        num_workers=2,\n        persistent_workers=True,\n        sampler=dict(type=DefaultSampler, shuffle=True),\n        batch_sampler=dict(type=AspectRatioBatchSampler),\n        dataset=dict(\n            type=RepeatDataset,\n            times=3,\n            dataset=dict(\n                type=dataset_type,\n                data_root=data_root,\n                ann_file='annotations/instances_train2017.json',\n                data_prefix=dict(img='train2017/'),\n                filter_cfg=dict(filter_empty_gt=True, min_size=32),\n                pipeline=train_pipeline,\n                backend_args=backend_args))))\nval_dataloader.update(\n    dict(\n        batch_size=1,\n        num_workers=2,\n        persistent_workers=True,\n        drop_last=False,\n        sampler=dict(type=DefaultSampler, shuffle=False),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_val2017.json',\n            data_prefix=dict(img='val2017/'),\n            test_mode=True,\n            pipeline=test_pipeline,\n            backend_args=backend_args)))\ntest_dataloader = val_dataloader\n\nval_evaluator.update(\n    dict(\n        type=CocoMetric,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        metric='bbox',\n        backend_args=backend_args))\ntest_evaluator = val_evaluator\n\n# training schedule for 3x with `RepeatDataset`\ntrain_cfg.update(dict(type=EpochBasedTrainLoop, max_epochs=12, val_interval=1))\nval_cfg.update(dict(type=ValLoop))\ntest_cfg.update(dict(type=TestLoop))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=12,\n        by_epoch=False,\n        milestones=[9, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper.update(\n    dict(\n        type=OptimWrapper,\n        optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001)))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr.update(dict(enable=False, base_batch_size=16))\n"
  },
  {
    "path": "mmdet/configs/common/ms_90k_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmcv.transforms import RandomChoiceResize\nfrom mmengine.dataset import RepeatDataset\nfrom mmengine.dataset.sampler import DefaultSampler, InfiniteSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import IterBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import AspectRatioBatchSampler, CocoDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  RandomResize, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Align with Detectron2\nbackend = 'pillow'\ntrain_pipeline = [\n    dict(\n        type=LoadImageFromFile,\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=RandomChoiceResize,\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True,\n        backend=backend),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(\n        type=LoadImageFromFile,\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True, backend=backend),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader.update(\n    dict(\n        batch_size=2,\n        num_workers=2,\n        persistent_workers=True,\n        pin_memory=True,\n        sampler=dict(type=InfiniteSampler, shuffle=True),\n        batch_sampler=dict(type=AspectRatioBatchSampler),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader.update(\n    dict(\n        batch_size=1,\n        num_workers=2,\n        persistent_workers=True,\n        drop_last=False,\n        pin_memory=True,\n        sampler=dict(type=DefaultSampler, shuffle=False),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_val2017.json',\n            data_prefix=dict(img='val2017/'),\n            test_mode=True,\n            pipeline=test_pipeline,\n            backend_args=backend_args)))\ntest_dataloader = val_dataloader\n\nval_evaluator.update(\n    dict(\n        type=CocoMetric,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        metric='bbox',\n        format_only=False,\n        backend_args=backend_args))\ntest_evaluator = val_evaluator\n\n# training schedule for 90k\nmax_iter = 90000\ntrain_cfg.update(\n    dict(type=IterBasedTrainLoop, max_iters=max_iter, val_interval=10000))\nval_cfg.update(dict(type=ValLoop))\ntest_cfg.update(dict(type=TestLoop))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=1000),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper.update(\n    dict(\n        type=OptimWrapper,\n        optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001)))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr.update(dict(enable=False, base_batch_size=16))\n\ndefault_hooks.update(dict(checkpoint=dict(by_epoch=False, interval=10000)))\nlog_processor.update(dict(by_epoch=False))\n"
  },
  {
    "path": "mmdet/configs/common/ms_poly_3x_coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmcv.transforms import RandomChoiceResize\nfrom mmengine.dataset import RepeatDataset\nfrom mmengine.dataset.sampler import DefaultSampler, InfiniteSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import IterBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import AspectRatioBatchSampler, CocoDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  RandomResize, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# In mstrain 3x config, img_scale=[(1333, 640), (1333, 800)],\n# multiscale_mode='range'\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type='RandomResize', scale=[(1333, 640), (1333, 800)],\n        keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader.update(\n    dict(\n        batch_size=2,\n        num_workers=2,\n        persistent_workers=True,\n        pin_memory=True,\n        sampler=dict(type=DefaultSampler, shuffle=True),\n        batch_sampler=dict(type=AspectRatioBatchSampler),\n        dataset=dict(\n            type=RepeatDataset,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader.update(\n    dict(\n        batch_size=2,\n        num_workers=2,\n        persistent_workers=True,\n        drop_last=False,\n        pin_memory=True,\n        sampler=dict(type=DefaultSampler, shuffle=False),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_val2017.json',\n            data_prefix=dict(img='val2017/'),\n            test_mode=True,\n            pipeline=test_pipeline,\n            backend_args=backend_args)))\ntest_dataloader = val_dataloader\n\nval_evaluator.update(\n    dict(\n        type=CocoMetric,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        metric=['bbox', 'segm'],\n        backend_args=backend_args))\ntest_evaluator = val_evaluator\n\n# training schedule for 3x with `RepeatDataset`\ntrain_cfg.update(dict(type=EpochBasedTrainLoop, max_iters=12, val_interval=1))\nval_cfg.update(dict(type=ValLoop))\ntest_cfg.update(dict(type=TestLoop))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=12,\n        by_epoch=False,\n        milestones=[9, 11],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper.update(\n    dict(\n        type=OptimWrapper,\n        optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001)))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr.update(dict(enable=False, base_batch_size=16))\n"
  },
  {
    "path": "mmdet/configs/common/ms_poly_90k_coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmcv.transforms import RandomChoiceResize\nfrom mmengine.dataset import RepeatDataset\nfrom mmengine.dataset.sampler import DefaultSampler, InfiniteSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import IterBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import AspectRatioBatchSampler, CocoDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  RandomResize, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Align with Detectron2\nbackend = 'pillow'\ntrain_pipeline = [\n    dict(\n        type=LoadImageFromFile,\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=RandomChoiceResize,\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True,\n        backend=backend),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(\n        type=LoadImageFromFile,\n        backend_args=backend_args,\n        imdecode_backend=backend),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True, backend=backend),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader.update(\n    dict(\n        batch_size=2,\n        num_workers=2,\n        persistent_workers=True,\n        pin_memory=True,\n        sampler=dict(type=InfiniteSampler, shuffle=True),\n        batch_sampler=dict(type=AspectRatioBatchSampler),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader.update(\n    dict(\n        batch_size=1,\n        num_workers=2,\n        persistent_workers=True,\n        drop_last=False,\n        pin_memory=True,\n        sampler=dict(type=DefaultSampler, shuffle=False),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_val2017.json',\n            data_prefix=dict(img='val2017/'),\n            test_mode=True,\n            pipeline=test_pipeline,\n            backend_args=backend_args)))\ntest_dataloader = val_dataloader\n\nval_evaluator.update(\n    dict(\n        type=CocoMetric,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        metric=['bbox', 'segm'],\n        format_only=False,\n        backend_args=backend_args))\ntest_evaluator = val_evaluator\n\n# training schedule for 90k\nmax_iter = 90000\ntrain_cfg.update(\n    dict(type=IterBasedTrainLoop, max_iters=max_iter, val_interval=10000))\nval_cfg.update(dict(type=ValLoop))\ntest_cfg.update(dict(type=TestLoop))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=1000),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[60000, 80000],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper.update(\n    dict(\n        type=OptimWrapper,\n        optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001)))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr.update(dict(enable=False, base_batch_size=16))\n\ndefault_hooks.update(dict(checkpoint=dict(by_epoch=False, interval=10000)))\nlog_processor.update(dict(by_epoch=False))\n"
  },
  {
    "path": "mmdet/configs/common/ssj_270_coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n\nfrom mmcv.transforms import RandomChoiceResize\nfrom mmengine.dataset import RepeatDataset\nfrom mmengine.dataset.sampler import DefaultSampler, InfiniteSampler\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\nfrom mmengine.runner.loops import IterBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim import SGD\n\nfrom mmdet.datasets import AspectRatioBatchSampler, CocoDataset\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations,\n                                               LoadImageFromFile)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  RandomResize, Resize)\nfrom mmdet.evaluation import CocoMetric\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Standard Scale Jittering (SSJ) resizes and crops an image\n# with a resize range of 0.8 to 1.25 of the original image size.\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=RandomResize,\n        scale=image_size,\n        ratio_range=(0.8, 1.25),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader.update(\n    dict(\n        batch_size=2,\n        num_workers=2,\n        persistent_workers=True,\n        sampler=dict(type=InfiniteSampler),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\nval_dataloader.update(\n    dict(\n        batch_size=1,\n        num_workers=2,\n        persistent_workers=True,\n        drop_last=False,\n        sampler=dict(type=DefaultSampler, shuffle=False),\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_val2017.json',\n            data_prefix=dict(img='val2017/'),\n            test_mode=True,\n            pipeline=test_pipeline,\n            backend_args=backend_args)))\ntest_dataloader = val_dataloader\n\nval_evaluator.update(\n    dict(\n        type=CocoMetric,\n        ann_file=data_root + 'annotations/instances_val2017.json',\n        metric=['bbox', 'segm'],\n        format_only=False,\n        backend_args=backend_args))\ntest_evaluator = val_evaluator\n\nval_evaluator = dict(\n    type=CocoMetric,\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False,\n    backend_args=backend_args)\ntest_evaluator = val_evaluator\n\n# The model is trained by 270k iterations with batch_size 64,\n# which is roughly equivalent to 144 epochs.\n\nmax_iter = 270000\ntrain_cfg.update(\n    dict(type=IterBasedTrainLoop, max_iters=max_iter, val_interval=10000))\nval_cfg.update(dict(type=ValLoop))\ntest_cfg.update(dict(type=TestLoop))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=1000),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_iter,\n        by_epoch=False,\n        milestones=[243000, 256500, 263250],\n        gamma=0.1)\n]\n\n# optimizer\noptim_wrapper.update(\n    dict(\n        type=OptimWrapper,\n        optimizer=dict(type=SGD, lr=0.1, momentum=0.9, weight_decay=0.00004)))\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr.update(dict(base_batch_size=64))\n\ndefault_hooks.update(dict(checkpoint=dict(by_epoch=False, interval=10000)))\nlog_processor.update(dict(by_epoch=False))\n"
  },
  {
    "path": "mmdet/configs/common/ssj_scp_270k_coco_instance.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .ssj_270_coco_instance import *\n\nfrom mmdet.datasets import MultiImageMixDataset\nfrom mmdet.datasets.transforms import CopyPaste\n\n# dataset settings\ndataset_type = CocoDataset\ndata_root = 'data/coco/'\nimage_size = (1024, 1024)\n# Example to use different file client\n# Method 1: simply set the data root and let the file I/O module\n# automatically infer from prefix (not support LMDB and Memcache yet)\n\n# data_root = 's3://openmmlab/datasets/detection/coco/'\n\n# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n# Standard Scale Jittering (SSJ) resizes and crops an image\n# with a resize range of 0.8 to 1.25 of the original image size.\nload_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=RandomResize,\n        scale=image_size,\n        ratio_range=(0.8, 1.25),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=image_size),\n]\ntrain_pipeline = [\n    dict(type=CopyPaste, max_num_pasted=100),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(\n    dict(\n        type=MultiImageMixDataset,\n        dataset=dict(\n            type=dataset_type,\n            data_root=data_root,\n            ann_file='annotations/instances_train2017.json',\n            data_prefix=dict(img='train2017/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=load_pipeline,\n            backend_args=backend_args),\n        pipeline=train_pipeline))\n"
  },
  {
    "path": "mmdet/configs/deformable_detr/deformable_detr_r50_16xb2_50e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_detection import *\n    from .._base_.default_runtime import *\n\nfrom mmcv.transforms import LoadImageFromFile, RandomChoice, RandomChoiceResize\nfrom mmengine.optim.optimizer import OptimWrapper\nfrom mmengine.optim.scheduler import MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.optim.adamw import AdamW\n\nfrom mmdet.datasets.transforms import (LoadAnnotations, PackDetInputs,\n                                       RandomCrop, RandomFlip, Resize)\nfrom mmdet.models.backbones import ResNet\nfrom mmdet.models.data_preprocessors import DetDataPreprocessor\nfrom mmdet.models.dense_heads import DeformableDETRHead\nfrom mmdet.models.detectors import DeformableDETR\nfrom mmdet.models.losses import FocalLoss, GIoULoss, L1Loss\nfrom mmdet.models.necks import ChannelMapper\nfrom mmdet.models.task_modules import (BBoxL1Cost, FocalLossCost,\n                                       HungarianAssigner, IoUCost)\n\nmodel = dict(\n    type=DeformableDETR,\n    num_queries=300,\n    num_feature_levels=4,\n    with_box_refine=False,\n    as_two_stage=False,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=ChannelMapper,\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(  # DeformableDetrTransformerEncoder\n        num_layers=6,\n        layer_cfg=dict(  # DeformableDetrTransformerEncoderLayer\n            self_attn_cfg=dict(  # MultiScaleDeformableAttention\n                embed_dims=256,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.1))),\n    decoder=dict(  # DeformableDetrTransformerDecoder\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(  # DeformableDetrTransformerDecoderLayer\n            self_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            cross_attn_cfg=dict(  # MultiScaleDeformableAttention\n                embed_dims=256,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256, feedforward_channels=1024, ffn_drop=0.1)),\n        post_norm_cfg=None),\n    positional_encoding=dict(num_feats=128, normalize=True, offset=-0.5),\n    bbox_head=dict(\n        type=DeformableDETRHead,\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type=FocalLoss,\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=2.0),\n        loss_bbox=dict(type=L1Loss, loss_weight=5.0),\n        loss_iou=dict(type=GIoULoss, loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type=HungarianAssigner,\n            match_costs=[\n                dict(type=FocalLossCost, weight=2.0),\n                dict(type=BBoxL1Cost, weight=5.0, box_format='xywh'),\n                dict(type=IoUCost, iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=100))\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(\n        type=RandomChoice,\n        transforms=[\n            [\n                dict(\n                    type=RandomChoiceResize,\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    resize_type=Resize,\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type=RandomChoiceResize,\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    resize_type=Resize,\n                    keep_ratio=True),\n                dict(\n                    type=RandomCrop,\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type=RandomChoiceResize,\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    resize_type=Resize,\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type=PackDetInputs)\n]\ntrain_dataloader.update(\n    dict(\n        dataset=dict(\n            filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline)))\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=AdamW, lr=0.0002, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1),\n            'sampling_offsets': dict(lr_mult=0.1),\n            'reference_points': dict(lr_mult=0.1)\n        }))\n\n# learning policy\nmax_epochs = 50\ntrain_cfg = dict(\n    type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\nparam_scheduler = [\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[40],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (16 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=32)\n"
  },
  {
    "path": "mmdet/configs/deformable_detr/deformable_detr_refine_r50_16xb2_50e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .deformable_detr_r50_16xb2_50e_coco import *\n\nmodel.update(dict(with_box_refine=True))\n"
  },
  {
    "path": "mmdet/configs/deformable_detr/deformable_detr_refine_twostage_r50_16xb2_50e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .deformable_detr_refine_r50_16xb2_50e_coco import *\n\nmodel.update(dict(as_two_stage=True))\n"
  },
  {
    "path": "mmdet/configs/detr/detr_r101_8xb2_500e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\n\nwith read_base():\n    from .detr_r50_8xb2_500e_coco import *\n\nmodel.update(\n    dict(\n        backbone=dict(\n            depth=101,\n            init_cfg=dict(\n                type=PretrainedInit, checkpoint='torchvision://resnet101'))))\n"
  },
  {
    "path": "mmdet/configs/detr/detr_r18_8xb2_500e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\n\nwith read_base():\n    from .detr_r50_8xb2_500e_coco import *\n\nmodel.update(\n    dict(\n        backbone=dict(\n            depth=18,\n            init_cfg=dict(\n                type=PretrainedInit, checkpoint='torchvision://resnet18')),\n        neck=dict(in_channels=[512])))\n"
  },
  {
    "path": "mmdet/configs/detr/detr_r50_8xb2_150e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms import RandomChoice, RandomChoiceResize\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\nfrom mmengine.optim.optimizer.optimizer_wrapper import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.nn.modules.activation import ReLU\nfrom torch.nn.modules.batchnorm import BatchNorm2d\nfrom torch.optim.adamw import AdamW\n\nfrom mmdet.datasets.transforms import (LoadAnnotations, PackDetInputs,\n                                       RandomCrop, RandomFlip, Resize)\nfrom mmdet.models import (DETR, ChannelMapper, DetDataPreprocessor, DETRHead,\n                          ResNet)\nfrom mmdet.models.losses.cross_entropy_loss import CrossEntropyLoss\nfrom mmdet.models.losses.iou_loss import GIoULoss\nfrom mmdet.models.losses.smooth_l1_loss import L1Loss\nfrom mmdet.models.task_modules import (BBoxL1Cost, ClassificationCost,\n                                       HungarianAssigner, IoUCost)\n\nwith read_base():\n    from .._base_.datasets.coco_detection import *\n    from .._base_.default_runtime import *\n\nmodel = dict(\n    type=DETR,\n    num_queries=100,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(3, ),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=ChannelMapper,\n        in_channels=[2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=None,\n        num_outs=1),\n    encoder=dict(  # DetrTransformerEncoder\n        num_layers=6,\n        layer_cfg=dict(  # DetrTransformerEncoderLayer\n            self_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,\n                num_fcs=2,\n                ffn_drop=0.1,\n                act_cfg=dict(type=ReLU, inplace=True)))),\n    decoder=dict(  # DetrTransformerDecoder\n        num_layers=6,\n        layer_cfg=dict(  # DetrTransformerDecoderLayer\n            self_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            cross_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1,\n                batch_first=True),\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,\n                num_fcs=2,\n                ffn_drop=0.1,\n                act_cfg=dict(type=ReLU, inplace=True))),\n        return_intermediate=True),\n    positional_encoding=dict(num_feats=128, normalize=True),\n    bbox_head=dict(\n        type=DETRHead,\n        num_classes=80,\n        embed_dims=256,\n        loss_cls=dict(\n            type=CrossEntropyLoss,\n            bg_cls_weight=0.1,\n            use_sigmoid=False,\n            loss_weight=1.0,\n            class_weight=1.0),\n        loss_bbox=dict(type=L1Loss, loss_weight=5.0),\n        loss_iou=dict(type=GIoULoss, loss_weight=2.0)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type=HungarianAssigner,\n            match_costs=[\n                dict(type=ClassificationCost, weight=1.),\n                dict(type=BBoxL1Cost, weight=5.0, box_format='xywh'),\n                dict(type=IoUCost, iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=100))\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(\n        type=RandomChoice,\n        transforms=[[\n            dict(\n                type=RandomChoiceResize,\n                resize_type=Resize,\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type=RandomChoiceResize,\n                            resize_type=Resize,\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True),\n                        dict(\n                            type=RandomCrop,\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type=RandomChoiceResize,\n                            resize_type=Resize,\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True)\n                    ]]),\n    dict(type=PackDetInputs)\n]\ntrain_dataloader.update(dataset=dict(pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=AdamW, lr=0.0001, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={'backbone': dict(lr_mult=0.1, decay_mult=1.0)}))\n\n# learning policy\nmax_epochs = 150\ntrain_cfg = dict(\n    type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\nparam_scheduler = [\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[100],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "mmdet/configs/detr/detr_r50_8xb2_500e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.optim.scheduler.lr_scheduler import MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop\n\nwith read_base():\n    from .detr_r50_8xb2_150e_coco import *\n\n# learning policy\nmax_epochs = 500\ntrain_cfg.update(\n    type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=10)\n\nparam_scheduler = [\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[334],\n        gamma=0.1)\n]\n\n# only keep latest 2 checkpoints\ndefault_hooks.update(checkpoint=dict(max_keep_ckpts=2))\n"
  },
  {
    "path": "mmdet/configs/dino/dino_4scale_r50_8xb2_12e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms import RandomChoice, RandomChoiceResize\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\nfrom mmengine.optim.optimizer.optimizer_wrapper import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import MultiStepLR\nfrom mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.nn.modules.batchnorm import BatchNorm2d\nfrom torch.nn.modules.normalization import GroupNorm\nfrom torch.optim.adamw import AdamW\n\nfrom mmdet.datasets.transforms import (LoadAnnotations, PackDetInputs,\n                                       RandomCrop, RandomFlip, Resize)\nfrom mmdet.models import (DINO, ChannelMapper, DetDataPreprocessor, DINOHead,\n                          ResNet)\nfrom mmdet.models.losses.focal_loss import FocalLoss\nfrom mmdet.models.losses.iou_loss import GIoULoss\nfrom mmdet.models.losses.smooth_l1_loss import L1Loss\nfrom mmdet.models.task_modules import (BBoxL1Cost, FocalLossCost,\n                                       HungarianAssigner, IoUCost)\n\nwith read_base():\n    from .._base_.datasets.coco_detection import *\n    from .._base_.default_runtime import *\n\nmodel = dict(\n    type=DINO,\n    num_queries=900,  # num_matching_queries\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type=ChannelMapper,\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type=GroupNorm, num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0))),  # 0.1 for DeformDETR\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=4,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128,\n        normalize=True,\n        offset=0.0,  # -0.5 for DeformDETR\n        temperature=20),  # 10000 for DeformDETR\n    bbox_head=dict(\n        type=DINOHead,\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type=FocalLoss,\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type=L1Loss, loss_weight=5.0),\n        loss_iou=dict(type=GIoULoss, loss_weight=2.0)),\n    dn_cfg=dict(  # TODO: Move to model.train_cfg ?\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None,\n                       num_dn_queries=100)),  # TODO: half num_dn_queries\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type=HungarianAssigner,\n            match_costs=[\n                dict(type=FocalLossCost, weight=2.0),\n                dict(type=BBoxL1Cost, weight=5.0, box_format='xywh'),\n                dict(type=IoUCost, iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))  # 100 for DeformDETR\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(\n        type=RandomChoice,\n        transforms=[\n            [\n                dict(\n                    type=RandomChoiceResize,\n                    resize_type=Resize,\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type=RandomChoiceResize,\n                    resize_type=Resize,\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type=RandomCrop,\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type=RandomChoiceResize,\n                    resize_type=Resize,\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type=PackDetInputs)\n]\ntrain_dataloader.update(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(\n        type=AdamW,\n        lr=0.0001,  # 0.0002 for DeformDETR\n        weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.1)})\n)  # custom_keys contains sampling_offsets and reference_points in DeformDETR  # noqa\n\n# learning policy\nmax_epochs = 12\ntrain_cfg = dict(\n    type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\nparam_scheduler = [\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "mmdet/configs/dino/dino_4scale_r50_8xb2_24e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.runner.loops import EpochBasedTrainLoop\n\nwith read_base():\n    from .dino_4scale_r50_8xb2_12e_coco import *\n\nmax_epochs = 24\ntrain_cfg.update(\n    dict(type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=1))\n\nparam_scheduler[0].update(dict(milestones=[20]))\n"
  },
  {
    "path": "mmdet/configs/dino/dino_4scale_r50_8xb2_36e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.runner.loops import EpochBasedTrainLoop\n\nwith read_base():\n    from .dino_4scale_r50_8xb2_12e_coco import *\n\nmax_epochs = 36\ntrain_cfg.update(\n    dict(type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=1))\n\nparam_scheduler[0].update(dict(milestones=[30]))\n"
  },
  {
    "path": "mmdet/configs/dino/dino_4scale_r50_improved_8xb2_12e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .dino_4scale_r50_8xb2_12e_coco import *\n\n# from deformable detr hyper\nmodel.update(\n    dict(\n        backbone=dict(frozen_stages=-1),\n        bbox_head=dict(loss_cls=dict(loss_weight=2.0)),\n        positional_encoding=dict(offset=-0.5, temperature=10000),\n        dn_cfg=dict(group_cfg=dict(num_dn_queries=300))))\n\n# optimizer\noptim_wrapper.update(\n    dict(\n        optimizer=dict(lr=0.0002),\n        paramwise_cfg=dict(\n            custom_keys={\n                'backbone': dict(lr_mult=0.1),\n                'sampling_offsets': dict(lr_mult=0.1),\n                'reference_points': dict(lr_mult=0.1)\n            })))\n"
  },
  {
    "path": "mmdet/configs/dino/dino_5scale_swin_l_8xb2_12e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\n\nfrom mmdet.models import SwinTransformer\n\nwith read_base():\n    from .dino_4scale_r50_8xb2_12e_coco import *\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\nnum_levels = 5\nmodel.merge(\n    dict(\n        num_feature_levels=num_levels,\n        backbone=dict(\n            _delete_=True,\n            type=SwinTransformer,\n            pretrain_img_size=384,\n            embed_dims=192,\n            depths=[2, 2, 18, 2],\n            num_heads=[6, 12, 24, 48],\n            window_size=12,\n            mlp_ratio=4,\n            qkv_bias=True,\n            qk_scale=None,\n            drop_rate=0.,\n            attn_drop_rate=0.,\n            drop_path_rate=0.2,\n            patch_norm=True,\n            out_indices=(0, 1, 2, 3),\n            # Please only add indices that would be used\n            # in FPN, otherwise some parameter will not be used\n            with_cp=True,\n            convert_weights=True,\n            init_cfg=dict(type=PretrainedInit, checkpoint=pretrained)),\n        neck=dict(in_channels=[192, 384, 768, 1536], num_outs=num_levels),\n        encoder=dict(\n            layer_cfg=dict(self_attn_cfg=dict(num_levels=num_levels))),\n        decoder=dict(\n            layer_cfg=dict(cross_attn_cfg=dict(num_levels=num_levels)))))\n"
  },
  {
    "path": "mmdet/configs/dino/dino_5scale_swin_l_8xb2_36e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.runner.loops import EpochBasedTrainLoop\n\nwith read_base():\n    from .dino_5scale_swin_l_8xb2_12e_coco import *\n\nmax_epochs = 36\ntrain_cfg.update(\n    dict(type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=1))\n\nparam_scheduler[0].update(dict(milestones=[27, 33]))\n"
  },
  {
    "path": "mmdet/configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_detection import *\n    from .._base_.default_runtime import *\n    from .._base_.models.faster_rcnn_r50_fpn import *\n    from .._base_.schedules.schedule_1x import *\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r101_caffe_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_poly_1x_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r101_caffe_fpn_ms_poly_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from ..common.ms_poly_3x_coco_instance import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        depth=101,\n        norm_cfg=dict(requires_grad=False),\n        norm_eval=True,\n        style='caffe',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet101_caffe')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r101_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.models.mask_rcnn_r50_fpn import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r101_fpn_2x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_2x_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r101_fpn_8xb8_amp_lsj_200e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r18_fpn_8xb8_amp_lsj_200e_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r101_fpn_ms_poly_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from ..common.ms_poly_3x_coco_instance import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    backbone=dict(\n        depth=101,\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet101')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r18_fpn_8xb8_amp_lsj_200e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_8xb8_amp_lsj_200e_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    backbone=dict(\n        depth=18,\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet18')),\n    neck=dict(in_channels=[64, 128, 256, 512]))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_caffe_c4_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_instance import *\n    from .._base_.default_runtime import *\n    from .._base_.models.mask_rcnn_r50_caffe_c4 import *\n    from .._base_.schedules.schedule_1x import *\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_1x_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_ms_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_1x_coco import *\n\nfrom mmcv.transforms import RandomChoiceResize\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args={{_base_.backend_args}}),\n    dict(type=LoadAnnotations, with_bbox=True, with_mask=True),\n    dict(\n        type=RandomChoiceResize,\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs),\n]\n\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_ms_poly_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_1x_coco import *\n\nfrom mmcv.transforms import RandomChoiceResize\nfrom mmengine.model.weight_init import PretrainedInit\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args={{_base_.backend_args}}),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=RandomChoiceResize,\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_ms_poly_2x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_caffe_fpn_ms_poly_1x_coco import *\n\ntrain_cfg = dict(max_epochs=24)\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[16, 22],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_ms_poly_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_caffe_fpn_ms_poly_1x_coco import *\n\ntrain_cfg = dict(max_epochs=36)\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=24,\n        by_epoch=True,\n        milestones=[28, 34],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_poly_1x_coco_v1.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_1x_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nfrom mmdet.models.losses import SmoothL1Loss\n\nmodel = dict(\n    # use caffe img_norm\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False),\n    backbone=dict(\n        norm_cfg=dict(requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')),\n    rpn_head=dict(\n        loss_bbox=dict(type=SmoothL1Loss, beta=1.0 / 9.0, loss_weight=1.0)),\n    roi_head=dict(\n        bbox_roi_extractor=dict(\n            roi_layer=dict(\n                type=RoIAlign, output_size=7, sampling_ratio=2,\n                aligned=False)),\n        bbox_head=dict(\n            loss_bbox=dict(type=SmoothL1Loss, beta=1.0, loss_weight=1.0)),\n        mask_roi_extractor=dict(\n            roi_layer=dict(\n                type=RoIAlign, output_size=14, sampling_ratio=2,\n                aligned=False))))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_instance import *\n    from .._base_.default_runtime import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n    from .._base_.schedules.schedule_1x import *\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_fpn_1x_wandb_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_instance import *\n    from .._base_.default_runtime import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n    from .._base_.schedules.schedule_1x import *\n\nfrom mmengine.visualization import LocalVisBackend, WandbVisBackend\n\nvis_backends.update(dict(type=WandbVisBackend))\nvis_backends.update(dict(type=LocalVisBackend))\nvisualizer.update(dict(vis_backends=vis_backends))\n\n# MMEngine support the following two ways, users can choose\n# according to convenience\n# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\ndefault_hooks.update(dict(checkpoint=dict(interval=4)))\n\ntrain_cfg.update(dict(val_interval=2))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_fpn_2x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_instance import *\n    from .._base_.default_runtime import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n    from .._base_.schedules.schedule_2x import *\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_fpn_8xb8_amp_lsj_200e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_fpn_amp_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_1x_coco import *\n\nfrom mmengine.optim.optimizer.amp_optimizer_wrapper import AmpOptimWrapper\n\noptim_wrapper.update(dict(type=AmpOptimWrapper))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_fpn_ms_poly_-3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.models.mask_rcnn_r50_fpn import *\n    from ..common.ms_poly_3x_coco_instance import *\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_r50_fpn_poly_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.coco_instance import *\n    from .._base_.default_runtime import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n    from .._base_.schedules.schedule_1x import *\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs),\n]\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_32x4d_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r101_fpn_1x_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nfrom mmdet.models.backbones.resnext import ResNeXt\n\nmodel = dict(\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_32x4d_fpn_2x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r50_fpn_2x_coco import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nfrom mmdet.models import ResNeXt\n\nmodel = dict(\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_32x4d_fpn_ms_poly_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from ..common.ms_poly_3x_coco_instance import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n\nfrom mmengine.model.weight_init import PretrainedInit\n\nfrom mmdet.models.backbones import ResNeXt\n\nmodel = dict(\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=32,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='open-mmlab://resnext101_32x4d')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_32x8d_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_x101_32x4d_fpn_1x_coco import *\n\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_32x8d_fpn_ms_poly_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_r101_fpn_1x_coco import *\n\nfrom mmcv.transforms import RandomChoiceResize, RandomFlip\nfrom mmcv.transforms.loading import LoadImageFromFile\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.models.backbones import ResNeXt\n\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n\nbackend_args = None\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=RandomChoiceResize,\n        scales=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),\n                (1333, 768), (1333, 800)],\n        keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs),\n]\n\ntrain_dataloader = dict(dataset=dict(pipeline=train_pipeline))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_32x8d_fpn_ms_poly_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from ..common.ms_poly_3x_coco_instance import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n\nfrom mmdet.models.backbones import ResNeXt\n\nmodel = dict(\n    # ResNeXt-101-32x8d model trained with Caffe2 at FB,\n    # so the mean and std need to be changed.\n    data_preprocessor=dict(\n        mean=[103.530, 116.280, 123.675],\n        std=[57.375, 57.120, 58.395],\n        bgr_to_rgb=False),\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=32,\n        base_width=8,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=False),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnext101_32x8d')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_64_4d_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_x101_32x4d_fpn_1x_coco import *\n\nmodel = dict(\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_64x4d_fpn_2x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .mask_rcnn_x101_32x4d_fpn_2x_coco import *\n\nmodel = dict(\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "mmdet/configs/mask_rcnn/mask_rcnn_x101_64x4d_fpn_ms_poly_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from ..common.ms_poly_3x_coco_instance import *\n    from .._base_.models.mask_rcnn_r50_fpn import *\n\nfrom mmdet.models.backbones import ResNeXt\n\nmodel = dict(\n    backbone=dict(\n        type=ResNeXt,\n        depth=101,\n        groups=64,\n        base_width=4,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=True),\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='open-mmlab://resnext101_64x4d')))\n"
  },
  {
    "path": "mmdet/configs/maskformer/maskformer_r50_ms_16xb1_75e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms import RandomChoice, RandomChoiceResize\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\nfrom mmengine.optim.optimizer import OptimWrapper\nfrom mmengine.optim.scheduler import MultiStepLR\nfrom mmengine.runner import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.nn.modules.activation import ReLU\nfrom torch.nn.modules.batchnorm import BatchNorm2d\nfrom torch.nn.modules.normalization import GroupNorm\nfrom torch.optim.adamw import AdamW\n\nfrom mmdet.datasets.transforms.transforms import RandomCrop\nfrom mmdet.models import MaskFormer\nfrom mmdet.models.backbones import ResNet\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.dense_heads.maskformer_head import MaskFormerHead\nfrom mmdet.models.layers.pixel_decoder import TransformerEncoderPixelDecoder\nfrom mmdet.models.losses import CrossEntropyLoss, DiceLoss, FocalLoss\nfrom mmdet.models.seg_heads.panoptic_fusion_heads import MaskFormerFusionHead\nfrom mmdet.models.task_modules.assigners.hungarian_assigner import \\\n    HungarianAssigner\nfrom mmdet.models.task_modules.assigners.match_cost import (ClassificationCost,\n                                                            DiceCost,\n                                                            FocalLossCost)\nfrom mmdet.models.task_modules.samplers import MaskPseudoSampler\n\nwith read_base():\n    from .._base_.datasets.coco_panoptic import *\n    from .._base_.default_runtime import *\n\ndata_preprocessor = dict(\n    type=DetDataPreprocessor,\n    mean=[123.675, 116.28, 103.53],\n    std=[58.395, 57.12, 57.375],\n    bgr_to_rgb=True,\n    pad_size_divisor=1,\n    pad_mask=True,\n    mask_pad_value=0,\n    pad_seg=True,\n    seg_pad_value=255)\n\nnum_things_classes = 80\nnum_stuff_classes = 53\nnum_classes = num_things_classes + num_stuff_classes\nmodel = dict(\n    type=MaskFormer,\n    data_preprocessor=data_preprocessor,\n    backbone=dict(\n        type=ResNet,\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=-1,\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(\n            type=PretrainedInit, checkpoint='torchvision://resnet50')),\n    panoptic_head=dict(\n        type=MaskFormerHead,\n        in_channels=[256, 512, 1024, 2048],  # pass to pixel_decoder inside\n        feat_channels=256,\n        out_channels=256,\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes,\n        num_queries=100,\n        pixel_decoder=dict(\n            type=TransformerEncoderPixelDecoder,\n            norm_cfg=dict(type=GroupNorm, num_groups=32),\n            act_cfg=dict(type=ReLU),\n            encoder=dict(  # DetrTransformerEncoder\n                num_layers=6,\n                layer_cfg=dict(  # DetrTransformerEncoderLayer\n                    self_attn_cfg=dict(  # MultiheadAttention\n                        embed_dims=256,\n                        num_heads=8,\n                        dropout=0.1,\n                        batch_first=True),\n                    ffn_cfg=dict(\n                        embed_dims=256,\n                        feedforward_channels=2048,\n                        num_fcs=2,\n                        ffn_drop=0.1,\n                        act_cfg=dict(type=ReLU, inplace=True)))),\n            positional_encoding=dict(num_feats=128, normalize=True)),\n        enforce_decoder_input_project=False,\n        positional_encoding=dict(num_feats=128, normalize=True),\n        transformer_decoder=dict(  # DetrTransformerDecoder\n            num_layers=6,\n            layer_cfg=dict(  # DetrTransformerDecoderLayer\n                self_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.1,\n                    batch_first=True),\n                cross_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.1,\n                    batch_first=True),\n                ffn_cfg=dict(\n                    embed_dims=256,\n                    feedforward_channels=2048,\n                    num_fcs=2,\n                    ffn_drop=0.1,\n                    act_cfg=dict(type=ReLU, inplace=True))),\n            return_intermediate=True),\n        loss_cls=dict(\n            type=CrossEntropyLoss,\n            use_sigmoid=False,\n            loss_weight=1.0,\n            reduction='mean',\n            class_weight=[1.0] * num_classes + [0.1]),\n        loss_mask=dict(\n            type=FocalLoss,\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            reduction='mean',\n            loss_weight=20.0),\n        loss_dice=dict(\n            type=DiceLoss,\n            use_sigmoid=True,\n            activate=True,\n            reduction='mean',\n            naive_dice=True,\n            eps=1.0,\n            loss_weight=1.0)),\n    panoptic_fusion_head=dict(\n        type=MaskFormerFusionHead,\n        num_things_classes=num_things_classes,\n        num_stuff_classes=num_stuff_classes,\n        loss_panoptic=None,\n        init_cfg=None),\n    train_cfg=dict(\n        assigner=dict(\n            type=HungarianAssigner,\n            match_costs=[\n                dict(type=ClassificationCost, weight=1.0),\n                dict(type=FocalLossCost, weight=20.0, binary_input=True),\n                dict(type=DiceCost, weight=1.0, pred_act=True, eps=1.0)\n            ]),\n        sampler=dict(type=MaskPseudoSampler)),\n    test_cfg=dict(\n        panoptic_on=True,\n        # For now, the dataset does not support\n        # evaluating semantic segmentation metric.\n        semantic_on=False,\n        instance_on=False,\n        # max_per_image is for instance segmentation.\n        max_per_image=100,\n        object_mask_thr=0.8,\n        iou_thr=0.8,\n        # In MaskFormer's panoptic postprocessing,\n        # it will not filter masks whose score is smaller than 0.5 .\n        filter_low_score=False),\n    init_cfg=None)\n\n# dataset settings\ntrain_pipeline = [\n    dict(type=LoadImageFromFile),\n    dict(\n        type=LoadPanopticAnnotations,\n        with_bbox=True,\n        with_mask=True,\n        with_seg=True),\n    dict(type=RandomFlip, prob=0.5),\n    # dict(type=Resize, scale=(1333, 800), keep_ratio=True),\n    dict(\n        type=RandomChoice,\n        transforms=[[\n            dict(\n                type=RandomChoiceResize,\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                resize_type=Resize,\n                keep_ratio=True)\n        ],\n                    [\n                        dict(\n                            type=RandomChoiceResize,\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            resize_type=Resize,\n                            keep_ratio=True),\n                        dict(\n                            type=RandomCrop,\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type=RandomChoiceResize,\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            resize_type=Resize,\n                            keep_ratio=True)\n                    ]]),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(\n    dict(batch_size=1, num_workers=1, dataset=dict(pipeline=train_pipeline)))\n\nval_dataloader.update(dict(batch_size=1, num_workers=1))\n\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(\n        type=AdamW,\n        lr=0.0001,\n        weight_decay=0.0001,\n        eps=1e-8,\n        betas=(0.9, 0.999)),\n    paramwise_cfg=dict(\n        custom_keys={\n            'backbone': dict(lr_mult=0.1, decay_mult=1.0),\n            'query_embed': dict(lr_mult=1.0, decay_mult=0.0)\n        },\n        norm_decay_mult=0.0),\n    clip_grad=dict(max_norm=0.01, norm_type=2))\n\nmax_epochs = 75\n\n# learning rate\nparam_scheduler = dict(\n    type=MultiStepLR,\n    begin=0,\n    end=max_epochs,\n    by_epoch=True,\n    milestones=[50],\n    gamma=0.1)\n\ntrain_cfg = dict(\n    type=EpochBasedTrainLoop, max_epochs=max_epochs, val_interval=1)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (16 GPUs) x (1 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=16)\n"
  },
  {
    "path": "mmdet/configs/maskformer/maskformer_swin_l_p4_w12_64xb1_ms_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.optim.scheduler import LinearLR\n\nfrom mmdet.models.backbones import SwinTransformer\nfrom mmdet.models.layers import PixelDecoder\n\nwith read_base():\n    from .maskformer_r50_ms_16xb1_75e_coco import *\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\ndepths = [2, 2, 18, 2]\nmodel.update(\n    dict(\n        backbone=dict(\n            _delete_=True,\n            type=SwinTransformer,\n            pretrain_img_size=384,\n            embed_dims=192,\n            patch_size=4,\n            window_size=12,\n            mlp_ratio=4,\n            depths=depths,\n            num_heads=[6, 12, 24, 48],\n            qkv_bias=True,\n            qk_scale=None,\n            drop_rate=0.,\n            attn_drop_rate=0.,\n            drop_path_rate=0.3,\n            patch_norm=True,\n            out_indices=(0, 1, 2, 3),\n            with_cp=False,\n            convert_weights=True,\n            init_cfg=dict(type=PretrainedInit, checkpoint=pretrained)),\n        panoptic_head=dict(\n            in_channels=[192, 384, 768, 1536],  # pass to pixel_decoder inside\n            pixel_decoder=dict(\n                _delete_=True,\n                type=PixelDecoder,\n                norm_cfg=dict(type=GroupNorm, num_groups=32),\n                act_cfg=dict(type=ReLU)),\n            enforce_decoder_input_project=True)))\n\n# optimizer\n\n# weight_decay = 0.01\n# norm_weight_decay = 0.0\n# embed_weight_decay = 0.0\nembed_multi = dict(lr_mult=1.0, decay_mult=0.0)\nnorm_multi = dict(lr_mult=1.0, decay_mult=0.0)\ncustom_keys = {\n    'norm': norm_multi,\n    'absolute_pos_embed': embed_multi,\n    'relative_position_bias_table': embed_multi,\n    'query_embed': embed_multi\n}\n\noptim_wrapper.update(\n    dict(\n        optimizer=dict(lr=6e-5, weight_decay=0.01),\n        paramwise_cfg=dict(custom_keys=custom_keys, norm_decay_mult=0.0)))\n\nmax_epochs = 300\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=1e-6, by_epoch=False, begin=0, end=1500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[250],\n        gamma=0.1)\n]\n\ntrain_cfg.update(dict(max_epochs=max_epochs))\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (64 GPUs) x (1 samples per GPU)\nauto_scale_lr.update(dict(base_batch_size=64))\n"
  },
  {
    "path": "mmdet/configs/panoptic_fpn/panoptic_fpn_r101_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\n\nwith read_base():\n    from .panoptic_fpn_r50_fpn_1x_coco import *\n\nmodel.update(\n    dict(\n        backbone=dict(\n            depth=101,\n            init_cfg=dict(\n                type=PretrainedInit, checkpoint='torchvision://resnet101'))))\n"
  },
  {
    "path": "mmdet/configs/panoptic_fpn/panoptic_fpn_r101_fpn_ms_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.model.weight_init import PretrainedInit\n\nwith read_base():\n    from .panoptic_fpn_r50_fpn_ms_3x_coco import *\n\nmodel.update(\n    dict(\n        backbone=dict(\n            depth=101,\n            init_cfg=dict(\n                type=PretrainedInit, checkpoint='torchvision://resnet101'))))\n"
  },
  {
    "path": "mmdet/configs/panoptic_fpn/panoptic_fpn_r50_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.models.mask_rcnn_r50_fpn import *\n    from .._base_.datasets.coco_panoptic import *\n    from .._base_.schedules.schedule_1x import *\n    from .._base_.default_runtime import *\n\nfrom mmcv.ops import nms\nfrom torch.nn import GroupNorm\n\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.detectors.panoptic_fpn import PanopticFPN\nfrom mmdet.models.losses.cross_entropy_loss import CrossEntropyLoss\nfrom mmdet.models.seg_heads.panoptic_fpn_head import PanopticFPNHead\nfrom mmdet.models.seg_heads.panoptic_fusion_heads import HeuristicFusionHead\n\nmodel.update(\n    dict(\n        type=PanopticFPN,\n        data_preprocessor=dict(\n            type=DetDataPreprocessor,\n            mean=[123.675, 116.28, 103.53],\n            std=[58.395, 57.12, 57.375],\n            bgr_to_rgb=True,\n            pad_size_divisor=32,\n            pad_mask=True,\n            mask_pad_value=0,\n            pad_seg=True,\n            seg_pad_value=255),\n        semantic_head=dict(\n            type=PanopticFPNHead,\n            num_things_classes=80,\n            num_stuff_classes=53,\n            in_channels=256,\n            inner_channels=128,\n            start_level=0,\n            end_level=4,\n            norm_cfg=dict(type=GroupNorm, num_groups=32, requires_grad=True),\n            conv_cfg=None,\n            loss_seg=dict(\n                type=CrossEntropyLoss, ignore_index=255, loss_weight=0.5)),\n        panoptic_fusion_head=dict(\n            type=HeuristicFusionHead,\n            num_things_classes=80,\n            num_stuff_classes=53),\n        test_cfg=dict(\n            rcnn=dict(\n                score_thr=0.6,\n                nms=dict(type=nms, iou_threshold=0.5, class_agnostic=True),\n                max_per_img=100,\n                mask_thr_binary=0.5),\n            # used in HeuristicFusionHead\n            panoptic=dict(mask_overlap=0.5, stuff_area_limit=4096))))\n\n# Forced to remove NumClassCheckHook\ncustom_hooks = []\n"
  },
  {
    "path": "mmdet/configs/panoptic_fpn/panoptic_fpn_r50_fpn_ms_3x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\nfrom mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR\n\nwith read_base():\n    from .panoptic_fpn_r50_fpn_1x_coco import *\n\nfrom mmcv.transforms import RandomResize\nfrom mmcv.transforms.loading import LoadImageFromFile\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadPanopticAnnotations\nfrom mmdet.datasets.transforms.transforms import RandomFlip\n\n# In mstrain 3x config, img_scale=[(1333, 640), (1333, 800)],\n# multiscale_mode='range'\ntrain_pipeline = [\n    dict(type=LoadImageFromFile),\n    dict(\n        type=LoadPanopticAnnotations,\n        with_bbox=True,\n        with_mask=True,\n        with_seg=True),\n    dict(type=RandomResize, scale=[(1333, 640), (1333, 800)], keep_ratio=True),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n\n# TODO: Use RepeatDataset to speed up training\n# training schedule for 3x\ntrain_cfg.update(dict(max_epochs=36, val_interval=3))\n\n# learning rate\nparam_scheduler = [\n    dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500),\n    dict(\n        type=MultiStepLR,\n        begin=0,\n        end=36,\n        by_epoch=True,\n        milestones=[24, 33],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "mmdet/configs/qdtrack/qdtrack_faster_rcnn_r50_fpn_4e_base.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.models.faster_rcnn_r50_fpn import *\n    from .._base_.models.faster_rcnn_r50_fpn import model\n    from .._base_.default_runtime import *\n\nfrom mmcv.ops import RoIAlign\nfrom mmengine.hooks import LoggerHook, SyncBuffersHook\nfrom mmengine.model.weight_init import PretrainedInit\nfrom mmengine.optim import MultiStepLR, OptimWrapper\nfrom mmengine.runner.runner import EpochBasedTrainLoop, TestLoop, ValLoop\nfrom torch.nn.modules.batchnorm import BatchNorm2d\nfrom torch.nn.modules.normalization import GroupNorm\nfrom torch.optim import SGD\n\nfrom mmdet.engine.hooks import TrackVisualizationHook\nfrom mmdet.models import (QDTrack, QuasiDenseEmbedHead, QuasiDenseTracker,\n                          QuasiDenseTrackHead, SingleRoIExtractor,\n                          TrackDataPreprocessor)\nfrom mmdet.models.losses import (L1Loss, MarginL2Loss,\n                                 MultiPosCrossEntropyLoss, SmoothL1Loss)\nfrom mmdet.models.task_modules import (CombinedSampler,\n                                       InstanceBalancedPosSampler,\n                                       MaxIoUAssigner, RandomSampler)\nfrom mmdet.visualization import TrackLocalVisualizer\n\ndetector = model\ndetector.pop('data_preprocessor')\n\ndetector['backbone'].update(\n    dict(\n        norm_cfg=dict(type=BatchNorm2d, requires_grad=False),\n        style='caffe',\n        init_cfg=dict(\n            type=PretrainedInit,\n            checkpoint='open-mmlab://detectron2/resnet50_caffe')))\ndetector.rpn_head.loss_bbox.update(\n    dict(type=SmoothL1Loss, beta=1.0 / 9.0, loss_weight=1.0))\ndetector.rpn_head.bbox_coder.update(dict(clip_border=False))\ndetector.roi_head.bbox_head.update(dict(num_classes=1))\ndetector.roi_head.bbox_head.bbox_coder.update(dict(clip_border=False))\ndetector['init_cfg'] = dict(\n    type=PretrainedInit,\n    checkpoint=  # noqa: E251\n    'https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/'\n    'faster_rcnn_r50_fpn_1x_coco-person/'\n    'faster_rcnn_r50_fpn_1x_coco-person_20201216_175929-d022e227.pth'\n    # noqa: E501\n)\ndel model\n\nmodel = dict(\n    type=QDTrack,\n    data_preprocessor=dict(\n        type=TrackDataPreprocessor,\n        mean=[103.530, 116.280, 123.675],\n        std=[1.0, 1.0, 1.0],\n        bgr_to_rgb=False,\n        pad_size_divisor=32),\n    detector=detector,\n    track_head=dict(\n        type=QuasiDenseTrackHead,\n        roi_extractor=dict(\n            type=SingleRoIExtractor,\n            roi_layer=dict(type=RoIAlign, output_size=7, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        embed_head=dict(\n            type=QuasiDenseEmbedHead,\n            num_convs=4,\n            num_fcs=1,\n            embed_channels=256,\n            norm_cfg=dict(type=GroupNorm, num_groups=32),\n            loss_track=dict(type=MultiPosCrossEntropyLoss, loss_weight=0.25),\n            loss_track_aux=dict(\n                type=MarginL2Loss,\n                neg_pos_ub=3,\n                pos_margin=0,\n                neg_margin=0.1,\n                hard_mining=True,\n                loss_weight=1.0)),\n        loss_bbox=dict(type=L1Loss, loss_weight=1.0),\n        train_cfg=dict(\n            assigner=dict(\n                type=MaxIoUAssigner,\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.5,\n                match_low_quality=False,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type=CombinedSampler,\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=3,\n                add_gt_as_proposals=True,\n                pos_sampler=dict(type=InstanceBalancedPosSampler),\n                neg_sampler=dict(type=RandomSampler)))),\n    tracker=dict(\n        type=QuasiDenseTracker,\n        init_score_thr=0.9,\n        obj_score_thr=0.5,\n        match_score_thr=0.5,\n        memo_tracklet_frames=30,\n        memo_backdrop_frames=1,\n        memo_momentum=0.8,\n        nms_conf_thr=0.5,\n        nms_backdrop_iou_thr=0.3,\n        nms_class_iou_thr=0.7,\n        with_cats=True,\n        match_metric='bisoftmax'))\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001),\n    clip_grad=dict(max_norm=35, norm_type=2))\n# learning policy\nparam_scheduler = [\n    dict(type=MultiStepLR, begin=0, end=4, by_epoch=True, milestones=[3])\n]\n\n# runtime settings\ntrain_cfg = dict(type=EpochBasedTrainLoop, max_epochs=4, val_interval=4)\nval_cfg = dict(type=ValLoop)\ntest_cfg = dict(type=TestLoop)\n\ndefault_hooks.update(\n    logger=dict(type=LoggerHook, interval=50),\n    visualization=dict(type=TrackVisualizationHook, draw=False))\n\nvisualizer.update(\n    type=TrackLocalVisualizer, vis_backends=vis_backends, name='visualizer')\n\n# custom hooks\ncustom_hooks = [\n    # Synchronize model buffers such as running_mean and running_var in BN\n    # at the end of each epoch\n    dict(type=SyncBuffersHook)\n]\n"
  },
  {
    "path": "mmdet/configs/qdtrack/qdtrack_faster_rcnn_r50_fpn_8xb2-4e_mot17halftrain_test-mot17halfval.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.datasets.mot_challenge import *\n    from .qdtrack_faster_rcnn_r50_fpn_4e_base import *\n\nfrom mmdet.evaluation import CocoVideoMetric, MOTChallengeMetric\n\n# evaluator\nval_evaluator = [\n    dict(type=CocoVideoMetric, metric=['bbox'], classwise=True),\n    dict(type=MOTChallengeMetric, metric=['HOTA', 'CLEAR', 'Identity'])\n]\n"
  },
  {
    "path": "mmdet/configs/retinanet/retinanet_r50_fpn_1x_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.models.retinanet_r50_fpn import *\n    from .._base_.datasets.coco_detection import *\n    from .._base_.schedules.schedule_1x import *\n    from .._base_.default_runtime import *\n    from .retinanet_tta import *\n\nfrom torch.optim.sgd import SGD\n\n# optimizer\noptim_wrapper.update(\n    dict(optimizer=dict(type=SGD, lr=0.01, momentum=0.9, weight_decay=0.0001)))\n"
  },
  {
    "path": "mmdet/configs/retinanet/retinanet_tta.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import TestTimeAug\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.datasets.transforms.transforms import RandomFlip, Resize\nfrom mmdet.models.test_time_augs.det_tta import DetTTAModel\n\ntta_model = dict(\n    type=DetTTAModel,\n    tta_cfg=dict(nms=dict(type='nms', iou_threshold=0.5), max_per_img=100))\n\nimg_scales = [(1333, 800), (666, 400), (2000, 1200)]\ntta_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=None),\n    dict(\n        type=TestTimeAug,\n        transforms=[\n            [dict(type=Resize, scale=s, keep_ratio=True) for s in img_scales],\n            [dict(type=RandomFlip, prob=1.),\n             dict(type=RandomFlip, prob=0.)],\n            [dict(type=LoadAnnotations, with_bbox=True)],\n            [\n                dict(\n                    type=PackDetInputs,\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'scale_factor', 'flip', 'flip_direction'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_ins_l_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_l_8xb32_300e_coco import *\n\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import RandomResize\nfrom mmengine.hooks.ema_hook import EMAHook\nfrom torch.nn.modules.activation import SiLU\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  Resize, YOLOXHSVRandomAug)\nfrom mmdet.engine.hooks.pipeline_switch_hook import PipelineSwitchHook\nfrom mmdet.models.dense_heads.rtmdet_ins_head import RTMDetInsSepBNHead\nfrom mmdet.models.layers.ema import ExpMomentumEMA\nfrom mmdet.models.losses.dice_loss import DiceLoss\nfrom mmdet.models.losses.gfocal_loss import QualityFocalLoss\nfrom mmdet.models.losses.iou_loss import GIoULoss\nfrom mmdet.models.task_modules.coders.distance_point_bbox_coder import \\\n    DistancePointBBoxCoder\nfrom mmdet.models.task_modules.prior_generators.point_generator import \\\n    MlvlPointGenerator\n\nmodel.merge(\n    dict(\n        bbox_head=dict(\n            _delete_=True,\n            type=RTMDetInsSepBNHead,\n            num_classes=80,\n            in_channels=256,\n            stacked_convs=2,\n            share_conv=True,\n            pred_kernel_size=1,\n            feat_channels=256,\n            act_cfg=dict(type=SiLU, inplace=True),\n            norm_cfg=dict(type='SyncBN', requires_grad=True),\n            anchor_generator=dict(\n                type=MlvlPointGenerator, offset=0, strides=[8, 16, 32]),\n            bbox_coder=dict(type=DistancePointBBoxCoder),\n            loss_cls=dict(\n                type=QualityFocalLoss,\n                use_sigmoid=True,\n                beta=2.0,\n                loss_weight=1.0),\n            loss_bbox=dict(type=GIoULoss, loss_weight=2.0),\n            loss_mask=dict(\n                type=DiceLoss, loss_weight=2.0, eps=5e-6, reduction='mean')),\n        test_cfg=dict(\n            nms_pre=1000,\n            min_bbox_size=0,\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.6),\n            max_per_img=100,\n            mask_thr_binary=0.5),\n    ))\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(type=CachedMosaic, img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type=RandomResize,\n        scale=(1280, 1280),\n        ratio_range=(0.1, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(\n        type=RandomCrop,\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type=CachedMixUp,\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type=FilterAnnotations, min_gt_bbox_wh=(1, 1)),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(\n    dict(pin_memory=True, dataset=dict(pipeline=train_pipeline)))\n\ntrain_pipeline_stage2 = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=RandomResize,\n        scale=(640, 640),\n        ratio_range=(0.1, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(\n        type=RandomCrop,\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type=FilterAnnotations, min_gt_bbox_wh=(1, 1)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type=PackDetInputs)\n]\ncustom_hooks = [\n    dict(\n        type=EMAHook,\n        ema_type=ExpMomentumEMA,\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type=PipelineSwitchHook,\n        switch_epoch=280,\n        switch_pipeline=train_pipeline_stage2)\n]\n\nval_evaluator.update(dict(metric=['bbox', 'segm']))\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_ins_m_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_ins_l_8xb32_300e_coco import *\n\nmodel.update(\n    dict(\n        backbone=dict(deepen_factor=0.67, widen_factor=0.75),\n        neck=dict(\n            in_channels=[192, 384, 768], out_channels=192, num_csp_blocks=2),\n        bbox_head=dict(in_channels=192, feat_channels=192)))\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_ins_s_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_ins_l_8xb32_300e_coco import *\n\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import RandomResize\nfrom mmengine.hooks.ema_hook import EMAHook\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  Resize, YOLOXHSVRandomAug)\nfrom mmdet.engine.hooks.pipeline_switch_hook import PipelineSwitchHook\nfrom mmdet.models.layers.ema import ExpMomentumEMA\n\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-s_imagenet_600e.pth'  # noqa\nmodel.update(\n    dict(\n        backbone=dict(\n            deepen_factor=0.33,\n            widen_factor=0.5,\n            init_cfg=dict(\n                type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n        neck=dict(\n            in_channels=[128, 256, 512], out_channels=128, num_csp_blocks=1),\n        bbox_head=dict(in_channels=128, feat_channels=128)))\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(type=CachedMosaic, img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type=RandomResize,\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(\n        type=RandomCrop,\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type=CachedMixUp,\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type=FilterAnnotations, min_gt_bbox_wh=(1, 1)),\n    dict(type=PackDetInputs)\n]\n\ntrain_pipeline_stage2 = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=RandomResize,\n        scale=(640, 640),\n        ratio_range=(0.5, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(\n        type=RandomCrop,\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type=FilterAnnotations, min_gt_bbox_wh=(1, 1)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n\ncustom_hooks = [\n    dict(\n        type=EMAHook,\n        ema_type=ExpMomentumEMA,\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type=PipelineSwitchHook,\n        switch_epoch=280,\n        switch_pipeline=train_pipeline_stage2)\n]\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_ins_tiny_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_ins_s_8xb32_300e_coco import *\n\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import RandomResize\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import (FilterAnnotations,\n                                               LoadAnnotations)\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  Resize, YOLOXHSVRandomAug)\n\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-tiny_imagenet_600e.pth'  # noqa\n\nmodel.update(\n    dict(\n        backbone=dict(\n            deepen_factor=0.167,\n            widen_factor=0.375,\n            init_cfg=dict(\n                type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n        neck=dict(\n            in_channels=[96, 192, 384], out_channels=96, num_csp_blocks=1),\n        bbox_head=dict(in_channels=96, feat_channels=96)))\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(\n        type=LoadAnnotations, with_bbox=True, with_mask=True, poly2mask=False),\n    dict(\n        type=CachedMosaic,\n        img_scale=(640, 640),\n        pad_val=114.0,\n        max_cached_images=20,\n        random_pop=False),\n    dict(\n        type=RandomResize,\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(type=RandomCrop, crop_size=(640, 640)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type=CachedMixUp,\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=10,\n        random_pop=False,\n        pad_val=(114, 114, 114),\n        prob=0.5),\n    dict(type=FilterAnnotations, min_gt_bbox_wh=(1, 1)),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_ins_x_8xb16_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_ins_l_8xb32_300e_coco import *\nfrom mmengine.optim.scheduler.lr_scheduler import CosineAnnealingLR, LinearLR\n\nmodel.update(\n    dict(\n        backbone=dict(deepen_factor=1.33, widen_factor=1.25),\n        neck=dict(\n            in_channels=[320, 640, 1280], out_channels=320, num_csp_blocks=4),\n        bbox_head=dict(in_channels=320, feat_channels=320)))\n\nbase_lr = 0.002\n\n# optimizer\noptim_wrapper.update(dict(optimizer=dict(lr=base_lr)))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type=LinearLR, start_factor=1.0e-5, by_epoch=False, begin=0, end=1000),\n    dict(\n        # use cosine lr from 150 to 300 epoch\n        type=CosineAnnealingLR,\n        eta_min=base_lr * 0.05,\n        begin=max_epochs // 2,\n        end=max_epochs,\n        T_max=max_epochs // 2,\n        by_epoch=True,\n        convert_to_iter_based=True),\n]\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_l_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .._base_.default_runtime import *\n    from .._base_.schedules.schedule_1x import *\n    from .._base_.datasets.coco_detection import *\n    from .rtmdet_tta import *\n\nfrom mmcv.ops import nms\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import RandomResize\nfrom mmengine.hooks.ema_hook import EMAHook\nfrom mmengine.optim.optimizer.optimizer_wrapper import OptimWrapper\nfrom mmengine.optim.scheduler.lr_scheduler import CosineAnnealingLR, LinearLR\nfrom torch.nn import SyncBatchNorm\nfrom torch.nn.modules.activation import SiLU\nfrom torch.optim.adamw import AdamW\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  Resize, YOLOXHSVRandomAug)\nfrom mmdet.engine.hooks.pipeline_switch_hook import PipelineSwitchHook\nfrom mmdet.models.backbones.cspnext import CSPNeXt\nfrom mmdet.models.data_preprocessors.data_preprocessor import \\\n    DetDataPreprocessor\nfrom mmdet.models.dense_heads.rtmdet_head import RTMDetSepBNHead\nfrom mmdet.models.detectors.rtmdet import RTMDet\nfrom mmdet.models.layers.ema import ExpMomentumEMA\nfrom mmdet.models.losses.gfocal_loss import QualityFocalLoss\nfrom mmdet.models.losses.iou_loss import GIoULoss\nfrom mmdet.models.necks.cspnext_pafpn import CSPNeXtPAFPN\nfrom mmdet.models.task_modules.assigners.dynamic_soft_label_assigner import \\\n    DynamicSoftLabelAssigner\nfrom mmdet.models.task_modules.coders.distance_point_bbox_coder import \\\n    DistancePointBBoxCoder\nfrom mmdet.models.task_modules.prior_generators.point_generator import \\\n    MlvlPointGenerator\n\nmodel = dict(\n    type=RTMDet,\n    data_preprocessor=dict(\n        type=DetDataPreprocessor,\n        mean=[103.53, 116.28, 123.675],\n        std=[57.375, 57.12, 58.395],\n        bgr_to_rgb=False,\n        batch_augments=None),\n    backbone=dict(\n        type=CSPNeXt,\n        arch='P5',\n        expand_ratio=0.5,\n        deepen_factor=1,\n        widen_factor=1,\n        channel_attention=True,\n        norm_cfg=dict(type=SyncBatchNorm),\n        act_cfg=dict(type=SiLU, inplace=True)),\n    neck=dict(\n        type=CSPNeXtPAFPN,\n        in_channels=[256, 512, 1024],\n        out_channels=256,\n        num_csp_blocks=3,\n        expand_ratio=0.5,\n        norm_cfg=dict(type=SyncBatchNorm),\n        act_cfg=dict(type=SiLU, inplace=True)),\n    bbox_head=dict(\n        type=RTMDetSepBNHead,\n        num_classes=80,\n        in_channels=256,\n        stacked_convs=2,\n        feat_channels=256,\n        anchor_generator=dict(\n            type=MlvlPointGenerator, offset=0, strides=[8, 16, 32]),\n        bbox_coder=dict(type=DistancePointBBoxCoder),\n        loss_cls=dict(\n            type=QualityFocalLoss, use_sigmoid=True, beta=2.0,\n            loss_weight=1.0),\n        loss_bbox=dict(type=GIoULoss, loss_weight=2.0),\n        with_objectness=False,\n        exp_on_reg=True,\n        share_conv=True,\n        pred_kernel_size=1,\n        norm_cfg=dict(type=SyncBatchNorm),\n        act_cfg=dict(type=SiLU, inplace=True)),\n    train_cfg=dict(\n        assigner=dict(type=DynamicSoftLabelAssigner, topk=13),\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=30000,\n        min_bbox_size=0,\n        score_thr=0.001,\n        nms=dict(type=nms, iou_threshold=0.65),\n        max_per_img=300),\n)\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(type=CachedMosaic, img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type=RandomResize,\n        scale=(1280, 1280),\n        ratio_range=(0.1, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(type=RandomCrop, crop_size=(640, 640)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type=CachedMixUp,\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type=PackDetInputs)\n]\n\ntrain_pipeline_stage2 = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=RandomResize,\n        scale=(640, 640),\n        ratio_range=(0.1, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(type=RandomCrop, crop_size=(640, 640)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type=PackDetInputs)\n]\n\ntest_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=Resize, scale=(640, 640), keep_ratio=True),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=PackDetInputs,\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader.update(\n    dict(\n        batch_size=32,\n        num_workers=10,\n        batch_sampler=None,\n        pin_memory=True,\n        dataset=dict(pipeline=train_pipeline)))\nval_dataloader.update(\n    dict(batch_size=5, num_workers=10, dataset=dict(pipeline=test_pipeline)))\ntest_dataloader = val_dataloader\n\nmax_epochs = 300\nstage2_num_epochs = 20\nbase_lr = 0.004\ninterval = 10\n\ntrain_cfg.update(\n    dict(\n        max_epochs=max_epochs,\n        val_interval=interval,\n        dynamic_intervals=[(max_epochs - stage2_num_epochs, 1)]))\n\nval_evaluator.update(dict(proposal_nums=(100, 1, 10)))\ntest_evaluator = val_evaluator\n\n# optimizer\noptim_wrapper = dict(\n    type=OptimWrapper,\n    optimizer=dict(type=AdamW, lr=base_lr, weight_decay=0.05),\n    paramwise_cfg=dict(\n        norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True))\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type=LinearLR, start_factor=1.0e-5, by_epoch=False, begin=0, end=1000),\n    dict(\n        # use cosine lr from 150 to 300 epoch\n        type=CosineAnnealingLR,\n        eta_min=base_lr * 0.05,\n        begin=max_epochs // 2,\n        end=max_epochs,\n        T_max=max_epochs // 2,\n        by_epoch=True,\n        convert_to_iter_based=True),\n]\n\n# hooks\ndefault_hooks.update(\n    dict(\n        checkpoint=dict(\n            interval=interval,\n            max_keep_ckpts=3  # only keep latest 3 checkpoints\n        )))\n\ncustom_hooks = [\n    dict(\n        type=EMAHook,\n        ema_type=ExpMomentumEMA,\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type=PipelineSwitchHook,\n        switch_epoch=max_epochs - stage2_num_epochs,\n        switch_pipeline=train_pipeline_stage2)\n]\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_m_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_l_8xb32_300e_coco import *\n\nmodel.update(\n    dict(\n        backbone=dict(deepen_factor=0.67, widen_factor=0.75),\n        neck=dict(\n            in_channels=[192, 384, 768], out_channels=192, num_csp_blocks=2),\n        bbox_head=dict(in_channels=192, feat_channels=192)))\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_s_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_l_8xb32_300e_coco import *\n\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import RandomResize\nfrom mmengine.hooks.ema_hook import EMAHook\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  Resize, YOLOXHSVRandomAug)\nfrom mmdet.engine.hooks.pipeline_switch_hook import PipelineSwitchHook\nfrom mmdet.models.layers.ema import ExpMomentumEMA\n\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-s_imagenet_600e.pth'  # noqa\nmodel.update(\n    dict(\n        backbone=dict(\n            deepen_factor=0.33,\n            widen_factor=0.5,\n            init_cfg=dict(\n                type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n        neck=dict(\n            in_channels=[128, 256, 512], out_channels=128, num_csp_blocks=1),\n        bbox_head=dict(in_channels=128, feat_channels=128, exp_on_reg=False)))\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(type=CachedMosaic, img_scale=(640, 640), pad_val=114.0),\n    dict(\n        type=RandomResize,\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(type=RandomCrop, crop_size=(640, 640)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type=CachedMixUp,\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=20,\n        pad_val=(114, 114, 114)),\n    dict(type=PackDetInputs)\n]\n\ntrain_pipeline_stage2 = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=RandomResize,\n        scale=(640, 640),\n        ratio_range=(0.5, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(type=RandomCrop, crop_size=(640, 640)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n\ncustom_hooks = [\n    dict(\n        type=EMAHook,\n        ema_type=ExpMomentumEMA,\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49),\n    dict(\n        type=PipelineSwitchHook,\n        switch_epoch=280,\n        switch_pipeline=train_pipeline_stage2)\n]\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_tiny_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_s_8xb32_300e_coco import *\n\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import RandomResize\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.datasets.transforms.transforms import (CachedMixUp, CachedMosaic,\n                                                  Pad, RandomCrop, RandomFlip,\n                                                  Resize, YOLOXHSVRandomAug)\n\ncheckpoint = 'https://download.openmmlab.com/mmdetection/v3.0/rtmdet/cspnext_rsb_pretrain/cspnext-tiny_imagenet_600e.pth'  # noqa\n\nmodel.update(\n    dict(\n        backbone=dict(\n            deepen_factor=0.167,\n            widen_factor=0.375,\n            init_cfg=dict(\n                type='Pretrained', prefix='backbone.', checkpoint=checkpoint)),\n        neck=dict(\n            in_channels=[96, 192, 384], out_channels=96, num_csp_blocks=1),\n        bbox_head=dict(in_channels=96, feat_channels=96, exp_on_reg=False)))\n\ntrain_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=backend_args),\n    dict(type=LoadAnnotations, with_bbox=True),\n    dict(\n        type=CachedMosaic,\n        img_scale=(640, 640),\n        pad_val=114.0,\n        max_cached_images=20,\n        random_pop=False),\n    dict(\n        type=RandomResize,\n        scale=(1280, 1280),\n        ratio_range=(0.5, 2.0),\n        resize_type=Resize,\n        keep_ratio=True),\n    dict(type=RandomCrop, crop_size=(640, 640)),\n    dict(type=YOLOXHSVRandomAug),\n    dict(type=RandomFlip, prob=0.5),\n    dict(type=Pad, size=(640, 640), pad_val=dict(img=(114, 114, 114))),\n    dict(\n        type=CachedMixUp,\n        img_scale=(640, 640),\n        ratio_range=(1.0, 1.0),\n        max_cached_images=10,\n        random_pop=False,\n        pad_val=(114, 114, 114),\n        prob=0.5),\n    dict(type=PackDetInputs)\n]\n\ntrain_dataloader.update(dict(dataset=dict(pipeline=train_pipeline)))\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_tta.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms.loading import LoadImageFromFile\nfrom mmcv.transforms.processing import TestTimeAug\n\nfrom mmdet.datasets.transforms.formatting import PackDetInputs\nfrom mmdet.datasets.transforms.loading import LoadAnnotations\nfrom mmdet.datasets.transforms.transforms import Pad, RandomFlip, Resize\nfrom mmdet.models.test_time_augs.det_tta import DetTTAModel\n\ntta_model = dict(\n    type=DetTTAModel,\n    tta_cfg=dict(nms=dict(type='nms', iou_threshold=0.6), max_per_img=100))\n\nimg_scales = [(640, 640), (320, 320), (960, 960)]\n\ntta_pipeline = [\n    dict(type=LoadImageFromFile, backend_args=None),\n    dict(\n        type=TestTimeAug,\n        transforms=[\n            [dict(type=Resize, scale=s, keep_ratio=True) for s in img_scales],\n            [\n                # ``RandomFlip`` must be placed before ``Pad``, otherwise\n                # bounding box coordinates after flipping cannot be\n                # recovered correctly.\n                dict(type=RandomFlip, prob=1.),\n                dict(type=RandomFlip, prob=0.)\n            ],\n            [\n                dict(\n                    type=Pad,\n                    size=(960, 960),\n                    pad_val=dict(img=(114, 114, 114))),\n            ],\n            [dict(type=LoadAnnotations, with_bbox=True)],\n            [\n                dict(\n                    type=PackDetInputs,\n                    meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                               'scale_factor', 'flip', 'flip_direction'))\n            ]\n        ])\n]\n"
  },
  {
    "path": "mmdet/configs/rtmdet/rtmdet_x_8xb32_300e_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Please refer to https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#a-pure-python-style-configuration-file-beta for more details. # noqa\n# mmcv >= 2.0.1\n# mmengine >= 0.8.0\n\nfrom mmengine.config import read_base\n\nwith read_base():\n    from .rtmdet_l_8xb32_300e_coco import *\n\nmodel.update(\n    dict(\n        backbone=dict(deepen_factor=1.33, widen_factor=1.25),\n        neck=dict(\n            in_channels=[320, 640, 1280], out_channels=320, num_csp_blocks=4),\n        bbox_head=dict(in_channels=320, feat_channels=320)))\n"
  },
  {
    "path": "mmdet/datasets/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .ade20k import (ADE20KInstanceDataset, ADE20KPanopticDataset,\n                     ADE20KSegDataset)\nfrom .base_det_dataset import BaseDetDataset\nfrom .base_semseg_dataset import BaseSegDataset\nfrom .base_video_dataset import BaseVideoDataset\nfrom .cityscapes import CityscapesDataset\nfrom .coco import CocoDataset\nfrom .coco_caption import CocoCaptionDataset\nfrom .coco_panoptic import CocoPanopticDataset\nfrom .coco_semantic import CocoSegDataset\nfrom .crowdhuman import CrowdHumanDataset\nfrom .dataset_wrappers import ConcatDataset, MultiImageMixDataset\nfrom .deepfashion import DeepFashionDataset\nfrom .dod import DODDataset\nfrom .dsdl import DSDLDetDataset\nfrom .flickr30k import Flickr30kDataset\nfrom .isaid import iSAIDDataset\nfrom .lvis import LVISDataset, LVISV1Dataset, LVISV05Dataset\nfrom .mdetr_style_refcoco import MDETRStyleRefCocoDataset\nfrom .mot_challenge_dataset import MOTChallengeDataset\nfrom .objects365 import Objects365V1Dataset, Objects365V2Dataset\nfrom .odvg import ODVGDataset\nfrom .openimages import OpenImagesChallengeDataset, OpenImagesDataset\nfrom .refcoco import RefCocoDataset\nfrom .reid_dataset import ReIDDataset\nfrom .samplers import (AspectRatioBatchSampler, ClassAwareSampler,\n                       CustomSampleSizeSampler, GroupMultiSourceSampler,\n                       MultiSourceSampler, TrackAspectRatioBatchSampler,\n                       TrackImgSampler)\nfrom .utils import get_loading_pipeline\nfrom .v3det import V3DetDataset\nfrom .voc import VOCDataset\nfrom .wider_face import WIDERFaceDataset\nfrom .xml_style import XMLDataset\nfrom .youtube_vis_dataset import YouTubeVISDataset\n\n__all__ = [\n    'XMLDataset', 'CocoDataset', 'DeepFashionDataset', 'VOCDataset',\n    'CityscapesDataset', 'LVISDataset', 'LVISV05Dataset', 'LVISV1Dataset',\n    'WIDERFaceDataset', 'get_loading_pipeline', 'CocoPanopticDataset',\n    'MultiImageMixDataset', 'OpenImagesDataset', 'OpenImagesChallengeDataset',\n    'AspectRatioBatchSampler', 'ClassAwareSampler', 'MultiSourceSampler',\n    'GroupMultiSourceSampler', 'BaseDetDataset', 'CrowdHumanDataset',\n    'Objects365V1Dataset', 'Objects365V2Dataset', 'DSDLDetDataset',\n    'BaseVideoDataset', 'MOTChallengeDataset', 'TrackImgSampler',\n    'ReIDDataset', 'YouTubeVISDataset', 'TrackAspectRatioBatchSampler',\n    'ADE20KPanopticDataset', 'CocoCaptionDataset', 'RefCocoDataset',\n    'BaseSegDataset', 'ADE20KSegDataset', 'CocoSegDataset',\n    'ADE20KInstanceDataset', 'iSAIDDataset', 'V3DetDataset', 'ConcatDataset',\n    'ODVGDataset', 'MDETRStyleRefCocoDataset', 'DODDataset',\n    'CustomSampleSizeSampler', 'Flickr30kDataset'\n]\n"
  },
  {
    "path": "mmdet/datasets/ade20k.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import List\n\nfrom mmengine import fileio\n\nfrom mmdet.registry import DATASETS\nfrom .base_semseg_dataset import BaseSegDataset\nfrom .coco import CocoDataset\nfrom .coco_panoptic import CocoPanopticDataset\n\nADE_PALETTE = [(120, 120, 120), (180, 120, 120), (6, 230, 230), (80, 50, 50),\n               (4, 200, 3), (120, 120, 80), (140, 140, 140), (204, 5, 255),\n               (230, 230, 230), (4, 250, 7), (224, 5, 255), (235, 255, 7),\n               (150, 5, 61), (120, 120, 70), (8, 255, 51), (255, 6, 82),\n               (143, 255, 140), (204, 255, 4), (255, 51, 7), (204, 70, 3),\n               (0, 102, 200), (61, 230, 250), (255, 6, 51), (11, 102, 255),\n               (255, 7, 71), (255, 9, 224), (9, 7, 230), (220, 220, 220),\n               (255, 9, 92), (112, 9, 255), (8, 255, 214), (7, 255, 224),\n               (255, 184, 6), (10, 255, 71), (255, 41, 10), (7, 255, 255),\n               (224, 255, 8), (102, 8, 255), (255, 61, 6), (255, 194, 7),\n               (255, 122, 8), (0, 255, 20), (255, 8, 41), (255, 5, 153),\n               (6, 51, 255), (235, 12, 255), (160, 150, 20), (0, 163, 255),\n               (140, 140, 140), (250, 10, 15), (20, 255, 0), (31, 255, 0),\n               (255, 31, 0), (255, 224, 0), (153, 255, 0), (0, 0, 255),\n               (255, 71, 0), (0, 235, 255), (0, 173, 255), (31, 0, 255),\n               (11, 200, 200), (255, 82, 0), (0, 255, 245), (0, 61, 255),\n               (0, 255, 112), (0, 255, 133), (255, 0, 0), (255, 163, 0),\n               (255, 102, 0), (194, 255, 0), (0, 143, 255), (51, 255, 0),\n               (0, 82, 255), (0, 255, 41), (0, 255, 173), (10, 0, 255),\n               (173, 255, 0), (0, 255, 153), (255, 92, 0), (255, 0, 255),\n               (255, 0, 245), (255, 0, 102), (255, 173, 0), (255, 0, 20),\n               (255, 184, 184), (0, 31, 255), (0, 255, 61), (0, 71, 255),\n               (255, 0, 204), (0, 255, 194), (0, 255, 82), (0, 10, 255),\n               (0, 112, 255), (51, 0, 255), (0, 194, 255), (0, 122, 255),\n               (0, 255, 163), (255, 153, 0), (0, 255, 10), (255, 112, 0),\n               (143, 255, 0), (82, 0, 255), (163, 255, 0), (255, 235, 0),\n               (8, 184, 170), (133, 0, 255), (0, 255, 92), (184, 0, 255),\n               (255, 0, 31), (0, 184, 255), (0, 214, 255), (255, 0, 112),\n               (92, 255, 0), (0, 224, 255), (112, 224, 255), (70, 184, 160),\n               (163, 0, 255), (153, 0, 255), (71, 255, 0), (255, 0, 163),\n               (255, 204, 0), (255, 0, 143), (0, 255, 235), (133, 255, 0),\n               (255, 0, 235), (245, 0, 255), (255, 0, 122), (255, 245, 0),\n               (10, 190, 212), (214, 255, 0), (0, 204, 255), (20, 0, 255),\n               (255, 255, 0), (0, 153, 255), (0, 41, 255), (0, 255, 204),\n               (41, 0, 255), (41, 255, 0), (173, 0, 255), (0, 245, 255),\n               (71, 0, 255), (122, 0, 255), (0, 255, 184), (0, 92, 255),\n               (184, 255, 0), (0, 133, 255), (255, 214, 0), (25, 194, 194),\n               (102, 255, 0), (92, 0, 255)]\n\n\n@DATASETS.register_module()\nclass ADE20KPanopticDataset(CocoPanopticDataset):\n    METAINFO = {\n        'classes':\n        ('bed', 'window', 'cabinet', 'person', 'door', 'table', 'curtain',\n         'chair', 'car', 'painting, picture', 'sofa', 'shelf', 'mirror',\n         'armchair', 'seat', 'fence', 'desk', 'wardrobe, closet, press',\n         'lamp', 'tub', 'rail', 'cushion', 'box', 'column, pillar',\n         'signboard, sign', 'chest of drawers, chest, bureau, dresser',\n         'counter', 'sink', 'fireplace', 'refrigerator, icebox', 'stairs',\n         'case, display case, showcase, vitrine',\n         'pool table, billiard table, snooker table', 'pillow',\n         'screen door, screen', 'bookcase', 'coffee table',\n         'toilet, can, commode, crapper, pot, potty, stool, throne', 'flower',\n         'book', 'bench', 'countertop', 'stove', 'palm, palm tree',\n         'kitchen island', 'computer', 'swivel chair', 'boat',\n         'arcade machine', 'bus', 'towel', 'light', 'truck', 'chandelier',\n         'awning, sunshade, sunblind', 'street lamp', 'booth', 'tv',\n         'airplane', 'clothes', 'pole',\n         'bannister, banister, balustrade, balusters, handrail',\n         'ottoman, pouf, pouffe, puff, hassock', 'bottle', 'van', 'ship',\n         'fountain', 'washer, automatic washer, washing machine',\n         'plaything, toy', 'stool', 'barrel, cask', 'basket, handbasket',\n         'bag', 'minibike, motorbike', 'oven', 'ball', 'food, solid food',\n         'step, stair', 'trade name', 'microwave', 'pot', 'animal', 'bicycle',\n         'dishwasher', 'screen', 'sculpture', 'hood, exhaust hood', 'sconce',\n         'vase', 'traffic light', 'tray', 'trash can', 'fan', 'plate',\n         'monitor', 'bulletin board', 'radiator', 'glass, drinking glass',\n         'clock', 'flag', 'wall', 'building', 'sky', 'floor', 'tree',\n         'ceiling', 'road, route', 'grass', 'sidewalk, pavement',\n         'earth, ground', 'mountain, mount', 'plant', 'water', 'house', 'sea',\n         'rug', 'field', 'rock, stone', 'base, pedestal, stand', 'sand',\n         'skyscraper', 'grandstand, covered stand', 'path', 'runway',\n         'stairway, staircase', 'river', 'bridge, span', 'blind, screen',\n         'hill', 'bar', 'hovel, hut, hutch, shack, shanty', 'tower',\n         'dirt track', 'land, ground, soil',\n         'escalator, moving staircase, moving stairway',\n         'buffet, counter, sideboard',\n         'poster, posting, placard, notice, bill, card', 'stage',\n         'conveyer belt, conveyor belt, conveyer, conveyor, transporter',\n         'canopy', 'pool', 'falls', 'tent', 'cradle', 'tank, storage tank',\n         'lake', 'blanket, cover', 'pier', 'crt screen', 'shower'),\n        'thing_classes':\n        ('bed', 'window', 'cabinet', 'person', 'door', 'table', 'curtain',\n         'chair', 'car', 'painting, picture', 'sofa', 'shelf', 'mirror',\n         'armchair', 'seat', 'fence', 'desk', 'wardrobe, closet, press',\n         'lamp', 'tub', 'rail', 'cushion', 'box', 'column, pillar',\n         'signboard, sign', 'chest of drawers, chest, bureau, dresser',\n         'counter', 'sink', 'fireplace', 'refrigerator, icebox', 'stairs',\n         'case, display case, showcase, vitrine',\n         'pool table, billiard table, snooker table', 'pillow',\n         'screen door, screen', 'bookcase', 'coffee table',\n         'toilet, can, commode, crapper, pot, potty, stool, throne', 'flower',\n         'book', 'bench', 'countertop', 'stove', 'palm, palm tree',\n         'kitchen island', 'computer', 'swivel chair', 'boat',\n         'arcade machine', 'bus', 'towel', 'light', 'truck', 'chandelier',\n         'awning, sunshade, sunblind', 'street lamp', 'booth', 'tv',\n         'airplane', 'clothes', 'pole',\n         'bannister, banister, balustrade, balusters, handrail',\n         'ottoman, pouf, pouffe, puff, hassock', 'bottle', 'van', 'ship',\n         'fountain', 'washer, automatic washer, washing machine',\n         'plaything, toy', 'stool', 'barrel, cask', 'basket, handbasket',\n         'bag', 'minibike, motorbike', 'oven', 'ball', 'food, solid food',\n         'step, stair', 'trade name', 'microwave', 'pot', 'animal', 'bicycle',\n         'dishwasher', 'screen', 'sculpture', 'hood, exhaust hood', 'sconce',\n         'vase', 'traffic light', 'tray', 'trash can', 'fan', 'plate',\n         'monitor', 'bulletin board', 'radiator', 'glass, drinking glass',\n         'clock', 'flag'),\n        'stuff_classes':\n        ('wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road, route',\n         'grass', 'sidewalk, pavement', 'earth, ground', 'mountain, mount',\n         'plant', 'water', 'house', 'sea', 'rug', 'field', 'rock, stone',\n         'base, pedestal, stand', 'sand', 'skyscraper',\n         'grandstand, covered stand', 'path', 'runway', 'stairway, staircase',\n         'river', 'bridge, span', 'blind, screen', 'hill', 'bar',\n         'hovel, hut, hutch, shack, shanty', 'tower', 'dirt track',\n         'land, ground, soil', 'escalator, moving staircase, moving stairway',\n         'buffet, counter, sideboard',\n         'poster, posting, placard, notice, bill, card', 'stage',\n         'conveyer belt, conveyor belt, conveyer, conveyor, transporter',\n         'canopy', 'pool', 'falls', 'tent', 'cradle', 'tank, storage tank',\n         'lake', 'blanket, cover', 'pier', 'crt screen', 'shower'),\n        'palette':\n        ADE_PALETTE\n    }\n\n\n@DATASETS.register_module()\nclass ADE20KInstanceDataset(CocoDataset):\n    METAINFO = {\n        'classes':\n        ('bed', 'windowpane', 'cabinet', 'person', 'door', 'table', 'curtain',\n         'chair', 'car', 'painting', 'sofa', 'shelf', 'mirror', 'armchair',\n         'seat', 'fence', 'desk', 'wardrobe', 'lamp', 'bathtub', 'railing',\n         'cushion', 'box', 'column', 'signboard', 'chest of drawers',\n         'counter', 'sink', 'fireplace', 'refrigerator', 'stairs', 'case',\n         'pool table', 'pillow', 'screen door', 'bookcase', 'coffee table',\n         'toilet', 'flower', 'book', 'bench', 'countertop', 'stove', 'palm',\n         'kitchen island', 'computer', 'swivel chair', 'boat',\n         'arcade machine', 'bus', 'towel', 'light', 'truck', 'chandelier',\n         'awning', 'streetlight', 'booth', 'television receiver', 'airplane',\n         'apparel', 'pole', 'bannister', 'ottoman', 'bottle', 'van', 'ship',\n         'fountain', 'washer', 'plaything', 'stool', 'barrel', 'basket', 'bag',\n         'minibike', 'oven', 'ball', 'food', 'step', 'trade name', 'microwave',\n         'pot', 'animal', 'bicycle', 'dishwasher', 'screen', 'sculpture',\n         'hood', 'sconce', 'vase', 'traffic light', 'tray', 'ashcan', 'fan',\n         'plate', 'monitor', 'bulletin board', 'radiator', 'glass', 'clock',\n         'flag'),\n        'palette': [(204, 5, 255), (230, 230, 230), (224, 5, 255),\n                    (150, 5, 61), (8, 255, 51), (255, 6, 82), (255, 51, 7),\n                    (204, 70, 3), (0, 102, 200), (255, 6, 51), (11, 102, 255),\n                    (255, 7, 71), (220, 220, 220), (8, 255, 214),\n                    (7, 255, 224), (255, 184, 6), (10, 255, 71), (7, 255, 255),\n                    (224, 255, 8), (102, 8, 255), (255, 61, 6), (255, 194, 7),\n                    (0, 255, 20), (255, 8, 41), (255, 5, 153), (6, 51, 255),\n                    (235, 12, 255), (0, 163, 255), (250, 10, 15), (20, 255, 0),\n                    (255, 224, 0), (0, 0, 255), (255, 71, 0), (0, 235, 255),\n                    (0, 173, 255), (0, 255, 245), (0, 255, 112), (0, 255, 133),\n                    (255, 0, 0), (255, 163, 0), (194, 255, 0), (0, 143, 255),\n                    (51, 255, 0), (0, 82, 255), (0, 255, 41), (0, 255, 173),\n                    (10, 0, 255), (173, 255, 0), (255, 92, 0), (255, 0, 245),\n                    (255, 0, 102), (255, 173, 0), (255, 0, 20), (0, 31, 255),\n                    (0, 255, 61), (0, 71, 255), (255, 0, 204), (0, 255, 194),\n                    (0, 255, 82), (0, 112, 255), (51, 0, 255), (0, 122, 255),\n                    (255, 153, 0), (0, 255, 10), (163, 255, 0), (255, 235, 0),\n                    (8, 184, 170), (184, 0, 255), (255, 0, 31), (0, 214, 255),\n                    (255, 0, 112), (92, 255, 0), (70, 184, 160), (163, 0, 255),\n                    (71, 255, 0), (255, 0, 163), (255, 204, 0), (255, 0, 143),\n                    (133, 255, 0), (255, 0, 235), (245, 0, 255), (255, 0, 122),\n                    (255, 245, 0), (214, 255, 0), (0, 204, 255), (255, 255, 0),\n                    (0, 153, 255), (0, 41, 255), (0, 255, 204), (41, 0, 255),\n                    (41, 255, 0), (173, 0, 255), (0, 245, 255), (0, 255, 184),\n                    (0, 92, 255), (184, 255, 0), (255, 214, 0), (25, 194, 194),\n                    (102, 255, 0), (92, 0, 255)],\n    }\n\n\n@DATASETS.register_module()\nclass ADE20KSegDataset(BaseSegDataset):\n    \"\"\"ADE20K dataset.\n\n    In segmentation map annotation for ADE20K, 0 stands for background, which\n    is not included in 150 categories. The ``img_suffix`` is fixed to '.jpg',\n    and ``seg_map_suffix`` is fixed to '.png'.\n    \"\"\"\n    METAINFO = dict(\n        classes=('wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road',\n                 'bed ', 'windowpane', 'grass', 'cabinet', 'sidewalk',\n                 'person', 'earth', 'door', 'table', 'mountain', 'plant',\n                 'curtain', 'chair', 'car', 'water', 'painting', 'sofa',\n                 'shelf', 'house', 'sea', 'mirror', 'rug', 'field', 'armchair',\n                 'seat', 'fence', 'desk', 'rock', 'wardrobe', 'lamp',\n                 'bathtub', 'railing', 'cushion', 'base', 'box', 'column',\n                 'signboard', 'chest of drawers', 'counter', 'sand', 'sink',\n                 'skyscraper', 'fireplace', 'refrigerator', 'grandstand',\n                 'path', 'stairs', 'runway', 'case', 'pool table', 'pillow',\n                 'screen door', 'stairway', 'river', 'bridge', 'bookcase',\n                 'blind', 'coffee table', 'toilet', 'flower', 'book', 'hill',\n                 'bench', 'countertop', 'stove', 'palm', 'kitchen island',\n                 'computer', 'swivel chair', 'boat', 'bar', 'arcade machine',\n                 'hovel', 'bus', 'towel', 'light', 'truck', 'tower',\n                 'chandelier', 'awning', 'streetlight', 'booth',\n                 'television receiver', 'airplane', 'dirt track', 'apparel',\n                 'pole', 'land', 'bannister', 'escalator', 'ottoman', 'bottle',\n                 'buffet', 'poster', 'stage', 'van', 'ship', 'fountain',\n                 'conveyer belt', 'canopy', 'washer', 'plaything',\n                 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall',\n                 'tent', 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food',\n                 'step', 'tank', 'trade name', 'microwave', 'pot', 'animal',\n                 'bicycle', 'lake', 'dishwasher', 'screen', 'blanket',\n                 'sculpture', 'hood', 'sconce', 'vase', 'traffic light',\n                 'tray', 'ashcan', 'fan', 'pier', 'crt screen', 'plate',\n                 'monitor', 'bulletin board', 'shower', 'radiator', 'glass',\n                 'clock', 'flag'),\n        palette=ADE_PALETTE)\n\n    def __init__(self,\n                 img_suffix='.jpg',\n                 seg_map_suffix='.png',\n                 return_classes=False,\n                 **kwargs) -> None:\n        self.return_classes = return_classes\n        super().__init__(\n            img_suffix=img_suffix, seg_map_suffix=seg_map_suffix, **kwargs)\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotation from directory or annotation file.\n\n        Returns:\n            List[dict]: All data info of dataset.\n        \"\"\"\n        data_list = []\n        img_dir = self.data_prefix.get('img_path', None)\n        ann_dir = self.data_prefix.get('seg_map_path', None)\n        for img in fileio.list_dir_or_file(\n                dir_path=img_dir,\n                list_dir=False,\n                suffix=self.img_suffix,\n                recursive=True,\n                backend_args=self.backend_args):\n            data_info = dict(img_path=osp.join(img_dir, img))\n            if ann_dir is not None:\n                seg_map = img.replace(self.img_suffix, self.seg_map_suffix)\n                data_info['seg_map_path'] = osp.join(ann_dir, seg_map)\n            data_info['label_map'] = self.label_map\n            if self.return_classes:\n                data_info['text'] = list(self._metainfo['classes'])\n            data_list.append(data_info)\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/api_wrappers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .coco_api import COCO, COCOeval, COCOPanoptic\nfrom .cocoeval_mp import COCOevalMP\n\n__all__ = ['COCO', 'COCOeval', 'COCOPanoptic', 'COCOevalMP']\n"
  },
  {
    "path": "mmdet/datasets/api_wrappers/coco_api.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# This file add snake case alias for coco api\n\nimport warnings\nfrom collections import defaultdict\nfrom typing import List, Optional, Union\n\nimport pycocotools\nfrom pycocotools.coco import COCO as _COCO\nfrom pycocotools.cocoeval import COCOeval as _COCOeval\n\n\nclass COCO(_COCO):\n    \"\"\"This class is almost the same as official pycocotools package.\n\n    It implements some snake case function aliases. So that the COCO class has\n    the same interface as LVIS class.\n    \"\"\"\n\n    def __init__(self, annotation_file=None):\n        if getattr(pycocotools, '__version__', '0') >= '12.0.2':\n            warnings.warn(\n                'mmpycocotools is deprecated. Please install official pycocotools by \"pip install pycocotools\"',  # noqa: E501\n                UserWarning)\n        super().__init__(annotation_file=annotation_file)\n        self.img_ann_map = self.imgToAnns\n        self.cat_img_map = self.catToImgs\n\n    def get_ann_ids(self, img_ids=[], cat_ids=[], area_rng=[], iscrowd=None):\n        return self.getAnnIds(img_ids, cat_ids, area_rng, iscrowd)\n\n    def get_cat_ids(self, cat_names=[], sup_names=[], cat_ids=[]):\n        return self.getCatIds(cat_names, sup_names, cat_ids)\n\n    def get_img_ids(self, img_ids=[], cat_ids=[]):\n        return self.getImgIds(img_ids, cat_ids)\n\n    def load_anns(self, ids):\n        return self.loadAnns(ids)\n\n    def load_cats(self, ids):\n        return self.loadCats(ids)\n\n    def load_imgs(self, ids):\n        return self.loadImgs(ids)\n\n\n# just for the ease of import\nCOCOeval = _COCOeval\n\n\nclass COCOPanoptic(COCO):\n    \"\"\"This wrapper is for loading the panoptic style annotation file.\n\n    The format is shown in the CocoPanopticDataset class.\n\n    Args:\n        annotation_file (str, optional): Path of annotation file.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self, annotation_file: Optional[str] = None) -> None:\n        super(COCOPanoptic, self).__init__(annotation_file)\n\n    def createIndex(self) -> None:\n        \"\"\"Create index.\"\"\"\n        # create index\n        print('creating index...')\n        # anns stores 'segment_id -> annotation'\n        anns, cats, imgs = {}, {}, {}\n        img_to_anns, cat_to_imgs = defaultdict(list), defaultdict(list)\n        if 'annotations' in self.dataset:\n            for ann in self.dataset['annotations']:\n                for seg_ann in ann['segments_info']:\n                    # to match with instance.json\n                    seg_ann['image_id'] = ann['image_id']\n                    img_to_anns[ann['image_id']].append(seg_ann)\n                    # segment_id is not unique in coco dataset orz...\n                    # annotations from different images but\n                    # may have same segment_id\n                    if seg_ann['id'] in anns.keys():\n                        anns[seg_ann['id']].append(seg_ann)\n                    else:\n                        anns[seg_ann['id']] = [seg_ann]\n\n            # filter out annotations from other images\n            img_to_anns_ = defaultdict(list)\n            for k, v in img_to_anns.items():\n                img_to_anns_[k] = [x for x in v if x['image_id'] == k]\n            img_to_anns = img_to_anns_\n\n        if 'images' in self.dataset:\n            for img_info in self.dataset['images']:\n                img_info['segm_file'] = img_info['file_name'].replace(\n                    '.jpg', '.png')\n                imgs[img_info['id']] = img_info\n\n        if 'categories' in self.dataset:\n            for cat in self.dataset['categories']:\n                cats[cat['id']] = cat\n\n        if 'annotations' in self.dataset and 'categories' in self.dataset:\n            for ann in self.dataset['annotations']:\n                for seg_ann in ann['segments_info']:\n                    cat_to_imgs[seg_ann['category_id']].append(ann['image_id'])\n\n        print('index created!')\n\n        self.anns = anns\n        self.imgToAnns = img_to_anns\n        self.catToImgs = cat_to_imgs\n        self.imgs = imgs\n        self.cats = cats\n\n    def load_anns(self,\n                  ids: Union[List[int], int] = []) -> Optional[List[dict]]:\n        \"\"\"Load anns with the specified ids.\n\n        ``self.anns`` is a list of annotation lists instead of a\n        list of annotations.\n\n        Args:\n            ids (Union[List[int], int]): Integer ids specifying anns.\n\n        Returns:\n            anns (List[dict], optional): Loaded ann objects.\n        \"\"\"\n        anns = []\n\n        if hasattr(ids, '__iter__') and hasattr(ids, '__len__'):\n            # self.anns is a list of annotation lists instead of\n            # a list of annotations\n            for id in ids:\n                anns += self.anns[id]\n            return anns\n        elif type(ids) == int:\n            return self.anns[ids]\n"
  },
  {
    "path": "mmdet/datasets/api_wrappers/cocoeval_mp.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport itertools\nimport time\nfrom collections import defaultdict\n\nimport numpy as np\nimport torch.multiprocessing as mp\nfrom mmengine.logging import MMLogger\nfrom pycocotools.cocoeval import COCOeval\nfrom tqdm import tqdm\n\n\nclass COCOevalMP(COCOeval):\n\n    def _prepare(self):\n        '''\n        Prepare ._gts and ._dts for evaluation based on params\n        :return: None\n        '''\n\n        def _toMask(anns, coco):\n            # modify ann['segmentation'] by reference\n            for ann in anns:\n                rle = coco.annToRLE(ann)\n                ann['segmentation'] = rle\n\n        p = self.params\n        if p.useCats:\n            gts = []\n            dts = []\n            img_ids = set(p.imgIds)\n            cat_ids = set(p.catIds)\n            for gt in self.cocoGt.dataset['annotations']:\n                if (gt['category_id'] in cat_ids) and (gt['image_id']\n                                                       in img_ids):\n                    gts.append(gt)\n            for dt in self.cocoDt.dataset['annotations']:\n                if (dt['category_id'] in cat_ids) and (dt['image_id']\n                                                       in img_ids):\n                    dts.append(dt)\n            # gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) # noqa\n            # dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) # noqa\n            # gts=self.cocoGt.dataset['annotations']\n            # dts=self.cocoDt.dataset['annotations']\n        else:\n            gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds))\n            dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds))\n\n        # convert ground truth to mask if iouType == 'segm'\n        if p.iouType == 'segm':\n            _toMask(gts, self.cocoGt)\n            _toMask(dts, self.cocoDt)\n        # set ignore flag\n        for gt in gts:\n            gt['ignore'] = gt['ignore'] if 'ignore' in gt else 0\n            gt['ignore'] = 'iscrowd' in gt and gt['iscrowd']\n            if p.iouType == 'keypoints':\n                gt['ignore'] = (gt['num_keypoints'] == 0) or gt['ignore']\n        self._gts = defaultdict(list)  # gt for evaluation\n        self._dts = defaultdict(list)  # dt for evaluation\n        for gt in gts:\n            self._gts[gt['image_id'], gt['category_id']].append(gt)\n        for dt in dts:\n            self._dts[dt['image_id'], dt['category_id']].append(dt)\n        self.evalImgs = defaultdict(\n            list)  # per-image per-category evaluation results\n        self.eval = {}  # accumulated evaluation results\n\n    def evaluate(self):\n        \"\"\"Run per image evaluation on given images and store results (a list\n        of dict) in self.evalImgs.\n\n        :return: None\n        \"\"\"\n        tic = time.time()\n        print('Running per image evaluation...')\n        p = self.params\n        # add backward compatibility if useSegm is specified in params\n        if p.useSegm is not None:\n            p.iouType = 'segm' if p.useSegm == 1 else 'bbox'\n            print('useSegm (deprecated) is not None. Running {} evaluation'.\n                  format(p.iouType))\n        print('Evaluate annotation type *{}*'.format(p.iouType))\n        p.imgIds = list(np.unique(p.imgIds))\n        if p.useCats:\n            p.catIds = list(np.unique(p.catIds))\n        p.maxDets = sorted(p.maxDets)\n        self.params = p\n\n        # loop through images, area range, max detection number\n        catIds = p.catIds if p.useCats else [-1]\n\n        nproc = 8\n        split_size = len(catIds) // nproc\n        mp_params = []\n        for i in range(nproc):\n            begin = i * split_size\n            end = (i + 1) * split_size\n            if i == nproc - 1:\n                end = len(catIds)\n            mp_params.append((catIds[begin:end], ))\n\n        MMLogger.get_current_instance().info(\n            'start multi processing evaluation ...')\n        with mp.Pool(nproc) as pool:\n            self.evalImgs = pool.starmap(self._evaluateImg, mp_params)\n\n        self.evalImgs = list(itertools.chain(*self.evalImgs))\n\n        self._paramsEval = copy.deepcopy(self.params)\n        toc = time.time()\n        print('DONE (t={:0.2f}s).'.format(toc - tic))\n\n    def _evaluateImg(self, catids_chunk):\n        self._prepare()\n        p = self.params\n        maxDet = max(p.maxDets)\n        all_params = []\n        for catId in catids_chunk:\n            for areaRng in p.areaRng:\n                for imgId in p.imgIds:\n                    all_params.append((catId, areaRng, imgId))\n        evalImgs = [\n            self.evaluateImg(imgId, catId, areaRng, maxDet)\n            for catId, areaRng, imgId in tqdm(all_params)\n        ]\n        return evalImgs\n\n    def evaluateImg(self, imgId, catId, aRng, maxDet):\n        p = self.params\n        if p.useCats:\n            gt = self._gts[imgId, catId]\n            dt = self._dts[imgId, catId]\n        else:\n            gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]]\n            dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]]\n        if len(gt) == 0 and len(dt) == 0:\n            return None\n\n        for g in gt:\n            if g['ignore'] or (g['area'] < aRng[0] or g['area'] > aRng[1]):\n                g['_ignore'] = 1\n            else:\n                g['_ignore'] = 0\n\n        # sort dt highest score first, sort gt ignore last\n        gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort')\n        gt = [gt[i] for i in gtind]\n        dtind = np.argsort([-d['score'] for d in dt], kind='mergesort')\n        dt = [dt[i] for i in dtind[0:maxDet]]\n        iscrowd = [int(o['iscrowd']) for o in gt]\n        # load computed ious\n        # ious = self.ious[imgId, catId][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] # noqa\n        ious = self.computeIoU(imgId, catId)\n        ious = ious[:, gtind] if len(ious) > 0 else ious\n\n        T = len(p.iouThrs)\n        G = len(gt)\n        D = len(dt)\n        gtm = np.zeros((T, G))\n        dtm = np.zeros((T, D))\n        gtIg = np.array([g['_ignore'] for g in gt])\n        dtIg = np.zeros((T, D))\n        if not len(ious) == 0:\n            for tind, t in enumerate(p.iouThrs):\n                for dind, d in enumerate(dt):\n                    # information about best match so far (m=-1 -> unmatched)\n                    iou = min([t, 1 - 1e-10])\n                    m = -1\n                    for gind, g in enumerate(gt):\n                        # if this gt already matched, and not a crowd, continue\n                        if gtm[tind, gind] > 0 and not iscrowd[gind]:\n                            continue\n                        # if dt matched to reg gt, and on ignore gt, stop\n                        if m > -1 and gtIg[m] == 0 and gtIg[gind] == 1:\n                            break\n                        # continue to next gt unless better match made\n                        if ious[dind, gind] < iou:\n                            continue\n                        # if match successful and best so far,\n                        # store appropriately\n                        iou = ious[dind, gind]\n                        m = gind\n                    # if match made store id of match for both dt and gt\n                    if m == -1:\n                        continue\n                    dtIg[tind, dind] = gtIg[m]\n                    dtm[tind, dind] = gt[m]['id']\n                    gtm[tind, m] = d['id']\n        # set unmatched detections outside of area range to ignore\n        a = np.array([d['area'] < aRng[0] or d['area'] > aRng[1]\n                      for d in dt]).reshape((1, len(dt)))\n        dtIg = np.logical_or(dtIg, np.logical_and(dtm == 0, np.repeat(a, T,\n                                                                      0)))\n        # store results for given image and category\n\n        return {\n            'image_id': imgId,\n            'category_id': catId,\n            'aRng': aRng,\n            'maxDet': maxDet,\n            'dtIds': [d['id'] for d in dt],\n            'gtIds': [g['id'] for g in gt],\n            'dtMatches': dtm,\n            'gtMatches': gtm,\n            'dtScores': [d['score'] for d in dt],\n            'gtIgnore': gtIg,\n            'dtIgnore': dtIg,\n        }\n\n    def summarize(self):\n        \"\"\"Compute and display summary metrics for evaluation results.\n\n        Note this function can *only* be applied on the default parameter\n        setting\n        \"\"\"\n\n        def _summarize(ap=1, iouThr=None, areaRng='all', maxDets=100):\n            p = self.params\n            iStr = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}'  # noqa\n            titleStr = 'Average Precision' if ap == 1 else 'Average Recall'\n            typeStr = '(AP)' if ap == 1 else '(AR)'\n            iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \\\n                if iouThr is None else '{:0.2f}'.format(iouThr)\n\n            aind = [\n                i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng\n            ]\n            mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets]\n            if ap == 1:\n                # dimension of precision: [TxRxKxAxM]\n                s = self.eval['precision']\n                # IoU\n                if iouThr is not None:\n                    t = np.where(iouThr == p.iouThrs)[0]\n                    s = s[t]\n                s = s[:, :, :, aind, mind]\n            else:\n                # dimension of recall: [TxKxAxM]\n                s = self.eval['recall']\n                if iouThr is not None:\n                    t = np.where(iouThr == p.iouThrs)[0]\n                    s = s[t]\n                s = s[:, :, aind, mind]\n            if len(s[s > -1]) == 0:\n                mean_s = -1\n            else:\n                mean_s = np.mean(s[s > -1])\n            print(\n                iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets,\n                            mean_s))\n            return mean_s\n\n        def _summarizeDets():\n            stats = []\n            stats.append(_summarize(1, maxDets=self.params.maxDets[-1]))\n            stats.append(\n                _summarize(1, iouThr=.5, maxDets=self.params.maxDets[-1]))\n            stats.append(\n                _summarize(1, iouThr=.75, maxDets=self.params.maxDets[-1]))\n            for area_rng in ('small', 'medium', 'large'):\n                stats.append(\n                    _summarize(\n                        1, areaRng=area_rng, maxDets=self.params.maxDets[-1]))\n            for max_det in self.params.maxDets:\n                stats.append(_summarize(0, maxDets=max_det))\n            for area_rng in ('small', 'medium', 'large'):\n                stats.append(\n                    _summarize(\n                        0, areaRng=area_rng, maxDets=self.params.maxDets[-1]))\n            stats = np.array(stats)\n            return stats\n\n        def _summarizeKps():\n            stats = np.zeros((10, ))\n            stats[0] = _summarize(1, maxDets=20)\n            stats[1] = _summarize(1, maxDets=20, iouThr=.5)\n            stats[2] = _summarize(1, maxDets=20, iouThr=.75)\n            stats[3] = _summarize(1, maxDets=20, areaRng='medium')\n            stats[4] = _summarize(1, maxDets=20, areaRng='large')\n            stats[5] = _summarize(0, maxDets=20)\n            stats[6] = _summarize(0, maxDets=20, iouThr=.5)\n            stats[7] = _summarize(0, maxDets=20, iouThr=.75)\n            stats[8] = _summarize(0, maxDets=20, areaRng='medium')\n            stats[9] = _summarize(0, maxDets=20, areaRng='large')\n            return stats\n\n        if not self.eval:\n            raise Exception('Please run accumulate() first')\n        iouType = self.params.iouType\n        if iouType == 'segm' or iouType == 'bbox':\n            summarize = _summarizeDets\n        elif iouType == 'keypoints':\n            summarize = _summarizeKps\n        self.stats = summarize()\n"
  },
  {
    "path": "mmdet/datasets/base_det_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import List, Optional\n\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.fileio import load\nfrom mmengine.utils import is_abs\n\nfrom ..registry import DATASETS\n\n\n@DATASETS.register_module()\nclass BaseDetDataset(BaseDataset):\n    \"\"\"Base dataset for detection.\n\n    Args:\n        proposal_file (str, optional): Proposals file path. Defaults to None.\n        file_client_args (dict): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n        return_classes (bool): Whether to return class information\n            for open vocabulary-based algorithms. Defaults to False.\n        caption_prompt (dict, optional): Prompt for captioning.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 seg_map_suffix: str = '.png',\n                 proposal_file: Optional[str] = None,\n                 file_client_args: dict = None,\n                 backend_args: dict = None,\n                 return_classes: bool = False,\n                 caption_prompt: Optional[dict] = None,\n                 **kwargs) -> None:\n        self.seg_map_suffix = seg_map_suffix\n        self.proposal_file = proposal_file\n        self.backend_args = backend_args\n        self.return_classes = return_classes\n        self.caption_prompt = caption_prompt\n        if self.caption_prompt is not None:\n            assert self.return_classes, \\\n                'return_classes must be True when using caption_prompt'\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n        super().__init__(*args, **kwargs)\n\n    def full_init(self) -> None:\n        \"\"\"Load annotation file and set ``BaseDataset._fully_initialized`` to\n        True.\n\n        If ``lazy_init=False``, ``full_init`` will be called during the\n        instantiation and ``self._fully_initialized`` will be set to True. If\n        ``obj._fully_initialized=False``, the class method decorated by\n        ``force_full_init`` will call ``full_init`` automatically.\n\n        Several steps to initialize annotation:\n\n            - load_data_list: Load annotations from annotation file.\n            - load_proposals: Load proposals from proposal file, if\n              `self.proposal_file` is not None.\n            - filter data information: Filter annotations according to\n              filter_cfg.\n            - slice_data: Slice dataset according to ``self._indices``\n            - serialize_data: Serialize ``self.data_list`` if\n            ``self.serialize_data`` is True.\n        \"\"\"\n        if self._fully_initialized:\n            return\n        # load data information\n        self.data_list = self.load_data_list()\n        # get proposals from file\n        if self.proposal_file is not None:\n            self.load_proposals()\n        # filter illegal data, such as data that has no annotations.\n        self.data_list = self.filter_data()\n\n        # Get subset data according to indices.\n        if self._indices is not None:\n            self.data_list = self._get_unserialized_subset(self._indices)\n\n        # serialize data_list\n        if self.serialize_data:\n            self.data_bytes, self.data_address = self._serialize_data()\n\n        self._fully_initialized = True\n\n    def load_proposals(self) -> None:\n        \"\"\"Load proposals from proposals file.\n\n        The `proposals_list` should be a dict[img_path: proposals]\n        with the same length as `data_list`. And the `proposals` should be\n        a `dict` or :obj:`InstanceData` usually contains following keys.\n\n            - bboxes (np.ndarry): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n            - scores (np.ndarry): Classification scores, has a shape\n              (num_instance, ).\n        \"\"\"\n        # TODO: Add Unit Test after fully support Dump-Proposal Metric\n        if not is_abs(self.proposal_file):\n            self.proposal_file = osp.join(self.data_root, self.proposal_file)\n        proposals_list = load(\n            self.proposal_file, backend_args=self.backend_args)\n        assert len(self.data_list) == len(proposals_list)\n        for data_info in self.data_list:\n            img_path = data_info['img_path']\n            # `file_name` is the key to obtain the proposals from the\n            # `proposals_list`.\n            file_name = osp.join(\n                osp.split(osp.split(img_path)[0])[-1],\n                osp.split(img_path)[-1])\n            proposals = proposals_list[file_name]\n            data_info['proposals'] = proposals\n\n    def get_cat_ids(self, idx: int) -> List[int]:\n        \"\"\"Get COCO category ids by index.\n\n        Args:\n            idx (int): Index of data.\n\n        Returns:\n            List[int]: All categories in the image of specified index.\n        \"\"\"\n        instances = self.get_data_info(idx)['instances']\n        return [instance['bbox_label'] for instance in instances]\n"
  },
  {
    "path": "mmdet/datasets/base_semseg_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nfrom typing import Callable, Dict, List, Optional, Sequence, Union\n\nimport mmengine\nimport mmengine.fileio as fileio\nimport numpy as np\nfrom mmengine.dataset import BaseDataset, Compose\n\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass BaseSegDataset(BaseDataset):\n    \"\"\"Custom dataset for semantic segmentation. An example of file structure\n    is as followed.\n\n    .. code-block:: none\n\n        ├── data\n        │   ├── my_dataset\n        │   │   ├── img_dir\n        │   │   │   ├── train\n        │   │   │   │   ├── xxx{img_suffix}\n        │   │   │   │   ├── yyy{img_suffix}\n        │   │   │   │   ├── zzz{img_suffix}\n        │   │   │   ├── val\n        │   │   ├── ann_dir\n        │   │   │   ├── train\n        │   │   │   │   ├── xxx{seg_map_suffix}\n        │   │   │   │   ├── yyy{seg_map_suffix}\n        │   │   │   │   ├── zzz{seg_map_suffix}\n        │   │   │   ├── val\n\n    The img/gt_semantic_seg pair of BaseSegDataset should be of the same\n    except suffix. A valid img/gt_semantic_seg filename pair should be like\n    ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included\n    in the suffix). If split is given, then ``xxx`` is specified in txt file.\n    Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded.\n    Please refer to ``docs/en/tutorials/new_dataset.md`` for more details.\n\n\n    Args:\n        ann_file (str): Annotation file path. Defaults to ''.\n        metainfo (dict, optional): Meta information for dataset, such as\n            specify classes to load. Defaults to None.\n        data_root (str, optional): The root directory for ``data_prefix`` and\n            ``ann_file``. Defaults to None.\n        data_prefix (dict, optional): Prefix for training data. Defaults to\n            dict(img_path=None, seg_map_path=None).\n        img_suffix (str): Suffix of images. Default: '.jpg'\n        seg_map_suffix (str): Suffix of segmentation maps. Default: '.png'\n        filter_cfg (dict, optional): Config for filter data. Defaults to None.\n        indices (int or Sequence[int], optional): Support using first few\n            data in annotation file to facilitate training/testing on a smaller\n            dataset. Defaults to None which means using all ``data_infos``.\n        serialize_data (bool, optional): Whether to hold memory using\n            serialized objects, when enabled, data loader workers can use\n            shared RAM from master process instead of making a copy. Defaults\n            to True.\n        pipeline (list, optional): Processing pipeline. Defaults to [].\n        test_mode (bool, optional): ``test_mode=True`` means in test phase.\n            Defaults to False.\n        lazy_init (bool, optional): Whether to load annotation during\n            instantiation. In some cases, such as visualization, only the meta\n            information of the dataset is needed, which is not necessary to\n            load annotation file. ``Basedataset`` can skip load annotations to\n            save time by set ``lazy_init=True``. Defaults to False.\n        use_label_map (bool, optional): Whether to use label map.\n            Defaults to False.\n        max_refetch (int, optional): If ``Basedataset.prepare_data`` get a\n            None img. The maximum extra number of cycles to get a valid\n            image. Defaults to 1000.\n        backend_args (dict, Optional): Arguments to instantiate a file backend.\n            See https://mmengine.readthedocs.io/en/latest/api/fileio.htm\n            for details. Defaults to None.\n            Notes: mmcv>=2.0.0rc4 required.\n    \"\"\"\n    METAINFO: dict = dict()\n\n    def __init__(self,\n                 ann_file: str = '',\n                 img_suffix='.jpg',\n                 seg_map_suffix='.png',\n                 metainfo: Optional[dict] = None,\n                 data_root: Optional[str] = None,\n                 data_prefix: dict = dict(img_path='', seg_map_path=''),\n                 filter_cfg: Optional[dict] = None,\n                 indices: Optional[Union[int, Sequence[int]]] = None,\n                 serialize_data: bool = True,\n                 pipeline: List[Union[dict, Callable]] = [],\n                 test_mode: bool = False,\n                 lazy_init: bool = False,\n                 use_label_map: bool = False,\n                 max_refetch: int = 1000,\n                 backend_args: Optional[dict] = None) -> None:\n\n        self.img_suffix = img_suffix\n        self.seg_map_suffix = seg_map_suffix\n        self.backend_args = backend_args.copy() if backend_args else None\n\n        self.data_root = data_root\n        self.data_prefix = copy.copy(data_prefix)\n        self.ann_file = ann_file\n        self.filter_cfg = copy.deepcopy(filter_cfg)\n        self._indices = indices\n        self.serialize_data = serialize_data\n        self.test_mode = test_mode\n        self.max_refetch = max_refetch\n        self.data_list: List[dict] = []\n        self.data_bytes: np.ndarray\n\n        # Set meta information.\n        self._metainfo = self._load_metainfo(copy.deepcopy(metainfo))\n\n        # Get label map for custom classes\n        new_classes = self._metainfo.get('classes', None)\n        self.label_map = self.get_label_map(\n            new_classes) if use_label_map else None\n        self._metainfo.update(dict(label_map=self.label_map))\n\n        # Update palette based on label map or generate palette\n        # if it is not defined\n        updated_palette = self._update_palette()\n        self._metainfo.update(dict(palette=updated_palette))\n\n        # Join paths.\n        if self.data_root is not None:\n            self._join_prefix()\n\n        # Build pipeline.\n        self.pipeline = Compose(pipeline)\n        # Full initialize the dataset.\n        if not lazy_init:\n            self.full_init()\n\n        if test_mode:\n            assert self._metainfo.get('classes') is not None, \\\n                'dataset metainfo `classes` should be specified when testing'\n\n    @classmethod\n    def get_label_map(cls,\n                      new_classes: Optional[Sequence] = None\n                      ) -> Union[Dict, None]:\n        \"\"\"Require label mapping.\n\n        The ``label_map`` is a dictionary, its keys are the old label ids and\n        its values are the new label ids, and is used for changing pixel\n        labels in load_annotations. If and only if old classes in cls.METAINFO\n        is not equal to new classes in self._metainfo and nether of them is not\n        None, `label_map` is not None.\n\n        Args:\n            new_classes (list, tuple, optional): The new classes name from\n                metainfo. Default to None.\n\n\n        Returns:\n            dict, optional: The mapping from old classes in cls.METAINFO to\n                new classes in self._metainfo\n        \"\"\"\n        old_classes = cls.METAINFO.get('classes', None)\n        if (new_classes is not None and old_classes is not None\n                and list(new_classes) != list(old_classes)):\n\n            label_map = {}\n            if not set(new_classes).issubset(cls.METAINFO['classes']):\n                raise ValueError(\n                    f'new classes {new_classes} is not a '\n                    f'subset of classes {old_classes} in METAINFO.')\n            for i, c in enumerate(old_classes):\n                if c not in new_classes:\n                    # 0 is background\n                    label_map[i] = 0\n                else:\n                    label_map[i] = new_classes.index(c)\n            return label_map\n        else:\n            return None\n\n    def _update_palette(self) -> list:\n        \"\"\"Update palette after loading metainfo.\n\n        If length of palette is equal to classes, just return the palette.\n        If palette is not defined, it will randomly generate a palette.\n        If classes is updated by customer, it will return the subset of\n        palette.\n\n        Returns:\n            Sequence: Palette for current dataset.\n        \"\"\"\n        palette = self._metainfo.get('palette', [])\n        classes = self._metainfo.get('classes', [])\n        # palette does match classes\n        if len(palette) == len(classes):\n            return palette\n\n        if len(palette) == 0:\n            # Get random state before set seed, and restore\n            # random state later.\n            # It will prevent loss of randomness, as the palette\n            # may be different in each iteration if not specified.\n            # See: https://github.com/open-mmlab/mmdetection/issues/5844\n            state = np.random.get_state()\n            np.random.seed(42)\n            # random palette\n            new_palette = np.random.randint(\n                0, 255, size=(len(classes), 3)).tolist()\n            np.random.set_state(state)\n        elif len(palette) >= len(classes) and self.label_map is not None:\n            new_palette = []\n            # return subset of palette\n            for old_id, new_id in sorted(\n                    self.label_map.items(), key=lambda x: x[1]):\n                # 0 is background\n                if new_id != 0:\n                    new_palette.append(palette[old_id])\n            new_palette = type(palette)(new_palette)\n        elif len(palette) >= len(classes):\n            # Allow palette length is greater than classes.\n            return palette\n        else:\n            raise ValueError('palette does not match classes '\n                             f'as metainfo is {self._metainfo}.')\n        return new_palette\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotation from directory or annotation file.\n\n        Returns:\n            list[dict]: All data info of dataset.\n        \"\"\"\n        data_list = []\n        img_dir = self.data_prefix.get('img_path', None)\n        ann_dir = self.data_prefix.get('seg_map_path', None)\n        if not osp.isdir(self.ann_file) and self.ann_file:\n            assert osp.isfile(self.ann_file), \\\n                f'Failed to load `ann_file` {self.ann_file}'\n            lines = mmengine.list_from_file(\n                self.ann_file, backend_args=self.backend_args)\n            for line in lines:\n                img_name = line.strip()\n                data_info = dict(\n                    img_path=osp.join(img_dir, img_name + self.img_suffix))\n                if ann_dir is not None:\n                    seg_map = img_name + self.seg_map_suffix\n                    data_info['seg_map_path'] = osp.join(ann_dir, seg_map)\n                data_info['label_map'] = self.label_map\n                data_list.append(data_info)\n        else:\n            for img in fileio.list_dir_or_file(\n                    dir_path=img_dir,\n                    list_dir=False,\n                    suffix=self.img_suffix,\n                    recursive=True,\n                    backend_args=self.backend_args):\n                data_info = dict(img_path=osp.join(img_dir, img))\n                if ann_dir is not None:\n                    seg_map = img.replace(self.img_suffix, self.seg_map_suffix)\n                    data_info['seg_map_path'] = osp.join(ann_dir, seg_map)\n                data_info['label_map'] = self.label_map\n                data_list.append(data_info)\n            data_list = sorted(data_list, key=lambda x: x['img_path'])\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/base_video_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nfrom collections import defaultdict\nfrom typing import Any, List, Tuple\n\nimport mmengine.fileio as fileio\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.logging import print_log\n\nfrom mmdet.datasets.api_wrappers import COCO\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass BaseVideoDataset(BaseDataset):\n    \"\"\"Base video dataset for VID, MOT and VIS tasks.\"\"\"\n\n    META = dict(classes=None)\n    # ann_id is unique in coco dataset.\n    ANN_ID_UNIQUE = True\n\n    def __init__(self, *args, backend_args: dict = None, **kwargs):\n        self.backend_args = backend_args\n        super().__init__(*args, **kwargs)\n\n    def load_data_list(self) -> Tuple[List[dict], List]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``.\n\n        Returns:\n            tuple(list[dict], list): A list of annotation and a list of\n            valid data indices.\n        \"\"\"\n        with fileio.get_local_path(self.ann_file) as local_path:\n            self.coco = COCO(local_path)\n        # The order of returned `cat_ids` will not\n        # change with the order of the classes\n        self.cat_ids = self.coco.get_cat_ids(\n            cat_names=self.metainfo['classes'])\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.coco.cat_img_map)\n        # used in `filter_data`\n        self.img_ids_with_ann = set()\n\n        img_ids = self.coco.get_img_ids()\n        total_ann_ids = []\n        # if ``video_id`` is not in the annotation file, we will assign a big\n        # unique video_id for this video.\n        single_video_id = 100000\n        videos = {}\n        for img_id in img_ids:\n            raw_img_info = self.coco.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n            if 'video_id' not in raw_img_info:\n                single_video_id = single_video_id + 1\n                video_id = single_video_id\n            else:\n                video_id = raw_img_info['video_id']\n\n            if video_id not in videos:\n                videos[video_id] = {\n                    'video_id': video_id,\n                    'images': [],\n                    'video_length': 0\n                }\n\n            videos[video_id]['video_length'] += 1\n            ann_ids = self.coco.get_ann_ids(\n                img_ids=[img_id], cat_ids=self.cat_ids)\n            raw_ann_info = self.coco.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n\n            parsed_data_info = self.parse_data_info(\n                dict(raw_img_info=raw_img_info, raw_ann_info=raw_ann_info))\n\n            if len(parsed_data_info['instances']) > 0:\n                self.img_ids_with_ann.add(parsed_data_info['img_id'])\n\n            videos[video_id]['images'].append(parsed_data_info)\n\n        data_list = [v for v in videos.values()]\n\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.coco\n\n        return data_list\n\n    def parse_data_info(self, raw_data_info: dict) -> dict:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            raw_data_info (dict): Raw data information loaded from\n                ``ann_file``.\n\n        Returns:\n            dict: Parsed annotation.\n        \"\"\"\n        img_info = raw_data_info['raw_img_info']\n        ann_info = raw_data_info['raw_ann_info']\n        data_info = {}\n\n        data_info.update(img_info)\n        if self.data_prefix.get('img_path', None) is not None:\n            img_path = osp.join(self.data_prefix['img_path'],\n                                img_info['file_name'])\n        else:\n            img_path = img_info['file_name']\n        data_info['img_path'] = img_path\n\n        instances = []\n        for i, ann in enumerate(ann_info):\n            instance = {}\n\n            if ann.get('ignore', False):\n                continue\n            x1, y1, w, h = ann['bbox']\n            inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                continue\n            if ann['category_id'] not in self.cat_ids:\n                continue\n            bbox = [x1, y1, x1 + w, y1 + h]\n\n            if ann.get('iscrowd', False):\n                instance['ignore_flag'] = 1\n            else:\n                instance['ignore_flag'] = 0\n            instance['bbox'] = bbox\n            instance['bbox_label'] = self.cat2label[ann['category_id']]\n            if ann.get('segmentation', None):\n                instance['mask'] = ann['segmentation']\n            if ann.get('instance_id', None):\n                instance['instance_id'] = ann['instance_id']\n            else:\n                # image dataset usually has no `instance_id`.\n                # Therefore, we set it to `i`.\n                instance['instance_id'] = i\n            instances.append(instance)\n        data_info['instances'] = instances\n        return data_info\n\n    def filter_data(self) -> List[int]:\n        \"\"\"Filter image annotations according to filter_cfg.\n\n        Returns:\n            list[int]: Filtered results.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        num_imgs_before_filter = sum(\n            [len(info['images']) for info in self.data_list])\n        num_imgs_after_filter = 0\n\n        # obtain images that contain annotations of the required categories\n        ids_in_cat = set()\n        for i, class_id in enumerate(self.cat_ids):\n            ids_in_cat |= set(self.cat_img_map[class_id])\n        # merge the image id sets of the two conditions and use the merged set\n        # to filter out images if self.filter_empty_gt=True\n        ids_in_cat &= self.img_ids_with_ann\n\n        new_data_list = []\n        for video_data_info in self.data_list:\n            imgs_data_info = video_data_info['images']\n            valid_imgs_data_info = []\n\n            for data_info in imgs_data_info:\n                img_id = data_info['img_id']\n                width = data_info['width']\n                height = data_info['height']\n                # TODO: simplify these conditions\n                if self.filter_cfg is None:\n                    if img_id not in ids_in_cat:\n                        video_data_info['video_length'] -= 1\n                        continue\n                    if min(width, height) >= 32:\n                        valid_imgs_data_info.append(data_info)\n                        num_imgs_after_filter += 1\n                    else:\n                        video_data_info['video_length'] -= 1\n                else:\n                    if self.filter_cfg.get('filter_empty_gt',\n                                           True) and img_id not in ids_in_cat:\n                        video_data_info['video_length'] -= 1\n                        continue\n                    if min(width, height) >= self.filter_cfg.get(\n                            'min_size', 32):\n                        valid_imgs_data_info.append(data_info)\n                        num_imgs_after_filter += 1\n                    else:\n                        video_data_info['video_length'] -= 1\n                video_data_info['images'] = valid_imgs_data_info\n            new_data_list.append(video_data_info)\n\n        print_log(\n            'The number of samples before and after filtering: '\n            f'{num_imgs_before_filter} / {num_imgs_after_filter}', 'current')\n        return new_data_list\n\n    def prepare_data(self, idx) -> Any:\n        \"\"\"Get date processed by ``self.pipeline``. Note that ``idx`` is a\n        video index in default since the base element of video dataset is a\n        video. However, in some cases, we need to specific both the video index\n        and frame index. For example, in traing mode, we may want to sample the\n        specific frames and all the frames must be sampled once in a epoch; in\n        test mode, we may want to output data of a single image rather than the\n        whole video for saving memory.\n\n        Args:\n            idx (int): The index of ``data_info``.\n\n        Returns:\n            Any: Depends on ``self.pipeline``.\n        \"\"\"\n        if isinstance(idx, tuple):\n            assert len(idx) == 2, 'The length of idx must be 2: '\n            '(video_index, frame_index)'\n            video_idx, frame_idx = idx[0], idx[1]\n        else:\n            video_idx, frame_idx = idx, None\n\n        data_info = self.get_data_info(video_idx)\n        if self.test_mode:\n            # Support two test_mode: frame-level and video-level\n            final_data_info = defaultdict(list)\n            if frame_idx is None:\n                frames_idx_list = list(range(data_info['video_length']))\n            else:\n                frames_idx_list = [frame_idx]\n            for index in frames_idx_list:\n                frame_ann = data_info['images'][index]\n                frame_ann['video_id'] = data_info['video_id']\n                # Collate data_list (list of dict to dict of list)\n                for key, value in frame_ann.items():\n                    final_data_info[key].append(value)\n                # copy the info in video-level into img-level\n                # TODO: the value of this key is the same as that of\n                # `video_length` in test mode\n                final_data_info['ori_video_length'].append(\n                    data_info['video_length'])\n\n            final_data_info['video_length'] = [len(frames_idx_list)\n                                               ] * len(frames_idx_list)\n            return self.pipeline(final_data_info)\n        else:\n            # Specify `key_frame_id` for the frame sampling in the pipeline\n            if frame_idx is not None:\n                data_info['key_frame_id'] = frame_idx\n            return self.pipeline(data_info)\n\n    def get_cat_ids(self, index) -> List[int]:\n        \"\"\"Following image detection, we provide this interface function. Get\n        category ids by video index and frame index.\n\n        Args:\n            index: The index of the dataset. It support two kinds of inputs:\n                Tuple:\n                    video_idx (int): Index of video.\n                    frame_idx (int): Index of frame.\n                Int: Index of video.\n\n        Returns:\n            List[int]: All categories in the image of specified video index\n            and frame index.\n        \"\"\"\n        if isinstance(index, tuple):\n            assert len(\n                index\n            ) == 2, f'Expect the length of index is 2, but got {len(index)}'\n            video_idx, frame_idx = index\n            instances = self.get_data_info(\n                video_idx)['images'][frame_idx]['instances']\n            return [instance['bbox_label'] for instance in instances]\n        else:\n            cat_ids = []\n            for img in self.get_data_info(index)['images']:\n                for instance in img['instances']:\n                    cat_ids.append(instance['bbox_label'])\n            return cat_ids\n\n    @property\n    def num_all_imgs(self):\n        \"\"\"Get the number of all the images in this video dataset.\"\"\"\n        return sum(\n            [len(self.get_data_info(i)['images']) for i in range(len(self))])\n\n    def get_len_per_video(self, idx):\n        \"\"\"Get length of one video.\n\n        Args:\n            idx (int): Index of video.\n\n        Returns:\n            int (int): The length of the video.\n        \"\"\"\n        return len(self.get_data_info(idx)['images'])\n"
  },
  {
    "path": "mmdet/datasets/cityscapes.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/data/datasets/cityscapes.py # noqa\n# and https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/evaluation/evalInstanceLevelSemanticLabeling.py # noqa\n\nfrom typing import List\n\nfrom mmdet.registry import DATASETS\nfrom .coco import CocoDataset\n\n\n@DATASETS.register_module()\nclass CityscapesDataset(CocoDataset):\n    \"\"\"Dataset for Cityscapes.\"\"\"\n\n    METAINFO = {\n        'classes': ('person', 'rider', 'car', 'truck', 'bus', 'train',\n                    'motorcycle', 'bicycle'),\n        'palette': [(220, 20, 60), (255, 0, 0), (0, 0, 142), (0, 0, 70),\n                    (0, 60, 100), (0, 80, 100), (0, 0, 230), (119, 11, 32)]\n    }\n\n    def filter_data(self) -> List[dict]:\n        \"\"\"Filter annotations according to filter_cfg.\n\n        Returns:\n            List[dict]: Filtered results.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        if self.filter_cfg is None:\n            return self.data_list\n\n        filter_empty_gt = self.filter_cfg.get('filter_empty_gt', False)\n        min_size = self.filter_cfg.get('min_size', 0)\n\n        # obtain images that contain annotation\n        ids_with_ann = set(data_info['img_id'] for data_info in self.data_list)\n        # obtain images that contain annotations of the required categories\n        ids_in_cat = set()\n        for i, class_id in enumerate(self.cat_ids):\n            ids_in_cat |= set(self.cat_img_map[class_id])\n        # merge the image id sets of the two conditions and use the merged set\n        # to filter out images if self.filter_empty_gt=True\n        ids_in_cat &= ids_with_ann\n\n        valid_data_infos = []\n        for i, data_info in enumerate(self.data_list):\n            img_id = data_info['img_id']\n            width = data_info['width']\n            height = data_info['height']\n            all_is_crowd = all([\n                instance['ignore_flag'] == 1\n                for instance in data_info['instances']\n            ])\n            if filter_empty_gt and (img_id not in ids_in_cat or all_is_crowd):\n                continue\n            if min(width, height) >= min_size:\n                valid_data_infos.append(data_info)\n\n        return valid_data_infos\n"
  },
  {
    "path": "mmdet/datasets/coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nfrom typing import List, Union\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.registry import DATASETS\nfrom .api_wrappers import COCO\nfrom .base_det_dataset import BaseDetDataset\n\n\n@DATASETS.register_module()\nclass CocoDataset(BaseDetDataset):\n    \"\"\"Dataset for COCO.\"\"\"\n\n    METAINFO = {\n        'classes':\n        ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n         'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',\n         'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',\n         'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',\n         'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',\n         'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard',\n         'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork',\n         'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',\n         'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair',\n         'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv',\n         'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',\n         'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',\n         'scissors', 'teddy bear', 'hair drier', 'toothbrush'),\n        # palette is a list of color tuples, which is used for visualization.\n        'palette':\n        [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),\n         (0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),\n         (100, 170, 30), (220, 220, 0), (175, 116, 175), (250, 0, 30),\n         (165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),\n         (0, 82, 0), (120, 166, 157), (110, 76, 0), (174, 57, 255),\n         (199, 100, 0), (72, 0, 118), (255, 179, 240), (0, 125, 92),\n         (209, 0, 151), (188, 208, 182), (0, 220, 176), (255, 99, 164),\n         (92, 0, 73), (133, 129, 255), (78, 180, 255), (0, 228, 0),\n         (174, 255, 243), (45, 89, 255), (134, 134, 103), (145, 148, 174),\n         (255, 208, 186), (197, 226, 255), (171, 134, 1), (109, 63, 54),\n         (207, 138, 255), (151, 0, 95), (9, 80, 61), (84, 105, 51),\n         (74, 65, 105), (166, 196, 102), (208, 195, 210), (255, 109, 65),\n         (0, 143, 149), (179, 0, 194), (209, 99, 106), (5, 121, 0),\n         (227, 255, 205), (147, 186, 208), (153, 69, 1), (3, 95, 161),\n         (163, 255, 0), (119, 0, 170), (0, 182, 199), (0, 165, 120),\n         (183, 130, 88), (95, 32, 0), (130, 114, 135), (110, 129, 133),\n         (166, 74, 118), (219, 142, 185), (79, 210, 114), (178, 90, 62),\n         (65, 70, 15), (127, 167, 115), (59, 105, 106), (142, 108, 45),\n         (196, 172, 0), (95, 54, 80), (128, 76, 255), (201, 57, 1),\n         (246, 0, 122), (191, 162, 208)]\n    }\n    COCOAPI = COCO\n    # ann_id is unique in coco dataset.\n    ANN_ID_UNIQUE = True\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.coco = self.COCOAPI(local_path)\n        # The order of returned `cat_ids` will not\n        # change with the order of the `classes`\n        self.cat_ids = self.coco.get_cat_ids(\n            cat_names=self.metainfo['classes'])\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.coco.cat_img_map)\n\n        img_ids = self.coco.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.coco.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n\n            ann_ids = self.coco.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = self.coco.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n\n            parsed_data_info = self.parse_data_info({\n                'raw_ann_info':\n                raw_ann_info,\n                'raw_img_info':\n                raw_img_info\n            })\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.coco\n\n        return data_list\n\n    def parse_data_info(self, raw_data_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            raw_data_info (dict): Raw data information load from ``ann_file``\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        img_info = raw_data_info['raw_img_info']\n        ann_info = raw_data_info['raw_ann_info']\n\n        data_info = {}\n\n        # TODO: need to change data_prefix['img'] to data_prefix['img_path']\n        img_path = osp.join(self.data_prefix['img'], img_info['file_name'])\n        if self.data_prefix.get('seg', None):\n            seg_map_path = osp.join(\n                self.data_prefix['seg'],\n                img_info['file_name'].rsplit('.', 1)[0] + self.seg_map_suffix)\n        else:\n            seg_map_path = None\n        data_info['img_path'] = img_path\n        data_info['img_id'] = img_info['img_id']\n        data_info['seg_map_path'] = seg_map_path\n        data_info['height'] = img_info['height']\n        data_info['width'] = img_info['width']\n\n        if self.return_classes:\n            data_info['text'] = self.metainfo['classes']\n            data_info['caption_prompt'] = self.caption_prompt\n            data_info['custom_entities'] = True\n\n        instances = []\n        for i, ann in enumerate(ann_info):\n            instance = {}\n\n            if ann.get('ignore', False):\n                continue\n            x1, y1, w, h = ann['bbox']\n            inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                continue\n            if ann['category_id'] not in self.cat_ids:\n                continue\n            bbox = [x1, y1, x1 + w, y1 + h]\n\n            if ann.get('iscrowd', False):\n                instance['ignore_flag'] = 1\n            else:\n                instance['ignore_flag'] = 0\n            instance['bbox'] = bbox\n            instance['bbox_label'] = self.cat2label[ann['category_id']]\n\n            if ann.get('segmentation', None):\n                instance['mask'] = ann['segmentation']\n\n            instances.append(instance)\n        data_info['instances'] = instances\n        return data_info\n\n    def filter_data(self) -> List[dict]:\n        \"\"\"Filter annotations according to filter_cfg.\n\n        Returns:\n            List[dict]: Filtered results.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        if self.filter_cfg is None:\n            return self.data_list\n\n        filter_empty_gt = self.filter_cfg.get('filter_empty_gt', False)\n        min_size = self.filter_cfg.get('min_size', 0)\n\n        # obtain images that contain annotation\n        ids_with_ann = set(data_info['img_id'] for data_info in self.data_list)\n        # obtain images that contain annotations of the required categories\n        ids_in_cat = set()\n        for i, class_id in enumerate(self.cat_ids):\n            ids_in_cat |= set(self.cat_img_map[class_id])\n        # merge the image id sets of the two conditions and use the merged set\n        # to filter out images if self.filter_empty_gt=True\n        ids_in_cat &= ids_with_ann\n\n        valid_data_infos = []\n        for i, data_info in enumerate(self.data_list):\n            img_id = data_info['img_id']\n            width = data_info['width']\n            height = data_info['height']\n            if filter_empty_gt and img_id not in ids_in_cat:\n                continue\n            if min(width, height) >= min_size:\n                valid_data_infos.append(data_info)\n\n        return valid_data_infos\n"
  },
  {
    "path": "mmdet/datasets/coco_caption.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom pathlib import Path\nfrom typing import List\n\nimport mmengine\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.fileio import get_file_backend\n\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass CocoCaptionDataset(BaseDataset):\n    \"\"\"COCO2014 Caption dataset.\"\"\"\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load data list.\"\"\"\n        img_prefix = self.data_prefix['img_path']\n        annotations = mmengine.load(self.ann_file)\n        file_backend = get_file_backend(img_prefix)\n\n        data_list = []\n        for ann in annotations:\n            data_info = {\n                'img_id': Path(ann['image']).stem.split('_')[-1],\n                'img_path': file_backend.join_path(img_prefix, ann['image']),\n                'gt_caption': ann['caption'],\n            }\n\n            data_list.append(data_info)\n\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/coco_panoptic.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import Callable, List, Optional, Sequence, Union\n\nfrom mmdet.registry import DATASETS\nfrom .api_wrappers import COCOPanoptic\nfrom .coco import CocoDataset\n\n\n@DATASETS.register_module()\nclass CocoPanopticDataset(CocoDataset):\n    \"\"\"Coco dataset for Panoptic segmentation.\n\n    The annotation format is shown as follows. The `ann` field is optional\n    for testing.\n\n    .. code-block:: none\n\n        [\n            {\n                'filename': f'{image_id:012}.png',\n                'image_id':9\n                'segments_info':\n                [\n                    {\n                        'id': 8345037, (segment_id in panoptic png,\n                                        convert from rgb)\n                        'category_id': 51,\n                        'iscrowd': 0,\n                        'bbox': (x1, y1, w, h),\n                        'area': 24315\n                    },\n                    ...\n                ]\n            },\n            ...\n        ]\n\n    Args:\n        ann_file (str): Annotation file path. Defaults to ''.\n        metainfo (dict, optional): Meta information for dataset, such as class\n            information. Defaults to None.\n        data_root (str, optional): The root directory for ``data_prefix`` and\n            ``ann_file``. Defaults to None.\n        data_prefix (dict, optional): Prefix for training data. Defaults to\n            ``dict(img=None, ann=None, seg=None)``. The prefix ``seg`` which is\n            for panoptic segmentation map must be not None.\n        filter_cfg (dict, optional): Config for filter data. Defaults to None.\n        indices (int or Sequence[int], optional): Support using first few\n            data in annotation file to facilitate training/testing on a smaller\n            dataset. Defaults to None which means using all ``data_infos``.\n        serialize_data (bool, optional): Whether to hold memory using\n            serialized objects, when enabled, data loader workers can use\n            shared RAM from master process instead of making a copy. Defaults\n            to True.\n        pipeline (list, optional): Processing pipeline. Defaults to [].\n        test_mode (bool, optional): ``test_mode=True`` means in test phase.\n            Defaults to False.\n        lazy_init (bool, optional): Whether to load annotation during\n            instantiation. In some cases, such as visualization, only the meta\n            information of the dataset is needed, which is not necessary to\n            load annotation file. ``Basedataset`` can skip load annotations to\n            save time by set ``lazy_init=False``. Defaults to False.\n        max_refetch (int, optional): If ``Basedataset.prepare_data`` get a\n            None img. The maximum extra number of cycles to get a valid\n            image. Defaults to 1000.\n    \"\"\"\n\n    METAINFO = {\n        'classes':\n        ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n         'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',\n         'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',\n         'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',\n         'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',\n         'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard',\n         'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork',\n         'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',\n         'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair',\n         'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv',\n         'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',\n         'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',\n         'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner',\n         'blanket', 'bridge', 'cardboard', 'counter', 'curtain', 'door-stuff',\n         'floor-wood', 'flower', 'fruit', 'gravel', 'house', 'light',\n         'mirror-stuff', 'net', 'pillow', 'platform', 'playingfield',\n         'railroad', 'river', 'road', 'roof', 'sand', 'sea', 'shelf', 'snow',\n         'stairs', 'tent', 'towel', 'wall-brick', 'wall-stone', 'wall-tile',\n         'wall-wood', 'water-other', 'window-blind', 'window-other',\n         'tree-merged', 'fence-merged', 'ceiling-merged', 'sky-other-merged',\n         'cabinet-merged', 'table-merged', 'floor-other-merged',\n         'pavement-merged', 'mountain-merged', 'grass-merged', 'dirt-merged',\n         'paper-merged', 'food-other-merged', 'building-other-merged',\n         'rock-merged', 'wall-other-merged', 'rug-merged'),\n        'thing_classes':\n        ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n         'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',\n         'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',\n         'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',\n         'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',\n         'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard',\n         'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork',\n         'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',\n         'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair',\n         'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv',\n         'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',\n         'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',\n         'scissors', 'teddy bear', 'hair drier', 'toothbrush'),\n        'stuff_classes':\n        ('banner', 'blanket', 'bridge', 'cardboard', 'counter', 'curtain',\n         'door-stuff', 'floor-wood', 'flower', 'fruit', 'gravel', 'house',\n         'light', 'mirror-stuff', 'net', 'pillow', 'platform', 'playingfield',\n         'railroad', 'river', 'road', 'roof', 'sand', 'sea', 'shelf', 'snow',\n         'stairs', 'tent', 'towel', 'wall-brick', 'wall-stone', 'wall-tile',\n         'wall-wood', 'water-other', 'window-blind', 'window-other',\n         'tree-merged', 'fence-merged', 'ceiling-merged', 'sky-other-merged',\n         'cabinet-merged', 'table-merged', 'floor-other-merged',\n         'pavement-merged', 'mountain-merged', 'grass-merged', 'dirt-merged',\n         'paper-merged', 'food-other-merged', 'building-other-merged',\n         'rock-merged', 'wall-other-merged', 'rug-merged'),\n        'palette':\n        [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),\n         (0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),\n         (100, 170, 30), (220, 220, 0), (175, 116, 175), (250, 0, 30),\n         (165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),\n         (0, 82, 0), (120, 166, 157), (110, 76, 0), (174, 57, 255),\n         (199, 100, 0), (72, 0, 118), (255, 179, 240), (0, 125, 92),\n         (209, 0, 151), (188, 208, 182), (0, 220, 176), (255, 99, 164),\n         (92, 0, 73), (133, 129, 255), (78, 180, 255), (0, 228, 0),\n         (174, 255, 243), (45, 89, 255), (134, 134, 103), (145, 148, 174),\n         (255, 208, 186), (197, 226, 255), (171, 134, 1), (109, 63, 54),\n         (207, 138, 255), (151, 0, 95), (9, 80, 61), (84, 105, 51),\n         (74, 65, 105), (166, 196, 102), (208, 195, 210), (255, 109, 65),\n         (0, 143, 149), (179, 0, 194), (209, 99, 106), (5, 121, 0),\n         (227, 255, 205), (147, 186, 208), (153, 69, 1), (3, 95, 161),\n         (163, 255, 0), (119, 0, 170), (0, 182, 199), (0, 165, 120),\n         (183, 130, 88), (95, 32, 0), (130, 114, 135), (110, 129, 133),\n         (166, 74, 118), (219, 142, 185), (79, 210, 114), (178, 90, 62),\n         (65, 70, 15), (127, 167, 115), (59, 105, 106), (142, 108, 45),\n         (196, 172, 0), (95, 54, 80), (128, 76, 255), (201, 57, 1),\n         (246, 0, 122), (191, 162, 208), (255, 255, 128), (147, 211, 203),\n         (150, 100, 100), (168, 171, 172), (146, 112, 198), (210, 170, 100),\n         (92, 136, 89), (218, 88, 184), (241, 129, 0), (217, 17, 255),\n         (124, 74, 181), (70, 70, 70), (255, 228, 255), (154, 208, 0),\n         (193, 0, 92), (76, 91, 113), (255, 180, 195), (106, 154, 176),\n         (230, 150, 140), (60, 143, 255), (128, 64, 128), (92, 82, 55),\n         (254, 212, 124), (73, 77, 174), (255, 160, 98), (255, 255, 255),\n         (104, 84, 109), (169, 164, 131), (225, 199, 255), (137, 54, 74),\n         (135, 158, 223), (7, 246, 231), (107, 255, 200), (58, 41, 149),\n         (183, 121, 142), (255, 73, 97), (107, 142, 35), (190, 153, 153),\n         (146, 139, 141), (70, 130, 180), (134, 199, 156), (209, 226, 140),\n         (96, 36, 108), (96, 96, 96), (64, 170, 64), (152, 251, 152),\n         (208, 229, 228), (206, 186, 171), (152, 161, 64), (116, 112, 0),\n         (0, 114, 143), (102, 102, 156), (250, 141, 255)]\n    }\n    COCOAPI = COCOPanoptic\n    # ann_id is not unique in coco panoptic dataset.\n    ANN_ID_UNIQUE = False\n\n    def __init__(self,\n                 ann_file: str = '',\n                 metainfo: Optional[dict] = None,\n                 data_root: Optional[str] = None,\n                 data_prefix: dict = dict(img=None, ann=None, seg=None),\n                 filter_cfg: Optional[dict] = None,\n                 indices: Optional[Union[int, Sequence[int]]] = None,\n                 serialize_data: bool = True,\n                 pipeline: List[Union[dict, Callable]] = [],\n                 test_mode: bool = False,\n                 lazy_init: bool = False,\n                 max_refetch: int = 1000,\n                 backend_args: dict = None,\n                 **kwargs) -> None:\n        super().__init__(\n            ann_file=ann_file,\n            metainfo=metainfo,\n            data_root=data_root,\n            data_prefix=data_prefix,\n            filter_cfg=filter_cfg,\n            indices=indices,\n            serialize_data=serialize_data,\n            pipeline=pipeline,\n            test_mode=test_mode,\n            lazy_init=lazy_init,\n            max_refetch=max_refetch,\n            backend_args=backend_args,\n            **kwargs)\n\n    def parse_data_info(self, raw_data_info: dict) -> dict:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            raw_data_info (dict): Raw data information load from ``ann_file``.\n\n        Returns:\n            dict: Parsed annotation.\n        \"\"\"\n        img_info = raw_data_info['raw_img_info']\n        ann_info = raw_data_info['raw_ann_info']\n        # filter out unmatched annotations which have\n        # same segment_id but belong to other image\n        ann_info = [\n            ann for ann in ann_info if ann['image_id'] == img_info['img_id']\n        ]\n        data_info = {}\n\n        img_path = osp.join(self.data_prefix['img'], img_info['file_name'])\n        if self.data_prefix.get('seg', None):\n            seg_map_path = osp.join(\n                self.data_prefix['seg'],\n                img_info['file_name'].replace('.jpg', '.png'))\n        else:\n            seg_map_path = None\n        data_info['img_path'] = img_path\n        data_info['img_id'] = img_info['img_id']\n        data_info['seg_map_path'] = seg_map_path\n        data_info['height'] = img_info['height']\n        data_info['width'] = img_info['width']\n\n        if self.return_classes:\n            data_info['text'] = self.metainfo['thing_classes']\n            data_info['stuff_text'] = self.metainfo['stuff_classes']\n            data_info['custom_entities'] = True  # no important\n\n        instances = []\n        segments_info = []\n        for ann in ann_info:\n            instance = {}\n            x1, y1, w, h = ann['bbox']\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                continue\n            bbox = [x1, y1, x1 + w, y1 + h]\n            category_id = ann['category_id']\n            contiguous_cat_id = self.cat2label[category_id]\n\n            is_thing = self.coco.load_cats(ids=category_id)[0]['isthing']\n            if is_thing:\n                is_crowd = ann.get('iscrowd', False)\n                instance['bbox'] = bbox\n                instance['bbox_label'] = contiguous_cat_id\n                if not is_crowd:\n                    instance['ignore_flag'] = 0\n                else:\n                    instance['ignore_flag'] = 1\n                    is_thing = False\n\n            segment_info = {\n                'id': ann['id'],\n                'category': contiguous_cat_id,\n                'is_thing': is_thing\n            }\n            segments_info.append(segment_info)\n            if len(instance) > 0 and is_thing:\n                instances.append(instance)\n        data_info['instances'] = instances\n        data_info['segments_info'] = segments_info\n        return data_info\n\n    def filter_data(self) -> List[dict]:\n        \"\"\"Filter images too small or without ground truth.\n\n        Returns:\n            List[dict]: ``self.data_list`` after filtering.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        if self.filter_cfg is None:\n            return self.data_list\n\n        filter_empty_gt = self.filter_cfg.get('filter_empty_gt', False)\n        min_size = self.filter_cfg.get('min_size', 0)\n\n        ids_with_ann = set()\n        # check whether images have legal thing annotations.\n        for data_info in self.data_list:\n            for segment_info in data_info['segments_info']:\n                if not segment_info['is_thing']:\n                    continue\n                ids_with_ann.add(data_info['img_id'])\n\n        valid_data_list = []\n        for data_info in self.data_list:\n            img_id = data_info['img_id']\n            width = data_info['width']\n            height = data_info['height']\n            if filter_empty_gt and img_id not in ids_with_ann:\n                continue\n            if min(width, height) >= min_size:\n                valid_data_list.append(data_info)\n\n        return valid_data_list\n"
  },
  {
    "path": "mmdet/datasets/coco_semantic.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import DATASETS\nfrom .ade20k import ADE20KSegDataset\n\n\n@DATASETS.register_module()\nclass CocoSegDataset(ADE20KSegDataset):\n    \"\"\"COCO dataset.\n\n    In segmentation map annotation for COCO. The ``img_suffix`` is fixed to\n    '.jpg',  and ``seg_map_suffix`` is fixed to '.png'.\n    \"\"\"\n\n    METAINFO = dict(\n        classes=(\n            'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus',\n            'train', 'truck', 'boat', 'traffic light', 'fire hydrant',\n            'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog',\n            'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe',\n            'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',\n            'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat',\n            'baseball glove', 'skateboard', 'surfboard', 'tennis racket',\n            'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl',\n            'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot',\n            'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',\n            'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop',\n            'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven',\n            'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',\n            'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner',\n            'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet',\n            'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile',\n            'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain',\n            'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble',\n            'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', 'flower',\n            'fog', 'food-other', 'fruit', 'furniture-other', 'grass', 'gravel',\n            'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', 'metal',\n            'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net',\n            'paper', 'pavement', 'pillow', 'plant-other', 'plastic',\n            'platform', 'playingfield', 'railing', 'railroad', 'river', 'road',\n            'rock', 'roof', 'rug', 'salad', 'sand', 'sea', 'shelf',\n            'sky-other', 'skyscraper', 'snow', 'solid-other', 'stairs',\n            'stone', 'straw', 'structural-other', 'table', 'tent',\n            'textile-other', 'towel', 'tree', 'vegetable', 'wall-brick',\n            'wall-concrete', 'wall-other', 'wall-panel', 'wall-stone',\n            'wall-tile', 'wall-wood', 'water-other', 'waterdrops',\n            'window-blind', 'window-other', 'wood'),\n        palette=[(120, 120, 120), (180, 120, 120), (6, 230, 230), (80, 50, 50),\n                 (4, 200, 3), (120, 120, 80), (140, 140, 140), (204, 5, 255),\n                 (230, 230, 230), (4, 250, 7), (224, 5, 255), (235, 255, 7),\n                 (150, 5, 61), (120, 120, 70), (8, 255, 51), (255, 6, 82),\n                 (143, 255, 140), (204, 255, 4), (255, 51, 7), (204, 70, 3),\n                 (0, 102, 200), (61, 230, 250), (255, 6, 51), (11, 102, 255),\n                 (255, 7, 71), (255, 9, 224), (9, 7, 230), (220, 220, 220),\n                 (255, 9, 92), (112, 9, 255), (8, 255, 214), (7, 255, 224),\n                 (255, 184, 6), (10, 255, 71), (255, 41, 10), (7, 255, 255),\n                 (224, 255, 8), (102, 8, 255), (255, 61, 6), (255, 194, 7),\n                 (255, 122, 8), (0, 255, 20), (255, 8, 41), (255, 5, 153),\n                 (6, 51, 255), (235, 12, 255), (160, 150, 20), (0, 163, 255),\n                 (140, 140, 140), (250, 10, 15), (20, 255, 0), (31, 255, 0),\n                 (255, 31, 0), (255, 224, 0), (153, 255, 0), (0, 0, 255),\n                 (255, 71, 0), (0, 235, 255), (0, 173, 255), (31, 0, 255),\n                 (11, 200, 200), (255, 82, 0), (0, 255, 245), (0, 61, 255),\n                 (0, 255, 112), (0, 255, 133), (255, 0, 0), (255, 163, 0),\n                 (255, 102, 0), (194, 255, 0), (0, 143, 255), (51, 255, 0),\n                 (0, 82, 255), (0, 255, 41), (0, 255, 173), (10, 0, 255),\n                 (173, 255, 0), (0, 255, 153), (255, 92, 0), (255, 0, 255),\n                 (255, 0, 245), (255, 0, 102), (255, 173, 0), (255, 0, 20),\n                 (255, 184, 184), (0, 31, 255), (0, 255, 61), (0, 71, 255),\n                 (255, 0, 204), (0, 255, 194), (0, 255, 82), (0, 10, 255),\n                 (0, 112, 255), (51, 0, 255), (0, 194, 255), (0, 122, 255),\n                 (0, 255, 163), (255, 153, 0), (0, 255, 10), (255, 112, 0),\n                 (143, 255, 0), (82, 0, 255), (163, 255, 0), (255, 235, 0),\n                 (8, 184, 170), (133, 0, 255), (0, 255, 92), (184, 0, 255),\n                 (255, 0, 31), (0, 184, 255), (0, 214, 255), (255, 0, 112),\n                 (92, 255, 0), (0, 224, 255), (112, 224, 255), (70, 184, 160),\n                 (163, 0, 255), (153, 0, 255), (71, 255, 0), (255, 0, 163),\n                 (255, 204, 0), (255, 0, 143), (0, 255, 235), (133, 255, 0),\n                 (255, 0, 235), (245, 0, 255), (255, 0, 122), (255, 245, 0),\n                 (10, 190, 212), (214, 255, 0), (0, 204, 255), (20, 0, 255),\n                 (255, 255, 0), (0, 153, 255), (0, 41, 255), (0, 255, 204),\n                 (41, 0, 255), (41, 255, 0), (173, 0, 255), (0, 245, 255),\n                 (71, 0, 255), (122, 0, 255), (0, 255, 184), (0, 92, 255),\n                 (184, 255, 0), (0, 133, 255), (255, 214, 0), (25, 194, 194),\n                 (102, 255, 0), (92, 0, 255), (107, 255, 200), (58, 41, 149),\n                 (183, 121, 142), (255, 73, 97), (107, 142, 35),\n                 (190, 153, 153), (146, 139, 141), (70, 130, 180),\n                 (134, 199, 156), (209, 226, 140), (96, 36, 108), (96, 96, 96),\n                 (64, 170, 64), (152, 251, 152), (208, 229, 228),\n                 (206, 186, 171), (152, 161, 64), (116, 112, 0), (0, 114, 143),\n                 (102, 102, 156), (250, 141, 255)])\n"
  },
  {
    "path": "mmdet/datasets/crowdhuman.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nimport logging\nimport os.path as osp\nimport warnings\nfrom typing import List, Union\n\nimport mmcv\nfrom mmengine.dist import get_rank\nfrom mmengine.fileio import dump, get, get_text, load\nfrom mmengine.logging import print_log\nfrom mmengine.utils import ProgressBar\n\nfrom mmdet.registry import DATASETS\nfrom .base_det_dataset import BaseDetDataset\n\n\n@DATASETS.register_module()\nclass CrowdHumanDataset(BaseDetDataset):\n    r\"\"\"Dataset for CrowdHuman.\n\n    Args:\n        data_root (str): The root directory for\n            ``data_prefix`` and ``ann_file``.\n        ann_file (str): Annotation file path.\n        extra_ann_file (str | optional):The path of extra image metas\n            for CrowdHuman. It can be created by CrowdHumanDataset\n            automatically or by tools/misc/get_crowdhuman_id_hw.py\n            manually. Defaults to None.\n    \"\"\"\n\n    METAINFO = {\n        'classes': ('person', ),\n        # palette is a list of color tuples, which is used for visualization.\n        'palette': [(220, 20, 60)]\n    }\n\n    def __init__(self, data_root, ann_file, extra_ann_file=None, **kwargs):\n        # extra_ann_file record the size of each image. This file is\n        # automatically created when you first load the CrowdHuman\n        # dataset by mmdet.\n        if extra_ann_file is not None:\n            self.extra_ann_exist = True\n            self.extra_anns = load(extra_ann_file)\n        else:\n            ann_file_name = osp.basename(ann_file)\n            if 'train' in ann_file_name:\n                self.extra_ann_file = osp.join(data_root, 'id_hw_train.json')\n            elif 'val' in ann_file_name:\n                self.extra_ann_file = osp.join(data_root, 'id_hw_val.json')\n            self.extra_ann_exist = False\n            if not osp.isfile(self.extra_ann_file):\n                print_log(\n                    'extra_ann_file does not exist, prepare to collect '\n                    'image height and width...',\n                    level=logging.INFO)\n                self.extra_anns = {}\n            else:\n                self.extra_ann_exist = True\n                self.extra_anns = load(self.extra_ann_file)\n        super().__init__(data_root=data_root, ann_file=ann_file, **kwargs)\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        anno_strs = get_text(\n            self.ann_file, backend_args=self.backend_args).strip().split('\\n')\n        print_log('loading CrowdHuman annotation...', level=logging.INFO)\n        data_list = []\n        prog_bar = ProgressBar(len(anno_strs))\n        for i, anno_str in enumerate(anno_strs):\n            anno_dict = json.loads(anno_str)\n            parsed_data_info = self.parse_data_info(anno_dict)\n            data_list.append(parsed_data_info)\n            prog_bar.update()\n        if not self.extra_ann_exist and get_rank() == 0:\n            #  TODO: support file client\n            try:\n                dump(self.extra_anns, self.extra_ann_file, file_format='json')\n            except:  # noqa\n                warnings.warn(\n                    'Cache files can not be saved automatically! To speed up'\n                    'loading the dataset, please manually generate the cache'\n                    ' file by file tools/misc/get_crowdhuman_id_hw.py')\n\n            print_log(\n                f'\\nsave extra_ann_file in {self.data_root}',\n                level=logging.INFO)\n\n        del self.extra_anns\n        print_log('\\nDone', level=logging.INFO)\n        return data_list\n\n    def parse_data_info(self, raw_data_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            raw_data_info (dict): Raw data information load from ``ann_file``\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        data_info = {}\n        img_path = osp.join(self.data_prefix['img'],\n                            f\"{raw_data_info['ID']}.jpg\")\n        data_info['img_path'] = img_path\n        data_info['img_id'] = raw_data_info['ID']\n\n        if not self.extra_ann_exist:\n            img_bytes = get(img_path, backend_args=self.backend_args)\n            img = mmcv.imfrombytes(img_bytes, backend='cv2')\n            data_info['height'], data_info['width'] = img.shape[:2]\n            self.extra_anns[raw_data_info['ID']] = img.shape[:2]\n            del img, img_bytes\n        else:\n            data_info['height'], data_info['width'] = self.extra_anns[\n                raw_data_info['ID']]\n\n        instances = []\n        for i, ann in enumerate(raw_data_info['gtboxes']):\n            instance = {}\n            if ann['tag'] not in self.metainfo['classes']:\n                instance['bbox_label'] = -1\n                instance['ignore_flag'] = 1\n            else:\n                instance['bbox_label'] = self.metainfo['classes'].index(\n                    ann['tag'])\n                instance['ignore_flag'] = 0\n            if 'extra' in ann:\n                if 'ignore' in ann['extra']:\n                    if ann['extra']['ignore'] != 0:\n                        instance['bbox_label'] = -1\n                        instance['ignore_flag'] = 1\n\n            x1, y1, w, h = ann['fbox']\n            bbox = [x1, y1, x1 + w, y1 + h]\n            instance['bbox'] = bbox\n\n            # Record the full bbox(fbox), head bbox(hbox) and visible\n            # bbox(vbox) as additional information. If you need to use\n            # this information, you just need to design the pipeline\n            # instead of overriding the CrowdHumanDataset.\n            instance['fbox'] = bbox\n            hbox = ann['hbox']\n            instance['hbox'] = [\n                hbox[0], hbox[1], hbox[0] + hbox[2], hbox[1] + hbox[3]\n            ]\n            vbox = ann['vbox']\n            instance['vbox'] = [\n                vbox[0], vbox[1], vbox[0] + vbox[2], vbox[1] + vbox[3]\n            ]\n\n            instances.append(instance)\n\n        data_info['instances'] = instances\n        return data_info\n"
  },
  {
    "path": "mmdet/datasets/dataset_wrappers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport collections\nimport copy\nfrom typing import List, Sequence, Union\n\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.dataset import ConcatDataset as MMENGINE_ConcatDataset\nfrom mmengine.dataset import force_full_init\n\nfrom mmdet.registry import DATASETS, TRANSFORMS\n\n\n@DATASETS.register_module()\nclass MultiImageMixDataset:\n    \"\"\"A wrapper of multiple images mixed dataset.\n\n    Suitable for training on multiple images mixed data augmentation like\n    mosaic and mixup. For the augmentation pipeline of mixed image data,\n    the `get_indexes` method needs to be provided to obtain the image\n    indexes, and you can set `skip_flags` to change the pipeline running\n    process. At the same time, we provide the `dynamic_scale` parameter\n    to dynamically change the output image size.\n\n    Args:\n        dataset (:obj:`CustomDataset`): The dataset to be mixed.\n        pipeline (Sequence[dict]): Sequence of transform object or\n            config dict to be composed.\n        dynamic_scale (tuple[int], optional): The image scale can be changed\n            dynamically. Default to None. It is deprecated.\n        skip_type_keys (list[str], optional): Sequence of type string to\n            be skip pipeline. Default to None.\n        max_refetch (int): The maximum number of retry iterations for getting\n            valid results from the pipeline. If the number of iterations is\n            greater than `max_refetch`, but results is still None, then the\n            iteration is terminated and raise the error. Default: 15.\n    \"\"\"\n\n    def __init__(self,\n                 dataset: Union[BaseDataset, dict],\n                 pipeline: Sequence[str],\n                 skip_type_keys: Union[Sequence[str], None] = None,\n                 max_refetch: int = 15,\n                 lazy_init: bool = False) -> None:\n        assert isinstance(pipeline, collections.abc.Sequence)\n        if skip_type_keys is not None:\n            assert all([\n                isinstance(skip_type_key, str)\n                for skip_type_key in skip_type_keys\n            ])\n        self._skip_type_keys = skip_type_keys\n\n        self.pipeline = []\n        self.pipeline_types = []\n        for transform in pipeline:\n            if isinstance(transform, dict):\n                self.pipeline_types.append(transform['type'])\n                transform = TRANSFORMS.build(transform)\n                self.pipeline.append(transform)\n            else:\n                raise TypeError('pipeline must be a dict')\n\n        self.dataset: BaseDataset\n        if isinstance(dataset, dict):\n            self.dataset = DATASETS.build(dataset)\n        elif isinstance(dataset, BaseDataset):\n            self.dataset = dataset\n        else:\n            raise TypeError(\n                'elements in datasets sequence should be config or '\n                f'`BaseDataset` instance, but got {type(dataset)}')\n\n        self._metainfo = self.dataset.metainfo\n        if hasattr(self.dataset, 'flag'):\n            self.flag = self.dataset.flag\n        self.num_samples = len(self.dataset)\n        self.max_refetch = max_refetch\n\n        self._fully_initialized = False\n        if not lazy_init:\n            self.full_init()\n\n    @property\n    def metainfo(self) -> dict:\n        \"\"\"Get the meta information of the multi-image-mixed dataset.\n\n        Returns:\n            dict: The meta information of multi-image-mixed dataset.\n        \"\"\"\n        return copy.deepcopy(self._metainfo)\n\n    def full_init(self):\n        \"\"\"Loop to ``full_init`` each dataset.\"\"\"\n        if self._fully_initialized:\n            return\n\n        self.dataset.full_init()\n        self._ori_len = len(self.dataset)\n        self._fully_initialized = True\n\n    @force_full_init\n    def get_data_info(self, idx: int) -> dict:\n        \"\"\"Get annotation by index.\n\n        Args:\n            idx (int): Global index of ``ConcatDataset``.\n\n        Returns:\n            dict: The idx-th annotation of the datasets.\n        \"\"\"\n        return self.dataset.get_data_info(idx)\n\n    @force_full_init\n    def __len__(self):\n        return self.num_samples\n\n    def __getitem__(self, idx):\n        results = copy.deepcopy(self.dataset[idx])\n        for (transform, transform_type) in zip(self.pipeline,\n                                               self.pipeline_types):\n            if self._skip_type_keys is not None and \\\n                    transform_type in self._skip_type_keys:\n                continue\n\n            if hasattr(transform, 'get_indexes'):\n                for i in range(self.max_refetch):\n                    # Make sure the results passed the loading pipeline\n                    # of the original dataset is not None.\n                    indexes = transform.get_indexes(self.dataset)\n                    if not isinstance(indexes, collections.abc.Sequence):\n                        indexes = [indexes]\n                    mix_results = [\n                        copy.deepcopy(self.dataset[index]) for index in indexes\n                    ]\n                    if None not in mix_results:\n                        results['mix_results'] = mix_results\n                        break\n                else:\n                    raise RuntimeError(\n                        'The loading pipeline of the original dataset'\n                        ' always return None. Please check the correctness '\n                        'of the dataset and its pipeline.')\n\n            for i in range(self.max_refetch):\n                # To confirm the results passed the training pipeline\n                # of the wrapper is not None.\n                updated_results = transform(copy.deepcopy(results))\n                if updated_results is not None:\n                    results = updated_results\n                    break\n            else:\n                raise RuntimeError(\n                    'The training pipeline of the dataset wrapper'\n                    ' always return None.Please check the correctness '\n                    'of the dataset and its pipeline.')\n\n            if 'mix_results' in results:\n                results.pop('mix_results')\n\n        return results\n\n    def update_skip_type_keys(self, skip_type_keys):\n        \"\"\"Update skip_type_keys. It is called by an external hook.\n\n        Args:\n            skip_type_keys (list[str], optional): Sequence of type\n                string to be skip pipeline.\n        \"\"\"\n        assert all([\n            isinstance(skip_type_key, str) for skip_type_key in skip_type_keys\n        ])\n        self._skip_type_keys = skip_type_keys\n\n\n@DATASETS.register_module()\nclass ConcatDataset(MMENGINE_ConcatDataset):\n    \"\"\"A wrapper of concatenated dataset.\n\n    Same as ``torch.utils.data.dataset.ConcatDataset``, support\n    lazy_init and get_dataset_source.\n\n    Note:\n        ``ConcatDataset`` should not inherit from ``BaseDataset`` since\n        ``get_subset`` and ``get_subset_`` could produce ambiguous meaning\n        sub-dataset which conflicts with original dataset. If you want to use\n        a sub-dataset of ``ConcatDataset``, you should set ``indices``\n        arguments for wrapped dataset which inherit from ``BaseDataset``.\n\n    Args:\n        datasets (Sequence[BaseDataset] or Sequence[dict]): A list of datasets\n            which will be concatenated.\n        lazy_init (bool, optional): Whether to load annotation during\n            instantiation. Defaults to False.\n        ignore_keys (List[str] or str): Ignore the keys that can be\n            unequal in `dataset.metainfo`. Defaults to None.\n            `New in version 0.3.0.`\n    \"\"\"\n\n    def __init__(self,\n                 datasets: Sequence[Union[BaseDataset, dict]],\n                 lazy_init: bool = False,\n                 ignore_keys: Union[str, List[str], None] = None):\n        self.datasets: List[BaseDataset] = []\n        for i, dataset in enumerate(datasets):\n            if isinstance(dataset, dict):\n                self.datasets.append(DATASETS.build(dataset))\n            elif isinstance(dataset, BaseDataset):\n                self.datasets.append(dataset)\n            else:\n                raise TypeError(\n                    'elements in datasets sequence should be config or '\n                    f'`BaseDataset` instance, but got {type(dataset)}')\n        if ignore_keys is None:\n            self.ignore_keys = []\n        elif isinstance(ignore_keys, str):\n            self.ignore_keys = [ignore_keys]\n        elif isinstance(ignore_keys, list):\n            self.ignore_keys = ignore_keys\n        else:\n            raise TypeError('ignore_keys should be a list or str, '\n                            f'but got {type(ignore_keys)}')\n\n        meta_keys: set = set()\n        for dataset in self.datasets:\n            meta_keys |= dataset.metainfo.keys()\n        # if the metainfo of multiple datasets are the same, use metainfo\n        # of the first dataset, else the metainfo is a list with metainfo\n        # of all the datasets\n        is_all_same = True\n        self._metainfo_first = self.datasets[0].metainfo\n        for i, dataset in enumerate(self.datasets, 1):\n            for key in meta_keys:\n                if key in self.ignore_keys:\n                    continue\n                if key not in dataset.metainfo:\n                    is_all_same = False\n                    break\n                if self._metainfo_first[key] != dataset.metainfo[key]:\n                    is_all_same = False\n                    break\n\n        if is_all_same:\n            self._metainfo = self.datasets[0].metainfo\n        else:\n            self._metainfo = [dataset.metainfo for dataset in self.datasets]\n\n        self._fully_initialized = False\n        if not lazy_init:\n            self.full_init()\n\n            if is_all_same:\n                self._metainfo.update(\n                    dict(cumulative_sizes=self.cumulative_sizes))\n            else:\n                for i, dataset in enumerate(self.datasets):\n                    self._metainfo[i].update(\n                        dict(cumulative_sizes=self.cumulative_sizes))\n\n    def get_dataset_source(self, idx: int) -> int:\n        dataset_idx, _ = self._get_ori_dataset_idx(idx)\n        return dataset_idx\n"
  },
  {
    "path": "mmdet/datasets/deepfashion.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import DATASETS\nfrom .coco import CocoDataset\n\n\n@DATASETS.register_module()\nclass DeepFashionDataset(CocoDataset):\n    \"\"\"Dataset for DeepFashion.\"\"\"\n\n    METAINFO = {\n        'classes': ('top', 'skirt', 'leggings', 'dress', 'outer', 'pants',\n                    'bag', 'neckwear', 'headwear', 'eyeglass', 'belt',\n                    'footwear', 'hair', 'skin', 'face'),\n        # palette is a list of color tuples, which is used for visualization.\n        'palette': [(0, 192, 64), (0, 64, 96), (128, 192, 192), (0, 64, 64),\n                    (0, 192, 224), (0, 192, 192), (128, 192, 64), (0, 192, 96),\n                    (128, 32, 192), (0, 0, 224), (0, 0, 64), (0, 160, 192),\n                    (128, 0, 96), (128, 0, 192), (0, 32, 192)]\n    }\n"
  },
  {
    "path": "mmdet/datasets/dod.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import List, Optional\n\nimport numpy as np\n\nfrom mmdet.registry import DATASETS\nfrom .base_det_dataset import BaseDetDataset\n\ntry:\n    from d_cube import D3\nexcept ImportError:\n    D3 = None\nfrom .api_wrappers import COCO\n\n\n@DATASETS.register_module()\nclass DODDataset(BaseDetDataset):\n\n    def __init__(self,\n                 *args,\n                 data_root: Optional[str] = '',\n                 data_prefix: dict = dict(img_path=''),\n                 **kwargs) -> None:\n        if D3 is None:\n            raise ImportError(\n                'Please install d3 by `pip install ddd-dataset`.')\n        pkl_anno_path = osp.join(data_root, data_prefix['anno'])\n        self.img_root = osp.join(data_root, data_prefix['img'])\n        self.d3 = D3(self.img_root, pkl_anno_path)\n\n        sent_infos = self.d3.load_sents()\n        classes = tuple([sent_info['raw_sent'] for sent_info in sent_infos])\n        super().__init__(\n            *args,\n            data_root=data_root,\n            data_prefix=data_prefix,\n            metainfo={'classes': classes},\n            **kwargs)\n\n    def load_data_list(self) -> List[dict]:\n        coco = COCO(self.ann_file)\n        data_list = []\n        img_ids = self.d3.get_img_ids()\n        for img_id in img_ids:\n            data_info = {}\n\n            img_info = self.d3.load_imgs(img_id)[0]\n            file_name = img_info['file_name']\n            img_path = osp.join(self.img_root, file_name)\n            data_info['img_path'] = img_path\n            data_info['img_id'] = img_id\n            data_info['height'] = img_info['height']\n            data_info['width'] = img_info['width']\n\n            group_ids = self.d3.get_group_ids(img_ids=[img_id])\n            sent_ids = self.d3.get_sent_ids(group_ids=group_ids)\n            sent_list = self.d3.load_sents(sent_ids=sent_ids)\n            text_list = [sent['raw_sent'] for sent in sent_list]\n            ann_ids = coco.get_ann_ids(img_ids=[img_id])\n            anno = coco.load_anns(ann_ids)\n\n            data_info['text'] = text_list\n            data_info['sent_ids'] = np.array([s for s in sent_ids])\n            data_info['custom_entities'] = True\n\n            instances = []\n            for i, ann in enumerate(anno):\n                instance = {}\n                x1, y1, w, h = ann['bbox']\n                bbox = [x1, y1, x1 + w, y1 + h]\n                instance['ignore_flag'] = 0\n                instance['bbox'] = bbox\n                instance['bbox_label'] = ann['category_id'] - 1\n                instances.append(instance)\n            data_info['instances'] = instances\n            data_list.append(data_info)\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/dsdl.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nfrom typing import List\n\nfrom mmdet.registry import DATASETS\nfrom .base_det_dataset import BaseDetDataset\n\ntry:\n    from dsdl.dataset import DSDLDataset\nexcept ImportError:\n    DSDLDataset = None\n\n\n@DATASETS.register_module()\nclass DSDLDetDataset(BaseDetDataset):\n    \"\"\"Dataset for dsdl detection.\n\n    Args:\n        with_bbox(bool): Load bbox or not, defaults to be True.\n        with_polygon(bool): Load polygon or not, defaults to be False.\n        with_mask(bool): Load seg map mask or not, defaults to be False.\n        with_imagelevel_label(bool): Load image level label or not,\n            defaults to be False.\n        with_hierarchy(bool): Load hierarchy information or not,\n            defaults to be False.\n        specific_key_path(dict): Path of specific key which can not\n            be loaded by it's field name.\n        pre_transform(dict): pre-transform functions before loading.\n    \"\"\"\n\n    METAINFO = {}\n\n    def __init__(self,\n                 with_bbox: bool = True,\n                 with_polygon: bool = False,\n                 with_mask: bool = False,\n                 with_imagelevel_label: bool = False,\n                 with_hierarchy: bool = False,\n                 specific_key_path: dict = {},\n                 pre_transform: dict = {},\n                 **kwargs) -> None:\n\n        if DSDLDataset is None:\n            raise RuntimeError(\n                'Package dsdl is not installed. Please run \"pip install dsdl\".'\n            )\n\n        self.with_hierarchy = with_hierarchy\n        self.specific_key_path = specific_key_path\n\n        loc_config = dict(type='LocalFileReader', working_dir='')\n        if kwargs.get('data_root'):\n            kwargs['ann_file'] = os.path.join(kwargs['data_root'],\n                                              kwargs['ann_file'])\n        self.required_fields = ['Image', 'ImageShape', 'Label', 'ignore_flag']\n        if with_bbox:\n            self.required_fields.append('Bbox')\n        if with_polygon:\n            self.required_fields.append('Polygon')\n        if with_mask:\n            self.required_fields.append('LabelMap')\n        if with_imagelevel_label:\n            self.required_fields.append('image_level_labels')\n            assert 'image_level_labels' in specific_key_path.keys(\n            ), '`image_level_labels` not specified in `specific_key_path` !'\n\n        self.extra_keys = [\n            key for key in self.specific_key_path.keys()\n            if key not in self.required_fields\n        ]\n\n        self.dsdldataset = DSDLDataset(\n            dsdl_yaml=kwargs['ann_file'],\n            location_config=loc_config,\n            required_fields=self.required_fields,\n            specific_key_path=specific_key_path,\n            transform=pre_transform,\n        )\n\n        BaseDetDataset.__init__(self, **kwargs)\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load data info from an dsdl yaml file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of data info.\n        \"\"\"\n        if self.with_hierarchy:\n            # get classes_names and relation_matrix\n            classes_names, relation_matrix = \\\n                self.dsdldataset.class_dom.get_hierarchy_info()\n            self._metainfo['classes'] = tuple(classes_names)\n            self._metainfo['RELATION_MATRIX'] = relation_matrix\n\n        else:\n            self._metainfo['classes'] = tuple(self.dsdldataset.class_names)\n\n        data_list = []\n\n        for i, data in enumerate(self.dsdldataset):\n            # basic image info, including image id, path and size.\n            datainfo = dict(\n                img_id=i,\n                img_path=os.path.join(self.data_prefix['img_path'],\n                                      data['Image'][0].location),\n                width=data['ImageShape'][0].width,\n                height=data['ImageShape'][0].height,\n            )\n\n            # get image label info\n            if 'image_level_labels' in data.keys():\n                if self.with_hierarchy:\n                    # get leaf node name when using hierarchy classes\n                    datainfo['image_level_labels'] = [\n                        self._metainfo['classes'].index(i.leaf_node_name)\n                        for i in data['image_level_labels']\n                    ]\n                else:\n                    datainfo['image_level_labels'] = [\n                        self._metainfo['classes'].index(i.name)\n                        for i in data['image_level_labels']\n                    ]\n\n            # get semantic segmentation info\n            if 'LabelMap' in data.keys():\n                datainfo['seg_map_path'] = data['LabelMap']\n\n            # load instance info\n            instances = []\n            if 'Bbox' in data.keys():\n                for idx in range(len(data['Bbox'])):\n                    bbox = data['Bbox'][idx]\n                    if self.with_hierarchy:\n                        # get leaf node name when using hierarchy classes\n                        label = data['Label'][idx].leaf_node_name\n                        label_index = self._metainfo['classes'].index(label)\n                    else:\n                        label = data['Label'][idx].name\n                        label_index = self._metainfo['classes'].index(label)\n\n                    instance = {}\n                    instance['bbox'] = bbox.xyxy\n                    instance['bbox_label'] = label_index\n\n                    if 'ignore_flag' in data.keys():\n                        # get ignore flag\n                        instance['ignore_flag'] = data['ignore_flag'][idx]\n                    else:\n                        instance['ignore_flag'] = 0\n\n                    if 'Polygon' in data.keys():\n                        # get polygon info\n                        polygon = data['Polygon'][idx]\n                        instance['mask'] = polygon.openmmlabformat\n\n                    for key in self.extra_keys:\n                        # load extra instance info\n                        instance[key] = data[key][idx]\n\n                    instances.append(instance)\n\n            datainfo['instances'] = instances\n            # append a standard sample in data list\n            if len(datainfo['instances']) > 0:\n                data_list.append(datainfo)\n\n        return data_list\n\n    def filter_data(self) -> List[dict]:\n        \"\"\"Filter annotations according to filter_cfg.\n\n        Returns:\n            List[dict]: Filtered results.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        filter_empty_gt = self.filter_cfg.get('filter_empty_gt', False) \\\n            if self.filter_cfg is not None else False\n        min_size = self.filter_cfg.get('min_size', 0) \\\n            if self.filter_cfg is not None else 0\n\n        valid_data_list = []\n        for i, data_info in enumerate(self.data_list):\n            width = data_info['width']\n            height = data_info['height']\n            if filter_empty_gt and len(data_info['instances']) == 0:\n                continue\n            if min(width, height) >= min_size:\n                valid_data_list.append(data_info)\n\n        return valid_data_list\n"
  },
  {
    "path": "mmdet/datasets/flickr30k.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import List\n\nfrom pycocotools.coco import COCO\n\nfrom mmdet.registry import DATASETS\nfrom .base_det_dataset import BaseDetDataset\n\n\ndef convert_phrase_ids(phrase_ids: list) -> list:\n    unique_elements = sorted(set(phrase_ids))\n    element_to_new_label = {\n        element: label\n        for label, element in enumerate(unique_elements)\n    }\n    phrase_ids = [element_to_new_label[element] for element in phrase_ids]\n    return phrase_ids\n\n\n@DATASETS.register_module()\nclass Flickr30kDataset(BaseDetDataset):\n    \"\"\"Flickr30K Dataset.\"\"\"\n\n    def load_data_list(self) -> List[dict]:\n\n        self.coco = COCO(self.ann_file)\n\n        self.ids = sorted(list(self.coco.imgs.keys()))\n\n        data_list = []\n        for img_id in self.ids:\n            if isinstance(img_id, str):\n                ann_ids = self.coco.getAnnIds(imgIds=[img_id], iscrowd=None)\n            else:\n                ann_ids = self.coco.getAnnIds(imgIds=img_id, iscrowd=None)\n\n            coco_img = self.coco.loadImgs(img_id)[0]\n\n            caption = coco_img['caption']\n            file_name = coco_img['file_name']\n            img_path = osp.join(self.data_prefix['img'], file_name)\n            width = coco_img['width']\n            height = coco_img['height']\n            tokens_positive = coco_img['tokens_positive_eval']\n            phrases = [caption[i[0][0]:i[0][1]] for i in tokens_positive]\n            phrase_ids = []\n\n            instances = []\n            annos = self.coco.loadAnns(ann_ids)\n            for anno in annos:\n                instance = {\n                    'bbox': [\n                        anno['bbox'][0], anno['bbox'][1],\n                        anno['bbox'][0] + anno['bbox'][2],\n                        anno['bbox'][1] + anno['bbox'][3]\n                    ],\n                    'bbox_label':\n                    anno['category_id'],\n                    'ignore_flag':\n                    anno['iscrowd']\n                }\n                phrase_ids.append(anno['phrase_ids'])\n                instances.append(instance)\n\n            phrase_ids = convert_phrase_ids(phrase_ids)\n\n            data_list.append(\n                dict(\n                    img_path=img_path,\n                    img_id=img_id,\n                    height=height,\n                    width=width,\n                    instances=instances,\n                    text=caption,\n                    phrase_ids=phrase_ids,\n                    tokens_positive=tokens_positive,\n                    phrases=phrases,\n                ))\n\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/isaid.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import DATASETS\nfrom .coco import CocoDataset\n\n\n@DATASETS.register_module()\nclass iSAIDDataset(CocoDataset):\n    \"\"\"Dataset for iSAID instance segmentation.\n\n    iSAID: A Large-scale Dataset for Instance Segmentation\n    in Aerial Images.\n\n    For more detail, please refer to \"projects/iSAID/README.md\"\n    \"\"\"\n\n    METAINFO = dict(\n        classes=('background', 'ship', 'store_tank', 'baseball_diamond',\n                 'tennis_court', 'basketball_court', 'Ground_Track_Field',\n                 'Bridge', 'Large_Vehicle', 'Small_Vehicle', 'Helicopter',\n                 'Swimming_pool', 'Roundabout', 'Soccer_ball_field', 'plane',\n                 'Harbor'),\n        palette=[(0, 0, 0), (0, 0, 63), (0, 63, 63), (0, 63, 0), (0, 63, 127),\n                 (0, 63, 191), (0, 63, 255), (0, 127, 63), (0, 127, 127),\n                 (0, 0, 127), (0, 0, 191), (0, 0, 255), (0, 191, 127),\n                 (0, 127, 191), (0, 127, 255), (0, 100, 155)])\n"
  },
  {
    "path": "mmdet/datasets/lvis.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport warnings\nfrom typing import List\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.registry import DATASETS\nfrom .coco import CocoDataset\n\n\n@DATASETS.register_module()\nclass LVISV05Dataset(CocoDataset):\n    \"\"\"LVIS v0.5 dataset for detection.\"\"\"\n\n    METAINFO = {\n        'classes':\n        ('acorn', 'aerosol_can', 'air_conditioner', 'airplane', 'alarm_clock',\n         'alcohol', 'alligator', 'almond', 'ambulance', 'amplifier', 'anklet',\n         'antenna', 'apple', 'apple_juice', 'applesauce', 'apricot', 'apron',\n         'aquarium', 'armband', 'armchair', 'armoire', 'armor', 'artichoke',\n         'trash_can', 'ashtray', 'asparagus', 'atomizer', 'avocado', 'award',\n         'awning', 'ax', 'baby_buggy', 'basketball_backboard', 'backpack',\n         'handbag', 'suitcase', 'bagel', 'bagpipe', 'baguet', 'bait', 'ball',\n         'ballet_skirt', 'balloon', 'bamboo', 'banana', 'Band_Aid', 'bandage',\n         'bandanna', 'banjo', 'banner', 'barbell', 'barge', 'barrel',\n         'barrette', 'barrow', 'baseball_base', 'baseball', 'baseball_bat',\n         'baseball_cap', 'baseball_glove', 'basket', 'basketball_hoop',\n         'basketball', 'bass_horn', 'bat_(animal)', 'bath_mat', 'bath_towel',\n         'bathrobe', 'bathtub', 'batter_(food)', 'battery', 'beachball',\n         'bead', 'beaker', 'bean_curd', 'beanbag', 'beanie', 'bear', 'bed',\n         'bedspread', 'cow', 'beef_(food)', 'beeper', 'beer_bottle',\n         'beer_can', 'beetle', 'bell', 'bell_pepper', 'belt', 'belt_buckle',\n         'bench', 'beret', 'bib', 'Bible', 'bicycle', 'visor', 'binder',\n         'binoculars', 'bird', 'birdfeeder', 'birdbath', 'birdcage',\n         'birdhouse', 'birthday_cake', 'birthday_card', 'biscuit_(bread)',\n         'pirate_flag', 'black_sheep', 'blackboard', 'blanket', 'blazer',\n         'blender', 'blimp', 'blinker', 'blueberry', 'boar', 'gameboard',\n         'boat', 'bobbin', 'bobby_pin', 'boiled_egg', 'bolo_tie', 'deadbolt',\n         'bolt', 'bonnet', 'book', 'book_bag', 'bookcase', 'booklet',\n         'bookmark', 'boom_microphone', 'boot', 'bottle', 'bottle_opener',\n         'bouquet', 'bow_(weapon)', 'bow_(decorative_ribbons)', 'bow-tie',\n         'bowl', 'pipe_bowl', 'bowler_hat', 'bowling_ball', 'bowling_pin',\n         'boxing_glove', 'suspenders', 'bracelet', 'brass_plaque', 'brassiere',\n         'bread-bin', 'breechcloth', 'bridal_gown', 'briefcase',\n         'bristle_brush', 'broccoli', 'broach', 'broom', 'brownie',\n         'brussels_sprouts', 'bubble_gum', 'bucket', 'horse_buggy', 'bull',\n         'bulldog', 'bulldozer', 'bullet_train', 'bulletin_board',\n         'bulletproof_vest', 'bullhorn', 'corned_beef', 'bun', 'bunk_bed',\n         'buoy', 'burrito', 'bus_(vehicle)', 'business_card', 'butcher_knife',\n         'butter', 'butterfly', 'button', 'cab_(taxi)', 'cabana', 'cabin_car',\n         'cabinet', 'locker', 'cake', 'calculator', 'calendar', 'calf',\n         'camcorder', 'camel', 'camera', 'camera_lens', 'camper_(vehicle)',\n         'can', 'can_opener', 'candelabrum', 'candle', 'candle_holder',\n         'candy_bar', 'candy_cane', 'walking_cane', 'canister', 'cannon',\n         'canoe', 'cantaloup', 'canteen', 'cap_(headwear)', 'bottle_cap',\n         'cape', 'cappuccino', 'car_(automobile)', 'railcar_(part_of_a_train)',\n         'elevator_car', 'car_battery', 'identity_card', 'card', 'cardigan',\n         'cargo_ship', 'carnation', 'horse_carriage', 'carrot', 'tote_bag',\n         'cart', 'carton', 'cash_register', 'casserole', 'cassette', 'cast',\n         'cat', 'cauliflower', 'caviar', 'cayenne_(spice)', 'CD_player',\n         'celery', 'cellular_telephone', 'chain_mail', 'chair',\n         'chaise_longue', 'champagne', 'chandelier', 'chap', 'checkbook',\n         'checkerboard', 'cherry', 'chessboard',\n         'chest_of_drawers_(furniture)', 'chicken_(animal)', 'chicken_wire',\n         'chickpea', 'Chihuahua', 'chili_(vegetable)', 'chime', 'chinaware',\n         'crisp_(potato_chip)', 'poker_chip', 'chocolate_bar',\n         'chocolate_cake', 'chocolate_milk', 'chocolate_mousse', 'choker',\n         'chopping_board', 'chopstick', 'Christmas_tree', 'slide', 'cider',\n         'cigar_box', 'cigarette', 'cigarette_case', 'cistern', 'clarinet',\n         'clasp', 'cleansing_agent', 'clementine', 'clip', 'clipboard',\n         'clock', 'clock_tower', 'clothes_hamper', 'clothespin', 'clutch_bag',\n         'coaster', 'coat', 'coat_hanger', 'coatrack', 'cock', 'coconut',\n         'coffee_filter', 'coffee_maker', 'coffee_table', 'coffeepot', 'coil',\n         'coin', 'colander', 'coleslaw', 'coloring_material',\n         'combination_lock', 'pacifier', 'comic_book', 'computer_keyboard',\n         'concrete_mixer', 'cone', 'control', 'convertible_(automobile)',\n         'sofa_bed', 'cookie', 'cookie_jar', 'cooking_utensil',\n         'cooler_(for_food)', 'cork_(bottle_plug)', 'corkboard', 'corkscrew',\n         'edible_corn', 'cornbread', 'cornet', 'cornice', 'cornmeal', 'corset',\n         'romaine_lettuce', 'costume', 'cougar', 'coverall', 'cowbell',\n         'cowboy_hat', 'crab_(animal)', 'cracker', 'crape', 'crate', 'crayon',\n         'cream_pitcher', 'credit_card', 'crescent_roll', 'crib', 'crock_pot',\n         'crossbar', 'crouton', 'crow', 'crown', 'crucifix', 'cruise_ship',\n         'police_cruiser', 'crumb', 'crutch', 'cub_(animal)', 'cube',\n         'cucumber', 'cufflink', 'cup', 'trophy_cup', 'cupcake', 'hair_curler',\n         'curling_iron', 'curtain', 'cushion', 'custard', 'cutting_tool',\n         'cylinder', 'cymbal', 'dachshund', 'dagger', 'dartboard',\n         'date_(fruit)', 'deck_chair', 'deer', 'dental_floss', 'desk',\n         'detergent', 'diaper', 'diary', 'die', 'dinghy', 'dining_table',\n         'tux', 'dish', 'dish_antenna', 'dishrag', 'dishtowel', 'dishwasher',\n         'dishwasher_detergent', 'diskette', 'dispenser', 'Dixie_cup', 'dog',\n         'dog_collar', 'doll', 'dollar', 'dolphin', 'domestic_ass', 'eye_mask',\n         'doorbell', 'doorknob', 'doormat', 'doughnut', 'dove', 'dragonfly',\n         'drawer', 'underdrawers', 'dress', 'dress_hat', 'dress_suit',\n         'dresser', 'drill', 'drinking_fountain', 'drone', 'dropper',\n         'drum_(musical_instrument)', 'drumstick', 'duck', 'duckling',\n         'duct_tape', 'duffel_bag', 'dumbbell', 'dumpster', 'dustpan',\n         'Dutch_oven', 'eagle', 'earphone', 'earplug', 'earring', 'easel',\n         'eclair', 'eel', 'egg', 'egg_roll', 'egg_yolk', 'eggbeater',\n         'eggplant', 'electric_chair', 'refrigerator', 'elephant', 'elk',\n         'envelope', 'eraser', 'escargot', 'eyepatch', 'falcon', 'fan',\n         'faucet', 'fedora', 'ferret', 'Ferris_wheel', 'ferry', 'fig_(fruit)',\n         'fighter_jet', 'figurine', 'file_cabinet', 'file_(tool)',\n         'fire_alarm', 'fire_engine', 'fire_extinguisher', 'fire_hose',\n         'fireplace', 'fireplug', 'fish', 'fish_(food)', 'fishbowl',\n         'fishing_boat', 'fishing_rod', 'flag', 'flagpole', 'flamingo',\n         'flannel', 'flash', 'flashlight', 'fleece', 'flip-flop_(sandal)',\n         'flipper_(footwear)', 'flower_arrangement', 'flute_glass', 'foal',\n         'folding_chair', 'food_processor', 'football_(American)',\n         'football_helmet', 'footstool', 'fork', 'forklift', 'freight_car',\n         'French_toast', 'freshener', 'frisbee', 'frog', 'fruit_juice',\n         'fruit_salad', 'frying_pan', 'fudge', 'funnel', 'futon', 'gag',\n         'garbage', 'garbage_truck', 'garden_hose', 'gargle', 'gargoyle',\n         'garlic', 'gasmask', 'gazelle', 'gelatin', 'gemstone', 'giant_panda',\n         'gift_wrap', 'ginger', 'giraffe', 'cincture',\n         'glass_(drink_container)', 'globe', 'glove', 'goat', 'goggles',\n         'goldfish', 'golf_club', 'golfcart', 'gondola_(boat)', 'goose',\n         'gorilla', 'gourd', 'surgical_gown', 'grape', 'grasshopper', 'grater',\n         'gravestone', 'gravy_boat', 'green_bean', 'green_onion', 'griddle',\n         'grillroom', 'grinder_(tool)', 'grits', 'grizzly', 'grocery_bag',\n         'guacamole', 'guitar', 'gull', 'gun', 'hair_spray', 'hairbrush',\n         'hairnet', 'hairpin', 'ham', 'hamburger', 'hammer', 'hammock',\n         'hamper', 'hamster', 'hair_dryer', 'hand_glass', 'hand_towel',\n         'handcart', 'handcuff', 'handkerchief', 'handle', 'handsaw',\n         'hardback_book', 'harmonium', 'hat', 'hatbox', 'hatch', 'veil',\n         'headband', 'headboard', 'headlight', 'headscarf', 'headset',\n         'headstall_(for_horses)', 'hearing_aid', 'heart', 'heater',\n         'helicopter', 'helmet', 'heron', 'highchair', 'hinge', 'hippopotamus',\n         'hockey_stick', 'hog', 'home_plate_(baseball)', 'honey', 'fume_hood',\n         'hook', 'horse', 'hose', 'hot-air_balloon', 'hotplate', 'hot_sauce',\n         'hourglass', 'houseboat', 'hummingbird', 'hummus', 'polar_bear',\n         'icecream', 'popsicle', 'ice_maker', 'ice_pack', 'ice_skate',\n         'ice_tea', 'igniter', 'incense', 'inhaler', 'iPod',\n         'iron_(for_clothing)', 'ironing_board', 'jacket', 'jam', 'jean',\n         'jeep', 'jelly_bean', 'jersey', 'jet_plane', 'jewelry', 'joystick',\n         'jumpsuit', 'kayak', 'keg', 'kennel', 'kettle', 'key', 'keycard',\n         'kilt', 'kimono', 'kitchen_sink', 'kitchen_table', 'kite', 'kitten',\n         'kiwi_fruit', 'knee_pad', 'knife', 'knight_(chess_piece)',\n         'knitting_needle', 'knob', 'knocker_(on_a_door)', 'koala', 'lab_coat',\n         'ladder', 'ladle', 'ladybug', 'lamb_(animal)', 'lamb-chop', 'lamp',\n         'lamppost', 'lampshade', 'lantern', 'lanyard', 'laptop_computer',\n         'lasagna', 'latch', 'lawn_mower', 'leather', 'legging_(clothing)',\n         'Lego', 'lemon', 'lemonade', 'lettuce', 'license_plate', 'life_buoy',\n         'life_jacket', 'lightbulb', 'lightning_rod', 'lime', 'limousine',\n         'linen_paper', 'lion', 'lip_balm', 'lipstick', 'liquor', 'lizard',\n         'Loafer_(type_of_shoe)', 'log', 'lollipop', 'lotion',\n         'speaker_(stereo_equipment)', 'loveseat', 'machine_gun', 'magazine',\n         'magnet', 'mail_slot', 'mailbox_(at_home)', 'mallet', 'mammoth',\n         'mandarin_orange', 'manger', 'manhole', 'map', 'marker', 'martini',\n         'mascot', 'mashed_potato', 'masher', 'mask', 'mast',\n         'mat_(gym_equipment)', 'matchbox', 'mattress', 'measuring_cup',\n         'measuring_stick', 'meatball', 'medicine', 'melon', 'microphone',\n         'microscope', 'microwave_oven', 'milestone', 'milk', 'minivan',\n         'mint_candy', 'mirror', 'mitten', 'mixer_(kitchen_tool)', 'money',\n         'monitor_(computer_equipment) computer_monitor', 'monkey', 'motor',\n         'motor_scooter', 'motor_vehicle', 'motorboat', 'motorcycle',\n         'mound_(baseball)', 'mouse_(animal_rodent)',\n         'mouse_(computer_equipment)', 'mousepad', 'muffin', 'mug', 'mushroom',\n         'music_stool', 'musical_instrument', 'nailfile', 'nameplate',\n         'napkin', 'neckerchief', 'necklace', 'necktie', 'needle', 'nest',\n         'newsstand', 'nightshirt', 'nosebag_(for_animals)',\n         'noseband_(for_animals)', 'notebook', 'notepad', 'nut', 'nutcracker',\n         'oar', 'octopus_(food)', 'octopus_(animal)', 'oil_lamp', 'olive_oil',\n         'omelet', 'onion', 'orange_(fruit)', 'orange_juice', 'oregano',\n         'ostrich', 'ottoman', 'overalls_(clothing)', 'owl', 'packet',\n         'inkpad', 'pad', 'paddle', 'padlock', 'paintbox', 'paintbrush',\n         'painting', 'pajamas', 'palette', 'pan_(for_cooking)',\n         'pan_(metal_container)', 'pancake', 'pantyhose', 'papaya',\n         'paperclip', 'paper_plate', 'paper_towel', 'paperback_book',\n         'paperweight', 'parachute', 'parakeet', 'parasail_(sports)',\n         'parchment', 'parka', 'parking_meter', 'parrot',\n         'passenger_car_(part_of_a_train)', 'passenger_ship', 'passport',\n         'pastry', 'patty_(food)', 'pea_(food)', 'peach', 'peanut_butter',\n         'pear', 'peeler_(tool_for_fruit_and_vegetables)', 'pegboard',\n         'pelican', 'pen', 'pencil', 'pencil_box', 'pencil_sharpener',\n         'pendulum', 'penguin', 'pennant', 'penny_(coin)', 'pepper',\n         'pepper_mill', 'perfume', 'persimmon', 'baby', 'pet', 'petfood',\n         'pew_(church_bench)', 'phonebook', 'phonograph_record', 'piano',\n         'pickle', 'pickup_truck', 'pie', 'pigeon', 'piggy_bank', 'pillow',\n         'pin_(non_jewelry)', 'pineapple', 'pinecone', 'ping-pong_ball',\n         'pinwheel', 'tobacco_pipe', 'pipe', 'pistol', 'pita_(bread)',\n         'pitcher_(vessel_for_liquid)', 'pitchfork', 'pizza', 'place_mat',\n         'plate', 'platter', 'playing_card', 'playpen', 'pliers',\n         'plow_(farm_equipment)', 'pocket_watch', 'pocketknife',\n         'poker_(fire_stirring_tool)', 'pole', 'police_van', 'polo_shirt',\n         'poncho', 'pony', 'pool_table', 'pop_(soda)', 'portrait',\n         'postbox_(public)', 'postcard', 'poster', 'pot', 'flowerpot',\n         'potato', 'potholder', 'pottery', 'pouch', 'power_shovel', 'prawn',\n         'printer', 'projectile_(weapon)', 'projector', 'propeller', 'prune',\n         'pudding', 'puffer_(fish)', 'puffin', 'pug-dog', 'pumpkin', 'puncher',\n         'puppet', 'puppy', 'quesadilla', 'quiche', 'quilt', 'rabbit',\n         'race_car', 'racket', 'radar', 'radiator', 'radio_receiver', 'radish',\n         'raft', 'rag_doll', 'raincoat', 'ram_(animal)', 'raspberry', 'rat',\n         'razorblade', 'reamer_(juicer)', 'rearview_mirror', 'receipt',\n         'recliner', 'record_player', 'red_cabbage', 'reflector',\n         'remote_control', 'rhinoceros', 'rib_(food)', 'rifle', 'ring',\n         'river_boat', 'road_map', 'robe', 'rocking_chair', 'roller_skate',\n         'Rollerblade', 'rolling_pin', 'root_beer',\n         'router_(computer_equipment)', 'rubber_band', 'runner_(carpet)',\n         'plastic_bag', 'saddle_(on_an_animal)', 'saddle_blanket', 'saddlebag',\n         'safety_pin', 'sail', 'salad', 'salad_plate', 'salami',\n         'salmon_(fish)', 'salmon_(food)', 'salsa', 'saltshaker',\n         'sandal_(type_of_shoe)', 'sandwich', 'satchel', 'saucepan', 'saucer',\n         'sausage', 'sawhorse', 'saxophone', 'scale_(measuring_instrument)',\n         'scarecrow', 'scarf', 'school_bus', 'scissors', 'scoreboard',\n         'scrambled_eggs', 'scraper', 'scratcher', 'screwdriver',\n         'scrubbing_brush', 'sculpture', 'seabird', 'seahorse', 'seaplane',\n         'seashell', 'seedling', 'serving_dish', 'sewing_machine', 'shaker',\n         'shampoo', 'shark', 'sharpener', 'Sharpie', 'shaver_(electric)',\n         'shaving_cream', 'shawl', 'shears', 'sheep', 'shepherd_dog',\n         'sherbert', 'shield', 'shirt', 'shoe', 'shopping_bag',\n         'shopping_cart', 'short_pants', 'shot_glass', 'shoulder_bag',\n         'shovel', 'shower_head', 'shower_curtain', 'shredder_(for_paper)',\n         'sieve', 'signboard', 'silo', 'sink', 'skateboard', 'skewer', 'ski',\n         'ski_boot', 'ski_parka', 'ski_pole', 'skirt', 'sled', 'sleeping_bag',\n         'sling_(bandage)', 'slipper_(footwear)', 'smoothie', 'snake',\n         'snowboard', 'snowman', 'snowmobile', 'soap', 'soccer_ball', 'sock',\n         'soda_fountain', 'carbonated_water', 'sofa', 'softball',\n         'solar_array', 'sombrero', 'soup', 'soup_bowl', 'soupspoon',\n         'sour_cream', 'soya_milk', 'space_shuttle', 'sparkler_(fireworks)',\n         'spatula', 'spear', 'spectacles', 'spice_rack', 'spider', 'sponge',\n         'spoon', 'sportswear', 'spotlight', 'squirrel',\n         'stapler_(stapling_machine)', 'starfish', 'statue_(sculpture)',\n         'steak_(food)', 'steak_knife', 'steamer_(kitchen_appliance)',\n         'steering_wheel', 'stencil', 'stepladder', 'step_stool',\n         'stereo_(sound_system)', 'stew', 'stirrer', 'stirrup',\n         'stockings_(leg_wear)', 'stool', 'stop_sign', 'brake_light', 'stove',\n         'strainer', 'strap', 'straw_(for_drinking)', 'strawberry',\n         'street_sign', 'streetlight', 'string_cheese', 'stylus', 'subwoofer',\n         'sugar_bowl', 'sugarcane_(plant)', 'suit_(clothing)', 'sunflower',\n         'sunglasses', 'sunhat', 'sunscreen', 'surfboard', 'sushi', 'mop',\n         'sweat_pants', 'sweatband', 'sweater', 'sweatshirt', 'sweet_potato',\n         'swimsuit', 'sword', 'syringe', 'Tabasco_sauce', 'table-tennis_table',\n         'table', 'table_lamp', 'tablecloth', 'tachometer', 'taco', 'tag',\n         'taillight', 'tambourine', 'army_tank', 'tank_(storage_vessel)',\n         'tank_top_(clothing)', 'tape_(sticky_cloth_or_paper)', 'tape_measure',\n         'tapestry', 'tarp', 'tartan', 'tassel', 'tea_bag', 'teacup',\n         'teakettle', 'teapot', 'teddy_bear', 'telephone', 'telephone_booth',\n         'telephone_pole', 'telephoto_lens', 'television_camera',\n         'television_set', 'tennis_ball', 'tennis_racket', 'tequila',\n         'thermometer', 'thermos_bottle', 'thermostat', 'thimble', 'thread',\n         'thumbtack', 'tiara', 'tiger', 'tights_(clothing)', 'timer',\n         'tinfoil', 'tinsel', 'tissue_paper', 'toast_(food)', 'toaster',\n         'toaster_oven', 'toilet', 'toilet_tissue', 'tomato', 'tongs',\n         'toolbox', 'toothbrush', 'toothpaste', 'toothpick', 'cover',\n         'tortilla', 'tow_truck', 'towel', 'towel_rack', 'toy',\n         'tractor_(farm_equipment)', 'traffic_light', 'dirt_bike',\n         'trailer_truck', 'train_(railroad_vehicle)', 'trampoline', 'tray',\n         'tree_house', 'trench_coat', 'triangle_(musical_instrument)',\n         'tricycle', 'tripod', 'trousers', 'truck', 'truffle_(chocolate)',\n         'trunk', 'vat', 'turban', 'turkey_(bird)', 'turkey_(food)', 'turnip',\n         'turtle', 'turtleneck_(clothing)', 'typewriter', 'umbrella',\n         'underwear', 'unicycle', 'urinal', 'urn', 'vacuum_cleaner', 'valve',\n         'vase', 'vending_machine', 'vent', 'videotape', 'vinegar', 'violin',\n         'vodka', 'volleyball', 'vulture', 'waffle', 'waffle_iron', 'wagon',\n         'wagon_wheel', 'walking_stick', 'wall_clock', 'wall_socket', 'wallet',\n         'walrus', 'wardrobe', 'wasabi', 'automatic_washer', 'watch',\n         'water_bottle', 'water_cooler', 'water_faucet', 'water_filter',\n         'water_heater', 'water_jug', 'water_gun', 'water_scooter',\n         'water_ski', 'water_tower', 'watering_can', 'watermelon',\n         'weathervane', 'webcam', 'wedding_cake', 'wedding_ring', 'wet_suit',\n         'wheel', 'wheelchair', 'whipped_cream', 'whiskey', 'whistle', 'wick',\n         'wig', 'wind_chime', 'windmill', 'window_box_(for_plants)',\n         'windshield_wiper', 'windsock', 'wine_bottle', 'wine_bucket',\n         'wineglass', 'wing_chair', 'blinder_(for_horses)', 'wok', 'wolf',\n         'wooden_spoon', 'wreath', 'wrench', 'wristband', 'wristlet', 'yacht',\n         'yak', 'yogurt', 'yoke_(animal_equipment)', 'zebra', 'zucchini'),\n        'palette':\n        None\n    }\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        try:\n            import lvis\n            if getattr(lvis, '__version__', '0') >= '10.5.3':\n                warnings.warn(\n                    'mmlvis is deprecated, please install official lvis-api by \"pip install git+https://github.com/lvis-dataset/lvis-api.git\"',  # noqa: E501\n                    UserWarning)\n            from lvis import LVIS\n        except ImportError:\n            raise ImportError(\n                'Package lvis is not installed. Please run \"pip install git+https://github.com/lvis-dataset/lvis-api.git\".'  # noqa: E501\n            )\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.lvis = LVIS(local_path)\n        self.cat_ids = self.lvis.get_cat_ids()\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.lvis.cat_img_map)\n\n        img_ids = self.lvis.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.lvis.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n            if raw_img_info['file_name'].startswith('COCO'):\n                # Convert form the COCO 2014 file naming convention of\n                # COCO_[train/val/test]2014_000000000000.jpg to the 2017\n                # naming convention of 000000000000.jpg\n                # (LVIS v1 will fix this naming issue)\n                raw_img_info['file_name'] = raw_img_info['file_name'][-16:]\n            ann_ids = self.lvis.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = self.lvis.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n\n            parsed_data_info = self.parse_data_info({\n                'raw_ann_info':\n                raw_ann_info,\n                'raw_img_info':\n                raw_img_info\n            })\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.lvis\n\n        return data_list\n\n\nLVISDataset = LVISV05Dataset\nDATASETS.register_module(name='LVISDataset', module=LVISDataset)\n\n\n@DATASETS.register_module()\nclass LVISV1Dataset(LVISDataset):\n    \"\"\"LVIS v1 dataset for detection.\"\"\"\n\n    METAINFO = {\n        'classes':\n        ('aerosol_can', 'air_conditioner', 'airplane', 'alarm_clock',\n         'alcohol', 'alligator', 'almond', 'ambulance', 'amplifier', 'anklet',\n         'antenna', 'apple', 'applesauce', 'apricot', 'apron', 'aquarium',\n         'arctic_(type_of_shoe)', 'armband', 'armchair', 'armoire', 'armor',\n         'artichoke', 'trash_can', 'ashtray', 'asparagus', 'atomizer',\n         'avocado', 'award', 'awning', 'ax', 'baboon', 'baby_buggy',\n         'basketball_backboard', 'backpack', 'handbag', 'suitcase', 'bagel',\n         'bagpipe', 'baguet', 'bait', 'ball', 'ballet_skirt', 'balloon',\n         'bamboo', 'banana', 'Band_Aid', 'bandage', 'bandanna', 'banjo',\n         'banner', 'barbell', 'barge', 'barrel', 'barrette', 'barrow',\n         'baseball_base', 'baseball', 'baseball_bat', 'baseball_cap',\n         'baseball_glove', 'basket', 'basketball', 'bass_horn', 'bat_(animal)',\n         'bath_mat', 'bath_towel', 'bathrobe', 'bathtub', 'batter_(food)',\n         'battery', 'beachball', 'bead', 'bean_curd', 'beanbag', 'beanie',\n         'bear', 'bed', 'bedpan', 'bedspread', 'cow', 'beef_(food)', 'beeper',\n         'beer_bottle', 'beer_can', 'beetle', 'bell', 'bell_pepper', 'belt',\n         'belt_buckle', 'bench', 'beret', 'bib', 'Bible', 'bicycle', 'visor',\n         'billboard', 'binder', 'binoculars', 'bird', 'birdfeeder', 'birdbath',\n         'birdcage', 'birdhouse', 'birthday_cake', 'birthday_card',\n         'pirate_flag', 'black_sheep', 'blackberry', 'blackboard', 'blanket',\n         'blazer', 'blender', 'blimp', 'blinker', 'blouse', 'blueberry',\n         'gameboard', 'boat', 'bob', 'bobbin', 'bobby_pin', 'boiled_egg',\n         'bolo_tie', 'deadbolt', 'bolt', 'bonnet', 'book', 'bookcase',\n         'booklet', 'bookmark', 'boom_microphone', 'boot', 'bottle',\n         'bottle_opener', 'bouquet', 'bow_(weapon)',\n         'bow_(decorative_ribbons)', 'bow-tie', 'bowl', 'pipe_bowl',\n         'bowler_hat', 'bowling_ball', 'box', 'boxing_glove', 'suspenders',\n         'bracelet', 'brass_plaque', 'brassiere', 'bread-bin', 'bread',\n         'breechcloth', 'bridal_gown', 'briefcase', 'broccoli', 'broach',\n         'broom', 'brownie', 'brussels_sprouts', 'bubble_gum', 'bucket',\n         'horse_buggy', 'bull', 'bulldog', 'bulldozer', 'bullet_train',\n         'bulletin_board', 'bulletproof_vest', 'bullhorn', 'bun', 'bunk_bed',\n         'buoy', 'burrito', 'bus_(vehicle)', 'business_card', 'butter',\n         'butterfly', 'button', 'cab_(taxi)', 'cabana', 'cabin_car', 'cabinet',\n         'locker', 'cake', 'calculator', 'calendar', 'calf', 'camcorder',\n         'camel', 'camera', 'camera_lens', 'camper_(vehicle)', 'can',\n         'can_opener', 'candle', 'candle_holder', 'candy_bar', 'candy_cane',\n         'walking_cane', 'canister', 'canoe', 'cantaloup', 'canteen',\n         'cap_(headwear)', 'bottle_cap', 'cape', 'cappuccino',\n         'car_(automobile)', 'railcar_(part_of_a_train)', 'elevator_car',\n         'car_battery', 'identity_card', 'card', 'cardigan', 'cargo_ship',\n         'carnation', 'horse_carriage', 'carrot', 'tote_bag', 'cart', 'carton',\n         'cash_register', 'casserole', 'cassette', 'cast', 'cat',\n         'cauliflower', 'cayenne_(spice)', 'CD_player', 'celery',\n         'cellular_telephone', 'chain_mail', 'chair', 'chaise_longue',\n         'chalice', 'chandelier', 'chap', 'checkbook', 'checkerboard',\n         'cherry', 'chessboard', 'chicken_(animal)', 'chickpea',\n         'chili_(vegetable)', 'chime', 'chinaware', 'crisp_(potato_chip)',\n         'poker_chip', 'chocolate_bar', 'chocolate_cake', 'chocolate_milk',\n         'chocolate_mousse', 'choker', 'chopping_board', 'chopstick',\n         'Christmas_tree', 'slide', 'cider', 'cigar_box', 'cigarette',\n         'cigarette_case', 'cistern', 'clarinet', 'clasp', 'cleansing_agent',\n         'cleat_(for_securing_rope)', 'clementine', 'clip', 'clipboard',\n         'clippers_(for_plants)', 'cloak', 'clock', 'clock_tower',\n         'clothes_hamper', 'clothespin', 'clutch_bag', 'coaster', 'coat',\n         'coat_hanger', 'coatrack', 'cock', 'cockroach', 'cocoa_(beverage)',\n         'coconut', 'coffee_maker', 'coffee_table', 'coffeepot', 'coil',\n         'coin', 'colander', 'coleslaw', 'coloring_material',\n         'combination_lock', 'pacifier', 'comic_book', 'compass',\n         'computer_keyboard', 'condiment', 'cone', 'control',\n         'convertible_(automobile)', 'sofa_bed', 'cooker', 'cookie',\n         'cooking_utensil', 'cooler_(for_food)', 'cork_(bottle_plug)',\n         'corkboard', 'corkscrew', 'edible_corn', 'cornbread', 'cornet',\n         'cornice', 'cornmeal', 'corset', 'costume', 'cougar', 'coverall',\n         'cowbell', 'cowboy_hat', 'crab_(animal)', 'crabmeat', 'cracker',\n         'crape', 'crate', 'crayon', 'cream_pitcher', 'crescent_roll', 'crib',\n         'crock_pot', 'crossbar', 'crouton', 'crow', 'crowbar', 'crown',\n         'crucifix', 'cruise_ship', 'police_cruiser', 'crumb', 'crutch',\n         'cub_(animal)', 'cube', 'cucumber', 'cufflink', 'cup', 'trophy_cup',\n         'cupboard', 'cupcake', 'hair_curler', 'curling_iron', 'curtain',\n         'cushion', 'cylinder', 'cymbal', 'dagger', 'dalmatian', 'dartboard',\n         'date_(fruit)', 'deck_chair', 'deer', 'dental_floss', 'desk',\n         'detergent', 'diaper', 'diary', 'die', 'dinghy', 'dining_table',\n         'tux', 'dish', 'dish_antenna', 'dishrag', 'dishtowel', 'dishwasher',\n         'dishwasher_detergent', 'dispenser', 'diving_board', 'Dixie_cup',\n         'dog', 'dog_collar', 'doll', 'dollar', 'dollhouse', 'dolphin',\n         'domestic_ass', 'doorknob', 'doormat', 'doughnut', 'dove',\n         'dragonfly', 'drawer', 'underdrawers', 'dress', 'dress_hat',\n         'dress_suit', 'dresser', 'drill', 'drone', 'dropper',\n         'drum_(musical_instrument)', 'drumstick', 'duck', 'duckling',\n         'duct_tape', 'duffel_bag', 'dumbbell', 'dumpster', 'dustpan', 'eagle',\n         'earphone', 'earplug', 'earring', 'easel', 'eclair', 'eel', 'egg',\n         'egg_roll', 'egg_yolk', 'eggbeater', 'eggplant', 'electric_chair',\n         'refrigerator', 'elephant', 'elk', 'envelope', 'eraser', 'escargot',\n         'eyepatch', 'falcon', 'fan', 'faucet', 'fedora', 'ferret',\n         'Ferris_wheel', 'ferry', 'fig_(fruit)', 'fighter_jet', 'figurine',\n         'file_cabinet', 'file_(tool)', 'fire_alarm', 'fire_engine',\n         'fire_extinguisher', 'fire_hose', 'fireplace', 'fireplug',\n         'first-aid_kit', 'fish', 'fish_(food)', 'fishbowl', 'fishing_rod',\n         'flag', 'flagpole', 'flamingo', 'flannel', 'flap', 'flash',\n         'flashlight', 'fleece', 'flip-flop_(sandal)', 'flipper_(footwear)',\n         'flower_arrangement', 'flute_glass', 'foal', 'folding_chair',\n         'food_processor', 'football_(American)', 'football_helmet',\n         'footstool', 'fork', 'forklift', 'freight_car', 'French_toast',\n         'freshener', 'frisbee', 'frog', 'fruit_juice', 'frying_pan', 'fudge',\n         'funnel', 'futon', 'gag', 'garbage', 'garbage_truck', 'garden_hose',\n         'gargle', 'gargoyle', 'garlic', 'gasmask', 'gazelle', 'gelatin',\n         'gemstone', 'generator', 'giant_panda', 'gift_wrap', 'ginger',\n         'giraffe', 'cincture', 'glass_(drink_container)', 'globe', 'glove',\n         'goat', 'goggles', 'goldfish', 'golf_club', 'golfcart',\n         'gondola_(boat)', 'goose', 'gorilla', 'gourd', 'grape', 'grater',\n         'gravestone', 'gravy_boat', 'green_bean', 'green_onion', 'griddle',\n         'grill', 'grits', 'grizzly', 'grocery_bag', 'guitar', 'gull', 'gun',\n         'hairbrush', 'hairnet', 'hairpin', 'halter_top', 'ham', 'hamburger',\n         'hammer', 'hammock', 'hamper', 'hamster', 'hair_dryer', 'hand_glass',\n         'hand_towel', 'handcart', 'handcuff', 'handkerchief', 'handle',\n         'handsaw', 'hardback_book', 'harmonium', 'hat', 'hatbox', 'veil',\n         'headband', 'headboard', 'headlight', 'headscarf', 'headset',\n         'headstall_(for_horses)', 'heart', 'heater', 'helicopter', 'helmet',\n         'heron', 'highchair', 'hinge', 'hippopotamus', 'hockey_stick', 'hog',\n         'home_plate_(baseball)', 'honey', 'fume_hood', 'hook', 'hookah',\n         'hornet', 'horse', 'hose', 'hot-air_balloon', 'hotplate', 'hot_sauce',\n         'hourglass', 'houseboat', 'hummingbird', 'hummus', 'polar_bear',\n         'icecream', 'popsicle', 'ice_maker', 'ice_pack', 'ice_skate',\n         'igniter', 'inhaler', 'iPod', 'iron_(for_clothing)', 'ironing_board',\n         'jacket', 'jam', 'jar', 'jean', 'jeep', 'jelly_bean', 'jersey',\n         'jet_plane', 'jewel', 'jewelry', 'joystick', 'jumpsuit', 'kayak',\n         'keg', 'kennel', 'kettle', 'key', 'keycard', 'kilt', 'kimono',\n         'kitchen_sink', 'kitchen_table', 'kite', 'kitten', 'kiwi_fruit',\n         'knee_pad', 'knife', 'knitting_needle', 'knob', 'knocker_(on_a_door)',\n         'koala', 'lab_coat', 'ladder', 'ladle', 'ladybug', 'lamb_(animal)',\n         'lamb-chop', 'lamp', 'lamppost', 'lampshade', 'lantern', 'lanyard',\n         'laptop_computer', 'lasagna', 'latch', 'lawn_mower', 'leather',\n         'legging_(clothing)', 'Lego', 'legume', 'lemon', 'lemonade',\n         'lettuce', 'license_plate', 'life_buoy', 'life_jacket', 'lightbulb',\n         'lightning_rod', 'lime', 'limousine', 'lion', 'lip_balm', 'liquor',\n         'lizard', 'log', 'lollipop', 'speaker_(stereo_equipment)', 'loveseat',\n         'machine_gun', 'magazine', 'magnet', 'mail_slot', 'mailbox_(at_home)',\n         'mallard', 'mallet', 'mammoth', 'manatee', 'mandarin_orange',\n         'manger', 'manhole', 'map', 'marker', 'martini', 'mascot',\n         'mashed_potato', 'masher', 'mask', 'mast', 'mat_(gym_equipment)',\n         'matchbox', 'mattress', 'measuring_cup', 'measuring_stick',\n         'meatball', 'medicine', 'melon', 'microphone', 'microscope',\n         'microwave_oven', 'milestone', 'milk', 'milk_can', 'milkshake',\n         'minivan', 'mint_candy', 'mirror', 'mitten', 'mixer_(kitchen_tool)',\n         'money', 'monitor_(computer_equipment) computer_monitor', 'monkey',\n         'motor', 'motor_scooter', 'motor_vehicle', 'motorcycle',\n         'mound_(baseball)', 'mouse_(computer_equipment)', 'mousepad',\n         'muffin', 'mug', 'mushroom', 'music_stool', 'musical_instrument',\n         'nailfile', 'napkin', 'neckerchief', 'necklace', 'necktie', 'needle',\n         'nest', 'newspaper', 'newsstand', 'nightshirt',\n         'nosebag_(for_animals)', 'noseband_(for_animals)', 'notebook',\n         'notepad', 'nut', 'nutcracker', 'oar', 'octopus_(food)',\n         'octopus_(animal)', 'oil_lamp', 'olive_oil', 'omelet', 'onion',\n         'orange_(fruit)', 'orange_juice', 'ostrich', 'ottoman', 'oven',\n         'overalls_(clothing)', 'owl', 'packet', 'inkpad', 'pad', 'paddle',\n         'padlock', 'paintbrush', 'painting', 'pajamas', 'palette',\n         'pan_(for_cooking)', 'pan_(metal_container)', 'pancake', 'pantyhose',\n         'papaya', 'paper_plate', 'paper_towel', 'paperback_book',\n         'paperweight', 'parachute', 'parakeet', 'parasail_(sports)',\n         'parasol', 'parchment', 'parka', 'parking_meter', 'parrot',\n         'passenger_car_(part_of_a_train)', 'passenger_ship', 'passport',\n         'pastry', 'patty_(food)', 'pea_(food)', 'peach', 'peanut_butter',\n         'pear', 'peeler_(tool_for_fruit_and_vegetables)', 'wooden_leg',\n         'pegboard', 'pelican', 'pen', 'pencil', 'pencil_box',\n         'pencil_sharpener', 'pendulum', 'penguin', 'pennant', 'penny_(coin)',\n         'pepper', 'pepper_mill', 'perfume', 'persimmon', 'person', 'pet',\n         'pew_(church_bench)', 'phonebook', 'phonograph_record', 'piano',\n         'pickle', 'pickup_truck', 'pie', 'pigeon', 'piggy_bank', 'pillow',\n         'pin_(non_jewelry)', 'pineapple', 'pinecone', 'ping-pong_ball',\n         'pinwheel', 'tobacco_pipe', 'pipe', 'pistol', 'pita_(bread)',\n         'pitcher_(vessel_for_liquid)', 'pitchfork', 'pizza', 'place_mat',\n         'plate', 'platter', 'playpen', 'pliers', 'plow_(farm_equipment)',\n         'plume', 'pocket_watch', 'pocketknife', 'poker_(fire_stirring_tool)',\n         'pole', 'polo_shirt', 'poncho', 'pony', 'pool_table', 'pop_(soda)',\n         'postbox_(public)', 'postcard', 'poster', 'pot', 'flowerpot',\n         'potato', 'potholder', 'pottery', 'pouch', 'power_shovel', 'prawn',\n         'pretzel', 'printer', 'projectile_(weapon)', 'projector', 'propeller',\n         'prune', 'pudding', 'puffer_(fish)', 'puffin', 'pug-dog', 'pumpkin',\n         'puncher', 'puppet', 'puppy', 'quesadilla', 'quiche', 'quilt',\n         'rabbit', 'race_car', 'racket', 'radar', 'radiator', 'radio_receiver',\n         'radish', 'raft', 'rag_doll', 'raincoat', 'ram_(animal)', 'raspberry',\n         'rat', 'razorblade', 'reamer_(juicer)', 'rearview_mirror', 'receipt',\n         'recliner', 'record_player', 'reflector', 'remote_control',\n         'rhinoceros', 'rib_(food)', 'rifle', 'ring', 'river_boat', 'road_map',\n         'robe', 'rocking_chair', 'rodent', 'roller_skate', 'Rollerblade',\n         'rolling_pin', 'root_beer', 'router_(computer_equipment)',\n         'rubber_band', 'runner_(carpet)', 'plastic_bag',\n         'saddle_(on_an_animal)', 'saddle_blanket', 'saddlebag', 'safety_pin',\n         'sail', 'salad', 'salad_plate', 'salami', 'salmon_(fish)',\n         'salmon_(food)', 'salsa', 'saltshaker', 'sandal_(type_of_shoe)',\n         'sandwich', 'satchel', 'saucepan', 'saucer', 'sausage', 'sawhorse',\n         'saxophone', 'scale_(measuring_instrument)', 'scarecrow', 'scarf',\n         'school_bus', 'scissors', 'scoreboard', 'scraper', 'screwdriver',\n         'scrubbing_brush', 'sculpture', 'seabird', 'seahorse', 'seaplane',\n         'seashell', 'sewing_machine', 'shaker', 'shampoo', 'shark',\n         'sharpener', 'Sharpie', 'shaver_(electric)', 'shaving_cream', 'shawl',\n         'shears', 'sheep', 'shepherd_dog', 'sherbert', 'shield', 'shirt',\n         'shoe', 'shopping_bag', 'shopping_cart', 'short_pants', 'shot_glass',\n         'shoulder_bag', 'shovel', 'shower_head', 'shower_cap',\n         'shower_curtain', 'shredder_(for_paper)', 'signboard', 'silo', 'sink',\n         'skateboard', 'skewer', 'ski', 'ski_boot', 'ski_parka', 'ski_pole',\n         'skirt', 'skullcap', 'sled', 'sleeping_bag', 'sling_(bandage)',\n         'slipper_(footwear)', 'smoothie', 'snake', 'snowboard', 'snowman',\n         'snowmobile', 'soap', 'soccer_ball', 'sock', 'sofa', 'softball',\n         'solar_array', 'sombrero', 'soup', 'soup_bowl', 'soupspoon',\n         'sour_cream', 'soya_milk', 'space_shuttle', 'sparkler_(fireworks)',\n         'spatula', 'spear', 'spectacles', 'spice_rack', 'spider', 'crawfish',\n         'sponge', 'spoon', 'sportswear', 'spotlight', 'squid_(food)',\n         'squirrel', 'stagecoach', 'stapler_(stapling_machine)', 'starfish',\n         'statue_(sculpture)', 'steak_(food)', 'steak_knife', 'steering_wheel',\n         'stepladder', 'step_stool', 'stereo_(sound_system)', 'stew',\n         'stirrer', 'stirrup', 'stool', 'stop_sign', 'brake_light', 'stove',\n         'strainer', 'strap', 'straw_(for_drinking)', 'strawberry',\n         'street_sign', 'streetlight', 'string_cheese', 'stylus', 'subwoofer',\n         'sugar_bowl', 'sugarcane_(plant)', 'suit_(clothing)', 'sunflower',\n         'sunglasses', 'sunhat', 'surfboard', 'sushi', 'mop', 'sweat_pants',\n         'sweatband', 'sweater', 'sweatshirt', 'sweet_potato', 'swimsuit',\n         'sword', 'syringe', 'Tabasco_sauce', 'table-tennis_table', 'table',\n         'table_lamp', 'tablecloth', 'tachometer', 'taco', 'tag', 'taillight',\n         'tambourine', 'army_tank', 'tank_(storage_vessel)',\n         'tank_top_(clothing)', 'tape_(sticky_cloth_or_paper)', 'tape_measure',\n         'tapestry', 'tarp', 'tartan', 'tassel', 'tea_bag', 'teacup',\n         'teakettle', 'teapot', 'teddy_bear', 'telephone', 'telephone_booth',\n         'telephone_pole', 'telephoto_lens', 'television_camera',\n         'television_set', 'tennis_ball', 'tennis_racket', 'tequila',\n         'thermometer', 'thermos_bottle', 'thermostat', 'thimble', 'thread',\n         'thumbtack', 'tiara', 'tiger', 'tights_(clothing)', 'timer',\n         'tinfoil', 'tinsel', 'tissue_paper', 'toast_(food)', 'toaster',\n         'toaster_oven', 'toilet', 'toilet_tissue', 'tomato', 'tongs',\n         'toolbox', 'toothbrush', 'toothpaste', 'toothpick', 'cover',\n         'tortilla', 'tow_truck', 'towel', 'towel_rack', 'toy',\n         'tractor_(farm_equipment)', 'traffic_light', 'dirt_bike',\n         'trailer_truck', 'train_(railroad_vehicle)', 'trampoline', 'tray',\n         'trench_coat', 'triangle_(musical_instrument)', 'tricycle', 'tripod',\n         'trousers', 'truck', 'truffle_(chocolate)', 'trunk', 'vat', 'turban',\n         'turkey_(food)', 'turnip', 'turtle', 'turtleneck_(clothing)',\n         'typewriter', 'umbrella', 'underwear', 'unicycle', 'urinal', 'urn',\n         'vacuum_cleaner', 'vase', 'vending_machine', 'vent', 'vest',\n         'videotape', 'vinegar', 'violin', 'vodka', 'volleyball', 'vulture',\n         'waffle', 'waffle_iron', 'wagon', 'wagon_wheel', 'walking_stick',\n         'wall_clock', 'wall_socket', 'wallet', 'walrus', 'wardrobe',\n         'washbasin', 'automatic_washer', 'watch', 'water_bottle',\n         'water_cooler', 'water_faucet', 'water_heater', 'water_jug',\n         'water_gun', 'water_scooter', 'water_ski', 'water_tower',\n         'watering_can', 'watermelon', 'weathervane', 'webcam', 'wedding_cake',\n         'wedding_ring', 'wet_suit', 'wheel', 'wheelchair', 'whipped_cream',\n         'whistle', 'wig', 'wind_chime', 'windmill', 'window_box_(for_plants)',\n         'windshield_wiper', 'windsock', 'wine_bottle', 'wine_bucket',\n         'wineglass', 'blinder_(for_horses)', 'wok', 'wolf', 'wooden_spoon',\n         'wreath', 'wrench', 'wristband', 'wristlet', 'yacht', 'yogurt',\n         'yoke_(animal_equipment)', 'zebra', 'zucchini'),\n        'palette':\n        None\n    }\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        try:\n            import lvis\n            if getattr(lvis, '__version__', '0') >= '10.5.3':\n                warnings.warn(\n                    'mmlvis is deprecated, please install official lvis-api by \"pip install git+https://github.com/lvis-dataset/lvis-api.git\"',  # noqa: E501\n                    UserWarning)\n            from lvis import LVIS\n        except ImportError:\n            raise ImportError(\n                'Package lvis is not installed. Please run \"pip install git+https://github.com/lvis-dataset/lvis-api.git\".'  # noqa: E501\n            )\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.lvis = LVIS(local_path)\n        self.cat_ids = self.lvis.get_cat_ids()\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.lvis.cat_img_map)\n\n        img_ids = self.lvis.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.lvis.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n            # coco_url is used in LVISv1 instead of file_name\n            # e.g. http://images.cocodataset.org/train2017/000000391895.jpg\n            # train/val split in specified in url\n            raw_img_info['file_name'] = raw_img_info['coco_url'].replace(\n                'http://images.cocodataset.org/', '')\n            ann_ids = self.lvis.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = self.lvis.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n            parsed_data_info = self.parse_data_info({\n                'raw_ann_info':\n                raw_ann_info,\n                'raw_img_info':\n                raw_img_info\n            })\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.lvis\n\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/mdetr_style_refcoco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import List\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.datasets import BaseDetDataset\nfrom mmdet.registry import DATASETS\nfrom .api_wrappers import COCO\n\n\n@DATASETS.register_module()\nclass MDETRStyleRefCocoDataset(BaseDetDataset):\n    \"\"\"RefCOCO dataset.\n\n    Only support evaluation now.\n    \"\"\"\n\n    def load_data_list(self) -> List[dict]:\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            coco = COCO(local_path)\n\n        img_ids = coco.get_img_ids()\n\n        data_infos = []\n        for img_id in img_ids:\n            raw_img_info = coco.load_imgs([img_id])[0]\n            ann_ids = coco.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = coco.load_anns(ann_ids)\n\n            data_info = {}\n            img_path = osp.join(self.data_prefix['img'],\n                                raw_img_info['file_name'])\n            data_info['img_path'] = img_path\n            data_info['img_id'] = img_id\n            data_info['height'] = raw_img_info['height']\n            data_info['width'] = raw_img_info['width']\n            data_info['dataset_mode'] = raw_img_info['dataset_name']\n\n            data_info['text'] = raw_img_info['caption']\n            data_info['custom_entities'] = False\n            data_info['tokens_positive'] = -1\n\n            instances = []\n            for i, ann in enumerate(raw_ann_info):\n                instance = {}\n                x1, y1, w, h = ann['bbox']\n                bbox = [x1, y1, x1 + w, y1 + h]\n                instance['bbox'] = bbox\n                instance['bbox_label'] = ann['category_id']\n                instance['ignore_flag'] = 0\n                instances.append(instance)\n\n            data_info['instances'] = instances\n            data_infos.append(data_info)\n        return data_infos\n"
  },
  {
    "path": "mmdet/datasets/mot_challenge_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import List, Union\n\nfrom mmdet.registry import DATASETS\nfrom .base_video_dataset import BaseVideoDataset\n\n\n@DATASETS.register_module()\nclass MOTChallengeDataset(BaseVideoDataset):\n    \"\"\"Dataset for MOTChallenge.\n\n    Args:\n        visibility_thr (float, optional): The minimum visibility\n            for the objects during training. Default to -1.\n    \"\"\"\n\n    METAINFO = {\n        'classes':\n        ('pedestrian', 'person_on_vehicle', 'car', 'bicycle', 'motorbike',\n         'non_mot_vehicle', 'static_person', 'distractor', 'occluder',\n         'occluder_on_ground', 'occluder_full', 'reflection', 'crowd')\n    }\n\n    def __init__(self, visibility_thr: float = -1, *args, **kwargs):\n        self.visibility_thr = visibility_thr\n        super().__init__(*args, **kwargs)\n\n    def parse_data_info(self, raw_data_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format. The difference between this\n        function and the one in ``BaseVideoDataset`` is that the parsing here\n        adds ``visibility`` and ``mot_conf``.\n\n        Args:\n            raw_data_info (dict): Raw data information load from ``ann_file``\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        img_info = raw_data_info['raw_img_info']\n        ann_info = raw_data_info['raw_ann_info']\n        data_info = {}\n\n        data_info.update(img_info)\n        if self.data_prefix.get('img_path', None) is not None:\n            img_path = osp.join(self.data_prefix['img_path'],\n                                img_info['file_name'])\n        else:\n            img_path = img_info['file_name']\n        data_info['img_path'] = img_path\n\n        instances = []\n        for i, ann in enumerate(ann_info):\n            instance = {}\n\n            if (not self.test_mode) and (ann['visibility'] <\n                                         self.visibility_thr):\n                continue\n            if ann.get('ignore', False):\n                continue\n            x1, y1, w, h = ann['bbox']\n            inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                continue\n            if ann['category_id'] not in self.cat_ids:\n                continue\n            bbox = [x1, y1, x1 + w, y1 + h]\n\n            if ann.get('iscrowd', False):\n                instance['ignore_flag'] = 1\n            else:\n                instance['ignore_flag'] = 0\n            instance['bbox'] = bbox\n            instance['bbox_label'] = self.cat2label[ann['category_id']]\n            instance['instance_id'] = ann['instance_id']\n            instance['category_id'] = ann['category_id']\n            instance['mot_conf'] = ann['mot_conf']\n            instance['visibility'] = ann['visibility']\n            if len(instance) > 0:\n                instances.append(instance)\n        if not self.test_mode:\n            assert len(instances) > 0, f'No valid instances found in ' \\\n                f'image {data_info[\"img_path\"]}!'\n        data_info['instances'] = instances\n        return data_info\n"
  },
  {
    "path": "mmdet/datasets/objects365.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nfrom typing import List\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.registry import DATASETS\nfrom .api_wrappers import COCO\nfrom .coco import CocoDataset\n\n# images exist in annotations but not in image folder.\nobjv2_ignore_list = [\n    osp.join('patch16', 'objects365_v2_00908726.jpg'),\n    osp.join('patch6', 'objects365_v1_00320532.jpg'),\n    osp.join('patch6', 'objects365_v1_00320534.jpg'),\n]\n\n\n@DATASETS.register_module()\nclass Objects365V1Dataset(CocoDataset):\n    \"\"\"Objects365 v1 dataset for detection.\"\"\"\n\n    METAINFO = {\n        'classes':\n        ('person', 'sneakers', 'chair', 'hat', 'lamp', 'bottle',\n         'cabinet/shelf', 'cup', 'car', 'glasses', 'picture/frame', 'desk',\n         'handbag', 'street lights', 'book', 'plate', 'helmet',\n         'leather shoes', 'pillow', 'glove', 'potted plant', 'bracelet',\n         'flower', 'tv', 'storage box', 'vase', 'bench', 'wine glass', 'boots',\n         'bowl', 'dining table', 'umbrella', 'boat', 'flag', 'speaker',\n         'trash bin/can', 'stool', 'backpack', 'couch', 'belt', 'carpet',\n         'basket', 'towel/napkin', 'slippers', 'barrel/bucket', 'coffee table',\n         'suv', 'toy', 'tie', 'bed', 'traffic light', 'pen/pencil',\n         'microphone', 'sandals', 'canned', 'necklace', 'mirror', 'faucet',\n         'bicycle', 'bread', 'high heels', 'ring', 'van', 'watch', 'sink',\n         'horse', 'fish', 'apple', 'camera', 'candle', 'teddy bear', 'cake',\n         'motorcycle', 'wild bird', 'laptop', 'knife', 'traffic sign',\n         'cell phone', 'paddle', 'truck', 'cow', 'power outlet', 'clock',\n         'drum', 'fork', 'bus', 'hanger', 'nightstand', 'pot/pan', 'sheep',\n         'guitar', 'traffic cone', 'tea pot', 'keyboard', 'tripod', 'hockey',\n         'fan', 'dog', 'spoon', 'blackboard/whiteboard', 'balloon',\n         'air conditioner', 'cymbal', 'mouse', 'telephone', 'pickup truck',\n         'orange', 'banana', 'airplane', 'luggage', 'skis', 'soccer',\n         'trolley', 'oven', 'remote', 'baseball glove', 'paper towel',\n         'refrigerator', 'train', 'tomato', 'machinery vehicle', 'tent',\n         'shampoo/shower gel', 'head phone', 'lantern', 'donut',\n         'cleaning products', 'sailboat', 'tangerine', 'pizza', 'kite',\n         'computer box', 'elephant', 'toiletries', 'gas stove', 'broccoli',\n         'toilet', 'stroller', 'shovel', 'baseball bat', 'microwave',\n         'skateboard', 'surfboard', 'surveillance camera', 'gun', 'life saver',\n         'cat', 'lemon', 'liquid soap', 'zebra', 'duck', 'sports car',\n         'giraffe', 'pumpkin', 'piano', 'stop sign', 'radiator', 'converter',\n         'tissue ', 'carrot', 'washing machine', 'vent', 'cookies',\n         'cutting/chopping board', 'tennis racket', 'candy',\n         'skating and skiing shoes', 'scissors', 'folder', 'baseball',\n         'strawberry', 'bow tie', 'pigeon', 'pepper', 'coffee machine',\n         'bathtub', 'snowboard', 'suitcase', 'grapes', 'ladder', 'pear',\n         'american football', 'basketball', 'potato', 'paint brush', 'printer',\n         'billiards', 'fire hydrant', 'goose', 'projector', 'sausage',\n         'fire extinguisher', 'extension cord', 'facial mask', 'tennis ball',\n         'chopsticks', 'electronic stove and gas stove', 'pie', 'frisbee',\n         'kettle', 'hamburger', 'golf club', 'cucumber', 'clutch', 'blender',\n         'tong', 'slide', 'hot dog', 'toothbrush', 'facial cleanser', 'mango',\n         'deer', 'egg', 'violin', 'marker', 'ship', 'chicken', 'onion',\n         'ice cream', 'tape', 'wheelchair', 'plum', 'bar soap', 'scale',\n         'watermelon', 'cabbage', 'router/modem', 'golf ball', 'pine apple',\n         'crane', 'fire truck', 'peach', 'cello', 'notepaper', 'tricycle',\n         'toaster', 'helicopter', 'green beans', 'brush', 'carriage', 'cigar',\n         'earphone', 'penguin', 'hurdle', 'swing', 'radio', 'CD',\n         'parking meter', 'swan', 'garlic', 'french fries', 'horn', 'avocado',\n         'saxophone', 'trumpet', 'sandwich', 'cue', 'kiwi fruit', 'bear',\n         'fishing rod', 'cherry', 'tablet', 'green vegetables', 'nuts', 'corn',\n         'key', 'screwdriver', 'globe', 'broom', 'pliers', 'volleyball',\n         'hammer', 'eggplant', 'trophy', 'dates', 'board eraser', 'rice',\n         'tape measure/ruler', 'dumbbell', 'hamimelon', 'stapler', 'camel',\n         'lettuce', 'goldfish', 'meat balls', 'medal', 'toothpaste',\n         'antelope', 'shrimp', 'rickshaw', 'trombone', 'pomegranate',\n         'coconut', 'jellyfish', 'mushroom', 'calculator', 'treadmill',\n         'butterfly', 'egg tart', 'cheese', 'pig', 'pomelo', 'race car',\n         'rice cooker', 'tuba', 'crosswalk sign', 'papaya', 'hair drier',\n         'green onion', 'chips', 'dolphin', 'sushi', 'urinal', 'donkey',\n         'electric drill', 'spring rolls', 'tortoise/turtle', 'parrot',\n         'flute', 'measuring cup', 'shark', 'steak', 'poker card',\n         'binoculars', 'llama', 'radish', 'noodles', 'yak', 'mop', 'crab',\n         'microscope', 'barbell', 'bread/bun', 'baozi', 'lion', 'red cabbage',\n         'polar bear', 'lighter', 'seal', 'mangosteen', 'comb', 'eraser',\n         'pitaya', 'scallop', 'pencil case', 'saw', 'table tennis paddle',\n         'okra', 'starfish', 'eagle', 'monkey', 'durian', 'game board',\n         'rabbit', 'french horn', 'ambulance', 'asparagus', 'hoverboard',\n         'pasta', 'target', 'hotair balloon', 'chainsaw', 'lobster', 'iron',\n         'flashlight'),\n        'palette':\n        None\n    }\n\n    COCOAPI = COCO\n    # ann_id is unique in coco dataset.\n    ANN_ID_UNIQUE = True\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.coco = self.COCOAPI(local_path)\n\n        # 'categories' list in objects365_train.json and objects365_val.json\n        # is inconsistent, need sort list(or dict) before get cat_ids.\n        cats = self.coco.cats\n        sorted_cats = {i: cats[i] for i in sorted(cats)}\n        self.coco.cats = sorted_cats\n        categories = self.coco.dataset['categories']\n        sorted_categories = sorted(categories, key=lambda i: i['id'])\n        self.coco.dataset['categories'] = sorted_categories\n        # The order of returned `cat_ids` will not\n        # change with the order of the `classes`\n        self.cat_ids = self.coco.get_cat_ids(\n            cat_names=self.metainfo['classes'])\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.coco.cat_img_map)\n\n        img_ids = self.coco.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.coco.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n\n            ann_ids = self.coco.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = self.coco.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n\n            parsed_data_info = self.parse_data_info({\n                'raw_ann_info':\n                raw_ann_info,\n                'raw_img_info':\n                raw_img_info\n            })\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.coco\n\n        return data_list\n\n\n@DATASETS.register_module()\nclass Objects365V2Dataset(CocoDataset):\n    \"\"\"Objects365 v2 dataset for detection.\"\"\"\n    METAINFO = {\n        'classes':\n        ('Person', 'Sneakers', 'Chair', 'Other Shoes', 'Hat', 'Car', 'Lamp',\n         'Glasses', 'Bottle', 'Desk', 'Cup', 'Street Lights', 'Cabinet/shelf',\n         'Handbag/Satchel', 'Bracelet', 'Plate', 'Picture/Frame', 'Helmet',\n         'Book', 'Gloves', 'Storage box', 'Boat', 'Leather Shoes', 'Flower',\n         'Bench', 'Potted Plant', 'Bowl/Basin', 'Flag', 'Pillow', 'Boots',\n         'Vase', 'Microphone', 'Necklace', 'Ring', 'SUV', 'Wine Glass', 'Belt',\n         'Moniter/TV', 'Backpack', 'Umbrella', 'Traffic Light', 'Speaker',\n         'Watch', 'Tie', 'Trash bin Can', 'Slippers', 'Bicycle', 'Stool',\n         'Barrel/bucket', 'Van', 'Couch', 'Sandals', 'Bakset', 'Drum',\n         'Pen/Pencil', 'Bus', 'Wild Bird', 'High Heels', 'Motorcycle',\n         'Guitar', 'Carpet', 'Cell Phone', 'Bread', 'Camera', 'Canned',\n         'Truck', 'Traffic cone', 'Cymbal', 'Lifesaver', 'Towel',\n         'Stuffed Toy', 'Candle', 'Sailboat', 'Laptop', 'Awning', 'Bed',\n         'Faucet', 'Tent', 'Horse', 'Mirror', 'Power outlet', 'Sink', 'Apple',\n         'Air Conditioner', 'Knife', 'Hockey Stick', 'Paddle', 'Pickup Truck',\n         'Fork', 'Traffic Sign', 'Ballon', 'Tripod', 'Dog', 'Spoon', 'Clock',\n         'Pot', 'Cow', 'Cake', 'Dinning Table', 'Sheep', 'Hanger',\n         'Blackboard/Whiteboard', 'Napkin', 'Other Fish', 'Orange/Tangerine',\n         'Toiletry', 'Keyboard', 'Tomato', 'Lantern', 'Machinery Vehicle',\n         'Fan', 'Green Vegetables', 'Banana', 'Baseball Glove', 'Airplane',\n         'Mouse', 'Train', 'Pumpkin', 'Soccer', 'Skiboard', 'Luggage',\n         'Nightstand', 'Tea pot', 'Telephone', 'Trolley', 'Head Phone',\n         'Sports Car', 'Stop Sign', 'Dessert', 'Scooter', 'Stroller', 'Crane',\n         'Remote', 'Refrigerator', 'Oven', 'Lemon', 'Duck', 'Baseball Bat',\n         'Surveillance Camera', 'Cat', 'Jug', 'Broccoli', 'Piano', 'Pizza',\n         'Elephant', 'Skateboard', 'Surfboard', 'Gun',\n         'Skating and Skiing shoes', 'Gas stove', 'Donut', 'Bow Tie', 'Carrot',\n         'Toilet', 'Kite', 'Strawberry', 'Other Balls', 'Shovel', 'Pepper',\n         'Computer Box', 'Toilet Paper', 'Cleaning Products', 'Chopsticks',\n         'Microwave', 'Pigeon', 'Baseball', 'Cutting/chopping Board',\n         'Coffee Table', 'Side Table', 'Scissors', 'Marker', 'Pie', 'Ladder',\n         'Snowboard', 'Cookies', 'Radiator', 'Fire Hydrant', 'Basketball',\n         'Zebra', 'Grape', 'Giraffe', 'Potato', 'Sausage', 'Tricycle',\n         'Violin', 'Egg', 'Fire Extinguisher', 'Candy', 'Fire Truck',\n         'Billards', 'Converter', 'Bathtub', 'Wheelchair', 'Golf Club',\n         'Briefcase', 'Cucumber', 'Cigar/Cigarette ', 'Paint Brush', 'Pear',\n         'Heavy Truck', 'Hamburger', 'Extractor', 'Extention Cord', 'Tong',\n         'Tennis Racket', 'Folder', 'American Football', 'earphone', 'Mask',\n         'Kettle', 'Tennis', 'Ship', 'Swing', 'Coffee Machine', 'Slide',\n         'Carriage', 'Onion', 'Green beans', 'Projector', 'Frisbee',\n         'Washing Machine/Drying Machine', 'Chicken', 'Printer', 'Watermelon',\n         'Saxophone', 'Tissue', 'Toothbrush', 'Ice cream', 'Hotair ballon',\n         'Cello', 'French Fries', 'Scale', 'Trophy', 'Cabbage', 'Hot dog',\n         'Blender', 'Peach', 'Rice', 'Wallet/Purse', 'Volleyball', 'Deer',\n         'Goose', 'Tape', 'Tablet', 'Cosmetics', 'Trumpet', 'Pineapple',\n         'Golf Ball', 'Ambulance', 'Parking meter', 'Mango', 'Key', 'Hurdle',\n         'Fishing Rod', 'Medal', 'Flute', 'Brush', 'Penguin', 'Megaphone',\n         'Corn', 'Lettuce', 'Garlic', 'Swan', 'Helicopter', 'Green Onion',\n         'Sandwich', 'Nuts', 'Speed Limit Sign', 'Induction Cooker', 'Broom',\n         'Trombone', 'Plum', 'Rickshaw', 'Goldfish', 'Kiwi fruit',\n         'Router/modem', 'Poker Card', 'Toaster', 'Shrimp', 'Sushi', 'Cheese',\n         'Notepaper', 'Cherry', 'Pliers', 'CD', 'Pasta', 'Hammer', 'Cue',\n         'Avocado', 'Hamimelon', 'Flask', 'Mushroon', 'Screwdriver', 'Soap',\n         'Recorder', 'Bear', 'Eggplant', 'Board Eraser', 'Coconut',\n         'Tape Measur/ Ruler', 'Pig', 'Showerhead', 'Globe', 'Chips', 'Steak',\n         'Crosswalk Sign', 'Stapler', 'Campel', 'Formula 1 ', 'Pomegranate',\n         'Dishwasher', 'Crab', 'Hoverboard', 'Meat ball', 'Rice Cooker',\n         'Tuba', 'Calculator', 'Papaya', 'Antelope', 'Parrot', 'Seal',\n         'Buttefly', 'Dumbbell', 'Donkey', 'Lion', 'Urinal', 'Dolphin',\n         'Electric Drill', 'Hair Dryer', 'Egg tart', 'Jellyfish', 'Treadmill',\n         'Lighter', 'Grapefruit', 'Game board', 'Mop', 'Radish', 'Baozi',\n         'Target', 'French', 'Spring Rolls', 'Monkey', 'Rabbit', 'Pencil Case',\n         'Yak', 'Red Cabbage', 'Binoculars', 'Asparagus', 'Barbell', 'Scallop',\n         'Noddles', 'Comb', 'Dumpling', 'Oyster', 'Table Teniis paddle',\n         'Cosmetics Brush/Eyeliner Pencil', 'Chainsaw', 'Eraser', 'Lobster',\n         'Durian', 'Okra', 'Lipstick', 'Cosmetics Mirror', 'Curling',\n         'Table Tennis '),\n        'palette':\n        None\n    }\n\n    COCOAPI = COCO\n    # ann_id is unique in coco dataset.\n    ANN_ID_UNIQUE = True\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.coco = self.COCOAPI(local_path)\n        # The order of returned `cat_ids` will not\n        # change with the order of the `classes`\n        self.cat_ids = self.coco.get_cat_ids(\n            cat_names=self.metainfo['classes'])\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.coco.cat_img_map)\n\n        img_ids = self.coco.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.coco.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n\n            ann_ids = self.coco.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = self.coco.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n\n            # file_name should be `patchX/xxx.jpg`\n            file_name = osp.join(\n                osp.split(osp.split(raw_img_info['file_name'])[0])[-1],\n                osp.split(raw_img_info['file_name'])[-1])\n\n            if file_name in objv2_ignore_list:\n                continue\n\n            raw_img_info['file_name'] = file_name\n            parsed_data_info = self.parse_data_info({\n                'raw_ann_info':\n                raw_ann_info,\n                'raw_img_info':\n                raw_img_info\n            })\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.coco\n\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/odvg.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nimport os.path as osp\nfrom typing import List, Optional\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.registry import DATASETS\nfrom .base_det_dataset import BaseDetDataset\n\n\n@DATASETS.register_module()\nclass ODVGDataset(BaseDetDataset):\n    \"\"\"object detection and visual grounding dataset.\"\"\"\n\n    def __init__(self,\n                 *args,\n                 data_root: str = '',\n                 label_map_file: Optional[str] = None,\n                 need_text: bool = True,\n                 **kwargs) -> None:\n        self.dataset_mode = 'VG'\n        self.need_text = need_text\n        if label_map_file:\n            label_map_file = osp.join(data_root, label_map_file)\n            with open(label_map_file, 'r') as file:\n                self.label_map = json.load(file)\n            self.dataset_mode = 'OD'\n        super().__init__(*args, data_root=data_root, **kwargs)\n        assert self.return_classes is True\n\n    def load_data_list(self) -> List[dict]:\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            with open(local_path, 'r') as f:\n                data_list = [json.loads(line) for line in f]\n\n        out_data_list = []\n        for data in data_list:\n            data_info = {}\n            img_path = osp.join(self.data_prefix['img'], data['filename'])\n            data_info['img_path'] = img_path\n            data_info['height'] = data['height']\n            data_info['width'] = data['width']\n            if self.dataset_mode == 'OD':\n                if self.need_text:\n                    data_info['text'] = self.label_map\n                anno = data.get('detection', {})\n                instances = [obj for obj in anno.get('instances', [])]\n                bboxes = [obj['bbox'] for obj in instances]\n                bbox_labels = [str(obj['label']) for obj in instances]\n\n                instances = []\n                for bbox, label in zip(bboxes, bbox_labels):\n                    instance = {}\n                    x1, y1, x2, y2 = bbox\n                    inter_w = max(0, min(x2, data['width']) - max(x1, 0))\n                    inter_h = max(0, min(y2, data['height']) - max(y1, 0))\n                    if inter_w * inter_h == 0:\n                        continue\n                    if (x2 - x1) < 1 or (y2 - y1) < 1:\n                        continue\n                    instance['ignore_flag'] = 0\n                    instance['bbox'] = bbox\n                    instance['bbox_label'] = int(label)\n                    instances.append(instance)\n                data_info['instances'] = instances\n                data_info['dataset_mode'] = self.dataset_mode\n                out_data_list.append(data_info)\n            else:\n                anno = data['grounding']\n                data_info['text'] = anno['caption']\n                regions = anno['regions']\n\n                instances = []\n                phrases = {}\n                for i, region in enumerate(regions):\n                    bbox = region['bbox']\n                    phrase = region['phrase']\n                    tokens_positive = region['tokens_positive']\n                    if not isinstance(bbox[0], list):\n                        bbox = [bbox]\n                    for box in bbox:\n                        instance = {}\n                        x1, y1, x2, y2 = box\n                        inter_w = max(0, min(x2, data['width']) - max(x1, 0))\n                        inter_h = max(0, min(y2, data['height']) - max(y1, 0))\n                        if inter_w * inter_h == 0:\n                            continue\n                        if (x2 - x1) < 1 or (y2 - y1) < 1:\n                            continue\n                        instance['ignore_flag'] = 0\n                        instance['bbox'] = box\n                        instance['bbox_label'] = i\n                        phrases[i] = {\n                            'phrase': phrase,\n                            'tokens_positive': tokens_positive\n                        }\n                        instances.append(instance)\n                data_info['instances'] = instances\n                data_info['phrases'] = phrases\n                data_info['dataset_mode'] = self.dataset_mode\n                out_data_list.append(data_info)\n\n        del data_list\n        return out_data_list\n"
  },
  {
    "path": "mmdet/datasets/openimages.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport csv\nimport os.path as osp\nfrom collections import defaultdict\nfrom typing import Dict, List, Optional\n\nimport numpy as np\nfrom mmengine.fileio import get_local_path, load\nfrom mmengine.utils import is_abs\n\nfrom mmdet.registry import DATASETS\nfrom .base_det_dataset import BaseDetDataset\n\n\n@DATASETS.register_module()\nclass OpenImagesDataset(BaseDetDataset):\n    \"\"\"Open Images dataset for detection.\n\n    Args:\n        ann_file (str): Annotation file path.\n        label_file (str): File path of the label description file that\n            maps the classes names in MID format to their short\n            descriptions.\n        meta_file (str): File path to get image metas.\n        hierarchy_file (str): The file path of the class hierarchy.\n        image_level_ann_file (str): Human-verified image level annotation,\n            which is used in evaluation.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n    \"\"\"\n\n    METAINFO: dict = dict(dataset_type='oid_v6')\n\n    def __init__(self,\n                 label_file: str,\n                 meta_file: str,\n                 hierarchy_file: str,\n                 image_level_ann_file: Optional[str] = None,\n                 **kwargs) -> None:\n        self.label_file = label_file\n        self.meta_file = meta_file\n        self.hierarchy_file = hierarchy_file\n        self.image_level_ann_file = image_level_ann_file\n        super().__init__(**kwargs)\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"\n        classes_names, label_id_mapping = self._parse_label_file(\n            self.label_file)\n        self._metainfo['classes'] = classes_names\n        self.label_id_mapping = label_id_mapping\n\n        if self.image_level_ann_file is not None:\n            img_level_anns = self._parse_img_level_ann(\n                self.image_level_ann_file)\n        else:\n            img_level_anns = None\n\n        # OpenImagesMetric can get the relation matrix from the dataset meta\n        relation_matrix = self._get_relation_matrix(self.hierarchy_file)\n        self._metainfo['RELATION_MATRIX'] = relation_matrix\n\n        data_list = []\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            with open(local_path, 'r') as f:\n                reader = csv.reader(f)\n                last_img_id = None\n                instances = []\n                for i, line in enumerate(reader):\n                    if i == 0:\n                        continue\n                    img_id = line[0]\n                    if last_img_id is None:\n                        last_img_id = img_id\n                    label_id = line[2]\n                    assert label_id in self.label_id_mapping\n                    label = int(self.label_id_mapping[label_id])\n                    bbox = [\n                        float(line[4]),  # xmin\n                        float(line[6]),  # ymin\n                        float(line[5]),  # xmax\n                        float(line[7])  # ymax\n                    ]\n                    is_occluded = True if int(line[8]) == 1 else False\n                    is_truncated = True if int(line[9]) == 1 else False\n                    is_group_of = True if int(line[10]) == 1 else False\n                    is_depiction = True if int(line[11]) == 1 else False\n                    is_inside = True if int(line[12]) == 1 else False\n\n                    instance = dict(\n                        bbox=bbox,\n                        bbox_label=label,\n                        ignore_flag=0,\n                        is_occluded=is_occluded,\n                        is_truncated=is_truncated,\n                        is_group_of=is_group_of,\n                        is_depiction=is_depiction,\n                        is_inside=is_inside)\n                    last_img_path = osp.join(self.data_prefix['img'],\n                                             f'{last_img_id}.jpg')\n                    if img_id != last_img_id:\n                        # switch to a new image, record previous image's data.\n                        data_info = dict(\n                            img_path=last_img_path,\n                            img_id=last_img_id,\n                            instances=instances,\n                        )\n                        data_list.append(data_info)\n                        instances = []\n                    instances.append(instance)\n                    last_img_id = img_id\n                data_list.append(\n                    dict(\n                        img_path=last_img_path,\n                        img_id=last_img_id,\n                        instances=instances,\n                    ))\n\n        # add image metas to data list\n        img_metas = load(\n            self.meta_file, file_format='pkl', backend_args=self.backend_args)\n        assert len(img_metas) == len(data_list)\n        for i, meta in enumerate(img_metas):\n            img_id = data_list[i]['img_id']\n            assert f'{img_id}.jpg' == osp.split(meta['filename'])[-1]\n            h, w = meta['ori_shape'][:2]\n            data_list[i]['height'] = h\n            data_list[i]['width'] = w\n            # denormalize bboxes\n            for j in range(len(data_list[i]['instances'])):\n                data_list[i]['instances'][j]['bbox'][0] *= w\n                data_list[i]['instances'][j]['bbox'][2] *= w\n                data_list[i]['instances'][j]['bbox'][1] *= h\n                data_list[i]['instances'][j]['bbox'][3] *= h\n            # add image-level annotation\n            if img_level_anns is not None:\n                img_labels = []\n                confidences = []\n                img_ann_list = img_level_anns.get(img_id, [])\n                for ann in img_ann_list:\n                    img_labels.append(int(ann['image_level_label']))\n                    confidences.append(float(ann['confidence']))\n                data_list[i]['image_level_labels'] = np.array(\n                    img_labels, dtype=np.int64)\n                data_list[i]['confidences'] = np.array(\n                    confidences, dtype=np.float32)\n        return data_list\n\n    def _parse_label_file(self, label_file: str) -> tuple:\n        \"\"\"Get classes name and index mapping from cls-label-description file.\n\n        Args:\n            label_file (str): File path of the label description file that\n                maps the classes names in MID format to their short\n                descriptions.\n\n        Returns:\n            tuple: Class name of OpenImages.\n        \"\"\"\n\n        index_list = []\n        classes_names = []\n        with get_local_path(\n                label_file, backend_args=self.backend_args) as local_path:\n            with open(local_path, 'r') as f:\n                reader = csv.reader(f)\n                for line in reader:\n                    # self.cat2label[line[0]] = line[1]\n                    classes_names.append(line[1])\n                    index_list.append(line[0])\n        index_mapping = {index: i for i, index in enumerate(index_list)}\n        return classes_names, index_mapping\n\n    def _parse_img_level_ann(self,\n                             img_level_ann_file: str) -> Dict[str, List[dict]]:\n        \"\"\"Parse image level annotations from csv style ann_file.\n\n        Args:\n            img_level_ann_file (str): CSV style image level annotation\n                file path.\n\n        Returns:\n            Dict[str, List[dict]]: Annotations where item of the defaultdict\n            indicates an image, each of which has (n) dicts.\n            Keys of dicts are:\n\n                - `image_level_label` (int): Label id.\n                - `confidence` (float): Labels that are human-verified to be\n                  present in an image have confidence = 1 (positive labels).\n                  Labels that are human-verified to be absent from an image\n                  have confidence = 0 (negative labels). Machine-generated\n                  labels have fractional confidences, generally >= 0.5.\n                  The higher the confidence, the smaller the chance for\n                  the label to be a false positive.\n        \"\"\"\n\n        item_lists = defaultdict(list)\n        with get_local_path(\n                img_level_ann_file,\n                backend_args=self.backend_args) as local_path:\n            with open(local_path, 'r') as f:\n                reader = csv.reader(f)\n                for i, line in enumerate(reader):\n                    if i == 0:\n                        continue\n                    img_id = line[0]\n                    item_lists[img_id].append(\n                        dict(\n                            image_level_label=int(\n                                self.label_id_mapping[line[2]]),\n                            confidence=float(line[3])))\n        return item_lists\n\n    def _get_relation_matrix(self, hierarchy_file: str) -> np.ndarray:\n        \"\"\"Get the matrix of class hierarchy from the hierarchy file. Hierarchy\n        for 600 classes can be found at https://storage.googleapis.com/openimag\n        es/2018_04/bbox_labels_600_hierarchy_visualizer/circle.html.\n\n        Args:\n            hierarchy_file (str): File path to the hierarchy for classes.\n\n        Returns:\n            np.ndarray: The matrix of the corresponding relationship between\n            the parent class and the child class, of shape\n            (class_num, class_num).\n        \"\"\"  # noqa\n\n        hierarchy = load(\n            hierarchy_file, file_format='json', backend_args=self.backend_args)\n        class_num = len(self._metainfo['classes'])\n        relation_matrix = np.eye(class_num, class_num)\n        relation_matrix = self._convert_hierarchy_tree(hierarchy,\n                                                       relation_matrix)\n        return relation_matrix\n\n    def _convert_hierarchy_tree(self,\n                                hierarchy_map: dict,\n                                relation_matrix: np.ndarray,\n                                parents: list = [],\n                                get_all_parents: bool = True) -> np.ndarray:\n        \"\"\"Get matrix of the corresponding relationship between the parent\n        class and the child class.\n\n        Args:\n            hierarchy_map (dict): Including label name and corresponding\n                subcategory. Keys of dicts are:\n\n                - `LabeName` (str): Name of the label.\n                - `Subcategory` (dict | list): Corresponding subcategory(ies).\n            relation_matrix (ndarray): The matrix of the corresponding\n                relationship between the parent class and the child class,\n                of shape (class_num, class_num).\n            parents (list): Corresponding parent class.\n            get_all_parents (bool): Whether get all parent names.\n                Default: True\n\n        Returns:\n            ndarray: The matrix of the corresponding relationship between\n            the parent class and the child class, of shape\n            (class_num, class_num).\n        \"\"\"\n\n        if 'Subcategory' in hierarchy_map:\n            for node in hierarchy_map['Subcategory']:\n                if 'LabelName' in node:\n                    children_name = node['LabelName']\n                    children_index = self.label_id_mapping[children_name]\n                    children = [children_index]\n                else:\n                    continue\n                if len(parents) > 0:\n                    for parent_index in parents:\n                        if get_all_parents:\n                            children.append(parent_index)\n                        relation_matrix[children_index, parent_index] = 1\n                relation_matrix = self._convert_hierarchy_tree(\n                    node, relation_matrix, parents=children)\n        return relation_matrix\n\n    def _join_prefix(self):\n        \"\"\"Join ``self.data_root`` with annotation path.\"\"\"\n        super()._join_prefix()\n        if not is_abs(self.label_file) and self.label_file:\n            self.label_file = osp.join(self.data_root, self.label_file)\n        if not is_abs(self.meta_file) and self.meta_file:\n            self.meta_file = osp.join(self.data_root, self.meta_file)\n        if not is_abs(self.hierarchy_file) and self.hierarchy_file:\n            self.hierarchy_file = osp.join(self.data_root, self.hierarchy_file)\n        if self.image_level_ann_file and not is_abs(self.image_level_ann_file):\n            self.image_level_ann_file = osp.join(self.data_root,\n                                                 self.image_level_ann_file)\n\n\n@DATASETS.register_module()\nclass OpenImagesChallengeDataset(OpenImagesDataset):\n    \"\"\"Open Images Challenge dataset for detection.\n\n    Args:\n        ann_file (str): Open Images Challenge box annotation in txt format.\n    \"\"\"\n\n    METAINFO: dict = dict(dataset_type='oid_challenge')\n\n    def __init__(self, ann_file: str, **kwargs) -> None:\n        if not ann_file.endswith('txt'):\n            raise TypeError('The annotation file of Open Images Challenge '\n                            'should be a txt file.')\n\n        super().__init__(ann_file=ann_file, **kwargs)\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"\n        classes_names, label_id_mapping = self._parse_label_file(\n            self.label_file)\n        self._metainfo['classes'] = classes_names\n        self.label_id_mapping = label_id_mapping\n\n        if self.image_level_ann_file is not None:\n            img_level_anns = self._parse_img_level_ann(\n                self.image_level_ann_file)\n        else:\n            img_level_anns = None\n\n        # OpenImagesMetric can get the relation matrix from the dataset meta\n        relation_matrix = self._get_relation_matrix(self.hierarchy_file)\n        self._metainfo['RELATION_MATRIX'] = relation_matrix\n\n        data_list = []\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            with open(local_path, 'r') as f:\n                lines = f.readlines()\n        i = 0\n        while i < len(lines):\n            instances = []\n            filename = lines[i].rstrip()\n            i += 2\n            img_gt_size = int(lines[i])\n            i += 1\n            for j in range(img_gt_size):\n                sp = lines[i + j].split()\n                instances.append(\n                    dict(\n                        bbox=[\n                            float(sp[1]),\n                            float(sp[2]),\n                            float(sp[3]),\n                            float(sp[4])\n                        ],\n                        bbox_label=int(sp[0]) - 1,  # labels begin from 1\n                        ignore_flag=0,\n                        is_group_ofs=True if int(sp[5]) == 1 else False))\n            i += img_gt_size\n            data_list.append(\n                dict(\n                    img_path=osp.join(self.data_prefix['img'], filename),\n                    instances=instances,\n                ))\n\n        # add image metas to data list\n        img_metas = load(\n            self.meta_file, file_format='pkl', backend_args=self.backend_args)\n        assert len(img_metas) == len(data_list)\n        for i, meta in enumerate(img_metas):\n            img_id = osp.split(data_list[i]['img_path'])[-1][:-4]\n            assert img_id == osp.split(meta['filename'])[-1][:-4]\n            h, w = meta['ori_shape'][:2]\n            data_list[i]['height'] = h\n            data_list[i]['width'] = w\n            data_list[i]['img_id'] = img_id\n            # denormalize bboxes\n            for j in range(len(data_list[i]['instances'])):\n                data_list[i]['instances'][j]['bbox'][0] *= w\n                data_list[i]['instances'][j]['bbox'][2] *= w\n                data_list[i]['instances'][j]['bbox'][1] *= h\n                data_list[i]['instances'][j]['bbox'][3] *= h\n            # add image-level annotation\n            if img_level_anns is not None:\n                img_labels = []\n                confidences = []\n                img_ann_list = img_level_anns.get(img_id, [])\n                for ann in img_ann_list:\n                    img_labels.append(int(ann['image_level_label']))\n                    confidences.append(float(ann['confidence']))\n                data_list[i]['image_level_labels'] = np.array(\n                    img_labels, dtype=np.int64)\n                data_list[i]['confidences'] = np.array(\n                    confidences, dtype=np.float32)\n        return data_list\n\n    def _parse_label_file(self, label_file: str) -> tuple:\n        \"\"\"Get classes name and index mapping from cls-label-description file.\n\n        Args:\n            label_file (str): File path of the label description file that\n                maps the classes names in MID format to their short\n                descriptions.\n\n        Returns:\n            tuple: Class name of OpenImages.\n        \"\"\"\n        label_list = []\n        id_list = []\n        index_mapping = {}\n        with get_local_path(\n                label_file, backend_args=self.backend_args) as local_path:\n            with open(local_path, 'r') as f:\n                reader = csv.reader(f)\n                for line in reader:\n                    label_name = line[0]\n                    label_id = int(line[2])\n                    label_list.append(line[1])\n                    id_list.append(label_id)\n                    index_mapping[label_name] = label_id - 1\n        indexes = np.argsort(id_list)\n        classes_names = []\n        for index in indexes:\n            classes_names.append(label_list[index])\n        return classes_names, index_mapping\n\n    def _parse_img_level_ann(self, image_level_ann_file):\n        \"\"\"Parse image level annotations from csv style ann_file.\n\n        Args:\n            image_level_ann_file (str): CSV style image level annotation\n                file path.\n\n        Returns:\n            defaultdict[list[dict]]: Annotations where item of the defaultdict\n            indicates an image, each of which has (n) dicts.\n            Keys of dicts are:\n\n                - `image_level_label` (int): of shape 1.\n                - `confidence` (float): of shape 1.\n        \"\"\"\n\n        item_lists = defaultdict(list)\n        with get_local_path(\n                image_level_ann_file,\n                backend_args=self.backend_args) as local_path:\n            with open(local_path, 'r') as f:\n                reader = csv.reader(f)\n                i = -1\n                for line in reader:\n                    i += 1\n                    if i == 0:\n                        continue\n                    else:\n                        img_id = line[0]\n                        label_id = line[1]\n                        assert label_id in self.label_id_mapping\n                        image_level_label = int(\n                            self.label_id_mapping[label_id])\n                        confidence = float(line[2])\n                        item_lists[img_id].append(\n                            dict(\n                                image_level_label=image_level_label,\n                                confidence=confidence))\n        return item_lists\n\n    def _get_relation_matrix(self, hierarchy_file: str) -> np.ndarray:\n        \"\"\"Get the matrix of class hierarchy from the hierarchy file.\n\n        Args:\n            hierarchy_file (str): File path to the hierarchy for classes.\n\n        Returns:\n            np.ndarray: The matrix of the corresponding\n            relationship between the parent class and the child class,\n            of shape (class_num, class_num).\n        \"\"\"\n        with get_local_path(\n                hierarchy_file, backend_args=self.backend_args) as local_path:\n            class_label_tree = np.load(local_path, allow_pickle=True)\n        return class_label_tree[1:, 1:]\n"
  },
  {
    "path": "mmdet/datasets/refcoco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport collections\nimport os.path as osp\nimport random\nfrom typing import Dict, List\n\nimport mmengine\nfrom mmengine.dataset import BaseDataset\n\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass RefCocoDataset(BaseDataset):\n    \"\"\"RefCOCO dataset.\n\n    The `Refcoco` and `Refcoco+` dataset is based on\n    `ReferItGame: Referring to Objects in Photographs of Natural Scenes\n    <http://tamaraberg.com/papers/referit.pdf>`_.\n\n    The `Refcocog` dataset is based on\n    `Generation and Comprehension of Unambiguous Object Descriptions\n    <https://arxiv.org/abs/1511.02283>`_.\n\n    Args:\n        ann_file (str): Annotation file path.\n        data_root (str): The root directory for ``data_prefix`` and\n            ``ann_file``. Defaults to ''.\n        data_prefix (str): Prefix for training data.\n        split_file (str): Split file path.\n        split (str): Split name. Defaults to 'train'.\n        text_mode (str): Text mode. Defaults to 'random'.\n        **kwargs: Other keyword arguments in :class:`BaseDataset`.\n    \"\"\"\n\n    def __init__(self,\n                 data_root: str,\n                 ann_file: str,\n                 split_file: str,\n                 data_prefix: Dict,\n                 split: str = 'train',\n                 text_mode: str = 'random',\n                 **kwargs):\n        self.split_file = split_file\n        self.split = split\n\n        assert text_mode in ['original', 'random', 'concat', 'select_first']\n        self.text_mode = text_mode\n        super().__init__(\n            data_root=data_root,\n            data_prefix=data_prefix,\n            ann_file=ann_file,\n            **kwargs,\n        )\n\n    def _join_prefix(self):\n        if not mmengine.is_abs(self.split_file) and self.split_file:\n            self.split_file = osp.join(self.data_root, self.split_file)\n\n        return super()._join_prefix()\n\n    def _init_refs(self):\n        \"\"\"Initialize the refs for RefCOCO.\"\"\"\n        anns, imgs = {}, {}\n        for ann in self.instances['annotations']:\n            anns[ann['id']] = ann\n        for img in self.instances['images']:\n            imgs[img['id']] = img\n\n        refs, ref_to_ann = {}, {}\n        for ref in self.splits:\n            # ids\n            ref_id = ref['ref_id']\n            ann_id = ref['ann_id']\n            # add mapping related to ref\n            refs[ref_id] = ref\n            ref_to_ann[ref_id] = anns[ann_id]\n\n        self.refs = refs\n        self.ref_to_ann = ref_to_ann\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load data list.\"\"\"\n        self.splits = mmengine.load(self.split_file, file_format='pkl')\n        self.instances = mmengine.load(self.ann_file, file_format='json')\n        self._init_refs()\n        img_prefix = self.data_prefix['img_path']\n\n        ref_ids = [\n            ref['ref_id'] for ref in self.splits if ref['split'] == self.split\n        ]\n        full_anno = []\n        for ref_id in ref_ids:\n            ref = self.refs[ref_id]\n            ann = self.ref_to_ann[ref_id]\n            ann.update(ref)\n            full_anno.append(ann)\n\n        image_id_list = []\n        final_anno = {}\n        for anno in full_anno:\n            image_id_list.append(anno['image_id'])\n            final_anno[anno['ann_id']] = anno\n        annotations = [value for key, value in final_anno.items()]\n\n        coco_train_id = []\n        image_annot = {}\n        for i in range(len(self.instances['images'])):\n            coco_train_id.append(self.instances['images'][i]['id'])\n            image_annot[self.instances['images'][i]\n                        ['id']] = self.instances['images'][i]\n\n        images = []\n        for image_id in list(set(image_id_list)):\n            images += [image_annot[image_id]]\n\n        data_list = []\n\n        grounding_dict = collections.defaultdict(list)\n        for anno in annotations:\n            image_id = int(anno['image_id'])\n            grounding_dict[image_id].append(anno)\n\n        join_path = mmengine.fileio.get_file_backend(img_prefix).join_path\n        for image in images:\n            img_id = image['id']\n            instances = []\n            sentences = []\n            for grounding_anno in grounding_dict[img_id]:\n                texts = [x['raw'].lower() for x in grounding_anno['sentences']]\n                # random select one text\n                if self.text_mode == 'random':\n                    idx = random.randint(0, len(texts) - 1)\n                    text = [texts[idx]]\n                # concat all texts\n                elif self.text_mode == 'concat':\n                    text = [''.join(texts)]\n                # select the first text\n                elif self.text_mode == 'select_first':\n                    text = [texts[0]]\n                # use all texts\n                elif self.text_mode == 'original':\n                    text = texts\n                else:\n                    raise ValueError(f'Invalid text mode \"{self.text_mode}\".')\n                ins = [{\n                    'mask': grounding_anno['segmentation'],\n                    'ignore_flag': 0\n                }] * len(text)\n                instances.extend(ins)\n                sentences.extend(text)\n            data_info = {\n                'img_path': join_path(img_prefix, image['file_name']),\n                'img_id': img_id,\n                'instances': instances,\n                'text': sentences\n            }\n            data_list.append(data_info)\n\n        if len(data_list) == 0:\n            raise ValueError(f'No sample in split \"{self.split}\".')\n\n        return data_list\n"
  },
  {
    "path": "mmdet/datasets/reid_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nfrom collections import defaultdict\nfrom typing import Any, Dict, List\n\nimport numpy as np\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.utils import check_file_exist\n\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass ReIDDataset(BaseDataset):\n    \"\"\"Dataset for ReID.\n\n    Args:\n        triplet_sampler (dict, optional): The sampler for hard mining\n            triplet loss. Defaults to None.\n        keys: num_ids (int): The number of person ids.\n              ins_per_id (int): The number of image for each person.\n    \"\"\"\n\n    def __init__(self, triplet_sampler: dict = None, *args, **kwargs):\n        self.triplet_sampler = triplet_sampler\n        super().__init__(*args, **kwargs)\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ''self.ann_file''.\n\n        Returns:\n              list[dict]: A list of annotation.\n        \"\"\"\n        assert isinstance(self.ann_file, str)\n        check_file_exist(self.ann_file)\n        data_list = []\n        with open(self.ann_file) as f:\n            samples = [x.strip().split(' ') for x in f.readlines()]\n            for filename, gt_label in samples:\n                info = dict(img_prefix=self.data_prefix)\n                if self.data_prefix['img_path'] is not None:\n                    info['img_path'] = osp.join(self.data_prefix['img_path'],\n                                                filename)\n                else:\n                    info['img_path'] = filename\n                info['gt_label'] = np.array(gt_label, dtype=np.int64)\n                data_list.append(info)\n        self._parse_ann_info(data_list)\n        return data_list\n\n    def _parse_ann_info(self, data_list: List[dict]):\n        \"\"\"Parse person id annotations.\"\"\"\n        index_tmp_dic = defaultdict(list)  # pid->[idx1,...,idxN]\n        self.index_dic = dict()  # pid->array([idx1,...,idxN])\n        for idx, info in enumerate(data_list):\n            pid = info['gt_label']\n            index_tmp_dic[int(pid)].append(idx)\n        for pid, idxs in index_tmp_dic.items():\n            self.index_dic[pid] = np.asarray(idxs, dtype=np.int64)\n        self.pids = np.asarray(list(self.index_dic.keys()), dtype=np.int64)\n\n    def prepare_data(self, idx: int) -> Any:\n        \"\"\"Get data processed by ''self.pipeline''.\n\n        Args:\n            idx (int): The index of ''data_info''\n\n        Returns:\n            Any: Depends on ''self.pipeline''\n        \"\"\"\n        data_info = self.get_data_info(idx)\n        if self.triplet_sampler is not None:\n            img_info = self.triplet_sampling(data_info['gt_label'],\n                                             **self.triplet_sampler)\n            data_info = copy.deepcopy(img_info)  # triplet -> list\n        else:\n            data_info = copy.deepcopy(data_info)  # no triplet -> dict\n        return self.pipeline(data_info)\n\n    def triplet_sampling(self,\n                         pos_pid,\n                         num_ids: int = 8,\n                         ins_per_id: int = 4) -> Dict:\n        \"\"\"Triplet sampler for hard mining triplet loss. First, for one\n        pos_pid, random sample ins_per_id images with same person id.\n\n        Then, random sample num_ids - 1 images for each negative id.\n        Finally, random sample ins_per_id images for each negative id.\n\n        Args:\n            pos_pid (ndarray): The person id of the anchor.\n            num_ids (int): The number of person ids.\n            ins_per_id (int): The number of images for each person.\n\n        Returns:\n            Dict: Annotation information of num_ids X ins_per_id images.\n        \"\"\"\n        assert len(self.pids) >= num_ids, \\\n            'The number of person ids in the training set must ' \\\n            'be greater than the number of person ids in the sample.'\n\n        pos_idxs = self.index_dic[int(\n            pos_pid)]  # all positive idxs for pos_pid\n        idxs_list = []\n        # select positive samplers\n        idxs_list.extend(pos_idxs[np.random.choice(\n            pos_idxs.shape[0], ins_per_id, replace=True)])\n        # select negative ids\n        neg_pids = np.random.choice(\n            [i for i, _ in enumerate(self.pids) if i != pos_pid],\n            num_ids - 1,\n            replace=False)\n        # select negative samplers for each negative id\n        for neg_pid in neg_pids:\n            neg_idxs = self.index_dic[neg_pid]\n            idxs_list.extend(neg_idxs[np.random.choice(\n                neg_idxs.shape[0], ins_per_id, replace=True)])\n        # return the final triplet batch\n        triplet_img_infos = []\n        for idx in idxs_list:\n            triplet_img_infos.append(copy.deepcopy(self.get_data_info(idx)))\n        # Collect data_list scatters (list of dict -> dict of list)\n        out = dict()\n        for key in triplet_img_infos[0].keys():\n            out[key] = [_info[key] for _info in triplet_img_infos]\n        return out\n"
  },
  {
    "path": "mmdet/datasets/samplers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .batch_sampler import (AspectRatioBatchSampler,\n                            MultiDataAspectRatioBatchSampler,\n                            TrackAspectRatioBatchSampler)\nfrom .class_aware_sampler import ClassAwareSampler\nfrom .custom_sample_size_sampler import CustomSampleSizeSampler\nfrom .multi_data_sampler import MultiDataSampler\nfrom .multi_source_sampler import GroupMultiSourceSampler, MultiSourceSampler\nfrom .track_img_sampler import TrackImgSampler\n\n__all__ = [\n    'ClassAwareSampler', 'AspectRatioBatchSampler', 'MultiSourceSampler',\n    'GroupMultiSourceSampler', 'TrackImgSampler',\n    'TrackAspectRatioBatchSampler', 'MultiDataSampler',\n    'MultiDataAspectRatioBatchSampler', 'CustomSampleSizeSampler'\n]\n"
  },
  {
    "path": "mmdet/datasets/samplers/batch_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Sequence\n\nfrom torch.utils.data import BatchSampler, Sampler\n\nfrom mmdet.datasets.samplers.track_img_sampler import TrackImgSampler\nfrom mmdet.registry import DATA_SAMPLERS\n\n\n# TODO: maybe replace with a data_loader wrapper\n@DATA_SAMPLERS.register_module()\nclass AspectRatioBatchSampler(BatchSampler):\n    \"\"\"A sampler wrapper for grouping images with similar aspect ratio (< 1 or.\n\n    >= 1) into a same batch.\n\n    Args:\n        sampler (Sampler): Base sampler.\n        batch_size (int): Size of mini-batch.\n        drop_last (bool): If ``True``, the sampler will drop the last batch if\n            its size would be less than ``batch_size``.\n    \"\"\"\n\n    def __init__(self,\n                 sampler: Sampler,\n                 batch_size: int,\n                 drop_last: bool = False) -> None:\n        if not isinstance(sampler, Sampler):\n            raise TypeError('sampler should be an instance of ``Sampler``, '\n                            f'but got {sampler}')\n        if not isinstance(batch_size, int) or batch_size <= 0:\n            raise ValueError('batch_size should be a positive integer value, '\n                             f'but got batch_size={batch_size}')\n        self.sampler = sampler\n        self.batch_size = batch_size\n        self.drop_last = drop_last\n        # two groups for w < h and w >= h\n        self._aspect_ratio_buckets = [[] for _ in range(2)]\n\n    def __iter__(self) -> Sequence[int]:\n        for idx in self.sampler:\n            data_info = self.sampler.dataset.get_data_info(idx)\n            width, height = data_info['width'], data_info['height']\n            bucket_id = 0 if width < height else 1\n            bucket = self._aspect_ratio_buckets[bucket_id]\n            bucket.append(idx)\n            # yield a batch of indices in the same aspect ratio group\n            if len(bucket) == self.batch_size:\n                yield bucket[:]\n                del bucket[:]\n\n        # yield the rest data and reset the bucket\n        left_data = self._aspect_ratio_buckets[0] + self._aspect_ratio_buckets[\n            1]\n        self._aspect_ratio_buckets = [[] for _ in range(2)]\n        while len(left_data) > 0:\n            if len(left_data) <= self.batch_size:\n                if not self.drop_last:\n                    yield left_data[:]\n                left_data = []\n            else:\n                yield left_data[:self.batch_size]\n                left_data = left_data[self.batch_size:]\n\n    def __len__(self) -> int:\n        if self.drop_last:\n            return len(self.sampler) // self.batch_size\n        else:\n            return (len(self.sampler) + self.batch_size - 1) // self.batch_size\n\n\n@DATA_SAMPLERS.register_module()\nclass TrackAspectRatioBatchSampler(AspectRatioBatchSampler):\n    \"\"\"A sampler wrapper for grouping images with similar aspect ratio (< 1 or.\n\n    >= 1) into a same batch.\n\n    Args:\n        sampler (Sampler): Base sampler.\n        batch_size (int): Size of mini-batch.\n        drop_last (bool): If ``True``, the sampler will drop the last batch if\n            its size would be less than ``batch_size``.\n    \"\"\"\n\n    def __iter__(self) -> Sequence[int]:\n        for idx in self.sampler:\n            # hard code to solve TrackImgSampler\n            if isinstance(self.sampler, TrackImgSampler):\n                video_idx, _ = idx\n            else:\n                video_idx = idx\n            # video_idx\n            data_info = self.sampler.dataset.get_data_info(video_idx)\n            # data_info {video_id, images, video_length}\n            img_data_info = data_info['images'][0]\n            width, height = img_data_info['width'], img_data_info['height']\n            bucket_id = 0 if width < height else 1\n            bucket = self._aspect_ratio_buckets[bucket_id]\n            bucket.append(idx)\n            # yield a batch of indices in the same aspect ratio group\n            if len(bucket) == self.batch_size:\n                yield bucket[:]\n                del bucket[:]\n\n        # yield the rest data and reset the bucket\n        left_data = self._aspect_ratio_buckets[0] + self._aspect_ratio_buckets[\n            1]\n        self._aspect_ratio_buckets = [[] for _ in range(2)]\n        while len(left_data) > 0:\n            if len(left_data) <= self.batch_size:\n                if not self.drop_last:\n                    yield left_data[:]\n                left_data = []\n            else:\n                yield left_data[:self.batch_size]\n                left_data = left_data[self.batch_size:]\n\n\n@DATA_SAMPLERS.register_module()\nclass MultiDataAspectRatioBatchSampler(BatchSampler):\n    \"\"\"A sampler wrapper for grouping images with similar aspect ratio (< 1 or.\n\n    >= 1) into a same batch for multi-source datasets.\n\n    Args:\n        sampler (Sampler): Base sampler.\n        batch_size (Sequence(int)): Size of mini-batch for multi-source\n        datasets.\n        num_datasets(int): Number of multi-source datasets.\n        drop_last (bool): If ``True``, the sampler will drop the last batch if\n        its size would be less than ``batch_size``.\n    \"\"\"\n\n    def __init__(self,\n                 sampler: Sampler,\n                 batch_size: Sequence[int],\n                 num_datasets: int,\n                 drop_last: bool = True) -> None:\n        if not isinstance(sampler, Sampler):\n            raise TypeError('sampler should be an instance of ``Sampler``, '\n                            f'but got {sampler}')\n        self.sampler = sampler\n        self.batch_size = batch_size\n        self.num_datasets = num_datasets\n        self.drop_last = drop_last\n        # two groups for w < h and w >= h for each dataset --> 2 * num_datasets\n        self._buckets = [[] for _ in range(2 * self.num_datasets)]\n\n    def __iter__(self) -> Sequence[int]:\n        for idx in self.sampler:\n            data_info = self.sampler.dataset.get_data_info(idx)\n            width, height = data_info['width'], data_info['height']\n            dataset_source_idx = self.sampler.dataset.get_dataset_source(idx)\n            aspect_ratio_bucket_id = 0 if width < height else 1\n            bucket_id = dataset_source_idx * 2 + aspect_ratio_bucket_id\n            bucket = self._buckets[bucket_id]\n            bucket.append(idx)\n            # yield a batch of indices in the same aspect ratio group\n            if len(bucket) == self.batch_size[dataset_source_idx]:\n                yield bucket[:]\n                del bucket[:]\n\n        # yield the rest data and reset the bucket\n        for i in range(self.num_datasets):\n            left_data = self._buckets[i * 2 + 0] + self._buckets[i * 2 + 1]\n            while len(left_data) > 0:\n                if len(left_data) <= self.batch_size[i]:\n                    if not self.drop_last:\n                        yield left_data[:]\n                    left_data = []\n                else:\n                    yield left_data[:self.batch_size[i]]\n                    left_data = left_data[self.batch_size[i]:]\n\n        self._buckets = [[] for _ in range(2 * self.num_datasets)]\n\n    def __len__(self) -> int:\n        sizes = [0 for _ in range(self.num_datasets)]\n        for idx in self.sampler:\n            dataset_source_idx = self.sampler.dataset.get_dataset_source(idx)\n            sizes[dataset_source_idx] += 1\n\n        if self.drop_last:\n            lens = 0\n            for i in range(self.num_datasets):\n                lens += sizes[i] // self.batch_size[i]\n            return lens\n        else:\n            lens = 0\n            for i in range(self.num_datasets):\n                lens += (sizes[i] + self.batch_size[i] -\n                         1) // self.batch_size[i]\n            return lens\n"
  },
  {
    "path": "mmdet/datasets/samplers/class_aware_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Dict, Iterator, Optional, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.dist import get_dist_info, sync_random_seed\nfrom torch.utils.data import Sampler\n\nfrom mmdet.registry import DATA_SAMPLERS\n\n\n@DATA_SAMPLERS.register_module()\nclass ClassAwareSampler(Sampler):\n    r\"\"\"Sampler that restricts data loading to the label of the dataset.\n\n    A class-aware sampling strategy to effectively tackle the\n    non-uniform class distribution. The length of the training data is\n    consistent with source data. Simple improvements based on `Relay\n    Backpropagation for Effective Learning of Deep Convolutional\n    Neural Networks <https://arxiv.org/abs/1512.05830>`_\n\n    The implementation logic is referred to\n    https://github.com/Sense-X/TSD/blob/master/mmdet/datasets/samplers/distributed_classaware_sampler.py\n\n    Args:\n        dataset: Dataset used for sampling.\n        seed (int, optional): random seed used to shuffle the sampler.\n            This number should be identical across all\n            processes in the distributed group. Defaults to None.\n        num_sample_class (int): The number of samples taken from each\n            per-label list. Defaults to 1.\n    \"\"\"\n\n    def __init__(self,\n                 dataset: BaseDataset,\n                 seed: Optional[int] = None,\n                 num_sample_class: int = 1) -> None:\n        rank, world_size = get_dist_info()\n        self.rank = rank\n        self.world_size = world_size\n\n        self.dataset = dataset\n        self.epoch = 0\n        # Must be the same across all workers. If None, will use a\n        # random seed shared among workers\n        # (require synchronization among all workers)\n        if seed is None:\n            seed = sync_random_seed()\n        self.seed = seed\n\n        # The number of samples taken from each per-label list\n        assert num_sample_class > 0 and isinstance(num_sample_class, int)\n        self.num_sample_class = num_sample_class\n        # Get per-label image list from dataset\n        self.cat_dict = self.get_cat2imgs()\n\n        self.num_samples = int(math.ceil(len(self.dataset) * 1.0 / world_size))\n        self.total_size = self.num_samples * self.world_size\n\n        # get number of images containing each category\n        self.num_cat_imgs = [len(x) for x in self.cat_dict.values()]\n        # filter labels without images\n        self.valid_cat_inds = [\n            i for i, length in enumerate(self.num_cat_imgs) if length != 0\n        ]\n        self.num_classes = len(self.valid_cat_inds)\n\n    def get_cat2imgs(self) -> Dict[int, list]:\n        \"\"\"Get a dict with class as key and img_ids as values.\n\n        Returns:\n            dict[int, list]: A dict of per-label image list,\n            the item of the dict indicates a label index,\n            corresponds to the image index that contains the label.\n        \"\"\"\n        classes = self.dataset.metainfo.get('classes', None)\n        if classes is None:\n            raise ValueError('dataset metainfo must contain `classes`')\n        # sort the label index\n        cat2imgs = {i: [] for i in range(len(classes))}\n        for i in range(len(self.dataset)):\n            cat_ids = set(self.dataset.get_cat_ids(i))\n            for cat in cat_ids:\n                cat2imgs[cat].append(i)\n        return cat2imgs\n\n    def __iter__(self) -> Iterator[int]:\n        # deterministically shuffle based on epoch\n        g = torch.Generator()\n        g.manual_seed(self.epoch + self.seed)\n\n        # initialize label list\n        label_iter_list = RandomCycleIter(self.valid_cat_inds, generator=g)\n        # initialize each per-label image list\n        data_iter_dict = dict()\n        for i in self.valid_cat_inds:\n            data_iter_dict[i] = RandomCycleIter(self.cat_dict[i], generator=g)\n\n        def gen_cat_img_inds(cls_list, data_dict, num_sample_cls):\n            \"\"\"Traverse the categories and extract `num_sample_cls` image\n            indexes of the corresponding categories one by one.\"\"\"\n            id_indices = []\n            for _ in range(len(cls_list)):\n                cls_idx = next(cls_list)\n                for _ in range(num_sample_cls):\n                    id = next(data_dict[cls_idx])\n                    id_indices.append(id)\n            return id_indices\n\n        # deterministically shuffle based on epoch\n        num_bins = int(\n            math.ceil(self.total_size * 1.0 / self.num_classes /\n                      self.num_sample_class))\n        indices = []\n        for i in range(num_bins):\n            indices += gen_cat_img_inds(label_iter_list, data_iter_dict,\n                                        self.num_sample_class)\n\n        # fix extra samples to make it evenly divisible\n        if len(indices) >= self.total_size:\n            indices = indices[:self.total_size]\n        else:\n            indices += indices[:(self.total_size - len(indices))]\n        assert len(indices) == self.total_size\n\n        # subsample\n        offset = self.num_samples * self.rank\n        indices = indices[offset:offset + self.num_samples]\n        assert len(indices) == self.num_samples\n\n        return iter(indices)\n\n    def __len__(self) -> int:\n        \"\"\"The number of samples in this rank.\"\"\"\n        return self.num_samples\n\n    def set_epoch(self, epoch: int) -> None:\n        \"\"\"Sets the epoch for this sampler.\n\n        When :attr:`shuffle=True`, this ensures all replicas use a different\n        random ordering for each epoch. Otherwise, the next iteration of this\n        sampler will yield the same ordering.\n\n        Args:\n            epoch (int): Epoch number.\n        \"\"\"\n        self.epoch = epoch\n\n\nclass RandomCycleIter:\n    \"\"\"Shuffle the list and do it again after the list have traversed.\n\n    The implementation logic is referred to\n    https://github.com/wutong16/DistributionBalancedLoss/blob/master/mllt/datasets/loader/sampler.py\n\n    Example:\n        >>> label_list = [0, 1, 2, 4, 5]\n        >>> g = torch.Generator()\n        >>> g.manual_seed(0)\n        >>> label_iter_list = RandomCycleIter(label_list, generator=g)\n        >>> index = next(label_iter_list)\n    Args:\n        data (list or ndarray): The data that needs to be shuffled.\n        generator: An torch.Generator object, which is used in setting the seed\n            for generating random numbers.\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 data: Union[list, np.ndarray],\n                 generator: torch.Generator = None) -> None:\n        self.data = data\n        self.length = len(data)\n        self.index = torch.randperm(self.length, generator=generator).numpy()\n        self.i = 0\n        self.generator = generator\n\n    def __iter__(self) -> Iterator:\n        return self\n\n    def __len__(self) -> int:\n        return len(self.data)\n\n    def __next__(self):\n        if self.i == self.length:\n            self.index = torch.randperm(\n                self.length, generator=self.generator).numpy()\n            self.i = 0\n        idx = self.data[self.index[self.i]]\n        self.i += 1\n        return idx\n"
  },
  {
    "path": "mmdet/datasets/samplers/custom_sample_size_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Iterator, Optional, Sequence, Sized\n\nimport torch\nfrom mmengine.dist import get_dist_info, sync_random_seed\nfrom torch.utils.data import Sampler\n\nfrom mmdet.registry import DATA_SAMPLERS\nfrom .class_aware_sampler import RandomCycleIter\n\n\n@DATA_SAMPLERS.register_module()\nclass CustomSampleSizeSampler(Sampler):\n\n    def __init__(self,\n                 dataset: Sized,\n                 dataset_size: Sequence[int],\n                 ratio_mode: bool = False,\n                 seed: Optional[int] = None,\n                 round_up: bool = True) -> None:\n        assert len(dataset.datasets) == len(dataset_size)\n        rank, world_size = get_dist_info()\n        self.rank = rank\n        self.world_size = world_size\n\n        self.dataset = dataset\n        if seed is None:\n            seed = sync_random_seed()\n        self.seed = seed\n        self.epoch = 0\n        self.round_up = round_up\n\n        total_size = 0\n        total_size_fake = 0\n        self.dataset_index = []\n        self.dataset_cycle_iter = []\n        new_dataset_size = []\n        for dataset, size in zip(dataset.datasets, dataset_size):\n            self.dataset_index.append(\n                list(range(total_size_fake,\n                           len(dataset) + total_size_fake)))\n            total_size_fake += len(dataset)\n            if size == -1:\n                total_size += len(dataset)\n                self.dataset_cycle_iter.append(None)\n                new_dataset_size.append(-1)\n            else:\n                if ratio_mode:\n                    size = int(size * len(dataset))\n                assert size <= len(\n                    dataset\n                ), f'dataset size {size} is larger than ' \\\n                   f'dataset length {len(dataset)}'\n                total_size += size\n                new_dataset_size.append(size)\n\n                g = torch.Generator()\n                g.manual_seed(self.seed)\n                self.dataset_cycle_iter.append(\n                    RandomCycleIter(self.dataset_index[-1], generator=g))\n        self.dataset_size = new_dataset_size\n\n        if self.round_up:\n            self.num_samples = math.ceil(total_size / world_size)\n            self.total_size = self.num_samples * self.world_size\n        else:\n            self.num_samples = math.ceil((total_size - rank) / world_size)\n            self.total_size = total_size\n\n    def __iter__(self) -> Iterator[int]:\n        \"\"\"Iterate the indices.\"\"\"\n        # deterministically shuffle based on epoch and seed\n        g = torch.Generator()\n        g.manual_seed(self.seed + self.epoch)\n\n        out_index = []\n        for data_size, data_index, cycle_iter in zip(self.dataset_size,\n                                                     self.dataset_index,\n                                                     self.dataset_cycle_iter):\n            if data_size == -1:\n                out_index += data_index\n            else:\n                index = [next(cycle_iter) for _ in range(data_size)]\n                out_index += index\n\n        index = torch.randperm(len(out_index), generator=g).numpy().tolist()\n        indices = [out_index[i] for i in index]\n\n        if self.round_up:\n            indices = (\n                indices *\n                int(self.total_size / len(indices) + 1))[:self.total_size]\n        indices = indices[self.rank:self.total_size:self.world_size]\n        return iter(indices)\n\n    def __len__(self) -> int:\n        \"\"\"The number of samples in this rank.\"\"\"\n        return self.num_samples\n\n    def set_epoch(self, epoch: int) -> None:\n        \"\"\"Sets the epoch for this sampler.\n\n        When :attr:`shuffle=True`, this ensures all replicas use a different\n        random ordering for each epoch. Otherwise, the next iteration of this\n        sampler will yield the same ordering.\n\n        Args:\n            epoch (int): Epoch number.\n        \"\"\"\n        self.epoch = epoch\n"
  },
  {
    "path": "mmdet/datasets/samplers/multi_data_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Iterator, Optional, Sequence, Sized\n\nimport torch\nfrom mmengine.dist import get_dist_info, sync_random_seed\nfrom mmengine.registry import DATA_SAMPLERS\nfrom torch.utils.data import Sampler\n\n\n@DATA_SAMPLERS.register_module()\nclass MultiDataSampler(Sampler):\n    \"\"\"The default data sampler for both distributed and non-distributed\n    environment.\n\n    It has several differences from the PyTorch ``DistributedSampler`` as\n    below:\n\n    1. This sampler supports non-distributed environment.\n\n    2. The round up behaviors are a little different.\n\n       - If ``round_up=True``, this sampler will add extra samples to make the\n         number of samples is evenly divisible by the world size. And\n         this behavior is the same as the ``DistributedSampler`` with\n         ``drop_last=False``.\n       - If ``round_up=False``, this sampler won't remove or add any samples\n         while the ``DistributedSampler`` with ``drop_last=True`` will remove\n         tail samples.\n\n    Args:\n        dataset (Sized): The dataset.\n        dataset_ratio (Sequence(int)) The ratios of different datasets.\n        seed (int, optional): Random seed used to shuffle the sampler if\n            :attr:`shuffle=True`. This number should be identical across all\n            processes in the distributed group. Defaults to None.\n        round_up (bool): Whether to add extra samples to make the number of\n            samples evenly divisible by the world size. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 dataset: Sized,\n                 dataset_ratio: Sequence[int],\n                 seed: Optional[int] = None,\n                 round_up: bool = True) -> None:\n        rank, world_size = get_dist_info()\n        self.rank = rank\n        self.world_size = world_size\n\n        self.dataset = dataset\n        self.dataset_ratio = dataset_ratio\n\n        if seed is None:\n            seed = sync_random_seed()\n        self.seed = seed\n        self.epoch = 0\n        self.round_up = round_up\n\n        if self.round_up:\n            self.num_samples = math.ceil(len(self.dataset) / world_size)\n            self.total_size = self.num_samples * self.world_size\n        else:\n            self.num_samples = math.ceil(\n                (len(self.dataset) - rank) / world_size)\n            self.total_size = len(self.dataset)\n\n        self.sizes = [len(dataset) for dataset in self.dataset.datasets]\n\n        dataset_weight = [\n            torch.ones(s) * max(self.sizes) / s * r / sum(self.dataset_ratio)\n            for i, (r, s) in enumerate(zip(self.dataset_ratio, self.sizes))\n        ]\n        self.weights = torch.cat(dataset_weight)\n\n    def __iter__(self) -> Iterator[int]:\n        \"\"\"Iterate the indices.\"\"\"\n        # deterministically shuffle based on epoch and seed\n        g = torch.Generator()\n        g.manual_seed(self.seed + self.epoch)\n\n        indices = torch.multinomial(\n            self.weights, len(self.weights), generator=g,\n            replacement=True).tolist()\n\n        # add extra samples to make it evenly divisible\n        if self.round_up:\n            indices = (\n                indices *\n                int(self.total_size / len(indices) + 1))[:self.total_size]\n\n        # subsample\n        indices = indices[self.rank:self.total_size:self.world_size]\n\n        return iter(indices)\n\n    def __len__(self) -> int:\n        \"\"\"The number of samples in this rank.\"\"\"\n        return self.num_samples\n\n    def set_epoch(self, epoch: int) -> None:\n        \"\"\"Sets the epoch for this sampler.\n\n        When :attr:`shuffle=True`, this ensures all replicas use a different\n        random ordering for each epoch. Otherwise, the next iteration of this\n        sampler will yield the same ordering.\n\n        Args:\n            epoch (int): Epoch number.\n        \"\"\"\n        self.epoch = epoch\n"
  },
  {
    "path": "mmdet/datasets/samplers/multi_source_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport itertools\nfrom typing import Iterator, List, Optional, Sized, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.dist import get_dist_info, sync_random_seed\nfrom torch.utils.data import Sampler\n\nfrom mmdet.registry import DATA_SAMPLERS\n\n\n@DATA_SAMPLERS.register_module()\nclass MultiSourceSampler(Sampler):\n    r\"\"\"Multi-Source Infinite Sampler.\n\n    According to the sampling ratio, sample data from different\n    datasets to form batches.\n\n    Args:\n        dataset (Sized): The dataset.\n        batch_size (int): Size of mini-batch.\n        source_ratio (list[int | float]): The sampling ratio of different\n            source datasets in a mini-batch.\n        shuffle (bool): Whether shuffle the dataset or not. Defaults to True.\n        seed (int, optional): Random seed. If None, set a random seed.\n            Defaults to None.\n\n    Examples:\n        >>> dataset_type = 'ConcatDataset'\n        >>> sub_dataset_type = 'CocoDataset'\n        >>> data_root = 'data/coco/'\n        >>> sup_ann = '../coco_semi_annos/instances_train2017.1@10.json'\n        >>> unsup_ann = '../coco_semi_annos/' \\\n        >>>             'instances_train2017.1@10-unlabeled.json'\n        >>> dataset = dict(type=dataset_type,\n        >>>     datasets=[\n        >>>         dict(\n        >>>             type=sub_dataset_type,\n        >>>             data_root=data_root,\n        >>>             ann_file=sup_ann,\n        >>>             data_prefix=dict(img='train2017/'),\n        >>>             filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        >>>             pipeline=sup_pipeline),\n        >>>         dict(\n        >>>             type=sub_dataset_type,\n        >>>             data_root=data_root,\n        >>>             ann_file=unsup_ann,\n        >>>             data_prefix=dict(img='train2017/'),\n        >>>             filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        >>>             pipeline=unsup_pipeline),\n        >>>         ])\n        >>>     train_dataloader = dict(\n        >>>         batch_size=5,\n        >>>         num_workers=5,\n        >>>         persistent_workers=True,\n        >>>         sampler=dict(type='MultiSourceSampler',\n        >>>             batch_size=5, source_ratio=[1, 4]),\n        >>>         batch_sampler=None,\n        >>>         dataset=dataset)\n    \"\"\"\n\n    def __init__(self,\n                 dataset: Sized,\n                 batch_size: int,\n                 source_ratio: List[Union[int, float]],\n                 shuffle: bool = True,\n                 seed: Optional[int] = None) -> None:\n\n        assert hasattr(dataset, 'cumulative_sizes'),\\\n            f'The dataset must be ConcatDataset, but get {dataset}'\n        assert isinstance(batch_size, int) and batch_size > 0, \\\n            'batch_size must be a positive integer value, ' \\\n            f'but got batch_size={batch_size}'\n        assert isinstance(source_ratio, list), \\\n            f'source_ratio must be a list, but got source_ratio={source_ratio}'\n        assert len(source_ratio) == len(dataset.cumulative_sizes), \\\n            'The length of source_ratio must be equal to ' \\\n            f'the number of datasets, but got source_ratio={source_ratio}'\n\n        rank, world_size = get_dist_info()\n        self.rank = rank\n        self.world_size = world_size\n\n        self.dataset = dataset\n        self.cumulative_sizes = [0] + dataset.cumulative_sizes\n        self.batch_size = batch_size\n        self.source_ratio = source_ratio\n\n        self.num_per_source = [\n            int(batch_size * sr / sum(source_ratio)) for sr in source_ratio\n        ]\n        self.num_per_source[0] = batch_size - sum(self.num_per_source[1:])\n\n        assert sum(self.num_per_source) == batch_size, \\\n            'The sum of num_per_source must be equal to ' \\\n            f'batch_size, but get {self.num_per_source}'\n\n        self.seed = sync_random_seed() if seed is None else seed\n        self.shuffle = shuffle\n        self.source2inds = {\n            source: self._indices_of_rank(len(ds))\n            for source, ds in enumerate(dataset.datasets)\n        }\n\n    def _infinite_indices(self, sample_size: int) -> Iterator[int]:\n        \"\"\"Infinitely yield a sequence of indices.\"\"\"\n        g = torch.Generator()\n        g.manual_seed(self.seed)\n        while True:\n            if self.shuffle:\n                yield from torch.randperm(sample_size, generator=g).tolist()\n            else:\n                yield from torch.arange(sample_size).tolist()\n\n    def _indices_of_rank(self, sample_size: int) -> Iterator[int]:\n        \"\"\"Slice the infinite indices by rank.\"\"\"\n        yield from itertools.islice(\n            self._infinite_indices(sample_size), self.rank, None,\n            self.world_size)\n\n    def __iter__(self) -> Iterator[int]:\n        batch_buffer = []\n        while True:\n            for source, num in enumerate(self.num_per_source):\n                batch_buffer_per_source = []\n                for idx in self.source2inds[source]:\n                    idx += self.cumulative_sizes[source]\n                    batch_buffer_per_source.append(idx)\n                    if len(batch_buffer_per_source) == num:\n                        batch_buffer += batch_buffer_per_source\n                        break\n            yield from batch_buffer\n            batch_buffer = []\n\n    def __len__(self) -> int:\n        return len(self.dataset)\n\n    def set_epoch(self, epoch: int) -> None:\n        \"\"\"Not supported in `epoch-based runner.\"\"\"\n        pass\n\n\n@DATA_SAMPLERS.register_module()\nclass GroupMultiSourceSampler(MultiSourceSampler):\n    r\"\"\"Group Multi-Source Infinite Sampler.\n\n    According to the sampling ratio, sample data from different\n    datasets but the same group to form batches.\n\n    Args:\n        dataset (Sized): The dataset.\n        batch_size (int): Size of mini-batch.\n        source_ratio (list[int | float]): The sampling ratio of different\n            source datasets in a mini-batch.\n        shuffle (bool): Whether shuffle the dataset or not. Defaults to True.\n        seed (int, optional): Random seed. If None, set a random seed.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 dataset: BaseDataset,\n                 batch_size: int,\n                 source_ratio: List[Union[int, float]],\n                 shuffle: bool = True,\n                 seed: Optional[int] = None) -> None:\n        super().__init__(\n            dataset=dataset,\n            batch_size=batch_size,\n            source_ratio=source_ratio,\n            shuffle=shuffle,\n            seed=seed)\n\n        self._get_source_group_info()\n        self.group_source2inds = [{\n            source:\n            self._indices_of_rank(self.group2size_per_source[source][group])\n            for source in range(len(dataset.datasets))\n        } for group in range(len(self.group_ratio))]\n\n    def _get_source_group_info(self) -> None:\n        self.group2size_per_source = [{0: 0, 1: 0}, {0: 0, 1: 0}]\n        self.group2inds_per_source = [{0: [], 1: []}, {0: [], 1: []}]\n        for source, dataset in enumerate(self.dataset.datasets):\n            for idx in range(len(dataset)):\n                data_info = dataset.get_data_info(idx)\n                width, height = data_info['width'], data_info['height']\n                group = 0 if width < height else 1\n                self.group2size_per_source[source][group] += 1\n                self.group2inds_per_source[source][group].append(idx)\n\n        self.group_sizes = np.zeros(2, dtype=np.int64)\n        for group2size in self.group2size_per_source:\n            for group, size in group2size.items():\n                self.group_sizes[group] += size\n        self.group_ratio = self.group_sizes / sum(self.group_sizes)\n\n    def __iter__(self) -> Iterator[int]:\n        batch_buffer = []\n        while True:\n            group = np.random.choice(\n                list(range(len(self.group_ratio))), p=self.group_ratio)\n            for source, num in enumerate(self.num_per_source):\n                batch_buffer_per_source = []\n                for idx in self.group_source2inds[group][source]:\n                    idx = self.group2inds_per_source[source][group][\n                        idx] + self.cumulative_sizes[source]\n                    batch_buffer_per_source.append(idx)\n                    if len(batch_buffer_per_source) == num:\n                        batch_buffer += batch_buffer_per_source\n                        break\n            yield from batch_buffer\n            batch_buffer = []\n"
  },
  {
    "path": "mmdet/datasets/samplers/track_img_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nimport random\nfrom typing import Iterator, Optional, Sized\n\nimport numpy as np\nfrom mmengine.dataset import ClassBalancedDataset, ConcatDataset\nfrom mmengine.dist import get_dist_info, sync_random_seed\nfrom torch.utils.data import Sampler\n\nfrom mmdet.registry import DATA_SAMPLERS\nfrom ..base_video_dataset import BaseVideoDataset\n\n\n@DATA_SAMPLERS.register_module()\nclass TrackImgSampler(Sampler):\n    \"\"\"Sampler that providing image-level sampling outputs for video datasets\n    in tracking tasks. It could be both used in both distributed and\n    non-distributed environment.\n    If using the default sampler in pytorch, the subsequent data receiver will\n    get one video, which is not desired in some cases:\n    (Take a non-distributed environment as an example)\n    1. In test mode, we want only one image is fed into the data pipeline. This\n    is in consideration of memory usage since feeding the whole video commonly\n    requires a large amount of memory (>=20G on MOTChallenge17 dataset), which\n    is not available in some machines.\n    2. In training mode, we may want to make sure all the images in one video\n    are randomly sampled once in one epoch and this can not be guaranteed in\n    the default sampler in pytorch.\n\n    Args:\n        dataset (Sized): Dataset used for sampling.\n        seed (int, optional): random seed used to shuffle the sampler. This\n            number should be identical across all processes in the distributed\n            group. Defaults to None.\n    \"\"\"\n\n    def __init__(\n        self,\n        dataset: Sized,\n        seed: Optional[int] = None,\n    ) -> None:\n        rank, world_size = get_dist_info()\n        self.rank = rank\n        self.world_size = world_size\n        self.epoch = 0\n        if seed is None:\n            self.seed = sync_random_seed()\n        else:\n            self.seed = seed\n\n        self.dataset = dataset\n        self.indices = []\n        # Hard code here to handle different dataset wrapper\n        if isinstance(self.dataset, ConcatDataset):\n            cat_datasets = self.dataset.datasets\n            assert isinstance(\n                cat_datasets[0], BaseVideoDataset\n            ), f'expected BaseVideoDataset, but got {type(cat_datasets[0])}'\n            self.test_mode = cat_datasets[0].test_mode\n            assert not self.test_mode, \"'ConcatDataset' should not exist in \"\n            'test mode'\n            for dataset in cat_datasets:\n                num_videos = len(dataset)\n                for video_ind in range(num_videos):\n                    self.indices.extend([\n                        (video_ind, frame_ind) for frame_ind in range(\n                            dataset.get_len_per_video(video_ind))\n                    ])\n        elif isinstance(self.dataset, ClassBalancedDataset):\n            ori_dataset = self.dataset.dataset\n            assert isinstance(\n                ori_dataset, BaseVideoDataset\n            ), f'expected BaseVideoDataset, but got {type(ori_dataset)}'\n            self.test_mode = ori_dataset.test_mode\n            assert not self.test_mode, \"'ClassBalancedDataset' should not \"\n            'exist in test mode'\n            video_indices = self.dataset.repeat_indices\n            for index in video_indices:\n                self.indices.extend([(index, frame_ind) for frame_ind in range(\n                    ori_dataset.get_len_per_video(index))])\n        else:\n            assert isinstance(\n                self.dataset, BaseVideoDataset\n            ), 'TrackImgSampler is only supported in BaseVideoDataset or '\n            'dataset wrapper: ClassBalancedDataset and ConcatDataset, but '\n            f'got {type(self.dataset)} '\n            self.test_mode = self.dataset.test_mode\n            num_videos = len(self.dataset)\n\n            if self.test_mode:\n                # in test mode, the images belong to the same video must be put\n                # on the same device.\n                if num_videos < self.world_size:\n                    raise ValueError(f'only {num_videos} videos loaded,'\n                                     f'but {self.world_size} gpus were given.')\n                chunks = np.array_split(\n                    list(range(num_videos)), self.world_size)\n                for videos_inds in chunks:\n                    indices_chunk = []\n                    for video_ind in videos_inds:\n                        indices_chunk.extend([\n                            (video_ind, frame_ind) for frame_ind in range(\n                                self.dataset.get_len_per_video(video_ind))\n                        ])\n                    self.indices.append(indices_chunk)\n            else:\n                for video_ind in range(num_videos):\n                    self.indices.extend([\n                        (video_ind, frame_ind) for frame_ind in range(\n                            self.dataset.get_len_per_video(video_ind))\n                    ])\n\n        if self.test_mode:\n            self.num_samples = len(self.indices[self.rank])\n            self.total_size = sum(\n                [len(index_list) for index_list in self.indices])\n        else:\n            self.num_samples = int(\n                math.ceil(len(self.indices) * 1.0 / self.world_size))\n            self.total_size = self.num_samples * self.world_size\n\n    def __iter__(self) -> Iterator:\n        if self.test_mode:\n            # in test mode, the order of frames can not be shuffled.\n            indices = self.indices[self.rank]\n        else:\n            # deterministically shuffle based on epoch\n            rng = random.Random(self.epoch + self.seed)\n            indices = rng.sample(self.indices, len(self.indices))\n\n            # add extra samples to make it evenly divisible\n            indices += indices[:(self.total_size - len(indices))]\n            assert len(indices) == self.total_size\n\n            # subsample\n            indices = indices[self.rank:self.total_size:self.world_size]\n            assert len(indices) == self.num_samples\n\n        return iter(indices)\n\n    def __len__(self):\n        return self.num_samples\n\n    def set_epoch(self, epoch):\n        self.epoch = epoch\n"
  },
  {
    "path": "mmdet/datasets/transforms/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .augment_wrappers import AutoAugment, RandAugment\nfrom .colorspace import (AutoContrast, Brightness, Color, ColorTransform,\n                         Contrast, Equalize, Invert, Posterize, Sharpness,\n                         Solarize, SolarizeAdd)\nfrom .formatting import (ImageToTensor, PackDetInputs, PackReIDInputs,\n                         PackTrackInputs, ToTensor, Transpose)\nfrom .frame_sampling import BaseFrameSample, UniformRefFrameSample\nfrom .geometric import (GeomTransform, Rotate, ShearX, ShearY, TranslateX,\n                        TranslateY)\nfrom .instaboost import InstaBoost\nfrom .loading import (FilterAnnotations, InferencerLoader, LoadAnnotations,\n                      LoadEmptyAnnotations, LoadImageFromNDArray,\n                      LoadMultiChannelImageFromFiles, LoadPanopticAnnotations,\n                      LoadProposals, LoadTrackAnnotations)\nfrom .text_transformers import LoadTextAnnotations, RandomSamplingNegPos\nfrom .transformers_glip import GTBoxSubOne_GLIP, RandomFlip_GLIP\nfrom .transforms import (Albu, CachedMixUp, CachedMosaic, CopyPaste, CutOut,\n                         Expand, FixScaleResize, FixShapeResize,\n                         MinIoURandomCrop, MixUp, Mosaic, Pad,\n                         PhotoMetricDistortion, RandomAffine,\n                         RandomCenterCropPad, RandomCrop, RandomErasing,\n                         RandomFlip, RandomShift, Resize, ResizeShortestEdge,\n                         SegRescale, YOLOXHSVRandomAug)\nfrom .wrappers import MultiBranch, ProposalBroadcaster, RandomOrder\n\n__all__ = [\n    'PackDetInputs', 'ToTensor', 'ImageToTensor', 'Transpose',\n    'LoadImageFromNDArray', 'LoadAnnotations', 'LoadPanopticAnnotations',\n    'LoadMultiChannelImageFromFiles', 'LoadProposals', 'Resize', 'RandomFlip',\n    'RandomCrop', 'SegRescale', 'MinIoURandomCrop', 'Expand',\n    'PhotoMetricDistortion', 'Albu', 'InstaBoost', 'RandomCenterCropPad',\n    'AutoAugment', 'CutOut', 'ShearX', 'ShearY', 'Rotate', 'Color', 'Equalize',\n    'Brightness', 'Contrast', 'TranslateX', 'TranslateY', 'RandomShift',\n    'Mosaic', 'MixUp', 'RandomAffine', 'YOLOXHSVRandomAug', 'CopyPaste',\n    'FilterAnnotations', 'Pad', 'GeomTransform', 'ColorTransform',\n    'RandAugment', 'Sharpness', 'Solarize', 'SolarizeAdd', 'Posterize',\n    'AutoContrast', 'Invert', 'MultiBranch', 'RandomErasing',\n    'LoadEmptyAnnotations', 'RandomOrder', 'CachedMosaic', 'CachedMixUp',\n    'FixShapeResize', 'ProposalBroadcaster', 'InferencerLoader',\n    'LoadTrackAnnotations', 'BaseFrameSample', 'UniformRefFrameSample',\n    'PackTrackInputs', 'PackReIDInputs', 'FixScaleResize',\n    'ResizeShortestEdge', 'GTBoxSubOne_GLIP', 'RandomFlip_GLIP',\n    'RandomSamplingNegPos', 'LoadTextAnnotations'\n]\n"
  },
  {
    "path": "mmdet/datasets/transforms/augment_wrappers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Union\n\nimport numpy as np\nfrom mmcv.transforms import RandomChoice\nfrom mmcv.transforms.utils import cache_randomness\nfrom mmengine.config import ConfigDict\n\nfrom mmdet.registry import TRANSFORMS\n\n# AutoAugment uses reinforcement learning to search for\n# some widely useful data augmentation strategies,\n# here we provide AUTOAUG_POLICIES_V0.\n# For AUTOAUG_POLICIES_V0, each tuple is an augmentation\n# operation of the form (operation, probability, magnitude).\n# Each element in policies is a policy that will be applied\n# sequentially on the image.\n\n# RandAugment defines a data augmentation search space, RANDAUG_SPACE,\n# sampling 1~3 data augmentations each time, and\n# setting the magnitude of each data augmentation randomly,\n# which will be applied sequentially on the image.\n\n_MAX_LEVEL = 10\n\nAUTOAUG_POLICIES_V0 = [\n    [('Equalize', 0.8, 1), ('ShearY', 0.8, 4)],\n    [('Color', 0.4, 9), ('Equalize', 0.6, 3)],\n    [('Color', 0.4, 1), ('Rotate', 0.6, 8)],\n    [('Solarize', 0.8, 3), ('Equalize', 0.4, 7)],\n    [('Solarize', 0.4, 2), ('Solarize', 0.6, 2)],\n    [('Color', 0.2, 0), ('Equalize', 0.8, 8)],\n    [('Equalize', 0.4, 8), ('SolarizeAdd', 0.8, 3)],\n    [('ShearX', 0.2, 9), ('Rotate', 0.6, 8)],\n    [('Color', 0.6, 1), ('Equalize', 1.0, 2)],\n    [('Invert', 0.4, 9), ('Rotate', 0.6, 0)],\n    [('Equalize', 1.0, 9), ('ShearY', 0.6, 3)],\n    [('Color', 0.4, 7), ('Equalize', 0.6, 0)],\n    [('Posterize', 0.4, 6), ('AutoContrast', 0.4, 7)],\n    [('Solarize', 0.6, 8), ('Color', 0.6, 9)],\n    [('Solarize', 0.2, 4), ('Rotate', 0.8, 9)],\n    [('Rotate', 1.0, 7), ('TranslateY', 0.8, 9)],\n    [('ShearX', 0.0, 0), ('Solarize', 0.8, 4)],\n    [('ShearY', 0.8, 0), ('Color', 0.6, 4)],\n    [('Color', 1.0, 0), ('Rotate', 0.6, 2)],\n    [('Equalize', 0.8, 4), ('Equalize', 0.0, 8)],\n    [('Equalize', 1.0, 4), ('AutoContrast', 0.6, 2)],\n    [('ShearY', 0.4, 7), ('SolarizeAdd', 0.6, 7)],\n    [('Posterize', 0.8, 2), ('Solarize', 0.6, 10)],\n    [('Solarize', 0.6, 8), ('Equalize', 0.6, 1)],\n    [('Color', 0.8, 6), ('Rotate', 0.4, 5)],\n]\n\n\ndef policies_v0():\n    \"\"\"Autoaugment policies that was used in AutoAugment Paper.\"\"\"\n    policies = list()\n    for policy_args in AUTOAUG_POLICIES_V0:\n        policy = list()\n        for args in policy_args:\n            policy.append(dict(type=args[0], prob=args[1], level=args[2]))\n        policies.append(policy)\n    return policies\n\n\nRANDAUG_SPACE = [[dict(type='AutoContrast')], [dict(type='Equalize')],\n                 [dict(type='Invert')], [dict(type='Rotate')],\n                 [dict(type='Posterize')], [dict(type='Solarize')],\n                 [dict(type='SolarizeAdd')], [dict(type='Color')],\n                 [dict(type='Contrast')], [dict(type='Brightness')],\n                 [dict(type='Sharpness')], [dict(type='ShearX')],\n                 [dict(type='ShearY')], [dict(type='TranslateX')],\n                 [dict(type='TranslateY')]]\n\n\ndef level_to_mag(level: Optional[int], min_mag: float,\n                 max_mag: float) -> float:\n    \"\"\"Map from level to magnitude.\"\"\"\n    if level is None:\n        return round(np.random.rand() * (max_mag - min_mag) + min_mag, 1)\n    else:\n        return round(level / _MAX_LEVEL * (max_mag - min_mag) + min_mag, 1)\n\n\n@TRANSFORMS.register_module()\nclass AutoAugment(RandomChoice):\n    \"\"\"Auto augmentation.\n\n    This data augmentation is proposed in `AutoAugment: Learning\n    Augmentation Policies from Data <https://arxiv.org/abs/1805.09501>`_\n    and in `Learning Data Augmentation Strategies for Object Detection\n    <https://arxiv.org/pdf/1906.11172>`_.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes\n    - gt_bboxes_labels\n    - gt_masks\n    - gt_ignore_flags\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        policies (List[List[Union[dict, ConfigDict]]]):\n            The policies of auto augmentation.Each policy in ``policies``\n            is a specific augmentation policy, and is composed by several\n            augmentations. When AutoAugment is called, a random policy in\n            ``policies`` will be selected to augment images.\n            Defaults to policy_v0().\n        prob (list[float], optional): The probabilities associated\n            with each policy. The length should be equal to the policy\n            number and the sum should be 1. If not given, a uniform\n            distribution will be assumed. Defaults to None.\n\n    Examples:\n        >>> policies = [\n        >>>     [\n        >>>         dict(type='Sharpness', prob=0.0, level=8),\n        >>>         dict(type='ShearX', prob=0.4, level=0,)\n        >>>     ],\n        >>>     [\n        >>>         dict(type='Rotate', prob=0.6, level=10),\n        >>>         dict(type='Color', prob=1.0, level=6)\n        >>>     ]\n        >>> ]\n        >>> augmentation = AutoAugment(policies)\n        >>> img = np.ones(100, 100, 3)\n        >>> gt_bboxes = np.ones(10, 4)\n        >>> results = dict(img=img, gt_bboxes=gt_bboxes)\n        >>> results = augmentation(results)\n    \"\"\"\n\n    def __init__(self,\n                 policies: List[List[Union[dict, ConfigDict]]] = policies_v0(),\n                 prob: Optional[List[float]] = None) -> None:\n        assert isinstance(policies, list) and len(policies) > 0, \\\n            'Policies must be a non-empty list.'\n        for policy in policies:\n            assert isinstance(policy, list) and len(policy) > 0, \\\n                'Each policy in policies must be a non-empty list.'\n            for augment in policy:\n                assert isinstance(augment, dict) and 'type' in augment, \\\n                    'Each specific augmentation must be a dict with key' \\\n                    ' \"type\".'\n        super().__init__(transforms=policies, prob=prob)\n        self.policies = policies\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}(policies={self.policies}, ' \\\n               f'prob={self.prob})'\n\n\n@TRANSFORMS.register_module()\nclass RandAugment(RandomChoice):\n    \"\"\"Rand augmentation.\n\n    This data augmentation is proposed in `RandAugment:\n    Practical automated data augmentation with a reduced\n    search space <https://arxiv.org/abs/1909.13719>`_.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes\n    - gt_bboxes_labels\n    - gt_masks\n    - gt_ignore_flags\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        aug_space (List[List[Union[dict, ConfigDict]]]): The augmentation space\n            of rand augmentation. Each augmentation transform in ``aug_space``\n            is a specific transform, and is composed by several augmentations.\n            When RandAugment is called, a random transform in ``aug_space``\n            will be selected to augment images. Defaults to aug_space.\n        aug_num (int): Number of augmentation to apply equentially.\n            Defaults to 2.\n        prob (list[float], optional): The probabilities associated with\n            each augmentation. The length should be equal to the\n            augmentation space and the sum should be 1. If not given,\n            a uniform distribution will be assumed. Defaults to None.\n\n    Examples:\n        >>> aug_space = [\n        >>>     dict(type='Sharpness'),\n        >>>     dict(type='ShearX'),\n        >>>     dict(type='Color'),\n        >>>     ],\n        >>> augmentation = RandAugment(aug_space)\n        >>> img = np.ones(100, 100, 3)\n        >>> gt_bboxes = np.ones(10, 4)\n        >>> results = dict(img=img, gt_bboxes=gt_bboxes)\n        >>> results = augmentation(results)\n    \"\"\"\n\n    def __init__(self,\n                 aug_space: List[Union[dict, ConfigDict]] = RANDAUG_SPACE,\n                 aug_num: int = 2,\n                 prob: Optional[List[float]] = None) -> None:\n        assert isinstance(aug_space, list) and len(aug_space) > 0, \\\n            'Augmentation space must be a non-empty list.'\n        for aug in aug_space:\n            assert isinstance(aug, list) and len(aug) == 1, \\\n                'Each augmentation in aug_space must be a list.'\n            for transform in aug:\n                assert isinstance(transform, dict) and 'type' in transform, \\\n                    'Each specific transform must be a dict with key' \\\n                    ' \"type\".'\n        super().__init__(transforms=aug_space, prob=prob)\n        self.aug_space = aug_space\n        self.aug_num = aug_num\n\n    @cache_randomness\n    def random_pipeline_index(self):\n        indices = np.arange(len(self.transforms))\n        return np.random.choice(\n            indices, self.aug_num, p=self.prob, replace=False)\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to use RandAugment.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Result dict with RandAugment.\n        \"\"\"\n        for idx in self.random_pipeline_index():\n            results = self.transforms[idx](results)\n        return results\n\n    def __repr__(self) -> str:\n        return f'{self.__class__.__name__}(' \\\n               f'aug_space={self.aug_space}, '\\\n               f'aug_num={self.aug_num}, ' \\\n               f'prob={self.prob})'\n"
  },
  {
    "path": "mmdet/datasets/transforms/colorspace.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Optional\n\nimport mmcv\nimport numpy as np\nfrom mmcv.transforms import BaseTransform\nfrom mmcv.transforms.utils import cache_randomness\n\nfrom mmdet.registry import TRANSFORMS\nfrom .augment_wrappers import _MAX_LEVEL, level_to_mag\n\n\n@TRANSFORMS.register_module()\nclass ColorTransform(BaseTransform):\n    \"\"\"Base class for color transformations. All color transformations need to\n    inherit from this base class. ``ColorTransform`` unifies the class\n    attributes and class functions of color transformations (Color, Brightness,\n    Contrast, Sharpness, Solarize, SolarizeAdd, Equalize, AutoContrast, Invert,\n    and Posterize), and only distort color channels, without impacting the\n    locations of the instances.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing the geometric\n            transformation and should be in range [0, 1]. Defaults to 1.0.\n        level (int, optional): The level should be in range [0, _MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for color transformation.\n            Defaults to 0.1.\n        max_mag (float): The maximum magnitude for color transformation.\n            Defaults to 1.9.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.1,\n                 max_mag: float = 1.9) -> None:\n        assert 0 <= prob <= 1.0, f'The probability of the transformation ' \\\n                                 f'should be in range [0,1], got {prob}.'\n        assert level is None or isinstance(level, int), \\\n            f'The level should be None or type int, got {type(level)}.'\n        assert level is None or 0 <= level <= _MAX_LEVEL, \\\n            f'The level should be in range [0,{_MAX_LEVEL}], got {level}.'\n        assert isinstance(min_mag, float), \\\n            f'min_mag should be type float, got {type(min_mag)}.'\n        assert isinstance(max_mag, float), \\\n            f'max_mag should be type float, got {type(max_mag)}.'\n        assert min_mag <= max_mag, \\\n            f'min_mag should smaller than max_mag, ' \\\n            f'got min_mag={min_mag} and max_mag={max_mag}'\n        self.prob = prob\n        self.level = level\n        self.min_mag = min_mag\n        self.max_mag = max_mag\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Transform the image.\"\"\"\n        pass\n\n    @cache_randomness\n    def _random_disable(self):\n        \"\"\"Randomly disable the transform.\"\"\"\n        return np.random.rand() > self.prob\n\n    @cache_randomness\n    def _get_mag(self):\n        \"\"\"Get the magnitude of the transform.\"\"\"\n        return level_to_mag(self.level, self.min_mag, self.max_mag)\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function for images.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Transformed results.\n        \"\"\"\n\n        if self._random_disable():\n            return results\n        mag = self._get_mag()\n        self._transform_img(results, mag)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(prob={self.prob}, '\n        repr_str += f'level={self.level}, '\n        repr_str += f'min_mag={self.min_mag}, '\n        repr_str += f'max_mag={self.max_mag})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass Color(ColorTransform):\n    \"\"\"Adjust the color balance of the image, in a manner similar to the\n    controls on a colour TV set. A magnitude=0 gives a black & white image,\n    whereas magnitude=1 gives the original image. The bboxes, masks and\n    segmentations are not modified.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing Color transformation.\n            Defaults to 1.0.\n        level (int, optional): Should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for Color transformation.\n            Defaults to 0.1.\n        max_mag (float): The maximum magnitude for Color transformation.\n            Defaults to 1.9.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.1,\n                 max_mag: float = 1.9) -> None:\n        assert 0. <= min_mag <= 2.0, \\\n            f'min_mag for Color should be in range [0,2], got {min_mag}.'\n        assert 0. <= max_mag <= 2.0, \\\n            f'max_mag for Color should be in range [0,2], got {max_mag}.'\n        super().__init__(\n            prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Apply Color transformation to image.\"\"\"\n        # NOTE defaultly the image should be BGR format\n        img = results['img']\n        results['img'] = mmcv.adjust_color(img, mag).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass Brightness(ColorTransform):\n    \"\"\"Adjust the brightness of the image. A magnitude=0 gives a black image,\n    whereas magnitude=1 gives the original image. The bboxes, masks and\n    segmentations are not modified.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing Brightness transformation.\n            Defaults to 1.0.\n        level (int, optional): Should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for Brightness transformation.\n            Defaults to 0.1.\n        max_mag (float): The maximum magnitude for Brightness transformation.\n            Defaults to 1.9.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.1,\n                 max_mag: float = 1.9) -> None:\n        assert 0. <= min_mag <= 2.0, \\\n            f'min_mag for Brightness should be in range [0,2], got {min_mag}.'\n        assert 0. <= max_mag <= 2.0, \\\n            f'max_mag for Brightness should be in range [0,2], got {max_mag}.'\n        super().__init__(\n            prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Adjust the brightness of image.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.adjust_brightness(img, mag).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass Contrast(ColorTransform):\n    \"\"\"Control the contrast of the image. A magnitude=0 gives a gray image,\n    whereas magnitude=1 gives the original imageThe bboxes, masks and\n    segmentations are not modified.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing Contrast transformation.\n            Defaults to 1.0.\n        level (int, optional): Should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for Contrast transformation.\n            Defaults to 0.1.\n        max_mag (float): The maximum magnitude for Contrast transformation.\n            Defaults to 1.9.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.1,\n                 max_mag: float = 1.9) -> None:\n        assert 0. <= min_mag <= 2.0, \\\n            f'min_mag for Contrast should be in range [0,2], got {min_mag}.'\n        assert 0. <= max_mag <= 2.0, \\\n            f'max_mag for Contrast should be in range [0,2], got {max_mag}.'\n        super().__init__(\n            prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Adjust the image contrast.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.adjust_contrast(img, mag).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass Sharpness(ColorTransform):\n    \"\"\"Adjust images sharpness. A positive magnitude would enhance the\n    sharpness and a negative magnitude would make the image blurry. A\n    magnitude=0 gives the origin img.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing Sharpness transformation.\n            Defaults to 1.0.\n        level (int, optional): Should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for Sharpness transformation.\n            Defaults to 0.1.\n        max_mag (float): The maximum magnitude for Sharpness transformation.\n            Defaults to 1.9.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.1,\n                 max_mag: float = 1.9) -> None:\n        assert 0. <= min_mag <= 2.0, \\\n            f'min_mag for Sharpness should be in range [0,2], got {min_mag}.'\n        assert 0. <= max_mag <= 2.0, \\\n            f'max_mag for Sharpness should be in range [0,2], got {max_mag}.'\n        super().__init__(\n            prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Adjust the image sharpness.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.adjust_sharpness(img, mag).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass Solarize(ColorTransform):\n    \"\"\"Solarize images (Invert all pixels above a threshold value of\n    magnitude.).\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing Solarize transformation.\n            Defaults to 1.0.\n        level (int, optional): Should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for Solarize transformation.\n            Defaults to 0.0.\n        max_mag (float): The maximum magnitude for Solarize transformation.\n            Defaults to 256.0.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 256.0) -> None:\n        assert 0. <= min_mag <= 256.0, f'min_mag for Solarize should be ' \\\n                                       f'in range [0, 256], got {min_mag}.'\n        assert 0. <= max_mag <= 256.0, f'max_mag for Solarize should be ' \\\n                                       f'in range [0, 256], got {max_mag}.'\n        super().__init__(\n            prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Invert all pixel values above magnitude.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.solarize(img, mag).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass SolarizeAdd(ColorTransform):\n    \"\"\"SolarizeAdd images. For each pixel in the image that is less than 128,\n    add an additional amount to it decided by the magnitude.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing SolarizeAdd\n            transformation. Defaults to 1.0.\n        level (int, optional): Should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for SolarizeAdd transformation.\n            Defaults to 0.0.\n        max_mag (float): The maximum magnitude for SolarizeAdd transformation.\n            Defaults to 110.0.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 110.0) -> None:\n        assert 0. <= min_mag <= 110.0, f'min_mag for SolarizeAdd should be ' \\\n                                       f'in range [0, 110], got {min_mag}.'\n        assert 0. <= max_mag <= 110.0, f'max_mag for SolarizeAdd should be ' \\\n                                       f'in range [0, 110], got {max_mag}.'\n        super().__init__(\n            prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"SolarizeAdd the image.\"\"\"\n        img = results['img']\n        img_solarized = np.where(img < 128, np.minimum(img + mag, 255), img)\n        results['img'] = img_solarized.astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass Posterize(ColorTransform):\n    \"\"\"Posterize images (reduce the number of bits for each color channel).\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing Posterize\n            transformation. Defaults to 1.0.\n        level (int, optional): Should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for Posterize transformation.\n            Defaults to 0.0.\n        max_mag (float): The maximum magnitude for Posterize transformation.\n            Defaults to 4.0.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 4.0) -> None:\n        assert 0. <= min_mag <= 8.0, f'min_mag for Posterize should be ' \\\n                                     f'in range [0, 8], got {min_mag}.'\n        assert 0. <= max_mag <= 8.0, f'max_mag for Posterize should be ' \\\n                                     f'in range [0, 8], got {max_mag}.'\n        super().__init__(\n            prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Posterize the image.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.posterize(img, math.ceil(mag)).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass Equalize(ColorTransform):\n    \"\"\"Equalize the image histogram. The bboxes, masks and segmentations are\n    not modified.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing Equalize transformation.\n            Defaults to 1.0.\n        level (int, optional): No use for Equalize transformation.\n            Defaults to None.\n        min_mag (float): No use for Equalize transformation. Defaults to 0.1.\n        max_mag (float): No use for Equalize transformation. Defaults to 1.9.\n    \"\"\"\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Equalizes the histogram of one image.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.imequalize(img).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass AutoContrast(ColorTransform):\n    \"\"\"Auto adjust image contrast.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing AutoContrast should\n             be in range [0, 1]. Defaults to 1.0.\n        level (int, optional): No use for AutoContrast transformation.\n            Defaults to None.\n        min_mag (float): No use for AutoContrast transformation.\n            Defaults to 0.1.\n        max_mag (float): No use for AutoContrast transformation.\n            Defaults to 1.9.\n    \"\"\"\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Auto adjust image contrast.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.auto_contrast(img).astype(img.dtype)\n\n\n@TRANSFORMS.register_module()\nclass Invert(ColorTransform):\n    \"\"\"Invert images.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        prob (float): The probability for performing invert therefore should\n             be in range [0, 1]. Defaults to 1.0.\n        level (int, optional): No use for Invert transformation.\n            Defaults to None.\n        min_mag (float): No use for Invert transformation. Defaults to 0.1.\n        max_mag (float): No use for Invert transformation. Defaults to 1.9.\n    \"\"\"\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Invert the image.\"\"\"\n        img = results['img']\n        results['img'] = mmcv.iminvert(img).astype(img.dtype)\n"
  },
  {
    "path": "mmdet/datasets/transforms/formatting.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Sequence\n\nimport numpy as np\nfrom mmcv.transforms import to_tensor\nfrom mmcv.transforms.base import BaseTransform\nfrom mmengine.structures import InstanceData, PixelData\n\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.structures import DetDataSample, ReIDDataSample, TrackDataSample\nfrom mmdet.structures.bbox import BaseBoxes\n\n\n@TRANSFORMS.register_module()\nclass PackDetInputs(BaseTransform):\n    \"\"\"Pack the inputs data for the detection / semantic segmentation /\n    panoptic segmentation.\n\n    The ``img_meta`` item is always populated.  The contents of the\n    ``img_meta`` dictionary depends on ``meta_keys``. By default this includes:\n\n        - ``img_id``: id of the image\n\n        - ``img_path``: path to the image file\n\n        - ``ori_shape``: original shape of the image as a tuple (h, w)\n\n        - ``img_shape``: shape of the image input to the network as a tuple \\\n            (h, w).  Note that images may be zero padded on the \\\n            bottom/right if the batch tensor is larger than this shape.\n\n        - ``scale_factor``: a float indicating the preprocessing scale\n\n        - ``flip``: a boolean indicating if image flip transform was used\n\n        - ``flip_direction``: the flipping direction\n\n    Args:\n        meta_keys (Sequence[str], optional): Meta keys to be converted to\n            ``mmcv.DataContainer`` and collected in ``data[img_metas]``.\n            Default: ``('img_id', 'img_path', 'ori_shape', 'img_shape',\n            'scale_factor', 'flip', 'flip_direction')``\n    \"\"\"\n    mapping_table = {\n        'gt_bboxes': 'bboxes',\n        'gt_bboxes_labels': 'labels',\n        'gt_masks': 'masks'\n    }\n\n    def __init__(self,\n                 meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                            'scale_factor', 'flip', 'flip_direction')):\n        self.meta_keys = meta_keys\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Method to pack the input data.\n\n        Args:\n            results (dict): Result dict from the data pipeline.\n\n        Returns:\n            dict:\n\n            - 'inputs' (obj:`torch.Tensor`): The forward data of models.\n            - 'data_sample' (obj:`DetDataSample`): The annotation info of the\n                sample.\n        \"\"\"\n        packed_results = dict()\n        if 'img' in results:\n            img = results['img']\n            if len(img.shape) < 3:\n                img = np.expand_dims(img, -1)\n            # To improve the computational speed by by 3-5 times, apply:\n            # If image is not contiguous, use\n            # `numpy.transpose()` followed by `numpy.ascontiguousarray()`\n            # If image is already contiguous, use\n            # `torch.permute()` followed by `torch.contiguous()`\n            # Refer to https://github.com/open-mmlab/mmdetection/pull/9533\n            # for more details\n            if not img.flags.c_contiguous:\n                img = np.ascontiguousarray(img.transpose(2, 0, 1))\n                img = to_tensor(img)\n            else:\n                img = to_tensor(img).permute(2, 0, 1).contiguous()\n\n            packed_results['inputs'] = img\n\n        if 'gt_ignore_flags' in results:\n            valid_idx = np.where(results['gt_ignore_flags'] == 0)[0]\n            ignore_idx = np.where(results['gt_ignore_flags'] == 1)[0]\n\n        data_sample = DetDataSample()\n        instance_data = InstanceData()\n        ignore_instance_data = InstanceData()\n\n        for key in self.mapping_table.keys():\n            if key not in results:\n                continue\n            if key == 'gt_masks' or isinstance(results[key], BaseBoxes):\n                if 'gt_ignore_flags' in results:\n                    instance_data[\n                        self.mapping_table[key]] = results[key][valid_idx]\n                    ignore_instance_data[\n                        self.mapping_table[key]] = results[key][ignore_idx]\n                else:\n                    instance_data[self.mapping_table[key]] = results[key]\n            else:\n                if 'gt_ignore_flags' in results:\n                    instance_data[self.mapping_table[key]] = to_tensor(\n                        results[key][valid_idx])\n                    ignore_instance_data[self.mapping_table[key]] = to_tensor(\n                        results[key][ignore_idx])\n                else:\n                    instance_data[self.mapping_table[key]] = to_tensor(\n                        results[key])\n        data_sample.gt_instances = instance_data\n        data_sample.ignored_instances = ignore_instance_data\n\n        if 'proposals' in results:\n            proposals = InstanceData(\n                bboxes=to_tensor(results['proposals']),\n                scores=to_tensor(results['proposals_scores']))\n            data_sample.proposals = proposals\n\n        if 'gt_seg_map' in results:\n            gt_sem_seg_data = dict(\n                sem_seg=to_tensor(results['gt_seg_map'][None, ...].copy()))\n            gt_sem_seg_data = PixelData(**gt_sem_seg_data)\n            if 'ignore_index' in results:\n                metainfo = dict(ignore_index=results['ignore_index'])\n                gt_sem_seg_data.set_metainfo(metainfo)\n            data_sample.gt_sem_seg = gt_sem_seg_data\n\n        img_meta = {}\n        for key in self.meta_keys:\n            if key in results:\n                img_meta[key] = results[key]\n        data_sample.set_metainfo(img_meta)\n        packed_results['data_samples'] = data_sample\n\n        return packed_results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(meta_keys={self.meta_keys})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass ToTensor:\n    \"\"\"Convert some results to :obj:`torch.Tensor` by given keys.\n\n    Args:\n        keys (Sequence[str]): Keys that need to be converted to Tensor.\n    \"\"\"\n\n    def __init__(self, keys):\n        self.keys = keys\n\n    def __call__(self, results):\n        \"\"\"Call function to convert data in results to :obj:`torch.Tensor`.\n\n        Args:\n            results (dict): Result dict contains the data to convert.\n\n        Returns:\n            dict: The result dict contains the data converted\n                to :obj:`torch.Tensor`.\n        \"\"\"\n        for key in self.keys:\n            results[key] = to_tensor(results[key])\n        return results\n\n    def __repr__(self):\n        return self.__class__.__name__ + f'(keys={self.keys})'\n\n\n@TRANSFORMS.register_module()\nclass ImageToTensor:\n    \"\"\"Convert image to :obj:`torch.Tensor` by given keys.\n\n    The dimension order of input image is (H, W, C). The pipeline will convert\n    it to (C, H, W). If only 2 dimension (H, W) is given, the output would be\n    (1, H, W).\n\n    Args:\n        keys (Sequence[str]): Key of images to be converted to Tensor.\n    \"\"\"\n\n    def __init__(self, keys):\n        self.keys = keys\n\n    def __call__(self, results):\n        \"\"\"Call function to convert image in results to :obj:`torch.Tensor` and\n        transpose the channel order.\n\n        Args:\n            results (dict): Result dict contains the image data to convert.\n\n        Returns:\n            dict: The result dict contains the image converted\n                to :obj:`torch.Tensor` and permuted to (C, H, W) order.\n        \"\"\"\n        for key in self.keys:\n            img = results[key]\n            if len(img.shape) < 3:\n                img = np.expand_dims(img, -1)\n            results[key] = to_tensor(img).permute(2, 0, 1).contiguous()\n\n        return results\n\n    def __repr__(self):\n        return self.__class__.__name__ + f'(keys={self.keys})'\n\n\n@TRANSFORMS.register_module()\nclass Transpose:\n    \"\"\"Transpose some results by given keys.\n\n    Args:\n        keys (Sequence[str]): Keys of results to be transposed.\n        order (Sequence[int]): Order of transpose.\n    \"\"\"\n\n    def __init__(self, keys, order):\n        self.keys = keys\n        self.order = order\n\n    def __call__(self, results):\n        \"\"\"Call function to transpose the channel order of data in results.\n\n        Args:\n            results (dict): Result dict contains the data to transpose.\n\n        Returns:\n            dict: The result dict contains the data transposed to \\\n                ``self.order``.\n        \"\"\"\n        for key in self.keys:\n            results[key] = results[key].transpose(self.order)\n        return results\n\n    def __repr__(self):\n        return self.__class__.__name__ + \\\n            f'(keys={self.keys}, order={self.order})'\n\n\n@TRANSFORMS.register_module()\nclass WrapFieldsToLists:\n    \"\"\"Wrap fields of the data dictionary into lists for evaluation.\n\n    This class can be used as a last step of a test or validation\n    pipeline for single image evaluation or inference.\n\n    Example:\n        >>> test_pipeline = [\n        >>>    dict(type='LoadImageFromFile'),\n        >>>    dict(type='Normalize',\n                    mean=[123.675, 116.28, 103.53],\n                    std=[58.395, 57.12, 57.375],\n                    to_rgb=True),\n        >>>    dict(type='Pad', size_divisor=32),\n        >>>    dict(type='ImageToTensor', keys=['img']),\n        >>>    dict(type='Collect', keys=['img']),\n        >>>    dict(type='WrapFieldsToLists')\n        >>> ]\n    \"\"\"\n\n    def __call__(self, results):\n        \"\"\"Call function to wrap fields into lists.\n\n        Args:\n            results (dict): Result dict contains the data to wrap.\n\n        Returns:\n            dict: The result dict where value of ``self.keys`` are wrapped \\\n                into list.\n        \"\"\"\n\n        # Wrap dict fields into lists\n        for key, val in results.items():\n            results[key] = [val]\n        return results\n\n    def __repr__(self):\n        return f'{self.__class__.__name__}()'\n\n\n@TRANSFORMS.register_module()\nclass PackTrackInputs(BaseTransform):\n    \"\"\"Pack the inputs data for the multi object tracking and video instance\n    segmentation. All the information of images are packed to ``inputs``. All\n    the information except images are packed to ``data_samples``. In order to\n    get the original annotaiton and meta info, we add `instances` key into meta\n    keys.\n\n    Args:\n        meta_keys (Sequence[str]): Meta keys to be collected in\n            ``data_sample.metainfo``. Defaults to None.\n        default_meta_keys (tuple): Default meta keys. Defaults to ('img_id',\n            'img_path', 'ori_shape', 'img_shape', 'scale_factor',\n            'flip', 'flip_direction', 'frame_id', 'is_video_data',\n            'video_id', 'video_length', 'instances').\n    \"\"\"\n    mapping_table = {\n        'gt_bboxes': 'bboxes',\n        'gt_bboxes_labels': 'labels',\n        'gt_masks': 'masks',\n        'gt_instances_ids': 'instances_ids'\n    }\n\n    def __init__(self,\n                 meta_keys: Optional[dict] = None,\n                 default_meta_keys: tuple = ('img_id', 'img_path', 'ori_shape',\n                                             'img_shape', 'scale_factor',\n                                             'flip', 'flip_direction',\n                                             'frame_id', 'video_id',\n                                             'video_length',\n                                             'ori_video_length', 'instances')):\n        self.meta_keys = default_meta_keys\n        if meta_keys is not None:\n            if isinstance(meta_keys, str):\n                meta_keys = (meta_keys, )\n            else:\n                assert isinstance(meta_keys, tuple), \\\n                    'meta_keys must be str or tuple'\n            self.meta_keys += meta_keys\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Method to pack the input data.\n        Args:\n            results (dict): Result dict from the data pipeline.\n        Returns:\n            dict:\n            - 'inputs' (dict[Tensor]): The forward data of models.\n            - 'data_samples' (obj:`TrackDataSample`): The annotation info of\n                the samples.\n        \"\"\"\n        packed_results = dict()\n        packed_results['inputs'] = dict()\n\n        # 1. Pack images\n        if 'img' in results:\n            imgs = results['img']\n            imgs = np.stack(imgs, axis=0)\n            imgs = imgs.transpose(0, 3, 1, 2)\n            packed_results['inputs'] = to_tensor(imgs)\n\n        # 2. Pack InstanceData\n        if 'gt_ignore_flags' in results:\n            gt_ignore_flags_list = results['gt_ignore_flags']\n            valid_idx_list, ignore_idx_list = [], []\n            for gt_ignore_flags in gt_ignore_flags_list:\n                valid_idx = np.where(gt_ignore_flags == 0)[0]\n                ignore_idx = np.where(gt_ignore_flags == 1)[0]\n                valid_idx_list.append(valid_idx)\n                ignore_idx_list.append(ignore_idx)\n\n        assert 'img_id' in results, \"'img_id' must contained in the results \"\n        'for counting the number of images'\n\n        num_imgs = len(results['img_id'])\n        instance_data_list = [InstanceData() for _ in range(num_imgs)]\n        ignore_instance_data_list = [InstanceData() for _ in range(num_imgs)]\n\n        for key in self.mapping_table.keys():\n            if key not in results:\n                continue\n            if key == 'gt_masks':\n                mapped_key = self.mapping_table[key]\n                gt_masks_list = results[key]\n                if 'gt_ignore_flags' in results:\n                    for i, gt_mask in enumerate(gt_masks_list):\n                        valid_idx, ignore_idx = valid_idx_list[\n                            i], ignore_idx_list[i]\n                        instance_data_list[i][mapped_key] = gt_mask[valid_idx]\n                        ignore_instance_data_list[i][mapped_key] = gt_mask[\n                            ignore_idx]\n\n                else:\n                    for i, gt_mask in enumerate(gt_masks_list):\n                        instance_data_list[i][mapped_key] = gt_mask\n\n            else:\n                anns_list = results[key]\n                if 'gt_ignore_flags' in results:\n                    for i, ann in enumerate(anns_list):\n                        valid_idx, ignore_idx = valid_idx_list[\n                            i], ignore_idx_list[i]\n                        instance_data_list[i][\n                            self.mapping_table[key]] = to_tensor(\n                                ann[valid_idx])\n                        ignore_instance_data_list[i][\n                            self.mapping_table[key]] = to_tensor(\n                                ann[ignore_idx])\n                else:\n                    for i, ann in enumerate(anns_list):\n                        instance_data_list[i][\n                            self.mapping_table[key]] = to_tensor(ann)\n\n        det_data_samples_list = []\n        for i in range(num_imgs):\n            det_data_sample = DetDataSample()\n            det_data_sample.gt_instances = instance_data_list[i]\n            det_data_sample.ignored_instances = ignore_instance_data_list[i]\n            det_data_samples_list.append(det_data_sample)\n\n        # 3. Pack metainfo\n        for key in self.meta_keys:\n            if key not in results:\n                continue\n            img_metas_list = results[key]\n            for i, img_meta in enumerate(img_metas_list):\n                det_data_samples_list[i].set_metainfo({f'{key}': img_meta})\n\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = det_data_samples_list\n        if 'key_frame_flags' in results:\n            key_frame_flags = np.asarray(results['key_frame_flags'])\n            key_frames_inds = np.where(key_frame_flags)[0].tolist()\n            ref_frames_inds = np.where(~key_frame_flags)[0].tolist()\n            track_data_sample.set_metainfo(\n                dict(key_frames_inds=key_frames_inds))\n            track_data_sample.set_metainfo(\n                dict(ref_frames_inds=ref_frames_inds))\n\n        packed_results['data_samples'] = track_data_sample\n        return packed_results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'meta_keys={self.meta_keys}, '\n        repr_str += f'default_meta_keys={self.default_meta_keys})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass PackReIDInputs(BaseTransform):\n    \"\"\"Pack the inputs data for the ReID. The ``meta_info`` item is always\n    populated. The contents of the ``meta_info`` dictionary depends on\n    ``meta_keys``. By default this includes:\n\n        - ``img_path``: path to the image file.\n        - ``ori_shape``: original shape of the image as a tuple (H, W).\n        - ``img_shape``: shape of the image input to the network as a tuple\n            (H, W). Note that images may be zero padded on the bottom/right\n          if the batch tensor is larger than this shape.\n        - ``scale``: scale of the image as a tuple (W, H).\n        - ``scale_factor``: a float indicating the pre-processing scale.\n        -  ``flip``: a boolean indicating if image flip transform was used.\n        - ``flip_direction``: the flipping direction.\n    Args:\n        meta_keys (Sequence[str], optional): The meta keys to saved in the\n            ``metainfo`` of the packed ``data_sample``.\n    \"\"\"\n    default_meta_keys = ('img_path', 'ori_shape', 'img_shape', 'scale',\n                         'scale_factor')\n\n    def __init__(self, meta_keys: Sequence[str] = ()) -> None:\n        self.meta_keys = self.default_meta_keys\n        if meta_keys is not None:\n            if isinstance(meta_keys, str):\n                meta_keys = (meta_keys, )\n            else:\n                assert isinstance(meta_keys, tuple), \\\n                    'meta_keys must be str or tuple.'\n            self.meta_keys += meta_keys\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Method to pack the input data.\n        Args:\n            results (dict): Result dict from the data pipeline.\n        Returns:\n            dict:\n            - 'inputs' (dict[Tensor]): The forward data of models.\n            - 'data_samples' (obj:`ReIDDataSample`): The meta info of the\n                sample.\n        \"\"\"\n        packed_results = dict(inputs=dict(), data_samples=None)\n        assert 'img' in results, 'Missing the key ``img``.'\n        _type = type(results['img'])\n        label = results['gt_label']\n\n        if _type == list:\n            img = results['img']\n            label = np.stack(label, axis=0)  # (N,)\n            assert all([type(v) == _type for v in results.values()]), \\\n                'All items in the results must have the same type.'\n        else:\n            img = [results['img']]\n\n        img = np.stack(img, axis=3)  # (H, W, C, N)\n        img = img.transpose(3, 2, 0, 1)  # (N, C, H, W)\n        img = np.ascontiguousarray(img)\n\n        packed_results['inputs'] = to_tensor(img)\n\n        data_sample = ReIDDataSample()\n        data_sample.set_gt_label(label)\n\n        meta_info = dict()\n        for key in self.meta_keys:\n            meta_info[key] = results[key]\n        data_sample.set_metainfo(meta_info)\n        packed_results['data_samples'] = data_sample\n\n        return packed_results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(meta_keys={self.meta_keys})'\n        return repr_str\n"
  },
  {
    "path": "mmdet/datasets/transforms/frame_sampling.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport random\nfrom collections import defaultdict\nfrom typing import Dict, List, Optional, Union\n\nfrom mmcv.transforms import BaseTransform\n\nfrom mmdet.registry import TRANSFORMS\n\n\n@TRANSFORMS.register_module()\nclass BaseFrameSample(BaseTransform):\n    \"\"\"Directly get the key frame, no reference frames.\n\n    Args:\n        collect_video_keys (list[str]): The keys of video info to be\n            collected.\n    \"\"\"\n\n    def __init__(self,\n                 collect_video_keys: List[str] = ['video_id', 'video_length']):\n        self.collect_video_keys = collect_video_keys\n\n    def prepare_data(self, video_infos: dict,\n                     sampled_inds: List[int]) -> Dict[str, List]:\n        \"\"\"Prepare data for the subsequent pipeline.\n\n        Args:\n            video_infos (dict): The whole video information.\n            sampled_inds (list[int]): The sampled frame indices.\n\n        Returns:\n            dict: The processed data information.\n        \"\"\"\n        frames_anns = video_infos['images']\n        final_data_info = defaultdict(list)\n        # for data in frames_anns:\n        for index in sampled_inds:\n            data = frames_anns[index]\n            # copy the info in video-level into img-level\n            for key in self.collect_video_keys:\n                if key == 'video_length':\n                    data['ori_video_length'] = video_infos[key]\n                    data['video_length'] = len(sampled_inds)\n                else:\n                    data[key] = video_infos[key]\n            # Collate data_list (list of dict to dict of list)\n            for key, value in data.items():\n                final_data_info[key].append(value)\n\n        return final_data_info\n\n    def transform(self, video_infos: dict) -> Optional[Dict[str, List]]:\n        \"\"\"Transform the video information.\n\n        Args:\n            video_infos (dict): The whole video information.\n\n        Returns:\n            dict: The data information of the key frames.\n        \"\"\"\n        if 'key_frame_id' in video_infos:\n            key_frame_id = video_infos['key_frame_id']\n            assert isinstance(video_infos['key_frame_id'], int)\n        else:\n            key_frame_id = random.sample(\n                list(range(video_infos['video_length'])), 1)[0]\n        results = self.prepare_data(video_infos, [key_frame_id])\n\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(collect_video_keys={self.collect_video_keys})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass UniformRefFrameSample(BaseFrameSample):\n    \"\"\"Uniformly sample reference frames.\n\n    Args:\n        num_ref_imgs (int): Number of reference frames to be sampled.\n        frame_range (int | list[int]): Range of frames to be sampled around\n            key frame. If int, the range is [-frame_range, frame_range].\n            Defaults to 10.\n        filter_key_img (bool): Whether to filter the key frame when\n            sampling reference frames. Defaults to True.\n        collect_video_keys (list[str]): The keys of video info to be\n            collected.\n    \"\"\"\n\n    def __init__(self,\n                 num_ref_imgs: int = 1,\n                 frame_range: Union[int, List[int]] = 10,\n                 filter_key_img: bool = True,\n                 collect_video_keys: List[str] = ['video_id', 'video_length']):\n        self.num_ref_imgs = num_ref_imgs\n        self.filter_key_img = filter_key_img\n        if isinstance(frame_range, int):\n            assert frame_range >= 0, 'frame_range can not be a negative value.'\n            frame_range = [-frame_range, frame_range]\n        elif isinstance(frame_range, list):\n            assert len(frame_range) == 2, 'The length must be 2.'\n            assert frame_range[0] <= 0 and frame_range[1] >= 0\n            for i in frame_range:\n                assert isinstance(i, int), 'Each element must be int.'\n        else:\n            raise TypeError('The type of frame_range must be int or list.')\n        self.frame_range = frame_range\n        super().__init__(collect_video_keys=collect_video_keys)\n\n    def sampling_frames(self, video_length: int, key_frame_id: int):\n        \"\"\"Sampling frames.\n\n        Args:\n            video_length (int): The length of the video.\n            key_frame_id (int): The key frame id.\n\n        Returns:\n            list[int]: The sampled frame indices.\n        \"\"\"\n        if video_length > 1:\n            left = max(0, key_frame_id + self.frame_range[0])\n            right = min(key_frame_id + self.frame_range[1], video_length - 1)\n            frame_ids = list(range(0, video_length))\n\n            valid_ids = frame_ids[left:right + 1]\n            if self.filter_key_img and key_frame_id in valid_ids:\n                valid_ids.remove(key_frame_id)\n            assert len(\n                valid_ids\n            ) > 0, 'After filtering key frame, there are no valid frames'\n            if len(valid_ids) < self.num_ref_imgs:\n                valid_ids = valid_ids * self.num_ref_imgs\n            ref_frame_ids = random.sample(valid_ids, self.num_ref_imgs)\n        else:\n            ref_frame_ids = [key_frame_id] * self.num_ref_imgs\n\n        sampled_frames_ids = [key_frame_id] + ref_frame_ids\n        sampled_frames_ids = sorted(sampled_frames_ids)\n\n        key_frames_ind = sampled_frames_ids.index(key_frame_id)\n        key_frame_flags = [False] * len(sampled_frames_ids)\n        key_frame_flags[key_frames_ind] = True\n        return sampled_frames_ids, key_frame_flags\n\n    def transform(self, video_infos: dict) -> Optional[Dict[str, List]]:\n        \"\"\"Transform the video information.\n\n        Args:\n            video_infos (dict): The whole video information.\n\n        Returns:\n            dict: The data information of the sampled frames.\n        \"\"\"\n        if 'key_frame_id' in video_infos:\n            key_frame_id = video_infos['key_frame_id']\n            assert isinstance(video_infos['key_frame_id'], int)\n        else:\n            key_frame_id = random.sample(\n                list(range(video_infos['video_length'])), 1)[0]\n\n        (sampled_frames_ids, key_frame_flags) = self.sampling_frames(\n            video_infos['video_length'], key_frame_id=key_frame_id)\n        results = self.prepare_data(video_infos, sampled_frames_ids)\n        results['key_frame_flags'] = key_frame_flags\n\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(num_ref_imgs={self.num_ref_imgs}, '\n        repr_str += f'frame_range={self.frame_range}, '\n        repr_str += f'filter_key_img={self.filter_key_img}, '\n        repr_str += f'collect_video_keys={self.collect_video_keys})'\n        return repr_str\n"
  },
  {
    "path": "mmdet/datasets/transforms/geometric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nfrom typing import Optional, Union\n\nimport cv2\nimport mmcv\nimport numpy as np\nfrom mmcv.transforms import BaseTransform\nfrom mmcv.transforms.utils import cache_randomness\n\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.structures.bbox import autocast_box_type\nfrom .augment_wrappers import _MAX_LEVEL, level_to_mag\n\n\n@TRANSFORMS.register_module()\nclass GeomTransform(BaseTransform):\n    \"\"\"Base class for geometric transformations. All geometric transformations\n    need to inherit from this base class. ``GeomTransform`` unifies the class\n    attributes and class functions of geometric transformations (ShearX,\n    ShearY, Rotate, TranslateX, and TranslateY), and records the homography\n    matrix.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        prob (float): The probability for performing the geometric\n            transformation and should be in range [0, 1]. Defaults to 1.0.\n        level (int, optional): The level should be in range [0, _MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum magnitude for geometric transformation.\n            Defaults to 0.0.\n        max_mag (float): The maximum magnitude for geometric transformation.\n            Defaults to 1.0.\n        reversal_prob (float): The probability that reverses the geometric\n            transformation magnitude. Should be in range [0,1].\n            Defaults to 0.5.\n        img_border_value (int | float | tuple): The filled values for\n            image border. If float, the same fill value will be used for\n            all the three channels of image. If tuple, it should be 3 elements.\n            Defaults to 128.\n        mask_border_value (int): The fill value used for masks. Defaults to 0.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 1.0,\n                 reversal_prob: float = 0.5,\n                 img_border_value: Union[int, float, tuple] = 128,\n                 mask_border_value: int = 0,\n                 seg_ignore_label: int = 255,\n                 interpolation: str = 'bilinear') -> None:\n        assert 0 <= prob <= 1.0, f'The probability of the transformation ' \\\n                                 f'should be in range [0,1], got {prob}.'\n        assert level is None or isinstance(level, int), \\\n            f'The level should be None or type int, got {type(level)}.'\n        assert level is None or 0 <= level <= _MAX_LEVEL, \\\n            f'The level should be in range [0,{_MAX_LEVEL}], got {level}.'\n        assert isinstance(min_mag, float), \\\n            f'min_mag should be type float, got {type(min_mag)}.'\n        assert isinstance(max_mag, float), \\\n            f'max_mag should be type float, got {type(max_mag)}.'\n        assert min_mag <= max_mag, \\\n            f'min_mag should smaller than max_mag, ' \\\n            f'got min_mag={min_mag} and max_mag={max_mag}'\n        assert isinstance(reversal_prob, float), \\\n            f'reversal_prob should be type float, got {type(max_mag)}.'\n        assert 0 <= reversal_prob <= 1.0, \\\n            f'The reversal probability of the transformation magnitude ' \\\n            f'should be type float, got {type(reversal_prob)}.'\n        if isinstance(img_border_value, (float, int)):\n            img_border_value = tuple([float(img_border_value)] * 3)\n        elif isinstance(img_border_value, tuple):\n            assert len(img_border_value) == 3, \\\n                f'img_border_value as tuple must have 3 elements, ' \\\n                f'got {len(img_border_value)}.'\n            img_border_value = tuple([float(val) for val in img_border_value])\n        else:\n            raise ValueError(\n                'img_border_value must be float or tuple with 3 elements.')\n        assert np.all([0 <= val <= 255 for val in img_border_value]), 'all ' \\\n            'elements of img_border_value should between range [0,255].' \\\n            f'got {img_border_value}.'\n        self.prob = prob\n        self.level = level\n        self.min_mag = min_mag\n        self.max_mag = max_mag\n        self.reversal_prob = reversal_prob\n        self.img_border_value = img_border_value\n        self.mask_border_value = mask_border_value\n        self.seg_ignore_label = seg_ignore_label\n        self.interpolation = interpolation\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Transform the image.\"\"\"\n        pass\n\n    def _transform_masks(self, results: dict, mag: float) -> None:\n        \"\"\"Transform the masks.\"\"\"\n        pass\n\n    def _transform_seg(self, results: dict, mag: float) -> None:\n        \"\"\"Transform the segmentation map.\"\"\"\n        pass\n\n    def _get_homography_matrix(self, results: dict, mag: float) -> np.ndarray:\n        \"\"\"Get the homography matrix for the geometric transformation.\"\"\"\n        return np.eye(3, dtype=np.float32)\n\n    def _transform_bboxes(self, results: dict, mag: float) -> None:\n        \"\"\"Transform the bboxes.\"\"\"\n        results['gt_bboxes'].project_(self.homography_matrix)\n        results['gt_bboxes'].clip_(results['img_shape'])\n\n    def _record_homography_matrix(self, results: dict) -> None:\n        \"\"\"Record the homography matrix for the geometric transformation.\"\"\"\n        if results.get('homography_matrix', None) is None:\n            results['homography_matrix'] = self.homography_matrix\n        else:\n            results['homography_matrix'] = self.homography_matrix @ results[\n                'homography_matrix']\n\n    @cache_randomness\n    def _random_disable(self):\n        \"\"\"Randomly disable the transform.\"\"\"\n        return np.random.rand() > self.prob\n\n    @cache_randomness\n    def _get_mag(self):\n        \"\"\"Get the magnitude of the transform.\"\"\"\n        mag = level_to_mag(self.level, self.min_mag, self.max_mag)\n        return -mag if np.random.rand() > self.reversal_prob else mag\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function for images, bounding boxes, masks and semantic\n        segmentation map.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Transformed results.\n        \"\"\"\n\n        if self._random_disable():\n            return results\n        mag = self._get_mag()\n        self.homography_matrix = self._get_homography_matrix(results, mag)\n        self._record_homography_matrix(results)\n        self._transform_img(results, mag)\n        if results.get('gt_bboxes', None) is not None:\n            self._transform_bboxes(results, mag)\n        if results.get('gt_masks', None) is not None:\n            self._transform_masks(results, mag)\n        if results.get('gt_seg_map', None) is not None:\n            self._transform_seg(results, mag)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(prob={self.prob}, '\n        repr_str += f'level={self.level}, '\n        repr_str += f'min_mag={self.min_mag}, '\n        repr_str += f'max_mag={self.max_mag}, '\n        repr_str += f'reversal_prob={self.reversal_prob}, '\n        repr_str += f'img_border_value={self.img_border_value}, '\n        repr_str += f'mask_border_value={self.mask_border_value}, '\n        repr_str += f'seg_ignore_label={self.seg_ignore_label}, '\n        repr_str += f'interpolation={self.interpolation})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass ShearX(GeomTransform):\n    \"\"\"Shear the images, bboxes, masks and segmentation map horizontally.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        prob (float): The probability for performing Shear and should be in\n            range [0, 1]. Defaults to 1.0.\n        level (int, optional): The level should be in range [0, _MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum angle for the horizontal shear.\n            Defaults to 0.0.\n        max_mag (float): The maximum angle for the horizontal shear.\n            Defaults to 30.0.\n        reversal_prob (float): The probability that reverses the horizontal\n            shear magnitude. Should be in range [0,1]. Defaults to 0.5.\n        img_border_value (int | float | tuple): The filled values for\n            image border. If float, the same fill value will be used for\n            all the three channels of image. If tuple, it should be 3 elements.\n            Defaults to 128.\n        mask_border_value (int): The fill value used for masks. Defaults to 0.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 30.0,\n                 reversal_prob: float = 0.5,\n                 img_border_value: Union[int, float, tuple] = 128,\n                 mask_border_value: int = 0,\n                 seg_ignore_label: int = 255,\n                 interpolation: str = 'bilinear') -> None:\n        assert 0. <= min_mag <= 90., \\\n            f'min_mag angle for ShearX should be ' \\\n            f'in range [0, 90], got {min_mag}.'\n        assert 0. <= max_mag <= 90., \\\n            f'max_mag angle for ShearX should be ' \\\n            f'in range [0, 90], got {max_mag}.'\n        super().__init__(\n            prob=prob,\n            level=level,\n            min_mag=min_mag,\n            max_mag=max_mag,\n            reversal_prob=reversal_prob,\n            img_border_value=img_border_value,\n            mask_border_value=mask_border_value,\n            seg_ignore_label=seg_ignore_label,\n            interpolation=interpolation)\n\n    @cache_randomness\n    def _get_mag(self):\n        \"\"\"Get the magnitude of the transform.\"\"\"\n        mag = level_to_mag(self.level, self.min_mag, self.max_mag)\n        mag = np.tan(mag * np.pi / 180)\n        return -mag if np.random.rand() > self.reversal_prob else mag\n\n    def _get_homography_matrix(self, results: dict, mag: float) -> np.ndarray:\n        \"\"\"Get the homography matrix for ShearX.\"\"\"\n        return np.array([[1, mag, 0], [0, 1, 0], [0, 0, 1]], dtype=np.float32)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Shear the image horizontally.\"\"\"\n        results['img'] = mmcv.imshear(\n            results['img'],\n            mag,\n            direction='horizontal',\n            border_value=self.img_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_masks(self, results: dict, mag: float) -> None:\n        \"\"\"Shear the masks horizontally.\"\"\"\n        results['gt_masks'] = results['gt_masks'].shear(\n            results['img_shape'],\n            mag,\n            direction='horizontal',\n            border_value=self.mask_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_seg(self, results: dict, mag: float) -> None:\n        \"\"\"Shear the segmentation map horizontally.\"\"\"\n        results['gt_seg_map'] = mmcv.imshear(\n            results['gt_seg_map'],\n            mag,\n            direction='horizontal',\n            border_value=self.seg_ignore_label,\n            interpolation='nearest')\n\n\n@TRANSFORMS.register_module()\nclass ShearY(GeomTransform):\n    \"\"\"Shear the images, bboxes, masks and segmentation map vertically.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        prob (float): The probability for performing ShearY and should be in\n            range [0, 1]. Defaults to 1.0.\n        level (int, optional): The level should be in range [0,_MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum angle for the vertical shear.\n            Defaults to 0.0.\n        max_mag (float): The maximum angle for the vertical shear.\n            Defaults to 30.0.\n        reversal_prob (float): The probability that reverses the vertical\n            shear magnitude. Should be in range [0,1]. Defaults to 0.5.\n        img_border_value (int | float | tuple): The filled values for\n            image border. If float, the same fill value will be used for\n            all the three channels of image. If tuple, it should be 3 elements.\n            Defaults to 128.\n        mask_border_value (int): The fill value used for masks. Defaults to 0.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 30.,\n                 reversal_prob: float = 0.5,\n                 img_border_value: Union[int, float, tuple] = 128,\n                 mask_border_value: int = 0,\n                 seg_ignore_label: int = 255,\n                 interpolation: str = 'bilinear') -> None:\n        assert 0. <= min_mag <= 90., \\\n            f'min_mag angle for ShearY should be ' \\\n            f'in range [0, 90], got {min_mag}.'\n        assert 0. <= max_mag <= 90., \\\n            f'max_mag angle for ShearY should be ' \\\n            f'in range [0, 90], got {max_mag}.'\n        super().__init__(\n            prob=prob,\n            level=level,\n            min_mag=min_mag,\n            max_mag=max_mag,\n            reversal_prob=reversal_prob,\n            img_border_value=img_border_value,\n            mask_border_value=mask_border_value,\n            seg_ignore_label=seg_ignore_label,\n            interpolation=interpolation)\n\n    @cache_randomness\n    def _get_mag(self):\n        \"\"\"Get the magnitude of the transform.\"\"\"\n        mag = level_to_mag(self.level, self.min_mag, self.max_mag)\n        mag = np.tan(mag * np.pi / 180)\n        return -mag if np.random.rand() > self.reversal_prob else mag\n\n    def _get_homography_matrix(self, results: dict, mag: float) -> np.ndarray:\n        \"\"\"Get the homography matrix for ShearY.\"\"\"\n        return np.array([[1, 0, 0], [mag, 1, 0], [0, 0, 1]], dtype=np.float32)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Shear the image vertically.\"\"\"\n        results['img'] = mmcv.imshear(\n            results['img'],\n            mag,\n            direction='vertical',\n            border_value=self.img_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_masks(self, results: dict, mag: float) -> None:\n        \"\"\"Shear the masks vertically.\"\"\"\n        results['gt_masks'] = results['gt_masks'].shear(\n            results['img_shape'],\n            mag,\n            direction='vertical',\n            border_value=self.mask_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_seg(self, results: dict, mag: float) -> None:\n        \"\"\"Shear the segmentation map vertically.\"\"\"\n        results['gt_seg_map'] = mmcv.imshear(\n            results['gt_seg_map'],\n            mag,\n            direction='vertical',\n            border_value=self.seg_ignore_label,\n            interpolation='nearest')\n\n\n@TRANSFORMS.register_module()\nclass Rotate(GeomTransform):\n    \"\"\"Rotate the images, bboxes, masks and segmentation map.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        prob (float): The probability for perform transformation and\n            should be in range 0 to 1. Defaults to 1.0.\n        level (int, optional): The level should be in range [0, _MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The maximum angle for rotation.\n            Defaults to 0.0.\n        max_mag (float): The maximum angle for rotation.\n            Defaults to 30.0.\n        reversal_prob (float): The probability that reverses the rotation\n            magnitude. Should be in range [0,1]. Defaults to 0.5.\n        img_border_value (int | float | tuple): The filled values for\n            image border. If float, the same fill value will be used for\n            all the three channels of image. If tuple, it should be 3 elements.\n            Defaults to 128.\n        mask_border_value (int): The fill value used for masks. Defaults to 0.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 30.0,\n                 reversal_prob: float = 0.5,\n                 img_border_value: Union[int, float, tuple] = 128,\n                 mask_border_value: int = 0,\n                 seg_ignore_label: int = 255,\n                 interpolation: str = 'bilinear') -> None:\n        assert 0. <= min_mag <= 180., \\\n            f'min_mag for Rotate should be in range [0,180], got {min_mag}.'\n        assert 0. <= max_mag <= 180., \\\n            f'max_mag for Rotate should be in range [0,180], got {max_mag}.'\n        super().__init__(\n            prob=prob,\n            level=level,\n            min_mag=min_mag,\n            max_mag=max_mag,\n            reversal_prob=reversal_prob,\n            img_border_value=img_border_value,\n            mask_border_value=mask_border_value,\n            seg_ignore_label=seg_ignore_label,\n            interpolation=interpolation)\n\n    def _get_homography_matrix(self, results: dict, mag: float) -> np.ndarray:\n        \"\"\"Get the homography matrix for Rotate.\"\"\"\n        img_shape = results['img_shape']\n        center = ((img_shape[1] - 1) * 0.5, (img_shape[0] - 1) * 0.5)\n        cv2_rotation_matrix = cv2.getRotationMatrix2D(center, -mag, 1.0)\n        return np.concatenate(\n            [cv2_rotation_matrix,\n             np.array([0, 0, 1]).reshape((1, 3))]).astype(np.float32)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Rotate the image.\"\"\"\n        results['img'] = mmcv.imrotate(\n            results['img'],\n            mag,\n            border_value=self.img_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_masks(self, results: dict, mag: float) -> None:\n        \"\"\"Rotate the masks.\"\"\"\n        results['gt_masks'] = results['gt_masks'].rotate(\n            results['img_shape'],\n            mag,\n            border_value=self.mask_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_seg(self, results: dict, mag: float) -> None:\n        \"\"\"Rotate the segmentation map.\"\"\"\n        results['gt_seg_map'] = mmcv.imrotate(\n            results['gt_seg_map'],\n            mag,\n            border_value=self.seg_ignore_label,\n            interpolation='nearest')\n\n\n@TRANSFORMS.register_module()\nclass TranslateX(GeomTransform):\n    \"\"\"Translate the images, bboxes, masks and segmentation map horizontally.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        prob (float): The probability for perform transformation and\n            should be in range 0 to 1. Defaults to 1.0.\n        level (int, optional): The level should be in range [0, _MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum pixel's offset ratio for horizontal\n            translation. Defaults to 0.0.\n        max_mag (float): The maximum pixel's offset ratio for horizontal\n            translation. Defaults to 0.1.\n        reversal_prob (float): The probability that reverses the horizontal\n            translation magnitude. Should be in range [0,1]. Defaults to 0.5.\n        img_border_value (int | float | tuple): The filled values for\n            image border. If float, the same fill value will be used for\n            all the three channels of image. If tuple, it should be 3 elements.\n            Defaults to 128.\n        mask_border_value (int): The fill value used for masks. Defaults to 0.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 0.1,\n                 reversal_prob: float = 0.5,\n                 img_border_value: Union[int, float, tuple] = 128,\n                 mask_border_value: int = 0,\n                 seg_ignore_label: int = 255,\n                 interpolation: str = 'bilinear') -> None:\n        assert 0. <= min_mag <= 1., \\\n            f'min_mag ratio for TranslateX should be ' \\\n            f'in range [0, 1], got {min_mag}.'\n        assert 0. <= max_mag <= 1., \\\n            f'max_mag ratio for TranslateX should be ' \\\n            f'in range [0, 1], got {max_mag}.'\n        super().__init__(\n            prob=prob,\n            level=level,\n            min_mag=min_mag,\n            max_mag=max_mag,\n            reversal_prob=reversal_prob,\n            img_border_value=img_border_value,\n            mask_border_value=mask_border_value,\n            seg_ignore_label=seg_ignore_label,\n            interpolation=interpolation)\n\n    def _get_homography_matrix(self, results: dict, mag: float) -> np.ndarray:\n        \"\"\"Get the homography matrix for TranslateX.\"\"\"\n        mag = int(results['img_shape'][1] * mag)\n        return np.array([[1, 0, mag], [0, 1, 0], [0, 0, 1]], dtype=np.float32)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Translate the image horizontally.\"\"\"\n        mag = int(results['img_shape'][1] * mag)\n        results['img'] = mmcv.imtranslate(\n            results['img'],\n            mag,\n            direction='horizontal',\n            border_value=self.img_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_masks(self, results: dict, mag: float) -> None:\n        \"\"\"Translate the masks horizontally.\"\"\"\n        mag = int(results['img_shape'][1] * mag)\n        results['gt_masks'] = results['gt_masks'].translate(\n            results['img_shape'],\n            mag,\n            direction='horizontal',\n            border_value=self.mask_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_seg(self, results: dict, mag: float) -> None:\n        \"\"\"Translate the segmentation map horizontally.\"\"\"\n        mag = int(results['img_shape'][1] * mag)\n        results['gt_seg_map'] = mmcv.imtranslate(\n            results['gt_seg_map'],\n            mag,\n            direction='horizontal',\n            border_value=self.seg_ignore_label,\n            interpolation='nearest')\n\n\n@TRANSFORMS.register_module()\nclass TranslateY(GeomTransform):\n    \"\"\"Translate the images, bboxes, masks and segmentation map vertically.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        prob (float): The probability for perform transformation and\n            should be in range 0 to 1. Defaults to 1.0.\n        level (int, optional): The level should be in range [0, _MAX_LEVEL].\n            If level is None, it will generate from [0, _MAX_LEVEL] randomly.\n            Defaults to None.\n        min_mag (float): The minimum pixel's offset ratio for vertical\n            translation. Defaults to 0.0.\n        max_mag (float): The maximum pixel's offset ratio for vertical\n            translation. Defaults to 0.1.\n        reversal_prob (float): The probability that reverses the vertical\n            translation magnitude. Should be in range [0,1]. Defaults to 0.5.\n        img_border_value (int | float | tuple): The filled values for\n            image border. If float, the same fill value will be used for\n            all the three channels of image. If tuple, it should be 3 elements.\n            Defaults to 128.\n        mask_border_value (int): The fill value used for masks. Defaults to 0.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 1.0,\n                 level: Optional[int] = None,\n                 min_mag: float = 0.0,\n                 max_mag: float = 0.1,\n                 reversal_prob: float = 0.5,\n                 img_border_value: Union[int, float, tuple] = 128,\n                 mask_border_value: int = 0,\n                 seg_ignore_label: int = 255,\n                 interpolation: str = 'bilinear') -> None:\n        assert 0. <= min_mag <= 1., \\\n            f'min_mag ratio for TranslateY should be ' \\\n            f'in range [0,1], got {min_mag}.'\n        assert 0. <= max_mag <= 1., \\\n            f'max_mag ratio for TranslateY should be ' \\\n            f'in range [0,1], got {max_mag}.'\n        super().__init__(\n            prob=prob,\n            level=level,\n            min_mag=min_mag,\n            max_mag=max_mag,\n            reversal_prob=reversal_prob,\n            img_border_value=img_border_value,\n            mask_border_value=mask_border_value,\n            seg_ignore_label=seg_ignore_label,\n            interpolation=interpolation)\n\n    def _get_homography_matrix(self, results: dict, mag: float) -> np.ndarray:\n        \"\"\"Get the homography matrix for TranslateY.\"\"\"\n        mag = int(results['img_shape'][0] * mag)\n        return np.array([[1, 0, 0], [0, 1, mag], [0, 0, 1]], dtype=np.float32)\n\n    def _transform_img(self, results: dict, mag: float) -> None:\n        \"\"\"Translate the image vertically.\"\"\"\n        mag = int(results['img_shape'][0] * mag)\n        results['img'] = mmcv.imtranslate(\n            results['img'],\n            mag,\n            direction='vertical',\n            border_value=self.img_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_masks(self, results: dict, mag: float) -> None:\n        \"\"\"Translate masks vertically.\"\"\"\n        mag = int(results['img_shape'][0] * mag)\n        results['gt_masks'] = results['gt_masks'].translate(\n            results['img_shape'],\n            mag,\n            direction='vertical',\n            border_value=self.mask_border_value,\n            interpolation=self.interpolation)\n\n    def _transform_seg(self, results: dict, mag: float) -> None:\n        \"\"\"Translate segmentation map vertically.\"\"\"\n        mag = int(results['img_shape'][0] * mag)\n        results['gt_seg_map'] = mmcv.imtranslate(\n            results['gt_seg_map'],\n            mag,\n            direction='vertical',\n            border_value=self.seg_ignore_label,\n            interpolation='nearest')\n"
  },
  {
    "path": "mmdet/datasets/transforms/instaboost.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport numpy as np\nfrom mmcv.transforms import BaseTransform\n\nfrom mmdet.registry import TRANSFORMS\n\n\n@TRANSFORMS.register_module()\nclass InstaBoost(BaseTransform):\n    r\"\"\"Data augmentation method in `InstaBoost: Boosting Instance\n    Segmentation Via Probability Map Guided Copy-Pasting\n    <https://arxiv.org/abs/1908.07801>`_.\n\n    Refer to https://github.com/GothicAi/Instaboost for implementation details.\n\n\n    Required Keys:\n\n    - img (np.uint8)\n    - instances\n\n    Modified Keys:\n\n    - img (np.uint8)\n    - instances\n\n    Args:\n        action_candidate (tuple): Action candidates. \"normal\", \"horizontal\", \\\n            \"vertical\", \"skip\" are supported. Defaults to ('normal', \\\n            'horizontal', 'skip').\n        action_prob (tuple): Corresponding action probabilities. Should be \\\n            the same length as action_candidate. Defaults to (1, 0, 0).\n        scale (tuple): (min scale, max scale). Defaults to (0.8, 1.2).\n        dx (int): The maximum x-axis shift will be (instance width) / dx.\n            Defaults to 15.\n        dy (int): The maximum y-axis shift will be (instance height) / dy.\n            Defaults to 15.\n        theta (tuple): (min rotation degree, max rotation degree). \\\n            Defaults to (-1, 1).\n        color_prob (float): Probability of images for color augmentation.\n            Defaults to 0.5.\n        hflag (bool): Whether to use heatmap guided. Defaults to False.\n        aug_ratio (float): Probability of applying this transformation. \\\n            Defaults to 0.5.\n    \"\"\"\n\n    def __init__(self,\n                 action_candidate: tuple = ('normal', 'horizontal', 'skip'),\n                 action_prob: tuple = (1, 0, 0),\n                 scale: tuple = (0.8, 1.2),\n                 dx: int = 15,\n                 dy: int = 15,\n                 theta: tuple = (-1, 1),\n                 color_prob: float = 0.5,\n                 hflag: bool = False,\n                 aug_ratio: float = 0.5) -> None:\n\n        import matplotlib\n        import matplotlib.pyplot as plt\n        default_backend = plt.get_backend()\n\n        try:\n            import instaboostfast as instaboost\n        except ImportError:\n            raise ImportError(\n                'Please run \"pip install instaboostfast\" '\n                'to install instaboostfast first for instaboost augmentation.')\n\n        # instaboost will modify the default backend\n        # and cause visualization to fail.\n        matplotlib.use(default_backend)\n\n        self.cfg = instaboost.InstaBoostConfig(action_candidate, action_prob,\n                                               scale, dx, dy, theta,\n                                               color_prob, hflag)\n        self.aug_ratio = aug_ratio\n\n    def _load_anns(self, results: dict) -> Tuple[list, list]:\n        \"\"\"Convert raw anns to instaboost expected input format.\"\"\"\n        anns = []\n        ignore_anns = []\n        for instance in results['instances']:\n            label = instance['bbox_label']\n            bbox = instance['bbox']\n            mask = instance['mask']\n            x1, y1, x2, y2 = bbox\n            # assert (x2 - x1) >= 1 and (y2 - y1) >= 1\n            bbox = [x1, y1, x2 - x1, y2 - y1]\n\n            if instance['ignore_flag'] == 0:\n                anns.append({\n                    'category_id': label,\n                    'segmentation': mask,\n                    'bbox': bbox\n                })\n            else:\n                # Ignore instances without data augmentation\n                ignore_anns.append(instance)\n        return anns, ignore_anns\n\n    def _parse_anns(self, results: dict, anns: list, ignore_anns: list,\n                    img: np.ndarray) -> dict:\n        \"\"\"Restore the result of instaboost processing to the original anns\n        format.\"\"\"\n        instances = []\n        for ann in anns:\n            x1, y1, w, h = ann['bbox']\n            # TODO: more essential bug need to be fixed in instaboost\n            if w <= 0 or h <= 0:\n                continue\n            bbox = [x1, y1, x1 + w, y1 + h]\n            instances.append(\n                dict(\n                    bbox=bbox,\n                    bbox_label=ann['category_id'],\n                    mask=ann['segmentation'],\n                    ignore_flag=0))\n\n        instances.extend(ignore_anns)\n        results['img'] = img\n        results['instances'] = instances\n        return results\n\n    def transform(self, results) -> dict:\n        \"\"\"The transform function.\"\"\"\n        img = results['img']\n        ori_type = img.dtype\n        if 'instances' not in results or len(results['instances']) == 0:\n            return results\n\n        anns, ignore_anns = self._load_anns(results)\n        if np.random.choice([0, 1], p=[1 - self.aug_ratio, self.aug_ratio]):\n            try:\n                import instaboostfast as instaboost\n            except ImportError:\n                raise ImportError('Please run \"pip install instaboostfast\" '\n                                  'to install instaboostfast first.')\n            anns, img = instaboost.get_new_data(\n                anns, img.astype(np.uint8), self.cfg, background=None)\n\n        results = self._parse_anns(results, anns, ignore_anns,\n                                   img.astype(ori_type))\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(aug_ratio={self.aug_ratio})'\n        return repr_str\n"
  },
  {
    "path": "mmdet/datasets/transforms/loading.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport mmcv\nimport numpy as np\nimport pycocotools.mask as maskUtils\nimport torch\nfrom mmcv.transforms import BaseTransform\nfrom mmcv.transforms import LoadAnnotations as MMCV_LoadAnnotations\nfrom mmcv.transforms import LoadImageFromFile\nfrom mmengine.fileio import get\nfrom mmengine.structures import BaseDataElement\n\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.structures.bbox import get_box_type\nfrom mmdet.structures.bbox.box_type import autocast_box_type\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\n\n\n@TRANSFORMS.register_module()\nclass LoadImageFromNDArray(LoadImageFromFile):\n    \"\"\"Load an image from ``results['img']``.\n\n    Similar with :obj:`LoadImageFromFile`, but the image has been loaded as\n    :obj:`np.ndarray` in ``results['img']``. Can be used when loading image\n    from webcam.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n    - img_path\n    - img_shape\n    - ori_shape\n\n    Args:\n        to_float32 (bool): Whether to convert the loaded image to a float32\n            numpy array. If set to False, the loaded image is an uint8 array.\n            Defaults to False.\n    \"\"\"\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to add image meta information.\n\n        Args:\n            results (dict): Result dict with Webcam read image in\n                ``results['img']``.\n\n        Returns:\n            dict: The dict contains loaded image and meta information.\n        \"\"\"\n\n        img = results['img']\n        if self.to_float32:\n            img = img.astype(np.float32)\n\n        results['img_path'] = None\n        results['img'] = img\n        results['img_shape'] = img.shape[:2]\n        results['ori_shape'] = img.shape[:2]\n        return results\n\n\n@TRANSFORMS.register_module()\nclass LoadMultiChannelImageFromFiles(BaseTransform):\n    \"\"\"Load multi-channel images from a list of separate channel files.\n\n    Required Keys:\n\n    - img_path\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - ori_shape\n\n    Args:\n        to_float32 (bool): Whether to convert the loaded image to a float32\n            numpy array. If set to False, the loaded image is an uint8 array.\n            Defaults to False.\n        color_type (str): The flag argument for :func:``mmcv.imfrombytes``.\n            Defaults to 'unchanged'.\n        imdecode_backend (str): The image decoding backend type. The backend\n            argument for :func:``mmcv.imfrombytes``.\n            See :func:``mmcv.imfrombytes`` for details.\n            Defaults to 'cv2'.\n        file_client_args (dict): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet >= 3.0.0rc7. Defaults to None.\n    \"\"\"\n\n    def __init__(\n        self,\n        to_float32: bool = False,\n        color_type: str = 'unchanged',\n        imdecode_backend: str = 'cv2',\n        file_client_args: dict = None,\n        backend_args: dict = None,\n    ) -> None:\n        self.to_float32 = to_float32\n        self.color_type = color_type\n        self.imdecode_backend = imdecode_backend\n        self.backend_args = backend_args\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform functions to load multiple images and get images meta\n        information.\n\n        Args:\n            results (dict): Result dict from :obj:`mmdet.CustomDataset`.\n\n        Returns:\n            dict: The dict contains loaded images and meta information.\n        \"\"\"\n\n        assert isinstance(results['img_path'], list)\n        img = []\n        for name in results['img_path']:\n            img_bytes = get(name, backend_args=self.backend_args)\n            img.append(\n                mmcv.imfrombytes(\n                    img_bytes,\n                    flag=self.color_type,\n                    backend=self.imdecode_backend))\n        img = np.stack(img, axis=-1)\n        if self.to_float32:\n            img = img.astype(np.float32)\n\n        results['img'] = img\n        results['img_shape'] = img.shape[:2]\n        results['ori_shape'] = img.shape[:2]\n        return results\n\n    def __repr__(self):\n        repr_str = (f'{self.__class__.__name__}('\n                    f'to_float32={self.to_float32}, '\n                    f\"color_type='{self.color_type}', \"\n                    f\"imdecode_backend='{self.imdecode_backend}', \"\n                    f'backend_args={self.backend_args})')\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass LoadAnnotations(MMCV_LoadAnnotations):\n    \"\"\"Load and process the ``instances`` and ``seg_map`` annotation provided\n    by dataset.\n\n    The annotation format is as the following:\n\n    .. code-block:: python\n\n        {\n            'instances':\n            [\n                {\n                # List of 4 numbers representing the bounding box of the\n                # instance, in (x1, y1, x2, y2) order.\n                'bbox': [x1, y1, x2, y2],\n\n                # Label of image classification.\n                'bbox_label': 1,\n\n                # Used in instance/panoptic segmentation. The segmentation mask\n                # of the instance or the information of segments.\n                # 1. If list[list[float]], it represents a list of polygons,\n                # one for each connected component of the object. Each\n                # list[float] is one simple polygon in the format of\n                # [x1, y1, ..., xn, yn] (n >= 3). The Xs and Ys are absolute\n                # coordinates in unit of pixels.\n                # 2. If dict, it represents the per-pixel segmentation mask in\n                # COCO's compressed RLE format. The dict should have keys\n                # “size” and “counts”.  Can be loaded by pycocotools\n                'mask': list[list[float]] or dict,\n\n                }\n            ]\n            # Filename of semantic or panoptic segmentation ground truth file.\n            'seg_map_path': 'a/b/c'\n        }\n\n    After this module, the annotation has been changed to the format below:\n\n    .. code-block:: python\n\n        {\n            # In (x1, y1, x2, y2) order, float type. N is the number of bboxes\n            # in an image\n            'gt_bboxes': BaseBoxes(N, 4)\n             # In int type.\n            'gt_bboxes_labels': np.ndarray(N, )\n             # In built-in class\n            'gt_masks': PolygonMasks (H, W) or BitmapMasks (H, W)\n             # In uint8 type.\n            'gt_seg_map': np.ndarray (H, W)\n             # in (x, y, v) order, float type.\n        }\n\n    Required Keys:\n\n    - height\n    - width\n    - instances\n\n      - bbox (optional)\n      - bbox_label\n      - mask (optional)\n      - ignore_flag\n\n    - seg_map_path (optional)\n\n    Added Keys:\n\n    - gt_bboxes (BaseBoxes[torch.float32])\n    - gt_bboxes_labels (np.int64)\n    - gt_masks (BitmapMasks | PolygonMasks)\n    - gt_seg_map (np.uint8)\n    - gt_ignore_flags (bool)\n\n    Args:\n        with_bbox (bool): Whether to parse and load the bbox annotation.\n            Defaults to True.\n        with_label (bool): Whether to parse and load the label annotation.\n            Defaults to True.\n        with_mask (bool): Whether to parse and load the mask annotation.\n             Default: False.\n        with_seg (bool): Whether to parse and load the semantic segmentation\n            annotation. Defaults to False.\n        poly2mask (bool): Whether to convert mask to bitmap. Default: True.\n        box_type (str): The box type used to wrap the bboxes. If ``box_type``\n            is None, gt_bboxes will keep being np.ndarray. Defaults to 'hbox'.\n        reduce_zero_label (bool): Whether reduce all label value\n            by 1. Usually used for datasets where 0 is background label.\n            Defaults to False.\n        ignore_index (int): The label index to be ignored.\n            Valid only if reduce_zero_label is true. Defaults is 255.\n        imdecode_backend (str): The image decoding backend type. The backend\n            argument for :func:``mmcv.imfrombytes``.\n            See :fun:``mmcv.imfrombytes`` for details.\n            Defaults to 'cv2'.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n    \"\"\"\n\n    def __init__(\n            self,\n            with_mask: bool = False,\n            poly2mask: bool = True,\n            box_type: str = 'hbox',\n            # use for semseg\n            reduce_zero_label: bool = False,\n            ignore_index: int = 255,\n            **kwargs) -> None:\n        super(LoadAnnotations, self).__init__(**kwargs)\n        self.with_mask = with_mask\n        self.poly2mask = poly2mask\n        self.box_type = box_type\n        self.reduce_zero_label = reduce_zero_label\n        self.ignore_index = ignore_index\n\n    def _load_bboxes(self, results: dict) -> None:\n        \"\"\"Private function to load bounding box annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmengine.BaseDataset``.\n        Returns:\n            dict: The dict contains loaded bounding box annotations.\n        \"\"\"\n        gt_bboxes = []\n        gt_ignore_flags = []\n        for instance in results.get('instances', []):\n            gt_bboxes.append(instance['bbox'])\n            gt_ignore_flags.append(instance['ignore_flag'])\n        if self.box_type is None:\n            results['gt_bboxes'] = np.array(\n                gt_bboxes, dtype=np.float32).reshape((-1, 4))\n        else:\n            _, box_type_cls = get_box_type(self.box_type)\n            results['gt_bboxes'] = box_type_cls(gt_bboxes, dtype=torch.float32)\n        results['gt_ignore_flags'] = np.array(gt_ignore_flags, dtype=bool)\n\n    def _load_labels(self, results: dict) -> None:\n        \"\"\"Private function to load label annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmengine.BaseDataset``.\n\n        Returns:\n            dict: The dict contains loaded label annotations.\n        \"\"\"\n        gt_bboxes_labels = []\n        for instance in results.get('instances', []):\n            gt_bboxes_labels.append(instance['bbox_label'])\n        # TODO: Inconsistent with mmcv, consider how to deal with it later.\n        results['gt_bboxes_labels'] = np.array(\n            gt_bboxes_labels, dtype=np.int64)\n\n    def _poly2mask(self, mask_ann: Union[list, dict], img_h: int,\n                   img_w: int) -> np.ndarray:\n        \"\"\"Private function to convert masks represented with polygon to\n        bitmaps.\n\n        Args:\n            mask_ann (list | dict): Polygon mask annotation input.\n            img_h (int): The height of output mask.\n            img_w (int): The width of output mask.\n\n        Returns:\n            np.ndarray: The decode bitmap mask of shape (img_h, img_w).\n        \"\"\"\n\n        if isinstance(mask_ann, list):\n            # polygon -- a single object might consist of multiple parts\n            # we merge all parts into one mask rle code\n            rles = maskUtils.frPyObjects(mask_ann, img_h, img_w)\n            rle = maskUtils.merge(rles)\n        elif isinstance(mask_ann['counts'], list):\n            # uncompressed RLE\n            rle = maskUtils.frPyObjects(mask_ann, img_h, img_w)\n        else:\n            # rle\n            rle = mask_ann\n        mask = maskUtils.decode(rle)\n        return mask\n\n    def _process_masks(self, results: dict) -> list:\n        \"\"\"Process gt_masks and filter invalid polygons.\n\n        Args:\n            results (dict): Result dict from :obj:``mmengine.BaseDataset``.\n\n        Returns:\n            list: Processed gt_masks.\n        \"\"\"\n        gt_masks = []\n        gt_ignore_flags = []\n        for instance in results.get('instances', []):\n            gt_mask = instance['mask']\n            # If the annotation of segmentation mask is invalid,\n            # ignore the whole instance.\n            if isinstance(gt_mask, list):\n                gt_mask = [\n                    np.array(polygon) for polygon in gt_mask\n                    if len(polygon) % 2 == 0 and len(polygon) >= 6\n                ]\n                if len(gt_mask) == 0:\n                    # ignore this instance and set gt_mask to a fake mask\n                    instance['ignore_flag'] = 1\n                    gt_mask = [np.zeros(6)]\n            elif not self.poly2mask:\n                # `PolygonMasks` requires a ploygon of format List[np.array],\n                # other formats are invalid.\n                instance['ignore_flag'] = 1\n                gt_mask = [np.zeros(6)]\n            elif isinstance(gt_mask, dict) and \\\n                    not (gt_mask.get('counts') is not None and\n                         gt_mask.get('size') is not None and\n                         isinstance(gt_mask['counts'], (list, str))):\n                # if gt_mask is a dict, it should include `counts` and `size`,\n                # so that `BitmapMasks` can uncompressed RLE\n                instance['ignore_flag'] = 1\n                gt_mask = [np.zeros(6)]\n            gt_masks.append(gt_mask)\n            # re-process gt_ignore_flags\n            gt_ignore_flags.append(instance['ignore_flag'])\n        results['gt_ignore_flags'] = np.array(gt_ignore_flags, dtype=bool)\n        return gt_masks\n\n    def _load_masks(self, results: dict) -> None:\n        \"\"\"Private function to load mask annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmengine.BaseDataset``.\n        \"\"\"\n        h, w = results['ori_shape']\n        gt_masks = self._process_masks(results)\n        if self.poly2mask:\n            gt_masks = BitmapMasks(\n                [self._poly2mask(mask, h, w) for mask in gt_masks], h, w)\n        else:\n            # fake polygon masks will be ignored in `PackDetInputs`\n            gt_masks = PolygonMasks([mask for mask in gt_masks], h, w)\n        results['gt_masks'] = gt_masks\n\n    def _load_seg_map(self, results: dict) -> None:\n        \"\"\"Private function to load semantic segmentation annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmcv.BaseDataset``.\n\n        Returns:\n            dict: The dict contains loaded semantic segmentation annotations.\n        \"\"\"\n        if results.get('seg_map_path', None) is None:\n            return\n\n        img_bytes = get(\n            results['seg_map_path'], backend_args=self.backend_args)\n        gt_semantic_seg = mmcv.imfrombytes(\n            img_bytes, flag='unchanged',\n            backend=self.imdecode_backend).squeeze()\n\n        if self.reduce_zero_label:\n            # avoid using underflow conversion\n            gt_semantic_seg[gt_semantic_seg == 0] = self.ignore_index\n            gt_semantic_seg = gt_semantic_seg - 1\n            gt_semantic_seg[gt_semantic_seg == self.ignore_index -\n                            1] = self.ignore_index\n\n        # modify if custom classes\n        if results.get('label_map', None) is not None:\n            # Add deep copy to solve bug of repeatedly\n            # replace `gt_semantic_seg`, which is reported in\n            # https://github.com/open-mmlab/mmsegmentation/pull/1445/\n            gt_semantic_seg_copy = gt_semantic_seg.copy()\n            for old_id, new_id in results['label_map'].items():\n                gt_semantic_seg[gt_semantic_seg_copy == old_id] = new_id\n        results['gt_seg_map'] = gt_semantic_seg\n        results['ignore_index'] = self.ignore_index\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Function to load multiple types annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmengine.BaseDataset``.\n\n        Returns:\n            dict: The dict contains loaded bounding box, label and\n            semantic segmentation.\n        \"\"\"\n\n        if self.with_bbox:\n            self._load_bboxes(results)\n        if self.with_label:\n            self._load_labels(results)\n        if self.with_mask:\n            self._load_masks(results)\n        if self.with_seg:\n            self._load_seg_map(results)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(with_bbox={self.with_bbox}, '\n        repr_str += f'with_label={self.with_label}, '\n        repr_str += f'with_mask={self.with_mask}, '\n        repr_str += f'with_seg={self.with_seg}, '\n        repr_str += f'poly2mask={self.poly2mask}, '\n        repr_str += f\"imdecode_backend='{self.imdecode_backend}', \"\n        repr_str += f'backend_args={self.backend_args})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass LoadPanopticAnnotations(LoadAnnotations):\n    \"\"\"Load multiple types of panoptic annotations.\n\n    The annotation format is as the following:\n\n    .. code-block:: python\n\n        {\n            'instances':\n            [\n                {\n                # List of 4 numbers representing the bounding box of the\n                # instance, in (x1, y1, x2, y2) order.\n                'bbox': [x1, y1, x2, y2],\n\n                # Label of image classification.\n                'bbox_label': 1,\n                },\n                ...\n            ]\n            'segments_info':\n            [\n                {\n                # id = cls_id + instance_id * INSTANCE_OFFSET\n                'id': int,\n\n                # Contiguous category id defined in dataset.\n                'category': int\n\n                # Thing flag.\n                'is_thing': bool\n                },\n                ...\n            ]\n\n            # Filename of semantic or panoptic segmentation ground truth file.\n            'seg_map_path': 'a/b/c'\n        }\n\n    After this module, the annotation has been changed to the format below:\n\n    .. code-block:: python\n\n        {\n            # In (x1, y1, x2, y2) order, float type. N is the number of bboxes\n            # in an image\n            'gt_bboxes': BaseBoxes(N, 4)\n             # In int type.\n            'gt_bboxes_labels': np.ndarray(N, )\n             # In built-in class\n            'gt_masks': PolygonMasks (H, W) or BitmapMasks (H, W)\n             # In uint8 type.\n            'gt_seg_map': np.ndarray (H, W)\n             # in (x, y, v) order, float type.\n        }\n\n    Required Keys:\n\n    - height\n    - width\n    - instances\n      - bbox\n      - bbox_label\n      - ignore_flag\n    - segments_info\n      - id\n      - category\n      - is_thing\n    - seg_map_path\n\n    Added Keys:\n\n    - gt_bboxes (BaseBoxes[torch.float32])\n    - gt_bboxes_labels (np.int64)\n    - gt_masks (BitmapMasks | PolygonMasks)\n    - gt_seg_map (np.uint8)\n    - gt_ignore_flags (bool)\n\n    Args:\n        with_bbox (bool): Whether to parse and load the bbox annotation.\n            Defaults to True.\n        with_label (bool): Whether to parse and load the label annotation.\n            Defaults to True.\n        with_mask (bool): Whether to parse and load the mask annotation.\n             Defaults to True.\n        with_seg (bool): Whether to parse and load the semantic segmentation\n            annotation. Defaults to False.\n        box_type (str): The box mode used to wrap the bboxes.\n        imdecode_backend (str): The image decoding backend type. The backend\n            argument for :func:``mmcv.imfrombytes``.\n            See :fun:``mmcv.imfrombytes`` for details.\n            Defaults to 'cv2'.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet >= 3.0.0rc7. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 with_bbox: bool = True,\n                 with_label: bool = True,\n                 with_mask: bool = True,\n                 with_seg: bool = True,\n                 box_type: str = 'hbox',\n                 imdecode_backend: str = 'cv2',\n                 backend_args: dict = None) -> None:\n        try:\n            from panopticapi import utils\n        except ImportError:\n            raise ImportError(\n                'panopticapi is not installed, please install it by: '\n                'pip install git+https://github.com/cocodataset/'\n                'panopticapi.git.')\n        self.rgb2id = utils.rgb2id\n\n        super(LoadPanopticAnnotations, self).__init__(\n            with_bbox=with_bbox,\n            with_label=with_label,\n            with_mask=with_mask,\n            with_seg=with_seg,\n            with_keypoints=False,\n            box_type=box_type,\n            imdecode_backend=imdecode_backend,\n            backend_args=backend_args)\n\n    def _load_masks_and_semantic_segs(self, results: dict) -> None:\n        \"\"\"Private function to load mask and semantic segmentation annotations.\n\n        In gt_semantic_seg, the foreground label is from ``0`` to\n        ``num_things - 1``, the background label is from ``num_things`` to\n        ``num_things + num_stuff - 1``, 255 means the ignored label (``VOID``).\n\n        Args:\n            results (dict): Result dict from :obj:``mmdet.CustomDataset``.\n        \"\"\"\n        # seg_map_path is None, when inference on the dataset without gts.\n        if results.get('seg_map_path', None) is None:\n            return\n\n        img_bytes = get(\n            results['seg_map_path'], backend_args=self.backend_args)\n        pan_png = mmcv.imfrombytes(\n            img_bytes, flag='color', channel_order='rgb').squeeze()\n        pan_png = self.rgb2id(pan_png)\n\n        gt_masks = []\n        gt_seg = np.zeros_like(pan_png) + 255  # 255 as ignore\n\n        for segment_info in results['segments_info']:\n            mask = (pan_png == segment_info['id'])\n            gt_seg = np.where(mask, segment_info['category'], gt_seg)\n\n            # The legal thing masks\n            if segment_info.get('is_thing'):\n                gt_masks.append(mask.astype(np.uint8))\n\n        if self.with_mask:\n            h, w = results['ori_shape']\n            gt_masks = BitmapMasks(gt_masks, h, w)\n            results['gt_masks'] = gt_masks\n\n        if self.with_seg:\n            results['gt_seg_map'] = gt_seg\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Function to load multiple types panoptic annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmdet.CustomDataset``.\n\n        Returns:\n            dict: The dict contains loaded bounding box, label, mask and\n                semantic segmentation annotations.\n        \"\"\"\n\n        if self.with_bbox:\n            self._load_bboxes(results)\n        if self.with_label:\n            self._load_labels(results)\n        if self.with_mask or self.with_seg:\n            # The tasks completed by '_load_masks' and '_load_semantic_segs'\n            # in LoadAnnotations are merged to one function.\n            self._load_masks_and_semantic_segs(results)\n\n        return results\n\n\n@TRANSFORMS.register_module()\nclass LoadProposals(BaseTransform):\n    \"\"\"Load proposal pipeline.\n\n    Required Keys:\n\n    - proposals\n\n    Modified Keys:\n\n    - proposals\n\n    Args:\n        num_max_proposals (int, optional): Maximum number of proposals to load.\n            If not specified, all proposals will be loaded.\n    \"\"\"\n\n    def __init__(self, num_max_proposals: Optional[int] = None) -> None:\n        self.num_max_proposals = num_max_proposals\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to load proposals from file.\n\n        Args:\n            results (dict): Result dict from :obj:`mmdet.CustomDataset`.\n\n        Returns:\n            dict: The dict contains loaded proposal annotations.\n        \"\"\"\n\n        proposals = results['proposals']\n        # the type of proposals should be `dict` or `InstanceData`\n        assert isinstance(proposals, dict) \\\n               or isinstance(proposals, BaseDataElement)\n        bboxes = proposals['bboxes'].astype(np.float32)\n        assert bboxes.shape[1] == 4, \\\n            f'Proposals should have shapes (n, 4), but found {bboxes.shape}'\n\n        if 'scores' in proposals:\n            scores = proposals['scores'].astype(np.float32)\n            assert bboxes.shape[0] == scores.shape[0]\n        else:\n            scores = np.zeros(bboxes.shape[0], dtype=np.float32)\n\n        if self.num_max_proposals is not None:\n            # proposals should sort by scores during dumping the proposals\n            bboxes = bboxes[:self.num_max_proposals]\n            scores = scores[:self.num_max_proposals]\n\n        if len(bboxes) == 0:\n            bboxes = np.zeros((0, 4), dtype=np.float32)\n            scores = np.zeros(0, dtype=np.float32)\n\n        results['proposals'] = bboxes\n        results['proposals_scores'] = scores\n        return results\n\n    def __repr__(self):\n        return self.__class__.__name__ + \\\n               f'(num_max_proposals={self.num_max_proposals})'\n\n\n@TRANSFORMS.register_module()\nclass FilterAnnotations(BaseTransform):\n    \"\"\"Filter invalid annotations.\n\n    Required Keys:\n\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_ignore_flags (bool) (optional)\n\n    Modified Keys:\n\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_masks (optional)\n    - gt_ignore_flags (optional)\n\n    Args:\n        min_gt_bbox_wh (tuple[float]): Minimum width and height of ground truth\n            boxes. Default: (1., 1.)\n        min_gt_mask_area (int): Minimum foreground area of ground truth masks.\n            Default: 1\n        by_box (bool): Filter instances with bounding boxes not meeting the\n            min_gt_bbox_wh threshold. Default: True\n        by_mask (bool): Filter instances with masks not meeting\n            min_gt_mask_area threshold. Default: False\n        keep_empty (bool): Whether to return None when it\n            becomes an empty bbox after filtering. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 min_gt_bbox_wh: Tuple[int, int] = (1, 1),\n                 min_gt_mask_area: int = 1,\n                 by_box: bool = True,\n                 by_mask: bool = False,\n                 keep_empty: bool = True) -> None:\n        # TODO: add more filter options\n        assert by_box or by_mask\n        self.min_gt_bbox_wh = min_gt_bbox_wh\n        self.min_gt_mask_area = min_gt_mask_area\n        self.by_box = by_box\n        self.by_mask = by_mask\n        self.keep_empty = keep_empty\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> Union[dict, None]:\n        \"\"\"Transform function to filter annotations.\n\n        Args:\n            results (dict): Result dict.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        assert 'gt_bboxes' in results\n        gt_bboxes = results['gt_bboxes']\n        if gt_bboxes.shape[0] == 0:\n            return results\n\n        tests = []\n        if self.by_box:\n            tests.append(\n                ((gt_bboxes.widths > self.min_gt_bbox_wh[0]) &\n                 (gt_bboxes.heights > self.min_gt_bbox_wh[1])).numpy())\n        if self.by_mask:\n            assert 'gt_masks' in results\n            gt_masks = results['gt_masks']\n            tests.append(gt_masks.areas >= self.min_gt_mask_area)\n\n        keep = tests[0]\n        for t in tests[1:]:\n            keep = keep & t\n\n        if not keep.any():\n            if self.keep_empty:\n                return None\n\n        keys = ('gt_bboxes', 'gt_bboxes_labels', 'gt_masks', 'gt_ignore_flags')\n        for key in keys:\n            if key in results:\n                results[key] = results[key][keep]\n\n        return results\n\n    def __repr__(self):\n        return self.__class__.__name__ + \\\n               f'(min_gt_bbox_wh={self.min_gt_bbox_wh}, ' \\\n               f'keep_empty={self.keep_empty})'\n\n\n@TRANSFORMS.register_module()\nclass LoadEmptyAnnotations(BaseTransform):\n    \"\"\"Load Empty Annotations for unlabeled images.\n\n    Added Keys:\n    - gt_bboxes (np.float32)\n    - gt_bboxes_labels (np.int64)\n    - gt_masks (BitmapMasks | PolygonMasks)\n    - gt_seg_map (np.uint8)\n    - gt_ignore_flags (bool)\n\n    Args:\n        with_bbox (bool): Whether to load the pseudo bbox annotation.\n            Defaults to True.\n        with_label (bool): Whether to load the pseudo label annotation.\n            Defaults to True.\n        with_mask (bool): Whether to load the pseudo mask annotation.\n             Default: False.\n        with_seg (bool): Whether to load the pseudo semantic segmentation\n            annotation. Defaults to False.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n    \"\"\"\n\n    def __init__(self,\n                 with_bbox: bool = True,\n                 with_label: bool = True,\n                 with_mask: bool = False,\n                 with_seg: bool = False,\n                 seg_ignore_label: int = 255) -> None:\n        self.with_bbox = with_bbox\n        self.with_label = with_label\n        self.with_mask = with_mask\n        self.with_seg = with_seg\n        self.seg_ignore_label = seg_ignore_label\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to load empty annotations.\n\n        Args:\n            results (dict): Result dict.\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        if self.with_bbox:\n            results['gt_bboxes'] = np.zeros((0, 4), dtype=np.float32)\n            results['gt_ignore_flags'] = np.zeros((0, ), dtype=bool)\n        if self.with_label:\n            results['gt_bboxes_labels'] = np.zeros((0, ), dtype=np.int64)\n        if self.with_mask:\n            # TODO: support PolygonMasks\n            h, w = results['img_shape']\n            gt_masks = np.zeros((0, h, w), dtype=np.uint8)\n            results['gt_masks'] = BitmapMasks(gt_masks, h, w)\n        if self.with_seg:\n            h, w = results['img_shape']\n            results['gt_seg_map'] = self.seg_ignore_label * np.ones(\n                (h, w), dtype=np.uint8)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(with_bbox={self.with_bbox}, '\n        repr_str += f'with_label={self.with_label}, '\n        repr_str += f'with_mask={self.with_mask}, '\n        repr_str += f'with_seg={self.with_seg}, '\n        repr_str += f'seg_ignore_label={self.seg_ignore_label})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass InferencerLoader(BaseTransform):\n    \"\"\"Load an image from ``results['img']``.\n\n    Similar with :obj:`LoadImageFromFile`, but the image has been loaded as\n    :obj:`np.ndarray` in ``results['img']``. Can be used when loading image\n    from webcam.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n    - img_path\n    - img_shape\n    - ori_shape\n\n    Args:\n        to_float32 (bool): Whether to convert the loaded image to a float32\n            numpy array. If set to False, the loaded image is an uint8 array.\n            Defaults to False.\n    \"\"\"\n\n    def __init__(self, **kwargs) -> None:\n        super().__init__()\n        self.from_file = TRANSFORMS.build(\n            dict(type='LoadImageFromFile', **kwargs))\n        self.from_ndarray = TRANSFORMS.build(\n            dict(type='mmdet.LoadImageFromNDArray', **kwargs))\n\n    def transform(self, results: Union[str, np.ndarray, dict]) -> dict:\n        \"\"\"Transform function to add image meta information.\n\n        Args:\n            results (str, np.ndarray or dict): The result.\n\n        Returns:\n            dict: The dict contains loaded image and meta information.\n        \"\"\"\n        if isinstance(results, str):\n            inputs = dict(img_path=results)\n        elif isinstance(results, np.ndarray):\n            inputs = dict(img=results)\n        elif isinstance(results, dict):\n            inputs = results\n        else:\n            raise NotImplementedError\n\n        if 'img' in inputs:\n            return self.from_ndarray(inputs)\n        return self.from_file(inputs)\n\n\n@TRANSFORMS.register_module()\nclass LoadTrackAnnotations(LoadAnnotations):\n    \"\"\"Load and process the ``instances`` and ``seg_map`` annotation provided\n    by dataset. It must load ``instances_ids`` which is only used in the\n    tracking tasks. The annotation format is as the following:\n\n    .. code-block:: python\n        {\n            'instances':\n            [\n                {\n                # List of 4 numbers representing the bounding box of the\n                # instance, in (x1, y1, x2, y2) order.\n                'bbox': [x1, y1, x2, y2],\n                # Label of image classification.\n                'bbox_label': 1,\n                # Used in tracking.\n                # Id of instances.\n                'instance_id': 100,\n                # Used in instance/panoptic segmentation. The segmentation mask\n                # of the instance or the information of segments.\n                # 1. If list[list[float]], it represents a list of polygons,\n                # one for each connected component of the object. Each\n                # list[float] is one simple polygon in the format of\n                # [x1, y1, ..., xn, yn] (n >= 3). The Xs and Ys are absolute\n                # coordinates in unit of pixels.\n                # 2. If dict, it represents the per-pixel segmentation mask in\n                # COCO's compressed RLE format. The dict should have keys\n                # “size” and “counts”.  Can be loaded by pycocotools\n                'mask': list[list[float]] or dict,\n                }\n            ]\n            # Filename of semantic or panoptic segmentation ground truth file.\n            'seg_map_path': 'a/b/c'\n        }\n\n    After this module, the annotation has been changed to the format below:\n    .. code-block:: python\n        {\n            # In (x1, y1, x2, y2) order, float type. N is the number of bboxes\n            # in an image\n            'gt_bboxes': np.ndarray(N, 4)\n             # In int type.\n            'gt_bboxes_labels': np.ndarray(N, )\n             # In built-in class\n            'gt_masks': PolygonMasks (H, W) or BitmapMasks (H, W)\n             # In uint8 type.\n            'gt_seg_map': np.ndarray (H, W)\n             # in (x, y, v) order, float type.\n        }\n\n    Required Keys:\n\n    - height (optional)\n    - width (optional)\n    - instances\n      - bbox (optional)\n      - bbox_label\n      - instance_id (optional)\n      - mask (optional)\n      - ignore_flag (optional)\n    - seg_map_path (optional)\n\n    Added Keys:\n\n    - gt_bboxes (np.float32)\n    - gt_bboxes_labels (np.int32)\n    - gt_instances_ids (np.int32)\n    - gt_masks (BitmapMasks | PolygonMasks)\n    - gt_seg_map (np.uint8)\n    - gt_ignore_flags (np.bool)\n    \"\"\"\n\n    def __init__(self, **kwargs) -> None:\n        super().__init__(**kwargs)\n\n    def _load_bboxes(self, results: dict) -> None:\n        \"\"\"Private function to load bounding box annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmcv.BaseDataset``.\n\n        Returns:\n            dict: The dict contains loaded bounding box annotations.\n        \"\"\"\n        gt_bboxes = []\n        gt_ignore_flags = []\n        # TODO: use bbox_type\n        for instance in results['instances']:\n            # The datasets which are only format in evaluation don't have\n            # groundtruth boxes.\n            if 'bbox' in instance:\n                gt_bboxes.append(instance['bbox'])\n            if 'ignore_flag' in instance:\n                gt_ignore_flags.append(instance['ignore_flag'])\n\n        # TODO: check this case\n        if len(gt_bboxes) != len(gt_ignore_flags):\n            # There may be no ``gt_ignore_flags`` in some cases, we treat them\n            # as all False in order to keep the length of ``gt_bboxes`` and\n            # ``gt_ignore_flags`` the same\n            gt_ignore_flags = [False] * len(gt_bboxes)\n\n        results['gt_bboxes'] = np.array(\n            gt_bboxes, dtype=np.float32).reshape(-1, 4)\n        results['gt_ignore_flags'] = np.array(gt_ignore_flags, dtype=bool)\n\n    def _load_instances_ids(self, results: dict) -> None:\n        \"\"\"Private function to load instances id annotations.\n\n        Args:\n            results (dict): Result dict from :obj :obj:``mmcv.BaseDataset``.\n\n        Returns:\n            dict: The dict containing instances id annotations.\n        \"\"\"\n        gt_instances_ids = []\n        for instance in results['instances']:\n            gt_instances_ids.append(instance['instance_id'])\n        results['gt_instances_ids'] = np.array(\n            gt_instances_ids, dtype=np.int32)\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Function to load multiple types annotations.\n\n        Args:\n            results (dict): Result dict from :obj:``mmcv.BaseDataset``.\n\n        Returns:\n            dict: The dict contains loaded bounding box, label, instances id\n            and semantic segmentation and keypoints annotations.\n        \"\"\"\n        results = super().transform(results)\n        self._load_instances_ids(results)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(with_bbox={self.with_bbox}, '\n        repr_str += f'with_label={self.with_label}, '\n        repr_str += f'with_mask={self.with_mask}, '\n        repr_str += f'with_seg={self.with_seg}, '\n        repr_str += f'poly2mask={self.poly2mask}, '\n        repr_str += f\"imdecode_backend='{self.imdecode_backend}', \"\n        repr_str += f'file_client_args={self.file_client_args})'\n        return repr_str\n"
  },
  {
    "path": "mmdet/datasets/transforms/text_transformers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\n\nfrom mmcv.transforms import BaseTransform\n\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.structures.bbox import BaseBoxes\n\ntry:\n    from transformers import AutoTokenizer\n    from transformers import BertModel as HFBertModel\nexcept ImportError:\n    AutoTokenizer = None\n    HFBertModel = None\n\nimport random\nimport re\n\nimport numpy as np\n\n\ndef clean_name(name):\n    name = re.sub(r'\\(.*\\)', '', name)\n    name = re.sub(r'_', ' ', name)\n    name = re.sub(r'  ', ' ', name)\n    name = name.lower()\n    return name\n\n\ndef check_for_positive_overflow(gt_bboxes, gt_labels, text, tokenizer,\n                                max_tokens):\n    # Check if we have too many positive labels\n    # generate a caption by appending the positive labels\n    positive_label_list = np.unique(gt_labels).tolist()\n    # random shuffule so we can sample different annotations\n    # at different epochs\n    random.shuffle(positive_label_list)\n\n    kept_lables = []\n    length = 0\n\n    for index, label in enumerate(positive_label_list):\n\n        label_text = clean_name(text[str(label)]) + '. '\n\n        tokenized = tokenizer.tokenize(label_text)\n\n        length += len(tokenized)\n\n        if length > max_tokens:\n            break\n        else:\n            kept_lables.append(label)\n\n    keep_box_index = []\n    keep_gt_labels = []\n    for i in range(len(gt_labels)):\n        if gt_labels[i] in kept_lables:\n            keep_box_index.append(i)\n            keep_gt_labels.append(gt_labels[i])\n\n    return gt_bboxes[keep_box_index], np.array(\n        keep_gt_labels, dtype=np.long), length\n\n\ndef generate_senetence_given_labels(positive_label_list, negative_label_list,\n                                    text):\n    label_to_positions = {}\n\n    label_list = negative_label_list + positive_label_list\n\n    random.shuffle(label_list)\n\n    pheso_caption = ''\n\n    label_remap_dict = {}\n    for index, label in enumerate(label_list):\n\n        start_index = len(pheso_caption)\n\n        pheso_caption += clean_name(text[str(label)])\n\n        end_index = len(pheso_caption)\n\n        if label in positive_label_list:\n            label_to_positions[index] = [[start_index, end_index]]\n            label_remap_dict[int(label)] = index\n\n        # if index != len(label_list) - 1:\n        #     pheso_caption += '. '\n        pheso_caption += '. '\n\n    return label_to_positions, pheso_caption, label_remap_dict\n\n\n@TRANSFORMS.register_module()\nclass RandomSamplingNegPos(BaseTransform):\n\n    def __init__(self,\n                 tokenizer_name,\n                 num_sample_negative=85,\n                 max_tokens=256,\n                 full_sampling_prob=0.5,\n                 label_map_file=None):\n        if AutoTokenizer is None:\n            raise RuntimeError(\n                'transformers is not installed, please install it by: '\n                'pip install transformers.')\n\n        self.tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)\n        self.num_sample_negative = num_sample_negative\n        self.full_sampling_prob = full_sampling_prob\n        self.max_tokens = max_tokens\n        self.label_map = None\n        if label_map_file:\n            with open(label_map_file, 'r') as file:\n                self.label_map = json.load(file)\n\n    def transform(self, results: dict) -> dict:\n        if 'phrases' in results:\n            return self.vg_aug(results)\n        else:\n            return self.od_aug(results)\n\n    def vg_aug(self, results):\n        gt_bboxes = results['gt_bboxes']\n        if isinstance(gt_bboxes, BaseBoxes):\n            gt_bboxes = gt_bboxes.tensor\n        gt_labels = results['gt_bboxes_labels']\n        text = results['text'].lower().strip()\n        if not text.endswith('.'):\n            text = text + '. '\n\n        phrases = results['phrases']\n        # TODO: add neg\n        positive_label_list = np.unique(gt_labels).tolist()\n        label_to_positions = {}\n        for label in positive_label_list:\n            label_to_positions[label] = phrases[label]['tokens_positive']\n\n        results['gt_bboxes'] = gt_bboxes\n        results['gt_bboxes_labels'] = gt_labels\n\n        results['text'] = text\n        results['tokens_positive'] = label_to_positions\n        return results\n\n    def od_aug(self, results):\n        gt_bboxes = results['gt_bboxes']\n        if isinstance(gt_bboxes, BaseBoxes):\n            gt_bboxes = gt_bboxes.tensor\n        gt_labels = results['gt_bboxes_labels']\n\n        if 'text' not in results:\n            assert self.label_map is not None\n            text = self.label_map\n        else:\n            text = results['text']\n\n        original_box_num = len(gt_labels)\n        # If the category name is in the format of 'a/b' (in object365),\n        # we randomly select one of them.\n        for key, value in text.items():\n            if '/' in value:\n                text[key] = random.choice(value.split('/')).strip()\n\n        gt_bboxes, gt_labels, positive_caption_length = \\\n            check_for_positive_overflow(gt_bboxes, gt_labels,\n                                        text, self.tokenizer, self.max_tokens)\n\n        if len(gt_bboxes) < original_box_num:\n            print('WARNING: removed {} boxes due to positive caption overflow'.\n                  format(original_box_num - len(gt_bboxes)))\n\n        valid_negative_indexes = list(text.keys())\n\n        positive_label_list = np.unique(gt_labels).tolist()\n        full_negative = self.num_sample_negative\n\n        if full_negative > len(valid_negative_indexes):\n            full_negative = len(valid_negative_indexes)\n\n        outer_prob = random.random()\n\n        if outer_prob < self.full_sampling_prob:\n            # c. probability_full: add both all positive and all negatives\n            num_negatives = full_negative\n        else:\n            if random.random() < 1.0:\n                num_negatives = np.random.choice(max(1, full_negative)) + 1\n            else:\n                num_negatives = full_negative\n\n        # Keep some negatives\n        negative_label_list = set()\n        if num_negatives != -1:\n            if num_negatives > len(valid_negative_indexes):\n                num_negatives = len(valid_negative_indexes)\n\n            for i in np.random.choice(\n                    valid_negative_indexes, size=num_negatives, replace=False):\n                if int(i) not in positive_label_list:\n                    negative_label_list.add(i)\n\n        random.shuffle(positive_label_list)\n\n        negative_label_list = list(negative_label_list)\n        random.shuffle(negative_label_list)\n\n        negative_max_length = self.max_tokens - positive_caption_length\n        screened_negative_label_list = []\n\n        for negative_label in negative_label_list:\n            label_text = clean_name(text[str(negative_label)]) + '. '\n\n            tokenized = self.tokenizer.tokenize(label_text)\n\n            negative_max_length -= len(tokenized)\n\n            if negative_max_length > 0:\n                screened_negative_label_list.append(negative_label)\n            else:\n                break\n        negative_label_list = screened_negative_label_list\n        label_to_positions, pheso_caption, label_remap_dict = \\\n            generate_senetence_given_labels(positive_label_list,\n                                            negative_label_list, text)\n\n        # label remap\n        if len(gt_labels) > 0:\n            gt_labels = np.vectorize(lambda x: label_remap_dict[x])(gt_labels)\n\n        results['gt_bboxes'] = gt_bboxes\n        results['gt_bboxes_labels'] = gt_labels\n\n        results['text'] = pheso_caption\n        results['tokens_positive'] = label_to_positions\n\n        return results\n\n\n@TRANSFORMS.register_module()\nclass LoadTextAnnotations(BaseTransform):\n\n    def transform(self, results: dict) -> dict:\n        if 'phrases' in results:\n            tokens_positive = [\n                phrase['tokens_positive']\n                for phrase in results['phrases'].values()\n            ]\n            results['tokens_positive'] = tokens_positive\n        else:\n            text = results['text']\n            results['text'] = list(text.values())\n        return results\n"
  },
  {
    "path": "mmdet/datasets/transforms/transformers_glip.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport mmcv\nimport numpy as np\nfrom mmcv.transforms import BaseTransform\n\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.structures.bbox import HorizontalBoxes, autocast_box_type\nfrom .transforms import RandomFlip\n\n\n@TRANSFORMS.register_module()\nclass GTBoxSubOne_GLIP(BaseTransform):\n    \"\"\"Subtract 1 from the x2 and y2 coordinates of the gt_bboxes.\"\"\"\n\n    def transform(self, results: dict) -> dict:\n        if 'gt_bboxes' in results:\n            gt_bboxes = results['gt_bboxes']\n            if isinstance(gt_bboxes, np.ndarray):\n                gt_bboxes[:, 2:] -= 1\n                results['gt_bboxes'] = gt_bboxes\n            elif isinstance(gt_bboxes, HorizontalBoxes):\n                gt_bboxes = results['gt_bboxes'].tensor\n                gt_bboxes[:, 2:] -= 1\n                results['gt_bboxes'] = HorizontalBoxes(gt_bboxes)\n            else:\n                raise NotImplementedError\n        return results\n\n\n@TRANSFORMS.register_module()\nclass RandomFlip_GLIP(RandomFlip):\n    \"\"\"Flip the image & bboxes & masks & segs horizontally or vertically.\n\n    When using horizontal flipping, the corresponding bbox x-coordinate needs\n    to be additionally subtracted by one.\n    \"\"\"\n\n    @autocast_box_type()\n    def _flip(self, results: dict) -> None:\n        \"\"\"Flip images, bounding boxes, and semantic segmentation map.\"\"\"\n        # flip image\n        results['img'] = mmcv.imflip(\n            results['img'], direction=results['flip_direction'])\n\n        img_shape = results['img'].shape[:2]\n\n        # flip bboxes\n        if results.get('gt_bboxes', None) is not None:\n            results['gt_bboxes'].flip_(img_shape, results['flip_direction'])\n            # Only change this line\n            if results['flip_direction'] == 'horizontal':\n                results['gt_bboxes'].translate_([-1, 0])\n\n        # TODO: check it\n        # flip masks\n        if results.get('gt_masks', None) is not None:\n            results['gt_masks'] = results['gt_masks'].flip(\n                results['flip_direction'])\n\n        # flip segs\n        if results.get('gt_seg_map', None) is not None:\n            results['gt_seg_map'] = mmcv.imflip(\n                results['gt_seg_map'], direction=results['flip_direction'])\n\n        # record homography matrix for flip\n        self._record_homography_matrix(results)\n"
  },
  {
    "path": "mmdet/datasets/transforms/transforms.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport inspect\nimport math\nimport warnings\nfrom typing import List, Optional, Sequence, Tuple, Union\n\nimport cv2\nimport mmcv\nimport numpy as np\nfrom mmcv.image import imresize\nfrom mmcv.image.geometric import _scale_size\nfrom mmcv.transforms import BaseTransform\nfrom mmcv.transforms import Pad as MMCV_Pad\nfrom mmcv.transforms import RandomFlip as MMCV_RandomFlip\nfrom mmcv.transforms import Resize as MMCV_Resize\nfrom mmcv.transforms.utils import avoid_cache_randomness, cache_randomness\nfrom mmengine.dataset import BaseDataset\nfrom mmengine.utils import is_str\nfrom numpy import random\n\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.structures.bbox import HorizontalBoxes, autocast_box_type\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\nfrom mmdet.utils import log_img_scale\n\ntry:\n    from imagecorruptions import corrupt\nexcept ImportError:\n    corrupt = None\n\ntry:\n    import albumentations\n    from albumentations import Compose\nexcept ImportError:\n    albumentations = None\n    Compose = None\n\nNumber = Union[int, float]\n\n\ndef _fixed_scale_size(\n    size: Tuple[int, int],\n    scale: Union[float, int, tuple],\n) -> Tuple[int, int]:\n    \"\"\"Rescale a size by a ratio.\n\n    Args:\n        size (tuple[int]): (w, h).\n        scale (float | tuple(float)): Scaling factor.\n\n    Returns:\n        tuple[int]: scaled size.\n    \"\"\"\n    if isinstance(scale, (float, int)):\n        scale = (scale, scale)\n    w, h = size\n    # don't need o.5 offset\n    return int(w * float(scale[0])), int(h * float(scale[1]))\n\n\ndef rescale_size(old_size: tuple,\n                 scale: Union[float, int, tuple],\n                 return_scale: bool = False) -> tuple:\n    \"\"\"Calculate the new size to be rescaled to.\n\n    Args:\n        old_size (tuple[int]): The old size (w, h) of image.\n        scale (float | tuple[int]): The scaling factor or maximum size.\n            If it is a float number, then the image will be rescaled by this\n            factor, else if it is a tuple of 2 integers, then the image will\n            be rescaled as large as possible within the scale.\n        return_scale (bool): Whether to return the scaling factor besides the\n            rescaled image size.\n\n    Returns:\n        tuple[int]: The new rescaled image size.\n    \"\"\"\n    w, h = old_size\n    if isinstance(scale, (float, int)):\n        if scale <= 0:\n            raise ValueError(f'Invalid scale {scale}, must be positive.')\n        scale_factor = scale\n    elif isinstance(scale, tuple):\n        max_long_edge = max(scale)\n        max_short_edge = min(scale)\n        scale_factor = min(max_long_edge / max(h, w),\n                           max_short_edge / min(h, w))\n    else:\n        raise TypeError(\n            f'Scale must be a number or tuple of int, but got {type(scale)}')\n    # only change this\n    new_size = _fixed_scale_size((w, h), scale_factor)\n\n    if return_scale:\n        return new_size, scale_factor\n    else:\n        return new_size\n\n\ndef imrescale(\n    img: np.ndarray,\n    scale: Union[float, Tuple[int, int]],\n    return_scale: bool = False,\n    interpolation: str = 'bilinear',\n    backend: Optional[str] = None\n) -> Union[np.ndarray, Tuple[np.ndarray, float]]:\n    \"\"\"Resize image while keeping the aspect ratio.\n\n    Args:\n        img (ndarray): The input image.\n        scale (float | tuple[int]): The scaling factor or maximum size.\n            If it is a float number, then the image will be rescaled by this\n            factor, else if it is a tuple of 2 integers, then the image will\n            be rescaled as large as possible within the scale.\n        return_scale (bool): Whether to return the scaling factor besides the\n            rescaled image.\n        interpolation (str): Same as :func:`resize`.\n        backend (str | None): Same as :func:`resize`.\n\n    Returns:\n        ndarray: The rescaled image.\n    \"\"\"\n    h, w = img.shape[:2]\n    new_size, scale_factor = rescale_size((w, h), scale, return_scale=True)\n    rescaled_img = imresize(\n        img, new_size, interpolation=interpolation, backend=backend)\n    if return_scale:\n        return rescaled_img, scale_factor\n    else:\n        return rescaled_img\n\n\n@TRANSFORMS.register_module()\nclass Resize(MMCV_Resize):\n    \"\"\"Resize images & bbox & seg.\n\n    This transform resizes the input image according to ``scale`` or\n    ``scale_factor``. Bboxes, masks, and seg map are then resized\n    with the same scale factor.\n    if ``scale`` and ``scale_factor`` are both set, it will use ``scale`` to\n    resize.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n\n    Added Keys:\n\n    - scale\n    - scale_factor\n    - keep_ratio\n    - homography_matrix\n\n    Args:\n        scale (int or tuple): Images scales for resizing. Defaults to None\n        scale_factor (float or tuple[float]): Scale factors for resizing.\n            Defaults to None.\n        keep_ratio (bool): Whether to keep the aspect ratio when resizing the\n            image. Defaults to False.\n        clip_object_border (bool): Whether to clip the objects\n            outside the border of the image. In some dataset like MOT17, the gt\n            bboxes are allowed to cross the border of images. Therefore, we\n            don't need to clip the gt bboxes in these cases. Defaults to True.\n        backend (str): Image resize backend, choices are 'cv2' and 'pillow'.\n            These two backends generates slightly different results. Defaults\n            to 'cv2'.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def _resize_masks(self, results: dict) -> None:\n        \"\"\"Resize masks with ``results['scale']``\"\"\"\n        if results.get('gt_masks', None) is not None:\n            if self.keep_ratio:\n                results['gt_masks'] = results['gt_masks'].rescale(\n                    results['scale'])\n            else:\n                results['gt_masks'] = results['gt_masks'].resize(\n                    results['img_shape'])\n\n    def _resize_bboxes(self, results: dict) -> None:\n        \"\"\"Resize bounding boxes with ``results['scale_factor']``.\"\"\"\n        if results.get('gt_bboxes', None) is not None:\n            results['gt_bboxes'].rescale_(results['scale_factor'])\n            if self.clip_object_border:\n                results['gt_bboxes'].clip_(results['img_shape'])\n\n    def _record_homography_matrix(self, results: dict) -> None:\n        \"\"\"Record the homography matrix for the Resize.\"\"\"\n        w_scale, h_scale = results['scale_factor']\n        homography_matrix = np.array(\n            [[w_scale, 0, 0], [0, h_scale, 0], [0, 0, 1]], dtype=np.float32)\n        if results.get('homography_matrix', None) is None:\n            results['homography_matrix'] = homography_matrix\n        else:\n            results['homography_matrix'] = homography_matrix @ results[\n                'homography_matrix']\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to resize images, bounding boxes and semantic\n        segmentation map.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n        Returns:\n            dict: Resized results, 'img', 'gt_bboxes', 'gt_seg_map',\n            'scale', 'scale_factor', 'height', 'width', and 'keep_ratio' keys\n            are updated in result dict.\n        \"\"\"\n        if self.scale:\n            results['scale'] = self.scale\n        else:\n            img_shape = results['img'].shape[:2]\n            results['scale'] = _scale_size(img_shape[::-1], self.scale_factor)\n        self._resize_img(results)\n        self._resize_bboxes(results)\n        self._resize_masks(results)\n        self._resize_seg(results)\n        self._record_homography_matrix(results)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(scale={self.scale}, '\n        repr_str += f'scale_factor={self.scale_factor}, '\n        repr_str += f'keep_ratio={self.keep_ratio}, '\n        repr_str += f'clip_object_border={self.clip_object_border}), '\n        repr_str += f'backend={self.backend}), '\n        repr_str += f'interpolation={self.interpolation})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass FixScaleResize(Resize):\n    \"\"\"Compared to Resize, FixScaleResize fixes the scaling issue when\n    `keep_ratio=true`.\"\"\"\n\n    def _resize_img(self, results):\n        \"\"\"Resize images with ``results['scale']``.\"\"\"\n        if results.get('img', None) is not None:\n            if self.keep_ratio:\n                img, scale_factor = imrescale(\n                    results['img'],\n                    results['scale'],\n                    interpolation=self.interpolation,\n                    return_scale=True,\n                    backend=self.backend)\n                new_h, new_w = img.shape[:2]\n                h, w = results['img'].shape[:2]\n                w_scale = new_w / w\n                h_scale = new_h / h\n            else:\n                img, w_scale, h_scale = mmcv.imresize(\n                    results['img'],\n                    results['scale'],\n                    interpolation=self.interpolation,\n                    return_scale=True,\n                    backend=self.backend)\n            results['img'] = img\n            results['img_shape'] = img.shape[:2]\n            results['scale_factor'] = (w_scale, h_scale)\n            results['keep_ratio'] = self.keep_ratio\n\n\n@TRANSFORMS.register_module()\nclass ResizeShortestEdge(BaseTransform):\n    \"\"\"Resize the image and mask while keeping the aspect ratio unchanged.\n\n    Modified from https://github.com/facebookresearch/detectron2/blob/main/detectron2/data/transforms/augmentation_impl.py#L130 # noqa:E501\n\n    This transform attempts to scale the shorter edge to the given\n    `scale`, as long as the longer edge does not exceed `max_size`.\n    If `max_size` is reached, then downscale so that the longer\n    edge does not exceed `max_size`.\n\n    Required Keys:\n        - img\n        - gt_seg_map (optional)\n    Modified Keys:\n        - img\n        - img_shape\n        - gt_seg_map (optional))\n    Added Keys:\n        - scale\n        - scale_factor\n        - keep_ratio\n\n    Args:\n        scale (Union[int, Tuple[int, int]]): The target short edge length.\n            If it's tuple, will select the min value as the short edge length.\n        max_size (int): The maximum allowed longest edge length.\n    \"\"\"\n\n    def __init__(self,\n                 scale: Union[int, Tuple[int, int]],\n                 max_size: Optional[int] = None,\n                 resize_type: str = 'Resize',\n                 **resize_kwargs) -> None:\n        super().__init__()\n        self.scale = scale\n        self.max_size = max_size\n\n        self.resize_cfg = dict(type=resize_type, **resize_kwargs)\n        self.resize = TRANSFORMS.build({'scale': 0, **self.resize_cfg})\n\n    def _get_output_shape(\n            self, img: np.ndarray,\n            short_edge_length: Union[int, Tuple[int, int]]) -> Tuple[int, int]:\n        \"\"\"Compute the target image shape with the given `short_edge_length`.\n\n        Args:\n            img (np.ndarray): The input image.\n            short_edge_length (Union[int, Tuple[int, int]]): The target short\n                edge length. If it's tuple, will select the min value as the\n                short edge length.\n        \"\"\"\n        h, w = img.shape[:2]\n        if isinstance(short_edge_length, int):\n            size = short_edge_length * 1.0\n        elif isinstance(short_edge_length, tuple):\n            size = min(short_edge_length) * 1.0\n        scale = size / min(h, w)\n        if h < w:\n            new_h, new_w = size, scale * w\n        else:\n            new_h, new_w = scale * h, size\n\n        if self.max_size and max(new_h, new_w) > self.max_size:\n            scale = self.max_size * 1.0 / max(new_h, new_w)\n            new_h *= scale\n            new_w *= scale\n\n        new_h = int(new_h + 0.5)\n        new_w = int(new_w + 0.5)\n        return new_w, new_h\n\n    def transform(self, results: dict) -> dict:\n        self.resize.scale = self._get_output_shape(results['img'], self.scale)\n        return self.resize(results)\n\n\n@TRANSFORMS.register_module()\nclass FixShapeResize(Resize):\n    \"\"\"Resize images & bbox & seg to the specified size.\n\n    This transform resizes the input image according to ``width`` and\n    ``height``. Bboxes, masks, and seg map are then resized\n    with the same parameters.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n\n    Added Keys:\n\n    - scale\n    - scale_factor\n    - keep_ratio\n    - homography_matrix\n\n    Args:\n        width (int): width for resizing.\n        height (int): height for resizing.\n            Defaults to None.\n        pad_val (Number | dict[str, Number], optional): Padding value for if\n            the pad_mode is \"constant\".  If it is a single number, the value\n            to pad the image is the number and to pad the semantic\n            segmentation map is 255. If it is a dict, it should have the\n            following keys:\n\n            - img: The value to pad the image.\n            - seg: The value to pad the semantic segmentation map.\n            Defaults to dict(img=0, seg=255).\n        keep_ratio (bool): Whether to keep the aspect ratio when resizing the\n            image. Defaults to False.\n        clip_object_border (bool): Whether to clip the objects\n            outside the border of the image. In some dataset like MOT17, the gt\n            bboxes are allowed to cross the border of images. Therefore, we\n            don't need to clip the gt bboxes in these cases. Defaults to True.\n        backend (str): Image resize backend, choices are 'cv2' and 'pillow'.\n            These two backends generates slightly different results. Defaults\n            to 'cv2'.\n        interpolation (str): Interpolation method, accepted values are\n            \"nearest\", \"bilinear\", \"bicubic\", \"area\", \"lanczos\" for 'cv2'\n            backend, \"nearest\", \"bilinear\" for 'pillow' backend. Defaults\n            to 'bilinear'.\n    \"\"\"\n\n    def __init__(self,\n                 width: int,\n                 height: int,\n                 pad_val: Union[Number, dict] = dict(img=0, seg=255),\n                 keep_ratio: bool = False,\n                 clip_object_border: bool = True,\n                 backend: str = 'cv2',\n                 interpolation: str = 'bilinear') -> None:\n        assert width is not None and height is not None, (\n            '`width` and'\n            '`height` can not be `None`')\n\n        self.width = width\n        self.height = height\n        self.scale = (width, height)\n\n        self.backend = backend\n        self.interpolation = interpolation\n        self.keep_ratio = keep_ratio\n        self.clip_object_border = clip_object_border\n\n        if keep_ratio is True:\n            # padding to the fixed size when keep_ratio=True\n            self.pad_transform = Pad(size=self.scale, pad_val=pad_val)\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to resize images, bounding boxes and semantic\n        segmentation map.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n        Returns:\n            dict: Resized results, 'img', 'gt_bboxes', 'gt_seg_map',\n            'scale', 'scale_factor', 'height', 'width', and 'keep_ratio' keys\n            are updated in result dict.\n        \"\"\"\n        img = results['img']\n        h, w = img.shape[:2]\n        if self.keep_ratio:\n            scale_factor = min(self.width / w, self.height / h)\n            results['scale_factor'] = (scale_factor, scale_factor)\n            real_w, real_h = int(w * float(scale_factor) +\n                                 0.5), int(h * float(scale_factor) + 0.5)\n            img, scale_factor = mmcv.imrescale(\n                results['img'], (real_w, real_h),\n                interpolation=self.interpolation,\n                return_scale=True,\n                backend=self.backend)\n            # the w_scale and h_scale has minor difference\n            # a real fix should be done in the mmcv.imrescale in the future\n            results['img'] = img\n            results['img_shape'] = img.shape[:2]\n            results['keep_ratio'] = self.keep_ratio\n            results['scale'] = (real_w, real_h)\n        else:\n            results['scale'] = (self.width, self.height)\n            results['scale_factor'] = (self.width / w, self.height / h)\n            super()._resize_img(results)\n\n        self._resize_bboxes(results)\n        self._resize_masks(results)\n        self._resize_seg(results)\n        self._record_homography_matrix(results)\n        if self.keep_ratio:\n            self.pad_transform(results)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(width={self.width}, height={self.height}, '\n        repr_str += f'keep_ratio={self.keep_ratio}, '\n        repr_str += f'clip_object_border={self.clip_object_border}), '\n        repr_str += f'backend={self.backend}), '\n        repr_str += f'interpolation={self.interpolation})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass RandomFlip(MMCV_RandomFlip):\n    \"\"\"Flip the image & bbox & mask & segmentation map. Added or Updated keys:\n    flip, flip_direction, img, gt_bboxes, and gt_seg_map. There are 3 flip\n    modes:\n\n     - ``prob`` is float, ``direction`` is string: the image will be\n         ``direction``ly flipped with probability of ``prob`` .\n         E.g., ``prob=0.5``, ``direction='horizontal'``,\n         then image will be horizontally flipped with probability of 0.5.\n     - ``prob`` is float, ``direction`` is list of string: the image will\n         be ``direction[i]``ly flipped with probability of\n         ``prob/len(direction)``.\n         E.g., ``prob=0.5``, ``direction=['horizontal', 'vertical']``,\n         then image will be horizontally flipped with probability of 0.25,\n         vertically with probability of 0.25.\n     - ``prob`` is list of float, ``direction`` is list of string:\n         given ``len(prob) == len(direction)``, the image will\n         be ``direction[i]``ly flipped with probability of ``prob[i]``.\n         E.g., ``prob=[0.3, 0.5]``, ``direction=['horizontal',\n         'vertical']``, then image will be horizontally flipped with\n         probability of 0.3, vertically with probability of 0.5.\n\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - flip\n    - flip_direction\n    - homography_matrix\n\n\n    Args:\n         prob (float | list[float], optional): The flipping probability.\n             Defaults to None.\n         direction(str | list[str]): The flipping direction. Options\n             If input is a list, the length must equal ``prob``. Each\n             element in ``prob`` indicates the flip probability of\n             corresponding direction. Defaults to 'horizontal'.\n    \"\"\"\n\n    def _record_homography_matrix(self, results: dict) -> None:\n        \"\"\"Record the homography matrix for the RandomFlip.\"\"\"\n        cur_dir = results['flip_direction']\n        h, w = results['img'].shape[:2]\n\n        if cur_dir == 'horizontal':\n            homography_matrix = np.array([[-1, 0, w], [0, 1, 0], [0, 0, 1]],\n                                         dtype=np.float32)\n        elif cur_dir == 'vertical':\n            homography_matrix = np.array([[1, 0, 0], [0, -1, h], [0, 0, 1]],\n                                         dtype=np.float32)\n        elif cur_dir == 'diagonal':\n            homography_matrix = np.array([[-1, 0, w], [0, -1, h], [0, 0, 1]],\n                                         dtype=np.float32)\n        else:\n            homography_matrix = np.eye(3, dtype=np.float32)\n\n        if results.get('homography_matrix', None) is None:\n            results['homography_matrix'] = homography_matrix\n        else:\n            results['homography_matrix'] = homography_matrix @ results[\n                'homography_matrix']\n\n    @autocast_box_type()\n    def _flip(self, results: dict) -> None:\n        \"\"\"Flip images, bounding boxes, and semantic segmentation map.\"\"\"\n        # flip image\n        results['img'] = mmcv.imflip(\n            results['img'], direction=results['flip_direction'])\n\n        img_shape = results['img'].shape[:2]\n\n        # flip bboxes\n        if results.get('gt_bboxes', None) is not None:\n            results['gt_bboxes'].flip_(img_shape, results['flip_direction'])\n\n        # flip masks\n        if results.get('gt_masks', None) is not None:\n            results['gt_masks'] = results['gt_masks'].flip(\n                results['flip_direction'])\n\n        # flip segs\n        if results.get('gt_seg_map', None) is not None:\n            results['gt_seg_map'] = mmcv.imflip(\n                results['gt_seg_map'], direction=results['flip_direction'])\n\n        # record homography matrix for flip\n        self._record_homography_matrix(results)\n\n\n@TRANSFORMS.register_module()\nclass RandomShift(BaseTransform):\n    \"\"\"Shift the image and box given shift pixels and probability.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32])\n    - gt_bboxes_labels (np.int64)\n    - gt_ignore_flags (bool) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes\n    - gt_bboxes_labels\n    - gt_ignore_flags (bool) (optional)\n\n    Args:\n        prob (float): Probability of shifts. Defaults to 0.5.\n        max_shift_px (int): The max pixels for shifting. Defaults to 32.\n        filter_thr_px (int): The width and height threshold for filtering.\n            The bbox and the rest of the targets below the width and\n            height threshold will be filtered. Defaults to 1.\n    \"\"\"\n\n    def __init__(self,\n                 prob: float = 0.5,\n                 max_shift_px: int = 32,\n                 filter_thr_px: int = 1) -> None:\n        assert 0 <= prob <= 1\n        assert max_shift_px >= 0\n        self.prob = prob\n        self.max_shift_px = max_shift_px\n        self.filter_thr_px = int(filter_thr_px)\n\n    @cache_randomness\n    def _random_prob(self) -> float:\n        return random.uniform(0, 1)\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to random shift images, bounding boxes.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Shift results.\n        \"\"\"\n        if self._random_prob() < self.prob:\n            img_shape = results['img'].shape[:2]\n\n            random_shift_x = random.randint(-self.max_shift_px,\n                                            self.max_shift_px)\n            random_shift_y = random.randint(-self.max_shift_px,\n                                            self.max_shift_px)\n            new_x = max(0, random_shift_x)\n            ori_x = max(0, -random_shift_x)\n            new_y = max(0, random_shift_y)\n            ori_y = max(0, -random_shift_y)\n\n            # TODO: support mask and semantic segmentation maps.\n            bboxes = results['gt_bboxes'].clone()\n            bboxes.translate_([random_shift_x, random_shift_y])\n\n            # clip border\n            bboxes.clip_(img_shape)\n\n            # remove invalid bboxes\n            valid_inds = (bboxes.widths > self.filter_thr_px).numpy() & (\n                bboxes.heights > self.filter_thr_px).numpy()\n            # If the shift does not contain any gt-bbox area, skip this\n            # image.\n            if not valid_inds.any():\n                return results\n            bboxes = bboxes[valid_inds]\n            results['gt_bboxes'] = bboxes\n            results['gt_bboxes_labels'] = results['gt_bboxes_labels'][\n                valid_inds]\n\n            if results.get('gt_ignore_flags', None) is not None:\n                results['gt_ignore_flags'] = \\\n                    results['gt_ignore_flags'][valid_inds]\n\n            # shift img\n            img = results['img']\n            new_img = np.zeros_like(img)\n            img_h, img_w = img.shape[:2]\n            new_h = img_h - np.abs(random_shift_y)\n            new_w = img_w - np.abs(random_shift_x)\n            new_img[new_y:new_y + new_h, new_x:new_x + new_w] \\\n                = img[ori_y:ori_y + new_h, ori_x:ori_x + new_w]\n            results['img'] = new_img\n\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(prob={self.prob}, '\n        repr_str += f'max_shift_px={self.max_shift_px}, '\n        repr_str += f'filter_thr_px={self.filter_thr_px})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass Pad(MMCV_Pad):\n    \"\"\"Pad the image & segmentation map.\n\n    There are three padding modes: (1) pad to a fixed size and (2) pad to the\n    minimum size that is divisible by some number. and (3)pad to square. Also,\n    pad to square and pad to the minimum size can be used as the same time.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_masks\n    - gt_seg_map\n\n    Added Keys:\n\n    - pad_shape\n    - pad_fixed_size\n    - pad_size_divisor\n\n    Args:\n        size (tuple, optional): Fixed padding size.\n            Expected padding shape (width, height). Defaults to None.\n        size_divisor (int, optional): The divisor of padded size. Defaults to\n            None.\n        pad_to_square (bool): Whether to pad the image into a square.\n            Currently only used for YOLOX. Defaults to False.\n        pad_val (Number | dict[str, Number], optional) - Padding value for if\n            the pad_mode is \"constant\".  If it is a single number, the value\n            to pad the image is the number and to pad the semantic\n            segmentation map is 255. If it is a dict, it should have the\n            following keys:\n\n            - img: The value to pad the image.\n            - seg: The value to pad the semantic segmentation map.\n            Defaults to dict(img=0, seg=255).\n        padding_mode (str): Type of padding. Should be: constant, edge,\n            reflect or symmetric. Defaults to 'constant'.\n\n            - constant: pads with a constant value, this value is specified\n              with pad_val.\n            - edge: pads with the last value at the edge of the image.\n            - reflect: pads with reflection of image without repeating the last\n              value on the edge. For example, padding [1, 2, 3, 4] with 2\n              elements on both sides in reflect mode will result in\n              [3, 2, 1, 2, 3, 4, 3, 2].\n            - symmetric: pads with reflection of image repeating the last value\n              on the edge. For example, padding [1, 2, 3, 4] with 2 elements on\n              both sides in symmetric mode will result in\n              [2, 1, 1, 2, 3, 4, 4, 3]\n    \"\"\"\n\n    def _pad_masks(self, results: dict) -> None:\n        \"\"\"Pad masks according to ``results['pad_shape']``.\"\"\"\n        if results.get('gt_masks', None) is not None:\n            pad_val = self.pad_val.get('masks', 0)\n            pad_shape = results['pad_shape'][:2]\n            results['gt_masks'] = results['gt_masks'].pad(\n                pad_shape, pad_val=pad_val)\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Call function to pad images, masks, semantic segmentation maps.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        self._pad_img(results)\n        self._pad_seg(results)\n        self._pad_masks(results)\n        return results\n\n\n@TRANSFORMS.register_module()\nclass RandomCrop(BaseTransform):\n    \"\"\"Random crop the image & bboxes & masks.\n\n    The absolute ``crop_size`` is sampled based on ``crop_type`` and\n    ``image_size``, then the cropped results are generated.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_masks (optional)\n    - gt_ignore_flags (optional)\n    - gt_seg_map (optional)\n    - gt_instances_ids (options, only used in MOT/VIS)\n\n    Added Keys:\n\n    - homography_matrix\n\n    Args:\n        crop_size (tuple): The relative ratio or absolute pixels of\n            (width, height).\n        crop_type (str, optional): One of \"relative_range\", \"relative\",\n            \"absolute\", \"absolute_range\". \"relative\" randomly crops\n            (h * crop_size[0], w * crop_size[1]) part from an input of size\n            (h, w). \"relative_range\" uniformly samples relative crop size from\n            range [crop_size[0], 1] and [crop_size[1], 1] for height and width\n            respectively. \"absolute\" crops from an input with absolute size\n            (crop_size[0], crop_size[1]). \"absolute_range\" uniformly samples\n            crop_h in range [crop_size[0], min(h, crop_size[1])] and crop_w\n            in range [crop_size[0], min(w, crop_size[1])].\n            Defaults to \"absolute\".\n        allow_negative_crop (bool, optional): Whether to allow a crop that does\n            not contain any bbox area. Defaults to False.\n        recompute_bbox (bool, optional): Whether to re-compute the boxes based\n            on cropped instance masks. Defaults to False.\n        bbox_clip_border (bool, optional): Whether clip the objects outside\n            the border of the image. Defaults to True.\n\n    Note:\n        - If the image is smaller than the absolute crop size, return the\n            original image.\n        - The keys for bboxes, labels and masks must be aligned. That is,\n          ``gt_bboxes`` corresponds to ``gt_labels`` and ``gt_masks``, and\n          ``gt_bboxes_ignore`` corresponds to ``gt_labels_ignore`` and\n          ``gt_masks_ignore``.\n        - If the crop does not contain any gt-bbox region and\n          ``allow_negative_crop`` is set to False, skip this image.\n    \"\"\"\n\n    def __init__(self,\n                 crop_size: tuple,\n                 crop_type: str = 'absolute',\n                 allow_negative_crop: bool = False,\n                 recompute_bbox: bool = False,\n                 bbox_clip_border: bool = True) -> None:\n        if crop_type not in [\n                'relative_range', 'relative', 'absolute', 'absolute_range'\n        ]:\n            raise ValueError(f'Invalid crop_type {crop_type}.')\n        if crop_type in ['absolute', 'absolute_range']:\n            assert crop_size[0] > 0 and crop_size[1] > 0\n            assert isinstance(crop_size[0], int) and isinstance(\n                crop_size[1], int)\n            if crop_type == 'absolute_range':\n                assert crop_size[0] <= crop_size[1]\n        else:\n            assert 0 < crop_size[0] <= 1 and 0 < crop_size[1] <= 1\n        self.crop_size = crop_size\n        self.crop_type = crop_type\n        self.allow_negative_crop = allow_negative_crop\n        self.bbox_clip_border = bbox_clip_border\n        self.recompute_bbox = recompute_bbox\n\n    def _crop_data(self, results: dict, crop_size: Tuple[int, int],\n                   allow_negative_crop: bool) -> Union[dict, None]:\n        \"\"\"Function to randomly crop images, bounding boxes, masks, semantic\n        segmentation maps.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n            crop_size (Tuple[int, int]): Expected absolute size after\n                cropping, (h, w).\n            allow_negative_crop (bool): Whether to allow a crop that does not\n                contain any bbox area.\n\n        Returns:\n            results (Union[dict, None]): Randomly cropped results, 'img_shape'\n                key in result dict is updated according to crop size. None will\n                be returned when there is no valid bbox after cropping.\n        \"\"\"\n        assert crop_size[0] > 0 and crop_size[1] > 0\n        img = results['img']\n        margin_h = max(img.shape[0] - crop_size[0], 0)\n        margin_w = max(img.shape[1] - crop_size[1], 0)\n        offset_h, offset_w = self._rand_offset((margin_h, margin_w))\n        crop_y1, crop_y2 = offset_h, offset_h + crop_size[0]\n        crop_x1, crop_x2 = offset_w, offset_w + crop_size[1]\n\n        # Record the homography matrix for the RandomCrop\n        homography_matrix = np.array(\n            [[1, 0, -offset_w], [0, 1, -offset_h], [0, 0, 1]],\n            dtype=np.float32)\n        if results.get('homography_matrix', None) is None:\n            results['homography_matrix'] = homography_matrix\n        else:\n            results['homography_matrix'] = homography_matrix @ results[\n                'homography_matrix']\n\n        # crop the image\n        img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...]\n        img_shape = img.shape\n        results['img'] = img\n        results['img_shape'] = img_shape[:2]\n\n        # crop bboxes accordingly and clip to the image boundary\n        if results.get('gt_bboxes', None) is not None:\n            bboxes = results['gt_bboxes']\n            bboxes.translate_([-offset_w, -offset_h])\n            if self.bbox_clip_border:\n                bboxes.clip_(img_shape[:2])\n            valid_inds = bboxes.is_inside(img_shape[:2]).numpy()\n            # If the crop does not contain any gt-bbox area and\n            # allow_negative_crop is False, skip this image.\n            if (not valid_inds.any() and not allow_negative_crop):\n                return None\n\n            results['gt_bboxes'] = bboxes[valid_inds]\n\n            if results.get('gt_ignore_flags', None) is not None:\n                results['gt_ignore_flags'] = \\\n                    results['gt_ignore_flags'][valid_inds]\n\n            if results.get('gt_bboxes_labels', None) is not None:\n                results['gt_bboxes_labels'] = \\\n                    results['gt_bboxes_labels'][valid_inds]\n\n            if results.get('gt_masks', None) is not None:\n                results['gt_masks'] = results['gt_masks'][\n                    valid_inds.nonzero()[0]].crop(\n                        np.asarray([crop_x1, crop_y1, crop_x2, crop_y2]))\n                if self.recompute_bbox:\n                    results['gt_bboxes'] = results['gt_masks'].get_bboxes(\n                        type(results['gt_bboxes']))\n\n            # We should remove the instance ids corresponding to invalid boxes.\n            if results.get('gt_instances_ids', None) is not None:\n                results['gt_instances_ids'] = \\\n                    results['gt_instances_ids'][valid_inds]\n\n        # crop semantic seg\n        if results.get('gt_seg_map', None) is not None:\n            results['gt_seg_map'] = results['gt_seg_map'][crop_y1:crop_y2,\n                                                          crop_x1:crop_x2]\n\n        return results\n\n    @cache_randomness\n    def _rand_offset(self, margin: Tuple[int, int]) -> Tuple[int, int]:\n        \"\"\"Randomly generate crop offset.\n\n        Args:\n            margin (Tuple[int, int]): The upper bound for the offset generated\n                randomly.\n\n        Returns:\n            Tuple[int, int]: The random offset for the crop.\n        \"\"\"\n        margin_h, margin_w = margin\n        offset_h = np.random.randint(0, margin_h + 1)\n        offset_w = np.random.randint(0, margin_w + 1)\n\n        return offset_h, offset_w\n\n    @cache_randomness\n    def _get_crop_size(self, image_size: Tuple[int, int]) -> Tuple[int, int]:\n        \"\"\"Randomly generates the absolute crop size based on `crop_type` and\n        `image_size`.\n\n        Args:\n            image_size (Tuple[int, int]): (h, w).\n\n        Returns:\n            crop_size (Tuple[int, int]): (crop_h, crop_w) in absolute pixels.\n        \"\"\"\n        h, w = image_size\n        if self.crop_type == 'absolute':\n            return min(self.crop_size[1], h), min(self.crop_size[0], w)\n        elif self.crop_type == 'absolute_range':\n            crop_h = np.random.randint(\n                min(h, self.crop_size[0]),\n                min(h, self.crop_size[1]) + 1)\n            crop_w = np.random.randint(\n                min(w, self.crop_size[0]),\n                min(w, self.crop_size[1]) + 1)\n            return crop_h, crop_w\n        elif self.crop_type == 'relative':\n            crop_w, crop_h = self.crop_size\n            return int(h * crop_h + 0.5), int(w * crop_w + 0.5)\n        else:\n            # 'relative_range'\n            crop_size = np.asarray(self.crop_size, dtype=np.float32)\n            crop_h, crop_w = crop_size + np.random.rand(2) * (1 - crop_size)\n            return int(h * crop_h + 0.5), int(w * crop_w + 0.5)\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> Union[dict, None]:\n        \"\"\"Transform function to randomly crop images, bounding boxes, masks,\n        semantic segmentation maps.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            results (Union[dict, None]): Randomly cropped results, 'img_shape'\n                key in result dict is updated according to crop size. None will\n                be returned when there is no valid bbox after cropping.\n        \"\"\"\n        image_size = results['img'].shape[:2]\n        crop_size = self._get_crop_size(image_size)\n        results = self._crop_data(results, crop_size, self.allow_negative_crop)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(crop_size={self.crop_size}, '\n        repr_str += f'crop_type={self.crop_type}, '\n        repr_str += f'allow_negative_crop={self.allow_negative_crop}, '\n        repr_str += f'recompute_bbox={self.recompute_bbox}, '\n        repr_str += f'bbox_clip_border={self.bbox_clip_border})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass SegRescale(BaseTransform):\n    \"\"\"Rescale semantic segmentation maps.\n\n    This transform rescale the ``gt_seg_map`` according to ``scale_factor``.\n\n    Required Keys:\n\n    - gt_seg_map\n\n    Modified Keys:\n\n    - gt_seg_map\n\n    Args:\n        scale_factor (float): The scale factor of the final output. Defaults\n            to 1.\n        backend (str): Image rescale backend, choices are 'cv2' and 'pillow'.\n            These two backends generates slightly different results. Defaults\n            to 'cv2'.\n    \"\"\"\n\n    def __init__(self, scale_factor: float = 1, backend: str = 'cv2') -> None:\n        self.scale_factor = scale_factor\n        self.backend = backend\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to scale the semantic segmentation map.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Result dict with semantic segmentation map scaled.\n        \"\"\"\n        if self.scale_factor != 1:\n            results['gt_seg_map'] = mmcv.imrescale(\n                results['gt_seg_map'],\n                self.scale_factor,\n                interpolation='nearest',\n                backend=self.backend)\n\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(scale_factor={self.scale_factor}, '\n        repr_str += f'backend={self.backend})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass PhotoMetricDistortion(BaseTransform):\n    \"\"\"Apply photometric distortion to image sequentially, every transformation\n    is applied with a probability of 0.5. The position of random contrast is in\n    second or second to last.\n\n    1. random brightness\n    2. random contrast (mode 0)\n    3. convert color from BGR to HSV\n    4. random saturation\n    5. random hue\n    6. convert color from HSV to BGR\n    7. random contrast (mode 1)\n    8. randomly swap channels\n\n    Required Keys:\n\n    - img (np.uint8)\n\n    Modified Keys:\n\n    - img (np.float32)\n\n    Args:\n        brightness_delta (int): delta of brightness.\n        contrast_range (sequence): range of contrast.\n        saturation_range (sequence): range of saturation.\n        hue_delta (int): delta of hue.\n    \"\"\"\n\n    def __init__(self,\n                 brightness_delta: int = 32,\n                 contrast_range: Sequence[Number] = (0.5, 1.5),\n                 saturation_range: Sequence[Number] = (0.5, 1.5),\n                 hue_delta: int = 18) -> None:\n        self.brightness_delta = brightness_delta\n        self.contrast_lower, self.contrast_upper = contrast_range\n        self.saturation_lower, self.saturation_upper = saturation_range\n        self.hue_delta = hue_delta\n\n    @cache_randomness\n    def _random_flags(self) -> Sequence[Number]:\n        mode = random.randint(2)\n        brightness_flag = random.randint(2)\n        contrast_flag = random.randint(2)\n        saturation_flag = random.randint(2)\n        hue_flag = random.randint(2)\n        swap_flag = random.randint(2)\n        delta_value = random.uniform(-self.brightness_delta,\n                                     self.brightness_delta)\n        alpha_value = random.uniform(self.contrast_lower, self.contrast_upper)\n        saturation_value = random.uniform(self.saturation_lower,\n                                          self.saturation_upper)\n        hue_value = random.uniform(-self.hue_delta, self.hue_delta)\n        swap_value = random.permutation(3)\n\n        return (mode, brightness_flag, contrast_flag, saturation_flag,\n                hue_flag, swap_flag, delta_value, alpha_value,\n                saturation_value, hue_value, swap_value)\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to perform photometric distortion on images.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Result dict with images distorted.\n        \"\"\"\n        assert 'img' in results, '`img` is not found in results'\n        img = results['img']\n        img = img.astype(np.float32)\n\n        (mode, brightness_flag, contrast_flag, saturation_flag, hue_flag,\n         swap_flag, delta_value, alpha_value, saturation_value, hue_value,\n         swap_value) = self._random_flags()\n\n        # random brightness\n        if brightness_flag:\n            img += delta_value\n\n        # mode == 0 --> do random contrast first\n        # mode == 1 --> do random contrast last\n        if mode == 1:\n            if contrast_flag:\n                img *= alpha_value\n\n        # convert color from BGR to HSV\n        img = mmcv.bgr2hsv(img)\n\n        # random saturation\n        if saturation_flag:\n            img[..., 1] *= saturation_value\n            # For image(type=float32), after convert bgr to hsv by opencv,\n            # valid saturation value range is [0, 1]\n            if saturation_value > 1:\n                img[..., 1] = img[..., 1].clip(0, 1)\n\n        # random hue\n        if hue_flag:\n            img[..., 0] += hue_value\n            img[..., 0][img[..., 0] > 360] -= 360\n            img[..., 0][img[..., 0] < 0] += 360\n\n        # convert color from HSV to BGR\n        img = mmcv.hsv2bgr(img)\n\n        # random contrast\n        if mode == 0:\n            if contrast_flag:\n                img *= alpha_value\n\n        # randomly swap channels\n        if swap_flag:\n            img = img[..., swap_value]\n\n        results['img'] = img\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(brightness_delta={self.brightness_delta}, '\n        repr_str += 'contrast_range='\n        repr_str += f'{(self.contrast_lower, self.contrast_upper)}, '\n        repr_str += 'saturation_range='\n        repr_str += f'{(self.saturation_lower, self.saturation_upper)}, '\n        repr_str += f'hue_delta={self.hue_delta})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass Expand(BaseTransform):\n    \"\"\"Random expand the image & bboxes & masks & segmentation map.\n\n    Randomly place the original image on a canvas of ``ratio`` x original image\n    size filled with mean values. The ratio is in the range of ratio_range.\n\n    Required Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes\n    - gt_masks\n    - gt_seg_map\n\n\n    Args:\n        mean (sequence): mean value of dataset.\n        to_rgb (bool): if need to convert the order of mean to align with RGB.\n        ratio_range (sequence)): range of expand ratio.\n        seg_ignore_label (int): label of ignore segmentation map.\n        prob (float): probability of applying this transformation\n    \"\"\"\n\n    def __init__(self,\n                 mean: Sequence[Number] = (0, 0, 0),\n                 to_rgb: bool = True,\n                 ratio_range: Sequence[Number] = (1, 4),\n                 seg_ignore_label: int = None,\n                 prob: float = 0.5) -> None:\n        self.to_rgb = to_rgb\n        self.ratio_range = ratio_range\n        if to_rgb:\n            self.mean = mean[::-1]\n        else:\n            self.mean = mean\n        self.min_ratio, self.max_ratio = ratio_range\n        self.seg_ignore_label = seg_ignore_label\n        self.prob = prob\n\n    @cache_randomness\n    def _random_prob(self) -> float:\n        return random.uniform(0, 1)\n\n    @cache_randomness\n    def _random_ratio(self) -> float:\n        return random.uniform(self.min_ratio, self.max_ratio)\n\n    @cache_randomness\n    def _random_left_top(self, ratio: float, h: int,\n                         w: int) -> Tuple[int, int]:\n        left = int(random.uniform(0, w * ratio - w))\n        top = int(random.uniform(0, h * ratio - h))\n        return left, top\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to expand images, bounding boxes, masks,\n        segmentation map.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Result dict with images, bounding boxes, masks, segmentation\n                map expanded.\n        \"\"\"\n        if self._random_prob() > self.prob:\n            return results\n        assert 'img' in results, '`img` is not found in results'\n        img = results['img']\n        h, w, c = img.shape\n        ratio = self._random_ratio()\n        # speedup expand when meets large image\n        if np.all(self.mean == self.mean[0]):\n            expand_img = np.empty((int(h * ratio), int(w * ratio), c),\n                                  img.dtype)\n            expand_img.fill(self.mean[0])\n        else:\n            expand_img = np.full((int(h * ratio), int(w * ratio), c),\n                                 self.mean,\n                                 dtype=img.dtype)\n        left, top = self._random_left_top(ratio, h, w)\n        expand_img[top:top + h, left:left + w] = img\n        results['img'] = expand_img\n        results['img_shape'] = expand_img.shape[:2]\n\n        # expand bboxes\n        if results.get('gt_bboxes', None) is not None:\n            results['gt_bboxes'].translate_([left, top])\n\n        # expand masks\n        if results.get('gt_masks', None) is not None:\n            results['gt_masks'] = results['gt_masks'].expand(\n                int(h * ratio), int(w * ratio), top, left)\n\n        # expand segmentation map\n        if results.get('gt_seg_map', None) is not None:\n            gt_seg = results['gt_seg_map']\n            expand_gt_seg = np.full((int(h * ratio), int(w * ratio)),\n                                    self.seg_ignore_label,\n                                    dtype=gt_seg.dtype)\n            expand_gt_seg[top:top + h, left:left + w] = gt_seg\n            results['gt_seg_map'] = expand_gt_seg\n\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(mean={self.mean}, to_rgb={self.to_rgb}, '\n        repr_str += f'ratio_range={self.ratio_range}, '\n        repr_str += f'seg_ignore_label={self.seg_ignore_label}, '\n        repr_str += f'prob={self.prob})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass MinIoURandomCrop(BaseTransform):\n    \"\"\"Random crop the image & bboxes & masks & segmentation map, the cropped\n    patches have minimum IoU requirement with original image & bboxes & masks.\n\n    & segmentation map, the IoU threshold is randomly selected from min_ious.\n\n\n    Required Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - gt_seg_map (np.uint8) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes\n    - gt_bboxes_labels\n    - gt_masks\n    - gt_ignore_flags\n    - gt_seg_map\n\n\n    Args:\n        min_ious (Sequence[float]): minimum IoU threshold for all intersections\n            with bounding boxes.\n        min_crop_size (float): minimum crop's size (i.e. h,w := a*h, a*w,\n        where a >= min_crop_size).\n        bbox_clip_border (bool, optional): Whether clip the objects outside\n            the border of the image. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 min_ious: Sequence[float] = (0.1, 0.3, 0.5, 0.7, 0.9),\n                 min_crop_size: float = 0.3,\n                 bbox_clip_border: bool = True) -> None:\n\n        self.min_ious = min_ious\n        self.sample_mode = (1, *min_ious, 0)\n        self.min_crop_size = min_crop_size\n        self.bbox_clip_border = bbox_clip_border\n\n    @cache_randomness\n    def _random_mode(self) -> Number:\n        return random.choice(self.sample_mode)\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to crop images and bounding boxes with minimum\n        IoU constraint.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Result dict with images and bounding boxes cropped, \\\n                'img_shape' key is updated.\n        \"\"\"\n        assert 'img' in results, '`img` is not found in results'\n        assert 'gt_bboxes' in results, '`gt_bboxes` is not found in results'\n        img = results['img']\n        boxes = results['gt_bboxes']\n        h, w, c = img.shape\n        while True:\n            mode = self._random_mode()\n            self.mode = mode\n            if mode == 1:\n                return results\n\n            min_iou = self.mode\n            for i in range(50):\n                new_w = random.uniform(self.min_crop_size * w, w)\n                new_h = random.uniform(self.min_crop_size * h, h)\n\n                # h / w in [0.5, 2]\n                if new_h / new_w < 0.5 or new_h / new_w > 2:\n                    continue\n\n                left = random.uniform(w - new_w)\n                top = random.uniform(h - new_h)\n\n                patch = np.array(\n                    (int(left), int(top), int(left + new_w), int(top + new_h)))\n                # Line or point crop is not allowed\n                if patch[2] == patch[0] or patch[3] == patch[1]:\n                    continue\n                overlaps = boxes.overlaps(\n                    HorizontalBoxes(patch.reshape(-1, 4).astype(np.float32)),\n                    boxes).numpy().reshape(-1)\n                if len(overlaps) > 0 and overlaps.min() < min_iou:\n                    continue\n\n                # center of boxes should inside the crop img\n                # only adjust boxes and instance masks when the gt is not empty\n                if len(overlaps) > 0:\n                    # adjust boxes\n                    def is_center_of_bboxes_in_patch(boxes, patch):\n                        centers = boxes.centers.numpy()\n                        mask = ((centers[:, 0] > patch[0]) *\n                                (centers[:, 1] > patch[1]) *\n                                (centers[:, 0] < patch[2]) *\n                                (centers[:, 1] < patch[3]))\n                        return mask\n\n                    mask = is_center_of_bboxes_in_patch(boxes, patch)\n                    if not mask.any():\n                        continue\n                    if results.get('gt_bboxes', None) is not None:\n                        boxes = results['gt_bboxes']\n                        mask = is_center_of_bboxes_in_patch(boxes, patch)\n                        boxes = boxes[mask]\n                        boxes.translate_([-patch[0], -patch[1]])\n                        if self.bbox_clip_border:\n                            boxes.clip_(\n                                [patch[3] - patch[1], patch[2] - patch[0]])\n                        results['gt_bboxes'] = boxes\n\n                        # ignore_flags\n                        if results.get('gt_ignore_flags', None) is not None:\n                            results['gt_ignore_flags'] = \\\n                                results['gt_ignore_flags'][mask]\n\n                        # labels\n                        if results.get('gt_bboxes_labels', None) is not None:\n                            results['gt_bboxes_labels'] = results[\n                                'gt_bboxes_labels'][mask]\n\n                        # mask fields\n                        if results.get('gt_masks', None) is not None:\n                            results['gt_masks'] = results['gt_masks'][\n                                mask.nonzero()[0]].crop(patch)\n                # adjust the img no matter whether the gt is empty before crop\n                img = img[patch[1]:patch[3], patch[0]:patch[2]]\n                results['img'] = img\n                results['img_shape'] = img.shape[:2]\n\n                # seg fields\n                if results.get('gt_seg_map', None) is not None:\n                    results['gt_seg_map'] = results['gt_seg_map'][\n                        patch[1]:patch[3], patch[0]:patch[2]]\n                return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(min_ious={self.min_ious}, '\n        repr_str += f'min_crop_size={self.min_crop_size}, '\n        repr_str += f'bbox_clip_border={self.bbox_clip_border})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass Corrupt(BaseTransform):\n    \"\"\"Corruption augmentation.\n\n    Corruption transforms implemented based on\n    `imagecorruptions <https://github.com/bethgelab/imagecorruptions>`_.\n\n    Required Keys:\n\n    - img (np.uint8)\n\n\n    Modified Keys:\n\n    - img (np.uint8)\n\n\n    Args:\n        corruption (str): Corruption name.\n        severity (int): The severity of corruption. Defaults to 1.\n    \"\"\"\n\n    def __init__(self, corruption: str, severity: int = 1) -> None:\n        self.corruption = corruption\n        self.severity = severity\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Call function to corrupt image.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Result dict with images corrupted.\n        \"\"\"\n\n        if corrupt is None:\n            raise RuntimeError('imagecorruptions is not installed')\n        results['img'] = corrupt(\n            results['img'].astype(np.uint8),\n            corruption_name=self.corruption,\n            severity=self.severity)\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(corruption={self.corruption}, '\n        repr_str += f'severity={self.severity})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\n@avoid_cache_randomness\nclass Albu(BaseTransform):\n    \"\"\"Albumentation augmentation.\n\n    Adds custom transformations from Albumentations library.\n    Please, visit `https://albumentations.readthedocs.io`\n    to get more information.\n\n    Required Keys:\n\n    - img (np.uint8)\n    - gt_bboxes (HorizontalBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n\n    Modified Keys:\n\n    - img (np.uint8)\n    - gt_bboxes (HorizontalBoxes[torch.float32]) (optional)\n    - gt_masks (BitmapMasks | PolygonMasks) (optional)\n    - img_shape (tuple)\n\n    An example of ``transforms`` is as followed:\n\n    .. code-block::\n\n        [\n            dict(\n                type='ShiftScaleRotate',\n                shift_limit=0.0625,\n                scale_limit=0.0,\n                rotate_limit=0,\n                interpolation=1,\n                p=0.5),\n            dict(\n                type='RandomBrightnessContrast',\n                brightness_limit=[0.1, 0.3],\n                contrast_limit=[0.1, 0.3],\n                p=0.2),\n            dict(type='ChannelShuffle', p=0.1),\n            dict(\n                type='OneOf',\n                transforms=[\n                    dict(type='Blur', blur_limit=3, p=1.0),\n                    dict(type='MedianBlur', blur_limit=3, p=1.0)\n                ],\n                p=0.1),\n        ]\n\n    Args:\n        transforms (list[dict]): A list of albu transformations\n        bbox_params (dict, optional): Bbox_params for albumentation `Compose`\n        keymap (dict, optional): Contains\n            {'input key':'albumentation-style key'}\n        skip_img_without_anno (bool): Whether to skip the image if no ann left\n            after aug. Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 transforms: List[dict],\n                 bbox_params: Optional[dict] = None,\n                 keymap: Optional[dict] = None,\n                 skip_img_without_anno: bool = False) -> None:\n        if Compose is None:\n            raise RuntimeError('albumentations is not installed')\n\n        # Args will be modified later, copying it will be safer\n        transforms = copy.deepcopy(transforms)\n        if bbox_params is not None:\n            bbox_params = copy.deepcopy(bbox_params)\n        if keymap is not None:\n            keymap = copy.deepcopy(keymap)\n        self.transforms = transforms\n        self.filter_lost_elements = False\n        self.skip_img_without_anno = skip_img_without_anno\n\n        # A simple workaround to remove masks without boxes\n        if (isinstance(bbox_params, dict) and 'label_fields' in bbox_params\n                and 'filter_lost_elements' in bbox_params):\n            self.filter_lost_elements = True\n            self.origin_label_fields = bbox_params['label_fields']\n            bbox_params['label_fields'] = ['idx_mapper']\n            del bbox_params['filter_lost_elements']\n\n        self.bbox_params = (\n            self.albu_builder(bbox_params) if bbox_params else None)\n        self.aug = Compose([self.albu_builder(t) for t in self.transforms],\n                           bbox_params=self.bbox_params)\n\n        if not keymap:\n            self.keymap_to_albu = {\n                'img': 'image',\n                'gt_masks': 'masks',\n                'gt_bboxes': 'bboxes'\n            }\n        else:\n            self.keymap_to_albu = keymap\n        self.keymap_back = {v: k for k, v in self.keymap_to_albu.items()}\n\n    def albu_builder(self, cfg: dict) -> albumentations:\n        \"\"\"Import a module from albumentations.\n\n        It inherits some of :func:`build_from_cfg` logic.\n\n        Args:\n            cfg (dict): Config dict. It should at least contain the key \"type\".\n\n        Returns:\n            obj: The constructed object.\n        \"\"\"\n\n        assert isinstance(cfg, dict) and 'type' in cfg\n        args = cfg.copy()\n        obj_type = args.pop('type')\n        if is_str(obj_type):\n            if albumentations is None:\n                raise RuntimeError('albumentations is not installed')\n            obj_cls = getattr(albumentations, obj_type)\n        elif inspect.isclass(obj_type):\n            obj_cls = obj_type\n        else:\n            raise TypeError(\n                f'type must be a str or valid type, but got {type(obj_type)}')\n\n        if 'transforms' in args:\n            args['transforms'] = [\n                self.albu_builder(transform)\n                for transform in args['transforms']\n            ]\n\n        return obj_cls(**args)\n\n    @staticmethod\n    def mapper(d: dict, keymap: dict) -> dict:\n        \"\"\"Dictionary mapper. Renames keys according to keymap provided.\n\n        Args:\n            d (dict): old dict\n            keymap (dict): {'old_key':'new_key'}\n        Returns:\n            dict: new dict.\n        \"\"\"\n        updated_dict = {}\n        for k, v in zip(d.keys(), d.values()):\n            new_k = keymap.get(k, k)\n            updated_dict[new_k] = d[k]\n        return updated_dict\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> Union[dict, None]:\n        \"\"\"Transform function of Albu.\"\"\"\n        # TODO: gt_seg_map is not currently supported\n        # dict to albumentations format\n        results = self.mapper(results, self.keymap_to_albu)\n        results, ori_masks = self._preprocess_results(results)\n        results = self.aug(**results)\n        results = self._postprocess_results(results, ori_masks)\n        if results is None:\n            return None\n        # back to the original format\n        results = self.mapper(results, self.keymap_back)\n        results['img_shape'] = results['img'].shape[:2]\n        return results\n\n    def _preprocess_results(self, results: dict) -> tuple:\n        \"\"\"Pre-processing results to facilitate the use of Albu.\"\"\"\n        if 'bboxes' in results:\n            # to list of boxes\n            if not isinstance(results['bboxes'], HorizontalBoxes):\n                raise NotImplementedError(\n                    'Albu only supports horizontal boxes now')\n            bboxes = results['bboxes'].numpy()\n            results['bboxes'] = [x for x in bboxes]\n            # add pseudo-field for filtration\n            if self.filter_lost_elements:\n                results['idx_mapper'] = np.arange(len(results['bboxes']))\n\n        # TODO: Support mask structure in albu\n        ori_masks = None\n        if 'masks' in results:\n            if isinstance(results['masks'], PolygonMasks):\n                raise NotImplementedError(\n                    'Albu only supports BitMap masks now')\n            ori_masks = results['masks']\n            if albumentations.__version__ < '0.5':\n                results['masks'] = results['masks'].masks\n            else:\n                results['masks'] = [mask for mask in results['masks'].masks]\n\n        return results, ori_masks\n\n    def _postprocess_results(\n            self,\n            results: dict,\n            ori_masks: Optional[Union[BitmapMasks,\n                                      PolygonMasks]] = None) -> dict:\n        \"\"\"Post-processing Albu output.\"\"\"\n        # albumentations may return np.array or list on different versions\n        if 'gt_bboxes_labels' in results and isinstance(\n                results['gt_bboxes_labels'], list):\n            results['gt_bboxes_labels'] = np.array(\n                results['gt_bboxes_labels'], dtype=np.int64)\n        if 'gt_ignore_flags' in results and isinstance(\n                results['gt_ignore_flags'], list):\n            results['gt_ignore_flags'] = np.array(\n                results['gt_ignore_flags'], dtype=bool)\n\n        if 'bboxes' in results:\n            if isinstance(results['bboxes'], list):\n                results['bboxes'] = np.array(\n                    results['bboxes'], dtype=np.float32)\n            results['bboxes'] = results['bboxes'].reshape(-1, 4)\n            results['bboxes'] = HorizontalBoxes(results['bboxes'])\n\n            # filter label_fields\n            if self.filter_lost_elements:\n\n                for label in self.origin_label_fields:\n                    results[label] = np.array(\n                        [results[label][i] for i in results['idx_mapper']])\n                if 'masks' in results:\n                    assert ori_masks is not None\n                    results['masks'] = np.array(\n                        [results['masks'][i] for i in results['idx_mapper']])\n                    results['masks'] = ori_masks.__class__(\n                        results['masks'],\n                        results['masks'][0].shape[0],\n                        results['masks'][0].shape[1],\n                    )\n                if (not len(results['idx_mapper'])\n                        and self.skip_img_without_anno):\n                    return None\n            elif 'masks' in results:\n                results['masks'] = ori_masks.__class__(results['masks'],\n                                                       ori_masks.height,\n                                                       ori_masks.width)\n\n        return results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__ + f'(transforms={self.transforms})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\n@avoid_cache_randomness\nclass RandomCenterCropPad(BaseTransform):\n    \"\"\"Random center crop and random around padding for CornerNet.\n\n    This operation generates randomly cropped image from the original image and\n    pads it simultaneously. Different from :class:`RandomCrop`, the output\n    shape may not equal to ``crop_size`` strictly. We choose a random value\n    from ``ratios`` and the output shape could be larger or smaller than\n    ``crop_size``. The padding operation is also different from :class:`Pad`,\n    here we use around padding instead of right-bottom padding.\n\n    The relation between output image (padding image) and original image:\n\n    .. code:: text\n\n                        output image\n\n               +----------------------------+\n               |          padded area       |\n        +------|----------------------------|----------+\n        |      |         cropped area       |          |\n        |      |         +---------------+  |          |\n        |      |         |    .   center |  |          | original image\n        |      |         |        range  |  |          |\n        |      |         +---------------+  |          |\n        +------|----------------------------|----------+\n               |          padded area       |\n               +----------------------------+\n\n    There are 5 main areas in the figure:\n\n    - output image: output image of this operation, also called padding\n      image in following instruction.\n    - original image: input image of this operation.\n    - padded area: non-intersect area of output image and original image.\n    - cropped area: the overlap of output image and original image.\n    - center range: a smaller area where random center chosen from.\n      center range is computed by ``border`` and original image's shape\n      to avoid our random center is too close to original image's border.\n\n    Also this operation act differently in train and test mode, the summary\n    pipeline is listed below.\n\n    Train pipeline:\n\n    1. Choose a ``random_ratio`` from ``ratios``, the shape of padding image\n       will be ``random_ratio * crop_size``.\n    2. Choose a ``random_center`` in center range.\n    3. Generate padding image with center matches the ``random_center``.\n    4. Initialize the padding image with pixel value equals to ``mean``.\n    5. Copy the cropped area to padding image.\n    6. Refine annotations.\n\n    Test pipeline:\n\n    1. Compute output shape according to ``test_pad_mode``.\n    2. Generate padding image with center matches the original image\n       center.\n    3. Initialize the padding image with pixel value equals to ``mean``.\n    4. Copy the ``cropped area`` to padding image.\n\n    Required Keys:\n\n    - img (np.float32)\n    - img_shape (tuple)\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n\n    Modified Keys:\n\n    - img (np.float32)\n    - img_shape (tuple)\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n\n    Args:\n        crop_size (tuple, optional): expected size after crop, final size will\n            computed according to ratio. Requires  (width, height)\n            in train mode, and None in test mode.\n        ratios (tuple, optional): random select a ratio from tuple and crop\n            image to (crop_size[0] * ratio) * (crop_size[1] * ratio).\n            Only available in train mode. Defaults to (0.9, 1.0, 1.1).\n        border (int, optional): max distance from center select area to image\n            border. Only available in train mode. Defaults to 128.\n        mean (sequence, optional): Mean values of 3 channels.\n        std (sequence, optional): Std values of 3 channels.\n        to_rgb (bool, optional): Whether to convert the image from BGR to RGB.\n        test_mode (bool): whether involve random variables in transform.\n            In train mode, crop_size is fixed, center coords and ratio is\n            random selected from predefined lists. In test mode, crop_size\n            is image's original shape, center coords and ratio is fixed.\n            Defaults to False.\n        test_pad_mode (tuple, optional): padding method and padding shape\n            value, only available in test mode. Default is using\n            'logical_or' with 127 as padding shape value.\n\n            - 'logical_or': final_shape = input_shape | padding_shape_value\n            - 'size_divisor': final_shape = int(\n              ceil(input_shape / padding_shape_value) * padding_shape_value)\n\n            Defaults to ('logical_or', 127).\n        test_pad_add_pix (int): Extra padding pixel in test mode.\n            Defaults to 0.\n        bbox_clip_border (bool): Whether clip the objects outside\n            the border of the image. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 crop_size: Optional[tuple] = None,\n                 ratios: Optional[tuple] = (0.9, 1.0, 1.1),\n                 border: Optional[int] = 128,\n                 mean: Optional[Sequence] = None,\n                 std: Optional[Sequence] = None,\n                 to_rgb: Optional[bool] = None,\n                 test_mode: bool = False,\n                 test_pad_mode: Optional[tuple] = ('logical_or', 127),\n                 test_pad_add_pix: int = 0,\n                 bbox_clip_border: bool = True) -> None:\n        if test_mode:\n            assert crop_size is None, 'crop_size must be None in test mode'\n            assert ratios is None, 'ratios must be None in test mode'\n            assert border is None, 'border must be None in test mode'\n            assert isinstance(test_pad_mode, (list, tuple))\n            assert test_pad_mode[0] in ['logical_or', 'size_divisor']\n        else:\n            assert isinstance(crop_size, (list, tuple))\n            assert crop_size[0] > 0 and crop_size[1] > 0, (\n                'crop_size must > 0 in train mode')\n            assert isinstance(ratios, (list, tuple))\n            assert test_pad_mode is None, (\n                'test_pad_mode must be None in train mode')\n\n        self.crop_size = crop_size\n        self.ratios = ratios\n        self.border = border\n        # We do not set default value to mean, std and to_rgb because these\n        # hyper-parameters are easy to forget but could affect the performance.\n        # Please use the same setting as Normalize for performance assurance.\n        assert mean is not None and std is not None and to_rgb is not None\n        self.to_rgb = to_rgb\n        self.input_mean = mean\n        self.input_std = std\n        if to_rgb:\n            self.mean = mean[::-1]\n            self.std = std[::-1]\n        else:\n            self.mean = mean\n            self.std = std\n        self.test_mode = test_mode\n        self.test_pad_mode = test_pad_mode\n        self.test_pad_add_pix = test_pad_add_pix\n        self.bbox_clip_border = bbox_clip_border\n\n    def _get_border(self, border, size):\n        \"\"\"Get final border for the target size.\n\n        This function generates a ``final_border`` according to image's shape.\n        The area between ``final_border`` and ``size - final_border`` is the\n        ``center range``. We randomly choose center from the ``center range``\n        to avoid our random center is too close to original image's border.\n        Also ``center range`` should be larger than 0.\n\n        Args:\n            border (int): The initial border, default is 128.\n            size (int): The width or height of original image.\n        Returns:\n            int: The final border.\n        \"\"\"\n        k = 2 * border / size\n        i = pow(2, np.ceil(np.log2(np.ceil(k))) + (k == int(k)))\n        return border // i\n\n    def _filter_boxes(self, patch, boxes):\n        \"\"\"Check whether the center of each box is in the patch.\n\n        Args:\n            patch (list[int]): The cropped area, [left, top, right, bottom].\n            boxes (numpy array, (N x 4)): Ground truth boxes.\n\n        Returns:\n            mask (numpy array, (N,)): Each box is inside or outside the patch.\n        \"\"\"\n        center = boxes.centers.numpy()\n        mask = (center[:, 0] > patch[0]) * (center[:, 1] > patch[1]) * (\n            center[:, 0] < patch[2]) * (\n                center[:, 1] < patch[3])\n        return mask\n\n    def _crop_image_and_paste(self, image, center, size):\n        \"\"\"Crop image with a given center and size, then paste the cropped\n        image to a blank image with two centers align.\n\n        This function is equivalent to generating a blank image with ``size``\n        as its shape. Then cover it on the original image with two centers (\n        the center of blank image and the random center of original image)\n        aligned. The overlap area is paste from the original image and the\n        outside area is filled with ``mean pixel``.\n\n        Args:\n            image (np array, H x W x C): Original image.\n            center (list[int]): Target crop center coord.\n            size (list[int]): Target crop size. [target_h, target_w]\n\n        Returns:\n            cropped_img (np array, target_h x target_w x C): Cropped image.\n            border (np array, 4): The distance of four border of\n                ``cropped_img`` to the original image area, [top, bottom,\n                left, right]\n            patch (list[int]): The cropped area, [left, top, right, bottom].\n        \"\"\"\n        center_y, center_x = center\n        target_h, target_w = size\n        img_h, img_w, img_c = image.shape\n\n        x0 = max(0, center_x - target_w // 2)\n        x1 = min(center_x + target_w // 2, img_w)\n        y0 = max(0, center_y - target_h // 2)\n        y1 = min(center_y + target_h // 2, img_h)\n        patch = np.array((int(x0), int(y0), int(x1), int(y1)))\n\n        left, right = center_x - x0, x1 - center_x\n        top, bottom = center_y - y0, y1 - center_y\n\n        cropped_center_y, cropped_center_x = target_h // 2, target_w // 2\n        cropped_img = np.zeros((target_h, target_w, img_c), dtype=image.dtype)\n        for i in range(img_c):\n            cropped_img[:, :, i] += self.mean[i]\n        y_slice = slice(cropped_center_y - top, cropped_center_y + bottom)\n        x_slice = slice(cropped_center_x - left, cropped_center_x + right)\n        cropped_img[y_slice, x_slice, :] = image[y0:y1, x0:x1, :]\n\n        border = np.array([\n            cropped_center_y - top, cropped_center_y + bottom,\n            cropped_center_x - left, cropped_center_x + right\n        ],\n                          dtype=np.float32)\n\n        return cropped_img, border, patch\n\n    def _train_aug(self, results):\n        \"\"\"Random crop and around padding the original image.\n\n        Args:\n            results (dict): Image infomations in the augment pipeline.\n\n        Returns:\n            results (dict): The updated dict.\n        \"\"\"\n        img = results['img']\n        h, w, c = img.shape\n        gt_bboxes = results['gt_bboxes']\n        while True:\n            scale = random.choice(self.ratios)\n            new_h = int(self.crop_size[1] * scale)\n            new_w = int(self.crop_size[0] * scale)\n            h_border = self._get_border(self.border, h)\n            w_border = self._get_border(self.border, w)\n\n            for i in range(50):\n                center_x = random.randint(low=w_border, high=w - w_border)\n                center_y = random.randint(low=h_border, high=h - h_border)\n\n                cropped_img, border, patch = self._crop_image_and_paste(\n                    img, [center_y, center_x], [new_h, new_w])\n\n                if len(gt_bboxes) == 0:\n                    results['img'] = cropped_img\n                    results['img_shape'] = cropped_img.shape[:2]\n                    return results\n\n                # if image do not have valid bbox, any crop patch is valid.\n                mask = self._filter_boxes(patch, gt_bboxes)\n                if not mask.any():\n                    continue\n\n                results['img'] = cropped_img\n                results['img_shape'] = cropped_img.shape[:2]\n\n                x0, y0, x1, y1 = patch\n\n                left_w, top_h = center_x - x0, center_y - y0\n                cropped_center_x, cropped_center_y = new_w // 2, new_h // 2\n\n                # crop bboxes accordingly and clip to the image boundary\n                gt_bboxes = gt_bboxes[mask]\n                gt_bboxes.translate_([\n                    cropped_center_x - left_w - x0,\n                    cropped_center_y - top_h - y0\n                ])\n                if self.bbox_clip_border:\n                    gt_bboxes.clip_([new_h, new_w])\n                keep = gt_bboxes.is_inside([new_h, new_w]).numpy()\n                gt_bboxes = gt_bboxes[keep]\n\n                results['gt_bboxes'] = gt_bboxes\n\n                # ignore_flags\n                if results.get('gt_ignore_flags', None) is not None:\n                    gt_ignore_flags = results['gt_ignore_flags'][mask]\n                    results['gt_ignore_flags'] = \\\n                        gt_ignore_flags[keep]\n\n                # labels\n                if results.get('gt_bboxes_labels', None) is not None:\n                    gt_labels = results['gt_bboxes_labels'][mask]\n                    results['gt_bboxes_labels'] = gt_labels[keep]\n\n                if 'gt_masks' in results or 'gt_seg_map' in results:\n                    raise NotImplementedError(\n                        'RandomCenterCropPad only supports bbox.')\n\n                return results\n\n    def _test_aug(self, results):\n        \"\"\"Around padding the original image without cropping.\n\n        The padding mode and value are from ``test_pad_mode``.\n\n        Args:\n            results (dict): Image infomations in the augment pipeline.\n\n        Returns:\n            results (dict): The updated dict.\n        \"\"\"\n        img = results['img']\n        h, w, c = img.shape\n        if self.test_pad_mode[0] in ['logical_or']:\n            # self.test_pad_add_pix is only used for centernet\n            target_h = (h | self.test_pad_mode[1]) + self.test_pad_add_pix\n            target_w = (w | self.test_pad_mode[1]) + self.test_pad_add_pix\n        elif self.test_pad_mode[0] in ['size_divisor']:\n            divisor = self.test_pad_mode[1]\n            target_h = int(np.ceil(h / divisor)) * divisor\n            target_w = int(np.ceil(w / divisor)) * divisor\n        else:\n            raise NotImplementedError(\n                'RandomCenterCropPad only support two testing pad mode:'\n                'logical-or and size_divisor.')\n\n        cropped_img, border, _ = self._crop_image_and_paste(\n            img, [h // 2, w // 2], [target_h, target_w])\n        results['img'] = cropped_img\n        results['img_shape'] = cropped_img.shape[:2]\n        results['border'] = border\n        return results\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        img = results['img']\n        assert img.dtype == np.float32, (\n            'RandomCenterCropPad needs the input image of dtype np.float32,'\n            ' please set \"to_float32=True\" in \"LoadImageFromFile\" pipeline')\n        h, w, c = img.shape\n        assert c == len(self.mean)\n        if self.test_mode:\n            return self._test_aug(results)\n        else:\n            return self._train_aug(results)\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(crop_size={self.crop_size}, '\n        repr_str += f'ratios={self.ratios}, '\n        repr_str += f'border={self.border}, '\n        repr_str += f'mean={self.input_mean}, '\n        repr_str += f'std={self.input_std}, '\n        repr_str += f'to_rgb={self.to_rgb}, '\n        repr_str += f'test_mode={self.test_mode}, '\n        repr_str += f'test_pad_mode={self.test_pad_mode}, '\n        repr_str += f'bbox_clip_border={self.bbox_clip_border})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass CutOut(BaseTransform):\n    \"\"\"CutOut operation.\n\n    Randomly drop some regions of image used in\n    `Cutout <https://arxiv.org/abs/1708.04552>`_.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        n_holes (int or tuple[int, int]): Number of regions to be dropped.\n            If it is given as a list, number of holes will be randomly\n            selected from the closed interval [``n_holes[0]``, ``n_holes[1]``].\n        cutout_shape (tuple[int, int] or list[tuple[int, int]], optional):\n            The candidate shape of dropped regions. It can be\n            ``tuple[int, int]`` to use a fixed cutout shape, or\n            ``list[tuple[int, int]]`` to randomly choose shape\n            from the list. Defaults to None.\n        cutout_ratio (tuple[float, float] or list[tuple[float, float]],\n            optional): The candidate ratio of dropped regions. It can be\n            ``tuple[float, float]`` to use a fixed ratio or\n            ``list[tuple[float, float]]`` to randomly choose ratio\n            from the list. Please note that ``cutout_shape`` and\n            ``cutout_ratio`` cannot be both given at the same time.\n            Defaults to None.\n        fill_in (tuple[float, float, float] or tuple[int, int, int]): The value\n            of pixel to fill in the dropped regions. Defaults to (0, 0, 0).\n    \"\"\"\n\n    def __init__(\n        self,\n        n_holes: Union[int, Tuple[int, int]],\n        cutout_shape: Optional[Union[Tuple[int, int],\n                                     List[Tuple[int, int]]]] = None,\n        cutout_ratio: Optional[Union[Tuple[float, float],\n                                     List[Tuple[float, float]]]] = None,\n        fill_in: Union[Tuple[float, float, float], Tuple[int, int,\n                                                         int]] = (0, 0, 0)\n    ) -> None:\n\n        assert (cutout_shape is None) ^ (cutout_ratio is None), \\\n            'Either cutout_shape or cutout_ratio should be specified.'\n        assert (isinstance(cutout_shape, (list, tuple))\n                or isinstance(cutout_ratio, (list, tuple)))\n        if isinstance(n_holes, tuple):\n            assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1]\n        else:\n            n_holes = (n_holes, n_holes)\n        self.n_holes = n_holes\n        self.fill_in = fill_in\n        self.with_ratio = cutout_ratio is not None\n        self.candidates = cutout_ratio if self.with_ratio else cutout_shape\n        if not isinstance(self.candidates, list):\n            self.candidates = [self.candidates]\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Call function to drop some regions of image.\"\"\"\n        h, w, c = results['img'].shape\n        n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1)\n        for _ in range(n_holes):\n            x1 = np.random.randint(0, w)\n            y1 = np.random.randint(0, h)\n            index = np.random.randint(0, len(self.candidates))\n            if not self.with_ratio:\n                cutout_w, cutout_h = self.candidates[index]\n            else:\n                cutout_w = int(self.candidates[index][0] * w)\n                cutout_h = int(self.candidates[index][1] * h)\n\n            x2 = np.clip(x1 + cutout_w, 0, w)\n            y2 = np.clip(y1 + cutout_h, 0, h)\n            results['img'][y1:y2, x1:x2, :] = self.fill_in\n\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(n_holes={self.n_holes}, '\n        repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio\n                     else f'cutout_shape={self.candidates}, ')\n        repr_str += f'fill_in={self.fill_in})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass Mosaic(BaseTransform):\n    \"\"\"Mosaic augmentation.\n\n    Given 4 images, mosaic transform combines them into\n    one output image. The output image is composed of the parts from each sub-\n    image.\n\n    .. code:: text\n\n                        mosaic transform\n                           center_x\n                +------------------------------+\n                |       pad        |  pad      |\n                |      +-----------+           |\n                |      |           |           |\n                |      |  image1   |--------+  |\n                |      |           |        |  |\n                |      |           | image2 |  |\n     center_y   |----+-------------+-----------|\n                |    |   cropped   |           |\n                |pad |   image3    |  image4   |\n                |    |             |           |\n                +----|-------------+-----------+\n                     |             |\n                     +-------------+\n\n     The mosaic transform steps are as follows:\n\n         1. Choose the mosaic center as the intersections of 4 images\n         2. Get the left top image according to the index, and randomly\n            sample another 3 images from the custom dataset.\n         3. Sub image will be cropped if image is larger than mosaic patch\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - mix_results (List[dict])\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_ignore_flags (optional)\n\n    Args:\n        img_scale (Sequence[int]): Image size before mosaic pipeline of single\n            image. The shape order should be (width, height).\n            Defaults to (640, 640).\n        center_ratio_range (Sequence[float]): Center ratio range of mosaic\n            output. Defaults to (0.5, 1.5).\n        bbox_clip_border (bool, optional): Whether to clip the objects outside\n            the border of the image. In some dataset like MOT17, the gt bboxes\n            are allowed to cross the border of images. Therefore, we don't\n            need to clip the gt bboxes in these cases. Defaults to True.\n        pad_val (int): Pad value. Defaults to 114.\n        prob (float): Probability of applying this transformation.\n            Defaults to 1.0.\n    \"\"\"\n\n    def __init__(self,\n                 img_scale: Tuple[int, int] = (640, 640),\n                 center_ratio_range: Tuple[float, float] = (0.5, 1.5),\n                 bbox_clip_border: bool = True,\n                 pad_val: float = 114.0,\n                 prob: float = 1.0) -> None:\n        assert isinstance(img_scale, tuple)\n        assert 0 <= prob <= 1.0, 'The probability should be in range [0,1]. ' \\\n                                 f'got {prob}.'\n\n        log_img_scale(img_scale, skip_square=True, shape_order='wh')\n        self.img_scale = img_scale\n        self.center_ratio_range = center_ratio_range\n        self.bbox_clip_border = bbox_clip_border\n        self.pad_val = pad_val\n        self.prob = prob\n\n    @cache_randomness\n    def get_indexes(self, dataset: BaseDataset) -> int:\n        \"\"\"Call function to collect indexes.\n\n        Args:\n            dataset (:obj:`MultiImageMixDataset`): The dataset.\n\n        Returns:\n            list: indexes.\n        \"\"\"\n\n        indexes = [random.randint(0, len(dataset)) for _ in range(3)]\n        return indexes\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Mosaic transform function.\n\n        Args:\n            results (dict): Result dict.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        if random.uniform(0, 1) > self.prob:\n            return results\n\n        assert 'mix_results' in results\n        mosaic_bboxes = []\n        mosaic_bboxes_labels = []\n        mosaic_ignore_flags = []\n        if len(results['img'].shape) == 3:\n            mosaic_img = np.full(\n                (int(self.img_scale[1] * 2), int(self.img_scale[0] * 2), 3),\n                self.pad_val,\n                dtype=results['img'].dtype)\n        else:\n            mosaic_img = np.full(\n                (int(self.img_scale[1] * 2), int(self.img_scale[0] * 2)),\n                self.pad_val,\n                dtype=results['img'].dtype)\n\n        # mosaic center x, y\n        center_x = int(\n            random.uniform(*self.center_ratio_range) * self.img_scale[0])\n        center_y = int(\n            random.uniform(*self.center_ratio_range) * self.img_scale[1])\n        center_position = (center_x, center_y)\n\n        loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right')\n        for i, loc in enumerate(loc_strs):\n            if loc == 'top_left':\n                results_patch = copy.deepcopy(results)\n            else:\n                results_patch = copy.deepcopy(results['mix_results'][i - 1])\n\n            img_i = results_patch['img']\n            h_i, w_i = img_i.shape[:2]\n            # keep_ratio resize\n            scale_ratio_i = min(self.img_scale[1] / h_i,\n                                self.img_scale[0] / w_i)\n            img_i = mmcv.imresize(\n                img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)))\n\n            # compute the combine parameters\n            paste_coord, crop_coord = self._mosaic_combine(\n                loc, center_position, img_i.shape[:2][::-1])\n            x1_p, y1_p, x2_p, y2_p = paste_coord\n            x1_c, y1_c, x2_c, y2_c = crop_coord\n\n            # crop and paste image\n            mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c]\n\n            # adjust coordinate\n            gt_bboxes_i = results_patch['gt_bboxes']\n            gt_bboxes_labels_i = results_patch['gt_bboxes_labels']\n            gt_ignore_flags_i = results_patch['gt_ignore_flags']\n\n            padw = x1_p - x1_c\n            padh = y1_p - y1_c\n            gt_bboxes_i.rescale_([scale_ratio_i, scale_ratio_i])\n            gt_bboxes_i.translate_([padw, padh])\n            mosaic_bboxes.append(gt_bboxes_i)\n            mosaic_bboxes_labels.append(gt_bboxes_labels_i)\n            mosaic_ignore_flags.append(gt_ignore_flags_i)\n\n        mosaic_bboxes = mosaic_bboxes[0].cat(mosaic_bboxes, 0)\n        mosaic_bboxes_labels = np.concatenate(mosaic_bboxes_labels, 0)\n        mosaic_ignore_flags = np.concatenate(mosaic_ignore_flags, 0)\n\n        if self.bbox_clip_border:\n            mosaic_bboxes.clip_([2 * self.img_scale[1], 2 * self.img_scale[0]])\n        # remove outside bboxes\n        inside_inds = mosaic_bboxes.is_inside(\n            [2 * self.img_scale[1], 2 * self.img_scale[0]]).numpy()\n        mosaic_bboxes = mosaic_bboxes[inside_inds]\n        mosaic_bboxes_labels = mosaic_bboxes_labels[inside_inds]\n        mosaic_ignore_flags = mosaic_ignore_flags[inside_inds]\n\n        results['img'] = mosaic_img\n        results['img_shape'] = mosaic_img.shape[:2]\n        results['gt_bboxes'] = mosaic_bboxes\n        results['gt_bboxes_labels'] = mosaic_bboxes_labels\n        results['gt_ignore_flags'] = mosaic_ignore_flags\n        return results\n\n    def _mosaic_combine(\n            self, loc: str, center_position_xy: Sequence[float],\n            img_shape_wh: Sequence[int]) -> Tuple[Tuple[int], Tuple[int]]:\n        \"\"\"Calculate global coordinate of mosaic image and local coordinate of\n        cropped sub-image.\n\n        Args:\n            loc (str): Index for the sub-image, loc in ('top_left',\n              'top_right', 'bottom_left', 'bottom_right').\n            center_position_xy (Sequence[float]): Mixing center for 4 images,\n                (x, y).\n            img_shape_wh (Sequence[int]): Width and height of sub-image\n\n        Returns:\n            tuple[tuple[float]]: Corresponding coordinate of pasting and\n                cropping\n                - paste_coord (tuple): paste corner coordinate in mosaic image.\n                - crop_coord (tuple): crop corner coordinate in mosaic image.\n        \"\"\"\n        assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right')\n        if loc == 'top_left':\n            # index0 to top left part of image\n            x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \\\n                             max(center_position_xy[1] - img_shape_wh[1], 0), \\\n                             center_position_xy[0], \\\n                             center_position_xy[1]\n            crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - (\n                y2 - y1), img_shape_wh[0], img_shape_wh[1]\n\n        elif loc == 'top_right':\n            # index1 to top right part of image\n            x1, y1, x2, y2 = center_position_xy[0], \\\n                             max(center_position_xy[1] - img_shape_wh[1], 0), \\\n                             min(center_position_xy[0] + img_shape_wh[0],\n                                 self.img_scale[0] * 2), \\\n                             center_position_xy[1]\n            crop_coord = 0, img_shape_wh[1] - (y2 - y1), min(\n                img_shape_wh[0], x2 - x1), img_shape_wh[1]\n\n        elif loc == 'bottom_left':\n            # index2 to bottom left part of image\n            x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \\\n                             center_position_xy[1], \\\n                             center_position_xy[0], \\\n                             min(self.img_scale[1] * 2, center_position_xy[1] +\n                                 img_shape_wh[1])\n            crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min(\n                y2 - y1, img_shape_wh[1])\n\n        else:\n            # index3 to bottom right part of image\n            x1, y1, x2, y2 = center_position_xy[0], \\\n                             center_position_xy[1], \\\n                             min(center_position_xy[0] + img_shape_wh[0],\n                                 self.img_scale[0] * 2), \\\n                             min(self.img_scale[1] * 2, center_position_xy[1] +\n                                 img_shape_wh[1])\n            crop_coord = 0, 0, min(img_shape_wh[0],\n                                   x2 - x1), min(y2 - y1, img_shape_wh[1])\n\n        paste_coord = x1, y1, x2, y2\n        return paste_coord, crop_coord\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(img_scale={self.img_scale}, '\n        repr_str += f'center_ratio_range={self.center_ratio_range}, '\n        repr_str += f'pad_val={self.pad_val}, '\n        repr_str += f'prob={self.prob})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass MixUp(BaseTransform):\n    \"\"\"MixUp data augmentation.\n\n    .. code:: text\n\n                         mixup transform\n                +------------------------------+\n                | mixup image   |              |\n                |      +--------|--------+     |\n                |      |        |        |     |\n                |---------------+        |     |\n                |      |                 |     |\n                |      |      image      |     |\n                |      |                 |     |\n                |      |                 |     |\n                |      |-----------------+     |\n                |             pad              |\n                +------------------------------+\n\n     The mixup transform steps are as follows:\n\n        1. Another random image is picked by dataset and embedded in\n           the top left patch(after padding and resizing)\n        2. The target of mixup transform is the weighted average of mixup\n           image and origin image.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - mix_results (List[dict])\n\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_ignore_flags (optional)\n\n\n    Args:\n        img_scale (Sequence[int]): Image output size after mixup pipeline.\n            The shape order should be (width, height). Defaults to (640, 640).\n        ratio_range (Sequence[float]): Scale ratio of mixup image.\n            Defaults to (0.5, 1.5).\n        flip_ratio (float): Horizontal flip ratio of mixup image.\n            Defaults to 0.5.\n        pad_val (int): Pad value. Defaults to 114.\n        max_iters (int): The maximum number of iterations. If the number of\n            iterations is greater than `max_iters`, but gt_bbox is still\n            empty, then the iteration is terminated. Defaults to 15.\n        bbox_clip_border (bool, optional): Whether to clip the objects outside\n            the border of the image. In some dataset like MOT17, the gt bboxes\n            are allowed to cross the border of images. Therefore, we don't\n            need to clip the gt bboxes in these cases. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 img_scale: Tuple[int, int] = (640, 640),\n                 ratio_range: Tuple[float, float] = (0.5, 1.5),\n                 flip_ratio: float = 0.5,\n                 pad_val: float = 114.0,\n                 max_iters: int = 15,\n                 bbox_clip_border: bool = True) -> None:\n        assert isinstance(img_scale, tuple)\n        log_img_scale(img_scale, skip_square=True, shape_order='wh')\n        self.dynamic_scale = img_scale\n        self.ratio_range = ratio_range\n        self.flip_ratio = flip_ratio\n        self.pad_val = pad_val\n        self.max_iters = max_iters\n        self.bbox_clip_border = bbox_clip_border\n\n    @cache_randomness\n    def get_indexes(self, dataset: BaseDataset) -> int:\n        \"\"\"Call function to collect indexes.\n\n        Args:\n            dataset (:obj:`MultiImageMixDataset`): The dataset.\n\n        Returns:\n            list: indexes.\n        \"\"\"\n\n        for i in range(self.max_iters):\n            index = random.randint(0, len(dataset))\n            gt_bboxes_i = dataset[index]['gt_bboxes']\n            if len(gt_bboxes_i) != 0:\n                break\n\n        return index\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"MixUp transform function.\n\n        Args:\n            results (dict): Result dict.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n\n        assert 'mix_results' in results\n        assert len(\n            results['mix_results']) == 1, 'MixUp only support 2 images now !'\n\n        if results['mix_results'][0]['gt_bboxes'].shape[0] == 0:\n            # empty bbox\n            return results\n\n        retrieve_results = results['mix_results'][0]\n        retrieve_img = retrieve_results['img']\n\n        jit_factor = random.uniform(*self.ratio_range)\n        is_flip = random.uniform(0, 1) > self.flip_ratio\n\n        if len(retrieve_img.shape) == 3:\n            out_img = np.ones(\n                (self.dynamic_scale[1], self.dynamic_scale[0], 3),\n                dtype=retrieve_img.dtype) * self.pad_val\n        else:\n            out_img = np.ones(\n                self.dynamic_scale[::-1],\n                dtype=retrieve_img.dtype) * self.pad_val\n\n        # 1. keep_ratio resize\n        scale_ratio = min(self.dynamic_scale[1] / retrieve_img.shape[0],\n                          self.dynamic_scale[0] / retrieve_img.shape[1])\n        retrieve_img = mmcv.imresize(\n            retrieve_img, (int(retrieve_img.shape[1] * scale_ratio),\n                           int(retrieve_img.shape[0] * scale_ratio)))\n\n        # 2. paste\n        out_img[:retrieve_img.shape[0], :retrieve_img.shape[1]] = retrieve_img\n\n        # 3. scale jit\n        scale_ratio *= jit_factor\n        out_img = mmcv.imresize(out_img, (int(out_img.shape[1] * jit_factor),\n                                          int(out_img.shape[0] * jit_factor)))\n\n        # 4. flip\n        if is_flip:\n            out_img = out_img[:, ::-1, :]\n\n        # 5. random crop\n        ori_img = results['img']\n        origin_h, origin_w = out_img.shape[:2]\n        target_h, target_w = ori_img.shape[:2]\n        padded_img = np.ones((max(origin_h, target_h), max(\n            origin_w, target_w), 3)) * self.pad_val\n        padded_img = padded_img.astype(np.uint8)\n        padded_img[:origin_h, :origin_w] = out_img\n\n        x_offset, y_offset = 0, 0\n        if padded_img.shape[0] > target_h:\n            y_offset = random.randint(0, padded_img.shape[0] - target_h)\n        if padded_img.shape[1] > target_w:\n            x_offset = random.randint(0, padded_img.shape[1] - target_w)\n        padded_cropped_img = padded_img[y_offset:y_offset + target_h,\n                                        x_offset:x_offset + target_w]\n\n        # 6. adjust bbox\n        retrieve_gt_bboxes = retrieve_results['gt_bboxes']\n        retrieve_gt_bboxes.rescale_([scale_ratio, scale_ratio])\n        if self.bbox_clip_border:\n            retrieve_gt_bboxes.clip_([origin_h, origin_w])\n\n        if is_flip:\n            retrieve_gt_bboxes.flip_([origin_h, origin_w],\n                                     direction='horizontal')\n\n        # 7. filter\n        cp_retrieve_gt_bboxes = retrieve_gt_bboxes.clone()\n        cp_retrieve_gt_bboxes.translate_([-x_offset, -y_offset])\n        if self.bbox_clip_border:\n            cp_retrieve_gt_bboxes.clip_([target_h, target_w])\n\n        # 8. mix up\n        ori_img = ori_img.astype(np.float32)\n        mixup_img = 0.5 * ori_img + 0.5 * padded_cropped_img.astype(np.float32)\n\n        retrieve_gt_bboxes_labels = retrieve_results['gt_bboxes_labels']\n        retrieve_gt_ignore_flags = retrieve_results['gt_ignore_flags']\n\n        mixup_gt_bboxes = cp_retrieve_gt_bboxes.cat(\n            (results['gt_bboxes'], cp_retrieve_gt_bboxes), dim=0)\n        mixup_gt_bboxes_labels = np.concatenate(\n            (results['gt_bboxes_labels'], retrieve_gt_bboxes_labels), axis=0)\n        mixup_gt_ignore_flags = np.concatenate(\n            (results['gt_ignore_flags'], retrieve_gt_ignore_flags), axis=0)\n\n        # remove outside bbox\n        inside_inds = mixup_gt_bboxes.is_inside([target_h, target_w]).numpy()\n        mixup_gt_bboxes = mixup_gt_bboxes[inside_inds]\n        mixup_gt_bboxes_labels = mixup_gt_bboxes_labels[inside_inds]\n        mixup_gt_ignore_flags = mixup_gt_ignore_flags[inside_inds]\n\n        results['img'] = mixup_img.astype(np.uint8)\n        results['img_shape'] = mixup_img.shape[:2]\n        results['gt_bboxes'] = mixup_gt_bboxes\n        results['gt_bboxes_labels'] = mixup_gt_bboxes_labels\n        results['gt_ignore_flags'] = mixup_gt_ignore_flags\n\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(dynamic_scale={self.dynamic_scale}, '\n        repr_str += f'ratio_range={self.ratio_range}, '\n        repr_str += f'flip_ratio={self.flip_ratio}, '\n        repr_str += f'pad_val={self.pad_val}, '\n        repr_str += f'max_iters={self.max_iters}, '\n        repr_str += f'bbox_clip_border={self.bbox_clip_border})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass RandomAffine(BaseTransform):\n    \"\"\"Random affine transform data augmentation.\n\n    This operation randomly generates affine transform matrix which including\n    rotation, translation, shear and scaling transforms.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_ignore_flags (optional)\n\n    Args:\n        max_rotate_degree (float): Maximum degrees of rotation transform.\n            Defaults to 10.\n        max_translate_ratio (float): Maximum ratio of translation.\n            Defaults to 0.1.\n        scaling_ratio_range (tuple[float]): Min and max ratio of\n            scaling transform. Defaults to (0.5, 1.5).\n        max_shear_degree (float): Maximum degrees of shear\n            transform. Defaults to 2.\n        border (tuple[int]): Distance from width and height sides of input\n            image to adjust output shape. Only used in mosaic dataset.\n            Defaults to (0, 0).\n        border_val (tuple[int]): Border padding values of 3 channels.\n            Defaults to (114, 114, 114).\n        bbox_clip_border (bool, optional): Whether to clip the objects outside\n            the border of the image. In some dataset like MOT17, the gt bboxes\n            are allowed to cross the border of images. Therefore, we don't\n            need to clip the gt bboxes in these cases. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 max_rotate_degree: float = 10.0,\n                 max_translate_ratio: float = 0.1,\n                 scaling_ratio_range: Tuple[float, float] = (0.5, 1.5),\n                 max_shear_degree: float = 2.0,\n                 border: Tuple[int, int] = (0, 0),\n                 border_val: Tuple[int, int, int] = (114, 114, 114),\n                 bbox_clip_border: bool = True) -> None:\n        assert 0 <= max_translate_ratio <= 1\n        assert scaling_ratio_range[0] <= scaling_ratio_range[1]\n        assert scaling_ratio_range[0] > 0\n        self.max_rotate_degree = max_rotate_degree\n        self.max_translate_ratio = max_translate_ratio\n        self.scaling_ratio_range = scaling_ratio_range\n        self.max_shear_degree = max_shear_degree\n        self.border = border\n        self.border_val = border_val\n        self.bbox_clip_border = bbox_clip_border\n\n    @cache_randomness\n    def _get_random_homography_matrix(self, height, width):\n        # Rotation\n        rotation_degree = random.uniform(-self.max_rotate_degree,\n                                         self.max_rotate_degree)\n        rotation_matrix = self._get_rotation_matrix(rotation_degree)\n\n        # Scaling\n        scaling_ratio = random.uniform(self.scaling_ratio_range[0],\n                                       self.scaling_ratio_range[1])\n        scaling_matrix = self._get_scaling_matrix(scaling_ratio)\n\n        # Shear\n        x_degree = random.uniform(-self.max_shear_degree,\n                                  self.max_shear_degree)\n        y_degree = random.uniform(-self.max_shear_degree,\n                                  self.max_shear_degree)\n        shear_matrix = self._get_shear_matrix(x_degree, y_degree)\n\n        # Translation\n        trans_x = random.uniform(-self.max_translate_ratio,\n                                 self.max_translate_ratio) * width\n        trans_y = random.uniform(-self.max_translate_ratio,\n                                 self.max_translate_ratio) * height\n        translate_matrix = self._get_translation_matrix(trans_x, trans_y)\n\n        warp_matrix = (\n            translate_matrix @ shear_matrix @ rotation_matrix @ scaling_matrix)\n        return warp_matrix\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        img = results['img']\n        height = img.shape[0] + self.border[1] * 2\n        width = img.shape[1] + self.border[0] * 2\n\n        warp_matrix = self._get_random_homography_matrix(height, width)\n\n        img = cv2.warpPerspective(\n            img,\n            warp_matrix,\n            dsize=(width, height),\n            borderValue=self.border_val)\n        results['img'] = img\n        results['img_shape'] = img.shape[:2]\n\n        bboxes = results['gt_bboxes']\n        num_bboxes = len(bboxes)\n        if num_bboxes:\n            bboxes.project_(warp_matrix)\n            if self.bbox_clip_border:\n                bboxes.clip_([height, width])\n            # remove outside bbox\n            valid_index = bboxes.is_inside([height, width]).numpy()\n            results['gt_bboxes'] = bboxes[valid_index]\n            results['gt_bboxes_labels'] = results['gt_bboxes_labels'][\n                valid_index]\n            results['gt_ignore_flags'] = results['gt_ignore_flags'][\n                valid_index]\n\n            if 'gt_masks' in results:\n                raise NotImplementedError('RandomAffine only supports bbox.')\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(max_rotate_degree={self.max_rotate_degree}, '\n        repr_str += f'max_translate_ratio={self.max_translate_ratio}, '\n        repr_str += f'scaling_ratio_range={self.scaling_ratio_range}, '\n        repr_str += f'max_shear_degree={self.max_shear_degree}, '\n        repr_str += f'border={self.border}, '\n        repr_str += f'border_val={self.border_val}, '\n        repr_str += f'bbox_clip_border={self.bbox_clip_border})'\n        return repr_str\n\n    @staticmethod\n    def _get_rotation_matrix(rotate_degrees: float) -> np.ndarray:\n        radian = math.radians(rotate_degrees)\n        rotation_matrix = np.array(\n            [[np.cos(radian), -np.sin(radian), 0.],\n             [np.sin(radian), np.cos(radian), 0.], [0., 0., 1.]],\n            dtype=np.float32)\n        return rotation_matrix\n\n    @staticmethod\n    def _get_scaling_matrix(scale_ratio: float) -> np.ndarray:\n        scaling_matrix = np.array(\n            [[scale_ratio, 0., 0.], [0., scale_ratio, 0.], [0., 0., 1.]],\n            dtype=np.float32)\n        return scaling_matrix\n\n    @staticmethod\n    def _get_shear_matrix(x_shear_degrees: float,\n                          y_shear_degrees: float) -> np.ndarray:\n        x_radian = math.radians(x_shear_degrees)\n        y_radian = math.radians(y_shear_degrees)\n        shear_matrix = np.array([[1, np.tan(x_radian), 0.],\n                                 [np.tan(y_radian), 1, 0.], [0., 0., 1.]],\n                                dtype=np.float32)\n        return shear_matrix\n\n    @staticmethod\n    def _get_translation_matrix(x: float, y: float) -> np.ndarray:\n        translation_matrix = np.array([[1, 0., x], [0., 1, y], [0., 0., 1.]],\n                                      dtype=np.float32)\n        return translation_matrix\n\n\n@TRANSFORMS.register_module()\nclass YOLOXHSVRandomAug(BaseTransform):\n    \"\"\"Apply HSV augmentation to image sequentially. It is referenced from\n    https://github.com/Megvii-\n    BaseDetection/YOLOX/blob/main/yolox/data/data_augment.py#L21.\n\n    Required Keys:\n\n    - img\n\n    Modified Keys:\n\n    - img\n\n    Args:\n        hue_delta (int): delta of hue. Defaults to 5.\n        saturation_delta (int): delta of saturation. Defaults to 30.\n        value_delta (int): delat of value. Defaults to 30.\n    \"\"\"\n\n    def __init__(self,\n                 hue_delta: int = 5,\n                 saturation_delta: int = 30,\n                 value_delta: int = 30) -> None:\n        self.hue_delta = hue_delta\n        self.saturation_delta = saturation_delta\n        self.value_delta = value_delta\n\n    @cache_randomness\n    def _get_hsv_gains(self):\n        hsv_gains = np.random.uniform(-1, 1, 3) * [\n            self.hue_delta, self.saturation_delta, self.value_delta\n        ]\n        # random selection of h, s, v\n        hsv_gains *= np.random.randint(0, 2, 3)\n        # prevent overflow\n        hsv_gains = hsv_gains.astype(np.int16)\n        return hsv_gains\n\n    def transform(self, results: dict) -> dict:\n        img = results['img']\n        hsv_gains = self._get_hsv_gains()\n        img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype(np.int16)\n\n        img_hsv[..., 0] = (img_hsv[..., 0] + hsv_gains[0]) % 180\n        img_hsv[..., 1] = np.clip(img_hsv[..., 1] + hsv_gains[1], 0, 255)\n        img_hsv[..., 2] = np.clip(img_hsv[..., 2] + hsv_gains[2], 0, 255)\n        cv2.cvtColor(img_hsv.astype(img.dtype), cv2.COLOR_HSV2BGR, dst=img)\n\n        results['img'] = img\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(hue_delta={self.hue_delta}, '\n        repr_str += f'saturation_delta={self.saturation_delta}, '\n        repr_str += f'value_delta={self.value_delta})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass CopyPaste(BaseTransform):\n    \"\"\"Simple Copy-Paste is a Strong Data Augmentation Method for Instance\n    Segmentation The simple copy-paste transform steps are as follows:\n\n    1. The destination image is already resized with aspect ratio kept,\n       cropped and padded.\n    2. Randomly select a source image, which is also already resized\n       with aspect ratio kept, cropped and padded in a similar way\n       as the destination image.\n    3. Randomly select some objects from the source image.\n    4. Paste these source objects to the destination image directly,\n       due to the source and destination image have the same size.\n    5. Update object masks of the destination image, for some origin objects\n       may be occluded.\n    6. Generate bboxes from the updated destination masks and\n       filter some objects which are totally occluded, and adjust bboxes\n       which are partly occluded.\n    7. Append selected source bboxes, masks, and labels.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (BaseBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - gt_masks (BitmapMasks) (optional)\n\n    Modified Keys:\n\n    - img\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_ignore_flags (optional)\n    - gt_masks (optional)\n\n    Args:\n        max_num_pasted (int): The maximum number of pasted objects.\n            Defaults to 100.\n        bbox_occluded_thr (int): The threshold of occluded bbox.\n            Defaults to 10.\n        mask_occluded_thr (int): The threshold of occluded mask.\n            Defaults to 300.\n        selected (bool): Whether select objects or not. If select is False,\n            all objects of the source image will be pasted to the\n            destination image.\n            Defaults to True.\n        paste_by_box (bool): Whether use boxes as masks when masks are not\n            available.\n            Defaults to False.\n    \"\"\"\n\n    def __init__(\n        self,\n        max_num_pasted: int = 100,\n        bbox_occluded_thr: int = 10,\n        mask_occluded_thr: int = 300,\n        selected: bool = True,\n        paste_by_box: bool = False,\n    ) -> None:\n        self.max_num_pasted = max_num_pasted\n        self.bbox_occluded_thr = bbox_occluded_thr\n        self.mask_occluded_thr = mask_occluded_thr\n        self.selected = selected\n        self.paste_by_box = paste_by_box\n\n    @cache_randomness\n    def get_indexes(self, dataset: BaseDataset) -> int:\n        \"\"\"Call function to collect indexes.s.\n\n        Args:\n            dataset (:obj:`MultiImageMixDataset`): The dataset.\n        Returns:\n            list: Indexes.\n        \"\"\"\n        return random.randint(0, len(dataset))\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to make a copy-paste of image.\n\n        Args:\n            results (dict): Result dict.\n        Returns:\n            dict: Result dict with copy-paste transformed.\n        \"\"\"\n\n        assert 'mix_results' in results\n        num_images = len(results['mix_results'])\n        assert num_images == 1, \\\n            f'CopyPaste only supports processing 2 images, got {num_images}'\n        if self.selected:\n            selected_results = self._select_object(results['mix_results'][0])\n        else:\n            selected_results = results['mix_results'][0]\n        return self._copy_paste(results, selected_results)\n\n    @cache_randomness\n    def _get_selected_inds(self, num_bboxes: int) -> np.ndarray:\n        max_num_pasted = min(num_bboxes + 1, self.max_num_pasted)\n        num_pasted = np.random.randint(0, max_num_pasted)\n        return np.random.choice(num_bboxes, size=num_pasted, replace=False)\n\n    def get_gt_masks(self, results: dict) -> BitmapMasks:\n        \"\"\"Get gt_masks originally or generated based on bboxes.\n\n        If gt_masks is not contained in results,\n        it will be generated based on gt_bboxes.\n        Args:\n            results (dict): Result dict.\n        Returns:\n            BitmapMasks: gt_masks, originally or generated based on bboxes.\n        \"\"\"\n        if results.get('gt_masks', None) is not None:\n            if self.paste_by_box:\n                warnings.warn('gt_masks is already contained in results, '\n                              'so paste_by_box is disabled.')\n            return results['gt_masks']\n        else:\n            if not self.paste_by_box:\n                raise RuntimeError('results does not contain masks.')\n            return results['gt_bboxes'].create_masks(results['img'].shape[:2])\n\n    def _select_object(self, results: dict) -> dict:\n        \"\"\"Select some objects from the source results.\"\"\"\n        bboxes = results['gt_bboxes']\n        labels = results['gt_bboxes_labels']\n        masks = self.get_gt_masks(results)\n        ignore_flags = results['gt_ignore_flags']\n\n        selected_inds = self._get_selected_inds(bboxes.shape[0])\n\n        selected_bboxes = bboxes[selected_inds]\n        selected_labels = labels[selected_inds]\n        selected_masks = masks[selected_inds]\n        selected_ignore_flags = ignore_flags[selected_inds]\n\n        results['gt_bboxes'] = selected_bboxes\n        results['gt_bboxes_labels'] = selected_labels\n        results['gt_masks'] = selected_masks\n        results['gt_ignore_flags'] = selected_ignore_flags\n        return results\n\n    def _copy_paste(self, dst_results: dict, src_results: dict) -> dict:\n        \"\"\"CopyPaste transform function.\n\n        Args:\n            dst_results (dict): Result dict of the destination image.\n            src_results (dict): Result dict of the source image.\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        dst_img = dst_results['img']\n        dst_bboxes = dst_results['gt_bboxes']\n        dst_labels = dst_results['gt_bboxes_labels']\n        dst_masks = self.get_gt_masks(dst_results)\n        dst_ignore_flags = dst_results['gt_ignore_flags']\n\n        src_img = src_results['img']\n        src_bboxes = src_results['gt_bboxes']\n        src_labels = src_results['gt_bboxes_labels']\n        src_masks = src_results['gt_masks']\n        src_ignore_flags = src_results['gt_ignore_flags']\n\n        if len(src_bboxes) == 0:\n            return dst_results\n\n        # update masks and generate bboxes from updated masks\n        composed_mask = np.where(np.any(src_masks.masks, axis=0), 1, 0)\n        updated_dst_masks = self._get_updated_masks(dst_masks, composed_mask)\n        updated_dst_bboxes = updated_dst_masks.get_bboxes(type(dst_bboxes))\n        assert len(updated_dst_bboxes) == len(updated_dst_masks)\n\n        # filter totally occluded objects\n        l1_distance = (updated_dst_bboxes.tensor - dst_bboxes.tensor).abs()\n        bboxes_inds = (l1_distance <= self.bbox_occluded_thr).all(\n            dim=-1).numpy()\n        masks_inds = updated_dst_masks.masks.sum(\n            axis=(1, 2)) > self.mask_occluded_thr\n        valid_inds = bboxes_inds | masks_inds\n\n        # Paste source objects to destination image directly\n        img = dst_img * (1 - composed_mask[..., np.newaxis]\n                         ) + src_img * composed_mask[..., np.newaxis]\n        bboxes = src_bboxes.cat([updated_dst_bboxes[valid_inds], src_bboxes])\n        labels = np.concatenate([dst_labels[valid_inds], src_labels])\n        masks = np.concatenate(\n            [updated_dst_masks.masks[valid_inds], src_masks.masks])\n        ignore_flags = np.concatenate(\n            [dst_ignore_flags[valid_inds], src_ignore_flags])\n\n        dst_results['img'] = img\n        dst_results['gt_bboxes'] = bboxes\n        dst_results['gt_bboxes_labels'] = labels\n        dst_results['gt_masks'] = BitmapMasks(masks, masks.shape[1],\n                                              masks.shape[2])\n        dst_results['gt_ignore_flags'] = ignore_flags\n\n        return dst_results\n\n    def _get_updated_masks(self, masks: BitmapMasks,\n                           composed_mask: np.ndarray) -> BitmapMasks:\n        \"\"\"Update masks with composed mask.\"\"\"\n        assert masks.masks.shape[-2:] == composed_mask.shape[-2:], \\\n            'Cannot compare two arrays of different size'\n        masks.masks = np.where(composed_mask, 0, masks.masks)\n        return masks\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(max_num_pasted={self.max_num_pasted}, '\n        repr_str += f'bbox_occluded_thr={self.bbox_occluded_thr}, '\n        repr_str += f'mask_occluded_thr={self.mask_occluded_thr}, '\n        repr_str += f'selected={self.selected}), '\n        repr_str += f'paste_by_box={self.paste_by_box})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass RandomErasing(BaseTransform):\n    \"\"\"RandomErasing operation.\n\n    Random Erasing randomly selects a rectangle region\n    in an image and erases its pixels with random values.\n    `RandomErasing <https://arxiv.org/abs/1708.04896>`_.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (HorizontalBoxes[torch.float32]) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - gt_masks (BitmapMasks) (optional)\n\n    Modified Keys:\n    - img\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_ignore_flags (optional)\n    - gt_masks (optional)\n\n    Args:\n        n_patches (int or tuple[int, int]): Number of regions to be dropped.\n            If it is given as a tuple, number of patches will be randomly\n            selected from the closed interval [``n_patches[0]``,\n            ``n_patches[1]``].\n        ratio (float or tuple[float, float]): The ratio of erased regions.\n            It can be ``float`` to use a fixed ratio or ``tuple[float, float]``\n            to randomly choose ratio from the interval.\n        squared (bool): Whether to erase square region. Defaults to True.\n        bbox_erased_thr (float): The threshold for the maximum area proportion\n            of the bbox to be erased. When the proportion of the area where the\n            bbox is erased is greater than the threshold, the bbox will be\n            removed. Defaults to 0.9.\n        img_border_value (int or float or tuple): The filled values for\n            image border. If float, the same fill value will be used for\n            all the three channels of image. If tuple, it should be 3 elements.\n            Defaults to 128.\n        mask_border_value (int): The fill value used for masks. Defaults to 0.\n        seg_ignore_label (int): The fill value used for segmentation map.\n            Note this value must equals ``ignore_label`` in ``semantic_head``\n            of the corresponding config. Defaults to 255.\n    \"\"\"\n\n    def __init__(\n        self,\n        n_patches: Union[int, Tuple[int, int]],\n        ratio: Union[float, Tuple[float, float]],\n        squared: bool = True,\n        bbox_erased_thr: float = 0.9,\n        img_border_value: Union[int, float, tuple] = 128,\n        mask_border_value: int = 0,\n        seg_ignore_label: int = 255,\n    ) -> None:\n        if isinstance(n_patches, tuple):\n            assert len(n_patches) == 2 and 0 <= n_patches[0] < n_patches[1]\n        else:\n            n_patches = (n_patches, n_patches)\n        if isinstance(ratio, tuple):\n            assert len(ratio) == 2 and 0 <= ratio[0] < ratio[1] <= 1\n        else:\n            ratio = (ratio, ratio)\n\n        self.n_patches = n_patches\n        self.ratio = ratio\n        self.squared = squared\n        self.bbox_erased_thr = bbox_erased_thr\n        self.img_border_value = img_border_value\n        self.mask_border_value = mask_border_value\n        self.seg_ignore_label = seg_ignore_label\n\n    @cache_randomness\n    def _get_patches(self, img_shape: Tuple[int, int]) -> List[list]:\n        \"\"\"Get patches for random erasing.\"\"\"\n        patches = []\n        n_patches = np.random.randint(self.n_patches[0], self.n_patches[1] + 1)\n        for _ in range(n_patches):\n            if self.squared:\n                ratio = np.random.random() * (self.ratio[1] -\n                                              self.ratio[0]) + self.ratio[0]\n                ratio = (ratio, ratio)\n            else:\n                ratio = (np.random.random() * (self.ratio[1] - self.ratio[0]) +\n                         self.ratio[0], np.random.random() *\n                         (self.ratio[1] - self.ratio[0]) + self.ratio[0])\n            ph, pw = int(img_shape[0] * ratio[0]), int(img_shape[1] * ratio[1])\n            px1, py1 = np.random.randint(0,\n                                         img_shape[1] - pw), np.random.randint(\n                                             0, img_shape[0] - ph)\n            px2, py2 = px1 + pw, py1 + ph\n            patches.append([px1, py1, px2, py2])\n        return np.array(patches)\n\n    def _transform_img(self, results: dict, patches: List[list]) -> None:\n        \"\"\"Random erasing the image.\"\"\"\n        for patch in patches:\n            px1, py1, px2, py2 = patch\n            results['img'][py1:py2, px1:px2, :] = self.img_border_value\n\n    def _transform_bboxes(self, results: dict, patches: List[list]) -> None:\n        \"\"\"Random erasing the bboxes.\"\"\"\n        bboxes = results['gt_bboxes']\n        # TODO: unify the logic by using operators in BaseBoxes.\n        assert isinstance(bboxes, HorizontalBoxes)\n        bboxes = bboxes.numpy()\n        left_top = np.maximum(bboxes[:, None, :2], patches[:, :2])\n        right_bottom = np.minimum(bboxes[:, None, 2:], patches[:, 2:])\n        wh = np.maximum(right_bottom - left_top, 0)\n        inter_areas = wh[:, :, 0] * wh[:, :, 1]\n        bbox_areas = (bboxes[:, 2] - bboxes[:, 0]) * (\n            bboxes[:, 3] - bboxes[:, 1])\n        bboxes_erased_ratio = inter_areas.sum(-1) / (bbox_areas + 1e-7)\n        valid_inds = bboxes_erased_ratio < self.bbox_erased_thr\n        results['gt_bboxes'] = HorizontalBoxes(bboxes[valid_inds])\n        results['gt_bboxes_labels'] = results['gt_bboxes_labels'][valid_inds]\n        results['gt_ignore_flags'] = results['gt_ignore_flags'][valid_inds]\n        if results.get('gt_masks', None) is not None:\n            results['gt_masks'] = results['gt_masks'][valid_inds]\n\n    def _transform_masks(self, results: dict, patches: List[list]) -> None:\n        \"\"\"Random erasing the masks.\"\"\"\n        for patch in patches:\n            px1, py1, px2, py2 = patch\n            results['gt_masks'].masks[:, py1:py2,\n                                      px1:px2] = self.mask_border_value\n\n    def _transform_seg(self, results: dict, patches: List[list]) -> None:\n        \"\"\"Random erasing the segmentation map.\"\"\"\n        for patch in patches:\n            px1, py1, px2, py2 = patch\n            results['gt_seg_map'][py1:py2, px1:px2] = self.seg_ignore_label\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to erase some regions of image.\"\"\"\n        patches = self._get_patches(results['img_shape'])\n        self._transform_img(results, patches)\n        if results.get('gt_bboxes', None) is not None:\n            self._transform_bboxes(results, patches)\n        if results.get('gt_masks', None) is not None:\n            self._transform_masks(results, patches)\n        if results.get('gt_seg_map', None) is not None:\n            self._transform_seg(results, patches)\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(n_patches={self.n_patches}, '\n        repr_str += f'ratio={self.ratio}, '\n        repr_str += f'squared={self.squared}, '\n        repr_str += f'bbox_erased_thr={self.bbox_erased_thr}, '\n        repr_str += f'img_border_value={self.img_border_value}, '\n        repr_str += f'mask_border_value={self.mask_border_value}, '\n        repr_str += f'seg_ignore_label={self.seg_ignore_label})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass CachedMosaic(Mosaic):\n    \"\"\"Cached mosaic augmentation.\n\n    Cached mosaic transform will random select images from the cache\n    and combine them into one output image.\n\n    .. code:: text\n\n                        mosaic transform\n                           center_x\n                +------------------------------+\n                |       pad        |  pad      |\n                |      +-----------+           |\n                |      |           |           |\n                |      |  image1   |--------+  |\n                |      |           |        |  |\n                |      |           | image2 |  |\n     center_y   |----+-------------+-----------|\n                |    |   cropped   |           |\n                |pad |   image3    |  image4   |\n                |    |             |           |\n                +----|-------------+-----------+\n                     |             |\n                     +-------------+\n\n     The cached mosaic transform steps are as follows:\n\n         1. Append the results from the last transform into the cache.\n         2. Choose the mosaic center as the intersections of 4 images\n         3. Get the left top image according to the index, and randomly\n            sample another 3 images from the result cache.\n         4. Sub image will be cropped if image is larger than mosaic patch\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (np.float32) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_ignore_flags (optional)\n\n    Args:\n        img_scale (Sequence[int]): Image size before mosaic pipeline of single\n            image. The shape order should be (width, height).\n            Defaults to (640, 640).\n        center_ratio_range (Sequence[float]): Center ratio range of mosaic\n            output. Defaults to (0.5, 1.5).\n        bbox_clip_border (bool, optional): Whether to clip the objects outside\n            the border of the image. In some dataset like MOT17, the gt bboxes\n            are allowed to cross the border of images. Therefore, we don't\n            need to clip the gt bboxes in these cases. Defaults to True.\n        pad_val (int): Pad value. Defaults to 114.\n        prob (float): Probability of applying this transformation.\n            Defaults to 1.0.\n        max_cached_images (int): The maximum length of the cache. The larger\n            the cache, the stronger the randomness of this transform. As a\n            rule of thumb, providing 10 caches for each image suffices for\n            randomness. Defaults to 40.\n        random_pop (bool): Whether to randomly pop a result from the cache\n            when the cache is full. If set to False, use FIFO popping method.\n            Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 max_cached_images: int = 40,\n                 random_pop: bool = True,\n                 **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        self.results_cache = []\n        self.random_pop = random_pop\n        assert max_cached_images >= 4, 'The length of cache must >= 4, ' \\\n                                       f'but got {max_cached_images}.'\n        self.max_cached_images = max_cached_images\n\n    @cache_randomness\n    def get_indexes(self, cache: list) -> list:\n        \"\"\"Call function to collect indexes.\n\n        Args:\n            cache (list): The results cache.\n\n        Returns:\n            list: indexes.\n        \"\"\"\n\n        indexes = [random.randint(0, len(cache) - 1) for _ in range(3)]\n        return indexes\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"Mosaic transform function.\n\n        Args:\n            results (dict): Result dict.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        # cache and pop images\n        self.results_cache.append(copy.deepcopy(results))\n        if len(self.results_cache) > self.max_cached_images:\n            if self.random_pop:\n                index = random.randint(0, len(self.results_cache) - 1)\n            else:\n                index = 0\n            self.results_cache.pop(index)\n\n        if len(self.results_cache) <= 4:\n            return results\n\n        if random.uniform(0, 1) > self.prob:\n            return results\n        indices = self.get_indexes(self.results_cache)\n        mix_results = [copy.deepcopy(self.results_cache[i]) for i in indices]\n\n        # TODO: refactor mosaic to reuse these code.\n        mosaic_bboxes = []\n        mosaic_bboxes_labels = []\n        mosaic_ignore_flags = []\n        mosaic_masks = []\n        with_mask = True if 'gt_masks' in results else False\n\n        if len(results['img'].shape) == 3:\n            mosaic_img = np.full(\n                (int(self.img_scale[1] * 2), int(self.img_scale[0] * 2), 3),\n                self.pad_val,\n                dtype=results['img'].dtype)\n        else:\n            mosaic_img = np.full(\n                (int(self.img_scale[1] * 2), int(self.img_scale[0] * 2)),\n                self.pad_val,\n                dtype=results['img'].dtype)\n\n        # mosaic center x, y\n        center_x = int(\n            random.uniform(*self.center_ratio_range) * self.img_scale[0])\n        center_y = int(\n            random.uniform(*self.center_ratio_range) * self.img_scale[1])\n        center_position = (center_x, center_y)\n\n        loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right')\n        for i, loc in enumerate(loc_strs):\n            if loc == 'top_left':\n                results_patch = copy.deepcopy(results)\n            else:\n                results_patch = copy.deepcopy(mix_results[i - 1])\n\n            img_i = results_patch['img']\n            h_i, w_i = img_i.shape[:2]\n            # keep_ratio resize\n            scale_ratio_i = min(self.img_scale[1] / h_i,\n                                self.img_scale[0] / w_i)\n            img_i = mmcv.imresize(\n                img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)))\n\n            # compute the combine parameters\n            paste_coord, crop_coord = self._mosaic_combine(\n                loc, center_position, img_i.shape[:2][::-1])\n            x1_p, y1_p, x2_p, y2_p = paste_coord\n            x1_c, y1_c, x2_c, y2_c = crop_coord\n\n            # crop and paste image\n            mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c]\n\n            # adjust coordinate\n            gt_bboxes_i = results_patch['gt_bboxes']\n            gt_bboxes_labels_i = results_patch['gt_bboxes_labels']\n            gt_ignore_flags_i = results_patch['gt_ignore_flags']\n\n            padw = x1_p - x1_c\n            padh = y1_p - y1_c\n            gt_bboxes_i.rescale_([scale_ratio_i, scale_ratio_i])\n            gt_bboxes_i.translate_([padw, padh])\n            mosaic_bboxes.append(gt_bboxes_i)\n            mosaic_bboxes_labels.append(gt_bboxes_labels_i)\n            mosaic_ignore_flags.append(gt_ignore_flags_i)\n            if with_mask and results_patch.get('gt_masks', None) is not None:\n                gt_masks_i = results_patch['gt_masks']\n                gt_masks_i = gt_masks_i.rescale(float(scale_ratio_i))\n                gt_masks_i = gt_masks_i.translate(\n                    out_shape=(int(self.img_scale[0] * 2),\n                               int(self.img_scale[1] * 2)),\n                    offset=padw,\n                    direction='horizontal')\n                gt_masks_i = gt_masks_i.translate(\n                    out_shape=(int(self.img_scale[0] * 2),\n                               int(self.img_scale[1] * 2)),\n                    offset=padh,\n                    direction='vertical')\n                mosaic_masks.append(gt_masks_i)\n\n        mosaic_bboxes = mosaic_bboxes[0].cat(mosaic_bboxes, 0)\n        mosaic_bboxes_labels = np.concatenate(mosaic_bboxes_labels, 0)\n        mosaic_ignore_flags = np.concatenate(mosaic_ignore_flags, 0)\n\n        if self.bbox_clip_border:\n            mosaic_bboxes.clip_([2 * self.img_scale[1], 2 * self.img_scale[0]])\n        # remove outside bboxes\n        inside_inds = mosaic_bboxes.is_inside(\n            [2 * self.img_scale[1], 2 * self.img_scale[0]]).numpy()\n        mosaic_bboxes = mosaic_bboxes[inside_inds]\n        mosaic_bboxes_labels = mosaic_bboxes_labels[inside_inds]\n        mosaic_ignore_flags = mosaic_ignore_flags[inside_inds]\n\n        results['img'] = mosaic_img\n        results['img_shape'] = mosaic_img.shape[:2]\n        results['gt_bboxes'] = mosaic_bboxes\n        results['gt_bboxes_labels'] = mosaic_bboxes_labels\n        results['gt_ignore_flags'] = mosaic_ignore_flags\n\n        if with_mask:\n            mosaic_masks = mosaic_masks[0].cat(mosaic_masks)\n            results['gt_masks'] = mosaic_masks[inside_inds]\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(img_scale={self.img_scale}, '\n        repr_str += f'center_ratio_range={self.center_ratio_range}, '\n        repr_str += f'pad_val={self.pad_val}, '\n        repr_str += f'prob={self.prob}, '\n        repr_str += f'max_cached_images={self.max_cached_images}, '\n        repr_str += f'random_pop={self.random_pop})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass CachedMixUp(BaseTransform):\n    \"\"\"Cached mixup data augmentation.\n\n    .. code:: text\n\n                         mixup transform\n                +------------------------------+\n                | mixup image   |              |\n                |      +--------|--------+     |\n                |      |        |        |     |\n                |---------------+        |     |\n                |      |                 |     |\n                |      |      image      |     |\n                |      |                 |     |\n                |      |                 |     |\n                |      |-----------------+     |\n                |             pad              |\n                +------------------------------+\n\n     The cached mixup transform steps are as follows:\n\n        1. Append the results from the last transform into the cache.\n        2. Another random image is picked from the cache and embedded in\n           the top left patch(after padding and resizing)\n        3. The target of mixup transform is the weighted average of mixup\n           image and origin image.\n\n    Required Keys:\n\n    - img\n    - gt_bboxes (np.float32) (optional)\n    - gt_bboxes_labels (np.int64) (optional)\n    - gt_ignore_flags (bool) (optional)\n    - mix_results (List[dict])\n\n\n    Modified Keys:\n\n    - img\n    - img_shape\n    - gt_bboxes (optional)\n    - gt_bboxes_labels (optional)\n    - gt_ignore_flags (optional)\n\n\n    Args:\n        img_scale (Sequence[int]): Image output size after mixup pipeline.\n            The shape order should be (width, height). Defaults to (640, 640).\n        ratio_range (Sequence[float]): Scale ratio of mixup image.\n            Defaults to (0.5, 1.5).\n        flip_ratio (float): Horizontal flip ratio of mixup image.\n            Defaults to 0.5.\n        pad_val (int): Pad value. Defaults to 114.\n        max_iters (int): The maximum number of iterations. If the number of\n            iterations is greater than `max_iters`, but gt_bbox is still\n            empty, then the iteration is terminated. Defaults to 15.\n        bbox_clip_border (bool, optional): Whether to clip the objects outside\n            the border of the image. In some dataset like MOT17, the gt bboxes\n            are allowed to cross the border of images. Therefore, we don't\n            need to clip the gt bboxes in these cases. Defaults to True.\n        max_cached_images (int): The maximum length of the cache. The larger\n            the cache, the stronger the randomness of this transform. As a\n            rule of thumb, providing 10 caches for each image suffices for\n            randomness. Defaults to 20.\n        random_pop (bool): Whether to randomly pop a result from the cache\n            when the cache is full. If set to False, use FIFO popping method.\n            Defaults to True.\n        prob (float): Probability of applying this transformation.\n            Defaults to 1.0.\n    \"\"\"\n\n    def __init__(self,\n                 img_scale: Tuple[int, int] = (640, 640),\n                 ratio_range: Tuple[float, float] = (0.5, 1.5),\n                 flip_ratio: float = 0.5,\n                 pad_val: float = 114.0,\n                 max_iters: int = 15,\n                 bbox_clip_border: bool = True,\n                 max_cached_images: int = 20,\n                 random_pop: bool = True,\n                 prob: float = 1.0) -> None:\n        assert isinstance(img_scale, tuple)\n        assert max_cached_images >= 2, 'The length of cache must >= 2, ' \\\n                                       f'but got {max_cached_images}.'\n        assert 0 <= prob <= 1.0, 'The probability should be in range [0,1]. ' \\\n                                 f'got {prob}.'\n        self.dynamic_scale = img_scale\n        self.ratio_range = ratio_range\n        self.flip_ratio = flip_ratio\n        self.pad_val = pad_val\n        self.max_iters = max_iters\n        self.bbox_clip_border = bbox_clip_border\n        self.results_cache = []\n\n        self.max_cached_images = max_cached_images\n        self.random_pop = random_pop\n        self.prob = prob\n\n    @cache_randomness\n    def get_indexes(self, cache: list) -> int:\n        \"\"\"Call function to collect indexes.\n\n        Args:\n            cache (list): The result cache.\n\n        Returns:\n            int: index.\n        \"\"\"\n\n        for i in range(self.max_iters):\n            index = random.randint(0, len(cache) - 1)\n            gt_bboxes_i = cache[index]['gt_bboxes']\n            if len(gt_bboxes_i) != 0:\n                break\n        return index\n\n    @autocast_box_type()\n    def transform(self, results: dict) -> dict:\n        \"\"\"MixUp transform function.\n\n        Args:\n            results (dict): Result dict.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        # cache and pop images\n        self.results_cache.append(copy.deepcopy(results))\n        if len(self.results_cache) > self.max_cached_images:\n            if self.random_pop:\n                index = random.randint(0, len(self.results_cache) - 1)\n            else:\n                index = 0\n            self.results_cache.pop(index)\n\n        if len(self.results_cache) <= 1:\n            return results\n\n        if random.uniform(0, 1) > self.prob:\n            return results\n\n        index = self.get_indexes(self.results_cache)\n        retrieve_results = copy.deepcopy(self.results_cache[index])\n\n        # TODO: refactor mixup to reuse these code.\n        if retrieve_results['gt_bboxes'].shape[0] == 0:\n            # empty bbox\n            return results\n\n        retrieve_img = retrieve_results['img']\n        with_mask = True if 'gt_masks' in results else False\n\n        jit_factor = random.uniform(*self.ratio_range)\n        is_flip = random.uniform(0, 1) > self.flip_ratio\n\n        if len(retrieve_img.shape) == 3:\n            out_img = np.ones(\n                (self.dynamic_scale[1], self.dynamic_scale[0], 3),\n                dtype=retrieve_img.dtype) * self.pad_val\n        else:\n            out_img = np.ones(\n                self.dynamic_scale[::-1],\n                dtype=retrieve_img.dtype) * self.pad_val\n\n        # 1. keep_ratio resize\n        scale_ratio = min(self.dynamic_scale[1] / retrieve_img.shape[0],\n                          self.dynamic_scale[0] / retrieve_img.shape[1])\n        retrieve_img = mmcv.imresize(\n            retrieve_img, (int(retrieve_img.shape[1] * scale_ratio),\n                           int(retrieve_img.shape[0] * scale_ratio)))\n\n        # 2. paste\n        out_img[:retrieve_img.shape[0], :retrieve_img.shape[1]] = retrieve_img\n\n        # 3. scale jit\n        scale_ratio *= jit_factor\n        out_img = mmcv.imresize(out_img, (int(out_img.shape[1] * jit_factor),\n                                          int(out_img.shape[0] * jit_factor)))\n\n        # 4. flip\n        if is_flip:\n            out_img = out_img[:, ::-1, :]\n\n        # 5. random crop\n        ori_img = results['img']\n        origin_h, origin_w = out_img.shape[:2]\n        target_h, target_w = ori_img.shape[:2]\n        padded_img = np.ones((max(origin_h, target_h), max(\n            origin_w, target_w), 3)) * self.pad_val\n        padded_img = padded_img.astype(np.uint8)\n        padded_img[:origin_h, :origin_w] = out_img\n\n        x_offset, y_offset = 0, 0\n        if padded_img.shape[0] > target_h:\n            y_offset = random.randint(0, padded_img.shape[0] - target_h)\n        if padded_img.shape[1] > target_w:\n            x_offset = random.randint(0, padded_img.shape[1] - target_w)\n        padded_cropped_img = padded_img[y_offset:y_offset + target_h,\n                                        x_offset:x_offset + target_w]\n\n        # 6. adjust bbox\n        retrieve_gt_bboxes = retrieve_results['gt_bboxes']\n        retrieve_gt_bboxes.rescale_([scale_ratio, scale_ratio])\n        if with_mask:\n            retrieve_gt_masks = retrieve_results['gt_masks'].rescale(\n                scale_ratio)\n\n        if self.bbox_clip_border:\n            retrieve_gt_bboxes.clip_([origin_h, origin_w])\n\n        if is_flip:\n            retrieve_gt_bboxes.flip_([origin_h, origin_w],\n                                     direction='horizontal')\n            if with_mask:\n                retrieve_gt_masks = retrieve_gt_masks.flip()\n\n        # 7. filter\n        cp_retrieve_gt_bboxes = retrieve_gt_bboxes.clone()\n        cp_retrieve_gt_bboxes.translate_([-x_offset, -y_offset])\n        if with_mask:\n            retrieve_gt_masks = retrieve_gt_masks.translate(\n                out_shape=(target_h, target_w),\n                offset=-x_offset,\n                direction='horizontal')\n            retrieve_gt_masks = retrieve_gt_masks.translate(\n                out_shape=(target_h, target_w),\n                offset=-y_offset,\n                direction='vertical')\n\n        if self.bbox_clip_border:\n            cp_retrieve_gt_bboxes.clip_([target_h, target_w])\n\n        # 8. mix up\n        ori_img = ori_img.astype(np.float32)\n        mixup_img = 0.5 * ori_img + 0.5 * padded_cropped_img.astype(np.float32)\n\n        retrieve_gt_bboxes_labels = retrieve_results['gt_bboxes_labels']\n        retrieve_gt_ignore_flags = retrieve_results['gt_ignore_flags']\n\n        mixup_gt_bboxes = cp_retrieve_gt_bboxes.cat(\n            (results['gt_bboxes'], cp_retrieve_gt_bboxes), dim=0)\n        mixup_gt_bboxes_labels = np.concatenate(\n            (results['gt_bboxes_labels'], retrieve_gt_bboxes_labels), axis=0)\n        mixup_gt_ignore_flags = np.concatenate(\n            (results['gt_ignore_flags'], retrieve_gt_ignore_flags), axis=0)\n        if with_mask:\n            mixup_gt_masks = retrieve_gt_masks.cat(\n                [results['gt_masks'], retrieve_gt_masks])\n\n        # remove outside bbox\n        inside_inds = mixup_gt_bboxes.is_inside([target_h, target_w]).numpy()\n        mixup_gt_bboxes = mixup_gt_bboxes[inside_inds]\n        mixup_gt_bboxes_labels = mixup_gt_bboxes_labels[inside_inds]\n        mixup_gt_ignore_flags = mixup_gt_ignore_flags[inside_inds]\n        if with_mask:\n            mixup_gt_masks = mixup_gt_masks[inside_inds]\n\n        results['img'] = mixup_img.astype(np.uint8)\n        results['img_shape'] = mixup_img.shape[:2]\n        results['gt_bboxes'] = mixup_gt_bboxes\n        results['gt_bboxes_labels'] = mixup_gt_bboxes_labels\n        results['gt_ignore_flags'] = mixup_gt_ignore_flags\n        if with_mask:\n            results['gt_masks'] = mixup_gt_masks\n        return results\n\n    def __repr__(self):\n        repr_str = self.__class__.__name__\n        repr_str += f'(dynamic_scale={self.dynamic_scale}, '\n        repr_str += f'ratio_range={self.ratio_range}, '\n        repr_str += f'flip_ratio={self.flip_ratio}, '\n        repr_str += f'pad_val={self.pad_val}, '\n        repr_str += f'max_iters={self.max_iters}, '\n        repr_str += f'bbox_clip_border={self.bbox_clip_border}, '\n        repr_str += f'max_cached_images={self.max_cached_images}, '\n        repr_str += f'random_pop={self.random_pop}, '\n        repr_str += f'prob={self.prob})'\n        return repr_str\n"
  },
  {
    "path": "mmdet/datasets/transforms/wrappers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Callable, Dict, List, Optional, Union\n\nimport numpy as np\nfrom mmcv.transforms import BaseTransform, Compose\nfrom mmcv.transforms.utils import cache_random_params, cache_randomness\n\nfrom mmdet.registry import TRANSFORMS\n\n\n@TRANSFORMS.register_module()\nclass MultiBranch(BaseTransform):\n    r\"\"\"Multiple branch pipeline wrapper.\n\n    Generate multiple data-augmented versions of the same image.\n    `MultiBranch` needs to specify the branch names of all\n    pipelines of the dataset, perform corresponding data augmentation\n    for the current branch, and return None for other branches,\n    which ensures the consistency of return format across\n    different samples.\n\n    Args:\n        branch_field (list): List of branch names.\n        branch_pipelines (dict): Dict of different pipeline configs\n            to be composed.\n\n    Examples:\n        >>> branch_field = ['sup', 'unsup_teacher', 'unsup_student']\n        >>> sup_pipeline = [\n        >>>     dict(type='LoadImageFromFile'),\n        >>>     dict(type='LoadAnnotations', with_bbox=True),\n        >>>     dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n        >>>     dict(type='RandomFlip', prob=0.5),\n        >>>     dict(\n        >>>         type='MultiBranch',\n        >>>         branch_field=branch_field,\n        >>>         sup=dict(type='PackDetInputs'))\n        >>>     ]\n        >>> weak_pipeline = [\n        >>>     dict(type='LoadImageFromFile'),\n        >>>     dict(type='LoadAnnotations', with_bbox=True),\n        >>>     dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n        >>>     dict(type='RandomFlip', prob=0.0),\n        >>>     dict(\n        >>>         type='MultiBranch',\n        >>>         branch_field=branch_field,\n        >>>         sup=dict(type='PackDetInputs'))\n        >>>     ]\n        >>> strong_pipeline = [\n        >>>     dict(type='LoadImageFromFile'),\n        >>>     dict(type='LoadAnnotations', with_bbox=True),\n        >>>     dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n        >>>     dict(type='RandomFlip', prob=1.0),\n        >>>     dict(\n        >>>         type='MultiBranch',\n        >>>         branch_field=branch_field,\n        >>>         sup=dict(type='PackDetInputs'))\n        >>>     ]\n        >>> unsup_pipeline = [\n        >>>     dict(type='LoadImageFromFile'),\n        >>>     dict(type='LoadEmptyAnnotations'),\n        >>>     dict(\n        >>>         type='MultiBranch',\n        >>>         branch_field=branch_field,\n        >>>         unsup_teacher=weak_pipeline,\n        >>>         unsup_student=strong_pipeline)\n        >>>     ]\n        >>> from mmcv.transforms import Compose\n        >>> sup_branch = Compose(sup_pipeline)\n        >>> unsup_branch = Compose(unsup_pipeline)\n        >>> print(sup_branch)\n        >>> Compose(\n        >>>     LoadImageFromFile(ignore_empty=False, to_float32=False, color_type='color', imdecode_backend='cv2') # noqa\n        >>>     LoadAnnotations(with_bbox=True, with_label=True, with_mask=False, with_seg=False, poly2mask=True, imdecode_backend='cv2') # noqa\n        >>>     Resize(scale=(1333, 800), scale_factor=None, keep_ratio=True, clip_object_border=True), backend=cv2), interpolation=bilinear) # noqa\n        >>>     RandomFlip(prob=0.5, direction=horizontal)\n        >>>     MultiBranch(branch_pipelines=['sup'])\n        >>> )\n        >>> print(unsup_branch)\n        >>> Compose(\n        >>>     LoadImageFromFile(ignore_empty=False, to_float32=False, color_type='color', imdecode_backend='cv2') # noqa\n        >>>     LoadEmptyAnnotations(with_bbox=True, with_label=True, with_mask=False, with_seg=False, seg_ignore_label=255) # noqa\n        >>>     MultiBranch(branch_pipelines=['unsup_teacher', 'unsup_student'])\n        >>> )\n    \"\"\"\n\n    def __init__(self, branch_field: List[str],\n                 **branch_pipelines: dict) -> None:\n        self.branch_field = branch_field\n        self.branch_pipelines = {\n            branch: Compose(pipeline)\n            for branch, pipeline in branch_pipelines.items()\n        }\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Transform function to apply transforms sequentially.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict:\n\n            - 'inputs' (Dict[str, obj:`torch.Tensor`]): The forward data of\n                models from different branches.\n            - 'data_sample' (Dict[str,obj:`DetDataSample`]): The annotation\n                info of the sample from different branches.\n        \"\"\"\n\n        multi_results = {}\n        for branch in self.branch_field:\n            multi_results[branch] = {'inputs': None, 'data_samples': None}\n        for branch, pipeline in self.branch_pipelines.items():\n            branch_results = pipeline(copy.deepcopy(results))\n            # If one branch pipeline returns None,\n            # it will sample another data from dataset.\n            if branch_results is None:\n                return None\n            multi_results[branch] = branch_results\n\n        format_results = {}\n        for branch, results in multi_results.items():\n            for key in results.keys():\n                if format_results.get(key, None) is None:\n                    format_results[key] = {branch: results[key]}\n                else:\n                    format_results[key][branch] = results[key]\n        return format_results\n\n    def __repr__(self) -> str:\n        repr_str = self.__class__.__name__\n        repr_str += f'(branch_pipelines={list(self.branch_pipelines.keys())})'\n        return repr_str\n\n\n@TRANSFORMS.register_module()\nclass RandomOrder(Compose):\n    \"\"\"Shuffle the transform Sequence.\"\"\"\n\n    @cache_randomness\n    def _random_permutation(self):\n        return np.random.permutation(len(self.transforms))\n\n    def transform(self, results: Dict) -> Optional[Dict]:\n        \"\"\"Transform function to apply transforms in random order.\n\n        Args:\n            results (dict): A result dict contains the results to transform.\n\n        Returns:\n            dict or None: Transformed results.\n        \"\"\"\n        inds = self._random_permutation()\n        for idx in inds:\n            t = self.transforms[idx]\n            results = t(results)\n            if results is None:\n                return None\n        return results\n\n    def __repr__(self):\n        \"\"\"Compute the string representation.\"\"\"\n        format_string = self.__class__.__name__ + '('\n        for t in self.transforms:\n            format_string += f'{t.__class__.__name__}, '\n        format_string += ')'\n        return format_string\n\n\n@TRANSFORMS.register_module()\nclass ProposalBroadcaster(BaseTransform):\n    \"\"\"A transform wrapper to apply the wrapped transforms to process both\n    `gt_bboxes` and `proposals` without adding any codes. It will do the\n    following steps:\n\n        1. Scatter the broadcasting targets to a list of inputs of the wrapped\n           transforms. The type of the list should be list[dict, dict], which\n           the first is the original inputs, the second is the processing\n           results that `gt_bboxes` being rewritten by the `proposals`.\n        2. Apply ``self.transforms``, with same random parameters, which is\n           sharing with a context manager. The type of the outputs is a\n           list[dict, dict].\n        3. Gather the outputs, update the `proposals` in the first item of\n           the outputs with the `gt_bboxes` in the second .\n\n    Args:\n         transforms (list, optional): Sequence of transform\n            object or config dict to be wrapped. Defaults to [].\n\n    Note: The `TransformBroadcaster` in MMCV can achieve the same operation as\n          `ProposalBroadcaster`, but need to set more complex parameters.\n\n    Examples:\n        >>> pipeline = [\n        >>>     dict(type='LoadImageFromFile'),\n        >>>     dict(type='LoadProposals', num_max_proposals=2000),\n        >>>     dict(type='LoadAnnotations', with_bbox=True),\n        >>>     dict(\n        >>>         type='ProposalBroadcaster',\n        >>>         transforms=[\n        >>>             dict(type='Resize', scale=(1333, 800),\n        >>>                  keep_ratio=True),\n        >>>             dict(type='RandomFlip', prob=0.5),\n        >>>         ]),\n        >>>     dict(type='PackDetInputs')]\n    \"\"\"\n\n    def __init__(self, transforms: List[Union[dict, Callable]] = []) -> None:\n        self.transforms = Compose(transforms)\n\n    def transform(self, results: dict) -> dict:\n        \"\"\"Apply wrapped transform functions to process both `gt_bboxes` and\n        `proposals`.\n\n        Args:\n            results (dict): Result dict from loading pipeline.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        assert results.get('proposals', None) is not None, \\\n            '`proposals` should be in the results, please delete ' \\\n            '`ProposalBroadcaster` in your configs, or check whether ' \\\n            'you have load proposals successfully.'\n\n        inputs = self._process_input(results)\n        outputs = self._apply_transforms(inputs)\n        outputs = self._process_output(outputs)\n        return outputs\n\n    def _process_input(self, data: dict) -> list:\n        \"\"\"Scatter the broadcasting targets to a list of inputs of the wrapped\n        transforms.\n\n        Args:\n            data (dict): The original input data.\n\n        Returns:\n            list[dict]: A list of input data.\n        \"\"\"\n        cp_data = copy.deepcopy(data)\n        cp_data['gt_bboxes'] = cp_data['proposals']\n        scatters = [data, cp_data]\n        return scatters\n\n    def _apply_transforms(self, inputs: list) -> list:\n        \"\"\"Apply ``self.transforms``.\n\n        Args:\n            inputs (list[dict, dict]): list of input data.\n\n        Returns:\n            list[dict]: The output of the wrapped pipeline.\n        \"\"\"\n        assert len(inputs) == 2\n        ctx = cache_random_params\n        with ctx(self.transforms):\n            output_scatters = [self.transforms(_input) for _input in inputs]\n        return output_scatters\n\n    def _process_output(self, output_scatters: list) -> dict:\n        \"\"\"Gathering and renaming data items.\n\n        Args:\n            output_scatters (list[dict, dict]): The output of the wrapped\n                pipeline.\n\n        Returns:\n            dict: Updated result dict.\n        \"\"\"\n        assert isinstance(output_scatters, list) and \\\n               isinstance(output_scatters[0], dict) and \\\n               len(output_scatters) == 2\n        outputs = output_scatters[0]\n        outputs['proposals'] = output_scatters[1]['gt_bboxes']\n        return outputs\n"
  },
  {
    "path": "mmdet/datasets/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nfrom mmcv.transforms import LoadImageFromFile\n\nfrom mmdet.datasets.transforms import LoadAnnotations, LoadPanopticAnnotations\nfrom mmdet.registry import TRANSFORMS\n\n\ndef get_loading_pipeline(pipeline):\n    \"\"\"Only keep loading image and annotations related configuration.\n\n    Args:\n        pipeline (list[dict]): Data pipeline configs.\n\n    Returns:\n        list[dict]: The new pipeline list with only keep\n            loading image and annotations related configuration.\n\n    Examples:\n        >>> pipelines = [\n        ...    dict(type='LoadImageFromFile'),\n        ...    dict(type='LoadAnnotations', with_bbox=True),\n        ...    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),\n        ...    dict(type='RandomFlip', flip_ratio=0.5),\n        ...    dict(type='Normalize', **img_norm_cfg),\n        ...    dict(type='Pad', size_divisor=32),\n        ...    dict(type='DefaultFormatBundle'),\n        ...    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])\n        ...    ]\n        >>> expected_pipelines = [\n        ...    dict(type='LoadImageFromFile'),\n        ...    dict(type='LoadAnnotations', with_bbox=True)\n        ...    ]\n        >>> assert expected_pipelines ==\\\n        ...        get_loading_pipeline(pipelines)\n    \"\"\"\n    loading_pipeline_cfg = []\n    for cfg in pipeline:\n        obj_cls = TRANSFORMS.get(cfg['type'])\n        # TODO:use more elegant way to distinguish loading modules\n        if obj_cls is not None and obj_cls in (LoadImageFromFile,\n                                               LoadAnnotations,\n                                               LoadPanopticAnnotations):\n            loading_pipeline_cfg.append(cfg)\n    assert len(loading_pipeline_cfg) == 2, \\\n        'The data pipeline in your config file must include ' \\\n        'loading image and annotations related pipeline.'\n    return loading_pipeline_cfg\n"
  },
  {
    "path": "mmdet/datasets/v3det.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path\nfrom typing import Optional\n\nimport mmengine\n\nfrom mmdet.registry import DATASETS\nfrom .coco import CocoDataset\n\n\n@DATASETS.register_module()\nclass V3DetDataset(CocoDataset):\n    \"\"\"Dataset for V3Det.\"\"\"\n\n    METAINFO = {\n        'classes': None,\n        'palette': None,\n    }\n\n    def __init__(\n            self,\n            *args,\n            metainfo: Optional[dict] = None,\n            data_root: str = '',\n            label_file='annotations/category_name_13204_v3det_2023_v1.txt',  # noqa\n            **kwargs) -> None:\n        class_names = tuple(\n            mmengine.list_from_file(os.path.join(data_root, label_file)))\n        if metainfo is None:\n            metainfo = {'classes': class_names}\n        super().__init__(\n            *args, data_root=data_root, metainfo=metainfo, **kwargs)\n"
  },
  {
    "path": "mmdet/datasets/voc.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import DATASETS\nfrom .xml_style import XMLDataset\n\n\n@DATASETS.register_module()\nclass VOCDataset(XMLDataset):\n    \"\"\"Dataset for PASCAL VOC.\"\"\"\n\n    METAINFO = {\n        'classes':\n        ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat',\n         'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person',\n         'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'),\n        # palette is a list of color tuples, which is used for visualization.\n        'palette': [(106, 0, 228), (119, 11, 32), (165, 42, 42), (0, 0, 192),\n                    (197, 226, 255), (0, 60, 100), (0, 0, 142), (255, 77, 255),\n                    (153, 69, 1), (120, 166, 157), (0, 182, 199),\n                    (0, 226, 252), (182, 182, 255), (0, 0, 230), (220, 20, 60),\n                    (163, 255, 0), (0, 82, 0), (3, 95, 161), (0, 80, 100),\n                    (183, 130, 88)]\n    }\n\n    def __init__(self, **kwargs):\n        super().__init__(**kwargs)\n        if 'VOC2007' in self.sub_data_root:\n            self._metainfo['dataset_type'] = 'VOC2007'\n        elif 'VOC2012' in self.sub_data_root:\n            self._metainfo['dataset_type'] = 'VOC2012'\n        else:\n            self._metainfo['dataset_type'] = None\n"
  },
  {
    "path": "mmdet/datasets/wider_face.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport xml.etree.ElementTree as ET\n\nfrom mmengine.dist import is_main_process\nfrom mmengine.fileio import get_local_path, list_from_file\nfrom mmengine.utils import ProgressBar\n\nfrom mmdet.registry import DATASETS\nfrom mmdet.utils.typing_utils import List, Union\nfrom .xml_style import XMLDataset\n\n\n@DATASETS.register_module()\nclass WIDERFaceDataset(XMLDataset):\n    \"\"\"Reader for the WIDER Face dataset in PASCAL VOC format.\n\n    Conversion scripts can be found in\n    https://github.com/sovrasov/wider-face-pascal-voc-annotations\n    \"\"\"\n    METAINFO = {'classes': ('face', ), 'palette': [(0, 255, 0)]}\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotation from XML style ann_file.\n\n        Returns:\n            list[dict]: Annotation info from XML file.\n        \"\"\"\n        assert self._metainfo.get('classes', None) is not None, \\\n            'classes in `XMLDataset` can not be None.'\n        self.cat2label = {\n            cat: i\n            for i, cat in enumerate(self._metainfo['classes'])\n        }\n\n        data_list = []\n        img_ids = list_from_file(self.ann_file, backend_args=self.backend_args)\n\n        # loading process takes around 10 mins\n        if is_main_process():\n            prog_bar = ProgressBar(len(img_ids))\n\n        for img_id in img_ids:\n            raw_img_info = {}\n            raw_img_info['img_id'] = img_id\n            raw_img_info['file_name'] = f'{img_id}.jpg'\n            parsed_data_info = self.parse_data_info(raw_img_info)\n            data_list.append(parsed_data_info)\n\n            if is_main_process():\n                prog_bar.update()\n        return data_list\n\n    def parse_data_info(self, img_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            img_info (dict): Raw image information, usually it includes\n                `img_id`, `file_name`, and `xml_path`.\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        data_info = {}\n        img_id = img_info['img_id']\n        xml_path = osp.join(self.data_prefix['img'], 'Annotations',\n                            f'{img_id}.xml')\n        data_info['img_id'] = img_id\n        data_info['xml_path'] = xml_path\n\n        # deal with xml file\n        with get_local_path(\n                xml_path, backend_args=self.backend_args) as local_path:\n            raw_ann_info = ET.parse(local_path)\n        root = raw_ann_info.getroot()\n        size = root.find('size')\n        width = int(size.find('width').text)\n        height = int(size.find('height').text)\n        folder = root.find('folder').text\n        img_path = osp.join(self.data_prefix['img'], folder,\n                            img_info['file_name'])\n        data_info['img_path'] = img_path\n\n        data_info['height'] = height\n        data_info['width'] = width\n\n        # Coordinates are in range [0, width - 1 or height - 1]\n        data_info['instances'] = self._parse_instance_info(\n            raw_ann_info, minus_one=False)\n        return data_info\n"
  },
  {
    "path": "mmdet/datasets/xml_style.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport xml.etree.ElementTree as ET\nfrom typing import List, Optional, Union\n\nimport mmcv\nfrom mmengine.fileio import get, get_local_path, list_from_file\n\nfrom mmdet.registry import DATASETS\nfrom .base_det_dataset import BaseDetDataset\n\n\n@DATASETS.register_module()\nclass XMLDataset(BaseDetDataset):\n    \"\"\"XML dataset for detection.\n\n    Args:\n        img_subdir (str): Subdir where images are stored. Default: JPEGImages.\n        ann_subdir (str): Subdir where annotations are. Default: Annotations.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 img_subdir: str = 'JPEGImages',\n                 ann_subdir: str = 'Annotations',\n                 **kwargs) -> None:\n        self.img_subdir = img_subdir\n        self.ann_subdir = ann_subdir\n        super().__init__(**kwargs)\n\n    @property\n    def sub_data_root(self) -> str:\n        \"\"\"Return the sub data root.\"\"\"\n        return self.data_prefix.get('sub_data_root', '')\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotation from XML style ann_file.\n\n        Returns:\n            list[dict]: Annotation info from XML file.\n        \"\"\"\n        assert self._metainfo.get('classes', None) is not None, \\\n            '`classes` in `XMLDataset` can not be None.'\n        self.cat2label = {\n            cat: i\n            for i, cat in enumerate(self._metainfo['classes'])\n        }\n\n        data_list = []\n        img_ids = list_from_file(self.ann_file, backend_args=self.backend_args)\n        for img_id in img_ids:\n            file_name = osp.join(self.img_subdir, f'{img_id}.jpg')\n            xml_path = osp.join(self.sub_data_root, self.ann_subdir,\n                                f'{img_id}.xml')\n\n            raw_img_info = {}\n            raw_img_info['img_id'] = img_id\n            raw_img_info['file_name'] = file_name\n            raw_img_info['xml_path'] = xml_path\n\n            parsed_data_info = self.parse_data_info(raw_img_info)\n            data_list.append(parsed_data_info)\n        return data_list\n\n    @property\n    def bbox_min_size(self) -> Optional[int]:\n        \"\"\"Return the minimum size of bounding boxes in the images.\"\"\"\n        if self.filter_cfg is not None:\n            return self.filter_cfg.get('bbox_min_size', None)\n        else:\n            return None\n\n    def parse_data_info(self, img_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            img_info (dict): Raw image information, usually it includes\n                `img_id`, `file_name`, and `xml_path`.\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        data_info = {}\n        img_path = osp.join(self.sub_data_root, img_info['file_name'])\n        data_info['img_path'] = img_path\n        data_info['img_id'] = img_info['img_id']\n        data_info['xml_path'] = img_info['xml_path']\n\n        # deal with xml file\n        with get_local_path(\n                img_info['xml_path'],\n                backend_args=self.backend_args) as local_path:\n            raw_ann_info = ET.parse(local_path)\n        root = raw_ann_info.getroot()\n        size = root.find('size')\n        if size is not None:\n            width = int(size.find('width').text)\n            height = int(size.find('height').text)\n        else:\n            img_bytes = get(img_path, backend_args=self.backend_args)\n            img = mmcv.imfrombytes(img_bytes, backend='cv2')\n            height, width = img.shape[:2]\n            del img, img_bytes\n\n        data_info['height'] = height\n        data_info['width'] = width\n\n        data_info['instances'] = self._parse_instance_info(\n            raw_ann_info, minus_one=True)\n\n        return data_info\n\n    def _parse_instance_info(self,\n                             raw_ann_info: ET,\n                             minus_one: bool = True) -> List[dict]:\n        \"\"\"parse instance information.\n\n        Args:\n            raw_ann_info (ElementTree): ElementTree object.\n            minus_one (bool): Whether to subtract 1 from the coordinates.\n                Defaults to True.\n\n        Returns:\n            List[dict]: List of instances.\n        \"\"\"\n        instances = []\n        for obj in raw_ann_info.findall('object'):\n            instance = {}\n            name = obj.find('name').text\n            if name not in self._metainfo['classes']:\n                continue\n            difficult = obj.find('difficult')\n            difficult = 0 if difficult is None else int(difficult.text)\n            bnd_box = obj.find('bndbox')\n            bbox = [\n                int(float(bnd_box.find('xmin').text)),\n                int(float(bnd_box.find('ymin').text)),\n                int(float(bnd_box.find('xmax').text)),\n                int(float(bnd_box.find('ymax').text))\n            ]\n\n            # VOC needs to subtract 1 from the coordinates\n            if minus_one:\n                bbox = [x - 1 for x in bbox]\n\n            ignore = False\n            if self.bbox_min_size is not None:\n                assert not self.test_mode\n                w = bbox[2] - bbox[0]\n                h = bbox[3] - bbox[1]\n                if w < self.bbox_min_size or h < self.bbox_min_size:\n                    ignore = True\n            if difficult or ignore:\n                instance['ignore_flag'] = 1\n            else:\n                instance['ignore_flag'] = 0\n            instance['bbox'] = bbox\n            instance['bbox_label'] = self.cat2label[name]\n            instances.append(instance)\n        return instances\n\n    def filter_data(self) -> List[dict]:\n        \"\"\"Filter annotations according to filter_cfg.\n\n        Returns:\n            List[dict]: Filtered results.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        filter_empty_gt = self.filter_cfg.get('filter_empty_gt', False) \\\n            if self.filter_cfg is not None else False\n        min_size = self.filter_cfg.get('min_size', 0) \\\n            if self.filter_cfg is not None else 0\n\n        valid_data_infos = []\n        for i, data_info in enumerate(self.data_list):\n            width = data_info['width']\n            height = data_info['height']\n            if filter_empty_gt and len(data_info['instances']) == 0:\n                continue\n            if min(width, height) >= min_size:\n                valid_data_infos.append(data_info)\n\n        return valid_data_infos\n"
  },
  {
    "path": "mmdet/datasets/youtube_vis_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import DATASETS\nfrom .base_video_dataset import BaseVideoDataset\n\n\n@DATASETS.register_module()\nclass YouTubeVISDataset(BaseVideoDataset):\n    \"\"\"YouTube VIS dataset for video instance segmentation.\n\n    Args:\n        dataset_version (str): Select dataset year version.\n    \"\"\"\n\n    def __init__(self, dataset_version: str, *args, **kwargs):\n        self.set_dataset_classes(dataset_version)\n        super().__init__(*args, **kwargs)\n\n    @classmethod\n    def set_dataset_classes(cls, dataset_version: str) -> None:\n        \"\"\"Pass the category of the corresponding year to metainfo.\n\n        Args:\n            dataset_version (str): Select dataset year version.\n        \"\"\"\n        classes_2019_version = ('person', 'giant_panda', 'lizard', 'parrot',\n                                'skateboard', 'sedan', 'ape', 'dog', 'snake',\n                                'monkey', 'hand', 'rabbit', 'duck', 'cat',\n                                'cow', 'fish', 'train', 'horse', 'turtle',\n                                'bear', 'motorbike', 'giraffe', 'leopard',\n                                'fox', 'deer', 'owl', 'surfboard', 'airplane',\n                                'truck', 'zebra', 'tiger', 'elephant',\n                                'snowboard', 'boat', 'shark', 'mouse', 'frog',\n                                'eagle', 'earless_seal', 'tennis_racket')\n\n        classes_2021_version = ('airplane', 'bear', 'bird', 'boat', 'car',\n                                'cat', 'cow', 'deer', 'dog', 'duck',\n                                'earless_seal', 'elephant', 'fish',\n                                'flying_disc', 'fox', 'frog', 'giant_panda',\n                                'giraffe', 'horse', 'leopard', 'lizard',\n                                'monkey', 'motorbike', 'mouse', 'parrot',\n                                'person', 'rabbit', 'shark', 'skateboard',\n                                'snake', 'snowboard', 'squirrel', 'surfboard',\n                                'tennis_racket', 'tiger', 'train', 'truck',\n                                'turtle', 'whale', 'zebra')\n\n        if dataset_version == '2019':\n            cls.METAINFO = dict(classes=classes_2019_version)\n        elif dataset_version == '2021':\n            cls.METAINFO = dict(classes=classes_2021_version)\n        else:\n            raise NotImplementedError('Not supported YouTubeVIS dataset'\n                                      f'version: {dataset_version}')\n"
  },
  {
    "path": "mmdet/engine/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .hooks import *  # noqa: F401, F403\nfrom .optimizers import *  # noqa: F401, F403\nfrom .runner import *  # noqa: F401, F403\nfrom .schedulers import *  # noqa: F401, F403\n"
  },
  {
    "path": "mmdet/engine/hooks/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .checkloss_hook import CheckInvalidLossHook\nfrom .mean_teacher_hook import MeanTeacherHook\nfrom .memory_profiler_hook import MemoryProfilerHook\nfrom .num_class_check_hook import NumClassCheckHook\nfrom .pipeline_switch_hook import PipelineSwitchHook\nfrom .set_epoch_info_hook import SetEpochInfoHook\nfrom .sync_norm_hook import SyncNormHook\nfrom .utils import trigger_visualization_hook\nfrom .visualization_hook import (DetVisualizationHook,\n                                 GroundingVisualizationHook,\n                                 TrackVisualizationHook)\nfrom .yolox_mode_switch_hook import YOLOXModeSwitchHook\n\n__all__ = [\n    'YOLOXModeSwitchHook', 'SyncNormHook', 'CheckInvalidLossHook',\n    'SetEpochInfoHook', 'MemoryProfilerHook', 'DetVisualizationHook',\n    'NumClassCheckHook', 'MeanTeacherHook', 'trigger_visualization_hook',\n    'PipelineSwitchHook', 'TrackVisualizationHook',\n    'GroundingVisualizationHook'\n]\n"
  },
  {
    "path": "mmdet/engine/hooks/checkloss_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom mmengine.hooks import Hook\nfrom mmengine.runner import Runner\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass CheckInvalidLossHook(Hook):\n    \"\"\"Check invalid loss hook.\n\n    This hook will regularly check whether the loss is valid\n    during training.\n\n    Args:\n        interval (int): Checking interval (every k iterations).\n            Default: 50.\n    \"\"\"\n\n    def __init__(self, interval: int = 50) -> None:\n        self.interval = interval\n\n    def after_train_iter(self,\n                         runner: Runner,\n                         batch_idx: int,\n                         data_batch: Optional[dict] = None,\n                         outputs: Optional[dict] = None) -> None:\n        \"\"\"Regularly check whether the loss is valid every n iterations.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training process.\n            batch_idx (int): The index of the current batch in the train loop.\n            data_batch (dict, Optional): Data from dataloader.\n                Defaults to None.\n            outputs (dict, Optional): Outputs from model. Defaults to None.\n        \"\"\"\n        if self.every_n_train_iters(runner, self.interval):\n            assert torch.isfinite(outputs['loss']), \\\n                runner.logger.info('loss become infinite or NaN!')\n"
  },
  {
    "path": "mmdet/engine/hooks/mean_teacher_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch.nn as nn\nfrom mmengine.hooks import Hook\nfrom mmengine.model import is_model_wrapper\nfrom mmengine.runner import Runner\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass MeanTeacherHook(Hook):\n    \"\"\"Mean Teacher Hook.\n\n    Mean Teacher is an efficient semi-supervised learning method in\n    `Mean Teacher <https://arxiv.org/abs/1703.01780>`_.\n    This method requires two models with exactly the same structure,\n    as the student model and the teacher model, respectively.\n    The student model updates the parameters through gradient descent,\n    and the teacher model updates the parameters through\n    exponential moving average of the student model.\n    Compared with the student model, the teacher model\n    is smoother and accumulates more knowledge.\n\n    Args:\n        momentum (float): The momentum used for updating teacher's parameter.\n            Teacher's parameter are updated with the formula:\n           `teacher = (1-momentum) * teacher + momentum * student`.\n            Defaults to 0.001.\n        interval (int): Update teacher's parameter every interval iteration.\n            Defaults to 1.\n        skip_buffers (bool): Whether to skip the model buffers, such as\n            batchnorm running stats (running_mean, running_var), it does not\n            perform the ema operation. Default to True.\n    \"\"\"\n\n    def __init__(self,\n                 momentum: float = 0.001,\n                 interval: int = 1,\n                 skip_buffer=True) -> None:\n        assert 0 < momentum < 1\n        self.momentum = momentum\n        self.interval = interval\n        self.skip_buffers = skip_buffer\n\n    def before_train(self, runner: Runner) -> None:\n        \"\"\"To check that teacher model and student model exist.\"\"\"\n        model = runner.model\n        if is_model_wrapper(model):\n            model = model.module\n        assert hasattr(model, 'teacher')\n        assert hasattr(model, 'student')\n        # only do it at initial stage\n        if runner.iter == 0:\n            self.momentum_update(model, 1)\n\n    def after_train_iter(self,\n                         runner: Runner,\n                         batch_idx: int,\n                         data_batch: Optional[dict] = None,\n                         outputs: Optional[dict] = None) -> None:\n        \"\"\"Update teacher's parameter every self.interval iterations.\"\"\"\n        if (runner.iter + 1) % self.interval != 0:\n            return\n        model = runner.model\n        if is_model_wrapper(model):\n            model = model.module\n        self.momentum_update(model, self.momentum)\n\n    def momentum_update(self, model: nn.Module, momentum: float) -> None:\n        \"\"\"Compute the moving average of the parameters using exponential\n        moving average.\"\"\"\n        if self.skip_buffers:\n            for (src_name, src_parm), (dst_name, dst_parm) in zip(\n                    model.student.named_parameters(),\n                    model.teacher.named_parameters()):\n                dst_parm.data.mul_(1 - momentum).add_(\n                    src_parm.data, alpha=momentum)\n        else:\n            for (src_parm,\n                 dst_parm) in zip(model.student.state_dict().values(),\n                                  model.teacher.state_dict().values()):\n                # exclude num_tracking\n                if dst_parm.dtype.is_floating_point:\n                    dst_parm.data.mul_(1 - momentum).add_(\n                        src_parm.data, alpha=momentum)\n"
  },
  {
    "path": "mmdet/engine/hooks/memory_profiler_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Sequence\n\nfrom mmengine.hooks import Hook\nfrom mmengine.runner import Runner\n\nfrom mmdet.registry import HOOKS\nfrom mmdet.structures import DetDataSample\n\n\n@HOOKS.register_module()\nclass MemoryProfilerHook(Hook):\n    \"\"\"Memory profiler hook recording memory information including virtual\n    memory, swap memory, and the memory of the current process.\n\n    Args:\n        interval (int): Checking interval (every k iterations).\n            Default: 50.\n    \"\"\"\n\n    def __init__(self, interval: int = 50) -> None:\n        try:\n            from psutil import swap_memory, virtual_memory\n            self._swap_memory = swap_memory\n            self._virtual_memory = virtual_memory\n        except ImportError:\n            raise ImportError('psutil is not installed, please install it by: '\n                              'pip install psutil')\n\n        try:\n            from memory_profiler import memory_usage\n            self._memory_usage = memory_usage\n        except ImportError:\n            raise ImportError(\n                'memory_profiler is not installed, please install it by: '\n                'pip install memory_profiler')\n\n        self.interval = interval\n\n    def _record_memory_information(self, runner: Runner) -> None:\n        \"\"\"Regularly record memory information.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training or evaluation\n                process.\n        \"\"\"\n        # in Byte\n        virtual_memory = self._virtual_memory()\n        swap_memory = self._swap_memory()\n        # in MB\n        process_memory = self._memory_usage()[0]\n        factor = 1024 * 1024\n        runner.logger.info(\n            'Memory information '\n            'available_memory: '\n            f'{round(virtual_memory.available / factor)} MB, '\n            'used_memory: '\n            f'{round(virtual_memory.used / factor)} MB, '\n            f'memory_utilization: {virtual_memory.percent} %, '\n            'available_swap_memory: '\n            f'{round((swap_memory.total - swap_memory.used) / factor)}'\n            ' MB, '\n            f'used_swap_memory: {round(swap_memory.used / factor)} MB, '\n            f'swap_memory_utilization: {swap_memory.percent} %, '\n            'current_process_memory: '\n            f'{round(process_memory)} MB')\n\n    def after_train_iter(self,\n                         runner: Runner,\n                         batch_idx: int,\n                         data_batch: Optional[dict] = None,\n                         outputs: Optional[dict] = None) -> None:\n        \"\"\"Regularly record memory information.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training process.\n            batch_idx (int): The index of the current batch in the train loop.\n            data_batch (dict, optional): Data from dataloader.\n                Defaults to None.\n            outputs (dict, optional): Outputs from model. Defaults to None.\n        \"\"\"\n        if self.every_n_inner_iters(batch_idx, self.interval):\n            self._record_memory_information(runner)\n\n    def after_val_iter(\n            self,\n            runner: Runner,\n            batch_idx: int,\n            data_batch: Optional[dict] = None,\n            outputs: Optional[Sequence[DetDataSample]] = None) -> None:\n        \"\"\"Regularly record memory information.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the validation process.\n            batch_idx (int): The index of the current batch in the val loop.\n            data_batch (dict, optional): Data from dataloader.\n                Defaults to None.\n            outputs (Sequence[:obj:`DetDataSample`], optional):\n                Outputs from model. Defaults to None.\n        \"\"\"\n        if self.every_n_inner_iters(batch_idx, self.interval):\n            self._record_memory_information(runner)\n\n    def after_test_iter(\n            self,\n            runner: Runner,\n            batch_idx: int,\n            data_batch: Optional[dict] = None,\n            outputs: Optional[Sequence[DetDataSample]] = None) -> None:\n        \"\"\"Regularly record memory information.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the testing process.\n            batch_idx (int): The index of the current batch in the test loop.\n            data_batch (dict, optional): Data from dataloader.\n                Defaults to None.\n            outputs (Sequence[:obj:`DetDataSample`], optional):\n                Outputs from model. Defaults to None.\n        \"\"\"\n        if self.every_n_inner_iters(batch_idx, self.interval):\n            self._record_memory_information(runner)\n"
  },
  {
    "path": "mmdet/engine/hooks/num_class_check_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.cnn import VGG\nfrom mmengine.hooks import Hook\nfrom mmengine.runner import Runner\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass NumClassCheckHook(Hook):\n    \"\"\"Check whether the `num_classes` in head matches the length of `classes`\n    in `dataset.metainfo`.\"\"\"\n\n    def _check_head(self, runner: Runner, mode: str) -> None:\n        \"\"\"Check whether the `num_classes` in head matches the length of\n        `classes` in `dataset.metainfo`.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training or evaluation\n                process.\n        \"\"\"\n        assert mode in ['train', 'val']\n        model = runner.model\n        dataset = runner.train_dataloader.dataset if mode == 'train' else \\\n            runner.val_dataloader.dataset\n        if dataset.metainfo.get('classes', None) is None:\n            runner.logger.warning(\n                f'Please set `classes` '\n                f'in the {dataset.__class__.__name__} `metainfo` and'\n                f'check if it is consistent with the `num_classes` '\n                f'of head')\n        else:\n            classes = dataset.metainfo['classes']\n            assert type(classes) is not str, \\\n                (f'`classes` in {dataset.__class__.__name__}'\n                 f'should be a tuple of str.'\n                 f'Add comma if number of classes is 1 as '\n                 f'classes = ({classes},)')\n            from mmdet.models.roi_heads.mask_heads import FusedSemanticHead\n            for name, module in model.named_modules():\n                if hasattr(module, 'num_classes') and not name.endswith(\n                        'rpn_head') and not isinstance(\n                            module, (VGG, FusedSemanticHead)):\n                    assert module.num_classes == len(classes), \\\n                        (f'The `num_classes` ({module.num_classes}) in '\n                         f'{module.__class__.__name__} of '\n                         f'{model.__class__.__name__} does not matches '\n                         f'the length of `classes` '\n                         f'{len(classes)}) in '\n                         f'{dataset.__class__.__name__}')\n\n    def before_train_epoch(self, runner: Runner) -> None:\n        \"\"\"Check whether the training dataset is compatible with head.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training or evaluation\n                process.\n        \"\"\"\n        self._check_head(runner, 'train')\n\n    def before_val_epoch(self, runner: Runner) -> None:\n        \"\"\"Check whether the dataset in val epoch is compatible with head.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the training or evaluation\n                process.\n        \"\"\"\n        self._check_head(runner, 'val')\n"
  },
  {
    "path": "mmdet/engine/hooks/pipeline_switch_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.transforms import Compose\nfrom mmengine.hooks import Hook\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass PipelineSwitchHook(Hook):\n    \"\"\"Switch data pipeline at switch_epoch.\n\n    Args:\n        switch_epoch (int): switch pipeline at this epoch.\n        switch_pipeline (list[dict]): the pipeline to switch to.\n    \"\"\"\n\n    def __init__(self, switch_epoch, switch_pipeline):\n        self.switch_epoch = switch_epoch\n        self.switch_pipeline = switch_pipeline\n        self._restart_dataloader = False\n        self._has_switched = False\n\n    def before_train_epoch(self, runner):\n        \"\"\"switch pipeline.\"\"\"\n        epoch = runner.epoch\n        train_loader = runner.train_dataloader\n        if epoch >= self.switch_epoch and not self._has_switched:\n            runner.logger.info('Switch pipeline now!')\n            # The dataset pipeline cannot be updated when persistent_workers\n            # is True, so we need to force the dataloader's multi-process\n            # restart. This is a very hacky approach.\n            train_loader.dataset.pipeline = Compose(self.switch_pipeline)\n            if hasattr(train_loader, 'persistent_workers'\n                       ) and train_loader.persistent_workers is True:\n                train_loader._DataLoader__initialized = False\n                train_loader._iterator = None\n                self._restart_dataloader = True\n            self._has_switched = True\n        else:\n            # Once the restart is complete, we need to restore\n            # the initialization flag.\n            if self._restart_dataloader:\n                train_loader._DataLoader__initialized = True\n"
  },
  {
    "path": "mmdet/engine/hooks/set_epoch_info_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.hooks import Hook\nfrom mmengine.model.wrappers import is_model_wrapper\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass SetEpochInfoHook(Hook):\n    \"\"\"Set runner's epoch information to the model.\"\"\"\n\n    def before_train_epoch(self, runner):\n        epoch = runner.epoch\n        model = runner.model\n        if is_model_wrapper(model):\n            model = model.module\n        model.set_epoch(epoch)\n"
  },
  {
    "path": "mmdet/engine/hooks/sync_norm_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom collections import OrderedDict\n\nfrom mmengine.dist import get_dist_info\nfrom mmengine.hooks import Hook\nfrom torch import nn\n\nfrom mmdet.registry import HOOKS\nfrom mmdet.utils import all_reduce_dict\n\n\ndef get_norm_states(module: nn.Module) -> OrderedDict:\n    \"\"\"Get the state_dict of batch norms in the module.\"\"\"\n    async_norm_states = OrderedDict()\n    for name, child in module.named_modules():\n        if isinstance(child, nn.modules.batchnorm._NormBase):\n            for k, v in child.state_dict().items():\n                async_norm_states['.'.join([name, k])] = v\n    return async_norm_states\n\n\n@HOOKS.register_module()\nclass SyncNormHook(Hook):\n    \"\"\"Synchronize Norm states before validation, currently used in YOLOX.\"\"\"\n\n    def before_val_epoch(self, runner):\n        \"\"\"Synchronizing norm.\"\"\"\n        module = runner.model\n        _, world_size = get_dist_info()\n        if world_size == 1:\n            return\n        norm_states = get_norm_states(module)\n        if len(norm_states) == 0:\n            return\n        # TODO: use `all_reduce_dict` in mmengine\n        norm_states = all_reduce_dict(norm_states, op='mean')\n        module.load_state_dict(norm_states, strict=False)\n"
  },
  {
    "path": "mmdet/engine/hooks/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\ndef trigger_visualization_hook(cfg, args):\n    default_hooks = cfg.default_hooks\n    if 'visualization' in default_hooks:\n        visualization_hook = default_hooks['visualization']\n        # Turn on visualization\n        visualization_hook['draw'] = True\n        if args.show:\n            visualization_hook['show'] = True\n            visualization_hook['wait_time'] = args.wait_time\n        if args.show_dir:\n            visualization_hook['test_out_dir'] = args.show_dir\n    else:\n        raise RuntimeError(\n            'VisualizationHook must be included in default_hooks.'\n            'refer to usage '\n            '\"visualization=dict(type=\\'VisualizationHook\\')\"')\n\n    return cfg\n"
  },
  {
    "path": "mmdet/engine/hooks/visualization_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport warnings\nfrom typing import Optional, Sequence\n\nimport mmcv\nimport numpy as np\nfrom mmengine.fileio import get\nfrom mmengine.hooks import Hook\nfrom mmengine.runner import Runner\nfrom mmengine.utils import mkdir_or_exist\nfrom mmengine.visualization import Visualizer\n\nfrom mmdet.datasets.samplers import TrackImgSampler\nfrom mmdet.registry import HOOKS\nfrom mmdet.structures import DetDataSample, TrackDataSample\nfrom mmdet.structures.bbox import BaseBoxes\nfrom mmdet.visualization.palette import _get_adaptive_scales\n\n\n@HOOKS.register_module()\nclass DetVisualizationHook(Hook):\n    \"\"\"Detection Visualization Hook. Used to visualize validation and testing\n    process prediction results.\n\n    In the testing phase:\n\n    1. If ``show`` is True, it means that only the prediction results are\n        visualized without storing data, so ``vis_backends`` needs to\n        be excluded.\n    2. If ``test_out_dir`` is specified, it means that the prediction results\n        need to be saved to ``test_out_dir``. In order to avoid vis_backends\n        also storing data, so ``vis_backends`` needs to be excluded.\n    3. ``vis_backends`` takes effect if the user does not specify ``show``\n        and `test_out_dir``. You can set ``vis_backends`` to WandbVisBackend or\n        TensorboardVisBackend to store the prediction result in Wandb or\n        Tensorboard.\n\n    Args:\n        draw (bool): whether to draw prediction results. If it is False,\n            it means that no drawing will be done. Defaults to False.\n        interval (int): The interval of visualization. Defaults to 50.\n        score_thr (float): The threshold to visualize the bboxes\n            and masks. Defaults to 0.3.\n        show (bool): Whether to display the drawn image. Default to False.\n        wait_time (float): The interval of show (s). Defaults to 0.\n        test_out_dir (str, optional): directory where painted images\n            will be saved in testing process.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 draw: bool = False,\n                 interval: int = 50,\n                 score_thr: float = 0.3,\n                 show: bool = False,\n                 wait_time: float = 0.,\n                 test_out_dir: Optional[str] = None,\n                 backend_args: dict = None):\n        self._visualizer: Visualizer = Visualizer.get_current_instance()\n        self.interval = interval\n        self.score_thr = score_thr\n        self.show = show\n        if self.show:\n            # No need to think about vis backends.\n            self._visualizer._vis_backends = {}\n            warnings.warn('The show is True, it means that only '\n                          'the prediction results are visualized '\n                          'without storing data, so vis_backends '\n                          'needs to be excluded.')\n\n        self.wait_time = wait_time\n        self.backend_args = backend_args\n        self.draw = draw\n        self.test_out_dir = test_out_dir\n        self._test_index = 0\n\n    def after_val_iter(self, runner: Runner, batch_idx: int, data_batch: dict,\n                       outputs: Sequence[DetDataSample]) -> None:\n        \"\"\"Run after every ``self.interval`` validation iterations.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the validation process.\n            batch_idx (int): The index of the current batch in the val loop.\n            data_batch (dict): Data from dataloader.\n            outputs (Sequence[:obj:`DetDataSample`]]): A batch of data samples\n                that contain annotations and predictions.\n        \"\"\"\n        if self.draw is False:\n            return\n\n        # There is no guarantee that the same batch of images\n        # is visualized for each evaluation.\n        total_curr_iter = runner.iter + batch_idx\n\n        # Visualize only the first data\n        img_path = outputs[0].img_path\n        img_bytes = get(img_path, backend_args=self.backend_args)\n        img = mmcv.imfrombytes(img_bytes, channel_order='rgb')\n\n        if total_curr_iter % self.interval == 0:\n            self._visualizer.add_datasample(\n                osp.basename(img_path) if self.show else 'val_img',\n                img,\n                data_sample=outputs[0],\n                show=self.show,\n                wait_time=self.wait_time,\n                pred_score_thr=self.score_thr,\n                step=total_curr_iter)\n\n    def after_test_iter(self, runner: Runner, batch_idx: int, data_batch: dict,\n                        outputs: Sequence[DetDataSample]) -> None:\n        \"\"\"Run after every testing iterations.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the testing process.\n            batch_idx (int): The index of the current batch in the val loop.\n            data_batch (dict): Data from dataloader.\n            outputs (Sequence[:obj:`DetDataSample`]): A batch of data samples\n                that contain annotations and predictions.\n        \"\"\"\n        if self.draw is False:\n            return\n\n        if self.test_out_dir is not None:\n            self.test_out_dir = osp.join(runner.work_dir, runner.timestamp,\n                                         self.test_out_dir)\n            mkdir_or_exist(self.test_out_dir)\n\n        for data_sample in outputs:\n            self._test_index += 1\n\n            img_path = data_sample.img_path\n            img_bytes = get(img_path, backend_args=self.backend_args)\n            img = mmcv.imfrombytes(img_bytes, channel_order='rgb')\n\n            out_file = None\n            if self.test_out_dir is not None:\n                out_file = osp.basename(img_path)\n                out_file = osp.join(self.test_out_dir, out_file)\n\n            self._visualizer.add_datasample(\n                osp.basename(img_path) if self.show else 'test_img',\n                img,\n                data_sample=data_sample,\n                show=self.show,\n                wait_time=self.wait_time,\n                pred_score_thr=self.score_thr,\n                out_file=out_file,\n                step=self._test_index)\n\n\n@HOOKS.register_module()\nclass TrackVisualizationHook(Hook):\n    \"\"\"Tracking Visualization Hook. Used to visualize validation and testing\n    process prediction results.\n\n    In the testing phase:\n\n    1. If ``show`` is True, it means that only the prediction results are\n        visualized without storing data, so ``vis_backends`` needs to\n        be excluded.\n    2. If ``test_out_dir`` is specified, it means that the prediction results\n        need to be saved to ``test_out_dir``. In order to avoid vis_backends\n        also storing data, so ``vis_backends`` needs to be excluded.\n    3. ``vis_backends`` takes effect if the user does not specify ``show``\n        and `test_out_dir``. You can set ``vis_backends`` to WandbVisBackend or\n        TensorboardVisBackend to store the prediction result in Wandb or\n        Tensorboard.\n\n    Args:\n        draw (bool): whether to draw prediction results. If it is False,\n            it means that no drawing will be done. Defaults to False.\n        frame_interval (int): The interval of visualization. Defaults to 30.\n        score_thr (float): The threshold to visualize the bboxes\n            and masks. Defaults to 0.3.\n        show (bool): Whether to display the drawn image. Default to False.\n        wait_time (float): The interval of show (s). Defaults to 0.\n        test_out_dir (str, optional): directory where painted images\n            will be saved in testing process.\n        backend_args (dict): Arguments to instantiate a file client.\n            Defaults to ``None``.\n    \"\"\"\n\n    def __init__(self,\n                 draw: bool = False,\n                 frame_interval: int = 30,\n                 score_thr: float = 0.3,\n                 show: bool = False,\n                 wait_time: float = 0.,\n                 test_out_dir: Optional[str] = None,\n                 backend_args: dict = None) -> None:\n        self._visualizer: Visualizer = Visualizer.get_current_instance()\n        self.frame_interval = frame_interval\n        self.score_thr = score_thr\n        self.show = show\n        if self.show:\n            # No need to think about vis backends.\n            self._visualizer._vis_backends = {}\n            warnings.warn('The show is True, it means that only '\n                          'the prediction results are visualized '\n                          'without storing data, so vis_backends '\n                          'needs to be excluded.')\n\n        self.wait_time = wait_time\n        self.backend_args = backend_args\n        self.draw = draw\n        self.test_out_dir = test_out_dir\n        self.image_idx = 0\n\n    def after_val_iter(self, runner: Runner, batch_idx: int, data_batch: dict,\n                       outputs: Sequence[TrackDataSample]) -> None:\n        \"\"\"Run after every ``self.interval`` validation iteration.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the validation process.\n            batch_idx (int): The index of the current batch in the val loop.\n            data_batch (dict): Data from dataloader.\n            outputs (Sequence[:obj:`TrackDataSample`]): Outputs from model.\n        \"\"\"\n        if self.draw is False:\n            return\n\n        assert len(outputs) == 1, \\\n            'only batch_size=1 is supported while validating.'\n\n        sampler = runner.val_dataloader.sampler\n        if isinstance(sampler, TrackImgSampler):\n            if self.every_n_inner_iters(batch_idx, self.frame_interval):\n                total_curr_iter = runner.iter + batch_idx\n                track_data_sample = outputs[0]\n                self.visualize_single_image(track_data_sample[0],\n                                            total_curr_iter)\n        else:\n            # video visualization DefaultSampler\n            if self.every_n_inner_iters(batch_idx, 1):\n                track_data_sample = outputs[0]\n                video_length = len(track_data_sample)\n\n                for frame_id in range(video_length):\n                    if frame_id % self.frame_interval == 0:\n                        total_curr_iter = runner.iter + self.image_idx + \\\n                                          frame_id\n                        img_data_sample = track_data_sample[frame_id]\n                        self.visualize_single_image(img_data_sample,\n                                                    total_curr_iter)\n                self.image_idx = self.image_idx + video_length\n\n    def after_test_iter(self, runner: Runner, batch_idx: int, data_batch: dict,\n                        outputs: Sequence[TrackDataSample]) -> None:\n        \"\"\"Run after every testing iteration.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the testing process.\n            batch_idx (int): The index of the current batch in the test loop.\n            data_batch (dict): Data from dataloader.\n            outputs (Sequence[:obj:`TrackDataSample`]): Outputs from model.\n        \"\"\"\n        if self.draw is False:\n            return\n\n        assert len(outputs) == 1, \\\n            'only batch_size=1 is supported while testing.'\n\n        if self.test_out_dir is not None:\n            self.test_out_dir = osp.join(runner.work_dir, runner.timestamp,\n                                         self.test_out_dir)\n            mkdir_or_exist(self.test_out_dir)\n\n        sampler = runner.test_dataloader.sampler\n        if isinstance(sampler, TrackImgSampler):\n            if self.every_n_inner_iters(batch_idx, self.frame_interval):\n                track_data_sample = outputs[0]\n                self.visualize_single_image(track_data_sample[0], batch_idx)\n        else:\n            # video visualization DefaultSampler\n            if self.every_n_inner_iters(batch_idx, 1):\n                track_data_sample = outputs[0]\n                video_length = len(track_data_sample)\n\n                for frame_id in range(video_length):\n                    if frame_id % self.frame_interval == 0:\n                        img_data_sample = track_data_sample[frame_id]\n                        self.visualize_single_image(img_data_sample,\n                                                    self.image_idx + frame_id)\n                self.image_idx = self.image_idx + video_length\n\n    def visualize_single_image(self, img_data_sample: DetDataSample,\n                               step: int) -> None:\n        \"\"\"\n        Args:\n            img_data_sample (DetDataSample): single image output.\n            step (int): The index of the current image.\n        \"\"\"\n        img_path = img_data_sample.img_path\n        img_bytes = get(img_path, backend_args=self.backend_args)\n        img = mmcv.imfrombytes(img_bytes, channel_order='rgb')\n\n        out_file = None\n        if self.test_out_dir is not None:\n            video_name = img_path.split('/')[-3]\n            mkdir_or_exist(osp.join(self.test_out_dir, video_name))\n            out_file = osp.join(self.test_out_dir, video_name,\n                                osp.basename(img_path))\n\n        self._visualizer.add_datasample(\n            osp.basename(img_path) if self.show else 'test_img',\n            img,\n            data_sample=img_data_sample,\n            show=self.show,\n            wait_time=self.wait_time,\n            pred_score_thr=self.score_thr,\n            out_file=out_file,\n            step=step)\n\n\ndef draw_all_character(visualizer, characters, w):\n    start_index = 2\n    y_index = 5\n    for char in characters:\n        if isinstance(char, str):\n            visualizer.draw_texts(\n                str(char),\n                positions=np.array([start_index, y_index]),\n                colors=(0, 0, 0),\n                font_families='monospace')\n            start_index += len(char) * 8\n        else:\n            visualizer.draw_texts(\n                str(char[0]),\n                positions=np.array([start_index, y_index]),\n                colors=char[1],\n                font_families='monospace')\n            start_index += len(char[0]) * 8\n\n        if start_index > w - 10:\n            start_index = 2\n            y_index += 15\n\n    drawn_text = visualizer.get_image()\n    return drawn_text\n\n\n@HOOKS.register_module()\nclass GroundingVisualizationHook(DetVisualizationHook):\n\n    def after_test_iter(self, runner: Runner, batch_idx: int, data_batch: dict,\n                        outputs: Sequence[DetDataSample]) -> None:\n        \"\"\"Run after every testing iterations.\n\n        Args:\n            runner (:obj:`Runner`): The runner of the testing process.\n            batch_idx (int): The index of the current batch in the val loop.\n            data_batch (dict): Data from dataloader.\n            outputs (Sequence[:obj:`DetDataSample`]): A batch of data samples\n                that contain annotations and predictions.\n        \"\"\"\n        if self.draw is False:\n            return\n\n        if self.test_out_dir is not None:\n            self.test_out_dir = osp.join(runner.work_dir, runner.timestamp,\n                                         self.test_out_dir)\n            mkdir_or_exist(self.test_out_dir)\n\n        for data_sample in outputs:\n            data_sample = data_sample.cpu()\n\n            self._test_index += 1\n\n            img_path = data_sample.img_path\n            img_bytes = get(img_path, backend_args=self.backend_args)\n            img = mmcv.imfrombytes(img_bytes, channel_order='rgb')\n\n            out_file = None\n            if self.test_out_dir is not None:\n                out_file = osp.basename(img_path)\n                out_file = osp.join(self.test_out_dir, out_file)\n\n            text = data_sample.text\n            if isinstance(text, str):  # VG\n                gt_instances = data_sample.gt_instances\n                tokens_positive = data_sample.tokens_positive\n                if 'phrase_ids' in data_sample:\n                    # flickr30k\n                    gt_labels = data_sample.phrase_ids\n                else:\n                    gt_labels = gt_instances.labels\n                gt_bboxes = gt_instances.get('bboxes', None)\n                if gt_bboxes is not None and isinstance(gt_bboxes, BaseBoxes):\n                    gt_instances.bboxes = gt_bboxes.tensor\n                print(gt_labels, tokens_positive, gt_bboxes, img_path)\n                pred_instances = data_sample.pred_instances\n                pred_instances = pred_instances[\n                    pred_instances.scores > self.score_thr]\n                pred_labels = pred_instances.labels\n                pred_bboxes = pred_instances.bboxes\n                pred_scores = pred_instances.scores\n\n                max_label = 0\n                if len(gt_labels) > 0:\n                    max_label = max(gt_labels)\n                if len(pred_labels) > 0:\n                    max_label = max(max(pred_labels), max_label)\n\n                max_label = int(max(max_label, 0))\n                palette = np.random.randint(0, 256, size=(max_label + 1, 3))\n                bbox_palette = [tuple(c) for c in palette]\n                # bbox_palette = get_palette('random', max_label + 1)\n                if len(gt_labels) >= len(pred_labels):\n                    colors = [bbox_palette[label] for label in gt_labels]\n                else:\n                    colors = [bbox_palette[label] for label in pred_labels]\n\n                self._visualizer.set_image(img)\n\n                for label, bbox, color in zip(gt_labels, gt_bboxes, colors):\n                    self._visualizer.draw_bboxes(\n                        bbox, edge_colors=color, face_colors=color, alpha=0.3)\n                    self._visualizer.draw_bboxes(\n                        bbox, edge_colors=color, alpha=1)\n\n                drawn_img = self._visualizer.get_image()\n\n                new_image = np.ones(\n                    (100, img.shape[1], 3), dtype=np.uint8) * 255\n                self._visualizer.set_image(new_image)\n\n                if tokens_positive == -1:  # REC\n                    gt_tokens_positive = [[]]\n                else:  # Phrase Grounding\n                    gt_tokens_positive = [\n                        tokens_positive[label] for label in gt_labels\n                    ]\n                split_by_character = [char for char in text]\n                characters = []\n                start_index = 0\n                end_index = 0\n                for w in split_by_character:\n                    end_index += len(w)\n                    is_find = False\n                    for i, positive in enumerate(gt_tokens_positive):\n                        for p in positive:\n                            if start_index >= p[0] and end_index <= p[1]:\n                                characters.append([w, colors[i]])\n                                is_find = True\n                                break\n                        if is_find:\n                            break\n                    if not is_find:\n                        characters.append([w, (0, 0, 0)])\n                    start_index = end_index\n\n                drawn_text = draw_all_character(self._visualizer, characters,\n                                                img.shape[1])\n                drawn_gt_img = np.concatenate((drawn_img, drawn_text), axis=0)\n\n                self._visualizer.set_image(img)\n\n                for label, bbox, color in zip(pred_labels, pred_bboxes,\n                                              colors):\n                    self._visualizer.draw_bboxes(\n                        bbox, edge_colors=color, face_colors=color, alpha=0.3)\n                    self._visualizer.draw_bboxes(\n                        bbox, edge_colors=color, alpha=1)\n                print(pred_labels, pred_bboxes, pred_scores, colors)\n                areas = (pred_bboxes[:, 3] - pred_bboxes[:, 1]) * (\n                    pred_bboxes[:, 2] - pred_bboxes[:, 0])\n                scales = _get_adaptive_scales(areas)\n                score = [str(round(s.item(), 2)) for s in pred_scores]\n                font_sizes = [int(13 * scales[i]) for i in range(len(scales))]\n                self._visualizer.draw_texts(\n                    score,\n                    pred_bboxes[:, :2].int(),\n                    colors=(255, 255, 255),\n                    font_sizes=font_sizes,\n                    bboxes=[{\n                        'facecolor': 'black',\n                        'alpha': 0.8,\n                        'pad': 0.7,\n                        'edgecolor': 'none'\n                    }] * len(pred_bboxes))\n\n                drawn_img = self._visualizer.get_image()\n\n                new_image = np.ones(\n                    (100, img.shape[1], 3), dtype=np.uint8) * 255\n                self._visualizer.set_image(new_image)\n                drawn_text = draw_all_character(self._visualizer, characters,\n                                                img.shape[1])\n                drawn_pred_img = np.concatenate((drawn_img, drawn_text),\n                                                axis=0)\n                drawn_img = np.concatenate((drawn_gt_img, drawn_pred_img),\n                                           axis=1)\n\n                if self.show:\n                    self._visualizer.show(\n                        drawn_img,\n                        win_name=osp.basename(img_path),\n                        wait_time=self.wait_time)\n                if out_file is not None:\n                    mmcv.imwrite(drawn_img[..., ::-1], out_file)\n                else:\n                    self.add_image('test_img', drawn_img, self._test_index)\n            else:  # OD\n                self._visualizer.add_datasample(\n                    osp.basename(img_path) if self.show else 'test_img',\n                    img,\n                    data_sample=data_sample,\n                    show=self.show,\n                    wait_time=self.wait_time,\n                    pred_score_thr=self.score_thr,\n                    out_file=out_file,\n                    step=self._test_index)\n"
  },
  {
    "path": "mmdet/engine/hooks/yolox_mode_switch_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Sequence\n\nfrom mmengine.hooks import Hook\nfrom mmengine.model import is_model_wrapper\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass YOLOXModeSwitchHook(Hook):\n    \"\"\"Switch the mode of YOLOX during training.\n\n    This hook turns off the mosaic and mixup data augmentation and switches\n    to use L1 loss in bbox_head.\n\n    Args:\n        num_last_epochs (int): The number of latter epochs in the end of the\n            training to close the data augmentation and switch to L1 loss.\n            Defaults to 15.\n       skip_type_keys (Sequence[str], optional): Sequence of type string to be\n            skip pipeline. Defaults to ('Mosaic', 'RandomAffine', 'MixUp').\n    \"\"\"\n\n    def __init__(\n        self,\n        num_last_epochs: int = 15,\n        skip_type_keys: Sequence[str] = ('Mosaic', 'RandomAffine', 'MixUp')\n    ) -> None:\n        self.num_last_epochs = num_last_epochs\n        self.skip_type_keys = skip_type_keys\n        self._restart_dataloader = False\n        self._has_switched = False\n\n    def before_train_epoch(self, runner) -> None:\n        \"\"\"Close mosaic and mixup augmentation and switches to use L1 loss.\"\"\"\n        epoch = runner.epoch\n        train_loader = runner.train_dataloader\n        model = runner.model\n        # TODO: refactor after mmengine using model wrapper\n        if is_model_wrapper(model):\n            model = model.module\n        epoch_to_be_switched = ((epoch + 1) >=\n                                runner.max_epochs - self.num_last_epochs)\n        if epoch_to_be_switched and not self._has_switched:\n            runner.logger.info('No mosaic and mixup aug now!')\n            # The dataset pipeline cannot be updated when persistent_workers\n            # is True, so we need to force the dataloader's multi-process\n            # restart. This is a very hacky approach.\n            train_loader.dataset.update_skip_type_keys(self.skip_type_keys)\n            if hasattr(train_loader, 'persistent_workers'\n                       ) and train_loader.persistent_workers is True:\n                train_loader._DataLoader__initialized = False\n                train_loader._iterator = None\n                self._restart_dataloader = True\n            runner.logger.info('Add additional L1 loss now!')\n            if hasattr(model, 'detector'):\n                model.detector.bbox_head.use_l1 = True\n            else:\n                model.bbox_head.use_l1 = True\n            self._has_switched = True\n        else:\n            # Once the restart is complete, we need to restore\n            # the initialization flag.\n            if self._restart_dataloader:\n                train_loader._DataLoader__initialized = True\n"
  },
  {
    "path": "mmdet/engine/optimizers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .layer_decay_optimizer_constructor import \\\n    LearningRateDecayOptimizerConstructor\n\n__all__ = ['LearningRateDecayOptimizerConstructor']\n"
  },
  {
    "path": "mmdet/engine/optimizers/layer_decay_optimizer_constructor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nfrom typing import List\n\nimport torch.nn as nn\nfrom mmengine.dist import get_dist_info\nfrom mmengine.logging import MMLogger\nfrom mmengine.optim import DefaultOptimWrapperConstructor\n\nfrom mmdet.registry import OPTIM_WRAPPER_CONSTRUCTORS\n\n\ndef get_layer_id_for_convnext(var_name, max_layer_id):\n    \"\"\"Get the layer id to set the different learning rates in ``layer_wise``\n    decay_type.\n\n    Args:\n        var_name (str): The key of the model.\n        max_layer_id (int): Maximum layer id.\n\n    Returns:\n        int: The id number corresponding to different learning rate in\n        ``LearningRateDecayOptimizerConstructor``.\n    \"\"\"\n\n    if var_name in ('backbone.cls_token', 'backbone.mask_token',\n                    'backbone.pos_embed'):\n        return 0\n    elif var_name.startswith('backbone.downsample_layers'):\n        stage_id = int(var_name.split('.')[2])\n        if stage_id == 0:\n            layer_id = 0\n        elif stage_id == 1:\n            layer_id = 2\n        elif stage_id == 2:\n            layer_id = 3\n        elif stage_id == 3:\n            layer_id = max_layer_id\n        return layer_id\n    elif var_name.startswith('backbone.stages'):\n        stage_id = int(var_name.split('.')[2])\n        block_id = int(var_name.split('.')[3])\n        if stage_id == 0:\n            layer_id = 1\n        elif stage_id == 1:\n            layer_id = 2\n        elif stage_id == 2:\n            layer_id = 3 + block_id // 3\n        elif stage_id == 3:\n            layer_id = max_layer_id\n        return layer_id\n    else:\n        return max_layer_id + 1\n\n\ndef get_stage_id_for_convnext(var_name, max_stage_id):\n    \"\"\"Get the stage id to set the different learning rates in ``stage_wise``\n    decay_type.\n\n    Args:\n        var_name (str): The key of the model.\n        max_stage_id (int): Maximum stage id.\n\n    Returns:\n        int: The id number corresponding to different learning rate in\n        ``LearningRateDecayOptimizerConstructor``.\n    \"\"\"\n\n    if var_name in ('backbone.cls_token', 'backbone.mask_token',\n                    'backbone.pos_embed'):\n        return 0\n    elif var_name.startswith('backbone.downsample_layers'):\n        return 0\n    elif var_name.startswith('backbone.stages'):\n        stage_id = int(var_name.split('.')[2])\n        return stage_id + 1\n    else:\n        return max_stage_id - 1\n\n\n@OPTIM_WRAPPER_CONSTRUCTORS.register_module()\nclass LearningRateDecayOptimizerConstructor(DefaultOptimWrapperConstructor):\n    # Different learning rates are set for different layers of backbone.\n    # Note: Currently, this optimizer constructor is built for ConvNeXt.\n\n    def add_params(self, params: List[dict], module: nn.Module,\n                   **kwargs) -> None:\n        \"\"\"Add all parameters of module to the params list.\n\n        The parameters of the given module will be added to the list of param\n        groups, with specific rules defined by paramwise_cfg.\n\n        Args:\n            params (list[dict]): A list of param groups, it will be modified\n                in place.\n            module (nn.Module): The module to be added.\n        \"\"\"\n        logger = MMLogger.get_current_instance()\n\n        parameter_groups = {}\n        logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}')\n        num_layers = self.paramwise_cfg.get('num_layers') + 2\n        decay_rate = self.paramwise_cfg.get('decay_rate')\n        decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise')\n        logger.info('Build LearningRateDecayOptimizerConstructor  '\n                    f'{decay_type} {decay_rate} - {num_layers}')\n        weight_decay = self.base_wd\n        for name, param in module.named_parameters():\n            if not param.requires_grad:\n                continue  # frozen weights\n            if len(param.shape) == 1 or name.endswith('.bias') or name in (\n                    'pos_embed', 'cls_token'):\n                group_name = 'no_decay'\n                this_weight_decay = 0.\n            else:\n                group_name = 'decay'\n                this_weight_decay = weight_decay\n            if 'layer_wise' in decay_type:\n                if 'ConvNeXt' in module.backbone.__class__.__name__:\n                    layer_id = get_layer_id_for_convnext(\n                        name, self.paramwise_cfg.get('num_layers'))\n                    logger.info(f'set param {name} as id {layer_id}')\n                else:\n                    raise NotImplementedError()\n            elif decay_type == 'stage_wise':\n                if 'ConvNeXt' in module.backbone.__class__.__name__:\n                    layer_id = get_stage_id_for_convnext(name, num_layers)\n                    logger.info(f'set param {name} as id {layer_id}')\n                else:\n                    raise NotImplementedError()\n            group_name = f'layer_{layer_id}_{group_name}'\n\n            if group_name not in parameter_groups:\n                scale = decay_rate**(num_layers - layer_id - 1)\n\n                parameter_groups[group_name] = {\n                    'weight_decay': this_weight_decay,\n                    'params': [],\n                    'param_names': [],\n                    'lr_scale': scale,\n                    'group_name': group_name,\n                    'lr': scale * self.base_lr,\n                }\n\n            parameter_groups[group_name]['params'].append(param)\n            parameter_groups[group_name]['param_names'].append(name)\n        rank, _ = get_dist_info()\n        if rank == 0:\n            to_display = {}\n            for key in parameter_groups:\n                to_display[key] = {\n                    'param_names': parameter_groups[key]['param_names'],\n                    'lr_scale': parameter_groups[key]['lr_scale'],\n                    'lr': parameter_groups[key]['lr'],\n                    'weight_decay': parameter_groups[key]['weight_decay'],\n                }\n            logger.info(f'Param groups = {json.dumps(to_display, indent=2)}')\n        params.extend(parameter_groups.values())\n"
  },
  {
    "path": "mmdet/engine/runner/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .loops import TeacherStudentValLoop\n\n__all__ = ['TeacherStudentValLoop']\n"
  },
  {
    "path": "mmdet/engine/runner/loops.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.model import is_model_wrapper\nfrom mmengine.runner import ValLoop\n\nfrom mmdet.registry import LOOPS\n\n\n@LOOPS.register_module()\nclass TeacherStudentValLoop(ValLoop):\n    \"\"\"Loop for validation of model teacher and student.\"\"\"\n\n    def run(self):\n        \"\"\"Launch validation for model teacher and student.\"\"\"\n        self.runner.call_hook('before_val')\n        self.runner.call_hook('before_val_epoch')\n        self.runner.model.eval()\n\n        model = self.runner.model\n        if is_model_wrapper(model):\n            model = model.module\n        assert hasattr(model, 'teacher')\n        assert hasattr(model, 'student')\n\n        predict_on = model.semi_test_cfg.get('predict_on', None)\n        multi_metrics = dict()\n        for _predict_on in ['teacher', 'student']:\n            model.semi_test_cfg['predict_on'] = _predict_on\n            for idx, data_batch in enumerate(self.dataloader):\n                self.run_iter(idx, data_batch)\n            # compute metrics\n            metrics = self.evaluator.evaluate(len(self.dataloader.dataset))\n            multi_metrics.update(\n                {'/'.join((_predict_on, k)): v\n                 for k, v in metrics.items()})\n        model.semi_test_cfg['predict_on'] = predict_on\n\n        self.runner.call_hook('after_val_epoch', metrics=multi_metrics)\n        self.runner.call_hook('after_val')\n"
  },
  {
    "path": "mmdet/engine/schedulers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .quadratic_warmup import (QuadraticWarmupLR, QuadraticWarmupMomentum,\n                               QuadraticWarmupParamScheduler)\n\n__all__ = [\n    'QuadraticWarmupParamScheduler', 'QuadraticWarmupMomentum',\n    'QuadraticWarmupLR'\n]\n"
  },
  {
    "path": "mmdet/engine/schedulers/quadratic_warmup.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.optim.scheduler.lr_scheduler import LRSchedulerMixin\nfrom mmengine.optim.scheduler.momentum_scheduler import MomentumSchedulerMixin\nfrom mmengine.optim.scheduler.param_scheduler import INF, _ParamScheduler\nfrom torch.optim import Optimizer\n\nfrom mmdet.registry import PARAM_SCHEDULERS\n\n\n@PARAM_SCHEDULERS.register_module()\nclass QuadraticWarmupParamScheduler(_ParamScheduler):\n    r\"\"\"Warm up the parameter value of each parameter group by quadratic\n    formula:\n\n    .. math::\n\n        X_{t} = X_{t-1} + \\frac{2t+1}{{(end-begin)}^{2}} \\times X_{base}\n\n    Args:\n        optimizer (Optimizer): Wrapped optimizer.\n        param_name (str): Name of the parameter to be adjusted, such as\n            ``lr``, ``momentum``.\n        begin (int): Step at which to start updating the parameters.\n            Defaults to 0.\n        end (int): Step at which to stop updating the parameters.\n            Defaults to INF.\n        last_step (int): The index of last step. Used for resume without\n            state dict. Defaults to -1.\n        by_epoch (bool): Whether the scheduled parameters are updated by\n            epochs. Defaults to True.\n        verbose (bool): Whether to print the value for each update.\n            Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 optimizer: Optimizer,\n                 param_name: str,\n                 begin: int = 0,\n                 end: int = INF,\n                 last_step: int = -1,\n                 by_epoch: bool = True,\n                 verbose: bool = False):\n        if end >= INF:\n            raise ValueError('``end`` must be less than infinity,'\n                             'Please set ``end`` parameter of '\n                             '``QuadraticWarmupScheduler`` as the '\n                             'number of warmup end.')\n        self.total_iters = end - begin\n        super().__init__(\n            optimizer=optimizer,\n            param_name=param_name,\n            begin=begin,\n            end=end,\n            last_step=last_step,\n            by_epoch=by_epoch,\n            verbose=verbose)\n\n    @classmethod\n    def build_iter_from_epoch(cls,\n                              *args,\n                              begin=0,\n                              end=INF,\n                              by_epoch=True,\n                              epoch_length=None,\n                              **kwargs):\n        \"\"\"Build an iter-based instance of this scheduler from an epoch-based\n        config.\"\"\"\n        assert by_epoch, 'Only epoch-based kwargs whose `by_epoch=True` can ' \\\n                         'be converted to iter-based.'\n        assert epoch_length is not None and epoch_length > 0, \\\n            f'`epoch_length` must be a positive integer, ' \\\n            f'but got {epoch_length}.'\n        by_epoch = False\n        begin = begin * epoch_length\n        if end != INF:\n            end = end * epoch_length\n        return cls(*args, begin=begin, end=end, by_epoch=by_epoch, **kwargs)\n\n    def _get_value(self):\n        \"\"\"Compute value using chainable form of the scheduler.\"\"\"\n        if self.last_step == 0:\n            return [\n                base_value * (2 * self.last_step + 1) / self.total_iters**2\n                for base_value in self.base_values\n            ]\n\n        return [\n            group[self.param_name] + base_value *\n            (2 * self.last_step + 1) / self.total_iters**2\n            for base_value, group in zip(self.base_values,\n                                         self.optimizer.param_groups)\n        ]\n\n\n@PARAM_SCHEDULERS.register_module()\nclass QuadraticWarmupLR(LRSchedulerMixin, QuadraticWarmupParamScheduler):\n    \"\"\"Warm up the learning rate of each parameter group by quadratic formula.\n\n    Args:\n        optimizer (Optimizer): Wrapped optimizer.\n        begin (int): Step at which to start updating the parameters.\n            Defaults to 0.\n        end (int): Step at which to stop updating the parameters.\n            Defaults to INF.\n        last_step (int): The index of last step. Used for resume without\n            state dict. Defaults to -1.\n        by_epoch (bool): Whether the scheduled parameters are updated by\n            epochs. Defaults to True.\n        verbose (bool): Whether to print the value for each update.\n            Defaults to False.\n    \"\"\"\n\n\n@PARAM_SCHEDULERS.register_module()\nclass QuadraticWarmupMomentum(MomentumSchedulerMixin,\n                              QuadraticWarmupParamScheduler):\n    \"\"\"Warm up the momentum value of each parameter group by quadratic formula.\n\n    Args:\n        optimizer (Optimizer): Wrapped optimizer.\n        begin (int): Step at which to start updating the parameters.\n            Defaults to 0.\n        end (int): Step at which to stop updating the parameters.\n            Defaults to INF.\n        last_step (int): The index of last step. Used for resume without\n            state dict. Defaults to -1.\n        by_epoch (bool): Whether the scheduled parameters are updated by\n            epochs. Defaults to True.\n        verbose (bool): Whether to print the value for each update.\n            Defaults to False.\n    \"\"\"\n"
  },
  {
    "path": "mmdet/evaluation/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .evaluator import *  # noqa: F401,F403\nfrom .functional import *  # noqa: F401,F403\nfrom .metrics import *  # noqa: F401,F403\n"
  },
  {
    "path": "mmdet/evaluation/evaluator/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .multi_datasets_evaluator import MultiDatasetsEvaluator\n\n__all__ = ['MultiDatasetsEvaluator']\n"
  },
  {
    "path": "mmdet/evaluation/evaluator/multi_datasets_evaluator.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom collections import OrderedDict\nfrom typing import Sequence, Union\n\nfrom mmengine.dist import (broadcast_object_list, collect_results,\n                           is_main_process)\nfrom mmengine.evaluator import BaseMetric, Evaluator\nfrom mmengine.evaluator.metric import _to_cpu\nfrom mmengine.registry import EVALUATOR\n\nfrom mmdet.utils import ConfigType\n\n\n@EVALUATOR.register_module()\nclass MultiDatasetsEvaluator(Evaluator):\n    \"\"\"Wrapper class to compose class: `ConcatDataset` and multiple\n    :class:`BaseMetric` instances.\n    The metrics will be evaluated on each dataset slice separately. The name of\n    the each metric is the concatenation of the dataset prefix, the metric\n    prefix and the key of metric - e.g.\n    `dataset_prefix/metric_prefix/accuracy`.\n\n    Args:\n        metrics (dict or BaseMetric or Sequence): The config of metrics.\n        dataset_prefixes (Sequence[str]): The prefix of each dataset. The\n            length of this sequence should be the same as the length of the\n            datasets.\n    \"\"\"\n\n    def __init__(self, metrics: Union[ConfigType, BaseMetric, Sequence],\n                 dataset_prefixes: Sequence[str]) -> None:\n        super().__init__(metrics)\n        self.dataset_prefixes = dataset_prefixes\n        self._setups = False\n\n    def _get_cumulative_sizes(self):\n        # ConcatDataset have a property `cumulative_sizes`\n        if isinstance(self.dataset_meta, Sequence):\n            dataset_slices = self.dataset_meta[0]['cumulative_sizes']\n            if not self._setups:\n                self._setups = True\n                for dataset_meta, metric in zip(self.dataset_meta,\n                                                self.metrics):\n                    metric.dataset_meta = dataset_meta\n        else:\n            dataset_slices = self.dataset_meta['cumulative_sizes']\n        return dataset_slices\n\n    def evaluate(self, size: int) -> dict:\n        \"\"\"Invoke ``evaluate`` method of each metric and collect the metrics\n        dictionary.\n\n        Args:\n            size (int): Length of the entire validation dataset. When batch\n                size > 1, the dataloader may pad some data samples to make\n                sure all ranks have the same length of dataset slice. The\n                ``collect_results`` function will drop the padded data based on\n                this size.\n\n        Returns:\n            dict: Evaluation results of all metrics. The keys are the names\n            of the metrics, and the values are corresponding results.\n        \"\"\"\n        metrics_results = OrderedDict()\n        dataset_slices = self._get_cumulative_sizes()\n        assert len(dataset_slices) == len(self.dataset_prefixes)\n\n        for dataset_prefix, start, end, metric in zip(\n                self.dataset_prefixes, [0] + dataset_slices[:-1],\n                dataset_slices, self.metrics):\n            if len(metric.results) == 0:\n                warnings.warn(\n                    f'{metric.__class__.__name__} got empty `self.results`.'\n                    'Please ensure that the processed results are properly '\n                    'added into `self.results` in `process` method.')\n\n            results = collect_results(metric.results, size,\n                                      metric.collect_device)\n\n            if is_main_process():\n                # cast all tensors in results list to cpu\n                results = _to_cpu(results)\n                _metrics = metric.compute_metrics(\n                    results[start:end])  # type: ignore\n\n                if metric.prefix:\n                    final_prefix = '/'.join((dataset_prefix, metric.prefix))\n                else:\n                    final_prefix = dataset_prefix\n                print(f'================{final_prefix}================')\n                metric_results = {\n                    '/'.join((final_prefix, k)): v\n                    for k, v in _metrics.items()\n                }\n\n                # Check metric name conflicts\n                for name in metric_results.keys():\n                    if name in metrics_results:\n                        raise ValueError(\n                            'There are multiple evaluation results with '\n                            f'the same metric name {name}. Please make '\n                            'sure all metrics have different prefixes.')\n                metrics_results.update(metric_results)\n            metric.results.clear()\n        if is_main_process():\n            metrics_results = [metrics_results]\n        else:\n            metrics_results = [None]  # type: ignore\n        broadcast_object_list(metrics_results)\n        return metrics_results[0]\n"
  },
  {
    "path": "mmdet/evaluation/functional/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .bbox_overlaps import bbox_overlaps\nfrom .cityscapes_utils import evaluateImgLists\nfrom .class_names import (cityscapes_classes, coco_classes,\n                          coco_panoptic_classes, dataset_aliases, get_classes,\n                          imagenet_det_classes, imagenet_vid_classes,\n                          objects365v1_classes, objects365v2_classes,\n                          oid_challenge_classes, oid_v6_classes, voc_classes)\nfrom .mean_ap import average_precision, eval_map, print_map_summary\nfrom .panoptic_utils import (INSTANCE_OFFSET, pq_compute_multi_core,\n                             pq_compute_single_core)\nfrom .recall import (eval_recalls, plot_iou_recall, plot_num_recall,\n                     print_recall_summary)\nfrom .ytvis import YTVIS\nfrom .ytviseval import YTVISeval\n\n__all__ = [\n    'voc_classes', 'imagenet_det_classes', 'imagenet_vid_classes',\n    'coco_classes', 'cityscapes_classes', 'dataset_aliases', 'get_classes',\n    'average_precision', 'eval_map', 'print_map_summary', 'eval_recalls',\n    'print_recall_summary', 'plot_num_recall', 'plot_iou_recall',\n    'oid_v6_classes', 'oid_challenge_classes', 'INSTANCE_OFFSET',\n    'pq_compute_single_core', 'pq_compute_multi_core', 'bbox_overlaps',\n    'objects365v1_classes', 'objects365v2_classes', 'coco_panoptic_classes',\n    'evaluateImgLists', 'YTVIS', 'YTVISeval'\n]\n"
  },
  {
    "path": "mmdet/evaluation/functional/bbox_overlaps.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\n\n\ndef bbox_overlaps(bboxes1,\n                  bboxes2,\n                  mode='iou',\n                  eps=1e-6,\n                  use_legacy_coordinate=False):\n    \"\"\"Calculate the ious between each bbox of bboxes1 and bboxes2.\n\n    Args:\n        bboxes1 (ndarray): Shape (n, 4)\n        bboxes2 (ndarray): Shape (k, 4)\n        mode (str): IOU (intersection over union) or IOF (intersection\n            over foreground)\n        use_legacy_coordinate (bool): Whether to use coordinate system in\n            mmdet v1.x. which means width, height should be\n            calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively.\n            Note when function is used in `VOCDataset`, it should be\n            True to align with the official implementation\n            `http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCdevkit_18-May-2011.tar`\n            Default: False.\n\n    Returns:\n        ious (ndarray): Shape (n, k)\n    \"\"\"\n\n    assert mode in ['iou', 'iof']\n    if not use_legacy_coordinate:\n        extra_length = 0.\n    else:\n        extra_length = 1.\n    bboxes1 = bboxes1.astype(np.float32)\n    bboxes2 = bboxes2.astype(np.float32)\n    rows = bboxes1.shape[0]\n    cols = bboxes2.shape[0]\n    ious = np.zeros((rows, cols), dtype=np.float32)\n    if rows * cols == 0:\n        return ious\n    exchange = False\n    if bboxes1.shape[0] > bboxes2.shape[0]:\n        bboxes1, bboxes2 = bboxes2, bboxes1\n        ious = np.zeros((cols, rows), dtype=np.float32)\n        exchange = True\n    area1 = (bboxes1[:, 2] - bboxes1[:, 0] + extra_length) * (\n        bboxes1[:, 3] - bboxes1[:, 1] + extra_length)\n    area2 = (bboxes2[:, 2] - bboxes2[:, 0] + extra_length) * (\n        bboxes2[:, 3] - bboxes2[:, 1] + extra_length)\n    for i in range(bboxes1.shape[0]):\n        x_start = np.maximum(bboxes1[i, 0], bboxes2[:, 0])\n        y_start = np.maximum(bboxes1[i, 1], bboxes2[:, 1])\n        x_end = np.minimum(bboxes1[i, 2], bboxes2[:, 2])\n        y_end = np.minimum(bboxes1[i, 3], bboxes2[:, 3])\n        overlap = np.maximum(x_end - x_start + extra_length, 0) * np.maximum(\n            y_end - y_start + extra_length, 0)\n        if mode == 'iou':\n            union = area1[i] + area2 - overlap\n        else:\n            union = area1[i] if not exchange else area2\n        union = np.maximum(union, eps)\n        ious[i, :] = overlap / union\n    if exchange:\n        ious = ious.T\n    return ious\n"
  },
  {
    "path": "mmdet/evaluation/functional/cityscapes_utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Copyright (c) https://github.com/mcordts/cityscapesScripts\n# A wrapper of `cityscapesscripts` which supports loading groundtruth\n# image from `backend_args`.\nimport json\nimport os\nimport sys\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nimport mmcv\nimport numpy as np\nfrom mmengine.fileio import get\n\ntry:\n    import cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling as CSEval  # noqa: E501\n    from cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling import \\\n        CArgs  # noqa: E501\n    from cityscapesscripts.evaluation.instance import Instance\n    from cityscapesscripts.helpers.csHelpers import (id2label, labels,\n                                                     writeDict2JSON)\n    HAS_CITYSCAPESAPI = True\nexcept ImportError:\n    CArgs = object\n    HAS_CITYSCAPESAPI = False\n\n\ndef evaluateImgLists(prediction_list: list,\n                     groundtruth_list: list,\n                     args: CArgs,\n                     backend_args: Optional[dict] = None,\n                     dump_matches: bool = False) -> dict:\n    \"\"\"A wrapper of obj:``cityscapesscripts.evaluation.\n\n    evalInstanceLevelSemanticLabeling.evaluateImgLists``. Support loading\n    groundtruth image from file backend.\n    Args:\n        prediction_list (list): A list of prediction txt file.\n        groundtruth_list (list): A list of groundtruth image file.\n        args (CArgs): A global object setting in\n            obj:``cityscapesscripts.evaluation.\n            evalInstanceLevelSemanticLabeling``\n        backend_args (dict, optional): Arguments to instantiate the\n            preifx of uri corresponding backend. Defaults to None.\n        dump_matches (bool): whether dump matches.json. Defaults to False.\n    Returns:\n        dict: The computed metric.\n    \"\"\"\n    if not HAS_CITYSCAPESAPI:\n        raise RuntimeError('Failed to import `cityscapesscripts`.'\n                           'Please try to install official '\n                           'cityscapesscripts by '\n                           '\"pip install cityscapesscripts\"')\n    # determine labels of interest\n    CSEval.setInstanceLabels(args)\n    # get dictionary of all ground truth instances\n    gt_instances = getGtInstances(\n        groundtruth_list, args, backend_args=backend_args)\n    # match predictions and ground truth\n    matches = matchGtWithPreds(prediction_list, groundtruth_list, gt_instances,\n                               args, backend_args)\n    if dump_matches:\n        CSEval.writeDict2JSON(matches, 'matches.json')\n    # evaluate matches\n    apScores = CSEval.evaluateMatches(matches, args)\n    # averages\n    avgDict = CSEval.computeAverages(apScores, args)\n    # result dict\n    resDict = CSEval.prepareJSONDataForResults(avgDict, apScores, args)\n    if args.JSONOutput:\n        # create output folder if necessary\n        path = os.path.dirname(args.exportFile)\n        CSEval.ensurePath(path)\n        # Write APs to JSON\n        CSEval.writeDict2JSON(resDict, args.exportFile)\n\n    CSEval.printResults(avgDict, args)\n\n    return resDict\n\n\ndef matchGtWithPreds(prediction_list: list,\n                     groundtruth_list: list,\n                     gt_instances: dict,\n                     args: CArgs,\n                     backend_args=None):\n    \"\"\"A wrapper of obj:``cityscapesscripts.evaluation.\n\n    evalInstanceLevelSemanticLabeling.matchGtWithPreds``. Support loading\n    groundtruth image from file backend.\n    Args:\n        prediction_list (list): A list of prediction txt file.\n        groundtruth_list (list): A list of groundtruth image file.\n        gt_instances (dict): Groundtruth dict.\n        args (CArgs): A global object setting in\n            obj:``cityscapesscripts.evaluation.\n            evalInstanceLevelSemanticLabeling``\n        backend_args (dict, optional): Arguments to instantiate the\n            preifx of uri corresponding backend. Defaults to None.\n    Returns:\n        dict: The processed prediction and groundtruth result.\n    \"\"\"\n    if not HAS_CITYSCAPESAPI:\n        raise RuntimeError('Failed to import `cityscapesscripts`.'\n                           'Please try to install official '\n                           'cityscapesscripts by '\n                           '\"pip install cityscapesscripts\"')\n    matches: dict = dict()\n    if not args.quiet:\n        print(f'Matching {len(prediction_list)} pairs of images...')\n\n    count = 0\n    for (pred, gt) in zip(prediction_list, groundtruth_list):\n        # Read input files\n        gt_image = readGTImage(gt, backend_args)\n        pred_info = readPredInfo(pred)\n        # Get and filter ground truth instances\n        unfiltered_instances = gt_instances[gt]\n        cur_gt_instances_orig = CSEval.filterGtInstances(\n            unfiltered_instances, args)\n\n        # Try to assign all predictions\n        (cur_gt_instances,\n         cur_pred_instances) = CSEval.assignGt2Preds(cur_gt_instances_orig,\n                                                     gt_image, pred_info, args)\n\n        # append to global dict\n        matches[gt] = {}\n        matches[gt]['groundTruth'] = cur_gt_instances\n        matches[gt]['prediction'] = cur_pred_instances\n\n        count += 1\n        if not args.quiet:\n            print(f'\\rImages Processed: {count}', end=' ')\n            sys.stdout.flush()\n\n    if not args.quiet:\n        print('')\n\n    return matches\n\n\ndef readGTImage(image_file: Union[str, Path],\n                backend_args: Optional[dict] = None) -> np.ndarray:\n    \"\"\"Read an image from path.\n\n    Same as obj:``cityscapesscripts.evaluation.\n    evalInstanceLevelSemanticLabeling.readGTImage``, but support loading\n    groundtruth image from file backend.\n    Args:\n        image_file (str or Path): Either a str or pathlib.Path.\n        backend_args (dict, optional): Instantiates the corresponding file\n            backend. It may contain `backend` key to specify the file\n            backend. If it contains, the file backend corresponding to this\n            value will be used and initialized with the remaining values,\n            otherwise the corresponding file backend will be selected\n            based on the prefix of the file path. Defaults to None.\n    Returns:\n        np.ndarray: The groundtruth image.\n    \"\"\"\n    img_bytes = get(image_file, backend_args=backend_args)\n    img = mmcv.imfrombytes(img_bytes, flag='unchanged', backend='pillow')\n    return img\n\n\ndef readPredInfo(prediction_file: str) -> dict:\n    \"\"\"A wrapper of obj:``cityscapesscripts.evaluation.\n\n    evalInstanceLevelSemanticLabeling.readPredInfo``.\n    Args:\n        prediction_file (str): The prediction txt file.\n    Returns:\n        dict: The processed prediction results.\n    \"\"\"\n    if not HAS_CITYSCAPESAPI:\n        raise RuntimeError('Failed to import `cityscapesscripts`.'\n                           'Please try to install official '\n                           'cityscapesscripts by '\n                           '\"pip install cityscapesscripts\"')\n    printError = CSEval.printError\n\n    predInfo = {}\n    if (not os.path.isfile(prediction_file)):\n        printError(f\"Infofile '{prediction_file}' \"\n                   'for the predictions not found.')\n    with open(prediction_file) as f:\n        for line in f:\n            splittedLine = line.split(' ')\n            if len(splittedLine) != 3:\n                printError('Invalid prediction file. Expected content: '\n                           'relPathPrediction1 labelIDPrediction1 '\n                           'confidencePrediction1')\n            if os.path.isabs(splittedLine[0]):\n                printError('Invalid prediction file. First entry in each '\n                           'line must be a relative path.')\n\n            filename = os.path.join(\n                os.path.dirname(prediction_file), splittedLine[0])\n\n            imageInfo = {}\n            imageInfo['labelID'] = int(float(splittedLine[1]))\n            imageInfo['conf'] = float(splittedLine[2])  # type: ignore\n            predInfo[filename] = imageInfo\n\n    return predInfo\n\n\ndef getGtInstances(groundtruth_list: list,\n                   args: CArgs,\n                   backend_args: Optional[dict] = None) -> dict:\n    \"\"\"A wrapper of obj:``cityscapesscripts.evaluation.\n\n    evalInstanceLevelSemanticLabeling.getGtInstances``. Support loading\n    groundtruth image from file backend.\n    Args:\n        groundtruth_list (list): A list of groundtruth image file.\n        args (CArgs): A global object setting in\n            obj:``cityscapesscripts.evaluation.\n            evalInstanceLevelSemanticLabeling``\n        backend_args (dict, optional): Arguments to instantiate the\n            preifx of uri corresponding backend. Defaults to None.\n    Returns:\n        dict: The computed metric.\n    \"\"\"\n    if not HAS_CITYSCAPESAPI:\n        raise RuntimeError('Failed to import `cityscapesscripts`.'\n                           'Please try to install official '\n                           'cityscapesscripts by '\n                           '\"pip install cityscapesscripts\"')\n    # if there is a global statistics json, then load it\n    if (os.path.isfile(args.gtInstancesFile)):\n        if not args.quiet:\n            print('Loading ground truth instances from JSON.')\n        with open(args.gtInstancesFile) as json_file:\n            gt_instances = json.load(json_file)\n    # otherwise create it\n    else:\n        if (not args.quiet):\n            print('Creating ground truth instances from png files.')\n        gt_instances = instances2dict(\n            groundtruth_list, args, backend_args=backend_args)\n        writeDict2JSON(gt_instances, args.gtInstancesFile)\n\n    return gt_instances\n\n\ndef instances2dict(image_list: list,\n                   args: CArgs,\n                   backend_args: Optional[dict] = None) -> dict:\n    \"\"\"A wrapper of obj:``cityscapesscripts.evaluation.\n\n    evalInstanceLevelSemanticLabeling.instances2dict``. Support loading\n    groundtruth image from file backend.\n    Args:\n        image_list (list): A list of image file.\n        args (CArgs): A global object setting in\n            obj:``cityscapesscripts.evaluation.\n            evalInstanceLevelSemanticLabeling``\n        backend_args (dict, optional): Arguments to instantiate the\n            preifx of uri corresponding backend. Defaults to None.\n    Returns:\n        dict: The processed groundtruth results.\n    \"\"\"\n    if not HAS_CITYSCAPESAPI:\n        raise RuntimeError('Failed to import `cityscapesscripts`.'\n                           'Please try to install official '\n                           'cityscapesscripts by '\n                           '\"pip install cityscapesscripts\"')\n    imgCount = 0\n    instanceDict = {}\n\n    if not isinstance(image_list, list):\n        image_list = [image_list]\n\n    if not args.quiet:\n        print(f'Processing {len(image_list)} images...')\n\n    for image_name in image_list:\n        # Load image\n        img_bytes = get(image_name, backend_args=backend_args)\n        imgNp = mmcv.imfrombytes(img_bytes, flag='unchanged', backend='pillow')\n\n        # Initialize label categories\n        instances: dict = {}\n        for label in labels:\n            instances[label.name] = []\n\n        # Loop through all instance ids in instance image\n        for instanceId in np.unique(imgNp):\n            instanceObj = Instance(imgNp, instanceId)\n\n            instances[id2label[instanceObj.labelID].name].append(\n                instanceObj.toDict())\n\n        instanceDict[image_name] = instances\n        imgCount += 1\n\n        if not args.quiet:\n            print(f'\\rImages Processed: {imgCount}', end=' ')\n            sys.stdout.flush()\n\n    return instanceDict\n"
  },
  {
    "path": "mmdet/evaluation/functional/class_names.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.utils import is_str\n\n\ndef wider_face_classes() -> list:\n    \"\"\"Class names of WIDERFace.\"\"\"\n    return ['face']\n\n\ndef voc_classes() -> list:\n    \"\"\"Class names of PASCAL VOC.\"\"\"\n    return [\n        'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat',\n        'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person',\n        'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'\n    ]\n\n\ndef imagenet_det_classes() -> list:\n    \"\"\"Class names of ImageNet Det.\"\"\"\n    return [\n        'accordion', 'airplane', 'ant', 'antelope', 'apple', 'armadillo',\n        'artichoke', 'axe', 'baby_bed', 'backpack', 'bagel', 'balance_beam',\n        'banana', 'band_aid', 'banjo', 'baseball', 'basketball', 'bathing_cap',\n        'beaker', 'bear', 'bee', 'bell_pepper', 'bench', 'bicycle', 'binder',\n        'bird', 'bookshelf', 'bow_tie', 'bow', 'bowl', 'brassiere', 'burrito',\n        'bus', 'butterfly', 'camel', 'can_opener', 'car', 'cart', 'cattle',\n        'cello', 'centipede', 'chain_saw', 'chair', 'chime', 'cocktail_shaker',\n        'coffee_maker', 'computer_keyboard', 'computer_mouse', 'corkscrew',\n        'cream', 'croquet_ball', 'crutch', 'cucumber', 'cup_or_mug', 'diaper',\n        'digital_clock', 'dishwasher', 'dog', 'domestic_cat', 'dragonfly',\n        'drum', 'dumbbell', 'electric_fan', 'elephant', 'face_powder', 'fig',\n        'filing_cabinet', 'flower_pot', 'flute', 'fox', 'french_horn', 'frog',\n        'frying_pan', 'giant_panda', 'goldfish', 'golf_ball', 'golfcart',\n        'guacamole', 'guitar', 'hair_dryer', 'hair_spray', 'hamburger',\n        'hammer', 'hamster', 'harmonica', 'harp', 'hat_with_a_wide_brim',\n        'head_cabbage', 'helmet', 'hippopotamus', 'horizontal_bar', 'horse',\n        'hotdog', 'iPod', 'isopod', 'jellyfish', 'koala_bear', 'ladle',\n        'ladybug', 'lamp', 'laptop', 'lemon', 'lion', 'lipstick', 'lizard',\n        'lobster', 'maillot', 'maraca', 'microphone', 'microwave', 'milk_can',\n        'miniskirt', 'monkey', 'motorcycle', 'mushroom', 'nail', 'neck_brace',\n        'oboe', 'orange', 'otter', 'pencil_box', 'pencil_sharpener', 'perfume',\n        'person', 'piano', 'pineapple', 'ping-pong_ball', 'pitcher', 'pizza',\n        'plastic_bag', 'plate_rack', 'pomegranate', 'popsicle', 'porcupine',\n        'power_drill', 'pretzel', 'printer', 'puck', 'punching_bag', 'purse',\n        'rabbit', 'racket', 'ray', 'red_panda', 'refrigerator',\n        'remote_control', 'rubber_eraser', 'rugby_ball', 'ruler',\n        'salt_or_pepper_shaker', 'saxophone', 'scorpion', 'screwdriver',\n        'seal', 'sheep', 'ski', 'skunk', 'snail', 'snake', 'snowmobile',\n        'snowplow', 'soap_dispenser', 'soccer_ball', 'sofa', 'spatula',\n        'squirrel', 'starfish', 'stethoscope', 'stove', 'strainer',\n        'strawberry', 'stretcher', 'sunglasses', 'swimming_trunks', 'swine',\n        'syringe', 'table', 'tape_player', 'tennis_ball', 'tick', 'tie',\n        'tiger', 'toaster', 'traffic_light', 'train', 'trombone', 'trumpet',\n        'turtle', 'tv_or_monitor', 'unicycle', 'vacuum', 'violin',\n        'volleyball', 'waffle_iron', 'washer', 'water_bottle', 'watercraft',\n        'whale', 'wine_bottle', 'zebra'\n    ]\n\n\ndef imagenet_vid_classes() -> list:\n    \"\"\"Class names of ImageNet VID.\"\"\"\n    return [\n        'airplane', 'antelope', 'bear', 'bicycle', 'bird', 'bus', 'car',\n        'cattle', 'dog', 'domestic_cat', 'elephant', 'fox', 'giant_panda',\n        'hamster', 'horse', 'lion', 'lizard', 'monkey', 'motorcycle', 'rabbit',\n        'red_panda', 'sheep', 'snake', 'squirrel', 'tiger', 'train', 'turtle',\n        'watercraft', 'whale', 'zebra'\n    ]\n\n\ndef coco_classes() -> list:\n    \"\"\"Class names of COCO.\"\"\"\n    return [\n        'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n        'truck', 'boat', 'traffic_light', 'fire_hydrant', 'stop_sign',\n        'parking_meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',\n        'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',\n        'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',\n        'sports_ball', 'kite', 'baseball_bat', 'baseball_glove', 'skateboard',\n        'surfboard', 'tennis_racket', 'bottle', 'wine_glass', 'cup', 'fork',\n        'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',\n        'broccoli', 'carrot', 'hot_dog', 'pizza', 'donut', 'cake', 'chair',\n        'couch', 'potted_plant', 'bed', 'dining_table', 'toilet', 'tv',\n        'laptop', 'mouse', 'remote', 'keyboard', 'cell_phone', 'microwave',\n        'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',\n        'scissors', 'teddy_bear', 'hair_drier', 'toothbrush'\n    ]\n\n\ndef coco_panoptic_classes() -> list:\n    \"\"\"Class names of COCO panoptic.\"\"\"\n    return [\n        'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n        'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',\n        'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',\n        'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',\n        'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',\n        'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard',\n        'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork',\n        'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',\n        'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair',\n        'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv',\n        'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',\n        'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',\n        'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner',\n        'blanket', 'bridge', 'cardboard', 'counter', 'curtain', 'door-stuff',\n        'floor-wood', 'flower', 'fruit', 'gravel', 'house', 'light',\n        'mirror-stuff', 'net', 'pillow', 'platform', 'playingfield',\n        'railroad', 'river', 'road', 'roof', 'sand', 'sea', 'shelf', 'snow',\n        'stairs', 'tent', 'towel', 'wall-brick', 'wall-stone', 'wall-tile',\n        'wall-wood', 'water-other', 'window-blind', 'window-other',\n        'tree-merged', 'fence-merged', 'ceiling-merged', 'sky-other-merged',\n        'cabinet-merged', 'table-merged', 'floor-other-merged',\n        'pavement-merged', 'mountain-merged', 'grass-merged', 'dirt-merged',\n        'paper-merged', 'food-other-merged', 'building-other-merged',\n        'rock-merged', 'wall-other-merged', 'rug-merged'\n    ]\n\n\ndef cityscapes_classes() -> list:\n    \"\"\"Class names of Cityscapes.\"\"\"\n    return [\n        'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle',\n        'bicycle'\n    ]\n\n\ndef oid_challenge_classes() -> list:\n    \"\"\"Class names of Open Images Challenge.\"\"\"\n    return [\n        'Footwear', 'Jeans', 'House', 'Tree', 'Woman', 'Man', 'Land vehicle',\n        'Person', 'Wheel', 'Bus', 'Human face', 'Bird', 'Dress', 'Girl',\n        'Vehicle', 'Building', 'Cat', 'Car', 'Belt', 'Elephant', 'Dessert',\n        'Butterfly', 'Train', 'Guitar', 'Poster', 'Book', 'Boy', 'Bee',\n        'Flower', 'Window', 'Hat', 'Human head', 'Dog', 'Human arm', 'Drink',\n        'Human mouth', 'Human hair', 'Human nose', 'Human hand', 'Table',\n        'Marine invertebrates', 'Fish', 'Sculpture', 'Rose', 'Street light',\n        'Glasses', 'Fountain', 'Skyscraper', 'Swimwear', 'Brassiere', 'Drum',\n        'Duck', 'Countertop', 'Furniture', 'Ball', 'Human leg', 'Boat',\n        'Balloon', 'Bicycle helmet', 'Goggles', 'Door', 'Human eye', 'Shirt',\n        'Toy', 'Teddy bear', 'Pasta', 'Tomato', 'Human ear',\n        'Vehicle registration plate', 'Microphone', 'Musical keyboard',\n        'Tower', 'Houseplant', 'Flowerpot', 'Fruit', 'Vegetable',\n        'Musical instrument', 'Suit', 'Motorcycle', 'Bagel', 'French fries',\n        'Hamburger', 'Chair', 'Salt and pepper shakers', 'Snail', 'Airplane',\n        'Horse', 'Laptop', 'Computer keyboard', 'Football helmet', 'Cocktail',\n        'Juice', 'Tie', 'Computer monitor', 'Human beard', 'Bottle',\n        'Saxophone', 'Lemon', 'Mouse', 'Sock', 'Cowboy hat', 'Sun hat',\n        'Football', 'Porch', 'Sunglasses', 'Lobster', 'Crab', 'Picture frame',\n        'Van', 'Crocodile', 'Surfboard', 'Shorts', 'Helicopter', 'Helmet',\n        'Sports uniform', 'Taxi', 'Swan', 'Goose', 'Coat', 'Jacket', 'Handbag',\n        'Flag', 'Skateboard', 'Television', 'Tire', 'Spoon', 'Palm tree',\n        'Stairs', 'Salad', 'Castle', 'Oven', 'Microwave oven', 'Wine',\n        'Ceiling fan', 'Mechanical fan', 'Cattle', 'Truck', 'Box', 'Ambulance',\n        'Desk', 'Wine glass', 'Reptile', 'Tank', 'Traffic light', 'Billboard',\n        'Tent', 'Insect', 'Spider', 'Treadmill', 'Cupboard', 'Shelf',\n        'Seat belt', 'Human foot', 'Bicycle', 'Bicycle wheel', 'Couch',\n        'Bookcase', 'Fedora', 'Backpack', 'Bench', 'Oyster',\n        'Moths and butterflies', 'Lavender', 'Waffle', 'Fork', 'Animal',\n        'Accordion', 'Mobile phone', 'Plate', 'Coffee cup', 'Saucer',\n        'Platter', 'Dagger', 'Knife', 'Bull', 'Tortoise', 'Sea turtle', 'Deer',\n        'Weapon', 'Apple', 'Ski', 'Taco', 'Traffic sign', 'Beer', 'Necklace',\n        'Sunflower', 'Piano', 'Organ', 'Harpsichord', 'Bed', 'Cabinetry',\n        'Nightstand', 'Curtain', 'Chest of drawers', 'Drawer', 'Parrot',\n        'Sandal', 'High heels', 'Tableware', 'Cart', 'Mushroom', 'Kite',\n        'Missile', 'Seafood', 'Camera', 'Paper towel', 'Toilet paper',\n        'Sombrero', 'Radish', 'Lighthouse', 'Segway', 'Pig', 'Watercraft',\n        'Golf cart', 'studio couch', 'Dolphin', 'Whale', 'Earrings', 'Otter',\n        'Sea lion', 'Whiteboard', 'Monkey', 'Gondola', 'Zebra',\n        'Baseball glove', 'Scarf', 'Adhesive tape', 'Trousers', 'Scoreboard',\n        'Lily', 'Carnivore', 'Power plugs and sockets', 'Office building',\n        'Sandwich', 'Swimming pool', 'Headphones', 'Tin can', 'Crown', 'Doll',\n        'Cake', 'Frog', 'Beetle', 'Ant', 'Gas stove', 'Canoe', 'Falcon',\n        'Blue jay', 'Egg', 'Fire hydrant', 'Raccoon', 'Muffin', 'Wall clock',\n        'Coffee', 'Mug', 'Tea', 'Bear', 'Waste container', 'Home appliance',\n        'Candle', 'Lion', 'Mirror', 'Starfish', 'Marine mammal', 'Wheelchair',\n        'Umbrella', 'Alpaca', 'Violin', 'Cello', 'Brown bear', 'Canary', 'Bat',\n        'Ruler', 'Plastic bag', 'Penguin', 'Watermelon', 'Harbor seal', 'Pen',\n        'Pumpkin', 'Harp', 'Kitchen appliance', 'Roller skates', 'Bust',\n        'Coffee table', 'Tennis ball', 'Tennis racket', 'Ladder', 'Boot',\n        'Bowl', 'Stop sign', 'Volleyball', 'Eagle', 'Paddle', 'Chicken',\n        'Skull', 'Lamp', 'Beehive', 'Maple', 'Sink', 'Goldfish', 'Tripod',\n        'Coconut', 'Bidet', 'Tap', 'Bathroom cabinet', 'Toilet',\n        'Filing cabinet', 'Pretzel', 'Table tennis racket', 'Bronze sculpture',\n        'Rocket', 'Mouse', 'Hamster', 'Lizard', 'Lifejacket', 'Goat',\n        'Washing machine', 'Trumpet', 'Horn', 'Trombone', 'Sheep',\n        'Tablet computer', 'Pillow', 'Kitchen & dining room table',\n        'Parachute', 'Raven', 'Glove', 'Loveseat', 'Christmas tree',\n        'Shellfish', 'Rifle', 'Shotgun', 'Sushi', 'Sparrow', 'Bread',\n        'Toaster', 'Watch', 'Asparagus', 'Artichoke', 'Suitcase', 'Antelope',\n        'Broccoli', 'Ice cream', 'Racket', 'Banana', 'Cookie', 'Cucumber',\n        'Dragonfly', 'Lynx', 'Caterpillar', 'Light bulb', 'Office supplies',\n        'Miniskirt', 'Skirt', 'Fireplace', 'Potato', 'Light switch',\n        'Croissant', 'Cabbage', 'Ladybug', 'Handgun', 'Luggage and bags',\n        'Window blind', 'Snowboard', 'Baseball bat', 'Digital clock',\n        'Serving tray', 'Infant bed', 'Sofa bed', 'Guacamole', 'Fox', 'Pizza',\n        'Snowplow', 'Jet ski', 'Refrigerator', 'Lantern', 'Convenience store',\n        'Sword', 'Rugby ball', 'Owl', 'Ostrich', 'Pancake', 'Strawberry',\n        'Carrot', 'Tart', 'Dice', 'Turkey', 'Rabbit', 'Invertebrate', 'Vase',\n        'Stool', 'Swim cap', 'Shower', 'Clock', 'Jellyfish', 'Aircraft',\n        'Chopsticks', 'Orange', 'Snake', 'Sewing machine', 'Kangaroo', 'Mixer',\n        'Food processor', 'Shrimp', 'Towel', 'Porcupine', 'Jaguar', 'Cannon',\n        'Limousine', 'Mule', 'Squirrel', 'Kitchen knife', 'Tiara', 'Tiger',\n        'Bow and arrow', 'Candy', 'Rhinoceros', 'Shark', 'Cricket ball',\n        'Doughnut', 'Plumbing fixture', 'Camel', 'Polar bear', 'Coin',\n        'Printer', 'Blender', 'Giraffe', 'Billiard table', 'Kettle',\n        'Dinosaur', 'Pineapple', 'Zucchini', 'Jug', 'Barge', 'Teapot',\n        'Golf ball', 'Binoculars', 'Scissors', 'Hot dog', 'Door handle',\n        'Seahorse', 'Bathtub', 'Leopard', 'Centipede', 'Grapefruit', 'Snowman',\n        'Cheetah', 'Alarm clock', 'Grape', 'Wrench', 'Wok', 'Bell pepper',\n        'Cake stand', 'Barrel', 'Woodpecker', 'Flute', 'Corded phone',\n        'Willow', 'Punching bag', 'Pomegranate', 'Telephone', 'Pear',\n        'Common fig', 'Bench', 'Wood-burning stove', 'Burrito', 'Nail',\n        'Turtle', 'Submarine sandwich', 'Drinking straw', 'Peach', 'Popcorn',\n        'Frying pan', 'Picnic basket', 'Honeycomb', 'Envelope', 'Mango',\n        'Cutting board', 'Pitcher', 'Stationary bicycle', 'Dumbbell',\n        'Personal care', 'Dog bed', 'Snowmobile', 'Oboe', 'Briefcase',\n        'Squash', 'Tick', 'Slow cooker', 'Coffeemaker', 'Measuring cup',\n        'Crutch', 'Stretcher', 'Screwdriver', 'Flashlight', 'Spatula',\n        'Pressure cooker', 'Ring binder', 'Beaker', 'Torch', 'Winter melon'\n    ]\n\n\ndef oid_v6_classes() -> list:\n    \"\"\"Class names of Open Images V6.\"\"\"\n    return [\n        'Tortoise', 'Container', 'Magpie', 'Sea turtle', 'Football',\n        'Ambulance', 'Ladder', 'Toothbrush', 'Syringe', 'Sink', 'Toy',\n        'Organ (Musical Instrument)', 'Cassette deck', 'Apple', 'Human eye',\n        'Cosmetics', 'Paddle', 'Snowman', 'Beer', 'Chopsticks', 'Human beard',\n        'Bird', 'Parking meter', 'Traffic light', 'Croissant', 'Cucumber',\n        'Radish', 'Towel', 'Doll', 'Skull', 'Washing machine', 'Glove', 'Tick',\n        'Belt', 'Sunglasses', 'Banjo', 'Cart', 'Ball', 'Backpack', 'Bicycle',\n        'Home appliance', 'Centipede', 'Boat', 'Surfboard', 'Boot',\n        'Headphones', 'Hot dog', 'Shorts', 'Fast food', 'Bus', 'Boy',\n        'Screwdriver', 'Bicycle wheel', 'Barge', 'Laptop', 'Miniskirt',\n        'Drill (Tool)', 'Dress', 'Bear', 'Waffle', 'Pancake', 'Brown bear',\n        'Woodpecker', 'Blue jay', 'Pretzel', 'Bagel', 'Tower', 'Teapot',\n        'Person', 'Bow and arrow', 'Swimwear', 'Beehive', 'Brassiere', 'Bee',\n        'Bat (Animal)', 'Starfish', 'Popcorn', 'Burrito', 'Chainsaw',\n        'Balloon', 'Wrench', 'Tent', 'Vehicle registration plate', 'Lantern',\n        'Toaster', 'Flashlight', 'Billboard', 'Tiara', 'Limousine', 'Necklace',\n        'Carnivore', 'Scissors', 'Stairs', 'Computer keyboard', 'Printer',\n        'Traffic sign', 'Chair', 'Shirt', 'Poster', 'Cheese', 'Sock',\n        'Fire hydrant', 'Land vehicle', 'Earrings', 'Tie', 'Watercraft',\n        'Cabinetry', 'Suitcase', 'Muffin', 'Bidet', 'Snack', 'Snowmobile',\n        'Clock', 'Medical equipment', 'Cattle', 'Cello', 'Jet ski', 'Camel',\n        'Coat', 'Suit', 'Desk', 'Cat', 'Bronze sculpture', 'Juice', 'Gondola',\n        'Beetle', 'Cannon', 'Computer mouse', 'Cookie', 'Office building',\n        'Fountain', 'Coin', 'Calculator', 'Cocktail', 'Computer monitor',\n        'Box', 'Stapler', 'Christmas tree', 'Cowboy hat', 'Hiking equipment',\n        'Studio couch', 'Drum', 'Dessert', 'Wine rack', 'Drink', 'Zucchini',\n        'Ladle', 'Human mouth', 'Dairy Product', 'Dice', 'Oven', 'Dinosaur',\n        'Ratchet (Device)', 'Couch', 'Cricket ball', 'Winter melon', 'Spatula',\n        'Whiteboard', 'Pencil sharpener', 'Door', 'Hat', 'Shower', 'Eraser',\n        'Fedora', 'Guacamole', 'Dagger', 'Scarf', 'Dolphin', 'Sombrero',\n        'Tin can', 'Mug', 'Tap', 'Harbor seal', 'Stretcher', 'Can opener',\n        'Goggles', 'Human body', 'Roller skates', 'Coffee cup',\n        'Cutting board', 'Blender', 'Plumbing fixture', 'Stop sign',\n        'Office supplies', 'Volleyball (Ball)', 'Vase', 'Slow cooker',\n        'Wardrobe', 'Coffee', 'Whisk', 'Paper towel', 'Personal care', 'Food',\n        'Sun hat', 'Tree house', 'Flying disc', 'Skirt', 'Gas stove',\n        'Salt and pepper shakers', 'Mechanical fan', 'Face powder', 'Fax',\n        'Fruit', 'French fries', 'Nightstand', 'Barrel', 'Kite', 'Tart',\n        'Treadmill', 'Fox', 'Flag', 'French horn', 'Window blind',\n        'Human foot', 'Golf cart', 'Jacket', 'Egg (Food)', 'Street light',\n        'Guitar', 'Pillow', 'Human leg', 'Isopod', 'Grape', 'Human ear',\n        'Power plugs and sockets', 'Panda', 'Giraffe', 'Woman', 'Door handle',\n        'Rhinoceros', 'Bathtub', 'Goldfish', 'Houseplant', 'Goat',\n        'Baseball bat', 'Baseball glove', 'Mixing bowl',\n        'Marine invertebrates', 'Kitchen utensil', 'Light switch', 'House',\n        'Horse', 'Stationary bicycle', 'Hammer', 'Ceiling fan', 'Sofa bed',\n        'Adhesive tape', 'Harp', 'Sandal', 'Bicycle helmet', 'Saucer',\n        'Harpsichord', 'Human hair', 'Heater', 'Harmonica', 'Hamster',\n        'Curtain', 'Bed', 'Kettle', 'Fireplace', 'Scale', 'Drinking straw',\n        'Insect', 'Hair dryer', 'Kitchenware', 'Indoor rower', 'Invertebrate',\n        'Food processor', 'Bookcase', 'Refrigerator', 'Wood-burning stove',\n        'Punching bag', 'Common fig', 'Cocktail shaker', 'Jaguar (Animal)',\n        'Golf ball', 'Fashion accessory', 'Alarm clock', 'Filing cabinet',\n        'Artichoke', 'Table', 'Tableware', 'Kangaroo', 'Koala', 'Knife',\n        'Bottle', 'Bottle opener', 'Lynx', 'Lavender (Plant)', 'Lighthouse',\n        'Dumbbell', 'Human head', 'Bowl', 'Humidifier', 'Porch', 'Lizard',\n        'Billiard table', 'Mammal', 'Mouse', 'Motorcycle',\n        'Musical instrument', 'Swim cap', 'Frying pan', 'Snowplow',\n        'Bathroom cabinet', 'Missile', 'Bust', 'Man', 'Waffle iron', 'Milk',\n        'Ring binder', 'Plate', 'Mobile phone', 'Baked goods', 'Mushroom',\n        'Crutch', 'Pitcher (Container)', 'Mirror', 'Personal flotation device',\n        'Table tennis racket', 'Pencil case', 'Musical keyboard', 'Scoreboard',\n        'Briefcase', 'Kitchen knife', 'Nail (Construction)', 'Tennis ball',\n        'Plastic bag', 'Oboe', 'Chest of drawers', 'Ostrich', 'Piano', 'Girl',\n        'Plant', 'Potato', 'Hair spray', 'Sports equipment', 'Pasta',\n        'Penguin', 'Pumpkin', 'Pear', 'Infant bed', 'Polar bear', 'Mixer',\n        'Cupboard', 'Jacuzzi', 'Pizza', 'Digital clock', 'Pig', 'Reptile',\n        'Rifle', 'Lipstick', 'Skateboard', 'Raven', 'High heels', 'Red panda',\n        'Rose', 'Rabbit', 'Sculpture', 'Saxophone', 'Shotgun', 'Seafood',\n        'Submarine sandwich', 'Snowboard', 'Sword', 'Picture frame', 'Sushi',\n        'Loveseat', 'Ski', 'Squirrel', 'Tripod', 'Stethoscope', 'Submarine',\n        'Scorpion', 'Segway', 'Training bench', 'Snake', 'Coffee table',\n        'Skyscraper', 'Sheep', 'Television', 'Trombone', 'Tea', 'Tank', 'Taco',\n        'Telephone', 'Torch', 'Tiger', 'Strawberry', 'Trumpet', 'Tree',\n        'Tomato', 'Train', 'Tool', 'Picnic basket', 'Cooking spray',\n        'Trousers', 'Bowling equipment', 'Football helmet', 'Truck',\n        'Measuring cup', 'Coffeemaker', 'Violin', 'Vehicle', 'Handbag',\n        'Paper cutter', 'Wine', 'Weapon', 'Wheel', 'Worm', 'Wok', 'Whale',\n        'Zebra', 'Auto part', 'Jug', 'Pizza cutter', 'Cream', 'Monkey', 'Lion',\n        'Bread', 'Platter', 'Chicken', 'Eagle', 'Helicopter', 'Owl', 'Duck',\n        'Turtle', 'Hippopotamus', 'Crocodile', 'Toilet', 'Toilet paper',\n        'Squid', 'Clothing', 'Footwear', 'Lemon', 'Spider', 'Deer', 'Frog',\n        'Banana', 'Rocket', 'Wine glass', 'Countertop', 'Tablet computer',\n        'Waste container', 'Swimming pool', 'Dog', 'Book', 'Elephant', 'Shark',\n        'Candle', 'Leopard', 'Axe', 'Hand dryer', 'Soap dispenser',\n        'Porcupine', 'Flower', 'Canary', 'Cheetah', 'Palm tree', 'Hamburger',\n        'Maple', 'Building', 'Fish', 'Lobster', 'Garden Asparagus',\n        'Furniture', 'Hedgehog', 'Airplane', 'Spoon', 'Otter', 'Bull',\n        'Oyster', 'Horizontal bar', 'Convenience store', 'Bomb', 'Bench',\n        'Ice cream', 'Caterpillar', 'Butterfly', 'Parachute', 'Orange',\n        'Antelope', 'Beaker', 'Moths and butterflies', 'Window', 'Closet',\n        'Castle', 'Jellyfish', 'Goose', 'Mule', 'Swan', 'Peach', 'Coconut',\n        'Seat belt', 'Raccoon', 'Chisel', 'Fork', 'Lamp', 'Camera',\n        'Squash (Plant)', 'Racket', 'Human face', 'Human arm', 'Vegetable',\n        'Diaper', 'Unicycle', 'Falcon', 'Chime', 'Snail', 'Shellfish',\n        'Cabbage', 'Carrot', 'Mango', 'Jeans', 'Flowerpot', 'Pineapple',\n        'Drawer', 'Stool', 'Envelope', 'Cake', 'Dragonfly', 'Common sunflower',\n        'Microwave oven', 'Honeycomb', 'Marine mammal', 'Sea lion', 'Ladybug',\n        'Shelf', 'Watch', 'Candy', 'Salad', 'Parrot', 'Handgun', 'Sparrow',\n        'Van', 'Grinder', 'Spice rack', 'Light bulb', 'Corded phone',\n        'Sports uniform', 'Tennis racket', 'Wall clock', 'Serving tray',\n        'Kitchen & dining room table', 'Dog bed', 'Cake stand',\n        'Cat furniture', 'Bathroom accessory', 'Facial tissue holder',\n        'Pressure cooker', 'Kitchen appliance', 'Tire', 'Ruler',\n        'Luggage and bags', 'Microphone', 'Broccoli', 'Umbrella', 'Pastry',\n        'Grapefruit', 'Band-aid', 'Animal', 'Bell pepper', 'Turkey', 'Lily',\n        'Pomegranate', 'Doughnut', 'Glasses', 'Human nose', 'Pen', 'Ant',\n        'Car', 'Aircraft', 'Human hand', 'Skunk', 'Teddy bear', 'Watermelon',\n        'Cantaloupe', 'Dishwasher', 'Flute', 'Balance beam', 'Sandwich',\n        'Shrimp', 'Sewing machine', 'Binoculars', 'Rays and skates', 'Ipod',\n        'Accordion', 'Willow', 'Crab', 'Crown', 'Seahorse', 'Perfume',\n        'Alpaca', 'Taxi', 'Canoe', 'Remote control', 'Wheelchair',\n        'Rugby ball', 'Armadillo', 'Maracas', 'Helmet'\n    ]\n\n\ndef objects365v1_classes() -> list:\n    \"\"\"Class names of Objects365 V1.\"\"\"\n    return [\n        'person', 'sneakers', 'chair', 'hat', 'lamp', 'bottle',\n        'cabinet/shelf', 'cup', 'car', 'glasses', 'picture/frame', 'desk',\n        'handbag', 'street lights', 'book', 'plate', 'helmet', 'leather shoes',\n        'pillow', 'glove', 'potted plant', 'bracelet', 'flower', 'tv',\n        'storage box', 'vase', 'bench', 'wine glass', 'boots', 'bowl',\n        'dining table', 'umbrella', 'boat', 'flag', 'speaker', 'trash bin/can',\n        'stool', 'backpack', 'couch', 'belt', 'carpet', 'basket',\n        'towel/napkin', 'slippers', 'barrel/bucket', 'coffee table', 'suv',\n        'toy', 'tie', 'bed', 'traffic light', 'pen/pencil', 'microphone',\n        'sandals', 'canned', 'necklace', 'mirror', 'faucet', 'bicycle',\n        'bread', 'high heels', 'ring', 'van', 'watch', 'sink', 'horse', 'fish',\n        'apple', 'camera', 'candle', 'teddy bear', 'cake', 'motorcycle',\n        'wild bird', 'laptop', 'knife', 'traffic sign', 'cell phone', 'paddle',\n        'truck', 'cow', 'power outlet', 'clock', 'drum', 'fork', 'bus',\n        'hanger', 'nightstand', 'pot/pan', 'sheep', 'guitar', 'traffic cone',\n        'tea pot', 'keyboard', 'tripod', 'hockey', 'fan', 'dog', 'spoon',\n        'blackboard/whiteboard', 'balloon', 'air conditioner', 'cymbal',\n        'mouse', 'telephone', 'pickup truck', 'orange', 'banana', 'airplane',\n        'luggage', 'skis', 'soccer', 'trolley', 'oven', 'remote',\n        'baseball glove', 'paper towel', 'refrigerator', 'train', 'tomato',\n        'machinery vehicle', 'tent', 'shampoo/shower gel', 'head phone',\n        'lantern', 'donut', 'cleaning products', 'sailboat', 'tangerine',\n        'pizza', 'kite', 'computer box', 'elephant', 'toiletries', 'gas stove',\n        'broccoli', 'toilet', 'stroller', 'shovel', 'baseball bat',\n        'microwave', 'skateboard', 'surfboard', 'surveillance camera', 'gun',\n        'life saver', 'cat', 'lemon', 'liquid soap', 'zebra', 'duck',\n        'sports car', 'giraffe', 'pumpkin', 'piano', 'stop sign', 'radiator',\n        'converter', 'tissue ', 'carrot', 'washing machine', 'vent', 'cookies',\n        'cutting/chopping board', 'tennis racket', 'candy',\n        'skating and skiing shoes', 'scissors', 'folder', 'baseball',\n        'strawberry', 'bow tie', 'pigeon', 'pepper', 'coffee machine',\n        'bathtub', 'snowboard', 'suitcase', 'grapes', 'ladder', 'pear',\n        'american football', 'basketball', 'potato', 'paint brush', 'printer',\n        'billiards', 'fire hydrant', 'goose', 'projector', 'sausage',\n        'fire extinguisher', 'extension cord', 'facial mask', 'tennis ball',\n        'chopsticks', 'electronic stove and gas stove', 'pie', 'frisbee',\n        'kettle', 'hamburger', 'golf club', 'cucumber', 'clutch', 'blender',\n        'tong', 'slide', 'hot dog', 'toothbrush', 'facial cleanser', 'mango',\n        'deer', 'egg', 'violin', 'marker', 'ship', 'chicken', 'onion',\n        'ice cream', 'tape', 'wheelchair', 'plum', 'bar soap', 'scale',\n        'watermelon', 'cabbage', 'router/modem', 'golf ball', 'pine apple',\n        'crane', 'fire truck', 'peach', 'cello', 'notepaper', 'tricycle',\n        'toaster', 'helicopter', 'green beans', 'brush', 'carriage', 'cigar',\n        'earphone', 'penguin', 'hurdle', 'swing', 'radio', 'CD',\n        'parking meter', 'swan', 'garlic', 'french fries', 'horn', 'avocado',\n        'saxophone', 'trumpet', 'sandwich', 'cue', 'kiwi fruit', 'bear',\n        'fishing rod', 'cherry', 'tablet', 'green vegetables', 'nuts', 'corn',\n        'key', 'screwdriver', 'globe', 'broom', 'pliers', 'volleyball',\n        'hammer', 'eggplant', 'trophy', 'dates', 'board eraser', 'rice',\n        'tape measure/ruler', 'dumbbell', 'hamimelon', 'stapler', 'camel',\n        'lettuce', 'goldfish', 'meat balls', 'medal', 'toothpaste', 'antelope',\n        'shrimp', 'rickshaw', 'trombone', 'pomegranate', 'coconut',\n        'jellyfish', 'mushroom', 'calculator', 'treadmill', 'butterfly',\n        'egg tart', 'cheese', 'pig', 'pomelo', 'race car', 'rice cooker',\n        'tuba', 'crosswalk sign', 'papaya', 'hair drier', 'green onion',\n        'chips', 'dolphin', 'sushi', 'urinal', 'donkey', 'electric drill',\n        'spring rolls', 'tortoise/turtle', 'parrot', 'flute', 'measuring cup',\n        'shark', 'steak', 'poker card', 'binoculars', 'llama', 'radish',\n        'noodles', 'yak', 'mop', 'crab', 'microscope', 'barbell', 'bread/bun',\n        'baozi', 'lion', 'red cabbage', 'polar bear', 'lighter', 'seal',\n        'mangosteen', 'comb', 'eraser', 'pitaya', 'scallop', 'pencil case',\n        'saw', 'table tennis paddle', 'okra', 'starfish', 'eagle', 'monkey',\n        'durian', 'game board', 'rabbit', 'french horn', 'ambulance',\n        'asparagus', 'hoverboard', 'pasta', 'target', 'hotair balloon',\n        'chainsaw', 'lobster', 'iron', 'flashlight'\n    ]\n\n\ndef objects365v2_classes() -> list:\n    \"\"\"Class names of Objects365 V2.\"\"\"\n    return [\n        'Person', 'Sneakers', 'Chair', 'Other Shoes', 'Hat', 'Car', 'Lamp',\n        'Glasses', 'Bottle', 'Desk', 'Cup', 'Street Lights', 'Cabinet/shelf',\n        'Handbag/Satchel', 'Bracelet', 'Plate', 'Picture/Frame', 'Helmet',\n        'Book', 'Gloves', 'Storage box', 'Boat', 'Leather Shoes', 'Flower',\n        'Bench', 'Potted Plant', 'Bowl/Basin', 'Flag', 'Pillow', 'Boots',\n        'Vase', 'Microphone', 'Necklace', 'Ring', 'SUV', 'Wine Glass', 'Belt',\n        'Moniter/TV', 'Backpack', 'Umbrella', 'Traffic Light', 'Speaker',\n        'Watch', 'Tie', 'Trash bin Can', 'Slippers', 'Bicycle', 'Stool',\n        'Barrel/bucket', 'Van', 'Couch', 'Sandals', 'Bakset', 'Drum',\n        'Pen/Pencil', 'Bus', 'Wild Bird', 'High Heels', 'Motorcycle', 'Guitar',\n        'Carpet', 'Cell Phone', 'Bread', 'Camera', 'Canned', 'Truck',\n        'Traffic cone', 'Cymbal', 'Lifesaver', 'Towel', 'Stuffed Toy',\n        'Candle', 'Sailboat', 'Laptop', 'Awning', 'Bed', 'Faucet', 'Tent',\n        'Horse', 'Mirror', 'Power outlet', 'Sink', 'Apple', 'Air Conditioner',\n        'Knife', 'Hockey Stick', 'Paddle', 'Pickup Truck', 'Fork',\n        'Traffic Sign', 'Ballon', 'Tripod', 'Dog', 'Spoon', 'Clock', 'Pot',\n        'Cow', 'Cake', 'Dinning Table', 'Sheep', 'Hanger',\n        'Blackboard/Whiteboard', 'Napkin', 'Other Fish', 'Orange/Tangerine',\n        'Toiletry', 'Keyboard', 'Tomato', 'Lantern', 'Machinery Vehicle',\n        'Fan', 'Green Vegetables', 'Banana', 'Baseball Glove', 'Airplane',\n        'Mouse', 'Train', 'Pumpkin', 'Soccer', 'Skiboard', 'Luggage',\n        'Nightstand', 'Tea pot', 'Telephone', 'Trolley', 'Head Phone',\n        'Sports Car', 'Stop Sign', 'Dessert', 'Scooter', 'Stroller', 'Crane',\n        'Remote', 'Refrigerator', 'Oven', 'Lemon', 'Duck', 'Baseball Bat',\n        'Surveillance Camera', 'Cat', 'Jug', 'Broccoli', 'Piano', 'Pizza',\n        'Elephant', 'Skateboard', 'Surfboard', 'Gun',\n        'Skating and Skiing shoes', 'Gas stove', 'Donut', 'Bow Tie', 'Carrot',\n        'Toilet', 'Kite', 'Strawberry', 'Other Balls', 'Shovel', 'Pepper',\n        'Computer Box', 'Toilet Paper', 'Cleaning Products', 'Chopsticks',\n        'Microwave', 'Pigeon', 'Baseball', 'Cutting/chopping Board',\n        'Coffee Table', 'Side Table', 'Scissors', 'Marker', 'Pie', 'Ladder',\n        'Snowboard', 'Cookies', 'Radiator', 'Fire Hydrant', 'Basketball',\n        'Zebra', 'Grape', 'Giraffe', 'Potato', 'Sausage', 'Tricycle', 'Violin',\n        'Egg', 'Fire Extinguisher', 'Candy', 'Fire Truck', 'Billards',\n        'Converter', 'Bathtub', 'Wheelchair', 'Golf Club', 'Briefcase',\n        'Cucumber', 'Cigar/Cigarette ', 'Paint Brush', 'Pear', 'Heavy Truck',\n        'Hamburger', 'Extractor', 'Extention Cord', 'Tong', 'Tennis Racket',\n        'Folder', 'American Football', 'earphone', 'Mask', 'Kettle', 'Tennis',\n        'Ship', 'Swing', 'Coffee Machine', 'Slide', 'Carriage', 'Onion',\n        'Green beans', 'Projector', 'Frisbee',\n        'Washing Machine/Drying Machine', 'Chicken', 'Printer', 'Watermelon',\n        'Saxophone', 'Tissue', 'Toothbrush', 'Ice cream', 'Hotair ballon',\n        'Cello', 'French Fries', 'Scale', 'Trophy', 'Cabbage', 'Hot dog',\n        'Blender', 'Peach', 'Rice', 'Wallet/Purse', 'Volleyball', 'Deer',\n        'Goose', 'Tape', 'Tablet', 'Cosmetics', 'Trumpet', 'Pineapple',\n        'Golf Ball', 'Ambulance', 'Parking meter', 'Mango', 'Key', 'Hurdle',\n        'Fishing Rod', 'Medal', 'Flute', 'Brush', 'Penguin', 'Megaphone',\n        'Corn', 'Lettuce', 'Garlic', 'Swan', 'Helicopter', 'Green Onion',\n        'Sandwich', 'Nuts', 'Speed Limit Sign', 'Induction Cooker', 'Broom',\n        'Trombone', 'Plum', 'Rickshaw', 'Goldfish', 'Kiwi fruit',\n        'Router/modem', 'Poker Card', 'Toaster', 'Shrimp', 'Sushi', 'Cheese',\n        'Notepaper', 'Cherry', 'Pliers', 'CD', 'Pasta', 'Hammer', 'Cue',\n        'Avocado', 'Hamimelon', 'Flask', 'Mushroon', 'Screwdriver', 'Soap',\n        'Recorder', 'Bear', 'Eggplant', 'Board Eraser', 'Coconut',\n        'Tape Measur/ Ruler', 'Pig', 'Showerhead', 'Globe', 'Chips', 'Steak',\n        'Crosswalk Sign', 'Stapler', 'Campel', 'Formula 1 ', 'Pomegranate',\n        'Dishwasher', 'Crab', 'Hoverboard', 'Meat ball', 'Rice Cooker', 'Tuba',\n        'Calculator', 'Papaya', 'Antelope', 'Parrot', 'Seal', 'Buttefly',\n        'Dumbbell', 'Donkey', 'Lion', 'Urinal', 'Dolphin', 'Electric Drill',\n        'Hair Dryer', 'Egg tart', 'Jellyfish', 'Treadmill', 'Lighter',\n        'Grapefruit', 'Game board', 'Mop', 'Radish', 'Baozi', 'Target',\n        'French', 'Spring Rolls', 'Monkey', 'Rabbit', 'Pencil Case', 'Yak',\n        'Red Cabbage', 'Binoculars', 'Asparagus', 'Barbell', 'Scallop',\n        'Noddles', 'Comb', 'Dumpling', 'Oyster', 'Table Teniis paddle',\n        'Cosmetics Brush/Eyeliner Pencil', 'Chainsaw', 'Eraser', 'Lobster',\n        'Durian', 'Okra', 'Lipstick', 'Cosmetics Mirror', 'Curling',\n        'Table Tennis '\n    ]\n\n\ndef lvis_classes() -> list:\n    \"\"\"Class names of LVIS.\"\"\"\n    return [\n        'aerosol_can', 'air_conditioner', 'airplane', 'alarm_clock', 'alcohol',\n        'alligator', 'almond', 'ambulance', 'amplifier', 'anklet', 'antenna',\n        'apple', 'applesauce', 'apricot', 'apron', 'aquarium',\n        'arctic_(type_of_shoe)', 'armband', 'armchair', 'armoire', 'armor',\n        'artichoke', 'trash_can', 'ashtray', 'asparagus', 'atomizer',\n        'avocado', 'award', 'awning', 'ax', 'baboon', 'baby_buggy',\n        'basketball_backboard', 'backpack', 'handbag', 'suitcase', 'bagel',\n        'bagpipe', 'baguet', 'bait', 'ball', 'ballet_skirt', 'balloon',\n        'bamboo', 'banana', 'Band_Aid', 'bandage', 'bandanna', 'banjo',\n        'banner', 'barbell', 'barge', 'barrel', 'barrette', 'barrow',\n        'baseball_base', 'baseball', 'baseball_bat', 'baseball_cap',\n        'baseball_glove', 'basket', 'basketball', 'bass_horn', 'bat_(animal)',\n        'bath_mat', 'bath_towel', 'bathrobe', 'bathtub', 'batter_(food)',\n        'battery', 'beachball', 'bead', 'bean_curd', 'beanbag', 'beanie',\n        'bear', 'bed', 'bedpan', 'bedspread', 'cow', 'beef_(food)', 'beeper',\n        'beer_bottle', 'beer_can', 'beetle', 'bell', 'bell_pepper', 'belt',\n        'belt_buckle', 'bench', 'beret', 'bib', 'Bible', 'bicycle', 'visor',\n        'billboard', 'binder', 'binoculars', 'bird', 'birdfeeder', 'birdbath',\n        'birdcage', 'birdhouse', 'birthday_cake', 'birthday_card',\n        'pirate_flag', 'black_sheep', 'blackberry', 'blackboard', 'blanket',\n        'blazer', 'blender', 'blimp', 'blinker', 'blouse', 'blueberry',\n        'gameboard', 'boat', 'bob', 'bobbin', 'bobby_pin', 'boiled_egg',\n        'bolo_tie', 'deadbolt', 'bolt', 'bonnet', 'book', 'bookcase',\n        'booklet', 'bookmark', 'boom_microphone', 'boot', 'bottle',\n        'bottle_opener', 'bouquet', 'bow_(weapon)', 'bow_(decorative_ribbons)',\n        'bow-tie', 'bowl', 'pipe_bowl', 'bowler_hat', 'bowling_ball', 'box',\n        'boxing_glove', 'suspenders', 'bracelet', 'brass_plaque', 'brassiere',\n        'bread-bin', 'bread', 'breechcloth', 'bridal_gown', 'briefcase',\n        'broccoli', 'broach', 'broom', 'brownie', 'brussels_sprouts',\n        'bubble_gum', 'bucket', 'horse_buggy', 'bull', 'bulldog', 'bulldozer',\n        'bullet_train', 'bulletin_board', 'bulletproof_vest', 'bullhorn',\n        'bun', 'bunk_bed', 'buoy', 'burrito', 'bus_(vehicle)', 'business_card',\n        'butter', 'butterfly', 'button', 'cab_(taxi)', 'cabana', 'cabin_car',\n        'cabinet', 'locker', 'cake', 'calculator', 'calendar', 'calf',\n        'camcorder', 'camel', 'camera', 'camera_lens', 'camper_(vehicle)',\n        'can', 'can_opener', 'candle', 'candle_holder', 'candy_bar',\n        'candy_cane', 'walking_cane', 'canister', 'canoe', 'cantaloup',\n        'canteen', 'cap_(headwear)', 'bottle_cap', 'cape', 'cappuccino',\n        'car_(automobile)', 'railcar_(part_of_a_train)', 'elevator_car',\n        'car_battery', 'identity_card', 'card', 'cardigan', 'cargo_ship',\n        'carnation', 'horse_carriage', 'carrot', 'tote_bag', 'cart', 'carton',\n        'cash_register', 'casserole', 'cassette', 'cast', 'cat', 'cauliflower',\n        'cayenne_(spice)', 'CD_player', 'celery', 'cellular_telephone',\n        'chain_mail', 'chair', 'chaise_longue', 'chalice', 'chandelier',\n        'chap', 'checkbook', 'checkerboard', 'cherry', 'chessboard',\n        'chicken_(animal)', 'chickpea', 'chili_(vegetable)', 'chime',\n        'chinaware', 'crisp_(potato_chip)', 'poker_chip', 'chocolate_bar',\n        'chocolate_cake', 'chocolate_milk', 'chocolate_mousse', 'choker',\n        'chopping_board', 'chopstick', 'Christmas_tree', 'slide', 'cider',\n        'cigar_box', 'cigarette', 'cigarette_case', 'cistern', 'clarinet',\n        'clasp', 'cleansing_agent', 'cleat_(for_securing_rope)', 'clementine',\n        'clip', 'clipboard', 'clippers_(for_plants)', 'cloak', 'clock',\n        'clock_tower', 'clothes_hamper', 'clothespin', 'clutch_bag', 'coaster',\n        'coat', 'coat_hanger', 'coatrack', 'cock', 'cockroach',\n        'cocoa_(beverage)', 'coconut', 'coffee_maker', 'coffee_table',\n        'coffeepot', 'coil', 'coin', 'colander', 'coleslaw',\n        'coloring_material', 'combination_lock', 'pacifier', 'comic_book',\n        'compass', 'computer_keyboard', 'condiment', 'cone', 'control',\n        'convertible_(automobile)', 'sofa_bed', 'cooker', 'cookie',\n        'cooking_utensil', 'cooler_(for_food)', 'cork_(bottle_plug)',\n        'corkboard', 'corkscrew', 'edible_corn', 'cornbread', 'cornet',\n        'cornice', 'cornmeal', 'corset', 'costume', 'cougar', 'coverall',\n        'cowbell', 'cowboy_hat', 'crab_(animal)', 'crabmeat', 'cracker',\n        'crape', 'crate', 'crayon', 'cream_pitcher', 'crescent_roll', 'crib',\n        'crock_pot', 'crossbar', 'crouton', 'crow', 'crowbar', 'crown',\n        'crucifix', 'cruise_ship', 'police_cruiser', 'crumb', 'crutch',\n        'cub_(animal)', 'cube', 'cucumber', 'cufflink', 'cup', 'trophy_cup',\n        'cupboard', 'cupcake', 'hair_curler', 'curling_iron', 'curtain',\n        'cushion', 'cylinder', 'cymbal', 'dagger', 'dalmatian', 'dartboard',\n        'date_(fruit)', 'deck_chair', 'deer', 'dental_floss', 'desk',\n        'detergent', 'diaper', 'diary', 'die', 'dinghy', 'dining_table', 'tux',\n        'dish', 'dish_antenna', 'dishrag', 'dishtowel', 'dishwasher',\n        'dishwasher_detergent', 'dispenser', 'diving_board', 'Dixie_cup',\n        'dog', 'dog_collar', 'doll', 'dollar', 'dollhouse', 'dolphin',\n        'domestic_ass', 'doorknob', 'doormat', 'doughnut', 'dove', 'dragonfly',\n        'drawer', 'underdrawers', 'dress', 'dress_hat', 'dress_suit',\n        'dresser', 'drill', 'drone', 'dropper', 'drum_(musical_instrument)',\n        'drumstick', 'duck', 'duckling', 'duct_tape', 'duffel_bag', 'dumbbell',\n        'dumpster', 'dustpan', 'eagle', 'earphone', 'earplug', 'earring',\n        'easel', 'eclair', 'eel', 'egg', 'egg_roll', 'egg_yolk', 'eggbeater',\n        'eggplant', 'electric_chair', 'refrigerator', 'elephant', 'elk',\n        'envelope', 'eraser', 'escargot', 'eyepatch', 'falcon', 'fan',\n        'faucet', 'fedora', 'ferret', 'Ferris_wheel', 'ferry', 'fig_(fruit)',\n        'fighter_jet', 'figurine', 'file_cabinet', 'file_(tool)', 'fire_alarm',\n        'fire_engine', 'fire_extinguisher', 'fire_hose', 'fireplace',\n        'fireplug', 'first-aid_kit', 'fish', 'fish_(food)', 'fishbowl',\n        'fishing_rod', 'flag', 'flagpole', 'flamingo', 'flannel', 'flap',\n        'flash', 'flashlight', 'fleece', 'flip-flop_(sandal)',\n        'flipper_(footwear)', 'flower_arrangement', 'flute_glass', 'foal',\n        'folding_chair', 'food_processor', 'football_(American)',\n        'football_helmet', 'footstool', 'fork', 'forklift', 'freight_car',\n        'French_toast', 'freshener', 'frisbee', 'frog', 'fruit_juice',\n        'frying_pan', 'fudge', 'funnel', 'futon', 'gag', 'garbage',\n        'garbage_truck', 'garden_hose', 'gargle', 'gargoyle', 'garlic',\n        'gasmask', 'gazelle', 'gelatin', 'gemstone', 'generator',\n        'giant_panda', 'gift_wrap', 'ginger', 'giraffe', 'cincture',\n        'glass_(drink_container)', 'globe', 'glove', 'goat', 'goggles',\n        'goldfish', 'golf_club', 'golfcart', 'gondola_(boat)', 'goose',\n        'gorilla', 'gourd', 'grape', 'grater', 'gravestone', 'gravy_boat',\n        'green_bean', 'green_onion', 'griddle', 'grill', 'grits', 'grizzly',\n        'grocery_bag', 'guitar', 'gull', 'gun', 'hairbrush', 'hairnet',\n        'hairpin', 'halter_top', 'ham', 'hamburger', 'hammer', 'hammock',\n        'hamper', 'hamster', 'hair_dryer', 'hand_glass', 'hand_towel',\n        'handcart', 'handcuff', 'handkerchief', 'handle', 'handsaw',\n        'hardback_book', 'harmonium', 'hat', 'hatbox', 'veil', 'headband',\n        'headboard', 'headlight', 'headscarf', 'headset',\n        'headstall_(for_horses)', 'heart', 'heater', 'helicopter', 'helmet',\n        'heron', 'highchair', 'hinge', 'hippopotamus', 'hockey_stick', 'hog',\n        'home_plate_(baseball)', 'honey', 'fume_hood', 'hook', 'hookah',\n        'hornet', 'horse', 'hose', 'hot-air_balloon', 'hotplate', 'hot_sauce',\n        'hourglass', 'houseboat', 'hummingbird', 'hummus', 'polar_bear',\n        'icecream', 'popsicle', 'ice_maker', 'ice_pack', 'ice_skate',\n        'igniter', 'inhaler', 'iPod', 'iron_(for_clothing)', 'ironing_board',\n        'jacket', 'jam', 'jar', 'jean', 'jeep', 'jelly_bean', 'jersey',\n        'jet_plane', 'jewel', 'jewelry', 'joystick', 'jumpsuit', 'kayak',\n        'keg', 'kennel', 'kettle', 'key', 'keycard', 'kilt', 'kimono',\n        'kitchen_sink', 'kitchen_table', 'kite', 'kitten', 'kiwi_fruit',\n        'knee_pad', 'knife', 'knitting_needle', 'knob', 'knocker_(on_a_door)',\n        'koala', 'lab_coat', 'ladder', 'ladle', 'ladybug', 'lamb_(animal)',\n        'lamb-chop', 'lamp', 'lamppost', 'lampshade', 'lantern', 'lanyard',\n        'laptop_computer', 'lasagna', 'latch', 'lawn_mower', 'leather',\n        'legging_(clothing)', 'Lego', 'legume', 'lemon', 'lemonade', 'lettuce',\n        'license_plate', 'life_buoy', 'life_jacket', 'lightbulb',\n        'lightning_rod', 'lime', 'limousine', 'lion', 'lip_balm', 'liquor',\n        'lizard', 'log', 'lollipop', 'speaker_(stereo_equipment)', 'loveseat',\n        'machine_gun', 'magazine', 'magnet', 'mail_slot', 'mailbox_(at_home)',\n        'mallard', 'mallet', 'mammoth', 'manatee', 'mandarin_orange', 'manger',\n        'manhole', 'map', 'marker', 'martini', 'mascot', 'mashed_potato',\n        'masher', 'mask', 'mast', 'mat_(gym_equipment)', 'matchbox',\n        'mattress', 'measuring_cup', 'measuring_stick', 'meatball', 'medicine',\n        'melon', 'microphone', 'microscope', 'microwave_oven', 'milestone',\n        'milk', 'milk_can', 'milkshake', 'minivan', 'mint_candy', 'mirror',\n        'mitten', 'mixer_(kitchen_tool)', 'money',\n        'monitor_(computer_equipment) computer_monitor', 'monkey', 'motor',\n        'motor_scooter', 'motor_vehicle', 'motorcycle', 'mound_(baseball)',\n        'mouse_(computer_equipment)', 'mousepad', 'muffin', 'mug', 'mushroom',\n        'music_stool', 'musical_instrument', 'nailfile', 'napkin',\n        'neckerchief', 'necklace', 'necktie', 'needle', 'nest', 'newspaper',\n        'newsstand', 'nightshirt', 'nosebag_(for_animals)',\n        'noseband_(for_animals)', 'notebook', 'notepad', 'nut', 'nutcracker',\n        'oar', 'octopus_(food)', 'octopus_(animal)', 'oil_lamp', 'olive_oil',\n        'omelet', 'onion', 'orange_(fruit)', 'orange_juice', 'ostrich',\n        'ottoman', 'oven', 'overalls_(clothing)', 'owl', 'packet', 'inkpad',\n        'pad', 'paddle', 'padlock', 'paintbrush', 'painting', 'pajamas',\n        'palette', 'pan_(for_cooking)', 'pan_(metal_container)', 'pancake',\n        'pantyhose', 'papaya', 'paper_plate', 'paper_towel', 'paperback_book',\n        'paperweight', 'parachute', 'parakeet', 'parasail_(sports)', 'parasol',\n        'parchment', 'parka', 'parking_meter', 'parrot',\n        'passenger_car_(part_of_a_train)', 'passenger_ship', 'passport',\n        'pastry', 'patty_(food)', 'pea_(food)', 'peach', 'peanut_butter',\n        'pear', 'peeler_(tool_for_fruit_and_vegetables)', 'wooden_leg',\n        'pegboard', 'pelican', 'pen', 'pencil', 'pencil_box',\n        'pencil_sharpener', 'pendulum', 'penguin', 'pennant', 'penny_(coin)',\n        'pepper', 'pepper_mill', 'perfume', 'persimmon', 'person', 'pet',\n        'pew_(church_bench)', 'phonebook', 'phonograph_record', 'piano',\n        'pickle', 'pickup_truck', 'pie', 'pigeon', 'piggy_bank', 'pillow',\n        'pin_(non_jewelry)', 'pineapple', 'pinecone', 'ping-pong_ball',\n        'pinwheel', 'tobacco_pipe', 'pipe', 'pistol', 'pita_(bread)',\n        'pitcher_(vessel_for_liquid)', 'pitchfork', 'pizza', 'place_mat',\n        'plate', 'platter', 'playpen', 'pliers', 'plow_(farm_equipment)',\n        'plume', 'pocket_watch', 'pocketknife', 'poker_(fire_stirring_tool)',\n        'pole', 'polo_shirt', 'poncho', 'pony', 'pool_table', 'pop_(soda)',\n        'postbox_(public)', 'postcard', 'poster', 'pot', 'flowerpot', 'potato',\n        'potholder', 'pottery', 'pouch', 'power_shovel', 'prawn', 'pretzel',\n        'printer', 'projectile_(weapon)', 'projector', 'propeller', 'prune',\n        'pudding', 'puffer_(fish)', 'puffin', 'pug-dog', 'pumpkin', 'puncher',\n        'puppet', 'puppy', 'quesadilla', 'quiche', 'quilt', 'rabbit',\n        'race_car', 'racket', 'radar', 'radiator', 'radio_receiver', 'radish',\n        'raft', 'rag_doll', 'raincoat', 'ram_(animal)', 'raspberry', 'rat',\n        'razorblade', 'reamer_(juicer)', 'rearview_mirror', 'receipt',\n        'recliner', 'record_player', 'reflector', 'remote_control',\n        'rhinoceros', 'rib_(food)', 'rifle', 'ring', 'river_boat', 'road_map',\n        'robe', 'rocking_chair', 'rodent', 'roller_skate', 'Rollerblade',\n        'rolling_pin', 'root_beer', 'router_(computer_equipment)',\n        'rubber_band', 'runner_(carpet)', 'plastic_bag',\n        'saddle_(on_an_animal)', 'saddle_blanket', 'saddlebag', 'safety_pin',\n        'sail', 'salad', 'salad_plate', 'salami', 'salmon_(fish)',\n        'salmon_(food)', 'salsa', 'saltshaker', 'sandal_(type_of_shoe)',\n        'sandwich', 'satchel', 'saucepan', 'saucer', 'sausage', 'sawhorse',\n        'saxophone', 'scale_(measuring_instrument)', 'scarecrow', 'scarf',\n        'school_bus', 'scissors', 'scoreboard', 'scraper', 'screwdriver',\n        'scrubbing_brush', 'sculpture', 'seabird', 'seahorse', 'seaplane',\n        'seashell', 'sewing_machine', 'shaker', 'shampoo', 'shark',\n        'sharpener', 'Sharpie', 'shaver_(electric)', 'shaving_cream', 'shawl',\n        'shears', 'sheep', 'shepherd_dog', 'sherbert', 'shield', 'shirt',\n        'shoe', 'shopping_bag', 'shopping_cart', 'short_pants', 'shot_glass',\n        'shoulder_bag', 'shovel', 'shower_head', 'shower_cap',\n        'shower_curtain', 'shredder_(for_paper)', 'signboard', 'silo', 'sink',\n        'skateboard', 'skewer', 'ski', 'ski_boot', 'ski_parka', 'ski_pole',\n        'skirt', 'skullcap', 'sled', 'sleeping_bag', 'sling_(bandage)',\n        'slipper_(footwear)', 'smoothie', 'snake', 'snowboard', 'snowman',\n        'snowmobile', 'soap', 'soccer_ball', 'sock', 'sofa', 'softball',\n        'solar_array', 'sombrero', 'soup', 'soup_bowl', 'soupspoon',\n        'sour_cream', 'soya_milk', 'space_shuttle', 'sparkler_(fireworks)',\n        'spatula', 'spear', 'spectacles', 'spice_rack', 'spider', 'crawfish',\n        'sponge', 'spoon', 'sportswear', 'spotlight', 'squid_(food)',\n        'squirrel', 'stagecoach', 'stapler_(stapling_machine)', 'starfish',\n        'statue_(sculpture)', 'steak_(food)', 'steak_knife', 'steering_wheel',\n        'stepladder', 'step_stool', 'stereo_(sound_system)', 'stew', 'stirrer',\n        'stirrup', 'stool', 'stop_sign', 'brake_light', 'stove', 'strainer',\n        'strap', 'straw_(for_drinking)', 'strawberry', 'street_sign',\n        'streetlight', 'string_cheese', 'stylus', 'subwoofer', 'sugar_bowl',\n        'sugarcane_(plant)', 'suit_(clothing)', 'sunflower', 'sunglasses',\n        'sunhat', 'surfboard', 'sushi', 'mop', 'sweat_pants', 'sweatband',\n        'sweater', 'sweatshirt', 'sweet_potato', 'swimsuit', 'sword',\n        'syringe', 'Tabasco_sauce', 'table-tennis_table', 'table',\n        'table_lamp', 'tablecloth', 'tachometer', 'taco', 'tag', 'taillight',\n        'tambourine', 'army_tank', 'tank_(storage_vessel)',\n        'tank_top_(clothing)', 'tape_(sticky_cloth_or_paper)', 'tape_measure',\n        'tapestry', 'tarp', 'tartan', 'tassel', 'tea_bag', 'teacup',\n        'teakettle', 'teapot', 'teddy_bear', 'telephone', 'telephone_booth',\n        'telephone_pole', 'telephoto_lens', 'television_camera',\n        'television_set', 'tennis_ball', 'tennis_racket', 'tequila',\n        'thermometer', 'thermos_bottle', 'thermostat', 'thimble', 'thread',\n        'thumbtack', 'tiara', 'tiger', 'tights_(clothing)', 'timer', 'tinfoil',\n        'tinsel', 'tissue_paper', 'toast_(food)', 'toaster', 'toaster_oven',\n        'toilet', 'toilet_tissue', 'tomato', 'tongs', 'toolbox', 'toothbrush',\n        'toothpaste', 'toothpick', 'cover', 'tortilla', 'tow_truck', 'towel',\n        'towel_rack', 'toy', 'tractor_(farm_equipment)', 'traffic_light',\n        'dirt_bike', 'trailer_truck', 'train_(railroad_vehicle)', 'trampoline',\n        'tray', 'trench_coat', 'triangle_(musical_instrument)', 'tricycle',\n        'tripod', 'trousers', 'truck', 'truffle_(chocolate)', 'trunk', 'vat',\n        'turban', 'turkey_(food)', 'turnip', 'turtle', 'turtleneck_(clothing)',\n        'typewriter', 'umbrella', 'underwear', 'unicycle', 'urinal', 'urn',\n        'vacuum_cleaner', 'vase', 'vending_machine', 'vent', 'vest',\n        'videotape', 'vinegar', 'violin', 'vodka', 'volleyball', 'vulture',\n        'waffle', 'waffle_iron', 'wagon', 'wagon_wheel', 'walking_stick',\n        'wall_clock', 'wall_socket', 'wallet', 'walrus', 'wardrobe',\n        'washbasin', 'automatic_washer', 'watch', 'water_bottle',\n        'water_cooler', 'water_faucet', 'water_heater', 'water_jug',\n        'water_gun', 'water_scooter', 'water_ski', 'water_tower',\n        'watering_can', 'watermelon', 'weathervane', 'webcam', 'wedding_cake',\n        'wedding_ring', 'wet_suit', 'wheel', 'wheelchair', 'whipped_cream',\n        'whistle', 'wig', 'wind_chime', 'windmill', 'window_box_(for_plants)',\n        'windshield_wiper', 'windsock', 'wine_bottle', 'wine_bucket',\n        'wineglass', 'blinder_(for_horses)', 'wok', 'wolf', 'wooden_spoon',\n        'wreath', 'wrench', 'wristband', 'wristlet', 'yacht', 'yogurt',\n        'yoke_(animal_equipment)', 'zebra', 'zucchini'\n    ]\n\n\ndataset_aliases = {\n    'voc': ['voc', 'pascal_voc', 'voc07', 'voc12'],\n    'imagenet_det': ['det', 'imagenet_det', 'ilsvrc_det'],\n    'imagenet_vid': ['vid', 'imagenet_vid', 'ilsvrc_vid'],\n    'coco': ['coco', 'mscoco', 'ms_coco'],\n    'coco_panoptic': ['coco_panoptic', 'panoptic'],\n    'wider_face': ['WIDERFaceDataset', 'wider_face', 'WIDERFace'],\n    'cityscapes': ['cityscapes'],\n    'oid_challenge': ['oid_challenge', 'openimages_challenge'],\n    'oid_v6': ['oid_v6', 'openimages_v6'],\n    'objects365v1': ['objects365v1', 'obj365v1'],\n    'objects365v2': ['objects365v2', 'obj365v2'],\n    'lvis': ['lvis', 'lvis_v1'],\n}\n\n\ndef get_classes(dataset) -> list:\n    \"\"\"Get class names of a dataset.\"\"\"\n    alias2name = {}\n    for name, aliases in dataset_aliases.items():\n        for alias in aliases:\n            alias2name[alias] = name\n\n    if is_str(dataset):\n        if dataset in alias2name:\n            labels = eval(alias2name[dataset] + '_classes()')\n        else:\n            raise ValueError(f'Unrecognized dataset: {dataset}')\n    else:\n        raise TypeError(f'dataset must a str, but got {type(dataset)}')\n    return labels\n"
  },
  {
    "path": "mmdet/evaluation/functional/mean_ap.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom multiprocessing import Pool\n\nimport numpy as np\nfrom mmengine.logging import print_log\nfrom mmengine.utils import is_str\nfrom terminaltables import AsciiTable\n\nfrom .bbox_overlaps import bbox_overlaps\nfrom .class_names import get_classes\n\n\ndef average_precision(recalls, precisions, mode='area'):\n    \"\"\"Calculate average precision (for single or multiple scales).\n\n    Args:\n        recalls (ndarray): shape (num_scales, num_dets) or (num_dets, )\n        precisions (ndarray): shape (num_scales, num_dets) or (num_dets, )\n        mode (str): 'area' or '11points', 'area' means calculating the area\n            under precision-recall curve, '11points' means calculating\n            the average precision of recalls at [0, 0.1, ..., 1]\n\n    Returns:\n        float or ndarray: calculated average precision\n    \"\"\"\n    no_scale = False\n    if recalls.ndim == 1:\n        no_scale = True\n        recalls = recalls[np.newaxis, :]\n        precisions = precisions[np.newaxis, :]\n    assert recalls.shape == precisions.shape and recalls.ndim == 2\n    num_scales = recalls.shape[0]\n    ap = np.zeros(num_scales, dtype=np.float32)\n    if mode == 'area':\n        zeros = np.zeros((num_scales, 1), dtype=recalls.dtype)\n        ones = np.ones((num_scales, 1), dtype=recalls.dtype)\n        mrec = np.hstack((zeros, recalls, ones))\n        mpre = np.hstack((zeros, precisions, zeros))\n        for i in range(mpre.shape[1] - 1, 0, -1):\n            mpre[:, i - 1] = np.maximum(mpre[:, i - 1], mpre[:, i])\n        for i in range(num_scales):\n            ind = np.where(mrec[i, 1:] != mrec[i, :-1])[0]\n            ap[i] = np.sum(\n                (mrec[i, ind + 1] - mrec[i, ind]) * mpre[i, ind + 1])\n    elif mode == '11points':\n        for i in range(num_scales):\n            for thr in np.arange(0, 1 + 1e-3, 0.1):\n                precs = precisions[i, recalls[i, :] >= thr]\n                prec = precs.max() if precs.size > 0 else 0\n                ap[i] += prec\n        ap /= 11\n    else:\n        raise ValueError(\n            'Unrecognized mode, only \"area\" and \"11points\" are supported')\n    if no_scale:\n        ap = ap[0]\n    return ap\n\n\ndef tpfp_imagenet(det_bboxes,\n                  gt_bboxes,\n                  gt_bboxes_ignore=None,\n                  default_iou_thr=0.5,\n                  area_ranges=None,\n                  use_legacy_coordinate=False,\n                  **kwargs):\n    \"\"\"Check if detected bboxes are true positive or false positive.\n\n    Args:\n        det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5).\n        gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4).\n        gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image,\n            of shape (k, 4). Defaults to None\n        default_iou_thr (float): IoU threshold to be considered as matched for\n            medium and large bboxes (small ones have special rules).\n            Defaults to 0.5.\n        area_ranges (list[tuple] | None): Range of bbox areas to be evaluated,\n            in the format [(min1, max1), (min2, max2), ...]. Defaults to None.\n        use_legacy_coordinate (bool): Whether to use coordinate system in\n            mmdet v1.x. which means width, height should be\n            calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively.\n            Defaults to False.\n\n    Returns:\n        tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape of\n        each array is (num_scales, m).\n    \"\"\"\n\n    if not use_legacy_coordinate:\n        extra_length = 0.\n    else:\n        extra_length = 1.\n\n    # an indicator of ignored gts\n    gt_ignore_inds = np.concatenate(\n        (np.zeros(gt_bboxes.shape[0],\n                  dtype=bool), np.ones(gt_bboxes_ignore.shape[0], dtype=bool)))\n    # stack gt_bboxes and gt_bboxes_ignore for convenience\n    gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore))\n\n    num_dets = det_bboxes.shape[0]\n    num_gts = gt_bboxes.shape[0]\n    if area_ranges is None:\n        area_ranges = [(None, None)]\n    num_scales = len(area_ranges)\n    # tp and fp are of shape (num_scales, num_gts), each row is tp or fp\n    # of a certain scale.\n    tp = np.zeros((num_scales, num_dets), dtype=np.float32)\n    fp = np.zeros((num_scales, num_dets), dtype=np.float32)\n    if gt_bboxes.shape[0] == 0:\n        if area_ranges == [(None, None)]:\n            fp[...] = 1\n        else:\n            det_areas = (\n                det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * (\n                    det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length)\n            for i, (min_area, max_area) in enumerate(area_ranges):\n                fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1\n        return tp, fp\n    ious = bbox_overlaps(\n        det_bboxes, gt_bboxes - 1, use_legacy_coordinate=use_legacy_coordinate)\n    gt_w = gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length\n    gt_h = gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length\n    iou_thrs = np.minimum((gt_w * gt_h) / ((gt_w + 10.0) * (gt_h + 10.0)),\n                          default_iou_thr)\n    # sort all detections by scores in descending order\n    sort_inds = np.argsort(-det_bboxes[:, -1])\n    for k, (min_area, max_area) in enumerate(area_ranges):\n        gt_covered = np.zeros(num_gts, dtype=bool)\n        # if no area range is specified, gt_area_ignore is all False\n        if min_area is None:\n            gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool)\n        else:\n            gt_areas = gt_w * gt_h\n            gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area)\n        for i in sort_inds:\n            max_iou = -1\n            matched_gt = -1\n            # find best overlapped available gt\n            for j in range(num_gts):\n                # different from PASCAL VOC: allow finding other gts if the\n                # best overlapped ones are already matched by other det bboxes\n                if gt_covered[j]:\n                    continue\n                elif ious[i, j] >= iou_thrs[j] and ious[i, j] > max_iou:\n                    max_iou = ious[i, j]\n                    matched_gt = j\n            # there are 4 cases for a det bbox:\n            # 1. it matches a gt, tp = 1, fp = 0\n            # 2. it matches an ignored gt, tp = 0, fp = 0\n            # 3. it matches no gt and within area range, tp = 0, fp = 1\n            # 4. it matches no gt but is beyond area range, tp = 0, fp = 0\n            if matched_gt >= 0:\n                gt_covered[matched_gt] = 1\n                if not (gt_ignore_inds[matched_gt]\n                        or gt_area_ignore[matched_gt]):\n                    tp[k, i] = 1\n            elif min_area is None:\n                fp[k, i] = 1\n            else:\n                bbox = det_bboxes[i, :4]\n                area = (bbox[2] - bbox[0] + extra_length) * (\n                    bbox[3] - bbox[1] + extra_length)\n                if area >= min_area and area < max_area:\n                    fp[k, i] = 1\n    return tp, fp\n\n\ndef tpfp_default(det_bboxes,\n                 gt_bboxes,\n                 gt_bboxes_ignore=None,\n                 iou_thr=0.5,\n                 area_ranges=None,\n                 use_legacy_coordinate=False,\n                 **kwargs):\n    \"\"\"Check if detected bboxes are true positive or false positive.\n\n    Args:\n        det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5).\n        gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4).\n        gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image,\n            of shape (k, 4). Defaults to None\n        iou_thr (float): IoU threshold to be considered as matched.\n            Defaults to 0.5.\n        area_ranges (list[tuple] | None): Range of bbox areas to be\n            evaluated, in the format [(min1, max1), (min2, max2), ...].\n            Defaults to None.\n        use_legacy_coordinate (bool): Whether to use coordinate system in\n            mmdet v1.x. which means width, height should be\n            calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively.\n            Defaults to False.\n\n    Returns:\n        tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape of\n        each array is (num_scales, m).\n    \"\"\"\n\n    if not use_legacy_coordinate:\n        extra_length = 0.\n    else:\n        extra_length = 1.\n\n    # an indicator of ignored gts\n    gt_ignore_inds = np.concatenate(\n        (np.zeros(gt_bboxes.shape[0],\n                  dtype=bool), np.ones(gt_bboxes_ignore.shape[0], dtype=bool)))\n    # stack gt_bboxes and gt_bboxes_ignore for convenience\n    gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore))\n\n    num_dets = det_bboxes.shape[0]\n    num_gts = gt_bboxes.shape[0]\n    if area_ranges is None:\n        area_ranges = [(None, None)]\n    num_scales = len(area_ranges)\n    # tp and fp are of shape (num_scales, num_gts), each row is tp or fp of\n    # a certain scale\n    tp = np.zeros((num_scales, num_dets), dtype=np.float32)\n    fp = np.zeros((num_scales, num_dets), dtype=np.float32)\n\n    # if there is no gt bboxes in this image, then all det bboxes\n    # within area range are false positives\n    if gt_bboxes.shape[0] == 0:\n        if area_ranges == [(None, None)]:\n            fp[...] = 1\n        else:\n            det_areas = (\n                det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * (\n                    det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length)\n            for i, (min_area, max_area) in enumerate(area_ranges):\n                fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1\n        return tp, fp\n\n    ious = bbox_overlaps(\n        det_bboxes, gt_bboxes, use_legacy_coordinate=use_legacy_coordinate)\n    # for each det, the max iou with all gts\n    ious_max = ious.max(axis=1)\n    # for each det, which gt overlaps most with it\n    ious_argmax = ious.argmax(axis=1)\n    # sort all dets in descending order by scores\n    sort_inds = np.argsort(-det_bboxes[:, -1])\n    for k, (min_area, max_area) in enumerate(area_ranges):\n        gt_covered = np.zeros(num_gts, dtype=bool)\n        # if no area range is specified, gt_area_ignore is all False\n        if min_area is None:\n            gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool)\n        else:\n            gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length) * (\n                gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length)\n            gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area)\n        for i in sort_inds:\n            if ious_max[i] >= iou_thr:\n                matched_gt = ious_argmax[i]\n                if not (gt_ignore_inds[matched_gt]\n                        or gt_area_ignore[matched_gt]):\n                    if not gt_covered[matched_gt]:\n                        gt_covered[matched_gt] = True\n                        tp[k, i] = 1\n                    else:\n                        fp[k, i] = 1\n                # otherwise ignore this detected bbox, tp = 0, fp = 0\n            elif min_area is None:\n                fp[k, i] = 1\n            else:\n                bbox = det_bboxes[i, :4]\n                area = (bbox[2] - bbox[0] + extra_length) * (\n                    bbox[3] - bbox[1] + extra_length)\n                if area >= min_area and area < max_area:\n                    fp[k, i] = 1\n    return tp, fp\n\n\ndef tpfp_openimages(det_bboxes,\n                    gt_bboxes,\n                    gt_bboxes_ignore=None,\n                    iou_thr=0.5,\n                    area_ranges=None,\n                    use_legacy_coordinate=False,\n                    gt_bboxes_group_of=None,\n                    use_group_of=True,\n                    ioa_thr=0.5,\n                    **kwargs):\n    \"\"\"Check if detected bboxes are true positive or false positive.\n\n    Args:\n        det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5).\n        gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4).\n        gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image,\n            of shape (k, 4). Defaults to None\n        iou_thr (float): IoU threshold to be considered as matched.\n            Defaults to 0.5.\n        area_ranges (list[tuple] | None): Range of bbox areas to be\n            evaluated, in the format [(min1, max1), (min2, max2), ...].\n            Defaults to None.\n        use_legacy_coordinate (bool): Whether to use coordinate system in\n            mmdet v1.x. which means width, height should be\n            calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively.\n            Defaults to False.\n        gt_bboxes_group_of (ndarray): GT group_of of this image, of shape\n            (k, 1). Defaults to None\n        use_group_of (bool): Whether to use group of when calculate TP and FP,\n            which only used in OpenImages evaluation. Defaults to True.\n        ioa_thr (float | None): IoA threshold to be considered as matched,\n            which only used in OpenImages evaluation. Defaults to 0.5.\n\n    Returns:\n        tuple[np.ndarray]: Returns a tuple (tp, fp, det_bboxes), where\n        (tp, fp) whose elements are 0 and 1. The shape of each array is\n        (num_scales, m). (det_bboxes) whose will filter those are not\n        matched by group of gts when processing Open Images evaluation.\n        The shape is (num_scales, m).\n    \"\"\"\n\n    if not use_legacy_coordinate:\n        extra_length = 0.\n    else:\n        extra_length = 1.\n\n    # an indicator of ignored gts\n    gt_ignore_inds = np.concatenate(\n        (np.zeros(gt_bboxes.shape[0],\n                  dtype=bool), np.ones(gt_bboxes_ignore.shape[0], dtype=bool)))\n    # stack gt_bboxes and gt_bboxes_ignore for convenience\n    gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore))\n\n    num_dets = det_bboxes.shape[0]\n    num_gts = gt_bboxes.shape[0]\n    if area_ranges is None:\n        area_ranges = [(None, None)]\n    num_scales = len(area_ranges)\n    # tp and fp are of shape (num_scales, num_gts), each row is tp or fp of\n    # a certain scale\n    tp = np.zeros((num_scales, num_dets), dtype=np.float32)\n    fp = np.zeros((num_scales, num_dets), dtype=np.float32)\n\n    # if there is no gt bboxes in this image, then all det bboxes\n    # within area range are false positives\n    if gt_bboxes.shape[0] == 0:\n        if area_ranges == [(None, None)]:\n            fp[...] = 1\n        else:\n            det_areas = (\n                det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * (\n                    det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length)\n            for i, (min_area, max_area) in enumerate(area_ranges):\n                fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1\n        return tp, fp, det_bboxes\n\n    if gt_bboxes_group_of is not None and use_group_of:\n        # if handle group-of boxes, divided gt boxes into two parts:\n        # non-group-of and group-of.Then calculate ious and ioas through\n        # non-group-of group-of gts respectively. This only used in\n        # OpenImages evaluation.\n        assert gt_bboxes_group_of.shape[0] == gt_bboxes.shape[0]\n        non_group_gt_bboxes = gt_bboxes[~gt_bboxes_group_of]\n        group_gt_bboxes = gt_bboxes[gt_bboxes_group_of]\n        num_gts_group = group_gt_bboxes.shape[0]\n        ious = bbox_overlaps(det_bboxes, non_group_gt_bboxes)\n        ioas = bbox_overlaps(det_bboxes, group_gt_bboxes, mode='iof')\n    else:\n        # if not consider group-of boxes, only calculate ious through gt boxes\n        ious = bbox_overlaps(\n            det_bboxes, gt_bboxes, use_legacy_coordinate=use_legacy_coordinate)\n        ioas = None\n\n    if ious.shape[1] > 0:\n        # for each det, the max iou with all gts\n        ious_max = ious.max(axis=1)\n        # for each det, which gt overlaps most with it\n        ious_argmax = ious.argmax(axis=1)\n        # sort all dets in descending order by scores\n        sort_inds = np.argsort(-det_bboxes[:, -1])\n        for k, (min_area, max_area) in enumerate(area_ranges):\n            gt_covered = np.zeros(num_gts, dtype=bool)\n            # if no area range is specified, gt_area_ignore is all False\n            if min_area is None:\n                gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool)\n            else:\n                gt_areas = (\n                    gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length) * (\n                        gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length)\n                gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area)\n            for i in sort_inds:\n                if ious_max[i] >= iou_thr:\n                    matched_gt = ious_argmax[i]\n                    if not (gt_ignore_inds[matched_gt]\n                            or gt_area_ignore[matched_gt]):\n                        if not gt_covered[matched_gt]:\n                            gt_covered[matched_gt] = True\n                            tp[k, i] = 1\n                        else:\n                            fp[k, i] = 1\n                    # otherwise ignore this detected bbox, tp = 0, fp = 0\n                elif min_area is None:\n                    fp[k, i] = 1\n                else:\n                    bbox = det_bboxes[i, :4]\n                    area = (bbox[2] - bbox[0] + extra_length) * (\n                        bbox[3] - bbox[1] + extra_length)\n                    if area >= min_area and area < max_area:\n                        fp[k, i] = 1\n    else:\n        # if there is no no-group-of gt bboxes in this image,\n        # then all det bboxes within area range are false positives.\n        # Only used in OpenImages evaluation.\n        if area_ranges == [(None, None)]:\n            fp[...] = 1\n        else:\n            det_areas = (\n                det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * (\n                    det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length)\n            for i, (min_area, max_area) in enumerate(area_ranges):\n                fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1\n\n    if ioas is None or ioas.shape[1] <= 0:\n        return tp, fp, det_bboxes\n    else:\n        # The evaluation of group-of TP and FP are done in two stages:\n        # 1. All detections are first matched to non group-of boxes; true\n        #    positives are determined.\n        # 2. Detections that are determined as false positives are matched\n        #    against group-of boxes and calculated group-of TP and FP.\n        # Only used in OpenImages evaluation.\n        det_bboxes_group = np.zeros(\n            (num_scales, ioas.shape[1], det_bboxes.shape[1]), dtype=float)\n        match_group_of = np.zeros((num_scales, num_dets), dtype=bool)\n        tp_group = np.zeros((num_scales, num_gts_group), dtype=np.float32)\n        ioas_max = ioas.max(axis=1)\n        # for each det, which gt overlaps most with it\n        ioas_argmax = ioas.argmax(axis=1)\n        # sort all dets in descending order by scores\n        sort_inds = np.argsort(-det_bboxes[:, -1])\n        for k, (min_area, max_area) in enumerate(area_ranges):\n            box_is_covered = tp[k]\n            # if no area range is specified, gt_area_ignore is all False\n            if min_area is None:\n                gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool)\n            else:\n                gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0]) * (\n                    gt_bboxes[:, 3] - gt_bboxes[:, 1])\n                gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area)\n            for i in sort_inds:\n                matched_gt = ioas_argmax[i]\n                if not box_is_covered[i]:\n                    if ioas_max[i] >= ioa_thr:\n                        if not (gt_ignore_inds[matched_gt]\n                                or gt_area_ignore[matched_gt]):\n                            if not tp_group[k, matched_gt]:\n                                tp_group[k, matched_gt] = 1\n                                match_group_of[k, i] = True\n                            else:\n                                match_group_of[k, i] = True\n\n                            if det_bboxes_group[k, matched_gt, -1] < \\\n                                    det_bboxes[i, -1]:\n                                det_bboxes_group[k, matched_gt] = \\\n                                    det_bboxes[i]\n\n        fp_group = (tp_group <= 0).astype(float)\n        tps = []\n        fps = []\n        # concatenate tp, fp, and det-boxes which not matched group of\n        # gt boxes and tp_group, fp_group, and det_bboxes_group which\n        # matched group of boxes respectively.\n        for i in range(num_scales):\n            tps.append(\n                np.concatenate((tp[i][~match_group_of[i]], tp_group[i])))\n            fps.append(\n                np.concatenate((fp[i][~match_group_of[i]], fp_group[i])))\n            det_bboxes = np.concatenate(\n                (det_bboxes[~match_group_of[i]], det_bboxes_group[i]))\n\n        tp = np.vstack(tps)\n        fp = np.vstack(fps)\n        return tp, fp, det_bboxes\n\n\ndef get_cls_results(det_results, annotations, class_id):\n    \"\"\"Get det results and gt information of a certain class.\n\n    Args:\n        det_results (list[list]): Same as `eval_map()`.\n        annotations (list[dict]): Same as `eval_map()`.\n        class_id (int): ID of a specific class.\n\n    Returns:\n        tuple[list[np.ndarray]]: detected bboxes, gt bboxes, ignored gt bboxes\n    \"\"\"\n    cls_dets = [img_res[class_id] for img_res in det_results]\n    cls_gts = []\n    cls_gts_ignore = []\n    for ann in annotations:\n        gt_inds = ann['labels'] == class_id\n        cls_gts.append(ann['bboxes'][gt_inds, :])\n\n        if ann.get('labels_ignore', None) is not None:\n            ignore_inds = ann['labels_ignore'] == class_id\n            cls_gts_ignore.append(ann['bboxes_ignore'][ignore_inds, :])\n        else:\n            cls_gts_ignore.append(np.empty((0, 4), dtype=np.float32))\n\n    return cls_dets, cls_gts, cls_gts_ignore\n\n\ndef get_cls_group_ofs(annotations, class_id):\n    \"\"\"Get `gt_group_of` of a certain class, which is used in Open Images.\n\n    Args:\n        annotations (list[dict]): Same as `eval_map()`.\n        class_id (int): ID of a specific class.\n\n    Returns:\n        list[np.ndarray]: `gt_group_of` of a certain class.\n    \"\"\"\n    gt_group_ofs = []\n    for ann in annotations:\n        gt_inds = ann['labels'] == class_id\n        if ann.get('gt_is_group_ofs', None) is not None:\n            gt_group_ofs.append(ann['gt_is_group_ofs'][gt_inds])\n        else:\n            gt_group_ofs.append(np.empty((0, 1), dtype=bool))\n\n    return gt_group_ofs\n\n\ndef eval_map(det_results,\n             annotations,\n             scale_ranges=None,\n             iou_thr=0.5,\n             ioa_thr=None,\n             dataset=None,\n             logger=None,\n             tpfp_fn=None,\n             nproc=4,\n             use_legacy_coordinate=False,\n             use_group_of=False,\n             eval_mode='area'):\n    \"\"\"Evaluate mAP of a dataset.\n\n    Args:\n        det_results (list[list]): [[cls1_det, cls2_det, ...], ...].\n            The outer list indicates images, and the inner list indicates\n            per-class detected bboxes.\n        annotations (list[dict]): Ground truth annotations where each item of\n            the list indicates an image. Keys of annotations are:\n\n            - `bboxes`: numpy array of shape (n, 4)\n            - `labels`: numpy array of shape (n, )\n            - `bboxes_ignore` (optional): numpy array of shape (k, 4)\n            - `labels_ignore` (optional): numpy array of shape (k, )\n        scale_ranges (list[tuple] | None): Range of scales to be evaluated,\n            in the format [(min1, max1), (min2, max2), ...]. A range of\n            (32, 64) means the area range between (32**2, 64**2).\n            Defaults to None.\n        iou_thr (float): IoU threshold to be considered as matched.\n            Defaults to 0.5.\n        ioa_thr (float | None): IoA threshold to be considered as matched,\n            which only used in OpenImages evaluation. Defaults to None.\n        dataset (list[str] | str | None): Dataset name or dataset classes,\n            there are minor differences in metrics for different datasets, e.g.\n            \"voc\", \"imagenet_det\", etc. Defaults to None.\n        logger (logging.Logger | str | None): The way to print the mAP\n            summary. See `mmengine.logging.print_log()` for details.\n            Defaults to None.\n        tpfp_fn (callable | None): The function used to determine true/\n            false positives. If None, :func:`tpfp_default` is used as default\n            unless dataset is 'det' or 'vid' (:func:`tpfp_imagenet` in this\n            case). If it is given as a function, then this function is used\n            to evaluate tp & fp. Default None.\n        nproc (int): Processes used for computing TP and FP.\n            Defaults to 4.\n        use_legacy_coordinate (bool): Whether to use coordinate system in\n            mmdet v1.x. which means width, height should be\n            calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively.\n            Defaults to False.\n        use_group_of (bool): Whether to use group of when calculate TP and FP,\n            which only used in OpenImages evaluation. Defaults to False.\n        eval_mode (str): 'area' or '11points', 'area' means calculating the\n            area under precision-recall curve, '11points' means calculating\n            the average precision of recalls at [0, 0.1, ..., 1],\n            PASCAL VOC2007 uses `11points` as default evaluate mode, while\n            others are 'area'. Defaults to 'area'.\n\n    Returns:\n        tuple: (mAP, [dict, dict, ...])\n    \"\"\"\n    assert len(det_results) == len(annotations)\n    assert eval_mode in ['area', '11points'], \\\n        f'Unrecognized {eval_mode} mode, only \"area\" and \"11points\" ' \\\n        'are supported'\n    if not use_legacy_coordinate:\n        extra_length = 0.\n    else:\n        extra_length = 1.\n\n    num_imgs = len(det_results)\n    num_scales = len(scale_ranges) if scale_ranges is not None else 1\n    num_classes = len(det_results[0])  # positive class num\n    area_ranges = ([(rg[0]**2, rg[1]**2) for rg in scale_ranges]\n                   if scale_ranges is not None else None)\n\n    # There is no need to use multi processes to process\n    # when num_imgs = 1 .\n    if num_imgs > 1:\n        assert nproc > 0, 'nproc must be at least one.'\n        nproc = min(nproc, num_imgs)\n        pool = Pool(nproc)\n\n    eval_results = []\n    for i in range(num_classes):\n        # get gt and det bboxes of this class\n        cls_dets, cls_gts, cls_gts_ignore = get_cls_results(\n            det_results, annotations, i)\n        # choose proper function according to datasets to compute tp and fp\n        if tpfp_fn is None:\n            if dataset in ['det', 'vid']:\n                tpfp_fn = tpfp_imagenet\n            elif dataset in ['oid_challenge', 'oid_v6'] \\\n                    or use_group_of is True:\n                tpfp_fn = tpfp_openimages\n            else:\n                tpfp_fn = tpfp_default\n        if not callable(tpfp_fn):\n            raise ValueError(\n                f'tpfp_fn has to be a function or None, but got {tpfp_fn}')\n\n        if num_imgs > 1:\n            # compute tp and fp for each image with multiple processes\n            args = []\n            if use_group_of:\n                # used in Open Images Dataset evaluation\n                gt_group_ofs = get_cls_group_ofs(annotations, i)\n                args.append(gt_group_ofs)\n                args.append([use_group_of for _ in range(num_imgs)])\n            if ioa_thr is not None:\n                args.append([ioa_thr for _ in range(num_imgs)])\n\n            tpfp = pool.starmap(\n                tpfp_fn,\n                zip(cls_dets, cls_gts, cls_gts_ignore,\n                    [iou_thr for _ in range(num_imgs)],\n                    [area_ranges for _ in range(num_imgs)],\n                    [use_legacy_coordinate for _ in range(num_imgs)], *args))\n        else:\n            tpfp = tpfp_fn(\n                cls_dets[0],\n                cls_gts[0],\n                cls_gts_ignore[0],\n                iou_thr,\n                area_ranges,\n                use_legacy_coordinate,\n                gt_bboxes_group_of=(get_cls_group_ofs(annotations, i)[0]\n                                    if use_group_of else None),\n                use_group_of=use_group_of,\n                ioa_thr=ioa_thr)\n            tpfp = [tpfp]\n\n        if use_group_of:\n            tp, fp, cls_dets = tuple(zip(*tpfp))\n        else:\n            tp, fp = tuple(zip(*tpfp))\n        # calculate gt number of each scale\n        # ignored gts or gts beyond the specific scale are not counted\n        num_gts = np.zeros(num_scales, dtype=int)\n        for j, bbox in enumerate(cls_gts):\n            if area_ranges is None:\n                num_gts[0] += bbox.shape[0]\n            else:\n                gt_areas = (bbox[:, 2] - bbox[:, 0] + extra_length) * (\n                    bbox[:, 3] - bbox[:, 1] + extra_length)\n                for k, (min_area, max_area) in enumerate(area_ranges):\n                    num_gts[k] += np.sum((gt_areas >= min_area)\n                                         & (gt_areas < max_area))\n        # sort all det bboxes by score, also sort tp and fp\n        cls_dets = np.vstack(cls_dets)\n        num_dets = cls_dets.shape[0]\n        sort_inds = np.argsort(-cls_dets[:, -1])\n        tp = np.hstack(tp)[:, sort_inds]\n        fp = np.hstack(fp)[:, sort_inds]\n        # calculate recall and precision with tp and fp\n        tp = np.cumsum(tp, axis=1)\n        fp = np.cumsum(fp, axis=1)\n        eps = np.finfo(np.float32).eps\n        recalls = tp / np.maximum(num_gts[:, np.newaxis], eps)\n        precisions = tp / np.maximum((tp + fp), eps)\n        # calculate AP\n        if scale_ranges is None:\n            recalls = recalls[0, :]\n            precisions = precisions[0, :]\n            num_gts = num_gts.item()\n        ap = average_precision(recalls, precisions, eval_mode)\n        eval_results.append({\n            'num_gts': num_gts,\n            'num_dets': num_dets,\n            'recall': recalls,\n            'precision': precisions,\n            'ap': ap\n        })\n\n    if num_imgs > 1:\n        pool.close()\n\n    if scale_ranges is not None:\n        # shape (num_classes, num_scales)\n        all_ap = np.vstack([cls_result['ap'] for cls_result in eval_results])\n        all_num_gts = np.vstack(\n            [cls_result['num_gts'] for cls_result in eval_results])\n        mean_ap = []\n        for i in range(num_scales):\n            if np.any(all_num_gts[:, i] > 0):\n                mean_ap.append(all_ap[all_num_gts[:, i] > 0, i].mean())\n            else:\n                mean_ap.append(0.0)\n    else:\n        aps = []\n        for cls_result in eval_results:\n            if cls_result['num_gts'] > 0:\n                aps.append(cls_result['ap'])\n        mean_ap = np.array(aps).mean().item() if aps else 0.0\n\n    print_map_summary(\n        mean_ap, eval_results, dataset, area_ranges, logger=logger)\n\n    return mean_ap, eval_results\n\n\ndef print_map_summary(mean_ap,\n                      results,\n                      dataset=None,\n                      scale_ranges=None,\n                      logger=None):\n    \"\"\"Print mAP and results of each class.\n\n    A table will be printed to show the gts/dets/recall/AP of each class and\n    the mAP.\n\n    Args:\n        mean_ap (float): Calculated from `eval_map()`.\n        results (list[dict]): Calculated from `eval_map()`.\n        dataset (list[str] | str | None): Dataset name or dataset classes.\n        scale_ranges (list[tuple] | None): Range of scales to be evaluated.\n        logger (logging.Logger | str | None): The way to print the mAP\n            summary. See `mmengine.logging.print_log()` for details.\n            Defaults to None.\n    \"\"\"\n\n    if logger == 'silent':\n        return\n\n    if isinstance(results[0]['ap'], np.ndarray):\n        num_scales = len(results[0]['ap'])\n    else:\n        num_scales = 1\n\n    if scale_ranges is not None:\n        assert len(scale_ranges) == num_scales\n\n    num_classes = len(results)\n\n    recalls = np.zeros((num_scales, num_classes), dtype=np.float32)\n    aps = np.zeros((num_scales, num_classes), dtype=np.float32)\n    num_gts = np.zeros((num_scales, num_classes), dtype=int)\n    for i, cls_result in enumerate(results):\n        if cls_result['recall'].size > 0:\n            recalls[:, i] = np.array(cls_result['recall'], ndmin=2)[:, -1]\n        aps[:, i] = cls_result['ap']\n        num_gts[:, i] = cls_result['num_gts']\n\n    if dataset is None:\n        label_names = [str(i) for i in range(num_classes)]\n    elif is_str(dataset):\n        label_names = get_classes(dataset)\n    else:\n        label_names = dataset\n\n    if not isinstance(mean_ap, list):\n        mean_ap = [mean_ap]\n\n    header = ['class', 'gts', 'dets', 'recall', 'ap']\n    for i in range(num_scales):\n        if scale_ranges is not None:\n            print_log(f'Scale range {scale_ranges[i]}', logger=logger)\n        table_data = [header]\n        for j in range(num_classes):\n            row_data = [\n                label_names[j], num_gts[i, j], results[j]['num_dets'],\n                f'{recalls[i, j]:.3f}', f'{aps[i, j]:.3f}'\n            ]\n            table_data.append(row_data)\n        table_data.append(['mAP', '', '', '', f'{mean_ap[i]:.3f}'])\n        table = AsciiTable(table_data)\n        table.inner_footing_row_border = True\n        print_log('\\n' + table.table, logger=logger)\n"
  },
  {
    "path": "mmdet/evaluation/functional/panoptic_utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Copyright (c) 2018, Alexander Kirillov\n# This file supports `backend_args` for `panopticapi`,\n# the source code is copied from `panopticapi`,\n# only the way to load the gt images is modified.\nimport multiprocessing\nimport os\n\nimport mmcv\nimport numpy as np\nfrom mmengine.fileio import get\n\n# A custom value to distinguish instance ID and category ID; need to\n# be greater than the number of categories.\n# For a pixel in the panoptic result map:\n#   pan_id = ins_id * INSTANCE_OFFSET + cat_id\nINSTANCE_OFFSET = 1000\n\ntry:\n    from panopticapi.evaluation import OFFSET, VOID, PQStat\n    from panopticapi.utils import rgb2id\nexcept ImportError:\n    PQStat = None\n    rgb2id = None\n    VOID = 0\n    OFFSET = 256 * 256 * 256\n\n\ndef pq_compute_single_core(proc_id,\n                           annotation_set,\n                           gt_folder,\n                           pred_folder,\n                           categories,\n                           backend_args=None,\n                           print_log=False):\n    \"\"\"The single core function to evaluate the metric of Panoptic\n    Segmentation.\n\n    Same as the function with the same name in `panopticapi`. Only the function\n    to load the images is changed to use the file client.\n\n    Args:\n        proc_id (int): The id of the mini process.\n        gt_folder (str): The path of the ground truth images.\n        pred_folder (str): The path of the prediction images.\n        categories (str): The categories of the dataset.\n        backend_args (object): The Backend of the dataset. If None,\n            the backend will be set to `local`.\n        print_log (bool): Whether to print the log. Defaults to False.\n    \"\"\"\n    if PQStat is None:\n        raise RuntimeError(\n            'panopticapi is not installed, please install it by: '\n            'pip install git+https://github.com/cocodataset/'\n            'panopticapi.git.')\n\n    pq_stat = PQStat()\n\n    idx = 0\n    for gt_ann, pred_ann in annotation_set:\n        if print_log and idx % 100 == 0:\n            print('Core: {}, {} from {} images processed'.format(\n                proc_id, idx, len(annotation_set)))\n        idx += 1\n        # The gt images can be on the local disk or `ceph`, so we use\n        # backend here.\n        img_bytes = get(\n            os.path.join(gt_folder, gt_ann['file_name']),\n            backend_args=backend_args)\n        pan_gt = mmcv.imfrombytes(img_bytes, flag='color', channel_order='rgb')\n        pan_gt = rgb2id(pan_gt)\n\n        # The predictions can only be on the local dist now.\n        pan_pred = mmcv.imread(\n            os.path.join(pred_folder, pred_ann['file_name']),\n            flag='color',\n            channel_order='rgb')\n        pan_pred = rgb2id(pan_pred)\n\n        gt_segms = {el['id']: el for el in gt_ann['segments_info']}\n        pred_segms = {el['id']: el for el in pred_ann['segments_info']}\n\n        # predicted segments area calculation + prediction sanity checks\n        pred_labels_set = set(el['id'] for el in pred_ann['segments_info'])\n        labels, labels_cnt = np.unique(pan_pred, return_counts=True)\n        for label, label_cnt in zip(labels, labels_cnt):\n            if label not in pred_segms:\n                if label == VOID:\n                    continue\n                raise KeyError(\n                    'In the image with ID {} segment with ID {} is '\n                    'presented in PNG and not presented in JSON.'.format(\n                        gt_ann['image_id'], label))\n            pred_segms[label]['area'] = label_cnt\n            pred_labels_set.remove(label)\n            if pred_segms[label]['category_id'] not in categories:\n                raise KeyError(\n                    'In the image with ID {} segment with ID {} has '\n                    'unknown category_id {}.'.format(\n                        gt_ann['image_id'], label,\n                        pred_segms[label]['category_id']))\n        if len(pred_labels_set) != 0:\n            raise KeyError(\n                'In the image with ID {} the following segment IDs {} '\n                'are presented in JSON and not presented in PNG.'.format(\n                    gt_ann['image_id'], list(pred_labels_set)))\n\n        # confusion matrix calculation\n        pan_gt_pred = pan_gt.astype(np.uint64) * OFFSET + pan_pred.astype(\n            np.uint64)\n        gt_pred_map = {}\n        labels, labels_cnt = np.unique(pan_gt_pred, return_counts=True)\n        for label, intersection in zip(labels, labels_cnt):\n            gt_id = label // OFFSET\n            pred_id = label % OFFSET\n            gt_pred_map[(gt_id, pred_id)] = intersection\n\n        # count all matched pairs\n        gt_matched = set()\n        pred_matched = set()\n        for label_tuple, intersection in gt_pred_map.items():\n            gt_label, pred_label = label_tuple\n            if gt_label not in gt_segms:\n                continue\n            if pred_label not in pred_segms:\n                continue\n            if gt_segms[gt_label]['iscrowd'] == 1:\n                continue\n            if gt_segms[gt_label]['category_id'] != pred_segms[pred_label][\n                    'category_id']:\n                continue\n\n            union = pred_segms[pred_label]['area'] + gt_segms[gt_label][\n                'area'] - intersection - gt_pred_map.get((VOID, pred_label), 0)\n            iou = intersection / union\n            if iou > 0.5:\n                pq_stat[gt_segms[gt_label]['category_id']].tp += 1\n                pq_stat[gt_segms[gt_label]['category_id']].iou += iou\n                gt_matched.add(gt_label)\n                pred_matched.add(pred_label)\n\n        # count false positives\n        crowd_labels_dict = {}\n        for gt_label, gt_info in gt_segms.items():\n            if gt_label in gt_matched:\n                continue\n            # crowd segments are ignored\n            if gt_info['iscrowd'] == 1:\n                crowd_labels_dict[gt_info['category_id']] = gt_label\n                continue\n            pq_stat[gt_info['category_id']].fn += 1\n\n        # count false positives\n        for pred_label, pred_info in pred_segms.items():\n            if pred_label in pred_matched:\n                continue\n            # intersection of the segment with VOID\n            intersection = gt_pred_map.get((VOID, pred_label), 0)\n            # plus intersection with corresponding CROWD region if it exists\n            if pred_info['category_id'] in crowd_labels_dict:\n                intersection += gt_pred_map.get(\n                    (crowd_labels_dict[pred_info['category_id']], pred_label),\n                    0)\n            # predicted segment is ignored if more than half of\n            # the segment correspond to VOID and CROWD regions\n            if intersection / pred_info['area'] > 0.5:\n                continue\n            pq_stat[pred_info['category_id']].fp += 1\n\n    if print_log:\n        print('Core: {}, all {} images processed'.format(\n            proc_id, len(annotation_set)))\n    return pq_stat\n\n\ndef pq_compute_multi_core(matched_annotations_list,\n                          gt_folder,\n                          pred_folder,\n                          categories,\n                          backend_args=None,\n                          nproc=32):\n    \"\"\"Evaluate the metrics of Panoptic Segmentation with multithreading.\n\n    Same as the function with the same name in `panopticapi`.\n\n    Args:\n        matched_annotations_list (list): The matched annotation list. Each\n            element is a tuple of annotations of the same image with the\n            format (gt_anns, pred_anns).\n        gt_folder (str): The path of the ground truth images.\n        pred_folder (str): The path of the prediction images.\n        categories (str): The categories of the dataset.\n        backend_args (object): The file client of the dataset. If None,\n            the backend will be set to `local`.\n        nproc (int): Number of processes for panoptic quality computing.\n            Defaults to 32. When `nproc` exceeds the number of cpu cores,\n            the number of cpu cores is used.\n    \"\"\"\n    if PQStat is None:\n        raise RuntimeError(\n            'panopticapi is not installed, please install it by: '\n            'pip install git+https://github.com/cocodataset/'\n            'panopticapi.git.')\n\n    cpu_num = min(nproc, multiprocessing.cpu_count())\n\n    annotations_split = np.array_split(matched_annotations_list, cpu_num)\n    print('Number of cores: {}, images per core: {}'.format(\n        cpu_num, len(annotations_split[0])))\n    workers = multiprocessing.Pool(processes=cpu_num)\n    processes = []\n    for proc_id, annotation_set in enumerate(annotations_split):\n        p = workers.apply_async(pq_compute_single_core,\n                                (proc_id, annotation_set, gt_folder,\n                                 pred_folder, categories, backend_args))\n        processes.append(p)\n\n    # Close the process pool, otherwise it will lead to memory\n    # leaking problems.\n    workers.close()\n    workers.join()\n\n    pq_stat = PQStat()\n    for p in processes:\n        pq_stat += p.get()\n\n    return pq_stat\n"
  },
  {
    "path": "mmdet/evaluation/functional/recall.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom collections.abc import Sequence\n\nimport numpy as np\nfrom mmengine.logging import print_log\nfrom terminaltables import AsciiTable\n\nfrom .bbox_overlaps import bbox_overlaps\n\n\ndef _recalls(all_ious, proposal_nums, thrs):\n\n    img_num = all_ious.shape[0]\n    total_gt_num = sum([ious.shape[0] for ious in all_ious])\n\n    _ious = np.zeros((proposal_nums.size, total_gt_num), dtype=np.float32)\n    for k, proposal_num in enumerate(proposal_nums):\n        tmp_ious = np.zeros(0)\n        for i in range(img_num):\n            ious = all_ious[i][:, :proposal_num].copy()\n            gt_ious = np.zeros((ious.shape[0]))\n            if ious.size == 0:\n                tmp_ious = np.hstack((tmp_ious, gt_ious))\n                continue\n            for j in range(ious.shape[0]):\n                gt_max_overlaps = ious.argmax(axis=1)\n                max_ious = ious[np.arange(0, ious.shape[0]), gt_max_overlaps]\n                gt_idx = max_ious.argmax()\n                gt_ious[j] = max_ious[gt_idx]\n                box_idx = gt_max_overlaps[gt_idx]\n                ious[gt_idx, :] = -1\n                ious[:, box_idx] = -1\n            tmp_ious = np.hstack((tmp_ious, gt_ious))\n        _ious[k, :] = tmp_ious\n\n    _ious = np.fliplr(np.sort(_ious, axis=1))\n    recalls = np.zeros((proposal_nums.size, thrs.size))\n    for i, thr in enumerate(thrs):\n        recalls[:, i] = (_ious >= thr).sum(axis=1) / float(total_gt_num)\n\n    return recalls\n\n\ndef set_recall_param(proposal_nums, iou_thrs):\n    \"\"\"Check proposal_nums and iou_thrs and set correct format.\"\"\"\n    if isinstance(proposal_nums, Sequence):\n        _proposal_nums = np.array(proposal_nums)\n    elif isinstance(proposal_nums, int):\n        _proposal_nums = np.array([proposal_nums])\n    else:\n        _proposal_nums = proposal_nums\n\n    if iou_thrs is None:\n        _iou_thrs = np.array([0.5])\n    elif isinstance(iou_thrs, Sequence):\n        _iou_thrs = np.array(iou_thrs)\n    elif isinstance(iou_thrs, float):\n        _iou_thrs = np.array([iou_thrs])\n    else:\n        _iou_thrs = iou_thrs\n\n    return _proposal_nums, _iou_thrs\n\n\ndef eval_recalls(gts,\n                 proposals,\n                 proposal_nums=None,\n                 iou_thrs=0.5,\n                 logger=None,\n                 use_legacy_coordinate=False):\n    \"\"\"Calculate recalls.\n\n    Args:\n        gts (list[ndarray]): a list of arrays of shape (n, 4)\n        proposals (list[ndarray]): a list of arrays of shape (k, 4) or (k, 5)\n        proposal_nums (int | Sequence[int]): Top N proposals to be evaluated.\n        iou_thrs (float | Sequence[float]): IoU thresholds. Default: 0.5.\n        logger (logging.Logger | str | None): The way to print the recall\n            summary. See `mmengine.logging.print_log()` for details.\n            Default: None.\n        use_legacy_coordinate (bool): Whether use coordinate system\n            in mmdet v1.x. \"1\" was added to both height and width\n            which means w, h should be\n            computed as 'x2 - x1 + 1` and 'y2 - y1 + 1'. Default: False.\n\n\n    Returns:\n        ndarray: recalls of different ious and proposal nums\n    \"\"\"\n\n    img_num = len(gts)\n    assert img_num == len(proposals)\n    proposal_nums, iou_thrs = set_recall_param(proposal_nums, iou_thrs)\n    all_ious = []\n    for i in range(img_num):\n        if proposals[i].ndim == 2 and proposals[i].shape[1] == 5:\n            scores = proposals[i][:, 4]\n            sort_idx = np.argsort(scores)[::-1]\n            img_proposal = proposals[i][sort_idx, :]\n        else:\n            img_proposal = proposals[i]\n        prop_num = min(img_proposal.shape[0], proposal_nums[-1])\n        if gts[i] is None or gts[i].shape[0] == 0:\n            ious = np.zeros((0, img_proposal.shape[0]), dtype=np.float32)\n        else:\n            ious = bbox_overlaps(\n                gts[i],\n                img_proposal[:prop_num, :4],\n                use_legacy_coordinate=use_legacy_coordinate)\n        all_ious.append(ious)\n    all_ious = np.array(all_ious)\n    recalls = _recalls(all_ious, proposal_nums, iou_thrs)\n\n    print_recall_summary(recalls, proposal_nums, iou_thrs, logger=logger)\n    return recalls\n\n\ndef print_recall_summary(recalls,\n                         proposal_nums,\n                         iou_thrs,\n                         row_idxs=None,\n                         col_idxs=None,\n                         logger=None):\n    \"\"\"Print recalls in a table.\n\n    Args:\n        recalls (ndarray): calculated from `bbox_recalls`\n        proposal_nums (ndarray or list): top N proposals\n        iou_thrs (ndarray or list): iou thresholds\n        row_idxs (ndarray): which rows(proposal nums) to print\n        col_idxs (ndarray): which cols(iou thresholds) to print\n        logger (logging.Logger | str | None): The way to print the recall\n            summary. See `mmengine.logging.print_log()` for details.\n            Default: None.\n    \"\"\"\n    proposal_nums = np.array(proposal_nums, dtype=np.int32)\n    iou_thrs = np.array(iou_thrs)\n    if row_idxs is None:\n        row_idxs = np.arange(proposal_nums.size)\n    if col_idxs is None:\n        col_idxs = np.arange(iou_thrs.size)\n    row_header = [''] + iou_thrs[col_idxs].tolist()\n    table_data = [row_header]\n    for i, num in enumerate(proposal_nums[row_idxs]):\n        row = [f'{val:.3f}' for val in recalls[row_idxs[i], col_idxs].tolist()]\n        row.insert(0, num)\n        table_data.append(row)\n    table = AsciiTable(table_data)\n    print_log('\\n' + table.table, logger=logger)\n\n\ndef plot_num_recall(recalls, proposal_nums):\n    \"\"\"Plot Proposal_num-Recalls curve.\n\n    Args:\n        recalls(ndarray or list): shape (k,)\n        proposal_nums(ndarray or list): same shape as `recalls`\n    \"\"\"\n    if isinstance(proposal_nums, np.ndarray):\n        _proposal_nums = proposal_nums.tolist()\n    else:\n        _proposal_nums = proposal_nums\n    if isinstance(recalls, np.ndarray):\n        _recalls = recalls.tolist()\n    else:\n        _recalls = recalls\n\n    import matplotlib.pyplot as plt\n    f = plt.figure()\n    plt.plot([0] + _proposal_nums, [0] + _recalls)\n    plt.xlabel('Proposal num')\n    plt.ylabel('Recall')\n    plt.axis([0, proposal_nums.max(), 0, 1])\n    f.show()\n\n\ndef plot_iou_recall(recalls, iou_thrs):\n    \"\"\"Plot IoU-Recalls curve.\n\n    Args:\n        recalls(ndarray or list): shape (k,)\n        iou_thrs(ndarray or list): same shape as `recalls`\n    \"\"\"\n    if isinstance(iou_thrs, np.ndarray):\n        _iou_thrs = iou_thrs.tolist()\n    else:\n        _iou_thrs = iou_thrs\n    if isinstance(recalls, np.ndarray):\n        _recalls = recalls.tolist()\n    else:\n        _recalls = recalls\n\n    import matplotlib.pyplot as plt\n    f = plt.figure()\n    plt.plot(_iou_thrs + [1.0], _recalls + [0.])\n    plt.xlabel('IoU')\n    plt.ylabel('Recall')\n    plt.axis([iou_thrs.min(), 1, 0, 1])\n    f.show()\n"
  },
  {
    "path": "mmdet/evaluation/functional/ytvis.py",
    "content": "# Copyright (c) Github URL\n# Copied from\n# https://github.com/youtubevos/cocoapi/blob/master/PythonAPI/pycocotools/ytvos.py\n__author__ = 'ychfan'\n# Interface for accessing the YouTubeVIS dataset.\n\n# The following API functions are defined:\n#  YTVIS       - YTVIS api class that loads YouTubeVIS annotation file\n#  and prepare data structures.\n#  decodeMask - Decode binary mask M encoded via run-length encoding.\n#  encodeMask - Encode binary mask M using run-length encoding.\n#  getAnnIds  - Get ann ids that satisfy given filter conditions.\n#  getCatIds  - Get cat ids that satisfy given filter conditions.\n#  getImgIds  - Get img ids that satisfy given filter conditions.\n#  loadAnns   - Load anns with the specified ids.\n#  loadCats   - Load cats with the specified ids.\n#  loadImgs   - Load imgs with the specified ids.\n#  annToMask  - Convert segmentation in an annotation to binary mask.\n#  loadRes    - Load algorithm results and create API for accessing them.\n\n# Microsoft COCO Toolbox.      version 2.0\n# Data, paper, and tutorials available at:  http://mscoco.org/\n# Code written by Piotr Dollar and Tsung-Yi Lin, 2014.\n# Licensed under the Simplified BSD License [see bsd.txt]\n\nimport copy\nimport itertools\nimport json\nimport sys\nimport time\nfrom collections import defaultdict\n\nimport numpy as np\nfrom pycocotools import mask as maskUtils\n\nPYTHON_VERSION = sys.version_info[0]\n\n\ndef _isArrayLike(obj):\n    return hasattr(obj, '__iter__') and hasattr(obj, '__len__')\n\n\nclass YTVIS:\n\n    def __init__(self, annotation_file=None):\n        \"\"\"Constructor of Microsoft COCO helper class for reading and\n        visualizing annotations.\n\n        :param annotation_file (str | dict): location of annotation file or\n            dict results.\n        :param image_folder (str): location to the folder that hosts images.\n        :return:\n        \"\"\"\n        # load dataset\n        self.dataset, self.anns, self.cats, self.vids = dict(), dict(), dict(\n        ), dict()\n        self.vidToAnns, self.catToVids = defaultdict(list), defaultdict(list)\n        if annotation_file is not None:\n            print('loading annotations into memory...')\n            tic = time.time()\n            if type(annotation_file) == str:\n                dataset = json.load(open(annotation_file, 'r'))\n            else:\n                dataset = annotation_file\n            assert type(\n                dataset\n            ) == dict, 'annotation file format {} not supported'.format(\n                type(dataset))\n            print('Done (t={:0.2f}s)'.format(time.time() - tic))\n            self.dataset = dataset\n            self.createIndex()\n\n    def createIndex(self):\n        # create index\n        print('creating index...')\n        anns, cats, vids = {}, {}, {}\n        vidToAnns, catToVids = defaultdict(list), defaultdict(list)\n        if 'annotations' in self.dataset:\n            for ann in self.dataset['annotations']:\n                vidToAnns[ann['video_id']].append(ann)\n                anns[ann['id']] = ann\n\n        if 'videos' in self.dataset:\n            for vid in self.dataset['videos']:\n                vids[vid['id']] = vid\n\n        if 'categories' in self.dataset:\n            for cat in self.dataset['categories']:\n                cats[cat['id']] = cat\n\n        if 'annotations' in self.dataset and 'categories' in self.dataset:\n            for ann in self.dataset['annotations']:\n                catToVids[ann['category_id']].append(ann['video_id'])\n\n        print('index created!')\n\n        # create class members\n        self.anns = anns\n        self.vidToAnns = vidToAnns\n        self.catToVids = catToVids\n        self.vids = vids\n        self.cats = cats\n\n    def getAnnIds(self, vidIds=[], catIds=[], areaRng=[], iscrowd=None):\n        \"\"\"Get ann ids that satisfy given filter conditions. default skips that\n        filter.\n\n        :param vidIds  (int array)     : get anns for given vids\n               catIds  (int array)     : get anns for given cats\n               areaRng (float array)   : get anns for given area range\n               iscrowd (boolean)       : get anns for given crowd label\n        :return: ids (int array)       : integer array of ann ids\n        \"\"\"\n        vidIds = vidIds if _isArrayLike(vidIds) else [vidIds]\n        catIds = catIds if _isArrayLike(catIds) else [catIds]\n\n        if len(vidIds) == len(catIds) == len(areaRng) == 0:\n            anns = self.dataset['annotations']\n        else:\n            if not len(vidIds) == 0:\n                lists = [\n                    self.vidToAnns[vidId] for vidId in vidIds\n                    if vidId in self.vidToAnns\n                ]\n                anns = list(itertools.chain.from_iterable(lists))\n            else:\n                anns = self.dataset['annotations']\n            anns = anns if len(catIds) == 0 else [\n                ann for ann in anns if ann['category_id'] in catIds\n            ]\n            anns = anns if len(areaRng) == 0 else [\n                ann for ann in anns if ann['avg_area'] > areaRng[0]\n                and ann['avg_area'] < areaRng[1]\n            ]\n        if iscrowd is not None:\n            ids = [ann['id'] for ann in anns if ann['iscrowd'] == iscrowd]\n        else:\n            ids = [ann['id'] for ann in anns]\n        return ids\n\n    def getCatIds(self, catNms=[], supNms=[], catIds=[]):\n        \"\"\"filtering parameters. default skips that filter.\n\n        :param catNms (str array)  : get cats for given cat names\n        :param supNms (str array)  : get cats for given supercategory names\n        :param catIds (int array)  : get cats for given cat ids\n        :return: ids (int array)   : integer array of cat ids\n        \"\"\"\n        catNms = catNms if _isArrayLike(catNms) else [catNms]\n        supNms = supNms if _isArrayLike(supNms) else [supNms]\n        catIds = catIds if _isArrayLike(catIds) else [catIds]\n\n        if len(catNms) == len(supNms) == len(catIds) == 0:\n            cats = self.dataset['categories']\n        else:\n            cats = self.dataset['categories']\n            cats = cats if len(catNms) == 0 else [\n                cat for cat in cats if cat['name'] in catNms\n            ]\n            cats = cats if len(supNms) == 0 else [\n                cat for cat in cats if cat['supercategory'] in supNms\n            ]\n            cats = cats if len(catIds) == 0 else [\n                cat for cat in cats if cat['id'] in catIds\n            ]\n        ids = [cat['id'] for cat in cats]\n        return ids\n\n    def getVidIds(self, vidIds=[], catIds=[]):\n        \"\"\"Get vid ids that satisfy given filter conditions.\n\n        :param vidIds (int array) : get vids for given ids\n        :param catIds (int array) : get vids with all given cats\n        :return: ids (int array)  : integer array of vid ids\n        \"\"\"\n        vidIds = vidIds if _isArrayLike(vidIds) else [vidIds]\n        catIds = catIds if _isArrayLike(catIds) else [catIds]\n\n        if len(vidIds) == len(catIds) == 0:\n            ids = self.vids.keys()\n        else:\n            ids = set(vidIds)\n            for i, catId in enumerate(catIds):\n                if i == 0 and len(ids) == 0:\n                    ids = set(self.catToVids[catId])\n                else:\n                    ids &= set(self.catToVids[catId])\n        return list(ids)\n\n    def loadAnns(self, ids=[]):\n        \"\"\"Load anns with the specified ids.\n\n        :param ids (int array)       : integer ids specifying anns\n        :return: anns (object array) : loaded ann objects\n        \"\"\"\n        if _isArrayLike(ids):\n            return [self.anns[id] for id in ids]\n        elif type(ids) == int:\n            return [self.anns[ids]]\n\n    def loadCats(self, ids=[]):\n        \"\"\"Load cats with the specified ids.\n\n        :param ids (int array)       : integer ids specifying cats\n        :return: cats (object array) : loaded cat objects\n        \"\"\"\n        if _isArrayLike(ids):\n            return [self.cats[id] for id in ids]\n        elif type(ids) == int:\n            return [self.cats[ids]]\n\n    def loadVids(self, ids=[]):\n        \"\"\"Load anns with the specified ids.\n\n        :param ids (int array)       : integer ids specifying vid\n        :return: vids (object array) : loaded vid objects\n        \"\"\"\n        if _isArrayLike(ids):\n            return [self.vids[id] for id in ids]\n        elif type(ids) == int:\n            return [self.vids[ids]]\n\n    def loadRes(self, resFile):\n        \"\"\"Load result file and return a result api object.\n\n        :param   resFile (str)     : file name of result file\n        :return: res (obj)         : result api object\n        \"\"\"\n        res = YTVIS()\n        res.dataset['videos'] = [img for img in self.dataset['videos']]\n\n        print('Loading and preparing results...')\n        tic = time.time()\n        if type(resFile) == str or (PYTHON_VERSION == 2\n                                    and type(resFile) == str):\n            anns = json.load(open(resFile))\n        elif type(resFile) == np.ndarray:\n            anns = self.loadNumpyAnnotations(resFile)\n        else:\n            anns = resFile\n        assert type(anns) == list, 'results in not an array of objects'\n        annsVidIds = [ann['video_id'] for ann in anns]\n        assert set(annsVidIds) == (set(annsVidIds) & set(self.getVidIds())), \\\n               'Results do not correspond to current coco set'\n        if 'segmentations' in anns[0]:\n            res.dataset['categories'] = copy.deepcopy(\n                self.dataset['categories'])\n            for id, ann in enumerate(anns):\n                ann['areas'] = []\n                if 'bboxes' not in ann:\n                    ann['bboxes'] = []\n                for seg in ann['segmentations']:\n                    # now only support compressed RLE format\n                    # as segmentation results\n                    if seg:\n                        ann['areas'].append(maskUtils.area(seg))\n                        if len(ann['bboxes']) < len(ann['areas']):\n                            ann['bboxes'].append(maskUtils.toBbox(seg))\n                    else:\n                        ann['areas'].append(None)\n                        if len(ann['bboxes']) < len(ann['areas']):\n                            ann['bboxes'].append(None)\n                ann['id'] = id + 1\n                l_ori = [a for a in ann['areas'] if a]\n                if len(l_ori) == 0:\n                    ann['avg_area'] = 0\n                else:\n                    ann['avg_area'] = np.array(l_ori).mean()\n                ann['iscrowd'] = 0\n        print('DONE (t={:0.2f}s)'.format(time.time() - tic))\n\n        res.dataset['annotations'] = anns\n        res.createIndex()\n        return res\n\n    def annToRLE(self, ann, frameId):\n        \"\"\"Convert annotation which can be polygons, uncompressed RLE to RLE.\n\n        :return: binary mask (numpy 2D array)\n        \"\"\"\n        t = self.vids[ann['video_id']]\n        h, w = t['height'], t['width']\n        segm = ann['segmentations'][frameId]\n        if type(segm) == list:\n            # polygon -- a single object might consist of multiple parts\n            # we merge all parts into one mask rle code\n            rles = maskUtils.frPyObjects(segm, h, w)\n            rle = maskUtils.merge(rles)\n        elif type(segm['counts']) == list:\n            # uncompressed RLE\n            rle = maskUtils.frPyObjects(segm, h, w)\n        else:\n            # rle\n            rle = segm\n        return rle\n\n    def annToMask(self, ann, frameId):\n        \"\"\"Convert annotation which can be polygons, uncompressed RLE, or RLE\n        to binary mask.\n\n        :return: binary mask (numpy 2D array)\n        \"\"\"\n        rle = self.annToRLE(ann, frameId)\n        m = maskUtils.decode(rle)\n        return m\n"
  },
  {
    "path": "mmdet/evaluation/functional/ytviseval.py",
    "content": "# Copyright (c) Github URL\n# Copied from\n# https://github.com/youtubevos/cocoapi/blob/master/PythonAPI/pycocotools/ytvoseval.py\n__author__ = 'ychfan'\n\nimport copy\nimport datetime\nimport time\nfrom collections import defaultdict\n\nimport numpy as np\nfrom pycocotools import mask as maskUtils\n\n\nclass YTVISeval:\n    # Interface for evaluating video instance segmentation on\n    # the YouTubeVIS dataset.\n    #\n    # The usage for YTVISeval is as follows:\n    #  cocoGt=..., cocoDt=...       # load dataset and results\n    #  E = YTVISeval(cocoGt,cocoDt); # initialize YTVISeval object\n    #  E.params.recThrs = ...;      # set parameters as desired\n    #  E.evaluate();                # run per image evaluation\n    #  E.accumulate();              # accumulate per image results\n    #  E.summarize();               # display summary metrics of results\n    # For example usage see evalDemo.m and http://mscoco.org/.\n    #\n    # The evaluation parameters are as follows (defaults in brackets):\n    #  imgIds     - [all] N img ids to use for evaluation\n    #  catIds     - [all] K cat ids to use for evaluation\n    #  iouThrs    - [.5:.05:.95] T=10 IoU thresholds for evaluation\n    #  recThrs    - [0:.01:1] R=101 recall thresholds for evaluation\n    #  areaRng    - [...] A=4 object area ranges for evaluation\n    #  maxDets    - [1 10 100] M=3 thresholds on max detections per image\n    #  iouType    - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints'\n    #  iouType replaced the now DEPRECATED useSegm parameter.\n    #  useCats    - [1] if true use category labels for evaluation\n    # Note: if useCats=0 category labels are ignored as in proposal scoring.\n    # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified.\n    #\n    # evaluate(): evaluates detections on every image and every category and\n    # concats the results into the \"evalImgs\" with fields:\n    #  dtIds      - [1xD] id for each of the D detections (dt)\n    #  gtIds      - [1xG] id for each of the G ground truths (gt)\n    #  dtMatches  - [TxD] matching gt id at each IoU or 0\n    #  gtMatches  - [TxG] matching dt id at each IoU or 0\n    #  dtScores   - [1xD] confidence of each dt\n    #  gtIgnore   - [1xG] ignore flag for each gt\n    #  dtIgnore   - [TxD] ignore flag for each dt at each IoU\n    #\n    # accumulate(): accumulates the per-image, per-category evaluation\n    # results in \"evalImgs\" into the dictionary \"eval\" with fields:\n    #  params     - parameters used for evaluation\n    #  date       - date evaluation was performed\n    #  counts     - [T,R,K,A,M] parameter dimensions (see above)\n    #  precision  - [TxRxKxAxM] precision for every evaluation setting\n    #  recall     - [TxKxAxM] max recall for every evaluation setting\n    # Note: precision and recall==-1 for settings with no gt objects.\n    #\n    # See also coco, mask, pycocoDemo, pycocoEvalDemo\n    #\n    # Microsoft COCO Toolbox.      version 2.0\n    # Data, paper, and tutorials available at:  http://mscoco.org/\n    # Code written by Piotr Dollar and Tsung-Yi Lin, 2015.\n    # Licensed under the Simplified BSD License [see coco/license.txt]\n    def __init__(self, cocoGt=None, cocoDt=None, iouType='segm'):\n        \"\"\"Initialize CocoEval using coco APIs for gt and dt.\n\n        :param cocoGt: coco object with ground truth annotations\n        :param cocoDt: coco object with detection results\n        :return: None\n        \"\"\"\n        if not iouType:\n            print('iouType not specified. use default iouType segm')\n        self.cocoGt = cocoGt  # ground truth COCO API\n        self.cocoDt = cocoDt  # detections COCO API\n        self.params = {}  # evaluation parameters\n        self.evalVids = defaultdict(\n            list)  # per-image per-category evaluation results [KxAxI] elements\n        self.eval = {}  # accumulated evaluation results\n        self._gts = defaultdict(list)  # gt for evaluation\n        self._dts = defaultdict(list)  # dt for evaluation\n        self.params = Params(iouType=iouType)  # parameters\n        self._paramsEval = {}  # parameters for evaluation\n        self.stats = []  # result summarization\n        self.ious = {}  # ious between all gts and dts\n        if cocoGt is not None:\n            self.params.vidIds = sorted(cocoGt.getVidIds())\n            self.params.catIds = sorted(cocoGt.getCatIds())\n\n    def _prepare(self):\n        '''\n        Prepare ._gts and ._dts for evaluation based on params\n        :return: None\n        '''\n\n        def _toMask(anns, coco):\n            # modify ann['segmentation'] by reference\n            for ann in anns:\n                for i, a in enumerate(ann['segmentations']):\n                    if a:\n                        rle = coco.annToRLE(ann, i)\n                        ann['segmentations'][i] = rle\n                l_ori = [a for a in ann['areas'] if a]\n                if len(l_ori) == 0:\n                    ann['avg_area'] = 0\n                else:\n                    ann['avg_area'] = np.array(l_ori).mean()\n\n        p = self.params\n        if p.useCats:\n            gts = self.cocoGt.loadAnns(\n                self.cocoGt.getAnnIds(vidIds=p.vidIds, catIds=p.catIds))\n            dts = self.cocoDt.loadAnns(\n                self.cocoDt.getAnnIds(vidIds=p.vidIds, catIds=p.catIds))\n        else:\n            gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(vidIds=p.vidIds))\n            dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(vidIds=p.vidIds))\n\n        # convert ground truth to mask if iouType == 'segm'\n        if p.iouType == 'segm':\n            _toMask(gts, self.cocoGt)\n            _toMask(dts, self.cocoDt)\n        # set ignore flag\n        for gt in gts:\n            gt['ignore'] = gt['ignore'] if 'ignore' in gt else 0\n            gt['ignore'] = 'iscrowd' in gt and gt['iscrowd']\n            if p.iouType == 'keypoints':\n                gt['ignore'] = (gt['num_keypoints'] == 0) or gt['ignore']\n        self._gts = defaultdict(list)  # gt for evaluation\n        self._dts = defaultdict(list)  # dt for evaluation\n        for gt in gts:\n            self._gts[gt['video_id'], gt['category_id']].append(gt)\n        for dt in dts:\n            self._dts[dt['video_id'], dt['category_id']].append(dt)\n        self.evalVids = defaultdict(\n            list)  # per-image per-category evaluation results\n        self.eval = {}  # accumulated evaluation results\n\n    def evaluate(self):\n        '''\n        Run per image evaluation on given images and store\n        results (a list of dict) in self.evalVids\n        :return: None\n        '''\n        tic = time.time()\n        print('Running per image evaluation...')\n        p = self.params\n        # add backward compatibility if useSegm is specified in params\n        if p.useSegm is not None:\n            p.iouType = 'segm' if p.useSegm == 1 else 'bbox'\n            print('useSegm (deprecated) is not None. Running {} evaluation'.\n                  format(p.iouType))\n        print('Evaluate annotation type *{}*'.format(p.iouType))\n        p.vidIds = list(np.unique(p.vidIds))\n        if p.useCats:\n            p.catIds = list(np.unique(p.catIds))\n        p.maxDets = sorted(p.maxDets)\n        self.params = p\n\n        self._prepare()\n        # loop through images, area range, max detection number\n        catIds = p.catIds if p.useCats else [-1]\n\n        if p.iouType == 'segm' or p.iouType == 'bbox':\n            computeIoU = self.computeIoU\n        elif p.iouType == 'keypoints':\n            computeIoU = self.computeOks\n        self.ious = {(vidId, catId): computeIoU(vidId, catId)\n                     for vidId in p.vidIds for catId in catIds}\n\n        evaluateVid = self.evaluateVid\n        maxDet = p.maxDets[-1]\n\n        self.evalImgs = [\n            evaluateVid(vidId, catId, areaRng, maxDet) for catId in catIds\n            for areaRng in p.areaRng for vidId in p.vidIds\n        ]\n        self._paramsEval = copy.deepcopy(self.params)\n        toc = time.time()\n        print('DONE (t={:0.2f}s).'.format(toc - tic))\n\n    def computeIoU(self, vidId, catId):\n        p = self.params\n        if p.useCats:\n            gt = self._gts[vidId, catId]\n            dt = self._dts[vidId, catId]\n        else:\n            gt = [_ for cId in p.catIds for _ in self._gts[vidId, cId]]\n            dt = [_ for cId in p.catIds for _ in self._dts[vidId, cId]]\n        if len(gt) == 0 and len(dt) == 0:\n            return []\n        inds = np.argsort([-d['score'] for d in dt], kind='mergesort')\n        dt = [dt[i] for i in inds]\n        if len(dt) > p.maxDets[-1]:\n            dt = dt[0:p.maxDets[-1]]\n\n        if p.iouType == 'segm':\n            g = [g['segmentations'] for g in gt]\n            d = [d['segmentations'] for d in dt]\n        elif p.iouType == 'bbox':\n            g = [g['bboxes'] for g in gt]\n            d = [d['bboxes'] for d in dt]\n        else:\n            raise Exception('unknown iouType for iou computation')\n\n        # compute iou between each dt and gt region\n\n        def iou_seq(d_seq, g_seq):\n            i = .0\n            u = .0\n            for d, g in zip(d_seq, g_seq):\n                if d and g:\n                    i += maskUtils.area(maskUtils.merge([d, g], True))\n                    u += maskUtils.area(maskUtils.merge([d, g], False))\n                elif not d and g:\n                    u += maskUtils.area(g)\n                elif d and not g:\n                    u += maskUtils.area(d)\n            if not u > .0:\n                print('Mask sizes in video {} and category {} may not match!'.\n                      format(vidId, catId))\n            iou = i / u if u > .0 else .0\n            return iou\n\n        ious = np.zeros([len(d), len(g)])\n        for i, j in np.ndindex(ious.shape):\n            ious[i, j] = iou_seq(d[i], g[j])\n\n        return ious\n\n    def computeOks(self, imgId, catId):\n        p = self.params\n\n        gts = self._gts[imgId, catId]\n        dts = self._dts[imgId, catId]\n        inds = np.argsort([-d['score'] for d in dts], kind='mergesort')\n        dts = [dts[i] for i in inds]\n        if len(dts) > p.maxDets[-1]:\n            dts = dts[0:p.maxDets[-1]]\n        # if len(gts) == 0 and len(dts) == 0:\n        if len(gts) == 0 or len(dts) == 0:\n            return []\n        ious = np.zeros((len(dts), len(gts)))\n        sigmas = np.array([\n            .26, .25, .25, .35, .35, .79, .79, .72, .72, .62, .62, 1.07, 1.07,\n            .87, .87, .89, .89\n        ]) / 10.0\n        vars = (sigmas * 2)**2\n        k = len(sigmas)\n        # compute oks between each detection and ground truth object\n        for j, gt in enumerate(gts):\n            # create bounds for ignore regions(double the gt bbox)\n            g = np.array(gt['keypoints'])\n            xg = g[0::3]\n            yg = g[1::3]\n            vg = g[2::3]\n            k1 = np.count_nonzero(vg > 0)\n            bb = gt['bbox']\n            x0 = bb[0] - bb[2]\n            x1 = bb[0] + bb[2] * 2\n            y0 = bb[1] - bb[3]\n            y1 = bb[1] + bb[3] * 2\n            for i, dt in enumerate(dts):\n                d = np.array(dt['keypoints'])\n                xd = d[0::3]\n                yd = d[1::3]\n                if k1 > 0:\n                    # measure the per-keypoint distance if keypoints visible\n                    dx = xd - xg\n                    dy = yd - yg\n                else:\n                    # measure minimum distance to keypoints\n                    z = np.zeros((k))\n                    dx = np.max((z, x0 - xd), axis=0) + np.max(\n                        (z, xd - x1), axis=0)\n                    dy = np.max((z, y0 - yd), axis=0) + np.max(\n                        (z, yd - y1), axis=0)\n                e = (dx**2 + dy**2) / vars / (gt['avg_area'] +\n                                              np.spacing(1)) / 2\n                if k1 > 0:\n                    e = e[vg > 0]\n                ious[i, j] = np.sum(np.exp(-e)) / e.shape[0]\n        return ious\n\n    def evaluateVid(self, vidId, catId, aRng, maxDet):\n        '''\n        perform evaluation for single category and image\n        :return: dict (single image results)\n        '''\n        p = self.params\n        if p.useCats:\n            gt = self._gts[vidId, catId]\n            dt = self._dts[vidId, catId]\n        else:\n            gt = [_ for cId in p.catIds for _ in self._gts[vidId, cId]]\n            dt = [_ for cId in p.catIds for _ in self._dts[vidId, cId]]\n        if len(gt) == 0 and len(dt) == 0:\n            return None\n\n        for g in gt:\n            if g['ignore'] or (g['avg_area'] < aRng[0]\n                               or g['avg_area'] > aRng[1]):\n                g['_ignore'] = 1\n            else:\n                g['_ignore'] = 0\n\n        # sort dt highest score first, sort gt ignore last\n        gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort')\n        gt = [gt[i] for i in gtind]\n        dtind = np.argsort([-d['score'] for d in dt], kind='mergesort')\n        dt = [dt[i] for i in dtind[0:maxDet]]\n        iscrowd = [int(o['iscrowd']) for o in gt]\n        # load computed ious\n        ious = self.ious[vidId, catId][:, gtind] if len(\n            self.ious[vidId, catId]) > 0 else self.ious[vidId, catId]\n\n        T = len(p.iouThrs)\n        G = len(gt)\n        D = len(dt)\n        gtm = np.zeros((T, G))\n        dtm = np.zeros((T, D))\n        gtIg = np.array([g['_ignore'] for g in gt])\n        dtIg = np.zeros((T, D))\n        if not len(ious) == 0:\n            for tind, t in enumerate(p.iouThrs):\n                for dind, d in enumerate(dt):\n                    # information about best match so far (m=-1 -> unmatched)\n                    iou = min([t, 1 - 1e-10])\n                    m = -1\n                    for gind, g in enumerate(gt):\n                        # if this gt already matched, and not a crowd, continue\n                        if gtm[tind, gind] > 0 and not iscrowd[gind]:\n                            continue\n                        # if dt matched to reg gt, and on ignore gt, stop\n                        if m > -1 and gtIg[m] == 0 and gtIg[gind] == 1:\n                            break\n                        # continue to next gt unless better match made\n                        if ious[dind, gind] < iou:\n                            continue\n                        # if match successful and best so far,\n                        # store appropriately\n                        iou = ious[dind, gind]\n                        m = gind\n                    # if match made store id of match for both dt and gt\n                    if m == -1:\n                        continue\n                    dtIg[tind, dind] = gtIg[m]\n                    dtm[tind, dind] = gt[m]['id']\n                    gtm[tind, m] = d['id']\n        # set unmatched detections outside of area range to ignore\n        a = np.array([\n            d['avg_area'] < aRng[0] or d['avg_area'] > aRng[1] for d in dt\n        ]).reshape((1, len(dt)))\n        dtIg = np.logical_or(dtIg, np.logical_and(dtm == 0, np.repeat(a, T,\n                                                                      0)))\n        # store results for given image and category\n        return {\n            'video_id': vidId,\n            'category_id': catId,\n            'aRng': aRng,\n            'maxDet': maxDet,\n            'dtIds': [d['id'] for d in dt],\n            'gtIds': [g['id'] for g in gt],\n            'dtMatches': dtm,\n            'gtMatches': gtm,\n            'dtScores': [d['score'] for d in dt],\n            'gtIgnore': gtIg,\n            'dtIgnore': dtIg,\n        }\n\n    def accumulate(self, p=None):\n        \"\"\"Accumulate per image evaluation results and store the result in\n        self.eval.\n\n        :param p: input params for evaluation\n        :return: None\n        \"\"\"\n        print('Accumulating evaluation results...')\n        tic = time.time()\n        if not self.evalImgs:\n            print('Please run evaluate() first')\n        # allows input customized parameters\n        if p is None:\n            p = self.params\n        p.catIds = p.catIds if p.useCats == 1 else [-1]\n        T = len(p.iouThrs)\n        R = len(p.recThrs)\n        K = len(p.catIds) if p.useCats else 1\n        A = len(p.areaRng)\n        M = len(p.maxDets)\n        precision = -np.ones(\n            (T, R, K, A, M))  # -1 for the precision of absent categories\n        recall = -np.ones((T, K, A, M))\n        scores = -np.ones((T, R, K, A, M))\n\n        # create dictionary for future indexing\n        _pe = self._paramsEval\n        catIds = _pe.catIds if _pe.useCats else [-1]\n        setK = set(catIds)\n        setA = set(map(tuple, _pe.areaRng))\n        setM = set(_pe.maxDets)\n        setI = set(_pe.vidIds)\n        # get inds to evaluate\n        k_list = [n for n, k in enumerate(p.catIds) if k in setK]\n        m_list = [m for n, m in enumerate(p.maxDets) if m in setM]\n        a_list = [\n            n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng))\n            if a in setA\n        ]\n        i_list = [n for n, i in enumerate(p.vidIds) if i in setI]\n        I0 = len(_pe.vidIds)\n        A0 = len(_pe.areaRng)\n        # retrieve E at each category, area range, and max number of detections\n        for k, k0 in enumerate(k_list):\n            Nk = k0 * A0 * I0\n            for a, a0 in enumerate(a_list):\n                Na = a0 * I0\n                for m, maxDet in enumerate(m_list):\n                    E = [self.evalImgs[Nk + Na + i] for i in i_list]\n                    E = [e for e in E if e is not None]\n                    if len(E) == 0:\n                        continue\n                    dtScores = np.concatenate(\n                        [e['dtScores'][0:maxDet] for e in E])\n\n                    inds = np.argsort(-dtScores, kind='mergesort')\n                    dtScoresSorted = dtScores[inds]\n\n                    dtm = np.concatenate(\n                        [e['dtMatches'][:, 0:maxDet] for e in E], axis=1)[:,\n                                                                          inds]\n                    dtIg = np.concatenate(\n                        [e['dtIgnore'][:, 0:maxDet] for e in E], axis=1)[:,\n                                                                         inds]\n                    gtIg = np.concatenate([e['gtIgnore'] for e in E])\n                    npig = np.count_nonzero(gtIg == 0)\n                    if npig == 0:\n                        continue\n                    tps = np.logical_and(dtm, np.logical_not(dtIg))\n                    fps = np.logical_and(\n                        np.logical_not(dtm), np.logical_not(dtIg))\n\n                    tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float)\n                    fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float)\n                    for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)):\n                        tp = np.array(tp)\n                        fp = np.array(fp)\n                        nd_ori = len(tp)\n                        rc = tp / npig\n                        pr = tp / (fp + tp + np.spacing(1))\n                        q = np.zeros((R, ))\n                        ss = np.zeros((R, ))\n\n                        if nd_ori:\n                            recall[t, k, a, m] = rc[-1]\n                        else:\n                            recall[t, k, a, m] = 0\n\n                        # use python array gets significant speed improvement\n                        pr = pr.tolist()\n                        q = q.tolist()\n\n                        for i in range(nd_ori - 1, 0, -1):\n                            if pr[i] > pr[i - 1]:\n                                pr[i - 1] = pr[i]\n\n                        inds = np.searchsorted(rc, p.recThrs, side='left')\n                        try:\n                            for ri, pi in enumerate(inds):\n                                q[ri] = pr[pi]\n                                ss[ri] = dtScoresSorted[pi]\n                        except Exception:\n                            pass\n                        precision[t, :, k, a, m] = np.array(q)\n                        scores[t, :, k, a, m] = np.array(ss)\n        self.eval = {\n            'params': p,\n            'counts': [T, R, K, A, M],\n            'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),\n            'precision': precision,\n            'recall': recall,\n            'scores': scores,\n        }\n        toc = time.time()\n        print('DONE (t={:0.2f}s).'.format(toc - tic))\n\n    def summarize(self):\n        \"\"\"Compute and display summary metrics for evaluation results.\n\n        Note this function can *only* be applied on the default parameter\n        setting\n        \"\"\"\n\n        def _summarize(ap=1, iouThr=None, areaRng='all', maxDets=100):\n            p = self.params\n            iStr = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | ' \\\n                   'maxDets={:>3d} ] = {:0.3f}'\n            titleStr = 'Average Precision' if ap == 1 else 'Average Recall'\n            typeStr = '(AP)' if ap == 1 else '(AR)'\n            iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \\\n                if iouThr is None else '{:0.2f}'.format(iouThr)\n\n            aind = [\n                i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng\n            ]\n            mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets]\n            if ap == 1:\n                # dimension of precision: [TxRxKxAxM]\n                s = self.eval['precision']\n                # IoU\n                if iouThr is not None:\n                    t = np.where(iouThr == p.iouThrs)[0]\n                    s = s[t]\n                s = s[:, :, :, aind, mind]\n            else:\n                # dimension of recall: [TxKxAxM]\n                s = self.eval['recall']\n                if iouThr is not None:\n                    t = np.where(iouThr == p.iouThrs)[0]\n                    s = s[t]\n                s = s[:, :, aind, mind]\n            if len(s[s > -1]) == 0:\n                mean_s = -1\n            else:\n                mean_s = np.mean(s[s > -1])\n            print(\n                iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets,\n                            mean_s))\n            return mean_s\n\n        def _summarizeDets():\n            stats = np.zeros((12, ))\n            stats[0] = _summarize(1)\n            stats[1] = _summarize(1, iouThr=.5, maxDets=self.params.maxDets[2])\n            stats[2] = _summarize(\n                1, iouThr=.75, maxDets=self.params.maxDets[2])\n            stats[3] = _summarize(\n                1, areaRng='small', maxDets=self.params.maxDets[2])\n            stats[4] = _summarize(\n                1, areaRng='medium', maxDets=self.params.maxDets[2])\n            stats[5] = _summarize(\n                1, areaRng='large', maxDets=self.params.maxDets[2])\n            stats[6] = _summarize(0, maxDets=self.params.maxDets[0])\n            stats[7] = _summarize(0, maxDets=self.params.maxDets[1])\n            stats[8] = _summarize(0, maxDets=self.params.maxDets[2])\n            stats[9] = _summarize(\n                0, areaRng='small', maxDets=self.params.maxDets[2])\n            stats[10] = _summarize(\n                0, areaRng='medium', maxDets=self.params.maxDets[2])\n            stats[11] = _summarize(\n                0, areaRng='large', maxDets=self.params.maxDets[2])\n            return stats\n\n        def _summarizeKps():\n            stats = np.zeros((10, ))\n            stats[0] = _summarize(1, maxDets=20)\n            stats[1] = _summarize(1, maxDets=20, iouThr=.5)\n            stats[2] = _summarize(1, maxDets=20, iouThr=.75)\n            stats[3] = _summarize(1, maxDets=20, areaRng='medium')\n            stats[4] = _summarize(1, maxDets=20, areaRng='large')\n            stats[5] = _summarize(0, maxDets=20)\n            stats[6] = _summarize(0, maxDets=20, iouThr=.5)\n            stats[7] = _summarize(0, maxDets=20, iouThr=.75)\n            stats[8] = _summarize(0, maxDets=20, areaRng='medium')\n            stats[9] = _summarize(0, maxDets=20, areaRng='large')\n            return stats\n\n        if not self.eval:\n            raise Exception('Please run accumulate() first')\n        iouType = self.params.iouType\n        if iouType == 'segm' or iouType == 'bbox':\n            summarize = _summarizeDets\n        elif iouType == 'keypoints':\n            summarize = _summarizeKps\n        self.stats = summarize()\n\n    def __str__(self):\n        self.summarize()\n\n\nclass Params:\n    \"\"\"Params for coco evaluation api.\"\"\"\n\n    def setDetParams(self):\n        self.vidIds = []\n        self.catIds = []\n        # np.arange causes trouble.  the data point on arange\n        # is slightly larger than the true value\n        self.iouThrs = np.linspace(\n            .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)\n        self.recThrs = np.linspace(\n            .0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True)\n        self.maxDets = [1, 10, 100]\n        self.areaRng = [[0**2, 1e5**2], [0**2, 128**2], [128**2, 256**2],\n                        [256**2, 1e5**2]]\n        self.areaRngLbl = ['all', 'small', 'medium', 'large']\n        self.useCats = 1\n\n    def setKpParams(self):\n        self.vidIds = []\n        self.catIds = []\n        # np.arange causes trouble.  the data point on arange\n        # is slightly larger than the true value\n        self.iouThrs = np.linspace(\n            .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)\n        self.recThrs = np.linspace(\n            .0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True)\n        self.maxDets = [20]\n        self.areaRng = [[0**2, 1e5**2], [32**2, 96**2], [96**2, 1e5**2]]\n        self.areaRngLbl = ['all', 'medium', 'large']\n        self.useCats = 1\n\n    def __init__(self, iouType='segm'):\n        if iouType == 'segm' or iouType == 'bbox':\n            self.setDetParams()\n        elif iouType == 'keypoints':\n            self.setKpParams()\n        else:\n            raise Exception('iouType not supported')\n        self.iouType = iouType\n        # useSegm is deprecated\n        self.useSegm = None\n"
  },
  {
    "path": "mmdet/evaluation/metrics/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_video_metric import BaseVideoMetric\nfrom .cityscapes_metric import CityScapesMetric\nfrom .coco_caption_metric import COCOCaptionMetric\nfrom .coco_metric import CocoMetric\nfrom .coco_occluded_metric import CocoOccludedSeparatedMetric\nfrom .coco_panoptic_metric import CocoPanopticMetric\nfrom .coco_video_metric import CocoVideoMetric\nfrom .crowdhuman_metric import CrowdHumanMetric\nfrom .dod_metric import DODCocoMetric\nfrom .dump_det_results import DumpDetResults\nfrom .dump_odvg_results import DumpODVGResults\nfrom .dump_proposals_metric import DumpProposals\nfrom .flickr30k_metric import Flickr30kMetric\nfrom .grefcoco_metric import gRefCOCOMetric\nfrom .lvis_metric import LVISMetric\nfrom .mot_challenge_metric import MOTChallengeMetric\nfrom .openimages_metric import OpenImagesMetric\nfrom .ov_coco_metric import OVCocoMetric\nfrom .refexp_metric import RefExpMetric\nfrom .refseg_metric import RefSegMetric\nfrom .reid_metric import ReIDMetrics\nfrom .semseg_metric import SemSegMetric\nfrom .voc_metric import VOCMetric\nfrom .youtube_vis_metric import YouTubeVISMetric\n\n__all__ = [\n    'CityScapesMetric', 'CocoMetric', 'CocoPanopticMetric', 'OpenImagesMetric',\n    'VOCMetric', 'LVISMetric', 'CrowdHumanMetric', 'DumpProposals',\n    'CocoOccludedSeparatedMetric', 'DumpDetResults', 'BaseVideoMetric',\n    'MOTChallengeMetric', 'CocoVideoMetric', 'ReIDMetrics', 'YouTubeVISMetric',\n    'COCOCaptionMetric', 'SemSegMetric', 'RefSegMetric', 'RefExpMetric',\n    'gRefCOCOMetric', 'DODCocoMetric', 'DumpODVGResults', 'Flickr30kMetric',\n    'OVCocoMetric'\n]\n"
  },
  {
    "path": "mmdet/evaluation/metrics/base_video_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport pickle\nimport shutil\nimport tempfile\nimport warnings\nfrom typing import Optional, Sequence\n\nimport torch\nfrom mmengine.dist import (barrier, broadcast, broadcast_object_list,\n                           get_dist_info, is_main_process)\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.utils import mkdir_or_exist\n\n\nclass BaseVideoMetric(BaseMetric):\n    \"\"\"Base class for a metric in video task.\n\n    The metric first processes each batch of data_samples and predictions,\n    and appends the processed results to the results list. Then it\n    collects all results together from all ranks if distributed training\n    is used. Finally, it computes the metrics of the entire dataset.\n\n    A subclass of class:`BaseVideoMetric` should assign a meaningful value\n    to the class attribute `default_prefix`. See the argument `prefix` for\n    details.\n    \"\"\"\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions.\n\n        The processed results should be stored in ``self.results``, which will\n        be used to compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for track_data_sample in data_samples:\n            video_data_samples = track_data_sample['video_data_samples']\n            ori_video_len = video_data_samples[0].ori_video_length\n            if ori_video_len == len(video_data_samples):\n                # video process\n                self.process_video(video_data_samples)\n            else:\n                # image process\n                self.process_image(video_data_samples, ori_video_len)\n\n    def evaluate(self, size: int = 1) -> dict:\n        \"\"\"Evaluate the model performance of the whole dataset after processing\n        all batches.\n\n        Args:\n            size (int): Length of the entire validation dataset.\n\n        Returns:\n            dict: Evaluation metrics dict on the val dataset. The keys are the\n            names of the metrics, and the values are corresponding results.\n        \"\"\"\n        if len(self.results) == 0:\n            warnings.warn(\n                f'{self.__class__.__name__} got empty `self.results`. Please '\n                'ensure that the processed results are properly added into '\n                '`self.results` in `process` method.')\n\n        results = collect_tracking_results(self.results, self.collect_device)\n\n        if is_main_process():\n            _metrics = self.compute_metrics(results)  # type: ignore\n            # Add prefix to metric names\n            if self.prefix:\n                _metrics = {\n                    '/'.join((self.prefix, k)): v\n                    for k, v in _metrics.items()\n                }\n            metrics = [_metrics]\n        else:\n            metrics = [None]  # type: ignore\n\n        broadcast_object_list(metrics)\n\n        # reset the results list\n        self.results.clear()\n        return metrics[0]\n\n\ndef collect_tracking_results(results: list,\n                             device: str = 'cpu',\n                             tmpdir: Optional[str] = None) -> Optional[list]:\n    \"\"\"Collected results in distributed environments. different from the\n    function mmengine.dist.collect_results, tracking compute metrics don't use\n    paramenter size, which means length of the entire validation dataset.\n    because it's equal to video num, but compute metrics need image num.\n\n    Args:\n        results (list): Result list containing result parts to be\n            collected. Each item of ``result_part`` should be a picklable\n            object.\n        device (str): Device name. Optional values are 'cpu' and 'gpu'.\n        tmpdir (str | None): Temporal directory for collected results to\n            store. If set to None, it will create a temporal directory for it.\n            ``tmpdir`` should be None when device is 'gpu'. Defaults to None.\n\n    Returns:\n        list or None: The collected results.\n    \"\"\"\n    if device not in ['gpu', 'cpu']:\n        raise NotImplementedError(\n            f\"device must be 'cpu' or 'gpu', but got {device}\")\n\n    if device == 'gpu':\n        assert tmpdir is None, 'tmpdir should be None when device is \"gpu\"'\n        raise NotImplementedError('GPU collecting has not been supported yet')\n    else:\n        return collect_tracking_results_cpu(results, tmpdir)\n\n\ndef collect_tracking_results_cpu(result_part: list,\n                                 tmpdir: Optional[str] = None\n                                 ) -> Optional[list]:\n    \"\"\"Collect results on cpu mode.\n\n    Saves the results on different gpus to 'tmpdir' and collects them by the\n    rank 0 worker.\n\n    Args:\n        result_part (list): The part of prediction results.\n        tmpdir (str): Path of directory to save the temporary results from\n            different gpus under cpu mode. If is None, use `tempfile.mkdtemp()`\n            to make a temporary path. Defaults to None.\n\n    Returns:\n        list or None: The collected results.\n    \"\"\"\n    rank, world_size = get_dist_info()\n    if world_size == 1:\n        return result_part\n\n    # create a tmp dir if it is not specified\n    if tmpdir is None:\n        MAX_LEN = 512\n        # 32 is whitespace\n        dir_tensor = torch.full((MAX_LEN, ), 32, dtype=torch.uint8)\n        if rank == 0:\n            mkdir_or_exist('.dist_test')\n            tmpdir = tempfile.mkdtemp(dir='.dist_test')\n            tmpdir = torch.tensor(\n                bytearray(tmpdir.encode()), dtype=torch.uint8)\n            dir_tensor[:len(tmpdir)] = tmpdir\n        broadcast(dir_tensor, 0)\n        tmpdir = dir_tensor.numpy().tobytes().decode().rstrip()\n    else:\n        mkdir_or_exist(tmpdir)\n\n    # dump the part result to the dir\n    with open(osp.join(tmpdir, f'part_{rank}.pkl'), 'wb') as f:  # type: ignore\n        pickle.dump(result_part, f, protocol=2)\n\n    barrier()\n\n    # collect all parts\n    if rank != 0:\n        return None\n    else:\n        # load results of all parts from tmp dir\n        part_list = []\n        for i in range(world_size):\n            path = osp.join(tmpdir, f'part_{i}.pkl')  # type: ignore\n            with open(path, 'rb') as f:\n                part_list.extend(pickle.load(f))\n        shutil.rmtree(tmpdir)\n        return part_list\n"
  },
  {
    "path": "mmdet/evaluation/metrics/cityscapes_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport os.path as osp\nimport shutil\nimport tempfile\nfrom collections import OrderedDict\nfrom typing import Dict, Optional, Sequence\n\nimport mmcv\nimport numpy as np\nfrom mmengine.dist import is_main_process\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.registry import METRICS\n\ntry:\n    import cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling as CSEval  # noqa: E501\n    import cityscapesscripts.helpers.labels as CSLabels\n\n    from mmdet.evaluation.functional import evaluateImgLists\n    HAS_CITYSCAPESAPI = True\nexcept ImportError:\n    HAS_CITYSCAPESAPI = False\n\n\n@METRICS.register_module()\nclass CityScapesMetric(BaseMetric):\n    \"\"\"CityScapes metric for instance segmentation.\n\n    Args:\n        outfile_prefix (str): The prefix of txt and png files. The txt and\n            png file will be save in a directory whose path is\n            \"outfile_prefix.results/\".\n        seg_prefix (str, optional): Path to the directory which contains the\n            cityscapes instance segmentation masks. It's necessary when\n            training and validation. It could be None when infer on test\n            dataset. Defaults to None.\n        format_only (bool): Format the output results without perform\n            evaluation. It is useful when you want to format the result\n            to a specific format and submit it to the test server.\n            Defaults to False.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n        dump_matches (bool): Whether dump matches.json file during evaluating.\n            Defaults to False.\n        file_client_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n    \"\"\"\n    default_prefix: Optional[str] = 'cityscapes'\n\n    def __init__(self,\n                 outfile_prefix: str,\n                 seg_prefix: Optional[str] = None,\n                 format_only: bool = False,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None,\n                 dump_matches: bool = False,\n                 file_client_args: dict = None,\n                 backend_args: dict = None) -> None:\n\n        if not HAS_CITYSCAPESAPI:\n            raise RuntimeError('Failed to import `cityscapesscripts`.'\n                               'Please try to install official '\n                               'cityscapesscripts by '\n                               '\"pip install cityscapesscripts\"')\n        super().__init__(collect_device=collect_device, prefix=prefix)\n\n        self.tmp_dir = None\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n        else:\n            assert seg_prefix is not None, '`seg_prefix` is necessary when '\n            'computing the CityScapes metrics'\n\n        if outfile_prefix is None:\n            self.tmp_dir = tempfile.TemporaryDirectory()\n            self.outfile_prefix = osp.join(self.tmp_dir.name, 'results')\n        else:\n            # the directory to save predicted panoptic segmentation mask\n            self.outfile_prefix = osp.join(outfile_prefix, 'results')  # type: ignore # yapf: disable # noqa: E501\n\n        dir_name = osp.expanduser(self.outfile_prefix)\n\n        if osp.exists(dir_name) and is_main_process():\n            logger: MMLogger = MMLogger.get_current_instance()\n            logger.info('remove previous results.')\n            shutil.rmtree(dir_name)\n        os.makedirs(dir_name, exist_ok=True)\n\n        self.backend_args = backend_args\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n\n        self.seg_prefix = seg_prefix\n        self.dump_matches = dump_matches\n\n    def __del__(self) -> None:\n        \"\"\"Clean up the results if necessary.\"\"\"\n        if self.tmp_dir is not None:\n            self.tmp_dir.cleanup()\n\n    # TODO: data_batch is no longer needed, consider adjusting the\n    #  parameter position\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            # parse pred\n            result = dict()\n            pred = data_sample['pred_instances']\n            filename = data_sample['img_path']\n            basename = osp.splitext(osp.basename(filename))[0]\n            pred_txt = osp.join(self.outfile_prefix, basename + '_pred.txt')\n            result['pred_txt'] = pred_txt\n            labels = pred['labels'].cpu().numpy()\n            masks = pred['masks'].cpu().numpy().astype(np.uint8)\n            if 'mask_scores' in pred:\n                # some detectors use different scores for bbox and mask\n                mask_scores = pred['mask_scores'].cpu().numpy()\n            else:\n                mask_scores = pred['scores'].cpu().numpy()\n\n            with open(pred_txt, 'w') as f:\n                for i, (label, mask, mask_score) in enumerate(\n                        zip(labels, masks, mask_scores)):\n                    class_name = self.dataset_meta['classes'][label]\n                    class_id = CSLabels.name2label[class_name].id\n                    png_filename = osp.join(\n                        self.outfile_prefix,\n                        basename + f'_{i}_{class_name}.png')\n                    mmcv.imwrite(mask, png_filename)\n                    f.write(f'{osp.basename(png_filename)} '\n                            f'{class_id} {mask_score}\\n')\n\n            # parse gt\n            gt = dict()\n            img_path = filename.replace('leftImg8bit.png',\n                                        'gtFine_instanceIds.png')\n            gt['file_name'] = img_path.replace('leftImg8bit', 'gtFine')\n\n            self.results.append((gt, result))\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n                the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        if self.format_only:\n            logger.info(\n                f'results are saved to {osp.dirname(self.outfile_prefix)}')\n            return OrderedDict()\n        logger.info('starts to compute metric')\n\n        gts, preds = zip(*results)\n        # set global states in cityscapes evaluation API\n        gt_instances_file = osp.join(self.outfile_prefix, 'gtInstances.json')  # type: ignore # yapf: disable # noqa: E501\n        # split gt and prediction list\n        gts, preds = zip(*results)\n        CSEval.args.JSONOutput = False\n        CSEval.args.colorized = False\n        CSEval.args.gtInstancesFile = gt_instances_file\n\n        groundTruthImgList = [gt['file_name'] for gt in gts]\n        predictionImgList = [pred['pred_txt'] for pred in preds]\n        CSEval_results = evaluateImgLists(\n            predictionImgList,\n            groundTruthImgList,\n            CSEval.args,\n            self.backend_args,\n            dump_matches=self.dump_matches)['averages']\n\n        eval_results = OrderedDict()\n        eval_results['mAP'] = CSEval_results['allAp']\n        eval_results['AP@50'] = CSEval_results['allAp50%']\n\n        return eval_results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/coco_caption_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nimport os\nimport tempfile\nfrom typing import List, Optional\n\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.utils import track_iter_progress\nfrom pycocotools.coco import COCO\n\nfrom mmdet.registry import METRICS\n\ntry:\n    from pycocoevalcap.eval import COCOEvalCap\nexcept ImportError:\n    COCOEvalCap = None\n\n\n@METRICS.register_module()\nclass COCOCaptionMetric(BaseMetric):\n    \"\"\"Coco Caption evaluation wrapper.\n\n    Save the generated captions and transform into coco format.\n    Calling COCO API for caption metrics.\n\n    Args:\n        ann_file (str): the path for the COCO format caption ground truth\n            json file, load for evaluations.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Should be modified according to the\n            `retrieval_type` for unambiguous results. Defaults to TR.\n    \"\"\"\n\n    def __init__(self,\n                 ann_file: str,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None):\n        if COCOEvalCap is None:\n            raise RuntimeError(\n                'COCOEvalCap is not installed, please install it by: '\n                'pip install pycocoevalcap')\n\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        self.ann_file = ann_file\n\n    def process(self, data_batch, data_samples):\n        \"\"\"Process one batch of data samples.\n\n        The processed results should be stored in ``self.results``, which will\n        be used to computed the metrics when all batches have been processed.\n\n        Args:\n            data_batch: A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of outputs from the model.\n        \"\"\"\n\n        for data_sample in data_samples:\n            result = dict()\n\n            result['caption'] = data_sample['pred_caption']\n            result['image_id'] = int(data_sample['img_id'])\n\n            # Save the result to `self.results`.\n            self.results.append(result)\n\n    def compute_metrics(self, results: List):\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (dict): The processed results of each batch.\n\n        Returns:\n            Dict: The computed metrics. The keys are the names of the metrics,\n            and the values are corresponding results.\n        \"\"\"\n        # NOTICE: don't access `self.results` from the method.\n\n        with tempfile.TemporaryDirectory() as temp_dir:\n\n            eval_result_file = save_result(\n                result=results,\n                result_dir=temp_dir,\n                filename='caption_pred',\n                remove_duplicate='image_id',\n            )\n\n            coco_val = coco_caption_eval(eval_result_file, self.ann_file)\n\n        return coco_val\n\n\ndef save_result(result, result_dir, filename, remove_duplicate=''):\n    \"\"\"Saving predictions as json file for evaluation.\"\"\"\n    # combine results from all processes\n    if remove_duplicate:\n        result_new = []\n        id_list = []\n        for res in track_iter_progress(result):\n            if res[remove_duplicate] not in id_list:\n                id_list.append(res[remove_duplicate])\n                result_new.append(res)\n        result = result_new\n\n    final_result_file_url = os.path.join(result_dir, '%s.json' % filename)\n    print(f'result file saved to {final_result_file_url}')\n    json.dump(result, open(final_result_file_url, 'w'))\n\n    return final_result_file_url\n\n\ndef coco_caption_eval(results_file, ann_file):\n    \"\"\"Evaluation between gt json and prediction json files.\"\"\"\n    # create coco object and coco_result object\n    coco = COCO(ann_file)\n    coco_result = coco.loadRes(results_file)\n\n    # create coco_eval object by taking coco and coco_result\n    coco_eval = COCOEvalCap(coco, coco_result)\n\n    # make sure the image ids are the same\n    coco_eval.params['image_id'] = coco_result.getImgIds()\n\n    # This will take some times at the first run\n    coco_eval.evaluate()\n\n    # print output evaluation scores\n    for metric, score in coco_eval.eval.items():\n        print(f'{metric}: {score:.3f}')\n\n    return coco_eval.eval\n"
  },
  {
    "path": "mmdet/evaluation/metrics/coco_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport datetime\nimport itertools\nimport os.path as osp\nimport tempfile\nfrom collections import OrderedDict\nfrom typing import Dict, List, Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import dump, get_local_path, load\nfrom mmengine.logging import MMLogger\nfrom terminaltables import AsciiTable\n\nfrom mmdet.datasets.api_wrappers import COCO, COCOeval, COCOevalMP\nfrom mmdet.registry import METRICS\nfrom mmdet.structures.mask import encode_mask_results\nfrom ..functional import eval_recalls\n\n\n@METRICS.register_module()\nclass CocoMetric(BaseMetric):\n    \"\"\"COCO evaluation metric.\n\n    Evaluate AR, AP, and mAP for detection tasks including proposal/box\n    detection and instance segmentation. Please refer to\n    https://cocodataset.org/#detection-eval for more details.\n\n    Args:\n        ann_file (str, optional): Path to the coco format annotation file.\n            If not specified, ground truth annotations from the dataset will\n            be converted to coco format. Defaults to None.\n        metric (str | List[str]): Metrics to be evaluated. Valid metrics\n            include 'bbox', 'segm', 'proposal', and 'proposal_fast'.\n            Defaults to 'bbox'.\n        classwise (bool): Whether to evaluate the metric class-wise.\n            Defaults to False.\n        proposal_nums (Sequence[int]): Numbers of proposals to be evaluated.\n            Defaults to (100, 300, 1000).\n        iou_thrs (float | List[float], optional): IoU threshold to compute AP\n            and AR. If not specified, IoUs from 0.5 to 0.95 will be used.\n            Defaults to None.\n        metric_items (List[str], optional): Metric result names to be\n            recorded in the evaluation result. Defaults to None.\n        format_only (bool): Format the output results without perform\n            evaluation. It is useful when you want to format the result\n            to a specific format and submit it to the test server.\n            Defaults to False.\n        outfile_prefix (str, optional): The prefix of json files. It includes\n            the file path and the prefix of filename, e.g., \"a/b/prefix\".\n            If not specified, a temp file will be created. Defaults to None.\n        file_client_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n        sort_categories (bool): Whether sort categories in annotations. Only\n            used for `Objects365V1Dataset`. Defaults to False.\n        use_mp_eval (bool): Whether to use mul-processing evaluation\n    \"\"\"\n    default_prefix: Optional[str] = 'coco'\n\n    def __init__(self,\n                 ann_file: Optional[str] = None,\n                 metric: Union[str, List[str]] = 'bbox',\n                 classwise: bool = False,\n                 proposal_nums: Sequence[int] = (100, 300, 1000),\n                 iou_thrs: Optional[Union[float, Sequence[float]]] = None,\n                 metric_items: Optional[Sequence[str]] = None,\n                 format_only: bool = False,\n                 outfile_prefix: Optional[str] = None,\n                 file_client_args: dict = None,\n                 backend_args: dict = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None,\n                 sort_categories: bool = False,\n                 use_mp_eval: bool = False) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        # coco evaluation metrics\n        self.metrics = metric if isinstance(metric, list) else [metric]\n        allowed_metrics = ['bbox', 'segm', 'proposal', 'proposal_fast']\n        for metric in self.metrics:\n            if metric not in allowed_metrics:\n                raise KeyError(\n                    \"metric should be one of 'bbox', 'segm', 'proposal', \"\n                    f\"'proposal_fast', but got {metric}.\")\n\n        # do class wise evaluation, default False\n        self.classwise = classwise\n        # whether to use multi processing evaluation, default False\n        self.use_mp_eval = use_mp_eval\n\n        # proposal_nums used to compute recall or precision.\n        self.proposal_nums = list(proposal_nums)\n\n        # iou_thrs used to compute recall or precision.\n        if iou_thrs is None:\n            iou_thrs = np.linspace(\n                .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)\n        self.iou_thrs = iou_thrs\n        self.metric_items = metric_items\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n\n        self.outfile_prefix = outfile_prefix\n\n        self.backend_args = backend_args\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n\n        # if ann_file is not specified,\n        # initialize coco api with the converted dataset\n        if ann_file is not None:\n            with get_local_path(\n                    ann_file, backend_args=self.backend_args) as local_path:\n                self._coco_api = COCO(local_path)\n                if sort_categories:\n                    # 'categories' list in objects365_train.json and\n                    # objects365_val.json is inconsistent, need sort\n                    # list(or dict) before get cat_ids.\n                    cats = self._coco_api.cats\n                    sorted_cats = {i: cats[i] for i in sorted(cats)}\n                    self._coco_api.cats = sorted_cats\n                    categories = self._coco_api.dataset['categories']\n                    sorted_categories = sorted(\n                        categories, key=lambda i: i['id'])\n                    self._coco_api.dataset['categories'] = sorted_categories\n        else:\n            self._coco_api = None\n\n        # handle dataset lazy init\n        self.cat_ids = None\n        self.img_ids = None\n\n    def fast_eval_recall(self,\n                         results: List[dict],\n                         proposal_nums: Sequence[int],\n                         iou_thrs: Sequence[float],\n                         logger: Optional[MMLogger] = None) -> np.ndarray:\n        \"\"\"Evaluate proposal recall with COCO's fast_eval_recall.\n\n        Args:\n            results (List[dict]): Results of the dataset.\n            proposal_nums (Sequence[int]): Proposal numbers used for\n                evaluation.\n            iou_thrs (Sequence[float]): IoU thresholds used for evaluation.\n            logger (MMLogger, optional): Logger used for logging the recall\n                summary.\n        Returns:\n            np.ndarray: Averaged recall results.\n        \"\"\"\n        gt_bboxes = []\n        pred_bboxes = [result['bboxes'] for result in results]\n        for i in range(len(self.img_ids)):\n            ann_ids = self._coco_api.get_ann_ids(img_ids=self.img_ids[i])\n            ann_info = self._coco_api.load_anns(ann_ids)\n            if len(ann_info) == 0:\n                gt_bboxes.append(np.zeros((0, 4)))\n                continue\n            bboxes = []\n            for ann in ann_info:\n                if ann.get('ignore', False) or ann['iscrowd']:\n                    continue\n                x1, y1, w, h = ann['bbox']\n                bboxes.append([x1, y1, x1 + w, y1 + h])\n            bboxes = np.array(bboxes, dtype=np.float32)\n            if bboxes.shape[0] == 0:\n                bboxes = np.zeros((0, 4))\n            gt_bboxes.append(bboxes)\n\n        recalls = eval_recalls(\n            gt_bboxes, pred_bboxes, proposal_nums, iou_thrs, logger=logger)\n        ar = recalls.mean(axis=1)\n        return ar\n\n    def xyxy2xywh(self, bbox: np.ndarray) -> list:\n        \"\"\"Convert ``xyxy`` style bounding boxes to ``xywh`` style for COCO\n        evaluation.\n\n        Args:\n            bbox (numpy.ndarray): The bounding boxes, shape (4, ), in\n                ``xyxy`` order.\n\n        Returns:\n            list[float]: The converted bounding boxes, in ``xywh`` order.\n        \"\"\"\n\n        _bbox: List = bbox.tolist()\n        return [\n            _bbox[0],\n            _bbox[1],\n            _bbox[2] - _bbox[0],\n            _bbox[3] - _bbox[1],\n        ]\n\n    def results2json(self, results: Sequence[dict],\n                     outfile_prefix: str) -> dict:\n        \"\"\"Dump the detection results to a COCO style json file.\n\n        There are 3 types of results: proposals, bbox predictions, mask\n        predictions, and they have different data types. This method will\n        automatically recognize the type, and dump them to json files.\n\n        Args:\n            results (Sequence[dict]): Testing results of the\n                dataset.\n            outfile_prefix (str): The filename prefix of the json files. If the\n                prefix is \"somepath/xxx\", the json files will be named\n                \"somepath/xxx.bbox.json\", \"somepath/xxx.segm.json\",\n                \"somepath/xxx.proposal.json\".\n\n        Returns:\n            dict: Possible keys are \"bbox\", \"segm\", \"proposal\", and\n            values are corresponding filenames.\n        \"\"\"\n        bbox_json_results = []\n        segm_json_results = [] if 'masks' in results[0] else None\n        for idx, result in enumerate(results):\n            image_id = result.get('img_id', idx)\n            labels = result['labels']\n            bboxes = result['bboxes']\n            scores = result['scores']\n            # bbox results\n            for i, label in enumerate(labels):\n                data = dict()\n                data['image_id'] = image_id\n                data['bbox'] = self.xyxy2xywh(bboxes[i])\n                data['score'] = float(scores[i])\n                data['category_id'] = self.cat_ids[label]\n                bbox_json_results.append(data)\n\n            if segm_json_results is None:\n                continue\n\n            # segm results\n            masks = result['masks']\n            mask_scores = result.get('mask_scores', scores)\n            for i, label in enumerate(labels):\n                data = dict()\n                data['image_id'] = image_id\n                data['bbox'] = self.xyxy2xywh(bboxes[i])\n                data['score'] = float(mask_scores[i])\n                data['category_id'] = self.cat_ids[label]\n                if isinstance(masks[i]['counts'], bytes):\n                    masks[i]['counts'] = masks[i]['counts'].decode()\n                data['segmentation'] = masks[i]\n                segm_json_results.append(data)\n\n        result_files = dict()\n        result_files['bbox'] = f'{outfile_prefix}.bbox.json'\n        result_files['proposal'] = f'{outfile_prefix}.bbox.json'\n        dump(bbox_json_results, result_files['bbox'])\n\n        if segm_json_results is not None:\n            result_files['segm'] = f'{outfile_prefix}.segm.json'\n            dump(segm_json_results, result_files['segm'])\n\n        return result_files\n\n    def gt_to_coco_json(self, gt_dicts: Sequence[dict],\n                        outfile_prefix: str) -> str:\n        \"\"\"Convert ground truth to coco format json file.\n\n        Args:\n            gt_dicts (Sequence[dict]): Ground truth of the dataset.\n            outfile_prefix (str): The filename prefix of the json files. If the\n                prefix is \"somepath/xxx\", the json file will be named\n                \"somepath/xxx.gt.json\".\n        Returns:\n            str: The filename of the json file.\n        \"\"\"\n        categories = [\n            dict(id=id, name=name)\n            for id, name in enumerate(self.dataset_meta['classes'])\n        ]\n        image_infos = []\n        annotations = []\n\n        for idx, gt_dict in enumerate(gt_dicts):\n            img_id = gt_dict.get('img_id', idx)\n            image_info = dict(\n                id=img_id,\n                width=gt_dict['width'],\n                height=gt_dict['height'],\n                file_name='')\n            image_infos.append(image_info)\n            for ann in gt_dict['anns']:\n                label = ann['bbox_label']\n                bbox = ann['bbox']\n                coco_bbox = [\n                    bbox[0],\n                    bbox[1],\n                    bbox[2] - bbox[0],\n                    bbox[3] - bbox[1],\n                ]\n\n                annotation = dict(\n                    id=len(annotations) +\n                    1,  # coco api requires id starts with 1\n                    image_id=img_id,\n                    bbox=coco_bbox,\n                    iscrowd=ann.get('ignore_flag', 0),\n                    category_id=int(label),\n                    area=coco_bbox[2] * coco_bbox[3])\n                if ann.get('mask', None):\n                    mask = ann['mask']\n                    # area = mask_util.area(mask)\n                    if isinstance(mask, dict) and isinstance(\n                            mask['counts'], bytes):\n                        mask['counts'] = mask['counts'].decode()\n                    annotation['segmentation'] = mask\n                    # annotation['area'] = float(area)\n                annotations.append(annotation)\n\n        info = dict(\n            date_created=str(datetime.datetime.now()),\n            description='Coco json file converted by mmdet CocoMetric.')\n        coco_json = dict(\n            info=info,\n            images=image_infos,\n            categories=categories,\n            licenses=None,\n        )\n        if len(annotations) > 0:\n            coco_json['annotations'] = annotations\n        converted_json_path = f'{outfile_prefix}.gt.json'\n        dump(coco_json, converted_json_path)\n        return converted_json_path\n\n    # TODO: data_batch is no longer needed, consider adjusting the\n    #  parameter position\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            result = dict()\n            pred = data_sample['pred_instances']\n            result['img_id'] = data_sample['img_id']\n            result['bboxes'] = pred['bboxes'].cpu().numpy()\n            result['scores'] = pred['scores'].cpu().numpy()\n            result['labels'] = pred['labels'].cpu().numpy()\n            # encode mask to RLE\n            if 'masks' in pred:\n                result['masks'] = encode_mask_results(\n                    pred['masks'].detach().cpu().numpy()) if isinstance(\n                        pred['masks'], torch.Tensor) else pred['masks']\n            # some detectors use different scores for bbox and mask\n            if 'mask_scores' in pred:\n                result['mask_scores'] = pred['mask_scores'].cpu().numpy()\n\n            # parse gt\n            gt = dict()\n            gt['width'] = data_sample['ori_shape'][1]\n            gt['height'] = data_sample['ori_shape'][0]\n            gt['img_id'] = data_sample['img_id']\n            if self._coco_api is None:\n                # TODO: Need to refactor to support LoadAnnotations\n                assert 'instances' in data_sample, \\\n                    'ground truth is required for evaluation when ' \\\n                    '`ann_file` is not provided'\n                gt['anns'] = data_sample['instances']\n            # add converted result to the results list\n            self.results.append((gt, result))\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        # split gt and prediction list\n        gts, preds = zip(*results)\n\n        tmp_dir = None\n        if self.outfile_prefix is None:\n            tmp_dir = tempfile.TemporaryDirectory()\n            outfile_prefix = osp.join(tmp_dir.name, 'results')\n        else:\n            outfile_prefix = self.outfile_prefix\n\n        if self._coco_api is None:\n            # use converted gt json file to initialize coco api\n            logger.info('Converting ground truth to coco format...')\n            coco_json_path = self.gt_to_coco_json(\n                gt_dicts=gts, outfile_prefix=outfile_prefix)\n            self._coco_api = COCO(coco_json_path)\n\n        # handle lazy init\n        if self.cat_ids is None:\n            self.cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['classes'])\n        if self.img_ids is None:\n            self.img_ids = self._coco_api.get_img_ids()\n\n        # convert predictions to coco format and dump to json file\n        result_files = self.results2json(preds, outfile_prefix)\n\n        eval_results = OrderedDict()\n        if self.format_only:\n            logger.info('results are saved in '\n                        f'{osp.dirname(outfile_prefix)}')\n            return eval_results\n\n        for metric in self.metrics:\n            logger.info(f'Evaluating {metric}...')\n\n            # TODO: May refactor fast_eval_recall to an independent metric?\n            # fast eval recall\n            if metric == 'proposal_fast':\n                ar = self.fast_eval_recall(\n                    preds, self.proposal_nums, self.iou_thrs, logger=logger)\n                log_msg = []\n                for i, num in enumerate(self.proposal_nums):\n                    eval_results[f'AR@{num}'] = ar[i]\n                    log_msg.append(f'\\nAR@{num}\\t{ar[i]:.4f}')\n                log_msg = ''.join(log_msg)\n                logger.info(log_msg)\n                continue\n\n            # evaluate proposal, bbox and segm\n            iou_type = 'bbox' if metric == 'proposal' else metric\n            if metric not in result_files:\n                raise KeyError(f'{metric} is not in results')\n            try:\n                predictions = load(result_files[metric])\n                if iou_type == 'segm':\n                    # Refer to https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/coco.py#L331  # noqa\n                    # When evaluating mask AP, if the results contain bbox,\n                    # cocoapi will use the box area instead of the mask area\n                    # for calculating the instance area. Though the overall AP\n                    # is not affected, this leads to different\n                    # small/medium/large mask AP results.\n                    for x in predictions:\n                        x.pop('bbox')\n                coco_dt = self._coco_api.loadRes(predictions)\n\n            except IndexError:\n                logger.error(\n                    'The testing results of the whole dataset is empty.')\n                break\n\n            if self.use_mp_eval:\n                coco_eval = COCOevalMP(self._coco_api, coco_dt, iou_type)\n            else:\n                coco_eval = COCOeval(self._coco_api, coco_dt, iou_type)\n\n            coco_eval.params.catIds = self.cat_ids\n            coco_eval.params.imgIds = self.img_ids\n            coco_eval.params.maxDets = list(self.proposal_nums)\n            coco_eval.params.iouThrs = self.iou_thrs\n\n            # mapping of cocoEval.stats\n            coco_metric_names = {\n                'mAP': 0,\n                'mAP_50': 1,\n                'mAP_75': 2,\n                'mAP_s': 3,\n                'mAP_m': 4,\n                'mAP_l': 5,\n                'AR@100': 6,\n                'AR@300': 7,\n                'AR@1000': 8,\n                'AR_s@1000': 9,\n                'AR_m@1000': 10,\n                'AR_l@1000': 11\n            }\n            metric_items = self.metric_items\n            if metric_items is not None:\n                for metric_item in metric_items:\n                    if metric_item not in coco_metric_names:\n                        raise KeyError(\n                            f'metric item \"{metric_item}\" is not supported')\n\n            if metric == 'proposal':\n                coco_eval.params.useCats = 0\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if metric_items is None:\n                    metric_items = [\n                        'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000',\n                        'AR_m@1000', 'AR_l@1000'\n                    ]\n\n                for item in metric_items:\n                    val = float(\n                        f'{coco_eval.stats[coco_metric_names[item]]:.3f}')\n                    eval_results[item] = val\n            else:\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if self.classwise:  # Compute per-category AP\n                    # Compute per-category AP\n                    # from https://github.com/facebookresearch/detectron2/\n                    precisions = coco_eval.eval['precision']\n                    # precision: (iou, recall, cls, area range, max dets)\n                    assert len(self.cat_ids) == precisions.shape[2]\n\n                    results_per_category = []\n                    for idx, cat_id in enumerate(self.cat_ids):\n                        t = []\n                        # area range index 0: all area ranges\n                        # max dets index -1: typically 100 per image\n                        nm = self._coco_api.loadCats(cat_id)[0]\n                        precision = precisions[:, :, idx, 0, -1]\n                        precision = precision[precision > -1]\n                        if precision.size:\n                            ap = np.mean(precision)\n                        else:\n                            ap = float('nan')\n                        t.append(f'{nm[\"name\"]}')\n                        t.append(f'{round(ap, 3)}')\n                        eval_results[f'{nm[\"name\"]}_precision'] = round(ap, 3)\n\n                        # indexes of IoU  @50 and @75\n                        for iou in [0, 5]:\n                            precision = precisions[iou, :, idx, 0, -1]\n                            precision = precision[precision > -1]\n                            if precision.size:\n                                ap = np.mean(precision)\n                            else:\n                                ap = float('nan')\n                            t.append(f'{round(ap, 3)}')\n\n                        # indexes of area of small, median and large\n                        for area in [1, 2, 3]:\n                            precision = precisions[:, :, idx, area, -1]\n                            precision = precision[precision > -1]\n                            if precision.size:\n                                ap = np.mean(precision)\n                            else:\n                                ap = float('nan')\n                            t.append(f'{round(ap, 3)}')\n                        results_per_category.append(tuple(t))\n\n                    num_columns = len(results_per_category[0])\n                    results_flatten = list(\n                        itertools.chain(*results_per_category))\n                    headers = [\n                        'category', 'mAP', 'mAP_50', 'mAP_75', 'mAP_s',\n                        'mAP_m', 'mAP_l'\n                    ]\n                    results_2d = itertools.zip_longest(*[\n                        results_flatten[i::num_columns]\n                        for i in range(num_columns)\n                    ])\n                    table_data = [headers]\n                    table_data += [result for result in results_2d]\n                    table = AsciiTable(table_data)\n                    logger.info('\\n' + table.table)\n\n                if metric_items is None:\n                    metric_items = [\n                        'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l'\n                    ]\n\n                for metric_item in metric_items:\n                    key = f'{metric}_{metric_item}'\n                    val = coco_eval.stats[coco_metric_names[metric_item]]\n                    eval_results[key] = float(f'{round(val, 3)}')\n\n                ap = coco_eval.stats[:6]\n                logger.info(f'{metric}_mAP_copypaste: {ap[0]:.3f} '\n                            f'{ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} '\n                            f'{ap[4]:.3f} {ap[5]:.3f}')\n\n        if tmp_dir is not None:\n            tmp_dir.cleanup()\n        return eval_results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/coco_occluded_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Union\n\nimport mmengine\nimport numpy as np\nfrom mmengine.fileio import load\nfrom mmengine.logging import print_log\nfrom pycocotools import mask as coco_mask\nfrom terminaltables import AsciiTable\n\nfrom mmdet.registry import METRICS\nfrom .coco_metric import CocoMetric\n\n\n@METRICS.register_module()\nclass CocoOccludedSeparatedMetric(CocoMetric):\n    \"\"\"Metric of separated and occluded masks which presented in paper `A Tri-\n    Layer Plugin to Improve Occluded Detection.\n\n    <https://arxiv.org/abs/2210.10046>`_.\n\n    Separated COCO and Occluded COCO are automatically generated subsets of\n    COCO val dataset, collecting separated objects and partially occluded\n    objects for a large variety of categories. In this way, we define\n    occlusion into two major categories: separated and partially occluded.\n\n    - Separation: target object segmentation mask is separated into distinct\n      regions by the occluder.\n    - Partial Occlusion: target object is partially occluded but the\n      segmentation mask is connected.\n\n    These two new scalable real-image datasets are to benchmark a model's\n    capability to detect occluded objects of 80 common categories.\n\n    Please cite the paper if you use this dataset:\n\n    @article{zhan2022triocc,\n        title={A Tri-Layer Plugin to Improve Occluded Detection},\n        author={Zhan, Guanqi and Xie, Weidi and Zisserman, Andrew},\n        journal={British Machine Vision Conference},\n        year={2022}\n    }\n\n    Args:\n        occluded_ann (str): Path to the occluded coco annotation file.\n        separated_ann (str): Path to the separated coco annotation file.\n        score_thr (float): Score threshold of the detection masks.\n            Defaults to 0.3.\n        iou_thr (float): IoU threshold for the recall calculation.\n            Defaults to 0.75.\n        metric (str | List[str]): Metrics to be evaluated. Valid metrics\n            include 'bbox', 'segm', 'proposal', and 'proposal_fast'.\n            Defaults to 'bbox'.\n    \"\"\"\n    default_prefix: Optional[str] = 'coco'\n\n    def __init__(\n            self,\n            *args,\n            occluded_ann:\n        str = 'https://www.robots.ox.ac.uk/~vgg/research/tpod/datasets/occluded_coco.pkl',  # noqa\n            separated_ann:\n        str = 'https://www.robots.ox.ac.uk/~vgg/research/tpod/datasets/separated_coco.pkl',  # noqa\n            score_thr: float = 0.3,\n            iou_thr: float = 0.75,\n            metric: Union[str, List[str]] = ['bbox', 'segm'],\n            **kwargs) -> None:\n        super().__init__(*args, metric=metric, **kwargs)\n        self.occluded_ann = load(occluded_ann)\n        self.separated_ann = load(separated_ann)\n        self.score_thr = score_thr\n        self.iou_thr = iou_thr\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        coco_metric_res = super().compute_metrics(results)\n        eval_res = self.evaluate_occluded_separated(results)\n        coco_metric_res.update(eval_res)\n        return coco_metric_res\n\n    def evaluate_occluded_separated(self, results: List[tuple]) -> dict:\n        \"\"\"Compute the recall of occluded and separated masks.\n\n        Args:\n            results (list[tuple]): Testing results of the dataset.\n\n        Returns:\n            dict[str, float]: The recall of occluded and separated masks.\n        \"\"\"\n        dict_det = {}\n        print_log('processing detection results...')\n        prog_bar = mmengine.ProgressBar(len(results))\n        for i in range(len(results)):\n            gt, dt = results[i]\n            img_id = dt['img_id']\n            cur_img_name = self._coco_api.imgs[img_id]['file_name']\n            if cur_img_name not in dict_det.keys():\n                dict_det[cur_img_name] = []\n\n            for bbox, score, label, mask in zip(dt['bboxes'], dt['scores'],\n                                                dt['labels'], dt['masks']):\n                cur_binary_mask = coco_mask.decode(mask)\n                dict_det[cur_img_name].append([\n                    score, self.dataset_meta['classes'][label],\n                    cur_binary_mask, bbox\n                ])\n            dict_det[cur_img_name].sort(\n                key=lambda x: (-x[0], x[3][0], x[3][1])\n            )  # rank by confidence from high to low, avoid same confidence\n            prog_bar.update()\n        print_log('\\ncomputing occluded mask recall...', logger='current')\n        occluded_correct_num, occluded_recall = self.compute_recall(\n            dict_det, gt_ann=self.occluded_ann, is_occ=True)\n        print_log(\n            f'\\nCOCO occluded mask recall: {occluded_recall:.2f}%',\n            logger='current')\n        print_log(\n            f'COCO occluded mask success num: {occluded_correct_num}',\n            logger='current')\n        print_log('computing separated mask recall...', logger='current')\n        separated_correct_num, separated_recall = self.compute_recall(\n            dict_det, gt_ann=self.separated_ann, is_occ=False)\n        print_log(\n            f'\\nCOCO separated mask recall: {separated_recall:.2f}%',\n            logger='current')\n        print_log(\n            f'COCO separated mask success num: {separated_correct_num}',\n            logger='current')\n        table_data = [\n            ['mask type', 'recall', 'num correct'],\n            ['occluded', f'{occluded_recall:.2f}%', occluded_correct_num],\n            ['separated', f'{separated_recall:.2f}%', separated_correct_num]\n        ]\n        table = AsciiTable(table_data)\n        print_log('\\n' + table.table, logger='current')\n        return dict(\n            occluded_recall=occluded_recall, separated_recall=separated_recall)\n\n    def compute_recall(self,\n                       result_dict: dict,\n                       gt_ann: list,\n                       is_occ: bool = True) -> tuple:\n        \"\"\"Compute the recall of occluded or separated masks.\n\n        Args:\n            result_dict (dict): Processed mask results.\n            gt_ann (list): Occluded or separated coco annotations.\n            is_occ (bool): Whether the annotation is occluded mask.\n                Defaults to True.\n        Returns:\n            tuple: number of correct masks and the recall.\n        \"\"\"\n        correct = 0\n        prog_bar = mmengine.ProgressBar(len(gt_ann))\n        for iter_i in range(len(gt_ann)):\n            cur_item = gt_ann[iter_i]\n            cur_img_name = cur_item[0]\n            cur_gt_bbox = cur_item[3]\n            if is_occ:\n                cur_gt_bbox = [\n                    cur_gt_bbox[0], cur_gt_bbox[1],\n                    cur_gt_bbox[0] + cur_gt_bbox[2],\n                    cur_gt_bbox[1] + cur_gt_bbox[3]\n                ]\n            cur_gt_class = cur_item[1]\n            cur_gt_mask = coco_mask.decode(cur_item[4])\n\n            assert cur_img_name in result_dict.keys()\n            cur_detections = result_dict[cur_img_name]\n\n            correct_flag = False\n            for i in range(len(cur_detections)):\n                cur_det_confidence = cur_detections[i][0]\n                if cur_det_confidence < self.score_thr:\n                    break\n                cur_det_class = cur_detections[i][1]\n                if cur_det_class != cur_gt_class:\n                    continue\n                cur_det_mask = cur_detections[i][2]\n                cur_iou = self.mask_iou(cur_det_mask, cur_gt_mask)\n                if cur_iou >= self.iou_thr:\n                    correct_flag = True\n                    break\n            if correct_flag:\n                correct += 1\n            prog_bar.update()\n        recall = correct / len(gt_ann) * 100\n        return correct, recall\n\n    def mask_iou(self, mask1: np.ndarray, mask2: np.ndarray) -> np.ndarray:\n        \"\"\"Compute IoU between two masks.\"\"\"\n        mask1_area = np.count_nonzero(mask1 == 1)\n        mask2_area = np.count_nonzero(mask2 == 1)\n        intersection = np.count_nonzero(np.logical_and(mask1 == 1, mask2 == 1))\n        iou = intersection / (mask1_area + mask2_area - intersection)\n        return iou\n"
  },
  {
    "path": "mmdet/evaluation/metrics/coco_panoptic_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport datetime\nimport itertools\nimport os.path as osp\nimport tempfile\nfrom typing import Dict, Optional, Sequence, Tuple, Union\n\nimport mmcv\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import dump, get_local_path, load\nfrom mmengine.logging import MMLogger, print_log\nfrom terminaltables import AsciiTable\n\nfrom mmdet.datasets.api_wrappers import COCOPanoptic\nfrom mmdet.registry import METRICS\nfrom ..functional import (INSTANCE_OFFSET, pq_compute_multi_core,\n                          pq_compute_single_core)\n\ntry:\n    import panopticapi\n    from panopticapi.evaluation import VOID, PQStat\n    from panopticapi.utils import id2rgb, rgb2id\nexcept ImportError:\n    panopticapi = None\n    id2rgb = None\n    rgb2id = None\n    VOID = None\n    PQStat = None\n\n\n@METRICS.register_module()\nclass CocoPanopticMetric(BaseMetric):\n    \"\"\"COCO panoptic segmentation evaluation metric.\n\n    Evaluate PQ, SQ RQ for panoptic segmentation tasks. Please refer to\n    https://cocodataset.org/#panoptic-eval for more details.\n\n    Args:\n        ann_file (str, optional): Path to the coco format annotation file.\n            If not specified, ground truth annotations from the dataset will\n            be converted to coco format. Defaults to None.\n        seg_prefix (str, optional): Path to the directory which contains the\n            coco panoptic segmentation mask. It should be specified when\n            evaluate. Defaults to None.\n        classwise (bool): Whether to evaluate the metric class-wise.\n            Defaults to False.\n        outfile_prefix (str, optional): The prefix of json files. It includes\n            the file path and the prefix of filename, e.g., \"a/b/prefix\".\n            If not specified, a temp file will be created.\n            It should be specified when format_only is True. Defaults to None.\n        format_only (bool): Format the output results without perform\n            evaluation. It is useful when you want to format the result\n            to a specific format and submit it to the test server.\n            Defaults to False.\n        nproc (int): Number of processes for panoptic quality computing.\n            Defaults to 32. When ``nproc`` exceeds the number of cpu cores,\n            the number of cpu cores is used.\n        file_client_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n    \"\"\"\n    default_prefix: Optional[str] = 'coco_panoptic'\n\n    def __init__(self,\n                 ann_file: Optional[str] = None,\n                 seg_prefix: Optional[str] = None,\n                 classwise: bool = False,\n                 format_only: bool = False,\n                 outfile_prefix: Optional[str] = None,\n                 nproc: int = 32,\n                 file_client_args: dict = None,\n                 backend_args: dict = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None) -> None:\n        if panopticapi is None:\n            raise RuntimeError(\n                'panopticapi is not installed, please install it by: '\n                'pip install git+https://github.com/cocodataset/'\n                'panopticapi.git.')\n\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        self.classwise = classwise\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n\n        self.tmp_dir = None\n        # outfile_prefix should be a prefix of a path which points to a shared\n        # storage when train or test with multi nodes.\n        self.outfile_prefix = outfile_prefix\n        if outfile_prefix is None:\n            self.tmp_dir = tempfile.TemporaryDirectory()\n            self.outfile_prefix = osp.join(self.tmp_dir.name, 'results')\n        # the directory to save predicted panoptic segmentation mask\n        self.seg_out_dir = f'{self.outfile_prefix}.panoptic'\n        self.nproc = nproc\n        self.seg_prefix = seg_prefix\n\n        self.cat_ids = None\n        self.cat2label = None\n\n        self.backend_args = backend_args\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n\n        if ann_file:\n            with get_local_path(\n                    ann_file, backend_args=self.backend_args) as local_path:\n                self._coco_api = COCOPanoptic(local_path)\n            self.categories = self._coco_api.cats\n        else:\n            self._coco_api = None\n            self.categories = None\n\n    def __del__(self) -> None:\n        \"\"\"Clean up.\"\"\"\n        if self.tmp_dir is not None:\n            self.tmp_dir.cleanup()\n\n    def gt_to_coco_json(self, gt_dicts: Sequence[dict],\n                        outfile_prefix: str) -> Tuple[str, str]:\n        \"\"\"Convert ground truth to coco panoptic segmentation format json file.\n\n        Args:\n            gt_dicts (Sequence[dict]): Ground truth of the dataset.\n            outfile_prefix (str): The filename prefix of the json file. If the\n                prefix is \"somepath/xxx\", the json file will be named\n                \"somepath/xxx.gt.json\".\n\n        Returns:\n            Tuple[str, str]: The filename of the json file and the name of the\\\n                directory which contains panoptic segmentation masks.\n        \"\"\"\n        assert len(gt_dicts) > 0, 'gt_dicts is empty.'\n        gt_folder = osp.dirname(gt_dicts[0]['seg_map_path'])\n        converted_json_path = f'{outfile_prefix}.gt.json'\n\n        categories = []\n        for id, name in enumerate(self.dataset_meta['classes']):\n            isthing = 1 if name in self.dataset_meta['thing_classes'] else 0\n            categories.append({'id': id, 'name': name, 'isthing': isthing})\n\n        image_infos = []\n        annotations = []\n        for gt_dict in gt_dicts:\n            img_id = gt_dict['image_id']\n            image_info = {\n                'id': img_id,\n                'width': gt_dict['width'],\n                'height': gt_dict['height'],\n                'file_name': osp.split(gt_dict['seg_map_path'])[-1]\n            }\n            image_infos.append(image_info)\n\n            pan_png = mmcv.imread(gt_dict['seg_map_path']).squeeze()\n            pan_png = pan_png[:, :, ::-1]\n            pan_png = rgb2id(pan_png)\n            segments_info = []\n            for segment_info in gt_dict['segments_info']:\n                id = segment_info['id']\n                label = segment_info['category']\n                mask = pan_png == id\n                isthing = categories[label]['isthing']\n                if isthing:\n                    iscrowd = 1 if not segment_info['is_thing'] else 0\n                else:\n                    iscrowd = 0\n\n                new_segment_info = {\n                    'id': id,\n                    'category_id': label,\n                    'isthing': isthing,\n                    'iscrowd': iscrowd,\n                    'area': mask.sum()\n                }\n                segments_info.append(new_segment_info)\n\n            segm_file = image_info['file_name'].replace('.jpg', '.png')\n            annotation = dict(\n                image_id=img_id,\n                segments_info=segments_info,\n                file_name=segm_file)\n            annotations.append(annotation)\n            pan_png = id2rgb(pan_png)\n\n        info = dict(\n            date_created=str(datetime.datetime.now()),\n            description='Coco json file converted by mmdet CocoPanopticMetric.'\n        )\n        coco_json = dict(\n            info=info,\n            images=image_infos,\n            categories=categories,\n            licenses=None,\n        )\n        if len(annotations) > 0:\n            coco_json['annotations'] = annotations\n        dump(coco_json, converted_json_path)\n        return converted_json_path, gt_folder\n\n    def result2json(self, results: Sequence[dict],\n                    outfile_prefix: str) -> Tuple[str, str]:\n        \"\"\"Dump the panoptic results to a COCO style json file and a directory.\n\n        Args:\n            results (Sequence[dict]): Testing results of the dataset.\n            outfile_prefix (str): The filename prefix of the json files and the\n                directory.\n\n        Returns:\n            Tuple[str, str]: The json file and the directory which contains \\\n                panoptic segmentation masks. The filename of the json is\n                \"somepath/xxx.panoptic.json\" and name of the directory is\n                \"somepath/xxx.panoptic\".\n        \"\"\"\n        label2cat = dict((v, k) for (k, v) in self.cat2label.items())\n        pred_annotations = []\n        for idx in range(len(results)):\n            result = results[idx]\n            for segment_info in result['segments_info']:\n                sem_label = segment_info['category_id']\n                # convert sem_label to json label\n                cat_id = label2cat[sem_label]\n                segment_info['category_id'] = label2cat[sem_label]\n                is_thing = self.categories[cat_id]['isthing']\n                segment_info['isthing'] = is_thing\n            pred_annotations.append(result)\n        pan_json_results = dict(annotations=pred_annotations)\n        json_filename = f'{outfile_prefix}.panoptic.json'\n        dump(pan_json_results, json_filename)\n        return json_filename, (\n            self.seg_out_dir\n            if self.tmp_dir is None else tempfile.gettempdir())\n\n    def _parse_predictions(self,\n                           pred: dict,\n                           img_id: int,\n                           segm_file: str,\n                           label2cat=None) -> dict:\n        \"\"\"Parse panoptic segmentation predictions.\n\n        Args:\n            pred (dict): Panoptic segmentation predictions.\n            img_id (int): Image id.\n            segm_file (str): Segmentation file name.\n            label2cat (dict): Mapping from label to category id.\n                Defaults to None.\n\n        Returns:\n            dict: Parsed predictions.\n        \"\"\"\n        result = dict()\n        result['img_id'] = img_id\n        # shape (1, H, W) -> (H, W)\n        pan = pred['pred_panoptic_seg']['sem_seg'].cpu().numpy()[0]\n        ignore_index = pred['pred_panoptic_seg'].get(\n            'ignore_index', len(self.dataset_meta['classes']))\n        pan_labels = np.unique(pan)\n        segments_info = []\n        for pan_label in pan_labels:\n            sem_label = pan_label % INSTANCE_OFFSET\n            # We reserve the length of dataset_meta['classes']\n            # and ignore_index for VOID label\n            if sem_label == len(\n                    self.dataset_meta['classes']) or sem_label == ignore_index:\n                continue\n            mask = pan == pan_label\n            area = mask.sum()\n            segments_info.append({\n                'id':\n                int(pan_label),\n                # when ann_file provided, sem_label should be cat_id, otherwise\n                # sem_label should be a continuous id, not the cat_id\n                # defined in dataset\n                'category_id':\n                label2cat[sem_label] if label2cat else sem_label,\n                'area':\n                int(area)\n            })\n        # evaluation script uses 0 for VOID label.\n        pan[pan % INSTANCE_OFFSET == len(self.dataset_meta['classes'])] = VOID\n        pan[pan % INSTANCE_OFFSET == ignore_index] = VOID\n\n        pan = id2rgb(pan).astype(np.uint8)\n        mmcv.imwrite(pan[:, :, ::-1], osp.join(self.seg_out_dir, segm_file))\n        result = {\n            'image_id': img_id,\n            'segments_info': segments_info,\n            'file_name': segm_file\n        }\n\n        return result\n\n    def _compute_batch_pq_stats(self, data_samples: Sequence[dict]):\n        \"\"\"Process gts and predictions when ``outfile_prefix`` is not set, gts\n        are from dataset or a json file which is defined by ``ann_file``.\n\n        Intermediate results, ``pq_stats``, are computed here and put into\n        ``self.results``.\n        \"\"\"\n        if self._coco_api is None:\n            categories = dict()\n            for id, name in enumerate(self.dataset_meta['classes']):\n                isthing = 1 if name in self.dataset_meta['thing_classes']\\\n                    else 0\n                categories[id] = {'id': id, 'name': name, 'isthing': isthing}\n            label2cat = None\n        else:\n            categories = self.categories\n            cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['classes'])\n            label2cat = {i: cat_id for i, cat_id in enumerate(cat_ids)}\n\n        for data_sample in data_samples:\n            # parse pred\n            img_id = data_sample['img_id']\n            segm_file = osp.basename(data_sample['img_path']).replace(\n                '.jpg', '.png')\n            result = self._parse_predictions(\n                pred=data_sample,\n                img_id=img_id,\n                segm_file=segm_file,\n                label2cat=label2cat)\n\n            # parse gt\n            gt = dict()\n            gt['image_id'] = img_id\n            gt['width'] = data_sample['ori_shape'][1]\n            gt['height'] = data_sample['ori_shape'][0]\n            gt['file_name'] = segm_file\n\n            if self._coco_api is None:\n                # get segments_info from data_sample\n                seg_map_path = osp.join(self.seg_prefix, segm_file)\n                pan_png = mmcv.imread(seg_map_path).squeeze()\n                pan_png = pan_png[:, :, ::-1]\n                pan_png = rgb2id(pan_png)\n                segments_info = []\n\n                for segment_info in data_sample['segments_info']:\n                    id = segment_info['id']\n                    label = segment_info['category']\n                    mask = pan_png == id\n                    isthing = categories[label]['isthing']\n                    if isthing:\n                        iscrowd = 1 if not segment_info['is_thing'] else 0\n                    else:\n                        iscrowd = 0\n\n                    new_segment_info = {\n                        'id': id,\n                        'category_id': label,\n                        'isthing': isthing,\n                        'iscrowd': iscrowd,\n                        'area': mask.sum()\n                    }\n                    segments_info.append(new_segment_info)\n            else:\n                # get segments_info from annotation file\n                segments_info = self._coco_api.imgToAnns[img_id]\n\n            gt['segments_info'] = segments_info\n\n            pq_stats = pq_compute_single_core(\n                proc_id=0,\n                annotation_set=[(gt, result)],\n                gt_folder=self.seg_prefix,\n                pred_folder=self.seg_out_dir,\n                categories=categories,\n                backend_args=self.backend_args)\n\n            self.results.append(pq_stats)\n\n    def _process_gt_and_predictions(self, data_samples: Sequence[dict]):\n        \"\"\"Process gts and predictions when ``outfile_prefix`` is set.\n\n        The predictions will be saved to directory specified by\n        ``outfile_predfix``. The matched pair (gt, result) will be put into\n        ``self.results``.\n        \"\"\"\n        for data_sample in data_samples:\n            # parse pred\n            img_id = data_sample['img_id']\n            segm_file = osp.basename(data_sample['img_path']).replace(\n                '.jpg', '.png')\n            result = self._parse_predictions(\n                pred=data_sample, img_id=img_id, segm_file=segm_file)\n\n            # parse gt\n            gt = dict()\n            gt['image_id'] = img_id\n            gt['width'] = data_sample['ori_shape'][1]\n            gt['height'] = data_sample['ori_shape'][0]\n\n            if self._coco_api is None:\n                # get segments_info from dataset\n                gt['segments_info'] = data_sample['segments_info']\n                gt['seg_map_path'] = data_sample['seg_map_path']\n\n            self.results.append((gt, result))\n\n    # TODO: data_batch is no longer needed, consider adjusting the\n    #  parameter position\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        # If ``self.tmp_dir`` is none, it will save gt and predictions to\n        # self.results, otherwise, it will compute pq_stats here.\n        if self.tmp_dir is None:\n            self._process_gt_and_predictions(data_samples)\n        else:\n            self._compute_batch_pq_stats(data_samples)\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch. There\n                are two cases:\n\n                - When ``outfile_prefix`` is not provided, the elements in\n                  results are pq_stats which can be summed directly to get PQ.\n                - When ``outfile_prefix`` is provided, the elements in\n                  results are tuples like (gt, pred).\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n                the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        if self.tmp_dir is None:\n            # do evaluation after collect all the results\n\n            # split gt and prediction list\n            gts, preds = zip(*results)\n\n            if self._coco_api is None:\n                # use converted gt json file to initialize coco api\n                logger.info('Converting ground truth to coco format...')\n                coco_json_path, gt_folder = self.gt_to_coco_json(\n                    gt_dicts=gts, outfile_prefix=self.outfile_prefix)\n                self._coco_api = COCOPanoptic(coco_json_path)\n            else:\n                gt_folder = self.seg_prefix\n\n            self.cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['classes'])\n            self.cat2label = {\n                cat_id: i\n                for i, cat_id in enumerate(self.cat_ids)\n            }\n            self.img_ids = self._coco_api.get_img_ids()\n            self.categories = self._coco_api.cats\n\n            # convert predictions to coco format and dump to json file\n            json_filename, pred_folder = self.result2json(\n                results=preds, outfile_prefix=self.outfile_prefix)\n\n            if self.format_only:\n                logger.info('results are saved in '\n                            f'{osp.dirname(self.outfile_prefix)}')\n                return dict()\n\n            imgs = self._coco_api.imgs\n            gt_json = self._coco_api.img_ann_map\n            gt_json = [{\n                'image_id': k,\n                'segments_info': v,\n                'file_name': imgs[k]['segm_file']\n            } for k, v in gt_json.items()]\n            pred_json = load(json_filename)\n            pred_json = dict(\n                (el['image_id'], el) for el in pred_json['annotations'])\n\n            # match the gt_anns and pred_anns in the same image\n            matched_annotations_list = []\n            for gt_ann in gt_json:\n                img_id = gt_ann['image_id']\n                if img_id not in pred_json.keys():\n                    raise Exception('no prediction for the image'\n                                    ' with id: {}'.format(img_id))\n                matched_annotations_list.append((gt_ann, pred_json[img_id]))\n\n            pq_stat = pq_compute_multi_core(\n                matched_annotations_list,\n                gt_folder,\n                pred_folder,\n                self.categories,\n                backend_args=self.backend_args,\n                nproc=self.nproc)\n\n        else:\n            # aggregate the results generated in process\n            if self._coco_api is None:\n                categories = dict()\n                for id, name in enumerate(self.dataset_meta['classes']):\n                    isthing = 1 if name in self.dataset_meta[\n                        'thing_classes'] else 0\n                    categories[id] = {\n                        'id': id,\n                        'name': name,\n                        'isthing': isthing\n                    }\n                self.categories = categories\n\n            pq_stat = PQStat()\n            for result in results:\n                pq_stat += result\n\n        metrics = [('All', None), ('Things', True), ('Stuff', False)]\n        pq_results = {}\n\n        for name, isthing in metrics:\n            pq_results[name], classwise_results = pq_stat.pq_average(\n                self.categories, isthing=isthing)\n            if name == 'All':\n                pq_results['classwise'] = classwise_results\n\n        classwise_results = None\n        if self.classwise:\n            classwise_results = {\n                k: v\n                for k, v in zip(self.dataset_meta['classes'],\n                                pq_results['classwise'].values())\n            }\n\n        print_panoptic_table(pq_results, classwise_results, logger=logger)\n        results = parse_pq_results(pq_results)\n\n        return results\n\n\ndef parse_pq_results(pq_results: dict) -> dict:\n    \"\"\"Parse the Panoptic Quality results.\n\n    Args:\n        pq_results (dict): Panoptic Quality results.\n\n    Returns:\n        dict: Panoptic Quality results parsed.\n    \"\"\"\n    result = dict()\n    result['PQ'] = 100 * pq_results['All']['pq']\n    result['SQ'] = 100 * pq_results['All']['sq']\n    result['RQ'] = 100 * pq_results['All']['rq']\n    result['PQ_th'] = 100 * pq_results['Things']['pq']\n    result['SQ_th'] = 100 * pq_results['Things']['sq']\n    result['RQ_th'] = 100 * pq_results['Things']['rq']\n    result['PQ_st'] = 100 * pq_results['Stuff']['pq']\n    result['SQ_st'] = 100 * pq_results['Stuff']['sq']\n    result['RQ_st'] = 100 * pq_results['Stuff']['rq']\n    return result\n\n\ndef print_panoptic_table(\n        pq_results: dict,\n        classwise_results: Optional[dict] = None,\n        logger: Optional[Union['MMLogger', str]] = None) -> None:\n    \"\"\"Print the panoptic evaluation results table.\n\n    Args:\n        pq_results(dict): The Panoptic Quality results.\n        classwise_results(dict, optional): The classwise Panoptic Quality.\n            results. The keys are class names and the values are metrics.\n            Defaults to None.\n        logger (:obj:`MMLogger` | str, optional): Logger used for printing\n            related information during evaluation. Default: None.\n    \"\"\"\n\n    headers = ['', 'PQ', 'SQ', 'RQ', 'categories']\n    data = [headers]\n    for name in ['All', 'Things', 'Stuff']:\n        numbers = [\n            f'{(pq_results[name][k] * 100):0.3f}' for k in ['pq', 'sq', 'rq']\n        ]\n        row = [name] + numbers + [pq_results[name]['n']]\n        data.append(row)\n    table = AsciiTable(data)\n    print_log('Panoptic Evaluation Results:\\n' + table.table, logger=logger)\n\n    if classwise_results is not None:\n        class_metrics = [(name, ) + tuple(f'{(metrics[k] * 100):0.3f}'\n                                          for k in ['pq', 'sq', 'rq'])\n                         for name, metrics in classwise_results.items()]\n        num_columns = min(8, len(class_metrics) * 4)\n        results_flatten = list(itertools.chain(*class_metrics))\n        headers = ['category', 'PQ', 'SQ', 'RQ'] * (num_columns // 4)\n        results_2d = itertools.zip_longest(\n            *[results_flatten[i::num_columns] for i in range(num_columns)])\n        data = [headers]\n        data += [result for result in results_2d]\n        table = AsciiTable(data)\n        print_log(\n            'Classwise Panoptic Evaluation Results:\\n' + table.table,\n            logger=logger)\n"
  },
  {
    "path": "mmdet/evaluation/metrics/coco_video_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import Sequence\n\nfrom mmengine.dist import broadcast_object_list, is_main_process\n\nfrom mmdet.registry import METRICS\nfrom .base_video_metric import collect_tracking_results\nfrom .coco_metric import CocoMetric\n\n\n@METRICS.register_module()\nclass CocoVideoMetric(CocoMetric):\n    \"\"\"COCO evaluation metric.\n\n    Evaluate AR, AP, and mAP for detection tasks including proposal/box\n    detection and instance segmentation. Please refer to\n    https://cocodataset.org/#detection-eval for more details.\n    \"\"\"\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions.\n\n        The processed results should be stored in ``self.results``, which will\n        be used to compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for track_data_sample in data_samples:\n            video_data_samples = track_data_sample['video_data_samples']\n            ori_video_len = video_data_samples[0].ori_video_length\n            video_len = len(video_data_samples)\n            if ori_video_len == video_len:\n                # video process\n                for frame_id in range(video_len):\n                    img_data_sample = video_data_samples[frame_id].to_dict()\n                    super().process(None, [img_data_sample])\n            else:\n                # image process\n                img_data_sample = video_data_samples[0].to_dict()\n                super().process(None, [img_data_sample])\n\n    def evaluate(self, size: int = 1) -> dict:\n        \"\"\"Evaluate the model performance of the whole dataset after processing\n        all batches.\n\n        Args:\n            size (int): Length of the entire validation dataset.\n        Returns:\n            dict: Evaluation metrics dict on the val dataset. The keys are the\n            names of the metrics, and the values are corresponding results.\n        \"\"\"\n        if len(self.results) == 0:\n            warnings.warn(\n                f'{self.__class__.__name__} got empty `self.results`. Please '\n                'ensure that the processed results are properly added into '\n                '`self.results` in `process` method.')\n\n        results = collect_tracking_results(self.results, self.collect_device)\n\n        if is_main_process():\n            _metrics = self.compute_metrics(results)  # type: ignore\n            # Add prefix to metric names\n            if self.prefix:\n                _metrics = {\n                    '/'.join((self.prefix, k)): v\n                    for k, v in _metrics.items()\n                }\n            metrics = [_metrics]\n        else:\n            metrics = [None]  # type: ignore\n\n        broadcast_object_list(metrics)\n\n        # reset the results list\n        self.results.clear()\n        return metrics[0]\n"
  },
  {
    "path": "mmdet/evaluation/metrics/crowdhuman_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport json\nimport os.path as osp\nimport tempfile\nfrom collections import OrderedDict\nfrom multiprocessing import Process, Queue\nfrom typing import Dict, List, Optional, Sequence, Union\n\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import dump, get_text, load\nfrom mmengine.logging import MMLogger\nfrom scipy.sparse import csr_matrix\nfrom scipy.sparse.csgraph import maximum_bipartite_matching\n\nfrom mmdet.evaluation.functional.bbox_overlaps import bbox_overlaps\nfrom mmdet.registry import METRICS\n\nPERSON_CLASSES = ['background', 'person']\n\n\n@METRICS.register_module()\nclass CrowdHumanMetric(BaseMetric):\n    \"\"\"CrowdHuman evaluation metric.\n\n    Evaluate Average Precision (AP), Miss Rate (MR) and Jaccard Index (JI)\n    for detection tasks.\n\n    Args:\n        ann_file (str): Path to the annotation file.\n        metric (str | List[str]): Metrics to be evaluated. Valid metrics\n            include 'AP', 'MR' and 'JI'. Defaults to 'AP'.\n        format_only (bool): Format the output results without perform\n            evaluation. It is useful when you want to format the result\n            to a specific format and submit it to the test server.\n            Defaults to False.\n        outfile_prefix (str, optional): The prefix of json files. It includes\n            the file path and the prefix of filename, e.g., \"a/b/prefix\".\n            If not specified, a temp file will be created. Defaults to None.\n        file_client_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n        eval_mode (int): Select the mode of evaluate. Valid mode include\n            0(just body box), 1(just head box) and 2(both of them).\n            Defaults to 0.\n        iou_thres (float): IoU threshold. Defaults to 0.5.\n        compare_matching_method (str, optional): Matching method to compare\n            the detection results with the ground_truth when compute 'AP'\n            and 'MR'.Valid method include VOC and None(CALTECH). Default to\n            None.\n        mr_ref (str): Different parameter selection to calculate MR. Valid\n            ref include CALTECH_-2 and CALTECH_-4. Defaults to CALTECH_-2.\n        num_ji_process (int): The number of processes to evaluation JI.\n            Defaults to 10.\n    \"\"\"\n    default_prefix: Optional[str] = 'crowd_human'\n\n    def __init__(self,\n                 ann_file: str,\n                 metric: Union[str, List[str]] = ['AP', 'MR', 'JI'],\n                 format_only: bool = False,\n                 outfile_prefix: Optional[str] = None,\n                 file_client_args: dict = None,\n                 backend_args: dict = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None,\n                 eval_mode: int = 0,\n                 iou_thres: float = 0.5,\n                 compare_matching_method: Optional[str] = None,\n                 mr_ref: str = 'CALTECH_-2',\n                 num_ji_process: int = 10) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n\n        self.ann_file = ann_file\n        # crowdhuman evaluation metrics\n        self.metrics = metric if isinstance(metric, list) else [metric]\n        allowed_metrics = ['MR', 'AP', 'JI']\n        for metric in self.metrics:\n            if metric not in allowed_metrics:\n                raise KeyError(f\"metric should be one of 'MR', 'AP', 'JI',\"\n                               f'but got {metric}.')\n\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n        self.outfile_prefix = outfile_prefix\n        self.backend_args = backend_args\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n\n        assert eval_mode in [0, 1, 2], \\\n            \"Unknown eval mode. mr_ref should be one of '0', '1', '2'.\"\n        assert compare_matching_method is None or \\\n               compare_matching_method == 'VOC', \\\n               'The alternative compare_matching_method is VOC.' \\\n               'This parameter defaults to CALTECH(None)'\n        assert mr_ref == 'CALTECH_-2' or mr_ref == 'CALTECH_-4', \\\n            \"mr_ref should be one of 'CALTECH_-2', 'CALTECH_-4'.\"\n        self.eval_mode = eval_mode\n        self.iou_thres = iou_thres\n        self.compare_matching_method = compare_matching_method\n        self.mr_ref = mr_ref\n        self.num_ji_process = num_ji_process\n\n    @staticmethod\n    def results2json(results: Sequence[dict], outfile_prefix: str) -> str:\n        \"\"\"Dump the detection results to a json file.\"\"\"\n        result_file_path = f'{outfile_prefix}.json'\n        bbox_json_results = []\n        for i, result in enumerate(results):\n            ann, pred = result\n            dump_dict = dict()\n            dump_dict['ID'] = ann['ID']\n            dump_dict['width'] = ann['width']\n            dump_dict['height'] = ann['height']\n            dtboxes = []\n            bboxes = pred.tolist()\n            for _, single_bbox in enumerate(bboxes):\n                temp_dict = dict()\n                x1, y1, x2, y2, score = single_bbox\n                temp_dict['box'] = [x1, y1, x2 - x1, y2 - y1]\n                temp_dict['score'] = score\n                temp_dict['tag'] = 1\n                dtboxes.append(temp_dict)\n            dump_dict['dtboxes'] = dtboxes\n            bbox_json_results.append(dump_dict)\n        dump(bbox_json_results, result_file_path)\n        return result_file_path\n\n    def process(self, data_batch: Sequence[dict],\n                data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            ann = dict()\n            ann['ID'] = data_sample['img_id']\n            ann['width'] = data_sample['ori_shape'][1]\n            ann['height'] = data_sample['ori_shape'][0]\n            pred_bboxes = data_sample['pred_instances']['bboxes'].cpu().numpy()\n            pred_scores = data_sample['pred_instances']['scores'].cpu().numpy()\n\n            pred_bbox_scores = np.hstack(\n                [pred_bboxes, pred_scores.reshape((-1, 1))])\n\n            self.results.append((ann, pred_bbox_scores))\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            eval_results(Dict[str, float]): The computed metrics.\n            The keys are the names of the metrics, and the values\n            are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        tmp_dir = None\n        if self.outfile_prefix is None:\n            tmp_dir = tempfile.TemporaryDirectory()\n            outfile_prefix = osp.join(tmp_dir.name, 'result')\n        else:\n            outfile_prefix = self.outfile_prefix\n\n        # convert predictions to coco format and dump to json file\n        result_file = self.results2json(results, outfile_prefix)\n        eval_results = OrderedDict()\n        if self.format_only:\n            logger.info(f'results are saved in {osp.dirname(outfile_prefix)}')\n            return eval_results\n\n        # load evaluation samples\n        eval_samples = self.load_eval_samples(result_file)\n\n        if 'AP' in self.metrics or 'MR' in self.metrics:\n            score_list = self.compare(eval_samples)\n            gt_num = sum([eval_samples[i].gt_num for i in eval_samples])\n            ign_num = sum([eval_samples[i].ign_num for i in eval_samples])\n            gt_num = gt_num - ign_num\n            img_num = len(eval_samples)\n\n        for metric in self.metrics:\n            logger.info(f'Evaluating {metric}...')\n            if metric == 'AP':\n                AP = self.eval_ap(score_list, gt_num, img_num)\n                eval_results['mAP'] = float(f'{round(AP, 4)}')\n            if metric == 'MR':\n                MR = self.eval_mr(score_list, gt_num, img_num)\n                eval_results['mMR'] = float(f'{round(MR, 4)}')\n            if metric == 'JI':\n                JI = self.eval_ji(eval_samples)\n                eval_results['JI'] = float(f'{round(JI, 4)}')\n        if tmp_dir is not None:\n            tmp_dir.cleanup()\n\n        return eval_results\n\n    def load_eval_samples(self, result_file):\n        \"\"\"Load data from annotations file and detection results.\n\n        Args:\n            result_file (str): The file path of the saved detection results.\n\n        Returns:\n            Dict[Image]: The detection result packaged by Image\n        \"\"\"\n        gt_str = get_text(\n            self.ann_file, backend_args=self.backend_args).strip().split('\\n')\n        gt_records = [json.loads(line) for line in gt_str]\n\n        pred_records = load(result_file, backend_args=self.backend_args)\n        eval_samples = dict()\n        for gt_record, pred_record in zip(gt_records, pred_records):\n            assert gt_record['ID'] == pred_record['ID'], \\\n                'please set val_dataloader.sampler.shuffle=False and try again'\n            eval_samples[pred_record['ID']] = Image(self.eval_mode)\n            eval_samples[pred_record['ID']].load(gt_record, 'box', None,\n                                                 PERSON_CLASSES, True)\n            eval_samples[pred_record['ID']].load(pred_record, 'box', None,\n                                                 PERSON_CLASSES, False)\n            eval_samples[pred_record['ID']].clip_all_boader()\n        return eval_samples\n\n    def compare(self, samples):\n        \"\"\"Match the detection results with the ground_truth.\n\n        Args:\n            samples (dict[Image]): The detection result packaged by Image.\n\n        Returns:\n            score_list(list[tuple[ndarray, int, str]]): Matching result.\n            a list of tuples (dtbox, label, imgID) in the descending\n            sort of dtbox.score.\n        \"\"\"\n        score_list = list()\n        for id in samples:\n            if self.compare_matching_method == 'VOC':\n                result = samples[id].compare_voc(self.iou_thres)\n            else:\n                result = samples[id].compare_caltech(self.iou_thres)\n            score_list.extend(result)\n        # In the descending sort of dtbox score.\n        score_list.sort(key=lambda x: x[0][-1], reverse=True)\n        return score_list\n\n    @staticmethod\n    def eval_ap(score_list, gt_num, img_num):\n        \"\"\"Evaluate by average precision.\n\n        Args:\n            score_list(list[tuple[ndarray, int, str]]): Matching result.\n                a list of tuples (dtbox, label, imgID) in the descending\n                sort of dtbox.score.\n            gt_num(int): The number of gt boxes in the entire dataset.\n            img_num(int): The number of images in the entire dataset.\n\n        Returns:\n            ap(float): result of average precision.\n        \"\"\"\n\n        # calculate general ap score\n        def _calculate_map(_recall, _precision):\n            assert len(_recall) == len(_precision)\n            area = 0\n            for k in range(1, len(_recall)):\n                delta_h = (_precision[k - 1] + _precision[k]) / 2\n                delta_w = _recall[k] - _recall[k - 1]\n                area += delta_w * delta_h\n            return area\n\n        tp, fp = 0.0, 0.0\n        rpX, rpY = list(), list()\n\n        fpn = []\n        recalln = []\n        thr = []\n        fppi = []\n        for i, item in enumerate(score_list):\n            if item[1] == 1:\n                tp += 1.0\n            elif item[1] == 0:\n                fp += 1.0\n            fn = gt_num - tp\n            recall = tp / (tp + fn)\n            precision = tp / (tp + fp)\n            rpX.append(recall)\n            rpY.append(precision)\n            fpn.append(fp)\n            recalln.append(tp)\n            thr.append(item[0][-1])\n            fppi.append(fp / img_num)\n\n        ap = _calculate_map(rpX, rpY)\n        return ap\n\n    def eval_mr(self, score_list, gt_num, img_num):\n        \"\"\"Evaluate by Caltech-style log-average miss rate.\n\n        Args:\n            score_list(list[tuple[ndarray, int, str]]): Matching result.\n                a list of tuples (dtbox, label, imgID) in the descending\n                sort of dtbox.score.\n            gt_num(int): The number of gt boxes in the entire dataset.\n            img_num(int): The number of image in the entire dataset.\n\n        Returns:\n            mr(float): result of miss rate.\n        \"\"\"\n\n        # find greater_than\n        def _find_gt(lst, target):\n            for idx, _item in enumerate(lst):\n                if _item >= target:\n                    return idx\n            return len(lst) - 1\n\n        if self.mr_ref == 'CALTECH_-2':\n            # CALTECH_MRREF_2: anchor points (from 10^-2 to 1) as in\n            # P.Dollar's paper\n            ref = [\n                0.0100, 0.0178, 0.03160, 0.0562, 0.1000, 0.1778, 0.3162,\n                0.5623, 1.000\n            ]\n        else:\n            # CALTECH_MRREF_4: anchor points (from 10^-4 to 1) as in\n            # S.Zhang's paper\n            ref = [\n                0.0001, 0.0003, 0.00100, 0.0032, 0.0100, 0.0316, 0.1000,\n                0.3162, 1.000\n            ]\n\n        tp, fp = 0.0, 0.0\n        fppiX, fppiY = list(), list()\n        for i, item in enumerate(score_list):\n            if item[1] == 1:\n                tp += 1.0\n            elif item[1] == 0:\n                fp += 1.0\n\n            fn = gt_num - tp\n            recall = tp / (tp + fn)\n            missrate = 1.0 - recall\n            fppi = fp / img_num\n            fppiX.append(fppi)\n            fppiY.append(missrate)\n\n        score = list()\n        for pos in ref:\n            argmin = _find_gt(fppiX, pos)\n            if argmin >= 0:\n                score.append(fppiY[argmin])\n        score = np.array(score)\n        mr = np.exp(np.log(score).mean())\n        return mr\n\n    def eval_ji(self, samples):\n        \"\"\"Evaluate by JI using multi_process.\n\n        Args:\n            samples(Dict[str, Image]): The detection result packaged by Image.\n\n        Returns:\n            ji(float): result of jaccard index.\n        \"\"\"\n        import math\n        res_line = []\n        res_ji = []\n        for i in range(10):\n            score_thr = 1e-1 * i\n            total = len(samples)\n            stride = math.ceil(total / self.num_ji_process)\n            result_queue = Queue(10000)\n            results, procs = [], []\n            records = list(samples.items())\n            for i in range(self.num_ji_process):\n                start = i * stride\n                end = np.min([start + stride, total])\n                sample_data = dict(records[start:end])\n                p = Process(\n                    target=self.compute_ji_with_ignore,\n                    args=(result_queue, sample_data, score_thr))\n                p.start()\n                procs.append(p)\n            for i in range(total):\n                t = result_queue.get()\n                results.append(t)\n            for p in procs:\n                p.join()\n            line, mean_ratio = self.gather(results)\n            line = 'score_thr:{:.1f}, {}'.format(score_thr, line)\n            res_line.append(line)\n            res_ji.append(mean_ratio)\n        return max(res_ji)\n\n    def compute_ji_with_ignore(self, result_queue, dt_result, score_thr):\n        \"\"\"Compute JI with ignore.\n\n        Args:\n            result_queue(Queue): The Queue for save compute result when\n                multi_process.\n            dt_result(dict[Image]): Detection result packaged by Image.\n            score_thr(float): The threshold of detection score.\n        Returns:\n            dict: compute result.\n        \"\"\"\n        for ID, record in dt_result.items():\n            gt_boxes = record.gt_boxes\n            dt_boxes = record.dt_boxes\n            keep = dt_boxes[:, -1] > score_thr\n            dt_boxes = dt_boxes[keep][:, :-1]\n\n            gt_tag = np.array(gt_boxes[:, -1] != -1)\n            matches = self.compute_ji_matching(dt_boxes, gt_boxes[gt_tag, :4])\n            # get the unmatched_indices\n            matched_indices = np.array([j for (j, _) in matches])\n            unmatched_indices = list(\n                set(np.arange(dt_boxes.shape[0])) - set(matched_indices))\n            num_ignore_dt = self.get_ignores(dt_boxes[unmatched_indices],\n                                             gt_boxes[~gt_tag, :4])\n            matched_indices = np.array([j for (_, j) in matches])\n            unmatched_indices = list(\n                set(np.arange(gt_boxes[gt_tag].shape[0])) -\n                set(matched_indices))\n            num_ignore_gt = self.get_ignores(\n                gt_boxes[gt_tag][unmatched_indices], gt_boxes[~gt_tag, :4])\n            # compute results\n            eps = 1e-6\n            k = len(matches)\n            m = gt_tag.sum() - num_ignore_gt\n            n = dt_boxes.shape[0] - num_ignore_dt\n            ratio = k / (m + n - k + eps)\n            recall = k / (m + eps)\n            cover = k / (n + eps)\n            noise = 1 - cover\n            result_dict = dict(\n                ratio=ratio,\n                recall=recall,\n                cover=cover,\n                noise=noise,\n                k=k,\n                m=m,\n                n=n)\n            result_queue.put_nowait(result_dict)\n\n    @staticmethod\n    def gather(results):\n        \"\"\"Integrate test results.\"\"\"\n        assert len(results)\n        img_num = 0\n        for result in results:\n            if result['n'] != 0 or result['m'] != 0:\n                img_num += 1\n        mean_ratio = np.sum([rb['ratio'] for rb in results]) / img_num\n        valids = np.sum([rb['k'] for rb in results])\n        total = np.sum([rb['n'] for rb in results])\n        gtn = np.sum([rb['m'] for rb in results])\n        line = 'mean_ratio:{:.4f}, valids:{}, total:{}, gtn:{}'\\\n            .format(mean_ratio, valids, total, gtn)\n        return line, mean_ratio\n\n    def compute_ji_matching(self, dt_boxes, gt_boxes):\n        \"\"\"Match the annotation box for each detection box.\n\n        Args:\n            dt_boxes(ndarray): Detection boxes.\n            gt_boxes(ndarray): Ground_truth boxes.\n\n        Returns:\n            matches_(list[tuple[int, int]]): Match result.\n        \"\"\"\n        assert dt_boxes.shape[-1] > 3 and gt_boxes.shape[-1] > 3\n        if dt_boxes.shape[0] < 1 or gt_boxes.shape[0] < 1:\n            return list()\n\n        ious = bbox_overlaps(dt_boxes, gt_boxes, mode='iou')\n        input_ = copy.deepcopy(ious)\n        input_[input_ < self.iou_thres] = 0\n        match_scipy = maximum_bipartite_matching(\n            csr_matrix(input_), perm_type='column')\n        matches_ = []\n        for i in range(len(match_scipy)):\n            if match_scipy[i] != -1:\n                matches_.append((i, int(match_scipy[i])))\n        return matches_\n\n    def get_ignores(self, dt_boxes, gt_boxes):\n        \"\"\"Get the number of ignore bboxes.\"\"\"\n        if gt_boxes.size:\n            ioas = bbox_overlaps(dt_boxes, gt_boxes, mode='iof')\n            ioas = np.max(ioas, axis=1)\n            rows = np.where(ioas > self.iou_thres)[0]\n            return len(rows)\n        else:\n            return 0\n\n\nclass Image(object):\n    \"\"\"Data structure for evaluation of CrowdHuman.\n\n    Note:\n        This implementation is modified from https://github.com/Purkialo/\n        CrowdDet/blob/master/lib/evaluate/APMRToolkits/image.py\n\n    Args:\n        mode (int): Select the mode of evaluate. Valid mode include\n            0(just body box), 1(just head box) and 2(both of them).\n            Defaults to 0.\n    \"\"\"\n\n    def __init__(self, mode):\n        self.ID = None\n        self.width = None\n        self.height = None\n        self.dt_boxes = None\n        self.gt_boxes = None\n        self.eval_mode = mode\n\n        self.ign_num = None\n        self.gt_num = None\n        self.dt_num = None\n\n    def load(self, record, body_key, head_key, class_names, gt_flag):\n        \"\"\"Loading information for evaluation.\n\n        Args:\n            record (dict): Label information or test results.\n                The format might look something like this:\n                {\n                    'ID': '273271,c9db000d5146c15',\n                    'gtboxes': [\n                        {'fbox': [72, 202, 163, 503], 'tag': 'person', ...},\n                        {'fbox': [199, 180, 144, 499], 'tag': 'person', ...},\n                        ...\n                    ]\n                }\n                or:\n                {\n                    'ID': '273271,c9db000d5146c15',\n                    'width': 800,\n                    'height': 1067,\n                    'dtboxes': [\n                        {\n                            'box': [306.22, 205.95, 164.05, 394.04],\n                            'score': 0.99,\n                            'tag': 1\n                        },\n                        {\n                            'box': [403.60, 178.66, 157.15, 421.33],\n                            'score': 0.99,\n                            'tag': 1\n                        },\n                        ...\n                    ]\n                }\n            body_key (str, None): key of detection body box.\n                Valid when loading detection results and self.eval_mode!=1.\n            head_key (str, None): key of detection head box.\n                Valid when loading detection results and self.eval_mode!=0.\n            class_names (list[str]):class names of data set.\n                Defaults to ['background', 'person'].\n            gt_flag (bool): Indicate whether record is ground truth\n                or predicting the outcome.\n        \"\"\"\n        if 'ID' in record and self.ID is None:\n            self.ID = record['ID']\n        if 'width' in record and self.width is None:\n            self.width = record['width']\n        if 'height' in record and self.height is None:\n            self.height = record['height']\n        if gt_flag:\n            self.gt_num = len(record['gtboxes'])\n            body_bbox, head_bbox = self.load_gt_boxes(record, 'gtboxes',\n                                                      class_names)\n            if self.eval_mode == 0:\n                self.gt_boxes = body_bbox\n                self.ign_num = (body_bbox[:, -1] == -1).sum()\n            elif self.eval_mode == 1:\n                self.gt_boxes = head_bbox\n                self.ign_num = (head_bbox[:, -1] == -1).sum()\n            else:\n                gt_tag = np.array([\n                    body_bbox[i, -1] != -1 and head_bbox[i, -1] != -1\n                    for i in range(len(body_bbox))\n                ])\n                self.ign_num = (gt_tag == 0).sum()\n                self.gt_boxes = np.hstack(\n                    (body_bbox[:, :-1], head_bbox[:, :-1],\n                     gt_tag.reshape(-1, 1)))\n\n        if not gt_flag:\n            self.dt_num = len(record['dtboxes'])\n            if self.eval_mode == 0:\n                self.dt_boxes = self.load_det_boxes(record, 'dtboxes',\n                                                    body_key, 'score')\n            elif self.eval_mode == 1:\n                self.dt_boxes = self.load_det_boxes(record, 'dtboxes',\n                                                    head_key, 'score')\n            else:\n                body_dtboxes = self.load_det_boxes(record, 'dtboxes', body_key,\n                                                   'score')\n                head_dtboxes = self.load_det_boxes(record, 'dtboxes', head_key,\n                                                   'score')\n                self.dt_boxes = np.hstack((body_dtboxes, head_dtboxes))\n\n    @staticmethod\n    def load_gt_boxes(dict_input, key_name, class_names):\n        \"\"\"load ground_truth and transform [x, y, w, h] to [x1, y1, x2, y2]\"\"\"\n        assert key_name in dict_input\n        if len(dict_input[key_name]) < 1:\n            return np.empty([0, 5])\n        head_bbox = []\n        body_bbox = []\n        for rb in dict_input[key_name]:\n            if rb['tag'] in class_names:\n                body_tag = class_names.index(rb['tag'])\n                head_tag = copy.deepcopy(body_tag)\n            else:\n                body_tag = -1\n                head_tag = -1\n            if 'extra' in rb:\n                if 'ignore' in rb['extra']:\n                    if rb['extra']['ignore'] != 0:\n                        body_tag = -1\n                        head_tag = -1\n            if 'head_attr' in rb:\n                if 'ignore' in rb['head_attr']:\n                    if rb['head_attr']['ignore'] != 0:\n                        head_tag = -1\n            head_bbox.append(np.hstack((rb['hbox'], head_tag)))\n            body_bbox.append(np.hstack((rb['fbox'], body_tag)))\n        head_bbox = np.array(head_bbox)\n        head_bbox[:, 2:4] += head_bbox[:, :2]\n        body_bbox = np.array(body_bbox)\n        body_bbox[:, 2:4] += body_bbox[:, :2]\n        return body_bbox, head_bbox\n\n    @staticmethod\n    def load_det_boxes(dict_input, key_name, key_box, key_score, key_tag=None):\n        \"\"\"load detection boxes.\"\"\"\n        assert key_name in dict_input\n        if len(dict_input[key_name]) < 1:\n            return np.empty([0, 5])\n        else:\n            assert key_box in dict_input[key_name][0]\n            if key_score:\n                assert key_score in dict_input[key_name][0]\n            if key_tag:\n                assert key_tag in dict_input[key_name][0]\n        if key_score:\n            if key_tag:\n                bboxes = np.vstack([\n                    np.hstack((rb[key_box], rb[key_score], rb[key_tag]))\n                    for rb in dict_input[key_name]\n                ])\n            else:\n                bboxes = np.vstack([\n                    np.hstack((rb[key_box], rb[key_score]))\n                    for rb in dict_input[key_name]\n                ])\n        else:\n            if key_tag:\n                bboxes = np.vstack([\n                    np.hstack((rb[key_box], rb[key_tag]))\n                    for rb in dict_input[key_name]\n                ])\n            else:\n                bboxes = np.vstack(\n                    [rb[key_box] for rb in dict_input[key_name]])\n        bboxes[:, 2:4] += bboxes[:, :2]\n        return bboxes\n\n    def clip_all_boader(self):\n        \"\"\"Make sure boxes are within the image range.\"\"\"\n\n        def _clip_boundary(boxes, height, width):\n            assert boxes.shape[-1] >= 4\n            boxes[:, 0] = np.minimum(np.maximum(boxes[:, 0], 0), width - 1)\n            boxes[:, 1] = np.minimum(np.maximum(boxes[:, 1], 0), height - 1)\n            boxes[:, 2] = np.maximum(np.minimum(boxes[:, 2], width), 0)\n            boxes[:, 3] = np.maximum(np.minimum(boxes[:, 3], height), 0)\n            return boxes\n\n        assert self.dt_boxes.shape[-1] >= 4\n        assert self.gt_boxes.shape[-1] >= 4\n        assert self.width is not None and self.height is not None\n        if self.eval_mode == 2:\n            self.dt_boxes[:, :4] = _clip_boundary(self.dt_boxes[:, :4],\n                                                  self.height, self.width)\n            self.gt_boxes[:, :4] = _clip_boundary(self.gt_boxes[:, :4],\n                                                  self.height, self.width)\n            self.dt_boxes[:, 4:8] = _clip_boundary(self.dt_boxes[:, 4:8],\n                                                   self.height, self.width)\n            self.gt_boxes[:, 4:8] = _clip_boundary(self.gt_boxes[:, 4:8],\n                                                   self.height, self.width)\n        else:\n            self.dt_boxes = _clip_boundary(self.dt_boxes, self.height,\n                                           self.width)\n            self.gt_boxes = _clip_boundary(self.gt_boxes, self.height,\n                                           self.width)\n\n    def compare_voc(self, thres):\n        \"\"\"Match the detection results with the ground_truth by VOC.\n\n        Args:\n            thres (float): IOU threshold.\n\n        Returns:\n            score_list(list[tuple[ndarray, int, str]]): Matching result.\n            a list of tuples (dtbox, label, imgID) in the descending\n            sort of dtbox.score.\n        \"\"\"\n        if self.dt_boxes is None:\n            return list()\n        dtboxes = self.dt_boxes\n        gtboxes = self.gt_boxes if self.gt_boxes is not None else list()\n        dtboxes.sort(key=lambda x: x.score, reverse=True)\n        gtboxes.sort(key=lambda x: x.ign)\n\n        score_list = list()\n        for i, dt in enumerate(dtboxes):\n            maxpos = -1\n            maxiou = thres\n\n            for j, gt in enumerate(gtboxes):\n                overlap = dt.iou(gt)\n                if overlap > maxiou:\n                    maxiou = overlap\n                    maxpos = j\n\n            if maxpos >= 0:\n                if gtboxes[maxpos].ign == 0:\n                    gtboxes[maxpos].matched = 1\n                    dtboxes[i].matched = 1\n                    score_list.append((dt, self.ID))\n                else:\n                    dtboxes[i].matched = -1\n            else:\n                dtboxes[i].matched = 0\n                score_list.append((dt, self.ID))\n        return score_list\n\n    def compare_caltech(self, thres):\n        \"\"\"Match the detection results with the ground_truth by Caltech\n        matching strategy.\n\n        Args:\n            thres (float): IOU threshold.\n\n        Returns:\n            score_list(list[tuple[ndarray, int, str]]): Matching result.\n            a list of tuples (dtbox, label, imgID) in the descending\n            sort of dtbox.score.\n        \"\"\"\n        if self.dt_boxes is None or self.gt_boxes is None:\n            return list()\n\n        dtboxes = self.dt_boxes if self.dt_boxes is not None else list()\n        gtboxes = self.gt_boxes if self.gt_boxes is not None else list()\n        dt_matched = np.zeros(dtboxes.shape[0])\n        gt_matched = np.zeros(gtboxes.shape[0])\n\n        dtboxes = np.array(sorted(dtboxes, key=lambda x: x[-1], reverse=True))\n        gtboxes = np.array(sorted(gtboxes, key=lambda x: x[-1], reverse=True))\n        if len(dtboxes):\n            overlap_iou = bbox_overlaps(dtboxes, gtboxes, mode='iou')\n            overlap_ioa = bbox_overlaps(dtboxes, gtboxes, mode='iof')\n        else:\n            return list()\n\n        score_list = list()\n        for i, dt in enumerate(dtboxes):\n            maxpos = -1\n            maxiou = thres\n            for j, gt in enumerate(gtboxes):\n                if gt_matched[j] == 1:\n                    continue\n                if gt[-1] > 0:\n                    overlap = overlap_iou[i][j]\n                    if overlap > maxiou:\n                        maxiou = overlap\n                        maxpos = j\n                else:\n                    if maxpos >= 0:\n                        break\n                    else:\n                        overlap = overlap_ioa[i][j]\n                        if overlap > thres:\n                            maxiou = overlap\n                            maxpos = j\n            if maxpos >= 0:\n                if gtboxes[maxpos, -1] > 0:\n                    gt_matched[maxpos] = 1\n                    dt_matched[i] = 1\n                    score_list.append((dt, 1, self.ID))\n                else:\n                    dt_matched[i] = -1\n            else:\n                dt_matched[i] = 0\n                score_list.append((dt, 0, self.ID))\n        return score_list\n"
  },
  {
    "path": "mmdet/evaluation/metrics/dod_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom collections import defaultdict\nfrom typing import List, Optional, Sequence\n\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import get_local_path\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.datasets.api_wrappers import COCO, COCOeval\nfrom mmdet.registry import METRICS\n\n\n@METRICS.register_module()\nclass DODCocoMetric(BaseMetric):\n\n    default_prefix: Optional[str] = 'dod'\n\n    def __init__(self,\n                 ann_file: Optional[str] = None,\n                 collect_device: str = 'cpu',\n                 outfile_prefix: Optional[str] = None,\n                 backend_args: dict = None,\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        self.outfile_prefix = outfile_prefix\n        with get_local_path(ann_file, backend_args=backend_args) as local_path:\n            self._coco_api = COCO(local_path)\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        for data_sample in data_samples:\n            result = dict()\n            pred = data_sample['pred_instances']\n            result['img_id'] = data_sample['img_id']\n            result['bboxes'] = pred['bboxes'].cpu().numpy()\n            result['scores'] = pred['scores'].cpu().numpy()\n\n            result['labels'] = pred['labels'].cpu().numpy()\n            result['labels'] = data_sample['sent_ids'][result['labels']]\n            self.results.append(result)\n\n    def xyxy2xywh(self, bbox: np.ndarray) -> list:\n        \"\"\"Convert ``xyxy`` style bounding boxes to ``xywh`` style for COCO\n        evaluation.\n\n        Args:\n            bbox (numpy.ndarray): The bounding boxes, shape (4, ), in\n                ``xyxy`` order.\n\n        Returns:\n            list[float]: The converted bounding boxes, in ``xywh`` order.\n        \"\"\"\n\n        _bbox: List = bbox.tolist()\n        return [\n            _bbox[0],\n            _bbox[1],\n            _bbox[2] - _bbox[0],\n            _bbox[3] - _bbox[1],\n        ]\n\n    def results2json(self, results: Sequence[dict]) -> list:\n        \"\"\"Dump the detection results to a COCO style json file.\n\n        There are 3 types of results: proposals, bbox predictions, mask\n        predictions, and they have different data types. This method will\n        automatically recognize the type, and dump them to json files.\n\n        Args:\n            results (Sequence[dict]): Testing results of the\n                dataset.\n\n        Returns:\n            dict: Possible keys are \"bbox\", \"segm\", \"proposal\", and\n            values are corresponding filenames.\n        \"\"\"\n        bbox_json_results = []\n        for idx, result in enumerate(results):\n            image_id = result.get('img_id', idx)\n            labels = result['labels']\n            bboxes = result['bboxes']\n            scores = result['scores']\n            for i, label in enumerate(labels):\n                data = dict()\n                data['image_id'] = image_id\n                data['bbox'] = self.xyxy2xywh(bboxes[i])\n                data['score'] = float(scores[i])\n                data['category_id'] = label\n                bbox_json_results.append(data)\n        return bbox_json_results\n\n    def compute_metrics(self, results: list) -> dict:\n        logger: MMLogger = MMLogger.get_current_instance()\n        result_files = self.results2json(results)\n        d3_res = self._coco_api.loadRes(result_files)\n        cocoEval = COCOeval(self._coco_api, d3_res, 'bbox')\n        cocoEval.evaluate()\n        cocoEval.accumulate()\n        cocoEval.summarize()\n\n        aps = cocoEval.eval['precision'][:, :, :, 0, -1]\n        category_ids = self._coco_api.getCatIds()\n        category_names = [\n            cat['name'] for cat in self._coco_api.loadCats(category_ids)\n        ]\n\n        aps_lens = defaultdict(list)\n        counter_lens = defaultdict(int)\n        for i in range(len(category_names)):\n            ap = aps[:, :, i]\n            ap_value = ap[ap > -1].mean()\n            if not np.isnan(ap_value):\n                len_ref = len(category_names[i].split(' '))\n                aps_lens[len_ref].append(ap_value)\n                counter_lens[len_ref] += 1\n\n        ap_sum_short = sum([sum(aps_lens[i]) for i in range(0, 4)])\n        ap_sum_mid = sum([sum(aps_lens[i]) for i in range(4, 7)])\n        ap_sum_long = sum([sum(aps_lens[i]) for i in range(7, 10)])\n        ap_sum_very_long = sum([\n            sum(aps_lens[i]) for i in range(10,\n                                            max(counter_lens.keys()) + 1)\n        ])\n        c_sum_short = sum([counter_lens[i] for i in range(1, 4)])\n        c_sum_mid = sum([counter_lens[i] for i in range(4, 7)])\n        c_sum_long = sum([counter_lens[i] for i in range(7, 10)])\n        c_sum_very_long = sum(\n            [counter_lens[i] for i in range(10,\n                                            max(counter_lens.keys()) + 1)])\n        map_short = ap_sum_short / c_sum_short\n        map_mid = ap_sum_mid / c_sum_mid\n        map_long = ap_sum_long / c_sum_long\n        map_very_long = ap_sum_very_long / c_sum_very_long\n\n        coco_metric_names = {\n            'mAP': 0,\n            'mAP_50': 1,\n            'mAP_75': 2,\n            'mAP_s': 3,\n            'mAP_m': 4,\n            'mAP_l': 5,\n            'AR@100': 6,\n            'AR@300': 7,\n            'AR@1000': 8,\n            'AR_s@1000': 9,\n            'AR_m@1000': 10,\n            'AR_l@1000': 11\n        }\n        metric_items = ['mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l']\n\n        eval_results = {}\n        for metric_item in metric_items:\n            key = f'{metric_item}'\n            val = cocoEval.stats[coco_metric_names[metric_item]]\n            eval_results[key] = float(f'{round(val, 3)}')\n\n        ap = cocoEval.stats[:6]\n        logger.info(f'mAP_copypaste: {ap[0]:.3f} '\n                    f'{ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} '\n                    f'{ap[4]:.3f} {ap[5]:.3f}')\n\n        logger.info(f'mAP over reference length: short - {map_short:.4f}, '\n                    f'mid - {map_mid:.4f}, long - {map_long:.4f}, '\n                    f'very long - {map_very_long:.4f}')\n        eval_results['mAP_short'] = float(f'{round(map_short, 3)}')\n        eval_results['mAP_mid'] = float(f'{round(map_mid, 3)}')\n        eval_results['mAP_long'] = float(f'{round(map_long, 3)}')\n        eval_results['mAP_very_long'] = float(f'{round(map_very_long, 3)}')\n        return eval_results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/dump_det_results.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import Sequence\n\nfrom mmengine.evaluator import DumpResults\nfrom mmengine.evaluator.metric import _to_cpu\n\nfrom mmdet.registry import METRICS\nfrom mmdet.structures.mask import encode_mask_results\n\n\n@METRICS.register_module()\nclass DumpDetResults(DumpResults):\n    \"\"\"Dump model predictions to a pickle file for offline evaluation.\n\n    Different from `DumpResults` in MMEngine, it compresses instance\n    segmentation masks into RLE format.\n\n    Args:\n        out_file_path (str): Path of the dumped file. Must end with '.pkl'\n            or '.pickle'.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n    \"\"\"\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"transfer tensors in predictions to CPU.\"\"\"\n        data_samples = _to_cpu(data_samples)\n        for data_sample in data_samples:\n            # remove gt\n            data_sample.pop('gt_instances', None)\n            data_sample.pop('ignored_instances', None)\n            data_sample.pop('gt_panoptic_seg', None)\n\n            if 'pred_instances' in data_sample:\n                pred = data_sample['pred_instances']\n                # encode mask to RLE\n                if 'masks' in pred:\n                    pred['masks'] = encode_mask_results(pred['masks'].numpy())\n            if 'pred_panoptic_seg' in data_sample:\n                warnings.warn(\n                    'Panoptic segmentation map will not be compressed. '\n                    'The dumped file will be extremely large! '\n                    'Suggest using `CocoPanopticMetric` to save the coco '\n                    'format json and segmentation png files directly.')\n        self.results.extend(data_samples)\n"
  },
  {
    "path": "mmdet/evaluation/metrics/dump_odvg_results.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Any, Optional, Sequence\n\nfrom mmcv.ops import batched_nms\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.logging import print_log\n\nfrom mmdet.registry import METRICS\n\ntry:\n    import jsonlines\nexcept ImportError:\n    jsonlines = None\n\n\n@METRICS.register_module()\nclass DumpODVGResults(BaseMetric):\n    default_prefix: Optional[str] = 'pl_odvg'\n\n    def __init__(self,\n                 outfile_path,\n                 img_prefix: str,\n                 score_thr: float = 0.1,\n                 collect_device: str = 'cpu',\n                 nms_thr: float = 0.5,\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        self.outfile_path = outfile_path\n        self.score_thr = score_thr\n        self.img_prefix = img_prefix\n        self.nms_thr = nms_thr\n\n        if jsonlines is None:\n            raise ImportError('Please run \"pip install jsonlines\" to install '\n                              'this package.')\n\n    def process(self, data_batch: Any, data_samples: Sequence[dict]) -> None:\n        for data_sample in data_samples:\n            result = {}\n\n            filename = data_sample['img_path']\n            filename = filename.replace(self.img_prefix, '')\n            if filename.startswith('/'):\n                filename = filename[1:]\n            result['filename'] = filename\n\n            height = data_sample['ori_shape'][0]\n            width = data_sample['ori_shape'][1]\n            result['height'] = height\n            result['width'] = width\n\n            pred_instances = data_sample['pred_instances']\n\n            bboxes = pred_instances['bboxes'].cpu()\n            scores = pred_instances['scores'].cpu()\n            labels = pred_instances['labels'].cpu()\n\n            bboxes = bboxes[scores > self.score_thr]\n            labels = labels[scores > self.score_thr]\n            scores = scores[scores > self.score_thr]\n\n            if 'tokens_positive' in data_sample:\n                task = 'vg'\n            else:\n                task = 'od'\n\n            if task == 'od':\n                classes_name = data_sample['text']\n                result['detection'] = {}\n\n                if len(bboxes) > 0:\n                    det_bboxes, keep = batched_nms(\n                        bboxes, scores, labels,\n                        dict(type='nms', iou_threshold=self.nms_thr))\n                    _scores = det_bboxes[:, -1]\n                    _bboxes = det_bboxes[:, :-1]\n                    _labels = labels[keep]\n\n                    instances = []\n                    _bboxes = _bboxes.numpy().tolist()\n                    _scores = _scores.numpy().tolist()\n                    _labels = _labels.numpy().tolist()\n                    for bbox, score, label in zip(_bboxes, _scores, _labels):\n                        round_bbox = [round(b, 2) for b in bbox]\n                        round_score = round(score, 2)\n                        instances.append({\n                            'bbox': round_bbox,\n                            'score': round_score,\n                            'label': label,\n                            'category': classes_name[label]\n                        })\n                    result['detection']['instances'] = instances\n                else:\n                    result['detection']['instances'] = []\n                self.results.append(result)\n            else:\n                caption = data_sample['text']\n                result['grounding'] = {}\n                result['grounding']['caption'] = caption\n\n                tokens_positive = data_sample['tokens_positive']\n\n                region_list = []\n                for label, positive in enumerate(tokens_positive):\n                    phrase = [caption[pos[0]:pos[1]] for pos in positive]\n\n                    _bboxes = bboxes[labels == label]\n                    _scores = scores[labels == label]\n                    det_bboxes, _ = batched_nms(\n                        _bboxes,\n                        _scores,\n                        None,\n                        dict(type='nms', iou_threshold=self.nms_thr),\n                        class_agnostic=True)\n                    _scores = det_bboxes[:, -1].numpy().tolist()\n                    _bboxes = det_bboxes[:, :-1].numpy().tolist()\n\n                    round_bboxes = []\n                    for bbox in _bboxes:\n                        round_bboxes.append([round(b, 2) for b in bbox])\n                    _scores = [[round(s, 2) for s in _scores]]\n                    region = {\n                        'phrase': phrase,\n                        'bbox': round_bboxes,\n                        'score': _scores,\n                        'tokens_positive': positive\n                    }\n                    region_list.append(region)\n                result['grounding']['regions'] = region_list\n                self.results.append(result)\n\n    def compute_metrics(self, results: list) -> dict:\n        with jsonlines.open(self.outfile_path, mode='w') as writer:\n            writer.write_all(results)\n        print_log(\n            f'Results has been saved to {self.outfile_path}.',\n            logger='current')\n        return {}\n"
  },
  {
    "path": "mmdet/evaluation/metrics/dump_proposals_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport os.path as osp\nfrom typing import Optional, Sequence\n\nfrom mmengine.dist import is_main_process\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import dump\nfrom mmengine.logging import MMLogger\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import METRICS\n\n\n@METRICS.register_module()\nclass DumpProposals(BaseMetric):\n    \"\"\"Dump proposals pseudo metric.\n\n    Args:\n        output_dir (str): The root directory for ``proposals_file``.\n            Defaults to ''.\n        proposals_file (str): Proposals file path. Defaults to 'proposals.pkl'.\n        num_max_proposals (int, optional): Maximum number of proposals to dump.\n            If not specified, all proposals will be dumped.\n        file_client_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n    \"\"\"\n\n    default_prefix: Optional[str] = 'dump_proposals'\n\n    def __init__(self,\n                 output_dir: str = '',\n                 proposals_file: str = 'proposals.pkl',\n                 num_max_proposals: Optional[int] = None,\n                 file_client_args: dict = None,\n                 backend_args: dict = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        self.num_max_proposals = num_max_proposals\n        # TODO: update after mmengine finish refactor fileio.\n        self.backend_args = backend_args\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n        self.output_dir = output_dir\n        assert proposals_file.endswith(('.pkl', '.pickle')), \\\n            'The output file must be a pkl file.'\n\n        self.proposals_file = os.path.join(self.output_dir, proposals_file)\n        if is_main_process():\n            os.makedirs(self.output_dir, exist_ok=True)\n\n    def process(self, data_batch: Sequence[dict],\n                data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            pred = data_sample['pred_instances']\n            # `bboxes` is sorted by `scores`\n            ranked_scores, rank_inds = pred['scores'].sort(descending=True)\n            ranked_bboxes = pred['bboxes'][rank_inds, :]\n\n            ranked_bboxes = ranked_bboxes.cpu().numpy()\n            ranked_scores = ranked_scores.cpu().numpy()\n\n            pred_instance = InstanceData()\n            pred_instance.bboxes = ranked_bboxes\n            pred_instance.scores = ranked_scores\n            if self.num_max_proposals is not None:\n                pred_instance = pred_instance[:self.num_max_proposals]\n\n            img_path = data_sample['img_path']\n            # `file_name` is the key to obtain the proposals from the\n            # `proposals_list`.\n            file_name = osp.join(\n                osp.split(osp.split(img_path)[0])[-1],\n                osp.split(img_path)[-1])\n            result = {file_name: pred_instance}\n            self.results.append(result)\n\n    def compute_metrics(self, results: list) -> dict:\n        \"\"\"Dump the processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            dict: An empty dict.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n        dump_results = {}\n        for result in results:\n            dump_results.update(result)\n        dump(\n            dump_results,\n            file=self.proposals_file,\n            backend_args=self.backend_args)\n        logger.info(f'Results are saved at {self.proposals_file}')\n        return {}\n"
  },
  {
    "path": "mmdet/evaluation/metrics/flickr30k_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved\nfrom collections import defaultdict\nfrom typing import Dict, List, Optional, Sequence\n\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.registry import METRICS\nfrom ..functional import bbox_overlaps\n\n\nclass RecallTracker:\n    \"\"\"Utility class to track recall@k for various k, split by categories.\"\"\"\n\n    def __init__(self, topk: Sequence[int]):\n        \"\"\"\n        Parameters:\n           - topk : tuple of ints corresponding to the recalls being\n           tracked (eg, recall@1, recall@10, ...)\n        \"\"\"\n\n        self.total_byk_bycat: Dict[int, Dict[str, int]] = {\n            k: defaultdict(int)\n            for k in topk\n        }\n        self.positives_byk_bycat: Dict[int, Dict[str, int]] = {\n            k: defaultdict(int)\n            for k in topk\n        }\n\n    def add_positive(self, k: int, category: str):\n        \"\"\"Log a positive hit @k for given category.\"\"\"\n        if k not in self.total_byk_bycat:\n            raise RuntimeError(f'{k} is not a valid recall threshold')\n        self.total_byk_bycat[k][category] += 1\n        self.positives_byk_bycat[k][category] += 1\n\n    def add_negative(self, k: int, category: str):\n        \"\"\"Log a negative hit @k for given category.\"\"\"\n        if k not in self.total_byk_bycat:\n            raise RuntimeError(f'{k} is not a valid recall threshold')\n        self.total_byk_bycat[k][category] += 1\n\n    def report(self) -> Dict[str, Dict[str, float]]:\n        \"\"\"Return a condensed report of the results as a dict of dict.\n\n        report[k][cat] is the recall@k for the given category\n        \"\"\"\n        report: Dict[str, Dict[str, float]] = {}\n        for k in self.total_byk_bycat:\n            assert k in self.positives_byk_bycat\n            report[str(k)] = {\n                cat:\n                self.positives_byk_bycat[k][cat] / self.total_byk_bycat[k][cat]\n                for cat in self.total_byk_bycat[k]\n            }\n        return report\n\n\n@METRICS.register_module()\nclass Flickr30kMetric(BaseMetric):\n    \"\"\"Phrase Grounding Metric.\"\"\"\n\n    def __init__(\n        self,\n        topk: Sequence[int] = (1, 5, 10, -1),\n        iou_thrs: float = 0.5,\n        merge_boxes: bool = False,\n        collect_device: str = 'cpu',\n        prefix: Optional[str] = None,\n    ) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n\n        self.iou_thrs = iou_thrs\n        self.topk = topk\n        self.merge = merge_boxes\n\n    def merge_boxes(self, boxes: List[List[int]]) -> List[List[int]]:\n        \"\"\"Return the boxes corresponding to the smallest enclosing box\n        containing all the provided boxes The boxes are expected in [x1, y1,\n        x2, y2] format.\"\"\"\n        if len(boxes) == 1:\n            return boxes\n\n        np_boxes = np.asarray(boxes)\n\n        return [[\n            np.boxes[:, 0].min(), np_boxes[:, 1].min(), np_boxes[:, 2].max(),\n            np_boxes[:, 3].max()\n        ]]\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions.\n\n        The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            pred = data_sample['pred_instances']\n            gt = data_sample['gt_instances']['bboxes']\n            gt_label = data_sample['phrase_ids']\n            phrases = data_sample['phrases']\n            assert len(gt) == len(gt_label)\n\n            self.results.append((pred, gt, gt_label, phrases))\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        pred_list, gt_list, gt_label_list, phrase_list = zip(*results)\n\n        recall_tracker = RecallTracker(self.topk)\n\n        for pred, gt_boxes, gt_labels, phrases in zip(pred_list, gt_list,\n                                                      gt_label_list,\n                                                      phrase_list):\n            pred_boxes = pred['bboxes'].cpu().numpy()\n            pred_labels = pred['labels'].cpu().numpy()\n            for i, phrase in enumerate(phrases):\n                cur_index = pred_labels == i\n                cur_boxes = pred_boxes[cur_index]\n                tar_index = [\n                    index for index, value in enumerate(gt_labels)\n                    if value == i\n                ]\n                tar_boxes = gt_boxes[tar_index]\n                if self.merge:\n                    tar_boxes = self.merge_boxes(tar_boxes)\n                if len(cur_boxes) == 0:\n                    cur_boxes = [[0., 0., 0., 0.]]\n                ious = bbox_overlaps(\n                    np.asarray(cur_boxes), np.asarray(tar_boxes))\n                for k in self.topk:\n                    if k == -1:\n                        maxi = ious.max()\n                    else:\n                        assert k > 0\n                        maxi = ious[:k].max()\n                    if maxi >= self.iou_thrs:\n                        recall_tracker.add_positive(k, 'all')\n                        # TODO: do not support class-wise evaluation yet\n                        # for phrase_type in phrase['phrase_type']:\n                        #     recall_tracker.add_positive(k, phrase_type)\n                    else:\n                        recall_tracker.add_negative(k, 'all')\n                        # for phrase_type in phrase['phrase_type']:\n                        #     recall_tracker.add_negative(k, phrase_type)\n\n        results = recall_tracker.report()\n        logger.info(results)\n        return results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/grefcoco_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Optional, Sequence\n\nimport numpy as np\nimport torch\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import get_local_path\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.datasets.api_wrappers import COCO\nfrom mmdet.registry import METRICS\nfrom ..functional import bbox_overlaps\n\n\n# refer from https://github.com/henghuiding/gRefCOCO/blob/main/mdetr/datasets/refexp.py # noqa\n@METRICS.register_module()\nclass gRefCOCOMetric(BaseMetric):\n    default_prefix: Optional[str] = 'grefcoco'\n\n    def __init__(self,\n                 ann_file: Optional[str] = None,\n                 metric: str = 'bbox',\n                 iou_thrs: float = 0.5,\n                 thresh_score: float = 0.7,\n                 thresh_f1: float = 1.0,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.metric = metric\n        self.iou_thrs = iou_thrs\n        self.thresh_score = thresh_score\n        self.thresh_f1 = thresh_f1\n\n        with get_local_path(ann_file) as local_path:\n            self.coco = COCO(local_path)\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        for data_sample in data_samples:\n            result = dict()\n            pred = data_sample['pred_instances']\n            result['img_id'] = data_sample['img_id']\n            result['bboxes'] = pred['bboxes'].cpu()\n            result['scores'] = pred['scores'].cpu()\n            self.results.append(result)\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        correct_image = 0\n        num_image = 0\n        nt = {'TP': 0, 'TN': 0, 'FP': 0, 'FN': 0}\n\n        for result in results:\n            img_id = result['img_id']\n            TP = 0\n\n            ann_ids = self.coco.getAnnIds(imgIds=img_id)\n            target = self.coco.loadAnns(ann_ids[0])\n\n            converted_bbox_all = []\n            no_target_flag = False\n            for one_target in target:\n                if one_target['category_id'] == -1:\n                    no_target_flag = True\n                target_bbox = one_target['bbox']\n                converted_bbox = [\n                    target_bbox[0],\n                    target_bbox[1],\n                    target_bbox[2] + target_bbox[0],\n                    target_bbox[3] + target_bbox[1],\n                ]\n                converted_bbox_all.append(\n                    np.array(converted_bbox).reshape(-1, 4))\n            gt_bbox_all = np.concatenate(converted_bbox_all, axis=0)\n\n            idx = result['scores'] >= self.thresh_score\n            filtered_boxes = result['bboxes'][idx]\n\n            iou = bbox_overlaps(filtered_boxes.numpy(), gt_bbox_all)\n            iou = torch.from_numpy(iou)\n\n            num_prediction = filtered_boxes.shape[0]\n            num_gt = gt_bbox_all.shape[0]\n            if no_target_flag:\n                if num_prediction >= 1:\n                    nt['FN'] += 1\n                else:\n                    nt['TP'] += 1\n                if num_prediction >= 1:\n                    f_1 = 0.\n                else:\n                    f_1 = 1.0\n            else:\n                if num_prediction >= 1:\n                    nt['TN'] += 1\n                else:\n                    nt['FP'] += 1\n                for i in range(min(num_prediction, num_gt)):\n                    top_value, top_index = torch.topk(iou.flatten(0, 1), 1)\n                    if top_value < self.iou_thrs:\n                        break\n                    else:\n                        top_index_x = top_index // num_gt\n                        top_index_y = top_index % num_gt\n                        TP += 1\n                        iou[top_index_x[0], :] = 0.0\n                        iou[:, top_index_y[0]] = 0.0\n                FP = num_prediction - TP\n                FN = num_gt - TP\n                f_1 = 2 * TP / (2 * TP + FP + FN)\n\n            if f_1 >= self.thresh_f1:\n                correct_image += 1\n            num_image += 1\n\n        score = correct_image / max(num_image, 1)\n        results = {\n            'F1_score': score,\n            'T_acc': nt['TN'] / (nt['TN'] + nt['FP']),\n            'N_acc': nt['TP'] / (nt['TP'] + nt['FN'])\n        }\n        logger.info(results)\n        return results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/lvis_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport itertools\nimport logging\nimport os.path as osp\nimport tempfile\nimport warnings\nfrom collections import OrderedDict, defaultdict\nfrom typing import Dict, List, Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.dist import (all_gather_object, broadcast_object_list,\n                           is_main_process)\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.evaluator.metric import _to_cpu\nfrom mmengine.fileio import get_local_path\nfrom mmengine.logging import MMLogger, print_log\nfrom terminaltables import AsciiTable\n\nfrom mmdet.registry import METRICS\nfrom mmdet.structures.mask import encode_mask_results\nfrom ..functional import eval_recalls\nfrom .coco_metric import CocoMetric\n\ntry:\n    import lvis\n\n    if getattr(lvis, '__version__', '0') >= '10.5.3':\n        warnings.warn(\n            'mmlvis is deprecated, please install official lvis-api by \"pip install git+https://github.com/lvis-dataset/lvis-api.git\"',  # noqa: E501\n            UserWarning)\n    from lvis import LVIS, LVISEval, LVISResults\nexcept ImportError:\n    lvis = None\n    LVISEval = None\n    LVISResults = None\n\n\n@METRICS.register_module()\nclass LVISMetric(CocoMetric):\n    \"\"\"LVIS evaluation metric.\n\n    Args:\n        ann_file (str, optional): Path to the coco format annotation file.\n            If not specified, ground truth annotations from the dataset will\n            be converted to coco format. Defaults to None.\n        metric (str | List[str]): Metrics to be evaluated. Valid metrics\n            include 'bbox', 'segm', 'proposal', and 'proposal_fast'.\n            Defaults to 'bbox'.\n        classwise (bool): Whether to evaluate the metric class-wise.\n            Defaults to False.\n        proposal_nums (Sequence[int]): Numbers of proposals to be evaluated.\n            Defaults to (100, 300, 1000).\n        iou_thrs (float | List[float], optional): IoU threshold to compute AP\n            and AR. If not specified, IoUs from 0.5 to 0.95 will be used.\n            Defaults to None.\n        metric_items (List[str], optional): Metric result names to be\n            recorded in the evaluation result. Defaults to None.\n        format_only (bool): Format the output results without perform\n            evaluation. It is useful when you want to format the result\n            to a specific format and submit it to the test server.\n            Defaults to False.\n        outfile_prefix (str, optional): The prefix of json files. It includes\n            the file path and the prefix of filename, e.g., \"a/b/prefix\".\n            If not specified, a temp file will be created. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n        file_client_args (dict, optional): Arguments to instantiate the\n            corresponding backend in mmdet <= 3.0.0rc6. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n    \"\"\"\n\n    default_prefix: Optional[str] = 'lvis'\n\n    def __init__(self,\n                 ann_file: Optional[str] = None,\n                 metric: Union[str, List[str]] = 'bbox',\n                 classwise: bool = False,\n                 proposal_nums: Sequence[int] = (100, 300, 1000),\n                 iou_thrs: Optional[Union[float, Sequence[float]]] = None,\n                 metric_items: Optional[Sequence[str]] = None,\n                 format_only: bool = False,\n                 outfile_prefix: Optional[str] = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None,\n                 file_client_args: dict = None,\n                 backend_args: dict = None) -> None:\n        if lvis is None:\n            raise RuntimeError(\n                'Package lvis is not installed. Please run \"pip install '\n                'git+https://github.com/lvis-dataset/lvis-api.git\".')\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        # coco evaluation metrics\n        self.metrics = metric if isinstance(metric, list) else [metric]\n        allowed_metrics = ['bbox', 'segm', 'proposal', 'proposal_fast']\n        for metric in self.metrics:\n            if metric not in allowed_metrics:\n                raise KeyError(\n                    \"metric should be one of 'bbox', 'segm', 'proposal', \"\n                    f\"'proposal_fast', but got {metric}.\")\n\n        # do class wise evaluation, default False\n        self.classwise = classwise\n\n        # proposal_nums used to compute recall or precision.\n        self.proposal_nums = list(proposal_nums)\n\n        # iou_thrs used to compute recall or precision.\n        if iou_thrs is None:\n            iou_thrs = np.linspace(\n                .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)\n        self.iou_thrs = iou_thrs\n        self.metric_items = metric_items\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n\n        self.outfile_prefix = outfile_prefix\n        self.backend_args = backend_args\n        if file_client_args is not None:\n            raise RuntimeError(\n                'The `file_client_args` is deprecated, '\n                'please use `backend_args` instead, please refer to'\n                'https://github.com/open-mmlab/mmdetection/blob/main/configs/_base_/datasets/coco_detection.py'  # noqa: E501\n            )\n\n        # if ann_file is not specified,\n        # initialize lvis api with the converted dataset\n        if ann_file is not None:\n            with get_local_path(\n                    ann_file, backend_args=self.backend_args) as local_path:\n                self._lvis_api = LVIS(local_path)\n        else:\n            self._lvis_api = None\n\n        # handle dataset lazy init\n        self.cat_ids = None\n        self.img_ids = None\n\n    def fast_eval_recall(self,\n                         results: List[dict],\n                         proposal_nums: Sequence[int],\n                         iou_thrs: Sequence[float],\n                         logger: Optional[MMLogger] = None) -> np.ndarray:\n        \"\"\"Evaluate proposal recall with LVIS's fast_eval_recall.\n\n        Args:\n            results (List[dict]): Results of the dataset.\n            proposal_nums (Sequence[int]): Proposal numbers used for\n                evaluation.\n            iou_thrs (Sequence[float]): IoU thresholds used for evaluation.\n            logger (MMLogger, optional): Logger used for logging the recall\n                summary.\n        Returns:\n            np.ndarray: Averaged recall results.\n        \"\"\"\n        gt_bboxes = []\n        pred_bboxes = [result['bboxes'] for result in results]\n        for i in range(len(self.img_ids)):\n            ann_ids = self._lvis_api.get_ann_ids(img_ids=[self.img_ids[i]])\n            ann_info = self._lvis_api.load_anns(ann_ids)\n            if len(ann_info) == 0:\n                gt_bboxes.append(np.zeros((0, 4)))\n                continue\n            bboxes = []\n            for ann in ann_info:\n                x1, y1, w, h = ann['bbox']\n                bboxes.append([x1, y1, x1 + w, y1 + h])\n            bboxes = np.array(bboxes, dtype=np.float32)\n            if bboxes.shape[0] == 0:\n                bboxes = np.zeros((0, 4))\n            gt_bboxes.append(bboxes)\n\n        recalls = eval_recalls(\n            gt_bboxes, pred_bboxes, proposal_nums, iou_thrs, logger=logger)\n        ar = recalls.mean(axis=1)\n        return ar\n\n    # TODO: data_batch is no longer needed, consider adjusting the\n    #  parameter position\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            result = dict()\n            pred = data_sample['pred_instances']\n            result['img_id'] = data_sample['img_id']\n            result['bboxes'] = pred['bboxes'].cpu().numpy()\n            result['scores'] = pred['scores'].cpu().numpy()\n            result['labels'] = pred['labels'].cpu().numpy()\n            # encode mask to RLE\n            if 'masks' in pred:\n                result['masks'] = encode_mask_results(\n                    pred['masks'].detach().cpu().numpy())\n            # some detectors use different scores for bbox and mask\n            if 'mask_scores' in pred:\n                result['mask_scores'] = pred['mask_scores'].cpu().numpy()\n\n            # parse gt\n            gt = dict()\n            gt['width'] = data_sample['ori_shape'][1]\n            gt['height'] = data_sample['ori_shape'][0]\n            gt['img_id'] = data_sample['img_id']\n            if self._lvis_api is None:\n                # TODO: Need to refactor to support LoadAnnotations\n                assert 'instances' in data_sample, \\\n                    'ground truth is required for evaluation when ' \\\n                    '`ann_file` is not provided'\n                gt['anns'] = data_sample['instances']\n            # add converted result to the results list\n            self.results.append((gt, result))\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        # split gt and prediction list\n        gts, preds = zip(*results)\n\n        tmp_dir = None\n        if self.outfile_prefix is None:\n            tmp_dir = tempfile.TemporaryDirectory()\n            outfile_prefix = osp.join(tmp_dir.name, 'results')\n        else:\n            outfile_prefix = self.outfile_prefix\n\n        if self._lvis_api is None:\n            # use converted gt json file to initialize coco api\n            logger.info('Converting ground truth to coco format...')\n            coco_json_path = self.gt_to_coco_json(\n                gt_dicts=gts, outfile_prefix=outfile_prefix)\n            self._lvis_api = LVIS(coco_json_path)\n\n        # handle lazy init\n        if self.cat_ids is None:\n            self.cat_ids = self._lvis_api.get_cat_ids()\n        if self.img_ids is None:\n            self.img_ids = self._lvis_api.get_img_ids()\n\n        # convert predictions to coco format and dump to json file\n        result_files = self.results2json(preds, outfile_prefix)\n\n        eval_results = OrderedDict()\n        if self.format_only:\n            logger.info('results are saved in '\n                        f'{osp.dirname(outfile_prefix)}')\n            return eval_results\n\n        lvis_gt = self._lvis_api\n\n        for metric in self.metrics:\n            logger.info(f'Evaluating {metric}...')\n\n            # TODO: May refactor fast_eval_recall to an independent metric?\n            # fast eval recall\n            if metric == 'proposal_fast':\n                ar = self.fast_eval_recall(\n                    preds, self.proposal_nums, self.iou_thrs, logger=logger)\n                log_msg = []\n                for i, num in enumerate(self.proposal_nums):\n                    eval_results[f'AR@{num}'] = ar[i]\n                    log_msg.append(f'\\nAR@{num}\\t{ar[i]:.4f}')\n                log_msg = ''.join(log_msg)\n                logger.info(log_msg)\n                continue\n\n            try:\n                lvis_dt = LVISResults(lvis_gt, result_files[metric])\n            except IndexError:\n                logger.info(\n                    'The testing results of the whole dataset is empty.')\n                break\n\n            iou_type = 'bbox' if metric == 'proposal' else metric\n            lvis_eval = LVISEval(lvis_gt, lvis_dt, iou_type)\n            lvis_eval.params.imgIds = self.img_ids\n            metric_items = self.metric_items\n            if metric == 'proposal':\n                lvis_eval.params.useCats = 0\n                lvis_eval.params.maxDets = list(self.proposal_nums)\n                lvis_eval.evaluate()\n                lvis_eval.accumulate()\n                lvis_eval.summarize()\n                if metric_items is None:\n                    metric_items = ['AR@300', 'ARs@300', 'ARm@300', 'ARl@300']\n                for k, v in lvis_eval.get_results().items():\n                    if k in metric_items:\n                        val = float('{:.3f}'.format(float(v)))\n                        eval_results[k] = val\n\n            else:\n                lvis_eval.evaluate()\n                lvis_eval.accumulate()\n                lvis_eval.summarize()\n                lvis_results = lvis_eval.get_results()\n                if self.classwise:  # Compute per-category AP\n                    # Compute per-category AP\n                    # from https://github.com/facebookresearch/detectron2/\n                    precisions = lvis_eval.eval['precision']\n                    # precision: (iou, recall, cls, area range, max dets)\n                    assert len(self.cat_ids) == precisions.shape[2]\n\n                    results_per_category = []\n                    for idx, catId in enumerate(self.cat_ids):\n                        # area range index 0: all area ranges\n                        # max dets index -1: typically 100 per image\n                        # the dimensions of precisions are\n                        # [num_thrs, num_recalls, num_cats, num_area_rngs]\n                        nm = self._lvis_api.load_cats([catId])[0]\n                        precision = precisions[:, :, idx, 0]\n                        precision = precision[precision > -1]\n                        if precision.size:\n                            ap = np.mean(precision)\n                        else:\n                            ap = float('nan')\n                        results_per_category.append(\n                            (f'{nm[\"name\"]}', f'{float(ap):0.3f}'))\n                        eval_results[f'{nm[\"name\"]}_precision'] = round(ap, 3)\n\n                    num_columns = min(6, len(results_per_category) * 2)\n                    results_flatten = list(\n                        itertools.chain(*results_per_category))\n                    headers = ['category', 'AP'] * (num_columns // 2)\n                    results_2d = itertools.zip_longest(*[\n                        results_flatten[i::num_columns]\n                        for i in range(num_columns)\n                    ])\n                    table_data = [headers]\n                    table_data += [result for result in results_2d]\n                    table = AsciiTable(table_data)\n                    logger.info('\\n' + table.table)\n\n                if metric_items is None:\n                    metric_items = [\n                        'AP', 'AP50', 'AP75', 'APs', 'APm', 'APl', 'APr',\n                        'APc', 'APf'\n                    ]\n\n                for k, v in lvis_results.items():\n                    if k in metric_items:\n                        key = '{}_{}'.format(metric, k)\n                        val = float('{:.3f}'.format(float(v)))\n                        eval_results[key] = val\n\n            lvis_eval.print_results()\n        if tmp_dir is not None:\n            tmp_dir.cleanup()\n        return eval_results\n\n\ndef _merge_lists(listA, listB, maxN, key):\n    result = []\n    indA, indB = 0, 0\n    while (indA < len(listA) or indB < len(listB)) and len(result) < maxN:\n        if (indB < len(listB)) and (indA >= len(listA)\n                                    or key(listA[indA]) < key(listB[indB])):\n            result.append(listB[indB])\n            indB += 1\n        else:\n            result.append(listA[indA])\n            indA += 1\n    return result\n\n\n@METRICS.register_module()\nclass LVISFixedAPMetric(BaseMetric):\n    default_prefix: Optional[str] = 'lvis_fixed_ap'\n\n    def __init__(self,\n                 ann_file: str,\n                 topk: int = 10000,\n                 format_only: bool = False,\n                 outfile_prefix: Optional[str] = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None,\n                 backend_args: dict = None) -> None:\n\n        if lvis is None:\n            raise RuntimeError(\n                'Package lvis is not installed. Please run \"pip install '\n                'git+https://github.com/lvis-dataset/lvis-api.git\".')\n        super().__init__(collect_device=collect_device, prefix=prefix)\n\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n\n        self.outfile_prefix = outfile_prefix\n        self.backend_args = backend_args\n\n        with get_local_path(\n                ann_file, backend_args=self.backend_args) as local_path:\n            self._lvis_api = LVIS(local_path)\n\n        self.cat_ids = self._lvis_api.get_cat_ids()\n\n        self.results = {}\n        self.topk = topk\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        cur_results = []\n        for data_sample in data_samples:\n            pred = data_sample['pred_instances']\n            xmin, ymin, xmax, ymax = pred['bboxes'].cpu().unbind(1)\n            boxes = torch.stack((xmin, ymin, xmax - xmin, ymax - ymin),\n                                dim=1).tolist()\n\n            scores = pred['scores'].cpu().numpy()\n            labels = pred['labels'].cpu().numpy()\n\n            if len(boxes) == 0:\n                continue\n\n            cur_results.extend([{\n                'image_id': data_sample['img_id'],\n                'category_id': self.cat_ids[labels[k]],\n                'bbox': box,\n                'score': scores[k],\n            } for k, box in enumerate(boxes)])\n\n        by_cat = defaultdict(list)\n        for ann in cur_results:\n            by_cat[ann['category_id']].append(ann)\n\n        for cat, cat_anns in by_cat.items():\n            if cat not in self.results:\n                self.results[cat] = []\n\n            cur = sorted(\n                cat_anns, key=lambda x: x['score'], reverse=True)[:self.topk]\n            self.results[cat] = _merge_lists(\n                self.results[cat], cur, self.topk, key=lambda x: x['score'])\n\n    def compute_metrics(self, results: dict) -> dict:\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        new_results = []\n\n        missing_dets_cats = set()\n        for cat, cat_anns in results.items():\n            if len(cat_anns) < self.topk:\n                missing_dets_cats.add(cat)\n            new_results.extend(\n                sorted(cat_anns, key=lambda x: x['score'],\n                       reverse=True)[:self.topk])\n\n        if missing_dets_cats:\n            logger.info(\n                f'\\n===\\n'\n                f'{len(missing_dets_cats)} classes had less than {self.topk} '\n                f'detections!\\n Outputting {self.topk} detections for each '\n                f'class will improve AP further.\\n ===')\n\n        new_results = LVISResults(self._lvis_api, new_results, max_dets=-1)\n        lvis_eval = LVISEval(self._lvis_api, new_results, iou_type='bbox')\n        params = lvis_eval.params\n        params.max_dets = -1  # No limit on detections per image.\n        lvis_eval.run()\n        lvis_eval.print_results()\n        metrics = {\n            k: v\n            for k, v in lvis_eval.results.items() if k.startswith('AP')\n        }\n        logger.info(f'mAP_copypaste: {metrics}')\n        return metrics\n\n    def evaluate(self, size: int) -> dict:\n        if len(self.results) == 0:\n            print_log(\n                f'{self.__class__.__name__} got empty `self.results`. Please '\n                'ensure that the processed results are properly added into '\n                '`self.results` in `process` method.',\n                logger='current',\n                level=logging.WARNING)\n\n        all_cats = all_gather_object(self.results)\n        results = defaultdict(list)\n        for cats in all_cats:\n            for cat, cat_anns in cats.items():\n                results[cat].extend(cat_anns)\n\n        if is_main_process():\n            # cast all tensors in results list to cpu\n            results = _to_cpu(results)\n            _metrics = self.compute_metrics(results)  # type: ignore\n            # Add prefix to metric names\n            if self.prefix:\n                _metrics = {\n                    '/'.join((self.prefix, k)): v\n                    for k, v in _metrics.items()\n                }\n            metrics = [_metrics]\n        else:\n            metrics = [None]  # type: ignore\n\n        broadcast_object_list(metrics)\n\n        # reset the results\n        self.results = {}\n        return metrics[0]\n"
  },
  {
    "path": "mmdet/evaluation/metrics/mot_challenge_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport os.path as osp\nimport shutil\nimport tempfile\nfrom collections import defaultdict\nfrom typing import List, Optional, Union\n\nimport numpy as np\nimport torch\n\ntry:\n    import trackeval\nexcept ImportError:\n    trackeval = None\nfrom mmengine.dist import (all_gather_object, barrier, broadcast,\n                           broadcast_object_list, get_dist_info,\n                           is_main_process)\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.registry import METRICS, TASK_UTILS\nfrom .base_video_metric import BaseVideoMetric\n\n\ndef get_tmpdir() -> str:\n    \"\"\"return the same tmpdir for all processes.\"\"\"\n    rank, world_size = get_dist_info()\n    MAX_LEN = 512\n    # 32 is whitespace\n    dir_tensor = torch.full((MAX_LEN, ), 32, dtype=torch.uint8)\n    if rank == 0:\n        tmpdir = tempfile.mkdtemp()\n        tmpdir = torch.tensor(bytearray(tmpdir.encode()), dtype=torch.uint8)\n        dir_tensor[:len(tmpdir)] = tmpdir\n    broadcast(dir_tensor, 0)\n    tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip()\n    return tmpdir\n\n\n@METRICS.register_module()\nclass MOTChallengeMetric(BaseVideoMetric):\n    \"\"\"Evaluation metrics for MOT Challenge.\n\n    Args:\n        metric (str | list[str]): Metrics to be evaluated. Options are\n            'HOTA', 'CLEAR', 'Identity'.\n            Defaults to ['HOTA', 'CLEAR', 'Identity'].\n        outfile_prefix (str, optional): Path to save the formatted results.\n            Defaults to None.\n        track_iou_thr (float): IoU threshold for tracking evaluation.\n            Defaults to 0.5.\n        benchmark (str): Benchmark to be evaluated. Defaults to 'MOT17'.\n        format_only (bool): If True, only formatting the results to the\n            official format and not performing evaluation. Defaults to False.\n        postprocess_tracklet_cfg (List[dict], optional): configs for tracklets\n            postprocessing methods. `InterpolateTracklets` is supported.\n            Defaults to []\n            - InterpolateTracklets:\n                - min_num_frames (int, optional): The minimum length of a\n                    track that will be interpolated. Defaults to 5.\n                - max_num_frames (int, optional): The maximum disconnected\n                    length in a track. Defaults to 20.\n                - use_gsi (bool, optional): Whether to use the GSI (Gaussian-\n                    smoothed interpolation) method. Defaults to False.\n                - smooth_tau (int, optional): smoothing parameter in GSI.\n                    Defaults to 10.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Default: None\n    Returns:\n    \"\"\"\n    TRACKER = 'default-tracker'\n    allowed_metrics = ['HOTA', 'CLEAR', 'Identity']\n    allowed_benchmarks = ['MOT15', 'MOT16', 'MOT17', 'MOT20', 'DanceTrack']\n    default_prefix: Optional[str] = 'motchallenge-metric'\n\n    def __init__(self,\n                 metric: Union[str, List[str]] = ['HOTA', 'CLEAR', 'Identity'],\n                 outfile_prefix: Optional[str] = None,\n                 track_iou_thr: float = 0.5,\n                 benchmark: str = 'MOT17',\n                 format_only: bool = False,\n                 use_postprocess: bool = False,\n                 postprocess_tracklet_cfg: Optional[List[dict]] = [],\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        if trackeval is None:\n            raise RuntimeError(\n                'trackeval is not installed,'\n                'please install it by: pip install'\n                'git+https://github.com/JonathonLuiten/TrackEval.git'\n                'trackeval need low version numpy, please install it'\n                'by: pip install -U numpy==1.23.5')\n        if isinstance(metric, list):\n            metrics = metric\n        elif isinstance(metric, str):\n            metrics = [metric]\n        else:\n            raise TypeError('metric must be a list or a str.')\n        for metric in metrics:\n            if metric not in self.allowed_metrics:\n                raise KeyError(f'metric {metric} is not supported.')\n        self.metrics = metrics\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n        self.use_postprocess = use_postprocess\n        self.postprocess_tracklet_cfg = postprocess_tracklet_cfg.copy()\n        self.postprocess_tracklet_methods = [\n            TASK_UTILS.build(cfg) for cfg in self.postprocess_tracklet_cfg\n        ]\n        assert benchmark in self.allowed_benchmarks\n        self.benchmark = benchmark\n        self.track_iou_thr = track_iou_thr\n        self.tmp_dir = tempfile.TemporaryDirectory()\n        self.tmp_dir.name = get_tmpdir()\n        self.seq_info = defaultdict(\n            lambda: dict(seq_length=-1, gt_tracks=[], pred_tracks=[]))\n        self.gt_dir = self._get_gt_dir()\n        self.pred_dir = self._get_pred_dir(outfile_prefix)\n        self.seqmap = osp.join(self.pred_dir, 'videoseq.txt')\n        with open(self.seqmap, 'w') as f:\n            f.write('name\\n')\n\n    def __del__(self):\n        # To avoid tmpdir being cleaned up too early, because in multiple\n        # consecutive ValLoops, the value of `self.tmp_dir.name` is unchanged,\n        # and calling `tmp_dir.cleanup()` in compute_metrics will cause errors.\n        self.tmp_dir.cleanup()\n\n    def _get_pred_dir(self, outfile_prefix):\n        \"\"\"Get directory to save the prediction results.\"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        if outfile_prefix is None:\n            outfile_prefix = self.tmp_dir.name\n        else:\n            if osp.exists(outfile_prefix) and is_main_process():\n                logger.info('remove previous results.')\n                shutil.rmtree(outfile_prefix)\n        pred_dir = osp.join(outfile_prefix, self.TRACKER)\n        os.makedirs(pred_dir, exist_ok=True)\n        return pred_dir\n\n    def _get_gt_dir(self):\n        \"\"\"Get directory to save the gt files.\"\"\"\n        output_dir = osp.join(self.tmp_dir.name, 'gt')\n        os.makedirs(output_dir, exist_ok=True)\n        return output_dir\n\n    def transform_gt_and_pred(self, img_data_sample, video, frame_id):\n\n        video = img_data_sample['img_path'].split(os.sep)[-3]\n        # load gts\n        if 'instances' in img_data_sample:\n            gt_instances = img_data_sample['instances']\n            gt_tracks = [\n                np.array([\n                    frame_id + 1, gt_instances[i]['instance_id'],\n                    gt_instances[i]['bbox'][0], gt_instances[i]['bbox'][1],\n                    gt_instances[i]['bbox'][2] - gt_instances[i]['bbox'][0],\n                    gt_instances[i]['bbox'][3] - gt_instances[i]['bbox'][1],\n                    gt_instances[i]['mot_conf'],\n                    gt_instances[i]['category_id'],\n                    gt_instances[i]['visibility']\n                ]) for i in range(len(gt_instances))\n            ]\n            self.seq_info[video]['gt_tracks'].extend(gt_tracks)\n\n        # load predictions\n        assert 'pred_track_instances' in img_data_sample\n        if self.use_postprocess:\n            pred_instances = img_data_sample['pred_track_instances']\n            pred_tracks = [\n                pred_instances['bboxes'][i]\n                for i in range(len(pred_instances['bboxes']))\n            ]\n        else:\n            pred_instances = img_data_sample['pred_track_instances']\n            pred_tracks = [\n                np.array([\n                    frame_id + 1, pred_instances['instances_id'][i].cpu(),\n                    pred_instances['bboxes'][i][0].cpu(),\n                    pred_instances['bboxes'][i][1].cpu(),\n                    (pred_instances['bboxes'][i][2] -\n                     pred_instances['bboxes'][i][0]).cpu(),\n                    (pred_instances['bboxes'][i][3] -\n                     pred_instances['bboxes'][i][1]).cpu(),\n                    pred_instances['scores'][i].cpu()\n                ]) for i in range(len(pred_instances['instances_id']))\n            ]\n        self.seq_info[video]['pred_tracks'].extend(pred_tracks)\n\n    def process_image(self, data_samples, video_len):\n\n        img_data_sample = data_samples[0].to_dict()\n        video = img_data_sample['img_path'].split(os.sep)[-3]\n        frame_id = img_data_sample['frame_id']\n        if self.seq_info[video]['seq_length'] == -1:\n            self.seq_info[video]['seq_length'] = video_len\n        self.transform_gt_and_pred(img_data_sample, video, frame_id)\n\n        if frame_id == video_len - 1:\n            # postprocessing\n            if self.postprocess_tracklet_cfg:\n                info = self.seq_info[video]\n                pred_tracks = np.array(info['pred_tracks'])\n                for postprocess_tracklet_methods in \\\n                        self.postprocess_tracklet_methods:\n                    pred_tracks = postprocess_tracklet_methods\\\n                        .forward(pred_tracks)\n                info['pred_tracks'] = pred_tracks\n            self._save_one_video_gts_preds(video)\n\n    def process_video(self, data_samples):\n\n        video_len = len(data_samples)\n        for frame_id in range(video_len):\n            img_data_sample = data_samples[frame_id].to_dict()\n            # load basic info\n            video = img_data_sample['img_path'].split(os.sep)[-3]\n            if self.seq_info[video]['seq_length'] == -1:\n                self.seq_info[video]['seq_length'] = video_len\n            self.transform_gt_and_pred(img_data_sample, video, frame_id)\n\n        if self.postprocess_tracklet_cfg:\n            info = self.seq_info[video]\n            pred_tracks = np.array(info['pred_tracks'])\n            for postprocess_tracklet_methods in \\\n                    self.postprocess_tracklet_methods:\n                pred_tracks = postprocess_tracklet_methods \\\n                    .forward(pred_tracks)\n            info['pred_tracks'] = pred_tracks\n        self._save_one_video_gts_preds(video)\n\n    def _save_one_video_gts_preds(self, seq: str) -> None:\n        \"\"\"Save the gt and prediction results.\"\"\"\n        info = self.seq_info[seq]\n        # save predictions\n        pred_file = osp.join(self.pred_dir, seq + '.txt')\n\n        pred_tracks = np.array(info['pred_tracks'])\n\n        with open(pred_file, 'wt') as f:\n            for tracks in pred_tracks:\n                line = '%d,%d,%.3f,%.3f,%.3f,%.3f,%.3f,-1,-1,-1\\n' % (\n                    tracks[0], tracks[1], tracks[2], tracks[3], tracks[4],\n                    tracks[5], tracks[6])\n                f.writelines(line)\n\n        info['pred_tracks'] = []\n        # save gts\n        if info['gt_tracks']:\n            gt_file = osp.join(self.gt_dir, seq + '.txt')\n            with open(gt_file, 'wt') as f:\n                for tracks in info['gt_tracks']:\n                    line = '%d,%d,%d,%d,%d,%d,%d,%d,%.5f\\n' % (\n                        tracks[0], tracks[1], tracks[2], tracks[3], tracks[4],\n                        tracks[5], tracks[6], tracks[7], tracks[8])\n                    f.writelines(line)\n            info['gt_tracks'].clear()\n        # save seq info\n        with open(self.seqmap, 'a') as f:\n            f.write(seq + '\\n')\n            f.close()\n\n    def compute_metrics(self, results: list = None) -> dict:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n                Defaults to None.\n\n        Returns:\n            dict: The computed metrics. The keys are the names of the metrics,\n            and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        # NOTICE: don't access `self.results` from the method.\n        eval_results = dict()\n\n        if self.format_only:\n            return eval_results\n\n        eval_config = trackeval.Evaluator.get_default_eval_config()\n\n        # need to split out the tracker name\n        # caused by the implementation of TrackEval\n        pred_dir_tmp = self.pred_dir.rsplit(osp.sep, 1)[0]\n        dataset_config = self.get_dataset_cfg(self.gt_dir, pred_dir_tmp)\n\n        evaluator = trackeval.Evaluator(eval_config)\n        dataset = [trackeval.datasets.MotChallenge2DBox(dataset_config)]\n        metrics = [\n            getattr(trackeval.metrics,\n                    metric)(dict(METRICS=[metric], THRESHOLD=0.5))\n            for metric in self.metrics\n        ]\n        output_res, _ = evaluator.evaluate(dataset, metrics)\n        output_res = output_res['MotChallenge2DBox'][\n            self.TRACKER]['COMBINED_SEQ']['pedestrian']\n\n        if 'HOTA' in self.metrics:\n            logger.info('Evaluating HOTA Metrics...')\n            eval_results['HOTA'] = np.average(output_res['HOTA']['HOTA'])\n            eval_results['AssA'] = np.average(output_res['HOTA']['AssA'])\n            eval_results['DetA'] = np.average(output_res['HOTA']['DetA'])\n\n        if 'CLEAR' in self.metrics:\n            logger.info('Evaluating CLEAR Metrics...')\n            eval_results['MOTA'] = np.average(output_res['CLEAR']['MOTA'])\n            eval_results['MOTP'] = np.average(output_res['CLEAR']['MOTP'])\n            eval_results['IDSW'] = np.average(output_res['CLEAR']['IDSW'])\n            eval_results['TP'] = np.average(output_res['CLEAR']['CLR_TP'])\n            eval_results['FP'] = np.average(output_res['CLEAR']['CLR_FP'])\n            eval_results['FN'] = np.average(output_res['CLEAR']['CLR_FN'])\n            eval_results['Frag'] = np.average(output_res['CLEAR']['Frag'])\n            eval_results['MT'] = np.average(output_res['CLEAR']['MT'])\n            eval_results['ML'] = np.average(output_res['CLEAR']['ML'])\n\n        if 'Identity' in self.metrics:\n            logger.info('Evaluating Identity Metrics...')\n            eval_results['IDF1'] = np.average(output_res['Identity']['IDF1'])\n            eval_results['IDTP'] = np.average(output_res['Identity']['IDTP'])\n            eval_results['IDFN'] = np.average(output_res['Identity']['IDFN'])\n            eval_results['IDFP'] = np.average(output_res['Identity']['IDFP'])\n            eval_results['IDP'] = np.average(output_res['Identity']['IDP'])\n            eval_results['IDR'] = np.average(output_res['Identity']['IDR'])\n\n        return eval_results\n\n    def evaluate(self, size: int = 1) -> dict:\n        \"\"\"Evaluate the model performance of the whole dataset after processing\n        all batches.\n\n        Args:\n            size (int): Length of the entire validation dataset.\n                Defaults to None.\n\n        Returns:\n            dict: Evaluation metrics dict on the val dataset. The keys are the\n            names of the metrics, and the values are corresponding results.\n        \"\"\"\n        # wait for all processes to complete prediction.\n        barrier()\n\n        # gather seq_info and convert the list of dict to a dict.\n        # convert self.seq_info to dict first to make it picklable.\n        gathered_seq_info = all_gather_object(dict(self.seq_info))\n        all_seq_info = dict()\n        for _seq_info in gathered_seq_info:\n            all_seq_info.update(_seq_info)\n        self.seq_info = all_seq_info\n\n        if is_main_process():\n            _metrics = self.compute_metrics()  # type: ignore\n            # Add prefix to metric names\n            if self.prefix:\n                _metrics = {\n                    '/'.join((self.prefix, k)): v\n                    for k, v in _metrics.items()\n                }\n            metrics = [_metrics]\n        else:\n            metrics = [None]  # type: ignore\n\n        broadcast_object_list(metrics)\n\n        # reset the results list\n        self.results.clear()\n        return metrics[0]\n\n    def get_dataset_cfg(self, gt_folder: str, tracker_folder: str):\n        \"\"\"Get default configs for trackeval.datasets.MotChallenge2DBox.\n\n        Args:\n            gt_folder (str): the name of the GT folder\n            tracker_folder (str): the name of the tracker folder\n\n        Returns:\n            Dataset Configs for MotChallenge2DBox.\n        \"\"\"\n        dataset_config = dict(\n            # Location of GT data\n            GT_FOLDER=gt_folder,\n            # Trackers location\n            TRACKERS_FOLDER=tracker_folder,\n            # Where to save eval results\n            # (if None, same as TRACKERS_FOLDER)\n            OUTPUT_FOLDER=None,\n            # Use self.TRACKER as the default tracker\n            TRACKERS_TO_EVAL=[self.TRACKER],\n            # Option values: ['pedestrian']\n            CLASSES_TO_EVAL=['pedestrian'],\n            # Option Values: 'MOT15', 'MOT16', 'MOT17', 'MOT20', 'DanceTrack'\n            BENCHMARK=self.benchmark,\n            # Option Values: 'train', 'test'\n            SPLIT_TO_EVAL='val' if self.benchmark == 'DanceTrack' else 'train',\n            # Whether tracker input files are zipped\n            INPUT_AS_ZIP=False,\n            # Whether to print current config\n            PRINT_CONFIG=True,\n            # Whether to perform preprocessing\n            # (never done for MOT15)\n            DO_PREPROC=False if self.benchmark == 'MOT15' else True,\n            # Tracker files are in\n            # TRACKER_FOLDER/tracker_name/TRACKER_SUB_FOLDER\n            TRACKER_SUB_FOLDER='',\n            # Output files are saved in\n            # OUTPUT_FOLDER/tracker_name/OUTPUT_SUB_FOLDER\n            OUTPUT_SUB_FOLDER='',\n            # Names of trackers to display\n            # (if None: TRACKERS_TO_EVAL)\n            TRACKER_DISPLAY_NAMES=None,\n            # Where seqmaps are found\n            # (if None: GT_FOLDER/seqmaps)\n            SEQMAP_FOLDER=None,\n            # Directly specify seqmap file\n            # (if none use seqmap_folder/benchmark-split_to_eval)\n            SEQMAP_FILE=self.seqmap,\n            # If not None, specify sequences to eval\n            # and their number of timesteps\n            SEQ_INFO={\n                seq: info['seq_length']\n                for seq, info in self.seq_info.items()\n            },\n            # '{gt_folder}/{seq}.txt'\n            GT_LOC_FORMAT='{gt_folder}/{seq}.txt',\n            # If False, data is in GT_FOLDER/BENCHMARK-SPLIT_TO_EVAL/ and in\n            # TRACKERS_FOLDER/BENCHMARK-SPLIT_TO_EVAL/tracker/\n            # If True, the middle 'benchmark-split' folder is skipped for both.\n            SKIP_SPLIT_FOL=True,\n        )\n\n        return dataset_config\n"
  },
  {
    "path": "mmdet/evaluation/metrics/openimages_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom collections import OrderedDict\nfrom typing import List, Optional, Sequence, Union\n\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.logging import MMLogger, print_log\n\nfrom mmdet.registry import METRICS\nfrom ..functional import eval_map\n\n\n@METRICS.register_module()\nclass OpenImagesMetric(BaseMetric):\n    \"\"\"OpenImages evaluation metric.\n\n    Evaluate detection mAP for OpenImages. Please refer to\n    https://storage.googleapis.com/openimages/web/evaluation.html for more\n    details.\n\n    Args:\n        iou_thrs (float or List[float]): IoU threshold. Defaults to 0.5.\n        ioa_thrs (float or List[float]): IoA threshold. Defaults to 0.5.\n        scale_ranges (List[tuple], optional): Scale ranges for evaluating\n            mAP. If not specified, all bounding boxes would be included in\n            evaluation. Defaults to None\n        use_group_of (bool): Whether consider group of groud truth bboxes\n            during evaluating. Defaults to True.\n        get_supercategory (bool): Whether to get parent class of the\n            current class. Default: True.\n        filter_labels (bool): Whether filter unannotated classes.\n            Default: True.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n    \"\"\"\n    default_prefix: Optional[str] = 'openimages'\n\n    def __init__(self,\n                 iou_thrs: Union[float, List[float]] = 0.5,\n                 ioa_thrs: Union[float, List[float]] = 0.5,\n                 scale_ranges: Optional[List[tuple]] = None,\n                 use_group_of: bool = True,\n                 get_supercategory: bool = True,\n                 filter_labels: bool = True,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        self.iou_thrs = [iou_thrs] if isinstance(iou_thrs, float) else iou_thrs\n        self.ioa_thrs = [ioa_thrs] if (isinstance(ioa_thrs, float)\n                                       or ioa_thrs is None) else ioa_thrs\n        assert isinstance(self.iou_thrs, list) and isinstance(\n            self.ioa_thrs, list)\n        assert len(self.iou_thrs) == len(self.ioa_thrs)\n\n        self.scale_ranges = scale_ranges\n        self.use_group_of = use_group_of\n        self.get_supercategory = get_supercategory\n        self.filter_labels = filter_labels\n\n    def _get_supercategory_ann(self, instances: List[dict]) -> List[dict]:\n        \"\"\"Get parent classes's annotation of the corresponding class.\n\n        Args:\n            instances (List[dict]): A list of annotations of the instances.\n\n        Returns:\n            List[dict]: Annotations extended with super-category.\n        \"\"\"\n        supercat_instances = []\n        relation_matrix = self.dataset_meta['RELATION_MATRIX']\n        for instance in instances:\n            labels = np.where(relation_matrix[instance['bbox_label']])[0]\n            for label in labels:\n                if label == instance['bbox_label']:\n                    continue\n                new_instance = copy.deepcopy(instance)\n                new_instance['bbox_label'] = label\n                supercat_instances.append(new_instance)\n        return supercat_instances\n\n    def _process_predictions(self, pred_bboxes: np.ndarray,\n                             pred_scores: np.ndarray, pred_labels: np.ndarray,\n                             gt_instances: list,\n                             image_level_labels: np.ndarray) -> tuple:\n        \"\"\"Process results of the corresponding class of the detection bboxes.\n\n        Note: It will choose to do the following two processing according to\n        the parameters:\n\n        1. Whether to add parent classes of the corresponding class of the\n        detection bboxes.\n\n        2. Whether to ignore the classes that unannotated on that image.\n\n        Args:\n            pred_bboxes (np.ndarray): bboxes predicted by the model\n            pred_scores (np.ndarray): scores predicted by the model\n            pred_labels (np.ndarray): labels predicted by the model\n            gt_instances (list): ground truth annotations\n            image_level_labels (np.ndarray): human-verified image level labels\n\n        Returns:\n            tuple: Processed bboxes, scores, and labels.\n        \"\"\"\n        processed_bboxes = copy.deepcopy(pred_bboxes)\n        processed_scores = copy.deepcopy(pred_scores)\n        processed_labels = copy.deepcopy(pred_labels)\n        gt_labels = np.array([ins['bbox_label'] for ins in gt_instances],\n                             dtype=np.int64)\n        if image_level_labels is not None:\n            allowed_classes = np.unique(\n                np.append(gt_labels, image_level_labels))\n        else:\n            allowed_classes = np.unique(gt_labels)\n        relation_matrix = self.dataset_meta['RELATION_MATRIX']\n        pred_classes = np.unique(pred_labels)\n        for pred_class in pred_classes:\n            classes = np.where(relation_matrix[pred_class])[0]\n            for cls in classes:\n                if (cls in allowed_classes and cls != pred_class\n                        and self.get_supercategory):\n                    # add super-supercategory preds\n                    index = np.where(pred_labels == pred_class)[0]\n                    processed_scores = np.concatenate(\n                        [processed_scores, pred_scores[index]])\n                    processed_bboxes = np.concatenate(\n                        [processed_bboxes, pred_bboxes[index]])\n                    extend_labels = np.full(index.shape, cls, dtype=np.int64)\n                    processed_labels = np.concatenate(\n                        [processed_labels, extend_labels])\n                elif cls not in allowed_classes and self.filter_labels:\n                    # remove unannotated preds\n                    index = np.where(processed_labels != cls)[0]\n                    processed_scores = processed_scores[index]\n                    processed_bboxes = processed_bboxes[index]\n                    processed_labels = processed_labels[index]\n        return processed_bboxes, processed_scores, processed_labels\n\n    # TODO: data_batch is no longer needed, consider adjusting the\n    #  parameter position\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            gt = copy.deepcopy(data_sample)\n            # add super-category instances\n            # TODO: Need to refactor to support LoadAnnotations\n            instances = gt['instances']\n            if self.get_supercategory:\n                supercat_instances = self._get_supercategory_ann(instances)\n                instances.extend(supercat_instances)\n            gt_labels = []\n            gt_bboxes = []\n            is_group_ofs = []\n            for ins in instances:\n                gt_labels.append(ins['bbox_label'])\n                gt_bboxes.append(ins['bbox'])\n                is_group_ofs.append(ins['is_group_of'])\n            ann = dict(\n                labels=np.array(gt_labels, dtype=np.int64),\n                bboxes=np.array(gt_bboxes, dtype=np.float32).reshape((-1, 4)),\n                gt_is_group_ofs=np.array(is_group_ofs, dtype=bool))\n\n            image_level_labels = gt.get('image_level_labels', None)\n            pred = data_sample['pred_instances']\n            pred_bboxes = pred['bboxes'].cpu().numpy()\n            pred_scores = pred['scores'].cpu().numpy()\n            pred_labels = pred['labels'].cpu().numpy()\n\n            pred_bboxes, pred_scores, pred_labels = self._process_predictions(\n                pred_bboxes, pred_scores, pred_labels, instances,\n                image_level_labels)\n\n            dets = []\n            for label in range(len(self.dataset_meta['classes'])):\n                index = np.where(pred_labels == label)[0]\n                pred_bbox_scores = np.hstack(\n                    [pred_bboxes[index], pred_scores[index].reshape((-1, 1))])\n                dets.append(pred_bbox_scores)\n            self.results.append((ann, dets))\n\n    def compute_metrics(self, results: list) -> dict:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            dict: The computed metrics. The keys are the names of the metrics,\n            and the values are corresponding results.\n        \"\"\"\n        logger = MMLogger.get_current_instance()\n        gts, preds = zip(*results)\n        eval_results = OrderedDict()\n        # get dataset type\n        dataset_type = self.dataset_meta.get('dataset_type')\n        if dataset_type not in ['oid_challenge', 'oid_v6']:\n            dataset_type = 'oid_v6'\n            print_log(\n                'Cannot infer dataset type from the length of the'\n                ' classes. Set `oid_v6` as dataset type.',\n                logger='current')\n        mean_aps = []\n        for i, (iou_thr,\n                ioa_thr) in enumerate(zip(self.iou_thrs, self.ioa_thrs)):\n            if self.use_group_of:\n                assert ioa_thr is not None, 'ioa_thr must have value when' \\\n                                            ' using group_of in evaluation.'\n            print_log(f'\\n{\"-\" * 15}iou_thr, ioa_thr: {iou_thr}, {ioa_thr}'\n                      f'{\"-\" * 15}')\n            mean_ap, _ = eval_map(\n                preds,\n                gts,\n                scale_ranges=self.scale_ranges,\n                iou_thr=iou_thr,\n                ioa_thr=ioa_thr,\n                dataset=dataset_type,\n                logger=logger,\n                use_group_of=self.use_group_of)\n\n            mean_aps.append(mean_ap)\n            eval_results[f'AP{int(iou_thr * 100):02d}'] = round(mean_ap, 3)\n        eval_results['mAP'] = sum(mean_aps) / len(mean_aps)\n        return eval_results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/ov_coco_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport itertools\nimport os.path as osp\nimport tempfile\nfrom collections import OrderedDict\nfrom typing import Dict\n\nimport numpy as np\nfrom mmengine.fileio import load\nfrom mmengine.logging import MMLogger\nfrom terminaltables import AsciiTable\n\nfrom mmdet.datasets.api_wrappers import COCO, COCOeval, COCOevalMP\nfrom mmdet.registry import METRICS\nfrom .coco_metric import CocoMetric\n\n\n@METRICS.register_module()\nclass OVCocoMetric(CocoMetric):\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        # split gt and prediction list\n        gts, preds = zip(*results)\n\n        tmp_dir = None\n        if self.outfile_prefix is None:\n            tmp_dir = tempfile.TemporaryDirectory()\n            outfile_prefix = osp.join(tmp_dir.name, 'results')\n        else:\n            outfile_prefix = self.outfile_prefix\n\n        if self._coco_api is None:\n            # use converted gt json file to initialize coco api\n            logger.info('Converting ground truth to coco format...')\n            coco_json_path = self.gt_to_coco_json(\n                gt_dicts=gts, outfile_prefix=outfile_prefix)\n            self._coco_api = COCO(coco_json_path)\n\n        # handle lazy init\n        if self.cat_ids is None:\n            self.cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['classes'])\n            self.base_cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['base_classes'])\n            self.novel_cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['novel_classes'])\n\n        if self.img_ids is None:\n            self.img_ids = self._coco_api.get_img_ids()\n\n        # convert predictions to coco format and dump to json file\n        result_files = self.results2json(preds, outfile_prefix)\n\n        eval_results = OrderedDict()\n        if self.format_only:\n            logger.info('results are saved in '\n                        f'{osp.dirname(outfile_prefix)}')\n            return eval_results\n\n        for metric in self.metrics:\n            logger.info(f'Evaluating {metric}...')\n\n            # TODO: May refactor fast_eval_recall to an independent metric?\n            # fast eval recall\n            if metric == 'proposal_fast':\n                ar = self.fast_eval_recall(\n                    preds, self.proposal_nums, self.iou_thrs, logger=logger)\n                log_msg = []\n                for i, num in enumerate(self.proposal_nums):\n                    eval_results[f'AR@{num}'] = ar[i]\n                    log_msg.append(f'\\nAR@{num}\\t{ar[i]:.4f}')\n                log_msg = ''.join(log_msg)\n                logger.info(log_msg)\n                continue\n\n            # evaluate proposal, bbox and segm\n            iou_type = 'bbox' if metric == 'proposal' else metric\n            if metric not in result_files:\n                raise KeyError(f'{metric} is not in results')\n            try:\n                predictions = load(result_files[metric])\n                if iou_type == 'segm':\n                    # Refer to https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/coco.py#L331  # noqa\n                    # When evaluating mask AP, if the results contain bbox,\n                    # cocoapi will use the box area instead of the mask area\n                    # for calculating the instance area. Though the overall AP\n                    # is not affected, this leads to different\n                    # small/medium/large mask AP results.\n                    for x in predictions:\n                        x.pop('bbox')\n                coco_dt = self._coco_api.loadRes(predictions)\n\n            except IndexError:\n                logger.error(\n                    'The testing results of the whole dataset is empty.')\n                break\n\n            if self.use_mp_eval:\n                coco_eval = COCOevalMP(self._coco_api, coco_dt, iou_type)\n            else:\n                coco_eval = COCOeval(self._coco_api, coco_dt, iou_type)\n\n            coco_eval.params.catIds = self.cat_ids\n            coco_eval.params.imgIds = self.img_ids\n            coco_eval.params.maxDets = list(self.proposal_nums)\n            coco_eval.params.iouThrs = self.iou_thrs\n\n            # mapping of cocoEval.stats\n            coco_metric_names = {\n                'mAP': 0,\n                'mAP_50': 1,\n                'mAP_75': 2,\n                'mAP_s': 3,\n                'mAP_m': 4,\n                'mAP_l': 5,\n                'AR@100': 6,\n                'AR@300': 7,\n                'AR@1000': 8,\n                'AR_s@1000': 9,\n                'AR_m@1000': 10,\n                'AR_l@1000': 11\n            }\n            metric_items = self.metric_items\n            if metric_items is not None:\n                for metric_item in metric_items:\n                    if metric_item not in coco_metric_names:\n                        raise KeyError(\n                            f'metric item \"{metric_item}\" is not supported')\n\n            if metric == 'proposal':\n                coco_eval.params.useCats = 0\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if metric_items is None:\n                    metric_items = [\n                        'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000',\n                        'AR_m@1000', 'AR_l@1000'\n                    ]\n\n                for item in metric_items:\n                    val = float(\n                        f'{coco_eval.stats[coco_metric_names[item]]:.3f}')\n                    eval_results[item] = val\n            else:\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if self.classwise:  # Compute per-category AP\n                    # Compute per-category AP\n                    # from https://github.com/facebookresearch/detectron2/\n                    precisions = coco_eval.eval['precision']\n                    # precision: (iou, recall, cls, area range, max dets)\n                    assert len(self.cat_ids) == precisions.shape[2]\n\n                    results_per_category = []\n                    for idx, cat_id in enumerate(self.cat_ids):\n                        t = []\n                        # area range index 0: all area ranges\n                        # max dets index -1: typically 100 per image\n                        nm = self._coco_api.loadCats(cat_id)[0]\n                        precision = precisions[:, :, idx, 0, -1]\n                        precision = precision[precision > -1]\n                        if precision.size:\n                            ap = np.mean(precision)\n                        else:\n                            ap = float('nan')\n                        t.append(f'{nm[\"name\"]}')\n                        t.append(f'{round(ap, 3)}')\n                        eval_results[f'{nm[\"name\"]}_precision'] = round(ap, 3)\n\n                        # indexes of IoU  @50 and @75\n                        for iou in [0, 5]:\n                            precision = precisions[iou, :, idx, 0, -1]\n                            precision = precision[precision > -1]\n                            if precision.size:\n                                ap = np.mean(precision)\n                            else:\n                                ap = float('nan')\n                            t.append(f'{round(ap, 3)}')\n\n                        # indexes of area of small, median and large\n                        for area in [1, 2, 3]:\n                            precision = precisions[:, :, idx, area, -1]\n                            precision = precision[precision > -1]\n                            if precision.size:\n                                ap = np.mean(precision)\n                            else:\n                                ap = float('nan')\n                            t.append(f'{round(ap, 3)}')\n                        results_per_category.append(tuple(t))\n\n                    num_columns = len(results_per_category[0])\n                    results_flatten = list(\n                        itertools.chain(*results_per_category))\n                    headers = [\n                        'category', 'mAP', 'mAP_50', 'mAP_75', 'mAP_s',\n                        'mAP_m', 'mAP_l'\n                    ]\n                    results_2d = itertools.zip_longest(*[\n                        results_flatten[i::num_columns]\n                        for i in range(num_columns)\n                    ])\n                    table_data = [headers]\n                    table_data += [result for result in results_2d]\n                    table = AsciiTable(table_data)\n                    logger.info('\\n' + table.table)\n\n                # ------------get novel_ap50 and base_ap50---------\n                precisions = coco_eval.eval['precision']\n                assert len(self.cat_ids) == precisions.shape[2]\n                base_inds, novel_inds = [], []\n\n                for idx, catId in enumerate(self.cat_ids):\n                    if catId in self.base_cat_ids:\n                        base_inds.append(idx)\n                    if catId in self.novel_cat_ids:\n                        novel_inds.append(idx)\n\n                base_ap = precisions[:, :, base_inds, 0, -1]\n                novel_ap = precisions[:, :, novel_inds, 0, -1]\n                base_ap50 = precisions[0, :, base_inds, 0, -1]\n                novel_ap50 = precisions[0, :, novel_inds, 0, -1]\n\n                eval_results['base_ap'] = np.mean(\n                    base_ap[base_ap > -1]) if len(\n                        base_ap[base_ap > -1]) else -1\n                eval_results['novel_ap'] = np.mean(\n                    novel_ap[novel_ap > -1]) if len(\n                        novel_ap[novel_ap > -1]) else -1\n                eval_results['base_ap50'] = np.mean(\n                    base_ap50[base_ap50 > -1]) if len(\n                        base_ap50[base_ap50 > -1]) else -1\n                eval_results['novel_ap50'] = np.mean(\n                    novel_ap50[novel_ap50 > -1]) if len(\n                        novel_ap50[novel_ap50 > -1]) else -1\n                # ------------get novel_ap50 and base_ap50---------\n                if metric_items is None:\n                    metric_items = [\n                        'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l'\n                    ]\n\n                for metric_item in metric_items:\n                    key = f'{metric}_{metric_item}'\n                    val = coco_eval.stats[coco_metric_names[metric_item]]\n                    eval_results[key] = float(f'{round(val, 3)}')\n\n                ap = coco_eval.stats[:6]\n                logger.info(f'{metric}_mAP_copypaste: {ap[0]:.3f} '\n                            f'{ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} '\n                            f'{ap[4]:.3f} {ap[5]:.3f}')\n\n        if tmp_dir is not None:\n            tmp_dir.cleanup()\n        return eval_results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/refexp_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Optional, Sequence\n\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import get_local_path\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.datasets.api_wrappers import COCO\nfrom mmdet.registry import METRICS\nfrom ..functional import bbox_overlaps\n\n\n@METRICS.register_module()\nclass RefExpMetric(BaseMetric):\n    default_prefix: Optional[str] = 'refexp'\n\n    def __init__(self,\n                 ann_file: Optional[str] = None,\n                 metric: str = 'bbox',\n                 topk=(1, 5, 10),\n                 iou_thrs: float = 0.5,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.metric = metric\n        self.topk = topk\n        self.iou_thrs = iou_thrs\n\n        with get_local_path(ann_file) as local_path:\n            self.coco = COCO(local_path)\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        for data_sample in data_samples:\n            result = dict()\n            pred = data_sample['pred_instances']\n            result['img_id'] = data_sample['img_id']\n            result['bboxes'] = pred['bboxes'].cpu().numpy()\n            result['scores'] = pred['scores'].cpu().numpy()\n            self.results.append(result)\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        dataset2score = {\n            'refcoco': {k: 0.0\n                        for k in self.topk},\n            'refcoco+': {k: 0.0\n                         for k in self.topk},\n            'refcocog': {k: 0.0\n                         for k in self.topk},\n        }\n        dataset2count = {'refcoco': 0.0, 'refcoco+': 0.0, 'refcocog': 0.0}\n\n        for result in results:\n            img_id = result['img_id']\n\n            ann_ids = self.coco.getAnnIds(imgIds=img_id)\n            assert len(ann_ids) == 1\n            img_info = self.coco.loadImgs(img_id)[0]\n            target = self.coco.loadAnns(ann_ids[0])\n\n            target_bbox = target[0]['bbox']\n            converted_bbox = [\n                target_bbox[0],\n                target_bbox[1],\n                target_bbox[2] + target_bbox[0],\n                target_bbox[3] + target_bbox[1],\n            ]\n            iou = bbox_overlaps(result['bboxes'],\n                                np.array(converted_bbox).reshape(-1, 4))\n            for k in self.topk:\n                if max(iou[:k]) >= self.iou_thrs:\n                    dataset2score[img_info['dataset_name']][k] += 1.0\n            dataset2count[img_info['dataset_name']] += 1.0\n\n        for key, value in dataset2score.items():\n            for k in self.topk:\n                try:\n                    value[k] /= dataset2count[key]\n                except Exception as e:\n                    print(e)\n\n        results = {}\n        mean_precision = 0.0\n        for key, value in dataset2score.items():\n            results[key] = sorted([v for k, v in value.items()])\n            mean_precision += sum(results[key])\n            logger.info(\n                f' Dataset: {key} - Precision @ 1, 5, 10: {results[key]}')\n\n        # `mean_precision` key is used for saving the best checkpoint\n        out_results = {'mean_precision': mean_precision / 9.0}\n\n        for i, k in enumerate(self.topk):\n            out_results[f'refcoco_precision@{k}'] = results['refcoco'][i]\n        for i, k in enumerate(self.topk):\n            out_results[f'refcoco+_precision@{k}'] = results['refcoco+'][i]\n        for i, k in enumerate(self.topk):\n            out_results[f'refcocog_precision@{k}'] = results['refcocog'][i]\n        return out_results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/refseg_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Sequence\n\nimport torch\nfrom mmengine.evaluator import BaseMetric\n\nfrom mmdet.registry import METRICS\n\n\n@METRICS.register_module()\nclass RefSegMetric(BaseMetric):\n    \"\"\"Referring Expression Segmentation Metric.\"\"\"\n\n    def __init__(self, metric: Sequence = ('cIoU', 'mIoU'), **kwargs):\n        super().__init__(**kwargs)\n        assert set(metric).issubset(['cIoU', 'mIoU']), \\\n            f'Only support cIoU and mIoU, but got {metric}'\n        assert len(metric) > 0, 'metrics should not be empty'\n        self.metrics = metric\n\n    def compute_iou(self, pred_seg: torch.Tensor,\n                    gt_seg: torch.Tensor) -> tuple:\n        overlap = pred_seg & gt_seg\n        union = pred_seg | gt_seg\n        return overlap, union\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data and data_samples.\n\n        The processed results should be stored in ``self.results``, which will\n        be used to compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of outputs from the model.\n        \"\"\"\n        for data_sample in data_samples:\n            pred_label = data_sample['pred_instances']['masks'].bool()\n            label = data_sample['gt_masks'].to_tensor(\n                pred_label.dtype, pred_label.device).bool()\n            # calculate iou\n            overlap, union = self.compute_iou(pred_label, label)\n\n            bs = len(pred_label)\n            iou = overlap.reshape(bs, -1).sum(-1) * 1.0 / union.reshape(\n                bs, -1).sum(-1)\n            iou = torch.nan_to_num_(iou, nan=0.0)\n            self.results.append((overlap.sum(), union.sum(), iou.sum(), bs))\n\n    def compute_metrics(self, results: list) -> dict:\n        results = tuple(zip(*results))\n        assert len(results) == 4\n        cum_i = sum(results[0])\n        cum_u = sum(results[1])\n        iou = sum(results[2])\n        seg_total = sum(results[3])\n\n        metrics = {}\n        if 'cIoU' in self.metrics:\n            metrics['cIoU'] = cum_i * 100 / cum_u\n        if 'mIoU' in self.metrics:\n            metrics['mIoU'] = iou * 100 / seg_total\n        return metrics\n"
  },
  {
    "path": "mmdet/evaluation/metrics/reid_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.evaluator import BaseMetric\n\nfrom mmdet.registry import METRICS\n\n\n@METRICS.register_module()\nclass ReIDMetrics(BaseMetric):\n    \"\"\"mAP and CMC evaluation metrics for the ReID task.\n\n    Args:\n        metric (str | list[str]): Metrics to be evaluated.\n            Default value is `mAP`.\n        metric_options: (dict, optional): Options for calculating metrics.\n            Allowed keys are 'rank_list' and 'max_rank'. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Default: None\n    \"\"\"\n    allowed_metrics = ['mAP', 'CMC']\n    default_prefix: Optional[str] = 'reid-metric'\n\n    def __init__(self,\n                 metric: Union[str, Sequence[str]] = 'mAP',\n                 metric_options: Optional[dict] = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device, prefix)\n\n        if isinstance(metric, list):\n            metrics = metric\n        elif isinstance(metric, str):\n            metrics = [metric]\n        else:\n            raise TypeError('metric must be a list or a str.')\n        for metric in metrics:\n            if metric not in self.allowed_metrics:\n                raise KeyError(f'metric {metric} is not supported.')\n        self.metrics = metrics\n\n        self.metric_options = metric_options or dict(\n            rank_list=[1, 5, 10, 20], max_rank=20)\n        for rank in self.metric_options['rank_list']:\n            assert 1 <= rank <= self.metric_options['max_rank']\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions.\n\n        The processed results should be stored in ``self.results``, which will\n        be used to compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            pred_feature = data_sample['pred_feature']\n            assert isinstance(pred_feature, torch.Tensor)\n            gt_label = data_sample.get('gt_label', data_sample['gt_label'])\n            assert isinstance(gt_label['label'], torch.Tensor)\n            result = dict(\n                pred_feature=pred_feature.data.cpu(),\n                gt_label=gt_label['label'].cpu())\n            self.results.append(result)\n\n    def compute_metrics(self, results: list) -> dict:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            dict: The computed metrics. The keys are the names of the metrics,\n            and the values are corresponding results.\n        \"\"\"\n        # NOTICE: don't access `self.results` from the method.\n        metrics = {}\n\n        pids = torch.cat([result['gt_label'] for result in results]).numpy()\n        features = torch.stack([result['pred_feature'] for result in results])\n\n        n, c = features.size()\n        mat = torch.pow(features, 2).sum(dim=1, keepdim=True).expand(n, n)\n        distmat = mat + mat.t()\n        distmat.addmm_(features, features.t(), beta=1, alpha=-2)\n        distmat = distmat.numpy()\n\n        indices = np.argsort(distmat, axis=1)\n        matches = (pids[indices] == pids[:, np.newaxis]).astype(np.int32)\n\n        all_cmc = []\n        all_AP = []\n        num_valid_q = 0.\n        for q_idx in range(n):\n            # remove self\n            raw_cmc = matches[q_idx][1:]\n            if not np.any(raw_cmc):\n                # this condition is true when query identity\n                # does not appear in gallery\n                continue\n\n            cmc = raw_cmc.cumsum()\n            cmc[cmc > 1] = 1\n\n            all_cmc.append(cmc[:self.metric_options['max_rank']])\n            num_valid_q += 1.\n\n            # compute average precision\n            num_rel = raw_cmc.sum()\n            tmp_cmc = raw_cmc.cumsum()\n            tmp_cmc = [x / (i + 1.) for i, x in enumerate(tmp_cmc)]\n            tmp_cmc = np.asarray(tmp_cmc) * raw_cmc\n            AP = tmp_cmc.sum() / num_rel\n            all_AP.append(AP)\n\n        assert num_valid_q > 0, \\\n            'Error: all query identities do not appear in gallery'\n\n        all_cmc = np.asarray(all_cmc)\n        all_cmc = all_cmc.sum(0) / num_valid_q\n        mAP = np.mean(all_AP)\n\n        if 'mAP' in self.metrics:\n            metrics['mAP'] = np.around(mAP, decimals=3)\n        if 'CMC' in self.metrics:\n            for rank in self.metric_options['rank_list']:\n                metrics[f'R{rank}'] = np.around(all_cmc[rank - 1], decimals=3)\n\n        return metrics\n"
  },
  {
    "path": "mmdet/evaluation/metrics/semseg_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom collections import OrderedDict\nfrom typing import Dict, Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nfrom mmcv import imwrite\nfrom mmengine.dist import is_main_process\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.logging import MMLogger, print_log\nfrom mmengine.utils import mkdir_or_exist\nfrom PIL import Image\n\ntry:\n    from prettytable import PrettyTable\nexcept ImportError:\n    PrettyTable = None\n\nfrom mmdet.registry import METRICS\n\n\n@METRICS.register_module()\nclass SemSegMetric(BaseMetric):\n    \"\"\"mIoU evaluation metric.\n\n    Args:\n        iou_metrics (list[str] | str): Metrics to be calculated, the options\n            includes 'mIoU', 'mDice' and 'mFscore'.\n        beta (int): Determines the weight of recall in the combined score.\n            Default: 1.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        output_dir (str): The directory for output prediction. Defaults to\n            None.\n        format_only (bool): Only format result for results commit without\n            perform evaluation. It is useful when you want to save the result\n            to a specific format and submit it to the test server.\n            Defaults to False.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 iou_metrics: Sequence[str] = ['mIoU'],\n                 beta: int = 1,\n                 collect_device: str = 'cpu',\n                 output_dir: Optional[str] = None,\n                 format_only: bool = False,\n                 backend_args: dict = None,\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n\n        if isinstance(iou_metrics, str):\n            iou_metrics = [iou_metrics]\n        if not set(iou_metrics).issubset(set(['mIoU', 'mDice', 'mFscore'])):\n            raise KeyError(f'metrics {iou_metrics} is not supported. '\n                           f'Only supports mIoU/mDice/mFscore.')\n        self.metrics = iou_metrics\n        self.beta = beta\n        self.output_dir = output_dir\n        if self.output_dir and is_main_process():\n            mkdir_or_exist(self.output_dir)\n        self.format_only = format_only\n        self.backend_args = backend_args\n\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data and data_samples.\n\n        The processed results should be stored in ``self.results``, which will\n        be used to compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of outputs from the model.\n        \"\"\"\n        num_classes = len(self.dataset_meta['classes'])\n        for data_sample in data_samples:\n            pred_label = data_sample['pred_sem_seg']['sem_seg'].squeeze()\n            # format_only always for test dataset without ground truth\n            if not self.format_only:\n                label = data_sample['gt_sem_seg']['sem_seg'].squeeze().to(\n                    pred_label)\n                ignore_index = data_sample['pred_sem_seg'].get(\n                    'ignore_index', 255)\n                self.results.append(\n                    self._compute_pred_stats(pred_label, label, num_classes,\n                                             ignore_index))\n\n            # format_result\n            if self.output_dir is not None:\n                basename = osp.splitext(osp.basename(\n                    data_sample['img_path']))[0]\n                png_filename = osp.abspath(\n                    osp.join(self.output_dir, f'{basename}.png'))\n                output_mask = pred_label.cpu().numpy()\n                output = Image.fromarray(output_mask.astype(np.uint8))\n                imwrite(output, png_filename, backend_args=self.backend_args)\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n                the metrics, and the values are corresponding results. The key\n                mainly includes aAcc, mIoU, mAcc, mDice, mFscore, mPrecision,\n                mRecall.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n        if self.format_only:\n            logger.info(f'results are saved to {osp.dirname(self.output_dir)}')\n            return OrderedDict()\n\n        ret_metrics = self.get_return_metrics(results)\n\n        # summary table\n        ret_metrics_summary = OrderedDict({\n            ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2)\n            for ret_metric, ret_metric_value in ret_metrics.items()\n        })\n        metrics = dict()\n        for key, val in ret_metrics_summary.items():\n            if key == 'aAcc':\n                metrics[key] = val\n            else:\n                metrics['m' + key] = val\n\n        print_semantic_table(ret_metrics, self.dataset_meta['classes'], logger)\n\n        return metrics\n\n    def _compute_pred_stats(self, pred_label: torch.tensor,\n                            label: torch.tensor, num_classes: int,\n                            ignore_index: int):\n        \"\"\"Parse semantic segmentation predictions.\n\n        Args:\n            pred_label (torch.tensor): Prediction segmentation map\n                or predict result filename. The shape is (H, W).\n            label (torch.tensor): Ground truth segmentation map\n                or label filename. The shape is (H, W).\n            num_classes (int): Number of categories.\n\n        Returns:\n            torch.Tensor: The intersection of prediction and ground truth\n                histogram on all classes.\n            torch.Tensor: The union of prediction and ground truth histogram on\n                all classes.\n            torch.Tensor: The prediction histogram on all classes.\n            torch.Tensor: The ground truth histogram on all classes.\n        \"\"\"\n        assert pred_label.shape == label.shape\n        mask = label != ignore_index\n        label, pred_label = label[mask], pred_label[mask]\n\n        intersect = pred_label[pred_label == label]\n        area_intersect = torch.histc(\n            intersect.float(), bins=num_classes, min=0, max=num_classes - 1)\n        area_pred_label = torch.histc(\n            pred_label.float(), bins=num_classes, min=0, max=num_classes - 1)\n        area_label = torch.histc(\n            label.float(), bins=num_classes, min=0, max=num_classes - 1)\n        area_union = area_pred_label + area_label - area_intersect\n        result = dict(\n            area_intersect=area_intersect,\n            area_union=area_union,\n            area_pred_label=area_pred_label,\n            area_label=area_label)\n        return result\n\n    def get_return_metrics(self, results: list) -> dict:\n        \"\"\"Calculate evaluation metrics.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, np.ndarray]: per category evaluation metrics,\n                shape (num_classes, ).\n        \"\"\"\n\n        def f_score(precision, recall, beta=1):\n            \"\"\"calculate the f-score value.\n\n            Args:\n                precision (float | torch.Tensor): The precision value.\n                recall (float | torch.Tensor): The recall value.\n                beta (int): Determines the weight of recall in the combined\n                    score. Default: 1.\n\n            Returns:\n                [torch.tensor]: The f-score value.\n            \"\"\"\n            score = (1 + beta**2) * (precision * recall) / (\n                (beta**2 * precision) + recall)\n            return score\n\n        total_area_intersect = sum([r['area_intersect'] for r in results])\n        total_area_union = sum([r['area_union'] for r in results])\n        total_area_pred_label = sum([r['area_pred_label'] for r in results])\n        total_area_label = sum([r['area_label'] for r in results])\n\n        all_acc = total_area_intersect / total_area_label\n        ret_metrics = OrderedDict({'aAcc': all_acc})\n        for metric in self.metrics:\n            if metric == 'mIoU':\n                iou = total_area_intersect / total_area_union\n                acc = total_area_intersect / total_area_label\n                ret_metrics['IoU'] = iou\n                ret_metrics['Acc'] = acc\n            elif metric == 'mDice':\n                dice = 2 * total_area_intersect / (\n                    total_area_pred_label + total_area_label)\n                acc = total_area_intersect / total_area_label\n                ret_metrics['Dice'] = dice\n                ret_metrics['Acc'] = acc\n            elif metric == 'mFscore':\n                precision = total_area_intersect / total_area_pred_label\n                recall = total_area_intersect / total_area_label\n                f_value = torch.tensor([\n                    f_score(x[0], x[1], self.beta)\n                    for x in zip(precision, recall)\n                ])\n                ret_metrics['Fscore'] = f_value\n                ret_metrics['Precision'] = precision\n                ret_metrics['Recall'] = recall\n\n        ret_metrics = {\n            metric: value.cpu().numpy()\n            for metric, value in ret_metrics.items()\n        }\n\n        return ret_metrics\n\n\ndef print_semantic_table(\n        results: dict,\n        class_names: list,\n        logger: Optional[Union['MMLogger', str]] = None) -> None:\n    \"\"\"Print semantic segmentation evaluation results table.\n\n    Args:\n        results (dict): The evaluation results.\n        class_names (list): Class names.\n        logger (MMLogger | str, optional): Logger used for printing.\n            Default: None.\n    \"\"\"\n    # each class table\n    results.pop('aAcc', None)\n    ret_metrics_class = OrderedDict({\n        ret_metric: np.round(ret_metric_value * 100, 2)\n        for ret_metric, ret_metric_value in results.items()\n    })\n\n    print_log('per class results:', logger)\n    if PrettyTable:\n        class_table_data = PrettyTable()\n        ret_metrics_class.update({'Class': class_names})\n        ret_metrics_class.move_to_end('Class', last=False)\n        for key, val in ret_metrics_class.items():\n            class_table_data.add_column(key, val)\n        print_log('\\n' + class_table_data.get_string(), logger=logger)\n    else:\n        logger.warning(\n            '`prettytable` is not installed, for better table format, '\n            'please consider installing it with \"pip install prettytable\"')\n        print_result = {}\n        for class_name, iou, acc in zip(class_names, ret_metrics_class['IoU'],\n                                        ret_metrics_class['Acc']):\n            print_result[class_name] = {'IoU': iou, 'Acc': acc}\n        print_log(print_result, logger)\n"
  },
  {
    "path": "mmdet/evaluation/metrics/voc_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport warnings\nfrom collections import OrderedDict\nfrom typing import List, Optional, Sequence, Union\n\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.registry import METRICS\nfrom ..functional import eval_map, eval_recalls\n\n\n@METRICS.register_module()\nclass VOCMetric(BaseMetric):\n    \"\"\"Pascal VOC evaluation metric.\n\n    Args:\n        iou_thrs (float or List[float]): IoU threshold. Defaults to 0.5.\n        scale_ranges (List[tuple], optional): Scale ranges for evaluating\n            mAP. If not specified, all bounding boxes would be included in\n            evaluation. Defaults to None.\n        metric (str | list[str]): Metrics to be evaluated. Options are\n            'mAP', 'recall'. If is list, the first setting in the list will\n             be used to evaluate metric.\n        proposal_nums (Sequence[int]): Proposal number used for evaluating\n            recalls, such as recall@100, recall@1000.\n            Default: (100, 300, 1000).\n        eval_mode (str): 'area' or '11points', 'area' means calculating the\n            area under precision-recall curve, '11points' means calculating\n            the average precision of recalls at [0, 0.1, ..., 1].\n            The PASCAL VOC2007 defaults to use '11points', while PASCAL\n            VOC2012 defaults to use 'area'.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n    \"\"\"\n\n    default_prefix: Optional[str] = 'pascal_voc'\n\n    def __init__(self,\n                 iou_thrs: Union[float, List[float]] = 0.5,\n                 scale_ranges: Optional[List[tuple]] = None,\n                 metric: Union[str, List[str]] = 'mAP',\n                 proposal_nums: Sequence[int] = (100, 300, 1000),\n                 eval_mode: str = '11points',\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        self.iou_thrs = [iou_thrs] if isinstance(iou_thrs, float) \\\n            else iou_thrs\n        self.scale_ranges = scale_ranges\n        # voc evaluation metrics\n        if not isinstance(metric, str):\n            assert len(metric) == 1\n            metric = metric[0]\n        allowed_metrics = ['recall', 'mAP']\n        if metric not in allowed_metrics:\n            raise KeyError(\n                f\"metric should be one of 'recall', 'mAP', but got {metric}.\")\n        self.metric = metric\n        self.proposal_nums = proposal_nums\n        assert eval_mode in ['area', '11points'], \\\n            'Unrecognized mode, only \"area\" and \"11points\" are supported'\n        self.eval_mode = eval_mode\n\n    # TODO: data_batch is no longer needed, consider adjusting the\n    #  parameter position\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            gt = copy.deepcopy(data_sample)\n            # TODO: Need to refactor to support LoadAnnotations\n            gt_instances = gt['gt_instances']\n            gt_ignore_instances = gt['ignored_instances']\n            ann = dict(\n                labels=gt_instances['labels'].cpu().numpy(),\n                bboxes=gt_instances['bboxes'].cpu().numpy(),\n                bboxes_ignore=gt_ignore_instances['bboxes'].cpu().numpy(),\n                labels_ignore=gt_ignore_instances['labels'].cpu().numpy())\n\n            pred = data_sample['pred_instances']\n            pred_bboxes = pred['bboxes'].cpu().numpy()\n            pred_scores = pred['scores'].cpu().numpy()\n            pred_labels = pred['labels'].cpu().numpy()\n\n            dets = []\n            for label in range(len(self.dataset_meta['classes'])):\n                index = np.where(pred_labels == label)[0]\n                pred_bbox_scores = np.hstack(\n                    [pred_bboxes[index], pred_scores[index].reshape((-1, 1))])\n                dets.append(pred_bbox_scores)\n\n            self.results.append((ann, dets))\n\n    def compute_metrics(self, results: list) -> dict:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            dict: The computed metrics. The keys are the names of the metrics,\n            and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n        gts, preds = zip(*results)\n        eval_results = OrderedDict()\n        if self.metric == 'mAP':\n            assert isinstance(self.iou_thrs, list)\n            dataset_type = self.dataset_meta.get('dataset_type')\n            if dataset_type in ['VOC2007', 'VOC2012']:\n                dataset_name = 'voc'\n                if dataset_type == 'VOC2007' and self.eval_mode != '11points':\n                    warnings.warn('Pascal VOC2007 uses `11points` as default '\n                                  'evaluate mode, but you are using '\n                                  f'{self.eval_mode}.')\n                elif dataset_type == 'VOC2012' and self.eval_mode != 'area':\n                    warnings.warn('Pascal VOC2012 uses `area` as default '\n                                  'evaluate mode, but you are using '\n                                  f'{self.eval_mode}.')\n            else:\n                dataset_name = self.dataset_meta['classes']\n\n            mean_aps = []\n            for iou_thr in self.iou_thrs:\n                logger.info(f'\\n{\"-\" * 15}iou_thr: {iou_thr}{\"-\" * 15}')\n                # Follow the official implementation,\n                # http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCdevkit_18-May-2011.tar\n                # we should use the legacy coordinate system in mmdet 1.x,\n                # which means w, h should be computed as 'x2 - x1 + 1` and\n                # `y2 - y1 + 1`\n                mean_ap, _ = eval_map(\n                    preds,\n                    gts,\n                    scale_ranges=self.scale_ranges,\n                    iou_thr=iou_thr,\n                    dataset=dataset_name,\n                    logger=logger,\n                    eval_mode=self.eval_mode,\n                    use_legacy_coordinate=True)\n                mean_aps.append(mean_ap)\n                eval_results[f'AP{int(iou_thr * 100):02d}'] = round(mean_ap, 3)\n            eval_results['mAP'] = sum(mean_aps) / len(mean_aps)\n            eval_results.move_to_end('mAP', last=False)\n        elif self.metric == 'recall':\n            gt_bboxes = [gt['bboxes'] for gt in gts]\n            pr_bboxes = [pred[0] for pred in preds]\n            recalls = eval_recalls(\n                gt_bboxes,\n                pr_bboxes,\n                self.proposal_nums,\n                self.iou_thrs,\n                logger=logger,\n                use_legacy_coordinate=True)\n            for i, num in enumerate(self.proposal_nums):\n                for j, iou_thr in enumerate(self.iou_thrs):\n                    eval_results[f'recall@{num}@{iou_thr}'] = recalls[i, j]\n            if recalls.shape[1] > 1:\n                ar = recalls.mean(axis=1)\n                for i, num in enumerate(self.proposal_nums):\n                    eval_results[f'AR@{num}'] = ar[i]\n        return eval_results\n"
  },
  {
    "path": "mmdet/evaluation/metrics/youtube_vis_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport tempfile\nimport warnings\nimport zipfile\nfrom collections import OrderedDict, defaultdict\nfrom typing import Dict, List, Optional, Sequence, Tuple, Union\n\nimport mmengine\nimport numpy as np\nfrom mmengine.dist import (all_gather_object, barrier, broadcast_object_list,\n                           is_main_process)\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.registry import METRICS\nfrom mmdet.structures.mask import encode_mask_results\nfrom ..functional import YTVIS, YTVISeval\nfrom .base_video_metric import BaseVideoMetric, collect_tracking_results\n\n\n@METRICS.register_module()\nclass YouTubeVISMetric(BaseVideoMetric):\n    \"\"\"mAP evaluation metrics for the VIS task.\n\n    Args:\n        metric (str | list[str]): Metrics to be evaluated.\n            Default value is `youtube_vis_ap`.\n        metric_items (List[str], optional): Metric result names to be\n            recorded in the evaluation result. Defaults to None.\n        outfile_prefix (str | None): The prefix of json files. It includes\n            the file path and the prefix of filename, e.g., \"a/b/prefix\".\n            If not specified, a temp file will be created. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonyms metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Default: None\n        format_only (bool): If True, only formatting the results to the\n            official format and not performing evaluation. Defaults to False.\n    \"\"\"\n\n    default_prefix: Optional[str] = 'youtube_vis'\n\n    def __init__(self,\n                 metric: Union[str, List[str]] = 'youtube_vis_ap',\n                 metric_items: Optional[Sequence[str]] = None,\n                 outfile_prefix: Optional[str] = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None,\n                 format_only: bool = False) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        # vis evaluation metrics\n        self.metrics = metric if isinstance(metric, list) else [metric]\n        self.format_only = format_only\n        allowed_metrics = ['youtube_vis_ap']\n        for metric in self.metrics:\n            if metric not in allowed_metrics:\n                raise KeyError(\n                    f\"metric should be 'youtube_vis_ap', but got {metric}.\")\n\n        self.metric_items = metric_items\n        self.outfile_prefix = outfile_prefix\n        self.per_video_res = []\n        self.categories = []\n        self._vis_meta_info = defaultdict(list)  # record video and image infos\n\n    def process_video(self, data_samples):\n\n        video_length = len(data_samples)\n        for frame_id in range(video_length):\n            result = dict()\n            img_data_sample = data_samples[frame_id].to_dict()\n            pred = img_data_sample['pred_track_instances']\n            video_id = img_data_sample['video_id']\n\n            result['img_id'] = img_data_sample['img_id']\n            result['bboxes'] = pred['bboxes'].cpu().numpy()\n            result['scores'] = pred['scores'].cpu().numpy()\n            result['labels'] = pred['labels'].cpu().numpy()\n            result['instances_id'] = pred['instances_id'].cpu().numpy()\n            # encode mask to RLE\n            assert 'masks' in pred, \\\n                'masks must exist in YouTube-VIS metric'\n            result['masks'] = encode_mask_results(\n                pred['masks'].detach().cpu().numpy())\n\n            # parse gt\n            gt = dict()\n            gt['width'] = img_data_sample['ori_shape'][1]\n            gt['height'] = img_data_sample['ori_shape'][0]\n            gt['img_id'] = img_data_sample['img_id']\n            gt['frame_id'] = frame_id\n            gt['video_id'] = video_id\n            gt['video_length'] = video_length\n\n            if 'instances' in img_data_sample:\n                gt['anns'] = img_data_sample['instances']\n            else:\n                gt['anns'] = dict()\n            self.per_video_res.append((result, gt))\n\n        preds, gts = zip(*self.per_video_res)\n        # format the results\n        # we must format gts first to update self._vis_meta_info\n        gt_results = self._format_one_video_gts(gts)\n        pred_results = self._format_one_video_preds(preds)\n        self.per_video_res.clear()\n        # add converted result to the results list\n        self.results.append((pred_results, gt_results))\n\n    def compute_metrics(self, results: List) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (List): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        # split gt and prediction list\n        tmp_pred_results, tmp_gt_results = zip(*results)\n        gt_results = self.format_gts(tmp_gt_results)\n        pred_results = self.format_preds(tmp_pred_results)\n\n        if self.format_only:\n            self.save_pred_results(pred_results)\n            return dict()\n\n        ytvis = YTVIS(gt_results)\n\n        ytvis_dets = ytvis.loadRes(pred_results)\n        vid_ids = ytvis.getVidIds()\n\n        iou_type = metric = 'segm'\n        eval_results = OrderedDict()\n        ytvisEval = YTVISeval(ytvis, ytvis_dets, iou_type)\n        ytvisEval.params.vidIds = vid_ids\n        ytvisEval.evaluate()\n        ytvisEval.accumulate()\n        ytvisEval.summarize()\n\n        coco_metric_names = {\n            'mAP': 0,\n            'mAP_50': 1,\n            'mAP_75': 2,\n            'mAP_s': 3,\n            'mAP_m': 4,\n            'mAP_l': 5,\n            'AR@1': 6,\n            'AR@10': 7,\n            'AR@100': 8,\n            'AR_s@100': 9,\n            'AR_m@100': 10,\n            'AR_l@100': 11\n        }\n        metric_items = self.metric_items\n        if metric_items is not None:\n            for metric_item in metric_items:\n                if metric_item not in coco_metric_names:\n                    raise KeyError(\n                        f'metric item \"{metric_item}\" is not supported')\n\n        if metric_items is None:\n            metric_items = [\n                'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l'\n            ]\n        for metric_item in metric_items:\n            key = f'{metric}_{metric_item}'\n            val = float(\n                f'{ytvisEval.stats[coco_metric_names[metric_item]]:.3f}')\n            eval_results[key] = val\n\n        return eval_results\n\n    def format_gts(self, gts: Tuple[List]) -> dict:\n        \"\"\"Gather all ground-truth from self.results.\"\"\"\n        self.categories = [\n            dict(id=id + 1, name=name)\n            for id, name in enumerate(self.dataset_meta['classes'])\n        ]\n        gt_results = dict(\n            categories=self.categories,\n            videos=self._vis_meta_info['videos'],\n            annotations=[])\n        for gt_result in gts:\n            gt_results['annotations'].extend(gt_result)\n        return gt_results\n\n    def format_preds(self, preds: Tuple[List]) -> List:\n        \"\"\"Gather all predictions from self.results.\"\"\"\n        pred_results = []\n        for pred_result in preds:\n            pred_results.extend(pred_result)\n        return pred_results\n\n    def _format_one_video_preds(self, pred_dicts: Tuple[dict]) -> List:\n        \"\"\"Convert the annotation to the format of YouTube-VIS.\n\n        This operation is to make it easier to use the official eval API.\n\n        Args:\n            pred_dicts (Tuple[dict]): Prediction of the dataset.\n\n        Returns:\n            List: The formatted predictions.\n        \"\"\"\n        # Collate preds scatters (tuple of dict to dict of list)\n        preds = defaultdict(list)\n        for pred in pred_dicts:\n            for key in pred.keys():\n                preds[key].append(pred[key])\n\n        img_infos = self._vis_meta_info['images']\n        vid_infos = self._vis_meta_info['videos']\n        inds = [i for i, _ in enumerate(img_infos) if _['frame_id'] == 0]\n        inds.append(len(img_infos))\n        json_results = []\n        video_id = vid_infos[-1]['id']\n        # collect data for each instances in a video.\n        collect_data = dict()\n        for frame_id, (masks, scores, labels, ids) in enumerate(\n                zip(preds['masks'], preds['scores'], preds['labels'],\n                    preds['instances_id'])):\n\n            assert len(masks) == len(labels)\n            for j, id in enumerate(ids):\n                if id not in collect_data:\n                    collect_data[id] = dict(\n                        category_ids=[], scores=[], segmentations=dict())\n                collect_data[id]['category_ids'].append(labels[j])\n                collect_data[id]['scores'].append(scores[j])\n                if isinstance(masks[j]['counts'], bytes):\n                    masks[j]['counts'] = masks[j]['counts'].decode()\n                collect_data[id]['segmentations'][frame_id] = masks[j]\n\n        # transform the collected data into official format\n        for id, id_data in collect_data.items():\n            output = dict()\n            output['video_id'] = video_id\n            output['score'] = np.array(id_data['scores']).mean().item()\n            # majority voting for sequence category\n            output['category_id'] = np.bincount(\n                np.array(id_data['category_ids'])).argmax().item() + 1\n            output['segmentations'] = []\n            for frame_id in range(inds[-1] - inds[-2]):\n                if frame_id in id_data['segmentations']:\n                    output['segmentations'].append(\n                        id_data['segmentations'][frame_id])\n                else:\n                    output['segmentations'].append(None)\n            json_results.append(output)\n\n        return json_results\n\n    def _format_one_video_gts(self, gt_dicts: Tuple[dict]) -> List:\n        \"\"\"Convert the annotation to the format of YouTube-VIS.\n\n        This operation is to make it easier to use the official eval API.\n\n        Args:\n            gt_dicts (Tuple[dict]): Ground truth of the dataset.\n\n        Returns:\n            list: The formatted gts.\n        \"\"\"\n        video_infos = []\n        image_infos = []\n        instance_infos = defaultdict(list)\n        len_videos = dict()  # mapping from instance_id to video_length\n        vis_anns = []\n\n        # get video infos\n        for gt_dict in gt_dicts:\n            frame_id = gt_dict['frame_id']\n            video_id = gt_dict['video_id']\n            img_id = gt_dict['img_id']\n            image_info = dict(\n                id=img_id,\n                width=gt_dict['width'],\n                height=gt_dict['height'],\n                frame_id=frame_id,\n                file_name='')\n            image_infos.append(image_info)\n            if frame_id == 0:\n                video_info = dict(\n                    id=video_id,\n                    width=gt_dict['width'],\n                    height=gt_dict['height'],\n                    file_name='')\n                video_infos.append(video_info)\n\n            for ann in gt_dict['anns']:\n                label = ann['bbox_label']\n                bbox = ann['bbox']\n                instance_id = ann['instance_id']\n                # update video length\n                len_videos[instance_id] = gt_dict['video_length']\n                coco_bbox = [\n                    bbox[0],\n                    bbox[1],\n                    bbox[2] - bbox[0],\n                    bbox[3] - bbox[1],\n                ]\n\n                annotation = dict(\n                    video_id=video_id,\n                    frame_id=frame_id,\n                    bbox=coco_bbox,\n                    instance_id=instance_id,\n                    iscrowd=ann.get('ignore_flag', 0),\n                    category_id=int(label) + 1,\n                    area=coco_bbox[2] * coco_bbox[3])\n                if ann.get('mask', None):\n                    mask = ann['mask']\n                    # area = mask_util.area(mask)\n                    if isinstance(mask, dict) and isinstance(\n                            mask['counts'], bytes):\n                        mask['counts'] = mask['counts'].decode()\n                    annotation['segmentation'] = mask\n\n                instance_infos[instance_id].append(annotation)\n\n        # update vis meta info\n        self._vis_meta_info['images'].extend(image_infos)\n        self._vis_meta_info['videos'].extend(video_infos)\n\n        for instance_id, ann_infos in instance_infos.items():\n            cur_video_len = len_videos[instance_id]\n            segm = [None] * cur_video_len\n            bbox = [None] * cur_video_len\n            area = [None] * cur_video_len\n            # In the official format, no instances are represented by\n            # 'None', however, only images with instances are recorded\n            # in the current annotations, so we need to use 'None' to\n            # initialize these lists.\n            for ann_info in ann_infos:\n                frame_id = ann_info['frame_id']\n                segm[frame_id] = ann_info['segmentation']\n                bbox[frame_id] = ann_info['bbox']\n                area[frame_id] = ann_info['area']\n            instance = dict(\n                category_id=ann_infos[0]['category_id'],\n                segmentations=segm,\n                bboxes=bbox,\n                video_id=ann_infos[0]['video_id'],\n                areas=area,\n                id=instance_id,\n                iscrowd=ann_infos[0]['iscrowd'])\n            vis_anns.append(instance)\n        return vis_anns\n\n    def save_pred_results(self, pred_results: List) -> None:\n        \"\"\"Save the results to a zip file (standard format for YouTube-VIS\n        Challenge).\n\n        Args:\n            pred_results (list): Testing results of the\n                dataset.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n        if self.outfile_prefix is None:\n            tmp_dir = tempfile.TemporaryDirectory()\n            outfile_prefix = osp.join(tmp_dir.name, 'results')\n        else:\n            outfile_prefix = self.outfile_prefix\n        mmengine.dump(pred_results, f'{outfile_prefix}.json')\n        # zip the json file in order to submit to the test server.\n        zip_file_name = f'{outfile_prefix}.submission_file.zip'\n        zf = zipfile.ZipFile(zip_file_name, 'w', zipfile.ZIP_DEFLATED)\n        logger.info(f\"zip the 'results.json' into '{zip_file_name}', \"\n                    'please submmit the zip file to the test server')\n        zf.write(f'{outfile_prefix}.json', 'results.json')\n        zf.close()\n\n    def evaluate(self, size: int) -> dict:\n        \"\"\"Evaluate the model performance of the whole dataset after processing\n        all batches.\n\n        Args:\n            size (int): Length of the entire validation dataset.\n\n        Returns:\n            dict: Evaluation metrics dict on the val dataset. The keys are the\n            names of the metrics, and the values are corresponding results.\n        \"\"\"\n        # wait for all processes to complete prediction.\n        barrier()\n\n        if len(self.results) == 0:\n            warnings.warn(\n                f'{self.__class__.__name__} got empty `self.results`. Please '\n                'ensure that the processed results are properly added into '\n                '`self.results` in `process` method.')\n\n        results = collect_tracking_results(self.results, self.collect_device)\n\n        # gather seq_info\n        gathered_seq_info = all_gather_object(self._vis_meta_info['videos'])\n        all_seq_info = []\n        for _seq_info in gathered_seq_info:\n            all_seq_info.extend(_seq_info)\n        # update self._vis_meta_info\n        self._vis_meta_info = dict(videos=all_seq_info)\n\n        if is_main_process():\n            _metrics = self.compute_metrics(results)  # type: ignore\n            # Add prefix to metric names\n            if self.prefix:\n                _metrics = {\n                    '/'.join((self.prefix, k)): v\n                    for k, v in _metrics.items()\n                }\n            metrics = [_metrics]\n        else:\n            metrics = [None]  # type: ignore\n\n        broadcast_object_list(metrics)\n\n        # reset the results list\n        self.results.clear()\n        # reset the vis_meta_info\n        self._vis_meta_info.clear()\n        return metrics[0]\n"
  },
  {
    "path": "mmdet/models/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .backbones import *  # noqa: F401,F403\nfrom .data_preprocessors import *  # noqa: F401,F403\nfrom .dense_heads import *  # noqa: F401,F403\nfrom .detectors import *  # noqa: F401,F403\nfrom .language_models import *  # noqa: F401,F403\nfrom .layers import *  # noqa: F401,F403\nfrom .losses import *  # noqa: F401,F403\nfrom .mot import *  # noqa: F401,F403\nfrom .necks import *  # noqa: F401,F403\nfrom .reid import *  # noqa: F401,F403\nfrom .roi_heads import *  # noqa: F401,F403\nfrom .seg_heads import *  # noqa: F401,F403\nfrom .task_modules import *  # noqa: F401,F403\nfrom .test_time_augs import *  # noqa: F401,F403\nfrom .trackers import *  # noqa: F401,F403\nfrom .tracking_heads import *  # noqa: F401,F403\nfrom .vis import *  # noqa: F401,F403\n"
  },
  {
    "path": "mmdet/models/backbones/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .csp_darknet import CSPDarknet\nfrom .cspnext import CSPNeXt\nfrom .darknet import Darknet\nfrom .detectors_resnet import DetectoRS_ResNet\nfrom .detectors_resnext import DetectoRS_ResNeXt\nfrom .efficientnet import EfficientNet\nfrom .hourglass import HourglassNet\nfrom .hrnet import HRNet\nfrom .mobilenet_v2 import MobileNetV2\nfrom .pvt import PyramidVisionTransformer, PyramidVisionTransformerV2\nfrom .regnet import RegNet\nfrom .res2net import Res2Net\nfrom .resnest import ResNeSt\nfrom .resnet import ResNet, ResNetV1d\nfrom .resnext import ResNeXt\nfrom .ssd_vgg import SSDVGG\nfrom .swin import SwinTransformer\nfrom .trident_resnet import TridentResNet\n\n__all__ = [\n    'RegNet', 'ResNet', 'ResNetV1d', 'ResNeXt', 'SSDVGG', 'HRNet',\n    'MobileNetV2', 'Res2Net', 'HourglassNet', 'DetectoRS_ResNet',\n    'DetectoRS_ResNeXt', 'Darknet', 'ResNeSt', 'TridentResNet', 'CSPDarknet',\n    'SwinTransformer', 'PyramidVisionTransformer',\n    'PyramidVisionTransformerV2', 'EfficientNet', 'CSPNeXt'\n]\n"
  },
  {
    "path": "mmdet/models/backbones/csp_darknet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmengine.model import BaseModule\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.registry import MODELS\nfrom ..layers import CSPLayer\n\n\nclass Focus(nn.Module):\n    \"\"\"Focus width and height information into channel space.\n\n    Args:\n        in_channels (int): The input channels of this Module.\n        out_channels (int): The output channels of this Module.\n        kernel_size (int): The kernel size of the convolution. Default: 1\n        stride (int): The stride of the convolution. Default: 1\n        conv_cfg (dict): Config dict for convolution layer. Default: None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='BN', momentum=0.03, eps=0.001).\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='Swish').\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 kernel_size=1,\n                 stride=1,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', momentum=0.03, eps=0.001),\n                 act_cfg=dict(type='Swish')):\n        super().__init__()\n        self.conv = ConvModule(\n            in_channels * 4,\n            out_channels,\n            kernel_size,\n            stride,\n            padding=(kernel_size - 1) // 2,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n\n    def forward(self, x):\n        # shape of x (b,c,w,h) -> y(b,4c,w/2,h/2)\n        patch_top_left = x[..., ::2, ::2]\n        patch_top_right = x[..., ::2, 1::2]\n        patch_bot_left = x[..., 1::2, ::2]\n        patch_bot_right = x[..., 1::2, 1::2]\n        x = torch.cat(\n            (\n                patch_top_left,\n                patch_bot_left,\n                patch_top_right,\n                patch_bot_right,\n            ),\n            dim=1,\n        )\n        return self.conv(x)\n\n\nclass SPPBottleneck(BaseModule):\n    \"\"\"Spatial pyramid pooling layer used in YOLOv3-SPP.\n\n    Args:\n        in_channels (int): The input channels of this Module.\n        out_channels (int): The output channels of this Module.\n        kernel_sizes (tuple[int]): Sequential of kernel sizes of pooling\n            layers. Default: (5, 9, 13).\n        conv_cfg (dict): Config dict for convolution layer. Default: None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='BN').\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='Swish').\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 kernel_sizes=(5, 9, 13),\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', momentum=0.03, eps=0.001),\n                 act_cfg=dict(type='Swish'),\n                 init_cfg=None):\n        super().__init__(init_cfg)\n        mid_channels = in_channels // 2\n        self.conv1 = ConvModule(\n            in_channels,\n            mid_channels,\n            1,\n            stride=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.poolings = nn.ModuleList([\n            nn.MaxPool2d(kernel_size=ks, stride=1, padding=ks // 2)\n            for ks in kernel_sizes\n        ])\n        conv2_channels = mid_channels * (len(kernel_sizes) + 1)\n        self.conv2 = ConvModule(\n            conv2_channels,\n            out_channels,\n            1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n\n    def forward(self, x):\n        x = self.conv1(x)\n        with torch.cuda.amp.autocast(enabled=False):\n            x = torch.cat(\n                [x] + [pooling(x) for pooling in self.poolings], dim=1)\n        x = self.conv2(x)\n        return x\n\n\n@MODELS.register_module()\nclass CSPDarknet(BaseModule):\n    \"\"\"CSP-Darknet backbone used in YOLOv5 and YOLOX.\n\n    Args:\n        arch (str): Architecture of CSP-Darknet, from {P5, P6}.\n            Default: P5.\n        deepen_factor (float): Depth multiplier, multiply number of\n            blocks in CSP layer by this amount. Default: 1.0.\n        widen_factor (float): Width multiplier, multiply number of\n            channels in each layer by this amount. Default: 1.0.\n        out_indices (Sequence[int]): Output from which stages.\n            Default: (2, 3, 4).\n        frozen_stages (int): Stages to be frozen (stop grad and set eval\n            mode). -1 means not freezing any parameters. Default: -1.\n        use_depthwise (bool): Whether to use depthwise separable convolution.\n            Default: False.\n        arch_ovewrite(list): Overwrite default arch settings. Default: None.\n        spp_kernal_sizes: (tuple[int]): Sequential of kernel sizes of SPP\n            layers. Default: (5, 9, 13).\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n            Default: dict(type='BN', requires_grad=True).\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='LeakyReLU', negative_slope=0.1).\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None.\n    Example:\n        >>> from mmdet.models import CSPDarknet\n        >>> import torch\n        >>> self = CSPDarknet(depth=53)\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 3, 416, 416)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_out in level_outputs:\n        ...     print(tuple(level_out.shape))\n        ...\n        (1, 256, 52, 52)\n        (1, 512, 26, 26)\n        (1, 1024, 13, 13)\n    \"\"\"\n    # From left to right:\n    # in_channels, out_channels, num_blocks, add_identity, use_spp\n    arch_settings = {\n        'P5': [[64, 128, 3, True, False], [128, 256, 9, True, False],\n               [256, 512, 9, True, False], [512, 1024, 3, False, True]],\n        'P6': [[64, 128, 3, True, False], [128, 256, 9, True, False],\n               [256, 512, 9, True, False], [512, 768, 3, True, False],\n               [768, 1024, 3, False, True]]\n    }\n\n    def __init__(self,\n                 arch='P5',\n                 deepen_factor=1.0,\n                 widen_factor=1.0,\n                 out_indices=(2, 3, 4),\n                 frozen_stages=-1,\n                 use_depthwise=False,\n                 arch_ovewrite=None,\n                 spp_kernal_sizes=(5, 9, 13),\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', momentum=0.03, eps=0.001),\n                 act_cfg=dict(type='Swish'),\n                 norm_eval=False,\n                 init_cfg=dict(\n                     type='Kaiming',\n                     layer='Conv2d',\n                     a=math.sqrt(5),\n                     distribution='uniform',\n                     mode='fan_in',\n                     nonlinearity='leaky_relu')):\n        super().__init__(init_cfg)\n        arch_setting = self.arch_settings[arch]\n        if arch_ovewrite:\n            arch_setting = arch_ovewrite\n        assert set(out_indices).issubset(\n            i for i in range(len(arch_setting) + 1))\n        if frozen_stages not in range(-1, len(arch_setting) + 1):\n            raise ValueError('frozen_stages must be in range(-1, '\n                             'len(arch_setting) + 1). But received '\n                             f'{frozen_stages}')\n\n        self.out_indices = out_indices\n        self.frozen_stages = frozen_stages\n        self.use_depthwise = use_depthwise\n        self.norm_eval = norm_eval\n        conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule\n\n        self.stem = Focus(\n            3,\n            int(arch_setting[0][0] * widen_factor),\n            kernel_size=3,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.layers = ['stem']\n\n        for i, (in_channels, out_channels, num_blocks, add_identity,\n                use_spp) in enumerate(arch_setting):\n            in_channels = int(in_channels * widen_factor)\n            out_channels = int(out_channels * widen_factor)\n            num_blocks = max(round(num_blocks * deepen_factor), 1)\n            stage = []\n            conv_layer = conv(\n                in_channels,\n                out_channels,\n                3,\n                stride=2,\n                padding=1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n            stage.append(conv_layer)\n            if use_spp:\n                spp = SPPBottleneck(\n                    out_channels,\n                    out_channels,\n                    kernel_sizes=spp_kernal_sizes,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg)\n                stage.append(spp)\n            csp_layer = CSPLayer(\n                out_channels,\n                out_channels,\n                num_blocks=num_blocks,\n                add_identity=add_identity,\n                use_depthwise=use_depthwise,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n            stage.append(csp_layer)\n            self.add_module(f'stage{i + 1}', nn.Sequential(*stage))\n            self.layers.append(f'stage{i + 1}')\n\n    def _freeze_stages(self):\n        if self.frozen_stages >= 0:\n            for i in range(self.frozen_stages + 1):\n                m = getattr(self, self.layers[i])\n                m.eval()\n                for param in m.parameters():\n                    param.requires_grad = False\n\n    def train(self, mode=True):\n        super(CSPDarknet, self).train(mode)\n        self._freeze_stages()\n        if mode and self.norm_eval:\n            for m in self.modules():\n                if isinstance(m, _BatchNorm):\n                    m.eval()\n\n    def forward(self, x):\n        outs = []\n        for i, layer_name in enumerate(self.layers):\n            layer = getattr(self, layer_name)\n            x = layer(x)\n            if i in self.out_indices:\n                outs.append(x)\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/backbones/cspnext.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Sequence, Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom ..layers import CSPLayer\nfrom .csp_darknet import SPPBottleneck\n\n\n@MODELS.register_module()\nclass CSPNeXt(BaseModule):\n    \"\"\"CSPNeXt backbone used in RTMDet.\n\n    Args:\n        arch (str): Architecture of CSPNeXt, from {P5, P6}.\n            Defaults to P5.\n        expand_ratio (float): Ratio to adjust the number of channels of the\n            hidden layer. Defaults to 0.5.\n        deepen_factor (float): Depth multiplier, multiply number of\n            blocks in CSP layer by this amount. Defaults to 1.0.\n        widen_factor (float): Width multiplier, multiply number of\n            channels in each layer by this amount. Defaults to 1.0.\n        out_indices (Sequence[int]): Output from which stages.\n            Defaults to (2, 3, 4).\n        frozen_stages (int): Stages to be frozen (stop grad and set eval\n            mode). -1 means not freezing any parameters. Defaults to -1.\n        use_depthwise (bool): Whether to use depthwise separable convolution.\n            Defaults to False.\n        arch_ovewrite (list): Overwrite default arch settings.\n            Defaults to None.\n        spp_kernel_sizes: (tuple[int]): Sequential of kernel sizes of SPP\n            layers. Defaults to (5, 9, 13).\n        channel_attention (bool): Whether to add channel attention in each\n            stage. Defaults to True.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): Dictionary to construct and\n            config norm layer. Defaults to dict(type='BN', requires_grad=True).\n        act_cfg (:obj:`ConfigDict` or dict): Config dict for activation layer.\n            Defaults to dict(type='SiLU').\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only.\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`]): Initialization config dict.\n    \"\"\"\n    # From left to right:\n    # in_channels, out_channels, num_blocks, add_identity, use_spp\n    arch_settings = {\n        'P5': [[64, 128, 3, True, False], [128, 256, 6, True, False],\n               [256, 512, 6, True, False], [512, 1024, 3, False, True]],\n        'P6': [[64, 128, 3, True, False], [128, 256, 6, True, False],\n               [256, 512, 6, True, False], [512, 768, 3, True, False],\n               [768, 1024, 3, False, True]]\n    }\n\n    def __init__(\n        self,\n        arch: str = 'P5',\n        deepen_factor: float = 1.0,\n        widen_factor: float = 1.0,\n        out_indices: Sequence[int] = (2, 3, 4),\n        frozen_stages: int = -1,\n        use_depthwise: bool = False,\n        expand_ratio: float = 0.5,\n        arch_ovewrite: dict = None,\n        spp_kernel_sizes: Sequence[int] = (5, 9, 13),\n        channel_attention: bool = True,\n        conv_cfg: OptConfigType = None,\n        norm_cfg: ConfigType = dict(type='BN', momentum=0.03, eps=0.001),\n        act_cfg: ConfigType = dict(type='SiLU'),\n        norm_eval: bool = False,\n        init_cfg: OptMultiConfig = dict(\n            type='Kaiming',\n            layer='Conv2d',\n            a=math.sqrt(5),\n            distribution='uniform',\n            mode='fan_in',\n            nonlinearity='leaky_relu')\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        arch_setting = self.arch_settings[arch]\n        if arch_ovewrite:\n            arch_setting = arch_ovewrite\n        assert set(out_indices).issubset(\n            i for i in range(len(arch_setting) + 1))\n        if frozen_stages not in range(-1, len(arch_setting) + 1):\n            raise ValueError('frozen_stages must be in range(-1, '\n                             'len(arch_setting) + 1). But received '\n                             f'{frozen_stages}')\n\n        self.out_indices = out_indices\n        self.frozen_stages = frozen_stages\n        self.use_depthwise = use_depthwise\n        self.norm_eval = norm_eval\n        conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule\n        self.stem = nn.Sequential(\n            ConvModule(\n                3,\n                int(arch_setting[0][0] * widen_factor // 2),\n                3,\n                padding=1,\n                stride=2,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg),\n            ConvModule(\n                int(arch_setting[0][0] * widen_factor // 2),\n                int(arch_setting[0][0] * widen_factor // 2),\n                3,\n                padding=1,\n                stride=1,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg),\n            ConvModule(\n                int(arch_setting[0][0] * widen_factor // 2),\n                int(arch_setting[0][0] * widen_factor),\n                3,\n                padding=1,\n                stride=1,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg))\n        self.layers = ['stem']\n\n        for i, (in_channels, out_channels, num_blocks, add_identity,\n                use_spp) in enumerate(arch_setting):\n            in_channels = int(in_channels * widen_factor)\n            out_channels = int(out_channels * widen_factor)\n            num_blocks = max(round(num_blocks * deepen_factor), 1)\n            stage = []\n            conv_layer = conv(\n                in_channels,\n                out_channels,\n                3,\n                stride=2,\n                padding=1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n            stage.append(conv_layer)\n            if use_spp:\n                spp = SPPBottleneck(\n                    out_channels,\n                    out_channels,\n                    kernel_sizes=spp_kernel_sizes,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg)\n                stage.append(spp)\n            csp_layer = CSPLayer(\n                out_channels,\n                out_channels,\n                num_blocks=num_blocks,\n                add_identity=add_identity,\n                use_depthwise=use_depthwise,\n                use_cspnext_block=True,\n                expand_ratio=expand_ratio,\n                channel_attention=channel_attention,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n            stage.append(csp_layer)\n            self.add_module(f'stage{i + 1}', nn.Sequential(*stage))\n            self.layers.append(f'stage{i + 1}')\n\n    def _freeze_stages(self) -> None:\n        if self.frozen_stages >= 0:\n            for i in range(self.frozen_stages + 1):\n                m = getattr(self, self.layers[i])\n                m.eval()\n                for param in m.parameters():\n                    param.requires_grad = False\n\n    def train(self, mode=True) -> None:\n        super().train(mode)\n        self._freeze_stages()\n        if mode and self.norm_eval:\n            for m in self.modules():\n                if isinstance(m, _BatchNorm):\n                    m.eval()\n\n    def forward(self, x: Tuple[Tensor, ...]) -> Tuple[Tensor, ...]:\n        outs = []\n        for i, layer_name in enumerate(self.layers):\n            layer = getattr(self, layer_name)\n            x = layer(x)\n            if i in self.out_indices:\n                outs.append(x)\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/backbones/darknet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Copyright (c) 2019 Western Digital Corporation or its affiliates.\n\nimport warnings\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.registry import MODELS\n\n\nclass ResBlock(BaseModule):\n    \"\"\"The basic residual block used in Darknet. Each ResBlock consists of two\n    ConvModules and the input is added to the final output. Each ConvModule is\n    composed of Conv, BN, and LeakyReLU. In YoloV3 paper, the first convLayer\n    has half of the number of the filters as much as the second convLayer. The\n    first convLayer has filter size of 1x1 and the second one has the filter\n    size of 3x3.\n\n    Args:\n        in_channels (int): The input channels. Must be even.\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n            Default: dict(type='BN', requires_grad=True)\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='LeakyReLU', negative_slope=0.1).\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', requires_grad=True),\n                 act_cfg=dict(type='LeakyReLU', negative_slope=0.1),\n                 init_cfg=None):\n        super(ResBlock, self).__init__(init_cfg)\n        assert in_channels % 2 == 0  # ensure the in_channels is even\n        half_in_channels = in_channels // 2\n\n        # shortcut\n        cfg = dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg, act_cfg=act_cfg)\n\n        self.conv1 = ConvModule(in_channels, half_in_channels, 1, **cfg)\n        self.conv2 = ConvModule(\n            half_in_channels, in_channels, 3, padding=1, **cfg)\n\n    def forward(self, x):\n        residual = x\n        out = self.conv1(x)\n        out = self.conv2(out)\n        out = out + residual\n\n        return out\n\n\n@MODELS.register_module()\nclass Darknet(BaseModule):\n    \"\"\"Darknet backbone.\n\n    Args:\n        depth (int): Depth of Darknet. Currently only support 53.\n        out_indices (Sequence[int]): Output from which stages.\n        frozen_stages (int): Stages to be frozen (stop grad and set eval mode).\n            -1 means not freezing any parameters. Default: -1.\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n            Default: dict(type='BN', requires_grad=True)\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='LeakyReLU', negative_slope=0.1).\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only.\n        pretrained (str, optional): model pretrained path. Default: None\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n\n    Example:\n        >>> from mmdet.models import Darknet\n        >>> import torch\n        >>> self = Darknet(depth=53)\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 3, 416, 416)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_out in level_outputs:\n        ...     print(tuple(level_out.shape))\n        ...\n        (1, 256, 52, 52)\n        (1, 512, 26, 26)\n        (1, 1024, 13, 13)\n    \"\"\"\n\n    # Dict(depth: (layers, channels))\n    arch_settings = {\n        53: ((1, 2, 8, 8, 4), ((32, 64), (64, 128), (128, 256), (256, 512),\n                               (512, 1024)))\n    }\n\n    def __init__(self,\n                 depth=53,\n                 out_indices=(3, 4, 5),\n                 frozen_stages=-1,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', requires_grad=True),\n                 act_cfg=dict(type='LeakyReLU', negative_slope=0.1),\n                 norm_eval=True,\n                 pretrained=None,\n                 init_cfg=None):\n        super(Darknet, self).__init__(init_cfg)\n        if depth not in self.arch_settings:\n            raise KeyError(f'invalid depth {depth} for darknet')\n\n        self.depth = depth\n        self.out_indices = out_indices\n        self.frozen_stages = frozen_stages\n        self.layers, self.channels = self.arch_settings[depth]\n\n        cfg = dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg, act_cfg=act_cfg)\n\n        self.conv1 = ConvModule(3, 32, 3, padding=1, **cfg)\n\n        self.cr_blocks = ['conv1']\n        for i, n_layers in enumerate(self.layers):\n            layer_name = f'conv_res_block{i + 1}'\n            in_c, out_c = self.channels[i]\n            self.add_module(\n                layer_name,\n                self.make_conv_res_block(in_c, out_c, n_layers, **cfg))\n            self.cr_blocks.append(layer_name)\n\n        self.norm_eval = norm_eval\n\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            if init_cfg is None:\n                self.init_cfg = [\n                    dict(type='Kaiming', layer='Conv2d'),\n                    dict(\n                        type='Constant',\n                        val=1,\n                        layer=['_BatchNorm', 'GroupNorm'])\n                ]\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n    def forward(self, x):\n        outs = []\n        for i, layer_name in enumerate(self.cr_blocks):\n            cr_block = getattr(self, layer_name)\n            x = cr_block(x)\n            if i in self.out_indices:\n                outs.append(x)\n\n        return tuple(outs)\n\n    def _freeze_stages(self):\n        if self.frozen_stages >= 0:\n            for i in range(self.frozen_stages):\n                m = getattr(self, self.cr_blocks[i])\n                m.eval()\n                for param in m.parameters():\n                    param.requires_grad = False\n\n    def train(self, mode=True):\n        super(Darknet, self).train(mode)\n        self._freeze_stages()\n        if mode and self.norm_eval:\n            for m in self.modules():\n                if isinstance(m, _BatchNorm):\n                    m.eval()\n\n    @staticmethod\n    def make_conv_res_block(in_channels,\n                            out_channels,\n                            res_repeat,\n                            conv_cfg=None,\n                            norm_cfg=dict(type='BN', requires_grad=True),\n                            act_cfg=dict(type='LeakyReLU',\n                                         negative_slope=0.1)):\n        \"\"\"In Darknet backbone, ConvLayer is usually followed by ResBlock. This\n        function will make that. The Conv layers always have 3x3 filters with\n        stride=2. The number of the filters in Conv layer is the same as the\n        out channels of the ResBlock.\n\n        Args:\n            in_channels (int): The number of input channels.\n            out_channels (int): The number of output channels.\n            res_repeat (int): The number of ResBlocks.\n            conv_cfg (dict): Config dict for convolution layer. Default: None.\n            norm_cfg (dict): Dictionary to construct and config norm layer.\n                Default: dict(type='BN', requires_grad=True)\n            act_cfg (dict): Config dict for activation layer.\n                Default: dict(type='LeakyReLU', negative_slope=0.1).\n        \"\"\"\n\n        cfg = dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg, act_cfg=act_cfg)\n\n        model = nn.Sequential()\n        model.add_module(\n            'conv',\n            ConvModule(\n                in_channels, out_channels, 3, stride=2, padding=1, **cfg))\n        for idx in range(res_repeat):\n            model.add_module('res{}'.format(idx),\n                             ResBlock(out_channels, **cfg))\n        return model\n"
  },
  {
    "path": "mmdet/models/backbones/detectors_resnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\nfrom mmengine.logging import MMLogger\nfrom mmengine.model import Sequential, constant_init, kaiming_init\nfrom mmengine.runner.checkpoint import load_checkpoint\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.registry import MODELS\nfrom .resnet import BasicBlock\nfrom .resnet import Bottleneck as _Bottleneck\nfrom .resnet import ResNet\n\n\nclass Bottleneck(_Bottleneck):\n    r\"\"\"Bottleneck for the ResNet backbone in `DetectoRS\n    <https://arxiv.org/pdf/2006.02334.pdf>`_.\n\n    This bottleneck allows the users to specify whether to use\n    SAC (Switchable Atrous Convolution) and RFP (Recursive Feature Pyramid).\n\n    Args:\n         inplanes (int): The number of input channels.\n         planes (int): The number of output channels before expansion.\n         rfp_inplanes (int, optional): The number of channels from RFP.\n             Default: None. If specified, an additional conv layer will be\n             added for ``rfp_feat``. Otherwise, the structure is the same as\n             base class.\n         sac (dict, optional): Dictionary to construct SAC. Default: None.\n         init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n    expansion = 4\n\n    def __init__(self,\n                 inplanes,\n                 planes,\n                 rfp_inplanes=None,\n                 sac=None,\n                 init_cfg=None,\n                 **kwargs):\n        super(Bottleneck, self).__init__(\n            inplanes, planes, init_cfg=init_cfg, **kwargs)\n\n        assert sac is None or isinstance(sac, dict)\n        self.sac = sac\n        self.with_sac = sac is not None\n        if self.with_sac:\n            self.conv2 = build_conv_layer(\n                self.sac,\n                planes,\n                planes,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=self.dilation,\n                dilation=self.dilation,\n                bias=False)\n\n        self.rfp_inplanes = rfp_inplanes\n        if self.rfp_inplanes:\n            self.rfp_conv = build_conv_layer(\n                None,\n                self.rfp_inplanes,\n                planes * self.expansion,\n                1,\n                stride=1,\n                bias=True)\n            if init_cfg is None:\n                self.init_cfg = dict(\n                    type='Constant', val=0, override=dict(name='rfp_conv'))\n\n    def rfp_forward(self, x, rfp_feat):\n        \"\"\"The forward function that also takes the RFP features as input.\"\"\"\n\n        def _inner_forward(x):\n            identity = x\n\n            out = self.conv1(x)\n            out = self.norm1(out)\n            out = self.relu(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv1_plugin_names)\n\n            out = self.conv2(out)\n            out = self.norm2(out)\n            out = self.relu(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv2_plugin_names)\n\n            out = self.conv3(out)\n            out = self.norm3(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv3_plugin_names)\n\n            if self.downsample is not None:\n                identity = self.downsample(x)\n\n            out += identity\n\n            return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        if self.rfp_inplanes:\n            rfp_feat = self.rfp_conv(rfp_feat)\n            out = out + rfp_feat\n\n        out = self.relu(out)\n\n        return out\n\n\nclass ResLayer(Sequential):\n    \"\"\"ResLayer to build ResNet style backbone for RPF in detectoRS.\n\n    The difference between this module and base class is that we pass\n    ``rfp_inplanes`` to the first block.\n\n    Args:\n        block (nn.Module): block used to build ResLayer.\n        inplanes (int): inplanes of block.\n        planes (int): planes of block.\n        num_blocks (int): number of blocks.\n        stride (int): stride of the first block. Default: 1\n        avg_down (bool): Use AvgPool instead of stride conv when\n            downsampling in the bottleneck. Default: False\n        conv_cfg (dict): dictionary to construct and config conv layer.\n            Default: None\n        norm_cfg (dict): dictionary to construct and config norm layer.\n            Default: dict(type='BN')\n        downsample_first (bool): Downsample at the first block or last block.\n            False for Hourglass, True for ResNet. Default: True\n        rfp_inplanes (int, optional): The number of channels from RFP.\n            Default: None. If specified, an additional conv layer will be\n            added for ``rfp_feat``. Otherwise, the structure is the same as\n            base class.\n    \"\"\"\n\n    def __init__(self,\n                 block,\n                 inplanes,\n                 planes,\n                 num_blocks,\n                 stride=1,\n                 avg_down=False,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 downsample_first=True,\n                 rfp_inplanes=None,\n                 **kwargs):\n        self.block = block\n        assert downsample_first, f'downsample_first={downsample_first} is ' \\\n                                 'not supported in DetectoRS'\n\n        downsample = None\n        if stride != 1 or inplanes != planes * block.expansion:\n            downsample = []\n            conv_stride = stride\n            if avg_down and stride != 1:\n                conv_stride = 1\n                downsample.append(\n                    nn.AvgPool2d(\n                        kernel_size=stride,\n                        stride=stride,\n                        ceil_mode=True,\n                        count_include_pad=False))\n            downsample.extend([\n                build_conv_layer(\n                    conv_cfg,\n                    inplanes,\n                    planes * block.expansion,\n                    kernel_size=1,\n                    stride=conv_stride,\n                    bias=False),\n                build_norm_layer(norm_cfg, planes * block.expansion)[1]\n            ])\n            downsample = nn.Sequential(*downsample)\n\n        layers = []\n        layers.append(\n            block(\n                inplanes=inplanes,\n                planes=planes,\n                stride=stride,\n                downsample=downsample,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                rfp_inplanes=rfp_inplanes,\n                **kwargs))\n        inplanes = planes * block.expansion\n        for _ in range(1, num_blocks):\n            layers.append(\n                block(\n                    inplanes=inplanes,\n                    planes=planes,\n                    stride=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    **kwargs))\n\n        super(ResLayer, self).__init__(*layers)\n\n\n@MODELS.register_module()\nclass DetectoRS_ResNet(ResNet):\n    \"\"\"ResNet backbone for DetectoRS.\n\n    Args:\n        sac (dict, optional): Dictionary to construct SAC (Switchable Atrous\n            Convolution). Default: None.\n        stage_with_sac (list): Which stage to use sac. Default: (False, False,\n            False, False).\n        rfp_inplanes (int, optional): The number of channels from RFP.\n            Default: None. If specified, an additional conv layer will be\n            added for ``rfp_feat``. Otherwise, the structure is the same as\n            base class.\n        output_img (bool): If ``True``, the input image will be inserted into\n            the starting position of output. Default: False.\n    \"\"\"\n\n    arch_settings = {\n        50: (Bottleneck, (3, 4, 6, 3)),\n        101: (Bottleneck, (3, 4, 23, 3)),\n        152: (Bottleneck, (3, 8, 36, 3))\n    }\n\n    def __init__(self,\n                 sac=None,\n                 stage_with_sac=(False, False, False, False),\n                 rfp_inplanes=None,\n                 output_img=False,\n                 pretrained=None,\n                 init_cfg=None,\n                 **kwargs):\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        self.pretrained = pretrained\n        if init_cfg is not None:\n            assert isinstance(init_cfg, dict), \\\n                f'init_cfg must be a dict, but got {type(init_cfg)}'\n            if 'type' in init_cfg:\n                assert init_cfg.get('type') == 'Pretrained', \\\n                    'Only can initialize module by loading a pretrained model'\n            else:\n                raise KeyError('`init_cfg` must contain the key \"type\"')\n            self.pretrained = init_cfg.get('checkpoint')\n        self.sac = sac\n        self.stage_with_sac = stage_with_sac\n        self.rfp_inplanes = rfp_inplanes\n        self.output_img = output_img\n        super(DetectoRS_ResNet, self).__init__(**kwargs)\n\n        self.inplanes = self.stem_channels\n        self.res_layers = []\n        for i, num_blocks in enumerate(self.stage_blocks):\n            stride = self.strides[i]\n            dilation = self.dilations[i]\n            dcn = self.dcn if self.stage_with_dcn[i] else None\n            sac = self.sac if self.stage_with_sac[i] else None\n            if self.plugins is not None:\n                stage_plugins = self.make_stage_plugins(self.plugins, i)\n            else:\n                stage_plugins = None\n            planes = self.base_channels * 2**i\n            res_layer = self.make_res_layer(\n                block=self.block,\n                inplanes=self.inplanes,\n                planes=planes,\n                num_blocks=num_blocks,\n                stride=stride,\n                dilation=dilation,\n                style=self.style,\n                avg_down=self.avg_down,\n                with_cp=self.with_cp,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg,\n                dcn=dcn,\n                sac=sac,\n                rfp_inplanes=rfp_inplanes if i > 0 else None,\n                plugins=stage_plugins)\n            self.inplanes = planes * self.block.expansion\n            layer_name = f'layer{i + 1}'\n            self.add_module(layer_name, res_layer)\n            self.res_layers.append(layer_name)\n\n        self._freeze_stages()\n\n    # In order to be properly initialized by RFP\n    def init_weights(self):\n        # Calling this method will cause parameter initialization exception\n        # super(DetectoRS_ResNet, self).init_weights()\n\n        if isinstance(self.pretrained, str):\n            logger = MMLogger.get_current_instance()\n            load_checkpoint(self, self.pretrained, strict=False, logger=logger)\n        elif self.pretrained is None:\n            for m in self.modules():\n                if isinstance(m, nn.Conv2d):\n                    kaiming_init(m)\n                elif isinstance(m, (_BatchNorm, nn.GroupNorm)):\n                    constant_init(m, 1)\n\n            if self.dcn is not None:\n                for m in self.modules():\n                    if isinstance(m, Bottleneck) and hasattr(\n                            m.conv2, 'conv_offset'):\n                        constant_init(m.conv2.conv_offset, 0)\n\n            if self.zero_init_residual:\n                for m in self.modules():\n                    if isinstance(m, Bottleneck):\n                        constant_init(m.norm3, 0)\n                    elif isinstance(m, BasicBlock):\n                        constant_init(m.norm2, 0)\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n    def make_res_layer(self, **kwargs):\n        \"\"\"Pack all blocks in a stage into a ``ResLayer`` for DetectoRS.\"\"\"\n        return ResLayer(**kwargs)\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        outs = list(super(DetectoRS_ResNet, self).forward(x))\n        if self.output_img:\n            outs.insert(0, x)\n        return tuple(outs)\n\n    def rfp_forward(self, x, rfp_feats):\n        \"\"\"Forward function for RFP.\"\"\"\n        if self.deep_stem:\n            x = self.stem(x)\n        else:\n            x = self.conv1(x)\n            x = self.norm1(x)\n            x = self.relu(x)\n        x = self.maxpool(x)\n        outs = []\n        for i, layer_name in enumerate(self.res_layers):\n            res_layer = getattr(self, layer_name)\n            rfp_feat = rfp_feats[i] if i > 0 else None\n            for layer in res_layer:\n                x = layer.rfp_forward(x, rfp_feat)\n            if i in self.out_indices:\n                outs.append(x)\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/backbones/detectors_resnext.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\n\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\n\nfrom mmdet.registry import MODELS\nfrom .detectors_resnet import Bottleneck as _Bottleneck\nfrom .detectors_resnet import DetectoRS_ResNet\n\n\nclass Bottleneck(_Bottleneck):\n    expansion = 4\n\n    def __init__(self,\n                 inplanes,\n                 planes,\n                 groups=1,\n                 base_width=4,\n                 base_channels=64,\n                 **kwargs):\n        \"\"\"Bottleneck block for ResNeXt.\n\n        If style is \"pytorch\", the stride-two layer is the 3x3 conv layer, if\n        it is \"caffe\", the stride-two layer is the first 1x1 conv layer.\n        \"\"\"\n        super(Bottleneck, self).__init__(inplanes, planes, **kwargs)\n\n        if groups == 1:\n            width = self.planes\n        else:\n            width = math.floor(self.planes *\n                               (base_width / base_channels)) * groups\n\n        self.norm1_name, norm1 = build_norm_layer(\n            self.norm_cfg, width, postfix=1)\n        self.norm2_name, norm2 = build_norm_layer(\n            self.norm_cfg, width, postfix=2)\n        self.norm3_name, norm3 = build_norm_layer(\n            self.norm_cfg, self.planes * self.expansion, postfix=3)\n\n        self.conv1 = build_conv_layer(\n            self.conv_cfg,\n            self.inplanes,\n            width,\n            kernel_size=1,\n            stride=self.conv1_stride,\n            bias=False)\n        self.add_module(self.norm1_name, norm1)\n        fallback_on_stride = False\n        self.with_modulated_dcn = False\n        if self.with_dcn:\n            fallback_on_stride = self.dcn.pop('fallback_on_stride', False)\n        if self.with_sac:\n            self.conv2 = build_conv_layer(\n                self.sac,\n                width,\n                width,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=self.dilation,\n                dilation=self.dilation,\n                groups=groups,\n                bias=False)\n        elif not self.with_dcn or fallback_on_stride:\n            self.conv2 = build_conv_layer(\n                self.conv_cfg,\n                width,\n                width,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=self.dilation,\n                dilation=self.dilation,\n                groups=groups,\n                bias=False)\n        else:\n            assert self.conv_cfg is None, 'conv_cfg must be None for DCN'\n            self.conv2 = build_conv_layer(\n                self.dcn,\n                width,\n                width,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=self.dilation,\n                dilation=self.dilation,\n                groups=groups,\n                bias=False)\n\n        self.add_module(self.norm2_name, norm2)\n        self.conv3 = build_conv_layer(\n            self.conv_cfg,\n            width,\n            self.planes * self.expansion,\n            kernel_size=1,\n            bias=False)\n        self.add_module(self.norm3_name, norm3)\n\n\n@MODELS.register_module()\nclass DetectoRS_ResNeXt(DetectoRS_ResNet):\n    \"\"\"ResNeXt backbone for DetectoRS.\n\n    Args:\n        groups (int): The number of groups in ResNeXt.\n        base_width (int): The base width of ResNeXt.\n    \"\"\"\n\n    arch_settings = {\n        50: (Bottleneck, (3, 4, 6, 3)),\n        101: (Bottleneck, (3, 4, 23, 3)),\n        152: (Bottleneck, (3, 8, 36, 3))\n    }\n\n    def __init__(self, groups=1, base_width=4, **kwargs):\n        self.groups = groups\n        self.base_width = base_width\n        super(DetectoRS_ResNeXt, self).__init__(**kwargs)\n\n    def make_res_layer(self, **kwargs):\n        return super().make_res_layer(\n            groups=self.groups,\n            base_width=self.base_width,\n            base_channels=self.base_channels,\n            **kwargs)\n"
  },
  {
    "path": "mmdet/models/backbones/efficientnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport math\nfrom functools import partial\n\nimport torch\nimport torch.nn as nn\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn.bricks import ConvModule, DropPath\nfrom mmengine.model import BaseModule, Sequential\n\nfrom mmdet.registry import MODELS\nfrom ..layers import InvertedResidual, SELayer\nfrom ..utils import make_divisible\n\n\nclass EdgeResidual(BaseModule):\n    \"\"\"Edge Residual Block.\n\n    Args:\n        in_channels (int): The input channels of this module.\n        out_channels (int): The output channels of this module.\n        mid_channels (int): The input channels of the second convolution.\n        kernel_size (int): The kernel size of the first convolution.\n            Defaults to 3.\n        stride (int): The stride of the first convolution. Defaults to 1.\n        se_cfg (dict, optional): Config dict for se layer. Defaults to None,\n            which means no se layer.\n        with_residual (bool): Use residual connection. Defaults to True.\n        conv_cfg (dict, optional): Config dict for convolution layer.\n            Defaults to None, which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Defaults to ``dict(type='BN')``.\n        act_cfg (dict): Config dict for activation layer.\n            Defaults to ``dict(type='ReLU')``.\n        drop_path_rate (float): stochastic depth rate. Defaults to 0.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed. Defaults to False.\n        init_cfg (dict | list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 mid_channels,\n                 kernel_size=3,\n                 stride=1,\n                 se_cfg=None,\n                 with_residual=True,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 act_cfg=dict(type='ReLU'),\n                 drop_path_rate=0.,\n                 with_cp=False,\n                 init_cfg=None,\n                 **kwargs):\n        super(EdgeResidual, self).__init__(init_cfg=init_cfg)\n        assert stride in [1, 2]\n        self.with_cp = with_cp\n        self.drop_path = DropPath(\n            drop_path_rate) if drop_path_rate > 0 else nn.Identity()\n        self.with_se = se_cfg is not None\n        self.with_residual = (\n            stride == 1 and in_channels == out_channels and with_residual)\n\n        if self.with_se:\n            assert isinstance(se_cfg, dict)\n\n        self.conv1 = ConvModule(\n            in_channels=in_channels,\n            out_channels=mid_channels,\n            kernel_size=kernel_size,\n            stride=1,\n            padding=kernel_size // 2,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n\n        if self.with_se:\n            self.se = SELayer(**se_cfg)\n\n        self.conv2 = ConvModule(\n            in_channels=mid_channels,\n            out_channels=out_channels,\n            kernel_size=1,\n            stride=stride,\n            padding=0,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=None)\n\n    def forward(self, x):\n\n        def _inner_forward(x):\n            out = x\n            out = self.conv1(out)\n\n            if self.with_se:\n                out = self.se(out)\n\n            out = self.conv2(out)\n\n            if self.with_residual:\n                return x + self.drop_path(out)\n            else:\n                return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        return out\n\n\ndef model_scaling(layer_setting, arch_setting):\n    \"\"\"Scaling operation to the layer's parameters according to the\n    arch_setting.\"\"\"\n    # scale width\n    new_layer_setting = copy.deepcopy(layer_setting)\n    for layer_cfg in new_layer_setting:\n        for block_cfg in layer_cfg:\n            block_cfg[1] = make_divisible(block_cfg[1] * arch_setting[0], 8)\n\n    # scale depth\n    split_layer_setting = [new_layer_setting[0]]\n    for layer_cfg in new_layer_setting[1:-1]:\n        tmp_index = [0]\n        for i in range(len(layer_cfg) - 1):\n            if layer_cfg[i + 1][1] != layer_cfg[i][1]:\n                tmp_index.append(i + 1)\n        tmp_index.append(len(layer_cfg))\n        for i in range(len(tmp_index) - 1):\n            split_layer_setting.append(layer_cfg[tmp_index[i]:tmp_index[i +\n                                                                        1]])\n    split_layer_setting.append(new_layer_setting[-1])\n\n    num_of_layers = [len(layer_cfg) for layer_cfg in split_layer_setting[1:-1]]\n    new_layers = [\n        int(math.ceil(arch_setting[1] * num)) for num in num_of_layers\n    ]\n\n    merge_layer_setting = [split_layer_setting[0]]\n    for i, layer_cfg in enumerate(split_layer_setting[1:-1]):\n        if new_layers[i] <= num_of_layers[i]:\n            tmp_layer_cfg = layer_cfg[:new_layers[i]]\n        else:\n            tmp_layer_cfg = copy.deepcopy(layer_cfg) + [layer_cfg[-1]] * (\n                new_layers[i] - num_of_layers[i])\n        if tmp_layer_cfg[0][3] == 1 and i != 0:\n            merge_layer_setting[-1] += tmp_layer_cfg.copy()\n        else:\n            merge_layer_setting.append(tmp_layer_cfg.copy())\n    merge_layer_setting.append(split_layer_setting[-1])\n\n    return merge_layer_setting\n\n\n@MODELS.register_module()\nclass EfficientNet(BaseModule):\n    \"\"\"EfficientNet backbone.\n\n    Args:\n        arch (str): Architecture of efficientnet. Defaults to b0.\n        out_indices (Sequence[int]): Output from which stages.\n            Defaults to (6, ).\n        frozen_stages (int): Stages to be frozen (all param fixed).\n            Defaults to 0, which means not freezing any parameters.\n        conv_cfg (dict): Config dict for convolution layer.\n            Defaults to None, which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Defaults to dict(type='BN').\n        act_cfg (dict): Config dict for activation layer.\n            Defaults to dict(type='Swish').\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only. Defaults to False.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed. Defaults to False.\n    \"\"\"\n\n    # Parameters to build layers.\n    # 'b' represents the architecture of normal EfficientNet family includes\n    # 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8'.\n    # 'e' represents the architecture of EfficientNet-EdgeTPU including 'es',\n    # 'em', 'el'.\n    # 6 parameters are needed to construct a layer, From left to right:\n    # - kernel_size: The kernel size of the block\n    # - out_channel: The number of out_channels of the block\n    # - se_ratio: The sequeeze ratio of SELayer.\n    # - stride: The stride of the block\n    # - expand_ratio: The expand_ratio of the mid_channels\n    # - block_type: -1: Not a block, 0: InvertedResidual, 1: EdgeResidual\n    layer_settings = {\n        'b': [[[3, 32, 0, 2, 0, -1]],\n              [[3, 16, 4, 1, 1, 0]],\n              [[3, 24, 4, 2, 6, 0],\n               [3, 24, 4, 1, 6, 0]],\n              [[5, 40, 4, 2, 6, 0],\n               [5, 40, 4, 1, 6, 0]],\n              [[3, 80, 4, 2, 6, 0],\n               [3, 80, 4, 1, 6, 0],\n               [3, 80, 4, 1, 6, 0],\n               [5, 112, 4, 1, 6, 0],\n               [5, 112, 4, 1, 6, 0],\n               [5, 112, 4, 1, 6, 0]],\n              [[5, 192, 4, 2, 6, 0],\n               [5, 192, 4, 1, 6, 0],\n               [5, 192, 4, 1, 6, 0],\n               [5, 192, 4, 1, 6, 0],\n               [3, 320, 4, 1, 6, 0]],\n              [[1, 1280, 0, 1, 0, -1]]\n              ],\n        'e': [[[3, 32, 0, 2, 0, -1]],\n              [[3, 24, 0, 1, 3, 1]],\n              [[3, 32, 0, 2, 8, 1],\n               [3, 32, 0, 1, 8, 1]],\n              [[3, 48, 0, 2, 8, 1],\n               [3, 48, 0, 1, 8, 1],\n               [3, 48, 0, 1, 8, 1],\n               [3, 48, 0, 1, 8, 1]],\n              [[5, 96, 0, 2, 8, 0],\n               [5, 96, 0, 1, 8, 0],\n               [5, 96, 0, 1, 8, 0],\n               [5, 96, 0, 1, 8, 0],\n               [5, 96, 0, 1, 8, 0],\n               [5, 144, 0, 1, 8, 0],\n               [5, 144, 0, 1, 8, 0],\n               [5, 144, 0, 1, 8, 0],\n               [5, 144, 0, 1, 8, 0]],\n              [[5, 192, 0, 2, 8, 0],\n               [5, 192, 0, 1, 8, 0]],\n              [[1, 1280, 0, 1, 0, -1]]\n              ]\n    }  # yapf: disable\n\n    # Parameters to build different kinds of architecture.\n    # From left to right: scaling factor for width, scaling factor for depth,\n    # resolution.\n    arch_settings = {\n        'b0': (1.0, 1.0, 224),\n        'b1': (1.0, 1.1, 240),\n        'b2': (1.1, 1.2, 260),\n        'b3': (1.2, 1.4, 300),\n        'b4': (1.4, 1.8, 380),\n        'b5': (1.6, 2.2, 456),\n        'b6': (1.8, 2.6, 528),\n        'b7': (2.0, 3.1, 600),\n        'b8': (2.2, 3.6, 672),\n        'es': (1.0, 1.0, 224),\n        'em': (1.0, 1.1, 240),\n        'el': (1.2, 1.4, 300)\n    }\n\n    def __init__(self,\n                 arch='b0',\n                 drop_path_rate=0.,\n                 out_indices=(6, ),\n                 frozen_stages=0,\n                 conv_cfg=dict(type='Conv2dAdaptivePadding'),\n                 norm_cfg=dict(type='BN', eps=1e-3),\n                 act_cfg=dict(type='Swish'),\n                 norm_eval=False,\n                 with_cp=False,\n                 init_cfg=[\n                     dict(type='Kaiming', layer='Conv2d'),\n                     dict(\n                         type='Constant',\n                         layer=['_BatchNorm', 'GroupNorm'],\n                         val=1)\n                 ]):\n        super(EfficientNet, self).__init__(init_cfg)\n        assert arch in self.arch_settings, \\\n            f'\"{arch}\" is not one of the arch_settings ' \\\n            f'({\", \".join(self.arch_settings.keys())})'\n        self.arch_setting = self.arch_settings[arch]\n        self.layer_setting = self.layer_settings[arch[:1]]\n        for index in out_indices:\n            if index not in range(0, len(self.layer_setting)):\n                raise ValueError('the item in out_indices must in '\n                                 f'range(0, {len(self.layer_setting)}). '\n                                 f'But received {index}')\n\n        if frozen_stages not in range(len(self.layer_setting) + 1):\n            raise ValueError('frozen_stages must be in range(0, '\n                             f'{len(self.layer_setting) + 1}). '\n                             f'But received {frozen_stages}')\n        self.drop_path_rate = drop_path_rate\n        self.out_indices = out_indices\n        self.frozen_stages = frozen_stages\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n        self.norm_eval = norm_eval\n        self.with_cp = with_cp\n\n        self.layer_setting = model_scaling(self.layer_setting,\n                                           self.arch_setting)\n        block_cfg_0 = self.layer_setting[0][0]\n        block_cfg_last = self.layer_setting[-1][0]\n        self.in_channels = make_divisible(block_cfg_0[1], 8)\n        self.out_channels = block_cfg_last[1]\n        self.layers = nn.ModuleList()\n        self.layers.append(\n            ConvModule(\n                in_channels=3,\n                out_channels=self.in_channels,\n                kernel_size=block_cfg_0[0],\n                stride=block_cfg_0[3],\n                padding=block_cfg_0[0] // 2,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg,\n                act_cfg=self.act_cfg))\n        self.make_layer()\n        # Avoid building unused layers in mmdetection.\n        if len(self.layers) < max(self.out_indices) + 1:\n            self.layers.append(\n                ConvModule(\n                    in_channels=self.in_channels,\n                    out_channels=self.out_channels,\n                    kernel_size=block_cfg_last[0],\n                    stride=block_cfg_last[3],\n                    padding=block_cfg_last[0] // 2,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    act_cfg=self.act_cfg))\n\n    def make_layer(self):\n        # Without the first and the final conv block.\n        layer_setting = self.layer_setting[1:-1]\n\n        total_num_blocks = sum([len(x) for x in layer_setting])\n        block_idx = 0\n        dpr = [\n            x.item()\n            for x in torch.linspace(0, self.drop_path_rate, total_num_blocks)\n        ]  # stochastic depth decay rule\n\n        for i, layer_cfg in enumerate(layer_setting):\n            # Avoid building unused layers in mmdetection.\n            if i > max(self.out_indices) - 1:\n                break\n            layer = []\n            for i, block_cfg in enumerate(layer_cfg):\n                (kernel_size, out_channels, se_ratio, stride, expand_ratio,\n                 block_type) = block_cfg\n\n                mid_channels = int(self.in_channels * expand_ratio)\n                out_channels = make_divisible(out_channels, 8)\n                if se_ratio <= 0:\n                    se_cfg = None\n                else:\n                    # In mmdetection, the `divisor` is deleted to align\n                    # the logic of SELayer with mmpretrain.\n                    se_cfg = dict(\n                        channels=mid_channels,\n                        ratio=expand_ratio * se_ratio,\n                        act_cfg=(self.act_cfg, dict(type='Sigmoid')))\n                if block_type == 1:  # edge tpu\n                    if i > 0 and expand_ratio == 3:\n                        with_residual = False\n                        expand_ratio = 4\n                    else:\n                        with_residual = True\n                    mid_channels = int(self.in_channels * expand_ratio)\n                    if se_cfg is not None:\n                        # In mmdetection, the `divisor` is deleted to align\n                        # the logic of SELayer with mmpretrain.\n                        se_cfg = dict(\n                            channels=mid_channels,\n                            ratio=se_ratio * expand_ratio,\n                            act_cfg=(self.act_cfg, dict(type='Sigmoid')))\n                    block = partial(EdgeResidual, with_residual=with_residual)\n                else:\n                    block = InvertedResidual\n                layer.append(\n                    block(\n                        in_channels=self.in_channels,\n                        out_channels=out_channels,\n                        mid_channels=mid_channels,\n                        kernel_size=kernel_size,\n                        stride=stride,\n                        se_cfg=se_cfg,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg,\n                        drop_path_rate=dpr[block_idx],\n                        with_cp=self.with_cp,\n                        # In mmdetection, `with_expand_conv` is set to align\n                        # the logic of InvertedResidual with mmpretrain.\n                        with_expand_conv=(mid_channels != self.in_channels)))\n                self.in_channels = out_channels\n                block_idx += 1\n            self.layers.append(Sequential(*layer))\n\n    def forward(self, x):\n        outs = []\n        for i, layer in enumerate(self.layers):\n            x = layer(x)\n            if i in self.out_indices:\n                outs.append(x)\n\n        return tuple(outs)\n\n    def _freeze_stages(self):\n        for i in range(self.frozen_stages):\n            m = self.layers[i]\n            m.eval()\n            for param in m.parameters():\n                param.requires_grad = False\n\n    def train(self, mode=True):\n        super(EfficientNet, self).train(mode)\n        self._freeze_stages()\n        if mode and self.norm_eval:\n            for m in self.modules():\n                if isinstance(m, nn.BatchNorm2d):\n                    m.eval()\n"
  },
  {
    "path": "mmdet/models/backbones/hourglass.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Sequence\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptMultiConfig\nfrom ..layers import ResLayer\nfrom .resnet import BasicBlock\n\n\nclass HourglassModule(BaseModule):\n    \"\"\"Hourglass Module for HourglassNet backbone.\n\n    Generate module recursively and use BasicBlock as the base unit.\n\n    Args:\n        depth (int): Depth of current HourglassModule.\n        stage_channels (list[int]): Feature channels of sub-modules in current\n            and follow-up HourglassModule.\n        stage_blocks (list[int]): Number of sub-modules stacked in current and\n            follow-up HourglassModule.\n        norm_cfg (ConfigType): Dictionary to construct and config norm layer.\n            Defaults to `dict(type='BN', requires_grad=True)`\n        upsample_cfg (ConfigType): Config dict for interpolate layer.\n            Defaults to `dict(mode='nearest')`\n       init_cfg (dict or ConfigDict, optional): the config to control the\n           initialization.\n    \"\"\"\n\n    def __init__(self,\n                 depth: int,\n                 stage_channels: List[int],\n                 stage_blocks: List[int],\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 upsample_cfg: ConfigType = dict(mode='nearest'),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg)\n\n        self.depth = depth\n\n        cur_block = stage_blocks[0]\n        next_block = stage_blocks[1]\n\n        cur_channel = stage_channels[0]\n        next_channel = stage_channels[1]\n\n        self.up1 = ResLayer(\n            BasicBlock, cur_channel, cur_channel, cur_block, norm_cfg=norm_cfg)\n\n        self.low1 = ResLayer(\n            BasicBlock,\n            cur_channel,\n            next_channel,\n            cur_block,\n            stride=2,\n            norm_cfg=norm_cfg)\n\n        if self.depth > 1:\n            self.low2 = HourglassModule(depth - 1, stage_channels[1:],\n                                        stage_blocks[1:])\n        else:\n            self.low2 = ResLayer(\n                BasicBlock,\n                next_channel,\n                next_channel,\n                next_block,\n                norm_cfg=norm_cfg)\n\n        self.low3 = ResLayer(\n            BasicBlock,\n            next_channel,\n            cur_channel,\n            cur_block,\n            norm_cfg=norm_cfg,\n            downsample_first=False)\n\n        self.up2 = F.interpolate\n        self.upsample_cfg = upsample_cfg\n\n    def forward(self, x: torch.Tensor) -> nn.Module:\n        \"\"\"Forward function.\"\"\"\n        up1 = self.up1(x)\n        low1 = self.low1(x)\n        low2 = self.low2(low1)\n        low3 = self.low3(low2)\n        # Fixing `scale factor` (e.g. 2) is common for upsampling, but\n        # in some cases the spatial size is mismatched and error will arise.\n        if 'scale_factor' in self.upsample_cfg:\n            up2 = self.up2(low3, **self.upsample_cfg)\n        else:\n            shape = up1.shape[2:]\n            up2 = self.up2(low3, size=shape, **self.upsample_cfg)\n        return up1 + up2\n\n\n@MODELS.register_module()\nclass HourglassNet(BaseModule):\n    \"\"\"HourglassNet backbone.\n\n    Stacked Hourglass Networks for Human Pose Estimation.\n    More details can be found in the `paper\n    <https://arxiv.org/abs/1603.06937>`_ .\n\n    Args:\n        downsample_times (int): Downsample times in a HourglassModule.\n        num_stacks (int): Number of HourglassModule modules stacked,\n            1 for Hourglass-52, 2 for Hourglass-104.\n        stage_channels (Sequence[int]): Feature channel of each sub-module in a\n            HourglassModule.\n        stage_blocks (Sequence[int]): Number of sub-modules stacked in a\n            HourglassModule.\n        feat_channel (int): Feature channel of conv after a HourglassModule.\n        norm_cfg (norm_cfg): Dictionary to construct and config norm layer.\n       init_cfg (dict or ConfigDict, optional): the config to control the\n           initialization.\n\n    Example:\n        >>> from mmdet.models import HourglassNet\n        >>> import torch\n        >>> self = HourglassNet()\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 3, 511, 511)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_output in level_outputs:\n        ...     print(tuple(level_output.shape))\n        (1, 256, 128, 128)\n        (1, 256, 128, 128)\n    \"\"\"\n\n    def __init__(self,\n                 downsample_times: int = 5,\n                 num_stacks: int = 2,\n                 stage_channels: Sequence = (256, 256, 384, 384, 384, 512),\n                 stage_blocks: Sequence = (2, 2, 2, 2, 2, 4),\n                 feat_channel: int = 256,\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 init_cfg: OptMultiConfig = None) -> None:\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super().__init__(init_cfg)\n\n        self.num_stacks = num_stacks\n        assert self.num_stacks >= 1\n        assert len(stage_channels) == len(stage_blocks)\n        assert len(stage_channels) > downsample_times\n\n        cur_channel = stage_channels[0]\n\n        self.stem = nn.Sequential(\n            ConvModule(\n                3, cur_channel // 2, 7, padding=3, stride=2,\n                norm_cfg=norm_cfg),\n            ResLayer(\n                BasicBlock,\n                cur_channel // 2,\n                cur_channel,\n                1,\n                stride=2,\n                norm_cfg=norm_cfg))\n\n        self.hourglass_modules = nn.ModuleList([\n            HourglassModule(downsample_times, stage_channels, stage_blocks)\n            for _ in range(num_stacks)\n        ])\n\n        self.inters = ResLayer(\n            BasicBlock,\n            cur_channel,\n            cur_channel,\n            num_stacks - 1,\n            norm_cfg=norm_cfg)\n\n        self.conv1x1s = nn.ModuleList([\n            ConvModule(\n                cur_channel, cur_channel, 1, norm_cfg=norm_cfg, act_cfg=None)\n            for _ in range(num_stacks - 1)\n        ])\n\n        self.out_convs = nn.ModuleList([\n            ConvModule(\n                cur_channel, feat_channel, 3, padding=1, norm_cfg=norm_cfg)\n            for _ in range(num_stacks)\n        ])\n\n        self.remap_convs = nn.ModuleList([\n            ConvModule(\n                feat_channel, cur_channel, 1, norm_cfg=norm_cfg, act_cfg=None)\n            for _ in range(num_stacks - 1)\n        ])\n\n        self.relu = nn.ReLU(inplace=True)\n\n    def init_weights(self) -> None:\n        \"\"\"Init module weights.\"\"\"\n        # Training Centripetal Model needs to reset parameters for Conv2d\n        super().init_weights()\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                m.reset_parameters()\n\n    def forward(self, x: torch.Tensor) -> List[torch.Tensor]:\n        \"\"\"Forward function.\"\"\"\n        inter_feat = self.stem(x)\n        out_feats = []\n\n        for ind in range(self.num_stacks):\n            single_hourglass = self.hourglass_modules[ind]\n            out_conv = self.out_convs[ind]\n\n            hourglass_feat = single_hourglass(inter_feat)\n            out_feat = out_conv(hourglass_feat)\n            out_feats.append(out_feat)\n\n            if ind < self.num_stacks - 1:\n                inter_feat = self.conv1x1s[ind](\n                    inter_feat) + self.remap_convs[ind](\n                        out_feat)\n                inter_feat = self.inters[ind](self.relu(inter_feat))\n\n        return out_feats\n"
  },
  {
    "path": "mmdet/models/backbones/hrnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport torch.nn as nn\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\nfrom mmengine.model import BaseModule, ModuleList, Sequential\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.registry import MODELS\nfrom .resnet import BasicBlock, Bottleneck\n\n\nclass HRModule(BaseModule):\n    \"\"\"High-Resolution Module for HRNet.\n\n    In this module, every branch has 4 BasicBlocks/Bottlenecks. Fusion/Exchange\n    is in this module.\n    \"\"\"\n\n    def __init__(self,\n                 num_branches,\n                 blocks,\n                 num_blocks,\n                 in_channels,\n                 num_channels,\n                 multiscale_output=True,\n                 with_cp=False,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 block_init_cfg=None,\n                 init_cfg=None):\n        super(HRModule, self).__init__(init_cfg)\n        self.block_init_cfg = block_init_cfg\n        self._check_branches(num_branches, num_blocks, in_channels,\n                             num_channels)\n\n        self.in_channels = in_channels\n        self.num_branches = num_branches\n\n        self.multiscale_output = multiscale_output\n        self.norm_cfg = norm_cfg\n        self.conv_cfg = conv_cfg\n        self.with_cp = with_cp\n        self.branches = self._make_branches(num_branches, blocks, num_blocks,\n                                            num_channels)\n        self.fuse_layers = self._make_fuse_layers()\n        self.relu = nn.ReLU(inplace=False)\n\n    def _check_branches(self, num_branches, num_blocks, in_channels,\n                        num_channels):\n        if num_branches != len(num_blocks):\n            error_msg = f'NUM_BRANCHES({num_branches}) ' \\\n                        f'!= NUM_BLOCKS({len(num_blocks)})'\n            raise ValueError(error_msg)\n\n        if num_branches != len(num_channels):\n            error_msg = f'NUM_BRANCHES({num_branches}) ' \\\n                        f'!= NUM_CHANNELS({len(num_channels)})'\n            raise ValueError(error_msg)\n\n        if num_branches != len(in_channels):\n            error_msg = f'NUM_BRANCHES({num_branches}) ' \\\n                        f'!= NUM_INCHANNELS({len(in_channels)})'\n            raise ValueError(error_msg)\n\n    def _make_one_branch(self,\n                         branch_index,\n                         block,\n                         num_blocks,\n                         num_channels,\n                         stride=1):\n        downsample = None\n        if stride != 1 or \\\n                self.in_channels[branch_index] != \\\n                num_channels[branch_index] * block.expansion:\n            downsample = nn.Sequential(\n                build_conv_layer(\n                    self.conv_cfg,\n                    self.in_channels[branch_index],\n                    num_channels[branch_index] * block.expansion,\n                    kernel_size=1,\n                    stride=stride,\n                    bias=False),\n                build_norm_layer(self.norm_cfg, num_channels[branch_index] *\n                                 block.expansion)[1])\n\n        layers = []\n        layers.append(\n            block(\n                self.in_channels[branch_index],\n                num_channels[branch_index],\n                stride,\n                downsample=downsample,\n                with_cp=self.with_cp,\n                norm_cfg=self.norm_cfg,\n                conv_cfg=self.conv_cfg,\n                init_cfg=self.block_init_cfg))\n        self.in_channels[branch_index] = \\\n            num_channels[branch_index] * block.expansion\n        for i in range(1, num_blocks[branch_index]):\n            layers.append(\n                block(\n                    self.in_channels[branch_index],\n                    num_channels[branch_index],\n                    with_cp=self.with_cp,\n                    norm_cfg=self.norm_cfg,\n                    conv_cfg=self.conv_cfg,\n                    init_cfg=self.block_init_cfg))\n\n        return Sequential(*layers)\n\n    def _make_branches(self, num_branches, block, num_blocks, num_channels):\n        branches = []\n\n        for i in range(num_branches):\n            branches.append(\n                self._make_one_branch(i, block, num_blocks, num_channels))\n\n        return ModuleList(branches)\n\n    def _make_fuse_layers(self):\n        if self.num_branches == 1:\n            return None\n\n        num_branches = self.num_branches\n        in_channels = self.in_channels\n        fuse_layers = []\n        num_out_branches = num_branches if self.multiscale_output else 1\n        for i in range(num_out_branches):\n            fuse_layer = []\n            for j in range(num_branches):\n                if j > i:\n                    fuse_layer.append(\n                        nn.Sequential(\n                            build_conv_layer(\n                                self.conv_cfg,\n                                in_channels[j],\n                                in_channels[i],\n                                kernel_size=1,\n                                stride=1,\n                                padding=0,\n                                bias=False),\n                            build_norm_layer(self.norm_cfg, in_channels[i])[1],\n                            nn.Upsample(\n                                scale_factor=2**(j - i), mode='nearest')))\n                elif j == i:\n                    fuse_layer.append(None)\n                else:\n                    conv_downsamples = []\n                    for k in range(i - j):\n                        if k == i - j - 1:\n                            conv_downsamples.append(\n                                nn.Sequential(\n                                    build_conv_layer(\n                                        self.conv_cfg,\n                                        in_channels[j],\n                                        in_channels[i],\n                                        kernel_size=3,\n                                        stride=2,\n                                        padding=1,\n                                        bias=False),\n                                    build_norm_layer(self.norm_cfg,\n                                                     in_channels[i])[1]))\n                        else:\n                            conv_downsamples.append(\n                                nn.Sequential(\n                                    build_conv_layer(\n                                        self.conv_cfg,\n                                        in_channels[j],\n                                        in_channels[j],\n                                        kernel_size=3,\n                                        stride=2,\n                                        padding=1,\n                                        bias=False),\n                                    build_norm_layer(self.norm_cfg,\n                                                     in_channels[j])[1],\n                                    nn.ReLU(inplace=False)))\n                    fuse_layer.append(nn.Sequential(*conv_downsamples))\n            fuse_layers.append(nn.ModuleList(fuse_layer))\n\n        return nn.ModuleList(fuse_layers)\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        if self.num_branches == 1:\n            return [self.branches[0](x[0])]\n\n        for i in range(self.num_branches):\n            x[i] = self.branches[i](x[i])\n\n        x_fuse = []\n        for i in range(len(self.fuse_layers)):\n            y = 0\n            for j in range(self.num_branches):\n                if i == j:\n                    y += x[j]\n                else:\n                    y += self.fuse_layers[i][j](x[j])\n            x_fuse.append(self.relu(y))\n        return x_fuse\n\n\n@MODELS.register_module()\nclass HRNet(BaseModule):\n    \"\"\"HRNet backbone.\n\n    `High-Resolution Representations for Labeling Pixels and Regions\n    arXiv: <https://arxiv.org/abs/1904.04514>`_.\n\n    Args:\n        extra (dict): Detailed configuration for each stage of HRNet.\n            There must be 4 stages, the configuration for each stage must have\n            5 keys:\n\n                - num_modules(int): The number of HRModule in this stage.\n                - num_branches(int): The number of branches in the HRModule.\n                - block(str): The type of convolution block.\n                - num_blocks(tuple): The number of blocks in each branch.\n                    The length must be equal to num_branches.\n                - num_channels(tuple): The number of channels in each branch.\n                    The length must be equal to num_branches.\n        in_channels (int): Number of input image channels. Default: 3.\n        conv_cfg (dict): Dictionary to construct and config conv layer.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only. Default: True.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed. Default: False.\n        zero_init_residual (bool): Whether to use zero init for last norm layer\n            in resblocks to let them behave as identity. Default: False.\n        multiscale_output (bool): Whether to output multi-level features\n            produced by multiple branches. If False, only the first level\n            feature will be output. Default: True.\n        pretrained (str, optional): Model pretrained path. Default: None.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None.\n\n    Example:\n        >>> from mmdet.models import HRNet\n        >>> import torch\n        >>> extra = dict(\n        >>>     stage1=dict(\n        >>>         num_modules=1,\n        >>>         num_branches=1,\n        >>>         block='BOTTLENECK',\n        >>>         num_blocks=(4, ),\n        >>>         num_channels=(64, )),\n        >>>     stage2=dict(\n        >>>         num_modules=1,\n        >>>         num_branches=2,\n        >>>         block='BASIC',\n        >>>         num_blocks=(4, 4),\n        >>>         num_channels=(32, 64)),\n        >>>     stage3=dict(\n        >>>         num_modules=4,\n        >>>         num_branches=3,\n        >>>         block='BASIC',\n        >>>         num_blocks=(4, 4, 4),\n        >>>         num_channels=(32, 64, 128)),\n        >>>     stage4=dict(\n        >>>         num_modules=3,\n        >>>         num_branches=4,\n        >>>         block='BASIC',\n        >>>         num_blocks=(4, 4, 4, 4),\n        >>>         num_channels=(32, 64, 128, 256)))\n        >>> self = HRNet(extra, in_channels=1)\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 1, 32, 32)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_out in level_outputs:\n        ...     print(tuple(level_out.shape))\n        (1, 32, 8, 8)\n        (1, 64, 4, 4)\n        (1, 128, 2, 2)\n        (1, 256, 1, 1)\n    \"\"\"\n\n    blocks_dict = {'BASIC': BasicBlock, 'BOTTLENECK': Bottleneck}\n\n    def __init__(self,\n                 extra,\n                 in_channels=3,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 norm_eval=True,\n                 with_cp=False,\n                 zero_init_residual=False,\n                 multiscale_output=True,\n                 pretrained=None,\n                 init_cfg=None):\n        super(HRNet, self).__init__(init_cfg)\n\n        self.pretrained = pretrained\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            if init_cfg is None:\n                self.init_cfg = [\n                    dict(type='Kaiming', layer='Conv2d'),\n                    dict(\n                        type='Constant',\n                        val=1,\n                        layer=['_BatchNorm', 'GroupNorm'])\n                ]\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n        # Assert configurations of 4 stages are in extra\n        assert 'stage1' in extra and 'stage2' in extra \\\n               and 'stage3' in extra and 'stage4' in extra\n        # Assert whether the length of `num_blocks` and `num_channels` are\n        # equal to `num_branches`\n        for i in range(4):\n            cfg = extra[f'stage{i + 1}']\n            assert len(cfg['num_blocks']) == cfg['num_branches'] and \\\n                   len(cfg['num_channels']) == cfg['num_branches']\n\n        self.extra = extra\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.norm_eval = norm_eval\n        self.with_cp = with_cp\n        self.zero_init_residual = zero_init_residual\n\n        # stem net\n        self.norm1_name, norm1 = build_norm_layer(self.norm_cfg, 64, postfix=1)\n        self.norm2_name, norm2 = build_norm_layer(self.norm_cfg, 64, postfix=2)\n\n        self.conv1 = build_conv_layer(\n            self.conv_cfg,\n            in_channels,\n            64,\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            bias=False)\n\n        self.add_module(self.norm1_name, norm1)\n        self.conv2 = build_conv_layer(\n            self.conv_cfg,\n            64,\n            64,\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            bias=False)\n\n        self.add_module(self.norm2_name, norm2)\n        self.relu = nn.ReLU(inplace=True)\n\n        # stage 1\n        self.stage1_cfg = self.extra['stage1']\n        num_channels = self.stage1_cfg['num_channels'][0]\n        block_type = self.stage1_cfg['block']\n        num_blocks = self.stage1_cfg['num_blocks'][0]\n\n        block = self.blocks_dict[block_type]\n        stage1_out_channels = num_channels * block.expansion\n        self.layer1 = self._make_layer(block, 64, num_channels, num_blocks)\n\n        # stage 2\n        self.stage2_cfg = self.extra['stage2']\n        num_channels = self.stage2_cfg['num_channels']\n        block_type = self.stage2_cfg['block']\n\n        block = self.blocks_dict[block_type]\n        num_channels = [channel * block.expansion for channel in num_channels]\n        self.transition1 = self._make_transition_layer([stage1_out_channels],\n                                                       num_channels)\n        self.stage2, pre_stage_channels = self._make_stage(\n            self.stage2_cfg, num_channels)\n\n        # stage 3\n        self.stage3_cfg = self.extra['stage3']\n        num_channels = self.stage3_cfg['num_channels']\n        block_type = self.stage3_cfg['block']\n\n        block = self.blocks_dict[block_type]\n        num_channels = [channel * block.expansion for channel in num_channels]\n        self.transition2 = self._make_transition_layer(pre_stage_channels,\n                                                       num_channels)\n        self.stage3, pre_stage_channels = self._make_stage(\n            self.stage3_cfg, num_channels)\n\n        # stage 4\n        self.stage4_cfg = self.extra['stage4']\n        num_channels = self.stage4_cfg['num_channels']\n        block_type = self.stage4_cfg['block']\n\n        block = self.blocks_dict[block_type]\n        num_channels = [channel * block.expansion for channel in num_channels]\n        self.transition3 = self._make_transition_layer(pre_stage_channels,\n                                                       num_channels)\n        self.stage4, pre_stage_channels = self._make_stage(\n            self.stage4_cfg, num_channels, multiscale_output=multiscale_output)\n\n    @property\n    def norm1(self):\n        \"\"\"nn.Module: the normalization layer named \"norm1\" \"\"\"\n        return getattr(self, self.norm1_name)\n\n    @property\n    def norm2(self):\n        \"\"\"nn.Module: the normalization layer named \"norm2\" \"\"\"\n        return getattr(self, self.norm2_name)\n\n    def _make_transition_layer(self, num_channels_pre_layer,\n                               num_channels_cur_layer):\n        num_branches_cur = len(num_channels_cur_layer)\n        num_branches_pre = len(num_channels_pre_layer)\n\n        transition_layers = []\n        for i in range(num_branches_cur):\n            if i < num_branches_pre:\n                if num_channels_cur_layer[i] != num_channels_pre_layer[i]:\n                    transition_layers.append(\n                        nn.Sequential(\n                            build_conv_layer(\n                                self.conv_cfg,\n                                num_channels_pre_layer[i],\n                                num_channels_cur_layer[i],\n                                kernel_size=3,\n                                stride=1,\n                                padding=1,\n                                bias=False),\n                            build_norm_layer(self.norm_cfg,\n                                             num_channels_cur_layer[i])[1],\n                            nn.ReLU(inplace=True)))\n                else:\n                    transition_layers.append(None)\n            else:\n                conv_downsamples = []\n                for j in range(i + 1 - num_branches_pre):\n                    in_channels = num_channels_pre_layer[-1]\n                    out_channels = num_channels_cur_layer[i] \\\n                        if j == i - num_branches_pre else in_channels\n                    conv_downsamples.append(\n                        nn.Sequential(\n                            build_conv_layer(\n                                self.conv_cfg,\n                                in_channels,\n                                out_channels,\n                                kernel_size=3,\n                                stride=2,\n                                padding=1,\n                                bias=False),\n                            build_norm_layer(self.norm_cfg, out_channels)[1],\n                            nn.ReLU(inplace=True)))\n                transition_layers.append(nn.Sequential(*conv_downsamples))\n\n        return nn.ModuleList(transition_layers)\n\n    def _make_layer(self, block, inplanes, planes, blocks, stride=1):\n        downsample = None\n        if stride != 1 or inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                build_conv_layer(\n                    self.conv_cfg,\n                    inplanes,\n                    planes * block.expansion,\n                    kernel_size=1,\n                    stride=stride,\n                    bias=False),\n                build_norm_layer(self.norm_cfg, planes * block.expansion)[1])\n\n        layers = []\n        block_init_cfg = None\n        if self.pretrained is None and not hasattr(\n                self, 'init_cfg') and self.zero_init_residual:\n            if block is BasicBlock:\n                block_init_cfg = dict(\n                    type='Constant', val=0, override=dict(name='norm2'))\n            elif block is Bottleneck:\n                block_init_cfg = dict(\n                    type='Constant', val=0, override=dict(name='norm3'))\n        layers.append(\n            block(\n                inplanes,\n                planes,\n                stride,\n                downsample=downsample,\n                with_cp=self.with_cp,\n                norm_cfg=self.norm_cfg,\n                conv_cfg=self.conv_cfg,\n                init_cfg=block_init_cfg,\n            ))\n        inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(\n                block(\n                    inplanes,\n                    planes,\n                    with_cp=self.with_cp,\n                    norm_cfg=self.norm_cfg,\n                    conv_cfg=self.conv_cfg,\n                    init_cfg=block_init_cfg))\n\n        return Sequential(*layers)\n\n    def _make_stage(self, layer_config, in_channels, multiscale_output=True):\n        num_modules = layer_config['num_modules']\n        num_branches = layer_config['num_branches']\n        num_blocks = layer_config['num_blocks']\n        num_channels = layer_config['num_channels']\n        block = self.blocks_dict[layer_config['block']]\n\n        hr_modules = []\n        block_init_cfg = None\n        if self.pretrained is None and not hasattr(\n                self, 'init_cfg') and self.zero_init_residual:\n            if block is BasicBlock:\n                block_init_cfg = dict(\n                    type='Constant', val=0, override=dict(name='norm2'))\n            elif block is Bottleneck:\n                block_init_cfg = dict(\n                    type='Constant', val=0, override=dict(name='norm3'))\n\n        for i in range(num_modules):\n            # multi_scale_output is only used for the last module\n            if not multiscale_output and i == num_modules - 1:\n                reset_multiscale_output = False\n            else:\n                reset_multiscale_output = True\n\n            hr_modules.append(\n                HRModule(\n                    num_branches,\n                    block,\n                    num_blocks,\n                    in_channels,\n                    num_channels,\n                    reset_multiscale_output,\n                    with_cp=self.with_cp,\n                    norm_cfg=self.norm_cfg,\n                    conv_cfg=self.conv_cfg,\n                    block_init_cfg=block_init_cfg))\n\n        return Sequential(*hr_modules), in_channels\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        x = self.conv1(x)\n        x = self.norm1(x)\n        x = self.relu(x)\n        x = self.conv2(x)\n        x = self.norm2(x)\n        x = self.relu(x)\n        x = self.layer1(x)\n\n        x_list = []\n        for i in range(self.stage2_cfg['num_branches']):\n            if self.transition1[i] is not None:\n                x_list.append(self.transition1[i](x))\n            else:\n                x_list.append(x)\n        y_list = self.stage2(x_list)\n\n        x_list = []\n        for i in range(self.stage3_cfg['num_branches']):\n            if self.transition2[i] is not None:\n                x_list.append(self.transition2[i](y_list[-1]))\n            else:\n                x_list.append(y_list[i])\n        y_list = self.stage3(x_list)\n\n        x_list = []\n        for i in range(self.stage4_cfg['num_branches']):\n            if self.transition3[i] is not None:\n                x_list.append(self.transition3[i](y_list[-1]))\n            else:\n                x_list.append(y_list[i])\n        y_list = self.stage4(x_list)\n\n        return y_list\n\n    def train(self, mode=True):\n        \"\"\"Convert the model into training mode will keeping the normalization\n        layer freezed.\"\"\"\n        super(HRNet, self).train(mode)\n        if mode and self.norm_eval:\n            for m in self.modules():\n                # trick: eval have effect on BatchNorm only\n                if isinstance(m, _BatchNorm):\n                    m.eval()\n"
  },
  {
    "path": "mmdet/models/backbones/mobilenet_v2.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.registry import MODELS\nfrom ..layers import InvertedResidual\nfrom ..utils import make_divisible\n\n\n@MODELS.register_module()\nclass MobileNetV2(BaseModule):\n    \"\"\"MobileNetV2 backbone.\n\n    Args:\n        widen_factor (float): Width multiplier, multiply number of\n            channels in each layer by this amount. Default: 1.0.\n        out_indices (Sequence[int], optional): Output from which stages.\n            Default: (1, 2, 4, 7).\n        frozen_stages (int): Stages to be frozen (all param fixed).\n            Default: -1, which means not freezing any parameters.\n        conv_cfg (dict, optional): Config dict for convolution layer.\n            Default: None, which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='BN').\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='ReLU6').\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only. Default: False.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed. Default: False.\n        pretrained (str, optional): model pretrained path. Default: None\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    # Parameters to build layers. 4 parameters are needed to construct a\n    # layer, from left to right: expand_ratio, channel, num_blocks, stride.\n    arch_settings = [[1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2],\n                     [6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2],\n                     [6, 320, 1, 1]]\n\n    def __init__(self,\n                 widen_factor=1.,\n                 out_indices=(1, 2, 4, 7),\n                 frozen_stages=-1,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 act_cfg=dict(type='ReLU6'),\n                 norm_eval=False,\n                 with_cp=False,\n                 pretrained=None,\n                 init_cfg=None):\n        super(MobileNetV2, self).__init__(init_cfg)\n\n        self.pretrained = pretrained\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            if init_cfg is None:\n                self.init_cfg = [\n                    dict(type='Kaiming', layer='Conv2d'),\n                    dict(\n                        type='Constant',\n                        val=1,\n                        layer=['_BatchNorm', 'GroupNorm'])\n                ]\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n        self.widen_factor = widen_factor\n        self.out_indices = out_indices\n        if not set(out_indices).issubset(set(range(0, 8))):\n            raise ValueError('out_indices must be a subset of range'\n                             f'(0, 8). But received {out_indices}')\n\n        if frozen_stages not in range(-1, 8):\n            raise ValueError('frozen_stages must be in range(-1, 8). '\n                             f'But received {frozen_stages}')\n        self.out_indices = out_indices\n        self.frozen_stages = frozen_stages\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n        self.norm_eval = norm_eval\n        self.with_cp = with_cp\n\n        self.in_channels = make_divisible(32 * widen_factor, 8)\n\n        self.conv1 = ConvModule(\n            in_channels=3,\n            out_channels=self.in_channels,\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            conv_cfg=self.conv_cfg,\n            norm_cfg=self.norm_cfg,\n            act_cfg=self.act_cfg)\n\n        self.layers = []\n\n        for i, layer_cfg in enumerate(self.arch_settings):\n            expand_ratio, channel, num_blocks, stride = layer_cfg\n            out_channels = make_divisible(channel * widen_factor, 8)\n            inverted_res_layer = self.make_layer(\n                out_channels=out_channels,\n                num_blocks=num_blocks,\n                stride=stride,\n                expand_ratio=expand_ratio)\n            layer_name = f'layer{i + 1}'\n            self.add_module(layer_name, inverted_res_layer)\n            self.layers.append(layer_name)\n\n        if widen_factor > 1.0:\n            self.out_channel = int(1280 * widen_factor)\n        else:\n            self.out_channel = 1280\n\n        layer = ConvModule(\n            in_channels=self.in_channels,\n            out_channels=self.out_channel,\n            kernel_size=1,\n            stride=1,\n            padding=0,\n            conv_cfg=self.conv_cfg,\n            norm_cfg=self.norm_cfg,\n            act_cfg=self.act_cfg)\n        self.add_module('conv2', layer)\n        self.layers.append('conv2')\n\n    def make_layer(self, out_channels, num_blocks, stride, expand_ratio):\n        \"\"\"Stack InvertedResidual blocks to build a layer for MobileNetV2.\n\n        Args:\n            out_channels (int): out_channels of block.\n            num_blocks (int): number of blocks.\n            stride (int): stride of the first block. Default: 1\n            expand_ratio (int): Expand the number of channels of the\n                hidden layer in InvertedResidual by this ratio. Default: 6.\n        \"\"\"\n        layers = []\n        for i in range(num_blocks):\n            if i >= 1:\n                stride = 1\n            layers.append(\n                InvertedResidual(\n                    self.in_channels,\n                    out_channels,\n                    mid_channels=int(round(self.in_channels * expand_ratio)),\n                    stride=stride,\n                    with_expand_conv=expand_ratio != 1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    act_cfg=self.act_cfg,\n                    with_cp=self.with_cp))\n            self.in_channels = out_channels\n\n        return nn.Sequential(*layers)\n\n    def _freeze_stages(self):\n        if self.frozen_stages >= 0:\n            for param in self.conv1.parameters():\n                param.requires_grad = False\n        for i in range(1, self.frozen_stages + 1):\n            layer = getattr(self, f'layer{i}')\n            layer.eval()\n            for param in layer.parameters():\n                param.requires_grad = False\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        x = self.conv1(x)\n        outs = []\n        for i, layer_name in enumerate(self.layers):\n            layer = getattr(self, layer_name)\n            x = layer(x)\n            if i in self.out_indices:\n                outs.append(x)\n        return tuple(outs)\n\n    def train(self, mode=True):\n        \"\"\"Convert the model into training mode while keep normalization layer\n        frozen.\"\"\"\n        super(MobileNetV2, self).train(mode)\n        self._freeze_stages()\n        if mode and self.norm_eval:\n            for m in self.modules():\n                # trick: eval have effect on BatchNorm only\n                if isinstance(m, _BatchNorm):\n                    m.eval()\n"
  },
  {
    "path": "mmdet/models/backbones/pvt.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nimport warnings\nfrom collections import OrderedDict\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Conv2d, build_activation_layer, build_norm_layer\nfrom mmcv.cnn.bricks.drop import build_dropout\nfrom mmcv.cnn.bricks.transformer import MultiheadAttention\nfrom mmengine.logging import MMLogger\nfrom mmengine.model import (BaseModule, ModuleList, Sequential, constant_init,\n                            normal_init, trunc_normal_init)\nfrom mmengine.model.weight_init import trunc_normal_\nfrom mmengine.runner.checkpoint import CheckpointLoader, load_state_dict\nfrom torch.nn.modules.utils import _pair as to_2tuple\n\nfrom mmdet.registry import MODELS\nfrom ..layers import PatchEmbed, nchw_to_nlc, nlc_to_nchw\n\n\nclass MixFFN(BaseModule):\n    \"\"\"An implementation of MixFFN of PVT.\n\n    The differences between MixFFN & FFN:\n        1. Use 1X1 Conv to replace Linear layer.\n        2. Introduce 3X3 Depth-wise Conv to encode positional information.\n\n    Args:\n        embed_dims (int): The feature dimension. Same as\n            `MultiheadAttention`.\n        feedforward_channels (int): The hidden dimension of FFNs.\n        act_cfg (dict, optional): The activation config for FFNs.\n            Default: dict(type='GELU').\n        ffn_drop (float, optional): Probability of an element to be\n            zeroed in FFN. Default 0.0.\n        dropout_layer (obj:`ConfigDict`): The dropout_layer used\n            when adding the shortcut.\n            Default: None.\n        use_conv (bool): If True, add 3x3 DWConv between two Linear layers.\n            Defaults: False.\n        init_cfg (obj:`mmengine.ConfigDict`): The Config for initialization.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims,\n                 feedforward_channels,\n                 act_cfg=dict(type='GELU'),\n                 ffn_drop=0.,\n                 dropout_layer=None,\n                 use_conv=False,\n                 init_cfg=None):\n        super(MixFFN, self).__init__(init_cfg=init_cfg)\n\n        self.embed_dims = embed_dims\n        self.feedforward_channels = feedforward_channels\n        self.act_cfg = act_cfg\n        activate = build_activation_layer(act_cfg)\n\n        in_channels = embed_dims\n        fc1 = Conv2d(\n            in_channels=in_channels,\n            out_channels=feedforward_channels,\n            kernel_size=1,\n            stride=1,\n            bias=True)\n        if use_conv:\n            # 3x3 depth wise conv to provide positional encode information\n            dw_conv = Conv2d(\n                in_channels=feedforward_channels,\n                out_channels=feedforward_channels,\n                kernel_size=3,\n                stride=1,\n                padding=(3 - 1) // 2,\n                bias=True,\n                groups=feedforward_channels)\n        fc2 = Conv2d(\n            in_channels=feedforward_channels,\n            out_channels=in_channels,\n            kernel_size=1,\n            stride=1,\n            bias=True)\n        drop = nn.Dropout(ffn_drop)\n        layers = [fc1, activate, drop, fc2, drop]\n        if use_conv:\n            layers.insert(1, dw_conv)\n        self.layers = Sequential(*layers)\n        self.dropout_layer = build_dropout(\n            dropout_layer) if dropout_layer else torch.nn.Identity()\n\n    def forward(self, x, hw_shape, identity=None):\n        out = nlc_to_nchw(x, hw_shape)\n        out = self.layers(out)\n        out = nchw_to_nlc(out)\n        if identity is None:\n            identity = x\n        return identity + self.dropout_layer(out)\n\n\nclass SpatialReductionAttention(MultiheadAttention):\n    \"\"\"An implementation of Spatial Reduction Attention of PVT.\n\n    This module is modified from MultiheadAttention which is a module from\n    mmcv.cnn.bricks.transformer.\n\n    Args:\n        embed_dims (int): The embedding dimension.\n        num_heads (int): Parallel attention heads.\n        attn_drop (float): A Dropout layer on attn_output_weights.\n            Default: 0.0.\n        proj_drop (float): A Dropout layer after `nn.MultiheadAttention`.\n            Default: 0.0.\n        dropout_layer (obj:`ConfigDict`): The dropout_layer used\n            when adding the shortcut. Default: None.\n        batch_first (bool): Key, Query and Value are shape of\n            (batch, n, embed_dim)\n            or (n, batch, embed_dim). Default: False.\n        qkv_bias (bool): enable bias for qkv if True. Default: True.\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='LN').\n        sr_ratio (int): The ratio of spatial reduction of Spatial Reduction\n            Attention of PVT. Default: 1.\n        init_cfg (obj:`mmengine.ConfigDict`): The Config for initialization.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims,\n                 num_heads,\n                 attn_drop=0.,\n                 proj_drop=0.,\n                 dropout_layer=None,\n                 batch_first=True,\n                 qkv_bias=True,\n                 norm_cfg=dict(type='LN'),\n                 sr_ratio=1,\n                 init_cfg=None):\n        super().__init__(\n            embed_dims,\n            num_heads,\n            attn_drop,\n            proj_drop,\n            batch_first=batch_first,\n            dropout_layer=dropout_layer,\n            bias=qkv_bias,\n            init_cfg=init_cfg)\n\n        self.sr_ratio = sr_ratio\n        if sr_ratio > 1:\n            self.sr = Conv2d(\n                in_channels=embed_dims,\n                out_channels=embed_dims,\n                kernel_size=sr_ratio,\n                stride=sr_ratio)\n            # The ret[0] of build_norm_layer is norm name.\n            self.norm = build_norm_layer(norm_cfg, embed_dims)[1]\n\n        # handle the BC-breaking from https://github.com/open-mmlab/mmcv/pull/1418 # noqa\n        from mmdet import digit_version, mmcv_version\n        if mmcv_version < digit_version('1.3.17'):\n            warnings.warn('The legacy version of forward function in'\n                          'SpatialReductionAttention is deprecated in'\n                          'mmcv>=1.3.17 and will no longer support in the'\n                          'future. Please upgrade your mmcv.')\n            self.forward = self.legacy_forward\n\n    def forward(self, x, hw_shape, identity=None):\n\n        x_q = x\n        if self.sr_ratio > 1:\n            x_kv = nlc_to_nchw(x, hw_shape)\n            x_kv = self.sr(x_kv)\n            x_kv = nchw_to_nlc(x_kv)\n            x_kv = self.norm(x_kv)\n        else:\n            x_kv = x\n\n        if identity is None:\n            identity = x_q\n\n        # Because the dataflow('key', 'query', 'value') of\n        # ``torch.nn.MultiheadAttention`` is (num_queries, batch,\n        # embed_dims), We should adjust the shape of dataflow from\n        # batch_first (batch, num_queries, embed_dims) to num_queries_first\n        # (num_queries ,batch, embed_dims), and recover ``attn_output``\n        # from num_queries_first to batch_first.\n        if self.batch_first:\n            x_q = x_q.transpose(0, 1)\n            x_kv = x_kv.transpose(0, 1)\n\n        out = self.attn(query=x_q, key=x_kv, value=x_kv)[0]\n\n        if self.batch_first:\n            out = out.transpose(0, 1)\n\n        return identity + self.dropout_layer(self.proj_drop(out))\n\n    def legacy_forward(self, x, hw_shape, identity=None):\n        \"\"\"multi head attention forward in mmcv version < 1.3.17.\"\"\"\n        x_q = x\n        if self.sr_ratio > 1:\n            x_kv = nlc_to_nchw(x, hw_shape)\n            x_kv = self.sr(x_kv)\n            x_kv = nchw_to_nlc(x_kv)\n            x_kv = self.norm(x_kv)\n        else:\n            x_kv = x\n\n        if identity is None:\n            identity = x_q\n\n        out = self.attn(query=x_q, key=x_kv, value=x_kv)[0]\n\n        return identity + self.dropout_layer(self.proj_drop(out))\n\n\nclass PVTEncoderLayer(BaseModule):\n    \"\"\"Implements one encoder layer in PVT.\n\n    Args:\n        embed_dims (int): The feature dimension.\n        num_heads (int): Parallel attention heads.\n        feedforward_channels (int): The hidden dimension for FFNs.\n        drop_rate (float): Probability of an element to be zeroed.\n            after the feed forward layer. Default: 0.0.\n        attn_drop_rate (float): The drop out rate for attention layer.\n            Default: 0.0.\n        drop_path_rate (float): stochastic depth rate. Default: 0.0.\n        qkv_bias (bool): enable bias for qkv if True.\n            Default: True.\n        act_cfg (dict): The activation config for FFNs.\n            Default: dict(type='GELU').\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='LN').\n        sr_ratio (int): The ratio of spatial reduction of Spatial Reduction\n            Attention of PVT. Default: 1.\n        use_conv_ffn (bool): If True, use Convolutional FFN to replace FFN.\n            Default: False.\n        init_cfg (dict, optional): Initialization config dict.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims,\n                 num_heads,\n                 feedforward_channels,\n                 drop_rate=0.,\n                 attn_drop_rate=0.,\n                 drop_path_rate=0.,\n                 qkv_bias=True,\n                 act_cfg=dict(type='GELU'),\n                 norm_cfg=dict(type='LN'),\n                 sr_ratio=1,\n                 use_conv_ffn=False,\n                 init_cfg=None):\n        super(PVTEncoderLayer, self).__init__(init_cfg=init_cfg)\n\n        # The ret[0] of build_norm_layer is norm name.\n        self.norm1 = build_norm_layer(norm_cfg, embed_dims)[1]\n\n        self.attn = SpatialReductionAttention(\n            embed_dims=embed_dims,\n            num_heads=num_heads,\n            attn_drop=attn_drop_rate,\n            proj_drop=drop_rate,\n            dropout_layer=dict(type='DropPath', drop_prob=drop_path_rate),\n            qkv_bias=qkv_bias,\n            norm_cfg=norm_cfg,\n            sr_ratio=sr_ratio)\n\n        # The ret[0] of build_norm_layer is norm name.\n        self.norm2 = build_norm_layer(norm_cfg, embed_dims)[1]\n\n        self.ffn = MixFFN(\n            embed_dims=embed_dims,\n            feedforward_channels=feedforward_channels,\n            ffn_drop=drop_rate,\n            dropout_layer=dict(type='DropPath', drop_prob=drop_path_rate),\n            use_conv=use_conv_ffn,\n            act_cfg=act_cfg)\n\n    def forward(self, x, hw_shape):\n        x = self.attn(self.norm1(x), hw_shape, identity=x)\n        x = self.ffn(self.norm2(x), hw_shape, identity=x)\n\n        return x\n\n\nclass AbsolutePositionEmbedding(BaseModule):\n    \"\"\"An implementation of the absolute position embedding in PVT.\n\n    Args:\n        pos_shape (int): The shape of the absolute position embedding.\n        pos_dim (int): The dimension of the absolute position embedding.\n        drop_rate (float): Probability of an element to be zeroed.\n            Default: 0.0.\n    \"\"\"\n\n    def __init__(self, pos_shape, pos_dim, drop_rate=0., init_cfg=None):\n        super().__init__(init_cfg=init_cfg)\n\n        if isinstance(pos_shape, int):\n            pos_shape = to_2tuple(pos_shape)\n        elif isinstance(pos_shape, tuple):\n            if len(pos_shape) == 1:\n                pos_shape = to_2tuple(pos_shape[0])\n            assert len(pos_shape) == 2, \\\n                f'The size of image should have length 1 or 2, ' \\\n                f'but got {len(pos_shape)}'\n        self.pos_shape = pos_shape\n        self.pos_dim = pos_dim\n\n        self.pos_embed = nn.Parameter(\n            torch.zeros(1, pos_shape[0] * pos_shape[1], pos_dim))\n        self.drop = nn.Dropout(p=drop_rate)\n\n    def init_weights(self):\n        trunc_normal_(self.pos_embed, std=0.02)\n\n    def resize_pos_embed(self, pos_embed, input_shape, mode='bilinear'):\n        \"\"\"Resize pos_embed weights.\n\n        Resize pos_embed using bilinear interpolate method.\n\n        Args:\n            pos_embed (torch.Tensor): Position embedding weights.\n            input_shape (tuple): Tuple for (downsampled input image height,\n                downsampled input image width).\n            mode (str): Algorithm used for upsampling:\n                ``'nearest'`` | ``'linear'`` | ``'bilinear'`` | ``'bicubic'`` |\n                ``'trilinear'``. Default: ``'bilinear'``.\n\n        Return:\n            torch.Tensor: The resized pos_embed of shape [B, L_new, C].\n        \"\"\"\n        assert pos_embed.ndim == 3, 'shape of pos_embed must be [B, L, C]'\n        pos_h, pos_w = self.pos_shape\n        pos_embed_weight = pos_embed[:, (-1 * pos_h * pos_w):]\n        pos_embed_weight = pos_embed_weight.reshape(\n            1, pos_h, pos_w, self.pos_dim).permute(0, 3, 1, 2).contiguous()\n        pos_embed_weight = F.interpolate(\n            pos_embed_weight, size=input_shape, mode=mode)\n        pos_embed_weight = torch.flatten(pos_embed_weight,\n                                         2).transpose(1, 2).contiguous()\n        pos_embed = pos_embed_weight\n\n        return pos_embed\n\n    def forward(self, x, hw_shape, mode='bilinear'):\n        pos_embed = self.resize_pos_embed(self.pos_embed, hw_shape, mode)\n        return self.drop(x + pos_embed)\n\n\n@MODELS.register_module()\nclass PyramidVisionTransformer(BaseModule):\n    \"\"\"Pyramid Vision Transformer (PVT)\n\n    Implementation of `Pyramid Vision Transformer: A Versatile Backbone for\n    Dense Prediction without Convolutions\n    <https://arxiv.org/pdf/2102.12122.pdf>`_.\n\n    Args:\n        pretrain_img_size (int | tuple[int]): The size of input image when\n            pretrain. Defaults: 224.\n        in_channels (int): Number of input channels. Default: 3.\n        embed_dims (int): Embedding dimension. Default: 64.\n        num_stags (int): The num of stages. Default: 4.\n        num_layers (Sequence[int]): The layer number of each transformer encode\n            layer. Default: [3, 4, 6, 3].\n        num_heads (Sequence[int]): The attention heads of each transformer\n            encode layer. Default: [1, 2, 5, 8].\n        patch_sizes (Sequence[int]): The patch_size of each patch embedding.\n            Default: [4, 2, 2, 2].\n        strides (Sequence[int]): The stride of each patch embedding.\n            Default: [4, 2, 2, 2].\n        paddings (Sequence[int]): The padding of each patch embedding.\n            Default: [0, 0, 0, 0].\n        sr_ratios (Sequence[int]): The spatial reduction rate of each\n            transformer encode layer. Default: [8, 4, 2, 1].\n        out_indices (Sequence[int] | int): Output from which stages.\n            Default: (0, 1, 2, 3).\n        mlp_ratios (Sequence[int]): The ratio of the mlp hidden dim to the\n            embedding dim of each transformer encode layer.\n            Default: [8, 8, 4, 4].\n        qkv_bias (bool): Enable bias for qkv if True. Default: True.\n        drop_rate (float): Probability of an element to be zeroed.\n            Default 0.0.\n        attn_drop_rate (float): The drop out rate for attention layer.\n            Default 0.0.\n        drop_path_rate (float): stochastic depth rate. Default 0.1.\n        use_abs_pos_embed (bool): If True, add absolute position embedding to\n            the patch embedding. Defaults: True.\n        use_conv_ffn (bool): If True, use Convolutional FFN to replace FFN.\n            Default: False.\n        act_cfg (dict): The activation config for FFNs.\n            Default: dict(type='GELU').\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='LN').\n        pretrained (str, optional): model pretrained path. Default: None.\n        convert_weights (bool): The flag indicates whether the\n            pre-trained model is from the original repo. We may need\n            to convert some keys to make it compatible.\n            Default: True.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 pretrain_img_size=224,\n                 in_channels=3,\n                 embed_dims=64,\n                 num_stages=4,\n                 num_layers=[3, 4, 6, 3],\n                 num_heads=[1, 2, 5, 8],\n                 patch_sizes=[4, 2, 2, 2],\n                 strides=[4, 2, 2, 2],\n                 paddings=[0, 0, 0, 0],\n                 sr_ratios=[8, 4, 2, 1],\n                 out_indices=(0, 1, 2, 3),\n                 mlp_ratios=[8, 8, 4, 4],\n                 qkv_bias=True,\n                 drop_rate=0.,\n                 attn_drop_rate=0.,\n                 drop_path_rate=0.1,\n                 use_abs_pos_embed=True,\n                 norm_after_stage=False,\n                 use_conv_ffn=False,\n                 act_cfg=dict(type='GELU'),\n                 norm_cfg=dict(type='LN', eps=1e-6),\n                 pretrained=None,\n                 convert_weights=True,\n                 init_cfg=None):\n        super().__init__(init_cfg=init_cfg)\n\n        self.convert_weights = convert_weights\n        if isinstance(pretrain_img_size, int):\n            pretrain_img_size = to_2tuple(pretrain_img_size)\n        elif isinstance(pretrain_img_size, tuple):\n            if len(pretrain_img_size) == 1:\n                pretrain_img_size = to_2tuple(pretrain_img_size[0])\n            assert len(pretrain_img_size) == 2, \\\n                f'The size of image should have length 1 or 2, ' \\\n                f'but got {len(pretrain_img_size)}'\n\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be setting at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            self.init_cfg = init_cfg\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n        self.embed_dims = embed_dims\n\n        self.num_stages = num_stages\n        self.num_layers = num_layers\n        self.num_heads = num_heads\n        self.patch_sizes = patch_sizes\n        self.strides = strides\n        self.sr_ratios = sr_ratios\n        assert num_stages == len(num_layers) == len(num_heads) \\\n               == len(patch_sizes) == len(strides) == len(sr_ratios)\n\n        self.out_indices = out_indices\n        assert max(out_indices) < self.num_stages\n        self.pretrained = pretrained\n\n        # transformer encoder\n        dpr = [\n            x.item()\n            for x in torch.linspace(0, drop_path_rate, sum(num_layers))\n        ]  # stochastic num_layer decay rule\n\n        cur = 0\n        self.layers = ModuleList()\n        for i, num_layer in enumerate(num_layers):\n            embed_dims_i = embed_dims * num_heads[i]\n            patch_embed = PatchEmbed(\n                in_channels=in_channels,\n                embed_dims=embed_dims_i,\n                kernel_size=patch_sizes[i],\n                stride=strides[i],\n                padding=paddings[i],\n                bias=True,\n                norm_cfg=norm_cfg)\n\n            layers = ModuleList()\n            if use_abs_pos_embed:\n                pos_shape = pretrain_img_size // np.prod(patch_sizes[:i + 1])\n                pos_embed = AbsolutePositionEmbedding(\n                    pos_shape=pos_shape,\n                    pos_dim=embed_dims_i,\n                    drop_rate=drop_rate)\n                layers.append(pos_embed)\n            layers.extend([\n                PVTEncoderLayer(\n                    embed_dims=embed_dims_i,\n                    num_heads=num_heads[i],\n                    feedforward_channels=mlp_ratios[i] * embed_dims_i,\n                    drop_rate=drop_rate,\n                    attn_drop_rate=attn_drop_rate,\n                    drop_path_rate=dpr[cur + idx],\n                    qkv_bias=qkv_bias,\n                    act_cfg=act_cfg,\n                    norm_cfg=norm_cfg,\n                    sr_ratio=sr_ratios[i],\n                    use_conv_ffn=use_conv_ffn) for idx in range(num_layer)\n            ])\n            in_channels = embed_dims_i\n            # The ret[0] of build_norm_layer is norm name.\n            if norm_after_stage:\n                norm = build_norm_layer(norm_cfg, embed_dims_i)[1]\n            else:\n                norm = nn.Identity()\n            self.layers.append(ModuleList([patch_embed, layers, norm]))\n            cur += num_layer\n\n    def init_weights(self):\n        logger = MMLogger.get_current_instance()\n        if self.init_cfg is None:\n            logger.warn(f'No pre-trained weights for '\n                        f'{self.__class__.__name__}, '\n                        f'training start from scratch')\n            for m in self.modules():\n                if isinstance(m, nn.Linear):\n                    trunc_normal_init(m, std=.02, bias=0.)\n                elif isinstance(m, nn.LayerNorm):\n                    constant_init(m, 1.0)\n                elif isinstance(m, nn.Conv2d):\n                    fan_out = m.kernel_size[0] * m.kernel_size[\n                        1] * m.out_channels\n                    fan_out //= m.groups\n                    normal_init(m, 0, math.sqrt(2.0 / fan_out))\n                elif isinstance(m, AbsolutePositionEmbedding):\n                    m.init_weights()\n        else:\n            assert 'checkpoint' in self.init_cfg, f'Only support ' \\\n                                                  f'specify `Pretrained` in ' \\\n                                                  f'`init_cfg` in ' \\\n                                                  f'{self.__class__.__name__} '\n            checkpoint = CheckpointLoader.load_checkpoint(\n                self.init_cfg.checkpoint, logger=logger, map_location='cpu')\n            logger.warn(f'Load pre-trained model for '\n                        f'{self.__class__.__name__} from original repo')\n            if 'state_dict' in checkpoint:\n                state_dict = checkpoint['state_dict']\n            elif 'model' in checkpoint:\n                state_dict = checkpoint['model']\n            else:\n                state_dict = checkpoint\n            if self.convert_weights:\n                # Because pvt backbones are not supported by mmpretrain,\n                # so we need to convert pre-trained weights to match this\n                # implementation.\n                state_dict = pvt_convert(state_dict)\n            load_state_dict(self, state_dict, strict=False, logger=logger)\n\n    def forward(self, x):\n        outs = []\n\n        for i, layer in enumerate(self.layers):\n            x, hw_shape = layer[0](x)\n\n            for block in layer[1]:\n                x = block(x, hw_shape)\n            x = layer[2](x)\n            x = nlc_to_nchw(x, hw_shape)\n            if i in self.out_indices:\n                outs.append(x)\n\n        return outs\n\n\n@MODELS.register_module()\nclass PyramidVisionTransformerV2(PyramidVisionTransformer):\n    \"\"\"Implementation of `PVTv2: Improved Baselines with Pyramid Vision\n    Transformer <https://arxiv.org/pdf/2106.13797.pdf>`_.\"\"\"\n\n    def __init__(self, **kwargs):\n        super(PyramidVisionTransformerV2, self).__init__(\n            patch_sizes=[7, 3, 3, 3],\n            paddings=[3, 1, 1, 1],\n            use_abs_pos_embed=False,\n            norm_after_stage=True,\n            use_conv_ffn=True,\n            **kwargs)\n\n\ndef pvt_convert(ckpt):\n    new_ckpt = OrderedDict()\n    # Process the concat between q linear weights and kv linear weights\n    use_abs_pos_embed = False\n    use_conv_ffn = False\n    for k in ckpt.keys():\n        if k.startswith('pos_embed'):\n            use_abs_pos_embed = True\n        if k.find('dwconv') >= 0:\n            use_conv_ffn = True\n    for k, v in ckpt.items():\n        if k.startswith('head'):\n            continue\n        if k.startswith('norm.'):\n            continue\n        if k.startswith('cls_token'):\n            continue\n        if k.startswith('pos_embed'):\n            stage_i = int(k.replace('pos_embed', ''))\n            new_k = k.replace(f'pos_embed{stage_i}',\n                              f'layers.{stage_i - 1}.1.0.pos_embed')\n            if stage_i == 4 and v.size(1) == 50:  # 1 (cls token) + 7 * 7\n                new_v = v[:, 1:, :]  # remove cls token\n            else:\n                new_v = v\n        elif k.startswith('patch_embed'):\n            stage_i = int(k.split('.')[0].replace('patch_embed', ''))\n            new_k = k.replace(f'patch_embed{stage_i}',\n                              f'layers.{stage_i - 1}.0')\n            new_v = v\n            if 'proj.' in new_k:\n                new_k = new_k.replace('proj.', 'projection.')\n        elif k.startswith('block'):\n            stage_i = int(k.split('.')[0].replace('block', ''))\n            layer_i = int(k.split('.')[1])\n            new_layer_i = layer_i + use_abs_pos_embed\n            new_k = k.replace(f'block{stage_i}.{layer_i}',\n                              f'layers.{stage_i - 1}.1.{new_layer_i}')\n            new_v = v\n            if 'attn.q.' in new_k:\n                sub_item_k = k.replace('q.', 'kv.')\n                new_k = new_k.replace('q.', 'attn.in_proj_')\n                new_v = torch.cat([v, ckpt[sub_item_k]], dim=0)\n            elif 'attn.kv.' in new_k:\n                continue\n            elif 'attn.proj.' in new_k:\n                new_k = new_k.replace('proj.', 'attn.out_proj.')\n            elif 'attn.sr.' in new_k:\n                new_k = new_k.replace('sr.', 'sr.')\n            elif 'mlp.' in new_k:\n                string = f'{new_k}-'\n                new_k = new_k.replace('mlp.', 'ffn.layers.')\n                if 'fc1.weight' in new_k or 'fc2.weight' in new_k:\n                    new_v = v.reshape((*v.shape, 1, 1))\n                new_k = new_k.replace('fc1.', '0.')\n                new_k = new_k.replace('dwconv.dwconv.', '1.')\n                if use_conv_ffn:\n                    new_k = new_k.replace('fc2.', '4.')\n                else:\n                    new_k = new_k.replace('fc2.', '3.')\n                string += f'{new_k} {v.shape}-{new_v.shape}'\n        elif k.startswith('norm'):\n            stage_i = int(k[4])\n            new_k = k.replace(f'norm{stage_i}', f'layers.{stage_i - 1}.2')\n            new_v = v\n        else:\n            new_k = k\n            new_v = v\n        new_ckpt[new_k] = new_v\n\n    return new_ckpt\n"
  },
  {
    "path": "mmdet/models/backbones/regnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport numpy as np\nimport torch.nn as nn\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\n\nfrom mmdet.registry import MODELS\nfrom .resnet import ResNet\nfrom .resnext import Bottleneck\n\n\n@MODELS.register_module()\nclass RegNet(ResNet):\n    \"\"\"RegNet backbone.\n\n    More details can be found in `paper <https://arxiv.org/abs/2003.13678>`_ .\n\n    Args:\n        arch (dict): The parameter of RegNets.\n\n            - w0 (int): initial width\n            - wa (float): slope of width\n            - wm (float): quantization parameter to quantize the width\n            - depth (int): depth of the backbone\n            - group_w (int): width of group\n            - bot_mul (float): bottleneck ratio, i.e. expansion of bottleneck.\n        strides (Sequence[int]): Strides of the first block of each stage.\n        base_channels (int): Base channels after stem layer.\n        in_channels (int): Number of input image channels. Default: 3.\n        dilations (Sequence[int]): Dilation of each stage.\n        out_indices (Sequence[int]): Output from which stages.\n        style (str): `pytorch` or `caffe`. If set to \"pytorch\", the stride-two\n            layer is the 3x3 conv layer, otherwise the stride-two layer is\n            the first 1x1 conv layer.\n        frozen_stages (int): Stages to be frozen (all param fixed). -1 means\n            not freezing any parameters.\n        norm_cfg (dict): dictionary to construct and config norm layer.\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed.\n        zero_init_residual (bool): whether to use zero init for last norm layer\n            in resblocks to let them behave as identity.\n        pretrained (str, optional): model pretrained path. Default: None\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n\n    Example:\n        >>> from mmdet.models import RegNet\n        >>> import torch\n        >>> self = RegNet(\n                arch=dict(\n                    w0=88,\n                    wa=26.31,\n                    wm=2.25,\n                    group_w=48,\n                    depth=25,\n                    bot_mul=1.0))\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 3, 32, 32)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_out in level_outputs:\n        ...     print(tuple(level_out.shape))\n        (1, 96, 8, 8)\n        (1, 192, 4, 4)\n        (1, 432, 2, 2)\n        (1, 1008, 1, 1)\n    \"\"\"\n    arch_settings = {\n        'regnetx_400mf':\n        dict(w0=24, wa=24.48, wm=2.54, group_w=16, depth=22, bot_mul=1.0),\n        'regnetx_800mf':\n        dict(w0=56, wa=35.73, wm=2.28, group_w=16, depth=16, bot_mul=1.0),\n        'regnetx_1.6gf':\n        dict(w0=80, wa=34.01, wm=2.25, group_w=24, depth=18, bot_mul=1.0),\n        'regnetx_3.2gf':\n        dict(w0=88, wa=26.31, wm=2.25, group_w=48, depth=25, bot_mul=1.0),\n        'regnetx_4.0gf':\n        dict(w0=96, wa=38.65, wm=2.43, group_w=40, depth=23, bot_mul=1.0),\n        'regnetx_6.4gf':\n        dict(w0=184, wa=60.83, wm=2.07, group_w=56, depth=17, bot_mul=1.0),\n        'regnetx_8.0gf':\n        dict(w0=80, wa=49.56, wm=2.88, group_w=120, depth=23, bot_mul=1.0),\n        'regnetx_12gf':\n        dict(w0=168, wa=73.36, wm=2.37, group_w=112, depth=19, bot_mul=1.0),\n    }\n\n    def __init__(self,\n                 arch,\n                 in_channels=3,\n                 stem_channels=32,\n                 base_channels=32,\n                 strides=(2, 2, 2, 2),\n                 dilations=(1, 1, 1, 1),\n                 out_indices=(0, 1, 2, 3),\n                 style='pytorch',\n                 deep_stem=False,\n                 avg_down=False,\n                 frozen_stages=-1,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', requires_grad=True),\n                 norm_eval=True,\n                 dcn=None,\n                 stage_with_dcn=(False, False, False, False),\n                 plugins=None,\n                 with_cp=False,\n                 zero_init_residual=True,\n                 pretrained=None,\n                 init_cfg=None):\n        super(ResNet, self).__init__(init_cfg)\n\n        # Generate RegNet parameters first\n        if isinstance(arch, str):\n            assert arch in self.arch_settings, \\\n                f'\"arch\": \"{arch}\" is not one of the' \\\n                ' arch_settings'\n            arch = self.arch_settings[arch]\n        elif not isinstance(arch, dict):\n            raise ValueError('Expect \"arch\" to be either a string '\n                             f'or a dict, got {type(arch)}')\n\n        widths, num_stages = self.generate_regnet(\n            arch['w0'],\n            arch['wa'],\n            arch['wm'],\n            arch['depth'],\n        )\n        # Convert to per stage format\n        stage_widths, stage_blocks = self.get_stages_from_blocks(widths)\n        # Generate group widths and bot muls\n        group_widths = [arch['group_w'] for _ in range(num_stages)]\n        self.bottleneck_ratio = [arch['bot_mul'] for _ in range(num_stages)]\n        # Adjust the compatibility of stage_widths and group_widths\n        stage_widths, group_widths = self.adjust_width_group(\n            stage_widths, self.bottleneck_ratio, group_widths)\n\n        # Group params by stage\n        self.stage_widths = stage_widths\n        self.group_widths = group_widths\n        self.depth = sum(stage_blocks)\n        self.stem_channels = stem_channels\n        self.base_channels = base_channels\n        self.num_stages = num_stages\n        assert num_stages >= 1 and num_stages <= 4\n        self.strides = strides\n        self.dilations = dilations\n        assert len(strides) == len(dilations) == num_stages\n        self.out_indices = out_indices\n        assert max(out_indices) < num_stages\n        self.style = style\n        self.deep_stem = deep_stem\n        self.avg_down = avg_down\n        self.frozen_stages = frozen_stages\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.with_cp = with_cp\n        self.norm_eval = norm_eval\n        self.dcn = dcn\n        self.stage_with_dcn = stage_with_dcn\n        if dcn is not None:\n            assert len(stage_with_dcn) == num_stages\n        self.plugins = plugins\n        self.zero_init_residual = zero_init_residual\n        self.block = Bottleneck\n        expansion_bak = self.block.expansion\n        self.block.expansion = 1\n        self.stage_blocks = stage_blocks[:num_stages]\n\n        self._make_stem_layer(in_channels, stem_channels)\n\n        block_init_cfg = None\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            if init_cfg is None:\n                self.init_cfg = [\n                    dict(type='Kaiming', layer='Conv2d'),\n                    dict(\n                        type='Constant',\n                        val=1,\n                        layer=['_BatchNorm', 'GroupNorm'])\n                ]\n                if self.zero_init_residual:\n                    block_init_cfg = dict(\n                        type='Constant', val=0, override=dict(name='norm3'))\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n        self.inplanes = stem_channels\n        self.res_layers = []\n        for i, num_blocks in enumerate(self.stage_blocks):\n            stride = self.strides[i]\n            dilation = self.dilations[i]\n            group_width = self.group_widths[i]\n            width = int(round(self.stage_widths[i] * self.bottleneck_ratio[i]))\n            stage_groups = width // group_width\n\n            dcn = self.dcn if self.stage_with_dcn[i] else None\n            if self.plugins is not None:\n                stage_plugins = self.make_stage_plugins(self.plugins, i)\n            else:\n                stage_plugins = None\n\n            res_layer = self.make_res_layer(\n                block=self.block,\n                inplanes=self.inplanes,\n                planes=self.stage_widths[i],\n                num_blocks=num_blocks,\n                stride=stride,\n                dilation=dilation,\n                style=self.style,\n                avg_down=self.avg_down,\n                with_cp=self.with_cp,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg,\n                dcn=dcn,\n                plugins=stage_plugins,\n                groups=stage_groups,\n                base_width=group_width,\n                base_channels=self.stage_widths[i],\n                init_cfg=block_init_cfg)\n            self.inplanes = self.stage_widths[i]\n            layer_name = f'layer{i + 1}'\n            self.add_module(layer_name, res_layer)\n            self.res_layers.append(layer_name)\n\n        self._freeze_stages()\n\n        self.feat_dim = stage_widths[-1]\n        self.block.expansion = expansion_bak\n\n    def _make_stem_layer(self, in_channels, base_channels):\n        self.conv1 = build_conv_layer(\n            self.conv_cfg,\n            in_channels,\n            base_channels,\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            bias=False)\n        self.norm1_name, norm1 = build_norm_layer(\n            self.norm_cfg, base_channels, postfix=1)\n        self.add_module(self.norm1_name, norm1)\n        self.relu = nn.ReLU(inplace=True)\n\n    def generate_regnet(self,\n                        initial_width,\n                        width_slope,\n                        width_parameter,\n                        depth,\n                        divisor=8):\n        \"\"\"Generates per block width from RegNet parameters.\n\n        Args:\n            initial_width ([int]): Initial width of the backbone\n            width_slope ([float]): Slope of the quantized linear function\n            width_parameter ([int]): Parameter used to quantize the width.\n            depth ([int]): Depth of the backbone.\n            divisor (int, optional): The divisor of channels. Defaults to 8.\n\n        Returns:\n            list, int: return a list of widths of each stage and the number \\\n                of stages\n        \"\"\"\n        assert width_slope >= 0\n        assert initial_width > 0\n        assert width_parameter > 1\n        assert initial_width % divisor == 0\n        widths_cont = np.arange(depth) * width_slope + initial_width\n        ks = np.round(\n            np.log(widths_cont / initial_width) / np.log(width_parameter))\n        widths = initial_width * np.power(width_parameter, ks)\n        widths = np.round(np.divide(widths, divisor)) * divisor\n        num_stages = len(np.unique(widths))\n        widths, widths_cont = widths.astype(int).tolist(), widths_cont.tolist()\n        return widths, num_stages\n\n    @staticmethod\n    def quantize_float(number, divisor):\n        \"\"\"Converts a float to closest non-zero int divisible by divisor.\n\n        Args:\n            number (int): Original number to be quantized.\n            divisor (int): Divisor used to quantize the number.\n\n        Returns:\n            int: quantized number that is divisible by devisor.\n        \"\"\"\n        return int(round(number / divisor) * divisor)\n\n    def adjust_width_group(self, widths, bottleneck_ratio, groups):\n        \"\"\"Adjusts the compatibility of widths and groups.\n\n        Args:\n            widths (list[int]): Width of each stage.\n            bottleneck_ratio (float): Bottleneck ratio.\n            groups (int): number of groups in each stage\n\n        Returns:\n            tuple(list): The adjusted widths and groups of each stage.\n        \"\"\"\n        bottleneck_width = [\n            int(w * b) for w, b in zip(widths, bottleneck_ratio)\n        ]\n        groups = [min(g, w_bot) for g, w_bot in zip(groups, bottleneck_width)]\n        bottleneck_width = [\n            self.quantize_float(w_bot, g)\n            for w_bot, g in zip(bottleneck_width, groups)\n        ]\n        widths = [\n            int(w_bot / b)\n            for w_bot, b in zip(bottleneck_width, bottleneck_ratio)\n        ]\n        return widths, groups\n\n    def get_stages_from_blocks(self, widths):\n        \"\"\"Gets widths/stage_blocks of network at each stage.\n\n        Args:\n            widths (list[int]): Width in each stage.\n\n        Returns:\n            tuple(list): width and depth of each stage\n        \"\"\"\n        width_diff = [\n            width != width_prev\n            for width, width_prev in zip(widths + [0], [0] + widths)\n        ]\n        stage_widths = [\n            width for width, diff in zip(widths, width_diff[:-1]) if diff\n        ]\n        stage_blocks = np.diff([\n            depth for depth, diff in zip(range(len(width_diff)), width_diff)\n            if diff\n        ]).tolist()\n        return stage_widths, stage_blocks\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        x = self.conv1(x)\n        x = self.norm1(x)\n        x = self.relu(x)\n\n        outs = []\n        for i, layer_name in enumerate(self.res_layers):\n            res_layer = getattr(self, layer_name)\n            x = res_layer(x)\n            if i in self.out_indices:\n                outs.append(x)\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/backbones/res2net.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\n\nimport torch\nimport torch.nn as nn\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\nfrom mmengine.model import Sequential\n\nfrom mmdet.registry import MODELS\nfrom .resnet import Bottleneck as _Bottleneck\nfrom .resnet import ResNet\n\n\nclass Bottle2neck(_Bottleneck):\n    expansion = 4\n\n    def __init__(self,\n                 inplanes,\n                 planes,\n                 scales=4,\n                 base_width=26,\n                 base_channels=64,\n                 stage_type='normal',\n                 **kwargs):\n        \"\"\"Bottle2neck block for Res2Net.\n\n        If style is \"pytorch\", the stride-two layer is the 3x3 conv layer, if\n        it is \"caffe\", the stride-two layer is the first 1x1 conv layer.\n        \"\"\"\n        super(Bottle2neck, self).__init__(inplanes, planes, **kwargs)\n        assert scales > 1, 'Res2Net degenerates to ResNet when scales = 1.'\n        width = int(math.floor(self.planes * (base_width / base_channels)))\n\n        self.norm1_name, norm1 = build_norm_layer(\n            self.norm_cfg, width * scales, postfix=1)\n        self.norm3_name, norm3 = build_norm_layer(\n            self.norm_cfg, self.planes * self.expansion, postfix=3)\n\n        self.conv1 = build_conv_layer(\n            self.conv_cfg,\n            self.inplanes,\n            width * scales,\n            kernel_size=1,\n            stride=self.conv1_stride,\n            bias=False)\n        self.add_module(self.norm1_name, norm1)\n\n        if stage_type == 'stage' and self.conv2_stride != 1:\n            self.pool = nn.AvgPool2d(\n                kernel_size=3, stride=self.conv2_stride, padding=1)\n        convs = []\n        bns = []\n\n        fallback_on_stride = False\n        if self.with_dcn:\n            fallback_on_stride = self.dcn.pop('fallback_on_stride', False)\n        if not self.with_dcn or fallback_on_stride:\n            for i in range(scales - 1):\n                convs.append(\n                    build_conv_layer(\n                        self.conv_cfg,\n                        width,\n                        width,\n                        kernel_size=3,\n                        stride=self.conv2_stride,\n                        padding=self.dilation,\n                        dilation=self.dilation,\n                        bias=False))\n                bns.append(\n                    build_norm_layer(self.norm_cfg, width, postfix=i + 1)[1])\n            self.convs = nn.ModuleList(convs)\n            self.bns = nn.ModuleList(bns)\n        else:\n            assert self.conv_cfg is None, 'conv_cfg must be None for DCN'\n            for i in range(scales - 1):\n                convs.append(\n                    build_conv_layer(\n                        self.dcn,\n                        width,\n                        width,\n                        kernel_size=3,\n                        stride=self.conv2_stride,\n                        padding=self.dilation,\n                        dilation=self.dilation,\n                        bias=False))\n                bns.append(\n                    build_norm_layer(self.norm_cfg, width, postfix=i + 1)[1])\n            self.convs = nn.ModuleList(convs)\n            self.bns = nn.ModuleList(bns)\n\n        self.conv3 = build_conv_layer(\n            self.conv_cfg,\n            width * scales,\n            self.planes * self.expansion,\n            kernel_size=1,\n            bias=False)\n        self.add_module(self.norm3_name, norm3)\n\n        self.stage_type = stage_type\n        self.scales = scales\n        self.width = width\n        delattr(self, 'conv2')\n        delattr(self, self.norm2_name)\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n\n        def _inner_forward(x):\n            identity = x\n\n            out = self.conv1(x)\n            out = self.norm1(out)\n            out = self.relu(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv1_plugin_names)\n\n            spx = torch.split(out, self.width, 1)\n            sp = self.convs[0](spx[0].contiguous())\n            sp = self.relu(self.bns[0](sp))\n            out = sp\n            for i in range(1, self.scales - 1):\n                if self.stage_type == 'stage':\n                    sp = spx[i]\n                else:\n                    sp = sp + spx[i]\n                sp = self.convs[i](sp.contiguous())\n                sp = self.relu(self.bns[i](sp))\n                out = torch.cat((out, sp), 1)\n\n            if self.stage_type == 'normal' or self.conv2_stride == 1:\n                out = torch.cat((out, spx[self.scales - 1]), 1)\n            elif self.stage_type == 'stage':\n                out = torch.cat((out, self.pool(spx[self.scales - 1])), 1)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv2_plugin_names)\n\n            out = self.conv3(out)\n            out = self.norm3(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv3_plugin_names)\n\n            if self.downsample is not None:\n                identity = self.downsample(x)\n\n            out += identity\n\n            return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        out = self.relu(out)\n\n        return out\n\n\nclass Res2Layer(Sequential):\n    \"\"\"Res2Layer to build Res2Net style backbone.\n\n    Args:\n        block (nn.Module): block used to build ResLayer.\n        inplanes (int): inplanes of block.\n        planes (int): planes of block.\n        num_blocks (int): number of blocks.\n        stride (int): stride of the first block. Default: 1\n        avg_down (bool): Use AvgPool instead of stride conv when\n            downsampling in the bottle2neck. Default: False\n        conv_cfg (dict): dictionary to construct and config conv layer.\n            Default: None\n        norm_cfg (dict): dictionary to construct and config norm layer.\n            Default: dict(type='BN')\n        scales (int): Scales used in Res2Net. Default: 4\n        base_width (int): Basic width of each scale. Default: 26\n    \"\"\"\n\n    def __init__(self,\n                 block,\n                 inplanes,\n                 planes,\n                 num_blocks,\n                 stride=1,\n                 avg_down=True,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 scales=4,\n                 base_width=26,\n                 **kwargs):\n        self.block = block\n\n        downsample = None\n        if stride != 1 or inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                nn.AvgPool2d(\n                    kernel_size=stride,\n                    stride=stride,\n                    ceil_mode=True,\n                    count_include_pad=False),\n                build_conv_layer(\n                    conv_cfg,\n                    inplanes,\n                    planes * block.expansion,\n                    kernel_size=1,\n                    stride=1,\n                    bias=False),\n                build_norm_layer(norm_cfg, planes * block.expansion)[1],\n            )\n\n        layers = []\n        layers.append(\n            block(\n                inplanes=inplanes,\n                planes=planes,\n                stride=stride,\n                downsample=downsample,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                scales=scales,\n                base_width=base_width,\n                stage_type='stage',\n                **kwargs))\n        inplanes = planes * block.expansion\n        for i in range(1, num_blocks):\n            layers.append(\n                block(\n                    inplanes=inplanes,\n                    planes=planes,\n                    stride=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    scales=scales,\n                    base_width=base_width,\n                    **kwargs))\n        super(Res2Layer, self).__init__(*layers)\n\n\n@MODELS.register_module()\nclass Res2Net(ResNet):\n    \"\"\"Res2Net backbone.\n\n    Args:\n        scales (int): Scales used in Res2Net. Default: 4\n        base_width (int): Basic width of each scale. Default: 26\n        depth (int): Depth of res2net, from {50, 101, 152}.\n        in_channels (int): Number of input image channels. Default: 3.\n        num_stages (int): Res2net stages. Default: 4.\n        strides (Sequence[int]): Strides of the first block of each stage.\n        dilations (Sequence[int]): Dilation of each stage.\n        out_indices (Sequence[int]): Output from which stages.\n        style (str): `pytorch` or `caffe`. If set to \"pytorch\", the stride-two\n            layer is the 3x3 conv layer, otherwise the stride-two layer is\n            the first 1x1 conv layer.\n        deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv\n        avg_down (bool): Use AvgPool instead of stride conv when\n            downsampling in the bottle2neck.\n        frozen_stages (int): Stages to be frozen (stop grad and set eval mode).\n            -1 means not freezing any parameters.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only.\n        plugins (list[dict]): List of plugins for stages, each dict contains:\n\n            - cfg (dict, required): Cfg dict to build plugin.\n            - position (str, required): Position inside block to insert\n              plugin, options are 'after_conv1', 'after_conv2', 'after_conv3'.\n            - stages (tuple[bool], optional): Stages to apply plugin, length\n              should be same as 'num_stages'.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed.\n        zero_init_residual (bool): Whether to use zero init for last norm layer\n            in resblocks to let them behave as identity.\n        pretrained (str, optional): model pretrained path. Default: None\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n\n    Example:\n        >>> from mmdet.models import Res2Net\n        >>> import torch\n        >>> self = Res2Net(depth=50, scales=4, base_width=26)\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 3, 32, 32)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_out in level_outputs:\n        ...     print(tuple(level_out.shape))\n        (1, 256, 8, 8)\n        (1, 512, 4, 4)\n        (1, 1024, 2, 2)\n        (1, 2048, 1, 1)\n    \"\"\"\n\n    arch_settings = {\n        50: (Bottle2neck, (3, 4, 6, 3)),\n        101: (Bottle2neck, (3, 4, 23, 3)),\n        152: (Bottle2neck, (3, 8, 36, 3))\n    }\n\n    def __init__(self,\n                 scales=4,\n                 base_width=26,\n                 style='pytorch',\n                 deep_stem=True,\n                 avg_down=True,\n                 pretrained=None,\n                 init_cfg=None,\n                 **kwargs):\n        self.scales = scales\n        self.base_width = base_width\n        super(Res2Net, self).__init__(\n            style='pytorch',\n            deep_stem=True,\n            avg_down=True,\n            pretrained=pretrained,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def make_res_layer(self, **kwargs):\n        return Res2Layer(\n            scales=self.scales,\n            base_width=self.base_width,\n            base_channels=self.base_channels,\n            **kwargs)\n"
  },
  {
    "path": "mmdet/models/backbones/resnest.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom ..layers import ResLayer\nfrom .resnet import Bottleneck as _Bottleneck\nfrom .resnet import ResNetV1d\n\n\nclass RSoftmax(nn.Module):\n    \"\"\"Radix Softmax module in ``SplitAttentionConv2d``.\n\n    Args:\n        radix (int): Radix of input.\n        groups (int): Groups of input.\n    \"\"\"\n\n    def __init__(self, radix, groups):\n        super().__init__()\n        self.radix = radix\n        self.groups = groups\n\n    def forward(self, x):\n        batch = x.size(0)\n        if self.radix > 1:\n            x = x.view(batch, self.groups, self.radix, -1).transpose(1, 2)\n            x = F.softmax(x, dim=1)\n            x = x.reshape(batch, -1)\n        else:\n            x = torch.sigmoid(x)\n        return x\n\n\nclass SplitAttentionConv2d(BaseModule):\n    \"\"\"Split-Attention Conv2d in ResNeSt.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        channels (int): Number of intermediate channels.\n        kernel_size (int | tuple[int]): Size of the convolution kernel.\n        stride (int | tuple[int]): Stride of the convolution.\n        padding (int | tuple[int]): Zero-padding added to both sides of\n        dilation (int | tuple[int]): Spacing between kernel elements.\n        groups (int): Number of blocked connections from input channels to\n            output channels.\n        groups (int): Same as nn.Conv2d.\n        radix (int): Radix of SpltAtConv2d. Default: 2\n        reduction_factor (int): Reduction factor of inter_channels. Default: 4.\n        conv_cfg (dict): Config dict for convolution layer. Default: None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer. Default: None.\n        dcn (dict): Config dict for DCN. Default: None.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 channels,\n                 kernel_size,\n                 stride=1,\n                 padding=0,\n                 dilation=1,\n                 groups=1,\n                 radix=2,\n                 reduction_factor=4,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 dcn=None,\n                 init_cfg=None):\n        super(SplitAttentionConv2d, self).__init__(init_cfg)\n        inter_channels = max(in_channels * radix // reduction_factor, 32)\n        self.radix = radix\n        self.groups = groups\n        self.channels = channels\n        self.with_dcn = dcn is not None\n        self.dcn = dcn\n        fallback_on_stride = False\n        if self.with_dcn:\n            fallback_on_stride = self.dcn.pop('fallback_on_stride', False)\n        if self.with_dcn and not fallback_on_stride:\n            assert conv_cfg is None, 'conv_cfg must be None for DCN'\n            conv_cfg = dcn\n        self.conv = build_conv_layer(\n            conv_cfg,\n            in_channels,\n            channels * radix,\n            kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            groups=groups * radix,\n            bias=False)\n        # To be consistent with original implementation, starting from 0\n        self.norm0_name, norm0 = build_norm_layer(\n            norm_cfg, channels * radix, postfix=0)\n        self.add_module(self.norm0_name, norm0)\n        self.relu = nn.ReLU(inplace=True)\n        self.fc1 = build_conv_layer(\n            None, channels, inter_channels, 1, groups=self.groups)\n        self.norm1_name, norm1 = build_norm_layer(\n            norm_cfg, inter_channels, postfix=1)\n        self.add_module(self.norm1_name, norm1)\n        self.fc2 = build_conv_layer(\n            None, inter_channels, channels * radix, 1, groups=self.groups)\n        self.rsoftmax = RSoftmax(radix, groups)\n\n    @property\n    def norm0(self):\n        \"\"\"nn.Module: the normalization layer named \"norm0\" \"\"\"\n        return getattr(self, self.norm0_name)\n\n    @property\n    def norm1(self):\n        \"\"\"nn.Module: the normalization layer named \"norm1\" \"\"\"\n        return getattr(self, self.norm1_name)\n\n    def forward(self, x):\n        x = self.conv(x)\n        x = self.norm0(x)\n        x = self.relu(x)\n\n        batch, rchannel = x.shape[:2]\n        batch = x.size(0)\n        if self.radix > 1:\n            splits = x.view(batch, self.radix, -1, *x.shape[2:])\n            gap = splits.sum(dim=1)\n        else:\n            gap = x\n        gap = F.adaptive_avg_pool2d(gap, 1)\n        gap = self.fc1(gap)\n\n        gap = self.norm1(gap)\n        gap = self.relu(gap)\n\n        atten = self.fc2(gap)\n        atten = self.rsoftmax(atten).view(batch, -1, 1, 1)\n\n        if self.radix > 1:\n            attens = atten.view(batch, self.radix, -1, *atten.shape[2:])\n            out = torch.sum(attens * splits, dim=1)\n        else:\n            out = atten * x\n        return out.contiguous()\n\n\nclass Bottleneck(_Bottleneck):\n    \"\"\"Bottleneck block for ResNeSt.\n\n    Args:\n        inplane (int): Input planes of this block.\n        planes (int): Middle planes of this block.\n        groups (int): Groups of conv2.\n        base_width (int): Base of width in terms of base channels. Default: 4.\n        base_channels (int): Base of channels for calculating width.\n            Default: 64.\n        radix (int): Radix of SpltAtConv2d. Default: 2\n        reduction_factor (int): Reduction factor of inter_channels in\n            SplitAttentionConv2d. Default: 4.\n        avg_down_stride (bool): Whether to use average pool for stride in\n            Bottleneck. Default: True.\n        kwargs (dict): Key word arguments for base class.\n    \"\"\"\n    expansion = 4\n\n    def __init__(self,\n                 inplanes,\n                 planes,\n                 groups=1,\n                 base_width=4,\n                 base_channels=64,\n                 radix=2,\n                 reduction_factor=4,\n                 avg_down_stride=True,\n                 **kwargs):\n        \"\"\"Bottleneck block for ResNeSt.\"\"\"\n        super(Bottleneck, self).__init__(inplanes, planes, **kwargs)\n\n        if groups == 1:\n            width = self.planes\n        else:\n            width = math.floor(self.planes *\n                               (base_width / base_channels)) * groups\n\n        self.avg_down_stride = avg_down_stride and self.conv2_stride > 1\n\n        self.norm1_name, norm1 = build_norm_layer(\n            self.norm_cfg, width, postfix=1)\n        self.norm3_name, norm3 = build_norm_layer(\n            self.norm_cfg, self.planes * self.expansion, postfix=3)\n\n        self.conv1 = build_conv_layer(\n            self.conv_cfg,\n            self.inplanes,\n            width,\n            kernel_size=1,\n            stride=self.conv1_stride,\n            bias=False)\n        self.add_module(self.norm1_name, norm1)\n        self.with_modulated_dcn = False\n        self.conv2 = SplitAttentionConv2d(\n            width,\n            width,\n            kernel_size=3,\n            stride=1 if self.avg_down_stride else self.conv2_stride,\n            padding=self.dilation,\n            dilation=self.dilation,\n            groups=groups,\n            radix=radix,\n            reduction_factor=reduction_factor,\n            conv_cfg=self.conv_cfg,\n            norm_cfg=self.norm_cfg,\n            dcn=self.dcn)\n        delattr(self, self.norm2_name)\n\n        if self.avg_down_stride:\n            self.avd_layer = nn.AvgPool2d(3, self.conv2_stride, padding=1)\n\n        self.conv3 = build_conv_layer(\n            self.conv_cfg,\n            width,\n            self.planes * self.expansion,\n            kernel_size=1,\n            bias=False)\n        self.add_module(self.norm3_name, norm3)\n\n    def forward(self, x):\n\n        def _inner_forward(x):\n            identity = x\n\n            out = self.conv1(x)\n            out = self.norm1(out)\n            out = self.relu(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv1_plugin_names)\n\n            out = self.conv2(out)\n\n            if self.avg_down_stride:\n                out = self.avd_layer(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv2_plugin_names)\n\n            out = self.conv3(out)\n            out = self.norm3(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv3_plugin_names)\n\n            if self.downsample is not None:\n                identity = self.downsample(x)\n\n            out += identity\n\n            return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        out = self.relu(out)\n\n        return out\n\n\n@MODELS.register_module()\nclass ResNeSt(ResNetV1d):\n    \"\"\"ResNeSt backbone.\n\n    Args:\n        groups (int): Number of groups of Bottleneck. Default: 1\n        base_width (int): Base width of Bottleneck. Default: 4\n        radix (int): Radix of SplitAttentionConv2d. Default: 2\n        reduction_factor (int): Reduction factor of inter_channels in\n            SplitAttentionConv2d. Default: 4.\n        avg_down_stride (bool): Whether to use average pool for stride in\n            Bottleneck. Default: True.\n        kwargs (dict): Keyword arguments for ResNet.\n    \"\"\"\n\n    arch_settings = {\n        50: (Bottleneck, (3, 4, 6, 3)),\n        101: (Bottleneck, (3, 4, 23, 3)),\n        152: (Bottleneck, (3, 8, 36, 3)),\n        200: (Bottleneck, (3, 24, 36, 3))\n    }\n\n    def __init__(self,\n                 groups=1,\n                 base_width=4,\n                 radix=2,\n                 reduction_factor=4,\n                 avg_down_stride=True,\n                 **kwargs):\n        self.groups = groups\n        self.base_width = base_width\n        self.radix = radix\n        self.reduction_factor = reduction_factor\n        self.avg_down_stride = avg_down_stride\n        super(ResNeSt, self).__init__(**kwargs)\n\n    def make_res_layer(self, **kwargs):\n        \"\"\"Pack all blocks in a stage into a ``ResLayer``.\"\"\"\n        return ResLayer(\n            groups=self.groups,\n            base_width=self.base_width,\n            base_channels=self.base_channels,\n            radix=self.radix,\n            reduction_factor=self.reduction_factor,\n            avg_down_stride=self.avg_down_stride,\n            **kwargs)\n"
  },
  {
    "path": "mmdet/models/backbones/resnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport torch.nn as nn\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer\nfrom mmengine.model import BaseModule\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.registry import MODELS\nfrom ..layers import ResLayer\n\n\nclass BasicBlock(BaseModule):\n    expansion = 1\n\n    def __init__(self,\n                 inplanes,\n                 planes,\n                 stride=1,\n                 dilation=1,\n                 downsample=None,\n                 style='pytorch',\n                 with_cp=False,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 dcn=None,\n                 plugins=None,\n                 init_cfg=None):\n        super(BasicBlock, self).__init__(init_cfg)\n        assert dcn is None, 'Not implemented yet.'\n        assert plugins is None, 'Not implemented yet.'\n\n        self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1)\n        self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2)\n\n        self.conv1 = build_conv_layer(\n            conv_cfg,\n            inplanes,\n            planes,\n            3,\n            stride=stride,\n            padding=dilation,\n            dilation=dilation,\n            bias=False)\n        self.add_module(self.norm1_name, norm1)\n        self.conv2 = build_conv_layer(\n            conv_cfg, planes, planes, 3, padding=1, bias=False)\n        self.add_module(self.norm2_name, norm2)\n\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n        self.stride = stride\n        self.dilation = dilation\n        self.with_cp = with_cp\n\n    @property\n    def norm1(self):\n        \"\"\"nn.Module: normalization layer after the first convolution layer\"\"\"\n        return getattr(self, self.norm1_name)\n\n    @property\n    def norm2(self):\n        \"\"\"nn.Module: normalization layer after the second convolution layer\"\"\"\n        return getattr(self, self.norm2_name)\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n\n        def _inner_forward(x):\n            identity = x\n\n            out = self.conv1(x)\n            out = self.norm1(out)\n            out = self.relu(out)\n\n            out = self.conv2(out)\n            out = self.norm2(out)\n\n            if self.downsample is not None:\n                identity = self.downsample(x)\n\n            out += identity\n\n            return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        out = self.relu(out)\n\n        return out\n\n\nclass Bottleneck(BaseModule):\n    expansion = 4\n\n    def __init__(self,\n                 inplanes,\n                 planes,\n                 stride=1,\n                 dilation=1,\n                 downsample=None,\n                 style='pytorch',\n                 with_cp=False,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 dcn=None,\n                 plugins=None,\n                 init_cfg=None):\n        \"\"\"Bottleneck block for ResNet.\n\n        If style is \"pytorch\", the stride-two layer is the 3x3 conv layer, if\n        it is \"caffe\", the stride-two layer is the first 1x1 conv layer.\n        \"\"\"\n        super(Bottleneck, self).__init__(init_cfg)\n        assert style in ['pytorch', 'caffe']\n        assert dcn is None or isinstance(dcn, dict)\n        assert plugins is None or isinstance(plugins, list)\n        if plugins is not None:\n            allowed_position = ['after_conv1', 'after_conv2', 'after_conv3']\n            assert all(p['position'] in allowed_position for p in plugins)\n\n        self.inplanes = inplanes\n        self.planes = planes\n        self.stride = stride\n        self.dilation = dilation\n        self.style = style\n        self.with_cp = with_cp\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.dcn = dcn\n        self.with_dcn = dcn is not None\n        self.plugins = plugins\n        self.with_plugins = plugins is not None\n\n        if self.with_plugins:\n            # collect plugins for conv1/conv2/conv3\n            self.after_conv1_plugins = [\n                plugin['cfg'] for plugin in plugins\n                if plugin['position'] == 'after_conv1'\n            ]\n            self.after_conv2_plugins = [\n                plugin['cfg'] for plugin in plugins\n                if plugin['position'] == 'after_conv2'\n            ]\n            self.after_conv3_plugins = [\n                plugin['cfg'] for plugin in plugins\n                if plugin['position'] == 'after_conv3'\n            ]\n\n        if self.style == 'pytorch':\n            self.conv1_stride = 1\n            self.conv2_stride = stride\n        else:\n            self.conv1_stride = stride\n            self.conv2_stride = 1\n\n        self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1)\n        self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2)\n        self.norm3_name, norm3 = build_norm_layer(\n            norm_cfg, planes * self.expansion, postfix=3)\n\n        self.conv1 = build_conv_layer(\n            conv_cfg,\n            inplanes,\n            planes,\n            kernel_size=1,\n            stride=self.conv1_stride,\n            bias=False)\n        self.add_module(self.norm1_name, norm1)\n        fallback_on_stride = False\n        if self.with_dcn:\n            fallback_on_stride = dcn.pop('fallback_on_stride', False)\n        if not self.with_dcn or fallback_on_stride:\n            self.conv2 = build_conv_layer(\n                conv_cfg,\n                planes,\n                planes,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=dilation,\n                dilation=dilation,\n                bias=False)\n        else:\n            assert self.conv_cfg is None, 'conv_cfg must be None for DCN'\n            self.conv2 = build_conv_layer(\n                dcn,\n                planes,\n                planes,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=dilation,\n                dilation=dilation,\n                bias=False)\n\n        self.add_module(self.norm2_name, norm2)\n        self.conv3 = build_conv_layer(\n            conv_cfg,\n            planes,\n            planes * self.expansion,\n            kernel_size=1,\n            bias=False)\n        self.add_module(self.norm3_name, norm3)\n\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n\n        if self.with_plugins:\n            self.after_conv1_plugin_names = self.make_block_plugins(\n                planes, self.after_conv1_plugins)\n            self.after_conv2_plugin_names = self.make_block_plugins(\n                planes, self.after_conv2_plugins)\n            self.after_conv3_plugin_names = self.make_block_plugins(\n                planes * self.expansion, self.after_conv3_plugins)\n\n    def make_block_plugins(self, in_channels, plugins):\n        \"\"\"make plugins for block.\n\n        Args:\n            in_channels (int): Input channels of plugin.\n            plugins (list[dict]): List of plugins cfg to build.\n\n        Returns:\n            list[str]: List of the names of plugin.\n        \"\"\"\n        assert isinstance(plugins, list)\n        plugin_names = []\n        for plugin in plugins:\n            plugin = plugin.copy()\n            name, layer = build_plugin_layer(\n                plugin,\n                in_channels=in_channels,\n                postfix=plugin.pop('postfix', ''))\n            assert not hasattr(self, name), f'duplicate plugin {name}'\n            self.add_module(name, layer)\n            plugin_names.append(name)\n        return plugin_names\n\n    def forward_plugin(self, x, plugin_names):\n        out = x\n        for name in plugin_names:\n            out = getattr(self, name)(out)\n        return out\n\n    @property\n    def norm1(self):\n        \"\"\"nn.Module: normalization layer after the first convolution layer\"\"\"\n        return getattr(self, self.norm1_name)\n\n    @property\n    def norm2(self):\n        \"\"\"nn.Module: normalization layer after the second convolution layer\"\"\"\n        return getattr(self, self.norm2_name)\n\n    @property\n    def norm3(self):\n        \"\"\"nn.Module: normalization layer after the third convolution layer\"\"\"\n        return getattr(self, self.norm3_name)\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n\n        def _inner_forward(x):\n            identity = x\n            out = self.conv1(x)\n            out = self.norm1(out)\n            out = self.relu(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv1_plugin_names)\n\n            out = self.conv2(out)\n            out = self.norm2(out)\n            out = self.relu(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv2_plugin_names)\n\n            out = self.conv3(out)\n            out = self.norm3(out)\n\n            if self.with_plugins:\n                out = self.forward_plugin(out, self.after_conv3_plugin_names)\n\n            if self.downsample is not None:\n                identity = self.downsample(x)\n\n            out += identity\n\n            return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        out = self.relu(out)\n\n        return out\n\n\n@MODELS.register_module()\nclass ResNet(BaseModule):\n    \"\"\"ResNet backbone.\n\n    Args:\n        depth (int): Depth of resnet, from {18, 34, 50, 101, 152}.\n        stem_channels (int | None): Number of stem channels. If not specified,\n            it will be the same as `base_channels`. Default: None.\n        base_channels (int): Number of base channels of res layer. Default: 64.\n        in_channels (int): Number of input image channels. Default: 3.\n        num_stages (int): Resnet stages. Default: 4.\n        strides (Sequence[int]): Strides of the first block of each stage.\n        dilations (Sequence[int]): Dilation of each stage.\n        out_indices (Sequence[int]): Output from which stages.\n        style (str): `pytorch` or `caffe`. If set to \"pytorch\", the stride-two\n            layer is the 3x3 conv layer, otherwise the stride-two layer is\n            the first 1x1 conv layer.\n        deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv\n        avg_down (bool): Use AvgPool instead of stride conv when\n            downsampling in the bottleneck.\n        frozen_stages (int): Stages to be frozen (stop grad and set eval mode).\n            -1 means not freezing any parameters.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only.\n        plugins (list[dict]): List of plugins for stages, each dict contains:\n\n            - cfg (dict, required): Cfg dict to build plugin.\n            - position (str, required): Position inside block to insert\n              plugin, options are 'after_conv1', 'after_conv2', 'after_conv3'.\n            - stages (tuple[bool], optional): Stages to apply plugin, length\n              should be same as 'num_stages'.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed.\n        zero_init_residual (bool): Whether to use zero init for last norm layer\n            in resblocks to let them behave as identity.\n        pretrained (str, optional): model pretrained path. Default: None\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n\n    Example:\n        >>> from mmdet.models import ResNet\n        >>> import torch\n        >>> self = ResNet(depth=18)\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 3, 32, 32)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_out in level_outputs:\n        ...     print(tuple(level_out.shape))\n        (1, 64, 8, 8)\n        (1, 128, 4, 4)\n        (1, 256, 2, 2)\n        (1, 512, 1, 1)\n    \"\"\"\n\n    arch_settings = {\n        18: (BasicBlock, (2, 2, 2, 2)),\n        34: (BasicBlock, (3, 4, 6, 3)),\n        50: (Bottleneck, (3, 4, 6, 3)),\n        101: (Bottleneck, (3, 4, 23, 3)),\n        152: (Bottleneck, (3, 8, 36, 3))\n    }\n\n    def __init__(self,\n                 depth,\n                 in_channels=3,\n                 stem_channels=None,\n                 base_channels=64,\n                 num_stages=4,\n                 strides=(1, 2, 2, 2),\n                 dilations=(1, 1, 1, 1),\n                 out_indices=(0, 1, 2, 3),\n                 style='pytorch',\n                 deep_stem=False,\n                 avg_down=False,\n                 frozen_stages=-1,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', requires_grad=True),\n                 norm_eval=True,\n                 dcn=None,\n                 stage_with_dcn=(False, False, False, False),\n                 plugins=None,\n                 with_cp=False,\n                 zero_init_residual=True,\n                 pretrained=None,\n                 init_cfg=None):\n        super(ResNet, self).__init__(init_cfg)\n        self.zero_init_residual = zero_init_residual\n        if depth not in self.arch_settings:\n            raise KeyError(f'invalid depth {depth} for resnet')\n\n        block_init_cfg = None\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            if init_cfg is None:\n                self.init_cfg = [\n                    dict(type='Kaiming', layer='Conv2d'),\n                    dict(\n                        type='Constant',\n                        val=1,\n                        layer=['_BatchNorm', 'GroupNorm'])\n                ]\n                block = self.arch_settings[depth][0]\n                if self.zero_init_residual:\n                    if block is BasicBlock:\n                        block_init_cfg = dict(\n                            type='Constant',\n                            val=0,\n                            override=dict(name='norm2'))\n                    elif block is Bottleneck:\n                        block_init_cfg = dict(\n                            type='Constant',\n                            val=0,\n                            override=dict(name='norm3'))\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n        self.depth = depth\n        if stem_channels is None:\n            stem_channels = base_channels\n        self.stem_channels = stem_channels\n        self.base_channels = base_channels\n        self.num_stages = num_stages\n        assert num_stages >= 1 and num_stages <= 4\n        self.strides = strides\n        self.dilations = dilations\n        assert len(strides) == len(dilations) == num_stages\n        self.out_indices = out_indices\n        assert max(out_indices) < num_stages\n        self.style = style\n        self.deep_stem = deep_stem\n        self.avg_down = avg_down\n        self.frozen_stages = frozen_stages\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.with_cp = with_cp\n        self.norm_eval = norm_eval\n        self.dcn = dcn\n        self.stage_with_dcn = stage_with_dcn\n        if dcn is not None:\n            assert len(stage_with_dcn) == num_stages\n        self.plugins = plugins\n        self.block, stage_blocks = self.arch_settings[depth]\n        self.stage_blocks = stage_blocks[:num_stages]\n        self.inplanes = stem_channels\n\n        self._make_stem_layer(in_channels, stem_channels)\n\n        self.res_layers = []\n        for i, num_blocks in enumerate(self.stage_blocks):\n            stride = strides[i]\n            dilation = dilations[i]\n            dcn = self.dcn if self.stage_with_dcn[i] else None\n            if plugins is not None:\n                stage_plugins = self.make_stage_plugins(plugins, i)\n            else:\n                stage_plugins = None\n            planes = base_channels * 2**i\n            res_layer = self.make_res_layer(\n                block=self.block,\n                inplanes=self.inplanes,\n                planes=planes,\n                num_blocks=num_blocks,\n                stride=stride,\n                dilation=dilation,\n                style=self.style,\n                avg_down=self.avg_down,\n                with_cp=with_cp,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                dcn=dcn,\n                plugins=stage_plugins,\n                init_cfg=block_init_cfg)\n            self.inplanes = planes * self.block.expansion\n            layer_name = f'layer{i + 1}'\n            self.add_module(layer_name, res_layer)\n            self.res_layers.append(layer_name)\n\n        self._freeze_stages()\n\n        self.feat_dim = self.block.expansion * base_channels * 2**(\n            len(self.stage_blocks) - 1)\n\n    def make_stage_plugins(self, plugins, stage_idx):\n        \"\"\"Make plugins for ResNet ``stage_idx`` th stage.\n\n        Currently we support to insert ``context_block``,\n        ``empirical_attention_block``, ``nonlocal_block`` into the backbone\n        like ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of\n        Bottleneck.\n\n        An example of plugins format could be:\n\n        Examples:\n            >>> plugins=[\n            ...     dict(cfg=dict(type='xxx', arg1='xxx'),\n            ...          stages=(False, True, True, True),\n            ...          position='after_conv2'),\n            ...     dict(cfg=dict(type='yyy'),\n            ...          stages=(True, True, True, True),\n            ...          position='after_conv3'),\n            ...     dict(cfg=dict(type='zzz', postfix='1'),\n            ...          stages=(True, True, True, True),\n            ...          position='after_conv3'),\n            ...     dict(cfg=dict(type='zzz', postfix='2'),\n            ...          stages=(True, True, True, True),\n            ...          position='after_conv3')\n            ... ]\n            >>> self = ResNet(depth=18)\n            >>> stage_plugins = self.make_stage_plugins(plugins, 0)\n            >>> assert len(stage_plugins) == 3\n\n        Suppose ``stage_idx=0``, the structure of blocks in the stage would be:\n\n        .. code-block:: none\n\n            conv1-> conv2->conv3->yyy->zzz1->zzz2\n\n        Suppose 'stage_idx=1', the structure of blocks in the stage would be:\n\n        .. code-block:: none\n\n            conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2\n\n        If stages is missing, the plugin would be applied to all stages.\n\n        Args:\n            plugins (list[dict]): List of plugins cfg to build. The postfix is\n                required if multiple same type plugins are inserted.\n            stage_idx (int): Index of stage to build\n\n        Returns:\n            list[dict]: Plugins for current stage\n        \"\"\"\n        stage_plugins = []\n        for plugin in plugins:\n            plugin = plugin.copy()\n            stages = plugin.pop('stages', None)\n            assert stages is None or len(stages) == self.num_stages\n            # whether to insert plugin into current stage\n            if stages is None or stages[stage_idx]:\n                stage_plugins.append(plugin)\n\n        return stage_plugins\n\n    def make_res_layer(self, **kwargs):\n        \"\"\"Pack all blocks in a stage into a ``ResLayer``.\"\"\"\n        return ResLayer(**kwargs)\n\n    @property\n    def norm1(self):\n        \"\"\"nn.Module: the normalization layer named \"norm1\" \"\"\"\n        return getattr(self, self.norm1_name)\n\n    def _make_stem_layer(self, in_channels, stem_channels):\n        if self.deep_stem:\n            self.stem = nn.Sequential(\n                build_conv_layer(\n                    self.conv_cfg,\n                    in_channels,\n                    stem_channels // 2,\n                    kernel_size=3,\n                    stride=2,\n                    padding=1,\n                    bias=False),\n                build_norm_layer(self.norm_cfg, stem_channels // 2)[1],\n                nn.ReLU(inplace=True),\n                build_conv_layer(\n                    self.conv_cfg,\n                    stem_channels // 2,\n                    stem_channels // 2,\n                    kernel_size=3,\n                    stride=1,\n                    padding=1,\n                    bias=False),\n                build_norm_layer(self.norm_cfg, stem_channels // 2)[1],\n                nn.ReLU(inplace=True),\n                build_conv_layer(\n                    self.conv_cfg,\n                    stem_channels // 2,\n                    stem_channels,\n                    kernel_size=3,\n                    stride=1,\n                    padding=1,\n                    bias=False),\n                build_norm_layer(self.norm_cfg, stem_channels)[1],\n                nn.ReLU(inplace=True))\n        else:\n            self.conv1 = build_conv_layer(\n                self.conv_cfg,\n                in_channels,\n                stem_channels,\n                kernel_size=7,\n                stride=2,\n                padding=3,\n                bias=False)\n            self.norm1_name, norm1 = build_norm_layer(\n                self.norm_cfg, stem_channels, postfix=1)\n            self.add_module(self.norm1_name, norm1)\n            self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n\n    def _freeze_stages(self):\n        if self.frozen_stages >= 0:\n            if self.deep_stem:\n                self.stem.eval()\n                for param in self.stem.parameters():\n                    param.requires_grad = False\n            else:\n                self.norm1.eval()\n                for m in [self.conv1, self.norm1]:\n                    for param in m.parameters():\n                        param.requires_grad = False\n\n        for i in range(1, self.frozen_stages + 1):\n            m = getattr(self, f'layer{i}')\n            m.eval()\n            for param in m.parameters():\n                param.requires_grad = False\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        if self.deep_stem:\n            x = self.stem(x)\n        else:\n            x = self.conv1(x)\n            x = self.norm1(x)\n            x = self.relu(x)\n        x = self.maxpool(x)\n        outs = []\n        for i, layer_name in enumerate(self.res_layers):\n            res_layer = getattr(self, layer_name)\n            x = res_layer(x)\n            if i in self.out_indices:\n                outs.append(x)\n        return tuple(outs)\n\n    def train(self, mode=True):\n        \"\"\"Convert the model into training mode while keep normalization layer\n        freezed.\"\"\"\n        super(ResNet, self).train(mode)\n        self._freeze_stages()\n        if mode and self.norm_eval:\n            for m in self.modules():\n                # trick: eval have effect on BatchNorm only\n                if isinstance(m, _BatchNorm):\n                    m.eval()\n\n\n@MODELS.register_module()\nclass ResNetV1d(ResNet):\n    r\"\"\"ResNetV1d variant described in `Bag of Tricks\n    <https://arxiv.org/pdf/1812.01187.pdf>`_.\n\n    Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in\n    the input stem with three 3x3 convs. And in the downsampling block, a 2x2\n    avg_pool with stride 2 is added before conv, whose stride is changed to 1.\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        super(ResNetV1d, self).__init__(\n            deep_stem=True, avg_down=True, **kwargs)\n"
  },
  {
    "path": "mmdet/models/backbones/resnext.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\n\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\n\nfrom mmdet.registry import MODELS\nfrom ..layers import ResLayer\nfrom .resnet import Bottleneck as _Bottleneck\nfrom .resnet import ResNet\n\n\nclass Bottleneck(_Bottleneck):\n    expansion = 4\n\n    def __init__(self,\n                 inplanes,\n                 planes,\n                 groups=1,\n                 base_width=4,\n                 base_channels=64,\n                 **kwargs):\n        \"\"\"Bottleneck block for ResNeXt.\n\n        If style is \"pytorch\", the stride-two layer is the 3x3 conv layer, if\n        it is \"caffe\", the stride-two layer is the first 1x1 conv layer.\n        \"\"\"\n        super(Bottleneck, self).__init__(inplanes, planes, **kwargs)\n\n        if groups == 1:\n            width = self.planes\n        else:\n            width = math.floor(self.planes *\n                               (base_width / base_channels)) * groups\n\n        self.norm1_name, norm1 = build_norm_layer(\n            self.norm_cfg, width, postfix=1)\n        self.norm2_name, norm2 = build_norm_layer(\n            self.norm_cfg, width, postfix=2)\n        self.norm3_name, norm3 = build_norm_layer(\n            self.norm_cfg, self.planes * self.expansion, postfix=3)\n\n        self.conv1 = build_conv_layer(\n            self.conv_cfg,\n            self.inplanes,\n            width,\n            kernel_size=1,\n            stride=self.conv1_stride,\n            bias=False)\n        self.add_module(self.norm1_name, norm1)\n        fallback_on_stride = False\n        self.with_modulated_dcn = False\n        if self.with_dcn:\n            fallback_on_stride = self.dcn.pop('fallback_on_stride', False)\n        if not self.with_dcn or fallback_on_stride:\n            self.conv2 = build_conv_layer(\n                self.conv_cfg,\n                width,\n                width,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=self.dilation,\n                dilation=self.dilation,\n                groups=groups,\n                bias=False)\n        else:\n            assert self.conv_cfg is None, 'conv_cfg must be None for DCN'\n            self.conv2 = build_conv_layer(\n                self.dcn,\n                width,\n                width,\n                kernel_size=3,\n                stride=self.conv2_stride,\n                padding=self.dilation,\n                dilation=self.dilation,\n                groups=groups,\n                bias=False)\n\n        self.add_module(self.norm2_name, norm2)\n        self.conv3 = build_conv_layer(\n            self.conv_cfg,\n            width,\n            self.planes * self.expansion,\n            kernel_size=1,\n            bias=False)\n        self.add_module(self.norm3_name, norm3)\n\n        if self.with_plugins:\n            self._del_block_plugins(self.after_conv1_plugin_names +\n                                    self.after_conv2_plugin_names +\n                                    self.after_conv3_plugin_names)\n            self.after_conv1_plugin_names = self.make_block_plugins(\n                width, self.after_conv1_plugins)\n            self.after_conv2_plugin_names = self.make_block_plugins(\n                width, self.after_conv2_plugins)\n            self.after_conv3_plugin_names = self.make_block_plugins(\n                self.planes * self.expansion, self.after_conv3_plugins)\n\n    def _del_block_plugins(self, plugin_names):\n        \"\"\"delete plugins for block if exist.\n\n        Args:\n            plugin_names (list[str]): List of plugins name to delete.\n        \"\"\"\n        assert isinstance(plugin_names, list)\n        for plugin_name in plugin_names:\n            del self._modules[plugin_name]\n\n\n@MODELS.register_module()\nclass ResNeXt(ResNet):\n    \"\"\"ResNeXt backbone.\n\n    Args:\n        depth (int): Depth of resnet, from {18, 34, 50, 101, 152}.\n        in_channels (int): Number of input image channels. Default: 3.\n        num_stages (int): Resnet stages. Default: 4.\n        groups (int): Group of resnext.\n        base_width (int): Base width of resnext.\n        strides (Sequence[int]): Strides of the first block of each stage.\n        dilations (Sequence[int]): Dilation of each stage.\n        out_indices (Sequence[int]): Output from which stages.\n        style (str): `pytorch` or `caffe`. If set to \"pytorch\", the stride-two\n            layer is the 3x3 conv layer, otherwise the stride-two layer is\n            the first 1x1 conv layer.\n        frozen_stages (int): Stages to be frozen (all param fixed). -1 means\n            not freezing any parameters.\n        norm_cfg (dict): dictionary to construct and config norm layer.\n        norm_eval (bool): Whether to set norm layers to eval mode, namely,\n            freeze running stats (mean and var). Note: Effect on Batch Norm\n            and its variants only.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed.\n        zero_init_residual (bool): whether to use zero init for last norm layer\n            in resblocks to let them behave as identity.\n    \"\"\"\n\n    arch_settings = {\n        50: (Bottleneck, (3, 4, 6, 3)),\n        101: (Bottleneck, (3, 4, 23, 3)),\n        152: (Bottleneck, (3, 8, 36, 3))\n    }\n\n    def __init__(self, groups=1, base_width=4, **kwargs):\n        self.groups = groups\n        self.base_width = base_width\n        super(ResNeXt, self).__init__(**kwargs)\n\n    def make_res_layer(self, **kwargs):\n        \"\"\"Pack all blocks in a stage into a ``ResLayer``\"\"\"\n        return ResLayer(\n            groups=self.groups,\n            base_width=self.base_width,\n            base_channels=self.base_channels,\n            **kwargs)\n"
  },
  {
    "path": "mmdet/models/backbones/ssd_vgg.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport torch.nn as nn\nfrom mmcv.cnn import VGG\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom ..necks import ssd_neck\n\n\n@MODELS.register_module()\nclass SSDVGG(VGG, BaseModule):\n    \"\"\"VGG Backbone network for single-shot-detection.\n\n    Args:\n        depth (int): Depth of vgg, from {11, 13, 16, 19}.\n        with_last_pool (bool): Whether to add a pooling layer at the last\n            of the model\n        ceil_mode (bool): When True, will use `ceil` instead of `floor`\n            to compute the output shape.\n        out_indices (Sequence[int]): Output from which stages.\n        out_feature_indices (Sequence[int]): Output from which feature map.\n        pretrained (str, optional): model pretrained path. Default: None\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n        input_size (int, optional): Deprecated argumment.\n            Width and height of input, from {300, 512}.\n        l2_norm_scale (float, optional) : Deprecated argumment.\n            L2 normalization layer init scale.\n\n    Example:\n        >>> self = SSDVGG(input_size=300, depth=11)\n        >>> self.eval()\n        >>> inputs = torch.rand(1, 3, 300, 300)\n        >>> level_outputs = self.forward(inputs)\n        >>> for level_out in level_outputs:\n        ...     print(tuple(level_out.shape))\n        (1, 1024, 19, 19)\n        (1, 512, 10, 10)\n        (1, 256, 5, 5)\n        (1, 256, 3, 3)\n        (1, 256, 1, 1)\n    \"\"\"\n    extra_setting = {\n        300: (256, 'S', 512, 128, 'S', 256, 128, 256, 128, 256),\n        512: (256, 'S', 512, 128, 'S', 256, 128, 'S', 256, 128, 'S', 256, 128),\n    }\n\n    def __init__(self,\n                 depth,\n                 with_last_pool=False,\n                 ceil_mode=True,\n                 out_indices=(3, 4),\n                 out_feature_indices=(22, 34),\n                 pretrained=None,\n                 init_cfg=None,\n                 input_size=None,\n                 l2_norm_scale=None):\n        # TODO: in_channels for mmcv.VGG\n        super(SSDVGG, self).__init__(\n            depth,\n            with_last_pool=with_last_pool,\n            ceil_mode=ceil_mode,\n            out_indices=out_indices)\n\n        self.features.add_module(\n            str(len(self.features)),\n            nn.MaxPool2d(kernel_size=3, stride=1, padding=1))\n        self.features.add_module(\n            str(len(self.features)),\n            nn.Conv2d(512, 1024, kernel_size=3, padding=6, dilation=6))\n        self.features.add_module(\n            str(len(self.features)), nn.ReLU(inplace=True))\n        self.features.add_module(\n            str(len(self.features)), nn.Conv2d(1024, 1024, kernel_size=1))\n        self.features.add_module(\n            str(len(self.features)), nn.ReLU(inplace=True))\n        self.out_feature_indices = out_feature_indices\n\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n\n        if init_cfg is not None:\n            self.init_cfg = init_cfg\n        elif isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            self.init_cfg = [\n                dict(type='Kaiming', layer='Conv2d'),\n                dict(type='Constant', val=1, layer='BatchNorm2d'),\n                dict(type='Normal', std=0.01, layer='Linear'),\n            ]\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n        if input_size is not None:\n            warnings.warn('DeprecationWarning: input_size is deprecated')\n        if l2_norm_scale is not None:\n            warnings.warn('DeprecationWarning: l2_norm_scale in VGG is '\n                          'deprecated, it has been moved to SSDNeck.')\n\n    def init_weights(self, pretrained=None):\n        super(VGG, self).init_weights()\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        outs = []\n        for i, layer in enumerate(self.features):\n            x = layer(x)\n            if i in self.out_feature_indices:\n                outs.append(x)\n\n        if len(outs) == 1:\n            return outs[0]\n        else:\n            return tuple(outs)\n\n\nclass L2Norm(ssd_neck.L2Norm):\n\n    def __init__(self, **kwargs):\n        super(L2Norm, self).__init__(**kwargs)\n        warnings.warn('DeprecationWarning: L2Norm in ssd_vgg.py '\n                      'is deprecated, please use L2Norm in '\n                      'mmdet/models/necks/ssd_neck.py instead')\n"
  },
  {
    "path": "mmdet/models/backbones/swin.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom collections import OrderedDict\nfrom copy import deepcopy\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn import build_norm_layer\nfrom mmcv.cnn.bricks.transformer import FFN, build_dropout\nfrom mmengine.logging import MMLogger\nfrom mmengine.model import BaseModule, ModuleList\nfrom mmengine.model.weight_init import (constant_init, trunc_normal_,\n                                        trunc_normal_init)\nfrom mmengine.runner.checkpoint import CheckpointLoader\nfrom mmengine.utils import to_2tuple\n\nfrom mmdet.registry import MODELS\nfrom ..layers import PatchEmbed, PatchMerging\n\n\nclass WindowMSA(BaseModule):\n    \"\"\"Window based multi-head self-attention (W-MSA) module with relative\n    position bias.\n\n    Args:\n        embed_dims (int): Number of input channels.\n        num_heads (int): Number of attention heads.\n        window_size (tuple[int]): The height and width of the window.\n        qkv_bias (bool, optional):  If True, add a learnable bias to q, k, v.\n            Default: True.\n        qk_scale (float | None, optional): Override default qk scale of\n            head_dim ** -0.5 if set. Default: None.\n        attn_drop_rate (float, optional): Dropout ratio of attention weight.\n            Default: 0.0\n        proj_drop_rate (float, optional): Dropout ratio of output. Default: 0.\n        init_cfg (dict | None, optional): The Config for initialization.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims,\n                 num_heads,\n                 window_size,\n                 qkv_bias=True,\n                 qk_scale=None,\n                 attn_drop_rate=0.,\n                 proj_drop_rate=0.,\n                 init_cfg=None):\n\n        super().__init__()\n        self.embed_dims = embed_dims\n        self.window_size = window_size  # Wh, Ww\n        self.num_heads = num_heads\n        head_embed_dims = embed_dims // num_heads\n        self.scale = qk_scale or head_embed_dims**-0.5\n        self.init_cfg = init_cfg\n\n        # define a parameter table of relative position bias\n        self.relative_position_bias_table = nn.Parameter(\n            torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1),\n                        num_heads))  # 2*Wh-1 * 2*Ww-1, nH\n\n        # About 2x faster than original impl\n        Wh, Ww = self.window_size\n        rel_index_coords = self.double_step_seq(2 * Ww - 1, Wh, 1, Ww)\n        rel_position_index = rel_index_coords + rel_index_coords.T\n        rel_position_index = rel_position_index.flip(1).contiguous()\n        self.register_buffer('relative_position_index', rel_position_index)\n\n        self.qkv = nn.Linear(embed_dims, embed_dims * 3, bias=qkv_bias)\n        self.attn_drop = nn.Dropout(attn_drop_rate)\n        self.proj = nn.Linear(embed_dims, embed_dims)\n        self.proj_drop = nn.Dropout(proj_drop_rate)\n\n        self.softmax = nn.Softmax(dim=-1)\n\n    def init_weights(self):\n        trunc_normal_(self.relative_position_bias_table, std=0.02)\n\n    def forward(self, x, mask=None):\n        \"\"\"\n        Args:\n\n            x (tensor): input features with shape of (num_windows*B, N, C)\n            mask (tensor | None, Optional): mask with shape of (num_windows,\n                Wh*Ww, Wh*Ww), value should be between (-inf, 0].\n        \"\"\"\n        B, N, C = x.shape\n        qkv = self.qkv(x).reshape(B, N, 3, self.num_heads,\n                                  C // self.num_heads).permute(2, 0, 3, 1, 4)\n        # make torchscript happy (cannot use tensor as tuple)\n        q, k, v = qkv[0], qkv[1], qkv[2]\n\n        q = q * self.scale\n        attn = (q @ k.transpose(-2, -1))\n\n        relative_position_bias = self.relative_position_bias_table[\n            self.relative_position_index.view(-1)].view(\n                self.window_size[0] * self.window_size[1],\n                self.window_size[0] * self.window_size[1],\n                -1)  # Wh*Ww,Wh*Ww,nH\n        relative_position_bias = relative_position_bias.permute(\n            2, 0, 1).contiguous()  # nH, Wh*Ww, Wh*Ww\n        attn = attn + relative_position_bias.unsqueeze(0)\n\n        if mask is not None:\n            nW = mask.shape[0]\n            attn = attn.view(B // nW, nW, self.num_heads, N,\n                             N) + mask.unsqueeze(1).unsqueeze(0)\n            attn = attn.view(-1, self.num_heads, N, N)\n        attn = self.softmax(attn)\n\n        attn = self.attn_drop(attn)\n\n        x = (attn @ v).transpose(1, 2).reshape(B, N, C)\n        x = self.proj(x)\n        x = self.proj_drop(x)\n        return x\n\n    @staticmethod\n    def double_step_seq(step1, len1, step2, len2):\n        seq1 = torch.arange(0, step1 * len1, step1)\n        seq2 = torch.arange(0, step2 * len2, step2)\n        return (seq1[:, None] + seq2[None, :]).reshape(1, -1)\n\n\nclass ShiftWindowMSA(BaseModule):\n    \"\"\"Shifted Window Multihead Self-Attention Module.\n\n    Args:\n        embed_dims (int): Number of input channels.\n        num_heads (int): Number of attention heads.\n        window_size (int): The height and width of the window.\n        shift_size (int, optional): The shift step of each window towards\n            right-bottom. If zero, act as regular window-msa. Defaults to 0.\n        qkv_bias (bool, optional): If True, add a learnable bias to q, k, v.\n            Default: True\n        qk_scale (float | None, optional): Override default qk scale of\n            head_dim ** -0.5 if set. Defaults: None.\n        attn_drop_rate (float, optional): Dropout ratio of attention weight.\n            Defaults: 0.\n        proj_drop_rate (float, optional): Dropout ratio of output.\n            Defaults: 0.\n        dropout_layer (dict, optional): The dropout_layer used before output.\n            Defaults: dict(type='DropPath', drop_prob=0.).\n        init_cfg (dict, optional): The extra config for initialization.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims,\n                 num_heads,\n                 window_size,\n                 shift_size=0,\n                 qkv_bias=True,\n                 qk_scale=None,\n                 attn_drop_rate=0,\n                 proj_drop_rate=0,\n                 dropout_layer=dict(type='DropPath', drop_prob=0.),\n                 init_cfg=None):\n        super().__init__(init_cfg)\n\n        self.window_size = window_size\n        self.shift_size = shift_size\n        assert 0 <= self.shift_size < self.window_size\n\n        self.w_msa = WindowMSA(\n            embed_dims=embed_dims,\n            num_heads=num_heads,\n            window_size=to_2tuple(window_size),\n            qkv_bias=qkv_bias,\n            qk_scale=qk_scale,\n            attn_drop_rate=attn_drop_rate,\n            proj_drop_rate=proj_drop_rate,\n            init_cfg=None)\n\n        self.drop = build_dropout(dropout_layer)\n\n    def forward(self, query, hw_shape):\n        B, L, C = query.shape\n        H, W = hw_shape\n        assert L == H * W, 'input feature has wrong size'\n        query = query.view(B, H, W, C)\n\n        # pad feature maps to multiples of window size\n        pad_r = (self.window_size - W % self.window_size) % self.window_size\n        pad_b = (self.window_size - H % self.window_size) % self.window_size\n        query = F.pad(query, (0, 0, 0, pad_r, 0, pad_b))\n        H_pad, W_pad = query.shape[1], query.shape[2]\n\n        # cyclic shift\n        if self.shift_size > 0:\n            shifted_query = torch.roll(\n                query,\n                shifts=(-self.shift_size, -self.shift_size),\n                dims=(1, 2))\n\n            # calculate attention mask for SW-MSA\n            img_mask = torch.zeros((1, H_pad, W_pad, 1), device=query.device)\n            h_slices = (slice(0, -self.window_size),\n                        slice(-self.window_size,\n                              -self.shift_size), slice(-self.shift_size, None))\n            w_slices = (slice(0, -self.window_size),\n                        slice(-self.window_size,\n                              -self.shift_size), slice(-self.shift_size, None))\n            cnt = 0\n            for h in h_slices:\n                for w in w_slices:\n                    img_mask[:, h, w, :] = cnt\n                    cnt += 1\n\n            # nW, window_size, window_size, 1\n            mask_windows = self.window_partition(img_mask)\n            mask_windows = mask_windows.view(\n                -1, self.window_size * self.window_size)\n            attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)\n            attn_mask = attn_mask.masked_fill(attn_mask != 0,\n                                              float(-100.0)).masked_fill(\n                                                  attn_mask == 0, float(0.0))\n        else:\n            shifted_query = query\n            attn_mask = None\n\n        # nW*B, window_size, window_size, C\n        query_windows = self.window_partition(shifted_query)\n        # nW*B, window_size*window_size, C\n        query_windows = query_windows.view(-1, self.window_size**2, C)\n\n        # W-MSA/SW-MSA (nW*B, window_size*window_size, C)\n        attn_windows = self.w_msa(query_windows, mask=attn_mask)\n\n        # merge windows\n        attn_windows = attn_windows.view(-1, self.window_size,\n                                         self.window_size, C)\n\n        # B H' W' C\n        shifted_x = self.window_reverse(attn_windows, H_pad, W_pad)\n        # reverse cyclic shift\n        if self.shift_size > 0:\n            x = torch.roll(\n                shifted_x,\n                shifts=(self.shift_size, self.shift_size),\n                dims=(1, 2))\n        else:\n            x = shifted_x\n\n        if pad_r > 0 or pad_b:\n            x = x[:, :H, :W, :].contiguous()\n\n        x = x.view(B, H * W, C)\n\n        x = self.drop(x)\n        return x\n\n    def window_reverse(self, windows, H, W):\n        \"\"\"\n        Args:\n            windows: (num_windows*B, window_size, window_size, C)\n            H (int): Height of image\n            W (int): Width of image\n        Returns:\n            x: (B, H, W, C)\n        \"\"\"\n        window_size = self.window_size\n        B = int(windows.shape[0] / (H * W / window_size / window_size))\n        x = windows.view(B, H // window_size, W // window_size, window_size,\n                         window_size, -1)\n        x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1)\n        return x\n\n    def window_partition(self, x):\n        \"\"\"\n        Args:\n            x: (B, H, W, C)\n        Returns:\n            windows: (num_windows*B, window_size, window_size, C)\n        \"\"\"\n        B, H, W, C = x.shape\n        window_size = self.window_size\n        x = x.view(B, H // window_size, window_size, W // window_size,\n                   window_size, C)\n        windows = x.permute(0, 1, 3, 2, 4, 5).contiguous()\n        windows = windows.view(-1, window_size, window_size, C)\n        return windows\n\n\nclass SwinBlock(BaseModule):\n    \"\"\"\"\n    Args:\n        embed_dims (int): The feature dimension.\n        num_heads (int): Parallel attention heads.\n        feedforward_channels (int): The hidden dimension for FFNs.\n        window_size (int, optional): The local window scale. Default: 7.\n        shift (bool, optional): whether to shift window or not. Default False.\n        qkv_bias (bool, optional): enable bias for qkv if True. Default: True.\n        qk_scale (float | None, optional): Override default qk scale of\n            head_dim ** -0.5 if set. Default: None.\n        drop_rate (float, optional): Dropout rate. Default: 0.\n        attn_drop_rate (float, optional): Attention dropout rate. Default: 0.\n        drop_path_rate (float, optional): Stochastic depth rate. Default: 0.\n        act_cfg (dict, optional): The config dict of activation function.\n            Default: dict(type='GELU').\n        norm_cfg (dict, optional): The config dict of normalization.\n            Default: dict(type='LN').\n        with_cp (bool, optional): Use checkpoint or not. Using checkpoint\n            will save some memory while slowing down the training speed.\n            Default: False.\n        init_cfg (dict | list | None, optional): The init config.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims,\n                 num_heads,\n                 feedforward_channels,\n                 window_size=7,\n                 shift=False,\n                 qkv_bias=True,\n                 qk_scale=None,\n                 drop_rate=0.,\n                 attn_drop_rate=0.,\n                 drop_path_rate=0.,\n                 act_cfg=dict(type='GELU'),\n                 norm_cfg=dict(type='LN'),\n                 with_cp=False,\n                 init_cfg=None):\n\n        super(SwinBlock, self).__init__()\n\n        self.init_cfg = init_cfg\n        self.with_cp = with_cp\n\n        self.norm1 = build_norm_layer(norm_cfg, embed_dims)[1]\n        self.attn = ShiftWindowMSA(\n            embed_dims=embed_dims,\n            num_heads=num_heads,\n            window_size=window_size,\n            shift_size=window_size // 2 if shift else 0,\n            qkv_bias=qkv_bias,\n            qk_scale=qk_scale,\n            attn_drop_rate=attn_drop_rate,\n            proj_drop_rate=drop_rate,\n            dropout_layer=dict(type='DropPath', drop_prob=drop_path_rate),\n            init_cfg=None)\n\n        self.norm2 = build_norm_layer(norm_cfg, embed_dims)[1]\n        self.ffn = FFN(\n            embed_dims=embed_dims,\n            feedforward_channels=feedforward_channels,\n            num_fcs=2,\n            ffn_drop=drop_rate,\n            dropout_layer=dict(type='DropPath', drop_prob=drop_path_rate),\n            act_cfg=act_cfg,\n            add_identity=True,\n            init_cfg=None)\n\n    def forward(self, x, hw_shape):\n\n        def _inner_forward(x):\n            identity = x\n            x = self.norm1(x)\n            x = self.attn(x, hw_shape)\n\n            x = x + identity\n\n            identity = x\n            x = self.norm2(x)\n            x = self.ffn(x, identity=identity)\n\n            return x\n\n        if self.with_cp and x.requires_grad:\n            x = cp.checkpoint(_inner_forward, x)\n        else:\n            x = _inner_forward(x)\n\n        return x\n\n\nclass SwinBlockSequence(BaseModule):\n    \"\"\"Implements one stage in Swin Transformer.\n\n    Args:\n        embed_dims (int): The feature dimension.\n        num_heads (int): Parallel attention heads.\n        feedforward_channels (int): The hidden dimension for FFNs.\n        depth (int): The number of blocks in this stage.\n        window_size (int, optional): The local window scale. Default: 7.\n        qkv_bias (bool, optional): enable bias for qkv if True. Default: True.\n        qk_scale (float | None, optional): Override default qk scale of\n            head_dim ** -0.5 if set. Default: None.\n        drop_rate (float, optional): Dropout rate. Default: 0.\n        attn_drop_rate (float, optional): Attention dropout rate. Default: 0.\n        drop_path_rate (float | list[float], optional): Stochastic depth\n            rate. Default: 0.\n        downsample (BaseModule | None, optional): The downsample operation\n            module. Default: None.\n        act_cfg (dict, optional): The config dict of activation function.\n            Default: dict(type='GELU').\n        norm_cfg (dict, optional): The config dict of normalization.\n            Default: dict(type='LN').\n        with_cp (bool, optional): Use checkpoint or not. Using checkpoint\n            will save some memory while slowing down the training speed.\n            Default: False.\n        init_cfg (dict | list | None, optional): The init config.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims,\n                 num_heads,\n                 feedforward_channels,\n                 depth,\n                 window_size=7,\n                 qkv_bias=True,\n                 qk_scale=None,\n                 drop_rate=0.,\n                 attn_drop_rate=0.,\n                 drop_path_rate=0.,\n                 downsample=None,\n                 act_cfg=dict(type='GELU'),\n                 norm_cfg=dict(type='LN'),\n                 with_cp=False,\n                 init_cfg=None):\n        super().__init__(init_cfg=init_cfg)\n\n        if isinstance(drop_path_rate, list):\n            drop_path_rates = drop_path_rate\n            assert len(drop_path_rates) == depth\n        else:\n            drop_path_rates = [deepcopy(drop_path_rate) for _ in range(depth)]\n\n        self.blocks = ModuleList()\n        for i in range(depth):\n            block = SwinBlock(\n                embed_dims=embed_dims,\n                num_heads=num_heads,\n                feedforward_channels=feedforward_channels,\n                window_size=window_size,\n                shift=False if i % 2 == 0 else True,\n                qkv_bias=qkv_bias,\n                qk_scale=qk_scale,\n                drop_rate=drop_rate,\n                attn_drop_rate=attn_drop_rate,\n                drop_path_rate=drop_path_rates[i],\n                act_cfg=act_cfg,\n                norm_cfg=norm_cfg,\n                with_cp=with_cp,\n                init_cfg=None)\n            self.blocks.append(block)\n\n        self.downsample = downsample\n\n    def forward(self, x, hw_shape):\n        for block in self.blocks:\n            x = block(x, hw_shape)\n\n        if self.downsample:\n            x_down, down_hw_shape = self.downsample(x, hw_shape)\n            return x_down, down_hw_shape, x, hw_shape\n        else:\n            return x, hw_shape, x, hw_shape\n\n\n@MODELS.register_module()\nclass SwinTransformer(BaseModule):\n    \"\"\" Swin Transformer\n    A PyTorch implement of : `Swin Transformer:\n    Hierarchical Vision Transformer using Shifted Windows`  -\n        https://arxiv.org/abs/2103.14030\n\n    Inspiration from\n    https://github.com/microsoft/Swin-Transformer\n\n    Args:\n        pretrain_img_size (int | tuple[int]): The size of input image when\n            pretrain. Defaults: 224.\n        in_channels (int): The num of input channels.\n            Defaults: 3.\n        embed_dims (int): The feature dimension. Default: 96.\n        patch_size (int | tuple[int]): Patch size. Default: 4.\n        window_size (int): Window size. Default: 7.\n        mlp_ratio (int): Ratio of mlp hidden dim to embedding dim.\n            Default: 4.\n        depths (tuple[int]): Depths of each Swin Transformer stage.\n            Default: (2, 2, 6, 2).\n        num_heads (tuple[int]): Parallel attention heads of each Swin\n            Transformer stage. Default: (3, 6, 12, 24).\n        strides (tuple[int]): The patch merging or patch embedding stride of\n            each Swin Transformer stage. (In swin, we set kernel size equal to\n            stride.) Default: (4, 2, 2, 2).\n        out_indices (tuple[int]): Output from which stages.\n            Default: (0, 1, 2, 3).\n        qkv_bias (bool, optional): If True, add a learnable bias to query, key,\n            value. Default: True\n        qk_scale (float | None, optional): Override default qk scale of\n            head_dim ** -0.5 if set. Default: None.\n        patch_norm (bool): If add a norm layer for patch embed and patch\n            merging. Default: True.\n        drop_rate (float): Dropout rate. Defaults: 0.\n        attn_drop_rate (float): Attention dropout rate. Default: 0.\n        drop_path_rate (float): Stochastic depth rate. Defaults: 0.1.\n        use_abs_pos_embed (bool): If True, add absolute position embedding to\n            the patch embedding. Defaults: False.\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='GELU').\n        norm_cfg (dict): Config dict for normalization layer at\n            output of backone. Defaults: dict(type='LN').\n        with_cp (bool, optional): Use checkpoint or not. Using checkpoint\n            will save some memory while slowing down the training speed.\n            Default: False.\n        pretrained (str, optional): model pretrained path. Default: None.\n        convert_weights (bool): The flag indicates whether the\n            pre-trained model is from the original repo. We may need\n            to convert some keys to make it compatible.\n            Default: False.\n        frozen_stages (int): Stages to be frozen (stop grad and set eval mode).\n            Default: -1 (-1 means not freezing any parameters).\n        init_cfg (dict, optional): The Config for initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 pretrain_img_size=224,\n                 in_channels=3,\n                 embed_dims=96,\n                 patch_size=4,\n                 window_size=7,\n                 mlp_ratio=4,\n                 depths=(2, 2, 6, 2),\n                 num_heads=(3, 6, 12, 24),\n                 strides=(4, 2, 2, 2),\n                 out_indices=(0, 1, 2, 3),\n                 qkv_bias=True,\n                 qk_scale=None,\n                 patch_norm=True,\n                 drop_rate=0.,\n                 attn_drop_rate=0.,\n                 drop_path_rate=0.1,\n                 use_abs_pos_embed=False,\n                 act_cfg=dict(type='GELU'),\n                 norm_cfg=dict(type='LN'),\n                 with_cp=False,\n                 pretrained=None,\n                 convert_weights=False,\n                 frozen_stages=-1,\n                 init_cfg=None):\n        self.convert_weights = convert_weights\n        self.frozen_stages = frozen_stages\n        if isinstance(pretrain_img_size, int):\n            pretrain_img_size = to_2tuple(pretrain_img_size)\n        elif isinstance(pretrain_img_size, tuple):\n            if len(pretrain_img_size) == 1:\n                pretrain_img_size = to_2tuple(pretrain_img_size[0])\n            assert len(pretrain_img_size) == 2, \\\n                f'The size of image should have length 1 or 2, ' \\\n                f'but got {len(pretrain_img_size)}'\n\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            self.init_cfg = init_cfg\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n        super(SwinTransformer, self).__init__(init_cfg=init_cfg)\n\n        num_layers = len(depths)\n        self.out_indices = out_indices\n        self.use_abs_pos_embed = use_abs_pos_embed\n\n        assert strides[0] == patch_size, 'Use non-overlapping patch embed.'\n\n        self.patch_embed = PatchEmbed(\n            in_channels=in_channels,\n            embed_dims=embed_dims,\n            conv_type='Conv2d',\n            kernel_size=patch_size,\n            stride=strides[0],\n            norm_cfg=norm_cfg if patch_norm else None,\n            init_cfg=None)\n\n        if self.use_abs_pos_embed:\n            patch_row = pretrain_img_size[0] // patch_size\n            patch_col = pretrain_img_size[1] // patch_size\n            num_patches = patch_row * patch_col\n            self.absolute_pos_embed = nn.Parameter(\n                torch.zeros((1, num_patches, embed_dims)))\n\n        self.drop_after_pos = nn.Dropout(p=drop_rate)\n\n        # set stochastic depth decay rule\n        total_depth = sum(depths)\n        dpr = [\n            x.item() for x in torch.linspace(0, drop_path_rate, total_depth)\n        ]\n\n        self.stages = ModuleList()\n        in_channels = embed_dims\n        for i in range(num_layers):\n            if i < num_layers - 1:\n                downsample = PatchMerging(\n                    in_channels=in_channels,\n                    out_channels=2 * in_channels,\n                    stride=strides[i + 1],\n                    norm_cfg=norm_cfg if patch_norm else None,\n                    init_cfg=None)\n            else:\n                downsample = None\n\n            stage = SwinBlockSequence(\n                embed_dims=in_channels,\n                num_heads=num_heads[i],\n                feedforward_channels=mlp_ratio * in_channels,\n                depth=depths[i],\n                window_size=window_size,\n                qkv_bias=qkv_bias,\n                qk_scale=qk_scale,\n                drop_rate=drop_rate,\n                attn_drop_rate=attn_drop_rate,\n                drop_path_rate=dpr[sum(depths[:i]):sum(depths[:i + 1])],\n                downsample=downsample,\n                act_cfg=act_cfg,\n                norm_cfg=norm_cfg,\n                with_cp=with_cp,\n                init_cfg=None)\n            self.stages.append(stage)\n            if downsample:\n                in_channels = downsample.out_channels\n\n        self.num_features = [int(embed_dims * 2**i) for i in range(num_layers)]\n        # Add a norm layer for each output\n        for i in out_indices:\n            layer = build_norm_layer(norm_cfg, self.num_features[i])[1]\n            layer_name = f'norm{i}'\n            self.add_module(layer_name, layer)\n\n    def train(self, mode=True):\n        \"\"\"Convert the model into training mode while keep layers freezed.\"\"\"\n        super(SwinTransformer, self).train(mode)\n        self._freeze_stages()\n\n    def _freeze_stages(self):\n        if self.frozen_stages >= 0:\n            self.patch_embed.eval()\n            for param in self.patch_embed.parameters():\n                param.requires_grad = False\n            if self.use_abs_pos_embed:\n                self.absolute_pos_embed.requires_grad = False\n            self.drop_after_pos.eval()\n\n        for i in range(1, self.frozen_stages + 1):\n\n            if (i - 1) in self.out_indices:\n                norm_layer = getattr(self, f'norm{i-1}')\n                norm_layer.eval()\n                for param in norm_layer.parameters():\n                    param.requires_grad = False\n\n            m = self.stages[i - 1]\n            m.eval()\n            for param in m.parameters():\n                param.requires_grad = False\n\n    def init_weights(self):\n        logger = MMLogger.get_current_instance()\n        if self.init_cfg is None:\n            logger.warn(f'No pre-trained weights for '\n                        f'{self.__class__.__name__}, '\n                        f'training start from scratch')\n            if self.use_abs_pos_embed:\n                trunc_normal_(self.absolute_pos_embed, std=0.02)\n            for m in self.modules():\n                if isinstance(m, nn.Linear):\n                    trunc_normal_init(m, std=.02, bias=0.)\n                elif isinstance(m, nn.LayerNorm):\n                    constant_init(m, 1.0)\n        else:\n            assert 'checkpoint' in self.init_cfg, f'Only support ' \\\n                                                  f'specify `Pretrained` in ' \\\n                                                  f'`init_cfg` in ' \\\n                                                  f'{self.__class__.__name__} '\n            ckpt = CheckpointLoader.load_checkpoint(\n                self.init_cfg.checkpoint, logger=logger, map_location='cpu')\n            if 'state_dict' in ckpt:\n                _state_dict = ckpt['state_dict']\n            elif 'model' in ckpt:\n                _state_dict = ckpt['model']\n            else:\n                _state_dict = ckpt\n            if self.convert_weights:\n                # supported loading weight from original repo,\n                _state_dict = swin_converter(_state_dict)\n\n            state_dict = OrderedDict()\n            for k, v in _state_dict.items():\n                if k.startswith('backbone.'):\n                    state_dict[k[9:]] = v\n\n            # strip prefix of state_dict\n            if list(state_dict.keys())[0].startswith('module.'):\n                state_dict = {k[7:]: v for k, v in state_dict.items()}\n\n            # reshape absolute position embedding\n            if state_dict.get('absolute_pos_embed') is not None:\n                absolute_pos_embed = state_dict['absolute_pos_embed']\n                N1, L, C1 = absolute_pos_embed.size()\n                N2, C2, H, W = self.absolute_pos_embed.size()\n                if N1 != N2 or C1 != C2 or L != H * W:\n                    logger.warning('Error in loading absolute_pos_embed, pass')\n                else:\n                    state_dict['absolute_pos_embed'] = absolute_pos_embed.view(\n                        N2, H, W, C2).permute(0, 3, 1, 2).contiguous()\n\n            # interpolate position bias table if needed\n            relative_position_bias_table_keys = [\n                k for k in state_dict.keys()\n                if 'relative_position_bias_table' in k\n            ]\n            for table_key in relative_position_bias_table_keys:\n                table_pretrained = state_dict[table_key]\n                table_current = self.state_dict()[table_key]\n                L1, nH1 = table_pretrained.size()\n                L2, nH2 = table_current.size()\n                if nH1 != nH2:\n                    logger.warning(f'Error in loading {table_key}, pass')\n                elif L1 != L2:\n                    S1 = int(L1**0.5)\n                    S2 = int(L2**0.5)\n                    table_pretrained_resized = F.interpolate(\n                        table_pretrained.permute(1, 0).reshape(1, nH1, S1, S1),\n                        size=(S2, S2),\n                        mode='bicubic')\n                    state_dict[table_key] = table_pretrained_resized.view(\n                        nH2, L2).permute(1, 0).contiguous()\n\n            # load state_dict\n            self.load_state_dict(state_dict, False)\n\n    def forward(self, x):\n        x, hw_shape = self.patch_embed(x)\n\n        if self.use_abs_pos_embed:\n            x = x + self.absolute_pos_embed\n        x = self.drop_after_pos(x)\n\n        outs = []\n        for i, stage in enumerate(self.stages):\n            x, hw_shape, out, out_hw_shape = stage(x, hw_shape)\n            if i in self.out_indices:\n                norm_layer = getattr(self, f'norm{i}')\n                out = norm_layer(out)\n                out = out.view(-1, *out_hw_shape,\n                               self.num_features[i]).permute(0, 3, 1,\n                                                             2).contiguous()\n                outs.append(out)\n\n        return outs\n\n\ndef swin_converter(ckpt):\n\n    new_ckpt = OrderedDict()\n\n    def correct_unfold_reduction_order(x):\n        out_channel, in_channel = x.shape\n        x = x.reshape(out_channel, 4, in_channel // 4)\n        x = x[:, [0, 2, 1, 3], :].transpose(1,\n                                            2).reshape(out_channel, in_channel)\n        return x\n\n    def correct_unfold_norm_order(x):\n        in_channel = x.shape[0]\n        x = x.reshape(4, in_channel // 4)\n        x = x[[0, 2, 1, 3], :].transpose(0, 1).reshape(in_channel)\n        return x\n\n    for k, v in ckpt.items():\n        if k.startswith('head'):\n            continue\n        elif k.startswith('layers'):\n            new_v = v\n            if 'attn.' in k:\n                new_k = k.replace('attn.', 'attn.w_msa.')\n            elif 'mlp.' in k:\n                if 'mlp.fc1.' in k:\n                    new_k = k.replace('mlp.fc1.', 'ffn.layers.0.0.')\n                elif 'mlp.fc2.' in k:\n                    new_k = k.replace('mlp.fc2.', 'ffn.layers.1.')\n                else:\n                    new_k = k.replace('mlp.', 'ffn.')\n            elif 'downsample' in k:\n                new_k = k\n                if 'reduction.' in k:\n                    new_v = correct_unfold_reduction_order(v)\n                elif 'norm.' in k:\n                    new_v = correct_unfold_norm_order(v)\n            else:\n                new_k = k\n            new_k = new_k.replace('layers', 'stages', 1)\n        elif k.startswith('patch_embed'):\n            new_v = v\n            if 'proj' in k:\n                new_k = k.replace('proj', 'projection')\n            else:\n                new_k = k\n        else:\n            new_v = v\n            new_k = k\n\n        new_ckpt['backbone.' + new_k] = new_v\n\n    return new_ckpt\n"
  },
  {
    "path": "mmdet/models/backbones/trident_resnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\nfrom mmengine.model import BaseModule\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.models.backbones.resnet import Bottleneck, ResNet\nfrom mmdet.registry import MODELS\n\n\nclass TridentConv(BaseModule):\n    \"\"\"Trident Convolution Module.\n\n    Args:\n        in_channels (int): Number of channels in input.\n        out_channels (int): Number of channels in output.\n        kernel_size (int): Size of convolution kernel.\n        stride (int, optional): Convolution stride. Default: 1.\n        trident_dilations (tuple[int, int, int], optional): Dilations of\n            different trident branch. Default: (1, 2, 3).\n        test_branch_idx (int, optional): In inference, all 3 branches will\n            be used if `test_branch_idx==-1`, otherwise only branch with\n            index `test_branch_idx` will be used. Default: 1.\n        bias (bool, optional): Whether to use bias in convolution or not.\n            Default: False.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 kernel_size,\n                 stride=1,\n                 trident_dilations=(1, 2, 3),\n                 test_branch_idx=1,\n                 bias=False,\n                 init_cfg=None):\n        super(TridentConv, self).__init__(init_cfg)\n        self.num_branch = len(trident_dilations)\n        self.with_bias = bias\n        self.test_branch_idx = test_branch_idx\n        self.stride = _pair(stride)\n        self.kernel_size = _pair(kernel_size)\n        self.paddings = _pair(trident_dilations)\n        self.dilations = trident_dilations\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.bias = bias\n\n        self.weight = nn.Parameter(\n            torch.Tensor(out_channels, in_channels, *self.kernel_size))\n        if bias:\n            self.bias = nn.Parameter(torch.Tensor(out_channels))\n        else:\n            self.bias = None\n\n    def extra_repr(self):\n        tmpstr = f'in_channels={self.in_channels}'\n        tmpstr += f', out_channels={self.out_channels}'\n        tmpstr += f', kernel_size={self.kernel_size}'\n        tmpstr += f', num_branch={self.num_branch}'\n        tmpstr += f', test_branch_idx={self.test_branch_idx}'\n        tmpstr += f', stride={self.stride}'\n        tmpstr += f', paddings={self.paddings}'\n        tmpstr += f', dilations={self.dilations}'\n        tmpstr += f', bias={self.bias}'\n        return tmpstr\n\n    def forward(self, inputs):\n        if self.training or self.test_branch_idx == -1:\n            outputs = [\n                F.conv2d(input, self.weight, self.bias, self.stride, padding,\n                         dilation) for input, dilation, padding in zip(\n                             inputs, self.dilations, self.paddings)\n            ]\n        else:\n            assert len(inputs) == 1\n            outputs = [\n                F.conv2d(inputs[0], self.weight, self.bias, self.stride,\n                         self.paddings[self.test_branch_idx],\n                         self.dilations[self.test_branch_idx])\n            ]\n\n        return outputs\n\n\n# Since TridentNet is defined over ResNet50 and ResNet101, here we\n# only support TridentBottleneckBlock.\nclass TridentBottleneck(Bottleneck):\n    \"\"\"BottleBlock for TridentResNet.\n\n    Args:\n        trident_dilations (tuple[int, int, int]): Dilations of different\n            trident branch.\n        test_branch_idx (int): In inference, all 3 branches will be used\n            if `test_branch_idx==-1`, otherwise only branch with index\n            `test_branch_idx` will be used.\n        concat_output (bool): Whether to concat the output list to a Tensor.\n            `True` only in the last Block.\n    \"\"\"\n\n    def __init__(self, trident_dilations, test_branch_idx, concat_output,\n                 **kwargs):\n\n        super(TridentBottleneck, self).__init__(**kwargs)\n        self.trident_dilations = trident_dilations\n        self.num_branch = len(trident_dilations)\n        self.concat_output = concat_output\n        self.test_branch_idx = test_branch_idx\n        self.conv2 = TridentConv(\n            self.planes,\n            self.planes,\n            kernel_size=3,\n            stride=self.conv2_stride,\n            bias=False,\n            trident_dilations=self.trident_dilations,\n            test_branch_idx=test_branch_idx,\n            init_cfg=dict(\n                type='Kaiming',\n                distribution='uniform',\n                mode='fan_in',\n                override=dict(name='conv2')))\n\n    def forward(self, x):\n\n        def _inner_forward(x):\n            num_branch = (\n                self.num_branch\n                if self.training or self.test_branch_idx == -1 else 1)\n            identity = x\n            if not isinstance(x, list):\n                x = (x, ) * num_branch\n                identity = x\n                if self.downsample is not None:\n                    identity = [self.downsample(b) for b in x]\n\n            out = [self.conv1(b) for b in x]\n            out = [self.norm1(b) for b in out]\n            out = [self.relu(b) for b in out]\n\n            if self.with_plugins:\n                for k in range(len(out)):\n                    out[k] = self.forward_plugin(out[k],\n                                                 self.after_conv1_plugin_names)\n\n            out = self.conv2(out)\n            out = [self.norm2(b) for b in out]\n            out = [self.relu(b) for b in out]\n            if self.with_plugins:\n                for k in range(len(out)):\n                    out[k] = self.forward_plugin(out[k],\n                                                 self.after_conv2_plugin_names)\n\n            out = [self.conv3(b) for b in out]\n            out = [self.norm3(b) for b in out]\n\n            if self.with_plugins:\n                for k in range(len(out)):\n                    out[k] = self.forward_plugin(out[k],\n                                                 self.after_conv3_plugin_names)\n\n            out = [\n                out_b + identity_b for out_b, identity_b in zip(out, identity)\n            ]\n            return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        out = [self.relu(b) for b in out]\n        if self.concat_output:\n            out = torch.cat(out, dim=0)\n        return out\n\n\ndef make_trident_res_layer(block,\n                           inplanes,\n                           planes,\n                           num_blocks,\n                           stride=1,\n                           trident_dilations=(1, 2, 3),\n                           style='pytorch',\n                           with_cp=False,\n                           conv_cfg=None,\n                           norm_cfg=dict(type='BN'),\n                           dcn=None,\n                           plugins=None,\n                           test_branch_idx=-1):\n    \"\"\"Build Trident Res Layers.\"\"\"\n\n    downsample = None\n    if stride != 1 or inplanes != planes * block.expansion:\n        downsample = []\n        conv_stride = stride\n        downsample.extend([\n            build_conv_layer(\n                conv_cfg,\n                inplanes,\n                planes * block.expansion,\n                kernel_size=1,\n                stride=conv_stride,\n                bias=False),\n            build_norm_layer(norm_cfg, planes * block.expansion)[1]\n        ])\n        downsample = nn.Sequential(*downsample)\n\n    layers = []\n    for i in range(num_blocks):\n        layers.append(\n            block(\n                inplanes=inplanes,\n                planes=planes,\n                stride=stride if i == 0 else 1,\n                trident_dilations=trident_dilations,\n                downsample=downsample if i == 0 else None,\n                style=style,\n                with_cp=with_cp,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                dcn=dcn,\n                plugins=plugins,\n                test_branch_idx=test_branch_idx,\n                concat_output=True if i == num_blocks - 1 else False))\n        inplanes = planes * block.expansion\n    return nn.Sequential(*layers)\n\n\n@MODELS.register_module()\nclass TridentResNet(ResNet):\n    \"\"\"The stem layer, stage 1 and stage 2 in Trident ResNet are identical to\n    ResNet, while in stage 3, Trident BottleBlock is utilized to replace the\n    normal BottleBlock to yield trident output. Different branch shares the\n    convolution weight but uses different dilations to achieve multi-scale\n    output.\n\n                               / stage3(b0) \\\n    x - stem - stage1 - stage2 - stage3(b1) - output\n                               \\ stage3(b2) /\n\n    Args:\n        depth (int): Depth of resnet, from {50, 101, 152}.\n        num_branch (int): Number of branches in TridentNet.\n        test_branch_idx (int): In inference, all 3 branches will be used\n            if `test_branch_idx==-1`, otherwise only branch with index\n            `test_branch_idx` will be used.\n        trident_dilations (tuple[int]): Dilations of different trident branch.\n            len(trident_dilations) should be equal to num_branch.\n    \"\"\"  # noqa\n\n    def __init__(self, depth, num_branch, test_branch_idx, trident_dilations,\n                 **kwargs):\n\n        assert num_branch == len(trident_dilations)\n        assert depth in (50, 101, 152)\n        super(TridentResNet, self).__init__(depth, **kwargs)\n        assert self.num_stages == 3\n        self.test_branch_idx = test_branch_idx\n        self.num_branch = num_branch\n\n        last_stage_idx = self.num_stages - 1\n        stride = self.strides[last_stage_idx]\n        dilation = trident_dilations\n        dcn = self.dcn if self.stage_with_dcn[last_stage_idx] else None\n        if self.plugins is not None:\n            stage_plugins = self.make_stage_plugins(self.plugins,\n                                                    last_stage_idx)\n        else:\n            stage_plugins = None\n        planes = self.base_channels * 2**last_stage_idx\n        res_layer = make_trident_res_layer(\n            TridentBottleneck,\n            inplanes=(self.block.expansion * self.base_channels *\n                      2**(last_stage_idx - 1)),\n            planes=planes,\n            num_blocks=self.stage_blocks[last_stage_idx],\n            stride=stride,\n            trident_dilations=dilation,\n            style=self.style,\n            with_cp=self.with_cp,\n            conv_cfg=self.conv_cfg,\n            norm_cfg=self.norm_cfg,\n            dcn=dcn,\n            plugins=stage_plugins,\n            test_branch_idx=self.test_branch_idx)\n\n        layer_name = f'layer{last_stage_idx + 1}'\n\n        self.__setattr__(layer_name, res_layer)\n        self.res_layers.pop(last_stage_idx)\n        self.res_layers.insert(last_stage_idx, layer_name)\n\n        self._freeze_stages()\n"
  },
  {
    "path": "mmdet/models/data_preprocessors/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .data_preprocessor import (BatchFixedSizePad, BatchResize,\n                                BatchSyncRandomResize, BoxInstDataPreprocessor,\n                                DetDataPreprocessor,\n                                MultiBranchDataPreprocessor)\nfrom .reid_data_preprocessor import ReIDDataPreprocessor\nfrom .track_data_preprocessor import TrackDataPreprocessor\n\n__all__ = [\n    'DetDataPreprocessor', 'BatchSyncRandomResize', 'BatchFixedSizePad',\n    'MultiBranchDataPreprocessor', 'BatchResize', 'BoxInstDataPreprocessor',\n    'TrackDataPreprocessor', 'ReIDDataPreprocessor'\n]\n"
  },
  {
    "path": "mmdet/models/data_preprocessors/data_preprocessor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport random\nfrom numbers import Number\nfrom typing import List, Optional, Sequence, Tuple, Union\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.dist import barrier, broadcast, get_dist_info\nfrom mmengine.logging import MessageHub\nfrom mmengine.model import BaseDataPreprocessor, ImgDataPreprocessor\nfrom mmengine.structures import PixelData\nfrom mmengine.utils import is_seq_of\nfrom torch import Tensor\n\nfrom mmdet.models.utils import unfold_wo_center\nfrom mmdet.models.utils.misc import samplelist_boxtype2tensor\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.mask import BitmapMasks\nfrom mmdet.utils import ConfigType\n\ntry:\n    import skimage\nexcept ImportError:\n    skimage = None\n\n\n@MODELS.register_module()\nclass DetDataPreprocessor(ImgDataPreprocessor):\n    \"\"\"Image pre-processor for detection tasks.\n\n    Comparing with the :class:`mmengine.ImgDataPreprocessor`,\n\n    1. It supports batch augmentations.\n    2. It will additionally append batch_input_shape and pad_shape\n    to data_samples considering the object detection task.\n\n    It provides the data pre-processing as follows\n\n    - Collate and move data to the target device.\n    - Pad inputs to the maximum size of current batch with defined\n      ``pad_value``. The padding size can be divisible by a defined\n      ``pad_size_divisor``\n    - Stack inputs to batch_inputs.\n    - Convert inputs from bgr to rgb if the shape of input is (3, H, W).\n    - Normalize image with defined std and mean.\n    - Do batch augmentations during training.\n\n    Args:\n        mean (Sequence[Number], optional): The pixel mean of R, G, B channels.\n            Defaults to None.\n        std (Sequence[Number], optional): The pixel standard deviation of\n            R, G, B channels. Defaults to None.\n        pad_size_divisor (int): The size of padded image should be\n            divisible by ``pad_size_divisor``. Defaults to 1.\n        pad_value (Number): The padded pixel value. Defaults to 0.\n        pad_mask (bool): Whether to pad instance masks. Defaults to False.\n        mask_pad_value (int): The padded pixel value for instance masks.\n            Defaults to 0.\n        pad_seg (bool): Whether to pad semantic segmentation maps.\n            Defaults to False.\n        seg_pad_value (int): The padded pixel value for semantic\n            segmentation maps. Defaults to 255.\n        bgr_to_rgb (bool): whether to convert image from BGR to RGB.\n            Defaults to False.\n        rgb_to_bgr (bool): whether to convert image from RGB to RGB.\n            Defaults to False.\n        boxtype2tensor (bool): Whether to convert the ``BaseBoxes`` type of\n            bboxes data to ``Tensor`` type. Defaults to True.\n        non_blocking (bool): Whether block current process\n            when transferring data to device. Defaults to False.\n        batch_augments (list[dict], optional): Batch-level augmentations\n    \"\"\"\n\n    def __init__(self,\n                 mean: Sequence[Number] = None,\n                 std: Sequence[Number] = None,\n                 pad_size_divisor: int = 1,\n                 pad_value: Union[float, int] = 0,\n                 pad_mask: bool = False,\n                 mask_pad_value: int = 0,\n                 pad_seg: bool = False,\n                 seg_pad_value: int = 255,\n                 bgr_to_rgb: bool = False,\n                 rgb_to_bgr: bool = False,\n                 boxtype2tensor: bool = True,\n                 non_blocking: Optional[bool] = False,\n                 batch_augments: Optional[List[dict]] = None):\n        super().__init__(\n            mean=mean,\n            std=std,\n            pad_size_divisor=pad_size_divisor,\n            pad_value=pad_value,\n            bgr_to_rgb=bgr_to_rgb,\n            rgb_to_bgr=rgb_to_bgr,\n            non_blocking=non_blocking)\n        if batch_augments is not None:\n            self.batch_augments = nn.ModuleList(\n                [MODELS.build(aug) for aug in batch_augments])\n        else:\n            self.batch_augments = None\n        self.pad_mask = pad_mask\n        self.mask_pad_value = mask_pad_value\n        self.pad_seg = pad_seg\n        self.seg_pad_value = seg_pad_value\n        self.boxtype2tensor = boxtype2tensor\n\n    def forward(self, data: dict, training: bool = False) -> dict:\n        \"\"\"Perform normalization,padding and bgr2rgb conversion based on\n        ``BaseDataPreprocessor``.\n\n        Args:\n            data (dict): Data sampled from dataloader.\n            training (bool): Whether to enable training time augmentation.\n\n        Returns:\n            dict: Data in the same format as the model input.\n        \"\"\"\n        batch_pad_shape = self._get_pad_shape(data)\n        data = super().forward(data=data, training=training)\n        inputs, data_samples = data['inputs'], data['data_samples']\n\n        if data_samples is not None:\n            # NOTE the batched image size information may be useful, e.g.\n            # in DETR, this is needed for the construction of masks, which is\n            # then used for the transformer_head.\n            batch_input_shape = tuple(inputs[0].size()[-2:])\n            for data_sample, pad_shape in zip(data_samples, batch_pad_shape):\n                data_sample.set_metainfo({\n                    'batch_input_shape': batch_input_shape,\n                    'pad_shape': pad_shape\n                })\n\n            if self.boxtype2tensor:\n                samplelist_boxtype2tensor(data_samples)\n\n            if self.pad_mask and training:\n                self.pad_gt_masks(data_samples)\n\n            if self.pad_seg and training:\n                self.pad_gt_sem_seg(data_samples)\n\n        if training and self.batch_augments is not None:\n            for batch_aug in self.batch_augments:\n                inputs, data_samples = batch_aug(inputs, data_samples)\n\n        return {'inputs': inputs, 'data_samples': data_samples}\n\n    def _get_pad_shape(self, data: dict) -> List[tuple]:\n        \"\"\"Get the pad_shape of each image based on data and\n        pad_size_divisor.\"\"\"\n        _batch_inputs = data['inputs']\n        # Process data with `pseudo_collate`.\n        if is_seq_of(_batch_inputs, torch.Tensor):\n            batch_pad_shape = []\n            for ori_input in _batch_inputs:\n                pad_h = int(\n                    np.ceil(ori_input.shape[1] /\n                            self.pad_size_divisor)) * self.pad_size_divisor\n                pad_w = int(\n                    np.ceil(ori_input.shape[2] /\n                            self.pad_size_divisor)) * self.pad_size_divisor\n                batch_pad_shape.append((pad_h, pad_w))\n        # Process data with `default_collate`.\n        elif isinstance(_batch_inputs, torch.Tensor):\n            assert _batch_inputs.dim() == 4, (\n                'The input of `ImgDataPreprocessor` should be a NCHW tensor '\n                'or a list of tensor, but got a tensor with shape: '\n                f'{_batch_inputs.shape}')\n            pad_h = int(\n                np.ceil(_batch_inputs.shape[2] /\n                        self.pad_size_divisor)) * self.pad_size_divisor\n            pad_w = int(\n                np.ceil(_batch_inputs.shape[3] /\n                        self.pad_size_divisor)) * self.pad_size_divisor\n            batch_pad_shape = [(pad_h, pad_w)] * _batch_inputs.shape[0]\n        else:\n            raise TypeError('Output of `cast_data` should be a dict '\n                            'or a tuple with inputs and data_samples, but got'\n                            f'{type(data)}: {data}')\n        return batch_pad_shape\n\n    def pad_gt_masks(self,\n                     batch_data_samples: Sequence[DetDataSample]) -> None:\n        \"\"\"Pad gt_masks to shape of batch_input_shape.\"\"\"\n        if 'masks' in batch_data_samples[0].gt_instances:\n            for data_samples in batch_data_samples:\n                masks = data_samples.gt_instances.masks\n                data_samples.gt_instances.masks = masks.pad(\n                    data_samples.batch_input_shape,\n                    pad_val=self.mask_pad_value)\n\n    def pad_gt_sem_seg(self,\n                       batch_data_samples: Sequence[DetDataSample]) -> None:\n        \"\"\"Pad gt_sem_seg to shape of batch_input_shape.\"\"\"\n        if 'gt_sem_seg' in batch_data_samples[0]:\n            for data_samples in batch_data_samples:\n                gt_sem_seg = data_samples.gt_sem_seg.sem_seg\n                h, w = gt_sem_seg.shape[-2:]\n                pad_h, pad_w = data_samples.batch_input_shape\n                gt_sem_seg = F.pad(\n                    gt_sem_seg,\n                    pad=(0, max(pad_w - w, 0), 0, max(pad_h - h, 0)),\n                    mode='constant',\n                    value=self.seg_pad_value)\n                data_samples.gt_sem_seg = PixelData(sem_seg=gt_sem_seg)\n\n\n@MODELS.register_module()\nclass BatchSyncRandomResize(nn.Module):\n    \"\"\"Batch random resize which synchronizes the random size across ranks.\n\n    Args:\n        random_size_range (tuple): The multi-scale random range during\n            multi-scale training.\n        interval (int): The iter interval of change\n            image size. Defaults to 10.\n        size_divisor (int): Image size divisible factor.\n            Defaults to 32.\n    \"\"\"\n\n    def __init__(self,\n                 random_size_range: Tuple[int, int],\n                 interval: int = 10,\n                 size_divisor: int = 32) -> None:\n        super().__init__()\n        self.rank, self.world_size = get_dist_info()\n        self._input_size = None\n        self._random_size_range = (round(random_size_range[0] / size_divisor),\n                                   round(random_size_range[1] / size_divisor))\n        self._interval = interval\n        self._size_divisor = size_divisor\n\n    def forward(\n        self, inputs: Tensor, data_samples: List[DetDataSample]\n    ) -> Tuple[Tensor, List[DetDataSample]]:\n        \"\"\"resize a batch of images and bboxes to shape ``self._input_size``\"\"\"\n        h, w = inputs.shape[-2:]\n        if self._input_size is None:\n            self._input_size = (h, w)\n        scale_y = self._input_size[0] / h\n        scale_x = self._input_size[1] / w\n        if scale_x != 1 or scale_y != 1:\n            inputs = F.interpolate(\n                inputs,\n                size=self._input_size,\n                mode='bilinear',\n                align_corners=False)\n            for data_sample in data_samples:\n                img_shape = (int(data_sample.img_shape[0] * scale_y),\n                             int(data_sample.img_shape[1] * scale_x))\n                pad_shape = (int(data_sample.pad_shape[0] * scale_y),\n                             int(data_sample.pad_shape[1] * scale_x))\n                data_sample.set_metainfo({\n                    'img_shape': img_shape,\n                    'pad_shape': pad_shape,\n                    'batch_input_shape': self._input_size\n                })\n                data_sample.gt_instances.bboxes[\n                    ...,\n                    0::2] = data_sample.gt_instances.bboxes[...,\n                                                            0::2] * scale_x\n                data_sample.gt_instances.bboxes[\n                    ...,\n                    1::2] = data_sample.gt_instances.bboxes[...,\n                                                            1::2] * scale_y\n                if 'ignored_instances' in data_sample:\n                    data_sample.ignored_instances.bboxes[\n                        ..., 0::2] = data_sample.ignored_instances.bboxes[\n                            ..., 0::2] * scale_x\n                    data_sample.ignored_instances.bboxes[\n                        ..., 1::2] = data_sample.ignored_instances.bboxes[\n                            ..., 1::2] * scale_y\n        message_hub = MessageHub.get_current_instance()\n        if (message_hub.get_info('iter') + 1) % self._interval == 0:\n            self._input_size = self._get_random_size(\n                aspect_ratio=float(w / h), device=inputs.device)\n        return inputs, data_samples\n\n    def _get_random_size(self, aspect_ratio: float,\n                         device: torch.device) -> Tuple[int, int]:\n        \"\"\"Randomly generate a shape in ``_random_size_range`` and broadcast to\n        all ranks.\"\"\"\n        tensor = torch.LongTensor(2).to(device)\n        if self.rank == 0:\n            size = random.randint(*self._random_size_range)\n            size = (self._size_divisor * size,\n                    self._size_divisor * int(aspect_ratio * size))\n            tensor[0] = size[0]\n            tensor[1] = size[1]\n        barrier()\n        broadcast(tensor, 0)\n        input_size = (tensor[0].item(), tensor[1].item())\n        return input_size\n\n\n@MODELS.register_module()\nclass BatchFixedSizePad(nn.Module):\n    \"\"\"Fixed size padding for batch images.\n\n    Args:\n        size (Tuple[int, int]): Fixed padding size. Expected padding\n            shape (h, w). Defaults to None.\n        img_pad_value (int): The padded pixel value for images.\n            Defaults to 0.\n        pad_mask (bool): Whether to pad instance masks. Defaults to False.\n        mask_pad_value (int): The padded pixel value for instance masks.\n            Defaults to 0.\n        pad_seg (bool): Whether to pad semantic segmentation maps.\n            Defaults to False.\n        seg_pad_value (int): The padded pixel value for semantic\n            segmentation maps. Defaults to 255.\n    \"\"\"\n\n    def __init__(self,\n                 size: Tuple[int, int],\n                 img_pad_value: int = 0,\n                 pad_mask: bool = False,\n                 mask_pad_value: int = 0,\n                 pad_seg: bool = False,\n                 seg_pad_value: int = 255) -> None:\n        super().__init__()\n        self.size = size\n        self.pad_mask = pad_mask\n        self.pad_seg = pad_seg\n        self.img_pad_value = img_pad_value\n        self.mask_pad_value = mask_pad_value\n        self.seg_pad_value = seg_pad_value\n\n    def forward(\n        self,\n        inputs: Tensor,\n        data_samples: Optional[List[dict]] = None\n    ) -> Tuple[Tensor, Optional[List[dict]]]:\n        \"\"\"Pad image, instance masks, segmantic segmentation maps.\"\"\"\n        src_h, src_w = inputs.shape[-2:]\n        dst_h, dst_w = self.size\n\n        if src_h >= dst_h and src_w >= dst_w:\n            return inputs, data_samples\n\n        inputs = F.pad(\n            inputs,\n            pad=(0, max(0, dst_w - src_w), 0, max(0, dst_h - src_h)),\n            mode='constant',\n            value=self.img_pad_value)\n\n        if data_samples is not None:\n            # update batch_input_shape\n            for data_sample in data_samples:\n                data_sample.set_metainfo({\n                    'batch_input_shape': (dst_h, dst_w),\n                    'pad_shape': (dst_h, dst_w)\n                })\n\n            if self.pad_mask:\n                for data_sample in data_samples:\n                    masks = data_sample.gt_instances.masks\n                    data_sample.gt_instances.masks = masks.pad(\n                        (dst_h, dst_w), pad_val=self.mask_pad_value)\n\n            if self.pad_seg:\n                for data_sample in data_samples:\n                    gt_sem_seg = data_sample.gt_sem_seg.sem_seg\n                    h, w = gt_sem_seg.shape[-2:]\n                    gt_sem_seg = F.pad(\n                        gt_sem_seg,\n                        pad=(0, max(0, dst_w - w), 0, max(0, dst_h - h)),\n                        mode='constant',\n                        value=self.seg_pad_value)\n                    data_sample.gt_sem_seg = PixelData(sem_seg=gt_sem_seg)\n\n        return inputs, data_samples\n\n\n@MODELS.register_module()\nclass MultiBranchDataPreprocessor(BaseDataPreprocessor):\n    \"\"\"DataPreprocessor wrapper for multi-branch data.\n\n    Take semi-supervised object detection as an example, assume that\n    the ratio of labeled data and unlabeled data in a batch is 1:2,\n    `sup` indicates the branch where the labeled data is augmented,\n    `unsup_teacher` and `unsup_student` indicate the branches where\n    the unlabeled data is augmented by different pipeline.\n\n    The input format of multi-branch data is shown as below :\n\n    .. code-block:: none\n        {\n            'inputs':\n                {\n                    'sup': [Tensor, None, None],\n                    'unsup_teacher': [None, Tensor, Tensor],\n                    'unsup_student': [None, Tensor, Tensor],\n                },\n            'data_sample':\n                {\n                    'sup': [DetDataSample, None, None],\n                    'unsup_teacher': [None, DetDataSample, DetDataSample],\n                    'unsup_student': [NOne, DetDataSample, DetDataSample],\n                }\n        }\n\n    The format of multi-branch data\n    after filtering None is shown as below :\n\n    .. code-block:: none\n        {\n            'inputs':\n                {\n                    'sup': [Tensor],\n                    'unsup_teacher': [Tensor, Tensor],\n                    'unsup_student': [Tensor, Tensor],\n                },\n            'data_sample':\n                {\n                    'sup': [DetDataSample],\n                    'unsup_teacher': [DetDataSample, DetDataSample],\n                    'unsup_student': [DetDataSample, DetDataSample],\n                }\n        }\n\n    In order to reuse `DetDataPreprocessor` for the data\n    from different branches, the format of multi-branch data\n    grouped by branch is as below :\n\n    .. code-block:: none\n        {\n            'sup':\n                {\n                    'inputs': [Tensor]\n                    'data_sample': [DetDataSample, DetDataSample]\n                },\n            'unsup_teacher':\n                {\n                    'inputs': [Tensor, Tensor]\n                    'data_sample': [DetDataSample, DetDataSample]\n                },\n            'unsup_student':\n                {\n                    'inputs': [Tensor, Tensor]\n                    'data_sample': [DetDataSample, DetDataSample]\n                },\n        }\n\n    After preprocessing data from different branches,\n    the multi-branch data needs to be reformatted as:\n\n    .. code-block:: none\n        {\n            'inputs':\n                {\n                    'sup': [Tensor],\n                    'unsup_teacher': [Tensor, Tensor],\n                    'unsup_student': [Tensor, Tensor],\n                },\n            'data_sample':\n                {\n                    'sup': [DetDataSample],\n                    'unsup_teacher': [DetDataSample, DetDataSample],\n                    'unsup_student': [DetDataSample, DetDataSample],\n                }\n        }\n\n    Args:\n        data_preprocessor (:obj:`ConfigDict` or dict): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n    \"\"\"\n\n    def __init__(self, data_preprocessor: ConfigType) -> None:\n        super().__init__()\n        self.data_preprocessor = MODELS.build(data_preprocessor)\n\n    def forward(self, data: dict, training: bool = False) -> dict:\n        \"\"\"Perform normalization,padding and bgr2rgb conversion based on\n        ``BaseDataPreprocessor`` for multi-branch data.\n\n        Args:\n            data (dict): Data sampled from dataloader.\n            training (bool): Whether to enable training time augmentation.\n\n        Returns:\n            dict:\n\n            - 'inputs' (Dict[str, obj:`torch.Tensor`]): The forward data of\n                models from different branches.\n            - 'data_sample' (Dict[str, obj:`DetDataSample`]): The annotation\n                info of the sample from different branches.\n        \"\"\"\n\n        if training is False:\n            return self.data_preprocessor(data, training)\n\n        # Filter out branches with a value of None\n        for key in data.keys():\n            for branch in data[key].keys():\n                data[key][branch] = list(\n                    filter(lambda x: x is not None, data[key][branch]))\n\n        # Group data by branch\n        multi_branch_data = {}\n        for key in data.keys():\n            for branch in data[key].keys():\n                if multi_branch_data.get(branch, None) is None:\n                    multi_branch_data[branch] = {key: data[key][branch]}\n                elif multi_branch_data[branch].get(key, None) is None:\n                    multi_branch_data[branch][key] = data[key][branch]\n                else:\n                    multi_branch_data[branch][key].append(data[key][branch])\n\n        # Preprocess data from different branches\n        for branch, _data in multi_branch_data.items():\n            multi_branch_data[branch] = self.data_preprocessor(_data, training)\n\n        # Format data by inputs and data_samples\n        format_data = {}\n        for branch in multi_branch_data.keys():\n            for key in multi_branch_data[branch].keys():\n                if format_data.get(key, None) is None:\n                    format_data[key] = {branch: multi_branch_data[branch][key]}\n                elif format_data[key].get(branch, None) is None:\n                    format_data[key][branch] = multi_branch_data[branch][key]\n                else:\n                    format_data[key][branch].append(\n                        multi_branch_data[branch][key])\n\n        return format_data\n\n    @property\n    def device(self):\n        return self.data_preprocessor.device\n\n    def to(self, device: Optional[Union[int, torch.device]], *args,\n           **kwargs) -> nn.Module:\n        \"\"\"Overrides this method to set the :attr:`device`\n\n        Args:\n            device (int or torch.device, optional): The desired device of the\n                parameters and buffers in this module.\n\n        Returns:\n            nn.Module: The model itself.\n        \"\"\"\n\n        return self.data_preprocessor.to(device, *args, **kwargs)\n\n    def cuda(self, *args, **kwargs) -> nn.Module:\n        \"\"\"Overrides this method to set the :attr:`device`\n\n        Returns:\n            nn.Module: The model itself.\n        \"\"\"\n\n        return self.data_preprocessor.cuda(*args, **kwargs)\n\n    def cpu(self, *args, **kwargs) -> nn.Module:\n        \"\"\"Overrides this method to set the :attr:`device`\n\n        Returns:\n            nn.Module: The model itself.\n        \"\"\"\n\n        return self.data_preprocessor.cpu(*args, **kwargs)\n\n\n@MODELS.register_module()\nclass BatchResize(nn.Module):\n    \"\"\"Batch resize during training. This implementation is modified from\n    https://github.com/Purkialo/CrowdDet/blob/master/lib/data/CrowdHuman.py.\n\n    It provides the data pre-processing as follows:\n    - A batch of all images will pad to a uniform size and stack them into\n      a torch.Tensor by `DetDataPreprocessor`.\n    - `BatchFixShapeResize` resize all images to the target size.\n    - Padding images to make sure the size of image can be divisible by\n      ``pad_size_divisor``.\n\n    Args:\n        scale (tuple): Images scales for resizing.\n        pad_size_divisor (int): Image size divisible factor.\n            Defaults to 1.\n        pad_value (Number): The padded pixel value. Defaults to 0.\n    \"\"\"\n\n    def __init__(\n        self,\n        scale: tuple,\n        pad_size_divisor: int = 1,\n        pad_value: Union[float, int] = 0,\n    ) -> None:\n        super().__init__()\n        self.min_size = min(scale)\n        self.max_size = max(scale)\n        self.pad_size_divisor = pad_size_divisor\n        self.pad_value = pad_value\n\n    def forward(\n        self, inputs: Tensor, data_samples: List[DetDataSample]\n    ) -> Tuple[Tensor, List[DetDataSample]]:\n        \"\"\"resize a batch of images and bboxes.\"\"\"\n\n        batch_height, batch_width = inputs.shape[-2:]\n        target_height, target_width, scale = self.get_target_size(\n            batch_height, batch_width)\n\n        inputs = F.interpolate(\n            inputs,\n            size=(target_height, target_width),\n            mode='bilinear',\n            align_corners=False)\n\n        inputs = self.get_padded_tensor(inputs, self.pad_value)\n\n        if data_samples is not None:\n            batch_input_shape = tuple(inputs.size()[-2:])\n            for data_sample in data_samples:\n                img_shape = [\n                    int(scale * _) for _ in list(data_sample.img_shape)\n                ]\n                data_sample.set_metainfo({\n                    'img_shape': tuple(img_shape),\n                    'batch_input_shape': batch_input_shape,\n                    'pad_shape': batch_input_shape,\n                    'scale_factor': (scale, scale)\n                })\n\n                data_sample.gt_instances.bboxes *= scale\n                data_sample.ignored_instances.bboxes *= scale\n\n        return inputs, data_samples\n\n    def get_target_size(self, height: int,\n                        width: int) -> Tuple[int, int, float]:\n        \"\"\"Get the target size of a batch of images based on data and scale.\"\"\"\n        im_size_min = np.min([height, width])\n        im_size_max = np.max([height, width])\n        scale = self.min_size / im_size_min\n        if scale * im_size_max > self.max_size:\n            scale = self.max_size / im_size_max\n        target_height, target_width = int(round(height * scale)), int(\n            round(width * scale))\n        return target_height, target_width, scale\n\n    def get_padded_tensor(self, tensor: Tensor, pad_value: int) -> Tensor:\n        \"\"\"Pad images according to pad_size_divisor.\"\"\"\n        assert tensor.ndim == 4\n        target_height, target_width = tensor.shape[-2], tensor.shape[-1]\n        divisor = self.pad_size_divisor\n        padded_height = (target_height + divisor - 1) // divisor * divisor\n        padded_width = (target_width + divisor - 1) // divisor * divisor\n        padded_tensor = torch.ones([\n            tensor.shape[0], tensor.shape[1], padded_height, padded_width\n        ]) * pad_value\n        padded_tensor = padded_tensor.type_as(tensor)\n        padded_tensor[:, :, :target_height, :target_width] = tensor\n        return padded_tensor\n\n\n@MODELS.register_module()\nclass BoxInstDataPreprocessor(DetDataPreprocessor):\n    \"\"\"Pseudo mask pre-processor for BoxInst.\n\n    Comparing with the :class:`mmdet.DetDataPreprocessor`,\n\n    1. It generates masks using box annotations.\n    2. It computes the images color similarity in LAB color space.\n\n    Args:\n        mask_stride (int): The mask output stride in boxinst. Defaults to 4.\n        pairwise_size (int): The size of neighborhood for each pixel.\n            Defaults to 3.\n        pairwise_dilation (int): The dilation of neighborhood for each pixel.\n            Defaults to 2.\n        pairwise_color_thresh (float): The thresh of image color similarity.\n            Defaults to 0.3.\n        bottom_pixels_removed (int): The length of removed pixels in bottom.\n            It is caused by the annotation error in coco dataset.\n            Defaults to 10.\n    \"\"\"\n\n    def __init__(self,\n                 *arg,\n                 mask_stride: int = 4,\n                 pairwise_size: int = 3,\n                 pairwise_dilation: int = 2,\n                 pairwise_color_thresh: float = 0.3,\n                 bottom_pixels_removed: int = 10,\n                 **kwargs) -> None:\n        super().__init__(*arg, **kwargs)\n        self.mask_stride = mask_stride\n        self.pairwise_size = pairwise_size\n        self.pairwise_dilation = pairwise_dilation\n        self.pairwise_color_thresh = pairwise_color_thresh\n        self.bottom_pixels_removed = bottom_pixels_removed\n\n        if skimage is None:\n            raise RuntimeError('skimage is not installed,\\\n                 please install it by: pip install scikit-image')\n\n    def get_images_color_similarity(self, inputs: Tensor,\n                                    image_masks: Tensor) -> Tensor:\n        \"\"\"Compute the image color similarity in LAB color space.\"\"\"\n        assert inputs.dim() == 4\n        assert inputs.size(0) == 1\n\n        unfolded_images = unfold_wo_center(\n            inputs,\n            kernel_size=self.pairwise_size,\n            dilation=self.pairwise_dilation)\n        diff = inputs[:, :, None] - unfolded_images\n        similarity = torch.exp(-torch.norm(diff, dim=1) * 0.5)\n\n        unfolded_weights = unfold_wo_center(\n            image_masks[None, None],\n            kernel_size=self.pairwise_size,\n            dilation=self.pairwise_dilation)\n        unfolded_weights = torch.max(unfolded_weights, dim=1)[0]\n\n        return similarity * unfolded_weights\n\n    def forward(self, data: dict, training: bool = False) -> dict:\n        \"\"\"Get pseudo mask labels using color similarity.\"\"\"\n        det_data = super().forward(data, training)\n        inputs, data_samples = det_data['inputs'], det_data['data_samples']\n\n        if training:\n            # get image masks and remove bottom pixels\n            b_img_h, b_img_w = data_samples[0].batch_input_shape\n            img_masks = []\n            for i in range(inputs.shape[0]):\n                img_h, img_w = data_samples[i].img_shape\n                img_mask = inputs.new_ones((img_h, img_w))\n                pixels_removed = int(self.bottom_pixels_removed *\n                                     float(img_h) / float(b_img_h))\n                if pixels_removed > 0:\n                    img_mask[-pixels_removed:, :] = 0\n                pad_w = b_img_w - img_w\n                pad_h = b_img_h - img_h\n                img_mask = F.pad(img_mask, (0, pad_w, 0, pad_h), 'constant',\n                                 0.)\n                img_masks.append(img_mask)\n            img_masks = torch.stack(img_masks, dim=0)\n            start = int(self.mask_stride // 2)\n            img_masks = img_masks[:, start::self.mask_stride,\n                                  start::self.mask_stride]\n\n            # Get origin rgb image for color similarity\n            ori_imgs = inputs * self.std + self.mean\n            downsampled_imgs = F.avg_pool2d(\n                ori_imgs.float(),\n                kernel_size=self.mask_stride,\n                stride=self.mask_stride,\n                padding=0)\n\n            # Compute color similarity for pseudo mask generation\n            for im_i, data_sample in enumerate(data_samples):\n                # TODO: Support rgb2lab in mmengine?\n                images_lab = skimage.color.rgb2lab(\n                    downsampled_imgs[im_i].byte().permute(1, 2,\n                                                          0).cpu().numpy())\n                images_lab = torch.as_tensor(\n                    images_lab, device=ori_imgs.device, dtype=torch.float32)\n                images_lab = images_lab.permute(2, 0, 1)[None]\n                images_color_similarity = self.get_images_color_similarity(\n                    images_lab, img_masks[im_i])\n                pairwise_mask = (images_color_similarity >=\n                                 self.pairwise_color_thresh).float()\n\n                per_im_bboxes = data_sample.gt_instances.bboxes\n                if per_im_bboxes.shape[0] > 0:\n                    per_im_masks = []\n                    for per_box in per_im_bboxes:\n                        mask_full = torch.zeros((b_img_h, b_img_w),\n                                                device=self.device).float()\n                        mask_full[int(per_box[1]):int(per_box[3] + 1),\n                                  int(per_box[0]):int(per_box[2] + 1)] = 1.0\n                        per_im_masks.append(mask_full)\n                    per_im_masks = torch.stack(per_im_masks, dim=0)\n                    pairwise_masks = torch.cat(\n                        [pairwise_mask for _ in range(per_im_bboxes.shape[0])],\n                        dim=0)\n                else:\n                    per_im_masks = torch.zeros((0, b_img_h, b_img_w))\n                    pairwise_masks = torch.zeros(\n                        (0, self.pairwise_size**2 - 1, b_img_h, b_img_w))\n\n                # TODO: Support BitmapMasks with tensor?\n                data_sample.gt_instances.masks = BitmapMasks(\n                    per_im_masks.cpu().numpy(), b_img_h, b_img_w)\n                data_sample.gt_instances.pairwise_masks = pairwise_masks\n        return {'inputs': inputs, 'data_samples': data_samples}\n"
  },
  {
    "path": "mmdet/models/data_preprocessors/reid_data_preprocessor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom numbers import Number\nfrom typing import Optional, Sequence\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.model import BaseDataPreprocessor, stack_batch\n\nfrom mmdet.registry import MODELS\n\ntry:\n    import mmpretrain\n    from mmpretrain.models.utils.batch_augments import RandomBatchAugment\n    from mmpretrain.structures import (batch_label_to_onehot, cat_batch_labels,\n                                       tensor_split)\nexcept ImportError:\n    mmpretrain = None\n\n\ndef stack_batch_scores(elements, device=None):\n    \"\"\"Stack the ``score`` of a batch of :obj:`LabelData` to a tensor.\n\n    Args:\n        elements (List[LabelData]): A batch of :obj`LabelData`.\n        device (torch.device, optional): The output device of the batch label.\n            Defaults to None.\n    Returns:\n        torch.Tensor: The stacked score tensor.\n    \"\"\"\n    item = elements[0]\n    if 'score' not in item._data_fields:\n        return None\n\n    batch_score = torch.stack([element.score for element in elements])\n    if device is not None:\n        batch_score = batch_score.to(device)\n    return batch_score\n\n\n@MODELS.register_module()\nclass ReIDDataPreprocessor(BaseDataPreprocessor):\n    \"\"\"Image pre-processor for classification tasks.\n\n    Comparing with the :class:`mmengine.model.ImgDataPreprocessor`,\n\n    1. It won't do normalization if ``mean`` is not specified.\n    2. It does normalization and color space conversion after stacking batch.\n    3. It supports batch augmentations like mixup and cutmix.\n\n    It provides the data pre-processing as follows\n\n    - Collate and move data to the target device.\n    - Pad inputs to the maximum size of current batch with defined\n      ``pad_value``. The padding size can be divisible by a defined\n      ``pad_size_divisor``\n    - Stack inputs to batch_inputs.\n    - Convert inputs from bgr to rgb if the shape of input is (3, H, W).\n    - Normalize image with defined std and mean.\n    - Do batch augmentations like Mixup and Cutmix during training.\n\n    Args:\n        mean (Sequence[Number], optional): The pixel mean of R, G, B channels.\n            Defaults to None.\n        std (Sequence[Number], optional): The pixel standard deviation of\n            R, G, B channels. Defaults to None.\n        pad_size_divisor (int): The size of padded image should be\n            divisible by ``pad_size_divisor``. Defaults to 1.\n        pad_value (Number): The padded pixel value. Defaults to 0.\n        to_rgb (bool): whether to convert image from BGR to RGB.\n            Defaults to False.\n        to_onehot (bool): Whether to generate one-hot format gt-labels and set\n            to data samples. Defaults to False.\n        num_classes (int, optional): The number of classes. Defaults to None.\n        batch_augments (dict, optional): The batch augmentations settings,\n            including \"augments\" and \"probs\". For more details, see\n            :class:`mmpretrain.models.RandomBatchAugment`.\n    \"\"\"\n\n    def __init__(self,\n                 mean: Sequence[Number] = None,\n                 std: Sequence[Number] = None,\n                 pad_size_divisor: int = 1,\n                 pad_value: Number = 0,\n                 to_rgb: bool = False,\n                 to_onehot: bool = False,\n                 num_classes: Optional[int] = None,\n                 batch_augments: Optional[dict] = None):\n        if mmpretrain is None:\n            raise RuntimeError('Please run \"pip install openmim\" and '\n                               'run \"mim install mmpretrain\" to '\n                               'install mmpretrain first.')\n        super().__init__()\n        self.pad_size_divisor = pad_size_divisor\n        self.pad_value = pad_value\n        self.to_rgb = to_rgb\n        self.to_onehot = to_onehot\n        self.num_classes = num_classes\n\n        if mean is not None:\n            assert std is not None, 'To enable the normalization in ' \\\n                'preprocessing, please specify both `mean` and `std`.'\n            # Enable the normalization in preprocessing.\n            self._enable_normalize = True\n            self.register_buffer('mean',\n                                 torch.tensor(mean).view(-1, 1, 1), False)\n            self.register_buffer('std',\n                                 torch.tensor(std).view(-1, 1, 1), False)\n        else:\n            self._enable_normalize = False\n\n        if batch_augments is not None:\n            self.batch_augments = RandomBatchAugment(**batch_augments)\n            if not self.to_onehot:\n                from mmengine.logging import MMLogger\n                MMLogger.get_current_instance().info(\n                    'Because batch augmentations are enabled, the data '\n                    'preprocessor automatically enables the `to_onehot` '\n                    'option to generate one-hot format labels.')\n                self.to_onehot = True\n        else:\n            self.batch_augments = None\n\n    def forward(self, data: dict, training: bool = False) -> dict:\n        \"\"\"Perform normalization, padding, bgr2rgb conversion and batch\n        augmentation based on ``BaseDataPreprocessor``.\n\n        Args:\n            data (dict): data sampled from dataloader.\n            training (bool): Whether to enable training time augmentation.\n\n        Returns:\n            dict: Data in the same format as the model input.\n        \"\"\"\n        inputs = self.cast_data(data['inputs'])\n\n        if isinstance(inputs, torch.Tensor):\n            # The branch if use `default_collate` as the collate_fn in the\n            # dataloader.\n\n            # ------ To RGB ------\n            if self.to_rgb and inputs.size(1) == 3:\n                inputs = inputs.flip(1)\n\n            # -- Normalization ---\n            inputs = inputs.float()\n            if self._enable_normalize:\n                inputs = (inputs - self.mean) / self.std\n\n            # ------ Padding -----\n            if self.pad_size_divisor > 1:\n                h, w = inputs.shape[-2:]\n\n                target_h = math.ceil(\n                    h / self.pad_size_divisor) * self.pad_size_divisor\n                target_w = math.ceil(\n                    w / self.pad_size_divisor) * self.pad_size_divisor\n                pad_h = target_h - h\n                pad_w = target_w - w\n                inputs = F.pad(inputs, (0, pad_w, 0, pad_h), 'constant',\n                               self.pad_value)\n        else:\n            # The branch if use `pseudo_collate` as the collate_fn in the\n            # dataloader.\n\n            processed_inputs = []\n            for input_ in inputs:\n                # ------ To RGB ------\n                if self.to_rgb and input_.size(0) == 3:\n                    input_ = input_.flip(0)\n\n                # -- Normalization ---\n                input_ = input_.float()\n                if self._enable_normalize:\n                    input_ = (input_ - self.mean) / self.std\n\n                processed_inputs.append(input_)\n            # Combine padding and stack\n            inputs = stack_batch(processed_inputs, self.pad_size_divisor,\n                                 self.pad_value)\n\n        data_samples = data.get('data_samples', None)\n        sample_item = data_samples[0] if data_samples is not None else None\n        if 'gt_label' in sample_item:\n            gt_labels = [sample.gt_label for sample in data_samples]\n            gt_labels_tensor = [gt_label.label for gt_label in gt_labels]\n            batch_label, label_indices = cat_batch_labels(gt_labels_tensor)\n            batch_label = batch_label.to(self.device)\n\n            batch_score = stack_batch_scores(gt_labels, device=self.device)\n            if batch_score is None and self.to_onehot:\n                assert batch_label is not None, \\\n                    'Cannot generate onehot format labels because no labels.'\n                num_classes = self.num_classes or data_samples[0].get(\n                    'num_classes')\n                assert num_classes is not None, \\\n                    'Cannot generate one-hot format labels because not set ' \\\n                    '`num_classes` in `data_preprocessor`.'\n                batch_score = batch_label_to_onehot(batch_label, label_indices,\n                                                    num_classes)\n\n            # ----- Batch Augmentations ----\n            if training and self.batch_augments is not None:\n                inputs, batch_score = self.batch_augments(inputs, batch_score)\n\n            # ----- scatter labels and scores to data samples ---\n            if batch_label is not None:\n                for sample, label in zip(\n                        data_samples, tensor_split(batch_label,\n                                                   label_indices)):\n                    sample.set_gt_label(label)\n            if batch_score is not None:\n                for sample, score in zip(data_samples, batch_score):\n                    sample.set_gt_score(score)\n\n        return {'inputs': inputs, 'data_samples': data_samples}\n"
  },
  {
    "path": "mmdet/models/data_preprocessors/track_data_preprocessor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.model.utils import stack_batch\n\nfrom mmdet.models.utils.misc import samplelist_boxtype2tensor\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackDataSample\nfrom mmdet.structures.mask import BitmapMasks\nfrom .data_preprocessor import DetDataPreprocessor\n\n\n@MODELS.register_module()\nclass TrackDataPreprocessor(DetDataPreprocessor):\n    \"\"\"Image pre-processor for tracking tasks.\n\n        Accepts the data sampled by the dataloader, and preprocesses\n        it into the format of the model input. ``TrackDataPreprocessor``\n        provides the tracking data pre-processing as follows:\n\n        - Collate and move data to the target device.\n        - Pad inputs to the maximum size of current batch with defined\n          ``pad_value``. The padding size can be divisible by a defined\n          ``pad_size_divisor``\n        - Stack inputs to inputs.\n        - Convert inputs from bgr to rgb if the shape of input is (1, 3, H, W).\n        - Normalize image with defined std and mean.\n        - Do batch augmentations during training.\n        - Record the information of ``batch_input_shape`` and ``pad_shape``.\n\n        Args:\n            mean (Sequence[Number], optional): The pixel mean of R, G, B\n                channels. Defaults to None.\n            std (Sequence[Number], optional): The pixel standard deviation of\n                R, G, B channels. Defaults to None.\n            pad_size_divisor (int): The size of padded image should be\n                divisible by ``pad_size_divisor``. Defaults to 1.\n            pad_value (Number): The padded pixel value. Defaults to 0.\n            pad_mask (bool): Whether to pad instance masks. Defaults to False.\n            mask_pad_value (int): The padded pixel value for instance masks.\n                Defaults to 0.\n            bgr_to_rgb (bool): whether to convert image from BGR to RGB.\n                Defaults to False.\n            rgb_to_bgr (bool): whether to convert image from RGB to RGB.\n                Defaults to False.\n            use_det_processor: (bool): whether to use DetDataPreprocessor\n                in training phrase. This is mainly for some tracking models\n                fed into one image rather than a group of image in training.\n                Defaults to False.\n    .       boxtype2tensor (bool): Whether to convert the ``BaseBoxes`` type of\n                bboxes data to ``Tensor`` type. Defaults to True.\n            batch_augments (list[dict], optional): Batch-level augmentations\n    \"\"\"\n\n    def __init__(self,\n                 mean: Optional[Sequence[Union[float, int]]] = None,\n                 std: Optional[Sequence[Union[float, int]]] = None,\n                 use_det_processor: bool = False,\n                 **kwargs):\n        super().__init__(mean=mean, std=std, **kwargs)\n        self.use_det_processor = use_det_processor\n        if mean is not None and not self.use_det_processor:\n            # overwrite the ``register_bufffer`` in ``ImgDataPreprocessor``\n            # since the shape of ``mean`` and ``std`` in tracking tasks must be\n            # (T, C, H, W), which T is the temporal length of the video.\n            self.register_buffer('mean',\n                                 torch.tensor(mean).view(1, -1, 1, 1), False)\n            self.register_buffer('std',\n                                 torch.tensor(std).view(1, -1, 1, 1), False)\n\n    def forward(self, data: dict, training: bool = False) -> Dict:\n        \"\"\"Perform normalization,padding and bgr2rgb conversion based on\n        ``TrackDataPreprocessor``.\n\n        Args:\n            data (dict): data sampled from dataloader.\n            training (bool): Whether to enable training time augmentation.\n\n        Returns:\n            Tuple[Dict[str, List[torch.Tensor]], OptSampleList]: Data in the\n            same format as the model input.\n        \"\"\"\n        if self.use_det_processor and training:\n            batch_pad_shape = self._get_pad_shape(data)\n        else:\n            batch_pad_shape = self._get_track_pad_shape(data)\n\n        data = self.cast_data(data)\n        imgs, data_samples = data['inputs'], data['data_samples']\n\n        if self.use_det_processor and training:\n            assert imgs[0].dim() == 3, \\\n                'Only support the 3 dims when use detpreprocessor in training'\n            if self._channel_conversion:\n                imgs = [_img[[2, 1, 0], ...] for _img in imgs]\n            # Convert to `float`\n            imgs = [_img.float() for _img in imgs]\n            if self._enable_normalize:\n                imgs = [(_img - self.mean) / self.std for _img in imgs]\n            inputs = stack_batch(imgs, self.pad_size_divisor, self.pad_value)\n        else:\n            assert imgs[0].dim() == 4, \\\n                'Only support the 4 dims when use trackprocessor in training'\n            # The shape of imgs[0] is (T, C, H, W).\n            channel = imgs[0].size(1)\n            if self._channel_conversion and channel == 3:\n                imgs = [_img[:, [2, 1, 0], ...] for _img in imgs]\n            # change to `float`\n            imgs = [_img.float() for _img in imgs]\n            if self._enable_normalize:\n                imgs = [(_img - self.mean) / self.std for _img in imgs]\n            inputs = stack_track_batch(imgs, self.pad_size_divisor,\n                                       self.pad_value)\n\n        if data_samples is not None:\n            # NOTE the batched image size information may be useful, e.g.\n            # in DETR, this is needed for the construction of masks, which is\n            # then used for the transformer_head.\n            batch_input_shape = tuple(inputs.size()[-2:])\n            if self.use_det_processor and training:\n                for data_sample, pad_shape in zip(data_samples,\n                                                  batch_pad_shape):\n                    data_sample.set_metainfo({\n                        'batch_input_shape': batch_input_shape,\n                        'pad_shape': pad_shape\n                    })\n                if self.boxtype2tensor:\n                    samplelist_boxtype2tensor(data_samples)\n                if self.pad_mask:\n                    self.pad_gt_masks(data_samples)\n            else:\n                for track_data_sample, pad_shapes in zip(\n                        data_samples, batch_pad_shape):\n                    for i in range(len(track_data_sample)):\n                        det_data_sample = track_data_sample[i]\n                        det_data_sample.set_metainfo({\n                            'batch_input_shape': batch_input_shape,\n                            'pad_shape': pad_shapes[i]\n                        })\n                if self.pad_mask and training:\n                    self.pad_track_gt_masks(data_samples)\n\n        if training and self.batch_augments is not None:\n            for batch_aug in self.batch_augments:\n                if self.use_det_processor and training:\n                    inputs, data_samples = batch_aug(inputs, data_samples)\n                else:\n                    # we only support T==1 when using batch augments.\n                    # Only yolox need batch_aug, and yolox can only process\n                    # (N, C, H, W) shape.\n                    # The shape of `inputs` is (N, T, C, H, W), hence, we use\n                    # inputs[:, 0] to change the shape to (N, C, H, W).\n                    assert inputs.size(1) == 1 and len(\n                        data_samples[0]\n                    ) == 1, 'Only support the number of sequence images equals to 1 when using batch augment.'  # noqa: E501\n                    det_data_samples = [\n                        track_data_sample[0]\n                        for track_data_sample in data_samples\n                    ]\n                    aug_inputs, aug_det_samples = batch_aug(\n                        inputs[:, 0], det_data_samples)\n                    inputs = aug_inputs.unsqueeze(1)\n                    for track_data_sample, det_sample in zip(\n                            data_samples, aug_det_samples):\n                        track_data_sample.video_data_samples = [det_sample]\n\n        # Note: inputs may contain large number of frames, so we must make\n        # sure that the mmeory is contiguous for stable forward\n        inputs = inputs.contiguous()\n\n        return dict(inputs=inputs, data_samples=data_samples)\n\n    def _get_track_pad_shape(self, data: dict) -> Dict[str, List]:\n        \"\"\"Get the pad_shape of each image based on data and pad_size_divisor.\n\n        Args:\n            data (dict): Data sampled from dataloader.\n\n        Returns:\n            Dict[str, List]: The shape of padding.\n        \"\"\"\n        batch_pad_shape = dict()\n        batch_pad_shape = []\n        for imgs in data['inputs']:\n            # The sequence images in one sample among a batch have the same\n            # original shape\n            pad_h = int(np.ceil(imgs.shape[-2] /\n                                self.pad_size_divisor)) * self.pad_size_divisor\n            pad_w = int(np.ceil(imgs.shape[-1] /\n                                self.pad_size_divisor)) * self.pad_size_divisor\n            pad_shapes = [(pad_h, pad_w)] * imgs.size(0)\n            batch_pad_shape.append(pad_shapes)\n        return batch_pad_shape\n\n    def pad_track_gt_masks(self,\n                           data_samples: Sequence[TrackDataSample]) -> None:\n        \"\"\"Pad gt_masks to shape of batch_input_shape.\"\"\"\n        if 'masks' in data_samples[0][0].get('gt_instances', None):\n            for track_data_sample in data_samples:\n                for i in range(len(track_data_sample)):\n                    det_data_sample = track_data_sample[i]\n                    masks = det_data_sample.gt_instances.masks\n                    # TODO: whether to use BitmapMasks\n                    assert isinstance(masks, BitmapMasks)\n                    batch_input_shape = det_data_sample.batch_input_shape\n                    det_data_sample.gt_instances.masks = masks.pad(\n                        batch_input_shape, pad_val=self.mask_pad_value)\n\n\ndef stack_track_batch(tensors: List[torch.Tensor],\n                      pad_size_divisor: int = 0,\n                      pad_value: Union[int, float] = 0) -> torch.Tensor:\n    \"\"\"Stack multiple tensors to form a batch and pad the images to the max\n    shape use the right bottom padding mode in these images. If\n    ``pad_size_divisor > 0``, add padding to ensure the common height and width\n    is divisible by ``pad_size_divisor``. The difference between this function\n    and ``stack_batch`` in MMEngine is that this function can process batch\n    sequence images with shape (N, T, C, H, W).\n\n    Args:\n        tensors (List[Tensor]): The input multiple tensors. each is a\n            TCHW 4D-tensor. T denotes the number of key/reference frames.\n        pad_size_divisor (int): If ``pad_size_divisor > 0``, add padding\n            to ensure the common height and width is divisible by\n            ``pad_size_divisor``. This depends on the model, and many\n            models need a divisibility of 32. Defaults to 0\n        pad_value (int, float): The padding value. Defaults to 0\n\n    Returns:\n       Tensor: The NTCHW 5D-tensor. N denotes the batch size.\n    \"\"\"\n    assert isinstance(tensors, list), \\\n        f'Expected input type to be list, but got {type(tensors)}'\n    assert len(set([tensor.ndim for tensor in tensors])) == 1, \\\n        f'Expected the dimensions of all tensors must be the same, ' \\\n        f'but got {[tensor.ndim for tensor in tensors]}'\n    assert tensors[0].ndim == 4, f'Expected tensor dimension to be 4, ' \\\n                                 f'but got {tensors[0].ndim}'\n    assert len(set([tensor.shape[0] for tensor in tensors])) == 1, \\\n        f'Expected the channels of all tensors must be the same, ' \\\n        f'but got {[tensor.shape[0] for tensor in tensors]}'\n\n    tensor_sizes = [(tensor.shape[-2], tensor.shape[-1]) for tensor in tensors]\n    max_size = np.stack(tensor_sizes).max(0)\n\n    if pad_size_divisor > 1:\n        # the last two dims are H,W, both subject to divisibility requirement\n        max_size = (\n            max_size +\n            (pad_size_divisor - 1)) // pad_size_divisor * pad_size_divisor\n\n    padded_samples = []\n    for tensor in tensors:\n        padding_size = [\n            0, max_size[-1] - tensor.shape[-1], 0,\n            max_size[-2] - tensor.shape[-2]\n        ]\n        if sum(padding_size) == 0:\n            padded_samples.append(tensor)\n        else:\n            padded_samples.append(F.pad(tensor, padding_size, value=pad_value))\n\n    return torch.stack(padded_samples, dim=0)\n"
  },
  {
    "path": "mmdet/models/dense_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .anchor_free_head import AnchorFreeHead\nfrom .anchor_head import AnchorHead\nfrom .atss_head import ATSSHead\nfrom .atss_vlfusion_head import ATSSVLFusionHead\nfrom .autoassign_head import AutoAssignHead\nfrom .boxinst_head import BoxInstBboxHead, BoxInstMaskHead\nfrom .cascade_rpn_head import CascadeRPNHead, StageCascadeRPNHead\nfrom .centernet_head import CenterNetHead\nfrom .centernet_update_head import CenterNetUpdateHead\nfrom .centripetal_head import CentripetalHead\nfrom .condinst_head import CondInstBboxHead, CondInstMaskHead\nfrom .conditional_detr_head import ConditionalDETRHead\nfrom .corner_head import CornerHead\nfrom .dab_detr_head import DABDETRHead\nfrom .ddod_head import DDODHead\nfrom .ddq_detr_head import DDQDETRHead\nfrom .deformable_detr_head import DeformableDETRHead\nfrom .detr_head import DETRHead\nfrom .dino_head import DINOHead\nfrom .embedding_rpn_head import EmbeddingRPNHead\nfrom .fcos_head import FCOSHead\nfrom .fovea_head import FoveaHead\nfrom .free_anchor_retina_head import FreeAnchorRetinaHead\nfrom .fsaf_head import FSAFHead\nfrom .ga_retina_head import GARetinaHead\nfrom .ga_rpn_head import GARPNHead\nfrom .gfl_head import GFLHead\nfrom .grounding_dino_head import GroundingDINOHead\nfrom .guided_anchor_head import FeatureAdaption, GuidedAnchorHead\nfrom .lad_head import LADHead\nfrom .ld_head import LDHead\nfrom .mask2former_head import Mask2FormerHead\nfrom .maskformer_head import MaskFormerHead\nfrom .nasfcos_head import NASFCOSHead\nfrom .paa_head import PAAHead\nfrom .pisa_retinanet_head import PISARetinaHead\nfrom .pisa_ssd_head import PISASSDHead\nfrom .reppoints_head import RepPointsHead\nfrom .retina_head import RetinaHead\nfrom .retina_sepbn_head import RetinaSepBNHead\nfrom .rpn_head import RPNHead\nfrom .rtmdet_head import RTMDetHead, RTMDetSepBNHead\nfrom .rtmdet_ins_head import RTMDetInsHead, RTMDetInsSepBNHead\nfrom .sabl_retina_head import SABLRetinaHead\nfrom .solo_head import DecoupledSOLOHead, DecoupledSOLOLightHead, SOLOHead\nfrom .solov2_head import SOLOV2Head\nfrom .ssd_head import SSDHead\nfrom .tood_head import TOODHead\nfrom .vfnet_head import VFNetHead\nfrom .yolact_head import YOLACTHead, YOLACTProtonet\nfrom .yolo_head import YOLOV3Head\nfrom .yolof_head import YOLOFHead\nfrom .yolox_head import YOLOXHead\n\n__all__ = [\n    'AnchorFreeHead', 'AnchorHead', 'GuidedAnchorHead', 'FeatureAdaption',\n    'RPNHead', 'GARPNHead', 'RetinaHead', 'RetinaSepBNHead', 'GARetinaHead',\n    'SSDHead', 'FCOSHead', 'RepPointsHead', 'FoveaHead',\n    'FreeAnchorRetinaHead', 'ATSSHead', 'FSAFHead', 'NASFCOSHead',\n    'PISARetinaHead', 'PISASSDHead', 'GFLHead', 'CornerHead', 'YOLACTHead',\n    'YOLACTProtonet', 'YOLOV3Head', 'PAAHead', 'SABLRetinaHead',\n    'CentripetalHead', 'VFNetHead', 'StageCascadeRPNHead', 'CascadeRPNHead',\n    'EmbeddingRPNHead', 'LDHead', 'AutoAssignHead', 'DETRHead', 'YOLOFHead',\n    'DeformableDETRHead', 'CenterNetHead', 'YOLOXHead', 'SOLOHead',\n    'DecoupledSOLOHead', 'DecoupledSOLOLightHead', 'SOLOV2Head', 'LADHead',\n    'TOODHead', 'MaskFormerHead', 'Mask2FormerHead', 'DDODHead',\n    'CenterNetUpdateHead', 'RTMDetHead', 'RTMDetSepBNHead', 'CondInstBboxHead',\n    'CondInstMaskHead', 'RTMDetInsHead', 'RTMDetInsSepBNHead',\n    'BoxInstBboxHead', 'BoxInstMaskHead', 'ConditionalDETRHead', 'DINOHead',\n    'ATSSVLFusionHead', 'DABDETRHead', 'DDQDETRHead', 'GroundingDINOHead'\n]\n"
  },
  {
    "path": "mmdet/models/dense_heads/anchor_free_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import abstractmethod\nfrom typing import Any, List, Sequence, Tuple, Union\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom numpy import ndarray\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig, OptConfigType,\n                         OptInstanceList)\nfrom ..task_modules.prior_generators import MlvlPointGenerator\nfrom ..utils import multi_apply\nfrom .base_dense_head import BaseDenseHead\n\nStrideType = Union[Sequence[int], Sequence[Tuple[int, int]]]\n\n\n@MODELS.register_module()\nclass AnchorFreeHead(BaseDenseHead):\n    \"\"\"Anchor-free head (FCOS, Fovea, RepPoints, etc.).\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels. Used in child classes.\n        stacked_convs (int): Number of stacking convs of the head.\n        strides (Sequence[int] or Sequence[Tuple[int, int]]): Downsample\n            factor of each feature map.\n        dcn_on_last_conv (bool): If true, use dcn in the last layer of\n            towers. Defaults to False.\n        conv_bias (bool or str): If specified as `auto`, it will be decided by\n            the norm_cfg. Bias of conv will be set as True if `norm_cfg` is\n            None, otherwise False. Default: \"auto\".\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of localization loss.\n        bbox_coder (:obj:`ConfigDict` or dict): Config of bbox coder. Defaults\n            'DistancePointBBoxCoder'.\n        conv_cfg (:obj:`ConfigDict` or dict, Optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict, Optional): Config dict for\n            normalization layer. Defaults to None.\n        train_cfg (:obj:`ConfigDict` or dict, Optional): Training config of\n            anchor-free head.\n        test_cfg (:obj:`ConfigDict` or dict, Optional): Testing config of\n            anchor-free head.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n    \"\"\"  # noqa: W605\n\n    _version = 1\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int,\n        feat_channels: int = 256,\n        stacked_convs: int = 4,\n        strides: StrideType = (4, 8, 16, 32, 64),\n        dcn_on_last_conv: bool = False,\n        conv_bias: Union[bool, str] = 'auto',\n        loss_cls: ConfigType = dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox: ConfigType = dict(type='IoULoss', loss_weight=1.0),\n        bbox_coder: ConfigType = dict(type='DistancePointBBoxCoder'),\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        init_cfg: MultiConfig = dict(\n            type='Normal',\n            layer='Conv2d',\n            std=0.01,\n            override=dict(\n                type='Normal', name='conv_cls', std=0.01, bias_prob=0.01))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.num_classes = num_classes\n        self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False)\n        if self.use_sigmoid_cls:\n            self.cls_out_channels = num_classes\n        else:\n            self.cls_out_channels = num_classes + 1\n        self.in_channels = in_channels\n        self.feat_channels = feat_channels\n        self.stacked_convs = stacked_convs\n        self.strides = strides\n        self.dcn_on_last_conv = dcn_on_last_conv\n        assert conv_bias == 'auto' or isinstance(conv_bias, bool)\n        self.conv_bias = conv_bias\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox = MODELS.build(loss_bbox)\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n\n        self.prior_generator = MlvlPointGenerator(strides)\n\n        # In order to keep a more general interface and be consistent with\n        # anchor_head. We can think of point like one anchor\n        self.num_base_priors = self.prior_generator.num_base_priors[0]\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.fp16_enabled = False\n\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self._init_cls_convs()\n        self._init_reg_convs()\n        self._init_predictor()\n\n    def _init_cls_convs(self) -> None:\n        \"\"\"Initialize classification conv layers of the head.\"\"\"\n        self.cls_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            if self.dcn_on_last_conv and i == self.stacked_convs - 1:\n                conv_cfg = dict(type='DCNv2')\n            else:\n                conv_cfg = self.conv_cfg\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    bias=self.conv_bias))\n\n    def _init_reg_convs(self) -> None:\n        \"\"\"Initialize bbox regression conv layers of the head.\"\"\"\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            if self.dcn_on_last_conv and i == self.stacked_convs - 1:\n                conv_cfg = dict(type='DCNv2')\n            else:\n                conv_cfg = self.conv_cfg\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    bias=self.conv_bias))\n\n    def _init_predictor(self) -> None:\n        \"\"\"Initialize predictor layers of the head.\"\"\"\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n        self.conv_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1)\n\n    def _load_from_state_dict(self, state_dict: dict, prefix: str,\n                              local_metadata: dict, strict: bool,\n                              missing_keys: Union[List[str], str],\n                              unexpected_keys: Union[List[str], str],\n                              error_msgs: Union[List[str], str]) -> None:\n        \"\"\"Hack some keys of the model state dict so that can load checkpoints\n        of previous version.\"\"\"\n        version = local_metadata.get('version', None)\n        if version is None:\n            # the key is different in early versions\n            # for example, 'fcos_cls' become 'conv_cls' now\n            bbox_head_keys = [\n                k for k in state_dict.keys() if k.startswith(prefix)\n            ]\n            ori_predictor_keys = []\n            new_predictor_keys = []\n            # e.g. 'fcos_cls' or 'fcos_reg'\n            for key in bbox_head_keys:\n                ori_predictor_keys.append(key)\n                key = key.split('.')\n                if len(key) < 2:\n                    conv_name = None\n                elif key[1].endswith('cls'):\n                    conv_name = 'conv_cls'\n                elif key[1].endswith('reg'):\n                    conv_name = 'conv_reg'\n                elif key[1].endswith('centerness'):\n                    conv_name = 'conv_centerness'\n                else:\n                    conv_name = None\n                if conv_name is not None:\n                    key[1] = conv_name\n                    new_predictor_keys.append('.'.join(key))\n                else:\n                    ori_predictor_keys.pop(-1)\n            for i in range(len(new_predictor_keys)):\n                state_dict[new_predictor_keys[i]] = state_dict.pop(\n                    ori_predictor_keys[i])\n        super()._load_from_state_dict(state_dict, prefix, local_metadata,\n                                      strict, missing_keys, unexpected_keys,\n                                      error_msgs)\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually contain classification scores and bbox predictions.\n\n            - cls_scores (list[Tensor]): Box scores for each scale level, \\\n            each is a 4D-tensor, the channel number is \\\n            num_points * num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for each scale \\\n            level, each is a 4D-tensor, the channel number is num_points * 4.\n        \"\"\"\n        return multi_apply(self.forward_single, x)[:2]\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, ...]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n\n        Returns:\n            tuple: Scores for each class, bbox predictions, features\n            after classification and regression conv layers, some\n            models needs these features like FCOS.\n        \"\"\"\n        cls_feat = x\n        reg_feat = x\n\n        for cls_layer in self.cls_convs:\n            cls_feat = cls_layer(cls_feat)\n        cls_score = self.conv_cls(cls_feat)\n\n        for reg_layer in self.reg_convs:\n            reg_feat = reg_layer(reg_feat)\n        bbox_pred = self.conv_reg(reg_feat)\n        return cls_score, bbox_pred, cls_feat, reg_feat\n\n    @abstractmethod\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is\n                num_points * num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is\n                num_points * 4.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n        \"\"\"\n\n        raise NotImplementedError\n\n    @abstractmethod\n    def get_targets(self, points: List[Tensor],\n                    batch_gt_instances: InstanceList) -> Any:\n        \"\"\"Compute regression, classification and centerness targets for points\n        in multiple images.\n\n        Args:\n            points (list[Tensor]): Points of each fpn level, each has shape\n                (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n        \"\"\"\n        raise NotImplementedError\n\n    # TODO refactor aug_test\n    def aug_test(self,\n                 aug_batch_feats: List[Tensor],\n                 aug_batch_img_metas: List[List[Tensor]],\n                 rescale: bool = False) -> List[ndarray]:\n        \"\"\"Test function with test time augmentation.\n\n        Args:\n            aug_batch_feats (list[Tensor]): the outer list indicates test-time\n                augmentations and inner Tensor should have a shape NxCxHxW,\n                which contains features for all images in the batch.\n            aug_batch_img_metas (list[list[dict]]): the outer list indicates\n                test-time augs (multiscale, flip, etc.) and the inner list\n                indicates images in a batch. each dict has image information.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[ndarray]: bbox results of each class\n        \"\"\"\n        return self.aug_test_bboxes(\n            aug_batch_feats, aug_batch_img_metas, rescale=rescale)\n"
  },
  {
    "path": "mmdet/models/dense_heads/anchor_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import BaseBoxes, cat_boxes, get_box_tensor\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, OptMultiConfig)\nfrom ..task_modules.prior_generators import (AnchorGenerator,\n                                             anchor_inside_flags)\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import images_to_levels, multi_apply, unmap\nfrom .base_dense_head import BaseDenseHead\n\n\n@MODELS.register_module()\nclass AnchorHead(BaseDenseHead):\n    \"\"\"Anchor-based head (RPN, RetinaNet, SSD, etc.).\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels. Used in child classes.\n        anchor_generator (dict): Config dict for anchor generator\n        bbox_coder (dict): Config of bounding box coder.\n        reg_decoded_bbox (bool): If true, the regression loss would be\n            applied directly on decoded bounding boxes, converting both\n            the predicted boxes and regression targets to absolute\n            coordinates format. Default False. It should be `True` when\n            using `IoULoss`, `GIoULoss`, or `DIoULoss` in the bbox head.\n        loss_cls (dict): Config of classification loss.\n        loss_bbox (dict): Config of localization loss.\n        train_cfg (dict): Training config of anchor head.\n        test_cfg (dict): Testing config of anchor head.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"  # noqa: W605\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int,\n        feat_channels: int = 256,\n        anchor_generator: ConfigType = dict(\n            type='AnchorGenerator',\n            scales=[8, 16, 32],\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        bbox_coder: ConfigType = dict(\n            type='DeltaXYWHBBoxCoder',\n            clip_border=True,\n            target_means=(.0, .0, .0, .0),\n            target_stds=(1.0, 1.0, 1.0, 1.0)),\n        reg_decoded_bbox: bool = False,\n        loss_cls: ConfigType = dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox: ConfigType = dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0),\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        init_cfg: OptMultiConfig = dict(\n            type='Normal', layer='Conv2d', std=0.01)\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.num_classes = num_classes\n        self.feat_channels = feat_channels\n        self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False)\n        if self.use_sigmoid_cls:\n            self.cls_out_channels = num_classes\n        else:\n            self.cls_out_channels = num_classes + 1\n\n        if self.cls_out_channels <= 0:\n            raise ValueError(f'num_classes={num_classes} is too small')\n        self.reg_decoded_bbox = reg_decoded_bbox\n\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox = MODELS.build(loss_bbox)\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            if train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler(context=self)\n\n        self.fp16_enabled = False\n\n        self.prior_generator = TASK_UTILS.build(anchor_generator)\n\n        # Usually the numbers of anchors for each level are the same\n        # except SSD detectors. So it is an int in the most dense\n        # heads but a list of int in SSDHead\n        self.num_base_priors = self.prior_generator.num_base_priors[0]\n        self._init_layers()\n\n    @property\n    def num_anchors(self) -> int:\n        warnings.warn('DeprecationWarning: `num_anchors` is deprecated, '\n                      'for consistency or also use '\n                      '`num_base_priors` instead')\n        return self.prior_generator.num_base_priors[0]\n\n    @property\n    def anchor_generator(self) -> AnchorGenerator:\n        warnings.warn('DeprecationWarning: anchor_generator is deprecated, '\n                      'please use \"prior_generator\" instead')\n        return self.prior_generator\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.conv_cls = nn.Conv2d(self.in_channels,\n                                  self.num_base_priors * self.cls_out_channels,\n                                  1)\n        reg_dim = self.bbox_coder.encode_size\n        self.conv_reg = nn.Conv2d(self.in_channels,\n                                  self.num_base_priors * reg_dim, 1)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n\n        Returns:\n            tuple:\n                cls_score (Tensor): Cls scores for a single scale level \\\n                    the channels number is num_base_priors * num_classes.\n                bbox_pred (Tensor): Box energies / deltas for a single scale \\\n                    level, the channels number is num_base_priors * 4.\n        \"\"\"\n        cls_score = self.conv_cls(x)\n        bbox_pred = self.conv_reg(x)\n        return cls_score, bbox_pred\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores and bbox prediction.\n\n                - cls_scores (list[Tensor]): Classification scores for all \\\n                    scale levels, each is a 4D-tensor, the channels number \\\n                    is num_base_priors * num_classes.\n                - bbox_preds (list[Tensor]): Box energies / deltas for all \\\n                    scale levels, each is a 4D-tensor, the channels number \\\n                    is num_base_priors * 4.\n        \"\"\"\n        return multi_apply(self.forward_single, x)\n\n    def get_anchors(self,\n                    featmap_sizes: List[tuple],\n                    batch_img_metas: List[dict],\n                    device: Union[torch.device, str] = 'cuda') \\\n            -> Tuple[List[List[Tensor]], List[List[Tensor]]]:\n        \"\"\"Get anchors according to feature map sizes.\n\n        Args:\n            featmap_sizes (list[tuple]): Multi-level feature map sizes.\n            batch_img_metas (list[dict]): Image meta info.\n            device (torch.device | str): Device for returned tensors.\n                Defaults to cuda.\n\n        Returns:\n            tuple:\n\n                - anchor_list (list[list[Tensor]]): Anchors of each image.\n                - valid_flag_list (list[list[Tensor]]): Valid flags of each\n                  image.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n\n        # since feature map sizes of all images are the same, we only compute\n        # anchors for one time\n        multi_level_anchors = self.prior_generator.grid_priors(\n            featmap_sizes, device=device)\n        anchor_list = [multi_level_anchors for _ in range(num_imgs)]\n\n        # for each image, we compute valid flags of multi level anchors\n        valid_flag_list = []\n        for img_id, img_meta in enumerate(batch_img_metas):\n            multi_level_flags = self.prior_generator.valid_flags(\n                featmap_sizes, img_meta['pad_shape'], device)\n            valid_flag_list.append(multi_level_flags)\n\n        return anchor_list, valid_flag_list\n\n    def _get_targets_single(self,\n                            flat_anchors: Union[Tensor, BaseBoxes],\n                            valid_flags: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors in a\n        single image.\n\n        Args:\n            flat_anchors (Tensor or :obj:`BaseBoxes`): Multi-level anchors\n                of the image, which are concatenated into a single tensor\n                or box type of shape (num_anchors, 4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors, ).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.  Defaults to True.\n\n        Returns:\n            tuple:\n\n                - labels (Tensor): Labels of each level.\n                - label_weights (Tensor): Label weights of each level.\n                - bbox_targets (Tensor): BBox targets of each level.\n                - bbox_weights (Tensor): BBox weights of each level.\n                - pos_inds (Tensor): positive samples indexes.\n                - neg_inds (Tensor): negative samples indexes.\n                - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # assign gt and sample anchors\n        anchors = flat_anchors[inside_flags]\n\n        pred_instances = InstanceData(priors=anchors)\n        assign_result = self.assigner.assign(pred_instances, gt_instances,\n                                             gt_instances_ignore)\n        # No sampling is required except for RPN and\n        # Guided Anchoring algorithms\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        target_dim = gt_instances.bboxes.size(-1) if self.reg_decoded_bbox \\\n            else self.bbox_coder.encode_size\n        bbox_targets = anchors.new_zeros(num_valid_anchors, target_dim)\n        bbox_weights = anchors.new_zeros(num_valid_anchors, target_dim)\n\n        # TODO: Considering saving memory, is it necessary to be long?\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        # `bbox_coder.encode` accepts tensor or box type inputs and generates\n        # tensor targets. If regressing decoded boxes, the code will convert\n        # box type `pos_bbox_targets` to tensor.\n        if len(pos_inds) > 0:\n            if not self.reg_decoded_bbox:\n                pos_bbox_targets = self.bbox_coder.encode(\n                    sampling_result.pos_priors, sampling_result.pos_gt_bboxes)\n            else:\n                pos_bbox_targets = sampling_result.pos_gt_bboxes\n                pos_bbox_targets = get_box_tensor(pos_bbox_targets)\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1.0\n\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags,\n                fill=self.num_classes)  # fill bg label\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags)\n            bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags)\n\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                neg_inds, sampling_result)\n\n    def get_targets(self,\n                    anchor_list: List[List[Tensor]],\n                    valid_flag_list: List[List[Tensor]],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs: bool = True,\n                    return_sampling_results: bool = False) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors in\n        multiple images.\n\n        Args:\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, 4).\n            valid_flag_list (list[list[Tensor]]): Multi level valid flags of\n                each image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, )\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors. Defaults to True.\n            return_sampling_results (bool): Whether to return the sampling\n                results. Defaults to False.\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n\n                - labels_list (list[Tensor]): Labels of each level.\n                - label_weights_list (list[Tensor]): Label weights of each\n                  level.\n                - bbox_targets_list (list[Tensor]): BBox targets of each level.\n                - bbox_weights_list (list[Tensor]): BBox weights of each level.\n                - avg_factor (int): Average factor that is used to average\n                  the loss. When using sampling method, avg_factor is usually\n                  the sum of positive and negative priors. When using\n                  `PseudoSampler`, `avg_factor` is usually equal to the number\n                  of positive priors.\n\n            additional_returns: This function enables user-defined returns from\n                `self._get_targets_single`. These returns are currently refined\n                to properties at each feature map (i.e. having HxW dimension).\n                The results will be concatenated after the end\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        # concat all level anchors to a single tensor\n        concat_anchor_list = []\n        concat_valid_flag_list = []\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            concat_anchor_list.append(cat_boxes(anchor_list[i]))\n            concat_valid_flag_list.append(torch.cat(valid_flag_list[i]))\n\n        # compute targets for each image\n        results = multi_apply(\n            self._get_targets_single,\n            concat_anchor_list,\n            concat_valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore,\n            unmap_outputs=unmap_outputs)\n        (all_labels, all_label_weights, all_bbox_targets, all_bbox_weights,\n         pos_inds_list, neg_inds_list, sampling_results_list) = results[:7]\n        rest_results = list(results[7:])  # user-added return values\n        # Get `avg_factor` of all images, which calculate in `SamplingResult`.\n        # When using sampling method, avg_factor is usually the sum of\n        # positive and negative priors. When using `PseudoSampler`,\n        # `avg_factor` is usually equal to the number of positive priors.\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # update `_raw_positive_infos`, which will be used when calling\n        # `get_positive_infos`.\n        self._raw_positive_infos.update(sampling_results=sampling_results_list)\n        # split targets to a list w.r.t. multiple levels\n        labels_list = images_to_levels(all_labels, num_level_anchors)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors)\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors)\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_anchors)\n        res = (labels_list, label_weights_list, bbox_targets_list,\n               bbox_weights_list, avg_factor)\n        if return_sampling_results:\n            res = res + (sampling_results_list, )\n        for i, r in enumerate(rest_results):  # user-added return values\n            rest_results[i] = images_to_levels(r, num_level_anchors)\n\n        return res + tuple(rest_results)\n\n    def loss_by_feat_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            anchors: Tensor, labels: Tensor,\n                            label_weights: Tensor, bbox_targets: Tensor,\n                            bbox_weights: Tensor, avg_factor: int) -> tuple:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W).\n            bbox_pred (Tensor): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            anchors (Tensor): Box reference for each scale level with shape\n                (N, num_total_anchors, 4).\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors)\n            bbox_targets (Tensor): BBox regression targets of each anchor\n                weight shape (N, num_total_anchors, 4).\n            bbox_weights (Tensor): BBox regression loss weights of each anchor\n                with shape (N, num_total_anchors, 4).\n            avg_factor (int): Average factor that is used to average the loss.\n\n        Returns:\n            tuple: loss components.\n        \"\"\"\n        # classification loss\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        cls_score = cls_score.permute(0, 2, 3,\n                                      1).reshape(-1, self.cls_out_channels)\n        loss_cls = self.loss_cls(\n            cls_score, labels, label_weights, avg_factor=avg_factor)\n        # regression loss\n        target_dim = bbox_targets.size(-1)\n        bbox_targets = bbox_targets.reshape(-1, target_dim)\n        bbox_weights = bbox_weights.reshape(-1, target_dim)\n        bbox_pred = bbox_pred.permute(0, 2, 3,\n                                      1).reshape(-1,\n                                                 self.bbox_coder.encode_size)\n        if self.reg_decoded_bbox:\n            # When the regression loss (e.g. `IouLoss`, `GIouLoss`)\n            # is applied directly on the decoded bounding boxes, it\n            # decodes the already encoded coordinates to absolute format.\n            anchors = anchors.reshape(-1, anchors.size(-1))\n            bbox_pred = self.bbox_coder.decode(anchors, bbox_pred)\n            bbox_pred = get_box_tensor(bbox_pred)\n        loss_bbox = self.loss_bbox(\n            bbox_pred, bbox_targets, bbox_weights, avg_factor=avg_factor)\n        return loss_cls, loss_bbox\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor) = cls_reg_targets\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        # concat all level anchors and flags to a single tensor\n        concat_anchor_list = []\n        for i in range(len(anchor_list)):\n            concat_anchor_list.append(cat_boxes(anchor_list[i]))\n        all_anchor_list = images_to_levels(concat_anchor_list,\n                                           num_level_anchors)\n\n        losses_cls, losses_bbox = multi_apply(\n            self.loss_by_feat_single,\n            cls_scores,\n            bbox_preds,\n            all_anchor_list,\n            labels_list,\n            label_weights_list,\n            bbox_targets_list,\n            bbox_weights_list,\n            avg_factor=avg_factor)\n        return dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n"
  },
  {
    "path": "mmdet/models/dense_heads/atss_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, Scale\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig, OptConfigType,\n                         OptInstanceList, reduce_mean)\nfrom ..task_modules.prior_generators import anchor_inside_flags\nfrom ..utils import images_to_levels, multi_apply, unmap\nfrom .anchor_head import AnchorHead\n\n\n@MODELS.register_module()\nclass ATSSHead(AnchorHead):\n    \"\"\"Detection Head of `ATSS <https://arxiv.org/abs/1912.02424>`_.\n\n    ATSS head structure is similar with FCOS, however ATSS use anchor boxes\n    and assign label by Adaptive Training Sample Selection instead max-iou.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        pred_kernel_size (int): Kernel size of ``nn.Conv2d``\n        stacked_convs (int): Number of stacking convs of the head.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): Config dict for normalization\n            layer. Defaults to ``dict(type='GN', num_groups=32,\n            requires_grad=True)``.\n        reg_decoded_bbox (bool): If true, the regression loss would be\n            applied directly on decoded bounding boxes, converting both\n            the predicted boxes and regression targets to absolute\n            coordinates format. Defaults to False. It should be `True` when\n            using `IoULoss`, `GIoULoss`, or `DIoULoss` in the bbox head.\n        loss_centerness (:obj:`ConfigDict` or dict): Config of centerness loss.\n            Defaults to ``dict(type='CrossEntropyLoss', use_sigmoid=True,\n            loss_weight=1.0)``.\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`]): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 pred_kernel_size: int = 3,\n                 stacked_convs: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 reg_decoded_bbox: bool = True,\n                 loss_centerness: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     loss_weight=1.0),\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='atss_cls',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs) -> None:\n        self.pred_kernel_size = pred_kernel_size\n        self.stacked_convs = stacked_convs\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            reg_decoded_bbox=reg_decoded_bbox,\n            init_cfg=init_cfg,\n            **kwargs)\n\n        self.sampling = False\n        self.loss_centerness = MODELS.build(loss_centerness)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        pred_pad_size = self.pred_kernel_size // 2\n        self.atss_cls = nn.Conv2d(\n            self.feat_channels,\n            self.num_anchors * self.cls_out_channels,\n            self.pred_kernel_size,\n            padding=pred_pad_size)\n        self.atss_reg = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * 4,\n            self.pred_kernel_size,\n            padding=pred_pad_size)\n        self.atss_centerness = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * 1,\n            self.pred_kernel_size,\n            padding=pred_pad_size)\n        self.scales = nn.ModuleList(\n            [Scale(1.0) for _ in self.prior_generator.strides])\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n                cls_scores (list[Tensor]): Classification scores for all scale\n                    levels, each is a 4D-tensor, the channels number is\n                    num_anchors * num_classes.\n                bbox_preds (list[Tensor]): Box energies / deltas for all scale\n                    levels, each is a 4D-tensor, the channels number is\n                    num_anchors * 4.\n        \"\"\"\n        return multi_apply(self.forward_single, x, self.scales)\n\n    def forward_single(self, x: Tensor, scale: Scale) -> Sequence[Tensor]:\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n            scale (:obj: `mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n\n        Returns:\n            tuple:\n                cls_score (Tensor): Cls scores for a single scale level\n                    the channels number is num_anchors * num_classes.\n                bbox_pred (Tensor): Box energies / deltas for a single scale\n                    level, the channels number is num_anchors * 4.\n                centerness (Tensor): Centerness for a single scale level, the\n                    channel number is (N, num_anchors * 1, H, W).\n        \"\"\"\n        cls_feat = x\n        reg_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            reg_feat = reg_conv(reg_feat)\n        cls_score = self.atss_cls(cls_feat)\n        # we just follow atss, not apply exp in bbox_pred\n        bbox_pred = scale(self.atss_reg(reg_feat)).float()\n        centerness = self.atss_centerness(reg_feat)\n        return cls_score, bbox_pred, centerness\n\n    def loss_by_feat_single(self, anchors: Tensor, cls_score: Tensor,\n                            bbox_pred: Tensor, centerness: Tensor,\n                            labels: Tensor, label_weights: Tensor,\n                            bbox_targets: Tensor, avg_factor: float) -> dict:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W).\n            bbox_pred (Tensor): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            anchors (Tensor): Box reference for each scale level with shape\n                (N, num_total_anchors, 4).\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors)\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (N, num_total_anchors, 4).\n            avg_factor (float): Average factor that is used to average\n                the loss. When using sampling method, avg_factor is usually\n                the sum of positive and negative priors. When using\n                `PseudoSampler`, `avg_factor` is usually equal to the number\n                of positive priors.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n\n        anchors = anchors.reshape(-1, 4)\n        cls_score = cls_score.permute(0, 2, 3, 1).reshape(\n            -1, self.cls_out_channels).contiguous()\n        bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n        centerness = centerness.permute(0, 2, 3, 1).reshape(-1)\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n\n        # classification loss\n        loss_cls = self.loss_cls(\n            cls_score, labels, label_weights, avg_factor=avg_factor)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().squeeze(1)\n\n        if len(pos_inds) > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n            pos_anchors = anchors[pos_inds]\n            pos_centerness = centerness[pos_inds]\n\n            centerness_targets = self.centerness_target(\n                pos_anchors, pos_bbox_targets)\n            pos_decode_bbox_pred = self.bbox_coder.decode(\n                pos_anchors, pos_bbox_pred)\n\n            # regression loss\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_bbox_targets,\n                weight=centerness_targets,\n                avg_factor=1.0)\n\n            # centerness loss\n            loss_centerness = self.loss_centerness(\n                pos_centerness, centerness_targets, avg_factor=avg_factor)\n\n        else:\n            loss_bbox = bbox_pred.sum() * 0\n            loss_centerness = centerness.sum() * 0\n            centerness_targets = bbox_targets.new_tensor(0.)\n\n        return loss_cls, loss_bbox, loss_centerness, centerness_targets.sum()\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            centernesses: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            centernesses (list[Tensor]): Centerness for each scale\n                level with shape (N, num_anchors * 1, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in bbox_preds]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor) = cls_reg_targets\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n\n        losses_cls, losses_bbox, loss_centerness, \\\n            bbox_avg_factor = multi_apply(\n                self.loss_by_feat_single,\n                anchor_list,\n                cls_scores,\n                bbox_preds,\n                centernesses,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                avg_factor=avg_factor)\n\n        bbox_avg_factor = sum(bbox_avg_factor)\n        bbox_avg_factor = reduce_mean(bbox_avg_factor).clamp_(min=1).item()\n        losses_bbox = list(map(lambda x: x / bbox_avg_factor, losses_bbox))\n        return dict(\n            loss_cls=losses_cls,\n            loss_bbox=losses_bbox,\n            loss_centerness=loss_centerness)\n\n    def centerness_target(self, anchors: Tensor, gts: Tensor) -> Tensor:\n        \"\"\"Calculate the centerness between anchors and gts.\n\n        Only calculate pos centerness targets, otherwise there may be nan.\n\n        Args:\n            anchors (Tensor): Anchors with shape (N, 4), \"xyxy\" format.\n            gts (Tensor): Ground truth bboxes with shape (N, 4), \"xyxy\" format.\n\n        Returns:\n            Tensor: Centerness between anchors and gts.\n        \"\"\"\n        anchors_cx = (anchors[:, 2] + anchors[:, 0]) / 2\n        anchors_cy = (anchors[:, 3] + anchors[:, 1]) / 2\n        l_ = anchors_cx - gts[:, 0]\n        t_ = anchors_cy - gts[:, 1]\n        r_ = gts[:, 2] - anchors_cx\n        b_ = gts[:, 3] - anchors_cy\n\n        left_right = torch.stack([l_, r_], dim=1)\n        top_bottom = torch.stack([t_, b_], dim=1)\n        centerness = torch.sqrt(\n            (left_right.min(dim=-1)[0] / left_right.max(dim=-1)[0]) *\n            (top_bottom.min(dim=-1)[0] / top_bottom.max(dim=-1)[0]))\n        assert not torch.isnan(centerness).any()\n        return centerness\n\n    def get_targets(self,\n                    anchor_list: List[List[Tensor]],\n                    valid_flag_list: List[List[Tensor]],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs: bool = True) -> tuple:\n        \"\"\"Get targets for ATSS head.\n\n        This method is almost the same as `AnchorHead.get_targets()`. Besides\n        returning the targets as the parent method does, it also returns the\n        anchors as the first element of the returned tuple.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        num_level_anchors_list = [num_level_anchors] * num_imgs\n\n        # concat all level anchors and flags to a single tensor\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            anchor_list[i] = torch.cat(anchor_list[i])\n            valid_flag_list[i] = torch.cat(valid_flag_list[i])\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n         all_bbox_weights, pos_inds_list, neg_inds_list,\n         sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             anchor_list,\n             valid_flag_list,\n             num_level_anchors_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             unmap_outputs=unmap_outputs)\n        # Get `avg_factor` of all images, which calculate in `SamplingResult`.\n        # When using sampling method, avg_factor is usually the sum of\n        # positive and negative priors. When using `PseudoSampler`,\n        # `avg_factor` is usually equal to the number of positive priors.\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        anchors_list = images_to_levels(all_anchors, num_level_anchors)\n        labels_list = images_to_levels(all_labels, num_level_anchors)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors)\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors)\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_anchors)\n        return (anchors_list, labels_list, label_weights_list,\n                bbox_targets_list, bbox_weights_list, avg_factor)\n\n    def _get_targets_single(self,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            num_level_anchors: List[int],\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression, classification targets for anchors in a single\n        image.\n\n        Args:\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors ,4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors,).\n            num_level_anchors (List[int]): Number of anchors of each scale\n                level.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Returns:\n            tuple: N is the number of total anchors in the image.\n                labels (Tensor): Labels of all anchors in the image with shape\n                    (N,).\n                label_weights (Tensor): Label weights of all anchor in the\n                    image with shape (N,).\n                bbox_targets (Tensor): BBox targets of all anchors in the\n                    image with shape (N, 4).\n                bbox_weights (Tensor): BBox weights of all anchors in the\n                    image with shape (N, 4)\n                pos_inds (Tensor): Indices of positive anchor with shape\n                    (num_pos,).\n                neg_inds (Tensor): Indices of negative anchor with shape\n                    (num_neg,).\n                sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # assign gt and sample anchors\n        anchors = flat_anchors[inside_flags, :]\n\n        num_level_anchors_inside = self.get_num_level_anchors_inside(\n            num_level_anchors, inside_flags)\n        pred_instances = InstanceData(priors=anchors)\n        assign_result = self.assigner.assign(pred_instances,\n                                             num_level_anchors_inside,\n                                             gt_instances, gt_instances_ignore)\n\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        bbox_targets = torch.zeros_like(anchors)\n        bbox_weights = torch.zeros_like(anchors)\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            if self.reg_decoded_bbox:\n                pos_bbox_targets = sampling_result.pos_gt_bboxes\n            else:\n                pos_bbox_targets = self.bbox_coder.encode(\n                    sampling_result.pos_priors, sampling_result.pos_gt_bboxes)\n\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1.0\n\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            anchors = unmap(anchors, num_total_anchors, inside_flags)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags, fill=self.num_classes)\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags)\n            bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags)\n\n        return (anchors, labels, label_weights, bbox_targets, bbox_weights,\n                pos_inds, neg_inds, sampling_result)\n\n    def get_num_level_anchors_inside(self, num_level_anchors, inside_flags):\n        \"\"\"Get the number of valid anchors in every level.\"\"\"\n\n        split_inside_flags = torch.split(inside_flags, num_level_anchors)\n        num_level_anchors_inside = [\n            int(flags.sum()) for flags in split_inside_flags\n        ]\n        return num_level_anchors_inside\n"
  },
  {
    "path": "mmdet/models/dense_heads/atss_vlfusion_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport math\nfrom typing import Callable, List, Optional, Sequence, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Scale\nfrom mmcv.ops.modulated_deform_conv import ModulatedDeformConv2d\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModel\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\ntry:\n    from transformers import BertConfig\nexcept ImportError:\n    BertConfig = None\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import cat_boxes\nfrom mmdet.utils import InstanceList, OptInstanceList, reduce_mean\nfrom ..utils import (BertEncoderLayer, VLFuse, filter_scores_and_topk,\n                     permute_and_flatten, select_single_mlvl,\n                     unpack_gt_instances)\nfrom ..utils.vlfuse_helper import MAX_CLAMP_VALUE\nfrom .atss_head import ATSSHead\n\n\ndef convert_grounding_to_cls_scores(logits: Tensor,\n                                    positive_maps: List[dict]) -> Tensor:\n    \"\"\"Convert logits to class scores.\"\"\"\n    assert len(positive_maps) == logits.shape[0]  # batch size\n\n    scores = torch.zeros(logits.shape[0], logits.shape[1],\n                         len(positive_maps[0])).to(logits.device)\n    if positive_maps is not None:\n        if all(x == positive_maps[0] for x in positive_maps):\n            # only need to compute once\n            positive_map = positive_maps[0]\n            for label_j in positive_map:\n                scores[:, :, label_j -\n                       1] = logits[:, :,\n                                   torch.LongTensor(positive_map[label_j]\n                                                    )].mean(-1)\n        else:\n            for i, positive_map in enumerate(positive_maps):\n                for label_j in positive_map:\n                    scores[i, :, label_j - 1] = logits[\n                        i, :, torch.LongTensor(positive_map[label_j])].mean(-1)\n    return scores\n\n\nclass Conv3x3Norm(nn.Module):\n    \"\"\"Conv3x3 and norm.\"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 stride: int,\n                 groups: int = 1,\n                 use_dcn: bool = False,\n                 norm_type: Optional[Union[Sequence, str]] = None):\n        super().__init__()\n\n        if use_dcn:\n            self.conv = ModulatedDeformConv2d(\n                in_channels,\n                out_channels,\n                kernel_size=3,\n                stride=stride,\n                padding=1,\n                groups=groups)\n        else:\n            self.conv = nn.Conv2d(\n                in_channels,\n                out_channels,\n                kernel_size=3,\n                stride=stride,\n                padding=1,\n                groups=groups)\n\n        if isinstance(norm_type, Sequence):\n            assert len(norm_type) == 2\n            assert norm_type[0] == 'gn'\n            gn_group = norm_type[1]\n            norm_type = norm_type[0]\n\n        if norm_type == 'bn':\n            bn_op = nn.BatchNorm2d(out_channels)\n        elif norm_type == 'gn':\n            bn_op = nn.GroupNorm(\n                num_groups=gn_group, num_channels=out_channels)\n        if norm_type is not None:\n            self.bn = bn_op\n        else:\n            self.bn = None\n\n    def forward(self, x, **kwargs):\n        x = self.conv(x, **kwargs)\n        if self.bn:\n            x = self.bn(x)\n        return x\n\n\nclass DyReLU(nn.Module):\n    \"\"\"Dynamic ReLU.\"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 expand_ratio: int = 4):\n        super().__init__()\n        self.avg_pool = nn.AdaptiveAvgPool2d(1)\n        self.expand_ratio = expand_ratio\n        self.out_channels = out_channels\n\n        self.fc = nn.Sequential(\n            nn.Linear(in_channels, in_channels // expand_ratio),\n            nn.ReLU(inplace=True),\n            nn.Linear(in_channels // expand_ratio,\n                      out_channels * self.expand_ratio),\n            nn.Hardsigmoid(inplace=True))\n\n    def forward(self, x) -> Tensor:\n        x_out = x\n        b, c, h, w = x.size()\n        x = self.avg_pool(x).view(b, c)\n        x = self.fc(x).view(b, -1, 1, 1)\n\n        a1, b1, a2, b2 = torch.split(x, self.out_channels, dim=1)\n        a1 = (a1 - 0.5) * 2 + 1.0\n        a2 = (a2 - 0.5) * 2\n        b1 = b1 - 0.5\n        b2 = b2 - 0.5\n        out = torch.max(x_out * a1 + b1, x_out * a2 + b2)\n        return out\n\n\nclass DyConv(nn.Module):\n    \"\"\"Dynamic Convolution.\"\"\"\n\n    def __init__(self,\n                 conv_func: Callable,\n                 in_channels: int,\n                 out_channels: int,\n                 use_dyfuse: bool = True,\n                 use_dyrelu: bool = False,\n                 use_dcn: bool = False):\n        super().__init__()\n\n        self.dyconvs = nn.ModuleList()\n        self.dyconvs.append(conv_func(in_channels, out_channels, 1))\n        self.dyconvs.append(conv_func(in_channels, out_channels, 1))\n        self.dyconvs.append(conv_func(in_channels, out_channels, 2))\n\n        if use_dyfuse:\n            self.attnconv = nn.Sequential(\n                nn.AdaptiveAvgPool2d(1),\n                nn.Conv2d(in_channels, 1, kernel_size=1),\n                nn.ReLU(inplace=True))\n            self.h_sigmoid = nn.Hardsigmoid(inplace=True)\n        else:\n            self.attnconv = None\n\n        if use_dyrelu:\n            self.relu = DyReLU(in_channels, out_channels)\n        else:\n            self.relu = nn.ReLU()\n\n        if use_dcn:\n            self.offset = nn.Conv2d(\n                in_channels, 27, kernel_size=3, stride=1, padding=1)\n        else:\n            self.offset = None\n\n        self.init_weights()\n\n    def init_weights(self):\n        for m in self.dyconvs.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.normal_(m.weight.data, 0, 0.01)\n                if m.bias is not None:\n                    m.bias.data.zero_()\n        if self.attnconv is not None:\n            for m in self.attnconv.modules():\n                if isinstance(m, nn.Conv2d):\n                    nn.init.normal_(m.weight.data, 0, 0.01)\n                    if m.bias is not None:\n                        m.bias.data.zero_()\n\n    def forward(self, inputs: dict) -> dict:\n        visual_feats = inputs['visual']\n\n        out_vis_feats = []\n        for level, feature in enumerate(visual_feats):\n\n            offset_conv_args = {}\n            if self.offset is not None:\n                offset_mask = self.offset(feature)\n                offset = offset_mask[:, :18, :, :]\n                mask = offset_mask[:, 18:, :, :].sigmoid()\n                offset_conv_args = dict(offset=offset, mask=mask)\n\n            temp_feats = [self.dyconvs[1](feature, **offset_conv_args)]\n\n            if level > 0:\n                temp_feats.append(self.dyconvs[2](visual_feats[level - 1],\n                                                  **offset_conv_args))\n            if level < len(visual_feats) - 1:\n                temp_feats.append(\n                    F.upsample_bilinear(\n                        self.dyconvs[0](visual_feats[level + 1],\n                                        **offset_conv_args),\n                        size=[feature.size(2),\n                              feature.size(3)]))\n            mean_feats = torch.mean(\n                torch.stack(temp_feats), dim=0, keepdim=False)\n\n            if self.attnconv is not None:\n                attn_feat = []\n                res_feat = []\n                for feat in temp_feats:\n                    res_feat.append(feat)\n                    attn_feat.append(self.attnconv(feat))\n\n                res_feat = torch.stack(res_feat)\n                spa_pyr_attn = self.h_sigmoid(torch.stack(attn_feat))\n\n                mean_feats = torch.mean(\n                    res_feat * spa_pyr_attn, dim=0, keepdim=False)\n\n            out_vis_feats.append(mean_feats)\n\n        out_vis_feats = [self.relu(item) for item in out_vis_feats]\n\n        features_dict = {'visual': out_vis_feats, 'lang': inputs['lang']}\n\n        return features_dict\n\n\nclass VLFusionModule(BaseModel):\n    \"\"\"Visual-lang Fusion Module.\"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 feat_channels: int,\n                 num_base_priors: int,\n                 early_fuse: bool = False,\n                 num_dyhead_blocks: int = 6,\n                 lang_model_name: str = 'bert-base-uncased',\n                 use_dyrelu: bool = True,\n                 use_dyfuse: bool = True,\n                 use_dcn: bool = True,\n                 use_checkpoint: bool = False,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n        if BertConfig is None:\n            raise RuntimeError(\n                'transformers is not installed, please install it by: '\n                'pip install transformers.')\n        self.in_channels = in_channels\n        self.feat_channels = feat_channels\n        self.num_base_priors = num_base_priors\n        self.early_fuse = early_fuse\n        self.num_dyhead_blocks = num_dyhead_blocks\n        self.use_dyrelu = use_dyrelu\n        self.use_dyfuse = use_dyfuse\n        self.use_dcn = use_dcn\n        self.use_checkpoint = use_checkpoint\n\n        self.lang_cfg = BertConfig.from_pretrained(lang_model_name)\n        self.lang_dim = self.lang_cfg.hidden_size\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the model.\"\"\"\n        bias_value = -math.log((1 - 0.01) / 0.01)\n\n        dyhead_tower = []\n        for i in range(self.num_dyhead_blocks):\n            if self.early_fuse:\n                # cross-modality fusion\n                dyhead_tower.append(VLFuse(use_checkpoint=self.use_checkpoint))\n                # lang branch\n                dyhead_tower.append(\n                    BertEncoderLayer(\n                        self.lang_cfg,\n                        clamp_min_for_underflow=True,\n                        clamp_max_for_overflow=True))\n\n            # vision branch\n            dyhead_tower.append(\n                DyConv(\n                    lambda i, o, s: Conv3x3Norm(\n                        i, o, s, use_dcn=self.use_dcn, norm_type=['gn', 16]),\n                    self.in_channels if i == 0 else self.feat_channels,\n                    self.feat_channels,\n                    use_dyrelu=(self.use_dyrelu\n                                and self.in_channels == self.feat_channels)\n                    if i == 0 else self.use_dyrelu,\n                    use_dyfuse=(self.use_dyfuse\n                                and self.in_channels == self.feat_channels)\n                    if i == 0 else self.use_dyfuse,\n                    use_dcn=(self.use_dcn\n                             and self.in_channels == self.feat_channels)\n                    if i == 0 else self.use_dcn,\n                ))\n\n        self.add_module('dyhead_tower', nn.Sequential(*dyhead_tower))\n\n        self.bbox_pred = nn.Conv2d(\n            self.feat_channels, self.num_base_priors * 4, kernel_size=1)\n        self.centerness = nn.Conv2d(\n            self.feat_channels, self.num_base_priors * 1, kernel_size=1)\n        self.dot_product_projection_text = nn.Linear(\n            self.lang_dim,\n            self.num_base_priors * self.feat_channels,\n            bias=True)\n        self.log_scale = nn.Parameter(torch.Tensor([0.0]), requires_grad=True)\n        self.bias_lang = nn.Parameter(\n            torch.zeros(self.lang_dim), requires_grad=True)\n        self.bias0 = nn.Parameter(\n            torch.Tensor([bias_value]), requires_grad=True)\n        self.scales = nn.ModuleList([Scale(1.0) for _ in range(5)])\n\n    def forward(self, visual_feats: Tuple[Tensor],\n                language_feats: dict) -> Tuple:\n        feat_inputs = {'visual': visual_feats, 'lang': language_feats}\n        dyhead_tower = self.dyhead_tower(feat_inputs)\n\n        if self.early_fuse:\n            embedding = dyhead_tower['lang']['hidden']\n        else:\n            embedding = language_feats['embedded']\n\n        embedding = F.normalize(embedding, p=2, dim=-1)\n        dot_product_proj_tokens = self.dot_product_projection_text(embedding /\n                                                                   2.0)\n        dot_product_proj_tokens_bias = torch.matmul(\n            embedding, self.bias_lang) + self.bias0\n\n        bbox_preds = []\n        centerness = []\n        cls_logits = []\n\n        for i, feature in enumerate(visual_feats):\n            visual = dyhead_tower['visual'][i]\n            B, C, H, W = visual.shape\n\n            bbox_pred = self.scales[i](self.bbox_pred(visual))\n            bbox_preds.append(bbox_pred)\n            centerness.append(self.centerness(visual))\n\n            dot_product_proj_queries = permute_and_flatten(\n                visual, B, self.num_base_priors, C, H, W)\n\n            bias = dot_product_proj_tokens_bias.unsqueeze(1).repeat(\n                1, self.num_base_priors, 1)\n            dot_product_logit = (\n                torch.matmul(dot_product_proj_queries,\n                             dot_product_proj_tokens.transpose(-1, -2)) /\n                self.log_scale.exp()) + bias\n            dot_product_logit = torch.clamp(\n                dot_product_logit, max=MAX_CLAMP_VALUE)\n            dot_product_logit = torch.clamp(\n                dot_product_logit, min=-MAX_CLAMP_VALUE)\n            cls_logits.append(dot_product_logit)\n\n        return bbox_preds, centerness, cls_logits\n\n\n@MODELS.register_module()\nclass ATSSVLFusionHead(ATSSHead):\n    \"\"\"ATSS head with visual-language fusion module.\n\n    Args:\n        early_fuse (bool): Whether to fuse visual and language features\n            Defaults to False.\n        use_checkpoint (bool): Whether to use checkpoint. Defaults to False.\n        num_dyhead_blocks (int): Number of dynamic head blocks. Defaults to 6.\n        lang_model_name (str): Name of the language model.\n            Defaults to 'bert-base-uncased'.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 early_fuse: bool = False,\n                 use_checkpoint: bool = False,\n                 num_dyhead_blocks: int = 6,\n                 lang_model_name: str = 'bert-base-uncased',\n                 init_cfg=None,\n                 **kwargs):\n        super().__init__(*args, **kwargs, init_cfg=init_cfg)\n        self.head = VLFusionModule(\n            in_channels=self.in_channels,\n            feat_channels=self.feat_channels,\n            num_base_priors=self.num_base_priors,\n            early_fuse=early_fuse,\n            use_checkpoint=use_checkpoint,\n            num_dyhead_blocks=num_dyhead_blocks,\n            lang_model_name=lang_model_name)\n        self.text_masks = None\n\n    def _init_layers(self) -> None:\n        \"\"\"No need to initialize the ATSS head layer.\"\"\"\n        pass\n\n    def forward(self, visual_feats: Tuple[Tensor],\n                language_feats: dict) -> Tuple[Tensor]:\n        \"\"\"Forward function.\"\"\"\n        bbox_preds, centerness, cls_logits = self.head(visual_feats,\n                                                       language_feats)\n        return cls_logits, bbox_preds, centerness\n\n    def loss(self, visual_feats: Tuple[Tensor], language_feats: dict,\n             batch_data_samples):\n        outputs = unpack_gt_instances(batch_data_samples)\n        (batch_gt_instances, batch_gt_instances_ignore,\n         batch_img_metas) = outputs\n\n        outs = self(visual_feats, language_feats)\n        self.text_masks = language_feats['masks']\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas,\n                              batch_gt_instances_ignore)\n        losses = self.loss_by_feat(*loss_inputs)\n        return losses\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            centernesses: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            centernesses (list[Tensor]): Centerness for each scale\n                level with shape (N, num_anchors * 1, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in bbox_preds]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor) = cls_reg_targets\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n\n        anchors = torch.cat(anchor_list, dim=1)\n        labels = torch.cat(labels_list, dim=1)\n        label_weights = torch.cat(label_weights_list, dim=1)\n        bbox_targets = torch.cat(bbox_targets_list, dim=1)\n        cls_scores = torch.cat(cls_scores, dim=1)\n\n        centernesses_ = []\n        bbox_preds_ = []\n        for bbox_pred, centerness in zip(bbox_preds, centernesses):\n            centernesses_.append(\n                centerness.permute(0, 2, 3,\n                                   1).reshape(cls_scores.size(0), -1, 1))\n            bbox_preds_.append(\n                bbox_pred.permute(0, 2, 3,\n                                  1).reshape(cls_scores.size(0), -1, 4))\n        bbox_preds = torch.cat(bbox_preds_, dim=1)\n        centernesses = torch.cat(centernesses_, dim=1)\n\n        losses_cls, losses_bbox, loss_centerness, bbox_avg_factor = \\\n            self._loss_by_feat(\n                anchors,\n                cls_scores,\n                bbox_preds,\n                centernesses,\n                labels,\n                label_weights,\n                bbox_targets,\n                avg_factor=avg_factor)\n\n        bbox_avg_factor = reduce_mean(bbox_avg_factor).clamp_(min=1).item()\n        losses_bbox = losses_bbox / bbox_avg_factor\n        return dict(\n            loss_cls=losses_cls,\n            loss_bbox=losses_bbox,\n            loss_centerness=loss_centerness)\n\n    def _loss_by_feat(self, anchors: Tensor, cls_score: Tensor,\n                      bbox_pred: Tensor, centerness: Tensor, labels: Tensor,\n                      label_weights: Tensor, bbox_targets: Tensor,\n                      avg_factor: float) -> dict:\n        \"\"\"Calculate the loss of all scale level based on the features\n        extracted by the detection head.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n\n        anchors = anchors.reshape(-1, 4)\n\n        # ===== this change =====\n        pos_inds = (labels.sum(-1) > 0).reshape(-1)\n\n        # Loss is not computed for the padded regions of the text.\n        assert (self.text_masks.dim() == 2)\n        text_mask = (self.text_masks > 0).unsqueeze(1)\n        text_mask = text_mask.repeat(1, cls_score.size(1), 1)\n        cls_score = torch.masked_select(cls_score, text_mask).contiguous()\n        labels = torch.masked_select(labels, text_mask)\n        label_weights = label_weights[...,\n                                      None].repeat(1, 1, text_mask.size(-1))\n        label_weights = torch.masked_select(label_weights, text_mask)\n\n        bbox_pred = bbox_pred.reshape(-1, 4)\n        centerness = centerness.reshape(-1)\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n\n        # classification loss\n        loss_cls = self.loss_cls(\n            cls_score, labels, label_weights, avg_factor=avg_factor)\n\n        if pos_inds.sum() > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n            pos_anchors = anchors[pos_inds]\n            pos_centerness = centerness[pos_inds]\n\n            centerness_targets = self.centerness_target(\n                pos_anchors, pos_bbox_targets)\n\n            if torch.isnan(centerness_targets).any():\n                print('=====Centerness includes NaN=====')\n                mask = ~torch.isnan(centerness_targets)\n                centerness_targets = centerness_targets[mask]\n                pos_centerness = pos_centerness[mask]\n                pos_anchors = pos_anchors[mask]\n                pos_bbox_targets = pos_bbox_targets[mask]\n                pos_bbox_pred = pos_bbox_pred[mask]\n\n                if pos_bbox_targets.shape[0] == 0:\n                    loss_bbox = bbox_pred.sum() * 0\n                    loss_centerness = centerness.sum() * 0\n                    centerness_targets = bbox_targets.new_tensor(0.)\n                    return loss_cls, loss_bbox, loss_centerness, \\\n                        centerness_targets.sum()\n\n            # The decoding process takes the offset into consideration.\n            pos_anchors[:, 2:] += 1\n            pos_decode_bbox_pred = self.bbox_coder.decode(\n                pos_anchors, pos_bbox_pred)\n\n            # regression loss\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_bbox_targets,\n                weight=centerness_targets,\n                avg_factor=1.0)\n\n            # centerness loss\n            loss_centerness = self.loss_centerness(\n                pos_centerness, centerness_targets, avg_factor=avg_factor)\n        else:\n            loss_bbox = bbox_pred.sum() * 0\n            loss_centerness = centerness.sum() * 0\n            centerness_targets = bbox_targets.new_tensor(0.)\n\n        return loss_cls, loss_bbox, loss_centerness, centerness_targets.sum()\n\n    def _get_targets_single(self,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            num_level_anchors: List[int],\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression, classification targets for anchors in a single\n        image.\n\n        Args:\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors ,4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors,).\n            num_level_anchors (List[int]): Number of anchors of each scale\n                level.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Returns:\n            tuple: N is the number of total anchors in the image.\n                labels (Tensor): Labels of all anchors in the image with shape\n                    (N,).\n                label_weights (Tensor): Label weights of all anchor in the\n                    image with shape (N,).\n                bbox_targets (Tensor): BBox targets of all anchors in the\n                    image with shape (N, 4).\n                bbox_weights (Tensor): BBox weights of all anchors in the\n                    image with shape (N, 4)\n                pos_inds (Tensor): Indices of positive anchor with shape\n                    (num_pos,).\n                neg_inds (Tensor): Indices of negative anchor with shape\n                    (num_neg,).\n                sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        anchors = flat_anchors\n        # Align the official implementation\n        anchors[:, 2:] -= 1\n\n        num_level_anchors_inside = num_level_anchors\n        pred_instances = InstanceData(priors=anchors)\n        assign_result = self.assigner.assign(pred_instances,\n                                             num_level_anchors_inside,\n                                             gt_instances, gt_instances_ignore)\n\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        bbox_targets = torch.zeros_like(anchors)\n        bbox_weights = torch.zeros_like(anchors)\n\n        # ===== this change =====\n        labels = anchors.new_full((num_valid_anchors, self.feat_channels),\n                                  0,\n                                  dtype=torch.float32)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            if self.reg_decoded_bbox:\n                pos_bbox_targets = sampling_result.pos_gt_bboxes\n            else:\n                pos_bbox_targets = self.bbox_coder.encode(\n                    sampling_result.pos_priors, sampling_result.pos_gt_bboxes)\n\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1.0\n\n            # ===== this change =====\n            labels[pos_inds] = gt_instances.positive_maps[\n                sampling_result.pos_assigned_gt_inds]\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        return (anchors, labels, label_weights, bbox_targets, bbox_weights,\n                pos_inds, neg_inds, sampling_result)\n\n    def centerness_target(self, anchors: Tensor, gts: Tensor) -> Tensor:\n        \"\"\"Calculate the centerness between anchors and gts.\n\n        Only calculate pos centerness targets, otherwise there may be nan.\n\n        Args:\n            anchors (Tensor): Anchors with shape (N, 4), \"xyxy\" format.\n            gts (Tensor): Ground truth bboxes with shape (N, 4), \"xyxy\" format.\n\n        Returns:\n            Tensor: Centerness between anchors and gts.\n        \"\"\"\n        anchors_cx = (anchors[:, 2] + anchors[:, 0]) / 2\n        anchors_cy = (anchors[:, 3] + anchors[:, 1]) / 2\n        l_ = anchors_cx - gts[:, 0]\n        t_ = anchors_cy - gts[:, 1]\n        r_ = gts[:, 2] - anchors_cx\n        b_ = gts[:, 3] - anchors_cy\n\n        left_right = torch.stack([l_, r_], dim=1)\n        top_bottom = torch.stack([t_, b_], dim=1)\n        centerness = torch.sqrt(\n            (left_right.min(dim=-1)[0] / left_right.max(dim=-1)[0]) *\n            (top_bottom.min(dim=-1)[0] / top_bottom.max(dim=-1)[0]))\n        # assert not torch.isnan(centerness).any()\n        return centerness\n\n    def predict(self,\n                visual_feats: Tuple[Tensor],\n                language_feats: dict,\n                batch_data_samples,\n                rescale: bool = True):\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network.\n\n        Args:\n            visual_feats (tuple[Tensor]): Multi-level visual features from the\n                upstream network, each is a 4D-tensor.\n            language_feats (dict): Language features from the upstream network.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n        batch_token_positive_maps = [\n            data_samples.token_positive_map\n            for data_samples in batch_data_samples\n        ]\n        outs = self(visual_feats, language_feats)\n\n        predictions = self.predict_by_feat(\n            *outs,\n            batch_img_metas=batch_img_metas,\n            batch_token_positive_maps=batch_token_positive_maps,\n            rescale=rescale)\n        return predictions\n\n    def predict_by_feat(self,\n                        cls_logits: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        score_factors: List[Tensor],\n                        batch_img_metas: Optional[List[dict]] = None,\n                        batch_token_positive_maps: Optional[List[dict]] = None,\n                        cfg: Optional[ConfigDict] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Note: When score_factors is not None, the cls_scores are\n        usually multiplied by it then obtain the real score used in NMS,\n        such as CenterNess in FCOS, IoU branch in ATSS.\n\n        Args:\n            cls_logits (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            score_factors (list[Tensor], optional): Score factor for\n                all scale level, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 1, H, W). Defaults to None.\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n                Defaults to None.\n            batch_token_positive_maps (list[dict], Optional): Batch token\n                positive map. Defaults to None.\n            cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(bbox_preds) == len(score_factors)\n        num_levels = len(bbox_preds)\n\n        featmap_sizes = [bbox_preds[i].shape[-2:] for i in range(num_levels)]\n        mlvl_priors = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device)\n\n        result_list = []\n\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            token_positive_maps = batch_token_positive_maps[img_id]\n            bbox_pred_list = select_single_mlvl(\n                bbox_preds, img_id, detach=True)\n            score_factor_list = select_single_mlvl(\n                score_factors, img_id, detach=True)\n            cls_logit_list = select_single_mlvl(\n                cls_logits, img_id, detach=True)\n\n            results = self._predict_by_feat_single(\n                bbox_pred_list=bbox_pred_list,\n                score_factor_list=score_factor_list,\n                cls_logit_list=cls_logit_list,\n                mlvl_priors=mlvl_priors,\n                token_positive_maps=token_positive_maps,\n                img_meta=img_meta,\n                cfg=cfg,\n                rescale=rescale,\n                with_nms=with_nms)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                cls_logit_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                token_positive_maps: dict,\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = True,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            cls_logit_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid. In all\n                anchor-based methods, it has shape (num_priors, 4). In\n                all anchor-free methods, it has shape (num_priors, 2)\n                when `with_stride=True`, otherwise it still has shape\n                (num_priors, 4).\n            token_positive_maps (dict): Token positive map.\n            img_meta (dict): Image meta info.\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n        score_thr = cfg.get('score_thr', 0)\n\n        mlvl_bbox_preds = []\n        mlvl_valid_priors = []\n        mlvl_scores = []\n        mlvl_labels = []\n\n        for level_idx, (bbox_pred, score_factor, cls_logit, priors) in \\\n                enumerate(zip(bbox_pred_list,\n                              score_factor_list, cls_logit_list, mlvl_priors)):\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(\n                -1, self.bbox_coder.encode_size)\n            score_factor = score_factor.permute(1, 2, 0).reshape(-1).sigmoid()\n\n            scores = convert_grounding_to_cls_scores(\n                logits=cls_logit.sigmoid()[None],\n                positive_maps=[token_positive_maps])[0]\n\n            results = filter_scores_and_topk(\n                scores, score_thr, nms_pre,\n                dict(bbox_pred=bbox_pred, priors=priors))\n\n            scores, labels, keep_idxs, filtered_results = results\n\n            bbox_pred = filtered_results['bbox_pred']\n            priors = filtered_results['priors']\n            score_factor = score_factor[keep_idxs]\n            scores = torch.sqrt(scores * score_factor)\n\n            mlvl_bbox_preds.append(bbox_pred)\n            mlvl_valid_priors.append(priors)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n\n        bbox_pred = torch.cat(mlvl_bbox_preds)\n        priors = cat_boxes(mlvl_valid_priors)\n        bboxes = self.bbox_coder.decode(priors, bbox_pred, max_shape=img_shape)\n\n        results = InstanceData()\n        results.bboxes = bboxes\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n\n        predictions = self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n        if len(predictions) > 0:\n            # Note: GLIP adopts a very strange bbox decoder logic,\n            # and if 1 is not added here, it will not align with\n            # the official mAP.\n            predictions.bboxes[:, 2:] = predictions.bboxes[:, 2:] + 1\n        return predictions\n"
  },
  {
    "path": "mmdet/models/dense_heads/autoassign_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Scale\nfrom mmengine.model import bias_init_with_prob, normal_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import InstanceList, OptInstanceList, reduce_mean\nfrom ..task_modules.prior_generators import MlvlPointGenerator\nfrom ..utils import levels_to_images, multi_apply\nfrom .fcos_head import FCOSHead\n\nEPS = 1e-12\n\n\nclass CenterPrior(nn.Module):\n    \"\"\"Center Weighting module to adjust the category-specific prior\n    distributions.\n\n    Args:\n        force_topk (bool): When no point falls into gt_bbox, forcibly\n            select the k points closest to the center to calculate\n            the center prior. Defaults to False.\n        topk (int): The number of points used to calculate the\n            center prior when no point falls in gt_bbox. Only work when\n            force_topk if True. Defaults to 9.\n        num_classes (int): The class number of dataset. Defaults to 80.\n        strides (Sequence[int]): The stride of each input feature map.\n            Defaults to (8, 16, 32, 64, 128).\n    \"\"\"\n\n    def __init__(\n        self,\n        force_topk: bool = False,\n        topk: int = 9,\n        num_classes: int = 80,\n        strides: Sequence[int] = (8, 16, 32, 64, 128)\n    ) -> None:\n        super().__init__()\n        self.mean = nn.Parameter(torch.zeros(num_classes, 2))\n        self.sigma = nn.Parameter(torch.ones(num_classes, 2))\n        self.strides = strides\n        self.force_topk = force_topk\n        self.topk = topk\n\n    def forward(self, anchor_points_list: List[Tensor],\n                gt_instances: InstanceData,\n                inside_gt_bbox_mask: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Get the center prior of each point on the feature map for each\n        instance.\n\n        Args:\n            anchor_points_list (list[Tensor]): list of coordinate\n                of points on feature map. Each with shape\n                (num_points, 2).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            inside_gt_bbox_mask (Tensor): Tensor of bool type,\n                with shape of (num_points, num_gt), each\n                value is used to mark whether this point falls\n                within a certain gt.\n\n        Returns:\n            tuple[Tensor, Tensor]:\n\n            - center_prior_weights(Tensor): Float tensor with shape  of \\\n            (num_points, num_gt). Each value represents the center \\\n            weighting coefficient.\n            - inside_gt_bbox_mask (Tensor): Tensor of bool type, with shape \\\n            of (num_points, num_gt), each value is used to mark whether this \\\n            point falls within a certain gt or is the topk nearest points for \\\n            a specific gt_bbox.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        labels = gt_instances.labels\n\n        inside_gt_bbox_mask = inside_gt_bbox_mask.clone()\n        num_gts = len(labels)\n        num_points = sum([len(item) for item in anchor_points_list])\n        if num_gts == 0:\n            return gt_bboxes.new_zeros(num_points,\n                                       num_gts), inside_gt_bbox_mask\n        center_prior_list = []\n        for slvl_points, stride in zip(anchor_points_list, self.strides):\n            # slvl_points: points from single level in FPN, has shape (h*w, 2)\n            # single_level_points has shape (h*w, num_gt, 2)\n            single_level_points = slvl_points[:, None, :].expand(\n                (slvl_points.size(0), len(gt_bboxes), 2))\n            gt_center_x = ((gt_bboxes[:, 0] + gt_bboxes[:, 2]) / 2)\n            gt_center_y = ((gt_bboxes[:, 1] + gt_bboxes[:, 3]) / 2)\n            gt_center = torch.stack((gt_center_x, gt_center_y), dim=1)\n            gt_center = gt_center[None]\n            # instance_center has shape (1, num_gt, 2)\n            instance_center = self.mean[labels][None]\n            # instance_sigma has shape (1, num_gt, 2)\n            instance_sigma = self.sigma[labels][None]\n            # distance has shape (num_points, num_gt, 2)\n            distance = (((single_level_points - gt_center) / float(stride) -\n                         instance_center)**2)\n            center_prior = torch.exp(-distance /\n                                     (2 * instance_sigma**2)).prod(dim=-1)\n            center_prior_list.append(center_prior)\n        center_prior_weights = torch.cat(center_prior_list, dim=0)\n\n        if self.force_topk:\n            gt_inds_no_points_inside = torch.nonzero(\n                inside_gt_bbox_mask.sum(0) == 0).reshape(-1)\n            if gt_inds_no_points_inside.numel():\n                topk_center_index = \\\n                    center_prior_weights[:, gt_inds_no_points_inside].topk(\n                                                             self.topk,\n                                                             dim=0)[1]\n                temp_mask = inside_gt_bbox_mask[:, gt_inds_no_points_inside]\n                inside_gt_bbox_mask[:, gt_inds_no_points_inside] = \\\n                    torch.scatter(temp_mask,\n                                  dim=0,\n                                  index=topk_center_index,\n                                  src=torch.ones_like(\n                                    topk_center_index,\n                                    dtype=torch.bool))\n\n        center_prior_weights[~inside_gt_bbox_mask] = 0\n        return center_prior_weights, inside_gt_bbox_mask\n\n\n@MODELS.register_module()\nclass AutoAssignHead(FCOSHead):\n    \"\"\"AutoAssignHead head used in AutoAssign.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/2007.03496>`_ .\n\n    Args:\n        force_topk (bool): Used in center prior initialization to\n            handle extremely small gt. Default is False.\n        topk (int): The number of points used to calculate the\n            center prior when no point falls in gt_bbox. Only work when\n            force_topk if True. Defaults to 9.\n        pos_loss_weight (float): The loss weight of positive loss\n            and with default value 0.25.\n        neg_loss_weight (float): The loss weight of negative loss\n            and with default value 0.75.\n        center_loss_weight (float): The loss weight of center prior\n            loss and with default value 0.75.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 force_topk: bool = False,\n                 topk: int = 9,\n                 pos_loss_weight: float = 0.25,\n                 neg_loss_weight: float = 0.75,\n                 center_loss_weight: float = 0.75,\n                 **kwargs) -> None:\n        super().__init__(*args, conv_bias=True, **kwargs)\n        self.center_prior = CenterPrior(\n            force_topk=force_topk,\n            topk=topk,\n            num_classes=self.num_classes,\n            strides=self.strides)\n        self.pos_loss_weight = pos_loss_weight\n        self.neg_loss_weight = neg_loss_weight\n        self.center_loss_weight = center_loss_weight\n        self.prior_generator = MlvlPointGenerator(self.strides, offset=0)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\n\n        In particular, we have special initialization for classified conv's and\n        regression conv's bias\n        \"\"\"\n\n        super(AutoAssignHead, self).init_weights()\n        bias_cls = bias_init_with_prob(0.02)\n        normal_init(self.conv_cls, std=0.01, bias=bias_cls)\n        normal_init(self.conv_reg, std=0.01, bias=4.0)\n\n    def forward_single(self, x: Tensor, scale: Scale,\n                       stride: int) -> Tuple[Tensor, Tensor, Tensor]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n            scale (:obj:`mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n            stride (int): The corresponding stride for feature maps, only\n                used to normalize the bbox prediction when self.norm_on_bbox\n                is True.\n\n        Returns:\n            tuple[Tensor, Tensor, Tensor]: scores for each class, bbox\n            predictions and centerness predictions of input feature maps.\n        \"\"\"\n        cls_score, bbox_pred, cls_feat, reg_feat = super(\n            FCOSHead, self).forward_single(x)\n        centerness = self.conv_centerness(reg_feat)\n        # scale the bbox_pred of different level\n        # float to avoid overflow when enabling FP16\n        bbox_pred = scale(bbox_pred).float()\n        # bbox_pred needed for gradient computation has been modified\n        # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace\n        # F.relu(bbox_pred) with bbox_pred.clamp(min=0)\n        bbox_pred = bbox_pred.clamp(min=0)\n        bbox_pred *= stride\n        return cls_score, bbox_pred, centerness\n\n    def get_pos_loss_single(self, cls_score: Tensor, objectness: Tensor,\n                            reg_loss: Tensor, gt_instances: InstanceData,\n                            center_prior_weights: Tensor) -> Tuple[Tensor]:\n        \"\"\"Calculate the positive loss of all points in gt_bboxes.\n\n        Args:\n            cls_score (Tensor): All category scores for each point on\n                the feature map. The shape is (num_points, num_class).\n            objectness (Tensor): Foreground probability of all points,\n                has shape (num_points, 1).\n            reg_loss (Tensor): The regression loss of each gt_bbox and each\n                prediction box, has shape of (num_points, num_gt).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            center_prior_weights (Tensor): Float tensor with shape\n                of (num_points, num_gt). Each value represents\n                the center weighting coefficient.\n\n        Returns:\n            tuple[Tensor]:\n\n            - pos_loss (Tensor): The positive loss of all points in the \\\n            gt_bboxes.\n        \"\"\"\n        gt_labels = gt_instances.labels\n        # p_loc: localization confidence\n        p_loc = torch.exp(-reg_loss)\n        # p_cls: classification confidence\n        p_cls = (cls_score * objectness)[:, gt_labels]\n        # p_pos: joint confidence indicator\n        p_pos = p_cls * p_loc\n\n        # 3 is a hyper-parameter to control the contributions of high and\n        # low confidence locations towards positive losses.\n        confidence_weight = torch.exp(p_pos * 3)\n        p_pos_weight = (confidence_weight * center_prior_weights) / (\n            (confidence_weight * center_prior_weights).sum(\n                0, keepdim=True)).clamp(min=EPS)\n        reweighted_p_pos = (p_pos * p_pos_weight).sum(0)\n        pos_loss = F.binary_cross_entropy(\n            reweighted_p_pos,\n            torch.ones_like(reweighted_p_pos),\n            reduction='none')\n        pos_loss = pos_loss.sum() * self.pos_loss_weight\n        return pos_loss,\n\n    def get_neg_loss_single(self, cls_score: Tensor, objectness: Tensor,\n                            gt_instances: InstanceData, ious: Tensor,\n                            inside_gt_bbox_mask: Tensor) -> Tuple[Tensor]:\n        \"\"\"Calculate the negative loss of all points in feature map.\n\n        Args:\n            cls_score (Tensor): All category scores for each point on\n                the feature map. The shape is (num_points, num_class).\n            objectness (Tensor): Foreground probability of all points\n                and is shape of (num_points, 1).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            ious (Tensor): Float tensor with shape of (num_points, num_gt).\n                Each value represent the iou of pred_bbox and gt_bboxes.\n            inside_gt_bbox_mask (Tensor): Tensor of bool type,\n                with shape of (num_points, num_gt), each\n                value is used to mark whether this point falls\n                within a certain gt.\n\n        Returns:\n            tuple[Tensor]:\n\n            - neg_loss (Tensor): The negative loss of all points in the \\\n            feature map.\n        \"\"\"\n        gt_labels = gt_instances.labels\n        num_gts = len(gt_labels)\n        joint_conf = (cls_score * objectness)\n        p_neg_weight = torch.ones_like(joint_conf)\n        if num_gts > 0:\n            # the order of dinmension would affect the value of\n            # p_neg_weight, we strictly follow the original\n            # implementation.\n            inside_gt_bbox_mask = inside_gt_bbox_mask.permute(1, 0)\n            ious = ious.permute(1, 0)\n\n            foreground_idxs = torch.nonzero(inside_gt_bbox_mask, as_tuple=True)\n            temp_weight = (1 / (1 - ious[foreground_idxs]).clamp_(EPS))\n\n            def normalize(x):\n                return (x - x.min() + EPS) / (x.max() - x.min() + EPS)\n\n            for instance_idx in range(num_gts):\n                idxs = foreground_idxs[0] == instance_idx\n                if idxs.any():\n                    temp_weight[idxs] = normalize(temp_weight[idxs])\n\n            p_neg_weight[foreground_idxs[1],\n                         gt_labels[foreground_idxs[0]]] = 1 - temp_weight\n\n        logits = (joint_conf * p_neg_weight)\n        neg_loss = (\n            logits**2 * F.binary_cross_entropy(\n                logits, torch.zeros_like(logits), reduction='none'))\n        neg_loss = neg_loss.sum() * self.neg_loss_weight\n        return neg_loss,\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        objectnesses: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is\n                num_points * num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is\n                num_points * 4.\n            objectnesses (list[Tensor]): objectness for each scale level, each\n                is a 4D-tensor, the channel number is num_points * 1.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n\n        assert len(cls_scores) == len(bbox_preds) == len(objectnesses)\n        all_num_gt = sum([len(item) for item in batch_gt_instances])\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        all_level_points = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device)\n        inside_gt_bbox_mask_list, bbox_targets_list = self.get_targets(\n            all_level_points, batch_gt_instances)\n\n        center_prior_weight_list = []\n        temp_inside_gt_bbox_mask_list = []\n        for gt_instances, inside_gt_bbox_mask in zip(batch_gt_instances,\n                                                     inside_gt_bbox_mask_list):\n            center_prior_weight, inside_gt_bbox_mask = \\\n                self.center_prior(all_level_points, gt_instances,\n                                  inside_gt_bbox_mask)\n            center_prior_weight_list.append(center_prior_weight)\n            temp_inside_gt_bbox_mask_list.append(inside_gt_bbox_mask)\n        inside_gt_bbox_mask_list = temp_inside_gt_bbox_mask_list\n        mlvl_points = torch.cat(all_level_points, dim=0)\n        bbox_preds = levels_to_images(bbox_preds)\n        cls_scores = levels_to_images(cls_scores)\n        objectnesses = levels_to_images(objectnesses)\n\n        reg_loss_list = []\n        ious_list = []\n        num_points = len(mlvl_points)\n\n        for bbox_pred, encoded_targets, inside_gt_bbox_mask in zip(\n                bbox_preds, bbox_targets_list, inside_gt_bbox_mask_list):\n            temp_num_gt = encoded_targets.size(1)\n            expand_mlvl_points = mlvl_points[:, None, :].expand(\n                num_points, temp_num_gt, 2).reshape(-1, 2)\n            encoded_targets = encoded_targets.reshape(-1, 4)\n            expand_bbox_pred = bbox_pred[:, None, :].expand(\n                num_points, temp_num_gt, 4).reshape(-1, 4)\n            decoded_bbox_preds = self.bbox_coder.decode(\n                expand_mlvl_points, expand_bbox_pred)\n            decoded_target_preds = self.bbox_coder.decode(\n                expand_mlvl_points, encoded_targets)\n            with torch.no_grad():\n                ious = bbox_overlaps(\n                    decoded_bbox_preds, decoded_target_preds, is_aligned=True)\n                ious = ious.reshape(num_points, temp_num_gt)\n                if temp_num_gt:\n                    ious = ious.max(\n                        dim=-1, keepdim=True).values.repeat(1, temp_num_gt)\n                else:\n                    ious = ious.new_zeros(num_points, temp_num_gt)\n                ious[~inside_gt_bbox_mask] = 0\n                ious_list.append(ious)\n            loss_bbox = self.loss_bbox(\n                decoded_bbox_preds,\n                decoded_target_preds,\n                weight=None,\n                reduction_override='none')\n            reg_loss_list.append(loss_bbox.reshape(num_points, temp_num_gt))\n\n        cls_scores = [item.sigmoid() for item in cls_scores]\n        objectnesses = [item.sigmoid() for item in objectnesses]\n        pos_loss_list, = multi_apply(self.get_pos_loss_single, cls_scores,\n                                     objectnesses, reg_loss_list,\n                                     batch_gt_instances,\n                                     center_prior_weight_list)\n        pos_avg_factor = reduce_mean(\n            bbox_pred.new_tensor(all_num_gt)).clamp_(min=1)\n        pos_loss = sum(pos_loss_list) / pos_avg_factor\n\n        neg_loss_list, = multi_apply(self.get_neg_loss_single, cls_scores,\n                                     objectnesses, batch_gt_instances,\n                                     ious_list, inside_gt_bbox_mask_list)\n        neg_avg_factor = sum(item.data.sum()\n                             for item in center_prior_weight_list)\n        neg_avg_factor = reduce_mean(neg_avg_factor).clamp_(min=1)\n        neg_loss = sum(neg_loss_list) / neg_avg_factor\n\n        center_loss = []\n        for i in range(len(batch_img_metas)):\n\n            if inside_gt_bbox_mask_list[i].any():\n                center_loss.append(\n                    len(batch_gt_instances[i]) /\n                    center_prior_weight_list[i].sum().clamp_(min=EPS))\n            # when width or height of gt_bbox is smaller than stride of p3\n            else:\n                center_loss.append(center_prior_weight_list[i].sum() * 0)\n\n        center_loss = torch.stack(center_loss).mean() * self.center_loss_weight\n\n        # avoid dead lock in DDP\n        if all_num_gt == 0:\n            pos_loss = bbox_preds[0].sum() * 0\n            dummy_center_prior_loss = self.center_prior.mean.sum(\n            ) * 0 + self.center_prior.sigma.sum() * 0\n            center_loss = objectnesses[0].sum() * 0 + dummy_center_prior_loss\n\n        loss = dict(\n            loss_pos=pos_loss, loss_neg=neg_loss, loss_center=center_loss)\n\n        return loss\n\n    def get_targets(\n            self, points: List[Tensor], batch_gt_instances: InstanceList\n    ) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Compute regression targets and each point inside or outside gt_bbox\n        in multiple images.\n\n        Args:\n            points (list[Tensor]): Points of all fpn level, each has shape\n                (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple(list[Tensor], list[Tensor]):\n\n            - inside_gt_bbox_mask_list (list[Tensor]): Each Tensor is with \\\n            bool type and shape of (num_points, num_gt), each value is used \\\n            to mark whether this point falls within a certain gt.\n            - concat_lvl_bbox_targets (list[Tensor]): BBox targets of each \\\n            level. Each tensor has shape (num_points, num_gt, 4).\n        \"\"\"\n\n        concat_points = torch.cat(points, dim=0)\n        # the number of points per img, per lvl\n        inside_gt_bbox_mask_list, bbox_targets_list = multi_apply(\n            self._get_targets_single, batch_gt_instances, points=concat_points)\n        return inside_gt_bbox_mask_list, bbox_targets_list\n\n    def _get_targets_single(self, gt_instances: InstanceData,\n                            points: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Compute regression targets and each point inside or outside gt_bbox\n        for a single image.\n\n        Args:\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            points (Tensor): Points of all fpn level, has shape\n                (num_points, 2).\n\n        Returns:\n            tuple[Tensor, Tensor]: Containing the following Tensors:\n\n            - inside_gt_bbox_mask (Tensor): Bool tensor with shape \\\n            (num_points, num_gt), each value is used to mark whether this \\\n            point falls within a certain gt.\n            - bbox_targets (Tensor): BBox targets of each points with each \\\n            gt_bboxes, has shape (num_points, num_gt, 4).\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        num_points = points.size(0)\n        num_gts = gt_bboxes.size(0)\n        gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 4)\n        xs, ys = points[:, 0], points[:, 1]\n        xs = xs[:, None]\n        ys = ys[:, None]\n        left = xs - gt_bboxes[..., 0]\n        right = gt_bboxes[..., 2] - xs\n        top = ys - gt_bboxes[..., 1]\n        bottom = gt_bboxes[..., 3] - ys\n        bbox_targets = torch.stack((left, top, right, bottom), -1)\n        if num_gts:\n            inside_gt_bbox_mask = bbox_targets.min(-1)[0] > 0\n        else:\n            inside_gt_bbox_mask = bbox_targets.new_zeros((num_points, num_gts),\n                                                         dtype=torch.bool)\n\n        return inside_gt_bbox_mask, bbox_targets\n"
  },
  {
    "path": "mmdet/models/dense_heads/base_dense_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom abc import ABCMeta, abstractmethod\nfrom inspect import signature\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom mmcv.ops import batched_nms\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule, constant_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import (cat_boxes, get_box_tensor, get_box_wh,\n                                   scale_boxes)\nfrom mmdet.utils import InstanceList, OptMultiConfig\nfrom ..test_time_augs import merge_aug_results\nfrom ..utils import (filter_scores_and_topk, select_single_mlvl,\n                     unpack_gt_instances)\n\n\nclass BaseDenseHead(BaseModule, metaclass=ABCMeta):\n    \"\"\"Base class for DenseHeads.\n\n    1. The ``init_weights`` method is used to initialize densehead's\n    model parameters. After detector initialization, ``init_weights``\n    is triggered when ``detector.init_weights()`` is called externally.\n\n    2. The ``loss`` method is used to calculate the loss of densehead,\n    which includes two steps: (1) the densehead model performs forward\n    propagation to obtain the feature maps (2) The ``loss_by_feat`` method\n    is called based on the feature maps to calculate the loss.\n\n    .. code:: text\n\n    loss(): forward() -> loss_by_feat()\n\n    3. The ``predict`` method is used to predict detection results,\n    which includes two steps: (1) the densehead model performs forward\n    propagation to obtain the feature maps (2) The ``predict_by_feat`` method\n    is called based on the feature maps to predict detection results including\n    post-processing.\n\n    .. code:: text\n\n    predict(): forward() -> predict_by_feat()\n\n    4. The ``loss_and_predict`` method is used to return loss and detection\n    results at the same time. It will call densehead's ``forward``,\n    ``loss_by_feat`` and ``predict_by_feat`` methods in order.  If one-stage is\n    used as RPN, the densehead needs to return both losses and predictions.\n    This predictions is used as the proposal of roihead.\n\n    .. code:: text\n\n    loss_and_predict(): forward() -> loss_by_feat() -> predict_by_feat()\n    \"\"\"\n\n    def __init__(self, init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        # `_raw_positive_infos` will be used in `get_positive_infos`, which\n        # can get positive information.\n        self._raw_positive_infos = dict()\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize the weights.\"\"\"\n        super().init_weights()\n        # avoid init_cfg overwrite the initialization of `conv_offset`\n        for m in self.modules():\n            # DeformConv2dPack, ModulatedDeformConv2dPack\n            if hasattr(m, 'conv_offset'):\n                constant_init(m.conv_offset, 0)\n\n    def get_positive_infos(self) -> InstanceList:\n        \"\"\"Get positive information from sampling results.\n\n        Returns:\n            list[:obj:`InstanceData`]: Positive information of each image,\n            usually including positive bboxes, positive labels, positive\n            priors, etc.\n        \"\"\"\n        if len(self._raw_positive_infos) == 0:\n            return None\n\n        sampling_results = self._raw_positive_infos.get(\n            'sampling_results', None)\n        assert sampling_results is not None\n        positive_infos = []\n        for sampling_result in enumerate(sampling_results):\n            pos_info = InstanceData()\n            pos_info.bboxes = sampling_result.pos_gt_bboxes\n            pos_info.labels = sampling_result.pos_gt_labels\n            pos_info.priors = sampling_result.pos_priors\n            pos_info.pos_assigned_gt_inds = \\\n                sampling_result.pos_assigned_gt_inds\n            pos_info.pos_inds = sampling_result.pos_inds\n            positive_infos.append(pos_info)\n        return positive_infos\n\n    def loss(self, x: Tuple[Tensor], batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        outs = self(x)\n\n        outputs = unpack_gt_instances(batch_data_samples)\n        (batch_gt_instances, batch_gt_instances_ignore,\n         batch_img_metas) = outputs\n\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas,\n                              batch_gt_instances_ignore)\n        losses = self.loss_by_feat(*loss_inputs)\n        return losses\n\n    @abstractmethod\n    def loss_by_feat(self, **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\"\"\"\n        pass\n\n    def loss_and_predict(\n        self,\n        x: Tuple[Tensor],\n        batch_data_samples: SampleList,\n        proposal_cfg: Optional[ConfigDict] = None\n    ) -> Tuple[dict, InstanceList]:\n        \"\"\"Perform forward propagation of the head, then calculate loss and\n        predictions from the features and data samples.\n\n        Args:\n            x (tuple[Tensor]): Features from FPN.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n            proposal_cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n\n        Returns:\n            tuple: the return value is a tuple contains:\n\n                - losses: (dict[str, Tensor]): A dictionary of loss components.\n                - predictions (list[:obj:`InstanceData`]): Detection\n                  results of each image after the post process.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        (batch_gt_instances, batch_gt_instances_ignore,\n         batch_img_metas) = outputs\n\n        outs = self(x)\n\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas,\n                              batch_gt_instances_ignore)\n        losses = self.loss_by_feat(*loss_inputs)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, cfg=proposal_cfg)\n        return losses, predictions\n\n    def predict(self,\n                x: Tuple[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        outs = self(x)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, rescale=rescale)\n        return predictions\n\n    def predict_by_feat(self,\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        score_factors: Optional[List[Tensor]] = None,\n                        batch_img_metas: Optional[List[dict]] = None,\n                        cfg: Optional[ConfigDict] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Note: When score_factors is not None, the cls_scores are\n        usually multiplied by it then obtain the real score used in NMS,\n        such as CenterNess in FCOS, IoU branch in ATSS.\n\n        Args:\n            cls_scores (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            score_factors (list[Tensor], optional): Score factor for\n                all scale level, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 1, H, W). Defaults to None.\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n                Defaults to None.\n            cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n\n        if score_factors is None:\n            # e.g. Retina, FreeAnchor, Foveabox, etc.\n            with_score_factors = False\n        else:\n            # e.g. FCOS, PAA, ATSS, AutoAssign, etc.\n            with_score_factors = True\n            assert len(cls_scores) == len(score_factors)\n\n        num_levels = len(cls_scores)\n\n        featmap_sizes = [cls_scores[i].shape[-2:] for i in range(num_levels)]\n        mlvl_priors = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=cls_scores[0].dtype,\n            device=cls_scores[0].device)\n\n        result_list = []\n\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            cls_score_list = select_single_mlvl(\n                cls_scores, img_id, detach=True)\n            bbox_pred_list = select_single_mlvl(\n                bbox_preds, img_id, detach=True)\n            if with_score_factors:\n                score_factor_list = select_single_mlvl(\n                    score_factors, img_id, detach=True)\n            else:\n                score_factor_list = [None for _ in range(num_levels)]\n\n            results = self._predict_by_feat_single(\n                cls_score_list=cls_score_list,\n                bbox_pred_list=bbox_pred_list,\n                score_factor_list=score_factor_list,\n                mlvl_priors=mlvl_priors,\n                img_meta=img_meta,\n                cfg=cfg,\n                rescale=rescale,\n                with_nms=with_nms)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid. In all\n                anchor-based methods, it has shape (num_priors, 4). In\n                all anchor-free methods, it has shape (num_priors, 2)\n                when `with_stride=True`, otherwise it still has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        if score_factor_list[0] is None:\n            # e.g. Retina, FreeAnchor, etc.\n            with_score_factors = False\n        else:\n            # e.g. FCOS, PAA, ATSS, etc.\n            with_score_factors = True\n\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bbox_preds = []\n        mlvl_valid_priors = []\n        mlvl_scores = []\n        mlvl_labels = []\n        if with_score_factors:\n            mlvl_score_factors = []\n        else:\n            mlvl_score_factors = None\n        for level_idx, (cls_score, bbox_pred, score_factor, priors) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list,\n                              score_factor_list, mlvl_priors)):\n\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            dim = self.bbox_coder.encode_size\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, dim)\n            if with_score_factors:\n                score_factor = score_factor.permute(1, 2,\n                                                    0).reshape(-1).sigmoid()\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n\n            # the `custom_cls_channels` parameter is derived from\n            # CrossEntropyCustomLoss and FocalCustomLoss, and is currently used\n            # in v3det.\n            if getattr(self.loss_cls, 'custom_cls_channels', False):\n                scores = self.loss_cls.get_activation(cls_score)\n            elif self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                # remind that we set FG labels to [0, num_class-1]\n                # since mmdet v2.0\n                # BG cat_id: num_class\n                scores = cls_score.softmax(-1)[:, :-1]\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            score_thr = cfg.get('score_thr', 0)\n\n            results = filter_scores_and_topk(\n                scores, score_thr, nms_pre,\n                dict(bbox_pred=bbox_pred, priors=priors))\n            scores, labels, keep_idxs, filtered_results = results\n\n            bbox_pred = filtered_results['bbox_pred']\n            priors = filtered_results['priors']\n\n            if with_score_factors:\n                score_factor = score_factor[keep_idxs]\n\n            mlvl_bbox_preds.append(bbox_pred)\n            mlvl_valid_priors.append(priors)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n\n            if with_score_factors:\n                mlvl_score_factors.append(score_factor)\n\n        bbox_pred = torch.cat(mlvl_bbox_preds)\n        priors = cat_boxes(mlvl_valid_priors)\n        bboxes = self.bbox_coder.decode(priors, bbox_pred, max_shape=img_shape)\n\n        results = InstanceData()\n        results.bboxes = bboxes\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n        if with_score_factors:\n            results.score_factors = torch.cat(mlvl_score_factors)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n    def _bbox_post_process(self,\n                           results: InstanceData,\n                           cfg: ConfigDict,\n                           rescale: bool = False,\n                           with_nms: bool = True,\n                           img_meta: Optional[dict] = None) -> InstanceData:\n        \"\"\"bbox post-processing method.\n\n        The boxes would be rescaled to the original image scale and do\n        the nms operation. Usually `with_nms` is False is used for aug test.\n\n        Args:\n            results (:obj:`InstaceData`): Detection instance results,\n                each item has shape (num_bboxes, ).\n            cfg (ConfigDict): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Default to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Default to True.\n            img_meta (dict, optional): Image meta info. Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = [1 / s for s in img_meta['scale_factor']]\n            results.bboxes = scale_boxes(results.bboxes, scale_factor)\n\n        if hasattr(results, 'score_factors'):\n            # TODO: Add sqrt operation in order to be consistent with\n            #  the paper.\n            score_factors = results.pop('score_factors')\n            results.scores = results.scores * score_factors\n\n        # filter small size bboxes\n        if cfg.get('min_bbox_size', -1) >= 0:\n            w, h = get_box_wh(results.bboxes)\n            valid_mask = (w > cfg.min_bbox_size) & (h > cfg.min_bbox_size)\n            if not valid_mask.all():\n                results = results[valid_mask]\n\n        # TODO: deal with `with_nms` and `nms_cfg=None` in test_cfg\n        if with_nms and results.bboxes.numel() > 0:\n            bboxes = get_box_tensor(results.bboxes)\n            det_bboxes, keep_idxs = batched_nms(bboxes, results.scores,\n                                                results.labels, cfg.nms)\n            results = results[keep_idxs]\n            # some nms would reweight the score, such as softnms\n            results.scores = det_bboxes[:, -1]\n            results = results[:cfg.max_per_img]\n\n        return results\n\n    def aug_test(self,\n                 aug_batch_feats,\n                 aug_batch_img_metas,\n                 rescale=False,\n                 with_ori_nms=False,\n                 **kwargs):\n        \"\"\"Test function with test time augmentation.\n\n        Args:\n            aug_batch_feats (list[tuple[Tensor]]): The outer list\n                indicates test-time augmentations and inner tuple\n                indicate the multi-level feats from\n                FPN, each Tensor should have a shape (B, C, H, W),\n            aug_batch_img_metas (list[list[dict]]): Meta information\n                of images under the different test-time augs\n                (multiscale, flip, etc.). The outer list indicate\n                the\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n            with_ori_nms (bool): Whether execute the nms in original head.\n                Defaults to False. It will be `True` when the head is\n                adopted as `rpn_head`.\n\n        Returns:\n            list(obj:`InstanceData`): Detection results of the\n            input images. Each item usually contains\\\n            following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance,)\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances,).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        # TODO: remove this for detr and deformdetr\n        sig_of_get_results = signature(self.get_results)\n        get_results_args = [\n            p.name for p in sig_of_get_results.parameters.values()\n        ]\n        get_results_single_sig = signature(self._get_results_single)\n        get_results_single_sig_args = [\n            p.name for p in get_results_single_sig.parameters.values()\n        ]\n        assert ('with_nms' in get_results_args) and \\\n               ('with_nms' in get_results_single_sig_args), \\\n               f'{self.__class__.__name__}' \\\n               'does not support test-time augmentation '\n\n        num_imgs = len(aug_batch_img_metas[0])\n        aug_batch_results = []\n        for x, img_metas in zip(aug_batch_feats, aug_batch_img_metas):\n            outs = self.forward(x)\n            batch_instance_results = self.get_results(\n                *outs,\n                img_metas=img_metas,\n                cfg=self.test_cfg,\n                rescale=False,\n                with_nms=with_ori_nms,\n                **kwargs)\n            aug_batch_results.append(batch_instance_results)\n\n        # after merging, bboxes will be rescaled to the original image\n        batch_results = merge_aug_results(aug_batch_results,\n                                          aug_batch_img_metas)\n\n        final_results = []\n        for img_id in range(num_imgs):\n            results = batch_results[img_id]\n            det_bboxes, keep_idxs = batched_nms(results.bboxes, results.scores,\n                                                results.labels,\n                                                self.test_cfg.nms)\n            results = results[keep_idxs]\n            # some nms operation may reweight the score such as softnms\n            results.scores = det_bboxes[:, -1]\n            results = results[:self.test_cfg.max_per_img]\n            if rescale:\n                # all results have been mapped to the original scale\n                # in `merge_aug_results`, so just pass\n                pass\n            else:\n                # map to the first aug image scale\n                scale_factor = results.bboxes.new_tensor(\n                    aug_batch_img_metas[0][img_id]['scale_factor'])\n                results.bboxes = \\\n                    results.bboxes * scale_factor\n\n            final_results.append(results)\n\n        return final_results\n"
  },
  {
    "path": "mmdet/models/dense_heads/base_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import List, Tuple, Union\n\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import InstanceList, OptInstanceList, OptMultiConfig\nfrom ..utils import unpack_gt_instances\n\n\nclass BaseMaskHead(BaseModule, metaclass=ABCMeta):\n    \"\"\"Base class for mask heads used in One-Stage Instance Segmentation.\"\"\"\n\n    def __init__(self, init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n\n    @abstractmethod\n    def loss_by_feat(self, *args, **kwargs):\n        \"\"\"Calculate the loss based on the features extracted by the mask\n        head.\"\"\"\n        pass\n\n    @abstractmethod\n    def predict_by_feat(self, *args, **kwargs):\n        \"\"\"Transform a batch of output features extracted from the head into\n        mask results.\"\"\"\n        pass\n\n    def loss(self,\n             x: Union[List[Tensor], Tuple[Tensor]],\n             batch_data_samples: SampleList,\n             positive_infos: OptInstanceList = None,\n             **kwargs) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the mask head on\n        the features of the upstream network.\n\n        Args:\n            x (list[Tensor] | tuple[Tensor]): Features from FPN.\n                Each has a shape (B, C, H, W).\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n            positive_infos (list[:obj:`InstanceData`], optional): Information\n                of positive samples. Used when the label assignment is\n                done outside the MaskHead, e.g., BboxHead in\n                YOLACT or CondInst, etc. When the label assignment is done in\n                MaskHead, it would be None, like SOLO or SOLOv2. All values\n                in it should have shape (num_positive_samples, *).\n\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        if positive_infos is None:\n            outs = self(x)\n        else:\n            outs = self(x, positive_infos)\n\n        assert isinstance(outs, tuple), 'Forward results should be a tuple, ' \\\n                                        'even if only one item is returned'\n\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n        for gt_instances, img_metas in zip(batch_gt_instances,\n                                           batch_img_metas):\n            img_shape = img_metas['batch_input_shape']\n            gt_masks = gt_instances.masks.pad(img_shape)\n            gt_instances.masks = gt_masks\n\n        losses = self.loss_by_feat(\n            *outs,\n            batch_gt_instances=batch_gt_instances,\n            batch_img_metas=batch_img_metas,\n            positive_infos=positive_infos,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            **kwargs)\n        return losses\n\n    def predict(self,\n                x: Tuple[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = False,\n                results_list: OptInstanceList = None,\n                **kwargs) -> InstanceList:\n        \"\"\"Test function without test-time augmentation.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n            results_list (list[obj:`InstanceData`], optional): Detection\n                results of each image after the post process. Only exist\n                if there is a `bbox_head`, like `YOLACT`, `CondInst`, etc.\n\n        Returns:\n            list[obj:`InstanceData`]: Instance segmentation\n            results of each image after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance,)\n                - labels (Tensor): Has a shape (num_instances,).\n                - masks (Tensor): Processed mask results, has a\n                  shape (num_instances, h, w).\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n        if results_list is None:\n            outs = self(x)\n        else:\n            outs = self(x, results_list)\n\n        results_list = self.predict_by_feat(\n            *outs,\n            batch_img_metas=batch_img_metas,\n            rescale=rescale,\n            results_list=results_list,\n            **kwargs)\n\n        return results_list\n"
  },
  {
    "path": "mmdet/models/dense_heads/boxinst_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine import MessageHub\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList\nfrom ..utils.misc import unfold_wo_center\nfrom .condinst_head import CondInstBboxHead, CondInstMaskHead\n\n\n@MODELS.register_module()\nclass BoxInstBboxHead(CondInstBboxHead):\n    \"\"\"BoxInst box head used in https://arxiv.org/abs/2012.02310.\"\"\"\n\n    def __init__(self, *args, **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n\n\n@MODELS.register_module()\nclass BoxInstMaskHead(CondInstMaskHead):\n    \"\"\"BoxInst mask head used in https://arxiv.org/abs/2012.02310.\n\n    This head outputs the mask for BoxInst.\n\n    Args:\n        pairwise_size (dict): The size of neighborhood for each pixel.\n            Defaults to 3.\n        pairwise_dilation (int): The dilation of neighborhood for each pixel.\n            Defaults to 2.\n        warmup_iters (int): Warmup iterations for pair-wise loss.\n            Defaults to 10000.\n    \"\"\"\n\n    def __init__(self,\n                 *arg,\n                 pairwise_size: int = 3,\n                 pairwise_dilation: int = 2,\n                 warmup_iters: int = 10000,\n                 **kwargs) -> None:\n        self.pairwise_size = pairwise_size\n        self.pairwise_dilation = pairwise_dilation\n        self.warmup_iters = warmup_iters\n        super().__init__(*arg, **kwargs)\n\n    def get_pairwise_affinity(self, mask_logits: Tensor) -> Tensor:\n        \"\"\"Compute the pairwise affinity for each pixel.\"\"\"\n        log_fg_prob = F.logsigmoid(mask_logits).unsqueeze(1)\n        log_bg_prob = F.logsigmoid(-mask_logits).unsqueeze(1)\n\n        log_fg_prob_unfold = unfold_wo_center(\n            log_fg_prob,\n            kernel_size=self.pairwise_size,\n            dilation=self.pairwise_dilation)\n        log_bg_prob_unfold = unfold_wo_center(\n            log_bg_prob,\n            kernel_size=self.pairwise_size,\n            dilation=self.pairwise_dilation)\n\n        # the probability of making the same prediction:\n        # p_i * p_j + (1 - p_i) * (1 - p_j)\n        # we compute the the probability in log space\n        # to avoid numerical instability\n        log_same_fg_prob = log_fg_prob[:, :, None] + log_fg_prob_unfold\n        log_same_bg_prob = log_bg_prob[:, :, None] + log_bg_prob_unfold\n\n        # TODO: Figure out the difference between it and directly sum\n        max_ = torch.max(log_same_fg_prob, log_same_bg_prob)\n        log_same_prob = torch.log(\n            torch.exp(log_same_fg_prob - max_) +\n            torch.exp(log_same_bg_prob - max_)) + max_\n\n        return -log_same_prob[:, 0]\n\n    def loss_by_feat(self, mask_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict], positive_infos: InstanceList,\n                     **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mask_preds (list[Tensor]): List of predicted masks, each has\n                shape (num_classes, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``masks``,\n                and ``labels`` attributes.\n            batch_img_metas (list[dict]): Meta information of multiple images.\n            positive_infos (List[:obj:``InstanceData``]): Information of\n                positive samples of each image that are assigned in detection\n                head.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert positive_infos is not None, \\\n            'positive_infos should not be None in `BoxInstMaskHead`'\n        losses = dict()\n\n        loss_mask_project = 0.\n        loss_mask_pairwise = 0.\n        num_imgs = len(mask_preds)\n        total_pos = 0.\n        avg_fatcor = 0.\n\n        for idx in range(num_imgs):\n            (mask_pred, pos_mask_targets, pos_pairwise_masks, num_pos) = \\\n                self._get_targets_single(\n                mask_preds[idx], batch_gt_instances[idx],\n                positive_infos[idx])\n            # mask loss\n            total_pos += num_pos\n            if num_pos == 0 or pos_mask_targets is None:\n                loss_project = mask_pred.new_zeros(1).mean()\n                loss_pairwise = mask_pred.new_zeros(1).mean()\n                avg_fatcor += 0.\n            else:\n                # compute the project term\n                loss_project_x = self.loss_mask(\n                    mask_pred.max(dim=1, keepdim=True)[0],\n                    pos_mask_targets.max(dim=1, keepdim=True)[0],\n                    reduction_override='none').sum()\n                loss_project_y = self.loss_mask(\n                    mask_pred.max(dim=2, keepdim=True)[0],\n                    pos_mask_targets.max(dim=2, keepdim=True)[0],\n                    reduction_override='none').sum()\n                loss_project = loss_project_x + loss_project_y\n                # compute the pairwise term\n                pairwise_affinity = self.get_pairwise_affinity(mask_pred)\n                avg_fatcor += pos_pairwise_masks.sum().clamp(min=1.0)\n                loss_pairwise = (pairwise_affinity * pos_pairwise_masks).sum()\n\n            loss_mask_project += loss_project\n            loss_mask_pairwise += loss_pairwise\n\n        if total_pos == 0:\n            total_pos += 1  # avoid nan\n        if avg_fatcor == 0:\n            avg_fatcor += 1  # avoid nan\n        loss_mask_project = loss_mask_project / total_pos\n        loss_mask_pairwise = loss_mask_pairwise / avg_fatcor\n        message_hub = MessageHub.get_current_instance()\n        iter = message_hub.get_info('iter')\n        warmup_factor = min(iter / float(self.warmup_iters), 1.0)\n        loss_mask_pairwise *= warmup_factor\n\n        losses.update(\n            loss_mask_project=loss_mask_project,\n            loss_mask_pairwise=loss_mask_pairwise)\n        return losses\n\n    def _get_targets_single(self, mask_preds: Tensor,\n                            gt_instances: InstanceData,\n                            positive_info: InstanceData):\n        \"\"\"Compute targets for predictions of single image.\n\n        Args:\n            mask_preds (Tensor): Predicted prototypes with shape\n                (num_classes, H, W).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes``, ``labels``,\n                and ``masks`` attributes.\n            positive_info (:obj:`InstanceData`): Information of positive\n                samples that are assigned in detection head. It usually\n                contains following keys.\n\n                    - pos_assigned_gt_inds (Tensor): Assigner GT indexes of\n                      positive proposals, has shape (num_pos, )\n                    - pos_inds (Tensor): Positive index of image, has\n                      shape (num_pos, ).\n                    - param_pred (Tensor): Positive param preditions\n                      with shape (num_pos, num_params).\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n\n            - mask_preds (Tensor): Positive predicted mask with shape\n              (num_pos, mask_h, mask_w).\n            - pos_mask_targets (Tensor): Positive mask targets with shape\n              (num_pos, mask_h, mask_w).\n            - pos_pairwise_masks (Tensor): Positive pairwise masks with\n              shape: (num_pos, num_neighborhood, mask_h, mask_w).\n            - num_pos (int): Positive numbers.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        device = gt_bboxes.device\n        # Note that gt_masks are generated by full box\n        # from BoxInstDataPreprocessor\n        gt_masks = gt_instances.masks.to_tensor(\n            dtype=torch.bool, device=device).float()\n        # Note that pairwise_masks are generated by image color similarity\n        # from BoxInstDataPreprocessor\n        pairwise_masks = gt_instances.pairwise_masks\n        pairwise_masks = pairwise_masks.to(device=device)\n\n        # process with mask targets\n        pos_assigned_gt_inds = positive_info.get('pos_assigned_gt_inds')\n        scores = positive_info.get('scores')\n        centernesses = positive_info.get('centernesses')\n        num_pos = pos_assigned_gt_inds.size(0)\n\n        if gt_masks.size(0) == 0 or num_pos == 0:\n            return mask_preds, None, None, 0\n        # Since we're producing (near) full image masks,\n        # it'd take too much vram to backprop on every single mask.\n        # Thus we select only a subset.\n        if (self.max_masks_to_train != -1) and \\\n           (num_pos > self.max_masks_to_train):\n            perm = torch.randperm(num_pos)\n            select = perm[:self.max_masks_to_train]\n            mask_preds = mask_preds[select]\n            pos_assigned_gt_inds = pos_assigned_gt_inds[select]\n            num_pos = self.max_masks_to_train\n        elif self.topk_masks_per_img != -1:\n            unique_gt_inds = pos_assigned_gt_inds.unique()\n            num_inst_per_gt = max(\n                int(self.topk_masks_per_img / len(unique_gt_inds)), 1)\n\n            keep_mask_preds = []\n            keep_pos_assigned_gt_inds = []\n            for gt_ind in unique_gt_inds:\n                per_inst_pos_inds = (pos_assigned_gt_inds == gt_ind)\n                mask_preds_per_inst = mask_preds[per_inst_pos_inds]\n                gt_inds_per_inst = pos_assigned_gt_inds[per_inst_pos_inds]\n                if sum(per_inst_pos_inds) > num_inst_per_gt:\n                    per_inst_scores = scores[per_inst_pos_inds].sigmoid().max(\n                        dim=1)[0]\n                    per_inst_centerness = centernesses[\n                        per_inst_pos_inds].sigmoid().reshape(-1, )\n                    select = (per_inst_scores * per_inst_centerness).topk(\n                        k=num_inst_per_gt, dim=0)[1]\n                    mask_preds_per_inst = mask_preds_per_inst[select]\n                    gt_inds_per_inst = gt_inds_per_inst[select]\n                keep_mask_preds.append(mask_preds_per_inst)\n                keep_pos_assigned_gt_inds.append(gt_inds_per_inst)\n            mask_preds = torch.cat(keep_mask_preds)\n            pos_assigned_gt_inds = torch.cat(keep_pos_assigned_gt_inds)\n            num_pos = pos_assigned_gt_inds.size(0)\n\n        # Follow the origin implement\n        start = int(self.mask_out_stride // 2)\n        gt_masks = gt_masks[:, start::self.mask_out_stride,\n                            start::self.mask_out_stride]\n        gt_masks = gt_masks.gt(0.5).float()\n        pos_mask_targets = gt_masks[pos_assigned_gt_inds]\n        pos_pairwise_masks = pairwise_masks[pos_assigned_gt_inds]\n        pos_pairwise_masks = pos_pairwise_masks * pos_mask_targets.unsqueeze(1)\n\n        return (mask_preds, pos_mask_targets, pos_pairwise_masks, num_pos)\n"
  },
  {
    "path": "mmdet/models/dense_heads/cascade_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom __future__ import division\nimport copy\nfrom typing import Dict, List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.ops import DeformConv2d\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule, ModuleList\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig,\n                         OptInstanceList, OptMultiConfig)\nfrom ..task_modules.assigners import RegionAssigner\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import (images_to_levels, multi_apply, select_single_mlvl,\n                     unpack_gt_instances)\nfrom .base_dense_head import BaseDenseHead\nfrom .rpn_head import RPNHead\n\n\nclass AdaptiveConv(BaseModule):\n    \"\"\"AdaptiveConv used to adapt the sampling location with the anchors.\n\n    Args:\n        in_channels (int): Number of channels in the input image.\n        out_channels (int): Number of channels produced by the convolution.\n        kernel_size (int or tuple[int]): Size of the conv kernel.\n            Defaults to 3.\n        stride (int or tuple[int]): Stride of the convolution. Defaults to 1.\n        padding (int or tuple[int]): Zero-padding added to both sides of\n            the input. Defaults to 1.\n        dilation (int or tuple[int]): Spacing between kernel elements.\n            Defaults to 3.\n        groups (int): Number of blocked connections from input channels to\n            output channels. Defaults to 1.\n        bias (bool): If set True, adds a learnable bias to the output.\n            Defaults to False.\n        adapt_type (str): Type of adaptive conv, can be either ``offset``\n            (arbitrary anchors) or 'dilation' (uniform anchor).\n            Defaults to 'dilation'.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or \\\n            list[dict]): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: int,\n        out_channels: int,\n        kernel_size: Union[int, Tuple[int]] = 3,\n        stride: Union[int, Tuple[int]] = 1,\n        padding: Union[int, Tuple[int]] = 1,\n        dilation: Union[int, Tuple[int]] = 3,\n        groups: int = 1,\n        bias: bool = False,\n        adapt_type: str = 'dilation',\n        init_cfg: MultiConfig = dict(\n            type='Normal', std=0.01, override=dict(name='conv'))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert adapt_type in ['offset', 'dilation']\n        self.adapt_type = adapt_type\n\n        assert kernel_size == 3, 'Adaptive conv only supports kernels 3'\n        if self.adapt_type == 'offset':\n            assert stride == 1 and padding == 1 and groups == 1, \\\n                'Adaptive conv offset mode only supports padding: {1}, ' \\\n                f'stride: {1}, groups: {1}'\n            self.conv = DeformConv2d(\n                in_channels,\n                out_channels,\n                kernel_size,\n                padding=padding,\n                stride=stride,\n                groups=groups,\n                bias=bias)\n        else:\n            self.conv = nn.Conv2d(\n                in_channels,\n                out_channels,\n                kernel_size,\n                padding=dilation,\n                dilation=dilation)\n\n    def forward(self, x: Tensor, offset: Tensor) -> Tensor:\n        \"\"\"Forward function.\"\"\"\n        if self.adapt_type == 'offset':\n            N, _, H, W = x.shape\n            assert offset is not None\n            assert H * W == offset.shape[1]\n            # reshape [N, NA, 18] to (N, 18, H, W)\n            offset = offset.permute(0, 2, 1).reshape(N, -1, H, W)\n            offset = offset.contiguous()\n            x = self.conv(x, offset)\n        else:\n            assert offset is None\n            x = self.conv(x)\n        return x\n\n\n@MODELS.register_module()\nclass StageCascadeRPNHead(RPNHead):\n    \"\"\"Stage of CascadeRPNHead.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        anchor_generator (:obj:`ConfigDict` or dict): anchor generator config.\n        adapt_cfg (:obj:`ConfigDict` or dict): adaptation config.\n        bridged_feature (bool): whether update rpn feature. Defaults to False.\n        with_cls (bool): whether use classification branch. Defaults to True.\n        init_cfg :obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 anchor_generator: ConfigType = dict(\n                     type='AnchorGenerator',\n                     scales=[8],\n                     ratios=[1.0],\n                     strides=[4, 8, 16, 32, 64]),\n                 adapt_cfg: ConfigType = dict(type='dilation', dilation=3),\n                 bridged_feature: bool = False,\n                 with_cls: bool = True,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        self.with_cls = with_cls\n        self.anchor_strides = anchor_generator['strides']\n        self.anchor_scales = anchor_generator['scales']\n        self.bridged_feature = bridged_feature\n        self.adapt_cfg = adapt_cfg\n        super().__init__(\n            in_channels=in_channels,\n            anchor_generator=anchor_generator,\n            init_cfg=init_cfg,\n            **kwargs)\n\n        # override sampling and sampler\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            # use PseudoSampler when sampling is False\n            if self.train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler(context=self)\n\n        if init_cfg is None:\n            self.init_cfg = dict(\n                type='Normal', std=0.01, override=[dict(name='rpn_reg')])\n            if self.with_cls:\n                self.init_cfg['override'].append(dict(name='rpn_cls'))\n\n    def _init_layers(self) -> None:\n        \"\"\"Init layers of a CascadeRPN stage.\"\"\"\n        adapt_cfg = copy.deepcopy(self.adapt_cfg)\n        adapt_cfg['adapt_type'] = adapt_cfg.pop('type')\n        self.rpn_conv = AdaptiveConv(self.in_channels, self.feat_channels,\n                                     **adapt_cfg)\n        if self.with_cls:\n            self.rpn_cls = nn.Conv2d(self.feat_channels,\n                                     self.num_anchors * self.cls_out_channels,\n                                     1)\n        self.rpn_reg = nn.Conv2d(self.feat_channels, self.num_anchors * 4, 1)\n        self.relu = nn.ReLU(inplace=True)\n\n    def forward_single(self, x: Tensor, offset: Tensor) -> Tuple[Tensor]:\n        \"\"\"Forward function of single scale.\"\"\"\n        bridged_x = x\n        x = self.relu(self.rpn_conv(x, offset))\n        if self.bridged_feature:\n            bridged_x = x  # update feature\n        cls_score = self.rpn_cls(x) if self.with_cls else None\n        bbox_pred = self.rpn_reg(x)\n        return bridged_x, cls_score, bbox_pred\n\n    def forward(\n            self,\n            feats: List[Tensor],\n            offset_list: Optional[List[Tensor]] = None) -> Tuple[List[Tensor]]:\n        \"\"\"Forward function.\"\"\"\n        if offset_list is None:\n            offset_list = [None for _ in range(len(feats))]\n        return multi_apply(self.forward_single, feats, offset_list)\n\n    def _region_targets_single(self, flat_anchors: Tensor, valid_flags: Tensor,\n                               gt_instances: InstanceData, img_meta: dict,\n                               gt_instances_ignore: InstanceData,\n                               featmap_sizes: List[Tuple[int, int]],\n                               num_level_anchors: List[int]) -> tuple:\n        \"\"\"Get anchor targets based on region for single level.\n\n        Args:\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors, 4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors, ).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            featmap_sizes (list[Tuple[int, int]]): Feature map size each level.\n            num_level_anchors (list[int]): The number of anchors in each level.\n\n        Returns:\n            tuple:\n\n                - labels (Tensor): Labels of each level.\n                - label_weights (Tensor): Label weights of each level.\n                - bbox_targets (Tensor): BBox targets of each level.\n                - bbox_weights (Tensor): BBox weights of each level.\n                - pos_inds (Tensor): positive samples indexes.\n                - neg_inds (Tensor): negative samples indexes.\n                - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        pred_instances = InstanceData()\n        pred_instances.priors = flat_anchors\n        pred_instances.valid_flags = valid_flags\n\n        assign_result = self.assigner.assign(\n            pred_instances,\n            gt_instances,\n            img_meta,\n            featmap_sizes,\n            num_level_anchors,\n            self.anchor_scales[0],\n            self.anchor_strides,\n            gt_instances_ignore=gt_instances_ignore,\n            allowed_border=self.train_cfg['allowed_border'])\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_anchors = flat_anchors.shape[0]\n        bbox_targets = torch.zeros_like(flat_anchors)\n        bbox_weights = torch.zeros_like(flat_anchors)\n        labels = flat_anchors.new_zeros(num_anchors, dtype=torch.long)\n        label_weights = flat_anchors.new_zeros(num_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            if not self.reg_decoded_bbox:\n                pos_bbox_targets = self.bbox_coder.encode(\n                    sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes)\n            else:\n                pos_bbox_targets = sampling_result.pos_gt_bboxes\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1.0\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                neg_inds, sampling_result)\n\n    def region_targets(\n        self,\n        anchor_list: List[List[Tensor]],\n        valid_flag_list: List[List[Tensor]],\n        featmap_sizes: List[Tuple[int, int]],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None,\n        return_sampling_results: bool = False,\n    ) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors when using\n        RegionAssigner.\n\n        Args:\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image.\n            valid_flag_list (list[list[Tensor]]): Multi level valid flags of\n                each image.\n            featmap_sizes (list[Tuple[int, int]]): Feature map size each level.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            tuple:\n\n                - labels_list (list[Tensor]): Labels of each level.\n                - label_weights_list (list[Tensor]): Label weights of each\n                  level.\n                - bbox_targets_list (list[Tensor]): BBox targets of each level.\n                - bbox_weights_list (list[Tensor]): BBox weights of each level.\n                - avg_factor (int): Average factor that is used to average\n                  the loss. When using sampling method, avg_factor is usually\n                  the sum of positive and negative priors. When using\n                  ``PseudoSampler``, ``avg_factor`` is usually equal to the\n                  number of positive priors.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        # concat all level anchors to a single tensor\n        concat_anchor_list = []\n        concat_valid_flag_list = []\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            concat_anchor_list.append(torch.cat(anchor_list[i]))\n            concat_valid_flag_list.append(torch.cat(valid_flag_list[i]))\n\n        # compute targets for each image\n        (all_labels, all_label_weights, all_bbox_targets, all_bbox_weights,\n         pos_inds_list, neg_inds_list, sampling_results_list) = multi_apply(\n             self._region_targets_single,\n             concat_anchor_list,\n             concat_valid_flag_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             featmap_sizes=featmap_sizes,\n             num_level_anchors=num_level_anchors)\n        # no valid anchors\n        if any([labels is None for labels in all_labels]):\n            return None\n        # sampled anchors of all images\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        labels_list = images_to_levels(all_labels, num_level_anchors)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors)\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors)\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_anchors)\n        res = (labels_list, label_weights_list, bbox_targets_list,\n               bbox_weights_list, avg_factor)\n        if return_sampling_results:\n            res = res + (sampling_results_list, )\n        return res\n\n    def get_targets(\n        self,\n        anchor_list: List[List[Tensor]],\n        valid_flag_list: List[List[Tensor]],\n        featmap_sizes: List[Tuple[int, int]],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None,\n        return_sampling_results: bool = False,\n    ) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors.\n\n        Args:\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image.\n            valid_flag_list (list[list[Tensor]]): Multi level valid flags of\n                each image.\n            featmap_sizes (list[Tuple[int, int]]): Feature map size each level.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            return_sampling_results (bool): Whether to return the sampling\n                results. Defaults to False.\n\n        Returns:\n            tuple:\n\n                - labels_list (list[Tensor]): Labels of each level.\n                - label_weights_list (list[Tensor]): Label weights of each\n                  level.\n                - bbox_targets_list (list[Tensor]): BBox targets of each level.\n                - bbox_weights_list (list[Tensor]): BBox weights of each level.\n                - avg_factor (int): Average factor that is used to average\n                  the loss. When using sampling method, avg_factor is usually\n                  the sum of positive and negative priors. When using\n                  ``PseudoSampler``, ``avg_factor`` is usually equal to the\n                  number of positive priors.\n        \"\"\"\n        if isinstance(self.assigner, RegionAssigner):\n            cls_reg_targets = self.region_targets(\n                anchor_list,\n                valid_flag_list,\n                featmap_sizes,\n                batch_gt_instances,\n                batch_img_metas,\n                batch_gt_instances_ignore=batch_gt_instances_ignore,\n                return_sampling_results=return_sampling_results)\n        else:\n            cls_reg_targets = super().get_targets(\n                anchor_list,\n                valid_flag_list,\n                batch_gt_instances,\n                batch_img_metas,\n                batch_gt_instances_ignore=batch_gt_instances_ignore,\n                return_sampling_results=return_sampling_results)\n        return cls_reg_targets\n\n    def anchor_offset(self, anchor_list: List[List[Tensor]],\n                      anchor_strides: List[int],\n                      featmap_sizes: List[Tuple[int, int]]) -> List[Tensor]:\n        \"\"\" Get offset for deformable conv based on anchor shape\n        NOTE: currently support deformable kernel_size=3 and dilation=1\n\n        Args:\n            anchor_list (list[list[tensor])): [NI, NLVL, NA, 4] list of\n                multi-level anchors\n            anchor_strides (list[int]): anchor stride of each level\n\n        Returns:\n            list[tensor]: offset of DeformConv kernel with shapes of\n            [NLVL, NA, 2, 18].\n        \"\"\"\n\n        def _shape_offset(anchors, stride, ks=3, dilation=1):\n            # currently support kernel_size=3 and dilation=1\n            assert ks == 3 and dilation == 1\n            pad = (ks - 1) // 2\n            idx = torch.arange(-pad, pad + 1, dtype=dtype, device=device)\n            yy, xx = torch.meshgrid(idx, idx)  # return order matters\n            xx = xx.reshape(-1)\n            yy = yy.reshape(-1)\n            w = (anchors[:, 2] - anchors[:, 0]) / stride\n            h = (anchors[:, 3] - anchors[:, 1]) / stride\n            w = w / (ks - 1) - dilation\n            h = h / (ks - 1) - dilation\n            offset_x = w[:, None] * xx  # (NA, ks**2)\n            offset_y = h[:, None] * yy  # (NA, ks**2)\n            return offset_x, offset_y\n\n        def _ctr_offset(anchors, stride, featmap_size):\n            feat_h, feat_w = featmap_size\n            assert len(anchors) == feat_h * feat_w\n\n            x = (anchors[:, 0] + anchors[:, 2]) * 0.5\n            y = (anchors[:, 1] + anchors[:, 3]) * 0.5\n            # compute centers on feature map\n            x = x / stride\n            y = y / stride\n            # compute predefine centers\n            xx = torch.arange(0, feat_w, device=anchors.device)\n            yy = torch.arange(0, feat_h, device=anchors.device)\n            yy, xx = torch.meshgrid(yy, xx)\n            xx = xx.reshape(-1).type_as(x)\n            yy = yy.reshape(-1).type_as(y)\n\n            offset_x = x - xx  # (NA, )\n            offset_y = y - yy  # (NA, )\n            return offset_x, offset_y\n\n        num_imgs = len(anchor_list)\n        num_lvls = len(anchor_list[0])\n        dtype = anchor_list[0][0].dtype\n        device = anchor_list[0][0].device\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n\n        offset_list = []\n        for i in range(num_imgs):\n            mlvl_offset = []\n            for lvl in range(num_lvls):\n                c_offset_x, c_offset_y = _ctr_offset(anchor_list[i][lvl],\n                                                     anchor_strides[lvl],\n                                                     featmap_sizes[lvl])\n                s_offset_x, s_offset_y = _shape_offset(anchor_list[i][lvl],\n                                                       anchor_strides[lvl])\n\n                # offset = ctr_offset + shape_offset\n                offset_x = s_offset_x + c_offset_x[:, None]\n                offset_y = s_offset_y + c_offset_y[:, None]\n\n                # offset order (y0, x0, y1, x2, .., y8, x8, y9, x9)\n                offset = torch.stack([offset_y, offset_x], dim=-1)\n                offset = offset.reshape(offset.size(0), -1)  # [NA, 2*ks**2]\n                mlvl_offset.append(offset)\n            offset_list.append(torch.cat(mlvl_offset))  # [totalNA, 2*ks**2]\n        offset_list = images_to_levels(offset_list, num_level_anchors)\n        return offset_list\n\n    def loss_by_feat_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            anchors: Tensor, labels: Tensor,\n                            label_weights: Tensor, bbox_targets: Tensor,\n                            bbox_weights: Tensor, avg_factor: int) -> tuple:\n        \"\"\"Loss function on single scale.\"\"\"\n        # classification loss\n        if self.with_cls:\n            labels = labels.reshape(-1)\n            label_weights = label_weights.reshape(-1)\n            cls_score = cls_score.permute(0, 2, 3,\n                                          1).reshape(-1, self.cls_out_channels)\n            loss_cls = self.loss_cls(\n                cls_score, labels, label_weights, avg_factor=avg_factor)\n        # regression loss\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        bbox_weights = bbox_weights.reshape(-1, 4)\n        bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n        if self.reg_decoded_bbox:\n            # When the regression loss (e.g. `IouLoss`, `GIouLoss`)\n            # is applied directly on the decoded bounding boxes, it\n            # decodes the already encoded coordinates to absolute format.\n            anchors = anchors.reshape(-1, 4)\n            bbox_pred = self.bbox_coder.decode(anchors, bbox_pred)\n        loss_reg = self.loss_bbox(\n            bbox_pred, bbox_targets, bbox_weights, avg_factor=avg_factor)\n        if self.with_cls:\n            return loss_cls, loss_reg\n        return None, loss_reg\n\n    def loss_by_feat(\n        self,\n        anchor_list: List[List[Tensor]],\n        valid_flag_list: List[List[Tensor]],\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Compute losses of the head.\n\n        Args:\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image.\n            valid_flag_list (list[list[Tensor]]): Multi level valid flags of\n                each image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, )\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in bbox_preds]\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            featmap_sizes,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            return_sampling_results=True)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor, sampling_results_list) = cls_reg_targets\n        if not sampling_results_list[0].avg_factor_with_neg:\n            # 200 is hard-coded average factor,\n            # which follows guided anchoring.\n            avg_factor = sum([label.numel() for label in labels_list]) / 200.0\n\n        # change per image, per level anchor_list to per_level, per_image\n        mlvl_anchor_list = list(zip(*anchor_list))\n        # concat mlvl_anchor_list\n        mlvl_anchor_list = [\n            torch.cat(anchors, dim=0) for anchors in mlvl_anchor_list\n        ]\n\n        losses = multi_apply(\n            self.loss_by_feat_single,\n            cls_scores,\n            bbox_preds,\n            mlvl_anchor_list,\n            labels_list,\n            label_weights_list,\n            bbox_targets_list,\n            bbox_weights_list,\n            avg_factor=avg_factor)\n        if self.with_cls:\n            return dict(loss_rpn_cls=losses[0], loss_rpn_reg=losses[1])\n        return dict(loss_rpn_reg=losses[1])\n\n    def predict_by_feat(self,\n                        anchor_list: List[List[Tensor]],\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        batch_img_metas: List[dict],\n                        cfg: Optional[ConfigDict] = None,\n                        rescale: bool = False) -> InstanceList:\n        \"\"\"Get proposal predict. Overriding to enable input ``anchor_list``\n        from outside.\n\n        Args:\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image.\n            cls_scores (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            batch_img_metas (list[dict], Optional): Image meta info.\n            cfg (:obj:`ConfigDict`, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score_list = select_single_mlvl(cls_scores, img_id)\n            bbox_pred_list = select_single_mlvl(bbox_preds, img_id)\n            proposals = self._predict_by_feat_single(\n                cls_scores=cls_score_list,\n                bbox_preds=bbox_pred_list,\n                mlvl_anchors=anchor_list[img_id],\n                img_meta=batch_img_metas[img_id],\n                cfg=cfg,\n                rescale=rescale)\n            result_list.append(proposals)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_scores: List[Tensor],\n                                bbox_preds: List[Tensor],\n                                mlvl_anchors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False) -> InstanceData:\n        \"\"\"Transform outputs of a single image into bbox predictions.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has\n                shape (num_anchors * 4, H, W).\n            mlvl_anchors (list[Tensor]): Box reference from all scale\n                levels of a single image, each item has shape\n                (num_total_anchors, 4).\n            img_shape (tuple[int]): Shape of the input image,\n                (height, width, 3).\n            scale_factor (ndarray): Scale factor of the image arange as\n                (w_scale, h_scale, w_scale, h_scale).\n            cfg (:obj:`ConfigDict`): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        # bboxes from different level should be independent during NMS,\n        # level_ids are used as labels for batched NMS to separate them\n        level_ids = []\n        mlvl_scores = []\n        mlvl_bbox_preds = []\n        mlvl_valid_anchors = []\n        nms_pre = cfg.get('nms_pre', -1)\n        for idx in range(len(cls_scores)):\n            rpn_cls_score = cls_scores[idx]\n            rpn_bbox_pred = bbox_preds[idx]\n            assert rpn_cls_score.size()[-2:] == rpn_bbox_pred.size()[-2:]\n            rpn_cls_score = rpn_cls_score.permute(1, 2, 0)\n            if self.use_sigmoid_cls:\n                rpn_cls_score = rpn_cls_score.reshape(-1)\n                scores = rpn_cls_score.sigmoid()\n            else:\n                rpn_cls_score = rpn_cls_score.reshape(-1, 2)\n                # We set FG labels to [0, num_class-1] and BG label to\n                # num_class in RPN head since mmdet v2.5, which is unified to\n                # be consistent with other head since mmdet v2.0. In mmdet v2.0\n                # to v2.4 we keep BG label as 0 and FG label as 1 in rpn head.\n                scores = rpn_cls_score.softmax(dim=1)[:, 0]\n            rpn_bbox_pred = rpn_bbox_pred.permute(1, 2, 0).reshape(-1, 4)\n            anchors = mlvl_anchors[idx]\n\n            if 0 < nms_pre < scores.shape[0]:\n                # sort is faster than topk\n                # _, topk_inds = scores.topk(cfg.nms_pre)\n                ranked_scores, rank_inds = scores.sort(descending=True)\n                topk_inds = rank_inds[:nms_pre]\n                scores = ranked_scores[:nms_pre]\n                rpn_bbox_pred = rpn_bbox_pred[topk_inds, :]\n                anchors = anchors[topk_inds, :]\n            mlvl_scores.append(scores)\n            mlvl_bbox_preds.append(rpn_bbox_pred)\n            mlvl_valid_anchors.append(anchors)\n            level_ids.append(\n                scores.new_full((scores.size(0), ), idx, dtype=torch.long))\n\n        anchors = torch.cat(mlvl_valid_anchors)\n        rpn_bbox_pred = torch.cat(mlvl_bbox_preds)\n        bboxes = self.bbox_coder.decode(\n            anchors, rpn_bbox_pred, max_shape=img_meta['img_shape'])\n\n        proposals = InstanceData()\n        proposals.bboxes = bboxes\n        proposals.scores = torch.cat(mlvl_scores)\n        proposals.level_ids = torch.cat(level_ids)\n\n        return self._bbox_post_process(\n            results=proposals, cfg=cfg, rescale=rescale, img_meta=img_meta)\n\n    def refine_bboxes(self, anchor_list: List[List[Tensor]],\n                      bbox_preds: List[Tensor],\n                      img_metas: List[dict]) -> List[List[Tensor]]:\n        \"\"\"Refine bboxes through stages.\"\"\"\n        num_levels = len(bbox_preds)\n        new_anchor_list = []\n        for img_id in range(len(img_metas)):\n            mlvl_anchors = []\n            for i in range(num_levels):\n                bbox_pred = bbox_preds[i][img_id].detach()\n                bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4)\n                img_shape = img_metas[img_id]['img_shape']\n                bboxes = self.bbox_coder.decode(anchor_list[img_id][i],\n                                                bbox_pred, img_shape)\n                mlvl_anchors.append(bboxes)\n            new_anchor_list.append(mlvl_anchors)\n        return new_anchor_list\n\n    def loss(self, x: Tuple[Tensor], batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, _, batch_img_metas = outputs\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in x]\n        device = x[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        if self.adapt_cfg['type'] == 'offset':\n            offset_list = self.anchor_offset(anchor_list, self.anchor_strides,\n                                             featmap_sizes)\n        else:\n            offset_list = None\n\n        x, cls_score, bbox_pred = self(x, offset_list)\n        rpn_loss_inputs = (anchor_list, valid_flag_list, cls_score, bbox_pred,\n                           batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(*rpn_loss_inputs)\n\n        return losses\n\n    def loss_and_predict(\n        self,\n        x: Tuple[Tensor],\n        batch_data_samples: SampleList,\n        proposal_cfg: Optional[ConfigDict] = None,\n    ) -> Tuple[dict, InstanceList]:\n        \"\"\"Perform forward propagation of the head, then calculate loss and\n        predictions from the features and data samples.\n\n        Args:\n            x (tuple[Tensor]): Features from FPN.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n            proposal_cfg (:obj`ConfigDict`, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n\n        Returns:\n            tuple: the return value is a tuple contains:\n\n                - losses: (dict[str, Tensor]): A dictionary of loss components.\n                - predictions (list[:obj:`InstanceData`]): Detection\n                  results of each image after the post process.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, _, batch_img_metas = outputs\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in x]\n        device = x[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        if self.adapt_cfg['type'] == 'offset':\n            offset_list = self.anchor_offset(anchor_list, self.anchor_strides,\n                                             featmap_sizes)\n        else:\n            offset_list = None\n\n        x, cls_score, bbox_pred = self(x, offset_list)\n        rpn_loss_inputs = (anchor_list, valid_flag_list, cls_score, bbox_pred,\n                           batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(*rpn_loss_inputs)\n\n        predictions = self.predict_by_feat(\n            anchor_list,\n            cls_score,\n            bbox_pred,\n            batch_img_metas=batch_img_metas,\n            cfg=proposal_cfg)\n        return losses, predictions\n\n    def predict(self,\n                x: Tuple[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in x]\n        device = x[0].device\n        anchor_list, _ = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        if self.adapt_cfg['type'] == 'offset':\n            offset_list = self.anchor_offset(anchor_list, self.anchor_strides,\n                                             featmap_sizes)\n        else:\n            offset_list = None\n\n        x, cls_score, bbox_pred = self(x, offset_list)\n        predictions = self.stages[-1].predict_by_feat(\n            anchor_list,\n            cls_score,\n            bbox_pred,\n            batch_img_metas=batch_img_metas,\n            rescale=rescale)\n        return predictions\n\n\n@MODELS.register_module()\nclass CascadeRPNHead(BaseDenseHead):\n    \"\"\"The CascadeRPNHead will predict more accurate region proposals, which is\n    required for two-stage detectors (such as Fast/Faster R-CNN). CascadeRPN\n    consists of a sequence of RPNStage to progressively improve the accuracy of\n    the detected proposals.\n\n    More details can be found in ``https://arxiv.org/abs/1909.06720``.\n\n    Args:\n        num_stages (int): number of CascadeRPN stages.\n        stages (list[:obj:`ConfigDict` or dict]): list of configs to build\n            the stages.\n        train_cfg (list[:obj:`ConfigDict` or dict]): list of configs at\n            training time each stage.\n        test_cfg (:obj:`ConfigDict` or dict): config at testing time.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or \\\n            list[dict]): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 num_stages: int,\n                 stages: List[ConfigType],\n                 train_cfg: List[ConfigType],\n                 test_cfg: ConfigType,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert num_classes == 1, 'Only support num_classes == 1'\n        assert num_stages == len(stages)\n        self.num_stages = num_stages\n        # Be careful! Pretrained weights cannot be loaded when use\n        # nn.ModuleList\n        self.stages = ModuleList()\n        for i in range(len(stages)):\n            train_cfg_i = train_cfg[i] if train_cfg is not None else None\n            stages[i].update(train_cfg=train_cfg_i)\n            stages[i].update(test_cfg=test_cfg)\n            self.stages.append(MODELS.build(stages[i]))\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n    def loss_by_feat(self):\n        \"\"\"loss_by_feat() is implemented in StageCascadeRPNHead.\"\"\"\n        pass\n\n    def predict_by_feat(self):\n        \"\"\"predict_by_feat() is implemented in StageCascadeRPNHead.\"\"\"\n        pass\n\n    def loss(self, x: Tuple[Tensor], batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, _, batch_img_metas = outputs\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in x]\n        device = x[0].device\n        anchor_list, valid_flag_list = self.stages[0].get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        losses = dict()\n\n        for i in range(self.num_stages):\n            stage = self.stages[i]\n\n            if stage.adapt_cfg['type'] == 'offset':\n                offset_list = stage.anchor_offset(anchor_list,\n                                                  stage.anchor_strides,\n                                                  featmap_sizes)\n            else:\n                offset_list = None\n            x, cls_score, bbox_pred = stage(x, offset_list)\n            rpn_loss_inputs = (anchor_list, valid_flag_list, cls_score,\n                               bbox_pred, batch_gt_instances, batch_img_metas)\n            stage_loss = stage.loss_by_feat(*rpn_loss_inputs)\n            for name, value in stage_loss.items():\n                losses['s{}.{}'.format(i, name)] = value\n\n            # refine boxes\n            if i < self.num_stages - 1:\n                anchor_list = stage.refine_bboxes(anchor_list, bbox_pred,\n                                                  batch_img_metas)\n\n        return losses\n\n    def loss_and_predict(\n        self,\n        x: Tuple[Tensor],\n        batch_data_samples: SampleList,\n        proposal_cfg: Optional[ConfigDict] = None,\n    ) -> Tuple[dict, InstanceList]:\n        \"\"\"Perform forward propagation of the head, then calculate loss and\n        predictions from the features and data samples.\n\n        Args:\n            x (tuple[Tensor]): Features from FPN.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n            proposal_cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n\n        Returns:\n            tuple: the return value is a tuple contains:\n\n                - losses: (dict[str, Tensor]): A dictionary of loss components.\n                - predictions (list[:obj:`InstanceData`]): Detection\n                  results of each image after the post process.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, _, batch_img_metas = outputs\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in x]\n        device = x[0].device\n        anchor_list, valid_flag_list = self.stages[0].get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        losses = dict()\n\n        for i in range(self.num_stages):\n            stage = self.stages[i]\n\n            if stage.adapt_cfg['type'] == 'offset':\n                offset_list = stage.anchor_offset(anchor_list,\n                                                  stage.anchor_strides,\n                                                  featmap_sizes)\n            else:\n                offset_list = None\n            x, cls_score, bbox_pred = stage(x, offset_list)\n            rpn_loss_inputs = (anchor_list, valid_flag_list, cls_score,\n                               bbox_pred, batch_gt_instances, batch_img_metas)\n            stage_loss = stage.loss_by_feat(*rpn_loss_inputs)\n            for name, value in stage_loss.items():\n                losses['s{}.{}'.format(i, name)] = value\n\n            # refine boxes\n            if i < self.num_stages - 1:\n                anchor_list = stage.refine_bboxes(anchor_list, bbox_pred,\n                                                  batch_img_metas)\n\n        predictions = self.stages[-1].predict_by_feat(\n            anchor_list,\n            cls_score,\n            bbox_pred,\n            batch_img_metas=batch_img_metas,\n            cfg=proposal_cfg)\n        return losses, predictions\n\n    def predict(self,\n                x: Tuple[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in x]\n        device = x[0].device\n        anchor_list, _ = self.stages[0].get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        for i in range(self.num_stages):\n            stage = self.stages[i]\n            if stage.adapt_cfg['type'] == 'offset':\n                offset_list = stage.anchor_offset(anchor_list,\n                                                  stage.anchor_strides,\n                                                  featmap_sizes)\n            else:\n                offset_list = None\n            x, cls_score, bbox_pred = stage(x, offset_list)\n            if i < self.num_stages - 1:\n                anchor_list = stage.refine_bboxes(anchor_list, bbox_pred,\n                                                  batch_img_metas)\n\n        predictions = self.stages[-1].predict_by_feat(\n            anchor_list,\n            cls_score,\n            bbox_pred,\n            batch_img_metas=batch_img_metas,\n            rescale=rescale)\n        return predictions\n"
  },
  {
    "path": "mmdet/models/dense_heads/centernet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.ops import batched_nms\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import bias_init_with_prob, normal_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, OptMultiConfig)\nfrom ..utils import (gaussian_radius, gen_gaussian_target, get_local_maximum,\n                     get_topk_from_heatmap, multi_apply,\n                     transpose_and_gather_feat)\nfrom .base_dense_head import BaseDenseHead\n\n\n@MODELS.register_module()\nclass CenterNetHead(BaseDenseHead):\n    \"\"\"Objects as Points Head. CenterHead use center_point to indicate object's\n    position. Paper link <https://arxiv.org/abs/1904.07850>\n\n    Args:\n        in_channels (int): Number of channel in the input feature map.\n        feat_channels (int): Number of channel in the intermediate feature map.\n        num_classes (int): Number of categories excluding the background\n            category.\n        loss_center_heatmap (:obj:`ConfigDict` or dict): Config of center\n            heatmap loss. Defaults to\n            dict(type='GaussianFocalLoss', loss_weight=1.0)\n        loss_wh (:obj:`ConfigDict` or dict): Config of wh loss. Defaults to\n             dict(type='L1Loss', loss_weight=0.1).\n        loss_offset (:obj:`ConfigDict` or dict): Config of offset loss.\n            Defaults to dict(type='L1Loss', loss_weight=1.0).\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config.\n            Useless in CenterNet, but we keep this variable for\n            SingleStageDetector.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config\n            of CenterNet.\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`], optional): Initialization\n            config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 feat_channels: int,\n                 num_classes: int,\n                 loss_center_heatmap: ConfigType = dict(\n                     type='GaussianFocalLoss', loss_weight=1.0),\n                 loss_wh: ConfigType = dict(type='L1Loss', loss_weight=0.1),\n                 loss_offset: ConfigType = dict(\n                     type='L1Loss', loss_weight=1.0),\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.num_classes = num_classes\n        self.heatmap_head = self._build_head(in_channels, feat_channels,\n                                             num_classes)\n        self.wh_head = self._build_head(in_channels, feat_channels, 2)\n        self.offset_head = self._build_head(in_channels, feat_channels, 2)\n\n        self.loss_center_heatmap = MODELS.build(loss_center_heatmap)\n        self.loss_wh = MODELS.build(loss_wh)\n        self.loss_offset = MODELS.build(loss_offset)\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        self.fp16_enabled = False\n\n    def _build_head(self, in_channels: int, feat_channels: int,\n                    out_channels: int) -> nn.Sequential:\n        \"\"\"Build head for each branch.\"\"\"\n        layer = nn.Sequential(\n            nn.Conv2d(in_channels, feat_channels, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(feat_channels, out_channels, kernel_size=1))\n        return layer\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        bias_init = bias_init_with_prob(0.1)\n        self.heatmap_head[-1].bias.data.fill_(bias_init)\n        for head in [self.wh_head, self.offset_head]:\n            for m in head.modules():\n                if isinstance(m, nn.Conv2d):\n                    normal_init(m, std=0.001)\n\n    def forward(self, x: Tuple[Tensor, ...]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features. Notice CenterNet head does not use FPN.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            center_heatmap_preds (list[Tensor]): center predict heatmaps for\n                all levels, the channels number is num_classes.\n            wh_preds (list[Tensor]): wh predicts for all levels, the channels\n                number is 2.\n            offset_preds (list[Tensor]): offset predicts for all levels, the\n               channels number is 2.\n        \"\"\"\n        return multi_apply(self.forward_single, x)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, ...]:\n        \"\"\"Forward feature of a single level.\n\n        Args:\n            x (Tensor): Feature of a single level.\n\n        Returns:\n            center_heatmap_pred (Tensor): center predict heatmaps, the\n               channels number is num_classes.\n            wh_pred (Tensor): wh predicts, the channels number is 2.\n            offset_pred (Tensor): offset predicts, the channels number is 2.\n        \"\"\"\n        center_heatmap_pred = self.heatmap_head(x).sigmoid()\n        wh_pred = self.wh_head(x)\n        offset_pred = self.offset_head(x)\n        return center_heatmap_pred, wh_pred, offset_pred\n\n    def loss_by_feat(\n            self,\n            center_heatmap_preds: List[Tensor],\n            wh_preds: List[Tensor],\n            offset_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Compute losses of the head.\n\n        Args:\n            center_heatmap_preds (list[Tensor]): center predict heatmaps for\n               all levels with shape (B, num_classes, H, W).\n            wh_preds (list[Tensor]): wh predicts for all levels with\n               shape (B, 2, H, W).\n            offset_preds (list[Tensor]): offset predicts for all levels\n               with shape (B, 2, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: which has components below:\n                - loss_center_heatmap (Tensor): loss of center heatmap.\n                - loss_wh (Tensor): loss of hw heatmap\n                - loss_offset (Tensor): loss of offset heatmap.\n        \"\"\"\n        assert len(center_heatmap_preds) == len(wh_preds) == len(\n            offset_preds) == 1\n        center_heatmap_pred = center_heatmap_preds[0]\n        wh_pred = wh_preds[0]\n        offset_pred = offset_preds[0]\n\n        gt_bboxes = [\n            gt_instances.bboxes for gt_instances in batch_gt_instances\n        ]\n        gt_labels = [\n            gt_instances.labels for gt_instances in batch_gt_instances\n        ]\n        img_shape = batch_img_metas[0]['batch_input_shape']\n        target_result, avg_factor = self.get_targets(gt_bboxes, gt_labels,\n                                                     center_heatmap_pred.shape,\n                                                     img_shape)\n\n        center_heatmap_target = target_result['center_heatmap_target']\n        wh_target = target_result['wh_target']\n        offset_target = target_result['offset_target']\n        wh_offset_target_weight = target_result['wh_offset_target_weight']\n\n        # Since the channel of wh_target and offset_target is 2, the avg_factor\n        # of loss_center_heatmap is always 1/2 of loss_wh and loss_offset.\n        loss_center_heatmap = self.loss_center_heatmap(\n            center_heatmap_pred, center_heatmap_target, avg_factor=avg_factor)\n        loss_wh = self.loss_wh(\n            wh_pred,\n            wh_target,\n            wh_offset_target_weight,\n            avg_factor=avg_factor * 2)\n        loss_offset = self.loss_offset(\n            offset_pred,\n            offset_target,\n            wh_offset_target_weight,\n            avg_factor=avg_factor * 2)\n        return dict(\n            loss_center_heatmap=loss_center_heatmap,\n            loss_wh=loss_wh,\n            loss_offset=loss_offset)\n\n    def get_targets(self, gt_bboxes: List[Tensor], gt_labels: List[Tensor],\n                    feat_shape: tuple, img_shape: tuple) -> Tuple[dict, int]:\n        \"\"\"Compute regression and classification targets in multiple images.\n\n        Args:\n            gt_bboxes (list[Tensor]): Ground truth bboxes for each image with\n                shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.\n            gt_labels (list[Tensor]): class indices corresponding to each box.\n            feat_shape (tuple): feature map shape with value [B, _, H, W]\n            img_shape (tuple): image shape.\n\n        Returns:\n            tuple[dict, float]: The float value is mean avg_factor, the dict\n            has components below:\n               - center_heatmap_target (Tensor): targets of center heatmap, \\\n                   shape (B, num_classes, H, W).\n               - wh_target (Tensor): targets of wh predict, shape \\\n                   (B, 2, H, W).\n               - offset_target (Tensor): targets of offset predict, shape \\\n                   (B, 2, H, W).\n               - wh_offset_target_weight (Tensor): weights of wh and offset \\\n                   predict, shape (B, 2, H, W).\n        \"\"\"\n        img_h, img_w = img_shape[:2]\n        bs, _, feat_h, feat_w = feat_shape\n\n        width_ratio = float(feat_w / img_w)\n        height_ratio = float(feat_h / img_h)\n\n        center_heatmap_target = gt_bboxes[-1].new_zeros(\n            [bs, self.num_classes, feat_h, feat_w])\n        wh_target = gt_bboxes[-1].new_zeros([bs, 2, feat_h, feat_w])\n        offset_target = gt_bboxes[-1].new_zeros([bs, 2, feat_h, feat_w])\n        wh_offset_target_weight = gt_bboxes[-1].new_zeros(\n            [bs, 2, feat_h, feat_w])\n\n        for batch_id in range(bs):\n            gt_bbox = gt_bboxes[batch_id]\n            gt_label = gt_labels[batch_id]\n            center_x = (gt_bbox[:, [0]] + gt_bbox[:, [2]]) * width_ratio / 2\n            center_y = (gt_bbox[:, [1]] + gt_bbox[:, [3]]) * height_ratio / 2\n            gt_centers = torch.cat((center_x, center_y), dim=1)\n\n            for j, ct in enumerate(gt_centers):\n                ctx_int, cty_int = ct.int()\n                ctx, cty = ct\n                scale_box_h = (gt_bbox[j][3] - gt_bbox[j][1]) * height_ratio\n                scale_box_w = (gt_bbox[j][2] - gt_bbox[j][0]) * width_ratio\n                radius = gaussian_radius([scale_box_h, scale_box_w],\n                                         min_overlap=0.3)\n                radius = max(0, int(radius))\n                ind = gt_label[j]\n                gen_gaussian_target(center_heatmap_target[batch_id, ind],\n                                    [ctx_int, cty_int], radius)\n\n                wh_target[batch_id, 0, cty_int, ctx_int] = scale_box_w\n                wh_target[batch_id, 1, cty_int, ctx_int] = scale_box_h\n\n                offset_target[batch_id, 0, cty_int, ctx_int] = ctx - ctx_int\n                offset_target[batch_id, 1, cty_int, ctx_int] = cty - cty_int\n\n                wh_offset_target_weight[batch_id, :, cty_int, ctx_int] = 1\n\n        avg_factor = max(1, center_heatmap_target.eq(1).sum())\n        target_result = dict(\n            center_heatmap_target=center_heatmap_target,\n            wh_target=wh_target,\n            offset_target=offset_target,\n            wh_offset_target_weight=wh_offset_target_weight)\n        return target_result, avg_factor\n\n    def predict_by_feat(self,\n                        center_heatmap_preds: List[Tensor],\n                        wh_preds: List[Tensor],\n                        offset_preds: List[Tensor],\n                        batch_img_metas: Optional[List[dict]] = None,\n                        rescale: bool = True,\n                        with_nms: bool = False) -> InstanceList:\n        \"\"\"Transform network output for a batch into bbox predictions.\n\n        Args:\n            center_heatmap_preds (list[Tensor]): Center predict heatmaps for\n                all levels with shape (B, num_classes, H, W).\n            wh_preds (list[Tensor]): WH predicts for all levels with\n                shape (B, 2, H, W).\n            offset_preds (list[Tensor]): Offset predicts for all levels\n                with shape (B, 2, H, W).\n            batch_img_metas (list[dict], optional): Batch image meta info.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to True.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Instance segmentation\n            results of each image after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(center_heatmap_preds) == len(wh_preds) == len(\n            offset_preds) == 1\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            result_list.append(\n                self._predict_by_feat_single(\n                    center_heatmap_preds[0][img_id:img_id + 1, ...],\n                    wh_preds[0][img_id:img_id + 1, ...],\n                    offset_preds[0][img_id:img_id + 1, ...],\n                    batch_img_metas[img_id],\n                    rescale=rescale,\n                    with_nms=with_nms))\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                center_heatmap_pred: Tensor,\n                                wh_pred: Tensor,\n                                offset_pred: Tensor,\n                                img_meta: dict,\n                                rescale: bool = True,\n                                with_nms: bool = False) -> InstanceData:\n        \"\"\"Transform outputs of a single image into bbox results.\n\n        Args:\n            center_heatmap_pred (Tensor): Center heatmap for current level with\n                shape (1, num_classes, H, W).\n            wh_pred (Tensor): WH heatmap for current level with shape\n                (1, num_classes, H, W).\n            offset_pred (Tensor): Offset for current level with shape\n                (1, corner_offset_channels, H, W).\n            img_meta (dict): Meta information of current image, e.g.,\n                image size, scaling factor, etc.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to True.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to False.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        batch_det_bboxes, batch_labels = self._decode_heatmap(\n            center_heatmap_pred,\n            wh_pred,\n            offset_pred,\n            img_meta['batch_input_shape'],\n            k=self.test_cfg.topk,\n            kernel=self.test_cfg.local_maximum_kernel)\n\n        det_bboxes = batch_det_bboxes.view([-1, 5])\n        det_labels = batch_labels.view(-1)\n\n        batch_border = det_bboxes.new_tensor(img_meta['border'])[...,\n                                                                 [2, 0, 2, 0]]\n        det_bboxes[..., :4] -= batch_border\n\n        if rescale and 'scale_factor' in img_meta:\n            det_bboxes[..., :4] /= det_bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n\n        if with_nms:\n            det_bboxes, det_labels = self._bboxes_nms(det_bboxes, det_labels,\n                                                      self.test_cfg)\n        results = InstanceData()\n        results.bboxes = det_bboxes[..., :4]\n        results.scores = det_bboxes[..., 4]\n        results.labels = det_labels\n        return results\n\n    def _decode_heatmap(self,\n                        center_heatmap_pred: Tensor,\n                        wh_pred: Tensor,\n                        offset_pred: Tensor,\n                        img_shape: tuple,\n                        k: int = 100,\n                        kernel: int = 3) -> Tuple[Tensor, Tensor]:\n        \"\"\"Transform outputs into detections raw bbox prediction.\n\n        Args:\n            center_heatmap_pred (Tensor): center predict heatmap,\n               shape (B, num_classes, H, W).\n            wh_pred (Tensor): wh predict, shape (B, 2, H, W).\n            offset_pred (Tensor): offset predict, shape (B, 2, H, W).\n            img_shape (tuple): image shape in hw format.\n            k (int): Get top k center keypoints from heatmap. Defaults to 100.\n            kernel (int): Max pooling kernel for extract local maximum pixels.\n               Defaults to 3.\n\n        Returns:\n            tuple[Tensor]: Decoded output of CenterNetHead, containing\n               the following Tensors:\n\n              - batch_bboxes (Tensor): Coords of each box with shape (B, k, 5)\n              - batch_topk_labels (Tensor): Categories of each box with \\\n                  shape (B, k)\n        \"\"\"\n        height, width = center_heatmap_pred.shape[2:]\n        inp_h, inp_w = img_shape\n\n        center_heatmap_pred = get_local_maximum(\n            center_heatmap_pred, kernel=kernel)\n\n        *batch_dets, topk_ys, topk_xs = get_topk_from_heatmap(\n            center_heatmap_pred, k=k)\n        batch_scores, batch_index, batch_topk_labels = batch_dets\n\n        wh = transpose_and_gather_feat(wh_pred, batch_index)\n        offset = transpose_and_gather_feat(offset_pred, batch_index)\n        topk_xs = topk_xs + offset[..., 0]\n        topk_ys = topk_ys + offset[..., 1]\n        tl_x = (topk_xs - wh[..., 0] / 2) * (inp_w / width)\n        tl_y = (topk_ys - wh[..., 1] / 2) * (inp_h / height)\n        br_x = (topk_xs + wh[..., 0] / 2) * (inp_w / width)\n        br_y = (topk_ys + wh[..., 1] / 2) * (inp_h / height)\n\n        batch_bboxes = torch.stack([tl_x, tl_y, br_x, br_y], dim=2)\n        batch_bboxes = torch.cat((batch_bboxes, batch_scores[..., None]),\n                                 dim=-1)\n        return batch_bboxes, batch_topk_labels\n\n    def _bboxes_nms(self, bboxes: Tensor, labels: Tensor,\n                    cfg: ConfigDict) -> Tuple[Tensor, Tensor]:\n        \"\"\"bboxes nms.\"\"\"\n        if labels.numel() > 0:\n            max_num = cfg.max_per_img\n            bboxes, keep = batched_nms(bboxes[:, :4], bboxes[:,\n                                                             -1].contiguous(),\n                                       labels, cfg.nms)\n            if max_num > 0:\n                bboxes = bboxes[:max_num]\n                labels = labels[keep][:max_num]\n\n        return bboxes, labels\n"
  },
  {
    "path": "mmdet/models/dense_heads/centernet_update_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import Scale\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox2distance\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, reduce_mean)\nfrom ..utils import multi_apply\nfrom .anchor_free_head import AnchorFreeHead\n\nINF = 1000000000\nRangeType = Sequence[Tuple[int, int]]\n\n\ndef _transpose(tensor_list: List[Tensor],\n               num_point_list: list) -> List[Tensor]:\n    \"\"\"This function is used to transpose image first tensors to level first\n    ones.\"\"\"\n    for img_idx in range(len(tensor_list)):\n        tensor_list[img_idx] = torch.split(\n            tensor_list[img_idx], num_point_list, dim=0)\n\n    tensors_level_first = []\n    for targets_per_level in zip(*tensor_list):\n        tensors_level_first.append(torch.cat(targets_per_level, dim=0))\n    return tensors_level_first\n\n\n@MODELS.register_module()\nclass CenterNetUpdateHead(AnchorFreeHead):\n    \"\"\"CenterNetUpdateHead is an improved version of CenterNet in CenterNet2.\n    Paper link `<https://arxiv.org/abs/2103.07461>`_.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channel in the input feature map.\n        regress_ranges (Sequence[Tuple[int, int]]): Regress range of multiple\n            level points.\n        hm_min_radius (int): Heatmap target minimum radius of cls branch.\n            Defaults to 4.\n        hm_min_overlap (float): Heatmap target minimum overlap of cls branch.\n            Defaults to 0.8.\n        more_pos_thresh (float): The filtering threshold when the cls branch\n            adds more positive samples. Defaults to 0.2.\n        more_pos_topk (int): The maximum number of additional positive samples\n            added to each gt. Defaults to 9.\n        soft_weight_on_reg (bool): Whether to use the soft target of the\n            cls branch as the soft weight of the bbox branch.\n            Defaults to False.\n        loss_cls (:obj:`ConfigDict` or dict): Config of cls loss. Defaults to\n            dict(type='GaussianFocalLoss', loss_weight=1.0)\n        loss_bbox (:obj:`ConfigDict` or dict): Config of bbox loss. Defaults to\n             dict(type='GIoULoss', loss_weight=2.0).\n        norm_cfg (:obj:`ConfigDict` or dict, optional): dictionary to construct\n            and config norm layer.  Defaults to\n            ``norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)``.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config.\n            Unused in CenterNet. Reserved for compatibility with\n            SingleStageDetector.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config\n            of CenterNet.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 regress_ranges: RangeType = ((0, 80), (64, 160), (128, 320),\n                                              (256, 640), (512, INF)),\n                 hm_min_radius: int = 4,\n                 hm_min_overlap: float = 0.8,\n                 more_pos_thresh: float = 0.2,\n                 more_pos_topk: int = 9,\n                 soft_weight_on_reg: bool = False,\n                 loss_cls: ConfigType = dict(\n                     type='GaussianFocalLoss',\n                     pos_weight=0.25,\n                     neg_weight=0.75,\n                     loss_weight=1.0),\n                 loss_bbox: ConfigType = dict(\n                     type='GIoULoss', loss_weight=2.0),\n                 norm_cfg: OptConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 **kwargs) -> None:\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            loss_cls=loss_cls,\n            loss_bbox=loss_bbox,\n            norm_cfg=norm_cfg,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            **kwargs)\n        self.soft_weight_on_reg = soft_weight_on_reg\n        self.hm_min_radius = hm_min_radius\n        self.more_pos_thresh = more_pos_thresh\n        self.more_pos_topk = more_pos_topk\n        self.delta = (1 - hm_min_overlap) / (1 + hm_min_overlap)\n        self.sigmoid_clamp = 0.0001\n\n        # GaussianFocalLoss must be sigmoid mode\n        self.use_sigmoid_cls = True\n        self.cls_out_channels = num_classes\n\n        self.regress_ranges = regress_ranges\n        self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides])\n\n    def _init_predictor(self) -> None:\n        \"\"\"Initialize predictor layers of the head.\"\"\"\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.num_classes, 3, padding=1)\n        self.conv_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1)\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of each level outputs.\n\n            - cls_scores (list[Tensor]): Box scores for each scale level, \\\n            each is a 4D-tensor, the channel number is num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for each \\\n            scale level, each is a 4D-tensor, the channel number is 4.\n        \"\"\"\n        return multi_apply(self.forward_single, x, self.scales, self.strides)\n\n    def forward_single(self, x: Tensor, scale: Scale,\n                       stride: int) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n            scale (:obj:`mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n            stride (int): The corresponding stride for feature maps.\n\n        Returns:\n            tuple: scores for each class, bbox predictions of\n            input feature maps.\n        \"\"\"\n        cls_score, bbox_pred, _, _ = super().forward_single(x)\n        # scale the bbox_pred of different level\n        # float to avoid overflow when enabling FP16\n        bbox_pred = scale(bbox_pred).float()\n        # bbox_pred needed for gradient computation has been modified\n        # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace\n        # F.relu(bbox_pred) with bbox_pred.clamp(min=0)\n        bbox_pred = bbox_pred.clamp(min=0)\n        if not self.training:\n            bbox_pred *= stride\n        return cls_score, bbox_pred\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is 4.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        num_imgs = cls_scores[0].size(0)\n        assert len(cls_scores) == len(bbox_preds)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        all_level_points = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device)\n\n        # 1 flatten outputs\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3, 1).reshape(-1, self.cls_out_channels)\n            for cls_score in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_cls_scores = torch.cat(flatten_cls_scores)\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds)\n\n        # repeat points to align with bbox_preds\n        flatten_points = torch.cat(\n            [points.repeat(num_imgs, 1) for points in all_level_points])\n\n        assert (torch.isfinite(flatten_bbox_preds).all().item())\n\n        # 2 calc reg and cls branch targets\n        cls_targets, bbox_targets = self.get_targets(all_level_points,\n                                                     batch_gt_instances)\n\n        # 3 add more pos index for cls branch\n        featmap_sizes = flatten_points.new_tensor(featmap_sizes)\n        pos_inds, cls_labels = self.add_cls_pos_inds(flatten_points,\n                                                     flatten_bbox_preds,\n                                                     featmap_sizes,\n                                                     batch_gt_instances)\n\n        # 4 calc cls loss\n        if pos_inds is None:\n            # num_gts=0\n            num_pos_cls = bbox_preds[0].new_tensor(0, dtype=torch.float)\n        else:\n            num_pos_cls = bbox_preds[0].new_tensor(\n                len(pos_inds), dtype=torch.float)\n        num_pos_cls = max(reduce_mean(num_pos_cls), 1.0)\n        flatten_cls_scores = flatten_cls_scores.sigmoid().clamp(\n            min=self.sigmoid_clamp, max=1 - self.sigmoid_clamp)\n        cls_loss = self.loss_cls(\n            flatten_cls_scores,\n            cls_targets,\n            pos_inds=pos_inds,\n            pos_labels=cls_labels,\n            avg_factor=num_pos_cls)\n\n        # 5 calc reg loss\n        pos_bbox_inds = torch.nonzero(\n            bbox_targets.max(dim=1)[0] >= 0).squeeze(1)\n        pos_bbox_preds = flatten_bbox_preds[pos_bbox_inds]\n        pos_bbox_targets = bbox_targets[pos_bbox_inds]\n\n        bbox_weight_map = cls_targets.max(dim=1)[0]\n        bbox_weight_map = bbox_weight_map[pos_bbox_inds]\n        bbox_weight_map = bbox_weight_map if self.soft_weight_on_reg \\\n            else torch.ones_like(bbox_weight_map)\n        num_pos_bbox = max(reduce_mean(bbox_weight_map.sum()), 1.0)\n\n        if len(pos_bbox_inds) > 0:\n            pos_points = flatten_points[pos_bbox_inds]\n            pos_decoded_bbox_preds = self.bbox_coder.decode(\n                pos_points, pos_bbox_preds)\n            pos_decoded_target_preds = self.bbox_coder.decode(\n                pos_points, pos_bbox_targets)\n            bbox_loss = self.loss_bbox(\n                pos_decoded_bbox_preds,\n                pos_decoded_target_preds,\n                weight=bbox_weight_map,\n                avg_factor=num_pos_bbox)\n        else:\n            bbox_loss = flatten_bbox_preds.sum() * 0\n\n        return dict(loss_cls=cls_loss, loss_bbox=bbox_loss)\n\n    def get_targets(\n        self,\n        points: List[Tensor],\n        batch_gt_instances: InstanceList,\n    ) -> Tuple[Tensor, Tensor]:\n        \"\"\"Compute classification and bbox targets for points in multiple\n        images.\n\n        Args:\n            points (list[Tensor]): Points of each fpn level, each has shape\n                (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple: Targets of each level.\n\n            - concat_lvl_labels (Tensor): Labels of all level and batch.\n            - concat_lvl_bbox_targets (Tensor): BBox targets of all \\\n            level and batch.\n        \"\"\"\n        assert len(points) == len(self.regress_ranges)\n\n        num_levels = len(points)\n        # the number of points per img, per lvl\n        num_points = [center.size(0) for center in points]\n\n        # expand regress ranges to align with points\n        expanded_regress_ranges = [\n            points[i].new_tensor(self.regress_ranges[i])[None].expand_as(\n                points[i]) for i in range(num_levels)\n        ]\n        # concat all levels points and regress ranges\n        concat_regress_ranges = torch.cat(expanded_regress_ranges, dim=0)\n        concat_points = torch.cat(points, dim=0)\n        concat_strides = torch.cat([\n            concat_points.new_ones(num_points[i]) * self.strides[i]\n            for i in range(num_levels)\n        ])\n\n        # get labels and bbox_targets of each image\n        cls_targets_list, bbox_targets_list = multi_apply(\n            self._get_targets_single,\n            batch_gt_instances,\n            points=concat_points,\n            regress_ranges=concat_regress_ranges,\n            strides=concat_strides)\n\n        bbox_targets_list = _transpose(bbox_targets_list, num_points)\n        cls_targets_list = _transpose(cls_targets_list, num_points)\n        concat_lvl_bbox_targets = torch.cat(bbox_targets_list, 0)\n        concat_lvl_cls_targets = torch.cat(cls_targets_list, dim=0)\n        return concat_lvl_cls_targets, concat_lvl_bbox_targets\n\n    def _get_targets_single(self, gt_instances: InstanceData, points: Tensor,\n                            regress_ranges: Tensor,\n                            strides: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Compute classification and bbox targets for a single image.\"\"\"\n        num_points = points.size(0)\n        num_gts = len(gt_instances)\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n\n        if num_gts == 0:\n            return gt_labels.new_full((num_points,\n                                       self.num_classes),\n                                      self.num_classes), \\\n                   gt_bboxes.new_full((num_points, 4), -1)\n\n        # Calculate the regression tblr target corresponding to all points\n        points = points[:, None].expand(num_points, num_gts, 2)\n        gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 4)\n        strides = strides[:, None, None].expand(num_points, num_gts, 2)\n\n        bbox_target = bbox2distance(points, gt_bboxes)  # M x N x 4\n\n        # condition1: inside a gt bbox\n        inside_gt_bbox_mask = bbox_target.min(dim=2)[0] > 0  # M x N\n\n        # condition2: Calculate the nearest points from\n        # the upper, lower, left and right ranges from\n        # the center of the gt bbox\n        centers = ((gt_bboxes[..., [0, 1]] + gt_bboxes[..., [2, 3]]) / 2)\n        centers_discret = ((centers / strides).int() * strides).float() + \\\n            strides / 2\n\n        centers_discret_dist = points - centers_discret\n        dist_x = centers_discret_dist[..., 0].abs()\n        dist_y = centers_discret_dist[..., 1].abs()\n        inside_gt_center3x3_mask = (dist_x <= strides[..., 0]) & \\\n                                   (dist_y <= strides[..., 0])\n\n        # condition3: limit the regression range for each location\n        bbox_target_wh = bbox_target[..., :2] + bbox_target[..., 2:]\n        crit = (bbox_target_wh**2).sum(dim=2)**0.5 / 2\n        inside_fpn_level_mask = (crit >= regress_ranges[:, [0]]) & \\\n                                (crit <= regress_ranges[:, [1]])\n        bbox_target_mask = inside_gt_bbox_mask & \\\n            inside_gt_center3x3_mask & \\\n            inside_fpn_level_mask\n\n        # Calculate the distance weight map\n        gt_center_peak_mask = ((centers_discret_dist**2).sum(dim=2) == 0)\n        weighted_dist = ((points - centers)**2).sum(dim=2)  # M x N\n        weighted_dist[gt_center_peak_mask] = 0\n\n        areas = (gt_bboxes[..., 2] - gt_bboxes[..., 0]) * (\n            gt_bboxes[..., 3] - gt_bboxes[..., 1])\n        radius = self.delta**2 * 2 * areas\n        radius = torch.clamp(radius, min=self.hm_min_radius**2)\n        weighted_dist = weighted_dist / radius\n\n        # Calculate bbox_target\n        bbox_weighted_dist = weighted_dist.clone()\n        bbox_weighted_dist[bbox_target_mask == 0] = INF * 1.0\n        min_dist, min_inds = bbox_weighted_dist.min(dim=1)\n        bbox_target = bbox_target[range(len(bbox_target)),\n                                  min_inds]  # M x N x 4 --> M x 4\n        bbox_target[min_dist == INF] = -INF\n\n        # Convert to feature map scale\n        bbox_target /= strides[:, 0, :].repeat(1, 2)\n\n        # Calculate cls_target\n        cls_target = self._create_heatmaps_from_dist(weighted_dist, gt_labels)\n\n        return cls_target, bbox_target\n\n    @torch.no_grad()\n    def add_cls_pos_inds(\n        self, flatten_points: Tensor, flatten_bbox_preds: Tensor,\n        featmap_sizes: Tensor, batch_gt_instances: InstanceList\n    ) -> Tuple[Optional[Tensor], Optional[Tensor]]:\n        \"\"\"Provide additional adaptive positive samples to the classification\n        branch.\n\n        Args:\n            flatten_points (Tensor): The point after flatten, including\n                batch image and all levels. The shape is (N, 2).\n            flatten_bbox_preds (Tensor): The bbox predicts after flatten,\n                including batch image and all levels. The shape is (N, 4).\n            featmap_sizes (Tensor): Feature map size of all layers.\n                The shape is (5, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n           tuple:\n\n           - pos_inds (Tensor): Adaptively selected positive sample index.\n           - cls_labels (Tensor): Corresponding positive class label.\n        \"\"\"\n        outputs = self._get_center3x3_region_index_targets(\n            batch_gt_instances, featmap_sizes)\n        cls_labels, fpn_level_masks, center3x3_inds, \\\n            center3x3_bbox_targets, center3x3_masks = outputs\n\n        num_gts, total_level, K = cls_labels.shape[0], len(\n            self.strides), center3x3_masks.shape[-1]\n\n        if num_gts == 0:\n            return None, None\n\n        # The out-of-bounds index is forcibly set to 0\n        # to prevent loss calculation errors\n        center3x3_inds[center3x3_masks == 0] = 0\n        reg_pred_center3x3 = flatten_bbox_preds[center3x3_inds]\n        center3x3_points = flatten_points[center3x3_inds].view(-1, 2)\n\n        center3x3_bbox_targets_expand = center3x3_bbox_targets.view(\n            -1, 4).clamp(min=0)\n\n        pos_decoded_bbox_preds = self.bbox_coder.decode(\n            center3x3_points, reg_pred_center3x3.view(-1, 4))\n        pos_decoded_target_preds = self.bbox_coder.decode(\n            center3x3_points, center3x3_bbox_targets_expand)\n        center3x3_bbox_loss = self.loss_bbox(\n            pos_decoded_bbox_preds,\n            pos_decoded_target_preds,\n            None,\n            reduction_override='none').view(num_gts, total_level,\n                                            K) / self.loss_bbox.loss_weight\n\n        # Invalid index Loss set to infinity\n        center3x3_bbox_loss[center3x3_masks == 0] = INF\n\n        # 4 is the center point of the sampled 9 points, the center point\n        # of gt bbox after discretization.\n        # The center point of gt bbox after discretization\n        # must be a positive sample, so we force its loss to be set to 0.\n        center3x3_bbox_loss.view(-1, K)[fpn_level_masks.view(-1), 4] = 0\n        center3x3_bbox_loss = center3x3_bbox_loss.view(num_gts, -1)\n\n        loss_thr = torch.kthvalue(\n            center3x3_bbox_loss, self.more_pos_topk, dim=1)[0]\n\n        loss_thr[loss_thr > self.more_pos_thresh] = self.more_pos_thresh\n        new_pos = center3x3_bbox_loss < loss_thr.view(num_gts, 1)\n        pos_inds = center3x3_inds.view(num_gts, -1)[new_pos]\n        cls_labels = cls_labels.view(num_gts,\n                                     1).expand(num_gts,\n                                               total_level * K)[new_pos]\n        return pos_inds, cls_labels\n\n    def _create_heatmaps_from_dist(self, weighted_dist: Tensor,\n                                   cls_labels: Tensor) -> Tensor:\n        \"\"\"Generate heatmaps of classification branch based on weighted\n        distance map.\"\"\"\n        heatmaps = weighted_dist.new_zeros(\n            (weighted_dist.shape[0], self.num_classes))\n        for c in range(self.num_classes):\n            inds = (cls_labels == c)  # N\n            if inds.int().sum() == 0:\n                continue\n            heatmaps[:, c] = torch.exp(-weighted_dist[:, inds].min(dim=1)[0])\n            zeros = heatmaps[:, c] < 1e-4\n            heatmaps[zeros, c] = 0\n        return heatmaps\n\n    def _get_center3x3_region_index_targets(self,\n                                            bacth_gt_instances: InstanceList,\n                                            shapes_per_level: Tensor) -> tuple:\n        \"\"\"Get the center (and the 3x3 region near center) locations and target\n        of each objects.\"\"\"\n        cls_labels = []\n        inside_fpn_level_masks = []\n        center3x3_inds = []\n        center3x3_masks = []\n        center3x3_bbox_targets = []\n\n        total_levels = len(self.strides)\n        batch = len(bacth_gt_instances)\n\n        shapes_per_level = shapes_per_level.long()\n        area_per_level = (shapes_per_level[:, 0] * shapes_per_level[:, 1])\n\n        # Select a total of 9 positions of 3x3 in the center of the gt bbox\n        # as candidate positive samples\n        K = 9\n        dx = shapes_per_level.new_tensor([-1, 0, 1, -1, 0, 1, -1, 0,\n                                          1]).view(1, 1, K)\n        dy = shapes_per_level.new_tensor([-1, -1, -1, 0, 0, 0, 1, 1,\n                                          1]).view(1, 1, K)\n\n        regress_ranges = shapes_per_level.new_tensor(self.regress_ranges).view(\n            len(self.regress_ranges), 2)  # L x 2\n        strides = shapes_per_level.new_tensor(self.strides)\n\n        start_coord_pre_level = []\n        _start = 0\n        for level in range(total_levels):\n            start_coord_pre_level.append(_start)\n            _start = _start + batch * area_per_level[level]\n        start_coord_pre_level = shapes_per_level.new_tensor(\n            start_coord_pre_level).view(1, total_levels, 1)\n        area_per_level = area_per_level.view(1, total_levels, 1)\n\n        for im_i in range(batch):\n            gt_instance = bacth_gt_instances[im_i]\n            gt_bboxes = gt_instance.bboxes\n            gt_labels = gt_instance.labels\n            num_gts = gt_bboxes.shape[0]\n            if num_gts == 0:\n                continue\n\n            cls_labels.append(gt_labels)\n\n            gt_bboxes = gt_bboxes[:, None].expand(num_gts, total_levels, 4)\n            expanded_strides = strides[None, :,\n                                       None].expand(num_gts, total_levels, 2)\n            expanded_regress_ranges = regress_ranges[None].expand(\n                num_gts, total_levels, 2)\n            expanded_shapes_per_level = shapes_per_level[None].expand(\n                num_gts, total_levels, 2)\n\n            # calc reg_target\n            centers = ((gt_bboxes[..., [0, 1]] + gt_bboxes[..., [2, 3]]) / 2)\n            centers_inds = (centers / expanded_strides).long()\n            centers_discret = centers_inds * expanded_strides \\\n                + expanded_strides // 2\n\n            bbox_target = bbox2distance(centers_discret,\n                                        gt_bboxes)  # M x N x 4\n\n            # calc inside_fpn_level_mask\n            bbox_target_wh = bbox_target[..., :2] + bbox_target[..., 2:]\n            crit = (bbox_target_wh**2).sum(dim=2)**0.5 / 2\n            inside_fpn_level_mask = \\\n                (crit >= expanded_regress_ranges[..., 0]) & \\\n                (crit <= expanded_regress_ranges[..., 1])\n\n            inside_gt_bbox_mask = bbox_target.min(dim=2)[0] >= 0\n            inside_fpn_level_mask = inside_gt_bbox_mask & inside_fpn_level_mask\n            inside_fpn_level_masks.append(inside_fpn_level_mask)\n\n            # calc center3x3_ind and mask\n            expand_ws = expanded_shapes_per_level[..., 1:2].expand(\n                num_gts, total_levels, K)\n            expand_hs = expanded_shapes_per_level[..., 0:1].expand(\n                num_gts, total_levels, K)\n            centers_inds_x = centers_inds[..., 0:1]\n            centers_inds_y = centers_inds[..., 1:2]\n\n            center3x3_idx = start_coord_pre_level + \\\n                im_i * area_per_level + \\\n                (centers_inds_y + dy) * expand_ws + \\\n                (centers_inds_x + dx)\n            center3x3_mask = \\\n                ((centers_inds_y + dy) < expand_hs) & \\\n                ((centers_inds_y + dy) >= 0) & \\\n                ((centers_inds_x + dx) < expand_ws) & \\\n                ((centers_inds_x + dx) >= 0)\n\n            # recalc center3x3 region reg target\n            bbox_target = bbox_target / expanded_strides.repeat(1, 1, 2)\n            center3x3_bbox_target = bbox_target[..., None, :].expand(\n                num_gts, total_levels, K, 4).clone()\n            center3x3_bbox_target[..., 0] += dx\n            center3x3_bbox_target[..., 1] += dy\n            center3x3_bbox_target[..., 2] -= dx\n            center3x3_bbox_target[..., 3] -= dy\n            # update center3x3_mask\n            center3x3_mask = center3x3_mask & (\n                center3x3_bbox_target.min(dim=3)[0] >= 0)  # n x L x K\n\n            center3x3_inds.append(center3x3_idx)\n            center3x3_masks.append(center3x3_mask)\n            center3x3_bbox_targets.append(center3x3_bbox_target)\n\n        if len(inside_fpn_level_masks) > 0:\n            cls_labels = torch.cat(cls_labels, dim=0)\n            inside_fpn_level_masks = torch.cat(inside_fpn_level_masks, dim=0)\n            center3x3_inds = torch.cat(center3x3_inds, dim=0).long()\n            center3x3_bbox_targets = torch.cat(center3x3_bbox_targets, dim=0)\n            center3x3_masks = torch.cat(center3x3_masks, dim=0)\n        else:\n            cls_labels = shapes_per_level.new_zeros(0).long()\n            inside_fpn_level_masks = shapes_per_level.new_zeros(\n                (0, total_levels)).bool()\n            center3x3_inds = shapes_per_level.new_zeros(\n                (0, total_levels, K)).long()\n            center3x3_bbox_targets = shapes_per_level.new_zeros(\n                (0, total_levels, K, 4)).float()\n            center3x3_masks = shapes_per_level.new_zeros(\n                (0, total_levels, K)).bool()\n        return cls_labels, inside_fpn_level_masks, center3x3_inds, \\\n            center3x3_bbox_targets, center3x3_masks\n"
  },
  {
    "path": "mmdet/models/dense_heads/centripetal_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops import DeformConv2d\nfrom mmengine.model import normal_init\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import (ConfigType, InstanceList, OptInstanceList,\n                         OptMultiConfig)\nfrom ..utils import multi_apply\nfrom .corner_head import CornerHead\n\n\n@MODELS.register_module()\nclass CentripetalHead(CornerHead):\n    \"\"\"Head of CentripetalNet: Pursuing High-quality Keypoint Pairs for Object\n    Detection.\n\n    CentripetalHead inherits from :class:`CornerHead`. It removes the\n    embedding branch and adds guiding shift and centripetal shift branches.\n    More details can be found in the `paper\n    <https://arxiv.org/abs/2003.09119>`_ .\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        num_feat_levels (int): Levels of feature from the previous module.\n            2 for HourglassNet-104 and 1 for HourglassNet-52. HourglassNet-104\n            outputs the final feature and intermediate supervision feature and\n            HourglassNet-52 only outputs the final feature. Defaults to 2.\n        corner_emb_channels (int): Channel of embedding vector. Defaults to 1.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config.\n            Useless in CornerHead, but we keep this variable for\n            SingleStageDetector.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            CornerHead.\n        loss_heatmap (:obj:`ConfigDict` or dict): Config of corner heatmap\n            loss. Defaults to GaussianFocalLoss.\n        loss_embedding (:obj:`ConfigDict` or dict): Config of corner embedding\n            loss. Defaults to AssociativeEmbeddingLoss.\n        loss_offset (:obj:`ConfigDict` or dict): Config of corner offset loss.\n            Defaults to SmoothL1Loss.\n        loss_guiding_shift (:obj:`ConfigDict` or dict): Config of\n            guiding shift loss. Defaults to SmoothL1Loss.\n        loss_centripetal_shift (:obj:`ConfigDict` or dict): Config of\n            centripetal shift loss. Defaults to SmoothL1Loss.\n       init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n           the initialization.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 centripetal_shift_channels: int = 2,\n                 guiding_shift_channels: int = 2,\n                 feat_adaption_conv_kernel: int = 3,\n                 loss_guiding_shift: ConfigType = dict(\n                     type='SmoothL1Loss', beta=1.0, loss_weight=0.05),\n                 loss_centripetal_shift: ConfigType = dict(\n                     type='SmoothL1Loss', beta=1.0, loss_weight=1),\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        assert centripetal_shift_channels == 2, (\n            'CentripetalHead only support centripetal_shift_channels == 2')\n        self.centripetal_shift_channels = centripetal_shift_channels\n        assert guiding_shift_channels == 2, (\n            'CentripetalHead only support guiding_shift_channels == 2')\n        self.guiding_shift_channels = guiding_shift_channels\n        self.feat_adaption_conv_kernel = feat_adaption_conv_kernel\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n        self.loss_guiding_shift = MODELS.build(loss_guiding_shift)\n        self.loss_centripetal_shift = MODELS.build(loss_centripetal_shift)\n\n    def _init_centripetal_layers(self) -> None:\n        \"\"\"Initialize centripetal layers.\n\n        Including feature adaption deform convs (feat_adaption), deform offset\n        prediction convs (dcn_off), guiding shift (guiding_shift) and\n        centripetal shift ( centripetal_shift). Each branch has two parts:\n        prefix `tl_` for top-left and `br_` for bottom-right.\n        \"\"\"\n        self.tl_feat_adaption = nn.ModuleList()\n        self.br_feat_adaption = nn.ModuleList()\n        self.tl_dcn_offset = nn.ModuleList()\n        self.br_dcn_offset = nn.ModuleList()\n        self.tl_guiding_shift = nn.ModuleList()\n        self.br_guiding_shift = nn.ModuleList()\n        self.tl_centripetal_shift = nn.ModuleList()\n        self.br_centripetal_shift = nn.ModuleList()\n\n        for _ in range(self.num_feat_levels):\n            self.tl_feat_adaption.append(\n                DeformConv2d(self.in_channels, self.in_channels,\n                             self.feat_adaption_conv_kernel, 1, 1))\n            self.br_feat_adaption.append(\n                DeformConv2d(self.in_channels, self.in_channels,\n                             self.feat_adaption_conv_kernel, 1, 1))\n\n            self.tl_guiding_shift.append(\n                self._make_layers(\n                    out_channels=self.guiding_shift_channels,\n                    in_channels=self.in_channels))\n            self.br_guiding_shift.append(\n                self._make_layers(\n                    out_channels=self.guiding_shift_channels,\n                    in_channels=self.in_channels))\n\n            self.tl_dcn_offset.append(\n                ConvModule(\n                    self.guiding_shift_channels,\n                    self.feat_adaption_conv_kernel**2 *\n                    self.guiding_shift_channels,\n                    1,\n                    bias=False,\n                    act_cfg=None))\n            self.br_dcn_offset.append(\n                ConvModule(\n                    self.guiding_shift_channels,\n                    self.feat_adaption_conv_kernel**2 *\n                    self.guiding_shift_channels,\n                    1,\n                    bias=False,\n                    act_cfg=None))\n\n            self.tl_centripetal_shift.append(\n                self._make_layers(\n                    out_channels=self.centripetal_shift_channels,\n                    in_channels=self.in_channels))\n            self.br_centripetal_shift.append(\n                self._make_layers(\n                    out_channels=self.centripetal_shift_channels,\n                    in_channels=self.in_channels))\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers for CentripetalHead.\n\n        Including two parts: CornerHead layers and CentripetalHead layers\n        \"\"\"\n        super()._init_layers()  # using _init_layers in CornerHead\n        self._init_centripetal_layers()\n\n    def init_weights(self) -> None:\n        super().init_weights()\n        for i in range(self.num_feat_levels):\n            normal_init(self.tl_feat_adaption[i], std=0.01)\n            normal_init(self.br_feat_adaption[i], std=0.01)\n            normal_init(self.tl_dcn_offset[i].conv, std=0.1)\n            normal_init(self.br_dcn_offset[i].conv, std=0.1)\n            _ = [x.conv.reset_parameters() for x in self.tl_guiding_shift[i]]\n            _ = [x.conv.reset_parameters() for x in self.br_guiding_shift[i]]\n            _ = [\n                x.conv.reset_parameters() for x in self.tl_centripetal_shift[i]\n            ]\n            _ = [\n                x.conv.reset_parameters() for x in self.br_centripetal_shift[i]\n            ]\n\n    def forward_single(self, x: Tensor, lvl_ind: int) -> List[Tensor]:\n        \"\"\"Forward feature of a single level.\n\n        Args:\n            x (Tensor): Feature of a single level.\n            lvl_ind (int): Level index of current feature.\n\n        Returns:\n            tuple[Tensor]: A tuple of CentripetalHead's output for current\n            feature level. Containing the following Tensors:\n\n                - tl_heat (Tensor): Predicted top-left corner heatmap.\n                - br_heat (Tensor): Predicted bottom-right corner heatmap.\n                - tl_off (Tensor): Predicted top-left offset heatmap.\n                - br_off (Tensor): Predicted bottom-right offset heatmap.\n                - tl_guiding_shift (Tensor): Predicted top-left guiding shift\n                  heatmap.\n                - br_guiding_shift (Tensor): Predicted bottom-right guiding\n                  shift heatmap.\n                - tl_centripetal_shift (Tensor): Predicted top-left centripetal\n                  shift heatmap.\n                - br_centripetal_shift (Tensor): Predicted bottom-right\n                  centripetal shift heatmap.\n        \"\"\"\n        tl_heat, br_heat, _, _, tl_off, br_off, tl_pool, br_pool = super(\n        ).forward_single(\n            x, lvl_ind, return_pool=True)\n\n        tl_guiding_shift = self.tl_guiding_shift[lvl_ind](tl_pool)\n        br_guiding_shift = self.br_guiding_shift[lvl_ind](br_pool)\n\n        tl_dcn_offset = self.tl_dcn_offset[lvl_ind](tl_guiding_shift.detach())\n        br_dcn_offset = self.br_dcn_offset[lvl_ind](br_guiding_shift.detach())\n\n        tl_feat_adaption = self.tl_feat_adaption[lvl_ind](tl_pool,\n                                                          tl_dcn_offset)\n        br_feat_adaption = self.br_feat_adaption[lvl_ind](br_pool,\n                                                          br_dcn_offset)\n\n        tl_centripetal_shift = self.tl_centripetal_shift[lvl_ind](\n            tl_feat_adaption)\n        br_centripetal_shift = self.br_centripetal_shift[lvl_ind](\n            br_feat_adaption)\n\n        result_list = [\n            tl_heat, br_heat, tl_off, br_off, tl_guiding_shift,\n            br_guiding_shift, tl_centripetal_shift, br_centripetal_shift\n        ]\n        return result_list\n\n    def loss_by_feat(\n            self,\n            tl_heats: List[Tensor],\n            br_heats: List[Tensor],\n            tl_offs: List[Tensor],\n            br_offs: List[Tensor],\n            tl_guiding_shifts: List[Tensor],\n            br_guiding_shifts: List[Tensor],\n            tl_centripetal_shifts: List[Tensor],\n            br_centripetal_shifts: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            tl_heats (list[Tensor]): Top-left corner heatmaps for each level\n                with shape (N, num_classes, H, W).\n            br_heats (list[Tensor]): Bottom-right corner heatmaps for each\n                level with shape (N, num_classes, H, W).\n            tl_offs (list[Tensor]): Top-left corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            br_offs (list[Tensor]): Bottom-right corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            tl_guiding_shifts (list[Tensor]): Top-left guiding shifts for each\n                level with shape (N, guiding_shift_channels, H, W).\n            br_guiding_shifts (list[Tensor]): Bottom-right guiding shifts for\n                each level with shape (N, guiding_shift_channels, H, W).\n            tl_centripetal_shifts (list[Tensor]): Top-left centripetal shifts\n                for each level with shape (N, centripetal_shift_channels, H,\n                W).\n            br_centripetal_shifts (list[Tensor]): Bottom-right centripetal\n                shifts for each level with shape (N,\n                centripetal_shift_channels, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Specify which bounding boxes can be ignored when computing\n                the loss.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components. Containing the\n            following losses:\n\n                - det_loss (list[Tensor]): Corner keypoint losses of all\n                  feature levels.\n                - off_loss (list[Tensor]): Corner offset losses of all feature\n                  levels.\n                - guiding_loss (list[Tensor]): Guiding shift losses of all\n                  feature levels.\n                - centripetal_loss (list[Tensor]): Centripetal shift losses of\n                  all feature levels.\n        \"\"\"\n        gt_bboxes = [\n            gt_instances.bboxes for gt_instances in batch_gt_instances\n        ]\n        gt_labels = [\n            gt_instances.labels for gt_instances in batch_gt_instances\n        ]\n\n        targets = self.get_targets(\n            gt_bboxes,\n            gt_labels,\n            tl_heats[-1].shape,\n            batch_img_metas[0]['batch_input_shape'],\n            with_corner_emb=self.with_corner_emb,\n            with_guiding_shift=True,\n            with_centripetal_shift=True)\n        mlvl_targets = [targets for _ in range(self.num_feat_levels)]\n        [det_losses, off_losses, guiding_losses, centripetal_losses\n         ] = multi_apply(self.loss_by_feat_single, tl_heats, br_heats, tl_offs,\n                         br_offs, tl_guiding_shifts, br_guiding_shifts,\n                         tl_centripetal_shifts, br_centripetal_shifts,\n                         mlvl_targets)\n        loss_dict = dict(\n            det_loss=det_losses,\n            off_loss=off_losses,\n            guiding_loss=guiding_losses,\n            centripetal_loss=centripetal_losses)\n        return loss_dict\n\n    def loss_by_feat_single(self, tl_hmp: Tensor, br_hmp: Tensor,\n                            tl_off: Tensor, br_off: Tensor,\n                            tl_guiding_shift: Tensor, br_guiding_shift: Tensor,\n                            tl_centripetal_shift: Tensor,\n                            br_centripetal_shift: Tensor,\n                            targets: dict) -> Tuple[Tensor, ...]:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            tl_hmp (Tensor): Top-left corner heatmap for current level with\n                shape (N, num_classes, H, W).\n            br_hmp (Tensor): Bottom-right corner heatmap for current level with\n                shape (N, num_classes, H, W).\n            tl_off (Tensor): Top-left corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            br_off (Tensor): Bottom-right corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            tl_guiding_shift (Tensor): Top-left guiding shift for current level\n                with shape (N, guiding_shift_channels, H, W).\n            br_guiding_shift (Tensor): Bottom-right guiding shift for current\n                level with shape (N, guiding_shift_channels, H, W).\n            tl_centripetal_shift (Tensor): Top-left centripetal shift for\n                current level with shape (N, centripetal_shift_channels, H, W).\n            br_centripetal_shift (Tensor): Bottom-right centripetal shift for\n                current level with shape (N, centripetal_shift_channels, H, W).\n            targets (dict): Corner target generated by `get_targets`.\n\n        Returns:\n            tuple[torch.Tensor]: Losses of the head's different branches\n            containing the following losses:\n\n                - det_loss (Tensor): Corner keypoint loss.\n                - off_loss (Tensor): Corner offset loss.\n                - guiding_loss (Tensor): Guiding shift loss.\n                - centripetal_loss (Tensor): Centripetal shift loss.\n        \"\"\"\n        targets['corner_embedding'] = None\n\n        det_loss, _, _, off_loss = super().loss_by_feat_single(\n            tl_hmp, br_hmp, None, None, tl_off, br_off, targets)\n\n        gt_tl_guiding_shift = targets['topleft_guiding_shift']\n        gt_br_guiding_shift = targets['bottomright_guiding_shift']\n        gt_tl_centripetal_shift = targets['topleft_centripetal_shift']\n        gt_br_centripetal_shift = targets['bottomright_centripetal_shift']\n\n        gt_tl_heatmap = targets['topleft_heatmap']\n        gt_br_heatmap = targets['bottomright_heatmap']\n        # We only compute the offset loss at the real corner position.\n        # The value of real corner would be 1 in heatmap ground truth.\n        # The mask is computed in class agnostic mode and its shape is\n        # batch * 1 * width * height.\n        tl_mask = gt_tl_heatmap.eq(1).sum(1).gt(0).unsqueeze(1).type_as(\n            gt_tl_heatmap)\n        br_mask = gt_br_heatmap.eq(1).sum(1).gt(0).unsqueeze(1).type_as(\n            gt_br_heatmap)\n\n        # Guiding shift loss\n        tl_guiding_loss = self.loss_guiding_shift(\n            tl_guiding_shift,\n            gt_tl_guiding_shift,\n            tl_mask,\n            avg_factor=tl_mask.sum())\n        br_guiding_loss = self.loss_guiding_shift(\n            br_guiding_shift,\n            gt_br_guiding_shift,\n            br_mask,\n            avg_factor=br_mask.sum())\n        guiding_loss = (tl_guiding_loss + br_guiding_loss) / 2.0\n        # Centripetal shift loss\n        tl_centripetal_loss = self.loss_centripetal_shift(\n            tl_centripetal_shift,\n            gt_tl_centripetal_shift,\n            tl_mask,\n            avg_factor=tl_mask.sum())\n        br_centripetal_loss = self.loss_centripetal_shift(\n            br_centripetal_shift,\n            gt_br_centripetal_shift,\n            br_mask,\n            avg_factor=br_mask.sum())\n        centripetal_loss = (tl_centripetal_loss + br_centripetal_loss) / 2.0\n\n        return det_loss, off_loss, guiding_loss, centripetal_loss\n\n    def predict_by_feat(self,\n                        tl_heats: List[Tensor],\n                        br_heats: List[Tensor],\n                        tl_offs: List[Tensor],\n                        br_offs: List[Tensor],\n                        tl_guiding_shifts: List[Tensor],\n                        br_guiding_shifts: List[Tensor],\n                        tl_centripetal_shifts: List[Tensor],\n                        br_centripetal_shifts: List[Tensor],\n                        batch_img_metas: Optional[List[dict]] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Args:\n            tl_heats (list[Tensor]): Top-left corner heatmaps for each level\n                with shape (N, num_classes, H, W).\n            br_heats (list[Tensor]): Bottom-right corner heatmaps for each\n                level with shape (N, num_classes, H, W).\n            tl_offs (list[Tensor]): Top-left corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            br_offs (list[Tensor]): Bottom-right corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            tl_guiding_shifts (list[Tensor]): Top-left guiding shifts for each\n                level with shape (N, guiding_shift_channels, H, W). Useless in\n                this function, we keep this arg because it's the raw output\n                from CentripetalHead.\n            br_guiding_shifts (list[Tensor]): Bottom-right guiding shifts for\n                each level with shape (N, guiding_shift_channels, H, W).\n                Useless in this function, we keep this arg because it's the\n                raw output from CentripetalHead.\n            tl_centripetal_shifts (list[Tensor]): Top-left centripetal shifts\n                for each level with shape (N, centripetal_shift_channels, H,\n                W).\n            br_centripetal_shifts (list[Tensor]): Bottom-right centripetal\n                shifts for each level with shape (N,\n                centripetal_shift_channels, H, W).\n            batch_img_metas (list[dict], optional): Batch image meta info.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert tl_heats[-1].shape[0] == br_heats[-1].shape[0] == len(\n            batch_img_metas)\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            result_list.append(\n                self._predict_by_feat_single(\n                    tl_heats[-1][img_id:img_id + 1, :],\n                    br_heats[-1][img_id:img_id + 1, :],\n                    tl_offs[-1][img_id:img_id + 1, :],\n                    br_offs[-1][img_id:img_id + 1, :],\n                    batch_img_metas[img_id],\n                    tl_emb=None,\n                    br_emb=None,\n                    tl_centripetal_shift=tl_centripetal_shifts[-1][\n                        img_id:img_id + 1, :],\n                    br_centripetal_shift=br_centripetal_shifts[-1][\n                        img_id:img_id + 1, :],\n                    rescale=rescale,\n                    with_nms=with_nms))\n\n        return result_list\n"
  },
  {
    "path": "mmdet/models/dense_heads/condinst_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Dict, List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, Scale\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule, kaiming_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import cat_boxes\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig, OptConfigType,\n                         OptInstanceList, reduce_mean)\nfrom ..task_modules.prior_generators import MlvlPointGenerator\nfrom ..utils import (aligned_bilinear, filter_scores_and_topk, multi_apply,\n                     relative_coordinate_maps, select_single_mlvl)\nfrom ..utils.misc import empty_instances\nfrom .base_mask_head import BaseMaskHead\nfrom .fcos_head import FCOSHead\n\nINF = 1e8\n\n\n@MODELS.register_module()\nclass CondInstBboxHead(FCOSHead):\n    \"\"\"CondInst box head used in https://arxiv.org/abs/1904.02689.\n\n    Note that CondInst Bbox Head is a extension of FCOS head.\n    Two differences are described as follows:\n\n    1. CondInst box head predicts a set of params for each instance.\n    2. CondInst box head return the pos_gt_inds and pos_inds.\n\n    Args:\n        num_params (int): Number of params for instance segmentation.\n    \"\"\"\n\n    def __init__(self, *args, num_params: int = 169, **kwargs) -> None:\n        self.num_params = num_params\n        super().__init__(*args, **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        super()._init_layers()\n        self.controller = nn.Conv2d(\n            self.feat_channels, self.num_params, 3, padding=1)\n\n    def forward_single(self, x: Tensor, scale: Scale,\n                       stride: int) -> Tuple[Tensor, Tensor, Tensor, Tensor]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n            scale (:obj:`mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n            stride (int): The corresponding stride for feature maps, only\n                used to normalize the bbox prediction when self.norm_on_bbox\n                is True.\n\n        Returns:\n            tuple: scores for each class, bbox predictions, centerness\n            predictions and param predictions of input feature maps.\n        \"\"\"\n        cls_score, bbox_pred, cls_feat, reg_feat = \\\n            super(FCOSHead, self).forward_single(x)\n        if self.centerness_on_reg:\n            centerness = self.conv_centerness(reg_feat)\n        else:\n            centerness = self.conv_centerness(cls_feat)\n        # scale the bbox_pred of different level\n        # float to avoid overflow when enabling FP16\n        bbox_pred = scale(bbox_pred).float()\n        if self.norm_on_bbox:\n            # bbox_pred needed for gradient computation has been modified\n            # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace\n            # F.relu(bbox_pred) with bbox_pred.clamp(min=0)\n            bbox_pred = bbox_pred.clamp(min=0)\n            if not self.training:\n                bbox_pred *= stride\n        else:\n            bbox_pred = bbox_pred.exp()\n        param_pred = self.controller(reg_feat)\n        return cls_score, bbox_pred, centerness, param_pred\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        centernesses: List[Tensor],\n        param_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is\n                num_points * num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is\n                num_points * 4.\n            centernesses (list[Tensor]): centerness for each scale level, each\n                is a 4D-tensor, the channel number is num_points * 1.\n            param_preds (List[Tensor]): param_pred for each scale level, each\n                is a 4D-tensor, the channel number is num_params.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds) == len(centernesses)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        # Need stride for rel coord compute\n        all_level_points_strides = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device,\n            with_stride=True)\n        all_level_points = [i[:, :2] for i in all_level_points_strides]\n        all_level_strides = [i[:, 2] for i in all_level_points_strides]\n        labels, bbox_targets, pos_inds_list, pos_gt_inds_list = \\\n            self.get_targets(all_level_points, batch_gt_instances)\n\n        num_imgs = cls_scores[0].size(0)\n        # flatten cls_scores, bbox_preds and centerness\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3, 1).reshape(-1, self.cls_out_channels)\n            for cls_score in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_centerness = [\n            centerness.permute(0, 2, 3, 1).reshape(-1)\n            for centerness in centernesses\n        ]\n        flatten_cls_scores = torch.cat(flatten_cls_scores)\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds)\n        flatten_centerness = torch.cat(flatten_centerness)\n        flatten_labels = torch.cat(labels)\n        flatten_bbox_targets = torch.cat(bbox_targets)\n        # repeat points to align with bbox_preds\n        flatten_points = torch.cat(\n            [points.repeat(num_imgs, 1) for points in all_level_points])\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((flatten_labels >= 0)\n                    & (flatten_labels < bg_class_ind)).nonzero().reshape(-1)\n        num_pos = torch.tensor(\n            len(pos_inds), dtype=torch.float, device=bbox_preds[0].device)\n        num_pos = max(reduce_mean(num_pos), 1.0)\n        loss_cls = self.loss_cls(\n            flatten_cls_scores, flatten_labels, avg_factor=num_pos)\n\n        pos_bbox_preds = flatten_bbox_preds[pos_inds]\n        pos_centerness = flatten_centerness[pos_inds]\n        pos_bbox_targets = flatten_bbox_targets[pos_inds]\n        pos_centerness_targets = self.centerness_target(pos_bbox_targets)\n        # centerness weighted iou loss\n        centerness_denorm = max(\n            reduce_mean(pos_centerness_targets.sum().detach()), 1e-6)\n\n        if len(pos_inds) > 0:\n            pos_points = flatten_points[pos_inds]\n            pos_decoded_bbox_preds = self.bbox_coder.decode(\n                pos_points, pos_bbox_preds)\n            pos_decoded_target_preds = self.bbox_coder.decode(\n                pos_points, pos_bbox_targets)\n            loss_bbox = self.loss_bbox(\n                pos_decoded_bbox_preds,\n                pos_decoded_target_preds,\n                weight=pos_centerness_targets,\n                avg_factor=centerness_denorm)\n            loss_centerness = self.loss_centerness(\n                pos_centerness, pos_centerness_targets, avg_factor=num_pos)\n        else:\n            loss_bbox = pos_bbox_preds.sum()\n            loss_centerness = pos_centerness.sum()\n\n        self._raw_positive_infos.update(cls_scores=cls_scores)\n        self._raw_positive_infos.update(centernesses=centernesses)\n        self._raw_positive_infos.update(param_preds=param_preds)\n        self._raw_positive_infos.update(all_level_points=all_level_points)\n        self._raw_positive_infos.update(all_level_strides=all_level_strides)\n        self._raw_positive_infos.update(pos_gt_inds_list=pos_gt_inds_list)\n        self._raw_positive_infos.update(pos_inds_list=pos_inds_list)\n\n        return dict(\n            loss_cls=loss_cls,\n            loss_bbox=loss_bbox,\n            loss_centerness=loss_centerness)\n\n    def get_targets(\n        self, points: List[Tensor], batch_gt_instances: InstanceList\n    ) -> Tuple[List[Tensor], List[Tensor], List[Tensor], List[Tensor]]:\n        \"\"\"Compute regression, classification and centerness targets for points\n        in multiple images.\n\n        Args:\n            points (list[Tensor]): Points of each fpn level, each has shape\n                (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple: Targets of each level.\n\n            - concat_lvl_labels (list[Tensor]): Labels of each level.\n            - concat_lvl_bbox_targets (list[Tensor]): BBox targets of each \\\n            level.\n            - pos_inds_list (list[Tensor]): pos_inds of each image.\n            - pos_gt_inds_list (List[Tensor]): pos_gt_inds of each image.\n        \"\"\"\n        assert len(points) == len(self.regress_ranges)\n        num_levels = len(points)\n        # expand regress ranges to align with points\n        expanded_regress_ranges = [\n            points[i].new_tensor(self.regress_ranges[i])[None].expand_as(\n                points[i]) for i in range(num_levels)\n        ]\n        # concat all levels points and regress ranges\n        concat_regress_ranges = torch.cat(expanded_regress_ranges, dim=0)\n        concat_points = torch.cat(points, dim=0)\n\n        # the number of points per img, per lvl\n        num_points = [center.size(0) for center in points]\n\n        # get labels and bbox_targets of each image\n        labels_list, bbox_targets_list, pos_inds_list, pos_gt_inds_list = \\\n            multi_apply(\n                self._get_targets_single,\n                batch_gt_instances,\n                points=concat_points,\n                regress_ranges=concat_regress_ranges,\n                num_points_per_lvl=num_points)\n\n        # split to per img, per level\n        labels_list = [labels.split(num_points, 0) for labels in labels_list]\n        bbox_targets_list = [\n            bbox_targets.split(num_points, 0)\n            for bbox_targets in bbox_targets_list\n        ]\n\n        # concat per level image\n        concat_lvl_labels = []\n        concat_lvl_bbox_targets = []\n        for i in range(num_levels):\n            concat_lvl_labels.append(\n                torch.cat([labels[i] for labels in labels_list]))\n            bbox_targets = torch.cat(\n                [bbox_targets[i] for bbox_targets in bbox_targets_list])\n            if self.norm_on_bbox:\n                bbox_targets = bbox_targets / self.strides[i]\n            concat_lvl_bbox_targets.append(bbox_targets)\n        return (concat_lvl_labels, concat_lvl_bbox_targets, pos_inds_list,\n                pos_gt_inds_list)\n\n    def _get_targets_single(\n        self, gt_instances: InstanceData, points: Tensor,\n        regress_ranges: Tensor, num_points_per_lvl: List[int]\n    ) -> Tuple[Tensor, Tensor, Tensor, Tensor]:\n        \"\"\"Compute regression and classification targets for a single image.\"\"\"\n        num_points = points.size(0)\n        num_gts = len(gt_instances)\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        gt_masks = gt_instances.get('masks', None)\n\n        if num_gts == 0:\n            return gt_labels.new_full((num_points,), self.num_classes), \\\n                   gt_bboxes.new_zeros((num_points, 4)), \\\n                   gt_bboxes.new_zeros((0,), dtype=torch.int64), \\\n                   gt_bboxes.new_zeros((0,), dtype=torch.int64)\n\n        areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0]) * (\n            gt_bboxes[:, 3] - gt_bboxes[:, 1])\n        # TODO: figure out why these two are different\n        # areas = areas[None].expand(num_points, num_gts)\n        areas = areas[None].repeat(num_points, 1)\n        regress_ranges = regress_ranges[:, None, :].expand(\n            num_points, num_gts, 2)\n        gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 4)\n        xs, ys = points[:, 0], points[:, 1]\n        xs = xs[:, None].expand(num_points, num_gts)\n        ys = ys[:, None].expand(num_points, num_gts)\n\n        left = xs - gt_bboxes[..., 0]\n        right = gt_bboxes[..., 2] - xs\n        top = ys - gt_bboxes[..., 1]\n        bottom = gt_bboxes[..., 3] - ys\n        bbox_targets = torch.stack((left, top, right, bottom), -1)\n\n        if self.center_sampling:\n            # condition1: inside a `center bbox`\n            radius = self.center_sample_radius\n            # if gt_mask not None, use gt mask's centroid to determine\n            # the center region rather than gt_bbox center\n            if gt_masks is None:\n                center_xs = (gt_bboxes[..., 0] + gt_bboxes[..., 2]) / 2\n                center_ys = (gt_bboxes[..., 1] + gt_bboxes[..., 3]) / 2\n            else:\n                h, w = gt_masks.height, gt_masks.width\n                masks = gt_masks.to_tensor(\n                    dtype=torch.bool, device=gt_bboxes.device)\n                yys = torch.arange(\n                    0, h, dtype=torch.float32, device=masks.device)\n                xxs = torch.arange(\n                    0, w, dtype=torch.float32, device=masks.device)\n                # m00/m10/m01 represent the moments of a contour\n                # centroid is computed by m00/m10 and m00/m01\n                m00 = masks.sum(dim=-1).sum(dim=-1).clamp(min=1e-6)\n                m10 = (masks * xxs).sum(dim=-1).sum(dim=-1)\n                m01 = (masks * yys[:, None]).sum(dim=-1).sum(dim=-1)\n                center_xs = m10 / m00\n                center_ys = m01 / m00\n\n                center_xs = center_xs[None].expand(num_points, num_gts)\n                center_ys = center_ys[None].expand(num_points, num_gts)\n            center_gts = torch.zeros_like(gt_bboxes)\n            stride = center_xs.new_zeros(center_xs.shape)\n\n            # project the points on current lvl back to the `original` sizes\n            lvl_begin = 0\n            for lvl_idx, num_points_lvl in enumerate(num_points_per_lvl):\n                lvl_end = lvl_begin + num_points_lvl\n                stride[lvl_begin:lvl_end] = self.strides[lvl_idx] * radius\n                lvl_begin = lvl_end\n\n            x_mins = center_xs - stride\n            y_mins = center_ys - stride\n            x_maxs = center_xs + stride\n            y_maxs = center_ys + stride\n            center_gts[..., 0] = torch.where(x_mins > gt_bboxes[..., 0],\n                                             x_mins, gt_bboxes[..., 0])\n            center_gts[..., 1] = torch.where(y_mins > gt_bboxes[..., 1],\n                                             y_mins, gt_bboxes[..., 1])\n            center_gts[..., 2] = torch.where(x_maxs > gt_bboxes[..., 2],\n                                             gt_bboxes[..., 2], x_maxs)\n            center_gts[..., 3] = torch.where(y_maxs > gt_bboxes[..., 3],\n                                             gt_bboxes[..., 3], y_maxs)\n\n            cb_dist_left = xs - center_gts[..., 0]\n            cb_dist_right = center_gts[..., 2] - xs\n            cb_dist_top = ys - center_gts[..., 1]\n            cb_dist_bottom = center_gts[..., 3] - ys\n            center_bbox = torch.stack(\n                (cb_dist_left, cb_dist_top, cb_dist_right, cb_dist_bottom), -1)\n            inside_gt_bbox_mask = center_bbox.min(-1)[0] > 0\n        else:\n            # condition1: inside a gt bbox\n            inside_gt_bbox_mask = bbox_targets.min(-1)[0] > 0\n\n        # condition2: limit the regression range for each location\n        max_regress_distance = bbox_targets.max(-1)[0]\n        inside_regress_range = (\n            (max_regress_distance >= regress_ranges[..., 0])\n            & (max_regress_distance <= regress_ranges[..., 1]))\n\n        # if there are still more than one objects for a location,\n        # we choose the one with minimal area\n        areas[inside_gt_bbox_mask == 0] = INF\n        areas[inside_regress_range == 0] = INF\n        min_area, min_area_inds = areas.min(dim=1)\n\n        labels = gt_labels[min_area_inds]\n        labels[min_area == INF] = self.num_classes  # set as BG\n        bbox_targets = bbox_targets[range(num_points), min_area_inds]\n\n        # return pos_inds & pos_gt_inds\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().reshape(-1)\n        pos_gt_inds = min_area_inds[labels < self.num_classes]\n        return labels, bbox_targets, pos_inds, pos_gt_inds\n\n    def get_positive_infos(self) -> InstanceList:\n        \"\"\"Get positive information from sampling results.\n\n        Returns:\n            list[:obj:`InstanceData`]: Positive information of each image,\n            usually including positive bboxes, positive labels, positive\n            priors, etc.\n        \"\"\"\n        assert len(self._raw_positive_infos) > 0\n\n        pos_gt_inds_list = self._raw_positive_infos['pos_gt_inds_list']\n        pos_inds_list = self._raw_positive_infos['pos_inds_list']\n        num_imgs = len(pos_gt_inds_list)\n\n        cls_score_list = []\n        centerness_list = []\n        param_pred_list = []\n        point_list = []\n        stride_list = []\n        for cls_score_per_lvl, centerness_per_lvl, param_pred_per_lvl,\\\n            point_per_lvl, stride_per_lvl in \\\n            zip(self._raw_positive_infos['cls_scores'],\n                self._raw_positive_infos['centernesses'],\n                self._raw_positive_infos['param_preds'],\n                self._raw_positive_infos['all_level_points'],\n                self._raw_positive_infos['all_level_strides']):\n            cls_score_per_lvl = \\\n                cls_score_per_lvl.permute(\n                    0, 2, 3, 1).reshape(num_imgs, -1, self.num_classes)\n            centerness_per_lvl = \\\n                centerness_per_lvl.permute(\n                    0, 2, 3, 1).reshape(num_imgs, -1, 1)\n            param_pred_per_lvl = \\\n                param_pred_per_lvl.permute(\n                    0, 2, 3, 1).reshape(num_imgs, -1, self.num_params)\n            point_per_lvl = point_per_lvl.unsqueeze(0).repeat(num_imgs, 1, 1)\n            stride_per_lvl = stride_per_lvl.unsqueeze(0).repeat(num_imgs, 1)\n\n            cls_score_list.append(cls_score_per_lvl)\n            centerness_list.append(centerness_per_lvl)\n            param_pred_list.append(param_pred_per_lvl)\n            point_list.append(point_per_lvl)\n            stride_list.append(stride_per_lvl)\n        cls_scores = torch.cat(cls_score_list, dim=1)\n        centernesses = torch.cat(centerness_list, dim=1)\n        param_preds = torch.cat(param_pred_list, dim=1)\n        all_points = torch.cat(point_list, dim=1)\n        all_strides = torch.cat(stride_list, dim=1)\n\n        positive_infos = []\n        for i, (pos_gt_inds,\n                pos_inds) in enumerate(zip(pos_gt_inds_list, pos_inds_list)):\n            pos_info = InstanceData()\n            pos_info.points = all_points[i][pos_inds]\n            pos_info.strides = all_strides[i][pos_inds]\n            pos_info.scores = cls_scores[i][pos_inds]\n            pos_info.centernesses = centernesses[i][pos_inds]\n            pos_info.param_preds = param_preds[i][pos_inds]\n            pos_info.pos_assigned_gt_inds = pos_gt_inds\n            pos_info.pos_inds = pos_inds\n            positive_infos.append(pos_info)\n        return positive_infos\n\n    def predict_by_feat(self,\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        score_factors: Optional[List[Tensor]] = None,\n                        param_preds: Optional[List[Tensor]] = None,\n                        batch_img_metas: Optional[List[dict]] = None,\n                        cfg: Optional[ConfigDict] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Note: When score_factors is not None, the cls_scores are\n        usually multiplied by it then obtain the real score used in NMS,\n        such as CenterNess in FCOS, IoU branch in ATSS.\n\n        Args:\n            cls_scores (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            score_factors (list[Tensor], optional): Score factor for\n                all scale level, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 1, H, W). Defaults to None.\n            param_preds (list[Tensor], optional): Params for all scale\n                level, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_params, H, W)\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n                Defaults to None.\n            cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n\n        if score_factors is None:\n            # e.g. Retina, FreeAnchor, Foveabox, etc.\n            with_score_factors = False\n        else:\n            # e.g. FCOS, PAA, ATSS, AutoAssign, etc.\n            with_score_factors = True\n            assert len(cls_scores) == len(score_factors)\n\n        num_levels = len(cls_scores)\n\n        featmap_sizes = [cls_scores[i].shape[-2:] for i in range(num_levels)]\n        all_level_points_strides = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device,\n            with_stride=True)\n        all_level_points = [i[:, :2] for i in all_level_points_strides]\n        all_level_strides = [i[:, 2] for i in all_level_points_strides]\n\n        result_list = []\n\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            cls_score_list = select_single_mlvl(\n                cls_scores, img_id, detach=True)\n            bbox_pred_list = select_single_mlvl(\n                bbox_preds, img_id, detach=True)\n            if with_score_factors:\n                score_factor_list = select_single_mlvl(\n                    score_factors, img_id, detach=True)\n            else:\n                score_factor_list = [None for _ in range(num_levels)]\n            param_pred_list = select_single_mlvl(\n                param_preds, img_id, detach=True)\n\n            results = self._predict_by_feat_single(\n                cls_score_list=cls_score_list,\n                bbox_pred_list=bbox_pred_list,\n                score_factor_list=score_factor_list,\n                param_pred_list=param_pred_list,\n                mlvl_points=all_level_points,\n                mlvl_strides=all_level_strides,\n                img_meta=img_meta,\n                cfg=cfg,\n                rescale=rescale,\n                with_nms=with_nms)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                param_pred_list: List[Tensor],\n                                mlvl_points: List[Tensor],\n                                mlvl_strides: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            param_pred_list (List[Tensor]): Param predition from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_params, H, W).\n            mlvl_points (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid.\n                It has shape (num_priors, 2)\n            mlvl_strides (List[Tensor]):  Each element in the list is\n                the stride of a single level in feature pyramid.\n                It has shape (num_priors, 1)\n            img_meta (dict): Image meta info.\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        if score_factor_list[0] is None:\n            # e.g. Retina, FreeAnchor, etc.\n            with_score_factors = False\n        else:\n            # e.g. FCOS, PAA, ATSS, etc.\n            with_score_factors = True\n\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bbox_preds = []\n        mlvl_param_preds = []\n        mlvl_valid_points = []\n        mlvl_valid_strides = []\n        mlvl_scores = []\n        mlvl_labels = []\n        if with_score_factors:\n            mlvl_score_factors = []\n        else:\n            mlvl_score_factors = None\n        for level_idx, (cls_score, bbox_pred, score_factor,\n                        param_pred, points, strides) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list,\n                              score_factor_list, param_pred_list,\n                              mlvl_points, mlvl_strides)):\n\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            dim = self.bbox_coder.encode_size\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, dim)\n            if with_score_factors:\n                score_factor = score_factor.permute(1, 2,\n                                                    0).reshape(-1).sigmoid()\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            if self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                # remind that we set FG labels to [0, num_class-1]\n                # since mmdet v2.0\n                # BG cat_id: num_class\n                scores = cls_score.softmax(-1)[:, :-1]\n\n            param_pred = param_pred.permute(1, 2,\n                                            0).reshape(-1, self.num_params)\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            score_thr = cfg.get('score_thr', 0)\n\n            results = filter_scores_and_topk(\n                scores, score_thr, nms_pre,\n                dict(\n                    bbox_pred=bbox_pred,\n                    param_pred=param_pred,\n                    points=points,\n                    strides=strides))\n            scores, labels, keep_idxs, filtered_results = results\n\n            bbox_pred = filtered_results['bbox_pred']\n            param_pred = filtered_results['param_pred']\n            points = filtered_results['points']\n            strides = filtered_results['strides']\n\n            if with_score_factors:\n                score_factor = score_factor[keep_idxs]\n\n            mlvl_bbox_preds.append(bbox_pred)\n            mlvl_param_preds.append(param_pred)\n            mlvl_valid_points.append(points)\n            mlvl_valid_strides.append(strides)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n\n            if with_score_factors:\n                mlvl_score_factors.append(score_factor)\n\n        bbox_pred = torch.cat(mlvl_bbox_preds)\n        priors = cat_boxes(mlvl_valid_points)\n        bboxes = self.bbox_coder.decode(priors, bbox_pred, max_shape=img_shape)\n\n        results = InstanceData()\n        results.bboxes = bboxes\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n        results.param_preds = torch.cat(mlvl_param_preds)\n        results.points = torch.cat(mlvl_valid_points)\n        results.strides = torch.cat(mlvl_valid_strides)\n        if with_score_factors:\n            results.score_factors = torch.cat(mlvl_score_factors)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n\nclass MaskFeatModule(BaseModule):\n    \"\"\"CondInst mask feature map branch used in \\\n    https://arxiv.org/abs/1904.02689.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels of the mask feature\n             map branch.\n        start_level (int): The starting feature map level from RPN that\n             will be used to predict the mask feature map.\n        end_level (int): The ending feature map level from rpn that\n             will be used to predict the mask feature map.\n        out_channels (int): Number of output channels of the mask feature\n             map branch. This is the channel count of the mask\n             feature map that to be dynamically convolved with the predicted\n             kernel.\n        mask_stride (int): Downsample factor of the mask feature map output.\n            Defaults to 4.\n        num_stacked_convs (int): Number of convs in mask feature branch.\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Config dict for normalization layer. Default: None.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 feat_channels: int,\n                 start_level: int,\n                 end_level: int,\n                 out_channels: int,\n                 mask_stride: int = 4,\n                 num_stacked_convs: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 init_cfg: MultiConfig = [\n                     dict(type='Normal', layer='Conv2d', std=0.01)\n                 ],\n                 **kwargs) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.feat_channels = feat_channels\n        self.start_level = start_level\n        self.end_level = end_level\n        self.mask_stride = mask_stride\n        self.num_stacked_convs = num_stacked_convs\n        assert start_level >= 0 and end_level >= start_level\n        self.out_channels = out_channels\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.convs_all_levels = nn.ModuleList()\n        for i in range(self.start_level, self.end_level + 1):\n            convs_per_level = nn.Sequential()\n            convs_per_level.add_module(\n                f'conv{i}',\n                ConvModule(\n                    self.in_channels,\n                    self.feat_channels,\n                    3,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    inplace=False,\n                    bias=False))\n            self.convs_all_levels.append(convs_per_level)\n\n        conv_branch = []\n        for _ in range(self.num_stacked_convs):\n            conv_branch.append(\n                ConvModule(\n                    self.feat_channels,\n                    self.feat_channels,\n                    3,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    bias=False))\n        self.conv_branch = nn.Sequential(*conv_branch)\n\n        self.conv_pred = nn.Conv2d(\n            self.feat_channels, self.out_channels, 1, stride=1)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        super().init_weights()\n        kaiming_init(self.convs_all_levels, a=1, distribution='uniform')\n        kaiming_init(self.conv_branch, a=1, distribution='uniform')\n        kaiming_init(self.conv_pred, a=1, distribution='uniform')\n\n    def forward(self, x: Tuple[Tensor]) -> Tensor:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            Tensor: The predicted mask feature map.\n        \"\"\"\n        inputs = x[self.start_level:self.end_level + 1]\n        assert len(inputs) == (self.end_level - self.start_level + 1)\n        feature_add_all_level = self.convs_all_levels[0](inputs[0])\n        target_h, target_w = feature_add_all_level.size()[2:]\n        for i in range(1, len(inputs)):\n            input_p = inputs[i]\n            x_p = self.convs_all_levels[i](input_p)\n            h, w = x_p.size()[2:]\n            factor_h = target_h // h\n            factor_w = target_w // w\n            assert factor_h == factor_w\n            feature_per_level = aligned_bilinear(x_p, factor_h)\n            feature_add_all_level = feature_add_all_level + \\\n                feature_per_level\n\n        feature_add_all_level = self.conv_branch(feature_add_all_level)\n        feature_pred = self.conv_pred(feature_add_all_level)\n        return feature_pred\n\n\n@MODELS.register_module()\nclass CondInstMaskHead(BaseMaskHead):\n    \"\"\"CondInst mask head used in https://arxiv.org/abs/1904.02689.\n\n    This head outputs the mask for CondInst.\n\n    Args:\n        mask_feature_head (dict): Config of CondInstMaskFeatHead.\n        num_layers (int): Number of dynamic conv layers.\n        feat_channels (int): Number of channels in the dynamic conv.\n        mask_out_stride (int): The stride of the mask feat.\n        size_of_interest (int): The size of the region used in rel coord.\n        max_masks_to_train (int): Maximum number of masks to train for\n            each image.\n        loss_segm (:obj:`ConfigDict` or dict, optional): Config of\n            segmentation loss.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config\n            of head.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            head.\n    \"\"\"\n\n    def __init__(self,\n                 mask_feature_head: ConfigType,\n                 num_layers: int = 3,\n                 feat_channels: int = 8,\n                 mask_out_stride: int = 4,\n                 size_of_interest: int = 8,\n                 max_masks_to_train: int = -1,\n                 topk_masks_per_img: int = -1,\n                 loss_mask: ConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None) -> None:\n        super().__init__()\n        self.mask_feature_head = MaskFeatModule(**mask_feature_head)\n        self.mask_feat_stride = self.mask_feature_head.mask_stride\n        self.in_channels = self.mask_feature_head.out_channels\n        self.num_layers = num_layers\n        self.feat_channels = feat_channels\n        self.size_of_interest = size_of_interest\n        self.mask_out_stride = mask_out_stride\n        self.max_masks_to_train = max_masks_to_train\n        self.topk_masks_per_img = topk_masks_per_img\n        self.prior_generator = MlvlPointGenerator([self.mask_feat_stride])\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        self.loss_mask = MODELS.build(loss_mask)\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        weight_nums, bias_nums = [], []\n        for i in range(self.num_layers):\n            if i == 0:\n                weight_nums.append((self.in_channels + 2) * self.feat_channels)\n                bias_nums.append(self.feat_channels)\n            elif i == self.num_layers - 1:\n                weight_nums.append(self.feat_channels * 1)\n                bias_nums.append(1)\n            else:\n                weight_nums.append(self.feat_channels * self.feat_channels)\n                bias_nums.append(self.feat_channels)\n\n        self.weight_nums = weight_nums\n        self.bias_nums = bias_nums\n        self.num_params = sum(weight_nums) + sum(bias_nums)\n\n    def parse_dynamic_params(\n            self, params: Tensor) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"parse the dynamic params for dynamic conv.\"\"\"\n        num_insts = params.size(0)\n        params_splits = list(\n            torch.split_with_sizes(\n                params, self.weight_nums + self.bias_nums, dim=1))\n        weight_splits = params_splits[:self.num_layers]\n        bias_splits = params_splits[self.num_layers:]\n        for i in range(self.num_layers):\n            if i < self.num_layers - 1:\n                weight_splits[i] = weight_splits[i].reshape(\n                    num_insts * self.in_channels, -1, 1, 1)\n                bias_splits[i] = bias_splits[i].reshape(num_insts *\n                                                        self.in_channels)\n            else:\n                # out_channels x in_channels x 1 x 1\n                weight_splits[i] = weight_splits[i].reshape(\n                    num_insts * 1, -1, 1, 1)\n                bias_splits[i] = bias_splits[i].reshape(num_insts)\n\n        return weight_splits, bias_splits\n\n    def dynamic_conv_forward(self, features: Tensor, weights: List[Tensor],\n                             biases: List[Tensor], num_insts: int) -> Tensor:\n        \"\"\"dynamic forward, each layer follow a relu.\"\"\"\n        n_layers = len(weights)\n        x = features\n        for i, (w, b) in enumerate(zip(weights, biases)):\n            x = F.conv2d(x, w, bias=b, stride=1, padding=0, groups=num_insts)\n            if i < n_layers - 1:\n                x = F.relu(x)\n        return x\n\n    def forward(self, x: tuple, positive_infos: InstanceList) -> tuple:\n        \"\"\"Forward feature from the upstream network to get prototypes and\n        linearly combine the prototypes, using masks coefficients, into\n        instance masks. Finally, crop the instance masks with given bboxes.\n\n        Args:\n            x (Tuple[Tensor]): Feature from the upstream network, which is\n                a 4D-tensor.\n            positive_infos (List[:obj:``InstanceData``]): Positive information\n                that calculate from detect head.\n\n        Returns:\n            tuple: Predicted instance segmentation masks\n        \"\"\"\n        mask_feats = self.mask_feature_head(x)\n        return multi_apply(self.forward_single, mask_feats, positive_infos)\n\n    def forward_single(self, mask_feat: Tensor,\n                       positive_info: InstanceData) -> Tensor:\n        \"\"\"Forward features of a each image.\"\"\"\n        pos_param_preds = positive_info.get('param_preds')\n        pos_points = positive_info.get('points')\n        pos_strides = positive_info.get('strides')\n\n        num_inst = pos_param_preds.shape[0]\n        mask_feat = mask_feat[None].repeat(num_inst, 1, 1, 1)\n        _, _, H, W = mask_feat.size()\n        if num_inst == 0:\n            return (pos_param_preds.new_zeros((0, 1, H, W)), )\n\n        locations = self.prior_generator.single_level_grid_priors(\n            mask_feat.size()[2:], 0, device=mask_feat.device)\n\n        rel_coords = relative_coordinate_maps(locations, pos_points,\n                                              pos_strides,\n                                              self.size_of_interest,\n                                              mask_feat.size()[2:])\n        mask_head_inputs = torch.cat([rel_coords, mask_feat], dim=1)\n        mask_head_inputs = mask_head_inputs.reshape(1, -1, H, W)\n\n        weights, biases = self.parse_dynamic_params(pos_param_preds)\n        mask_preds = self.dynamic_conv_forward(mask_head_inputs, weights,\n                                               biases, num_inst)\n        mask_preds = mask_preds.reshape(-1, H, W)\n        mask_preds = aligned_bilinear(\n            mask_preds.unsqueeze(0),\n            int(self.mask_feat_stride / self.mask_out_stride)).squeeze(0)\n\n        return (mask_preds, )\n\n    def loss_by_feat(self, mask_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict], positive_infos: InstanceList,\n                     **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mask_preds (list[Tensor]): List of predicted masks, each has\n                shape (num_classes, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``masks``,\n                and ``labels`` attributes.\n            batch_img_metas (list[dict]): Meta information of multiple images.\n            positive_infos (List[:obj:``InstanceData``]): Information of\n                positive samples of each image that are assigned in detection\n                head.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert positive_infos is not None, \\\n            'positive_infos should not be None in `CondInstMaskHead`'\n        losses = dict()\n\n        loss_mask = 0.\n        num_imgs = len(mask_preds)\n        total_pos = 0\n\n        for idx in range(num_imgs):\n            (mask_pred, pos_mask_targets, num_pos) = \\\n                self._get_targets_single(\n                mask_preds[idx], batch_gt_instances[idx],\n                positive_infos[idx])\n            # mask loss\n            total_pos += num_pos\n            if num_pos == 0 or pos_mask_targets is None:\n                loss = mask_pred.new_zeros(1).mean()\n            else:\n                loss = self.loss_mask(\n                    mask_pred, pos_mask_targets,\n                    reduction_override='none').sum()\n            loss_mask += loss\n\n        if total_pos == 0:\n            total_pos += 1  # avoid nan\n        loss_mask = loss_mask / total_pos\n        losses.update(loss_mask=loss_mask)\n        return losses\n\n    def _get_targets_single(self, mask_preds: Tensor,\n                            gt_instances: InstanceData,\n                            positive_info: InstanceData):\n        \"\"\"Compute targets for predictions of single image.\n\n        Args:\n            mask_preds (Tensor): Predicted prototypes with shape\n                (num_classes, H, W).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes``, ``labels``,\n                and ``masks`` attributes.\n            positive_info (:obj:`InstanceData`): Information of positive\n                samples that are assigned in detection head. It usually\n                contains following keys.\n\n                    - pos_assigned_gt_inds (Tensor): Assigner GT indexes of\n                      positive proposals, has shape (num_pos, )\n                    - pos_inds (Tensor): Positive index of image, has\n                      shape (num_pos, ).\n                    - param_pred (Tensor): Positive param preditions\n                      with shape (num_pos, num_params).\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n\n            - mask_preds (Tensor): Positive predicted mask with shape\n              (num_pos, mask_h, mask_w).\n            - pos_mask_targets (Tensor): Positive mask targets with shape\n              (num_pos, mask_h, mask_w).\n            - num_pos (int): Positive numbers.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        device = gt_bboxes.device\n        gt_masks = gt_instances.masks.to_tensor(\n            dtype=torch.bool, device=device).float()\n\n        # process with mask targets\n        pos_assigned_gt_inds = positive_info.get('pos_assigned_gt_inds')\n        scores = positive_info.get('scores')\n        centernesses = positive_info.get('centernesses')\n        num_pos = pos_assigned_gt_inds.size(0)\n\n        if gt_masks.size(0) == 0 or num_pos == 0:\n            return mask_preds, None, 0\n        # Since we're producing (near) full image masks,\n        # it'd take too much vram to backprop on every single mask.\n        # Thus we select only a subset.\n        if (self.max_masks_to_train != -1) and \\\n           (num_pos > self.max_masks_to_train):\n            perm = torch.randperm(num_pos)\n            select = perm[:self.max_masks_to_train]\n            mask_preds = mask_preds[select]\n            pos_assigned_gt_inds = pos_assigned_gt_inds[select]\n            num_pos = self.max_masks_to_train\n        elif self.topk_masks_per_img != -1:\n            unique_gt_inds = pos_assigned_gt_inds.unique()\n            num_inst_per_gt = max(\n                int(self.topk_masks_per_img / len(unique_gt_inds)), 1)\n\n            keep_mask_preds = []\n            keep_pos_assigned_gt_inds = []\n            for gt_ind in unique_gt_inds:\n                per_inst_pos_inds = (pos_assigned_gt_inds == gt_ind)\n                mask_preds_per_inst = mask_preds[per_inst_pos_inds]\n                gt_inds_per_inst = pos_assigned_gt_inds[per_inst_pos_inds]\n                if sum(per_inst_pos_inds) > num_inst_per_gt:\n                    per_inst_scores = scores[per_inst_pos_inds].sigmoid().max(\n                        dim=1)[0]\n                    per_inst_centerness = centernesses[\n                        per_inst_pos_inds].sigmoid().reshape(-1, )\n                    select = (per_inst_scores * per_inst_centerness).topk(\n                        k=num_inst_per_gt, dim=0)[1]\n                    mask_preds_per_inst = mask_preds_per_inst[select]\n                    gt_inds_per_inst = gt_inds_per_inst[select]\n                keep_mask_preds.append(mask_preds_per_inst)\n                keep_pos_assigned_gt_inds.append(gt_inds_per_inst)\n            mask_preds = torch.cat(keep_mask_preds)\n            pos_assigned_gt_inds = torch.cat(keep_pos_assigned_gt_inds)\n            num_pos = pos_assigned_gt_inds.size(0)\n\n        # Follow the origin implement\n        start = int(self.mask_out_stride // 2)\n        gt_masks = gt_masks[:, start::self.mask_out_stride,\n                            start::self.mask_out_stride]\n        gt_masks = gt_masks.gt(0.5).float()\n        pos_mask_targets = gt_masks[pos_assigned_gt_inds]\n\n        return (mask_preds, pos_mask_targets, num_pos)\n\n    def predict_by_feat(self,\n                        mask_preds: List[Tensor],\n                        results_list: InstanceList,\n                        batch_img_metas: List[dict],\n                        rescale: bool = True,\n                        **kwargs) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        mask results.\n\n        Args:\n            mask_preds (list[Tensor]): Predicted prototypes with shape\n                (num_classes, H, W).\n            results_list (List[:obj:``InstanceData``]): BBoxHead results.\n            batch_img_metas (list[dict]): Meta information of all images.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Processed results of multiple\n            images.Each :obj:`InstanceData` usually contains\n            following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n        assert len(mask_preds) == len(results_list) == len(batch_img_metas)\n\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            results = results_list[img_id]\n            bboxes = results.bboxes\n            mask_pred = mask_preds[img_id]\n            if bboxes.shape[0] == 0 or mask_pred.shape[0] == 0:\n                results_list[img_id] = empty_instances(\n                    [img_meta],\n                    bboxes.device,\n                    task_type='mask',\n                    instance_results=[results])[0]\n            else:\n                im_mask = self._predict_by_feat_single(\n                    mask_preds=mask_pred,\n                    bboxes=bboxes,\n                    img_meta=img_meta,\n                    rescale=rescale)\n                results.masks = im_mask\n        return results_list\n\n    def _predict_by_feat_single(self,\n                                mask_preds: Tensor,\n                                bboxes: Tensor,\n                                img_meta: dict,\n                                rescale: bool,\n                                cfg: OptConfigType = None):\n        \"\"\"Transform a single image's features extracted from the head into\n        mask results.\n\n        Args:\n            mask_preds (Tensor): Predicted prototypes, has shape [H, W, N].\n            img_meta (dict): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            rescale (bool): If rescale is False, then returned masks will\n                fit the scale of imgs[0].\n            cfg (dict, optional): Config used in test phase.\n                Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Processed results of single image.\n             it usually contains following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        scale_factor = bboxes.new_tensor(img_meta['scale_factor']).repeat(\n            (1, 2))\n        img_h, img_w = img_meta['img_shape'][:2]\n        ori_h, ori_w = img_meta['ori_shape'][:2]\n\n        mask_preds = mask_preds.sigmoid().unsqueeze(0)\n        mask_preds = aligned_bilinear(mask_preds, self.mask_out_stride)\n        mask_preds = mask_preds[:, :, :img_h, :img_w]\n        if rescale:  # in-placed rescale the bboxes\n            scale_factor = bboxes.new_tensor(img_meta['scale_factor']).repeat(\n                (1, 2))\n            bboxes /= scale_factor\n\n            masks = F.interpolate(\n                mask_preds, (ori_h, ori_w),\n                mode='bilinear',\n                align_corners=False).squeeze(0) > cfg.mask_thr\n        else:\n            masks = mask_preds.squeeze(0) > cfg.mask_thr\n\n        return masks\n"
  },
  {
    "path": "mmdet/models/dense_heads/conditional_detr_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.model import bias_init_with_prob\nfrom torch import Tensor\n\nfrom mmdet.models.layers.transformer import inverse_sigmoid\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import InstanceList\nfrom .detr_head import DETRHead\n\n\n@MODELS.register_module()\nclass ConditionalDETRHead(DETRHead):\n    \"\"\"Head of Conditional DETR. Conditional DETR: Conditional DETR for Fast\n    Training Convergence. More details can be found in the `paper.\n\n    <https://arxiv.org/abs/2108.06152>`_ .\n    \"\"\"\n\n    def init_weights(self):\n        \"\"\"Initialize weights of the transformer head.\"\"\"\n        super().init_weights()\n        # The initialization below for transformer head is very\n        # important as we use Focal_loss for loss_cls\n        if self.loss_cls.use_sigmoid:\n            bias_init = bias_init_with_prob(0.01)\n            nn.init.constant_(self.fc_cls.bias, bias_init)\n\n    def forward(self, hidden_states: Tensor,\n                references: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"\"Forward function.\n\n        Args:\n            hidden_states (Tensor): Features from transformer decoder. If\n                `return_intermediate_dec` is True output has shape\n                (num_decoder_layers, bs, num_queries, dim), else has shape (1,\n                bs, num_queries, dim) which only contains the last layer\n                outputs.\n            references (Tensor): References from transformer decoder, has\n                shape (bs, num_queries, 2).\n        Returns:\n            tuple[Tensor]: results of head containing the following tensor.\n\n            - layers_cls_scores (Tensor): Outputs from the classification head,\n              shape (num_decoder_layers, bs, num_queries, cls_out_channels).\n              Note cls_out_channels should include background.\n            - layers_bbox_preds (Tensor): Sigmoid outputs from the regression\n              head with normalized coordinate format (cx, cy, w, h), has shape\n              (num_decoder_layers, bs, num_queries, 4).\n        \"\"\"\n\n        references_unsigmoid = inverse_sigmoid(references)\n        layers_bbox_preds = []\n        for layer_id in range(hidden_states.shape[0]):\n            tmp_reg_preds = self.fc_reg(\n                self.activate(self.reg_ffn(hidden_states[layer_id])))\n            tmp_reg_preds[..., :2] += references_unsigmoid\n            outputs_coord = tmp_reg_preds.sigmoid()\n            layers_bbox_preds.append(outputs_coord)\n        layers_bbox_preds = torch.stack(layers_bbox_preds)\n\n        layers_cls_scores = self.fc_cls(hidden_states)\n        return layers_cls_scores, layers_bbox_preds\n\n    def loss(self, hidden_states: Tensor, references: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the features of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Features from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, dim).\n            references (Tensor): References from the transformer decoder, has\n               shape (num_decoder_layers, bs, num_queries, 2).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states, references)\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(*loss_inputs)\n        return losses\n\n    def loss_and_predict(\n            self, hidden_states: Tensor, references: Tensor,\n            batch_data_samples: SampleList) -> Tuple[dict, InstanceList]:\n        \"\"\"Perform forward propagation of the head, then calculate loss and\n        predictions from the features and data samples. Over-write because\n        img_metas are needed as inputs for bbox_head.\n\n        Args:\n            hidden_states (Tensor): Features from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, dim).\n            references (Tensor): References from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, 2).\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n\n        Returns:\n            tuple: The return value is a tuple contains:\n\n            - losses: (dict[str, Tensor]): A dictionary of loss components.\n            - predictions (list[:obj:`InstanceData`]): Detection\n              results of each image after the post process.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states, references)\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(*loss_inputs)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas)\n        return losses, predictions\n\n    def predict(self,\n                hidden_states: Tensor,\n                references: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network. Over-write\n        because img_metas are needed as inputs for bbox_head.\n\n        Args:\n            hidden_states (Tensor): Features from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, dim).\n            references (Tensor): References from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, 2).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        last_layer_hidden_state = hidden_states[-1].unsqueeze(0)\n        outs = self(last_layer_hidden_state, references)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, rescale=rescale)\n\n        return predictions\n"
  },
  {
    "path": "mmdet/models/dense_heads/corner_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom logging import warning\nfrom math import ceil, log\nfrom typing import List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops import CornerPool, batched_nms\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule, bias_init_with_prob\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, OptMultiConfig)\nfrom ..utils import (gather_feat, gaussian_radius, gen_gaussian_target,\n                     get_local_maximum, get_topk_from_heatmap, multi_apply,\n                     transpose_and_gather_feat)\nfrom .base_dense_head import BaseDenseHead\n\n\nclass BiCornerPool(BaseModule):\n    \"\"\"Bidirectional Corner Pooling Module (TopLeft, BottomRight, etc.)\n\n    Args:\n        in_channels (int): Input channels of module.\n        directions (list[str]): Directions of two CornerPools.\n        out_channels (int): Output channels of module.\n        feat_channels (int): Feature channels of module.\n        norm_cfg (:obj:`ConfigDict` or dict): Dictionary to construct\n            and config norm layer.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to\n            control the initialization.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 directions: List[int],\n                 feat_channels: int = 128,\n                 out_channels: int = 128,\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg)\n        self.direction1_conv = ConvModule(\n            in_channels, feat_channels, 3, padding=1, norm_cfg=norm_cfg)\n        self.direction2_conv = ConvModule(\n            in_channels, feat_channels, 3, padding=1, norm_cfg=norm_cfg)\n\n        self.aftpool_conv = ConvModule(\n            feat_channels,\n            out_channels,\n            3,\n            padding=1,\n            norm_cfg=norm_cfg,\n            act_cfg=None)\n\n        self.conv1 = ConvModule(\n            in_channels, out_channels, 1, norm_cfg=norm_cfg, act_cfg=None)\n        self.conv2 = ConvModule(\n            in_channels, out_channels, 3, padding=1, norm_cfg=norm_cfg)\n\n        self.direction1_pool = CornerPool(directions[0])\n        self.direction2_pool = CornerPool(directions[1])\n        self.relu = nn.ReLU(inplace=True)\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tensor): Input feature of BiCornerPool.\n\n        Returns:\n            conv2 (tensor): Output feature of BiCornerPool.\n        \"\"\"\n        direction1_conv = self.direction1_conv(x)\n        direction2_conv = self.direction2_conv(x)\n        direction1_feat = self.direction1_pool(direction1_conv)\n        direction2_feat = self.direction2_pool(direction2_conv)\n        aftpool_conv = self.aftpool_conv(direction1_feat + direction2_feat)\n        conv1 = self.conv1(x)\n        relu = self.relu(aftpool_conv + conv1)\n        conv2 = self.conv2(relu)\n        return conv2\n\n\n@MODELS.register_module()\nclass CornerHead(BaseDenseHead):\n    \"\"\"Head of CornerNet: Detecting Objects as Paired Keypoints.\n\n    Code is modified from the `official github repo\n    <https://github.com/princeton-vl/CornerNet/blob/master/models/py_utils/\n    kp.py#L73>`_ .\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/1808.01244>`_ .\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        num_feat_levels (int): Levels of feature from the previous module.\n            2 for HourglassNet-104 and 1 for HourglassNet-52. Because\n            HourglassNet-104 outputs the final feature and intermediate\n            supervision feature and HourglassNet-52 only outputs the final\n            feature. Defaults to 2.\n        corner_emb_channels (int): Channel of embedding vector. Defaults to 1.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config.\n            Useless in CornerHead, but we keep this variable for\n            SingleStageDetector.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            CornerHead.\n        loss_heatmap (:obj:`ConfigDict` or dict): Config of corner heatmap\n            loss. Defaults to GaussianFocalLoss.\n        loss_embedding (:obj:`ConfigDict` or dict): Config of corner embedding\n            loss. Defaults to AssociativeEmbeddingLoss.\n        loss_offset (:obj:`ConfigDict` or dict): Config of corner offset loss.\n            Defaults to SmoothL1Loss.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 num_feat_levels: int = 2,\n                 corner_emb_channels: int = 1,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 loss_heatmap: ConfigType = dict(\n                     type='GaussianFocalLoss',\n                     alpha=2.0,\n                     gamma=4.0,\n                     loss_weight=1),\n                 loss_embedding: ConfigType = dict(\n                     type='AssociativeEmbeddingLoss',\n                     pull_weight=0.25,\n                     push_weight=0.25),\n                 loss_offset: ConfigType = dict(\n                     type='SmoothL1Loss', beta=1.0, loss_weight=1),\n                 init_cfg: OptMultiConfig = None) -> None:\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super().__init__(init_cfg=init_cfg)\n        self.num_classes = num_classes\n        self.in_channels = in_channels\n        self.corner_emb_channels = corner_emb_channels\n        self.with_corner_emb = self.corner_emb_channels > 0\n        self.corner_offset_channels = 2\n        self.num_feat_levels = num_feat_levels\n        self.loss_heatmap = MODELS.build(\n            loss_heatmap) if loss_heatmap is not None else None\n        self.loss_embedding = MODELS.build(\n            loss_embedding) if loss_embedding is not None else None\n        self.loss_offset = MODELS.build(\n            loss_offset) if loss_offset is not None else None\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n        self._init_layers()\n\n    def _make_layers(self,\n                     out_channels: int,\n                     in_channels: int = 256,\n                     feat_channels: int = 256) -> nn.Sequential:\n        \"\"\"Initialize conv sequential for CornerHead.\"\"\"\n        return nn.Sequential(\n            ConvModule(in_channels, feat_channels, 3, padding=1),\n            ConvModule(\n                feat_channels, out_channels, 1, norm_cfg=None, act_cfg=None))\n\n    def _init_corner_kpt_layers(self) -> None:\n        \"\"\"Initialize corner keypoint layers.\n\n        Including corner heatmap branch and corner offset branch. Each branch\n        has two parts: prefix `tl_` for top-left and `br_` for bottom-right.\n        \"\"\"\n        self.tl_pool, self.br_pool = nn.ModuleList(), nn.ModuleList()\n        self.tl_heat, self.br_heat = nn.ModuleList(), nn.ModuleList()\n        self.tl_off, self.br_off = nn.ModuleList(), nn.ModuleList()\n\n        for _ in range(self.num_feat_levels):\n            self.tl_pool.append(\n                BiCornerPool(\n                    self.in_channels, ['top', 'left'],\n                    out_channels=self.in_channels))\n            self.br_pool.append(\n                BiCornerPool(\n                    self.in_channels, ['bottom', 'right'],\n                    out_channels=self.in_channels))\n\n            self.tl_heat.append(\n                self._make_layers(\n                    out_channels=self.num_classes,\n                    in_channels=self.in_channels))\n            self.br_heat.append(\n                self._make_layers(\n                    out_channels=self.num_classes,\n                    in_channels=self.in_channels))\n\n            self.tl_off.append(\n                self._make_layers(\n                    out_channels=self.corner_offset_channels,\n                    in_channels=self.in_channels))\n            self.br_off.append(\n                self._make_layers(\n                    out_channels=self.corner_offset_channels,\n                    in_channels=self.in_channels))\n\n    def _init_corner_emb_layers(self) -> None:\n        \"\"\"Initialize corner embedding layers.\n\n        Only include corner embedding branch with two parts: prefix `tl_` for\n        top-left and `br_` for bottom-right.\n        \"\"\"\n        self.tl_emb, self.br_emb = nn.ModuleList(), nn.ModuleList()\n\n        for _ in range(self.num_feat_levels):\n            self.tl_emb.append(\n                self._make_layers(\n                    out_channels=self.corner_emb_channels,\n                    in_channels=self.in_channels))\n            self.br_emb.append(\n                self._make_layers(\n                    out_channels=self.corner_emb_channels,\n                    in_channels=self.in_channels))\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers for CornerHead.\n\n        Including two parts: corner keypoint layers and corner embedding layers\n        \"\"\"\n        self._init_corner_kpt_layers()\n        if self.with_corner_emb:\n            self._init_corner_emb_layers()\n\n    def init_weights(self) -> None:\n        super().init_weights()\n        bias_init = bias_init_with_prob(0.1)\n        for i in range(self.num_feat_levels):\n            # The initialization of parameters are different between\n            # nn.Conv2d and ConvModule. Our experiments show that\n            # using the original initialization of nn.Conv2d increases\n            # the final mAP by about 0.2%\n            self.tl_heat[i][-1].conv.reset_parameters()\n            self.tl_heat[i][-1].conv.bias.data.fill_(bias_init)\n            self.br_heat[i][-1].conv.reset_parameters()\n            self.br_heat[i][-1].conv.bias.data.fill_(bias_init)\n            self.tl_off[i][-1].conv.reset_parameters()\n            self.br_off[i][-1].conv.reset_parameters()\n            if self.with_corner_emb:\n                self.tl_emb[i][-1].conv.reset_parameters()\n                self.br_emb[i][-1].conv.reset_parameters()\n\n    def forward(self, feats: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of corner heatmaps, offset heatmaps and\n            embedding heatmaps.\n                - tl_heats (list[Tensor]): Top-left corner heatmaps for all\n                  levels, each is a 4D-tensor, the channels number is\n                  num_classes.\n                - br_heats (list[Tensor]): Bottom-right corner heatmaps for all\n                  levels, each is a 4D-tensor, the channels number is\n                  num_classes.\n                - tl_embs (list[Tensor] | list[None]): Top-left embedding\n                  heatmaps for all levels, each is a 4D-tensor or None.\n                  If not None, the channels number is corner_emb_channels.\n                - br_embs (list[Tensor] | list[None]): Bottom-right embedding\n                  heatmaps for all levels, each is a 4D-tensor or None.\n                  If not None, the channels number is corner_emb_channels.\n                - tl_offs (list[Tensor]): Top-left offset heatmaps for all\n                  levels, each is a 4D-tensor. The channels number is\n                  corner_offset_channels.\n                - br_offs (list[Tensor]): Bottom-right offset heatmaps for all\n                  levels, each is a 4D-tensor. The channels number is\n                  corner_offset_channels.\n        \"\"\"\n        lvl_ind = list(range(self.num_feat_levels))\n        return multi_apply(self.forward_single, feats, lvl_ind)\n\n    def forward_single(self,\n                       x: Tensor,\n                       lvl_ind: int,\n                       return_pool: bool = False) -> List[Tensor]:\n        \"\"\"Forward feature of a single level.\n\n        Args:\n            x (Tensor): Feature of a single level.\n            lvl_ind (int): Level index of current feature.\n            return_pool (bool): Return corner pool feature or not.\n                Defaults to False.\n\n        Returns:\n            tuple[Tensor]: A tuple of CornerHead's output for current feature\n            level. Containing the following Tensors:\n\n                - tl_heat (Tensor): Predicted top-left corner heatmap.\n                - br_heat (Tensor): Predicted bottom-right corner heatmap.\n                - tl_emb (Tensor | None): Predicted top-left embedding heatmap.\n                  None for `self.with_corner_emb == False`.\n                - br_emb (Tensor | None): Predicted bottom-right embedding\n                  heatmap. None for `self.with_corner_emb == False`.\n                - tl_off (Tensor): Predicted top-left offset heatmap.\n                - br_off (Tensor): Predicted bottom-right offset heatmap.\n                - tl_pool (Tensor): Top-left corner pool feature. Not must\n                  have.\n                - br_pool (Tensor): Bottom-right corner pool feature. Not must\n                  have.\n        \"\"\"\n        tl_pool = self.tl_pool[lvl_ind](x)\n        tl_heat = self.tl_heat[lvl_ind](tl_pool)\n        br_pool = self.br_pool[lvl_ind](x)\n        br_heat = self.br_heat[lvl_ind](br_pool)\n\n        tl_emb, br_emb = None, None\n        if self.with_corner_emb:\n            tl_emb = self.tl_emb[lvl_ind](tl_pool)\n            br_emb = self.br_emb[lvl_ind](br_pool)\n\n        tl_off = self.tl_off[lvl_ind](tl_pool)\n        br_off = self.br_off[lvl_ind](br_pool)\n\n        result_list = [tl_heat, br_heat, tl_emb, br_emb, tl_off, br_off]\n        if return_pool:\n            result_list.append(tl_pool)\n            result_list.append(br_pool)\n\n        return result_list\n\n    def get_targets(self,\n                    gt_bboxes: List[Tensor],\n                    gt_labels: List[Tensor],\n                    feat_shape: Sequence[int],\n                    img_shape: Sequence[int],\n                    with_corner_emb: bool = False,\n                    with_guiding_shift: bool = False,\n                    with_centripetal_shift: bool = False) -> dict:\n        \"\"\"Generate corner targets.\n\n        Including corner heatmap, corner offset.\n\n        Optional: corner embedding, corner guiding shift, centripetal shift.\n\n        For CornerNet, we generate corner heatmap, corner offset and corner\n        embedding from this function.\n\n        For CentripetalNet, we generate corner heatmap, corner offset, guiding\n        shift and centripetal shift from this function.\n\n        Args:\n            gt_bboxes (list[Tensor]): Ground truth bboxes of each image, each\n                has shape (num_gt, 4).\n            gt_labels (list[Tensor]): Ground truth labels of each box, each has\n                shape (num_gt, ).\n            feat_shape (Sequence[int]): Shape of output feature,\n                [batch, channel, height, width].\n            img_shape (Sequence[int]): Shape of input image,\n                [height, width, channel].\n            with_corner_emb (bool): Generate corner embedding target or not.\n                Defaults to False.\n            with_guiding_shift (bool): Generate guiding shift target or not.\n                Defaults to False.\n            with_centripetal_shift (bool): Generate centripetal shift target or\n                not. Defaults to False.\n\n        Returns:\n            dict: Ground truth of corner heatmap, corner offset, corner\n            embedding, guiding shift and centripetal shift. Containing the\n            following keys:\n\n                - topleft_heatmap (Tensor): Ground truth top-left corner\n                  heatmap.\n                - bottomright_heatmap (Tensor): Ground truth bottom-right\n                  corner heatmap.\n                - topleft_offset (Tensor): Ground truth top-left corner offset.\n                - bottomright_offset (Tensor): Ground truth bottom-right corner\n                  offset.\n                - corner_embedding (list[list[list[int]]]): Ground truth corner\n                  embedding. Not must have.\n                - topleft_guiding_shift (Tensor): Ground truth top-left corner\n                  guiding shift. Not must have.\n                - bottomright_guiding_shift (Tensor): Ground truth bottom-right\n                  corner guiding shift. Not must have.\n                - topleft_centripetal_shift (Tensor): Ground truth top-left\n                  corner centripetal shift. Not must have.\n                - bottomright_centripetal_shift (Tensor): Ground truth\n                  bottom-right corner centripetal shift. Not must have.\n        \"\"\"\n        batch_size, _, height, width = feat_shape\n        img_h, img_w = img_shape[:2]\n\n        width_ratio = float(width / img_w)\n        height_ratio = float(height / img_h)\n\n        gt_tl_heatmap = gt_bboxes[-1].new_zeros(\n            [batch_size, self.num_classes, height, width])\n        gt_br_heatmap = gt_bboxes[-1].new_zeros(\n            [batch_size, self.num_classes, height, width])\n        gt_tl_offset = gt_bboxes[-1].new_zeros([batch_size, 2, height, width])\n        gt_br_offset = gt_bboxes[-1].new_zeros([batch_size, 2, height, width])\n\n        if with_corner_emb:\n            match = []\n\n        # Guiding shift is a kind of offset, from center to corner\n        if with_guiding_shift:\n            gt_tl_guiding_shift = gt_bboxes[-1].new_zeros(\n                [batch_size, 2, height, width])\n            gt_br_guiding_shift = gt_bboxes[-1].new_zeros(\n                [batch_size, 2, height, width])\n        # Centripetal shift is also a kind of offset, from center to corner\n        # and normalized by log.\n        if with_centripetal_shift:\n            gt_tl_centripetal_shift = gt_bboxes[-1].new_zeros(\n                [batch_size, 2, height, width])\n            gt_br_centripetal_shift = gt_bboxes[-1].new_zeros(\n                [batch_size, 2, height, width])\n\n        for batch_id in range(batch_size):\n            # Ground truth of corner embedding per image is a list of coord set\n            corner_match = []\n            for box_id in range(len(gt_labels[batch_id])):\n                left, top, right, bottom = gt_bboxes[batch_id][box_id]\n                center_x = (left + right) / 2.0\n                center_y = (top + bottom) / 2.0\n                label = gt_labels[batch_id][box_id]\n\n                # Use coords in the feature level to generate ground truth\n                scale_left = left * width_ratio\n                scale_right = right * width_ratio\n                scale_top = top * height_ratio\n                scale_bottom = bottom * height_ratio\n                scale_center_x = center_x * width_ratio\n                scale_center_y = center_y * height_ratio\n\n                # Int coords on feature map/ground truth tensor\n                left_idx = int(min(scale_left, width - 1))\n                right_idx = int(min(scale_right, width - 1))\n                top_idx = int(min(scale_top, height - 1))\n                bottom_idx = int(min(scale_bottom, height - 1))\n\n                # Generate gaussian heatmap\n                scale_box_width = ceil(scale_right - scale_left)\n                scale_box_height = ceil(scale_bottom - scale_top)\n                radius = gaussian_radius((scale_box_height, scale_box_width),\n                                         min_overlap=0.3)\n                radius = max(0, int(radius))\n                gt_tl_heatmap[batch_id, label] = gen_gaussian_target(\n                    gt_tl_heatmap[batch_id, label], [left_idx, top_idx],\n                    radius)\n                gt_br_heatmap[batch_id, label] = gen_gaussian_target(\n                    gt_br_heatmap[batch_id, label], [right_idx, bottom_idx],\n                    radius)\n\n                # Generate corner offset\n                left_offset = scale_left - left_idx\n                top_offset = scale_top - top_idx\n                right_offset = scale_right - right_idx\n                bottom_offset = scale_bottom - bottom_idx\n                gt_tl_offset[batch_id, 0, top_idx, left_idx] = left_offset\n                gt_tl_offset[batch_id, 1, top_idx, left_idx] = top_offset\n                gt_br_offset[batch_id, 0, bottom_idx, right_idx] = right_offset\n                gt_br_offset[batch_id, 1, bottom_idx,\n                             right_idx] = bottom_offset\n\n                # Generate corner embedding\n                if with_corner_emb:\n                    corner_match.append([[top_idx, left_idx],\n                                         [bottom_idx, right_idx]])\n                # Generate guiding shift\n                if with_guiding_shift:\n                    gt_tl_guiding_shift[batch_id, 0, top_idx,\n                                        left_idx] = scale_center_x - left_idx\n                    gt_tl_guiding_shift[batch_id, 1, top_idx,\n                                        left_idx] = scale_center_y - top_idx\n                    gt_br_guiding_shift[batch_id, 0, bottom_idx,\n                                        right_idx] = right_idx - scale_center_x\n                    gt_br_guiding_shift[\n                        batch_id, 1, bottom_idx,\n                        right_idx] = bottom_idx - scale_center_y\n                # Generate centripetal shift\n                if with_centripetal_shift:\n                    gt_tl_centripetal_shift[batch_id, 0, top_idx,\n                                            left_idx] = log(scale_center_x -\n                                                            scale_left)\n                    gt_tl_centripetal_shift[batch_id, 1, top_idx,\n                                            left_idx] = log(scale_center_y -\n                                                            scale_top)\n                    gt_br_centripetal_shift[batch_id, 0, bottom_idx,\n                                            right_idx] = log(scale_right -\n                                                             scale_center_x)\n                    gt_br_centripetal_shift[batch_id, 1, bottom_idx,\n                                            right_idx] = log(scale_bottom -\n                                                             scale_center_y)\n\n            if with_corner_emb:\n                match.append(corner_match)\n\n        target_result = dict(\n            topleft_heatmap=gt_tl_heatmap,\n            topleft_offset=gt_tl_offset,\n            bottomright_heatmap=gt_br_heatmap,\n            bottomright_offset=gt_br_offset)\n\n        if with_corner_emb:\n            target_result.update(corner_embedding=match)\n        if with_guiding_shift:\n            target_result.update(\n                topleft_guiding_shift=gt_tl_guiding_shift,\n                bottomright_guiding_shift=gt_br_guiding_shift)\n        if with_centripetal_shift:\n            target_result.update(\n                topleft_centripetal_shift=gt_tl_centripetal_shift,\n                bottomright_centripetal_shift=gt_br_centripetal_shift)\n\n        return target_result\n\n    def loss_by_feat(\n            self,\n            tl_heats: List[Tensor],\n            br_heats: List[Tensor],\n            tl_embs: List[Tensor],\n            br_embs: List[Tensor],\n            tl_offs: List[Tensor],\n            br_offs: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            tl_heats (list[Tensor]): Top-left corner heatmaps for each level\n                with shape (N, num_classes, H, W).\n            br_heats (list[Tensor]): Bottom-right corner heatmaps for each\n                level with shape (N, num_classes, H, W).\n            tl_embs (list[Tensor]): Top-left corner embeddings for each level\n                with shape (N, corner_emb_channels, H, W).\n            br_embs (list[Tensor]): Bottom-right corner embeddings for each\n                level with shape (N, corner_emb_channels, H, W).\n            tl_offs (list[Tensor]): Top-left corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            br_offs (list[Tensor]): Bottom-right corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Specify which bounding boxes can be ignored when computing\n                the loss.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components. Containing the\n            following losses:\n\n                - det_loss (list[Tensor]): Corner keypoint losses of all\n                  feature levels.\n                - pull_loss (list[Tensor]): Part one of AssociativeEmbedding\n                  losses of all feature levels.\n                - push_loss (list[Tensor]): Part two of AssociativeEmbedding\n                  losses of all feature levels.\n                - off_loss (list[Tensor]): Corner offset losses of all feature\n                  levels.\n        \"\"\"\n        gt_bboxes = [\n            gt_instances.bboxes for gt_instances in batch_gt_instances\n        ]\n        gt_labels = [\n            gt_instances.labels for gt_instances in batch_gt_instances\n        ]\n\n        targets = self.get_targets(\n            gt_bboxes,\n            gt_labels,\n            tl_heats[-1].shape,\n            batch_img_metas[0]['batch_input_shape'],\n            with_corner_emb=self.with_corner_emb)\n        mlvl_targets = [targets for _ in range(self.num_feat_levels)]\n        det_losses, pull_losses, push_losses, off_losses = multi_apply(\n            self.loss_by_feat_single, tl_heats, br_heats, tl_embs, br_embs,\n            tl_offs, br_offs, mlvl_targets)\n        loss_dict = dict(det_loss=det_losses, off_loss=off_losses)\n        if self.with_corner_emb:\n            loss_dict.update(pull_loss=pull_losses, push_loss=push_losses)\n        return loss_dict\n\n    def loss_by_feat_single(self, tl_hmp: Tensor, br_hmp: Tensor,\n                            tl_emb: Optional[Tensor], br_emb: Optional[Tensor],\n                            tl_off: Tensor, br_off: Tensor,\n                            targets: dict) -> Tuple[Tensor, ...]:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            tl_hmp (Tensor): Top-left corner heatmap for current level with\n                shape (N, num_classes, H, W).\n            br_hmp (Tensor): Bottom-right corner heatmap for current level with\n                shape (N, num_classes, H, W).\n            tl_emb (Tensor, optional): Top-left corner embedding for current\n                level with shape (N, corner_emb_channels, H, W).\n            br_emb (Tensor, optional): Bottom-right corner embedding for\n                current level with shape (N, corner_emb_channels, H, W).\n            tl_off (Tensor): Top-left corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            br_off (Tensor): Bottom-right corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            targets (dict): Corner target generated by `get_targets`.\n\n        Returns:\n            tuple[torch.Tensor]: Losses of the head's different branches\n            containing the following losses:\n\n                - det_loss (Tensor): Corner keypoint loss.\n                - pull_loss (Tensor): Part one of AssociativeEmbedding loss.\n                - push_loss (Tensor): Part two of AssociativeEmbedding loss.\n                - off_loss (Tensor): Corner offset loss.\n        \"\"\"\n        gt_tl_hmp = targets['topleft_heatmap']\n        gt_br_hmp = targets['bottomright_heatmap']\n        gt_tl_off = targets['topleft_offset']\n        gt_br_off = targets['bottomright_offset']\n        gt_embedding = targets['corner_embedding']\n\n        # Detection loss\n        tl_det_loss = self.loss_heatmap(\n            tl_hmp.sigmoid(),\n            gt_tl_hmp,\n            avg_factor=max(1,\n                           gt_tl_hmp.eq(1).sum()))\n        br_det_loss = self.loss_heatmap(\n            br_hmp.sigmoid(),\n            gt_br_hmp,\n            avg_factor=max(1,\n                           gt_br_hmp.eq(1).sum()))\n        det_loss = (tl_det_loss + br_det_loss) / 2.0\n\n        # AssociativeEmbedding loss\n        if self.with_corner_emb and self.loss_embedding is not None:\n            pull_loss, push_loss = self.loss_embedding(tl_emb, br_emb,\n                                                       gt_embedding)\n        else:\n            pull_loss, push_loss = None, None\n\n        # Offset loss\n        # We only compute the offset loss at the real corner position.\n        # The value of real corner would be 1 in heatmap ground truth.\n        # The mask is computed in class agnostic mode and its shape is\n        # batch * 1 * width * height.\n        tl_off_mask = gt_tl_hmp.eq(1).sum(1).gt(0).unsqueeze(1).type_as(\n            gt_tl_hmp)\n        br_off_mask = gt_br_hmp.eq(1).sum(1).gt(0).unsqueeze(1).type_as(\n            gt_br_hmp)\n        tl_off_loss = self.loss_offset(\n            tl_off,\n            gt_tl_off,\n            tl_off_mask,\n            avg_factor=max(1, tl_off_mask.sum()))\n        br_off_loss = self.loss_offset(\n            br_off,\n            gt_br_off,\n            br_off_mask,\n            avg_factor=max(1, br_off_mask.sum()))\n\n        off_loss = (tl_off_loss + br_off_loss) / 2.0\n\n        return det_loss, pull_loss, push_loss, off_loss\n\n    def predict_by_feat(self,\n                        tl_heats: List[Tensor],\n                        br_heats: List[Tensor],\n                        tl_embs: List[Tensor],\n                        br_embs: List[Tensor],\n                        tl_offs: List[Tensor],\n                        br_offs: List[Tensor],\n                        batch_img_metas: Optional[List[dict]] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Args:\n            tl_heats (list[Tensor]): Top-left corner heatmaps for each level\n                with shape (N, num_classes, H, W).\n            br_heats (list[Tensor]): Bottom-right corner heatmaps for each\n                level with shape (N, num_classes, H, W).\n            tl_embs (list[Tensor]): Top-left corner embeddings for each level\n                with shape (N, corner_emb_channels, H, W).\n            br_embs (list[Tensor]): Bottom-right corner embeddings for each\n                level with shape (N, corner_emb_channels, H, W).\n            tl_offs (list[Tensor]): Top-left corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            br_offs (list[Tensor]): Bottom-right corner offsets for each level\n                with shape (N, corner_offset_channels, H, W).\n            batch_img_metas (list[dict], optional): Batch image meta info.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert tl_heats[-1].shape[0] == br_heats[-1].shape[0] == len(\n            batch_img_metas)\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            result_list.append(\n                self._predict_by_feat_single(\n                    tl_heats[-1][img_id:img_id + 1, :],\n                    br_heats[-1][img_id:img_id + 1, :],\n                    tl_offs[-1][img_id:img_id + 1, :],\n                    br_offs[-1][img_id:img_id + 1, :],\n                    batch_img_metas[img_id],\n                    tl_emb=tl_embs[-1][img_id:img_id + 1, :],\n                    br_emb=br_embs[-1][img_id:img_id + 1, :],\n                    rescale=rescale,\n                    with_nms=with_nms))\n\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                tl_heat: Tensor,\n                                br_heat: Tensor,\n                                tl_off: Tensor,\n                                br_off: Tensor,\n                                img_meta: dict,\n                                tl_emb: Optional[Tensor] = None,\n                                br_emb: Optional[Tensor] = None,\n                                tl_centripetal_shift: Optional[Tensor] = None,\n                                br_centripetal_shift: Optional[Tensor] = None,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            tl_heat (Tensor): Top-left corner heatmap for current level with\n                shape (N, num_classes, H, W).\n            br_heat (Tensor): Bottom-right corner heatmap for current level\n                with shape (N, num_classes, H, W).\n            tl_off (Tensor): Top-left corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            br_off (Tensor): Bottom-right corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            img_meta (dict): Meta information of current image, e.g.,\n                image size, scaling factor, etc.\n            tl_emb (Tensor): Top-left corner embedding for current level with\n                shape (N, corner_emb_channels, H, W).\n            br_emb (Tensor): Bottom-right corner embedding for current level\n                with shape (N, corner_emb_channels, H, W).\n            tl_centripetal_shift: Top-left corner's centripetal shift for\n                current level with shape (N, 2, H, W).\n            br_centripetal_shift: Bottom-right corner's centripetal shift for\n                current level with shape (N, 2, H, W).\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        if isinstance(img_meta, (list, tuple)):\n            img_meta = img_meta[0]\n\n        batch_bboxes, batch_scores, batch_clses = self._decode_heatmap(\n            tl_heat=tl_heat.sigmoid(),\n            br_heat=br_heat.sigmoid(),\n            tl_off=tl_off,\n            br_off=br_off,\n            tl_emb=tl_emb,\n            br_emb=br_emb,\n            tl_centripetal_shift=tl_centripetal_shift,\n            br_centripetal_shift=br_centripetal_shift,\n            img_meta=img_meta,\n            k=self.test_cfg.corner_topk,\n            kernel=self.test_cfg.local_maximum_kernel,\n            distance_threshold=self.test_cfg.distance_threshold)\n\n        if rescale and 'scale_factor' in img_meta:\n            batch_bboxes /= batch_bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n\n        bboxes = batch_bboxes.view([-1, 4])\n        scores = batch_scores.view(-1)\n        clses = batch_clses.view(-1)\n\n        det_bboxes = torch.cat([bboxes, scores.unsqueeze(-1)], -1)\n        keepinds = (det_bboxes[:, -1] > -0.1)\n        det_bboxes = det_bboxes[keepinds]\n        det_labels = clses[keepinds]\n\n        if with_nms:\n            det_bboxes, det_labels = self._bboxes_nms(det_bboxes, det_labels,\n                                                      self.test_cfg)\n\n        results = InstanceData()\n        results.bboxes = det_bboxes[..., :4]\n        results.scores = det_bboxes[..., 4]\n        results.labels = det_labels\n        return results\n\n    def _bboxes_nms(self, bboxes: Tensor, labels: Tensor,\n                    cfg: ConfigDict) -> Tuple[Tensor, Tensor]:\n        \"\"\"bboxes nms.\"\"\"\n        if 'nms_cfg' in cfg:\n            warning.warn('nms_cfg in test_cfg will be deprecated. '\n                         'Please rename it as nms')\n        if 'nms' not in cfg:\n            cfg.nms = cfg.nms_cfg\n\n        if labels.numel() > 0:\n            max_num = cfg.max_per_img\n            bboxes, keep = batched_nms(bboxes[:, :4], bboxes[:,\n                                                             -1].contiguous(),\n                                       labels, cfg.nms)\n            if max_num > 0:\n                bboxes = bboxes[:max_num]\n                labels = labels[keep][:max_num]\n\n        return bboxes, labels\n\n    def _decode_heatmap(self,\n                        tl_heat: Tensor,\n                        br_heat: Tensor,\n                        tl_off: Tensor,\n                        br_off: Tensor,\n                        tl_emb: Optional[Tensor] = None,\n                        br_emb: Optional[Tensor] = None,\n                        tl_centripetal_shift: Optional[Tensor] = None,\n                        br_centripetal_shift: Optional[Tensor] = None,\n                        img_meta: Optional[dict] = None,\n                        k: int = 100,\n                        kernel: int = 3,\n                        distance_threshold: float = 0.5,\n                        num_dets: int = 1000) -> Tuple[Tensor, Tensor, Tensor]:\n        \"\"\"Transform outputs into detections raw bbox prediction.\n\n        Args:\n            tl_heat (Tensor): Top-left corner heatmap for current level with\n                shape (N, num_classes, H, W).\n            br_heat (Tensor): Bottom-right corner heatmap for current level\n                with shape (N, num_classes, H, W).\n            tl_off (Tensor): Top-left corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            br_off (Tensor): Bottom-right corner offset for current level with\n                shape (N, corner_offset_channels, H, W).\n            tl_emb (Tensor, Optional): Top-left corner embedding for current\n                level with shape (N, corner_emb_channels, H, W).\n            br_emb (Tensor, Optional): Bottom-right corner embedding for\n                current level with shape (N, corner_emb_channels, H, W).\n            tl_centripetal_shift (Tensor, Optional): Top-left centripetal shift\n                for current level with shape (N, 2, H, W).\n            br_centripetal_shift (Tensor, Optional): Bottom-right centripetal\n                shift for current level with shape (N, 2, H, W).\n            img_meta (dict): Meta information of current image, e.g.,\n                image size, scaling factor, etc.\n            k (int): Get top k corner keypoints from heatmap.\n            kernel (int): Max pooling kernel for extract local maximum pixels.\n            distance_threshold (float): Distance threshold. Top-left and\n                bottom-right corner keypoints with feature distance less than\n                the threshold will be regarded as keypoints from same object.\n            num_dets (int): Num of raw boxes before doing nms.\n\n        Returns:\n            tuple[torch.Tensor]: Decoded output of CornerHead, containing the\n            following Tensors:\n\n            - bboxes (Tensor): Coords of each box.\n            - scores (Tensor): Scores of each box.\n            - clses (Tensor): Categories of each box.\n        \"\"\"\n        with_embedding = tl_emb is not None and br_emb is not None\n        with_centripetal_shift = (\n            tl_centripetal_shift is not None\n            and br_centripetal_shift is not None)\n        assert with_embedding + with_centripetal_shift == 1\n        batch, _, height, width = tl_heat.size()\n        if torch.onnx.is_in_onnx_export():\n            inp_h, inp_w = img_meta['pad_shape_for_onnx'][:2]\n        else:\n            inp_h, inp_w = img_meta['batch_input_shape'][:2]\n\n        # perform nms on heatmaps\n        tl_heat = get_local_maximum(tl_heat, kernel=kernel)\n        br_heat = get_local_maximum(br_heat, kernel=kernel)\n\n        tl_scores, tl_inds, tl_clses, tl_ys, tl_xs = get_topk_from_heatmap(\n            tl_heat, k=k)\n        br_scores, br_inds, br_clses, br_ys, br_xs = get_topk_from_heatmap(\n            br_heat, k=k)\n\n        # We use repeat instead of expand here because expand is a\n        # shallow-copy function. Thus it could cause unexpected testing result\n        # sometimes. Using expand will decrease about 10% mAP during testing\n        # compared to repeat.\n        tl_ys = tl_ys.view(batch, k, 1).repeat(1, 1, k)\n        tl_xs = tl_xs.view(batch, k, 1).repeat(1, 1, k)\n        br_ys = br_ys.view(batch, 1, k).repeat(1, k, 1)\n        br_xs = br_xs.view(batch, 1, k).repeat(1, k, 1)\n\n        tl_off = transpose_and_gather_feat(tl_off, tl_inds)\n        tl_off = tl_off.view(batch, k, 1, 2)\n        br_off = transpose_and_gather_feat(br_off, br_inds)\n        br_off = br_off.view(batch, 1, k, 2)\n\n        tl_xs = tl_xs + tl_off[..., 0]\n        tl_ys = tl_ys + tl_off[..., 1]\n        br_xs = br_xs + br_off[..., 0]\n        br_ys = br_ys + br_off[..., 1]\n\n        if with_centripetal_shift:\n            tl_centripetal_shift = transpose_and_gather_feat(\n                tl_centripetal_shift, tl_inds).view(batch, k, 1, 2).exp()\n            br_centripetal_shift = transpose_and_gather_feat(\n                br_centripetal_shift, br_inds).view(batch, 1, k, 2).exp()\n\n            tl_ctxs = tl_xs + tl_centripetal_shift[..., 0]\n            tl_ctys = tl_ys + tl_centripetal_shift[..., 1]\n            br_ctxs = br_xs - br_centripetal_shift[..., 0]\n            br_ctys = br_ys - br_centripetal_shift[..., 1]\n\n        # all possible boxes based on top k corners (ignoring class)\n        tl_xs *= (inp_w / width)\n        tl_ys *= (inp_h / height)\n        br_xs *= (inp_w / width)\n        br_ys *= (inp_h / height)\n\n        if with_centripetal_shift:\n            tl_ctxs *= (inp_w / width)\n            tl_ctys *= (inp_h / height)\n            br_ctxs *= (inp_w / width)\n            br_ctys *= (inp_h / height)\n\n        x_off, y_off = 0, 0  # no crop\n        if not torch.onnx.is_in_onnx_export():\n            # since `RandomCenterCropPad` is done on CPU with numpy and it's\n            # not dynamic traceable when exporting to ONNX, thus 'border'\n            # does not appears as key in 'img_meta'. As a tmp solution,\n            # we move this 'border' handle part to the postprocess after\n            # finished exporting to ONNX, which is handle in\n            # `mmdet/core/export/model_wrappers.py`. Though difference between\n            # pytorch and exported onnx model, it might be ignored since\n            # comparable performance is achieved between them (e.g. 40.4 vs\n            # 40.6 on COCO val2017, for CornerNet without test-time flip)\n            if 'border' in img_meta:\n                x_off = img_meta['border'][2]\n                y_off = img_meta['border'][0]\n\n        tl_xs -= x_off\n        tl_ys -= y_off\n        br_xs -= x_off\n        br_ys -= y_off\n\n        zeros = tl_xs.new_zeros(*tl_xs.size())\n        tl_xs = torch.where(tl_xs > 0.0, tl_xs, zeros)\n        tl_ys = torch.where(tl_ys > 0.0, tl_ys, zeros)\n        br_xs = torch.where(br_xs > 0.0, br_xs, zeros)\n        br_ys = torch.where(br_ys > 0.0, br_ys, zeros)\n\n        bboxes = torch.stack((tl_xs, tl_ys, br_xs, br_ys), dim=3)\n        area_bboxes = ((br_xs - tl_xs) * (br_ys - tl_ys)).abs()\n\n        if with_centripetal_shift:\n            tl_ctxs -= x_off\n            tl_ctys -= y_off\n            br_ctxs -= x_off\n            br_ctys -= y_off\n\n            tl_ctxs *= tl_ctxs.gt(0.0).type_as(tl_ctxs)\n            tl_ctys *= tl_ctys.gt(0.0).type_as(tl_ctys)\n            br_ctxs *= br_ctxs.gt(0.0).type_as(br_ctxs)\n            br_ctys *= br_ctys.gt(0.0).type_as(br_ctys)\n\n            ct_bboxes = torch.stack((tl_ctxs, tl_ctys, br_ctxs, br_ctys),\n                                    dim=3)\n            area_ct_bboxes = ((br_ctxs - tl_ctxs) * (br_ctys - tl_ctys)).abs()\n\n            rcentral = torch.zeros_like(ct_bboxes)\n            # magic nums from paper section 4.1\n            mu = torch.ones_like(area_bboxes) / 2.4\n            mu[area_bboxes > 3500] = 1 / 2.1  # large bbox have smaller mu\n\n            bboxes_center_x = (bboxes[..., 0] + bboxes[..., 2]) / 2\n            bboxes_center_y = (bboxes[..., 1] + bboxes[..., 3]) / 2\n            rcentral[..., 0] = bboxes_center_x - mu * (bboxes[..., 2] -\n                                                       bboxes[..., 0]) / 2\n            rcentral[..., 1] = bboxes_center_y - mu * (bboxes[..., 3] -\n                                                       bboxes[..., 1]) / 2\n            rcentral[..., 2] = bboxes_center_x + mu * (bboxes[..., 2] -\n                                                       bboxes[..., 0]) / 2\n            rcentral[..., 3] = bboxes_center_y + mu * (bboxes[..., 3] -\n                                                       bboxes[..., 1]) / 2\n            area_rcentral = ((rcentral[..., 2] - rcentral[..., 0]) *\n                             (rcentral[..., 3] - rcentral[..., 1])).abs()\n            dists = area_ct_bboxes / area_rcentral\n\n            tl_ctx_inds = (ct_bboxes[..., 0] <= rcentral[..., 0]) | (\n                ct_bboxes[..., 0] >= rcentral[..., 2])\n            tl_cty_inds = (ct_bboxes[..., 1] <= rcentral[..., 1]) | (\n                ct_bboxes[..., 1] >= rcentral[..., 3])\n            br_ctx_inds = (ct_bboxes[..., 2] <= rcentral[..., 0]) | (\n                ct_bboxes[..., 2] >= rcentral[..., 2])\n            br_cty_inds = (ct_bboxes[..., 3] <= rcentral[..., 1]) | (\n                ct_bboxes[..., 3] >= rcentral[..., 3])\n\n        if with_embedding:\n            tl_emb = transpose_and_gather_feat(tl_emb, tl_inds)\n            tl_emb = tl_emb.view(batch, k, 1)\n            br_emb = transpose_and_gather_feat(br_emb, br_inds)\n            br_emb = br_emb.view(batch, 1, k)\n            dists = torch.abs(tl_emb - br_emb)\n\n        tl_scores = tl_scores.view(batch, k, 1).repeat(1, 1, k)\n        br_scores = br_scores.view(batch, 1, k).repeat(1, k, 1)\n\n        scores = (tl_scores + br_scores) / 2  # scores for all possible boxes\n\n        # tl and br should have same class\n        tl_clses = tl_clses.view(batch, k, 1).repeat(1, 1, k)\n        br_clses = br_clses.view(batch, 1, k).repeat(1, k, 1)\n        cls_inds = (tl_clses != br_clses)\n\n        # reject boxes based on distances\n        dist_inds = dists > distance_threshold\n\n        # reject boxes based on widths and heights\n        width_inds = (br_xs <= tl_xs)\n        height_inds = (br_ys <= tl_ys)\n\n        # No use `scores[cls_inds]`, instead we use `torch.where` here.\n        # Since only 1-D indices with type 'tensor(bool)' are supported\n        # when exporting to ONNX, any other bool indices with more dimensions\n        # (e.g. 2-D bool tensor) as input parameter in node is invalid\n        negative_scores = -1 * torch.ones_like(scores)\n        scores = torch.where(cls_inds, negative_scores, scores)\n        scores = torch.where(width_inds, negative_scores, scores)\n        scores = torch.where(height_inds, negative_scores, scores)\n        scores = torch.where(dist_inds, negative_scores, scores)\n\n        if with_centripetal_shift:\n            scores[tl_ctx_inds] = -1\n            scores[tl_cty_inds] = -1\n            scores[br_ctx_inds] = -1\n            scores[br_cty_inds] = -1\n\n        scores = scores.view(batch, -1)\n        scores, inds = torch.topk(scores, num_dets)\n        scores = scores.unsqueeze(2)\n\n        bboxes = bboxes.view(batch, -1, 4)\n        bboxes = gather_feat(bboxes, inds)\n\n        clses = tl_clses.contiguous().view(batch, -1, 1)\n        clses = gather_feat(clses, inds)\n\n        return bboxes, scores, clses\n"
  },
  {
    "path": "mmdet/models/dense_heads/dab_detr_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import Linear\nfrom mmengine.model import bias_init_with_prob, constant_init\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import InstanceList\nfrom ..layers import MLP, inverse_sigmoid\nfrom .conditional_detr_head import ConditionalDETRHead\n\n\n@MODELS.register_module()\nclass DABDETRHead(ConditionalDETRHead):\n    \"\"\"Head of DAB-DETR. DAB-DETR: Dynamic Anchor Boxes are Better Queries for\n    DETR.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/2201.12329>`_ .\n    \"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the transformer head.\"\"\"\n        # cls branch\n        self.fc_cls = Linear(self.embed_dims, self.cls_out_channels)\n        # reg branch\n        self.fc_reg = MLP(self.embed_dims, self.embed_dims, 4, 3)\n\n    def init_weights(self) -> None:\n        \"\"\"initialize weights.\"\"\"\n        if self.loss_cls.use_sigmoid:\n            bias_init = bias_init_with_prob(0.01)\n            nn.init.constant_(self.fc_cls.bias, bias_init)\n        constant_init(self.fc_reg.layers[-1], 0., bias=0.)\n\n    def forward(self, hidden_states: Tensor,\n                references: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"\"Forward function.\n\n        Args:\n            hidden_states (Tensor): Features from transformer decoder. If\n                `return_intermediate_dec` is True output has shape\n                (num_decoder_layers, bs, num_queries, dim), else has shape (1,\n                bs, num_queries, dim) which only contains the last layer\n                outputs.\n            references (Tensor): References from transformer decoder. If\n                `return_intermediate_dec` is True output has shape\n                (num_decoder_layers, bs, num_queries, 2/4), else has shape (1,\n                bs, num_queries, 2/4)\n                which only contains the last layer reference.\n        Returns:\n            tuple[Tensor]: results of head containing the following tensor.\n\n            - layers_cls_scores (Tensor): Outputs from the classification head,\n              shape (num_decoder_layers, bs, num_queries, cls_out_channels).\n              Note cls_out_channels should include background.\n            - layers_bbox_preds (Tensor): Sigmoid outputs from the regression\n              head with normalized coordinate format (cx, cy, w, h), has shape\n              (num_decoder_layers, bs, num_queries, 4).\n        \"\"\"\n        layers_cls_scores = self.fc_cls(hidden_states)\n        references_before_sigmoid = inverse_sigmoid(references, eps=1e-3)\n        tmp_reg_preds = self.fc_reg(hidden_states)\n        tmp_reg_preds[..., :references_before_sigmoid.\n                      size(-1)] += references_before_sigmoid\n        layers_bbox_preds = tmp_reg_preds.sigmoid()\n        return layers_cls_scores, layers_bbox_preds\n\n    def predict(self,\n                hidden_states: Tensor,\n                references: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network. Over-write\n        because img_metas are needed as inputs for bbox_head.\n\n        Args:\n            hidden_states (Tensor): Feature from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, dim).\n            references (Tensor): references from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, 2/4).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        last_layer_hidden_state = hidden_states[-1].unsqueeze(0)\n        last_layer_reference = references[-1].unsqueeze(0)\n        outs = self(last_layer_hidden_state, last_layer_reference)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, rescale=rescale)\n        return predictions\n"
  },
  {
    "path": "mmdet/models/dense_heads/ddod_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, Scale\nfrom mmengine.model import bias_init_with_prob, normal_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, reduce_mean)\nfrom ..task_modules.prior_generators import anchor_inside_flags\nfrom ..utils import images_to_levels, multi_apply, unmap\nfrom .anchor_head import AnchorHead\n\nEPS = 1e-12\n\n\n@MODELS.register_module()\nclass DDODHead(AnchorHead):\n    \"\"\"Detection Head of `DDOD <https://arxiv.org/abs/2107.02963>`_.\n\n    DDOD head decomposes conjunctions lying in most current one-stage\n    detectors via label assignment disentanglement, spatial feature\n    disentanglement, and pyramid supervision disentanglement.\n\n    Args:\n        num_classes (int): Number of categories excluding the\n            background category.\n        in_channels (int): Number of channels in the input feature map.\n        stacked_convs (int): The number of stacked Conv. Defaults to 4.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        use_dcn (bool): Use dcn, Same as ATSS when False. Defaults to True.\n        norm_cfg (:obj:`ConfigDict` or dict): Normal config of ddod head.\n            Defaults to dict(type='GN', num_groups=32, requires_grad=True).\n        loss_iou (:obj:`ConfigDict` or dict): Config of IoU loss. Defaults to\n            dict(type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0).\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 stacked_convs: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 use_dcn: bool = True,\n                 norm_cfg: ConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 loss_iou: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     loss_weight=1.0),\n                 **kwargs) -> None:\n        self.stacked_convs = stacked_convs\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.use_dcn = use_dcn\n        super().__init__(num_classes, in_channels, **kwargs)\n\n        if self.train_cfg:\n            self.cls_assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            self.reg_assigner = TASK_UTILS.build(\n                self.train_cfg['reg_assigner'])\n        self.loss_iou = MODELS.build(loss_iou)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=dict(type='DCN', deform_groups=1)\n                    if i == 0 and self.use_dcn else self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=dict(type='DCN', deform_groups=1)\n                    if i == 0 and self.use_dcn else self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        self.atss_cls = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * self.cls_out_channels,\n            3,\n            padding=1)\n        self.atss_reg = nn.Conv2d(\n            self.feat_channels, self.num_base_priors * 4, 3, padding=1)\n        self.atss_iou = nn.Conv2d(\n            self.feat_channels, self.num_base_priors * 1, 3, padding=1)\n        self.scales = nn.ModuleList(\n            [Scale(1.0) for _ in self.prior_generator.strides])\n\n        # we use the global list in loss\n        self.cls_num_pos_samples_per_level = [\n            0. for _ in range(len(self.prior_generator.strides))\n        ]\n        self.reg_num_pos_samples_per_level = [\n            0. for _ in range(len(self.prior_generator.strides))\n        ]\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        for m in self.cls_convs:\n            normal_init(m.conv, std=0.01)\n        for m in self.reg_convs:\n            normal_init(m.conv, std=0.01)\n        normal_init(self.atss_reg, std=0.01)\n        normal_init(self.atss_iou, std=0.01)\n        bias_cls = bias_init_with_prob(0.01)\n        normal_init(self.atss_cls, std=0.01, bias=bias_cls)\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores, bbox predictions,\n            and iou predictions.\n\n            - cls_scores (list[Tensor]): Classification scores for all \\\n            scale levels, each is a 4D-tensor, the channels number is \\\n            num_base_priors * num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for all \\\n            scale levels, each is a 4D-tensor, the channels number is \\\n            num_base_priors * 4.\n            - iou_preds (list[Tensor]): IoU scores for all scale levels, \\\n            each is a 4D-tensor, the channels number is num_base_priors * 1.\n        \"\"\"\n        return multi_apply(self.forward_single, x, self.scales)\n\n    def forward_single(self, x: Tensor, scale: Scale) -> Sequence[Tensor]:\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n            scale (:obj: `mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n\n        Returns:\n            tuple:\n\n            - cls_score (Tensor): Cls scores for a single scale level \\\n            the channels number is num_base_priors * num_classes.\n            - bbox_pred (Tensor): Box energies / deltas for a single \\\n            scale level, the channels number is num_base_priors * 4.\n            - iou_pred (Tensor): Iou for a single scale level, the \\\n            channel number is (N, num_base_priors * 1, H, W).\n        \"\"\"\n        cls_feat = x\n        reg_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            reg_feat = reg_conv(reg_feat)\n        cls_score = self.atss_cls(cls_feat)\n        # we just follow atss, not apply exp in bbox_pred\n        bbox_pred = scale(self.atss_reg(reg_feat)).float()\n        iou_pred = self.atss_iou(reg_feat)\n        return cls_score, bbox_pred, iou_pred\n\n    def loss_cls_by_feat_single(self, cls_score: Tensor, labels: Tensor,\n                                label_weights: Tensor,\n                                reweight_factor: List[float],\n                                avg_factor: float) -> Tuple[Tensor]:\n        \"\"\"Compute cls loss of a single scale level.\n\n        Args:\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_base_priors * num_classes, H, W).\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors)\n            reweight_factor (List[float]): Reweight factor for cls and reg\n                loss.\n            avg_factor (float): Average factor that is used to average\n                the loss. When using sampling method, avg_factor is usually\n                the sum of positive and negative priors. When using\n                `PseudoSampler`, `avg_factor` is usually equal to the number\n                of positive priors.\n\n        Returns:\n            Tuple[Tensor]: A tuple of loss components.\n        \"\"\"\n        cls_score = cls_score.permute(0, 2, 3, 1).reshape(\n            -1, self.cls_out_channels).contiguous()\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        loss_cls = self.loss_cls(\n            cls_score, labels, label_weights, avg_factor=avg_factor)\n        return reweight_factor * loss_cls,\n\n    def loss_reg_by_feat_single(self, anchors: Tensor, bbox_pred: Tensor,\n                                iou_pred: Tensor, labels,\n                                label_weights: Tensor, bbox_targets: Tensor,\n                                bbox_weights: Tensor,\n                                reweight_factor: List[float],\n                                avg_factor: float) -> Tuple[Tensor, Tensor]:\n        \"\"\"Compute reg loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            anchors (Tensor): Box reference for each scale level with shape\n                (N, num_total_anchors, 4).\n            bbox_pred (Tensor): Box energies / deltas for each scale\n                level with shape (N, num_base_priors * 4, H, W).\n            iou_pred (Tensor): Iou for a single scale level, the\n                channel number is (N, num_base_priors * 1, H, W).\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors)\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (N, num_total_anchors, 4).\n            bbox_weights (Tensor): BBox weights of all anchors in the\n                image with shape (N, 4)\n            reweight_factor (List[float]): Reweight factor for cls and reg\n                loss.\n            avg_factor (float): Average factor that is used to average\n                the loss. When using sampling method, avg_factor is usually\n                the sum of positive and negative priors. When using\n                `PseudoSampler`, `avg_factor` is usually equal to the number\n                of positive priors.\n        Returns:\n            Tuple[Tensor, Tensor]: A tuple of loss components.\n        \"\"\"\n        anchors = anchors.reshape(-1, 4)\n        bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n        iou_pred = iou_pred.permute(0, 2, 3, 1).reshape(-1, )\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        bbox_weights = bbox_weights.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n\n        iou_targets = label_weights.new_zeros(labels.shape)\n        iou_weights = label_weights.new_zeros(labels.shape)\n        iou_weights[(bbox_weights.sum(axis=1) > 0).nonzero(\n            as_tuple=False)] = 1.\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    &\n                    (labels < bg_class_ind)).nonzero(as_tuple=False).squeeze(1)\n\n        if len(pos_inds) > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n            pos_anchors = anchors[pos_inds]\n\n            pos_decode_bbox_pred = self.bbox_coder.decode(\n                pos_anchors, pos_bbox_pred)\n            pos_decode_bbox_targets = self.bbox_coder.decode(\n                pos_anchors, pos_bbox_targets)\n\n            # regression loss\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_decode_bbox_targets,\n                avg_factor=avg_factor)\n\n            iou_targets[pos_inds] = bbox_overlaps(\n                pos_decode_bbox_pred.detach(),\n                pos_decode_bbox_targets,\n                is_aligned=True)\n            loss_iou = self.loss_iou(\n                iou_pred, iou_targets, iou_weights, avg_factor=avg_factor)\n        else:\n            loss_bbox = bbox_pred.sum() * 0\n            loss_iou = iou_pred.sum() * 0\n\n        return reweight_factor * loss_bbox, reweight_factor * loss_iou\n\n    def calc_reweight_factor(self, labels_list: List[Tensor]) -> List[float]:\n        \"\"\"Compute reweight_factor for regression and classification loss.\"\"\"\n        # get pos samples for each level\n        bg_class_ind = self.num_classes\n        for ii, each_level_label in enumerate(labels_list):\n            pos_inds = ((each_level_label >= 0) &\n                        (each_level_label < bg_class_ind)).nonzero(\n                            as_tuple=False).squeeze(1)\n            self.cls_num_pos_samples_per_level[ii] += len(pos_inds)\n        # get reweight factor from 1 ~ 2 with bilinear interpolation\n        min_pos_samples = min(self.cls_num_pos_samples_per_level)\n        max_pos_samples = max(self.cls_num_pos_samples_per_level)\n        interval = 1. / (max_pos_samples - min_pos_samples + 1e-10)\n        reweight_factor_per_level = []\n        for pos_samples in self.cls_num_pos_samples_per_level:\n            factor = 2. - (pos_samples - min_pos_samples) * interval\n            reweight_factor_per_level.append(factor)\n        return reweight_factor_per_level\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            iou_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_base_priors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_base_priors * 4, H, W)\n            iou_preds (list[Tensor]): Score factor for all scale level,\n                each is a 4D-tensor, has shape (batch_size, 1, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        # calculate common vars for cls and reg assigners at once\n        targets_com = self.process_predictions_and_anchors(\n            anchor_list, valid_flag_list, cls_scores, bbox_preds,\n            batch_img_metas, batch_gt_instances_ignore)\n        (anchor_list, valid_flag_list, num_level_anchors_list, cls_score_list,\n         bbox_pred_list, batch_gt_instances_ignore) = targets_com\n\n        # classification branch assigner\n        cls_targets = self.get_cls_targets(\n            anchor_list,\n            valid_flag_list,\n            num_level_anchors_list,\n            cls_score_list,\n            bbox_pred_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        (cls_anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor) = cls_targets\n\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n        avg_factor = max(avg_factor, 1.0)\n\n        reweight_factor_per_level = self.calc_reweight_factor(labels_list)\n\n        cls_losses_cls, = multi_apply(\n            self.loss_cls_by_feat_single,\n            cls_scores,\n            labels_list,\n            label_weights_list,\n            reweight_factor_per_level,\n            avg_factor=avg_factor)\n\n        # regression branch assigner\n        reg_targets = self.get_reg_targets(\n            anchor_list,\n            valid_flag_list,\n            num_level_anchors_list,\n            cls_score_list,\n            bbox_pred_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        (reg_anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor) = reg_targets\n\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n        avg_factor = max(avg_factor, 1.0)\n\n        reweight_factor_per_level = self.calc_reweight_factor(labels_list)\n\n        reg_losses_bbox, reg_losses_iou = multi_apply(\n            self.loss_reg_by_feat_single,\n            reg_anchor_list,\n            bbox_preds,\n            iou_preds,\n            labels_list,\n            label_weights_list,\n            bbox_targets_list,\n            bbox_weights_list,\n            reweight_factor_per_level,\n            avg_factor=avg_factor)\n\n        return dict(\n            loss_cls=cls_losses_cls,\n            loss_bbox=reg_losses_bbox,\n            loss_iou=reg_losses_iou)\n\n    def process_predictions_and_anchors(\n            self,\n            anchor_list: List[List[Tensor]],\n            valid_flag_list: List[List[Tensor]],\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> tuple:\n        \"\"\"Compute common vars for regression and classification targets.\n\n        Args:\n            anchor_list (List[List[Tensor]]): anchors of each image.\n            valid_flag_list (List[List[Tensor]]): Valid flags of each image.\n            cls_scores (List[Tensor]): Classification scores for all scale\n                levels, each is a 4D-tensor, the channels number is\n                num_base_priors * num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for all scale\n                levels, each is a 4D-tensor, the channels number is\n                num_base_priors * 4.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Return:\n            tuple[Tensor]: A tuple of common loss vars.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        num_level_anchors_list = [num_level_anchors] * num_imgs\n\n        anchor_list_ = []\n        valid_flag_list_ = []\n        # concat all level anchors and flags to a single tensor\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            anchor_list_.append(torch.cat(anchor_list[i]))\n            valid_flag_list_.append(torch.cat(valid_flag_list[i]))\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None for _ in range(num_imgs)]\n\n        num_levels = len(cls_scores)\n        cls_score_list = []\n        bbox_pred_list = []\n\n        mlvl_cls_score_list = [\n            cls_score.permute(0, 2, 3, 1).reshape(\n                num_imgs, -1, self.num_base_priors * self.cls_out_channels)\n            for cls_score in cls_scores\n        ]\n        mlvl_bbox_pred_list = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                  self.num_base_priors * 4)\n            for bbox_pred in bbox_preds\n        ]\n\n        for i in range(num_imgs):\n            mlvl_cls_tensor_list = [\n                mlvl_cls_score_list[j][i] for j in range(num_levels)\n            ]\n            mlvl_bbox_tensor_list = [\n                mlvl_bbox_pred_list[j][i] for j in range(num_levels)\n            ]\n            cat_mlvl_cls_score = torch.cat(mlvl_cls_tensor_list, dim=0)\n            cat_mlvl_bbox_pred = torch.cat(mlvl_bbox_tensor_list, dim=0)\n            cls_score_list.append(cat_mlvl_cls_score)\n            bbox_pred_list.append(cat_mlvl_bbox_pred)\n        return (anchor_list_, valid_flag_list_, num_level_anchors_list,\n                cls_score_list, bbox_pred_list, batch_gt_instances_ignore)\n\n    def get_cls_targets(self,\n                        anchor_list: List[Tensor],\n                        valid_flag_list: List[Tensor],\n                        num_level_anchors_list: List[int],\n                        cls_score_list: List[Tensor],\n                        bbox_pred_list: List[Tensor],\n                        batch_gt_instances: InstanceList,\n                        batch_img_metas: List[dict],\n                        batch_gt_instances_ignore: OptInstanceList = None,\n                        unmap_outputs: bool = True) -> tuple:\n        \"\"\"Get cls targets for DDOD head.\n\n        This method is almost the same as `AnchorHead.get_targets()`.\n        Besides returning the targets as the parent  method does,\n        it also returns the anchors as the first element of the\n        returned tuple.\n\n        Args:\n            anchor_list (list[Tensor]): anchors of each image.\n            valid_flag_list (list[Tensor]): Valid flags of each image.\n            num_level_anchors_list (list[Tensor]): Number of anchors of each\n                scale level of all image.\n            cls_score_list (list[Tensor]): Classification scores for all scale\n                levels, each is a 4D-tensor, the channels number is\n                num_base_priors * num_classes.\n            bbox_pred_list (list[Tensor]): Box energies / deltas for all scale\n                levels, each is a 4D-tensor, the channels number is\n                num_base_priors * 4.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Return:\n            tuple[Tensor]: A tuple of cls targets components.\n        \"\"\"\n        (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n         all_bbox_weights, pos_inds_list, neg_inds_list,\n         sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             anchor_list,\n             valid_flag_list,\n             cls_score_list,\n             bbox_pred_list,\n             num_level_anchors_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             unmap_outputs=unmap_outputs,\n             is_cls_assigner=True)\n        # Get `avg_factor` of all images, which calculate in `SamplingResult`.\n        # When using sampling method, avg_factor is usually the sum of\n        # positive and negative priors. When using `PseudoSampler`,\n        # `avg_factor` is usually equal to the number of positive priors.\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        anchors_list = images_to_levels(all_anchors, num_level_anchors_list[0])\n        labels_list = images_to_levels(all_labels, num_level_anchors_list[0])\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors_list[0])\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors_list[0])\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_anchors_list[0])\n        return (anchors_list, labels_list, label_weights_list,\n                bbox_targets_list, bbox_weights_list, avg_factor)\n\n    def get_reg_targets(self,\n                        anchor_list: List[Tensor],\n                        valid_flag_list: List[Tensor],\n                        num_level_anchors_list: List[int],\n                        cls_score_list: List[Tensor],\n                        bbox_pred_list: List[Tensor],\n                        batch_gt_instances: InstanceList,\n                        batch_img_metas: List[dict],\n                        batch_gt_instances_ignore: OptInstanceList = None,\n                        unmap_outputs: bool = True) -> tuple:\n        \"\"\"Get reg targets for DDOD head.\n\n        This method is almost the same as `AnchorHead.get_targets()` when\n        is_cls_assigner is False. Besides returning the targets as the parent\n        method does, it also returns the anchors as the first element of the\n        returned tuple.\n\n        Args:\n            anchor_list (list[Tensor]): anchors of each image.\n            valid_flag_list (list[Tensor]): Valid flags of each image.\n            num_level_anchors_list (list[Tensor]): Number of anchors of each\n                scale level of all image.\n            cls_score_list (list[Tensor]): Classification scores for all scale\n                levels, each is a 4D-tensor, the channels number is\n                num_base_priors * num_classes.\n            bbox_pred_list (list[Tensor]): Box energies / deltas for all scale\n                levels, each is a 4D-tensor, the channels number is\n                num_base_priors * 4.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Return:\n            tuple[Tensor]: A tuple of reg targets components.\n        \"\"\"\n        (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n         all_bbox_weights, pos_inds_list, neg_inds_list,\n         sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             anchor_list,\n             valid_flag_list,\n             cls_score_list,\n             bbox_pred_list,\n             num_level_anchors_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             unmap_outputs=unmap_outputs,\n             is_cls_assigner=False)\n        # Get `avg_factor` of all images, which calculate in `SamplingResult`.\n        # When using sampling method, avg_factor is usually the sum of\n        # positive and negative priors. When using `PseudoSampler`,\n        # `avg_factor` is usually equal to the number of positive priors.\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        anchors_list = images_to_levels(all_anchors, num_level_anchors_list[0])\n        labels_list = images_to_levels(all_labels, num_level_anchors_list[0])\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors_list[0])\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors_list[0])\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_anchors_list[0])\n        return (anchors_list, labels_list, label_weights_list,\n                bbox_targets_list, bbox_weights_list, avg_factor)\n\n    def _get_targets_single(self,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            cls_scores: Tensor,\n                            bbox_preds: Tensor,\n                            num_level_anchors: List[int],\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True,\n                            is_cls_assigner: bool = True) -> tuple:\n        \"\"\"Compute regression, classification targets for anchors in a single\n        image.\n\n        Args:\n            flat_anchors (Tensor): Multi-level anchors of the image,\n                which are concatenated into a single tensor of shape\n                (num_base_priors, 4).\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                shape (num_base_priors,).\n            cls_scores (Tensor): Classification scores for all scale\n                levels of the image.\n            bbox_preds (Tensor): Box energies / deltas for all scale\n                levels of the image.\n            num_level_anchors (List[int]): Number of anchors of each\n                scale level.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors. Defaults to True.\n            is_cls_assigner (bool): Classification or regression.\n                Defaults to True.\n\n        Returns:\n            tuple: N is the number of total anchors in the image.\n            - anchors (Tensor): all anchors in the image with shape (N, 4).\n            - labels (Tensor): Labels of all anchors in the image with \\\n            shape (N, ).\n            - label_weights (Tensor): Label weights of all anchor in the \\\n            image with shape (N, ).\n            - bbox_targets (Tensor): BBox targets of all anchors in the \\\n            image with shape (N, 4).\n            - bbox_weights (Tensor): BBox weights of all anchors in the \\\n            image with shape (N, 4)\n            - pos_inds (Tensor): Indices of positive anchor with shape \\\n            (num_pos, ).\n            - neg_inds (Tensor): Indices of negative anchor with shape \\\n            (num_neg, ).\n            - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # assign gt and sample anchors\n        anchors = flat_anchors[inside_flags, :]\n\n        num_level_anchors_inside = self.get_num_level_anchors_inside(\n            num_level_anchors, inside_flags)\n        bbox_preds_valid = bbox_preds[inside_flags, :]\n        cls_scores_valid = cls_scores[inside_flags, :]\n\n        assigner = self.cls_assigner if is_cls_assigner else self.reg_assigner\n\n        # decode prediction out of assigner\n        bbox_preds_valid = self.bbox_coder.decode(anchors, bbox_preds_valid)\n        pred_instances = InstanceData(\n            priors=anchors, bboxes=bbox_preds_valid, scores=cls_scores_valid)\n\n        assign_result = assigner.assign(\n            pred_instances=pred_instances,\n            num_level_priors=num_level_anchors_inside,\n            gt_instances=gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n        sampling_result = self.sampler.sample(\n            assign_result=assign_result,\n            pred_instances=pred_instances,\n            gt_instances=gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        bbox_targets = torch.zeros_like(anchors)\n        bbox_weights = torch.zeros_like(anchors)\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            pos_bbox_targets = self.bbox_coder.encode(\n                sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes)\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1.0\n\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            anchors = unmap(anchors, num_total_anchors, inside_flags)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags, fill=self.num_classes)\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags)\n            bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags)\n\n        return (anchors, labels, label_weights, bbox_targets, bbox_weights,\n                pos_inds, neg_inds, sampling_result)\n\n    def get_num_level_anchors_inside(self, num_level_anchors: List[int],\n                                     inside_flags: Tensor) -> List[int]:\n        \"\"\"Get the anchors of each scale level inside.\n\n        Args:\n            num_level_anchors (list[int]): Number of anchors of each\n                scale level.\n            inside_flags (Tensor): Multi level inside flags of the image,\n                which are concatenated into a single tensor of\n                shape (num_base_priors,).\n\n        Returns:\n            list[int]: Number of anchors of each scale level inside.\n        \"\"\"\n        split_inside_flags = torch.split(inside_flags, num_level_anchors)\n        num_level_anchors_inside = [\n            int(flags.sum()) for flags in split_inside_flags\n        ]\n        return num_level_anchors_inside\n"
  },
  {
    "path": "mmdet/models/dense_heads/ddq_detr_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Dict, List, Tuple\n\nimport torch\nfrom mmengine.model import bias_init_with_prob, constant_init\nfrom torch import Tensor, nn\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox_cxcywh_to_xyxy\nfrom mmdet.utils import InstanceList, OptInstanceList, reduce_mean\nfrom ..layers import inverse_sigmoid\nfrom ..losses import DDQAuxLoss\nfrom ..utils import multi_apply\nfrom .dino_head import DINOHead\n\n\n@MODELS.register_module()\nclass DDQDETRHead(DINOHead):\n    r\"\"\"Head of DDQDETR: Dense Distinct Query for\n        End-to-End Object Detection.\n\n    Code is modified from the `official github repo\n        <https://github.com/jshilong/DDQ>`_.\n\n    More details can be found in the `paper\n        <https://arxiv.org/abs/2303.12776>`_ .\n\n    Args:\n        aux_num_pos (int): Number of positive targets assigned to a\n            perdicted object. Defaults to 4.\n    \"\"\"\n\n    def __init__(self, *args, aux_num_pos=4, **kwargs):\n        super(DDQDETRHead, self).__init__(*args, **kwargs)\n        self.aux_loss_for_dense = DDQAuxLoss(\n            train_cfg=dict(\n                assigner=dict(type='TopkHungarianAssigner', topk=aux_num_pos),\n                alpha=1,\n                beta=6))\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize classification branch and regression branch of aux head\n        for dense queries.\"\"\"\n        super(DDQDETRHead, self)._init_layers()\n        # If decoder `num_layers` = 6 and `as_two_stage` = True, then:\n        #   1) 6 main heads are required for\n        #       each decoder output of distinct queries.\n        #   2) 1 main head is required for `output_memory` of distinct queries.\n        #   3) 1 aux head is required for `output_memory` of dense queries,\n        #       which is done by code below this comment.\n        # So 8 heads are required in sum.\n        # aux head for dense queries on encoder feature map\n        self.cls_branches.append(copy.deepcopy(self.cls_branches[-1]))\n        self.reg_branches.append(copy.deepcopy(self.reg_branches[-1]))\n\n        # If decoder `num_layers` = 6 and `as_two_stage` = True, then:\n        #   6 aux heads are required for each decoder output of dense queries.\n        # So 8 + 6 = 14 heads and heads are requires in sum.\n        # self.num_pred_layer is 7\n        # aux head for dense queries in decoder\n        self.aux_cls_branches = nn.ModuleList([\n            copy.deepcopy(self.cls_branches[-1])\n            for _ in range(self.num_pred_layer - 1)\n        ])\n        self.aux_reg_branches = nn.ModuleList([\n            copy.deepcopy(self.reg_branches[-1])\n            for _ in range(self.num_pred_layer - 1)\n        ])\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the Deformable DETR head.\"\"\"\n        bias_init = bias_init_with_prob(0.01)\n        for m in self.cls_branches:\n            nn.init.constant_(m.bias, bias_init)\n        for m in self.aux_cls_branches:\n            nn.init.constant_(m.bias, bias_init)\n        for m in self.reg_branches:\n            constant_init(m[-1], 0, bias=0)\n        for m in self.reg_branches:\n            nn.init.constant_(m[-1].bias.data[2:], 0.0)\n\n        for m in self.aux_reg_branches:\n            constant_init(m[-1], 0, bias=0)\n\n        for m in self.aux_reg_branches:\n            nn.init.constant_(m[-1].bias.data[2:], 0.0)\n\n    def forward(self, hidden_states: Tensor,\n                references: List[Tensor]) -> Tuple[Tensor]:\n        \"\"\"Forward function.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, bs, num_queries_total,\n                dim), where `num_queries_total` is the sum of\n                `num_denoising_queries`, `num_queries` and `num_dense_queries`\n                when `self.training` is `True`, else `num_queries`.\n            references (list[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). Each reference has shape (bs,\n                num_queries_total, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n\n        Returns:\n            tuple[Tensor]: results of head containing the following tensors.\n\n            - all_layers_outputs_classes (Tensor): Outputs from the\n              classification head, has shape (num_decoder_layers, bs,\n              num_queries_total, cls_out_channels).\n            - all_layers_outputs_coords (Tensor): Sigmoid outputs from the\n              regression head with normalized coordinate format (cx, cy, w,\n              h), has shape (num_decoder_layers, bs, num_queries_total, 4)\n              with the last dimension arranged as (cx, cy, w, h).\n        \"\"\"\n        all_layers_outputs_classes = []\n        all_layers_outputs_coords = []\n        if self.training:\n            num_dense = self.cache_dict['num_dense_queries']\n        for layer_id in range(hidden_states.shape[0]):\n            reference = inverse_sigmoid(references[layer_id])\n            hidden_state = hidden_states[layer_id]\n            if self.training:\n                dense_hidden_state = hidden_state[:, -num_dense:]\n                hidden_state = hidden_state[:, :-num_dense]\n\n            outputs_class = self.cls_branches[layer_id](hidden_state)\n            tmp_reg_preds = self.reg_branches[layer_id](hidden_state)\n            if self.training:\n                dense_outputs_class = self.aux_cls_branches[layer_id](\n                    dense_hidden_state)\n                dense_tmp_reg_preds = self.aux_reg_branches[layer_id](\n                    dense_hidden_state)\n                outputs_class = torch.cat([outputs_class, dense_outputs_class],\n                                          dim=1)\n                tmp_reg_preds = torch.cat([tmp_reg_preds, dense_tmp_reg_preds],\n                                          dim=1)\n\n            if reference.shape[-1] == 4:\n                tmp_reg_preds += reference\n            else:\n                assert reference.shape[-1] == 2\n                tmp_reg_preds[..., :2] += reference\n            outputs_coord = tmp_reg_preds.sigmoid()\n            all_layers_outputs_classes.append(outputs_class)\n            all_layers_outputs_coords.append(outputs_coord)\n\n        all_layers_outputs_classes = torch.stack(all_layers_outputs_classes)\n        all_layers_outputs_coords = torch.stack(all_layers_outputs_coords)\n\n        return all_layers_outputs_classes, all_layers_outputs_coords\n\n    def loss(self,\n             hidden_states: Tensor,\n             references: List[Tensor],\n             enc_outputs_class: Tensor,\n             enc_outputs_coord: Tensor,\n             batch_data_samples: SampleList,\n             dn_meta: Dict[str, int],\n             aux_enc_outputs_class=None,\n             aux_enc_outputs_coord=None) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the queries of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, bs, num_queries_total,\n                dim), where `num_queries_total` is the sum of\n                `num_denoising_queries`, `num_queries` and `num_dense_queries`\n                when `self.training` is `True`, else `num_queries`.\n            references (list[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). Each reference has shape (bs,\n                num_queries_total, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            enc_outputs_class (Tensor): The top k classification score of\n                each point on encoder feature map, has shape (bs, num_queries,\n                cls_out_channels).\n            enc_outputs_coord (Tensor): The proposal generated from points\n                with top k score, has shape (bs, num_queries, 4) with the\n                last dimension arranged as (cx, cy, w, h).\n            batch_data_samples (list[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n            aux_enc_outputs_class (Tensor): The `dense_topk` classification\n                score of each point on encoder feature map, has shape (bs,\n                num_dense_queries, cls_out_channels).\n                It is `None` when `self.training` is `False`.\n            aux_enc_outputs_coord (Tensor): The proposal generated from points\n                with `dense_topk` score, has shape (bs, num_dense_queries, 4)\n                with the last dimension arranged as (cx, cy, w, h).\n                It is `None` when `self.training` is `False`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states, references)\n        loss_inputs = outs + (enc_outputs_class, enc_outputs_coord,\n                              batch_gt_instances, batch_img_metas, dn_meta)\n        losses = self.loss_by_feat(*loss_inputs)\n\n        aux_enc_outputs_coord = bbox_cxcywh_to_xyxy(aux_enc_outputs_coord)\n        aux_enc_outputs_coord_list = []\n        for img_id in range(len(aux_enc_outputs_coord)):\n            det_bboxes = aux_enc_outputs_coord[img_id]\n            img_shape = batch_img_metas[img_id]['img_shape']\n            det_bboxes[:, 0::2] = det_bboxes[:, 0::2] * img_shape[1]\n            det_bboxes[:, 1::2] = det_bboxes[:, 1::2] * img_shape[0]\n            aux_enc_outputs_coord_list.append(det_bboxes)\n        aux_enc_outputs_coord = torch.stack(aux_enc_outputs_coord_list)\n        aux_loss = self.aux_loss_for_dense.loss(\n            aux_enc_outputs_class.sigmoid(), aux_enc_outputs_coord,\n            [item.bboxes for item in batch_gt_instances],\n            [item.labels for item in batch_gt_instances], batch_img_metas)\n        for k, v in aux_loss.items():\n            losses[f'aux_enc_{k}'] = v\n\n        return losses\n\n    def loss_by_feat(\n        self,\n        all_layers_cls_scores: Tensor,\n        all_layers_bbox_preds: Tensor,\n        enc_cls_scores: Tensor,\n        enc_bbox_preds: Tensor,\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        dn_meta: Dict[str, int],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Loss function.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs,\n                num_queries_total, cls_out_channels).\n            all_layers_bbox_preds (Tensor): Bbox coordinates of all decoder\n                layers. Each has shape (num_decoder_layers, bs,\n                num_queries_total, 4) with normalized coordinate format\n                (cx, cy, w, h).\n            enc_cls_scores (Tensor): The top k score of each point on\n                encoder feature map, has shape (bs, num_queries,\n                cls_out_channels).\n            enc_bbox_preds (Tensor): The proposal generated from points\n                with top k score, has shape (bs, num_queries, 4) with the\n                last dimension arranged as (cx, cy, w, h).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image,\n                e.g., image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n                group collation, including 'num_denoising_queries' and\n                'num_denoising_groups'. It will be used for split outputs of\n                denoising and matching parts and loss calculation.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        (all_layers_matching_cls_scores, all_layers_matching_bbox_preds,\n         all_layers_denoising_cls_scores, all_layers_denoising_bbox_preds) = \\\n            self.split_outputs(\n                all_layers_cls_scores, all_layers_bbox_preds, dn_meta)\n\n        num_dense_queries = dn_meta['num_dense_queries']\n        num_layer = all_layers_matching_bbox_preds.size(0)\n        dense_all_layers_matching_cls_scores = all_layers_matching_cls_scores[:, :,  # noqa: E501\n                                                                              -num_dense_queries:]  # noqa: E501\n        dense_all_layers_matching_bbox_preds = all_layers_matching_bbox_preds[:, :,  # noqa: E501\n                                                                              -num_dense_queries:]  # noqa: E501\n\n        all_layers_matching_cls_scores = all_layers_matching_cls_scores[:, :, :  # noqa: E501\n                                                                        -num_dense_queries]  # noqa: E501\n        all_layers_matching_bbox_preds = all_layers_matching_bbox_preds[:, :, :  # noqa: E501\n                                                                        -num_dense_queries]  # noqa: E501\n\n        loss_dict = self.loss_for_distinct_queries(\n            all_layers_matching_cls_scores, all_layers_matching_bbox_preds,\n            batch_gt_instances, batch_img_metas, batch_gt_instances_ignore)\n\n        if enc_cls_scores is not None:\n\n            enc_loss_cls, enc_losses_bbox, enc_losses_iou = \\\n                self.loss_by_feat_single(\n                    enc_cls_scores, enc_bbox_preds,\n                    batch_gt_instances=batch_gt_instances,\n                    batch_img_metas=batch_img_metas)\n            loss_dict['enc_loss_cls'] = enc_loss_cls\n            loss_dict['enc_loss_bbox'] = enc_losses_bbox\n            loss_dict['enc_loss_iou'] = enc_losses_iou\n\n        if all_layers_denoising_cls_scores is not None:\n            dn_losses_cls, dn_losses_bbox, dn_losses_iou = self.loss_dn(\n                all_layers_denoising_cls_scores,\n                all_layers_denoising_bbox_preds,\n                batch_gt_instances=batch_gt_instances,\n                batch_img_metas=batch_img_metas,\n                dn_meta=dn_meta)\n            loss_dict['dn_loss_cls'] = dn_losses_cls[-1]\n            loss_dict['dn_loss_bbox'] = dn_losses_bbox[-1]\n            loss_dict['dn_loss_iou'] = dn_losses_iou[-1]\n            for num_dec_layer, (loss_cls_i, loss_bbox_i, loss_iou_i) in \\\n                    enumerate(zip(dn_losses_cls[:-1], dn_losses_bbox[:-1],\n                                  dn_losses_iou[:-1])):\n                loss_dict[f'd{num_dec_layer}.dn_loss_cls'] = loss_cls_i\n                loss_dict[f'd{num_dec_layer}.dn_loss_bbox'] = loss_bbox_i\n                loss_dict[f'd{num_dec_layer}.dn_loss_iou'] = loss_iou_i\n\n        for l_id in range(num_layer):\n            cls_scores = dense_all_layers_matching_cls_scores[l_id].sigmoid()\n            bbox_preds = dense_all_layers_matching_bbox_preds[l_id]\n\n            bbox_preds = bbox_cxcywh_to_xyxy(bbox_preds)\n            bbox_preds_list = []\n            for img_id in range(len(bbox_preds)):\n                det_bboxes = bbox_preds[img_id]\n                img_shape = batch_img_metas[img_id]['img_shape']\n                det_bboxes[:, 0::2] = det_bboxes[:, 0::2] * img_shape[1]\n                det_bboxes[:, 1::2] = det_bboxes[:, 1::2] * img_shape[0]\n                bbox_preds_list.append(det_bboxes)\n            bbox_preds = torch.stack(bbox_preds_list)\n            aux_loss = self.aux_loss_for_dense.loss(\n                cls_scores, bbox_preds,\n                [item.bboxes for item in batch_gt_instances],\n                [item.labels for item in batch_gt_instances], batch_img_metas)\n            for k, v in aux_loss.items():\n                loss_dict[f'{l_id}_aux_{k}'] = v\n\n        return loss_dict\n\n    def loss_for_distinct_queries(\n        self,\n        all_layers_cls_scores: Tensor,\n        all_layers_bbox_preds: Tensor,\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss of distinct queries, that is, excluding denoising\n        and dense queries. Only select the distinct queries in decoder for\n        loss.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs,\n                num_queries, cls_out_channels).\n            all_layers_bbox_preds (Tensor): Bbox coordinates of all decoder\n                layers. It has shape (num_decoder_layers, bs,\n                num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image,\n            e.g., image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert batch_gt_instances_ignore is None, \\\n            f'{self.__class__.__name__} only supports ' \\\n            'for batch_gt_instances_ignore setting to None.'\n\n        losses_cls, losses_bbox, losses_iou = multi_apply(\n            self._loss_for_distinct_queries_single,\n            all_layers_cls_scores,\n            all_layers_bbox_preds,\n            [i for i in range(len(all_layers_bbox_preds))],\n            batch_gt_instances=batch_gt_instances,\n            batch_img_metas=batch_img_metas)\n\n        loss_dict = dict()\n        # loss from the last decoder layer\n        loss_dict['loss_cls'] = losses_cls[-1]\n        loss_dict['loss_bbox'] = losses_bbox[-1]\n        loss_dict['loss_iou'] = losses_iou[-1]\n        # loss from other decoder layers\n        num_dec_layer = 0\n        for loss_cls_i, loss_bbox_i, loss_iou_i in \\\n                zip(losses_cls[:-1], losses_bbox[:-1], losses_iou[:-1]):\n            loss_dict[f'd{num_dec_layer}.loss_cls'] = loss_cls_i\n            loss_dict[f'd{num_dec_layer}.loss_bbox'] = loss_bbox_i\n            loss_dict[f'd{num_dec_layer}.loss_iou'] = loss_iou_i\n            num_dec_layer += 1\n        return loss_dict\n\n    def _loss_for_distinct_queries_single(self, cls_scores, bbox_preds, l_id,\n                                          batch_gt_instances, batch_img_metas):\n        \"\"\"Calculate the loss for outputs from a single decoder layer of\n        distinct queries, that is, excluding denoising and dense queries. Only\n        select the distinct queries in decoder for loss.\n\n        Args:\n            cls_scores (Tensor): Classification scores of a single\n                decoder layer, has shape (bs, num_queries, cls_out_channels).\n            bbox_preds (Tensor): Bbox coordinates of a single decoder\n                layer. It has shape (bs, num_queries, 4) with the last\n                dimension arranged as (cx, cy, w, h).\n            l_id (int): Decoder layer index for these outputs.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image,\n            e.g., image size, scaling factor, etc.\n\n        Returns:\n            Tuple[Tensor]: A tuple including `loss_cls`, `loss_box` and\n            `loss_iou`.\n        \"\"\"\n        num_imgs = cls_scores.size(0)\n        if 0 < l_id:\n            batch_mask = [\n                self.cache_dict['distinct_query_mask'][l_id - 1][\n                    img_id * self.cache_dict['num_heads']][0]\n                for img_id in range(num_imgs)\n            ]\n        else:\n            batch_mask = [\n                torch.ones(len(cls_scores[i]),\n                           device=cls_scores.device).bool()\n                for i in range(num_imgs)\n            ]\n        # only select the distinct queries in decoder for loss\n        cls_scores_list = [\n            cls_scores[i][batch_mask[i]] for i in range(num_imgs)\n        ]\n        bbox_preds_list = [\n            bbox_preds[i][batch_mask[i]] for i in range(num_imgs)\n        ]\n        cls_scores = torch.cat(cls_scores_list)\n\n        cls_reg_targets = self.get_targets(cls_scores_list, bbox_preds_list,\n                                           batch_gt_instances, batch_img_metas)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         num_total_pos, num_total_neg) = cls_reg_targets\n        labels = torch.cat(labels_list, 0)\n        label_weights = torch.cat(label_weights_list, 0)\n        bbox_targets = torch.cat(bbox_targets_list, 0)\n        bbox_weights = torch.cat(bbox_weights_list, 0)\n\n        # classification loss\n        cls_scores = cls_scores.reshape(-1, self.cls_out_channels)\n        # construct weighted avg_factor to match with the official DETR repo\n        cls_avg_factor = num_total_pos * 1.0 + \\\n            num_total_neg * self.bg_cls_weight\n        if self.sync_cls_avg_factor:\n            cls_avg_factor = reduce_mean(\n                cls_scores.new_tensor([cls_avg_factor]))\n        cls_avg_factor = max(cls_avg_factor, 1)\n\n        loss_cls = self.loss_cls(\n            cls_scores, labels, label_weights, avg_factor=cls_avg_factor)\n\n        # Compute the average number of gt boxes across all gpus, for\n        # normalization purposes\n        num_total_pos = loss_cls.new_tensor([num_total_pos])\n        num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()\n\n        # construct factors used for rescale bboxes\n        factors = []\n        for img_meta, bbox_pred in zip(batch_img_metas, bbox_preds_list):\n            img_h, img_w, = img_meta['img_shape']\n            factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                           img_h]).unsqueeze(0).repeat(\n                                               bbox_pred.size(0), 1)\n            factors.append(factor)\n        factors = torch.cat(factors, 0)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image,\n        # thus the learning target is normalized by the image size. So here\n        # we need to re-scale them for calculating IoU loss\n        bbox_preds = torch.cat(bbox_preds_list)\n        bbox_preds = bbox_preds.reshape(-1, 4)\n        bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors\n        bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors\n\n        # regression IoU loss, defaultly GIoU loss\n        loss_iou = self.loss_iou(\n            bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)\n\n        # regression L1 loss\n        loss_bbox = self.loss_bbox(\n            bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)\n        return loss_cls, loss_bbox, loss_iou\n\n    def predict_by_feat(self,\n                        layer_cls_scores: Tensor,\n                        layer_bbox_preds: Tensor,\n                        batch_img_metas: List[dict],\n                        rescale: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Args:\n            layer_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs,\n                num_queries, cls_out_channels).\n            layer_bbox_preds (Tensor): Bbox coordinates of all decoder layers.\n                Each has shape (num_decoder_layers, bs, num_queries, 4)\n                with normalized coordinate format (cx, cy, w, h).\n            batch_img_metas (list[dict]): Meta information of each image.\n            rescale (bool, optional): If `True`, return boxes in original\n                image space. Default `False`.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        cls_scores = layer_cls_scores[-1]\n        bbox_preds = layer_bbox_preds[-1]\n\n        num_imgs = cls_scores.size(0)\n        # -1 is last layer input query mask\n\n        batch_mask = [\n            self.cache_dict['distinct_query_mask'][-1][\n                img_id * self.cache_dict['num_heads']][0]\n            for img_id in range(num_imgs)\n        ]\n\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score = cls_scores[img_id][batch_mask[img_id]]\n            bbox_pred = bbox_preds[img_id][batch_mask[img_id]]\n            img_meta = batch_img_metas[img_id]\n            results = self._predict_by_feat_single(cls_score, bbox_pred,\n                                                   img_meta, rescale)\n            result_list.append(results)\n        return result_list\n"
  },
  {
    "path": "mmdet/models/dense_heads/deformable_detr_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Dict, List, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import Linear\nfrom mmengine.model import bias_init_with_prob, constant_init\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import InstanceList, OptInstanceList\nfrom ..layers import inverse_sigmoid\nfrom .detr_head import DETRHead\n\n\n@MODELS.register_module()\nclass DeformableDETRHead(DETRHead):\n    r\"\"\"Head of DeformDETR: Deformable DETR: Deformable Transformers for\n    End-to-End Object Detection.\n\n    Code is modified from the `official github repo\n    <https://github.com/fundamentalvision/Deformable-DETR>`_.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/2010.04159>`_ .\n\n    Args:\n        share_pred_layer (bool): Whether to share parameters for all the\n            prediction layers. Defaults to `False`.\n        num_pred_layer (int): The number of the prediction layers.\n            Defaults to 6.\n        as_two_stage (bool, optional): Whether to generate the proposal\n            from the outputs of encoder. Defaults to `False`.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 share_pred_layer: bool = False,\n                 num_pred_layer: int = 6,\n                 as_two_stage: bool = False,\n                 **kwargs) -> None:\n        self.share_pred_layer = share_pred_layer\n        self.num_pred_layer = num_pred_layer\n        self.as_two_stage = as_two_stage\n\n        super().__init__(*args, **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize classification branch and regression branch of head.\"\"\"\n        fc_cls = Linear(self.embed_dims, self.cls_out_channels)\n        reg_branch = []\n        for _ in range(self.num_reg_fcs):\n            reg_branch.append(Linear(self.embed_dims, self.embed_dims))\n            reg_branch.append(nn.ReLU())\n        reg_branch.append(Linear(self.embed_dims, 4))\n        reg_branch = nn.Sequential(*reg_branch)\n\n        if self.share_pred_layer:\n            self.cls_branches = nn.ModuleList(\n                [fc_cls for _ in range(self.num_pred_layer)])\n            self.reg_branches = nn.ModuleList(\n                [reg_branch for _ in range(self.num_pred_layer)])\n        else:\n            self.cls_branches = nn.ModuleList(\n                [copy.deepcopy(fc_cls) for _ in range(self.num_pred_layer)])\n            self.reg_branches = nn.ModuleList([\n                copy.deepcopy(reg_branch) for _ in range(self.num_pred_layer)\n            ])\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the Deformable DETR head.\"\"\"\n        if self.loss_cls.use_sigmoid:\n            bias_init = bias_init_with_prob(0.01)\n            for m in self.cls_branches:\n                if hasattr(m, 'bias') and m.bias is not None:\n                    nn.init.constant_(m.bias, bias_init)\n        for m in self.reg_branches:\n            constant_init(m[-1], 0, bias=0)\n        nn.init.constant_(self.reg_branches[0][-1].bias.data[2:], -2.0)\n        if self.as_two_stage:\n            for m in self.reg_branches:\n                nn.init.constant_(m[-1].bias.data[2:], 0.0)\n\n    def forward(self, hidden_states: Tensor,\n                references: List[Tensor]) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward function.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, bs, num_queries, dim).\n            references (list[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). The `init_reference` has shape (bs,\n                num_queries, 4) when `as_two_stage` of the detector is `True`,\n                otherwise (bs, num_queries, 2). Each `inter_reference` has\n                shape (bs, num_queries, 4) when `with_box_refine` of the\n                detector is `True`, otherwise (bs, num_queries, 2). The\n                coordinates are arranged as (cx, cy) when the last dimension is\n                2, and (cx, cy, w, h) when it is 4.\n\n        Returns:\n            tuple[Tensor]: results of head containing the following tensor.\n\n            - all_layers_outputs_classes (Tensor): Outputs from the\n              classification head, has shape (num_decoder_layers, bs,\n              num_queries, cls_out_channels).\n            - all_layers_outputs_coords (Tensor): Sigmoid outputs from the\n              regression head with normalized coordinate format (cx, cy, w,\n              h), has shape (num_decoder_layers, bs, num_queries, 4) with the\n              last dimension arranged as (cx, cy, w, h).\n        \"\"\"\n        all_layers_outputs_classes = []\n        all_layers_outputs_coords = []\n\n        for layer_id in range(hidden_states.shape[0]):\n            reference = inverse_sigmoid(references[layer_id])\n            # NOTE The last reference will not be used.\n            hidden_state = hidden_states[layer_id]\n            outputs_class = self.cls_branches[layer_id](hidden_state)\n            tmp_reg_preds = self.reg_branches[layer_id](hidden_state)\n            if reference.shape[-1] == 4:\n                # When `layer` is 0 and `as_two_stage` of the detector\n                # is `True`, or when `layer` is greater than 0 and\n                # `with_box_refine` of the detector is `True`.\n                tmp_reg_preds += reference\n            else:\n                # When `layer` is 0 and `as_two_stage` of the detector\n                # is `False`, or when `layer` is greater than 0 and\n                # `with_box_refine` of the detector is `False`.\n                assert reference.shape[-1] == 2\n                tmp_reg_preds[..., :2] += reference\n            outputs_coord = tmp_reg_preds.sigmoid()\n            all_layers_outputs_classes.append(outputs_class)\n            all_layers_outputs_coords.append(outputs_coord)\n\n        all_layers_outputs_classes = torch.stack(all_layers_outputs_classes)\n        all_layers_outputs_coords = torch.stack(all_layers_outputs_coords)\n\n        return all_layers_outputs_classes, all_layers_outputs_coords\n\n    def loss(self, hidden_states: Tensor, references: List[Tensor],\n             enc_outputs_class: Tensor, enc_outputs_coord: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the queries of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, num_queries, bs, dim).\n            references (list[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). The `init_reference` has shape (bs,\n                num_queries, 4) when `as_two_stage` of the detector is `True`,\n                otherwise (bs, num_queries, 2). Each `inter_reference` has\n                shape (bs, num_queries, 4) when `with_box_refine` of the\n                detector is `True`, otherwise (bs, num_queries, 2). The\n                coordinates are arranged as (cx, cy) when the last dimension is\n                2, and (cx, cy, w, h) when it is 4.\n            enc_outputs_class (Tensor): The score of each point on encode\n                feature map, has shape (bs, num_feat_points, cls_out_channels).\n                Only when `as_two_stage` is `True` it would be passed in,\n                otherwise it would be `None`.\n            enc_outputs_coord (Tensor): The proposal generate from the encode\n                feature map, has shape (bs, num_feat_points, 4) with the last\n                dimension arranged as (cx, cy, w, h). Only when `as_two_stage`\n                is `True` it would be passed in, otherwise it would be `None`.\n            batch_data_samples (list[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states, references)\n        loss_inputs = outs + (enc_outputs_class, enc_outputs_coord,\n                              batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(*loss_inputs)\n        return losses\n\n    def loss_by_feat(\n        self,\n        all_layers_cls_scores: Tensor,\n        all_layers_bbox_preds: Tensor,\n        enc_cls_scores: Tensor,\n        enc_bbox_preds: Tensor,\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Loss function.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs, num_queries,\n                cls_out_channels).\n            all_layers_bbox_preds (Tensor): Regression outputs of all decoder\n                layers. Each is a 4D-tensor with normalized coordinate format\n                (cx, cy, w, h) and has shape (num_decoder_layers, bs,\n                num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            enc_cls_scores (Tensor): The score of each point on encode\n                feature map, has shape (bs, num_feat_points, cls_out_channels).\n                Only when `as_two_stage` is `True` it would be passes in,\n                otherwise, it would be `None`.\n            enc_bbox_preds (Tensor): The proposal generate from the encode\n                feature map, has shape (bs, num_feat_points, 4) with the last\n                dimension arranged as (cx, cy, w, h). Only when `as_two_stage`\n                is `True` it would be passed in, otherwise it would be `None`.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        loss_dict = super().loss_by_feat(all_layers_cls_scores,\n                                         all_layers_bbox_preds,\n                                         batch_gt_instances, batch_img_metas,\n                                         batch_gt_instances_ignore)\n\n        # loss of proposal generated from encode feature map.\n        if enc_cls_scores is not None:\n            proposal_gt_instances = copy.deepcopy(batch_gt_instances)\n            for i in range(len(proposal_gt_instances)):\n                proposal_gt_instances[i].labels = torch.zeros_like(\n                    proposal_gt_instances[i].labels)\n            enc_loss_cls, enc_losses_bbox, enc_losses_iou = \\\n                self.loss_by_feat_single(\n                    enc_cls_scores, enc_bbox_preds,\n                    batch_gt_instances=proposal_gt_instances,\n                    batch_img_metas=batch_img_metas)\n            loss_dict['enc_loss_cls'] = enc_loss_cls\n            loss_dict['enc_loss_bbox'] = enc_losses_bbox\n            loss_dict['enc_loss_iou'] = enc_losses_iou\n        return loss_dict\n\n    def predict(self,\n                hidden_states: Tensor,\n                references: List[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> InstanceList:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the queries of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, num_queries, bs, dim).\n            references (list[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). The `init_reference` has shape (bs,\n                num_queries, 4) when `as_two_stage` of the detector is `True`,\n                otherwise (bs, num_queries, 2). Each `inter_reference` has\n                shape (bs, num_queries, 4) when `with_box_refine` of the\n                detector is `True`, otherwise (bs, num_queries, 2). The\n                coordinates are arranged as (cx, cy) when the last dimension is\n                2, and (cx, cy, w, h) when it is 4.\n            batch_data_samples (list[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): If `True`, return boxes in original\n                image space. Defaults to `True`.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        outs = self(hidden_states, references)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, rescale=rescale)\n        return predictions\n\n    def predict_by_feat(self,\n                        all_layers_cls_scores: Tensor,\n                        all_layers_bbox_preds: Tensor,\n                        batch_img_metas: List[Dict],\n                        rescale: bool = False) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs, num_queries,\n                cls_out_channels).\n            all_layers_bbox_preds (Tensor): Regression outputs of all decoder\n                layers. Each is a 4D-tensor with normalized coordinate format\n                (cx, cy, w, h) and shape (num_decoder_layers, bs, num_queries,\n                4) with the last dimension arranged as (cx, cy, w, h).\n            batch_img_metas (list[dict]): Meta information of each image.\n            rescale (bool, optional): If `True`, return boxes in original\n                image space. Default `False`.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        cls_scores = all_layers_cls_scores[-1]\n        bbox_preds = all_layers_bbox_preds[-1]\n\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score = cls_scores[img_id]\n            bbox_pred = bbox_preds[img_id]\n            img_meta = batch_img_metas[img_id]\n            results = self._predict_by_feat_single(cls_score, bbox_pred,\n                                                   img_meta, rescale)\n            result_list.append(results)\n        return result_list\n"
  },
  {
    "path": "mmdet/models/dense_heads/dense_test_mixins.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport sys\nimport warnings\nfrom inspect import signature\n\nimport torch\nfrom mmcv.ops import batched_nms\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.structures.bbox import bbox_mapping_back\nfrom ..test_time_augs import merge_aug_proposals\n\nif sys.version_info >= (3, 7):\n    from mmdet.utils.contextmanagers import completed\n\n\nclass BBoxTestMixin(object):\n    \"\"\"Mixin class for testing det bboxes via DenseHead.\"\"\"\n\n    def simple_test_bboxes(self, feats, img_metas, rescale=False):\n        \"\"\"Test det bboxes without test-time augmentation, can be applied in\n        DenseHead except for ``RPNHead`` and its variants, e.g., ``GARPNHead``,\n        etc.\n\n        Args:\n            feats (tuple[torch.Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            img_metas (list[dict]): List of image information.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each\n                image after the post process. \\\n                Each item usually contains following keys. \\\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance,)\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances,).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        warnings.warn('You are calling `simple_test_bboxes` in '\n                      '`dense_test_mixins`, but the `dense_test_mixins`'\n                      'will be deprecated soon. Please use '\n                      '`simple_test` instead.')\n        outs = self.forward(feats)\n        results_list = self.get_results(\n            *outs, img_metas=img_metas, rescale=rescale)\n        return results_list\n\n    def aug_test_bboxes(self, feats, img_metas, rescale=False):\n        \"\"\"Test det bboxes with test time augmentation, can be applied in\n        DenseHead except for ``RPNHead`` and its variants, e.g., ``GARPNHead``,\n        etc.\n\n        Args:\n            feats (list[Tensor]): the outer list indicates test-time\n                augmentations and inner Tensor should have a shape NxCxHxW,\n                which contains features for all images in the batch.\n            img_metas (list[list[dict]]): the outer list indicates test-time\n                augs (multiscale, flip, etc.) and the inner list indicates\n                images in a batch. each dict has image information.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple.\n                The first item is ``bboxes`` with shape (n, 5),\n                where 5 represent (tl_x, tl_y, br_x, br_y, score).\n                The shape of the second tensor in the tuple is ``labels``\n                with shape (n,). The length of list should always be 1.\n        \"\"\"\n\n        warnings.warn('You are calling `aug_test_bboxes` in '\n                      '`dense_test_mixins`, but the `dense_test_mixins`'\n                      'will be deprecated soon. Please use '\n                      '`aug_test` instead.')\n        # check with_nms argument\n        gb_sig = signature(self.get_results)\n        gb_args = [p.name for p in gb_sig.parameters.values()]\n        gbs_sig = signature(self._get_results_single)\n        gbs_args = [p.name for p in gbs_sig.parameters.values()]\n        assert ('with_nms' in gb_args) and ('with_nms' in gbs_args), \\\n            f'{self.__class__.__name__}' \\\n            ' does not support test-time augmentation'\n\n        aug_bboxes = []\n        aug_scores = []\n        aug_labels = []\n        for x, img_meta in zip(feats, img_metas):\n            # only one image in the batch\n            outs = self.forward(x)\n            bbox_outputs = self.get_results(\n                *outs,\n                img_metas=img_meta,\n                cfg=self.test_cfg,\n                rescale=False,\n                with_nms=False)[0]\n            aug_bboxes.append(bbox_outputs.bboxes)\n            aug_scores.append(bbox_outputs.scores)\n            if len(bbox_outputs) >= 3:\n                aug_labels.append(bbox_outputs.labels)\n\n        # after merging, bboxes will be rescaled to the original image size\n        merged_bboxes, merged_scores = self.merge_aug_bboxes(\n            aug_bboxes, aug_scores, img_metas)\n        merged_labels = torch.cat(aug_labels, dim=0) if aug_labels else None\n\n        if merged_bboxes.numel() == 0:\n            det_bboxes = torch.cat([merged_bboxes, merged_scores[:, None]], -1)\n            return [\n                (det_bboxes, merged_labels),\n            ]\n\n        det_bboxes, keep_idxs = batched_nms(merged_bboxes, merged_scores,\n                                            merged_labels, self.test_cfg.nms)\n        det_bboxes = det_bboxes[:self.test_cfg.max_per_img]\n        det_labels = merged_labels[keep_idxs][:self.test_cfg.max_per_img]\n\n        if rescale:\n            _det_bboxes = det_bboxes\n        else:\n            _det_bboxes = det_bboxes.clone()\n            _det_bboxes[:, :4] *= det_bboxes.new_tensor(\n                img_metas[0][0]['scale_factor'])\n\n        results = InstanceData()\n        results.bboxes = _det_bboxes[:, :4]\n        results.scores = _det_bboxes[:, 4]\n        results.labels = det_labels\n        return [results]\n\n    def aug_test_rpn(self, feats, img_metas):\n        \"\"\"Test with augmentation for only for ``RPNHead`` and its variants,\n        e.g., ``GARPNHead``, etc.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                        a 4D-tensor.\n            img_metas (list[dict]): Meta info of each image.\n\n        Returns:\n            list[Tensor]: Proposals of each image, each item has shape (n, 5),\n                where 5 represent (tl_x, tl_y, br_x, br_y, score).\n        \"\"\"\n        samples_per_gpu = len(img_metas[0])\n        aug_proposals = [[] for _ in range(samples_per_gpu)]\n        for x, img_meta in zip(feats, img_metas):\n            results_list = self.simple_test_rpn(x, img_meta)\n            for i, results in enumerate(results_list):\n                proposals = torch.cat(\n                    [results.bboxes, results.scores[:, None]], dim=-1)\n                aug_proposals[i].append(proposals)\n        # reorganize the order of 'img_metas' to match the dimensions\n        # of 'aug_proposals'\n        aug_img_metas = []\n        for i in range(samples_per_gpu):\n            aug_img_meta = []\n            for j in range(len(img_metas)):\n                aug_img_meta.append(img_metas[j][i])\n            aug_img_metas.append(aug_img_meta)\n        # after merging, proposals will be rescaled to the original image size\n\n        merged_proposals = []\n        for proposals, aug_img_meta in zip(aug_proposals, aug_img_metas):\n            merged_proposal = merge_aug_proposals(proposals, aug_img_meta,\n                                                  self.test_cfg)\n            results = InstanceData()\n            results.bboxes = merged_proposal[:, :4]\n            results.scores = merged_proposal[:, 4]\n            merged_proposals.append(results)\n        return merged_proposals\n\n    if sys.version_info >= (3, 7):\n\n        async def async_simple_test_rpn(self, x, img_metas):\n            sleep_interval = self.test_cfg.pop('async_sleep_interval', 0.025)\n            async with completed(\n                    __name__, 'rpn_head_forward',\n                    sleep_interval=sleep_interval):\n                rpn_outs = self(x)\n\n            proposal_list = self.get_results(*rpn_outs, img_metas=img_metas)\n            return proposal_list\n\n    def merge_aug_bboxes(self, aug_bboxes, aug_scores, img_metas):\n        \"\"\"Merge augmented detection bboxes and scores.\n\n        Args:\n            aug_bboxes (list[Tensor]): shape (n, 4*#class)\n            aug_scores (list[Tensor] or None): shape (n, #class)\n            img_shapes (list[Tensor]): shape (3, ).\n\n        Returns:\n            tuple[Tensor]: ``bboxes`` with shape (n,4), where\n            4 represent (tl_x, tl_y, br_x, br_y)\n            and ``scores`` with shape (n,).\n        \"\"\"\n        recovered_bboxes = []\n        for bboxes, img_info in zip(aug_bboxes, img_metas):\n            img_shape = img_info[0]['img_shape']\n            scale_factor = img_info[0]['scale_factor']\n            flip = img_info[0]['flip']\n            flip_direction = img_info[0]['flip_direction']\n            bboxes = bbox_mapping_back(bboxes, img_shape, scale_factor, flip,\n                                       flip_direction)\n            recovered_bboxes.append(bboxes)\n        bboxes = torch.cat(recovered_bboxes, dim=0)\n        if aug_scores is None:\n            return bboxes\n        else:\n            scores = torch.cat(aug_scores, dim=0)\n            return bboxes, scores\n"
  },
  {
    "path": "mmdet/models/dense_heads/detr_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Linear\nfrom mmcv.cnn.bricks.transformer import FFN\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import (bbox_cxcywh_to_xyxy, bbox_overlaps,\n                                   bbox_xyxy_to_cxcywh)\nfrom mmdet.utils import (ConfigType, InstanceList, OptInstanceList,\n                         OptMultiConfig, reduce_mean)\nfrom ..losses import QualityFocalLoss\nfrom ..utils import multi_apply\n\n\n@MODELS.register_module()\nclass DETRHead(BaseModule):\n    r\"\"\"Head of DETR. DETR:End-to-End Object Detection with Transformers.\n\n    More details can be found in the `paper\n    <https://arxiv.org/pdf/2005.12872>`_ .\n\n    Args:\n        num_classes (int): Number of categories excluding the background.\n        embed_dims (int): The dims of Transformer embedding.\n        num_reg_fcs (int): Number of fully-connected layers used in `FFN`,\n            which is then used for the regression head. Defaults to 2.\n        sync_cls_avg_factor (bool): Whether to sync the `avg_factor` of\n            all ranks. Default to `False`.\n        loss_cls (:obj:`ConfigDict` or dict): Config of the classification\n            loss. Defaults to `CrossEntropyLoss`.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of the regression bbox\n            loss. Defaults to `L1Loss`.\n        loss_iou (:obj:`ConfigDict` or dict): Config of the regression iou\n            loss. Defaults to `GIoULoss`.\n        train_cfg (:obj:`ConfigDict` or dict): Training config of transformer\n            head.\n        test_cfg (:obj:`ConfigDict` or dict): Testing config of transformer\n            head.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    _version = 2\n\n    def __init__(\n            self,\n            num_classes: int,\n            embed_dims: int = 256,\n            num_reg_fcs: int = 2,\n            sync_cls_avg_factor: bool = False,\n            loss_cls: ConfigType = dict(\n                type='CrossEntropyLoss',\n                bg_cls_weight=0.1,\n                use_sigmoid=False,\n                loss_weight=1.0,\n                class_weight=1.0),\n            loss_bbox: ConfigType = dict(type='L1Loss', loss_weight=5.0),\n            loss_iou: ConfigType = dict(type='GIoULoss', loss_weight=2.0),\n            train_cfg: ConfigType = dict(\n                assigner=dict(\n                    type='HungarianAssigner',\n                    match_costs=[\n                        dict(type='ClassificationCost', weight=1.),\n                        dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                        dict(type='IoUCost', iou_mode='giou', weight=2.0)\n                    ])),\n            test_cfg: ConfigType = dict(max_per_img=100),\n            init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.bg_cls_weight = 0\n        self.sync_cls_avg_factor = sync_cls_avg_factor\n        class_weight = loss_cls.get('class_weight', None)\n        if class_weight is not None and (self.__class__ is DETRHead):\n            assert isinstance(class_weight, float), 'Expected ' \\\n                'class_weight to have type float. Found ' \\\n                f'{type(class_weight)}.'\n            # NOTE following the official DETR repo, bg_cls_weight means\n            # relative classification weight of the no-object class.\n            bg_cls_weight = loss_cls.get('bg_cls_weight', class_weight)\n            assert isinstance(bg_cls_weight, float), 'Expected ' \\\n                'bg_cls_weight to have type float. Found ' \\\n                f'{type(bg_cls_weight)}.'\n            class_weight = torch.ones(num_classes + 1) * class_weight\n            # set background class as the last indice\n            class_weight[num_classes] = bg_cls_weight\n            loss_cls.update({'class_weight': class_weight})\n            if 'bg_cls_weight' in loss_cls:\n                loss_cls.pop('bg_cls_weight')\n            self.bg_cls_weight = bg_cls_weight\n\n        if train_cfg:\n            assert 'assigner' in train_cfg, 'assigner should be provided ' \\\n                                            'when train_cfg is set.'\n            assigner = train_cfg['assigner']\n            self.assigner = TASK_UTILS.build(assigner)\n            if train_cfg.get('sampler', None) is not None:\n                raise RuntimeError('DETR do not build sampler.')\n        self.num_classes = num_classes\n        self.embed_dims = embed_dims\n        self.num_reg_fcs = num_reg_fcs\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox = MODELS.build(loss_bbox)\n        self.loss_iou = MODELS.build(loss_iou)\n\n        if self.loss_cls.use_sigmoid:\n            self.cls_out_channels = num_classes\n        else:\n            self.cls_out_channels = num_classes + 1\n\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the transformer head.\"\"\"\n        # cls branch\n        self.fc_cls = Linear(self.embed_dims, self.cls_out_channels)\n        # reg branch\n        self.activate = nn.ReLU()\n        self.reg_ffn = FFN(\n            self.embed_dims,\n            self.embed_dims,\n            self.num_reg_fcs,\n            dict(type='ReLU', inplace=True),\n            dropout=0.0,\n            add_residual=False)\n        # NOTE the activations of reg_branch here is the same as\n        # those in transformer, but they are actually different\n        # in DAB-DETR (prelu in transformer and relu in reg_branch)\n        self.fc_reg = Linear(self.embed_dims, 4)\n\n    def forward(self, hidden_states: Tensor) -> Tuple[Tensor]:\n        \"\"\"\"Forward function.\n\n        Args:\n            hidden_states (Tensor): Features from transformer decoder. If\n                `return_intermediate_dec` in detr.py is True output has shape\n                (num_decoder_layers, bs, num_queries, dim), else has shape\n                (1, bs, num_queries, dim) which only contains the last layer\n                outputs.\n        Returns:\n            tuple[Tensor]: results of head containing the following tensor.\n\n            - layers_cls_scores (Tensor): Outputs from the classification head,\n              shape (num_decoder_layers, bs, num_queries, cls_out_channels).\n              Note cls_out_channels should include background.\n            - layers_bbox_preds (Tensor): Sigmoid outputs from the regression\n              head with normalized coordinate format (cx, cy, w, h), has shape\n              (num_decoder_layers, bs, num_queries, 4).\n        \"\"\"\n        layers_cls_scores = self.fc_cls(hidden_states)\n        layers_bbox_preds = self.fc_reg(\n            self.activate(self.reg_ffn(hidden_states))).sigmoid()\n        return layers_cls_scores, layers_bbox_preds\n\n    def loss(self, hidden_states: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the features of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Feature from the transformer decoder, has\n                shape (num_decoder_layers, bs, num_queries, cls_out_channels)\n                or (num_decoder_layers, num_queries, bs, cls_out_channels).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states)\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(*loss_inputs)\n        return losses\n\n    def loss_by_feat(\n        self,\n        all_layers_cls_scores: Tensor,\n        all_layers_bbox_preds: Tensor,\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"\"Loss function.\n\n        Only outputs from the last feature level are used for computing\n        losses by default.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification outputs\n                of each decoder layers. Each is a 4D-tensor, has shape\n                (num_decoder_layers, bs, num_queries, cls_out_channels).\n            all_layers_bbox_preds (Tensor): Sigmoid regression\n                outputs of each decoder layers. Each is a 4D-tensor with\n                normalized coordinate format (cx, cy, w, h) and shape\n                (num_decoder_layers, bs, num_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert batch_gt_instances_ignore is None, \\\n            f'{self.__class__.__name__} only supports ' \\\n            'for batch_gt_instances_ignore setting to None.'\n\n        losses_cls, losses_bbox, losses_iou = multi_apply(\n            self.loss_by_feat_single,\n            all_layers_cls_scores,\n            all_layers_bbox_preds,\n            batch_gt_instances=batch_gt_instances,\n            batch_img_metas=batch_img_metas)\n\n        loss_dict = dict()\n        # loss from the last decoder layer\n        loss_dict['loss_cls'] = losses_cls[-1]\n        loss_dict['loss_bbox'] = losses_bbox[-1]\n        loss_dict['loss_iou'] = losses_iou[-1]\n        # loss from other decoder layers\n        num_dec_layer = 0\n        for loss_cls_i, loss_bbox_i, loss_iou_i in \\\n                zip(losses_cls[:-1], losses_bbox[:-1], losses_iou[:-1]):\n            loss_dict[f'd{num_dec_layer}.loss_cls'] = loss_cls_i\n            loss_dict[f'd{num_dec_layer}.loss_bbox'] = loss_bbox_i\n            loss_dict[f'd{num_dec_layer}.loss_iou'] = loss_iou_i\n            num_dec_layer += 1\n        return loss_dict\n\n    def loss_by_feat_single(self, cls_scores: Tensor, bbox_preds: Tensor,\n                            batch_gt_instances: InstanceList,\n                            batch_img_metas: List[dict]) -> Tuple[Tensor]:\n        \"\"\"Loss function for outputs from a single decoder layer of a single\n        feature level.\n\n        Args:\n            cls_scores (Tensor): Box score logits from a single decoder layer\n                for all images, has shape (bs, num_queries, cls_out_channels).\n            bbox_preds (Tensor): Sigmoid outputs from a single decoder layer\n                for all images, with normalized coordinate (cx, cy, w, h) and\n                shape (bs, num_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n\n        Returns:\n            Tuple[Tensor]: A tuple including `loss_cls`, `loss_box` and\n            `loss_iou`.\n        \"\"\"\n        num_imgs = cls_scores.size(0)\n        cls_scores_list = [cls_scores[i] for i in range(num_imgs)]\n        bbox_preds_list = [bbox_preds[i] for i in range(num_imgs)]\n        cls_reg_targets = self.get_targets(cls_scores_list, bbox_preds_list,\n                                           batch_gt_instances, batch_img_metas)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         num_total_pos, num_total_neg) = cls_reg_targets\n        labels = torch.cat(labels_list, 0)\n        label_weights = torch.cat(label_weights_list, 0)\n        bbox_targets = torch.cat(bbox_targets_list, 0)\n        bbox_weights = torch.cat(bbox_weights_list, 0)\n\n        # classification loss\n        cls_scores = cls_scores.reshape(-1, self.cls_out_channels)\n        # construct weighted avg_factor to match with the official DETR repo\n        cls_avg_factor = num_total_pos * 1.0 + \\\n            num_total_neg * self.bg_cls_weight\n        if self.sync_cls_avg_factor:\n            cls_avg_factor = reduce_mean(\n                cls_scores.new_tensor([cls_avg_factor]))\n        cls_avg_factor = max(cls_avg_factor, 1)\n\n        if isinstance(self.loss_cls, QualityFocalLoss):\n            bg_class_ind = self.num_classes\n            pos_inds = ((labels >= 0)\n                        & (labels < bg_class_ind)).nonzero().squeeze(1)\n            scores = label_weights.new_zeros(labels.shape)\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_decode_bbox_targets = bbox_cxcywh_to_xyxy(pos_bbox_targets)\n            pos_bbox_pred = bbox_preds.reshape(-1, 4)[pos_inds]\n            pos_decode_bbox_pred = bbox_cxcywh_to_xyxy(pos_bbox_pred)\n            scores[pos_inds] = bbox_overlaps(\n                pos_decode_bbox_pred.detach(),\n                pos_decode_bbox_targets,\n                is_aligned=True)\n            loss_cls = self.loss_cls(\n                cls_scores, (labels, scores),\n                label_weights,\n                avg_factor=cls_avg_factor)\n        else:\n            loss_cls = self.loss_cls(\n                cls_scores, labels, label_weights, avg_factor=cls_avg_factor)\n\n        # Compute the average number of gt boxes across all gpus, for\n        # normalization purposes\n        num_total_pos = loss_cls.new_tensor([num_total_pos])\n        num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()\n\n        # construct factors used for rescale bboxes\n        factors = []\n        for img_meta, bbox_pred in zip(batch_img_metas, bbox_preds):\n            img_h, img_w, = img_meta['img_shape']\n            factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                           img_h]).unsqueeze(0).repeat(\n                                               bbox_pred.size(0), 1)\n            factors.append(factor)\n        factors = torch.cat(factors, 0)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image,\n        # thus the learning target is normalized by the image size. So here\n        # we need to re-scale them for calculating IoU loss\n        bbox_preds = bbox_preds.reshape(-1, 4)\n        bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors\n        bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors\n\n        # regression IoU loss, defaultly GIoU loss\n        loss_iou = self.loss_iou(\n            bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)\n\n        # regression L1 loss\n        loss_bbox = self.loss_bbox(\n            bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)\n        return loss_cls, loss_bbox, loss_iou\n\n    def get_targets(self, cls_scores_list: List[Tensor],\n                    bbox_preds_list: List[Tensor],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict]) -> tuple:\n        \"\"\"Compute regression and classification targets for a batch image.\n\n        Outputs from a single decoder layer of a single feature level are used.\n\n        Args:\n            cls_scores_list (list[Tensor]): Box score logits from a single\n                decoder layer for each image, has shape [num_queries,\n                cls_out_channels].\n            bbox_preds_list (list[Tensor]): Sigmoid outputs from a single\n                decoder layer for each image, with normalized coordinate\n                (cx, cy, w, h) and shape [num_queries, 4].\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n\n        Returns:\n            tuple: a tuple containing the following targets.\n\n            - labels_list (list[Tensor]): Labels for all images.\n            - label_weights_list (list[Tensor]): Label weights for all images.\n            - bbox_targets_list (list[Tensor]): BBox targets for all images.\n            - bbox_weights_list (list[Tensor]): BBox weights for all images.\n            - num_total_pos (int): Number of positive samples in all images.\n            - num_total_neg (int): Number of negative samples in all images.\n        \"\"\"\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         pos_inds_list,\n         neg_inds_list) = multi_apply(self._get_targets_single,\n                                      cls_scores_list, bbox_preds_list,\n                                      batch_gt_instances, batch_img_metas)\n        num_total_pos = sum((inds.numel() for inds in pos_inds_list))\n        num_total_neg = sum((inds.numel() for inds in neg_inds_list))\n        return (labels_list, label_weights_list, bbox_targets_list,\n                bbox_weights_list, num_total_pos, num_total_neg)\n\n    def _get_targets_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict) -> tuple:\n        \"\"\"Compute regression and classification targets for one image.\n\n        Outputs from a single decoder layer of a single feature level are used.\n\n        Args:\n            cls_score (Tensor): Box score logits from a single decoder layer\n                for one image. Shape [num_queries, cls_out_channels].\n            bbox_pred (Tensor): Sigmoid outputs from a single decoder layer\n                for one image, with normalized coordinate (cx, cy, w, h) and\n                shape [num_queries, 4].\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for one image.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - labels (Tensor): Labels of each image.\n            - label_weights (Tensor]): Label weights of each image.\n            - bbox_targets (Tensor): BBox targets of each image.\n            - bbox_weights (Tensor): BBox weights of each image.\n            - pos_inds (Tensor): Sampled positive indices for each image.\n            - neg_inds (Tensor): Sampled negative indices for each image.\n        \"\"\"\n        img_h, img_w = img_meta['img_shape']\n        factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                       img_h]).unsqueeze(0)\n        num_bboxes = bbox_pred.size(0)\n        # convert bbox_pred from xywh, normalized to xyxy, unnormalized\n        bbox_pred = bbox_cxcywh_to_xyxy(bbox_pred)\n        bbox_pred = bbox_pred * factor\n\n        pred_instances = InstanceData(scores=cls_score, bboxes=bbox_pred)\n        # assigner and sampler\n        assign_result = self.assigner.assign(\n            pred_instances=pred_instances,\n            gt_instances=gt_instances,\n            img_meta=img_meta)\n\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        pos_inds = torch.nonzero(\n            assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique()\n        neg_inds = torch.nonzero(\n            assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique()\n        pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1\n        pos_gt_bboxes = gt_bboxes[pos_assigned_gt_inds.long(), :]\n\n        # label targets\n        labels = gt_bboxes.new_full((num_bboxes, ),\n                                    self.num_classes,\n                                    dtype=torch.long)\n        labels[pos_inds] = gt_labels[pos_assigned_gt_inds]\n        label_weights = gt_bboxes.new_ones(num_bboxes)\n\n        # bbox targets\n        bbox_targets = torch.zeros_like(bbox_pred, dtype=gt_bboxes.dtype)\n        bbox_weights = torch.zeros_like(bbox_pred, dtype=gt_bboxes.dtype)\n        bbox_weights[pos_inds] = 1.0\n\n        # DETR regress the relative position of boxes (cxcywh) in the image.\n        # Thus the learning target should be normalized by the image size, also\n        # the box format should be converted from defaultly x1y1x2y2 to cxcywh.\n        pos_gt_bboxes_normalized = pos_gt_bboxes / factor\n        pos_gt_bboxes_targets = bbox_xyxy_to_cxcywh(pos_gt_bboxes_normalized)\n        bbox_targets[pos_inds] = pos_gt_bboxes_targets\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                neg_inds)\n\n    def loss_and_predict(\n            self, hidden_states: Tuple[Tensor],\n            batch_data_samples: SampleList) -> Tuple[dict, InstanceList]:\n        \"\"\"Perform forward propagation of the head, then calculate loss and\n        predictions from the features and data samples. Over-write because\n        img_metas are needed as inputs for bbox_head.\n\n        Args:\n            hidden_states (tuple[Tensor]): Feature from the transformer\n                decoder, has shape (num_decoder_layers, bs, num_queries, dim).\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n\n        Returns:\n            tuple: the return value is a tuple contains:\n\n            - losses: (dict[str, Tensor]): A dictionary of loss components.\n            - predictions (list[:obj:`InstanceData`]): Detection\n              results of each image after the post process.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states)\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(*loss_inputs)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas)\n        return losses, predictions\n\n    def predict(self,\n                hidden_states: Tuple[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network. Over-write\n        because img_metas are needed as inputs for bbox_head.\n\n        Args:\n            hidden_states (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        last_layer_hidden_state = hidden_states[-1].unsqueeze(0)\n        outs = self(last_layer_hidden_state)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, rescale=rescale)\n\n        return predictions\n\n    def predict_by_feat(self,\n                        layer_cls_scores: Tensor,\n                        layer_bbox_preds: Tensor,\n                        batch_img_metas: List[dict],\n                        rescale: bool = True) -> InstanceList:\n        \"\"\"Transform network outputs for a batch into bbox predictions.\n\n        Args:\n            layer_cls_scores (Tensor): Classification outputs of the last or\n                all decoder layer. Each is a 4D-tensor, has shape\n                (num_decoder_layers, bs, num_queries, cls_out_channels).\n            layer_bbox_preds (Tensor): Sigmoid regression outputs of the last\n                or all decoder layer. Each is a 4D-tensor with normalized\n                coordinate format (cx, cy, w, h) and shape\n                (num_decoder_layers, bs, num_queries, 4).\n            batch_img_metas (list[dict]): Meta information of each image.\n            rescale (bool, optional): If `True`, return boxes in original\n                image space. Defaults to `True`.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        # NOTE only using outputs from the last feature level,\n        # and only the outputs from the last decoder layer is used.\n        cls_scores = layer_cls_scores[-1]\n        bbox_preds = layer_bbox_preds[-1]\n\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score = cls_scores[img_id]\n            bbox_pred = bbox_preds[img_id]\n            img_meta = batch_img_metas[img_id]\n            results = self._predict_by_feat_single(cls_score, bbox_pred,\n                                                   img_meta, rescale)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_score: Tensor,\n                                bbox_pred: Tensor,\n                                img_meta: dict,\n                                rescale: bool = True) -> InstanceData:\n        \"\"\"Transform outputs from the last decoder layer into bbox predictions\n        for each image.\n\n        Args:\n            cls_score (Tensor): Box score logits from the last decoder layer\n                for each image. Shape [num_queries, cls_out_channels].\n            bbox_pred (Tensor): Sigmoid outputs from the last decoder layer\n                for each image, with coordinate format (cx, cy, w, h) and\n                shape [num_queries, 4].\n            img_meta (dict): Image meta info.\n            rescale (bool): If True, return boxes in original image\n                space. Default True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_score) == len(bbox_pred)  # num_queries\n        max_per_img = self.test_cfg.get('max_per_img', len(cls_score))\n        img_shape = img_meta['img_shape']\n        # exclude background\n        if self.loss_cls.use_sigmoid:\n            cls_score = cls_score.sigmoid()\n            scores, indexes = cls_score.view(-1).topk(max_per_img)\n            det_labels = indexes % self.num_classes\n            bbox_index = indexes // self.num_classes\n            bbox_pred = bbox_pred[bbox_index]\n        else:\n            scores, det_labels = F.softmax(cls_score, dim=-1)[..., :-1].max(-1)\n            scores, bbox_index = scores.topk(max_per_img)\n            bbox_pred = bbox_pred[bbox_index]\n            det_labels = det_labels[bbox_index]\n\n        det_bboxes = bbox_cxcywh_to_xyxy(bbox_pred)\n        det_bboxes[:, 0::2] = det_bboxes[:, 0::2] * img_shape[1]\n        det_bboxes[:, 1::2] = det_bboxes[:, 1::2] * img_shape[0]\n        det_bboxes[:, 0::2].clamp_(min=0, max=img_shape[1])\n        det_bboxes[:, 1::2].clamp_(min=0, max=img_shape[0])\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            det_bboxes /= det_bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n\n        results = InstanceData()\n        results.bboxes = det_bboxes\n        results.scores = scores\n        results.labels = det_labels\n        return results\n"
  },
  {
    "path": "mmdet/models/dense_heads/dino_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Tuple\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import (bbox_cxcywh_to_xyxy, bbox_overlaps,\n                                   bbox_xyxy_to_cxcywh)\nfrom mmdet.utils import InstanceList, OptInstanceList, reduce_mean\nfrom ..losses import QualityFocalLoss\nfrom ..utils import multi_apply\nfrom .deformable_detr_head import DeformableDETRHead\n\n\n@MODELS.register_module()\nclass DINOHead(DeformableDETRHead):\n    r\"\"\"Head of the DINO: DETR with Improved DeNoising Anchor Boxes\n    for End-to-End Object Detection\n\n    Code is modified from the `official github repo\n    <https://github.com/IDEA-Research/DINO>`_.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/2203.03605>`_ .\n    \"\"\"\n\n    def loss(self, hidden_states: Tensor, references: List[Tensor],\n             enc_outputs_class: Tensor, enc_outputs_coord: Tensor,\n             batch_data_samples: SampleList, dn_meta: Dict[str, int]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the queries of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, bs, num_queries_total,\n                dim), where `num_queries_total` is the sum of\n                `num_denoising_queries` and `num_matching_queries` when\n                `self.training` is `True`, else `num_matching_queries`.\n            references (list[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). The `init_reference` has shape (bs,\n                num_queries_total, 4) and each `inter_reference` has shape\n                (bs, num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            enc_outputs_class (Tensor): The score of each point on encode\n                feature map, has shape (bs, num_feat_points, cls_out_channels).\n            enc_outputs_coord (Tensor): The proposal generate from the\n                encode feature map, has shape (bs, num_feat_points, 4) with the\n                last dimension arranged as (cx, cy, w, h).\n            batch_data_samples (list[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states, references)\n        loss_inputs = outs + (enc_outputs_class, enc_outputs_coord,\n                              batch_gt_instances, batch_img_metas, dn_meta)\n        losses = self.loss_by_feat(*loss_inputs)\n        return losses\n\n    def loss_by_feat(\n        self,\n        all_layers_cls_scores: Tensor,\n        all_layers_bbox_preds: Tensor,\n        enc_cls_scores: Tensor,\n        enc_bbox_preds: Tensor,\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        dn_meta: Dict[str, int],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Loss function.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs,\n                num_queries_total, cls_out_channels), where\n                `num_queries_total` is the sum of `num_denoising_queries`\n                and `num_matching_queries`.\n            all_layers_bbox_preds (Tensor): Regression outputs of all decoder\n                layers. Each is a 4D-tensor with normalized coordinate format\n                (cx, cy, w, h) and has shape (num_decoder_layers, bs,\n                num_queries_total, 4).\n            enc_cls_scores (Tensor): The score of each point on encode\n                feature map, has shape (bs, num_feat_points, cls_out_channels).\n            enc_bbox_preds (Tensor): The proposal generate from the encode\n                feature map, has shape (bs, num_feat_points, 4) with the last\n                dimension arranged as (cx, cy, w, h).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n                group collation, including 'num_denoising_queries' and\n                'num_denoising_groups'. It will be used for split outputs of\n                denoising and matching parts and loss calculation.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        # extract denoising and matching part of outputs\n        (all_layers_matching_cls_scores, all_layers_matching_bbox_preds,\n         all_layers_denoising_cls_scores, all_layers_denoising_bbox_preds) = \\\n            self.split_outputs(\n                all_layers_cls_scores, all_layers_bbox_preds, dn_meta)\n\n        loss_dict = super(DeformableDETRHead, self).loss_by_feat(\n            all_layers_matching_cls_scores, all_layers_matching_bbox_preds,\n            batch_gt_instances, batch_img_metas, batch_gt_instances_ignore)\n        # NOTE DETRHead.loss_by_feat but not DeformableDETRHead.loss_by_feat\n        # is called, because the encoder loss calculations are different\n        # between DINO and DeformableDETR.\n\n        # loss of proposal generated from encode feature map.\n        if enc_cls_scores is not None:\n            # NOTE The enc_loss calculation of the DINO is\n            # different from that of Deformable DETR.\n            enc_loss_cls, enc_losses_bbox, enc_losses_iou = \\\n                self.loss_by_feat_single(\n                    enc_cls_scores, enc_bbox_preds,\n                    batch_gt_instances=batch_gt_instances,\n                    batch_img_metas=batch_img_metas)\n            loss_dict['enc_loss_cls'] = enc_loss_cls\n            loss_dict['enc_loss_bbox'] = enc_losses_bbox\n            loss_dict['enc_loss_iou'] = enc_losses_iou\n\n        if all_layers_denoising_cls_scores is not None:\n            # calculate denoising loss from all decoder layers\n            dn_losses_cls, dn_losses_bbox, dn_losses_iou = self.loss_dn(\n                all_layers_denoising_cls_scores,\n                all_layers_denoising_bbox_preds,\n                batch_gt_instances=batch_gt_instances,\n                batch_img_metas=batch_img_metas,\n                dn_meta=dn_meta)\n            # collate denoising loss\n            loss_dict['dn_loss_cls'] = dn_losses_cls[-1]\n            loss_dict['dn_loss_bbox'] = dn_losses_bbox[-1]\n            loss_dict['dn_loss_iou'] = dn_losses_iou[-1]\n            for num_dec_layer, (loss_cls_i, loss_bbox_i, loss_iou_i) in \\\n                    enumerate(zip(dn_losses_cls[:-1], dn_losses_bbox[:-1],\n                                  dn_losses_iou[:-1])):\n                loss_dict[f'd{num_dec_layer}.dn_loss_cls'] = loss_cls_i\n                loss_dict[f'd{num_dec_layer}.dn_loss_bbox'] = loss_bbox_i\n                loss_dict[f'd{num_dec_layer}.dn_loss_iou'] = loss_iou_i\n        return loss_dict\n\n    def loss_dn(self, all_layers_denoising_cls_scores: Tensor,\n                all_layers_denoising_bbox_preds: Tensor,\n                batch_gt_instances: InstanceList, batch_img_metas: List[dict],\n                dn_meta: Dict[str, int]) -> Tuple[List[Tensor]]:\n        \"\"\"Calculate denoising loss.\n\n        Args:\n            all_layers_denoising_cls_scores (Tensor): Classification scores of\n                all decoder layers in denoising part, has shape (\n                num_decoder_layers, bs, num_denoising_queries,\n                cls_out_channels).\n            all_layers_denoising_bbox_preds (Tensor): Regression outputs of all\n                decoder layers in denoising part. Each is a 4D-tensor with\n                normalized coordinate format (cx, cy, w, h) and has shape\n                (num_decoder_layers, bs, num_denoising_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            Tuple[List[Tensor]]: The loss_dn_cls, loss_dn_bbox, and loss_dn_iou\n            of each decoder layers.\n        \"\"\"\n        return multi_apply(\n            self._loss_dn_single,\n            all_layers_denoising_cls_scores,\n            all_layers_denoising_bbox_preds,\n            batch_gt_instances=batch_gt_instances,\n            batch_img_metas=batch_img_metas,\n            dn_meta=dn_meta)\n\n    def _loss_dn_single(self, dn_cls_scores: Tensor, dn_bbox_preds: Tensor,\n                        batch_gt_instances: InstanceList,\n                        batch_img_metas: List[dict],\n                        dn_meta: Dict[str, int]) -> Tuple[Tensor]:\n        \"\"\"Denoising loss for outputs from a single decoder layer.\n\n        Args:\n            dn_cls_scores (Tensor): Classification scores of a single decoder\n                layer in denoising part, has shape (bs, num_denoising_queries,\n                cls_out_channels).\n            dn_bbox_preds (Tensor): Regression outputs of a single decoder\n                layer in denoising part. Each is a 4D-tensor with normalized\n                coordinate format (cx, cy, w, h) and has shape\n                (bs, num_denoising_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            Tuple[Tensor]: A tuple including `loss_cls`, `loss_box` and\n            `loss_iou`.\n        \"\"\"\n        cls_reg_targets = self.get_dn_targets(batch_gt_instances,\n                                              batch_img_metas, dn_meta)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         num_total_pos, num_total_neg) = cls_reg_targets\n        labels = torch.cat(labels_list, 0)\n        label_weights = torch.cat(label_weights_list, 0)\n        bbox_targets = torch.cat(bbox_targets_list, 0)\n        bbox_weights = torch.cat(bbox_weights_list, 0)\n\n        # classification loss\n        cls_scores = dn_cls_scores.reshape(-1, self.cls_out_channels)\n        # construct weighted avg_factor to match with the official DETR repo\n        cls_avg_factor = \\\n            num_total_pos * 1.0 + num_total_neg * self.bg_cls_weight\n        if self.sync_cls_avg_factor:\n            cls_avg_factor = reduce_mean(\n                cls_scores.new_tensor([cls_avg_factor]))\n        cls_avg_factor = max(cls_avg_factor, 1)\n\n        if len(cls_scores) > 0:\n            if isinstance(self.loss_cls, QualityFocalLoss):\n                bg_class_ind = self.num_classes\n                pos_inds = ((labels >= 0)\n                            & (labels < bg_class_ind)).nonzero().squeeze(1)\n                scores = label_weights.new_zeros(labels.shape)\n                pos_bbox_targets = bbox_targets[pos_inds]\n                pos_decode_bbox_targets = bbox_cxcywh_to_xyxy(pos_bbox_targets)\n                pos_bbox_pred = dn_bbox_preds.reshape(-1, 4)[pos_inds]\n                pos_decode_bbox_pred = bbox_cxcywh_to_xyxy(pos_bbox_pred)\n                scores[pos_inds] = bbox_overlaps(\n                    pos_decode_bbox_pred.detach(),\n                    pos_decode_bbox_targets,\n                    is_aligned=True)\n                loss_cls = self.loss_cls(\n                    cls_scores, (labels, scores),\n                    weight=label_weights,\n                    avg_factor=cls_avg_factor)\n            else:\n                loss_cls = self.loss_cls(\n                    cls_scores,\n                    labels,\n                    label_weights,\n                    avg_factor=cls_avg_factor)\n        else:\n            loss_cls = torch.zeros(\n                1, dtype=cls_scores.dtype, device=cls_scores.device)\n\n        # Compute the average number of gt boxes across all gpus, for\n        # normalization purposes\n        num_total_pos = loss_cls.new_tensor([num_total_pos])\n        num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()\n\n        # construct factors used for rescale bboxes\n        factors = []\n        for img_meta, bbox_pred in zip(batch_img_metas, dn_bbox_preds):\n            img_h, img_w = img_meta['img_shape']\n            factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                           img_h]).unsqueeze(0).repeat(\n                                               bbox_pred.size(0), 1)\n            factors.append(factor)\n        factors = torch.cat(factors)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image,\n        # thus the learning target is normalized by the image size. So here\n        # we need to re-scale them for calculating IoU loss\n        bbox_preds = dn_bbox_preds.reshape(-1, 4)\n        bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors\n        bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors\n\n        # regression IoU loss, defaultly GIoU loss\n        loss_iou = self.loss_iou(\n            bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)\n\n        # regression L1 loss\n        loss_bbox = self.loss_bbox(\n            bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)\n        return loss_cls, loss_bbox, loss_iou\n\n    def get_dn_targets(self, batch_gt_instances: InstanceList,\n                       batch_img_metas: dict, dn_meta: Dict[str,\n                                                            int]) -> tuple:\n        \"\"\"Get targets in denoising part for a batch of images.\n\n        Args:\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            tuple: a tuple containing the following targets.\n\n            - labels_list (list[Tensor]): Labels for all images.\n            - label_weights_list (list[Tensor]): Label weights for all images.\n            - bbox_targets_list (list[Tensor]): BBox targets for all images.\n            - bbox_weights_list (list[Tensor]): BBox weights for all images.\n            - num_total_pos (int): Number of positive samples in all images.\n            - num_total_neg (int): Number of negative samples in all images.\n        \"\"\"\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         pos_inds_list, neg_inds_list) = multi_apply(\n             self._get_dn_targets_single,\n             batch_gt_instances,\n             batch_img_metas,\n             dn_meta=dn_meta)\n        num_total_pos = sum((inds.numel() for inds in pos_inds_list))\n        num_total_neg = sum((inds.numel() for inds in neg_inds_list))\n        return (labels_list, label_weights_list, bbox_targets_list,\n                bbox_weights_list, num_total_pos, num_total_neg)\n\n    def _get_dn_targets_single(self, gt_instances: InstanceData,\n                               img_meta: dict, dn_meta: Dict[str,\n                                                             int]) -> tuple:\n        \"\"\"Get targets in denoising part for one image.\n\n        Args:\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for one image.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - labels (Tensor): Labels of each image.\n            - label_weights (Tensor]): Label weights of each image.\n            - bbox_targets (Tensor): BBox targets of each image.\n            - bbox_weights (Tensor): BBox weights of each image.\n            - pos_inds (Tensor): Sampled positive indices for each image.\n            - neg_inds (Tensor): Sampled negative indices for each image.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        num_groups = dn_meta['num_denoising_groups']\n        num_denoising_queries = dn_meta['num_denoising_queries']\n        num_queries_each_group = int(num_denoising_queries / num_groups)\n        device = gt_bboxes.device\n\n        if len(gt_labels) > 0:\n            t = torch.arange(len(gt_labels), dtype=torch.long, device=device)\n            t = t.unsqueeze(0).repeat(num_groups, 1)\n            pos_assigned_gt_inds = t.flatten()\n            pos_inds = torch.arange(\n                num_groups, dtype=torch.long, device=device)\n            pos_inds = pos_inds.unsqueeze(1) * num_queries_each_group + t\n            pos_inds = pos_inds.flatten()\n        else:\n            pos_inds = pos_assigned_gt_inds = \\\n                gt_bboxes.new_tensor([], dtype=torch.long)\n\n        neg_inds = pos_inds + num_queries_each_group // 2\n\n        # label targets\n        labels = gt_bboxes.new_full((num_denoising_queries, ),\n                                    self.num_classes,\n                                    dtype=torch.long)\n        labels[pos_inds] = gt_labels[pos_assigned_gt_inds]\n        label_weights = gt_bboxes.new_ones(num_denoising_queries)\n\n        # bbox targets\n        bbox_targets = torch.zeros(num_denoising_queries, 4, device=device)\n        bbox_weights = torch.zeros(num_denoising_queries, 4, device=device)\n        bbox_weights[pos_inds] = 1.0\n        img_h, img_w = img_meta['img_shape']\n\n        # DETR regress the relative position of boxes (cxcywh) in the image.\n        # Thus the learning target should be normalized by the image size, also\n        # the box format should be converted from defaultly x1y1x2y2 to cxcywh.\n        factor = gt_bboxes.new_tensor([img_w, img_h, img_w,\n                                       img_h]).unsqueeze(0)\n        gt_bboxes_normalized = gt_bboxes / factor\n        gt_bboxes_targets = bbox_xyxy_to_cxcywh(gt_bboxes_normalized)\n        bbox_targets[pos_inds] = gt_bboxes_targets.repeat([num_groups, 1])\n\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                neg_inds)\n\n    @staticmethod\n    def split_outputs(all_layers_cls_scores: Tensor,\n                      all_layers_bbox_preds: Tensor,\n                      dn_meta: Dict[str, int]) -> Tuple[Tensor]:\n        \"\"\"Split outputs of the denoising part and the matching part.\n\n        For the total outputs of `num_queries_total` length, the former\n        `num_denoising_queries` outputs are from denoising queries, and\n        the rest `num_matching_queries` ones are from matching queries,\n        where `num_queries_total` is the sum of `num_denoising_queries` and\n        `num_matching_queries`.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs,\n                num_queries_total, cls_out_channels).\n            all_layers_bbox_preds (Tensor): Regression outputs of all decoder\n                layers. Each is a 4D-tensor with normalized coordinate format\n                (cx, cy, w, h) and has shape (num_decoder_layers, bs,\n                num_queries_total, 4).\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'.\n\n        Returns:\n            Tuple[Tensor]: a tuple containing the following outputs.\n\n            - all_layers_matching_cls_scores (Tensor): Classification scores\n              of all decoder layers in matching part, has shape\n              (num_decoder_layers, bs, num_matching_queries, cls_out_channels).\n            - all_layers_matching_bbox_preds (Tensor): Regression outputs of\n              all decoder layers in matching part. Each is a 4D-tensor with\n              normalized coordinate format (cx, cy, w, h) and has shape\n              (num_decoder_layers, bs, num_matching_queries, 4).\n            - all_layers_denoising_cls_scores (Tensor): Classification scores\n              of all decoder layers in denoising part, has shape\n              (num_decoder_layers, bs, num_denoising_queries,\n              cls_out_channels).\n            - all_layers_denoising_bbox_preds (Tensor): Regression outputs of\n              all decoder layers in denoising part. Each is a 4D-tensor with\n              normalized coordinate format (cx, cy, w, h) and has shape\n              (num_decoder_layers, bs, num_denoising_queries, 4).\n        \"\"\"\n        num_denoising_queries = dn_meta['num_denoising_queries']\n        if dn_meta is not None:\n            all_layers_denoising_cls_scores = \\\n                all_layers_cls_scores[:, :, : num_denoising_queries, :]\n            all_layers_denoising_bbox_preds = \\\n                all_layers_bbox_preds[:, :, : num_denoising_queries, :]\n            all_layers_matching_cls_scores = \\\n                all_layers_cls_scores[:, :, num_denoising_queries:, :]\n            all_layers_matching_bbox_preds = \\\n                all_layers_bbox_preds[:, :, num_denoising_queries:, :]\n        else:\n            all_layers_denoising_cls_scores = None\n            all_layers_denoising_bbox_preds = None\n            all_layers_matching_cls_scores = all_layers_cls_scores\n            all_layers_matching_bbox_preds = all_layers_bbox_preds\n        return (all_layers_matching_cls_scores, all_layers_matching_bbox_preds,\n                all_layers_denoising_cls_scores,\n                all_layers_denoising_bbox_preds)\n"
  },
  {
    "path": "mmdet/models/dense_heads/embedding_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox_cxcywh_to_xyxy\nfrom mmdet.structures.det_data_sample import SampleList\nfrom mmdet.utils import InstanceList, OptConfigType\n\n\n@MODELS.register_module()\nclass EmbeddingRPNHead(BaseModule):\n    \"\"\"RPNHead in the `Sparse R-CNN <https://arxiv.org/abs/2011.12450>`_ .\n\n    Unlike traditional RPNHead, this module does not need FPN input, but just\n    decode `init_proposal_bboxes` and expand the first dimension of\n    `init_proposal_bboxes` and `init_proposal_features` to the batch_size.\n\n    Args:\n        num_proposals (int): Number of init_proposals. Defaults to 100.\n        proposal_feature_channel (int): Channel number of\n            init_proposal_feature. Defaults to 256.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_proposals: int = 100,\n                 proposal_feature_channel: int = 256,\n                 init_cfg: OptConfigType = None,\n                 **kwargs) -> None:\n        # `**kwargs` is necessary to avoid some potential error.\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super().__init__(init_cfg=init_cfg)\n        self.num_proposals = num_proposals\n        self.proposal_feature_channel = proposal_feature_channel\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize a sparse set of proposal boxes and proposal features.\"\"\"\n        self.init_proposal_bboxes = nn.Embedding(self.num_proposals, 4)\n        self.init_proposal_features = nn.Embedding(\n            self.num_proposals, self.proposal_feature_channel)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize the init_proposal_bboxes as normalized.\n\n        [c_x, c_y, w, h], and we initialize it to the size of  the entire\n        image.\n        \"\"\"\n        super().init_weights()\n        nn.init.constant_(self.init_proposal_bboxes.weight[:, :2], 0.5)\n        nn.init.constant_(self.init_proposal_bboxes.weight[:, 2:], 1)\n\n    def _decode_init_proposals(self, x: List[Tensor],\n                               batch_data_samples: SampleList) -> InstanceList:\n        \"\"\"Decode init_proposal_bboxes according to the size of images and\n        expand dimension of init_proposal_features to batch_size.\n\n        Args:\n            x (list[Tensor]): List of FPN features.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            List[:obj:`InstanceData`:] Detection results of each image.\n            Each item usually contains following keys.\n\n            - proposals: Decoded proposal bboxes,\n              has shape (num_proposals, 4).\n            - features: init_proposal_features, expanded proposal\n              features, has shape\n              (num_proposals, proposal_feature_channel).\n            - imgs_whwh: Tensor with shape\n              (num_proposals, 4), the dimension means\n              [img_width, img_height, img_width, img_height].\n        \"\"\"\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n\n        proposals = self.init_proposal_bboxes.weight.clone()\n        proposals = bbox_cxcywh_to_xyxy(proposals)\n        imgs_whwh = []\n        for meta in batch_img_metas:\n            h, w = meta['img_shape'][:2]\n            imgs_whwh.append(x[0].new_tensor([[w, h, w, h]]))\n        imgs_whwh = torch.cat(imgs_whwh, dim=0)\n        imgs_whwh = imgs_whwh[:, None, :]\n        proposals = proposals * imgs_whwh\n\n        rpn_results_list = []\n        for idx in range(len(batch_img_metas)):\n            rpn_results = InstanceData()\n            rpn_results.bboxes = proposals[idx]\n            rpn_results.imgs_whwh = imgs_whwh[idx].repeat(\n                self.num_proposals, 1)\n            rpn_results.features = self.init_proposal_features.weight.clone()\n            rpn_results_list.append(rpn_results)\n        return rpn_results_list\n\n    def loss(self, *args, **kwargs):\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the features of the upstream network.\"\"\"\n        raise NotImplementedError(\n            'EmbeddingRPNHead does not have `loss`, please use '\n            '`predict` or `loss_and_predict` instead.')\n\n    def predict(self, x: List[Tensor], batch_data_samples: SampleList,\n                **kwargs) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network.\"\"\"\n        # `**kwargs` is necessary to avoid some potential error.\n        return self._decode_init_proposals(\n            x=x, batch_data_samples=batch_data_samples)\n\n    def loss_and_predict(self, x: List[Tensor], batch_data_samples: SampleList,\n                         **kwargs) -> tuple:\n        \"\"\"Perform forward propagation of the head, then calculate loss and\n        predictions from the features and data samples.\"\"\"\n        # `**kwargs` is necessary to avoid some potential error.\n        predictions = self._decode_init_proposals(\n            x=x, batch_data_samples=batch_data_samples)\n\n        return dict(), predictions\n"
  },
  {
    "path": "mmdet/models/dense_heads/fcos_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import Scale\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.layers import NormedConv2d\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig,\n                         OptInstanceList, RangeType, reduce_mean)\nfrom ..utils import multi_apply\nfrom .anchor_free_head import AnchorFreeHead\n\nINF = 1e8\n\n\n@MODELS.register_module()\nclass FCOSHead(AnchorFreeHead):\n    \"\"\"Anchor-free head used in `FCOS <https://arxiv.org/abs/1904.01355>`_.\n\n    The FCOS head does not use anchor boxes. Instead bounding boxes are\n    predicted at each pixel and a centerness measure is used to suppress\n    low-quality predictions.\n    Here norm_on_bbox, centerness_on_reg, dcn_on_last_conv are training\n    tricks used in official repo, which will bring remarkable mAP gains\n    of up to 4.9. Please see https://github.com/tianzhi0549/FCOS for\n    more detail.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        strides (Sequence[int] or Sequence[Tuple[int, int]]): Strides of points\n            in multiple feature levels. Defaults to (4, 8, 16, 32, 64).\n        regress_ranges (Sequence[Tuple[int, int]]): Regress range of multiple\n            level points.\n        center_sampling (bool): If true, use center sampling.\n            Defaults to False.\n        center_sample_radius (float): Radius of center sampling.\n            Defaults to 1.5.\n        norm_on_bbox (bool): If true, normalize the regression targets with\n            FPN strides. Defaults to False.\n        centerness_on_reg (bool): If true, position centerness on the\n            regress branch. Please refer to https://github.com/tianzhi0549/FCOS/issues/89#issuecomment-516877042.\n            Defaults to False.\n        conv_bias (bool or str): If specified as `auto`, it will be decided by\n            the norm_cfg. Bias of conv will be set as True if `norm_cfg` is\n            None, otherwise False. Defaults to \"auto\".\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of localization loss.\n        loss_centerness (:obj:`ConfigDict`, or dict): Config of centerness\n            loss.\n        norm_cfg (:obj:`ConfigDict` or dict): dictionary to construct and\n            config norm layer.  Defaults to\n            ``norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)``.\n        cls_predictor_cfg (:obj:`ConfigDict` or dict): dictionary to construct and\n            config conv_cls. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n\n    Example:\n        >>> self = FCOSHead(11, 7)\n        >>> feats = [torch.rand(1, 7, s, s) for s in [4, 8, 16, 32, 64]]\n        >>> cls_score, bbox_pred, centerness = self.forward(feats)\n        >>> assert len(cls_score) == len(self.scales)\n    \"\"\"  # noqa: E501\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 regress_ranges: RangeType = ((-1, 64), (64, 128), (128, 256),\n                                              (256, 512), (512, INF)),\n                 center_sampling: bool = False,\n                 center_sample_radius: float = 1.5,\n                 norm_on_bbox: bool = False,\n                 centerness_on_reg: bool = False,\n                 loss_cls: ConfigType = dict(\n                     type='FocalLoss',\n                     use_sigmoid=True,\n                     gamma=2.0,\n                     alpha=0.25,\n                     loss_weight=1.0),\n                 loss_bbox: ConfigType = dict(type='IoULoss', loss_weight=1.0),\n                 loss_centerness: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     loss_weight=1.0),\n                 norm_cfg: ConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 cls_predictor_cfg=None,\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='conv_cls',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs) -> None:\n        self.regress_ranges = regress_ranges\n        self.center_sampling = center_sampling\n        self.center_sample_radius = center_sample_radius\n        self.norm_on_bbox = norm_on_bbox\n        self.centerness_on_reg = centerness_on_reg\n        self.cls_predictor_cfg = cls_predictor_cfg\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            loss_cls=loss_cls,\n            loss_bbox=loss_bbox,\n            norm_cfg=norm_cfg,\n            init_cfg=init_cfg,\n            **kwargs)\n        self.loss_centerness = MODELS.build(loss_centerness)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        super()._init_layers()\n        self.conv_centerness = nn.Conv2d(self.feat_channels, 1, 3, padding=1)\n        self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides])\n        if self.cls_predictor_cfg is not None:\n            self.cls_predictor_cfg.pop('type')\n            self.conv_cls = NormedConv2d(\n                self.feat_channels,\n                self.cls_out_channels,\n                1,\n                padding=0,\n                **self.cls_predictor_cfg)\n\n    def forward(\n            self, x: Tuple[Tensor]\n    ) -> Tuple[List[Tensor], List[Tensor], List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of each level outputs.\n\n            - cls_scores (list[Tensor]): Box scores for each scale level, \\\n            each is a 4D-tensor, the channel number is \\\n            num_points * num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for each \\\n            scale level, each is a 4D-tensor, the channel number is \\\n            num_points * 4.\n            - centernesses (list[Tensor]): centerness for each scale level, \\\n            each is a 4D-tensor, the channel number is num_points * 1.\n        \"\"\"\n        return multi_apply(self.forward_single, x, self.scales, self.strides)\n\n    def forward_single(self, x: Tensor, scale: Scale,\n                       stride: int) -> Tuple[Tensor, Tensor, Tensor]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n            scale (:obj:`mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n            stride (int): The corresponding stride for feature maps, only\n                used to normalize the bbox prediction when self.norm_on_bbox\n                is True.\n\n        Returns:\n            tuple: scores for each class, bbox predictions and centerness\n            predictions of input feature maps.\n        \"\"\"\n        cls_score, bbox_pred, cls_feat, reg_feat = super().forward_single(x)\n        if self.centerness_on_reg:\n            centerness = self.conv_centerness(reg_feat)\n        else:\n            centerness = self.conv_centerness(cls_feat)\n        # scale the bbox_pred of different level\n        # float to avoid overflow when enabling FP16\n        bbox_pred = scale(bbox_pred).float()\n        if self.norm_on_bbox:\n            # bbox_pred needed for gradient computation has been modified\n            # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace\n            # F.relu(bbox_pred) with bbox_pred.clamp(min=0)\n            bbox_pred = bbox_pred.clamp(min=0)\n            if not self.training:\n                bbox_pred *= stride\n        else:\n            bbox_pred = bbox_pred.exp()\n        return cls_score, bbox_pred, centerness\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        centernesses: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is\n                num_points * num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is\n                num_points * 4.\n            centernesses (list[Tensor]): centerness for each scale level, each\n                is a 4D-tensor, the channel number is num_points * 1.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds) == len(centernesses)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        all_level_points = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device)\n        labels, bbox_targets = self.get_targets(all_level_points,\n                                                batch_gt_instances)\n\n        num_imgs = cls_scores[0].size(0)\n        # flatten cls_scores, bbox_preds and centerness\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3, 1).reshape(-1, self.cls_out_channels)\n            for cls_score in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_centerness = [\n            centerness.permute(0, 2, 3, 1).reshape(-1)\n            for centerness in centernesses\n        ]\n        flatten_cls_scores = torch.cat(flatten_cls_scores)\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds)\n        flatten_centerness = torch.cat(flatten_centerness)\n        flatten_labels = torch.cat(labels)\n        flatten_bbox_targets = torch.cat(bbox_targets)\n        # repeat points to align with bbox_preds\n        flatten_points = torch.cat(\n            [points.repeat(num_imgs, 1) for points in all_level_points])\n\n        losses = dict()\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((flatten_labels >= 0)\n                    & (flatten_labels < bg_class_ind)).nonzero().reshape(-1)\n        num_pos = torch.tensor(\n            len(pos_inds), dtype=torch.float, device=bbox_preds[0].device)\n        num_pos = max(reduce_mean(num_pos), 1.0)\n        loss_cls = self.loss_cls(\n            flatten_cls_scores, flatten_labels, avg_factor=num_pos)\n\n        if getattr(self.loss_cls, 'custom_accuracy', False):\n            acc = self.loss_cls.get_accuracy(flatten_cls_scores,\n                                             flatten_labels)\n            losses.update(acc)\n\n        pos_bbox_preds = flatten_bbox_preds[pos_inds]\n        pos_centerness = flatten_centerness[pos_inds]\n        pos_bbox_targets = flatten_bbox_targets[pos_inds]\n        pos_centerness_targets = self.centerness_target(pos_bbox_targets)\n        # centerness weighted iou loss\n        centerness_denorm = max(\n            reduce_mean(pos_centerness_targets.sum().detach()), 1e-6)\n\n        if len(pos_inds) > 0:\n            pos_points = flatten_points[pos_inds]\n            pos_decoded_bbox_preds = self.bbox_coder.decode(\n                pos_points, pos_bbox_preds)\n            pos_decoded_target_preds = self.bbox_coder.decode(\n                pos_points, pos_bbox_targets)\n            loss_bbox = self.loss_bbox(\n                pos_decoded_bbox_preds,\n                pos_decoded_target_preds,\n                weight=pos_centerness_targets,\n                avg_factor=centerness_denorm)\n            loss_centerness = self.loss_centerness(\n                pos_centerness, pos_centerness_targets, avg_factor=num_pos)\n        else:\n            loss_bbox = pos_bbox_preds.sum()\n            loss_centerness = pos_centerness.sum()\n\n        losses['loss_cls'] = loss_cls\n        losses['loss_bbox'] = loss_bbox\n        losses['loss_centerness'] = loss_centerness\n\n        return losses\n\n    def get_targets(\n            self, points: List[Tensor], batch_gt_instances: InstanceList\n    ) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Compute regression, classification and centerness targets for points\n        in multiple images.\n\n        Args:\n            points (list[Tensor]): Points of each fpn level, each has shape\n                (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple: Targets of each level.\n\n            - concat_lvl_labels (list[Tensor]): Labels of each level.\n            - concat_lvl_bbox_targets (list[Tensor]): BBox targets of each \\\n            level.\n        \"\"\"\n        assert len(points) == len(self.regress_ranges)\n        num_levels = len(points)\n        # expand regress ranges to align with points\n        expanded_regress_ranges = [\n            points[i].new_tensor(self.regress_ranges[i])[None].expand_as(\n                points[i]) for i in range(num_levels)\n        ]\n        # concat all levels points and regress ranges\n        concat_regress_ranges = torch.cat(expanded_regress_ranges, dim=0)\n        concat_points = torch.cat(points, dim=0)\n\n        # the number of points per img, per lvl\n        num_points = [center.size(0) for center in points]\n\n        # get labels and bbox_targets of each image\n        labels_list, bbox_targets_list = multi_apply(\n            self._get_targets_single,\n            batch_gt_instances,\n            points=concat_points,\n            regress_ranges=concat_regress_ranges,\n            num_points_per_lvl=num_points)\n\n        # split to per img, per level\n        labels_list = [labels.split(num_points, 0) for labels in labels_list]\n        bbox_targets_list = [\n            bbox_targets.split(num_points, 0)\n            for bbox_targets in bbox_targets_list\n        ]\n\n        # concat per level image\n        concat_lvl_labels = []\n        concat_lvl_bbox_targets = []\n        for i in range(num_levels):\n            concat_lvl_labels.append(\n                torch.cat([labels[i] for labels in labels_list]))\n            bbox_targets = torch.cat(\n                [bbox_targets[i] for bbox_targets in bbox_targets_list])\n            if self.norm_on_bbox:\n                bbox_targets = bbox_targets / self.strides[i]\n            concat_lvl_bbox_targets.append(bbox_targets)\n        return concat_lvl_labels, concat_lvl_bbox_targets\n\n    def _get_targets_single(\n            self, gt_instances: InstanceData, points: Tensor,\n            regress_ranges: Tensor,\n            num_points_per_lvl: List[int]) -> Tuple[Tensor, Tensor]:\n        \"\"\"Compute regression and classification targets for a single image.\"\"\"\n        num_points = points.size(0)\n        num_gts = len(gt_instances)\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n\n        if num_gts == 0:\n            return gt_labels.new_full((num_points,), self.num_classes), \\\n                   gt_bboxes.new_zeros((num_points, 4))\n\n        areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0]) * (\n            gt_bboxes[:, 3] - gt_bboxes[:, 1])\n        # TODO: figure out why these two are different\n        # areas = areas[None].expand(num_points, num_gts)\n        areas = areas[None].repeat(num_points, 1)\n        regress_ranges = regress_ranges[:, None, :].expand(\n            num_points, num_gts, 2)\n        gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 4)\n        xs, ys = points[:, 0], points[:, 1]\n        xs = xs[:, None].expand(num_points, num_gts)\n        ys = ys[:, None].expand(num_points, num_gts)\n\n        left = xs - gt_bboxes[..., 0]\n        right = gt_bboxes[..., 2] - xs\n        top = ys - gt_bboxes[..., 1]\n        bottom = gt_bboxes[..., 3] - ys\n        bbox_targets = torch.stack((left, top, right, bottom), -1)\n\n        if self.center_sampling:\n            # condition1: inside a `center bbox`\n            radius = self.center_sample_radius\n            center_xs = (gt_bboxes[..., 0] + gt_bboxes[..., 2]) / 2\n            center_ys = (gt_bboxes[..., 1] + gt_bboxes[..., 3]) / 2\n            center_gts = torch.zeros_like(gt_bboxes)\n            stride = center_xs.new_zeros(center_xs.shape)\n\n            # project the points on current lvl back to the `original` sizes\n            lvl_begin = 0\n            for lvl_idx, num_points_lvl in enumerate(num_points_per_lvl):\n                lvl_end = lvl_begin + num_points_lvl\n                stride[lvl_begin:lvl_end] = self.strides[lvl_idx] * radius\n                lvl_begin = lvl_end\n\n            x_mins = center_xs - stride\n            y_mins = center_ys - stride\n            x_maxs = center_xs + stride\n            y_maxs = center_ys + stride\n            center_gts[..., 0] = torch.where(x_mins > gt_bboxes[..., 0],\n                                             x_mins, gt_bboxes[..., 0])\n            center_gts[..., 1] = torch.where(y_mins > gt_bboxes[..., 1],\n                                             y_mins, gt_bboxes[..., 1])\n            center_gts[..., 2] = torch.where(x_maxs > gt_bboxes[..., 2],\n                                             gt_bboxes[..., 2], x_maxs)\n            center_gts[..., 3] = torch.where(y_maxs > gt_bboxes[..., 3],\n                                             gt_bboxes[..., 3], y_maxs)\n\n            cb_dist_left = xs - center_gts[..., 0]\n            cb_dist_right = center_gts[..., 2] - xs\n            cb_dist_top = ys - center_gts[..., 1]\n            cb_dist_bottom = center_gts[..., 3] - ys\n            center_bbox = torch.stack(\n                (cb_dist_left, cb_dist_top, cb_dist_right, cb_dist_bottom), -1)\n            inside_gt_bbox_mask = center_bbox.min(-1)[0] > 0\n        else:\n            # condition1: inside a gt bbox\n            inside_gt_bbox_mask = bbox_targets.min(-1)[0] > 0\n\n        # condition2: limit the regression range for each location\n        max_regress_distance = bbox_targets.max(-1)[0]\n        inside_regress_range = (\n            (max_regress_distance >= regress_ranges[..., 0])\n            & (max_regress_distance <= regress_ranges[..., 1]))\n\n        # if there are still more than one objects for a location,\n        # we choose the one with minimal area\n        areas[inside_gt_bbox_mask == 0] = INF\n        areas[inside_regress_range == 0] = INF\n        min_area, min_area_inds = areas.min(dim=1)\n\n        labels = gt_labels[min_area_inds]\n        labels[min_area == INF] = self.num_classes  # set as BG\n        bbox_targets = bbox_targets[range(num_points), min_area_inds]\n\n        return labels, bbox_targets\n\n    def centerness_target(self, pos_bbox_targets: Tensor) -> Tensor:\n        \"\"\"Compute centerness targets.\n\n        Args:\n            pos_bbox_targets (Tensor): BBox targets of positive bboxes in shape\n                (num_pos, 4)\n\n        Returns:\n            Tensor: Centerness target.\n        \"\"\"\n        # only calculate pos centerness targets, otherwise there may be nan\n        left_right = pos_bbox_targets[:, [0, 2]]\n        top_bottom = pos_bbox_targets[:, [1, 3]]\n        if len(left_right) == 0:\n            centerness_targets = left_right[..., 0]\n        else:\n            centerness_targets = (\n                left_right.min(dim=-1)[0] / left_right.max(dim=-1)[0]) * (\n                    top_bottom.min(dim=-1)[0] / top_bottom.max(dim=-1)[0])\n        return torch.sqrt(centerness_targets)\n"
  },
  {
    "path": "mmdet/models/dense_heads/fovea_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops import DeformConv2d\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList, OptInstanceList, OptMultiConfig\nfrom ..utils import filter_scores_and_topk, multi_apply\nfrom .anchor_free_head import AnchorFreeHead\n\nINF = 1e8\n\n\nclass FeatureAlign(BaseModule):\n    \"\"\"Feature Align Module.\n\n    Feature Align Module is implemented based on DCN v1.\n    It uses anchor shape prediction rather than feature map to\n    predict offsets of deform conv layer.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        out_channels (int): Number of channels in the output feature map.\n        kernel_size (int): Size of the convolution kernel.\n            ``norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)``.\n        deform_groups: (int): Group number of DCN in\n            FeatureAdaption module.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: int,\n        out_channels: int,\n        kernel_size: int = 3,\n        deform_groups: int = 4,\n        init_cfg: OptMultiConfig = dict(\n            type='Normal',\n            layer='Conv2d',\n            std=0.1,\n            override=dict(type='Normal', name='conv_adaption', std=0.01))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        offset_channels = kernel_size * kernel_size * 2\n        self.conv_offset = nn.Conv2d(\n            4, deform_groups * offset_channels, 1, bias=False)\n        self.conv_adaption = DeformConv2d(\n            in_channels,\n            out_channels,\n            kernel_size=kernel_size,\n            padding=(kernel_size - 1) // 2,\n            deform_groups=deform_groups)\n        self.relu = nn.ReLU(inplace=True)\n\n    def forward(self, x: Tensor, shape: Tensor) -> Tensor:\n        \"\"\"Forward function of feature align module.\n\n        Args:\n            x (Tensor): Features from the upstream network.\n            shape (Tensor): Exponential of bbox predictions.\n\n        Returns:\n            x (Tensor): The aligned features.\n        \"\"\"\n        offset = self.conv_offset(shape)\n        x = self.relu(self.conv_adaption(x, offset))\n        return x\n\n\n@MODELS.register_module()\nclass FoveaHead(AnchorFreeHead):\n    \"\"\"Detection Head of `FoveaBox: Beyond Anchor-based Object Detector.\n\n    <https://arxiv.org/abs/1904.03797>`_.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        base_edge_list (list[int]): List of edges.\n        scale_ranges (list[tuple]): Range of scales.\n        sigma (float): Super parameter of ``FoveaHead``.\n        with_deform (bool):  Whether use deform conv.\n        deform_groups (int): Deformable conv group size.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 base_edge_list: List[int] = (16, 32, 64, 128, 256),\n                 scale_ranges: List[tuple] = ((8, 32), (16, 64), (32, 128),\n                                              (64, 256), (128, 512)),\n                 sigma: float = 0.4,\n                 with_deform: bool = False,\n                 deform_groups: int = 4,\n                 init_cfg: OptMultiConfig = dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='conv_cls',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs) -> None:\n        self.base_edge_list = base_edge_list\n        self.scale_ranges = scale_ranges\n        self.sigma = sigma\n        self.with_deform = with_deform\n        self.deform_groups = deform_groups\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        # box branch\n        super()._init_reg_convs()\n        self.conv_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1)\n\n        # cls branch\n        if not self.with_deform:\n            super()._init_cls_convs()\n            self.conv_cls = nn.Conv2d(\n                self.feat_channels, self.cls_out_channels, 3, padding=1)\n        else:\n            self.cls_convs = nn.ModuleList()\n            self.cls_convs.append(\n                ConvModule(\n                    self.feat_channels, (self.feat_channels * 4),\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    bias=self.norm_cfg is None))\n            self.cls_convs.append(\n                ConvModule((self.feat_channels * 4), (self.feat_channels * 4),\n                           1,\n                           stride=1,\n                           padding=0,\n                           conv_cfg=self.conv_cfg,\n                           norm_cfg=self.norm_cfg,\n                           bias=self.norm_cfg is None))\n            self.feature_adaption = FeatureAlign(\n                self.feat_channels,\n                self.feat_channels,\n                kernel_size=3,\n                deform_groups=self.deform_groups)\n            self.conv_cls = nn.Conv2d(\n                int(self.feat_channels * 4),\n                self.cls_out_channels,\n                3,\n                padding=1)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n\n        Returns:\n            tuple: scores for each class and bbox predictions of input\n            feature maps.\n        \"\"\"\n        cls_feat = x\n        reg_feat = x\n        for reg_layer in self.reg_convs:\n            reg_feat = reg_layer(reg_feat)\n        bbox_pred = self.conv_reg(reg_feat)\n        if self.with_deform:\n            cls_feat = self.feature_adaption(cls_feat, bbox_pred.exp())\n        for cls_layer in self.cls_convs:\n            cls_feat = cls_layer(cls_feat)\n        cls_score = self.conv_cls(cls_feat)\n        return cls_score, bbox_pred\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is\n                num_priors * num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is\n                num_priors * 4.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        priors = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device)\n        num_imgs = cls_scores[0].size(0)\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3, 1).reshape(-1, self.cls_out_channels)\n            for cls_score in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_cls_scores = torch.cat(flatten_cls_scores)\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds)\n        flatten_labels, flatten_bbox_targets = self.get_targets(\n            batch_gt_instances, featmap_sizes, priors)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        pos_inds = ((flatten_labels >= 0)\n                    & (flatten_labels < self.num_classes)).nonzero().view(-1)\n        num_pos = len(pos_inds)\n\n        loss_cls = self.loss_cls(\n            flatten_cls_scores, flatten_labels, avg_factor=num_pos + num_imgs)\n        if num_pos > 0:\n            pos_bbox_preds = flatten_bbox_preds[pos_inds]\n            pos_bbox_targets = flatten_bbox_targets[pos_inds]\n            pos_weights = pos_bbox_targets.new_ones(pos_bbox_targets.size())\n            loss_bbox = self.loss_bbox(\n                pos_bbox_preds,\n                pos_bbox_targets,\n                pos_weights,\n                avg_factor=num_pos)\n        else:\n            loss_bbox = torch.tensor(\n                0,\n                dtype=flatten_bbox_preds.dtype,\n                device=flatten_bbox_preds.device)\n        return dict(loss_cls=loss_cls, loss_bbox=loss_bbox)\n\n    def get_targets(\n            self, batch_gt_instances: InstanceList, featmap_sizes: List[tuple],\n            priors_list: List[Tensor]) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Compute regression and classification for priors in multiple images.\n\n        Args:\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            featmap_sizes (list[tuple]): Size tuple of feature maps.\n            priors_list (list[Tensor]): Priors list of each fpn level, each has\n                shape (num_priors, 2).\n\n        Returns:\n            tuple: Targets of each level.\n\n            - flatten_labels (list[Tensor]): Labels of each level.\n            - flatten_bbox_targets (list[Tensor]): BBox targets of each\n              level.\n        \"\"\"\n        label_list, bbox_target_list = multi_apply(\n            self._get_targets_single,\n            batch_gt_instances,\n            featmap_size_list=featmap_sizes,\n            priors_list=priors_list)\n        flatten_labels = [\n            torch.cat([\n                labels_level_img.flatten() for labels_level_img in labels_level\n            ]) for labels_level in zip(*label_list)\n        ]\n        flatten_bbox_targets = [\n            torch.cat([\n                bbox_targets_level_img.reshape(-1, 4)\n                for bbox_targets_level_img in bbox_targets_level\n            ]) for bbox_targets_level in zip(*bbox_target_list)\n        ]\n        flatten_labels = torch.cat(flatten_labels)\n        flatten_bbox_targets = torch.cat(flatten_bbox_targets)\n        return flatten_labels, flatten_bbox_targets\n\n    def _get_targets_single(self,\n                            gt_instances: InstanceData,\n                            featmap_size_list: List[tuple] = None,\n                            priors_list: List[Tensor] = None) -> tuple:\n        \"\"\"Compute regression and classification targets for a single image.\n\n        Args:\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            featmap_size_list (list[tuple]): Size tuple of feature maps.\n            priors_list (list[Tensor]): Priors of each fpn level, each has\n                shape (num_priors, 2).\n\n        Returns:\n            tuple:\n\n            - label_list (list[Tensor]): Labels of all anchors in the image.\n            - box_target_list (list[Tensor]): BBox targets of all anchors in\n              the image.\n        \"\"\"\n        gt_bboxes_raw = gt_instances.bboxes\n        gt_labels_raw = gt_instances.labels\n        gt_areas = torch.sqrt((gt_bboxes_raw[:, 2] - gt_bboxes_raw[:, 0]) *\n                              (gt_bboxes_raw[:, 3] - gt_bboxes_raw[:, 1]))\n        label_list = []\n        bbox_target_list = []\n        # for each pyramid, find the cls and box target\n        for base_len, (lower_bound, upper_bound), stride, featmap_size, \\\n            priors in zip(self.base_edge_list, self.scale_ranges,\n                          self.strides, featmap_size_list, priors_list):\n            # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n            priors = priors.view(*featmap_size, 2)\n            x, y = priors[..., 0], priors[..., 1]\n            labels = gt_labels_raw.new_full(featmap_size, self.num_classes)\n            bbox_targets = gt_bboxes_raw.new_ones(featmap_size[0],\n                                                  featmap_size[1], 4)\n            # scale assignment\n            hit_indices = ((gt_areas >= lower_bound) &\n                           (gt_areas <= upper_bound)).nonzero().flatten()\n            if len(hit_indices) == 0:\n                label_list.append(labels)\n                bbox_target_list.append(torch.log(bbox_targets))\n                continue\n            _, hit_index_order = torch.sort(-gt_areas[hit_indices])\n            hit_indices = hit_indices[hit_index_order]\n            gt_bboxes = gt_bboxes_raw[hit_indices, :] / stride\n            gt_labels = gt_labels_raw[hit_indices]\n            half_w = 0.5 * (gt_bboxes[:, 2] - gt_bboxes[:, 0])\n            half_h = 0.5 * (gt_bboxes[:, 3] - gt_bboxes[:, 1])\n            # valid fovea area: left, right, top, down\n            pos_left = torch.ceil(\n                gt_bboxes[:, 0] + (1 - self.sigma) * half_w - 0.5).long(). \\\n                clamp(0, featmap_size[1] - 1)\n            pos_right = torch.floor(\n                gt_bboxes[:, 0] + (1 + self.sigma) * half_w - 0.5).long(). \\\n                clamp(0, featmap_size[1] - 1)\n            pos_top = torch.ceil(\n                gt_bboxes[:, 1] + (1 - self.sigma) * half_h - 0.5).long(). \\\n                clamp(0, featmap_size[0] - 1)\n            pos_down = torch.floor(\n                gt_bboxes[:, 1] + (1 + self.sigma) * half_h - 0.5).long(). \\\n                clamp(0, featmap_size[0] - 1)\n            for px1, py1, px2, py2, label, (gt_x1, gt_y1, gt_x2, gt_y2) in \\\n                    zip(pos_left, pos_top, pos_right, pos_down, gt_labels,\n                        gt_bboxes_raw[hit_indices, :]):\n                labels[py1:py2 + 1, px1:px2 + 1] = label\n                bbox_targets[py1:py2 + 1, px1:px2 + 1, 0] = \\\n                    (x[py1:py2 + 1, px1:px2 + 1] - gt_x1) / base_len\n                bbox_targets[py1:py2 + 1, px1:px2 + 1, 1] = \\\n                    (y[py1:py2 + 1, px1:px2 + 1] - gt_y1) / base_len\n                bbox_targets[py1:py2 + 1, px1:px2 + 1, 2] = \\\n                    (gt_x2 - x[py1:py2 + 1, px1:px2 + 1]) / base_len\n                bbox_targets[py1:py2 + 1, px1:px2 + 1, 3] = \\\n                    (gt_y2 - y[py1:py2 + 1, px1:px2 + 1]) / base_len\n            bbox_targets = bbox_targets.clamp(min=1. / 16, max=16.)\n            label_list.append(labels)\n            bbox_target_list.append(torch.log(bbox_targets))\n        return label_list, bbox_target_list\n\n    # Same as base_dense_head/_predict_by_feat_single except self._bbox_decode\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: Optional[ConfigDict] = None,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid, has shape\n                (num_priors, 2).\n            img_meta (dict): Image meta info.\n            cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        assert len(cls_score_list) == len(bbox_pred_list)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bboxes = []\n        mlvl_scores = []\n        mlvl_labels = []\n        for level_idx, (cls_score, bbox_pred, stride, base_len, priors) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list, self.strides,\n                              self.base_edge_list, mlvl_priors)):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4)\n\n            scores = cls_score.permute(1, 2, 0).reshape(\n                -1, self.cls_out_channels).sigmoid()\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            results = filter_scores_and_topk(\n                scores, cfg.score_thr, nms_pre,\n                dict(bbox_pred=bbox_pred, priors=priors))\n            scores, labels, _, filtered_results = results\n\n            bbox_pred = filtered_results['bbox_pred']\n            priors = filtered_results['priors']\n\n            bboxes = self._bbox_decode(priors, bbox_pred, base_len, img_shape)\n\n            mlvl_bboxes.append(bboxes)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bboxes)\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n    def _bbox_decode(self, priors: Tensor, bbox_pred: Tensor, base_len: int,\n                     max_shape: int) -> Tensor:\n        \"\"\"Function to decode bbox.\n\n        Args:\n            priors (Tensor): Center proiors of an image, has shape\n                (num_instances, 2).\n            bbox_preds (Tensor): Box energies / deltas for all instances,\n                has shape (batch_size, num_instances, 4).\n            base_len (int): The base length.\n            max_shape (int): The max shape of bbox.\n\n        Returns:\n            Tensor: Decoded bboxes in (tl_x, tl_y, br_x, br_y) format. Has\n            shape (batch_size, num_instances, 4).\n        \"\"\"\n        bbox_pred = bbox_pred.exp()\n\n        y = priors[:, 1]\n        x = priors[:, 0]\n        x1 = (x - base_len * bbox_pred[:, 0]). \\\n            clamp(min=0, max=max_shape[1] - 1)\n        y1 = (y - base_len * bbox_pred[:, 1]). \\\n            clamp(min=0, max=max_shape[0] - 1)\n        x2 = (x + base_len * bbox_pred[:, 2]). \\\n            clamp(min=0, max=max_shape[1] - 1)\n        y2 = (y + base_len * bbox_pred[:, 3]). \\\n            clamp(min=0, max=max_shape[0] - 1)\n        decoded_bboxes = torch.stack([x1, y1, x2, y2], -1)\n        return decoded_bboxes\n"
  },
  {
    "path": "mmdet/models/dense_heads/free_anchor_retina_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import InstanceList, OptConfigType, OptInstanceList\nfrom ..utils import multi_apply\nfrom .retina_head import RetinaHead\n\nEPS = 1e-12\n\n\n@MODELS.register_module()\nclass FreeAnchorRetinaHead(RetinaHead):\n    \"\"\"FreeAnchor RetinaHead used in https://arxiv.org/abs/1909.02466.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        stacked_convs (int): Number of conv layers in cls and reg tower.\n            Defaults to 4.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): dictionary to\n            construct and config conv layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): dictionary to\n            construct and config norm layer. Defaults to\n            norm_cfg=dict(type='GN', num_groups=32, requires_grad=True).\n        pre_anchor_topk (int): Number of boxes that be token in each bag.\n            Defaults to 50\n        bbox_thr (float): The threshold of the saturated linear function.\n            It is usually the same with the IoU threshold used in NMS.\n            Defaults to 0.6.\n        gamma (float): Gamma parameter in focal loss. Defaults to 2.0.\n        alpha (float): Alpha parameter in focal loss. Defaults to 0.5.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 stacked_convs: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 pre_anchor_topk: int = 50,\n                 bbox_thr: float = 0.6,\n                 gamma: float = 2.0,\n                 alpha: float = 0.5,\n                 **kwargs) -> None:\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            stacked_convs=stacked_convs,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            **kwargs)\n\n        self.pre_anchor_topk = pre_anchor_topk\n        self.bbox_thr = bbox_thr\n        self.gamma = gamma\n        self.alpha = alpha\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, _ = self.get_anchors(\n            featmap_sizes=featmap_sizes,\n            batch_img_metas=batch_img_metas,\n            device=device)\n        concat_anchor_list = [torch.cat(anchor) for anchor in anchor_list]\n\n        # concatenate each level\n        cls_scores = [\n            cls.permute(0, 2, 3,\n                        1).reshape(cls.size(0), -1, self.cls_out_channels)\n            for cls in cls_scores\n        ]\n        bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(bbox_pred.size(0), -1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        cls_scores = torch.cat(cls_scores, dim=1)\n        cls_probs = torch.sigmoid(cls_scores)\n        bbox_preds = torch.cat(bbox_preds, dim=1)\n\n        box_probs, positive_losses, num_pos_list = multi_apply(\n            self.positive_loss_single, cls_probs, bbox_preds,\n            concat_anchor_list, batch_gt_instances)\n\n        num_pos = sum(num_pos_list)\n        positive_loss = torch.cat(positive_losses).sum() / max(1, num_pos)\n\n        # box_prob: P{a_{j} \\in A_{+}}\n        box_probs = torch.stack(box_probs, dim=0)\n\n        # negative_loss:\n        # \\sum_{j}{ FL((1 - P{a_{j} \\in A_{+}}) * (1 - P_{j}^{bg})) } / n||B||\n        negative_loss = self.negative_bag_loss(cls_probs, box_probs).sum() / \\\n            max(1, num_pos * self.pre_anchor_topk)\n\n        # avoid the absence of gradients in regression subnet\n        # when no ground-truth in a batch\n        if num_pos == 0:\n            positive_loss = bbox_preds.sum() * 0\n\n        losses = {\n            'positive_bag_loss': positive_loss,\n            'negative_bag_loss': negative_loss\n        }\n        return losses\n\n    def positive_loss_single(self, cls_prob: Tensor, bbox_pred: Tensor,\n                             flat_anchors: Tensor,\n                             gt_instances: InstanceData) -> tuple:\n        \"\"\"Compute positive loss.\n\n        Args:\n            cls_prob (Tensor): Classification probability of shape\n                (num_anchors, num_classes).\n            bbox_pred (Tensor): Box probability of shape (num_anchors, 4).\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors, 4)\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple:\n\n                - box_prob (Tensor): Box probability of shape (num_anchors, 4).\n                - positive_loss (Tensor): Positive loss of shape (num_pos, ).\n                - num_pos (int): positive samples indexes.\n        \"\"\"\n\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        with torch.no_grad():\n            if len(gt_bboxes) == 0:\n                image_box_prob = torch.zeros(\n                    flat_anchors.size(0),\n                    self.cls_out_channels).type_as(bbox_pred)\n            else:\n                # box_localization: a_{j}^{loc}, shape: [j, 4]\n                pred_boxes = self.bbox_coder.decode(flat_anchors, bbox_pred)\n\n                # object_box_iou: IoU_{ij}^{loc}, shape: [i, j]\n                object_box_iou = bbox_overlaps(gt_bboxes, pred_boxes)\n\n                # object_box_prob: P{a_{j} -> b_{i}}, shape: [i, j]\n                t1 = self.bbox_thr\n                t2 = object_box_iou.max(\n                    dim=1, keepdim=True).values.clamp(min=t1 + 1e-12)\n                object_box_prob = ((object_box_iou - t1) / (t2 - t1)).clamp(\n                    min=0, max=1)\n\n                # object_cls_box_prob: P{a_{j} -> b_{i}}, shape: [i, c, j]\n                num_obj = gt_labels.size(0)\n                indices = torch.stack(\n                    [torch.arange(num_obj).type_as(gt_labels), gt_labels],\n                    dim=0)\n                object_cls_box_prob = torch.sparse_coo_tensor(\n                    indices, object_box_prob)\n\n                # image_box_iou: P{a_{j} \\in A_{+}}, shape: [c, j]\n                \"\"\"\n                from \"start\" to \"end\" implement:\n                image_box_iou = torch.sparse.max(object_cls_box_prob,\n                                                 dim=0).t()\n\n                \"\"\"\n                # start\n                box_cls_prob = torch.sparse.sum(\n                    object_cls_box_prob, dim=0).to_dense()\n\n                indices = torch.nonzero(box_cls_prob, as_tuple=False).t_()\n                if indices.numel() == 0:\n                    image_box_prob = torch.zeros(\n                        flat_anchors.size(0),\n                        self.cls_out_channels).type_as(object_box_prob)\n                else:\n                    nonzero_box_prob = torch.where(\n                        (gt_labels.unsqueeze(dim=-1) == indices[0]),\n                        object_box_prob[:, indices[1]],\n                        torch.tensor(\n                            [0]).type_as(object_box_prob)).max(dim=0).values\n\n                    # upmap to shape [j, c]\n                    image_box_prob = torch.sparse_coo_tensor(\n                        indices.flip([0]),\n                        nonzero_box_prob,\n                        size=(flat_anchors.size(0),\n                              self.cls_out_channels)).to_dense()\n                # end\n            box_prob = image_box_prob\n\n        # construct bags for objects\n        match_quality_matrix = bbox_overlaps(gt_bboxes, flat_anchors)\n        _, matched = torch.topk(\n            match_quality_matrix, self.pre_anchor_topk, dim=1, sorted=False)\n        del match_quality_matrix\n\n        # matched_cls_prob: P_{ij}^{cls}\n        matched_cls_prob = torch.gather(\n            cls_prob[matched], 2,\n            gt_labels.view(-1, 1, 1).repeat(1, self.pre_anchor_topk,\n                                            1)).squeeze(2)\n\n        # matched_box_prob: P_{ij}^{loc}\n        matched_anchors = flat_anchors[matched]\n        matched_object_targets = self.bbox_coder.encode(\n            matched_anchors,\n            gt_bboxes.unsqueeze(dim=1).expand_as(matched_anchors))\n        loss_bbox = self.loss_bbox(\n            bbox_pred[matched],\n            matched_object_targets,\n            reduction_override='none').sum(-1)\n        matched_box_prob = torch.exp(-loss_bbox)\n\n        # positive_losses: {-log( Mean-max(P_{ij}^{cls} * P_{ij}^{loc}) )}\n        num_pos = len(gt_bboxes)\n        positive_loss = self.positive_bag_loss(matched_cls_prob,\n                                               matched_box_prob)\n\n        return box_prob, positive_loss, num_pos\n\n    def positive_bag_loss(self, matched_cls_prob: Tensor,\n                          matched_box_prob: Tensor) -> Tensor:\n        \"\"\"Compute positive bag loss.\n\n        :math:`-log( Mean-max(P_{ij}^{cls} * P_{ij}^{loc}) )`.\n\n        :math:`P_{ij}^{cls}`: matched_cls_prob, classification probability of matched samples.\n\n        :math:`P_{ij}^{loc}`: matched_box_prob, box probability of matched samples.\n\n        Args:\n            matched_cls_prob (Tensor): Classification probability of matched\n                samples in shape (num_gt, pre_anchor_topk).\n            matched_box_prob (Tensor): BBox probability of matched samples,\n                in shape (num_gt, pre_anchor_topk).\n\n        Returns:\n            Tensor: Positive bag loss in shape (num_gt,).\n        \"\"\"  # noqa: E501, W605\n        # bag_prob = Mean-max(matched_prob)\n        matched_prob = matched_cls_prob * matched_box_prob\n        weight = 1 / torch.clamp(1 - matched_prob, 1e-12, None)\n        weight /= weight.sum(dim=1).unsqueeze(dim=-1)\n        bag_prob = (weight * matched_prob).sum(dim=1)\n        # positive_bag_loss = -self.alpha * log(bag_prob)\n        return self.alpha * F.binary_cross_entropy(\n            bag_prob, torch.ones_like(bag_prob), reduction='none')\n\n    def negative_bag_loss(self, cls_prob: Tensor, box_prob: Tensor) -> Tensor:\n        \"\"\"Compute negative bag loss.\n\n        :math:`FL((1 - P_{a_{j} \\in A_{+}}) * (1 - P_{j}^{bg}))`.\n\n        :math:`P_{a_{j} \\in A_{+}}`: Box_probability of matched samples.\n\n        :math:`P_{j}^{bg}`: Classification probability of negative samples.\n\n        Args:\n            cls_prob (Tensor): Classification probability, in shape\n                (num_img, num_anchors, num_classes).\n            box_prob (Tensor): Box probability, in shape\n                (num_img, num_anchors, num_classes).\n\n        Returns:\n            Tensor: Negative bag loss in shape (num_img, num_anchors,\n            num_classes).\n        \"\"\"  # noqa: E501, W605\n        prob = cls_prob * (1 - box_prob)\n        # There are some cases when neg_prob = 0.\n        # This will cause the neg_prob.log() to be inf without clamp.\n        prob = prob.clamp(min=EPS, max=1 - EPS)\n        negative_bag_loss = prob**self.gamma * F.binary_cross_entropy(\n            prob, torch.zeros_like(prob), reduction='none')\n        return (1 - self.alpha) * negative_bag_loss\n"
  },
  {
    "path": "mmdet/models/dense_heads/fsaf_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Tuple\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList, OptInstanceList, OptMultiConfig\nfrom ..losses.accuracy import accuracy\nfrom ..losses.utils import weight_reduce_loss\nfrom ..task_modules.prior_generators import anchor_inside_flags\nfrom ..utils import images_to_levels, multi_apply, unmap\nfrom .retina_head import RetinaHead\n\n\n@MODELS.register_module()\nclass FSAFHead(RetinaHead):\n    \"\"\"Anchor-free head used in `FSAF <https://arxiv.org/abs/1903.00621>`_.\n\n    The head contains two subnetworks. The first classifies anchor boxes and\n    the second regresses deltas for the anchors (num_anchors is 1 for anchor-\n    free methods)\n\n    Args:\n        *args: Same as its base class in :class:`RetinaHead`\n        score_threshold (float, optional): The score_threshold to calculate\n            positive recall. If given, prediction scores lower than this value\n            is counted as incorrect prediction. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n        **kwargs: Same as its base class in :class:`RetinaHead`\n\n    Example:\n        >>> import torch\n        >>> self = FSAFHead(11, 7)\n        >>> x = torch.rand(1, 7, 32, 32)\n        >>> cls_score, bbox_pred = self.forward_single(x)\n        >>> # Each anchor predicts a score for each class except background\n        >>> cls_per_anchor = cls_score.shape[1] / self.num_anchors\n        >>> box_per_anchor = bbox_pred.shape[1] / self.num_anchors\n        >>> assert cls_per_anchor == self.num_classes\n        >>> assert box_per_anchor == 4\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 score_threshold: Optional[float] = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        # The positive bias in self.retina_reg conv is to prevent predicted \\\n        #  bbox with 0 area\n        if init_cfg is None:\n            init_cfg = dict(\n                type='Normal',\n                layer='Conv2d',\n                std=0.01,\n                override=[\n                    dict(\n                        type='Normal',\n                        name='retina_cls',\n                        std=0.01,\n                        bias_prob=0.01),\n                    dict(\n                        type='Normal', name='retina_reg', std=0.01, bias=0.25)\n                ])\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n        self.score_threshold = score_threshold\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward feature map of a single scale level.\n\n        Args:\n            x (Tensor): Feature map of a single scale level.\n\n        Returns:\n            tuple[Tensor, Tensor]:\n\n            - cls_score (Tensor): Box scores for each scale level Has \\\n            shape (N, num_points * num_classes, H, W).\n            - bbox_pred (Tensor): Box energies / deltas for each scale \\\n            level with shape (N, num_points * 4, H, W).\n        \"\"\"\n        cls_score, bbox_pred = super().forward_single(x)\n        # relu: TBLR encoder only accepts positive bbox_pred\n        return cls_score, self.relu(bbox_pred)\n\n    def _get_targets_single(self,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors in a\n        single image.\n\n        Most of the codes are the same with the base class :obj: `AnchorHead`,\n        except that it also collects and returns the matched gt index in the\n        image (from 0 to num_gt-1). If the anchor bbox is not matched to any\n        gt, the corresponding value in pos_gt_inds is -1.\n\n        Args:\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors, 4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors, ).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.  Defaults to True.\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # Assign gt and sample anchors\n        anchors = flat_anchors[inside_flags.type(torch.bool), :]\n\n        pred_instances = InstanceData(priors=anchors)\n        assign_result = self.assigner.assign(pred_instances, gt_instances,\n                                             gt_instances_ignore)\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        bbox_targets = torch.zeros_like(anchors)\n        bbox_weights = torch.zeros_like(anchors)\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(\n            (num_valid_anchors, self.cls_out_channels), dtype=torch.float)\n        pos_gt_inds = anchors.new_full((num_valid_anchors, ),\n                                       -1,\n                                       dtype=torch.long)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n\n        if len(pos_inds) > 0:\n            if not self.reg_decoded_bbox:\n                pos_bbox_targets = self.bbox_coder.encode(\n                    sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes)\n            else:\n                # When the regression loss (e.g. `IouLoss`, `GIouLoss`)\n                # is applied directly on the decoded bounding boxes, both\n                # the predicted boxes and regression targets should be with\n                # absolute coordinate format.\n                pos_bbox_targets = sampling_result.pos_gt_bboxes\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1.0\n            # The assigned gt_index for each anchor. (0-based)\n            pos_gt_inds[pos_inds] = sampling_result.pos_assigned_gt_inds\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # shadowed_labels is a tensor composed of tuples\n        #  (anchor_inds, class_label) that indicate those anchors lying in the\n        #  outer region of a gt or overlapped by another gt with a smaller\n        #  area.\n        #\n        # Therefore, only the shadowed labels are ignored for loss calculation.\n        # the key `shadowed_labels` is defined in :obj:`CenterRegionAssigner`\n        shadowed_labels = assign_result.get_extra_property('shadowed_labels')\n        if shadowed_labels is not None and shadowed_labels.numel():\n            if len(shadowed_labels.shape) == 2:\n                idx_, label_ = shadowed_labels[:, 0], shadowed_labels[:, 1]\n                assert (labels[idx_] != label_).all(), \\\n                    'One label cannot be both positive and ignored'\n                label_weights[idx_, label_] = 0\n            else:\n                label_weights[shadowed_labels] = 0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags,\n                fill=self.num_classes)  # fill bg label\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags)\n            bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags)\n            pos_gt_inds = unmap(\n                pos_gt_inds, num_total_anchors, inside_flags, fill=-1)\n\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                neg_inds, sampling_result, pos_gt_inds)\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Compute loss of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_points * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_points * 4, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        for i in range(len(bbox_preds)):  # loop over fpn level\n            # avoid 0 area of the predicted bbox\n            bbox_preds[i] = bbox_preds[i].clamp(min=1e-4)\n        # TODO: It may directly use the base-class loss function.\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n        batch_size = len(batch_img_metas)\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            return_sampling_results=True)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor, sampling_results_list,\n         pos_assigned_gt_inds_list) = cls_reg_targets\n\n        num_gts = np.array(list(map(len, batch_gt_instances)))\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        # concat all level anchors and flags to a single tensor\n        concat_anchor_list = []\n        for i in range(len(anchor_list)):\n            concat_anchor_list.append(torch.cat(anchor_list[i]))\n        all_anchor_list = images_to_levels(concat_anchor_list,\n                                           num_level_anchors)\n        losses_cls, losses_bbox = multi_apply(\n            self.loss_by_feat_single,\n            cls_scores,\n            bbox_preds,\n            all_anchor_list,\n            labels_list,\n            label_weights_list,\n            bbox_targets_list,\n            bbox_weights_list,\n            avg_factor=avg_factor)\n\n        # `pos_assigned_gt_inds_list` (length: fpn_levels) stores the assigned\n        # gt index of each anchor bbox in each fpn level.\n        cum_num_gts = list(np.cumsum(num_gts))  # length of batch_size\n        for i, assign in enumerate(pos_assigned_gt_inds_list):\n            # loop over fpn levels\n            for j in range(1, batch_size):\n                # loop over batch size\n                # Convert gt indices in each img to those in the batch\n                assign[j][assign[j] >= 0] += int(cum_num_gts[j - 1])\n            pos_assigned_gt_inds_list[i] = assign.flatten()\n            labels_list[i] = labels_list[i].flatten()\n        num_gts = num_gts.sum()  # total number of gt in the batch\n        # The unique label index of each gt in the batch\n        label_sequence = torch.arange(num_gts, device=device)\n        # Collect the average loss of each gt in each level\n        with torch.no_grad():\n            loss_levels, = multi_apply(\n                self.collect_loss_level_single,\n                losses_cls,\n                losses_bbox,\n                pos_assigned_gt_inds_list,\n                labels_seq=label_sequence)\n            # Shape: (fpn_levels, num_gts). Loss of each gt at each fpn level\n            loss_levels = torch.stack(loss_levels, dim=0)\n            # Locate the best fpn level for loss back-propagation\n            if loss_levels.numel() == 0:  # zero gt\n                argmin = loss_levels.new_empty((num_gts, ), dtype=torch.long)\n            else:\n                _, argmin = loss_levels.min(dim=0)\n\n        # Reweight the loss of each (anchor, label) pair, so that only those\n        #  at the best gt level are back-propagated.\n        losses_cls, losses_bbox, pos_inds = multi_apply(\n            self.reweight_loss_single,\n            losses_cls,\n            losses_bbox,\n            pos_assigned_gt_inds_list,\n            labels_list,\n            list(range(len(losses_cls))),\n            min_levels=argmin)\n        num_pos = torch.cat(pos_inds, 0).sum().float()\n        pos_recall = self.calculate_pos_recall(cls_scores, labels_list,\n                                               pos_inds)\n\n        if num_pos == 0:  # No gt\n            num_total_neg = sum(\n                [results.num_neg for results in sampling_results_list])\n            avg_factor = num_pos + num_total_neg\n        else:\n            avg_factor = num_pos\n        for i in range(len(losses_cls)):\n            losses_cls[i] /= avg_factor\n            losses_bbox[i] /= avg_factor\n        return dict(\n            loss_cls=losses_cls,\n            loss_bbox=losses_bbox,\n            num_pos=num_pos / batch_size,\n            pos_recall=pos_recall)\n\n    def calculate_pos_recall(self, cls_scores: List[Tensor],\n                             labels_list: List[Tensor],\n                             pos_inds: List[Tensor]) -> Tensor:\n        \"\"\"Calculate positive recall with score threshold.\n\n        Args:\n            cls_scores (list[Tensor]): Classification scores at all fpn levels.\n                Each tensor is in shape (N, num_classes * num_anchors, H, W)\n            labels_list (list[Tensor]): The label that each anchor is assigned\n                to. Shape (N * H * W * num_anchors, )\n            pos_inds (list[Tensor]): List of bool tensors indicating whether\n                the anchor is assigned to a positive label.\n                Shape (N * H * W * num_anchors, )\n\n        Returns:\n            Tensor: A single float number indicating the positive recall.\n        \"\"\"\n        with torch.no_grad():\n            num_class = self.num_classes\n            scores = [\n                cls.permute(0, 2, 3, 1).reshape(-1, num_class)[pos]\n                for cls, pos in zip(cls_scores, pos_inds)\n            ]\n            labels = [\n                label.reshape(-1)[pos]\n                for label, pos in zip(labels_list, pos_inds)\n            ]\n            scores = torch.cat(scores, dim=0)\n            labels = torch.cat(labels, dim=0)\n            if self.use_sigmoid_cls:\n                scores = scores.sigmoid()\n            else:\n                scores = scores.softmax(dim=1)\n\n            return accuracy(scores, labels, thresh=self.score_threshold)\n\n    def collect_loss_level_single(self, cls_loss: Tensor, reg_loss: Tensor,\n                                  assigned_gt_inds: Tensor,\n                                  labels_seq: Tensor) -> Tensor:\n        \"\"\"Get the average loss in each FPN level w.r.t. each gt label.\n\n        Args:\n            cls_loss (Tensor): Classification loss of each feature map pixel,\n              shape (num_anchor, num_class)\n            reg_loss (Tensor): Regression loss of each feature map pixel,\n              shape (num_anchor, 4)\n            assigned_gt_inds (Tensor): It indicates which gt the prior is\n              assigned to (0-based, -1: no assignment). shape (num_anchor),\n            labels_seq: The rank of labels. shape (num_gt)\n\n        Returns:\n            Tensor: shape (num_gt), average loss of each gt in this level\n        \"\"\"\n        if len(reg_loss.shape) == 2:  # iou loss has shape (num_prior, 4)\n            reg_loss = reg_loss.sum(dim=-1)  # sum loss in tblr dims\n        if len(cls_loss.shape) == 2:\n            cls_loss = cls_loss.sum(dim=-1)  # sum loss in class dims\n        loss = cls_loss + reg_loss\n        assert loss.size(0) == assigned_gt_inds.size(0)\n        # Default loss value is 1e6 for a layer where no anchor is positive\n        #  to ensure it will not be chosen to back-propagate gradient\n        losses_ = loss.new_full(labels_seq.shape, 1e6)\n        for i, l in enumerate(labels_seq):\n            match = assigned_gt_inds == l\n            if match.any():\n                losses_[i] = loss[match].mean()\n        return losses_,\n\n    def reweight_loss_single(self, cls_loss: Tensor, reg_loss: Tensor,\n                             assigned_gt_inds: Tensor, labels: Tensor,\n                             level: int, min_levels: Tensor) -> tuple:\n        \"\"\"Reweight loss values at each level.\n\n        Reassign loss values at each level by masking those where the\n        pre-calculated loss is too large. Then return the reduced losses.\n\n        Args:\n            cls_loss (Tensor): Element-wise classification loss.\n              Shape: (num_anchors, num_classes)\n            reg_loss (Tensor): Element-wise regression loss.\n              Shape: (num_anchors, 4)\n            assigned_gt_inds (Tensor): The gt indices that each anchor bbox\n              is assigned to. -1 denotes a negative anchor, otherwise it is the\n              gt index (0-based). Shape: (num_anchors, ),\n            labels (Tensor): Label assigned to anchors. Shape: (num_anchors, ).\n            level (int): The current level index in the pyramid\n              (0-4 for RetinaNet)\n            min_levels (Tensor): The best-matching level for each gt.\n              Shape: (num_gts, ),\n\n        Returns:\n            tuple:\n\n            - cls_loss: Reduced corrected classification loss. Scalar.\n            - reg_loss: Reduced corrected regression loss. Scalar.\n            - pos_flags (Tensor): Corrected bool tensor indicating the \\\n            final positive anchors. Shape: (num_anchors, ).\n        \"\"\"\n        loc_weight = torch.ones_like(reg_loss)\n        cls_weight = torch.ones_like(cls_loss)\n        pos_flags = assigned_gt_inds >= 0  # positive pixel flag\n        pos_indices = torch.nonzero(pos_flags, as_tuple=False).flatten()\n\n        if pos_flags.any():  # pos pixels exist\n            pos_assigned_gt_inds = assigned_gt_inds[pos_flags]\n            zeroing_indices = (min_levels[pos_assigned_gt_inds] != level)\n            neg_indices = pos_indices[zeroing_indices]\n\n            if neg_indices.numel():\n                pos_flags[neg_indices] = 0\n                loc_weight[neg_indices] = 0\n                # Only the weight corresponding to the label is\n                #  zeroed out if not selected\n                zeroing_labels = labels[neg_indices]\n                assert (zeroing_labels >= 0).all()\n                cls_weight[neg_indices, zeroing_labels] = 0\n\n        # Weighted loss for both cls and reg loss\n        cls_loss = weight_reduce_loss(cls_loss, cls_weight, reduction='sum')\n        reg_loss = weight_reduce_loss(reg_loss, loc_weight, reduction='sum')\n\n        return cls_loss, reg_loss, pos_flags\n"
  },
  {
    "path": "mmdet/models/dense_heads/ga_retina_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops import MaskedConv2d\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .guided_anchor_head import FeatureAdaption, GuidedAnchorHead\n\n\n@MODELS.register_module()\nclass GARetinaHead(GuidedAnchorHead):\n    \"\"\"Guided-Anchor-based RetinaNet head.\"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 stacked_convs: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        if init_cfg is None:\n            init_cfg = dict(\n                type='Normal',\n                layer='Conv2d',\n                std=0.01,\n                override=[\n                    dict(\n                        type='Normal',\n                        name='conv_loc',\n                        std=0.01,\n                        bias_prob=0.01),\n                    dict(\n                        type='Normal',\n                        name='retina_cls',\n                        std=0.01,\n                        bias_prob=0.01)\n                ])\n        self.stacked_convs = stacked_convs\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n\n        self.conv_loc = nn.Conv2d(self.feat_channels, 1, 1)\n        num_anchors = self.square_anchor_generator.num_base_priors[0]\n        self.conv_shape = nn.Conv2d(self.feat_channels, num_anchors * 2, 1)\n        self.feature_adaption_cls = FeatureAdaption(\n            self.feat_channels,\n            self.feat_channels,\n            kernel_size=3,\n            deform_groups=self.deform_groups)\n        self.feature_adaption_reg = FeatureAdaption(\n            self.feat_channels,\n            self.feat_channels,\n            kernel_size=3,\n            deform_groups=self.deform_groups)\n        self.retina_cls = MaskedConv2d(\n            self.feat_channels,\n            self.num_base_priors * self.cls_out_channels,\n            3,\n            padding=1)\n        self.retina_reg = MaskedConv2d(\n            self.feat_channels, self.num_base_priors * 4, 3, padding=1)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor]:\n        \"\"\"Forward feature map of a single scale level.\"\"\"\n        cls_feat = x\n        reg_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            reg_feat = reg_conv(reg_feat)\n\n        loc_pred = self.conv_loc(cls_feat)\n        shape_pred = self.conv_shape(reg_feat)\n\n        cls_feat = self.feature_adaption_cls(cls_feat, shape_pred)\n        reg_feat = self.feature_adaption_reg(reg_feat, shape_pred)\n\n        if not self.training:\n            mask = loc_pred.sigmoid()[0] >= self.loc_filter_thr\n        else:\n            mask = None\n        cls_score = self.retina_cls(cls_feat, mask)\n        bbox_pred = self.retina_reg(reg_feat, mask)\n        return cls_score, bbox_pred, shape_pred, loc_pred\n"
  },
  {
    "path": "mmdet/models/dense_heads/ga_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.ops import nms\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig, OptInstanceList\nfrom .guided_anchor_head import GuidedAnchorHead\n\n\n@MODELS.register_module()\nclass GARPNHead(GuidedAnchorHead):\n    \"\"\"Guided-Anchor-based RPN head.\"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 num_classes: int = 1,\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='conv_loc',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs) -> None:\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.rpn_conv = nn.Conv2d(\n            self.in_channels, self.feat_channels, 3, padding=1)\n        super(GARPNHead, self)._init_layers()\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor]:\n        \"\"\"Forward feature of a single scale level.\"\"\"\n\n        x = self.rpn_conv(x)\n        x = F.relu(x, inplace=True)\n        (cls_score, bbox_pred, shape_pred,\n         loc_pred) = super().forward_single(x)\n        return cls_score, bbox_pred, shape_pred, loc_pred\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            shape_preds: List[Tensor],\n            loc_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            shape_preds (list[Tensor]): shape predictions for each scale\n                level with shape (N, 1, H, W).\n            loc_preds (list[Tensor]): location predictions for each scale\n                level with shape (N, num_anchors * 2, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        losses = super().loss_by_feat(\n            cls_scores,\n            bbox_preds,\n            shape_preds,\n            loc_preds,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        return dict(\n            loss_rpn_cls=losses['loss_cls'],\n            loss_rpn_bbox=losses['loss_bbox'],\n            loss_anchor_shape=losses['loss_shape'],\n            loss_anchor_loc=losses['loss_loc'])\n\n    def _predict_by_feat_single(self,\n                                cls_scores: List[Tensor],\n                                bbox_preds: List[Tensor],\n                                mlvl_anchors: List[Tensor],\n                                mlvl_masks: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigType,\n                                rescale: bool = False) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            mlvl_anchors (list[Tensor]): Each element in the list is\n                the anchors of a single level in feature pyramid. it has\n                shape (num_priors, 4).\n            mlvl_masks (list[Tensor]): Each element in the list is location\n                masks of a single level.\n            img_meta (dict): Image meta info.\n            cfg (:obj:`ConfigDict` or dict): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4), the last\n              dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        assert cfg.nms.get('type', 'nms') == 'nms', 'GARPNHead only support ' \\\n            'naive nms.'\n\n        mlvl_proposals = []\n        for idx in range(len(cls_scores)):\n            rpn_cls_score = cls_scores[idx]\n            rpn_bbox_pred = bbox_preds[idx]\n            anchors = mlvl_anchors[idx]\n            mask = mlvl_masks[idx]\n            assert rpn_cls_score.size()[-2:] == rpn_bbox_pred.size()[-2:]\n            # if no location is kept, end.\n            if mask.sum() == 0:\n                continue\n            rpn_cls_score = rpn_cls_score.permute(1, 2, 0)\n            if self.use_sigmoid_cls:\n                rpn_cls_score = rpn_cls_score.reshape(-1)\n                scores = rpn_cls_score.sigmoid()\n            else:\n                rpn_cls_score = rpn_cls_score.reshape(-1, 2)\n                # remind that we set FG labels to [0, num_class-1]\n                # since mmdet v2.0\n                # BG cat_id: num_class\n                scores = rpn_cls_score.softmax(dim=1)[:, :-1]\n            # filter scores, bbox_pred w.r.t. mask.\n            # anchors are filtered in get_anchors() beforehand.\n            scores = scores[mask]\n            rpn_bbox_pred = rpn_bbox_pred.permute(1, 2, 0).reshape(-1,\n                                                                   4)[mask, :]\n            if scores.dim() == 0:\n                rpn_bbox_pred = rpn_bbox_pred.unsqueeze(0)\n                anchors = anchors.unsqueeze(0)\n                scores = scores.unsqueeze(0)\n            # filter anchors, bbox_pred, scores w.r.t. scores\n            if cfg.nms_pre > 0 and scores.shape[0] > cfg.nms_pre:\n                _, topk_inds = scores.topk(cfg.nms_pre)\n                rpn_bbox_pred = rpn_bbox_pred[topk_inds, :]\n                anchors = anchors[topk_inds, :]\n                scores = scores[topk_inds]\n            # get proposals w.r.t. anchors and rpn_bbox_pred\n            proposals = self.bbox_coder.decode(\n                anchors, rpn_bbox_pred, max_shape=img_meta['img_shape'])\n            # filter out too small bboxes\n            if cfg.min_bbox_size >= 0:\n                w = proposals[:, 2] - proposals[:, 0]\n                h = proposals[:, 3] - proposals[:, 1]\n                valid_mask = (w > cfg.min_bbox_size) & (h > cfg.min_bbox_size)\n                if not valid_mask.all():\n                    proposals = proposals[valid_mask]\n                    scores = scores[valid_mask]\n\n            # NMS in current level\n            proposals, _ = nms(proposals, scores, cfg.nms.iou_threshold)\n            proposals = proposals[:cfg.nms_post, :]\n            mlvl_proposals.append(proposals)\n        proposals = torch.cat(mlvl_proposals, 0)\n        if cfg.get('nms_across_levels', False):\n            # NMS across multi levels\n            proposals, _ = nms(proposals[:, :4], proposals[:, -1],\n                               cfg.nms.iou_threshold)\n            proposals = proposals[:cfg.max_per_img, :]\n        else:\n            scores = proposals[:, 4]\n            num = min(cfg.max_per_img, proposals.shape[0])\n            _, topk_inds = scores.topk(num)\n            proposals = proposals[topk_inds, :]\n\n        bboxes = proposals[:, :-1]\n        scores = proposals[:, -1]\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            bboxes /= bboxes.new_tensor(img_meta['scale_factor']).repeat(\n                (1, 2))\n\n        results = InstanceData()\n        results.bboxes = bboxes\n        results.scores = scores\n        results.labels = scores.new_zeros(scores.size(0), dtype=torch.long)\n        return results\n"
  },
  {
    "path": "mmdet/models/dense_heads/gfl_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, Scale\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig, OptConfigType,\n                         OptInstanceList, reduce_mean)\nfrom ..task_modules.prior_generators import anchor_inside_flags\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import (filter_scores_and_topk, images_to_levels, multi_apply,\n                     unmap)\nfrom .anchor_head import AnchorHead\n\n\nclass Integral(nn.Module):\n    \"\"\"A fixed layer for calculating integral result from distribution.\n\n    This layer calculates the target location by :math: ``sum{P(y_i) * y_i}``,\n    P(y_i) denotes the softmax vector that represents the discrete distribution\n    y_i denotes the discrete set, usually {0, 1, 2, ..., reg_max}\n\n    Args:\n        reg_max (int): The maximal value of the discrete set. Defaults to 16.\n            You may want to reset it according to your new dataset or related\n            settings.\n    \"\"\"\n\n    def __init__(self, reg_max: int = 16) -> None:\n        super().__init__()\n        self.reg_max = reg_max\n        self.register_buffer('project',\n                             torch.linspace(0, self.reg_max, self.reg_max + 1))\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward feature from the regression head to get integral result of\n        bounding box location.\n\n        Args:\n            x (Tensor): Features of the regression head, shape (N, 4*(n+1)),\n                n is self.reg_max.\n\n        Returns:\n            x (Tensor): Integral result of box locations, i.e., distance\n                offsets from the box center in four directions, shape (N, 4).\n        \"\"\"\n        x = F.softmax(x.reshape(-1, self.reg_max + 1), dim=1)\n        x = F.linear(x, self.project.type_as(x)).reshape(-1, 4)\n        return x\n\n\n@MODELS.register_module()\nclass GFLHead(AnchorHead):\n    \"\"\"Generalized Focal Loss: Learning Qualified and Distributed Bounding\n    Boxes for Dense Object Detection.\n\n    GFL head structure is similar with ATSS, however GFL uses\n    1) joint representation for classification and localization quality, and\n    2) flexible General distribution for bounding box locations,\n    which are supervised by\n    Quality Focal Loss (QFL) and Distribution Focal Loss (DFL), respectively\n\n    https://arxiv.org/abs/2006.04388\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        stacked_convs (int): Number of conv layers in cls and reg tower.\n            Defaults to 4.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): dictionary to construct\n            and config conv layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): dictionary to construct and\n            config norm layer. Default: dict(type='GN', num_groups=32,\n            requires_grad=True).\n        loss_qfl (:obj:`ConfigDict` or dict): Config of Quality Focal Loss\n            (QFL).\n        bbox_coder (:obj:`ConfigDict` or dict): Config of bbox coder. Defaults\n             to 'DistancePointBBoxCoder'.\n        reg_max (int): Max value of integral set :math: ``{0, ..., reg_max}``\n            in QFL setting. Defaults to 16.\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`]): Initialization config dict.\n    Example:\n        >>> self = GFLHead(11, 7)\n        >>> feats = [torch.rand(1, 7, s, s) for s in [4, 8, 16, 32, 64]]\n        >>> cls_quality_score, bbox_pred = self.forward(feats)\n        >>> assert len(cls_quality_score) == len(self.scales)\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 stacked_convs: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 loss_dfl: ConfigType = dict(\n                     type='DistributionFocalLoss', loss_weight=0.25),\n                 bbox_coder: ConfigType = dict(type='DistancePointBBoxCoder'),\n                 reg_max: int = 16,\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='gfl_cls',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs) -> None:\n        self.stacked_convs = stacked_convs\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.reg_max = reg_max\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            bbox_coder=bbox_coder,\n            init_cfg=init_cfg,\n            **kwargs)\n\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            if self.train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler(context=self)\n\n        self.integral = Integral(self.reg_max)\n        self.loss_dfl = MODELS.build(loss_dfl)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU()\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        assert self.num_anchors == 1, 'anchor free version'\n        self.gfl_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n        self.gfl_reg = nn.Conv2d(\n            self.feat_channels, 4 * (self.reg_max + 1), 3, padding=1)\n        self.scales = nn.ModuleList(\n            [Scale(1.0) for _ in self.prior_generator.strides])\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n\n            - cls_scores (list[Tensor]): Classification and quality (IoU)\n              joint scores for all scale levels, each is a 4D-tensor,\n              the channel number is num_classes.\n            - bbox_preds (list[Tensor]): Box distribution logits for all\n              scale levels, each is a 4D-tensor, the channel number is\n              4*(n+1), n is max value of integral set.\n        \"\"\"\n        return multi_apply(self.forward_single, x, self.scales)\n\n    def forward_single(self, x: Tensor, scale: Scale) -> Sequence[Tensor]:\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n            scale (:obj: `mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n\n        Returns:\n            tuple:\n\n            - cls_score (Tensor): Cls and quality joint scores for a single\n              scale level the channel number is num_classes.\n            - bbox_pred (Tensor): Box distribution logits for a single scale\n              level, the channel number is 4*(n+1), n is max value of\n              integral set.\n        \"\"\"\n        cls_feat = x\n        reg_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            reg_feat = reg_conv(reg_feat)\n        cls_score = self.gfl_cls(cls_feat)\n        bbox_pred = scale(self.gfl_reg(reg_feat)).float()\n        return cls_score, bbox_pred\n\n    def anchor_center(self, anchors: Tensor) -> Tensor:\n        \"\"\"Get anchor centers from anchors.\n\n        Args:\n            anchors (Tensor): Anchor list with shape (N, 4), ``xyxy`` format.\n\n        Returns:\n            Tensor: Anchor centers with shape (N, 2), ``xy`` format.\n        \"\"\"\n        anchors_cx = (anchors[..., 2] + anchors[..., 0]) / 2\n        anchors_cy = (anchors[..., 3] + anchors[..., 1]) / 2\n        return torch.stack([anchors_cx, anchors_cy], dim=-1)\n\n    def loss_by_feat_single(self, anchors: Tensor, cls_score: Tensor,\n                            bbox_pred: Tensor, labels: Tensor,\n                            label_weights: Tensor, bbox_targets: Tensor,\n                            stride: Tuple[int], avg_factor: int) -> dict:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            anchors (Tensor): Box reference for each scale level with shape\n                (N, num_total_anchors, 4).\n            cls_score (Tensor): Cls and quality joint scores for each scale\n                level has shape (N, num_classes, H, W).\n            bbox_pred (Tensor): Box distribution logits for each scale\n                level with shape (N, 4*(n+1), H, W), n is max value of integral\n                set.\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors)\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (N, num_total_anchors, 4).\n            stride (Tuple[int]): Stride in this scale level.\n            avg_factor (int): Average factor that is used to average\n                the loss. When using sampling method, avg_factor is usually\n                the sum of positive and negative priors. When using\n                `PseudoSampler`, `avg_factor` is usually equal to the number\n                of positive priors.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert stride[0] == stride[1], 'h stride is not equal to w stride!'\n        anchors = anchors.reshape(-1, 4)\n        cls_score = cls_score.permute(0, 2, 3,\n                                      1).reshape(-1, self.cls_out_channels)\n        bbox_pred = bbox_pred.permute(0, 2, 3,\n                                      1).reshape(-1, 4 * (self.reg_max + 1))\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().squeeze(1)\n        score = label_weights.new_zeros(labels.shape)\n\n        if len(pos_inds) > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n            pos_anchors = anchors[pos_inds]\n            pos_anchor_centers = self.anchor_center(pos_anchors) / stride[0]\n\n            weight_targets = cls_score.detach().sigmoid()\n            weight_targets = weight_targets.max(dim=1)[0][pos_inds]\n            pos_bbox_pred_corners = self.integral(pos_bbox_pred)\n            pos_decode_bbox_pred = self.bbox_coder.decode(\n                pos_anchor_centers, pos_bbox_pred_corners)\n            pos_decode_bbox_targets = pos_bbox_targets / stride[0]\n            score[pos_inds] = bbox_overlaps(\n                pos_decode_bbox_pred.detach(),\n                pos_decode_bbox_targets,\n                is_aligned=True)\n            pred_corners = pos_bbox_pred.reshape(-1, self.reg_max + 1)\n            target_corners = self.bbox_coder.encode(pos_anchor_centers,\n                                                    pos_decode_bbox_targets,\n                                                    self.reg_max).reshape(-1)\n\n            # regression loss\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_decode_bbox_targets,\n                weight=weight_targets,\n                avg_factor=1.0)\n\n            # dfl loss\n            loss_dfl = self.loss_dfl(\n                pred_corners,\n                target_corners,\n                weight=weight_targets[:, None].expand(-1, 4).reshape(-1),\n                avg_factor=4.0)\n        else:\n            loss_bbox = bbox_pred.sum() * 0\n            loss_dfl = bbox_pred.sum() * 0\n            weight_targets = bbox_pred.new_tensor(0)\n\n        # cls (qfl) loss\n        loss_cls = self.loss_cls(\n            cls_score, (labels, score),\n            weight=label_weights,\n            avg_factor=avg_factor)\n\n        return loss_cls, loss_bbox, loss_dfl, weight_targets.sum()\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Cls and quality scores for each scale\n                level has shape (N, num_classes, H, W).\n            bbox_preds (list[Tensor]): Box distribution logits for each scale\n                level with shape (N, 4*(n+1), H, W), n is max value of integral\n                set.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor) = cls_reg_targets\n\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n\n        losses_cls, losses_bbox, losses_dfl,\\\n            avg_factor = multi_apply(\n                self.loss_by_feat_single,\n                anchor_list,\n                cls_scores,\n                bbox_preds,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                self.prior_generator.strides,\n                avg_factor=avg_factor)\n\n        avg_factor = sum(avg_factor)\n        avg_factor = reduce_mean(avg_factor).clamp_(min=1).item()\n        losses_bbox = list(map(lambda x: x / avg_factor, losses_bbox))\n        losses_dfl = list(map(lambda x: x / avg_factor, losses_dfl))\n        return dict(\n            loss_cls=losses_cls, loss_bbox=losses_bbox, loss_dfl=losses_dfl)\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image. GFL head does not need this value.\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid, has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (:obj: `ConfigDict`): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            tuple[Tensor]: Results of detected bboxes and labels. If with_nms\n            is False and mlvl_score_factor is None, return mlvl_bboxes and\n            mlvl_scores, else return mlvl_bboxes, mlvl_scores and\n            mlvl_score_factor. Usually with_nms is False is used for aug\n            test. If with_nms is True, then return the following format\n\n            - det_bboxes (Tensor): Predicted bboxes with shape\n              [num_bboxes, 5], where the first 4 columns are bounding\n              box positions (tl_x, tl_y, br_x, br_y) and the 5-th\n              column are scores between 0 and 1.\n            - det_labels (Tensor): Predicted labels of the corresponding\n              box with shape [num_bboxes].\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bboxes = []\n        mlvl_scores = []\n        mlvl_labels = []\n        for level_idx, (cls_score, bbox_pred, stride, priors) in enumerate(\n                zip(cls_score_list, bbox_pred_list,\n                    self.prior_generator.strides, mlvl_priors)):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n            assert stride[0] == stride[1]\n\n            bbox_pred = bbox_pred.permute(1, 2, 0)\n            bbox_pred = self.integral(bbox_pred) * stride[0]\n\n            scores = cls_score.permute(1, 2, 0).reshape(\n                -1, self.cls_out_channels).sigmoid()\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            results = filter_scores_and_topk(\n                scores, cfg.score_thr, nms_pre,\n                dict(bbox_pred=bbox_pred, priors=priors))\n            scores, labels, _, filtered_results = results\n\n            bbox_pred = filtered_results['bbox_pred']\n            priors = filtered_results['priors']\n\n            bboxes = self.bbox_coder.decode(\n                self.anchor_center(priors), bbox_pred, max_shape=img_shape)\n            mlvl_bboxes.append(bboxes)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bboxes)\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n    def get_targets(self,\n                    anchor_list: List[Tensor],\n                    valid_flag_list: List[Tensor],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs=True) -> tuple:\n        \"\"\"Get targets for GFL head.\n\n        This method is almost the same as `AnchorHead.get_targets()`. Besides\n        returning the targets as the parent method does, it also returns the\n        anchors as the first element of the returned tuple.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        num_level_anchors_list = [num_level_anchors] * num_imgs\n\n        # concat all level anchors and flags to a single tensor\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            anchor_list[i] = torch.cat(anchor_list[i])\n            valid_flag_list[i] = torch.cat(valid_flag_list[i])\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n         all_bbox_weights, pos_inds_list, neg_inds_list,\n         sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             anchor_list,\n             valid_flag_list,\n             num_level_anchors_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             unmap_outputs=unmap_outputs)\n        # Get `avg_factor` of all images, which calculate in `SamplingResult`.\n        # When using sampling method, avg_factor is usually the sum of\n        # positive and negative priors. When using `PseudoSampler`,\n        # `avg_factor` is usually equal to the number of positive priors.\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        anchors_list = images_to_levels(all_anchors, num_level_anchors)\n        labels_list = images_to_levels(all_labels, num_level_anchors)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors)\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors)\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_anchors)\n        return (anchors_list, labels_list, label_weights_list,\n                bbox_targets_list, bbox_weights_list, avg_factor)\n\n    def _get_targets_single(self,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            num_level_anchors: List[int],\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression, classification targets for anchors in a single\n        image.\n\n        Args:\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors, 4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors,).\n            num_level_anchors (list[int]): Number of anchors of each scale\n                level.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors. Defaults to True.\n\n        Returns:\n            tuple: N is the number of total anchors in the image.\n\n            - anchors (Tensor): All anchors in the image with shape (N, 4).\n            - labels (Tensor): Labels of all anchors in the image with\n              shape (N,).\n            - label_weights (Tensor): Label weights of all anchor in the\n              image with shape (N,).\n            - bbox_targets (Tensor): BBox targets of all anchors in the\n              image with shape (N, 4).\n            - bbox_weights (Tensor): BBox weights of all anchors in the\n              image with shape (N, 4).\n            - pos_inds (Tensor): Indices of positive anchor with shape\n              (num_pos,).\n            - neg_inds (Tensor): Indices of negative anchor with shape\n              (num_neg,).\n            - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # assign gt and sample anchors\n        anchors = flat_anchors[inside_flags, :]\n        num_level_anchors_inside = self.get_num_level_anchors_inside(\n            num_level_anchors, inside_flags)\n        pred_instances = InstanceData(priors=anchors)\n        assign_result = self.assigner.assign(\n            pred_instances=pred_instances,\n            num_level_priors=num_level_anchors_inside,\n            gt_instances=gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n\n        sampling_result = self.sampler.sample(\n            assign_result=assign_result,\n            pred_instances=pred_instances,\n            gt_instances=gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        bbox_targets = torch.zeros_like(anchors)\n        bbox_weights = torch.zeros_like(anchors)\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            pos_bbox_targets = sampling_result.pos_gt_bboxes\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1.0\n\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            anchors = unmap(anchors, num_total_anchors, inside_flags)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags, fill=self.num_classes)\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags)\n            bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags)\n\n        return (anchors, labels, label_weights, bbox_targets, bbox_weights,\n                pos_inds, neg_inds, sampling_result)\n\n    def get_num_level_anchors_inside(self, num_level_anchors: List[int],\n                                     inside_flags: Tensor) -> List[int]:\n        \"\"\"Get the number of valid anchors in every level.\"\"\"\n\n        split_inside_flags = torch.split(inside_flags, num_level_anchors)\n        num_level_anchors_inside = [\n            int(flags.sum()) for flags in split_inside_flags\n        ]\n        return num_level_anchors_inside\n"
  },
  {
    "path": "mmdet/models/dense_heads/grounding_dino_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport math\nfrom typing import Dict, List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import Linear\nfrom mmengine.model import constant_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.losses import QualityFocalLoss\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox_cxcywh_to_xyxy, bbox_xyxy_to_cxcywh\nfrom mmdet.utils import InstanceList, reduce_mean\nfrom ..layers import inverse_sigmoid\nfrom .atss_vlfusion_head import convert_grounding_to_cls_scores\nfrom .dino_head import DINOHead\n\n\nclass ContrastiveEmbed(nn.Module):\n    \"\"\"text visual ContrastiveEmbed layer.\n\n    Args:\n        max_text_len (int, optional): Maximum length of text.\n        log_scale (Optional[Union[str, float]]):  The initial value of a\n          learnable parameter to multiply with the similarity\n          matrix to normalize the output.  Defaults to 0.0.\n          - If set to 'auto', the similarity matrix will be normalized by\n            a fixed value ``sqrt(d_c)`` where ``d_c`` is the channel number.\n          - If set to 'none' or ``None``, there is no normalization applied.\n          - If set to a float number, the similarity matrix will be multiplied\n            by ``exp(log_scale)``, where ``log_scale`` is learnable.\n        bias (bool, optional): Whether to add bias to the output.\n          If set to ``True``, a learnable bias that is initialized as -4.6\n          will be added to the output. Useful when training from scratch.\n          Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 max_text_len: int = 256,\n                 log_scale: Optional[Union[str, float]] = None,\n                 bias: bool = False):\n        super().__init__()\n        self.max_text_len = max_text_len\n        self.log_scale = log_scale\n        if isinstance(log_scale, float):\n            self.log_scale = nn.Parameter(\n                torch.Tensor([float(log_scale)]), requires_grad=True)\n        elif log_scale not in ['auto', 'none', None]:\n            raise ValueError(f'log_scale should be one of '\n                             f'\"auto\", \"none\", None, but got {log_scale}')\n\n        self.bias = None\n        if bias:\n            bias_value = -math.log((1 - 0.01) / 0.01)\n            self.bias = nn.Parameter(\n                torch.Tensor([bias_value]), requires_grad=True)\n\n    def forward(self, visual_feat: Tensor, text_feat: Tensor,\n                text_token_mask: Tensor) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            visual_feat (Tensor): Visual features.\n            text_feat (Tensor): Text features.\n            text_token_mask (Tensor): A mask used for text feats.\n\n        Returns:\n            Tensor: Classification score.\n        \"\"\"\n        res = visual_feat @ text_feat.transpose(-1, -2)\n        if isinstance(self.log_scale, nn.Parameter):\n            res = res * self.log_scale.exp()\n        elif self.log_scale == 'auto':\n            # NOTE: similar to the normalizer in self-attention\n            res = res / math.sqrt(visual_feat.shape[-1])\n        if self.bias is not None:\n            res = res + self.bias\n        res.masked_fill_(~text_token_mask[:, None, :], float('-inf'))\n\n        new_res = torch.full((*res.shape[:-1], self.max_text_len),\n                             float('-inf'),\n                             device=res.device)\n        new_res[..., :res.shape[-1]] = res\n\n        return new_res\n\n\n@MODELS.register_module()\nclass GroundingDINOHead(DINOHead):\n    \"\"\"Head of the Grounding DINO: Marrying DINO with Grounded Pre-Training for\n    Open-Set Object Detection.\n\n    Args:\n        contrastive_cfg (dict, optional): Contrastive config that contains\n          keys like ``max_text_len``. Defaults to dict(max_text_len=256).\n    \"\"\"\n\n    def __init__(self, contrastive_cfg=dict(max_text_len=256), **kwargs):\n        self.contrastive_cfg = contrastive_cfg\n        self.max_text_len = contrastive_cfg.get('max_text_len', 256)\n        super().__init__(**kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize classification branch and regression branch of head.\"\"\"\n        fc_cls = ContrastiveEmbed(**self.contrastive_cfg)\n        reg_branch = []\n        for _ in range(self.num_reg_fcs):\n            reg_branch.append(Linear(self.embed_dims, self.embed_dims))\n            reg_branch.append(nn.ReLU())\n        reg_branch.append(Linear(self.embed_dims, 4))\n        reg_branch = nn.Sequential(*reg_branch)\n\n        # NOTE: due to the fc_cls is a contrastive embedding and don't\n        # have any trainable parameters,we do not need to copy it.\n        if self.share_pred_layer:\n            self.cls_branches = nn.ModuleList(\n                [fc_cls for _ in range(self.num_pred_layer)])\n            self.reg_branches = nn.ModuleList(\n                [reg_branch for _ in range(self.num_pred_layer)])\n        else:\n            self.cls_branches = nn.ModuleList(\n                [copy.deepcopy(fc_cls) for _ in range(self.num_pred_layer)])\n            self.reg_branches = nn.ModuleList([\n                copy.deepcopy(reg_branch) for _ in range(self.num_pred_layer)\n            ])\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the Deformable DETR head.\"\"\"\n        for m in self.reg_branches:\n            constant_init(m[-1], 0, bias=0)\n        nn.init.constant_(self.reg_branches[0][-1].bias.data[2:], -2.0)\n        if self.as_two_stage:\n            for m in self.reg_branches:\n                nn.init.constant_(m[-1].bias.data[2:], 0.0)\n\n    def _get_targets_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict) -> tuple:\n        \"\"\"Compute regression and classification targets for one image.\n\n        Outputs from a single decoder layer of a single feature level are used.\n\n        Args:\n            cls_score (Tensor): Box score logits from a single decoder layer\n                for one image. Shape [num_queries, cls_out_channels].\n            bbox_pred (Tensor): Sigmoid outputs from a single decoder layer\n                for one image, with normalized coordinate (cx, cy, w, h) and\n                shape [num_queries, 4].\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for one image.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - labels (Tensor): Labels of each image.\n            - label_weights (Tensor]): Label weights of each image.\n            - bbox_targets (Tensor): BBox targets of each image.\n            - bbox_weights (Tensor): BBox weights of each image.\n            - pos_inds (Tensor): Sampled positive indices for each image.\n            - neg_inds (Tensor): Sampled negative indices for each image.\n        \"\"\"\n        img_h, img_w = img_meta['img_shape']\n        factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                       img_h]).unsqueeze(0)\n        num_bboxes = bbox_pred.size(0)\n        # convert bbox_pred from xywh, normalized to xyxy, unnormalized\n        bbox_pred = bbox_cxcywh_to_xyxy(bbox_pred)\n        bbox_pred = bbox_pred * factor\n\n        pred_instances = InstanceData(scores=cls_score, bboxes=bbox_pred)\n        # assigner and sampler\n        assign_result = self.assigner.assign(\n            pred_instances=pred_instances,\n            gt_instances=gt_instances,\n            img_meta=img_meta)\n        gt_bboxes = gt_instances.bboxes\n\n        pos_inds = torch.nonzero(\n            assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique()\n        neg_inds = torch.nonzero(\n            assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique()\n        pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1\n        pos_gt_bboxes = gt_bboxes[pos_assigned_gt_inds.long(), :]\n\n        # Major changes. The labels are 0-1 binary labels for each bbox\n        # and text tokens.\n        labels = gt_bboxes.new_full((num_bboxes, self.max_text_len),\n                                    0,\n                                    dtype=torch.float32)\n        labels[pos_inds] = gt_instances.positive_maps[pos_assigned_gt_inds]\n        label_weights = gt_bboxes.new_ones(num_bboxes)\n\n        # bbox targets\n        bbox_targets = torch.zeros_like(bbox_pred, dtype=gt_bboxes.dtype)\n        bbox_weights = torch.zeros_like(bbox_pred, dtype=gt_bboxes.dtype)\n        bbox_weights[pos_inds] = 1.0\n\n        # DETR regress the relative position of boxes (cxcywh) in the image.\n        # Thus the learning target should be normalized by the image size, also\n        # the box format should be converted from defaultly x1y1x2y2 to cxcywh.\n        pos_gt_bboxes_normalized = pos_gt_bboxes / factor\n        pos_gt_bboxes_targets = bbox_xyxy_to_cxcywh(pos_gt_bboxes_normalized)\n        bbox_targets[pos_inds] = pos_gt_bboxes_targets\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                neg_inds)\n\n    def forward(\n        self,\n        hidden_states: Tensor,\n        references: List[Tensor],\n        memory_text: Tensor,\n        text_token_mask: Tensor,\n    ) -> Tuple[Tensor]:\n        \"\"\"Forward function.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, bs, num_queries, dim).\n            references (List[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). The `init_reference` has shape (bs,\n                num_queries, 4) when `as_two_stage` of the detector is `True`,\n                otherwise (bs, num_queries, 2). Each `inter_reference` has\n                shape (bs, num_queries, 4) when `with_box_refine` of the\n                detector is `True`, otherwise (bs, num_queries, 2). The\n                coordinates are arranged as (cx, cy) when the last dimension is\n                2, and (cx, cy, w, h) when it is 4.\n            memory_text (Tensor): Memory text. It has shape (bs, len_text,\n                text_embed_dims).\n            text_token_mask (Tensor): Text token mask. It has shape (bs,\n                len_text).\n\n        Returns:\n            tuple[Tensor]: results of head containing the following tensor.\n\n            - all_layers_outputs_classes (Tensor): Outputs from the\n              classification head, has shape (num_decoder_layers, bs,\n              num_queries, cls_out_channels).\n            - all_layers_outputs_coords (Tensor): Sigmoid outputs from the\n              regression head with normalized coordinate format (cx, cy, w,\n              h), has shape (num_decoder_layers, bs, num_queries, 4) with the\n              last dimension arranged as (cx, cy, w, h).\n        \"\"\"\n        all_layers_outputs_classes = []\n        all_layers_outputs_coords = []\n\n        for layer_id in range(hidden_states.shape[0]):\n            reference = inverse_sigmoid(references[layer_id])\n            # NOTE The last reference will not be used.\n            hidden_state = hidden_states[layer_id]\n            outputs_class = self.cls_branches[layer_id](hidden_state,\n                                                        memory_text,\n                                                        text_token_mask)\n            tmp_reg_preds = self.reg_branches[layer_id](hidden_state)\n            if reference.shape[-1] == 4:\n                # When `layer` is 0 and `as_two_stage` of the detector\n                # is `True`, or when `layer` is greater than 0 and\n                # `with_box_refine` of the detector is `True`.\n                tmp_reg_preds += reference\n            else:\n                # When `layer` is 0 and `as_two_stage` of the detector\n                # is `False`, or when `layer` is greater than 0 and\n                # `with_box_refine` of the detector is `False`.\n                assert reference.shape[-1] == 2\n                tmp_reg_preds[..., :2] += reference\n            outputs_coord = tmp_reg_preds.sigmoid()\n            all_layers_outputs_classes.append(outputs_class)\n            all_layers_outputs_coords.append(outputs_coord)\n\n        all_layers_outputs_classes = torch.stack(all_layers_outputs_classes)\n        all_layers_outputs_coords = torch.stack(all_layers_outputs_coords)\n\n        return all_layers_outputs_classes, all_layers_outputs_coords\n\n    def predict(self,\n                hidden_states: Tensor,\n                references: List[Tensor],\n                memory_text: Tensor,\n                text_token_mask: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> InstanceList:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the queries of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, num_queries, bs, dim).\n            references (List[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). The `init_reference` has shape (bs,\n                num_queries, 4) when `as_two_stage` of the detector is `True`,\n                otherwise (bs, num_queries, 2). Each `inter_reference` has\n                shape (bs, num_queries, 4) when `with_box_refine` of the\n                detector is `True`, otherwise (bs, num_queries, 2). The\n                coordinates are arranged as (cx, cy) when the last dimension is\n                2, and (cx, cy, w, h) when it is 4.\n            memory_text (Tensor): Memory text. It has shape (bs, len_text,\n                text_embed_dims).\n            text_token_mask (Tensor): Text token mask. It has shape (bs,\n                len_text).\n            batch_data_samples (SampleList): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): If `True`, return boxes in original\n                image space. Defaults to `True`.\n\n        Returns:\n            InstanceList: Detection results of each image\n                after the post process.\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n        batch_token_positive_maps = [\n            data_samples.token_positive_map\n            for data_samples in batch_data_samples\n        ]\n\n        outs = self(hidden_states, references, memory_text, text_token_mask)\n\n        predictions = self.predict_by_feat(\n            *outs,\n            batch_img_metas=batch_img_metas,\n            batch_token_positive_maps=batch_token_positive_maps,\n            rescale=rescale)\n        return predictions\n\n    def predict_by_feat(self,\n                        all_layers_cls_scores: Tensor,\n                        all_layers_bbox_preds: Tensor,\n                        batch_img_metas: List[Dict],\n                        batch_token_positive_maps: Optional[List[dict]] = None,\n                        rescale: bool = False) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Args:\n            all_layers_cls_scores (Tensor):  Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs, num_queries,\n                cls_out_channels).\n            all_layers_bbox_preds (Tensor): Regression outputs of all decoder\n                layers. Each is a 4D-tensor with normalized coordinate format\n                (cx, cy, w, h) and shape (num_decoder_layers, bs, num_queries,\n                4) with the last dimension arranged as (cx, cy, w, h).\n            batch_img_metas (List[Dict]): _description_\n            batch_token_positive_maps (list[dict], Optional): Batch token\n                positive map. Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cls_scores = all_layers_cls_scores[-1]\n        bbox_preds = all_layers_bbox_preds[-1]\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score = cls_scores[img_id]\n            bbox_pred = bbox_preds[img_id]\n            img_meta = batch_img_metas[img_id]\n            token_positive_maps = batch_token_positive_maps[img_id]\n            results = self._predict_by_feat_single(cls_score, bbox_pred,\n                                                   token_positive_maps,\n                                                   img_meta, rescale)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_score: Tensor,\n                                bbox_pred: Tensor,\n                                token_positive_maps: dict,\n                                img_meta: dict,\n                                rescale: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score (Tensor): Box score logits from the last decoder layer\n                for each image. Shape [num_queries, cls_out_channels].\n            bbox_pred (Tensor): Sigmoid outputs from the last decoder layer\n                for each image, with coordinate format (cx, cy, w, h) and\n                shape [num_queries, 4].\n            token_positive_maps (dict): Token positive map.\n            img_meta (dict): Image meta info.\n            rescale (bool, optional): If True, return boxes in original image\n                space. Default True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_score) == len(bbox_pred)  # num_queries\n        max_per_img = self.test_cfg.get('max_per_img', len(cls_score))\n        img_shape = img_meta['img_shape']\n\n        if token_positive_maps is not None:\n            cls_score = convert_grounding_to_cls_scores(\n                logits=cls_score.sigmoid()[None],\n                positive_maps=[token_positive_maps])[0]\n            scores, indexes = cls_score.view(-1).topk(max_per_img)\n            num_classes = cls_score.shape[-1]\n            det_labels = indexes % num_classes\n            bbox_index = indexes // num_classes\n            bbox_pred = bbox_pred[bbox_index]\n        else:\n            cls_score = cls_score.sigmoid()\n            scores, _ = cls_score.max(-1)\n            scores, indexes = scores.topk(max_per_img)\n            bbox_pred = bbox_pred[indexes]\n            det_labels = scores.new_zeros(scores.shape, dtype=torch.long)\n\n        det_bboxes = bbox_cxcywh_to_xyxy(bbox_pred)\n        det_bboxes[:, 0::2] = det_bboxes[:, 0::2] * img_shape[1]\n        det_bboxes[:, 1::2] = det_bboxes[:, 1::2] * img_shape[0]\n        det_bboxes[:, 0::2].clamp_(min=0, max=img_shape[1])\n        det_bboxes[:, 1::2].clamp_(min=0, max=img_shape[0])\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            det_bboxes /= det_bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n        results = InstanceData()\n        results.bboxes = det_bboxes\n        results.scores = scores\n        results.labels = det_labels\n        return results\n\n    def loss(self, hidden_states: Tensor, references: List[Tensor],\n             memory_text: Tensor, text_token_mask: Tensor,\n             enc_outputs_class: Tensor, enc_outputs_coord: Tensor,\n             batch_data_samples: SampleList, dn_meta: Dict[str, int]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the queries of the upstream network.\n\n        Args:\n            hidden_states (Tensor): Hidden states output from each decoder\n                layer, has shape (num_decoder_layers, bs, num_queries_total,\n                dim), where `num_queries_total` is the sum of\n                `num_denoising_queries` and `num_matching_queries` when\n                `self.training` is `True`, else `num_matching_queries`.\n            references (list[Tensor]): List of the reference from the decoder.\n                The first reference is the `init_reference` (initial) and the\n                other num_decoder_layers(6) references are `inter_references`\n                (intermediate). The `init_reference` has shape (bs,\n                num_queries_total, 4) and each `inter_reference` has shape\n                (bs, num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            memory_text (Tensor): Memory text. It has shape (bs, len_text,\n                text_embed_dims).\n            enc_outputs_class (Tensor): The score of each point on encode\n                feature map, has shape (bs, num_feat_points, cls_out_channels).\n            enc_outputs_coord (Tensor): The proposal generate from the\n                encode feature map, has shape (bs, num_feat_points, 4) with the\n                last dimension arranged as (cx, cy, w, h).\n            batch_data_samples (list[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        outs = self(hidden_states, references, memory_text, text_token_mask)\n        self.text_masks = text_token_mask\n        loss_inputs = outs + (enc_outputs_class, enc_outputs_coord,\n                              batch_gt_instances, batch_img_metas, dn_meta)\n        losses = self.loss_by_feat(*loss_inputs)\n        return losses\n\n    def loss_by_feat_single(self, cls_scores: Tensor, bbox_preds: Tensor,\n                            batch_gt_instances: InstanceList,\n                            batch_img_metas: List[dict]) -> Tuple[Tensor]:\n        \"\"\"Loss function for outputs from a single decoder layer of a single\n        feature level.\n\n        Args:\n            cls_scores (Tensor): Box score logits from a single decoder layer\n                for all images, has shape (bs, num_queries, cls_out_channels).\n            bbox_preds (Tensor): Sigmoid outputs from a single decoder layer\n                for all images, with normalized coordinate (cx, cy, w, h) and\n                shape (bs, num_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n\n        Returns:\n            Tuple[Tensor]: A tuple including `loss_cls`, `loss_box` and\n            `loss_iou`.\n        \"\"\"\n        num_imgs = cls_scores.size(0)\n        cls_scores_list = [cls_scores[i] for i in range(num_imgs)]\n        bbox_preds_list = [bbox_preds[i] for i in range(num_imgs)]\n        with torch.no_grad():\n            cls_reg_targets = self.get_targets(cls_scores_list,\n                                               bbox_preds_list,\n                                               batch_gt_instances,\n                                               batch_img_metas)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         num_total_pos, num_total_neg) = cls_reg_targets\n        labels = torch.stack(labels_list, 0)\n        label_weights = torch.stack(label_weights_list, 0)\n        bbox_targets = torch.cat(bbox_targets_list, 0)\n        bbox_weights = torch.cat(bbox_weights_list, 0)\n\n        # ===== this change =====\n        # Loss is not computed for the padded regions of the text.\n        assert (self.text_masks.dim() == 2)\n        text_masks = self.text_masks.new_zeros(\n            (self.text_masks.size(0), self.max_text_len))\n        text_masks[:, :self.text_masks.size(1)] = self.text_masks\n        text_mask = (text_masks > 0).unsqueeze(1)\n        text_mask = text_mask.repeat(1, cls_scores.size(1), 1)\n        cls_scores = torch.masked_select(cls_scores, text_mask).contiguous()\n\n        labels = torch.masked_select(labels, text_mask)\n        label_weights = label_weights[...,\n                                      None].repeat(1, 1, text_mask.size(-1))\n        label_weights = torch.masked_select(label_weights, text_mask)\n\n        # classification loss\n        # construct weighted avg_factor to match with the official DETR repo\n        cls_avg_factor = num_total_pos * 1.0 + \\\n            num_total_neg * self.bg_cls_weight\n        if self.sync_cls_avg_factor:\n            cls_avg_factor = reduce_mean(\n                cls_scores.new_tensor([cls_avg_factor]))\n        cls_avg_factor = max(cls_avg_factor, 1)\n\n        if isinstance(self.loss_cls, QualityFocalLoss):\n            raise NotImplementedError(\n                'QualityFocalLoss for GroundingDINOHead is not supported yet.')\n        else:\n            loss_cls = self.loss_cls(\n                cls_scores, labels, label_weights, avg_factor=cls_avg_factor)\n\n        # Compute the average number of gt boxes across all gpus, for\n        # normalization purposes\n        num_total_pos = loss_cls.new_tensor([num_total_pos])\n        num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()\n\n        # construct factors used for rescale bboxes\n        factors = []\n        for img_meta, bbox_pred in zip(batch_img_metas, bbox_preds):\n            img_h, img_w, = img_meta['img_shape']\n            factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                           img_h]).unsqueeze(0).repeat(\n                                               bbox_pred.size(0), 1)\n            factors.append(factor)\n        factors = torch.cat(factors, 0)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image,\n        # thus the learning target is normalized by the image size. So here\n        # we need to re-scale them for calculating IoU loss\n        bbox_preds = bbox_preds.reshape(-1, 4)\n        bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors\n        bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors\n\n        # regression IoU loss, defaultly GIoU loss\n        loss_iou = self.loss_iou(\n            bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)\n\n        # regression L1 loss\n        loss_bbox = self.loss_bbox(\n            bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)\n        return loss_cls, loss_bbox, loss_iou\n\n    def _loss_dn_single(self, dn_cls_scores: Tensor, dn_bbox_preds: Tensor,\n                        batch_gt_instances: InstanceList,\n                        batch_img_metas: List[dict],\n                        dn_meta: Dict[str, int]) -> Tuple[Tensor]:\n        \"\"\"Denoising loss for outputs from a single decoder layer.\n\n        Args:\n            dn_cls_scores (Tensor): Classification scores of a single decoder\n                layer in denoising part, has shape (bs, num_denoising_queries,\n                cls_out_channels).\n            dn_bbox_preds (Tensor): Regression outputs of a single decoder\n                layer in denoising part. Each is a 4D-tensor with normalized\n                coordinate format (cx, cy, w, h) and has shape\n                (bs, num_denoising_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            Tuple[Tensor]: A tuple including `loss_cls`, `loss_box` and\n            `loss_iou`.\n        \"\"\"\n        cls_reg_targets = self.get_dn_targets(batch_gt_instances,\n                                              batch_img_metas, dn_meta)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         num_total_pos, num_total_neg) = cls_reg_targets\n        labels = torch.stack(labels_list, 0)\n        label_weights = torch.stack(label_weights_list, 0)\n        bbox_targets = torch.cat(bbox_targets_list, 0)\n        bbox_weights = torch.cat(bbox_weights_list, 0)\n        # ===== this change =====\n        # Loss is not computed for the padded regions of the text.\n        assert (self.text_masks.dim() == 2)\n        text_masks = self.text_masks.new_zeros(\n            (self.text_masks.size(0), self.max_text_len))\n        text_masks[:, :self.text_masks.size(1)] = self.text_masks\n        text_mask = (text_masks > 0).unsqueeze(1)\n        text_mask = text_mask.repeat(1, dn_cls_scores.size(1), 1)\n        cls_scores = torch.masked_select(dn_cls_scores, text_mask).contiguous()\n        labels = torch.masked_select(labels, text_mask)\n        label_weights = label_weights[...,\n                                      None].repeat(1, 1, text_mask.size(-1))\n        label_weights = torch.masked_select(label_weights, text_mask)\n        # =======================\n\n        # classification loss\n        # construct weighted avg_factor to match with the official DETR repo\n        cls_avg_factor = \\\n            num_total_pos * 1.0 + num_total_neg * self.bg_cls_weight\n        if self.sync_cls_avg_factor:\n            cls_avg_factor = reduce_mean(\n                cls_scores.new_tensor([cls_avg_factor]))\n        cls_avg_factor = max(cls_avg_factor, 1)\n\n        if len(cls_scores) > 0:\n            if isinstance(self.loss_cls, QualityFocalLoss):\n                raise NotImplementedError('QualityFocalLoss is not supported')\n            else:\n                loss_cls = self.loss_cls(\n                    cls_scores,\n                    labels,\n                    label_weights,\n                    avg_factor=cls_avg_factor)\n        else:\n            loss_cls = torch.zeros(\n                1, dtype=cls_scores.dtype, device=cls_scores.device)\n\n        # Compute the average number of gt boxes across all gpus, for\n        # normalization purposes\n        num_total_pos = loss_cls.new_tensor([num_total_pos])\n        num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()\n\n        # construct factors used for rescale bboxes\n        factors = []\n        for img_meta, bbox_pred in zip(batch_img_metas, dn_bbox_preds):\n            img_h, img_w = img_meta['img_shape']\n            factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                           img_h]).unsqueeze(0).repeat(\n                                               bbox_pred.size(0), 1)\n            factors.append(factor)\n        factors = torch.cat(factors)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image,\n        # thus the learning target is normalized by the image size. So here\n        # we need to re-scale them for calculating IoU loss\n        bbox_preds = dn_bbox_preds.reshape(-1, 4)\n        bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors\n        bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors\n\n        # regression IoU loss, defaultly GIoU loss\n        loss_iou = self.loss_iou(\n            bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)\n\n        # regression L1 loss\n        loss_bbox = self.loss_bbox(\n            bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)\n        return loss_cls, loss_bbox, loss_iou\n\n    def _get_dn_targets_single(self, gt_instances: InstanceData,\n                               img_meta: dict, dn_meta: Dict[str,\n                                                             int]) -> tuple:\n        \"\"\"Get targets in denoising part for one image.\n\n        Args:\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for one image.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - labels (Tensor): Labels of each image.\n            - label_weights (Tensor]): Label weights of each image.\n            - bbox_targets (Tensor): BBox targets of each image.\n            - bbox_weights (Tensor): BBox weights of each image.\n            - pos_inds (Tensor): Sampled positive indices for each image.\n            - neg_inds (Tensor): Sampled negative indices for each image.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        num_groups = dn_meta['num_denoising_groups']\n        num_denoising_queries = dn_meta['num_denoising_queries']\n        num_queries_each_group = int(num_denoising_queries / num_groups)\n        device = gt_bboxes.device\n\n        if len(gt_labels) > 0:\n            t = torch.arange(len(gt_labels), dtype=torch.long, device=device)\n            t = t.unsqueeze(0).repeat(num_groups, 1)\n            pos_assigned_gt_inds = t.flatten()\n            pos_inds = torch.arange(\n                num_groups, dtype=torch.long, device=device)\n            pos_inds = pos_inds.unsqueeze(1) * num_queries_each_group + t\n            pos_inds = pos_inds.flatten()\n        else:\n            pos_inds = pos_assigned_gt_inds = \\\n                gt_bboxes.new_tensor([], dtype=torch.long)\n\n        neg_inds = pos_inds + num_queries_each_group // 2\n        # label targets\n        # this change\n        labels = gt_bboxes.new_full((num_denoising_queries, self.max_text_len),\n                                    0,\n                                    dtype=torch.float32)\n        labels[pos_inds] = gt_instances.positive_maps[pos_assigned_gt_inds]\n        label_weights = gt_bboxes.new_ones(num_denoising_queries)\n\n        # bbox targets\n        bbox_targets = torch.zeros(num_denoising_queries, 4, device=device)\n        bbox_weights = torch.zeros(num_denoising_queries, 4, device=device)\n        bbox_weights[pos_inds] = 1.0\n        img_h, img_w = img_meta['img_shape']\n\n        # DETR regress the relative position of boxes (cxcywh) in the image.\n        # Thus the learning target should be normalized by the image size, also\n        # the box format should be converted from defaultly x1y1x2y2 to cxcywh.\n        factor = gt_bboxes.new_tensor([img_w, img_h, img_w,\n                                       img_h]).unsqueeze(0)\n        gt_bboxes_normalized = gt_bboxes / factor\n        gt_bboxes_targets = bbox_xyxy_to_cxcywh(gt_bboxes_normalized)\n        bbox_targets[pos_inds] = gt_bboxes_targets.repeat([num_groups, 1])\n\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                neg_inds)\n"
  },
  {
    "path": "mmdet/models/dense_heads/guided_anchor_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.ops import DeformConv2d, MaskedConv2d\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig, OptConfigType,\n                         OptInstanceList)\nfrom ..layers import multiclass_nms\nfrom ..task_modules.prior_generators import anchor_inside_flags, calc_region\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import images_to_levels, multi_apply, unmap\nfrom .anchor_head import AnchorHead\n\n\nclass FeatureAdaption(BaseModule):\n    \"\"\"Feature Adaption Module.\n\n    Feature Adaption Module is implemented based on DCN v1.\n    It uses anchor shape prediction rather than feature map to\n    predict offsets of deform conv layer.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        out_channels (int): Number of channels in the output feature map.\n        kernel_size (int): Deformable conv kernel size. Defaults to 3.\n        deform_groups (int): Deformable conv group size. Defaults to 4.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or \\\n            list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: int,\n        out_channels: int,\n        kernel_size: int = 3,\n        deform_groups: int = 4,\n        init_cfg: MultiConfig = dict(\n            type='Normal',\n            layer='Conv2d',\n            std=0.1,\n            override=dict(type='Normal', name='conv_adaption', std=0.01))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        offset_channels = kernel_size * kernel_size * 2\n        self.conv_offset = nn.Conv2d(\n            2, deform_groups * offset_channels, 1, bias=False)\n        self.conv_adaption = DeformConv2d(\n            in_channels,\n            out_channels,\n            kernel_size=kernel_size,\n            padding=(kernel_size - 1) // 2,\n            deform_groups=deform_groups)\n        self.relu = nn.ReLU(inplace=True)\n\n    def forward(self, x: Tensor, shape: Tensor) -> Tensor:\n        offset = self.conv_offset(shape.detach())\n        x = self.relu(self.conv_adaption(x, offset))\n        return x\n\n\n@MODELS.register_module()\nclass GuidedAnchorHead(AnchorHead):\n    \"\"\"Guided-Anchor-based head (GA-RPN, GA-RetinaNet, etc.).\n\n    This GuidedAnchorHead will predict high-quality feature guided\n    anchors and locations where anchors will be kept in inference.\n    There are mainly 3 categories of bounding-boxes.\n\n    - Sampled 9 pairs for target assignment. (approxes)\n    - The square boxes where the predicted anchors are based on. (squares)\n    - Guided anchors.\n\n    Please refer to https://arxiv.org/abs/1901.03278 for more details.\n\n    Args:\n        num_classes (int): Number of classes.\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels. Defaults to 256.\n        approx_anchor_generator (:obj:`ConfigDict` or dict): Config dict\n            for approx generator\n        square_anchor_generator (:obj:`ConfigDict` or dict): Config dict\n            for square generator\n        anchor_coder (:obj:`ConfigDict` or dict): Config dict for anchor coder\n        bbox_coder (:obj:`ConfigDict` or dict): Config dict for bbox coder\n        reg_decoded_bbox (bool): If true, the regression loss would be\n            applied directly on decoded bounding boxes, converting both\n            the predicted boxes and regression targets to absolute\n            coordinates format. Defaults to False. It should be `True` when\n            using `IoULoss`, `GIoULoss`, or `DIoULoss` in the bbox head.\n        deform_groups: (int): Group number of DCN in FeatureAdaption module.\n            Defaults to 4.\n        loc_filter_thr (float): Threshold to filter out unconcerned regions.\n            Defaults to 0.01.\n        loss_loc (:obj:`ConfigDict` or dict): Config of location loss.\n        loss_shape (:obj:`ConfigDict` or dict): Config of anchor shape loss.\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of bbox regression loss.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or \\\n            list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int,\n        feat_channels: int = 256,\n        approx_anchor_generator: ConfigType = dict(\n            type='AnchorGenerator',\n            octave_base_scale=8,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        square_anchor_generator: ConfigType = dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[8],\n            strides=[4, 8, 16, 32, 64]),\n        anchor_coder: ConfigType = dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        bbox_coder: ConfigType = dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        reg_decoded_bbox: bool = False,\n        deform_groups: int = 4,\n        loc_filter_thr: float = 0.01,\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        loss_loc: ConfigType = dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape: ConfigType = dict(\n            type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls: ConfigType = dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox: ConfigType = dict(\n            type='SmoothL1Loss', beta=1.0, loss_weight=1.0),\n        init_cfg: MultiConfig = dict(\n            type='Normal',\n            layer='Conv2d',\n            std=0.01,\n            override=dict(\n                type='Normal', name='conv_loc', std=0.01, lbias_prob=0.01))\n    ) -> None:\n        super(AnchorHead, self).__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.num_classes = num_classes\n        self.feat_channels = feat_channels\n        self.deform_groups = deform_groups\n        self.loc_filter_thr = loc_filter_thr\n\n        # build approx_anchor_generator and square_anchor_generator\n        assert (approx_anchor_generator['octave_base_scale'] ==\n                square_anchor_generator['scales'][0])\n        assert (approx_anchor_generator['strides'] ==\n                square_anchor_generator['strides'])\n        self.approx_anchor_generator = TASK_UTILS.build(\n            approx_anchor_generator)\n        self.square_anchor_generator = TASK_UTILS.build(\n            square_anchor_generator)\n        self.approxs_per_octave = self.approx_anchor_generator \\\n            .num_base_priors[0]\n\n        self.reg_decoded_bbox = reg_decoded_bbox\n\n        # one anchor per location\n        self.num_base_priors = self.square_anchor_generator.num_base_priors[0]\n\n        self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False)\n        self.loc_focal_loss = loss_loc['type'] in ['FocalLoss']\n        if self.use_sigmoid_cls:\n            self.cls_out_channels = self.num_classes\n        else:\n            self.cls_out_channels = self.num_classes + 1\n\n        # build bbox_coder\n        self.anchor_coder = TASK_UTILS.build(anchor_coder)\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n\n        # build losses\n        self.loss_loc = MODELS.build(loss_loc)\n        self.loss_shape = MODELS.build(loss_shape)\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox = MODELS.build(loss_bbox)\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            # use PseudoSampler when no sampler in train_cfg\n            if train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler()\n\n            self.ga_assigner = TASK_UTILS.build(self.train_cfg['ga_assigner'])\n            if train_cfg.get('ga_sampler', None) is not None:\n                self.ga_sampler = TASK_UTILS.build(\n                    self.train_cfg['ga_sampler'],\n                    default_args=dict(context=self))\n            else:\n                self.ga_sampler = PseudoSampler()\n\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.conv_loc = nn.Conv2d(self.in_channels, 1, 1)\n        self.conv_shape = nn.Conv2d(self.in_channels, self.num_base_priors * 2,\n                                    1)\n        self.feature_adaption = FeatureAdaption(\n            self.in_channels,\n            self.feat_channels,\n            kernel_size=3,\n            deform_groups=self.deform_groups)\n        self.conv_cls = MaskedConv2d(\n            self.feat_channels, self.num_base_priors * self.cls_out_channels,\n            1)\n        self.conv_reg = MaskedConv2d(self.feat_channels,\n                                     self.num_base_priors * 4, 1)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor]:\n        \"\"\"Forward feature of a single scale level.\"\"\"\n        loc_pred = self.conv_loc(x)\n        shape_pred = self.conv_shape(x)\n        x = self.feature_adaption(x, shape_pred)\n        # masked conv is only used during inference for speed-up\n        if not self.training:\n            mask = loc_pred.sigmoid()[0] >= self.loc_filter_thr\n        else:\n            mask = None\n        cls_score = self.conv_cls(x, mask)\n        bbox_pred = self.conv_reg(x, mask)\n        return cls_score, bbox_pred, shape_pred, loc_pred\n\n    def forward(self, x: List[Tensor]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\"\"\"\n        return multi_apply(self.forward_single, x)\n\n    def get_sampled_approxs(self,\n                            featmap_sizes: List[Tuple[int, int]],\n                            batch_img_metas: List[dict],\n                            device: str = 'cuda') -> tuple:\n        \"\"\"Get sampled approxs and inside flags according to feature map sizes.\n\n        Args:\n            featmap_sizes (list[tuple]): Multi-level feature map sizes.\n            batch_img_metas (list[dict]): Image meta info.\n            device (str): device for returned tensors\n\n        Returns:\n            tuple: approxes of each image, inside flags of each image\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n\n        # since feature map sizes of all images are the same, we only compute\n        # approxes for one time\n        multi_level_approxs = self.approx_anchor_generator.grid_priors(\n            featmap_sizes, device=device)\n        approxs_list = [multi_level_approxs for _ in range(num_imgs)]\n\n        # for each image, we compute inside flags of multi level approxes\n        inside_flag_list = []\n        for img_id, img_meta in enumerate(batch_img_metas):\n            multi_level_flags = []\n            multi_level_approxs = approxs_list[img_id]\n\n            # obtain valid flags for each approx first\n            multi_level_approx_flags = self.approx_anchor_generator \\\n                .valid_flags(featmap_sizes,\n                             img_meta['pad_shape'],\n                             device=device)\n\n            for i, flags in enumerate(multi_level_approx_flags):\n                approxs = multi_level_approxs[i]\n                inside_flags_list = []\n                for j in range(self.approxs_per_octave):\n                    split_valid_flags = flags[j::self.approxs_per_octave]\n                    split_approxs = approxs[j::self.approxs_per_octave, :]\n                    inside_flags = anchor_inside_flags(\n                        split_approxs, split_valid_flags,\n                        img_meta['img_shape'][:2],\n                        self.train_cfg['allowed_border'])\n                    inside_flags_list.append(inside_flags)\n                # inside_flag for a position is true if any anchor in this\n                # position is true\n                inside_flags = (\n                    torch.stack(inside_flags_list, 0).sum(dim=0) > 0)\n                multi_level_flags.append(inside_flags)\n            inside_flag_list.append(multi_level_flags)\n        return approxs_list, inside_flag_list\n\n    def get_anchors(self,\n                    featmap_sizes: List[Tuple[int, int]],\n                    shape_preds: List[Tensor],\n                    loc_preds: List[Tensor],\n                    batch_img_metas: List[dict],\n                    use_loc_filter: bool = False,\n                    device: str = 'cuda') -> tuple:\n        \"\"\"Get squares according to feature map sizes and guided anchors.\n\n        Args:\n            featmap_sizes (list[tuple]): Multi-level feature map sizes.\n            shape_preds (list[tensor]): Multi-level shape predictions.\n            loc_preds (list[tensor]): Multi-level location predictions.\n            batch_img_metas (list[dict]): Image meta info.\n            use_loc_filter (bool): Use loc filter or not. Defaults to False\n            device (str): device for returned tensors.\n                Defaults to `cuda`.\n\n        Returns:\n            tuple: square approxs of each image, guided anchors of each image,\n            loc masks of each image.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        num_levels = len(featmap_sizes)\n\n        # since feature map sizes of all images are the same, we only compute\n        # squares for one time\n        multi_level_squares = self.square_anchor_generator.grid_priors(\n            featmap_sizes, device=device)\n        squares_list = [multi_level_squares for _ in range(num_imgs)]\n\n        # for each image, we compute multi level guided anchors\n        guided_anchors_list = []\n        loc_mask_list = []\n        for img_id, img_meta in enumerate(batch_img_metas):\n            multi_level_guided_anchors = []\n            multi_level_loc_mask = []\n            for i in range(num_levels):\n                squares = squares_list[img_id][i]\n                shape_pred = shape_preds[i][img_id]\n                loc_pred = loc_preds[i][img_id]\n                guided_anchors, loc_mask = self._get_guided_anchors_single(\n                    squares,\n                    shape_pred,\n                    loc_pred,\n                    use_loc_filter=use_loc_filter)\n                multi_level_guided_anchors.append(guided_anchors)\n                multi_level_loc_mask.append(loc_mask)\n            guided_anchors_list.append(multi_level_guided_anchors)\n            loc_mask_list.append(multi_level_loc_mask)\n        return squares_list, guided_anchors_list, loc_mask_list\n\n    def _get_guided_anchors_single(\n            self,\n            squares: Tensor,\n            shape_pred: Tensor,\n            loc_pred: Tensor,\n            use_loc_filter: bool = False) -> Tuple[Tensor]:\n        \"\"\"Get guided anchors and loc masks for a single level.\n\n        Args:\n            squares (tensor): Squares of a single level.\n            shape_pred (tensor): Shape predictions of a single level.\n            loc_pred (tensor): Loc predictions of a single level.\n            use_loc_filter (list[tensor]): Use loc filter or not.\n                Defaults to False.\n\n        Returns:\n            tuple: guided anchors, location masks\n        \"\"\"\n        # calculate location filtering mask\n        loc_pred = loc_pred.sigmoid().detach()\n        if use_loc_filter:\n            loc_mask = loc_pred >= self.loc_filter_thr\n        else:\n            loc_mask = loc_pred >= 0.0\n        mask = loc_mask.permute(1, 2, 0).expand(-1, -1, self.num_base_priors)\n        mask = mask.contiguous().view(-1)\n        # calculate guided anchors\n        squares = squares[mask]\n        anchor_deltas = shape_pred.permute(1, 2, 0).contiguous().view(\n            -1, 2).detach()[mask]\n        bbox_deltas = anchor_deltas.new_full(squares.size(), 0)\n        bbox_deltas[:, 2:] = anchor_deltas\n        guided_anchors = self.anchor_coder.decode(\n            squares, bbox_deltas, wh_ratio_clip=1e-6)\n        return guided_anchors, mask\n\n    def ga_loc_targets(self, batch_gt_instances: InstanceList,\n                       featmap_sizes: List[Tuple[int, int]]) -> tuple:\n        \"\"\"Compute location targets for guided anchoring.\n\n        Each feature map is divided into positive, negative and ignore regions.\n        - positive regions: target 1, weight 1\n        - ignore regions: target 0, weight 0\n        - negative regions: target 0, weight 0.1\n\n        Args:\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            featmap_sizes (list[tuple]): Multi level sizes of each feature\n                maps.\n\n        Returns:\n            tuple: Returns a tuple containing location targets.\n        \"\"\"\n        anchor_scale = self.approx_anchor_generator.octave_base_scale\n        anchor_strides = self.approx_anchor_generator.strides\n        # Currently only supports same stride in x and y direction.\n        for stride in anchor_strides:\n            assert (stride[0] == stride[1])\n        anchor_strides = [stride[0] for stride in anchor_strides]\n\n        center_ratio = self.train_cfg['center_ratio']\n        ignore_ratio = self.train_cfg['ignore_ratio']\n        img_per_gpu = len(batch_gt_instances)\n        num_lvls = len(featmap_sizes)\n        r1 = (1 - center_ratio) / 2\n        r2 = (1 - ignore_ratio) / 2\n        all_loc_targets = []\n        all_loc_weights = []\n        all_ignore_map = []\n        for lvl_id in range(num_lvls):\n            h, w = featmap_sizes[lvl_id]\n            loc_targets = torch.zeros(\n                img_per_gpu,\n                1,\n                h,\n                w,\n                device=batch_gt_instances[0].bboxes.device,\n                dtype=torch.float32)\n            loc_weights = torch.full_like(loc_targets, -1)\n            ignore_map = torch.zeros_like(loc_targets)\n            all_loc_targets.append(loc_targets)\n            all_loc_weights.append(loc_weights)\n            all_ignore_map.append(ignore_map)\n        for img_id in range(img_per_gpu):\n            gt_bboxes = batch_gt_instances[img_id].bboxes\n            scale = torch.sqrt((gt_bboxes[:, 2] - gt_bboxes[:, 0]) *\n                               (gt_bboxes[:, 3] - gt_bboxes[:, 1]))\n            min_anchor_size = scale.new_full(\n                (1, ), float(anchor_scale * anchor_strides[0]))\n            # assign gt bboxes to different feature levels w.r.t. their scales\n            target_lvls = torch.floor(\n                torch.log2(scale) - torch.log2(min_anchor_size) + 0.5)\n            target_lvls = target_lvls.clamp(min=0, max=num_lvls - 1).long()\n            for gt_id in range(gt_bboxes.size(0)):\n                lvl = target_lvls[gt_id].item()\n                # rescaled to corresponding feature map\n                gt_ = gt_bboxes[gt_id, :4] / anchor_strides[lvl]\n                # calculate ignore regions\n                ignore_x1, ignore_y1, ignore_x2, ignore_y2 = calc_region(\n                    gt_, r2, featmap_sizes[lvl])\n                # calculate positive (center) regions\n                ctr_x1, ctr_y1, ctr_x2, ctr_y2 = calc_region(\n                    gt_, r1, featmap_sizes[lvl])\n                all_loc_targets[lvl][img_id, 0, ctr_y1:ctr_y2 + 1,\n                                     ctr_x1:ctr_x2 + 1] = 1\n                all_loc_weights[lvl][img_id, 0, ignore_y1:ignore_y2 + 1,\n                                     ignore_x1:ignore_x2 + 1] = 0\n                all_loc_weights[lvl][img_id, 0, ctr_y1:ctr_y2 + 1,\n                                     ctr_x1:ctr_x2 + 1] = 1\n                # calculate ignore map on nearby low level feature\n                if lvl > 0:\n                    d_lvl = lvl - 1\n                    # rescaled to corresponding feature map\n                    gt_ = gt_bboxes[gt_id, :4] / anchor_strides[d_lvl]\n                    ignore_x1, ignore_y1, ignore_x2, ignore_y2 = calc_region(\n                        gt_, r2, featmap_sizes[d_lvl])\n                    all_ignore_map[d_lvl][img_id, 0, ignore_y1:ignore_y2 + 1,\n                                          ignore_x1:ignore_x2 + 1] = 1\n                # calculate ignore map on nearby high level feature\n                if lvl < num_lvls - 1:\n                    u_lvl = lvl + 1\n                    # rescaled to corresponding feature map\n                    gt_ = gt_bboxes[gt_id, :4] / anchor_strides[u_lvl]\n                    ignore_x1, ignore_y1, ignore_x2, ignore_y2 = calc_region(\n                        gt_, r2, featmap_sizes[u_lvl])\n                    all_ignore_map[u_lvl][img_id, 0, ignore_y1:ignore_y2 + 1,\n                                          ignore_x1:ignore_x2 + 1] = 1\n        for lvl_id in range(num_lvls):\n            # ignore negative regions w.r.t. ignore map\n            all_loc_weights[lvl_id][(all_loc_weights[lvl_id] < 0)\n                                    & (all_ignore_map[lvl_id] > 0)] = 0\n            # set negative regions with weight 0.1\n            all_loc_weights[lvl_id][all_loc_weights[lvl_id] < 0] = 0.1\n        # loc average factor to balance loss\n        loc_avg_factor = sum(\n            [t.size(0) * t.size(-1) * t.size(-2)\n             for t in all_loc_targets]) / 200\n        return all_loc_targets, all_loc_weights, loc_avg_factor\n\n    def _ga_shape_target_single(self,\n                                flat_approxs: Tensor,\n                                inside_flags: Tensor,\n                                flat_squares: Tensor,\n                                gt_instances: InstanceData,\n                                gt_instances_ignore: Optional[InstanceData],\n                                img_meta: dict,\n                                unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute guided anchoring targets.\n\n        This function returns sampled anchors and gt bboxes directly\n        rather than calculates regression targets.\n\n        Args:\n            flat_approxs (Tensor): flat approxs of a single image,\n                shape (n, 4)\n            inside_flags (Tensor): inside flags of a single image,\n                shape (n, ).\n            flat_squares (Tensor): flat squares of a single image,\n                shape (approxs_per_octave * n, 4)\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n            img_meta (dict): Meta info of a single image.\n            unmap_outputs (bool): unmap outputs or not.\n\n        Returns:\n            tuple: Returns a tuple containing shape targets of each image.\n        \"\"\"\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # assign gt and sample anchors\n        num_square = flat_squares.size(0)\n        approxs = flat_approxs.view(num_square, self.approxs_per_octave, 4)\n        approxs = approxs[inside_flags, ...]\n        squares = flat_squares[inside_flags, :]\n\n        pred_instances = InstanceData()\n        pred_instances.priors = squares\n        pred_instances.approxs = approxs\n\n        assign_result = self.ga_assigner.assign(\n            pred_instances=pred_instances,\n            gt_instances=gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n        sampling_result = self.ga_sampler.sample(\n            assign_result=assign_result,\n            pred_instances=pred_instances,\n            gt_instances=gt_instances)\n\n        bbox_anchors = torch.zeros_like(squares)\n        bbox_gts = torch.zeros_like(squares)\n        bbox_weights = torch.zeros_like(squares)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            bbox_anchors[pos_inds, :] = sampling_result.pos_bboxes\n            bbox_gts[pos_inds, :] = sampling_result.pos_gt_bboxes\n            bbox_weights[pos_inds, :] = 1.0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_squares.size(0)\n            bbox_anchors = unmap(bbox_anchors, num_total_anchors, inside_flags)\n            bbox_gts = unmap(bbox_gts, num_total_anchors, inside_flags)\n            bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags)\n\n        return (bbox_anchors, bbox_gts, bbox_weights, pos_inds, neg_inds,\n                sampling_result)\n\n    def ga_shape_targets(self,\n                         approx_list: List[List[Tensor]],\n                         inside_flag_list: List[List[Tensor]],\n                         square_list: List[List[Tensor]],\n                         batch_gt_instances: InstanceList,\n                         batch_img_metas: List[dict],\n                         batch_gt_instances_ignore: OptInstanceList = None,\n                         unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute guided anchoring targets.\n\n        Args:\n            approx_list (list[list[Tensor]]): Multi level approxs of each\n                image.\n            inside_flag_list (list[list[Tensor]]): Multi level inside flags\n                of each image.\n            square_list (list[list[Tensor]]): Multi level squares of each\n                image.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): unmap outputs or not. Defaults to None.\n\n        Returns:\n            tuple:  Returns a tuple containing shape targets.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(approx_list) == len(inside_flag_list) == len(\n            square_list) == num_imgs\n        # anchor number of multi levels\n        num_level_squares = [squares.size(0) for squares in square_list[0]]\n        # concat all level anchors and flags to a single tensor\n        inside_flag_flat_list = []\n        approx_flat_list = []\n        square_flat_list = []\n        for i in range(num_imgs):\n            assert len(square_list[i]) == len(inside_flag_list[i])\n            inside_flag_flat_list.append(torch.cat(inside_flag_list[i]))\n            approx_flat_list.append(torch.cat(approx_list[i]))\n            square_flat_list.append(torch.cat(square_list[i]))\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None for _ in range(num_imgs)]\n        (all_bbox_anchors, all_bbox_gts, all_bbox_weights, pos_inds_list,\n         neg_inds_list, sampling_results_list) = multi_apply(\n             self._ga_shape_target_single,\n             approx_flat_list,\n             inside_flag_flat_list,\n             square_flat_list,\n             batch_gt_instances,\n             batch_gt_instances_ignore,\n             batch_img_metas,\n             unmap_outputs=unmap_outputs)\n        # sampled anchors of all images\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        bbox_anchors_list = images_to_levels(all_bbox_anchors,\n                                             num_level_squares)\n        bbox_gts_list = images_to_levels(all_bbox_gts, num_level_squares)\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_squares)\n        return (bbox_anchors_list, bbox_gts_list, bbox_weights_list,\n                avg_factor)\n\n    def loss_shape_single(self, shape_pred: Tensor, bbox_anchors: Tensor,\n                          bbox_gts: Tensor, anchor_weights: Tensor,\n                          avg_factor: int) -> Tensor:\n        \"\"\"Compute shape loss in single level.\"\"\"\n        shape_pred = shape_pred.permute(0, 2, 3, 1).contiguous().view(-1, 2)\n        bbox_anchors = bbox_anchors.contiguous().view(-1, 4)\n        bbox_gts = bbox_gts.contiguous().view(-1, 4)\n        anchor_weights = anchor_weights.contiguous().view(-1, 4)\n        bbox_deltas = bbox_anchors.new_full(bbox_anchors.size(), 0)\n        bbox_deltas[:, 2:] += shape_pred\n        # filter out negative samples to speed-up weighted_bounded_iou_loss\n        inds = torch.nonzero(\n            anchor_weights[:, 0] > 0, as_tuple=False).squeeze(1)\n        bbox_deltas_ = bbox_deltas[inds]\n        bbox_anchors_ = bbox_anchors[inds]\n        bbox_gts_ = bbox_gts[inds]\n        anchor_weights_ = anchor_weights[inds]\n        pred_anchors_ = self.anchor_coder.decode(\n            bbox_anchors_, bbox_deltas_, wh_ratio_clip=1e-6)\n        loss_shape = self.loss_shape(\n            pred_anchors_, bbox_gts_, anchor_weights_, avg_factor=avg_factor)\n        return loss_shape\n\n    def loss_loc_single(self, loc_pred: Tensor, loc_target: Tensor,\n                        loc_weight: Tensor, avg_factor: float) -> Tensor:\n        \"\"\"Compute location loss in single level.\"\"\"\n        loss_loc = self.loss_loc(\n            loc_pred.reshape(-1, 1),\n            loc_target.reshape(-1).long(),\n            loc_weight.reshape(-1),\n            avg_factor=avg_factor)\n        return loss_loc\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            shape_preds: List[Tensor],\n            loc_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            shape_preds (list[Tensor]): shape predictions for each scale\n                level with shape (N, 1, H, W).\n            loc_preds (list[Tensor]): location predictions for each scale\n                level with shape (N, num_anchors * 2, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.approx_anchor_generator.num_levels\n\n        device = cls_scores[0].device\n\n        # get loc targets\n        loc_targets, loc_weights, loc_avg_factor = self.ga_loc_targets(\n            batch_gt_instances, featmap_sizes)\n\n        # get sampled approxes\n        approxs_list, inside_flag_list = self.get_sampled_approxs(\n            featmap_sizes, batch_img_metas, device=device)\n        # get squares and guided anchors\n        squares_list, guided_anchors_list, _ = self.get_anchors(\n            featmap_sizes,\n            shape_preds,\n            loc_preds,\n            batch_img_metas,\n            device=device)\n\n        # get shape targets\n        shape_targets = self.ga_shape_targets(approxs_list, inside_flag_list,\n                                              squares_list, batch_gt_instances,\n                                              batch_img_metas)\n        (bbox_anchors_list, bbox_gts_list, anchor_weights_list,\n         ga_avg_factor) = shape_targets\n\n        # get anchor targets\n        cls_reg_targets = self.get_targets(\n            guided_anchors_list,\n            inside_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor) = cls_reg_targets\n\n        # anchor number of multi levels\n        num_level_anchors = [\n            anchors.size(0) for anchors in guided_anchors_list[0]\n        ]\n        # concat all level anchors to a single tensor\n        concat_anchor_list = []\n        for i in range(len(guided_anchors_list)):\n            concat_anchor_list.append(torch.cat(guided_anchors_list[i]))\n        all_anchor_list = images_to_levels(concat_anchor_list,\n                                           num_level_anchors)\n\n        # get classification and bbox regression losses\n        losses_cls, losses_bbox = multi_apply(\n            self.loss_by_feat_single,\n            cls_scores,\n            bbox_preds,\n            all_anchor_list,\n            labels_list,\n            label_weights_list,\n            bbox_targets_list,\n            bbox_weights_list,\n            avg_factor=avg_factor)\n\n        # get anchor location loss\n        losses_loc = []\n        for i in range(len(loc_preds)):\n            loss_loc = self.loss_loc_single(\n                loc_preds[i],\n                loc_targets[i],\n                loc_weights[i],\n                avg_factor=loc_avg_factor)\n            losses_loc.append(loss_loc)\n\n        # get anchor shape loss\n        losses_shape = []\n        for i in range(len(shape_preds)):\n            loss_shape = self.loss_shape_single(\n                shape_preds[i],\n                bbox_anchors_list[i],\n                bbox_gts_list[i],\n                anchor_weights_list[i],\n                avg_factor=ga_avg_factor)\n            losses_shape.append(loss_shape)\n\n        return dict(\n            loss_cls=losses_cls,\n            loss_bbox=losses_bbox,\n            loss_shape=losses_shape,\n            loss_loc=losses_loc)\n\n    def predict_by_feat(self,\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        shape_preds: List[Tensor],\n                        loc_preds: List[Tensor],\n                        batch_img_metas: List[dict],\n                        cfg: OptConfigType = None,\n                        rescale: bool = False) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_scores (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            shape_preds (list[Tensor]): shape predictions for each scale\n                level with shape (N, 1, H, W).\n            loc_preds (list[Tensor]): location predictions for each scale\n                level with shape (N, num_anchors * 2, H, W).\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n                Defaults to None.\n            cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4), the last\n              dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds) == len(shape_preds) == len(\n            loc_preds)\n        num_levels = len(cls_scores)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        device = cls_scores[0].device\n        # get guided anchors\n        _, guided_anchors, loc_masks = self.get_anchors(\n            featmap_sizes,\n            shape_preds,\n            loc_preds,\n            batch_img_metas,\n            use_loc_filter=not self.training,\n            device=device)\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score_list = [\n                cls_scores[i][img_id].detach() for i in range(num_levels)\n            ]\n            bbox_pred_list = [\n                bbox_preds[i][img_id].detach() for i in range(num_levels)\n            ]\n            guided_anchor_list = [\n                guided_anchors[img_id][i].detach() for i in range(num_levels)\n            ]\n            loc_mask_list = [\n                loc_masks[img_id][i].detach() for i in range(num_levels)\n            ]\n            proposals = self._predict_by_feat_single(\n                cls_scores=cls_score_list,\n                bbox_preds=bbox_pred_list,\n                mlvl_anchors=guided_anchor_list,\n                mlvl_masks=loc_mask_list,\n                img_meta=batch_img_metas[img_id],\n                cfg=cfg,\n                rescale=rescale)\n            result_list.append(proposals)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_scores: List[Tensor],\n                                bbox_preds: List[Tensor],\n                                mlvl_anchors: List[Tensor],\n                                mlvl_masks: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigType,\n                                rescale: bool = False) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            mlvl_anchors (list[Tensor]): Each element in the list is\n                the anchors of a single level in feature pyramid. it has\n                shape (num_priors, 4).\n            mlvl_masks (list[Tensor]): Each element in the list is location\n                masks of a single level.\n            img_meta (dict): Image meta info.\n            cfg (:obj:`ConfigDict` or dict): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4), the last\n              dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        assert len(cls_scores) == len(bbox_preds) == len(mlvl_anchors)\n        mlvl_bbox_preds = []\n        mlvl_valid_anchors = []\n        mlvl_scores = []\n        for cls_score, bbox_pred, anchors, mask in zip(cls_scores, bbox_preds,\n                                                       mlvl_anchors,\n                                                       mlvl_masks):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n            # if no location is kept, end.\n            if mask.sum() == 0:\n                continue\n            # reshape scores and bbox_pred\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            if self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                scores = cls_score.softmax(-1)\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4)\n            # filter scores, bbox_pred w.r.t. mask.\n            # anchors are filtered in get_anchors() beforehand.\n            scores = scores[mask, :]\n            bbox_pred = bbox_pred[mask, :]\n            if scores.dim() == 0:\n                anchors = anchors.unsqueeze(0)\n                scores = scores.unsqueeze(0)\n                bbox_pred = bbox_pred.unsqueeze(0)\n            # filter anchors, bbox_pred, scores w.r.t. scores\n            nms_pre = cfg.get('nms_pre', -1)\n            if nms_pre > 0 and scores.shape[0] > nms_pre:\n                if self.use_sigmoid_cls:\n                    max_scores, _ = scores.max(dim=1)\n                else:\n                    # remind that we set FG labels to [0, num_class-1]\n                    # since mmdet v2.0\n                    # BG cat_id: num_class\n                    max_scores, _ = scores[:, :-1].max(dim=1)\n                _, topk_inds = max_scores.topk(nms_pre)\n                anchors = anchors[topk_inds, :]\n                bbox_pred = bbox_pred[topk_inds, :]\n                scores = scores[topk_inds, :]\n\n            mlvl_bbox_preds.append(bbox_pred)\n            mlvl_valid_anchors.append(anchors)\n            mlvl_scores.append(scores)\n\n        mlvl_bbox_preds = torch.cat(mlvl_bbox_preds)\n        mlvl_anchors = torch.cat(mlvl_valid_anchors)\n        mlvl_scores = torch.cat(mlvl_scores)\n        mlvl_bboxes = self.bbox_coder.decode(\n            mlvl_anchors, mlvl_bbox_preds, max_shape=img_meta['img_shape'])\n\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            mlvl_bboxes /= mlvl_bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n\n        if self.use_sigmoid_cls:\n            # Add a dummy background class to the backend when using sigmoid\n            # remind that we set FG labels to [0, num_class-1] since mmdet v2.0\n            # BG cat_id: num_class\n            padding = mlvl_scores.new_zeros(mlvl_scores.shape[0], 1)\n            mlvl_scores = torch.cat([mlvl_scores, padding], dim=1)\n        # multi class NMS\n        det_bboxes, det_labels = multiclass_nms(mlvl_bboxes, mlvl_scores,\n                                                cfg.score_thr, cfg.nms,\n                                                cfg.max_per_img)\n\n        results = InstanceData()\n        results.bboxes = det_bboxes[:, :-1]\n        results.scores = det_bboxes[:, -1]\n        results.labels = det_labels\n        return results\n"
  },
  {
    "path": "mmdet/models/dense_heads/lad_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import InstanceList, OptInstanceList\nfrom ..utils import levels_to_images, multi_apply, unpack_gt_instances\nfrom .paa_head import PAAHead\n\n\n@MODELS.register_module()\nclass LADHead(PAAHead):\n    \"\"\"Label Assignment Head from the paper: `Improving Object Detection by\n    Label Assignment Distillation <https://arxiv.org/pdf/2108.10520.pdf>`_\"\"\"\n\n    def get_label_assignment(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            iou_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> tuple:\n        \"\"\"Get label assignment (from teacher).\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            iou_preds (list[Tensor]): iou_preds for each scale\n                level with shape (N, num_anchors * 1, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            tuple: Returns a tuple containing label assignment variables.\n\n            - labels (Tensor): Labels of all anchors, each with\n              shape (num_anchors,).\n            - labels_weight (Tensor): Label weights of all anchor.\n              each with shape (num_anchors,).\n            - bboxes_target (Tensor): BBox targets of all anchors.\n              each with shape (num_anchors, 4).\n            - bboxes_weight (Tensor): BBox weights of all anchors.\n              each with shape (num_anchors, 4).\n            - pos_inds_flatten (Tensor): Contains all index of positive\n              sample in all anchor.\n            - pos_anchors (Tensor): Positive anchors.\n            - num_pos (int): Number of positive anchors.\n        \"\"\"\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n        )\n        (labels, labels_weight, bboxes_target, bboxes_weight, pos_inds,\n         pos_gt_index) = cls_reg_targets\n        cls_scores = levels_to_images(cls_scores)\n        cls_scores = [\n            item.reshape(-1, self.cls_out_channels) for item in cls_scores\n        ]\n        bbox_preds = levels_to_images(bbox_preds)\n        bbox_preds = [item.reshape(-1, 4) for item in bbox_preds]\n        pos_losses_list, = multi_apply(self.get_pos_loss, anchor_list,\n                                       cls_scores, bbox_preds, labels,\n                                       labels_weight, bboxes_target,\n                                       bboxes_weight, pos_inds)\n\n        with torch.no_grad():\n            reassign_labels, reassign_label_weight, \\\n                reassign_bbox_weights, num_pos = multi_apply(\n                    self.paa_reassign,\n                    pos_losses_list,\n                    labels,\n                    labels_weight,\n                    bboxes_weight,\n                    pos_inds,\n                    pos_gt_index,\n                    anchor_list)\n            num_pos = sum(num_pos)\n        # convert all tensor list to a flatten tensor\n        labels = torch.cat(reassign_labels, 0).view(-1)\n        flatten_anchors = torch.cat(\n            [torch.cat(item, 0) for item in anchor_list])\n        labels_weight = torch.cat(reassign_label_weight, 0).view(-1)\n        bboxes_target = torch.cat(bboxes_target,\n                                  0).view(-1, bboxes_target[0].size(-1))\n\n        pos_inds_flatten = ((labels >= 0)\n                            &\n                            (labels < self.num_classes)).nonzero().reshape(-1)\n\n        if num_pos:\n            pos_anchors = flatten_anchors[pos_inds_flatten]\n        else:\n            pos_anchors = None\n\n        label_assignment_results = (labels, labels_weight, bboxes_target,\n                                    bboxes_weight, pos_inds_flatten,\n                                    pos_anchors, num_pos)\n        return label_assignment_results\n\n    def loss(self, x: List[Tensor], label_assignment_results: tuple,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Forward train with the available label assignment (student receives\n        from teacher).\n\n        Args:\n            x (list[Tensor]): Features from FPN.\n            label_assignment_results (tuple): As the outputs defined in the\n                function `self.get_label_assignment`.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            losses: (dict[str, Tensor]): A dictionary of loss components.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n\n        outs = self(x)\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas)\n        losses = self.loss_by_feat(\n            *loss_inputs,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            label_assignment_results=label_assignment_results)\n        return losses\n\n    def loss_by_feat(self,\n                     cls_scores: List[Tensor],\n                     bbox_preds: List[Tensor],\n                     iou_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict],\n                     batch_gt_instances_ignore: OptInstanceList = None,\n                     label_assignment_results: Optional[tuple] = None) -> dict:\n        \"\"\"Compute losses of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            iou_preds (list[Tensor]): iou_preds for each scale\n                level with shape (N, num_anchors * 1, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            label_assignment_results (tuple, optional): As the outputs defined\n                in the function `self.get_\n                label_assignment`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss gmm_assignment.\n        \"\"\"\n\n        (labels, labels_weight, bboxes_target, bboxes_weight, pos_inds_flatten,\n         pos_anchors, num_pos) = label_assignment_results\n\n        cls_scores = levels_to_images(cls_scores)\n        cls_scores = [\n            item.reshape(-1, self.cls_out_channels) for item in cls_scores\n        ]\n        bbox_preds = levels_to_images(bbox_preds)\n        bbox_preds = [item.reshape(-1, 4) for item in bbox_preds]\n        iou_preds = levels_to_images(iou_preds)\n        iou_preds = [item.reshape(-1, 1) for item in iou_preds]\n\n        # convert all tensor list to a flatten tensor\n        cls_scores = torch.cat(cls_scores, 0).view(-1, cls_scores[0].size(-1))\n        bbox_preds = torch.cat(bbox_preds, 0).view(-1, bbox_preds[0].size(-1))\n        iou_preds = torch.cat(iou_preds, 0).view(-1, iou_preds[0].size(-1))\n\n        losses_cls = self.loss_cls(\n            cls_scores,\n            labels,\n            labels_weight,\n            avg_factor=max(num_pos, len(batch_img_metas)))  # avoid num_pos=0\n        if num_pos:\n            pos_bbox_pred = self.bbox_coder.decode(\n                pos_anchors, bbox_preds[pos_inds_flatten])\n            pos_bbox_target = bboxes_target[pos_inds_flatten]\n            iou_target = bbox_overlaps(\n                pos_bbox_pred.detach(), pos_bbox_target, is_aligned=True)\n            losses_iou = self.loss_centerness(\n                iou_preds[pos_inds_flatten],\n                iou_target.unsqueeze(-1),\n                avg_factor=num_pos)\n            losses_bbox = self.loss_bbox(\n                pos_bbox_pred, pos_bbox_target, avg_factor=num_pos)\n\n        else:\n            losses_iou = iou_preds.sum() * 0\n            losses_bbox = bbox_preds.sum() * 0\n\n        return dict(\n            loss_cls=losses_cls, loss_bbox=losses_bbox, loss_iou=losses_iou)\n"
  },
  {
    "path": "mmdet/models/dense_heads/ld_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import ConfigType, InstanceList, OptInstanceList, reduce_mean\nfrom ..utils import multi_apply, unpack_gt_instances\nfrom .gfl_head import GFLHead\n\n\n@MODELS.register_module()\nclass LDHead(GFLHead):\n    \"\"\"Localization distillation Head. (Short description)\n\n    It utilizes the learned bbox distributions to transfer the localization\n    dark knowledge from teacher to student. Original paper: `Localization\n    Distillation for Object Detection. <https://arxiv.org/abs/2102.12252>`_\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        loss_ld (:obj:`ConfigDict` or dict): Config of Localization\n            Distillation Loss (LD), T is the temperature for distillation.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 loss_ld: ConfigType = dict(\n                     type='LocalizationDistillationLoss',\n                     loss_weight=0.25,\n                     T=10),\n                 **kwargs) -> dict:\n\n        super().__init__(\n            num_classes=num_classes, in_channels=in_channels, **kwargs)\n        self.loss_ld = MODELS.build(loss_ld)\n\n    def loss_by_feat_single(self, anchors: Tensor, cls_score: Tensor,\n                            bbox_pred: Tensor, labels: Tensor,\n                            label_weights: Tensor, bbox_targets: Tensor,\n                            stride: Tuple[int], soft_targets: Tensor,\n                            avg_factor: int):\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            anchors (Tensor): Box reference for each scale level with shape\n                (N, num_total_anchors, 4).\n            cls_score (Tensor): Cls and quality joint scores for each scale\n                level has shape (N, num_classes, H, W).\n            bbox_pred (Tensor): Box distribution logits for each scale\n                level with shape (N, 4*(n+1), H, W), n is max value of integral\n                set.\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors)\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (N, num_total_anchors, 4).\n            stride (tuple): Stride in this scale level.\n            soft_targets (Tensor): Soft BBox regression targets.\n            avg_factor (int): Average factor that is used to average\n                the loss. When using sampling method, avg_factor is usually\n                the sum of positive and negative priors. When using\n                `PseudoSampler`, `avg_factor` is usually equal to the number\n                of positive priors.\n\n        Returns:\n            dict[tuple, Tensor]: Loss components and weight targets.\n        \"\"\"\n        assert stride[0] == stride[1], 'h stride is not equal to w stride!'\n        anchors = anchors.reshape(-1, 4)\n        cls_score = cls_score.permute(0, 2, 3,\n                                      1).reshape(-1, self.cls_out_channels)\n        bbox_pred = bbox_pred.permute(0, 2, 3,\n                                      1).reshape(-1, 4 * (self.reg_max + 1))\n        soft_targets = soft_targets.permute(0, 2, 3,\n                                            1).reshape(-1,\n                                                       4 * (self.reg_max + 1))\n\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().squeeze(1)\n        score = label_weights.new_zeros(labels.shape)\n\n        if len(pos_inds) > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n            pos_anchors = anchors[pos_inds]\n            pos_anchor_centers = self.anchor_center(pos_anchors) / stride[0]\n\n            weight_targets = cls_score.detach().sigmoid()\n            weight_targets = weight_targets.max(dim=1)[0][pos_inds]\n            pos_bbox_pred_corners = self.integral(pos_bbox_pred)\n            pos_decode_bbox_pred = self.bbox_coder.decode(\n                pos_anchor_centers, pos_bbox_pred_corners)\n            pos_decode_bbox_targets = pos_bbox_targets / stride[0]\n            score[pos_inds] = bbox_overlaps(\n                pos_decode_bbox_pred.detach(),\n                pos_decode_bbox_targets,\n                is_aligned=True)\n            pred_corners = pos_bbox_pred.reshape(-1, self.reg_max + 1)\n            pos_soft_targets = soft_targets[pos_inds]\n            soft_corners = pos_soft_targets.reshape(-1, self.reg_max + 1)\n\n            target_corners = self.bbox_coder.encode(pos_anchor_centers,\n                                                    pos_decode_bbox_targets,\n                                                    self.reg_max).reshape(-1)\n\n            # regression loss\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_decode_bbox_targets,\n                weight=weight_targets,\n                avg_factor=1.0)\n\n            # dfl loss\n            loss_dfl = self.loss_dfl(\n                pred_corners,\n                target_corners,\n                weight=weight_targets[:, None].expand(-1, 4).reshape(-1),\n                avg_factor=4.0)\n\n            # ld loss\n            loss_ld = self.loss_ld(\n                pred_corners,\n                soft_corners,\n                weight=weight_targets[:, None].expand(-1, 4).reshape(-1),\n                avg_factor=4.0)\n\n        else:\n            loss_ld = bbox_pred.sum() * 0\n            loss_bbox = bbox_pred.sum() * 0\n            loss_dfl = bbox_pred.sum() * 0\n            weight_targets = bbox_pred.new_tensor(0)\n\n        # cls (qfl) loss\n        loss_cls = self.loss_cls(\n            cls_score, (labels, score),\n            weight=label_weights,\n            avg_factor=avg_factor)\n\n        return loss_cls, loss_bbox, loss_dfl, loss_ld, weight_targets.sum()\n\n    def loss(self, x: List[Tensor], out_teacher: Tuple[Tensor],\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"\n        Args:\n            x (list[Tensor]): Features from FPN.\n            out_teacher (tuple[Tensor]): The output of teacher.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            tuple[dict, list]: The loss components and proposals of each image.\n\n            - losses (dict[str, Tensor]): A dictionary of loss components.\n            - proposal_list (list[Tensor]): Proposals of each image.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n\n        outs = self(x)\n        soft_targets = out_teacher[1]\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas,\n                              soft_targets)\n        losses = self.loss_by_feat(\n            *loss_inputs, batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        return losses\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            soft_targets: List[Tensor],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Compute losses of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Cls and quality scores for each scale\n                level has shape (N, num_classes, H, W).\n            bbox_preds (list[Tensor]): Box distribution logits for each scale\n                level with shape (N, 4*(n+1), H, W), n is max value of integral\n                set.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            soft_targets (list[Tensor]): Soft BBox regression targets.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor) = cls_reg_targets\n\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n\n        losses_cls, losses_bbox, losses_dfl, losses_ld, \\\n            avg_factor = multi_apply(\n                self.loss_by_feat_single,\n                anchor_list,\n                cls_scores,\n                bbox_preds,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                self.prior_generator.strides,\n                soft_targets,\n                avg_factor=avg_factor)\n\n        avg_factor = sum(avg_factor) + 1e-6\n        avg_factor = reduce_mean(avg_factor).item()\n        losses_bbox = [x / avg_factor for x in losses_bbox]\n        losses_dfl = [x / avg_factor for x in losses_dfl]\n        return dict(\n            loss_cls=losses_cls,\n            loss_bbox=losses_bbox,\n            loss_dfl=losses_dfl,\n            loss_ld=losses_ld)\n"
  },
  {
    "path": "mmdet/models/dense_heads/mask2former_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Conv2d\nfrom mmcv.ops import point_sample\nfrom mmengine.model import ModuleList, caffe2_xavier_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig, reduce_mean\nfrom ..layers import Mask2FormerTransformerDecoder, SinePositionalEncoding\nfrom ..utils import get_uncertain_point_coords_with_randomness\nfrom .anchor_free_head import AnchorFreeHead\nfrom .maskformer_head import MaskFormerHead\n\n\n@MODELS.register_module()\nclass Mask2FormerHead(MaskFormerHead):\n    \"\"\"Implements the Mask2Former head.\n\n    See `Masked-attention Mask Transformer for Universal Image\n    Segmentation <https://arxiv.org/pdf/2112.01527>`_ for details.\n\n    Args:\n        in_channels (list[int]): Number of channels in the input feature map.\n        feat_channels (int): Number of channels for features.\n        out_channels (int): Number of channels for output.\n        num_things_classes (int): Number of things.\n        num_stuff_classes (int): Number of stuff.\n        num_queries (int): Number of query in Transformer decoder.\n        pixel_decoder (:obj:`ConfigDict` or dict): Config for pixel\n            decoder. Defaults to None.\n        enforce_decoder_input_project (bool, optional): Whether to add\n            a layer to change the embed_dim of tranformer encoder in\n            pixel decoder to the embed_dim of transformer decoder.\n            Defaults to False.\n        transformer_decoder (:obj:`ConfigDict` or dict): Config for\n            transformer decoder. Defaults to None.\n        positional_encoding (:obj:`ConfigDict` or dict): Config for\n            transformer decoder position encoding. Defaults to\n            dict(num_feats=128, normalize=True).\n        loss_cls (:obj:`ConfigDict` or dict): Config of the classification\n            loss. Defaults to None.\n        loss_mask (:obj:`ConfigDict` or dict): Config of the mask loss.\n            Defaults to None.\n        loss_dice (:obj:`ConfigDict` or dict): Config of the dice loss.\n            Defaults to None.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config of\n            Mask2Former head.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            Mask2Former head.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: List[int],\n                 feat_channels: int,\n                 out_channels: int,\n                 num_things_classes: int = 80,\n                 num_stuff_classes: int = 53,\n                 num_queries: int = 100,\n                 num_transformer_feat_level: int = 3,\n                 pixel_decoder: ConfigType = ...,\n                 enforce_decoder_input_project: bool = False,\n                 transformer_decoder: ConfigType = ...,\n                 positional_encoding: ConfigType = dict(\n                     num_feats=128, normalize=True),\n                 loss_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=False,\n                     loss_weight=2.0,\n                     reduction='mean',\n                     class_weight=[1.0] * 133 + [0.1]),\n                 loss_mask: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     reduction='mean',\n                     loss_weight=5.0),\n                 loss_dice: ConfigType = dict(\n                     type='DiceLoss',\n                     use_sigmoid=True,\n                     activate=True,\n                     reduction='mean',\n                     naive_dice=True,\n                     eps=1.0,\n                     loss_weight=5.0),\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        super(AnchorFreeHead, self).__init__(init_cfg=init_cfg)\n        self.num_things_classes = num_things_classes\n        self.num_stuff_classes = num_stuff_classes\n        self.num_classes = self.num_things_classes + self.num_stuff_classes\n        self.num_queries = num_queries\n        self.num_transformer_feat_level = num_transformer_feat_level\n        self.num_heads = transformer_decoder.layer_cfg.cross_attn_cfg.num_heads\n        self.num_transformer_decoder_layers = transformer_decoder.num_layers\n        assert pixel_decoder.encoder.layer_cfg. \\\n            self_attn_cfg.num_levels == num_transformer_feat_level\n        pixel_decoder_ = copy.deepcopy(pixel_decoder)\n        pixel_decoder_.update(\n            in_channels=in_channels,\n            feat_channels=feat_channels,\n            out_channels=out_channels)\n        self.pixel_decoder = MODELS.build(pixel_decoder_)\n        self.transformer_decoder = Mask2FormerTransformerDecoder(\n            **transformer_decoder)\n        self.decoder_embed_dims = self.transformer_decoder.embed_dims\n\n        self.decoder_input_projs = ModuleList()\n        # from low resolution to high resolution\n        for _ in range(num_transformer_feat_level):\n            if (self.decoder_embed_dims != feat_channels\n                    or enforce_decoder_input_project):\n                self.decoder_input_projs.append(\n                    Conv2d(\n                        feat_channels, self.decoder_embed_dims, kernel_size=1))\n            else:\n                self.decoder_input_projs.append(nn.Identity())\n        self.decoder_positional_encoding = SinePositionalEncoding(\n            **positional_encoding)\n        self.query_embed = nn.Embedding(self.num_queries, feat_channels)\n        self.query_feat = nn.Embedding(self.num_queries, feat_channels)\n        # from low resolution to high resolution\n        self.level_embed = nn.Embedding(self.num_transformer_feat_level,\n                                        feat_channels)\n\n        self.cls_embed = nn.Linear(feat_channels, self.num_classes + 1)\n        self.mask_embed = nn.Sequential(\n            nn.Linear(feat_channels, feat_channels), nn.ReLU(inplace=True),\n            nn.Linear(feat_channels, feat_channels), nn.ReLU(inplace=True),\n            nn.Linear(feat_channels, out_channels))\n\n        self.test_cfg = test_cfg\n        self.train_cfg = train_cfg\n        if train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            self.sampler = TASK_UTILS.build(\n                self.train_cfg['sampler'], default_args=dict(context=self))\n            self.num_points = self.train_cfg.get('num_points', 12544)\n            self.oversample_ratio = self.train_cfg.get('oversample_ratio', 3.0)\n            self.importance_sample_ratio = self.train_cfg.get(\n                'importance_sample_ratio', 0.75)\n\n        self.class_weight = loss_cls.class_weight\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_mask = MODELS.build(loss_mask)\n        self.loss_dice = MODELS.build(loss_dice)\n\n    def init_weights(self) -> None:\n        for m in self.decoder_input_projs:\n            if isinstance(m, Conv2d):\n                caffe2_xavier_init(m, bias=0)\n\n        self.pixel_decoder.init_weights()\n\n        for p in self.transformer_decoder.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_normal_(p)\n\n    def _get_targets_single(self, cls_score: Tensor, mask_pred: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict) -> Tuple[Tensor]:\n        \"\"\"Compute classification and mask targets for one image.\n\n        Args:\n            cls_score (Tensor): Mask score logits from a single decoder layer\n                for one image. Shape (num_queries, cls_out_channels).\n            mask_pred (Tensor): Mask logits for a single decoder layer for one\n                image. Shape (num_queries, h, w).\n            gt_instances (:obj:`InstanceData`): It contains ``labels`` and\n                ``masks``.\n            img_meta (dict): Image informtation.\n\n        Returns:\n            tuple[Tensor]: A tuple containing the following for one image.\n\n                - labels (Tensor): Labels of each image. \\\n                    shape (num_queries, ).\n                - label_weights (Tensor): Label weights of each image. \\\n                    shape (num_queries, ).\n                - mask_targets (Tensor): Mask targets of each image. \\\n                    shape (num_queries, h, w).\n                - mask_weights (Tensor): Mask weights of each image. \\\n                    shape (num_queries, ).\n                - pos_inds (Tensor): Sampled positive indices for each \\\n                    image.\n                - neg_inds (Tensor): Sampled negative indices for each \\\n                    image.\n                - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        gt_labels = gt_instances.labels\n        gt_masks = gt_instances.masks\n        # sample points\n        num_queries = cls_score.shape[0]\n        num_gts = gt_labels.shape[0]\n\n        point_coords = torch.rand((1, self.num_points, 2),\n                                  device=cls_score.device)\n        # shape (num_queries, num_points)\n        mask_points_pred = point_sample(\n            mask_pred.unsqueeze(1), point_coords.repeat(num_queries, 1,\n                                                        1)).squeeze(1)\n        # shape (num_gts, num_points)\n        gt_points_masks = point_sample(\n            gt_masks.unsqueeze(1).float(), point_coords.repeat(num_gts, 1,\n                                                               1)).squeeze(1)\n\n        sampled_gt_instances = InstanceData(\n            labels=gt_labels, masks=gt_points_masks)\n        sampled_pred_instances = InstanceData(\n            scores=cls_score, masks=mask_points_pred)\n        # assign and sample\n        assign_result = self.assigner.assign(\n            pred_instances=sampled_pred_instances,\n            gt_instances=sampled_gt_instances,\n            img_meta=img_meta)\n        pred_instances = InstanceData(scores=cls_score, masks=mask_pred)\n        sampling_result = self.sampler.sample(\n            assign_result=assign_result,\n            pred_instances=pred_instances,\n            gt_instances=gt_instances)\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n\n        # label target\n        labels = gt_labels.new_full((self.num_queries, ),\n                                    self.num_classes,\n                                    dtype=torch.long)\n        labels[pos_inds] = gt_labels[sampling_result.pos_assigned_gt_inds]\n        label_weights = gt_labels.new_ones((self.num_queries, ))\n\n        # mask target\n        mask_targets = gt_masks[sampling_result.pos_assigned_gt_inds]\n        mask_weights = mask_pred.new_zeros((self.num_queries, ))\n        mask_weights[pos_inds] = 1.0\n\n        return (labels, label_weights, mask_targets, mask_weights, pos_inds,\n                neg_inds, sampling_result)\n\n    def _loss_by_feat_single(self, cls_scores: Tensor, mask_preds: Tensor,\n                             batch_gt_instances: List[InstanceData],\n                             batch_img_metas: List[dict]) -> Tuple[Tensor]:\n        \"\"\"Loss function for outputs from a single decoder layer.\n\n        Args:\n            cls_scores (Tensor): Mask score logits from a single decoder layer\n                for all images. Shape (batch_size, num_queries,\n                cls_out_channels). Note `cls_out_channels` should includes\n                background.\n            mask_preds (Tensor): Mask logits for a pixel decoder for all\n                images. Shape (batch_size, num_queries, h, w).\n            batch_gt_instances (list[obj:`InstanceData`]): each contains\n                ``labels`` and ``masks``.\n            batch_img_metas (list[dict]): List of image meta information.\n\n        Returns:\n            tuple[Tensor]: Loss components for outputs from a single \\\n                decoder layer.\n        \"\"\"\n        num_imgs = cls_scores.size(0)\n        cls_scores_list = [cls_scores[i] for i in range(num_imgs)]\n        mask_preds_list = [mask_preds[i] for i in range(num_imgs)]\n        (labels_list, label_weights_list, mask_targets_list, mask_weights_list,\n         avg_factor) = self.get_targets(cls_scores_list, mask_preds_list,\n                                        batch_gt_instances, batch_img_metas)\n        # shape (batch_size, num_queries)\n        labels = torch.stack(labels_list, dim=0)\n        # shape (batch_size, num_queries)\n        label_weights = torch.stack(label_weights_list, dim=0)\n        # shape (num_total_gts, h, w)\n        mask_targets = torch.cat(mask_targets_list, dim=0)\n        # shape (batch_size, num_queries)\n        mask_weights = torch.stack(mask_weights_list, dim=0)\n\n        # classfication loss\n        # shape (batch_size * num_queries, )\n        cls_scores = cls_scores.flatten(0, 1)\n        labels = labels.flatten(0, 1)\n        label_weights = label_weights.flatten(0, 1)\n\n        class_weight = cls_scores.new_tensor(self.class_weight)\n        loss_cls = self.loss_cls(\n            cls_scores,\n            labels,\n            label_weights,\n            avg_factor=class_weight[labels].sum())\n\n        num_total_masks = reduce_mean(cls_scores.new_tensor([avg_factor]))\n        num_total_masks = max(num_total_masks, 1)\n\n        # extract positive ones\n        # shape (batch_size, num_queries, h, w) -> (num_total_gts, h, w)\n        mask_preds = mask_preds[mask_weights > 0]\n\n        if mask_targets.shape[0] == 0:\n            # zero match\n            loss_dice = mask_preds.sum()\n            loss_mask = mask_preds.sum()\n            return loss_cls, loss_mask, loss_dice\n\n        with torch.no_grad():\n            points_coords = get_uncertain_point_coords_with_randomness(\n                mask_preds.unsqueeze(1), None, self.num_points,\n                self.oversample_ratio, self.importance_sample_ratio)\n            # shape (num_total_gts, h, w) -> (num_total_gts, num_points)\n            mask_point_targets = point_sample(\n                mask_targets.unsqueeze(1).float(), points_coords).squeeze(1)\n        # shape (num_queries, h, w) -> (num_queries, num_points)\n        mask_point_preds = point_sample(\n            mask_preds.unsqueeze(1), points_coords).squeeze(1)\n\n        # dice loss\n        loss_dice = self.loss_dice(\n            mask_point_preds, mask_point_targets, avg_factor=num_total_masks)\n\n        # mask loss\n        # shape (num_queries, num_points) -> (num_queries * num_points, )\n        mask_point_preds = mask_point_preds.reshape(-1)\n        # shape (num_total_gts, num_points) -> (num_total_gts * num_points, )\n        mask_point_targets = mask_point_targets.reshape(-1)\n        loss_mask = self.loss_mask(\n            mask_point_preds,\n            mask_point_targets,\n            avg_factor=num_total_masks * self.num_points)\n\n        return loss_cls, loss_mask, loss_dice\n\n    def _forward_head(self, decoder_out: Tensor, mask_feature: Tensor,\n                      attn_mask_target_size: Tuple[int, int]) -> Tuple[Tensor]:\n        \"\"\"Forward for head part which is called after every decoder layer.\n\n        Args:\n            decoder_out (Tensor): in shape (batch_size, num_queries, c).\n            mask_feature (Tensor): in shape (batch_size, c, h, w).\n            attn_mask_target_size (tuple[int, int]): target attention\n                mask size.\n\n        Returns:\n            tuple: A tuple contain three elements.\n\n                - cls_pred (Tensor): Classification scores in shape \\\n                    (batch_size, num_queries, cls_out_channels). \\\n                    Note `cls_out_channels` should includes background.\n                - mask_pred (Tensor): Mask scores in shape \\\n                    (batch_size, num_queries,h, w).\n                - attn_mask (Tensor): Attention mask in shape \\\n                    (batch_size * num_heads, num_queries, h, w).\n        \"\"\"\n        decoder_out = self.transformer_decoder.post_norm(decoder_out)\n        # shape (num_queries, batch_size, c)\n        cls_pred = self.cls_embed(decoder_out)\n        # shape (num_queries, batch_size, c)\n        mask_embed = self.mask_embed(decoder_out)\n        # shape (num_queries, batch_size, h, w)\n        mask_pred = torch.einsum('bqc,bchw->bqhw', mask_embed, mask_feature)\n        attn_mask = F.interpolate(\n            mask_pred,\n            attn_mask_target_size,\n            mode='bilinear',\n            align_corners=False)\n        # shape (num_queries, batch_size, h, w) ->\n        #   (batch_size * num_head, num_queries, h, w)\n        attn_mask = attn_mask.flatten(2).unsqueeze(1).repeat(\n            (1, self.num_heads, 1, 1)).flatten(0, 1)\n        attn_mask = attn_mask.sigmoid() < 0.5\n        attn_mask = attn_mask.detach()\n\n        return cls_pred, mask_pred, attn_mask\n\n    def forward(self, x: List[Tensor],\n                batch_data_samples: SampleList) -> Tuple[List[Tensor]]:\n        \"\"\"Forward function.\n\n        Args:\n            x (list[Tensor]): Multi scale Features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            tuple[list[Tensor]]: A tuple contains two elements.\n\n                - cls_pred_list (list[Tensor)]: Classification logits \\\n                    for each decoder layer. Each is a 3D-tensor with shape \\\n                    (batch_size, num_queries, cls_out_channels). \\\n                    Note `cls_out_channels` should includes background.\n                - mask_pred_list (list[Tensor]): Mask logits for each \\\n                    decoder layer. Each with shape (batch_size, num_queries, \\\n                    h, w).\n        \"\"\"\n        batch_size = x[0].shape[0]\n        mask_features, multi_scale_memorys = self.pixel_decoder(x)\n        # multi_scale_memorys (from low resolution to high resolution)\n        decoder_inputs = []\n        decoder_positional_encodings = []\n        for i in range(self.num_transformer_feat_level):\n            decoder_input = self.decoder_input_projs[i](multi_scale_memorys[i])\n            # shape (batch_size, c, h, w) -> (batch_size, h*w, c)\n            decoder_input = decoder_input.flatten(2).permute(0, 2, 1)\n            level_embed = self.level_embed.weight[i].view(1, 1, -1)\n            decoder_input = decoder_input + level_embed\n            # shape (batch_size, c, h, w) -> (batch_size, h*w, c)\n            mask = decoder_input.new_zeros(\n                (batch_size, ) + multi_scale_memorys[i].shape[-2:],\n                dtype=torch.bool)\n            decoder_positional_encoding = self.decoder_positional_encoding(\n                mask)\n            decoder_positional_encoding = decoder_positional_encoding.flatten(\n                2).permute(0, 2, 1)\n            decoder_inputs.append(decoder_input)\n            decoder_positional_encodings.append(decoder_positional_encoding)\n        # shape (num_queries, c) -> (batch_size, num_queries, c)\n        query_feat = self.query_feat.weight.unsqueeze(0).repeat(\n            (batch_size, 1, 1))\n        query_embed = self.query_embed.weight.unsqueeze(0).repeat(\n            (batch_size, 1, 1))\n\n        cls_pred_list = []\n        mask_pred_list = []\n        cls_pred, mask_pred, attn_mask = self._forward_head(\n            query_feat, mask_features, multi_scale_memorys[0].shape[-2:])\n        cls_pred_list.append(cls_pred)\n        mask_pred_list.append(mask_pred)\n\n        for i in range(self.num_transformer_decoder_layers):\n            level_idx = i % self.num_transformer_feat_level\n            # if a mask is all True(all background), then set it all False.\n            mask_sum = (attn_mask.sum(-1) != attn_mask.shape[-1]).unsqueeze(-1)\n            attn_mask = attn_mask & mask_sum\n            # cross_attn + self_attn\n            layer = self.transformer_decoder.layers[i]\n            query_feat = layer(\n                query=query_feat,\n                key=decoder_inputs[level_idx],\n                value=decoder_inputs[level_idx],\n                query_pos=query_embed,\n                key_pos=decoder_positional_encodings[level_idx],\n                cross_attn_mask=attn_mask,\n                query_key_padding_mask=None,\n                # here we do not apply masking on padded region\n                key_padding_mask=None)\n            cls_pred, mask_pred, attn_mask = self._forward_head(\n                query_feat, mask_features, multi_scale_memorys[\n                    (i + 1) % self.num_transformer_feat_level].shape[-2:])\n\n            cls_pred_list.append(cls_pred)\n            mask_pred_list.append(mask_pred)\n\n        return cls_pred_list, mask_pred_list\n"
  },
  {
    "path": "mmdet/models/dense_heads/maskformer_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Conv2d\nfrom mmengine.model import caffe2_xavier_init\nfrom mmengine.structures import InstanceData, PixelData\nfrom torch import Tensor\n\nfrom mmdet.models.layers.pixel_decoder import PixelDecoder\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptMultiConfig, reduce_mean)\nfrom ..layers import DetrTransformerDecoder, SinePositionalEncoding\nfrom ..utils import multi_apply, preprocess_panoptic_gt\nfrom .anchor_free_head import AnchorFreeHead\n\n\n@MODELS.register_module()\nclass MaskFormerHead(AnchorFreeHead):\n    \"\"\"Implements the MaskFormer head.\n\n    See `Per-Pixel Classification is Not All You Need for Semantic\n    Segmentation <https://arxiv.org/pdf/2107.06278>`_ for details.\n\n    Args:\n        in_channels (list[int]): Number of channels in the input feature map.\n        feat_channels (int): Number of channels for feature.\n        out_channels (int): Number of channels for output.\n        num_things_classes (int): Number of things.\n        num_stuff_classes (int): Number of stuff.\n        num_queries (int): Number of query in Transformer.\n        pixel_decoder (:obj:`ConfigDict` or dict): Config for pixel\n            decoder.\n        enforce_decoder_input_project (bool): Whether to add a layer\n            to change the embed_dim of transformer encoder in pixel decoder to\n            the embed_dim of transformer decoder. Defaults to False.\n        transformer_decoder (:obj:`ConfigDict` or dict): Config for\n            transformer decoder.\n        positional_encoding (:obj:`ConfigDict` or dict): Config for\n            transformer decoder position encoding.\n        loss_cls (:obj:`ConfigDict` or dict): Config of the classification\n            loss. Defaults to `CrossEntropyLoss`.\n        loss_mask (:obj:`ConfigDict` or dict): Config of the mask loss.\n            Defaults to `FocalLoss`.\n        loss_dice (:obj:`ConfigDict` or dict): Config of the dice loss.\n            Defaults to `DiceLoss`.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config of\n            MaskFormer head.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            MaskFormer head.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: List[int],\n                 feat_channels: int,\n                 out_channels: int,\n                 num_things_classes: int = 80,\n                 num_stuff_classes: int = 53,\n                 num_queries: int = 100,\n                 pixel_decoder: ConfigType = ...,\n                 enforce_decoder_input_project: bool = False,\n                 transformer_decoder: ConfigType = ...,\n                 positional_encoding: ConfigType = dict(\n                     num_feats=128, normalize=True),\n                 loss_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=False,\n                     loss_weight=1.0,\n                     class_weight=[1.0] * 133 + [0.1]),\n                 loss_mask: ConfigType = dict(\n                     type='FocalLoss',\n                     use_sigmoid=True,\n                     gamma=2.0,\n                     alpha=0.25,\n                     loss_weight=20.0),\n                 loss_dice: ConfigType = dict(\n                     type='DiceLoss',\n                     use_sigmoid=True,\n                     activate=True,\n                     naive_dice=True,\n                     loss_weight=1.0),\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        super(AnchorFreeHead, self).__init__(init_cfg=init_cfg)\n        self.num_things_classes = num_things_classes\n        self.num_stuff_classes = num_stuff_classes\n        self.num_classes = self.num_things_classes + self.num_stuff_classes\n        self.num_queries = num_queries\n\n        pixel_decoder.update(\n            in_channels=in_channels,\n            feat_channels=feat_channels,\n            out_channels=out_channels)\n        self.pixel_decoder = MODELS.build(pixel_decoder)\n        self.transformer_decoder = DetrTransformerDecoder(\n            **transformer_decoder)\n        self.decoder_embed_dims = self.transformer_decoder.embed_dims\n        if type(self.pixel_decoder) == PixelDecoder and (\n                self.decoder_embed_dims != in_channels[-1]\n                or enforce_decoder_input_project):\n            self.decoder_input_proj = Conv2d(\n                in_channels[-1], self.decoder_embed_dims, kernel_size=1)\n        else:\n            self.decoder_input_proj = nn.Identity()\n        self.decoder_pe = SinePositionalEncoding(**positional_encoding)\n        self.query_embed = nn.Embedding(self.num_queries, out_channels)\n\n        self.cls_embed = nn.Linear(feat_channels, self.num_classes + 1)\n        self.mask_embed = nn.Sequential(\n            nn.Linear(feat_channels, feat_channels), nn.ReLU(inplace=True),\n            nn.Linear(feat_channels, feat_channels), nn.ReLU(inplace=True),\n            nn.Linear(feat_channels, out_channels))\n\n        self.test_cfg = test_cfg\n        self.train_cfg = train_cfg\n        if train_cfg:\n            self.assigner = TASK_UTILS.build(train_cfg['assigner'])\n            self.sampler = TASK_UTILS.build(\n                train_cfg['sampler'], default_args=dict(context=self))\n\n        self.class_weight = loss_cls.class_weight\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_mask = MODELS.build(loss_mask)\n        self.loss_dice = MODELS.build(loss_dice)\n\n    def init_weights(self) -> None:\n        if isinstance(self.decoder_input_proj, Conv2d):\n            caffe2_xavier_init(self.decoder_input_proj, bias=0)\n\n        self.pixel_decoder.init_weights()\n\n        for p in self.transformer_decoder.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def preprocess_gt(\n            self, batch_gt_instances: InstanceList,\n            batch_gt_semantic_segs: List[Optional[PixelData]]) -> InstanceList:\n        \"\"\"Preprocess the ground truth for all images.\n\n        Args:\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``labels``, each is\n                ground truth labels of each bbox, with shape (num_gts, )\n                and ``masks``, each is ground truth masks of each instances\n                of a image, shape (num_gts, h, w).\n            gt_semantic_seg (list[Optional[PixelData]]): Ground truth of\n                semantic segmentation, each with the shape (1, h, w).\n                [0, num_thing_class - 1] means things,\n                [num_thing_class, num_class-1] means stuff,\n                255 means VOID. It's None when training instance segmentation.\n\n        Returns:\n            list[obj:`InstanceData`]: each contains the following keys\n\n                - labels (Tensor): Ground truth class indices\\\n                    for a image, with shape (n, ), n is the sum of\\\n                    number of stuff type and number of instance in a image.\n                - masks (Tensor): Ground truth mask for a\\\n                    image, with shape (n, h, w).\n        \"\"\"\n        num_things_list = [self.num_things_classes] * len(batch_gt_instances)\n        num_stuff_list = [self.num_stuff_classes] * len(batch_gt_instances)\n        gt_labels_list = [\n            gt_instances['labels'] for gt_instances in batch_gt_instances\n        ]\n        gt_masks_list = [\n            gt_instances['masks'] for gt_instances in batch_gt_instances\n        ]\n        gt_semantic_segs = [\n            None if gt_semantic_seg is None else gt_semantic_seg.sem_seg\n            for gt_semantic_seg in batch_gt_semantic_segs\n        ]\n        targets = multi_apply(preprocess_panoptic_gt, gt_labels_list,\n                              gt_masks_list, gt_semantic_segs, num_things_list,\n                              num_stuff_list)\n        labels, masks = targets\n        batch_gt_instances = [\n            InstanceData(labels=label, masks=mask)\n            for label, mask in zip(labels, masks)\n        ]\n        return batch_gt_instances\n\n    def get_targets(\n        self,\n        cls_scores_list: List[Tensor],\n        mask_preds_list: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        return_sampling_results: bool = False\n    ) -> Tuple[List[Union[Tensor, int]]]:\n        \"\"\"Compute classification and mask targets for all images for a decoder\n        layer.\n\n        Args:\n            cls_scores_list (list[Tensor]): Mask score logits from a single\n                decoder layer for all images. Each with shape (num_queries,\n                cls_out_channels).\n            mask_preds_list (list[Tensor]): Mask logits from a single decoder\n                layer for all images. Each with shape (num_queries, h, w).\n            batch_gt_instances (list[obj:`InstanceData`]): each contains\n                ``labels`` and ``masks``.\n            batch_img_metas (list[dict]): List of image meta information.\n            return_sampling_results (bool): Whether to return the sampling\n                results. Defaults to False.\n\n        Returns:\n            tuple: a tuple containing the following targets.\n\n                - labels_list (list[Tensor]): Labels of all images.\\\n                    Each with shape (num_queries, ).\n                - label_weights_list (list[Tensor]): Label weights\\\n                    of all images. Each with shape (num_queries, ).\n                - mask_targets_list (list[Tensor]): Mask targets of\\\n                    all images. Each with shape (num_queries, h, w).\n                - mask_weights_list (list[Tensor]): Mask weights of\\\n                    all images. Each with shape (num_queries, ).\n                - avg_factor (int): Average factor that is used to average\\\n                    the loss. When using sampling method, avg_factor is\n                    usually the sum of positive and negative priors. When\n                    using `MaskPseudoSampler`, `avg_factor` is usually equal\n                    to the number of positive priors.\n\n            additional_returns: This function enables user-defined returns from\n                `self._get_targets_single`. These returns are currently refined\n                to properties at each feature map (i.e. having HxW dimension).\n                The results will be concatenated after the end.\n        \"\"\"\n        results = multi_apply(self._get_targets_single, cls_scores_list,\n                              mask_preds_list, batch_gt_instances,\n                              batch_img_metas)\n        (labels_list, label_weights_list, mask_targets_list, mask_weights_list,\n         pos_inds_list, neg_inds_list, sampling_results_list) = results[:7]\n        rest_results = list(results[7:])\n\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n\n        res = (labels_list, label_weights_list, mask_targets_list,\n               mask_weights_list, avg_factor)\n        if return_sampling_results:\n            res = res + (sampling_results_list)\n\n        return res + tuple(rest_results)\n\n    def _get_targets_single(self, cls_score: Tensor, mask_pred: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict) -> Tuple[Tensor]:\n        \"\"\"Compute classification and mask targets for one image.\n\n        Args:\n            cls_score (Tensor): Mask score logits from a single decoder layer\n                for one image. Shape (num_queries, cls_out_channels).\n            mask_pred (Tensor): Mask logits for a single decoder layer for one\n                image. Shape (num_queries, h, w).\n            gt_instances (:obj:`InstanceData`): It contains ``labels`` and\n                ``masks``.\n            img_meta (dict): Image informtation.\n\n        Returns:\n            tuple: a tuple containing the following for one image.\n\n                - labels (Tensor): Labels of each image.\n                    shape (num_queries, ).\n                - label_weights (Tensor): Label weights of each image.\n                    shape (num_queries, ).\n                - mask_targets (Tensor): Mask targets of each image.\n                    shape (num_queries, h, w).\n                - mask_weights (Tensor): Mask weights of each image.\n                    shape (num_queries, ).\n                - pos_inds (Tensor): Sampled positive indices for each image.\n                - neg_inds (Tensor): Sampled negative indices for each image.\n                - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        gt_masks = gt_instances.masks\n        gt_labels = gt_instances.labels\n\n        target_shape = mask_pred.shape[-2:]\n        if gt_masks.shape[0] > 0:\n            gt_masks_downsampled = F.interpolate(\n                gt_masks.unsqueeze(1).float(), target_shape,\n                mode='nearest').squeeze(1).long()\n        else:\n            gt_masks_downsampled = gt_masks\n\n        pred_instances = InstanceData(scores=cls_score, masks=mask_pred)\n        downsampled_gt_instances = InstanceData(\n            labels=gt_labels, masks=gt_masks_downsampled)\n        # assign and sample\n        assign_result = self.assigner.assign(\n            pred_instances=pred_instances,\n            gt_instances=downsampled_gt_instances,\n            img_meta=img_meta)\n        sampling_result = self.sampler.sample(\n            assign_result=assign_result,\n            pred_instances=pred_instances,\n            gt_instances=gt_instances)\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n\n        # label target\n        labels = gt_labels.new_full((self.num_queries, ),\n                                    self.num_classes,\n                                    dtype=torch.long)\n        labels[pos_inds] = gt_labels[sampling_result.pos_assigned_gt_inds]\n        label_weights = gt_labels.new_ones(self.num_queries)\n\n        # mask target\n        mask_targets = gt_masks[sampling_result.pos_assigned_gt_inds]\n        mask_weights = mask_pred.new_zeros((self.num_queries, ))\n        mask_weights[pos_inds] = 1.0\n\n        return (labels, label_weights, mask_targets, mask_weights, pos_inds,\n                neg_inds, sampling_result)\n\n    def loss_by_feat(self, all_cls_scores: Tensor, all_mask_preds: Tensor,\n                     batch_gt_instances: List[InstanceData],\n                     batch_img_metas: List[dict]) -> Dict[str, Tensor]:\n        \"\"\"Loss function.\n\n        Args:\n            all_cls_scores (Tensor): Classification scores for all decoder\n                layers with shape (num_decoder, batch_size, num_queries,\n                cls_out_channels). Note `cls_out_channels` should includes\n                background.\n            all_mask_preds (Tensor): Mask scores for all decoder layers with\n                shape (num_decoder, batch_size, num_queries, h, w).\n            batch_gt_instances (list[obj:`InstanceData`]): each contains\n                ``labels`` and ``masks``.\n            batch_img_metas (list[dict]): List of image meta information.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        num_dec_layers = len(all_cls_scores)\n        batch_gt_instances_list = [\n            batch_gt_instances for _ in range(num_dec_layers)\n        ]\n        img_metas_list = [batch_img_metas for _ in range(num_dec_layers)]\n        losses_cls, losses_mask, losses_dice = multi_apply(\n            self._loss_by_feat_single, all_cls_scores, all_mask_preds,\n            batch_gt_instances_list, img_metas_list)\n\n        loss_dict = dict()\n        # loss from the last decoder layer\n        loss_dict['loss_cls'] = losses_cls[-1]\n        loss_dict['loss_mask'] = losses_mask[-1]\n        loss_dict['loss_dice'] = losses_dice[-1]\n        # loss from other decoder layers\n        num_dec_layer = 0\n        for loss_cls_i, loss_mask_i, loss_dice_i in zip(\n                losses_cls[:-1], losses_mask[:-1], losses_dice[:-1]):\n            loss_dict[f'd{num_dec_layer}.loss_cls'] = loss_cls_i\n            loss_dict[f'd{num_dec_layer}.loss_mask'] = loss_mask_i\n            loss_dict[f'd{num_dec_layer}.loss_dice'] = loss_dice_i\n            num_dec_layer += 1\n        return loss_dict\n\n    def _loss_by_feat_single(self, cls_scores: Tensor, mask_preds: Tensor,\n                             batch_gt_instances: List[InstanceData],\n                             batch_img_metas: List[dict]) -> Tuple[Tensor]:\n        \"\"\"Loss function for outputs from a single decoder layer.\n\n        Args:\n            cls_scores (Tensor): Mask score logits from a single decoder layer\n                for all images. Shape (batch_size, num_queries,\n                cls_out_channels). Note `cls_out_channels` should includes\n                background.\n            mask_preds (Tensor): Mask logits for a pixel decoder for all\n                images. Shape (batch_size, num_queries, h, w).\n            batch_gt_instances (list[obj:`InstanceData`]): each contains\n                ``labels`` and ``masks``.\n            batch_img_metas (list[dict]): List of image meta information.\n\n        Returns:\n            tuple[Tensor]: Loss components for outputs from a single decoder\\\n                layer.\n        \"\"\"\n        num_imgs = cls_scores.size(0)\n        cls_scores_list = [cls_scores[i] for i in range(num_imgs)]\n        mask_preds_list = [mask_preds[i] for i in range(num_imgs)]\n\n        (labels_list, label_weights_list, mask_targets_list, mask_weights_list,\n         avg_factor) = self.get_targets(cls_scores_list, mask_preds_list,\n                                        batch_gt_instances, batch_img_metas)\n        # shape (batch_size, num_queries)\n        labels = torch.stack(labels_list, dim=0)\n        # shape (batch_size, num_queries)\n        label_weights = torch.stack(label_weights_list, dim=0)\n        # shape (num_total_gts, h, w)\n        mask_targets = torch.cat(mask_targets_list, dim=0)\n        # shape (batch_size, num_queries)\n        mask_weights = torch.stack(mask_weights_list, dim=0)\n\n        # classfication loss\n        # shape (batch_size * num_queries, )\n        cls_scores = cls_scores.flatten(0, 1)\n        labels = labels.flatten(0, 1)\n        label_weights = label_weights.flatten(0, 1)\n\n        class_weight = cls_scores.new_tensor(self.class_weight)\n        loss_cls = self.loss_cls(\n            cls_scores,\n            labels,\n            label_weights,\n            avg_factor=class_weight[labels].sum())\n\n        num_total_masks = reduce_mean(cls_scores.new_tensor([avg_factor]))\n        num_total_masks = max(num_total_masks, 1)\n\n        # extract positive ones\n        # shape (batch_size, num_queries, h, w) -> (num_total_gts, h, w)\n        mask_preds = mask_preds[mask_weights > 0]\n        target_shape = mask_targets.shape[-2:]\n\n        if mask_targets.shape[0] == 0:\n            # zero match\n            loss_dice = mask_preds.sum()\n            loss_mask = mask_preds.sum()\n            return loss_cls, loss_mask, loss_dice\n\n        # upsample to shape of target\n        # shape (num_total_gts, h, w)\n        mask_preds = F.interpolate(\n            mask_preds.unsqueeze(1),\n            target_shape,\n            mode='bilinear',\n            align_corners=False).squeeze(1)\n\n        # dice loss\n        loss_dice = self.loss_dice(\n            mask_preds, mask_targets, avg_factor=num_total_masks)\n\n        # mask loss\n        # FocalLoss support input of shape (n, num_class)\n        h, w = mask_preds.shape[-2:]\n        # shape (num_total_gts, h, w) -> (num_total_gts * h * w, 1)\n        mask_preds = mask_preds.reshape(-1, 1)\n        # shape (num_total_gts, h, w) -> (num_total_gts * h * w)\n        mask_targets = mask_targets.reshape(-1)\n        # target is (1 - mask_targets) !!!\n        loss_mask = self.loss_mask(\n            mask_preds, 1 - mask_targets, avg_factor=num_total_masks * h * w)\n\n        return loss_cls, loss_mask, loss_dice\n\n    def forward(self, x: Tuple[Tensor],\n                batch_data_samples: SampleList) -> Tuple[Tensor]:\n        \"\"\"Forward function.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each\n                is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            tuple[Tensor]: a tuple contains two elements.\n\n                - all_cls_scores (Tensor): Classification scores for each\\\n                    scale level. Each is a 4D-tensor with shape\\\n                    (num_decoder, batch_size, num_queries, cls_out_channels).\\\n                    Note `cls_out_channels` should includes background.\n                - all_mask_preds (Tensor): Mask scores for each decoder\\\n                    layer. Each with shape (num_decoder, batch_size,\\\n                    num_queries, h, w).\n        \"\"\"\n        batch_img_metas = [\n            data_sample.metainfo for data_sample in batch_data_samples\n        ]\n        batch_size = x[0].shape[0]\n        input_img_h, input_img_w = batch_img_metas[0]['batch_input_shape']\n        padding_mask = x[-1].new_ones((batch_size, input_img_h, input_img_w),\n                                      dtype=torch.float32)\n        for i in range(batch_size):\n            img_h, img_w = batch_img_metas[i]['img_shape']\n            padding_mask[i, :img_h, :img_w] = 0\n        padding_mask = F.interpolate(\n            padding_mask.unsqueeze(1), size=x[-1].shape[-2:],\n            mode='nearest').to(torch.bool).squeeze(1)\n        # when backbone is swin, memory is output of last stage of swin.\n        # when backbone is r50, memory is output of tranformer encoder.\n        mask_features, memory = self.pixel_decoder(x, batch_img_metas)\n        pos_embed = self.decoder_pe(padding_mask)\n        memory = self.decoder_input_proj(memory)\n        # shape (batch_size, c, h, w) -> (batch_size, h*w, c)\n        memory = memory.flatten(2).permute(0, 2, 1)\n        pos_embed = pos_embed.flatten(2).permute(0, 2, 1)\n        # shape (batch_size, h * w)\n        padding_mask = padding_mask.flatten(1)\n        # shape = (num_queries, embed_dims)\n        query_embed = self.query_embed.weight\n        # shape = (batch_size, num_queries, embed_dims)\n        query_embed = query_embed.unsqueeze(0).repeat(batch_size, 1, 1)\n        target = torch.zeros_like(query_embed)\n        # shape (num_decoder, num_queries, batch_size, embed_dims)\n        out_dec = self.transformer_decoder(\n            query=target,\n            key=memory,\n            value=memory,\n            query_pos=query_embed,\n            key_pos=pos_embed,\n            key_padding_mask=padding_mask)\n\n        # cls_scores\n        all_cls_scores = self.cls_embed(out_dec)\n\n        # mask_preds\n        mask_embed = self.mask_embed(out_dec)\n        all_mask_preds = torch.einsum('lbqc,bchw->lbqhw', mask_embed,\n                                      mask_features)\n\n        return all_cls_scores, all_mask_preds\n\n    def loss(\n        self,\n        x: Tuple[Tensor],\n        batch_data_samples: SampleList,\n    ) -> Dict[str, Tensor]:\n        \"\"\"Perform forward propagation and loss calculation of the panoptic\n        head on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the upstream\n                network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components\n        \"\"\"\n        batch_img_metas = []\n        batch_gt_instances = []\n        batch_gt_semantic_segs = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n            if 'gt_sem_seg' in data_sample:\n                batch_gt_semantic_segs.append(data_sample.gt_sem_seg)\n            else:\n                batch_gt_semantic_segs.append(None)\n\n        # forward\n        all_cls_scores, all_mask_preds = self(x, batch_data_samples)\n\n        # preprocess ground truth\n        batch_gt_instances = self.preprocess_gt(batch_gt_instances,\n                                                batch_gt_semantic_segs)\n\n        # loss\n        losses = self.loss_by_feat(all_cls_scores, all_mask_preds,\n                                   batch_gt_instances, batch_img_metas)\n\n        return losses\n\n    def predict(self, x: Tuple[Tensor],\n                batch_data_samples: SampleList) -> Tuple[Tensor]:\n        \"\"\"Test without augmentaton.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            tuple[Tensor]: A tuple contains two tensors.\n\n                - mask_cls_results (Tensor): Mask classification logits,\\\n                    shape (batch_size, num_queries, cls_out_channels).\n                    Note `cls_out_channels` should includes background.\n                - mask_pred_results (Tensor): Mask logits, shape \\\n                    (batch_size, num_queries, h, w).\n        \"\"\"\n        batch_img_metas = [\n            data_sample.metainfo for data_sample in batch_data_samples\n        ]\n        all_cls_scores, all_mask_preds = self(x, batch_data_samples)\n        mask_cls_results = all_cls_scores[-1]\n        mask_pred_results = all_mask_preds[-1]\n\n        # upsample masks\n        img_shape = batch_img_metas[0]['batch_input_shape']\n        mask_pred_results = F.interpolate(\n            mask_pred_results,\n            size=(img_shape[0], img_shape[1]),\n            mode='bilinear',\n            align_corners=False)\n\n        return mask_cls_results, mask_pred_results\n"
  },
  {
    "path": "mmdet/models/dense_heads/nasfcos_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, Scale\n\nfrom mmdet.models.dense_heads.fcos_head import FCOSHead\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptMultiConfig\n\n\n@MODELS.register_module()\nclass NASFCOSHead(FCOSHead):\n    \"\"\"Anchor-free head used in `NASFCOS <https://arxiv.org/abs/1906.04423>`_.\n\n    It is quite similar with FCOS head, except for the searched structure of\n    classification branch and bbox regression branch, where a structure of\n    \"dconv3x3, conv3x3, dconv3x3, conv1x1\" is utilized instead.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        strides (Sequence[int] or Sequence[Tuple[int, int]]): Strides of points\n            in multiple feature levels. Defaults to (4, 8, 16, 32, 64).\n        regress_ranges (Sequence[Tuple[int, int]]): Regress range of multiple\n            level points.\n        center_sampling (bool): If true, use center sampling.\n            Defaults to False.\n        center_sample_radius (float): Radius of center sampling.\n            Defaults to 1.5.\n        norm_on_bbox (bool): If true, normalize the regression targets with\n            FPN strides. Defaults to False.\n        centerness_on_reg (bool): If true, position centerness on the\n            regress branch. Please refer to https://github.com/tianzhi0549/FCOS/issues/89#issuecomment-516877042.\n            Defaults to False.\n        conv_bias (bool or str): If specified as `auto`, it will be decided by\n            the norm_cfg. Bias of conv will be set as True if `norm_cfg` is\n            None, otherwise False. Defaults to \"auto\".\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of localization loss.\n        loss_centerness (:obj:`ConfigDict`, or dict): Config of centerness\n            loss.\n        norm_cfg (:obj:`ConfigDict` or dict): dictionary to construct and\n            config norm layer.  Defaults to\n            ``norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)``.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], opitonal): Initialization config dict.\n    \"\"\"  # noqa: E501\n\n    def __init__(self,\n                 *args,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        if init_cfg is None:\n            init_cfg = [\n                dict(type='Caffe2Xavier', layer=['ConvModule', 'Conv2d']),\n                dict(\n                    type='Normal',\n                    std=0.01,\n                    override=[\n                        dict(name='conv_reg'),\n                        dict(name='conv_centerness'),\n                        dict(\n                            name='conv_cls',\n                            type='Normal',\n                            std=0.01,\n                            bias_prob=0.01)\n                    ]),\n            ]\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        dconv3x3_config = dict(\n            type='DCNv2',\n            kernel_size=3,\n            use_bias=True,\n            deform_groups=2,\n            padding=1)\n        conv3x3_config = dict(type='Conv', kernel_size=3, padding=1)\n        conv1x1_config = dict(type='Conv', kernel_size=1)\n\n        self.arch_config = [\n            dconv3x3_config, conv3x3_config, dconv3x3_config, conv1x1_config\n        ]\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i, op_ in enumerate(self.arch_config):\n            op = copy.deepcopy(op_)\n            chn = self.in_channels if i == 0 else self.feat_channels\n            assert isinstance(op, dict)\n            use_bias = op.pop('use_bias', False)\n            padding = op.pop('padding', 0)\n            kernel_size = op.pop('kernel_size')\n            module = ConvModule(\n                chn,\n                self.feat_channels,\n                kernel_size,\n                stride=1,\n                padding=padding,\n                norm_cfg=self.norm_cfg,\n                bias=use_bias,\n                conv_cfg=op)\n\n            self.cls_convs.append(copy.deepcopy(module))\n            self.reg_convs.append(copy.deepcopy(module))\n\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n        self.conv_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1)\n        self.conv_centerness = nn.Conv2d(self.feat_channels, 1, 3, padding=1)\n\n        self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides])\n"
  },
  {
    "path": "mmdet/models/dense_heads/paa_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList)\nfrom ..layers import multiclass_nms\nfrom ..utils import levels_to_images, multi_apply\nfrom . import ATSSHead\n\nEPS = 1e-12\ntry:\n    import sklearn.mixture as skm\nexcept ImportError:\n    skm = None\n\n\n@MODELS.register_module()\nclass PAAHead(ATSSHead):\n    \"\"\"Head of PAAAssignment: Probabilistic Anchor Assignment with IoU\n    Prediction for Object Detection.\n\n    Code is modified from the `official github repo\n    <https://github.com/kkhoot/PAA/blob/master/paa_core\n    /modeling/rpn/paa/loss.py>`_.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/2007.08103>`_ .\n\n    Args:\n        topk (int): Select topk samples with smallest loss in\n            each level.\n        score_voting (bool): Whether to use score voting in post-process.\n        covariance_type : String describing the type of covariance parameters\n            to be used in :class:`sklearn.mixture.GaussianMixture`.\n            It must be one of:\n\n            - 'full': each component has its own general covariance matrix\n            - 'tied': all components share the same general covariance matrix\n            - 'diag': each component has its own diagonal covariance matrix\n            - 'spherical': each component has its own single variance\n            Default: 'diag'. From 'full' to 'spherical', the gmm fitting\n            process is faster yet the performance could be influenced. For most\n            cases, 'diag' should be a good choice.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 topk: int = 9,\n                 score_voting: bool = True,\n                 covariance_type: str = 'diag',\n                 **kwargs):\n        # topk used in paa reassign process\n        self.topk = topk\n        self.with_score_voting = score_voting\n        self.covariance_type = covariance_type\n        super().__init__(*args, **kwargs)\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            iou_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            iou_preds (list[Tensor]): iou_preds for each scale\n                level with shape (N, num_anchors * 1, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss gmm_assignment.\n        \"\"\"\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n        )\n        (labels, labels_weight, bboxes_target, bboxes_weight, pos_inds,\n         pos_gt_index) = cls_reg_targets\n        cls_scores = levels_to_images(cls_scores)\n        cls_scores = [\n            item.reshape(-1, self.cls_out_channels) for item in cls_scores\n        ]\n        bbox_preds = levels_to_images(bbox_preds)\n        bbox_preds = [item.reshape(-1, 4) for item in bbox_preds]\n        iou_preds = levels_to_images(iou_preds)\n        iou_preds = [item.reshape(-1, 1) for item in iou_preds]\n        pos_losses_list, = multi_apply(self.get_pos_loss, anchor_list,\n                                       cls_scores, bbox_preds, labels,\n                                       labels_weight, bboxes_target,\n                                       bboxes_weight, pos_inds)\n\n        with torch.no_grad():\n            reassign_labels, reassign_label_weight, \\\n                reassign_bbox_weights, num_pos = multi_apply(\n                    self.paa_reassign,\n                    pos_losses_list,\n                    labels,\n                    labels_weight,\n                    bboxes_weight,\n                    pos_inds,\n                    pos_gt_index,\n                    anchor_list)\n            num_pos = sum(num_pos)\n        # convert all tensor list to a flatten tensor\n        cls_scores = torch.cat(cls_scores, 0).view(-1, cls_scores[0].size(-1))\n        bbox_preds = torch.cat(bbox_preds, 0).view(-1, bbox_preds[0].size(-1))\n        iou_preds = torch.cat(iou_preds, 0).view(-1, iou_preds[0].size(-1))\n        labels = torch.cat(reassign_labels, 0).view(-1)\n        flatten_anchors = torch.cat(\n            [torch.cat(item, 0) for item in anchor_list])\n        labels_weight = torch.cat(reassign_label_weight, 0).view(-1)\n        bboxes_target = torch.cat(bboxes_target,\n                                  0).view(-1, bboxes_target[0].size(-1))\n\n        pos_inds_flatten = ((labels >= 0)\n                            &\n                            (labels < self.num_classes)).nonzero().reshape(-1)\n\n        losses_cls = self.loss_cls(\n            cls_scores,\n            labels,\n            labels_weight,\n            avg_factor=max(num_pos, len(batch_img_metas)))  # avoid num_pos=0\n        if num_pos:\n            pos_bbox_pred = self.bbox_coder.decode(\n                flatten_anchors[pos_inds_flatten],\n                bbox_preds[pos_inds_flatten])\n            pos_bbox_target = bboxes_target[pos_inds_flatten]\n            iou_target = bbox_overlaps(\n                pos_bbox_pred.detach(), pos_bbox_target, is_aligned=True)\n            losses_iou = self.loss_centerness(\n                iou_preds[pos_inds_flatten],\n                iou_target.unsqueeze(-1),\n                avg_factor=num_pos)\n            losses_bbox = self.loss_bbox(\n                pos_bbox_pred,\n                pos_bbox_target,\n                iou_target.clamp(min=EPS),\n                avg_factor=iou_target.sum())\n        else:\n            losses_iou = iou_preds.sum() * 0\n            losses_bbox = bbox_preds.sum() * 0\n\n        return dict(\n            loss_cls=losses_cls, loss_bbox=losses_bbox, loss_iou=losses_iou)\n\n    def get_pos_loss(self, anchors: List[Tensor], cls_score: Tensor,\n                     bbox_pred: Tensor, label: Tensor, label_weight: Tensor,\n                     bbox_target: dict, bbox_weight: Tensor,\n                     pos_inds: Tensor) -> Tensor:\n        \"\"\"Calculate loss of all potential positive samples obtained from first\n        match process.\n\n        Args:\n            anchors (list[Tensor]): Anchors of each scale.\n            cls_score (Tensor): Box scores of single image with shape\n                (num_anchors, num_classes)\n            bbox_pred (Tensor): Box energies / deltas of single image\n                with shape (num_anchors, 4)\n            label (Tensor): classification target of each anchor with\n                shape (num_anchors,)\n            label_weight (Tensor): Classification loss weight of each\n                anchor with shape (num_anchors).\n            bbox_target (dict): Regression target of each anchor with\n                shape (num_anchors, 4).\n            bbox_weight (Tensor): Bbox weight of each anchor with shape\n                (num_anchors, 4).\n            pos_inds (Tensor): Index of all positive samples got from\n                first assign process.\n\n        Returns:\n            Tensor: Losses of all positive samples in single image.\n        \"\"\"\n        if not len(pos_inds):\n            return cls_score.new([]),\n        anchors_all_level = torch.cat(anchors, 0)\n        pos_scores = cls_score[pos_inds]\n        pos_bbox_pred = bbox_pred[pos_inds]\n        pos_label = label[pos_inds]\n        pos_label_weight = label_weight[pos_inds]\n        pos_bbox_target = bbox_target[pos_inds]\n        pos_bbox_weight = bbox_weight[pos_inds]\n        pos_anchors = anchors_all_level[pos_inds]\n        pos_bbox_pred = self.bbox_coder.decode(pos_anchors, pos_bbox_pred)\n\n        # to keep loss dimension\n        loss_cls = self.loss_cls(\n            pos_scores,\n            pos_label,\n            pos_label_weight,\n            avg_factor=1.0,\n            reduction_override='none')\n\n        loss_bbox = self.loss_bbox(\n            pos_bbox_pred,\n            pos_bbox_target,\n            pos_bbox_weight,\n            avg_factor=1.0,  # keep same loss weight before reassign\n            reduction_override='none')\n\n        loss_cls = loss_cls.sum(-1)\n        pos_loss = loss_bbox + loss_cls\n        return pos_loss,\n\n    def paa_reassign(self, pos_losses: Tensor, label: Tensor,\n                     label_weight: Tensor, bbox_weight: Tensor,\n                     pos_inds: Tensor, pos_gt_inds: Tensor,\n                     anchors: List[Tensor]) -> tuple:\n        \"\"\"Fit loss to GMM distribution and separate positive, ignore, negative\n        samples again with GMM model.\n\n        Args:\n            pos_losses (Tensor): Losses of all positive samples in\n                single image.\n            label (Tensor): classification target of each anchor with\n                shape (num_anchors,)\n            label_weight (Tensor): Classification loss weight of each\n                anchor with shape (num_anchors).\n            bbox_weight (Tensor): Bbox weight of each anchor with shape\n                (num_anchors, 4).\n            pos_inds (Tensor): Index of all positive samples got from\n                first assign process.\n            pos_gt_inds (Tensor): Gt_index of all positive samples got\n                from first assign process.\n            anchors (list[Tensor]): Anchors of each scale.\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n\n                - label (Tensor): classification target of each anchor after\n                  paa assign, with shape (num_anchors,)\n                - label_weight (Tensor): Classification loss weight of each\n                  anchor after paa assign, with shape (num_anchors).\n                - bbox_weight (Tensor): Bbox weight of each anchor with shape\n                  (num_anchors, 4).\n                - num_pos (int): The number of positive samples after paa\n                  assign.\n        \"\"\"\n        if not len(pos_inds):\n            return label, label_weight, bbox_weight, 0\n        label = label.clone()\n        label_weight = label_weight.clone()\n        bbox_weight = bbox_weight.clone()\n        num_gt = pos_gt_inds.max() + 1\n        num_level = len(anchors)\n        num_anchors_each_level = [item.size(0) for item in anchors]\n        num_anchors_each_level.insert(0, 0)\n        inds_level_interval = np.cumsum(num_anchors_each_level)\n        pos_level_mask = []\n        for i in range(num_level):\n            mask = (pos_inds >= inds_level_interval[i]) & (\n                pos_inds < inds_level_interval[i + 1])\n            pos_level_mask.append(mask)\n        pos_inds_after_paa = [label.new_tensor([])]\n        ignore_inds_after_paa = [label.new_tensor([])]\n        for gt_ind in range(num_gt):\n            pos_inds_gmm = []\n            pos_loss_gmm = []\n            gt_mask = pos_gt_inds == gt_ind\n            for level in range(num_level):\n                level_mask = pos_level_mask[level]\n                level_gt_mask = level_mask & gt_mask\n                value, topk_inds = pos_losses[level_gt_mask].topk(\n                    min(level_gt_mask.sum(), self.topk), largest=False)\n                pos_inds_gmm.append(pos_inds[level_gt_mask][topk_inds])\n                pos_loss_gmm.append(value)\n            pos_inds_gmm = torch.cat(pos_inds_gmm)\n            pos_loss_gmm = torch.cat(pos_loss_gmm)\n            # fix gmm need at least two sample\n            if len(pos_inds_gmm) < 2:\n                continue\n            device = pos_inds_gmm.device\n            pos_loss_gmm, sort_inds = pos_loss_gmm.sort()\n            pos_inds_gmm = pos_inds_gmm[sort_inds]\n            pos_loss_gmm = pos_loss_gmm.view(-1, 1).cpu().numpy()\n            min_loss, max_loss = pos_loss_gmm.min(), pos_loss_gmm.max()\n            means_init = np.array([min_loss, max_loss]).reshape(2, 1)\n            weights_init = np.array([0.5, 0.5])\n            precisions_init = np.array([1.0, 1.0]).reshape(2, 1, 1)  # full\n            if self.covariance_type == 'spherical':\n                precisions_init = precisions_init.reshape(2)\n            elif self.covariance_type == 'diag':\n                precisions_init = precisions_init.reshape(2, 1)\n            elif self.covariance_type == 'tied':\n                precisions_init = np.array([[1.0]])\n            if skm is None:\n                raise ImportError('Please run \"pip install sklearn\" '\n                                  'to install sklearn first.')\n            gmm = skm.GaussianMixture(\n                2,\n                weights_init=weights_init,\n                means_init=means_init,\n                precisions_init=precisions_init,\n                covariance_type=self.covariance_type)\n            gmm.fit(pos_loss_gmm)\n            gmm_assignment = gmm.predict(pos_loss_gmm)\n            scores = gmm.score_samples(pos_loss_gmm)\n            gmm_assignment = torch.from_numpy(gmm_assignment).to(device)\n            scores = torch.from_numpy(scores).to(device)\n\n            pos_inds_temp, ignore_inds_temp = self.gmm_separation_scheme(\n                gmm_assignment, scores, pos_inds_gmm)\n            pos_inds_after_paa.append(pos_inds_temp)\n            ignore_inds_after_paa.append(ignore_inds_temp)\n\n        pos_inds_after_paa = torch.cat(pos_inds_after_paa)\n        ignore_inds_after_paa = torch.cat(ignore_inds_after_paa)\n        reassign_mask = (pos_inds.unsqueeze(1) != pos_inds_after_paa).all(1)\n        reassign_ids = pos_inds[reassign_mask]\n        label[reassign_ids] = self.num_classes\n        label_weight[ignore_inds_after_paa] = 0\n        bbox_weight[reassign_ids] = 0\n        num_pos = len(pos_inds_after_paa)\n        return label, label_weight, bbox_weight, num_pos\n\n    def gmm_separation_scheme(self, gmm_assignment: Tensor, scores: Tensor,\n                              pos_inds_gmm: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"A general separation scheme for gmm model.\n\n        It separates a GMM distribution of candidate samples into three\n        parts, 0 1 and uncertain areas, and you can implement other\n        separation schemes by rewriting this function.\n\n        Args:\n            gmm_assignment (Tensor): The prediction of GMM which is of shape\n                (num_samples,). The 0/1 value indicates the distribution\n                that each sample comes from.\n            scores (Tensor): The probability of sample coming from the\n                fit GMM distribution. The tensor is of shape (num_samples,).\n            pos_inds_gmm (Tensor): All the indexes of samples which are used\n                to fit GMM model. The tensor is of shape (num_samples,)\n\n        Returns:\n            tuple[Tensor, Tensor]: The indices of positive and ignored samples.\n\n                - pos_inds_temp (Tensor): Indices of positive samples.\n                - ignore_inds_temp (Tensor): Indices of ignore samples.\n        \"\"\"\n        # The implementation is (c) in Fig.3 in origin paper instead of (b).\n        # You can refer to issues such as\n        # https://github.com/kkhoot/PAA/issues/8 and\n        # https://github.com/kkhoot/PAA/issues/9.\n        fgs = gmm_assignment == 0\n        pos_inds_temp = fgs.new_tensor([], dtype=torch.long)\n        ignore_inds_temp = fgs.new_tensor([], dtype=torch.long)\n        if fgs.nonzero().numel():\n            _, pos_thr_ind = scores[fgs].topk(1)\n            pos_inds_temp = pos_inds_gmm[fgs][:pos_thr_ind + 1]\n            ignore_inds_temp = pos_inds_gmm.new_tensor([])\n        return pos_inds_temp, ignore_inds_temp\n\n    def get_targets(self,\n                    anchor_list: List[List[Tensor]],\n                    valid_flag_list: List[List[Tensor]],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs: bool = True) -> tuple:\n        \"\"\"Get targets for PAA head.\n\n        This method is almost the same as `AnchorHead.get_targets()`. We direct\n        return the results from _get_targets_single instead map it to levels\n        by images_to_levels function.\n\n        Args:\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, 4).\n            valid_flag_list (list[list[Tensor]]): Multi level valid flags of\n                each image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, )\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors. Defaults to True.\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n\n                - labels (list[Tensor]): Labels of all anchors, each with\n                    shape (num_anchors,).\n                - label_weights (list[Tensor]): Label weights of all anchor.\n                    each with shape (num_anchors,).\n                - bbox_targets (list[Tensor]): BBox targets of all anchors.\n                    each with shape (num_anchors, 4).\n                - bbox_weights (list[Tensor]): BBox weights of all anchors.\n                    each with shape (num_anchors, 4).\n                - pos_inds (list[Tensor]): Contains all index of positive\n                    sample in all anchor.\n                - gt_inds (list[Tensor]): Contains all gt_index of positive\n                    sample in all anchor.\n        \"\"\"\n\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n        concat_anchor_list = []\n        concat_valid_flag_list = []\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            concat_anchor_list.append(torch.cat(anchor_list[i]))\n            concat_valid_flag_list.append(torch.cat(valid_flag_list[i]))\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        results = multi_apply(\n            self._get_targets_single,\n            concat_anchor_list,\n            concat_valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore,\n            unmap_outputs=unmap_outputs)\n\n        (labels, label_weights, bbox_targets, bbox_weights, valid_pos_inds,\n         valid_neg_inds, sampling_result) = results\n\n        # Due to valid flag of anchors, we have to calculate the real pos_inds\n        # in origin anchor set.\n        pos_inds = []\n        for i, single_labels in enumerate(labels):\n            pos_mask = (0 <= single_labels) & (\n                single_labels < self.num_classes)\n            pos_inds.append(pos_mask.nonzero().view(-1))\n\n        gt_inds = [item.pos_assigned_gt_inds for item in sampling_result]\n        return (labels, label_weights, bbox_targets, bbox_weights, pos_inds,\n                gt_inds)\n\n    def _get_targets_single(self,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors in a\n        single image.\n\n        This method is same as `AnchorHead._get_targets_single()`.\n        \"\"\"\n        assert unmap_outputs, 'We must map outputs back to the original' \\\n                              'set of anchors in PAAhead'\n        return super(ATSSHead, self)._get_targets_single(\n            flat_anchors,\n            valid_flags,\n            gt_instances,\n            img_meta,\n            gt_instances_ignore,\n            unmap_outputs=True)\n\n    def predict_by_feat(self,\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        score_factors: Optional[List[Tensor]] = None,\n                        batch_img_metas: Optional[List[dict]] = None,\n                        cfg: OptConfigType = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        This method is same as `BaseDenseHead.get_results()`.\n        \"\"\"\n        assert with_nms, 'PAA only supports \"with_nms=True\" now and it ' \\\n                         'means PAAHead does not support ' \\\n                         'test-time augmentation'\n        return super().predict_by_feat(\n            cls_scores=cls_scores,\n            bbox_preds=bbox_preds,\n            score_factors=score_factors,\n            batch_img_metas=batch_img_metas,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms)\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: OptConfigType = None,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factors from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid, has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (:obj:`ConfigDict` or dict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Default: False.\n            with_nms (bool): If True, do nms before return boxes.\n                Default: True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bboxes = []\n        mlvl_scores = []\n        mlvl_score_factors = []\n        for level_idx, (cls_score, bbox_pred, score_factor, priors) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list,\n                              score_factor_list, mlvl_priors)):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            scores = cls_score.permute(1, 2, 0).reshape(\n                -1, self.cls_out_channels).sigmoid()\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4)\n            score_factor = score_factor.permute(1, 2, 0).reshape(-1).sigmoid()\n\n            if 0 < nms_pre < scores.shape[0]:\n                max_scores, _ = (scores *\n                                 score_factor[:, None]).sqrt().max(dim=1)\n                _, topk_inds = max_scores.topk(nms_pre)\n                priors = priors[topk_inds, :]\n                bbox_pred = bbox_pred[topk_inds, :]\n                scores = scores[topk_inds, :]\n                score_factor = score_factor[topk_inds]\n\n            bboxes = self.bbox_coder.decode(\n                priors, bbox_pred, max_shape=img_shape)\n            mlvl_bboxes.append(bboxes)\n            mlvl_scores.append(scores)\n            mlvl_score_factors.append(score_factor)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bboxes)\n        results.scores = torch.cat(mlvl_scores)\n        results.score_factors = torch.cat(mlvl_score_factors)\n\n        return self._bbox_post_process(results, cfg, rescale, with_nms,\n                                       img_meta)\n\n    def _bbox_post_process(self,\n                           results: InstanceData,\n                           cfg: ConfigType,\n                           rescale: bool = False,\n                           with_nms: bool = True,\n                           img_meta: Optional[dict] = None):\n        \"\"\"bbox post-processing method.\n\n        The boxes would be rescaled to the original image scale and do\n        the nms operation. Usually with_nms is False is used for aug test.\n\n        Args:\n            results (:obj:`InstaceData`): Detection instance results,\n                each item has shape (num_bboxes, ).\n            cfg (:obj:`ConfigDict` or dict): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Default: False.\n            with_nms (bool): If True, do nms before return boxes.\n                Default: True.\n            img_meta (dict, optional): Image meta info. Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        if rescale:\n            results.bboxes /= results.bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n        # Add a dummy background class to the backend when using sigmoid\n        # remind that we set FG labels to [0, num_class-1] since mmdet v2.0\n        # BG cat_id: num_class\n        padding = results.scores.new_zeros(results.scores.shape[0], 1)\n        mlvl_scores = torch.cat([results.scores, padding], dim=1)\n\n        mlvl_nms_scores = (mlvl_scores * results.score_factors[:, None]).sqrt()\n        det_bboxes, det_labels = multiclass_nms(\n            results.bboxes,\n            mlvl_nms_scores,\n            cfg.score_thr,\n            cfg.nms,\n            cfg.max_per_img,\n            score_factors=None)\n        if self.with_score_voting and len(det_bboxes) > 0:\n            det_bboxes, det_labels = self.score_voting(det_bboxes, det_labels,\n                                                       results.bboxes,\n                                                       mlvl_nms_scores,\n                                                       cfg.score_thr)\n        nms_results = InstanceData()\n        nms_results.bboxes = det_bboxes[:, :-1]\n        nms_results.scores = det_bboxes[:, -1]\n        nms_results.labels = det_labels\n        return nms_results\n\n    def score_voting(self, det_bboxes: Tensor, det_labels: Tensor,\n                     mlvl_bboxes: Tensor, mlvl_nms_scores: Tensor,\n                     score_thr: float) -> Tuple[Tensor, Tensor]:\n        \"\"\"Implementation of score voting method works on each remaining boxes\n        after NMS procedure.\n\n        Args:\n            det_bboxes (Tensor): Remaining boxes after NMS procedure,\n                with shape (k, 5), each dimension means\n                (x1, y1, x2, y2, score).\n            det_labels (Tensor): The label of remaining boxes, with shape\n                (k, 1),Labels are 0-based.\n            mlvl_bboxes (Tensor): All boxes before the NMS procedure,\n                with shape (num_anchors,4).\n            mlvl_nms_scores (Tensor): The scores of all boxes which is used\n                in the NMS procedure, with shape (num_anchors, num_class)\n            score_thr (float): The score threshold of bboxes.\n\n        Returns:\n            tuple: Usually returns a tuple containing voting results.\n\n                - det_bboxes_voted (Tensor): Remaining boxes after\n                    score voting procedure, with shape (k, 5), each\n                    dimension means (x1, y1, x2, y2, score).\n                - det_labels_voted (Tensor): Label of remaining bboxes\n                    after voting, with shape (num_anchors,).\n        \"\"\"\n        candidate_mask = mlvl_nms_scores > score_thr\n        candidate_mask_nonzeros = candidate_mask.nonzero(as_tuple=False)\n        candidate_inds = candidate_mask_nonzeros[:, 0]\n        candidate_labels = candidate_mask_nonzeros[:, 1]\n        candidate_bboxes = mlvl_bboxes[candidate_inds]\n        candidate_scores = mlvl_nms_scores[candidate_mask]\n        det_bboxes_voted = []\n        det_labels_voted = []\n        for cls in range(self.cls_out_channels):\n            candidate_cls_mask = candidate_labels == cls\n            if not candidate_cls_mask.any():\n                continue\n            candidate_cls_scores = candidate_scores[candidate_cls_mask]\n            candidate_cls_bboxes = candidate_bboxes[candidate_cls_mask]\n            det_cls_mask = det_labels == cls\n            det_cls_bboxes = det_bboxes[det_cls_mask].view(\n                -1, det_bboxes.size(-1))\n            det_candidate_ious = bbox_overlaps(det_cls_bboxes[:, :4],\n                                               candidate_cls_bboxes)\n            for det_ind in range(len(det_cls_bboxes)):\n                single_det_ious = det_candidate_ious[det_ind]\n                pos_ious_mask = single_det_ious > 0.01\n                pos_ious = single_det_ious[pos_ious_mask]\n                pos_bboxes = candidate_cls_bboxes[pos_ious_mask]\n                pos_scores = candidate_cls_scores[pos_ious_mask]\n                pis = (torch.exp(-(1 - pos_ious)**2 / 0.025) *\n                       pos_scores)[:, None]\n                voted_box = torch.sum(\n                    pis * pos_bboxes, dim=0) / torch.sum(\n                        pis, dim=0)\n                voted_score = det_cls_bboxes[det_ind][-1:][None, :]\n                det_bboxes_voted.append(\n                    torch.cat((voted_box[None, :], voted_score), dim=1))\n                det_labels_voted.append(cls)\n\n        det_bboxes_voted = torch.cat(det_bboxes_voted, dim=0)\n        det_labels_voted = det_labels.new_tensor(det_labels_voted)\n        return det_bboxes_voted, det_labels_voted\n"
  },
  {
    "path": "mmdet/models/dense_heads/pisa_retinanet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList, OptInstanceList\nfrom ..losses import carl_loss, isr_p\nfrom ..utils import images_to_levels\nfrom .retina_head import RetinaHead\n\n\n@MODELS.register_module()\nclass PISARetinaHead(RetinaHead):\n    \"\"\"PISA Retinanet Head.\n\n    The head owns the same structure with Retinanet Head, but differs in two\n        aspects:\n        1. Importance-based Sample Reweighting Positive (ISR-P) is applied to\n            change the positive loss weights.\n        2. Classification-aware regression loss is adopted as a third loss.\n    \"\"\"\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Compute losses of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: Loss dict, comprise classification loss, regression loss and\n            carl loss.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        label_channels = self.cls_out_channels if self.use_sigmoid_cls else 1\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            return_sampling_results=True)\n        if cls_reg_targets is None:\n            return None\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor, sampling_results_list) = cls_reg_targets\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        # concat all level anchors and flags to a single tensor\n        concat_anchor_list = []\n        for i in range(len(anchor_list)):\n            concat_anchor_list.append(torch.cat(anchor_list[i]))\n        all_anchor_list = images_to_levels(concat_anchor_list,\n                                           num_level_anchors)\n\n        num_imgs = len(batch_img_metas)\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3, 1).reshape(num_imgs, -1, label_channels)\n            for cls_score in cls_scores\n        ]\n        flatten_cls_scores = torch.cat(\n            flatten_cls_scores, dim=1).reshape(-1,\n                                               flatten_cls_scores[0].size(-1))\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_bbox_preds = torch.cat(\n            flatten_bbox_preds, dim=1).view(-1, flatten_bbox_preds[0].size(-1))\n        flatten_labels = torch.cat(labels_list, dim=1).reshape(-1)\n        flatten_label_weights = torch.cat(\n            label_weights_list, dim=1).reshape(-1)\n        flatten_anchors = torch.cat(all_anchor_list, dim=1).reshape(-1, 4)\n        flatten_bbox_targets = torch.cat(\n            bbox_targets_list, dim=1).reshape(-1, 4)\n        flatten_bbox_weights = torch.cat(\n            bbox_weights_list, dim=1).reshape(-1, 4)\n\n        # Apply ISR-P\n        isr_cfg = self.train_cfg.get('isr', None)\n        if isr_cfg is not None:\n            all_targets = (flatten_labels, flatten_label_weights,\n                           flatten_bbox_targets, flatten_bbox_weights)\n            with torch.no_grad():\n                all_targets = isr_p(\n                    flatten_cls_scores,\n                    flatten_bbox_preds,\n                    all_targets,\n                    flatten_anchors,\n                    sampling_results_list,\n                    bbox_coder=self.bbox_coder,\n                    loss_cls=self.loss_cls,\n                    num_class=self.num_classes,\n                    **self.train_cfg['isr'])\n            (flatten_labels, flatten_label_weights, flatten_bbox_targets,\n             flatten_bbox_weights) = all_targets\n\n        # For convenience we compute loss once instead separating by fpn level,\n        # so that we don't need to separate the weights by level again.\n        # The result should be the same\n        losses_cls = self.loss_cls(\n            flatten_cls_scores,\n            flatten_labels,\n            flatten_label_weights,\n            avg_factor=avg_factor)\n        losses_bbox = self.loss_bbox(\n            flatten_bbox_preds,\n            flatten_bbox_targets,\n            flatten_bbox_weights,\n            avg_factor=avg_factor)\n        loss_dict = dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n\n        # CARL Loss\n        carl_cfg = self.train_cfg.get('carl', None)\n        if carl_cfg is not None:\n            loss_carl = carl_loss(\n                flatten_cls_scores,\n                flatten_labels,\n                flatten_bbox_preds,\n                flatten_bbox_targets,\n                self.loss_bbox,\n                **self.train_cfg['carl'],\n                avg_factor=avg_factor,\n                sigmoid=True,\n                num_class=self.num_classes)\n            loss_dict.update(loss_carl)\n\n        return loss_dict\n"
  },
  {
    "path": "mmdet/models/dense_heads/pisa_ssd_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Union\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList, OptInstanceList\nfrom ..losses import CrossEntropyLoss, SmoothL1Loss, carl_loss, isr_p\nfrom ..utils import multi_apply\nfrom .ssd_head import SSDHead\n\n\n# TODO: add loss evaluator for SSD\n@MODELS.register_module()\nclass PISASSDHead(SSDHead):\n    \"\"\"Implementation of `PISA SSD head <https://arxiv.org/abs/1904.04821>`_\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (Sequence[int]): Number of channels in the input feature\n            map.\n        stacked_convs (int): Number of conv layers in cls and reg tower.\n            Defaults to 0.\n        feat_channels (int): Number of hidden channels when stacked_convs\n            > 0. Defaults to 256.\n        use_depthwise (bool): Whether to use DepthwiseSeparableConv.\n            Defaults to False.\n        conv_cfg (:obj:`ConfigDict` or dict, Optional): Dictionary to construct\n            and config conv layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict, Optional): Dictionary to construct\n            and config norm layer. Defaults to None.\n        act_cfg (:obj:`ConfigDict` or dict, Optional): Dictionary to construct\n            and config activation layer. Defaults to None.\n        anchor_generator (:obj:`ConfigDict` or dict): Config dict for anchor\n            generator.\n        bbox_coder (:obj:`ConfigDict` or dict): Config of bounding box coder.\n        reg_decoded_bbox (bool): If true, the regression loss would be\n            applied directly on decoded bounding boxes, converting both\n            the predicted boxes and regression targets to absolute\n            coordinates format. Defaults to False. It should be `True` when\n            using `IoULoss`, `GIoULoss`, or `DIoULoss` in the bbox head.\n        train_cfg (:obj:`ConfigDict` or dict, Optional): Training config of\n            anchor head.\n        test_cfg (:obj:`ConfigDict` or dict, Optional): Testing config of\n            anchor head.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], Optional): Initialization config dict.\n    \"\"\"  # noqa: W605\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Union[List[Tensor], Tensor]]:\n        \"\"\"Compute losses of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Union[List[Tensor], Tensor]]: A dictionary of loss\n            components. the dict has components below:\n\n            - loss_cls (list[Tensor]): A list containing each feature map \\\n            classification loss.\n            - loss_bbox (list[Tensor]): A list containing each feature map \\\n            regression loss.\n            - loss_carl (Tensor): The loss of CARL.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            unmap_outputs=False,\n            return_sampling_results=True)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor, sampling_results_list) = cls_reg_targets\n\n        num_images = len(batch_img_metas)\n        all_cls_scores = torch.cat([\n            s.permute(0, 2, 3, 1).reshape(\n                num_images, -1, self.cls_out_channels) for s in cls_scores\n        ], 1)\n        all_labels = torch.cat(labels_list, -1).view(num_images, -1)\n        all_label_weights = torch.cat(label_weights_list,\n                                      -1).view(num_images, -1)\n        all_bbox_preds = torch.cat([\n            b.permute(0, 2, 3, 1).reshape(num_images, -1, 4)\n            for b in bbox_preds\n        ], -2)\n        all_bbox_targets = torch.cat(bbox_targets_list,\n                                     -2).view(num_images, -1, 4)\n        all_bbox_weights = torch.cat(bbox_weights_list,\n                                     -2).view(num_images, -1, 4)\n\n        # concat all level anchors to a single tensor\n        all_anchors = []\n        for i in range(num_images):\n            all_anchors.append(torch.cat(anchor_list[i]))\n\n        isr_cfg = self.train_cfg.get('isr', None)\n        all_targets = (all_labels.view(-1), all_label_weights.view(-1),\n                       all_bbox_targets.view(-1,\n                                             4), all_bbox_weights.view(-1, 4))\n        # apply ISR-P\n        if isr_cfg is not None:\n            all_targets = isr_p(\n                all_cls_scores.view(-1, all_cls_scores.size(-1)),\n                all_bbox_preds.view(-1, 4),\n                all_targets,\n                torch.cat(all_anchors),\n                sampling_results_list,\n                loss_cls=CrossEntropyLoss(),\n                bbox_coder=self.bbox_coder,\n                **self.train_cfg['isr'],\n                num_class=self.num_classes)\n            (new_labels, new_label_weights, new_bbox_targets,\n             new_bbox_weights) = all_targets\n            all_labels = new_labels.view(all_labels.shape)\n            all_label_weights = new_label_weights.view(all_label_weights.shape)\n            all_bbox_targets = new_bbox_targets.view(all_bbox_targets.shape)\n            all_bbox_weights = new_bbox_weights.view(all_bbox_weights.shape)\n\n        # add CARL loss\n        carl_loss_cfg = self.train_cfg.get('carl', None)\n        if carl_loss_cfg is not None:\n            loss_carl = carl_loss(\n                all_cls_scores.view(-1, all_cls_scores.size(-1)),\n                all_targets[0],\n                all_bbox_preds.view(-1, 4),\n                all_targets[2],\n                SmoothL1Loss(beta=1.),\n                **self.train_cfg['carl'],\n                avg_factor=avg_factor,\n                num_class=self.num_classes)\n\n        # check NaN and Inf\n        assert torch.isfinite(all_cls_scores).all().item(), \\\n            'classification scores become infinite or NaN!'\n        assert torch.isfinite(all_bbox_preds).all().item(), \\\n            'bbox predications become infinite or NaN!'\n\n        losses_cls, losses_bbox = multi_apply(\n            self.loss_by_feat_single,\n            all_cls_scores,\n            all_bbox_preds,\n            all_anchors,\n            all_labels,\n            all_label_weights,\n            all_bbox_targets,\n            all_bbox_weights,\n            avg_factor=avg_factor)\n        loss_dict = dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n        if carl_loss_cfg is not None:\n            loss_dict.update(loss_carl)\n        return loss_dict\n"
  },
  {
    "path": "mmdet/models/dense_heads/reppoints_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Sequence, Tuple\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops import DeformConv2d\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig, OptInstanceList\nfrom ..task_modules.prior_generators import MlvlPointGenerator\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import (filter_scores_and_topk, images_to_levels, multi_apply,\n                     unmap)\nfrom .anchor_free_head import AnchorFreeHead\n\n\n@MODELS.register_module()\nclass RepPointsHead(AnchorFreeHead):\n    \"\"\"RepPoint head.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        point_feat_channels (int): Number of channels of points features.\n        num_points (int): Number of points.\n        gradient_mul (float): The multiplier to gradients from\n            points refinement and recognition.\n        point_strides (Sequence[int]): points strides.\n        point_base_scale (int): bbox scale for assigning labels.\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox_init (:obj:`ConfigDict` or dict): Config of initial points\n            loss.\n        loss_bbox_refine (:obj:`ConfigDict` or dict): Config of points loss in\n            refinement.\n        use_grid_points (bool): If we use bounding box representation, the\n        reppoints is represented as grid points on the bounding box.\n        center_init (bool): Whether to use center point assignment.\n        transform_method (str): The methods to transform RepPoints to bbox.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 point_feat_channels: int = 256,\n                 num_points: int = 9,\n                 gradient_mul: float = 0.1,\n                 point_strides: Sequence[int] = [8, 16, 32, 64, 128],\n                 point_base_scale: int = 4,\n                 loss_cls: ConfigType = dict(\n                     type='FocalLoss',\n                     use_sigmoid=True,\n                     gamma=2.0,\n                     alpha=0.25,\n                     loss_weight=1.0),\n                 loss_bbox_init: ConfigType = dict(\n                     type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=0.5),\n                 loss_bbox_refine: ConfigType = dict(\n                     type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0),\n                 use_grid_points: bool = False,\n                 center_init: bool = True,\n                 transform_method: str = 'moment',\n                 moment_mul: float = 0.01,\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='reppoints_cls_out',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs) -> None:\n        self.num_points = num_points\n        self.point_feat_channels = point_feat_channels\n        self.use_grid_points = use_grid_points\n        self.center_init = center_init\n\n        # we use deform conv to extract points features\n        self.dcn_kernel = int(np.sqrt(num_points))\n        self.dcn_pad = int((self.dcn_kernel - 1) / 2)\n        assert self.dcn_kernel * self.dcn_kernel == num_points, \\\n            'The points number should be a square number.'\n        assert self.dcn_kernel % 2 == 1, \\\n            'The points number should be an odd square number.'\n        dcn_base = np.arange(-self.dcn_pad,\n                             self.dcn_pad + 1).astype(np.float64)\n        dcn_base_y = np.repeat(dcn_base, self.dcn_kernel)\n        dcn_base_x = np.tile(dcn_base, self.dcn_kernel)\n        dcn_base_offset = np.stack([dcn_base_y, dcn_base_x], axis=1).reshape(\n            (-1))\n        self.dcn_base_offset = torch.tensor(dcn_base_offset).view(1, -1, 1, 1)\n\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            loss_cls=loss_cls,\n            init_cfg=init_cfg,\n            **kwargs)\n\n        self.gradient_mul = gradient_mul\n        self.point_base_scale = point_base_scale\n        self.point_strides = point_strides\n        self.prior_generator = MlvlPointGenerator(\n            self.point_strides, offset=0.)\n\n        if self.train_cfg:\n            self.init_assigner = TASK_UTILS.build(\n                self.train_cfg['init']['assigner'])\n            self.refine_assigner = TASK_UTILS.build(\n                self.train_cfg['refine']['assigner'])\n\n            if self.train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler(context=self)\n\n        self.transform_method = transform_method\n        if self.transform_method == 'moment':\n            self.moment_transfer = nn.Parameter(\n                data=torch.zeros(2), requires_grad=True)\n            self.moment_mul = moment_mul\n\n        self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False)\n        if self.use_sigmoid_cls:\n            self.cls_out_channels = self.num_classes\n        else:\n            self.cls_out_channels = self.num_classes + 1\n        self.loss_bbox_init = MODELS.build(loss_bbox_init)\n        self.loss_bbox_refine = MODELS.build(loss_bbox_refine)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        pts_out_dim = 4 if self.use_grid_points else 2 * self.num_points\n        self.reppoints_cls_conv = DeformConv2d(self.feat_channels,\n                                               self.point_feat_channels,\n                                               self.dcn_kernel, 1,\n                                               self.dcn_pad)\n        self.reppoints_cls_out = nn.Conv2d(self.point_feat_channels,\n                                           self.cls_out_channels, 1, 1, 0)\n        self.reppoints_pts_init_conv = nn.Conv2d(self.feat_channels,\n                                                 self.point_feat_channels, 3,\n                                                 1, 1)\n        self.reppoints_pts_init_out = nn.Conv2d(self.point_feat_channels,\n                                                pts_out_dim, 1, 1, 0)\n        self.reppoints_pts_refine_conv = DeformConv2d(self.feat_channels,\n                                                      self.point_feat_channels,\n                                                      self.dcn_kernel, 1,\n                                                      self.dcn_pad)\n        self.reppoints_pts_refine_out = nn.Conv2d(self.point_feat_channels,\n                                                  pts_out_dim, 1, 1, 0)\n\n    def points2bbox(self, pts: Tensor, y_first: bool = True) -> Tensor:\n        \"\"\"Converting the points set into bounding box.\n\n        Args:\n            pts (Tensor): the input points sets (fields), each points\n                set (fields) is represented as 2n scalar.\n            y_first (bool): if y_first=True, the point set is\n                represented as [y1, x1, y2, x2 ... yn, xn], otherwise\n                the point set is represented as\n                [x1, y1, x2, y2 ... xn, yn]. Defaults to True.\n\n        Returns:\n            Tensor: each points set is converting to a bbox [x1, y1, x2, y2].\n        \"\"\"\n        pts_reshape = pts.view(pts.shape[0], -1, 2, *pts.shape[2:])\n        pts_y = pts_reshape[:, :, 0, ...] if y_first else pts_reshape[:, :, 1,\n                                                                      ...]\n        pts_x = pts_reshape[:, :, 1, ...] if y_first else pts_reshape[:, :, 0,\n                                                                      ...]\n        if self.transform_method == 'minmax':\n            bbox_left = pts_x.min(dim=1, keepdim=True)[0]\n            bbox_right = pts_x.max(dim=1, keepdim=True)[0]\n            bbox_up = pts_y.min(dim=1, keepdim=True)[0]\n            bbox_bottom = pts_y.max(dim=1, keepdim=True)[0]\n            bbox = torch.cat([bbox_left, bbox_up, bbox_right, bbox_bottom],\n                             dim=1)\n        elif self.transform_method == 'partial_minmax':\n            pts_y = pts_y[:, :4, ...]\n            pts_x = pts_x[:, :4, ...]\n            bbox_left = pts_x.min(dim=1, keepdim=True)[0]\n            bbox_right = pts_x.max(dim=1, keepdim=True)[0]\n            bbox_up = pts_y.min(dim=1, keepdim=True)[0]\n            bbox_bottom = pts_y.max(dim=1, keepdim=True)[0]\n            bbox = torch.cat([bbox_left, bbox_up, bbox_right, bbox_bottom],\n                             dim=1)\n        elif self.transform_method == 'moment':\n            pts_y_mean = pts_y.mean(dim=1, keepdim=True)\n            pts_x_mean = pts_x.mean(dim=1, keepdim=True)\n            pts_y_std = torch.std(pts_y - pts_y_mean, dim=1, keepdim=True)\n            pts_x_std = torch.std(pts_x - pts_x_mean, dim=1, keepdim=True)\n            moment_transfer = (self.moment_transfer * self.moment_mul) + (\n                self.moment_transfer.detach() * (1 - self.moment_mul))\n            moment_width_transfer = moment_transfer[0]\n            moment_height_transfer = moment_transfer[1]\n            half_width = pts_x_std * torch.exp(moment_width_transfer)\n            half_height = pts_y_std * torch.exp(moment_height_transfer)\n            bbox = torch.cat([\n                pts_x_mean - half_width, pts_y_mean - half_height,\n                pts_x_mean + half_width, pts_y_mean + half_height\n            ],\n                             dim=1)\n        else:\n            raise NotImplementedError\n        return bbox\n\n    def gen_grid_from_reg(self, reg: Tensor,\n                          previous_boxes: Tensor) -> Tuple[Tensor]:\n        \"\"\"Base on the previous bboxes and regression values, we compute the\n        regressed bboxes and generate the grids on the bboxes.\n\n        Args:\n            reg (Tensor): the regression value to previous bboxes.\n            previous_boxes (Tensor): previous bboxes.\n\n        Returns:\n            Tuple[Tensor]: generate grids on the regressed bboxes.\n        \"\"\"\n        b, _, h, w = reg.shape\n        bxy = (previous_boxes[:, :2, ...] + previous_boxes[:, 2:, ...]) / 2.\n        bwh = (previous_boxes[:, 2:, ...] -\n               previous_boxes[:, :2, ...]).clamp(min=1e-6)\n        grid_topleft = bxy + bwh * reg[:, :2, ...] - 0.5 * bwh * torch.exp(\n            reg[:, 2:, ...])\n        grid_wh = bwh * torch.exp(reg[:, 2:, ...])\n        grid_left = grid_topleft[:, [0], ...]\n        grid_top = grid_topleft[:, [1], ...]\n        grid_width = grid_wh[:, [0], ...]\n        grid_height = grid_wh[:, [1], ...]\n        intervel = torch.linspace(0., 1., self.dcn_kernel).view(\n            1, self.dcn_kernel, 1, 1).type_as(reg)\n        grid_x = grid_left + grid_width * intervel\n        grid_x = grid_x.unsqueeze(1).repeat(1, self.dcn_kernel, 1, 1, 1)\n        grid_x = grid_x.view(b, -1, h, w)\n        grid_y = grid_top + grid_height * intervel\n        grid_y = grid_y.unsqueeze(2).repeat(1, 1, self.dcn_kernel, 1, 1)\n        grid_y = grid_y.view(b, -1, h, w)\n        grid_yx = torch.stack([grid_y, grid_x], dim=2)\n        grid_yx = grid_yx.view(b, -1, h, w)\n        regressed_bbox = torch.cat([\n            grid_left, grid_top, grid_left + grid_width, grid_top + grid_height\n        ], 1)\n        return grid_yx, regressed_bbox\n\n    def forward(self, feats: Tuple[Tensor]) -> Tuple[Tensor]:\n        return multi_apply(self.forward_single, feats)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor]:\n        \"\"\"Forward feature map of a single FPN level.\"\"\"\n        dcn_base_offset = self.dcn_base_offset.type_as(x)\n        # If we use center_init, the initial reppoints is from center points.\n        # If we use bounding bbox representation, the initial reppoints is\n        #   from regular grid placed on a pre-defined bbox.\n        if self.use_grid_points or not self.center_init:\n            scale = self.point_base_scale / 2\n            points_init = dcn_base_offset / dcn_base_offset.max() * scale\n            bbox_init = x.new_tensor([-scale, -scale, scale,\n                                      scale]).view(1, 4, 1, 1)\n        else:\n            points_init = 0\n        cls_feat = x\n        pts_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            pts_feat = reg_conv(pts_feat)\n        # initialize reppoints\n        pts_out_init = self.reppoints_pts_init_out(\n            self.relu(self.reppoints_pts_init_conv(pts_feat)))\n        if self.use_grid_points:\n            pts_out_init, bbox_out_init = self.gen_grid_from_reg(\n                pts_out_init, bbox_init.detach())\n        else:\n            pts_out_init = pts_out_init + points_init\n        # refine and classify reppoints\n        pts_out_init_grad_mul = (1 - self.gradient_mul) * pts_out_init.detach(\n        ) + self.gradient_mul * pts_out_init\n        dcn_offset = pts_out_init_grad_mul - dcn_base_offset\n        cls_out = self.reppoints_cls_out(\n            self.relu(self.reppoints_cls_conv(cls_feat, dcn_offset)))\n        pts_out_refine = self.reppoints_pts_refine_out(\n            self.relu(self.reppoints_pts_refine_conv(pts_feat, dcn_offset)))\n        if self.use_grid_points:\n            pts_out_refine, bbox_out_refine = self.gen_grid_from_reg(\n                pts_out_refine, bbox_out_init.detach())\n        else:\n            pts_out_refine = pts_out_refine + pts_out_init.detach()\n\n        if self.training:\n            return cls_out, pts_out_init, pts_out_refine\n        else:\n            return cls_out, self.points2bbox(pts_out_refine)\n\n    def get_points(self, featmap_sizes: List[Tuple[int]],\n                   batch_img_metas: List[dict], device: str) -> tuple:\n        \"\"\"Get points according to feature map sizes.\n\n        Args:\n            featmap_sizes (list[tuple]): Multi-level feature map sizes.\n            batch_img_metas (list[dict]): Image meta info.\n\n        Returns:\n            tuple: points of each image, valid flags of each image\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n\n        # since feature map sizes of all images are the same, we only compute\n        # points center for one time\n        multi_level_points = self.prior_generator.grid_priors(\n            featmap_sizes, device=device, with_stride=True)\n        points_list = [[point.clone() for point in multi_level_points]\n                       for _ in range(num_imgs)]\n\n        # for each image, we compute valid flags of multi level grids\n        valid_flag_list = []\n        for img_id, img_meta in enumerate(batch_img_metas):\n            multi_level_flags = self.prior_generator.valid_flags(\n                featmap_sizes, img_meta['pad_shape'], device=device)\n            valid_flag_list.append(multi_level_flags)\n\n        return points_list, valid_flag_list\n\n    def centers_to_bboxes(self, point_list: List[Tensor]) -> List[Tensor]:\n        \"\"\"Get bboxes according to center points.\n\n        Only used in :class:`MaxIoUAssigner`.\n        \"\"\"\n        bbox_list = []\n        for i_img, point in enumerate(point_list):\n            bbox = []\n            for i_lvl in range(len(self.point_strides)):\n                scale = self.point_base_scale * self.point_strides[i_lvl] * 0.5\n                bbox_shift = torch.Tensor([-scale, -scale, scale,\n                                           scale]).view(1, 4).type_as(point[0])\n                bbox_center = torch.cat(\n                    [point[i_lvl][:, :2], point[i_lvl][:, :2]], dim=1)\n                bbox.append(bbox_center + bbox_shift)\n            bbox_list.append(bbox)\n        return bbox_list\n\n    def offset_to_pts(self, center_list: List[Tensor],\n                      pred_list: List[Tensor]) -> List[Tensor]:\n        \"\"\"Change from point offset to point coordinate.\"\"\"\n        pts_list = []\n        for i_lvl in range(len(self.point_strides)):\n            pts_lvl = []\n            for i_img in range(len(center_list)):\n                pts_center = center_list[i_img][i_lvl][:, :2].repeat(\n                    1, self.num_points)\n                pts_shift = pred_list[i_lvl][i_img]\n                yx_pts_shift = pts_shift.permute(1, 2, 0).view(\n                    -1, 2 * self.num_points)\n                y_pts_shift = yx_pts_shift[..., 0::2]\n                x_pts_shift = yx_pts_shift[..., 1::2]\n                xy_pts_shift = torch.stack([x_pts_shift, y_pts_shift], -1)\n                xy_pts_shift = xy_pts_shift.view(*yx_pts_shift.shape[:-1], -1)\n                pts = xy_pts_shift * self.point_strides[i_lvl] + pts_center\n                pts_lvl.append(pts)\n            pts_lvl = torch.stack(pts_lvl, 0)\n            pts_list.append(pts_lvl)\n        return pts_list\n\n    def _get_targets_single(self,\n                            flat_proposals: Tensor,\n                            valid_flags: Tensor,\n                            gt_instances: InstanceData,\n                            gt_instances_ignore: InstanceData,\n                            stage: str = 'init',\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute corresponding GT box and classification targets for\n        proposals.\n\n        Args:\n            flat_proposals (Tensor): Multi level points of a image.\n            valid_flags (Tensor): Multi level valid flags of a image.\n            gt_instances (InstanceData): It usually includes ``bboxes`` and\n                ``labels`` attributes.\n            gt_instances_ignore (InstanceData): It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n            stage (str): 'init' or 'refine'. Generate target for\n                init stage or refine stage. Defaults to 'init'.\n            unmap_outputs (bool): Whether to map outputs back to\n                the original set of anchors. Defaults to True.\n\n        Returns:\n            tuple:\n\n                - labels (Tensor): Labels of each level.\n                - label_weights (Tensor): Label weights of each level.\n                - bbox_targets (Tensor): BBox targets of each level.\n                - bbox_weights (Tensor): BBox weights of each level.\n                - pos_inds (Tensor): positive samples indexes.\n                - neg_inds (Tensor): negative samples indexes.\n                - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        inside_flags = valid_flags\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid proposal inside the image boundary. Please '\n                'check the image size.')\n        # assign gt and sample proposals\n        proposals = flat_proposals[inside_flags, :]\n        pred_instances = InstanceData(priors=proposals)\n\n        if stage == 'init':\n            assigner = self.init_assigner\n            pos_weight = self.train_cfg['init']['pos_weight']\n        else:\n            assigner = self.refine_assigner\n            pos_weight = self.train_cfg['refine']['pos_weight']\n\n        assign_result = assigner.assign(pred_instances, gt_instances,\n                                        gt_instances_ignore)\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_proposals = proposals.shape[0]\n        bbox_gt = proposals.new_zeros([num_valid_proposals, 4])\n        pos_proposals = torch.zeros_like(proposals)\n        proposals_weights = proposals.new_zeros([num_valid_proposals, 4])\n        labels = proposals.new_full((num_valid_proposals, ),\n                                    self.num_classes,\n                                    dtype=torch.long)\n        label_weights = proposals.new_zeros(\n            num_valid_proposals, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            bbox_gt[pos_inds, :] = sampling_result.pos_gt_bboxes\n            pos_proposals[pos_inds, :] = proposals[pos_inds, :]\n            proposals_weights[pos_inds, :] = 1.0\n\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if pos_weight <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = pos_weight\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # map up to original set of proposals\n        if unmap_outputs:\n            num_total_proposals = flat_proposals.size(0)\n            labels = unmap(\n                labels,\n                num_total_proposals,\n                inside_flags,\n                fill=self.num_classes)  # fill bg label\n            label_weights = unmap(label_weights, num_total_proposals,\n                                  inside_flags)\n            bbox_gt = unmap(bbox_gt, num_total_proposals, inside_flags)\n            pos_proposals = unmap(pos_proposals, num_total_proposals,\n                                  inside_flags)\n            proposals_weights = unmap(proposals_weights, num_total_proposals,\n                                      inside_flags)\n\n        return (labels, label_weights, bbox_gt, pos_proposals,\n                proposals_weights, pos_inds, neg_inds, sampling_result)\n\n    def get_targets(self,\n                    proposals_list: List[Tensor],\n                    valid_flag_list: List[Tensor],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    stage: str = 'init',\n                    unmap_outputs: bool = True,\n                    return_sampling_results: bool = False) -> tuple:\n        \"\"\"Compute corresponding GT box and classification targets for\n        proposals.\n\n        Args:\n            proposals_list (list[Tensor]): Multi level points/bboxes of each\n                image.\n            valid_flag_list (list[Tensor]): Multi level valid flags of each\n                image.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            stage (str): 'init' or 'refine'. Generate target for init stage or\n                refine stage.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n            return_sampling_results (bool): Whether to return the sampling\n                results. Defaults to False.\n\n        Returns:\n            tuple:\n\n                - labels_list (list[Tensor]): Labels of each level.\n                - label_weights_list (list[Tensor]): Label weights of each\n                  level.\n                - bbox_gt_list (list[Tensor]): Ground truth bbox of each level.\n                - proposals_list (list[Tensor]): Proposals(points/bboxes) of\n                  each level.\n                - proposal_weights_list (list[Tensor]): Proposal weights of\n                  each level.\n                - avg_factor (int): Average factor that is used to average\n                  the loss. When using sampling method, avg_factor is usually\n                  the sum of positive and negative priors. When using\n                  `PseudoSampler`, `avg_factor` is usually equal to the number\n                  of positive priors.\n        \"\"\"\n        assert stage in ['init', 'refine']\n        num_imgs = len(batch_img_metas)\n        assert len(proposals_list) == len(valid_flag_list) == num_imgs\n\n        # points number of multi levels\n        num_level_proposals = [points.size(0) for points in proposals_list[0]]\n\n        # concat all level points and flags to a single tensor\n        for i in range(num_imgs):\n            assert len(proposals_list[i]) == len(valid_flag_list[i])\n            proposals_list[i] = torch.cat(proposals_list[i])\n            valid_flag_list[i] = torch.cat(valid_flag_list[i])\n\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n\n        (all_labels, all_label_weights, all_bbox_gt, all_proposals,\n         all_proposal_weights, pos_inds_list, neg_inds_list,\n         sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             proposals_list,\n             valid_flag_list,\n             batch_gt_instances,\n             batch_gt_instances_ignore,\n             stage=stage,\n             unmap_outputs=unmap_outputs)\n\n        # sampled points of all images\n        avg_refactor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        labels_list = images_to_levels(all_labels, num_level_proposals)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_proposals)\n        bbox_gt_list = images_to_levels(all_bbox_gt, num_level_proposals)\n        proposals_list = images_to_levels(all_proposals, num_level_proposals)\n        proposal_weights_list = images_to_levels(all_proposal_weights,\n                                                 num_level_proposals)\n        res = (labels_list, label_weights_list, bbox_gt_list, proposals_list,\n               proposal_weights_list, avg_refactor)\n        if return_sampling_results:\n            res = res + (sampling_results_list, )\n\n        return res\n\n    def loss_by_feat_single(self, cls_score: Tensor, pts_pred_init: Tensor,\n                            pts_pred_refine: Tensor, labels: Tensor,\n                            label_weights, bbox_gt_init: Tensor,\n                            bbox_weights_init: Tensor, bbox_gt_refine: Tensor,\n                            bbox_weights_refine: Tensor, stride: int,\n                            avg_factor_init: int,\n                            avg_factor_refine: int) -> Tuple[Tensor]:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_classes, h_i, w_i).\n            pts_pred_init (Tensor): Points of shape\n                (batch_size, h_i * w_i, num_points * 2).\n            pts_pred_refine (Tensor): Points refined of shape\n                (batch_size, h_i * w_i, num_points * 2).\n            labels (Tensor): Ground truth class indices with shape\n                (batch_size, h_i * w_i).\n            label_weights (Tensor): Label weights of shape\n                (batch_size, h_i * w_i).\n            bbox_gt_init (Tensor): BBox regression targets in the init stage\n                of shape (batch_size, h_i * w_i, 4).\n            bbox_weights_init (Tensor): BBox regression loss weights in the\n                init stage of shape (batch_size, h_i * w_i, 4).\n            bbox_gt_refine (Tensor): BBox regression targets in the refine\n                stage of shape (batch_size, h_i * w_i, 4).\n            bbox_weights_refine (Tensor): BBox regression loss weights in the\n                refine stage of shape (batch_size, h_i * w_i, 4).\n            stride (int): Point stride.\n            avg_factor_init (int): Average factor that is used to average\n                the loss in the init stage.\n            avg_factor_refine (int): Average factor that is used to average\n                the loss in the refine stage.\n\n        Returns:\n            Tuple[Tensor]: loss components.\n        \"\"\"\n        # classification loss\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        cls_score = cls_score.permute(0, 2, 3,\n                                      1).reshape(-1, self.cls_out_channels)\n        cls_score = cls_score.contiguous()\n        loss_cls = self.loss_cls(\n            cls_score, labels, label_weights, avg_factor=avg_factor_refine)\n\n        # points loss\n        bbox_gt_init = bbox_gt_init.reshape(-1, 4)\n        bbox_weights_init = bbox_weights_init.reshape(-1, 4)\n        bbox_pred_init = self.points2bbox(\n            pts_pred_init.reshape(-1, 2 * self.num_points), y_first=False)\n        bbox_gt_refine = bbox_gt_refine.reshape(-1, 4)\n        bbox_weights_refine = bbox_weights_refine.reshape(-1, 4)\n        bbox_pred_refine = self.points2bbox(\n            pts_pred_refine.reshape(-1, 2 * self.num_points), y_first=False)\n        normalize_term = self.point_base_scale * stride\n        loss_pts_init = self.loss_bbox_init(\n            bbox_pred_init / normalize_term,\n            bbox_gt_init / normalize_term,\n            bbox_weights_init,\n            avg_factor=avg_factor_init)\n        loss_pts_refine = self.loss_bbox_refine(\n            bbox_pred_refine / normalize_term,\n            bbox_gt_refine / normalize_term,\n            bbox_weights_refine,\n            avg_factor=avg_factor_refine)\n        return loss_cls, loss_pts_init, loss_pts_refine\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        pts_preds_init: List[Tensor],\n        pts_preds_refine: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, of shape (batch_size, num_classes, h, w).\n            pts_preds_init (list[Tensor]): Points for each scale level, each is\n                a 3D-tensor, of shape (batch_size, h_i * w_i, num_points * 2).\n            pts_preds_refine (list[Tensor]): Points refined for each scale\n                level, each is a 3D-tensor, of shape\n                (batch_size, h_i * w_i, num_points * 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        device = cls_scores[0].device\n\n        # target for initial stage\n        center_list, valid_flag_list = self.get_points(featmap_sizes,\n                                                       batch_img_metas, device)\n        pts_coordinate_preds_init = self.offset_to_pts(center_list,\n                                                       pts_preds_init)\n        if self.train_cfg['init']['assigner']['type'] == 'PointAssigner':\n            # Assign target for center list\n            candidate_list = center_list\n        else:\n            # transform center list to bbox list and\n            #   assign target for bbox list\n            bbox_list = self.centers_to_bboxes(center_list)\n            candidate_list = bbox_list\n        cls_reg_targets_init = self.get_targets(\n            proposals_list=candidate_list,\n            valid_flag_list=valid_flag_list,\n            batch_gt_instances=batch_gt_instances,\n            batch_img_metas=batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            stage='init',\n            return_sampling_results=False)\n        (*_, bbox_gt_list_init, candidate_list_init, bbox_weights_list_init,\n         avg_factor_init) = cls_reg_targets_init\n\n        # target for refinement stage\n        center_list, valid_flag_list = self.get_points(featmap_sizes,\n                                                       batch_img_metas, device)\n        pts_coordinate_preds_refine = self.offset_to_pts(\n            center_list, pts_preds_refine)\n        bbox_list = []\n        for i_img, center in enumerate(center_list):\n            bbox = []\n            for i_lvl in range(len(pts_preds_refine)):\n                bbox_preds_init = self.points2bbox(\n                    pts_preds_init[i_lvl].detach())\n                bbox_shift = bbox_preds_init * self.point_strides[i_lvl]\n                bbox_center = torch.cat(\n                    [center[i_lvl][:, :2], center[i_lvl][:, :2]], dim=1)\n                bbox.append(bbox_center +\n                            bbox_shift[i_img].permute(1, 2, 0).reshape(-1, 4))\n            bbox_list.append(bbox)\n        cls_reg_targets_refine = self.get_targets(\n            proposals_list=bbox_list,\n            valid_flag_list=valid_flag_list,\n            batch_gt_instances=batch_gt_instances,\n            batch_img_metas=batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            stage='refine',\n            return_sampling_results=False)\n        (labels_list, label_weights_list, bbox_gt_list_refine,\n         candidate_list_refine, bbox_weights_list_refine,\n         avg_factor_refine) = cls_reg_targets_refine\n\n        # compute loss\n        losses_cls, losses_pts_init, losses_pts_refine = multi_apply(\n            self.loss_by_feat_single,\n            cls_scores,\n            pts_coordinate_preds_init,\n            pts_coordinate_preds_refine,\n            labels_list,\n            label_weights_list,\n            bbox_gt_list_init,\n            bbox_weights_list_init,\n            bbox_gt_list_refine,\n            bbox_weights_list_refine,\n            self.point_strides,\n            avg_factor_init=avg_factor_init,\n            avg_factor_refine=avg_factor_refine)\n        loss_dict_all = {\n            'loss_cls': losses_cls,\n            'loss_pts_init': losses_pts_init,\n            'loss_pts_refine': losses_pts_refine\n        }\n        return loss_dict_all\n\n    # Same as base_dense_head/_get_bboxes_single except self._bbox_decode\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform outputs of a single image into bbox predictions.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image. RepPoints head does not need\n                this value.\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid, has shape\n                (num_priors, 2).\n            img_meta (dict): Image meta info.\n            cfg (:obj:`ConfigDict`): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        assert len(cls_score_list) == len(bbox_pred_list)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bboxes = []\n        mlvl_scores = []\n        mlvl_labels = []\n        for level_idx, (cls_score, bbox_pred, priors) in enumerate(\n                zip(cls_score_list, bbox_pred_list, mlvl_priors)):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4)\n\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            if self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                scores = cls_score.softmax(-1)[:, :-1]\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            results = filter_scores_and_topk(\n                scores, cfg.score_thr, nms_pre,\n                dict(bbox_pred=bbox_pred, priors=priors))\n            scores, labels, _, filtered_results = results\n\n            bbox_pred = filtered_results['bbox_pred']\n            priors = filtered_results['priors']\n\n            bboxes = self._bbox_decode(priors, bbox_pred,\n                                       self.point_strides[level_idx],\n                                       img_shape)\n\n            mlvl_bboxes.append(bboxes)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bboxes)\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n    def _bbox_decode(self, points: Tensor, bbox_pred: Tensor, stride: int,\n                     max_shape: Tuple[int, int]) -> Tensor:\n        \"\"\"Decode the prediction to bounding box.\n\n        Args:\n            points (Tensor): shape (h_i * w_i, 2).\n            bbox_pred (Tensor): shape (h_i * w_i, 4).\n            stride (int): Stride for bbox_pred in different level.\n            max_shape (Tuple[int, int]): image shape.\n\n        Returns:\n            Tensor: Bounding boxes decoded.\n        \"\"\"\n        bbox_pos_center = torch.cat([points[:, :2], points[:, :2]], dim=1)\n        bboxes = bbox_pred * stride + bbox_pos_center\n        x1 = bboxes[:, 0].clamp(min=0, max=max_shape[1])\n        y1 = bboxes[:, 1].clamp(min=0, max=max_shape[0])\n        x2 = bboxes[:, 2].clamp(min=0, max=max_shape[1])\n        y2 = bboxes[:, 3].clamp(min=0, max=max_shape[0])\n        decoded_bboxes = torch.stack([x1, y1, x2, y2], dim=-1)\n        return decoded_bboxes\n"
  },
  {
    "path": "mmdet/models/dense_heads/retina_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\n\nfrom mmdet.registry import MODELS\nfrom .anchor_head import AnchorHead\n\n\n@MODELS.register_module()\nclass RetinaHead(AnchorHead):\n    r\"\"\"An anchor-based head used in `RetinaNet\n    <https://arxiv.org/pdf/1708.02002.pdf>`_.\n\n    The head contains two subnetworks. The first classifies anchor boxes and\n    the second regresses deltas for the anchors.\n\n    Example:\n        >>> import torch\n        >>> self = RetinaHead(11, 7)\n        >>> x = torch.rand(1, 7, 32, 32)\n        >>> cls_score, bbox_pred = self.forward_single(x)\n        >>> # Each anchor predicts a score for each class except background\n        >>> cls_per_anchor = cls_score.shape[1] / self.num_anchors\n        >>> box_per_anchor = bbox_pred.shape[1] / self.num_anchors\n        >>> assert cls_per_anchor == (self.num_classes)\n        >>> assert box_per_anchor == 4\n    \"\"\"\n\n    def __init__(self,\n                 num_classes,\n                 in_channels,\n                 stacked_convs=4,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 anchor_generator=dict(\n                     type='AnchorGenerator',\n                     octave_base_scale=4,\n                     scales_per_octave=3,\n                     ratios=[0.5, 1.0, 2.0],\n                     strides=[8, 16, 32, 64, 128]),\n                 init_cfg=dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='retina_cls',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs):\n        assert stacked_convs >= 0, \\\n            '`stacked_convs` must be non-negative integers, ' \\\n            f'but got {stacked_convs} instead.'\n        self.stacked_convs = stacked_convs\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        super(RetinaHead, self).__init__(\n            num_classes,\n            in_channels,\n            anchor_generator=anchor_generator,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self):\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        in_channels = self.in_channels\n        for i in range(self.stacked_convs):\n            self.cls_convs.append(\n                ConvModule(\n                    in_channels,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    in_channels,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            in_channels = self.feat_channels\n        self.retina_cls = nn.Conv2d(\n            in_channels,\n            self.num_base_priors * self.cls_out_channels,\n            3,\n            padding=1)\n        reg_dim = self.bbox_coder.encode_size\n        self.retina_reg = nn.Conv2d(\n            in_channels, self.num_base_priors * reg_dim, 3, padding=1)\n\n    def forward_single(self, x):\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n\n        Returns:\n            tuple:\n                cls_score (Tensor): Cls scores for a single scale level\n                    the channels number is num_anchors * num_classes.\n                bbox_pred (Tensor): Box energies / deltas for a single scale\n                    level, the channels number is num_anchors * 4.\n        \"\"\"\n        cls_feat = x\n        reg_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            reg_feat = reg_conv(reg_feat)\n        cls_score = self.retina_cls(cls_feat)\n        bbox_pred = self.retina_reg(reg_feat)\n        return cls_score, bbox_pred\n"
  },
  {
    "path": "mmdet/models/dense_heads/retina_sepbn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import bias_init_with_prob, normal_init\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .anchor_head import AnchorHead\n\n\n@MODELS.register_module()\nclass RetinaSepBNHead(AnchorHead):\n    \"\"\"\"RetinaHead with separate BN.\n\n    In RetinaHead, conv/norm layers are shared across different FPN levels,\n    while in RetinaSepBNHead, conv layers are shared across different FPN\n    levels, but BN layers are separated.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 num_ins: int,\n                 in_channels: int,\n                 stacked_convs: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        self.stacked_convs = stacked_convs\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.num_ins = num_ins\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.num_ins):\n            cls_convs = nn.ModuleList()\n            reg_convs = nn.ModuleList()\n            for j in range(self.stacked_convs):\n                chn = self.in_channels if j == 0 else self.feat_channels\n                cls_convs.append(\n                    ConvModule(\n                        chn,\n                        self.feat_channels,\n                        3,\n                        stride=1,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg))\n                reg_convs.append(\n                    ConvModule(\n                        chn,\n                        self.feat_channels,\n                        3,\n                        stride=1,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg))\n            self.cls_convs.append(cls_convs)\n            self.reg_convs.append(reg_convs)\n        for i in range(self.stacked_convs):\n            for j in range(1, self.num_ins):\n                self.cls_convs[j][i].conv = self.cls_convs[0][i].conv\n                self.reg_convs[j][i].conv = self.reg_convs[0][i].conv\n        self.retina_cls = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * self.cls_out_channels,\n            3,\n            padding=1)\n        self.retina_reg = nn.Conv2d(\n            self.feat_channels, self.num_base_priors * 4, 3, padding=1)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        super().init_weights()\n        for m in self.cls_convs[0]:\n            normal_init(m.conv, std=0.01)\n        for m in self.reg_convs[0]:\n            normal_init(m.conv, std=0.01)\n        bias_cls = bias_init_with_prob(0.01)\n        normal_init(self.retina_cls, std=0.01, bias=bias_cls)\n        normal_init(self.retina_reg, std=0.01)\n\n    def forward(self, feats: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n\n                - cls_scores (list[Tensor]): Classification scores for all\n                  scale levels, each is a 4D-tensor, the channels number is\n                  num_anchors * num_classes.\n                - bbox_preds (list[Tensor]): Box energies / deltas for all\n                  scale levels, each is a 4D-tensor, the channels number is\n                  num_anchors * 4.\n        \"\"\"\n        cls_scores = []\n        bbox_preds = []\n        for i, x in enumerate(feats):\n            cls_feat = feats[i]\n            reg_feat = feats[i]\n            for cls_conv in self.cls_convs[i]:\n                cls_feat = cls_conv(cls_feat)\n            for reg_conv in self.reg_convs[i]:\n                reg_feat = reg_conv(reg_feat)\n            cls_score = self.retina_cls(cls_feat)\n            bbox_pred = self.retina_reg(reg_feat)\n            cls_scores.append(cls_score)\n            bbox_preds.append(bbox_pred)\n        return cls_scores, bbox_preds\n"
  },
  {
    "path": "mmdet/models/dense_heads/rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops import batched_nms\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import (cat_boxes, empty_box_as, get_box_tensor,\n                                   get_box_wh, scale_boxes)\nfrom mmdet.utils import InstanceList, MultiConfig, OptInstanceList\nfrom .anchor_head import AnchorHead\n\n\n@MODELS.register_module()\nclass RPNHead(AnchorHead):\n    \"\"\"Implementation of RPN head.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        num_classes (int): Number of categories excluding the background\n            category. Defaults to 1.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or \\\n            list[dict]): Initialization config dict.\n        num_convs (int): Number of convolution layers in the head.\n            Defaults to 1.\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 in_channels: int,\n                 num_classes: int = 1,\n                 init_cfg: MultiConfig = dict(\n                     type='Normal', layer='Conv2d', std=0.01),\n                 num_convs: int = 1,\n                 **kwargs) -> None:\n        self.num_convs = num_convs\n        assert num_classes == 1\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        if self.num_convs > 1:\n            rpn_convs = []\n            for i in range(self.num_convs):\n                if i == 0:\n                    in_channels = self.in_channels\n                else:\n                    in_channels = self.feat_channels\n                # use ``inplace=False`` to avoid error: one of the variables\n                # needed for gradient computation has been modified by an\n                # inplace operation.\n                rpn_convs.append(\n                    ConvModule(\n                        in_channels,\n                        self.feat_channels,\n                        3,\n                        padding=1,\n                        inplace=False))\n            self.rpn_conv = nn.Sequential(*rpn_convs)\n        else:\n            self.rpn_conv = nn.Conv2d(\n                self.in_channels, self.feat_channels, 3, padding=1)\n        self.rpn_cls = nn.Conv2d(self.feat_channels,\n                                 self.num_base_priors * self.cls_out_channels,\n                                 1)\n        reg_dim = self.bbox_coder.encode_size\n        self.rpn_reg = nn.Conv2d(self.feat_channels,\n                                 self.num_base_priors * reg_dim, 1)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n\n        Returns:\n            tuple:\n                cls_score (Tensor): Cls scores for a single scale level \\\n                    the channels number is num_base_priors * num_classes.\n                bbox_pred (Tensor): Box energies / deltas for a single scale \\\n                    level, the channels number is num_base_priors * 4.\n        \"\"\"\n        x = self.rpn_conv(x)\n        x = F.relu(x)\n        rpn_cls_score = self.rpn_cls(x)\n        rpn_bbox_pred = self.rpn_reg(x)\n        return rpn_cls_score, rpn_bbox_pred\n\n    def loss_by_feat(self,\n                     cls_scores: List[Tensor],\n                     bbox_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict],\n                     batch_gt_instances_ignore: OptInstanceList = None) \\\n            -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            batch_gt_instances (list[obj:InstanceData]): Batch of gt_instance.\n                It usually includes ``bboxes`` and ``labels`` attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[obj:InstanceData], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        losses = super().loss_by_feat(\n            cls_scores,\n            bbox_preds,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        return dict(\n            loss_rpn_cls=losses['loss_cls'], loss_rpn_bbox=losses['loss_bbox'])\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Be compatible with\n                BaseDenseHead. Not used in RPNHead.\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid. In all\n                anchor-based methods, it has shape (num_priors, 4). In\n                all anchor-free methods, it has shape (num_priors, 2)\n                when `with_stride=True`, otherwise it still has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (ConfigDict, optional): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bbox_preds = []\n        mlvl_valid_priors = []\n        mlvl_scores = []\n        level_ids = []\n        for level_idx, (cls_score, bbox_pred, priors) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list,\n                              mlvl_priors)):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            reg_dim = self.bbox_coder.encode_size\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, reg_dim)\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            if self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                # remind that we set FG labels to [0] since mmdet v2.0\n                # BG cat_id: 1\n                scores = cls_score.softmax(-1)[:, :-1]\n\n            scores = torch.squeeze(scores)\n            if 0 < nms_pre < scores.shape[0]:\n                # sort is faster than topk\n                # _, topk_inds = scores.topk(cfg.nms_pre)\n                ranked_scores, rank_inds = scores.sort(descending=True)\n                topk_inds = rank_inds[:nms_pre]\n                scores = ranked_scores[:nms_pre]\n                bbox_pred = bbox_pred[topk_inds, :]\n                priors = priors[topk_inds]\n\n            mlvl_bbox_preds.append(bbox_pred)\n            mlvl_valid_priors.append(priors)\n            mlvl_scores.append(scores)\n\n            # use level id to implement the separate level nms\n            level_ids.append(\n                scores.new_full((scores.size(0), ),\n                                level_idx,\n                                dtype=torch.long))\n\n        bbox_pred = torch.cat(mlvl_bbox_preds)\n        priors = cat_boxes(mlvl_valid_priors)\n        bboxes = self.bbox_coder.decode(priors, bbox_pred, max_shape=img_shape)\n\n        results = InstanceData()\n        results.bboxes = bboxes\n        results.scores = torch.cat(mlvl_scores)\n        results.level_ids = torch.cat(level_ids)\n\n        return self._bbox_post_process(\n            results=results, cfg=cfg, rescale=rescale, img_meta=img_meta)\n\n    def _bbox_post_process(self,\n                           results: InstanceData,\n                           cfg: ConfigDict,\n                           rescale: bool = False,\n                           with_nms: bool = True,\n                           img_meta: Optional[dict] = None) -> InstanceData:\n        \"\"\"bbox post-processing method.\n\n        The boxes would be rescaled to the original image scale and do\n        the nms operation.\n\n        Args:\n            results (:obj:`InstaceData`): Detection instance results,\n                each item has shape (num_bboxes, ).\n            cfg (ConfigDict): Test / postprocessing configuration.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Default to True.\n            img_meta (dict, optional): Image meta info. Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert with_nms, '`with_nms` must be True in RPNHead'\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = [1 / s for s in img_meta['scale_factor']]\n            results.bboxes = scale_boxes(results.bboxes, scale_factor)\n\n        # filter small size bboxes\n        if cfg.get('min_bbox_size', -1) >= 0:\n            w, h = get_box_wh(results.bboxes)\n            valid_mask = (w > cfg.min_bbox_size) & (h > cfg.min_bbox_size)\n            if not valid_mask.all():\n                results = results[valid_mask]\n\n        if results.bboxes.numel() > 0:\n            bboxes = get_box_tensor(results.bboxes)\n            det_bboxes, keep_idxs = batched_nms(bboxes, results.scores,\n                                                results.level_ids, cfg.nms)\n            results = results[keep_idxs]\n            # some nms would reweight the score, such as softnms\n            results.scores = det_bboxes[:, -1]\n            results = results[:cfg.max_per_img]\n            # TODO: This would unreasonably show the 0th class label\n            #  in visualization\n            results.labels = results.scores.new_zeros(\n                len(results), dtype=torch.long)\n            del results.level_ids\n        else:\n            # To avoid some potential error\n            results_ = InstanceData()\n            results_.bboxes = empty_box_as(results.bboxes)\n            results_.scores = results.scores.new_zeros(0)\n            results_.labels = results.scores.new_zeros(0)\n            results = results_\n        return results\n"
  },
  {
    "path": "mmdet/models/dense_heads/rtmdet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule, Scale, is_norm\nfrom mmengine.model import bias_init_with_prob, constant_init, normal_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import distance2bbox\nfrom mmdet.utils import ConfigType, InstanceList, OptInstanceList, reduce_mean\nfrom ..layers.transformer import inverse_sigmoid\nfrom ..task_modules import anchor_inside_flags\nfrom ..utils import (images_to_levels, multi_apply, sigmoid_geometric_mean,\n                     unmap)\nfrom .atss_head import ATSSHead\n\n\n@MODELS.register_module()\nclass RTMDetHead(ATSSHead):\n    \"\"\"Detection Head of RTMDet.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        with_objectness (bool): Whether to add an objectness branch.\n            Defaults to True.\n        act_cfg (:obj:`ConfigDict` or dict): Config dict for activation layer.\n            Default: dict(type='ReLU')\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 with_objectness: bool = True,\n                 act_cfg: ConfigType = dict(type='ReLU'),\n                 **kwargs) -> None:\n        self.act_cfg = act_cfg\n        self.with_objectness = with_objectness\n        super().__init__(num_classes, in_channels, **kwargs)\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n\n    def _init_layers(self):\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    act_cfg=self.act_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    act_cfg=self.act_cfg))\n        pred_pad_size = self.pred_kernel_size // 2\n        self.rtm_cls = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * self.cls_out_channels,\n            self.pred_kernel_size,\n            padding=pred_pad_size)\n        self.rtm_reg = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * 4,\n            self.pred_kernel_size,\n            padding=pred_pad_size)\n        if self.with_objectness:\n            self.rtm_obj = nn.Conv2d(\n                self.feat_channels,\n                1,\n                self.pred_kernel_size,\n                padding=pred_pad_size)\n\n        self.scales = nn.ModuleList(\n            [Scale(1.0) for _ in self.prior_generator.strides])\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, mean=0, std=0.01)\n            if is_norm(m):\n                constant_init(m, 1)\n        bias_cls = bias_init_with_prob(0.01)\n        normal_init(self.rtm_cls, std=0.01, bias=bias_cls)\n        normal_init(self.rtm_reg, std=0.01)\n        if self.with_objectness:\n            normal_init(self.rtm_obj, std=0.01, bias=bias_cls)\n\n    def forward(self, feats: Tuple[Tensor, ...]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n            - cls_scores (list[Tensor]): Classification scores for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_base_priors * num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_base_priors * 4.\n        \"\"\"\n\n        cls_scores = []\n        bbox_preds = []\n        for idx, (x, scale, stride) in enumerate(\n                zip(feats, self.scales, self.prior_generator.strides)):\n            cls_feat = x\n            reg_feat = x\n\n            for cls_layer in self.cls_convs:\n                cls_feat = cls_layer(cls_feat)\n            cls_score = self.rtm_cls(cls_feat)\n\n            for reg_layer in self.reg_convs:\n                reg_feat = reg_layer(reg_feat)\n\n            if self.with_objectness:\n                objectness = self.rtm_obj(reg_feat)\n                cls_score = inverse_sigmoid(\n                    sigmoid_geometric_mean(cls_score, objectness))\n\n            reg_dist = scale(self.rtm_reg(reg_feat).exp()).float() * stride[0]\n\n            cls_scores.append(cls_score)\n            bbox_preds.append(reg_dist)\n        return tuple(cls_scores), tuple(bbox_preds)\n\n    def loss_by_feat_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            labels: Tensor, label_weights: Tensor,\n                            bbox_targets: Tensor, assign_metrics: Tensor,\n                            stride: List[int]):\n        \"\"\"Compute loss of a single scale level.\n\n        Args:\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W).\n            bbox_pred (Tensor): Decoded bboxes for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors).\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (N, num_total_anchors, 4).\n            assign_metrics (Tensor): Assign metrics with shape\n                (N, num_total_anchors).\n            stride (List[int]): Downsample stride of the feature map.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert stride[0] == stride[1], 'h stride is not equal to w stride!'\n        cls_score = cls_score.permute(0, 2, 3, 1).reshape(\n            -1, self.cls_out_channels).contiguous()\n        bbox_pred = bbox_pred.reshape(-1, 4)\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        assign_metrics = assign_metrics.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        targets = (labels, assign_metrics)\n\n        loss_cls = self.loss_cls(\n            cls_score, targets, label_weights, avg_factor=1.0)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().squeeze(1)\n\n        if len(pos_inds) > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n\n            pos_decode_bbox_pred = pos_bbox_pred\n            pos_decode_bbox_targets = pos_bbox_targets\n\n            # regression loss\n            pos_bbox_weight = assign_metrics[pos_inds]\n\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_decode_bbox_targets,\n                weight=pos_bbox_weight,\n                avg_factor=1.0)\n        else:\n            loss_bbox = bbox_pred.sum() * 0\n            pos_bbox_weight = bbox_targets.new_tensor(0.)\n\n        return loss_cls, loss_bbox, assign_metrics.sum(), pos_bbox_weight.sum()\n\n    def loss_by_feat(self,\n                     cls_scores: List[Tensor],\n                     bbox_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict],\n                     batch_gt_instances_ignore: OptInstanceList = None):\n        \"\"\"Compute losses of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Decoded box for each scale\n                level with shape (N, num_anchors * 4, H, W) in\n                [tl_x, tl_y, br_x, br_y] format.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        flatten_cls_scores = torch.cat([\n            cls_score.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                  self.cls_out_channels)\n            for cls_score in cls_scores\n        ], 1)\n        decoded_bboxes = []\n        for anchor, bbox_pred in zip(anchor_list[0], bbox_preds):\n            anchor = anchor.reshape(-1, 4)\n            bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1, 4)\n            bbox_pred = distance2bbox(anchor, bbox_pred)\n            decoded_bboxes.append(bbox_pred)\n\n        flatten_bboxes = torch.cat(decoded_bboxes, 1)\n\n        cls_reg_targets = self.get_targets(\n            flatten_cls_scores,\n            flatten_bboxes,\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         assign_metrics_list, sampling_results_list) = cls_reg_targets\n\n        losses_cls, losses_bbox,\\\n            cls_avg_factors, bbox_avg_factors = multi_apply(\n                self.loss_by_feat_single,\n                cls_scores,\n                decoded_bboxes,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                assign_metrics_list,\n                self.prior_generator.strides)\n\n        cls_avg_factor = reduce_mean(sum(cls_avg_factors)).clamp_(min=1).item()\n        losses_cls = list(map(lambda x: x / cls_avg_factor, losses_cls))\n\n        bbox_avg_factor = reduce_mean(\n            sum(bbox_avg_factors)).clamp_(min=1).item()\n        losses_bbox = list(map(lambda x: x / bbox_avg_factor, losses_bbox))\n        return dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n\n    def get_targets(self,\n                    cls_scores: Tensor,\n                    bbox_preds: Tensor,\n                    anchor_list: List[List[Tensor]],\n                    valid_flag_list: List[List[Tensor]],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs=True):\n        \"\"\"Compute regression and classification targets for anchors in\n        multiple images.\n\n        Args:\n            cls_scores (Tensor): Classification predictions of images,\n                a 3D-Tensor with shape [num_imgs, num_priors, num_classes].\n            bbox_preds (Tensor): Decoded bboxes predictions of one image,\n                a 3D-Tensor with shape [num_imgs, num_priors, 4] in [tl_x,\n                tl_y, br_x, br_y] format.\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, 4).\n            valid_flag_list (list[list[Tensor]]): Multi level valid flags of\n                each image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, )\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors. Defaults to True.\n\n        Returns:\n            tuple: a tuple containing learning targets.\n\n            - anchors_list (list[list[Tensor]]): Anchors of each level.\n            - labels_list (list[Tensor]): Labels of each level.\n            - label_weights_list (list[Tensor]): Label weights of each\n              level.\n            - bbox_targets_list (list[Tensor]): BBox targets of each level.\n            - assign_metrics_list (list[Tensor]): alignment metrics of each\n              level.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n\n        # concat all level anchors and flags to a single tensor\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            anchor_list[i] = torch.cat(anchor_list[i])\n            valid_flag_list[i] = torch.cat(valid_flag_list[i])\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        # anchor_list: list(b * [-1, 4])\n        (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n         all_assign_metrics, sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             cls_scores.detach(),\n             bbox_preds.detach(),\n             anchor_list,\n             valid_flag_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             unmap_outputs=unmap_outputs)\n        # no valid anchors\n        if any([labels is None for labels in all_labels]):\n            return None\n\n        # split targets to a list w.r.t. multiple levels\n        anchors_list = images_to_levels(all_anchors, num_level_anchors)\n        labels_list = images_to_levels(all_labels, num_level_anchors)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors)\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors)\n        assign_metrics_list = images_to_levels(all_assign_metrics,\n                                               num_level_anchors)\n\n        return (anchors_list, labels_list, label_weights_list,\n                bbox_targets_list, assign_metrics_list, sampling_results_list)\n\n    def _get_targets_single(self,\n                            cls_scores: Tensor,\n                            bbox_preds: Tensor,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs=True):\n        \"\"\"Compute regression, classification targets for anchors in a single\n        image.\n\n        Args:\n            cls_scores (list(Tensor)): Box scores for each image.\n            bbox_preds (list(Tensor)): Box energies / deltas for each image.\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors ,4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors,).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors. Defaults to True.\n\n        Returns:\n            tuple: N is the number of total anchors in the image.\n\n            - anchors (Tensor): All anchors in the image with shape (N, 4).\n            - labels (Tensor): Labels of all anchors in the image with shape\n              (N,).\n            - label_weights (Tensor): Label weights of all anchor in the\n              image with shape (N,).\n            - bbox_targets (Tensor): BBox targets of all anchors in the\n              image with shape (N, 4).\n            - norm_alignment_metrics (Tensor): Normalized alignment metrics\n              of all priors in the image with shape (N,).\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            return (None, ) * 7\n        # assign gt and sample anchors\n        anchors = flat_anchors[inside_flags, :]\n\n        pred_instances = InstanceData(\n            scores=cls_scores[inside_flags, :],\n            bboxes=bbox_preds[inside_flags, :],\n            priors=anchors)\n\n        assign_result = self.assigner.assign(pred_instances, gt_instances,\n                                             gt_instances_ignore)\n\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        bbox_targets = torch.zeros_like(anchors)\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n        assign_metrics = anchors.new_zeros(\n            num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            # point-based\n            pos_bbox_targets = sampling_result.pos_gt_bboxes\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        class_assigned_gt_inds = torch.unique(\n            sampling_result.pos_assigned_gt_inds)\n        for gt_inds in class_assigned_gt_inds:\n            gt_class_inds = pos_inds[sampling_result.pos_assigned_gt_inds ==\n                                     gt_inds]\n            assign_metrics[gt_class_inds] = assign_result.max_overlaps[\n                gt_class_inds]\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            anchors = unmap(anchors, num_total_anchors, inside_flags)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags, fill=self.num_classes)\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags)\n            assign_metrics = unmap(assign_metrics, num_total_anchors,\n                                   inside_flags)\n        return (anchors, labels, label_weights, bbox_targets, assign_metrics,\n                sampling_result)\n\n    def get_anchors(self,\n                    featmap_sizes: List[tuple],\n                    batch_img_metas: List[dict],\n                    device: Union[torch.device, str] = 'cuda') \\\n            -> Tuple[List[List[Tensor]], List[List[Tensor]]]:\n        \"\"\"Get anchors according to feature map sizes.\n\n        Args:\n            featmap_sizes (list[tuple]): Multi-level feature map sizes.\n            batch_img_metas (list[dict]): Image meta info.\n            device (torch.device or str): Device for returned tensors.\n                Defaults to cuda.\n\n        Returns:\n            tuple:\n\n            - anchor_list (list[list[Tensor]]): Anchors of each image.\n            - valid_flag_list (list[list[Tensor]]): Valid flags of each\n              image.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n\n        # since feature map sizes of all images are the same, we only compute\n        # anchors for one time\n        multi_level_anchors = self.prior_generator.grid_priors(\n            featmap_sizes, device=device, with_stride=True)\n        anchor_list = [multi_level_anchors for _ in range(num_imgs)]\n\n        # for each image, we compute valid flags of multi level anchors\n        valid_flag_list = []\n        for img_id, img_meta in enumerate(batch_img_metas):\n            multi_level_flags = self.prior_generator.valid_flags(\n                featmap_sizes, img_meta['pad_shape'], device)\n            valid_flag_list.append(multi_level_flags)\n        return anchor_list, valid_flag_list\n\n\n@MODELS.register_module()\nclass RTMDetSepBNHead(RTMDetHead):\n    \"\"\"RTMDetHead with separated BN layers and shared conv layers.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        share_conv (bool): Whether to share conv layers between stages.\n            Defaults to True.\n        use_depthwise (bool): Whether to use depthwise separable convolution in\n            head. Defaults to False.\n        norm_cfg (:obj:`ConfigDict` or dict)): Config dict for normalization\n            layer. Defaults to dict(type='BN', momentum=0.03, eps=0.001).\n        act_cfg (:obj:`ConfigDict` or dict)): Config dict for activation layer.\n            Defaults to dict(type='SiLU').\n        pred_kernel_size (int): Kernel size of prediction layer. Defaults to 1.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 share_conv: bool = True,\n                 use_depthwise: bool = False,\n                 norm_cfg: ConfigType = dict(\n                     type='BN', momentum=0.03, eps=0.001),\n                 act_cfg: ConfigType = dict(type='SiLU'),\n                 pred_kernel_size: int = 1,\n                 exp_on_reg=False,\n                 **kwargs) -> None:\n        self.share_conv = share_conv\n        self.exp_on_reg = exp_on_reg\n        self.use_depthwise = use_depthwise\n        super().__init__(\n            num_classes,\n            in_channels,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg,\n            pred_kernel_size=pred_kernel_size,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        conv = DepthwiseSeparableConvModule \\\n            if self.use_depthwise else ConvModule\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n\n        self.rtm_cls = nn.ModuleList()\n        self.rtm_reg = nn.ModuleList()\n        if self.with_objectness:\n            self.rtm_obj = nn.ModuleList()\n        for n in range(len(self.prior_generator.strides)):\n            cls_convs = nn.ModuleList()\n            reg_convs = nn.ModuleList()\n            for i in range(self.stacked_convs):\n                chn = self.in_channels if i == 0 else self.feat_channels\n                cls_convs.append(\n                    conv(\n                        chn,\n                        self.feat_channels,\n                        3,\n                        stride=1,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n                reg_convs.append(\n                    conv(\n                        chn,\n                        self.feat_channels,\n                        3,\n                        stride=1,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n            self.cls_convs.append(cls_convs)\n            self.reg_convs.append(reg_convs)\n\n            self.rtm_cls.append(\n                nn.Conv2d(\n                    self.feat_channels,\n                    self.num_base_priors * self.cls_out_channels,\n                    self.pred_kernel_size,\n                    padding=self.pred_kernel_size // 2))\n            self.rtm_reg.append(\n                nn.Conv2d(\n                    self.feat_channels,\n                    self.num_base_priors * 4,\n                    self.pred_kernel_size,\n                    padding=self.pred_kernel_size // 2))\n            if self.with_objectness:\n                self.rtm_obj.append(\n                    nn.Conv2d(\n                        self.feat_channels,\n                        1,\n                        self.pred_kernel_size,\n                        padding=self.pred_kernel_size // 2))\n\n        if self.share_conv:\n            for n in range(len(self.prior_generator.strides)):\n                for i in range(self.stacked_convs):\n                    self.cls_convs[n][i].conv = self.cls_convs[0][i].conv\n                    self.reg_convs[n][i].conv = self.reg_convs[0][i].conv\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, mean=0, std=0.01)\n            if is_norm(m):\n                constant_init(m, 1)\n        bias_cls = bias_init_with_prob(0.01)\n        for rtm_cls, rtm_reg in zip(self.rtm_cls, self.rtm_reg):\n            normal_init(rtm_cls, std=0.01, bias=bias_cls)\n            normal_init(rtm_reg, std=0.01)\n        if self.with_objectness:\n            for rtm_obj in self.rtm_obj:\n                normal_init(rtm_obj, std=0.01, bias=bias_cls)\n\n    def forward(self, feats: Tuple[Tensor, ...]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n\n            - cls_scores (tuple[Tensor]): Classification scores for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_anchors * num_classes.\n            - bbox_preds (tuple[Tensor]): Box energies / deltas for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_anchors * 4.\n        \"\"\"\n\n        cls_scores = []\n        bbox_preds = []\n        for idx, (x, stride) in enumerate(\n                zip(feats, self.prior_generator.strides)):\n            cls_feat = x\n            reg_feat = x\n\n            for cls_layer in self.cls_convs[idx]:\n                cls_feat = cls_layer(cls_feat)\n            cls_score = self.rtm_cls[idx](cls_feat)\n\n            for reg_layer in self.reg_convs[idx]:\n                reg_feat = reg_layer(reg_feat)\n\n            if self.with_objectness:\n                objectness = self.rtm_obj[idx](reg_feat)\n                cls_score = inverse_sigmoid(\n                    sigmoid_geometric_mean(cls_score, objectness))\n            if self.exp_on_reg:\n                reg_dist = self.rtm_reg[idx](reg_feat).exp() * stride[0]\n            else:\n                reg_dist = self.rtm_reg[idx](reg_feat) * stride[0]\n            cls_scores.append(cls_score)\n            bbox_preds.append(reg_dist)\n        return tuple(cls_scores), tuple(bbox_preds)\n"
  },
  {
    "path": "mmdet/models/dense_heads/rtmdet_ins_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport math\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, is_norm\nfrom mmcv.ops import batched_nms\nfrom mmengine.model import (BaseModule, bias_init_with_prob, constant_init,\n                            normal_init)\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.layers.transformer import inverse_sigmoid\nfrom mmdet.models.utils import (filter_scores_and_topk, multi_apply,\n                                select_single_mlvl, sigmoid_geometric_mean)\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import (cat_boxes, distance2bbox, get_box_tensor,\n                                   get_box_wh, scale_boxes)\nfrom mmdet.utils import ConfigType, InstanceList, OptInstanceList, reduce_mean\nfrom .rtmdet_head import RTMDetHead\n\n\n@MODELS.register_module()\nclass RTMDetInsHead(RTMDetHead):\n    \"\"\"Detection Head of RTMDet-Ins.\n\n    Args:\n        num_prototypes (int): Number of mask prototype features extracted\n            from the mask head. Defaults to 8.\n        dyconv_channels (int): Channel of the dynamic conv layers.\n            Defaults to 8.\n        num_dyconvs (int): Number of the dynamic convolution layers.\n            Defaults to 3.\n        mask_loss_stride (int): Down sample stride of the masks for loss\n            computation. Defaults to 4.\n        loss_mask (:obj:`ConfigDict` or dict): Config dict for mask loss.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 num_prototypes: int = 8,\n                 dyconv_channels: int = 8,\n                 num_dyconvs: int = 3,\n                 mask_loss_stride: int = 4,\n                 loss_mask=dict(\n                     type='DiceLoss',\n                     loss_weight=2.0,\n                     eps=5e-6,\n                     reduction='mean'),\n                 **kwargs) -> None:\n        self.num_prototypes = num_prototypes\n        self.num_dyconvs = num_dyconvs\n        self.dyconv_channels = dyconv_channels\n        self.mask_loss_stride = mask_loss_stride\n        super().__init__(*args, **kwargs)\n        self.loss_mask = MODELS.build(loss_mask)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        super()._init_layers()\n        # a branch to predict kernels of dynamic convs\n        self.kernel_convs = nn.ModuleList()\n        # calculate num dynamic parameters\n        weight_nums, bias_nums = [], []\n        for i in range(self.num_dyconvs):\n            if i == 0:\n                weight_nums.append(\n                    # mask prototype and coordinate features\n                    (self.num_prototypes + 2) * self.dyconv_channels)\n                bias_nums.append(self.dyconv_channels * 1)\n            elif i == self.num_dyconvs - 1:\n                weight_nums.append(self.dyconv_channels * 1)\n                bias_nums.append(1)\n            else:\n                weight_nums.append(self.dyconv_channels * self.dyconv_channels)\n                bias_nums.append(self.dyconv_channels * 1)\n        self.weight_nums = weight_nums\n        self.bias_nums = bias_nums\n        self.num_gen_params = sum(weight_nums) + sum(bias_nums)\n\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.kernel_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    act_cfg=self.act_cfg))\n        pred_pad_size = self.pred_kernel_size // 2\n        self.rtm_kernel = nn.Conv2d(\n            self.feat_channels,\n            self.num_gen_params,\n            self.pred_kernel_size,\n            padding=pred_pad_size)\n        self.mask_head = MaskFeatModule(\n            in_channels=self.in_channels,\n            feat_channels=self.feat_channels,\n            stacked_convs=4,\n            num_levels=len(self.prior_generator.strides),\n            num_prototypes=self.num_prototypes,\n            act_cfg=self.act_cfg,\n            norm_cfg=self.norm_cfg)\n\n    def forward(self, feats: Tuple[Tensor, ...]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n            - cls_scores (list[Tensor]): Classification scores for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_base_priors * num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_base_priors * 4.\n            - kernel_preds (list[Tensor]): Dynamic conv kernels for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_gen_params.\n            - mask_feat (Tensor): Output feature of the mask head. Each is a\n              4D-tensor, the channels number is num_prototypes.\n        \"\"\"\n        mask_feat = self.mask_head(feats)\n\n        cls_scores = []\n        bbox_preds = []\n        kernel_preds = []\n        for idx, (x, scale, stride) in enumerate(\n                zip(feats, self.scales, self.prior_generator.strides)):\n            cls_feat = x\n            reg_feat = x\n            kernel_feat = x\n\n            for cls_layer in self.cls_convs:\n                cls_feat = cls_layer(cls_feat)\n            cls_score = self.rtm_cls(cls_feat)\n\n            for kernel_layer in self.kernel_convs:\n                kernel_feat = kernel_layer(kernel_feat)\n            kernel_pred = self.rtm_kernel(kernel_feat)\n\n            for reg_layer in self.reg_convs:\n                reg_feat = reg_layer(reg_feat)\n\n            if self.with_objectness:\n                objectness = self.rtm_obj(reg_feat)\n                cls_score = inverse_sigmoid(\n                    sigmoid_geometric_mean(cls_score, objectness))\n\n            reg_dist = scale(self.rtm_reg(reg_feat)) * stride[0]\n\n            cls_scores.append(cls_score)\n            bbox_preds.append(reg_dist)\n            kernel_preds.append(kernel_pred)\n        return tuple(cls_scores), tuple(bbox_preds), tuple(\n            kernel_preds), mask_feat\n\n    def predict_by_feat(self,\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        kernel_preds: List[Tensor],\n                        mask_feat: Tensor,\n                        score_factors: Optional[List[Tensor]] = None,\n                        batch_img_metas: Optional[List[dict]] = None,\n                        cfg: Optional[ConfigType] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Note: When score_factors is not None, the cls_scores are\n        usually multiplied by it then obtain the real score used in NMS,\n        such as CenterNess in FCOS, IoU branch in ATSS.\n\n        Args:\n            cls_scores (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            kernel_preds (list[Tensor]): Kernel predictions of dynamic\n                convs for all scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_params, H, W).\n            mask_feat (Tensor): Mask prototype features extracted from the\n                mask head, has shape (batch_size, num_prototypes, H, W).\n            score_factors (list[Tensor], optional): Score factor for\n                all scale level, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 1, H, W). Defaults to None.\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n                Defaults to None.\n            cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, h, w).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n\n        if score_factors is None:\n            # e.g. Retina, FreeAnchor, Foveabox, etc.\n            with_score_factors = False\n        else:\n            # e.g. FCOS, PAA, ATSS, AutoAssign, etc.\n            with_score_factors = True\n            assert len(cls_scores) == len(score_factors)\n\n        num_levels = len(cls_scores)\n\n        featmap_sizes = [cls_scores[i].shape[-2:] for i in range(num_levels)]\n        mlvl_priors = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=cls_scores[0].dtype,\n            device=cls_scores[0].device,\n            with_stride=True)\n\n        result_list = []\n\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            cls_score_list = select_single_mlvl(\n                cls_scores, img_id, detach=True)\n            bbox_pred_list = select_single_mlvl(\n                bbox_preds, img_id, detach=True)\n            kernel_pred_list = select_single_mlvl(\n                kernel_preds, img_id, detach=True)\n            if with_score_factors:\n                score_factor_list = select_single_mlvl(\n                    score_factors, img_id, detach=True)\n            else:\n                score_factor_list = [None for _ in range(num_levels)]\n\n            results = self._predict_by_feat_single(\n                cls_score_list=cls_score_list,\n                bbox_pred_list=bbox_pred_list,\n                kernel_pred_list=kernel_pred_list,\n                mask_feat=mask_feat[img_id],\n                score_factor_list=score_factor_list,\n                mlvl_priors=mlvl_priors,\n                img_meta=img_meta,\n                cfg=cfg,\n                rescale=rescale,\n                with_nms=with_nms)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                kernel_pred_list: List[Tensor],\n                                mask_feat: Tensor,\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigType,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox and mask results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            kernel_preds (list[Tensor]): Kernel predictions of dynamic\n                convs for all scale levels of a single image, each is a\n                4D-tensor, has shape (num_params, H, W).\n            mask_feat (Tensor): Mask prototype features of a single image\n                extracted from the mask head, has shape (num_prototypes, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid. In all\n                anchor-based methods, it has shape (num_priors, 4). In\n                all anchor-free methods, it has shape (num_priors, 2)\n                when `with_stride=True`, otherwise it still has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, h, w).\n        \"\"\"\n        if score_factor_list[0] is None:\n            # e.g. Retina, FreeAnchor, etc.\n            with_score_factors = False\n        else:\n            # e.g. FCOS, PAA, ATSS, etc.\n            with_score_factors = True\n\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bbox_preds = []\n        mlvl_kernels = []\n        mlvl_valid_priors = []\n        mlvl_scores = []\n        mlvl_labels = []\n        if with_score_factors:\n            mlvl_score_factors = []\n        else:\n            mlvl_score_factors = None\n\n        for level_idx, (cls_score, bbox_pred, kernel_pred,\n                        score_factor, priors) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list, kernel_pred_list,\n                              score_factor_list, mlvl_priors)):\n\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            dim = self.bbox_coder.encode_size\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, dim)\n            if with_score_factors:\n                score_factor = score_factor.permute(1, 2,\n                                                    0).reshape(-1).sigmoid()\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            kernel_pred = kernel_pred.permute(1, 2, 0).reshape(\n                -1, self.num_gen_params)\n            if self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                # remind that we set FG labels to [0, num_class-1]\n                # since mmdet v2.0\n                # BG cat_id: num_class\n                scores = cls_score.softmax(-1)[:, :-1]\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            score_thr = cfg.get('score_thr', 0)\n\n            results = filter_scores_and_topk(\n                scores, score_thr, nms_pre,\n                dict(\n                    bbox_pred=bbox_pred,\n                    priors=priors,\n                    kernel_pred=kernel_pred))\n            scores, labels, keep_idxs, filtered_results = results\n\n            bbox_pred = filtered_results['bbox_pred']\n            priors = filtered_results['priors']\n            kernel_pred = filtered_results['kernel_pred']\n\n            if with_score_factors:\n                score_factor = score_factor[keep_idxs]\n\n            mlvl_bbox_preds.append(bbox_pred)\n            mlvl_valid_priors.append(priors)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n            mlvl_kernels.append(kernel_pred)\n\n            if with_score_factors:\n                mlvl_score_factors.append(score_factor)\n\n        bbox_pred = torch.cat(mlvl_bbox_preds)\n        priors = cat_boxes(mlvl_valid_priors)\n        bboxes = self.bbox_coder.decode(\n            priors[..., :2], bbox_pred, max_shape=img_shape)\n\n        results = InstanceData()\n        results.bboxes = bboxes\n        results.priors = priors\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n        results.kernels = torch.cat(mlvl_kernels)\n        if with_score_factors:\n            results.score_factors = torch.cat(mlvl_score_factors)\n\n        return self._bbox_mask_post_process(\n            results=results,\n            mask_feat=mask_feat,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n    def _bbox_mask_post_process(\n            self,\n            results: InstanceData,\n            mask_feat,\n            cfg: ConfigType,\n            rescale: bool = False,\n            with_nms: bool = True,\n            img_meta: Optional[dict] = None) -> InstanceData:\n        \"\"\"bbox and mask post-processing method.\n\n        The boxes would be rescaled to the original image scale and do\n        the nms operation. Usually `with_nms` is False is used for aug test.\n\n        Args:\n            results (:obj:`InstaceData`): Detection instance results,\n                each item has shape (num_bboxes, ).\n            cfg (ConfigDict): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Default to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Default to True.\n            img_meta (dict, optional): Image meta info. Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, h, w).\n        \"\"\"\n        stride = self.prior_generator.strides[0][0]\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = [1 / s for s in img_meta['scale_factor']]\n            results.bboxes = scale_boxes(results.bboxes, scale_factor)\n\n        if hasattr(results, 'score_factors'):\n            # TODO: Add sqrt operation in order to be consistent with\n            #  the paper.\n            score_factors = results.pop('score_factors')\n            results.scores = results.scores * score_factors\n\n        # filter small size bboxes\n        if cfg.get('min_bbox_size', -1) >= 0:\n            w, h = get_box_wh(results.bboxes)\n            valid_mask = (w > cfg.min_bbox_size) & (h > cfg.min_bbox_size)\n            if not valid_mask.all():\n                results = results[valid_mask]\n\n        # TODO: deal with `with_nms` and `nms_cfg=None` in test_cfg\n        assert with_nms, 'with_nms must be True for RTMDet-Ins'\n        if results.bboxes.numel() > 0:\n            bboxes = get_box_tensor(results.bboxes)\n            det_bboxes, keep_idxs = batched_nms(bboxes, results.scores,\n                                                results.labels, cfg.nms)\n            results = results[keep_idxs]\n            # some nms would reweight the score, such as softnms\n            results.scores = det_bboxes[:, -1]\n            results = results[:cfg.max_per_img]\n\n            # process masks\n            mask_logits = self._mask_predict_by_feat_single(\n                mask_feat, results.kernels, results.priors)\n\n            mask_logits = F.interpolate(\n                mask_logits.unsqueeze(0), scale_factor=stride, mode='bilinear')\n            if rescale:\n                ori_h, ori_w = img_meta['ori_shape'][:2]\n                mask_logits = F.interpolate(\n                    mask_logits,\n                    size=[\n                        math.ceil(mask_logits.shape[-2] * scale_factor[0]),\n                        math.ceil(mask_logits.shape[-1] * scale_factor[1])\n                    ],\n                    mode='bilinear',\n                    align_corners=False)[..., :ori_h, :ori_w]\n            masks = mask_logits.sigmoid().squeeze(0)\n            masks = masks > cfg.mask_thr_binary\n            results.masks = masks\n        else:\n            h, w = img_meta['ori_shape'][:2] if rescale else img_meta[\n                'img_shape'][:2]\n            results.masks = torch.zeros(\n                size=(results.bboxes.shape[0], h, w),\n                dtype=torch.bool,\n                device=results.bboxes.device)\n\n        return results\n\n    def parse_dynamic_params(self, flatten_kernels: Tensor) -> tuple:\n        \"\"\"split kernel head prediction to conv weight and bias.\"\"\"\n        n_inst = flatten_kernels.size(0)\n        n_layers = len(self.weight_nums)\n        params_splits = list(\n            torch.split_with_sizes(\n                flatten_kernels, self.weight_nums + self.bias_nums, dim=1))\n        weight_splits = params_splits[:n_layers]\n        bias_splits = params_splits[n_layers:]\n        for i in range(n_layers):\n            if i < n_layers - 1:\n                weight_splits[i] = weight_splits[i].reshape(\n                    n_inst * self.dyconv_channels, -1, 1, 1)\n                bias_splits[i] = bias_splits[i].reshape(n_inst *\n                                                        self.dyconv_channels)\n            else:\n                weight_splits[i] = weight_splits[i].reshape(n_inst, -1, 1, 1)\n                bias_splits[i] = bias_splits[i].reshape(n_inst)\n\n        return weight_splits, bias_splits\n\n    def _mask_predict_by_feat_single(self, mask_feat: Tensor, kernels: Tensor,\n                                     priors: Tensor) -> Tensor:\n        \"\"\"Generate mask logits from mask features with dynamic convs.\n\n        Args:\n            mask_feat (Tensor): Mask prototype features.\n                Has shape (num_prototypes, H, W).\n            kernels (Tensor): Kernel parameters for each instance.\n                Has shape (num_instance, num_params)\n            priors (Tensor): Center priors for each instance.\n                Has shape (num_instance, 4).\n        Returns:\n            Tensor: Instance segmentation masks for each instance.\n                Has shape (num_instance, H, W).\n        \"\"\"\n        num_inst = priors.shape[0]\n        h, w = mask_feat.size()[-2:]\n        if num_inst < 1:\n            return torch.empty(\n                size=(num_inst, h, w),\n                dtype=mask_feat.dtype,\n                device=mask_feat.device)\n        if len(mask_feat.shape) < 4:\n            mask_feat.unsqueeze(0)\n\n        coord = self.prior_generator.single_level_grid_priors(\n            (h, w), level_idx=0, device=mask_feat.device).reshape(1, -1, 2)\n        num_inst = priors.shape[0]\n        points = priors[:, :2].reshape(-1, 1, 2)\n        strides = priors[:, 2:].reshape(-1, 1, 2)\n        relative_coord = (points - coord).permute(0, 2, 1) / (\n            strides[..., 0].reshape(-1, 1, 1) * 8)\n        relative_coord = relative_coord.reshape(num_inst, 2, h, w)\n\n        mask_feat = torch.cat(\n            [relative_coord,\n             mask_feat.repeat(num_inst, 1, 1, 1)], dim=1)\n        weights, biases = self.parse_dynamic_params(kernels)\n\n        n_layers = len(weights)\n        x = mask_feat.reshape(1, -1, h, w)\n        for i, (weight, bias) in enumerate(zip(weights, biases)):\n            x = F.conv2d(\n                x, weight, bias=bias, stride=1, padding=0, groups=num_inst)\n            if i < n_layers - 1:\n                x = F.relu(x)\n        x = x.reshape(num_inst, h, w)\n        return x\n\n    def loss_mask_by_feat(self, mask_feats: Tensor, flatten_kernels: Tensor,\n                          sampling_results_list: list,\n                          batch_gt_instances: InstanceList) -> Tensor:\n        \"\"\"Compute instance segmentation loss.\n\n        Args:\n            mask_feats (list[Tensor]): Mask prototype features extracted from\n                the mask head. Has shape (N, num_prototypes, H, W)\n            flatten_kernels (list[Tensor]): Kernels of the dynamic conv layers.\n                Has shape (N, num_instances, num_params)\n            sampling_results_list (list[:obj:`SamplingResults`]) Batch of\n                assignment results.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            Tensor: The mask loss tensor.\n        \"\"\"\n        batch_pos_mask_logits = []\n        pos_gt_masks = []\n        for idx, (mask_feat, kernels, sampling_results,\n                  gt_instances) in enumerate(\n                      zip(mask_feats, flatten_kernels, sampling_results_list,\n                          batch_gt_instances)):\n            pos_priors = sampling_results.pos_priors\n            pos_inds = sampling_results.pos_inds\n            pos_kernels = kernels[pos_inds]  # n_pos, num_gen_params\n            pos_mask_logits = self._mask_predict_by_feat_single(\n                mask_feat, pos_kernels, pos_priors)\n            if gt_instances.masks.numel() == 0:\n                gt_masks = torch.empty_like(gt_instances.masks)\n            else:\n                gt_masks = gt_instances.masks[\n                    sampling_results.pos_assigned_gt_inds, :]\n            batch_pos_mask_logits.append(pos_mask_logits)\n            pos_gt_masks.append(gt_masks)\n\n        pos_gt_masks = torch.cat(pos_gt_masks, 0)\n        batch_pos_mask_logits = torch.cat(batch_pos_mask_logits, 0)\n\n        # avg_factor\n        num_pos = batch_pos_mask_logits.shape[0]\n        num_pos = reduce_mean(mask_feats.new_tensor([num_pos\n                                                     ])).clamp_(min=1).item()\n\n        if batch_pos_mask_logits.shape[0] == 0:\n            return mask_feats.sum() * 0\n\n        scale = self.prior_generator.strides[0][0] // self.mask_loss_stride\n        # upsample pred masks\n        batch_pos_mask_logits = F.interpolate(\n            batch_pos_mask_logits.unsqueeze(0),\n            scale_factor=scale,\n            mode='bilinear',\n            align_corners=False).squeeze(0)\n        # downsample gt masks\n        pos_gt_masks = pos_gt_masks[:, self.mask_loss_stride //\n                                    2::self.mask_loss_stride,\n                                    self.mask_loss_stride //\n                                    2::self.mask_loss_stride]\n\n        loss_mask = self.loss_mask(\n            batch_pos_mask_logits,\n            pos_gt_masks,\n            weight=None,\n            avg_factor=num_pos)\n\n        return loss_mask\n\n    def loss_by_feat(self,\n                     cls_scores: List[Tensor],\n                     bbox_preds: List[Tensor],\n                     kernel_preds: List[Tensor],\n                     mask_feat: Tensor,\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict],\n                     batch_gt_instances_ignore: OptInstanceList = None):\n        \"\"\"Compute losses of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Decoded box for each scale\n                level with shape (N, num_anchors * 4, H, W) in\n                [tl_x, tl_y, br_x, br_y] format.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        flatten_cls_scores = torch.cat([\n            cls_score.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                  self.cls_out_channels)\n            for cls_score in cls_scores\n        ], 1)\n        flatten_kernels = torch.cat([\n            kernel_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                    self.num_gen_params)\n            for kernel_pred in kernel_preds\n        ], 1)\n        decoded_bboxes = []\n        for anchor, bbox_pred in zip(anchor_list[0], bbox_preds):\n            anchor = anchor.reshape(-1, 4)\n            bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1, 4)\n            bbox_pred = distance2bbox(anchor, bbox_pred)\n            decoded_bboxes.append(bbox_pred)\n\n        flatten_bboxes = torch.cat(decoded_bboxes, 1)\n        for gt_instances in batch_gt_instances:\n            gt_instances.masks = gt_instances.masks.to_tensor(\n                dtype=torch.bool, device=device)\n\n        cls_reg_targets = self.get_targets(\n            flatten_cls_scores,\n            flatten_bboxes,\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         assign_metrics_list, sampling_results_list) = cls_reg_targets\n\n        losses_cls, losses_bbox,\\\n            cls_avg_factors, bbox_avg_factors = multi_apply(\n                self.loss_by_feat_single,\n                cls_scores,\n                decoded_bboxes,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                assign_metrics_list,\n                self.prior_generator.strides)\n\n        cls_avg_factor = reduce_mean(sum(cls_avg_factors)).clamp_(min=1).item()\n        losses_cls = list(map(lambda x: x / cls_avg_factor, losses_cls))\n\n        bbox_avg_factor = reduce_mean(\n            sum(bbox_avg_factors)).clamp_(min=1).item()\n        losses_bbox = list(map(lambda x: x / bbox_avg_factor, losses_bbox))\n\n        loss_mask = self.loss_mask_by_feat(mask_feat, flatten_kernels,\n                                           sampling_results_list,\n                                           batch_gt_instances)\n        loss = dict(\n            loss_cls=losses_cls, loss_bbox=losses_bbox, loss_mask=loss_mask)\n        return loss\n\n\nclass MaskFeatModule(BaseModule):\n    \"\"\"Mask feature head used in RTMDet-Ins.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels of the mask feature\n             map branch.\n        num_levels (int): The starting feature map level from RPN that\n             will be used to predict the mask feature map.\n        num_prototypes (int): Number of output channel of the mask feature\n             map branch. This is the channel count of the mask\n             feature map that to be dynamically convolved with the predicted\n             kernel.\n        stacked_convs (int): Number of convs in mask feature branch.\n        act_cfg (:obj:`ConfigDict` or dict): Config dict for activation layer.\n            Default: dict(type='ReLU', inplace=True)\n        norm_cfg (dict): Config dict for normalization layer. Default: None.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: int,\n        feat_channels: int = 256,\n        stacked_convs: int = 4,\n        num_levels: int = 3,\n        num_prototypes: int = 8,\n        act_cfg: ConfigType = dict(type='ReLU', inplace=True),\n        norm_cfg: ConfigType = dict(type='BN')\n    ) -> None:\n        super().__init__(init_cfg=None)\n        self.num_levels = num_levels\n        self.fusion_conv = nn.Conv2d(num_levels * in_channels, in_channels, 1)\n        convs = []\n        for i in range(stacked_convs):\n            in_c = in_channels if i == 0 else feat_channels\n            convs.append(\n                ConvModule(\n                    in_c,\n                    feat_channels,\n                    3,\n                    padding=1,\n                    act_cfg=act_cfg,\n                    norm_cfg=norm_cfg))\n        self.stacked_convs = nn.Sequential(*convs)\n        self.projection = nn.Conv2d(\n            feat_channels, num_prototypes, kernel_size=1)\n\n    def forward(self, features: Tuple[Tensor, ...]) -> Tensor:\n        # multi-level feature fusion\n        fusion_feats = [features[0]]\n        size = features[0].shape[-2:]\n        for i in range(1, self.num_levels):\n            f = F.interpolate(features[i], size=size, mode='bilinear')\n            fusion_feats.append(f)\n        fusion_feats = torch.cat(fusion_feats, dim=1)\n        fusion_feats = self.fusion_conv(fusion_feats)\n        # pred mask feats\n        mask_features = self.stacked_convs(fusion_feats)\n        mask_features = self.projection(mask_features)\n        return mask_features\n\n\n@MODELS.register_module()\nclass RTMDetInsSepBNHead(RTMDetInsHead):\n    \"\"\"Detection Head of RTMDet-Ins with sep-bn layers.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        share_conv (bool): Whether to share conv layers between stages.\n            Defaults to True.\n        norm_cfg (:obj:`ConfigDict` or dict)): Config dict for normalization\n            layer. Defaults to dict(type='BN').\n        act_cfg (:obj:`ConfigDict` or dict)): Config dict for activation layer.\n            Defaults to dict(type='SiLU', inplace=True).\n        pred_kernel_size (int): Kernel size of prediction layer. Defaults to 1.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 share_conv: bool = True,\n                 with_objectness: bool = False,\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 act_cfg: ConfigType = dict(type='SiLU', inplace=True),\n                 pred_kernel_size: int = 1,\n                 **kwargs) -> None:\n        self.share_conv = share_conv\n        super().__init__(\n            num_classes,\n            in_channels,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg,\n            pred_kernel_size=pred_kernel_size,\n            with_objectness=with_objectness,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        self.kernel_convs = nn.ModuleList()\n\n        self.rtm_cls = nn.ModuleList()\n        self.rtm_reg = nn.ModuleList()\n        self.rtm_kernel = nn.ModuleList()\n        self.rtm_obj = nn.ModuleList()\n\n        # calculate num dynamic parameters\n        weight_nums, bias_nums = [], []\n        for i in range(self.num_dyconvs):\n            if i == 0:\n                weight_nums.append(\n                    (self.num_prototypes + 2) * self.dyconv_channels)\n                bias_nums.append(self.dyconv_channels)\n            elif i == self.num_dyconvs - 1:\n                weight_nums.append(self.dyconv_channels)\n                bias_nums.append(1)\n            else:\n                weight_nums.append(self.dyconv_channels * self.dyconv_channels)\n                bias_nums.append(self.dyconv_channels)\n        self.weight_nums = weight_nums\n        self.bias_nums = bias_nums\n        self.num_gen_params = sum(weight_nums) + sum(bias_nums)\n        pred_pad_size = self.pred_kernel_size // 2\n\n        for n in range(len(self.prior_generator.strides)):\n            cls_convs = nn.ModuleList()\n            reg_convs = nn.ModuleList()\n            kernel_convs = nn.ModuleList()\n            for i in range(self.stacked_convs):\n                chn = self.in_channels if i == 0 else self.feat_channels\n                cls_convs.append(\n                    ConvModule(\n                        chn,\n                        self.feat_channels,\n                        3,\n                        stride=1,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n                reg_convs.append(\n                    ConvModule(\n                        chn,\n                        self.feat_channels,\n                        3,\n                        stride=1,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n                kernel_convs.append(\n                    ConvModule(\n                        chn,\n                        self.feat_channels,\n                        3,\n                        stride=1,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n            self.cls_convs.append(cls_convs)\n            self.reg_convs.append(cls_convs)\n            self.kernel_convs.append(kernel_convs)\n\n            self.rtm_cls.append(\n                nn.Conv2d(\n                    self.feat_channels,\n                    self.num_base_priors * self.cls_out_channels,\n                    self.pred_kernel_size,\n                    padding=pred_pad_size))\n            self.rtm_reg.append(\n                nn.Conv2d(\n                    self.feat_channels,\n                    self.num_base_priors * 4,\n                    self.pred_kernel_size,\n                    padding=pred_pad_size))\n            self.rtm_kernel.append(\n                nn.Conv2d(\n                    self.feat_channels,\n                    self.num_gen_params,\n                    self.pred_kernel_size,\n                    padding=pred_pad_size))\n            if self.with_objectness:\n                self.rtm_obj.append(\n                    nn.Conv2d(\n                        self.feat_channels,\n                        1,\n                        self.pred_kernel_size,\n                        padding=pred_pad_size))\n\n        if self.share_conv:\n            for n in range(len(self.prior_generator.strides)):\n                for i in range(self.stacked_convs):\n                    self.cls_convs[n][i].conv = self.cls_convs[0][i].conv\n                    self.reg_convs[n][i].conv = self.reg_convs[0][i].conv\n\n        self.mask_head = MaskFeatModule(\n            in_channels=self.in_channels,\n            feat_channels=self.feat_channels,\n            stacked_convs=4,\n            num_levels=len(self.prior_generator.strides),\n            num_prototypes=self.num_prototypes,\n            act_cfg=self.act_cfg,\n            norm_cfg=self.norm_cfg)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, mean=0, std=0.01)\n            if is_norm(m):\n                constant_init(m, 1)\n        bias_cls = bias_init_with_prob(0.01)\n        for rtm_cls, rtm_reg, rtm_kernel in zip(self.rtm_cls, self.rtm_reg,\n                                                self.rtm_kernel):\n            normal_init(rtm_cls, std=0.01, bias=bias_cls)\n            normal_init(rtm_reg, std=0.01, bias=1)\n        if self.with_objectness:\n            for rtm_obj in self.rtm_obj:\n                normal_init(rtm_obj, std=0.01, bias=bias_cls)\n\n    def forward(self, feats: Tuple[Tensor, ...]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n            - cls_scores (list[Tensor]): Classification scores for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_base_priors * num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_base_priors * 4.\n            - kernel_preds (list[Tensor]): Dynamic conv kernels for all scale\n              levels, each is a 4D-tensor, the channels number is\n              num_gen_params.\n            - mask_feat (Tensor): Output feature of the mask head. Each is a\n              4D-tensor, the channels number is num_prototypes.\n        \"\"\"\n        mask_feat = self.mask_head(feats)\n\n        cls_scores = []\n        bbox_preds = []\n        kernel_preds = []\n        for idx, (x, stride) in enumerate(\n                zip(feats, self.prior_generator.strides)):\n            cls_feat = x\n            reg_feat = x\n            kernel_feat = x\n\n            for cls_layer in self.cls_convs[idx]:\n                cls_feat = cls_layer(cls_feat)\n            cls_score = self.rtm_cls[idx](cls_feat)\n\n            for kernel_layer in self.kernel_convs[idx]:\n                kernel_feat = kernel_layer(kernel_feat)\n            kernel_pred = self.rtm_kernel[idx](kernel_feat)\n\n            for reg_layer in self.reg_convs[idx]:\n                reg_feat = reg_layer(reg_feat)\n\n            if self.with_objectness:\n                objectness = self.rtm_obj[idx](reg_feat)\n                cls_score = inverse_sigmoid(\n                    sigmoid_geometric_mean(cls_score, objectness))\n\n            reg_dist = F.relu(self.rtm_reg[idx](reg_feat)) * stride[0]\n\n            cls_scores.append(cls_score)\n            bbox_preds.append(reg_dist)\n            kernel_preds.append(kernel_pred)\n        return tuple(cls_scores), tuple(bbox_preds), tuple(\n            kernel_preds), mask_feat\n"
  },
  {
    "path": "mmdet/models/dense_heads/sabl_retina_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple, Union\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig, OptConfigType,\n                         OptInstanceList)\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import (filter_scores_and_topk, images_to_levels, multi_apply,\n                     unmap)\nfrom .base_dense_head import BaseDenseHead\nfrom .guided_anchor_head import GuidedAnchorHead\n\n\n@MODELS.register_module()\nclass SABLRetinaHead(BaseDenseHead):\n    \"\"\"Side-Aware Boundary Localization (SABL) for RetinaNet.\n\n    The anchor generation, assigning and sampling in SABLRetinaHead\n    are the same as GuidedAnchorHead for guided anchoring.\n\n    Please refer to https://arxiv.org/abs/1912.04260 for more details.\n\n    Args:\n        num_classes (int): Number of classes.\n        in_channels (int): Number of channels in the input feature map.\n        stacked_convs (int): Number of Convs for classification and\n            regression branches. Defaults to 4.\n        feat_channels (int): Number of hidden channels. Defaults to 256.\n        approx_anchor_generator (:obj:`ConfigType` or dict): Config dict for\n            approx generator.\n        square_anchor_generator (:obj:`ConfigDict` or dict): Config dict for\n            square generator.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            ConvModule. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            Norm Layer. Defaults to None.\n        bbox_coder (:obj:`ConfigDict` or dict): Config dict for bbox coder.\n        reg_decoded_bbox (bool): If true, the regression loss would be\n            applied directly on decoded bounding boxes, converting both\n            the predicted boxes and regression targets to absolute\n            coordinates format. Default False. It should be ``True`` when\n            using ``IoULoss``, ``GIoULoss``, or ``DIoULoss`` in the bbox head.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config of\n            SABLRetinaHead.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            SABLRetinaHead.\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox_cls (:obj:`ConfigDict` or dict): Config of classification\n            loss for bbox branch.\n        loss_bbox_reg (:obj:`ConfigDict` or dict): Config of regression loss\n            for bbox branch.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int,\n        stacked_convs: int = 4,\n        feat_channels: int = 256,\n        approx_anchor_generator: ConfigType = dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator: ConfigType = dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        bbox_coder: ConfigType = dict(\n            type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n        reg_decoded_bbox: bool = False,\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        loss_cls: ConfigType = dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox_cls: ConfigType = dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n        loss_bbox_reg: ConfigType = dict(\n            type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5),\n        init_cfg: MultiConfig = dict(\n            type='Normal',\n            layer='Conv2d',\n            std=0.01,\n            override=dict(\n                type='Normal', name='retina_cls', std=0.01, bias_prob=0.01))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.num_classes = num_classes\n        self.feat_channels = feat_channels\n        self.num_buckets = bbox_coder['num_buckets']\n        self.side_num = int(np.ceil(self.num_buckets / 2))\n\n        assert (approx_anchor_generator['octave_base_scale'] ==\n                square_anchor_generator['scales'][0])\n        assert (approx_anchor_generator['strides'] ==\n                square_anchor_generator['strides'])\n\n        self.approx_anchor_generator = TASK_UTILS.build(\n            approx_anchor_generator)\n        self.square_anchor_generator = TASK_UTILS.build(\n            square_anchor_generator)\n        self.approxs_per_octave = (\n            self.approx_anchor_generator.num_base_priors[0])\n\n        # one anchor per location\n        self.num_base_priors = self.square_anchor_generator.num_base_priors[0]\n\n        self.stacked_convs = stacked_convs\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n\n        self.reg_decoded_bbox = reg_decoded_bbox\n\n        self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False)\n        if self.use_sigmoid_cls:\n            self.cls_out_channels = num_classes\n        else:\n            self.cls_out_channels = num_classes + 1\n\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox_cls = MODELS.build(loss_bbox_cls)\n        self.loss_bbox_reg = MODELS.build(loss_bbox_reg)\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            # use PseudoSampler when sampling is False\n            if 'sampler' in self.train_cfg:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler(context=self)\n\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        self.retina_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n        self.retina_bbox_reg = nn.Conv2d(\n            self.feat_channels, self.side_num * 4, 3, padding=1)\n        self.retina_bbox_cls = nn.Conv2d(\n            self.feat_channels, self.side_num * 4, 3, padding=1)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, Tensor]:\n        cls_feat = x\n        reg_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            reg_feat = reg_conv(reg_feat)\n        cls_score = self.retina_cls(cls_feat)\n        bbox_cls_pred = self.retina_bbox_cls(reg_feat)\n        bbox_reg_pred = self.retina_bbox_reg(reg_feat)\n        bbox_pred = (bbox_cls_pred, bbox_reg_pred)\n        return cls_score, bbox_pred\n\n    def forward(self, feats: List[Tensor]) -> Tuple[List[Tensor]]:\n        return multi_apply(self.forward_single, feats)\n\n    def get_anchors(\n        self,\n        featmap_sizes: List[tuple],\n        img_metas: List[dict],\n        device: Union[torch.device, str] = 'cuda'\n    ) -> Tuple[List[List[Tensor]], List[List[Tensor]]]:\n        \"\"\"Get squares according to feature map sizes and guided anchors.\n\n        Args:\n            featmap_sizes (list[tuple]): Multi-level feature map sizes.\n            img_metas (list[dict]): Image meta info.\n            device (torch.device | str): device for returned tensors\n\n        Returns:\n            tuple: square approxs of each image\n        \"\"\"\n        num_imgs = len(img_metas)\n\n        # since feature map sizes of all images are the same, we only compute\n        # squares for one time\n        multi_level_squares = self.square_anchor_generator.grid_priors(\n            featmap_sizes, device=device)\n        squares_list = [multi_level_squares for _ in range(num_imgs)]\n\n        return squares_list\n\n    def get_targets(self,\n                    approx_list: List[List[Tensor]],\n                    inside_flag_list: List[List[Tensor]],\n                    square_list: List[List[Tensor]],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas,\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs=True) -> tuple:\n        \"\"\"Compute bucketing targets.\n\n        Args:\n            approx_list (list[list[Tensor]]): Multi level approxs of each\n                image.\n            inside_flag_list (list[list[Tensor]]): Multi level inside flags of\n                each image.\n            square_list (list[list[Tensor]]): Multi level squares of each\n                image.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors. Defaults to True.\n\n        Returns:\n            tuple: Returns a tuple containing learning targets.\n\n            - labels_list (list[Tensor]): Labels of each level.\n            - label_weights_list (list[Tensor]): Label weights of each level.\n            - bbox_cls_targets_list (list[Tensor]): BBox cls targets of \\\n            each level.\n            - bbox_cls_weights_list (list[Tensor]): BBox cls weights of \\\n            each level.\n            - bbox_reg_targets_list (list[Tensor]): BBox reg targets of \\\n            each level.\n            - bbox_reg_weights_list (list[Tensor]): BBox reg weights of \\\n            each level.\n            - num_total_pos (int): Number of positive samples in all images.\n            - num_total_neg (int): Number of negative samples in all images.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(approx_list) == len(inside_flag_list) == len(\n            square_list) == num_imgs\n        # anchor number of multi levels\n        num_level_squares = [squares.size(0) for squares in square_list[0]]\n        # concat all level anchors and flags to a single tensor\n        inside_flag_flat_list = []\n        approx_flat_list = []\n        square_flat_list = []\n        for i in range(num_imgs):\n            assert len(square_list[i]) == len(inside_flag_list[i])\n            inside_flag_flat_list.append(torch.cat(inside_flag_list[i]))\n            approx_flat_list.append(torch.cat(approx_list[i]))\n            square_flat_list.append(torch.cat(square_list[i]))\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None for _ in range(num_imgs)]\n        (all_labels, all_label_weights, all_bbox_cls_targets,\n         all_bbox_cls_weights, all_bbox_reg_targets, all_bbox_reg_weights,\n         pos_inds_list, neg_inds_list, sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             approx_flat_list,\n             inside_flag_flat_list,\n             square_flat_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             unmap_outputs=unmap_outputs)\n\n        # sampled anchors of all images\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        labels_list = images_to_levels(all_labels, num_level_squares)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_squares)\n        bbox_cls_targets_list = images_to_levels(all_bbox_cls_targets,\n                                                 num_level_squares)\n        bbox_cls_weights_list = images_to_levels(all_bbox_cls_weights,\n                                                 num_level_squares)\n        bbox_reg_targets_list = images_to_levels(all_bbox_reg_targets,\n                                                 num_level_squares)\n        bbox_reg_weights_list = images_to_levels(all_bbox_reg_weights,\n                                                 num_level_squares)\n        return (labels_list, label_weights_list, bbox_cls_targets_list,\n                bbox_cls_weights_list, bbox_reg_targets_list,\n                bbox_reg_weights_list, avg_factor)\n\n    def _get_targets_single(self,\n                            flat_approxs: Tensor,\n                            inside_flags: Tensor,\n                            flat_squares: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors in a\n        single image.\n\n        Args:\n            flat_approxs (Tensor): flat approxs of a single image,\n                shape (n, 4)\n            inside_flags (Tensor): inside flags of a single image,\n                shape (n, ).\n            flat_squares (Tensor): flat squares of a single image,\n                shape (approxs_per_octave * n, 4)\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.  Defaults to True.\n\n        Returns:\n            tuple:\n\n            - labels_list (Tensor): Labels in a single image.\n            - label_weights (Tensor): Label weights in a single image.\n            - bbox_cls_targets (Tensor): BBox cls targets in a single image.\n            - bbox_cls_weights (Tensor): BBox cls weights in a single image.\n            - bbox_reg_targets (Tensor): BBox reg targets in a single image.\n            - bbox_reg_weights (Tensor): BBox reg weights in a single image.\n            - num_total_pos (int): Number of positive samples in a single \\\n            image.\n            - num_total_neg (int): Number of negative samples in a single \\\n            image.\n            - sampling_result (:obj:`SamplingResult`): Sampling result object.\n        \"\"\"\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # assign gt and sample anchors\n        num_square = flat_squares.size(0)\n        approxs = flat_approxs.view(num_square, self.approxs_per_octave, 4)\n        approxs = approxs[inside_flags, ...]\n        squares = flat_squares[inside_flags, :]\n\n        pred_instances = InstanceData()\n        pred_instances.priors = squares\n        pred_instances.approxs = approxs\n        assign_result = self.assigner.assign(pred_instances, gt_instances,\n                                             gt_instances_ignore)\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_squares = squares.shape[0]\n        bbox_cls_targets = squares.new_zeros(\n            (num_valid_squares, self.side_num * 4))\n        bbox_cls_weights = squares.new_zeros(\n            (num_valid_squares, self.side_num * 4))\n        bbox_reg_targets = squares.new_zeros(\n            (num_valid_squares, self.side_num * 4))\n        bbox_reg_weights = squares.new_zeros(\n            (num_valid_squares, self.side_num * 4))\n        labels = squares.new_full((num_valid_squares, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = squares.new_zeros(num_valid_squares, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            (pos_bbox_reg_targets, pos_bbox_reg_weights, pos_bbox_cls_targets,\n             pos_bbox_cls_weights) = self.bbox_coder.encode(\n                 sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes)\n\n            bbox_cls_targets[pos_inds, :] = pos_bbox_cls_targets\n            bbox_reg_targets[pos_inds, :] = pos_bbox_reg_targets\n            bbox_cls_weights[pos_inds, :] = pos_bbox_cls_weights\n            bbox_reg_weights[pos_inds, :] = pos_bbox_reg_weights\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_squares.size(0)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags, fill=self.num_classes)\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_cls_targets = unmap(bbox_cls_targets, num_total_anchors,\n                                     inside_flags)\n            bbox_cls_weights = unmap(bbox_cls_weights, num_total_anchors,\n                                     inside_flags)\n            bbox_reg_targets = unmap(bbox_reg_targets, num_total_anchors,\n                                     inside_flags)\n            bbox_reg_weights = unmap(bbox_reg_weights, num_total_anchors,\n                                     inside_flags)\n        return (labels, label_weights, bbox_cls_targets, bbox_cls_weights,\n                bbox_reg_targets, bbox_reg_weights, pos_inds, neg_inds,\n                sampling_result)\n\n    def loss_by_feat_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            labels: Tensor, label_weights: Tensor,\n                            bbox_cls_targets: Tensor, bbox_cls_weights: Tensor,\n                            bbox_reg_targets: Tensor, bbox_reg_weights: Tensor,\n                            avg_factor: float) -> Tuple[Tensor]:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W).\n            bbox_pred (Tensor): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            labels (Tensor): Labels in a single image.\n            label_weights (Tensor): Label weights in a single level.\n            bbox_cls_targets (Tensor): BBox cls targets in a single level.\n            bbox_cls_weights (Tensor): BBox cls weights in a single level.\n            bbox_reg_targets (Tensor): BBox reg targets in a single level.\n            bbox_reg_weights (Tensor): BBox reg weights in a single level.\n            avg_factor (int): Average factor that is used to average the loss.\n\n        Returns:\n            tuple: loss components.\n        \"\"\"\n        # classification loss\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        cls_score = cls_score.permute(0, 2, 3,\n                                      1).reshape(-1, self.cls_out_channels)\n        loss_cls = self.loss_cls(\n            cls_score, labels, label_weights, avg_factor=avg_factor)\n        # regression loss\n        bbox_cls_targets = bbox_cls_targets.reshape(-1, self.side_num * 4)\n        bbox_cls_weights = bbox_cls_weights.reshape(-1, self.side_num * 4)\n        bbox_reg_targets = bbox_reg_targets.reshape(-1, self.side_num * 4)\n        bbox_reg_weights = bbox_reg_weights.reshape(-1, self.side_num * 4)\n        (bbox_cls_pred, bbox_reg_pred) = bbox_pred\n        bbox_cls_pred = bbox_cls_pred.permute(0, 2, 3, 1).reshape(\n            -1, self.side_num * 4)\n        bbox_reg_pred = bbox_reg_pred.permute(0, 2, 3, 1).reshape(\n            -1, self.side_num * 4)\n        loss_bbox_cls = self.loss_bbox_cls(\n            bbox_cls_pred,\n            bbox_cls_targets.long(),\n            bbox_cls_weights,\n            avg_factor=avg_factor * 4 * self.side_num)\n        loss_bbox_reg = self.loss_bbox_reg(\n            bbox_reg_pred,\n            bbox_reg_targets,\n            bbox_reg_weights,\n            avg_factor=avg_factor * 4 * self.bbox_coder.offset_topk)\n        return loss_cls, loss_bbox_cls, loss_bbox_reg\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.approx_anchor_generator.num_levels\n\n        device = cls_scores[0].device\n\n        # get sampled approxes\n        approxs_list, inside_flag_list = GuidedAnchorHead.get_sampled_approxs(\n            self, featmap_sizes, batch_img_metas, device=device)\n\n        square_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        cls_reg_targets = self.get_targets(\n            approxs_list,\n            inside_flag_list,\n            square_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        (labels_list, label_weights_list, bbox_cls_targets_list,\n         bbox_cls_weights_list, bbox_reg_targets_list, bbox_reg_weights_list,\n         avg_factor) = cls_reg_targets\n\n        losses_cls, losses_bbox_cls, losses_bbox_reg = multi_apply(\n            self.loss_by_feat_single,\n            cls_scores,\n            bbox_preds,\n            labels_list,\n            label_weights_list,\n            bbox_cls_targets_list,\n            bbox_cls_weights_list,\n            bbox_reg_targets_list,\n            bbox_reg_weights_list,\n            avg_factor=avg_factor)\n        return dict(\n            loss_cls=losses_cls,\n            loss_bbox_cls=losses_bbox_cls,\n            loss_bbox_reg=losses_bbox_reg)\n\n    def predict_by_feat(self,\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        batch_img_metas: List[dict],\n                        cfg: Optional[ConfigDict] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Note: When score_factors is not None, the cls_scores are\n        usually multiplied by it then obtain the real score used in NMS,\n        such as CenterNess in FCOS, IoU branch in ATSS.\n\n        Args:\n            cls_scores (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n            cfg (:obj:`ConfigDict`, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n        num_levels = len(cls_scores)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n\n        device = cls_scores[0].device\n        mlvl_anchors = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score_list = [\n                cls_scores[i][img_id].detach() for i in range(num_levels)\n            ]\n            bbox_cls_pred_list = [\n                bbox_preds[i][0][img_id].detach() for i in range(num_levels)\n            ]\n            bbox_reg_pred_list = [\n                bbox_preds[i][1][img_id].detach() for i in range(num_levels)\n            ]\n            proposals = self._predict_by_feat_single(\n                cls_scores=cls_score_list,\n                bbox_cls_preds=bbox_cls_pred_list,\n                bbox_reg_preds=bbox_reg_pred_list,\n                mlvl_anchors=mlvl_anchors[img_id],\n                img_meta=batch_img_metas[img_id],\n                cfg=cfg,\n                rescale=rescale,\n                with_nms=with_nms)\n            result_list.append(proposals)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_scores: List[Tensor],\n                                bbox_cls_preds: List[Tensor],\n                                bbox_reg_preds: List[Tensor],\n                                mlvl_anchors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        cfg = self.test_cfg if cfg is None else cfg\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bboxes = []\n        mlvl_scores = []\n        mlvl_confids = []\n        mlvl_labels = []\n        assert len(cls_scores) == len(bbox_cls_preds) == len(\n            bbox_reg_preds) == len(mlvl_anchors)\n        for cls_score, bbox_cls_pred, bbox_reg_pred, anchors in zip(\n                cls_scores, bbox_cls_preds, bbox_reg_preds, mlvl_anchors):\n            assert cls_score.size()[-2:] == bbox_cls_pred.size(\n            )[-2:] == bbox_reg_pred.size()[-2::]\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            if self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                scores = cls_score.softmax(-1)[:, :-1]\n            bbox_cls_pred = bbox_cls_pred.permute(1, 2, 0).reshape(\n                -1, self.side_num * 4)\n            bbox_reg_pred = bbox_reg_pred.permute(1, 2, 0).reshape(\n                -1, self.side_num * 4)\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            results = filter_scores_and_topk(\n                scores, cfg.score_thr, nms_pre,\n                dict(\n                    anchors=anchors,\n                    bbox_cls_pred=bbox_cls_pred,\n                    bbox_reg_pred=bbox_reg_pred))\n            scores, labels, _, filtered_results = results\n\n            anchors = filtered_results['anchors']\n            bbox_cls_pred = filtered_results['bbox_cls_pred']\n            bbox_reg_pred = filtered_results['bbox_reg_pred']\n\n            bbox_preds = [\n                bbox_cls_pred.contiguous(),\n                bbox_reg_pred.contiguous()\n            ]\n            bboxes, confids = self.bbox_coder.decode(\n                anchors.contiguous(),\n                bbox_preds,\n                max_shape=img_meta['img_shape'])\n\n            mlvl_bboxes.append(bboxes)\n            mlvl_scores.append(scores)\n            mlvl_confids.append(confids)\n            mlvl_labels.append(labels)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bboxes)\n        results.scores = torch.cat(mlvl_scores)\n        results.score_factors = torch.cat(mlvl_confids)\n        results.labels = torch.cat(mlvl_labels)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n"
  },
  {
    "path": "mmdet/models/dense_heads/solo_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport mmcv\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.utils.misc import floordiv\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig, OptConfigType\nfrom ..layers import mask_matrix_nms\nfrom ..utils import center_of_mass, generate_coordinate, multi_apply\nfrom .base_mask_head import BaseMaskHead\n\n\n@MODELS.register_module()\nclass SOLOHead(BaseMaskHead):\n    \"\"\"SOLO mask head used in `SOLO: Segmenting Objects by Locations.\n\n    <https://arxiv.org/abs/1912.04488>`_\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels. Used in child classes.\n            Defaults to 256.\n        stacked_convs (int): Number of stacking convs of the head.\n            Defaults to 4.\n        strides (tuple): Downsample factor of each feature map.\n        scale_ranges (tuple[tuple[int, int]]): Area range of multiple\n            level masks, in the format [(min1, max1), (min2, max2), ...].\n            A range of (16, 64) means the area range between (16, 64).\n        pos_scale (float): Constant scale factor to control the center region.\n        num_grids (list[int]): Divided image into a uniform grids, each\n            feature map has a different grid value. The number of output\n            channels is grid ** 2. Defaults to [40, 36, 24, 16, 12].\n        cls_down_index (int): The index of downsample operation in\n            classification branch. Defaults to 0.\n        loss_mask (dict): Config of mask loss.\n        loss_cls (dict): Config of classification loss.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n            Defaults to norm_cfg=dict(type='GN', num_groups=32,\n            requires_grad=True).\n        train_cfg (dict): Training config of head.\n        test_cfg (dict): Testing config of head.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int,\n        feat_channels: int = 256,\n        stacked_convs: int = 4,\n        strides: tuple = (4, 8, 16, 32, 64),\n        scale_ranges: tuple = ((8, 32), (16, 64), (32, 128), (64, 256), (128,\n                                                                         512)),\n        pos_scale: float = 0.2,\n        num_grids: list = [40, 36, 24, 16, 12],\n        cls_down_index: int = 0,\n        loss_mask: ConfigType = dict(\n            type='DiceLoss', use_sigmoid=True, loss_weight=3.0),\n        loss_cls: ConfigType = dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        norm_cfg: ConfigType = dict(\n            type='GN', num_groups=32, requires_grad=True),\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        init_cfg: MultiConfig = [\n            dict(type='Normal', layer='Conv2d', std=0.01),\n            dict(\n                type='Normal',\n                std=0.01,\n                bias_prob=0.01,\n                override=dict(name='conv_mask_list')),\n            dict(\n                type='Normal',\n                std=0.01,\n                bias_prob=0.01,\n                override=dict(name='conv_cls'))\n        ]\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.num_classes = num_classes\n        self.cls_out_channels = self.num_classes\n        self.in_channels = in_channels\n        self.feat_channels = feat_channels\n        self.stacked_convs = stacked_convs\n        self.strides = strides\n        self.num_grids = num_grids\n        # number of FPN feats\n        self.num_levels = len(strides)\n        assert self.num_levels == len(scale_ranges) == len(num_grids)\n        self.scale_ranges = scale_ranges\n        self.pos_scale = pos_scale\n\n        self.cls_down_index = cls_down_index\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_mask = MODELS.build(loss_mask)\n        self.norm_cfg = norm_cfg\n        self.init_cfg = init_cfg\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.mask_convs = nn.ModuleList()\n        self.cls_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels + 2 if i == 0 else self.feat_channels\n            self.mask_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    norm_cfg=self.norm_cfg))\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    norm_cfg=self.norm_cfg))\n        self.conv_mask_list = nn.ModuleList()\n        for num_grid in self.num_grids:\n            self.conv_mask_list.append(\n                nn.Conv2d(self.feat_channels, num_grid**2, 1))\n\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n\n    def resize_feats(self, x: Tuple[Tensor]) -> List[Tensor]:\n        \"\"\"Downsample the first feat and upsample last feat in feats.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            list[Tensor]: Features after resizing, each is a 4D-tensor.\n        \"\"\"\n        out = []\n        for i in range(len(x)):\n            if i == 0:\n                out.append(\n                    F.interpolate(x[0], scale_factor=0.5, mode='bilinear'))\n            elif i == len(x) - 1:\n                out.append(\n                    F.interpolate(\n                        x[i], size=x[i - 1].shape[-2:], mode='bilinear'))\n            else:\n                out.append(x[i])\n        return out\n\n    def forward(self, x: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores and mask prediction.\n\n                - mlvl_mask_preds (list[Tensor]): Multi-level mask prediction.\n                  Each element in the list has shape\n                  (batch_size, num_grids**2 ,h ,w).\n                - mlvl_cls_preds (list[Tensor]): Multi-level scores.\n                  Each element in the list has shape\n                  (batch_size, num_classes, num_grids ,num_grids).\n        \"\"\"\n        assert len(x) == self.num_levels\n        feats = self.resize_feats(x)\n        mlvl_mask_preds = []\n        mlvl_cls_preds = []\n        for i in range(self.num_levels):\n            x = feats[i]\n            mask_feat = x\n            cls_feat = x\n            # generate and concat the coordinate\n            coord_feat = generate_coordinate(mask_feat.size(),\n                                             mask_feat.device)\n            mask_feat = torch.cat([mask_feat, coord_feat], 1)\n\n            for mask_layer in (self.mask_convs):\n                mask_feat = mask_layer(mask_feat)\n\n            mask_feat = F.interpolate(\n                mask_feat, scale_factor=2, mode='bilinear')\n            mask_preds = self.conv_mask_list[i](mask_feat)\n\n            # cls branch\n            for j, cls_layer in enumerate(self.cls_convs):\n                if j == self.cls_down_index:\n                    num_grid = self.num_grids[i]\n                    cls_feat = F.interpolate(\n                        cls_feat, size=num_grid, mode='bilinear')\n                cls_feat = cls_layer(cls_feat)\n\n            cls_pred = self.conv_cls(cls_feat)\n\n            if not self.training:\n                feat_wh = feats[0].size()[-2:]\n                upsampled_size = (feat_wh[0] * 2, feat_wh[1] * 2)\n                mask_preds = F.interpolate(\n                    mask_preds.sigmoid(), size=upsampled_size, mode='bilinear')\n                cls_pred = cls_pred.sigmoid()\n                # get local maximum\n                local_max = F.max_pool2d(cls_pred, 2, stride=1, padding=1)\n                keep_mask = local_max[:, :, :-1, :-1] == cls_pred\n                cls_pred = cls_pred * keep_mask\n\n            mlvl_mask_preds.append(mask_preds)\n            mlvl_cls_preds.append(cls_pred)\n        return mlvl_mask_preds, mlvl_cls_preds\n\n    def loss_by_feat(self, mlvl_mask_preds: List[Tensor],\n                     mlvl_cls_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict], **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mlvl_mask_preds (list[Tensor]): Multi-level mask prediction.\n                Each element in the list has shape\n                (batch_size, num_grids**2 ,h ,w).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``masks``,\n                and ``labels`` attributes.\n            batch_img_metas (list[dict]): Meta information of multiple images.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        num_levels = self.num_levels\n        num_imgs = len(batch_img_metas)\n\n        featmap_sizes = [featmap.size()[-2:] for featmap in mlvl_mask_preds]\n\n        # `BoolTensor` in `pos_masks` represent\n        # whether the corresponding point is\n        # positive\n        pos_mask_targets, labels, pos_masks = multi_apply(\n            self._get_targets_single,\n            batch_gt_instances,\n            featmap_sizes=featmap_sizes)\n\n        # change from the outside list meaning multi images\n        # to the outside list meaning multi levels\n        mlvl_pos_mask_targets = [[] for _ in range(num_levels)]\n        mlvl_pos_mask_preds = [[] for _ in range(num_levels)]\n        mlvl_pos_masks = [[] for _ in range(num_levels)]\n        mlvl_labels = [[] for _ in range(num_levels)]\n        for img_id in range(num_imgs):\n            assert num_levels == len(pos_mask_targets[img_id])\n            for lvl in range(num_levels):\n                mlvl_pos_mask_targets[lvl].append(\n                    pos_mask_targets[img_id][lvl])\n                mlvl_pos_mask_preds[lvl].append(\n                    mlvl_mask_preds[lvl][img_id, pos_masks[img_id][lvl], ...])\n                mlvl_pos_masks[lvl].append(pos_masks[img_id][lvl].flatten())\n                mlvl_labels[lvl].append(labels[img_id][lvl].flatten())\n\n        # cat multiple image\n        temp_mlvl_cls_preds = []\n        for lvl in range(num_levels):\n            mlvl_pos_mask_targets[lvl] = torch.cat(\n                mlvl_pos_mask_targets[lvl], dim=0)\n            mlvl_pos_mask_preds[lvl] = torch.cat(\n                mlvl_pos_mask_preds[lvl], dim=0)\n            mlvl_pos_masks[lvl] = torch.cat(mlvl_pos_masks[lvl], dim=0)\n            mlvl_labels[lvl] = torch.cat(mlvl_labels[lvl], dim=0)\n            temp_mlvl_cls_preds.append(mlvl_cls_preds[lvl].permute(\n                0, 2, 3, 1).reshape(-1, self.cls_out_channels))\n\n        num_pos = sum(item.sum() for item in mlvl_pos_masks)\n        # dice loss\n        loss_mask = []\n        for pred, target in zip(mlvl_pos_mask_preds, mlvl_pos_mask_targets):\n            if pred.size()[0] == 0:\n                loss_mask.append(pred.sum().unsqueeze(0))\n                continue\n            loss_mask.append(\n                self.loss_mask(pred, target, reduction_override='none'))\n        if num_pos > 0:\n            loss_mask = torch.cat(loss_mask).sum() / num_pos\n        else:\n            loss_mask = torch.cat(loss_mask).mean()\n\n        flatten_labels = torch.cat(mlvl_labels)\n        flatten_cls_preds = torch.cat(temp_mlvl_cls_preds)\n        loss_cls = self.loss_cls(\n            flatten_cls_preds, flatten_labels, avg_factor=num_pos + 1)\n        return dict(loss_mask=loss_mask, loss_cls=loss_cls)\n\n    def _get_targets_single(self,\n                            gt_instances: InstanceData,\n                            featmap_sizes: Optional[list] = None) -> tuple:\n        \"\"\"Compute targets for predictions of single image.\n\n        Args:\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes``, ``labels``,\n                and ``masks`` attributes.\n            featmap_sizes (list[:obj:`torch.size`]): Size of each\n                feature map from feature pyramid, each element\n                means (feat_h, feat_w). Defaults to None.\n\n        Returns:\n            Tuple: Usually returns a tuple containing targets for predictions.\n\n                - mlvl_pos_mask_targets (list[Tensor]): Each element represent\n                  the binary mask targets for positive points in this\n                  level, has shape (num_pos, out_h, out_w).\n                - mlvl_labels (list[Tensor]): Each element is\n                  classification labels for all\n                  points in this level, has shape\n                  (num_grid, num_grid).\n                - mlvl_pos_masks (list[Tensor]): Each element is\n                  a `BoolTensor` to represent whether the\n                  corresponding point in single level\n                  is positive, has shape (num_grid **2).\n        \"\"\"\n        gt_labels = gt_instances.labels\n        device = gt_labels.device\n\n        gt_bboxes = gt_instances.bboxes\n        gt_areas = torch.sqrt((gt_bboxes[:, 2] - gt_bboxes[:, 0]) *\n                              (gt_bboxes[:, 3] - gt_bboxes[:, 1]))\n\n        gt_masks = gt_instances.masks.to_tensor(\n            dtype=torch.bool, device=device)\n\n        mlvl_pos_mask_targets = []\n        mlvl_labels = []\n        mlvl_pos_masks = []\n        for (lower_bound, upper_bound), stride, featmap_size, num_grid \\\n                in zip(self.scale_ranges, self.strides,\n                       featmap_sizes, self.num_grids):\n\n            mask_target = torch.zeros(\n                [num_grid**2, featmap_size[0], featmap_size[1]],\n                dtype=torch.uint8,\n                device=device)\n            # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n            labels = torch.zeros([num_grid, num_grid],\n                                 dtype=torch.int64,\n                                 device=device) + self.num_classes\n            pos_mask = torch.zeros([num_grid**2],\n                                   dtype=torch.bool,\n                                   device=device)\n\n            gt_inds = ((gt_areas >= lower_bound) &\n                       (gt_areas <= upper_bound)).nonzero().flatten()\n            if len(gt_inds) == 0:\n                mlvl_pos_mask_targets.append(\n                    mask_target.new_zeros(0, featmap_size[0], featmap_size[1]))\n                mlvl_labels.append(labels)\n                mlvl_pos_masks.append(pos_mask)\n                continue\n            hit_gt_bboxes = gt_bboxes[gt_inds]\n            hit_gt_labels = gt_labels[gt_inds]\n            hit_gt_masks = gt_masks[gt_inds, ...]\n\n            pos_w_ranges = 0.5 * (hit_gt_bboxes[:, 2] -\n                                  hit_gt_bboxes[:, 0]) * self.pos_scale\n            pos_h_ranges = 0.5 * (hit_gt_bboxes[:, 3] -\n                                  hit_gt_bboxes[:, 1]) * self.pos_scale\n\n            # Make sure hit_gt_masks has a value\n            valid_mask_flags = hit_gt_masks.sum(dim=-1).sum(dim=-1) > 0\n            output_stride = stride / 2\n\n            for gt_mask, gt_label, pos_h_range, pos_w_range, \\\n                valid_mask_flag in \\\n                    zip(hit_gt_masks, hit_gt_labels, pos_h_ranges,\n                        pos_w_ranges, valid_mask_flags):\n                if not valid_mask_flag:\n                    continue\n                upsampled_size = (featmap_sizes[0][0] * 4,\n                                  featmap_sizes[0][1] * 4)\n                center_h, center_w = center_of_mass(gt_mask)\n\n                coord_w = int(\n                    floordiv((center_w / upsampled_size[1]), (1. / num_grid),\n                             rounding_mode='trunc'))\n                coord_h = int(\n                    floordiv((center_h / upsampled_size[0]), (1. / num_grid),\n                             rounding_mode='trunc'))\n\n                # left, top, right, down\n                top_box = max(\n                    0,\n                    int(\n                        floordiv(\n                            (center_h - pos_h_range) / upsampled_size[0],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n                down_box = min(\n                    num_grid - 1,\n                    int(\n                        floordiv(\n                            (center_h + pos_h_range) / upsampled_size[0],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n                left_box = max(\n                    0,\n                    int(\n                        floordiv(\n                            (center_w - pos_w_range) / upsampled_size[1],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n                right_box = min(\n                    num_grid - 1,\n                    int(\n                        floordiv(\n                            (center_w + pos_w_range) / upsampled_size[1],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n\n                top = max(top_box, coord_h - 1)\n                down = min(down_box, coord_h + 1)\n                left = max(coord_w - 1, left_box)\n                right = min(right_box, coord_w + 1)\n\n                labels[top:(down + 1), left:(right + 1)] = gt_label\n                # ins\n                gt_mask = np.uint8(gt_mask.cpu().numpy())\n                # Follow the original implementation, F.interpolate is\n                # different from cv2 and opencv\n                gt_mask = mmcv.imrescale(gt_mask, scale=1. / output_stride)\n                gt_mask = torch.from_numpy(gt_mask).to(device=device)\n\n                for i in range(top, down + 1):\n                    for j in range(left, right + 1):\n                        index = int(i * num_grid + j)\n                        mask_target[index, :gt_mask.shape[0], :gt_mask.\n                                    shape[1]] = gt_mask\n                        pos_mask[index] = True\n            mlvl_pos_mask_targets.append(mask_target[pos_mask])\n            mlvl_labels.append(labels)\n            mlvl_pos_masks.append(pos_mask)\n        return mlvl_pos_mask_targets, mlvl_labels, mlvl_pos_masks\n\n    def predict_by_feat(self, mlvl_mask_preds: List[Tensor],\n                        mlvl_cls_scores: List[Tensor],\n                        batch_img_metas: List[dict], **kwargs) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        mask results.\n\n        Args:\n            mlvl_mask_preds (list[Tensor]): Multi-level mask prediction.\n                Each element in the list has shape\n                (batch_size, num_grids**2 ,h ,w).\n            mlvl_cls_scores (list[Tensor]): Multi-level scores. Each element\n                in the list has shape\n                (batch_size, num_classes, num_grids ,num_grids).\n            batch_img_metas (list[dict]): Meta information of all images.\n\n        Returns:\n            list[:obj:`InstanceData`]: Processed results of multiple\n            images.Each :obj:`InstanceData` usually contains\n            following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n        mlvl_cls_scores = [\n            item.permute(0, 2, 3, 1) for item in mlvl_cls_scores\n        ]\n        assert len(mlvl_mask_preds) == len(mlvl_cls_scores)\n        num_levels = len(mlvl_cls_scores)\n\n        results_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_pred_list = [\n                mlvl_cls_scores[lvl][img_id].view(-1, self.cls_out_channels)\n                for lvl in range(num_levels)\n            ]\n            mask_pred_list = [\n                mlvl_mask_preds[lvl][img_id] for lvl in range(num_levels)\n            ]\n\n            cls_pred_list = torch.cat(cls_pred_list, dim=0)\n            mask_pred_list = torch.cat(mask_pred_list, dim=0)\n            img_meta = batch_img_metas[img_id]\n\n            results = self._predict_by_feat_single(\n                cls_pred_list, mask_pred_list, img_meta=img_meta)\n            results_list.append(results)\n\n        return results_list\n\n    def _predict_by_feat_single(self,\n                                cls_scores: Tensor,\n                                mask_preds: Tensor,\n                                img_meta: dict,\n                                cfg: OptConfigType = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        mask results.\n\n        Args:\n            cls_scores (Tensor): Classification score of all points\n                in single image, has shape (num_points, num_classes).\n            mask_preds (Tensor): Mask prediction of all points in\n                single image, has shape (num_points, feat_h, feat_w).\n            img_meta (dict): Meta information of corresponding image.\n            cfg (dict, optional): Config used in test phase.\n                Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Processed results of single image.\n             it usually contains following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n\n        def empty_results(cls_scores, ori_shape):\n            \"\"\"Generate a empty results.\"\"\"\n            results = InstanceData()\n            results.scores = cls_scores.new_ones(0)\n            results.masks = cls_scores.new_zeros(0, *ori_shape)\n            results.labels = cls_scores.new_ones(0)\n            results.bboxes = cls_scores.new_zeros(0, 4)\n            return results\n\n        cfg = self.test_cfg if cfg is None else cfg\n        assert len(cls_scores) == len(mask_preds)\n\n        featmap_size = mask_preds.size()[-2:]\n\n        h, w = img_meta['img_shape'][:2]\n        upsampled_size = (featmap_size[0] * 4, featmap_size[1] * 4)\n\n        score_mask = (cls_scores > cfg.score_thr)\n        cls_scores = cls_scores[score_mask]\n        if len(cls_scores) == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n\n        inds = score_mask.nonzero()\n        cls_labels = inds[:, 1]\n\n        # Filter the mask mask with an area is smaller than\n        # stride of corresponding feature level\n        lvl_interval = cls_labels.new_tensor(self.num_grids).pow(2).cumsum(0)\n        strides = cls_scores.new_ones(lvl_interval[-1])\n        strides[:lvl_interval[0]] *= self.strides[0]\n        for lvl in range(1, self.num_levels):\n            strides[lvl_interval[lvl -\n                                 1]:lvl_interval[lvl]] *= self.strides[lvl]\n        strides = strides[inds[:, 0]]\n        mask_preds = mask_preds[inds[:, 0]]\n\n        masks = mask_preds > cfg.mask_thr\n        sum_masks = masks.sum((1, 2)).float()\n        keep = sum_masks > strides\n        if keep.sum() == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n        masks = masks[keep]\n        mask_preds = mask_preds[keep]\n        sum_masks = sum_masks[keep]\n        cls_scores = cls_scores[keep]\n        cls_labels = cls_labels[keep]\n\n        # maskness.\n        mask_scores = (mask_preds * masks).sum((1, 2)) / sum_masks\n        cls_scores *= mask_scores\n\n        scores, labels, _, keep_inds = mask_matrix_nms(\n            masks,\n            cls_labels,\n            cls_scores,\n            mask_area=sum_masks,\n            nms_pre=cfg.nms_pre,\n            max_num=cfg.max_per_img,\n            kernel=cfg.kernel,\n            sigma=cfg.sigma,\n            filter_thr=cfg.filter_thr)\n        # mask_matrix_nms may return an empty Tensor\n        if len(keep_inds) == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n        mask_preds = mask_preds[keep_inds]\n        mask_preds = F.interpolate(\n            mask_preds.unsqueeze(0), size=upsampled_size,\n            mode='bilinear')[:, :, :h, :w]\n        mask_preds = F.interpolate(\n            mask_preds, size=img_meta['ori_shape'][:2],\n            mode='bilinear').squeeze(0)\n        masks = mask_preds > cfg.mask_thr\n\n        results = InstanceData()\n        results.masks = masks\n        results.labels = labels\n        results.scores = scores\n        # create an empty bbox in InstanceData to avoid bugs when\n        # calculating metrics.\n        results.bboxes = results.scores.new_zeros(len(scores), 4)\n        return results\n\n\n@MODELS.register_module()\nclass DecoupledSOLOHead(SOLOHead):\n    \"\"\"Decoupled SOLO mask head used in `SOLO: Segmenting Objects by Locations.\n\n    <https://arxiv.org/abs/1912.04488>`_\n\n    Args:\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 init_cfg: MultiConfig = [\n                     dict(type='Normal', layer='Conv2d', std=0.01),\n                     dict(\n                         type='Normal',\n                         std=0.01,\n                         bias_prob=0.01,\n                         override=dict(name='conv_mask_list_x')),\n                     dict(\n                         type='Normal',\n                         std=0.01,\n                         bias_prob=0.01,\n                         override=dict(name='conv_mask_list_y')),\n                     dict(\n                         type='Normal',\n                         std=0.01,\n                         bias_prob=0.01,\n                         override=dict(name='conv_cls'))\n                 ],\n                 **kwargs) -> None:\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n\n    def _init_layers(self) -> None:\n        self.mask_convs_x = nn.ModuleList()\n        self.mask_convs_y = nn.ModuleList()\n        self.cls_convs = nn.ModuleList()\n\n        for i in range(self.stacked_convs):\n            chn = self.in_channels + 1 if i == 0 else self.feat_channels\n            self.mask_convs_x.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    norm_cfg=self.norm_cfg))\n            self.mask_convs_y.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    norm_cfg=self.norm_cfg))\n\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    norm_cfg=self.norm_cfg))\n\n        self.conv_mask_list_x = nn.ModuleList()\n        self.conv_mask_list_y = nn.ModuleList()\n        for num_grid in self.num_grids:\n            self.conv_mask_list_x.append(\n                nn.Conv2d(self.feat_channels, num_grid, 3, padding=1))\n            self.conv_mask_list_y.append(\n                nn.Conv2d(self.feat_channels, num_grid, 3, padding=1))\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores and mask prediction.\n\n                - mlvl_mask_preds_x (list[Tensor]): Multi-level mask prediction\n                  from x branch. Each element in the list has shape\n                  (batch_size, num_grids ,h ,w).\n                - mlvl_mask_preds_y (list[Tensor]): Multi-level mask prediction\n                  from y branch. Each element in the list has shape\n                  (batch_size, num_grids ,h ,w).\n                - mlvl_cls_preds (list[Tensor]): Multi-level scores.\n                  Each element in the list has shape\n                  (batch_size, num_classes, num_grids ,num_grids).\n        \"\"\"\n        assert len(x) == self.num_levels\n        feats = self.resize_feats(x)\n        mask_preds_x = []\n        mask_preds_y = []\n        cls_preds = []\n        for i in range(self.num_levels):\n            x = feats[i]\n            mask_feat = x\n            cls_feat = x\n            # generate and concat the coordinate\n            coord_feat = generate_coordinate(mask_feat.size(),\n                                             mask_feat.device)\n            mask_feat_x = torch.cat([mask_feat, coord_feat[:, 0:1, ...]], 1)\n            mask_feat_y = torch.cat([mask_feat, coord_feat[:, 1:2, ...]], 1)\n\n            for mask_layer_x, mask_layer_y in \\\n                    zip(self.mask_convs_x, self.mask_convs_y):\n                mask_feat_x = mask_layer_x(mask_feat_x)\n                mask_feat_y = mask_layer_y(mask_feat_y)\n\n            mask_feat_x = F.interpolate(\n                mask_feat_x, scale_factor=2, mode='bilinear')\n            mask_feat_y = F.interpolate(\n                mask_feat_y, scale_factor=2, mode='bilinear')\n\n            mask_pred_x = self.conv_mask_list_x[i](mask_feat_x)\n            mask_pred_y = self.conv_mask_list_y[i](mask_feat_y)\n\n            # cls branch\n            for j, cls_layer in enumerate(self.cls_convs):\n                if j == self.cls_down_index:\n                    num_grid = self.num_grids[i]\n                    cls_feat = F.interpolate(\n                        cls_feat, size=num_grid, mode='bilinear')\n                cls_feat = cls_layer(cls_feat)\n\n            cls_pred = self.conv_cls(cls_feat)\n\n            if not self.training:\n                feat_wh = feats[0].size()[-2:]\n                upsampled_size = (feat_wh[0] * 2, feat_wh[1] * 2)\n                mask_pred_x = F.interpolate(\n                    mask_pred_x.sigmoid(),\n                    size=upsampled_size,\n                    mode='bilinear')\n                mask_pred_y = F.interpolate(\n                    mask_pred_y.sigmoid(),\n                    size=upsampled_size,\n                    mode='bilinear')\n                cls_pred = cls_pred.sigmoid()\n                # get local maximum\n                local_max = F.max_pool2d(cls_pred, 2, stride=1, padding=1)\n                keep_mask = local_max[:, :, :-1, :-1] == cls_pred\n                cls_pred = cls_pred * keep_mask\n\n            mask_preds_x.append(mask_pred_x)\n            mask_preds_y.append(mask_pred_y)\n            cls_preds.append(cls_pred)\n        return mask_preds_x, mask_preds_y, cls_preds\n\n    def loss_by_feat(self, mlvl_mask_preds_x: List[Tensor],\n                     mlvl_mask_preds_y: List[Tensor],\n                     mlvl_cls_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict], **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mlvl_mask_preds_x (list[Tensor]): Multi-level mask prediction\n                from x branch. Each element in the list has shape\n                (batch_size, num_grids ,h ,w).\n            mlvl_mask_preds_y (list[Tensor]): Multi-level mask prediction\n                from y branch. Each element in the list has shape\n                (batch_size, num_grids ,h ,w).\n            mlvl_cls_preds (list[Tensor]): Multi-level scores. Each element\n                in the list has shape\n                (batch_size, num_classes, num_grids ,num_grids).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``masks``,\n                and ``labels`` attributes.\n            batch_img_metas (list[dict]): Meta information of multiple images.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        num_levels = self.num_levels\n        num_imgs = len(batch_img_metas)\n        featmap_sizes = [featmap.size()[-2:] for featmap in mlvl_mask_preds_x]\n\n        pos_mask_targets, labels, xy_pos_indexes = multi_apply(\n            self._get_targets_single,\n            batch_gt_instances,\n            featmap_sizes=featmap_sizes)\n\n        # change from the outside list meaning multi images\n        # to the outside list meaning multi levels\n        mlvl_pos_mask_targets = [[] for _ in range(num_levels)]\n        mlvl_pos_mask_preds_x = [[] for _ in range(num_levels)]\n        mlvl_pos_mask_preds_y = [[] for _ in range(num_levels)]\n        mlvl_labels = [[] for _ in range(num_levels)]\n        for img_id in range(num_imgs):\n\n            for lvl in range(num_levels):\n                mlvl_pos_mask_targets[lvl].append(\n                    pos_mask_targets[img_id][lvl])\n                mlvl_pos_mask_preds_x[lvl].append(\n                    mlvl_mask_preds_x[lvl][img_id,\n                                           xy_pos_indexes[img_id][lvl][:, 1]])\n                mlvl_pos_mask_preds_y[lvl].append(\n                    mlvl_mask_preds_y[lvl][img_id,\n                                           xy_pos_indexes[img_id][lvl][:, 0]])\n                mlvl_labels[lvl].append(labels[img_id][lvl].flatten())\n\n        # cat multiple image\n        temp_mlvl_cls_preds = []\n        for lvl in range(num_levels):\n            mlvl_pos_mask_targets[lvl] = torch.cat(\n                mlvl_pos_mask_targets[lvl], dim=0)\n            mlvl_pos_mask_preds_x[lvl] = torch.cat(\n                mlvl_pos_mask_preds_x[lvl], dim=0)\n            mlvl_pos_mask_preds_y[lvl] = torch.cat(\n                mlvl_pos_mask_preds_y[lvl], dim=0)\n            mlvl_labels[lvl] = torch.cat(mlvl_labels[lvl], dim=0)\n            temp_mlvl_cls_preds.append(mlvl_cls_preds[lvl].permute(\n                0, 2, 3, 1).reshape(-1, self.cls_out_channels))\n\n        num_pos = 0.\n        # dice loss\n        loss_mask = []\n        for pred_x, pred_y, target in \\\n                zip(mlvl_pos_mask_preds_x,\n                    mlvl_pos_mask_preds_y, mlvl_pos_mask_targets):\n            num_masks = pred_x.size(0)\n            if num_masks == 0:\n                # make sure can get grad\n                loss_mask.append((pred_x.sum() + pred_y.sum()).unsqueeze(0))\n                continue\n            num_pos += num_masks\n            pred_mask = pred_y.sigmoid() * pred_x.sigmoid()\n            loss_mask.append(\n                self.loss_mask(pred_mask, target, reduction_override='none'))\n        if num_pos > 0:\n            loss_mask = torch.cat(loss_mask).sum() / num_pos\n        else:\n            loss_mask = torch.cat(loss_mask).mean()\n\n        # cate\n        flatten_labels = torch.cat(mlvl_labels)\n        flatten_cls_preds = torch.cat(temp_mlvl_cls_preds)\n\n        loss_cls = self.loss_cls(\n            flatten_cls_preds, flatten_labels, avg_factor=num_pos + 1)\n        return dict(loss_mask=loss_mask, loss_cls=loss_cls)\n\n    def _get_targets_single(self,\n                            gt_instances: InstanceData,\n                            featmap_sizes: Optional[list] = None) -> tuple:\n        \"\"\"Compute targets for predictions of single image.\n\n        Args:\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes``, ``labels``,\n                and ``masks`` attributes.\n            featmap_sizes (list[:obj:`torch.size`]): Size of each\n                feature map from feature pyramid, each element\n                means (feat_h, feat_w). Defaults to None.\n\n        Returns:\n            Tuple: Usually returns a tuple containing targets for predictions.\n\n                - mlvl_pos_mask_targets (list[Tensor]): Each element represent\n                  the binary mask targets for positive points in this\n                  level, has shape (num_pos, out_h, out_w).\n                - mlvl_labels (list[Tensor]): Each element is\n                  classification labels for all\n                  points in this level, has shape\n                  (num_grid, num_grid).\n                - mlvl_xy_pos_indexes (list[Tensor]): Each element\n                  in the list contains the index of positive samples in\n                  corresponding level, has shape (num_pos, 2), last\n                  dimension 2 present (index_x, index_y).\n        \"\"\"\n        mlvl_pos_mask_targets, mlvl_labels, mlvl_pos_masks = \\\n            super()._get_targets_single(gt_instances,\n                                        featmap_sizes=featmap_sizes)\n\n        mlvl_xy_pos_indexes = [(item - self.num_classes).nonzero()\n                               for item in mlvl_labels]\n\n        return mlvl_pos_mask_targets, mlvl_labels, mlvl_xy_pos_indexes\n\n    def predict_by_feat(self, mlvl_mask_preds_x: List[Tensor],\n                        mlvl_mask_preds_y: List[Tensor],\n                        mlvl_cls_scores: List[Tensor],\n                        batch_img_metas: List[dict], **kwargs) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        mask results.\n\n        Args:\n            mlvl_mask_preds_x (list[Tensor]): Multi-level mask prediction\n                from x branch. Each element in the list has shape\n                (batch_size, num_grids ,h ,w).\n            mlvl_mask_preds_y (list[Tensor]): Multi-level mask prediction\n                from y branch. Each element in the list has shape\n                (batch_size, num_grids ,h ,w).\n            mlvl_cls_scores (list[Tensor]): Multi-level scores. Each element\n                in the list has shape\n                (batch_size, num_classes ,num_grids ,num_grids).\n            batch_img_metas (list[dict]): Meta information of all images.\n\n        Returns:\n            list[:obj:`InstanceData`]: Processed results of multiple\n            images.Each :obj:`InstanceData` usually contains\n            following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n        mlvl_cls_scores = [\n            item.permute(0, 2, 3, 1) for item in mlvl_cls_scores\n        ]\n        assert len(mlvl_mask_preds_x) == len(mlvl_cls_scores)\n        num_levels = len(mlvl_cls_scores)\n\n        results_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_pred_list = [\n                mlvl_cls_scores[i][img_id].view(\n                    -1, self.cls_out_channels).detach()\n                for i in range(num_levels)\n            ]\n            mask_pred_list_x = [\n                mlvl_mask_preds_x[i][img_id] for i in range(num_levels)\n            ]\n            mask_pred_list_y = [\n                mlvl_mask_preds_y[i][img_id] for i in range(num_levels)\n            ]\n\n            cls_pred_list = torch.cat(cls_pred_list, dim=0)\n            mask_pred_list_x = torch.cat(mask_pred_list_x, dim=0)\n            mask_pred_list_y = torch.cat(mask_pred_list_y, dim=0)\n            img_meta = batch_img_metas[img_id]\n\n            results = self._predict_by_feat_single(\n                cls_pred_list,\n                mask_pred_list_x,\n                mask_pred_list_y,\n                img_meta=img_meta)\n            results_list.append(results)\n        return results_list\n\n    def _predict_by_feat_single(self,\n                                cls_scores: Tensor,\n                                mask_preds_x: Tensor,\n                                mask_preds_y: Tensor,\n                                img_meta: dict,\n                                cfg: OptConfigType = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        mask results.\n\n        Args:\n            cls_scores (Tensor): Classification score of all points\n                in single image, has shape (num_points, num_classes).\n            mask_preds_x (Tensor): Mask prediction of x branch of\n                all points in single image, has shape\n                (sum_num_grids, feat_h, feat_w).\n            mask_preds_y (Tensor): Mask prediction of y branch of\n                all points in single image, has shape\n                (sum_num_grids, feat_h, feat_w).\n            img_meta (dict): Meta information of corresponding image.\n            cfg (dict): Config used in test phase.\n\n        Returns:\n            :obj:`InstanceData`: Processed results of single image.\n             it usually contains following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n\n        def empty_results(cls_scores, ori_shape):\n            \"\"\"Generate a empty results.\"\"\"\n            results = InstanceData()\n            results.scores = cls_scores.new_ones(0)\n            results.masks = cls_scores.new_zeros(0, *ori_shape)\n            results.labels = cls_scores.new_ones(0)\n            results.bboxes = cls_scores.new_zeros(0, 4)\n            return results\n\n        cfg = self.test_cfg if cfg is None else cfg\n\n        featmap_size = mask_preds_x.size()[-2:]\n\n        h, w = img_meta['img_shape'][:2]\n        upsampled_size = (featmap_size[0] * 4, featmap_size[1] * 4)\n\n        score_mask = (cls_scores > cfg.score_thr)\n        cls_scores = cls_scores[score_mask]\n        inds = score_mask.nonzero()\n        lvl_interval = inds.new_tensor(self.num_grids).pow(2).cumsum(0)\n        num_all_points = lvl_interval[-1]\n        lvl_start_index = inds.new_ones(num_all_points)\n        num_grids = inds.new_ones(num_all_points)\n        seg_size = inds.new_tensor(self.num_grids).cumsum(0)\n        mask_lvl_start_index = inds.new_ones(num_all_points)\n        strides = inds.new_ones(num_all_points)\n\n        lvl_start_index[:lvl_interval[0]] *= 0\n        mask_lvl_start_index[:lvl_interval[0]] *= 0\n        num_grids[:lvl_interval[0]] *= self.num_grids[0]\n        strides[:lvl_interval[0]] *= self.strides[0]\n\n        for lvl in range(1, self.num_levels):\n            lvl_start_index[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \\\n                lvl_interval[lvl - 1]\n            mask_lvl_start_index[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \\\n                seg_size[lvl - 1]\n            num_grids[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \\\n                self.num_grids[lvl]\n            strides[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \\\n                self.strides[lvl]\n\n        lvl_start_index = lvl_start_index[inds[:, 0]]\n        mask_lvl_start_index = mask_lvl_start_index[inds[:, 0]]\n        num_grids = num_grids[inds[:, 0]]\n        strides = strides[inds[:, 0]]\n\n        y_lvl_offset = (inds[:, 0] - lvl_start_index) // num_grids\n        x_lvl_offset = (inds[:, 0] - lvl_start_index) % num_grids\n        y_inds = mask_lvl_start_index + y_lvl_offset\n        x_inds = mask_lvl_start_index + x_lvl_offset\n\n        cls_labels = inds[:, 1]\n        mask_preds = mask_preds_x[x_inds, ...] * mask_preds_y[y_inds, ...]\n\n        masks = mask_preds > cfg.mask_thr\n        sum_masks = masks.sum((1, 2)).float()\n        keep = sum_masks > strides\n        if keep.sum() == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n\n        masks = masks[keep]\n        mask_preds = mask_preds[keep]\n        sum_masks = sum_masks[keep]\n        cls_scores = cls_scores[keep]\n        cls_labels = cls_labels[keep]\n\n        # maskness.\n        mask_scores = (mask_preds * masks).sum((1, 2)) / sum_masks\n        cls_scores *= mask_scores\n\n        scores, labels, _, keep_inds = mask_matrix_nms(\n            masks,\n            cls_labels,\n            cls_scores,\n            mask_area=sum_masks,\n            nms_pre=cfg.nms_pre,\n            max_num=cfg.max_per_img,\n            kernel=cfg.kernel,\n            sigma=cfg.sigma,\n            filter_thr=cfg.filter_thr)\n        # mask_matrix_nms may return an empty Tensor\n        if len(keep_inds) == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n        mask_preds = mask_preds[keep_inds]\n        mask_preds = F.interpolate(\n            mask_preds.unsqueeze(0), size=upsampled_size,\n            mode='bilinear')[:, :, :h, :w]\n        mask_preds = F.interpolate(\n            mask_preds, size=img_meta['ori_shape'][:2],\n            mode='bilinear').squeeze(0)\n        masks = mask_preds > cfg.mask_thr\n\n        results = InstanceData()\n        results.masks = masks\n        results.labels = labels\n        results.scores = scores\n        # create an empty bbox in InstanceData to avoid bugs when\n        # calculating metrics.\n        results.bboxes = results.scores.new_zeros(len(scores), 4)\n\n        return results\n\n\n@MODELS.register_module()\nclass DecoupledSOLOLightHead(DecoupledSOLOHead):\n    \"\"\"Decoupled Light SOLO mask head used in `SOLO: Segmenting Objects by\n    Locations <https://arxiv.org/abs/1912.04488>`_\n\n    Args:\n        with_dcn (bool): Whether use dcn in mask_convs and cls_convs,\n            Defaults to False.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 dcn_cfg: OptConfigType = None,\n                 init_cfg: MultiConfig = [\n                     dict(type='Normal', layer='Conv2d', std=0.01),\n                     dict(\n                         type='Normal',\n                         std=0.01,\n                         bias_prob=0.01,\n                         override=dict(name='conv_mask_list_x')),\n                     dict(\n                         type='Normal',\n                         std=0.01,\n                         bias_prob=0.01,\n                         override=dict(name='conv_mask_list_y')),\n                     dict(\n                         type='Normal',\n                         std=0.01,\n                         bias_prob=0.01,\n                         override=dict(name='conv_cls'))\n                 ],\n                 **kwargs) -> None:\n        assert dcn_cfg is None or isinstance(dcn_cfg, dict)\n        self.dcn_cfg = dcn_cfg\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n\n    def _init_layers(self) -> None:\n        self.mask_convs = nn.ModuleList()\n        self.cls_convs = nn.ModuleList()\n\n        for i in range(self.stacked_convs):\n            if self.dcn_cfg is not None \\\n                    and i == self.stacked_convs - 1:\n                conv_cfg = self.dcn_cfg\n            else:\n                conv_cfg = None\n\n            chn = self.in_channels + 2 if i == 0 else self.feat_channels\n            self.mask_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg))\n\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg))\n\n        self.conv_mask_list_x = nn.ModuleList()\n        self.conv_mask_list_y = nn.ModuleList()\n        for num_grid in self.num_grids:\n            self.conv_mask_list_x.append(\n                nn.Conv2d(self.feat_channels, num_grid, 3, padding=1))\n            self.conv_mask_list_y.append(\n                nn.Conv2d(self.feat_channels, num_grid, 3, padding=1))\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores and mask prediction.\n\n                - mlvl_mask_preds_x (list[Tensor]): Multi-level mask prediction\n                  from x branch. Each element in the list has shape\n                  (batch_size, num_grids ,h ,w).\n                - mlvl_mask_preds_y (list[Tensor]): Multi-level mask prediction\n                  from y branch. Each element in the list has shape\n                  (batch_size, num_grids ,h ,w).\n                - mlvl_cls_preds (list[Tensor]): Multi-level scores.\n                  Each element in the list has shape\n                  (batch_size, num_classes, num_grids ,num_grids).\n        \"\"\"\n        assert len(x) == self.num_levels\n        feats = self.resize_feats(x)\n        mask_preds_x = []\n        mask_preds_y = []\n        cls_preds = []\n        for i in range(self.num_levels):\n            x = feats[i]\n            mask_feat = x\n            cls_feat = x\n            # generate and concat the coordinate\n            coord_feat = generate_coordinate(mask_feat.size(),\n                                             mask_feat.device)\n            mask_feat = torch.cat([mask_feat, coord_feat], 1)\n\n            for mask_layer in self.mask_convs:\n                mask_feat = mask_layer(mask_feat)\n\n            mask_feat = F.interpolate(\n                mask_feat, scale_factor=2, mode='bilinear')\n\n            mask_pred_x = self.conv_mask_list_x[i](mask_feat)\n            mask_pred_y = self.conv_mask_list_y[i](mask_feat)\n\n            # cls branch\n            for j, cls_layer in enumerate(self.cls_convs):\n                if j == self.cls_down_index:\n                    num_grid = self.num_grids[i]\n                    cls_feat = F.interpolate(\n                        cls_feat, size=num_grid, mode='bilinear')\n                cls_feat = cls_layer(cls_feat)\n\n            cls_pred = self.conv_cls(cls_feat)\n\n            if not self.training:\n                feat_wh = feats[0].size()[-2:]\n                upsampled_size = (feat_wh[0] * 2, feat_wh[1] * 2)\n                mask_pred_x = F.interpolate(\n                    mask_pred_x.sigmoid(),\n                    size=upsampled_size,\n                    mode='bilinear')\n                mask_pred_y = F.interpolate(\n                    mask_pred_y.sigmoid(),\n                    size=upsampled_size,\n                    mode='bilinear')\n                cls_pred = cls_pred.sigmoid()\n                # get local maximum\n                local_max = F.max_pool2d(cls_pred, 2, stride=1, padding=1)\n                keep_mask = local_max[:, :, :-1, :-1] == cls_pred\n                cls_pred = cls_pred * keep_mask\n\n            mask_preds_x.append(mask_pred_x)\n            mask_preds_y.append(mask_pred_y)\n            cls_preds.append(cls_pred)\n        return mask_preds_x, mask_preds_y, cls_preds\n"
  },
  {
    "path": "mmdet/models/dense_heads/solov2_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import List, Optional, Tuple\n\nimport mmcv\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.utils.misc import floordiv\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig, OptConfigType\nfrom ..layers import mask_matrix_nms\nfrom ..utils import center_of_mass, generate_coordinate, multi_apply\nfrom .solo_head import SOLOHead\n\n\nclass MaskFeatModule(BaseModule):\n    \"\"\"SOLOv2 mask feature map branch used in `SOLOv2: Dynamic and Fast\n    Instance Segmentation. <https://arxiv.org/pdf/2003.10152>`_\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels of the mask feature\n             map branch.\n        start_level (int): The starting feature map level from RPN that\n             will be used to predict the mask feature map.\n        end_level (int): The ending feature map level from rpn that\n             will be used to predict the mask feature map.\n        out_channels (int): Number of output channels of the mask feature\n             map branch. This is the channel count of the mask\n             feature map that to be dynamically convolved with the predicted\n             kernel.\n        mask_stride (int): Downsample factor of the mask feature map output.\n            Defaults to 4.\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Config dict for normalization layer. Default: None.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: int,\n        feat_channels: int,\n        start_level: int,\n        end_level: int,\n        out_channels: int,\n        mask_stride: int = 4,\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        init_cfg: MultiConfig = [\n            dict(type='Normal', layer='Conv2d', std=0.01)\n        ]\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.feat_channels = feat_channels\n        self.start_level = start_level\n        self.end_level = end_level\n        self.mask_stride = mask_stride\n        assert start_level >= 0 and end_level >= start_level\n        self.out_channels = out_channels\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self._init_layers()\n        self.fp16_enabled = False\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.convs_all_levels = nn.ModuleList()\n        for i in range(self.start_level, self.end_level + 1):\n            convs_per_level = nn.Sequential()\n            if i == 0:\n                convs_per_level.add_module(\n                    f'conv{i}',\n                    ConvModule(\n                        self.in_channels,\n                        self.feat_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        inplace=False))\n                self.convs_all_levels.append(convs_per_level)\n                continue\n\n            for j in range(i):\n                if j == 0:\n                    if i == self.end_level:\n                        chn = self.in_channels + 2\n                    else:\n                        chn = self.in_channels\n                    convs_per_level.add_module(\n                        f'conv{j}',\n                        ConvModule(\n                            chn,\n                            self.feat_channels,\n                            3,\n                            padding=1,\n                            conv_cfg=self.conv_cfg,\n                            norm_cfg=self.norm_cfg,\n                            inplace=False))\n                    convs_per_level.add_module(\n                        f'upsample{j}',\n                        nn.Upsample(\n                            scale_factor=2,\n                            mode='bilinear',\n                            align_corners=False))\n                    continue\n\n                convs_per_level.add_module(\n                    f'conv{j}',\n                    ConvModule(\n                        self.feat_channels,\n                        self.feat_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        inplace=False))\n                convs_per_level.add_module(\n                    f'upsample{j}',\n                    nn.Upsample(\n                        scale_factor=2, mode='bilinear', align_corners=False))\n\n            self.convs_all_levels.append(convs_per_level)\n\n        self.conv_pred = ConvModule(\n            self.feat_channels,\n            self.out_channels,\n            1,\n            padding=0,\n            conv_cfg=self.conv_cfg,\n            norm_cfg=self.norm_cfg)\n\n    def forward(self, x: Tuple[Tensor]) -> Tensor:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            Tensor: The predicted mask feature map.\n        \"\"\"\n        inputs = x[self.start_level:self.end_level + 1]\n        assert len(inputs) == (self.end_level - self.start_level + 1)\n        feature_add_all_level = self.convs_all_levels[0](inputs[0])\n        for i in range(1, len(inputs)):\n            input_p = inputs[i]\n            if i == len(inputs) - 1:\n                coord_feat = generate_coordinate(input_p.size(),\n                                                 input_p.device)\n                input_p = torch.cat([input_p, coord_feat], 1)\n\n            feature_add_all_level = feature_add_all_level + \\\n                self.convs_all_levels[i](input_p)\n\n        feature_pred = self.conv_pred(feature_add_all_level)\n        return feature_pred\n\n\n@MODELS.register_module()\nclass SOLOV2Head(SOLOHead):\n    \"\"\"SOLOv2 mask head used in `SOLOv2: Dynamic and Fast Instance\n    Segmentation. <https://arxiv.org/pdf/2003.10152>`_\n\n    Args:\n        mask_feature_head (dict): Config of SOLOv2MaskFeatHead.\n        dynamic_conv_size (int): Dynamic Conv kernel size. Defaults to 1.\n        dcn_cfg (dict): Dcn conv configurations in kernel_convs and cls_conv.\n            Defaults to None.\n        dcn_apply_to_all_conv (bool): Whether to use dcn in every layer of\n            kernel_convs and cls_convs, or only the last layer. It shall be set\n            `True` for the normal version of SOLOv2 and `False` for the\n            light-weight version. Defaults to True.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 mask_feature_head: ConfigType,\n                 dynamic_conv_size: int = 1,\n                 dcn_cfg: OptConfigType = None,\n                 dcn_apply_to_all_conv: bool = True,\n                 init_cfg: MultiConfig = [\n                     dict(type='Normal', layer='Conv2d', std=0.01),\n                     dict(\n                         type='Normal',\n                         std=0.01,\n                         bias_prob=0.01,\n                         override=dict(name='conv_cls'))\n                 ],\n                 **kwargs) -> None:\n        assert dcn_cfg is None or isinstance(dcn_cfg, dict)\n        self.dcn_cfg = dcn_cfg\n        self.with_dcn = dcn_cfg is not None\n        self.dcn_apply_to_all_conv = dcn_apply_to_all_conv\n        self.dynamic_conv_size = dynamic_conv_size\n        mask_out_channels = mask_feature_head.get('out_channels')\n        self.kernel_out_channels = \\\n            mask_out_channels * self.dynamic_conv_size * self.dynamic_conv_size\n\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n\n        # update the in_channels of mask_feature_head\n        if mask_feature_head.get('in_channels', None) is not None:\n            if mask_feature_head.in_channels != self.in_channels:\n                warnings.warn('The `in_channels` of SOLOv2MaskFeatHead and '\n                              'SOLOv2Head should be same, changing '\n                              'mask_feature_head.in_channels to '\n                              f'{self.in_channels}')\n                mask_feature_head.update(in_channels=self.in_channels)\n        else:\n            mask_feature_head.update(in_channels=self.in_channels)\n\n        self.mask_feature_head = MaskFeatModule(**mask_feature_head)\n        self.mask_stride = self.mask_feature_head.mask_stride\n        self.fp16_enabled = False\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.cls_convs = nn.ModuleList()\n        self.kernel_convs = nn.ModuleList()\n        conv_cfg = None\n        for i in range(self.stacked_convs):\n            if self.with_dcn:\n                if self.dcn_apply_to_all_conv:\n                    conv_cfg = self.dcn_cfg\n                elif i == self.stacked_convs - 1:\n                    # light head\n                    conv_cfg = self.dcn_cfg\n\n            chn = self.in_channels + 2 if i == 0 else self.feat_channels\n            self.kernel_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    bias=self.norm_cfg is None))\n\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    bias=self.norm_cfg is None))\n\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n\n        self.conv_kernel = nn.Conv2d(\n            self.feat_channels, self.kernel_out_channels, 3, padding=1)\n\n    def forward(self, x):\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores, mask prediction,\n            and mask features.\n\n                - mlvl_kernel_preds (list[Tensor]): Multi-level dynamic kernel\n                  prediction. The kernel is used to generate instance\n                  segmentation masks by dynamic convolution. Each element in\n                  the list has shape\n                  (batch_size, kernel_out_channels, num_grids, num_grids).\n                - mlvl_cls_preds (list[Tensor]): Multi-level scores. Each\n                  element in the list has shape\n                  (batch_size, num_classes, num_grids, num_grids).\n                - mask_feats (Tensor): Unified mask feature map used to\n                  generate instance segmentation masks by dynamic convolution.\n                  Has shape (batch_size, mask_out_channels, h, w).\n        \"\"\"\n        assert len(x) == self.num_levels\n        mask_feats = self.mask_feature_head(x)\n        ins_kernel_feats = self.resize_feats(x)\n        mlvl_kernel_preds = []\n        mlvl_cls_preds = []\n        for i in range(self.num_levels):\n            ins_kernel_feat = ins_kernel_feats[i]\n            # ins branch\n            # concat coord\n            coord_feat = generate_coordinate(ins_kernel_feat.size(),\n                                             ins_kernel_feat.device)\n            ins_kernel_feat = torch.cat([ins_kernel_feat, coord_feat], 1)\n\n            # kernel branch\n            kernel_feat = ins_kernel_feat\n            kernel_feat = F.interpolate(\n                kernel_feat,\n                size=self.num_grids[i],\n                mode='bilinear',\n                align_corners=False)\n\n            cate_feat = kernel_feat[:, :-2, :, :]\n\n            kernel_feat = kernel_feat.contiguous()\n            for i, kernel_conv in enumerate(self.kernel_convs):\n                kernel_feat = kernel_conv(kernel_feat)\n            kernel_pred = self.conv_kernel(kernel_feat)\n\n            # cate branch\n            cate_feat = cate_feat.contiguous()\n            for i, cls_conv in enumerate(self.cls_convs):\n                cate_feat = cls_conv(cate_feat)\n            cate_pred = self.conv_cls(cate_feat)\n\n            mlvl_kernel_preds.append(kernel_pred)\n            mlvl_cls_preds.append(cate_pred)\n\n        return mlvl_kernel_preds, mlvl_cls_preds, mask_feats\n\n    def _get_targets_single(self,\n                            gt_instances: InstanceData,\n                            featmap_sizes: Optional[list] = None) -> tuple:\n        \"\"\"Compute targets for predictions of single image.\n\n        Args:\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes``, ``labels``,\n                and ``masks`` attributes.\n            featmap_sizes (list[:obj:`torch.size`]): Size of each\n                feature map from feature pyramid, each element\n                means (feat_h, feat_w). Defaults to None.\n\n        Returns:\n            Tuple: Usually returns a tuple containing targets for predictions.\n\n                - mlvl_pos_mask_targets (list[Tensor]): Each element represent\n                  the binary mask targets for positive points in this\n                  level, has shape (num_pos, out_h, out_w).\n                - mlvl_labels (list[Tensor]): Each element is\n                  classification labels for all\n                  points in this level, has shape\n                  (num_grid, num_grid).\n                - mlvl_pos_masks  (list[Tensor]): Each element is\n                  a `BoolTensor` to represent whether the\n                  corresponding point in single level\n                  is positive, has shape (num_grid **2).\n                - mlvl_pos_indexes  (list[list]): Each element\n                  in the list contains the positive index in\n                  corresponding level, has shape (num_pos).\n        \"\"\"\n        gt_labels = gt_instances.labels\n        device = gt_labels.device\n\n        gt_bboxes = gt_instances.bboxes\n        gt_areas = torch.sqrt((gt_bboxes[:, 2] - gt_bboxes[:, 0]) *\n                              (gt_bboxes[:, 3] - gt_bboxes[:, 1]))\n        gt_masks = gt_instances.masks.to_tensor(\n            dtype=torch.bool, device=device)\n\n        mlvl_pos_mask_targets = []\n        mlvl_pos_indexes = []\n        mlvl_labels = []\n        mlvl_pos_masks = []\n        for (lower_bound, upper_bound), num_grid \\\n                in zip(self.scale_ranges, self.num_grids):\n            mask_target = []\n            # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n            pos_index = []\n            labels = torch.zeros([num_grid, num_grid],\n                                 dtype=torch.int64,\n                                 device=device) + self.num_classes\n            pos_mask = torch.zeros([num_grid**2],\n                                   dtype=torch.bool,\n                                   device=device)\n\n            gt_inds = ((gt_areas >= lower_bound) &\n                       (gt_areas <= upper_bound)).nonzero().flatten()\n            if len(gt_inds) == 0:\n                mlvl_pos_mask_targets.append(\n                    torch.zeros([0, featmap_sizes[0], featmap_sizes[1]],\n                                dtype=torch.uint8,\n                                device=device))\n                mlvl_labels.append(labels)\n                mlvl_pos_masks.append(pos_mask)\n                mlvl_pos_indexes.append([])\n                continue\n            hit_gt_bboxes = gt_bboxes[gt_inds]\n            hit_gt_labels = gt_labels[gt_inds]\n            hit_gt_masks = gt_masks[gt_inds, ...]\n\n            pos_w_ranges = 0.5 * (hit_gt_bboxes[:, 2] -\n                                  hit_gt_bboxes[:, 0]) * self.pos_scale\n            pos_h_ranges = 0.5 * (hit_gt_bboxes[:, 3] -\n                                  hit_gt_bboxes[:, 1]) * self.pos_scale\n\n            # Make sure hit_gt_masks has a value\n            valid_mask_flags = hit_gt_masks.sum(dim=-1).sum(dim=-1) > 0\n\n            for gt_mask, gt_label, pos_h_range, pos_w_range, \\\n                valid_mask_flag in \\\n                    zip(hit_gt_masks, hit_gt_labels, pos_h_ranges,\n                        pos_w_ranges, valid_mask_flags):\n                if not valid_mask_flag:\n                    continue\n                upsampled_size = (featmap_sizes[0] * self.mask_stride,\n                                  featmap_sizes[1] * self.mask_stride)\n                center_h, center_w = center_of_mass(gt_mask)\n\n                coord_w = int(\n                    floordiv((center_w / upsampled_size[1]), (1. / num_grid),\n                             rounding_mode='trunc'))\n                coord_h = int(\n                    floordiv((center_h / upsampled_size[0]), (1. / num_grid),\n                             rounding_mode='trunc'))\n\n                # left, top, right, down\n                top_box = max(\n                    0,\n                    int(\n                        floordiv(\n                            (center_h - pos_h_range) / upsampled_size[0],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n                down_box = min(\n                    num_grid - 1,\n                    int(\n                        floordiv(\n                            (center_h + pos_h_range) / upsampled_size[0],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n                left_box = max(\n                    0,\n                    int(\n                        floordiv(\n                            (center_w - pos_w_range) / upsampled_size[1],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n                right_box = min(\n                    num_grid - 1,\n                    int(\n                        floordiv(\n                            (center_w + pos_w_range) / upsampled_size[1],\n                            (1. / num_grid),\n                            rounding_mode='trunc')))\n\n                top = max(top_box, coord_h - 1)\n                down = min(down_box, coord_h + 1)\n                left = max(coord_w - 1, left_box)\n                right = min(right_box, coord_w + 1)\n\n                labels[top:(down + 1), left:(right + 1)] = gt_label\n                # ins\n                gt_mask = np.uint8(gt_mask.cpu().numpy())\n                # Follow the original implementation, F.interpolate is\n                # different from cv2 and opencv\n                gt_mask = mmcv.imrescale(gt_mask, scale=1. / self.mask_stride)\n                gt_mask = torch.from_numpy(gt_mask).to(device=device)\n\n                for i in range(top, down + 1):\n                    for j in range(left, right + 1):\n                        index = int(i * num_grid + j)\n                        this_mask_target = torch.zeros(\n                            [featmap_sizes[0], featmap_sizes[1]],\n                            dtype=torch.uint8,\n                            device=device)\n                        this_mask_target[:gt_mask.shape[0], :gt_mask.\n                                         shape[1]] = gt_mask\n                        mask_target.append(this_mask_target)\n                        pos_mask[index] = True\n                        pos_index.append(index)\n            if len(mask_target) == 0:\n                mask_target = torch.zeros(\n                    [0, featmap_sizes[0], featmap_sizes[1]],\n                    dtype=torch.uint8,\n                    device=device)\n            else:\n                mask_target = torch.stack(mask_target, 0)\n            mlvl_pos_mask_targets.append(mask_target)\n            mlvl_labels.append(labels)\n            mlvl_pos_masks.append(pos_mask)\n            mlvl_pos_indexes.append(pos_index)\n        return (mlvl_pos_mask_targets, mlvl_labels, mlvl_pos_masks,\n                mlvl_pos_indexes)\n\n    def loss_by_feat(self, mlvl_kernel_preds: List[Tensor],\n                     mlvl_cls_preds: List[Tensor], mask_feats: Tensor,\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict], **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mlvl_kernel_preds (list[Tensor]): Multi-level dynamic kernel\n                prediction. The kernel is used to generate instance\n                segmentation masks by dynamic convolution. Each element in the\n                list has shape\n                (batch_size, kernel_out_channels, num_grids, num_grids).\n            mlvl_cls_preds (list[Tensor]): Multi-level scores. Each element\n                in the list has shape\n                (batch_size, num_classes, num_grids, num_grids).\n            mask_feats (Tensor): Unified mask feature map used to generate\n                instance segmentation masks by dynamic convolution. Has shape\n                (batch_size, mask_out_channels, h, w).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``masks``,\n                and ``labels`` attributes.\n            batch_img_metas (list[dict]): Meta information of multiple images.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = mask_feats.size()[-2:]\n\n        pos_mask_targets, labels, pos_masks, pos_indexes = multi_apply(\n            self._get_targets_single,\n            batch_gt_instances,\n            featmap_sizes=featmap_sizes)\n\n        mlvl_mask_targets = [\n            torch.cat(lvl_mask_targets, 0)\n            for lvl_mask_targets in zip(*pos_mask_targets)\n        ]\n\n        mlvl_pos_kernel_preds = []\n        for lvl_kernel_preds, lvl_pos_indexes in zip(mlvl_kernel_preds,\n                                                     zip(*pos_indexes)):\n            lvl_pos_kernel_preds = []\n            for img_lvl_kernel_preds, img_lvl_pos_indexes in zip(\n                    lvl_kernel_preds, lvl_pos_indexes):\n                img_lvl_pos_kernel_preds = img_lvl_kernel_preds.view(\n                    img_lvl_kernel_preds.shape[0], -1)[:, img_lvl_pos_indexes]\n                lvl_pos_kernel_preds.append(img_lvl_pos_kernel_preds)\n            mlvl_pos_kernel_preds.append(lvl_pos_kernel_preds)\n\n        # make multilevel mlvl_mask_pred\n        mlvl_mask_preds = []\n        for lvl_pos_kernel_preds in mlvl_pos_kernel_preds:\n            lvl_mask_preds = []\n            for img_id, img_lvl_pos_kernel_pred in enumerate(\n                    lvl_pos_kernel_preds):\n                if img_lvl_pos_kernel_pred.size()[-1] == 0:\n                    continue\n                img_mask_feats = mask_feats[[img_id]]\n                h, w = img_mask_feats.shape[-2:]\n                num_kernel = img_lvl_pos_kernel_pred.shape[1]\n                img_lvl_mask_pred = F.conv2d(\n                    img_mask_feats,\n                    img_lvl_pos_kernel_pred.permute(1, 0).view(\n                        num_kernel, -1, self.dynamic_conv_size,\n                        self.dynamic_conv_size),\n                    stride=1).view(-1, h, w)\n                lvl_mask_preds.append(img_lvl_mask_pred)\n            if len(lvl_mask_preds) == 0:\n                lvl_mask_preds = None\n            else:\n                lvl_mask_preds = torch.cat(lvl_mask_preds, 0)\n            mlvl_mask_preds.append(lvl_mask_preds)\n        # dice loss\n        num_pos = 0\n        for img_pos_masks in pos_masks:\n            for lvl_img_pos_masks in img_pos_masks:\n                # Fix `Tensor` object has no attribute `count_nonzero()`\n                # in PyTorch 1.6, the type of `lvl_img_pos_masks`\n                # should be `torch.bool`.\n                num_pos += lvl_img_pos_masks.nonzero().numel()\n        loss_mask = []\n        for lvl_mask_preds, lvl_mask_targets in zip(mlvl_mask_preds,\n                                                    mlvl_mask_targets):\n            if lvl_mask_preds is None:\n                continue\n            loss_mask.append(\n                self.loss_mask(\n                    lvl_mask_preds,\n                    lvl_mask_targets,\n                    reduction_override='none'))\n        if num_pos > 0:\n            loss_mask = torch.cat(loss_mask).sum() / num_pos\n        else:\n            loss_mask = mask_feats.sum() * 0\n\n        # cate\n        flatten_labels = [\n            torch.cat(\n                [img_lvl_labels.flatten() for img_lvl_labels in lvl_labels])\n            for lvl_labels in zip(*labels)\n        ]\n        flatten_labels = torch.cat(flatten_labels)\n\n        flatten_cls_preds = [\n            lvl_cls_preds.permute(0, 2, 3, 1).reshape(-1, self.num_classes)\n            for lvl_cls_preds in mlvl_cls_preds\n        ]\n        flatten_cls_preds = torch.cat(flatten_cls_preds)\n\n        loss_cls = self.loss_cls(\n            flatten_cls_preds, flatten_labels, avg_factor=num_pos + 1)\n        return dict(loss_mask=loss_mask, loss_cls=loss_cls)\n\n    def predict_by_feat(self, mlvl_kernel_preds: List[Tensor],\n                        mlvl_cls_scores: List[Tensor], mask_feats: Tensor,\n                        batch_img_metas: List[dict], **kwargs) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        mask results.\n\n        Args:\n            mlvl_kernel_preds (list[Tensor]): Multi-level dynamic kernel\n                prediction. The kernel is used to generate instance\n                segmentation masks by dynamic convolution. Each element in the\n                list has shape\n                (batch_size, kernel_out_channels, num_grids, num_grids).\n            mlvl_cls_scores (list[Tensor]): Multi-level scores. Each element\n                in the list has shape\n                (batch_size, num_classes, num_grids, num_grids).\n            mask_feats (Tensor): Unified mask feature map used to generate\n                instance segmentation masks by dynamic convolution. Has shape\n                (batch_size, mask_out_channels, h, w).\n            batch_img_metas (list[dict]): Meta information of all images.\n\n        Returns:\n            list[:obj:`InstanceData`]: Processed results of multiple\n            images.Each :obj:`InstanceData` usually contains\n            following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n        num_levels = len(mlvl_cls_scores)\n        assert len(mlvl_kernel_preds) == len(mlvl_cls_scores)\n\n        for lvl in range(num_levels):\n            cls_scores = mlvl_cls_scores[lvl]\n            cls_scores = cls_scores.sigmoid()\n            local_max = F.max_pool2d(cls_scores, 2, stride=1, padding=1)\n            keep_mask = local_max[:, :, :-1, :-1] == cls_scores\n            cls_scores = cls_scores * keep_mask\n            mlvl_cls_scores[lvl] = cls_scores.permute(0, 2, 3, 1)\n\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            img_cls_pred = [\n                mlvl_cls_scores[lvl][img_id].view(-1, self.cls_out_channels)\n                for lvl in range(num_levels)\n            ]\n            img_mask_feats = mask_feats[[img_id]]\n            img_kernel_pred = [\n                mlvl_kernel_preds[lvl][img_id].permute(1, 2, 0).view(\n                    -1, self.kernel_out_channels) for lvl in range(num_levels)\n            ]\n            img_cls_pred = torch.cat(img_cls_pred, dim=0)\n            img_kernel_pred = torch.cat(img_kernel_pred, dim=0)\n            result = self._predict_by_feat_single(\n                img_kernel_pred,\n                img_cls_pred,\n                img_mask_feats,\n                img_meta=batch_img_metas[img_id])\n            result_list.append(result)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                kernel_preds: Tensor,\n                                cls_scores: Tensor,\n                                mask_feats: Tensor,\n                                img_meta: dict,\n                                cfg: OptConfigType = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        mask results.\n\n        Args:\n            kernel_preds (Tensor): Dynamic kernel prediction of all points\n                in single image, has shape\n                (num_points, kernel_out_channels).\n            cls_scores (Tensor): Classification score of all points\n                in single image, has shape (num_points, num_classes).\n            mask_feats (Tensor): Mask prediction of all points in\n                single image, has shape (num_points, feat_h, feat_w).\n            img_meta (dict): Meta information of corresponding image.\n            cfg (dict, optional): Config used in test phase.\n                Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Processed results of single image.\n             it usually contains following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n\n        def empty_results(cls_scores, ori_shape):\n            \"\"\"Generate a empty results.\"\"\"\n            results = InstanceData()\n            results.scores = cls_scores.new_ones(0)\n            results.masks = cls_scores.new_zeros(0, *ori_shape)\n            results.labels = cls_scores.new_ones(0)\n            results.bboxes = cls_scores.new_zeros(0, 4)\n            return results\n\n        cfg = self.test_cfg if cfg is None else cfg\n        assert len(kernel_preds) == len(cls_scores)\n\n        featmap_size = mask_feats.size()[-2:]\n\n        # overall info\n        h, w = img_meta['img_shape'][:2]\n        upsampled_size = (featmap_size[0] * self.mask_stride,\n                          featmap_size[1] * self.mask_stride)\n\n        # process.\n        score_mask = (cls_scores > cfg.score_thr)\n        cls_scores = cls_scores[score_mask]\n        if len(cls_scores) == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n\n        # cate_labels & kernel_preds\n        inds = score_mask.nonzero()\n        cls_labels = inds[:, 1]\n        kernel_preds = kernel_preds[inds[:, 0]]\n\n        # trans vector.\n        lvl_interval = cls_labels.new_tensor(self.num_grids).pow(2).cumsum(0)\n        strides = kernel_preds.new_ones(lvl_interval[-1])\n\n        strides[:lvl_interval[0]] *= self.strides[0]\n        for lvl in range(1, self.num_levels):\n            strides[lvl_interval[lvl -\n                                 1]:lvl_interval[lvl]] *= self.strides[lvl]\n        strides = strides[inds[:, 0]]\n\n        # mask encoding.\n        kernel_preds = kernel_preds.view(\n            kernel_preds.size(0), -1, self.dynamic_conv_size,\n            self.dynamic_conv_size)\n        mask_preds = F.conv2d(\n            mask_feats, kernel_preds, stride=1).squeeze(0).sigmoid()\n        # mask.\n        masks = mask_preds > cfg.mask_thr\n        sum_masks = masks.sum((1, 2)).float()\n        keep = sum_masks > strides\n        if keep.sum() == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n        masks = masks[keep]\n        mask_preds = mask_preds[keep]\n        sum_masks = sum_masks[keep]\n        cls_scores = cls_scores[keep]\n        cls_labels = cls_labels[keep]\n\n        # maskness.\n        mask_scores = (mask_preds * masks).sum((1, 2)) / sum_masks\n        cls_scores *= mask_scores\n\n        scores, labels, _, keep_inds = mask_matrix_nms(\n            masks,\n            cls_labels,\n            cls_scores,\n            mask_area=sum_masks,\n            nms_pre=cfg.nms_pre,\n            max_num=cfg.max_per_img,\n            kernel=cfg.kernel,\n            sigma=cfg.sigma,\n            filter_thr=cfg.filter_thr)\n        if len(keep_inds) == 0:\n            return empty_results(cls_scores, img_meta['ori_shape'][:2])\n        mask_preds = mask_preds[keep_inds]\n        mask_preds = F.interpolate(\n            mask_preds.unsqueeze(0),\n            size=upsampled_size,\n            mode='bilinear',\n            align_corners=False)[:, :, :h, :w]\n        mask_preds = F.interpolate(\n            mask_preds,\n            size=img_meta['ori_shape'][:2],\n            mode='bilinear',\n            align_corners=False).squeeze(0)\n        masks = mask_preds > cfg.mask_thr\n\n        results = InstanceData()\n        results.masks = masks\n        results.labels = labels\n        results.scores = scores\n        # create an empty bbox in InstanceData to avoid bugs when\n        # calculating metrics.\n        results.bboxes = results.scores.new_zeros(len(scores), 4)\n\n        return results\n"
  },
  {
    "path": "mmdet/models/dense_heads/ssd_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig, OptInstanceList\nfrom ..losses import smooth_l1_loss\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import multi_apply\nfrom .anchor_head import AnchorHead\n\n\n# TODO: add loss evaluator for SSD\n@MODELS.register_module()\nclass SSDHead(AnchorHead):\n    \"\"\"Implementation of `SSD head <https://arxiv.org/abs/1512.02325>`_\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (Sequence[int]): Number of channels in the input feature\n            map.\n        stacked_convs (int): Number of conv layers in cls and reg tower.\n            Defaults to 0.\n        feat_channels (int): Number of hidden channels when stacked_convs\n            > 0. Defaults to 256.\n        use_depthwise (bool): Whether to use DepthwiseSeparableConv.\n            Defaults to False.\n        conv_cfg (:obj:`ConfigDict` or dict, Optional): Dictionary to construct\n            and config conv layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict, Optional): Dictionary to construct\n            and config norm layer. Defaults to None.\n        act_cfg (:obj:`ConfigDict` or dict, Optional): Dictionary to construct\n            and config activation layer. Defaults to None.\n        anchor_generator (:obj:`ConfigDict` or dict): Config dict for anchor\n            generator.\n        bbox_coder (:obj:`ConfigDict` or dict): Config of bounding box coder.\n        reg_decoded_bbox (bool): If true, the regression loss would be\n            applied directly on decoded bounding boxes, converting both\n            the predicted boxes and regression targets to absolute\n            coordinates format. Defaults to False. It should be `True` when\n            using `IoULoss`, `GIoULoss`, or `DIoULoss` in the bbox head.\n        train_cfg (:obj:`ConfigDict` or dict, Optional): Training config of\n            anchor head.\n        test_cfg (:obj:`ConfigDict` or dict, Optional): Testing config of\n            anchor head.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], Optional): Initialization config dict.\n    \"\"\"  # noqa: W605\n\n    def __init__(\n        self,\n        num_classes: int = 80,\n        in_channels: Sequence[int] = (512, 1024, 512, 256, 256, 256),\n        stacked_convs: int = 0,\n        feat_channels: int = 256,\n        use_depthwise: bool = False,\n        conv_cfg: Optional[ConfigType] = None,\n        norm_cfg: Optional[ConfigType] = None,\n        act_cfg: Optional[ConfigType] = None,\n        anchor_generator: ConfigType = dict(\n            type='SSDAnchorGenerator',\n            scale_major=False,\n            input_size=300,\n            strides=[8, 16, 32, 64, 100, 300],\n            ratios=([2], [2, 3], [2, 3], [2, 3], [2], [2]),\n            basesize_ratio_range=(0.1, 0.9)),\n        bbox_coder: ConfigType = dict(\n            type='DeltaXYWHBBoxCoder',\n            clip_border=True,\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0],\n        ),\n        reg_decoded_bbox: bool = False,\n        train_cfg: Optional[ConfigType] = None,\n        test_cfg: Optional[ConfigType] = None,\n        init_cfg: MultiConfig = dict(\n            type='Xavier', layer='Conv2d', distribution='uniform', bias=0)\n    ) -> None:\n        super(AnchorHead, self).__init__(init_cfg=init_cfg)\n        self.num_classes = num_classes\n        self.in_channels = in_channels\n        self.stacked_convs = stacked_convs\n        self.feat_channels = feat_channels\n        self.use_depthwise = use_depthwise\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n\n        self.cls_out_channels = num_classes + 1  # add background class\n        self.prior_generator = TASK_UTILS.build(anchor_generator)\n\n        # Usually the numbers of anchors for each level are the same\n        # except SSD detectors. So it is an int in the most dense\n        # heads but a list of int in SSDHead\n        self.num_base_priors = self.prior_generator.num_base_priors\n\n        self._init_layers()\n\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n        self.reg_decoded_bbox = reg_decoded_bbox\n        self.use_sigmoid_cls = False\n        self.cls_focal_loss = False\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            if self.train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler(context=self)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        # TODO: Use registry to choose ConvModule type\n        conv = DepthwiseSeparableConvModule \\\n            if self.use_depthwise else ConvModule\n\n        for channel, num_base_priors in zip(self.in_channels,\n                                            self.num_base_priors):\n            cls_layers = []\n            reg_layers = []\n            in_channel = channel\n            # build stacked conv tower, not used in default ssd\n            for i in range(self.stacked_convs):\n                cls_layers.append(\n                    conv(\n                        in_channel,\n                        self.feat_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n                reg_layers.append(\n                    conv(\n                        in_channel,\n                        self.feat_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n                in_channel = self.feat_channels\n            # SSD-Lite head\n            if self.use_depthwise:\n                cls_layers.append(\n                    ConvModule(\n                        in_channel,\n                        in_channel,\n                        3,\n                        padding=1,\n                        groups=in_channel,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n                reg_layers.append(\n                    ConvModule(\n                        in_channel,\n                        in_channel,\n                        3,\n                        padding=1,\n                        groups=in_channel,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg,\n                        act_cfg=self.act_cfg))\n            cls_layers.append(\n                nn.Conv2d(\n                    in_channel,\n                    num_base_priors * self.cls_out_channels,\n                    kernel_size=1 if self.use_depthwise else 3,\n                    padding=0 if self.use_depthwise else 1))\n            reg_layers.append(\n                nn.Conv2d(\n                    in_channel,\n                    num_base_priors * 4,\n                    kernel_size=1 if self.use_depthwise else 3,\n                    padding=0 if self.use_depthwise else 1))\n            self.cls_convs.append(nn.Sequential(*cls_layers))\n            self.reg_convs.append(nn.Sequential(*reg_layers))\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple[list[Tensor], list[Tensor]]: A tuple of cls_scores list and\n            bbox_preds list.\n\n            - cls_scores (list[Tensor]): Classification scores for all scale \\\n            levels, each is a 4D-tensor, the channels number is \\\n            num_anchors * num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for all scale \\\n            levels, each is a 4D-tensor, the channels number is \\\n            num_anchors * 4.\n        \"\"\"\n        cls_scores = []\n        bbox_preds = []\n        for feat, reg_conv, cls_conv in zip(x, self.reg_convs, self.cls_convs):\n            cls_scores.append(cls_conv(feat))\n            bbox_preds.append(reg_conv(feat))\n        return cls_scores, bbox_preds\n\n    def loss_by_feat_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            anchor: Tensor, labels: Tensor,\n                            label_weights: Tensor, bbox_targets: Tensor,\n                            bbox_weights: Tensor,\n                            avg_factor: int) -> Tuple[Tensor, Tensor]:\n        \"\"\"Compute loss of a single image.\n\n        Args:\n            cls_score (Tensor): Box scores for eachimage\n                Has shape (num_total_anchors, num_classes).\n            bbox_pred (Tensor): Box energies / deltas for each image\n                level with shape (num_total_anchors, 4).\n            anchors (Tensor): Box reference for each scale level with shape\n                (num_total_anchors, 4).\n            labels (Tensor): Labels of each anchors with shape\n                (num_total_anchors,).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (num_total_anchors,)\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (num_total_anchors, 4).\n            bbox_weights (Tensor): BBox regression loss weights of each anchor\n                with shape (num_total_anchors, 4).\n            avg_factor (int): Average factor that is used to average\n                the loss. When using sampling method, avg_factor is usually\n                the sum of positive and negative priors. When using\n                `PseudoSampler`, `avg_factor` is usually equal to the number\n                of positive priors.\n\n        Returns:\n            Tuple[Tensor, Tensor]: A tuple of cls loss and bbox loss of one\n            feature map.\n        \"\"\"\n\n        loss_cls_all = F.cross_entropy(\n            cls_score, labels, reduction='none') * label_weights\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        pos_inds = ((labels >= 0) & (labels < self.num_classes)).nonzero(\n            as_tuple=False).reshape(-1)\n        neg_inds = (labels == self.num_classes).nonzero(\n            as_tuple=False).view(-1)\n\n        num_pos_samples = pos_inds.size(0)\n        num_neg_samples = self.train_cfg['neg_pos_ratio'] * num_pos_samples\n        if num_neg_samples > neg_inds.size(0):\n            num_neg_samples = neg_inds.size(0)\n        topk_loss_cls_neg, _ = loss_cls_all[neg_inds].topk(num_neg_samples)\n        loss_cls_pos = loss_cls_all[pos_inds].sum()\n        loss_cls_neg = topk_loss_cls_neg.sum()\n        loss_cls = (loss_cls_pos + loss_cls_neg) / avg_factor\n\n        if self.reg_decoded_bbox:\n            # When the regression loss (e.g. `IouLoss`, `GIouLoss`)\n            # is applied directly on the decoded bounding boxes, it\n            # decodes the already encoded coordinates to absolute format.\n            bbox_pred = self.bbox_coder.decode(anchor, bbox_pred)\n\n        loss_bbox = smooth_l1_loss(\n            bbox_pred,\n            bbox_targets,\n            bbox_weights,\n            beta=self.train_cfg['smoothl1_beta'],\n            avg_factor=avg_factor)\n        return loss_cls[None], loss_bbox\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, List[Tensor]]:\n        \"\"\"Compute losses of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, list[Tensor]]: A dictionary of loss components. the dict\n            has components below:\n\n            - loss_cls (list[Tensor]): A list containing each feature map \\\n            classification loss.\n            - loss_bbox (list[Tensor]): A list containing each feature map \\\n            regression loss.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            unmap_outputs=True)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor) = cls_reg_targets\n\n        num_images = len(batch_img_metas)\n        all_cls_scores = torch.cat([\n            s.permute(0, 2, 3, 1).reshape(\n                num_images, -1, self.cls_out_channels) for s in cls_scores\n        ], 1)\n        all_labels = torch.cat(labels_list, -1).view(num_images, -1)\n        all_label_weights = torch.cat(label_weights_list,\n                                      -1).view(num_images, -1)\n        all_bbox_preds = torch.cat([\n            b.permute(0, 2, 3, 1).reshape(num_images, -1, 4)\n            for b in bbox_preds\n        ], -2)\n        all_bbox_targets = torch.cat(bbox_targets_list,\n                                     -2).view(num_images, -1, 4)\n        all_bbox_weights = torch.cat(bbox_weights_list,\n                                     -2).view(num_images, -1, 4)\n\n        # concat all level anchors to a single tensor\n        all_anchors = []\n        for i in range(num_images):\n            all_anchors.append(torch.cat(anchor_list[i]))\n\n        losses_cls, losses_bbox = multi_apply(\n            self.loss_by_feat_single,\n            all_cls_scores,\n            all_bbox_preds,\n            all_anchors,\n            all_labels,\n            all_label_weights,\n            all_bbox_targets,\n            all_bbox_weights,\n            avg_factor=avg_factor)\n        return dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n"
  },
  {
    "path": "mmdet/models/dense_heads/tood_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, Scale\nfrom mmcv.ops import deform_conv2d\nfrom mmengine import MessageHub\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import bias_init_with_prob, normal_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import distance2bbox\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, reduce_mean)\nfrom ..task_modules.prior_generators import anchor_inside_flags\nfrom ..utils import (filter_scores_and_topk, images_to_levels, multi_apply,\n                     sigmoid_geometric_mean, unmap)\nfrom .atss_head import ATSSHead\n\n\nclass TaskDecomposition(nn.Module):\n    \"\"\"Task decomposition module in task-aligned predictor of TOOD.\n\n    Args:\n        feat_channels (int): Number of feature channels in TOOD head.\n        stacked_convs (int): Number of conv layers in TOOD head.\n        la_down_rate (int): Downsample rate of layer attention.\n            Defaults to 8.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict, optional):  Config dict for\n        normalization layer. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 feat_channels: int,\n                 stacked_convs: int,\n                 la_down_rate: int = 8,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None) -> None:\n        super().__init__()\n        self.feat_channels = feat_channels\n        self.stacked_convs = stacked_convs\n        self.in_channels = self.feat_channels * self.stacked_convs\n        self.norm_cfg = norm_cfg\n        self.layer_attention = nn.Sequential(\n            nn.Conv2d(self.in_channels, self.in_channels // la_down_rate, 1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(\n                self.in_channels // la_down_rate,\n                self.stacked_convs,\n                1,\n                padding=0), nn.Sigmoid())\n\n        self.reduction_conv = ConvModule(\n            self.in_channels,\n            self.feat_channels,\n            1,\n            stride=1,\n            padding=0,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            bias=norm_cfg is None)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize the parameters.\"\"\"\n        for m in self.layer_attention.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, std=0.001)\n        normal_init(self.reduction_conv.conv, std=0.01)\n\n    def forward(self,\n                feat: Tensor,\n                avg_feat: Optional[Tensor] = None) -> Tensor:\n        \"\"\"Forward function of task decomposition module.\"\"\"\n        b, c, h, w = feat.shape\n        if avg_feat is None:\n            avg_feat = F.adaptive_avg_pool2d(feat, (1, 1))\n        weight = self.layer_attention(avg_feat)\n\n        # here we first compute the product between layer attention weight and\n        # conv weight, and then compute the convolution between new conv weight\n        # and feature map, in order to save memory and FLOPs.\n        conv_weight = weight.reshape(\n            b, 1, self.stacked_convs,\n            1) * self.reduction_conv.conv.weight.reshape(\n                1, self.feat_channels, self.stacked_convs, self.feat_channels)\n        conv_weight = conv_weight.reshape(b, self.feat_channels,\n                                          self.in_channels)\n        feat = feat.reshape(b, self.in_channels, h * w)\n        feat = torch.bmm(conv_weight, feat).reshape(b, self.feat_channels, h,\n                                                    w)\n        if self.norm_cfg is not None:\n            feat = self.reduction_conv.norm(feat)\n        feat = self.reduction_conv.activate(feat)\n\n        return feat\n\n\n@MODELS.register_module()\nclass TOODHead(ATSSHead):\n    \"\"\"TOODHead used in `TOOD: Task-aligned One-stage Object Detection.\n\n    <https://arxiv.org/abs/2108.07755>`_.\n\n    TOOD uses Task-aligned head (T-head) and is optimized by Task Alignment\n    Learning (TAL).\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        num_dcn (int): Number of deformable convolution in the head.\n            Defaults to 0.\n        anchor_type (str): If set to ``anchor_free``, the head will use centers\n            to regress bboxes. If set to ``anchor_based``, the head will\n            regress bboxes based on anchors. Defaults to ``anchor_free``.\n        initial_loss_cls (:obj:`ConfigDict` or dict): Config of initial loss.\n\n    Example:\n        >>> self = TOODHead(11, 7)\n        >>> feats = [torch.rand(1, 7, s, s) for s in [4, 8, 16, 32, 64]]\n        >>> cls_score, bbox_pred = self.forward(feats)\n        >>> assert len(cls_score) == len(self.scales)\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 num_dcn: int = 0,\n                 anchor_type: str = 'anchor_free',\n                 initial_loss_cls: ConfigType = dict(\n                     type='FocalLoss',\n                     use_sigmoid=True,\n                     activated=True,\n                     gamma=2.0,\n                     alpha=0.25,\n                     loss_weight=1.0),\n                 **kwargs) -> None:\n        assert anchor_type in ['anchor_free', 'anchor_based']\n        self.num_dcn = num_dcn\n        self.anchor_type = anchor_type\n        super().__init__(\n            num_classes=num_classes, in_channels=in_channels, **kwargs)\n\n        if self.train_cfg:\n            self.initial_epoch = self.train_cfg['initial_epoch']\n            self.initial_assigner = TASK_UTILS.build(\n                self.train_cfg['initial_assigner'])\n            self.initial_loss_cls = MODELS.build(initial_loss_cls)\n            self.assigner = self.initial_assigner\n            self.alignment_assigner = TASK_UTILS.build(\n                self.train_cfg['assigner'])\n            self.alpha = self.train_cfg['alpha']\n            self.beta = self.train_cfg['beta']\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.inter_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            if i < self.num_dcn:\n                conv_cfg = dict(type='DCNv2', deform_groups=4)\n            else:\n                conv_cfg = self.conv_cfg\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.inter_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg))\n\n        self.cls_decomp = TaskDecomposition(self.feat_channels,\n                                            self.stacked_convs,\n                                            self.stacked_convs * 8,\n                                            self.conv_cfg, self.norm_cfg)\n        self.reg_decomp = TaskDecomposition(self.feat_channels,\n                                            self.stacked_convs,\n                                            self.stacked_convs * 8,\n                                            self.conv_cfg, self.norm_cfg)\n\n        self.tood_cls = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * self.cls_out_channels,\n            3,\n            padding=1)\n        self.tood_reg = nn.Conv2d(\n            self.feat_channels, self.num_base_priors * 4, 3, padding=1)\n\n        self.cls_prob_module = nn.Sequential(\n            nn.Conv2d(self.feat_channels * self.stacked_convs,\n                      self.feat_channels // 4, 1), nn.ReLU(inplace=True),\n            nn.Conv2d(self.feat_channels // 4, 1, 3, padding=1))\n        self.reg_offset_module = nn.Sequential(\n            nn.Conv2d(self.feat_channels * self.stacked_convs,\n                      self.feat_channels // 4, 1), nn.ReLU(inplace=True),\n            nn.Conv2d(self.feat_channels // 4, 4 * 2, 3, padding=1))\n\n        self.scales = nn.ModuleList(\n            [Scale(1.0) for _ in self.prior_generator.strides])\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        bias_cls = bias_init_with_prob(0.01)\n        for m in self.inter_convs:\n            normal_init(m.conv, std=0.01)\n        for m in self.cls_prob_module:\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, std=0.01)\n        for m in self.reg_offset_module:\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, std=0.001)\n        normal_init(self.cls_prob_module[-1], std=0.01, bias=bias_cls)\n\n        self.cls_decomp.init_weights()\n        self.reg_decomp.init_weights()\n\n        normal_init(self.tood_cls, std=0.01, bias=bias_cls)\n        normal_init(self.tood_reg, std=0.01)\n\n    def forward(self, feats: Tuple[Tensor]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            feats (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: Usually a tuple of classification scores and bbox prediction\n                cls_scores (list[Tensor]): Classification scores for all scale\n                    levels, each is a 4D-tensor, the channels number is\n                    num_anchors * num_classes.\n                bbox_preds (list[Tensor]): Decoded box for all scale levels,\n                    each is a 4D-tensor, the channels number is\n                    num_anchors * 4. In [tl_x, tl_y, br_x, br_y] format.\n        \"\"\"\n        cls_scores = []\n        bbox_preds = []\n        for idx, (x, scale, stride) in enumerate(\n                zip(feats, self.scales, self.prior_generator.strides)):\n            b, c, h, w = x.shape\n            anchor = self.prior_generator.single_level_grid_priors(\n                (h, w), idx, device=x.device)\n            anchor = torch.cat([anchor for _ in range(b)])\n            # extract task interactive features\n            inter_feats = []\n            for inter_conv in self.inter_convs:\n                x = inter_conv(x)\n                inter_feats.append(x)\n            feat = torch.cat(inter_feats, 1)\n\n            # task decomposition\n            avg_feat = F.adaptive_avg_pool2d(feat, (1, 1))\n            cls_feat = self.cls_decomp(feat, avg_feat)\n            reg_feat = self.reg_decomp(feat, avg_feat)\n\n            # cls prediction and alignment\n            cls_logits = self.tood_cls(cls_feat)\n            cls_prob = self.cls_prob_module(feat)\n            cls_score = sigmoid_geometric_mean(cls_logits, cls_prob)\n\n            # reg prediction and alignment\n            if self.anchor_type == 'anchor_free':\n                reg_dist = scale(self.tood_reg(reg_feat).exp()).float()\n                reg_dist = reg_dist.permute(0, 2, 3, 1).reshape(-1, 4)\n                reg_bbox = distance2bbox(\n                    self.anchor_center(anchor) / stride[0],\n                    reg_dist).reshape(b, h, w, 4).permute(0, 3, 1,\n                                                          2)  # (b, c, h, w)\n            elif self.anchor_type == 'anchor_based':\n                reg_dist = scale(self.tood_reg(reg_feat)).float()\n                reg_dist = reg_dist.permute(0, 2, 3, 1).reshape(-1, 4)\n                reg_bbox = self.bbox_coder.decode(anchor, reg_dist).reshape(\n                    b, h, w, 4).permute(0, 3, 1, 2) / stride[0]\n            else:\n                raise NotImplementedError(\n                    f'Unknown anchor type: {self.anchor_type}.'\n                    f'Please use `anchor_free` or `anchor_based`.')\n            reg_offset = self.reg_offset_module(feat)\n            bbox_pred = self.deform_sampling(reg_bbox.contiguous(),\n                                             reg_offset.contiguous())\n\n            # After deform_sampling, some boxes will become invalid (The\n            # left-top point is at the right or bottom of the right-bottom\n            # point), which will make the GIoULoss negative.\n            invalid_bbox_idx = (bbox_pred[:, [0]] > bbox_pred[:, [2]]) | \\\n                               (bbox_pred[:, [1]] > bbox_pred[:, [3]])\n            invalid_bbox_idx = invalid_bbox_idx.expand_as(bbox_pred)\n            bbox_pred = torch.where(invalid_bbox_idx, reg_bbox, bbox_pred)\n\n            cls_scores.append(cls_score)\n            bbox_preds.append(bbox_pred)\n        return tuple(cls_scores), tuple(bbox_preds)\n\n    def deform_sampling(self, feat: Tensor, offset: Tensor) -> Tensor:\n        \"\"\"Sampling the feature x according to offset.\n\n        Args:\n            feat (Tensor): Feature\n            offset (Tensor): Spatial offset for feature sampling\n        \"\"\"\n        # it is an equivalent implementation of bilinear interpolation\n        b, c, h, w = feat.shape\n        weight = feat.new_ones(c, 1, 1, 1)\n        y = deform_conv2d(feat, offset, weight, 1, 0, 1, c, c)\n        return y\n\n    def anchor_center(self, anchors: Tensor) -> Tensor:\n        \"\"\"Get anchor centers from anchors.\n\n        Args:\n            anchors (Tensor): Anchor list with shape (N, 4), \"xyxy\" format.\n\n        Returns:\n            Tensor: Anchor centers with shape (N, 2), \"xy\" format.\n        \"\"\"\n        anchors_cx = (anchors[:, 2] + anchors[:, 0]) / 2\n        anchors_cy = (anchors[:, 3] + anchors[:, 1]) / 2\n        return torch.stack([anchors_cx, anchors_cy], dim=-1)\n\n    def loss_by_feat_single(self, anchors: Tensor, cls_score: Tensor,\n                            bbox_pred: Tensor, labels: Tensor,\n                            label_weights: Tensor, bbox_targets: Tensor,\n                            alignment_metrics: Tensor,\n                            stride: Tuple[int, int]) -> dict:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            anchors (Tensor): Box reference for each scale level with shape\n                (N, num_total_anchors, 4).\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W).\n            bbox_pred (Tensor): Decoded bboxes for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors).\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (N, num_total_anchors, 4).\n            alignment_metrics (Tensor): Alignment metrics with shape\n                (N, num_total_anchors).\n            stride (Tuple[int, int]): Downsample stride of the feature map.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert stride[0] == stride[1], 'h stride is not equal to w stride!'\n        anchors = anchors.reshape(-1, 4)\n        cls_score = cls_score.permute(0, 2, 3, 1).reshape(\n            -1, self.cls_out_channels).contiguous()\n        bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        alignment_metrics = alignment_metrics.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        targets = labels if self.epoch < self.initial_epoch else (\n            labels, alignment_metrics)\n        cls_loss_func = self.initial_loss_cls \\\n            if self.epoch < self.initial_epoch else self.loss_cls\n\n        loss_cls = cls_loss_func(\n            cls_score, targets, label_weights, avg_factor=1.0)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().squeeze(1)\n\n        if len(pos_inds) > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n            pos_anchors = anchors[pos_inds]\n\n            pos_decode_bbox_pred = pos_bbox_pred\n            pos_decode_bbox_targets = pos_bbox_targets / stride[0]\n\n            # regression loss\n            pos_bbox_weight = self.centerness_target(\n                pos_anchors, pos_bbox_targets\n            ) if self.epoch < self.initial_epoch else alignment_metrics[\n                pos_inds]\n\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_decode_bbox_targets,\n                weight=pos_bbox_weight,\n                avg_factor=1.0)\n        else:\n            loss_bbox = bbox_pred.sum() * 0\n            pos_bbox_weight = bbox_targets.new_tensor(0.)\n\n        return loss_cls, loss_bbox, alignment_metrics.sum(\n        ), pos_bbox_weight.sum()\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Decoded box for each scale\n                level with shape (N, num_anchors * 4, H, W) in\n                [tl_x, tl_y, br_x, br_y] format.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        flatten_cls_scores = torch.cat([\n            cls_score.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                  self.cls_out_channels)\n            for cls_score in cls_scores\n        ], 1)\n        flatten_bbox_preds = torch.cat([\n            bbox_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1, 4) * stride[0]\n            for bbox_pred, stride in zip(bbox_preds,\n                                         self.prior_generator.strides)\n        ], 1)\n\n        cls_reg_targets = self.get_targets(\n            flatten_cls_scores,\n            flatten_bbox_preds,\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         alignment_metrics_list) = cls_reg_targets\n\n        losses_cls, losses_bbox, \\\n            cls_avg_factors, bbox_avg_factors = multi_apply(\n                self.loss_by_feat_single,\n                anchor_list,\n                cls_scores,\n                bbox_preds,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                alignment_metrics_list,\n                self.prior_generator.strides)\n\n        cls_avg_factor = reduce_mean(sum(cls_avg_factors)).clamp_(min=1).item()\n        losses_cls = list(map(lambda x: x / cls_avg_factor, losses_cls))\n\n        bbox_avg_factor = reduce_mean(\n            sum(bbox_avg_factors)).clamp_(min=1).item()\n        losses_bbox = list(map(lambda x: x / bbox_avg_factor, losses_bbox))\n        return dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: Optional[ConfigDict] = None,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid. In all\n                anchor-based methods, it has shape (num_priors, 4). In\n                all anchor-free methods, it has shape (num_priors, 2)\n                when `with_stride=True`, otherwise it still has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (:obj:`ConfigDict`, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            tuple[Tensor]: Results of detected bboxes and labels. If with_nms\n                is False and mlvl_score_factor is None, return mlvl_bboxes and\n                mlvl_scores, else return mlvl_bboxes, mlvl_scores and\n                mlvl_score_factor. Usually with_nms is False is used for aug\n                test. If with_nms is True, then return the following format\n\n                - det_bboxes (Tensor): Predicted bboxes with shape \\\n                    [num_bboxes, 5], where the first 4 columns are bounding \\\n                    box positions (tl_x, tl_y, br_x, br_y) and the 5-th \\\n                    column are scores between 0 and 1.\n                - det_labels (Tensor): Predicted labels of the corresponding \\\n                    box with shape [num_bboxes].\n        \"\"\"\n\n        cfg = self.test_cfg if cfg is None else cfg\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bboxes = []\n        mlvl_scores = []\n        mlvl_labels = []\n        for cls_score, bbox_pred, priors, stride in zip(\n                cls_score_list, bbox_pred_list, mlvl_priors,\n                self.prior_generator.strides):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4) * stride[0]\n            scores = cls_score.permute(1, 2,\n                                       0).reshape(-1, self.cls_out_channels)\n\n            # After https://github.com/open-mmlab/mmdetection/pull/6268/,\n            # this operation keeps fewer bboxes under the same `nms_pre`.\n            # There is no difference in performance for most models. If you\n            # find a slight drop in performance, you can set a larger\n            # `nms_pre` than before.\n            results = filter_scores_and_topk(\n                scores, cfg.score_thr, nms_pre,\n                dict(bbox_pred=bbox_pred, priors=priors))\n            scores, labels, keep_idxs, filtered_results = results\n\n            bboxes = filtered_results['bbox_pred']\n\n            mlvl_bboxes.append(bboxes)\n            mlvl_scores.append(scores)\n            mlvl_labels.append(labels)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bboxes)\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n    def get_targets(self,\n                    cls_scores: List[List[Tensor]],\n                    bbox_preds: List[List[Tensor]],\n                    anchor_list: List[List[Tensor]],\n                    valid_flag_list: List[List[Tensor]],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors in\n        multiple images.\n\n        Args:\n            cls_scores (list[list[Tensor]]): Classification predictions of\n                images, a 3D-Tensor with shape [num_imgs, num_priors,\n                num_classes].\n            bbox_preds (list[list[Tensor]]): Decoded bboxes predictions of one\n                image, a 3D-Tensor with shape [num_imgs, num_priors, 4] in\n                [tl_x, tl_y, br_x, br_y] format.\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, 4).\n            valid_flag_list (list[list[Tensor]]): Multi level valid flags of\n                each image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_anchors, )\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Returns:\n            tuple: a tuple containing learning targets.\n\n                - anchors_list (list[list[Tensor]]): Anchors of each level.\n                - labels_list (list[Tensor]): Labels of each level.\n                - label_weights_list (list[Tensor]): Label weights of each\n                  level.\n                - bbox_targets_list (list[Tensor]): BBox targets of each level.\n                - norm_alignment_metrics_list (list[Tensor]): Normalized\n                  alignment metrics of each level.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        num_level_anchors_list = [num_level_anchors] * num_imgs\n\n        # concat all level anchors and flags to a single tensor\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            anchor_list[i] = torch.cat(anchor_list[i])\n            valid_flag_list[i] = torch.cat(valid_flag_list[i])\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        # anchor_list: list(b * [-1, 4])\n\n        # get epoch information from message hub\n        message_hub = MessageHub.get_current_instance()\n        self.epoch = message_hub.get_info('epoch')\n\n        if self.epoch < self.initial_epoch:\n            (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n             all_bbox_weights, pos_inds_list, neg_inds_list,\n             sampling_result) = multi_apply(\n                 super()._get_targets_single,\n                 anchor_list,\n                 valid_flag_list,\n                 num_level_anchors_list,\n                 batch_gt_instances,\n                 batch_img_metas,\n                 batch_gt_instances_ignore,\n                 unmap_outputs=unmap_outputs)\n            all_assign_metrics = [\n                weight[..., 0] for weight in all_bbox_weights\n            ]\n        else:\n            (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n             all_assign_metrics) = multi_apply(\n                 self._get_targets_single,\n                 cls_scores,\n                 bbox_preds,\n                 anchor_list,\n                 valid_flag_list,\n                 batch_gt_instances,\n                 batch_img_metas,\n                 batch_gt_instances_ignore,\n                 unmap_outputs=unmap_outputs)\n\n        # split targets to a list w.r.t. multiple levels\n        anchors_list = images_to_levels(all_anchors, num_level_anchors)\n        labels_list = images_to_levels(all_labels, num_level_anchors)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors)\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors)\n        norm_alignment_metrics_list = images_to_levels(all_assign_metrics,\n                                                       num_level_anchors)\n\n        return (anchors_list, labels_list, label_weights_list,\n                bbox_targets_list, norm_alignment_metrics_list)\n\n    def _get_targets_single(self,\n                            cls_scores: Tensor,\n                            bbox_preds: Tensor,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression, classification targets for anchors in a single\n        image.\n\n        Args:\n            cls_scores (Tensor): Box scores for each image.\n            bbox_preds (Tensor): Box energies / deltas for each image.\n            flat_anchors (Tensor): Multi-level anchors of the image, which are\n                concatenated into a single tensor of shape (num_anchors ,4)\n            valid_flags (Tensor): Multi level valid flags of the image,\n                which are concatenated into a single tensor of\n                    shape (num_anchors,).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Returns:\n            tuple: N is the number of total anchors in the image.\n                anchors (Tensor): All anchors in the image with shape (N, 4).\n                labels (Tensor): Labels of all anchors in the image with shape\n                    (N,).\n                label_weights (Tensor): Label weights of all anchor in the\n                    image with shape (N,).\n                bbox_targets (Tensor): BBox targets of all anchors in the\n                    image with shape (N, 4).\n                norm_alignment_metrics (Tensor): Normalized alignment metrics\n                    of all priors in the image with shape (N,).\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n        # assign gt and sample anchors\n        anchors = flat_anchors[inside_flags, :]\n        pred_instances = InstanceData(\n            priors=anchors,\n            scores=cls_scores[inside_flags, :],\n            bboxes=bbox_preds[inside_flags, :])\n        assign_result = self.alignment_assigner.assign(pred_instances,\n                                                       gt_instances,\n                                                       gt_instances_ignore,\n                                                       self.alpha, self.beta)\n        assign_ious = assign_result.max_overlaps\n        assign_metrics = assign_result.assign_metrics\n\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_anchors = anchors.shape[0]\n        bbox_targets = torch.zeros_like(anchors)\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n        norm_alignment_metrics = anchors.new_zeros(\n            num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            # point-based\n            pos_bbox_targets = sampling_result.pos_gt_bboxes\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        class_assigned_gt_inds = torch.unique(\n            sampling_result.pos_assigned_gt_inds)\n        for gt_inds in class_assigned_gt_inds:\n            gt_class_inds = pos_inds[sampling_result.pos_assigned_gt_inds ==\n                                     gt_inds]\n            pos_alignment_metrics = assign_metrics[gt_class_inds]\n            pos_ious = assign_ious[gt_class_inds]\n            pos_norm_alignment_metrics = pos_alignment_metrics / (\n                pos_alignment_metrics.max() + 10e-8) * pos_ious.max()\n            norm_alignment_metrics[gt_class_inds] = pos_norm_alignment_metrics\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            anchors = unmap(anchors, num_total_anchors, inside_flags)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags, fill=self.num_classes)\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n            bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags)\n            norm_alignment_metrics = unmap(norm_alignment_metrics,\n                                           num_total_anchors, inside_flags)\n        return (anchors, labels, label_weights, bbox_targets,\n                norm_alignment_metrics)\n"
  },
  {
    "path": "mmdet/models/dense_heads/vfnet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, Scale\nfrom mmcv.ops import DeformConv2d\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig,\n                         OptInstanceList, RangeType, reduce_mean)\nfrom ..task_modules.prior_generators import MlvlPointGenerator\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import multi_apply\nfrom .atss_head import ATSSHead\nfrom .fcos_head import FCOSHead\n\nINF = 1e8\n\n\n@MODELS.register_module()\nclass VFNetHead(ATSSHead, FCOSHead):\n    \"\"\"Head of `VarifocalNet (VFNet): An IoU-aware Dense Object\n    Detector.<https://arxiv.org/abs/2008.13367>`_.\n\n    The VFNet predicts IoU-aware classification scores which mix the\n    object presence confidence and object localization accuracy as the\n    detection score. It is built on the FCOS architecture and uses ATSS\n    for defining positive/negative training examples. The VFNet is trained\n    with Varifocal Loss and empolys star-shaped deformable convolution to\n    extract features for a bbox.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        regress_ranges (Sequence[Tuple[int, int]]): Regress range of multiple\n            level points.\n        center_sampling (bool): If true, use center sampling. Defaults to False.\n        center_sample_radius (float): Radius of center sampling. Defaults to 1.5.\n        sync_num_pos (bool): If true, synchronize the number of positive\n            examples across GPUs. Defaults to True\n        gradient_mul (float): The multiplier to gradients from bbox refinement\n            and recognition. Defaults to 0.1.\n        bbox_norm_type (str): The bbox normalization type, 'reg_denom' or\n            'stride'. Defaults to reg_denom\n        loss_cls_fl (:obj:`ConfigDict` or dict): Config of focal loss.\n        use_vfl (bool): If true, use varifocal loss for training.\n            Defaults to True.\n        loss_cls (:obj:`ConfigDict` or dict): Config of varifocal loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of localization loss,\n            GIoU Loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of localization\n            refinement loss, GIoU Loss.\n        norm_cfg (:obj:`ConfigDict` or dict): dictionary to construct and\n            config norm layer. Defaults to norm_cfg=dict(type='GN',\n            num_groups=32, requires_grad=True).\n        use_atss (bool): If true, use ATSS to define positive/negative\n            examples. Defaults to True.\n        anchor_generator (:obj:`ConfigDict` or dict): Config of anchor\n            generator for ATSS.\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`]): Initialization config dict.\n\n    Example:\n        >>> self = VFNetHead(11, 7)\n        >>> feats = [torch.rand(1, 7, s, s) for s in [4, 8, 16, 32, 64]]\n        >>> cls_score, bbox_pred, bbox_pred_refine= self.forward(feats)\n        >>> assert len(cls_score) == len(self.scales)\n    \"\"\"  # noqa: E501\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 regress_ranges: RangeType = ((-1, 64), (64, 128), (128, 256),\n                                              (256, 512), (512, INF)),\n                 center_sampling: bool = False,\n                 center_sample_radius: float = 1.5,\n                 sync_num_pos: bool = True,\n                 gradient_mul: float = 0.1,\n                 bbox_norm_type: str = 'reg_denom',\n                 loss_cls_fl: ConfigType = dict(\n                     type='FocalLoss',\n                     use_sigmoid=True,\n                     gamma=2.0,\n                     alpha=0.25,\n                     loss_weight=1.0),\n                 use_vfl: bool = True,\n                 loss_cls: ConfigType = dict(\n                     type='VarifocalLoss',\n                     use_sigmoid=True,\n                     alpha=0.75,\n                     gamma=2.0,\n                     iou_weighted=True,\n                     loss_weight=1.0),\n                 loss_bbox: ConfigType = dict(\n                     type='GIoULoss', loss_weight=1.5),\n                 loss_bbox_refine: ConfigType = dict(\n                     type='GIoULoss', loss_weight=2.0),\n                 norm_cfg: ConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 use_atss: bool = True,\n                 reg_decoded_bbox: bool = True,\n                 anchor_generator: ConfigType = dict(\n                     type='AnchorGenerator',\n                     ratios=[1.0],\n                     octave_base_scale=8,\n                     scales_per_octave=1,\n                     center_offset=0.0,\n                     strides=[8, 16, 32, 64, 128]),\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     layer='Conv2d',\n                     std=0.01,\n                     override=dict(\n                         type='Normal',\n                         name='vfnet_cls',\n                         std=0.01,\n                         bias_prob=0.01)),\n                 **kwargs) -> None:\n        # dcn base offsets, adapted from reppoints_head.py\n        self.num_dconv_points = 9\n        self.dcn_kernel = int(np.sqrt(self.num_dconv_points))\n        self.dcn_pad = int((self.dcn_kernel - 1) / 2)\n        dcn_base = np.arange(-self.dcn_pad,\n                             self.dcn_pad + 1).astype(np.float64)\n        dcn_base_y = np.repeat(dcn_base, self.dcn_kernel)\n        dcn_base_x = np.tile(dcn_base, self.dcn_kernel)\n        dcn_base_offset = np.stack([dcn_base_y, dcn_base_x], axis=1).reshape(\n            (-1))\n        self.dcn_base_offset = torch.tensor(dcn_base_offset).view(1, -1, 1, 1)\n\n        super(FCOSHead, self).__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            norm_cfg=norm_cfg,\n            init_cfg=init_cfg,\n            **kwargs)\n        self.regress_ranges = regress_ranges\n        self.reg_denoms = [\n            regress_range[-1] for regress_range in regress_ranges\n        ]\n        self.reg_denoms[-1] = self.reg_denoms[-2] * 2\n        self.center_sampling = center_sampling\n        self.center_sample_radius = center_sample_radius\n        self.sync_num_pos = sync_num_pos\n        self.bbox_norm_type = bbox_norm_type\n        self.gradient_mul = gradient_mul\n        self.use_vfl = use_vfl\n        if self.use_vfl:\n            self.loss_cls = MODELS.build(loss_cls)\n        else:\n            self.loss_cls = MODELS.build(loss_cls_fl)\n        self.loss_bbox = MODELS.build(loss_bbox)\n        self.loss_bbox_refine = MODELS.build(loss_bbox_refine)\n\n        # for getting ATSS targets\n        self.use_atss = use_atss\n        self.reg_decoded_bbox = reg_decoded_bbox\n        self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False)\n\n        self.anchor_center_offset = anchor_generator['center_offset']\n\n        self.num_base_priors = self.prior_generator.num_base_priors[0]\n\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            if self.train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], default_args=dict(context=self))\n            else:\n                self.sampler = PseudoSampler()\n        # only be used in `get_atss_targets` when `use_atss` is True\n        self.atss_prior_generator = TASK_UTILS.build(anchor_generator)\n\n        self.fcos_prior_generator = MlvlPointGenerator(\n            anchor_generator['strides'],\n            self.anchor_center_offset if self.use_atss else 0.5)\n\n        # In order to reuse the `get_bboxes` in `BaseDenseHead.\n        # Only be used in testing phase.\n        self.prior_generator = self.fcos_prior_generator\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        super(FCOSHead, self)._init_cls_convs()\n        super(FCOSHead, self)._init_reg_convs()\n        self.relu = nn.ReLU()\n        self.vfnet_reg_conv = ConvModule(\n            self.feat_channels,\n            self.feat_channels,\n            3,\n            stride=1,\n            padding=1,\n            conv_cfg=self.conv_cfg,\n            norm_cfg=self.norm_cfg,\n            bias=self.conv_bias)\n        self.vfnet_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1)\n        self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides])\n\n        self.vfnet_reg_refine_dconv = DeformConv2d(\n            self.feat_channels,\n            self.feat_channels,\n            self.dcn_kernel,\n            1,\n            padding=self.dcn_pad)\n        self.vfnet_reg_refine = nn.Conv2d(self.feat_channels, 4, 3, padding=1)\n        self.scales_refine = nn.ModuleList([Scale(1.0) for _ in self.strides])\n\n        self.vfnet_cls_dconv = DeformConv2d(\n            self.feat_channels,\n            self.feat_channels,\n            self.dcn_kernel,\n            1,\n            padding=self.dcn_pad)\n        self.vfnet_cls = nn.Conv2d(\n            self.feat_channels, self.cls_out_channels, 3, padding=1)\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple:\n\n            - cls_scores (list[Tensor]): Box iou-aware scores for each scale\n              level, each is a 4D-tensor, the channel number is\n              num_points * num_classes.\n            - bbox_preds (list[Tensor]): Box offsets for each\n              scale level, each is a 4D-tensor, the channel number is\n              num_points * 4.\n            - bbox_preds_refine (list[Tensor]): Refined Box offsets for\n              each scale level, each is a 4D-tensor, the channel\n              number is num_points * 4.\n        \"\"\"\n        return multi_apply(self.forward_single, x, self.scales,\n                           self.scales_refine, self.strides, self.reg_denoms)\n\n    def forward_single(self, x: Tensor, scale: Scale, scale_refine: Scale,\n                       stride: int, reg_denom: int) -> tuple:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n            scale (:obj: `mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n            scale_refine (:obj: `mmcv.cnn.Scale`): Learnable scale module to\n                resize the refined bbox prediction.\n            stride (int): The corresponding stride for feature maps,\n                used to normalize the bbox prediction when\n                bbox_norm_type = 'stride'.\n            reg_denom (int): The corresponding regression range for feature\n                maps, only used to normalize the bbox prediction when\n                bbox_norm_type = 'reg_denom'.\n\n        Returns:\n            tuple: iou-aware cls scores for each box, bbox predictions and\n            refined bbox predictions of input feature maps.\n        \"\"\"\n        cls_feat = x\n        reg_feat = x\n\n        for cls_layer in self.cls_convs:\n            cls_feat = cls_layer(cls_feat)\n\n        for reg_layer in self.reg_convs:\n            reg_feat = reg_layer(reg_feat)\n\n        # predict the bbox_pred of different level\n        reg_feat_init = self.vfnet_reg_conv(reg_feat)\n        if self.bbox_norm_type == 'reg_denom':\n            bbox_pred = scale(\n                self.vfnet_reg(reg_feat_init)).float().exp() * reg_denom\n        elif self.bbox_norm_type == 'stride':\n            bbox_pred = scale(\n                self.vfnet_reg(reg_feat_init)).float().exp() * stride\n        else:\n            raise NotImplementedError\n\n        # compute star deformable convolution offsets\n        # converting dcn_offset to reg_feat.dtype thus VFNet can be\n        # trained with FP16\n        dcn_offset = self.star_dcn_offset(bbox_pred, self.gradient_mul,\n                                          stride).to(reg_feat.dtype)\n\n        # refine the bbox_pred\n        reg_feat = self.relu(self.vfnet_reg_refine_dconv(reg_feat, dcn_offset))\n        bbox_pred_refine = scale_refine(\n            self.vfnet_reg_refine(reg_feat)).float().exp()\n        bbox_pred_refine = bbox_pred_refine * bbox_pred.detach()\n\n        # predict the iou-aware cls score\n        cls_feat = self.relu(self.vfnet_cls_dconv(cls_feat, dcn_offset))\n        cls_score = self.vfnet_cls(cls_feat)\n\n        if self.training:\n            return cls_score, bbox_pred, bbox_pred_refine\n        else:\n            return cls_score, bbox_pred_refine\n\n    def star_dcn_offset(self, bbox_pred: Tensor, gradient_mul: float,\n                        stride: int) -> Tensor:\n        \"\"\"Compute the star deformable conv offsets.\n\n        Args:\n            bbox_pred (Tensor): Predicted bbox distance offsets (l, r, t, b).\n            gradient_mul (float): Gradient multiplier.\n            stride (int): The corresponding stride for feature maps,\n                used to project the bbox onto the feature map.\n\n        Returns:\n            Tensor: The offsets for deformable convolution.\n        \"\"\"\n        dcn_base_offset = self.dcn_base_offset.type_as(bbox_pred)\n        bbox_pred_grad_mul = (1 - gradient_mul) * bbox_pred.detach() + \\\n            gradient_mul * bbox_pred\n        # map to the feature map scale\n        bbox_pred_grad_mul = bbox_pred_grad_mul / stride\n        N, C, H, W = bbox_pred.size()\n\n        x1 = bbox_pred_grad_mul[:, 0, :, :]\n        y1 = bbox_pred_grad_mul[:, 1, :, :]\n        x2 = bbox_pred_grad_mul[:, 2, :, :]\n        y2 = bbox_pred_grad_mul[:, 3, :, :]\n        bbox_pred_grad_mul_offset = bbox_pred.new_zeros(\n            N, 2 * self.num_dconv_points, H, W)\n        bbox_pred_grad_mul_offset[:, 0, :, :] = -1.0 * y1  # -y1\n        bbox_pred_grad_mul_offset[:, 1, :, :] = -1.0 * x1  # -x1\n        bbox_pred_grad_mul_offset[:, 2, :, :] = -1.0 * y1  # -y1\n        bbox_pred_grad_mul_offset[:, 4, :, :] = -1.0 * y1  # -y1\n        bbox_pred_grad_mul_offset[:, 5, :, :] = x2  # x2\n        bbox_pred_grad_mul_offset[:, 7, :, :] = -1.0 * x1  # -x1\n        bbox_pred_grad_mul_offset[:, 11, :, :] = x2  # x2\n        bbox_pred_grad_mul_offset[:, 12, :, :] = y2  # y2\n        bbox_pred_grad_mul_offset[:, 13, :, :] = -1.0 * x1  # -x1\n        bbox_pred_grad_mul_offset[:, 14, :, :] = y2  # y2\n        bbox_pred_grad_mul_offset[:, 16, :, :] = y2  # y2\n        bbox_pred_grad_mul_offset[:, 17, :, :] = x2  # x2\n        dcn_offset = bbox_pred_grad_mul_offset - dcn_base_offset\n\n        return dcn_offset\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            bbox_preds_refine: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Compute loss of the head.\n\n        Args:\n            cls_scores (list[Tensor]): Box iou-aware scores for each scale\n                level, each is a 4D-tensor, the channel number is\n                num_points * num_classes.\n            bbox_preds (list[Tensor]): Box offsets for each\n                scale level, each is a 4D-tensor, the channel number is\n                num_points * 4.\n            bbox_preds_refine (list[Tensor]): Refined Box offsets for\n                each scale level, each is a 4D-tensor, the channel\n                number is num_points * 4.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds) == len(bbox_preds_refine)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        all_level_points = self.fcos_prior_generator.grid_priors(\n            featmap_sizes, bbox_preds[0].dtype, bbox_preds[0].device)\n        labels, label_weights, bbox_targets, bbox_weights = self.get_targets(\n            cls_scores,\n            all_level_points,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        num_imgs = cls_scores[0].size(0)\n        # flatten cls_scores, bbox_preds and bbox_preds_refine\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3,\n                              1).reshape(-1,\n                                         self.cls_out_channels).contiguous()\n            for cls_score in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4).contiguous()\n            for bbox_pred in bbox_preds\n        ]\n        flatten_bbox_preds_refine = [\n            bbox_pred_refine.permute(0, 2, 3, 1).reshape(-1, 4).contiguous()\n            for bbox_pred_refine in bbox_preds_refine\n        ]\n        flatten_cls_scores = torch.cat(flatten_cls_scores)\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds)\n        flatten_bbox_preds_refine = torch.cat(flatten_bbox_preds_refine)\n        flatten_labels = torch.cat(labels)\n        flatten_bbox_targets = torch.cat(bbox_targets)\n        # repeat points to align with bbox_preds\n        flatten_points = torch.cat(\n            [points.repeat(num_imgs, 1) for points in all_level_points])\n\n        # FG cat_id: [0, num_classes - 1], BG cat_id: num_classes\n        bg_class_ind = self.num_classes\n        pos_inds = torch.where(\n            ((flatten_labels >= 0) & (flatten_labels < bg_class_ind)) > 0)[0]\n        num_pos = len(pos_inds)\n\n        pos_bbox_preds = flatten_bbox_preds[pos_inds]\n        pos_bbox_preds_refine = flatten_bbox_preds_refine[pos_inds]\n        pos_labels = flatten_labels[pos_inds]\n\n        # sync num_pos across all gpus\n        if self.sync_num_pos:\n            num_pos_avg_per_gpu = reduce_mean(\n                pos_inds.new_tensor(num_pos).float()).item()\n            num_pos_avg_per_gpu = max(num_pos_avg_per_gpu, 1.0)\n        else:\n            num_pos_avg_per_gpu = num_pos\n\n        pos_bbox_targets = flatten_bbox_targets[pos_inds]\n        pos_points = flatten_points[pos_inds]\n\n        pos_decoded_bbox_preds = self.bbox_coder.decode(\n            pos_points, pos_bbox_preds)\n        pos_decoded_target_preds = self.bbox_coder.decode(\n            pos_points, pos_bbox_targets)\n        iou_targets_ini = bbox_overlaps(\n            pos_decoded_bbox_preds,\n            pos_decoded_target_preds.detach(),\n            is_aligned=True).clamp(min=1e-6)\n        bbox_weights_ini = iou_targets_ini.clone().detach()\n        bbox_avg_factor_ini = reduce_mean(\n            bbox_weights_ini.sum()).clamp_(min=1).item()\n\n        pos_decoded_bbox_preds_refine = \\\n            self.bbox_coder.decode(pos_points, pos_bbox_preds_refine)\n        iou_targets_rf = bbox_overlaps(\n            pos_decoded_bbox_preds_refine,\n            pos_decoded_target_preds.detach(),\n            is_aligned=True).clamp(min=1e-6)\n        bbox_weights_rf = iou_targets_rf.clone().detach()\n        bbox_avg_factor_rf = reduce_mean(\n            bbox_weights_rf.sum()).clamp_(min=1).item()\n\n        if num_pos > 0:\n            loss_bbox = self.loss_bbox(\n                pos_decoded_bbox_preds,\n                pos_decoded_target_preds.detach(),\n                weight=bbox_weights_ini,\n                avg_factor=bbox_avg_factor_ini)\n\n            loss_bbox_refine = self.loss_bbox_refine(\n                pos_decoded_bbox_preds_refine,\n                pos_decoded_target_preds.detach(),\n                weight=bbox_weights_rf,\n                avg_factor=bbox_avg_factor_rf)\n\n            # build IoU-aware cls_score targets\n            if self.use_vfl:\n                pos_ious = iou_targets_rf.clone().detach()\n                cls_iou_targets = torch.zeros_like(flatten_cls_scores)\n                cls_iou_targets[pos_inds, pos_labels] = pos_ious\n        else:\n            loss_bbox = pos_bbox_preds.sum() * 0\n            loss_bbox_refine = pos_bbox_preds_refine.sum() * 0\n            if self.use_vfl:\n                cls_iou_targets = torch.zeros_like(flatten_cls_scores)\n\n        if self.use_vfl:\n            loss_cls = self.loss_cls(\n                flatten_cls_scores,\n                cls_iou_targets,\n                avg_factor=num_pos_avg_per_gpu)\n        else:\n            loss_cls = self.loss_cls(\n                flatten_cls_scores,\n                flatten_labels,\n                weight=label_weights,\n                avg_factor=num_pos_avg_per_gpu)\n\n        return dict(\n            loss_cls=loss_cls,\n            loss_bbox=loss_bbox,\n            loss_bbox_rf=loss_bbox_refine)\n\n    def get_targets(\n            self,\n            cls_scores: List[Tensor],\n            mlvl_points: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> tuple:\n        \"\"\"A wrapper for computing ATSS and FCOS targets for points in multiple\n        images.\n\n        Args:\n            cls_scores (list[Tensor]): Box iou-aware scores for each scale\n                level with shape (N, num_points * num_classes, H, W).\n            mlvl_points (list[Tensor]): Points of each fpn level, each has\n                shape (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            tuple:\n\n            - labels_list (list[Tensor]): Labels of each level.\n            - label_weights (Tensor/None): Label weights of all levels.\n            - bbox_targets_list (list[Tensor]): Regression targets of each\n              level, (l, t, r, b).\n            - bbox_weights (Tensor/None): Bbox weights of all levels.\n        \"\"\"\n        if self.use_atss:\n            return self.get_atss_targets(cls_scores, mlvl_points,\n                                         batch_gt_instances, batch_img_metas,\n                                         batch_gt_instances_ignore)\n        else:\n            self.norm_on_bbox = False\n            return self.get_fcos_targets(mlvl_points, batch_gt_instances)\n\n    def _get_targets_single(self, *args, **kwargs):\n        \"\"\"Avoid ambiguity in multiple inheritance.\"\"\"\n        if self.use_atss:\n            return ATSSHead._get_targets_single(self, *args, **kwargs)\n        else:\n            return FCOSHead._get_targets_single(self, *args, **kwargs)\n\n    def get_fcos_targets(self, points: List[Tensor],\n                         batch_gt_instances: InstanceList) -> tuple:\n        \"\"\"Compute FCOS regression and classification targets for points in\n        multiple images.\n\n        Args:\n            points (list[Tensor]): Points of each fpn level, each has shape\n                (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple:\n\n            - labels (list[Tensor]): Labels of each level.\n            - label_weights: None, to be compatible with ATSS targets.\n            - bbox_targets (list[Tensor]): BBox targets of each level.\n            - bbox_weights: None, to be compatible with ATSS targets.\n        \"\"\"\n        labels, bbox_targets = FCOSHead.get_targets(self, points,\n                                                    batch_gt_instances)\n        label_weights = None\n        bbox_weights = None\n        return labels, label_weights, bbox_targets, bbox_weights\n\n    def get_anchors(self,\n                    featmap_sizes: List[Tuple],\n                    batch_img_metas: List[dict],\n                    device: str = 'cuda') -> tuple:\n        \"\"\"Get anchors according to feature map sizes.\n\n        Args:\n            featmap_sizes (list[tuple]): Multi-level feature map sizes.\n            batch_img_metas (list[dict]): Image meta info.\n            device (str): Device for returned tensors\n\n        Returns:\n            tuple:\n\n            - anchor_list (list[Tensor]): Anchors of each image.\n            - valid_flag_list (list[Tensor]): Valid flags of each image.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n\n        # since feature map sizes of all images are the same, we only compute\n        # anchors for one time\n        multi_level_anchors = self.atss_prior_generator.grid_priors(\n            featmap_sizes, device=device)\n        anchor_list = [multi_level_anchors for _ in range(num_imgs)]\n\n        # for each image, we compute valid flags of multi level anchors\n        valid_flag_list = []\n        for img_id, img_meta in enumerate(batch_img_metas):\n            multi_level_flags = self.atss_prior_generator.valid_flags(\n                featmap_sizes, img_meta['pad_shape'], device=device)\n            valid_flag_list.append(multi_level_flags)\n\n        return anchor_list, valid_flag_list\n\n    def get_atss_targets(\n            self,\n            cls_scores: List[Tensor],\n            mlvl_points: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> tuple:\n        \"\"\"A wrapper for computing ATSS targets for points in multiple images.\n\n        Args:\n            cls_scores (list[Tensor]): Box iou-aware scores for each scale\n                level with shape (N, num_points * num_classes, H, W).\n            mlvl_points (list[Tensor]): Points of each fpn level, each has\n                shape (num_points, 2).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            tuple:\n\n            - labels_list (list[Tensor]): Labels of each level.\n            - label_weights (Tensor): Label weights of all levels.\n            - bbox_targets_list (list[Tensor]): Regression targets of each\n              level, (l, t, r, b).\n            - bbox_weights (Tensor): Bbox weights of all levels.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(\n            featmap_sizes\n        ) == self.atss_prior_generator.num_levels == \\\n            self.fcos_prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        cls_reg_targets = ATSSHead.get_targets(\n            self,\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore,\n            unmap_outputs=True)\n\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor) = cls_reg_targets\n\n        bbox_targets_list = [\n            bbox_targets.reshape(-1, 4) for bbox_targets in bbox_targets_list\n        ]\n\n        num_imgs = len(batch_img_metas)\n        # transform bbox_targets (x1, y1, x2, y2) into (l, t, r, b) format\n        bbox_targets_list = self.transform_bbox_targets(\n            bbox_targets_list, mlvl_points, num_imgs)\n\n        labels_list = [labels.reshape(-1) for labels in labels_list]\n        label_weights_list = [\n            label_weights.reshape(-1) for label_weights in label_weights_list\n        ]\n        bbox_weights_list = [\n            bbox_weights.reshape(-1) for bbox_weights in bbox_weights_list\n        ]\n        label_weights = torch.cat(label_weights_list)\n        bbox_weights = torch.cat(bbox_weights_list)\n        return labels_list, label_weights, bbox_targets_list, bbox_weights\n\n    def transform_bbox_targets(self, decoded_bboxes: List[Tensor],\n                               mlvl_points: List[Tensor],\n                               num_imgs: int) -> List[Tensor]:\n        \"\"\"Transform bbox_targets (x1, y1, x2, y2) into (l, t, r, b) format.\n\n        Args:\n            decoded_bboxes (list[Tensor]): Regression targets of each level,\n                in the form of (x1, y1, x2, y2).\n            mlvl_points (list[Tensor]): Points of each fpn level, each has\n                shape (num_points, 2).\n            num_imgs (int): the number of images in a batch.\n\n        Returns:\n            bbox_targets (list[Tensor]): Regression targets of each level in\n                the form of (l, t, r, b).\n        \"\"\"\n        # TODO: Re-implemented in Class PointCoder\n        assert len(decoded_bboxes) == len(mlvl_points)\n        num_levels = len(decoded_bboxes)\n        mlvl_points = [points.repeat(num_imgs, 1) for points in mlvl_points]\n        bbox_targets = []\n        for i in range(num_levels):\n            bbox_target = self.bbox_coder.encode(mlvl_points[i],\n                                                 decoded_bboxes[i])\n            bbox_targets.append(bbox_target)\n\n        return bbox_targets\n\n    def _load_from_state_dict(self, state_dict: dict, prefix: str,\n                              local_metadata: dict, strict: bool,\n                              missing_keys: Union[List[str], str],\n                              unexpected_keys: Union[List[str], str],\n                              error_msgs: Union[List[str], str]) -> None:\n        \"\"\"Override the method in the parent class to avoid changing para's\n        name.\"\"\"\n        pass\n"
  },
  {
    "path": "mmdet/models/dense_heads/yolact_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List, Optional\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule, ModuleList\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, OptMultiConfig)\nfrom ..layers import fast_nms\nfrom ..utils import images_to_levels, multi_apply, select_single_mlvl\nfrom ..utils.misc import empty_instances\nfrom .anchor_head import AnchorHead\nfrom .base_mask_head import BaseMaskHead\n\n\n@MODELS.register_module()\nclass YOLACTHead(AnchorHead):\n    \"\"\"YOLACT box head used in https://arxiv.org/abs/1904.02689.\n\n    Note that YOLACT head is a light version of RetinaNet head.\n    Four differences are described as follows:\n\n    1. YOLACT box head has three-times fewer anchors.\n    2. YOLACT box head shares the convs for box and cls branches.\n    3. YOLACT box head uses OHEM instead of Focal loss.\n    4. YOLACT box head predicts a set of mask coefficients for each box.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        anchor_generator (:obj:`ConfigDict` or dict): Config dict for\n            anchor generator\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of localization loss.\n        num_head_convs (int): Number of the conv layers shared by\n            box and cls branches.\n        num_protos (int): Number of the mask coefficients.\n        use_ohem (bool): If true, ``loss_single_OHEM`` will be used for\n            cls loss calculation. If false, ``loss_single`` will be used.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Dictionary to\n            construct and config conv layer.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Dictionary to\n            construct and config norm layer.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 anchor_generator: ConfigType = dict(\n                     type='AnchorGenerator',\n                     octave_base_scale=3,\n                     scales_per_octave=1,\n                     ratios=[0.5, 1.0, 2.0],\n                     strides=[8, 16, 32, 64, 128]),\n                 loss_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=False,\n                     reduction='none',\n                     loss_weight=1.0),\n                 loss_bbox: ConfigType = dict(\n                     type='SmoothL1Loss', beta=1.0, loss_weight=1.5),\n                 num_head_convs: int = 1,\n                 num_protos: int = 32,\n                 use_ohem: bool = True,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = dict(\n                     type='Xavier',\n                     distribution='uniform',\n                     bias=0,\n                     layer='Conv2d'),\n                 **kwargs) -> None:\n        self.num_head_convs = num_head_convs\n        self.num_protos = num_protos\n        self.use_ohem = use_ohem\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            loss_cls=loss_cls,\n            loss_bbox=loss_bbox,\n            anchor_generator=anchor_generator,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.relu = nn.ReLU(inplace=True)\n        self.head_convs = ModuleList()\n        for i in range(self.num_head_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.head_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * self.cls_out_channels,\n            3,\n            padding=1)\n        self.conv_reg = nn.Conv2d(\n            self.feat_channels, self.num_base_priors * 4, 3, padding=1)\n        self.conv_coeff = nn.Conv2d(\n            self.feat_channels,\n            self.num_base_priors * self.num_protos,\n            3,\n            padding=1)\n\n    def forward_single(self, x: Tensor) -> tuple:\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n\n        Returns:\n            tuple:\n\n            - cls_score (Tensor): Cls scores for a single scale level\n              the channels number is num_anchors * num_classes.\n            - bbox_pred (Tensor): Box energies / deltas for a single scale\n              level, the channels number is num_anchors * 4.\n            - coeff_pred (Tensor): Mask coefficients for a single scale\n              level, the channels number is num_anchors * num_protos.\n        \"\"\"\n        for head_conv in self.head_convs:\n            x = head_conv(x)\n        cls_score = self.conv_cls(x)\n        bbox_pred = self.conv_reg(x)\n        coeff_pred = self.conv_coeff(x).tanh()\n        return cls_score, bbox_pred, coeff_pred\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            coeff_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the bbox head.\n\n        When ``self.use_ohem == True``, it functions like ``SSDHead.loss``,\n        otherwise, it follows ``AnchorHead.loss``.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            coeff_preds (list[Tensor]): Mask coefficients for each scale\n                level with shape (N, num_anchors * num_protos, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore,\n            unmap_outputs=not self.use_ohem,\n            return_sampling_results=True)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor, sampling_results) = cls_reg_targets\n\n        if self.use_ohem:\n            num_images = len(batch_img_metas)\n            all_cls_scores = torch.cat([\n                s.permute(0, 2, 3, 1).reshape(\n                    num_images, -1, self.cls_out_channels) for s in cls_scores\n            ], 1)\n            all_labels = torch.cat(labels_list, -1).view(num_images, -1)\n            all_label_weights = torch.cat(label_weights_list,\n                                          -1).view(num_images, -1)\n            all_bbox_preds = torch.cat([\n                b.permute(0, 2, 3, 1).reshape(num_images, -1, 4)\n                for b in bbox_preds\n            ], -2)\n            all_bbox_targets = torch.cat(bbox_targets_list,\n                                         -2).view(num_images, -1, 4)\n            all_bbox_weights = torch.cat(bbox_weights_list,\n                                         -2).view(num_images, -1, 4)\n\n            # concat all level anchors to a single tensor\n            all_anchors = []\n            for i in range(num_images):\n                all_anchors.append(torch.cat(anchor_list[i]))\n\n            # check NaN and Inf\n            assert torch.isfinite(all_cls_scores).all().item(), \\\n                'classification scores become infinite or NaN!'\n            assert torch.isfinite(all_bbox_preds).all().item(), \\\n                'bbox predications become infinite or NaN!'\n\n            losses_cls, losses_bbox = multi_apply(\n                self.OHEMloss_by_feat_single,\n                all_cls_scores,\n                all_bbox_preds,\n                all_anchors,\n                all_labels,\n                all_label_weights,\n                all_bbox_targets,\n                all_bbox_weights,\n                avg_factor=avg_factor)\n        else:\n            # anchor number of multi levels\n            num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n            # concat all level anchors and flags to a single tensor\n            concat_anchor_list = []\n            for i in range(len(anchor_list)):\n                concat_anchor_list.append(torch.cat(anchor_list[i]))\n            all_anchor_list = images_to_levels(concat_anchor_list,\n                                               num_level_anchors)\n            losses_cls, losses_bbox = multi_apply(\n                self.loss_by_feat_single,\n                cls_scores,\n                bbox_preds,\n                all_anchor_list,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                bbox_weights_list,\n                avg_factor=avg_factor)\n        losses = dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n        # update `_raw_positive_infos`, which will be used when calling\n        # `get_positive_infos`.\n        self._raw_positive_infos.update(coeff_preds=coeff_preds)\n        return losses\n\n    def OHEMloss_by_feat_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                                anchors: Tensor, labels: Tensor,\n                                label_weights: Tensor, bbox_targets: Tensor,\n                                bbox_weights: Tensor,\n                                avg_factor: int) -> tuple:\n        \"\"\"Compute loss of a single image. Similar to\n        func:``SSDHead.loss_by_feat_single``\n\n        Args:\n            cls_score (Tensor): Box scores for eachimage\n                Has shape (num_total_anchors, num_classes).\n            bbox_pred (Tensor): Box energies / deltas for each image\n                level with shape (num_total_anchors, 4).\n            anchors (Tensor): Box reference for each scale level with shape\n                (num_total_anchors, 4).\n            labels (Tensor): Labels of each anchors with shape\n                (num_total_anchors,).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (num_total_anchors,)\n            bbox_targets (Tensor): BBox regression targets of each anchor with\n                shape (num_total_anchors, 4).\n            bbox_weights (Tensor): BBox regression loss weights of each anchor\n                with shape (num_total_anchors, 4).\n            avg_factor (int): Average factor that is used to average\n                the loss. When using sampling method, avg_factor is usually\n                the sum of positive and negative priors. When using\n                `PseudoSampler`, `avg_factor` is usually equal to the number\n                of positive priors.\n\n        Returns:\n            Tuple[Tensor, Tensor]: A tuple of cls loss and bbox loss of one\n            feature map.\n        \"\"\"\n\n        loss_cls_all = self.loss_cls(cls_score, labels, label_weights)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        pos_inds = ((labels >= 0) & (labels < self.num_classes)).nonzero(\n            as_tuple=False).reshape(-1)\n        neg_inds = (labels == self.num_classes).nonzero(\n            as_tuple=False).view(-1)\n\n        num_pos_samples = pos_inds.size(0)\n        if num_pos_samples == 0:\n            num_neg_samples = neg_inds.size(0)\n        else:\n            num_neg_samples = self.train_cfg['neg_pos_ratio'] * \\\n                              num_pos_samples\n            if num_neg_samples > neg_inds.size(0):\n                num_neg_samples = neg_inds.size(0)\n        topk_loss_cls_neg, _ = loss_cls_all[neg_inds].topk(num_neg_samples)\n        loss_cls_pos = loss_cls_all[pos_inds].sum()\n        loss_cls_neg = topk_loss_cls_neg.sum()\n        loss_cls = (loss_cls_pos + loss_cls_neg) / avg_factor\n        if self.reg_decoded_bbox:\n            # When the regression loss (e.g. `IouLoss`, `GIouLoss`)\n            # is applied directly on the decoded bounding boxes, it\n            # decodes the already encoded coordinates to absolute format.\n            bbox_pred = self.bbox_coder.decode(anchors, bbox_pred)\n        loss_bbox = self.loss_bbox(\n            bbox_pred, bbox_targets, bbox_weights, avg_factor=avg_factor)\n        return loss_cls[None], loss_bbox\n\n    def get_positive_infos(self) -> InstanceList:\n        \"\"\"Get positive information from sampling results.\n\n        Returns:\n            list[:obj:`InstanceData`]: Positive Information of each image,\n            usually including positive bboxes, positive labels, positive\n            priors, positive coeffs, etc.\n        \"\"\"\n        assert len(self._raw_positive_infos) > 0\n        sampling_results = self._raw_positive_infos['sampling_results']\n        num_imgs = len(sampling_results)\n\n        coeff_pred_list = []\n        for coeff_pred_per_level in self._raw_positive_infos['coeff_preds']:\n            coeff_pred_per_level = \\\n                coeff_pred_per_level.permute(\n                    0, 2, 3, 1).reshape(num_imgs, -1, self.num_protos)\n            coeff_pred_list.append(coeff_pred_per_level)\n        coeff_preds = torch.cat(coeff_pred_list, dim=1)\n\n        pos_info_list = []\n        for idx, sampling_result in enumerate(sampling_results):\n            pos_info = InstanceData()\n            coeff_preds_single = coeff_preds[idx]\n            pos_info.pos_assigned_gt_inds = \\\n                sampling_result.pos_assigned_gt_inds\n            pos_info.pos_inds = sampling_result.pos_inds\n            pos_info.coeffs = coeff_preds_single[sampling_result.pos_inds]\n            pos_info.bboxes = sampling_result.pos_gt_bboxes\n            pos_info_list.append(pos_info)\n        return pos_info_list\n\n    def predict_by_feat(self,\n                        cls_scores,\n                        bbox_preds,\n                        coeff_preds,\n                        batch_img_metas,\n                        cfg=None,\n                        rescale=True,\n                        **kwargs):\n        \"\"\"Similar to func:``AnchorHead.get_bboxes``, but additionally\n        processes coeff_preds.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                with shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            coeff_preds (list[Tensor]): Mask coefficients for each scale\n                level with shape (N, num_anchors * num_protos, H, W)\n            batch_img_metas (list[dict]): Batch image meta info.\n            cfg (:obj:`Config` | None): Test / postprocessing configuration,\n                if None, test_cfg would be used\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - coeffs (Tensor): the predicted mask coefficients of\n                  instance inside the corresponding box has a shape\n                  (n, num_protos).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n        num_levels = len(cls_scores)\n\n        device = cls_scores[0].device\n        featmap_sizes = [cls_scores[i].shape[-2:] for i in range(num_levels)]\n        mlvl_priors = self.prior_generator.grid_priors(\n            featmap_sizes, device=device)\n\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            cls_score_list = select_single_mlvl(cls_scores, img_id)\n            bbox_pred_list = select_single_mlvl(bbox_preds, img_id)\n            coeff_pred_list = select_single_mlvl(coeff_preds, img_id)\n            results = self._predict_by_feat_single(\n                cls_score_list=cls_score_list,\n                bbox_pred_list=bbox_pred_list,\n                coeff_preds_list=coeff_pred_list,\n                mlvl_priors=mlvl_priors,\n                img_meta=img_meta,\n                cfg=cfg,\n                rescale=rescale)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                coeff_preds_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigType,\n                                rescale: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results. Similar to func:``AnchorHead._predict_by_feat_single``,\n        but additionally processes coeff_preds_list and uses fast NMS instead\n        of traditional NMS.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores for a single scale level\n                Has shape (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas for a single\n                scale level with shape (num_priors * 4, H, W).\n            coeff_preds_list (list[Tensor]): Mask coefficients for a single\n                scale level with shape (num_priors * num_protos, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid,\n                has shape (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - coeffs (Tensor): the predicted mask coefficients of\n                  instance inside the corresponding box has a shape\n                  (n, num_protos).\n        \"\"\"\n        assert len(cls_score_list) == len(bbox_pred_list) == len(mlvl_priors)\n\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        img_shape = img_meta['img_shape']\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bbox_preds = []\n        mlvl_valid_priors = []\n        mlvl_scores = []\n        mlvl_coeffs = []\n        for cls_score, bbox_pred, coeff_pred, priors in \\\n                zip(cls_score_list, bbox_pred_list,\n                    coeff_preds_list, mlvl_priors):\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            if self.use_sigmoid_cls:\n                scores = cls_score.sigmoid()\n            else:\n                scores = cls_score.softmax(-1)\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4)\n            coeff_pred = coeff_pred.permute(1, 2,\n                                            0).reshape(-1, self.num_protos)\n\n            if 0 < nms_pre < scores.shape[0]:\n                # Get maximum scores for foreground classes.\n                if self.use_sigmoid_cls:\n                    max_scores, _ = scores.max(dim=1)\n                else:\n                    # remind that we set FG labels to [0, num_class-1]\n                    # since mmdet v2.0\n                    # BG cat_id: num_class\n                    max_scores, _ = scores[:, :-1].max(dim=1)\n                _, topk_inds = max_scores.topk(nms_pre)\n                priors = priors[topk_inds, :]\n                bbox_pred = bbox_pred[topk_inds, :]\n                scores = scores[topk_inds, :]\n                coeff_pred = coeff_pred[topk_inds, :]\n\n            mlvl_bbox_preds.append(bbox_pred)\n            mlvl_valid_priors.append(priors)\n            mlvl_scores.append(scores)\n            mlvl_coeffs.append(coeff_pred)\n\n        bbox_pred = torch.cat(mlvl_bbox_preds)\n        priors = torch.cat(mlvl_valid_priors)\n        multi_bboxes = self.bbox_coder.decode(\n            priors, bbox_pred, max_shape=img_shape)\n\n        multi_scores = torch.cat(mlvl_scores)\n        multi_coeffs = torch.cat(mlvl_coeffs)\n\n        return self._bbox_post_process(\n            multi_bboxes=multi_bboxes,\n            multi_scores=multi_scores,\n            multi_coeffs=multi_coeffs,\n            cfg=cfg,\n            rescale=rescale,\n            img_meta=img_meta)\n\n    def _bbox_post_process(self,\n                           multi_bboxes: Tensor,\n                           multi_scores: Tensor,\n                           multi_coeffs: Tensor,\n                           cfg: ConfigType,\n                           rescale: bool = False,\n                           img_meta: Optional[dict] = None,\n                           **kwargs) -> InstanceData:\n        \"\"\"bbox post-processing method.\n\n        The boxes would be rescaled to the original image scale and do\n        the nms operation. Usually `with_nms` is False is used for aug test.\n\n        Args:\n            multi_bboxes (Tensor): Predicted bbox that concat all levels.\n            multi_scores (Tensor): Bbox scores that concat all levels.\n            multi_coeffs (Tensor): Mask coefficients  that concat all levels.\n            cfg (ConfigDict): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Default to False.\n            img_meta (dict, optional): Image meta info. Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - coeffs (Tensor): the predicted mask coefficients of\n                  instance inside the corresponding box has a shape\n                  (n, num_protos).\n        \"\"\"\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            multi_bboxes /= multi_bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n            # mlvl_bboxes /= mlvl_bboxes.new_tensor(scale_factor)\n\n        if self.use_sigmoid_cls:\n            # Add a dummy background class to the backend when using sigmoid\n            # remind that we set FG labels to [0, num_class-1] since mmdet v2.0\n            # BG cat_id: num_class\n\n            padding = multi_scores.new_zeros(multi_scores.shape[0], 1)\n            multi_scores = torch.cat([multi_scores, padding], dim=1)\n        det_bboxes, det_labels, det_coeffs = fast_nms(\n            multi_bboxes, multi_scores, multi_coeffs, cfg.score_thr,\n            cfg.iou_thr, cfg.top_k, cfg.max_per_img)\n        results = InstanceData()\n        results.bboxes = det_bboxes[:, :4]\n        results.scores = det_bboxes[:, -1]\n        results.labels = det_labels\n        results.coeffs = det_coeffs\n        return results\n\n\n@MODELS.register_module()\nclass YOLACTProtonet(BaseMaskHead):\n    \"\"\"YOLACT mask head used in https://arxiv.org/abs/1904.02689.\n\n    This head outputs the mask prototypes for YOLACT.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        proto_channels (tuple[int]): Output channels of protonet convs.\n        proto_kernel_sizes (tuple[int]): Kernel sizes of protonet convs.\n        include_last_relu (bool): If keep the last relu of protonet.\n        num_protos (int): Number of prototypes.\n        num_classes (int): Number of categories excluding the background\n            category.\n        loss_mask_weight (float): Reweight the mask loss by this factor.\n        max_masks_to_train (int): Maximum number of masks to train for\n            each image.\n        with_seg_branch (bool): Whether to apply a semantic segmentation\n            branch and calculate loss during training to increase\n            performance with no speed penalty. Defaults to True.\n        loss_segm (:obj:`ConfigDict` or dict, optional): Config of\n            semantic segmentation loss.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config\n            of head.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            head.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int = 256,\n        proto_channels: tuple = (256, 256, 256, None, 256, 32),\n        proto_kernel_sizes: tuple = (3, 3, 3, -2, 3, 1),\n        include_last_relu: bool = True,\n        num_protos: int = 32,\n        loss_mask_weight: float = 1.0,\n        max_masks_to_train: int = 100,\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        with_seg_branch: bool = True,\n        loss_segm: ConfigType = dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        init_cfg=dict(\n            type='Xavier',\n            distribution='uniform',\n            override=dict(name='protonet'))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.proto_channels = proto_channels\n        self.proto_kernel_sizes = proto_kernel_sizes\n        self.include_last_relu = include_last_relu\n\n        # Segmentation branch\n        self.with_seg_branch = with_seg_branch\n        self.segm_branch = SegmentationModule(\n            num_classes=num_classes, in_channels=in_channels) \\\n            if with_seg_branch else None\n        self.loss_segm = MODELS.build(loss_segm) if with_seg_branch else None\n\n        self.loss_mask_weight = loss_mask_weight\n        self.num_protos = num_protos\n        self.num_classes = num_classes\n        self.max_masks_to_train = max_masks_to_train\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        # Possible patterns:\n        # ( 256, 3) -> conv\n        # ( 256,-2) -> deconv\n        # (None,-2) -> bilinear interpolate\n        in_channels = self.in_channels\n        protonets = ModuleList()\n        for num_channels, kernel_size in zip(self.proto_channels,\n                                             self.proto_kernel_sizes):\n            if kernel_size > 0:\n                layer = nn.Conv2d(\n                    in_channels,\n                    num_channels,\n                    kernel_size,\n                    padding=kernel_size // 2)\n            else:\n                if num_channels is None:\n                    layer = InterpolateModule(\n                        scale_factor=-kernel_size,\n                        mode='bilinear',\n                        align_corners=False)\n                else:\n                    layer = nn.ConvTranspose2d(\n                        in_channels,\n                        num_channels,\n                        -kernel_size,\n                        padding=kernel_size // 2)\n            protonets.append(layer)\n            protonets.append(nn.ReLU(inplace=True))\n            in_channels = num_channels if num_channels is not None \\\n                else in_channels\n        if not self.include_last_relu:\n            protonets = protonets[:-1]\n        self.protonet = nn.Sequential(*protonets)\n\n    def forward(self, x: tuple, positive_infos: InstanceList) -> tuple:\n        \"\"\"Forward feature from the upstream network to get prototypes and\n        linearly combine the prototypes, using masks coefficients, into\n        instance masks. Finally, crop the instance masks with given bboxes.\n\n        Args:\n            x (Tuple[Tensor]): Feature from the upstream network, which is\n                a 4D-tensor.\n            positive_infos (List[:obj:``InstanceData``]): Positive information\n                that calculate from detect head.\n\n        Returns:\n            tuple: Predicted instance segmentation masks and\n            semantic segmentation map.\n        \"\"\"\n        # YOLACT used single feature map to get segmentation masks\n        single_x = x[0]\n\n        # YOLACT segmentation branch, if not training or segmentation branch\n        # is None, will not process the forward function.\n        if self.segm_branch is not None and self.training:\n            segm_preds = self.segm_branch(single_x)\n        else:\n            segm_preds = None\n        # YOLACT mask head\n        prototypes = self.protonet(single_x)\n        prototypes = prototypes.permute(0, 2, 3, 1).contiguous()\n\n        num_imgs = single_x.size(0)\n\n        mask_pred_list = []\n        for idx in range(num_imgs):\n            cur_prototypes = prototypes[idx]\n            pos_coeffs = positive_infos[idx].coeffs\n\n            # Linearly combine the prototypes with the mask coefficients\n            mask_preds = cur_prototypes @ pos_coeffs.t()\n            mask_preds = torch.sigmoid(mask_preds)\n            mask_pred_list.append(mask_preds)\n        return mask_pred_list, segm_preds\n\n    def loss_by_feat(self, mask_preds: List[Tensor], segm_preds: List[Tensor],\n                     batch_gt_instances: InstanceList,\n                     batch_img_metas: List[dict], positive_infos: InstanceList,\n                     **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mask_preds (list[Tensor]): List of predicted prototypes, each has\n                shape (num_classes, H, W).\n            segm_preds (Tensor):  Predicted semantic segmentation map with\n                shape (N, num_classes, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``masks``,\n                and ``labels`` attributes.\n            batch_img_metas (list[dict]): Meta information of multiple images.\n            positive_infos (List[:obj:``InstanceData``]): Information of\n                positive samples of each image that are assigned in detection\n                head.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        assert positive_infos is not None, \\\n            'positive_infos should not be None in `YOLACTProtonet`'\n        losses = dict()\n\n        # crop\n        croped_mask_pred = self.crop_mask_preds(mask_preds, batch_img_metas,\n                                                positive_infos)\n\n        loss_mask = []\n        loss_segm = []\n        num_imgs, _, mask_h, mask_w = segm_preds.size()\n        assert num_imgs == len(croped_mask_pred)\n        segm_avg_factor = num_imgs * mask_h * mask_w\n        total_pos = 0\n\n        if self.segm_branch is not None:\n            assert segm_preds is not None\n\n        for idx in range(num_imgs):\n            img_meta = batch_img_metas[idx]\n\n            (mask_preds, pos_mask_targets, segm_targets, num_pos,\n             gt_bboxes_for_reweight) = self._get_targets_single(\n                 croped_mask_pred[idx], segm_preds[idx],\n                 batch_gt_instances[idx], positive_infos[idx])\n\n            # segmentation loss\n            if self.with_seg_branch:\n                if segm_targets is None:\n                    loss = segm_preds[idx].sum() * 0.\n                else:\n                    loss = self.loss_segm(\n                        segm_preds[idx],\n                        segm_targets,\n                        avg_factor=segm_avg_factor)\n                loss_segm.append(loss)\n            # mask loss\n            total_pos += num_pos\n            if num_pos == 0 or pos_mask_targets is None:\n                loss = mask_preds.sum() * 0.\n            else:\n                mask_preds = torch.clamp(mask_preds, 0, 1)\n                loss = F.binary_cross_entropy(\n                    mask_preds, pos_mask_targets,\n                    reduction='none') * self.loss_mask_weight\n\n                h, w = img_meta['img_shape'][:2]\n                gt_bboxes_width = (gt_bboxes_for_reweight[:, 2] -\n                                   gt_bboxes_for_reweight[:, 0]) / w\n                gt_bboxes_height = (gt_bboxes_for_reweight[:, 3] -\n                                    gt_bboxes_for_reweight[:, 1]) / h\n                loss = loss.mean(dim=(1,\n                                      2)) / gt_bboxes_width / gt_bboxes_height\n                loss = torch.sum(loss)\n            loss_mask.append(loss)\n\n        if total_pos == 0:\n            total_pos += 1  # avoid nan\n        loss_mask = [x / total_pos for x in loss_mask]\n\n        losses.update(loss_mask=loss_mask)\n        if self.with_seg_branch:\n            losses.update(loss_segm=loss_segm)\n\n        return losses\n\n    def _get_targets_single(self, mask_preds: Tensor, segm_pred: Tensor,\n                            gt_instances: InstanceData,\n                            positive_info: InstanceData):\n        \"\"\"Compute targets for predictions of single image.\n\n        Args:\n            mask_preds (Tensor): Predicted prototypes with shape\n                (num_classes, H, W).\n            segm_pred (Tensor): Predicted semantic segmentation map\n                with shape (num_classes, H, W).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes``, ``labels``,\n                and ``masks`` attributes.\n            positive_info (:obj:`InstanceData`): Information of positive\n                samples that are assigned in detection head. It usually\n                contains following keys.\n\n                    - pos_assigned_gt_inds (Tensor): Assigner GT indexes of\n                      positive proposals, has shape (num_pos, )\n                    - pos_inds (Tensor): Positive index of image, has\n                      shape (num_pos, ).\n                    - coeffs (Tensor): Positive mask coefficients\n                      with shape (num_pos, num_protos).\n                    - bboxes (Tensor): Positive bboxes with shape\n                      (num_pos, 4)\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n\n            - mask_preds (Tensor): Positive predicted mask with shape\n              (num_pos, mask_h, mask_w).\n            - pos_mask_targets (Tensor): Positive mask targets with shape\n              (num_pos, mask_h, mask_w).\n            - segm_targets (Tensor): Semantic segmentation targets with shape\n              (num_classes, segm_h, segm_w).\n            - num_pos (int): Positive numbers.\n            - gt_bboxes_for_reweight (Tensor): GT bboxes that match to the\n              positive priors has shape (num_pos, 4).\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        device = gt_bboxes.device\n        gt_masks = gt_instances.masks.to_tensor(\n            dtype=torch.bool, device=device).float()\n        if gt_masks.size(0) == 0:\n            return mask_preds, None, None, 0, None\n\n        # process with semantic segmentation targets\n        if segm_pred is not None:\n            num_classes, segm_h, segm_w = segm_pred.size()\n            with torch.no_grad():\n                downsampled_masks = F.interpolate(\n                    gt_masks.unsqueeze(0), (segm_h, segm_w),\n                    mode='bilinear',\n                    align_corners=False).squeeze(0)\n                downsampled_masks = downsampled_masks.gt(0.5).float()\n                segm_targets = torch.zeros_like(segm_pred, requires_grad=False)\n                for obj_idx in range(downsampled_masks.size(0)):\n                    segm_targets[gt_labels[obj_idx] - 1] = torch.max(\n                        segm_targets[gt_labels[obj_idx] - 1],\n                        downsampled_masks[obj_idx])\n        else:\n            segm_targets = None\n        # process with mask targets\n        pos_assigned_gt_inds = positive_info.pos_assigned_gt_inds\n        num_pos = pos_assigned_gt_inds.size(0)\n        # Since we're producing (near) full image masks,\n        # it'd take too much vram to backprop on every single mask.\n        # Thus we select only a subset.\n        if num_pos > self.max_masks_to_train:\n            perm = torch.randperm(num_pos)\n            select = perm[:self.max_masks_to_train]\n            mask_preds = mask_preds[select]\n            pos_assigned_gt_inds = pos_assigned_gt_inds[select]\n            num_pos = self.max_masks_to_train\n\n        gt_bboxes_for_reweight = gt_bboxes[pos_assigned_gt_inds]\n\n        mask_h, mask_w = mask_preds.shape[-2:]\n        gt_masks = F.interpolate(\n            gt_masks.unsqueeze(0), (mask_h, mask_w),\n            mode='bilinear',\n            align_corners=False).squeeze(0)\n        gt_masks = gt_masks.gt(0.5).float()\n        pos_mask_targets = gt_masks[pos_assigned_gt_inds]\n\n        return (mask_preds, pos_mask_targets, segm_targets, num_pos,\n                gt_bboxes_for_reweight)\n\n    def crop_mask_preds(self, mask_preds: List[Tensor],\n                        batch_img_metas: List[dict],\n                        positive_infos: InstanceList) -> list:\n        \"\"\"Crop predicted masks by zeroing out everything not in the predicted\n        bbox.\n\n        Args:\n            mask_preds (list[Tensor]): Predicted prototypes with shape\n                (num_classes, H, W).\n            batch_img_metas (list[dict]): Meta information of multiple images.\n            positive_infos (List[:obj:``InstanceData``]): Positive\n                information that calculate from detect head.\n\n        Returns:\n            list: The cropped masks.\n        \"\"\"\n        croped_mask_preds = []\n        for img_meta, mask_preds, cur_info in zip(batch_img_metas, mask_preds,\n                                                  positive_infos):\n            bboxes_for_cropping = copy.deepcopy(cur_info.bboxes)\n            h, w = img_meta['img_shape'][:2]\n            bboxes_for_cropping[:, 0::2] /= w\n            bboxes_for_cropping[:, 1::2] /= h\n            mask_preds = self.crop_single(mask_preds, bboxes_for_cropping)\n            mask_preds = mask_preds.permute(2, 0, 1).contiguous()\n            croped_mask_preds.append(mask_preds)\n        return croped_mask_preds\n\n    def crop_single(self,\n                    masks: Tensor,\n                    boxes: Tensor,\n                    padding: int = 1) -> Tensor:\n        \"\"\"Crop single predicted masks by zeroing out everything not in the\n        predicted bbox.\n\n        Args:\n            masks (Tensor): Predicted prototypes, has shape [H, W, N].\n            boxes (Tensor): Bbox coords in relative point form with\n                shape [N, 4].\n            padding (int): Image padding size.\n\n        Return:\n            Tensor: The cropped masks.\n        \"\"\"\n        h, w, n = masks.size()\n        x1, x2 = self.sanitize_coordinates(\n            boxes[:, 0], boxes[:, 2], w, padding, cast=False)\n        y1, y2 = self.sanitize_coordinates(\n            boxes[:, 1], boxes[:, 3], h, padding, cast=False)\n\n        rows = torch.arange(\n            w, device=masks.device, dtype=x1.dtype).view(1, -1,\n                                                         1).expand(h, w, n)\n        cols = torch.arange(\n            h, device=masks.device, dtype=x1.dtype).view(-1, 1,\n                                                         1).expand(h, w, n)\n\n        masks_left = rows >= x1.view(1, 1, -1)\n        masks_right = rows < x2.view(1, 1, -1)\n        masks_up = cols >= y1.view(1, 1, -1)\n        masks_down = cols < y2.view(1, 1, -1)\n\n        crop_mask = masks_left * masks_right * masks_up * masks_down\n\n        return masks * crop_mask.float()\n\n    def sanitize_coordinates(self,\n                             x1: Tensor,\n                             x2: Tensor,\n                             img_size: int,\n                             padding: int = 0,\n                             cast: bool = True) -> tuple:\n        \"\"\"Sanitizes the input coordinates so that x1 < x2, x1 != x2, x1 >= 0,\n        and x2 <= image_size. Also converts from relative to absolute\n        coordinates and casts the results to long tensors.\n\n        Warning: this does things in-place behind the scenes so\n        copy if necessary.\n\n        Args:\n            x1 (Tensor): shape (N, ).\n            x2 (Tensor): shape (N, ).\n            img_size (int): Size of the input image.\n            padding (int): x1 >= padding, x2 <= image_size-padding.\n            cast (bool): If cast is false, the result won't be cast to longs.\n\n        Returns:\n            tuple:\n\n            - x1 (Tensor): Sanitized _x1.\n            - x2 (Tensor): Sanitized _x2.\n        \"\"\"\n        x1 = x1 * img_size\n        x2 = x2 * img_size\n        if cast:\n            x1 = x1.long()\n            x2 = x2.long()\n        x1 = torch.min(x1, x2)\n        x2 = torch.max(x1, x2)\n        x1 = torch.clamp(x1 - padding, min=0)\n        x2 = torch.clamp(x2 + padding, max=img_size)\n        return x1, x2\n\n    def predict_by_feat(self,\n                        mask_preds: List[Tensor],\n                        segm_preds: Tensor,\n                        results_list: InstanceList,\n                        batch_img_metas: List[dict],\n                        rescale: bool = True,\n                        **kwargs) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        mask results.\n\n        Args:\n            mask_preds (list[Tensor]): Predicted prototypes with shape\n                (num_classes, H, W).\n            results_list (List[:obj:``InstanceData``]): BBoxHead results.\n            batch_img_metas (list[dict]): Meta information of all images.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Processed results of multiple\n            images.Each :obj:`InstanceData` usually contains\n            following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n        assert len(mask_preds) == len(results_list) == len(batch_img_metas)\n\n        croped_mask_pred = self.crop_mask_preds(mask_preds, batch_img_metas,\n                                                results_list)\n\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            results = results_list[img_id]\n            bboxes = results.bboxes\n            mask_preds = croped_mask_pred[img_id]\n            if bboxes.shape[0] == 0 or mask_preds.shape[0] == 0:\n                results_list[img_id] = empty_instances(\n                    [img_meta],\n                    bboxes.device,\n                    task_type='mask',\n                    instance_results=[results])[0]\n            else:\n                im_mask = self._predict_by_feat_single(\n                    mask_preds=croped_mask_pred[img_id],\n                    bboxes=bboxes,\n                    img_meta=img_meta,\n                    rescale=rescale)\n                results.masks = im_mask\n        return results_list\n\n    def _predict_by_feat_single(self,\n                                mask_preds: Tensor,\n                                bboxes: Tensor,\n                                img_meta: dict,\n                                rescale: bool,\n                                cfg: OptConfigType = None):\n        \"\"\"Transform a single image's features extracted from the head into\n        mask results.\n\n        Args:\n            mask_preds (Tensor): Predicted prototypes, has shape [H, W, N].\n            bboxes (Tensor): Bbox coords in relative point form with\n                shape [N, 4].\n            img_meta (dict): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            rescale (bool): If rescale is False, then returned masks will\n                fit the scale of imgs[0].\n            cfg (dict, optional): Config used in test phase.\n                Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Processed results of single image.\n             it usually contains following keys.\n\n                - scores (Tensor): Classification scores, has shape\n                  (num_instance,).\n                - labels (Tensor): Has shape (num_instances,).\n                - masks (Tensor): Processed mask results, has\n                  shape (num_instances, h, w).\n        \"\"\"\n        cfg = self.test_cfg if cfg is None else cfg\n        scale_factor = bboxes.new_tensor(img_meta['scale_factor']).repeat(\n            (1, 2))\n        img_h, img_w = img_meta['ori_shape'][:2]\n        if rescale:  # in-placed rescale the bboxes\n            scale_factor = bboxes.new_tensor(img_meta['scale_factor']).repeat(\n                (1, 2))\n            bboxes /= scale_factor\n        else:\n            w_scale, h_scale = scale_factor[0, 0], scale_factor[0, 1]\n            img_h = np.round(img_h * h_scale.item()).astype(np.int32)\n            img_w = np.round(img_w * w_scale.item()).astype(np.int32)\n\n        masks = F.interpolate(\n            mask_preds.unsqueeze(0), (img_h, img_w),\n            mode='bilinear',\n            align_corners=False).squeeze(0) > cfg.mask_thr\n\n        if cfg.mask_thr_binary < 0:\n            # for visualization and debugging\n            masks = (masks * 255).to(dtype=torch.uint8)\n\n        return masks\n\n\nclass SegmentationModule(BaseModule):\n    \"\"\"YOLACT segmentation branch used in <https://arxiv.org/abs/1904.02689>`_\n\n    In mmdet v2.x `segm_loss` is calculated in YOLACTSegmHead, while in\n    mmdet v3.x `SegmentationModule` is used to obtain the predicted semantic\n    segmentation map and `segm_loss` is calculated in YOLACTProtonet.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int = 256,\n        init_cfg: ConfigType = dict(\n            type='Xavier',\n            distribution='uniform',\n            override=dict(name='segm_conv'))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.num_classes = num_classes\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.segm_conv = nn.Conv2d(\n            self.in_channels, self.num_classes, kernel_size=1)\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward feature from the upstream network.\n\n        Args:\n            x (Tensor): Feature from the upstream network, which is\n                a 4D-tensor.\n\n        Returns:\n            Tensor: Predicted semantic segmentation map with shape\n                (N, num_classes, H, W).\n        \"\"\"\n        return self.segm_conv(x)\n\n\nclass InterpolateModule(BaseModule):\n    \"\"\"This is a module version of F.interpolate.\n\n    Any arguments you give it just get passed along for the ride.\n    \"\"\"\n\n    def __init__(self, *args, init_cfg=None, **kwargs) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.args = args\n        self.kwargs = kwargs\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (Tensor): Feature from the upstream network, which is\n                a 4D-tensor.\n\n        Returns:\n            Tensor: A 4D-tensor feature map.\n        \"\"\"\n        return F.interpolate(x, *self.args, **self.kwargs)\n"
  },
  {
    "path": "mmdet/models/dense_heads/yolo_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Copyright (c) 2019 Western Digital Corporation or its affiliates.\n\nimport copy\nimport warnings\nfrom typing import List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, is_norm\nfrom mmengine.model import bias_init_with_prob, constant_init, normal_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList)\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import filter_scores_and_topk, images_to_levels, multi_apply\nfrom .base_dense_head import BaseDenseHead\n\n\n@MODELS.register_module()\nclass YOLOV3Head(BaseDenseHead):\n    \"\"\"YOLOV3Head Paper link: https://arxiv.org/abs/1804.02767.\n\n    Args:\n        num_classes (int): The number of object classes (w/o background)\n        in_channels (Sequence[int]): Number of input channels per scale.\n        out_channels (Sequence[int]): The number of output channels per scale\n            before the final 1x1 layer. Default: (1024, 512, 256).\n        anchor_generator (:obj:`ConfigDict` or dict): Config dict for anchor\n            generator.\n        bbox_coder (:obj:`ConfigDict` or dict): Config of bounding box coder.\n        featmap_strides (Sequence[int]): The stride of each scale.\n            Should be in descending order. Defaults to (32, 16, 8).\n        one_hot_smoother (float): Set a non-zero value to enable label-smooth\n            Defaults to 0.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): Dictionary to construct and\n            config norm layer. Defaults to dict(type='BN', requires_grad=True).\n        act_cfg (:obj:`ConfigDict` or dict): Config dict for activation layer.\n            Defaults to dict(type='LeakyReLU', negative_slope=0.1).\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_conf (:obj:`ConfigDict` or dict): Config of confidence loss.\n        loss_xy (:obj:`ConfigDict` or dict): Config of xy coordinate loss.\n        loss_wh (:obj:`ConfigDict` or dict): Config of wh coordinate loss.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config of\n            YOLOV3 head. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            YOLOV3 head. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: Sequence[int],\n                 out_channels: Sequence[int] = (1024, 512, 256),\n                 anchor_generator: ConfigType = dict(\n                     type='YOLOAnchorGenerator',\n                     base_sizes=[[(116, 90), (156, 198), (373, 326)],\n                                 [(30, 61), (62, 45), (59, 119)],\n                                 [(10, 13), (16, 30), (33, 23)]],\n                     strides=[32, 16, 8]),\n                 bbox_coder: ConfigType = dict(type='YOLOBBoxCoder'),\n                 featmap_strides: Sequence[int] = (32, 16, 8),\n                 one_hot_smoother: float = 0.,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 act_cfg: ConfigType = dict(\n                     type='LeakyReLU', negative_slope=0.1),\n                 loss_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     loss_weight=1.0),\n                 loss_conf: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     loss_weight=1.0),\n                 loss_xy: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     loss_weight=1.0),\n                 loss_wh: ConfigType = dict(type='MSELoss', loss_weight=1.0),\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None) -> None:\n        super().__init__(init_cfg=None)\n        # Check params\n        assert (len(in_channels) == len(out_channels) == len(featmap_strides))\n\n        self.num_classes = num_classes\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.featmap_strides = featmap_strides\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            if train_cfg.get('sampler', None) is not None:\n                self.sampler = TASK_UTILS.build(\n                    self.train_cfg['sampler'], context=self)\n            else:\n                self.sampler = PseudoSampler()\n\n        self.one_hot_smoother = one_hot_smoother\n\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n\n        self.prior_generator = TASK_UTILS.build(anchor_generator)\n\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_conf = MODELS.build(loss_conf)\n        self.loss_xy = MODELS.build(loss_xy)\n        self.loss_wh = MODELS.build(loss_wh)\n\n        self.num_base_priors = self.prior_generator.num_base_priors[0]\n        assert len(\n            self.prior_generator.num_base_priors) == len(featmap_strides)\n        self._init_layers()\n\n    @property\n    def num_levels(self) -> int:\n        \"\"\"int: number of feature map levels\"\"\"\n        return len(self.featmap_strides)\n\n    @property\n    def num_attrib(self) -> int:\n        \"\"\"int: number of attributes in pred_map, bboxes (4) +\n        objectness (1) + num_classes\"\"\"\n\n        return 5 + self.num_classes\n\n    def _init_layers(self) -> None:\n        \"\"\"initialize conv layers in YOLOv3 head.\"\"\"\n        self.convs_bridge = nn.ModuleList()\n        self.convs_pred = nn.ModuleList()\n        for i in range(self.num_levels):\n            conv_bridge = ConvModule(\n                self.in_channels[i],\n                self.out_channels[i],\n                3,\n                padding=1,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg,\n                act_cfg=self.act_cfg)\n            conv_pred = nn.Conv2d(self.out_channels[i],\n                                  self.num_base_priors * self.num_attrib, 1)\n\n            self.convs_bridge.append(conv_bridge)\n            self.convs_pred.append(conv_pred)\n\n    def init_weights(self) -> None:\n        \"\"\"initialize weights.\"\"\"\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, mean=0, std=0.01)\n            if is_norm(m):\n                constant_init(m, 1)\n\n        # Use prior in model initialization to improve stability\n        for conv_pred, stride in zip(self.convs_pred, self.featmap_strides):\n            bias = conv_pred.bias.reshape(self.num_base_priors, -1)\n            # init objectness with prior of 8 objects per feature map\n            # refer to https://github.com/ultralytics/yolov3\n            nn.init.constant_(bias.data[:, 4],\n                              bias_init_with_prob(8 / (608 / stride)**2))\n            nn.init.constant_(bias.data[:, 5:], bias_init_with_prob(0.01))\n\n    def forward(self, x: Tuple[Tensor, ...]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple[Tensor]: A tuple of multi-level predication map, each is a\n                4D-tensor of shape (batch_size, 5+num_classes, height, width).\n        \"\"\"\n\n        assert len(x) == self.num_levels\n        pred_maps = []\n        for i in range(self.num_levels):\n            feat = x[i]\n            feat = self.convs_bridge[i](feat)\n            pred_map = self.convs_pred[i](feat)\n            pred_maps.append(pred_map)\n\n        return tuple(pred_maps),\n\n    def predict_by_feat(self,\n                        pred_maps: Sequence[Tensor],\n                        batch_img_metas: Optional[List[dict]],\n                        cfg: OptConfigType = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results. It has been accelerated since PR #5991.\n\n        Args:\n            pred_maps (Sequence[Tensor]): Raw predictions for a batch of\n                images.\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n                Defaults to None.\n            cfg (:obj:`ConfigDict` or dict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(pred_maps) == self.num_levels\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n\n        num_imgs = len(batch_img_metas)\n        featmap_sizes = [pred_map.shape[-2:] for pred_map in pred_maps]\n\n        mlvl_anchors = self.prior_generator.grid_priors(\n            featmap_sizes, device=pred_maps[0].device)\n        flatten_preds = []\n        flatten_strides = []\n        for pred, stride in zip(pred_maps, self.featmap_strides):\n            pred = pred.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                    self.num_attrib)\n            pred[..., :2].sigmoid_()\n            flatten_preds.append(pred)\n            flatten_strides.append(\n                pred.new_tensor(stride).expand(pred.size(1)))\n\n        flatten_preds = torch.cat(flatten_preds, dim=1)\n        flatten_bbox_preds = flatten_preds[..., :4]\n        flatten_objectness = flatten_preds[..., 4].sigmoid()\n        flatten_cls_scores = flatten_preds[..., 5:].sigmoid()\n        flatten_anchors = torch.cat(mlvl_anchors)\n        flatten_strides = torch.cat(flatten_strides)\n        flatten_bboxes = self.bbox_coder.decode(flatten_anchors,\n                                                flatten_bbox_preds,\n                                                flatten_strides.unsqueeze(-1))\n        results_list = []\n        for (bboxes, scores, objectness,\n             img_meta) in zip(flatten_bboxes, flatten_cls_scores,\n                              flatten_objectness, batch_img_metas):\n            # Filtering out all predictions with conf < conf_thr\n            conf_thr = cfg.get('conf_thr', -1)\n            if conf_thr > 0:\n                conf_inds = objectness >= conf_thr\n                bboxes = bboxes[conf_inds, :]\n                scores = scores[conf_inds, :]\n                objectness = objectness[conf_inds]\n\n            score_thr = cfg.get('score_thr', 0)\n            nms_pre = cfg.get('nms_pre', -1)\n            scores, labels, keep_idxs, _ = filter_scores_and_topk(\n                scores, score_thr, nms_pre)\n\n            results = InstanceData(\n                scores=scores,\n                labels=labels,\n                bboxes=bboxes[keep_idxs],\n                score_factors=objectness[keep_idxs],\n            )\n            results = self._bbox_post_process(\n                results=results,\n                cfg=cfg,\n                rescale=rescale,\n                with_nms=with_nms,\n                img_meta=img_meta)\n            results_list.append(results)\n        return results_list\n\n    def loss_by_feat(\n            self,\n            pred_maps: Sequence[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            pred_maps (list[Tensor]): Prediction map for each scale level,\n                shape (N, num_anchors * num_attrib, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        device = pred_maps[0][0].device\n\n        featmap_sizes = [\n            pred_maps[i].shape[-2:] for i in range(self.num_levels)\n        ]\n        mlvl_anchors = self.prior_generator.grid_priors(\n            featmap_sizes, device=device)\n        anchor_list = [mlvl_anchors for _ in range(num_imgs)]\n\n        responsible_flag_list = []\n        for img_id in range(num_imgs):\n            responsible_flag_list.append(\n                self.responsible_flags(featmap_sizes,\n                                       batch_gt_instances[img_id].bboxes,\n                                       device))\n\n        target_maps_list, neg_maps_list = self.get_targets(\n            anchor_list, responsible_flag_list, batch_gt_instances)\n\n        losses_cls, losses_conf, losses_xy, losses_wh = multi_apply(\n            self.loss_by_feat_single, pred_maps, target_maps_list,\n            neg_maps_list)\n\n        return dict(\n            loss_cls=losses_cls,\n            loss_conf=losses_conf,\n            loss_xy=losses_xy,\n            loss_wh=losses_wh)\n\n    def loss_by_feat_single(self, pred_map: Tensor, target_map: Tensor,\n                            neg_map: Tensor) -> tuple:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            pred_map (Tensor): Raw predictions for a single level.\n            target_map (Tensor): The Ground-Truth target for a single level.\n            neg_map (Tensor): The negative masks for a single level.\n\n        Returns:\n            tuple:\n                loss_cls (Tensor): Classification loss.\n                loss_conf (Tensor): Confidence loss.\n                loss_xy (Tensor): Regression loss of x, y coordinate.\n                loss_wh (Tensor): Regression loss of w, h coordinate.\n        \"\"\"\n\n        num_imgs = len(pred_map)\n        pred_map = pred_map.permute(0, 2, 3,\n                                    1).reshape(num_imgs, -1, self.num_attrib)\n        neg_mask = neg_map.float()\n        pos_mask = target_map[..., 4]\n        pos_and_neg_mask = neg_mask + pos_mask\n        pos_mask = pos_mask.unsqueeze(dim=-1)\n        if torch.max(pos_and_neg_mask) > 1.:\n            warnings.warn('There is overlap between pos and neg sample.')\n            pos_and_neg_mask = pos_and_neg_mask.clamp(min=0., max=1.)\n\n        pred_xy = pred_map[..., :2]\n        pred_wh = pred_map[..., 2:4]\n        pred_conf = pred_map[..., 4]\n        pred_label = pred_map[..., 5:]\n\n        target_xy = target_map[..., :2]\n        target_wh = target_map[..., 2:4]\n        target_conf = target_map[..., 4]\n        target_label = target_map[..., 5:]\n\n        loss_cls = self.loss_cls(pred_label, target_label, weight=pos_mask)\n        loss_conf = self.loss_conf(\n            pred_conf, target_conf, weight=pos_and_neg_mask)\n        loss_xy = self.loss_xy(pred_xy, target_xy, weight=pos_mask)\n        loss_wh = self.loss_wh(pred_wh, target_wh, weight=pos_mask)\n\n        return loss_cls, loss_conf, loss_xy, loss_wh\n\n    def get_targets(self, anchor_list: List[List[Tensor]],\n                    responsible_flag_list: List[List[Tensor]],\n                    batch_gt_instances: List[InstanceData]) -> tuple:\n        \"\"\"Compute target maps for anchors in multiple images.\n\n        Args:\n            anchor_list (list[list[Tensor]]): Multi level anchors of each\n                image. The outer list indicates images, and the inner list\n                corresponds to feature levels of the image. Each element of\n                the inner list is a tensor of shape (num_total_anchors, 4).\n            responsible_flag_list (list[list[Tensor]]): Multi level responsible\n                flags of each image. Each element is a tensor of shape\n                (num_total_anchors, )\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n                - target_map_list (list[Tensor]): Target map of each level.\n                - neg_map_list (list[Tensor]): Negative map of each level.\n        \"\"\"\n        num_imgs = len(anchor_list)\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n\n        results = multi_apply(self._get_targets_single, anchor_list,\n                              responsible_flag_list, batch_gt_instances)\n\n        all_target_maps, all_neg_maps = results\n        assert num_imgs == len(all_target_maps) == len(all_neg_maps)\n        target_maps_list = images_to_levels(all_target_maps, num_level_anchors)\n        neg_maps_list = images_to_levels(all_neg_maps, num_level_anchors)\n\n        return target_maps_list, neg_maps_list\n\n    def _get_targets_single(self, anchors: List[Tensor],\n                            responsible_flags: List[Tensor],\n                            gt_instances: InstanceData) -> tuple:\n        \"\"\"Generate matching bounding box prior and converted GT.\n\n        Args:\n            anchors (List[Tensor]): Multi-level anchors of the image.\n            responsible_flags (List[Tensor]): Multi-level responsible flags of\n                anchors\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            tuple:\n                target_map (Tensor): Predication target map of each\n                    scale level, shape (num_total_anchors,\n                    5+num_classes)\n                neg_map (Tensor): Negative map of each scale level,\n                    shape (num_total_anchors,)\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        anchor_strides = []\n        for i in range(len(anchors)):\n            anchor_strides.append(\n                torch.tensor(self.featmap_strides[i],\n                             device=gt_bboxes.device).repeat(len(anchors[i])))\n        concat_anchors = torch.cat(anchors)\n        concat_responsible_flags = torch.cat(responsible_flags)\n\n        anchor_strides = torch.cat(anchor_strides)\n        assert len(anchor_strides) == len(concat_anchors) == \\\n               len(concat_responsible_flags)\n        pred_instances = InstanceData(\n            priors=concat_anchors, responsible_flags=concat_responsible_flags)\n\n        assign_result = self.assigner.assign(pred_instances, gt_instances)\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        target_map = concat_anchors.new_zeros(\n            concat_anchors.size(0), self.num_attrib)\n\n        target_map[sampling_result.pos_inds, :4] = self.bbox_coder.encode(\n            sampling_result.pos_priors, sampling_result.pos_gt_bboxes,\n            anchor_strides[sampling_result.pos_inds])\n\n        target_map[sampling_result.pos_inds, 4] = 1\n\n        gt_labels_one_hot = F.one_hot(\n            gt_labels, num_classes=self.num_classes).float()\n        if self.one_hot_smoother != 0:  # label smooth\n            gt_labels_one_hot = gt_labels_one_hot * (\n                1 - self.one_hot_smoother\n            ) + self.one_hot_smoother / self.num_classes\n        target_map[sampling_result.pos_inds, 5:] = gt_labels_one_hot[\n            sampling_result.pos_assigned_gt_inds]\n\n        neg_map = concat_anchors.new_zeros(\n            concat_anchors.size(0), dtype=torch.uint8)\n        neg_map[sampling_result.neg_inds] = 1\n\n        return target_map, neg_map\n\n    def responsible_flags(self, featmap_sizes: List[tuple], gt_bboxes: Tensor,\n                          device: str) -> List[Tensor]:\n        \"\"\"Generate responsible anchor flags of grid cells in multiple scales.\n\n        Args:\n            featmap_sizes (List[tuple]): List of feature map sizes in multiple\n                feature levels.\n            gt_bboxes (Tensor): Ground truth boxes, shape (n, 4).\n            device (str): Device where the anchors will be put on.\n\n        Return:\n            List[Tensor]: responsible flags of anchors in multiple level\n        \"\"\"\n        assert self.num_levels == len(featmap_sizes)\n        multi_level_responsible_flags = []\n        for i in range(self.num_levels):\n            anchor_stride = self.prior_generator.strides[i]\n            feat_h, feat_w = featmap_sizes[i]\n            gt_cx = ((gt_bboxes[:, 0] + gt_bboxes[:, 2]) * 0.5).to(device)\n            gt_cy = ((gt_bboxes[:, 1] + gt_bboxes[:, 3]) * 0.5).to(device)\n            gt_grid_x = torch.floor(gt_cx / anchor_stride[0]).long()\n            gt_grid_y = torch.floor(gt_cy / anchor_stride[1]).long()\n            # row major indexing\n            gt_bboxes_grid_idx = gt_grid_y * feat_w + gt_grid_x\n\n            responsible_grid = torch.zeros(\n                feat_h * feat_w, dtype=torch.uint8, device=device)\n            responsible_grid[gt_bboxes_grid_idx] = 1\n\n            responsible_grid = responsible_grid[:, None].expand(\n                responsible_grid.size(0),\n                self.prior_generator.num_base_priors[i]).contiguous().view(-1)\n\n            multi_level_responsible_flags.append(responsible_grid)\n        return multi_level_responsible_flags\n"
  },
  {
    "path": "mmdet/models/dense_heads/yolof_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, is_norm\nfrom mmengine.model import bias_init_with_prob, constant_init, normal_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, InstanceList, OptInstanceList, reduce_mean\nfrom ..task_modules.prior_generators import anchor_inside_flags\nfrom ..utils import levels_to_images, multi_apply, unmap\nfrom .anchor_head import AnchorHead\n\nINF = 1e8\n\n\n@MODELS.register_module()\nclass YOLOFHead(AnchorHead):\n    \"\"\"Detection Head of `YOLOF <https://arxiv.org/abs/2103.09460>`_\n\n    Args:\n        num_classes (int): The number of object classes (w/o background)\n        in_channels (list[int]): The number of input channels per scale.\n        cls_num_convs (int): The number of convolutions of cls branch.\n           Defaults to 2.\n        reg_num_convs (int): The number of convolutions of reg branch.\n           Defaults to 4.\n        norm_cfg (:obj:`ConfigDict` or dict): Config dict for normalization\n            layer. Defaults to ``dict(type='BN', requires_grad=True)``.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: List[int],\n                 num_cls_convs: int = 2,\n                 num_reg_convs: int = 4,\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 **kwargs) -> None:\n        self.num_cls_convs = num_cls_convs\n        self.num_reg_convs = num_reg_convs\n        self.norm_cfg = norm_cfg\n        super().__init__(\n            num_classes=num_classes, in_channels=in_channels, **kwargs)\n\n    def _init_layers(self) -> None:\n        cls_subnet = []\n        bbox_subnet = []\n        for i in range(self.num_cls_convs):\n            cls_subnet.append(\n                ConvModule(\n                    self.in_channels,\n                    self.in_channels,\n                    kernel_size=3,\n                    padding=1,\n                    norm_cfg=self.norm_cfg))\n        for i in range(self.num_reg_convs):\n            bbox_subnet.append(\n                ConvModule(\n                    self.in_channels,\n                    self.in_channels,\n                    kernel_size=3,\n                    padding=1,\n                    norm_cfg=self.norm_cfg))\n        self.cls_subnet = nn.Sequential(*cls_subnet)\n        self.bbox_subnet = nn.Sequential(*bbox_subnet)\n        self.cls_score = nn.Conv2d(\n            self.in_channels,\n            self.num_base_priors * self.num_classes,\n            kernel_size=3,\n            stride=1,\n            padding=1)\n        self.bbox_pred = nn.Conv2d(\n            self.in_channels,\n            self.num_base_priors * 4,\n            kernel_size=3,\n            stride=1,\n            padding=1)\n        self.object_pred = nn.Conv2d(\n            self.in_channels,\n            self.num_base_priors,\n            kernel_size=3,\n            stride=1,\n            padding=1)\n\n    def init_weights(self) -> None:\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, mean=0, std=0.01)\n            if is_norm(m):\n                constant_init(m, 1)\n\n        # Use prior in model initialization to improve stability\n        bias_cls = bias_init_with_prob(0.01)\n        torch.nn.init.constant_(self.cls_score.bias, bias_cls)\n\n    def forward_single(self, x: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward feature of a single scale level.\n\n        Args:\n            x (Tensor): Features of a single scale level.\n\n        Returns:\n            tuple:\n                normalized_cls_score (Tensor): Normalized Cls scores for a \\\n                    single scale level, the channels number is \\\n                    num_base_priors * num_classes.\n                bbox_reg (Tensor): Box energies / deltas for a single scale \\\n                    level, the channels number is num_base_priors * 4.\n        \"\"\"\n        cls_score = self.cls_score(self.cls_subnet(x))\n        N, _, H, W = cls_score.shape\n        cls_score = cls_score.view(N, -1, self.num_classes, H, W)\n\n        reg_feat = self.bbox_subnet(x)\n        bbox_reg = self.bbox_pred(reg_feat)\n        objectness = self.object_pred(reg_feat)\n\n        # implicit objectness\n        objectness = objectness.view(N, -1, 1, H, W)\n        normalized_cls_score = cls_score + objectness - torch.log(\n            1. + torch.clamp(cls_score.exp(), max=INF) +\n            torch.clamp(objectness.exp(), max=INF))\n        normalized_cls_score = normalized_cls_score.view(N, -1, H, W)\n        return normalized_cls_score, bbox_reg\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        assert len(cls_scores) == 1\n        assert self.prior_generator.num_levels == 1\n\n        device = cls_scores[0].device\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        # The output level is always 1\n        anchor_list = [anchors[0] for anchors in anchor_list]\n        valid_flag_list = [valid_flags[0] for valid_flags in valid_flag_list]\n\n        cls_scores_list = levels_to_images(cls_scores)\n        bbox_preds_list = levels_to_images(bbox_preds)\n\n        cls_reg_targets = self.get_targets(\n            cls_scores_list,\n            bbox_preds_list,\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        if cls_reg_targets is None:\n            return None\n        (batch_labels, batch_label_weights, avg_factor, batch_bbox_weights,\n         batch_pos_predicted_boxes, batch_target_boxes) = cls_reg_targets\n\n        flatten_labels = batch_labels.reshape(-1)\n        batch_label_weights = batch_label_weights.reshape(-1)\n        cls_score = cls_scores[0].permute(0, 2, 3,\n                                          1).reshape(-1, self.cls_out_channels)\n\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n\n        # classification loss\n        loss_cls = self.loss_cls(\n            cls_score,\n            flatten_labels,\n            batch_label_weights,\n            avg_factor=avg_factor)\n\n        # regression loss\n        if batch_pos_predicted_boxes.shape[0] == 0:\n            # no pos sample\n            loss_bbox = batch_pos_predicted_boxes.sum() * 0\n        else:\n            loss_bbox = self.loss_bbox(\n                batch_pos_predicted_boxes,\n                batch_target_boxes,\n                batch_bbox_weights.float(),\n                avg_factor=avg_factor)\n\n        return dict(loss_cls=loss_cls, loss_bbox=loss_bbox)\n\n    def get_targets(self,\n                    cls_scores_list: List[Tensor],\n                    bbox_preds_list: List[Tensor],\n                    anchor_list: List[Tensor],\n                    valid_flag_list: List[Tensor],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs: bool = True):\n        \"\"\"Compute regression and classification targets for anchors in\n        multiple images.\n\n        Args:\n            cls_scores_list (list[Tensor]): Classification scores of\n                each image. each is a 4D-tensor, the shape is\n                (h * w, num_anchors * num_classes).\n            bbox_preds_list (list[Tensor]): Bbox preds of each image.\n                each is a 4D-tensor, the shape is (h * w, num_anchors * 4).\n            anchor_list (list[Tensor]): Anchors of each image. Each element of\n                is a tensor of shape (h * w * num_anchors, 4).\n            valid_flag_list (list[Tensor]): Valid flags of each image. Each\n               element of is a tensor of shape (h * w * num_anchors, )\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Returns:\n            tuple: Usually returns a tuple containing learning targets.\n\n                - batch_labels (Tensor): Label of all images. Each element \\\n                    of is a tensor of shape (batch, h * w * num_anchors)\n                - batch_label_weights (Tensor): Label weights of all images \\\n                    of is a tensor of shape (batch, h * w * num_anchors)\n                - num_total_pos (int): Number of positive samples in all \\\n                    images.\n                - num_total_neg (int): Number of negative samples in all \\\n                    images.\n            additional_returns: This function enables user-defined returns from\n                `self._get_targets_single`. These returns are currently refined\n                to properties at each feature map (i.e. having HxW dimension).\n                The results will be concatenated after the end\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        results = multi_apply(\n            self._get_targets_single,\n            bbox_preds_list,\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore,\n            unmap_outputs=unmap_outputs)\n        (all_labels, all_label_weights, pos_inds, neg_inds,\n         sampling_results_list) = results[:5]\n        # Get `avg_factor` of all images, which calculate in `SamplingResult`.\n        # When using sampling method, avg_factor is usually the sum of\n        # positive and negative priors. When using `PseudoSampler`,\n        # `avg_factor` is usually equal to the number of positive priors.\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        rest_results = list(results[5:])  # user-added return values\n\n        batch_labels = torch.stack(all_labels, 0)\n        batch_label_weights = torch.stack(all_label_weights, 0)\n\n        res = (batch_labels, batch_label_weights, avg_factor)\n        for i, rests in enumerate(rest_results):  # user-added return values\n            rest_results[i] = torch.cat(rests, 0)\n\n        return res + tuple(rest_results)\n\n    def _get_targets_single(self,\n                            bbox_preds: Tensor,\n                            flat_anchors: Tensor,\n                            valid_flags: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict,\n                            gt_instances_ignore: Optional[InstanceData] = None,\n                            unmap_outputs: bool = True) -> tuple:\n        \"\"\"Compute regression and classification targets for anchors in a\n        single image.\n\n        Args:\n            bbox_preds (Tensor): Bbox prediction of the image, which\n                shape is (h * w ,4)\n            flat_anchors (Tensor): Anchors of the image, which shape is\n                (h * w * num_anchors ,4)\n            valid_flags (Tensor): Valid flags of the image, which shape is\n                (h * w * num_anchors,).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            unmap_outputs (bool): Whether to map outputs back to the original\n                set of anchors.\n\n        Returns:\n            tuple:\n                labels (Tensor): Labels of image, which shape is\n                    (h * w * num_anchors, ).\n                label_weights (Tensor): Label weights of image, which shape is\n                    (h * w * num_anchors, ).\n                pos_inds (Tensor): Pos index of image.\n                neg_inds (Tensor): Neg index of image.\n                sampling_result (obj:`SamplingResult`): Sampling result.\n                pos_bbox_weights (Tensor): The Weight of using to calculate\n                    the bbox branch loss, which shape is (num, ).\n                pos_predicted_boxes (Tensor): boxes predicted value of\n                    using to calculate the bbox branch loss, which shape is\n                    (num, 4).\n                pos_target_boxes (Tensor): boxes target value of\n                    using to calculate the bbox branch loss, which shape is\n                    (num, 4).\n        \"\"\"\n        inside_flags = anchor_inside_flags(flat_anchors, valid_flags,\n                                           img_meta['img_shape'][:2],\n                                           self.train_cfg['allowed_border'])\n        if not inside_flags.any():\n            raise ValueError(\n                'There is no valid anchor inside the image boundary. Please '\n                'check the image size and anchor sizes, or set '\n                '``allowed_border`` to -1 to skip the condition.')\n\n        # assign gt and sample anchors\n        anchors = flat_anchors[inside_flags, :]\n        bbox_preds = bbox_preds.reshape(-1, 4)\n        bbox_preds = bbox_preds[inside_flags, :]\n\n        # decoded bbox\n        decoder_bbox_preds = self.bbox_coder.decode(anchors, bbox_preds)\n        pred_instances = InstanceData(\n            priors=anchors, decoder_priors=decoder_bbox_preds)\n        assign_result = self.assigner.assign(pred_instances, gt_instances,\n                                             gt_instances_ignore)\n\n        pos_bbox_weights = assign_result.get_extra_property('pos_idx')\n        pos_predicted_boxes = assign_result.get_extra_property(\n            'pos_predicted_boxes')\n        pos_target_boxes = assign_result.get_extra_property('target_boxes')\n\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n        num_valid_anchors = anchors.shape[0]\n        labels = anchors.new_full((num_valid_anchors, ),\n                                  self.num_classes,\n                                  dtype=torch.long)\n        label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            labels[pos_inds] = sampling_result.pos_gt_labels\n            if self.train_cfg['pos_weight'] <= 0:\n                label_weights[pos_inds] = 1.0\n            else:\n                label_weights[pos_inds] = self.train_cfg['pos_weight']\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        # map up to original set of anchors\n        if unmap_outputs:\n            num_total_anchors = flat_anchors.size(0)\n            labels = unmap(\n                labels, num_total_anchors, inside_flags,\n                fill=self.num_classes)  # fill bg label\n            label_weights = unmap(label_weights, num_total_anchors,\n                                  inside_flags)\n\n        return (labels, label_weights, pos_inds, neg_inds, sampling_result,\n                pos_bbox_weights, pos_predicted_boxes, pos_target_boxes)\n"
  },
  {
    "path": "mmdet/models/dense_heads/yolox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import List, Optional, Sequence, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmcv.ops.nms import batched_nms\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import bias_init_with_prob\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import bbox_xyxy_to_cxcywh\nfrom mmdet.utils import (ConfigType, OptConfigType, OptInstanceList,\n                         OptMultiConfig, reduce_mean)\nfrom ..task_modules.prior_generators import MlvlPointGenerator\nfrom ..task_modules.samplers import PseudoSampler\nfrom ..utils import multi_apply\nfrom .base_dense_head import BaseDenseHead\n\n\n@MODELS.register_module()\nclass YOLOXHead(BaseDenseHead):\n    \"\"\"YOLOXHead head used in `YOLOX <https://arxiv.org/abs/2107.08430>`_.\n\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channels in the input feature map.\n        feat_channels (int): Number of hidden channels in stacking convs.\n            Defaults to 256\n        stacked_convs (int): Number of stacking convs of the head.\n            Defaults to (8, 16, 32).\n        strides (Sequence[int]): Downsample factor of each feature map.\n             Defaults to None.\n        use_depthwise (bool): Whether to depthwise separable convolution in\n            blocks. Defaults to False.\n        dcn_on_last_conv (bool): If true, use dcn in the last layer of\n            towers. Defaults to False.\n        conv_bias (bool or str): If specified as `auto`, it will be decided by\n            the norm_cfg. Bias of conv will be set as True if `norm_cfg` is\n            None, otherwise False. Defaults to \"auto\".\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): Config dict for normalization\n            layer. Defaults to dict(type='BN', momentum=0.03, eps=0.001).\n        act_cfg (:obj:`ConfigDict` or dict): Config dict for activation layer.\n            Defaults to None.\n        loss_cls (:obj:`ConfigDict` or dict): Config of classification loss.\n        loss_bbox (:obj:`ConfigDict` or dict): Config of localization loss.\n        loss_obj (:obj:`ConfigDict` or dict): Config of objectness loss.\n        loss_l1 (:obj:`ConfigDict` or dict): Config of L1 loss.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config of\n            anchor head. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            anchor head. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes: int,\n        in_channels: int,\n        feat_channels: int = 256,\n        stacked_convs: int = 2,\n        strides: Sequence[int] = (8, 16, 32),\n        use_depthwise: bool = False,\n        dcn_on_last_conv: bool = False,\n        conv_bias: Union[bool, str] = 'auto',\n        conv_cfg: OptConfigType = None,\n        norm_cfg: ConfigType = dict(type='BN', momentum=0.03, eps=0.001),\n        act_cfg: ConfigType = dict(type='Swish'),\n        loss_cls: ConfigType = dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='sum',\n            loss_weight=1.0),\n        loss_bbox: ConfigType = dict(\n            type='IoULoss',\n            mode='square',\n            eps=1e-16,\n            reduction='sum',\n            loss_weight=5.0),\n        loss_obj: ConfigType = dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='sum',\n            loss_weight=1.0),\n        loss_l1: ConfigType = dict(\n            type='L1Loss', reduction='sum', loss_weight=1.0),\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        init_cfg: OptMultiConfig = dict(\n            type='Kaiming',\n            layer='Conv2d',\n            a=math.sqrt(5),\n            distribution='uniform',\n            mode='fan_in',\n            nonlinearity='leaky_relu')\n    ) -> None:\n\n        super().__init__(init_cfg=init_cfg)\n        self.num_classes = num_classes\n        self.cls_out_channels = num_classes\n        self.in_channels = in_channels\n        self.feat_channels = feat_channels\n        self.stacked_convs = stacked_convs\n        self.strides = strides\n        self.use_depthwise = use_depthwise\n        self.dcn_on_last_conv = dcn_on_last_conv\n        assert conv_bias == 'auto' or isinstance(conv_bias, bool)\n        self.conv_bias = conv_bias\n        self.use_sigmoid_cls = True\n\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n\n        self.loss_cls: nn.Module = MODELS.build(loss_cls)\n        self.loss_bbox: nn.Module = MODELS.build(loss_bbox)\n        self.loss_obj: nn.Module = MODELS.build(loss_obj)\n\n        self.use_l1 = False  # This flag will be modified by hooks.\n        self.loss_l1: nn.Module = MODELS.build(loss_l1)\n\n        self.prior_generator = MlvlPointGenerator(strides, offset=0)\n\n        self.test_cfg = test_cfg\n        self.train_cfg = train_cfg\n\n        if self.train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n            # YOLOX does not support sampling\n            self.sampler = PseudoSampler()\n\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize heads for all level feature maps.\"\"\"\n        self.multi_level_cls_convs = nn.ModuleList()\n        self.multi_level_reg_convs = nn.ModuleList()\n        self.multi_level_conv_cls = nn.ModuleList()\n        self.multi_level_conv_reg = nn.ModuleList()\n        self.multi_level_conv_obj = nn.ModuleList()\n        for _ in self.strides:\n            self.multi_level_cls_convs.append(self._build_stacked_convs())\n            self.multi_level_reg_convs.append(self._build_stacked_convs())\n            conv_cls, conv_reg, conv_obj = self._build_predictor()\n            self.multi_level_conv_cls.append(conv_cls)\n            self.multi_level_conv_reg.append(conv_reg)\n            self.multi_level_conv_obj.append(conv_obj)\n\n    def _build_stacked_convs(self) -> nn.Sequential:\n        \"\"\"Initialize conv layers of a single level head.\"\"\"\n        conv = DepthwiseSeparableConvModule \\\n            if self.use_depthwise else ConvModule\n        stacked_convs = []\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            if self.dcn_on_last_conv and i == self.stacked_convs - 1:\n                conv_cfg = dict(type='DCNv2')\n            else:\n                conv_cfg = self.conv_cfg\n            stacked_convs.append(\n                conv(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    act_cfg=self.act_cfg,\n                    bias=self.conv_bias))\n        return nn.Sequential(*stacked_convs)\n\n    def _build_predictor(self) -> Tuple[nn.Module, nn.Module, nn.Module]:\n        \"\"\"Initialize predictor layers of a single level head.\"\"\"\n        conv_cls = nn.Conv2d(self.feat_channels, self.cls_out_channels, 1)\n        conv_reg = nn.Conv2d(self.feat_channels, 4, 1)\n        conv_obj = nn.Conv2d(self.feat_channels, 1, 1)\n        return conv_cls, conv_reg, conv_obj\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        super(YOLOXHead, self).init_weights()\n        # Use prior in model initialization to improve stability\n        bias_init = bias_init_with_prob(0.01)\n        for conv_cls, conv_obj in zip(self.multi_level_conv_cls,\n                                      self.multi_level_conv_obj):\n            conv_cls.bias.data.fill_(bias_init)\n            conv_obj.bias.data.fill_(bias_init)\n\n    def forward_single(self, x: Tensor, cls_convs: nn.Module,\n                       reg_convs: nn.Module, conv_cls: nn.Module,\n                       conv_reg: nn.Module,\n                       conv_obj: nn.Module) -> Tuple[Tensor, Tensor, Tensor]:\n        \"\"\"Forward feature of a single scale level.\"\"\"\n\n        cls_feat = cls_convs(x)\n        reg_feat = reg_convs(x)\n\n        cls_score = conv_cls(cls_feat)\n        bbox_pred = conv_reg(reg_feat)\n        objectness = conv_obj(reg_feat)\n\n        return cls_score, bbox_pred, objectness\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (Tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n        Returns:\n            Tuple[List]: A tuple of multi-level classification scores, bbox\n            predictions, and objectnesses.\n        \"\"\"\n\n        return multi_apply(self.forward_single, x, self.multi_level_cls_convs,\n                           self.multi_level_reg_convs,\n                           self.multi_level_conv_cls,\n                           self.multi_level_conv_reg,\n                           self.multi_level_conv_obj)\n\n    def predict_by_feat(self,\n                        cls_scores: List[Tensor],\n                        bbox_preds: List[Tensor],\n                        objectnesses: Optional[List[Tensor]],\n                        batch_img_metas: Optional[List[dict]] = None,\n                        cfg: Optional[ConfigDict] = None,\n                        rescale: bool = False,\n                        with_nms: bool = True) -> List[InstanceData]:\n        \"\"\"Transform a batch of output features extracted by the head into\n        bbox results.\n        Args:\n            cls_scores (list[Tensor]): Classification scores for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for all\n                scale levels, each is a 4D-tensor, has shape\n                (batch_size, num_priors * 4, H, W).\n            objectnesses (list[Tensor], Optional): Score factor for\n                all scale level, each is a 4D-tensor, has shape\n                (batch_size, 1, H, W).\n            batch_img_metas (list[dict], Optional): Batch image meta info.\n                Defaults to None.\n            cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`InstanceData`]: Object detection results of each image\n            after the post process. Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds) == len(objectnesses)\n        cfg = self.test_cfg if cfg is None else cfg\n\n        num_imgs = len(batch_img_metas)\n        featmap_sizes = [cls_score.shape[2:] for cls_score in cls_scores]\n        mlvl_priors = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=cls_scores[0].dtype,\n            device=cls_scores[0].device,\n            with_stride=True)\n\n        # flatten cls_scores, bbox_preds and objectness\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                  self.cls_out_channels)\n            for cls_score in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_objectness = [\n            objectness.permute(0, 2, 3, 1).reshape(num_imgs, -1)\n            for objectness in objectnesses\n        ]\n\n        flatten_cls_scores = torch.cat(flatten_cls_scores, dim=1).sigmoid()\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds, dim=1)\n        flatten_objectness = torch.cat(flatten_objectness, dim=1).sigmoid()\n        flatten_priors = torch.cat(mlvl_priors)\n\n        flatten_bboxes = self._bbox_decode(flatten_priors, flatten_bbox_preds)\n\n        result_list = []\n        for img_id, img_meta in enumerate(batch_img_metas):\n            max_scores, labels = torch.max(flatten_cls_scores[img_id], 1)\n            valid_mask = flatten_objectness[\n                img_id] * max_scores >= cfg.score_thr\n            results = InstanceData(\n                bboxes=flatten_bboxes[img_id][valid_mask],\n                scores=max_scores[valid_mask] *\n                flatten_objectness[img_id][valid_mask],\n                labels=labels[valid_mask])\n\n            result_list.append(\n                self._bbox_post_process(\n                    results=results,\n                    cfg=cfg,\n                    rescale=rescale,\n                    with_nms=with_nms,\n                    img_meta=img_meta))\n\n        return result_list\n\n    def _bbox_decode(self, priors: Tensor, bbox_preds: Tensor) -> Tensor:\n        \"\"\"Decode regression results (delta_x, delta_x, w, h) to bboxes (tl_x,\n        tl_y, br_x, br_y).\n\n        Args:\n            priors (Tensor): Center proiors of an image, has shape\n                (num_instances, 2).\n            bbox_preds (Tensor): Box energies / deltas for all instances,\n                has shape (batch_size, num_instances, 4).\n\n        Returns:\n            Tensor: Decoded bboxes in (tl_x, tl_y, br_x, br_y) format. Has\n            shape (batch_size, num_instances, 4).\n        \"\"\"\n        xys = (bbox_preds[..., :2] * priors[:, 2:]) + priors[:, :2]\n        whs = bbox_preds[..., 2:].exp() * priors[:, 2:]\n\n        tl_x = (xys[..., 0] - whs[..., 0] / 2)\n        tl_y = (xys[..., 1] - whs[..., 1] / 2)\n        br_x = (xys[..., 0] + whs[..., 0] / 2)\n        br_y = (xys[..., 1] + whs[..., 1] / 2)\n\n        decoded_bboxes = torch.stack([tl_x, tl_y, br_x, br_y], -1)\n        return decoded_bboxes\n\n    def _bbox_post_process(self,\n                           results: InstanceData,\n                           cfg: ConfigDict,\n                           rescale: bool = False,\n                           with_nms: bool = True,\n                           img_meta: Optional[dict] = None) -> InstanceData:\n        \"\"\"bbox post-processing method.\n\n        The boxes would be rescaled to the original image scale and do\n        the nms operation. Usually `with_nms` is False is used for aug test.\n\n        Args:\n            results (:obj:`InstaceData`): Detection instance results,\n                each item has shape (num_bboxes, ).\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Default to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Default to True.\n            img_meta (dict, optional): Image meta info. Defaults to None.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            results.bboxes /= results.bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n\n        if with_nms and results.bboxes.numel() > 0:\n            det_bboxes, keep_idxs = batched_nms(results.bboxes, results.scores,\n                                                results.labels, cfg.nms)\n            results = results[keep_idxs]\n            # some nms would reweight the score, such as softnms\n            results.scores = det_bboxes[:, -1]\n        return results\n\n    def loss_by_feat(\n            self,\n            cls_scores: Sequence[Tensor],\n            bbox_preds: Sequence[Tensor],\n            objectnesses: Sequence[Tensor],\n            batch_gt_instances: Sequence[InstanceData],\n            batch_img_metas: Sequence[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (Sequence[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is\n                num_priors * num_classes.\n            bbox_preds (Sequence[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is\n                num_priors * 4.\n            objectnesses (Sequence[Tensor]): Score factor for\n                all scale level, each is a 4D-tensor, has shape\n                (batch_size, 1, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n        Returns:\n            dict[str, Tensor]: A dictionary of losses.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n\n        featmap_sizes = [cls_score.shape[2:] for cls_score in cls_scores]\n        mlvl_priors = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=cls_scores[0].dtype,\n            device=cls_scores[0].device,\n            with_stride=True)\n\n        flatten_cls_preds = [\n            cls_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1,\n                                                 self.cls_out_channels)\n            for cls_pred in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(num_imgs, -1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_objectness = [\n            objectness.permute(0, 2, 3, 1).reshape(num_imgs, -1)\n            for objectness in objectnesses\n        ]\n\n        flatten_cls_preds = torch.cat(flatten_cls_preds, dim=1)\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds, dim=1)\n        flatten_objectness = torch.cat(flatten_objectness, dim=1)\n        flatten_priors = torch.cat(mlvl_priors)\n        flatten_bboxes = self._bbox_decode(flatten_priors, flatten_bbox_preds)\n\n        (pos_masks, cls_targets, obj_targets, bbox_targets, l1_targets,\n         num_fg_imgs) = multi_apply(\n             self._get_targets_single,\n             flatten_priors.unsqueeze(0).repeat(num_imgs, 1, 1),\n             flatten_cls_preds.detach(), flatten_bboxes.detach(),\n             flatten_objectness.detach(), batch_gt_instances, batch_img_metas,\n             batch_gt_instances_ignore)\n\n        # The experimental results show that 'reduce_mean' can improve\n        # performance on the COCO dataset.\n        num_pos = torch.tensor(\n            sum(num_fg_imgs),\n            dtype=torch.float,\n            device=flatten_cls_preds.device)\n        num_total_samples = max(reduce_mean(num_pos), 1.0)\n\n        pos_masks = torch.cat(pos_masks, 0)\n        cls_targets = torch.cat(cls_targets, 0)\n        obj_targets = torch.cat(obj_targets, 0)\n        bbox_targets = torch.cat(bbox_targets, 0)\n        if self.use_l1:\n            l1_targets = torch.cat(l1_targets, 0)\n\n        loss_obj = self.loss_obj(flatten_objectness.view(-1, 1),\n                                 obj_targets) / num_total_samples\n        if num_pos > 0:\n            loss_cls = self.loss_cls(\n                flatten_cls_preds.view(-1, self.num_classes)[pos_masks],\n                cls_targets) / num_total_samples\n            loss_bbox = self.loss_bbox(\n                flatten_bboxes.view(-1, 4)[pos_masks],\n                bbox_targets) / num_total_samples\n        else:\n            # Avoid cls and reg branch not participating in the gradient\n            # propagation when there is no ground-truth in the images.\n            # For more details, please refer to\n            # https://github.com/open-mmlab/mmdetection/issues/7298\n            loss_cls = flatten_cls_preds.sum() * 0\n            loss_bbox = flatten_bboxes.sum() * 0\n\n        loss_dict = dict(\n            loss_cls=loss_cls, loss_bbox=loss_bbox, loss_obj=loss_obj)\n\n        if self.use_l1:\n            if num_pos > 0:\n                loss_l1 = self.loss_l1(\n                    flatten_bbox_preds.view(-1, 4)[pos_masks],\n                    l1_targets) / num_total_samples\n            else:\n                # Avoid cls and reg branch not participating in the gradient\n                # propagation when there is no ground-truth in the images.\n                # For more details, please refer to\n                # https://github.com/open-mmlab/mmdetection/issues/7298\n                loss_l1 = flatten_bbox_preds.sum() * 0\n            loss_dict.update(loss_l1=loss_l1)\n\n        return loss_dict\n\n    @torch.no_grad()\n    def _get_targets_single(\n            self,\n            priors: Tensor,\n            cls_preds: Tensor,\n            decoded_bboxes: Tensor,\n            objectness: Tensor,\n            gt_instances: InstanceData,\n            img_meta: dict,\n            gt_instances_ignore: Optional[InstanceData] = None) -> tuple:\n        \"\"\"Compute classification, regression, and objectness targets for\n        priors in a single image.\n\n        Args:\n            priors (Tensor): All priors of one image, a 2D-Tensor with shape\n                [num_priors, 4] in [cx, xy, stride_w, stride_y] format.\n            cls_preds (Tensor): Classification predictions of one image,\n                a 2D-Tensor with shape [num_priors, num_classes]\n            decoded_bboxes (Tensor): Decoded bboxes predictions of one image,\n                a 2D-Tensor with shape [num_priors, 4] in [tl_x, tl_y,\n                br_x, br_y] format.\n            objectness (Tensor): Objectness predictions of one image,\n                a 1D-Tensor with shape [num_priors]\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for current image.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n        Returns:\n            tuple:\n                foreground_mask (list[Tensor]): Binary mask of foreground\n                targets.\n                cls_target (list[Tensor]): Classification targets of an image.\n                obj_target (list[Tensor]): Objectness targets of an image.\n                bbox_target (list[Tensor]): BBox targets of an image.\n                l1_target (int): BBox L1 targets of an image.\n                num_pos_per_img (int): Number of positive samples in an image.\n        \"\"\"\n\n        num_priors = priors.size(0)\n        num_gts = len(gt_instances)\n        # No target\n        if num_gts == 0:\n            cls_target = cls_preds.new_zeros((0, self.num_classes))\n            bbox_target = cls_preds.new_zeros((0, 4))\n            l1_target = cls_preds.new_zeros((0, 4))\n            obj_target = cls_preds.new_zeros((num_priors, 1))\n            foreground_mask = cls_preds.new_zeros(num_priors).bool()\n            return (foreground_mask, cls_target, obj_target, bbox_target,\n                    l1_target, 0)\n\n        # YOLOX uses center priors with 0.5 offset to assign targets,\n        # but use center priors without offset to regress bboxes.\n        offset_priors = torch.cat(\n            [priors[:, :2] + priors[:, 2:] * 0.5, priors[:, 2:]], dim=-1)\n\n        scores = cls_preds.sigmoid() * objectness.unsqueeze(1).sigmoid()\n        pred_instances = InstanceData(\n            bboxes=decoded_bboxes, scores=scores.sqrt_(), priors=offset_priors)\n        assign_result = self.assigner.assign(\n            pred_instances=pred_instances,\n            gt_instances=gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n        pos_inds = sampling_result.pos_inds\n        num_pos_per_img = pos_inds.size(0)\n\n        pos_ious = assign_result.max_overlaps[pos_inds]\n        # IOU aware classification score\n        cls_target = F.one_hot(sampling_result.pos_gt_labels,\n                               self.num_classes) * pos_ious.unsqueeze(-1)\n        obj_target = torch.zeros_like(objectness).unsqueeze(-1)\n        obj_target[pos_inds] = 1\n        bbox_target = sampling_result.pos_gt_bboxes\n        l1_target = cls_preds.new_zeros((num_pos_per_img, 4))\n        if self.use_l1:\n            l1_target = self._get_l1_target(l1_target, bbox_target,\n                                            priors[pos_inds])\n        foreground_mask = torch.zeros_like(objectness).to(torch.bool)\n        foreground_mask[pos_inds] = 1\n        return (foreground_mask, cls_target, obj_target, bbox_target,\n                l1_target, num_pos_per_img)\n\n    def _get_l1_target(self,\n                       l1_target: Tensor,\n                       gt_bboxes: Tensor,\n                       priors: Tensor,\n                       eps: float = 1e-8) -> Tensor:\n        \"\"\"Convert gt bboxes to center offset and log width height.\"\"\"\n        gt_cxcywh = bbox_xyxy_to_cxcywh(gt_bboxes)\n        l1_target[:, :2] = (gt_cxcywh[:, :2] - priors[:, :2]) / priors[:, 2:]\n        l1_target[:, 2:] = torch.log(gt_cxcywh[:, 2:] / priors[:, 2:] + eps)\n        return l1_target\n"
  },
  {
    "path": "mmdet/models/detectors/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .atss import ATSS\nfrom .autoassign import AutoAssign\nfrom .base import BaseDetector\nfrom .base_detr import DetectionTransformer\nfrom .boxinst import BoxInst\nfrom .cascade_rcnn import CascadeRCNN\nfrom .centernet import CenterNet\nfrom .condinst import CondInst\nfrom .conditional_detr import ConditionalDETR\nfrom .cornernet import CornerNet\nfrom .crowddet import CrowdDet\nfrom .d2_wrapper import Detectron2Wrapper\nfrom .dab_detr import DABDETR\nfrom .ddod import DDOD\nfrom .ddq_detr import DDQDETR\nfrom .deformable_detr import DeformableDETR\nfrom .detr import DETR\nfrom .dino import DINO\nfrom .fast_rcnn import FastRCNN\nfrom .faster_rcnn import FasterRCNN\nfrom .fcos import FCOS\nfrom .fovea import FOVEA\nfrom .fsaf import FSAF\nfrom .gfl import GFL\nfrom .glip import GLIP\nfrom .grid_rcnn import GridRCNN\nfrom .grounding_dino import GroundingDINO\nfrom .htc import HybridTaskCascade\nfrom .kd_one_stage import KnowledgeDistillationSingleStageDetector\nfrom .lad import LAD\nfrom .mask2former import Mask2Former\nfrom .mask_rcnn import MaskRCNN\nfrom .mask_scoring_rcnn import MaskScoringRCNN\nfrom .maskformer import MaskFormer\nfrom .nasfcos import NASFCOS\nfrom .paa import PAA\nfrom .panoptic_fpn import PanopticFPN\nfrom .panoptic_two_stage_segmentor import TwoStagePanopticSegmentor\nfrom .point_rend import PointRend\nfrom .queryinst import QueryInst\nfrom .reppoints_detector import RepPointsDetector\nfrom .retinanet import RetinaNet\nfrom .rpn import RPN\nfrom .rtmdet import RTMDet\nfrom .scnet import SCNet\nfrom .semi_base import SemiBaseDetector\nfrom .single_stage import SingleStageDetector\nfrom .soft_teacher import SoftTeacher\nfrom .solo import SOLO\nfrom .solov2 import SOLOv2\nfrom .sparse_rcnn import SparseRCNN\nfrom .tood import TOOD\nfrom .trident_faster_rcnn import TridentFasterRCNN\nfrom .two_stage import TwoStageDetector\nfrom .vfnet import VFNet\nfrom .yolact import YOLACT\nfrom .yolo import YOLOV3\nfrom .yolof import YOLOF\nfrom .yolox import YOLOX\n\n__all__ = [\n    'ATSS', 'BaseDetector', 'SingleStageDetector', 'TwoStageDetector', 'RPN',\n    'KnowledgeDistillationSingleStageDetector', 'FastRCNN', 'FasterRCNN',\n    'MaskRCNN', 'CascadeRCNN', 'HybridTaskCascade', 'RetinaNet', 'FCOS',\n    'GridRCNN', 'MaskScoringRCNN', 'RepPointsDetector', 'FOVEA', 'FSAF',\n    'NASFCOS', 'PointRend', 'GFL', 'CornerNet', 'PAA', 'YOLOV3', 'YOLACT',\n    'VFNet', 'DETR', 'TridentFasterRCNN', 'SparseRCNN', 'SCNet', 'SOLO',\n    'SOLOv2', 'DeformableDETR', 'AutoAssign', 'YOLOF', 'CenterNet', 'YOLOX',\n    'TwoStagePanopticSegmentor', 'PanopticFPN', 'QueryInst', 'LAD', 'TOOD',\n    'MaskFormer', 'DDOD', 'Mask2Former', 'SemiBaseDetector', 'SoftTeacher',\n    'RTMDet', 'Detectron2Wrapper', 'CrowdDet', 'CondInst', 'BoxInst',\n    'DetectionTransformer', 'ConditionalDETR', 'DINO', 'DABDETR', 'GLIP',\n    'DDQDETR', 'GroundingDINO'\n]\n"
  },
  {
    "path": "mmdet/models/detectors/atss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass ATSS(SingleStageDetector):\n    \"\"\"Implementation of `ATSS <https://arxiv.org/abs/1912.02424>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of ATSS. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of ATSS. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/autoassign.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass AutoAssign(SingleStageDetector):\n    \"\"\"Implementation of `AutoAssign: Differentiable Label Assignment for Dense\n    Object Detection <https://arxiv.org/abs/2007.03496>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of AutoAssign. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of AutoAssign. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/base.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import Dict, List, Tuple, Union\n\nimport torch\nfrom mmengine.model import BaseModel\nfrom torch import Tensor\n\nfrom mmdet.structures import DetDataSample, OptSampleList, SampleList\nfrom mmdet.utils import InstanceList, OptConfigType, OptMultiConfig\nfrom ..utils import samplelist_boxtype2tensor\n\nForwardResults = Union[Dict[str, torch.Tensor], List[DetDataSample],\n                       Tuple[torch.Tensor], torch.Tensor]\n\n\nclass BaseDetector(BaseModel, metaclass=ABCMeta):\n    \"\"\"Base class for detectors.\n\n    Args:\n       data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`BaseDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n       init_cfg (dict or ConfigDict, optional): the config to control the\n           initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n\n    @property\n    def with_neck(self) -> bool:\n        \"\"\"bool: whether the detector has a neck\"\"\"\n        return hasattr(self, 'neck') and self.neck is not None\n\n    # TODO: these properties need to be carefully handled\n    # for both single stage & two stage detectors\n    @property\n    def with_shared_head(self) -> bool:\n        \"\"\"bool: whether the detector has a shared head in the RoI Head\"\"\"\n        return hasattr(self, 'roi_head') and self.roi_head.with_shared_head\n\n    @property\n    def with_bbox(self) -> bool:\n        \"\"\"bool: whether the detector has a bbox head\"\"\"\n        return ((hasattr(self, 'roi_head') and self.roi_head.with_bbox)\n                or (hasattr(self, 'bbox_head') and self.bbox_head is not None))\n\n    @property\n    def with_mask(self) -> bool:\n        \"\"\"bool: whether the detector has a mask head\"\"\"\n        return ((hasattr(self, 'roi_head') and self.roi_head.with_mask)\n                or (hasattr(self, 'mask_head') and self.mask_head is not None))\n\n    def forward(self,\n                inputs: torch.Tensor,\n                data_samples: OptSampleList = None,\n                mode: str = 'tensor') -> ForwardResults:\n        \"\"\"The unified entry for a forward process in both training and test.\n\n        The method should accept three modes: \"tensor\", \"predict\" and \"loss\":\n\n        - \"tensor\": Forward the whole network and return tensor or tuple of\n        tensor without any post-processing, same as a common nn.Module.\n        - \"predict\": Forward and return the predictions, which are fully\n        processed to a list of :obj:`DetDataSample`.\n        - \"loss\": Forward and return a dict of losses according to the given\n        inputs and data samples.\n\n        Note that this method doesn't handle either back propagation or\n        parameter update, which are supposed to be done in :meth:`train_step`.\n\n        Args:\n            inputs (torch.Tensor): The input tensor with shape\n                (N, C, ...) in general.\n            data_samples (list[:obj:`DetDataSample`], optional): A batch of\n                data samples that contain annotations and predictions.\n                Defaults to None.\n            mode (str): Return what kind of value. Defaults to 'tensor'.\n\n        Returns:\n            The return type depends on ``mode``.\n\n            - If ``mode=\"tensor\"``, return a tensor or a tuple of tensor.\n            - If ``mode=\"predict\"``, return a list of :obj:`DetDataSample`.\n            - If ``mode=\"loss\"``, return a dict of tensor.\n        \"\"\"\n        if mode == 'loss':\n            return self.loss(inputs, data_samples)\n        elif mode == 'predict':\n            return self.predict(inputs, data_samples)\n        elif mode == 'tensor':\n            return self._forward(inputs, data_samples)\n        else:\n            raise RuntimeError(f'Invalid mode \"{mode}\". '\n                               'Only supports loss, predict and tensor mode')\n\n    @abstractmethod\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, tuple]:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\"\"\"\n        pass\n\n    @abstractmethod\n    def predict(self, batch_inputs: Tensor,\n                batch_data_samples: SampleList) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\"\"\"\n        pass\n\n    @abstractmethod\n    def _forward(self,\n                 batch_inputs: Tensor,\n                 batch_data_samples: OptSampleList = None):\n        \"\"\"Network forward process.\n\n        Usually includes backbone, neck and head forward without any post-\n        processing.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def extract_feat(self, batch_inputs: Tensor):\n        \"\"\"Extract features from images.\"\"\"\n        pass\n\n    def add_pred_to_datasample(self, data_samples: SampleList,\n                               results_list: InstanceList) -> SampleList:\n        \"\"\"Add predictions to `DetDataSample`.\n\n        Args:\n            data_samples (list[:obj:`DetDataSample`], optional): A batch of\n                data samples that contain annotations and predictions.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances'. And the ``pred_instances`` usually\n            contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        for data_sample, pred_instances in zip(data_samples, results_list):\n            data_sample.pred_instances = pred_instances\n        samplelist_boxtype2tensor(data_samples)\n        return data_samples\n"
  },
  {
    "path": "mmdet/models/detectors/base_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import Dict, List, Tuple, Union\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList, SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .base import BaseDetector\n\n\n@MODELS.register_module()\nclass DetectionTransformer(BaseDetector, metaclass=ABCMeta):\n    r\"\"\"Base class for Detection Transformer.\n\n    In Detection Transformer, an encoder is used to process output features of\n    neck, then several queries interact with the encoder features using a\n    decoder and do the regression and classification with the bounding box\n    head.\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): Config of the backbone.\n        neck (:obj:`ConfigDict` or dict, optional): Config of the neck.\n            Defaults to None.\n        encoder (:obj:`ConfigDict` or dict, optional): Config of the\n            Transformer encoder. Defaults to None.\n        decoder (:obj:`ConfigDict` or dict, optional): Config of the\n            Transformer decoder. Defaults to None.\n        bbox_head (:obj:`ConfigDict` or dict, optional): Config for the\n            bounding box head module. Defaults to None.\n        positional_encoding (:obj:`ConfigDict` or dict, optional): Config\n            of the positional encoding module. Defaults to None.\n        num_queries (int, optional): Number of decoder query in Transformer.\n            Defaults to 100.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config of\n            the bounding box head module. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            the bounding box head module. Defaults to None.\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n            config of :class:`BaseDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 encoder: OptConfigType = None,\n                 decoder: OptConfigType = None,\n                 bbox_head: OptConfigType = None,\n                 positional_encoding: OptConfigType = None,\n                 num_queries: int = 100,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        # process args\n        bbox_head.update(train_cfg=train_cfg)\n        bbox_head.update(test_cfg=test_cfg)\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        self.encoder = encoder\n        self.decoder = decoder\n        self.positional_encoding = positional_encoding\n        self.num_queries = num_queries\n\n        # init model layers\n        self.backbone = MODELS.build(backbone)\n        if neck is not None:\n            self.neck = MODELS.build(neck)\n        self.bbox_head = MODELS.build(bbox_head)\n        self._init_layers()\n\n    @abstractmethod\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        pass\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, list]:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (bs, dim, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components\n        \"\"\"\n        img_feats = self.extract_feat(batch_inputs)\n        head_inputs_dict = self.forward_transformer(img_feats,\n                                                    batch_data_samples)\n        losses = self.bbox_head.loss(\n            **head_inputs_dict, batch_data_samples=batch_data_samples)\n\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs, has shape (bs, dim, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the input images.\n            Each DetDataSample usually contain 'pred_instances'. And the\n            `pred_instances` usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        img_feats = self.extract_feat(batch_inputs)\n        head_inputs_dict = self.forward_transformer(img_feats,\n                                                    batch_data_samples)\n        results_list = self.bbox_head.predict(\n            **head_inputs_dict,\n            rescale=rescale,\n            batch_data_samples=batch_data_samples)\n        batch_data_samples = self.add_pred_to_datasample(\n            batch_data_samples, results_list)\n        return batch_data_samples\n\n    def _forward(\n            self,\n            batch_inputs: Tensor,\n            batch_data_samples: OptSampleList = None) -> Tuple[List[Tensor]]:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n         Args:\n            batch_inputs (Tensor): Inputs, has shape (bs, dim, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`], optional): The\n                batch data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            tuple[Tensor]: A tuple of features from ``bbox_head`` forward.\n        \"\"\"\n        img_feats = self.extract_feat(batch_inputs)\n        head_inputs_dict = self.forward_transformer(img_feats,\n                                                    batch_data_samples)\n        results = self.bbox_head.forward(**head_inputs_dict)\n        return results\n\n    def forward_transformer(self,\n                            img_feats: Tuple[Tensor],\n                            batch_data_samples: OptSampleList = None) -> Dict:\n        \"\"\"Forward process of Transformer, which includes four steps:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'. We\n        summarized the parameters flow of the existing DETR-like detector,\n        which can be illustrated as follow:\n\n        .. code:: text\n\n                 img_feats & batch_data_samples\n                               |\n                               V\n                      +-----------------+\n                      | pre_transformer |\n                      +-----------------+\n                          |          |\n                          |          V\n                          |    +-----------------+\n                          |    | forward_encoder |\n                          |    +-----------------+\n                          |             |\n                          |             V\n                          |     +---------------+\n                          |     |  pre_decoder  |\n                          |     +---------------+\n                          |         |       |\n                          V         V       |\n                      +-----------------+   |\n                      | forward_decoder |   |\n                      +-----------------+   |\n                                |           |\n                                V           V\n                               head_inputs_dict\n\n        Args:\n            img_feats (tuple[Tensor]): Tuple of feature maps from neck. Each\n                    feature map has shape (bs, dim, H, W).\n            batch_data_samples (list[:obj:`DetDataSample`], optional): The\n                batch data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            dict: The dictionary of bbox_head function inputs, which always\n            includes the `hidden_states` of the decoder output and may contain\n            `references` including the initial and intermediate references.\n        \"\"\"\n        encoder_inputs_dict, decoder_inputs_dict = self.pre_transformer(\n            img_feats, batch_data_samples)\n\n        encoder_outputs_dict = self.forward_encoder(**encoder_inputs_dict)\n\n        tmp_dec_in, head_inputs_dict = self.pre_decoder(**encoder_outputs_dict)\n        decoder_inputs_dict.update(tmp_dec_in)\n\n        decoder_outputs_dict = self.forward_decoder(**decoder_inputs_dict)\n        head_inputs_dict.update(decoder_outputs_dict)\n        return head_inputs_dict\n\n    def extract_feat(self, batch_inputs: Tensor) -> Tuple[Tensor]:\n        \"\"\"Extract features.\n\n        Args:\n            batch_inputs (Tensor): Image tensor, has shape (bs, dim, H, W).\n\n        Returns:\n            tuple[Tensor]: Tuple of feature maps from neck. Each feature map\n            has shape (bs, dim, H, W).\n        \"\"\"\n        x = self.backbone(batch_inputs)\n        if self.with_neck:\n            x = self.neck(x)\n        return x\n\n    @abstractmethod\n    def pre_transformer(\n            self,\n            img_feats: Tuple[Tensor],\n            batch_data_samples: OptSampleList = None) -> Tuple[Dict, Dict]:\n        \"\"\"Process image features before feeding them to the transformer.\n\n        Args:\n            img_feats (tuple[Tensor]): Tuple of feature maps from neck. Each\n                feature map has shape (bs, dim, H, W).\n            batch_data_samples (list[:obj:`DetDataSample`], optional): The\n                batch data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            tuple[dict, dict]: The first dict contains the inputs of encoder\n            and the second dict contains the inputs of decoder.\n\n            - encoder_inputs_dict (dict): The keyword args dictionary of\n              `self.forward_encoder()`, which includes 'feat', 'feat_mask',\n              'feat_pos', and other algorithm-specific arguments.\n            - decoder_inputs_dict (dict): The keyword args dictionary of\n              `self.forward_decoder()`, which includes 'memory_mask', and\n              other algorithm-specific arguments.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def forward_encoder(self, feat: Tensor, feat_mask: Tensor,\n                        feat_pos: Tensor, **kwargs) -> Dict:\n        \"\"\"Forward with Transformer encoder.\n\n        Args:\n            feat (Tensor): Sequential features, has shape (bs, num_feat_points,\n                dim).\n            feat_mask (Tensor): ByteTensor, the padding mask of the features,\n                has shape (bs, num_feat_points).\n            feat_pos (Tensor): The positional embeddings of the features, has\n                shape (bs, num_feat_points, dim).\n\n        Returns:\n            dict: The dictionary of encoder outputs, which includes the\n            `memory` of the encoder output and other algorithm-specific\n            arguments.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def pre_decoder(self, memory: Tensor, **kwargs) -> Tuple[Dict, Dict]:\n        \"\"\"Prepare intermediate variables before entering Transformer decoder,\n        such as `query`, `query_pos`, and `reference_points`.\n\n        Args:\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n\n        Returns:\n            tuple[dict, dict]: The first dict contains the inputs of decoder\n            and the second dict contains the inputs of the bbox_head function.\n\n            - decoder_inputs_dict (dict): The keyword dictionary args of\n              `self.forward_decoder()`, which includes 'query', 'query_pos',\n              'memory', and other algorithm-specific arguments.\n            - head_inputs_dict (dict): The keyword dictionary args of the\n              bbox_head functions, which is usually empty, or includes\n              `enc_outputs_class` and `enc_outputs_class` when the detector\n              support 'two stage' or 'query selection' strategies.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def forward_decoder(self, query: Tensor, query_pos: Tensor, memory: Tensor,\n                        **kwargs) -> Dict:\n        \"\"\"Forward with Transformer decoder.\n\n        Args:\n            query (Tensor): The queries of decoder inputs, has shape\n                (bs, num_queries, dim).\n            query_pos (Tensor): The positional queries of decoder inputs,\n                has shape (bs, num_queries, dim).\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n\n        Returns:\n            dict: The dictionary of decoder outputs, which includes the\n            `hidden_states` of the decoder output, `references` including\n            the initial and intermediate reference_points, and other\n            algorithm-specific arguments.\n        \"\"\"\n        pass\n"
  },
  {
    "path": "mmdet/models/detectors/boxinst.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage_instance_seg import SingleStageInstanceSegmentor\n\n\n@MODELS.register_module()\nclass BoxInst(SingleStageInstanceSegmentor):\n    \"\"\"Implementation of `BoxInst <https://arxiv.org/abs/2012.02310>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 mask_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            mask_head=mask_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/cascade_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass CascadeRCNN(TwoStageDetector):\n    r\"\"\"Implementation of `Cascade R-CNN: Delving into High Quality Object\n    Detection <https://arxiv.org/abs/1906.09756>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 rpn_head: OptConfigType = None,\n                 roi_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/centernet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass CenterNet(SingleStageDetector):\n    \"\"\"Implementation of CenterNet(Objects as Points)\n\n    <https://arxiv.org/abs/1904.07850>.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/condinst.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage_instance_seg import SingleStageInstanceSegmentor\n\n\n@MODELS.register_module()\nclass CondInst(SingleStageInstanceSegmentor):\n    \"\"\"Implementation of `CondInst <https://arxiv.org/abs/2003.05664>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 mask_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            mask_head=mask_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/conditional_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict\n\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom ..layers import (ConditionalDetrTransformerDecoder,\n                      DetrTransformerEncoder, SinePositionalEncoding)\nfrom .detr import DETR\n\n\n@MODELS.register_module()\nclass ConditionalDETR(DETR):\n    r\"\"\"Implementation of `Conditional DETR for Fast Training Convergence.\n\n    <https://arxiv.org/abs/2108.06152>`_.\n\n    Code is modified from the `official github repo\n    <https://github.com/Atten4Vis/ConditionalDETR>`_.\n    \"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        self.positional_encoding = SinePositionalEncoding(\n            **self.positional_encoding)\n        self.encoder = DetrTransformerEncoder(**self.encoder)\n        self.decoder = ConditionalDetrTransformerDecoder(**self.decoder)\n        self.embed_dims = self.encoder.embed_dims\n        # NOTE The embed_dims is typically passed from the inside out.\n        # For example in DETR, The embed_dims is passed as\n        # self_attn -> the first encoder layer -> encoder -> detector.\n        self.query_embedding = nn.Embedding(self.num_queries, self.embed_dims)\n\n        num_feats = self.positional_encoding.num_feats\n        assert num_feats * 2 == self.embed_dims, \\\n            f'embed_dims should be exactly 2 times of num_feats. ' \\\n            f'Found {self.embed_dims} and {num_feats}.'\n\n    def forward_decoder(self, query: Tensor, query_pos: Tensor, memory: Tensor,\n                        memory_mask: Tensor, memory_pos: Tensor) -> Dict:\n        \"\"\"Forward with Transformer decoder.\n\n        Args:\n            query (Tensor): The queries of decoder inputs, has shape\n                (bs, num_queries, dim).\n            query_pos (Tensor): The positional queries of decoder inputs,\n                has shape (bs, num_queries, dim).\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points).\n            memory_pos (Tensor): The positional embeddings of memory, has\n                shape (bs, num_feat_points, dim).\n\n        Returns:\n            dict: The dictionary of decoder outputs, which includes the\n            `hidden_states` and `references` of the decoder output.\n\n            - hidden_states (Tensor): Has shape\n                (num_decoder_layers, bs, num_queries, dim)\n            - references (Tensor): Has shape\n                (bs, num_queries, 2)\n        \"\"\"\n\n        hidden_states, references = self.decoder(\n            query=query,\n            key=memory,\n            query_pos=query_pos,\n            key_pos=memory_pos,\n            key_padding_mask=memory_mask)\n        head_inputs_dict = dict(\n            hidden_states=hidden_states, references=references)\n        return head_inputs_dict\n"
  },
  {
    "path": "mmdet/models/detectors/cornernet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass CornerNet(SingleStageDetector):\n    \"\"\"CornerNet.\n\n    This detector is the implementation of the paper `CornerNet: Detecting\n    Objects as Paired Keypoints <https://arxiv.org/abs/1808.01244>`_ .\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/crowddet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass CrowdDet(TwoStageDetector):\n    \"\"\"Implementation of `CrowdDet <https://arxiv.org/abs/2003.09163>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        rpn_head (:obj:`ConfigDict` or dict): The rpn config.\n        roi_head (:obj:`ConfigDict` or dict): The roi config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of FCOS. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of FCOS. Defaults to None.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 rpn_head: ConfigType,\n                 roi_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            init_cfg=init_cfg,\n            data_preprocessor=data_preprocessor)\n"
  },
  {
    "path": "mmdet/models/detectors/d2_wrapper.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import BaseBoxes\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\nfrom mmdet.utils import ConfigType\nfrom .base import BaseDetector\n\ntry:\n    import detectron2\n    from detectron2.config import get_cfg\n    from detectron2.modeling import build_model\n    from detectron2.structures.masks import BitMasks as D2_BitMasks\n    from detectron2.structures.masks import PolygonMasks as D2_PolygonMasks\n    from detectron2.utils.events import EventStorage\nexcept ImportError:\n    detectron2 = None\n\n\ndef _to_cfgnode_list(cfg: ConfigType,\n                     config_list: list = [],\n                     father_name: str = 'MODEL') -> tuple:\n    \"\"\"Convert the key and value of mmengine.ConfigDict into a list.\n\n    Args:\n        cfg (ConfigDict): The detectron2 model config.\n        config_list (list): A list contains the key and value of ConfigDict.\n            Defaults to [].\n        father_name (str): The father name add before the key.\n            Defaults to \"MODEL\".\n\n    Returns:\n        tuple:\n\n        - config_list: A list contains the key and value of ConfigDict.\n        - father_name (str): The father name add before the key.\n          Defaults to \"MODEL\".\n    \"\"\"\n    for key, value in cfg.items():\n        name = f'{father_name}.{key.upper()}'\n        if isinstance(value, ConfigDict) or isinstance(value, dict):\n            config_list, fater_name = \\\n                _to_cfgnode_list(value, config_list, name)\n        else:\n            config_list.append(name)\n            config_list.append(value)\n\n    return config_list, father_name\n\n\ndef convert_d2_pred_to_datasample(data_samples: SampleList,\n                                  d2_results_list: list) -> SampleList:\n    \"\"\"Convert the Detectron2's result to DetDataSample.\n\n    Args:\n        data_samples (list[:obj:`DetDataSample`]): The batch\n            data samples. It usually includes information such\n            as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n        d2_results_list (list): The list of the results of Detectron2's model.\n\n    Returns:\n        list[:obj:`DetDataSample`]: Detection results of the\n        input images. Each DetDataSample usually contain\n        'pred_instances'. And the ``pred_instances`` usually\n        contains following keys.\n\n        - scores (Tensor): Classification scores, has a shape\n          (num_instance, )\n        - labels (Tensor): Labels of bboxes, has a shape\n          (num_instances, ).\n        - bboxes (Tensor): Has a shape (num_instances, 4),\n          the last dimension 4 arrange as (x1, y1, x2, y2).\n    \"\"\"\n    assert len(data_samples) == len(d2_results_list)\n    for data_sample, d2_results in zip(data_samples, d2_results_list):\n        d2_instance = d2_results['instances']\n\n        results = InstanceData()\n        results.bboxes = d2_instance.pred_boxes.tensor\n        results.scores = d2_instance.scores\n        results.labels = d2_instance.pred_classes\n\n        if d2_instance.has('pred_masks'):\n            results.masks = d2_instance.pred_masks\n        data_sample.pred_instances = results\n\n    return data_samples\n\n\n@MODELS.register_module()\nclass Detectron2Wrapper(BaseDetector):\n    \"\"\"Wrapper of a Detectron2 model. Input/output formats of this class follow\n    MMDetection's convention, so a Detectron2 model can be trained and\n    evaluated in MMDetection.\n\n    Args:\n        detector (:obj:`ConfigDict` or dict): The module config of\n            Detectron2.\n        bgr_to_rgb (bool): whether to convert image from BGR to RGB.\n            Defaults to False.\n        rgb_to_bgr (bool): whether to convert image from RGB to BGR.\n            Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 detector: ConfigType,\n                 bgr_to_rgb: bool = False,\n                 rgb_to_bgr: bool = False) -> None:\n        if detectron2 is None:\n            raise ImportError('Please install Detectron2 first')\n        assert not (bgr_to_rgb and rgb_to_bgr), (\n            '`bgr2rgb` and `rgb2bgr` cannot be set to True at the same time')\n        super().__init__()\n        self._channel_conversion = rgb_to_bgr or bgr_to_rgb\n        cfgnode_list, _ = _to_cfgnode_list(detector)\n        self.cfg = get_cfg()\n        self.cfg.merge_from_list(cfgnode_list)\n        self.d2_model = build_model(self.cfg)\n        self.storage = EventStorage()\n\n    def init_weights(self) -> None:\n        \"\"\"Initialization Backbone.\n\n        NOTE: The initialization of other layers are in Detectron2,\n        if users want to change the initialization way, please\n        change the code in Detectron2.\n        \"\"\"\n        from detectron2.checkpoint import DetectionCheckpointer\n        checkpointer = DetectionCheckpointer(model=self.d2_model)\n        checkpointer.load(self.cfg.MODEL.WEIGHTS, checkpointables=[])\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, tuple]:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        The inputs will first convert to the Detectron2 type and feed into\n        D2 models.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        d2_batched_inputs = self._convert_to_d2_inputs(\n            batch_inputs=batch_inputs,\n            batch_data_samples=batch_data_samples,\n            training=True)\n\n        with self.storage as storage:  # noqa\n            losses = self.d2_model(d2_batched_inputs)\n        # storage contains some training information, such as cls_accuracy.\n        # you can use storage.latest() to get the detail information\n        return losses\n\n    def predict(self, batch_inputs: Tensor,\n                batch_data_samples: SampleList) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        The inputs will first convert to the Detectron2 type and feed into\n        D2 models. And the results will convert back to the MMDet type.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances'. And the ``pred_instances`` usually\n            contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        d2_batched_inputs = self._convert_to_d2_inputs(\n            batch_inputs=batch_inputs,\n            batch_data_samples=batch_data_samples,\n            training=False)\n        # results in detectron2 has already rescale\n        d2_results_list = self.d2_model(d2_batched_inputs)\n        batch_data_samples = convert_d2_pred_to_datasample(\n            data_samples=batch_data_samples, d2_results_list=d2_results_list)\n\n        return batch_data_samples\n\n    def _forward(self, *args, **kwargs):\n        \"\"\"Network forward process.\n\n        Usually includes backbone, neck and head forward without any post-\n        processing.\n        \"\"\"\n        raise NotImplementedError(\n            f'`_forward` is not implemented in {self.__class__.__name__}')\n\n    def extract_feat(self, *args, **kwargs):\n        \"\"\"Extract features from images.\n\n        `extract_feat` will not be used in obj:``Detectron2Wrapper``.\n        \"\"\"\n        pass\n\n    def _convert_to_d2_inputs(self,\n                              batch_inputs: Tensor,\n                              batch_data_samples: SampleList,\n                              training=True) -> list:\n        \"\"\"Convert inputs type to support Detectron2's model.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n            training (bool): Whether to enable training time processing.\n\n        Returns:\n            list[dict]: A list of dict, which will be fed into Detectron2's\n            model. And the dict usually contains following keys.\n\n            - image (Tensor): Image in (C, H, W) format.\n            - instances (Instances): GT Instance.\n            - height (int): the output height resolution of the model\n            - width (int): the output width resolution of the model\n        \"\"\"\n        from detectron2.data.detection_utils import filter_empty_instances\n        from detectron2.structures import Boxes, Instances\n\n        batched_d2_inputs = []\n        for image, data_samples in zip(batch_inputs, batch_data_samples):\n            d2_inputs = dict()\n            # deal with metainfo\n            meta_info = data_samples.metainfo\n            d2_inputs['file_name'] = meta_info['img_path']\n            d2_inputs['height'], d2_inputs['width'] = meta_info['ori_shape']\n            d2_inputs['image_id'] = meta_info['img_id']\n            # deal with image\n            if self._channel_conversion:\n                image = image[[2, 1, 0], ...]\n            d2_inputs['image'] = image\n            # deal with gt_instances\n            gt_instances = data_samples.gt_instances\n            d2_instances = Instances(meta_info['img_shape'])\n\n            gt_boxes = gt_instances.bboxes\n            # TODO: use mmdet.structures.box.get_box_tensor after PR 8658\n            #  has merged\n            if isinstance(gt_boxes, BaseBoxes):\n                gt_boxes = gt_boxes.tensor\n            d2_instances.gt_boxes = Boxes(gt_boxes)\n\n            d2_instances.gt_classes = gt_instances.labels\n            if gt_instances.get('masks', None) is not None:\n                gt_masks = gt_instances.masks\n                if isinstance(gt_masks, PolygonMasks):\n                    d2_instances.gt_masks = D2_PolygonMasks(gt_masks.masks)\n                elif isinstance(gt_masks, BitmapMasks):\n                    d2_instances.gt_masks = D2_BitMasks(gt_masks.masks)\n                else:\n                    raise TypeError('The type of `gt_mask` can be '\n                                    '`PolygonMasks` or `BitMasks`, but get '\n                                    f'{type(gt_masks)}.')\n            # convert to cpu and convert back to cuda to avoid\n            # some potential error\n            if training:\n                device = gt_boxes.device\n                d2_instances = filter_empty_instances(\n                    d2_instances.to('cpu')).to(device)\n                d2_inputs['instances'] = d2_instances\n            batched_d2_inputs.append(d2_inputs)\n\n        return batched_d2_inputs\n"
  },
  {
    "path": "mmdet/models/detectors/dab_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Tuple\n\nfrom mmengine.model import uniform_init\nfrom torch import Tensor, nn\n\nfrom mmdet.registry import MODELS\nfrom ..layers import SinePositionalEncoding\nfrom ..layers.transformer import (DABDetrTransformerDecoder,\n                                  DABDetrTransformerEncoder, inverse_sigmoid)\nfrom .detr import DETR\n\n\n@MODELS.register_module()\nclass DABDETR(DETR):\n    r\"\"\"Implementation of `DAB-DETR:\n    Dynamic Anchor Boxes are Better Queries for DETR.\n\n    <https://arxiv.org/abs/2201.12329>`_.\n\n    Code is modified from the `official github repo\n    <https://github.com/IDEA-Research/DAB-DETR>`_.\n\n    Args:\n        with_random_refpoints (bool): Whether to randomly initialize query\n            embeddings and not update them during training.\n            Defaults to False.\n        num_patterns (int): Inspired by Anchor-DETR. Defaults to 0.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 with_random_refpoints: bool = False,\n                 num_patterns: int = 0,\n                 **kwargs) -> None:\n        self.with_random_refpoints = with_random_refpoints\n        assert isinstance(num_patterns, int), \\\n            f'num_patterns should be int but {num_patterns}.'\n        self.num_patterns = num_patterns\n\n        super().__init__(*args, **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        self.positional_encoding = SinePositionalEncoding(\n            **self.positional_encoding)\n        self.encoder = DABDetrTransformerEncoder(**self.encoder)\n        self.decoder = DABDetrTransformerDecoder(**self.decoder)\n        self.embed_dims = self.encoder.embed_dims\n        self.query_dim = self.decoder.query_dim\n        self.query_embedding = nn.Embedding(self.num_queries, self.query_dim)\n        if self.num_patterns > 0:\n            self.patterns = nn.Embedding(self.num_patterns, self.embed_dims)\n\n        num_feats = self.positional_encoding.num_feats\n        assert num_feats * 2 == self.embed_dims, \\\n            f'embed_dims should be exactly 2 times of num_feats. ' \\\n            f'Found {self.embed_dims} and {num_feats}.'\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights for Transformer and other components.\"\"\"\n        super(DABDETR, self).init_weights()\n        if self.with_random_refpoints:\n            uniform_init(self.query_embedding)\n            self.query_embedding.weight.data[:, :2] = \\\n                inverse_sigmoid(self.query_embedding.weight.data[:, :2])\n            self.query_embedding.weight.data[:, :2].requires_grad = False\n\n    def pre_decoder(self, memory: Tensor) -> Tuple[Dict, Dict]:\n        \"\"\"Prepare intermediate variables before entering Transformer decoder,\n        such as `query`, `query_pos`.\n\n        Args:\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n\n        Returns:\n            tuple[dict, dict]: The first dict contains the inputs of decoder\n            and the second dict contains the inputs of the bbox_head function.\n\n            - decoder_inputs_dict (dict): The keyword args dictionary of\n                `self.forward_decoder()`, which includes 'query', 'query_pos',\n                'memory' and 'reg_branches'.\n            - head_inputs_dict (dict): The keyword args dictionary of the\n                bbox_head functions, which is usually empty, or includes\n                `enc_outputs_class` and `enc_outputs_class` when the detector\n                support 'two stage' or 'query selection' strategies.\n        \"\"\"\n        batch_size = memory.size(0)\n        query_pos = self.query_embedding.weight\n        query_pos = query_pos.unsqueeze(0).repeat(batch_size, 1, 1)\n        if self.num_patterns == 0:\n            query = query_pos.new_zeros(batch_size, self.num_queries,\n                                        self.embed_dims)\n        else:\n            query = self.patterns.weight[:, None, None, :]\\\n                .repeat(1, self.num_queries, batch_size, 1)\\\n                .view(-1, batch_size, self.embed_dims)\\\n                .permute(1, 0, 2)\n            query_pos = query_pos.repeat(1, self.num_patterns, 1)\n\n        decoder_inputs_dict = dict(\n            query_pos=query_pos, query=query, memory=memory)\n        head_inputs_dict = dict()\n        return decoder_inputs_dict, head_inputs_dict\n\n    def forward_decoder(self, query: Tensor, query_pos: Tensor, memory: Tensor,\n                        memory_mask: Tensor, memory_pos: Tensor) -> Dict:\n        \"\"\"Forward with Transformer decoder.\n\n        Args:\n            query (Tensor): The queries of decoder inputs, has shape\n                (bs, num_queries, dim).\n            query_pos (Tensor): The positional queries of decoder inputs,\n                has shape (bs, num_queries, dim).\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points).\n            memory_pos (Tensor): The positional embeddings of memory, has\n                shape (bs, num_feat_points, dim).\n\n        Returns:\n            dict: The dictionary of decoder outputs, which includes the\n            `hidden_states` and `references` of the decoder output.\n        \"\"\"\n\n        hidden_states, references = self.decoder(\n            query=query,\n            key=memory,\n            query_pos=query_pos,\n            key_pos=memory_pos,\n            key_padding_mask=memory_mask,\n            reg_branches=self.bbox_head.\n            fc_reg  # iterative refinement for anchor boxes\n        )\n        head_inputs_dict = dict(\n            hidden_states=hidden_states, references=references)\n        return head_inputs_dict\n"
  },
  {
    "path": "mmdet/models/detectors/ddod.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass DDOD(SingleStageDetector):\n    \"\"\"Implementation of `DDOD <https://arxiv.org/pdf/2107.02963.pdf>`_.\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of ATSS. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of ATSS. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/ddq_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Tuple\n\n# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom mmcv.ops import MultiScaleDeformableAttention, batched_nms\nfrom torch import Tensor, nn\nfrom torch.nn.init import normal_\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList\nfrom mmdet.structures.bbox import bbox_cxcywh_to_xyxy\nfrom mmdet.utils import OptConfigType\nfrom ..layers import DDQTransformerDecoder\nfrom ..utils import align_tensor\nfrom .deformable_detr import DeformableDETR\nfrom .dino import DINO\n\n\n@MODELS.register_module()\nclass DDQDETR(DINO):\n    r\"\"\"Implementation of `Dense Distinct Query for\n    End-to-End Object Detection <https://arxiv.org/abs/2303.12776>`_\n\n    Code is modified from the `official github repo\n    <https://github.com/jshilong/DDQ>`_.\n\n    Args:\n        dense_topk_ratio (float): Ratio of num_dense queries to num_queries.\n            Defaults to 1.5.\n        dqs_cfg (:obj:`ConfigDict` or dict, optional): Config of\n            Distinct Queries Selection. Defaults to nms with\n            `iou_threshold` = 0.8.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 dense_topk_ratio: float = 1.5,\n                 dqs_cfg: OptConfigType = dict(type='nms', iou_threshold=0.8),\n                 **kwargs):\n        self.dense_topk_ratio = dense_topk_ratio\n        self.decoder_cfg = kwargs['decoder']\n        self.dqs_cfg = dqs_cfg\n        super().__init__(*args, **kwargs)\n\n        # a share dict in all moduls\n        # pass some intermediate results and config parameters\n        cache_dict = dict()\n        for m in self.modules():\n            m.cache_dict = cache_dict\n        # first element is the start index of matching queries\n        # second element is the number of matching queries\n        self.cache_dict['dis_query_info'] = [0, 0]\n\n        # mask for distinct queries in each decoder layer\n        self.cache_dict['distinct_query_mask'] = []\n        # pass to decoder do the dqs\n        self.cache_dict['cls_branches'] = self.bbox_head.cls_branches\n        # Used to construct the attention mask after dqs\n        self.cache_dict['num_heads'] = self.encoder.layers[\n            0].self_attn.num_heads\n        # pass to decoder to do the dqs\n        self.cache_dict['dqs_cfg'] = self.dqs_cfg\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        super(DDQDETR, self)._init_layers()\n        self.decoder = DDQTransformerDecoder(**self.decoder_cfg)\n        self.query_embedding = None\n        self.query_map = nn.Linear(self.embed_dims, self.embed_dims)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights for Transformer and other components.\"\"\"\n        super(DeformableDETR, self).init_weights()\n        for coder in self.encoder, self.decoder:\n            for p in coder.parameters():\n                if p.dim() > 1:\n                    nn.init.xavier_uniform_(p)\n        for m in self.modules():\n            if isinstance(m, MultiScaleDeformableAttention):\n                m.init_weights()\n        nn.init.xavier_uniform_(self.memory_trans_fc.weight)\n        normal_(self.level_embed)\n\n    def pre_decoder(\n        self,\n        memory: Tensor,\n        memory_mask: Tensor,\n        spatial_shapes: Tensor,\n        batch_data_samples: OptSampleList = None,\n    ) -> Tuple[Dict]:\n        \"\"\"Prepare intermediate variables before entering Transformer decoder,\n        such as `query`, `memory`, and `reference_points`.\n\n        Args:\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points). Will only be used when\n                `as_two_stage` is `True`.\n            spatial_shapes (Tensor): Spatial shapes of features in all levels.\n                With shape (num_levels, 2), last dimension represents (h, w).\n                Will only be used when `as_two_stage` is `True`.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            tuple[dict]: The decoder_inputs_dict and head_inputs_dict.\n\n            - decoder_inputs_dict (dict): The keyword dictionary args of\n              `self.forward_decoder()`, which includes 'query', 'memory',\n              `reference_points`, and `dn_mask`. The reference points of\n              decoder input here are 4D boxes, although it has `points`\n              in its name.\n            - head_inputs_dict (dict): The keyword dictionary args of the\n              bbox_head functions, which includes `topk_score`, `topk_coords`,\n              `dense_topk_score`, `dense_topk_coords`,\n              and `dn_meta`, when `self.training` is `True`, else is empty.\n        \"\"\"\n        bs, _, c = memory.shape\n        output_memory, output_proposals = self.gen_encoder_output_proposals(\n            memory, memory_mask, spatial_shapes)\n        enc_outputs_class = self.bbox_head.cls_branches[\n            self.decoder.num_layers](\n                output_memory)\n        enc_outputs_coord_unact = self.bbox_head.reg_branches[\n            self.decoder.num_layers](output_memory) + output_proposals\n\n        if self.training:\n            # aux dense branch particularly in DDQ DETR, which doesn't exist\n            #   in DINO.\n            # -1 is the aux head for the encoder\n            dense_enc_outputs_class = self.bbox_head.cls_branches[-1](\n                output_memory)\n            dense_enc_outputs_coord_unact = self.bbox_head.reg_branches[-1](\n                output_memory) + output_proposals\n\n        topk = self.num_queries\n        dense_topk = int(topk * self.dense_topk_ratio)\n\n        proposals = enc_outputs_coord_unact.sigmoid()\n        proposals = bbox_cxcywh_to_xyxy(proposals)\n        scores = enc_outputs_class.max(-1)[0].sigmoid()\n\n        if self.training:\n            # aux dense branch particularly in DDQ DETR, which doesn't exist\n            #   in DINO.\n            dense_proposals = dense_enc_outputs_coord_unact.sigmoid()\n            dense_proposals = bbox_cxcywh_to_xyxy(dense_proposals)\n            dense_scores = dense_enc_outputs_class.max(-1)[0].sigmoid()\n\n        num_imgs = len(scores)\n        topk_score = []\n        topk_coords_unact = []\n        # Distinct query.\n        query = []\n\n        dense_topk_score = []\n        dense_topk_coords_unact = []\n        dense_query = []\n\n        for img_id in range(num_imgs):\n            single_proposals = proposals[img_id]\n            single_scores = scores[img_id]\n\n            # `batched_nms` of class scores and bbox coordinations is used\n            #   particularly by DDQ DETR for region proposal generation,\n            #   instead of `topk` of class scores by DINO.\n            _, keep_idxs = batched_nms(\n                single_proposals, single_scores,\n                torch.ones(len(single_scores), device=single_scores.device),\n                self.cache_dict['dqs_cfg'])\n\n            if self.training:\n                # aux dense branch particularly in DDQ DETR, which doesn't\n                #   exist in DINO.\n                dense_single_proposals = dense_proposals[img_id]\n                dense_single_scores = dense_scores[img_id]\n                # sort according the score\n                # Only sort by classification score, neither nms nor topk is\n                #   required. So input parameter `nms_cfg` = None.\n                _, dense_keep_idxs = batched_nms(\n                    dense_single_proposals, dense_single_scores,\n                    torch.ones(\n                        len(dense_single_scores),\n                        device=dense_single_scores.device), None)\n\n                dense_topk_score.append(dense_enc_outputs_class[img_id]\n                                        [dense_keep_idxs][:dense_topk])\n                dense_topk_coords_unact.append(\n                    dense_enc_outputs_coord_unact[img_id][dense_keep_idxs]\n                    [:dense_topk])\n\n            topk_score.append(enc_outputs_class[img_id][keep_idxs][:topk])\n\n            # Instead of initializing the content part with transformed\n            #   coordinates in Deformable DETR, we fuse the feature map\n            #   embedding of distinct positions as the content part, which\n            #   makes the initial queries more distinct.\n            topk_coords_unact.append(\n                enc_outputs_coord_unact[img_id][keep_idxs][:topk])\n\n            map_memory = self.query_map(memory[img_id].detach())\n            query.append(map_memory[keep_idxs][:topk])\n            if self.training:\n                # aux dense branch particularly in DDQ DETR, which doesn't\n                # exist in DINO.\n                dense_query.append(map_memory[dense_keep_idxs][:dense_topk])\n\n        topk_score = align_tensor(topk_score, topk)\n        topk_coords_unact = align_tensor(topk_coords_unact, topk)\n        query = align_tensor(query, topk)\n        if self.training:\n            dense_topk_score = align_tensor(dense_topk_score)\n            dense_topk_coords_unact = align_tensor(dense_topk_coords_unact)\n\n            dense_query = align_tensor(dense_query)\n            num_dense_queries = dense_query.size(1)\n        if self.training:\n            query = torch.cat([query, dense_query], dim=1)\n            topk_coords_unact = torch.cat(\n                [topk_coords_unact, dense_topk_coords_unact], dim=1)\n\n        topk_coords = topk_coords_unact.sigmoid()\n        if self.training:\n            dense_topk_coords = topk_coords[:, -num_dense_queries:]\n            topk_coords = topk_coords[:, :-num_dense_queries]\n\n        topk_coords_unact = topk_coords_unact.detach()\n\n        if self.training:\n            dn_label_query, dn_bbox_query, dn_mask, dn_meta = \\\n                self.dn_query_generator(batch_data_samples)\n            query = torch.cat([dn_label_query, query], dim=1)\n            reference_points = torch.cat([dn_bbox_query, topk_coords_unact],\n                                         dim=1)\n\n            # Update `dn_mask` to add mask for dense queries.\n            ori_size = dn_mask.size(-1)\n            new_size = dn_mask.size(-1) + num_dense_queries\n            new_dn_mask = dn_mask.new_ones((new_size, new_size)).bool()\n            dense_mask = torch.zeros(num_dense_queries,\n                                     num_dense_queries).bool()\n            self.cache_dict['dis_query_info'] = [dn_label_query.size(1), topk]\n\n            new_dn_mask[ori_size:, ori_size:] = dense_mask\n            new_dn_mask[:ori_size, :ori_size] = dn_mask\n            dn_meta['num_dense_queries'] = num_dense_queries\n            dn_mask = new_dn_mask\n            self.cache_dict['num_dense_queries'] = num_dense_queries\n            self.decoder.aux_reg_branches = self.bbox_head.aux_reg_branches\n\n        else:\n            self.cache_dict['dis_query_info'] = [0, topk]\n            reference_points = topk_coords_unact\n            dn_mask, dn_meta = None, None\n\n        reference_points = reference_points.sigmoid()\n\n        decoder_inputs_dict = dict(\n            query=query,\n            memory=memory,\n            reference_points=reference_points,\n            dn_mask=dn_mask)\n        head_inputs_dict = dict(\n            enc_outputs_class=topk_score,\n            enc_outputs_coord=topk_coords,\n            aux_enc_outputs_class=dense_topk_score,\n            aux_enc_outputs_coord=dense_topk_coords,\n            dn_meta=dn_meta) if self.training else dict()\n\n        return decoder_inputs_dict, head_inputs_dict\n"
  },
  {
    "path": "mmdet/models/detectors/deformable_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Dict, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention\nfrom mmengine.model import xavier_init\nfrom torch import Tensor, nn\nfrom torch.nn.init import normal_\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList\nfrom mmdet.utils import OptConfigType\nfrom ..layers import (DeformableDetrTransformerDecoder,\n                      DeformableDetrTransformerEncoder, SinePositionalEncoding)\nfrom .base_detr import DetectionTransformer\n\n\n@MODELS.register_module()\nclass DeformableDETR(DetectionTransformer):\n    r\"\"\"Implementation of `Deformable DETR: Deformable Transformers for\n    End-to-End Object Detection <https://arxiv.org/abs/2010.04159>`_\n\n    Code is modified from the `official github repo\n    <https://github.com/fundamentalvision/Deformable-DETR>`_.\n\n    Args:\n        decoder (:obj:`ConfigDict` or dict, optional): Config of the\n            Transformer decoder. Defaults to None.\n        bbox_head (:obj:`ConfigDict` or dict, optional): Config for the\n            bounding box head module. Defaults to None.\n        with_box_refine (bool, optional): Whether to refine the references\n            in the decoder. Defaults to `False`.\n        as_two_stage (bool, optional): Whether to generate the proposal\n            from the outputs of encoder. Defaults to `False`.\n        num_feature_levels (int, optional): Number of feature levels.\n            Defaults to 4.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 decoder: OptConfigType = None,\n                 bbox_head: OptConfigType = None,\n                 with_box_refine: bool = False,\n                 as_two_stage: bool = False,\n                 num_feature_levels: int = 4,\n                 **kwargs) -> None:\n        self.with_box_refine = with_box_refine\n        self.as_two_stage = as_two_stage\n        self.num_feature_levels = num_feature_levels\n\n        if bbox_head is not None:\n            assert 'share_pred_layer' not in bbox_head and \\\n                   'num_pred_layer' not in bbox_head and \\\n                   'as_two_stage' not in bbox_head, \\\n                'The two keyword args `share_pred_layer`, `num_pred_layer`, ' \\\n                'and `as_two_stage are set in `detector.__init__()`, users ' \\\n                'should not set them in `bbox_head` config.'\n            # The last prediction layer is used to generate proposal\n            # from encode feature map when `as_two_stage` is `True`.\n            # And all the prediction layers should share parameters\n            # when `with_box_refine` is `True`.\n            bbox_head['share_pred_layer'] = not with_box_refine\n            bbox_head['num_pred_layer'] = (decoder['num_layers'] + 1) \\\n                if self.as_two_stage else decoder['num_layers']\n            bbox_head['as_two_stage'] = as_two_stage\n\n        super().__init__(*args, decoder=decoder, bbox_head=bbox_head, **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        self.positional_encoding = SinePositionalEncoding(\n            **self.positional_encoding)\n        self.encoder = DeformableDetrTransformerEncoder(**self.encoder)\n        self.decoder = DeformableDetrTransformerDecoder(**self.decoder)\n        self.embed_dims = self.encoder.embed_dims\n        if not self.as_two_stage:\n            self.query_embedding = nn.Embedding(self.num_queries,\n                                                self.embed_dims * 2)\n            # NOTE The query_embedding will be split into query and query_pos\n            # in self.pre_decoder, hence, the embed_dims are doubled.\n\n        num_feats = self.positional_encoding.num_feats\n        assert num_feats * 2 == self.embed_dims, \\\n            'embed_dims should be exactly 2 times of num_feats. ' \\\n            f'Found {self.embed_dims} and {num_feats}.'\n\n        self.level_embed = nn.Parameter(\n            torch.Tensor(self.num_feature_levels, self.embed_dims))\n\n        if self.as_two_stage:\n            self.memory_trans_fc = nn.Linear(self.embed_dims, self.embed_dims)\n            self.memory_trans_norm = nn.LayerNorm(self.embed_dims)\n            self.pos_trans_fc = nn.Linear(self.embed_dims * 2,\n                                          self.embed_dims * 2)\n            self.pos_trans_norm = nn.LayerNorm(self.embed_dims * 2)\n        else:\n            self.reference_points_fc = nn.Linear(self.embed_dims, 2)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights for Transformer and other components.\"\"\"\n        super().init_weights()\n        for coder in self.encoder, self.decoder:\n            for p in coder.parameters():\n                if p.dim() > 1:\n                    nn.init.xavier_uniform_(p)\n        for m in self.modules():\n            if isinstance(m, MultiScaleDeformableAttention):\n                m.init_weights()\n        if self.as_two_stage:\n            nn.init.xavier_uniform_(self.memory_trans_fc.weight)\n            nn.init.xavier_uniform_(self.pos_trans_fc.weight)\n        else:\n            xavier_init(\n                self.reference_points_fc, distribution='uniform', bias=0.)\n        normal_(self.level_embed)\n\n    def pre_transformer(\n            self,\n            mlvl_feats: Tuple[Tensor],\n            batch_data_samples: OptSampleList = None) -> Tuple[Dict]:\n        \"\"\"Process image features before feeding them to the transformer.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            mlvl_feats (tuple[Tensor]): Multi-level features that may have\n                different resolutions, output from neck. Each feature has\n                shape (bs, dim, h_lvl, w_lvl), where 'lvl' means 'layer'.\n            batch_data_samples (list[:obj:`DetDataSample`], optional): The\n                batch data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            tuple[dict]: The first dict contains the inputs of encoder and the\n            second dict contains the inputs of decoder.\n\n            - encoder_inputs_dict (dict): The keyword args dictionary of\n              `self.forward_encoder()`, which includes 'feat', 'feat_mask',\n              and 'feat_pos'.\n            - decoder_inputs_dict (dict): The keyword args dictionary of\n              `self.forward_decoder()`, which includes 'memory_mask'.\n        \"\"\"\n        batch_size = mlvl_feats[0].size(0)\n\n        # construct binary masks for the transformer.\n        assert batch_data_samples is not None\n        batch_input_shape = batch_data_samples[0].batch_input_shape\n        input_img_h, input_img_w = batch_input_shape\n        img_shape_list = [sample.img_shape for sample in batch_data_samples]\n        same_shape_flag = all([\n            s[0] == input_img_h and s[1] == input_img_w for s in img_shape_list\n        ])\n        # support torch2onnx without feeding masks\n        if torch.onnx.is_in_onnx_export() or same_shape_flag:\n            mlvl_masks = []\n            mlvl_pos_embeds = []\n            for feat in mlvl_feats:\n                mlvl_masks.append(None)\n                mlvl_pos_embeds.append(\n                    self.positional_encoding(None, input=feat))\n        else:\n            masks = mlvl_feats[0].new_ones(\n                (batch_size, input_img_h, input_img_w))\n            for img_id in range(batch_size):\n                img_h, img_w = img_shape_list[img_id]\n                masks[img_id, :img_h, :img_w] = 0\n            # NOTE following the official DETR repo, non-zero\n            # values representing ignored positions, while\n            # zero values means valid positions.\n\n            mlvl_masks = []\n            mlvl_pos_embeds = []\n            for feat in mlvl_feats:\n                mlvl_masks.append(\n                    F.interpolate(masks[None], size=feat.shape[-2:]).to(\n                        torch.bool).squeeze(0))\n                mlvl_pos_embeds.append(\n                    self.positional_encoding(mlvl_masks[-1]))\n\n        feat_flatten = []\n        lvl_pos_embed_flatten = []\n        mask_flatten = []\n        spatial_shapes = []\n        for lvl, (feat, mask, pos_embed) in enumerate(\n                zip(mlvl_feats, mlvl_masks, mlvl_pos_embeds)):\n            batch_size, c, h, w = feat.shape\n            spatial_shape = torch._shape_as_tensor(feat)[2:].to(feat.device)\n            # [bs, c, h_lvl, w_lvl] -> [bs, h_lvl*w_lvl, c]\n            feat = feat.view(batch_size, c, -1).permute(0, 2, 1)\n            pos_embed = pos_embed.view(batch_size, c, -1).permute(0, 2, 1)\n            lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)\n            # [bs, h_lvl, w_lvl] -> [bs, h_lvl*w_lvl]\n            if mask is not None:\n                mask = mask.flatten(1)\n\n            feat_flatten.append(feat)\n            lvl_pos_embed_flatten.append(lvl_pos_embed)\n            mask_flatten.append(mask)\n            spatial_shapes.append(spatial_shape)\n\n        # (bs, num_feat_points, dim)\n        feat_flatten = torch.cat(feat_flatten, 1)\n        lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)\n        # (bs, num_feat_points), where num_feat_points = sum_lvl(h_lvl*w_lvl)\n        if mask_flatten[0] is not None:\n            mask_flatten = torch.cat(mask_flatten, 1)\n        else:\n            mask_flatten = None\n\n        # (num_level, 2)\n        spatial_shapes = torch.cat(spatial_shapes).view(-1, 2)\n        level_start_index = torch.cat((\n            spatial_shapes.new_zeros((1, )),  # (num_level)\n            spatial_shapes.prod(1).cumsum(0)[:-1]))\n        if mlvl_masks[0] is not None:\n            valid_ratios = torch.stack(  # (bs, num_level, 2)\n                [self.get_valid_ratio(m) for m in mlvl_masks], 1)\n        else:\n            valid_ratios = mlvl_feats[0].new_ones(batch_size, len(mlvl_feats),\n                                                  2)\n\n        encoder_inputs_dict = dict(\n            feat=feat_flatten,\n            feat_mask=mask_flatten,\n            feat_pos=lvl_pos_embed_flatten,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios)\n        decoder_inputs_dict = dict(\n            memory_mask=mask_flatten,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios)\n        return encoder_inputs_dict, decoder_inputs_dict\n\n    def forward_encoder(self, feat: Tensor, feat_mask: Tensor,\n                        feat_pos: Tensor, spatial_shapes: Tensor,\n                        level_start_index: Tensor,\n                        valid_ratios: Tensor) -> Dict:\n        \"\"\"Forward with Transformer encoder.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            feat (Tensor): Sequential features, has shape (bs, num_feat_points,\n                dim).\n            feat_mask (Tensor): ByteTensor, the padding mask of the features,\n                has shape (bs, num_feat_points).\n            feat_pos (Tensor): The positional embeddings of the features, has\n                shape (bs, num_feat_points, dim).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n\n        Returns:\n            dict: The dictionary of encoder outputs, which includes the\n            `memory` of the encoder output.\n        \"\"\"\n        memory = self.encoder(\n            query=feat,\n            query_pos=feat_pos,\n            key_padding_mask=feat_mask,  # for self_attn\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios)\n        encoder_outputs_dict = dict(\n            memory=memory,\n            memory_mask=feat_mask,\n            spatial_shapes=spatial_shapes)\n        return encoder_outputs_dict\n\n    def pre_decoder(self, memory: Tensor, memory_mask: Tensor,\n                    spatial_shapes: Tensor) -> Tuple[Dict, Dict]:\n        \"\"\"Prepare intermediate variables before entering Transformer decoder,\n        such as `query`, `query_pos`, and `reference_points`.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points). It will only be used when\n                `as_two_stage` is `True`.\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n                It will only be used when `as_two_stage` is `True`.\n\n        Returns:\n            tuple[dict, dict]: The decoder_inputs_dict and head_inputs_dict.\n\n            - decoder_inputs_dict (dict): The keyword dictionary args of\n              `self.forward_decoder()`, which includes 'query', 'query_pos',\n              'memory', and `reference_points`. The reference_points of\n              decoder input here are 4D boxes when `as_two_stage` is `True`,\n              otherwise 2D points, although it has `points` in its name.\n              The reference_points in encoder is always 2D points.\n            - head_inputs_dict (dict): The keyword dictionary args of the\n              bbox_head functions, which includes `enc_outputs_class` and\n              `enc_outputs_coord`. They are both `None` when 'as_two_stage'\n              is `False`. The dict is empty when `self.training` is `False`.\n        \"\"\"\n        batch_size, _, c = memory.shape\n        if self.as_two_stage:\n            output_memory, output_proposals = \\\n                self.gen_encoder_output_proposals(\n                    memory, memory_mask, spatial_shapes)\n            enc_outputs_class = self.bbox_head.cls_branches[\n                self.decoder.num_layers](\n                    output_memory)\n            enc_outputs_coord_unact = self.bbox_head.reg_branches[\n                self.decoder.num_layers](output_memory) + output_proposals\n            enc_outputs_coord = enc_outputs_coord_unact.sigmoid()\n            # We only use the first channel in enc_outputs_class as foreground,\n            # the other (num_classes - 1) channels are actually not used.\n            # Its targets are set to be 0s, which indicates the first\n            # class (foreground) because we use [0, num_classes - 1] to\n            # indicate class labels, background class is indicated by\n            # num_classes (similar convention in RPN).\n            # See https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/dense_heads/deformable_detr_head.py#L241 # noqa\n            # This follows the official implementation of Deformable DETR.\n            topk_proposals = torch.topk(\n                enc_outputs_class[..., 0], self.num_queries, dim=1)[1]\n            topk_coords_unact = torch.gather(\n                enc_outputs_coord_unact, 1,\n                topk_proposals.unsqueeze(-1).repeat(1, 1, 4))\n            topk_coords_unact = topk_coords_unact.detach()\n            reference_points = topk_coords_unact.sigmoid()\n            pos_trans_out = self.pos_trans_fc(\n                self.get_proposal_pos_embed(topk_coords_unact))\n            pos_trans_out = self.pos_trans_norm(pos_trans_out)\n            query_pos, query = torch.split(pos_trans_out, c, dim=2)\n        else:\n            enc_outputs_class, enc_outputs_coord = None, None\n            query_embed = self.query_embedding.weight\n            query_pos, query = torch.split(query_embed, c, dim=1)\n            query_pos = query_pos.unsqueeze(0).expand(batch_size, -1, -1)\n            query = query.unsqueeze(0).expand(batch_size, -1, -1)\n            reference_points = self.reference_points_fc(query_pos).sigmoid()\n\n        decoder_inputs_dict = dict(\n            query=query,\n            query_pos=query_pos,\n            memory=memory,\n            reference_points=reference_points)\n        head_inputs_dict = dict(\n            enc_outputs_class=enc_outputs_class,\n            enc_outputs_coord=enc_outputs_coord) if self.training else dict()\n        return decoder_inputs_dict, head_inputs_dict\n\n    def forward_decoder(self, query: Tensor, query_pos: Tensor, memory: Tensor,\n                        memory_mask: Tensor, reference_points: Tensor,\n                        spatial_shapes: Tensor, level_start_index: Tensor,\n                        valid_ratios: Tensor) -> Dict:\n        \"\"\"Forward with Transformer decoder.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            query (Tensor): The queries of decoder inputs, has shape\n                (bs, num_queries, dim).\n            query_pos (Tensor): The positional queries of decoder inputs,\n                has shape (bs, num_queries, dim).\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points).\n            reference_points (Tensor): The initial reference, has shape\n                (bs, num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h) when `as_two_stage` is `True`, otherwise has\n                shape (bs, num_queries, 2) with the last dimension arranged as\n                (cx, cy).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n\n        Returns:\n            dict: The dictionary of decoder outputs, which includes the\n            `hidden_states` of the decoder output and `references` including\n            the initial and intermediate reference_points.\n        \"\"\"\n        inter_states, inter_references = self.decoder(\n            query=query,\n            value=memory,\n            query_pos=query_pos,\n            key_padding_mask=memory_mask,  # for cross_attn\n            reference_points=reference_points,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            reg_branches=self.bbox_head.reg_branches\n            if self.with_box_refine else None)\n        references = [reference_points, *inter_references]\n        decoder_outputs_dict = dict(\n            hidden_states=inter_states, references=references)\n        return decoder_outputs_dict\n\n    @staticmethod\n    def get_valid_ratio(mask: Tensor) -> Tensor:\n        \"\"\"Get the valid radios of feature map in a level.\n\n        .. code:: text\n\n                    |---> valid_W <---|\n                 ---+-----------------+-----+---\n                  A |                 |     | A\n                  | |                 |     | |\n                  | |                 |     | |\n            valid_H |                 |     | |\n                  | |                 |     | H\n                  | |                 |     | |\n                  V |                 |     | |\n                 ---+-----------------+     | |\n                    |                       | V\n                    +-----------------------+---\n                    |---------> W <---------|\n\n          The valid_ratios are defined as:\n                r_h = valid_H / H,  r_w = valid_W / W\n          They are the factors to re-normalize the relative coordinates of the\n          image to the relative coordinates of the current level feature map.\n\n        Args:\n            mask (Tensor): Binary mask of a feature map, has shape (bs, H, W).\n\n        Returns:\n            Tensor: valid ratios [r_w, r_h] of a feature map, has shape (1, 2).\n        \"\"\"\n        _, H, W = mask.shape\n        valid_H = torch.sum(~mask[:, :, 0], 1)\n        valid_W = torch.sum(~mask[:, 0, :], 1)\n        valid_ratio_h = valid_H.float() / H\n        valid_ratio_w = valid_W.float() / W\n        valid_ratio = torch.stack([valid_ratio_w, valid_ratio_h], -1)\n        return valid_ratio\n\n    def gen_encoder_output_proposals(\n            self, memory: Tensor, memory_mask: Tensor,\n            spatial_shapes: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Generate proposals from encoded memory. The function will only be\n        used when `as_two_stage` is `True`.\n\n        Args:\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n\n        Returns:\n            tuple: A tuple of transformed memory and proposals.\n\n            - output_memory (Tensor): The transformed memory for obtaining\n              top-k proposals, has shape (bs, num_feat_points, dim).\n            - output_proposals (Tensor): The inverse-normalized proposal, has\n              shape (batch_size, num_keys, 4) with the last dimension arranged\n              as (cx, cy, w, h).\n        \"\"\"\n\n        bs = memory.size(0)\n        proposals = []\n        _cur = 0  # start index in the sequence of the current level\n        for lvl, HW in enumerate(spatial_shapes):\n            H, W = HW\n\n            if memory_mask is not None:\n                mask_flatten_ = memory_mask[:, _cur:(_cur + H * W)].view(\n                    bs, H, W, 1)\n                valid_H = torch.sum(~mask_flatten_[:, :, 0, 0],\n                                    1).unsqueeze(-1)\n                valid_W = torch.sum(~mask_flatten_[:, 0, :, 0],\n                                    1).unsqueeze(-1)\n                scale = torch.cat([valid_W, valid_H], 1).view(bs, 1, 1, 2)\n            else:\n                if not isinstance(HW, torch.Tensor):\n                    HW = memory.new_tensor(HW)\n                scale = HW.unsqueeze(0).flip(dims=[0, 1]).view(1, 1, 1, 2)\n            grid_y, grid_x = torch.meshgrid(\n                torch.linspace(\n                    0, H - 1, H, dtype=torch.float32, device=memory.device),\n                torch.linspace(\n                    0, W - 1, W, dtype=torch.float32, device=memory.device))\n            grid = torch.cat([grid_x.unsqueeze(-1), grid_y.unsqueeze(-1)], -1)\n            grid = (grid.unsqueeze(0).expand(bs, -1, -1, -1) + 0.5) / scale\n            wh = torch.ones_like(grid) * 0.05 * (2.0**lvl)\n            proposal = torch.cat((grid, wh), -1).view(bs, -1, 4)\n            proposals.append(proposal)\n            _cur += (H * W)\n        output_proposals = torch.cat(proposals, 1)\n        # do not use `all` to make it exportable to onnx\n        output_proposals_valid = (\n            (output_proposals > 0.01) & (output_proposals < 0.99)).sum(\n                -1, keepdim=True) == output_proposals.shape[-1]\n        # inverse_sigmoid\n        output_proposals = torch.log(output_proposals / (1 - output_proposals))\n        if memory_mask is not None:\n            output_proposals = output_proposals.masked_fill(\n                memory_mask.unsqueeze(-1), float('inf'))\n        output_proposals = output_proposals.masked_fill(\n            ~output_proposals_valid, float('inf'))\n\n        output_memory = memory\n        if memory_mask is not None:\n            output_memory = output_memory.masked_fill(\n                memory_mask.unsqueeze(-1), float(0))\n        output_memory = output_memory.masked_fill(~output_proposals_valid,\n                                                  float(0))\n        output_memory = self.memory_trans_fc(output_memory)\n        output_memory = self.memory_trans_norm(output_memory)\n        # [bs, sum(hw), 2]\n        return output_memory, output_proposals\n\n    @staticmethod\n    def get_proposal_pos_embed(proposals: Tensor,\n                               num_pos_feats: int = 128,\n                               temperature: int = 10000) -> Tensor:\n        \"\"\"Get the position embedding of the proposal.\n\n        Args:\n            proposals (Tensor): Not normalized proposals, has shape\n                (bs, num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            num_pos_feats (int, optional): The feature dimension for each\n                position along x, y, w, and h-axis. Note the final returned\n                dimension for each position is 4 times of num_pos_feats.\n                Default to 128.\n            temperature (int, optional): The temperature used for scaling the\n                position embedding. Defaults to 10000.\n\n        Returns:\n            Tensor: The position embedding of proposal, has shape\n            (bs, num_queries, num_pos_feats * 4), with the last dimension\n            arranged as (cx, cy, w, h)\n        \"\"\"\n        scale = 2 * math.pi\n        dim_t = torch.arange(\n            num_pos_feats, dtype=torch.float32, device=proposals.device)\n        dim_t = temperature**(2 * (dim_t // 2) / num_pos_feats)\n        # N, L, 4\n        proposals = proposals.sigmoid() * scale\n        # N, L, 4, 128\n        pos = proposals[:, :, :, None] / dim_t\n        # N, L, 4, 64, 2\n        pos = torch.stack((pos[:, :, :, 0::2].sin(), pos[:, :, :, 1::2].cos()),\n                          dim=4).flatten(2)\n        return pos\n"
  },
  {
    "path": "mmdet/models/detectors/detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor, nn\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList\nfrom ..layers import (DetrTransformerDecoder, DetrTransformerEncoder,\n                      SinePositionalEncoding)\nfrom .base_detr import DetectionTransformer\n\n\n@MODELS.register_module()\nclass DETR(DetectionTransformer):\n    r\"\"\"Implementation of `DETR: End-to-End Object Detection with Transformers.\n\n    <https://arxiv.org/pdf/2005.12872>`_.\n\n    Code is modified from the `official github repo\n    <https://github.com/facebookresearch/detr>`_.\n    \"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        self.positional_encoding = SinePositionalEncoding(\n            **self.positional_encoding)\n        self.encoder = DetrTransformerEncoder(**self.encoder)\n        self.decoder = DetrTransformerDecoder(**self.decoder)\n        self.embed_dims = self.encoder.embed_dims\n        # NOTE The embed_dims is typically passed from the inside out.\n        # For example in DETR, The embed_dims is passed as\n        # self_attn -> the first encoder layer -> encoder -> detector.\n        self.query_embedding = nn.Embedding(self.num_queries, self.embed_dims)\n\n        num_feats = self.positional_encoding.num_feats\n        assert num_feats * 2 == self.embed_dims, \\\n            'embed_dims should be exactly 2 times of num_feats. ' \\\n            f'Found {self.embed_dims} and {num_feats}.'\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights for Transformer and other components.\"\"\"\n        super().init_weights()\n        for coder in self.encoder, self.decoder:\n            for p in coder.parameters():\n                if p.dim() > 1:\n                    nn.init.xavier_uniform_(p)\n\n    def pre_transformer(\n            self,\n            img_feats: Tuple[Tensor],\n            batch_data_samples: OptSampleList = None) -> Tuple[Dict, Dict]:\n        \"\"\"Prepare the inputs of the Transformer.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            img_feats (Tuple[Tensor]): Tuple of features output from the neck,\n                has shape (bs, c, h, w).\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such as\n                `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            tuple[dict, dict]: The first dict contains the inputs of encoder\n            and the second dict contains the inputs of decoder.\n\n            - encoder_inputs_dict (dict): The keyword args dictionary of\n              `self.forward_encoder()`, which includes 'feat', 'feat_mask',\n              and 'feat_pos'.\n            - decoder_inputs_dict (dict): The keyword args dictionary of\n              `self.forward_decoder()`, which includes 'memory_mask',\n              and 'memory_pos'.\n        \"\"\"\n\n        feat = img_feats[-1]  # NOTE img_feats contains only one feature.\n        batch_size, feat_dim, _, _ = feat.shape\n        # construct binary masks which for the transformer.\n        assert batch_data_samples is not None\n        batch_input_shape = batch_data_samples[0].batch_input_shape\n        input_img_h, input_img_w = batch_input_shape\n        img_shape_list = [sample.img_shape for sample in batch_data_samples]\n        same_shape_flag = all([\n            s[0] == input_img_h and s[1] == input_img_w for s in img_shape_list\n        ])\n        if torch.onnx.is_in_onnx_export() or same_shape_flag:\n            masks = None\n            # [batch_size, embed_dim, h, w]\n            pos_embed = self.positional_encoding(masks, input=feat)\n        else:\n            masks = feat.new_ones((batch_size, input_img_h, input_img_w))\n            for img_id in range(batch_size):\n                img_h, img_w = img_shape_list[img_id]\n                masks[img_id, :img_h, :img_w] = 0\n            # NOTE following the official DETR repo, non-zero values represent\n            # ignored positions, while zero values mean valid positions.\n\n            masks = F.interpolate(\n                masks.unsqueeze(1),\n                size=feat.shape[-2:]).to(torch.bool).squeeze(1)\n            # [batch_size, embed_dim, h, w]\n            pos_embed = self.positional_encoding(masks)\n\n        # use `view` instead of `flatten` for dynamically exporting to ONNX\n        # [bs, c, h, w] -> [bs, h*w, c]\n        feat = feat.view(batch_size, feat_dim, -1).permute(0, 2, 1)\n        pos_embed = pos_embed.view(batch_size, feat_dim, -1).permute(0, 2, 1)\n        # [bs, h, w] -> [bs, h*w]\n        if masks is not None:\n            masks = masks.view(batch_size, -1)\n\n        # prepare transformer_inputs_dict\n        encoder_inputs_dict = dict(\n            feat=feat, feat_mask=masks, feat_pos=pos_embed)\n        decoder_inputs_dict = dict(memory_mask=masks, memory_pos=pos_embed)\n        return encoder_inputs_dict, decoder_inputs_dict\n\n    def forward_encoder(self, feat: Tensor, feat_mask: Tensor,\n                        feat_pos: Tensor) -> Dict:\n        \"\"\"Forward with Transformer encoder.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            feat (Tensor): Sequential features, has shape (bs, num_feat_points,\n                dim).\n            feat_mask (Tensor): ByteTensor, the padding mask of the features,\n                has shape (bs, num_feat_points).\n            feat_pos (Tensor): The positional embeddings of the features, has\n                shape (bs, num_feat_points, dim).\n\n        Returns:\n            dict: The dictionary of encoder outputs, which includes the\n            `memory` of the encoder output.\n        \"\"\"\n        memory = self.encoder(\n            query=feat, query_pos=feat_pos,\n            key_padding_mask=feat_mask)  # for self_attn\n        encoder_outputs_dict = dict(memory=memory)\n        return encoder_outputs_dict\n\n    def pre_decoder(self, memory: Tensor) -> Tuple[Dict, Dict]:\n        \"\"\"Prepare intermediate variables before entering Transformer decoder,\n        such as `query`, `query_pos`.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n\n        Returns:\n            tuple[dict, dict]: The first dict contains the inputs of decoder\n            and the second dict contains the inputs of the bbox_head function.\n\n            - decoder_inputs_dict (dict): The keyword args dictionary of\n              `self.forward_decoder()`, which includes 'query', 'query_pos',\n              'memory'.\n            - head_inputs_dict (dict): The keyword args dictionary of the\n              bbox_head functions, which is usually empty, or includes\n              `enc_outputs_class` and `enc_outputs_class` when the detector\n              support 'two stage' or 'query selection' strategies.\n        \"\"\"\n\n        batch_size = memory.size(0)  # (bs, num_feat_points, dim)\n        query_pos = self.query_embedding.weight\n        # (num_queries, dim) -> (bs, num_queries, dim)\n        query_pos = query_pos.unsqueeze(0).repeat(batch_size, 1, 1)\n        query = torch.zeros_like(query_pos)\n\n        decoder_inputs_dict = dict(\n            query_pos=query_pos, query=query, memory=memory)\n        head_inputs_dict = dict()\n        return decoder_inputs_dict, head_inputs_dict\n\n    def forward_decoder(self, query: Tensor, query_pos: Tensor, memory: Tensor,\n                        memory_mask: Tensor, memory_pos: Tensor) -> Dict:\n        \"\"\"Forward with Transformer decoder.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            query (Tensor): The queries of decoder inputs, has shape\n                (bs, num_queries, dim).\n            query_pos (Tensor): The positional queries of decoder inputs,\n                has shape (bs, num_queries, dim).\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points).\n            memory_pos (Tensor): The positional embeddings of memory, has\n                shape (bs, num_feat_points, dim).\n\n        Returns:\n            dict: The dictionary of decoder outputs, which includes the\n            `hidden_states` of the decoder output.\n\n            - hidden_states (Tensor): Has shape\n              (num_decoder_layers, bs, num_queries, dim)\n        \"\"\"\n\n        hidden_states = self.decoder(\n            query=query,\n            key=memory,\n            value=memory,\n            query_pos=query_pos,\n            key_pos=memory_pos,\n            key_padding_mask=memory_mask)  # for cross_attn\n\n        head_inputs_dict = dict(hidden_states=hidden_states)\n        return head_inputs_dict\n"
  },
  {
    "path": "mmdet/models/detectors/dino.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Optional, Tuple\n\nimport torch\nfrom torch import Tensor, nn\nfrom torch.nn.init import normal_\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList\nfrom mmdet.utils import OptConfigType\nfrom ..layers import (CdnQueryGenerator, DeformableDetrTransformerEncoder,\n                      DinoTransformerDecoder, SinePositionalEncoding)\nfrom .deformable_detr import DeformableDETR, MultiScaleDeformableAttention\n\n\n@MODELS.register_module()\nclass DINO(DeformableDETR):\n    r\"\"\"Implementation of `DINO: DETR with Improved DeNoising Anchor Boxes\n    for End-to-End Object Detection <https://arxiv.org/abs/2203.03605>`_\n\n    Code is modified from the `official github repo\n    <https://github.com/IDEA-Research/DINO>`_.\n\n    Args:\n        dn_cfg (:obj:`ConfigDict` or dict, optional): Config of denoising\n            query generator. Defaults to `None`.\n    \"\"\"\n\n    def __init__(self, *args, dn_cfg: OptConfigType = None, **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        assert self.as_two_stage, 'as_two_stage must be True for DINO'\n        assert self.with_box_refine, 'with_box_refine must be True for DINO'\n\n        if dn_cfg is not None:\n            assert 'num_classes' not in dn_cfg and \\\n                   'num_queries' not in dn_cfg and \\\n                   'hidden_dim' not in dn_cfg, \\\n                'The three keyword args `num_classes`, `embed_dims`, and ' \\\n                '`num_matching_queries` are set in `detector.__init__()`, ' \\\n                'users should not set them in `dn_cfg` config.'\n            dn_cfg['num_classes'] = self.bbox_head.num_classes\n            dn_cfg['embed_dims'] = self.embed_dims\n            dn_cfg['num_matching_queries'] = self.num_queries\n        self.dn_query_generator = CdnQueryGenerator(**dn_cfg)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        self.positional_encoding = SinePositionalEncoding(\n            **self.positional_encoding)\n        self.encoder = DeformableDetrTransformerEncoder(**self.encoder)\n        self.decoder = DinoTransformerDecoder(**self.decoder)\n        self.embed_dims = self.encoder.embed_dims\n        self.query_embedding = nn.Embedding(self.num_queries, self.embed_dims)\n        # NOTE In DINO, the query_embedding only contains content\n        # queries, while in Deformable DETR, the query_embedding\n        # contains both content and spatial queries, and in DETR,\n        # it only contains spatial queries.\n\n        num_feats = self.positional_encoding.num_feats\n        assert num_feats * 2 == self.embed_dims, \\\n            f'embed_dims should be exactly 2 times of num_feats. ' \\\n            f'Found {self.embed_dims} and {num_feats}.'\n\n        self.level_embed = nn.Parameter(\n            torch.Tensor(self.num_feature_levels, self.embed_dims))\n        self.memory_trans_fc = nn.Linear(self.embed_dims, self.embed_dims)\n        self.memory_trans_norm = nn.LayerNorm(self.embed_dims)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights for Transformer and other components.\"\"\"\n        super(DeformableDETR, self).init_weights()\n        for coder in self.encoder, self.decoder:\n            for p in coder.parameters():\n                if p.dim() > 1:\n                    nn.init.xavier_uniform_(p)\n        for m in self.modules():\n            if isinstance(m, MultiScaleDeformableAttention):\n                m.init_weights()\n        nn.init.xavier_uniform_(self.memory_trans_fc.weight)\n        nn.init.xavier_uniform_(self.query_embedding.weight)\n        normal_(self.level_embed)\n\n    def forward_transformer(\n        self,\n        img_feats: Tuple[Tensor],\n        batch_data_samples: OptSampleList = None,\n    ) -> Dict:\n        \"\"\"Forward process of Transformer.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n        The difference is that the ground truth in `batch_data_samples` is\n        required for the `pre_decoder` to prepare the query of DINO.\n        Additionally, DINO inherits the `pre_transformer` method and the\n        `forward_encoder` method of DeformableDETR. More details about the\n        two methods can be found in `mmdet/detector/deformable_detr.py`.\n\n        Args:\n            img_feats (tuple[Tensor]): Tuple of feature maps from neck. Each\n                feature map has shape (bs, dim, H, W).\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            dict: The dictionary of bbox_head function inputs, which always\n            includes the `hidden_states` of the decoder output and may contain\n            `references` including the initial and intermediate references.\n        \"\"\"\n        encoder_inputs_dict, decoder_inputs_dict = self.pre_transformer(\n            img_feats, batch_data_samples)\n\n        encoder_outputs_dict = self.forward_encoder(**encoder_inputs_dict)\n\n        tmp_dec_in, head_inputs_dict = self.pre_decoder(\n            **encoder_outputs_dict, batch_data_samples=batch_data_samples)\n        decoder_inputs_dict.update(tmp_dec_in)\n\n        decoder_outputs_dict = self.forward_decoder(**decoder_inputs_dict)\n        head_inputs_dict.update(decoder_outputs_dict)\n        return head_inputs_dict\n\n    def pre_decoder(\n        self,\n        memory: Tensor,\n        memory_mask: Tensor,\n        spatial_shapes: Tensor,\n        batch_data_samples: OptSampleList = None,\n    ) -> Tuple[Dict]:\n        \"\"\"Prepare intermediate variables before entering Transformer decoder,\n        such as `query`, `query_pos`, and `reference_points`.\n\n        Args:\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points). Will only be used when\n                `as_two_stage` is `True`.\n            spatial_shapes (Tensor): Spatial shapes of features in all levels.\n                With shape (num_levels, 2), last dimension represents (h, w).\n                Will only be used when `as_two_stage` is `True`.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n                Defaults to None.\n\n        Returns:\n            tuple[dict]: The decoder_inputs_dict and head_inputs_dict.\n\n            - decoder_inputs_dict (dict): The keyword dictionary args of\n              `self.forward_decoder()`, which includes 'query', 'memory',\n              `reference_points`, and `dn_mask`. The reference points of\n              decoder input here are 4D boxes, although it has `points`\n              in its name.\n            - head_inputs_dict (dict): The keyword dictionary args of the\n              bbox_head functions, which includes `topk_score`, `topk_coords`,\n              and `dn_meta` when `self.training` is `True`, else is empty.\n        \"\"\"\n        bs, _, c = memory.shape\n        cls_out_features = self.bbox_head.cls_branches[\n            self.decoder.num_layers].out_features\n\n        output_memory, output_proposals = self.gen_encoder_output_proposals(\n            memory, memory_mask, spatial_shapes)\n        enc_outputs_class = self.bbox_head.cls_branches[\n            self.decoder.num_layers](\n                output_memory)\n        enc_outputs_coord_unact = self.bbox_head.reg_branches[\n            self.decoder.num_layers](output_memory) + output_proposals\n\n        # NOTE The DINO selects top-k proposals according to scores of\n        # multi-class classification, while DeformDETR, where the input\n        # is `enc_outputs_class[..., 0]` selects according to scores of\n        # binary classification.\n        topk_indices = torch.topk(\n            enc_outputs_class.max(-1)[0], k=self.num_queries, dim=1)[1]\n        topk_score = torch.gather(\n            enc_outputs_class, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, cls_out_features))\n        topk_coords_unact = torch.gather(\n            enc_outputs_coord_unact, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, 4))\n        topk_coords = topk_coords_unact.sigmoid()\n        topk_coords_unact = topk_coords_unact.detach()\n\n        query = self.query_embedding.weight[:, None, :]\n        query = query.repeat(1, bs, 1).transpose(0, 1)\n        if self.training:\n            dn_label_query, dn_bbox_query, dn_mask, dn_meta = \\\n                self.dn_query_generator(batch_data_samples)\n            query = torch.cat([dn_label_query, query], dim=1)\n            reference_points = torch.cat([dn_bbox_query, topk_coords_unact],\n                                         dim=1)\n        else:\n            reference_points = topk_coords_unact\n            dn_mask, dn_meta = None, None\n        reference_points = reference_points.sigmoid()\n\n        decoder_inputs_dict = dict(\n            query=query,\n            memory=memory,\n            reference_points=reference_points,\n            dn_mask=dn_mask)\n        # NOTE DINO calculates encoder losses on scores and coordinates\n        # of selected top-k encoder queries, while DeformDETR is of all\n        # encoder queries.\n        head_inputs_dict = dict(\n            enc_outputs_class=topk_score,\n            enc_outputs_coord=topk_coords,\n            dn_meta=dn_meta) if self.training else dict()\n        return decoder_inputs_dict, head_inputs_dict\n\n    def forward_decoder(self,\n                        query: Tensor,\n                        memory: Tensor,\n                        memory_mask: Tensor,\n                        reference_points: Tensor,\n                        spatial_shapes: Tensor,\n                        level_start_index: Tensor,\n                        valid_ratios: Tensor,\n                        dn_mask: Optional[Tensor] = None,\n                        **kwargs) -> Dict:\n        \"\"\"Forward with Transformer decoder.\n\n        The forward procedure of the transformer is defined as:\n        'pre_transformer' -> 'encoder' -> 'pre_decoder' -> 'decoder'\n        More details can be found at `TransformerDetector.forward_transformer`\n        in `mmdet/detector/base_detr.py`.\n\n        Args:\n            query (Tensor): The queries of decoder inputs, has shape\n                (bs, num_queries_total, dim), where `num_queries_total` is the\n                sum of `num_denoising_queries` and `num_matching_queries` when\n                `self.training` is `True`, else `num_matching_queries`.\n            memory (Tensor): The output embeddings of the Transformer encoder,\n                has shape (bs, num_feat_points, dim).\n            memory_mask (Tensor): ByteTensor, the padding mask of the memory,\n                has shape (bs, num_feat_points).\n            reference_points (Tensor): The initial reference, has shape\n                (bs, num_queries_total, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n            dn_mask (Tensor, optional): The attention mask to prevent\n                information leakage from different denoising groups and\n                matching parts, will be used as `self_attn_mask` of the\n                `self.decoder`, has shape (num_queries_total,\n                num_queries_total).\n                It is `None` when `self.training` is `False`.\n\n        Returns:\n            dict: The dictionary of decoder outputs, which includes the\n            `hidden_states` of the decoder output and `references` including\n            the initial and intermediate reference_points.\n        \"\"\"\n        inter_states, references = self.decoder(\n            query=query,\n            value=memory,\n            key_padding_mask=memory_mask,\n            self_attn_mask=dn_mask,\n            reference_points=reference_points,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            reg_branches=self.bbox_head.reg_branches,\n            **kwargs)\n\n        if len(query) == self.num_queries:\n            # NOTE: This is to make sure label_embeding can be involved to\n            # produce loss even if there is no denoising query (no ground truth\n            # target in this GPU), otherwise, this will raise runtime error in\n            # distributed training.\n            inter_states[0] += \\\n                self.dn_query_generator.label_embedding.weight[0, 0] * 0.0\n\n        decoder_outputs_dict = dict(\n            hidden_states=inter_states, references=list(references))\n        return decoder_outputs_dict\n"
  },
  {
    "path": "mmdet/models/detectors/fast_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass FastRCNN(TwoStageDetector):\n    \"\"\"Implementation of `Fast R-CNN <https://arxiv.org/abs/1504.08083>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 roi_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            init_cfg=init_cfg,\n            data_preprocessor=data_preprocessor)\n"
  },
  {
    "path": "mmdet/models/detectors/faster_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass FasterRCNN(TwoStageDetector):\n    \"\"\"Implementation of `Faster R-CNN <https://arxiv.org/abs/1506.01497>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 rpn_head: ConfigType,\n                 roi_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            init_cfg=init_cfg,\n            data_preprocessor=data_preprocessor)\n"
  },
  {
    "path": "mmdet/models/detectors/fcos.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass FCOS(SingleStageDetector):\n    \"\"\"Implementation of `FCOS <https://arxiv.org/abs/1904.01355>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of FCOS. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of FCOS. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/fovea.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass FOVEA(SingleStageDetector):\n    \"\"\"Implementation of `FoveaBox <https://arxiv.org/abs/1904.03797>`_\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of FOVEA. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of FOVEA. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/fsaf.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass FSAF(SingleStageDetector):\n    \"\"\"Implementation of `FSAF <https://arxiv.org/abs/1903.00621>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/gfl.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass GFL(SingleStageDetector):\n    \"\"\"Implementation of `GFL <https://arxiv.org/abs/2006.04388>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of GFL. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of GFL. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/glip.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport re\nimport warnings\nfrom typing import Optional, Tuple, Union\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\ndef find_noun_phrases(caption: str) -> list:\n    \"\"\"Find noun phrases in a caption using nltk.\n    Args:\n        caption (str): The caption to analyze.\n\n    Returns:\n        list: List of noun phrases found in the caption.\n\n    Examples:\n        >>> caption = 'There is two cat and a remote in the picture'\n        >>> find_noun_phrases(caption) # ['cat', 'a remote', 'the picture']\n    \"\"\"\n    try:\n        import nltk\n        nltk.download('punkt', download_dir='~/nltk_data')\n        nltk.download('averaged_perceptron_tagger', download_dir='~/nltk_data')\n    except ImportError:\n        raise RuntimeError('nltk is not installed, please install it by: '\n                           'pip install nltk.')\n\n    caption = caption.lower()\n    tokens = nltk.word_tokenize(caption)\n    pos_tags = nltk.pos_tag(tokens)\n\n    grammar = 'NP: {<DT>?<JJ.*>*<NN.*>+}'\n    cp = nltk.RegexpParser(grammar)\n    result = cp.parse(pos_tags)\n\n    noun_phrases = []\n    for subtree in result.subtrees():\n        if subtree.label() == 'NP':\n            noun_phrases.append(' '.join(t[0] for t in subtree.leaves()))\n\n    return noun_phrases\n\n\ndef remove_punctuation(text: str) -> str:\n    \"\"\"Remove punctuation from a text.\n    Args:\n        text (str): The input text.\n\n    Returns:\n        str: The text with punctuation removed.\n    \"\"\"\n    punctuation = [\n        '|', ':', ';', '@', '(', ')', '[', ']', '{', '}', '^', '\\'', '\\\"', '’',\n        '`', '?', '$', '%', '#', '!', '&', '*', '+', ',', '.'\n    ]\n    for p in punctuation:\n        text = text.replace(p, '')\n    return text.strip()\n\n\ndef run_ner(caption: str) -> Tuple[list, list]:\n    \"\"\"Run NER on a caption and return the tokens and noun phrases.\n    Args:\n        caption (str): The input caption.\n\n    Returns:\n        Tuple[List, List]: A tuple containing the tokens and noun phrases.\n            - tokens_positive (List): A list of token positions.\n            - noun_phrases (List): A list of noun phrases.\n    \"\"\"\n    noun_phrases = find_noun_phrases(caption)\n    noun_phrases = [remove_punctuation(phrase) for phrase in noun_phrases]\n    noun_phrases = [phrase for phrase in noun_phrases if phrase != '']\n    print('noun_phrases:', noun_phrases)\n    relevant_phrases = noun_phrases\n    labels = noun_phrases\n\n    tokens_positive = []\n    for entity, label in zip(relevant_phrases, labels):\n        try:\n            # search all occurrences and mark them as different entities\n            # TODO: Not Robust\n            for m in re.finditer(entity, caption.lower()):\n                tokens_positive.append([[m.start(), m.end()]])\n        except Exception:\n            print('noun entities:', noun_phrases)\n            print('entity:', entity)\n            print('caption:', caption.lower())\n    return tokens_positive, noun_phrases\n\n\ndef create_positive_map(tokenized,\n                        tokens_positive: list,\n                        max_num_entities: int = 256) -> Tensor:\n    \"\"\"construct a map such that positive_map[i,j] = True\n    if box i is associated to token j\n\n    Args:\n        tokenized: The tokenized input.\n        tokens_positive (list): A list of token ranges\n            associated with positive boxes.\n        max_num_entities (int, optional): The maximum number of entities.\n            Defaults to 256.\n\n    Returns:\n        torch.Tensor: The positive map.\n\n    Raises:\n        Exception: If an error occurs during token-to-char mapping.\n    \"\"\"\n    positive_map = torch.zeros((len(tokens_positive), max_num_entities),\n                               dtype=torch.float)\n\n    for j, tok_list in enumerate(tokens_positive):\n        for (beg, end) in tok_list:\n            try:\n                beg_pos = tokenized.char_to_token(beg)\n                end_pos = tokenized.char_to_token(end - 1)\n            except Exception as e:\n                print('beg:', beg, 'end:', end)\n                print('token_positive:', tokens_positive)\n                raise e\n            if beg_pos is None:\n                try:\n                    beg_pos = tokenized.char_to_token(beg + 1)\n                    if beg_pos is None:\n                        beg_pos = tokenized.char_to_token(beg + 2)\n                except Exception:\n                    beg_pos = None\n            if end_pos is None:\n                try:\n                    end_pos = tokenized.char_to_token(end - 2)\n                    if end_pos is None:\n                        end_pos = tokenized.char_to_token(end - 3)\n                except Exception:\n                    end_pos = None\n            if beg_pos is None or end_pos is None:\n                continue\n\n            assert beg_pos is not None and end_pos is not None\n            positive_map[j, beg_pos:end_pos + 1].fill_(1)\n    return positive_map / (positive_map.sum(-1)[:, None] + 1e-6)\n\n\ndef create_positive_map_label_to_token(positive_map: Tensor,\n                                       plus: int = 0) -> dict:\n    \"\"\"Create a dictionary mapping the label to the token.\n    Args:\n        positive_map (Tensor): The positive map tensor.\n        plus (int, optional): Value added to the label for indexing.\n            Defaults to 0.\n\n    Returns:\n        dict: The dictionary mapping the label to the token.\n    \"\"\"\n    positive_map_label_to_token = {}\n    for i in range(len(positive_map)):\n        positive_map_label_to_token[i + plus] = torch.nonzero(\n            positive_map[i], as_tuple=True)[0].tolist()\n    return positive_map_label_to_token\n\n\ndef clean_label_name(name: str) -> str:\n    name = re.sub(r'\\(.*\\)', '', name)\n    name = re.sub(r'_', ' ', name)\n    name = re.sub(r'  ', ' ', name)\n    return name\n\n\ndef chunks(lst: list, n: int) -> list:\n    \"\"\"Yield successive n-sized chunks from lst.\"\"\"\n    all_ = []\n    for i in range(0, len(lst), n):\n        data_index = lst[i:i + n]\n        all_.append(data_index)\n    counter = 0\n    for i in all_:\n        counter += len(i)\n    assert (counter == len(lst))\n\n    return all_\n\n\n@MODELS.register_module()\nclass GLIP(SingleStageDetector):\n    \"\"\"Implementation of `GLIP <https://arxiv.org/abs/2112.03857>`_\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head config.\n        language_model (:obj:`ConfigDict` or dict): The language model config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of GLIP. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of GLIP. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 language_model: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n        self.language_model = MODELS.build(language_model)\n\n        self._special_tokens = '. '\n\n    def to_enhance_text_prompts(self, original_caption, enhanced_text_prompts):\n        caption_string = ''\n        tokens_positive = []\n        for idx, word in enumerate(original_caption):\n            if word in enhanced_text_prompts:\n                enhanced_text_dict = enhanced_text_prompts[word]\n                if 'prefix' in enhanced_text_dict:\n                    caption_string += enhanced_text_dict['prefix']\n                start_i = len(caption_string)\n                if 'name' in enhanced_text_dict:\n                    caption_string += enhanced_text_dict['name']\n                else:\n                    caption_string += word\n                end_i = len(caption_string)\n                tokens_positive.append([[start_i, end_i]])\n\n                if 'suffix' in enhanced_text_dict:\n                    caption_string += enhanced_text_dict['suffix']\n            else:\n                tokens_positive.append(\n                    [[len(caption_string),\n                      len(caption_string) + len(word)]])\n                caption_string += word\n\n            if idx != len(original_caption) - 1:\n                caption_string += self._special_tokens\n        return caption_string, tokens_positive\n\n    def to_plain_text_prompts(self, original_caption):\n        caption_string = ''\n        tokens_positive = []\n        for idx, word in enumerate(original_caption):\n            tokens_positive.append(\n                [[len(caption_string),\n                  len(caption_string) + len(word)]])\n            caption_string += word\n            if idx != len(original_caption) - 1:\n                caption_string += self._special_tokens\n        return caption_string, tokens_positive\n\n    def get_tokens_and_prompts(\n        self,\n        original_caption: Union[str, list, tuple],\n        custom_entities: bool = False,\n        enhanced_text_prompts: Optional[ConfigType] = None\n    ) -> Tuple[dict, str, list, list]:\n        \"\"\"Get the tokens positive and prompts for the caption.\"\"\"\n        if isinstance(original_caption, (list, tuple)) or custom_entities:\n            if custom_entities and isinstance(original_caption, str):\n                original_caption = original_caption.strip(self._special_tokens)\n                original_caption = original_caption.split(self._special_tokens)\n                original_caption = list(\n                    filter(lambda x: len(x) > 0, original_caption))\n\n            original_caption = [clean_label_name(i) for i in original_caption]\n\n            if custom_entities and enhanced_text_prompts is not None:\n                caption_string, tokens_positive = self.to_enhance_text_prompts(\n                    original_caption, enhanced_text_prompts)\n            else:\n                caption_string, tokens_positive = self.to_plain_text_prompts(\n                    original_caption)\n\n            tokenized = self.language_model.tokenizer([caption_string],\n                                                      return_tensors='pt')\n            entities = original_caption\n        else:\n            original_caption = original_caption.strip(self._special_tokens)\n            tokenized = self.language_model.tokenizer([original_caption],\n                                                      return_tensors='pt')\n            tokens_positive, noun_phrases = run_ner(original_caption)\n            entities = noun_phrases\n            caption_string = original_caption\n\n        return tokenized, caption_string, tokens_positive, entities\n\n    def get_positive_map(self, tokenized, tokens_positive):\n        positive_map = create_positive_map(tokenized, tokens_positive)\n        positive_map_label_to_token = create_positive_map_label_to_token(\n            positive_map, plus=1)\n        return positive_map_label_to_token, positive_map\n\n    def get_tokens_positive_and_prompts(\n        self,\n        original_caption: Union[str, list, tuple],\n        custom_entities: bool = False,\n        enhanced_text_prompt: Optional[ConfigType] = None,\n        tokens_positive: Optional[list] = None,\n    ) -> Tuple[dict, str, Tensor, list]:\n        if tokens_positive is not None:\n            if tokens_positive == -1:\n                if not original_caption.endswith('.'):\n                    original_caption = original_caption + self._special_tokens\n                return None, original_caption, None, original_caption\n            else:\n                if not original_caption.endswith('.'):\n                    original_caption = original_caption + self._special_tokens\n                tokenized = self.language_model.tokenizer([original_caption],\n                                                          return_tensors='pt')\n                positive_map_label_to_token, positive_map = \\\n                    self.get_positive_map(tokenized, tokens_positive)\n\n                entities = []\n                for token_positive in tokens_positive:\n                    instance_entities = []\n                    for t in token_positive:\n                        instance_entities.append(original_caption[t[0]:t[1]])\n                    entities.append(' / '.join(instance_entities))\n                return positive_map_label_to_token, original_caption, \\\n                    positive_map, entities\n\n        chunked_size = self.test_cfg.get('chunked_size', -1)\n        if not self.training and chunked_size > 0:\n            assert isinstance(original_caption,\n                              (list, tuple)) or custom_entities is True\n            all_output = self.get_tokens_positive_and_prompts_chunked(\n                original_caption, enhanced_text_prompt)\n            positive_map_label_to_token, \\\n                caption_string, \\\n                positive_map, \\\n                entities = all_output\n        else:\n            tokenized, caption_string, tokens_positive, entities = \\\n                self.get_tokens_and_prompts(\n                    original_caption, custom_entities, enhanced_text_prompt)\n            positive_map_label_to_token, positive_map = self.get_positive_map(\n                tokenized, tokens_positive)\n            if tokenized.input_ids.shape[1] > self.language_model.max_tokens:\n                warnings.warn('Inputting a text that is too long will result '\n                              'in poor prediction performance. '\n                              'Please reduce the text length.')\n        return positive_map_label_to_token, caption_string, \\\n            positive_map, entities\n\n    def get_tokens_positive_and_prompts_chunked(\n            self,\n            original_caption: Union[list, tuple],\n            enhanced_text_prompts: Optional[ConfigType] = None):\n        chunked_size = self.test_cfg.get('chunked_size', -1)\n        original_caption = [clean_label_name(i) for i in original_caption]\n\n        original_caption_chunked = chunks(original_caption, chunked_size)\n        ids_chunked = chunks(\n            list(range(1,\n                       len(original_caption) + 1)), chunked_size)\n\n        positive_map_label_to_token_chunked = []\n        caption_string_chunked = []\n        positive_map_chunked = []\n        entities_chunked = []\n\n        for i in range(len(ids_chunked)):\n            if enhanced_text_prompts is not None:\n                caption_string, tokens_positive = self.to_enhance_text_prompts(\n                    original_caption_chunked[i], enhanced_text_prompts)\n            else:\n                caption_string, tokens_positive = self.to_plain_text_prompts(\n                    original_caption_chunked[i])\n            tokenized = self.language_model.tokenizer([caption_string],\n                                                      return_tensors='pt')\n            if tokenized.input_ids.shape[1] > self.language_model.max_tokens:\n                warnings.warn('Inputting a text that is too long will result '\n                              'in poor prediction performance. '\n                              'Please reduce the --chunked-size.')\n            positive_map_label_to_token, positive_map = self.get_positive_map(\n                tokenized, tokens_positive)\n\n            caption_string_chunked.append(caption_string)\n            positive_map_label_to_token_chunked.append(\n                positive_map_label_to_token)\n            positive_map_chunked.append(positive_map)\n            entities_chunked.append(original_caption_chunked[i])\n\n        return positive_map_label_to_token_chunked, \\\n            caption_string_chunked, \\\n            positive_map_chunked, \\\n            entities_chunked\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, list]:\n        # TODO: Only open vocabulary tasks are supported for training now.\n        text_prompts = [\n            data_samples.text for data_samples in batch_data_samples\n        ]\n\n        gt_labels = [\n            data_samples.gt_instances.labels\n            for data_samples in batch_data_samples\n        ]\n\n        new_text_prompts = []\n        positive_maps = []\n        if len(set(text_prompts)) == 1:\n            # All the text prompts are the same,\n            # so there is no need to calculate them multiple times.\n            tokenized, caption_string, tokens_positive, _ = \\\n                self.get_tokens_and_prompts(\n                    text_prompts[0], True)\n            new_text_prompts = [caption_string] * len(batch_inputs)\n            for gt_label in gt_labels:\n                new_tokens_positive = [\n                    tokens_positive[label] for label in gt_label\n                ]\n                _, positive_map = self.get_positive_map(\n                    tokenized, new_tokens_positive)\n                positive_maps.append(positive_map)\n        else:\n            for text_prompt, gt_label in zip(text_prompts, gt_labels):\n                tokenized, caption_string, tokens_positive, _ = \\\n                    self.get_tokens_and_prompts(\n                        text_prompt, True)\n                new_tokens_positive = [\n                    tokens_positive[label] for label in gt_label\n                ]\n                _, positive_map = self.get_positive_map(\n                    tokenized, new_tokens_positive)\n                positive_maps.append(positive_map)\n                new_text_prompts.append(caption_string)\n\n        language_dict_features = self.language_model(new_text_prompts)\n        for i, data_samples in enumerate(batch_data_samples):\n            # .bool().float() is very important\n            positive_map = positive_maps[i].to(\n                batch_inputs.device).bool().float()\n            data_samples.gt_instances.positive_maps = positive_map\n\n        visual_features = self.extract_feat(batch_inputs)\n\n        losses = self.bbox_head.loss(visual_features, language_dict_features,\n                                     batch_data_samples)\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances'. And the ``pred_instances`` usually\n            contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - label_names (List[str]): Label names of bboxes.\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        text_prompts = []\n        enhanced_text_prompts = []\n        tokens_positives = []\n        for data_samples in batch_data_samples:\n            text_prompts.append(data_samples.text)\n            if 'caption_prompt' in data_samples:\n                enhanced_text_prompts.append(data_samples.caption_prompt)\n            else:\n                enhanced_text_prompts.append(None)\n            tokens_positives.append(data_samples.get('tokens_positive', None))\n\n        if 'custom_entities' in batch_data_samples[0]:\n            # Assuming that the `custom_entities` flag\n            # inside a batch is always the same. For single image inference\n            custom_entities = batch_data_samples[0].custom_entities\n        else:\n            custom_entities = False\n\n        if len(set(text_prompts)) == 1:\n            # All the text prompts are the same,\n            # so there is no need to calculate them multiple times.\n            _positive_maps_and_prompts = [\n                self.get_tokens_positive_and_prompts(\n                    text_prompts[0], custom_entities, enhanced_text_prompts[0],\n                    tokens_positives[0])\n            ] * len(batch_inputs)\n        else:\n            _positive_maps_and_prompts = [\n                self.get_tokens_positive_and_prompts(text_prompt,\n                                                     custom_entities,\n                                                     enhanced_text_prompt,\n                                                     tokens_positive)\n                for text_prompt, enhanced_text_prompt, tokens_positive in zip(\n                    text_prompts, enhanced_text_prompts, tokens_positives)\n            ]\n\n        token_positive_maps, text_prompts, _, entities = zip(\n            *_positive_maps_and_prompts)\n\n        visual_features = self.extract_feat(batch_inputs)\n\n        if isinstance(text_prompts[0], list):\n            # chunked text prompts, only bs=1 is supported\n            assert len(batch_inputs) == 1\n            count = 0\n            results_list = []\n\n            entities = [[item for lst in entities[0] for item in lst]]\n\n            for b in range(len(text_prompts[0])):\n                text_prompts_once = [text_prompts[0][b]]\n                token_positive_maps_once = token_positive_maps[0][b]\n                language_dict_features = self.language_model(text_prompts_once)\n                batch_data_samples[\n                    0].token_positive_map = token_positive_maps_once\n\n                pred_instances = self.bbox_head.predict(\n                    copy.deepcopy(visual_features),\n                    language_dict_features,\n                    batch_data_samples,\n                    rescale=rescale)[0]\n\n                if len(pred_instances) > 0:\n                    pred_instances.labels += count\n                count += len(token_positive_maps_once)\n                results_list.append(pred_instances)\n            results_list = [results_list[0].cat(results_list)]\n        else:\n            language_dict_features = self.language_model(list(text_prompts))\n\n            for i, data_samples in enumerate(batch_data_samples):\n                data_samples.token_positive_map = token_positive_maps[i]\n\n            results_list = self.bbox_head.predict(\n                visual_features,\n                language_dict_features,\n                batch_data_samples,\n                rescale=rescale)\n\n        for data_sample, pred_instances, entity in zip(batch_data_samples,\n                                                       results_list, entities):\n            if len(pred_instances) > 0:\n                label_names = []\n                for labels in pred_instances.labels:\n                    if labels >= len(entity):\n                        warnings.warn(\n                            'The unexpected output indicates an issue with '\n                            'named entity recognition. You can try '\n                            'setting custom_entities=True and running '\n                            'again to see if it helps.')\n                        label_names.append('unobject')\n                    else:\n                        label_names.append(entity[labels])\n                # for visualization\n                pred_instances.label_names = label_names\n            data_sample.pred_instances = pred_instances\n        return batch_data_samples\n"
  },
  {
    "path": "mmdet/models/detectors/grid_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass GridRCNN(TwoStageDetector):\n    \"\"\"Grid R-CNN.\n\n    This detector is the implementation of:\n    - Grid R-CNN (https://arxiv.org/abs/1811.12030)\n    - Grid R-CNN Plus: Faster and Better (https://arxiv.org/abs/1906.05688)\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 rpn_head: ConfigType,\n                 roi_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/grounding_dino.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport re\nimport warnings\nfrom typing import Dict, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.runner.amp import autocast\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList, SampleList\nfrom mmdet.utils import ConfigType\nfrom ..layers import SinePositionalEncoding\nfrom ..layers.transformer.grounding_dino_layers import (\n    GroundingDinoTransformerDecoder, GroundingDinoTransformerEncoder)\nfrom .dino import DINO\nfrom .glip import (create_positive_map, create_positive_map_label_to_token,\n                   run_ner)\n\n\ndef clean_label_name(name: str) -> str:\n    name = re.sub(r'\\(.*\\)', '', name)\n    name = re.sub(r'_', ' ', name)\n    name = re.sub(r'  ', ' ', name)\n    return name\n\n\ndef chunks(lst: list, n: int) -> list:\n    \"\"\"Yield successive n-sized chunks from lst.\"\"\"\n    all_ = []\n    for i in range(0, len(lst), n):\n        data_index = lst[i:i + n]\n        all_.append(data_index)\n    counter = 0\n    for i in all_:\n        counter += len(i)\n    assert (counter == len(lst))\n\n    return all_\n\n\n@MODELS.register_module()\nclass GroundingDINO(DINO):\n    \"\"\"Implementation of `Grounding DINO: Marrying DINO with Grounded Pre-\n    Training for Open-Set Object Detection.\n\n    <https://arxiv.org/abs/2303.05499>`_\n\n    Code is modified from the `official github repo\n    <https://github.com/IDEA-Research/GroundingDINO>`_.\n    \"\"\"\n\n    def __init__(self,\n                 language_model,\n                 *args,\n                 use_autocast=False,\n                 **kwargs) -> None:\n\n        self.language_model_cfg = language_model\n        self._special_tokens = '. '\n        self.use_autocast = use_autocast\n        super().__init__(*args, **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers except for backbone, neck and bbox_head.\"\"\"\n        self.positional_encoding = SinePositionalEncoding(\n            **self.positional_encoding)\n        self.encoder = GroundingDinoTransformerEncoder(**self.encoder)\n        self.decoder = GroundingDinoTransformerDecoder(**self.decoder)\n        self.embed_dims = self.encoder.embed_dims\n        self.query_embedding = nn.Embedding(self.num_queries, self.embed_dims)\n        num_feats = self.positional_encoding.num_feats\n        assert num_feats * 2 == self.embed_dims, \\\n            f'embed_dims should be exactly 2 times of num_feats. ' \\\n            f'Found {self.embed_dims} and {num_feats}.'\n\n        self.level_embed = nn.Parameter(\n            torch.Tensor(self.num_feature_levels, self.embed_dims))\n        self.memory_trans_fc = nn.Linear(self.embed_dims, self.embed_dims)\n        self.memory_trans_norm = nn.LayerNorm(self.embed_dims)\n\n        # text modules\n        self.language_model = MODELS.build(self.language_model_cfg)\n        self.text_feat_map = nn.Linear(\n            self.language_model.language_backbone.body.language_dim,\n            self.embed_dims,\n            bias=True)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights for Transformer and other components.\"\"\"\n        super().init_weights()\n        nn.init.constant_(self.text_feat_map.bias.data, 0)\n        nn.init.xavier_uniform_(self.text_feat_map.weight.data)\n\n    def to_enhance_text_prompts(self, original_caption, enhanced_text_prompts):\n        caption_string = ''\n        tokens_positive = []\n        for idx, word in enumerate(original_caption):\n            if word in enhanced_text_prompts:\n                enhanced_text_dict = enhanced_text_prompts[word]\n                if 'prefix' in enhanced_text_dict:\n                    caption_string += enhanced_text_dict['prefix']\n                start_i = len(caption_string)\n                if 'name' in enhanced_text_dict:\n                    caption_string += enhanced_text_dict['name']\n                else:\n                    caption_string += word\n                end_i = len(caption_string)\n                tokens_positive.append([[start_i, end_i]])\n\n                if 'suffix' in enhanced_text_dict:\n                    caption_string += enhanced_text_dict['suffix']\n            else:\n                tokens_positive.append(\n                    [[len(caption_string),\n                      len(caption_string) + len(word)]])\n                caption_string += word\n            caption_string += self._special_tokens\n        return caption_string, tokens_positive\n\n    def to_plain_text_prompts(self, original_caption):\n        caption_string = ''\n        tokens_positive = []\n        for idx, word in enumerate(original_caption):\n            tokens_positive.append(\n                [[len(caption_string),\n                  len(caption_string) + len(word)]])\n            caption_string += word\n            caption_string += self._special_tokens\n        return caption_string, tokens_positive\n\n    def get_tokens_and_prompts(\n        self,\n        original_caption: Union[str, list, tuple],\n        custom_entities: bool = False,\n        enhanced_text_prompts: Optional[ConfigType] = None\n    ) -> Tuple[dict, str, list]:\n        \"\"\"Get the tokens positive and prompts for the caption.\"\"\"\n        if isinstance(original_caption, (list, tuple)) or custom_entities:\n            if custom_entities and isinstance(original_caption, str):\n                original_caption = original_caption.strip(self._special_tokens)\n                original_caption = original_caption.split(self._special_tokens)\n                original_caption = list(\n                    filter(lambda x: len(x) > 0, original_caption))\n\n            original_caption = [clean_label_name(i) for i in original_caption]\n\n            if custom_entities and enhanced_text_prompts is not None:\n                caption_string, tokens_positive = self.to_enhance_text_prompts(\n                    original_caption, enhanced_text_prompts)\n            else:\n                caption_string, tokens_positive = self.to_plain_text_prompts(\n                    original_caption)\n\n            # NOTE: Tokenizer in Grounding DINO is different from\n            # that in GLIP. The tokenizer in GLIP will pad the\n            # caption_string to max_length, while the tokenizer\n            # in Grounding DINO will not.\n            tokenized = self.language_model.tokenizer(\n                [caption_string],\n                padding='max_length'\n                if self.language_model.pad_to_max else 'longest',\n                return_tensors='pt')\n            entities = original_caption\n        else:\n            if not original_caption.endswith('.'):\n                original_caption = original_caption + self._special_tokens\n            # NOTE: Tokenizer in Grounding DINO is different from\n            # that in GLIP. The tokenizer in GLIP will pad the\n            # caption_string to max_length, while the tokenizer\n            # in Grounding DINO will not.\n            tokenized = self.language_model.tokenizer(\n                [original_caption],\n                padding='max_length'\n                if self.language_model.pad_to_max else 'longest',\n                return_tensors='pt')\n            tokens_positive, noun_phrases = run_ner(original_caption)\n            entities = noun_phrases\n            caption_string = original_caption\n\n        return tokenized, caption_string, tokens_positive, entities\n\n    def get_positive_map(self, tokenized, tokens_positive):\n        positive_map = create_positive_map(\n            tokenized,\n            tokens_positive,\n            max_num_entities=self.bbox_head.cls_branches[\n                self.decoder.num_layers].max_text_len)\n        positive_map_label_to_token = create_positive_map_label_to_token(\n            positive_map, plus=1)\n        return positive_map_label_to_token, positive_map\n\n    def get_tokens_positive_and_prompts(\n        self,\n        original_caption: Union[str, list, tuple],\n        custom_entities: bool = False,\n        enhanced_text_prompt: Optional[ConfigType] = None,\n        tokens_positive: Optional[list] = None,\n    ) -> Tuple[dict, str, Tensor, list]:\n        \"\"\"Get the tokens positive and prompts for the caption.\n\n        Args:\n            original_caption (str): The original caption, e.g. 'bench . car .'\n            custom_entities (bool, optional): Whether to use custom entities.\n                If ``True``, the ``original_caption`` should be a list of\n                strings, each of which is a word. Defaults to False.\n\n        Returns:\n            Tuple[dict, str, dict, str]: The dict is a mapping from each entity\n            id, which is numbered from 1, to its positive token id.\n            The str represents the prompts.\n        \"\"\"\n        if tokens_positive is not None:\n            if tokens_positive == -1:\n                if not original_caption.endswith('.'):\n                    original_caption = original_caption + self._special_tokens\n                return None, original_caption, None, original_caption\n            else:\n                if not original_caption.endswith('.'):\n                    original_caption = original_caption + self._special_tokens\n                tokenized = self.language_model.tokenizer(\n                    [original_caption],\n                    padding='max_length'\n                    if self.language_model.pad_to_max else 'longest',\n                    return_tensors='pt')\n                positive_map_label_to_token, positive_map = \\\n                    self.get_positive_map(tokenized, tokens_positive)\n\n                entities = []\n                for token_positive in tokens_positive:\n                    instance_entities = []\n                    for t in token_positive:\n                        instance_entities.append(original_caption[t[0]:t[1]])\n                    entities.append(' / '.join(instance_entities))\n                return positive_map_label_to_token, original_caption, \\\n                    positive_map, entities\n\n        chunked_size = self.test_cfg.get('chunked_size', -1)\n        if not self.training and chunked_size > 0:\n            assert isinstance(original_caption,\n                              (list, tuple)) or custom_entities is True\n            all_output = self.get_tokens_positive_and_prompts_chunked(\n                original_caption, enhanced_text_prompt)\n            positive_map_label_to_token, \\\n                caption_string, \\\n                positive_map, \\\n                entities = all_output\n        else:\n            tokenized, caption_string, tokens_positive, entities = \\\n                self.get_tokens_and_prompts(\n                    original_caption, custom_entities, enhanced_text_prompt)\n            positive_map_label_to_token, positive_map = self.get_positive_map(\n                tokenized, tokens_positive)\n        return positive_map_label_to_token, caption_string, \\\n            positive_map, entities\n\n    def get_tokens_positive_and_prompts_chunked(\n            self,\n            original_caption: Union[list, tuple],\n            enhanced_text_prompts: Optional[ConfigType] = None):\n        chunked_size = self.test_cfg.get('chunked_size', -1)\n        original_caption = [clean_label_name(i) for i in original_caption]\n\n        original_caption_chunked = chunks(original_caption, chunked_size)\n        ids_chunked = chunks(\n            list(range(1,\n                       len(original_caption) + 1)), chunked_size)\n\n        positive_map_label_to_token_chunked = []\n        caption_string_chunked = []\n        positive_map_chunked = []\n        entities_chunked = []\n\n        for i in range(len(ids_chunked)):\n            if enhanced_text_prompts is not None:\n                caption_string, tokens_positive = self.to_enhance_text_prompts(\n                    original_caption_chunked[i], enhanced_text_prompts)\n            else:\n                caption_string, tokens_positive = self.to_plain_text_prompts(\n                    original_caption_chunked[i])\n            tokenized = self.language_model.tokenizer([caption_string],\n                                                      return_tensors='pt')\n            if tokenized.input_ids.shape[1] > self.language_model.max_tokens:\n                warnings.warn('Inputting a text that is too long will result '\n                              'in poor prediction performance. '\n                              'Please reduce the --chunked-size.')\n            positive_map_label_to_token, positive_map = self.get_positive_map(\n                tokenized, tokens_positive)\n\n            caption_string_chunked.append(caption_string)\n            positive_map_label_to_token_chunked.append(\n                positive_map_label_to_token)\n            positive_map_chunked.append(positive_map)\n            entities_chunked.append(original_caption_chunked[i])\n\n        return positive_map_label_to_token_chunked, \\\n            caption_string_chunked, \\\n            positive_map_chunked, \\\n            entities_chunked\n\n    def forward_transformer(\n        self,\n        img_feats: Tuple[Tensor],\n        text_dict: Dict,\n        batch_data_samples: OptSampleList = None,\n    ) -> Dict:\n        encoder_inputs_dict, decoder_inputs_dict = self.pre_transformer(\n            img_feats, batch_data_samples)\n\n        encoder_outputs_dict = self.forward_encoder(\n            **encoder_inputs_dict, text_dict=text_dict)\n\n        tmp_dec_in, head_inputs_dict = self.pre_decoder(\n            **encoder_outputs_dict, batch_data_samples=batch_data_samples)\n        decoder_inputs_dict.update(tmp_dec_in)\n\n        decoder_outputs_dict = self.forward_decoder(**decoder_inputs_dict)\n        head_inputs_dict.update(decoder_outputs_dict)\n        return head_inputs_dict\n\n    def forward_encoder(self, feat: Tensor, feat_mask: Tensor,\n                        feat_pos: Tensor, spatial_shapes: Tensor,\n                        level_start_index: Tensor, valid_ratios: Tensor,\n                        text_dict: Dict) -> Dict:\n        text_token_mask = text_dict['text_token_mask']\n        memory, memory_text = self.encoder(\n            query=feat,\n            query_pos=feat_pos,\n            key_padding_mask=feat_mask,  # for self_attn\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            # for text encoder\n            memory_text=text_dict['embedded'],\n            text_attention_mask=~text_token_mask,\n            position_ids=text_dict['position_ids'],\n            text_self_attention_masks=text_dict['masks'])\n        encoder_outputs_dict = dict(\n            memory=memory,\n            memory_mask=feat_mask,\n            spatial_shapes=spatial_shapes,\n            memory_text=memory_text,\n            text_token_mask=text_token_mask)\n        return encoder_outputs_dict\n\n    def pre_decoder(\n        self,\n        memory: Tensor,\n        memory_mask: Tensor,\n        spatial_shapes: Tensor,\n        memory_text: Tensor,\n        text_token_mask: Tensor,\n        batch_data_samples: OptSampleList = None,\n    ) -> Tuple[Dict]:\n        bs, _, c = memory.shape\n\n        output_memory, output_proposals = self.gen_encoder_output_proposals(\n            memory, memory_mask, spatial_shapes)\n\n        enc_outputs_class = self.bbox_head.cls_branches[\n            self.decoder.num_layers](output_memory, memory_text,\n                                     text_token_mask)\n        cls_out_features = self.bbox_head.cls_branches[\n            self.decoder.num_layers].max_text_len\n        enc_outputs_coord_unact = self.bbox_head.reg_branches[\n            self.decoder.num_layers](output_memory) + output_proposals\n\n        # NOTE The DINO selects top-k proposals according to scores of\n        # multi-class classification, while DeformDETR, where the input\n        # is `enc_outputs_class[..., 0]` selects according to scores of\n        # binary classification.\n        topk_indices = torch.topk(\n            enc_outputs_class.max(-1)[0], k=self.num_queries, dim=1)[1]\n\n        topk_score = torch.gather(\n            enc_outputs_class, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, cls_out_features))\n        topk_coords_unact = torch.gather(\n            enc_outputs_coord_unact, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, 4))\n        topk_coords = topk_coords_unact.sigmoid()\n        topk_coords_unact = topk_coords_unact.detach()\n\n        query = self.query_embedding.weight[:, None, :]\n        query = query.repeat(1, bs, 1).transpose(0, 1)\n        if self.training:\n            dn_label_query, dn_bbox_query, dn_mask, dn_meta = \\\n                self.dn_query_generator(batch_data_samples)\n            query = torch.cat([dn_label_query, query], dim=1)\n            reference_points = torch.cat([dn_bbox_query, topk_coords_unact],\n                                         dim=1)\n        else:\n            reference_points = topk_coords_unact\n            dn_mask, dn_meta = None, None\n        reference_points = reference_points.sigmoid()\n\n        decoder_inputs_dict = dict(\n            query=query,\n            memory=memory,\n            reference_points=reference_points,\n            dn_mask=dn_mask,\n            memory_text=memory_text,\n            text_attention_mask=~text_token_mask,\n        )\n        # NOTE DINO calculates encoder losses on scores and coordinates\n        # of selected top-k encoder queries, while DeformDETR is of all\n        # encoder queries.\n        head_inputs_dict = dict(\n            enc_outputs_class=topk_score,\n            enc_outputs_coord=topk_coords,\n            dn_meta=dn_meta) if self.training else dict()\n        # append text_feats to head_inputs_dict\n        head_inputs_dict['memory_text'] = memory_text\n        head_inputs_dict['text_token_mask'] = text_token_mask\n        return decoder_inputs_dict, head_inputs_dict\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, list]:\n        text_prompts = [\n            data_samples.text for data_samples in batch_data_samples\n        ]\n\n        gt_labels = [\n            data_samples.gt_instances.labels\n            for data_samples in batch_data_samples\n        ]\n\n        if 'tokens_positive' in batch_data_samples[0]:\n            tokens_positive = [\n                data_samples.tokens_positive\n                for data_samples in batch_data_samples\n            ]\n            positive_maps = []\n            for token_positive, text_prompt, gt_label in zip(\n                    tokens_positive, text_prompts, gt_labels):\n                tokenized = self.language_model.tokenizer(\n                    [text_prompt],\n                    padding='max_length'\n                    if self.language_model.pad_to_max else 'longest',\n                    return_tensors='pt')\n                new_tokens_positive = [\n                    token_positive[label.item()] for label in gt_label\n                ]\n                _, positive_map = self.get_positive_map(\n                    tokenized, new_tokens_positive)\n                positive_maps.append(positive_map)\n            new_text_prompts = text_prompts\n        else:\n            new_text_prompts = []\n            positive_maps = []\n            if len(set(text_prompts)) == 1:\n                # All the text prompts are the same,\n                # so there is no need to calculate them multiple times.\n                tokenized, caption_string, tokens_positive, _ = \\\n                    self.get_tokens_and_prompts(\n                        text_prompts[0], True)\n                new_text_prompts = [caption_string] * len(batch_inputs)\n                for gt_label in gt_labels:\n                    new_tokens_positive = [\n                        tokens_positive[label] for label in gt_label\n                    ]\n                    _, positive_map = self.get_positive_map(\n                        tokenized, new_tokens_positive)\n                    positive_maps.append(positive_map)\n            else:\n                for text_prompt, gt_label in zip(text_prompts, gt_labels):\n                    tokenized, caption_string, tokens_positive, _ = \\\n                        self.get_tokens_and_prompts(\n                            text_prompt, True)\n                    new_tokens_positive = [\n                        tokens_positive[label] for label in gt_label\n                    ]\n                    _, positive_map = self.get_positive_map(\n                        tokenized, new_tokens_positive)\n                    positive_maps.append(positive_map)\n                    new_text_prompts.append(caption_string)\n\n        text_dict = self.language_model(new_text_prompts)\n        if self.text_feat_map is not None:\n            text_dict['embedded'] = self.text_feat_map(text_dict['embedded'])\n\n        for i, data_samples in enumerate(batch_data_samples):\n            positive_map = positive_maps[i].to(\n                batch_inputs.device).bool().float()\n            text_token_mask = text_dict['text_token_mask'][i]\n            data_samples.gt_instances.positive_maps = positive_map\n            data_samples.gt_instances.text_token_mask = \\\n                text_token_mask.unsqueeze(0).repeat(\n                    len(positive_map), 1)\n        if self.use_autocast:\n            with autocast(enabled=True):\n                visual_features = self.extract_feat(batch_inputs)\n        else:\n            visual_features = self.extract_feat(batch_inputs)\n        head_inputs_dict = self.forward_transformer(visual_features, text_dict,\n                                                    batch_data_samples)\n\n        losses = self.bbox_head.loss(\n            **head_inputs_dict, batch_data_samples=batch_data_samples)\n        return losses\n\n    def predict(self, batch_inputs, batch_data_samples, rescale: bool = True):\n        text_prompts = []\n        enhanced_text_prompts = []\n        tokens_positives = []\n        for data_samples in batch_data_samples:\n            text_prompts.append(data_samples.text)\n            if 'caption_prompt' in data_samples:\n                enhanced_text_prompts.append(data_samples.caption_prompt)\n            else:\n                enhanced_text_prompts.append(None)\n            tokens_positives.append(data_samples.get('tokens_positive', None))\n\n        if 'custom_entities' in batch_data_samples[0]:\n            # Assuming that the `custom_entities` flag\n            # inside a batch is always the same. For single image inference\n            custom_entities = batch_data_samples[0].custom_entities\n        else:\n            custom_entities = False\n        if len(text_prompts) == 1:\n            # All the text prompts are the same,\n            # so there is no need to calculate them multiple times.\n            _positive_maps_and_prompts = [\n                self.get_tokens_positive_and_prompts(\n                    text_prompts[0], custom_entities, enhanced_text_prompts[0],\n                    tokens_positives[0])\n            ] * len(batch_inputs)\n        else:\n            _positive_maps_and_prompts = [\n                self.get_tokens_positive_and_prompts(text_prompt,\n                                                     custom_entities,\n                                                     enhanced_text_prompt,\n                                                     tokens_positive)\n                for text_prompt, enhanced_text_prompt, tokens_positive in zip(\n                    text_prompts, enhanced_text_prompts, tokens_positives)\n            ]\n        token_positive_maps, text_prompts, _, entities = zip(\n            *_positive_maps_and_prompts)\n\n        # image feature extraction\n        visual_feats = self.extract_feat(batch_inputs)\n\n        if isinstance(text_prompts[0], list):\n            # chunked text prompts, only bs=1 is supported\n            assert len(batch_inputs) == 1\n            count = 0\n            results_list = []\n\n            entities = [[item for lst in entities[0] for item in lst]]\n\n            for b in range(len(text_prompts[0])):\n                text_prompts_once = [text_prompts[0][b]]\n                token_positive_maps_once = token_positive_maps[0][b]\n                text_dict = self.language_model(text_prompts_once)\n                # text feature map layer\n                if self.text_feat_map is not None:\n                    text_dict['embedded'] = self.text_feat_map(\n                        text_dict['embedded'])\n\n                batch_data_samples[\n                    0].token_positive_map = token_positive_maps_once\n\n                head_inputs_dict = self.forward_transformer(\n                    copy.deepcopy(visual_feats), text_dict, batch_data_samples)\n                pred_instances = self.bbox_head.predict(\n                    **head_inputs_dict,\n                    rescale=rescale,\n                    batch_data_samples=batch_data_samples)[0]\n\n                if len(pred_instances) > 0:\n                    pred_instances.labels += count\n                count += len(token_positive_maps_once)\n                results_list.append(pred_instances)\n            results_list = [results_list[0].cat(results_list)]\n            is_rec_tasks = [False] * len(results_list)\n        else:\n            # extract text feats\n            text_dict = self.language_model(list(text_prompts))\n            # text feature map layer\n            if self.text_feat_map is not None:\n                text_dict['embedded'] = self.text_feat_map(\n                    text_dict['embedded'])\n\n            is_rec_tasks = []\n            for i, data_samples in enumerate(batch_data_samples):\n                if token_positive_maps[i] is not None:\n                    is_rec_tasks.append(False)\n                else:\n                    is_rec_tasks.append(True)\n                data_samples.token_positive_map = token_positive_maps[i]\n\n            head_inputs_dict = self.forward_transformer(\n                visual_feats, text_dict, batch_data_samples)\n            results_list = self.bbox_head.predict(\n                **head_inputs_dict,\n                rescale=rescale,\n                batch_data_samples=batch_data_samples)\n\n        for data_sample, pred_instances, entity, is_rec_task in zip(\n                batch_data_samples, results_list, entities, is_rec_tasks):\n            if len(pred_instances) > 0:\n                label_names = []\n                for labels in pred_instances.labels:\n                    if is_rec_task:\n                        label_names.append(entity)\n                        continue\n                    if labels >= len(entity):\n                        warnings.warn(\n                            'The unexpected output indicates an issue with '\n                            'named entity recognition. You can try '\n                            'setting custom_entities=True and running '\n                            'again to see if it helps.')\n                        label_names.append('unobject')\n                    else:\n                        label_names.append(entity[labels])\n                # for visualization\n                pred_instances.label_names = label_names\n            data_sample.pred_instances = pred_instances\n        return batch_data_samples\n"
  },
  {
    "path": "mmdet/models/detectors/htc.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom .cascade_rcnn import CascadeRCNN\n\n\n@MODELS.register_module()\nclass HybridTaskCascade(CascadeRCNN):\n    \"\"\"Implementation of `HTC <https://arxiv.org/abs/1901.07518>`_\"\"\"\n\n    def __init__(self, **kwargs) -> None:\n        super().__init__(**kwargs)\n\n    @property\n    def with_semantic(self) -> bool:\n        \"\"\"bool: whether the detector has a semantic head\"\"\"\n        return self.roi_head.with_semantic\n"
  },
  {
    "path": "mmdet/models/detectors/kd_one_stage.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom pathlib import Path\nfrom typing import Any, Optional, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.config import Config\nfrom mmengine.runner import load_checkpoint\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass KnowledgeDistillationSingleStageDetector(SingleStageDetector):\n    r\"\"\"Implementation of `Distilling the Knowledge in a Neural Network.\n    <https://arxiv.org/abs/1503.02531>`_.\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        teacher_config (:obj:`ConfigDict` | dict | str | Path): Config file\n            path or the config object of teacher model.\n        teacher_ckpt (str, optional): Checkpoint path of teacher model.\n            If left as None, the model will not load any weights.\n            Defaults to True.\n        eval_teacher (bool): Set the train mode for teacher.\n            Defaults to True.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of ATSS. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of ATSS. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(\n        self,\n        backbone: ConfigType,\n        neck: ConfigType,\n        bbox_head: ConfigType,\n        teacher_config: Union[ConfigType, str, Path],\n        teacher_ckpt: Optional[str] = None,\n        eval_teacher: bool = True,\n        train_cfg: OptConfigType = None,\n        test_cfg: OptConfigType = None,\n        data_preprocessor: OptConfigType = None,\n    ) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor)\n        self.eval_teacher = eval_teacher\n        # Build teacher model\n        if isinstance(teacher_config, (str, Path)):\n            teacher_config = Config.fromfile(teacher_config)\n        self.teacher_model = MODELS.build(teacher_config['model'])\n        if teacher_ckpt is not None:\n            load_checkpoint(\n                self.teacher_model, teacher_ckpt, map_location='cpu')\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n        with torch.no_grad():\n            teacher_x = self.teacher_model.extract_feat(batch_inputs)\n            out_teacher = self.teacher_model.bbox_head(teacher_x)\n        losses = self.bbox_head.loss(x, out_teacher, batch_data_samples)\n        return losses\n\n    def cuda(self, device: Optional[str] = None) -> nn.Module:\n        \"\"\"Since teacher_model is registered as a plain object, it is necessary\n        to put the teacher model to cuda when calling ``cuda`` function.\"\"\"\n        self.teacher_model.cuda(device=device)\n        return super().cuda(device=device)\n\n    def to(self, device: Optional[str] = None) -> nn.Module:\n        \"\"\"Since teacher_model is registered as a plain object, it is necessary\n        to put the teacher model to other device when calling ``to``\n        function.\"\"\"\n        self.teacher_model.to(device=device)\n        return super().to(device=device)\n\n    def train(self, mode: bool = True) -> None:\n        \"\"\"Set the same train mode for teacher and student model.\"\"\"\n        if self.eval_teacher:\n            self.teacher_model.train(False)\n        else:\n            self.teacher_model.train(mode)\n        super().train(mode)\n\n    def __setattr__(self, name: str, value: Any) -> None:\n        \"\"\"Set attribute, i.e. self.name = value\n\n        This reloading prevent the teacher model from being registered as a\n        nn.Module. The teacher module is registered as a plain object, so that\n        the teacher parameters will not show up when calling\n        ``self.parameters``, ``self.modules``, ``self.children`` methods.\n        \"\"\"\n        if name == 'teacher_model':\n            object.__setattr__(self, name, value)\n        else:\n            super().__setattr__(name, value)\n"
  },
  {
    "path": "mmdet/models/detectors/lad.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.runner import load_checkpoint\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType\nfrom ..utils.misc import unpack_gt_instances\nfrom .kd_one_stage import KnowledgeDistillationSingleStageDetector\n\n\n@MODELS.register_module()\nclass LAD(KnowledgeDistillationSingleStageDetector):\n    \"\"\"Implementation of `LAD <https://arxiv.org/pdf/2108.10520.pdf>`_.\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 teacher_backbone: ConfigType,\n                 teacher_neck: ConfigType,\n                 teacher_bbox_head: ConfigType,\n                 teacher_ckpt: Optional[str] = None,\n                 eval_teacher: bool = True,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None) -> None:\n        super(KnowledgeDistillationSingleStageDetector, self).__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor)\n        self.eval_teacher = eval_teacher\n        self.teacher_model = nn.Module()\n        self.teacher_model.backbone = MODELS.build(teacher_backbone)\n        if teacher_neck is not None:\n            self.teacher_model.neck = MODELS.build(teacher_neck)\n        teacher_bbox_head.update(train_cfg=train_cfg)\n        teacher_bbox_head.update(test_cfg=test_cfg)\n        self.teacher_model.bbox_head = MODELS.build(teacher_bbox_head)\n        if teacher_ckpt is not None:\n            load_checkpoint(\n                self.teacher_model, teacher_ckpt, map_location='cpu')\n\n    @property\n    def with_teacher_neck(self) -> bool:\n        \"\"\"bool: whether the detector has a teacher_neck\"\"\"\n        return hasattr(self.teacher_model, 'neck') and \\\n            self.teacher_model.neck is not None\n\n    def extract_teacher_feat(self, batch_inputs: Tensor) -> Tensor:\n        \"\"\"Directly extract teacher features from the backbone+neck.\"\"\"\n        x = self.teacher_model.backbone(batch_inputs)\n        if self.with_teacher_neck:\n            x = self.teacher_model.neck(x)\n        return x\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n        # get label assignment from the teacher\n        with torch.no_grad():\n            x_teacher = self.extract_teacher_feat(batch_inputs)\n            outs_teacher = self.teacher_model.bbox_head(x_teacher)\n            label_assignment_results = \\\n                self.teacher_model.bbox_head.get_label_assignment(\n                    *outs_teacher, batch_gt_instances, batch_img_metas,\n                    batch_gt_instances_ignore)\n\n        # the student use the label assignment from the teacher to learn\n        x = self.extract_feat(batch_inputs)\n        losses = self.bbox_head.loss(x, label_assignment_results,\n                                     batch_data_samples)\n        return losses\n"
  },
  {
    "path": "mmdet/models/detectors/mask2former.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .maskformer import MaskFormer\n\n\n@MODELS.register_module()\nclass Mask2Former(MaskFormer):\n    r\"\"\"Implementation of `Masked-attention Mask\n    Transformer for Universal Image Segmentation\n    <https://arxiv.org/pdf/2112.01527>`_.\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 panoptic_head: OptConfigType = None,\n                 panoptic_fusion_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            panoptic_head=panoptic_head,\n            panoptic_fusion_head=panoptic_fusion_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/mask_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import ConfigDict\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass MaskRCNN(TwoStageDetector):\n    \"\"\"Implementation of `Mask R-CNN <https://arxiv.org/abs/1703.06870>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigDict,\n                 rpn_head: ConfigDict,\n                 roi_head: ConfigDict,\n                 train_cfg: ConfigDict,\n                 test_cfg: ConfigDict,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            init_cfg=init_cfg,\n            data_preprocessor=data_preprocessor)\n"
  },
  {
    "path": "mmdet/models/detectors/mask_scoring_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass MaskScoringRCNN(TwoStageDetector):\n    \"\"\"Mask Scoring RCNN.\n\n    https://arxiv.org/abs/1903.00241\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 rpn_head: ConfigType,\n                 roi_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/maskformer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Tuple\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass MaskFormer(SingleStageDetector):\n    r\"\"\"Implementation of `Per-Pixel Classification is\n    NOT All You Need for Semantic Segmentation\n    <https://arxiv.org/pdf/2107.06278>`_.\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 panoptic_head: OptConfigType = None,\n                 panoptic_fusion_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super(SingleStageDetector, self).__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.backbone = MODELS.build(backbone)\n        if neck is not None:\n            self.neck = MODELS.build(neck)\n\n        panoptic_head_ = panoptic_head.deepcopy()\n        panoptic_head_.update(train_cfg=train_cfg)\n        panoptic_head_.update(test_cfg=test_cfg)\n        self.panoptic_head = MODELS.build(panoptic_head_)\n\n        panoptic_fusion_head_ = panoptic_fusion_head.deepcopy()\n        panoptic_fusion_head_.update(test_cfg=test_cfg)\n        self.panoptic_fusion_head = MODELS.build(panoptic_fusion_head_)\n\n        self.num_things_classes = self.panoptic_head.num_things_classes\n        self.num_stuff_classes = self.panoptic_head.num_stuff_classes\n        self.num_classes = self.panoptic_head.num_classes\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Dict[str, Tensor]:\n        \"\"\"\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n        losses = self.panoptic_head.loss(x, batch_data_samples)\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances' and `pred_panoptic_seg`. And the\n            ``pred_instances`` usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n\n            And the ``pred_panoptic_seg`` contains the following key\n\n                - sem_seg (Tensor): panoptic segmentation mask, has a\n                    shape (1, h, w).\n        \"\"\"\n        feats = self.extract_feat(batch_inputs)\n        mask_cls_results, mask_pred_results = self.panoptic_head.predict(\n            feats, batch_data_samples)\n        results_list = self.panoptic_fusion_head.predict(\n            mask_cls_results,\n            mask_pred_results,\n            batch_data_samples,\n            rescale=rescale)\n        results = self.add_pred_to_datasample(batch_data_samples, results_list)\n\n        return results\n\n    def add_pred_to_datasample(self, data_samples: SampleList,\n                               results_list: List[dict]) -> SampleList:\n        \"\"\"Add predictions to `DetDataSample`.\n\n        Args:\n            data_samples (list[:obj:`DetDataSample`], optional): A batch of\n                data samples that contain annotations and predictions.\n            results_list (List[dict]): Instance segmentation, segmantic\n                segmentation and panoptic segmentation results.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances' and `pred_panoptic_seg`. And the\n            ``pred_instances`` usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n\n            And the ``pred_panoptic_seg`` contains the following key\n\n                - sem_seg (Tensor): panoptic segmentation mask, has a\n                    shape (1, h, w).\n        \"\"\"\n        for data_sample, pred_results in zip(data_samples, results_list):\n            if 'pan_results' in pred_results:\n                data_sample.pred_panoptic_seg = pred_results['pan_results']\n\n            if 'ins_results' in pred_results:\n                data_sample.pred_instances = pred_results['ins_results']\n\n            assert 'sem_results' not in pred_results, 'segmantic ' \\\n                'segmentation results are not supported yet.'\n\n        return data_samples\n\n    def _forward(self, batch_inputs: Tensor,\n                 batch_data_samples: SampleList) -> Tuple[List[Tensor]]:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n         Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            tuple[List[Tensor]]: A tuple of features from ``panoptic_head``\n            forward.\n        \"\"\"\n        feats = self.extract_feat(batch_inputs)\n        results = self.panoptic_head.forward(feats, batch_data_samples)\n        return results\n"
  },
  {
    "path": "mmdet/models/detectors/nasfcos.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass NASFCOS(SingleStageDetector):\n    \"\"\"Implementation of `NAS-FCOS: Fast Neural Architecture Search for Object\n    Detection. <https://arxiv.org/abs/1906.0442>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of NASFCOS. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of NASFCOS. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/paa.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass PAA(SingleStageDetector):\n    \"\"\"Implementation of `PAA <https://arxiv.org/pdf/2007.08103.pdf>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of PAA. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of PAA. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/panoptic_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .panoptic_two_stage_segmentor import TwoStagePanopticSegmentor\n\n\n@MODELS.register_module()\nclass PanopticFPN(TwoStagePanopticSegmentor):\n    r\"\"\"Implementation of `Panoptic feature pyramid\n    networks <https://arxiv.org/pdf/1901.02446>`_\"\"\"\n\n    def __init__(\n            self,\n            backbone: ConfigType,\n            neck: OptConfigType = None,\n            rpn_head: OptConfigType = None,\n            roi_head: OptConfigType = None,\n            train_cfg: OptConfigType = None,\n            test_cfg: OptConfigType = None,\n            data_preprocessor: OptConfigType = None,\n            init_cfg: OptMultiConfig = None,\n            # for panoptic segmentation\n            semantic_head: OptConfigType = None,\n            panoptic_fusion_head: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg,\n            semantic_head=semantic_head,\n            panoptic_fusion_head=panoptic_fusion_head)\n"
  },
  {
    "path": "mmdet/models/detectors/panoptic_two_stage_segmentor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List\n\nimport torch\nfrom mmengine.structures import PixelData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass TwoStagePanopticSegmentor(TwoStageDetector):\n    \"\"\"Base class of Two-stage Panoptic Segmentor.\n\n    As well as the components in TwoStageDetector, Panoptic Segmentor has extra\n    semantic_head and panoptic_fusion_head.\n    \"\"\"\n\n    def __init__(\n            self,\n            backbone: ConfigType,\n            neck: OptConfigType = None,\n            rpn_head: OptConfigType = None,\n            roi_head: OptConfigType = None,\n            train_cfg: OptConfigType = None,\n            test_cfg: OptConfigType = None,\n            data_preprocessor: OptConfigType = None,\n            init_cfg: OptMultiConfig = None,\n            # for panoptic segmentation\n            semantic_head: OptConfigType = None,\n            panoptic_fusion_head: OptConfigType = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n\n        if semantic_head is not None:\n            self.semantic_head = MODELS.build(semantic_head)\n\n        if panoptic_fusion_head is not None:\n            panoptic_cfg = test_cfg.panoptic if test_cfg is not None else None\n            panoptic_fusion_head_ = panoptic_fusion_head.deepcopy()\n            panoptic_fusion_head_.update(test_cfg=panoptic_cfg)\n            self.panoptic_fusion_head = MODELS.build(panoptic_fusion_head_)\n\n            self.num_things_classes = self.panoptic_fusion_head.\\\n                num_things_classes\n            self.num_stuff_classes = self.panoptic_fusion_head.\\\n                num_stuff_classes\n            self.num_classes = self.panoptic_fusion_head.num_classes\n\n    @property\n    def with_semantic_head(self) -> bool:\n        \"\"\"bool: whether the detector has semantic head\"\"\"\n        return hasattr(self,\n                       'semantic_head') and self.semantic_head is not None\n\n    @property\n    def with_panoptic_fusion_head(self) -> bool:\n        \"\"\"bool: whether the detector has panoptic fusion head\"\"\"\n        return hasattr(self, 'panoptic_fusion_head') and \\\n            self.panoptic_fusion_head is not None\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n\n        losses = dict()\n\n        # RPN forward and loss\n        if self.with_rpn:\n            proposal_cfg = self.train_cfg.get('rpn_proposal',\n                                              self.test_cfg.rpn)\n            rpn_data_samples = copy.deepcopy(batch_data_samples)\n            # set cat_id of gt_labels to 0 in RPN\n            for data_sample in rpn_data_samples:\n                data_sample.gt_instances.labels = \\\n                    torch.zeros_like(data_sample.gt_instances.labels)\n\n            rpn_losses, rpn_results_list = self.rpn_head.loss_and_predict(\n                x, rpn_data_samples, proposal_cfg=proposal_cfg)\n            # avoid get same name with roi_head loss\n            keys = rpn_losses.keys()\n            for key in list(keys):\n                if 'loss' in key and 'rpn' not in key:\n                    rpn_losses[f'rpn_{key}'] = rpn_losses.pop(key)\n            losses.update(rpn_losses)\n        else:\n            # TODO: Not support currently, should have a check at Fast R-CNN\n            assert batch_data_samples[0].get('proposals', None) is not None\n            # use pre-defined proposals in InstanceData for the second stage\n            # to extract ROI features.\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        roi_losses = self.roi_head.loss(x, rpn_results_list,\n                                        batch_data_samples)\n        losses.update(roi_losses)\n\n        semantic_loss = self.semantic_head.loss(x, batch_data_samples)\n        losses.update(semantic_loss)\n\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            List[:obj:`DetDataSample`]: Return the packed panoptic segmentation\n                results of input images. Each DetDataSample usually contains\n                'pred_panoptic_seg'. And the 'pred_panoptic_seg' has a key\n                ``sem_seg``, which is a tensor of shape (1, h, w).\n        \"\"\"\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        x = self.extract_feat(batch_inputs)\n\n        # If there are no pre-defined proposals, use RPN to get proposals\n        if batch_data_samples[0].get('proposals', None) is None:\n            rpn_results_list = self.rpn_head.predict(\n                x, batch_data_samples, rescale=False)\n        else:\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        results_list = self.roi_head.predict(\n            x, rpn_results_list, batch_data_samples, rescale=rescale)\n\n        seg_preds = self.semantic_head.predict(x, batch_img_metas, rescale)\n\n        results_list = self.panoptic_fusion_head.predict(\n            results_list, seg_preds)\n\n        batch_data_samples = self.add_pred_to_datasample(\n            batch_data_samples, results_list)\n        return batch_data_samples\n\n    # TODO the code has not been verified and needs to be refactored later.\n    def _forward(self, batch_inputs: Tensor,\n                 batch_data_samples: SampleList) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n\n        Returns:\n            tuple: A tuple of features from ``rpn_head``, ``roi_head`` and\n                ``semantic_head`` forward.\n        \"\"\"\n        results = ()\n        x = self.extract_feat(batch_inputs)\n        rpn_outs = self.rpn_head.forward(x)\n        results = results + (rpn_outs)\n\n        # If there are no pre-defined proposals, use RPN to get proposals\n        if batch_data_samples[0].get('proposals', None) is None:\n            batch_img_metas = [\n                data_samples.metainfo for data_samples in batch_data_samples\n            ]\n            rpn_results_list = self.rpn_head.predict_by_feat(\n                *rpn_outs, batch_img_metas=batch_img_metas, rescale=False)\n        else:\n            # TODO: Not checked currently.\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        # roi_head\n        roi_outs = self.roi_head(x, rpn_results_list)\n        results = results + (roi_outs)\n\n        # semantic_head\n        sem_outs = self.semantic_head.forward(x)\n        results = results + (sem_outs['seg_preds'], )\n\n        return results\n\n    def add_pred_to_datasample(self, data_samples: SampleList,\n                               results_list: List[PixelData]) -> SampleList:\n        \"\"\"Add predictions to `DetDataSample`.\n\n        Args:\n            data_samples (list[:obj:`DetDataSample`]): The\n                annotation data of every samples.\n            results_list (List[PixelData]): Panoptic segmentation results of\n                each image.\n\n        Returns:\n            List[:obj:`DetDataSample`]: Return the packed panoptic segmentation\n                results of input images. Each DetDataSample usually contains\n                'pred_panoptic_seg'. And the 'pred_panoptic_seg' has a key\n                ``sem_seg``, which is a tensor of shape (1, h, w).\n        \"\"\"\n\n        for data_sample, pred_panoptic_seg in zip(data_samples, results_list):\n            data_sample.pred_panoptic_seg = pred_panoptic_seg\n        return data_samples\n"
  },
  {
    "path": "mmdet/models/detectors/point_rend.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.config import ConfigDict\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass PointRend(TwoStageDetector):\n    \"\"\"PointRend: Image Segmentation as Rendering\n\n    This detector is the implementation of\n    `PointRend <https://arxiv.org/abs/1912.08193>`_.\n\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigDict,\n                 rpn_head: ConfigDict,\n                 roi_head: ConfigDict,\n                 train_cfg: ConfigDict,\n                 test_cfg: ConfigDict,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            init_cfg=init_cfg,\n            data_preprocessor=data_preprocessor)\n"
  },
  {
    "path": "mmdet/models/detectors/queryinst.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .sparse_rcnn import SparseRCNN\n\n\n@MODELS.register_module()\nclass QueryInst(SparseRCNN):\n    r\"\"\"Implementation of\n    `Instances as Queries <http://arxiv.org/abs/2105.01928>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 rpn_head: ConfigType,\n                 roi_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/reppoints_detector.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass RepPointsDetector(SingleStageDetector):\n    \"\"\"RepPoints: Point Set Representation for Object Detection.\n\n        This detector is the implementation of:\n        - RepPoints detector (https://arxiv.org/pdf/1904.11490)\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/retinanet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass RetinaNet(SingleStageDetector):\n    \"\"\"Implementation of `RetinaNet <https://arxiv.org/abs/1708.02002>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/rpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport warnings\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass RPN(SingleStageDetector):\n    \"\"\"Implementation of Region Proposal Network.\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 rpn_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        super(SingleStageDetector, self).__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.backbone = MODELS.build(backbone)\n        self.neck = MODELS.build(neck) if neck is not None else None\n        rpn_train_cfg = train_cfg['rpn'] if train_cfg is not None else None\n        rpn_head_num_classes = rpn_head.get('num_classes', 1)\n        if rpn_head_num_classes != 1:\n            warnings.warn('The `num_classes` should be 1 in RPN, but get '\n                          f'{rpn_head_num_classes}, please set '\n                          'rpn_head.num_classes = 1 in your config file.')\n            rpn_head.update(num_classes=1)\n        rpn_head.update(train_cfg=rpn_train_cfg)\n        rpn_head.update(test_cfg=test_cfg['rpn'])\n        self.bbox_head = MODELS.build(rpn_head)\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n\n        # set cat_id of gt_labels to 0 in RPN\n        rpn_data_samples = copy.deepcopy(batch_data_samples)\n        for data_sample in rpn_data_samples:\n            data_sample.gt_instances.labels = \\\n                torch.zeros_like(data_sample.gt_instances.labels)\n\n        losses = self.bbox_head.loss(x, rpn_data_samples)\n        return losses\n"
  },
  {
    "path": "mmdet/models/detectors/rtmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom mmengine.dist import get_world_size\nfrom mmengine.logging import print_log\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass RTMDet(SingleStageDetector):\n    \"\"\"Implementation of RTMDet.\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of ATSS. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of ATSS. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n        use_syncbn (bool): Whether to use SyncBatchNorm. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 use_syncbn: bool = True) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n\n        # TODO: Waiting for mmengine support\n        if use_syncbn and get_world_size() > 1:\n            torch.nn.SyncBatchNorm.convert_sync_batchnorm(self)\n            print_log('Using SyncBatchNorm()', 'current')\n"
  },
  {
    "path": "mmdet/models/detectors/scnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom .cascade_rcnn import CascadeRCNN\n\n\n@MODELS.register_module()\nclass SCNet(CascadeRCNN):\n    \"\"\"Implementation of `SCNet <https://arxiv.org/abs/2012.10150>`_\"\"\"\n\n    def __init__(self, **kwargs) -> None:\n        super().__init__(**kwargs)\n"
  },
  {
    "path": "mmdet/models/detectors/semi_base.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Dict, List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.models.utils import (filter_gt_instances, rename_loss_dict,\n                                reweight_loss_dict)\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox_project\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .base import BaseDetector\n\n\n@MODELS.register_module()\nclass SemiBaseDetector(BaseDetector):\n    \"\"\"Base class for semi-supervised detectors.\n\n    Semi-supervised detectors typically consisting of a teacher model\n    updated by exponential moving average and a student model updated\n    by gradient descent.\n\n    Args:\n        detector (:obj:`ConfigDict` or dict): The detector config.\n        semi_train_cfg (:obj:`ConfigDict` or dict, optional):\n            The semi-supervised training config.\n        semi_test_cfg (:obj:`ConfigDict` or dict, optional):\n            The semi-supervised testing config.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: ConfigType,\n                 semi_train_cfg: OptConfigType = None,\n                 semi_test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.student = MODELS.build(detector)\n        self.teacher = MODELS.build(detector)\n        self.semi_train_cfg = semi_train_cfg\n        self.semi_test_cfg = semi_test_cfg\n        if self.semi_train_cfg.get('freeze_teacher', True) is True:\n            self.freeze(self.teacher)\n\n    @staticmethod\n    def freeze(model: nn.Module):\n        \"\"\"Freeze the model.\"\"\"\n        model.eval()\n        for param in model.parameters():\n            param.requires_grad = False\n\n    def loss(self, multi_batch_inputs: Dict[str, Tensor],\n             multi_batch_data_samples: Dict[str, SampleList]) -> dict:\n        \"\"\"Calculate losses from multi-branch inputs and data samples.\n\n        Args:\n            multi_batch_inputs (Dict[str, Tensor]): The dict of multi-branch\n                input images, each value with shape (N, C, H, W).\n                Each value should usually be mean centered and std scaled.\n            multi_batch_data_samples (Dict[str, List[:obj:`DetDataSample`]]):\n                The dict of multi-branch data samples.\n\n        Returns:\n            dict: A dictionary of loss components\n        \"\"\"\n        losses = dict()\n        losses.update(**self.loss_by_gt_instances(\n            multi_batch_inputs['sup'], multi_batch_data_samples['sup']))\n\n        origin_pseudo_data_samples, batch_info = self.get_pseudo_instances(\n            multi_batch_inputs['unsup_teacher'],\n            multi_batch_data_samples['unsup_teacher'])\n        multi_batch_data_samples[\n            'unsup_student'] = self.project_pseudo_instances(\n                origin_pseudo_data_samples,\n                multi_batch_data_samples['unsup_student'])\n        losses.update(**self.loss_by_pseudo_instances(\n            multi_batch_inputs['unsup_student'],\n            multi_batch_data_samples['unsup_student'], batch_info))\n        return losses\n\n    def loss_by_gt_instances(self, batch_inputs: Tensor,\n                             batch_data_samples: SampleList) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and ground-truth data\n        samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components\n        \"\"\"\n\n        losses = self.student.loss(batch_inputs, batch_data_samples)\n        sup_weight = self.semi_train_cfg.get('sup_weight', 1.)\n        return rename_loss_dict('sup_', reweight_loss_dict(losses, sup_weight))\n\n    def loss_by_pseudo_instances(self,\n                                 batch_inputs: Tensor,\n                                 batch_data_samples: SampleList,\n                                 batch_info: Optional[dict] = None) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and pseudo data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`,\n                which are `pseudo_instance` or `pseudo_panoptic_seg`\n                or `pseudo_sem_seg` in fact.\n            batch_info (dict): Batch information of teacher model\n                forward propagation process. Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components\n        \"\"\"\n        batch_data_samples = filter_gt_instances(\n            batch_data_samples, score_thr=self.semi_train_cfg.cls_pseudo_thr)\n        losses = self.student.loss(batch_inputs, batch_data_samples)\n        pseudo_instances_num = sum([\n            len(data_samples.gt_instances)\n            for data_samples in batch_data_samples\n        ])\n        unsup_weight = self.semi_train_cfg.get(\n            'unsup_weight', 1.) if pseudo_instances_num > 0 else 0.\n        return rename_loss_dict('unsup_',\n                                reweight_loss_dict(losses, unsup_weight))\n\n    @torch.no_grad()\n    def get_pseudo_instances(\n            self, batch_inputs: Tensor, batch_data_samples: SampleList\n    ) -> Tuple[SampleList, Optional[dict]]:\n        \"\"\"Get pseudo instances from teacher model.\"\"\"\n        self.teacher.eval()\n        results_list = self.teacher.predict(\n            batch_inputs, batch_data_samples, rescale=False)\n        batch_info = {}\n        for data_samples, results in zip(batch_data_samples, results_list):\n            data_samples.gt_instances = results.pred_instances\n            data_samples.gt_instances.bboxes = bbox_project(\n                data_samples.gt_instances.bboxes,\n                torch.from_numpy(data_samples.homography_matrix).inverse().to(\n                    self.data_preprocessor.device), data_samples.ori_shape)\n        return batch_data_samples, batch_info\n\n    def project_pseudo_instances(self, batch_pseudo_instances: SampleList,\n                                 batch_data_samples: SampleList) -> SampleList:\n        \"\"\"Project pseudo instances.\"\"\"\n        for pseudo_instances, data_samples in zip(batch_pseudo_instances,\n                                                  batch_data_samples):\n            data_samples.gt_instances = copy.deepcopy(\n                pseudo_instances.gt_instances)\n            data_samples.gt_instances.bboxes = bbox_project(\n                data_samples.gt_instances.bboxes,\n                torch.tensor(data_samples.homography_matrix).to(\n                    self.data_preprocessor.device), data_samples.img_shape)\n        wh_thr = self.semi_train_cfg.get('min_pseudo_bbox_wh', (1e-2, 1e-2))\n        return filter_gt_instances(batch_data_samples, wh_thr=wh_thr)\n\n    def predict(self, batch_inputs: Tensor,\n                batch_data_samples: SampleList) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Return the detection results of the\n            input images. The returns value is DetDataSample,\n            which usually contain 'pred_instances'. And the\n            ``pred_instances`` usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        if self.semi_test_cfg.get('predict_on', 'teacher') == 'teacher':\n            return self.teacher(\n                batch_inputs, batch_data_samples, mode='predict')\n        else:\n            return self.student(\n                batch_inputs, batch_data_samples, mode='predict')\n\n    def _forward(self, batch_inputs: Tensor,\n                 batch_data_samples: SampleList) -> SampleList:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n\n        Returns:\n            tuple: A tuple of features from ``rpn_head`` and ``roi_head``\n            forward.\n        \"\"\"\n        if self.semi_test_cfg.get('forward_on', 'teacher') == 'teacher':\n            return self.teacher(\n                batch_inputs, batch_data_samples, mode='tensor')\n        else:\n            return self.student(\n                batch_inputs, batch_data_samples, mode='tensor')\n\n    def extract_feat(self, batch_inputs: Tensor) -> Tuple[Tensor]:\n        \"\"\"Extract features.\n\n        Args:\n            batch_inputs (Tensor): Image tensor with shape (N, C, H ,W).\n\n        Returns:\n            tuple[Tensor]: Multi-level features that may have\n            different resolutions.\n        \"\"\"\n        if self.semi_test_cfg.get('extract_feat_on', 'teacher') == 'teacher':\n            return self.teacher.extract_feat(batch_inputs)\n        else:\n            return self.student.extract_feat(batch_inputs)\n\n    def _load_from_state_dict(self, state_dict: dict, prefix: str,\n                              local_metadata: dict, strict: bool,\n                              missing_keys: Union[List[str], str],\n                              unexpected_keys: Union[List[str], str],\n                              error_msgs: Union[List[str], str]) -> None:\n        \"\"\"Add teacher and student prefixes to model parameter names.\"\"\"\n        if not any([\n                'student' in key or 'teacher' in key\n                for key in state_dict.keys()\n        ]):\n            keys = list(state_dict.keys())\n            state_dict.update({'teacher.' + k: state_dict[k] for k in keys})\n            state_dict.update({'student.' + k: state_dict[k] for k in keys})\n            for k in keys:\n                state_dict.pop(k)\n        return super()._load_from_state_dict(\n            state_dict,\n            prefix,\n            local_metadata,\n            strict,\n            missing_keys,\n            unexpected_keys,\n            error_msgs,\n        )\n"
  },
  {
    "path": "mmdet/models/detectors/single_stage.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList, SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .base import BaseDetector\n\n\n@MODELS.register_module()\nclass SingleStageDetector(BaseDetector):\n    \"\"\"Base class for single-stage detectors.\n\n    Single-stage detectors directly and densely predict bounding boxes on the\n    output features of the backbone+neck.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 bbox_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.backbone = MODELS.build(backbone)\n        if neck is not None:\n            self.neck = MODELS.build(neck)\n        bbox_head.update(train_cfg=train_cfg)\n        bbox_head.update(test_cfg=test_cfg)\n        self.bbox_head = MODELS.build(bbox_head)\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n    def _load_from_state_dict(self, state_dict: dict, prefix: str,\n                              local_metadata: dict, strict: bool,\n                              missing_keys: Union[List[str], str],\n                              unexpected_keys: Union[List[str], str],\n                              error_msgs: Union[List[str], str]) -> None:\n        \"\"\"Exchange bbox_head key to rpn_head key when loading two-stage\n        weights into single-stage model.\"\"\"\n        bbox_head_prefix = prefix + '.bbox_head' if prefix else 'bbox_head'\n        bbox_head_keys = [\n            k for k in state_dict.keys() if k.startswith(bbox_head_prefix)\n        ]\n        rpn_head_prefix = prefix + '.rpn_head' if prefix else 'rpn_head'\n        rpn_head_keys = [\n            k for k in state_dict.keys() if k.startswith(rpn_head_prefix)\n        ]\n        if len(bbox_head_keys) == 0 and len(rpn_head_keys) != 0:\n            for rpn_head_key in rpn_head_keys:\n                bbox_head_key = bbox_head_prefix + \\\n                                rpn_head_key[len(rpn_head_prefix):]\n                state_dict[bbox_head_key] = state_dict.pop(rpn_head_key)\n        super()._load_from_state_dict(state_dict, prefix, local_metadata,\n                                      strict, missing_keys, unexpected_keys,\n                                      error_msgs)\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, list]:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n        losses = self.bbox_head.loss(x, batch_data_samples)\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances'. And the ``pred_instances`` usually\n            contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n        results_list = self.bbox_head.predict(\n            x, batch_data_samples, rescale=rescale)\n        batch_data_samples = self.add_pred_to_datasample(\n            batch_data_samples, results_list)\n        return batch_data_samples\n\n    def _forward(\n            self,\n            batch_inputs: Tensor,\n            batch_data_samples: OptSampleList = None) -> Tuple[List[Tensor]]:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n         Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n\n        Returns:\n            tuple[list]: A tuple of features from ``bbox_head`` forward.\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n        results = self.bbox_head.forward(x)\n        return results\n\n    def extract_feat(self, batch_inputs: Tensor) -> Tuple[Tensor]:\n        \"\"\"Extract features.\n\n        Args:\n            batch_inputs (Tensor): Image tensor with shape (N, C, H ,W).\n\n        Returns:\n            tuple[Tensor]: Multi-level features that may have\n            different resolutions.\n        \"\"\"\n        x = self.backbone(batch_inputs)\n        if self.with_neck:\n            x = self.neck(x)\n        return x\n"
  },
  {
    "path": "mmdet/models/detectors/single_stage_instance_seg.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Tuple\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList, SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .base import BaseDetector\n\nINF = 1e8\n\n\n@MODELS.register_module()\nclass SingleStageInstanceSegmentor(BaseDetector):\n    \"\"\"Base class for single-stage instance segmentors.\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 bbox_head: OptConfigType = None,\n                 mask_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.backbone = MODELS.build(backbone)\n        if neck is not None:\n            self.neck = MODELS.build(neck)\n        else:\n            self.neck = None\n        if bbox_head is not None:\n            bbox_head.update(train_cfg=copy.deepcopy(train_cfg))\n            bbox_head.update(test_cfg=copy.deepcopy(test_cfg))\n            self.bbox_head = MODELS.build(bbox_head)\n        else:\n            self.bbox_head = None\n\n        assert mask_head, f'`mask_head` must ' \\\n                          f'be implemented in {self.__class__.__name__}'\n        mask_head.update(train_cfg=copy.deepcopy(train_cfg))\n        mask_head.update(test_cfg=copy.deepcopy(test_cfg))\n        self.mask_head = MODELS.build(mask_head)\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n    def extract_feat(self, batch_inputs: Tensor) -> Tuple[Tensor]:\n        \"\"\"Extract features.\n\n        Args:\n            batch_inputs (Tensor): Image tensor with shape (N, C, H ,W).\n\n        Returns:\n            tuple[Tensor]: Multi-level features that may have different\n            resolutions.\n        \"\"\"\n        x = self.backbone(batch_inputs)\n        if self.with_neck:\n            x = self.neck(x)\n        return x\n\n    def _forward(self,\n                 batch_inputs: Tensor,\n                 batch_data_samples: OptSampleList = None,\n                 **kwargs) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n         Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n\n        Returns:\n            tuple: A tuple of features from ``bbox_head`` forward.\n        \"\"\"\n        outs = ()\n        # backbone\n        x = self.extract_feat(batch_inputs)\n        # bbox_head\n        positive_infos = None\n        if self.with_bbox:\n            assert batch_data_samples is not None\n            bbox_outs = self.bbox_head.forward(x)\n            outs = outs + (bbox_outs, )\n            # It is necessary to use `bbox_head.loss` to update\n            # `_raw_positive_infos` which will be used in `get_positive_infos`\n            # positive_infos will be used in the following mask head.\n            _ = self.bbox_head.loss(x, batch_data_samples, **kwargs)\n            positive_infos = self.bbox_head.get_positive_infos()\n        # mask_head\n        if positive_infos is None:\n            mask_outs = self.mask_head.forward(x)\n        else:\n            mask_outs = self.mask_head.forward(x, positive_infos)\n        outs = outs + (mask_outs, )\n        return outs\n\n    def loss(self, batch_inputs: Tensor, batch_data_samples: SampleList,\n             **kwargs) -> dict:\n        \"\"\"\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n        losses = dict()\n\n        positive_infos = None\n        # CondInst and YOLACT have bbox_head\n        if self.with_bbox:\n            bbox_losses = self.bbox_head.loss(x, batch_data_samples, **kwargs)\n            losses.update(bbox_losses)\n            # get positive information from bbox head, which will be used\n            # in the following mask head.\n            positive_infos = self.bbox_head.get_positive_infos()\n\n        mask_loss = self.mask_head.loss(\n            x, batch_data_samples, positive_infos=positive_infos, **kwargs)\n        # avoid loss override\n        assert not set(mask_loss.keys()) & set(losses.keys())\n\n        losses.update(mask_loss)\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True,\n                **kwargs) -> SampleList:\n        \"\"\"Perform forward propagation of the mask head and predict mask\n        results on the features of the upstream network.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances'. And the ``pred_instances`` usually\n            contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n                (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n                (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n                the last dimension 4 arrange as (x1, y1, x2, y2).\n            - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n        if self.with_bbox:\n            # the bbox branch does not need to be scaled to the original\n            # image scale, because the mask branch will scale both bbox\n            # and mask at the same time.\n            bbox_rescale = rescale if not self.with_mask else False\n            results_list = self.bbox_head.predict(\n                x, batch_data_samples, rescale=bbox_rescale)\n        else:\n            results_list = None\n\n        results_list = self.mask_head.predict(\n            x, batch_data_samples, rescale=rescale, results_list=results_list)\n\n        batch_data_samples = self.add_pred_to_datasample(\n            batch_data_samples, results_list)\n        return batch_data_samples\n"
  },
  {
    "path": "mmdet/models/detectors/soft_teacher.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.utils import (filter_gt_instances, rename_loss_dict,\n                                reweight_loss_dict)\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi, bbox_project\nfrom mmdet.utils import ConfigType, InstanceList, OptConfigType, OptMultiConfig\nfrom ..utils.misc import unpack_gt_instances\nfrom .semi_base import SemiBaseDetector\n\n\n@MODELS.register_module()\nclass SoftTeacher(SemiBaseDetector):\n    r\"\"\"Implementation of `End-to-End Semi-Supervised Object Detection\n    with Soft Teacher <https://arxiv.org/abs/2106.09018>`_\n\n    Args:\n        detector (:obj:`ConfigDict` or dict): The detector config.\n        semi_train_cfg (:obj:`ConfigDict` or dict, optional):\n            The semi-supervised training config.\n        semi_test_cfg (:obj:`ConfigDict` or dict, optional):\n            The semi-supervised testing config.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: ConfigType,\n                 semi_train_cfg: OptConfigType = None,\n                 semi_test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            detector=detector,\n            semi_train_cfg=semi_train_cfg,\n            semi_test_cfg=semi_test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n\n    def loss_by_pseudo_instances(self,\n                                 batch_inputs: Tensor,\n                                 batch_data_samples: SampleList,\n                                 batch_info: Optional[dict] = None) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and pseudo data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`,\n                which are `pseudo_instance` or `pseudo_panoptic_seg`\n                or `pseudo_sem_seg` in fact.\n            batch_info (dict): Batch information of teacher model\n                forward propagation process. Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components\n        \"\"\"\n\n        x = self.student.extract_feat(batch_inputs)\n\n        losses = {}\n        rpn_losses, rpn_results_list = self.rpn_loss_by_pseudo_instances(\n            x, batch_data_samples)\n        losses.update(**rpn_losses)\n        losses.update(**self.rcnn_cls_loss_by_pseudo_instances(\n            x, rpn_results_list, batch_data_samples, batch_info))\n        losses.update(**self.rcnn_reg_loss_by_pseudo_instances(\n            x, rpn_results_list, batch_data_samples))\n        unsup_weight = self.semi_train_cfg.get('unsup_weight', 1.)\n        return rename_loss_dict('unsup_',\n                                reweight_loss_dict(losses, unsup_weight))\n\n    @torch.no_grad()\n    def get_pseudo_instances(\n            self, batch_inputs: Tensor, batch_data_samples: SampleList\n    ) -> Tuple[SampleList, Optional[dict]]:\n        \"\"\"Get pseudo instances from teacher model.\"\"\"\n        assert self.teacher.with_bbox, 'Bbox head must be implemented.'\n        x = self.teacher.extract_feat(batch_inputs)\n\n        # If there are no pre-defined proposals, use RPN to get proposals\n        if batch_data_samples[0].get('proposals', None) is None:\n            rpn_results_list = self.teacher.rpn_head.predict(\n                x, batch_data_samples, rescale=False)\n        else:\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        results_list = self.teacher.roi_head.predict(\n            x, rpn_results_list, batch_data_samples, rescale=False)\n\n        for data_samples, results in zip(batch_data_samples, results_list):\n            data_samples.gt_instances = results\n\n        batch_data_samples = filter_gt_instances(\n            batch_data_samples,\n            score_thr=self.semi_train_cfg.pseudo_label_initial_score_thr)\n\n        reg_uncs_list = self.compute_uncertainty_with_aug(\n            x, batch_data_samples)\n\n        for data_samples, reg_uncs in zip(batch_data_samples, reg_uncs_list):\n            data_samples.gt_instances['reg_uncs'] = reg_uncs\n            data_samples.gt_instances.bboxes = bbox_project(\n                data_samples.gt_instances.bboxes,\n                torch.from_numpy(data_samples.homography_matrix).inverse().to(\n                    self.data_preprocessor.device), data_samples.ori_shape)\n\n        batch_info = {\n            'feat': x,\n            'img_shape': [],\n            'homography_matrix': [],\n            'metainfo': []\n        }\n        for data_samples in batch_data_samples:\n            batch_info['img_shape'].append(data_samples.img_shape)\n            batch_info['homography_matrix'].append(\n                torch.from_numpy(data_samples.homography_matrix).to(\n                    self.data_preprocessor.device))\n            batch_info['metainfo'].append(data_samples.metainfo)\n        return batch_data_samples, batch_info\n\n    def rpn_loss_by_pseudo_instances(self, x: Tuple[Tensor],\n                                     batch_data_samples: SampleList) -> dict:\n        \"\"\"Calculate rpn loss from a batch of inputs and pseudo data samples.\n\n        Args:\n            x (tuple[Tensor]): Features from FPN.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`,\n                which are `pseudo_instance` or `pseudo_panoptic_seg`\n                or `pseudo_sem_seg` in fact.\n        Returns:\n            dict: A dictionary of rpn loss components\n        \"\"\"\n\n        rpn_data_samples = copy.deepcopy(batch_data_samples)\n        rpn_data_samples = filter_gt_instances(\n            rpn_data_samples, score_thr=self.semi_train_cfg.rpn_pseudo_thr)\n        proposal_cfg = self.student.train_cfg.get('rpn_proposal',\n                                                  self.student.test_cfg.rpn)\n        # set cat_id of gt_labels to 0 in RPN\n        for data_sample in rpn_data_samples:\n            data_sample.gt_instances.labels = \\\n                torch.zeros_like(data_sample.gt_instances.labels)\n\n        rpn_losses, rpn_results_list = self.student.rpn_head.loss_and_predict(\n            x, rpn_data_samples, proposal_cfg=proposal_cfg)\n        for key in rpn_losses.keys():\n            if 'loss' in key and 'rpn' not in key:\n                rpn_losses[f'rpn_{key}'] = rpn_losses.pop(key)\n        return rpn_losses, rpn_results_list\n\n    def rcnn_cls_loss_by_pseudo_instances(self, x: Tuple[Tensor],\n                                          unsup_rpn_results_list: InstanceList,\n                                          batch_data_samples: SampleList,\n                                          batch_info: dict) -> dict:\n        \"\"\"Calculate classification loss from a batch of inputs and pseudo data\n        samples.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            unsup_rpn_results_list (list[:obj:`InstanceData`]):\n                List of region proposals.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`,\n                which are `pseudo_instance` or `pseudo_panoptic_seg`\n                or `pseudo_sem_seg` in fact.\n            batch_info (dict): Batch information of teacher model\n                forward propagation process.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of rcnn\n                classification loss components\n        \"\"\"\n        rpn_results_list = copy.deepcopy(unsup_rpn_results_list)\n        cls_data_samples = copy.deepcopy(batch_data_samples)\n        cls_data_samples = filter_gt_instances(\n            cls_data_samples, score_thr=self.semi_train_cfg.cls_pseudo_thr)\n\n        outputs = unpack_gt_instances(cls_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, _ = outputs\n\n        # assign gts and sample proposals\n        num_imgs = len(cls_data_samples)\n        sampling_results = []\n        for i in range(num_imgs):\n            # rename rpn_results.bboxes to rpn_results.priors\n            rpn_results = rpn_results_list[i]\n            rpn_results.priors = rpn_results.pop('bboxes')\n            assign_result = self.student.roi_head.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.student.roi_head.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in x])\n            sampling_results.append(sampling_result)\n\n        selected_bboxes = [res.priors for res in sampling_results]\n        rois = bbox2roi(selected_bboxes)\n        bbox_results = self.student.roi_head._bbox_forward(x, rois)\n        # cls_reg_targets is a tuple of labels, label_weights,\n        # and bbox_targets, bbox_weights\n        cls_reg_targets = self.student.roi_head.bbox_head.get_targets(\n            sampling_results, self.student.train_cfg.rcnn)\n\n        selected_results_list = []\n        for bboxes, data_samples, teacher_matrix, teacher_img_shape in zip(\n                selected_bboxes, batch_data_samples,\n                batch_info['homography_matrix'], batch_info['img_shape']):\n            student_matrix = torch.tensor(\n                data_samples.homography_matrix, device=teacher_matrix.device)\n            homography_matrix = teacher_matrix @ student_matrix.inverse()\n            projected_bboxes = bbox_project(bboxes, homography_matrix,\n                                            teacher_img_shape)\n            selected_results_list.append(InstanceData(bboxes=projected_bboxes))\n\n        with torch.no_grad():\n            results_list = self.teacher.roi_head.predict_bbox(\n                batch_info['feat'],\n                batch_info['metainfo'],\n                selected_results_list,\n                rcnn_test_cfg=None,\n                rescale=False)\n            bg_score = torch.cat(\n                [results.scores[:, -1] for results in results_list])\n            # cls_reg_targets[0] is labels\n            neg_inds = cls_reg_targets[\n                0] == self.student.roi_head.bbox_head.num_classes\n            # cls_reg_targets[1] is label_weights\n            cls_reg_targets[1][neg_inds] = bg_score[neg_inds].detach()\n\n        losses = self.student.roi_head.bbox_head.loss(\n            bbox_results['cls_score'], bbox_results['bbox_pred'], rois,\n            *cls_reg_targets)\n        # cls_reg_targets[1] is label_weights\n        losses['loss_cls'] = losses['loss_cls'] * len(\n            cls_reg_targets[1]) / max(sum(cls_reg_targets[1]), 1.0)\n        return losses\n\n    def rcnn_reg_loss_by_pseudo_instances(\n            self, x: Tuple[Tensor], unsup_rpn_results_list: InstanceList,\n            batch_data_samples: SampleList) -> dict:\n        \"\"\"Calculate rcnn regression loss from a batch of inputs and pseudo\n        data samples.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            unsup_rpn_results_list (list[:obj:`InstanceData`]):\n                List of region proposals.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`,\n                which are `pseudo_instance` or `pseudo_panoptic_seg`\n                or `pseudo_sem_seg` in fact.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of rcnn\n                regression loss components\n        \"\"\"\n        rpn_results_list = copy.deepcopy(unsup_rpn_results_list)\n        reg_data_samples = copy.deepcopy(batch_data_samples)\n        for data_samples in reg_data_samples:\n            if data_samples.gt_instances.bboxes.shape[0] > 0:\n                data_samples.gt_instances = data_samples.gt_instances[\n                    data_samples.gt_instances.reg_uncs <\n                    self.semi_train_cfg.reg_pseudo_thr]\n        roi_losses = self.student.roi_head.loss(x, rpn_results_list,\n                                                reg_data_samples)\n        return {'loss_bbox': roi_losses['loss_bbox']}\n\n    def compute_uncertainty_with_aug(\n            self, x: Tuple[Tensor],\n            batch_data_samples: SampleList) -> List[Tensor]:\n        \"\"\"Compute uncertainty with augmented bboxes.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`,\n                which are `pseudo_instance` or `pseudo_panoptic_seg`\n                or `pseudo_sem_seg` in fact.\n\n        Returns:\n            list[Tensor]: A list of uncertainty for pseudo bboxes.\n        \"\"\"\n        auged_results_list = self.aug_box(batch_data_samples,\n                                          self.semi_train_cfg.jitter_times,\n                                          self.semi_train_cfg.jitter_scale)\n        # flatten\n        auged_results_list = [\n            InstanceData(bboxes=auged.reshape(-1, auged.shape[-1]))\n            for auged in auged_results_list\n        ]\n\n        self.teacher.roi_head.test_cfg = None\n        results_list = self.teacher.roi_head.predict(\n            x, auged_results_list, batch_data_samples, rescale=False)\n        self.teacher.roi_head.test_cfg = self.teacher.test_cfg.rcnn\n\n        reg_channel = max(\n            [results.bboxes.shape[-1] for results in results_list]) // 4\n        bboxes = [\n            results.bboxes.reshape(self.semi_train_cfg.jitter_times, -1,\n                                   results.bboxes.shape[-1])\n            if results.bboxes.numel() > 0 else results.bboxes.new_zeros(\n                self.semi_train_cfg.jitter_times, 0, 4 * reg_channel).float()\n            for results in results_list\n        ]\n\n        box_unc = [bbox.std(dim=0) for bbox in bboxes]\n        bboxes = [bbox.mean(dim=0) for bbox in bboxes]\n        labels = [\n            data_samples.gt_instances.labels\n            for data_samples in batch_data_samples\n        ]\n        if reg_channel != 1:\n            bboxes = [\n                bbox.reshape(bbox.shape[0], reg_channel,\n                             4)[torch.arange(bbox.shape[0]), label]\n                for bbox, label in zip(bboxes, labels)\n            ]\n            box_unc = [\n                unc.reshape(unc.shape[0], reg_channel,\n                            4)[torch.arange(unc.shape[0]), label]\n                for unc, label in zip(box_unc, labels)\n            ]\n\n        box_shape = [(bbox[:, 2:4] - bbox[:, :2]).clamp(min=1.0)\n                     for bbox in bboxes]\n        box_unc = [\n            torch.mean(\n                unc / wh[:, None, :].expand(-1, 2, 2).reshape(-1, 4), dim=-1)\n            if wh.numel() > 0 else unc for unc, wh in zip(box_unc, box_shape)\n        ]\n        return box_unc\n\n    @staticmethod\n    def aug_box(batch_data_samples, times, frac):\n        \"\"\"Augment bboxes with jitter.\"\"\"\n\n        def _aug_single(box):\n            box_scale = box[:, 2:4] - box[:, :2]\n            box_scale = (\n                box_scale.clamp(min=1)[:, None, :].expand(-1, 2,\n                                                          2).reshape(-1, 4))\n            aug_scale = box_scale * frac  # [n,4]\n\n            offset = (\n                torch.randn(times, box.shape[0], 4, device=box.device) *\n                aug_scale[None, ...])\n            new_box = box.clone()[None, ...].expand(times, box.shape[0],\n                                                    -1) + offset\n            return new_box\n\n        return [\n            _aug_single(data_samples.gt_instances.bboxes)\n            for data_samples in batch_data_samples\n        ]\n"
  },
  {
    "path": "mmdet/models/detectors/solo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage_instance_seg import SingleStageInstanceSegmentor\n\n\n@MODELS.register_module()\nclass SOLO(SingleStageInstanceSegmentor):\n    \"\"\"`SOLO: Segmenting Objects by Locations\n    <https://arxiv.org/abs/1912.04488>`_\n\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 bbox_head: OptConfigType = None,\n                 mask_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            mask_head=mask_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/solov2.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage_instance_seg import SingleStageInstanceSegmentor\n\n\n@MODELS.register_module()\nclass SOLOv2(SingleStageInstanceSegmentor):\n    \"\"\"`SOLOv2: Dynamic and Fast Instance Segmentation\n    <https://arxiv.org/abs/2003.10152>`_\n\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 bbox_head: OptConfigType = None,\n                 mask_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            mask_head=mask_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/sparse_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .two_stage import TwoStageDetector\n\n\n@MODELS.register_module()\nclass SparseRCNN(TwoStageDetector):\n    r\"\"\"Implementation of `Sparse R-CNN: End-to-End Object Detection with\n    Learnable Proposals <https://arxiv.org/abs/2011.12450>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 rpn_head: OptConfigType = None,\n                 roi_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n        assert self.with_rpn, 'Sparse R-CNN and QueryInst ' \\\n            'do not support external proposals'\n"
  },
  {
    "path": "mmdet/models/detectors/tood.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass TOOD(SingleStageDetector):\n    r\"\"\"Implementation of `TOOD: Task-aligned One-stage Object Detection.\n    <https://arxiv.org/abs/2108.07755>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of TOOD. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of TOOD. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/trident_faster_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .faster_rcnn import FasterRCNN\n\n\n@MODELS.register_module()\nclass TridentFasterRCNN(FasterRCNN):\n    \"\"\"Implementation of `TridentNet <https://arxiv.org/abs/1901.01892>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 rpn_head: ConfigType,\n                 roi_head: ConfigType,\n                 train_cfg: ConfigType,\n                 test_cfg: ConfigType,\n                 neck: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            rpn_head=rpn_head,\n            roi_head=roi_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n        assert self.backbone.num_branch == self.roi_head.num_branch\n        assert self.backbone.test_branch_idx == self.roi_head.test_branch_idx\n        self.num_branch = self.backbone.num_branch\n        self.test_branch_idx = self.backbone.test_branch_idx\n\n    def _forward(self, batch_inputs: Tensor,\n                 batch_data_samples: SampleList) -> tuple:\n        \"\"\"copy the ``batch_data_samples`` to fit multi-branch.\"\"\"\n        num_branch = self.num_branch \\\n            if self.training or self.test_branch_idx == -1 else 1\n        trident_data_samples = batch_data_samples * num_branch\n        return super()._forward(\n            batch_inputs=batch_inputs, batch_data_samples=trident_data_samples)\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"copy the ``batch_data_samples`` to fit multi-branch.\"\"\"\n        num_branch = self.num_branch \\\n            if self.training or self.test_branch_idx == -1 else 1\n        trident_data_samples = batch_data_samples * num_branch\n        return super().loss(\n            batch_inputs=batch_inputs, batch_data_samples=trident_data_samples)\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"copy the ``batch_data_samples`` to fit multi-branch.\"\"\"\n        num_branch = self.num_branch \\\n            if self.training or self.test_branch_idx == -1 else 1\n        trident_data_samples = batch_data_samples * num_branch\n        return super().predict(\n            batch_inputs=batch_inputs,\n            batch_data_samples=trident_data_samples,\n            rescale=rescale)\n\n    # TODO need to refactor\n    def aug_test(self, imgs, img_metas, rescale=False):\n        \"\"\"Test with augmentations.\n\n        If rescale is False, then returned bboxes and masks will fit the scale\n        of imgs[0].\n        \"\"\"\n        x = self.extract_feats(imgs)\n        num_branch = (self.num_branch if self.test_branch_idx == -1 else 1)\n        trident_img_metas = [img_metas * num_branch for img_metas in img_metas]\n        proposal_list = self.rpn_head.aug_test_rpn(x, trident_img_metas)\n        return self.roi_head.aug_test(\n            x, proposal_list, img_metas, rescale=rescale)\n"
  },
  {
    "path": "mmdet/models/detectors/two_stage.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport warnings\nfrom typing import List, Tuple, Union\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .base import BaseDetector\n\n\n@MODELS.register_module()\nclass TwoStageDetector(BaseDetector):\n    \"\"\"Base class for two-stage detectors.\n\n    Two-stage detectors typically consisting of a region proposal network and a\n    task-specific regression head.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 rpn_head: OptConfigType = None,\n                 roi_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.backbone = MODELS.build(backbone)\n\n        if neck is not None:\n            self.neck = MODELS.build(neck)\n\n        if rpn_head is not None:\n            rpn_train_cfg = train_cfg.rpn if train_cfg is not None else None\n            rpn_head_ = rpn_head.copy()\n            rpn_head_.update(train_cfg=rpn_train_cfg, test_cfg=test_cfg.rpn)\n            rpn_head_num_classes = rpn_head_.get('num_classes', None)\n            if rpn_head_num_classes is None:\n                rpn_head_.update(num_classes=1)\n            else:\n                if rpn_head_num_classes != 1:\n                    warnings.warn(\n                        'The `num_classes` should be 1 in RPN, but get '\n                        f'{rpn_head_num_classes}, please set '\n                        'rpn_head.num_classes = 1 in your config file.')\n                    rpn_head_.update(num_classes=1)\n            self.rpn_head = MODELS.build(rpn_head_)\n\n        if roi_head is not None:\n            # update train and test cfg here for now\n            # TODO: refactor assigner & sampler\n            rcnn_train_cfg = train_cfg.rcnn if train_cfg is not None else None\n            roi_head.update(train_cfg=rcnn_train_cfg)\n            roi_head.update(test_cfg=test_cfg.rcnn)\n            self.roi_head = MODELS.build(roi_head)\n\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n    def _load_from_state_dict(self, state_dict: dict, prefix: str,\n                              local_metadata: dict, strict: bool,\n                              missing_keys: Union[List[str], str],\n                              unexpected_keys: Union[List[str], str],\n                              error_msgs: Union[List[str], str]) -> None:\n        \"\"\"Exchange bbox_head key to rpn_head key when loading single-stage\n        weights into two-stage model.\"\"\"\n        bbox_head_prefix = prefix + '.bbox_head' if prefix else 'bbox_head'\n        bbox_head_keys = [\n            k for k in state_dict.keys() if k.startswith(bbox_head_prefix)\n        ]\n        rpn_head_prefix = prefix + '.rpn_head' if prefix else 'rpn_head'\n        rpn_head_keys = [\n            k for k in state_dict.keys() if k.startswith(rpn_head_prefix)\n        ]\n        if len(bbox_head_keys) != 0 and len(rpn_head_keys) == 0:\n            for bbox_head_key in bbox_head_keys:\n                rpn_head_key = rpn_head_prefix + \\\n                               bbox_head_key[len(bbox_head_prefix):]\n                state_dict[rpn_head_key] = state_dict.pop(bbox_head_key)\n        super()._load_from_state_dict(state_dict, prefix, local_metadata,\n                                      strict, missing_keys, unexpected_keys,\n                                      error_msgs)\n\n    @property\n    def with_rpn(self) -> bool:\n        \"\"\"bool: whether the detector has RPN\"\"\"\n        return hasattr(self, 'rpn_head') and self.rpn_head is not None\n\n    @property\n    def with_roi_head(self) -> bool:\n        \"\"\"bool: whether the detector has a RoI head\"\"\"\n        return hasattr(self, 'roi_head') and self.roi_head is not None\n\n    def extract_feat(self, batch_inputs: Tensor) -> Tuple[Tensor]:\n        \"\"\"Extract features.\n\n        Args:\n            batch_inputs (Tensor): Image tensor with shape (N, C, H ,W).\n\n        Returns:\n            tuple[Tensor]: Multi-level features that may have\n            different resolutions.\n        \"\"\"\n        x = self.backbone(batch_inputs)\n        if self.with_neck:\n            x = self.neck(x)\n        return x\n\n    def _forward(self, batch_inputs: Tensor,\n                 batch_data_samples: SampleList) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n\n        Returns:\n            tuple: A tuple of features from ``rpn_head`` and ``roi_head``\n            forward.\n        \"\"\"\n        results = ()\n        x = self.extract_feat(batch_inputs)\n\n        if self.with_rpn:\n            rpn_results_list = self.rpn_head.predict(\n                x, batch_data_samples, rescale=False)\n        else:\n            assert batch_data_samples[0].get('proposals', None) is not None\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n        roi_outs = self.roi_head.forward(x, rpn_results_list,\n                                         batch_data_samples)\n        results = results + (roi_outs, )\n        return results\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components\n        \"\"\"\n        x = self.extract_feat(batch_inputs)\n\n        losses = dict()\n\n        # RPN forward and loss\n        if self.with_rpn:\n            proposal_cfg = self.train_cfg.get('rpn_proposal',\n                                              self.test_cfg.rpn)\n            rpn_data_samples = copy.deepcopy(batch_data_samples)\n            # set cat_id of gt_labels to 0 in RPN\n            for data_sample in rpn_data_samples:\n                data_sample.gt_instances.labels = \\\n                    torch.zeros_like(data_sample.gt_instances.labels)\n\n            rpn_losses, rpn_results_list = self.rpn_head.loss_and_predict(\n                x, rpn_data_samples, proposal_cfg=proposal_cfg)\n            # avoid get same name with roi_head loss\n            keys = rpn_losses.keys()\n            for key in list(keys):\n                if 'loss' in key and 'rpn' not in key:\n                    rpn_losses[f'rpn_{key}'] = rpn_losses.pop(key)\n            losses.update(rpn_losses)\n        else:\n            assert batch_data_samples[0].get('proposals', None) is not None\n            # use pre-defined proposals in InstanceData for the second stage\n            # to extract ROI features.\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        roi_losses = self.roi_head.loss(x, rpn_results_list,\n                                        batch_data_samples)\n        losses.update(roi_losses)\n\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Return the detection results of the\n            input images. The returns value is DetDataSample,\n            which usually contain 'pred_instances'. And the\n            ``pred_instances`` usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n\n        assert self.with_bbox, 'Bbox head must be implemented.'\n        x = self.extract_feat(batch_inputs)\n\n        # If there are no pre-defined proposals, use RPN to get proposals\n        if batch_data_samples[0].get('proposals', None) is None:\n            rpn_results_list = self.rpn_head.predict(\n                x, batch_data_samples, rescale=False)\n        else:\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        results_list = self.roi_head.predict(\n            x, rpn_results_list, batch_data_samples, rescale=rescale)\n\n        batch_data_samples = self.add_pred_to_datasample(\n            batch_data_samples, results_list)\n        return batch_data_samples\n"
  },
  {
    "path": "mmdet/models/detectors/vfnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass VFNet(SingleStageDetector):\n    \"\"\"Implementation of `VarifocalNet\n    (VFNet).<https://arxiv.org/abs/2008.13367>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of VFNet. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of VFNet. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/yolact.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage_instance_seg import SingleStageInstanceSegmentor\n\n\n@MODELS.register_module()\nclass YOLACT(SingleStageInstanceSegmentor):\n    \"\"\"Implementation of `YOLACT <https://arxiv.org/abs/1904.02689>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 mask_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            mask_head=mask_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/yolo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Copyright (c) 2019 Western Digital Corporation or its affiliates.\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass YOLOV3(SingleStageDetector):\n    r\"\"\"Implementation of `Yolov3: An incremental improvement\n    <https://arxiv.org/abs/1804.02767>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of YOLOX. Default: None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of YOLOX. Default: None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional):\n            Model preprocessing config for processing the input data.\n            it usually includes ``to_rgb``, ``pad_size_divisor``,\n            ``pad_value``, ``mean`` and ``std``. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/yolof.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass YOLOF(SingleStageDetector):\n    r\"\"\"Implementation of `You Only Look One-level Feature\n    <https://arxiv.org/abs/2103.09460>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        neck (:obj:`ConfigDict` or dict): The neck module.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head module.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of YOLOF. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of YOLOF. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional):\n            Model preprocessing config for processing the input data.\n            it usually includes ``to_rgb``, ``pad_size_divisor``,\n            ``pad_value``, ``mean`` and ``std``. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/detectors/yolox.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .single_stage import SingleStageDetector\n\n\n@MODELS.register_module()\nclass YOLOX(SingleStageDetector):\n    r\"\"\"Implementation of `YOLOX: Exceeding YOLO Series in 2021\n    <https://arxiv.org/abs/2107.08430>`_\n\n    Args:\n        backbone (:obj:`ConfigDict` or dict): The backbone config.\n        neck (:obj:`ConfigDict` or dict): The neck config.\n        bbox_head (:obj:`ConfigDict` or dict): The bbox head config.\n        train_cfg (:obj:`ConfigDict` or dict, optional): The training config\n            of YOLOX. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of YOLOX. Defaults to None.\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "mmdet/models/language_models/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .bert import BertModel\n\n__all__ = ['BertModel']\n"
  },
  {
    "path": "mmdet/models/language_models/bert.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom collections import OrderedDict\nfrom typing import Sequence\n\nimport torch\nfrom mmengine.model import BaseModel\nfrom torch import nn\n\ntry:\n    from transformers import AutoTokenizer, BertConfig\n    from transformers import BertModel as HFBertModel\nexcept ImportError:\n    AutoTokenizer = None\n    HFBertModel = None\n\nfrom mmdet.registry import MODELS\n\n\ndef generate_masks_with_special_tokens_and_transfer_map(\n        tokenized, special_tokens_list):\n    \"\"\"Generate attention mask between each pair of special tokens.\n\n    Only token pairs in between two special tokens are attended to\n    and thus the attention mask for these pairs is positive.\n\n    Args:\n        input_ids (torch.Tensor): input ids. Shape: [bs, num_token]\n        special_tokens_mask (list): special tokens mask.\n\n    Returns:\n        Tuple(Tensor, Tensor):\n        - attention_mask is the attention mask between each tokens.\n          Only token pairs in between two special tokens are positive.\n          Shape: [bs, num_token, num_token].\n        - position_ids is the position id of tokens within each valid sentence.\n          The id starts from 0 whenenver a special token is encountered.\n          Shape: [bs, num_token]\n    \"\"\"\n    input_ids = tokenized['input_ids']\n    bs, num_token = input_ids.shape\n    # special_tokens_mask:\n    # bs, num_token. 1 for special tokens. 0 for normal tokens\n    special_tokens_mask = torch.zeros((bs, num_token),\n                                      device=input_ids.device).bool()\n\n    for special_token in special_tokens_list:\n        special_tokens_mask |= input_ids == special_token\n\n    # idxs: each row is a list of indices of special tokens\n    idxs = torch.nonzero(special_tokens_mask)\n\n    # generate attention mask and positional ids\n    attention_mask = (\n        torch.eye(num_token,\n                  device=input_ids.device).bool().unsqueeze(0).repeat(\n                      bs, 1, 1))\n    position_ids = torch.zeros((bs, num_token), device=input_ids.device)\n    previous_col = 0\n    for i in range(idxs.shape[0]):\n        row, col = idxs[i]\n        if (col == 0) or (col == num_token - 1):\n            attention_mask[row, col, col] = True\n            position_ids[row, col] = 0\n        else:\n            attention_mask[row, previous_col + 1:col + 1,\n                           previous_col + 1:col + 1] = True\n            position_ids[row, previous_col + 1:col + 1] = torch.arange(\n                0, col - previous_col, device=input_ids.device)\n        previous_col = col\n\n    return attention_mask, position_ids.to(torch.long)\n\n\n@MODELS.register_module()\nclass BertModel(BaseModel):\n    \"\"\"BERT model for language embedding only encoder.\n\n    Args:\n        name (str, optional): name of the pretrained BERT model from\n            HuggingFace. Defaults to bert-base-uncased.\n        max_tokens (int, optional): maximum number of tokens to be\n            used for BERT. Defaults to 256.\n        pad_to_max (bool, optional): whether to pad the tokens to max_tokens.\n             Defaults to True.\n        use_sub_sentence_represent (bool, optional): whether to use sub\n            sentence represent introduced in `Grounding DINO\n            <https://arxiv.org/abs/2303.05499>`. Defaults to False.\n        special_tokens_list (list, optional): special tokens used to split\n            subsentence. It cannot be None when `use_sub_sentence_represent`\n            is True. Defaults to None.\n        add_pooling_layer (bool, optional): whether to adding pooling\n            layer in bert encoder. Defaults to False.\n        num_layers_of_embedded (int, optional): number of layers of\n            the embedded model. Defaults to 1.\n        use_checkpoint (bool, optional): whether to use gradient checkpointing.\n             Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 name: str = 'bert-base-uncased',\n                 max_tokens: int = 256,\n                 pad_to_max: bool = True,\n                 use_sub_sentence_represent: bool = False,\n                 special_tokens_list: list = None,\n                 add_pooling_layer: bool = False,\n                 num_layers_of_embedded: int = 1,\n                 use_checkpoint: bool = False,\n                 **kwargs) -> None:\n\n        super().__init__(**kwargs)\n        self.max_tokens = max_tokens\n        self.pad_to_max = pad_to_max\n\n        if AutoTokenizer is None:\n            raise RuntimeError(\n                'transformers is not installed, please install it by: '\n                'pip install transformers.')\n\n        self.tokenizer = AutoTokenizer.from_pretrained(name)\n        self.language_backbone = nn.Sequential(\n            OrderedDict([('body',\n                          BertEncoder(\n                              name,\n                              add_pooling_layer=add_pooling_layer,\n                              num_layers_of_embedded=num_layers_of_embedded,\n                              use_checkpoint=use_checkpoint))]))\n\n        self.use_sub_sentence_represent = use_sub_sentence_represent\n        if self.use_sub_sentence_represent:\n            assert special_tokens_list is not None, \\\n                'special_tokens should not be None \\\n                    if use_sub_sentence_represent is True'\n\n            self.special_tokens = self.tokenizer.convert_tokens_to_ids(\n                special_tokens_list)\n\n    def forward(self, captions: Sequence[str], **kwargs) -> dict:\n        \"\"\"Forward function.\"\"\"\n        device = next(self.language_backbone.parameters()).device\n        tokenized = self.tokenizer.batch_encode_plus(\n            captions,\n            max_length=self.max_tokens,\n            padding='max_length' if self.pad_to_max else 'longest',\n            return_special_tokens_mask=True,\n            return_tensors='pt',\n            truncation=True).to(device)\n        input_ids = tokenized.input_ids\n        if self.use_sub_sentence_represent:\n            attention_mask, position_ids = \\\n                generate_masks_with_special_tokens_and_transfer_map(\n                    tokenized, self.special_tokens)\n            token_type_ids = tokenized['token_type_ids']\n\n        else:\n            attention_mask = tokenized.attention_mask\n            position_ids = None\n            token_type_ids = None\n\n        tokenizer_input = {\n            'input_ids': input_ids,\n            'attention_mask': attention_mask,\n            'position_ids': position_ids,\n            'token_type_ids': token_type_ids\n        }\n        language_dict_features = self.language_backbone(tokenizer_input)\n        if self.use_sub_sentence_represent:\n            language_dict_features['position_ids'] = position_ids\n            language_dict_features[\n                'text_token_mask'] = tokenized.attention_mask.bool()\n        return language_dict_features\n\n\nclass BertEncoder(nn.Module):\n    \"\"\"BERT encoder for language embedding.\n\n    Args:\n        name (str): name of the pretrained BERT model from HuggingFace.\n                Defaults to bert-base-uncased.\n        add_pooling_layer (bool): whether to add a pooling layer.\n        num_layers_of_embedded (int): number of layers of the embedded model.\n                Defaults to 1.\n        use_checkpoint (bool): whether to use gradient checkpointing.\n                Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 name: str,\n                 add_pooling_layer: bool = False,\n                 num_layers_of_embedded: int = 1,\n                 use_checkpoint: bool = False):\n        super().__init__()\n        if BertConfig is None:\n            raise RuntimeError(\n                'transformers is not installed, please install it by: '\n                'pip install transformers.')\n        config = BertConfig.from_pretrained(name)\n        config.gradient_checkpointing = use_checkpoint\n        # only encoder\n        self.model = HFBertModel.from_pretrained(\n            name, add_pooling_layer=add_pooling_layer, config=config)\n        self.language_dim = config.hidden_size\n        self.num_layers_of_embedded = num_layers_of_embedded\n\n    def forward(self, x) -> dict:\n        mask = x['attention_mask']\n\n        outputs = self.model(\n            input_ids=x['input_ids'],\n            attention_mask=mask,\n            position_ids=x['position_ids'],\n            token_type_ids=x['token_type_ids'],\n            output_hidden_states=True,\n        )\n\n        # outputs has 13 layers, 1 input layer and 12 hidden layers\n        encoded_layers = outputs.hidden_states[1:]\n        features = torch.stack(encoded_layers[-self.num_layers_of_embedded:],\n                               1).mean(1)\n        # language embedding has shape [len(phrase), seq_len, language_dim]\n        features = features / self.num_layers_of_embedded\n        if mask.dim() == 2:\n            embedded = features * mask.unsqueeze(-1).float()\n        else:\n            embedded = features\n\n        results = {\n            'embedded': embedded,\n            'masks': mask,\n            'hidden': encoded_layers[-1]\n        }\n        return results\n"
  },
  {
    "path": "mmdet/models/layers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .activations import SiLU\nfrom .bbox_nms import fast_nms, multiclass_nms\nfrom .brick_wrappers import (AdaptiveAvgPool2d, FrozenBatchNorm2d,\n                             adaptive_avg_pool2d)\nfrom .conv_upsample import ConvUpsample\nfrom .csp_layer import CSPLayer\nfrom .dropblock import DropBlock\nfrom .ema import ExpMomentumEMA\nfrom .inverted_residual import InvertedResidual\nfrom .matrix_nms import mask_matrix_nms\nfrom .msdeformattn_pixel_decoder import MSDeformAttnPixelDecoder\nfrom .normed_predictor import NormedConv2d, NormedLinear\nfrom .pixel_decoder import PixelDecoder, TransformerEncoderPixelDecoder\nfrom .positional_encoding import (LearnedPositionalEncoding,\n                                  SinePositionalEncoding,\n                                  SinePositionalEncoding3D)\nfrom .res_layer import ResLayer, SimplifiedBasicBlock\nfrom .se_layer import ChannelAttention, DyReLU, SELayer\n# yapf: disable\nfrom .transformer import (MLP, AdaptivePadding, CdnQueryGenerator,\n                          ConditionalAttention,\n                          ConditionalDetrTransformerDecoder,\n                          ConditionalDetrTransformerDecoderLayer,\n                          DABDetrTransformerDecoder,\n                          DABDetrTransformerDecoderLayer,\n                          DABDetrTransformerEncoder, DDQTransformerDecoder,\n                          DeformableDetrTransformerDecoder,\n                          DeformableDetrTransformerDecoderLayer,\n                          DeformableDetrTransformerEncoder,\n                          DeformableDetrTransformerEncoderLayer,\n                          DetrTransformerDecoder, DetrTransformerDecoderLayer,\n                          DetrTransformerEncoder, DetrTransformerEncoderLayer,\n                          DinoTransformerDecoder, DynamicConv,\n                          Mask2FormerTransformerDecoder,\n                          Mask2FormerTransformerDecoderLayer,\n                          Mask2FormerTransformerEncoder, PatchEmbed,\n                          PatchMerging, coordinate_to_encoding,\n                          inverse_sigmoid, nchw_to_nlc, nlc_to_nchw)\n\n# yapf: enable\n\n__all__ = [\n    'fast_nms', 'multiclass_nms', 'mask_matrix_nms', 'DropBlock',\n    'PixelDecoder', 'TransformerEncoderPixelDecoder',\n    'MSDeformAttnPixelDecoder', 'ResLayer', 'PatchMerging',\n    'SinePositionalEncoding', 'LearnedPositionalEncoding', 'DynamicConv',\n    'SimplifiedBasicBlock', 'NormedLinear', 'NormedConv2d', 'InvertedResidual',\n    'SELayer', 'ConvUpsample', 'CSPLayer', 'adaptive_avg_pool2d',\n    'AdaptiveAvgPool2d', 'PatchEmbed', 'nchw_to_nlc', 'nlc_to_nchw', 'DyReLU',\n    'ExpMomentumEMA', 'inverse_sigmoid', 'ChannelAttention', 'SiLU', 'MLP',\n    'DetrTransformerEncoderLayer', 'DetrTransformerDecoderLayer',\n    'DetrTransformerEncoder', 'DetrTransformerDecoder',\n    'DeformableDetrTransformerEncoder', 'DeformableDetrTransformerDecoder',\n    'DeformableDetrTransformerEncoderLayer',\n    'DeformableDetrTransformerDecoderLayer', 'AdaptivePadding',\n    'coordinate_to_encoding', 'ConditionalAttention',\n    'DABDetrTransformerDecoderLayer', 'DABDetrTransformerDecoder',\n    'DABDetrTransformerEncoder', 'DDQTransformerDecoder',\n    'ConditionalDetrTransformerDecoder',\n    'ConditionalDetrTransformerDecoderLayer', 'DinoTransformerDecoder',\n    'CdnQueryGenerator', 'Mask2FormerTransformerEncoder',\n    'Mask2FormerTransformerDecoderLayer', 'Mask2FormerTransformerDecoder',\n    'SinePositionalEncoding3D', 'FrozenBatchNorm2d'\n]\n"
  },
  {
    "path": "mmdet/models/layers/activations.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmengine.utils import digit_version\n\nfrom mmdet.registry import MODELS\n\nif digit_version(torch.__version__) >= digit_version('1.7.0'):\n    from torch.nn import SiLU\nelse:\n\n    class SiLU(nn.Module):\n        \"\"\"Sigmoid Weighted Liner Unit.\"\"\"\n\n        def __init__(self, inplace=True):\n            super().__init__()\n\n        def forward(self, inputs) -> torch.Tensor:\n            return inputs * torch.sigmoid(inputs)\n\n\nMODELS.register_module(module=SiLU, name='SiLU')\n"
  },
  {
    "path": "mmdet/models/layers/bbox_nms.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport torch\nfrom mmcv.ops.nms import batched_nms\nfrom torch import Tensor\n\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom mmdet.utils import ConfigType\n\n\ndef multiclass_nms(\n    multi_bboxes: Tensor,\n    multi_scores: Tensor,\n    score_thr: float,\n    nms_cfg: ConfigType,\n    max_num: int = -1,\n    score_factors: Optional[Tensor] = None,\n    return_inds: bool = False,\n    box_dim: int = 4\n) -> Union[Tuple[Tensor, Tensor, Tensor], Tuple[Tensor, Tensor]]:\n    \"\"\"NMS for multi-class bboxes.\n\n    Args:\n        multi_bboxes (Tensor): shape (n, #class*4) or (n, 4)\n        multi_scores (Tensor): shape (n, #class), where the last column\n            contains scores of the background class, but this will be ignored.\n        score_thr (float): bbox threshold, bboxes with scores lower than it\n            will not be considered.\n        nms_cfg (Union[:obj:`ConfigDict`, dict]): a dict that contains\n            the arguments of nms operations.\n        max_num (int, optional): if there are more than max_num bboxes after\n            NMS, only top max_num will be kept. Default to -1.\n        score_factors (Tensor, optional): The factors multiplied to scores\n            before applying NMS. Default to None.\n        return_inds (bool, optional): Whether return the indices of kept\n            bboxes. Default to False.\n        box_dim (int): The dimension of boxes. Defaults to 4.\n\n    Returns:\n        Union[Tuple[Tensor, Tensor, Tensor], Tuple[Tensor, Tensor]]:\n            (dets, labels, indices (optional)), tensors of shape (k, 5),\n            (k), and (k). Dets are boxes with scores. Labels are 0-based.\n    \"\"\"\n    num_classes = multi_scores.size(1) - 1\n    # exclude background category\n    if multi_bboxes.shape[1] > box_dim:\n        bboxes = multi_bboxes.view(multi_scores.size(0), -1, box_dim)\n    else:\n        bboxes = multi_bboxes[:, None].expand(\n            multi_scores.size(0), num_classes, box_dim)\n\n    scores = multi_scores[:, :-1]\n\n    labels = torch.arange(num_classes, dtype=torch.long, device=scores.device)\n    labels = labels.view(1, -1).expand_as(scores)\n\n    bboxes = bboxes.reshape(-1, box_dim)\n    scores = scores.reshape(-1)\n    labels = labels.reshape(-1)\n\n    if not torch.onnx.is_in_onnx_export():\n        # NonZero not supported  in TensorRT\n        # remove low scoring boxes\n        valid_mask = scores > score_thr\n    # multiply score_factor after threshold to preserve more bboxes, improve\n    # mAP by 1% for YOLOv3\n    if score_factors is not None:\n        # expand the shape to match original shape of score\n        score_factors = score_factors.view(-1, 1).expand(\n            multi_scores.size(0), num_classes)\n        score_factors = score_factors.reshape(-1)\n        scores = scores * score_factors\n\n    if not torch.onnx.is_in_onnx_export():\n        # NonZero not supported  in TensorRT\n        inds = valid_mask.nonzero(as_tuple=False).squeeze(1)\n        bboxes, scores, labels = bboxes[inds], scores[inds], labels[inds]\n    else:\n        # TensorRT NMS plugin has invalid output filled with -1\n        # add dummy data to make detection output correct.\n        bboxes = torch.cat([bboxes, bboxes.new_zeros(1, box_dim)], dim=0)\n        scores = torch.cat([scores, scores.new_zeros(1)], dim=0)\n        labels = torch.cat([labels, labels.new_zeros(1)], dim=0)\n\n    if bboxes.numel() == 0:\n        if torch.onnx.is_in_onnx_export():\n            raise RuntimeError('[ONNX Error] Can not record NMS '\n                               'as it has not been executed this time')\n        dets = torch.cat([bboxes, scores[:, None]], -1)\n        if return_inds:\n            return dets, labels, inds\n        else:\n            return dets, labels\n\n    dets, keep = batched_nms(bboxes, scores, labels, nms_cfg)\n\n    if max_num > 0:\n        dets = dets[:max_num]\n        keep = keep[:max_num]\n\n    if return_inds:\n        return dets, labels[keep], inds[keep]\n    else:\n        return dets, labels[keep]\n\n\ndef fast_nms(\n    multi_bboxes: Tensor,\n    multi_scores: Tensor,\n    multi_coeffs: Tensor,\n    score_thr: float,\n    iou_thr: float,\n    top_k: int,\n    max_num: int = -1\n) -> Union[Tuple[Tensor, Tensor, Tensor], Tuple[Tensor, Tensor]]:\n    \"\"\"Fast NMS in `YOLACT <https://arxiv.org/abs/1904.02689>`_.\n\n    Fast NMS allows already-removed detections to suppress other detections so\n    that every instance can be decided to be kept or discarded in parallel,\n    which is not possible in traditional NMS. This relaxation allows us to\n    implement Fast NMS entirely in standard GPU-accelerated matrix operations.\n\n    Args:\n        multi_bboxes (Tensor): shape (n, #class*4) or (n, 4)\n        multi_scores (Tensor): shape (n, #class+1), where the last column\n            contains scores of the background class, but this will be ignored.\n        multi_coeffs (Tensor): shape (n, #class*coeffs_dim).\n        score_thr (float): bbox threshold, bboxes with scores lower than it\n            will not be considered.\n        iou_thr (float): IoU threshold to be considered as conflicted.\n        top_k (int): if there are more than top_k bboxes before NMS,\n            only top top_k will be kept.\n        max_num (int): if there are more than max_num bboxes after NMS,\n            only top max_num will be kept. If -1, keep all the bboxes.\n            Default: -1.\n\n    Returns:\n        Union[Tuple[Tensor, Tensor, Tensor], Tuple[Tensor, Tensor]]:\n            (dets, labels, coefficients), tensors of shape (k, 5), (k, 1),\n            and (k, coeffs_dim). Dets are boxes with scores.\n            Labels are 0-based.\n    \"\"\"\n\n    scores = multi_scores[:, :-1].t()  # [#class, n]\n    scores, idx = scores.sort(1, descending=True)\n\n    idx = idx[:, :top_k].contiguous()\n    scores = scores[:, :top_k]  # [#class, topk]\n    num_classes, num_dets = idx.size()\n    boxes = multi_bboxes[idx.view(-1), :].view(num_classes, num_dets, 4)\n    coeffs = multi_coeffs[idx.view(-1), :].view(num_classes, num_dets, -1)\n\n    iou = bbox_overlaps(boxes, boxes)  # [#class, topk, topk]\n    iou.triu_(diagonal=1)\n    iou_max, _ = iou.max(dim=1)\n\n    # Now just filter out the ones higher than the threshold\n    keep = iou_max <= iou_thr\n\n    # Second thresholding introduces 0.2 mAP gain at negligible time cost\n    keep *= scores > score_thr\n\n    # Assign each kept detection to its corresponding class\n    classes = torch.arange(\n        num_classes, device=boxes.device)[:, None].expand_as(keep)\n    classes = classes[keep]\n\n    boxes = boxes[keep]\n    coeffs = coeffs[keep]\n    scores = scores[keep]\n\n    # Only keep the top max_num highest scores across all classes\n    scores, idx = scores.sort(0, descending=True)\n    if max_num > 0:\n        idx = idx[:max_num]\n        scores = scores[:max_num]\n\n    classes = classes[idx]\n    boxes = boxes[idx]\n    coeffs = coeffs[idx]\n\n    cls_dets = torch.cat([boxes, scores[:, None]], dim=1)\n    return cls_dets, classes, coeffs\n"
  },
  {
    "path": "mmdet/models/layers/brick_wrappers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn.bricks.wrappers import NewEmptyTensorOp, obsolete_torch_version\n\nfrom mmdet.registry import MODELS\n\nif torch.__version__ == 'parrots':\n    TORCH_VERSION = torch.__version__\nelse:\n    # torch.__version__ could be 1.3.1+cu92, we only need the first two\n    # for comparison\n    TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2])\n\n\ndef adaptive_avg_pool2d(input, output_size):\n    \"\"\"Handle empty batch dimension to adaptive_avg_pool2d.\n\n    Args:\n        input (tensor): 4D tensor.\n        output_size (int, tuple[int,int]): the target output size.\n    \"\"\"\n    if input.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)):\n        if isinstance(output_size, int):\n            output_size = [output_size, output_size]\n        output_size = [*input.shape[:2], *output_size]\n        empty = NewEmptyTensorOp.apply(input, output_size)\n        return empty\n    else:\n        return F.adaptive_avg_pool2d(input, output_size)\n\n\nclass AdaptiveAvgPool2d(nn.AdaptiveAvgPool2d):\n    \"\"\"Handle empty batch dimension to AdaptiveAvgPool2d.\"\"\"\n\n    def forward(self, x):\n        # PyTorch 1.9 does not support empty tensor inference yet\n        if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)):\n            output_size = self.output_size\n            if isinstance(output_size, int):\n                output_size = [output_size, output_size]\n            else:\n                output_size = [\n                    v if v is not None else d\n                    for v, d in zip(output_size,\n                                    x.size()[-2:])\n                ]\n            output_size = [*x.shape[:2], *output_size]\n            empty = NewEmptyTensorOp.apply(x, output_size)\n            return empty\n\n        return super().forward(x)\n\n\n# Modified from\n# https://github.com/facebookresearch/detectron2/blob/main/detectron2/layers/batch_norm.py#L13 # noqa\n@MODELS.register_module('FrozenBN')\nclass FrozenBatchNorm2d(nn.Module):\n    \"\"\"BatchNorm2d where the batch statistics and the affine parameters are\n    fixed.\n\n    It contains non-trainable buffers called\n    \"weight\" and \"bias\", \"running_mean\", \"running_var\",\n    initialized to perform identity transformation.\n    Args:\n       num_features (int):  :math:`C` from an expected input of size\n            :math:`(N, C, H, W)`.\n       eps (float): a value added to the denominator for numerical stability.\n            Default: 1e-5\n    \"\"\"\n\n    def __init__(self, num_features, eps=1e-5, **kwargs):\n        super().__init__()\n        self.num_features = num_features\n        self.eps = eps\n        self.register_buffer('weight', torch.ones(num_features))\n        self.register_buffer('bias', torch.zeros(num_features))\n        self.register_buffer('running_mean', torch.zeros(num_features))\n        self.register_buffer('running_var', torch.ones(num_features) - eps)\n\n    def forward(self, x):\n        if x.requires_grad:\n            # When gradients are needed, F.batch_norm will use extra memory\n            # because its backward op computes gradients for weight/bias\n            # as well.\n            scale = self.weight * (self.running_var + self.eps).rsqrt()\n            bias = self.bias - self.running_mean * scale\n            scale = scale.reshape(1, -1, 1, 1)\n            bias = bias.reshape(1, -1, 1, 1)\n            out_dtype = x.dtype  # may be half\n            return x * scale.to(out_dtype) + bias.to(out_dtype)\n        else:\n            # When gradients are not needed, F.batch_norm is a single fused op\n            # and provide more optimization opportunities.\n            return F.batch_norm(\n                x,\n                self.running_mean,\n                self.running_var,\n                self.weight,\n                self.bias,\n                training=False,\n                eps=self.eps,\n            )\n\n    def __repr__(self):\n        return 'FrozenBatchNorm2d(num_features={}, eps={})'.format(\n            self.num_features, self.eps)\n\n    @classmethod\n    def convert_frozen_batchnorm(cls, module):\n        \"\"\"Convert all BatchNorm/SyncBatchNorm in module into FrozenBatchNorm.\n\n        Args:\n            module (torch.nn.Module):\n        Returns:\n            If module is BatchNorm/SyncBatchNorm, returns a new module.\n            Otherwise, in-place convert module and return it.\n        Similar to convert_sync_batchnorm in\n        https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/batchnorm.py\n        \"\"\"\n        bn_module = nn.modules.batchnorm\n        bn_module = (bn_module.BatchNorm2d, bn_module.SyncBatchNorm)\n        res = module\n        if isinstance(module, bn_module):\n            res = cls(module.num_features)\n            if module.affine:\n                res.weight.data = module.weight.data.clone().detach()\n                res.bias.data = module.bias.data.clone().detach()\n            res.running_mean.data = module.running_mean.data\n            res.running_var.data = module.running_var.data\n            res.eps = module.eps\n        else:\n            for name, child in module.named_children():\n                new_child = cls.convert_frozen_batchnorm(child)\n                if new_child is not child:\n                    res.add_module(name, new_child)\n        return res\n"
  },
  {
    "path": "mmdet/models/layers/conv_upsample.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule, ModuleList\n\n\nclass ConvUpsample(BaseModule):\n    \"\"\"ConvUpsample performs 2x upsampling after Conv.\n\n    There are several `ConvModule` layers. In the first few layers, upsampling\n    will be applied after each layer of convolution. The number of upsampling\n    must be no more than the number of ConvModule layers.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n        inner_channels (int): Number of channels produced by the convolution.\n        num_layers (int): Number of convolution layers.\n        num_upsample (int | optional): Number of upsampling layer. Must be no\n            more than num_layers. Upsampling will be applied after the first\n            ``num_upsample`` layers of convolution. Default: ``num_layers``.\n        conv_cfg (dict): Config dict for convolution layer. Default: None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer. Default: None.\n        init_cfg (dict): Config dict for initialization. Default: None.\n        kwargs (key word augments): Other augments used in ConvModule.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 inner_channels,\n                 num_layers=1,\n                 num_upsample=None,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 init_cfg=None,\n                 **kwargs):\n        super(ConvUpsample, self).__init__(init_cfg)\n        if num_upsample is None:\n            num_upsample = num_layers\n        assert num_upsample <= num_layers, \\\n            f'num_upsample({num_upsample})must be no more than ' \\\n            f'num_layers({num_layers})'\n        self.num_layers = num_layers\n        self.num_upsample = num_upsample\n        self.conv = ModuleList()\n        for i in range(num_layers):\n            self.conv.append(\n                ConvModule(\n                    in_channels,\n                    inner_channels,\n                    3,\n                    padding=1,\n                    stride=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    **kwargs))\n            in_channels = inner_channels\n\n    def forward(self, x):\n        num_upsample = self.num_upsample\n        for i in range(self.num_layers):\n            x = self.conv[i](x)\n            if num_upsample > 0:\n                num_upsample -= 1\n                x = F.interpolate(\n                    x, scale_factor=2, mode='bilinear', align_corners=False)\n        return x\n"
  },
  {
    "path": "mmdet/models/layers/csp_layer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom .se_layer import ChannelAttention\n\n\nclass DarknetBottleneck(BaseModule):\n    \"\"\"The basic bottleneck block used in Darknet.\n\n    Each ResBlock consists of two ConvModules and the input is added to the\n    final output. Each ConvModule is composed of Conv, BN, and LeakyReLU.\n    The first convLayer has filter size of 1x1 and the second one has the\n    filter size of 3x3.\n\n    Args:\n        in_channels (int): The input channels of this Module.\n        out_channels (int): The output channels of this Module.\n        expansion (float): The kernel size of the convolution.\n            Defaults to 0.5.\n        add_identity (bool): Whether to add identity to the out.\n            Defaults to True.\n        use_depthwise (bool): Whether to use depthwise separable convolution.\n            Defaults to False.\n        conv_cfg (dict): Config dict for convolution layer. Defaults to None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Defaults to dict(type='BN').\n        act_cfg (dict): Config dict for activation layer.\n            Defaults to dict(type='Swish').\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 expansion: float = 0.5,\n                 add_identity: bool = True,\n                 use_depthwise: bool = False,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(\n                     type='BN', momentum=0.03, eps=0.001),\n                 act_cfg: ConfigType = dict(type='Swish'),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        hidden_channels = int(out_channels * expansion)\n        conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule\n        self.conv1 = ConvModule(\n            in_channels,\n            hidden_channels,\n            1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.conv2 = conv(\n            hidden_channels,\n            out_channels,\n            3,\n            stride=1,\n            padding=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.add_identity = \\\n            add_identity and in_channels == out_channels\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function.\"\"\"\n        identity = x\n        out = self.conv1(x)\n        out = self.conv2(out)\n\n        if self.add_identity:\n            return out + identity\n        else:\n            return out\n\n\nclass CSPNeXtBlock(BaseModule):\n    \"\"\"The basic bottleneck block used in CSPNeXt.\n\n    Args:\n        in_channels (int): The input channels of this Module.\n        out_channels (int): The output channels of this Module.\n        expansion (float): Expand ratio of the hidden channel. Defaults to 0.5.\n        add_identity (bool): Whether to add identity to the out. Only works\n            when in_channels == out_channels. Defaults to True.\n        use_depthwise (bool): Whether to use depthwise separable convolution.\n            Defaults to False.\n        kernel_size (int): The kernel size of the second convolution layer.\n            Defaults to 5.\n        conv_cfg (dict): Config dict for convolution layer. Defaults to None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Defaults to dict(type='BN', momentum=0.03, eps=0.001).\n        act_cfg (dict): Config dict for activation layer.\n            Defaults to dict(type='SiLU').\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 expansion: float = 0.5,\n                 add_identity: bool = True,\n                 use_depthwise: bool = False,\n                 kernel_size: int = 5,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(\n                     type='BN', momentum=0.03, eps=0.001),\n                 act_cfg: ConfigType = dict(type='SiLU'),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        hidden_channels = int(out_channels * expansion)\n        conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule\n        self.conv1 = conv(\n            in_channels,\n            hidden_channels,\n            3,\n            stride=1,\n            padding=1,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.conv2 = DepthwiseSeparableConvModule(\n            hidden_channels,\n            out_channels,\n            kernel_size,\n            stride=1,\n            padding=kernel_size // 2,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.add_identity = \\\n            add_identity and in_channels == out_channels\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function.\"\"\"\n        identity = x\n        out = self.conv1(x)\n        out = self.conv2(out)\n\n        if self.add_identity:\n            return out + identity\n        else:\n            return out\n\n\nclass CSPLayer(BaseModule):\n    \"\"\"Cross Stage Partial Layer.\n\n    Args:\n        in_channels (int): The input channels of the CSP layer.\n        out_channels (int): The output channels of the CSP layer.\n        expand_ratio (float): Ratio to adjust the number of channels of the\n            hidden layer. Defaults to 0.5.\n        num_blocks (int): Number of blocks. Defaults to 1.\n        add_identity (bool): Whether to add identity in blocks.\n            Defaults to True.\n        use_cspnext_block (bool): Whether to use CSPNeXt block.\n            Defaults to False.\n        use_depthwise (bool): Whether to use depthwise separable convolution in\n            blocks. Defaults to False.\n        channel_attention (bool): Whether to add channel attention in each\n            stage. Defaults to True.\n        conv_cfg (dict, optional): Config dict for convolution layer.\n            Defaults to None, which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Defaults to dict(type='BN')\n        act_cfg (dict): Config dict for activation layer.\n            Defaults to dict(type='Swish')\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 expand_ratio: float = 0.5,\n                 num_blocks: int = 1,\n                 add_identity: bool = True,\n                 use_depthwise: bool = False,\n                 use_cspnext_block: bool = False,\n                 channel_attention: bool = False,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(\n                     type='BN', momentum=0.03, eps=0.001),\n                 act_cfg: ConfigType = dict(type='Swish'),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        block = CSPNeXtBlock if use_cspnext_block else DarknetBottleneck\n        mid_channels = int(out_channels * expand_ratio)\n        self.channel_attention = channel_attention\n        self.main_conv = ConvModule(\n            in_channels,\n            mid_channels,\n            1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.short_conv = ConvModule(\n            in_channels,\n            mid_channels,\n            1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.final_conv = ConvModule(\n            2 * mid_channels,\n            out_channels,\n            1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n\n        self.blocks = nn.Sequential(*[\n            block(\n                mid_channels,\n                mid_channels,\n                1.0,\n                add_identity,\n                use_depthwise,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg) for _ in range(num_blocks)\n        ])\n        if channel_attention:\n            self.attention = ChannelAttention(2 * mid_channels)\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function.\"\"\"\n        x_short = self.short_conv(x)\n\n        x_main = self.main_conv(x)\n        x_main = self.blocks(x_main)\n\n        x_final = torch.cat((x_main, x_short), dim=1)\n\n        if self.channel_attention:\n            x_final = self.attention(x_final)\n        return self.final_conv(x_final)\n"
  },
  {
    "path": "mmdet/models/layers/dropblock.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mmdet.registry import MODELS\n\neps = 1e-6\n\n\n@MODELS.register_module()\nclass DropBlock(nn.Module):\n    \"\"\"Randomly drop some regions of feature maps.\n\n     Please refer to the method proposed in `DropBlock\n     <https://arxiv.org/abs/1810.12890>`_ for details.\n\n    Args:\n        drop_prob (float): The probability of dropping each block.\n        block_size (int): The size of dropped blocks.\n        warmup_iters (int): The drop probability will linearly increase\n            from `0` to `drop_prob` during the first `warmup_iters` iterations.\n            Default: 2000.\n    \"\"\"\n\n    def __init__(self, drop_prob, block_size, warmup_iters=2000, **kwargs):\n        super(DropBlock, self).__init__()\n        assert block_size % 2 == 1\n        assert 0 < drop_prob <= 1\n        assert warmup_iters >= 0\n        self.drop_prob = drop_prob\n        self.block_size = block_size\n        self.warmup_iters = warmup_iters\n        self.iter_cnt = 0\n\n    def forward(self, x):\n        \"\"\"\n        Args:\n            x (Tensor): Input feature map on which some areas will be randomly\n                dropped.\n\n        Returns:\n            Tensor: The tensor after DropBlock layer.\n        \"\"\"\n        if not self.training:\n            return x\n        self.iter_cnt += 1\n        N, C, H, W = list(x.shape)\n        gamma = self._compute_gamma((H, W))\n        mask_shape = (N, C, H - self.block_size + 1, W - self.block_size + 1)\n        mask = torch.bernoulli(torch.full(mask_shape, gamma, device=x.device))\n\n        mask = F.pad(mask, [self.block_size // 2] * 4, value=0)\n        mask = F.max_pool2d(\n            input=mask,\n            stride=(1, 1),\n            kernel_size=(self.block_size, self.block_size),\n            padding=self.block_size // 2)\n        mask = 1 - mask\n        x = x * mask * mask.numel() / (eps + mask.sum())\n        return x\n\n    def _compute_gamma(self, feat_size):\n        \"\"\"Compute the value of gamma according to paper. gamma is the\n        parameter of bernoulli distribution, which controls the number of\n        features to drop.\n\n        gamma = (drop_prob * fm_area) / (drop_area * keep_area)\n\n        Args:\n            feat_size (tuple[int, int]): The height and width of feature map.\n\n        Returns:\n            float: The value of gamma.\n        \"\"\"\n        gamma = (self.drop_prob * feat_size[0] * feat_size[1])\n        gamma /= ((feat_size[0] - self.block_size + 1) *\n                  (feat_size[1] - self.block_size + 1))\n        gamma /= (self.block_size**2)\n        factor = (1.0 if self.iter_cnt > self.warmup_iters else self.iter_cnt /\n                  self.warmup_iters)\n        return gamma * factor\n\n    def extra_repr(self):\n        return (f'drop_prob={self.drop_prob}, block_size={self.block_size}, '\n                f'warmup_iters={self.warmup_iters}')\n"
  },
  {
    "path": "mmdet/models/layers/ema.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.model import ExponentialMovingAverage\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass ExpMomentumEMA(ExponentialMovingAverage):\n    \"\"\"Exponential moving average (EMA) with exponential momentum strategy,\n    which is used in YOLOX.\n\n    Args:\n        model (nn.Module): The model to be averaged.\n        momentum (float): The momentum used for updating ema parameter.\n            Ema's parameter are updated with the formula:\n           `averaged_param = (1-momentum) * averaged_param + momentum *\n           source_param`. Defaults to 0.0002.\n        gamma (int): Use a larger momentum early in training and gradually\n            annealing to a smaller value to update the ema model smoothly. The\n            momentum is calculated as\n            `(1 - momentum) * exp(-(1 + steps) / gamma) + momentum`.\n            Defaults to 2000.\n        interval (int): Interval between two updates. Defaults to 1.\n        device (torch.device, optional): If provided, the averaged model will\n            be stored on the :attr:`device`. Defaults to None.\n        update_buffers (bool): if True, it will compute running averages for\n            both the parameters and the buffers of the model. Defaults to\n            False.\n    \"\"\"\n\n    def __init__(self,\n                 model: nn.Module,\n                 momentum: float = 0.0002,\n                 gamma: int = 2000,\n                 interval=1,\n                 device: Optional[torch.device] = None,\n                 update_buffers: bool = False) -> None:\n        super().__init__(\n            model=model,\n            momentum=momentum,\n            interval=interval,\n            device=device,\n            update_buffers=update_buffers)\n        assert gamma > 0, f'gamma must be greater than 0, but got {gamma}'\n        self.gamma = gamma\n\n    def avg_func(self, averaged_param: Tensor, source_param: Tensor,\n                 steps: int) -> None:\n        \"\"\"Compute the moving average of the parameters using the exponential\n        momentum strategy.\n\n        Args:\n            averaged_param (Tensor): The averaged parameters.\n            source_param (Tensor): The source parameters.\n            steps (int): The number of times the parameters have been\n                updated.\n        \"\"\"\n        momentum = (1 - self.momentum) * math.exp(\n            -float(1 + steps) / self.gamma) + self.momentum\n        averaged_param.lerp_(source_param, momentum)\n"
  },
  {
    "path": "mmdet/models/layers/inverted_residual.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nimport torch.utils.checkpoint as cp\nfrom mmcv.cnn import ConvModule\nfrom mmcv.cnn.bricks import DropPath\nfrom mmengine.model import BaseModule\n\nfrom .se_layer import SELayer\n\n\nclass InvertedResidual(BaseModule):\n    \"\"\"Inverted Residual Block.\n\n    Args:\n        in_channels (int): The input channels of this Module.\n        out_channels (int): The output channels of this Module.\n        mid_channels (int): The input channels of the depthwise convolution.\n        kernel_size (int): The kernel size of the depthwise convolution.\n            Default: 3.\n        stride (int): The stride of the depthwise convolution. Default: 1.\n        se_cfg (dict): Config dict for se layer. Default: None, which means no\n            se layer.\n        with_expand_conv (bool): Use expand conv or not. If set False,\n            mid_channels must be the same with in_channels.\n            Default: True.\n        conv_cfg (dict): Config dict for convolution layer. Default: None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='BN').\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='ReLU').\n        drop_path_rate (float): stochastic depth rate. Defaults to 0.\n        with_cp (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed. Default: False.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n\n    Returns:\n        Tensor: The output tensor.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 mid_channels,\n                 kernel_size=3,\n                 stride=1,\n                 se_cfg=None,\n                 with_expand_conv=True,\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN'),\n                 act_cfg=dict(type='ReLU'),\n                 drop_path_rate=0.,\n                 with_cp=False,\n                 init_cfg=None):\n        super(InvertedResidual, self).__init__(init_cfg)\n        self.with_res_shortcut = (stride == 1 and in_channels == out_channels)\n        assert stride in [1, 2], f'stride must in [1, 2]. ' \\\n            f'But received {stride}.'\n        self.with_cp = with_cp\n        self.drop_path = DropPath(\n            drop_path_rate) if drop_path_rate > 0 else nn.Identity()\n        self.with_se = se_cfg is not None\n        self.with_expand_conv = with_expand_conv\n\n        if self.with_se:\n            assert isinstance(se_cfg, dict)\n        if not self.with_expand_conv:\n            assert mid_channels == in_channels\n\n        if self.with_expand_conv:\n            self.expand_conv = ConvModule(\n                in_channels=in_channels,\n                out_channels=mid_channels,\n                kernel_size=1,\n                stride=1,\n                padding=0,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n        self.depthwise_conv = ConvModule(\n            in_channels=mid_channels,\n            out_channels=mid_channels,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=kernel_size // 2,\n            groups=mid_channels,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n\n        if self.with_se:\n            self.se = SELayer(**se_cfg)\n\n        self.linear_conv = ConvModule(\n            in_channels=mid_channels,\n            out_channels=out_channels,\n            kernel_size=1,\n            stride=1,\n            padding=0,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=None)\n\n    def forward(self, x):\n\n        def _inner_forward(x):\n            out = x\n\n            if self.with_expand_conv:\n                out = self.expand_conv(out)\n\n            out = self.depthwise_conv(out)\n\n            if self.with_se:\n                out = self.se(out)\n\n            out = self.linear_conv(out)\n\n            if self.with_res_shortcut:\n                return x + self.drop_path(out)\n            else:\n                return out\n\n        if self.with_cp and x.requires_grad:\n            out = cp.checkpoint(_inner_forward, x)\n        else:\n            out = _inner_forward(x)\n\n        return out\n"
  },
  {
    "path": "mmdet/models/layers/matrix_nms.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\n\n\ndef mask_matrix_nms(masks,\n                    labels,\n                    scores,\n                    filter_thr=-1,\n                    nms_pre=-1,\n                    max_num=-1,\n                    kernel='gaussian',\n                    sigma=2.0,\n                    mask_area=None):\n    \"\"\"Matrix NMS for multi-class masks.\n\n    Args:\n        masks (Tensor): Has shape (num_instances, h, w)\n        labels (Tensor): Labels of corresponding masks,\n            has shape (num_instances,).\n        scores (Tensor): Mask scores of corresponding masks,\n            has shape (num_instances).\n        filter_thr (float): Score threshold to filter the masks\n            after matrix nms. Default: -1, which means do not\n            use filter_thr.\n        nms_pre (int): The max number of instances to do the matrix nms.\n            Default: -1, which means do not use nms_pre.\n        max_num (int, optional): If there are more than max_num masks after\n            matrix, only top max_num will be kept. Default: -1, which means\n            do not use max_num.\n        kernel (str): 'linear' or 'gaussian'.\n        sigma (float): std in gaussian method.\n        mask_area (Tensor): The sum of seg_masks.\n\n    Returns:\n        tuple(Tensor): Processed mask results.\n\n            - scores (Tensor): Updated scores, has shape (n,).\n            - labels (Tensor): Remained labels, has shape (n,).\n            - masks (Tensor): Remained masks, has shape (n, w, h).\n            - keep_inds (Tensor): The indices number of\n                the remaining mask in the input mask, has shape (n,).\n    \"\"\"\n    assert len(labels) == len(masks) == len(scores)\n    if len(labels) == 0:\n        return scores.new_zeros(0), labels.new_zeros(0), masks.new_zeros(\n            0, *masks.shape[-2:]), labels.new_zeros(0)\n    if mask_area is None:\n        mask_area = masks.sum((1, 2)).float()\n    else:\n        assert len(masks) == len(mask_area)\n\n    # sort and keep top nms_pre\n    scores, sort_inds = torch.sort(scores, descending=True)\n\n    keep_inds = sort_inds\n    if nms_pre > 0 and len(sort_inds) > nms_pre:\n        sort_inds = sort_inds[:nms_pre]\n        keep_inds = keep_inds[:nms_pre]\n        scores = scores[:nms_pre]\n    masks = masks[sort_inds]\n    mask_area = mask_area[sort_inds]\n    labels = labels[sort_inds]\n\n    num_masks = len(labels)\n    flatten_masks = masks.reshape(num_masks, -1).float()\n    # inter.\n    inter_matrix = torch.mm(flatten_masks, flatten_masks.transpose(1, 0))\n    expanded_mask_area = mask_area.expand(num_masks, num_masks)\n    # Upper triangle iou matrix.\n    iou_matrix = (inter_matrix /\n                  (expanded_mask_area + expanded_mask_area.transpose(1, 0) -\n                   inter_matrix)).triu(diagonal=1)\n    # label_specific matrix.\n    expanded_labels = labels.expand(num_masks, num_masks)\n    # Upper triangle label matrix.\n    label_matrix = (expanded_labels == expanded_labels.transpose(\n        1, 0)).triu(diagonal=1)\n\n    # IoU compensation\n    compensate_iou, _ = (iou_matrix * label_matrix).max(0)\n    compensate_iou = compensate_iou.expand(num_masks,\n                                           num_masks).transpose(1, 0)\n\n    # IoU decay\n    decay_iou = iou_matrix * label_matrix\n\n    # Calculate the decay_coefficient\n    if kernel == 'gaussian':\n        decay_matrix = torch.exp(-1 * sigma * (decay_iou**2))\n        compensate_matrix = torch.exp(-1 * sigma * (compensate_iou**2))\n        decay_coefficient, _ = (decay_matrix / compensate_matrix).min(0)\n    elif kernel == 'linear':\n        decay_matrix = (1 - decay_iou) / (1 - compensate_iou)\n        decay_coefficient, _ = decay_matrix.min(0)\n    else:\n        raise NotImplementedError(\n            f'{kernel} kernel is not supported in matrix nms!')\n    # update the score.\n    scores = scores * decay_coefficient\n\n    if filter_thr > 0:\n        keep = scores >= filter_thr\n        keep_inds = keep_inds[keep]\n        if not keep.any():\n            return scores.new_zeros(0), labels.new_zeros(0), masks.new_zeros(\n                0, *masks.shape[-2:]), labels.new_zeros(0)\n        masks = masks[keep]\n        scores = scores[keep]\n        labels = labels[keep]\n\n    # sort and keep top max_num\n    scores, sort_inds = torch.sort(scores, descending=True)\n    keep_inds = keep_inds[sort_inds]\n    if max_num > 0 and len(sort_inds) > max_num:\n        sort_inds = sort_inds[:max_num]\n        keep_inds = keep_inds[:max_num]\n        scores = scores[:max_num]\n    masks = masks[sort_inds]\n    labels = labels[sort_inds]\n\n    return scores, labels, masks, keep_inds\n"
  },
  {
    "path": "mmdet/models/layers/msdeformattn_pixel_decoder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Conv2d, ConvModule\nfrom mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention\nfrom mmengine.model import (BaseModule, ModuleList, caffe2_xavier_init,\n                            normal_init, xavier_init)\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptMultiConfig\nfrom ..task_modules.prior_generators import MlvlPointGenerator\nfrom .positional_encoding import SinePositionalEncoding\nfrom .transformer import Mask2FormerTransformerEncoder\n\n\n@MODELS.register_module()\nclass MSDeformAttnPixelDecoder(BaseModule):\n    \"\"\"Pixel decoder with multi-scale deformable attention.\n\n    Args:\n        in_channels (list[int] | tuple[int]): Number of channels in the\n            input feature maps.\n        strides (list[int] | tuple[int]): Output strides of feature from\n            backbone.\n        feat_channels (int): Number of channels for feature.\n        out_channels (int): Number of channels for output.\n        num_outs (int): Number of output scales.\n        norm_cfg (:obj:`ConfigDict` or dict): Config for normalization.\n            Defaults to dict(type='GN', num_groups=32).\n        act_cfg (:obj:`ConfigDict` or dict): Config for activation.\n            Defaults to dict(type='ReLU').\n        encoder (:obj:`ConfigDict` or dict): Config for transformer\n            encoder. Defaults to None.\n        positional_encoding (:obj:`ConfigDict` or dict): Config for\n            transformer encoder position encoding. Defaults to\n            dict(num_feats=128, normalize=True).\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: Union[List[int],\n                                    Tuple[int]] = [256, 512, 1024, 2048],\n                 strides: Union[List[int], Tuple[int]] = [4, 8, 16, 32],\n                 feat_channels: int = 256,\n                 out_channels: int = 256,\n                 num_outs: int = 3,\n                 norm_cfg: ConfigType = dict(type='GN', num_groups=32),\n                 act_cfg: ConfigType = dict(type='ReLU'),\n                 encoder: ConfigType = None,\n                 positional_encoding: ConfigType = dict(\n                     num_feats=128, normalize=True),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.strides = strides\n        self.num_input_levels = len(in_channels)\n        self.num_encoder_levels = \\\n            encoder.layer_cfg.self_attn_cfg.num_levels\n        assert self.num_encoder_levels >= 1, \\\n            'num_levels in attn_cfgs must be at least one'\n        input_conv_list = []\n        # from top to down (low to high resolution)\n        for i in range(self.num_input_levels - 1,\n                       self.num_input_levels - self.num_encoder_levels - 1,\n                       -1):\n            input_conv = ConvModule(\n                in_channels[i],\n                feat_channels,\n                kernel_size=1,\n                norm_cfg=norm_cfg,\n                act_cfg=None,\n                bias=True)\n            input_conv_list.append(input_conv)\n        self.input_convs = ModuleList(input_conv_list)\n\n        self.encoder = Mask2FormerTransformerEncoder(**encoder)\n        self.postional_encoding = SinePositionalEncoding(**positional_encoding)\n        # high resolution to low resolution\n        self.level_encoding = nn.Embedding(self.num_encoder_levels,\n                                           feat_channels)\n\n        # fpn-like structure\n        self.lateral_convs = ModuleList()\n        self.output_convs = ModuleList()\n        self.use_bias = norm_cfg is None\n        # from top to down (low to high resolution)\n        # fpn for the rest features that didn't pass in encoder\n        for i in range(self.num_input_levels - self.num_encoder_levels - 1, -1,\n                       -1):\n            lateral_conv = ConvModule(\n                in_channels[i],\n                feat_channels,\n                kernel_size=1,\n                bias=self.use_bias,\n                norm_cfg=norm_cfg,\n                act_cfg=None)\n            output_conv = ConvModule(\n                feat_channels,\n                feat_channels,\n                kernel_size=3,\n                stride=1,\n                padding=1,\n                bias=self.use_bias,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n            self.lateral_convs.append(lateral_conv)\n            self.output_convs.append(output_conv)\n\n        self.mask_feature = Conv2d(\n            feat_channels, out_channels, kernel_size=1, stride=1, padding=0)\n\n        self.num_outs = num_outs\n        self.point_generator = MlvlPointGenerator(strides)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights.\"\"\"\n        for i in range(0, self.num_encoder_levels):\n            xavier_init(\n                self.input_convs[i].conv,\n                gain=1,\n                bias=0,\n                distribution='uniform')\n\n        for i in range(0, self.num_input_levels - self.num_encoder_levels):\n            caffe2_xavier_init(self.lateral_convs[i].conv, bias=0)\n            caffe2_xavier_init(self.output_convs[i].conv, bias=0)\n\n        caffe2_xavier_init(self.mask_feature, bias=0)\n\n        normal_init(self.level_encoding, mean=0, std=1)\n        for p in self.encoder.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_normal_(p)\n\n        # init_weights defined in MultiScaleDeformableAttention\n        for m in self.encoder.layers.modules():\n            if isinstance(m, MultiScaleDeformableAttention):\n                m.init_weights()\n\n    def forward(self, feats: List[Tensor]) -> Tuple[Tensor, Tensor]:\n        \"\"\"\n        Args:\n            feats (list[Tensor]): Feature maps of each level. Each has\n                shape of (batch_size, c, h, w).\n\n        Returns:\n            tuple: A tuple containing the following:\n\n                - mask_feature (Tensor): shape (batch_size, c, h, w).\n                - multi_scale_features (list[Tensor]): Multi scale \\\n                        features, each in shape (batch_size, c, h, w).\n        \"\"\"\n        # generate padding mask for each level, for each image\n        batch_size = feats[0].shape[0]\n        encoder_input_list = []\n        padding_mask_list = []\n        level_positional_encoding_list = []\n        spatial_shapes = []\n        reference_points_list = []\n        for i in range(self.num_encoder_levels):\n            level_idx = self.num_input_levels - i - 1\n            feat = feats[level_idx]\n            feat_projected = self.input_convs[i](feat)\n            feat_hw = torch._shape_as_tensor(feat)[2:].to(feat.device)\n\n            # no padding\n            padding_mask_resized = feat.new_zeros(\n                (batch_size, ) + feat.shape[-2:], dtype=torch.bool)\n            pos_embed = self.postional_encoding(padding_mask_resized)\n            level_embed = self.level_encoding.weight[i]\n            level_pos_embed = level_embed.view(1, -1, 1, 1) + pos_embed\n            # (h_i * w_i, 2)\n            reference_points = self.point_generator.single_level_grid_priors(\n                feat.shape[-2:], level_idx, device=feat.device)\n            # normalize\n            feat_wh = feat_hw.unsqueeze(0).flip(dims=[0, 1])\n            factor = feat_wh * self.strides[level_idx]\n            reference_points = reference_points / factor\n\n            # shape (batch_size, c, h_i, w_i) -> (h_i * w_i, batch_size, c)\n            feat_projected = feat_projected.flatten(2).permute(0, 2, 1)\n            level_pos_embed = level_pos_embed.flatten(2).permute(0, 2, 1)\n            padding_mask_resized = padding_mask_resized.flatten(1)\n\n            encoder_input_list.append(feat_projected)\n            padding_mask_list.append(padding_mask_resized)\n            level_positional_encoding_list.append(level_pos_embed)\n            spatial_shapes.append(feat_hw)\n            reference_points_list.append(reference_points)\n        # shape (batch_size, total_num_queries),\n        # total_num_queries=sum([., h_i * w_i,.])\n        padding_masks = torch.cat(padding_mask_list, dim=1)\n        # shape (total_num_queries, batch_size, c)\n        encoder_inputs = torch.cat(encoder_input_list, dim=1)\n        level_positional_encodings = torch.cat(\n            level_positional_encoding_list, dim=1)\n        # shape (num_encoder_levels, 2), from low\n        # resolution to high resolution\n        num_queries_per_level = [e[0] * e[1] for e in spatial_shapes]\n        spatial_shapes = torch.cat(spatial_shapes).view(-1, 2)\n        # shape (0, h_0*w_0, h_0*w_0+h_1*w_1, ...)\n        level_start_index = torch.cat((spatial_shapes.new_zeros(\n            (1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))\n        reference_points = torch.cat(reference_points_list, dim=0)\n        reference_points = reference_points[None, :, None].repeat(\n            batch_size, 1, self.num_encoder_levels, 1)\n        valid_radios = reference_points.new_ones(\n            (batch_size, self.num_encoder_levels, 2))\n        # shape (num_total_queries, batch_size, c)\n        memory = self.encoder(\n            query=encoder_inputs,\n            query_pos=level_positional_encodings,\n            key_padding_mask=padding_masks,\n            spatial_shapes=spatial_shapes,\n            reference_points=reference_points,\n            level_start_index=level_start_index,\n            valid_ratios=valid_radios)\n        # (batch_size, c, num_total_queries)\n        memory = memory.permute(0, 2, 1)\n\n        # from low resolution to high resolution\n        outs = torch.split(memory, num_queries_per_level, dim=-1)\n        outs = [\n            x.reshape(batch_size, -1, spatial_shapes[i][0],\n                      spatial_shapes[i][1]) for i, x in enumerate(outs)\n        ]\n\n        for i in range(self.num_input_levels - self.num_encoder_levels - 1, -1,\n                       -1):\n            x = feats[i]\n            cur_feat = self.lateral_convs[i](x)\n            y = cur_feat + F.interpolate(\n                outs[-1],\n                size=cur_feat.shape[-2:],\n                mode='bilinear',\n                align_corners=False)\n            y = self.output_convs[i](y)\n            outs.append(y)\n        multi_scale_features = outs[:self.num_outs]\n\n        mask_feature = self.mask_feature(outs[-1])\n        return mask_feature, multi_scale_features\n"
  },
  {
    "path": "mmdet/models/layers/normed_predictor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.utils import digit_version\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\n\nMODELS.register_module('Linear', module=nn.Linear)\n\n\n@MODELS.register_module(name='NormedLinear')\nclass NormedLinear(nn.Linear):\n    \"\"\"Normalized Linear Layer.\n\n    Args:\n        tempeature (float, optional): Tempeature term. Defaults to 20.\n        power (int, optional): Power term. Defaults to 1.0.\n        eps (float, optional): The minimal value of divisor to\n             keep numerical stability. Defaults to 1e-6.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 tempearture: float = 20,\n                 power: int = 1.0,\n                 eps: float = 1e-6,\n                 **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        self.tempearture = tempearture\n        self.power = power\n        self.eps = eps\n        self.init_weights()\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize the weights.\"\"\"\n        nn.init.normal_(self.weight, mean=0, std=0.01)\n        if self.bias is not None:\n            nn.init.constant_(self.bias, 0)\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function for `NormedLinear`.\"\"\"\n        weight_ = self.weight / (\n            self.weight.norm(dim=1, keepdim=True).pow(self.power) + self.eps)\n        x_ = x / (x.norm(dim=1, keepdim=True).pow(self.power) + self.eps)\n        x_ = x_ * self.tempearture\n\n        return F.linear(x_, weight_, self.bias)\n\n\n@MODELS.register_module(name='NormedConv2d')\nclass NormedConv2d(nn.Conv2d):\n    \"\"\"Normalized Conv2d Layer.\n\n    Args:\n        tempeature (float, optional): Tempeature term. Defaults to 20.\n        power (int, optional): Power term. Defaults to 1.0.\n        eps (float, optional): The minimal value of divisor to\n             keep numerical stability. Defaults to 1e-6.\n        norm_over_kernel (bool, optional): Normalize over kernel.\n             Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 tempearture: float = 20,\n                 power: int = 1.0,\n                 eps: float = 1e-6,\n                 norm_over_kernel: bool = False,\n                 **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        self.tempearture = tempearture\n        self.power = power\n        self.norm_over_kernel = norm_over_kernel\n        self.eps = eps\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function for `NormedConv2d`.\"\"\"\n        if not self.norm_over_kernel:\n            weight_ = self.weight / (\n                self.weight.norm(dim=1, keepdim=True).pow(self.power) +\n                self.eps)\n        else:\n            weight_ = self.weight / (\n                self.weight.view(self.weight.size(0), -1).norm(\n                    dim=1, keepdim=True).pow(self.power)[..., None, None] +\n                self.eps)\n        x_ = x / (x.norm(dim=1, keepdim=True).pow(self.power) + self.eps)\n        x_ = x_ * self.tempearture\n\n        if hasattr(self, 'conv2d_forward'):\n            x_ = self.conv2d_forward(x_, weight_)\n        else:\n            if digit_version(torch.__version__) >= digit_version('1.8'):\n                x_ = self._conv_forward(x_, weight_, self.bias)\n            else:\n                x_ = self._conv_forward(x_, weight_)\n        return x_\n"
  },
  {
    "path": "mmdet/models/layers/pixel_decoder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Conv2d, ConvModule\nfrom mmengine.model import BaseModule, ModuleList, caffe2_xavier_init\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptMultiConfig\nfrom .positional_encoding import SinePositionalEncoding\nfrom .transformer import DetrTransformerEncoder\n\n\n@MODELS.register_module()\nclass PixelDecoder(BaseModule):\n    \"\"\"Pixel decoder with a structure like fpn.\n\n    Args:\n        in_channels (list[int] | tuple[int]): Number of channels in the\n            input feature maps.\n        feat_channels (int): Number channels for feature.\n        out_channels (int): Number channels for output.\n        norm_cfg (:obj:`ConfigDict` or dict): Config for normalization.\n            Defaults to dict(type='GN', num_groups=32).\n        act_cfg (:obj:`ConfigDict` or dict): Config for activation.\n            Defaults to dict(type='ReLU').\n        encoder (:obj:`ConfigDict` or dict): Config for transorformer\n            encoder.Defaults to None.\n        positional_encoding (:obj:`ConfigDict` or dict): Config for\n            transformer encoder position encoding. Defaults to\n            dict(type='SinePositionalEncoding', num_feats=128,\n            normalize=True).\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: Union[List[int], Tuple[int]],\n                 feat_channels: int,\n                 out_channels: int,\n                 norm_cfg: ConfigType = dict(type='GN', num_groups=32),\n                 act_cfg: ConfigType = dict(type='ReLU'),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.num_inputs = len(in_channels)\n        self.lateral_convs = ModuleList()\n        self.output_convs = ModuleList()\n        self.use_bias = norm_cfg is None\n        for i in range(0, self.num_inputs - 1):\n            lateral_conv = ConvModule(\n                in_channels[i],\n                feat_channels,\n                kernel_size=1,\n                bias=self.use_bias,\n                norm_cfg=norm_cfg,\n                act_cfg=None)\n            output_conv = ConvModule(\n                feat_channels,\n                feat_channels,\n                kernel_size=3,\n                stride=1,\n                padding=1,\n                bias=self.use_bias,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n            self.lateral_convs.append(lateral_conv)\n            self.output_convs.append(output_conv)\n\n        self.last_feat_conv = ConvModule(\n            in_channels[-1],\n            feat_channels,\n            kernel_size=3,\n            padding=1,\n            stride=1,\n            bias=self.use_bias,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n        self.mask_feature = Conv2d(\n            feat_channels, out_channels, kernel_size=3, stride=1, padding=1)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights.\"\"\"\n        for i in range(0, self.num_inputs - 2):\n            caffe2_xavier_init(self.lateral_convs[i].conv, bias=0)\n            caffe2_xavier_init(self.output_convs[i].conv, bias=0)\n\n        caffe2_xavier_init(self.mask_feature, bias=0)\n        caffe2_xavier_init(self.last_feat_conv, bias=0)\n\n    def forward(self, feats: List[Tensor],\n                batch_img_metas: List[dict]) -> Tuple[Tensor, Tensor]:\n        \"\"\"\n        Args:\n            feats (list[Tensor]): Feature maps of each level. Each has\n                shape of (batch_size, c, h, w).\n            batch_img_metas (list[dict]): List of image information.\n                Pass in for creating more accurate padding mask. Not\n                used here.\n\n        Returns:\n            tuple[Tensor, Tensor]: a tuple containing the following:\n\n                - mask_feature (Tensor): Shape (batch_size, c, h, w).\n                - memory (Tensor): Output of last stage of backbone.\\\n                        Shape (batch_size, c, h, w).\n        \"\"\"\n        y = self.last_feat_conv(feats[-1])\n        for i in range(self.num_inputs - 2, -1, -1):\n            x = feats[i]\n            cur_feat = self.lateral_convs[i](x)\n            y = cur_feat + \\\n                F.interpolate(y, size=cur_feat.shape[-2:], mode='nearest')\n            y = self.output_convs[i](y)\n\n        mask_feature = self.mask_feature(y)\n        memory = feats[-1]\n        return mask_feature, memory\n\n\n@MODELS.register_module()\nclass TransformerEncoderPixelDecoder(PixelDecoder):\n    \"\"\"Pixel decoder with transormer encoder inside.\n\n    Args:\n        in_channels (list[int] | tuple[int]): Number of channels in the\n            input feature maps.\n        feat_channels (int): Number channels for feature.\n        out_channels (int): Number channels for output.\n        norm_cfg (:obj:`ConfigDict` or dict): Config for normalization.\n            Defaults to dict(type='GN', num_groups=32).\n        act_cfg (:obj:`ConfigDict` or dict): Config for activation.\n            Defaults to dict(type='ReLU').\n        encoder (:obj:`ConfigDict` or dict): Config for transformer encoder.\n            Defaults to None.\n        positional_encoding (:obj:`ConfigDict` or dict): Config for\n            transformer encoder position encoding. Defaults to\n            dict(num_feats=128, normalize=True).\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: Union[List[int], Tuple[int]],\n                 feat_channels: int,\n                 out_channels: int,\n                 norm_cfg: ConfigType = dict(type='GN', num_groups=32),\n                 act_cfg: ConfigType = dict(type='ReLU'),\n                 encoder: ConfigType = None,\n                 positional_encoding: ConfigType = dict(\n                     num_feats=128, normalize=True),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            in_channels=in_channels,\n            feat_channels=feat_channels,\n            out_channels=out_channels,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg,\n            init_cfg=init_cfg)\n        self.last_feat_conv = None\n\n        self.encoder = DetrTransformerEncoder(**encoder)\n        self.encoder_embed_dims = self.encoder.embed_dims\n        assert self.encoder_embed_dims == feat_channels, 'embed_dims({}) of ' \\\n            'tranformer encoder must equal to feat_channels({})'.format(\n                feat_channels, self.encoder_embed_dims)\n        self.positional_encoding = SinePositionalEncoding(\n            **positional_encoding)\n        self.encoder_in_proj = Conv2d(\n            in_channels[-1], feat_channels, kernel_size=1)\n        self.encoder_out_proj = ConvModule(\n            feat_channels,\n            feat_channels,\n            kernel_size=3,\n            stride=1,\n            padding=1,\n            bias=self.use_bias,\n            norm_cfg=norm_cfg,\n            act_cfg=act_cfg)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights.\"\"\"\n        for i in range(0, self.num_inputs - 2):\n            caffe2_xavier_init(self.lateral_convs[i].conv, bias=0)\n            caffe2_xavier_init(self.output_convs[i].conv, bias=0)\n\n        caffe2_xavier_init(self.mask_feature, bias=0)\n        caffe2_xavier_init(self.encoder_in_proj, bias=0)\n        caffe2_xavier_init(self.encoder_out_proj.conv, bias=0)\n\n        for p in self.encoder.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def forward(self, feats: List[Tensor],\n                batch_img_metas: List[dict]) -> Tuple[Tensor, Tensor]:\n        \"\"\"\n        Args:\n            feats (list[Tensor]): Feature maps of each level. Each has\n                shape of (batch_size, c, h, w).\n            batch_img_metas (list[dict]): List of image information. Pass in\n                for creating more accurate padding mask.\n\n        Returns:\n            tuple: a tuple containing the following:\n\n                - mask_feature (Tensor): shape (batch_size, c, h, w).\n                - memory (Tensor): shape (batch_size, c, h, w).\n        \"\"\"\n        feat_last = feats[-1]\n        bs, c, h, w = feat_last.shape\n        input_img_h, input_img_w = batch_img_metas[0]['batch_input_shape']\n        padding_mask = feat_last.new_ones((bs, input_img_h, input_img_w),\n                                          dtype=torch.float32)\n        for i in range(bs):\n            img_h, img_w = batch_img_metas[i]['img_shape']\n            padding_mask[i, :img_h, :img_w] = 0\n        padding_mask = F.interpolate(\n            padding_mask.unsqueeze(1),\n            size=feat_last.shape[-2:],\n            mode='nearest').to(torch.bool).squeeze(1)\n\n        pos_embed = self.positional_encoding(padding_mask)\n        feat_last = self.encoder_in_proj(feat_last)\n        # (batch_size, c, h, w) -> (batch_size, num_queries, c)\n        feat_last = feat_last.flatten(2).permute(0, 2, 1)\n        pos_embed = pos_embed.flatten(2).permute(0, 2, 1)\n        # (batch_size, h, w) -> (batch_size, h*w)\n        padding_mask = padding_mask.flatten(1)\n        memory = self.encoder(\n            query=feat_last,\n            query_pos=pos_embed,\n            key_padding_mask=padding_mask)\n        # (batch_size, num_queries, c) -> (batch_size, c, h, w)\n        memory = memory.permute(0, 2, 1).view(bs, self.encoder_embed_dims, h,\n                                              w)\n        y = self.encoder_out_proj(memory)\n        for i in range(self.num_inputs - 2, -1, -1):\n            x = feats[i]\n            cur_feat = self.lateral_convs[i](x)\n            y = cur_feat + \\\n                F.interpolate(y, size=cur_feat.shape[-2:], mode='nearest')\n            y = self.output_convs[i](y)\n\n        mask_feature = self.mask_feature(y)\n        return mask_feature, memory\n"
  },
  {
    "path": "mmdet/models/layers/positional_encoding.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig, OptMultiConfig\n\n\n@MODELS.register_module()\nclass SinePositionalEncoding(BaseModule):\n    \"\"\"Position encoding with sine and cosine functions.\n\n    See `End-to-End Object Detection with Transformers\n    <https://arxiv.org/pdf/2005.12872>`_ for details.\n\n    Args:\n        num_feats (int): The feature dimension for each position\n            along x-axis or y-axis. Note the final returned dimension\n            for each position is 2 times of this value.\n        temperature (int, optional): The temperature used for scaling\n            the position embedding. Defaults to 10000.\n        normalize (bool, optional): Whether to normalize the position\n            embedding. Defaults to False.\n        scale (float, optional): A scale factor that scales the position\n            embedding. The scale will be used only when `normalize` is True.\n            Defaults to 2*pi.\n        eps (float, optional): A value added to the denominator for\n            numerical stability. Defaults to 1e-6.\n        offset (float): offset add to embed when do the normalization.\n            Defaults to 0.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None\n    \"\"\"\n\n    def __init__(self,\n                 num_feats: int,\n                 temperature: int = 10000,\n                 normalize: bool = False,\n                 scale: float = 2 * math.pi,\n                 eps: float = 1e-6,\n                 offset: float = 0.,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        if normalize:\n            assert isinstance(scale, (float, int)), 'when normalize is set,' \\\n                'scale should be provided and in float or int type, ' \\\n                f'found {type(scale)}'\n        self.num_feats = num_feats\n        self.temperature = temperature\n        self.normalize = normalize\n        self.scale = scale\n        self.eps = eps\n        self.offset = offset\n\n    def forward(self, mask: Tensor, input: Optional[Tensor] = None) -> Tensor:\n        \"\"\"Forward function for `SinePositionalEncoding`.\n\n        Args:\n            mask (Tensor): ByteTensor mask. Non-zero values representing\n                ignored positions, while zero values means valid positions\n                for this image. Shape [bs, h, w].\n            input (Tensor, optional): Input image/feature Tensor.\n                Shape [bs, c, h, w]\n\n        Returns:\n            pos (Tensor): Returned position embedding with shape\n                [bs, num_feats*2, h, w].\n        \"\"\"\n        assert not (mask is None and input is None)\n\n        if mask is not None:\n            B, H, W = mask.size()\n            device = mask.device\n            # For convenience of exporting to ONNX,\n            # it's required to convert\n            # `masks` from bool to int.\n            mask = mask.to(torch.int)\n            not_mask = 1 - mask  # logical_not\n            y_embed = not_mask.cumsum(1, dtype=torch.float32)\n            x_embed = not_mask.cumsum(2, dtype=torch.float32)\n        else:\n            # single image or batch image with no padding\n            B, _, H, W = input.shape\n            device = input.device\n            x_embed = torch.arange(\n                1, W + 1, dtype=torch.float32, device=device)\n            x_embed = x_embed.view(1, 1, -1).repeat(B, H, 1)\n            y_embed = torch.arange(\n                1, H + 1, dtype=torch.float32, device=device)\n            y_embed = y_embed.view(1, -1, 1).repeat(B, 1, W)\n        if self.normalize:\n            y_embed = (y_embed + self.offset) / \\\n                      (y_embed[:, -1:, :] + self.eps) * self.scale\n            x_embed = (x_embed + self.offset) / \\\n                      (x_embed[:, :, -1:] + self.eps) * self.scale\n        dim_t = torch.arange(\n            self.num_feats, dtype=torch.float32, device=device)\n        dim_t = self.temperature**(2 * (dim_t // 2) / self.num_feats)\n        pos_x = x_embed[:, :, :, None] / dim_t\n        pos_y = y_embed[:, :, :, None] / dim_t\n        # use `view` instead of `flatten` for dynamically exporting to ONNX\n\n        pos_x = torch.stack(\n            (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()),\n            dim=4).view(B, H, W, -1)\n        pos_y = torch.stack(\n            (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()),\n            dim=4).view(B, H, W, -1)\n        pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)\n        return pos\n\n    def __repr__(self) -> str:\n        \"\"\"str: a string that describes the module\"\"\"\n        repr_str = self.__class__.__name__\n        repr_str += f'(num_feats={self.num_feats}, '\n        repr_str += f'temperature={self.temperature}, '\n        repr_str += f'normalize={self.normalize}, '\n        repr_str += f'scale={self.scale}, '\n        repr_str += f'eps={self.eps})'\n        return repr_str\n\n\n@MODELS.register_module()\nclass LearnedPositionalEncoding(BaseModule):\n    \"\"\"Position embedding with learnable embedding weights.\n\n    Args:\n        num_feats (int): The feature dimension for each position\n            along x-axis or y-axis. The final returned dimension for\n            each position is 2 times of this value.\n        row_num_embed (int, optional): The dictionary size of row embeddings.\n            Defaults to 50.\n        col_num_embed (int, optional): The dictionary size of col embeddings.\n            Defaults to 50.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_feats: int,\n        row_num_embed: int = 50,\n        col_num_embed: int = 50,\n        init_cfg: MultiConfig = dict(type='Uniform', layer='Embedding')\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.row_embed = nn.Embedding(row_num_embed, num_feats)\n        self.col_embed = nn.Embedding(col_num_embed, num_feats)\n        self.num_feats = num_feats\n        self.row_num_embed = row_num_embed\n        self.col_num_embed = col_num_embed\n\n    def forward(self, mask: Tensor) -> Tensor:\n        \"\"\"Forward function for `LearnedPositionalEncoding`.\n\n        Args:\n            mask (Tensor): ByteTensor mask. Non-zero values representing\n                ignored positions, while zero values means valid positions\n                for this image. Shape [bs, h, w].\n\n        Returns:\n            pos (Tensor): Returned position embedding with shape\n                [bs, num_feats*2, h, w].\n        \"\"\"\n        h, w = mask.shape[-2:]\n        x = torch.arange(w, device=mask.device)\n        y = torch.arange(h, device=mask.device)\n        x_embed = self.col_embed(x)\n        y_embed = self.row_embed(y)\n        pos = torch.cat(\n            (x_embed.unsqueeze(0).repeat(h, 1, 1), y_embed.unsqueeze(1).repeat(\n                1, w, 1)),\n            dim=-1).permute(2, 0,\n                            1).unsqueeze(0).repeat(mask.shape[0], 1, 1, 1)\n        return pos\n\n    def __repr__(self) -> str:\n        \"\"\"str: a string that describes the module\"\"\"\n        repr_str = self.__class__.__name__\n        repr_str += f'(num_feats={self.num_feats}, '\n        repr_str += f'row_num_embed={self.row_num_embed}, '\n        repr_str += f'col_num_embed={self.col_num_embed})'\n        return repr_str\n\n\n@MODELS.register_module()\nclass SinePositionalEncoding3D(SinePositionalEncoding):\n    \"\"\"Position encoding with sine and cosine functions.\n\n    See `End-to-End Object Detection with Transformers\n    <https://arxiv.org/pdf/2005.12872>`_ for details.\n\n    Args:\n        num_feats (int): The feature dimension for each position\n            along x-axis or y-axis. Note the final returned dimension\n            for each position is 2 times of this value.\n        temperature (int, optional): The temperature used for scaling\n            the position embedding. Defaults to 10000.\n        normalize (bool, optional): Whether to normalize the position\n            embedding. Defaults to False.\n        scale (float, optional): A scale factor that scales the position\n            embedding. The scale will be used only when `normalize` is True.\n            Defaults to 2*pi.\n        eps (float, optional): A value added to the denominator for\n            numerical stability. Defaults to 1e-6.\n        offset (float): offset add to embed when do the normalization.\n            Defaults to 0.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def forward(self, mask: Tensor) -> Tensor:\n        \"\"\"Forward function for `SinePositionalEncoding3D`.\n\n        Args:\n            mask (Tensor): ByteTensor mask. Non-zero values representing\n                ignored positions, while zero values means valid positions\n                for this image. Shape [bs, t, h, w].\n\n        Returns:\n            pos (Tensor): Returned position embedding with shape\n                [bs, num_feats*2, h, w].\n        \"\"\"\n        assert mask.dim() == 4,\\\n            f'{mask.shape} should be a 4-dimensional Tensor,' \\\n            f' got {mask.dim()}-dimensional Tensor instead '\n        # For convenience of exporting to ONNX, it's required to convert\n        # `masks` from bool to int.\n        mask = mask.to(torch.int)\n        not_mask = 1 - mask  # logical_not\n        z_embed = not_mask.cumsum(1, dtype=torch.float32)\n        y_embed = not_mask.cumsum(2, dtype=torch.float32)\n        x_embed = not_mask.cumsum(3, dtype=torch.float32)\n        if self.normalize:\n            z_embed = (z_embed + self.offset) / \\\n                      (z_embed[:, -1:, :, :] + self.eps) * self.scale\n            y_embed = (y_embed + self.offset) / \\\n                      (y_embed[:, :, -1:, :] + self.eps) * self.scale\n            x_embed = (x_embed + self.offset) / \\\n                      (x_embed[:, :, :, -1:] + self.eps) * self.scale\n        dim_t = torch.arange(\n            self.num_feats, dtype=torch.float32, device=mask.device)\n        dim_t = self.temperature**(2 * (dim_t // 2) / self.num_feats)\n\n        dim_t_z = torch.arange((self.num_feats * 2),\n                               dtype=torch.float32,\n                               device=mask.device)\n        dim_t_z = self.temperature**(2 * (dim_t_z // 2) / (self.num_feats * 2))\n\n        pos_x = x_embed[:, :, :, :, None] / dim_t\n        pos_y = y_embed[:, :, :, :, None] / dim_t\n        pos_z = z_embed[:, :, :, :, None] / dim_t_z\n        # use `view` instead of `flatten` for dynamically exporting to ONNX\n        B, T, H, W = mask.size()\n        pos_x = torch.stack(\n            (pos_x[:, :, :, :, 0::2].sin(), pos_x[:, :, :, :, 1::2].cos()),\n            dim=5).view(B, T, H, W, -1)\n        pos_y = torch.stack(\n            (pos_y[:, :, :, :, 0::2].sin(), pos_y[:, :, :, :, 1::2].cos()),\n            dim=5).view(B, T, H, W, -1)\n        pos_z = torch.stack(\n            (pos_z[:, :, :, :, 0::2].sin(), pos_z[:, :, :, :, 1::2].cos()),\n            dim=5).view(B, T, H, W, -1)\n        pos = (torch.cat((pos_y, pos_x), dim=4) + pos_z).permute(0, 1, 4, 2, 3)\n        return pos\n"
  },
  {
    "path": "mmdet/models/layers/res_layer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nfrom mmcv.cnn import build_conv_layer, build_norm_layer\nfrom mmengine.model import BaseModule, Sequential\nfrom torch import Tensor\nfrom torch import nn as nn\n\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\n\n\nclass ResLayer(Sequential):\n    \"\"\"ResLayer to build ResNet style backbone.\n\n    Args:\n        block (nn.Module): block used to build ResLayer.\n        inplanes (int): inplanes of block.\n        planes (int): planes of block.\n        num_blocks (int): number of blocks.\n        stride (int): stride of the first block. Defaults to 1\n        avg_down (bool): Use AvgPool instead of stride conv when\n            downsampling in the bottleneck. Defaults to False\n        conv_cfg (dict): dictionary to construct and config conv layer.\n            Defaults to None\n        norm_cfg (dict): dictionary to construct and config norm layer.\n            Defaults to dict(type='BN')\n        downsample_first (bool): Downsample at the first block or last block.\n            False for Hourglass, True for ResNet. Defaults to True\n    \"\"\"\n\n    def __init__(self,\n                 block: BaseModule,\n                 inplanes: int,\n                 planes: int,\n                 num_blocks: int,\n                 stride: int = 1,\n                 avg_down: bool = False,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 downsample_first: bool = True,\n                 **kwargs) -> None:\n        self.block = block\n\n        downsample = None\n        if stride != 1 or inplanes != planes * block.expansion:\n            downsample = []\n            conv_stride = stride\n            if avg_down:\n                conv_stride = 1\n                downsample.append(\n                    nn.AvgPool2d(\n                        kernel_size=stride,\n                        stride=stride,\n                        ceil_mode=True,\n                        count_include_pad=False))\n            downsample.extend([\n                build_conv_layer(\n                    conv_cfg,\n                    inplanes,\n                    planes * block.expansion,\n                    kernel_size=1,\n                    stride=conv_stride,\n                    bias=False),\n                build_norm_layer(norm_cfg, planes * block.expansion)[1]\n            ])\n            downsample = nn.Sequential(*downsample)\n\n        layers = []\n        if downsample_first:\n            layers.append(\n                block(\n                    inplanes=inplanes,\n                    planes=planes,\n                    stride=stride,\n                    downsample=downsample,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    **kwargs))\n            inplanes = planes * block.expansion\n            for _ in range(1, num_blocks):\n                layers.append(\n                    block(\n                        inplanes=inplanes,\n                        planes=planes,\n                        stride=1,\n                        conv_cfg=conv_cfg,\n                        norm_cfg=norm_cfg,\n                        **kwargs))\n\n        else:  # downsample_first=False is for HourglassModule\n            for _ in range(num_blocks - 1):\n                layers.append(\n                    block(\n                        inplanes=inplanes,\n                        planes=inplanes,\n                        stride=1,\n                        conv_cfg=conv_cfg,\n                        norm_cfg=norm_cfg,\n                        **kwargs))\n            layers.append(\n                block(\n                    inplanes=inplanes,\n                    planes=planes,\n                    stride=stride,\n                    downsample=downsample,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    **kwargs))\n        super().__init__(*layers)\n\n\nclass SimplifiedBasicBlock(BaseModule):\n    \"\"\"Simplified version of original basic residual block. This is used in\n    `SCNet <https://arxiv.org/abs/2012.10150>`_.\n\n    - Norm layer is now optional\n    - Last ReLU in forward function is removed\n    \"\"\"\n    expansion = 1\n\n    def __init__(self,\n                 inplanes: int,\n                 planes: int,\n                 stride: int = 1,\n                 dilation: int = 1,\n                 downsample: Optional[Sequential] = None,\n                 style: ConfigType = 'pytorch',\n                 with_cp: bool = False,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 dcn: OptConfigType = None,\n                 plugins: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert dcn is None, 'Not implemented yet.'\n        assert plugins is None, 'Not implemented yet.'\n        assert not with_cp, 'Not implemented yet.'\n        self.with_norm = norm_cfg is not None\n        with_bias = True if norm_cfg is None else False\n        self.conv1 = build_conv_layer(\n            conv_cfg,\n            inplanes,\n            planes,\n            3,\n            stride=stride,\n            padding=dilation,\n            dilation=dilation,\n            bias=with_bias)\n        if self.with_norm:\n            self.norm1_name, norm1 = build_norm_layer(\n                norm_cfg, planes, postfix=1)\n            self.add_module(self.norm1_name, norm1)\n        self.conv2 = build_conv_layer(\n            conv_cfg, planes, planes, 3, padding=1, bias=with_bias)\n        if self.with_norm:\n            self.norm2_name, norm2 = build_norm_layer(\n                norm_cfg, planes, postfix=2)\n            self.add_module(self.norm2_name, norm2)\n\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n        self.stride = stride\n        self.dilation = dilation\n        self.with_cp = with_cp\n\n    @property\n    def norm1(self) -> Optional[BaseModule]:\n        \"\"\"nn.Module: normalization layer after the first convolution layer\"\"\"\n        return getattr(self, self.norm1_name) if self.with_norm else None\n\n    @property\n    def norm2(self) -> Optional[BaseModule]:\n        \"\"\"nn.Module: normalization layer after the second convolution layer\"\"\"\n        return getattr(self, self.norm2_name) if self.with_norm else None\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function for SimplifiedBasicBlock.\"\"\"\n\n        identity = x\n\n        out = self.conv1(x)\n        if self.with_norm:\n            out = self.norm1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        if self.with_norm:\n            out = self.norm2(out)\n\n        if self.downsample is not None:\n            identity = self.downsample(x)\n\n        out += identity\n\n        return out\n"
  },
  {
    "path": "mmdet/models/layers/se_layer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom mmengine.utils import digit_version, is_tuple_of\nfrom torch import Tensor\n\nfrom mmdet.utils import MultiConfig, OptConfigType, OptMultiConfig\n\n\nclass SELayer(BaseModule):\n    \"\"\"Squeeze-and-Excitation Module.\n\n    Args:\n        channels (int): The input (and output) channels of the SE layer.\n        ratio (int): Squeeze ratio in SELayer, the intermediate channel will be\n            ``int(channels/ratio)``. Defaults to 16.\n        conv_cfg (None or dict): Config dict for convolution layer.\n            Defaults to None, which means using conv2d.\n        act_cfg (dict or Sequence[dict]): Config dict for activation layer.\n            If act_cfg is a dict, two activation layers will be configurated\n            by this dict. If act_cfg is a sequence of dicts, the first\n            activation layer will be configurated by the first dict and the\n            second activation layer will be configurated by the second dict.\n            Defaults to (dict(type='ReLU'), dict(type='Sigmoid'))\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None\n    \"\"\"\n\n    def __init__(self,\n                 channels: int,\n                 ratio: int = 16,\n                 conv_cfg: OptConfigType = None,\n                 act_cfg: MultiConfig = (dict(type='ReLU'),\n                                         dict(type='Sigmoid')),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        if isinstance(act_cfg, dict):\n            act_cfg = (act_cfg, act_cfg)\n        assert len(act_cfg) == 2\n        assert is_tuple_of(act_cfg, dict)\n        self.global_avgpool = nn.AdaptiveAvgPool2d(1)\n        self.conv1 = ConvModule(\n            in_channels=channels,\n            out_channels=int(channels / ratio),\n            kernel_size=1,\n            stride=1,\n            conv_cfg=conv_cfg,\n            act_cfg=act_cfg[0])\n        self.conv2 = ConvModule(\n            in_channels=int(channels / ratio),\n            out_channels=channels,\n            kernel_size=1,\n            stride=1,\n            conv_cfg=conv_cfg,\n            act_cfg=act_cfg[1])\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function for SELayer.\"\"\"\n        out = self.global_avgpool(x)\n        out = self.conv1(out)\n        out = self.conv2(out)\n        return x * out\n\n\nclass DyReLU(BaseModule):\n    \"\"\"Dynamic ReLU (DyReLU) module.\n\n    See `Dynamic ReLU <https://arxiv.org/abs/2003.10027>`_ for details.\n    Current implementation is specialized for task-aware attention in DyHead.\n    HSigmoid arguments in default act_cfg follow DyHead official code.\n    https://github.com/microsoft/DynamicHead/blob/master/dyhead/dyrelu.py\n\n    Args:\n        channels (int): The input (and output) channels of DyReLU module.\n        ratio (int): Squeeze ratio in Squeeze-and-Excitation-like module,\n            the intermediate channel will be ``int(channels/ratio)``.\n            Defaults to 4.\n        conv_cfg (None or dict): Config dict for convolution layer.\n            Defaults to None, which means using conv2d.\n        act_cfg (dict or Sequence[dict]): Config dict for activation layer.\n            If act_cfg is a dict, two activation layers will be configurated\n            by this dict. If act_cfg is a sequence of dicts, the first\n            activation layer will be configurated by the first dict and the\n            second activation layer will be configurated by the second dict.\n            Defaults to (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0,\n            divisor=6.0))\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None\n    \"\"\"\n\n    def __init__(self,\n                 channels: int,\n                 ratio: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 act_cfg: MultiConfig = (dict(type='ReLU'),\n                                         dict(\n                                             type='HSigmoid',\n                                             bias=3.0,\n                                             divisor=6.0)),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        if isinstance(act_cfg, dict):\n            act_cfg = (act_cfg, act_cfg)\n        assert len(act_cfg) == 2\n        assert is_tuple_of(act_cfg, dict)\n        self.channels = channels\n        self.expansion = 4  # for a1, b1, a2, b2\n        self.global_avgpool = nn.AdaptiveAvgPool2d(1)\n        self.conv1 = ConvModule(\n            in_channels=channels,\n            out_channels=int(channels / ratio),\n            kernel_size=1,\n            stride=1,\n            conv_cfg=conv_cfg,\n            act_cfg=act_cfg[0])\n        self.conv2 = ConvModule(\n            in_channels=int(channels / ratio),\n            out_channels=channels * self.expansion,\n            kernel_size=1,\n            stride=1,\n            conv_cfg=conv_cfg,\n            act_cfg=act_cfg[1])\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function.\"\"\"\n        coeffs = self.global_avgpool(x)\n        coeffs = self.conv1(coeffs)\n        coeffs = self.conv2(coeffs) - 0.5  # value range: [-0.5, 0.5]\n        a1, b1, a2, b2 = torch.split(coeffs, self.channels, dim=1)\n        a1 = a1 * 2.0 + 1.0  # [-1.0, 1.0] + 1.0\n        a2 = a2 * 2.0  # [-1.0, 1.0]\n        out = torch.max(x * a1 + b1, x * a2 + b2)\n        return out\n\n\nclass ChannelAttention(BaseModule):\n    \"\"\"Channel attention Module.\n\n    Args:\n        channels (int): The input (and output) channels of the attention layer.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None\n    \"\"\"\n\n    def __init__(self, channels: int, init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.global_avgpool = nn.AdaptiveAvgPool2d(1)\n        self.fc = nn.Conv2d(channels, channels, 1, 1, 0, bias=True)\n        if digit_version(torch.__version__) < (1, 7, 0):\n            self.act = nn.Hardsigmoid()\n        else:\n            self.act = nn.Hardsigmoid(inplace=True)\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function for ChannelAttention.\"\"\"\n        with torch.cuda.amp.autocast(enabled=False):\n            out = self.global_avgpool(x)\n        out = self.fc(out)\n        out = self.act(out)\n        return x * out\n"
  },
  {
    "path": "mmdet/models/layers/transformer/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .conditional_detr_layers import (ConditionalDetrTransformerDecoder,\n                                      ConditionalDetrTransformerDecoderLayer)\nfrom .dab_detr_layers import (DABDetrTransformerDecoder,\n                              DABDetrTransformerDecoderLayer,\n                              DABDetrTransformerEncoder)\nfrom .ddq_detr_layers import DDQTransformerDecoder\nfrom .deformable_detr_layers import (DeformableDetrTransformerDecoder,\n                                     DeformableDetrTransformerDecoderLayer,\n                                     DeformableDetrTransformerEncoder,\n                                     DeformableDetrTransformerEncoderLayer)\nfrom .detr_layers import (DetrTransformerDecoder, DetrTransformerDecoderLayer,\n                          DetrTransformerEncoder, DetrTransformerEncoderLayer)\nfrom .dino_layers import CdnQueryGenerator, DinoTransformerDecoder\nfrom .grounding_dino_layers import (GroundingDinoTransformerDecoder,\n                                    GroundingDinoTransformerDecoderLayer,\n                                    GroundingDinoTransformerEncoder)\nfrom .mask2former_layers import (Mask2FormerTransformerDecoder,\n                                 Mask2FormerTransformerDecoderLayer,\n                                 Mask2FormerTransformerEncoder)\nfrom .utils import (MLP, AdaptivePadding, ConditionalAttention, DynamicConv,\n                    PatchEmbed, PatchMerging, coordinate_to_encoding,\n                    inverse_sigmoid, nchw_to_nlc, nlc_to_nchw)\n\n__all__ = [\n    'nlc_to_nchw', 'nchw_to_nlc', 'AdaptivePadding', 'PatchEmbed',\n    'PatchMerging', 'inverse_sigmoid', 'DynamicConv', 'MLP',\n    'DetrTransformerEncoder', 'DetrTransformerDecoder',\n    'DetrTransformerEncoderLayer', 'DetrTransformerDecoderLayer',\n    'DeformableDetrTransformerEncoder', 'DeformableDetrTransformerDecoder',\n    'DeformableDetrTransformerEncoderLayer',\n    'DeformableDetrTransformerDecoderLayer', 'coordinate_to_encoding',\n    'ConditionalAttention', 'DABDetrTransformerDecoderLayer',\n    'DABDetrTransformerDecoder', 'DABDetrTransformerEncoder',\n    'DDQTransformerDecoder', 'ConditionalDetrTransformerDecoder',\n    'ConditionalDetrTransformerDecoderLayer', 'DinoTransformerDecoder',\n    'CdnQueryGenerator', 'Mask2FormerTransformerEncoder',\n    'Mask2FormerTransformerDecoderLayer', 'Mask2FormerTransformerDecoder',\n    'GroundingDinoTransformerDecoderLayer', 'GroundingDinoTransformerEncoder',\n    'GroundingDinoTransformerDecoder'\n]\n"
  },
  {
    "path": "mmdet/models/layers/transformer/conditional_detr_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom mmcv.cnn import build_norm_layer\nfrom mmcv.cnn.bricks.transformer import FFN\nfrom torch import Tensor\nfrom torch.nn import ModuleList\n\nfrom .detr_layers import DetrTransformerDecoder, DetrTransformerDecoderLayer\nfrom .utils import MLP, ConditionalAttention, coordinate_to_encoding\n\n\nclass ConditionalDetrTransformerDecoder(DetrTransformerDecoder):\n    \"\"\"Decoder of Conditional DETR.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize decoder layers and other layers.\"\"\"\n        self.layers = ModuleList([\n            ConditionalDetrTransformerDecoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.embed_dims = self.layers[0].embed_dims\n        self.post_norm = build_norm_layer(self.post_norm_cfg,\n                                          self.embed_dims)[1]\n        # conditional detr affline\n        self.query_scale = MLP(self.embed_dims, self.embed_dims,\n                               self.embed_dims, 2)\n        self.ref_point_head = MLP(self.embed_dims, self.embed_dims, 2, 2)\n        # we have substitute 'qpos_proj' with 'qpos_sine_proj' except for\n        # the first decoder layer), so 'qpos_proj' should be deleted\n        # in other layers.\n        for layer_id in range(self.num_layers - 1):\n            self.layers[layer_id + 1].cross_attn.qpos_proj = None\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor = None,\n                query_pos: Tensor = None,\n                key_pos: Tensor = None,\n                key_padding_mask: Tensor = None):\n        \"\"\"Forward function of decoder.\n\n        Args:\n            query (Tensor): The input query with shape\n                (bs, num_queries, dim).\n            key (Tensor): The input key with shape (bs, num_keys, dim) If\n                `None`, the `query` will be used. Defaults to `None`.\n            query_pos (Tensor): The positional encoding for `query`, with the\n                same shape as `query`. If not `None`, it will be added to\n                `query` before forward function. Defaults to `None`.\n            key_pos (Tensor): The positional encoding for `key`, with the\n                same shape as `key`. If not `None`, it will be added to\n                `key` before forward function. If `None`, and `query_pos`\n                has the same shape as `key`, then `query_pos` will be used\n                as `key_pos`. Defaults to `None`.\n            key_padding_mask (Tensor): ByteTensor with shape (bs, num_keys).\n                Defaults to `None`.\n        Returns:\n            List[Tensor]: forwarded results with shape (num_decoder_layers,\n            bs, num_queries, dim) if `return_intermediate` is True, otherwise\n            with shape (1, bs, num_queries, dim). References with shape\n            (bs, num_queries, 2).\n        \"\"\"\n        reference_unsigmoid = self.ref_point_head(\n            query_pos)  # [bs, num_queries, 2]\n        reference = reference_unsigmoid.sigmoid()\n        reference_xy = reference[..., :2]\n        intermediate = []\n        for layer_id, layer in enumerate(self.layers):\n            if layer_id == 0:\n                pos_transformation = 1\n            else:\n                pos_transformation = self.query_scale(query)\n            # get sine embedding for the query reference\n            ref_sine_embed = coordinate_to_encoding(coord_tensor=reference_xy)\n            # apply transformation\n            ref_sine_embed = ref_sine_embed * pos_transformation\n            query = layer(\n                query,\n                key=key,\n                query_pos=query_pos,\n                key_pos=key_pos,\n                key_padding_mask=key_padding_mask,\n                ref_sine_embed=ref_sine_embed,\n                is_first=(layer_id == 0))\n            if self.return_intermediate:\n                intermediate.append(self.post_norm(query))\n\n        if self.return_intermediate:\n            return torch.stack(intermediate), reference\n\n        query = self.post_norm(query)\n        return query.unsqueeze(0), reference\n\n\nclass ConditionalDetrTransformerDecoderLayer(DetrTransformerDecoderLayer):\n    \"\"\"Implements decoder layer in Conditional DETR transformer.\"\"\"\n\n    def _init_layers(self):\n        \"\"\"Initialize self-attention, cross-attention, FFN, and\n        normalization.\"\"\"\n        self.self_attn = ConditionalAttention(**self.self_attn_cfg)\n        self.cross_attn = ConditionalAttention(**self.cross_attn_cfg)\n        self.embed_dims = self.self_attn.embed_dims\n        self.ffn = FFN(**self.ffn_cfg)\n        norms_list = [\n            build_norm_layer(self.norm_cfg, self.embed_dims)[1]\n            for _ in range(3)\n        ]\n        self.norms = ModuleList(norms_list)\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor = None,\n                query_pos: Tensor = None,\n                key_pos: Tensor = None,\n                self_attn_masks: Tensor = None,\n                cross_attn_masks: Tensor = None,\n                key_padding_mask: Tensor = None,\n                ref_sine_embed: Tensor = None,\n                is_first: bool = False):\n        \"\"\"\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim)\n            key (Tensor, optional): The input key, has shape (bs, num_keys,\n                dim). If `None`, the `query` will be used. Defaults to `None`.\n            query_pos (Tensor, optional): The positional encoding for `query`,\n                has the same shape as `query`. If not `None`, it will be\n                added to `query` before forward function. Defaults to `None`.\n            ref_sine_embed (Tensor): The positional encoding for query in\n                cross attention, with the same shape as `x`. Defaults to None.\n            key_pos (Tensor, optional): The positional encoding for `key`, has\n                the same shape as `key`. If not None, it will be added to\n                `key` before forward function. If None, and `query_pos` has\n                the same shape as `key`, then `query_pos` will be used for\n                `key_pos`. Defaults to None.\n            self_attn_masks (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), Same in `nn.MultiheadAttention.\n                forward`. Defaults to None.\n            cross_attn_masks (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), Same in `nn.MultiheadAttention.\n                forward`. Defaults to None.\n            key_padding_mask (Tensor, optional): ByteTensor, has shape\n                (bs, num_keys). Defaults to None.\n            is_first (bool): A indicator to tell whether the current layer\n                is the first layer of the decoder. Defaults to False.\n\n        Returns:\n            Tensor: Forwarded results, has shape (bs, num_queries, dim).\n        \"\"\"\n        query = self.self_attn(\n            query=query,\n            key=query,\n            query_pos=query_pos,\n            key_pos=query_pos,\n            attn_mask=self_attn_masks)\n        query = self.norms[0](query)\n        query = self.cross_attn(\n            query=query,\n            key=key,\n            query_pos=query_pos,\n            key_pos=key_pos,\n            attn_mask=cross_attn_masks,\n            key_padding_mask=key_padding_mask,\n            ref_sine_embed=ref_sine_embed,\n            is_first=is_first)\n        query = self.norms[1](query)\n        query = self.ffn(query)\n        query = self.norms[2](query)\n\n        return query\n"
  },
  {
    "path": "mmdet/models/layers/transformer/dab_detr_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import build_norm_layer\nfrom mmcv.cnn.bricks.transformer import FFN\nfrom mmengine.model import ModuleList\nfrom torch import Tensor\n\nfrom .detr_layers import (DetrTransformerDecoder, DetrTransformerDecoderLayer,\n                          DetrTransformerEncoder, DetrTransformerEncoderLayer)\nfrom .utils import (MLP, ConditionalAttention, coordinate_to_encoding,\n                    inverse_sigmoid)\n\n\nclass DABDetrTransformerDecoderLayer(DetrTransformerDecoderLayer):\n    \"\"\"Implements decoder layer in DAB-DETR transformer.\"\"\"\n\n    def _init_layers(self):\n        \"\"\"Initialize self-attention, cross-attention, FFN, normalization and\n        others.\"\"\"\n        self.self_attn = ConditionalAttention(**self.self_attn_cfg)\n        self.cross_attn = ConditionalAttention(**self.cross_attn_cfg)\n        self.embed_dims = self.self_attn.embed_dims\n        self.ffn = FFN(**self.ffn_cfg)\n        norms_list = [\n            build_norm_layer(self.norm_cfg, self.embed_dims)[1]\n            for _ in range(3)\n        ]\n        self.norms = ModuleList(norms_list)\n        self.keep_query_pos = self.cross_attn.keep_query_pos\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor,\n                query_pos: Tensor,\n                key_pos: Tensor,\n                ref_sine_embed: Tensor = None,\n                self_attn_masks: Tensor = None,\n                cross_attn_masks: Tensor = None,\n                key_padding_mask: Tensor = None,\n                is_first: bool = False,\n                **kwargs) -> Tensor:\n        \"\"\"\n        Args:\n            query (Tensor): The input query with shape [bs, num_queries,\n                dim].\n            key (Tensor): The key tensor with shape [bs, num_keys,\n                dim].\n            query_pos (Tensor): The positional encoding for query in self\n                attention, with the same shape as `x`.\n            key_pos (Tensor): The positional encoding for `key`, with the\n                same shape as `key`.\n            ref_sine_embed (Tensor): The positional encoding for query in\n                cross attention, with the same shape as `x`.\n                Defaults to None.\n            self_attn_masks (Tensor): ByteTensor mask with shape [num_queries,\n                num_keys]. Same in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            cross_attn_masks (Tensor): ByteTensor mask with shape [num_queries,\n                num_keys]. Same in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys].\n                Defaults to None.\n            is_first (bool): A indicator to tell whether the current layer\n                is the first layer of the decoder.\n                Defaults to False.\n\n        Returns:\n            Tensor: forwarded results with shape\n            [bs, num_queries, dim].\n        \"\"\"\n\n        query = self.self_attn(\n            query=query,\n            key=query,\n            query_pos=query_pos,\n            key_pos=query_pos,\n            attn_mask=self_attn_masks,\n            **kwargs)\n        query = self.norms[0](query)\n        query = self.cross_attn(\n            query=query,\n            key=key,\n            query_pos=query_pos,\n            key_pos=key_pos,\n            ref_sine_embed=ref_sine_embed,\n            attn_mask=cross_attn_masks,\n            key_padding_mask=key_padding_mask,\n            is_first=is_first,\n            **kwargs)\n        query = self.norms[1](query)\n        query = self.ffn(query)\n        query = self.norms[2](query)\n\n        return query\n\n\nclass DABDetrTransformerDecoder(DetrTransformerDecoder):\n    \"\"\"Decoder of DAB-DETR.\n\n    Args:\n        query_dim (int): The last dimension of query pos,\n            4 for anchor format, 2 for point format.\n            Defaults to 4.\n        query_scale_type (str): Type of transformation applied\n            to content query. Defaults to `cond_elewise`.\n        with_modulated_hw_attn (bool): Whether to inject h&w info\n            during cross conditional attention. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 query_dim: int = 4,\n                 query_scale_type: str = 'cond_elewise',\n                 with_modulated_hw_attn: bool = True,\n                 **kwargs):\n\n        self.query_dim = query_dim\n        self.query_scale_type = query_scale_type\n        self.with_modulated_hw_attn = with_modulated_hw_attn\n\n        super().__init__(*args, **kwargs)\n\n    def _init_layers(self):\n        \"\"\"Initialize decoder layers and other layers.\"\"\"\n        assert self.query_dim in [2, 4], \\\n            f'{\"dab-detr only supports anchor prior or reference point prior\"}'\n        assert self.query_scale_type in [\n            'cond_elewise', 'cond_scalar', 'fix_elewise'\n        ]\n\n        self.layers = ModuleList([\n            DABDetrTransformerDecoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n\n        embed_dims = self.layers[0].embed_dims\n        self.embed_dims = embed_dims\n\n        self.post_norm = build_norm_layer(self.post_norm_cfg, embed_dims)[1]\n        if self.query_scale_type == 'cond_elewise':\n            self.query_scale = MLP(embed_dims, embed_dims, embed_dims, 2)\n        elif self.query_scale_type == 'cond_scalar':\n            self.query_scale = MLP(embed_dims, embed_dims, 1, 2)\n        elif self.query_scale_type == 'fix_elewise':\n            self.query_scale = nn.Embedding(self.num_layers, embed_dims)\n        else:\n            raise NotImplementedError('Unknown query_scale_type: {}'.format(\n                self.query_scale_type))\n\n        self.ref_point_head = MLP(self.query_dim // 2 * embed_dims, embed_dims,\n                                  embed_dims, 2)\n\n        if self.with_modulated_hw_attn and self.query_dim == 4:\n            self.ref_anchor_head = MLP(embed_dims, embed_dims, 2, 2)\n\n        self.keep_query_pos = self.layers[0].keep_query_pos\n        if not self.keep_query_pos:\n            for layer_id in range(self.num_layers - 1):\n                self.layers[layer_id + 1].cross_attn.qpos_proj = None\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor,\n                query_pos: Tensor,\n                key_pos: Tensor,\n                reg_branches: nn.Module,\n                key_padding_mask: Tensor = None,\n                **kwargs) -> List[Tensor]:\n        \"\"\"Forward function of decoder.\n\n        Args:\n            query (Tensor): The input query with shape (bs, num_queries, dim).\n            key (Tensor): The input key with shape (bs, num_keys, dim).\n            query_pos (Tensor): The positional encoding for `query`, with the\n                same shape as `query`.\n            key_pos (Tensor): The positional encoding for `key`, with the\n                same shape as `key`.\n            reg_branches (nn.Module): The regression branch for dynamically\n                updating references in each layer.\n            key_padding_mask (Tensor): ByteTensor with shape (bs, num_keys).\n                Defaults to `None`.\n\n        Returns:\n            List[Tensor]: forwarded results with shape (num_decoder_layers,\n            bs, num_queries, dim) if `return_intermediate` is True, otherwise\n            with shape (1, bs, num_queries, dim). references with shape\n            (num_decoder_layers, bs, num_queries, 2/4).\n        \"\"\"\n        output = query\n        unsigmoid_references = query_pos\n\n        reference_points = unsigmoid_references.sigmoid()\n        intermediate_reference_points = [reference_points]\n\n        intermediate = []\n        for layer_id, layer in enumerate(self.layers):\n            obj_center = reference_points[..., :self.query_dim]\n            ref_sine_embed = coordinate_to_encoding(\n                coord_tensor=obj_center, num_feats=self.embed_dims // 2)\n            query_pos = self.ref_point_head(\n                ref_sine_embed)  # [bs, nq, 2c] -> [bs, nq, c]\n            # For the first decoder layer, do not apply transformation\n            if self.query_scale_type != 'fix_elewise':\n                if layer_id == 0:\n                    pos_transformation = 1\n                else:\n                    pos_transformation = self.query_scale(output)\n            else:\n                pos_transformation = self.query_scale.weight[layer_id]\n            # apply transformation\n            ref_sine_embed = ref_sine_embed[\n                ..., :self.embed_dims] * pos_transformation\n            # modulated height and weight attention\n            if self.with_modulated_hw_attn:\n                assert obj_center.size(-1) == 4\n                ref_hw = self.ref_anchor_head(output).sigmoid()\n                ref_sine_embed[..., self.embed_dims // 2:] *= \\\n                    (ref_hw[..., 0] / obj_center[..., 2]).unsqueeze(-1)\n                ref_sine_embed[..., : self.embed_dims // 2] *= \\\n                    (ref_hw[..., 1] / obj_center[..., 3]).unsqueeze(-1)\n\n            output = layer(\n                output,\n                key,\n                query_pos=query_pos,\n                ref_sine_embed=ref_sine_embed,\n                key_pos=key_pos,\n                key_padding_mask=key_padding_mask,\n                is_first=(layer_id == 0),\n                **kwargs)\n            # iter update\n            tmp_reg_preds = reg_branches(output)\n            tmp_reg_preds[..., :self.query_dim] += inverse_sigmoid(\n                reference_points)\n            new_reference_points = tmp_reg_preds[\n                ..., :self.query_dim].sigmoid()\n            if layer_id != self.num_layers - 1:\n                intermediate_reference_points.append(new_reference_points)\n            reference_points = new_reference_points.detach()\n\n            if self.return_intermediate:\n                intermediate.append(self.post_norm(output))\n\n        output = self.post_norm(output)\n\n        if self.return_intermediate:\n            return [\n                torch.stack(intermediate),\n                torch.stack(intermediate_reference_points),\n            ]\n        else:\n            return [\n                output.unsqueeze(0),\n                torch.stack(intermediate_reference_points)\n            ]\n\n\nclass DABDetrTransformerEncoder(DetrTransformerEncoder):\n    \"\"\"Encoder of DAB-DETR.\"\"\"\n\n    def _init_layers(self):\n        \"\"\"Initialize encoder layers.\"\"\"\n        self.layers = ModuleList([\n            DetrTransformerEncoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        embed_dims = self.layers[0].embed_dims\n        self.embed_dims = embed_dims\n        self.query_scale = MLP(embed_dims, embed_dims, embed_dims, 2)\n\n    def forward(self, query: Tensor, query_pos: Tensor,\n                key_padding_mask: Tensor, **kwargs):\n        \"\"\"Forward function of encoder.\n\n        Args:\n            query (Tensor): Input queries of encoder, has shape\n                (bs, num_queries, dim).\n            query_pos (Tensor): The positional embeddings of the queries, has\n                shape (bs, num_feat_points, dim).\n            key_padding_mask (Tensor): ByteTensor, the key padding mask\n                of the queries, has shape (bs, num_feat_points).\n\n        Returns:\n            Tensor: With shape (num_queries, bs, dim).\n        \"\"\"\n\n        for layer in self.layers:\n            pos_scales = self.query_scale(query)\n            query = layer(\n                query,\n                query_pos=query_pos * pos_scales,\n                key_padding_mask=key_padding_mask,\n                **kwargs)\n\n        return query\n"
  },
  {
    "path": "mmdet/models/layers/transformer/ddq_detr_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\n\nimport torch\nfrom mmcv.ops import batched_nms\nfrom torch import Tensor, nn\n\nfrom mmdet.structures.bbox import bbox_cxcywh_to_xyxy\nfrom .deformable_detr_layers import DeformableDetrTransformerDecoder\nfrom .utils import MLP, coordinate_to_encoding, inverse_sigmoid\n\n\nclass DDQTransformerDecoder(DeformableDetrTransformerDecoder):\n    \"\"\"Transformer decoder of DDQ.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize encoder layers.\"\"\"\n        super()._init_layers()\n        self.ref_point_head = MLP(self.embed_dims * 2, self.embed_dims,\n                                  self.embed_dims, 2)\n        self.norm = nn.LayerNorm(self.embed_dims)\n\n    def select_distinct_queries(self, reference_points: Tensor, query: Tensor,\n                                self_attn_mask: Tensor, layer_index):\n        \"\"\"Get updated `self_attn_mask` for distinct queries selection, it is\n        used in self attention layers of decoder.\n\n        Args:\n            reference_points (Tensor): The input reference of decoder,\n                has shape (bs, num_queries, 4) with the last dimension\n                arranged as (cx, cy, w, h).\n            query (Tensor): The input query of decoder, has shape\n                (bs, num_queries, dims).\n            self_attn_mask (Tensor): The input self attention mask of\n                last decoder layer, has shape (bs, num_queries_total,\n                num_queries_total).\n            layer_index (int): Last decoder layer index, used to get\n                classification score of last layer output, for\n                distinct queries selection.\n\n        Returns:\n            Tensor: `self_attn_mask` used in self attention layers\n                of decoder, has shape (bs, num_queries_total,\n                num_queries_total).\n        \"\"\"\n        num_imgs = len(reference_points)\n        dis_start, num_dis = self.cache_dict['dis_query_info']\n        # shape of self_attn_mask\n        # (batch⋅num_heads, num_queries, embed_dims)\n        dis_mask = self_attn_mask[:, dis_start:dis_start + num_dis,\n                                  dis_start:dis_start + num_dis]\n        # cls_branches from DDQDETRHead\n        scores = self.cache_dict['cls_branches'][layer_index](\n            query[:, dis_start:dis_start + num_dis]).sigmoid().max(-1).values\n        proposals = reference_points[:, dis_start:dis_start + num_dis]\n        proposals = bbox_cxcywh_to_xyxy(proposals)\n\n        attn_mask_list = []\n        for img_id in range(num_imgs):\n            single_proposals = proposals[img_id]\n            single_scores = scores[img_id]\n            attn_mask = ~dis_mask[img_id * self.cache_dict['num_heads']][0]\n            # distinct query inds in this layer\n            ori_index = attn_mask.nonzero().view(-1)\n            _, keep_idxs = batched_nms(single_proposals[ori_index],\n                                       single_scores[ori_index],\n                                       torch.ones(len(ori_index)),\n                                       self.cache_dict['dqs_cfg'])\n\n            real_keep_index = ori_index[keep_idxs]\n\n            attn_mask = torch.ones_like(dis_mask[0]).bool()\n            # such a attn_mask give best result\n            # If it requires to keep index i, then all cells in row or column\n            #   i should be kept in `attn_mask` . For example, if\n            #   `real_keep_index` = [1, 4], and `attn_mask` size = [8, 8],\n            #   then all cells at rows or columns [1, 4] should be kept, and\n            #   all the other cells should be masked out. So the value of\n            #  `attn_mask` should be:\n            #\n            # target\\source   0 1 2 3 4 5 6 7\n            #             0 [ 0 1 0 0 1 0 0 0 ]\n            #             1 [ 1 1 1 1 1 1 1 1 ]\n            #             2 [ 0 1 0 0 1 0 0 0 ]\n            #             3 [ 0 1 0 0 1 0 0 0 ]\n            #             4 [ 1 1 1 1 1 1 1 1 ]\n            #             5 [ 0 1 0 0 1 0 0 0 ]\n            #             6 [ 0 1 0 0 1 0 0 0 ]\n            #             7 [ 0 1 0 0 1 0 0 0 ]\n            attn_mask[real_keep_index] = False\n            attn_mask[:, real_keep_index] = False\n\n            attn_mask = attn_mask[None].repeat(self.cache_dict['num_heads'], 1,\n                                               1)\n            attn_mask_list.append(attn_mask)\n        attn_mask = torch.cat(attn_mask_list)\n        self_attn_mask = copy.deepcopy(self_attn_mask)\n        self_attn_mask[:, dis_start:dis_start + num_dis,\n                       dis_start:dis_start + num_dis] = attn_mask\n        # will be used in loss and inference\n        self.cache_dict['distinct_query_mask'].append(~attn_mask)\n        return self_attn_mask\n\n    def forward(self, query: Tensor, value: Tensor, key_padding_mask: Tensor,\n                self_attn_mask: Tensor, reference_points: Tensor,\n                spatial_shapes: Tensor, level_start_index: Tensor,\n                valid_ratios: Tensor, reg_branches: nn.ModuleList,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function of Transformer decoder.\n\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries,\n                dims).\n            value (Tensor): The input values, has shape (bs, num_value, dim).\n            key_padding_mask (Tensor): The `key_padding_mask` of `cross_attn`\n                input. ByteTensor, has shape (bs, num_value).\n            self_attn_mask (Tensor): The attention mask to prevent information\n                leakage from different denoising groups, distinct queries and\n                dense queries, has shape (num_queries_total,\n                num_queries_total). It will be updated for distinct queries\n                selection in this forward function. It is `None` when\n                `self.training` is `False`.\n            reference_points (Tensor): The initial reference, has shape\n                (bs, num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n            reg_branches: (obj:`nn.ModuleList`): Used for refining the\n                regression results.\n\n        Returns:\n            tuple[Tensor]: Output queries and references of Transformer\n                decoder\n\n            - query (Tensor): Output embeddings of the last decoder, has\n              shape (bs, num_queries, embed_dims) when `return_intermediate`\n              is `False`. Otherwise, Intermediate output embeddings of all\n              decoder layers, has shape (num_decoder_layers, bs, num_queries,\n              embed_dims).\n            - reference_points (Tensor): The reference of the last decoder\n              layer, has shape (bs, num_queries, 4) when `return_intermediate`\n              is `False`. Otherwise, Intermediate references of all decoder\n              layers, has shape (1 + num_decoder_layers, bs, num_queries, 4).\n              The coordinates are arranged as (cx, cy, w, h).\n        \"\"\"\n        intermediate = []\n        intermediate_reference_points = [reference_points]\n        self.cache_dict['distinct_query_mask'] = []\n        if self_attn_mask is None:\n            self_attn_mask = torch.zeros((query.size(1), query.size(1)),\n                                         device=query.device).bool()\n        # shape is (batch*number_heads, num_queries, num_queries)\n        self_attn_mask = self_attn_mask[None].repeat(\n            len(query) * self.cache_dict['num_heads'], 1, 1)\n        for layer_index, layer in enumerate(self.layers):\n            if reference_points.shape[-1] == 4:\n                reference_points_input = \\\n                    reference_points[:, :, None] * torch.cat(\n                        [valid_ratios, valid_ratios], -1)[:, None]\n            else:\n                assert reference_points.shape[-1] == 2\n                reference_points_input = \\\n                    reference_points[:, :, None] * valid_ratios[:, None]\n\n            query_sine_embed = coordinate_to_encoding(\n                reference_points_input[:, :, 0, :],\n                num_feats=self.embed_dims // 2)\n            query_pos = self.ref_point_head(query_sine_embed)\n\n            query = layer(\n                query,\n                query_pos=query_pos,\n                value=value,\n                key_padding_mask=key_padding_mask,\n                self_attn_mask=self_attn_mask,\n                spatial_shapes=spatial_shapes,\n                level_start_index=level_start_index,\n                valid_ratios=valid_ratios,\n                reference_points=reference_points_input,\n                **kwargs)\n\n            if not self.training:\n                tmp = reg_branches[layer_index](query)\n                assert reference_points.shape[-1] == 4\n                new_reference_points = tmp + inverse_sigmoid(\n                    reference_points, eps=1e-3)\n                new_reference_points = new_reference_points.sigmoid()\n                reference_points = new_reference_points.detach()\n                if layer_index < (len(self.layers) - 1):\n                    self_attn_mask = self.select_distinct_queries(\n                        reference_points, query, self_attn_mask, layer_index)\n\n            else:\n                num_dense = self.cache_dict['num_dense_queries']\n                tmp = reg_branches[layer_index](query[:, :-num_dense])\n                tmp_dense = self.aux_reg_branches[layer_index](\n                    query[:, -num_dense:])\n\n                tmp = torch.cat([tmp, tmp_dense], dim=1)\n                assert reference_points.shape[-1] == 4\n                new_reference_points = tmp + inverse_sigmoid(\n                    reference_points, eps=1e-3)\n                new_reference_points = new_reference_points.sigmoid()\n                reference_points = new_reference_points.detach()\n                if layer_index < (len(self.layers) - 1):\n                    self_attn_mask = self.select_distinct_queries(\n                        reference_points, query, self_attn_mask, layer_index)\n\n            if self.return_intermediate:\n                intermediate.append(self.norm(query))\n                intermediate_reference_points.append(new_reference_points)\n\n        if self.return_intermediate:\n            return torch.stack(intermediate), torch.stack(\n                intermediate_reference_points)\n\n        return query, reference_points\n"
  },
  {
    "path": "mmdet/models/layers/transformer/deformable_detr_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport torch\nfrom mmcv.cnn import build_norm_layer\nfrom mmcv.cnn.bricks.transformer import FFN, MultiheadAttention\nfrom mmcv.ops import MultiScaleDeformableAttention\nfrom mmengine.model import ModuleList\nfrom torch import Tensor, nn\n\nfrom .detr_layers import (DetrTransformerDecoder, DetrTransformerDecoderLayer,\n                          DetrTransformerEncoder, DetrTransformerEncoderLayer)\nfrom .utils import inverse_sigmoid\n\ntry:\n    from fairscale.nn.checkpoint import checkpoint_wrapper\nexcept Exception:\n    checkpoint_wrapper = None\n\n\nclass DeformableDetrTransformerEncoder(DetrTransformerEncoder):\n    \"\"\"Transformer encoder of Deformable DETR.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize encoder layers.\"\"\"\n        self.layers = ModuleList([\n            DeformableDetrTransformerEncoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n\n        if self.num_cp > 0:\n            if checkpoint_wrapper is None:\n                raise NotImplementedError(\n                    'If you want to reduce GPU memory usage, \\\n                    please install fairscale by executing the \\\n                    following command: pip install fairscale.')\n            for i in range(self.num_cp):\n                self.layers[i] = checkpoint_wrapper(self.layers[i])\n\n        self.embed_dims = self.layers[0].embed_dims\n\n    def forward(self, query: Tensor, query_pos: Tensor,\n                key_padding_mask: Tensor, spatial_shapes: Tensor,\n                level_start_index: Tensor, valid_ratios: Tensor,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function of Transformer encoder.\n\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            query_pos (Tensor): The positional encoding for query, has shape\n                (bs, num_queries, dim).\n            key_padding_mask (Tensor): The `key_padding_mask` of `self_attn`\n                input. ByteTensor, has shape (bs, num_queries).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n\n        Returns:\n            Tensor: Output queries of Transformer encoder, which is also\n            called 'encoder output embeddings' or 'memory', has shape\n            (bs, num_queries, dim)\n        \"\"\"\n        reference_points = self.get_encoder_reference_points(\n            spatial_shapes, valid_ratios, device=query.device)\n        for layer in self.layers:\n            query = layer(\n                query=query,\n                query_pos=query_pos,\n                key_padding_mask=key_padding_mask,\n                spatial_shapes=spatial_shapes,\n                level_start_index=level_start_index,\n                valid_ratios=valid_ratios,\n                reference_points=reference_points,\n                **kwargs)\n        return query\n\n    @staticmethod\n    def get_encoder_reference_points(\n            spatial_shapes: Tensor, valid_ratios: Tensor,\n            device: Union[torch.device, str]) -> Tensor:\n        \"\"\"Get the reference points used in encoder.\n\n        Args:\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n            device (obj:`device` or str): The device acquired by the\n                `reference_points`.\n\n        Returns:\n            Tensor: Reference points used in decoder, has shape (bs, length,\n            num_levels, 2).\n        \"\"\"\n\n        reference_points_list = []\n        for lvl, (H, W) in enumerate(spatial_shapes):\n            ref_y, ref_x = torch.meshgrid(\n                torch.linspace(\n                    0.5, H - 0.5, H, dtype=torch.float32, device=device),\n                torch.linspace(\n                    0.5, W - 0.5, W, dtype=torch.float32, device=device))\n            ref_y = ref_y.reshape(-1)[None] / (\n                valid_ratios[:, None, lvl, 1] * H)\n            ref_x = ref_x.reshape(-1)[None] / (\n                valid_ratios[:, None, lvl, 0] * W)\n            ref = torch.stack((ref_x, ref_y), -1)\n            reference_points_list.append(ref)\n        reference_points = torch.cat(reference_points_list, 1)\n        # [bs, sum(hw), num_level, 2]\n        reference_points = reference_points[:, :, None] * valid_ratios[:, None]\n        return reference_points\n\n\nclass DeformableDetrTransformerDecoder(DetrTransformerDecoder):\n    \"\"\"Transformer Decoder of Deformable DETR.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize decoder layers.\"\"\"\n        self.layers = ModuleList([\n            DeformableDetrTransformerDecoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.embed_dims = self.layers[0].embed_dims\n        if self.post_norm_cfg is not None:\n            raise ValueError('There is not post_norm in '\n                             f'{self._get_name()}')\n\n    def forward(self,\n                query: Tensor,\n                query_pos: Tensor,\n                value: Tensor,\n                key_padding_mask: Tensor,\n                reference_points: Tensor,\n                spatial_shapes: Tensor,\n                level_start_index: Tensor,\n                valid_ratios: Tensor,\n                reg_branches: Optional[nn.Module] = None,\n                **kwargs) -> Tuple[Tensor]:\n        \"\"\"Forward function of Transformer decoder.\n\n        Args:\n            query (Tensor): The input queries, has shape (bs, num_queries,\n                dim).\n            query_pos (Tensor): The input positional query, has shape\n                (bs, num_queries, dim). It will be added to `query` before\n                forward function.\n            value (Tensor): The input values, has shape (bs, num_value, dim).\n            key_padding_mask (Tensor): The `key_padding_mask` of `cross_attn`\n                input. ByteTensor, has shape (bs, num_value).\n            reference_points (Tensor): The initial reference, has shape\n                (bs, num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h) when `as_two_stage` is `True`, otherwise has\n                shape (bs, num_queries, 2) with the last dimension arranged\n                as (cx, cy).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n            reg_branches: (obj:`nn.ModuleList`, optional): Used for refining\n                the regression results. Only would be passed when\n                `with_box_refine` is `True`, otherwise would be `None`.\n\n        Returns:\n            tuple[Tensor]: Outputs of Deformable Transformer Decoder.\n\n            - output (Tensor): Output embeddings of the last decoder, has\n              shape (num_queries, bs, embed_dims) when `return_intermediate`\n              is `False`. Otherwise, Intermediate output embeddings of all\n              decoder layers, has shape (num_decoder_layers, num_queries, bs,\n              embed_dims).\n            - reference_points (Tensor): The reference of the last decoder\n              layer, has shape (bs, num_queries, 4)  when `return_intermediate`\n              is `False`. Otherwise, Intermediate references of all decoder\n              layers, has shape (num_decoder_layers, bs, num_queries, 4). The\n              coordinates are arranged as (cx, cy, w, h)\n        \"\"\"\n        output = query\n        intermediate = []\n        intermediate_reference_points = []\n        for layer_id, layer in enumerate(self.layers):\n            if reference_points.shape[-1] == 4:\n                reference_points_input = \\\n                    reference_points[:, :, None] * \\\n                    torch.cat([valid_ratios, valid_ratios], -1)[:, None]\n            else:\n                assert reference_points.shape[-1] == 2\n                reference_points_input = \\\n                    reference_points[:, :, None] * \\\n                    valid_ratios[:, None]\n            output = layer(\n                output,\n                query_pos=query_pos,\n                value=value,\n                key_padding_mask=key_padding_mask,\n                spatial_shapes=spatial_shapes,\n                level_start_index=level_start_index,\n                valid_ratios=valid_ratios,\n                reference_points=reference_points_input,\n                **kwargs)\n\n            if reg_branches is not None:\n                tmp_reg_preds = reg_branches[layer_id](output)\n                if reference_points.shape[-1] == 4:\n                    new_reference_points = tmp_reg_preds + inverse_sigmoid(\n                        reference_points)\n                    new_reference_points = new_reference_points.sigmoid()\n                else:\n                    assert reference_points.shape[-1] == 2\n                    new_reference_points = tmp_reg_preds\n                    new_reference_points[..., :2] = tmp_reg_preds[\n                        ..., :2] + inverse_sigmoid(reference_points)\n                    new_reference_points = new_reference_points.sigmoid()\n                reference_points = new_reference_points.detach()\n\n            if self.return_intermediate:\n                intermediate.append(output)\n                intermediate_reference_points.append(reference_points)\n\n        if self.return_intermediate:\n            return torch.stack(intermediate), torch.stack(\n                intermediate_reference_points)\n\n        return output, reference_points\n\n\nclass DeformableDetrTransformerEncoderLayer(DetrTransformerEncoderLayer):\n    \"\"\"Encoder layer of Deformable DETR.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize self_attn, ffn, and norms.\"\"\"\n        self.self_attn = MultiScaleDeformableAttention(**self.self_attn_cfg)\n        self.embed_dims = self.self_attn.embed_dims\n        self.ffn = FFN(**self.ffn_cfg)\n        norms_list = [\n            build_norm_layer(self.norm_cfg, self.embed_dims)[1]\n            for _ in range(2)\n        ]\n        self.norms = ModuleList(norms_list)\n\n\nclass DeformableDetrTransformerDecoderLayer(DetrTransformerDecoderLayer):\n    \"\"\"Decoder layer of Deformable DETR.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize self_attn, cross-attn, ffn, and norms.\"\"\"\n        self.self_attn = MultiheadAttention(**self.self_attn_cfg)\n        self.cross_attn = MultiScaleDeformableAttention(**self.cross_attn_cfg)\n        self.embed_dims = self.self_attn.embed_dims\n        self.ffn = FFN(**self.ffn_cfg)\n        norms_list = [\n            build_norm_layer(self.norm_cfg, self.embed_dims)[1]\n            for _ in range(3)\n        ]\n        self.norms = ModuleList(norms_list)\n"
  },
  {
    "path": "mmdet/models/layers/transformer/detr_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nimport torch\nfrom mmcv.cnn import build_norm_layer\nfrom mmcv.cnn.bricks.transformer import FFN, MultiheadAttention\nfrom mmengine import ConfigDict\nfrom mmengine.model import BaseModule, ModuleList\nfrom torch import Tensor\n\nfrom mmdet.utils import ConfigType, OptConfigType\n\ntry:\n    from fairscale.nn.checkpoint import checkpoint_wrapper\nexcept Exception:\n    checkpoint_wrapper = None\n\n\nclass DetrTransformerEncoder(BaseModule):\n    \"\"\"Encoder of DETR.\n\n    Args:\n        num_layers (int): Number of encoder layers.\n        layer_cfg (:obj:`ConfigDict` or dict): the config of each encoder\n            layer. All the layers will share the same config.\n        num_cp (int): Number of checkpointing blocks in encoder layer.\n            Default to -1.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_layers: int,\n                 layer_cfg: ConfigType,\n                 num_cp: int = -1,\n                 init_cfg: OptConfigType = None) -> None:\n\n        super().__init__(init_cfg=init_cfg)\n        self.num_layers = num_layers\n        self.layer_cfg = layer_cfg\n        self.num_cp = num_cp\n        assert self.num_cp <= self.num_layers\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize encoder layers.\"\"\"\n        self.layers = ModuleList([\n            DetrTransformerEncoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n\n        if self.num_cp > 0:\n            if checkpoint_wrapper is None:\n                raise NotImplementedError(\n                    'If you want to reduce GPU memory usage, \\\n                    please install fairscale by executing the \\\n                    following command: pip install fairscale.')\n            for i in range(self.num_cp):\n                self.layers[i] = checkpoint_wrapper(self.layers[i])\n\n        self.embed_dims = self.layers[0].embed_dims\n\n    def forward(self, query: Tensor, query_pos: Tensor,\n                key_padding_mask: Tensor, **kwargs) -> Tensor:\n        \"\"\"Forward function of encoder.\n\n        Args:\n            query (Tensor): Input queries of encoder, has shape\n                (bs, num_queries, dim).\n            query_pos (Tensor): The positional embeddings of the queries, has\n                shape (bs, num_queries, dim).\n            key_padding_mask (Tensor): The `key_padding_mask` of `self_attn`\n                input. ByteTensor, has shape (bs, num_queries).\n\n        Returns:\n            Tensor: Has shape (bs, num_queries, dim) if `batch_first` is\n            `True`, otherwise (num_queries, bs, dim).\n        \"\"\"\n        for layer in self.layers:\n            query = layer(query, query_pos, key_padding_mask, **kwargs)\n        return query\n\n\nclass DetrTransformerDecoder(BaseModule):\n    \"\"\"Decoder of DETR.\n\n    Args:\n        num_layers (int): Number of decoder layers.\n        layer_cfg (:obj:`ConfigDict` or dict): the config of each encoder\n            layer. All the layers will share the same config.\n        post_norm_cfg (:obj:`ConfigDict` or dict, optional): Config of the\n            post normalization layer. Defaults to `LN`.\n        return_intermediate (bool, optional): Whether to return outputs of\n            intermediate layers. Defaults to `True`,\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_layers: int,\n                 layer_cfg: ConfigType,\n                 post_norm_cfg: OptConfigType = dict(type='LN'),\n                 return_intermediate: bool = True,\n                 init_cfg: Union[dict, ConfigDict] = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.layer_cfg = layer_cfg\n        self.num_layers = num_layers\n        self.post_norm_cfg = post_norm_cfg\n        self.return_intermediate = return_intermediate\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize decoder layers.\"\"\"\n        self.layers = ModuleList([\n            DetrTransformerDecoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.embed_dims = self.layers[0].embed_dims\n        self.post_norm = build_norm_layer(self.post_norm_cfg,\n                                          self.embed_dims)[1]\n\n    def forward(self, query: Tensor, key: Tensor, value: Tensor,\n                query_pos: Tensor, key_pos: Tensor, key_padding_mask: Tensor,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function of decoder\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            key (Tensor): The input key, has shape (bs, num_keys, dim).\n            value (Tensor): The input value with the same shape as `key`.\n            query_pos (Tensor): The positional encoding for `query`, with the\n                same shape as `query`.\n            key_pos (Tensor): The positional encoding for `key`, with the\n                same shape as `key`.\n            key_padding_mask (Tensor): The `key_padding_mask` of `cross_attn`\n                input. ByteTensor, has shape (bs, num_value).\n\n        Returns:\n            Tensor: The forwarded results will have shape\n            (num_decoder_layers, bs, num_queries, dim) if\n            `return_intermediate` is `True` else (1, bs, num_queries, dim).\n        \"\"\"\n        intermediate = []\n        for layer in self.layers:\n            query = layer(\n                query,\n                key=key,\n                value=value,\n                query_pos=query_pos,\n                key_pos=key_pos,\n                key_padding_mask=key_padding_mask,\n                **kwargs)\n            if self.return_intermediate:\n                intermediate.append(self.post_norm(query))\n        query = self.post_norm(query)\n\n        if self.return_intermediate:\n            return torch.stack(intermediate)\n\n        return query.unsqueeze(0)\n\n\nclass DetrTransformerEncoderLayer(BaseModule):\n    \"\"\"Implements encoder layer in DETR transformer.\n\n    Args:\n        self_attn_cfg (:obj:`ConfigDict` or dict, optional): Config for self\n            attention.\n        ffn_cfg (:obj:`ConfigDict` or dict, optional): Config for FFN.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Config for\n            normalization layers. All the layers will share the same\n            config. Defaults to `LN`.\n        init_cfg (:obj:`ConfigDict` or dict, optional): Config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 self_attn_cfg: OptConfigType = dict(\n                     embed_dims=256, num_heads=8, dropout=0.0),\n                 ffn_cfg: OptConfigType = dict(\n                     embed_dims=256,\n                     feedforward_channels=1024,\n                     num_fcs=2,\n                     ffn_drop=0.,\n                     act_cfg=dict(type='ReLU', inplace=True)),\n                 norm_cfg: OptConfigType = dict(type='LN'),\n                 init_cfg: OptConfigType = None) -> None:\n\n        super().__init__(init_cfg=init_cfg)\n\n        self.self_attn_cfg = self_attn_cfg\n        if 'batch_first' not in self.self_attn_cfg:\n            self.self_attn_cfg['batch_first'] = True\n        else:\n            assert self.self_attn_cfg['batch_first'] is True, 'First \\\n            dimension of all DETRs in mmdet is `batch`, \\\n            please set `batch_first` flag.'\n\n        self.ffn_cfg = ffn_cfg\n        self.norm_cfg = norm_cfg\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize self-attention, FFN, and normalization.\"\"\"\n        self.self_attn = MultiheadAttention(**self.self_attn_cfg)\n        self.embed_dims = self.self_attn.embed_dims\n        self.ffn = FFN(**self.ffn_cfg)\n        norms_list = [\n            build_norm_layer(self.norm_cfg, self.embed_dims)[1]\n            for _ in range(2)\n        ]\n        self.norms = ModuleList(norms_list)\n\n    def forward(self, query: Tensor, query_pos: Tensor,\n                key_padding_mask: Tensor, **kwargs) -> Tensor:\n        \"\"\"Forward function of an encoder layer.\n\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            query_pos (Tensor): The positional encoding for query, with\n                the same shape as `query`.\n            key_padding_mask (Tensor): The `key_padding_mask` of `self_attn`\n                input. ByteTensor. has shape (bs, num_queries).\n        Returns:\n            Tensor: forwarded results, has shape (bs, num_queries, dim).\n        \"\"\"\n        query = self.self_attn(\n            query=query,\n            key=query,\n            value=query,\n            query_pos=query_pos,\n            key_pos=query_pos,\n            key_padding_mask=key_padding_mask,\n            **kwargs)\n        query = self.norms[0](query)\n        query = self.ffn(query)\n        query = self.norms[1](query)\n\n        return query\n\n\nclass DetrTransformerDecoderLayer(BaseModule):\n    \"\"\"Implements decoder layer in DETR transformer.\n\n    Args:\n        self_attn_cfg (:obj:`ConfigDict` or dict, optional): Config for self\n            attention.\n        cross_attn_cfg (:obj:`ConfigDict` or dict, optional): Config for cross\n            attention.\n        ffn_cfg (:obj:`ConfigDict` or dict, optional): Config for FFN.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Config for\n            normalization layers. All the layers will share the same\n            config. Defaults to `LN`.\n        init_cfg (:obj:`ConfigDict` or dict, optional): Config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 self_attn_cfg: OptConfigType = dict(\n                     embed_dims=256,\n                     num_heads=8,\n                     dropout=0.0,\n                     batch_first=True),\n                 cross_attn_cfg: OptConfigType = dict(\n                     embed_dims=256,\n                     num_heads=8,\n                     dropout=0.0,\n                     batch_first=True),\n                 ffn_cfg: OptConfigType = dict(\n                     embed_dims=256,\n                     feedforward_channels=1024,\n                     num_fcs=2,\n                     ffn_drop=0.,\n                     act_cfg=dict(type='ReLU', inplace=True),\n                 ),\n                 norm_cfg: OptConfigType = dict(type='LN'),\n                 init_cfg: OptConfigType = None) -> None:\n\n        super().__init__(init_cfg=init_cfg)\n\n        self.self_attn_cfg = self_attn_cfg\n        self.cross_attn_cfg = cross_attn_cfg\n        if 'batch_first' not in self.self_attn_cfg:\n            self.self_attn_cfg['batch_first'] = True\n        else:\n            assert self.self_attn_cfg['batch_first'] is True, 'First \\\n            dimension of all DETRs in mmdet is `batch`, \\\n            please set `batch_first` flag.'\n\n        if 'batch_first' not in self.cross_attn_cfg:\n            self.cross_attn_cfg['batch_first'] = True\n        else:\n            assert self.cross_attn_cfg['batch_first'] is True, 'First \\\n            dimension of all DETRs in mmdet is `batch`, \\\n            please set `batch_first` flag.'\n\n        self.ffn_cfg = ffn_cfg\n        self.norm_cfg = norm_cfg\n        self._init_layers()\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize self-attention, FFN, and normalization.\"\"\"\n        self.self_attn = MultiheadAttention(**self.self_attn_cfg)\n        self.cross_attn = MultiheadAttention(**self.cross_attn_cfg)\n        self.embed_dims = self.self_attn.embed_dims\n        self.ffn = FFN(**self.ffn_cfg)\n        norms_list = [\n            build_norm_layer(self.norm_cfg, self.embed_dims)[1]\n            for _ in range(3)\n        ]\n        self.norms = ModuleList(norms_list)\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor = None,\n                value: Tensor = None,\n                query_pos: Tensor = None,\n                key_pos: Tensor = None,\n                self_attn_mask: Tensor = None,\n                cross_attn_mask: Tensor = None,\n                key_padding_mask: Tensor = None,\n                **kwargs) -> Tensor:\n        \"\"\"\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            key (Tensor, optional): The input key, has shape (bs, num_keys,\n                dim). If `None`, the `query` will be used. Defaults to `None`.\n            value (Tensor, optional): The input value, has the same shape as\n                `key`, as in `nn.MultiheadAttention.forward`. If `None`, the\n                `key` will be used. Defaults to `None`.\n            query_pos (Tensor, optional): The positional encoding for `query`,\n                has the same shape as `query`. If not `None`, it will be added\n                to `query` before forward function. Defaults to `None`.\n            key_pos (Tensor, optional): The positional encoding for `key`, has\n                the same shape as `key`. If not `None`, it will be added to\n                `key` before forward function. If None, and `query_pos` has the\n                same shape as `key`, then `query_pos` will be used for\n                `key_pos`. Defaults to None.\n            self_attn_mask (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), as in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            cross_attn_mask (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), as in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            key_padding_mask (Tensor, optional): The `key_padding_mask` of\n                `self_attn` input. ByteTensor, has shape (bs, num_value).\n                Defaults to None.\n\n        Returns:\n            Tensor: forwarded results, has shape (bs, num_queries, dim).\n        \"\"\"\n\n        query = self.self_attn(\n            query=query,\n            key=query,\n            value=query,\n            query_pos=query_pos,\n            key_pos=query_pos,\n            attn_mask=self_attn_mask,\n            **kwargs)\n        query = self.norms[0](query)\n        query = self.cross_attn(\n            query=query,\n            key=key,\n            value=value,\n            query_pos=query_pos,\n            key_pos=key_pos,\n            attn_mask=cross_attn_mask,\n            key_padding_mask=key_padding_mask,\n            **kwargs)\n        query = self.norms[1](query)\n        query = self.ffn(query)\n        query = self.norms[2](query)\n\n        return query\n"
  },
  {
    "path": "mmdet/models/layers/transformer/dino_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import Tuple, Union\n\nimport torch\nfrom mmengine.model import BaseModule\nfrom torch import Tensor, nn\n\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox_xyxy_to_cxcywh\nfrom mmdet.utils import OptConfigType\nfrom .deformable_detr_layers import DeformableDetrTransformerDecoder\nfrom .utils import MLP, coordinate_to_encoding, inverse_sigmoid\n\n\nclass DinoTransformerDecoder(DeformableDetrTransformerDecoder):\n    \"\"\"Transformer decoder of DINO.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize decoder layers.\"\"\"\n        super()._init_layers()\n        self.ref_point_head = MLP(self.embed_dims * 2, self.embed_dims,\n                                  self.embed_dims, 2)\n        self.norm = nn.LayerNorm(self.embed_dims)\n\n    def forward(self, query: Tensor, value: Tensor, key_padding_mask: Tensor,\n                self_attn_mask: Tensor, reference_points: Tensor,\n                spatial_shapes: Tensor, level_start_index: Tensor,\n                valid_ratios: Tensor, reg_branches: nn.ModuleList,\n                **kwargs) -> Tuple[Tensor]:\n        \"\"\"Forward function of Transformer decoder.\n\n        Args:\n            query (Tensor): The input query, has shape (num_queries, bs, dim).\n            value (Tensor): The input values, has shape (num_value, bs, dim).\n            key_padding_mask (Tensor): The `key_padding_mask` of `self_attn`\n                input. ByteTensor, has shape (num_queries, bs).\n            self_attn_mask (Tensor): The attention mask to prevent information\n                leakage from different denoising groups and matching parts, has\n                shape (num_queries_total, num_queries_total). It is `None` when\n                `self.training` is `False`.\n            reference_points (Tensor): The initial reference, has shape\n                (bs, num_queries, 4) with the last dimension arranged as\n                (cx, cy, w, h).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n            reg_branches: (obj:`nn.ModuleList`): Used for refining the\n                regression results.\n\n        Returns:\n            tuple[Tensor]: Output queries and references of Transformer\n                decoder\n\n            - query (Tensor): Output embeddings of the last decoder, has\n              shape (num_queries, bs, embed_dims) when `return_intermediate`\n              is `False`. Otherwise, Intermediate output embeddings of all\n              decoder layers, has shape (num_decoder_layers, num_queries, bs,\n              embed_dims).\n            - reference_points (Tensor): The reference of the last decoder\n              layer, has shape (bs, num_queries, 4)  when `return_intermediate`\n              is `False`. Otherwise, Intermediate references of all decoder\n              layers, has shape (num_decoder_layers, bs, num_queries, 4). The\n              coordinates are arranged as (cx, cy, w, h)\n        \"\"\"\n        intermediate = []\n        intermediate_reference_points = [reference_points]\n        for lid, layer in enumerate(self.layers):\n            if reference_points.shape[-1] == 4:\n                reference_points_input = \\\n                    reference_points[:, :, None] * torch.cat(\n                        [valid_ratios, valid_ratios], -1)[:, None]\n            else:\n                assert reference_points.shape[-1] == 2\n                reference_points_input = \\\n                    reference_points[:, :, None] * valid_ratios[:, None]\n\n            query_sine_embed = coordinate_to_encoding(\n                reference_points_input[:, :, 0, :])\n            query_pos = self.ref_point_head(query_sine_embed)\n\n            query = layer(\n                query,\n                query_pos=query_pos,\n                value=value,\n                key_padding_mask=key_padding_mask,\n                self_attn_mask=self_attn_mask,\n                spatial_shapes=spatial_shapes,\n                level_start_index=level_start_index,\n                valid_ratios=valid_ratios,\n                reference_points=reference_points_input,\n                **kwargs)\n\n            if reg_branches is not None:\n                tmp = reg_branches[lid](query)\n                assert reference_points.shape[-1] == 4\n                new_reference_points = tmp + inverse_sigmoid(\n                    reference_points, eps=1e-3)\n                new_reference_points = new_reference_points.sigmoid()\n                reference_points = new_reference_points.detach()\n\n            if self.return_intermediate:\n                intermediate.append(self.norm(query))\n                intermediate_reference_points.append(new_reference_points)\n                # NOTE this is for the \"Look Forward Twice\" module,\n                # in the DeformDETR, reference_points was appended.\n\n        if self.return_intermediate:\n            return torch.stack(intermediate), torch.stack(\n                intermediate_reference_points)\n\n        return query, reference_points\n\n\nclass CdnQueryGenerator(BaseModule):\n    \"\"\"Implement query generator of the Contrastive denoising (CDN) proposed in\n    `DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object\n    Detection <https://arxiv.org/abs/2203.03605>`_\n\n    Code is modified from the `official github repo\n    <https://github.com/IDEA-Research/DINO>`_.\n\n    Args:\n        num_classes (int): Number of object classes.\n        embed_dims (int): The embedding dimensions of the generated queries.\n        num_matching_queries (int): The queries number of the matching part.\n            Used for generating dn_mask.\n        label_noise_scale (float): The scale of label noise, defaults to 0.5.\n        box_noise_scale (float): The scale of box noise, defaults to 1.0.\n        group_cfg (:obj:`ConfigDict` or dict, optional): The config of the\n            denoising queries grouping, includes `dynamic`, `num_dn_queries`,\n            and `num_groups`. Two grouping strategies, 'static dn groups' and\n            'dynamic dn groups', are supported. When `dynamic` is `False`,\n            the `num_groups` should be set, and the number of denoising query\n            groups will always be `num_groups`. When `dynamic` is `True`, the\n            `num_dn_queries` should be set, and the group number will be\n            dynamic to ensure that the denoising queries number will not exceed\n            `num_dn_queries` to prevent large fluctuations of memory. Defaults\n            to `None`.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 embed_dims: int,\n                 num_matching_queries: int,\n                 label_noise_scale: float = 0.5,\n                 box_noise_scale: float = 1.0,\n                 group_cfg: OptConfigType = None) -> None:\n        super().__init__()\n        self.num_classes = num_classes\n        self.embed_dims = embed_dims\n        self.num_matching_queries = num_matching_queries\n        self.label_noise_scale = label_noise_scale\n        self.box_noise_scale = box_noise_scale\n\n        # prepare grouping strategy\n        group_cfg = {} if group_cfg is None else group_cfg\n        self.dynamic_dn_groups = group_cfg.get('dynamic', True)\n        if self.dynamic_dn_groups:\n            if 'num_dn_queries' not in group_cfg:\n                warnings.warn(\"'num_dn_queries' should be set when using \"\n                              'dynamic dn groups, use 100 as default.')\n            self.num_dn_queries = group_cfg.get('num_dn_queries', 100)\n            assert isinstance(self.num_dn_queries, int), \\\n                f'Expected the num_dn_queries to have type int, but got ' \\\n                f'{self.num_dn_queries}({type(self.num_dn_queries)}). '\n        else:\n            assert 'num_groups' in group_cfg, \\\n                'num_groups should be set when using static dn groups'\n            self.num_groups = group_cfg['num_groups']\n            assert isinstance(self.num_groups, int), \\\n                f'Expected the num_groups to have type int, but got ' \\\n                f'{self.num_groups}({type(self.num_groups)}). '\n\n        # NOTE The original repo of DINO set the num_embeddings 92 for coco,\n        # 91 (0~90) of which represents target classes and the 92 (91)\n        # indicates `Unknown` class. However, the embedding of `unknown` class\n        # is not used in the original DINO.\n        # TODO: num_classes + 1 or num_classes ?\n        self.label_embedding = nn.Embedding(self.num_classes, self.embed_dims)\n\n    def __call__(self, batch_data_samples: SampleList) -> tuple:\n        \"\"\"Generate contrastive denoising (cdn) queries with ground truth.\n\n        Descriptions of the Number Values in code and comments:\n            - num_target_total: the total target number of the input batch\n              samples.\n            - max_num_target: the max target number of the input batch samples.\n            - num_noisy_targets: the total targets number after adding noise,\n              i.e., num_target_total * num_groups * 2.\n            - num_denoising_queries: the length of the output batched queries,\n              i.e., max_num_target * num_groups * 2.\n\n        NOTE The format of input bboxes in batch_data_samples is unnormalized\n        (x, y, x, y), and the output bbox queries are embedded by normalized\n        (cx, cy, w, h) format bboxes going through inverse_sigmoid.\n\n        Args:\n            batch_data_samples (list[:obj:`DetDataSample`]): List of the batch\n                data samples, each includes `gt_instance` which has attributes\n                `bboxes` and `labels`. The `bboxes` has unnormalized coordinate\n                format (x, y, x, y).\n\n        Returns:\n            tuple: The outputs of the dn query generator.\n\n            - dn_label_query (Tensor): The output content queries for denoising\n              part, has shape (bs, num_denoising_queries, dim), where\n              `num_denoising_queries = max_num_target * num_groups * 2`.\n            - dn_bbox_query (Tensor): The output reference bboxes as positions\n              of queries for denoising part, which are embedded by normalized\n              (cx, cy, w, h) format bboxes going through inverse_sigmoid, has\n              shape (bs, num_denoising_queries, 4) with the last dimension\n              arranged as (cx, cy, w, h).\n            - attn_mask (Tensor): The attention mask to prevent information\n              leakage from different denoising groups and matching parts,\n              will be used as `self_attn_mask` of the `decoder`, has shape\n              (num_queries_total, num_queries_total), where `num_queries_total`\n              is the sum of `num_denoising_queries` and `num_matching_queries`.\n            - dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n        \"\"\"\n        # normalize bbox and collate ground truth (gt)\n        gt_labels_list = []\n        gt_bboxes_list = []\n        for sample in batch_data_samples:\n            img_h, img_w = sample.img_shape\n            bboxes = sample.gt_instances.bboxes\n            factor = bboxes.new_tensor([img_w, img_h, img_w,\n                                        img_h]).unsqueeze(0)\n            bboxes_normalized = bboxes / factor\n            gt_bboxes_list.append(bboxes_normalized)\n            gt_labels_list.append(sample.gt_instances.labels)\n        gt_labels = torch.cat(gt_labels_list)  # (num_target_total, 4)\n        gt_bboxes = torch.cat(gt_bboxes_list)\n\n        num_target_list = [len(bboxes) for bboxes in gt_bboxes_list]\n        max_num_target = max(num_target_list)\n        num_groups = self.get_num_groups(max_num_target)\n\n        dn_label_query = self.generate_dn_label_query(gt_labels, num_groups)\n        dn_bbox_query = self.generate_dn_bbox_query(gt_bboxes, num_groups)\n\n        # The `batch_idx` saves the batch index of the corresponding sample\n        # for each target, has shape (num_target_total).\n        batch_idx = torch.cat([\n            torch.full_like(t.long(), i) for i, t in enumerate(gt_labels_list)\n        ])\n        dn_label_query, dn_bbox_query = self.collate_dn_queries(\n            dn_label_query, dn_bbox_query, batch_idx, len(batch_data_samples),\n            num_groups)\n\n        attn_mask = self.generate_dn_mask(\n            max_num_target, num_groups, device=dn_label_query.device)\n\n        dn_meta = dict(\n            num_denoising_queries=int(max_num_target * 2 * num_groups),\n            num_denoising_groups=num_groups)\n\n        return dn_label_query, dn_bbox_query, attn_mask, dn_meta\n\n    def get_num_groups(self, max_num_target: int = None) -> int:\n        \"\"\"Calculate denoising query groups number.\n\n        Two grouping strategies, 'static dn groups' and 'dynamic dn groups',\n        are supported. When `self.dynamic_dn_groups` is `False`, the number\n        of denoising query groups will always be `self.num_groups`. When\n        `self.dynamic_dn_groups` is `True`, the group number will be dynamic,\n        ensuring the denoising queries number will not exceed\n        `self.num_dn_queries` to prevent large fluctuations of memory.\n\n        NOTE The `num_group` is shared for different samples in a batch. When\n        the target numbers in the samples varies, the denoising queries of the\n        samples containing fewer targets are padded to the max length.\n\n        Args:\n            max_num_target (int, optional): The max target number of the batch\n                samples. It will only be used when `self.dynamic_dn_groups` is\n                `True`. Defaults to `None`.\n\n        Returns:\n            int: The denoising group number of the current batch.\n        \"\"\"\n        if self.dynamic_dn_groups:\n            assert max_num_target is not None, \\\n                'group_queries should be provided when using ' \\\n                'dynamic dn groups'\n            if max_num_target == 0:\n                num_groups = 1\n            else:\n                num_groups = self.num_dn_queries // max_num_target\n        else:\n            num_groups = self.num_groups\n        if num_groups < 1:\n            num_groups = 1\n        return int(num_groups)\n\n    def generate_dn_label_query(self, gt_labels: Tensor,\n                                num_groups: int) -> Tensor:\n        \"\"\"Generate noisy labels and their query embeddings.\n\n        The strategy for generating noisy labels is: Randomly choose labels of\n        `self.label_noise_scale * 0.5` proportion and override each of them\n        with a random object category label.\n\n        NOTE Not add noise to all labels. Besides, the `self.label_noise_scale\n        * 0.5` arg is the ratio of the chosen positions, which is higher than\n        the actual proportion of noisy labels, because the labels to override\n        may be correct. And the gap becomes larger as the number of target\n        categories decreases. The users should notice this and modify the scale\n        arg or the corresponding logic according to specific dataset.\n\n        Args:\n            gt_labels (Tensor): The concatenated gt labels of all samples\n                in the batch, has shape (num_target_total, ) where\n                `num_target_total = sum(num_target_list)`.\n            num_groups (int): The number of denoising query groups.\n\n        Returns:\n            Tensor: The query embeddings of noisy labels, has shape\n            (num_noisy_targets, embed_dims), where `num_noisy_targets =\n            num_target_total * num_groups * 2`.\n        \"\"\"\n        assert self.label_noise_scale > 0\n        gt_labels_expand = gt_labels.repeat(2 * num_groups,\n                                            1).view(-1)  # Note `* 2`  # noqa\n        p = torch.rand_like(gt_labels_expand.float())\n        chosen_indice = torch.nonzero(p < (self.label_noise_scale * 0.5)).view(\n            -1)  # Note `* 0.5`\n        new_labels = torch.randint_like(chosen_indice, 0, self.num_classes)\n        noisy_labels_expand = gt_labels_expand.scatter(0, chosen_indice,\n                                                       new_labels)\n        dn_label_query = self.label_embedding(noisy_labels_expand)\n        return dn_label_query\n\n    def generate_dn_bbox_query(self, gt_bboxes: Tensor,\n                               num_groups: int) -> Tensor:\n        \"\"\"Generate noisy bboxes and their query embeddings.\n\n        The strategy for generating noisy bboxes is as follow:\n\n        .. code:: text\n\n            +--------------------+\n            |      negative      |\n            |    +----------+    |\n            |    | positive |    |\n            |    |    +-----|----+------------+\n            |    |    |     |    |            |\n            |    +----+-----+    |            |\n            |         |          |            |\n            +---------+----------+            |\n                      |                       |\n                      |        gt bbox        |\n                      |                       |\n                      |             +---------+----------+\n                      |             |         |          |\n                      |             |    +----+-----+    |\n                      |             |    |    |     |    |\n                      +-------------|--- +----+     |    |\n                                    |    | positive |    |\n                                    |    +----------+    |\n                                    |      negative      |\n                                    +--------------------+\n\n         The random noise is added to the top-left and down-right point\n         positions, hence, normalized (x, y, x, y) format of bboxes are\n         required. The noisy bboxes of positive queries have the points\n         both within the inner square, while those of negative queries\n         have the points both between the inner and outer squares.\n\n        Besides, the length of outer square is twice as long as that of\n        the inner square, i.e., self.box_noise_scale * w_or_h / 2.\n        NOTE The noise is added to all the bboxes. Moreover, there is still\n        unconsidered case when one point is within the positive square and\n        the others is between the inner and outer squares.\n\n        Args:\n            gt_bboxes (Tensor): The concatenated gt bboxes of all samples\n                in the batch, has shape (num_target_total, 4) with the last\n                dimension arranged as (cx, cy, w, h) where\n                `num_target_total = sum(num_target_list)`.\n            num_groups (int): The number of denoising query groups.\n\n        Returns:\n            Tensor: The output noisy bboxes, which are embedded by normalized\n            (cx, cy, w, h) format bboxes going through inverse_sigmoid, has\n            shape (num_noisy_targets, 4) with the last dimension arranged as\n            (cx, cy, w, h), where\n            `num_noisy_targets = num_target_total * num_groups * 2`.\n        \"\"\"\n        assert self.box_noise_scale > 0\n        device = gt_bboxes.device\n\n        # expand gt_bboxes as groups\n        gt_bboxes_expand = gt_bboxes.repeat(2 * num_groups, 1)  # xyxy\n\n        # obtain index of negative queries in gt_bboxes_expand\n        positive_idx = torch.arange(\n            len(gt_bboxes), dtype=torch.long, device=device)\n        positive_idx = positive_idx.unsqueeze(0).repeat(num_groups, 1)\n        positive_idx += 2 * len(gt_bboxes) * torch.arange(\n            num_groups, dtype=torch.long, device=device)[:, None]\n        positive_idx = positive_idx.flatten()\n        negative_idx = positive_idx + len(gt_bboxes)\n\n        # determine the sign of each element in the random part of the added\n        # noise to be positive or negative randomly.\n        rand_sign = torch.randint_like(\n            gt_bboxes_expand, low=0, high=2,\n            dtype=torch.float32) * 2.0 - 1.0  # [low, high), 1 or -1, randomly\n\n        # calculate the random part of the added noise\n        rand_part = torch.rand_like(gt_bboxes_expand)  # [0, 1)\n        rand_part[negative_idx] += 1.0  # pos: [0, 1); neg: [1, 2)\n        rand_part *= rand_sign  # pos: (-1, 1); neg: (-2, -1] U [1, 2)\n\n        # add noise to the bboxes\n        bboxes_whwh = bbox_xyxy_to_cxcywh(gt_bboxes_expand)[:, 2:].repeat(1, 2)\n        noisy_bboxes_expand = gt_bboxes_expand + torch.mul(\n            rand_part, bboxes_whwh) * self.box_noise_scale / 2  # xyxy\n        noisy_bboxes_expand = noisy_bboxes_expand.clamp(min=0.0, max=1.0)\n        noisy_bboxes_expand = bbox_xyxy_to_cxcywh(noisy_bboxes_expand)\n\n        dn_bbox_query = inverse_sigmoid(noisy_bboxes_expand, eps=1e-3)\n        return dn_bbox_query\n\n    def collate_dn_queries(self, input_label_query: Tensor,\n                           input_bbox_query: Tensor, batch_idx: Tensor,\n                           batch_size: int, num_groups: int) -> Tuple[Tensor]:\n        \"\"\"Collate generated queries to obtain batched dn queries.\n\n        The strategy for query collation is as follow:\n\n        .. code:: text\n\n                    input_queries (num_target_total, query_dim)\n            P_A1 P_B1 P_B2 N_A1 N_B1 N_B2 P'A1 P'B1 P'B2 N'A1 N'B1 N'B2\n              |________ group1 ________|    |________ group2 ________|\n                                         |\n                                         V\n                      P_A1 Pad0 N_A1 Pad0 P'A1 Pad0 N'A1 Pad0\n                      P_B1 P_B2 N_B1 N_B2 P'B1 P'B2 N'B1 N'B2\n                       |____ group1 ____| |____ group2 ____|\n             batched_queries (batch_size, max_num_target, query_dim)\n\n            where query_dim is 4 for bbox and self.embed_dims for label.\n            Notation: _-group 1; '-group 2;\n                      A-Sample1(has 1 target); B-sample2(has 2 targets)\n\n        Args:\n            input_label_query (Tensor): The generated label queries of all\n                targets, has shape (num_target_total, embed_dims) where\n                `num_target_total = sum(num_target_list)`.\n            input_bbox_query (Tensor): The generated bbox queries of all\n                targets, has shape (num_target_total, 4) with the last\n                dimension arranged as (cx, cy, w, h).\n            batch_idx (Tensor): The batch index of the corresponding sample\n                for each target, has shape (num_target_total).\n            batch_size (int): The size of the input batch.\n            num_groups (int): The number of denoising query groups.\n\n        Returns:\n            tuple[Tensor]: Output batched label and bbox queries.\n            - batched_label_query (Tensor): The output batched label queries,\n              has shape (batch_size, max_num_target, embed_dims).\n            - batched_bbox_query (Tensor): The output batched bbox queries,\n              has shape (batch_size, max_num_target, 4) with the last dimension\n              arranged as (cx, cy, w, h).\n        \"\"\"\n        device = input_label_query.device\n        num_target_list = [\n            torch.sum(batch_idx == idx) for idx in range(batch_size)\n        ]\n        max_num_target = max(num_target_list)\n        num_denoising_queries = int(max_num_target * 2 * num_groups)\n\n        map_query_index = torch.cat([\n            torch.arange(num_target, device=device)\n            for num_target in num_target_list\n        ])\n        map_query_index = torch.cat([\n            map_query_index + max_num_target * i for i in range(2 * num_groups)\n        ]).long()\n        batch_idx_expand = batch_idx.repeat(2 * num_groups, 1).view(-1)\n        mapper = (batch_idx_expand, map_query_index)\n\n        batched_label_query = torch.zeros(\n            batch_size, num_denoising_queries, self.embed_dims, device=device)\n        batched_bbox_query = torch.zeros(\n            batch_size, num_denoising_queries, 4, device=device)\n\n        batched_label_query[mapper] = input_label_query\n        batched_bbox_query[mapper] = input_bbox_query\n        return batched_label_query, batched_bbox_query\n\n    def generate_dn_mask(self, max_num_target: int, num_groups: int,\n                         device: Union[torch.device, str]) -> Tensor:\n        \"\"\"Generate attention mask to prevent information leakage from\n        different denoising groups and matching parts.\n\n        .. code:: text\n\n                        0 0 0 0 1 1 1 1 0 0 0 0 0\n                        0 0 0 0 1 1 1 1 0 0 0 0 0\n                        0 0 0 0 1 1 1 1 0 0 0 0 0\n                        0 0 0 0 1 1 1 1 0 0 0 0 0\n                        1 1 1 1 0 0 0 0 0 0 0 0 0\n                        1 1 1 1 0 0 0 0 0 0 0 0 0\n                        1 1 1 1 0 0 0 0 0 0 0 0 0\n                        1 1 1 1 0 0 0 0 0 0 0 0 0\n                        1 1 1 1 1 1 1 1 0 0 0 0 0\n                        1 1 1 1 1 1 1 1 0 0 0 0 0\n                        1 1 1 1 1 1 1 1 0 0 0 0 0\n                        1 1 1 1 1 1 1 1 0 0 0 0 0\n                        1 1 1 1 1 1 1 1 0 0 0 0 0\n         max_num_target |_|           |_________| num_matching_queries\n                        |_____________| num_denoising_queries\n\n               1 -> True  (Masked), means 'can not see'.\n               0 -> False (UnMasked), means 'can see'.\n\n        Args:\n            max_num_target (int): The max target number of the input batch\n                samples.\n            num_groups (int): The number of denoising query groups.\n            device (obj:`device` or str): The device of generated mask.\n\n        Returns:\n            Tensor: The attention mask to prevent information leakage from\n            different denoising groups and matching parts, will be used as\n            `self_attn_mask` of the `decoder`, has shape (num_queries_total,\n            num_queries_total), where `num_queries_total` is the sum of\n            `num_denoising_queries` and `num_matching_queries`.\n        \"\"\"\n        num_denoising_queries = int(max_num_target * 2 * num_groups)\n        num_queries_total = num_denoising_queries + self.num_matching_queries\n        attn_mask = torch.zeros(\n            num_queries_total,\n            num_queries_total,\n            device=device,\n            dtype=torch.bool)\n        # Make the matching part cannot see the denoising groups\n        attn_mask[num_denoising_queries:, :num_denoising_queries] = True\n        # Make the denoising groups cannot see each other\n        for i in range(num_groups):\n            # Mask rows of one group per step.\n            row_scope = slice(max_num_target * 2 * i,\n                              max_num_target * 2 * (i + 1))\n            left_scope = slice(max_num_target * 2 * i)\n            right_scope = slice(max_num_target * 2 * (i + 1),\n                                num_denoising_queries)\n            attn_mask[row_scope, right_scope] = True\n            attn_mask[row_scope, left_scope] = True\n        return attn_mask\n"
  },
  {
    "path": "mmdet/models/layers/transformer/grounding_dino_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import build_norm_layer\nfrom mmcv.cnn.bricks.transformer import FFN, MultiheadAttention\nfrom mmcv.ops import MultiScaleDeformableAttention\nfrom mmengine.model import ModuleList\nfrom torch import Tensor\n\nfrom mmdet.models.utils.vlfuse_helper import SingleScaleBiAttentionBlock\nfrom mmdet.utils import ConfigType, OptConfigType\nfrom .deformable_detr_layers import (DeformableDetrTransformerDecoderLayer,\n                                     DeformableDetrTransformerEncoder,\n                                     DeformableDetrTransformerEncoderLayer)\nfrom .detr_layers import DetrTransformerEncoderLayer\nfrom .dino_layers import DinoTransformerDecoder\nfrom .utils import MLP, get_text_sine_pos_embed\n\ntry:\n    from fairscale.nn.checkpoint import checkpoint_wrapper\nexcept Exception:\n    checkpoint_wrapper = None\n\n\nclass GroundingDinoTransformerDecoderLayer(\n        DeformableDetrTransformerDecoderLayer):\n\n    def __init__(self,\n                 cross_attn_text_cfg: OptConfigType = dict(\n                     embed_dims=256,\n                     num_heads=8,\n                     dropout=0.0,\n                     batch_first=True),\n                 **kwargs) -> None:\n        \"\"\"Decoder layer of Deformable DETR.\"\"\"\n        self.cross_attn_text_cfg = cross_attn_text_cfg\n        if 'batch_first' not in self.cross_attn_text_cfg:\n            self.cross_attn_text_cfg['batch_first'] = True\n        super().__init__(**kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize self_attn, cross-attn, ffn, and norms.\"\"\"\n        self.self_attn = MultiheadAttention(**self.self_attn_cfg)\n        self.cross_attn_text = MultiheadAttention(**self.cross_attn_text_cfg)\n        self.cross_attn = MultiScaleDeformableAttention(**self.cross_attn_cfg)\n        self.embed_dims = self.self_attn.embed_dims\n        self.ffn = FFN(**self.ffn_cfg)\n        norms_list = [\n            build_norm_layer(self.norm_cfg, self.embed_dims)[1]\n            for _ in range(4)\n        ]\n        self.norms = ModuleList(norms_list)\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor = None,\n                value: Tensor = None,\n                query_pos: Tensor = None,\n                key_pos: Tensor = None,\n                self_attn_mask: Tensor = None,\n                cross_attn_mask: Tensor = None,\n                key_padding_mask: Tensor = None,\n                memory_text: Tensor = None,\n                text_attention_mask: Tensor = None,\n                **kwargs) -> Tensor:\n        \"\"\"Implements decoder layer in Grounding DINO transformer.\n\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            key (Tensor, optional): The input key, has shape (bs, num_keys,\n                dim). If `None`, the `query` will be used. Defaults to `None`.\n            value (Tensor, optional): The input value, has the same shape as\n                `key`, as in `nn.MultiheadAttention.forward`. If `None`, the\n                `key` will be used. Defaults to `None`.\n            query_pos (Tensor, optional): The positional encoding for `query`,\n                has the same shape as `query`. If not `None`, it will be added\n                to `query` before forward function. Defaults to `None`.\n            key_pos (Tensor, optional): The positional encoding for `key`, has\n                the same shape as `key`. If not `None`, it will be added to\n                `key` before forward function. If None, and `query_pos` has the\n                same shape as `key`, then `query_pos` will be used for\n                `key_pos`. Defaults to None.\n            self_attn_mask (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), as in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            cross_attn_mask (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), as in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            key_padding_mask (Tensor, optional): The `key_padding_mask` of\n                `self_attn` input. ByteTensor, has shape (bs, num_value).\n                Defaults to None.\n            memory_text (Tensor): Memory text. It has shape (bs, len_text,\n                text_embed_dims).\n            text_attention_mask (Tensor): Text token mask. It has shape (bs,\n                len_text).\n\n        Returns:\n            Tensor: forwarded results, has shape (bs, num_queries, dim).\n        \"\"\"\n        # self attention\n        query = self.self_attn(\n            query=query,\n            key=query,\n            value=query,\n            query_pos=query_pos,\n            key_pos=query_pos,\n            attn_mask=self_attn_mask,\n            **kwargs)\n        query = self.norms[0](query)\n        # cross attention between query and text\n        query = self.cross_attn_text(\n            query=query,\n            query_pos=query_pos,\n            key=memory_text,\n            value=memory_text,\n            key_padding_mask=text_attention_mask)\n        query = self.norms[1](query)\n        # cross attention between query and image\n        query = self.cross_attn(\n            query=query,\n            key=key,\n            value=value,\n            query_pos=query_pos,\n            key_pos=key_pos,\n            attn_mask=cross_attn_mask,\n            key_padding_mask=key_padding_mask,\n            **kwargs)\n        query = self.norms[2](query)\n        query = self.ffn(query)\n        query = self.norms[3](query)\n\n        return query\n\n\nclass GroundingDinoTransformerEncoder(DeformableDetrTransformerEncoder):\n\n    def __init__(self, text_layer_cfg: ConfigType,\n                 fusion_layer_cfg: ConfigType, **kwargs) -> None:\n        self.text_layer_cfg = text_layer_cfg\n        self.fusion_layer_cfg = fusion_layer_cfg\n        super().__init__(**kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize encoder layers.\"\"\"\n        self.layers = ModuleList([\n            DeformableDetrTransformerEncoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.text_layers = ModuleList([\n            DetrTransformerEncoderLayer(**self.text_layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.fusion_layers = ModuleList([\n            SingleScaleBiAttentionBlock(**self.fusion_layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.embed_dims = self.layers[0].embed_dims\n        if self.num_cp > 0:\n            if checkpoint_wrapper is None:\n                raise NotImplementedError(\n                    'If you want to reduce GPU memory usage, \\\n                    please install fairscale by executing the \\\n                    following command: pip install fairscale.')\n            for i in range(self.num_cp):\n                self.layers[i] = checkpoint_wrapper(self.layers[i])\n                self.fusion_layers[i] = checkpoint_wrapper(\n                    self.fusion_layers[i])\n\n    def forward(self,\n                query: Tensor,\n                query_pos: Tensor,\n                key_padding_mask: Tensor,\n                spatial_shapes: Tensor,\n                level_start_index: Tensor,\n                valid_ratios: Tensor,\n                memory_text: Tensor = None,\n                text_attention_mask: Tensor = None,\n                pos_text: Tensor = None,\n                text_self_attention_masks: Tensor = None,\n                position_ids: Tensor = None):\n        \"\"\"Forward function of Transformer encoder.\n\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            query_pos (Tensor): The positional encoding for query, has shape\n                (bs, num_queries, dim).\n            key_padding_mask (Tensor): The `key_padding_mask` of `self_attn`\n                input. ByteTensor, has shape (bs, num_queries).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n            memory_text (Tensor, optional): Memory text. It has shape (bs,\n                len_text, text_embed_dims).\n            text_attention_mask (Tensor, optional): Text token mask. It has\n                shape (bs,len_text).\n            pos_text (Tensor, optional): The positional encoding for text.\n                Defaults to None.\n            text_self_attention_masks (Tensor, optional): Text self attention\n                mask. Defaults to None.\n            position_ids (Tensor, optional): Text position ids.\n                Defaults to None.\n        \"\"\"\n        output = query\n        reference_points = self.get_encoder_reference_points(\n            spatial_shapes, valid_ratios, device=query.device)\n        if self.text_layers:\n            # generate pos_text\n            bs, n_text, _ = memory_text.shape\n            if pos_text is None and position_ids is None:\n                pos_text = (\n                    torch.arange(n_text,\n                                 device=memory_text.device).float().unsqueeze(\n                                     0).unsqueeze(-1).repeat(bs, 1, 1))\n                pos_text = get_text_sine_pos_embed(\n                    pos_text, num_pos_feats=256, exchange_xy=False)\n            if position_ids is not None:\n                pos_text = get_text_sine_pos_embed(\n                    position_ids[..., None],\n                    num_pos_feats=256,\n                    exchange_xy=False)\n\n        # main process\n        for layer_id, layer in enumerate(self.layers):\n            if self.fusion_layers:\n                output, memory_text = self.fusion_layers[layer_id](\n                    visual_feature=output,\n                    lang_feature=memory_text,\n                    attention_mask_v=key_padding_mask,\n                    attention_mask_l=text_attention_mask,\n                )\n            if self.text_layers:\n                text_num_heads = self.text_layers[\n                    layer_id].self_attn_cfg.num_heads\n                memory_text = self.text_layers[layer_id](\n                    query=memory_text,\n                    query_pos=(pos_text if pos_text is not None else None),\n                    attn_mask=~text_self_attention_masks.repeat(\n                        text_num_heads, 1, 1),  # note we use ~ for mask here\n                    key_padding_mask=None,\n                )\n            output = layer(\n                query=output,\n                query_pos=query_pos,\n                reference_points=reference_points,\n                spatial_shapes=spatial_shapes,\n                level_start_index=level_start_index,\n                key_padding_mask=key_padding_mask)\n        return output, memory_text\n\n\nclass GroundingDinoTransformerDecoder(DinoTransformerDecoder):\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize decoder layers.\"\"\"\n        self.layers = ModuleList([\n            GroundingDinoTransformerDecoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.embed_dims = self.layers[0].embed_dims\n        if self.post_norm_cfg is not None:\n            raise ValueError('There is not post_norm in '\n                             f'{self._get_name()}')\n        self.ref_point_head = MLP(self.embed_dims * 2, self.embed_dims,\n                                  self.embed_dims, 2)\n        self.norm = nn.LayerNorm(self.embed_dims)\n"
  },
  {
    "path": "mmdet/models/layers/transformer/mask2former_layers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.cnn import build_norm_layer\nfrom mmengine.model import ModuleList\nfrom torch import Tensor\n\nfrom .deformable_detr_layers import DeformableDetrTransformerEncoder\nfrom .detr_layers import DetrTransformerDecoder, DetrTransformerDecoderLayer\n\n\nclass Mask2FormerTransformerEncoder(DeformableDetrTransformerEncoder):\n    \"\"\"Encoder in PixelDecoder of Mask2Former.\"\"\"\n\n    def forward(self, query: Tensor, query_pos: Tensor,\n                key_padding_mask: Tensor, spatial_shapes: Tensor,\n                level_start_index: Tensor, valid_ratios: Tensor,\n                reference_points: Tensor, **kwargs) -> Tensor:\n        \"\"\"Forward function of Transformer encoder.\n\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            query_pos (Tensor): The positional encoding for query, has shape\n                (bs, num_queries, dim). If not None, it will be added to the\n                `query` before forward function. Defaults to None.\n            key_padding_mask (Tensor): The `key_padding_mask` of `self_attn`\n                input. ByteTensor, has shape (bs, num_queries).\n            spatial_shapes (Tensor): Spatial shapes of features in all levels,\n                has shape (num_levels, 2), last dimension represents (h, w).\n            level_start_index (Tensor): The start index of each level.\n                A tensor has shape (num_levels, ) and can be represented\n                as [0, h_0*w_0, h_0*w_0+h_1*w_1, ...].\n            valid_ratios (Tensor): The ratios of the valid width and the valid\n                height relative to the width and the height of features in all\n                levels, has shape (bs, num_levels, 2).\n            reference_points (Tensor): The initial reference, has shape\n                (bs, num_queries, 2) with the last dimension arranged\n                as (cx, cy).\n\n        Returns:\n            Tensor: Output queries of Transformer encoder, which is also\n            called 'encoder output embeddings' or 'memory', has shape\n            (bs, num_queries, dim)\n        \"\"\"\n        for layer in self.layers:\n            query = layer(\n                query=query,\n                query_pos=query_pos,\n                key_padding_mask=key_padding_mask,\n                spatial_shapes=spatial_shapes,\n                level_start_index=level_start_index,\n                valid_ratios=valid_ratios,\n                reference_points=reference_points,\n                **kwargs)\n        return query\n\n\nclass Mask2FormerTransformerDecoder(DetrTransformerDecoder):\n    \"\"\"Decoder of Mask2Former.\"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize decoder layers.\"\"\"\n        self.layers = ModuleList([\n            Mask2FormerTransformerDecoderLayer(**self.layer_cfg)\n            for _ in range(self.num_layers)\n        ])\n        self.embed_dims = self.layers[0].embed_dims\n        self.post_norm = build_norm_layer(self.post_norm_cfg,\n                                          self.embed_dims)[1]\n\n\nclass Mask2FormerTransformerDecoderLayer(DetrTransformerDecoderLayer):\n    \"\"\"Implements decoder layer in Mask2Former transformer.\"\"\"\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor = None,\n                value: Tensor = None,\n                query_pos: Tensor = None,\n                key_pos: Tensor = None,\n                self_attn_mask: Tensor = None,\n                cross_attn_mask: Tensor = None,\n                key_padding_mask: Tensor = None,\n                **kwargs) -> Tensor:\n        \"\"\"\n        Args:\n            query (Tensor): The input query, has shape (bs, num_queries, dim).\n            key (Tensor, optional): The input key, has shape (bs, num_keys,\n                dim). If `None`, the `query` will be used. Defaults to `None`.\n            value (Tensor, optional): The input value, has the same shape as\n                `key`, as in `nn.MultiheadAttention.forward`. If `None`, the\n                `key` will be used. Defaults to `None`.\n            query_pos (Tensor, optional): The positional encoding for `query`,\n                has the same shape as `query`. If not `None`, it will be added\n                to `query` before forward function. Defaults to `None`.\n            key_pos (Tensor, optional): The positional encoding for `key`, has\n                the same shape as `key`. If not `None`, it will be added to\n                `key` before forward function. If None, and `query_pos` has the\n                same shape as `key`, then `query_pos` will be used for\n                `key_pos`. Defaults to None.\n            self_attn_mask (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), as in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            cross_attn_mask (Tensor, optional): ByteTensor mask, has shape\n                (num_queries, num_keys), as in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            key_padding_mask (Tensor, optional): The `key_padding_mask` of\n                `self_attn` input. ByteTensor, has shape (bs, num_value).\n                Defaults to None.\n\n        Returns:\n            Tensor: forwarded results, has shape (bs, num_queries, dim).\n        \"\"\"\n\n        query = self.cross_attn(\n            query=query,\n            key=key,\n            value=value,\n            query_pos=query_pos,\n            key_pos=key_pos,\n            attn_mask=cross_attn_mask,\n            key_padding_mask=key_padding_mask,\n            **kwargs)\n        query = self.norms[0](query)\n        query = self.self_attn(\n            query=query,\n            key=query,\n            value=query,\n            query_pos=query_pos,\n            key_pos=query_pos,\n            attn_mask=self_attn_mask,\n            **kwargs)\n        query = self.norms[1](query)\n        query = self.ffn(query)\n        query = self.norms[2](query)\n\n        return query\n"
  },
  {
    "path": "mmdet/models/layers/transformer/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nimport warnings\nfrom typing import Optional, Sequence, Tuple, Union\n\nimport torch\nimport torch.nn.functional as F\nfrom mmcv.cnn import (Linear, build_activation_layer, build_conv_layer,\n                      build_norm_layer)\nfrom mmcv.cnn.bricks.drop import Dropout\nfrom mmengine.model import BaseModule, ModuleList\nfrom mmengine.utils import to_2tuple\nfrom torch import Tensor, nn\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\n\n\ndef nlc_to_nchw(x: Tensor, hw_shape: Sequence[int]) -> Tensor:\n    \"\"\"Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor.\n\n    Args:\n        x (Tensor): The input tensor of shape [N, L, C] before conversion.\n        hw_shape (Sequence[int]): The height and width of output feature map.\n\n    Returns:\n        Tensor: The output tensor of shape [N, C, H, W] after conversion.\n    \"\"\"\n    H, W = hw_shape\n    assert len(x.shape) == 3\n    B, L, C = x.shape\n    assert L == H * W, 'The seq_len does not match H, W'\n    return x.transpose(1, 2).reshape(B, C, H, W).contiguous()\n\n\ndef nchw_to_nlc(x):\n    \"\"\"Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor.\n\n    Args:\n        x (Tensor): The input tensor of shape [N, C, H, W] before conversion.\n\n    Returns:\n        Tensor: The output tensor of shape [N, L, C] after conversion.\n    \"\"\"\n    assert len(x.shape) == 4\n    return x.flatten(2).transpose(1, 2).contiguous()\n\n\ndef coordinate_to_encoding(coord_tensor: Tensor,\n                           num_feats: int = 128,\n                           temperature: int = 10000,\n                           scale: float = 2 * math.pi):\n    \"\"\"Convert coordinate tensor to positional encoding.\n\n    Args:\n        coord_tensor (Tensor): Coordinate tensor to be converted to\n            positional encoding. With the last dimension as 2 or 4.\n        num_feats (int, optional): The feature dimension for each position\n            along x-axis or y-axis. Note the final returned dimension\n            for each position is 2 times of this value. Defaults to 128.\n        temperature (int, optional): The temperature used for scaling\n            the position embedding. Defaults to 10000.\n        scale (float, optional): A scale factor that scales the position\n            embedding. The scale will be used only when `normalize` is True.\n            Defaults to 2*pi.\n    Returns:\n        Tensor: Returned encoded positional tensor.\n    \"\"\"\n    dim_t = torch.arange(\n        num_feats, dtype=torch.float32, device=coord_tensor.device)\n    dim_t = temperature**(2 * (dim_t // 2) / num_feats)\n    x_embed = coord_tensor[..., 0] * scale\n    y_embed = coord_tensor[..., 1] * scale\n    pos_x = x_embed[..., None] / dim_t\n    pos_y = y_embed[..., None] / dim_t\n    pos_x = torch.stack((pos_x[..., 0::2].sin(), pos_x[..., 1::2].cos()),\n                        dim=-1).flatten(2)\n    pos_y = torch.stack((pos_y[..., 0::2].sin(), pos_y[..., 1::2].cos()),\n                        dim=-1).flatten(2)\n    if coord_tensor.size(-1) == 2:\n        pos = torch.cat((pos_y, pos_x), dim=-1)\n    elif coord_tensor.size(-1) == 4:\n        w_embed = coord_tensor[..., 2] * scale\n        pos_w = w_embed[..., None] / dim_t\n        pos_w = torch.stack((pos_w[..., 0::2].sin(), pos_w[..., 1::2].cos()),\n                            dim=-1).flatten(2)\n\n        h_embed = coord_tensor[..., 3] * scale\n        pos_h = h_embed[..., None] / dim_t\n        pos_h = torch.stack((pos_h[..., 0::2].sin(), pos_h[..., 1::2].cos()),\n                            dim=-1).flatten(2)\n\n        pos = torch.cat((pos_y, pos_x, pos_w, pos_h), dim=-1)\n    else:\n        raise ValueError('Unknown pos_tensor shape(-1):{}'.format(\n            coord_tensor.size(-1)))\n    return pos\n\n\ndef inverse_sigmoid(x: Tensor, eps: float = 1e-5) -> Tensor:\n    \"\"\"Inverse function of sigmoid.\n\n    Args:\n        x (Tensor): The tensor to do the inverse.\n        eps (float): EPS avoid numerical overflow. Defaults 1e-5.\n    Returns:\n        Tensor: The x has passed the inverse function of sigmoid, has the same\n        shape with input.\n    \"\"\"\n    x = x.clamp(min=0, max=1)\n    x1 = x.clamp(min=eps)\n    x2 = (1 - x).clamp(min=eps)\n    return torch.log(x1 / x2)\n\n\nclass AdaptivePadding(nn.Module):\n    \"\"\"Applies padding to input (if needed) so that input can get fully covered\n    by filter you specified. It support two modes \"same\" and \"corner\". The\n    \"same\" mode is same with \"SAME\" padding mode in TensorFlow, pad zero around\n    input. The \"corner\"  mode would pad zero to bottom right.\n\n    Args:\n        kernel_size (int | tuple): Size of the kernel:\n        stride (int | tuple): Stride of the filter. Default: 1:\n        dilation (int | tuple): Spacing between kernel elements.\n            Default: 1\n        padding (str): Support \"same\" and \"corner\", \"corner\" mode\n            would pad zero to bottom right, and \"same\" mode would\n            pad zero around input. Default: \"corner\".\n    Example:\n        >>> kernel_size = 16\n        >>> stride = 16\n        >>> dilation = 1\n        >>> input = torch.rand(1, 1, 15, 17)\n        >>> adap_pad = AdaptivePadding(\n        >>>     kernel_size=kernel_size,\n        >>>     stride=stride,\n        >>>     dilation=dilation,\n        >>>     padding=\"corner\")\n        >>> out = adap_pad(input)\n        >>> assert (out.shape[2], out.shape[3]) == (16, 32)\n        >>> input = torch.rand(1, 1, 16, 17)\n        >>> out = adap_pad(input)\n        >>> assert (out.shape[2], out.shape[3]) == (16, 32)\n    \"\"\"\n\n    def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'):\n\n        super(AdaptivePadding, self).__init__()\n\n        assert padding in ('same', 'corner')\n\n        kernel_size = to_2tuple(kernel_size)\n        stride = to_2tuple(stride)\n        padding = to_2tuple(padding)\n        dilation = to_2tuple(dilation)\n\n        self.padding = padding\n        self.kernel_size = kernel_size\n        self.stride = stride\n        self.dilation = dilation\n\n    def get_pad_shape(self, input_shape):\n        input_h, input_w = input_shape\n        kernel_h, kernel_w = self.kernel_size\n        stride_h, stride_w = self.stride\n        output_h = math.ceil(input_h / stride_h)\n        output_w = math.ceil(input_w / stride_w)\n        pad_h = max((output_h - 1) * stride_h +\n                    (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0)\n        pad_w = max((output_w - 1) * stride_w +\n                    (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0)\n        return pad_h, pad_w\n\n    def forward(self, x):\n        pad_h, pad_w = self.get_pad_shape(x.size()[-2:])\n        if pad_h > 0 or pad_w > 0:\n            if self.padding == 'corner':\n                x = F.pad(x, [0, pad_w, 0, pad_h])\n            elif self.padding == 'same':\n                x = F.pad(x, [\n                    pad_w // 2, pad_w - pad_w // 2, pad_h // 2,\n                    pad_h - pad_h // 2\n                ])\n        return x\n\n\nclass PatchEmbed(BaseModule):\n    \"\"\"Image to Patch Embedding.\n\n    We use a conv layer to implement PatchEmbed.\n\n    Args:\n        in_channels (int): The num of input channels. Default: 3\n        embed_dims (int): The dimensions of embedding. Default: 768\n        conv_type (str): The config dict for embedding\n            conv layer type selection. Default: \"Conv2d.\n        kernel_size (int): The kernel_size of embedding conv. Default: 16.\n        stride (int): The slide stride of embedding conv.\n            Default: None (Would be set as `kernel_size`).\n        padding (int | tuple | string ): The padding length of\n            embedding conv. When it is a string, it means the mode\n            of adaptive padding, support \"same\" and \"corner\" now.\n            Default: \"corner\".\n        dilation (int): The dilation rate of embedding conv. Default: 1.\n        bias (bool): Bias of embed conv. Default: True.\n        norm_cfg (dict, optional): Config dict for normalization layer.\n            Default: None.\n        input_size (int | tuple | None): The size of input, which will be\n            used to calculate the out size. Only work when `dynamic_size`\n            is False. Default: None.\n        init_cfg (`mmengine.ConfigDict`, optional): The Config for\n            initialization. Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int = 3,\n                 embed_dims: int = 768,\n                 conv_type: str = 'Conv2d',\n                 kernel_size: int = 16,\n                 stride: int = 16,\n                 padding: Union[int, tuple, str] = 'corner',\n                 dilation: int = 1,\n                 bias: bool = True,\n                 norm_cfg: OptConfigType = None,\n                 input_size: Union[int, tuple] = None,\n                 init_cfg: OptConfigType = None) -> None:\n        super(PatchEmbed, self).__init__(init_cfg=init_cfg)\n\n        self.embed_dims = embed_dims\n        if stride is None:\n            stride = kernel_size\n\n        kernel_size = to_2tuple(kernel_size)\n        stride = to_2tuple(stride)\n        dilation = to_2tuple(dilation)\n\n        if isinstance(padding, str):\n            self.adap_padding = AdaptivePadding(\n                kernel_size=kernel_size,\n                stride=stride,\n                dilation=dilation,\n                padding=padding)\n            # disable the padding of conv\n            padding = 0\n        else:\n            self.adap_padding = None\n        padding = to_2tuple(padding)\n\n        self.projection = build_conv_layer(\n            dict(type=conv_type),\n            in_channels=in_channels,\n            out_channels=embed_dims,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        if norm_cfg is not None:\n            self.norm = build_norm_layer(norm_cfg, embed_dims)[1]\n        else:\n            self.norm = None\n\n        if input_size:\n            input_size = to_2tuple(input_size)\n            # `init_out_size` would be used outside to\n            # calculate the num_patches\n            # when `use_abs_pos_embed` outside\n            self.init_input_size = input_size\n            if self.adap_padding:\n                pad_h, pad_w = self.adap_padding.get_pad_shape(input_size)\n                input_h, input_w = input_size\n                input_h = input_h + pad_h\n                input_w = input_w + pad_w\n                input_size = (input_h, input_w)\n\n            # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html\n            h_out = (input_size[0] + 2 * padding[0] - dilation[0] *\n                     (kernel_size[0] - 1) - 1) // stride[0] + 1\n            w_out = (input_size[1] + 2 * padding[1] - dilation[1] *\n                     (kernel_size[1] - 1) - 1) // stride[1] + 1\n            self.init_out_size = (h_out, w_out)\n        else:\n            self.init_input_size = None\n            self.init_out_size = None\n\n    def forward(self, x: Tensor) -> Tuple[Tensor, Tuple[int]]:\n        \"\"\"\n        Args:\n            x (Tensor): Has shape (B, C, H, W). In most case, C is 3.\n\n        Returns:\n            tuple: Contains merged results and its spatial shape.\n\n                - x (Tensor): Has shape (B, out_h * out_w, embed_dims)\n                - out_size (tuple[int]): Spatial shape of x, arrange as\n                    (out_h, out_w).\n        \"\"\"\n\n        if self.adap_padding:\n            x = self.adap_padding(x)\n\n        x = self.projection(x)\n        out_size = (x.shape[2], x.shape[3])\n        x = x.flatten(2).transpose(1, 2)\n        if self.norm is not None:\n            x = self.norm(x)\n        return x, out_size\n\n\nclass PatchMerging(BaseModule):\n    \"\"\"Merge patch feature map.\n\n    This layer groups feature map by kernel_size, and applies norm and linear\n    layers to the grouped feature map. Our implementation uses `nn.Unfold` to\n    merge patch, which is about 25% faster than original implementation.\n    Instead, we need to modify pretrained models for compatibility.\n\n    Args:\n        in_channels (int): The num of input channels.\n            to gets fully covered by filter and stride you specified..\n            Default: True.\n        out_channels (int): The num of output channels.\n        kernel_size (int | tuple, optional): the kernel size in the unfold\n            layer. Defaults to 2.\n        stride (int | tuple, optional): the stride of the sliding blocks in the\n            unfold layer. Default: None. (Would be set as `kernel_size`)\n        padding (int | tuple | string ): The padding length of\n            embedding conv. When it is a string, it means the mode\n            of adaptive padding, support \"same\" and \"corner\" now.\n            Default: \"corner\".\n        dilation (int | tuple, optional): dilation parameter in the unfold\n            layer. Default: 1.\n        bias (bool, optional): Whether to add bias in linear layer or not.\n            Defaults: False.\n        norm_cfg (dict, optional): Config dict for normalization layer.\n            Default: dict(type='LN').\n        init_cfg (dict, optional): The extra config for initialization.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 kernel_size: Optional[Union[int, tuple]] = 2,\n                 stride: Optional[Union[int, tuple]] = None,\n                 padding: Union[int, tuple, str] = 'corner',\n                 dilation: Optional[Union[int, tuple]] = 1,\n                 bias: Optional[bool] = False,\n                 norm_cfg: OptConfigType = dict(type='LN'),\n                 init_cfg: OptConfigType = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        if stride:\n            stride = stride\n        else:\n            stride = kernel_size\n\n        kernel_size = to_2tuple(kernel_size)\n        stride = to_2tuple(stride)\n        dilation = to_2tuple(dilation)\n\n        if isinstance(padding, str):\n            self.adap_padding = AdaptivePadding(\n                kernel_size=kernel_size,\n                stride=stride,\n                dilation=dilation,\n                padding=padding)\n            # disable the padding of unfold\n            padding = 0\n        else:\n            self.adap_padding = None\n\n        padding = to_2tuple(padding)\n        self.sampler = nn.Unfold(\n            kernel_size=kernel_size,\n            dilation=dilation,\n            padding=padding,\n            stride=stride)\n\n        sample_dim = kernel_size[0] * kernel_size[1] * in_channels\n\n        if norm_cfg is not None:\n            self.norm = build_norm_layer(norm_cfg, sample_dim)[1]\n        else:\n            self.norm = None\n\n        self.reduction = nn.Linear(sample_dim, out_channels, bias=bias)\n\n    def forward(self, x: Tensor,\n                input_size: Tuple[int]) -> Tuple[Tensor, Tuple[int]]:\n        \"\"\"\n        Args:\n            x (Tensor): Has shape (B, H*W, C_in).\n            input_size (tuple[int]): The spatial shape of x, arrange as (H, W).\n                Default: None.\n\n        Returns:\n            tuple: Contains merged results and its spatial shape.\n\n                - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out)\n                - out_size (tuple[int]): Spatial shape of x, arrange as\n                    (Merged_H, Merged_W).\n        \"\"\"\n        B, L, C = x.shape\n        assert isinstance(input_size, Sequence), f'Expect ' \\\n                                                 f'input_size is ' \\\n                                                 f'`Sequence` ' \\\n                                                 f'but get {input_size}'\n\n        H, W = input_size\n        assert L == H * W, 'input feature has wrong size'\n\n        x = x.view(B, H, W, C).permute([0, 3, 1, 2])  # B, C, H, W\n        # Use nn.Unfold to merge patch. About 25% faster than original method,\n        # but need to modify pretrained model for compatibility\n\n        if self.adap_padding:\n            x = self.adap_padding(x)\n            H, W = x.shape[-2:]\n\n        x = self.sampler(x)\n        # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2)\n\n        out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] *\n                 (self.sampler.kernel_size[0] - 1) -\n                 1) // self.sampler.stride[0] + 1\n        out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] *\n                 (self.sampler.kernel_size[1] - 1) -\n                 1) // self.sampler.stride[1] + 1\n\n        output_size = (out_h, out_w)\n        x = x.transpose(1, 2)  # B, H/2*W/2, 4*C\n        x = self.norm(x) if self.norm else x\n        x = self.reduction(x)\n        return x, output_size\n\n\nclass ConditionalAttention(BaseModule):\n    \"\"\"A wrapper of conditional attention, dropout and residual connection.\n\n    Args:\n        embed_dims (int): The embedding dimension.\n        num_heads (int): Parallel attention heads.\n        attn_drop (float): A Dropout layer on attn_output_weights.\n            Default: 0.0.\n        proj_drop: A Dropout layer after `nn.MultiheadAttention`.\n            Default: 0.0.\n        cross_attn (bool): Whether the attention module is for cross attention.\n            Default: False\n        keep_query_pos (bool): Whether to transform query_pos before cross\n            attention.\n            Default: False.\n        batch_first (bool): When it is True, Key, Query and Value are shape of\n            (batch, n, embed_dim), otherwise (n, batch, embed_dim).\n             Default: True.\n        init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 embed_dims: int,\n                 num_heads: int,\n                 attn_drop: float = 0.,\n                 proj_drop: float = 0.,\n                 cross_attn: bool = False,\n                 keep_query_pos: bool = False,\n                 batch_first: bool = True,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(init_cfg=init_cfg)\n\n        assert batch_first is True, 'Set `batch_first`\\\n        to False is NOT supported in ConditionalAttention. \\\n        First dimension of all DETRs in mmdet is `batch`, \\\n        please set `batch_first` to True.'\n\n        self.cross_attn = cross_attn\n        self.keep_query_pos = keep_query_pos\n        self.embed_dims = embed_dims\n        self.num_heads = num_heads\n        self.attn_drop = Dropout(attn_drop)\n        self.proj_drop = Dropout(proj_drop)\n\n        self._init_layers()\n\n    def _init_layers(self):\n        \"\"\"Initialize layers for qkv projection.\"\"\"\n        embed_dims = self.embed_dims\n        self.qcontent_proj = Linear(embed_dims, embed_dims)\n        self.qpos_proj = Linear(embed_dims, embed_dims)\n        self.kcontent_proj = Linear(embed_dims, embed_dims)\n        self.kpos_proj = Linear(embed_dims, embed_dims)\n        self.v_proj = Linear(embed_dims, embed_dims)\n        if self.cross_attn:\n            self.qpos_sine_proj = Linear(embed_dims, embed_dims)\n        self.out_proj = Linear(embed_dims, embed_dims)\n\n        nn.init.constant_(self.out_proj.bias, 0.)\n\n    def forward_attn(self,\n                     query: Tensor,\n                     key: Tensor,\n                     value: Tensor,\n                     attn_mask: Tensor = None,\n                     key_padding_mask: Tensor = None) -> Tuple[Tensor]:\n        \"\"\"Forward process for `ConditionalAttention`.\n\n        Args:\n            query (Tensor): The input query with shape [bs, num_queries,\n                embed_dims].\n            key (Tensor): The key tensor with shape [bs, num_keys,\n                embed_dims].\n                If None, the `query` will be used. Defaults to None.\n            value (Tensor): The value tensor with same shape as `key`.\n                Same in `nn.MultiheadAttention.forward`. Defaults to None.\n                If None, the `key` will be used.\n            attn_mask (Tensor): ByteTensor mask with shape [num_queries,\n                num_keys]. Same in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys].\n                Defaults to None.\n        Returns:\n            Tuple[Tensor]: Attention outputs of shape :math:`(N, L, E)`,\n            where :math:`N` is the batch size, :math:`L` is the target\n            sequence length , and :math:`E` is the embedding dimension\n            `embed_dim`. Attention weights per head of shape :math:`\n            (num_heads, L, S)`. where :math:`N` is batch size, :math:`L`\n            is target sequence length, and :math:`S` is the source sequence\n            length.\n        \"\"\"\n        assert key.size(1) == value.size(1), \\\n            f'{\"key, value must have the same sequence length\"}'\n        assert query.size(0) == key.size(0) == value.size(0), \\\n            f'{\"batch size must be equal for query, key, value\"}'\n        assert query.size(2) == key.size(2), \\\n            f'{\"q_dims, k_dims must be equal\"}'\n        assert value.size(2) == self.embed_dims, \\\n            f'{\"v_dims must be equal to embed_dims\"}'\n\n        bs, tgt_len, hidden_dims = query.size()\n        _, src_len, _ = key.size()\n        head_dims = hidden_dims // self.num_heads\n        v_head_dims = self.embed_dims // self.num_heads\n        assert head_dims * self.num_heads == hidden_dims, \\\n            f'{\"hidden_dims must be divisible by num_heads\"}'\n        scaling = float(head_dims)**-0.5\n\n        q = query * scaling\n        k = key\n        v = value\n\n        if attn_mask is not None:\n            assert attn_mask.dtype == torch.float32 or \\\n                   attn_mask.dtype == torch.float64 or \\\n                   attn_mask.dtype == torch.float16 or \\\n                   attn_mask.dtype == torch.uint8 or \\\n                   attn_mask.dtype == torch.bool, \\\n                   'Only float, byte, and bool types are supported for \\\n                    attn_mask'\n\n            if attn_mask.dtype == torch.uint8:\n                warnings.warn('Byte tensor for attn_mask is deprecated.\\\n                     Use bool tensor instead.')\n                attn_mask = attn_mask.to(torch.bool)\n            if attn_mask.dim() == 2:\n                attn_mask = attn_mask.unsqueeze(0)\n                if list(attn_mask.size()) != [1, query.size(1), key.size(1)]:\n                    raise RuntimeError(\n                        'The size of the 2D attn_mask is not correct.')\n            elif attn_mask.dim() == 3:\n                if list(attn_mask.size()) != [\n                        bs * self.num_heads,\n                        query.size(1),\n                        key.size(1)\n                ]:\n                    raise RuntimeError(\n                        'The size of the 3D attn_mask is not correct.')\n            else:\n                raise RuntimeError(\n                    \"attn_mask's dimension {} is not supported\".format(\n                        attn_mask.dim()))\n        # attn_mask's dim is 3 now.\n\n        if key_padding_mask is not None and key_padding_mask.dtype == int:\n            key_padding_mask = key_padding_mask.to(torch.bool)\n\n        q = q.contiguous().view(bs, tgt_len, self.num_heads,\n                                head_dims).permute(0, 2, 1, 3).flatten(0, 1)\n        if k is not None:\n            k = k.contiguous().view(bs, src_len, self.num_heads,\n                                    head_dims).permute(0, 2, 1,\n                                                       3).flatten(0, 1)\n        if v is not None:\n            v = v.contiguous().view(bs, src_len, self.num_heads,\n                                    v_head_dims).permute(0, 2, 1,\n                                                         3).flatten(0, 1)\n\n        if key_padding_mask is not None:\n            assert key_padding_mask.size(0) == bs\n            assert key_padding_mask.size(1) == src_len\n\n        attn_output_weights = torch.bmm(q, k.transpose(1, 2))\n        assert list(attn_output_weights.size()) == [\n            bs * self.num_heads, tgt_len, src_len\n        ]\n\n        if attn_mask is not None:\n            if attn_mask.dtype == torch.bool:\n                attn_output_weights.masked_fill_(attn_mask, float('-inf'))\n            else:\n                attn_output_weights += attn_mask\n\n        if key_padding_mask is not None:\n            attn_output_weights = attn_output_weights.view(\n                bs, self.num_heads, tgt_len, src_len)\n            attn_output_weights = attn_output_weights.masked_fill(\n                key_padding_mask.unsqueeze(1).unsqueeze(2),\n                float('-inf'),\n            )\n            attn_output_weights = attn_output_weights.view(\n                bs * self.num_heads, tgt_len, src_len)\n\n        attn_output_weights = F.softmax(\n            attn_output_weights -\n            attn_output_weights.max(dim=-1, keepdim=True)[0],\n            dim=-1)\n        attn_output_weights = self.attn_drop(attn_output_weights)\n\n        attn_output = torch.bmm(attn_output_weights, v)\n        assert list(\n            attn_output.size()) == [bs * self.num_heads, tgt_len, v_head_dims]\n        attn_output = attn_output.view(bs, self.num_heads, tgt_len,\n                                       v_head_dims).permute(0, 2, 1,\n                                                            3).flatten(2)\n        attn_output = self.out_proj(attn_output)\n\n        # average attention weights over heads\n        attn_output_weights = attn_output_weights.view(bs, self.num_heads,\n                                                       tgt_len, src_len)\n        return attn_output, attn_output_weights.sum(dim=1) / self.num_heads\n\n    def forward(self,\n                query: Tensor,\n                key: Tensor,\n                query_pos: Tensor = None,\n                ref_sine_embed: Tensor = None,\n                key_pos: Tensor = None,\n                attn_mask: Tensor = None,\n                key_padding_mask: Tensor = None,\n                is_first: bool = False) -> Tensor:\n        \"\"\"Forward function for `ConditionalAttention`.\n        Args:\n            query (Tensor): The input query with shape [bs, num_queries,\n                embed_dims].\n            key (Tensor): The key tensor with shape [bs, num_keys,\n                embed_dims].\n                If None, the `query` will be used. Defaults to None.\n            query_pos (Tensor): The positional encoding for query in self\n                attention, with the same shape as `x`. If not None, it will\n                be added to `x` before forward function.\n                Defaults to None.\n            query_sine_embed (Tensor): The positional encoding for query in\n                cross attention, with the same shape as `x`. If not None, it\n                will be added to `x` before forward function.\n                Defaults to None.\n            key_pos (Tensor): The positional encoding for `key`, with the\n                same shape as `key`. Defaults to None. If not None, it will\n                be added to `key` before forward function. If None, and\n                `query_pos` has the same shape as `key`, then `query_pos`\n                will be used for `key_pos`. Defaults to None.\n            attn_mask (Tensor): ByteTensor mask with shape [num_queries,\n                num_keys]. Same in `nn.MultiheadAttention.forward`.\n                Defaults to None.\n            key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys].\n                Defaults to None.\n            is_first (bool): A indicator to tell whether the current layer\n                is the first layer of the decoder.\n                Defaults to False.\n        Returns:\n            Tensor: forwarded results with shape\n            [bs, num_queries, embed_dims].\n        \"\"\"\n\n        if self.cross_attn:\n            q_content = self.qcontent_proj(query)\n            k_content = self.kcontent_proj(key)\n            v = self.v_proj(key)\n\n            bs, nq, c = q_content.size()\n            _, hw, _ = k_content.size()\n\n            k_pos = self.kpos_proj(key_pos)\n            if is_first or self.keep_query_pos:\n                q_pos = self.qpos_proj(query_pos)\n                q = q_content + q_pos\n                k = k_content + k_pos\n            else:\n                q = q_content\n                k = k_content\n            q = q.view(bs, nq, self.num_heads, c // self.num_heads)\n            query_sine_embed = self.qpos_sine_proj(ref_sine_embed)\n            query_sine_embed = query_sine_embed.view(bs, nq, self.num_heads,\n                                                     c // self.num_heads)\n            q = torch.cat([q, query_sine_embed], dim=3).view(bs, nq, 2 * c)\n            k = k.view(bs, hw, self.num_heads, c // self.num_heads)\n            k_pos = k_pos.view(bs, hw, self.num_heads, c // self.num_heads)\n            k = torch.cat([k, k_pos], dim=3).view(bs, hw, 2 * c)\n            ca_output = self.forward_attn(\n                query=q,\n                key=k,\n                value=v,\n                attn_mask=attn_mask,\n                key_padding_mask=key_padding_mask)[0]\n            query = query + self.proj_drop(ca_output)\n        else:\n            q_content = self.qcontent_proj(query)\n            q_pos = self.qpos_proj(query_pos)\n            k_content = self.kcontent_proj(query)\n            k_pos = self.kpos_proj(query_pos)\n            v = self.v_proj(query)\n            q = q_content if q_pos is None else q_content + q_pos\n            k = k_content if k_pos is None else k_content + k_pos\n            sa_output = self.forward_attn(\n                query=q,\n                key=k,\n                value=v,\n                attn_mask=attn_mask,\n                key_padding_mask=key_padding_mask)[0]\n            query = query + self.proj_drop(sa_output)\n\n        return query\n\n\nclass MLP(BaseModule):\n    \"\"\"Very simple multi-layer perceptron (also called FFN) with relu. Mostly\n    used in DETR series detectors.\n\n    Args:\n        input_dim (int): Feature dim of the input tensor.\n        hidden_dim (int): Feature dim of the hidden layer.\n        output_dim (int): Feature dim of the output tensor.\n        num_layers (int): Number of FFN layers. As the last\n            layer of MLP only contains FFN (Linear).\n    \"\"\"\n\n    def __init__(self, input_dim: int, hidden_dim: int, output_dim: int,\n                 num_layers: int) -> None:\n        super().__init__()\n        self.num_layers = num_layers\n        h = [hidden_dim] * (num_layers - 1)\n        self.layers = ModuleList(\n            Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]))\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function of MLP.\n\n        Args:\n            x (Tensor): The input feature, has shape\n                (num_queries, bs, input_dim).\n        Returns:\n            Tensor: The output feature, has shape\n                (num_queries, bs, output_dim).\n        \"\"\"\n        for i, layer in enumerate(self.layers):\n            x = F.relu(layer(x)) if i < self.num_layers - 1 else layer(x)\n        return x\n\n\n@MODELS.register_module()\nclass DynamicConv(BaseModule):\n    \"\"\"Implements Dynamic Convolution.\n\n    This module generate parameters for each sample and\n    use bmm to implement 1*1 convolution. Code is modified\n    from the `official github repo <https://github.com/PeizeSun/\n    SparseR-CNN/blob/main/projects/SparseRCNN/sparsercnn/head.py#L258>`_ .\n\n    Args:\n        in_channels (int): The input feature channel.\n            Defaults to 256.\n        feat_channels (int): The inner feature channel.\n            Defaults to 64.\n        out_channels (int, optional): The output feature channel.\n            When not specified, it will be set to `in_channels`\n            by default\n        input_feat_shape (int): The shape of input feature.\n            Defaults to 7.\n        with_proj (bool): Project two-dimentional feature to\n            one-dimentional feature. Default to True.\n        act_cfg (dict): The activation config for DynamicConv.\n        norm_cfg (dict): Config dict for normalization layer. Default\n            layer normalization.\n        init_cfg (obj:`mmengine.ConfigDict`): The Config for initialization.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int = 256,\n                 feat_channels: int = 64,\n                 out_channels: Optional[int] = None,\n                 input_feat_shape: int = 7,\n                 with_proj: bool = True,\n                 act_cfg: OptConfigType = dict(type='ReLU', inplace=True),\n                 norm_cfg: OptConfigType = dict(type='LN'),\n                 init_cfg: OptConfigType = None) -> None:\n        super(DynamicConv, self).__init__(init_cfg)\n        self.in_channels = in_channels\n        self.feat_channels = feat_channels\n        self.out_channels_raw = out_channels\n        self.input_feat_shape = input_feat_shape\n        self.with_proj = with_proj\n        self.act_cfg = act_cfg\n        self.norm_cfg = norm_cfg\n        self.out_channels = out_channels if out_channels else in_channels\n\n        self.num_params_in = self.in_channels * self.feat_channels\n        self.num_params_out = self.out_channels * self.feat_channels\n        self.dynamic_layer = nn.Linear(\n            self.in_channels, self.num_params_in + self.num_params_out)\n\n        self.norm_in = build_norm_layer(norm_cfg, self.feat_channels)[1]\n        self.norm_out = build_norm_layer(norm_cfg, self.out_channels)[1]\n\n        self.activation = build_activation_layer(act_cfg)\n\n        num_output = self.out_channels * input_feat_shape**2\n        if self.with_proj:\n            self.fc_layer = nn.Linear(num_output, self.out_channels)\n            self.fc_norm = build_norm_layer(norm_cfg, self.out_channels)[1]\n\n    def forward(self, param_feature: Tensor, input_feature: Tensor) -> Tensor:\n        \"\"\"Forward function for `DynamicConv`.\n\n        Args:\n            param_feature (Tensor): The feature can be used\n                to generate the parameter, has shape\n                (num_all_proposals, in_channels).\n            input_feature (Tensor): Feature that\n                interact with parameters, has shape\n                (num_all_proposals, in_channels, H, W).\n\n        Returns:\n            Tensor: The output feature has shape\n            (num_all_proposals, out_channels).\n        \"\"\"\n        input_feature = input_feature.flatten(2).permute(2, 0, 1)\n\n        input_feature = input_feature.permute(1, 0, 2)\n        parameters = self.dynamic_layer(param_feature)\n\n        param_in = parameters[:, :self.num_params_in].view(\n            -1, self.in_channels, self.feat_channels)\n        param_out = parameters[:, -self.num_params_out:].view(\n            -1, self.feat_channels, self.out_channels)\n\n        # input_feature has shape (num_all_proposals, H*W, in_channels)\n        # param_in has shape (num_all_proposals, in_channels, feat_channels)\n        # feature has shape (num_all_proposals, H*W, feat_channels)\n        features = torch.bmm(input_feature, param_in)\n        features = self.norm_in(features)\n        features = self.activation(features)\n\n        # param_out has shape (batch_size, feat_channels, out_channels)\n        features = torch.bmm(features, param_out)\n        features = self.norm_out(features)\n        features = self.activation(features)\n\n        if self.with_proj:\n            features = features.flatten(1)\n            features = self.fc_layer(features)\n            features = self.fc_norm(features)\n            features = self.activation(features)\n\n        return features\n\n\ndef get_text_sine_pos_embed(\n    pos_tensor: torch.Tensor,\n    num_pos_feats: int = 128,\n    temperature: int = 10000,\n    exchange_xy: bool = True,\n):\n    \"\"\"generate sine position embedding from a position tensor\n    Args:\n        pos_tensor (torch.Tensor): shape: [..., n].\n        num_pos_feats (int): projected shape for each float in the tensor.\n        temperature (int): temperature in the sine/cosine function.\n        exchange_xy (bool, optional): exchange pos x and pos y. For example,\n            input tensor is [x,y], the results will be [pos(y), pos(x)].\n            Defaults to True.\n    Returns:\n        pos_embed (torch.Tensor): shape: [..., n*num_pos_feats].\n    \"\"\"\n    scale = 2 * math.pi\n    dim_t = torch.arange(\n        num_pos_feats, dtype=torch.float32, device=pos_tensor.device)\n    dim_t = temperature**(2 * torch.div(dim_t, 2, rounding_mode='floor') /\n                          num_pos_feats)\n\n    def sine_func(x: torch.Tensor):\n        sin_x = x * scale / dim_t\n        sin_x = torch.stack((sin_x[..., 0::2].sin(), sin_x[..., 1::2].cos()),\n                            dim=3).flatten(2)\n        return sin_x\n\n    pos_res = [\n        sine_func(x)\n        for x in pos_tensor.split([1] * pos_tensor.shape[-1], dim=-1)\n    ]\n    if exchange_xy:\n        pos_res[0], pos_res[1] = pos_res[1], pos_res[0]\n    pos_res = torch.cat(pos_res, dim=-1)\n    return pos_res\n"
  },
  {
    "path": "mmdet/models/losses/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .accuracy import Accuracy, accuracy\nfrom .ae_loss import AssociativeEmbeddingLoss\nfrom .balanced_l1_loss import BalancedL1Loss, balanced_l1_loss\nfrom .cross_entropy_loss import (CrossEntropyCustomLoss, CrossEntropyLoss,\n                                 binary_cross_entropy, cross_entropy,\n                                 mask_cross_entropy)\nfrom .ddq_detr_aux_loss import DDQAuxLoss\nfrom .dice_loss import DiceLoss\nfrom .eqlv2_loss import EQLV2Loss\nfrom .focal_loss import FocalCustomLoss, FocalLoss, sigmoid_focal_loss\nfrom .gaussian_focal_loss import GaussianFocalLoss\nfrom .gfocal_loss import DistributionFocalLoss, QualityFocalLoss\nfrom .ghm_loss import GHMC, GHMR\nfrom .iou_loss import (BoundedIoULoss, CIoULoss, DIoULoss, EIoULoss, GIoULoss,\n                       IoULoss, SIoULoss, bounded_iou_loss, iou_loss)\nfrom .kd_loss import KnowledgeDistillationKLDivLoss\nfrom .l2_loss import L2Loss\nfrom .margin_loss import MarginL2Loss\nfrom .mse_loss import MSELoss, mse_loss\nfrom .multipos_cross_entropy_loss import MultiPosCrossEntropyLoss\nfrom .pisa_loss import carl_loss, isr_p\nfrom .seesaw_loss import SeesawLoss\nfrom .smooth_l1_loss import L1Loss, SmoothL1Loss, l1_loss, smooth_l1_loss\nfrom .triplet_loss import TripletLoss\nfrom .utils import reduce_loss, weight_reduce_loss, weighted_loss\nfrom .varifocal_loss import VarifocalLoss\n\n__all__ = [\n    'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy',\n    'mask_cross_entropy', 'CrossEntropyLoss', 'sigmoid_focal_loss',\n    'FocalLoss', 'smooth_l1_loss', 'SmoothL1Loss', 'balanced_l1_loss',\n    'BalancedL1Loss', 'mse_loss', 'MSELoss', 'iou_loss', 'bounded_iou_loss',\n    'IoULoss', 'BoundedIoULoss', 'GIoULoss', 'DIoULoss', 'CIoULoss',\n    'EIoULoss', 'SIoULoss', 'GHMC', 'GHMR', 'reduce_loss',\n    'weight_reduce_loss', 'weighted_loss', 'L1Loss', 'l1_loss', 'isr_p',\n    'carl_loss', 'AssociativeEmbeddingLoss', 'GaussianFocalLoss',\n    'QualityFocalLoss', 'DistributionFocalLoss', 'VarifocalLoss',\n    'KnowledgeDistillationKLDivLoss', 'SeesawLoss', 'DiceLoss', 'EQLV2Loss',\n    'MarginL2Loss', 'MultiPosCrossEntropyLoss', 'L2Loss', 'TripletLoss',\n    'DDQAuxLoss', 'CrossEntropyCustomLoss', 'FocalCustomLoss'\n]\n"
  },
  {
    "path": "mmdet/models/losses/accuracy.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\n\n\ndef accuracy(pred, target, topk=1, thresh=None):\n    \"\"\"Calculate accuracy according to the prediction and target.\n\n    Args:\n        pred (torch.Tensor): The model prediction, shape (N, num_class)\n        target (torch.Tensor): The target of each prediction, shape (N, )\n        topk (int | tuple[int], optional): If the predictions in ``topk``\n            matches the target, the predictions will be regarded as\n            correct ones. Defaults to 1.\n        thresh (float, optional): If not None, predictions with scores under\n            this threshold are considered incorrect. Default to None.\n\n    Returns:\n        float | tuple[float]: If the input ``topk`` is a single integer,\n            the function will return a single float as accuracy. If\n            ``topk`` is a tuple containing multiple integers, the\n            function will return a tuple containing accuracies of\n            each ``topk`` number.\n    \"\"\"\n    assert isinstance(topk, (int, tuple))\n    if isinstance(topk, int):\n        topk = (topk, )\n        return_single = True\n    else:\n        return_single = False\n\n    maxk = max(topk)\n    if pred.size(0) == 0:\n        accu = [pred.new_tensor(0.) for i in range(len(topk))]\n        return accu[0] if return_single else accu\n    assert pred.ndim == 2 and target.ndim == 1\n    assert pred.size(0) == target.size(0)\n    assert maxk <= pred.size(1), \\\n        f'maxk {maxk} exceeds pred dimension {pred.size(1)}'\n    pred_value, pred_label = pred.topk(maxk, dim=1)\n    pred_label = pred_label.t()  # transpose to shape (maxk, N)\n    correct = pred_label.eq(target.view(1, -1).expand_as(pred_label))\n    if thresh is not None:\n        # Only prediction values larger than thresh are counted as correct\n        correct = correct & (pred_value > thresh).t()\n    res = []\n    for k in topk:\n        correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True)\n        res.append(correct_k.mul_(100.0 / pred.size(0)))\n    return res[0] if return_single else res\n\n\nclass Accuracy(nn.Module):\n\n    def __init__(self, topk=(1, ), thresh=None):\n        \"\"\"Module to calculate the accuracy.\n\n        Args:\n            topk (tuple, optional): The criterion used to calculate the\n                accuracy. Defaults to (1,).\n            thresh (float, optional): If not None, predictions with scores\n                under this threshold are considered incorrect. Default to None.\n        \"\"\"\n        super().__init__()\n        self.topk = topk\n        self.thresh = thresh\n\n    def forward(self, pred, target):\n        \"\"\"Forward function to calculate accuracy.\n\n        Args:\n            pred (torch.Tensor): Prediction of models.\n            target (torch.Tensor): Target for each prediction.\n\n        Returns:\n            tuple[float]: The accuracies under different topk criterions.\n        \"\"\"\n        return accuracy(pred, target, self.topk, self.thresh)\n"
  },
  {
    "path": "mmdet/models/losses/ae_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mmdet.registry import MODELS\n\n\ndef ae_loss_per_image(tl_preds, br_preds, match):\n    \"\"\"Associative Embedding Loss in one image.\n\n    Associative Embedding Loss including two parts: pull loss and push loss.\n    Pull loss makes embedding vectors from same object closer to each other.\n    Push loss distinguish embedding vector from different objects, and makes\n        the gap between them is large enough.\n\n    During computing, usually there are 3 cases:\n        - no object in image: both pull loss and push loss will be 0.\n        - one object in image: push loss will be 0 and pull loss is computed\n            by the two corner of the only object.\n        - more than one objects in image: pull loss is computed by corner pairs\n            from each object, push loss is computed by each object with all\n            other objects. We use confusion matrix with 0 in diagonal to\n            compute the push loss.\n\n    Args:\n        tl_preds (tensor): Embedding feature map of left-top corner.\n        br_preds (tensor): Embedding feature map of bottim-right corner.\n        match (list): Downsampled coordinates pair of each ground truth box.\n    \"\"\"\n\n    tl_list, br_list, me_list = [], [], []\n    if len(match) == 0:  # no object in image\n        pull_loss = tl_preds.sum() * 0.\n        push_loss = tl_preds.sum() * 0.\n    else:\n        for m in match:\n            [tl_y, tl_x], [br_y, br_x] = m\n            tl_e = tl_preds[:, tl_y, tl_x].view(-1, 1)\n            br_e = br_preds[:, br_y, br_x].view(-1, 1)\n            tl_list.append(tl_e)\n            br_list.append(br_e)\n            me_list.append((tl_e + br_e) / 2.0)\n\n        tl_list = torch.cat(tl_list)\n        br_list = torch.cat(br_list)\n        me_list = torch.cat(me_list)\n\n        assert tl_list.size() == br_list.size()\n\n        # N is object number in image, M is dimension of embedding vector\n        N, M = tl_list.size()\n\n        pull_loss = (tl_list - me_list).pow(2) + (br_list - me_list).pow(2)\n        pull_loss = pull_loss.sum() / N\n\n        margin = 1  # exp setting of CornerNet, details in section 3.3 of paper\n\n        # confusion matrix of push loss\n        conf_mat = me_list.expand((N, N, M)).permute(1, 0, 2) - me_list\n        conf_weight = 1 - torch.eye(N).type_as(me_list)\n        conf_mat = conf_weight * (margin - conf_mat.sum(-1).abs())\n\n        if N > 1:  # more than one object in current image\n            push_loss = F.relu(conf_mat).sum() / (N * (N - 1))\n        else:\n            push_loss = tl_preds.sum() * 0.\n\n    return pull_loss, push_loss\n\n\n@MODELS.register_module()\nclass AssociativeEmbeddingLoss(nn.Module):\n    \"\"\"Associative Embedding Loss.\n\n    More details can be found in\n    `Associative Embedding <https://arxiv.org/abs/1611.05424>`_ and\n    `CornerNet <https://arxiv.org/abs/1808.01244>`_ .\n    Code is modified from `kp_utils.py <https://github.com/princeton-vl/CornerNet/blob/master/models/py_utils/kp_utils.py#L180>`_  # noqa: E501\n\n    Args:\n        pull_weight (float): Loss weight for corners from same object.\n        push_weight (float): Loss weight for corners from different object.\n    \"\"\"\n\n    def __init__(self, pull_weight=0.25, push_weight=0.25):\n        super(AssociativeEmbeddingLoss, self).__init__()\n        self.pull_weight = pull_weight\n        self.push_weight = push_weight\n\n    def forward(self, pred, target, match):\n        \"\"\"Forward function.\"\"\"\n        batch = pred.size(0)\n        pull_all, push_all = 0.0, 0.0\n        for i in range(batch):\n            pull, push = ae_loss_per_image(pred[i], target[i], match[i])\n\n            pull_all += self.pull_weight * pull\n            push_all += self.push_weight * push\n\n        return pull_all, push_all\n"
  },
  {
    "path": "mmdet/models/losses/balanced_l1_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport torch\nimport torch.nn as nn\n\nfrom mmdet.registry import MODELS\nfrom .utils import weighted_loss\n\n\n@weighted_loss\ndef balanced_l1_loss(pred,\n                     target,\n                     beta=1.0,\n                     alpha=0.5,\n                     gamma=1.5,\n                     reduction='mean'):\n    \"\"\"Calculate balanced L1 loss.\n\n    Please see the `Libra R-CNN <https://arxiv.org/pdf/1904.02701.pdf>`_\n\n    Args:\n        pred (torch.Tensor): The prediction with shape (N, 4).\n        target (torch.Tensor): The learning target of the prediction with\n            shape (N, 4).\n        beta (float): The loss is a piecewise function of prediction and target\n            and ``beta`` serves as a threshold for the difference between the\n            prediction and target. Defaults to 1.0.\n        alpha (float): The denominator ``alpha`` in the balanced L1 loss.\n            Defaults to 0.5.\n        gamma (float): The ``gamma`` in the balanced L1 loss.\n            Defaults to 1.5.\n        reduction (str, optional): The method that reduces the loss to a\n            scalar. Options are \"none\", \"mean\" and \"sum\".\n\n    Returns:\n        torch.Tensor: The calculated loss\n    \"\"\"\n    assert beta > 0\n    if target.numel() == 0:\n        return pred.sum() * 0\n\n    assert pred.size() == target.size()\n\n    diff = torch.abs(pred - target)\n    b = np.e**(gamma / alpha) - 1\n    loss = torch.where(\n        diff < beta, alpha / b *\n        (b * diff + 1) * torch.log(b * diff / beta + 1) - alpha * diff,\n        gamma * diff + gamma / b - alpha * beta)\n\n    return loss\n\n\n@MODELS.register_module()\nclass BalancedL1Loss(nn.Module):\n    \"\"\"Balanced L1 Loss.\n\n    arXiv: https://arxiv.org/pdf/1904.02701.pdf (CVPR 2019)\n\n    Args:\n        alpha (float): The denominator ``alpha`` in the balanced L1 loss.\n            Defaults to 0.5.\n        gamma (float): The ``gamma`` in the balanced L1 loss. Defaults to 1.5.\n        beta (float, optional): The loss is a piecewise function of prediction\n            and target. ``beta`` serves as a threshold for the difference\n            between the prediction and target. Defaults to 1.0.\n        reduction (str, optional): The method that reduces the loss to a\n            scalar. Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float, optional): The weight of the loss. Defaults to 1.0\n    \"\"\"\n\n    def __init__(self,\n                 alpha=0.5,\n                 gamma=1.5,\n                 beta=1.0,\n                 reduction='mean',\n                 loss_weight=1.0):\n        super(BalancedL1Loss, self).__init__()\n        self.alpha = alpha\n        self.gamma = gamma\n        self.beta = beta\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None,\n                **kwargs):\n        \"\"\"Forward function of loss.\n\n        Args:\n            pred (torch.Tensor): The prediction with shape (N, 4).\n            target (torch.Tensor): The learning target of the prediction with\n                shape (N, 4).\n            weight (torch.Tensor, optional): Sample-wise loss weight with\n                shape (N, ).\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            torch.Tensor: The calculated loss\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_bbox = self.loss_weight * balanced_l1_loss(\n            pred,\n            target,\n            weight,\n            alpha=self.alpha,\n            gamma=self.gamma,\n            beta=self.beta,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss_bbox\n"
  },
  {
    "path": "mmdet/models/losses/cross_entropy_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mmdet.registry import MODELS\nfrom .accuracy import accuracy\nfrom .utils import weight_reduce_loss\n\n\ndef cross_entropy(pred,\n                  label,\n                  weight=None,\n                  reduction='mean',\n                  avg_factor=None,\n                  class_weight=None,\n                  ignore_index=-100,\n                  avg_non_ignore=False):\n    \"\"\"Calculate the CrossEntropy loss.\n\n    Args:\n        pred (torch.Tensor): The prediction with shape (N, C), C is the number\n            of classes.\n        label (torch.Tensor): The learning label of the prediction.\n        weight (torch.Tensor, optional): Sample-wise loss weight.\n        reduction (str, optional): The method used to reduce the loss.\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n        class_weight (list[float], optional): The weight for each class.\n        ignore_index (int | None): The label index to be ignored.\n            If None, it will be set to default value. Default: -100.\n        avg_non_ignore (bool): The flag decides to whether the loss is\n            only averaged over non-ignored targets. Default: False.\n\n    Returns:\n        torch.Tensor: The calculated loss\n    \"\"\"\n    # The default value of ignore_index is the same as F.cross_entropy\n    ignore_index = -100 if ignore_index is None else ignore_index\n    # element-wise losses\n    loss = F.cross_entropy(\n        pred,\n        label,\n        weight=class_weight,\n        reduction='none',\n        ignore_index=ignore_index)\n\n    # average loss over non-ignored elements\n    # pytorch's official cross_entropy average loss over non-ignored elements\n    # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660  # noqa\n    if (avg_factor is None) and avg_non_ignore and reduction == 'mean':\n        avg_factor = label.numel() - (label == ignore_index).sum().item()\n\n    # apply weights and do the reduction\n    if weight is not None:\n        weight = weight.float()\n    loss = weight_reduce_loss(\n        loss, weight=weight, reduction=reduction, avg_factor=avg_factor)\n\n    return loss\n\n\ndef _expand_onehot_labels(labels, label_weights, label_channels, ignore_index):\n    \"\"\"Expand onehot labels to match the size of prediction.\"\"\"\n    bin_labels = labels.new_full((labels.size(0), label_channels), 0)\n    valid_mask = (labels >= 0) & (labels != ignore_index)\n    inds = torch.nonzero(\n        valid_mask & (labels < label_channels), as_tuple=False)\n\n    if inds.numel() > 0:\n        bin_labels[inds, labels[inds]] = 1\n\n    valid_mask = valid_mask.view(-1, 1).expand(labels.size(0),\n                                               label_channels).float()\n    if label_weights is None:\n        bin_label_weights = valid_mask\n    else:\n        bin_label_weights = label_weights.view(-1, 1).repeat(1, label_channels)\n        bin_label_weights *= valid_mask\n\n    return bin_labels, bin_label_weights, valid_mask\n\n\ndef binary_cross_entropy(pred,\n                         label,\n                         weight=None,\n                         reduction='mean',\n                         avg_factor=None,\n                         class_weight=None,\n                         ignore_index=-100,\n                         avg_non_ignore=False):\n    \"\"\"Calculate the binary CrossEntropy loss.\n\n    Args:\n        pred (torch.Tensor): The prediction with shape (N, 1) or (N, ).\n            When the shape of pred is (N, 1), label will be expanded to\n            one-hot format, and when the shape of pred is (N, ), label\n            will not be expanded to one-hot format.\n        label (torch.Tensor): The learning label of the prediction,\n            with shape (N, ).\n        weight (torch.Tensor, optional): Sample-wise loss weight.\n        reduction (str, optional): The method used to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\".\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n        class_weight (list[float], optional): The weight for each class.\n        ignore_index (int | None): The label index to be ignored.\n            If None, it will be set to default value. Default: -100.\n        avg_non_ignore (bool): The flag decides to whether the loss is\n            only averaged over non-ignored targets. Default: False.\n\n    Returns:\n        torch.Tensor: The calculated loss.\n    \"\"\"\n    # The default value of ignore_index is the same as F.cross_entropy\n    ignore_index = -100 if ignore_index is None else ignore_index\n\n    if pred.dim() != label.dim():\n        label, weight, valid_mask = _expand_onehot_labels(\n            label, weight, pred.size(-1), ignore_index)\n    else:\n        # should mask out the ignored elements\n        valid_mask = ((label >= 0) & (label != ignore_index)).float()\n        if weight is not None:\n            # The inplace writing method will have a mismatched broadcast\n            # shape error if the weight and valid_mask dimensions\n            # are inconsistent such as (B,N,1) and (B,N,C).\n            weight = weight * valid_mask\n        else:\n            weight = valid_mask\n\n    # average loss over non-ignored elements\n    if (avg_factor is None) and avg_non_ignore and reduction == 'mean':\n        avg_factor = valid_mask.sum().item()\n\n    # weighted element-wise losses\n    weight = weight.float()\n    loss = F.binary_cross_entropy_with_logits(\n        pred, label.float(), pos_weight=class_weight, reduction='none')\n    # do the reduction for the weighted loss\n    loss = weight_reduce_loss(\n        loss, weight, reduction=reduction, avg_factor=avg_factor)\n\n    return loss\n\n\ndef mask_cross_entropy(pred,\n                       target,\n                       label,\n                       reduction='mean',\n                       avg_factor=None,\n                       class_weight=None,\n                       ignore_index=None,\n                       **kwargs):\n    \"\"\"Calculate the CrossEntropy loss for masks.\n\n    Args:\n        pred (torch.Tensor): The prediction with shape (N, C, *), C is the\n            number of classes. The trailing * indicates arbitrary shape.\n        target (torch.Tensor): The learning label of the prediction.\n        label (torch.Tensor): ``label`` indicates the class label of the mask\n            corresponding object. This will be used to select the mask in the\n            of the class which the object belongs to when the mask prediction\n            if not class-agnostic.\n        reduction (str, optional): The method used to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\".\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n        class_weight (list[float], optional): The weight for each class.\n        ignore_index (None): Placeholder, to be consistent with other loss.\n            Default: None.\n\n    Returns:\n        torch.Tensor: The calculated loss\n\n    Example:\n        >>> N, C = 3, 11\n        >>> H, W = 2, 2\n        >>> pred = torch.randn(N, C, H, W) * 1000\n        >>> target = torch.rand(N, H, W)\n        >>> label = torch.randint(0, C, size=(N,))\n        >>> reduction = 'mean'\n        >>> avg_factor = None\n        >>> class_weights = None\n        >>> loss = mask_cross_entropy(pred, target, label, reduction,\n        >>>                           avg_factor, class_weights)\n        >>> assert loss.shape == (1,)\n    \"\"\"\n    assert ignore_index is None, 'BCE loss does not support ignore_index'\n    # TODO: handle these two reserved arguments\n    assert reduction == 'mean' and avg_factor is None\n    num_rois = pred.size()[0]\n    inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device)\n    pred_slice = pred[inds, label].squeeze(1)\n    return F.binary_cross_entropy_with_logits(\n        pred_slice, target, weight=class_weight, reduction='mean')[None]\n\n\n@MODELS.register_module()\nclass CrossEntropyLoss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid=False,\n                 use_mask=False,\n                 reduction='mean',\n                 class_weight=None,\n                 ignore_index=None,\n                 loss_weight=1.0,\n                 avg_non_ignore=False):\n        \"\"\"CrossEntropyLoss.\n\n        Args:\n            use_sigmoid (bool, optional): Whether the prediction uses sigmoid\n                of softmax. Defaults to False.\n            use_mask (bool, optional): Whether to use mask cross entropy loss.\n                Defaults to False.\n            reduction (str, optional): . Defaults to 'mean'.\n                Options are \"none\", \"mean\" and \"sum\".\n            class_weight (list[float], optional): Weight of each class.\n                Defaults to None.\n            ignore_index (int | None): The label index to be ignored.\n                Defaults to None.\n            loss_weight (float, optional): Weight of the loss. Defaults to 1.0.\n            avg_non_ignore (bool): The flag decides to whether the loss is\n                only averaged over non-ignored targets. Default: False.\n        \"\"\"\n        super(CrossEntropyLoss, self).__init__()\n        assert (use_sigmoid is False) or (use_mask is False)\n        self.use_sigmoid = use_sigmoid\n        self.use_mask = use_mask\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.class_weight = class_weight\n        self.ignore_index = ignore_index\n        self.avg_non_ignore = avg_non_ignore\n        if ((ignore_index is not None) and not self.avg_non_ignore\n                and self.reduction == 'mean'):\n            warnings.warn(\n                'Default ``avg_non_ignore`` is False, if you would like to '\n                'ignore the certain label and average loss over non-ignore '\n                'labels, which is the same with PyTorch official '\n                'cross_entropy, set ``avg_non_ignore=True``.')\n\n        if self.use_sigmoid:\n            self.cls_criterion = binary_cross_entropy\n        elif self.use_mask:\n            self.cls_criterion = mask_cross_entropy\n        else:\n            self.cls_criterion = cross_entropy\n\n    def extra_repr(self):\n        \"\"\"Extra repr.\"\"\"\n        s = f'avg_non_ignore={self.avg_non_ignore}'\n        return s\n\n    def forward(self,\n                cls_score,\n                label,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None,\n                ignore_index=None,\n                **kwargs):\n        \"\"\"Forward function.\n\n        Args:\n            cls_score (torch.Tensor): The prediction.\n            label (torch.Tensor): The learning label of the prediction.\n            weight (torch.Tensor, optional): Sample-wise loss weight.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The method used to reduce the\n                loss. Options are \"none\", \"mean\" and \"sum\".\n            ignore_index (int | None): The label index to be ignored.\n                If not None, it will override the default value. Default: None.\n        Returns:\n            torch.Tensor: The calculated loss.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if ignore_index is None:\n            ignore_index = self.ignore_index\n\n        if self.class_weight is not None:\n            class_weight = cls_score.new_tensor(\n                self.class_weight, device=cls_score.device)\n        else:\n            class_weight = None\n        loss_cls = self.loss_weight * self.cls_criterion(\n            cls_score,\n            label,\n            weight,\n            class_weight=class_weight,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            ignore_index=ignore_index,\n            avg_non_ignore=self.avg_non_ignore,\n            **kwargs)\n        return loss_cls\n\n\n@MODELS.register_module()\nclass CrossEntropyCustomLoss(CrossEntropyLoss):\n\n    def __init__(self,\n                 use_sigmoid=False,\n                 use_mask=False,\n                 reduction='mean',\n                 num_classes=-1,\n                 class_weight=None,\n                 ignore_index=None,\n                 loss_weight=1.0,\n                 avg_non_ignore=False):\n        \"\"\"CrossEntropyCustomLoss.\n\n        Args:\n            use_sigmoid (bool, optional): Whether the prediction uses sigmoid\n                of softmax. Defaults to False.\n            use_mask (bool, optional): Whether to use mask cross entropy loss.\n                Defaults to False.\n            reduction (str, optional): . Defaults to 'mean'.\n                Options are \"none\", \"mean\" and \"sum\".\n            num_classes (int): Number of classes to classify.\n            class_weight (list[float], optional): Weight of each class.\n                Defaults to None.\n            ignore_index (int | None): The label index to be ignored.\n                Defaults to None.\n            loss_weight (float, optional): Weight of the loss. Defaults to 1.0.\n            avg_non_ignore (bool): The flag decides to whether the loss is\n                only averaged over non-ignored targets. Default: False.\n        \"\"\"\n        super(CrossEntropyCustomLoss, self).__init__()\n        assert (use_sigmoid is False) or (use_mask is False)\n        self.use_sigmoid = use_sigmoid\n        self.use_mask = use_mask\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.class_weight = class_weight\n        self.ignore_index = ignore_index\n        self.avg_non_ignore = avg_non_ignore\n        if ((ignore_index is not None) and not self.avg_non_ignore\n                and self.reduction == 'mean'):\n            warnings.warn(\n                'Default ``avg_non_ignore`` is False, if you would like to '\n                'ignore the certain label and average loss over non-ignore '\n                'labels, which is the same with PyTorch official '\n                'cross_entropy, set ``avg_non_ignore=True``.')\n\n        if self.use_sigmoid:\n            self.cls_criterion = binary_cross_entropy\n        elif self.use_mask:\n            self.cls_criterion = mask_cross_entropy\n        else:\n            self.cls_criterion = cross_entropy\n\n        self.num_classes = num_classes\n\n        assert self.num_classes != -1\n\n        # custom output channels of the classifier\n        self.custom_cls_channels = True\n        # custom activation of cls_score\n        self.custom_activation = True\n        # custom accuracy of the classsifier\n        self.custom_accuracy = True\n\n    def get_cls_channels(self, num_classes):\n        assert num_classes == self.num_classes\n        if not self.use_sigmoid:\n            return num_classes + 1\n        else:\n            return num_classes\n\n    def get_activation(self, cls_score):\n\n        fine_cls_score = cls_score[:, :self.num_classes]\n\n        if not self.use_sigmoid:\n            bg_score = cls_score[:, [-1]]\n            new_score = torch.cat([fine_cls_score, bg_score], dim=-1)\n            scores = F.softmax(new_score, dim=-1)\n        else:\n            score_classes = fine_cls_score.sigmoid()\n            score_neg = 1 - score_classes.sum(dim=1, keepdim=True)\n            score_neg = score_neg.clamp(min=0, max=1)\n            scores = torch.cat([score_classes, score_neg], dim=1)\n\n        return scores\n\n    def get_accuracy(self, cls_score, labels):\n\n        fine_cls_score = cls_score[:, :self.num_classes]\n\n        pos_inds = labels < self.num_classes\n        acc_classes = accuracy(fine_cls_score[pos_inds], labels[pos_inds])\n        acc = dict()\n        acc['acc_classes'] = acc_classes\n        return acc\n"
  },
  {
    "path": "mmdet/models/losses/ddq_detr_aux_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmengine.structures import BaseDataElement\n\nfrom mmdet.models.utils import multi_apply\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import reduce_mean\n\n\nclass DDQAuxLoss(nn.Module):\n    \"\"\"DDQ auxiliary branches loss for dense queries.\n\n    Args:\n        loss_cls (dict):\n            Configuration of classification loss function.\n        loss_bbox (dict):\n            Configuration of bbox regression loss function.\n        train_cfg (dict):\n            Configuration of gt targets assigner for each predicted bbox.\n    \"\"\"\n\n    def __init__(\n        self,\n        loss_cls=dict(\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            activated=True,  # use probability instead of logit as input\n            beta=2.0,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        train_cfg=dict(\n            assigner=dict(type='TopkHungarianAssigner', topk=8),\n            alpha=1,\n            beta=6),\n    ):\n        super(DDQAuxLoss, self).__init__()\n        self.train_cfg = train_cfg\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox = MODELS.build(loss_bbox)\n        self.assigner = TASK_UTILS.build(self.train_cfg['assigner'])\n\n        sampler_cfg = dict(type='PseudoSampler')\n        self.sampler = TASK_UTILS.build(sampler_cfg)\n\n    def loss_single(self, cls_score, bbox_pred, labels, label_weights,\n                    bbox_targets, alignment_metrics):\n        \"\"\"Calculate auxiliary branches loss for dense queries for one image.\n\n        Args:\n            cls_score (Tensor): Predicted normalized classification\n                scores for one image, has shape (num_dense_queries,\n                cls_out_channels).\n            bbox_pred (Tensor): Predicted unnormalized bbox coordinates\n                for one image, has shape (num_dense_queries, 4) with the\n                last dimension arranged as (x1, y1, x2, y2).\n            labels (Tensor): Labels for one image.\n            label_weights (Tensor): Label weights for one image.\n            bbox_targets (Tensor): Bbox targets for one image.\n            alignment_metrics (Tensor): Normalized alignment metrics for one\n                image.\n\n        Returns:\n            tuple: A tuple of loss components and loss weights.\n        \"\"\"\n        bbox_targets = bbox_targets.reshape(-1, 4)\n        labels = labels.reshape(-1)\n        alignment_metrics = alignment_metrics.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        targets = (labels, alignment_metrics)\n        cls_loss_func = self.loss_cls\n\n        loss_cls = cls_loss_func(\n            cls_score, targets, label_weights, avg_factor=1.0)\n\n        # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n        bg_class_ind = cls_score.size(-1)\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().squeeze(1)\n\n        if len(pos_inds) > 0:\n            pos_bbox_targets = bbox_targets[pos_inds]\n            pos_bbox_pred = bbox_pred[pos_inds]\n\n            pos_decode_bbox_pred = pos_bbox_pred\n            pos_decode_bbox_targets = pos_bbox_targets\n\n            # regression loss\n            pos_bbox_weight = alignment_metrics[pos_inds]\n\n            loss_bbox = self.loss_bbox(\n                pos_decode_bbox_pred,\n                pos_decode_bbox_targets,\n                weight=pos_bbox_weight,\n                avg_factor=1.0)\n        else:\n            loss_bbox = bbox_pred.sum() * 0\n            pos_bbox_weight = bbox_targets.new_tensor(0.)\n\n        return loss_cls, loss_bbox, alignment_metrics.sum(\n        ), pos_bbox_weight.sum()\n\n    def loss(self, cls_scores, bbox_preds, gt_bboxes, gt_labels, img_metas,\n             **kwargs):\n        \"\"\"Calculate auxiliary branches loss for dense queries.\n\n        Args:\n            cls_scores (Tensor): Predicted normalized classification\n                scores, has shape (bs, num_dense_queries,\n                cls_out_channels).\n            bbox_preds (Tensor): Predicted unnormalized bbox coordinates,\n                has shape (bs, num_dense_queries, 4) with the last\n                dimension arranged as (x1, y1, x2, y2).\n            gt_bboxes (list[Tensor]): List of unnormalized ground truth\n                bboxes for each image, each has shape (num_gt, 4) with the\n                last dimension arranged as (x1, y1, x2, y2).\n                NOTE: num_gt is dynamic for each image.\n            gt_labels (list[Tensor]): List of ground truth classification\n                index for each image, each has shape (num_gt,).\n                NOTE: num_gt is dynamic for each image.\n            img_metas (list[dict]): Meta information for one image,\n                e.g., image size, scaling factor, etc.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        flatten_cls_scores = cls_scores\n        flatten_bbox_preds = bbox_preds\n\n        cls_reg_targets = self.get_targets(\n            flatten_cls_scores,\n            flatten_bbox_preds,\n            gt_bboxes,\n            img_metas,\n            gt_labels_list=gt_labels,\n        )\n        (labels_list, label_weights_list, bbox_targets_list,\n         alignment_metrics_list) = cls_reg_targets\n\n        losses_cls, losses_bbox, \\\n            cls_avg_factors, bbox_avg_factors = multi_apply(\n                self.loss_single,\n                flatten_cls_scores,\n                flatten_bbox_preds,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                alignment_metrics_list,\n                )\n\n        cls_avg_factor = reduce_mean(sum(cls_avg_factors)).clamp_(min=1).item()\n        losses_cls = list(map(lambda x: x / cls_avg_factor, losses_cls))\n\n        bbox_avg_factor = reduce_mean(\n            sum(bbox_avg_factors)).clamp_(min=1).item()\n        losses_bbox = list(map(lambda x: x / bbox_avg_factor, losses_bbox))\n        return dict(aux_loss_cls=losses_cls, aux_loss_bbox=losses_bbox)\n\n    def get_targets(self,\n                    cls_scores,\n                    bbox_preds,\n                    gt_bboxes_list,\n                    img_metas,\n                    gt_labels_list=None,\n                    **kwargs):\n        \"\"\"Compute regression and classification targets for a batch images.\n\n        Args:\n            cls_scores (Tensor): Predicted normalized classification\n                scores, has shape (bs, num_dense_queries,\n                cls_out_channels).\n            bbox_preds (Tensor): Predicted unnormalized bbox coordinates,\n                has shape (bs, num_dense_queries, 4) with the last\n                dimension arranged as (x1, y1, x2, y2).\n            gt_bboxes_list (List[Tensor]): List of unnormalized ground truth\n                bboxes for each image, each has shape (num_gt, 4) with the\n                last dimension arranged as (x1, y1, x2, y2).\n                NOTE: num_gt is dynamic for each image.\n            img_metas (list[dict]): Meta information for one image,\n                e.g., image size, scaling factor, etc.\n            gt_labels_list (list[Tensor]): List of ground truth classification\n                    index for each image, each has shape (num_gt,).\n                    NOTE: num_gt is dynamic for each image.\n                    Default: None.\n\n        Returns:\n            tuple: a tuple containing the following targets.\n\n            - all_labels (list[Tensor]): Labels for all images.\n            - all_label_weights (list[Tensor]): Label weights for all images.\n            - all_bbox_targets (list[Tensor]): Bbox targets for all images.\n            - all_assign_metrics (list[Tensor]): Normalized alignment metrics\n                for all images.\n        \"\"\"\n        (all_labels, all_label_weights, all_bbox_targets,\n         all_assign_metrics) = multi_apply(self._get_target_single, cls_scores,\n                                           bbox_preds, gt_bboxes_list,\n                                           gt_labels_list, img_metas)\n\n        return (all_labels, all_label_weights, all_bbox_targets,\n                all_assign_metrics)\n\n    def _get_target_single(self, cls_scores, bbox_preds, gt_bboxes, gt_labels,\n                           img_meta, **kwargs):\n        \"\"\"Compute regression and classification targets for one image.\n\n        Args:\n            cls_scores (Tensor): Predicted normalized classification\n                scores for one image, has shape (num_dense_queries,\n                cls_out_channels).\n            bbox_preds (Tensor): Predicted unnormalized bbox coordinates\n                for one image, has shape (num_dense_queries, 4) with the\n                last dimension arranged as (x1, y1, x2, y2).\n            gt_bboxes (Tensor): Unnormalized ground truth\n                bboxes for one image, has shape (num_gt, 4) with the\n                last dimension arranged as (x1, y1, x2, y2).\n                NOTE: num_gt is dynamic for each image.\n            gt_labels (Tensor): Ground truth classification\n                    index for the image, has shape (num_gt,).\n                    NOTE: num_gt is dynamic for each image.\n            img_meta (dict): Meta information for one image.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - labels (Tensor): Labels for one image.\n            - label_weights (Tensor): Label weights for one image.\n            - bbox_targets (Tensor): Bbox targets for one image.\n            - norm_alignment_metrics (Tensor): Normalized alignment\n                metrics for one image.\n        \"\"\"\n        if len(gt_labels) == 0:\n            num_valid_anchors = len(cls_scores)\n            bbox_targets = torch.zeros_like(bbox_preds)\n            labels = bbox_preds.new_full((num_valid_anchors, ),\n                                         cls_scores.size(-1),\n                                         dtype=torch.long)\n            label_weights = bbox_preds.new_zeros(\n                num_valid_anchors, dtype=torch.float)\n            norm_alignment_metrics = bbox_preds.new_zeros(\n                num_valid_anchors, dtype=torch.float)\n            return (labels, label_weights, bbox_targets,\n                    norm_alignment_metrics)\n\n        assign_result = self.assigner.assign(cls_scores, bbox_preds, gt_bboxes,\n                                             gt_labels, img_meta)\n        assign_ious = assign_result.max_overlaps\n        assign_metrics = assign_result.assign_metrics\n\n        pred_instances = BaseDataElement()\n        gt_instances = BaseDataElement()\n\n        pred_instances.bboxes = bbox_preds\n        gt_instances.bboxes = gt_bboxes\n\n        pred_instances.priors = cls_scores\n        gt_instances.labels = gt_labels\n\n        sampling_result = self.sampler.sample(assign_result, pred_instances,\n                                              gt_instances)\n\n        num_valid_anchors = len(cls_scores)\n        bbox_targets = torch.zeros_like(bbox_preds)\n        labels = bbox_preds.new_full((num_valid_anchors, ),\n                                     cls_scores.size(-1),\n                                     dtype=torch.long)\n        label_weights = bbox_preds.new_zeros(\n            num_valid_anchors, dtype=torch.float)\n        norm_alignment_metrics = bbox_preds.new_zeros(\n            num_valid_anchors, dtype=torch.float)\n\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n        if len(pos_inds) > 0:\n            # point-based\n            pos_bbox_targets = sampling_result.pos_gt_bboxes\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n\n            if gt_labels is None:\n                # Only dense_heads gives gt_labels as None\n                # Foreground is the first class since v2.5.0\n                labels[pos_inds] = 0\n            else:\n                labels[pos_inds] = gt_labels[\n                    sampling_result.pos_assigned_gt_inds]\n\n            label_weights[pos_inds] = 1.0\n\n        if len(neg_inds) > 0:\n            label_weights[neg_inds] = 1.0\n\n        class_assigned_gt_inds = torch.unique(\n            sampling_result.pos_assigned_gt_inds)\n        for gt_inds in class_assigned_gt_inds:\n            gt_class_inds = sampling_result.pos_assigned_gt_inds == gt_inds\n            pos_alignment_metrics = assign_metrics[gt_class_inds]\n            pos_ious = assign_ious[gt_class_inds]\n            pos_norm_alignment_metrics = pos_alignment_metrics / (\n                pos_alignment_metrics.max() + 10e-8) * pos_ious.max()\n            norm_alignment_metrics[\n                pos_inds[gt_class_inds]] = pos_norm_alignment_metrics\n\n        return (labels, label_weights, bbox_targets, norm_alignment_metrics)\n"
  },
  {
    "path": "mmdet/models/losses/dice_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\n\nfrom mmdet.registry import MODELS\nfrom .utils import weight_reduce_loss\n\n\ndef dice_loss(pred,\n              target,\n              weight=None,\n              eps=1e-3,\n              reduction='mean',\n              naive_dice=False,\n              avg_factor=None):\n    \"\"\"Calculate dice loss, there are two forms of dice loss is supported:\n\n        - the one proposed in `V-Net: Fully Convolutional Neural\n            Networks for Volumetric Medical Image Segmentation\n            <https://arxiv.org/abs/1606.04797>`_.\n        - the dice loss in which the power of the number in the\n            denominator is the first power instead of the second\n            power.\n\n    Args:\n        pred (torch.Tensor): The prediction, has a shape (n, *)\n        target (torch.Tensor): The learning label of the prediction,\n            shape (n, *), same shape of pred.\n        weight (torch.Tensor, optional): The weight of loss for each\n            prediction, has a shape (n,). Defaults to None.\n        eps (float): Avoid dividing by zero. Default: 1e-3.\n        reduction (str, optional): The method used to reduce the loss into\n            a scalar. Defaults to 'mean'.\n            Options are \"none\", \"mean\" and \"sum\".\n        naive_dice (bool, optional): If false, use the dice\n                loss defined in the V-Net paper, otherwise, use the\n                naive dice loss in which the power of the number in the\n                denominator is the first power instead of the second\n                power.Defaults to False.\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n    \"\"\"\n\n    input = pred.flatten(1)\n    target = target.flatten(1).float()\n\n    a = torch.sum(input * target, 1)\n    if naive_dice:\n        b = torch.sum(input, 1)\n        c = torch.sum(target, 1)\n        d = (2 * a + eps) / (b + c + eps)\n    else:\n        b = torch.sum(input * input, 1) + eps\n        c = torch.sum(target * target, 1) + eps\n        d = (2 * a) / (b + c)\n\n    loss = 1 - d\n    if weight is not None:\n        assert weight.ndim == loss.ndim\n        assert len(weight) == len(pred)\n    loss = weight_reduce_loss(loss, weight, reduction, avg_factor)\n    return loss\n\n\n@MODELS.register_module()\nclass DiceLoss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid=True,\n                 activate=True,\n                 reduction='mean',\n                 naive_dice=False,\n                 loss_weight=1.0,\n                 eps=1e-3):\n        \"\"\"Compute dice loss.\n\n        Args:\n            use_sigmoid (bool, optional): Whether to the prediction is\n                used for sigmoid or softmax. Defaults to True.\n            activate (bool): Whether to activate the predictions inside,\n                this will disable the inside sigmoid operation.\n                Defaults to True.\n            reduction (str, optional): The method used\n                to reduce the loss. Options are \"none\",\n                \"mean\" and \"sum\". Defaults to 'mean'.\n            naive_dice (bool, optional): If false, use the dice\n                loss defined in the V-Net paper, otherwise, use the\n                naive dice loss in which the power of the number in the\n                denominator is the first power instead of the second\n                power. Defaults to False.\n            loss_weight (float, optional): Weight of loss. Defaults to 1.0.\n            eps (float): Avoid dividing by zero. Defaults to 1e-3.\n        \"\"\"\n\n        super(DiceLoss, self).__init__()\n        self.use_sigmoid = use_sigmoid\n        self.reduction = reduction\n        self.naive_dice = naive_dice\n        self.loss_weight = loss_weight\n        self.eps = eps\n        self.activate = activate\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                reduction_override=None,\n                avg_factor=None):\n        \"\"\"Forward function.\n\n        Args:\n            pred (torch.Tensor): The prediction, has a shape (n, *).\n            target (torch.Tensor): The label of the prediction,\n                shape (n, *), same shape of pred.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction, has a shape (n,). Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            torch.Tensor: The calculated loss\n        \"\"\"\n\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n\n        if self.activate:\n            if self.use_sigmoid:\n                pred = pred.sigmoid()\n            else:\n                raise NotImplementedError\n\n        loss = self.loss_weight * dice_loss(\n            pred,\n            target,\n            weight,\n            eps=self.eps,\n            reduction=reduction,\n            naive_dice=self.naive_dice,\n            avg_factor=avg_factor)\n\n        return loss\n"
  },
  {
    "path": "mmdet/models/losses/eqlv2_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport logging\nfrom functools import partial\nfrom typing import Optional\n\nimport torch\nimport torch.distributed as dist\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.logging import print_log\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass EQLV2Loss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid: bool = True,\n                 reduction: str = 'mean',\n                 class_weight: Optional[Tensor] = None,\n                 loss_weight: float = 1.0,\n                 num_classes: int = 1203,\n                 use_distributed: bool = False,\n                 mu: float = 0.8,\n                 alpha: float = 4.0,\n                 gamma: int = 12,\n                 vis_grad: bool = False,\n                 test_with_obj: bool = True) -> None:\n        \"\"\"`Equalization Loss v2 <https://arxiv.org/abs/2012.08548>`_\n\n        Args:\n            use_sigmoid (bool): EQLv2 uses the sigmoid function to transform\n                the predicted logits to an estimated probability distribution.\n            reduction (str, optional): The method used to reduce the loss into\n                a scalar. Defaults to 'mean'.\n            class_weight (Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            loss_weight (float, optional): The weight of the total EQLv2 loss.\n                Defaults to 1.0.\n            num_classes (int): 1203 for lvis v1.0, 1230 for lvis v0.5.\n            use_distributed (bool, float): EQLv2 will calculate the gradients\n                on all GPUs if there is any. Change to True if you are using\n                distributed training. Default to False.\n            mu (float, optional): Defaults to 0.8\n            alpha (float, optional): A balance factor for the negative part of\n                EQLV2 Loss. Defaults to 4.0.\n            gamma (int, optional): The gamma for calculating the modulating\n                factor. Defaults to 12.\n            vis_grad (bool, optional): Default to False.\n            test_with_obj (bool, optional): Default to True.\n\n        Returns:\n            None.\n        \"\"\"\n        super().__init__()\n        self.use_sigmoid = True\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.class_weight = class_weight\n        self.num_classes = num_classes\n        self.group = True\n\n        # cfg for eqlv2\n        self.vis_grad = vis_grad\n        self.mu = mu\n        self.alpha = alpha\n        self.gamma = gamma\n        self.use_distributed = use_distributed\n\n        # initial variables\n        self.register_buffer('pos_grad', torch.zeros(self.num_classes))\n        self.register_buffer('neg_grad', torch.zeros(self.num_classes))\n        # At the beginning of training, we set a high value (eg. 100)\n        # for the initial gradient ratio so that the weight for pos\n        # gradients and neg gradients are 1.\n        self.register_buffer('pos_neg', torch.ones(self.num_classes) * 100)\n\n        self.test_with_obj = test_with_obj\n\n        def _func(x, gamma, mu):\n            return 1 / (1 + torch.exp(-gamma * (x - mu)))\n\n        self.map_func = partial(_func, gamma=self.gamma, mu=self.mu)\n\n        print_log(\n            f'build EQL v2, gamma: {gamma}, mu: {mu}, alpha: {alpha}',\n            logger='current',\n            level=logging.DEBUG)\n\n    def forward(self,\n                cls_score: Tensor,\n                label: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[Tensor] = None) -> Tensor:\n        \"\"\"`Equalization Loss v2 <https://arxiv.org/abs/2012.08548>`_\n\n        Args:\n            cls_score (Tensor): The prediction with shape (N, C), C is the\n                number of classes.\n            label (Tensor): The ground truth label of the predicted target with\n                shape (N, C), C is the number of classes.\n            weight (Tensor, optional): The weight of loss for each prediction.\n                Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n           Tensor: The calculated loss\n        \"\"\"\n        self.n_i, self.n_c = cls_score.size()\n        self.gt_classes = label\n        self.pred_class_logits = cls_score\n\n        def expand_label(pred, gt_classes):\n            target = pred.new_zeros(self.n_i, self.n_c)\n            target[torch.arange(self.n_i), gt_classes] = 1\n            return target\n\n        target = expand_label(cls_score, label)\n\n        pos_w, neg_w = self.get_weight(cls_score)\n\n        weight = pos_w * target + neg_w * (1 - target)\n\n        cls_loss = F.binary_cross_entropy_with_logits(\n            cls_score, target, reduction='none')\n        cls_loss = torch.sum(cls_loss * weight) / self.n_i\n\n        self.collect_grad(cls_score.detach(), target.detach(), weight.detach())\n\n        return self.loss_weight * cls_loss\n\n    def get_channel_num(self, num_classes):\n        num_channel = num_classes + 1\n        return num_channel\n\n    def get_activation(self, pred):\n        pred = torch.sigmoid(pred)\n        n_i, n_c = pred.size()\n        bg_score = pred[:, -1].view(n_i, 1)\n        if self.test_with_obj:\n            pred[:, :-1] *= (1 - bg_score)\n        return pred\n\n    def collect_grad(self, pred, target, weight):\n        prob = torch.sigmoid(pred)\n        grad = target * (prob - 1) + (1 - target) * prob\n        grad = torch.abs(grad)\n\n        # do not collect grad for objectiveness branch [:-1]\n        pos_grad = torch.sum(grad * target * weight, dim=0)[:-1]\n        neg_grad = torch.sum(grad * (1 - target) * weight, dim=0)[:-1]\n\n        if self.use_distributed:\n            dist.all_reduce(pos_grad)\n            dist.all_reduce(neg_grad)\n\n        self.pos_grad += pos_grad\n        self.neg_grad += neg_grad\n        self.pos_neg = self.pos_grad / (self.neg_grad + 1e-10)\n\n    def get_weight(self, pred):\n        neg_w = torch.cat([self.map_func(self.pos_neg), pred.new_ones(1)])\n        pos_w = 1 + self.alpha * (1 - neg_w)\n        neg_w = neg_w.view(1, -1).expand(self.n_i, self.n_c)\n        pos_w = pos_w.view(1, -1).expand(self.n_i, self.n_c)\n        return pos_w, neg_w\n"
  },
  {
    "path": "mmdet/models/losses/focal_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss\n\nfrom mmdet.registry import MODELS\nfrom .accuracy import accuracy\nfrom .utils import weight_reduce_loss\n\n\n# This method is only for debugging\ndef py_sigmoid_focal_loss(pred,\n                          target,\n                          weight=None,\n                          gamma=2.0,\n                          alpha=0.25,\n                          reduction='mean',\n                          avg_factor=None):\n    \"\"\"PyTorch version of `Focal Loss <https://arxiv.org/abs/1708.02002>`_.\n\n    Args:\n        pred (torch.Tensor): The prediction with shape (N, C), C is the\n            number of classes\n        target (torch.Tensor): The learning label of the prediction.\n        weight (torch.Tensor, optional): Sample-wise loss weight.\n        gamma (float, optional): The gamma for calculating the modulating\n            factor. Defaults to 2.0.\n        alpha (float, optional): A balanced form for Focal Loss.\n            Defaults to 0.25.\n        reduction (str, optional): The method used to reduce the loss into\n            a scalar. Defaults to 'mean'.\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n    \"\"\"\n    pred_sigmoid = pred.sigmoid()\n    target = target.type_as(pred)\n    # Actually, pt here denotes (1 - pt) in the Focal Loss paper\n    pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target)\n    # Thus it's pt.pow(gamma) rather than (1 - pt).pow(gamma)\n    focal_weight = (alpha * target + (1 - alpha) *\n                    (1 - target)) * pt.pow(gamma)\n    loss = F.binary_cross_entropy_with_logits(\n        pred, target, reduction='none') * focal_weight\n    if weight is not None:\n        if weight.shape != loss.shape:\n            if weight.size(0) == loss.size(0):\n                # For most cases, weight is of shape (num_priors, ),\n                #  which means it does not have the second axis num_class\n                weight = weight.view(-1, 1)\n            else:\n                # Sometimes, weight per anchor per class is also needed. e.g.\n                #  in FSAF. But it may be flattened of shape\n                #  (num_priors x num_class, ), while loss is still of shape\n                #  (num_priors, num_class).\n                assert weight.numel() == loss.numel()\n                weight = weight.view(loss.size(0), -1)\n        assert weight.ndim == loss.ndim\n    loss = weight_reduce_loss(loss, weight, reduction, avg_factor)\n    return loss\n\n\ndef py_focal_loss_with_prob(pred,\n                            target,\n                            weight=None,\n                            gamma=2.0,\n                            alpha=0.25,\n                            reduction='mean',\n                            avg_factor=None):\n    \"\"\"PyTorch version of `Focal Loss <https://arxiv.org/abs/1708.02002>`_.\n    Different from `py_sigmoid_focal_loss`, this function accepts probability\n    as input.\n\n    Args:\n        pred (torch.Tensor): The prediction probability with shape (N, C),\n            C is the number of classes.\n        target (torch.Tensor): The learning label of the prediction.\n            The target shape support (N,C) or (N,), (N,C) means one-hot form.\n        weight (torch.Tensor, optional): Sample-wise loss weight.\n        gamma (float, optional): The gamma for calculating the modulating\n            factor. Defaults to 2.0.\n        alpha (float, optional): A balanced form for Focal Loss.\n            Defaults to 0.25.\n        reduction (str, optional): The method used to reduce the loss into\n            a scalar. Defaults to 'mean'.\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n    \"\"\"\n    if pred.dim() != target.dim():\n        num_classes = pred.size(1)\n        target = F.one_hot(target, num_classes=num_classes + 1)\n        target = target[:, :num_classes]\n\n    target = target.type_as(pred)\n    pt = (1 - pred) * target + pred * (1 - target)\n    focal_weight = (alpha * target + (1 - alpha) *\n                    (1 - target)) * pt.pow(gamma)\n    loss = F.binary_cross_entropy(\n        pred, target, reduction='none') * focal_weight\n    if weight is not None:\n        if weight.shape != loss.shape:\n            if weight.size(0) == loss.size(0):\n                # For most cases, weight is of shape (num_priors, ),\n                #  which means it does not have the second axis num_class\n                weight = weight.view(-1, 1)\n            else:\n                # Sometimes, weight per anchor per class is also needed. e.g.\n                #  in FSAF. But it may be flattened of shape\n                #  (num_priors x num_class, ), while loss is still of shape\n                #  (num_priors, num_class).\n                assert weight.numel() == loss.numel()\n                weight = weight.view(loss.size(0), -1)\n        assert weight.ndim == loss.ndim\n    loss = weight_reduce_loss(loss, weight, reduction, avg_factor)\n    return loss\n\n\ndef sigmoid_focal_loss(pred,\n                       target,\n                       weight=None,\n                       gamma=2.0,\n                       alpha=0.25,\n                       reduction='mean',\n                       avg_factor=None):\n    r\"\"\"A wrapper of cuda version `Focal Loss\n    <https://arxiv.org/abs/1708.02002>`_.\n\n    Args:\n        pred (torch.Tensor): The prediction with shape (N, C), C is the number\n            of classes.\n        target (torch.Tensor): The learning label of the prediction.\n        weight (torch.Tensor, optional): Sample-wise loss weight.\n        gamma (float, optional): The gamma for calculating the modulating\n            factor. Defaults to 2.0.\n        alpha (float, optional): A balanced form for Focal Loss.\n            Defaults to 0.25.\n        reduction (str, optional): The method used to reduce the loss into\n            a scalar. Defaults to 'mean'. Options are \"none\", \"mean\" and \"sum\".\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n    \"\"\"\n    # Function.apply does not accept keyword arguments, so the decorator\n    # \"weighted_loss\" is not applicable\n    loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), gamma,\n                               alpha, None, 'none')\n    if weight is not None:\n        if weight.shape != loss.shape:\n            if weight.size(0) == loss.size(0):\n                # For most cases, weight is of shape (num_priors, ),\n                #  which means it does not have the second axis num_class\n                weight = weight.view(-1, 1)\n            else:\n                # Sometimes, weight per anchor per class is also needed. e.g.\n                #  in FSAF. But it may be flattened of shape\n                #  (num_priors x num_class, ), while loss is still of shape\n                #  (num_priors, num_class).\n                assert weight.numel() == loss.numel()\n                weight = weight.view(loss.size(0), -1)\n        assert weight.ndim == loss.ndim\n    loss = weight_reduce_loss(loss, weight, reduction, avg_factor)\n    return loss\n\n\n@MODELS.register_module()\nclass FocalLoss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid=True,\n                 gamma=2.0,\n                 alpha=0.25,\n                 reduction='mean',\n                 loss_weight=1.0,\n                 activated=False):\n        \"\"\"`Focal Loss <https://arxiv.org/abs/1708.02002>`_\n\n        Args:\n            use_sigmoid (bool, optional): Whether to the prediction is\n                used for sigmoid or softmax. Defaults to True.\n            gamma (float, optional): The gamma for calculating the modulating\n                factor. Defaults to 2.0.\n            alpha (float, optional): A balanced form for Focal Loss.\n                Defaults to 0.25.\n            reduction (str, optional): The method used to reduce the loss into\n                a scalar. Defaults to 'mean'. Options are \"none\", \"mean\" and\n                \"sum\".\n            loss_weight (float, optional): Weight of loss. Defaults to 1.0.\n            activated (bool, optional): Whether the input is activated.\n                If True, it means the input has been activated and can be\n                treated as probabilities. Else, it should be treated as logits.\n                Defaults to False.\n        \"\"\"\n        super(FocalLoss, self).__init__()\n        assert use_sigmoid is True, 'Only sigmoid focal loss supported now.'\n        self.use_sigmoid = use_sigmoid\n        self.gamma = gamma\n        self.alpha = alpha\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.activated = activated\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None):\n        \"\"\"Forward function.\n\n        Args:\n            pred (torch.Tensor): The prediction.\n            target (torch.Tensor): The learning label of the prediction.\n                The target shape support (N,C) or (N,), (N,C) means\n                one-hot form.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            torch.Tensor: The calculated loss\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if self.use_sigmoid:\n            if self.activated:\n                calculate_loss_func = py_focal_loss_with_prob\n            else:\n                if pred.dim() == target.dim():\n                    # this means that target is already in One-Hot form.\n                    calculate_loss_func = py_sigmoid_focal_loss\n                elif torch.cuda.is_available() and pred.is_cuda:\n                    calculate_loss_func = sigmoid_focal_loss\n                else:\n                    num_classes = pred.size(1)\n                    target = F.one_hot(target, num_classes=num_classes + 1)\n                    target = target[:, :num_classes]\n                    calculate_loss_func = py_sigmoid_focal_loss\n\n            loss_cls = self.loss_weight * calculate_loss_func(\n                pred,\n                target,\n                weight,\n                gamma=self.gamma,\n                alpha=self.alpha,\n                reduction=reduction,\n                avg_factor=avg_factor)\n\n        else:\n            raise NotImplementedError\n        return loss_cls\n\n\n@MODELS.register_module()\nclass FocalCustomLoss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid=True,\n                 num_classes=-1,\n                 gamma=2.0,\n                 alpha=0.25,\n                 reduction='mean',\n                 loss_weight=1.0,\n                 activated=False):\n        \"\"\"`Focal Loss for V3Det <https://arxiv.org/abs/1708.02002>`_\n\n        Args:\n            use_sigmoid (bool, optional): Whether to the prediction is\n                used for sigmoid or softmax. Defaults to True.\n            num_classes (int): Number of classes to classify.\n            gamma (float, optional): The gamma for calculating the modulating\n                factor. Defaults to 2.0.\n            alpha (float, optional): A balanced form for Focal Loss.\n                Defaults to 0.25.\n            reduction (str, optional): The method used to reduce the loss into\n                a scalar. Defaults to 'mean'. Options are \"none\", \"mean\" and\n                \"sum\".\n            loss_weight (float, optional): Weight of loss. Defaults to 1.0.\n            activated (bool, optional): Whether the input is activated.\n                If True, it means the input has been activated and can be\n                treated as probabilities. Else, it should be treated as logits.\n                Defaults to False.\n        \"\"\"\n        super(FocalCustomLoss, self).__init__()\n        assert use_sigmoid is True, 'Only sigmoid focal loss supported now.'\n        self.use_sigmoid = use_sigmoid\n        self.num_classes = num_classes\n        self.gamma = gamma\n        self.alpha = alpha\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.activated = activated\n\n        assert self.num_classes != -1\n\n        # custom output channels of the classifier\n        self.custom_cls_channels = True\n        # custom activation of cls_score\n        self.custom_activation = True\n        # custom accuracy of the classsifier\n        self.custom_accuracy = True\n\n    def get_cls_channels(self, num_classes):\n        assert num_classes == self.num_classes\n        return num_classes\n\n    def get_activation(self, cls_score):\n\n        fine_cls_score = cls_score[:, :self.num_classes]\n\n        score_classes = fine_cls_score.sigmoid()\n\n        return score_classes\n\n    def get_accuracy(self, cls_score, labels):\n\n        fine_cls_score = cls_score[:, :self.num_classes]\n\n        pos_inds = labels < self.num_classes\n        acc_classes = accuracy(fine_cls_score[pos_inds], labels[pos_inds])\n        acc = dict()\n        acc['acc_classes'] = acc_classes\n        return acc\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None):\n        \"\"\"Forward function.\n\n        Args:\n            pred (torch.Tensor): The prediction.\n            target (torch.Tensor): The learning label of the prediction.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            torch.Tensor: The calculated loss\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if self.use_sigmoid:\n\n            num_classes = pred.size(1)\n            target = F.one_hot(target, num_classes=num_classes + 1)\n            target = target[:, :num_classes]\n            calculate_loss_func = py_sigmoid_focal_loss\n\n            loss_cls = self.loss_weight * calculate_loss_func(\n                pred,\n                target,\n                weight,\n                gamma=self.gamma,\n                alpha=self.alpha,\n                reduction=reduction,\n                avg_factor=avg_factor)\n\n        else:\n            raise NotImplementedError\n        return loss_cls\n"
  },
  {
    "path": "mmdet/models/losses/gaussian_focal_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Union\n\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .utils import weight_reduce_loss, weighted_loss\n\n\n@weighted_loss\ndef gaussian_focal_loss(pred: Tensor,\n                        gaussian_target: Tensor,\n                        alpha: float = 2.0,\n                        gamma: float = 4.0,\n                        pos_weight: float = 1.0,\n                        neg_weight: float = 1.0) -> Tensor:\n    \"\"\"`Focal Loss <https://arxiv.org/abs/1708.02002>`_ for targets in gaussian\n    distribution.\n\n    Args:\n        pred (torch.Tensor): The prediction.\n        gaussian_target (torch.Tensor): The learning target of the prediction\n            in gaussian distribution.\n        alpha (float, optional): A balanced form for Focal Loss.\n            Defaults to 2.0.\n        gamma (float, optional): The gamma for calculating the modulating\n            factor. Defaults to 4.0.\n        pos_weight(float): Positive sample loss weight. Defaults to 1.0.\n        neg_weight(float): Negative sample loss weight. Defaults to 1.0.\n    \"\"\"\n    eps = 1e-12\n    pos_weights = gaussian_target.eq(1)\n    neg_weights = (1 - gaussian_target).pow(gamma)\n    pos_loss = -(pred + eps).log() * (1 - pred).pow(alpha) * pos_weights\n    neg_loss = -(1 - pred + eps).log() * pred.pow(alpha) * neg_weights\n    return pos_weight * pos_loss + neg_weight * neg_loss\n\n\ndef gaussian_focal_loss_with_pos_inds(\n        pred: Tensor,\n        gaussian_target: Tensor,\n        pos_inds: Tensor,\n        pos_labels: Tensor,\n        alpha: float = 2.0,\n        gamma: float = 4.0,\n        pos_weight: float = 1.0,\n        neg_weight: float = 1.0,\n        reduction: str = 'mean',\n        avg_factor: Optional[Union[int, float]] = None) -> Tensor:\n    \"\"\"`Focal Loss <https://arxiv.org/abs/1708.02002>`_ for targets in gaussian\n    distribution.\n\n    Note: The index with a value of 1 in ``gaussian_target`` in the\n    ``gaussian_focal_loss`` function is a positive sample, but in\n    ``gaussian_focal_loss_with_pos_inds`` the positive sample is passed\n    in through the ``pos_inds`` parameter.\n\n    Args:\n        pred (torch.Tensor): The prediction. The shape is (N, num_classes).\n        gaussian_target (torch.Tensor): The learning target of the prediction\n            in gaussian distribution. The shape is (N, num_classes).\n        pos_inds (torch.Tensor): The positive sample index.\n            The shape is (M, ).\n        pos_labels (torch.Tensor): The label corresponding to the positive\n            sample index. The shape is (M, ).\n        alpha (float, optional): A balanced form for Focal Loss.\n            Defaults to 2.0.\n        gamma (float, optional): The gamma for calculating the modulating\n            factor. Defaults to 4.0.\n        pos_weight(float): Positive sample loss weight. Defaults to 1.0.\n        neg_weight(float): Negative sample loss weight. Defaults to 1.0.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n            Defaults to 'mean`.\n        avg_factor (int, float, optional): Average factor that is used to\n            average the loss. Defaults to None.\n    \"\"\"\n    eps = 1e-12\n    neg_weights = (1 - gaussian_target).pow(gamma)\n\n    pos_pred_pix = pred[pos_inds]\n    pos_pred = pos_pred_pix.gather(1, pos_labels.unsqueeze(1))\n    pos_loss = -(pos_pred + eps).log() * (1 - pos_pred).pow(alpha)\n    pos_loss = weight_reduce_loss(pos_loss, None, reduction, avg_factor)\n\n    neg_loss = -(1 - pred + eps).log() * pred.pow(alpha) * neg_weights\n    neg_loss = weight_reduce_loss(neg_loss, None, reduction, avg_factor)\n\n    return pos_weight * pos_loss + neg_weight * neg_loss\n\n\n@MODELS.register_module()\nclass GaussianFocalLoss(nn.Module):\n    \"\"\"GaussianFocalLoss is a variant of focal loss.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/1808.01244>`_\n    Code is modified from `kp_utils.py\n    <https://github.com/princeton-vl/CornerNet/blob/master/models/py_utils/kp_utils.py#L152>`_  # noqa: E501\n    Please notice that the target in GaussianFocalLoss is a gaussian heatmap,\n    not 0/1 binary target.\n\n    Args:\n        alpha (float): Power of prediction.\n        gamma (float): Power of target for negative samples.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Loss weight of current loss.\n        pos_weight(float): Positive sample loss weight. Defaults to 1.0.\n        neg_weight(float): Negative sample loss weight. Defaults to 1.0.\n    \"\"\"\n\n    def __init__(self,\n                 alpha: float = 2.0,\n                 gamma: float = 4.0,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0,\n                 pos_weight: float = 1.0,\n                 neg_weight: float = 1.0) -> None:\n        super().__init__()\n        self.alpha = alpha\n        self.gamma = gamma\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.pos_weight = pos_weight\n        self.neg_weight = neg_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                pos_inds: Optional[Tensor] = None,\n                pos_labels: Optional[Tensor] = None,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[Union[int, float]] = None,\n                reduction_override: Optional[str] = None) -> Tensor:\n        \"\"\"Forward function.\n\n        If you want to manually determine which positions are\n        positive samples, you can set the pos_index and pos_label\n        parameter. Currently, only the CenterNet update version uses\n        the parameter.\n\n        Args:\n            pred (torch.Tensor): The prediction. The shape is (N, num_classes).\n            target (torch.Tensor): The learning target of the prediction\n                in gaussian distribution. The shape is (N, num_classes).\n            pos_inds (torch.Tensor): The positive sample index.\n                Defaults to None.\n            pos_labels (torch.Tensor): The label corresponding to the positive\n                sample index. Defaults to None.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, float, optional): Average factor that is used to\n                average the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if pos_inds is not None:\n            assert pos_labels is not None\n            # Only used by centernet update version\n            loss_reg = self.loss_weight * gaussian_focal_loss_with_pos_inds(\n                pred,\n                target,\n                pos_inds,\n                pos_labels,\n                alpha=self.alpha,\n                gamma=self.gamma,\n                pos_weight=self.pos_weight,\n                neg_weight=self.neg_weight,\n                reduction=reduction,\n                avg_factor=avg_factor)\n        else:\n            loss_reg = self.loss_weight * gaussian_focal_loss(\n                pred,\n                target,\n                weight,\n                alpha=self.alpha,\n                gamma=self.gamma,\n                pos_weight=self.pos_weight,\n                neg_weight=self.neg_weight,\n                reduction=reduction,\n                avg_factor=avg_factor)\n        return loss_reg\n"
  },
  {
    "path": "mmdet/models/losses/gfocal_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom functools import partial\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mmdet.models.losses.utils import weighted_loss\nfrom mmdet.registry import MODELS\n\n\n@weighted_loss\ndef quality_focal_loss(pred, target, beta=2.0):\n    r\"\"\"Quality Focal Loss (QFL) is from `Generalized Focal Loss: Learning\n    Qualified and Distributed Bounding Boxes for Dense Object Detection\n    <https://arxiv.org/abs/2006.04388>`_.\n\n    Args:\n        pred (torch.Tensor): Predicted joint representation of classification\n            and quality (IoU) estimation with shape (N, C), C is the number of\n            classes.\n        target (tuple([torch.Tensor])): Target category label with shape (N,)\n            and target quality label with shape (N,).\n        beta (float): The beta parameter for calculating the modulating factor.\n            Defaults to 2.0.\n\n    Returns:\n        torch.Tensor: Loss tensor with shape (N,).\n    \"\"\"\n    assert len(target) == 2, \"\"\"target for QFL must be a tuple of two elements,\n        including category label and quality label, respectively\"\"\"\n    # label denotes the category id, score denotes the quality score\n    label, score = target\n\n    # negatives are supervised by 0 quality score\n    pred_sigmoid = pred.sigmoid()\n    scale_factor = pred_sigmoid\n    zerolabel = scale_factor.new_zeros(pred.shape)\n    loss = F.binary_cross_entropy_with_logits(\n        pred, zerolabel, reduction='none') * scale_factor.pow(beta)\n\n    # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n    bg_class_ind = pred.size(1)\n    pos = ((label >= 0) & (label < bg_class_ind)).nonzero().squeeze(1)\n    pos_label = label[pos].long()\n    # positives are supervised by bbox quality (IoU) score\n    scale_factor = score[pos] - pred_sigmoid[pos, pos_label]\n    loss[pos, pos_label] = F.binary_cross_entropy_with_logits(\n        pred[pos, pos_label], score[pos],\n        reduction='none') * scale_factor.abs().pow(beta)\n\n    loss = loss.sum(dim=1, keepdim=False)\n    return loss\n\n\n@weighted_loss\ndef quality_focal_loss_tensor_target(pred, target, beta=2.0, activated=False):\n    \"\"\"`QualityFocal Loss <https://arxiv.org/abs/2008.13367>`_\n    Args:\n        pred (torch.Tensor): The prediction with shape (N, C), C is the\n            number of classes\n        target (torch.Tensor): The learning target of the iou-aware\n            classification score with shape (N, C), C is the number of classes.\n        beta (float): The beta parameter for calculating the modulating factor.\n            Defaults to 2.0.\n        activated (bool): Whether the input is activated.\n            If True, it means the input has been activated and can be\n            treated as probabilities. Else, it should be treated as logits.\n            Defaults to False.\n    \"\"\"\n    # pred and target should be of the same size\n    assert pred.size() == target.size()\n    if activated:\n        pred_sigmoid = pred\n        loss_function = F.binary_cross_entropy\n    else:\n        pred_sigmoid = pred.sigmoid()\n        loss_function = F.binary_cross_entropy_with_logits\n\n    scale_factor = pred_sigmoid\n    target = target.type_as(pred)\n\n    zerolabel = scale_factor.new_zeros(pred.shape)\n    loss = loss_function(\n        pred, zerolabel, reduction='none') * scale_factor.pow(beta)\n\n    pos = (target != 0)\n    scale_factor = target[pos] - pred_sigmoid[pos]\n    loss[pos] = loss_function(\n        pred[pos], target[pos],\n        reduction='none') * scale_factor.abs().pow(beta)\n\n    loss = loss.sum(dim=1, keepdim=False)\n    return loss\n\n\n@weighted_loss\ndef quality_focal_loss_with_prob(pred, target, beta=2.0):\n    r\"\"\"Quality Focal Loss (QFL) is from `Generalized Focal Loss: Learning\n    Qualified and Distributed Bounding Boxes for Dense Object Detection\n    <https://arxiv.org/abs/2006.04388>`_.\n    Different from `quality_focal_loss`, this function accepts probability\n    as input.\n\n    Args:\n        pred (torch.Tensor): Predicted joint representation of classification\n            and quality (IoU) estimation with shape (N, C), C is the number of\n            classes.\n        target (tuple([torch.Tensor])): Target category label with shape (N,)\n            and target quality label with shape (N,).\n        beta (float): The beta parameter for calculating the modulating factor.\n            Defaults to 2.0.\n\n    Returns:\n        torch.Tensor: Loss tensor with shape (N,).\n    \"\"\"\n    assert len(target) == 2, \"\"\"target for QFL must be a tuple of two elements,\n        including category label and quality label, respectively\"\"\"\n    # label denotes the category id, score denotes the quality score\n    label, score = target\n\n    # negatives are supervised by 0 quality score\n    pred_sigmoid = pred\n    scale_factor = pred_sigmoid\n    zerolabel = scale_factor.new_zeros(pred.shape)\n    loss = F.binary_cross_entropy(\n        pred, zerolabel, reduction='none') * scale_factor.pow(beta)\n\n    # FG cat_id: [0, num_classes -1], BG cat_id: num_classes\n    bg_class_ind = pred.size(1)\n    pos = ((label >= 0) & (label < bg_class_ind)).nonzero().squeeze(1)\n    pos_label = label[pos].long()\n    # positives are supervised by bbox quality (IoU) score\n    scale_factor = score[pos] - pred_sigmoid[pos, pos_label]\n    loss[pos, pos_label] = F.binary_cross_entropy(\n        pred[pos, pos_label], score[pos],\n        reduction='none') * scale_factor.abs().pow(beta)\n\n    loss = loss.sum(dim=1, keepdim=False)\n    return loss\n\n\n@weighted_loss\ndef distribution_focal_loss(pred, label):\n    r\"\"\"Distribution Focal Loss (DFL) is from `Generalized Focal Loss: Learning\n    Qualified and Distributed Bounding Boxes for Dense Object Detection\n    <https://arxiv.org/abs/2006.04388>`_.\n\n    Args:\n        pred (torch.Tensor): Predicted general distribution of bounding boxes\n            (before softmax) with shape (N, n+1), n is the max value of the\n            integral set `{0, ..., n}` in paper.\n        label (torch.Tensor): Target distance label for bounding boxes with\n            shape (N,).\n\n    Returns:\n        torch.Tensor: Loss tensor with shape (N,).\n    \"\"\"\n    dis_left = label.long()\n    dis_right = dis_left + 1\n    weight_left = dis_right.float() - label\n    weight_right = label - dis_left.float()\n    loss = F.cross_entropy(pred, dis_left, reduction='none') * weight_left \\\n        + F.cross_entropy(pred, dis_right, reduction='none') * weight_right\n    return loss\n\n\n@MODELS.register_module()\nclass QualityFocalLoss(nn.Module):\n    r\"\"\"Quality Focal Loss (QFL) is a variant of `Generalized Focal Loss:\n    Learning Qualified and Distributed Bounding Boxes for Dense Object\n    Detection <https://arxiv.org/abs/2006.04388>`_.\n\n    Args:\n        use_sigmoid (bool): Whether sigmoid operation is conducted in QFL.\n            Defaults to True.\n        beta (float): The beta parameter for calculating the modulating factor.\n            Defaults to 2.0.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Loss weight of current loss.\n        activated (bool, optional): Whether the input is activated.\n            If True, it means the input has been activated and can be\n            treated as probabilities. Else, it should be treated as logits.\n            Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 use_sigmoid=True,\n                 beta=2.0,\n                 reduction='mean',\n                 loss_weight=1.0,\n                 activated=False):\n        super(QualityFocalLoss, self).__init__()\n        assert use_sigmoid is True, 'Only sigmoid in QFL supported now.'\n        self.use_sigmoid = use_sigmoid\n        self.beta = beta\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.activated = activated\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None):\n        \"\"\"Forward function.\n\n        Args:\n            pred (torch.Tensor): Predicted joint representation of\n                classification and quality (IoU) estimation with shape (N, C),\n                C is the number of classes.\n            target (Union(tuple([torch.Tensor]),Torch.Tensor)): The type is\n                tuple, it should be included Target category label with\n                shape (N,) and target quality label with shape (N,).The type\n                is torch.Tensor, the target should be one-hot form with\n                soft weights.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if self.use_sigmoid:\n            if self.activated:\n                calculate_loss_func = quality_focal_loss_with_prob\n            else:\n                calculate_loss_func = quality_focal_loss\n            if isinstance(target, torch.Tensor):\n                # the target shape with (N,C) or (N,C,...), which means\n                # the target is one-hot form with soft weights.\n                calculate_loss_func = partial(\n                    quality_focal_loss_tensor_target, activated=self.activated)\n\n            loss_cls = self.loss_weight * calculate_loss_func(\n                pred,\n                target,\n                weight,\n                beta=self.beta,\n                reduction=reduction,\n                avg_factor=avg_factor)\n        else:\n            raise NotImplementedError\n        return loss_cls\n\n\n@MODELS.register_module()\nclass DistributionFocalLoss(nn.Module):\n    r\"\"\"Distribution Focal Loss (DFL) is a variant of `Generalized Focal Loss:\n    Learning Qualified and Distributed Bounding Boxes for Dense Object\n    Detection <https://arxiv.org/abs/2006.04388>`_.\n\n    Args:\n        reduction (str): Options are `'none'`, `'mean'` and `'sum'`.\n        loss_weight (float): Loss weight of current loss.\n    \"\"\"\n\n    def __init__(self, reduction='mean', loss_weight=1.0):\n        super(DistributionFocalLoss, self).__init__()\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred,\n                target,\n                weight=None,\n                avg_factor=None,\n                reduction_override=None):\n        \"\"\"Forward function.\n\n        Args:\n            pred (torch.Tensor): Predicted general distribution of bounding\n                boxes (before softmax) with shape (N, n+1), n is the max value\n                of the integral set `{0, ..., n}` in paper.\n            target (torch.Tensor): Target distance label for bounding boxes\n                with shape (N,).\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_cls = self.loss_weight * distribution_focal_loss(\n            pred, target, weight, reduction=reduction, avg_factor=avg_factor)\n        return loss_cls\n"
  },
  {
    "path": "mmdet/models/losses/ghm_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mmdet.registry import MODELS\nfrom .utils import weight_reduce_loss\n\n\ndef _expand_onehot_labels(labels, label_weights, label_channels):\n    bin_labels = labels.new_full((labels.size(0), label_channels), 0)\n    inds = torch.nonzero(\n        (labels >= 0) & (labels < label_channels), as_tuple=False).squeeze()\n    if inds.numel() > 0:\n        bin_labels[inds, labels[inds]] = 1\n    bin_label_weights = label_weights.view(-1, 1).expand(\n        label_weights.size(0), label_channels)\n    return bin_labels, bin_label_weights\n\n\n# TODO: code refactoring to make it consistent with other losses\n@MODELS.register_module()\nclass GHMC(nn.Module):\n    \"\"\"GHM Classification Loss.\n\n    Details of the theorem can be viewed in the paper\n    `Gradient Harmonized Single-stage Detector\n    <https://arxiv.org/abs/1811.05181>`_.\n\n    Args:\n        bins (int): Number of the unit regions for distribution calculation.\n        momentum (float): The parameter for moving average.\n        use_sigmoid (bool): Can only be true for BCE based loss now.\n        loss_weight (float): The weight of the total GHM-C loss.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n            Defaults to \"mean\"\n    \"\"\"\n\n    def __init__(self,\n                 bins=10,\n                 momentum=0,\n                 use_sigmoid=True,\n                 loss_weight=1.0,\n                 reduction='mean'):\n        super(GHMC, self).__init__()\n        self.bins = bins\n        self.momentum = momentum\n        edges = torch.arange(bins + 1).float() / bins\n        self.register_buffer('edges', edges)\n        self.edges[-1] += 1e-6\n        if momentum > 0:\n            acc_sum = torch.zeros(bins)\n            self.register_buffer('acc_sum', acc_sum)\n        self.use_sigmoid = use_sigmoid\n        if not self.use_sigmoid:\n            raise NotImplementedError\n        self.loss_weight = loss_weight\n        self.reduction = reduction\n\n    def forward(self,\n                pred,\n                target,\n                label_weight,\n                reduction_override=None,\n                **kwargs):\n        \"\"\"Calculate the GHM-C loss.\n\n        Args:\n            pred (float tensor of size [batch_num, class_num]):\n                The direct prediction of classification fc layer.\n            target (float tensor of size [batch_num, class_num]):\n                Binary class target for each sample.\n            label_weight (float tensor of size [batch_num, class_num]):\n                the value is 1 if the sample is valid and 0 if ignored.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        Returns:\n            The gradient harmonized loss.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        # the target should be binary class label\n        if pred.dim() != target.dim():\n            target, label_weight = _expand_onehot_labels(\n                target, label_weight, pred.size(-1))\n        target, label_weight = target.float(), label_weight.float()\n        edges = self.edges\n        mmt = self.momentum\n        weights = torch.zeros_like(pred)\n\n        # gradient length\n        g = torch.abs(pred.sigmoid().detach() - target)\n\n        valid = label_weight > 0\n        tot = max(valid.float().sum().item(), 1.0)\n        n = 0  # n valid bins\n        for i in range(self.bins):\n            inds = (g >= edges[i]) & (g < edges[i + 1]) & valid\n            num_in_bin = inds.sum().item()\n            if num_in_bin > 0:\n                if mmt > 0:\n                    self.acc_sum[i] = mmt * self.acc_sum[i] \\\n                        + (1 - mmt) * num_in_bin\n                    weights[inds] = tot / self.acc_sum[i]\n                else:\n                    weights[inds] = tot / num_in_bin\n                n += 1\n        if n > 0:\n            weights = weights / n\n\n        loss = F.binary_cross_entropy_with_logits(\n            pred, target, reduction='none')\n        loss = weight_reduce_loss(\n            loss, weights, reduction=reduction, avg_factor=tot)\n        return loss * self.loss_weight\n\n\n# TODO: code refactoring to make it consistent with other losses\n@MODELS.register_module()\nclass GHMR(nn.Module):\n    \"\"\"GHM Regression Loss.\n\n    Details of the theorem can be viewed in the paper\n    `Gradient Harmonized Single-stage Detector\n    <https://arxiv.org/abs/1811.05181>`_.\n\n    Args:\n        mu (float): The parameter for the Authentic Smooth L1 loss.\n        bins (int): Number of the unit regions for distribution calculation.\n        momentum (float): The parameter for moving average.\n        loss_weight (float): The weight of the total GHM-R loss.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n            Defaults to \"mean\"\n    \"\"\"\n\n    def __init__(self,\n                 mu=0.02,\n                 bins=10,\n                 momentum=0,\n                 loss_weight=1.0,\n                 reduction='mean'):\n        super(GHMR, self).__init__()\n        self.mu = mu\n        self.bins = bins\n        edges = torch.arange(bins + 1).float() / bins\n        self.register_buffer('edges', edges)\n        self.edges[-1] = 1e3\n        self.momentum = momentum\n        if momentum > 0:\n            acc_sum = torch.zeros(bins)\n            self.register_buffer('acc_sum', acc_sum)\n        self.loss_weight = loss_weight\n        self.reduction = reduction\n\n    # TODO: support reduction parameter\n    def forward(self,\n                pred,\n                target,\n                label_weight,\n                avg_factor=None,\n                reduction_override=None):\n        \"\"\"Calculate the GHM-R loss.\n\n        Args:\n            pred (float tensor of size [batch_num, 4 (* class_num)]):\n                The prediction of box regression layer. Channel number can be 4\n                or 4 * class_num depending on whether it is class-agnostic.\n            target (float tensor of size [batch_num, 4 (* class_num)]):\n                The target regression values with the same size of pred.\n            label_weight (float tensor of size [batch_num, 4 (* class_num)]):\n                The weight of each sample, 0 if ignored.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        Returns:\n            The gradient harmonized loss.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        mu = self.mu\n        edges = self.edges\n        mmt = self.momentum\n\n        # ASL1 loss\n        diff = pred - target\n        loss = torch.sqrt(diff * diff + mu * mu) - mu\n\n        # gradient length\n        g = torch.abs(diff / torch.sqrt(mu * mu + diff * diff)).detach()\n        weights = torch.zeros_like(g)\n\n        valid = label_weight > 0\n        tot = max(label_weight.float().sum().item(), 1.0)\n        n = 0  # n: valid bins\n        for i in range(self.bins):\n            inds = (g >= edges[i]) & (g < edges[i + 1]) & valid\n            num_in_bin = inds.sum().item()\n            if num_in_bin > 0:\n                n += 1\n                if mmt > 0:\n                    self.acc_sum[i] = mmt * self.acc_sum[i] \\\n                        + (1 - mmt) * num_in_bin\n                    weights[inds] = tot / self.acc_sum[i]\n                else:\n                    weights[inds] = tot / num_in_bin\n        if n > 0:\n            weights /= n\n        loss = weight_reduce_loss(\n            loss, weights, reduction=reduction, avg_factor=tot)\n        return loss * self.loss_weight\n"
  },
  {
    "path": "mmdet/models/losses/iou_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nimport warnings\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom .utils import weighted_loss\n\n\n@weighted_loss\ndef iou_loss(pred: Tensor,\n             target: Tensor,\n             linear: bool = False,\n             mode: str = 'log',\n             eps: float = 1e-6) -> Tensor:\n    \"\"\"IoU loss.\n\n    Computing the IoU loss between a set of predicted bboxes and target bboxes.\n    The loss is calculated as negative log of IoU.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        linear (bool, optional): If True, use linear scale of loss instead of\n            log scale. Default: False.\n        mode (str): Loss scaling mode, including \"linear\", \"square\", and \"log\".\n            Default: 'log'\n        eps (float): Epsilon to avoid log(0).\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n    assert mode in ['linear', 'square', 'log']\n    if linear:\n        mode = 'linear'\n        warnings.warn('DeprecationWarning: Setting \"linear=True\" in '\n                      'iou_loss is deprecated, please use \"mode=`linear`\" '\n                      'instead.')\n    # avoid fp16 overflow\n    if pred.dtype == torch.float16:\n        fp16 = True\n        pred = pred.to(torch.float32)\n    else:\n        fp16 = False\n\n    ious = bbox_overlaps(pred, target, is_aligned=True).clamp(min=eps)\n\n    if fp16:\n        ious = ious.to(torch.float16)\n\n    if mode == 'linear':\n        loss = 1 - ious\n    elif mode == 'square':\n        loss = 1 - ious**2\n    elif mode == 'log':\n        loss = -ious.log()\n    else:\n        raise NotImplementedError\n    return loss\n\n\n@weighted_loss\ndef bounded_iou_loss(pred: Tensor,\n                     target: Tensor,\n                     beta: float = 0.2,\n                     eps: float = 1e-3) -> Tensor:\n    \"\"\"BIoULoss.\n\n    This is an implementation of paper\n    `Improving Object Localization with Fitness NMS and Bounded IoU Loss.\n    <https://arxiv.org/abs/1711.00164>`_.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        beta (float, optional): Beta parameter in smoothl1.\n        eps (float, optional): Epsilon to avoid NaN values.\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n    pred_ctrx = (pred[:, 0] + pred[:, 2]) * 0.5\n    pred_ctry = (pred[:, 1] + pred[:, 3]) * 0.5\n    pred_w = pred[:, 2] - pred[:, 0]\n    pred_h = pred[:, 3] - pred[:, 1]\n    with torch.no_grad():\n        target_ctrx = (target[:, 0] + target[:, 2]) * 0.5\n        target_ctry = (target[:, 1] + target[:, 3]) * 0.5\n        target_w = target[:, 2] - target[:, 0]\n        target_h = target[:, 3] - target[:, 1]\n\n    dx = target_ctrx - pred_ctrx\n    dy = target_ctry - pred_ctry\n\n    loss_dx = 1 - torch.max(\n        (target_w - 2 * dx.abs()) /\n        (target_w + 2 * dx.abs() + eps), torch.zeros_like(dx))\n    loss_dy = 1 - torch.max(\n        (target_h - 2 * dy.abs()) /\n        (target_h + 2 * dy.abs() + eps), torch.zeros_like(dy))\n    loss_dw = 1 - torch.min(target_w / (pred_w + eps), pred_w /\n                            (target_w + eps))\n    loss_dh = 1 - torch.min(target_h / (pred_h + eps), pred_h /\n                            (target_h + eps))\n    # view(..., -1) does not work for empty tensor\n    loss_comb = torch.stack([loss_dx, loss_dy, loss_dw, loss_dh],\n                            dim=-1).flatten(1)\n\n    loss = torch.where(loss_comb < beta, 0.5 * loss_comb * loss_comb / beta,\n                       loss_comb - 0.5 * beta)\n    return loss\n\n\n@weighted_loss\ndef giou_loss(pred: Tensor, target: Tensor, eps: float = 1e-7) -> Tensor:\n    r\"\"\"`Generalized Intersection over Union: A Metric and A Loss for Bounding\n    Box Regression <https://arxiv.org/abs/1902.09630>`_.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        eps (float): Epsilon to avoid log(0).\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n    # avoid fp16 overflow\n    if pred.dtype == torch.float16:\n        fp16 = True\n        pred = pred.to(torch.float32)\n    else:\n        fp16 = False\n\n    gious = bbox_overlaps(pred, target, mode='giou', is_aligned=True, eps=eps)\n\n    if fp16:\n        gious = gious.to(torch.float16)\n\n    loss = 1 - gious\n    return loss\n\n\n@weighted_loss\ndef diou_loss(pred: Tensor, target: Tensor, eps: float = 1e-7) -> Tensor:\n    r\"\"\"Implementation of `Distance-IoU Loss: Faster and Better\n    Learning for Bounding Box Regression https://arxiv.org/abs/1911.08287`_.\n\n    Code is modified from https://github.com/Zzh-tju/DIoU.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        eps (float): Epsilon to avoid log(0).\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n    # overlap\n    lt = torch.max(pred[:, :2], target[:, :2])\n    rb = torch.min(pred[:, 2:], target[:, 2:])\n    wh = (rb - lt).clamp(min=0)\n    overlap = wh[:, 0] * wh[:, 1]\n\n    # union\n    ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])\n    ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])\n    union = ap + ag - overlap + eps\n\n    # IoU\n    ious = overlap / union\n\n    # enclose area\n    enclose_x1y1 = torch.min(pred[:, :2], target[:, :2])\n    enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:])\n    enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0)\n\n    cw = enclose_wh[:, 0]\n    ch = enclose_wh[:, 1]\n\n    c2 = cw**2 + ch**2 + eps\n\n    b1_x1, b1_y1 = pred[:, 0], pred[:, 1]\n    b1_x2, b1_y2 = pred[:, 2], pred[:, 3]\n    b2_x1, b2_y1 = target[:, 0], target[:, 1]\n    b2_x2, b2_y2 = target[:, 2], target[:, 3]\n\n    left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4\n    right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4\n    rho2 = left + right\n\n    # DIoU\n    dious = ious - rho2 / c2\n    loss = 1 - dious\n    return loss\n\n\n@weighted_loss\ndef ciou_loss(pred: Tensor, target: Tensor, eps: float = 1e-7) -> Tensor:\n    r\"\"\"`Implementation of paper `Enhancing Geometric Factors into\n    Model Learning and Inference for Object Detection and Instance\n    Segmentation <https://arxiv.org/abs/2005.03572>`_.\n\n    Code is modified from https://github.com/Zzh-tju/CIoU.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        eps (float): Epsilon to avoid log(0).\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n    # overlap\n    lt = torch.max(pred[:, :2], target[:, :2])\n    rb = torch.min(pred[:, 2:], target[:, 2:])\n    wh = (rb - lt).clamp(min=0)\n    overlap = wh[:, 0] * wh[:, 1]\n\n    # union\n    ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])\n    ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])\n    union = ap + ag - overlap + eps\n\n    # IoU\n    ious = overlap / union\n\n    # enclose area\n    enclose_x1y1 = torch.min(pred[:, :2], target[:, :2])\n    enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:])\n    enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0)\n\n    cw = enclose_wh[:, 0]\n    ch = enclose_wh[:, 1]\n\n    c2 = cw**2 + ch**2 + eps\n\n    b1_x1, b1_y1 = pred[:, 0], pred[:, 1]\n    b1_x2, b1_y2 = pred[:, 2], pred[:, 3]\n    b2_x1, b2_y1 = target[:, 0], target[:, 1]\n    b2_x2, b2_y2 = target[:, 2], target[:, 3]\n\n    w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps\n    w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps\n\n    left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4\n    right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4\n    rho2 = left + right\n\n    factor = 4 / math.pi**2\n    v = factor * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)\n\n    with torch.no_grad():\n        alpha = (ious > 0.5).float() * v / (1 - ious + v)\n\n    # CIoU\n    cious = ious - (rho2 / c2 + alpha * v)\n    loss = 1 - cious.clamp(min=-1.0, max=1.0)\n    return loss\n\n\n@weighted_loss\ndef eiou_loss(pred: Tensor,\n              target: Tensor,\n              smooth_point: float = 0.1,\n              eps: float = 1e-7) -> Tensor:\n    r\"\"\"Implementation of paper `Extended-IoU Loss: A Systematic\n    IoU-Related Method: Beyond Simplified Regression for Better\n    Localization <https://ieeexplore.ieee.org/abstract/document/9429909>`_\n\n    Code is modified from https://github.com//ShiqiYu/libfacedetection.train.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        smooth_point (float): hyperparameter, default is 0.1.\n        eps (float): Epsilon to avoid log(0).\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n    px1, py1, px2, py2 = pred[:, 0], pred[:, 1], pred[:, 2], pred[:, 3]\n    tx1, ty1, tx2, ty2 = target[:, 0], target[:, 1], target[:, 2], target[:, 3]\n\n    # extent top left\n    ex1 = torch.min(px1, tx1)\n    ey1 = torch.min(py1, ty1)\n\n    # intersection coordinates\n    ix1 = torch.max(px1, tx1)\n    iy1 = torch.max(py1, ty1)\n    ix2 = torch.min(px2, tx2)\n    iy2 = torch.min(py2, ty2)\n\n    # extra\n    xmin = torch.min(ix1, ix2)\n    ymin = torch.min(iy1, iy2)\n    xmax = torch.max(ix1, ix2)\n    ymax = torch.max(iy1, iy2)\n\n    # Intersection\n    intersection = (ix2 - ex1) * (iy2 - ey1) + (xmin - ex1) * (ymin - ey1) - (\n        ix1 - ex1) * (ymax - ey1) - (xmax - ex1) * (\n            iy1 - ey1)\n    # Union\n    union = (px2 - px1) * (py2 - py1) + (tx2 - tx1) * (\n        ty2 - ty1) - intersection + eps\n    # IoU\n    ious = 1 - (intersection / union)\n\n    # Smooth-EIoU\n    smooth_sign = (ious < smooth_point).detach().float()\n    loss = 0.5 * smooth_sign * (ious**2) / smooth_point + (1 - smooth_sign) * (\n        ious - 0.5 * smooth_point)\n    return loss\n\n\n@weighted_loss\ndef siou_loss(pred, target, eps=1e-7, neg_gamma=False):\n    r\"\"\"`Implementation of paper `SIoU Loss: More Powerful Learning\n    for Bounding Box Regression <https://arxiv.org/abs/2205.12740>`_.\n\n    Code is modified from https://github.com/meituan/YOLOv6.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        eps (float): Eps to avoid log(0).\n        neg_gamma (bool): `True` follows original implementation in paper.\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n    # overlap\n    lt = torch.max(pred[:, :2], target[:, :2])\n    rb = torch.min(pred[:, 2:], target[:, 2:])\n    wh = (rb - lt).clamp(min=0)\n    overlap = wh[:, 0] * wh[:, 1]\n\n    # union\n    ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])\n    ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])\n    union = ap + ag - overlap + eps\n\n    # IoU\n    ious = overlap / union\n\n    # enclose area\n    enclose_x1y1 = torch.min(pred[:, :2], target[:, :2])\n    enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:])\n    # modified clamp threshold zero to eps to avoid NaN\n    enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=eps)\n\n    cw = enclose_wh[:, 0]\n    ch = enclose_wh[:, 1]\n\n    b1_x1, b1_y1 = pred[:, 0], pred[:, 1]\n    b1_x2, b1_y2 = pred[:, 2], pred[:, 3]\n    b2_x1, b2_y1 = target[:, 0], target[:, 1]\n    b2_x2, b2_y2 = target[:, 2], target[:, 3]\n\n    w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps\n    w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps\n\n    # angle cost\n    s_cw = (b2_x1 + b2_x2 - b1_x1 - b1_x2) * 0.5 + eps\n    s_ch = (b2_y1 + b2_y2 - b1_y1 - b1_y2) * 0.5 + eps\n\n    sigma = torch.pow(s_cw**2 + s_ch**2, 0.5)\n\n    sin_alpha_1 = torch.abs(s_cw) / sigma\n    sin_alpha_2 = torch.abs(s_ch) / sigma\n    threshold = pow(2, 0.5) / 2\n    sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)\n    angle_cost = torch.cos(torch.asin(sin_alpha) * 2 - math.pi / 2)\n\n    # distance cost\n    rho_x = (s_cw / cw)**2\n    rho_y = (s_ch / ch)**2\n\n    # `neg_gamma=True` follows original implementation in paper\n    # but setting `neg_gamma=False` makes training more stable.\n    gamma = angle_cost - 2 if neg_gamma else 2 - angle_cost\n    distance_cost = 2 - torch.exp(gamma * rho_x) - torch.exp(gamma * rho_y)\n\n    # shape cost\n    omiga_w = torch.abs(w1 - w2) / torch.max(w1, w2)\n    omiga_h = torch.abs(h1 - h2) / torch.max(h1, h2)\n    shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(\n        1 - torch.exp(-1 * omiga_h), 4)\n\n    # SIoU\n    sious = ious - 0.5 * (distance_cost + shape_cost)\n    loss = 1 - sious.clamp(min=-1.0, max=1.0)\n    return loss\n\n\n@MODELS.register_module()\nclass IoULoss(nn.Module):\n    \"\"\"IoULoss.\n\n    Computing the IoU loss between a set of predicted bboxes and target bboxes.\n\n    Args:\n        linear (bool): If True, use linear scale of loss else determined\n            by mode. Default: False.\n        eps (float): Epsilon to avoid log(0).\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Weight of loss.\n        mode (str): Loss scaling mode, including \"linear\", \"square\", and \"log\".\n            Default: 'log'\n    \"\"\"\n\n    def __init__(self,\n                 linear: bool = False,\n                 eps: float = 1e-6,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0,\n                 mode: str = 'log') -> None:\n        super().__init__()\n        assert mode in ['linear', 'square', 'log']\n        if linear:\n            mode = 'linear'\n            warnings.warn('DeprecationWarning: Setting \"linear=True\" in '\n                          'IOULoss is deprecated, please use \"mode=`linear`\" '\n                          'instead.')\n        self.mode = mode\n        self.linear = linear\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n                shape (n, 4).\n            target (Tensor): The learning target of the prediction,\n                shape (n, 4).\n            weight (Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None. Options are \"none\", \"mean\" and \"sum\".\n\n        Return:\n            Tensor: Loss tensor.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if (weight is not None) and (not torch.any(weight > 0)) and (\n                reduction != 'none'):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()  # 0\n        if weight is not None and weight.dim() > 1:\n            # TODO: remove this in the future\n            # reduce the weight of shape (n, 4) to (n,) to match the\n            # iou_loss of shape (n,)\n            assert weight.shape == pred.shape\n            weight = weight.mean(-1)\n        loss = self.loss_weight * iou_loss(\n            pred,\n            target,\n            weight,\n            mode=self.mode,\n            eps=self.eps,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss\n\n\n@MODELS.register_module()\nclass BoundedIoULoss(nn.Module):\n    \"\"\"BIoULoss.\n\n    This is an implementation of paper\n    `Improving Object Localization with Fitness NMS and Bounded IoU Loss.\n    <https://arxiv.org/abs/1711.00164>`_.\n\n    Args:\n        beta (float, optional): Beta parameter in smoothl1.\n        eps (float, optional): Epsilon to avoid NaN values.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 beta: float = 0.2,\n                 eps: float = 1e-3,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.beta = beta\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n                shape (n, 4).\n            target (Tensor): The learning target of the prediction,\n                shape (n, 4).\n            weight (Optional[Tensor], optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (Optional[int], optional): Average factor that is used\n                to average the loss. Defaults to None.\n            reduction_override (Optional[str], optional): The reduction method\n                used to override the original reduction method of the loss.\n                Defaults to None. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()  # 0\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss = self.loss_weight * bounded_iou_loss(\n            pred,\n            target,\n            weight,\n            beta=self.beta,\n            eps=self.eps,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss\n\n\n@MODELS.register_module()\nclass GIoULoss(nn.Module):\n    r\"\"\"`Generalized Intersection over Union: A Metric and A Loss for Bounding\n    Box Regression <https://arxiv.org/abs/1902.09630>`_.\n\n    Args:\n        eps (float): Epsilon to avoid log(0).\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 eps: float = 1e-6,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n                shape (n, 4).\n            target (Tensor): The learning target of the prediction,\n                shape (n, 4).\n            weight (Optional[Tensor], optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (Optional[int], optional): Average factor that is used\n                to average the loss. Defaults to None.\n            reduction_override (Optional[str], optional): The reduction method\n                used to override the original reduction method of the loss.\n                Defaults to None. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()  # 0\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if weight is not None and weight.dim() > 1:\n            # TODO: remove this in the future\n            # reduce the weight of shape (n, 4) to (n,) to match the\n            # giou_loss of shape (n,)\n            assert weight.shape == pred.shape\n            weight = weight.mean(-1)\n        loss = self.loss_weight * giou_loss(\n            pred,\n            target,\n            weight,\n            eps=self.eps,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss\n\n\n@MODELS.register_module()\nclass DIoULoss(nn.Module):\n    r\"\"\"Implementation of `Distance-IoU Loss: Faster and Better\n    Learning for Bounding Box Regression https://arxiv.org/abs/1911.08287`_.\n\n    Code is modified from https://github.com/Zzh-tju/DIoU.\n\n    Args:\n        eps (float): Epsilon to avoid log(0).\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 eps: float = 1e-6,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n                shape (n, 4).\n            target (Tensor): The learning target of the prediction,\n                shape (n, 4).\n            weight (Optional[Tensor], optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (Optional[int], optional): Average factor that is used\n                to average the loss. Defaults to None.\n            reduction_override (Optional[str], optional): The reduction method\n                used to override the original reduction method of the loss.\n                Defaults to None. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()  # 0\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if weight is not None and weight.dim() > 1:\n            # TODO: remove this in the future\n            # reduce the weight of shape (n, 4) to (n,) to match the\n            # giou_loss of shape (n,)\n            assert weight.shape == pred.shape\n            weight = weight.mean(-1)\n        loss = self.loss_weight * diou_loss(\n            pred,\n            target,\n            weight,\n            eps=self.eps,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss\n\n\n@MODELS.register_module()\nclass CIoULoss(nn.Module):\n    r\"\"\"`Implementation of paper `Enhancing Geometric Factors into\n    Model Learning and Inference for Object Detection and Instance\n    Segmentation <https://arxiv.org/abs/2005.03572>`_.\n\n    Code is modified from https://github.com/Zzh-tju/CIoU.\n\n    Args:\n        eps (float): Epsilon to avoid log(0).\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 eps: float = 1e-6,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n                shape (n, 4).\n            target (Tensor): The learning target of the prediction,\n                shape (n, 4).\n            weight (Optional[Tensor], optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (Optional[int], optional): Average factor that is used\n                to average the loss. Defaults to None.\n            reduction_override (Optional[str], optional): The reduction method\n                used to override the original reduction method of the loss.\n                Defaults to None. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()  # 0\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if weight is not None and weight.dim() > 1:\n            # TODO: remove this in the future\n            # reduce the weight of shape (n, 4) to (n,) to match the\n            # giou_loss of shape (n,)\n            assert weight.shape == pred.shape\n            weight = weight.mean(-1)\n        loss = self.loss_weight * ciou_loss(\n            pred,\n            target,\n            weight,\n            eps=self.eps,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss\n\n\n@MODELS.register_module()\nclass EIoULoss(nn.Module):\n    r\"\"\"Implementation of paper `Extended-IoU Loss: A Systematic\n    IoU-Related Method: Beyond Simplified Regression for Better\n    Localization <https://ieeexplore.ieee.org/abstract/document/9429909>`_\n\n    Code is modified from https://github.com//ShiqiYu/libfacedetection.train.\n\n    Args:\n        eps (float): Epsilon to avoid log(0).\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Weight of loss.\n        smooth_point (float): hyperparameter, default is 0.1.\n    \"\"\"\n\n    def __init__(self,\n                 eps: float = 1e-6,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0,\n                 smooth_point: float = 0.1) -> None:\n        super().__init__()\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.smooth_point = smooth_point\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n                shape (n, 4).\n            target (Tensor): The learning target of the prediction,\n                shape (n, 4).\n            weight (Optional[Tensor], optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (Optional[int], optional): Average factor that is used\n                to average the loss. Defaults to None.\n            reduction_override (Optional[str], optional): The reduction method\n                used to override the original reduction method of the loss.\n                Defaults to None. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()  # 0\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if weight is not None and weight.dim() > 1:\n            assert weight.shape == pred.shape\n            weight = weight.mean(-1)\n        loss = self.loss_weight * eiou_loss(\n            pred,\n            target,\n            weight,\n            smooth_point=self.smooth_point,\n            eps=self.eps,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss\n\n\n@MODELS.register_module()\nclass SIoULoss(nn.Module):\n    r\"\"\"`Implementation of paper `SIoU Loss: More Powerful Learning\n    for Bounding Box Regression <https://arxiv.org/abs/2205.12740>`_.\n\n    Code is modified from https://github.com/meituan/YOLOv6.\n\n    Args:\n        pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n            shape (n, 4).\n        target (Tensor): Corresponding gt bboxes, shape (n, 4).\n        eps (float): Eps to avoid log(0).\n        neg_gamma (bool): `True` follows original implementation in paper.\n\n    Return:\n        Tensor: Loss tensor.\n    \"\"\"\n\n    def __init__(self,\n                 eps: float = 1e-6,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0,\n                 neg_gamma: bool = False) -> None:\n        super().__init__()\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.neg_gamma = neg_gamma\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),\n                shape (n, 4).\n            target (Tensor): The learning target of the prediction,\n                shape (n, 4).\n            weight (Optional[Tensor], optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (Optional[int], optional): Average factor that is used\n                to average the loss. Defaults to None.\n            reduction_override (Optional[str], optional): The reduction method\n                used to override the original reduction method of the loss.\n                Defaults to None. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()  # 0\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if weight is not None and weight.dim() > 1:\n            # TODO: remove this in the future\n            # reduce the weight of shape (n, 4) to (n,) to match the\n            # giou_loss of shape (n,)\n            assert weight.shape == pred.shape\n            weight = weight.mean(-1)\n        loss = self.loss_weight * siou_loss(\n            pred,\n            target,\n            weight,\n            eps=self.eps,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            neg_gamma=self.neg_gamma,\n            **kwargs)\n        return loss\n"
  },
  {
    "path": "mmdet/models/losses/kd_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .utils import weighted_loss\n\n\n@weighted_loss\ndef knowledge_distillation_kl_div_loss(pred: Tensor,\n                                       soft_label: Tensor,\n                                       T: int,\n                                       detach_target: bool = True) -> Tensor:\n    r\"\"\"Loss function for knowledge distilling using KL divergence.\n\n    Args:\n        pred (Tensor): Predicted logits with shape (N, n + 1).\n        soft_label (Tensor): Target logits with shape (N, N + 1).\n        T (int): Temperature for distillation.\n        detach_target (bool): Remove soft_label from automatic differentiation\n\n    Returns:\n        Tensor: Loss tensor with shape (N,).\n    \"\"\"\n    assert pred.size() == soft_label.size()\n    target = F.softmax(soft_label / T, dim=1)\n    if detach_target:\n        target = target.detach()\n\n    kd_loss = F.kl_div(\n        F.log_softmax(pred / T, dim=1), target, reduction='none').mean(1) * (\n            T * T)\n\n    return kd_loss\n\n\n@MODELS.register_module()\nclass KnowledgeDistillationKLDivLoss(nn.Module):\n    \"\"\"Loss function for knowledge distilling using KL divergence.\n\n    Args:\n        reduction (str): Options are `'none'`, `'mean'` and `'sum'`.\n        loss_weight (float): Loss weight of current loss.\n        T (int): Temperature for distillation.\n    \"\"\"\n\n    def __init__(self,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0,\n                 T: int = 10) -> None:\n        super().__init__()\n        assert T >= 1\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.T = T\n\n    def forward(self,\n                pred: Tensor,\n                soft_label: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): Predicted logits with shape (N, n + 1).\n            soft_label (Tensor): Target logits with shape (N, N + 1).\n            weight (Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n\n        loss_kd = self.loss_weight * knowledge_distillation_kl_div_loss(\n            pred,\n            soft_label,\n            weight,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            T=self.T)\n\n        return loss_kd\n"
  },
  {
    "path": "mmdet/models/losses/l2_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .utils import weighted_loss\n\n\n@weighted_loss\ndef l2_loss(pred: Tensor, target: Tensor) -> Tensor:\n    \"\"\"L2 loss.\n\n    Args:\n        pred (torch.Tensor): The prediction.\n        target (torch.Tensor): The learning target of the prediction.\n\n    Returns:\n        torch.Tensor: Calculated loss\n    \"\"\"\n    assert pred.size() == target.size()\n    loss = torch.abs(pred - target)**2\n    return loss\n\n\n@MODELS.register_module()\nclass L2Loss(BaseModule):\n    \"\"\"L2 loss.\n\n    Args:\n        reduction (str, optional): The method to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float, optional): The weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 neg_pos_ub: int = -1,\n                 pos_margin: float = -1,\n                 neg_margin: float = -1,\n                 hard_mining: bool = False,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0):\n        super(L2Loss, self).__init__()\n        self.neg_pos_ub = neg_pos_ub\n        self.pos_margin = pos_margin\n        self.neg_margin = neg_margin\n        self.hard_mining = hard_mining\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[float] = None,\n                reduction_override: Optional[str] = None) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (torch.Tensor): The prediction.\n            target (torch.Tensor): The learning target of the prediction.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (float, optional): Average factor that is used to\n                average the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        pred, weight, avg_factor = self.update_weight(pred, target, weight,\n                                                      avg_factor)\n        loss_bbox = self.loss_weight * l2_loss(\n            pred, target, weight, reduction=reduction, avg_factor=avg_factor)\n        return loss_bbox\n\n    def update_weight(self, pred: Tensor, target: Tensor, weight: Tensor,\n                      avg_factor: float) -> Tuple[Tensor, Tensor, float]:\n        \"\"\"Update the weight according to targets.\"\"\"\n        if weight is None:\n            weight = target.new_ones(target.size())\n\n        invalid_inds = weight <= 0\n        target[invalid_inds] = -1\n        pos_inds = target == 1\n        neg_inds = target == 0\n\n        if self.pos_margin > 0:\n            pred[pos_inds] -= self.pos_margin\n        if self.neg_margin > 0:\n            pred[neg_inds] -= self.neg_margin\n        pred = torch.clamp(pred, min=0, max=1)\n\n        num_pos = int((target == 1).sum())\n        num_neg = int((target == 0).sum())\n        if self.neg_pos_ub > 0 and num_neg / (num_pos +\n                                              1e-6) > self.neg_pos_ub:\n            num_neg = num_pos * self.neg_pos_ub\n            neg_idx = torch.nonzero(target == 0, as_tuple=False)\n\n            if self.hard_mining:\n                costs = l2_loss(\n                    pred, target, reduction='none')[neg_idx[:, 0],\n                                                    neg_idx[:, 1]].detach()\n                neg_idx = neg_idx[costs.topk(num_neg)[1], :]\n            else:\n                neg_idx = self.random_choice(neg_idx, num_neg)\n\n            new_neg_inds = neg_inds.new_zeros(neg_inds.size()).bool()\n            new_neg_inds[neg_idx[:, 0], neg_idx[:, 1]] = True\n\n            invalid_neg_inds = torch.logical_xor(neg_inds, new_neg_inds)\n            weight[invalid_neg_inds] = 0\n\n        avg_factor = (weight > 0).sum()\n        return pred, weight, avg_factor\n\n    @staticmethod\n    def random_choice(gallery: Union[list, np.ndarray, Tensor],\n                      num: int) -> np.ndarray:\n        \"\"\"Random select some elements from the gallery.\n\n        It seems that Pytorch's implementation is slower than numpy so we use\n        numpy to randperm the indices.\n        \"\"\"\n        assert len(gallery) >= num\n        if isinstance(gallery, list):\n            gallery = np.array(gallery)\n        cands = np.arange(len(gallery))\n        np.random.shuffle(cands)\n        rand_inds = cands[:num]\n        if not isinstance(gallery, np.ndarray):\n            rand_inds = torch.from_numpy(rand_inds).long().to(gallery.device)\n        return gallery[rand_inds]\n"
  },
  {
    "path": "mmdet/models/losses/margin_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .mse_loss import mse_loss\n\n\n@MODELS.register_module()\nclass MarginL2Loss(BaseModule):\n    \"\"\"L2 loss with margin.\n\n    Args:\n        neg_pos_ub (int, optional): The upper bound of negative to positive\n            samples in hard mining. Defaults to -1.\n        pos_margin (float, optional): The similarity margin for positive\n            samples in hard mining. Defaults to -1.\n        neg_margin (float, optional): The similarity margin for negative\n            samples in hard mining. Defaults to -1.\n        hard_mining (bool, optional): Whether to use hard mining. Defaults to\n            False.\n        reduction (str, optional): The method to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\". Defaults to \"mean\".\n        loss_weight (float, optional): The weight of loss. Defaults to 1.0.\n    \"\"\"\n\n    def __init__(self,\n                 neg_pos_ub: int = -1,\n                 pos_margin: float = -1,\n                 neg_margin: float = -1,\n                 hard_mining: bool = False,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0):\n        super(MarginL2Loss, self).__init__()\n        self.neg_pos_ub = neg_pos_ub\n        self.pos_margin = pos_margin\n        self.neg_margin = neg_margin\n        self.hard_mining = hard_mining\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[float] = None,\n                reduction_override: Optional[str] = None) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (torch.Tensor): The prediction.\n            target (torch.Tensor): The learning target of the prediction.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (float, optional): Average factor that is used to\n                average the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        pred, weight, avg_factor = self.update_weight(pred, target, weight,\n                                                      avg_factor)\n        loss_bbox = self.loss_weight * mse_loss(\n            pred,\n            target.float(),\n            weight.float(),\n            reduction=reduction,\n            avg_factor=avg_factor)\n        return loss_bbox\n\n    def update_weight(self, pred: Tensor, target: Tensor, weight: Tensor,\n                      avg_factor: float) -> Tuple[Tensor, Tensor, float]:\n        \"\"\"Update the weight according to targets.\n\n        Args:\n            pred (torch.Tensor): The prediction.\n            target (torch.Tensor): The learning target of the prediction.\n            weight (torch.Tensor): The weight of loss for each prediction.\n            avg_factor (float): Average factor that is used to average the\n                loss.\n\n        Returns:\n            tuple[torch.Tensor]: The updated prediction, weight and average\n            factor.\n        \"\"\"\n        if weight is None:\n            weight = target.new_ones(target.size())\n\n        invalid_inds = weight <= 0\n        target[invalid_inds] = -1\n        pos_inds = target == 1\n        neg_inds = target == 0\n\n        if self.pos_margin > 0:\n            pred[pos_inds] -= self.pos_margin\n        if self.neg_margin > 0:\n            pred[neg_inds] -= self.neg_margin\n        pred = torch.clamp(pred, min=0, max=1)\n\n        num_pos = int((target == 1).sum())\n        num_neg = int((target == 0).sum())\n        if self.neg_pos_ub > 0 and num_neg / (num_pos +\n                                              1e-6) > self.neg_pos_ub:\n            num_neg = num_pos * self.neg_pos_ub\n            neg_idx = torch.nonzero(target == 0, as_tuple=False)\n\n            if self.hard_mining:\n                costs = mse_loss(\n                    pred, target.float(),\n                    reduction='none')[neg_idx[:, 0], neg_idx[:, 1]].detach()\n                neg_idx = neg_idx[costs.topk(num_neg)[1], :]\n            else:\n                neg_idx = self.random_choice(neg_idx, num_neg)\n\n            new_neg_inds = neg_inds.new_zeros(neg_inds.size()).bool()\n            new_neg_inds[neg_idx[:, 0], neg_idx[:, 1]] = True\n\n            invalid_neg_inds = torch.logical_xor(neg_inds, new_neg_inds)\n            weight[invalid_neg_inds] = 0\n\n        avg_factor = (weight > 0).sum()\n        return pred, weight, avg_factor\n\n    @staticmethod\n    def random_choice(gallery: Union[list, np.ndarray, Tensor],\n                      num: int) -> np.ndarray:\n        \"\"\"Random select some elements from the gallery.\n\n        It seems that Pytorch's implementation is slower than numpy so we use\n        numpy to randperm the indices.\n\n        Args:\n            gallery (list | np.ndarray | torch.Tensor): The gallery from\n                which to sample.\n            num (int): The number of elements to sample.\n        \"\"\"\n        assert len(gallery) >= num\n        if isinstance(gallery, list):\n            gallery = np.array(gallery)\n        cands = np.arange(len(gallery))\n        np.random.shuffle(cands)\n        rand_inds = cands[:num]\n        if not isinstance(gallery, np.ndarray):\n            rand_inds = torch.from_numpy(rand_inds).long().to(gallery.device)\n        return gallery[rand_inds]\n"
  },
  {
    "path": "mmdet/models/losses/mse_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .utils import weighted_loss\n\n\n@weighted_loss\ndef mse_loss(pred: Tensor, target: Tensor) -> Tensor:\n    \"\"\"A Wrapper of MSE loss.\n    Args:\n        pred (Tensor): The prediction.\n        target (Tensor): The learning target of the prediction.\n\n    Returns:\n        Tensor: loss Tensor\n    \"\"\"\n    return F.mse_loss(pred, target, reduction='none')\n\n\n@MODELS.register_module()\nclass MSELoss(nn.Module):\n    \"\"\"MSELoss.\n\n    Args:\n        reduction (str, optional): The method that reduces the loss to a\n            scalar. Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float, optional): The weight of the loss. Defaults to 1.0\n    \"\"\"\n\n    def __init__(self,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None) -> Tensor:\n        \"\"\"Forward function of loss.\n\n        Args:\n            pred (Tensor): The prediction.\n            target (Tensor): The learning target of the prediction.\n            weight (Tensor, optional): Weight of the loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n\n        Returns:\n            Tensor: The calculated loss.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss = self.loss_weight * mse_loss(\n            pred, target, weight, reduction=reduction, avg_factor=avg_factor)\n        return loss\n"
  },
  {
    "path": "mmdet/models/losses/multipos_cross_entropy_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .utils import weight_reduce_loss\n\n\n@MODELS.register_module()\nclass MultiPosCrossEntropyLoss(BaseModule):\n    \"\"\"multi-positive targets cross entropy loss.\n\n    Args:\n        reduction (str, optional): The method to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\". Defaults to \"mean\".\n        loss_weight (float, optional): The weight of loss. Defaults to 1.0.\n    \"\"\"\n\n    def __init__(self, reduction: str = 'mean', loss_weight: float = 1.0):\n        super(MultiPosCrossEntropyLoss, self).__init__()\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def multi_pos_cross_entropy(self,\n                                pred: Tensor,\n                                label: Tensor,\n                                weight: Optional[Tensor] = None,\n                                reduction: str = 'mean',\n                                avg_factor: Optional[float] = None) -> Tensor:\n        \"\"\"Multi-positive targets cross entropy loss.\n\n        Args:\n            pred (torch.Tensor): The prediction.\n            label (torch.Tensor): The assigned label of the prediction.\n            weight (torch.Tensor): The element-wise weight.\n            reduction (str): Same as built-in losses of PyTorch.\n            avg_factor (float): Average factor when computing\n                the mean of losses.\n\n        Returns:\n            torch.Tensor: Calculated loss\n        \"\"\"\n\n        pos_inds = (label >= 1)\n        neg_inds = (label == 0)\n        pred_pos = pred * pos_inds.float()\n        pred_neg = pred * neg_inds.float()\n        # use -inf to mask out unwanted elements.\n        pred_pos[neg_inds] = pred_pos[neg_inds] + float('inf')\n        pred_neg[pos_inds] = pred_neg[pos_inds] + float('-inf')\n\n        _pos_expand = torch.repeat_interleave(pred_pos, pred.shape[1], dim=1)\n        _neg_expand = pred_neg.repeat(1, pred.shape[1])\n\n        x = torch.nn.functional.pad((_neg_expand - _pos_expand), (0, 1),\n                                    'constant', 0)\n        loss = torch.logsumexp(x, dim=1)\n\n        # apply weights and do the reduction\n        if weight is not None:\n            weight = weight.float()\n        loss = weight_reduce_loss(\n            loss, weight=weight, reduction=reduction, avg_factor=avg_factor)\n\n        return loss\n\n    def forward(self,\n                cls_score: Tensor,\n                label: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[float] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            cls_score (torch.Tensor): The classification score.\n            label (torch.Tensor): The assigned label of the prediction.\n            weight (torch.Tensor): The element-wise weight.\n            avg_factor (float): Average factor when computing\n                the mean of losses.\n            reduction_override (str): Same as built-in losses of PyTorch.\n\n        Returns:\n            torch.Tensor: Calculated loss\n        \"\"\"\n        assert cls_score.size() == label.size()\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_cls = self.loss_weight * self.multi_pos_cross_entropy(\n            cls_score,\n            label,\n            weight,\n            reduction=reduction,\n            avg_factor=avg_factor)\n        return loss_cls\n"
  },
  {
    "path": "mmdet/models/losses/pisa_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom ..task_modules.coders import BaseBBoxCoder\nfrom ..task_modules.samplers import SamplingResult\n\n\ndef isr_p(cls_score: Tensor,\n          bbox_pred: Tensor,\n          bbox_targets: Tuple[Tensor],\n          rois: Tensor,\n          sampling_results: List[SamplingResult],\n          loss_cls: nn.Module,\n          bbox_coder: BaseBBoxCoder,\n          k: float = 2,\n          bias: float = 0,\n          num_class: int = 80) -> tuple:\n    \"\"\"Importance-based Sample Reweighting (ISR_P), positive part.\n\n    Args:\n        cls_score (Tensor): Predicted classification scores.\n        bbox_pred (Tensor): Predicted bbox deltas.\n        bbox_targets (tuple[Tensor]): A tuple of bbox targets, the are\n            labels, label_weights, bbox_targets, bbox_weights, respectively.\n        rois (Tensor): Anchors (single_stage) in shape (n, 4) or RoIs\n            (two_stage) in shape (n, 5).\n        sampling_results (:obj:`SamplingResult`): Sampling results.\n        loss_cls (:obj:`nn.Module`): Classification loss func of the head.\n        bbox_coder (:obj:`BaseBBoxCoder`): BBox coder of the head.\n        k (float): Power of the non-linear mapping. Defaults to 2.\n        bias (float): Shift of the non-linear mapping. Defaults to 0.\n        num_class (int): Number of classes, defaults to 80.\n\n    Return:\n        tuple([Tensor]): labels, imp_based_label_weights, bbox_targets,\n            bbox_target_weights\n    \"\"\"\n\n    labels, label_weights, bbox_targets, bbox_weights = bbox_targets\n    pos_label_inds = ((labels >= 0) &\n                      (labels < num_class)).nonzero().reshape(-1)\n    pos_labels = labels[pos_label_inds]\n\n    # if no positive samples, return the original targets\n    num_pos = float(pos_label_inds.size(0))\n    if num_pos == 0:\n        return labels, label_weights, bbox_targets, bbox_weights\n\n    # merge pos_assigned_gt_inds of per image to a single tensor\n    gts = list()\n    last_max_gt = 0\n    for i in range(len(sampling_results)):\n        gt_i = sampling_results[i].pos_assigned_gt_inds\n        gts.append(gt_i + last_max_gt)\n        if len(gt_i) != 0:\n            last_max_gt = gt_i.max() + 1\n    gts = torch.cat(gts)\n    assert len(gts) == num_pos\n\n    cls_score = cls_score.detach()\n    bbox_pred = bbox_pred.detach()\n\n    # For single stage detectors, rois here indicate anchors, in shape (N, 4)\n    # For two stage detectors, rois are in shape (N, 5)\n    if rois.size(-1) == 5:\n        pos_rois = rois[pos_label_inds][:, 1:]\n    else:\n        pos_rois = rois[pos_label_inds]\n\n    if bbox_pred.size(-1) > 4:\n        bbox_pred = bbox_pred.view(bbox_pred.size(0), -1, 4)\n        pos_delta_pred = bbox_pred[pos_label_inds, pos_labels].view(-1, 4)\n    else:\n        pos_delta_pred = bbox_pred[pos_label_inds].view(-1, 4)\n\n    # compute iou of the predicted bbox and the corresponding GT\n    pos_delta_target = bbox_targets[pos_label_inds].view(-1, 4)\n    pos_bbox_pred = bbox_coder.decode(pos_rois, pos_delta_pred)\n    target_bbox_pred = bbox_coder.decode(pos_rois, pos_delta_target)\n    ious = bbox_overlaps(pos_bbox_pred, target_bbox_pred, is_aligned=True)\n\n    pos_imp_weights = label_weights[pos_label_inds]\n    # Two steps to compute IoU-HLR. Samples are first sorted by IoU locally,\n    # then sorted again within the same-rank group\n    max_l_num = pos_labels.bincount().max()\n    for label in pos_labels.unique():\n        l_inds = (pos_labels == label).nonzero().view(-1)\n        l_gts = gts[l_inds]\n        for t in l_gts.unique():\n            t_inds = l_inds[l_gts == t]\n            t_ious = ious[t_inds]\n            _, t_iou_rank_idx = t_ious.sort(descending=True)\n            _, t_iou_rank = t_iou_rank_idx.sort()\n            ious[t_inds] += max_l_num - t_iou_rank.float()\n        l_ious = ious[l_inds]\n        _, l_iou_rank_idx = l_ious.sort(descending=True)\n        _, l_iou_rank = l_iou_rank_idx.sort()  # IoU-HLR\n        # linearly map HLR to label weights\n        pos_imp_weights[l_inds] *= (max_l_num - l_iou_rank.float()) / max_l_num\n\n    pos_imp_weights = (bias + pos_imp_weights * (1 - bias)).pow(k)\n\n    # normalize to make the new weighted loss value equal to the original loss\n    pos_loss_cls = loss_cls(\n        cls_score[pos_label_inds], pos_labels, reduction_override='none')\n    if pos_loss_cls.dim() > 1:\n        ori_pos_loss_cls = pos_loss_cls * label_weights[pos_label_inds][:,\n                                                                        None]\n        new_pos_loss_cls = pos_loss_cls * pos_imp_weights[:, None]\n    else:\n        ori_pos_loss_cls = pos_loss_cls * label_weights[pos_label_inds]\n        new_pos_loss_cls = pos_loss_cls * pos_imp_weights\n    pos_loss_cls_ratio = ori_pos_loss_cls.sum() / new_pos_loss_cls.sum()\n    pos_imp_weights = pos_imp_weights * pos_loss_cls_ratio\n    label_weights[pos_label_inds] = pos_imp_weights\n\n    bbox_targets = labels, label_weights, bbox_targets, bbox_weights\n    return bbox_targets\n\n\ndef carl_loss(cls_score: Tensor,\n              labels: Tensor,\n              bbox_pred: Tensor,\n              bbox_targets: Tensor,\n              loss_bbox: nn.Module,\n              k: float = 1,\n              bias: float = 0.2,\n              avg_factor: Optional[int] = None,\n              sigmoid: bool = False,\n              num_class: int = 80) -> dict:\n    \"\"\"Classification-Aware Regression Loss (CARL).\n\n    Args:\n        cls_score (Tensor): Predicted classification scores.\n        labels (Tensor): Targets of classification.\n        bbox_pred (Tensor): Predicted bbox deltas.\n        bbox_targets (Tensor): Target of bbox regression.\n        loss_bbox (func): Regression loss func of the head.\n        bbox_coder (obj): BBox coder of the head.\n        k (float): Power of the non-linear mapping. Defaults to 1.\n        bias (float): Shift of the non-linear mapping. Defaults to 0.2.\n        avg_factor (int, optional): Average factor used in regression loss.\n        sigmoid (bool): Activation of the classification score.\n        num_class (int): Number of classes, defaults to 80.\n\n    Return:\n        dict: CARL loss dict.\n    \"\"\"\n    pos_label_inds = ((labels >= 0) &\n                      (labels < num_class)).nonzero().reshape(-1)\n    if pos_label_inds.numel() == 0:\n        return dict(loss_carl=cls_score.sum()[None] * 0.)\n    pos_labels = labels[pos_label_inds]\n\n    # multiply pos_cls_score with the corresponding bbox weight\n    # and remain gradient\n    if sigmoid:\n        pos_cls_score = cls_score.sigmoid()[pos_label_inds, pos_labels]\n    else:\n        pos_cls_score = cls_score.softmax(-1)[pos_label_inds, pos_labels]\n    carl_loss_weights = (bias + (1 - bias) * pos_cls_score).pow(k)\n\n    # normalize carl_loss_weight to make its sum equal to num positive\n    num_pos = float(pos_cls_score.size(0))\n    weight_ratio = num_pos / carl_loss_weights.sum()\n    carl_loss_weights *= weight_ratio\n\n    if avg_factor is None:\n        avg_factor = bbox_targets.size(0)\n    # if is class agnostic, bbox pred is in shape (N, 4)\n    # otherwise, bbox pred is in shape (N, #classes, 4)\n    if bbox_pred.size(-1) > 4:\n        bbox_pred = bbox_pred.view(bbox_pred.size(0), -1, 4)\n        pos_bbox_preds = bbox_pred[pos_label_inds, pos_labels]\n    else:\n        pos_bbox_preds = bbox_pred[pos_label_inds]\n    ori_loss_reg = loss_bbox(\n        pos_bbox_preds,\n        bbox_targets[pos_label_inds],\n        reduction_override='none') / avg_factor\n    loss_carl = (ori_loss_reg * carl_loss_weights[:, None]).sum()\n    return dict(loss_carl=loss_carl[None])\n"
  },
  {
    "path": "mmdet/models/losses/seesaw_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .accuracy import accuracy\nfrom .cross_entropy_loss import cross_entropy\nfrom .utils import weight_reduce_loss\n\n\ndef seesaw_ce_loss(cls_score: Tensor,\n                   labels: Tensor,\n                   label_weights: Tensor,\n                   cum_samples: Tensor,\n                   num_classes: int,\n                   p: float,\n                   q: float,\n                   eps: float,\n                   reduction: str = 'mean',\n                   avg_factor: Optional[int] = None) -> Tensor:\n    \"\"\"Calculate the Seesaw CrossEntropy loss.\n\n    Args:\n        cls_score (Tensor): The prediction with shape (N, C),\n             C is the number of classes.\n        labels (Tensor): The learning label of the prediction.\n        label_weights (Tensor): Sample-wise loss weight.\n        cum_samples (Tensor): Cumulative samples for each category.\n        num_classes (int): The number of classes.\n        p (float): The ``p`` in the mitigation factor.\n        q (float): The ``q`` in the compenstation factor.\n        eps (float): The minimal value of divisor to smooth\n             the computation of compensation factor\n        reduction (str, optional): The method used to reduce the loss.\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n\n    Returns:\n        Tensor: The calculated loss\n    \"\"\"\n    assert cls_score.size(-1) == num_classes\n    assert len(cum_samples) == num_classes\n\n    onehot_labels = F.one_hot(labels, num_classes)\n    seesaw_weights = cls_score.new_ones(onehot_labels.size())\n\n    # mitigation factor\n    if p > 0:\n        sample_ratio_matrix = cum_samples[None, :].clamp(\n            min=1) / cum_samples[:, None].clamp(min=1)\n        index = (sample_ratio_matrix < 1.0).float()\n        sample_weights = sample_ratio_matrix.pow(p) * index + (1 - index)\n        mitigation_factor = sample_weights[labels.long(), :]\n        seesaw_weights = seesaw_weights * mitigation_factor\n\n    # compensation factor\n    if q > 0:\n        scores = F.softmax(cls_score.detach(), dim=1)\n        self_scores = scores[\n            torch.arange(0, len(scores)).to(scores.device).long(),\n            labels.long()]\n        score_matrix = scores / self_scores[:, None].clamp(min=eps)\n        index = (score_matrix > 1.0).float()\n        compensation_factor = score_matrix.pow(q) * index + (1 - index)\n        seesaw_weights = seesaw_weights * compensation_factor\n\n    cls_score = cls_score + (seesaw_weights.log() * (1 - onehot_labels))\n\n    loss = F.cross_entropy(cls_score, labels, weight=None, reduction='none')\n\n    if label_weights is not None:\n        label_weights = label_weights.float()\n    loss = weight_reduce_loss(\n        loss, weight=label_weights, reduction=reduction, avg_factor=avg_factor)\n    return loss\n\n\n@MODELS.register_module()\nclass SeesawLoss(nn.Module):\n    \"\"\"\n    Seesaw Loss for Long-Tailed Instance Segmentation (CVPR 2021)\n    arXiv: https://arxiv.org/abs/2008.10032\n\n    Args:\n        use_sigmoid (bool, optional): Whether the prediction uses sigmoid\n             of softmax. Only False is supported.\n        p (float, optional): The ``p`` in the mitigation factor.\n             Defaults to 0.8.\n        q (float, optional): The ``q`` in the compenstation factor.\n             Defaults to 2.0.\n        num_classes (int, optional): The number of classes.\n             Default to 1203 for LVIS v1 dataset.\n        eps (float, optional): The minimal value of divisor to smooth\n             the computation of compensation factor\n        reduction (str, optional): The method that reduces the loss to a\n             scalar. Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float, optional): The weight of the loss. Defaults to 1.0\n        return_dict (bool, optional): Whether return the losses as a dict.\n             Default to True.\n    \"\"\"\n\n    def __init__(self,\n                 use_sigmoid: bool = False,\n                 p: float = 0.8,\n                 q: float = 2.0,\n                 num_classes: int = 1203,\n                 eps: float = 1e-2,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0,\n                 return_dict: bool = True) -> None:\n        super().__init__()\n        assert not use_sigmoid\n        self.use_sigmoid = False\n        self.p = p\n        self.q = q\n        self.num_classes = num_classes\n        self.eps = eps\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n        self.return_dict = return_dict\n\n        # 0 for pos, 1 for neg\n        self.cls_criterion = seesaw_ce_loss\n\n        # cumulative samples for each category\n        self.register_buffer(\n            'cum_samples',\n            torch.zeros(self.num_classes + 1, dtype=torch.float))\n\n        # custom output channels of the classifier\n        self.custom_cls_channels = True\n        # custom activation of cls_score\n        self.custom_activation = True\n        # custom accuracy of the classsifier\n        self.custom_accuracy = True\n\n    def _split_cls_score(self, cls_score: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"split cls_score.\n\n        Args:\n            cls_score (Tensor): The prediction with shape (N, C + 2).\n\n        Returns:\n            Tuple[Tensor, Tensor]: The score for classes and objectness,\n                 respectively\n        \"\"\"\n        # split cls_score to cls_score_classes and cls_score_objectness\n        assert cls_score.size(-1) == self.num_classes + 2\n        cls_score_classes = cls_score[..., :-2]\n        cls_score_objectness = cls_score[..., -2:]\n        return cls_score_classes, cls_score_objectness\n\n    def get_cls_channels(self, num_classes: int) -> int:\n        \"\"\"Get custom classification channels.\n\n        Args:\n            num_classes (int): The number of classes.\n\n        Returns:\n            int: The custom classification channels.\n        \"\"\"\n        assert num_classes == self.num_classes\n        return num_classes + 2\n\n    def get_activation(self, cls_score: Tensor) -> Tensor:\n        \"\"\"Get custom activation of cls_score.\n\n        Args:\n            cls_score (Tensor): The prediction with shape (N, C + 2).\n\n        Returns:\n            Tensor: The custom activation of cls_score with shape\n                 (N, C + 1).\n        \"\"\"\n        cls_score_classes, cls_score_objectness = self._split_cls_score(\n            cls_score)\n        score_classes = F.softmax(cls_score_classes, dim=-1)\n        score_objectness = F.softmax(cls_score_objectness, dim=-1)\n        score_pos = score_objectness[..., [0]]\n        score_neg = score_objectness[..., [1]]\n        score_classes = score_classes * score_pos\n        scores = torch.cat([score_classes, score_neg], dim=-1)\n        return scores\n\n    def get_accuracy(self, cls_score: Tensor,\n                     labels: Tensor) -> Dict[str, Tensor]:\n        \"\"\"Get custom accuracy w.r.t. cls_score and labels.\n\n        Args:\n            cls_score (Tensor): The prediction with shape (N, C + 2).\n            labels (Tensor): The learning label of the prediction.\n\n        Returns:\n            Dict [str, Tensor]: The accuracy for objectness and classes,\n                 respectively.\n        \"\"\"\n        pos_inds = labels < self.num_classes\n        obj_labels = (labels == self.num_classes).long()\n        cls_score_classes, cls_score_objectness = self._split_cls_score(\n            cls_score)\n        acc_objectness = accuracy(cls_score_objectness, obj_labels)\n        acc_classes = accuracy(cls_score_classes[pos_inds], labels[pos_inds])\n        acc = dict()\n        acc['acc_objectness'] = acc_objectness\n        acc['acc_classes'] = acc_classes\n        return acc\n\n    def forward(\n        self,\n        cls_score: Tensor,\n        labels: Tensor,\n        label_weights: Optional[Tensor] = None,\n        avg_factor: Optional[int] = None,\n        reduction_override: Optional[str] = None\n    ) -> Union[Tensor, Dict[str, Tensor]]:\n        \"\"\"Forward function.\n\n        Args:\n            cls_score (Tensor): The prediction with shape (N, C + 2).\n            labels (Tensor): The learning label of the prediction.\n            label_weights (Tensor, optional): Sample-wise loss weight.\n            avg_factor (int, optional): Average factor that is used to average\n                 the loss. Defaults to None.\n            reduction (str, optional): The method used to reduce the loss.\n                 Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor | Dict [str, Tensor]:\n                 if return_dict == False: The calculated loss |\n                 if return_dict == True: The dict of calculated losses\n                 for objectness and classes, respectively.\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        assert cls_score.size(-1) == self.num_classes + 2\n        pos_inds = labels < self.num_classes\n        # 0 for pos, 1 for neg\n        obj_labels = (labels == self.num_classes).long()\n\n        # accumulate the samples for each category\n        unique_labels = labels.unique()\n        for u_l in unique_labels:\n            inds_ = labels == u_l.item()\n            self.cum_samples[u_l] += inds_.sum()\n\n        if label_weights is not None:\n            label_weights = label_weights.float()\n        else:\n            label_weights = labels.new_ones(labels.size(), dtype=torch.float)\n\n        cls_score_classes, cls_score_objectness = self._split_cls_score(\n            cls_score)\n        # calculate loss_cls_classes (only need pos samples)\n        if pos_inds.sum() > 0:\n            loss_cls_classes = self.loss_weight * self.cls_criterion(\n                cls_score_classes[pos_inds], labels[pos_inds],\n                label_weights[pos_inds], self.cum_samples[:self.num_classes],\n                self.num_classes, self.p, self.q, self.eps, reduction,\n                avg_factor)\n        else:\n            loss_cls_classes = cls_score_classes[pos_inds].sum()\n        # calculate loss_cls_objectness\n        loss_cls_objectness = self.loss_weight * cross_entropy(\n            cls_score_objectness, obj_labels, label_weights, reduction,\n            avg_factor)\n\n        if self.return_dict:\n            loss_cls = dict()\n            loss_cls['loss_cls_objectness'] = loss_cls_objectness\n            loss_cls['loss_cls_classes'] = loss_cls_classes\n        else:\n            loss_cls = loss_cls_classes + loss_cls_objectness\n        return loss_cls\n"
  },
  {
    "path": "mmdet/models/losses/smooth_l1_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .utils import weighted_loss\n\n\n@weighted_loss\ndef smooth_l1_loss(pred: Tensor, target: Tensor, beta: float = 1.0) -> Tensor:\n    \"\"\"Smooth L1 loss.\n\n    Args:\n        pred (Tensor): The prediction.\n        target (Tensor): The learning target of the prediction.\n        beta (float, optional): The threshold in the piecewise function.\n            Defaults to 1.0.\n\n    Returns:\n        Tensor: Calculated loss\n    \"\"\"\n    assert beta > 0\n    if target.numel() == 0:\n        return pred.sum() * 0\n\n    assert pred.size() == target.size()\n    diff = torch.abs(pred - target)\n    loss = torch.where(diff < beta, 0.5 * diff * diff / beta,\n                       diff - 0.5 * beta)\n    return loss\n\n\n@weighted_loss\ndef l1_loss(pred: Tensor, target: Tensor) -> Tensor:\n    \"\"\"L1 loss.\n\n    Args:\n        pred (Tensor): The prediction.\n        target (Tensor): The learning target of the prediction.\n\n    Returns:\n        Tensor: Calculated loss\n    \"\"\"\n    if target.numel() == 0:\n        return pred.sum() * 0\n\n    assert pred.size() == target.size()\n    loss = torch.abs(pred - target)\n    return loss\n\n\n@MODELS.register_module()\nclass SmoothL1Loss(nn.Module):\n    \"\"\"Smooth L1 loss.\n\n    Args:\n        beta (float, optional): The threshold in the piecewise function.\n            Defaults to 1.0.\n        reduction (str, optional): The method to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\". Defaults to \"mean\".\n        loss_weight (float, optional): The weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 beta: float = 1.0,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.beta = beta\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): The prediction.\n            target (Tensor): The learning target of the prediction.\n            weight (Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n\n        Returns:\n            Tensor: Calculated loss\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_bbox = self.loss_weight * smooth_l1_loss(\n            pred,\n            target,\n            weight,\n            beta=self.beta,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss_bbox\n\n\n@MODELS.register_module()\nclass L1Loss(nn.Module):\n    \"\"\"L1 loss.\n\n    Args:\n        reduction (str, optional): The method to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float, optional): The weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): The prediction.\n            target (Tensor): The learning target of the prediction.\n            weight (Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n\n        Returns:\n            Tensor: Calculated loss\n        \"\"\"\n        if weight is not None and not torch.any(weight > 0):\n            if pred.dim() == weight.dim() + 1:\n                weight = weight.unsqueeze(1)\n            return (pred * weight).sum()\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_bbox = self.loss_weight * l1_loss(\n            pred, target, weight, reduction=reduction, avg_factor=avg_factor)\n        return loss_bbox\n"
  },
  {
    "path": "mmdet/models/losses/triplet_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass TripletLoss(BaseModule):\n    \"\"\"Triplet loss with hard positive/negative mining.\n\n    Reference:\n        Hermans et al. In Defense of the Triplet Loss for\n            Person Re-Identification. arXiv:1703.07737.\n    Imported from `<https://github.com/KaiyangZhou/deep-person-reid/blob/\n        master/torchreid/losses/hard_mine_triplet_loss.py>`_.\n    Args:\n        margin (float, optional): Margin for triplet loss. Defaults to 0.3.\n        loss_weight (float, optional): Weight of the loss. Defaults to 1.0.\n        hard_mining (bool, optional): Whether to perform hard mining.\n            Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 margin: float = 0.3,\n                 loss_weight: float = 1.0,\n                 hard_mining=True):\n        super(TripletLoss, self).__init__()\n        self.margin = margin\n        self.ranking_loss = nn.MarginRankingLoss(margin=margin)\n        self.loss_weight = loss_weight\n        self.hard_mining = hard_mining\n\n    def hard_mining_triplet_loss_forward(\n            self, inputs: torch.Tensor,\n            targets: torch.LongTensor) -> torch.Tensor:\n        \"\"\"\n        Args:\n            inputs (torch.Tensor): feature matrix with shape\n                (batch_size, feat_dim).\n            targets (torch.LongTensor): ground truth labels with shape\n                (batch_size).\n\n        Returns:\n            torch.Tensor: triplet loss with hard mining.\n        \"\"\"\n\n        batch_size = inputs.size(0)\n\n        # Compute Euclidean distance\n        dist = torch.pow(inputs, 2).sum(\n            dim=1, keepdim=True).expand(batch_size, batch_size)\n        dist = dist + dist.t()\n        dist.addmm_(inputs, inputs.t(), beta=1, alpha=-2)\n        dist = dist.clamp(min=1e-12).sqrt()  # for numerical stability\n\n        # For each anchor, find the furthest positive sample\n        # and nearest negative sample in the embedding space\n        mask = targets.expand(batch_size, batch_size).eq(\n            targets.expand(batch_size, batch_size).t())\n        dist_ap, dist_an = [], []\n        for i in range(batch_size):\n            dist_ap.append(dist[i][mask[i]].max().unsqueeze(0))\n            dist_an.append(dist[i][mask[i] == 0].min().unsqueeze(0))\n        dist_ap = torch.cat(dist_ap)\n        dist_an = torch.cat(dist_an)\n\n        # Compute ranking hinge loss\n        y = torch.ones_like(dist_an)\n        return self.loss_weight * self.ranking_loss(dist_an, dist_ap, y)\n\n    def forward(self, inputs: torch.Tensor,\n                targets: torch.LongTensor) -> torch.Tensor:\n        \"\"\"\n        Args:\n            inputs (torch.Tensor): feature matrix with shape\n                (batch_size, feat_dim).\n            targets (torch.LongTensor): ground truth labels with shape\n                (num_classes).\n\n        Returns:\n            torch.Tensor: triplet loss.\n        \"\"\"\n        if self.hard_mining:\n            return self.hard_mining_triplet_loss_forward(inputs, targets)\n        else:\n            raise NotImplementedError()\n"
  },
  {
    "path": "mmdet/models/losses/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport functools\nfrom typing import Callable, Optional\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor\n\n\ndef reduce_loss(loss: Tensor, reduction: str) -> Tensor:\n    \"\"\"Reduce loss as specified.\n\n    Args:\n        loss (Tensor): Elementwise loss tensor.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n\n    Return:\n        Tensor: Reduced loss tensor.\n    \"\"\"\n    reduction_enum = F._Reduction.get_enum(reduction)\n    # none: 0, elementwise_mean:1, sum: 2\n    if reduction_enum == 0:\n        return loss\n    elif reduction_enum == 1:\n        return loss.mean()\n    elif reduction_enum == 2:\n        return loss.sum()\n\n\ndef weight_reduce_loss(loss: Tensor,\n                       weight: Optional[Tensor] = None,\n                       reduction: str = 'mean',\n                       avg_factor: Optional[float] = None) -> Tensor:\n    \"\"\"Apply element-wise weight and reduce loss.\n\n    Args:\n        loss (Tensor): Element-wise loss.\n        weight (Optional[Tensor], optional): Element-wise weights.\n            Defaults to None.\n        reduction (str, optional): Same as built-in losses of PyTorch.\n            Defaults to 'mean'.\n        avg_factor (Optional[float], optional): Average factor when\n            computing the mean of losses. Defaults to None.\n\n    Returns:\n        Tensor: Processed loss values.\n    \"\"\"\n    # if weight is specified, apply element-wise weight\n    if weight is not None:\n        loss = loss * weight\n\n    # if avg_factor is not specified, just reduce the loss\n    if avg_factor is None:\n        loss = reduce_loss(loss, reduction)\n    else:\n        # if reduction is mean, then average the loss by avg_factor\n        if reduction == 'mean':\n            # Avoid causing ZeroDivisionError when avg_factor is 0.0,\n            # i.e., all labels of an image belong to ignore index.\n            eps = torch.finfo(torch.float32).eps\n            loss = loss.sum() / (avg_factor + eps)\n        # if reduction is 'none', then do nothing, otherwise raise an error\n        elif reduction != 'none':\n            raise ValueError('avg_factor can not be used with reduction=\"sum\"')\n    return loss\n\n\ndef weighted_loss(loss_func: Callable) -> Callable:\n    \"\"\"Create a weighted version of a given loss function.\n\n    To use this decorator, the loss function must have the signature like\n    `loss_func(pred, target, **kwargs)`. The function only needs to compute\n    element-wise loss without any reduction. This decorator will add weight\n    and reduction arguments to the function. The decorated function will have\n    the signature like `loss_func(pred, target, weight=None, reduction='mean',\n    avg_factor=None, **kwargs)`.\n\n    :Example:\n\n    >>> import torch\n    >>> @weighted_loss\n    >>> def l1_loss(pred, target):\n    >>>     return (pred - target).abs()\n\n    >>> pred = torch.Tensor([0, 2, 3])\n    >>> target = torch.Tensor([1, 1, 1])\n    >>> weight = torch.Tensor([1, 0, 1])\n\n    >>> l1_loss(pred, target)\n    tensor(1.3333)\n    >>> l1_loss(pred, target, weight)\n    tensor(1.)\n    >>> l1_loss(pred, target, reduction='none')\n    tensor([1., 1., 2.])\n    >>> l1_loss(pred, target, weight, avg_factor=2)\n    tensor(1.5000)\n    \"\"\"\n\n    @functools.wraps(loss_func)\n    def wrapper(pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                reduction: str = 'mean',\n                avg_factor: Optional[int] = None,\n                **kwargs) -> Tensor:\n        \"\"\"\n        Args:\n            pred (Tensor): The prediction.\n            target (Tensor): Target bboxes.\n            weight (Optional[Tensor], optional): The weight of loss for each\n                prediction. Defaults to None.\n            reduction (str, optional): Options are \"none\", \"mean\" and \"sum\".\n                Defaults to 'mean'.\n            avg_factor (Optional[int], optional): Average factor that is used\n                to average the loss. Defaults to None.\n\n        Returns:\n            Tensor: Loss tensor.\n        \"\"\"\n        # get element-wise loss\n        loss = loss_func(pred, target, **kwargs)\n        loss = weight_reduce_loss(loss, weight, reduction, avg_factor)\n        return loss\n\n    return wrapper\n"
  },
  {
    "path": "mmdet/models/losses/varifocal_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .utils import weight_reduce_loss\n\n\ndef varifocal_loss(pred: Tensor,\n                   target: Tensor,\n                   weight: Optional[Tensor] = None,\n                   alpha: float = 0.75,\n                   gamma: float = 2.0,\n                   iou_weighted: bool = True,\n                   reduction: str = 'mean',\n                   avg_factor: Optional[int] = None) -> Tensor:\n    \"\"\"`Varifocal Loss <https://arxiv.org/abs/2008.13367>`_\n\n    Args:\n        pred (Tensor): The prediction with shape (N, C), C is the\n            number of classes.\n        target (Tensor): The learning target of the iou-aware\n            classification score with shape (N, C), C is the number of classes.\n        weight (Tensor, optional): The weight of loss for each\n            prediction. Defaults to None.\n        alpha (float, optional): A balance factor for the negative part of\n            Varifocal Loss, which is different from the alpha of Focal Loss.\n            Defaults to 0.75.\n        gamma (float, optional): The gamma for calculating the modulating\n            factor. Defaults to 2.0.\n        iou_weighted (bool, optional): Whether to weight the loss of the\n            positive example with the iou target. Defaults to True.\n        reduction (str, optional): The method used to reduce the loss into\n            a scalar. Defaults to 'mean'. Options are \"none\", \"mean\" and\n            \"sum\".\n        avg_factor (int, optional): Average factor that is used to average\n            the loss. Defaults to None.\n\n    Returns:\n        Tensor: Loss tensor.\n    \"\"\"\n    # pred and target should be of the same size\n    assert pred.size() == target.size()\n    pred_sigmoid = pred.sigmoid()\n    target = target.type_as(pred)\n    if iou_weighted:\n        focal_weight = target * (target > 0.0).float() + \\\n            alpha * (pred_sigmoid - target).abs().pow(gamma) * \\\n            (target <= 0.0).float()\n    else:\n        focal_weight = (target > 0.0).float() + \\\n            alpha * (pred_sigmoid - target).abs().pow(gamma) * \\\n            (target <= 0.0).float()\n    loss = F.binary_cross_entropy_with_logits(\n        pred, target, reduction='none') * focal_weight\n    loss = weight_reduce_loss(loss, weight, reduction, avg_factor)\n    return loss\n\n\n@MODELS.register_module()\nclass VarifocalLoss(nn.Module):\n\n    def __init__(self,\n                 use_sigmoid: bool = True,\n                 alpha: float = 0.75,\n                 gamma: float = 2.0,\n                 iou_weighted: bool = True,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        \"\"\"`Varifocal Loss <https://arxiv.org/abs/2008.13367>`_\n\n        Args:\n            use_sigmoid (bool, optional): Whether the prediction is\n                used for sigmoid or softmax. Defaults to True.\n            alpha (float, optional): A balance factor for the negative part of\n                Varifocal Loss, which is different from the alpha of Focal\n                Loss. Defaults to 0.75.\n            gamma (float, optional): The gamma for calculating the modulating\n                factor. Defaults to 2.0.\n            iou_weighted (bool, optional): Whether to weight the loss of the\n                positive examples with the iou target. Defaults to True.\n            reduction (str, optional): The method used to reduce the loss into\n                a scalar. Defaults to 'mean'. Options are \"none\", \"mean\" and\n                \"sum\".\n            loss_weight (float, optional): Weight of loss. Defaults to 1.0.\n        \"\"\"\n        super().__init__()\n        assert use_sigmoid is True, \\\n            'Only sigmoid varifocal loss supported now.'\n        assert alpha >= 0.0\n        self.use_sigmoid = use_sigmoid\n        self.alpha = alpha\n        self.gamma = gamma\n        self.iou_weighted = iou_weighted\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): The prediction with shape (N, C), C is the\n                number of classes.\n            target (Tensor): The learning target of the iou-aware\n                classification score with shape (N, C), C is\n                the number of classes.\n            weight (Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            Tensor: The calculated loss\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        if self.use_sigmoid:\n            loss_cls = self.loss_weight * varifocal_loss(\n                pred,\n                target,\n                weight,\n                alpha=self.alpha,\n                gamma=self.gamma,\n                iou_weighted=self.iou_weighted,\n                reduction=reduction,\n                avg_factor=avg_factor)\n        else:\n            raise NotImplementedError\n        return loss_cls\n"
  },
  {
    "path": "mmdet/models/mot/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base import BaseMOTModel\nfrom .bytetrack import ByteTrack\nfrom .deep_sort import DeepSORT\nfrom .ocsort import OCSORT\nfrom .qdtrack import QDTrack\nfrom .strongsort import StrongSORT\n\n__all__ = [\n    'BaseMOTModel', 'ByteTrack', 'QDTrack', 'DeepSORT', 'StrongSORT', 'OCSORT'\n]\n"
  },
  {
    "path": "mmdet/models/mot/base.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import Dict, List, Tuple, Union\n\nfrom mmengine.model import BaseModel\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptTrackSampleList, TrackSampleList\nfrom mmdet.utils import OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass BaseMOTModel(BaseModel, metaclass=ABCMeta):\n    \"\"\"Base class for multiple object tracking.\n\n    Args:\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`TrackDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n        init_cfg (dict or list[dict]): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n\n    def freeze_module(self, module: Union[List[str], Tuple[str], str]) -> None:\n        \"\"\"Freeze module during training.\"\"\"\n        if isinstance(module, str):\n            modules = [module]\n        else:\n            if not (isinstance(module, list) or isinstance(module, tuple)):\n                raise TypeError('module must be a str or a list.')\n            else:\n                modules = module\n        for module in modules:\n            m = getattr(self, module)\n            m.eval()\n            for param in m.parameters():\n                param.requires_grad = False\n\n    @property\n    def with_detector(self) -> bool:\n        \"\"\"bool: whether the framework has a detector.\"\"\"\n        return hasattr(self, 'detector') and self.detector is not None\n\n    @property\n    def with_reid(self) -> bool:\n        \"\"\"bool: whether the framework has a reid model.\"\"\"\n        return hasattr(self, 'reid') and self.reid is not None\n\n    @property\n    def with_motion(self) -> bool:\n        \"\"\"bool: whether the framework has a motion model.\"\"\"\n        return hasattr(self, 'motion') and self.motion is not None\n\n    @property\n    def with_track_head(self) -> bool:\n        \"\"\"bool: whether the framework has a track_head.\"\"\"\n        return hasattr(self, 'track_head') and self.track_head is not None\n\n    @property\n    def with_tracker(self) -> bool:\n        \"\"\"bool: whether the framework has a tracker.\"\"\"\n        return hasattr(self, 'tracker') and self.tracker is not None\n\n    def forward(self,\n                inputs: Dict[str, Tensor],\n                data_samples: OptTrackSampleList = None,\n                mode: str = 'predict',\n                **kwargs):\n        \"\"\"The unified entry for a forward process in both training and test.\n\n        The method should accept three modes: \"tensor\", \"predict\" and \"loss\":\n\n        - \"tensor\": Forward the whole network and return tensor or tuple of\n        tensor without any post-processing, same as a common nn.Module.\n        - \"predict\": Forward and return the predictions, which are fully\n        processed to a list of :obj:`TrackDataSample`.\n        - \"loss\": Forward and return a dict of losses according to the given\n        inputs and data samples.\n\n        Note that this method doesn't handle neither back propagation nor\n        optimizer updating, which are done in the :meth:`train_step`.\n\n        Args:\n            inputs (Dict[str, Tensor]): of shape (N, T, C, H, W)\n                encoding input images. Typically these should be mean centered\n                and std scaled. The N denotes batch size. The T denotes the\n                number of key/reference frames.\n                - img (Tensor) : The key images.\n                - ref_img (Tensor): The reference images.\n            data_samples (list[:obj:`TrackDataSample`], optional): The\n                annotation data of every samples. Defaults to None.\n            mode (str): Return what kind of value. Defaults to 'predict'.\n\n        Returns:\n            The return type depends on ``mode``.\n\n            - If ``mode=\"tensor\"``, return a tensor or a tuple of tensor.\n            - If ``mode=\"predict\"``, return a list of :obj:`TrackDataSample`.\n            - If ``mode=\"loss\"``, return a dict of tensor.\n        \"\"\"\n        if mode == 'loss':\n            return self.loss(inputs, data_samples, **kwargs)\n        elif mode == 'predict':\n            return self.predict(inputs, data_samples, **kwargs)\n        elif mode == 'tensor':\n            return self._forward(inputs, data_samples, **kwargs)\n        else:\n            raise RuntimeError(f'Invalid mode \"{mode}\". '\n                               'Only supports loss, predict and tensor mode')\n\n    @abstractmethod\n    def loss(self, inputs: Dict[str, Tensor], data_samples: TrackSampleList,\n             **kwargs) -> Union[dict, tuple]:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\"\"\"\n        pass\n\n    @abstractmethod\n    def predict(self, inputs: Dict[str, Tensor], data_samples: TrackSampleList,\n                **kwargs) -> TrackSampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\"\"\"\n        pass\n\n    def _forward(self,\n                 inputs: Dict[str, Tensor],\n                 data_samples: OptTrackSampleList = None,\n                 **kwargs):\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n         Args:\n            inputs (Dict[str, Tensor]): of shape (N, T, C, H, W).\n            data_samples (List[:obj:`TrackDataSample`], optional): The\n                Data Samples. It usually includes information such as\n                `gt_instance`.\n\n        Returns:\n            tuple[list]: A tuple of features from ``head`` forward.\n        \"\"\"\n        raise NotImplementedError(\n            \"_forward function (namely 'tensor' mode) is not supported now\")\n"
  },
  {
    "path": "mmdet/models/mot/bytetrack.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Optional\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList, TrackSampleList\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .base import BaseMOTModel\n\n\n@MODELS.register_module()\nclass ByteTrack(BaseMOTModel):\n    \"\"\"ByteTrack: Multi-Object Tracking by Associating Every Detection Box.\n\n    This multi object tracker is the implementation of `ByteTrack\n    <https://arxiv.org/abs/2110.06864>`_.\n\n    Args:\n        detector (dict): Configuration of detector. Defaults to None.\n        tracker (dict): Configuration of tracker. Defaults to None.\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`TrackDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n        init_cfg (dict or list[dict]): Configuration of initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: Optional[dict] = None,\n                 tracker: Optional[dict] = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(data_preprocessor, init_cfg)\n\n        if detector is not None:\n            self.detector = MODELS.build(detector)\n\n        if tracker is not None:\n            self.tracker = MODELS.build(tracker)\n\n    def loss(self, inputs: Tensor, data_samples: SampleList, **kwargs) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            inputs (Tensor): of shape (N, C, H, W) encoding\n                input images. Typically these should be mean centered and std\n                scaled. The N denotes batch size\n            data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        return self.detector.loss(inputs, data_samples, **kwargs)\n\n    def predict(self, inputs: Dict[str, Tensor], data_samples: TrackSampleList,\n                **kwargs) -> TrackSampleList:\n        \"\"\"Predict results from a video and data samples with post-processing.\n\n        Args:\n            inputs (Tensor): of shape (N, T, C, H, W) encoding\n                input images. The N denotes batch size.\n                The T denotes the number of frames in a video.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `video_data_samples`.\n        Returns:\n            TrackSampleList: Tracking results of the inputs.\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        assert inputs.size(0) == 1, \\\n            'Bytetrack inference only support ' \\\n            '1 batch size per gpu for now.'\n\n        assert len(data_samples) == 1, \\\n            'Bytetrack inference only support 1 batch size per gpu for now.'\n\n        track_data_sample = data_samples[0]\n        video_len = len(track_data_sample)\n\n        for frame_id in range(video_len):\n            img_data_sample = track_data_sample[frame_id]\n            single_img = inputs[:, frame_id].contiguous()\n            # det_results List[DetDataSample]\n            det_results = self.detector.predict(single_img, [img_data_sample])\n            assert len(det_results) == 1, 'Batch inference is not supported.'\n\n            pred_track_instances = self.tracker.track(\n                data_sample=det_results[0], **kwargs)\n            img_data_sample.pred_track_instances = pred_track_instances\n\n        return [track_data_sample]\n"
  },
  {
    "path": "mmdet/models/mot/deep_sort.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackSampleList\nfrom mmdet.utils import OptConfigType\nfrom .base import BaseMOTModel\n\n\n@MODELS.register_module()\nclass DeepSORT(BaseMOTModel):\n    \"\"\"Simple online and realtime tracking with a deep association metric.\n\n    Details can be found at `DeepSORT<https://arxiv.org/abs/1703.07402>`_.\n\n    Args:\n        detector (dict): Configuration of detector. Defaults to None.\n        reid (dict): Configuration of reid. Defaults to None\n        tracker (dict): Configuration of tracker. Defaults to None.\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`TrackDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n        init_cfg (dict or list[dict]): Configuration of initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: Optional[dict] = None,\n                 reid: Optional[dict] = None,\n                 tracker: Optional[dict] = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptConfigType = None):\n        super().__init__(data_preprocessor, init_cfg)\n\n        if detector is not None:\n            self.detector = MODELS.build(detector)\n\n        if reid is not None:\n            self.reid = MODELS.build(reid)\n\n        if tracker is not None:\n            self.tracker = MODELS.build(tracker)\n\n        self.preprocess_cfg = data_preprocessor\n\n    def loss(self, inputs: Tensor, data_samples: TrackSampleList,\n             **kwargs) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\"\"\"\n        raise NotImplementedError(\n            'Please train `detector` and `reid` models firstly, then \\\n                inference with SORT/DeepSORT.')\n\n    def predict(self,\n                inputs: Tensor,\n                data_samples: TrackSampleList,\n                rescale: bool = True,\n                **kwargs) -> TrackSampleList:\n        \"\"\"Predict results from a video and data samples with post- processing.\n\n        Args:\n            inputs (Tensor): of shape (N, T, C, H, W) encoding\n                input images. The N denotes batch size.\n                The T denotes the number of key frames\n                and reference frames.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance`.\n            rescale (bool, Optional): If False, then returned bboxes and masks\n                will fit the scale of img, otherwise, returned bboxes and masks\n                will fit the scale of original image shape. Defaults to True.\n\n        Returns:\n            TrackSampleList: List[TrackDataSample]\n            Tracking results of the input videos.\n            Each DetDataSample usually contains ``pred_track_instances``.\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        assert inputs.size(0) == 1, \\\n            'SORT/DeepSORT inference only support ' \\\n            '1 batch size per gpu for now.'\n\n        assert len(data_samples) == 1, \\\n            'SORT/DeepSORT inference only support ' \\\n            '1 batch size per gpu for now.'\n\n        track_data_sample = data_samples[0]\n        video_len = len(track_data_sample)\n        if track_data_sample[0].frame_id == 0:\n            self.tracker.reset()\n\n        for frame_id in range(video_len):\n            img_data_sample = track_data_sample[frame_id]\n            single_img = inputs[:, frame_id].contiguous()\n            # det_results List[DetDataSample]\n            det_results = self.detector.predict(single_img, [img_data_sample])\n            assert len(det_results) == 1, 'Batch inference is not supported.'\n\n            pred_track_instances = self.tracker.track(\n                model=self,\n                img=single_img,\n                feats=None,\n                data_sample=det_results[0],\n                data_preprocessor=self.preprocess_cfg,\n                rescale=rescale,\n                **kwargs)\n            img_data_sample.pred_track_instances = pred_track_instances\n\n        return [track_data_sample]\n"
  },
  {
    "path": "mmdet/models/mot/ocsort.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nfrom typing import Dict, Optional\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackSampleList\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .base import BaseMOTModel\n\n\n@MODELS.register_module()\nclass OCSORT(BaseMOTModel):\n    \"\"\"OCOSRT: Observation-Centric SORT: Rethinking SORT for Robust\n    Multi-Object Tracking\n\n    This multi object tracker is the implementation of `OC-SORT\n    <https://arxiv.org/abs/2203.14360>`_.\n\n    Args:\n        detector (dict): Configuration of detector. Defaults to None.\n        tracker (dict): Configuration of tracker. Defaults to None.\n        motion (dict): Configuration of motion. Defaults to None.\n        init_cfg (dict): Configuration of initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: Optional[dict] = None,\n                 tracker: Optional[dict] = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(data_preprocessor, init_cfg)\n\n        if detector is not None:\n            self.detector = MODELS.build(detector)\n\n        if tracker is not None:\n            self.tracker = MODELS.build(tracker)\n\n    def loss(self, inputs: Tensor, data_samples: TrackSampleList,\n             **kwargs) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\"\"\"\n        return self.detector.loss(inputs, data_samples, **kwargs)\n\n    def predict(self, inputs: Dict[str, Tensor], data_samples: TrackSampleList,\n                **kwargs) -> TrackSampleList:\n        \"\"\"Predict results from a video and data samples with post-processing.\n\n        Args:\n            inputs (Tensor): of shape (N, T, C, H, W) encoding\n                input images. The N denotes batch size.\n                The T denotes the number of frames in a video.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `video_data_samples`.\n        Returns:\n            TrackSampleList: Tracking results of the inputs.\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        assert inputs.size(0) == 1, \\\n            'OCSORT inference only support ' \\\n            '1 batch size per gpu for now.'\n\n        assert len(data_samples) == 1, \\\n            'OCSORT inference only support 1 batch size per gpu for now.'\n\n        track_data_sample = data_samples[0]\n        video_len = len(track_data_sample)\n\n        for frame_id in range(video_len):\n            img_data_sample = track_data_sample[frame_id]\n            single_img = inputs[:, frame_id].contiguous()\n            # det_results List[DetDataSample]\n            det_results = self.detector.predict(single_img, [img_data_sample])\n            assert len(det_results) == 1, 'Batch inference is not supported.'\n\n            pred_track_instances = self.tracker.track(\n                data_sample=det_results[0], **kwargs)\n            img_data_sample.pred_track_instances = pred_track_instances\n\n        return [track_data_sample]\n"
  },
  {
    "path": "mmdet/models/mot/qdtrack.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Union\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackSampleList\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .base import BaseMOTModel\n\n\n@MODELS.register_module()\nclass QDTrack(BaseMOTModel):\n    \"\"\"Quasi-Dense Similarity Learning for Multiple Object Tracking.\n\n    This multi object tracker is the implementation of `QDTrack\n    <https://arxiv.org/abs/2006.06664>`_.\n\n    Args:\n        detector (dict): Configuration of detector. Defaults to None.\n        track_head (dict): Configuration of track head. Defaults to None.\n        tracker (dict): Configuration of tracker. Defaults to None.\n        freeze_detector (bool): If True, freeze the detector weights.\n            Defaults to False.\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`TrackDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n        init_cfg (dict or list[dict]): Configuration of initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: Optional[dict] = None,\n                 track_head: Optional[dict] = None,\n                 tracker: Optional[dict] = None,\n                 freeze_detector: bool = False,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(data_preprocessor, init_cfg)\n        if detector is not None:\n            self.detector = MODELS.build(detector)\n\n        if track_head is not None:\n            self.track_head = MODELS.build(track_head)\n\n        if tracker is not None:\n            self.tracker = MODELS.build(tracker)\n\n        self.freeze_detector = freeze_detector\n        if self.freeze_detector:\n            self.freeze_module('detector')\n\n    def predict(self,\n                inputs: Tensor,\n                data_samples: TrackSampleList,\n                rescale: bool = True,\n                **kwargs) -> TrackSampleList:\n        \"\"\"Predict results from a video and data samples with post- processing.\n\n        Args:\n            inputs (Tensor): of shape (N, T, C, H, W) encoding\n                input images. The N denotes batch size.\n                The T denotes the number of frames in a video.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `video_data_samples`.\n            rescale (bool, Optional): If False, then returned bboxes and masks\n                will fit the scale of img, otherwise, returned bboxes and masks\n                will fit the scale of original image shape. Defaults to True.\n\n        Returns:\n            TrackSampleList: Tracking results of the inputs.\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        assert inputs.size(0) == 1, \\\n            'QDTrack inference only support 1 batch size per gpu for now.'\n\n        assert len(data_samples) == 1, \\\n            'QDTrack only support 1 batch size per gpu for now.'\n\n        track_data_sample = data_samples[0]\n        video_len = len(track_data_sample)\n        if track_data_sample[0].frame_id == 0:\n            self.tracker.reset()\n\n        for frame_id in range(video_len):\n            img_data_sample = track_data_sample[frame_id]\n            single_img = inputs[:, frame_id].contiguous()\n            x = self.detector.extract_feat(single_img)\n            rpn_results_list = self.detector.rpn_head.predict(\n                x, [img_data_sample])\n            # det_results List[InstanceData]\n            det_results = self.detector.roi_head.predict(\n                x, rpn_results_list, [img_data_sample], rescale=rescale)\n            assert len(det_results) == 1, 'Batch inference is not supported.'\n            img_data_sample.pred_instances = det_results[0]\n            frame_pred_track_instances = self.tracker.track(\n                model=self,\n                img=single_img,\n                feats=x,\n                data_sample=img_data_sample,\n                **kwargs)\n            img_data_sample.pred_track_instances = frame_pred_track_instances\n\n        return [track_data_sample]\n\n    def loss(self, inputs: Tensor, data_samples: TrackSampleList,\n             **kwargs) -> Union[dict, tuple]:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            inputs (Dict[str, Tensor]): of shape (N, T, C, H, W) encoding\n                input images. Typically these should be mean centered and std\n                scaled. The N denotes batch size. The T denotes the number of\n                frames.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `video_data_samples`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        # modify the inputs shape to fit mmdet\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        assert inputs.size(1) == 2, \\\n            'QDTrack can only have 1 key frame and 1 reference frame.'\n\n        # split the data_samples into two aspects: key frames and reference\n        # frames\n        ref_data_samples, key_data_samples = [], []\n        key_frame_inds, ref_frame_inds = [], []\n        # set cat_id of gt_labels to 0 in RPN\n        for track_data_sample in data_samples:\n            key_frame_inds.append(track_data_sample.key_frames_inds[0])\n            ref_frame_inds.append(track_data_sample.ref_frames_inds[0])\n            key_data_sample = track_data_sample.get_key_frames()[0]\n            key_data_sample.gt_instances.labels = \\\n                torch.zeros_like(key_data_sample.gt_instances.labels)\n            key_data_samples.append(key_data_sample)\n            ref_data_sample = track_data_sample.get_ref_frames()[0]\n            ref_data_samples.append(ref_data_sample)\n\n        key_frame_inds = torch.tensor(key_frame_inds, dtype=torch.int64)\n        ref_frame_inds = torch.tensor(ref_frame_inds, dtype=torch.int64)\n        batch_inds = torch.arange(len(inputs))\n        key_imgs = inputs[batch_inds, key_frame_inds].contiguous()\n        ref_imgs = inputs[batch_inds, ref_frame_inds].contiguous()\n\n        x = self.detector.extract_feat(key_imgs)\n        ref_x = self.detector.extract_feat(ref_imgs)\n\n        losses = dict()\n        # RPN head forward and loss\n        assert self.detector.with_rpn, \\\n            'QDTrack only support detector with RPN.'\n\n        proposal_cfg = self.detector.train_cfg.get('rpn_proposal',\n                                                   self.detector.test_cfg.rpn)\n        rpn_losses, rpn_results_list = self.detector.rpn_head. \\\n            loss_and_predict(x,\n                             key_data_samples,\n                             proposal_cfg=proposal_cfg,\n                             **kwargs)\n        ref_rpn_results_list = self.detector.rpn_head.predict(\n            ref_x, ref_data_samples, **kwargs)\n\n        # avoid get same name with roi_head loss\n        keys = rpn_losses.keys()\n        for key in keys:\n            if 'loss' in key and 'rpn' not in key:\n                rpn_losses[f'rpn_{key}'] = rpn_losses.pop(key)\n        losses.update(rpn_losses)\n\n        # roi_head loss\n        losses_detect = self.detector.roi_head.loss(x, rpn_results_list,\n                                                    key_data_samples, **kwargs)\n        losses.update(losses_detect)\n\n        # tracking head loss\n        losses_track = self.track_head.loss(x, ref_x, rpn_results_list,\n                                            ref_rpn_results_list, data_samples,\n                                            **kwargs)\n        losses.update(losses_track)\n\n        return losses\n"
  },
  {
    "path": "mmdet/models/mot/strongsort.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport numpy as np\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import TrackSampleList\nfrom mmdet.utils import OptConfigType\nfrom .deep_sort import DeepSORT\n\n\n@MODELS.register_module()\nclass StrongSORT(DeepSORT):\n    \"\"\"StrongSORT: Make DeepSORT Great Again.\n\n    Details can be found at `StrongSORT<https://arxiv.org/abs/2202.13514>`_.\n\n    Args:\n        detector (dict): Configuration of detector. Defaults to None.\n        reid (dict): Configuration of reid. Defaults to None\n        tracker (dict): Configuration of tracker. Defaults to None.\n        kalman (dict): Configuration of Kalman filter. Defaults to None.\n        cmc (dict): Configuration of camera model compensation.\n            Defaults to None.\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`TrackDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n        init_cfg (dict or list[dict]): Configuration of initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: Optional[dict] = None,\n                 reid: Optional[dict] = None,\n                 cmc: Optional[dict] = None,\n                 tracker: Optional[dict] = None,\n                 postprocess_model: Optional[dict] = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptConfigType = None):\n        super().__init__(detector, reid, tracker, data_preprocessor, init_cfg)\n\n        if cmc is not None:\n            self.cmc = TASK_UTILS.build(cmc)\n\n        if postprocess_model is not None:\n            self.postprocess_model = TASK_UTILS.build(postprocess_model)\n\n    @property\n    def with_cmc(self):\n        \"\"\"bool: whether the framework has a camera model compensation\n                model.\n        \"\"\"\n        return hasattr(self, 'cmc') and self.cmc is not None\n\n    def predict(self,\n                inputs: Tensor,\n                data_samples: TrackSampleList,\n                rescale: bool = True,\n                **kwargs) -> TrackSampleList:\n        \"\"\"Predict results from a video and data samples with post- processing.\n\n        Args:\n            inputs (Tensor): of shape (N, T, C, H, W) encoding\n                input images. The N denotes batch size.\n                The T denotes the number of key frames\n                and reference frames.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance`.\n            rescale (bool, Optional): If False, then returned bboxes and masks\n                will fit the scale of img, otherwise, returned bboxes and masks\n                will fit the scale of original image shape. Defaults to True.\n\n        Returns:\n            TrackSampleList: List[TrackDataSample]\n            Tracking results of the input videos.\n            Each DetDataSample usually contains ``pred_track_instances``.\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        assert inputs.size(0) == 1, \\\n            'SORT/DeepSORT inference only support ' \\\n            '1 batch size per gpu for now.'\n\n        assert len(data_samples) == 1, \\\n            'SORT/DeepSORT inference only support ' \\\n            '1 batch size per gpu for now.'\n\n        track_data_sample = data_samples[0]\n        video_len = len(track_data_sample)\n\n        video_track_instances = []\n        for frame_id in range(video_len):\n            img_data_sample = track_data_sample[frame_id]\n            single_img = inputs[:, frame_id].contiguous()\n            # det_results List[DetDataSample]\n            det_results = self.detector.predict(single_img, [img_data_sample])\n            assert len(det_results) == 1, 'Batch inference is not supported.'\n\n            pred_track_instances = self.tracker.track(\n                model=self,\n                img=single_img,\n                data_sample=det_results[0],\n                data_preprocessor=self.preprocess_cfg,\n                rescale=rescale,\n                **kwargs)\n            for i in range(len(pred_track_instances.instances_id)):\n                video_track_instances.append(\n                    np.array([\n                        frame_id + 1,\n                        pred_track_instances.instances_id[i].cpu(),\n                        pred_track_instances.bboxes[i][0].cpu(),\n                        pred_track_instances.bboxes[i][1].cpu(),\n                        (pred_track_instances.bboxes[i][2] -\n                         pred_track_instances.bboxes[i][0]).cpu(),\n                        (pred_track_instances.bboxes[i][3] -\n                         pred_track_instances.bboxes[i][1]).cpu(),\n                        pred_track_instances.scores[i].cpu()\n                    ]))\n        video_track_instances = np.array(video_track_instances).reshape(-1, 7)\n        video_track_instances = self.postprocess_model.forward(\n            video_track_instances)\n        for frame_id in range(video_len):\n            track_data_sample[frame_id].pred_track_instances = \\\n                    InstanceData(bboxes=video_track_instances[\n                        video_track_instances[:, 0] == frame_id + 1, :])\n\n        return [track_data_sample]\n"
  },
  {
    "path": "mmdet/models/necks/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .bfp import BFP\nfrom .channel_mapper import ChannelMapper\nfrom .cspnext_pafpn import CSPNeXtPAFPN\nfrom .ct_resnet_neck import CTResNetNeck\nfrom .dilated_encoder import DilatedEncoder\nfrom .dyhead import DyHead\nfrom .fpg import FPG\nfrom .fpn import FPN\nfrom .fpn_carafe import FPN_CARAFE\nfrom .fpn_dropblock import FPN_DropBlock\nfrom .hrfpn import HRFPN\nfrom .nas_fpn import NASFPN\nfrom .nasfcos_fpn import NASFCOS_FPN\nfrom .pafpn import PAFPN\nfrom .rfp import RFP\nfrom .ssd_neck import SSDNeck\nfrom .ssh import SSH\nfrom .yolo_neck import YOLOV3Neck\nfrom .yolox_pafpn import YOLOXPAFPN\n\n__all__ = [\n    'FPN', 'BFP', 'ChannelMapper', 'HRFPN', 'NASFPN', 'FPN_CARAFE', 'PAFPN',\n    'NASFCOS_FPN', 'RFP', 'YOLOV3Neck', 'FPG', 'DilatedEncoder',\n    'CTResNetNeck', 'SSDNeck', 'YOLOXPAFPN', 'DyHead', 'CSPNeXtPAFPN', 'SSH',\n    'FPN_DropBlock'\n]\n"
  },
  {
    "path": "mmdet/models/necks/bfp.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmcv.cnn.bricks import NonLocal2d\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass BFP(BaseModule):\n    \"\"\"BFP (Balanced Feature Pyramids)\n\n    BFP takes multi-level features as inputs and gather them into a single one,\n    then refine the gathered feature and scatter the refined results to\n    multi-level features. This module is used in Libra R-CNN (CVPR 2019), see\n    the paper `Libra R-CNN: Towards Balanced Learning for Object Detection\n    <https://arxiv.org/abs/1904.02701>`_ for details.\n\n    Args:\n        in_channels (int): Number of input channels (feature maps of all levels\n            should have the same channels).\n        num_levels (int): Number of input feature levels.\n        refine_level (int): Index of integration and refine level of BSF in\n            multi-level features from bottom to top.\n        refine_type (str): Type of the refine op, currently support\n            [None, 'conv', 'non_local'].\n        conv_cfg (:obj:`ConfigDict` or dict, optional): The config dict for\n            convolution layers.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): The config dict for\n            normalization layers.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or\n            dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: int,\n        num_levels: int,\n        refine_level: int = 2,\n        refine_type: str = None,\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        init_cfg: OptMultiConfig = dict(\n            type='Xavier', layer='Conv2d', distribution='uniform')\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert refine_type in [None, 'conv', 'non_local']\n\n        self.in_channels = in_channels\n        self.num_levels = num_levels\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n\n        self.refine_level = refine_level\n        self.refine_type = refine_type\n        assert 0 <= self.refine_level < self.num_levels\n\n        if self.refine_type == 'conv':\n            self.refine = ConvModule(\n                self.in_channels,\n                self.in_channels,\n                3,\n                padding=1,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg)\n        elif self.refine_type == 'non_local':\n            self.refine = NonLocal2d(\n                self.in_channels,\n                reduction=1,\n                use_scale=False,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg)\n\n    def forward(self, inputs: Tuple[Tensor]) -> Tuple[Tensor]:\n        \"\"\"Forward function.\"\"\"\n        assert len(inputs) == self.num_levels\n\n        # step 1: gather multi-level features by resize and average\n        feats = []\n        gather_size = inputs[self.refine_level].size()[2:]\n        for i in range(self.num_levels):\n            if i < self.refine_level:\n                gathered = F.adaptive_max_pool2d(\n                    inputs[i], output_size=gather_size)\n            else:\n                gathered = F.interpolate(\n                    inputs[i], size=gather_size, mode='nearest')\n            feats.append(gathered)\n\n        bsf = sum(feats) / len(feats)\n\n        # step 2: refine gathered features\n        if self.refine_type is not None:\n            bsf = self.refine(bsf)\n\n        # step 3: scatter refined features to multi-levels by a residual path\n        outs = []\n        for i in range(self.num_levels):\n            out_size = inputs[i].size()[2:]\n            if i < self.refine_level:\n                residual = F.interpolate(bsf, size=out_size, mode='nearest')\n            else:\n                residual = F.adaptive_max_pool2d(bsf, output_size=out_size)\n            outs.append(residual + inputs[i])\n\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/channel_mapper.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass ChannelMapper(BaseModule):\n    \"\"\"Channel Mapper to reduce/increase channels of backbone features.\n\n    This is used to reduce/increase channels of backbone features.\n\n    Args:\n        in_channels (List[int]): Number of input channels per scale.\n        out_channels (int): Number of output channels (used at each scale).\n        kernel_size (int, optional): kernel_size for reducing channels (used\n            at each scale). Default: 3.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Default: None.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            normalization layer. Default: None.\n        act_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            activation layer in ConvModule. Default: dict(type='ReLU').\n        bias (bool | str): If specified as `auto`, it will be decided by the\n            norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise\n            False. Default: \"auto\".\n        num_outs (int, optional): Number of output feature maps. There would\n            be extra_convs when num_outs larger than the length of in_channels.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or dict],\n            optional): Initialization config dict.\n    Example:\n        >>> import torch\n        >>> in_channels = [2, 3, 5, 7]\n        >>> scales = [340, 170, 84, 43]\n        >>> inputs = [torch.rand(1, c, s, s)\n        ...           for c, s in zip(in_channels, scales)]\n        >>> self = ChannelMapper(in_channels, 11, 3).eval()\n        >>> outputs = self.forward(inputs)\n        >>> for i in range(len(outputs)):\n        ...     print(f'outputs[{i}].shape = {outputs[i].shape}')\n        outputs[0].shape = torch.Size([1, 11, 340, 340])\n        outputs[1].shape = torch.Size([1, 11, 170, 170])\n        outputs[2].shape = torch.Size([1, 11, 84, 84])\n        outputs[3].shape = torch.Size([1, 11, 43, 43])\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: List[int],\n        out_channels: int,\n        kernel_size: int = 3,\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        act_cfg: OptConfigType = dict(type='ReLU'),\n        bias: Union[bool, str] = 'auto',\n        num_outs: int = None,\n        init_cfg: OptMultiConfig = dict(\n            type='Xavier', layer='Conv2d', distribution='uniform')\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert isinstance(in_channels, list)\n        self.extra_convs = None\n        if num_outs is None:\n            num_outs = len(in_channels)\n        self.convs = nn.ModuleList()\n        for in_channel in in_channels:\n            self.convs.append(\n                ConvModule(\n                    in_channel,\n                    out_channels,\n                    kernel_size,\n                    padding=(kernel_size - 1) // 2,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg,\n                    bias=bias))\n        if num_outs > len(in_channels):\n            self.extra_convs = nn.ModuleList()\n            for i in range(len(in_channels), num_outs):\n                if i == len(in_channels):\n                    in_channel = in_channels[-1]\n                else:\n                    in_channel = out_channels\n                self.extra_convs.append(\n                    ConvModule(\n                        in_channel,\n                        out_channels,\n                        3,\n                        stride=2,\n                        padding=1,\n                        conv_cfg=conv_cfg,\n                        norm_cfg=norm_cfg,\n                        act_cfg=act_cfg,\n                        bias=bias))\n\n    def forward(self, inputs: Tuple[Tensor]) -> Tuple[Tensor]:\n        \"\"\"Forward function.\"\"\"\n        assert len(inputs) == len(self.convs)\n        outs = [self.convs[i](inputs[i]) for i in range(len(inputs))]\n        if self.extra_convs:\n            for i in range(len(self.extra_convs)):\n                if i == 0:\n                    outs.append(self.extra_convs[0](inputs[-1]))\n                else:\n                    outs.append(self.extra_convs[i](outs[-1]))\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/cspnext_pafpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptMultiConfig\nfrom ..layers import CSPLayer\n\n\n@MODELS.register_module()\nclass CSPNeXtPAFPN(BaseModule):\n    \"\"\"Path Aggregation Network with CSPNeXt blocks.\n\n    Args:\n        in_channels (Sequence[int]): Number of input channels per scale.\n        out_channels (int): Number of output channels (used at each scale)\n        num_csp_blocks (int): Number of bottlenecks in CSPLayer.\n            Defaults to 3.\n        use_depthwise (bool): Whether to use depthwise separable convolution in\n            blocks. Defaults to False.\n        expand_ratio (float): Ratio to adjust the number of channels of the\n            hidden layer. Default: 0.5\n        upsample_cfg (dict): Config dict for interpolate layer.\n            Default: `dict(scale_factor=2, mode='nearest')`\n        conv_cfg (dict, optional): Config dict for convolution layer.\n            Default: None, which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='BN')\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='Swish')\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: Sequence[int],\n        out_channels: int,\n        num_csp_blocks: int = 3,\n        use_depthwise: bool = False,\n        expand_ratio: float = 0.5,\n        upsample_cfg: ConfigType = dict(scale_factor=2, mode='nearest'),\n        conv_cfg: bool = None,\n        norm_cfg: ConfigType = dict(type='BN', momentum=0.03, eps=0.001),\n        act_cfg: ConfigType = dict(type='Swish'),\n        init_cfg: OptMultiConfig = dict(\n            type='Kaiming',\n            layer='Conv2d',\n            a=math.sqrt(5),\n            distribution='uniform',\n            mode='fan_in',\n            nonlinearity='leaky_relu')\n    ) -> None:\n        super().__init__(init_cfg)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n\n        conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule\n\n        # build top-down blocks\n        self.upsample = nn.Upsample(**upsample_cfg)\n        self.reduce_layers = nn.ModuleList()\n        self.top_down_blocks = nn.ModuleList()\n        for idx in range(len(in_channels) - 1, 0, -1):\n            self.reduce_layers.append(\n                ConvModule(\n                    in_channels[idx],\n                    in_channels[idx - 1],\n                    1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n            self.top_down_blocks.append(\n                CSPLayer(\n                    in_channels[idx - 1] * 2,\n                    in_channels[idx - 1],\n                    num_blocks=num_csp_blocks,\n                    add_identity=False,\n                    use_depthwise=use_depthwise,\n                    use_cspnext_block=True,\n                    expand_ratio=expand_ratio,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n\n        # build bottom-up blocks\n        self.downsamples = nn.ModuleList()\n        self.bottom_up_blocks = nn.ModuleList()\n        for idx in range(len(in_channels) - 1):\n            self.downsamples.append(\n                conv(\n                    in_channels[idx],\n                    in_channels[idx],\n                    3,\n                    stride=2,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n            self.bottom_up_blocks.append(\n                CSPLayer(\n                    in_channels[idx] * 2,\n                    in_channels[idx + 1],\n                    num_blocks=num_csp_blocks,\n                    add_identity=False,\n                    use_depthwise=use_depthwise,\n                    use_cspnext_block=True,\n                    expand_ratio=expand_ratio,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n\n        self.out_convs = nn.ModuleList()\n        for i in range(len(in_channels)):\n            self.out_convs.append(\n                conv(\n                    in_channels[i],\n                    out_channels,\n                    3,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n\n    def forward(self, inputs: Tuple[Tensor, ...]) -> Tuple[Tensor, ...]:\n        \"\"\"\n        Args:\n            inputs (tuple[Tensor]): input features.\n\n        Returns:\n            tuple[Tensor]: YOLOXPAFPN features.\n        \"\"\"\n        assert len(inputs) == len(self.in_channels)\n\n        # top-down path\n        inner_outs = [inputs[-1]]\n        for idx in range(len(self.in_channels) - 1, 0, -1):\n            feat_heigh = inner_outs[0]\n            feat_low = inputs[idx - 1]\n            feat_heigh = self.reduce_layers[len(self.in_channels) - 1 - idx](\n                feat_heigh)\n            inner_outs[0] = feat_heigh\n\n            upsample_feat = self.upsample(feat_heigh)\n\n            inner_out = self.top_down_blocks[len(self.in_channels) - 1 - idx](\n                torch.cat([upsample_feat, feat_low], 1))\n            inner_outs.insert(0, inner_out)\n\n        # bottom-up path\n        outs = [inner_outs[0]]\n        for idx in range(len(self.in_channels) - 1):\n            feat_low = outs[-1]\n            feat_height = inner_outs[idx + 1]\n            downsample_feat = self.downsamples[idx](feat_low)\n            out = self.bottom_up_blocks[idx](\n                torch.cat([downsample_feat, feat_height], 1))\n            outs.append(out)\n\n        # out convs\n        for idx, conv in enumerate(self.out_convs):\n            outs[idx] = conv(outs[idx])\n\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/ct_resnet_neck.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\nfrom typing import Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptMultiConfig\n\n\n@MODELS.register_module()\nclass CTResNetNeck(BaseModule):\n    \"\"\"The neck used in `CenterNet <https://arxiv.org/abs/1904.07850>`_ for\n    object classification and box regression.\n\n    Args:\n         in_channels (int): Number of input channels.\n         num_deconv_filters (tuple[int]): Number of filters per stage.\n         num_deconv_kernels (tuple[int]): Number of kernels per stage.\n         use_dcn (bool): If True, use DCNv2. Defaults to True.\n         init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n             list[:obj:`ConfigDict`], optional): Initialization\n             config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 num_deconv_filters: Tuple[int, ...],\n                 num_deconv_kernels: Tuple[int, ...],\n                 use_dcn: bool = True,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert len(num_deconv_filters) == len(num_deconv_kernels)\n        self.fp16_enabled = False\n        self.use_dcn = use_dcn\n        self.in_channels = in_channels\n        self.deconv_layers = self._make_deconv_layer(num_deconv_filters,\n                                                     num_deconv_kernels)\n\n    def _make_deconv_layer(\n            self, num_deconv_filters: Tuple[int, ...],\n            num_deconv_kernels: Tuple[int, ...]) -> nn.Sequential:\n        \"\"\"use deconv layers to upsample backbone's output.\"\"\"\n        layers = []\n        for i in range(len(num_deconv_filters)):\n            feat_channels = num_deconv_filters[i]\n            conv_module = ConvModule(\n                self.in_channels,\n                feat_channels,\n                3,\n                padding=1,\n                conv_cfg=dict(type='DCNv2') if self.use_dcn else None,\n                norm_cfg=dict(type='BN'))\n            layers.append(conv_module)\n            upsample_module = ConvModule(\n                feat_channels,\n                feat_channels,\n                num_deconv_kernels[i],\n                stride=2,\n                padding=1,\n                conv_cfg=dict(type='deconv'),\n                norm_cfg=dict(type='BN'))\n            layers.append(upsample_module)\n            self.in_channels = feat_channels\n\n        return nn.Sequential(*layers)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize the parameters.\"\"\"\n        for m in self.modules():\n            if isinstance(m, nn.ConvTranspose2d):\n                # In order to be consistent with the source code,\n                # reset the ConvTranspose2d initialization parameters\n                m.reset_parameters()\n                # Simulated bilinear upsampling kernel\n                w = m.weight.data\n                f = math.ceil(w.size(2) / 2)\n                c = (2 * f - 1 - f % 2) / (2. * f)\n                for i in range(w.size(2)):\n                    for j in range(w.size(3)):\n                        w[0, 0, i, j] = \\\n                            (1 - math.fabs(i / f - c)) * (\n                                    1 - math.fabs(j / f - c))\n                for c in range(1, w.size(0)):\n                    w[c, 0, :, :] = w[0, 0, :, :]\n            elif isinstance(m, nn.BatchNorm2d):\n                nn.init.constant_(m.weight, 1)\n                nn.init.constant_(m.bias, 0)\n            # self.use_dcn is False\n            elif not self.use_dcn and isinstance(m, nn.Conv2d):\n                # In order to be consistent with the source code,\n                # reset the Conv2d initialization parameters\n                m.reset_parameters()\n\n    def forward(self, x: Sequence[torch.Tensor]) -> Tuple[torch.Tensor]:\n        \"\"\"model forward.\"\"\"\n        assert isinstance(x, (list, tuple))\n        outs = self.deconv_layers(x[-1])\n        return outs,\n"
  },
  {
    "path": "mmdet/models/necks/dilated_encoder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, is_norm\nfrom mmengine.model import caffe2_xavier_init, constant_init, normal_init\nfrom torch.nn import BatchNorm2d\n\nfrom mmdet.registry import MODELS\n\n\nclass Bottleneck(nn.Module):\n    \"\"\"Bottleneck block for DilatedEncoder used in `YOLOF.\n\n    <https://arxiv.org/abs/2103.09460>`.\n\n    The Bottleneck contains three ConvLayers and one residual connection.\n\n    Args:\n        in_channels (int): The number of input channels.\n        mid_channels (int): The number of middle output channels.\n        dilation (int): Dilation rate.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 mid_channels,\n                 dilation,\n                 norm_cfg=dict(type='BN', requires_grad=True)):\n        super(Bottleneck, self).__init__()\n        self.conv1 = ConvModule(\n            in_channels, mid_channels, 1, norm_cfg=norm_cfg)\n        self.conv2 = ConvModule(\n            mid_channels,\n            mid_channels,\n            3,\n            padding=dilation,\n            dilation=dilation,\n            norm_cfg=norm_cfg)\n        self.conv3 = ConvModule(\n            mid_channels, in_channels, 1, norm_cfg=norm_cfg)\n\n    def forward(self, x):\n        identity = x\n        out = self.conv1(x)\n        out = self.conv2(out)\n        out = self.conv3(out)\n        out = out + identity\n        return out\n\n\n@MODELS.register_module()\nclass DilatedEncoder(nn.Module):\n    \"\"\"Dilated Encoder for YOLOF <https://arxiv.org/abs/2103.09460>`.\n\n    This module contains two types of components:\n        - the original FPN lateral convolution layer and fpn convolution layer,\n              which are 1x1 conv + 3x3 conv\n        - the dilated residual block\n\n    Args:\n        in_channels (int): The number of input channels.\n        out_channels (int): The number of output channels.\n        block_mid_channels (int): The number of middle block output channels\n        num_residual_blocks (int): The number of residual blocks.\n        block_dilations (list): The list of residual blocks dilation.\n    \"\"\"\n\n    def __init__(self, in_channels, out_channels, block_mid_channels,\n                 num_residual_blocks, block_dilations):\n        super(DilatedEncoder, self).__init__()\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.block_mid_channels = block_mid_channels\n        self.num_residual_blocks = num_residual_blocks\n        self.block_dilations = block_dilations\n        self._init_layers()\n\n    def _init_layers(self):\n        self.lateral_conv = nn.Conv2d(\n            self.in_channels, self.out_channels, kernel_size=1)\n        self.lateral_norm = BatchNorm2d(self.out_channels)\n        self.fpn_conv = nn.Conv2d(\n            self.out_channels, self.out_channels, kernel_size=3, padding=1)\n        self.fpn_norm = BatchNorm2d(self.out_channels)\n        encoder_blocks = []\n        for i in range(self.num_residual_blocks):\n            dilation = self.block_dilations[i]\n            encoder_blocks.append(\n                Bottleneck(\n                    self.out_channels,\n                    self.block_mid_channels,\n                    dilation=dilation))\n        self.dilated_encoder_blocks = nn.Sequential(*encoder_blocks)\n\n    def init_weights(self):\n        caffe2_xavier_init(self.lateral_conv)\n        caffe2_xavier_init(self.fpn_conv)\n        for m in [self.lateral_norm, self.fpn_norm]:\n            constant_init(m, 1)\n        for m in self.dilated_encoder_blocks.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, mean=0, std=0.01)\n            if is_norm(m):\n                constant_init(m, 1)\n\n    def forward(self, feature):\n        out = self.lateral_norm(self.lateral_conv(feature[-1]))\n        out = self.fpn_norm(self.fpn_conv(out))\n        return self.dilated_encoder_blocks(out),\n"
  },
  {
    "path": "mmdet/models/necks/dyhead.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import build_activation_layer, build_norm_layer\nfrom mmcv.ops.modulated_deform_conv import ModulatedDeformConv2d\nfrom mmengine.model import BaseModule, constant_init, normal_init\n\nfrom mmdet.registry import MODELS\nfrom ..layers import DyReLU\n\n# Reference:\n# https://github.com/microsoft/DynamicHead\n# https://github.com/jshilong/SEPC\n\n\nclass DyDCNv2(nn.Module):\n    \"\"\"ModulatedDeformConv2d with normalization layer used in DyHead.\n\n    This module cannot be configured with `conv_cfg=dict(type='DCNv2')`\n    because DyHead calculates offset and mask from middle-level feature.\n\n    Args:\n        in_channels (int): Number of input channels.\n        out_channels (int): Number of output channels.\n        stride (int | tuple[int], optional): Stride of the convolution.\n            Default: 1.\n        norm_cfg (dict, optional): Config dict for normalization layer.\n            Default: dict(type='GN', num_groups=16, requires_grad=True).\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 stride=1,\n                 norm_cfg=dict(type='GN', num_groups=16, requires_grad=True)):\n        super().__init__()\n        self.with_norm = norm_cfg is not None\n        bias = not self.with_norm\n        self.conv = ModulatedDeformConv2d(\n            in_channels, out_channels, 3, stride=stride, padding=1, bias=bias)\n        if self.with_norm:\n            self.norm = build_norm_layer(norm_cfg, out_channels)[1]\n\n    def forward(self, x, offset, mask):\n        \"\"\"Forward function.\"\"\"\n        x = self.conv(x.contiguous(), offset, mask)\n        if self.with_norm:\n            x = self.norm(x)\n        return x\n\n\nclass DyHeadBlock(nn.Module):\n    \"\"\"DyHead Block with three types of attention.\n\n    HSigmoid arguments in default act_cfg follow official code, not paper.\n    https://github.com/microsoft/DynamicHead/blob/master/dyhead/dyrelu.py\n\n    Args:\n        in_channels (int): Number of input channels.\n        out_channels (int): Number of output channels.\n        zero_init_offset (bool, optional): Whether to use zero init for\n            `spatial_conv_offset`. Default: True.\n        act_cfg (dict, optional): Config dict for the last activation layer of\n            scale-aware attention. Default: dict(type='HSigmoid', bias=3.0,\n            divisor=6.0).\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 zero_init_offset=True,\n                 act_cfg=dict(type='HSigmoid', bias=3.0, divisor=6.0)):\n        super().__init__()\n        self.zero_init_offset = zero_init_offset\n        # (offset_x, offset_y, mask) * kernel_size_y * kernel_size_x\n        self.offset_and_mask_dim = 3 * 3 * 3\n        self.offset_dim = 2 * 3 * 3\n\n        self.spatial_conv_high = DyDCNv2(in_channels, out_channels)\n        self.spatial_conv_mid = DyDCNv2(in_channels, out_channels)\n        self.spatial_conv_low = DyDCNv2(in_channels, out_channels, stride=2)\n        self.spatial_conv_offset = nn.Conv2d(\n            in_channels, self.offset_and_mask_dim, 3, padding=1)\n        self.scale_attn_module = nn.Sequential(\n            nn.AdaptiveAvgPool2d(1), nn.Conv2d(out_channels, 1, 1),\n            nn.ReLU(inplace=True), build_activation_layer(act_cfg))\n        self.task_attn_module = DyReLU(out_channels)\n        self._init_weights()\n\n    def _init_weights(self):\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                normal_init(m, 0, 0.01)\n        if self.zero_init_offset:\n            constant_init(self.spatial_conv_offset, 0)\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        outs = []\n        for level in range(len(x)):\n            # calculate offset and mask of DCNv2 from middle-level feature\n            offset_and_mask = self.spatial_conv_offset(x[level])\n            offset = offset_and_mask[:, :self.offset_dim, :, :]\n            mask = offset_and_mask[:, self.offset_dim:, :, :].sigmoid()\n\n            mid_feat = self.spatial_conv_mid(x[level], offset, mask)\n            sum_feat = mid_feat * self.scale_attn_module(mid_feat)\n            summed_levels = 1\n            if level > 0:\n                low_feat = self.spatial_conv_low(x[level - 1], offset, mask)\n                sum_feat += low_feat * self.scale_attn_module(low_feat)\n                summed_levels += 1\n            if level < len(x) - 1:\n                # this upsample order is weird, but faster than natural order\n                # https://github.com/microsoft/DynamicHead/issues/25\n                high_feat = F.interpolate(\n                    self.spatial_conv_high(x[level + 1], offset, mask),\n                    size=x[level].shape[-2:],\n                    mode='bilinear',\n                    align_corners=True)\n                sum_feat += high_feat * self.scale_attn_module(high_feat)\n                summed_levels += 1\n            outs.append(self.task_attn_module(sum_feat / summed_levels))\n\n        return outs\n\n\n@MODELS.register_module()\nclass DyHead(BaseModule):\n    \"\"\"DyHead neck consisting of multiple DyHead Blocks.\n\n    See `Dynamic Head: Unifying Object Detection Heads with Attentions\n    <https://arxiv.org/abs/2106.08322>`_ for details.\n\n    Args:\n        in_channels (int): Number of input channels.\n        out_channels (int): Number of output channels.\n        num_blocks (int, optional): Number of DyHead Blocks. Default: 6.\n        zero_init_offset (bool, optional): Whether to use zero init for\n            `spatial_conv_offset`. Default: True.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_blocks=6,\n                 zero_init_offset=True,\n                 init_cfg=None):\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_blocks = num_blocks\n        self.zero_init_offset = zero_init_offset\n\n        dyhead_blocks = []\n        for i in range(num_blocks):\n            in_channels = self.in_channels if i == 0 else self.out_channels\n            dyhead_blocks.append(\n                DyHeadBlock(\n                    in_channels,\n                    self.out_channels,\n                    zero_init_offset=zero_init_offset))\n        self.dyhead_blocks = nn.Sequential(*dyhead_blocks)\n\n    def forward(self, inputs):\n        \"\"\"Forward function.\"\"\"\n        assert isinstance(inputs, (tuple, list))\n        outs = self.dyhead_blocks(inputs)\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/fpg.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\n\n\nclass Transition(BaseModule):\n    \"\"\"Base class for transition.\n\n    Args:\n        in_channels (int): Number of input channels.\n        out_channels (int): Number of output channels.\n    \"\"\"\n\n    def __init__(self, in_channels, out_channels, init_cfg=None):\n        super().__init__(init_cfg)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n\n    def forward(x):\n        pass\n\n\nclass UpInterpolationConv(Transition):\n    \"\"\"A transition used for up-sampling.\n\n    Up-sample the input by interpolation then refines the feature by\n    a convolution layer.\n\n    Args:\n        in_channels (int): Number of input channels.\n        out_channels (int): Number of output channels.\n        scale_factor (int): Up-sampling factor. Default: 2.\n        mode (int): Interpolation mode. Default: nearest.\n        align_corners (bool): Whether align corners when interpolation.\n            Default: None.\n        kernel_size (int): Kernel size for the conv. Default: 3.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 scale_factor=2,\n                 mode='nearest',\n                 align_corners=None,\n                 kernel_size=3,\n                 init_cfg=None,\n                 **kwargs):\n        super().__init__(in_channels, out_channels, init_cfg)\n        self.mode = mode\n        self.scale_factor = scale_factor\n        self.align_corners = align_corners\n        self.conv = ConvModule(\n            in_channels,\n            out_channels,\n            kernel_size,\n            padding=(kernel_size - 1) // 2,\n            **kwargs)\n\n    def forward(self, x):\n        x = F.interpolate(\n            x,\n            scale_factor=self.scale_factor,\n            mode=self.mode,\n            align_corners=self.align_corners)\n        x = self.conv(x)\n        return x\n\n\nclass LastConv(Transition):\n    \"\"\"A transition used for refining the output of the last stage.\n\n    Args:\n        in_channels (int): Number of input channels.\n        out_channels (int): Number of output channels.\n        num_inputs (int): Number of inputs of the FPN features.\n        kernel_size (int): Kernel size for the conv. Default: 3.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_inputs,\n                 kernel_size=3,\n                 init_cfg=None,\n                 **kwargs):\n        super().__init__(in_channels, out_channels, init_cfg)\n        self.num_inputs = num_inputs\n        self.conv_out = ConvModule(\n            in_channels,\n            out_channels,\n            kernel_size,\n            padding=(kernel_size - 1) // 2,\n            **kwargs)\n\n    def forward(self, inputs):\n        assert len(inputs) == self.num_inputs\n        return self.conv_out(inputs[-1])\n\n\n@MODELS.register_module()\nclass FPG(BaseModule):\n    \"\"\"FPG.\n\n    Implementation of `Feature Pyramid Grids (FPG)\n    <https://arxiv.org/abs/2004.03580>`_.\n    This implementation only gives the basic structure stated in the paper.\n    But users can implement different type of transitions to fully explore the\n    the potential power of the structure of FPG.\n\n    Args:\n        in_channels (int): Number of input channels (feature maps of all levels\n            should have the same channels).\n        out_channels (int): Number of output channels (used at each scale)\n        num_outs (int): Number of output scales.\n        stack_times (int): The number of times the pyramid architecture will\n            be stacked.\n        paths (list[str]): Specify the path order of each stack level.\n            Each element in the list should be either 'bu' (bottom-up) or\n            'td' (top-down).\n        inter_channels (int): Number of inter channels.\n        same_up_trans (dict): Transition that goes down at the same stage.\n        same_down_trans (dict): Transition that goes up at the same stage.\n        across_lateral_trans (dict): Across-pathway same-stage\n        across_down_trans (dict): Across-pathway bottom-up connection.\n        across_up_trans (dict): Across-pathway top-down connection.\n        across_skip_trans (dict): Across-pathway skip connection.\n        output_trans (dict): Transition that trans the output of the\n            last stage.\n        start_level (int): Index of the start input backbone level used to\n            build the feature pyramid. Default: 0.\n        end_level (int): Index of the end input backbone level (exclusive) to\n            build the feature pyramid. Default: -1, which means the last level.\n        add_extra_convs (bool): It decides whether to add conv\n            layers on top of the original feature maps. Default to False.\n            If True, its actual mode is specified by `extra_convs_on_inputs`.\n        norm_cfg (dict): Config dict for normalization layer. Default: None.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    transition_types = {\n        'conv': ConvModule,\n        'interpolation_conv': UpInterpolationConv,\n        'last_conv': LastConv,\n    }\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_outs,\n                 stack_times,\n                 paths,\n                 inter_channels=None,\n                 same_down_trans=None,\n                 same_up_trans=dict(\n                     type='conv', kernel_size=3, stride=2, padding=1),\n                 across_lateral_trans=dict(type='conv', kernel_size=1),\n                 across_down_trans=dict(type='conv', kernel_size=3),\n                 across_up_trans=None,\n                 across_skip_trans=dict(type='identity'),\n                 output_trans=dict(type='last_conv', kernel_size=3),\n                 start_level=0,\n                 end_level=-1,\n                 add_extra_convs=False,\n                 norm_cfg=None,\n                 skip_inds=None,\n                 init_cfg=[\n                     dict(type='Caffe2Xavier', layer='Conv2d'),\n                     dict(\n                         type='Constant',\n                         layer=[\n                             '_BatchNorm', '_InstanceNorm', 'GroupNorm',\n                             'LayerNorm'\n                         ],\n                         val=1.0)\n                 ]):\n        super(FPG, self).__init__(init_cfg)\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)\n        self.num_outs = num_outs\n        if inter_channels is None:\n            self.inter_channels = [out_channels for _ in range(num_outs)]\n        elif isinstance(inter_channels, int):\n            self.inter_channels = [inter_channels for _ in range(num_outs)]\n        else:\n            assert isinstance(inter_channels, list)\n            assert len(inter_channels) == num_outs\n            self.inter_channels = inter_channels\n        self.stack_times = stack_times\n        self.paths = paths\n        assert isinstance(paths, list) and len(paths) == stack_times\n        for d in paths:\n            assert d in ('bu', 'td')\n\n        self.same_down_trans = same_down_trans\n        self.same_up_trans = same_up_trans\n        self.across_lateral_trans = across_lateral_trans\n        self.across_down_trans = across_down_trans\n        self.across_up_trans = across_up_trans\n        self.output_trans = output_trans\n        self.across_skip_trans = across_skip_trans\n\n        self.with_bias = norm_cfg is None\n        # skip inds must be specified if across skip trans is not None\n        if self.across_skip_trans is not None:\n            skip_inds is not None\n        self.skip_inds = skip_inds\n        assert len(self.skip_inds[0]) <= self.stack_times\n\n        if end_level == -1 or end_level == self.num_ins - 1:\n            self.backbone_end_level = self.num_ins\n            assert num_outs >= self.num_ins - start_level\n        else:\n            # if end_level is not the last level, no extra level is allowed\n            self.backbone_end_level = end_level + 1\n            assert end_level < self.num_ins\n            assert num_outs == end_level - start_level + 1\n        self.start_level = start_level\n        self.end_level = end_level\n        self.add_extra_convs = add_extra_convs\n\n        # build lateral 1x1 convs to reduce channels\n        self.lateral_convs = nn.ModuleList()\n        for i in range(self.start_level, self.backbone_end_level):\n            l_conv = nn.Conv2d(self.in_channels[i],\n                               self.inter_channels[i - self.start_level], 1)\n            self.lateral_convs.append(l_conv)\n\n        extra_levels = num_outs - self.backbone_end_level + self.start_level\n        self.extra_downsamples = nn.ModuleList()\n        for i in range(extra_levels):\n            if self.add_extra_convs:\n                fpn_idx = self.backbone_end_level - self.start_level + i\n                extra_conv = nn.Conv2d(\n                    self.inter_channels[fpn_idx - 1],\n                    self.inter_channels[fpn_idx],\n                    3,\n                    stride=2,\n                    padding=1)\n                self.extra_downsamples.append(extra_conv)\n            else:\n                self.extra_downsamples.append(nn.MaxPool2d(1, stride=2))\n\n        self.fpn_transitions = nn.ModuleList()  # stack times\n        for s in range(self.stack_times):\n            stage_trans = nn.ModuleList()  # num of feature levels\n            for i in range(self.num_outs):\n                # same, across_lateral, across_down, across_up\n                trans = nn.ModuleDict()\n                if s in self.skip_inds[i]:\n                    stage_trans.append(trans)\n                    continue\n                # build same-stage down trans (used in bottom-up paths)\n                if i == 0 or self.same_up_trans is None:\n                    same_up_trans = None\n                else:\n                    same_up_trans = self.build_trans(\n                        self.same_up_trans, self.inter_channels[i - 1],\n                        self.inter_channels[i])\n                trans['same_up'] = same_up_trans\n                # build same-stage up trans (used in top-down paths)\n                if i == self.num_outs - 1 or self.same_down_trans is None:\n                    same_down_trans = None\n                else:\n                    same_down_trans = self.build_trans(\n                        self.same_down_trans, self.inter_channels[i + 1],\n                        self.inter_channels[i])\n                trans['same_down'] = same_down_trans\n                # build across lateral trans\n                across_lateral_trans = self.build_trans(\n                    self.across_lateral_trans, self.inter_channels[i],\n                    self.inter_channels[i])\n                trans['across_lateral'] = across_lateral_trans\n                # build across down trans\n                if i == self.num_outs - 1 or self.across_down_trans is None:\n                    across_down_trans = None\n                else:\n                    across_down_trans = self.build_trans(\n                        self.across_down_trans, self.inter_channels[i + 1],\n                        self.inter_channels[i])\n                trans['across_down'] = across_down_trans\n                # build across up trans\n                if i == 0 or self.across_up_trans is None:\n                    across_up_trans = None\n                else:\n                    across_up_trans = self.build_trans(\n                        self.across_up_trans, self.inter_channels[i - 1],\n                        self.inter_channels[i])\n                trans['across_up'] = across_up_trans\n                if self.across_skip_trans is None:\n                    across_skip_trans = None\n                else:\n                    across_skip_trans = self.build_trans(\n                        self.across_skip_trans, self.inter_channels[i - 1],\n                        self.inter_channels[i])\n                trans['across_skip'] = across_skip_trans\n                # build across_skip trans\n                stage_trans.append(trans)\n            self.fpn_transitions.append(stage_trans)\n\n        self.output_transition = nn.ModuleList()  # output levels\n        for i in range(self.num_outs):\n            trans = self.build_trans(\n                self.output_trans,\n                self.inter_channels[i],\n                self.out_channels,\n                num_inputs=self.stack_times + 1)\n            self.output_transition.append(trans)\n\n        self.relu = nn.ReLU(inplace=True)\n\n    def build_trans(self, cfg, in_channels, out_channels, **extra_args):\n        cfg_ = cfg.copy()\n        trans_type = cfg_.pop('type')\n        trans_cls = self.transition_types[trans_type]\n        return trans_cls(in_channels, out_channels, **cfg_, **extra_args)\n\n    def fuse(self, fuse_dict):\n        out = None\n        for item in fuse_dict.values():\n            if item is not None:\n                if out is None:\n                    out = item\n                else:\n                    out = out + item\n        return out\n\n    def forward(self, inputs):\n        assert len(inputs) == len(self.in_channels)\n\n        # build all levels from original feature maps\n        feats = [\n            lateral_conv(inputs[i + self.start_level])\n            for i, lateral_conv in enumerate(self.lateral_convs)\n        ]\n        for downsample in self.extra_downsamples:\n            feats.append(downsample(feats[-1]))\n\n        outs = [feats]\n\n        for i in range(self.stack_times):\n            current_outs = outs[-1]\n            next_outs = []\n            direction = self.paths[i]\n            for j in range(self.num_outs):\n                if i in self.skip_inds[j]:\n                    next_outs.append(outs[-1][j])\n                    continue\n                # feature level\n                if direction == 'td':\n                    lvl = self.num_outs - j - 1\n                else:\n                    lvl = j\n                # get transitions\n                if direction == 'td':\n                    same_trans = self.fpn_transitions[i][lvl]['same_down']\n                else:\n                    same_trans = self.fpn_transitions[i][lvl]['same_up']\n                across_lateral_trans = self.fpn_transitions[i][lvl][\n                    'across_lateral']\n                across_down_trans = self.fpn_transitions[i][lvl]['across_down']\n                across_up_trans = self.fpn_transitions[i][lvl]['across_up']\n                across_skip_trans = self.fpn_transitions[i][lvl]['across_skip']\n                # init output\n                to_fuse = dict(\n                    same=None, lateral=None, across_up=None, across_down=None)\n                # same downsample/upsample\n                if same_trans is not None:\n                    to_fuse['same'] = same_trans(next_outs[-1])\n                # across lateral\n                if across_lateral_trans is not None:\n                    to_fuse['lateral'] = across_lateral_trans(\n                        current_outs[lvl])\n                # across downsample\n                if lvl > 0 and across_up_trans is not None:\n                    to_fuse['across_up'] = across_up_trans(current_outs[lvl -\n                                                                        1])\n                # across upsample\n                if (lvl < self.num_outs - 1 and across_down_trans is not None):\n                    to_fuse['across_down'] = across_down_trans(\n                        current_outs[lvl + 1])\n                if across_skip_trans is not None:\n                    to_fuse['across_skip'] = across_skip_trans(outs[0][lvl])\n                x = self.fuse(to_fuse)\n                next_outs.append(x)\n\n            if direction == 'td':\n                outs.append(next_outs[::-1])\n            else:\n                outs.append(next_outs)\n\n        # output trans\n        final_outs = []\n        for i in range(self.num_outs):\n            lvl_out_list = []\n            for s in range(len(outs)):\n                lvl_out_list.append(outs[s][i])\n            lvl_out = self.output_transition[i](lvl_out_list)\n            final_outs.append(lvl_out)\n\n        return final_outs\n"
  },
  {
    "path": "mmdet/models/necks/fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, MultiConfig, OptConfigType\n\n\n@MODELS.register_module()\nclass FPN(BaseModule):\n    r\"\"\"Feature Pyramid Network.\n\n    This is an implementation of paper `Feature Pyramid Networks for Object\n    Detection <https://arxiv.org/abs/1612.03144>`_.\n\n    Args:\n        in_channels (list[int]): Number of input channels per scale.\n        out_channels (int): Number of output channels (used at each scale).\n        num_outs (int): Number of output scales.\n        start_level (int): Index of the start input backbone level used to\n            build the feature pyramid. Defaults to 0.\n        end_level (int): Index of the end input backbone level (exclusive) to\n            build the feature pyramid. Defaults to -1, which means the\n            last level.\n        add_extra_convs (bool | str): If bool, it decides whether to add conv\n            layers on top of the original feature maps. Defaults to False.\n            If True, it is equivalent to `add_extra_convs='on_input'`.\n            If str, it specifies the source feature map of the extra convs.\n            Only the following options are allowed\n\n            - 'on_input': Last feat map of neck inputs (i.e. backbone feature).\n            - 'on_lateral': Last feature map after lateral convs.\n            - 'on_output': The last output feature map after fpn convs.\n        relu_before_extra_convs (bool): Whether to apply relu before the extra\n            conv. Defaults to False.\n        no_norm_on_lateral (bool): Whether to apply norm on lateral.\n            Defaults to False.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            normalization layer. Defaults to None.\n        act_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            activation layer in ConvModule. Defaults to None.\n        upsample_cfg (:obj:`ConfigDict` or dict, optional): Config dict\n            for interpolate layer. Defaults to dict(mode='nearest').\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n\n    Example:\n        >>> import torch\n        >>> in_channels = [2, 3, 5, 7]\n        >>> scales = [340, 170, 84, 43]\n        >>> inputs = [torch.rand(1, c, s, s)\n        ...           for c, s in zip(in_channels, scales)]\n        >>> self = FPN(in_channels, 11, len(in_channels)).eval()\n        >>> outputs = self.forward(inputs)\n        >>> for i in range(len(outputs)):\n        ...     print(f'outputs[{i}].shape = {outputs[i].shape}')\n        outputs[0].shape = torch.Size([1, 11, 340, 340])\n        outputs[1].shape = torch.Size([1, 11, 170, 170])\n        outputs[2].shape = torch.Size([1, 11, 84, 84])\n        outputs[3].shape = torch.Size([1, 11, 43, 43])\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: List[int],\n        out_channels: int,\n        num_outs: int,\n        start_level: int = 0,\n        end_level: int = -1,\n        add_extra_convs: Union[bool, str] = False,\n        relu_before_extra_convs: bool = False,\n        no_norm_on_lateral: bool = False,\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        act_cfg: OptConfigType = None,\n        upsample_cfg: ConfigType = dict(mode='nearest'),\n        init_cfg: MultiConfig = dict(\n            type='Xavier', layer='Conv2d', distribution='uniform')\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)\n        self.num_outs = num_outs\n        self.relu_before_extra_convs = relu_before_extra_convs\n        self.no_norm_on_lateral = no_norm_on_lateral\n        self.fp16_enabled = False\n        self.upsample_cfg = upsample_cfg.copy()\n\n        if end_level == -1 or end_level == self.num_ins - 1:\n            self.backbone_end_level = self.num_ins\n            assert num_outs >= self.num_ins - start_level\n        else:\n            # if end_level is not the last level, no extra level is allowed\n            self.backbone_end_level = end_level + 1\n            assert end_level < self.num_ins\n            assert num_outs == end_level - start_level + 1\n        self.start_level = start_level\n        self.end_level = end_level\n        self.add_extra_convs = add_extra_convs\n        assert isinstance(add_extra_convs, (str, bool))\n        if isinstance(add_extra_convs, str):\n            # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output'\n            assert add_extra_convs in ('on_input', 'on_lateral', 'on_output')\n        elif add_extra_convs:  # True\n            self.add_extra_convs = 'on_input'\n\n        self.lateral_convs = nn.ModuleList()\n        self.fpn_convs = nn.ModuleList()\n\n        for i in range(self.start_level, self.backbone_end_level):\n            l_conv = ConvModule(\n                in_channels[i],\n                out_channels,\n                1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg if not self.no_norm_on_lateral else None,\n                act_cfg=act_cfg,\n                inplace=False)\n            fpn_conv = ConvModule(\n                out_channels,\n                out_channels,\n                3,\n                padding=1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg,\n                inplace=False)\n\n            self.lateral_convs.append(l_conv)\n            self.fpn_convs.append(fpn_conv)\n\n        # add extra conv layers (e.g., RetinaNet)\n        extra_levels = num_outs - self.backbone_end_level + self.start_level\n        if self.add_extra_convs and extra_levels >= 1:\n            for i in range(extra_levels):\n                if i == 0 and self.add_extra_convs == 'on_input':\n                    in_channels = self.in_channels[self.backbone_end_level - 1]\n                else:\n                    in_channels = out_channels\n                extra_fpn_conv = ConvModule(\n                    in_channels,\n                    out_channels,\n                    3,\n                    stride=2,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg,\n                    inplace=False)\n                self.fpn_convs.append(extra_fpn_conv)\n\n    def forward(self, inputs: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward function.\n\n        Args:\n            inputs (tuple[Tensor]): Features from the upstream network, each\n                is a 4D-tensor.\n\n        Returns:\n            tuple: Feature maps, each is a 4D-tensor.\n        \"\"\"\n        assert len(inputs) == len(self.in_channels)\n\n        # build laterals\n        laterals = [\n            lateral_conv(inputs[i + self.start_level])\n            for i, lateral_conv in enumerate(self.lateral_convs)\n        ]\n\n        # build top-down path\n        used_backbone_levels = len(laterals)\n        for i in range(used_backbone_levels - 1, 0, -1):\n            # In some cases, fixing `scale factor` (e.g. 2) is preferred, but\n            #  it cannot co-exist with `size` in `F.interpolate`.\n            if 'scale_factor' in self.upsample_cfg:\n                # fix runtime error of \"+=\" inplace operation in PyTorch 1.10\n                laterals[i - 1] = laterals[i - 1] + F.interpolate(\n                    laterals[i], **self.upsample_cfg)\n            else:\n                prev_shape = laterals[i - 1].shape[2:]\n                laterals[i - 1] = laterals[i - 1] + F.interpolate(\n                    laterals[i], size=prev_shape, **self.upsample_cfg)\n\n        # build outputs\n        # part 1: from original levels\n        outs = [\n            self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)\n        ]\n        # part 2: add extra levels\n        if self.num_outs > len(outs):\n            # use max pool to get more levels on top of outputs\n            # (e.g., Faster R-CNN, Mask R-CNN)\n            if not self.add_extra_convs:\n                for i in range(self.num_outs - used_backbone_levels):\n                    outs.append(F.max_pool2d(outs[-1], 1, stride=2))\n            # add conv layers on top of original feature maps (RetinaNet)\n            else:\n                if self.add_extra_convs == 'on_input':\n                    extra_source = inputs[self.backbone_end_level - 1]\n                elif self.add_extra_convs == 'on_lateral':\n                    extra_source = laterals[-1]\n                elif self.add_extra_convs == 'on_output':\n                    extra_source = outs[-1]\n                else:\n                    raise NotImplementedError\n                outs.append(self.fpn_convs[used_backbone_levels](extra_source))\n                for i in range(used_backbone_levels + 1, self.num_outs):\n                    if self.relu_before_extra_convs:\n                        outs.append(self.fpn_convs[i](F.relu(outs[-1])))\n                    else:\n                        outs.append(self.fpn_convs[i](outs[-1]))\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/fpn_carafe.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, build_upsample_layer\nfrom mmcv.ops.carafe import CARAFEPack\nfrom mmengine.model import BaseModule, ModuleList, xavier_init\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass FPN_CARAFE(BaseModule):\n    \"\"\"FPN_CARAFE is a more flexible implementation of FPN. It allows more\n    choice for upsample methods during the top-down pathway.\n\n    It can reproduce the performance of ICCV 2019 paper\n    CARAFE: Content-Aware ReAssembly of FEatures\n    Please refer to https://arxiv.org/abs/1905.02188 for more details.\n\n    Args:\n        in_channels (list[int]): Number of channels for each input feature map.\n        out_channels (int): Output channels of feature pyramids.\n        num_outs (int): Number of output stages.\n        start_level (int): Start level of feature pyramids.\n            (Default: 0)\n        end_level (int): End level of feature pyramids.\n            (Default: -1 indicates the last level).\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n        activate (str): Type of activation function in ConvModule\n            (Default: None indicates w/o activation).\n        order (dict): Order of components in ConvModule.\n        upsample (str): Type of upsample layer.\n        upsample_cfg (dict): Dictionary to construct and config upsample layer.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_outs,\n                 start_level=0,\n                 end_level=-1,\n                 norm_cfg=None,\n                 act_cfg=None,\n                 order=('conv', 'norm', 'act'),\n                 upsample_cfg=dict(\n                     type='carafe',\n                     up_kernel=5,\n                     up_group=1,\n                     encoder_kernel=3,\n                     encoder_dilation=1),\n                 init_cfg=None):\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super(FPN_CARAFE, self).__init__(init_cfg)\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)\n        self.num_outs = num_outs\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n        self.with_bias = norm_cfg is None\n        self.upsample_cfg = upsample_cfg.copy()\n        self.upsample = self.upsample_cfg.get('type')\n        self.relu = nn.ReLU(inplace=False)\n\n        self.order = order\n        assert order in [('conv', 'norm', 'act'), ('act', 'conv', 'norm')]\n\n        assert self.upsample in [\n            'nearest', 'bilinear', 'deconv', 'pixel_shuffle', 'carafe', None\n        ]\n        if self.upsample in ['deconv', 'pixel_shuffle']:\n            assert hasattr(\n                self.upsample_cfg,\n                'upsample_kernel') and self.upsample_cfg.upsample_kernel > 0\n            self.upsample_kernel = self.upsample_cfg.pop('upsample_kernel')\n\n        if end_level == -1 or end_level == self.num_ins - 1:\n            self.backbone_end_level = self.num_ins\n            assert num_outs >= self.num_ins - start_level\n        else:\n            # if end_level is not the last level, no extra level is allowed\n            self.backbone_end_level = end_level + 1\n            assert end_level < self.num_ins\n            assert num_outs == end_level - start_level + 1\n        self.start_level = start_level\n        self.end_level = end_level\n\n        self.lateral_convs = ModuleList()\n        self.fpn_convs = ModuleList()\n        self.upsample_modules = ModuleList()\n\n        for i in range(self.start_level, self.backbone_end_level):\n            l_conv = ConvModule(\n                in_channels[i],\n                out_channels,\n                1,\n                norm_cfg=norm_cfg,\n                bias=self.with_bias,\n                act_cfg=act_cfg,\n                inplace=False,\n                order=self.order)\n            fpn_conv = ConvModule(\n                out_channels,\n                out_channels,\n                3,\n                padding=1,\n                norm_cfg=self.norm_cfg,\n                bias=self.with_bias,\n                act_cfg=act_cfg,\n                inplace=False,\n                order=self.order)\n            if i != self.backbone_end_level - 1:\n                upsample_cfg_ = self.upsample_cfg.copy()\n                if self.upsample == 'deconv':\n                    upsample_cfg_.update(\n                        in_channels=out_channels,\n                        out_channels=out_channels,\n                        kernel_size=self.upsample_kernel,\n                        stride=2,\n                        padding=(self.upsample_kernel - 1) // 2,\n                        output_padding=(self.upsample_kernel - 1) // 2)\n                elif self.upsample == 'pixel_shuffle':\n                    upsample_cfg_.update(\n                        in_channels=out_channels,\n                        out_channels=out_channels,\n                        scale_factor=2,\n                        upsample_kernel=self.upsample_kernel)\n                elif self.upsample == 'carafe':\n                    upsample_cfg_.update(channels=out_channels, scale_factor=2)\n                else:\n                    # suppress warnings\n                    align_corners = (None\n                                     if self.upsample == 'nearest' else False)\n                    upsample_cfg_.update(\n                        scale_factor=2,\n                        mode=self.upsample,\n                        align_corners=align_corners)\n                upsample_module = build_upsample_layer(upsample_cfg_)\n                self.upsample_modules.append(upsample_module)\n            self.lateral_convs.append(l_conv)\n            self.fpn_convs.append(fpn_conv)\n\n        # add extra conv layers (e.g., RetinaNet)\n        extra_out_levels = (\n            num_outs - self.backbone_end_level + self.start_level)\n        if extra_out_levels >= 1:\n            for i in range(extra_out_levels):\n                in_channels = (\n                    self.in_channels[self.backbone_end_level -\n                                     1] if i == 0 else out_channels)\n                extra_l_conv = ConvModule(\n                    in_channels,\n                    out_channels,\n                    3,\n                    stride=2,\n                    padding=1,\n                    norm_cfg=norm_cfg,\n                    bias=self.with_bias,\n                    act_cfg=act_cfg,\n                    inplace=False,\n                    order=self.order)\n                if self.upsample == 'deconv':\n                    upsampler_cfg_ = dict(\n                        in_channels=out_channels,\n                        out_channels=out_channels,\n                        kernel_size=self.upsample_kernel,\n                        stride=2,\n                        padding=(self.upsample_kernel - 1) // 2,\n                        output_padding=(self.upsample_kernel - 1) // 2)\n                elif self.upsample == 'pixel_shuffle':\n                    upsampler_cfg_ = dict(\n                        in_channels=out_channels,\n                        out_channels=out_channels,\n                        scale_factor=2,\n                        upsample_kernel=self.upsample_kernel)\n                elif self.upsample == 'carafe':\n                    upsampler_cfg_ = dict(\n                        channels=out_channels,\n                        scale_factor=2,\n                        **self.upsample_cfg)\n                else:\n                    # suppress warnings\n                    align_corners = (None\n                                     if self.upsample == 'nearest' else False)\n                    upsampler_cfg_ = dict(\n                        scale_factor=2,\n                        mode=self.upsample,\n                        align_corners=align_corners)\n                upsampler_cfg_['type'] = self.upsample\n                upsample_module = build_upsample_layer(upsampler_cfg_)\n                extra_fpn_conv = ConvModule(\n                    out_channels,\n                    out_channels,\n                    3,\n                    padding=1,\n                    norm_cfg=self.norm_cfg,\n                    bias=self.with_bias,\n                    act_cfg=act_cfg,\n                    inplace=False,\n                    order=self.order)\n                self.upsample_modules.append(upsample_module)\n                self.fpn_convs.append(extra_fpn_conv)\n                self.lateral_convs.append(extra_l_conv)\n\n    # default init_weights for conv(msra) and norm in ConvModule\n    def init_weights(self):\n        \"\"\"Initialize the weights of module.\"\"\"\n        super(FPN_CARAFE, self).init_weights()\n        for m in self.modules():\n            if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)):\n                xavier_init(m, distribution='uniform')\n        for m in self.modules():\n            if isinstance(m, CARAFEPack):\n                m.init_weights()\n\n    def slice_as(self, src, dst):\n        \"\"\"Slice ``src`` as ``dst``\n\n        Note:\n            ``src`` should have the same or larger size than ``dst``.\n\n        Args:\n            src (torch.Tensor): Tensors to be sliced.\n            dst (torch.Tensor): ``src`` will be sliced to have the same\n                size as ``dst``.\n\n        Returns:\n            torch.Tensor: Sliced tensor.\n        \"\"\"\n        assert (src.size(2) >= dst.size(2)) and (src.size(3) >= dst.size(3))\n        if src.size(2) == dst.size(2) and src.size(3) == dst.size(3):\n            return src\n        else:\n            return src[:, :, :dst.size(2), :dst.size(3)]\n\n    def tensor_add(self, a, b):\n        \"\"\"Add tensors ``a`` and ``b`` that might have different sizes.\"\"\"\n        if a.size() == b.size():\n            c = a + b\n        else:\n            c = a + self.slice_as(b, a)\n        return c\n\n    def forward(self, inputs):\n        \"\"\"Forward function.\"\"\"\n        assert len(inputs) == len(self.in_channels)\n\n        # build laterals\n        laterals = []\n        for i, lateral_conv in enumerate(self.lateral_convs):\n            if i <= self.backbone_end_level - self.start_level:\n                input = inputs[min(i + self.start_level, len(inputs) - 1)]\n            else:\n                input = laterals[-1]\n            lateral = lateral_conv(input)\n            laterals.append(lateral)\n\n        # build top-down path\n        for i in range(len(laterals) - 1, 0, -1):\n            if self.upsample is not None:\n                upsample_feat = self.upsample_modules[i - 1](laterals[i])\n            else:\n                upsample_feat = laterals[i]\n            laterals[i - 1] = self.tensor_add(laterals[i - 1], upsample_feat)\n\n        # build outputs\n        num_conv_outs = len(self.fpn_convs)\n        outs = []\n        for i in range(num_conv_outs):\n            out = self.fpn_convs[i](laterals[i])\n            outs.append(out)\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/fpn_dropblock.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple\n\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .fpn import FPN\n\n\n@MODELS.register_module()\nclass FPN_DropBlock(FPN):\n\n    def __init__(self,\n                 *args,\n                 plugin: Optional[dict] = dict(\n                     type='DropBlock',\n                     drop_prob=0.3,\n                     block_size=3,\n                     warmup_iters=0),\n                 **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        self.plugin = None\n        if plugin is not None:\n            self.plugin = MODELS.build(plugin)\n\n    def forward(self, inputs: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward function.\n\n        Args:\n            inputs (tuple[Tensor]): Features from the upstream network, each\n                is a 4D-tensor.\n\n        Returns:\n            tuple: Feature maps, each is a 4D-tensor.\n        \"\"\"\n        assert len(inputs) == len(self.in_channels)\n\n        # build laterals\n        laterals = [\n            lateral_conv(inputs[i + self.start_level])\n            for i, lateral_conv in enumerate(self.lateral_convs)\n        ]\n\n        # build top-down path\n        used_backbone_levels = len(laterals)\n        for i in range(used_backbone_levels - 1, 0, -1):\n            # In some cases, fixing `scale factor` (e.g. 2) is preferred, but\n            #  it cannot co-exist with `size` in `F.interpolate`.\n            if 'scale_factor' in self.upsample_cfg:\n                # fix runtime error of \"+=\" inplace operation in PyTorch 1.10\n                laterals[i - 1] = laterals[i - 1] + F.interpolate(\n                    laterals[i], **self.upsample_cfg)\n            else:\n                prev_shape = laterals[i - 1].shape[2:]\n                laterals[i - 1] = laterals[i - 1] + F.interpolate(\n                    laterals[i], size=prev_shape, **self.upsample_cfg)\n\n            if self.plugin is not None:\n                laterals[i - 1] = self.plugin(laterals[i - 1])\n\n        # build outputs\n        # part 1: from original levels\n        outs = [\n            self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)\n        ]\n        # part 2: add extra levels\n        if self.num_outs > len(outs):\n            # use max pool to get more levels on top of outputs\n            # (e.g., Faster R-CNN, Mask R-CNN)\n            if not self.add_extra_convs:\n                for i in range(self.num_outs - used_backbone_levels):\n                    outs.append(F.max_pool2d(outs[-1], 1, stride=2))\n            # add conv layers on top of original feature maps (RetinaNet)\n            else:\n                if self.add_extra_convs == 'on_input':\n                    extra_source = inputs[self.backbone_end_level - 1]\n                elif self.add_extra_convs == 'on_lateral':\n                    extra_source = laterals[-1]\n                elif self.add_extra_convs == 'on_output':\n                    extra_source = outs[-1]\n                else:\n                    raise NotImplementedError\n                outs.append(self.fpn_convs[used_backbone_levels](extra_source))\n                for i in range(used_backbone_levels + 1, self.num_outs):\n                    if self.relu_before_extra_convs:\n                        outs.append(self.fpn_convs[i](F.relu(outs[-1])))\n                    else:\n                        outs.append(self.fpn_convs[i](outs[-1]))\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/hrfpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch.utils.checkpoint import checkpoint\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass HRFPN(BaseModule):\n    \"\"\"HRFPN (High Resolution Feature Pyramids)\n\n    paper: `High-Resolution Representations for Labeling Pixels and Regions\n    <https://arxiv.org/abs/1904.04514>`_.\n\n    Args:\n        in_channels (list): number of channels for each branch.\n        out_channels (int): output channels of feature pyramids.\n        num_outs (int): number of output stages.\n        pooling_type (str): pooling for generating feature pyramids\n            from {MAX, AVG}.\n        conv_cfg (dict): dictionary to construct and config conv layer.\n        norm_cfg (dict): dictionary to construct and config norm layer.\n        with_cp  (bool): Use checkpoint or not. Using checkpoint will save some\n            memory while slowing down the training speed.\n        stride (int): stride of 3x3 convolutional layers\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_outs=5,\n                 pooling_type='AVG',\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 with_cp=False,\n                 stride=1,\n                 init_cfg=dict(type='Caffe2Xavier', layer='Conv2d')):\n        super(HRFPN, self).__init__(init_cfg)\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)\n        self.num_outs = num_outs\n        self.with_cp = with_cp\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n\n        self.reduction_conv = ConvModule(\n            sum(in_channels),\n            out_channels,\n            kernel_size=1,\n            conv_cfg=self.conv_cfg,\n            act_cfg=None)\n\n        self.fpn_convs = nn.ModuleList()\n        for i in range(self.num_outs):\n            self.fpn_convs.append(\n                ConvModule(\n                    out_channels,\n                    out_channels,\n                    kernel_size=3,\n                    padding=1,\n                    stride=stride,\n                    conv_cfg=self.conv_cfg,\n                    act_cfg=None))\n\n        if pooling_type == 'MAX':\n            self.pooling = F.max_pool2d\n        else:\n            self.pooling = F.avg_pool2d\n\n    def forward(self, inputs):\n        \"\"\"Forward function.\"\"\"\n        assert len(inputs) == self.num_ins\n        outs = [inputs[0]]\n        for i in range(1, self.num_ins):\n            outs.append(\n                F.interpolate(inputs[i], scale_factor=2**i, mode='bilinear'))\n        out = torch.cat(outs, dim=1)\n        if out.requires_grad and self.with_cp:\n            out = checkpoint(self.reduction_conv, out)\n        else:\n            out = self.reduction_conv(out)\n        outs = [out]\n        for i in range(1, self.num_outs):\n            outs.append(self.pooling(out, kernel_size=2**i, stride=2**i))\n        outputs = []\n\n        for i in range(self.num_outs):\n            if outs[i].requires_grad and self.with_cp:\n                tmp_out = checkpoint(self.fpn_convs[i], outs[i])\n            else:\n                tmp_out = self.fpn_convs[i](outs[i])\n            outputs.append(tmp_out)\n        return tuple(outputs)\n"
  },
  {
    "path": "mmdet/models/necks/nas_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops.merge_cells import GlobalPoolingCell, SumCell\nfrom mmengine.model import BaseModule, ModuleList\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig, OptConfigType\n\n\n@MODELS.register_module()\nclass NASFPN(BaseModule):\n    \"\"\"NAS-FPN.\n\n    Implementation of `NAS-FPN: Learning Scalable Feature Pyramid Architecture\n    for Object Detection <https://arxiv.org/abs/1904.07392>`_\n\n    Args:\n        in_channels (List[int]): Number of input channels per scale.\n        out_channels (int): Number of output channels (used at each scale)\n        num_outs (int): Number of output scales.\n        stack_times (int): The number of times the pyramid architecture will\n            be stacked.\n        start_level (int): Index of the start input backbone level used to\n            build the feature pyramid. Defaults to 0.\n        end_level (int): Index of the end input backbone level (exclusive) to\n            build the feature pyramid. Defaults to -1, which means the\n            last level.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            normalization layer. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: List[int],\n        out_channels: int,\n        num_outs: int,\n        stack_times: int,\n        start_level: int = 0,\n        end_level: int = -1,\n        norm_cfg: OptConfigType = None,\n        init_cfg: MultiConfig = dict(type='Caffe2Xavier', layer='Conv2d')\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)  # num of input feature levels\n        self.num_outs = num_outs  # num of output feature levels\n        self.stack_times = stack_times\n        self.norm_cfg = norm_cfg\n\n        if end_level == -1 or end_level == self.num_ins - 1:\n            self.backbone_end_level = self.num_ins\n            assert num_outs >= self.num_ins - start_level\n        else:\n            # if end_level is not the last level, no extra level is allowed\n            self.backbone_end_level = end_level + 1\n            assert end_level < self.num_ins\n            assert num_outs == end_level - start_level + 1\n        self.start_level = start_level\n        self.end_level = end_level\n\n        # add lateral connections\n        self.lateral_convs = nn.ModuleList()\n        for i in range(self.start_level, self.backbone_end_level):\n            l_conv = ConvModule(\n                in_channels[i],\n                out_channels,\n                1,\n                norm_cfg=norm_cfg,\n                act_cfg=None)\n            self.lateral_convs.append(l_conv)\n\n        # add extra downsample layers (stride-2 pooling or conv)\n        extra_levels = num_outs - self.backbone_end_level + self.start_level\n        self.extra_downsamples = nn.ModuleList()\n        for i in range(extra_levels):\n            extra_conv = ConvModule(\n                out_channels, out_channels, 1, norm_cfg=norm_cfg, act_cfg=None)\n            self.extra_downsamples.append(\n                nn.Sequential(extra_conv, nn.MaxPool2d(2, 2)))\n\n        # add NAS FPN connections\n        self.fpn_stages = ModuleList()\n        for _ in range(self.stack_times):\n            stage = nn.ModuleDict()\n            # gp(p6, p4) -> p4_1\n            stage['gp_64_4'] = GlobalPoolingCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                out_norm_cfg=norm_cfg)\n            # sum(p4_1, p4) -> p4_2\n            stage['sum_44_4'] = SumCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                out_norm_cfg=norm_cfg)\n            # sum(p4_2, p3) -> p3_out\n            stage['sum_43_3'] = SumCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                out_norm_cfg=norm_cfg)\n            # sum(p3_out, p4_2) -> p4_out\n            stage['sum_34_4'] = SumCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                out_norm_cfg=norm_cfg)\n            # sum(p5, gp(p4_out, p3_out)) -> p5_out\n            stage['gp_43_5'] = GlobalPoolingCell(with_out_conv=False)\n            stage['sum_55_5'] = SumCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                out_norm_cfg=norm_cfg)\n            # sum(p7, gp(p5_out, p4_2)) -> p7_out\n            stage['gp_54_7'] = GlobalPoolingCell(with_out_conv=False)\n            stage['sum_77_7'] = SumCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                out_norm_cfg=norm_cfg)\n            # gp(p7_out, p5_out) -> p6_out\n            stage['gp_75_6'] = GlobalPoolingCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                out_norm_cfg=norm_cfg)\n            self.fpn_stages.append(stage)\n\n    def forward(self, inputs: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward function.\n\n         Args:\n            inputs (tuple[Tensor]): Features from the upstream network, each\n                is a 4D-tensor.\n\n        Returns:\n            tuple: Feature maps, each is a 4D-tensor.\n        \"\"\"\n        # build P3-P5\n        feats = [\n            lateral_conv(inputs[i + self.start_level])\n            for i, lateral_conv in enumerate(self.lateral_convs)\n        ]\n        # build P6-P7 on top of P5\n        for downsample in self.extra_downsamples:\n            feats.append(downsample(feats[-1]))\n\n        p3, p4, p5, p6, p7 = feats\n\n        for stage in self.fpn_stages:\n            # gp(p6, p4) -> p4_1\n            p4_1 = stage['gp_64_4'](p6, p4, out_size=p4.shape[-2:])\n            # sum(p4_1, p4) -> p4_2\n            p4_2 = stage['sum_44_4'](p4_1, p4, out_size=p4.shape[-2:])\n            # sum(p4_2, p3) -> p3_out\n            p3 = stage['sum_43_3'](p4_2, p3, out_size=p3.shape[-2:])\n            # sum(p3_out, p4_2) -> p4_out\n            p4 = stage['sum_34_4'](p3, p4_2, out_size=p4.shape[-2:])\n            # sum(p5, gp(p4_out, p3_out)) -> p5_out\n            p5_tmp = stage['gp_43_5'](p4, p3, out_size=p5.shape[-2:])\n            p5 = stage['sum_55_5'](p5, p5_tmp, out_size=p5.shape[-2:])\n            # sum(p7, gp(p5_out, p4_2)) -> p7_out\n            p7_tmp = stage['gp_54_7'](p5, p4_2, out_size=p7.shape[-2:])\n            p7 = stage['sum_77_7'](p7, p7_tmp, out_size=p7.shape[-2:])\n            # gp(p7_out, p5_out) -> p6_out\n            p6 = stage['gp_75_6'](p7, p5, out_size=p6.shape[-2:])\n\n        return p3, p4, p5, p6, p7\n"
  },
  {
    "path": "mmdet/models/necks/nasfcos_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops.merge_cells import ConcatCell\nfrom mmengine.model import BaseModule, caffe2_xavier_init\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass NASFCOS_FPN(BaseModule):\n    \"\"\"FPN structure in NASFPN.\n\n    Implementation of paper `NAS-FCOS: Fast Neural Architecture Search for\n    Object Detection <https://arxiv.org/abs/1906.04423>`_\n\n    Args:\n        in_channels (List[int]): Number of input channels per scale.\n        out_channels (int): Number of output channels (used at each scale)\n        num_outs (int): Number of output scales.\n        start_level (int): Index of the start input backbone level used to\n            build the feature pyramid. Default: 0.\n        end_level (int): Index of the end input backbone level (exclusive) to\n            build the feature pyramid. Default: -1, which means the last level.\n        add_extra_convs (bool): It decides whether to add conv\n            layers on top of the original feature maps. Default to False.\n            If True, its actual mode is specified by `extra_convs_on_inputs`.\n        conv_cfg (dict): dictionary to construct and config conv layer.\n        norm_cfg (dict): dictionary to construct and config norm layer.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_outs,\n                 start_level=1,\n                 end_level=-1,\n                 add_extra_convs=False,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 init_cfg=None):\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super(NASFCOS_FPN, self).__init__(init_cfg)\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)\n        self.num_outs = num_outs\n        self.norm_cfg = norm_cfg\n        self.conv_cfg = conv_cfg\n\n        if end_level == -1 or end_level == self.num_ins - 1:\n            self.backbone_end_level = self.num_ins\n            assert num_outs >= self.num_ins - start_level\n        else:\n            # if end_level is not the last level, no extra level is allowed\n            self.backbone_end_level = end_level + 1\n            assert end_level < self.num_ins\n            assert num_outs == end_level - start_level + 1\n        self.start_level = start_level\n        self.end_level = end_level\n        self.add_extra_convs = add_extra_convs\n\n        self.adapt_convs = nn.ModuleList()\n        for i in range(self.start_level, self.backbone_end_level):\n            adapt_conv = ConvModule(\n                in_channels[i],\n                out_channels,\n                1,\n                stride=1,\n                padding=0,\n                bias=False,\n                norm_cfg=dict(type='BN'),\n                act_cfg=dict(type='ReLU', inplace=False))\n            self.adapt_convs.append(adapt_conv)\n\n        # C2 is omitted according to the paper\n        extra_levels = num_outs - self.backbone_end_level + self.start_level\n\n        def build_concat_cell(with_input1_conv, with_input2_conv):\n            cell_conv_cfg = dict(\n                kernel_size=1, padding=0, bias=False, groups=out_channels)\n            return ConcatCell(\n                in_channels=out_channels,\n                out_channels=out_channels,\n                with_out_conv=True,\n                out_conv_cfg=cell_conv_cfg,\n                out_norm_cfg=dict(type='BN'),\n                out_conv_order=('norm', 'act', 'conv'),\n                with_input1_conv=with_input1_conv,\n                with_input2_conv=with_input2_conv,\n                input_conv_cfg=conv_cfg,\n                input_norm_cfg=norm_cfg,\n                upsample_mode='nearest')\n\n        # Denote c3=f0, c4=f1, c5=f2 for convince\n        self.fpn = nn.ModuleDict()\n        self.fpn['c22_1'] = build_concat_cell(True, True)\n        self.fpn['c22_2'] = build_concat_cell(True, True)\n        self.fpn['c32'] = build_concat_cell(True, False)\n        self.fpn['c02'] = build_concat_cell(True, False)\n        self.fpn['c42'] = build_concat_cell(True, True)\n        self.fpn['c36'] = build_concat_cell(True, True)\n        self.fpn['c61'] = build_concat_cell(True, True)  # f9\n        self.extra_downsamples = nn.ModuleList()\n        for i in range(extra_levels):\n            extra_act_cfg = None if i == 0 \\\n                else dict(type='ReLU', inplace=False)\n            self.extra_downsamples.append(\n                ConvModule(\n                    out_channels,\n                    out_channels,\n                    3,\n                    stride=2,\n                    padding=1,\n                    act_cfg=extra_act_cfg,\n                    order=('act', 'norm', 'conv')))\n\n    def forward(self, inputs):\n        \"\"\"Forward function.\"\"\"\n        feats = [\n            adapt_conv(inputs[i + self.start_level])\n            for i, adapt_conv in enumerate(self.adapt_convs)\n        ]\n\n        for (i, module_name) in enumerate(self.fpn):\n            idx_1, idx_2 = int(module_name[1]), int(module_name[2])\n            res = self.fpn[module_name](feats[idx_1], feats[idx_2])\n            feats.append(res)\n\n        ret = []\n        for (idx, input_idx) in zip([9, 8, 7], [1, 2, 3]):  # add P3, P4, P5\n            feats1, feats2 = feats[idx], feats[5]\n            feats2_resize = F.interpolate(\n                feats2,\n                size=feats1.size()[2:],\n                mode='bilinear',\n                align_corners=False)\n\n            feats_sum = feats1 + feats2_resize\n            ret.append(\n                F.interpolate(\n                    feats_sum,\n                    size=inputs[input_idx].size()[2:],\n                    mode='bilinear',\n                    align_corners=False))\n\n        for submodule in self.extra_downsamples:\n            ret.append(submodule(ret[-1]))\n\n        return tuple(ret)\n\n    def init_weights(self):\n        \"\"\"Initialize the weights of module.\"\"\"\n        super(NASFCOS_FPN, self).init_weights()\n        for module in self.fpn.values():\n            if hasattr(module, 'conv_out'):\n                caffe2_xavier_init(module.out_conv.conv)\n\n        for modules in [\n                self.adapt_convs.modules(),\n                self.extra_downsamples.modules()\n        ]:\n            for module in modules:\n                if isinstance(module, nn.Conv2d):\n                    caffe2_xavier_init(module)\n"
  },
  {
    "path": "mmdet/models/necks/pafpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\n\nfrom mmdet.registry import MODELS\nfrom .fpn import FPN\n\n\n@MODELS.register_module()\nclass PAFPN(FPN):\n    \"\"\"Path Aggregation Network for Instance Segmentation.\n\n    This is an implementation of the `PAFPN in Path Aggregation Network\n    <https://arxiv.org/abs/1803.01534>`_.\n\n    Args:\n        in_channels (List[int]): Number of input channels per scale.\n        out_channels (int): Number of output channels (used at each scale)\n        num_outs (int): Number of output scales.\n        start_level (int): Index of the start input backbone level used to\n            build the feature pyramid. Default: 0.\n        end_level (int): Index of the end input backbone level (exclusive) to\n            build the feature pyramid. Default: -1, which means the last level.\n        add_extra_convs (bool | str): If bool, it decides whether to add conv\n            layers on top of the original feature maps. Default to False.\n            If True, it is equivalent to `add_extra_convs='on_input'`.\n            If str, it specifies the source feature map of the extra convs.\n            Only the following options are allowed\n\n            - 'on_input': Last feat map of neck inputs (i.e. backbone feature).\n            - 'on_lateral':  Last feature map after lateral convs.\n            - 'on_output': The last output feature map after fpn convs.\n        relu_before_extra_convs (bool): Whether to apply relu before the extra\n            conv. Default: False.\n        no_norm_on_lateral (bool): Whether to apply norm on lateral.\n            Default: False.\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Config dict for normalization layer. Default: None.\n        act_cfg (str): Config dict for activation layer in ConvModule.\n            Default: None.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_outs,\n                 start_level=0,\n                 end_level=-1,\n                 add_extra_convs=False,\n                 relu_before_extra_convs=False,\n                 no_norm_on_lateral=False,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 act_cfg=None,\n                 init_cfg=dict(\n                     type='Xavier', layer='Conv2d', distribution='uniform')):\n        super(PAFPN, self).__init__(\n            in_channels,\n            out_channels,\n            num_outs,\n            start_level,\n            end_level,\n            add_extra_convs,\n            relu_before_extra_convs,\n            no_norm_on_lateral,\n            conv_cfg,\n            norm_cfg,\n            act_cfg,\n            init_cfg=init_cfg)\n        # add extra bottom up pathway\n        self.downsample_convs = nn.ModuleList()\n        self.pafpn_convs = nn.ModuleList()\n        for i in range(self.start_level + 1, self.backbone_end_level):\n            d_conv = ConvModule(\n                out_channels,\n                out_channels,\n                3,\n                stride=2,\n                padding=1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg,\n                inplace=False)\n            pafpn_conv = ConvModule(\n                out_channels,\n                out_channels,\n                3,\n                padding=1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg,\n                inplace=False)\n            self.downsample_convs.append(d_conv)\n            self.pafpn_convs.append(pafpn_conv)\n\n    def forward(self, inputs):\n        \"\"\"Forward function.\"\"\"\n        assert len(inputs) == len(self.in_channels)\n\n        # build laterals\n        laterals = [\n            lateral_conv(inputs[i + self.start_level])\n            for i, lateral_conv in enumerate(self.lateral_convs)\n        ]\n\n        # build top-down path\n        used_backbone_levels = len(laterals)\n        for i in range(used_backbone_levels - 1, 0, -1):\n            prev_shape = laterals[i - 1].shape[2:]\n            laterals[i - 1] = laterals[i - 1] + F.interpolate(\n                laterals[i], size=prev_shape, mode='nearest')\n\n        # build outputs\n        # part 1: from original levels\n        inter_outs = [\n            self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)\n        ]\n\n        # part 2: add bottom-up path\n        for i in range(0, used_backbone_levels - 1):\n            inter_outs[i + 1] = inter_outs[i + 1] + \\\n                                self.downsample_convs[i](inter_outs[i])\n\n        outs = []\n        outs.append(inter_outs[0])\n        outs.extend([\n            self.pafpn_convs[i - 1](inter_outs[i])\n            for i in range(1, used_backbone_levels)\n        ])\n\n        # part 3: add extra levels\n        if self.num_outs > len(outs):\n            # use max pool to get more levels on top of outputs\n            # (e.g., Faster R-CNN, Mask R-CNN)\n            if not self.add_extra_convs:\n                for i in range(self.num_outs - used_backbone_levels):\n                    outs.append(F.max_pool2d(outs[-1], 1, stride=2))\n            # add conv layers on top of original feature maps (RetinaNet)\n            else:\n                if self.add_extra_convs == 'on_input':\n                    orig = inputs[self.backbone_end_level - 1]\n                    outs.append(self.fpn_convs[used_backbone_levels](orig))\n                elif self.add_extra_convs == 'on_lateral':\n                    outs.append(self.fpn_convs[used_backbone_levels](\n                        laterals[-1]))\n                elif self.add_extra_convs == 'on_output':\n                    outs.append(self.fpn_convs[used_backbone_levels](outs[-1]))\n                else:\n                    raise NotImplementedError\n                for i in range(used_backbone_levels + 1, self.num_outs):\n                    if self.relu_before_extra_convs:\n                        outs.append(self.fpn_convs[i](F.relu(outs[-1])))\n                    else:\n                        outs.append(self.fpn_convs[i](outs[-1]))\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/rfp.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.model import BaseModule, ModuleList, constant_init, xavier_init\n\nfrom mmdet.registry import MODELS\nfrom .fpn import FPN\n\n\nclass ASPP(BaseModule):\n    \"\"\"ASPP (Atrous Spatial Pyramid Pooling)\n\n    This is an implementation of the ASPP module used in DetectoRS\n    (https://arxiv.org/pdf/2006.02334.pdf)\n\n    Args:\n        in_channels (int): Number of input channels.\n        out_channels (int): Number of channels produced by this module\n        dilations (tuple[int]): Dilations of the four branches.\n            Default: (1, 3, 6, 1)\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 dilations=(1, 3, 6, 1),\n                 init_cfg=dict(type='Kaiming', layer='Conv2d')):\n        super().__init__(init_cfg)\n        assert dilations[-1] == 1\n        self.aspp = nn.ModuleList()\n        for dilation in dilations:\n            kernel_size = 3 if dilation > 1 else 1\n            padding = dilation if dilation > 1 else 0\n            conv = nn.Conv2d(\n                in_channels,\n                out_channels,\n                kernel_size=kernel_size,\n                stride=1,\n                dilation=dilation,\n                padding=padding,\n                bias=True)\n            self.aspp.append(conv)\n        self.gap = nn.AdaptiveAvgPool2d(1)\n\n    def forward(self, x):\n        avg_x = self.gap(x)\n        out = []\n        for aspp_idx in range(len(self.aspp)):\n            inp = avg_x if (aspp_idx == len(self.aspp) - 1) else x\n            out.append(F.relu_(self.aspp[aspp_idx](inp)))\n        out[-1] = out[-1].expand_as(out[-2])\n        out = torch.cat(out, dim=1)\n        return out\n\n\n@MODELS.register_module()\nclass RFP(FPN):\n    \"\"\"RFP (Recursive Feature Pyramid)\n\n    This is an implementation of RFP in `DetectoRS\n    <https://arxiv.org/pdf/2006.02334.pdf>`_. Different from standard FPN, the\n    input of RFP should be multi level features along with origin input image\n    of backbone.\n\n    Args:\n        rfp_steps (int): Number of unrolled steps of RFP.\n        rfp_backbone (dict): Configuration of the backbone for RFP.\n        aspp_out_channels (int): Number of output channels of ASPP module.\n        aspp_dilations (tuple[int]): Dilation rates of four branches.\n            Default: (1, 3, 6, 1)\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 rfp_steps,\n                 rfp_backbone,\n                 aspp_out_channels,\n                 aspp_dilations=(1, 3, 6, 1),\n                 init_cfg=None,\n                 **kwargs):\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super().__init__(init_cfg=init_cfg, **kwargs)\n        self.rfp_steps = rfp_steps\n        # Be careful! Pretrained weights cannot be loaded when use\n        # nn.ModuleList\n        self.rfp_modules = ModuleList()\n        for rfp_idx in range(1, rfp_steps):\n            rfp_module = MODELS.build(rfp_backbone)\n            self.rfp_modules.append(rfp_module)\n        self.rfp_aspp = ASPP(self.out_channels, aspp_out_channels,\n                             aspp_dilations)\n        self.rfp_weight = nn.Conv2d(\n            self.out_channels,\n            1,\n            kernel_size=1,\n            stride=1,\n            padding=0,\n            bias=True)\n\n    def init_weights(self):\n        # Avoid using super().init_weights(), which may alter the default\n        # initialization of the modules in self.rfp_modules that have missing\n        # keys in the pretrained checkpoint.\n        for convs in [self.lateral_convs, self.fpn_convs]:\n            for m in convs.modules():\n                if isinstance(m, nn.Conv2d):\n                    xavier_init(m, distribution='uniform')\n        for rfp_idx in range(self.rfp_steps - 1):\n            self.rfp_modules[rfp_idx].init_weights()\n        constant_init(self.rfp_weight, 0)\n\n    def forward(self, inputs):\n        inputs = list(inputs)\n        assert len(inputs) == len(self.in_channels) + 1  # +1 for input image\n        img = inputs.pop(0)\n        # FPN forward\n        x = super().forward(tuple(inputs))\n        for rfp_idx in range(self.rfp_steps - 1):\n            rfp_feats = [x[0]] + list(\n                self.rfp_aspp(x[i]) for i in range(1, len(x)))\n            x_idx = self.rfp_modules[rfp_idx].rfp_forward(img, rfp_feats)\n            # FPN forward\n            x_idx = super().forward(x_idx)\n            x_new = []\n            for ft_idx in range(len(x_idx)):\n                add_weight = torch.sigmoid(self.rfp_weight(x_idx[ft_idx]))\n                x_new.append(add_weight * x_idx[ft_idx] +\n                             (1 - add_weight) * x[ft_idx])\n            x = x_new\n        return x\n"
  },
  {
    "path": "mmdet/models/necks/ssd_neck.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass SSDNeck(BaseModule):\n    \"\"\"Extra layers of SSD backbone to generate multi-scale feature maps.\n\n    Args:\n        in_channels (Sequence[int]): Number of input channels per scale.\n        out_channels (Sequence[int]): Number of output channels per scale.\n        level_strides (Sequence[int]): Stride of 3x3 conv per level.\n        level_paddings (Sequence[int]): Padding size of 3x3 conv per level.\n        l2_norm_scale (float|None): L2 normalization layer init scale.\n            If None, not use L2 normalization on the first input feature.\n        last_kernel_size (int): Kernel size of the last conv layer.\n            Default: 3.\n        use_depthwise (bool): Whether to use DepthwiseSeparableConv.\n            Default: False.\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n            Default: None.\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='ReLU').\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 level_strides,\n                 level_paddings,\n                 l2_norm_scale=20.,\n                 last_kernel_size=3,\n                 use_depthwise=False,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 act_cfg=dict(type='ReLU'),\n                 init_cfg=[\n                     dict(\n                         type='Xavier', distribution='uniform',\n                         layer='Conv2d'),\n                     dict(type='Constant', val=1, layer='BatchNorm2d'),\n                 ]):\n        super(SSDNeck, self).__init__(init_cfg)\n        assert len(out_channels) > len(in_channels)\n        assert len(out_channels) - len(in_channels) == len(level_strides)\n        assert len(level_strides) == len(level_paddings)\n        assert in_channels == out_channels[:len(in_channels)]\n\n        if l2_norm_scale:\n            self.l2_norm = L2Norm(in_channels[0], l2_norm_scale)\n            self.init_cfg += [\n                dict(\n                    type='Constant',\n                    val=self.l2_norm.scale,\n                    override=dict(name='l2_norm'))\n            ]\n\n        self.extra_layers = nn.ModuleList()\n        extra_layer_channels = out_channels[len(in_channels):]\n        second_conv = DepthwiseSeparableConvModule if \\\n            use_depthwise else ConvModule\n\n        for i, (out_channel, stride, padding) in enumerate(\n                zip(extra_layer_channels, level_strides, level_paddings)):\n            kernel_size = last_kernel_size \\\n                if i == len(extra_layer_channels) - 1 else 3\n            per_lvl_convs = nn.Sequential(\n                ConvModule(\n                    out_channels[len(in_channels) - 1 + i],\n                    out_channel // 2,\n                    1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg),\n                second_conv(\n                    out_channel // 2,\n                    out_channel,\n                    kernel_size,\n                    stride=stride,\n                    padding=padding,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n            self.extra_layers.append(per_lvl_convs)\n\n    def forward(self, inputs):\n        \"\"\"Forward function.\"\"\"\n        outs = [feat for feat in inputs]\n        if hasattr(self, 'l2_norm'):\n            outs[0] = self.l2_norm(outs[0])\n\n        feat = outs[-1]\n        for layer in self.extra_layers:\n            feat = layer(feat)\n            outs.append(feat)\n        return tuple(outs)\n\n\nclass L2Norm(nn.Module):\n\n    def __init__(self, n_dims, scale=20., eps=1e-10):\n        \"\"\"L2 normalization layer.\n\n        Args:\n            n_dims (int): Number of dimensions to be normalized\n            scale (float, optional): Defaults to 20..\n            eps (float, optional): Used to avoid division by zero.\n                Defaults to 1e-10.\n        \"\"\"\n        super(L2Norm, self).__init__()\n        self.n_dims = n_dims\n        self.weight = nn.Parameter(torch.Tensor(self.n_dims))\n        self.eps = eps\n        self.scale = scale\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        # normalization layer convert to FP32 in FP16 training\n        x_float = x.float()\n        norm = x_float.pow(2).sum(1, keepdim=True).sqrt() + self.eps\n        return (self.weight[None, :, None, None].float().expand_as(x_float) *\n                x_float / norm).type_as(x)\n"
  },
  {
    "path": "mmdet/models/necks/ssh.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\n\n\nclass SSHContextModule(BaseModule):\n    \"\"\"This is an implementation of `SSH context module` described in `SSH:\n    Single Stage Headless Face Detector.\n\n    <https://arxiv.org/pdf/1708.03979.pdf>`_.\n\n    Args:\n        in_channels (int): Number of input channels used at each scale.\n        out_channels (int): Number of output channels used at each scale.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): Config dict for normalization\n            layer. Defaults to dict(type='BN').\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(init_cfg=init_cfg)\n        assert out_channels % 4 == 0\n\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n\n        self.conv5x5_1 = ConvModule(\n            self.in_channels,\n            self.out_channels // 4,\n            3,\n            stride=1,\n            padding=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n        )\n\n        self.conv5x5_2 = ConvModule(\n            self.out_channels // 4,\n            self.out_channels // 4,\n            3,\n            stride=1,\n            padding=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=None)\n\n        self.conv7x7_2 = ConvModule(\n            self.out_channels // 4,\n            self.out_channels // 4,\n            3,\n            stride=1,\n            padding=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n        )\n\n        self.conv7x7_3 = ConvModule(\n            self.out_channels // 4,\n            self.out_channels // 4,\n            3,\n            stride=1,\n            padding=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=None,\n        )\n\n    def forward(self, x: torch.Tensor) -> tuple:\n        conv5x5_1 = self.conv5x5_1(x)\n        conv5x5 = self.conv5x5_2(conv5x5_1)\n        conv7x7_2 = self.conv7x7_2(conv5x5_1)\n        conv7x7 = self.conv7x7_3(conv7x7_2)\n\n        return (conv5x5, conv7x7)\n\n\nclass SSHDetModule(BaseModule):\n    \"\"\"This is an implementation of `SSH detection module` described in `SSH:\n    Single Stage Headless Face Detector.\n\n    <https://arxiv.org/pdf/1708.03979.pdf>`_.\n\n    Args:\n        in_channels (int): Number of input channels used at each scale.\n        out_channels (int): Number of output channels used at each scale.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): Config dict for normalization\n            layer. Defaults to dict(type='BN').\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(init_cfg=init_cfg)\n        assert out_channels % 4 == 0\n\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n\n        self.conv3x3 = ConvModule(\n            self.in_channels,\n            self.out_channels // 2,\n            3,\n            stride=1,\n            padding=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=None)\n\n        self.context_module = SSHContextModule(\n            in_channels=self.in_channels,\n            out_channels=self.out_channels,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg)\n\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n        conv3x3 = self.conv3x3(x)\n        conv5x5, conv7x7 = self.context_module(x)\n        out = torch.cat([conv3x3, conv5x5, conv7x7], dim=1)\n        out = F.relu(out)\n\n        return out\n\n\n@MODELS.register_module()\nclass SSH(BaseModule):\n    \"\"\"`SSH Neck` used in `SSH: Single Stage Headless Face Detector.\n\n    <https://arxiv.org/pdf/1708.03979.pdf>`_.\n\n    Args:\n        num_scales (int): The number of scales / stages.\n        in_channels (list[int]): The number of input channels per scale.\n        out_channels (list[int]): The number of output channels  per scale.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for\n            convolution layer. Defaults to None.\n        norm_cfg (:obj:`ConfigDict` or dict): Config dict for normalization\n            layer. Defaults to dict(type='BN').\n        init_cfg (:obj:`ConfigDict` or list[:obj:`ConfigDict`] or dict or\n            list[dict], optional): Initialization config dict.\n\n    Example:\n        >>> import torch\n        >>> in_channels = [8, 16, 32, 64]\n        >>> out_channels = [16, 32, 64, 128]\n        >>> scales = [340, 170, 84, 43]\n        >>> inputs = [torch.rand(1, c, s, s)\n        ...           for c, s in zip(in_channels, scales)]\n        >>> self = SSH(num_scales=4, in_channels=in_channels,\n        ...           out_channels=out_channels)\n        >>> outputs = self.forward(inputs)\n        >>> for i in range(len(outputs)):\n        ...     print(f'outputs[{i}].shape = {outputs[i].shape}')\n        outputs[0].shape = torch.Size([1, 16, 340, 340])\n        outputs[1].shape = torch.Size([1, 32, 170, 170])\n        outputs[2].shape = torch.Size([1, 64, 84, 84])\n        outputs[3].shape = torch.Size([1, 128, 43, 43])\n    \"\"\"\n\n    def __init__(self,\n                 num_scales: int,\n                 in_channels: List[int],\n                 out_channels: List[int],\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 init_cfg: OptMultiConfig = dict(\n                     type='Xavier', layer='Conv2d', distribution='uniform')):\n        super().__init__(init_cfg=init_cfg)\n        assert (num_scales == len(in_channels) == len(out_channels))\n        self.num_scales = num_scales\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n\n        for idx in range(self.num_scales):\n            in_c, out_c = self.in_channels[idx], self.out_channels[idx]\n            self.add_module(\n                f'ssh_module{idx}',\n                SSHDetModule(\n                    in_channels=in_c,\n                    out_channels=out_c,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg))\n\n    def forward(self, inputs: Tuple[torch.Tensor]) -> tuple:\n        assert len(inputs) == self.num_scales\n\n        outs = []\n        for idx, x in enumerate(inputs):\n            ssh_module = getattr(self, f'ssh_module{idx}')\n            out = ssh_module(x)\n            outs.append(out)\n\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/yolo_neck.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Copyright (c) 2019 Western Digital Corporation or its affiliates.\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\n\n\nclass DetectionBlock(BaseModule):\n    \"\"\"Detection block in YOLO neck.\n\n    Let out_channels = n, the DetectionBlock contains:\n    Six ConvLayers, 1 Conv2D Layer and 1 YoloLayer.\n    The first 6 ConvLayers are formed the following way:\n        1x1xn, 3x3x2n, 1x1xn, 3x3x2n, 1x1xn, 3x3x2n.\n    The Conv2D layer is 1x1x255.\n    Some block will have branch after the fifth ConvLayer.\n    The input channel is arbitrary (in_channels)\n\n    Args:\n        in_channels (int): The number of input channels.\n        out_channels (int): The number of output channels.\n        conv_cfg (dict): Config dict for convolution layer. Default: None.\n        norm_cfg (dict): Dictionary to construct and config norm layer.\n            Default: dict(type='BN', requires_grad=True)\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='LeakyReLU', negative_slope=0.1).\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 act_cfg: ConfigType = dict(\n                     type='LeakyReLU', negative_slope=0.1),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super(DetectionBlock, self).__init__(init_cfg)\n        double_out_channels = out_channels * 2\n\n        # shortcut\n        cfg = dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg, act_cfg=act_cfg)\n        self.conv1 = ConvModule(in_channels, out_channels, 1, **cfg)\n        self.conv2 = ConvModule(\n            out_channels, double_out_channels, 3, padding=1, **cfg)\n        self.conv3 = ConvModule(double_out_channels, out_channels, 1, **cfg)\n        self.conv4 = ConvModule(\n            out_channels, double_out_channels, 3, padding=1, **cfg)\n        self.conv5 = ConvModule(double_out_channels, out_channels, 1, **cfg)\n\n    def forward(self, x: Tensor) -> Tensor:\n        tmp = self.conv1(x)\n        tmp = self.conv2(tmp)\n        tmp = self.conv3(tmp)\n        tmp = self.conv4(tmp)\n        out = self.conv5(tmp)\n        return out\n\n\n@MODELS.register_module()\nclass YOLOV3Neck(BaseModule):\n    \"\"\"The neck of YOLOV3.\n\n    It can be treated as a simplified version of FPN. It\n    will take the result from Darknet backbone and do some upsampling and\n    concatenation. It will finally output the detection result.\n\n    Note:\n        The input feats should be from top to bottom.\n            i.e., from high-lvl to low-lvl\n        But YOLOV3Neck will process them in reversed order.\n            i.e., from bottom (high-lvl) to top (low-lvl)\n\n    Args:\n        num_scales (int): The number of scales / stages.\n        in_channels (List[int]): The number of input channels per scale.\n        out_channels (List[int]): The number of output channels  per scale.\n        conv_cfg (dict, optional): Config dict for convolution layer.\n            Default: None.\n        norm_cfg (dict, optional): Dictionary to construct and config norm\n            layer. Default: dict(type='BN', requires_grad=True)\n        act_cfg (dict, optional): Config dict for activation layer.\n            Default: dict(type='LeakyReLU', negative_slope=0.1).\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 num_scales: int,\n                 in_channels: List[int],\n                 out_channels: List[int],\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN', requires_grad=True),\n                 act_cfg: ConfigType = dict(\n                     type='LeakyReLU', negative_slope=0.1),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super(YOLOV3Neck, self).__init__(init_cfg)\n        assert (num_scales == len(in_channels) == len(out_channels))\n        self.num_scales = num_scales\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n\n        # shortcut\n        cfg = dict(conv_cfg=conv_cfg, norm_cfg=norm_cfg, act_cfg=act_cfg)\n\n        # To support arbitrary scales, the code looks awful, but it works.\n        # Better solution is welcomed.\n        self.detect1 = DetectionBlock(in_channels[0], out_channels[0], **cfg)\n        for i in range(1, self.num_scales):\n            in_c, out_c = self.in_channels[i], self.out_channels[i]\n            inter_c = out_channels[i - 1]\n            self.add_module(f'conv{i}', ConvModule(inter_c, out_c, 1, **cfg))\n            # in_c + out_c : High-lvl feats will be cat with low-lvl feats\n            self.add_module(f'detect{i+1}',\n                            DetectionBlock(in_c + out_c, out_c, **cfg))\n\n    def forward(self, feats=Tuple[Tensor]) -> Tuple[Tensor]:\n        assert len(feats) == self.num_scales\n\n        # processed from bottom (high-lvl) to top (low-lvl)\n        outs = []\n        out = self.detect1(feats[-1])\n        outs.append(out)\n\n        for i, x in enumerate(reversed(feats[:-1])):\n            conv = getattr(self, f'conv{i+1}')\n            tmp = conv(out)\n\n            # Cat with low-lvl feats\n            tmp = F.interpolate(tmp, scale_factor=2)\n            tmp = torch.cat((tmp, x), 1)\n\n            detect = getattr(self, f'detect{i+2}')\n            out = detect(tmp)\n            outs.append(out)\n\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/necks/yolox_pafpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport math\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom ..layers import CSPLayer\n\n\n@MODELS.register_module()\nclass YOLOXPAFPN(BaseModule):\n    \"\"\"Path Aggregation Network used in YOLOX.\n\n    Args:\n        in_channels (List[int]): Number of input channels per scale.\n        out_channels (int): Number of output channels (used at each scale)\n        num_csp_blocks (int): Number of bottlenecks in CSPLayer. Default: 3\n        use_depthwise (bool): Whether to depthwise separable convolution in\n            blocks. Default: False\n        upsample_cfg (dict): Config dict for interpolate layer.\n            Default: `dict(scale_factor=2, mode='nearest')`\n        conv_cfg (dict, optional): Config dict for convolution layer.\n            Default: None, which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: dict(type='BN')\n        act_cfg (dict): Config dict for activation layer.\n            Default: dict(type='Swish')\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Default: None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_csp_blocks=3,\n                 use_depthwise=False,\n                 upsample_cfg=dict(scale_factor=2, mode='nearest'),\n                 conv_cfg=None,\n                 norm_cfg=dict(type='BN', momentum=0.03, eps=0.001),\n                 act_cfg=dict(type='Swish'),\n                 init_cfg=dict(\n                     type='Kaiming',\n                     layer='Conv2d',\n                     a=math.sqrt(5),\n                     distribution='uniform',\n                     mode='fan_in',\n                     nonlinearity='leaky_relu')):\n        super(YOLOXPAFPN, self).__init__(init_cfg)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n\n        conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule\n\n        # build top-down blocks\n        self.upsample = nn.Upsample(**upsample_cfg)\n        self.reduce_layers = nn.ModuleList()\n        self.top_down_blocks = nn.ModuleList()\n        for idx in range(len(in_channels) - 1, 0, -1):\n            self.reduce_layers.append(\n                ConvModule(\n                    in_channels[idx],\n                    in_channels[idx - 1],\n                    1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n            self.top_down_blocks.append(\n                CSPLayer(\n                    in_channels[idx - 1] * 2,\n                    in_channels[idx - 1],\n                    num_blocks=num_csp_blocks,\n                    add_identity=False,\n                    use_depthwise=use_depthwise,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n\n        # build bottom-up blocks\n        self.downsamples = nn.ModuleList()\n        self.bottom_up_blocks = nn.ModuleList()\n        for idx in range(len(in_channels) - 1):\n            self.downsamples.append(\n                conv(\n                    in_channels[idx],\n                    in_channels[idx],\n                    3,\n                    stride=2,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n            self.bottom_up_blocks.append(\n                CSPLayer(\n                    in_channels[idx] * 2,\n                    in_channels[idx + 1],\n                    num_blocks=num_csp_blocks,\n                    add_identity=False,\n                    use_depthwise=use_depthwise,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n\n        self.out_convs = nn.ModuleList()\n        for i in range(len(in_channels)):\n            self.out_convs.append(\n                ConvModule(\n                    in_channels[i],\n                    out_channels,\n                    1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    act_cfg=act_cfg))\n\n    def forward(self, inputs):\n        \"\"\"\n        Args:\n            inputs (tuple[Tensor]): input features.\n\n        Returns:\n            tuple[Tensor]: YOLOXPAFPN features.\n        \"\"\"\n        assert len(inputs) == len(self.in_channels)\n\n        # top-down path\n        inner_outs = [inputs[-1]]\n        for idx in range(len(self.in_channels) - 1, 0, -1):\n            feat_heigh = inner_outs[0]\n            feat_low = inputs[idx - 1]\n            feat_heigh = self.reduce_layers[len(self.in_channels) - 1 - idx](\n                feat_heigh)\n            inner_outs[0] = feat_heigh\n\n            upsample_feat = self.upsample(feat_heigh)\n\n            inner_out = self.top_down_blocks[len(self.in_channels) - 1 - idx](\n                torch.cat([upsample_feat, feat_low], 1))\n            inner_outs.insert(0, inner_out)\n\n        # bottom-up path\n        outs = [inner_outs[0]]\n        for idx in range(len(self.in_channels) - 1):\n            feat_low = outs[-1]\n            feat_height = inner_outs[idx + 1]\n            downsample_feat = self.downsamples[idx](feat_low)\n            out = self.bottom_up_blocks[idx](\n                torch.cat([downsample_feat, feat_height], 1))\n            outs.append(out)\n\n        # out convs\n        for idx, conv in enumerate(self.out_convs):\n            outs[idx] = conv(outs[idx])\n\n        return tuple(outs)\n"
  },
  {
    "path": "mmdet/models/reid/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_reid import BaseReID\nfrom .fc_module import FcModule\nfrom .gap import GlobalAveragePooling\nfrom .linear_reid_head import LinearReIDHead\n\n__all__ = ['BaseReID', 'GlobalAveragePooling', 'LinearReIDHead', 'FcModule']\n"
  },
  {
    "path": "mmdet/models/reid/base_reid.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional\n\nimport torch\n\ntry:\n    import mmpretrain\n    from mmpretrain.models.classifiers import ImageClassifier\nexcept ImportError:\n    mmpretrain = None\n    ImageClassifier = object\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import ReIDDataSample\n\n\n@MODELS.register_module()\nclass BaseReID(ImageClassifier):\n    \"\"\"Base model for re-identification.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if mmpretrain is None:\n            raise RuntimeError('Please run \"pip install openmim\" and '\n                               'run \"mim install mmpretrain\" to '\n                               'install mmpretrain first.')\n        super().__init__(*args, **kwargs)\n\n    def forward(self,\n                inputs: torch.Tensor,\n                data_samples: Optional[List[ReIDDataSample]] = None,\n                mode: str = 'tensor'):\n        \"\"\"The unified entry for a forward process in both training and test.\n\n        The method should accept three modes: \"tensor\", \"predict\" and \"loss\":\n\n        - \"tensor\": Forward the whole network and return tensor or tuple of\n          tensor without any post-processing, same as a common nn.Module.\n        - \"predict\": Forward and return the predictions, which are fully\n          processed to a list of :obj:`ReIDDataSample`.\n        - \"loss\": Forward and return a dict of losses according to the given\n          inputs and data samples.\n\n        Note that this method doesn't handle neither back propagation nor\n        optimizer updating, which are done in the :meth:`train_step`.\n\n        Args:\n            inputs (torch.Tensor): The input tensor with shape\n                (N, C, H, W) or (N, T, C, H, W).\n            data_samples (List[ReIDDataSample], optional): The annotation\n                data of every sample. It's required if ``mode=\"loss\"``.\n                Defaults to None.\n            mode (str): Return what kind of value. Defaults to 'tensor'.\n\n        Returns:\n            The return type depends on ``mode``.\n\n            - If ``mode=\"tensor\"``, return a tensor or a tuple of tensor.\n            - If ``mode=\"predict\"``, return a list of\n              :obj:`ReIDDataSample`.\n            - If ``mode=\"loss\"``, return a dict of tensor.\n        \"\"\"\n        if len(inputs.size()) == 5:\n            assert inputs.size(0) == 1\n            inputs = inputs[0]\n        return super().forward(inputs, data_samples, mode)\n"
  },
  {
    "path": "mmdet/models/reid/fc_module.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch.nn as nn\nfrom mmcv.cnn import build_activation_layer, build_norm_layer\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass FcModule(BaseModule):\n    \"\"\"Fully-connected layer module.\n\n    Args:\n        in_channels (int): Input channels.\n        out_channels (int): Ourput channels.\n        norm_cfg (dict, optional): Configuration of normlization method\n            after fc. Defaults to None.\n        act_cfg (dict, optional): Configuration of activation method after fc.\n            Defaults to dict(type='ReLU').\n        inplace (bool, optional): Whether inplace the activatation module.\n            Defaults to True.\n        init_cfg (dict, optional): Initialization config dict.\n            Defaults to dict(type='Kaiming', layer='Linear').\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 norm_cfg: dict = None,\n                 act_cfg: dict = dict(type='ReLU'),\n                 inplace: bool = True,\n                 init_cfg=dict(type='Kaiming', layer='Linear')):\n        super(FcModule, self).__init__(init_cfg)\n        assert norm_cfg is None or isinstance(norm_cfg, dict)\n        assert act_cfg is None or isinstance(act_cfg, dict)\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n        self.inplace = inplace\n\n        self.with_norm = norm_cfg is not None\n        self.with_activation = act_cfg is not None\n\n        self.fc = nn.Linear(in_channels, out_channels)\n        # build normalization layers\n        if self.with_norm:\n            self.norm_name, norm = build_norm_layer(norm_cfg, out_channels)\n            self.add_module(self.norm_name, norm)\n\n        # build activation layer\n        if self.with_activation:\n            act_cfg_ = act_cfg.copy()\n            # nn.Tanh has no 'inplace' argument\n            if act_cfg_['type'] not in [\n                    'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish'\n            ]:\n                act_cfg_.setdefault('inplace', inplace)\n            self.activate = build_activation_layer(act_cfg_)\n\n    @property\n    def norm(self):\n        \"\"\"Normalization.\"\"\"\n        return getattr(self, self.norm_name)\n\n    def forward(self, x, activate=True, norm=True):\n        \"\"\"Model forward.\"\"\"\n        x = self.fc(x)\n        if norm and self.with_norm:\n            x = self.norm(x)\n        if activate and self.with_activation:\n            x = self.activate(x)\n        return x\n"
  },
  {
    "path": "mmdet/models/reid/gap.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass GlobalAveragePooling(BaseModule):\n    \"\"\"Global Average Pooling neck.\n\n    Note that we use `view` to remove extra channel after pooling. We do not\n    use `squeeze` as it will also remove the batch dimension when the tensor\n    has a batch dimension of size 1, which can lead to unexpected errors.\n    \"\"\"\n\n    def __init__(self, kernel_size=None, stride=None):\n        super(GlobalAveragePooling, self).__init__()\n        if kernel_size is None and stride is None:\n            self.gap = nn.AdaptiveAvgPool2d((1, 1))\n        else:\n            self.gap = nn.AvgPool2d(kernel_size, stride)\n\n    def forward(self, inputs):\n        if isinstance(inputs, tuple):\n            outs = tuple([self.gap(x) for x in inputs])\n            outs = tuple([\n                out.view(x.size(0),\n                         torch.tensor(out.size()[1:]).prod())\n                for out, x in zip(outs, inputs)\n            ])\n        elif isinstance(inputs, torch.Tensor):\n            outs = self.gap(inputs)\n            outs = outs.view(\n                inputs.size(0),\n                torch.tensor(outs.size()[1:]).prod())\n        else:\n            raise TypeError('neck inputs should be tuple or torch.tensor')\n        return outs\n"
  },
  {
    "path": "mmdet/models/reid/linear_reid_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\n\ntry:\n    import mmpretrain\n    from mmpretrain.evaluation.metrics import Accuracy\nexcept ImportError:\n    mmpretrain = None\n\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import ReIDDataSample\nfrom .fc_module import FcModule\n\n\n@MODELS.register_module()\nclass LinearReIDHead(BaseModule):\n    \"\"\"Linear head for re-identification.\n\n    Args:\n        num_fcs (int): Number of fcs.\n        in_channels (int): Number of channels in the input.\n        fc_channels (int): Number of channels in the fcs.\n        out_channels (int): Number of channels in the output.\n        norm_cfg (dict, optional): Configuration of normlization method\n            after fc. Defaults to None.\n        act_cfg (dict, optional): Configuration of activation method after fc.\n            Defaults to None.\n        num_classes (int, optional): Number of the identities. Default to None.\n        loss_cls (dict, optional): Cross entropy loss to train the ReID module.\n            Defaults to None.\n        loss_triplet (dict, optional): Triplet loss to train the ReID module.\n            Defaults to None.\n        topk (int | Tuple[int]): Top-k accuracy. Defaults to ``(1, )``.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to dict(type='Normal',layer='Linear', mean=0, std=0.01,\n            bias=0).\n    \"\"\"\n\n    def __init__(self,\n                 num_fcs: int,\n                 in_channels: int,\n                 fc_channels: int,\n                 out_channels: int,\n                 norm_cfg: Optional[dict] = None,\n                 act_cfg: Optional[dict] = None,\n                 num_classes: Optional[int] = None,\n                 loss_cls: Optional[dict] = None,\n                 loss_triplet: Optional[dict] = None,\n                 topk: Union[int, Tuple[int]] = (1, ),\n                 init_cfg: Union[dict, List[dict]] = dict(\n                     type='Normal', layer='Linear', mean=0, std=0.01, bias=0)):\n        if mmpretrain is None:\n            raise RuntimeError('Please run \"pip install openmim\" and '\n                               'run \"mim install mmpretrain\" to '\n                               'install mmpretrain first.')\n        super(LinearReIDHead, self).__init__(init_cfg=init_cfg)\n\n        assert isinstance(topk, (int, tuple))\n        if isinstance(topk, int):\n            topk = (topk, )\n        for _topk in topk:\n            assert _topk > 0, 'Top-k should be larger than 0'\n        self.topk = topk\n\n        if loss_cls is None:\n            if isinstance(num_classes, int):\n                warnings.warn('Since cross entropy is not set, '\n                              'the num_classes will be ignored.')\n            if loss_triplet is None:\n                raise ValueError('Please choose at least one loss in '\n                                 'triplet loss and cross entropy loss.')\n        elif not isinstance(num_classes, int):\n            raise TypeError('The num_classes must be a current number, '\n                            'if there is cross entropy loss.')\n        self.loss_cls = MODELS.build(loss_cls) if loss_cls else None\n        self.loss_triplet = MODELS.build(loss_triplet) \\\n            if loss_triplet else None\n\n        self.num_fcs = num_fcs\n        self.in_channels = in_channels\n        self.fc_channels = fc_channels\n        self.out_channels = out_channels\n        self.norm_cfg = norm_cfg\n        self.act_cfg = act_cfg\n        self.num_classes = num_classes\n\n        self._init_layers()\n\n    def _init_layers(self):\n        \"\"\"Initialize fc layers.\"\"\"\n        self.fcs = nn.ModuleList()\n        for i in range(self.num_fcs):\n            in_channels = self.in_channels if i == 0 else self.fc_channels\n            self.fcs.append(\n                FcModule(in_channels, self.fc_channels, self.norm_cfg,\n                         self.act_cfg))\n        in_channels = self.in_channels if self.num_fcs == 0 else \\\n            self.fc_channels\n        self.fc_out = nn.Linear(in_channels, self.out_channels)\n        if self.loss_cls:\n            self.bn = nn.BatchNorm1d(self.out_channels)\n            self.classifier = nn.Linear(self.out_channels, self.num_classes)\n\n    def forward(self, feats: Tuple[torch.Tensor]) -> torch.Tensor:\n        \"\"\"The forward process.\"\"\"\n        # Multiple stage inputs are acceptable\n        # but only the last stage will be used.\n        feats = feats[-1]\n\n        for m in self.fcs:\n            feats = m(feats)\n        feats = self.fc_out(feats)\n        return feats\n\n    def loss(self, feats: Tuple[torch.Tensor],\n             data_samples: List[ReIDDataSample]) -> dict:\n        \"\"\"Calculate losses.\n\n        Args:\n            feats (tuple[Tensor]): The features extracted from the backbone.\n            data_samples (List[ReIDDataSample]): The annotation data of\n                every samples.\n\n        Returns:\n            dict: a dictionary of loss components\n        \"\"\"\n        # The part can be traced by torch.fx\n        feats = self(feats)\n\n        # The part can not be traced by torch.fx\n        losses = self.loss_by_feat(feats, data_samples)\n        return losses\n\n    def loss_by_feat(self, feats: torch.Tensor,\n                     data_samples: List[ReIDDataSample]) -> dict:\n        \"\"\"Unpack data samples and compute loss.\"\"\"\n        losses = dict()\n        gt_label = torch.cat([i.gt_label.label for i in data_samples])\n        gt_label = gt_label.to(feats.device)\n\n        if self.loss_triplet:\n            losses['triplet_loss'] = self.loss_triplet(feats, gt_label)\n\n        if self.loss_cls:\n            feats_bn = self.bn(feats)\n            cls_score = self.classifier(feats_bn)\n            losses['ce_loss'] = self.loss_cls(cls_score, gt_label)\n            acc = Accuracy.calculate(cls_score, gt_label, topk=self.topk)\n            losses.update(\n                {f'accuracy_top-{k}': a\n                 for k, a in zip(self.topk, acc)})\n\n        return losses\n\n    def predict(\n            self,\n            feats: Tuple[torch.Tensor],\n            data_samples: List[ReIDDataSample] = None) -> List[ReIDDataSample]:\n        \"\"\"Inference without augmentation.\n\n        Args:\n            feats (Tuple[Tensor]): The features extracted from the backbone.\n                Multiple stage inputs are acceptable but only the last stage\n                will be used.\n            data_samples (List[ReIDDataSample], optional): The annotation\n                data of every samples. If not None, set ``pred_label`` of\n                the input data samples. Defaults to None.\n\n        Returns:\n            List[ReIDDataSample]: A list of data samples which contains the\n            predicted results.\n        \"\"\"\n        # The part can be traced by torch.fx\n        feats = self(feats)\n\n        # The part can not be traced by torch.fx\n        data_samples = self.predict_by_feat(feats, data_samples)\n\n        return data_samples\n\n    def predict_by_feat(\n            self,\n            feats: torch.Tensor,\n            data_samples: List[ReIDDataSample] = None) -> List[ReIDDataSample]:\n        \"\"\"Add prediction features to data samples.\"\"\"\n        if data_samples is not None:\n            for data_sample, feat in zip(data_samples, feats):\n                data_sample.pred_feature = feat\n        else:\n            data_samples = []\n            for feat in feats:\n                data_sample = ReIDDataSample()\n                data_sample.pred_feature = feat\n                data_samples.append(data_sample)\n\n        return data_samples\n"
  },
  {
    "path": "mmdet/models/roi_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_roi_head import BaseRoIHead\nfrom .bbox_heads import (BBoxHead, ConvFCBBoxHead, DIIHead,\n                         DoubleConvFCBBoxHead, SABLHead, SCNetBBoxHead,\n                         Shared2FCBBoxHead, Shared4Conv1FCBBoxHead)\nfrom .cascade_roi_head import CascadeRoIHead\nfrom .double_roi_head import DoubleHeadRoIHead\nfrom .dynamic_roi_head import DynamicRoIHead\nfrom .grid_roi_head import GridRoIHead\nfrom .htc_roi_head import HybridTaskCascadeRoIHead\nfrom .mask_heads import (CoarseMaskHead, FCNMaskHead, FeatureRelayHead,\n                         FusedSemanticHead, GlobalContextHead, GridHead,\n                         HTCMaskHead, MaskIoUHead, MaskPointHead,\n                         SCNetMaskHead, SCNetSemanticHead)\nfrom .mask_scoring_roi_head import MaskScoringRoIHead\nfrom .multi_instance_roi_head import MultiInstanceRoIHead\nfrom .pisa_roi_head import PISARoIHead\nfrom .point_rend_roi_head import PointRendRoIHead\nfrom .roi_extractors import (BaseRoIExtractor, GenericRoIExtractor,\n                             SingleRoIExtractor)\nfrom .scnet_roi_head import SCNetRoIHead\nfrom .shared_heads import ResLayer\nfrom .sparse_roi_head import SparseRoIHead\nfrom .standard_roi_head import StandardRoIHead\nfrom .trident_roi_head import TridentRoIHead\n\n__all__ = [\n    'BaseRoIHead', 'CascadeRoIHead', 'DoubleHeadRoIHead', 'MaskScoringRoIHead',\n    'HybridTaskCascadeRoIHead', 'GridRoIHead', 'ResLayer', 'BBoxHead',\n    'ConvFCBBoxHead', 'DIIHead', 'SABLHead', 'Shared2FCBBoxHead',\n    'StandardRoIHead', 'Shared4Conv1FCBBoxHead', 'DoubleConvFCBBoxHead',\n    'FCNMaskHead', 'HTCMaskHead', 'FusedSemanticHead', 'GridHead',\n    'MaskIoUHead', 'BaseRoIExtractor', 'GenericRoIExtractor',\n    'SingleRoIExtractor', 'PISARoIHead', 'PointRendRoIHead', 'MaskPointHead',\n    'CoarseMaskHead', 'DynamicRoIHead', 'SparseRoIHead', 'TridentRoIHead',\n    'SCNetRoIHead', 'SCNetMaskHead', 'SCNetSemanticHead', 'SCNetBBoxHead',\n    'FeatureRelayHead', 'GlobalContextHead', 'MultiInstanceRoIHead'\n]\n"
  },
  {
    "path": "mmdet/models/roi_heads/base_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import Tuple\n\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import InstanceList, OptConfigType, OptMultiConfig\n\n\nclass BaseRoIHead(BaseModule, metaclass=ABCMeta):\n    \"\"\"Base class for RoIHeads.\"\"\"\n\n    def __init__(self,\n                 bbox_roi_extractor: OptMultiConfig = None,\n                 bbox_head: OptMultiConfig = None,\n                 mask_roi_extractor: OptMultiConfig = None,\n                 mask_head: OptMultiConfig = None,\n                 shared_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n        if shared_head is not None:\n            self.shared_head = MODELS.build(shared_head)\n\n        if bbox_head is not None:\n            self.init_bbox_head(bbox_roi_extractor, bbox_head)\n\n        if mask_head is not None:\n            self.init_mask_head(mask_roi_extractor, mask_head)\n\n        self.init_assigner_sampler()\n\n    @property\n    def with_bbox(self) -> bool:\n        \"\"\"bool: whether the RoI head contains a `bbox_head`\"\"\"\n        return hasattr(self, 'bbox_head') and self.bbox_head is not None\n\n    @property\n    def with_mask(self) -> bool:\n        \"\"\"bool: whether the RoI head contains a `mask_head`\"\"\"\n        return hasattr(self, 'mask_head') and self.mask_head is not None\n\n    @property\n    def with_shared_head(self) -> bool:\n        \"\"\"bool: whether the RoI head contains a `shared_head`\"\"\"\n        return hasattr(self, 'shared_head') and self.shared_head is not None\n\n    @abstractmethod\n    def init_bbox_head(self, *args, **kwargs):\n        \"\"\"Initialize ``bbox_head``\"\"\"\n        pass\n\n    @abstractmethod\n    def init_mask_head(self, *args, **kwargs):\n        \"\"\"Initialize ``mask_head``\"\"\"\n        pass\n\n    @abstractmethod\n    def init_assigner_sampler(self, *args, **kwargs):\n        \"\"\"Initialize assigner and sampler.\"\"\"\n        pass\n\n    @abstractmethod\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList):\n        \"\"\"Perform forward propagation and loss calculation of the roi head on\n        the features of the upstream network.\"\"\"\n\n    def predict(self,\n                x: Tuple[Tensor],\n                rpn_results_list: InstanceList,\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the roi head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from upstream network. Each\n                has shape (N, C, H, W).\n            rpn_results_list (list[:obj:`InstanceData`]): list of region\n                proposals.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results to\n                the original image. Defaults to True.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        assert self.with_bbox, 'Bbox head must be implemented.'\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        # TODO: nms_op in mmcv need be enhanced, the bbox result may get\n        #  difference when not rescale in bbox_head\n\n        # If it has the mask branch, the bbox branch does not need\n        # to be scaled to the original image scale, because the mask\n        # branch will scale both bbox and mask at the same time.\n        bbox_rescale = rescale if not self.with_mask else False\n        results_list = self.predict_bbox(\n            x,\n            batch_img_metas,\n            rpn_results_list,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=bbox_rescale)\n\n        if self.with_mask:\n            results_list = self.predict_mask(\n                x, batch_img_metas, results_list, rescale=rescale)\n\n        return results_list\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .bbox_head import BBoxHead\nfrom .convfc_bbox_head import (ConvFCBBoxHead, Shared2FCBBoxHead,\n                               Shared4Conv1FCBBoxHead)\nfrom .dii_head import DIIHead\nfrom .double_bbox_head import DoubleConvFCBBoxHead\nfrom .multi_instance_bbox_head import MultiInstanceBBoxHead\nfrom .sabl_head import SABLHead\nfrom .scnet_bbox_head import SCNetBBoxHead\n\n__all__ = [\n    'BBoxHead', 'ConvFCBBoxHead', 'Shared2FCBBoxHead',\n    'Shared4Conv1FCBBoxHead', 'DoubleConvFCBBoxHead', 'SABLHead', 'DIIHead',\n    'SCNetBBoxHead', 'MultiInstanceBBoxHead'\n]\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.models.layers import multiclass_nms\nfrom mmdet.models.losses import accuracy\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.utils import empty_instances, multi_apply\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import get_box_tensor, scale_boxes\nfrom mmdet.utils import ConfigType, InstanceList, OptMultiConfig\n\n\n@MODELS.register_module()\nclass BBoxHead(BaseModule):\n    \"\"\"Simplest RoI head, with only two fc layers for classification and\n    regression respectively.\"\"\"\n\n    def __init__(self,\n                 with_avg_pool: bool = False,\n                 with_cls: bool = True,\n                 with_reg: bool = True,\n                 roi_feat_size: int = 7,\n                 in_channels: int = 256,\n                 num_classes: int = 80,\n                 bbox_coder: ConfigType = dict(\n                     type='DeltaXYWHBBoxCoder',\n                     clip_border=True,\n                     target_means=[0., 0., 0., 0.],\n                     target_stds=[0.1, 0.1, 0.2, 0.2]),\n                 predict_box_type: str = 'hbox',\n                 reg_class_agnostic: bool = False,\n                 reg_decoded_bbox: bool = False,\n                 reg_predictor_cfg: ConfigType = dict(type='Linear'),\n                 cls_predictor_cfg: ConfigType = dict(type='Linear'),\n                 loss_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=False,\n                     loss_weight=1.0),\n                 loss_bbox: ConfigType = dict(\n                     type='SmoothL1Loss', beta=1.0, loss_weight=1.0),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert with_cls or with_reg\n        self.with_avg_pool = with_avg_pool\n        self.with_cls = with_cls\n        self.with_reg = with_reg\n        self.roi_feat_size = _pair(roi_feat_size)\n        self.roi_feat_area = self.roi_feat_size[0] * self.roi_feat_size[1]\n        self.in_channels = in_channels\n        self.num_classes = num_classes\n        self.predict_box_type = predict_box_type\n        self.reg_class_agnostic = reg_class_agnostic\n        self.reg_decoded_bbox = reg_decoded_bbox\n        self.reg_predictor_cfg = reg_predictor_cfg\n        self.cls_predictor_cfg = cls_predictor_cfg\n\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox = MODELS.build(loss_bbox)\n\n        in_channels = self.in_channels\n        if self.with_avg_pool:\n            self.avg_pool = nn.AvgPool2d(self.roi_feat_size)\n        else:\n            in_channels *= self.roi_feat_area\n        if self.with_cls:\n            # need to add background class\n            if self.custom_cls_channels:\n                cls_channels = self.loss_cls.get_cls_channels(self.num_classes)\n            else:\n                cls_channels = num_classes + 1\n            cls_predictor_cfg_ = self.cls_predictor_cfg.copy()\n            cls_predictor_cfg_.update(\n                in_features=in_channels, out_features=cls_channels)\n            self.fc_cls = MODELS.build(cls_predictor_cfg_)\n        if self.with_reg:\n            box_dim = self.bbox_coder.encode_size\n            out_dim_reg = box_dim if reg_class_agnostic else \\\n                box_dim * num_classes\n            reg_predictor_cfg_ = self.reg_predictor_cfg.copy()\n            if isinstance(reg_predictor_cfg_, (dict, ConfigDict)):\n                reg_predictor_cfg_.update(\n                    in_features=in_channels, out_features=out_dim_reg)\n            self.fc_reg = MODELS.build(reg_predictor_cfg_)\n        self.debug_imgs = None\n        if init_cfg is None:\n            self.init_cfg = []\n            if self.with_cls:\n                self.init_cfg += [\n                    dict(\n                        type='Normal', std=0.01, override=dict(name='fc_cls'))\n                ]\n            if self.with_reg:\n                self.init_cfg += [\n                    dict(\n                        type='Normal', std=0.001, override=dict(name='fc_reg'))\n                ]\n\n    # TODO: Create a SeasawBBoxHead to simplified logic in BBoxHead\n    @property\n    def custom_cls_channels(self) -> bool:\n        \"\"\"get custom_cls_channels from loss_cls.\"\"\"\n        return getattr(self.loss_cls, 'custom_cls_channels', False)\n\n    # TODO: Create a SeasawBBoxHead to simplified logic in BBoxHead\n    @property\n    def custom_activation(self) -> bool:\n        \"\"\"get custom_activation from loss_cls.\"\"\"\n        return getattr(self.loss_cls, 'custom_activation', False)\n\n    # TODO: Create a SeasawBBoxHead to simplified logic in BBoxHead\n    @property\n    def custom_accuracy(self) -> bool:\n        \"\"\"get custom_accuracy from loss_cls.\"\"\"\n        return getattr(self.loss_cls, 'custom_accuracy', False)\n\n    def forward(self, x: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores and bbox prediction.\n\n                - cls_score (Tensor): Classification scores for all\n                  scale levels, each is a 4D-tensor, the channels number\n                  is num_base_priors * num_classes.\n                - bbox_pred (Tensor): Box energies / deltas for all\n                  scale levels, each is a 4D-tensor, the channels number\n                  is num_base_priors * 4.\n        \"\"\"\n        if self.with_avg_pool:\n            if x.numel() > 0:\n                x = self.avg_pool(x)\n                x = x.view(x.size(0), -1)\n            else:\n                # avg_pool does not support empty tensor,\n                # so use torch.mean instead it\n                x = torch.mean(x, dim=(-1, -2))\n        cls_score = self.fc_cls(x) if self.with_cls else None\n        bbox_pred = self.fc_reg(x) if self.with_reg else None\n        return cls_score, bbox_pred\n\n    def _get_targets_single(self, pos_priors: Tensor, neg_priors: Tensor,\n                            pos_gt_bboxes: Tensor, pos_gt_labels: Tensor,\n                            cfg: ConfigDict) -> tuple:\n        \"\"\"Calculate the ground truth for proposals in the single image\n        according to the sampling results.\n\n        Args:\n            pos_priors (Tensor): Contains all the positive boxes,\n                has shape (num_pos, 4), the last dimension 4\n                represents [tl_x, tl_y, br_x, br_y].\n            neg_priors (Tensor): Contains all the negative boxes,\n                has shape (num_neg, 4), the last dimension 4\n                represents [tl_x, tl_y, br_x, br_y].\n            pos_gt_bboxes (Tensor): Contains gt_boxes for\n                all positive samples, has shape (num_pos, 4),\n                the last dimension 4\n                represents [tl_x, tl_y, br_x, br_y].\n            pos_gt_labels (Tensor): Contains gt_labels for\n                all positive samples, has shape (num_pos, ).\n            cfg (obj:`ConfigDict`): `train_cfg` of R-CNN.\n\n        Returns:\n            Tuple[Tensor]: Ground truth for proposals\n            in a single image. Containing the following Tensors:\n\n                - labels(Tensor): Gt_labels for all proposals, has\n                  shape (num_proposals,).\n                - label_weights(Tensor): Labels_weights for all\n                  proposals, has shape (num_proposals,).\n                - bbox_targets(Tensor):Regression target for all\n                  proposals, has shape (num_proposals, 4), the\n                  last dimension 4 represents [tl_x, tl_y, br_x, br_y].\n                - bbox_weights(Tensor):Regression weights for all\n                  proposals, has shape (num_proposals, 4).\n        \"\"\"\n        num_pos = pos_priors.size(0)\n        num_neg = neg_priors.size(0)\n        num_samples = num_pos + num_neg\n\n        # original implementation uses new_zeros since BG are set to be 0\n        # now use empty & fill because BG cat_id = num_classes,\n        # FG cat_id = [0, num_classes-1]\n        labels = pos_priors.new_full((num_samples, ),\n                                     self.num_classes,\n                                     dtype=torch.long)\n        reg_dim = pos_gt_bboxes.size(-1) if self.reg_decoded_bbox \\\n            else self.bbox_coder.encode_size\n        label_weights = pos_priors.new_zeros(num_samples)\n        bbox_targets = pos_priors.new_zeros(num_samples, reg_dim)\n        bbox_weights = pos_priors.new_zeros(num_samples, reg_dim)\n        if num_pos > 0:\n            labels[:num_pos] = pos_gt_labels\n            pos_weight = 1.0 if cfg.pos_weight <= 0 else cfg.pos_weight\n            label_weights[:num_pos] = pos_weight\n            if not self.reg_decoded_bbox:\n                pos_bbox_targets = self.bbox_coder.encode(\n                    pos_priors, pos_gt_bboxes)\n            else:\n                # When the regression loss (e.g. `IouLoss`, `GIouLoss`)\n                # is applied directly on the decoded bounding boxes, both\n                # the predicted boxes and regression targets should be with\n                # absolute coordinate format.\n                pos_bbox_targets = get_box_tensor(pos_gt_bboxes)\n            bbox_targets[:num_pos, :] = pos_bbox_targets\n            bbox_weights[:num_pos, :] = 1\n        if num_neg > 0:\n            label_weights[-num_neg:] = 1.0\n\n        return labels, label_weights, bbox_targets, bbox_weights\n\n    def get_targets(self,\n                    sampling_results: List[SamplingResult],\n                    rcnn_train_cfg: ConfigDict,\n                    concat: bool = True) -> tuple:\n        \"\"\"Calculate the ground truth for all samples in a batch according to\n        the sampling_results.\n\n        Almost the same as the implementation in bbox_head, we passed\n        additional parameters pos_inds_list and neg_inds_list to\n        `_get_targets_single` function.\n\n        Args:\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n            concat (bool): Whether to concatenate the results of all\n                the images in a single batch.\n\n        Returns:\n            Tuple[Tensor]: Ground truth for proposals in a single image.\n            Containing the following list of Tensors:\n\n            - labels (list[Tensor],Tensor): Gt_labels for all\n                proposals in a batch, each tensor in list has\n                shape (num_proposals,) when `concat=False`, otherwise\n                just a single tensor has shape (num_all_proposals,).\n            - label_weights (list[Tensor]): Labels_weights for\n                all proposals in a batch, each tensor in list has\n                shape (num_proposals,) when `concat=False`, otherwise\n                just a single tensor has shape (num_all_proposals,).\n            - bbox_targets (list[Tensor],Tensor): Regression target\n                for all proposals in a batch, each tensor in list\n                has shape (num_proposals, 4) when `concat=False`,\n                otherwise just a single tensor has shape\n                (num_all_proposals, 4), the last dimension 4 represents\n                [tl_x, tl_y, br_x, br_y].\n            - bbox_weights (list[tensor],Tensor): Regression weights for\n                all proposals in a batch, each tensor in list has shape\n                (num_proposals, 4) when `concat=False`, otherwise just a\n                single tensor has shape (num_all_proposals, 4).\n        \"\"\"\n        pos_priors_list = [res.pos_priors for res in sampling_results]\n        neg_priors_list = [res.neg_priors for res in sampling_results]\n        pos_gt_bboxes_list = [res.pos_gt_bboxes for res in sampling_results]\n        pos_gt_labels_list = [res.pos_gt_labels for res in sampling_results]\n        labels, label_weights, bbox_targets, bbox_weights = multi_apply(\n            self._get_targets_single,\n            pos_priors_list,\n            neg_priors_list,\n            pos_gt_bboxes_list,\n            pos_gt_labels_list,\n            cfg=rcnn_train_cfg)\n\n        if concat:\n            labels = torch.cat(labels, 0)\n            label_weights = torch.cat(label_weights, 0)\n            bbox_targets = torch.cat(bbox_targets, 0)\n            bbox_weights = torch.cat(bbox_weights, 0)\n        return labels, label_weights, bbox_targets, bbox_weights\n\n    def loss_and_target(self,\n                        cls_score: Tensor,\n                        bbox_pred: Tensor,\n                        rois: Tensor,\n                        sampling_results: List[SamplingResult],\n                        rcnn_train_cfg: ConfigDict,\n                        concat: bool = True,\n                        reduction_override: Optional[str] = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the bbox head.\n\n        Args:\n            cls_score (Tensor): Classification prediction\n                results of all class, has shape\n                (batch_size * num_proposals_single_image, num_classes)\n            bbox_pred (Tensor): Regression prediction results,\n                has shape\n                (batch_size * num_proposals_single_image, 4), the last\n                dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            rois (Tensor): RoIs with the shape\n                (batch_size * num_proposals_single_image, 5) where the first\n                column indicates batch id of each RoI.\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n            concat (bool): Whether to concatenate the results of all\n                the images in a single batch. Defaults to True.\n            reduction_override (str, optional): The reduction\n                method used to override the original reduction\n                method of the loss. Options are \"none\",\n                \"mean\" and \"sum\". Defaults to None,\n\n        Returns:\n            dict: A dictionary of loss and targets components.\n                The targets are only used for cascade rcnn.\n        \"\"\"\n\n        cls_reg_targets = self.get_targets(\n            sampling_results, rcnn_train_cfg, concat=concat)\n        losses = self.loss(\n            cls_score,\n            bbox_pred,\n            rois,\n            *cls_reg_targets,\n            reduction_override=reduction_override)\n\n        # cls_reg_targets is only for cascade rcnn\n        return dict(loss_bbox=losses, bbox_targets=cls_reg_targets)\n\n    def loss(self,\n             cls_score: Tensor,\n             bbox_pred: Tensor,\n             rois: Tensor,\n             labels: Tensor,\n             label_weights: Tensor,\n             bbox_targets: Tensor,\n             bbox_weights: Tensor,\n             reduction_override: Optional[str] = None) -> dict:\n        \"\"\"Calculate the loss based on the network predictions and targets.\n\n        Args:\n            cls_score (Tensor): Classification prediction\n                results of all class, has shape\n                (batch_size * num_proposals_single_image, num_classes)\n            bbox_pred (Tensor): Regression prediction results,\n                has shape\n                (batch_size * num_proposals_single_image, 4), the last\n                dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            rois (Tensor): RoIs with the shape\n                (batch_size * num_proposals_single_image, 5) where the first\n                column indicates batch id of each RoI.\n            labels (Tensor): Gt_labels for all proposals in a batch, has\n                shape (batch_size * num_proposals_single_image, ).\n            label_weights (Tensor): Labels_weights for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, ).\n            bbox_targets (Tensor): Regression target for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, 4),\n                the last dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            bbox_weights (Tensor): Regression weights for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, 4).\n            reduction_override (str, optional): The reduction\n                method used to override the original reduction\n                method of the loss. Options are \"none\",\n                \"mean\" and \"sum\". Defaults to None,\n\n        Returns:\n            dict: A dictionary of loss.\n        \"\"\"\n\n        losses = dict()\n\n        if cls_score is not None:\n            avg_factor = max(torch.sum(label_weights > 0).float().item(), 1.)\n            if cls_score.numel() > 0:\n                loss_cls_ = self.loss_cls(\n                    cls_score,\n                    labels,\n                    label_weights,\n                    avg_factor=avg_factor,\n                    reduction_override=reduction_override)\n                if isinstance(loss_cls_, dict):\n                    losses.update(loss_cls_)\n                else:\n                    losses['loss_cls'] = loss_cls_\n                if self.custom_activation:\n                    acc_ = self.loss_cls.get_accuracy(cls_score, labels)\n                    losses.update(acc_)\n                else:\n                    losses['acc'] = accuracy(cls_score, labels)\n        if bbox_pred is not None:\n            bg_class_ind = self.num_classes\n            # 0~self.num_classes-1 are FG, self.num_classes is BG\n            pos_inds = (labels >= 0) & (labels < bg_class_ind)\n            # do not perform bounding box regression for BG anymore.\n            if pos_inds.any():\n                if self.reg_decoded_bbox:\n                    # When the regression loss (e.g. `IouLoss`,\n                    # `GIouLoss`, `DIouLoss`) is applied directly on\n                    # the decoded bounding boxes, it decodes the\n                    # already encoded coordinates to absolute format.\n                    bbox_pred = self.bbox_coder.decode(rois[:, 1:], bbox_pred)\n                    bbox_pred = get_box_tensor(bbox_pred)\n                if self.reg_class_agnostic:\n                    pos_bbox_pred = bbox_pred.view(\n                        bbox_pred.size(0), -1)[pos_inds.type(torch.bool)]\n                else:\n                    pos_bbox_pred = bbox_pred.view(\n                        bbox_pred.size(0), self.num_classes,\n                        -1)[pos_inds.type(torch.bool),\n                            labels[pos_inds.type(torch.bool)]]\n                losses['loss_bbox'] = self.loss_bbox(\n                    pos_bbox_pred,\n                    bbox_targets[pos_inds.type(torch.bool)],\n                    bbox_weights[pos_inds.type(torch.bool)],\n                    avg_factor=bbox_targets.size(0),\n                    reduction_override=reduction_override)\n            else:\n                losses['loss_bbox'] = bbox_pred[pos_inds].sum()\n\n        return losses\n\n    def predict_by_feat(self,\n                        rois: Tuple[Tensor],\n                        cls_scores: Tuple[Tensor],\n                        bbox_preds: Tuple[Tensor],\n                        batch_img_metas: List[dict],\n                        rcnn_test_cfg: Optional[ConfigDict] = None,\n                        rescale: bool = False) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        bbox results.\n\n        Args:\n            rois (tuple[Tensor]): Tuple of boxes to be transformed.\n                Each has shape  (num_boxes, 5). last dimension 5 arrange as\n                (batch_index, x1, y1, x2, y2).\n            cls_scores (tuple[Tensor]): Tuple of box scores, each has shape\n                (num_boxes, num_classes + 1).\n            bbox_preds (tuple[Tensor]): Tuple of box energies / deltas, each\n                has shape (num_boxes, num_classes * 4).\n            batch_img_metas (list[dict]): List of image information.\n            rcnn_test_cfg (obj:`ConfigDict`, optional): `test_cfg` of R-CNN.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Instance segmentation\n            results of each image after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_scores) == len(bbox_preds)\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            results = self._predict_by_feat_single(\n                roi=rois[img_id],\n                cls_score=cls_scores[img_id],\n                bbox_pred=bbox_preds[img_id],\n                img_meta=img_meta,\n                rescale=rescale,\n                rcnn_test_cfg=rcnn_test_cfg)\n            result_list.append(results)\n\n        return result_list\n\n    def _predict_by_feat_single(\n            self,\n            roi: Tensor,\n            cls_score: Tensor,\n            bbox_pred: Tensor,\n            img_meta: dict,\n            rescale: bool = False,\n            rcnn_test_cfg: Optional[ConfigDict] = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            roi (Tensor): Boxes to be transformed. Has shape (num_boxes, 5).\n                last dimension 5 arrange as (batch_index, x1, y1, x2, y2).\n            cls_score (Tensor): Box scores, has shape\n                (num_boxes, num_classes + 1).\n            bbox_pred (Tensor): Box energies / deltas.\n                has shape (num_boxes, num_classes * 4).\n            img_meta (dict): image information.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of Bbox Head.\n                Defaults to None\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\\\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        results = InstanceData()\n        if roi.shape[0] == 0:\n            return empty_instances([img_meta],\n                                   roi.device,\n                                   task_type='bbox',\n                                   instance_results=[results],\n                                   box_type=self.predict_box_type,\n                                   use_box_type=False,\n                                   num_classes=self.num_classes,\n                                   score_per_cls=rcnn_test_cfg is None)[0]\n\n        # some loss (Seesaw loss..) may have custom activation\n        if self.custom_cls_channels:\n            scores = self.loss_cls.get_activation(cls_score)\n        else:\n            scores = F.softmax(\n                cls_score, dim=-1) if cls_score is not None else None\n\n        img_shape = img_meta['img_shape']\n        num_rois = roi.size(0)\n        # bbox_pred would be None in some detector when with_reg is False,\n        # e.g. Grid R-CNN.\n        if bbox_pred is not None:\n            num_classes = 1 if self.reg_class_agnostic else self.num_classes\n            roi = roi.repeat_interleave(num_classes, dim=0)\n            bbox_pred = bbox_pred.view(-1, self.bbox_coder.encode_size)\n            bboxes = self.bbox_coder.decode(\n                roi[..., 1:], bbox_pred, max_shape=img_shape)\n        else:\n            bboxes = roi[:, 1:].clone()\n            if img_shape is not None and bboxes.size(-1) == 4:\n                bboxes[:, [0, 2]].clamp_(min=0, max=img_shape[1])\n                bboxes[:, [1, 3]].clamp_(min=0, max=img_shape[0])\n\n        if rescale and bboxes.size(0) > 0:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = [1 / s for s in img_meta['scale_factor']]\n            bboxes = scale_boxes(bboxes, scale_factor)\n\n        # Get the inside tensor when `bboxes` is a box type\n        bboxes = get_box_tensor(bboxes)\n        box_dim = bboxes.size(-1)\n        bboxes = bboxes.view(num_rois, -1)\n\n        if rcnn_test_cfg is None:\n            # This means that it is aug test.\n            # It needs to return the raw results without nms.\n            results.bboxes = bboxes\n            results.scores = scores\n        else:\n            det_bboxes, det_labels = multiclass_nms(\n                bboxes,\n                scores,\n                rcnn_test_cfg.score_thr,\n                rcnn_test_cfg.nms,\n                rcnn_test_cfg.max_per_img,\n                box_dim=box_dim)\n            results.bboxes = det_bboxes[:, :-1]\n            results.scores = det_bboxes[:, -1]\n            results.labels = det_labels\n        return results\n\n    def refine_bboxes(self, sampling_results: Union[List[SamplingResult],\n                                                    InstanceList],\n                      bbox_results: dict,\n                      batch_img_metas: List[dict]) -> InstanceList:\n        \"\"\"Refine bboxes during training.\n\n        Args:\n            sampling_results (List[:obj:`SamplingResult`] or\n                List[:obj:`InstanceData`]): Sampling results.\n                :obj:`SamplingResult` is the real sampling results\n                calculate from bbox_head, while :obj:`InstanceData` is\n                fake sampling results, e.g., in Sparse R-CNN or QueryInst, etc.\n            bbox_results (dict): Usually is a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `rois` (Tensor): RoIs with the shape (n, 5) where the first\n                  column indicates batch id of each RoI.\n                - `bbox_targets` (tuple):  Ground truth for proposals in a\n                  single image. Containing the following list of Tensors:\n                  (labels, label_weights, bbox_targets, bbox_weights)\n            batch_img_metas (List[dict]): List of image information.\n\n        Returns:\n            list[:obj:`InstanceData`]: Refined bboxes of each image.\n\n        Example:\n            >>> # xdoctest: +REQUIRES(module:kwarray)\n            >>> import numpy as np\n            >>> from mmdet.models.task_modules.samplers.\n            ... sampling_result import random_boxes\n            >>> from mmdet.models.task_modules.samplers import SamplingResult\n            >>> self = BBoxHead(reg_class_agnostic=True)\n            >>> n_roi = 2\n            >>> n_img = 4\n            >>> scale = 512\n            >>> rng = np.random.RandomState(0)\n            ... batch_img_metas = [{'img_shape': (scale, scale)}\n            >>>                     for _ in range(n_img)]\n            >>> sampling_results = [SamplingResult.random(rng=10)\n            ...                     for _ in range(n_img)]\n            >>> # Create rois in the expected format\n            >>> roi_boxes = random_boxes(n_roi, scale=scale, rng=rng)\n            >>> img_ids = torch.randint(0, n_img, (n_roi,))\n            >>> img_ids = img_ids.float()\n            >>> rois = torch.cat([img_ids[:, None], roi_boxes], dim=1)\n            >>> # Create other args\n            >>> labels = torch.randint(0, 81, (scale,)).long()\n            >>> bbox_preds = random_boxes(n_roi, scale=scale, rng=rng)\n            >>> cls_score = torch.randn((scale, 81))\n            ... # For each image, pretend random positive boxes are gts\n            >>> bbox_targets = (labels, None, None, None)\n            ... bbox_results = dict(rois=rois, bbox_pred=bbox_preds,\n            ...                     cls_score=cls_score,\n            ...                     bbox_targets=bbox_targets)\n            >>> bboxes_list = self.refine_bboxes(sampling_results,\n            ...                                  bbox_results,\n            ...                                  batch_img_metas)\n            >>> print(bboxes_list)\n        \"\"\"\n        pos_is_gts = [res.pos_is_gt for res in sampling_results]\n        # bbox_targets is a tuple\n        labels = bbox_results['bbox_targets'][0]\n        cls_scores = bbox_results['cls_score']\n        rois = bbox_results['rois']\n        bbox_preds = bbox_results['bbox_pred']\n        if self.custom_activation:\n            # TODO: Create a SeasawBBoxHead to simplified logic in BBoxHead\n            cls_scores = self.loss_cls.get_activation(cls_scores)\n        if cls_scores.numel() == 0:\n            return None\n        if cls_scores.shape[-1] == self.num_classes + 1:\n            # remove background class\n            cls_scores = cls_scores[:, :-1]\n        elif cls_scores.shape[-1] != self.num_classes:\n            raise ValueError('The last dim of `cls_scores` should equal to '\n                             '`num_classes` or `num_classes + 1`,'\n                             f'but got {cls_scores.shape[-1]}.')\n        labels = torch.where(labels == self.num_classes, cls_scores.argmax(1),\n                             labels)\n\n        img_ids = rois[:, 0].long().unique(sorted=True)\n        assert img_ids.numel() <= len(batch_img_metas)\n\n        results_list = []\n        for i in range(len(batch_img_metas)):\n            inds = torch.nonzero(\n                rois[:, 0] == i, as_tuple=False).squeeze(dim=1)\n            num_rois = inds.numel()\n\n            bboxes_ = rois[inds, 1:]\n            label_ = labels[inds]\n            bbox_pred_ = bbox_preds[inds]\n            img_meta_ = batch_img_metas[i]\n            pos_is_gts_ = pos_is_gts[i]\n\n            bboxes = self.regress_by_class(bboxes_, label_, bbox_pred_,\n                                           img_meta_)\n            # filter gt bboxes\n            pos_keep = 1 - pos_is_gts_\n            keep_inds = pos_is_gts_.new_ones(num_rois)\n            keep_inds[:len(pos_is_gts_)] = pos_keep\n            results = InstanceData(bboxes=bboxes[keep_inds.type(torch.bool)])\n            results_list.append(results)\n\n        return results_list\n\n    def regress_by_class(self, priors: Tensor, label: Tensor,\n                         bbox_pred: Tensor, img_meta: dict) -> Tensor:\n        \"\"\"Regress the bbox for the predicted class. Used in Cascade R-CNN.\n\n        Args:\n            priors (Tensor): Priors from `rpn_head` or last stage\n                `bbox_head`, has shape (num_proposals, 4).\n            label (Tensor): Only used when `self.reg_class_agnostic`\n                is False, has shape (num_proposals, ).\n            bbox_pred (Tensor): Regression prediction of\n                current stage `bbox_head`. When `self.reg_class_agnostic`\n                is False, it has shape (n, num_classes * 4), otherwise\n                it has shape (n, 4).\n            img_meta (dict): Image meta info.\n\n        Returns:\n            Tensor: Regressed bboxes, the same shape as input rois.\n        \"\"\"\n        reg_dim = self.bbox_coder.encode_size\n        if not self.reg_class_agnostic:\n            label = label * reg_dim\n            inds = torch.stack([label + i for i in range(reg_dim)], 1)\n            bbox_pred = torch.gather(bbox_pred, 1, inds)\n        assert bbox_pred.size()[1] == reg_dim\n\n        max_shape = img_meta['img_shape']\n        regressed_bboxes = self.bbox_coder.decode(\n            priors, bbox_pred, max_shape=max_shape)\n        return regressed_bboxes\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/convfc_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.config import ConfigDict\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .bbox_head import BBoxHead\n\n\n@MODELS.register_module()\nclass ConvFCBBoxHead(BBoxHead):\n    r\"\"\"More general bbox head, with shared conv and fc layers and two optional\n    separated branches.\n\n    .. code-block:: none\n\n                                    /-> cls convs -> cls fcs -> cls\n        shared convs -> shared fcs\n                                    \\-> reg convs -> reg fcs -> reg\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 num_shared_convs: int = 0,\n                 num_shared_fcs: int = 0,\n                 num_cls_convs: int = 0,\n                 num_cls_fcs: int = 0,\n                 num_reg_convs: int = 0,\n                 num_reg_fcs: int = 0,\n                 conv_out_channels: int = 256,\n                 fc_out_channels: int = 1024,\n                 conv_cfg: Optional[Union[dict, ConfigDict]] = None,\n                 norm_cfg: Optional[Union[dict, ConfigDict]] = None,\n                 init_cfg: Optional[Union[dict, ConfigDict]] = None,\n                 *args,\n                 **kwargs) -> None:\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n        assert (num_shared_convs + num_shared_fcs + num_cls_convs +\n                num_cls_fcs + num_reg_convs + num_reg_fcs > 0)\n        if num_cls_convs > 0 or num_reg_convs > 0:\n            assert num_shared_fcs == 0\n        if not self.with_cls:\n            assert num_cls_convs == 0 and num_cls_fcs == 0\n        if not self.with_reg:\n            assert num_reg_convs == 0 and num_reg_fcs == 0\n        self.num_shared_convs = num_shared_convs\n        self.num_shared_fcs = num_shared_fcs\n        self.num_cls_convs = num_cls_convs\n        self.num_cls_fcs = num_cls_fcs\n        self.num_reg_convs = num_reg_convs\n        self.num_reg_fcs = num_reg_fcs\n        self.conv_out_channels = conv_out_channels\n        self.fc_out_channels = fc_out_channels\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n\n        # add shared convs and fcs\n        self.shared_convs, self.shared_fcs, last_layer_dim = \\\n            self._add_conv_fc_branch(\n                self.num_shared_convs, self.num_shared_fcs, self.in_channels,\n                True)\n        self.shared_out_channels = last_layer_dim\n\n        # add cls specific branch\n        self.cls_convs, self.cls_fcs, self.cls_last_dim = \\\n            self._add_conv_fc_branch(\n                self.num_cls_convs, self.num_cls_fcs, self.shared_out_channels)\n\n        # add reg specific branch\n        self.reg_convs, self.reg_fcs, self.reg_last_dim = \\\n            self._add_conv_fc_branch(\n                self.num_reg_convs, self.num_reg_fcs, self.shared_out_channels)\n\n        if self.num_shared_fcs == 0 and not self.with_avg_pool:\n            if self.num_cls_fcs == 0:\n                self.cls_last_dim *= self.roi_feat_area\n            if self.num_reg_fcs == 0:\n                self.reg_last_dim *= self.roi_feat_area\n\n        self.relu = nn.ReLU(inplace=True)\n        # reconstruct fc_cls and fc_reg since input channels are changed\n        if self.with_cls:\n            if self.custom_cls_channels:\n                cls_channels = self.loss_cls.get_cls_channels(self.num_classes)\n            else:\n                cls_channels = self.num_classes + 1\n            cls_predictor_cfg_ = self.cls_predictor_cfg.copy()\n            cls_predictor_cfg_.update(\n                in_features=self.cls_last_dim, out_features=cls_channels)\n            self.fc_cls = MODELS.build(cls_predictor_cfg_)\n        if self.with_reg:\n            box_dim = self.bbox_coder.encode_size\n            out_dim_reg = box_dim if self.reg_class_agnostic else \\\n                box_dim * self.num_classes\n            reg_predictor_cfg_ = self.reg_predictor_cfg.copy()\n            if isinstance(reg_predictor_cfg_, (dict, ConfigDict)):\n                reg_predictor_cfg_.update(\n                    in_features=self.reg_last_dim, out_features=out_dim_reg)\n            self.fc_reg = MODELS.build(reg_predictor_cfg_)\n\n        if init_cfg is None:\n            # when init_cfg is None,\n            # It has been set to\n            # [[dict(type='Normal', std=0.01, override=dict(name='fc_cls'))],\n            #  [dict(type='Normal', std=0.001, override=dict(name='fc_reg'))]\n            # after `super(ConvFCBBoxHead, self).__init__()`\n            # we only need to append additional configuration\n            # for `shared_fcs`, `cls_fcs` and `reg_fcs`\n            self.init_cfg += [\n                dict(\n                    type='Xavier',\n                    distribution='uniform',\n                    override=[\n                        dict(name='shared_fcs'),\n                        dict(name='cls_fcs'),\n                        dict(name='reg_fcs')\n                    ])\n            ]\n\n    def _add_conv_fc_branch(self,\n                            num_branch_convs: int,\n                            num_branch_fcs: int,\n                            in_channels: int,\n                            is_shared: bool = False) -> tuple:\n        \"\"\"Add shared or separable branch.\n\n        convs -> avg pool (optional) -> fcs\n        \"\"\"\n        last_layer_dim = in_channels\n        # add branch specific conv layers\n        branch_convs = nn.ModuleList()\n        if num_branch_convs > 0:\n            for i in range(num_branch_convs):\n                conv_in_channels = (\n                    last_layer_dim if i == 0 else self.conv_out_channels)\n                branch_convs.append(\n                    ConvModule(\n                        conv_in_channels,\n                        self.conv_out_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg))\n            last_layer_dim = self.conv_out_channels\n        # add branch specific fc layers\n        branch_fcs = nn.ModuleList()\n        if num_branch_fcs > 0:\n            # for shared branch, only consider self.with_avg_pool\n            # for separated branches, also consider self.num_shared_fcs\n            if (is_shared\n                    or self.num_shared_fcs == 0) and not self.with_avg_pool:\n                last_layer_dim *= self.roi_feat_area\n            for i in range(num_branch_fcs):\n                fc_in_channels = (\n                    last_layer_dim if i == 0 else self.fc_out_channels)\n                branch_fcs.append(\n                    nn.Linear(fc_in_channels, self.fc_out_channels))\n            last_layer_dim = self.fc_out_channels\n        return branch_convs, branch_fcs, last_layer_dim\n\n    def forward(self, x: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores and bbox prediction.\n\n                - cls_score (Tensor): Classification scores for all \\\n                    scale levels, each is a 4D-tensor, the channels number \\\n                    is num_base_priors * num_classes.\n                - bbox_pred (Tensor): Box energies / deltas for all \\\n                    scale levels, each is a 4D-tensor, the channels number \\\n                    is num_base_priors * 4.\n        \"\"\"\n        # shared part\n        if self.num_shared_convs > 0:\n            for conv in self.shared_convs:\n                x = conv(x)\n\n        if self.num_shared_fcs > 0:\n            if self.with_avg_pool:\n                x = self.avg_pool(x)\n\n            x = x.flatten(1)\n\n            for fc in self.shared_fcs:\n                x = self.relu(fc(x))\n        # separate branches\n        x_cls = x\n        x_reg = x\n\n        for conv in self.cls_convs:\n            x_cls = conv(x_cls)\n        if x_cls.dim() > 2:\n            if self.with_avg_pool:\n                x_cls = self.avg_pool(x_cls)\n            x_cls = x_cls.flatten(1)\n        for fc in self.cls_fcs:\n            x_cls = self.relu(fc(x_cls))\n\n        for conv in self.reg_convs:\n            x_reg = conv(x_reg)\n        if x_reg.dim() > 2:\n            if self.with_avg_pool:\n                x_reg = self.avg_pool(x_reg)\n            x_reg = x_reg.flatten(1)\n        for fc in self.reg_fcs:\n            x_reg = self.relu(fc(x_reg))\n\n        cls_score = self.fc_cls(x_cls) if self.with_cls else None\n        bbox_pred = self.fc_reg(x_reg) if self.with_reg else None\n        return cls_score, bbox_pred\n\n\n@MODELS.register_module()\nclass Shared2FCBBoxHead(ConvFCBBoxHead):\n\n    def __init__(self, fc_out_channels: int = 1024, *args, **kwargs) -> None:\n        super().__init__(\n            num_shared_convs=0,\n            num_shared_fcs=2,\n            num_cls_convs=0,\n            num_cls_fcs=0,\n            num_reg_convs=0,\n            num_reg_fcs=0,\n            fc_out_channels=fc_out_channels,\n            *args,\n            **kwargs)\n\n\n@MODELS.register_module()\nclass Shared4Conv1FCBBoxHead(ConvFCBBoxHead):\n\n    def __init__(self, fc_out_channels: int = 1024, *args, **kwargs) -> None:\n        super().__init__(\n            num_shared_convs=4,\n            num_shared_fcs=1,\n            num_cls_convs=0,\n            num_cls_fcs=0,\n            num_reg_convs=0,\n            num_reg_fcs=0,\n            fc_out_channels=fc_out_channels,\n            *args,\n            **kwargs)\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/dii_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import build_activation_layer, build_norm_layer\nfrom mmcv.cnn.bricks.transformer import FFN, MultiheadAttention\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import bias_init_with_prob\nfrom torch import Tensor\n\nfrom mmdet.models.losses import accuracy\nfrom mmdet.models.task_modules import SamplingResult\nfrom mmdet.models.utils import multi_apply\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, reduce_mean\nfrom .bbox_head import BBoxHead\n\n\n@MODELS.register_module()\nclass DIIHead(BBoxHead):\n    r\"\"\"Dynamic Instance Interactive Head for `Sparse R-CNN: End-to-End Object\n    Detection with Learnable Proposals <https://arxiv.org/abs/2011.12450>`_\n\n    Args:\n        num_classes (int): Number of class in dataset.\n            Defaults to 80.\n        num_ffn_fcs (int): The number of fully-connected\n            layers in FFNs. Defaults to 2.\n        num_heads (int): The hidden dimension of FFNs.\n            Defaults to 8.\n        num_cls_fcs (int): The number of fully-connected\n            layers in classification subnet. Defaults to 1.\n        num_reg_fcs (int): The number of fully-connected\n            layers in regression subnet. Defaults to 3.\n        feedforward_channels (int): The hidden dimension\n            of FFNs. Defaults to 2048\n        in_channels (int): Hidden_channels of MultiheadAttention.\n            Defaults to 256.\n        dropout (float): Probability of drop the channel.\n            Defaults to 0.0\n        ffn_act_cfg (:obj:`ConfigDict` or dict): The activation config\n            for FFNs.\n        dynamic_conv_cfg (:obj:`ConfigDict` or dict): The convolution\n            config for DynamicConv.\n        loss_iou (:obj:`ConfigDict` or dict): The config for iou or\n            giou loss.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int = 80,\n                 num_ffn_fcs: int = 2,\n                 num_heads: int = 8,\n                 num_cls_fcs: int = 1,\n                 num_reg_fcs: int = 3,\n                 feedforward_channels: int = 2048,\n                 in_channels: int = 256,\n                 dropout: float = 0.0,\n                 ffn_act_cfg: ConfigType = dict(type='ReLU', inplace=True),\n                 dynamic_conv_cfg: ConfigType = dict(\n                     type='DynamicConv',\n                     in_channels=256,\n                     feat_channels=64,\n                     out_channels=256,\n                     input_feat_shape=7,\n                     act_cfg=dict(type='ReLU', inplace=True),\n                     norm_cfg=dict(type='LN')),\n                 loss_iou: ConfigType = dict(type='GIoULoss', loss_weight=2.0),\n                 init_cfg: OptConfigType = None,\n                 **kwargs) -> None:\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super().__init__(\n            num_classes=num_classes,\n            reg_decoded_bbox=True,\n            reg_class_agnostic=True,\n            init_cfg=init_cfg,\n            **kwargs)\n        self.loss_iou = MODELS.build(loss_iou)\n        self.in_channels = in_channels\n        self.fp16_enabled = False\n        self.attention = MultiheadAttention(in_channels, num_heads, dropout)\n        self.attention_norm = build_norm_layer(dict(type='LN'), in_channels)[1]\n\n        self.instance_interactive_conv = MODELS.build(dynamic_conv_cfg)\n        self.instance_interactive_conv_dropout = nn.Dropout(dropout)\n        self.instance_interactive_conv_norm = build_norm_layer(\n            dict(type='LN'), in_channels)[1]\n\n        self.ffn = FFN(\n            in_channels,\n            feedforward_channels,\n            num_ffn_fcs,\n            act_cfg=ffn_act_cfg,\n            dropout=dropout)\n        self.ffn_norm = build_norm_layer(dict(type='LN'), in_channels)[1]\n\n        self.cls_fcs = nn.ModuleList()\n        for _ in range(num_cls_fcs):\n            self.cls_fcs.append(\n                nn.Linear(in_channels, in_channels, bias=False))\n            self.cls_fcs.append(\n                build_norm_layer(dict(type='LN'), in_channels)[1])\n            self.cls_fcs.append(\n                build_activation_layer(dict(type='ReLU', inplace=True)))\n\n        # over load the self.fc_cls in BBoxHead\n        if self.loss_cls.use_sigmoid:\n            self.fc_cls = nn.Linear(in_channels, self.num_classes)\n        else:\n            self.fc_cls = nn.Linear(in_channels, self.num_classes + 1)\n\n        self.reg_fcs = nn.ModuleList()\n        for _ in range(num_reg_fcs):\n            self.reg_fcs.append(\n                nn.Linear(in_channels, in_channels, bias=False))\n            self.reg_fcs.append(\n                build_norm_layer(dict(type='LN'), in_channels)[1])\n            self.reg_fcs.append(\n                build_activation_layer(dict(type='ReLU', inplace=True)))\n        # over load the self.fc_cls in BBoxHead\n        self.fc_reg = nn.Linear(in_channels, 4)\n\n        assert self.reg_class_agnostic, 'DIIHead only ' \\\n            'suppport `reg_class_agnostic=True` '\n        assert self.reg_decoded_bbox, 'DIIHead only ' \\\n            'suppport `reg_decoded_bbox=True`'\n\n    def init_weights(self) -> None:\n        \"\"\"Use xavier initialization for all weight parameter and set\n        classification head bias as a specific value when use focal loss.\"\"\"\n        super().init_weights()\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n            else:\n                # adopt the default initialization for\n                # the weight and bias of the layer norm\n                pass\n        if self.loss_cls.use_sigmoid:\n            bias_init = bias_init_with_prob(0.01)\n            nn.init.constant_(self.fc_cls.bias, bias_init)\n\n    def forward(self, roi_feat: Tensor, proposal_feat: Tensor) -> tuple:\n        \"\"\"Forward function of Dynamic Instance Interactive Head.\n\n        Args:\n            roi_feat (Tensor): Roi-pooling features with shape\n                (batch_size*num_proposals, feature_dimensions,\n                pooling_h , pooling_w).\n            proposal_feat (Tensor): Intermediate feature get from\n                diihead in last stage, has shape\n                (batch_size, num_proposals, feature_dimensions)\n\n        Returns:\n            tuple[Tensor]: Usually a tuple of classification scores\n            and bbox prediction and a intermediate feature.\n\n            - cls_scores (Tensor): Classification scores for\n              all proposals, has shape\n              (batch_size, num_proposals, num_classes).\n            - bbox_preds (Tensor): Box energies / deltas for\n              all proposals, has shape\n              (batch_size, num_proposals, 4).\n            - obj_feat (Tensor): Object feature before classification\n              and regression subnet, has shape\n              (batch_size, num_proposal, feature_dimensions).\n            - attn_feats (Tensor): Intermediate feature.\n        \"\"\"\n        N, num_proposals = proposal_feat.shape[:2]\n\n        # Self attention\n        proposal_feat = proposal_feat.permute(1, 0, 2)\n        proposal_feat = self.attention_norm(self.attention(proposal_feat))\n        attn_feats = proposal_feat.permute(1, 0, 2)\n\n        # instance interactive\n        proposal_feat = attn_feats.reshape(-1, self.in_channels)\n        proposal_feat_iic = self.instance_interactive_conv(\n            proposal_feat, roi_feat)\n        proposal_feat = proposal_feat + self.instance_interactive_conv_dropout(\n            proposal_feat_iic)\n        obj_feat = self.instance_interactive_conv_norm(proposal_feat)\n\n        # FFN\n        obj_feat = self.ffn_norm(self.ffn(obj_feat))\n\n        cls_feat = obj_feat\n        reg_feat = obj_feat\n\n        for cls_layer in self.cls_fcs:\n            cls_feat = cls_layer(cls_feat)\n        for reg_layer in self.reg_fcs:\n            reg_feat = reg_layer(reg_feat)\n\n        cls_score = self.fc_cls(cls_feat).view(\n            N, num_proposals, self.num_classes\n            if self.loss_cls.use_sigmoid else self.num_classes + 1)\n        bbox_delta = self.fc_reg(reg_feat).view(N, num_proposals, 4)\n\n        return cls_score, bbox_delta, obj_feat.view(\n            N, num_proposals, self.in_channels), attn_feats\n\n    def loss_and_target(self,\n                        cls_score: Tensor,\n                        bbox_pred: Tensor,\n                        sampling_results: List[SamplingResult],\n                        rcnn_train_cfg: ConfigType,\n                        imgs_whwh: Tensor,\n                        concat: bool = True,\n                        reduction_override: str = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the DIIHead.\n\n        Args:\n            cls_score (Tensor): Classification prediction\n                results of all class, has shape\n                (batch_size * num_proposals_single_image, num_classes)\n            bbox_pred (Tensor): Regression prediction results, has shape\n                (batch_size * num_proposals_single_image, 4), the last\n                dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n            imgs_whwh (Tensor): imgs_whwh (Tensor): Tensor with\\\n                shape (batch_size, num_proposals, 4), the last\n                dimension means\n                [img_width,img_height, img_width, img_height].\n            concat (bool): Whether to concatenate the results of all\n                the images in a single batch. Defaults to True.\n            reduction_override (str, optional): The reduction\n                method used to override the original reduction\n                method of the loss. Options are \"none\",\n                \"mean\" and \"sum\". Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss and targets components.\n            The targets are only used for cascade rcnn.\n        \"\"\"\n        cls_reg_targets = self.get_targets(\n            sampling_results=sampling_results,\n            rcnn_train_cfg=rcnn_train_cfg,\n            concat=concat)\n        (labels, label_weights, bbox_targets, bbox_weights) = cls_reg_targets\n\n        losses = dict()\n        bg_class_ind = self.num_classes\n        # note in spare rcnn num_gt == num_pos\n        pos_inds = (labels >= 0) & (labels < bg_class_ind)\n        num_pos = pos_inds.sum().float()\n        avg_factor = reduce_mean(num_pos)\n        if cls_score is not None:\n            if cls_score.numel() > 0:\n                losses['loss_cls'] = self.loss_cls(\n                    cls_score,\n                    labels,\n                    label_weights,\n                    avg_factor=avg_factor,\n                    reduction_override=reduction_override)\n                losses['pos_acc'] = accuracy(cls_score[pos_inds],\n                                             labels[pos_inds])\n        if bbox_pred is not None:\n            # 0~self.num_classes-1 are FG, self.num_classes is BG\n            # do not perform bounding box regression for BG anymore.\n            if pos_inds.any():\n                pos_bbox_pred = bbox_pred.reshape(bbox_pred.size(0),\n                                                  4)[pos_inds.type(torch.bool)]\n                imgs_whwh = imgs_whwh.reshape(bbox_pred.size(0),\n                                              4)[pos_inds.type(torch.bool)]\n                losses['loss_bbox'] = self.loss_bbox(\n                    pos_bbox_pred / imgs_whwh,\n                    bbox_targets[pos_inds.type(torch.bool)] / imgs_whwh,\n                    bbox_weights[pos_inds.type(torch.bool)],\n                    avg_factor=avg_factor)\n                losses['loss_iou'] = self.loss_iou(\n                    pos_bbox_pred,\n                    bbox_targets[pos_inds.type(torch.bool)],\n                    bbox_weights[pos_inds.type(torch.bool)],\n                    avg_factor=avg_factor)\n            else:\n                losses['loss_bbox'] = bbox_pred.sum() * 0\n                losses['loss_iou'] = bbox_pred.sum() * 0\n        return dict(loss_bbox=losses, bbox_targets=cls_reg_targets)\n\n    def _get_targets_single(self, pos_inds: Tensor, neg_inds: Tensor,\n                            pos_priors: Tensor, neg_priors: Tensor,\n                            pos_gt_bboxes: Tensor, pos_gt_labels: Tensor,\n                            cfg: ConfigDict) -> tuple:\n        \"\"\"Calculate the ground truth for proposals in the single image\n        according to the sampling results.\n\n        Almost the same as the implementation in `bbox_head`,\n        we add pos_inds and neg_inds to select positive and\n        negative samples instead of selecting the first num_pos\n        as positive samples.\n\n        Args:\n            pos_inds (Tensor): The length is equal to the\n                positive sample numbers contain all index\n                of the positive sample in the origin proposal set.\n            neg_inds (Tensor): The length is equal to the\n                negative sample numbers contain all index\n                of the negative sample in the origin proposal set.\n            pos_priors (Tensor): Contains all the positive boxes,\n                has shape (num_pos, 4), the last dimension 4\n                represents [tl_x, tl_y, br_x, br_y].\n            neg_priors (Tensor): Contains all the negative boxes,\n                has shape (num_neg, 4), the last dimension 4\n                represents [tl_x, tl_y, br_x, br_y].\n            pos_gt_bboxes (Tensor): Contains gt_boxes for\n                all positive samples, has shape (num_pos, 4),\n                the last dimension 4\n                represents [tl_x, tl_y, br_x, br_y].\n            pos_gt_labels (Tensor): Contains gt_labels for\n                all positive samples, has shape (num_pos, ).\n            cfg (obj:`ConfigDict`): `train_cfg` of R-CNN.\n\n        Returns:\n            Tuple[Tensor]: Ground truth for proposals in a single image.\n            Containing the following Tensors:\n\n            - labels(Tensor): Gt_labels for all proposals, has\n              shape (num_proposals,).\n            - label_weights(Tensor): Labels_weights for all proposals, has\n              shape (num_proposals,).\n            - bbox_targets(Tensor):Regression target for all proposals, has\n              shape (num_proposals, 4), the last dimension 4\n              represents [tl_x, tl_y, br_x, br_y].\n            - bbox_weights(Tensor):Regression weights for all proposals,\n              has shape (num_proposals, 4).\n        \"\"\"\n        num_pos = pos_priors.size(0)\n        num_neg = neg_priors.size(0)\n        num_samples = num_pos + num_neg\n\n        # original implementation uses new_zeros since BG are set to be 0\n        # now use empty & fill because BG cat_id = num_classes,\n        # FG cat_id = [0, num_classes-1]\n        labels = pos_priors.new_full((num_samples, ),\n                                     self.num_classes,\n                                     dtype=torch.long)\n        label_weights = pos_priors.new_zeros(num_samples)\n        bbox_targets = pos_priors.new_zeros(num_samples, 4)\n        bbox_weights = pos_priors.new_zeros(num_samples, 4)\n        if num_pos > 0:\n            labels[pos_inds] = pos_gt_labels\n            pos_weight = 1.0 if cfg.pos_weight <= 0 else cfg.pos_weight\n            label_weights[pos_inds] = pos_weight\n            if not self.reg_decoded_bbox:\n                pos_bbox_targets = self.bbox_coder.encode(\n                    pos_priors, pos_gt_bboxes)\n            else:\n                pos_bbox_targets = pos_gt_bboxes\n            bbox_targets[pos_inds, :] = pos_bbox_targets\n            bbox_weights[pos_inds, :] = 1\n        if num_neg > 0:\n            label_weights[neg_inds] = 1.0\n\n        return labels, label_weights, bbox_targets, bbox_weights\n\n    def get_targets(self,\n                    sampling_results: List[SamplingResult],\n                    rcnn_train_cfg: ConfigDict,\n                    concat: bool = True) -> tuple:\n        \"\"\"Calculate the ground truth for all samples in a batch according to\n        the sampling_results.\n\n        Almost the same as the implementation in bbox_head, we passed\n        additional parameters pos_inds_list and neg_inds_list to\n        `_get_targets_single` function.\n\n        Args:\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n            concat (bool): Whether to concatenate the results of all\n                the images in a single batch.\n\n        Returns:\n            Tuple[Tensor]: Ground truth for proposals in a single image.\n            Containing the following list of Tensors:\n\n            - labels (list[Tensor],Tensor): Gt_labels for all\n              proposals in a batch, each tensor in list has\n              shape (num_proposals,) when `concat=False`, otherwise just\n              a single tensor has shape (num_all_proposals,).\n            - label_weights (list[Tensor]): Labels_weights for\n              all proposals in a batch, each tensor in list has shape\n              (num_proposals,) when `concat=False`, otherwise just a\n              single tensor has shape (num_all_proposals,).\n            - bbox_targets (list[Tensor],Tensor): Regression target\n              for all proposals in a batch, each tensor in list has\n              shape (num_proposals, 4) when `concat=False`, otherwise\n              just a single tensor has shape (num_all_proposals, 4),\n              the last dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            - bbox_weights (list[tensor],Tensor): Regression weights for\n              all proposals in a batch, each tensor in list has shape\n              (num_proposals, 4) when `concat=False`, otherwise just a\n              single tensor has shape (num_all_proposals, 4).\n        \"\"\"\n        pos_inds_list = [res.pos_inds for res in sampling_results]\n        neg_inds_list = [res.neg_inds for res in sampling_results]\n        pos_priors_list = [res.pos_priors for res in sampling_results]\n        neg_priors_list = [res.neg_priors for res in sampling_results]\n        pos_gt_bboxes_list = [res.pos_gt_bboxes for res in sampling_results]\n        pos_gt_labels_list = [res.pos_gt_labels for res in sampling_results]\n        labels, label_weights, bbox_targets, bbox_weights = multi_apply(\n            self._get_targets_single,\n            pos_inds_list,\n            neg_inds_list,\n            pos_priors_list,\n            neg_priors_list,\n            pos_gt_bboxes_list,\n            pos_gt_labels_list,\n            cfg=rcnn_train_cfg)\n        if concat:\n            labels = torch.cat(labels, 0)\n            label_weights = torch.cat(label_weights, 0)\n            bbox_targets = torch.cat(bbox_targets, 0)\n            bbox_weights = torch.cat(bbox_weights, 0)\n        return labels, label_weights, bbox_targets, bbox_weights\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/double_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule, ModuleList\nfrom torch import Tensor\n\nfrom mmdet.models.backbones.resnet import Bottleneck\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, MultiConfig, OptConfigType, OptMultiConfig\nfrom .bbox_head import BBoxHead\n\n\nclass BasicResBlock(BaseModule):\n    \"\"\"Basic residual block.\n\n    This block is a little different from the block in the ResNet backbone.\n    The kernel size of conv1 is 1 in this block while 3 in ResNet BasicBlock.\n\n    Args:\n        in_channels (int): Channels of the input feature map.\n        out_channels (int): Channels of the output feature map.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): The config dict\n            for convolution layers.\n        norm_cfg (:obj:`ConfigDict` or dict): The config dict for\n            normalization layers.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n\n        # main path\n        self.conv1 = ConvModule(\n            in_channels,\n            in_channels,\n            kernel_size=3,\n            padding=1,\n            bias=False,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg)\n        self.conv2 = ConvModule(\n            in_channels,\n            out_channels,\n            kernel_size=1,\n            bias=False,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=None)\n\n        # identity path\n        self.conv_identity = ConvModule(\n            in_channels,\n            out_channels,\n            kernel_size=1,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            act_cfg=None)\n\n        self.relu = nn.ReLU(inplace=True)\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function.\"\"\"\n        identity = x\n\n        x = self.conv1(x)\n        x = self.conv2(x)\n\n        identity = self.conv_identity(identity)\n        out = x + identity\n\n        out = self.relu(out)\n        return out\n\n\n@MODELS.register_module()\nclass DoubleConvFCBBoxHead(BBoxHead):\n    r\"\"\"Bbox head used in Double-Head R-CNN\n\n    .. code-block:: none\n\n                                          /-> cls\n                      /-> shared convs ->\n                                          \\-> reg\n        roi features\n                                          /-> cls\n                      \\-> shared fc    ->\n                                          \\-> reg\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 num_convs: int = 0,\n                 num_fcs: int = 0,\n                 conv_out_channels: int = 1024,\n                 fc_out_channels: int = 1024,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(type='BN'),\n                 init_cfg: MultiConfig = dict(\n                     type='Normal',\n                     override=[\n                         dict(type='Normal', name='fc_cls', std=0.01),\n                         dict(type='Normal', name='fc_reg', std=0.001),\n                         dict(\n                             type='Xavier',\n                             name='fc_branch',\n                             distribution='uniform')\n                     ]),\n                 **kwargs) -> None:\n        kwargs.setdefault('with_avg_pool', True)\n        super().__init__(init_cfg=init_cfg, **kwargs)\n        assert self.with_avg_pool\n        assert num_convs > 0\n        assert num_fcs > 0\n        self.num_convs = num_convs\n        self.num_fcs = num_fcs\n        self.conv_out_channels = conv_out_channels\n        self.fc_out_channels = fc_out_channels\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n\n        # increase the channel of input features\n        self.res_block = BasicResBlock(self.in_channels,\n                                       self.conv_out_channels)\n\n        # add conv heads\n        self.conv_branch = self._add_conv_branch()\n        # add fc heads\n        self.fc_branch = self._add_fc_branch()\n\n        out_dim_reg = 4 if self.reg_class_agnostic else 4 * self.num_classes\n        self.fc_reg = nn.Linear(self.conv_out_channels, out_dim_reg)\n\n        self.fc_cls = nn.Linear(self.fc_out_channels, self.num_classes + 1)\n        self.relu = nn.ReLU()\n\n    def _add_conv_branch(self) -> None:\n        \"\"\"Add the fc branch which consists of a sequential of conv layers.\"\"\"\n        branch_convs = ModuleList()\n        for i in range(self.num_convs):\n            branch_convs.append(\n                Bottleneck(\n                    inplanes=self.conv_out_channels,\n                    planes=self.conv_out_channels // 4,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        return branch_convs\n\n    def _add_fc_branch(self) -> None:\n        \"\"\"Add the fc branch which consists of a sequential of fc layers.\"\"\"\n        branch_fcs = ModuleList()\n        for i in range(self.num_fcs):\n            fc_in_channels = (\n                self.in_channels *\n                self.roi_feat_area if i == 0 else self.fc_out_channels)\n            branch_fcs.append(nn.Linear(fc_in_channels, self.fc_out_channels))\n        return branch_fcs\n\n    def forward(self, x_cls: Tensor, x_reg: Tensor) -> Tuple[Tensor]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x_cls (Tensor): Classification features of rois\n            x_reg (Tensor): Regression features from the upstream network.\n\n        Returns:\n            tuple: A tuple of classification scores and bbox prediction.\n\n                - cls_score (Tensor): Classification score predictions of rois.\n                  each roi predicts num_classes + 1 channels.\n                - bbox_pred (Tensor): BBox deltas predictions of rois. each roi\n                  predicts 4 * num_classes channels.\n        \"\"\"\n        # conv head\n        x_conv = self.res_block(x_reg)\n\n        for conv in self.conv_branch:\n            x_conv = conv(x_conv)\n\n        if self.with_avg_pool:\n            x_conv = self.avg_pool(x_conv)\n\n        x_conv = x_conv.view(x_conv.size(0), -1)\n        bbox_pred = self.fc_reg(x_conv)\n\n        # fc head\n        x_fc = x_cls.view(x_cls.size(0), -1)\n        for fc in self.fc_branch:\n            x_fc = self.relu(fc(x_fc))\n\n        cls_score = self.fc_cls(x_fc)\n\n        return cls_score, bbox_pred\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/multi_instance_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple, Union\n\nimport numpy as np\nimport torch\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor, nn\n\nfrom mmdet.models.roi_heads.bbox_heads.bbox_head import BBoxHead\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.utils import empty_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox_overlaps\n\n\n@MODELS.register_module()\nclass MultiInstanceBBoxHead(BBoxHead):\n    r\"\"\"Bbox head used in CrowdDet.\n\n    .. code-block:: none\n\n                                      /-> cls convs_1 -> cls fcs_1 -> cls_1\n                                   |--\n                                   |  \\-> reg convs_1 -> reg fcs_1 -> reg_1\n                                   |\n                                   |  /-> cls convs_2 -> cls fcs_2 -> cls_2\n        shared convs -> shared fcs |--\n                                   |  \\-> reg convs_2 -> reg fcs_2 -> reg_2\n                                   |\n                                   |                     ...\n                                   |\n                                   |  /-> cls convs_k -> cls fcs_k -> cls_k\n                                   |--\n                                      \\-> reg convs_k -> reg fcs_k -> reg_k\n\n\n    Args:\n        num_instance (int): The number of branches after shared fcs.\n            Defaults to 2.\n        with_refine (bool): Whether to use refine module. Defaults to False.\n        num_shared_convs (int): The number of shared convs. Defaults to 0.\n        num_shared_fcs (int): The number of shared fcs. Defaults to 2.\n        num_cls_convs (int): The number of cls convs. Defaults to 0.\n        num_cls_fcs (int): The number of cls fcs. Defaults to 0.\n        num_reg_convs (int): The number of reg convs. Defaults to 0.\n        num_reg_fcs (int): The number of reg fcs. Defaults to 0.\n        conv_out_channels (int): The number of conv out channels.\n            Defaults to 256.\n        fc_out_channels (int): The number of fc out channels. Defaults to 1024.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"  # noqa: W605\n\n    def __init__(self,\n                 num_instance: int = 2,\n                 with_refine: bool = False,\n                 num_shared_convs: int = 0,\n                 num_shared_fcs: int = 2,\n                 num_cls_convs: int = 0,\n                 num_cls_fcs: int = 0,\n                 num_reg_convs: int = 0,\n                 num_reg_fcs: int = 0,\n                 conv_out_channels: int = 256,\n                 fc_out_channels: int = 1024,\n                 init_cfg: Optional[Union[dict, ConfigDict]] = None,\n                 *args,\n                 **kwargs) -> None:\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n        assert (num_shared_convs + num_shared_fcs + num_cls_convs +\n                num_cls_fcs + num_reg_convs + num_reg_fcs > 0)\n        assert num_instance == 2, 'Currently only 2 instances are supported'\n        if num_cls_convs > 0 or num_reg_convs > 0:\n            assert num_shared_fcs == 0\n        if not self.with_cls:\n            assert num_cls_convs == 0 and num_cls_fcs == 0\n        if not self.with_reg:\n            assert num_reg_convs == 0 and num_reg_fcs == 0\n        self.num_instance = num_instance\n        self.num_shared_convs = num_shared_convs\n        self.num_shared_fcs = num_shared_fcs\n        self.num_cls_convs = num_cls_convs\n        self.num_cls_fcs = num_cls_fcs\n        self.num_reg_convs = num_reg_convs\n        self.num_reg_fcs = num_reg_fcs\n        self.conv_out_channels = conv_out_channels\n        self.fc_out_channels = fc_out_channels\n        self.with_refine = with_refine\n\n        # add shared convs and fcs\n        self.shared_convs, self.shared_fcs, last_layer_dim = \\\n            self._add_conv_fc_branch(\n                self.num_shared_convs, self.num_shared_fcs, self.in_channels,\n                True)\n        self.shared_out_channels = last_layer_dim\n        self.relu = nn.ReLU(inplace=True)\n\n        if self.with_refine:\n            refine_model_cfg = {\n                'type': 'Linear',\n                'in_features': self.shared_out_channels + 20,\n                'out_features': self.shared_out_channels\n            }\n            self.shared_fcs_ref = MODELS.build(refine_model_cfg)\n            self.fc_cls_ref = nn.ModuleList()\n            self.fc_reg_ref = nn.ModuleList()\n\n        self.cls_convs = nn.ModuleList()\n        self.cls_fcs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        self.reg_fcs = nn.ModuleList()\n        self.cls_last_dim = list()\n        self.reg_last_dim = list()\n        self.fc_cls = nn.ModuleList()\n        self.fc_reg = nn.ModuleList()\n        for k in range(self.num_instance):\n            # add cls specific branch\n            cls_convs, cls_fcs, cls_last_dim = self._add_conv_fc_branch(\n                self.num_cls_convs, self.num_cls_fcs, self.shared_out_channels)\n            self.cls_convs.append(cls_convs)\n            self.cls_fcs.append(cls_fcs)\n            self.cls_last_dim.append(cls_last_dim)\n\n            # add reg specific branch\n            reg_convs, reg_fcs, reg_last_dim = self._add_conv_fc_branch(\n                self.num_reg_convs, self.num_reg_fcs, self.shared_out_channels)\n            self.reg_convs.append(reg_convs)\n            self.reg_fcs.append(reg_fcs)\n            self.reg_last_dim.append(reg_last_dim)\n\n            if self.num_shared_fcs == 0 and not self.with_avg_pool:\n                if self.num_cls_fcs == 0:\n                    self.cls_last_dim *= self.roi_feat_area\n                if self.num_reg_fcs == 0:\n                    self.reg_last_dim *= self.roi_feat_area\n\n            if self.with_cls:\n                if self.custom_cls_channels:\n                    cls_channels = self.loss_cls.get_cls_channels(\n                        self.num_classes)\n                else:\n                    cls_channels = self.num_classes + 1\n                cls_predictor_cfg_ = self.cls_predictor_cfg.copy()  # deepcopy\n                cls_predictor_cfg_.update(\n                    in_features=self.cls_last_dim[k],\n                    out_features=cls_channels)\n                self.fc_cls.append(MODELS.build(cls_predictor_cfg_))\n                if self.with_refine:\n                    self.fc_cls_ref.append(MODELS.build(cls_predictor_cfg_))\n\n            if self.with_reg:\n                out_dim_reg = (4 if self.reg_class_agnostic else 4 *\n                               self.num_classes)\n                reg_predictor_cfg_ = self.reg_predictor_cfg.copy()\n                reg_predictor_cfg_.update(\n                    in_features=self.reg_last_dim[k], out_features=out_dim_reg)\n                self.fc_reg.append(MODELS.build(reg_predictor_cfg_))\n                if self.with_refine:\n                    self.fc_reg_ref.append(MODELS.build(reg_predictor_cfg_))\n\n        if init_cfg is None:\n            # when init_cfg is None,\n            # It has been set to\n            # [[dict(type='Normal', std=0.01, override=dict(name='fc_cls'))],\n            #  [dict(type='Normal', std=0.001, override=dict(name='fc_reg'))]\n            # after `super(ConvFCBBoxHead, self).__init__()`\n            # we only need to append additional configuration\n            # for `shared_fcs`, `cls_fcs` and `reg_fcs`\n            self.init_cfg += [\n                dict(\n                    type='Xavier',\n                    distribution='uniform',\n                    override=[\n                        dict(name='shared_fcs'),\n                        dict(name='cls_fcs'),\n                        dict(name='reg_fcs')\n                    ])\n            ]\n\n    def _add_conv_fc_branch(self,\n                            num_branch_convs: int,\n                            num_branch_fcs: int,\n                            in_channels: int,\n                            is_shared: bool = False) -> tuple:\n        \"\"\"Add shared or separable branch.\n\n        convs -> avg pool (optional) -> fcs\n        \"\"\"\n        last_layer_dim = in_channels\n        # add branch specific conv layers\n        branch_convs = nn.ModuleList()\n        if num_branch_convs > 0:\n            for i in range(num_branch_convs):\n                conv_in_channels = (\n                    last_layer_dim if i == 0 else self.conv_out_channels)\n                branch_convs.append(\n                    ConvModule(\n                        conv_in_channels, self.conv_out_channels, 3,\n                        padding=1))\n            last_layer_dim = self.conv_out_channels\n        # add branch specific fc layers\n        branch_fcs = nn.ModuleList()\n        if num_branch_fcs > 0:\n            # for shared branch, only consider self.with_avg_pool\n            # for separated branches, also consider self.num_shared_fcs\n            if (is_shared\n                    or self.num_shared_fcs == 0) and not self.with_avg_pool:\n                last_layer_dim *= self.roi_feat_area\n            for i in range(num_branch_fcs):\n                fc_in_channels = (\n                    last_layer_dim if i == 0 else self.fc_out_channels)\n                branch_fcs.append(\n                    nn.Linear(fc_in_channels, self.fc_out_channels))\n            last_layer_dim = self.fc_out_channels\n        return branch_convs, branch_fcs, last_layer_dim\n\n    def forward(self, x: Tuple[Tensor]) -> tuple:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of classification scores and bbox prediction.\n\n                - cls_score (Tensor): Classification scores for all scale\n                  levels, each is a 4D-tensor, the channels number is\n                  num_base_priors * num_classes.\n                - bbox_pred (Tensor): Box energies / deltas for all scale\n                  levels, each is a 4D-tensor, the channels number is\n                  num_base_priors * 4.\n                - cls_score_ref (Tensor): The cls_score after refine model.\n                - bbox_pred_ref (Tensor): The bbox_pred after refine model.\n        \"\"\"\n        # shared part\n        if self.num_shared_convs > 0:\n            for conv in self.shared_convs:\n                x = conv(x)\n\n        if self.num_shared_fcs > 0:\n            if self.with_avg_pool:\n                x = self.avg_pool(x)\n\n            x = x.flatten(1)\n            for fc in self.shared_fcs:\n                x = self.relu(fc(x))\n\n        x_cls = x\n        x_reg = x\n        # separate branches\n        cls_score = list()\n        bbox_pred = list()\n        for k in range(self.num_instance):\n            for conv in self.cls_convs[k]:\n                x_cls = conv(x_cls)\n            if x_cls.dim() > 2:\n                if self.with_avg_pool:\n                    x_cls = self.avg_pool(x_cls)\n                x_cls = x_cls.flatten(1)\n            for fc in self.cls_fcs[k]:\n                x_cls = self.relu(fc(x_cls))\n\n            for conv in self.reg_convs[k]:\n                x_reg = conv(x_reg)\n            if x_reg.dim() > 2:\n                if self.with_avg_pool:\n                    x_reg = self.avg_pool(x_reg)\n                x_reg = x_reg.flatten(1)\n            for fc in self.reg_fcs[k]:\n                x_reg = self.relu(fc(x_reg))\n\n            cls_score.append(self.fc_cls[k](x_cls) if self.with_cls else None)\n            bbox_pred.append(self.fc_reg[k](x_reg) if self.with_reg else None)\n\n        if self.with_refine:\n            x_ref = x\n            cls_score_ref = list()\n            bbox_pred_ref = list()\n            for k in range(self.num_instance):\n                feat_ref = cls_score[k].softmax(dim=-1)\n                feat_ref = torch.cat((bbox_pred[k], feat_ref[:, 1][:, None]),\n                                     dim=1).repeat(1, 4)\n                feat_ref = torch.cat((x_ref, feat_ref), dim=1)\n                feat_ref = F.relu_(self.shared_fcs_ref(feat_ref))\n\n                cls_score_ref.append(self.fc_cls_ref[k](feat_ref))\n                bbox_pred_ref.append(self.fc_reg_ref[k](feat_ref))\n\n            cls_score = torch.cat(cls_score, dim=1)\n            bbox_pred = torch.cat(bbox_pred, dim=1)\n            cls_score_ref = torch.cat(cls_score_ref, dim=1)\n            bbox_pred_ref = torch.cat(bbox_pred_ref, dim=1)\n            return cls_score, bbox_pred, cls_score_ref, bbox_pred_ref\n\n        cls_score = torch.cat(cls_score, dim=1)\n        bbox_pred = torch.cat(bbox_pred, dim=1)\n\n        return cls_score, bbox_pred\n\n    def get_targets(self,\n                    sampling_results: List[SamplingResult],\n                    rcnn_train_cfg: ConfigDict,\n                    concat: bool = True) -> tuple:\n        \"\"\"Calculate the ground truth for all samples in a batch according to\n        the sampling_results.\n\n        Almost the same as the implementation in bbox_head, we passed\n        additional parameters pos_inds_list and neg_inds_list to\n        `_get_targets_single` function.\n\n        Args:\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n            concat (bool): Whether to concatenate the results of all\n                the images in a single batch.\n\n        Returns:\n            Tuple[Tensor]: Ground truth for proposals in a single image.\n            Containing the following list of Tensors:\n\n            - labels (list[Tensor],Tensor): Gt_labels for all proposals in a\n              batch, each tensor in list has shape (num_proposals,) when\n              `concat=False`, otherwise just a single tensor has shape\n              (num_all_proposals,).\n            - label_weights (list[Tensor]): Labels_weights for\n              all proposals in a batch, each tensor in list has shape\n              (num_proposals,) when `concat=False`, otherwise just a single\n              tensor has shape (num_all_proposals,).\n            - bbox_targets (list[Tensor],Tensor): Regression target for all\n              proposals in a batch, each tensor in list has shape\n              (num_proposals, 4) when `concat=False`, otherwise just a single\n              tensor has shape (num_all_proposals, 4), the last dimension 4\n              represents [tl_x, tl_y, br_x, br_y].\n            - bbox_weights (list[tensor],Tensor): Regression weights for\n              all proposals in a batch, each tensor in list has shape\n              (num_proposals, 4) when `concat=False`, otherwise just a\n              single tensor has shape (num_all_proposals, 4).\n        \"\"\"\n        labels = []\n        bbox_targets = []\n        bbox_weights = []\n        label_weights = []\n        for i in range(len(sampling_results)):\n            sample_bboxes = torch.cat([\n                sampling_results[i].pos_gt_bboxes,\n                sampling_results[i].neg_gt_bboxes\n            ])\n            sample_priors = sampling_results[i].priors\n            sample_priors = sample_priors.repeat(1, self.num_instance).reshape(\n                -1, 4)\n            sample_bboxes = sample_bboxes.reshape(-1, 4)\n\n            if not self.reg_decoded_bbox:\n                _bbox_targets = self.bbox_coder.encode(sample_priors,\n                                                       sample_bboxes)\n            else:\n                _bbox_targets = sample_priors\n            _bbox_targets = _bbox_targets.reshape(-1, self.num_instance * 4)\n            _bbox_weights = torch.ones(_bbox_targets.shape)\n            _labels = torch.cat([\n                sampling_results[i].pos_gt_labels,\n                sampling_results[i].neg_gt_labels\n            ])\n            _labels_weights = torch.ones(_labels.shape)\n\n            bbox_targets.append(_bbox_targets)\n            bbox_weights.append(_bbox_weights)\n            labels.append(_labels)\n            label_weights.append(_labels_weights)\n\n        if concat:\n            labels = torch.cat(labels, 0)\n            label_weights = torch.cat(label_weights, 0)\n            bbox_targets = torch.cat(bbox_targets, 0)\n            bbox_weights = torch.cat(bbox_weights, 0)\n        return labels, label_weights, bbox_targets, bbox_weights\n\n    def loss(self, cls_score: Tensor, bbox_pred: Tensor, rois: Tensor,\n             labels: Tensor, label_weights: Tensor, bbox_targets: Tensor,\n             bbox_weights: Tensor, **kwargs) -> dict:\n        \"\"\"Calculate the loss based on the network predictions and targets.\n\n        Args:\n            cls_score (Tensor): Classification prediction results of all class,\n                has shape (batch_size * num_proposals_single_image,\n                (num_classes + 1) * k), k represents the number of prediction\n                boxes generated by each proposal box.\n            bbox_pred (Tensor): Regression prediction results, has shape\n                (batch_size * num_proposals_single_image, 4 * k), the last\n                dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            rois (Tensor): RoIs with the shape\n                (batch_size * num_proposals_single_image, 5) where the first\n                column indicates batch id of each RoI.\n            labels (Tensor): Gt_labels for all proposals in a batch, has\n                shape (batch_size * num_proposals_single_image, k).\n            label_weights (Tensor): Labels_weights for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, k).\n            bbox_targets (Tensor): Regression target for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image,\n                4 * k), the last dimension 4 represents [tl_x, tl_y, br_x,\n                br_y].\n            bbox_weights (Tensor): Regression weights for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image,\n                4 * k).\n\n        Returns:\n            dict: A dictionary of loss.\n        \"\"\"\n        losses = dict()\n        if bbox_pred.numel():\n            loss_0 = self.emd_loss(bbox_pred[:, 0:4], cls_score[:, 0:2],\n                                   bbox_pred[:, 4:8], cls_score[:, 2:4],\n                                   bbox_targets, labels)\n            loss_1 = self.emd_loss(bbox_pred[:, 4:8], cls_score[:, 2:4],\n                                   bbox_pred[:, 0:4], cls_score[:, 0:2],\n                                   bbox_targets, labels)\n            loss = torch.cat([loss_0, loss_1], dim=1)\n            _, min_indices = loss.min(dim=1)\n            loss_emd = loss[torch.arange(loss.shape[0]), min_indices]\n            loss_emd = loss_emd.mean()\n        else:\n            loss_emd = bbox_pred.sum()\n        losses['loss_rcnn_emd'] = loss_emd\n        return losses\n\n    def emd_loss(self, bbox_pred_0: Tensor, cls_score_0: Tensor,\n                 bbox_pred_1: Tensor, cls_score_1: Tensor, targets: Tensor,\n                 labels: Tensor) -> Tensor:\n        \"\"\"Calculate the emd loss.\n\n        Note:\n            This implementation is modified from https://github.com/Purkialo/\n            CrowdDet/blob/master/lib/det_oprs/loss_opr.py\n\n        Args:\n            bbox_pred_0 (Tensor): Part of regression prediction results, has\n                shape (batch_size * num_proposals_single_image, 4), the last\n                dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            cls_score_0 (Tensor): Part of classification prediction results,\n                has shape (batch_size * num_proposals_single_image,\n                (num_classes + 1)), where 1 represents the background.\n            bbox_pred_1 (Tensor): The other part of regression prediction\n                results, has shape (batch_size*num_proposals_single_image, 4).\n            cls_score_1 (Tensor):The other part of classification prediction\n                results, has shape (batch_size * num_proposals_single_image,\n                (num_classes + 1)).\n            targets (Tensor):Regression target for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image,\n                4 * k), the last dimension 4 represents [tl_x, tl_y, br_x,\n                br_y], k represents the number of prediction boxes generated\n                by each proposal box.\n            labels (Tensor): Gt_labels for all proposals in a batch, has\n                shape (batch_size * num_proposals_single_image, k).\n\n        Returns:\n            torch.Tensor: The calculated loss.\n        \"\"\"\n\n        bbox_pred = torch.cat([bbox_pred_0, bbox_pred_1],\n                              dim=1).reshape(-1, bbox_pred_0.shape[-1])\n        cls_score = torch.cat([cls_score_0, cls_score_1],\n                              dim=1).reshape(-1, cls_score_0.shape[-1])\n        targets = targets.reshape(-1, 4)\n        labels = labels.long().flatten()\n\n        # masks\n        valid_masks = labels >= 0\n        fg_masks = labels > 0\n\n        # multiple class\n        bbox_pred = bbox_pred.reshape(-1, self.num_classes, 4)\n        fg_gt_classes = labels[fg_masks]\n        bbox_pred = bbox_pred[fg_masks, fg_gt_classes - 1, :]\n\n        # loss for regression\n        loss_bbox = self.loss_bbox(bbox_pred, targets[fg_masks])\n        loss_bbox = loss_bbox.sum(dim=1)\n\n        # loss for classification\n        labels = labels * valid_masks\n        loss_cls = self.loss_cls(cls_score, labels)\n\n        loss_cls[fg_masks] = loss_cls[fg_masks] + loss_bbox\n        loss = loss_cls.reshape(-1, 2).sum(dim=1)\n        return loss.reshape(-1, 1)\n\n    def _predict_by_feat_single(\n            self,\n            roi: Tensor,\n            cls_score: Tensor,\n            bbox_pred: Tensor,\n            img_meta: dict,\n            rescale: bool = False,\n            rcnn_test_cfg: Optional[ConfigDict] = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            roi (Tensor): Boxes to be transformed. Has shape (num_boxes, 5).\n                last dimension 5 arrange as (batch_index, x1, y1, x2, y2).\n            cls_score (Tensor): Box scores, has shape\n                (num_boxes, num_classes + 1).\n            bbox_pred (Tensor): Box energies / deltas. has shape\n                (num_boxes, num_classes * 4).\n            img_meta (dict): image information.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of Bbox Head.\n                Defaults to None\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n\n        cls_score = cls_score.reshape(-1, self.num_classes + 1)\n        bbox_pred = bbox_pred.reshape(-1, 4)\n        roi = roi.repeat_interleave(self.num_instance, dim=0)\n\n        results = InstanceData()\n        if roi.shape[0] == 0:\n            return empty_instances([img_meta],\n                                   roi.device,\n                                   task_type='bbox',\n                                   instance_results=[results])[0]\n\n        scores = cls_score.softmax(dim=-1) if cls_score is not None else None\n        img_shape = img_meta['img_shape']\n        bboxes = self.bbox_coder.decode(\n            roi[..., 1:], bbox_pred, max_shape=img_shape)\n\n        if rescale and bboxes.size(0) > 0:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = bboxes.new_tensor(img_meta['scale_factor']).repeat(\n                (1, 2))\n            bboxes = (bboxes.view(bboxes.size(0), -1, 4) / scale_factor).view(\n                bboxes.size()[0], -1)\n\n        if rcnn_test_cfg is None:\n            # This means that it is aug test.\n            # It needs to return the raw results without nms.\n            results.bboxes = bboxes\n            results.scores = scores\n        else:\n            roi_idx = np.tile(\n                np.arange(bboxes.shape[0] / self.num_instance)[:, None],\n                (1, self.num_instance)).reshape(-1, 1)[:, 0]\n            roi_idx = torch.from_numpy(roi_idx).to(bboxes.device).reshape(\n                -1, 1)\n            bboxes = torch.cat([bboxes, roi_idx], dim=1)\n            det_bboxes, det_scores = self.set_nms(\n                bboxes, scores[:, 1], rcnn_test_cfg.score_thr,\n                rcnn_test_cfg.nms['iou_threshold'], rcnn_test_cfg.max_per_img)\n\n            results.bboxes = det_bboxes[:, :-1]\n            results.scores = det_scores\n            results.labels = torch.zeros_like(det_scores)\n\n        return results\n\n    @staticmethod\n    def set_nms(bboxes: Tensor,\n                scores: Tensor,\n                score_thr: float,\n                iou_threshold: float,\n                max_num: int = -1) -> Tuple[Tensor, Tensor]:\n        \"\"\"NMS for multi-instance prediction. Please refer to\n        https://github.com/Purkialo/CrowdDet for more details.\n\n        Args:\n            bboxes (Tensor): predict bboxes.\n            scores (Tensor): The score of each predict bbox.\n            score_thr (float): bbox threshold, bboxes with scores lower than it\n                will not be considered.\n            iou_threshold (float): IoU threshold to be considered as\n                conflicted.\n            max_num (int, optional): if there are more than max_num bboxes\n                after NMS, only top max_num will be kept. Default to -1.\n\n        Returns:\n            Tuple[Tensor, Tensor]: (bboxes, scores).\n        \"\"\"\n\n        bboxes = bboxes[scores > score_thr]\n        scores = scores[scores > score_thr]\n\n        ordered_scores, order = scores.sort(descending=True)\n        ordered_bboxes = bboxes[order]\n        roi_idx = ordered_bboxes[:, -1]\n\n        keep = torch.ones(len(ordered_bboxes)) == 1\n        ruler = torch.arange(len(ordered_bboxes))\n\n        keep = keep.to(bboxes.device)\n        ruler = ruler.to(bboxes.device)\n\n        while ruler.shape[0] > 0:\n            basement = ruler[0]\n            ruler = ruler[1:]\n            idx = roi_idx[basement]\n            # calculate the body overlap\n            basement_bbox = ordered_bboxes[:, :4][basement].reshape(-1, 4)\n            ruler_bbox = ordered_bboxes[:, :4][ruler].reshape(-1, 4)\n            overlap = bbox_overlaps(basement_bbox, ruler_bbox)\n            indices = torch.where(overlap > iou_threshold)[1]\n            loc = torch.where(roi_idx[ruler][indices] == idx)\n            # the mask won't change in the step\n            mask = keep[ruler[indices][loc]]\n            keep[ruler[indices]] = False\n            keep[ruler[indices][loc][mask]] = True\n            ruler[~keep[ruler]] = -1\n            ruler = ruler[ruler > 0]\n\n        keep = keep[order.sort()[1]]\n        return bboxes[keep][:max_num, :], scores[keep][:max_num]\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/sabl_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Sequence, Tuple\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.layers import multiclass_nms\nfrom mmdet.models.losses import accuracy\nfrom mmdet.models.task_modules import SamplingResult\nfrom mmdet.models.utils import multi_apply\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import ConfigType, InstanceList, OptConfigType, OptMultiConfig\nfrom .bbox_head import BBoxHead\n\n\n@MODELS.register_module()\nclass SABLHead(BBoxHead):\n    \"\"\"Side-Aware Boundary Localization (SABL) for RoI-Head.\n\n    Side-Aware features are extracted by conv layers\n    with an attention mechanism.\n    Boundary Localization with Bucketing and Bucketing Guided Rescoring\n    are implemented in BucketingBBoxCoder.\n\n    Please refer to https://arxiv.org/abs/1912.04260 for more details.\n\n    Args:\n        cls_in_channels (int): Input channels of cls RoI feature. \\\n            Defaults to 256.\n        reg_in_channels (int): Input channels of reg RoI feature. \\\n            Defaults to 256.\n        roi_feat_size (int): Size of RoI features. Defaults to 7.\n        reg_feat_up_ratio (int): Upsample ratio of reg features. \\\n            Defaults to 2.\n        reg_pre_kernel (int): Kernel of 2D conv layers before \\\n            attention pooling. Defaults to 3.\n        reg_post_kernel (int): Kernel of 1D conv layers after \\\n            attention pooling. Defaults to 3.\n        reg_pre_num (int): Number of pre convs. Defaults to 2.\n        reg_post_num (int): Number of post convs. Defaults to 1.\n        num_classes (int): Number of classes in dataset. Defaults to 80.\n        cls_out_channels (int): Hidden channels in cls fcs. Defaults to 1024.\n        reg_offset_out_channels (int): Hidden and output channel \\\n            of reg offset branch. Defaults to 256.\n        reg_cls_out_channels (int): Hidden and output channel \\\n            of reg cls branch. Defaults to 256.\n        num_cls_fcs (int): Number of fcs for cls branch. Defaults to 1.\n        num_reg_fcs (int): Number of fcs for reg branch.. Defaults to 0.\n        reg_class_agnostic (bool): Class agnostic regression or not. \\\n            Defaults to True.\n        norm_cfg (dict): Config of norm layers. Defaults to None.\n        bbox_coder (dict): Config of bbox coder. Defaults 'BucketingBBoxCoder'.\n        loss_cls (dict): Config of classification loss.\n        loss_bbox_cls (dict): Config of classification loss for bbox branch.\n        loss_bbox_reg (dict): Config of regression loss for bbox branch.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 cls_in_channels: int = 256,\n                 reg_in_channels: int = 256,\n                 roi_feat_size: int = 7,\n                 reg_feat_up_ratio: int = 2,\n                 reg_pre_kernel: int = 3,\n                 reg_post_kernel: int = 3,\n                 reg_pre_num: int = 2,\n                 reg_post_num: int = 1,\n                 cls_out_channels: int = 1024,\n                 reg_offset_out_channels: int = 256,\n                 reg_cls_out_channels: int = 256,\n                 num_cls_fcs: int = 1,\n                 num_reg_fcs: int = 0,\n                 reg_class_agnostic: bool = True,\n                 norm_cfg: OptConfigType = None,\n                 bbox_coder: ConfigType = dict(\n                     type='BucketingBBoxCoder',\n                     num_buckets=14,\n                     scale_factor=1.7),\n                 loss_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=False,\n                     loss_weight=1.0),\n                 loss_bbox_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     loss_weight=1.0),\n                 loss_bbox_reg: ConfigType = dict(\n                     type='SmoothL1Loss', beta=0.1, loss_weight=1.0),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super(BBoxHead, self).__init__(init_cfg=init_cfg)\n        self.cls_in_channels = cls_in_channels\n        self.reg_in_channels = reg_in_channels\n        self.roi_feat_size = roi_feat_size\n        self.reg_feat_up_ratio = int(reg_feat_up_ratio)\n        self.num_buckets = bbox_coder['num_buckets']\n        assert self.reg_feat_up_ratio // 2 >= 1\n        self.up_reg_feat_size = roi_feat_size * self.reg_feat_up_ratio\n        assert self.up_reg_feat_size == bbox_coder['num_buckets']\n        self.reg_pre_kernel = reg_pre_kernel\n        self.reg_post_kernel = reg_post_kernel\n        self.reg_pre_num = reg_pre_num\n        self.reg_post_num = reg_post_num\n        self.num_classes = num_classes\n        self.cls_out_channels = cls_out_channels\n        self.reg_offset_out_channels = reg_offset_out_channels\n        self.reg_cls_out_channels = reg_cls_out_channels\n        self.num_cls_fcs = num_cls_fcs\n        self.num_reg_fcs = num_reg_fcs\n        self.reg_class_agnostic = reg_class_agnostic\n        assert self.reg_class_agnostic\n        self.norm_cfg = norm_cfg\n\n        self.bbox_coder = TASK_UTILS.build(bbox_coder)\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox_cls = MODELS.build(loss_bbox_cls)\n        self.loss_bbox_reg = MODELS.build(loss_bbox_reg)\n\n        self.cls_fcs = self._add_fc_branch(self.num_cls_fcs,\n                                           self.cls_in_channels,\n                                           self.roi_feat_size,\n                                           self.cls_out_channels)\n\n        self.side_num = int(np.ceil(self.num_buckets / 2))\n\n        if self.reg_feat_up_ratio > 1:\n            self.upsample_x = nn.ConvTranspose1d(\n                reg_in_channels,\n                reg_in_channels,\n                self.reg_feat_up_ratio,\n                stride=self.reg_feat_up_ratio)\n            self.upsample_y = nn.ConvTranspose1d(\n                reg_in_channels,\n                reg_in_channels,\n                self.reg_feat_up_ratio,\n                stride=self.reg_feat_up_ratio)\n\n        self.reg_pre_convs = nn.ModuleList()\n        for i in range(self.reg_pre_num):\n            reg_pre_conv = ConvModule(\n                reg_in_channels,\n                reg_in_channels,\n                kernel_size=reg_pre_kernel,\n                padding=reg_pre_kernel // 2,\n                norm_cfg=norm_cfg,\n                act_cfg=dict(type='ReLU'))\n            self.reg_pre_convs.append(reg_pre_conv)\n\n        self.reg_post_conv_xs = nn.ModuleList()\n        for i in range(self.reg_post_num):\n            reg_post_conv_x = ConvModule(\n                reg_in_channels,\n                reg_in_channels,\n                kernel_size=(1, reg_post_kernel),\n                padding=(0, reg_post_kernel // 2),\n                norm_cfg=norm_cfg,\n                act_cfg=dict(type='ReLU'))\n            self.reg_post_conv_xs.append(reg_post_conv_x)\n        self.reg_post_conv_ys = nn.ModuleList()\n        for i in range(self.reg_post_num):\n            reg_post_conv_y = ConvModule(\n                reg_in_channels,\n                reg_in_channels,\n                kernel_size=(reg_post_kernel, 1),\n                padding=(reg_post_kernel // 2, 0),\n                norm_cfg=norm_cfg,\n                act_cfg=dict(type='ReLU'))\n            self.reg_post_conv_ys.append(reg_post_conv_y)\n\n        self.reg_conv_att_x = nn.Conv2d(reg_in_channels, 1, 1)\n        self.reg_conv_att_y = nn.Conv2d(reg_in_channels, 1, 1)\n\n        self.fc_cls = nn.Linear(self.cls_out_channels, self.num_classes + 1)\n        self.relu = nn.ReLU(inplace=True)\n\n        self.reg_cls_fcs = self._add_fc_branch(self.num_reg_fcs,\n                                               self.reg_in_channels, 1,\n                                               self.reg_cls_out_channels)\n        self.reg_offset_fcs = self._add_fc_branch(self.num_reg_fcs,\n                                                  self.reg_in_channels, 1,\n                                                  self.reg_offset_out_channels)\n        self.fc_reg_cls = nn.Linear(self.reg_cls_out_channels, 1)\n        self.fc_reg_offset = nn.Linear(self.reg_offset_out_channels, 1)\n\n        if init_cfg is None:\n            self.init_cfg = [\n                dict(\n                    type='Xavier',\n                    layer='Linear',\n                    distribution='uniform',\n                    override=[\n                        dict(type='Normal', name='reg_conv_att_x', std=0.01),\n                        dict(type='Normal', name='reg_conv_att_y', std=0.01),\n                        dict(type='Normal', name='fc_reg_cls', std=0.01),\n                        dict(type='Normal', name='fc_cls', std=0.01),\n                        dict(type='Normal', name='fc_reg_offset', std=0.001)\n                    ])\n            ]\n            if self.reg_feat_up_ratio > 1:\n                self.init_cfg += [\n                    dict(\n                        type='Kaiming',\n                        distribution='normal',\n                        override=[\n                            dict(name='upsample_x'),\n                            dict(name='upsample_y')\n                        ])\n                ]\n\n    def _add_fc_branch(self, num_branch_fcs: int, in_channels: int,\n                       roi_feat_size: int,\n                       fc_out_channels: int) -> nn.ModuleList:\n        \"\"\"build fc layers.\"\"\"\n        in_channels = in_channels * roi_feat_size * roi_feat_size\n        branch_fcs = nn.ModuleList()\n        for i in range(num_branch_fcs):\n            fc_in_channels = (in_channels if i == 0 else fc_out_channels)\n            branch_fcs.append(nn.Linear(fc_in_channels, fc_out_channels))\n        return branch_fcs\n\n    def cls_forward(self, cls_x: Tensor) -> Tensor:\n        \"\"\"forward of classification fc layers.\"\"\"\n        cls_x = cls_x.view(cls_x.size(0), -1)\n        for fc in self.cls_fcs:\n            cls_x = self.relu(fc(cls_x))\n        cls_score = self.fc_cls(cls_x)\n        return cls_score\n\n    def attention_pool(self, reg_x: Tensor) -> tuple:\n        \"\"\"Extract direction-specific features fx and fy with attention\n        methanism.\"\"\"\n        reg_fx = reg_x\n        reg_fy = reg_x\n        reg_fx_att = self.reg_conv_att_x(reg_fx).sigmoid()\n        reg_fy_att = self.reg_conv_att_y(reg_fy).sigmoid()\n        reg_fx_att = reg_fx_att / reg_fx_att.sum(dim=2).unsqueeze(2)\n        reg_fy_att = reg_fy_att / reg_fy_att.sum(dim=3).unsqueeze(3)\n        reg_fx = (reg_fx * reg_fx_att).sum(dim=2)\n        reg_fy = (reg_fy * reg_fy_att).sum(dim=3)\n        return reg_fx, reg_fy\n\n    def side_aware_feature_extractor(self, reg_x: Tensor) -> tuple:\n        \"\"\"Refine and extract side-aware features without split them.\"\"\"\n        for reg_pre_conv in self.reg_pre_convs:\n            reg_x = reg_pre_conv(reg_x)\n        reg_fx, reg_fy = self.attention_pool(reg_x)\n\n        if self.reg_post_num > 0:\n            reg_fx = reg_fx.unsqueeze(2)\n            reg_fy = reg_fy.unsqueeze(3)\n            for i in range(self.reg_post_num):\n                reg_fx = self.reg_post_conv_xs[i](reg_fx)\n                reg_fy = self.reg_post_conv_ys[i](reg_fy)\n            reg_fx = reg_fx.squeeze(2)\n            reg_fy = reg_fy.squeeze(3)\n        if self.reg_feat_up_ratio > 1:\n            reg_fx = self.relu(self.upsample_x(reg_fx))\n            reg_fy = self.relu(self.upsample_y(reg_fy))\n        reg_fx = torch.transpose(reg_fx, 1, 2)\n        reg_fy = torch.transpose(reg_fy, 1, 2)\n        return reg_fx.contiguous(), reg_fy.contiguous()\n\n    def reg_pred(self, x: Tensor, offset_fcs: nn.ModuleList,\n                 cls_fcs: nn.ModuleList) -> tuple:\n        \"\"\"Predict bucketing estimation (cls_pred) and fine regression (offset\n        pred) with side-aware features.\"\"\"\n        x_offset = x.view(-1, self.reg_in_channels)\n        x_cls = x.view(-1, self.reg_in_channels)\n\n        for fc in offset_fcs:\n            x_offset = self.relu(fc(x_offset))\n        for fc in cls_fcs:\n            x_cls = self.relu(fc(x_cls))\n        offset_pred = self.fc_reg_offset(x_offset)\n        cls_pred = self.fc_reg_cls(x_cls)\n\n        offset_pred = offset_pred.view(x.size(0), -1)\n        cls_pred = cls_pred.view(x.size(0), -1)\n\n        return offset_pred, cls_pred\n\n    def side_aware_split(self, feat: Tensor) -> Tensor:\n        \"\"\"Split side-aware features aligned with orders of bucketing\n        targets.\"\"\"\n        l_end = int(np.ceil(self.up_reg_feat_size / 2))\n        r_start = int(np.floor(self.up_reg_feat_size / 2))\n        feat_fl = feat[:, :l_end]\n        feat_fr = feat[:, r_start:].flip(dims=(1, ))\n        feat_fl = feat_fl.contiguous()\n        feat_fr = feat_fr.contiguous()\n        feat = torch.cat([feat_fl, feat_fr], dim=-1)\n        return feat\n\n    def bbox_pred_split(self, bbox_pred: tuple,\n                        num_proposals_per_img: Sequence[int]) -> tuple:\n        \"\"\"Split batch bbox prediction back to each image.\"\"\"\n        bucket_cls_preds, bucket_offset_preds = bbox_pred\n        bucket_cls_preds = bucket_cls_preds.split(num_proposals_per_img, 0)\n        bucket_offset_preds = bucket_offset_preds.split(\n            num_proposals_per_img, 0)\n        bbox_pred = tuple(zip(bucket_cls_preds, bucket_offset_preds))\n        return bbox_pred\n\n    def reg_forward(self, reg_x: Tensor) -> tuple:\n        \"\"\"forward of regression branch.\"\"\"\n        outs = self.side_aware_feature_extractor(reg_x)\n        edge_offset_preds = []\n        edge_cls_preds = []\n        reg_fx = outs[0]\n        reg_fy = outs[1]\n        offset_pred_x, cls_pred_x = self.reg_pred(reg_fx, self.reg_offset_fcs,\n                                                  self.reg_cls_fcs)\n        offset_pred_y, cls_pred_y = self.reg_pred(reg_fy, self.reg_offset_fcs,\n                                                  self.reg_cls_fcs)\n        offset_pred_x = self.side_aware_split(offset_pred_x)\n        offset_pred_y = self.side_aware_split(offset_pred_y)\n        cls_pred_x = self.side_aware_split(cls_pred_x)\n        cls_pred_y = self.side_aware_split(cls_pred_y)\n        edge_offset_preds = torch.cat([offset_pred_x, offset_pred_y], dim=-1)\n        edge_cls_preds = torch.cat([cls_pred_x, cls_pred_y], dim=-1)\n\n        return edge_cls_preds, edge_offset_preds\n\n    def forward(self, x: Tensor) -> tuple:\n        \"\"\"Forward features from the upstream network.\"\"\"\n        bbox_pred = self.reg_forward(x)\n        cls_score = self.cls_forward(x)\n\n        return cls_score, bbox_pred\n\n    def get_targets(self,\n                    sampling_results: List[SamplingResult],\n                    rcnn_train_cfg: ConfigDict,\n                    concat: bool = True) -> tuple:\n        \"\"\"Calculate the ground truth for all samples in a batch according to\n        the sampling_results.\"\"\"\n        pos_proposals = [res.pos_bboxes for res in sampling_results]\n        neg_proposals = [res.neg_bboxes for res in sampling_results]\n        pos_gt_bboxes = [res.pos_gt_bboxes for res in sampling_results]\n        pos_gt_labels = [res.pos_gt_labels for res in sampling_results]\n        cls_reg_targets = self.bucket_target(\n            pos_proposals,\n            neg_proposals,\n            pos_gt_bboxes,\n            pos_gt_labels,\n            rcnn_train_cfg,\n            concat=concat)\n        (labels, label_weights, bucket_cls_targets, bucket_cls_weights,\n         bucket_offset_targets, bucket_offset_weights) = cls_reg_targets\n        return (labels, label_weights, (bucket_cls_targets,\n                                        bucket_offset_targets),\n                (bucket_cls_weights, bucket_offset_weights))\n\n    def bucket_target(self,\n                      pos_proposals_list: list,\n                      neg_proposals_list: list,\n                      pos_gt_bboxes_list: list,\n                      pos_gt_labels_list: list,\n                      rcnn_train_cfg: ConfigDict,\n                      concat: bool = True) -> tuple:\n        \"\"\"Compute bucketing estimation targets and fine regression targets for\n        a batch of images.\"\"\"\n        (labels, label_weights, bucket_cls_targets, bucket_cls_weights,\n         bucket_offset_targets, bucket_offset_weights) = multi_apply(\n             self._bucket_target_single,\n             pos_proposals_list,\n             neg_proposals_list,\n             pos_gt_bboxes_list,\n             pos_gt_labels_list,\n             cfg=rcnn_train_cfg)\n\n        if concat:\n            labels = torch.cat(labels, 0)\n            label_weights = torch.cat(label_weights, 0)\n            bucket_cls_targets = torch.cat(bucket_cls_targets, 0)\n            bucket_cls_weights = torch.cat(bucket_cls_weights, 0)\n            bucket_offset_targets = torch.cat(bucket_offset_targets, 0)\n            bucket_offset_weights = torch.cat(bucket_offset_weights, 0)\n        return (labels, label_weights, bucket_cls_targets, bucket_cls_weights,\n                bucket_offset_targets, bucket_offset_weights)\n\n    def _bucket_target_single(self, pos_proposals: Tensor,\n                              neg_proposals: Tensor, pos_gt_bboxes: Tensor,\n                              pos_gt_labels: Tensor, cfg: ConfigDict) -> tuple:\n        \"\"\"Compute bucketing estimation targets and fine regression targets for\n        a single image.\n\n        Args:\n            pos_proposals (Tensor): positive proposals of a single image,\n                 Shape (n_pos, 4)\n            neg_proposals (Tensor): negative proposals of a single image,\n                 Shape (n_neg, 4).\n            pos_gt_bboxes (Tensor): gt bboxes assigned to positive proposals\n                 of a single image, Shape (n_pos, 4).\n            pos_gt_labels (Tensor): gt labels assigned to positive proposals\n                 of a single image, Shape (n_pos, ).\n            cfg (dict): Config of calculating targets\n\n        Returns:\n            tuple:\n\n            - labels (Tensor): Labels in a single image. Shape (n,).\n            - label_weights (Tensor): Label weights in a single image.\n                Shape (n,)\n            - bucket_cls_targets (Tensor): Bucket cls targets in\n                a single image. Shape (n, num_buckets*2).\n            - bucket_cls_weights (Tensor): Bucket cls weights in\n                a single image. Shape (n, num_buckets*2).\n            - bucket_offset_targets (Tensor): Bucket offset targets\n                in a single image. Shape (n, num_buckets*2).\n            - bucket_offset_targets (Tensor): Bucket offset weights\n                in a single image. Shape (n, num_buckets*2).\n        \"\"\"\n        num_pos = pos_proposals.size(0)\n        num_neg = neg_proposals.size(0)\n        num_samples = num_pos + num_neg\n        labels = pos_gt_bboxes.new_full((num_samples, ),\n                                        self.num_classes,\n                                        dtype=torch.long)\n        label_weights = pos_proposals.new_zeros(num_samples)\n        bucket_cls_targets = pos_proposals.new_zeros(num_samples,\n                                                     4 * self.side_num)\n        bucket_cls_weights = pos_proposals.new_zeros(num_samples,\n                                                     4 * self.side_num)\n        bucket_offset_targets = pos_proposals.new_zeros(\n            num_samples, 4 * self.side_num)\n        bucket_offset_weights = pos_proposals.new_zeros(\n            num_samples, 4 * self.side_num)\n        if num_pos > 0:\n            labels[:num_pos] = pos_gt_labels\n            label_weights[:num_pos] = 1.0\n            (pos_bucket_offset_targets, pos_bucket_offset_weights,\n             pos_bucket_cls_targets,\n             pos_bucket_cls_weights) = self.bbox_coder.encode(\n                 pos_proposals, pos_gt_bboxes)\n            bucket_cls_targets[:num_pos, :] = pos_bucket_cls_targets\n            bucket_cls_weights[:num_pos, :] = pos_bucket_cls_weights\n            bucket_offset_targets[:num_pos, :] = pos_bucket_offset_targets\n            bucket_offset_weights[:num_pos, :] = pos_bucket_offset_weights\n        if num_neg > 0:\n            label_weights[-num_neg:] = 1.0\n        return (labels, label_weights, bucket_cls_targets, bucket_cls_weights,\n                bucket_offset_targets, bucket_offset_weights)\n\n    def loss(self,\n             cls_score: Tensor,\n             bbox_pred: Tuple[Tensor, Tensor],\n             rois: Tensor,\n             labels: Tensor,\n             label_weights: Tensor,\n             bbox_targets: Tuple[Tensor, Tensor],\n             bbox_weights: Tuple[Tensor, Tensor],\n             reduction_override: Optional[str] = None) -> dict:\n        \"\"\"Calculate the loss based on the network predictions and targets.\n\n        Args:\n            cls_score (Tensor): Classification prediction\n                results of all class, has shape\n                (batch_size * num_proposals_single_image, num_classes)\n            bbox_pred (Tensor): A tuple of regression prediction results\n                containing `bucket_cls_preds and` `bucket_offset_preds`.\n            rois (Tensor): RoIs with the shape\n                (batch_size * num_proposals_single_image, 5) where the first\n                column indicates batch id of each RoI.\n            labels (Tensor): Gt_labels for all proposals in a batch, has\n                shape (batch_size * num_proposals_single_image, ).\n            label_weights (Tensor): Labels_weights for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, ).\n            bbox_targets (Tuple[Tensor, Tensor]): A tuple of regression target\n                containing `bucket_cls_targets` and `bucket_offset_targets`.\n                the last dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            bbox_weights (Tuple[Tensor, Tensor]): A tuple of regression\n                weights containing `bucket_cls_weights` and\n                `bucket_offset_weights`.\n            reduction_override (str, optional): The reduction\n                method used to override the original reduction\n                method of the loss. Options are \"none\",\n                \"mean\" and \"sum\". Defaults to None,\n\n        Returns:\n            dict: A dictionary of loss.\n        \"\"\"\n        losses = dict()\n        if cls_score is not None:\n            avg_factor = max(torch.sum(label_weights > 0).float().item(), 1.)\n            losses['loss_cls'] = self.loss_cls(\n                cls_score,\n                labels,\n                label_weights,\n                avg_factor=avg_factor,\n                reduction_override=reduction_override)\n            losses['acc'] = accuracy(cls_score, labels)\n\n        if bbox_pred is not None:\n            bucket_cls_preds, bucket_offset_preds = bbox_pred\n            bucket_cls_targets, bucket_offset_targets = bbox_targets\n            bucket_cls_weights, bucket_offset_weights = bbox_weights\n            # edge cls\n            bucket_cls_preds = bucket_cls_preds.view(-1, self.side_num)\n            bucket_cls_targets = bucket_cls_targets.view(-1, self.side_num)\n            bucket_cls_weights = bucket_cls_weights.view(-1, self.side_num)\n            losses['loss_bbox_cls'] = self.loss_bbox_cls(\n                bucket_cls_preds,\n                bucket_cls_targets,\n                bucket_cls_weights,\n                avg_factor=bucket_cls_targets.size(0),\n                reduction_override=reduction_override)\n\n            losses['loss_bbox_reg'] = self.loss_bbox_reg(\n                bucket_offset_preds,\n                bucket_offset_targets,\n                bucket_offset_weights,\n                avg_factor=bucket_offset_targets.size(0),\n                reduction_override=reduction_override)\n\n        return losses\n\n    def _predict_by_feat_single(\n            self,\n            roi: Tensor,\n            cls_score: Tensor,\n            bbox_pred: Tuple[Tensor, Tensor],\n            img_meta: dict,\n            rescale: bool = False,\n            rcnn_test_cfg: Optional[ConfigDict] = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            roi (Tensor): Boxes to be transformed. Has shape (num_boxes, 5).\n                last dimension 5 arrange as (batch_index, x1, y1, x2, y2).\n            cls_score (Tensor): Box scores, has shape\n                (num_boxes, num_classes + 1).\n            bbox_pred (Tuple[Tensor, Tensor]): Box cls preds and offset preds.\n            img_meta (dict): image information.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of Bbox Head.\n                Defaults to None\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        results = InstanceData()\n        if isinstance(cls_score, list):\n            cls_score = sum(cls_score) / float(len(cls_score))\n        scores = F.softmax(cls_score, dim=1) if cls_score is not None else None\n        img_shape = img_meta['img_shape']\n        if bbox_pred is not None:\n            bboxes, confidences = self.bbox_coder.decode(\n                roi[:, 1:], bbox_pred, img_shape)\n        else:\n            bboxes = roi[:, 1:].clone()\n            confidences = None\n            if img_shape is not None:\n                bboxes[:, [0, 2]].clamp_(min=0, max=img_shape[1] - 1)\n                bboxes[:, [1, 3]].clamp_(min=0, max=img_shape[0] - 1)\n\n        if rescale and bboxes.size(0) > 0:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = bboxes.new_tensor(img_meta['scale_factor']).repeat(\n                (1, 2))\n            bboxes = (bboxes.view(bboxes.size(0), -1, 4) / scale_factor).view(\n                bboxes.size()[0], -1)\n\n        if rcnn_test_cfg is None:\n            results.bboxes = bboxes\n            results.scores = scores\n        else:\n            det_bboxes, det_labels = multiclass_nms(\n                bboxes,\n                scores,\n                rcnn_test_cfg.score_thr,\n                rcnn_test_cfg.nms,\n                rcnn_test_cfg.max_per_img,\n                score_factors=confidences)\n            results.bboxes = det_bboxes[:, :4]\n            results.scores = det_bboxes[:, -1]\n            results.labels = det_labels\n        return results\n\n    def refine_bboxes(self, sampling_results: List[SamplingResult],\n                      bbox_results: dict,\n                      batch_img_metas: List[dict]) -> InstanceList:\n        \"\"\"Refine bboxes during training.\n\n        Args:\n            sampling_results (List[:obj:`SamplingResult`]): Sampling results.\n            bbox_results (dict): Usually is a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `rois` (Tensor): RoIs with the shape (n, 5) where the first\n                  column indicates batch id of each RoI.\n                - `bbox_targets` (tuple):  Ground truth for proposals in a\n                  single image. Containing the following list of Tensors:\n                  (labels, label_weights, bbox_targets, bbox_weights)\n            batch_img_metas (List[dict]): List of image information.\n\n        Returns:\n            list[:obj:`InstanceData`]: Refined bboxes of each image.\n        \"\"\"\n        pos_is_gts = [res.pos_is_gt for res in sampling_results]\n        # bbox_targets is a tuple\n        labels = bbox_results['bbox_targets'][0]\n        cls_scores = bbox_results['cls_score']\n        rois = bbox_results['rois']\n        bbox_preds = bbox_results['bbox_pred']\n\n        if cls_scores.numel() == 0:\n            return None\n\n        labels = torch.where(labels == self.num_classes,\n                             cls_scores[:, :-1].argmax(1), labels)\n\n        img_ids = rois[:, 0].long().unique(sorted=True)\n        assert img_ids.numel() <= len(batch_img_metas)\n\n        results_list = []\n        for i in range(len(batch_img_metas)):\n            inds = torch.nonzero(\n                rois[:, 0] == i, as_tuple=False).squeeze(dim=1)\n            num_rois = inds.numel()\n\n            bboxes_ = rois[inds, 1:]\n            label_ = labels[inds]\n            edge_cls_preds, edge_offset_preds = bbox_preds\n            edge_cls_preds_ = edge_cls_preds[inds]\n            edge_offset_preds_ = edge_offset_preds[inds]\n            bbox_pred_ = (edge_cls_preds_, edge_offset_preds_)\n            img_meta_ = batch_img_metas[i]\n            pos_is_gts_ = pos_is_gts[i]\n\n            bboxes = self.regress_by_class(bboxes_, label_, bbox_pred_,\n                                           img_meta_)\n            # filter gt bboxes\n            pos_keep = 1 - pos_is_gts_\n            keep_inds = pos_is_gts_.new_ones(num_rois)\n            keep_inds[:len(pos_is_gts_)] = pos_keep\n            results = InstanceData(bboxes=bboxes[keep_inds.type(torch.bool)])\n            results_list.append(results)\n\n        return results_list\n\n    def regress_by_class(self, rois: Tensor, label: Tensor, bbox_pred: tuple,\n                         img_meta: dict) -> Tensor:\n        \"\"\"Regress the bbox for the predicted class. Used in Cascade R-CNN.\n\n        Args:\n            rois (Tensor): shape (n, 4) or (n, 5)\n            label (Tensor): shape (n, )\n            bbox_pred (Tuple[Tensor]): shape [(n, num_buckets *2), \\\n                (n, num_buckets *2)]\n            img_meta (dict): Image meta info.\n\n        Returns:\n            Tensor: Regressed bboxes, the same shape as input rois.\n        \"\"\"\n        assert rois.size(1) == 4 or rois.size(1) == 5\n\n        if rois.size(1) == 4:\n            new_rois, _ = self.bbox_coder.decode(rois, bbox_pred,\n                                                 img_meta['img_shape'])\n        else:\n            bboxes, _ = self.bbox_coder.decode(rois[:, 1:], bbox_pred,\n                                               img_meta['img_shape'])\n            new_rois = torch.cat((rois[:, [0]], bboxes), dim=1)\n\n        return new_rois\n"
  },
  {
    "path": "mmdet/models/roi_heads/bbox_heads/scnet_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple, Union\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .convfc_bbox_head import ConvFCBBoxHead\n\n\n@MODELS.register_module()\nclass SCNetBBoxHead(ConvFCBBoxHead):\n    \"\"\"BBox head for `SCNet <https://arxiv.org/abs/2012.10150>`_.\n\n    This inherits ``ConvFCBBoxHead`` with modified forward() function, allow us\n    to get intermediate shared feature.\n    \"\"\"\n\n    def _forward_shared(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function for shared part.\n\n        Args:\n            x (Tensor): Input feature.\n\n        Returns:\n            Tensor: Shared feature.\n        \"\"\"\n        if self.num_shared_convs > 0:\n            for conv in self.shared_convs:\n                x = conv(x)\n\n        if self.num_shared_fcs > 0:\n            if self.with_avg_pool:\n                x = self.avg_pool(x)\n\n            x = x.flatten(1)\n\n            for fc in self.shared_fcs:\n                x = self.relu(fc(x))\n\n        return x\n\n    def _forward_cls_reg(self, x: Tensor) -> Tuple[Tensor]:\n        \"\"\"Forward function for classification and regression parts.\n\n        Args:\n            x (Tensor): Input feature.\n\n        Returns:\n            tuple[Tensor]:\n\n                - cls_score (Tensor): classification prediction.\n                - bbox_pred (Tensor): bbox prediction.\n        \"\"\"\n        x_cls = x\n        x_reg = x\n\n        for conv in self.cls_convs:\n            x_cls = conv(x_cls)\n        if x_cls.dim() > 2:\n            if self.with_avg_pool:\n                x_cls = self.avg_pool(x_cls)\n            x_cls = x_cls.flatten(1)\n        for fc in self.cls_fcs:\n            x_cls = self.relu(fc(x_cls))\n\n        for conv in self.reg_convs:\n            x_reg = conv(x_reg)\n        if x_reg.dim() > 2:\n            if self.with_avg_pool:\n                x_reg = self.avg_pool(x_reg)\n            x_reg = x_reg.flatten(1)\n        for fc in self.reg_fcs:\n            x_reg = self.relu(fc(x_reg))\n\n        cls_score = self.fc_cls(x_cls) if self.with_cls else None\n        bbox_pred = self.fc_reg(x_reg) if self.with_reg else None\n\n        return cls_score, bbox_pred\n\n    def forward(\n            self,\n            x: Tensor,\n            return_shared_feat: bool = False) -> Union[Tensor, Tuple[Tensor]]:\n        \"\"\"Forward function.\n\n        Args:\n            x (Tensor): input features\n            return_shared_feat (bool): If True, return cls-reg-shared feature.\n\n        Return:\n            out (tuple[Tensor]): contain ``cls_score`` and ``bbox_pred``,\n                if  ``return_shared_feat`` is True, append ``x_shared`` to the\n                returned tuple.\n        \"\"\"\n        x_shared = self._forward_shared(x)\n        out = self._forward_cls_reg(x_shared)\n\n        if return_shared_feat:\n            out += (x_shared, )\n\n        return out\n"
  },
  {
    "path": "mmdet/models/roi_heads/cascade_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Sequence, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.model import ModuleList\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.test_time_augs import merge_aug_masks\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi, get_box_tensor\nfrom mmdet.utils import (ConfigType, InstanceList, MultiConfig, OptConfigType,\n                         OptMultiConfig)\nfrom ..utils.misc import empty_instances, unpack_gt_instances\nfrom .base_roi_head import BaseRoIHead\n\n\n@MODELS.register_module()\nclass CascadeRoIHead(BaseRoIHead):\n    \"\"\"Cascade roi head including one bbox head and one mask head.\n\n    https://arxiv.org/abs/1712.00726\n    \"\"\"\n\n    def __init__(self,\n                 num_stages: int,\n                 stage_loss_weights: Union[List[float], Tuple[float]],\n                 bbox_roi_extractor: OptMultiConfig = None,\n                 bbox_head: OptMultiConfig = None,\n                 mask_roi_extractor: OptMultiConfig = None,\n                 mask_head: OptMultiConfig = None,\n                 shared_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        assert bbox_roi_extractor is not None\n        assert bbox_head is not None\n        assert shared_head is None, \\\n            'Shared head is not supported in Cascade RCNN anymore'\n\n        self.num_stages = num_stages\n        self.stage_loss_weights = stage_loss_weights\n        super().__init__(\n            bbox_roi_extractor=bbox_roi_extractor,\n            bbox_head=bbox_head,\n            mask_roi_extractor=mask_roi_extractor,\n            mask_head=mask_head,\n            shared_head=shared_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            init_cfg=init_cfg)\n\n    def init_bbox_head(self, bbox_roi_extractor: MultiConfig,\n                       bbox_head: MultiConfig) -> None:\n        \"\"\"Initialize box head and box roi extractor.\n\n        Args:\n            bbox_roi_extractor (:obj:`ConfigDict`, dict or list):\n                Config of box roi extractor.\n            bbox_head (:obj:`ConfigDict`, dict or list): Config\n                of box in box head.\n        \"\"\"\n        self.bbox_roi_extractor = ModuleList()\n        self.bbox_head = ModuleList()\n        if not isinstance(bbox_roi_extractor, list):\n            bbox_roi_extractor = [\n                bbox_roi_extractor for _ in range(self.num_stages)\n            ]\n        if not isinstance(bbox_head, list):\n            bbox_head = [bbox_head for _ in range(self.num_stages)]\n        assert len(bbox_roi_extractor) == len(bbox_head) == self.num_stages\n        for roi_extractor, head in zip(bbox_roi_extractor, bbox_head):\n            self.bbox_roi_extractor.append(MODELS.build(roi_extractor))\n            self.bbox_head.append(MODELS.build(head))\n\n    def init_mask_head(self, mask_roi_extractor: MultiConfig,\n                       mask_head: MultiConfig) -> None:\n        \"\"\"Initialize mask head and mask roi extractor.\n\n        Args:\n            mask_head (dict): Config of mask in mask head.\n            mask_roi_extractor (:obj:`ConfigDict`, dict or list):\n                Config of mask roi extractor.\n        \"\"\"\n        self.mask_head = nn.ModuleList()\n        if not isinstance(mask_head, list):\n            mask_head = [mask_head for _ in range(self.num_stages)]\n        assert len(mask_head) == self.num_stages\n        for head in mask_head:\n            self.mask_head.append(MODELS.build(head))\n        if mask_roi_extractor is not None:\n            self.share_roi_extractor = False\n            self.mask_roi_extractor = ModuleList()\n            if not isinstance(mask_roi_extractor, list):\n                mask_roi_extractor = [\n                    mask_roi_extractor for _ in range(self.num_stages)\n                ]\n            assert len(mask_roi_extractor) == self.num_stages\n            for roi_extractor in mask_roi_extractor:\n                self.mask_roi_extractor.append(MODELS.build(roi_extractor))\n        else:\n            self.share_roi_extractor = True\n            self.mask_roi_extractor = self.bbox_roi_extractor\n\n    def init_assigner_sampler(self) -> None:\n        \"\"\"Initialize assigner and sampler for each stage.\"\"\"\n        self.bbox_assigner = []\n        self.bbox_sampler = []\n        if self.train_cfg is not None:\n            for idx, rcnn_train_cfg in enumerate(self.train_cfg):\n                self.bbox_assigner.append(\n                    TASK_UTILS.build(rcnn_train_cfg.assigner))\n                self.current_stage = idx\n                self.bbox_sampler.append(\n                    TASK_UTILS.build(\n                        rcnn_train_cfg.sampler,\n                        default_args=dict(context=self)))\n\n    def _bbox_forward(self, stage: int, x: Tuple[Tensor],\n                      rois: Tensor) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        bbox_roi_extractor = self.bbox_roi_extractor[stage]\n        bbox_head = self.bbox_head[stage]\n        bbox_feats = bbox_roi_extractor(x[:bbox_roi_extractor.num_inputs],\n                                        rois)\n        # do not support caffe_c4 model anymore\n        cls_score, bbox_pred = bbox_head(bbox_feats)\n\n        bbox_results = dict(\n            cls_score=cls_score, bbox_pred=bbox_pred, bbox_feats=bbox_feats)\n        return bbox_results\n\n    def bbox_loss(self, stage: int, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult]) -> dict:\n        \"\"\"Run forward function and calculate loss for box head in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n                - `rois` (Tensor): RoIs with the shape (n, 5) where the first\n                  column indicates batch id of each RoI.\n                - `bbox_targets` (tuple):  Ground truth for proposals in a\n                  single image. Containing the following list of Tensors:\n                  (labels, label_weights, bbox_targets, bbox_weights)\n        \"\"\"\n        bbox_head = self.bbox_head[stage]\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(stage, x, rois)\n        bbox_results.update(rois=rois)\n\n        bbox_loss_and_target = bbox_head.loss_and_target(\n            cls_score=bbox_results['cls_score'],\n            bbox_pred=bbox_results['bbox_pred'],\n            rois=rois,\n            sampling_results=sampling_results,\n            rcnn_train_cfg=self.train_cfg[stage])\n        bbox_results.update(bbox_loss_and_target)\n\n        return bbox_results\n\n    def _mask_forward(self, stage: int, x: Tuple[Tensor],\n                      rois: Tensor) -> dict:\n        \"\"\"Mask head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n        \"\"\"\n        mask_roi_extractor = self.mask_roi_extractor[stage]\n        mask_head = self.mask_head[stage]\n        mask_feats = mask_roi_extractor(x[:mask_roi_extractor.num_inputs],\n                                        rois)\n        # do not support caffe_c4 model anymore\n        mask_preds = mask_head(mask_feats)\n\n        mask_results = dict(mask_preds=mask_preds)\n        return mask_results\n\n    def mask_loss(self, stage: int, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult],\n                  batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Run forward function and calculate loss for mask head in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `loss_mask` (dict): A dictionary of mask loss components.\n        \"\"\"\n        pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n        mask_results = self._mask_forward(stage, x, pos_rois)\n\n        mask_head = self.mask_head[stage]\n\n        mask_loss_and_target = mask_head.loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=self.train_cfg[stage])\n        mask_results.update(mask_loss_and_target)\n\n        return mask_results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        # TODO: May add a new function in baseroihead\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n\n        num_imgs = len(batch_data_samples)\n        losses = dict()\n        results_list = rpn_results_list\n        for stage in range(self.num_stages):\n            self.current_stage = stage\n\n            stage_loss_weight = self.stage_loss_weights[stage]\n\n            # assign gts and sample proposals\n            sampling_results = []\n            if self.with_bbox or self.with_mask:\n                bbox_assigner = self.bbox_assigner[stage]\n                bbox_sampler = self.bbox_sampler[stage]\n\n                for i in range(num_imgs):\n                    results = results_list[i]\n                    # rename rpn_results.bboxes to rpn_results.priors\n                    results.priors = results.pop('bboxes')\n\n                    assign_result = bbox_assigner.assign(\n                        results, batch_gt_instances[i],\n                        batch_gt_instances_ignore[i])\n\n                    sampling_result = bbox_sampler.sample(\n                        assign_result,\n                        results,\n                        batch_gt_instances[i],\n                        feats=[lvl_feat[i][None] for lvl_feat in x])\n                    sampling_results.append(sampling_result)\n\n            # bbox head forward and loss\n            bbox_results = self.bbox_loss(stage, x, sampling_results)\n\n            for name, value in bbox_results['loss_bbox'].items():\n                losses[f's{stage}.{name}'] = (\n                    value * stage_loss_weight if 'loss' in name else value)\n\n            # mask head forward and loss\n            if self.with_mask:\n                mask_results = self.mask_loss(stage, x, sampling_results,\n                                              batch_gt_instances)\n                for name, value in mask_results['loss_mask'].items():\n                    losses[f's{stage}.{name}'] = (\n                        value * stage_loss_weight if 'loss' in name else value)\n\n            # refine bboxes\n            if stage < self.num_stages - 1:\n                bbox_head = self.bbox_head[stage]\n                with torch.no_grad():\n                    results_list = bbox_head.refine_bboxes(\n                        sampling_results, bbox_results, batch_img_metas)\n                    # Empty proposal\n                    if results_list is None:\n                        break\n        return losses\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False,\n                     **kwargs) -> InstanceList:\n        \"\"\"Perform forward propagation of the bbox head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of R-CNN.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        proposals = [res.bboxes for res in rpn_results_list]\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = bbox2roi(proposals)\n\n        if rois.shape[0] == 0:\n            return empty_instances(\n                batch_img_metas,\n                rois.device,\n                task_type='bbox',\n                box_type=self.bbox_head[-1].predict_box_type,\n                num_classes=self.bbox_head[-1].num_classes,\n                score_per_cls=rcnn_test_cfg is None)\n\n        rois, cls_scores, bbox_preds = self._refine_roi(\n            x=x,\n            rois=rois,\n            batch_img_metas=batch_img_metas,\n            num_proposals_per_img=num_proposals_per_img,\n            **kwargs)\n\n        results_list = self.bbox_head[-1].predict_by_feat(\n            rois=rois,\n            cls_scores=cls_scores,\n            bbox_preds=bbox_preds,\n            batch_img_metas=batch_img_metas,\n            rescale=rescale,\n            rcnn_test_cfg=rcnn_test_cfg)\n        return results_list\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: List[InstanceData],\n                     rescale: bool = False) -> List[InstanceData]:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas,\n                mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        num_mask_rois_per_img = [len(res) for res in results_list]\n        aug_masks = []\n        for stage in range(self.num_stages):\n            mask_results = self._mask_forward(stage, x, mask_rois)\n            mask_preds = mask_results['mask_preds']\n            # split batch mask prediction back to each image\n            mask_preds = mask_preds.split(num_mask_rois_per_img, 0)\n            aug_masks.append([m.sigmoid().detach() for m in mask_preds])\n\n        merged_masks = []\n        for i in range(len(batch_img_metas)):\n            aug_mask = [mask[i] for mask in aug_masks]\n            merged_mask = merge_aug_masks(aug_mask, batch_img_metas[i])\n            merged_masks.append(merged_mask)\n        results_list = self.mask_head[-1].predict_by_feat(\n            mask_preds=merged_masks,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale,\n            activate_map=True)\n        return results_list\n\n    def _refine_roi(self, x: Tuple[Tensor], rois: Tensor,\n                    batch_img_metas: List[dict],\n                    num_proposals_per_img: Sequence[int], **kwargs) -> tuple:\n        \"\"\"Multi-stage refinement of RoI.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): shape (n, 5), [batch_ind, x1, y1, x2, y2]\n            batch_img_metas (list[dict]): List of image information.\n            num_proposals_per_img (sequence[int]): number of proposals\n                in each image.\n\n        Returns:\n            tuple:\n\n               - rois (Tensor): Refined RoI.\n               - cls_scores (list[Tensor]): Average predicted\n                   cls score per image.\n               - bbox_preds (list[Tensor]): Bbox branch predictions\n                   for the last stage of per image.\n        \"\"\"\n        # \"ms\" in variable names means multi-stage\n        ms_scores = []\n        for stage in range(self.num_stages):\n            bbox_results = self._bbox_forward(\n                stage=stage, x=x, rois=rois, **kwargs)\n\n            # split batch bbox prediction back to each image\n            cls_scores = bbox_results['cls_score']\n            bbox_preds = bbox_results['bbox_pred']\n\n            rois = rois.split(num_proposals_per_img, 0)\n            cls_scores = cls_scores.split(num_proposals_per_img, 0)\n            ms_scores.append(cls_scores)\n\n            # some detector with_reg is False, bbox_preds will be None\n            if bbox_preds is not None:\n                # TODO move this to a sabl_roi_head\n                # the bbox prediction of some detectors like SABL is not Tensor\n                if isinstance(bbox_preds, torch.Tensor):\n                    bbox_preds = bbox_preds.split(num_proposals_per_img, 0)\n                else:\n                    bbox_preds = self.bbox_head[stage].bbox_pred_split(\n                        bbox_preds, num_proposals_per_img)\n            else:\n                bbox_preds = (None, ) * len(batch_img_metas)\n\n            if stage < self.num_stages - 1:\n                bbox_head = self.bbox_head[stage]\n                if bbox_head.custom_activation:\n                    cls_scores = [\n                        bbox_head.loss_cls.get_activation(s)\n                        for s in cls_scores\n                    ]\n                refine_rois_list = []\n                for i in range(len(batch_img_metas)):\n                    if rois[i].shape[0] > 0:\n                        bbox_label = cls_scores[i][:, :-1].argmax(dim=1)\n                        # Refactor `bbox_head.regress_by_class` to only accept\n                        # box tensor without img_idx concatenated.\n                        refined_bboxes = bbox_head.regress_by_class(\n                            rois[i][:, 1:], bbox_label, bbox_preds[i],\n                            batch_img_metas[i])\n                        refined_bboxes = get_box_tensor(refined_bboxes)\n                        refined_rois = torch.cat(\n                            [rois[i][:, [0]], refined_bboxes], dim=1)\n                        refine_rois_list.append(refined_rois)\n                rois = torch.cat(refine_rois_list)\n\n        # average scores of each image by stages\n        cls_scores = [\n            sum([score[i] for score in ms_scores]) / float(len(ms_scores))\n            for i in range(len(batch_img_metas))\n        ]\n        return rois, cls_scores, bbox_preds\n\n    def forward(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n                batch_data_samples: SampleList) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            x (List[Tensor]): Multi-level features that may have different\n                resolutions.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n\n        Returns\n            tuple: A tuple of features from ``bbox_head`` and ``mask_head``\n            forward.\n        \"\"\"\n        results = ()\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n        proposals = [rpn_results.bboxes for rpn_results in rpn_results_list]\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = bbox2roi(proposals)\n        # bbox head\n        if self.with_bbox:\n            rois, cls_scores, bbox_preds = self._refine_roi(\n                x, rois, batch_img_metas, num_proposals_per_img)\n            results = results + (cls_scores, bbox_preds)\n        # mask head\n        if self.with_mask:\n            aug_masks = []\n            rois = torch.cat(rois)\n            for stage in range(self.num_stages):\n                mask_results = self._mask_forward(stage, x, rois)\n                mask_preds = mask_results['mask_preds']\n                mask_preds = mask_preds.split(num_proposals_per_img, 0)\n                aug_masks.append([m.sigmoid().detach() for m in mask_preds])\n\n            merged_masks = []\n            for i in range(len(batch_img_metas)):\n                aug_mask = [mask[i] for mask in aug_masks]\n                merged_mask = merge_aug_masks(aug_mask, batch_img_metas[i])\n                merged_masks.append(merged_mask)\n            results = results + (merged_masks, )\n        return results\n"
  },
  {
    "path": "mmdet/models/roi_heads/double_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass DoubleHeadRoIHead(StandardRoIHead):\n    \"\"\"RoI head for `Double Head RCNN <https://arxiv.org/abs/1904.06493>`_.\n\n    Args:\n        reg_roi_scale_factor (float): The scale factor to extend the rois\n            used to extract the regression features.\n    \"\"\"\n\n    def __init__(self, reg_roi_scale_factor: float, **kwargs):\n        super().__init__(**kwargs)\n        self.reg_roi_scale_factor = reg_roi_scale_factor\n\n    def _bbox_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        bbox_cls_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs], rois)\n        bbox_reg_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs],\n            rois,\n            roi_scale_factor=self.reg_roi_scale_factor)\n        if self.with_shared_head:\n            bbox_cls_feats = self.shared_head(bbox_cls_feats)\n            bbox_reg_feats = self.shared_head(bbox_reg_feats)\n        cls_score, bbox_pred = self.bbox_head(bbox_cls_feats, bbox_reg_feats)\n\n        bbox_results = dict(\n            cls_score=cls_score,\n            bbox_pred=bbox_pred,\n            bbox_feats=bbox_cls_feats)\n        return bbox_results\n"
  },
  {
    "path": "mmdet/models/roi_heads/dynamic_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.models.losses import SmoothL1Loss\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import InstanceList\nfrom ..utils.misc import unpack_gt_instances\nfrom .standard_roi_head import StandardRoIHead\n\nEPS = 1e-15\n\n\n@MODELS.register_module()\nclass DynamicRoIHead(StandardRoIHead):\n    \"\"\"RoI head for `Dynamic R-CNN <https://arxiv.org/abs/2004.06002>`_.\"\"\"\n\n    def __init__(self, **kwargs) -> None:\n        super().__init__(**kwargs)\n        assert isinstance(self.bbox_head.loss_bbox, SmoothL1Loss)\n        # the IoU history of the past `update_iter_interval` iterations\n        self.iou_history = []\n        # the beta history of the past `update_iter_interval` iterations\n        self.beta_history = []\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Forward function for training.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, _ = outputs\n\n        # assign gts and sample proposals\n        num_imgs = len(batch_data_samples)\n        sampling_results = []\n        cur_iou = []\n        for i in range(num_imgs):\n            # rename rpn_results.bboxes to rpn_results.priors\n            rpn_results = rpn_results_list[i]\n            rpn_results.priors = rpn_results.pop('bboxes')\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in x])\n            # record the `iou_topk`-th largest IoU in an image\n            iou_topk = min(self.train_cfg.dynamic_rcnn.iou_topk,\n                           len(assign_result.max_overlaps))\n            ious, _ = torch.topk(assign_result.max_overlaps, iou_topk)\n            cur_iou.append(ious[-1].item())\n            sampling_results.append(sampling_result)\n        # average the current IoUs over images\n        cur_iou = np.mean(cur_iou)\n        self.iou_history.append(cur_iou)\n\n        losses = dict()\n        # bbox head forward and loss\n        if self.with_bbox:\n            bbox_results = self.bbox_loss(x, sampling_results)\n            losses.update(bbox_results['loss_bbox'])\n\n        # mask head forward and loss\n        if self.with_mask:\n            mask_results = self.mask_loss(x, sampling_results,\n                                          bbox_results['bbox_feats'],\n                                          batch_gt_instances)\n            losses.update(mask_results['loss_mask'])\n\n        # update IoU threshold and SmoothL1 beta\n        update_iter_interval = self.train_cfg.dynamic_rcnn.update_iter_interval\n        if len(self.iou_history) % update_iter_interval == 0:\n            new_iou_thr, new_beta = self.update_hyperparameters()\n\n        return losses\n\n    def bbox_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the bbox head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n        \"\"\"\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(x, rois)\n\n        bbox_loss_and_target = self.bbox_head.loss_and_target(\n            cls_score=bbox_results['cls_score'],\n            bbox_pred=bbox_results['bbox_pred'],\n            rois=rois,\n            sampling_results=sampling_results,\n            rcnn_train_cfg=self.train_cfg)\n        bbox_results.update(loss_bbox=bbox_loss_and_target['loss_bbox'])\n\n        # record the `beta_topk`-th smallest target\n        # `bbox_targets[2]` and `bbox_targets[3]` stand for bbox_targets\n        # and bbox_weights, respectively\n        bbox_targets = bbox_loss_and_target['bbox_targets']\n        pos_inds = bbox_targets[3][:, 0].nonzero().squeeze(1)\n        num_pos = len(pos_inds)\n        num_imgs = len(sampling_results)\n        if num_pos > 0:\n            cur_target = bbox_targets[2][pos_inds, :2].abs().mean(dim=1)\n            beta_topk = min(self.train_cfg.dynamic_rcnn.beta_topk * num_imgs,\n                            num_pos)\n            cur_target = torch.kthvalue(cur_target, beta_topk)[0].item()\n            self.beta_history.append(cur_target)\n\n        return bbox_results\n\n    def update_hyperparameters(self):\n        \"\"\"Update hyperparameters like IoU thresholds for assigner and beta for\n        SmoothL1 loss based on the training statistics.\n\n        Returns:\n            tuple[float]: the updated ``iou_thr`` and ``beta``.\n        \"\"\"\n        new_iou_thr = max(self.train_cfg.dynamic_rcnn.initial_iou,\n                          np.mean(self.iou_history))\n        self.iou_history = []\n        self.bbox_assigner.pos_iou_thr = new_iou_thr\n        self.bbox_assigner.neg_iou_thr = new_iou_thr\n        self.bbox_assigner.min_pos_iou = new_iou_thr\n        if (not self.beta_history) or (np.median(self.beta_history) < EPS):\n            # avoid 0 or too small value for new_beta\n            new_beta = self.bbox_head.loss_bbox.beta\n        else:\n            new_beta = min(self.train_cfg.dynamic_rcnn.initial_beta,\n                           np.median(self.beta_history))\n        self.beta_history = []\n        self.bbox_head.loss_bbox.beta = new_beta\n        return new_iou_thr, new_beta\n"
  },
  {
    "path": "mmdet/models/roi_heads/grid_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils.misc import unpack_gt_instances\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass GridRoIHead(StandardRoIHead):\n    \"\"\"Implementation of `Grid RoI Head <https://arxiv.org/abs/1811.12030>`_\n\n    Args:\n        grid_roi_extractor (:obj:`ConfigDict` or dict): Config of\n            roi extractor.\n        grid_head (:obj:`ConfigDict` or dict): Config of grid head\n    \"\"\"\n\n    def __init__(self, grid_roi_extractor: ConfigType, grid_head: ConfigType,\n                 **kwargs) -> None:\n        assert grid_head is not None\n        super().__init__(**kwargs)\n        if grid_roi_extractor is not None:\n            self.grid_roi_extractor = MODELS.build(grid_roi_extractor)\n            self.share_roi_extractor = False\n        else:\n            self.share_roi_extractor = True\n            self.grid_roi_extractor = self.bbox_roi_extractor\n        self.grid_head = MODELS.build(grid_head)\n\n    def _random_jitter(self,\n                       sampling_results: List[SamplingResult],\n                       batch_img_metas: List[dict],\n                       amplitude: float = 0.15) -> List[SamplingResult]:\n        \"\"\"Ramdom jitter positive proposals for training.\n\n        Args:\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            batch_img_metas (list[dict]): List of image information.\n            amplitude (float): Amplitude of random offset. Defaults to 0.15.\n\n        Returns:\n            list[obj:SamplingResult]: SamplingResults after random jittering.\n        \"\"\"\n        for sampling_result, img_meta in zip(sampling_results,\n                                             batch_img_metas):\n            bboxes = sampling_result.pos_priors\n            random_offsets = bboxes.new_empty(bboxes.shape[0], 4).uniform_(\n                -amplitude, amplitude)\n            # before jittering\n            cxcy = (bboxes[:, 2:4] + bboxes[:, :2]) / 2\n            wh = (bboxes[:, 2:4] - bboxes[:, :2]).abs()\n            # after jittering\n            new_cxcy = cxcy + wh * random_offsets[:, :2]\n            new_wh = wh * (1 + random_offsets[:, 2:])\n            # xywh to xyxy\n            new_x1y1 = (new_cxcy - new_wh / 2)\n            new_x2y2 = (new_cxcy + new_wh / 2)\n            new_bboxes = torch.cat([new_x1y1, new_x2y2], dim=1)\n            # clip bboxes\n            max_shape = img_meta['img_shape']\n            if max_shape is not None:\n                new_bboxes[:, 0::2].clamp_(min=0, max=max_shape[1] - 1)\n                new_bboxes[:, 1::2].clamp_(min=0, max=max_shape[0] - 1)\n\n            sampling_result.pos_priors = new_bboxes\n        return sampling_results\n\n    # TODO: Forward is incorrect and need to refactor.\n    def forward(self,\n                x: Tuple[Tensor],\n                rpn_results_list: InstanceList,\n                batch_data_samples: SampleList = None) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            x (Tuple[Tensor]): Multi-level features that may have different\n                resolutions.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n            the meta information of each image and corresponding\n            annotations.\n\n        Returns\n            tuple: A tuple of features from ``bbox_head`` and ``mask_head``\n            forward.\n        \"\"\"\n        results = ()\n        proposals = [rpn_results.bboxes for rpn_results in rpn_results_list]\n        rois = bbox2roi(proposals)\n        # bbox head\n        if self.with_bbox:\n            bbox_results = self._bbox_forward(x, rois)\n            results = results + (bbox_results['cls_score'], )\n            if self.bbox_head.with_reg:\n                results = results + (bbox_results['bbox_pred'], )\n\n            # grid head\n            grid_rois = rois[:100]\n            grid_feats = self.grid_roi_extractor(\n                x[:len(self.grid_roi_extractor.featmap_strides)], grid_rois)\n            if self.with_shared_head:\n                grid_feats = self.shared_head(grid_feats)\n            self.grid_head.test_mode = True\n            grid_preds = self.grid_head(grid_feats)\n            results = results + (grid_preds, )\n\n        # mask head\n        if self.with_mask:\n            mask_rois = rois[:100]\n            mask_results = self._mask_forward(x, mask_rois)\n            results = results + (mask_results['mask_preds'], )\n        return results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList, **kwargs) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        (batch_gt_instances, batch_gt_instances_ignore,\n         batch_img_metas) = outputs\n\n        # assign gts and sample proposals\n        num_imgs = len(batch_data_samples)\n        sampling_results = []\n        for i in range(num_imgs):\n            # rename rpn_results.bboxes to rpn_results.priors\n            rpn_results = rpn_results_list[i]\n            rpn_results.priors = rpn_results.pop('bboxes')\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in x])\n            sampling_results.append(sampling_result)\n\n        losses = dict()\n        # bbox head loss\n        if self.with_bbox:\n            bbox_results = self.bbox_loss(x, sampling_results, batch_img_metas)\n            losses.update(bbox_results['loss_bbox'])\n\n        # mask head forward and loss\n        if self.with_mask:\n            mask_results = self.mask_loss(x, sampling_results,\n                                          bbox_results['bbox_feats'],\n                                          batch_gt_instances)\n            losses.update(mask_results['loss_mask'])\n\n        return losses\n\n    def bbox_loss(self,\n                  x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult],\n                  batch_img_metas: Optional[List[dict]] = None) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the bbox head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[:obj:`SamplingResult`]): Sampling results.\n            batch_img_metas (list[dict], optional): Meta information of each\n                image, e.g., image size, scaling factor, etc.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n            - `cls_score` (Tensor): Classification scores.\n            - `bbox_pred` (Tensor): Box energies / deltas.\n            - `bbox_feats` (Tensor): Extract bbox RoI features.\n            - `loss_bbox` (dict): A dictionary of bbox loss components.\n        \"\"\"\n        assert batch_img_metas is not None\n        bbox_results = super().bbox_loss(x, sampling_results)\n\n        # Grid head forward and loss\n        sampling_results = self._random_jitter(sampling_results,\n                                               batch_img_metas)\n        pos_rois = bbox2roi([res.pos_bboxes for res in sampling_results])\n\n        # GN in head does not support zero shape input\n        if pos_rois.shape[0] == 0:\n            return bbox_results\n\n        grid_feats = self.grid_roi_extractor(\n            x[:self.grid_roi_extractor.num_inputs], pos_rois)\n        if self.with_shared_head:\n            grid_feats = self.shared_head(grid_feats)\n        # Accelerate training\n        max_sample_num_grid = self.train_cfg.get('max_num_grid', 192)\n        sample_idx = torch.randperm(\n            grid_feats.shape[0])[:min(grid_feats.shape[0], max_sample_num_grid\n                                      )]\n        grid_feats = grid_feats[sample_idx]\n        grid_pred = self.grid_head(grid_feats)\n\n        loss_grid = self.grid_head.loss(grid_pred, sample_idx,\n                                        sampling_results, self.train_cfg)\n\n        bbox_results['loss_bbox'].update(loss_grid)\n        return bbox_results\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the bbox head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            rcnn_test_cfg (:obj:`ConfigDict`): `test_cfg` of R-CNN.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape \\\n            (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4), the last \\\n            dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        results_list = super().predict_bbox(\n            x,\n            batch_img_metas=batch_img_metas,\n            rpn_results_list=rpn_results_list,\n            rcnn_test_cfg=rcnn_test_cfg,\n            rescale=False)\n\n        grid_rois = bbox2roi([res.bboxes for res in results_list])\n        if grid_rois.shape[0] != 0:\n            grid_feats = self.grid_roi_extractor(\n                x[:len(self.grid_roi_extractor.featmap_strides)], grid_rois)\n            if self.with_shared_head:\n                grid_feats = self.shared_head(grid_feats)\n            self.grid_head.test_mode = True\n            grid_preds = self.grid_head(grid_feats)\n            results_list = self.grid_head.predict_by_feat(\n                grid_preds=grid_preds,\n                results_list=results_list,\n                batch_img_metas=batch_img_metas,\n                rescale=rescale)\n\n        return results_list\n"
  },
  {
    "path": "mmdet/models/roi_heads/htc_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom mmdet.models.test_time_augs import merge_aug_masks\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import InstanceList, OptConfigType\nfrom ..layers import adaptive_avg_pool2d\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils import empty_instances, unpack_gt_instances\nfrom .cascade_roi_head import CascadeRoIHead\n\n\n@MODELS.register_module()\nclass HybridTaskCascadeRoIHead(CascadeRoIHead):\n    \"\"\"Hybrid task cascade roi head including one bbox head and one mask head.\n\n    https://arxiv.org/abs/1901.07518\n\n    Args:\n        num_stages (int): Number of cascade stages.\n        stage_loss_weights (list[float]): Loss weight for every stage.\n        semantic_roi_extractor (:obj:`ConfigDict` or dict, optional):\n            Config of semantic roi extractor. Defaults to None.\n        Semantic_head (:obj:`ConfigDict` or dict, optional):\n            Config of semantic head. Defaults to None.\n        interleaved (bool): Whether to interleaves the box branch and mask\n            branch. If True, the mask branch can take the refined bounding\n            box predictions. Defaults to True.\n        mask_info_flow (bool): Whether to turn on the mask information flow,\n            which means that feeding the mask features of the preceding stage\n            to the current stage. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 num_stages: int,\n                 stage_loss_weights: List[float],\n                 semantic_roi_extractor: OptConfigType = None,\n                 semantic_head: OptConfigType = None,\n                 semantic_fusion: Tuple[str] = ('bbox', 'mask'),\n                 interleaved: bool = True,\n                 mask_info_flow: bool = True,\n                 **kwargs) -> None:\n        super().__init__(\n            num_stages=num_stages,\n            stage_loss_weights=stage_loss_weights,\n            **kwargs)\n        assert self.with_bbox\n        assert not self.with_shared_head  # shared head is not supported\n\n        if semantic_head is not None:\n            self.semantic_roi_extractor = MODELS.build(semantic_roi_extractor)\n            self.semantic_head = MODELS.build(semantic_head)\n\n        self.semantic_fusion = semantic_fusion\n        self.interleaved = interleaved\n        self.mask_info_flow = mask_info_flow\n\n    # TODO move to base_roi_head later\n    @property\n    def with_semantic(self) -> bool:\n        \"\"\"bool: whether the head has semantic head\"\"\"\n        return hasattr(self,\n                       'semantic_head') and self.semantic_head is not None\n\n    def _bbox_forward(\n            self,\n            stage: int,\n            x: Tuple[Tensor],\n            rois: Tensor,\n            semantic_feat: Optional[Tensor] = None) -> Dict[str, Tensor]:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            semantic_feat (Tensor, optional): Semantic feature. Defaults to\n                None.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        bbox_roi_extractor = self.bbox_roi_extractor[stage]\n        bbox_head = self.bbox_head[stage]\n        bbox_feats = bbox_roi_extractor(x[:bbox_roi_extractor.num_inputs],\n                                        rois)\n        if self.with_semantic and 'bbox' in self.semantic_fusion:\n            bbox_semantic_feat = self.semantic_roi_extractor([semantic_feat],\n                                                             rois)\n            if bbox_semantic_feat.shape[-2:] != bbox_feats.shape[-2:]:\n                bbox_semantic_feat = adaptive_avg_pool2d(\n                    bbox_semantic_feat, bbox_feats.shape[-2:])\n            bbox_feats += bbox_semantic_feat\n        cls_score, bbox_pred = bbox_head(bbox_feats)\n\n        bbox_results = dict(cls_score=cls_score, bbox_pred=bbox_pred)\n        return bbox_results\n\n    def bbox_loss(self,\n                  stage: int,\n                  x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult],\n                  semantic_feat: Optional[Tensor] = None) -> dict:\n        \"\"\"Run forward function and calculate loss for box head in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            semantic_feat (Tensor, optional): Semantic feature. Defaults to\n                None.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n                - `rois` (Tensor): RoIs with the shape (n, 5) where the first\n                  column indicates batch id of each RoI.\n                - `bbox_targets` (tuple):  Ground truth for proposals in a\n                  single image. Containing the following list of Tensors:\n                  (labels, label_weights, bbox_targets, bbox_weights)\n        \"\"\"\n        bbox_head = self.bbox_head[stage]\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(\n            stage, x, rois, semantic_feat=semantic_feat)\n        bbox_results.update(rois=rois)\n\n        bbox_loss_and_target = bbox_head.loss_and_target(\n            cls_score=bbox_results['cls_score'],\n            bbox_pred=bbox_results['bbox_pred'],\n            rois=rois,\n            sampling_results=sampling_results,\n            rcnn_train_cfg=self.train_cfg[stage])\n        bbox_results.update(bbox_loss_and_target)\n        return bbox_results\n\n    def _mask_forward(self,\n                      stage: int,\n                      x: Tuple[Tensor],\n                      rois: Tensor,\n                      semantic_feat: Optional[Tensor] = None,\n                      training: bool = True) -> Dict[str, Tensor]:\n        \"\"\"Mask head forward function used only in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            semantic_feat (Tensor, optional): Semantic feature. Defaults to\n                None.\n            training (bool): Mask Forward is different between training and\n                testing. If True, use the mask forward in training.\n                Defaults to True.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n        \"\"\"\n        mask_roi_extractor = self.mask_roi_extractor[stage]\n        mask_head = self.mask_head[stage]\n        mask_feats = mask_roi_extractor(x[:mask_roi_extractor.num_inputs],\n                                        rois)\n\n        # semantic feature fusion\n        # element-wise sum for original features and pooled semantic features\n        if self.with_semantic and 'mask' in self.semantic_fusion:\n            mask_semantic_feat = self.semantic_roi_extractor([semantic_feat],\n                                                             rois)\n            if mask_semantic_feat.shape[-2:] != mask_feats.shape[-2:]:\n                mask_semantic_feat = F.adaptive_avg_pool2d(\n                    mask_semantic_feat, mask_feats.shape[-2:])\n            mask_feats = mask_feats + mask_semantic_feat\n\n        # mask information flow\n        # forward all previous mask heads to obtain last_feat, and fuse it\n        # with the normal mask feature\n        if training:\n            if self.mask_info_flow:\n                last_feat = None\n                for i in range(stage):\n                    last_feat = self.mask_head[i](\n                        mask_feats, last_feat, return_logits=False)\n                mask_preds = mask_head(\n                    mask_feats, last_feat, return_feat=False)\n            else:\n                mask_preds = mask_head(mask_feats, return_feat=False)\n\n            mask_results = dict(mask_preds=mask_preds)\n        else:\n            aug_masks = []\n            last_feat = None\n            for i in range(self.num_stages):\n                mask_head = self.mask_head[i]\n                if self.mask_info_flow:\n                    mask_preds, last_feat = mask_head(mask_feats, last_feat)\n                else:\n                    mask_preds = mask_head(mask_feats)\n            aug_masks.append(mask_preds)\n\n            mask_results = dict(mask_preds=aug_masks)\n\n        return mask_results\n\n    def mask_loss(self,\n                  stage: int,\n                  x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult],\n                  batch_gt_instances: InstanceList,\n                  semantic_feat: Optional[Tensor] = None) -> dict:\n        \"\"\"Run forward function and calculate loss for mask head in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            semantic_feat (Tensor, optional): Semantic feature. Defaults to\n                None.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `loss_mask` (dict): A dictionary of mask loss components.\n        \"\"\"\n        pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n        mask_results = self._mask_forward(\n            stage=stage,\n            x=x,\n            rois=pos_rois,\n            semantic_feat=semantic_feat,\n            training=True)\n\n        mask_head = self.mask_head[stage]\n        mask_loss_and_target = mask_head.loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=self.train_cfg[stage])\n        mask_results.update(mask_loss_and_target)\n\n        return mask_results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n\n        # semantic segmentation part\n        # 2 outputs: segmentation prediction and embedded features\n        losses = dict()\n        if self.with_semantic:\n            gt_semantic_segs = [\n                data_sample.gt_sem_seg.sem_seg\n                for data_sample in batch_data_samples\n            ]\n            gt_semantic_segs = torch.stack(gt_semantic_segs)\n            semantic_pred, semantic_feat = self.semantic_head(x)\n            loss_seg = self.semantic_head.loss(semantic_pred, gt_semantic_segs)\n            losses['loss_semantic_seg'] = loss_seg\n        else:\n            semantic_feat = None\n\n        results_list = rpn_results_list\n        num_imgs = len(batch_img_metas)\n        for stage in range(self.num_stages):\n            self.current_stage = stage\n\n            stage_loss_weight = self.stage_loss_weights[stage]\n\n            # assign gts and sample proposals\n            sampling_results = []\n            bbox_assigner = self.bbox_assigner[stage]\n            bbox_sampler = self.bbox_sampler[stage]\n            for i in range(num_imgs):\n                results = results_list[i]\n                # rename rpn_results.bboxes to rpn_results.priors\n                if 'bboxes' in results:\n                    results.priors = results.pop('bboxes')\n\n                assign_result = bbox_assigner.assign(\n                    results, batch_gt_instances[i],\n                    batch_gt_instances_ignore[i])\n                sampling_result = bbox_sampler.sample(\n                    assign_result,\n                    results,\n                    batch_gt_instances[i],\n                    feats=[lvl_feat[i][None] for lvl_feat in x])\n                sampling_results.append(sampling_result)\n\n            # bbox head forward and loss\n            bbox_results = self.bbox_loss(\n                stage=stage,\n                x=x,\n                sampling_results=sampling_results,\n                semantic_feat=semantic_feat)\n\n            for name, value in bbox_results['loss_bbox'].items():\n                losses[f's{stage}.{name}'] = (\n                    value * stage_loss_weight if 'loss' in name else value)\n\n            # mask head forward and loss\n            if self.with_mask:\n                # interleaved execution: use regressed bboxes by the box branch\n                # to train the mask branch\n                if self.interleaved:\n                    bbox_head = self.bbox_head[stage]\n                    with torch.no_grad():\n                        results_list = bbox_head.refine_bboxes(\n                            sampling_results, bbox_results, batch_img_metas)\n                        # re-assign and sample 512 RoIs from 512 RoIs\n                        sampling_results = []\n                        for i in range(num_imgs):\n                            results = results_list[i]\n                            # rename rpn_results.bboxes to rpn_results.priors\n                            results.priors = results.pop('bboxes')\n                            assign_result = bbox_assigner.assign(\n                                results, batch_gt_instances[i],\n                                batch_gt_instances_ignore[i])\n                            sampling_result = bbox_sampler.sample(\n                                assign_result,\n                                results,\n                                batch_gt_instances[i],\n                                feats=[lvl_feat[i][None] for lvl_feat in x])\n                            sampling_results.append(sampling_result)\n                mask_results = self.mask_loss(\n                    stage=stage,\n                    x=x,\n                    sampling_results=sampling_results,\n                    batch_gt_instances=batch_gt_instances,\n                    semantic_feat=semantic_feat)\n                for name, value in mask_results['loss_mask'].items():\n                    losses[f's{stage}.{name}'] = (\n                        value * stage_loss_weight if 'loss' in name else value)\n\n            # refine bboxes (same as Cascade R-CNN)\n            if stage < self.num_stages - 1 and not self.interleaved:\n                bbox_head = self.bbox_head[stage]\n                with torch.no_grad():\n                    results_list = bbox_head.refine_bboxes(\n                        sampling_results=sampling_results,\n                        bbox_results=bbox_results,\n                        batch_img_metas=batch_img_metas)\n\n        return losses\n\n    def predict(self,\n                x: Tuple[Tensor],\n                rpn_results_list: InstanceList,\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the roi head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from upstream network. Each\n                has shape (N, C, H, W).\n            rpn_results_list (list[:obj:`InstanceData`]): list of region\n                proposals.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results to\n                the original image. Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        assert self.with_bbox, 'Bbox head must be implemented.'\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        if self.with_semantic:\n            _, semantic_feat = self.semantic_head(x)\n        else:\n            semantic_feat = None\n\n        # TODO: nms_op in mmcv need be enhanced, the bbox result may get\n        #  difference when not rescale in bbox_head\n\n        # If it has the mask branch, the bbox branch does not need\n        # to be scaled to the original image scale, because the mask\n        # branch will scale both bbox and mask at the same time.\n        bbox_rescale = rescale if not self.with_mask else False\n        results_list = self.predict_bbox(\n            x=x,\n            semantic_feat=semantic_feat,\n            batch_img_metas=batch_img_metas,\n            rpn_results_list=rpn_results_list,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=bbox_rescale)\n\n        if self.with_mask:\n            results_list = self.predict_mask(\n                x=x,\n                semantic_heat=semantic_feat,\n                batch_img_metas=batch_img_metas,\n                results_list=results_list,\n                rescale=rescale)\n\n        return results_list\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     semantic_heat: Tensor,\n                     batch_img_metas: List[dict],\n                     results_list: InstanceList,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            semantic_feat (Tensor): Semantic feature.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas=batch_img_metas,\n                device=mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        num_mask_rois_per_img = [len(res) for res in results_list]\n        mask_results = self._mask_forward(\n            stage=-1,\n            x=x,\n            rois=mask_rois,\n            semantic_feat=semantic_heat,\n            training=False)\n        # split batch mask prediction back to each image\n        aug_masks = [[\n            mask.sigmoid().detach()\n            for mask in mask_preds.split(num_mask_rois_per_img, 0)\n        ] for mask_preds in mask_results['mask_preds']]\n\n        merged_masks = []\n        for i in range(num_imgs):\n            aug_mask = [mask[i] for mask in aug_masks]\n            merged_mask = merge_aug_masks(aug_mask, batch_img_metas[i])\n            merged_masks.append(merged_mask)\n\n        results_list = self.mask_head[-1].predict_by_feat(\n            mask_preds=merged_masks,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale,\n            activate_map=True)\n\n        return results_list\n\n    def forward(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n                batch_data_samples: SampleList) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            x (List[Tensor]): Multi-level features that may have different\n                resolutions.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n\n        Returns\n            tuple: A tuple of features from ``bbox_head`` and ``mask_head``\n            forward.\n        \"\"\"\n        results = ()\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n        num_imgs = len(batch_img_metas)\n\n        if self.with_semantic:\n            _, semantic_feat = self.semantic_head(x)\n        else:\n            semantic_feat = None\n\n        proposals = [rpn_results.bboxes for rpn_results in rpn_results_list]\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = bbox2roi(proposals)\n        # bbox head\n        if self.with_bbox:\n            rois, cls_scores, bbox_preds = self._refine_roi(\n                x=x,\n                rois=rois,\n                semantic_feat=semantic_feat,\n                batch_img_metas=batch_img_metas,\n                num_proposals_per_img=num_proposals_per_img)\n            results = results + (cls_scores, bbox_preds)\n        # mask head\n        if self.with_mask:\n            rois = torch.cat(rois)\n            mask_results = self._mask_forward(\n                stage=-1,\n                x=x,\n                rois=rois,\n                semantic_feat=semantic_feat,\n                training=False)\n            aug_masks = [[\n                mask.sigmoid().detach()\n                for mask in mask_preds.split(num_proposals_per_img, 0)\n            ] for mask_preds in mask_results['mask_preds']]\n\n            merged_masks = []\n            for i in range(num_imgs):\n                aug_mask = [mask[i] for mask in aug_masks]\n                merged_mask = merge_aug_masks(aug_mask, batch_img_metas[i])\n                merged_masks.append(merged_mask)\n            results = results + (merged_masks, )\n        return results\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .coarse_mask_head import CoarseMaskHead\nfrom .dynamic_mask_head import DynamicMaskHead\nfrom .fcn_mask_head import FCNMaskHead\nfrom .feature_relay_head import FeatureRelayHead\nfrom .fused_semantic_head import FusedSemanticHead\nfrom .global_context_head import GlobalContextHead\nfrom .grid_head import GridHead\nfrom .htc_mask_head import HTCMaskHead\nfrom .mask_point_head import MaskPointHead\nfrom .maskiou_head import MaskIoUHead\nfrom .scnet_mask_head import SCNetMaskHead\nfrom .scnet_semantic_head import SCNetSemanticHead\n\n__all__ = [\n    'FCNMaskHead', 'HTCMaskHead', 'FusedSemanticHead', 'GridHead',\n    'MaskIoUHead', 'CoarseMaskHead', 'MaskPointHead', 'SCNetMaskHead',\n    'SCNetSemanticHead', 'GlobalContextHead', 'FeatureRelayHead',\n    'DynamicMaskHead'\n]\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/coarse_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmcv.cnn import ConvModule, Linear\nfrom mmengine.model import ModuleList\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig\nfrom .fcn_mask_head import FCNMaskHead\n\n\n@MODELS.register_module()\nclass CoarseMaskHead(FCNMaskHead):\n    \"\"\"Coarse mask head used in PointRend.\n\n    Compared with standard ``FCNMaskHead``, ``CoarseMaskHead`` will downsample\n    the input feature map instead of upsample it.\n\n    Args:\n        num_convs (int): Number of conv layers in the head. Defaults to 0.\n        num_fcs (int): Number of fc layers in the head. Defaults to 2.\n        fc_out_channels (int): Number of output channels of fc layer.\n            Defaults to 1024.\n        downsample_factor (int): The factor that feature map is downsampled by.\n            Defaults to 2.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 num_convs: int = 0,\n                 num_fcs: int = 2,\n                 fc_out_channels: int = 1024,\n                 downsample_factor: int = 2,\n                 init_cfg: MultiConfig = dict(\n                     type='Xavier',\n                     override=[\n                         dict(name='fcs'),\n                         dict(type='Constant', val=0.001, name='fc_logits')\n                     ]),\n                 *arg,\n                 **kwarg) -> None:\n        super().__init__(\n            *arg,\n            num_convs=num_convs,\n            upsample_cfg=dict(type=None),\n            init_cfg=None,\n            **kwarg)\n        self.init_cfg = init_cfg\n        self.num_fcs = num_fcs\n        assert self.num_fcs > 0\n        self.fc_out_channels = fc_out_channels\n        self.downsample_factor = downsample_factor\n        assert self.downsample_factor >= 1\n        # remove conv_logit\n        delattr(self, 'conv_logits')\n\n        if downsample_factor > 1:\n            downsample_in_channels = (\n                self.conv_out_channels\n                if self.num_convs > 0 else self.in_channels)\n            self.downsample_conv = ConvModule(\n                downsample_in_channels,\n                self.conv_out_channels,\n                kernel_size=downsample_factor,\n                stride=downsample_factor,\n                padding=0,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg)\n        else:\n            self.downsample_conv = None\n\n        self.output_size = (self.roi_feat_size[0] // downsample_factor,\n                            self.roi_feat_size[1] // downsample_factor)\n        self.output_area = self.output_size[0] * self.output_size[1]\n\n        last_layer_dim = self.conv_out_channels * self.output_area\n\n        self.fcs = ModuleList()\n        for i in range(num_fcs):\n            fc_in_channels = (\n                last_layer_dim if i == 0 else self.fc_out_channels)\n            self.fcs.append(Linear(fc_in_channels, self.fc_out_channels))\n        last_layer_dim = self.fc_out_channels\n        output_channels = self.num_classes * self.output_area\n        self.fc_logits = Linear(last_layer_dim, output_channels)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights.\"\"\"\n        super(FCNMaskHead, self).init_weights()\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (Tensor): Extract mask RoI features.\n\n        Returns:\n            Tensor: Predicted foreground masks.\n        \"\"\"\n        for conv in self.convs:\n            x = conv(x)\n\n        if self.downsample_conv is not None:\n            x = self.downsample_conv(x)\n\n        x = x.flatten(1)\n        for fc in self.fcs:\n            x = self.relu(fc(x))\n        mask_preds = self.fc_logits(x).view(\n            x.size(0), self.num_classes, *self.output_size)\n        return mask_preds\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/dynamic_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.config import ConfigDict\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules import SamplingResult\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, InstanceList, OptConfigType, reduce_mean\nfrom .fcn_mask_head import FCNMaskHead\n\n\n@MODELS.register_module()\nclass DynamicMaskHead(FCNMaskHead):\n    r\"\"\"Dynamic Mask Head for\n    `Instances as Queries <http://arxiv.org/abs/2105.01928>`_\n\n    Args:\n        num_convs (int): Number of convolution layer.\n            Defaults to 4.\n        roi_feat_size (int): The output size of RoI extractor,\n            Defaults to 14.\n        in_channels (int): Input feature channels.\n            Defaults to 256.\n        conv_kernel_size (int): Kernel size of convolution layers.\n            Defaults to 3.\n        conv_out_channels (int): Output channels of convolution layers.\n            Defaults to 256.\n        num_classes (int): Number of classes.\n            Defaults to 80\n        class_agnostic (int): Whether generate class agnostic prediction.\n            Defaults to False.\n        dropout (float): Probability of drop the channel.\n            Defaults to 0.0\n        upsample_cfg (:obj:`ConfigDict` or dict): The config for\n            upsample layer.\n        conv_cfg (:obj:`ConfigDict` or dict, optional): The convolution\n            layer config.\n        norm_cfg (:obj:`ConfigDict` or dict, optional): The norm layer config.\n        dynamic_conv_cfg (:obj:`ConfigDict` or dict): The dynamic convolution\n            layer config.\n        loss_mask (:obj:`ConfigDict` or dict): The config for mask loss.\n    \"\"\"\n\n    def __init__(self,\n                 num_convs: int = 4,\n                 roi_feat_size: int = 14,\n                 in_channels: int = 256,\n                 conv_kernel_size: int = 3,\n                 conv_out_channels: int = 256,\n                 num_classes: int = 80,\n                 class_agnostic: bool = False,\n                 upsample_cfg: ConfigType = dict(\n                     type='deconv', scale_factor=2),\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 dynamic_conv_cfg: ConfigType = dict(\n                     type='DynamicConv',\n                     in_channels=256,\n                     feat_channels=64,\n                     out_channels=256,\n                     input_feat_shape=14,\n                     with_proj=False,\n                     act_cfg=dict(type='ReLU', inplace=True),\n                     norm_cfg=dict(type='LN')),\n                 loss_mask: ConfigType = dict(\n                     type='DiceLoss', loss_weight=8.0),\n                 **kwargs) -> None:\n        super().__init__(\n            num_convs=num_convs,\n            roi_feat_size=roi_feat_size,\n            in_channels=in_channels,\n            conv_kernel_size=conv_kernel_size,\n            conv_out_channels=conv_out_channels,\n            num_classes=num_classes,\n            class_agnostic=class_agnostic,\n            upsample_cfg=upsample_cfg,\n            conv_cfg=conv_cfg,\n            norm_cfg=norm_cfg,\n            loss_mask=loss_mask,\n            **kwargs)\n        assert class_agnostic is False, \\\n            'DynamicMaskHead only support class_agnostic=False'\n        self.fp16_enabled = False\n\n        self.instance_interactive_conv = MODELS.build(dynamic_conv_cfg)\n\n    def init_weights(self) -> None:\n        \"\"\"Use xavier initialization for all weight parameter and set\n        classification head bias as a specific value when use focal loss.\"\"\"\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n            nn.init.constant_(self.conv_logits.bias, 0.)\n\n    def forward(self, roi_feat: Tensor, proposal_feat: Tensor) -> Tensor:\n        \"\"\"Forward function of DynamicMaskHead.\n\n        Args:\n            roi_feat (Tensor): Roi-pooling features with shape\n                (batch_size*num_proposals, feature_dimensions,\n                pooling_h , pooling_w).\n            proposal_feat (Tensor): Intermediate feature get from\n                diihead in last stage, has shape\n                (batch_size*num_proposals, feature_dimensions)\n\n          Returns:\n            mask_preds (Tensor): Predicted foreground masks with shape\n            (batch_size*num_proposals, num_classes, pooling_h*2, pooling_w*2).\n        \"\"\"\n\n        proposal_feat = proposal_feat.reshape(-1, self.in_channels)\n        proposal_feat_iic = self.instance_interactive_conv(\n            proposal_feat, roi_feat)\n\n        x = proposal_feat_iic.permute(0, 2, 1).reshape(roi_feat.size())\n\n        for conv in self.convs:\n            x = conv(x)\n        if self.upsample is not None:\n            x = self.upsample(x)\n            if self.upsample_method == 'deconv':\n                x = self.relu(x)\n        mask_preds = self.conv_logits(x)\n        return mask_preds\n\n    def loss_and_target(self, mask_preds: Tensor,\n                        sampling_results: List[SamplingResult],\n                        batch_gt_instances: InstanceList,\n                        rcnn_train_cfg: ConfigDict) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mask_preds (Tensor): Predicted foreground masks, has shape\n                (num_pos, num_classes, h, w).\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n\n        Returns:\n            dict: A dictionary of loss and targets components.\n        \"\"\"\n        mask_targets = self.get_targets(\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=rcnn_train_cfg)\n        pos_labels = torch.cat([res.pos_gt_labels for res in sampling_results])\n\n        num_pos = pos_labels.new_ones(pos_labels.size()).float().sum()\n        avg_factor = torch.clamp(reduce_mean(num_pos), min=1.).item()\n        loss = dict()\n        if mask_preds.size(0) == 0:\n            loss_mask = mask_preds.sum()\n        else:\n            loss_mask = self.loss_mask(\n                mask_preds[torch.arange(num_pos).long(), pos_labels,\n                           ...].sigmoid(),\n                mask_targets,\n                avg_factor=avg_factor)\n        loss['loss_mask'] = loss_mask\n        return dict(loss_mask=loss, mask_targets=mask_targets)\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/fcn_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, build_conv_layer, build_upsample_layer\nfrom mmcv.ops.carafe import CARAFEPack\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule, ModuleList\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.utils import empty_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.mask import mask_target\nfrom mmdet.utils import ConfigType, InstanceList, OptConfigType, OptMultiConfig\n\nBYTES_PER_FLOAT = 4\n# TODO: This memory limit may be too much or too little. It would be better to\n#  determine it based on available resources.\nGPU_MEM_LIMIT = 1024**3  # 1 GB memory limit\n\n\n@MODELS.register_module()\nclass FCNMaskHead(BaseModule):\n\n    def __init__(self,\n                 num_convs: int = 4,\n                 roi_feat_size: int = 14,\n                 in_channels: int = 256,\n                 conv_kernel_size: int = 3,\n                 conv_out_channels: int = 256,\n                 num_classes: int = 80,\n                 class_agnostic: int = False,\n                 upsample_cfg: ConfigType = dict(\n                     type='deconv', scale_factor=2),\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 predictor_cfg: ConfigType = dict(type='Conv'),\n                 loss_mask: ConfigType = dict(\n                     type='CrossEntropyLoss', use_mask=True, loss_weight=1.0),\n                 init_cfg: OptMultiConfig = None) -> None:\n        assert init_cfg is None, 'To prevent abnormal initialization ' \\\n                                 'behavior, init_cfg is not allowed to be set'\n        super().__init__(init_cfg=init_cfg)\n        self.upsample_cfg = upsample_cfg.copy()\n        if self.upsample_cfg['type'] not in [\n                None, 'deconv', 'nearest', 'bilinear', 'carafe'\n        ]:\n            raise ValueError(\n                f'Invalid upsample method {self.upsample_cfg[\"type\"]}, '\n                'accepted methods are \"deconv\", \"nearest\", \"bilinear\", '\n                '\"carafe\"')\n        self.num_convs = num_convs\n        # WARN: roi_feat_size is reserved and not used\n        self.roi_feat_size = _pair(roi_feat_size)\n        self.in_channels = in_channels\n        self.conv_kernel_size = conv_kernel_size\n        self.conv_out_channels = conv_out_channels\n        self.upsample_method = self.upsample_cfg.get('type')\n        self.scale_factor = self.upsample_cfg.pop('scale_factor', None)\n        self.num_classes = num_classes\n        self.class_agnostic = class_agnostic\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.predictor_cfg = predictor_cfg\n        self.loss_mask = MODELS.build(loss_mask)\n\n        self.convs = ModuleList()\n        for i in range(self.num_convs):\n            in_channels = (\n                self.in_channels if i == 0 else self.conv_out_channels)\n            padding = (self.conv_kernel_size - 1) // 2\n            self.convs.append(\n                ConvModule(\n                    in_channels,\n                    self.conv_out_channels,\n                    self.conv_kernel_size,\n                    padding=padding,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg))\n        upsample_in_channels = (\n            self.conv_out_channels if self.num_convs > 0 else in_channels)\n        upsample_cfg_ = self.upsample_cfg.copy()\n        if self.upsample_method is None:\n            self.upsample = None\n        elif self.upsample_method == 'deconv':\n            upsample_cfg_.update(\n                in_channels=upsample_in_channels,\n                out_channels=self.conv_out_channels,\n                kernel_size=self.scale_factor,\n                stride=self.scale_factor)\n            self.upsample = build_upsample_layer(upsample_cfg_)\n        elif self.upsample_method == 'carafe':\n            upsample_cfg_.update(\n                channels=upsample_in_channels, scale_factor=self.scale_factor)\n            self.upsample = build_upsample_layer(upsample_cfg_)\n        else:\n            # suppress warnings\n            align_corners = (None\n                             if self.upsample_method == 'nearest' else False)\n            upsample_cfg_.update(\n                scale_factor=self.scale_factor,\n                mode=self.upsample_method,\n                align_corners=align_corners)\n            self.upsample = build_upsample_layer(upsample_cfg_)\n\n        out_channels = 1 if self.class_agnostic else self.num_classes\n        logits_in_channel = (\n            self.conv_out_channels\n            if self.upsample_method == 'deconv' else upsample_in_channels)\n        self.conv_logits = build_conv_layer(self.predictor_cfg,\n                                            logits_in_channel, out_channels, 1)\n        self.relu = nn.ReLU(inplace=True)\n        self.debug_imgs = None\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize the weights.\"\"\"\n        super().init_weights()\n        for m in [self.upsample, self.conv_logits]:\n            if m is None:\n                continue\n            elif isinstance(m, CARAFEPack):\n                m.init_weights()\n            elif hasattr(m, 'weight') and hasattr(m, 'bias'):\n                nn.init.kaiming_normal_(\n                    m.weight, mode='fan_out', nonlinearity='relu')\n                nn.init.constant_(m.bias, 0)\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (Tensor): Extract mask RoI features.\n\n        Returns:\n            Tensor: Predicted foreground masks.\n        \"\"\"\n        for conv in self.convs:\n            x = conv(x)\n        if self.upsample is not None:\n            x = self.upsample(x)\n            if self.upsample_method == 'deconv':\n                x = self.relu(x)\n        mask_preds = self.conv_logits(x)\n        return mask_preds\n\n    def get_targets(self, sampling_results: List[SamplingResult],\n                    batch_gt_instances: InstanceList,\n                    rcnn_train_cfg: ConfigDict) -> Tensor:\n        \"\"\"Calculate the ground truth for all samples in a batch according to\n        the sampling_results.\n\n        Args:\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n\n        Returns:\n            Tensor: Mask target of each positive proposals in the image.\n        \"\"\"\n        pos_proposals = [res.pos_priors for res in sampling_results]\n        pos_assigned_gt_inds = [\n            res.pos_assigned_gt_inds for res in sampling_results\n        ]\n        gt_masks = [res.masks for res in batch_gt_instances]\n        mask_targets = mask_target(pos_proposals, pos_assigned_gt_inds,\n                                   gt_masks, rcnn_train_cfg)\n        return mask_targets\n\n    def loss_and_target(self, mask_preds: Tensor,\n                        sampling_results: List[SamplingResult],\n                        batch_gt_instances: InstanceList,\n                        rcnn_train_cfg: ConfigDict) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the mask head.\n\n        Args:\n            mask_preds (Tensor): Predicted foreground masks, has shape\n                (num_pos, num_classes, h, w).\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n\n        Returns:\n            dict: A dictionary of loss and targets components.\n        \"\"\"\n        mask_targets = self.get_targets(\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=rcnn_train_cfg)\n\n        pos_labels = torch.cat([res.pos_gt_labels for res in sampling_results])\n\n        loss = dict()\n        if mask_preds.size(0) == 0:\n            loss_mask = mask_preds.sum()\n        else:\n            if self.class_agnostic:\n                loss_mask = self.loss_mask(mask_preds, mask_targets,\n                                           torch.zeros_like(pos_labels))\n            else:\n                loss_mask = self.loss_mask(mask_preds, mask_targets,\n                                           pos_labels)\n        loss['loss_mask'] = loss_mask\n        # TODO: which algorithm requires mask_targets?\n        return dict(loss_mask=loss, mask_targets=mask_targets)\n\n    def predict_by_feat(self,\n                        mask_preds: Tuple[Tensor],\n                        results_list: List[InstanceData],\n                        batch_img_metas: List[dict],\n                        rcnn_test_cfg: ConfigDict,\n                        rescale: bool = False,\n                        activate_map: bool = False) -> InstanceList:\n        \"\"\"Transform a batch of output features extracted from the head into\n        mask results.\n\n        Args:\n            mask_preds (tuple[Tensor]): Tuple of predicted foreground masks,\n                each has shape (n, num_classes, h, w).\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            batch_img_metas (list[dict]): List of image information.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of Bbox Head.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            activate_map (book): Whether get results with augmentations test.\n                If True, the `mask_preds` will not process with sigmoid.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        assert len(mask_preds) == len(results_list) == len(batch_img_metas)\n\n        for img_id in range(len(batch_img_metas)):\n            img_meta = batch_img_metas[img_id]\n            results = results_list[img_id]\n            bboxes = results.bboxes\n            if bboxes.shape[0] == 0:\n                results_list[img_id] = empty_instances(\n                    [img_meta],\n                    bboxes.device,\n                    task_type='mask',\n                    instance_results=[results],\n                    mask_thr_binary=rcnn_test_cfg.mask_thr_binary)[0]\n            else:\n                im_mask = self._predict_by_feat_single(\n                    mask_preds=mask_preds[img_id],\n                    bboxes=bboxes,\n                    labels=results.labels,\n                    img_meta=img_meta,\n                    rcnn_test_cfg=rcnn_test_cfg,\n                    rescale=rescale,\n                    activate_map=activate_map)\n                results.masks = im_mask\n        return results_list\n\n    def _predict_by_feat_single(self,\n                                mask_preds: Tensor,\n                                bboxes: Tensor,\n                                labels: Tensor,\n                                img_meta: dict,\n                                rcnn_test_cfg: ConfigDict,\n                                rescale: bool = False,\n                                activate_map: bool = False) -> Tensor:\n        \"\"\"Get segmentation masks from mask_preds and bboxes.\n\n        Args:\n            mask_preds (Tensor): Predicted foreground masks, has shape\n                (n, num_classes, h, w).\n            bboxes (Tensor): Predicted bboxes, has shape (n, 4)\n            labels (Tensor): Labels of bboxes, has shape (n, )\n            img_meta (dict): image information.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of Bbox Head.\n                Defaults to None.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            activate_map (book): Whether get results with augmentations test.\n                If True, the `mask_preds` will not process with sigmoid.\n                Defaults to False.\n\n        Returns:\n            Tensor: Encoded masks, has shape (n, img_w, img_h)\n\n        Example:\n            >>> from mmengine.config import Config\n            >>> from mmdet.models.roi_heads.mask_heads.fcn_mask_head import *  # NOQA\n            >>> N = 7  # N = number of extracted ROIs\n            >>> C, H, W = 11, 32, 32\n            >>> # Create example instance of FCN Mask Head.\n            >>> self = FCNMaskHead(num_classes=C, num_convs=0)\n            >>> inputs = torch.rand(N, self.in_channels, H, W)\n            >>> mask_preds = self.forward(inputs)\n            >>> # Each input is associated with some bounding box\n            >>> bboxes = torch.Tensor([[1, 1, 42, 42 ]] * N)\n            >>> labels = torch.randint(0, C, size=(N,))\n            >>> rcnn_test_cfg = Config({'mask_thr_binary': 0, })\n            >>> ori_shape = (H * 4, W * 4)\n            >>> scale_factor = (1, 1)\n            >>> rescale = False\n            >>> img_meta = {'scale_factor': scale_factor,\n            ...             'ori_shape': ori_shape}\n            >>> # Encoded masks are a list for each category.\n            >>> encoded_masks = self._get_seg_masks_single(\n            ...     mask_preds, bboxes, labels,\n            ...     img_meta, rcnn_test_cfg, rescale)\n            >>> assert encoded_masks.size()[0] == N\n            >>> assert encoded_masks.size()[1:] == ori_shape\n        \"\"\"\n        scale_factor = bboxes.new_tensor(img_meta['scale_factor']).repeat(\n            (1, 2))\n        img_h, img_w = img_meta['ori_shape'][:2]\n        device = bboxes.device\n\n        if not activate_map:\n            mask_preds = mask_preds.sigmoid()\n        else:\n            # In AugTest, has been activated before\n            mask_preds = bboxes.new_tensor(mask_preds)\n\n        if rescale:  # in-placed rescale the bboxes\n            bboxes /= scale_factor\n        else:\n            w_scale, h_scale = scale_factor[0, 0], scale_factor[0, 1]\n            img_h = np.round(img_h * h_scale.item()).astype(np.int32)\n            img_w = np.round(img_w * w_scale.item()).astype(np.int32)\n\n        N = len(mask_preds)\n        # The actual implementation split the input into chunks,\n        # and paste them chunk by chunk.\n        if device.type == 'cpu':\n            # CPU is most efficient when they are pasted one by one with\n            # skip_empty=True, so that it performs minimal number of\n            # operations.\n            num_chunks = N\n        else:\n            # GPU benefits from parallelism for larger chunks,\n            # but may have memory issue\n            # the types of img_w and img_h are np.int32,\n            # when the image resolution is large,\n            # the calculation of num_chunks will overflow.\n            # so we need to change the types of img_w and img_h to int.\n            # See https://github.com/open-mmlab/mmdetection/pull/5191\n            num_chunks = int(\n                np.ceil(N * int(img_h) * int(img_w) * BYTES_PER_FLOAT /\n                        GPU_MEM_LIMIT))\n            assert (num_chunks <=\n                    N), 'Default GPU_MEM_LIMIT is too small; try increasing it'\n        chunks = torch.chunk(torch.arange(N, device=device), num_chunks)\n\n        threshold = rcnn_test_cfg.mask_thr_binary\n        im_mask = torch.zeros(\n            N,\n            img_h,\n            img_w,\n            device=device,\n            dtype=torch.bool if threshold >= 0 else torch.uint8)\n\n        if not self.class_agnostic:\n            mask_preds = mask_preds[range(N), labels][:, None]\n\n        for inds in chunks:\n            masks_chunk, spatial_inds = _do_paste_mask(\n                mask_preds[inds],\n                bboxes[inds],\n                img_h,\n                img_w,\n                skip_empty=device.type == 'cpu')\n\n            if threshold >= 0:\n                masks_chunk = (masks_chunk >= threshold).to(dtype=torch.bool)\n            else:\n                # for visualization and debugging\n                masks_chunk = (masks_chunk * 255).to(dtype=torch.uint8)\n\n            im_mask[(inds, ) + spatial_inds] = masks_chunk\n        return im_mask\n\n\ndef _do_paste_mask(masks: Tensor,\n                   boxes: Tensor,\n                   img_h: int,\n                   img_w: int,\n                   skip_empty: bool = True) -> tuple:\n    \"\"\"Paste instance masks according to boxes.\n\n    This implementation is modified from\n    https://github.com/facebookresearch/detectron2/\n\n    Args:\n        masks (Tensor): N, 1, H, W\n        boxes (Tensor): N, 4\n        img_h (int): Height of the image to be pasted.\n        img_w (int): Width of the image to be pasted.\n        skip_empty (bool): Only paste masks within the region that\n            tightly bound all boxes, and returns the results this region only.\n            An important optimization for CPU.\n\n    Returns:\n        tuple: (Tensor, tuple). The first item is mask tensor, the second one\n        is the slice object.\n\n            If skip_empty == False, the whole image will be pasted. It will\n            return a mask of shape (N, img_h, img_w) and an empty tuple.\n\n            If skip_empty == True, only area around the mask will be pasted.\n            A mask of shape (N, h', w') and its start and end coordinates\n            in the original image will be returned.\n    \"\"\"\n    # On GPU, paste all masks together (up to chunk size)\n    # by using the entire image to sample the masks\n    # Compared to pasting them one by one,\n    # this has more operations but is faster on COCO-scale dataset.\n    device = masks.device\n    if skip_empty:\n        x0_int, y0_int = torch.clamp(\n            boxes.min(dim=0).values.floor()[:2] - 1,\n            min=0).to(dtype=torch.int32)\n        x1_int = torch.clamp(\n            boxes[:, 2].max().ceil() + 1, max=img_w).to(dtype=torch.int32)\n        y1_int = torch.clamp(\n            boxes[:, 3].max().ceil() + 1, max=img_h).to(dtype=torch.int32)\n    else:\n        x0_int, y0_int = 0, 0\n        x1_int, y1_int = img_w, img_h\n    x0, y0, x1, y1 = torch.split(boxes, 1, dim=1)  # each is Nx1\n\n    N = masks.shape[0]\n\n    img_y = torch.arange(y0_int, y1_int, device=device).to(torch.float32) + 0.5\n    img_x = torch.arange(x0_int, x1_int, device=device).to(torch.float32) + 0.5\n    img_y = (img_y - y0) / (y1 - y0) * 2 - 1\n    img_x = (img_x - x0) / (x1 - x0) * 2 - 1\n    # img_x, img_y have shapes (N, w), (N, h)\n    # IsInf op is not supported with ONNX<=1.7.0\n    if not torch.onnx.is_in_onnx_export():\n        if torch.isinf(img_x).any():\n            inds = torch.where(torch.isinf(img_x))\n            img_x[inds] = 0\n        if torch.isinf(img_y).any():\n            inds = torch.where(torch.isinf(img_y))\n            img_y[inds] = 0\n\n    gx = img_x[:, None, :].expand(N, img_y.size(1), img_x.size(1))\n    gy = img_y[:, :, None].expand(N, img_y.size(1), img_x.size(1))\n    grid = torch.stack([gx, gy], dim=3)\n\n    img_masks = F.grid_sample(\n        masks.to(dtype=torch.float32), grid, align_corners=False)\n\n    if skip_empty:\n        return img_masks[:, 0], (slice(y0_int, y1_int), slice(x0_int, x1_int))\n    else:\n        return img_masks[:, 0], ()\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/feature_relay_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch.nn as nn\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig\n\n\n@MODELS.register_module()\nclass FeatureRelayHead(BaseModule):\n    \"\"\"Feature Relay Head used in `SCNet <https://arxiv.org/abs/2012.10150>`_.\n\n    Args:\n        in_channels (int): number of input channels. Defaults to 256.\n        conv_out_channels (int): number of output channels before\n            classification layer. Defaults to 256.\n        roi_feat_size (int): roi feat size at box head. Default: 7.\n        scale_factor (int): scale factor to match roi feat size\n            at mask head. Defaults to 2.\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`]): Initialization config dict. Defaults to\n            dict(type='Kaiming', layer='Linear').\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels: int = 1024,\n        out_conv_channels: int = 256,\n        roi_feat_size: int = 7,\n        scale_factor: int = 2,\n        init_cfg: MultiConfig = dict(type='Kaiming', layer='Linear')\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert isinstance(roi_feat_size, int)\n\n        self.in_channels = in_channels\n        self.out_conv_channels = out_conv_channels\n        self.roi_feat_size = roi_feat_size\n        self.out_channels = (roi_feat_size**2) * out_conv_channels\n        self.scale_factor = scale_factor\n        self.fp16_enabled = False\n\n        self.fc = nn.Linear(self.in_channels, self.out_channels)\n        self.upsample = nn.Upsample(\n            scale_factor=scale_factor, mode='bilinear', align_corners=True)\n\n    def forward(self, x: Tensor) -> Optional[Tensor]:\n        \"\"\"Forward function.\n\n        Args:\n            x (Tensor): Input feature.\n\n        Returns:\n            Optional[Tensor]: Output feature. When the first dim of input is\n            0, None is returned.\n        \"\"\"\n        N, _ = x.shape\n        if N > 0:\n            out_C = self.out_conv_channels\n            out_HW = self.roi_feat_size\n            x = self.fc(x)\n            x = x.reshape(N, out_C, out_HW, out_HW)\n            x = self.upsample(x)\n            return x\n        return None\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/fused_semantic_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import Tuple\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig, OptConfigType\n\n\n@MODELS.register_module()\nclass FusedSemanticHead(BaseModule):\n    r\"\"\"Multi-level fused semantic segmentation head.\n\n    .. code-block:: none\n\n        in_1 -> 1x1 conv ---\n                            |\n        in_2 -> 1x1 conv -- |\n                           ||\n        in_3 -> 1x1 conv - ||\n                          |||                  /-> 1x1 conv (mask prediction)\n        in_4 -> 1x1 conv -----> 3x3 convs (*4)\n                            |                  \\-> 1x1 conv (feature)\n        in_5 -> 1x1 conv ---\n    \"\"\"  # noqa: W605\n\n    def __init__(\n        self,\n        num_ins: int,\n        fusion_level: int,\n        seg_scale_factor=1 / 8,\n        num_convs: int = 4,\n        in_channels: int = 256,\n        conv_out_channels: int = 256,\n        num_classes: int = 183,\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        ignore_label: int = None,\n        loss_weight: float = None,\n        loss_seg: ConfigDict = dict(\n            type='CrossEntropyLoss', ignore_index=255, loss_weight=0.2),\n        init_cfg: MultiConfig = dict(\n            type='Kaiming', override=dict(name='conv_logits'))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.num_ins = num_ins\n        self.fusion_level = fusion_level\n        self.seg_scale_factor = seg_scale_factor\n        self.num_convs = num_convs\n        self.in_channels = in_channels\n        self.conv_out_channels = conv_out_channels\n        self.num_classes = num_classes\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.fp16_enabled = False\n\n        self.lateral_convs = nn.ModuleList()\n        for i in range(self.num_ins):\n            self.lateral_convs.append(\n                ConvModule(\n                    self.in_channels,\n                    self.in_channels,\n                    1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    inplace=False))\n\n        self.convs = nn.ModuleList()\n        for i in range(self.num_convs):\n            in_channels = self.in_channels if i == 0 else conv_out_channels\n            self.convs.append(\n                ConvModule(\n                    in_channels,\n                    conv_out_channels,\n                    3,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        self.conv_embedding = ConvModule(\n            conv_out_channels,\n            conv_out_channels,\n            1,\n            conv_cfg=self.conv_cfg,\n            norm_cfg=self.norm_cfg)\n        self.conv_logits = nn.Conv2d(conv_out_channels, self.num_classes, 1)\n        if ignore_label:\n            loss_seg['ignore_index'] = ignore_label\n        if loss_weight:\n            loss_seg['loss_weight'] = loss_weight\n        if ignore_label or loss_weight:\n            warnings.warn('``ignore_label`` and ``loss_weight`` would be '\n                          'deprecated soon. Please set ``ingore_index`` and '\n                          '``loss_weight`` in ``loss_seg`` instead.')\n        self.criterion = MODELS.build(loss_seg)\n\n    def forward(self, feats: Tuple[Tensor]) -> Tuple[Tensor]:\n        \"\"\"Forward function.\n\n        Args:\n            feats (tuple[Tensor]): Multi scale feature maps.\n\n        Returns:\n            tuple[Tensor]:\n\n                - mask_preds (Tensor): Predicted mask logits.\n                - x (Tensor): Fused feature.\n        \"\"\"\n        x = self.lateral_convs[self.fusion_level](feats[self.fusion_level])\n        fused_size = tuple(x.shape[-2:])\n        for i, feat in enumerate(feats):\n            if i != self.fusion_level:\n                feat = F.interpolate(\n                    feat, size=fused_size, mode='bilinear', align_corners=True)\n                # fix runtime error of \"+=\" inplace operation in PyTorch 1.10\n                x = x + self.lateral_convs[i](feat)\n\n        for i in range(self.num_convs):\n            x = self.convs[i](x)\n\n        mask_preds = self.conv_logits(x)\n        x = self.conv_embedding(x)\n        return mask_preds, x\n\n    def loss(self, mask_preds: Tensor, labels: Tensor) -> Tensor:\n        \"\"\"Loss function.\n\n        Args:\n            mask_preds (Tensor): Predicted mask logits.\n            labels (Tensor): Ground truth.\n\n        Returns:\n            Tensor: Semantic segmentation loss.\n        \"\"\"\n        labels = F.interpolate(\n            labels.float(), scale_factor=self.seg_scale_factor, mode='nearest')\n        labels = labels.squeeze(1).long()\n        loss_semantic_seg = self.criterion(mask_preds, labels)\n        return loss_semantic_seg\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/global_context_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.models.layers import ResLayer, SimplifiedBasicBlock\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig, OptConfigType\n\n\n@MODELS.register_module()\nclass GlobalContextHead(BaseModule):\n    \"\"\"Global context head used in `SCNet <https://arxiv.org/abs/2012.10150>`_.\n\n    Args:\n        num_convs (int, optional): number of convolutional layer in GlbCtxHead.\n            Defaults to 4.\n        in_channels (int, optional): number of input channels. Defaults to 256.\n        conv_out_channels (int, optional): number of output channels before\n            classification layer. Defaults to 256.\n        num_classes (int, optional): number of classes. Defaults to 80.\n        loss_weight (float, optional): global context loss weight.\n            Defaults to 1.\n        conv_cfg (dict, optional): config to init conv layer. Defaults to None.\n        norm_cfg (dict, optional): config to init norm layer. Defaults to None.\n        conv_to_res (bool, optional): if True, 2 convs will be grouped into\n            1 `SimplifiedBasicBlock` using a skip connection.\n            Defaults to False.\n        init_cfg (:obj:`ConfigDict` or dict or list[dict] or\n            list[:obj:`ConfigDict`]): Initialization config dict. Defaults to\n            dict(type='Normal', std=0.01, override=dict(name='fc')).\n    \"\"\"\n\n    def __init__(\n        self,\n        num_convs: int = 4,\n        in_channels: int = 256,\n        conv_out_channels: int = 256,\n        num_classes: int = 80,\n        loss_weight: float = 1.0,\n        conv_cfg: OptConfigType = None,\n        norm_cfg: OptConfigType = None,\n        conv_to_res: bool = False,\n        init_cfg: MultiConfig = dict(\n            type='Normal', std=0.01, override=dict(name='fc'))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.num_convs = num_convs\n        self.in_channels = in_channels\n        self.conv_out_channels = conv_out_channels\n        self.num_classes = num_classes\n        self.loss_weight = loss_weight\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.conv_to_res = conv_to_res\n        self.fp16_enabled = False\n\n        if self.conv_to_res:\n            num_res_blocks = num_convs // 2\n            self.convs = ResLayer(\n                SimplifiedBasicBlock,\n                in_channels,\n                self.conv_out_channels,\n                num_res_blocks,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg)\n            self.num_convs = num_res_blocks\n        else:\n            self.convs = nn.ModuleList()\n            for i in range(self.num_convs):\n                in_channels = self.in_channels if i == 0 else conv_out_channels\n                self.convs.append(\n                    ConvModule(\n                        in_channels,\n                        conv_out_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg))\n\n        self.pool = nn.AdaptiveAvgPool2d(1)\n        self.fc = nn.Linear(conv_out_channels, num_classes)\n\n        self.criterion = nn.BCEWithLogitsLoss()\n\n    def forward(self, feats: Tuple[Tensor]) -> Tuple[Tensor]:\n        \"\"\"Forward function.\n\n        Args:\n            feats (Tuple[Tensor]): Multi-scale feature maps.\n\n        Returns:\n            Tuple[Tensor]:\n\n                - mc_pred (Tensor): Multi-class prediction.\n                - x (Tensor): Global context feature.\n        \"\"\"\n        x = feats[-1]\n        for i in range(self.num_convs):\n            x = self.convs[i](x)\n        x = self.pool(x)\n\n        # multi-class prediction\n        mc_pred = x.reshape(x.size(0), -1)\n        mc_pred = self.fc(mc_pred)\n\n        return mc_pred, x\n\n    def loss(self, pred: Tensor, labels: List[Tensor]) -> Tensor:\n        \"\"\"Loss function.\n\n        Args:\n            pred (Tensor): Logits.\n            labels (list[Tensor]): Grouth truths.\n\n        Returns:\n            Tensor: Loss.\n        \"\"\"\n        labels = [lbl.unique() for lbl in labels]\n        targets = pred.new_zeros(pred.size())\n        for i, label in enumerate(labels):\n            targets[i, label] = 1.0\n        loss = self.loss_weight * self.criterion(pred, targets)\n        return loss\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/grid_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Tuple\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig, OptConfigType\n\n\n@MODELS.register_module()\nclass GridHead(BaseModule):\n    \"\"\"Implementation of `Grid Head <https://arxiv.org/abs/1811.12030>`_\n\n    Args:\n        grid_points (int): The number of grid points. Defaults to 9.\n        num_convs (int): The number of convolution layers. Defaults to 8.\n        roi_feat_size (int): RoI feature size. Default to 14.\n        in_channels (int): The channel number of inputs features.\n            Defaults to 256.\n        conv_kernel_size (int): The kernel size of convolution layers.\n            Defaults to 3.\n        point_feat_channels (int): The number of channels of each point\n            features. Defaults to 64.\n        class_agnostic (bool): Whether use class agnostic classification.\n            If so, the output channels of logits will be 1. Defaults to False.\n        loss_grid (:obj:`ConfigDict` or dict): Config of grid loss.\n        conv_cfg (:obj:`ConfigDict` or dict, optional) dictionary to\n            construct and config conv layer.\n        norm_cfg (:obj:`ConfigDict` or dict): dictionary to construct and\n            config norm layer.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        grid_points: int = 9,\n        num_convs: int = 8,\n        roi_feat_size: int = 14,\n        in_channels: int = 256,\n        conv_kernel_size: int = 3,\n        point_feat_channels: int = 64,\n        deconv_kernel_size: int = 4,\n        class_agnostic: bool = False,\n        loss_grid: ConfigType = dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=15),\n        conv_cfg: OptConfigType = None,\n        norm_cfg: ConfigType = dict(type='GN', num_groups=36),\n        init_cfg: MultiConfig = [\n            dict(type='Kaiming', layer=['Conv2d', 'Linear']),\n            dict(\n                type='Normal',\n                layer='ConvTranspose2d',\n                std=0.001,\n                override=dict(\n                    type='Normal',\n                    name='deconv2',\n                    std=0.001,\n                    bias=-np.log(0.99 / 0.01)))\n        ]\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.grid_points = grid_points\n        self.num_convs = num_convs\n        self.roi_feat_size = roi_feat_size\n        self.in_channels = in_channels\n        self.conv_kernel_size = conv_kernel_size\n        self.point_feat_channels = point_feat_channels\n        self.conv_out_channels = self.point_feat_channels * self.grid_points\n        self.class_agnostic = class_agnostic\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        if isinstance(norm_cfg, dict) and norm_cfg['type'] == 'GN':\n            assert self.conv_out_channels % norm_cfg['num_groups'] == 0\n\n        assert self.grid_points >= 4\n        self.grid_size = int(np.sqrt(self.grid_points))\n        if self.grid_size * self.grid_size != self.grid_points:\n            raise ValueError('grid_points must be a square number')\n\n        # the predicted heatmap is half of whole_map_size\n        if not isinstance(self.roi_feat_size, int):\n            raise ValueError('Only square RoIs are supporeted in Grid R-CNN')\n        self.whole_map_size = self.roi_feat_size * 4\n\n        # compute point-wise sub-regions\n        self.sub_regions = self.calc_sub_regions()\n\n        self.convs = []\n        for i in range(self.num_convs):\n            in_channels = (\n                self.in_channels if i == 0 else self.conv_out_channels)\n            stride = 2 if i == 0 else 1\n            padding = (self.conv_kernel_size - 1) // 2\n            self.convs.append(\n                ConvModule(\n                    in_channels,\n                    self.conv_out_channels,\n                    self.conv_kernel_size,\n                    stride=stride,\n                    padding=padding,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg,\n                    bias=True))\n        self.convs = nn.Sequential(*self.convs)\n\n        self.deconv1 = nn.ConvTranspose2d(\n            self.conv_out_channels,\n            self.conv_out_channels,\n            kernel_size=deconv_kernel_size,\n            stride=2,\n            padding=(deconv_kernel_size - 2) // 2,\n            groups=grid_points)\n        self.norm1 = nn.GroupNorm(grid_points, self.conv_out_channels)\n        self.deconv2 = nn.ConvTranspose2d(\n            self.conv_out_channels,\n            grid_points,\n            kernel_size=deconv_kernel_size,\n            stride=2,\n            padding=(deconv_kernel_size - 2) // 2,\n            groups=grid_points)\n\n        # find the 4-neighbor of each grid point\n        self.neighbor_points = []\n        grid_size = self.grid_size\n        for i in range(grid_size):  # i-th column\n            for j in range(grid_size):  # j-th row\n                neighbors = []\n                if i > 0:  # left: (i - 1, j)\n                    neighbors.append((i - 1) * grid_size + j)\n                if j > 0:  # up: (i, j - 1)\n                    neighbors.append(i * grid_size + j - 1)\n                if j < grid_size - 1:  # down: (i, j + 1)\n                    neighbors.append(i * grid_size + j + 1)\n                if i < grid_size - 1:  # right: (i + 1, j)\n                    neighbors.append((i + 1) * grid_size + j)\n                self.neighbor_points.append(tuple(neighbors))\n        # total edges in the grid\n        self.num_edges = sum([len(p) for p in self.neighbor_points])\n\n        self.forder_trans = nn.ModuleList()  # first-order feature transition\n        self.sorder_trans = nn.ModuleList()  # second-order feature transition\n        for neighbors in self.neighbor_points:\n            fo_trans = nn.ModuleList()\n            so_trans = nn.ModuleList()\n            for _ in range(len(neighbors)):\n                # each transition module consists of a 5x5 depth-wise conv and\n                # 1x1 conv.\n                fo_trans.append(\n                    nn.Sequential(\n                        nn.Conv2d(\n                            self.point_feat_channels,\n                            self.point_feat_channels,\n                            5,\n                            stride=1,\n                            padding=2,\n                            groups=self.point_feat_channels),\n                        nn.Conv2d(self.point_feat_channels,\n                                  self.point_feat_channels, 1)))\n                so_trans.append(\n                    nn.Sequential(\n                        nn.Conv2d(\n                            self.point_feat_channels,\n                            self.point_feat_channels,\n                            5,\n                            1,\n                            2,\n                            groups=self.point_feat_channels),\n                        nn.Conv2d(self.point_feat_channels,\n                                  self.point_feat_channels, 1)))\n            self.forder_trans.append(fo_trans)\n            self.sorder_trans.append(so_trans)\n\n        self.loss_grid = MODELS.build(loss_grid)\n\n    def forward(self, x: Tensor) -> Dict[str, Tensor]:\n        \"\"\"forward function of ``GridHead``.\n\n        Args:\n            x (Tensor): RoI features, has shape\n                (num_rois, num_channels, roi_feat_size, roi_feat_size).\n\n        Returns:\n            Dict[str, Tensor]: Return a dict including fused and unfused\n            heatmap.\n        \"\"\"\n        assert x.shape[-1] == x.shape[-2] == self.roi_feat_size\n        # RoI feature transformation, downsample 2x\n        x = self.convs(x)\n\n        c = self.point_feat_channels\n        # first-order fusion\n        x_fo = [None for _ in range(self.grid_points)]\n        for i, points in enumerate(self.neighbor_points):\n            x_fo[i] = x[:, i * c:(i + 1) * c]\n            for j, point_idx in enumerate(points):\n                x_fo[i] = x_fo[i] + self.forder_trans[i][j](\n                    x[:, point_idx * c:(point_idx + 1) * c])\n\n        # second-order fusion\n        x_so = [None for _ in range(self.grid_points)]\n        for i, points in enumerate(self.neighbor_points):\n            x_so[i] = x[:, i * c:(i + 1) * c]\n            for j, point_idx in enumerate(points):\n                x_so[i] = x_so[i] + self.sorder_trans[i][j](x_fo[point_idx])\n\n        # predicted heatmap with fused features\n        x2 = torch.cat(x_so, dim=1)\n        x2 = self.deconv1(x2)\n        x2 = F.relu(self.norm1(x2), inplace=True)\n        heatmap = self.deconv2(x2)\n\n        # predicted heatmap with original features (applicable during training)\n        if self.training:\n            x1 = x\n            x1 = self.deconv1(x1)\n            x1 = F.relu(self.norm1(x1), inplace=True)\n            heatmap_unfused = self.deconv2(x1)\n        else:\n            heatmap_unfused = heatmap\n\n        return dict(fused=heatmap, unfused=heatmap_unfused)\n\n    def calc_sub_regions(self) -> List[Tuple[float]]:\n        \"\"\"Compute point specific representation regions.\n\n        See `Grid R-CNN Plus <https://arxiv.org/abs/1906.05688>`_ for details.\n        \"\"\"\n        # to make it consistent with the original implementation, half_size\n        # is computed as 2 * quarter_size, which is smaller\n        half_size = self.whole_map_size // 4 * 2\n        sub_regions = []\n        for i in range(self.grid_points):\n            x_idx = i // self.grid_size\n            y_idx = i % self.grid_size\n            if x_idx == 0:\n                sub_x1 = 0\n            elif x_idx == self.grid_size - 1:\n                sub_x1 = half_size\n            else:\n                ratio = x_idx / (self.grid_size - 1) - 0.25\n                sub_x1 = max(int(ratio * self.whole_map_size), 0)\n\n            if y_idx == 0:\n                sub_y1 = 0\n            elif y_idx == self.grid_size - 1:\n                sub_y1 = half_size\n            else:\n                ratio = y_idx / (self.grid_size - 1) - 0.25\n                sub_y1 = max(int(ratio * self.whole_map_size), 0)\n            sub_regions.append(\n                (sub_x1, sub_y1, sub_x1 + half_size, sub_y1 + half_size))\n        return sub_regions\n\n    def get_targets(self, sampling_results: List[SamplingResult],\n                    rcnn_train_cfg: ConfigDict) -> Tensor:\n        \"\"\"Calculate the ground truth for all samples in a batch according to\n        the sampling_results.\".\n\n        Args:\n            sampling_results (List[:obj:`SamplingResult`]): Assign results of\n                all images in a batch after sampling.\n            rcnn_train_cfg (:obj:`ConfigDict`): `train_cfg` of RCNN.\n\n        Returns:\n            Tensor: Grid heatmap targets.\n        \"\"\"\n        # mix all samples (across images) together.\n        pos_bboxes = torch.cat([res.pos_bboxes for res in sampling_results],\n                               dim=0).cpu()\n        pos_gt_bboxes = torch.cat(\n            [res.pos_gt_bboxes for res in sampling_results], dim=0).cpu()\n        assert pos_bboxes.shape == pos_gt_bboxes.shape\n\n        # expand pos_bboxes to 2x of original size\n        x1 = pos_bboxes[:, 0] - (pos_bboxes[:, 2] - pos_bboxes[:, 0]) / 2\n        y1 = pos_bboxes[:, 1] - (pos_bboxes[:, 3] - pos_bboxes[:, 1]) / 2\n        x2 = pos_bboxes[:, 2] + (pos_bboxes[:, 2] - pos_bboxes[:, 0]) / 2\n        y2 = pos_bboxes[:, 3] + (pos_bboxes[:, 3] - pos_bboxes[:, 1]) / 2\n        pos_bboxes = torch.stack([x1, y1, x2, y2], dim=-1)\n        pos_bbox_ws = (pos_bboxes[:, 2] - pos_bboxes[:, 0]).unsqueeze(-1)\n        pos_bbox_hs = (pos_bboxes[:, 3] - pos_bboxes[:, 1]).unsqueeze(-1)\n\n        num_rois = pos_bboxes.shape[0]\n        map_size = self.whole_map_size\n        # this is not the final target shape\n        targets = torch.zeros((num_rois, self.grid_points, map_size, map_size),\n                              dtype=torch.float)\n\n        # pre-compute interpolation factors for all grid points.\n        # the first item is the factor of x-dim, and the second is y-dim.\n        # for a 9-point grid, factors are like (1, 0), (0.5, 0.5), (0, 1)\n        factors = []\n        for j in range(self.grid_points):\n            x_idx = j // self.grid_size\n            y_idx = j % self.grid_size\n            factors.append((1 - x_idx / (self.grid_size - 1),\n                            1 - y_idx / (self.grid_size - 1)))\n\n        radius = rcnn_train_cfg.pos_radius\n        radius2 = radius**2\n        for i in range(num_rois):\n            # ignore small bboxes\n            if (pos_bbox_ws[i] <= self.grid_size\n                    or pos_bbox_hs[i] <= self.grid_size):\n                continue\n            # for each grid point, mark a small circle as positive\n            for j in range(self.grid_points):\n                factor_x, factor_y = factors[j]\n                gridpoint_x = factor_x * pos_gt_bboxes[i, 0] + (\n                    1 - factor_x) * pos_gt_bboxes[i, 2]\n                gridpoint_y = factor_y * pos_gt_bboxes[i, 1] + (\n                    1 - factor_y) * pos_gt_bboxes[i, 3]\n\n                cx = int((gridpoint_x - pos_bboxes[i, 0]) / pos_bbox_ws[i] *\n                         map_size)\n                cy = int((gridpoint_y - pos_bboxes[i, 1]) / pos_bbox_hs[i] *\n                         map_size)\n\n                for x in range(cx - radius, cx + radius + 1):\n                    for y in range(cy - radius, cy + radius + 1):\n                        if x >= 0 and x < map_size and y >= 0 and y < map_size:\n                            if (x - cx)**2 + (y - cy)**2 <= radius2:\n                                targets[i, j, y, x] = 1\n        # reduce the target heatmap size by a half\n        # proposed in Grid R-CNN Plus (https://arxiv.org/abs/1906.05688).\n        sub_targets = []\n        for i in range(self.grid_points):\n            sub_x1, sub_y1, sub_x2, sub_y2 = self.sub_regions[i]\n            sub_targets.append(targets[:, [i], sub_y1:sub_y2, sub_x1:sub_x2])\n        sub_targets = torch.cat(sub_targets, dim=1)\n        sub_targets = sub_targets.to(sampling_results[0].pos_bboxes.device)\n        return sub_targets\n\n    def loss(self, grid_pred: Tensor, sample_idx: Tensor,\n             sampling_results: List[SamplingResult],\n             rcnn_train_cfg: ConfigDict) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the grid head.\n\n        Args:\n            grid_pred (dict[str, Tensor]): Outputs of grid_head forward.\n            sample_idx (Tensor): The sampling index of ``grid_pred``.\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            rcnn_train_cfg (obj:`ConfigDict`): `train_cfg` of RCNN.\n\n        Returns:\n            dict: A dictionary of loss and targets components.\n        \"\"\"\n        grid_targets = self.get_targets(sampling_results, rcnn_train_cfg)\n        grid_targets = grid_targets[sample_idx]\n\n        loss_fused = self.loss_grid(grid_pred['fused'], grid_targets)\n        loss_unfused = self.loss_grid(grid_pred['unfused'], grid_targets)\n        loss_grid = loss_fused + loss_unfused\n        return dict(loss_grid=loss_grid)\n\n    def predict_by_feat(self,\n                        grid_preds: Dict[str, Tensor],\n                        results_list: List[InstanceData],\n                        batch_img_metas: List[dict],\n                        rescale: bool = False) -> InstanceList:\n        \"\"\"Adjust the predicted bboxes from bbox head.\n\n        Args:\n            grid_preds (dict[str, Tensor]): dictionary outputted by forward\n                function.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            batch_img_metas (list[dict]): List of image information.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process. Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape \\\n            (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4), the last \\\n            dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        num_roi_per_img = tuple(res.bboxes.size(0) for res in results_list)\n        grid_preds = {\n            k: v.split(num_roi_per_img, 0)\n            for k, v in grid_preds.items()\n        }\n\n        for i, results in enumerate(results_list):\n            if len(results) != 0:\n                bboxes = self._predict_by_feat_single(\n                    grid_pred=grid_preds['fused'][i],\n                    bboxes=results.bboxes,\n                    img_meta=batch_img_metas[i],\n                    rescale=rescale)\n                results.bboxes = bboxes\n        return results_list\n\n    def _predict_by_feat_single(self,\n                                grid_pred: Tensor,\n                                bboxes: Tensor,\n                                img_meta: dict,\n                                rescale: bool = False) -> Tensor:\n        \"\"\"Adjust ``bboxes`` according to ``grid_pred``.\n\n        Args:\n            grid_pred (Tensor): Grid fused heatmap.\n            bboxes (Tensor): Predicted bboxes, has shape (n, 4)\n            img_meta (dict): image information.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            Tensor: adjusted bboxes.\n        \"\"\"\n        assert bboxes.size(0) == grid_pred.size(0)\n        grid_pred = grid_pred.sigmoid()\n\n        R, c, h, w = grid_pred.shape\n        half_size = self.whole_map_size // 4 * 2\n        assert h == w == half_size\n        assert c == self.grid_points\n\n        # find the point with max scores in the half-sized heatmap\n        grid_pred = grid_pred.view(R * c, h * w)\n        pred_scores, pred_position = grid_pred.max(dim=1)\n        xs = pred_position % w\n        ys = pred_position // w\n\n        # get the position in the whole heatmap instead of half-sized heatmap\n        for i in range(self.grid_points):\n            xs[i::self.grid_points] += self.sub_regions[i][0]\n            ys[i::self.grid_points] += self.sub_regions[i][1]\n\n        # reshape to (num_rois, grid_points)\n        pred_scores, xs, ys = tuple(\n            map(lambda x: x.view(R, c), [pred_scores, xs, ys]))\n\n        # get expanded pos_bboxes\n        widths = (bboxes[:, 2] - bboxes[:, 0]).unsqueeze(-1)\n        heights = (bboxes[:, 3] - bboxes[:, 1]).unsqueeze(-1)\n        x1 = (bboxes[:, 0, None] - widths / 2)\n        y1 = (bboxes[:, 1, None] - heights / 2)\n        # map the grid point to the absolute coordinates\n        abs_xs = (xs.float() + 0.5) / w * widths + x1\n        abs_ys = (ys.float() + 0.5) / h * heights + y1\n\n        # get the grid points indices that fall on the bbox boundaries\n        x1_inds = [i for i in range(self.grid_size)]\n        y1_inds = [i * self.grid_size for i in range(self.grid_size)]\n        x2_inds = [\n            self.grid_points - self.grid_size + i\n            for i in range(self.grid_size)\n        ]\n        y2_inds = [(i + 1) * self.grid_size - 1 for i in range(self.grid_size)]\n\n        # voting of all grid points on some boundary\n        bboxes_x1 = (abs_xs[:, x1_inds] * pred_scores[:, x1_inds]).sum(\n            dim=1, keepdim=True) / (\n                pred_scores[:, x1_inds].sum(dim=1, keepdim=True))\n        bboxes_y1 = (abs_ys[:, y1_inds] * pred_scores[:, y1_inds]).sum(\n            dim=1, keepdim=True) / (\n                pred_scores[:, y1_inds].sum(dim=1, keepdim=True))\n        bboxes_x2 = (abs_xs[:, x2_inds] * pred_scores[:, x2_inds]).sum(\n            dim=1, keepdim=True) / (\n                pred_scores[:, x2_inds].sum(dim=1, keepdim=True))\n        bboxes_y2 = (abs_ys[:, y2_inds] * pred_scores[:, y2_inds]).sum(\n            dim=1, keepdim=True) / (\n                pred_scores[:, y2_inds].sum(dim=1, keepdim=True))\n\n        bboxes = torch.cat([bboxes_x1, bboxes_y1, bboxes_x2, bboxes_y2], dim=1)\n        bboxes[:, [0, 2]].clamp_(min=0, max=img_meta['img_shape'][1])\n        bboxes[:, [1, 3]].clamp_(min=0, max=img_meta['img_shape'][0])\n\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            bboxes /= bboxes.new_tensor(img_meta['scale_factor']).repeat(\n                (1, 2))\n\n        return bboxes\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/htc_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Union\n\nfrom mmcv.cnn import ConvModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom .fcn_mask_head import FCNMaskHead\n\n\n@MODELS.register_module()\nclass HTCMaskHead(FCNMaskHead):\n    \"\"\"Mask head for HTC.\n\n    Args:\n        with_conv_res (bool): Whether add conv layer for ``res_feat``.\n            Defaults to True.\n    \"\"\"\n\n    def __init__(self, with_conv_res: bool = True, *args, **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        self.with_conv_res = with_conv_res\n        if self.with_conv_res:\n            self.conv_res = ConvModule(\n                self.conv_out_channels,\n                self.conv_out_channels,\n                1,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg)\n\n    def forward(self,\n                x: Tensor,\n                res_feat: Optional[Tensor] = None,\n                return_logits: bool = True,\n                return_feat: bool = True) -> Union[Tensor, List[Tensor]]:\n        \"\"\"\n        Args:\n            x (Tensor): Feature map.\n            res_feat (Tensor, optional): Feature for residual connection.\n                Defaults to None.\n            return_logits (bool): Whether return mask logits. Defaults to True.\n            return_feat (bool): Whether return feature map. Defaults to True.\n\n        Returns:\n            Union[Tensor, List[Tensor]]: The return result is one of three\n                results: res_feat, logits, or [logits, res_feat].\n        \"\"\"\n        assert not (not return_logits and not return_feat)\n        if res_feat is not None:\n            assert self.with_conv_res\n            res_feat = self.conv_res(res_feat)\n            x = x + res_feat\n        for conv in self.convs:\n            x = conv(x)\n        res_feat = x\n        outs = []\n        if return_logits:\n            x = self.upsample(x)\n            if self.upsample_method == 'deconv':\n                x = self.relu(x)\n            mask_preds = self.conv_logits(x)\n            outs.append(mask_preds)\n        if return_feat:\n            outs.append(res_feat)\n        return outs if len(outs) > 1 else outs[0]\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/mask_point_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend/point_head/point_head.py  # noqa\n\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmcv.ops import point_sample, rel_roi_point_to_rel_img_point\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.utils import (get_uncertain_point_coords_with_randomness,\n                                get_uncertainty)\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig, OptConfigType\n\n\n@MODELS.register_module()\nclass MaskPointHead(BaseModule):\n    \"\"\"A mask point head use in PointRend.\n\n    ``MaskPointHead`` use shared multi-layer perceptron (equivalent to\n    nn.Conv1d) to predict the logit of input points. The fine-grained feature\n    and coarse feature will be concatenate together for predication.\n\n    Args:\n        num_fcs (int): Number of fc layers in the head. Defaults to 3.\n        in_channels (int): Number of input channels. Defaults to 256.\n        fc_channels (int): Number of fc channels. Defaults to 256.\n        num_classes (int): Number of classes for logits. Defaults to 80.\n        class_agnostic (bool): Whether use class agnostic classification.\n            If so, the output channels of logits will be 1. Defaults to False.\n        coarse_pred_each_layer (bool): Whether concatenate coarse feature with\n            the output of each fc layer. Defaults to True.\n        conv_cfg (:obj:`ConfigDict` or dict): Dictionary to construct\n            and config conv layer. Defaults to dict(type='Conv1d')).\n        norm_cfg (:obj:`ConfigDict` or dict, optional): Dictionary to construct\n            and config norm layer. Defaults to None.\n        loss_point (:obj:`ConfigDict` or dict): Dictionary to construct and\n            config loss layer of point head. Defaults to\n            dict(type='CrossEntropyLoss', use_mask=True, loss_weight=1.0).\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes: int,\n        num_fcs: int = 3,\n        in_channels: int = 256,\n        fc_channels: int = 256,\n        class_agnostic: bool = False,\n        coarse_pred_each_layer: bool = True,\n        conv_cfg: ConfigType = dict(type='Conv1d'),\n        norm_cfg: OptConfigType = None,\n        act_cfg: ConfigType = dict(type='ReLU'),\n        loss_point: ConfigType = dict(\n            type='CrossEntropyLoss', use_mask=True, loss_weight=1.0),\n        init_cfg: MultiConfig = dict(\n            type='Normal', std=0.001, override=dict(name='fc_logits'))\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.num_fcs = num_fcs\n        self.in_channels = in_channels\n        self.fc_channels = fc_channels\n        self.num_classes = num_classes\n        self.class_agnostic = class_agnostic\n        self.coarse_pred_each_layer = coarse_pred_each_layer\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.loss_point = MODELS.build(loss_point)\n\n        fc_in_channels = in_channels + num_classes\n        self.fcs = nn.ModuleList()\n        for _ in range(num_fcs):\n            fc = ConvModule(\n                fc_in_channels,\n                fc_channels,\n                kernel_size=1,\n                stride=1,\n                padding=0,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg)\n            self.fcs.append(fc)\n            fc_in_channels = fc_channels\n            fc_in_channels += num_classes if self.coarse_pred_each_layer else 0\n\n        out_channels = 1 if self.class_agnostic else self.num_classes\n        self.fc_logits = nn.Conv1d(\n            fc_in_channels, out_channels, kernel_size=1, stride=1, padding=0)\n\n    def forward(self, fine_grained_feats: Tensor,\n                coarse_feats: Tensor) -> Tensor:\n        \"\"\"Classify each point base on fine grained and coarse feats.\n\n        Args:\n            fine_grained_feats (Tensor): Fine grained feature sampled from FPN,\n                shape (num_rois, in_channels, num_points).\n            coarse_feats (Tensor): Coarse feature sampled from CoarseMaskHead,\n                shape (num_rois, num_classes, num_points).\n\n        Returns:\n            Tensor: Point classification results,\n            shape (num_rois, num_class, num_points).\n        \"\"\"\n\n        x = torch.cat([fine_grained_feats, coarse_feats], dim=1)\n        for fc in self.fcs:\n            x = fc(x)\n            if self.coarse_pred_each_layer:\n                x = torch.cat((x, coarse_feats), dim=1)\n        return self.fc_logits(x)\n\n    def get_targets(self, rois: Tensor, rel_roi_points: Tensor,\n                    sampling_results: List[SamplingResult],\n                    batch_gt_instances: InstanceList,\n                    cfg: ConfigType) -> Tensor:\n        \"\"\"Get training targets of MaskPointHead for all images.\n\n        Args:\n            rois (Tensor): Region of Interest, shape (num_rois, 5).\n            rel_roi_points (Tensor): Points coordinates relative to RoI, shape\n                (num_rois, num_points, 2).\n            sampling_results (:obj:`SamplingResult`): Sampling result after\n                sampling and assignment.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            cfg (obj:`ConfigDict` or dict): Training cfg.\n\n        Returns:\n            Tensor: Point target, shape (num_rois, num_points).\n        \"\"\"\n\n        num_imgs = len(sampling_results)\n        rois_list = []\n        rel_roi_points_list = []\n        for batch_ind in range(num_imgs):\n            inds = (rois[:, 0] == batch_ind)\n            rois_list.append(rois[inds])\n            rel_roi_points_list.append(rel_roi_points[inds])\n        pos_assigned_gt_inds_list = [\n            res.pos_assigned_gt_inds for res in sampling_results\n        ]\n        cfg_list = [cfg for _ in range(num_imgs)]\n\n        point_targets = map(self._get_targets_single, rois_list,\n                            rel_roi_points_list, pos_assigned_gt_inds_list,\n                            batch_gt_instances, cfg_list)\n        point_targets = list(point_targets)\n\n        if len(point_targets) > 0:\n            point_targets = torch.cat(point_targets)\n\n        return point_targets\n\n    def _get_targets_single(self, rois: Tensor, rel_roi_points: Tensor,\n                            pos_assigned_gt_inds: Tensor,\n                            gt_instances: InstanceData,\n                            cfg: ConfigType) -> Tensor:\n        \"\"\"Get training target of MaskPointHead for each image.\"\"\"\n        num_pos = rois.size(0)\n        num_points = cfg.num_points\n        if num_pos > 0:\n            gt_masks_th = (\n                gt_instances.masks.to_tensor(rois.dtype,\n                                             rois.device).index_select(\n                                                 0, pos_assigned_gt_inds))\n            gt_masks_th = gt_masks_th.unsqueeze(1)\n            rel_img_points = rel_roi_point_to_rel_img_point(\n                rois, rel_roi_points, gt_masks_th)\n            point_targets = point_sample(gt_masks_th,\n                                         rel_img_points).squeeze(1)\n        else:\n            point_targets = rois.new_zeros((0, num_points))\n        return point_targets\n\n    def loss_and_target(self, point_pred: Tensor, rel_roi_points: Tensor,\n                        sampling_results: List[SamplingResult],\n                        batch_gt_instances: InstanceList,\n                        cfg: ConfigType) -> dict:\n        \"\"\"Calculate loss for MaskPointHead.\n\n        Args:\n            point_pred (Tensor): Point predication result, shape\n                (num_rois, num_classes, num_points).\n            rel_roi_points (Tensor): Points coordinates relative to RoI, shape\n                (num_rois, num_points, 2).\n             sampling_results (:obj:`SamplingResult`): Sampling result after\n                sampling and assignment.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            cfg (obj:`ConfigDict` or dict): Training cfg.\n\n        Returns:\n            dict: a dictionary of point loss and point target.\n        \"\"\"\n        rois = bbox2roi([res.pos_bboxes for res in sampling_results])\n        pos_labels = torch.cat([res.pos_gt_labels for res in sampling_results])\n\n        point_target = self.get_targets(rois, rel_roi_points, sampling_results,\n                                        batch_gt_instances, cfg)\n        if self.class_agnostic:\n            loss_point = self.loss_point(point_pred, point_target,\n                                         torch.zeros_like(pos_labels))\n        else:\n            loss_point = self.loss_point(point_pred, point_target, pos_labels)\n\n        return dict(loss_point=loss_point, point_target=point_target)\n\n    def get_roi_rel_points_train(self, mask_preds: Tensor, labels: Tensor,\n                                 cfg: ConfigType) -> Tensor:\n        \"\"\"Get ``num_points`` most uncertain points with random points during\n        train.\n\n        Sample points in [0, 1] x [0, 1] coordinate space based on their\n        uncertainty. The uncertainties are calculated for each point using\n        '_get_uncertainty()' function that takes point's logit prediction as\n        input.\n\n        Args:\n            mask_preds (Tensor): A tensor of shape (num_rois, num_classes,\n                mask_height, mask_width) for class-specific or class-agnostic\n                prediction.\n            labels (Tensor): The ground truth class for each instance.\n            cfg (:obj:`ConfigDict` or dict): Training config of point head.\n\n        Returns:\n            point_coords (Tensor): A tensor of shape (num_rois, num_points, 2)\n            that contains the coordinates sampled points.\n        \"\"\"\n        point_coords = get_uncertain_point_coords_with_randomness(\n            mask_preds, labels, cfg.num_points, cfg.oversample_ratio,\n            cfg.importance_sample_ratio)\n        return point_coords\n\n    def get_roi_rel_points_test(self, mask_preds: Tensor, label_preds: Tensor,\n                                cfg: ConfigType) -> Tuple[Tensor, Tensor]:\n        \"\"\"Get ``num_points`` most uncertain points during test.\n\n        Args:\n            mask_preds (Tensor): A tensor of shape (num_rois, num_classes,\n                mask_height, mask_width) for class-specific or class-agnostic\n                prediction.\n            label_preds (Tensor): The predication class for each instance.\n            cfg (:obj:`ConfigDict` or dict): Testing config of point head.\n\n        Returns:\n            tuple:\n\n            - point_indices (Tensor): A tensor of shape (num_rois, num_points)\n              that contains indices from [0, mask_height x mask_width) of the\n              most uncertain points.\n            - point_coords (Tensor): A tensor of shape (num_rois, num_points,\n              2) that contains [0, 1] x [0, 1] normalized coordinates of the\n              most uncertain points from the [mask_height, mask_width] grid.\n        \"\"\"\n        num_points = cfg.subdivision_num_points\n        uncertainty_map = get_uncertainty(mask_preds, label_preds)\n        num_rois, _, mask_height, mask_width = uncertainty_map.shape\n\n        # During ONNX exporting, the type of each elements of 'shape' is\n        # `Tensor(float)`, while it is `float` during PyTorch inference.\n        if isinstance(mask_height, torch.Tensor):\n            h_step = 1.0 / mask_height.float()\n            w_step = 1.0 / mask_width.float()\n        else:\n            h_step = 1.0 / mask_height\n            w_step = 1.0 / mask_width\n        # cast to int to avoid dynamic K for TopK op in ONNX\n        mask_size = int(mask_height * mask_width)\n        uncertainty_map = uncertainty_map.view(num_rois, mask_size)\n        num_points = min(mask_size, num_points)\n        point_indices = uncertainty_map.topk(num_points, dim=1)[1]\n        xs = w_step / 2.0 + (point_indices % mask_width).float() * w_step\n        ys = h_step / 2.0 + (point_indices // mask_width).float() * h_step\n        point_coords = torch.stack([xs, ys], dim=2)\n        return point_indices, point_coords\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/maskiou_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import Conv2d, Linear, MaxPool2d\nfrom mmengine.config import ConfigDict\nfrom mmengine.model import BaseModule\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, InstanceList, OptMultiConfig\n\n\n@MODELS.register_module()\nclass MaskIoUHead(BaseModule):\n    \"\"\"Mask IoU Head.\n\n    This head predicts the IoU of predicted masks and corresponding gt masks.\n\n    Args:\n        num_convs (int): The number of convolution layers. Defaults to 4.\n        num_fcs (int): The number of fully connected layers. Defaults to 2.\n        roi_feat_size (int): RoI feature size. Default to 14.\n        in_channels (int): The channel number of inputs features.\n            Defaults to 256.\n        conv_out_channels (int): The feature channels of convolution layers.\n            Defaults to 256.\n        fc_out_channels (int): The feature channels of fully connected layers.\n            Defaults to 1024.\n        num_classes (int): Number of categories excluding the background\n            category. Defaults to 80.\n        loss_iou (:obj:`ConfigDict` or dict): IoU loss.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_convs: int = 4,\n        num_fcs: int = 2,\n        roi_feat_size: int = 14,\n        in_channels: int = 256,\n        conv_out_channels: int = 256,\n        fc_out_channels: int = 1024,\n        num_classes: int = 80,\n        loss_iou: ConfigType = dict(type='MSELoss', loss_weight=0.5),\n        init_cfg: OptMultiConfig = [\n            dict(type='Kaiming', override=dict(name='convs')),\n            dict(type='Caffe2Xavier', override=dict(name='fcs')),\n            dict(type='Normal', std=0.01, override=dict(name='fc_mask_iou'))\n        ]\n    ) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.in_channels = in_channels\n        self.conv_out_channels = conv_out_channels\n        self.fc_out_channels = fc_out_channels\n        self.num_classes = num_classes\n\n        self.convs = nn.ModuleList()\n        for i in range(num_convs):\n            if i == 0:\n                # concatenation of mask feature and mask prediction\n                in_channels = self.in_channels + 1\n            else:\n                in_channels = self.conv_out_channels\n            stride = 2 if i == num_convs - 1 else 1\n            self.convs.append(\n                Conv2d(\n                    in_channels,\n                    self.conv_out_channels,\n                    3,\n                    stride=stride,\n                    padding=1))\n\n        roi_feat_size = _pair(roi_feat_size)\n        pooled_area = (roi_feat_size[0] // 2) * (roi_feat_size[1] // 2)\n        self.fcs = nn.ModuleList()\n        for i in range(num_fcs):\n            in_channels = (\n                self.conv_out_channels *\n                pooled_area if i == 0 else self.fc_out_channels)\n            self.fcs.append(Linear(in_channels, self.fc_out_channels))\n\n        self.fc_mask_iou = Linear(self.fc_out_channels, self.num_classes)\n        self.relu = nn.ReLU()\n        self.max_pool = MaxPool2d(2, 2)\n        self.loss_iou = MODELS.build(loss_iou)\n\n    def forward(self, mask_feat: Tensor, mask_preds: Tensor) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            mask_feat (Tensor): Mask features from upstream models.\n            mask_preds (Tensor): Mask predictions from mask head.\n\n        Returns:\n            Tensor: Mask IoU predictions.\n        \"\"\"\n        mask_preds = mask_preds.sigmoid()\n        mask_pred_pooled = self.max_pool(mask_preds.unsqueeze(1))\n\n        x = torch.cat((mask_feat, mask_pred_pooled), 1)\n\n        for conv in self.convs:\n            x = self.relu(conv(x))\n        x = x.flatten(1)\n        for fc in self.fcs:\n            x = self.relu(fc(x))\n        mask_iou = self.fc_mask_iou(x)\n        return mask_iou\n\n    def loss_and_target(self, mask_iou_pred: Tensor, mask_preds: Tensor,\n                        mask_targets: Tensor,\n                        sampling_results: List[SamplingResult],\n                        batch_gt_instances: InstanceList,\n                        rcnn_train_cfg: ConfigDict) -> dict:\n        \"\"\"Calculate the loss and targets of MaskIoUHead.\n\n        Args:\n            mask_iou_pred (Tensor): Mask IoU predictions results, has shape\n                (num_pos, num_classes)\n            mask_preds (Tensor): Mask predictions from mask head, has shape\n                (num_pos, mask_size, mask_size).\n            mask_targets (Tensor): The ground truth masks assigned with\n                predictions, has shape\n                (num_pos, mask_size, mask_size).\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It includes ``masks`` inside.\n            rcnn_train_cfg (obj:`ConfigDict`): `train_cfg` of RCNN.\n\n        Returns:\n            dict: A dictionary of loss and targets components.\n                The targets are only used for cascade rcnn.\n        \"\"\"\n        mask_iou_targets = self.get_targets(\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            mask_preds=mask_preds,\n            mask_targets=mask_targets,\n            rcnn_train_cfg=rcnn_train_cfg)\n\n        pos_inds = mask_iou_targets > 0\n        if pos_inds.sum() > 0:\n            loss_mask_iou = self.loss_iou(mask_iou_pred[pos_inds],\n                                          mask_iou_targets[pos_inds])\n        else:\n            loss_mask_iou = mask_iou_pred.sum() * 0\n        return dict(loss_mask_iou=loss_mask_iou)\n\n    def get_targets(self, sampling_results: List[SamplingResult],\n                    batch_gt_instances: InstanceList, mask_preds: Tensor,\n                    mask_targets: Tensor,\n                    rcnn_train_cfg: ConfigDict) -> Tensor:\n        \"\"\"Compute target of mask IoU.\n\n        Mask IoU target is the IoU of the predicted mask (inside a bbox) and\n        the gt mask of corresponding gt mask (the whole instance).\n        The intersection area is computed inside the bbox, and the gt mask area\n        is computed with two steps, firstly we compute the gt area inside the\n        bbox, then divide it by the area ratio of gt area inside the bbox and\n        the gt area of the whole instance.\n\n        Args:\n            sampling_results (list[:obj:`SamplingResult`]): sampling results.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It includes ``masks`` inside.\n            mask_preds (Tensor): Predicted masks of each positive proposal,\n                shape (num_pos, h, w).\n            mask_targets (Tensor): Gt mask of each positive proposal,\n                binary map of the shape (num_pos, h, w).\n            rcnn_train_cfg (obj:`ConfigDict`): Training config for R-CNN part.\n\n        Returns:\n            Tensor: mask iou target (length == num positive).\n        \"\"\"\n        pos_proposals = [res.pos_priors for res in sampling_results]\n        pos_assigned_gt_inds = [\n            res.pos_assigned_gt_inds for res in sampling_results\n        ]\n        gt_masks = [res.masks for res in batch_gt_instances]\n\n        # compute the area ratio of gt areas inside the proposals and\n        # the whole instance\n        area_ratios = map(self._get_area_ratio, pos_proposals,\n                          pos_assigned_gt_inds, gt_masks)\n        area_ratios = torch.cat(list(area_ratios))\n        assert mask_targets.size(0) == area_ratios.size(0)\n\n        mask_preds = (mask_preds > rcnn_train_cfg.mask_thr_binary).float()\n        mask_pred_areas = mask_preds.sum((-1, -2))\n\n        # mask_preds and mask_targets are binary maps\n        overlap_areas = (mask_preds * mask_targets).sum((-1, -2))\n\n        # compute the mask area of the whole instance\n        gt_full_areas = mask_targets.sum((-1, -2)) / (area_ratios + 1e-7)\n\n        mask_iou_targets = overlap_areas / (\n            mask_pred_areas + gt_full_areas - overlap_areas)\n        return mask_iou_targets\n\n    def _get_area_ratio(self, pos_proposals: Tensor,\n                        pos_assigned_gt_inds: Tensor,\n                        gt_masks: InstanceData) -> Tensor:\n        \"\"\"Compute area ratio of the gt mask inside the proposal and the gt\n        mask of the corresponding instance.\n\n        Args:\n            pos_proposals (Tensor): Positive proposals, has shape (num_pos, 4).\n            pos_assigned_gt_inds (Tensor): positive proposals assigned ground\n                truth index.\n            gt_masks (BitmapMask or PolygonMask): Gt masks (the whole instance)\n                of each image, with the same shape of the input image.\n\n        Returns:\n            Tensor: The area ratio of the gt mask inside the proposal and the\n            gt mask of the corresponding instance.\n        \"\"\"\n        num_pos = pos_proposals.size(0)\n        if num_pos > 0:\n            area_ratios = []\n            proposals_np = pos_proposals.cpu().numpy()\n            pos_assigned_gt_inds = pos_assigned_gt_inds.cpu().numpy()\n            # compute mask areas of gt instances (batch processing for speedup)\n            gt_instance_mask_area = gt_masks.areas\n            for i in range(num_pos):\n                gt_mask = gt_masks[pos_assigned_gt_inds[i]]\n\n                # crop the gt mask inside the proposal\n                bbox = proposals_np[i, :].astype(np.int32)\n                gt_mask_in_proposal = gt_mask.crop(bbox)\n\n                ratio = gt_mask_in_proposal.areas[0] / (\n                    gt_instance_mask_area[pos_assigned_gt_inds[i]] + 1e-7)\n                area_ratios.append(ratio)\n            area_ratios = torch.from_numpy(np.stack(area_ratios)).float().to(\n                pos_proposals.device)\n        else:\n            area_ratios = pos_proposals.new_zeros((0, ))\n        return area_ratios\n\n    def predict_by_feat(self, mask_iou_preds: Tuple[Tensor],\n                        results_list: InstanceList) -> InstanceList:\n        \"\"\"Predict the mask iou and calculate it into ``results.scores``.\n\n        Args:\n            mask_iou_preds (Tensor): Mask IoU predictions results, has shape\n                (num_proposals, num_classes)\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process. Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        assert len(mask_iou_preds) == len(results_list)\n        for results, mask_iou_pred in zip(results_list, mask_iou_preds):\n            labels = results.labels\n            scores = results.scores\n            results.scores = scores * mask_iou_pred[range(labels.size(0)),\n                                                    labels]\n        return results_list\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/scnet_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.models.layers import ResLayer, SimplifiedBasicBlock\nfrom mmdet.registry import MODELS\nfrom .fcn_mask_head import FCNMaskHead\n\n\n@MODELS.register_module()\nclass SCNetMaskHead(FCNMaskHead):\n    \"\"\"Mask head for `SCNet <https://arxiv.org/abs/2012.10150>`_.\n\n    Args:\n        conv_to_res (bool, optional): if True, change the conv layers to\n            ``SimplifiedBasicBlock``.\n    \"\"\"\n\n    def __init__(self, conv_to_res: bool = True, **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.conv_to_res = conv_to_res\n        if conv_to_res:\n            assert self.conv_kernel_size == 3\n            self.num_res_blocks = self.num_convs // 2\n            self.convs = ResLayer(\n                SimplifiedBasicBlock,\n                self.in_channels,\n                self.conv_out_channels,\n                self.num_res_blocks,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg)\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_heads/scnet_semantic_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.models.layers import ResLayer, SimplifiedBasicBlock\nfrom mmdet.registry import MODELS\nfrom .fused_semantic_head import FusedSemanticHead\n\n\n@MODELS.register_module()\nclass SCNetSemanticHead(FusedSemanticHead):\n    \"\"\"Mask head for `SCNet <https://arxiv.org/abs/2012.10150>`_.\n\n    Args:\n        conv_to_res (bool, optional): if True, change the conv layers to\n            ``SimplifiedBasicBlock``.\n    \"\"\"\n\n    def __init__(self, conv_to_res: bool = True, **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.conv_to_res = conv_to_res\n        if self.conv_to_res:\n            num_res_blocks = self.num_convs // 2\n            self.convs = ResLayer(\n                SimplifiedBasicBlock,\n                self.in_channels,\n                self.conv_out_channels,\n                num_res_blocks,\n                conv_cfg=self.conv_cfg,\n                norm_cfg=self.norm_cfg)\n            self.num_convs = num_res_blocks\n"
  },
  {
    "path": "mmdet/models/roi_heads/mask_scoring_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils.misc import empty_instances\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass MaskScoringRoIHead(StandardRoIHead):\n    \"\"\"Mask Scoring RoIHead for `Mask Scoring RCNN.\n\n    <https://arxiv.org/abs/1903.00241>`_.\n\n    Args:\n        mask_iou_head (:obj`ConfigDict`, dict): The config of mask_iou_head.\n    \"\"\"\n\n    def __init__(self, mask_iou_head: ConfigType, **kwargs):\n        assert mask_iou_head is not None\n        super().__init__(**kwargs)\n        self.mask_iou_head = MODELS.build(mask_iou_head)\n\n    def forward(self,\n                x: Tuple[Tensor],\n                rpn_results_list: InstanceList,\n                batch_data_samples: SampleList = None) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            x (List[Tensor]): Multi-level features that may have different\n                resolutions.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n            the meta information of each image and corresponding\n            annotations.\n\n        Returns\n            tuple: A tuple of features from ``bbox_head`` and ``mask_head``\n            forward.\n        \"\"\"\n        results = ()\n        proposals = [rpn_results.bboxes for rpn_results in rpn_results_list]\n        rois = bbox2roi(proposals)\n        # bbox head\n        if self.with_bbox:\n            bbox_results = self._bbox_forward(x, rois)\n            results = results + (bbox_results['cls_score'],\n                                 bbox_results['bbox_pred'])\n        # mask head\n        if self.with_mask:\n            mask_rois = rois[:100]\n            mask_results = self._mask_forward(x, mask_rois)\n            results = results + (mask_results['mask_preds'], )\n\n            # mask iou head\n            cls_score = bbox_results['cls_score'][:100]\n            mask_preds = mask_results['mask_preds']\n            mask_feats = mask_results['mask_feats']\n            _, labels = cls_score[:, :self.bbox_head.num_classes].max(dim=1)\n            mask_iou_preds = self.mask_iou_head(\n                mask_feats, mask_preds[range(labels.size(0)), labels])\n            results = results + (mask_iou_preds, )\n\n        return results\n\n    def mask_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult], bbox_feats,\n                  batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the mask head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            bbox_feats (Tensor): Extract bbox RoI features.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `mask_feats` (Tensor): Extract mask RoI features.\n                - `mask_targets` (Tensor): Mask target of each positive\\\n                    proposals in the image.\n                - `loss_mask` (dict): A dictionary of mask loss components.\n                - `loss_mask_iou` (Tensor): mask iou loss.\n        \"\"\"\n        if not self.share_roi_extractor:\n            pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n            mask_results = self._mask_forward(x, pos_rois)\n        else:\n            pos_inds = []\n            device = bbox_feats.device\n            for res in sampling_results:\n                pos_inds.append(\n                    torch.ones(\n                        res.pos_priors.shape[0],\n                        device=device,\n                        dtype=torch.uint8))\n                pos_inds.append(\n                    torch.zeros(\n                        res.neg_priors.shape[0],\n                        device=device,\n                        dtype=torch.uint8))\n            pos_inds = torch.cat(pos_inds)\n\n            mask_results = self._mask_forward(\n                x, pos_inds=pos_inds, bbox_feats=bbox_feats)\n\n        mask_loss_and_target = self.mask_head.loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=self.train_cfg)\n        mask_targets = mask_loss_and_target['mask_targets']\n        mask_results.update(loss_mask=mask_loss_and_target['loss_mask'])\n        if mask_results['loss_mask'] is None:\n            return mask_results\n\n        # mask iou head forward and loss\n        pos_labels = torch.cat([res.pos_gt_labels for res in sampling_results])\n        pos_mask_pred = mask_results['mask_preds'][\n            range(mask_results['mask_preds'].size(0)), pos_labels]\n        mask_iou_pred = self.mask_iou_head(mask_results['mask_feats'],\n                                           pos_mask_pred)\n        pos_mask_iou_pred = mask_iou_pred[range(mask_iou_pred.size(0)),\n                                          pos_labels]\n\n        loss_mask_iou = self.mask_iou_head.loss_and_target(\n            pos_mask_iou_pred, pos_mask_pred, mask_targets, sampling_results,\n            batch_gt_instances, self.train_cfg)\n        mask_results['loss_mask'].update(loss_mask_iou)\n        return mask_results\n\n    def predict_mask(self,\n                     x: Tensor,\n                     batch_img_metas: List[dict],\n                     results_list: InstanceList,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas,\n                mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        mask_results = self._mask_forward(x, mask_rois)\n        mask_preds = mask_results['mask_preds']\n        mask_feats = mask_results['mask_feats']\n        # get mask scores with mask iou head\n        labels = torch.cat([res.labels for res in results_list])\n        mask_iou_preds = self.mask_iou_head(\n            mask_feats, mask_preds[range(labels.size(0)), labels])\n        # split batch mask prediction back to each image\n        num_mask_rois_per_img = [len(res) for res in results_list]\n        mask_preds = mask_preds.split(num_mask_rois_per_img, 0)\n        mask_iou_preds = mask_iou_preds.split(num_mask_rois_per_img, 0)\n\n        # TODO: Handle the case where rescale is false\n        results_list = self.mask_head.predict_by_feat(\n            mask_preds=mask_preds,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale)\n        results_list = self.mask_iou_head.predict_by_feat(\n            mask_iou_preds=mask_iou_preds, results_list=results_list)\n        return results_list\n"
  },
  {
    "path": "mmdet/models/roi_heads/multi_instance_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils import empty_instances, unpack_gt_instances\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass MultiInstanceRoIHead(StandardRoIHead):\n    \"\"\"The roi head for Multi-instance prediction.\"\"\"\n\n    def __init__(self, num_instance: int = 2, *args, **kwargs) -> None:\n        self.num_instance = num_instance\n        super().__init__(*args, **kwargs)\n\n    def init_bbox_head(self, bbox_roi_extractor: ConfigType,\n                       bbox_head: ConfigType) -> None:\n        \"\"\"Initialize box head and box roi extractor.\n\n        Args:\n            bbox_roi_extractor (dict or ConfigDict): Config of box\n                roi extractor.\n            bbox_head (dict or ConfigDict): Config of box in box head.\n        \"\"\"\n        self.bbox_roi_extractor = MODELS.build(bbox_roi_extractor)\n        self.bbox_head = MODELS.build(bbox_head)\n\n    def _bbox_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `cls_score_ref` (Tensor): The cls_score after refine model.\n                - `bbox_pred_ref` (Tensor): The bbox_pred after refine model.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        # TODO: a more flexible way to decide which feature maps to use\n        bbox_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs], rois)\n        bbox_results = self.bbox_head(bbox_feats)\n\n        if self.bbox_head.with_refine:\n            bbox_results = dict(\n                cls_score=bbox_results[0],\n                bbox_pred=bbox_results[1],\n                cls_score_ref=bbox_results[2],\n                bbox_pred_ref=bbox_results[3],\n                bbox_feats=bbox_feats)\n        else:\n            bbox_results = dict(\n                cls_score=bbox_results[0],\n                bbox_pred=bbox_results[1],\n                bbox_feats=bbox_feats)\n\n        return bbox_results\n\n    def bbox_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the bbox head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n        \"\"\"\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(x, rois)\n\n        # If there is a refining process, add refine loss.\n        if 'cls_score_ref' in bbox_results:\n            bbox_loss_and_target = self.bbox_head.loss_and_target(\n                cls_score=bbox_results['cls_score'],\n                bbox_pred=bbox_results['bbox_pred'],\n                rois=rois,\n                sampling_results=sampling_results,\n                rcnn_train_cfg=self.train_cfg)\n            bbox_results.update(loss_bbox=bbox_loss_and_target['loss_bbox'])\n            bbox_loss_and_target_ref = self.bbox_head.loss_and_target(\n                cls_score=bbox_results['cls_score_ref'],\n                bbox_pred=bbox_results['bbox_pred_ref'],\n                rois=rois,\n                sampling_results=sampling_results,\n                rcnn_train_cfg=self.train_cfg)\n            bbox_results['loss_bbox']['loss_rcnn_emd_ref'] = \\\n                bbox_loss_and_target_ref['loss_bbox']['loss_rcnn_emd']\n        else:\n            bbox_loss_and_target = self.bbox_head.loss_and_target(\n                cls_score=bbox_results['cls_score'],\n                bbox_pred=bbox_results['bbox_pred'],\n                rois=rois,\n                sampling_results=sampling_results,\n                rcnn_train_cfg=self.train_cfg)\n            bbox_results.update(loss_bbox=bbox_loss_and_target['loss_bbox'])\n\n        return bbox_results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: List[DetDataSample]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, _ = outputs\n\n        sampling_results = []\n        for i in range(len(batch_data_samples)):\n            # rename rpn_results.bboxes to rpn_results.priors\n            rpn_results = rpn_results_list[i]\n            rpn_results.priors = rpn_results.pop('bboxes')\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                batch_gt_instances_ignore=batch_gt_instances_ignore[i])\n            sampling_results.append(sampling_result)\n\n        losses = dict()\n        # bbox head loss\n        if self.with_bbox:\n            bbox_results = self.bbox_loss(x, sampling_results)\n            losses.update(bbox_results['loss_bbox'])\n\n        return losses\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the bbox head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of R-CNN.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        proposals = [res.bboxes for res in rpn_results_list]\n        rois = bbox2roi(proposals)\n\n        if rois.shape[0] == 0:\n            return empty_instances(\n                batch_img_metas, rois.device, task_type='bbox')\n\n        bbox_results = self._bbox_forward(x, rois)\n\n        # split batch bbox prediction back to each image\n        if 'cls_score_ref' in bbox_results:\n            cls_scores = bbox_results['cls_score_ref']\n            bbox_preds = bbox_results['bbox_pred_ref']\n        else:\n            cls_scores = bbox_results['cls_score']\n            bbox_preds = bbox_results['bbox_pred']\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = rois.split(num_proposals_per_img, 0)\n        cls_scores = cls_scores.split(num_proposals_per_img, 0)\n\n        if bbox_preds is not None:\n            bbox_preds = bbox_preds.split(num_proposals_per_img, 0)\n        else:\n            bbox_preds = (None, ) * len(proposals)\n\n        result_list = self.bbox_head.predict_by_feat(\n            rois=rois,\n            cls_scores=cls_scores,\n            bbox_preds=bbox_preds,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=rcnn_test_cfg,\n            rescale=rescale)\n        return result_list\n"
  },
  {
    "path": "mmdet/models/roi_heads/pisa_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules import SamplingResult\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import InstanceList\nfrom ..losses.pisa_loss import carl_loss, isr_p\nfrom ..utils import unpack_gt_instances\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass PISARoIHead(StandardRoIHead):\n    r\"\"\"The RoI head for `Prime Sample Attention in Object Detection\n    <https://arxiv.org/abs/1904.04821>`_.\"\"\"\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: List[DetDataSample]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, _ = outputs\n\n        # assign gts and sample proposals\n        num_imgs = len(batch_data_samples)\n        sampling_results = []\n        neg_label_weights = []\n        for i in range(num_imgs):\n            # rename rpn_results.bboxes to rpn_results.priors\n            rpn_results = rpn_results_list[i]\n            rpn_results.priors = rpn_results.pop('bboxes')\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in x])\n            if isinstance(sampling_result, tuple):\n                sampling_result, neg_label_weight = sampling_result\n            sampling_results.append(sampling_result)\n            neg_label_weights.append(neg_label_weight)\n\n        losses = dict()\n        # bbox head forward and loss\n        if self.with_bbox:\n            bbox_results = self.bbox_loss(\n                x, sampling_results, neg_label_weights=neg_label_weights)\n            losses.update(bbox_results['loss_bbox'])\n\n        # mask head forward and loss\n        if self.with_mask:\n            mask_results = self.mask_loss(x, sampling_results,\n                                          bbox_results['bbox_feats'],\n                                          batch_gt_instances)\n            losses.update(mask_results['loss_mask'])\n\n        return losses\n\n    def bbox_loss(self,\n                  x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult],\n                  neg_label_weights: List[Tensor] = None) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the bbox head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n            - `cls_score` (Tensor): Classification scores.\n            - `bbox_pred` (Tensor): Box energies / deltas.\n            - `bbox_feats` (Tensor): Extract bbox RoI features.\n            - `loss_bbox` (dict): A dictionary of bbox loss components.\n        \"\"\"\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(x, rois)\n        bbox_targets = self.bbox_head.get_targets(sampling_results,\n                                                  self.train_cfg)\n\n        # neg_label_weights obtained by sampler is image-wise, mapping back to\n        # the corresponding location in label weights\n        if neg_label_weights[0] is not None:\n            label_weights = bbox_targets[1]\n            cur_num_rois = 0\n            for i in range(len(sampling_results)):\n                num_pos = sampling_results[i].pos_inds.size(0)\n                num_neg = sampling_results[i].neg_inds.size(0)\n                label_weights[cur_num_rois + num_pos:cur_num_rois + num_pos +\n                              num_neg] = neg_label_weights[i]\n                cur_num_rois += num_pos + num_neg\n\n        cls_score = bbox_results['cls_score']\n        bbox_pred = bbox_results['bbox_pred']\n\n        # Apply ISR-P\n        isr_cfg = self.train_cfg.get('isr', None)\n        if isr_cfg is not None:\n            bbox_targets = isr_p(\n                cls_score,\n                bbox_pred,\n                bbox_targets,\n                rois,\n                sampling_results,\n                self.bbox_head.loss_cls,\n                self.bbox_head.bbox_coder,\n                **isr_cfg,\n                num_class=self.bbox_head.num_classes)\n        loss_bbox = self.bbox_head.loss(cls_score, bbox_pred, rois,\n                                        *bbox_targets)\n\n        # Add CARL Loss\n        carl_cfg = self.train_cfg.get('carl', None)\n        if carl_cfg is not None:\n            loss_carl = carl_loss(\n                cls_score,\n                bbox_targets[0],\n                bbox_pred,\n                bbox_targets[2],\n                self.bbox_head.loss_bbox,\n                **carl_cfg,\n                num_class=self.bbox_head.num_classes)\n            loss_bbox.update(loss_carl)\n\n        bbox_results.update(loss_bbox=loss_bbox)\n        return bbox_results\n"
  },
  {
    "path": "mmdet/models/roi_heads/point_rend_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend  # noqa\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmcv.ops import point_sample, rel_roi_point_to_rel_img_point\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils import empty_instances\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass PointRendRoIHead(StandardRoIHead):\n    \"\"\"`PointRend <https://arxiv.org/abs/1912.08193>`_.\"\"\"\n\n    def __init__(self, point_head: ConfigType, *args, **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        assert self.with_bbox and self.with_mask\n        self.init_point_head(point_head)\n\n    def init_point_head(self, point_head: ConfigType) -> None:\n        \"\"\"Initialize ``point_head``\"\"\"\n        self.point_head = MODELS.build(point_head)\n\n    def mask_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult], bbox_feats: Tensor,\n                  batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Run forward function and calculate loss for mask head and point head\n        in training.\"\"\"\n        mask_results = super().mask_loss(\n            x=x,\n            sampling_results=sampling_results,\n            bbox_feats=bbox_feats,\n            batch_gt_instances=batch_gt_instances)\n\n        mask_point_results = self._mask_point_loss(\n            x=x,\n            sampling_results=sampling_results,\n            mask_preds=mask_results['mask_preds'],\n            batch_gt_instances=batch_gt_instances)\n        mask_results['loss_mask'].update(\n            loss_point=mask_point_results['loss_point'])\n\n        return mask_results\n\n    def _mask_point_loss(self, x: Tuple[Tensor],\n                         sampling_results: List[SamplingResult],\n                         mask_preds: Tensor,\n                         batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Run forward function and calculate loss for point head in\n        training.\"\"\"\n        pos_labels = torch.cat([res.pos_gt_labels for res in sampling_results])\n        rel_roi_points = self.point_head.get_roi_rel_points_train(\n            mask_preds, pos_labels, cfg=self.train_cfg)\n        rois = bbox2roi([res.pos_bboxes for res in sampling_results])\n\n        fine_grained_point_feats = self._get_fine_grained_point_feats(\n            x, rois, rel_roi_points)\n        coarse_point_feats = point_sample(mask_preds, rel_roi_points)\n        mask_point_pred = self.point_head(fine_grained_point_feats,\n                                          coarse_point_feats)\n\n        loss_and_target = self.point_head.loss_and_target(\n            point_pred=mask_point_pred,\n            rel_roi_points=rel_roi_points,\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            cfg=self.train_cfg)\n\n        return loss_and_target\n\n    def _mask_point_forward_test(self, x: Tuple[Tensor], rois: Tensor,\n                                 label_preds: Tensor,\n                                 mask_preds: Tensor) -> Tensor:\n        \"\"\"Mask refining process with point head in testing.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            rois (Tensor): shape (num_rois, 5).\n            label_preds (Tensor): The predication class for each rois.\n            mask_preds (Tensor): The predication coarse masks of\n                shape (num_rois, num_classes, small_size, small_size).\n\n        Returns:\n            Tensor: The refined masks of shape (num_rois, num_classes,\n            large_size, large_size).\n        \"\"\"\n        refined_mask_pred = mask_preds.clone()\n        for subdivision_step in range(self.test_cfg.subdivision_steps):\n            refined_mask_pred = F.interpolate(\n                refined_mask_pred,\n                scale_factor=self.test_cfg.scale_factor,\n                mode='bilinear',\n                align_corners=False)\n            # If `subdivision_num_points` is larger or equal to the\n            # resolution of the next step, then we can skip this step\n            num_rois, channels, mask_height, mask_width = \\\n                refined_mask_pred.shape\n            if (self.test_cfg.subdivision_num_points >=\n                    self.test_cfg.scale_factor**2 * mask_height * mask_width\n                    and\n                    subdivision_step < self.test_cfg.subdivision_steps - 1):\n                continue\n            point_indices, rel_roi_points = \\\n                self.point_head.get_roi_rel_points_test(\n                    refined_mask_pred, label_preds, cfg=self.test_cfg)\n\n            fine_grained_point_feats = self._get_fine_grained_point_feats(\n                x=x, rois=rois, rel_roi_points=rel_roi_points)\n            coarse_point_feats = point_sample(mask_preds, rel_roi_points)\n            mask_point_pred = self.point_head(fine_grained_point_feats,\n                                              coarse_point_feats)\n\n            point_indices = point_indices.unsqueeze(1).expand(-1, channels, -1)\n            refined_mask_pred = refined_mask_pred.reshape(\n                num_rois, channels, mask_height * mask_width)\n            refined_mask_pred = refined_mask_pred.scatter_(\n                2, point_indices, mask_point_pred)\n            refined_mask_pred = refined_mask_pred.view(num_rois, channels,\n                                                       mask_height, mask_width)\n\n        return refined_mask_pred\n\n    def _get_fine_grained_point_feats(self, x: Tuple[Tensor], rois: Tensor,\n                                      rel_roi_points: Tensor) -> Tensor:\n        \"\"\"Sample fine grained feats from each level feature map and\n        concatenate them together.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            rois (Tensor): shape (num_rois, 5).\n            rel_roi_points (Tensor): A tensor of shape (num_rois, num_points,\n                2) that contains [0, 1] x [0, 1] normalized coordinates of the\n                most uncertain points from the [mask_height, mask_width] grid.\n\n        Returns:\n            Tensor: The fine grained features for each points,\n            has shape (num_rois, feats_channels, num_points).\n        \"\"\"\n        assert rois.shape[0] > 0, 'RoI is a empty tensor.'\n        num_imgs = x[0].shape[0]\n        fine_grained_feats = []\n        for idx in range(self.mask_roi_extractor.num_inputs):\n            feats = x[idx]\n            spatial_scale = 1. / float(\n                self.mask_roi_extractor.featmap_strides[idx])\n            point_feats = []\n            for batch_ind in range(num_imgs):\n                # unravel batch dim\n                feat = feats[batch_ind].unsqueeze(0)\n                inds = (rois[:, 0].long() == batch_ind)\n                if inds.any():\n                    rel_img_points = rel_roi_point_to_rel_img_point(\n                        rois=rois[inds],\n                        rel_roi_points=rel_roi_points[inds],\n                        img=feat.shape[2:],\n                        spatial_scale=spatial_scale).unsqueeze(0)\n                    point_feat = point_sample(feat, rel_img_points)\n                    point_feat = point_feat.squeeze(0).transpose(0, 1)\n                    point_feats.append(point_feat)\n            fine_grained_feats.append(torch.cat(point_feats, dim=0))\n        return torch.cat(fine_grained_feats, dim=1)\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: InstanceList,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n            - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        # don't need to consider aug_test.\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas,\n                mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        mask_results = self._mask_forward(x, mask_rois)\n        mask_preds = mask_results['mask_preds']\n        # split batch mask prediction back to each image\n        num_mask_rois_per_img = [len(res) for res in results_list]\n        mask_preds = mask_preds.split(num_mask_rois_per_img, 0)\n\n        # refine mask_preds\n        mask_rois = mask_rois.split(num_mask_rois_per_img, 0)\n        mask_preds_refined = []\n        for i in range(len(batch_img_metas)):\n            labels = results_list[i].labels\n            x_i = [xx[[i]] for xx in x]\n            mask_rois_i = mask_rois[i]\n            mask_rois_i[:, 0] = 0\n            mask_pred_i = self._mask_point_forward_test(\n                x_i, mask_rois_i, labels, mask_preds[i])\n            mask_preds_refined.append(mask_pred_i)\n\n        # TODO: Handle the case where rescale is false\n        results_list = self.mask_head.predict_by_feat(\n            mask_preds=mask_preds_refined,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale)\n        return results_list\n"
  },
  {
    "path": "mmdet/models/roi_heads/roi_extractors/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_roi_extractor import BaseRoIExtractor\nfrom .generic_roi_extractor import GenericRoIExtractor\nfrom .single_level_roi_extractor import SingleRoIExtractor\n\n__all__ = ['BaseRoIExtractor', 'SingleRoIExtractor', 'GenericRoIExtractor']\n"
  },
  {
    "path": "mmdet/models/roi_heads/roi_extractors/base_roi_extractor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv import ops\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.utils import ConfigType, OptMultiConfig\n\n\nclass BaseRoIExtractor(BaseModule, metaclass=ABCMeta):\n    \"\"\"Base class for RoI extractor.\n\n    Args:\n        roi_layer (:obj:`ConfigDict` or dict): Specify RoI layer type and\n            arguments.\n        out_channels (int): Output channels of RoI layers.\n        featmap_strides (list[int]): Strides of input feature maps.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 roi_layer: ConfigType,\n                 out_channels: int,\n                 featmap_strides: List[int],\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.roi_layers = self.build_roi_layers(roi_layer, featmap_strides)\n        self.out_channels = out_channels\n        self.featmap_strides = featmap_strides\n\n    @property\n    def num_inputs(self) -> int:\n        \"\"\"int: Number of input feature maps.\"\"\"\n        return len(self.featmap_strides)\n\n    def build_roi_layers(self, layer_cfg: ConfigType,\n                         featmap_strides: List[int]) -> nn.ModuleList:\n        \"\"\"Build RoI operator to extract feature from each level feature map.\n\n        Args:\n            layer_cfg (:obj:`ConfigDict` or dict): Dictionary to construct and\n                config RoI layer operation. Options are modules under\n                ``mmcv/ops`` such as ``RoIAlign``.\n            featmap_strides (list[int]): The stride of input feature map w.r.t\n                to the original image size, which would be used to scale RoI\n                coordinate (original image coordinate system) to feature\n                coordinate system.\n\n        Returns:\n            :obj:`nn.ModuleList`: The RoI extractor modules for each level\n                feature map.\n        \"\"\"\n\n        cfg = layer_cfg.copy()\n        layer_type = cfg.pop('type')\n        if isinstance(layer_type, str):\n            assert hasattr(ops, layer_type)\n            layer_cls = getattr(ops, layer_type)\n        else:\n            layer_cls = layer_type\n        roi_layers = nn.ModuleList(\n            [layer_cls(spatial_scale=1 / s, **cfg) for s in featmap_strides])\n        return roi_layers\n\n    def roi_rescale(self, rois: Tensor, scale_factor: float) -> Tensor:\n        \"\"\"Scale RoI coordinates by scale factor.\n\n        Args:\n            rois (Tensor): RoI (Region of Interest), shape (n, 5)\n            scale_factor (float): Scale factor that RoI will be multiplied by.\n\n        Returns:\n            Tensor: Scaled RoI.\n        \"\"\"\n\n        cx = (rois[:, 1] + rois[:, 3]) * 0.5\n        cy = (rois[:, 2] + rois[:, 4]) * 0.5\n        w = rois[:, 3] - rois[:, 1]\n        h = rois[:, 4] - rois[:, 2]\n        new_w = w * scale_factor\n        new_h = h * scale_factor\n        x1 = cx - new_w * 0.5\n        x2 = cx + new_w * 0.5\n        y1 = cy - new_h * 0.5\n        y2 = cy + new_h * 0.5\n        new_rois = torch.stack((rois[:, 0], x1, y1, x2, y2), dim=-1)\n        return new_rois\n\n    @abstractmethod\n    def forward(self,\n                feats: Tuple[Tensor],\n                rois: Tensor,\n                roi_scale_factor: Optional[float] = None) -> Tensor:\n        \"\"\"Extractor ROI feats.\n\n        Args:\n            feats (Tuple[Tensor]): Multi-scale features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            roi_scale_factor (Optional[float]): RoI scale factor.\n                Defaults to None.\n\n        Returns:\n            Tensor: RoI feature.\n        \"\"\"\n        pass\n"
  },
  {
    "path": "mmdet/models/roi_heads/roi_extractors/generic_roi_extractor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple\n\nfrom mmcv.cnn.bricks import build_plugin_layer\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType\nfrom .base_roi_extractor import BaseRoIExtractor\n\n\n@MODELS.register_module()\nclass GenericRoIExtractor(BaseRoIExtractor):\n    \"\"\"Extract RoI features from all level feature maps levels.\n\n    This is the implementation of `A novel Region of Interest Extraction Layer\n    for Instance Segmentation <https://arxiv.org/abs/2004.13665>`_.\n\n    Args:\n        aggregation (str): The method to aggregate multiple feature maps.\n            Options are 'sum', 'concat'. Defaults to 'sum'.\n        pre_cfg (:obj:`ConfigDict` or dict): Specify pre-processing modules.\n            Defaults to None.\n        post_cfg (:obj:`ConfigDict` or dict): Specify post-processing modules.\n            Defaults to None.\n        kwargs (keyword arguments): Arguments that are the same\n            as :class:`BaseRoIExtractor`.\n    \"\"\"\n\n    def __init__(self,\n                 aggregation: str = 'sum',\n                 pre_cfg: OptConfigType = None,\n                 post_cfg: OptConfigType = None,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n\n        assert aggregation in ['sum', 'concat']\n\n        self.aggregation = aggregation\n        self.with_post = post_cfg is not None\n        self.with_pre = pre_cfg is not None\n        # build pre/post processing modules\n        if self.with_post:\n            self.post_module = build_plugin_layer(post_cfg, '_post_module')[1]\n        if self.with_pre:\n            self.pre_module = build_plugin_layer(pre_cfg, '_pre_module')[1]\n\n    def forward(self,\n                feats: Tuple[Tensor],\n                rois: Tensor,\n                roi_scale_factor: Optional[float] = None) -> Tensor:\n        \"\"\"Extractor ROI feats.\n\n        Args:\n            feats (Tuple[Tensor]): Multi-scale features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            roi_scale_factor (Optional[float]): RoI scale factor.\n                Defaults to None.\n\n        Returns:\n            Tensor: RoI feature.\n        \"\"\"\n        out_size = self.roi_layers[0].output_size\n        num_levels = len(feats)\n        roi_feats = feats[0].new_zeros(\n            rois.size(0), self.out_channels, *out_size)\n\n        # some times rois is an empty tensor\n        if roi_feats.shape[0] == 0:\n            return roi_feats\n\n        if num_levels == 1:\n            return self.roi_layers[0](feats[0], rois)\n\n        if roi_scale_factor is not None:\n            rois = self.roi_rescale(rois, roi_scale_factor)\n\n        # mark the starting channels for concat mode\n        start_channels = 0\n        for i in range(num_levels):\n            roi_feats_t = self.roi_layers[i](feats[i], rois)\n            end_channels = start_channels + roi_feats_t.size(1)\n            if self.with_pre:\n                # apply pre-processing to a RoI extracted from each layer\n                roi_feats_t = self.pre_module(roi_feats_t)\n            if self.aggregation == 'sum':\n                # and sum them all\n                roi_feats += roi_feats_t\n            else:\n                # and concat them along channel dimension\n                roi_feats[:, start_channels:end_channels] = roi_feats_t\n            # update channels starting position\n            start_channels = end_channels\n        # check if concat channels match at the end\n        if self.aggregation == 'concat':\n            assert start_channels == self.out_channels\n\n        if self.with_post:\n            # apply post-processing before return the result\n            roi_feats = self.post_module(roi_feats)\n        return roi_feats\n"
  },
  {
    "path": "mmdet/models/roi_heads/roi_extractors/single_level_roi_extractor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptMultiConfig\nfrom .base_roi_extractor import BaseRoIExtractor\n\n\n@MODELS.register_module()\nclass SingleRoIExtractor(BaseRoIExtractor):\n    \"\"\"Extract RoI features from a single level feature map.\n\n    If there are multiple input feature levels, each RoI is mapped to a level\n    according to its scale. The mapping rule is proposed in\n    `FPN <https://arxiv.org/abs/1612.03144>`_.\n\n    Args:\n        roi_layer (:obj:`ConfigDict` or dict): Specify RoI layer type and\n            arguments.\n        out_channels (int): Output channels of RoI layers.\n        featmap_strides (List[int]): Strides of input feature maps.\n        finest_scale (int): Scale threshold of mapping to level 0.\n            Defaults to 56.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 roi_layer: ConfigType,\n                 out_channels: int,\n                 featmap_strides: List[int],\n                 finest_scale: int = 56,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            roi_layer=roi_layer,\n            out_channels=out_channels,\n            featmap_strides=featmap_strides,\n            init_cfg=init_cfg)\n        self.finest_scale = finest_scale\n\n    def map_roi_levels(self, rois: Tensor, num_levels: int) -> Tensor:\n        \"\"\"Map rois to corresponding feature levels by scales.\n\n        - scale < finest_scale * 2: level 0\n        - finest_scale * 2 <= scale < finest_scale * 4: level 1\n        - finest_scale * 4 <= scale < finest_scale * 8: level 2\n        - scale >= finest_scale * 8: level 3\n\n        Args:\n            rois (Tensor): Input RoIs, shape (k, 5).\n            num_levels (int): Total level number.\n\n        Returns:\n            Tensor: Level index (0-based) of each RoI, shape (k, )\n        \"\"\"\n        scale = torch.sqrt(\n            (rois[:, 3] - rois[:, 1]) * (rois[:, 4] - rois[:, 2]))\n        target_lvls = torch.floor(torch.log2(scale / self.finest_scale + 1e-6))\n        target_lvls = target_lvls.clamp(min=0, max=num_levels - 1).long()\n        return target_lvls\n\n    def forward(self,\n                feats: Tuple[Tensor],\n                rois: Tensor,\n                roi_scale_factor: Optional[float] = None):\n        \"\"\"Extractor ROI feats.\n\n        Args:\n            feats (Tuple[Tensor]): Multi-scale features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            roi_scale_factor (Optional[float]): RoI scale factor.\n                Defaults to None.\n\n        Returns:\n            Tensor: RoI feature.\n        \"\"\"\n        # convert fp32 to fp16 when amp is on\n        rois = rois.type_as(feats[0])\n        out_size = self.roi_layers[0].output_size\n        num_levels = len(feats)\n        roi_feats = feats[0].new_zeros(\n            rois.size(0), self.out_channels, *out_size)\n\n        # TODO: remove this when parrots supports\n        if torch.__version__ == 'parrots':\n            roi_feats.requires_grad = True\n\n        if num_levels == 1:\n            if len(rois) == 0:\n                return roi_feats\n            return self.roi_layers[0](feats[0], rois)\n\n        target_lvls = self.map_roi_levels(rois, num_levels)\n\n        if roi_scale_factor is not None:\n            rois = self.roi_rescale(rois, roi_scale_factor)\n\n        for i in range(num_levels):\n            mask = target_lvls == i\n            inds = mask.nonzero(as_tuple=False).squeeze(1)\n            if inds.numel() > 0:\n                rois_ = rois[inds]\n                roi_feats_t = self.roi_layers[i](feats[i], rois_)\n                roi_feats[inds] = roi_feats_t\n            else:\n                # Sometimes some pyramid levels will not be used for RoI\n                # feature extraction and this will cause an incomplete\n                # computation graph in one GPU, which is different from those\n                # in other GPUs and will cause a hanging error.\n                # Therefore, we add it to ensure each feature pyramid is\n                # included in the computation graph to avoid runtime bugs.\n                roi_feats += sum(\n                    x.view(-1)[0]\n                    for x in self.parameters()) * 0. + feats[i].sum() * 0.\n        return roi_feats\n"
  },
  {
    "path": "mmdet/models/roi_heads/scnet_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList, OptConfigType\nfrom ..layers import adaptive_avg_pool2d\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils import empty_instances, unpack_gt_instances\nfrom .cascade_roi_head import CascadeRoIHead\n\n\n@MODELS.register_module()\nclass SCNetRoIHead(CascadeRoIHead):\n    \"\"\"RoIHead for `SCNet <https://arxiv.org/abs/2012.10150>`_.\n\n    Args:\n        num_stages (int): number of cascade stages.\n        stage_loss_weights (list): loss weight of cascade stages.\n        semantic_roi_extractor (dict): config to init semantic roi extractor.\n        semantic_head (dict): config to init semantic head.\n        feat_relay_head (dict): config to init feature_relay_head.\n        glbctx_head (dict): config to init global context head.\n    \"\"\"\n\n    def __init__(self,\n                 num_stages: int,\n                 stage_loss_weights: List[float],\n                 semantic_roi_extractor: OptConfigType = None,\n                 semantic_head: OptConfigType = None,\n                 feat_relay_head: OptConfigType = None,\n                 glbctx_head: OptConfigType = None,\n                 **kwargs) -> None:\n        super().__init__(\n            num_stages=num_stages,\n            stage_loss_weights=stage_loss_weights,\n            **kwargs)\n        assert self.with_bbox and self.with_mask\n        assert not self.with_shared_head  # shared head is not supported\n\n        if semantic_head is not None:\n            self.semantic_roi_extractor = MODELS.build(semantic_roi_extractor)\n            self.semantic_head = MODELS.build(semantic_head)\n\n        if feat_relay_head is not None:\n            self.feat_relay_head = MODELS.build(feat_relay_head)\n\n        if glbctx_head is not None:\n            self.glbctx_head = MODELS.build(glbctx_head)\n\n    def init_mask_head(self, mask_roi_extractor: ConfigType,\n                       mask_head: ConfigType) -> None:\n        \"\"\"Initialize ``mask_head``\"\"\"\n        if mask_roi_extractor is not None:\n            self.mask_roi_extractor = MODELS.build(mask_roi_extractor)\n            self.mask_head = MODELS.build(mask_head)\n\n    # TODO move to base_roi_head later\n    @property\n    def with_semantic(self) -> bool:\n        \"\"\"bool: whether the head has semantic head\"\"\"\n        return hasattr(self,\n                       'semantic_head') and self.semantic_head is not None\n\n    @property\n    def with_feat_relay(self) -> bool:\n        \"\"\"bool: whether the head has feature relay head\"\"\"\n        return (hasattr(self, 'feat_relay_head')\n                and self.feat_relay_head is not None)\n\n    @property\n    def with_glbctx(self) -> bool:\n        \"\"\"bool: whether the head has global context head\"\"\"\n        return hasattr(self, 'glbctx_head') and self.glbctx_head is not None\n\n    def _fuse_glbctx(self, roi_feats: Tensor, glbctx_feat: Tensor,\n                     rois: Tensor) -> Tensor:\n        \"\"\"Fuse global context feats with roi feats.\n\n        Args:\n            roi_feats (Tensor): RoI features.\n            glbctx_feat (Tensor): Global context feature..\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n            Tensor: Fused feature.\n        \"\"\"\n        assert roi_feats.size(0) == rois.size(0)\n        # RuntimeError: isDifferentiableType(variable.scalar_type())\n        # INTERNAL ASSERT FAILED if detach() is not used when calling\n        # roi_head.predict().\n        img_inds = torch.unique(rois[:, 0].detach().cpu(), sorted=True).long()\n        fused_feats = torch.zeros_like(roi_feats)\n        for img_id in img_inds:\n            inds = (rois[:, 0] == img_id.item())\n            fused_feats[inds] = roi_feats[inds] + glbctx_feat[img_id]\n        return fused_feats\n\n    def _slice_pos_feats(self, feats: Tensor,\n                         sampling_results: List[SamplingResult]) -> Tensor:\n        \"\"\"Get features from pos rois.\n\n        Args:\n            feats (Tensor): Input features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            Tensor: Sliced features.\n        \"\"\"\n        num_rois = [res.priors.size(0) for res in sampling_results]\n        num_pos_rois = [res.pos_priors.size(0) for res in sampling_results]\n        inds = torch.zeros(sum(num_rois), dtype=torch.bool)\n        start = 0\n        for i in range(len(num_rois)):\n            start = 0 if i == 0 else start + num_rois[i - 1]\n            stop = start + num_pos_rois[i]\n            inds[start:stop] = 1\n        sliced_feats = feats[inds]\n        return sliced_feats\n\n    def _bbox_forward(self,\n                      stage: int,\n                      x: Tuple[Tensor],\n                      rois: Tensor,\n                      semantic_feat: Optional[Tensor] = None,\n                      glbctx_feat: Optional[Tensor] = None) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            semantic_feat (Tensor): Semantic feature. Defaults to None.\n            glbctx_feat (Tensor): Global context feature. Defaults to None.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        bbox_roi_extractor = self.bbox_roi_extractor[stage]\n        bbox_head = self.bbox_head[stage]\n        bbox_feats = bbox_roi_extractor(x[:bbox_roi_extractor.num_inputs],\n                                        rois)\n        if self.with_semantic and semantic_feat is not None:\n            bbox_semantic_feat = self.semantic_roi_extractor([semantic_feat],\n                                                             rois)\n            if bbox_semantic_feat.shape[-2:] != bbox_feats.shape[-2:]:\n                bbox_semantic_feat = adaptive_avg_pool2d(\n                    bbox_semantic_feat, bbox_feats.shape[-2:])\n            bbox_feats += bbox_semantic_feat\n        if self.with_glbctx and glbctx_feat is not None:\n            bbox_feats = self._fuse_glbctx(bbox_feats, glbctx_feat, rois)\n        cls_score, bbox_pred, relayed_feat = bbox_head(\n            bbox_feats, return_shared_feat=True)\n\n        bbox_results = dict(\n            cls_score=cls_score,\n            bbox_pred=bbox_pred,\n            relayed_feat=relayed_feat)\n        return bbox_results\n\n    def _mask_forward(self,\n                      x: Tuple[Tensor],\n                      rois: Tensor,\n                      semantic_feat: Optional[Tensor] = None,\n                      glbctx_feat: Optional[Tensor] = None,\n                      relayed_feat: Optional[Tensor] = None) -> dict:\n        \"\"\"Mask head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            semantic_feat (Tensor): Semantic feature. Defaults to None.\n            glbctx_feat (Tensor): Global context feature. Defaults to None.\n            relayed_feat (Tensor): Relayed feature. Defaults to None.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n        \"\"\"\n        mask_feats = self.mask_roi_extractor(\n            x[:self.mask_roi_extractor.num_inputs], rois)\n        if self.with_semantic and semantic_feat is not None:\n            mask_semantic_feat = self.semantic_roi_extractor([semantic_feat],\n                                                             rois)\n            if mask_semantic_feat.shape[-2:] != mask_feats.shape[-2:]:\n                mask_semantic_feat = F.adaptive_avg_pool2d(\n                    mask_semantic_feat, mask_feats.shape[-2:])\n            mask_feats += mask_semantic_feat\n        if self.with_glbctx and glbctx_feat is not None:\n            mask_feats = self._fuse_glbctx(mask_feats, glbctx_feat, rois)\n        if self.with_feat_relay and relayed_feat is not None:\n            mask_feats = mask_feats + relayed_feat\n        mask_preds = self.mask_head(mask_feats)\n        mask_results = dict(mask_preds=mask_preds)\n\n        return mask_results\n\n    def bbox_loss(self,\n                  stage: int,\n                  x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult],\n                  semantic_feat: Optional[Tensor] = None,\n                  glbctx_feat: Optional[Tensor] = None) -> dict:\n        \"\"\"Run forward function and calculate loss for box head in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            semantic_feat (Tensor): Semantic feature. Defaults to None.\n            glbctx_feat (Tensor): Global context feature. Defaults to None.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n                - `rois` (Tensor): RoIs with the shape (n, 5) where the first\n                  column indicates batch id of each RoI.\n                - `bbox_targets` (tuple):  Ground truth for proposals in a\n                  single image. Containing the following list of Tensors:\n                  (labels, label_weights, bbox_targets, bbox_weights)\n        \"\"\"\n        bbox_head = self.bbox_head[stage]\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(\n            stage,\n            x,\n            rois,\n            semantic_feat=semantic_feat,\n            glbctx_feat=glbctx_feat)\n        bbox_results.update(rois=rois)\n\n        bbox_loss_and_target = bbox_head.loss_and_target(\n            cls_score=bbox_results['cls_score'],\n            bbox_pred=bbox_results['bbox_pred'],\n            rois=rois,\n            sampling_results=sampling_results,\n            rcnn_train_cfg=self.train_cfg[stage])\n\n        bbox_results.update(bbox_loss_and_target)\n        return bbox_results\n\n    def mask_loss(self,\n                  x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult],\n                  batch_gt_instances: InstanceList,\n                  semantic_feat: Optional[Tensor] = None,\n                  glbctx_feat: Optional[Tensor] = None,\n                  relayed_feat: Optional[Tensor] = None) -> dict:\n        \"\"\"Run forward function and calculate loss for mask head in training.\n\n        Args:\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            semantic_feat (Tensor): Semantic feature. Defaults to None.\n            glbctx_feat (Tensor): Global context feature. Defaults to None.\n            relayed_feat (Tensor): Relayed feature. Defaults to None.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `loss_mask` (dict): A dictionary of mask loss components.\n        \"\"\"\n        pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n        mask_results = self._mask_forward(\n            x,\n            pos_rois,\n            semantic_feat=semantic_feat,\n            glbctx_feat=glbctx_feat,\n            relayed_feat=relayed_feat)\n\n        mask_loss_and_target = self.mask_head.loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=self.train_cfg[-1])\n        mask_results.update(mask_loss_and_target)\n\n        return mask_results\n\n    def semantic_loss(self, x: Tuple[Tensor],\n                      batch_data_samples: SampleList) -> dict:\n        \"\"\"Semantic segmentation loss.\n\n        Args:\n            x (Tuple[Tensor]): Tuple of multi-level img features.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `semantic_feat` (Tensor): Semantic feature.\n                - `loss_seg` (dict): Semantic segmentation loss.\n        \"\"\"\n        gt_semantic_segs = [\n            data_sample.gt_sem_seg.sem_seg\n            for data_sample in batch_data_samples\n        ]\n        gt_semantic_segs = torch.stack(gt_semantic_segs)\n        semantic_pred, semantic_feat = self.semantic_head(x)\n        loss_seg = self.semantic_head.loss(semantic_pred, gt_semantic_segs)\n\n        semantic_results = dict(loss_seg=loss_seg, semantic_feat=semantic_feat)\n\n        return semantic_results\n\n    def global_context_loss(self, x: Tuple[Tensor],\n                            batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Global context loss.\n\n        Args:\n            x (Tuple[Tensor]): Tuple of multi-level img features.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `glbctx_feat` (Tensor): Global context feature.\n                - `loss_glbctx` (dict): Global context loss.\n        \"\"\"\n        gt_labels = [\n            gt_instances.labels for gt_instances in batch_gt_instances\n        ]\n        mc_pred, glbctx_feat = self.glbctx_head(x)\n        loss_glbctx = self.glbctx_head.loss(mc_pred, gt_labels)\n        global_context_results = dict(\n            loss_glbctx=loss_glbctx, glbctx_feat=glbctx_feat)\n\n        return global_context_results\n\n    def loss(self, x: Tensor, rpn_results_list: InstanceList,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n\n        losses = dict()\n\n        # semantic segmentation branch\n        if self.with_semantic:\n            semantic_results = self.semantic_loss(\n                x=x, batch_data_samples=batch_data_samples)\n            losses['loss_semantic_seg'] = semantic_results['loss_seg']\n            semantic_feat = semantic_results['semantic_feat']\n        else:\n            semantic_feat = None\n\n        # global context branch\n        if self.with_glbctx:\n            global_context_results = self.global_context_loss(\n                x=x, batch_gt_instances=batch_gt_instances)\n            losses['loss_glbctx'] = global_context_results['loss_glbctx']\n            glbctx_feat = global_context_results['glbctx_feat']\n        else:\n            glbctx_feat = None\n\n        results_list = rpn_results_list\n        num_imgs = len(batch_img_metas)\n        for stage in range(self.num_stages):\n            stage_loss_weight = self.stage_loss_weights[stage]\n\n            # assign gts and sample proposals\n            sampling_results = []\n            bbox_assigner = self.bbox_assigner[stage]\n            bbox_sampler = self.bbox_sampler[stage]\n            for i in range(num_imgs):\n                results = results_list[i]\n                # rename rpn_results.bboxes to rpn_results.priors\n                results.priors = results.pop('bboxes')\n\n                assign_result = bbox_assigner.assign(\n                    results, batch_gt_instances[i],\n                    batch_gt_instances_ignore[i])\n                sampling_result = bbox_sampler.sample(\n                    assign_result,\n                    results,\n                    batch_gt_instances[i],\n                    feats=[lvl_feat[i][None] for lvl_feat in x])\n                sampling_results.append(sampling_result)\n\n            # bbox head forward and loss\n            bbox_results = self.bbox_loss(\n                stage=stage,\n                x=x,\n                sampling_results=sampling_results,\n                semantic_feat=semantic_feat,\n                glbctx_feat=glbctx_feat)\n\n            for name, value in bbox_results['loss_bbox'].items():\n                losses[f's{stage}.{name}'] = (\n                    value * stage_loss_weight if 'loss' in name else value)\n\n            # refine bboxes\n            if stage < self.num_stages - 1:\n                bbox_head = self.bbox_head[stage]\n                with torch.no_grad():\n                    results_list = bbox_head.refine_bboxes(\n                        sampling_results=sampling_results,\n                        bbox_results=bbox_results,\n                        batch_img_metas=batch_img_metas)\n\n        if self.with_feat_relay:\n            relayed_feat = self._slice_pos_feats(bbox_results['relayed_feat'],\n                                                 sampling_results)\n            relayed_feat = self.feat_relay_head(relayed_feat)\n        else:\n            relayed_feat = None\n\n        # mask head forward and loss\n        mask_results = self.mask_loss(\n            x=x,\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            semantic_feat=semantic_feat,\n            glbctx_feat=glbctx_feat,\n            relayed_feat=relayed_feat)\n        mask_stage_loss_weight = sum(self.stage_loss_weights)\n        losses['loss_mask'] = mask_stage_loss_weight * mask_results[\n            'loss_mask']['loss_mask']\n\n        return losses\n\n    def predict(self,\n                x: Tuple[Tensor],\n                rpn_results_list: InstanceList,\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the roi head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from upstream network. Each\n                has shape (N, C, H, W).\n            rpn_results_list (list[:obj:`InstanceData`]): list of region\n                proposals.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results to\n                the original image. Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        assert self.with_bbox, 'Bbox head must be implemented.'\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        if self.with_semantic:\n            _, semantic_feat = self.semantic_head(x)\n        else:\n            semantic_feat = None\n\n        if self.with_glbctx:\n            _, glbctx_feat = self.glbctx_head(x)\n        else:\n            glbctx_feat = None\n\n        # TODO: nms_op in mmcv need be enhanced, the bbox result may get\n        #  difference when not rescale in bbox_head\n\n        # If it has the mask branch, the bbox branch does not need\n        # to be scaled to the original image scale, because the mask\n        # branch will scale both bbox and mask at the same time.\n        bbox_rescale = rescale if not self.with_mask else False\n        results_list = self.predict_bbox(\n            x=x,\n            semantic_feat=semantic_feat,\n            glbctx_feat=glbctx_feat,\n            batch_img_metas=batch_img_metas,\n            rpn_results_list=rpn_results_list,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=bbox_rescale)\n\n        if self.with_mask:\n            results_list = self.predict_mask(\n                x=x,\n                semantic_heat=semantic_feat,\n                glbctx_feat=glbctx_feat,\n                batch_img_metas=batch_img_metas,\n                results_list=results_list,\n                rescale=rescale)\n\n        return results_list\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     semantic_heat: Tensor,\n                     glbctx_feat: Tensor,\n                     batch_img_metas: List[dict],\n                     results_list: List[InstanceData],\n                     rescale: bool = False) -> List[InstanceData]:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            semantic_feat (Tensor): Semantic feature.\n            glbctx_feat (Tensor): Global context feature.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas=batch_img_metas,\n                device=mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        bboxes_results = self._bbox_forward(\n            stage=-1,\n            x=x,\n            rois=mask_rois,\n            semantic_feat=semantic_heat,\n            glbctx_feat=glbctx_feat)\n        relayed_feat = bboxes_results['relayed_feat']\n        relayed_feat = self.feat_relay_head(relayed_feat)\n\n        mask_results = self._mask_forward(\n            x=x,\n            rois=mask_rois,\n            semantic_feat=semantic_heat,\n            glbctx_feat=glbctx_feat,\n            relayed_feat=relayed_feat)\n        mask_preds = mask_results['mask_preds']\n\n        # split batch mask prediction back to each image\n        num_bbox_per_img = tuple(len(_bbox) for _bbox in bboxes)\n        mask_preds = mask_preds.split(num_bbox_per_img, 0)\n\n        results_list = self.mask_head.predict_by_feat(\n            mask_preds=mask_preds,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale)\n\n        return results_list\n\n    def forward(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n                batch_data_samples: SampleList) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            x (List[Tensor]): Multi-level features that may have different\n                resolutions.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n\n        Returns\n            tuple: A tuple of features from ``bbox_head`` and ``mask_head``\n            forward.\n        \"\"\"\n        results = ()\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        if self.with_semantic:\n            _, semantic_feat = self.semantic_head(x)\n        else:\n            semantic_feat = None\n\n        if self.with_glbctx:\n            _, glbctx_feat = self.glbctx_head(x)\n        else:\n            glbctx_feat = None\n\n        proposals = [rpn_results.bboxes for rpn_results in rpn_results_list]\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = bbox2roi(proposals)\n        # bbox head\n        if self.with_bbox:\n            rois, cls_scores, bbox_preds = self._refine_roi(\n                x=x,\n                rois=rois,\n                semantic_feat=semantic_feat,\n                glbctx_feat=glbctx_feat,\n                batch_img_metas=batch_img_metas,\n                num_proposals_per_img=num_proposals_per_img)\n            results = results + (cls_scores, bbox_preds)\n        # mask head\n        if self.with_mask:\n            rois = torch.cat(rois)\n            bboxes_results = self._bbox_forward(\n                stage=-1,\n                x=x,\n                rois=rois,\n                semantic_feat=semantic_feat,\n                glbctx_feat=glbctx_feat)\n            relayed_feat = bboxes_results['relayed_feat']\n            relayed_feat = self.feat_relay_head(relayed_feat)\n            mask_results = self._mask_forward(\n                x=x,\n                rois=rois,\n                semantic_feat=semantic_feat,\n                glbctx_feat=glbctx_feat,\n                relayed_feat=relayed_feat)\n            mask_preds = mask_results['mask_preds']\n            mask_preds = mask_preds.split(num_proposals_per_img, 0)\n            results = results + (mask_preds, )\n        return results\n"
  },
  {
    "path": "mmdet/models/roi_heads/shared_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .res_layer import ResLayer\n\n__all__ = ['ResLayer']\n"
  },
  {
    "path": "mmdet/models/roi_heads/shared_heads/res_layer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport torch.nn as nn\nfrom mmengine.model import BaseModule\n\nfrom mmdet.models.backbones import ResNet\nfrom mmdet.models.layers import ResLayer as _ResLayer\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass ResLayer(BaseModule):\n\n    def __init__(self,\n                 depth,\n                 stage=3,\n                 stride=2,\n                 dilation=1,\n                 style='pytorch',\n                 norm_cfg=dict(type='BN', requires_grad=True),\n                 norm_eval=True,\n                 with_cp=False,\n                 dcn=None,\n                 pretrained=None,\n                 init_cfg=None):\n        super(ResLayer, self).__init__(init_cfg)\n\n        self.norm_eval = norm_eval\n        self.norm_cfg = norm_cfg\n        self.stage = stage\n        self.fp16_enabled = False\n        block, stage_blocks = ResNet.arch_settings[depth]\n        stage_block = stage_blocks[stage]\n        planes = 64 * 2**stage\n        inplanes = 64 * 2**(stage - 1) * block.expansion\n\n        res_layer = _ResLayer(\n            block,\n            inplanes,\n            planes,\n            stage_block,\n            stride=stride,\n            dilation=dilation,\n            style=style,\n            with_cp=with_cp,\n            norm_cfg=self.norm_cfg,\n            dcn=dcn)\n        self.add_module(f'layer{stage + 1}', res_layer)\n\n        assert not (init_cfg and pretrained), \\\n            'init_cfg and pretrained cannot be specified at the same time'\n        if isinstance(pretrained, str):\n            warnings.warn('DeprecationWarning: pretrained is a deprecated, '\n                          'please use \"init_cfg\" instead')\n            self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)\n        elif pretrained is None:\n            if init_cfg is None:\n                self.init_cfg = [\n                    dict(type='Kaiming', layer='Conv2d'),\n                    dict(\n                        type='Constant',\n                        val=1,\n                        layer=['_BatchNorm', 'GroupNorm'])\n                ]\n        else:\n            raise TypeError('pretrained must be a str or None')\n\n    def forward(self, x):\n        res_layer = getattr(self, f'layer{self.stage + 1}')\n        out = res_layer(x)\n        return out\n\n    def train(self, mode=True):\n        super(ResLayer, self).train(mode)\n        if self.norm_eval:\n            for m in self.modules():\n                if isinstance(m, nn.BatchNorm2d):\n                    m.eval()\n"
  },
  {
    "path": "mmdet/models/roi_heads/sparse_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules.samplers import PseudoSampler\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList, OptConfigType\nfrom ..utils.misc import empty_instances, unpack_gt_instances\nfrom .cascade_roi_head import CascadeRoIHead\n\n\n@MODELS.register_module()\nclass SparseRoIHead(CascadeRoIHead):\n    r\"\"\"The RoIHead for `Sparse R-CNN: End-to-End Object Detection with\n    Learnable Proposals <https://arxiv.org/abs/2011.12450>`_\n    and `Instances as Queries <http://arxiv.org/abs/2105.01928>`_\n\n    Args:\n        num_stages (int): Number of stage whole iterative process.\n            Defaults to 6.\n        stage_loss_weights (Tuple[float]): The loss\n            weight of each stage. By default all stages have\n            the same weight 1.\n        bbox_roi_extractor (:obj:`ConfigDict` or dict): Config of box\n            roi extractor.\n        mask_roi_extractor (:obj:`ConfigDict` or dict): Config of mask\n            roi extractor.\n        bbox_head (:obj:`ConfigDict` or dict): Config of box head.\n        mask_head (:obj:`ConfigDict` or dict): Config of mask head.\n        train_cfg (:obj:`ConfigDict` or dict, Optional): Configuration\n            information in train stage. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, Optional): Configuration\n            information in test stage. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_stages: int = 6,\n                 stage_loss_weights: Tuple[float] = (1, 1, 1, 1, 1, 1),\n                 proposal_feature_channel: int = 256,\n                 bbox_roi_extractor: ConfigType = dict(\n                     type='SingleRoIExtractor',\n                     roi_layer=dict(\n                         type='RoIAlign', output_size=7, sampling_ratio=2),\n                     out_channels=256,\n                     featmap_strides=[4, 8, 16, 32]),\n                 mask_roi_extractor: OptConfigType = None,\n                 bbox_head: ConfigType = dict(\n                     type='DIIHead',\n                     num_classes=80,\n                     num_fcs=2,\n                     num_heads=8,\n                     num_cls_fcs=1,\n                     num_reg_fcs=3,\n                     feedforward_channels=2048,\n                     hidden_channels=256,\n                     dropout=0.0,\n                     roi_feat_size=7,\n                     ffn_act_cfg=dict(type='ReLU', inplace=True)),\n                 mask_head: OptConfigType = None,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptConfigType = None) -> None:\n        assert bbox_roi_extractor is not None\n        assert bbox_head is not None\n        assert len(stage_loss_weights) == num_stages\n        self.num_stages = num_stages\n        self.stage_loss_weights = stage_loss_weights\n        self.proposal_feature_channel = proposal_feature_channel\n        super().__init__(\n            num_stages=num_stages,\n            stage_loss_weights=stage_loss_weights,\n            bbox_roi_extractor=bbox_roi_extractor,\n            mask_roi_extractor=mask_roi_extractor,\n            bbox_head=bbox_head,\n            mask_head=mask_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            init_cfg=init_cfg)\n        # train_cfg would be None when run the test.py\n        if train_cfg is not None:\n            for stage in range(num_stages):\n                assert isinstance(self.bbox_sampler[stage], PseudoSampler), \\\n                    'Sparse R-CNN and QueryInst only support `PseudoSampler`'\n\n    def bbox_loss(self, stage: int, x: Tuple[Tensor],\n                  results_list: InstanceList, object_feats: Tensor,\n                  batch_img_metas: List[dict],\n                  batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the bbox head on\n        the features of the upstream network.\n\n        Args:\n            stage (int): The current stage in iterative process.\n            x (tuple[Tensor]): List of multi-level img features.\n            results_list (List[:obj:`InstanceData`]) : List of region\n                proposals.\n            object_feats (Tensor): The object feature extracted from\n                the previous stage.\n            batch_img_metas (list[dict]): Meta information of each image.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n            - `cls_score` (Tensor): Classification scores.\n            - `bbox_pred` (Tensor): Box energies / deltas.\n            - `bbox_feats` (Tensor): Extract bbox RoI features.\n            - `loss_bbox` (dict): A dictionary of bbox loss components.\n        \"\"\"\n        proposal_list = [res.bboxes for res in results_list]\n        rois = bbox2roi(proposal_list)\n        bbox_results = self._bbox_forward(stage, x, rois, object_feats,\n                                          batch_img_metas)\n        imgs_whwh = torch.cat(\n            [res.imgs_whwh[None, ...] for res in results_list])\n        cls_pred_list = bbox_results['detached_cls_scores']\n        proposal_list = bbox_results['detached_proposals']\n\n        sampling_results = []\n        bbox_head = self.bbox_head[stage]\n        for i in range(len(batch_img_metas)):\n            pred_instances = InstanceData()\n            # TODO: Enhance the logic\n            pred_instances.bboxes = proposal_list[i]  # for assinger\n            pred_instances.scores = cls_pred_list[i]\n            pred_instances.priors = proposal_list[i]  # for sampler\n\n            assign_result = self.bbox_assigner[stage].assign(\n                pred_instances=pred_instances,\n                gt_instances=batch_gt_instances[i],\n                gt_instances_ignore=None,\n                img_meta=batch_img_metas[i])\n\n            sampling_result = self.bbox_sampler[stage].sample(\n                assign_result, pred_instances, batch_gt_instances[i])\n            sampling_results.append(sampling_result)\n\n        bbox_results.update(sampling_results=sampling_results)\n\n        cls_score = bbox_results['cls_score']\n        decoded_bboxes = bbox_results['decoded_bboxes']\n        cls_score = cls_score.view(-1, cls_score.size(-1))\n        decoded_bboxes = decoded_bboxes.view(-1, 4)\n        bbox_loss_and_target = bbox_head.loss_and_target(\n            cls_score,\n            decoded_bboxes,\n            sampling_results,\n            self.train_cfg[stage],\n            imgs_whwh=imgs_whwh,\n            concat=True)\n        bbox_results.update(bbox_loss_and_target)\n\n        # propose for the new proposal_list\n        proposal_list = []\n        for idx in range(len(batch_img_metas)):\n            results = InstanceData()\n            results.imgs_whwh = results_list[idx].imgs_whwh\n            results.bboxes = bbox_results['detached_proposals'][idx]\n            proposal_list.append(results)\n        bbox_results.update(results_list=proposal_list)\n        return bbox_results\n\n    def _bbox_forward(self, stage: int, x: Tuple[Tensor], rois: Tensor,\n                      object_feats: Tensor,\n                      batch_img_metas: List[dict]) -> dict:\n        \"\"\"Box head forward function used in both training and testing. Returns\n        all regression, classification results and a intermediate feature.\n\n        Args:\n            stage (int): The current stage in iterative process.\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n                Each dimension means (img_index, x1, y1, x2, y2).\n            object_feats (Tensor): The object feature extracted from\n                the previous stage.\n            batch_img_metas (list[dict]): Meta information of each image.\n\n        Returns:\n            dict[str, Tensor]: a dictionary of bbox head outputs,\n            Containing the following results:\n\n            - cls_score (Tensor): The score of each class, has\n              shape (batch_size, num_proposals, num_classes)\n              when use focal loss or\n              (batch_size, num_proposals, num_classes+1)\n              otherwise.\n            - decoded_bboxes (Tensor): The regression results\n              with shape (batch_size, num_proposal, 4).\n              The last dimension 4 represents\n              [tl_x, tl_y, br_x, br_y].\n            - object_feats (Tensor): The object feature extracted\n              from current stage\n            - detached_cls_scores (list[Tensor]): The detached\n              classification results, length is batch_size, and\n              each tensor has shape (num_proposal, num_classes).\n            - detached_proposals (list[tensor]): The detached\n              regression results, length is batch_size, and each\n              tensor has shape (num_proposal, 4). The last\n              dimension 4 represents [tl_x, tl_y, br_x, br_y].\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        bbox_roi_extractor = self.bbox_roi_extractor[stage]\n        bbox_head = self.bbox_head[stage]\n        bbox_feats = bbox_roi_extractor(x[:bbox_roi_extractor.num_inputs],\n                                        rois)\n        cls_score, bbox_pred, object_feats, attn_feats = bbox_head(\n            bbox_feats, object_feats)\n\n        fake_bbox_results = dict(\n            rois=rois,\n            bbox_targets=(rois.new_zeros(len(rois), dtype=torch.long), None),\n            bbox_pred=bbox_pred.view(-1, bbox_pred.size(-1)),\n            cls_score=cls_score.view(-1, cls_score.size(-1)))\n        fake_sampling_results = [\n            InstanceData(pos_is_gt=rois.new_zeros(object_feats.size(1)))\n            for _ in range(len(batch_img_metas))\n        ]\n\n        results_list = bbox_head.refine_bboxes(\n            sampling_results=fake_sampling_results,\n            bbox_results=fake_bbox_results,\n            batch_img_metas=batch_img_metas)\n        proposal_list = [res.bboxes for res in results_list]\n        bbox_results = dict(\n            cls_score=cls_score,\n            decoded_bboxes=torch.cat(proposal_list),\n            object_feats=object_feats,\n            attn_feats=attn_feats,\n            # detach then use it in label assign\n            detached_cls_scores=[\n                cls_score[i].detach() for i in range(num_imgs)\n            ],\n            detached_proposals=[item.detach() for item in proposal_list])\n\n        return bbox_results\n\n    def _mask_forward(self, stage: int, x: Tuple[Tensor], rois: Tensor,\n                      attn_feats) -> dict:\n        \"\"\"Mask head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            attn_feats (Tensot): Intermediate feature get from the last\n                diihead, has shape\n                (batch_size*num_proposals, feature_dimensions)\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n            - `mask_preds` (Tensor): Mask prediction.\n        \"\"\"\n        mask_roi_extractor = self.mask_roi_extractor[stage]\n        mask_head = self.mask_head[stage]\n        mask_feats = mask_roi_extractor(x[:mask_roi_extractor.num_inputs],\n                                        rois)\n        # do not support caffe_c4 model anymore\n        mask_preds = mask_head(mask_feats, attn_feats)\n\n        mask_results = dict(mask_preds=mask_preds)\n        return mask_results\n\n    def mask_loss(self, stage: int, x: Tuple[Tensor], bbox_results: dict,\n                  batch_gt_instances: InstanceList,\n                  rcnn_train_cfg: ConfigDict) -> dict:\n        \"\"\"Run forward function and calculate loss for mask head in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            bbox_results (dict): Results obtained from `bbox_loss`.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n            rcnn_train_cfg (obj:ConfigDict): `train_cfg` of RCNN.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n            - `mask_preds` (Tensor): Mask prediction.\n            - `loss_mask` (dict): A dictionary of mask loss components.\n        \"\"\"\n        attn_feats = bbox_results['attn_feats']\n        sampling_results = bbox_results['sampling_results']\n\n        pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n\n        attn_feats = torch.cat([\n            feats[res.pos_inds]\n            for (feats, res) in zip(attn_feats, sampling_results)\n        ])\n        mask_results = self._mask_forward(stage, x, pos_rois, attn_feats)\n\n        mask_loss_and_target = self.mask_head[stage].loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=rcnn_train_cfg)\n        mask_results.update(mask_loss_and_target)\n\n        return mask_results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (List[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: a dictionary of loss components of all stage.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n\n        object_feats = torch.cat(\n            [res.pop('features')[None, ...] for res in rpn_results_list])\n        results_list = rpn_results_list\n        losses = {}\n        for stage in range(self.num_stages):\n            stage_loss_weight = self.stage_loss_weights[stage]\n\n            # bbox head forward and loss\n            bbox_results = self.bbox_loss(\n                stage=stage,\n                x=x,\n                object_feats=object_feats,\n                results_list=results_list,\n                batch_img_metas=batch_img_metas,\n                batch_gt_instances=batch_gt_instances)\n\n            for name, value in bbox_results['loss_bbox'].items():\n                losses[f's{stage}.{name}'] = (\n                    value * stage_loss_weight if 'loss' in name else value)\n\n            if self.with_mask:\n                mask_results = self.mask_loss(\n                    stage=stage,\n                    x=x,\n                    bbox_results=bbox_results,\n                    batch_gt_instances=batch_gt_instances,\n                    rcnn_train_cfg=self.train_cfg[stage])\n\n                for name, value in mask_results['loss_mask'].items():\n                    losses[f's{stage}.{name}'] = (\n                        value * stage_loss_weight if 'loss' in name else value)\n\n            object_feats = bbox_results['object_feats']\n            results_list = bbox_results['results_list']\n        return losses\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the bbox head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x(tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of R-CNN.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        proposal_list = [res.bboxes for res in rpn_results_list]\n        object_feats = torch.cat(\n            [res.pop('features')[None, ...] for res in rpn_results_list])\n        if all([proposal.shape[0] == 0 for proposal in proposal_list]):\n            # There is no proposal in the whole batch\n            return empty_instances(\n                batch_img_metas, x[0].device, task_type='bbox')\n\n        for stage in range(self.num_stages):\n            rois = bbox2roi(proposal_list)\n            bbox_results = self._bbox_forward(stage, x, rois, object_feats,\n                                              batch_img_metas)\n            object_feats = bbox_results['object_feats']\n            cls_score = bbox_results['cls_score']\n            proposal_list = bbox_results['detached_proposals']\n\n        num_classes = self.bbox_head[-1].num_classes\n\n        if self.bbox_head[-1].loss_cls.use_sigmoid:\n            cls_score = cls_score.sigmoid()\n        else:\n            cls_score = cls_score.softmax(-1)[..., :-1]\n\n        topk_inds_list = []\n        results_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score_per_img = cls_score[img_id]\n            scores_per_img, topk_inds = cls_score_per_img.flatten(0, 1).topk(\n                self.test_cfg.max_per_img, sorted=False)\n            labels_per_img = topk_inds % num_classes\n            bboxes_per_img = proposal_list[img_id][topk_inds // num_classes]\n            topk_inds_list.append(topk_inds)\n            if rescale and bboxes_per_img.size(0) > 0:\n                assert batch_img_metas[img_id].get('scale_factor') is not None\n                scale_factor = bboxes_per_img.new_tensor(\n                    batch_img_metas[img_id]['scale_factor']).repeat((1, 2))\n                bboxes_per_img = (\n                    bboxes_per_img.view(bboxes_per_img.size(0), -1, 4) /\n                    scale_factor).view(bboxes_per_img.size()[0], -1)\n\n            results = InstanceData()\n            results.bboxes = bboxes_per_img\n            results.scores = scores_per_img\n            results.labels = labels_per_img\n            results_list.append(results)\n        if self.with_mask:\n            for img_id in range(len(batch_img_metas)):\n                # add positive information in InstanceData to predict\n                # mask results in `mask_head`.\n                proposals = bbox_results['detached_proposals'][img_id]\n                topk_inds = topk_inds_list[img_id]\n                attn_feats = bbox_results['attn_feats'][img_id]\n\n                results_list[img_id].proposals = proposals\n                results_list[img_id].topk_inds = topk_inds\n                results_list[img_id].attn_feats = attn_feats\n        return results_list\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: InstanceList,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image. Each item usually contains following keys:\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - proposal (Tensor): Bboxes predicted from bbox_head,\n                  has a shape (num_instances, 4).\n                - topk_inds (Tensor): Topk indices of each image, has\n                  shape (num_instances, )\n                - attn_feats (Tensor): Intermediate feature get from the last\n                  diihead, has shape (num_instances, feature_dimensions)\n\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n            - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        proposal_list = [res.pop('proposals') for res in results_list]\n        topk_inds_list = [res.pop('topk_inds') for res in results_list]\n        attn_feats = torch.cat(\n            [res.pop('attn_feats')[None, ...] for res in results_list])\n\n        rois = bbox2roi(proposal_list)\n\n        if rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas,\n                rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        last_stage = self.num_stages - 1\n        mask_results = self._mask_forward(last_stage, x, rois, attn_feats)\n\n        num_imgs = len(batch_img_metas)\n        mask_results['mask_preds'] = mask_results['mask_preds'].reshape(\n            num_imgs, -1, *mask_results['mask_preds'].size()[1:])\n        num_classes = self.bbox_head[-1].num_classes\n\n        mask_preds = []\n        for img_id in range(num_imgs):\n            topk_inds = topk_inds_list[img_id]\n            masks_per_img = mask_results['mask_preds'][img_id].flatten(\n                0, 1)[topk_inds]\n            masks_per_img = masks_per_img[:, None,\n                                          ...].repeat(1, num_classes, 1, 1)\n            mask_preds.append(masks_per_img)\n        results_list = self.mask_head[-1].predict_by_feat(\n            mask_preds,\n            results_list,\n            batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale)\n\n        return results_list\n\n    # TODO: Need to refactor later\n    def forward(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n                batch_data_samples: SampleList) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            x (List[Tensor]): Multi-level features that may have different\n                resolutions.\n            rpn_results_list (List[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns\n            tuple: A tuple of features from ``bbox_head`` and ``mask_head``\n            forward.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        (batch_gt_instances, batch_gt_instances_ignore,\n         batch_img_metas) = outputs\n\n        all_stage_bbox_results = []\n        object_feats = torch.cat(\n            [res.pop('features')[None, ...] for res in rpn_results_list])\n        results_list = rpn_results_list\n        if self.with_bbox:\n            for stage in range(self.num_stages):\n                bbox_results = self.bbox_loss(\n                    stage=stage,\n                    x=x,\n                    results_list=results_list,\n                    object_feats=object_feats,\n                    batch_img_metas=batch_img_metas,\n                    batch_gt_instances=batch_gt_instances)\n                bbox_results.pop('loss_bbox')\n                # torch.jit does not support obj:SamplingResult\n                bbox_results.pop('results_list')\n                bbox_res = bbox_results.copy()\n                bbox_res.pop('sampling_results')\n                all_stage_bbox_results.append((bbox_res, ))\n\n                if self.with_mask:\n                    attn_feats = bbox_results['attn_feats']\n                    sampling_results = bbox_results['sampling_results']\n\n                    pos_rois = bbox2roi(\n                        [res.pos_priors for res in sampling_results])\n\n                    attn_feats = torch.cat([\n                        feats[res.pos_inds]\n                        for (feats, res) in zip(attn_feats, sampling_results)\n                    ])\n                    mask_results = self._mask_forward(stage, x, pos_rois,\n                                                      attn_feats)\n                    all_stage_bbox_results[-1] += (mask_results, )\n        return tuple(all_stage_bbox_results)\n"
  },
  {
    "path": "mmdet/models/roi_heads/standard_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import DetDataSample, SampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import ConfigType, InstanceList\nfrom ..task_modules.samplers import SamplingResult\nfrom ..utils import empty_instances, unpack_gt_instances\nfrom .base_roi_head import BaseRoIHead\n\n\n@MODELS.register_module()\nclass StandardRoIHead(BaseRoIHead):\n    \"\"\"Simplest base roi head including one bbox head and one mask head.\"\"\"\n\n    def init_assigner_sampler(self) -> None:\n        \"\"\"Initialize assigner and sampler.\"\"\"\n        self.bbox_assigner = None\n        self.bbox_sampler = None\n        if self.train_cfg:\n            self.bbox_assigner = TASK_UTILS.build(self.train_cfg.assigner)\n            self.bbox_sampler = TASK_UTILS.build(\n                self.train_cfg.sampler, default_args=dict(context=self))\n\n    def init_bbox_head(self, bbox_roi_extractor: ConfigType,\n                       bbox_head: ConfigType) -> None:\n        \"\"\"Initialize box head and box roi extractor.\n\n        Args:\n            bbox_roi_extractor (dict or ConfigDict): Config of box\n                roi extractor.\n            bbox_head (dict or ConfigDict): Config of box in box head.\n        \"\"\"\n        self.bbox_roi_extractor = MODELS.build(bbox_roi_extractor)\n        self.bbox_head = MODELS.build(bbox_head)\n\n    def init_mask_head(self, mask_roi_extractor: ConfigType,\n                       mask_head: ConfigType) -> None:\n        \"\"\"Initialize mask head and mask roi extractor.\n\n        Args:\n            mask_roi_extractor (dict or ConfigDict): Config of mask roi\n                extractor.\n            mask_head (dict or ConfigDict): Config of mask in mask head.\n        \"\"\"\n        if mask_roi_extractor is not None:\n            self.mask_roi_extractor = MODELS.build(mask_roi_extractor)\n            self.share_roi_extractor = False\n        else:\n            self.share_roi_extractor = True\n            self.mask_roi_extractor = self.bbox_roi_extractor\n        self.mask_head = MODELS.build(mask_head)\n\n    # TODO: Need to refactor later\n    def forward(self,\n                x: Tuple[Tensor],\n                rpn_results_list: InstanceList,\n                batch_data_samples: SampleList = None) -> tuple:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n        Args:\n            x (List[Tensor]): Multi-level features that may have different\n                resolutions.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n            the meta information of each image and corresponding\n            annotations.\n\n        Returns\n            tuple: A tuple of features from ``bbox_head`` and ``mask_head``\n            forward.\n        \"\"\"\n        results = ()\n        proposals = [rpn_results.bboxes for rpn_results in rpn_results_list]\n        rois = bbox2roi(proposals)\n        # bbox head\n        if self.with_bbox:\n            bbox_results = self._bbox_forward(x, rois)\n            results = results + (bbox_results['cls_score'],\n                                 bbox_results['bbox_pred'])\n        # mask head\n        if self.with_mask:\n            mask_rois = rois[:100]\n            mask_results = self._mask_forward(x, mask_rois)\n            results = results + (mask_results['mask_preds'], )\n        return results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: List[DetDataSample]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, _ = outputs\n\n        # assign gts and sample proposals\n        num_imgs = len(batch_data_samples)\n        sampling_results = []\n        for i in range(num_imgs):\n            # rename rpn_results.bboxes to rpn_results.priors\n            rpn_results = rpn_results_list[i]\n            rpn_results.priors = rpn_results.pop('bboxes')\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in x])\n            sampling_results.append(sampling_result)\n\n        losses = dict()\n        # bbox head loss\n        if self.with_bbox:\n            bbox_results = self.bbox_loss(x, sampling_results)\n            losses.update(bbox_results['loss_bbox'])\n\n        # mask head forward and loss\n        if self.with_mask:\n            mask_results = self.mask_loss(x, sampling_results,\n                                          bbox_results['bbox_feats'],\n                                          batch_gt_instances)\n            losses.update(mask_results['loss_mask'])\n\n        return losses\n\n    def _bbox_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        # TODO: a more flexible way to decide which feature maps to use\n        bbox_feats = self.bbox_roi_extractor(\n            x[:self.bbox_roi_extractor.num_inputs], rois)\n        if self.with_shared_head:\n            bbox_feats = self.shared_head(bbox_feats)\n        cls_score, bbox_pred = self.bbox_head(bbox_feats)\n\n        bbox_results = dict(\n            cls_score=cls_score, bbox_pred=bbox_pred, bbox_feats=bbox_feats)\n        return bbox_results\n\n    def bbox_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the bbox head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n        \"\"\"\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(x, rois)\n\n        bbox_loss_and_target = self.bbox_head.loss_and_target(\n            cls_score=bbox_results['cls_score'],\n            bbox_pred=bbox_results['bbox_pred'],\n            rois=rois,\n            sampling_results=sampling_results,\n            rcnn_train_cfg=self.train_cfg)\n\n        bbox_results.update(loss_bbox=bbox_loss_and_target['loss_bbox'])\n        return bbox_results\n\n    def mask_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult], bbox_feats: Tensor,\n                  batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the mask head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            bbox_feats (Tensor): Extract bbox RoI features.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `mask_feats` (Tensor): Extract mask RoI features.\n                - `mask_targets` (Tensor): Mask target of each positive\\\n                    proposals in the image.\n                - `loss_mask` (dict): A dictionary of mask loss components.\n        \"\"\"\n        if not self.share_roi_extractor:\n            pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n            mask_results = self._mask_forward(x, pos_rois)\n        else:\n            pos_inds = []\n            device = bbox_feats.device\n            for res in sampling_results:\n                pos_inds.append(\n                    torch.ones(\n                        res.pos_priors.shape[0],\n                        device=device,\n                        dtype=torch.uint8))\n                pos_inds.append(\n                    torch.zeros(\n                        res.neg_priors.shape[0],\n                        device=device,\n                        dtype=torch.uint8))\n            pos_inds = torch.cat(pos_inds)\n\n            mask_results = self._mask_forward(\n                x, pos_inds=pos_inds, bbox_feats=bbox_feats)\n\n        mask_loss_and_target = self.mask_head.loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=self.train_cfg)\n\n        mask_results.update(loss_mask=mask_loss_and_target['loss_mask'])\n        return mask_results\n\n    def _mask_forward(self,\n                      x: Tuple[Tensor],\n                      rois: Tensor = None,\n                      pos_inds: Optional[Tensor] = None,\n                      bbox_feats: Optional[Tensor] = None) -> dict:\n        \"\"\"Mask head forward function used in both training and testing.\n\n        Args:\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n            pos_inds (Tensor, optional): Indices of positive samples.\n                Defaults to None.\n            bbox_feats (Tensor): Extract bbox RoI features. Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `mask_feats` (Tensor): Extract mask RoI features.\n        \"\"\"\n        assert ((rois is not None) ^\n                (pos_inds is not None and bbox_feats is not None))\n        if rois is not None:\n            mask_feats = self.mask_roi_extractor(\n                x[:self.mask_roi_extractor.num_inputs], rois)\n            if self.with_shared_head:\n                mask_feats = self.shared_head(mask_feats)\n        else:\n            assert bbox_feats is not None\n            mask_feats = bbox_feats[pos_inds]\n\n        mask_preds = self.mask_head(mask_feats)\n        mask_results = dict(mask_preds=mask_preds, mask_feats=mask_feats)\n        return mask_results\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the bbox head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of R-CNN.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        proposals = [res.bboxes for res in rpn_results_list]\n        rois = bbox2roi(proposals)\n\n        if rois.shape[0] == 0:\n            return empty_instances(\n                batch_img_metas,\n                rois.device,\n                task_type='bbox',\n                box_type=self.bbox_head.predict_box_type,\n                num_classes=self.bbox_head.num_classes,\n                score_per_cls=rcnn_test_cfg is None)\n\n        bbox_results = self._bbox_forward(x, rois)\n\n        # split batch bbox prediction back to each image\n        cls_scores = bbox_results['cls_score']\n        bbox_preds = bbox_results['bbox_pred']\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = rois.split(num_proposals_per_img, 0)\n        cls_scores = cls_scores.split(num_proposals_per_img, 0)\n\n        # some detector with_reg is False, bbox_preds will be None\n        if bbox_preds is not None:\n            # TODO move this to a sabl_roi_head\n            # the bbox prediction of some detectors like SABL is not Tensor\n            if isinstance(bbox_preds, torch.Tensor):\n                bbox_preds = bbox_preds.split(num_proposals_per_img, 0)\n            else:\n                bbox_preds = self.bbox_head.bbox_pred_split(\n                    bbox_preds, num_proposals_per_img)\n        else:\n            bbox_preds = (None, ) * len(proposals)\n\n        result_list = self.bbox_head.predict_by_feat(\n            rois=rois,\n            cls_scores=cls_scores,\n            bbox_preds=bbox_preds,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=rcnn_test_cfg,\n            rescale=rescale)\n        return result_list\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: InstanceList,\n                     rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        # don't need to consider aug_test.\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas,\n                mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        mask_results = self._mask_forward(x, mask_rois)\n        mask_preds = mask_results['mask_preds']\n        # split batch mask prediction back to each image\n        num_mask_rois_per_img = [len(res) for res in results_list]\n        mask_preds = mask_preds.split(num_mask_rois_per_img, 0)\n\n        # TODO: Handle the case where rescale is false\n        results_list = self.mask_head.predict_by_feat(\n            mask_preds=mask_preds,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale)\n        return results_list\n"
  },
  {
    "path": "mmdet/models/roi_heads/test_mixins.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# TODO: delete this file after refactor\nimport sys\n\nimport torch\n\nfrom mmdet.models.layers import multiclass_nms\nfrom mmdet.models.test_time_augs import merge_aug_bboxes, merge_aug_masks\nfrom mmdet.structures.bbox import bbox2roi, bbox_mapping\n\nif sys.version_info >= (3, 7):\n    from mmdet.utils.contextmanagers import completed\n\n\nclass BBoxTestMixin:\n\n    if sys.version_info >= (3, 7):\n        # TODO: Currently not supported\n        async def async_test_bboxes(self,\n                                    x,\n                                    img_metas,\n                                    proposals,\n                                    rcnn_test_cfg,\n                                    rescale=False,\n                                    **kwargs):\n            \"\"\"Asynchronized test for box head without augmentation.\"\"\"\n            rois = bbox2roi(proposals)\n            roi_feats = self.bbox_roi_extractor(\n                x[:len(self.bbox_roi_extractor.featmap_strides)], rois)\n            if self.with_shared_head:\n                roi_feats = self.shared_head(roi_feats)\n            sleep_interval = rcnn_test_cfg.get('async_sleep_interval', 0.017)\n\n            async with completed(\n                    __name__, 'bbox_head_forward',\n                    sleep_interval=sleep_interval):\n                cls_score, bbox_pred = self.bbox_head(roi_feats)\n\n            img_shape = img_metas[0]['img_shape']\n            scale_factor = img_metas[0]['scale_factor']\n            det_bboxes, det_labels = self.bbox_head.get_bboxes(\n                rois,\n                cls_score,\n                bbox_pred,\n                img_shape,\n                scale_factor,\n                rescale=rescale,\n                cfg=rcnn_test_cfg)\n            return det_bboxes, det_labels\n\n    # TODO: Currently not supported\n    def aug_test_bboxes(self, feats, img_metas, rpn_results_list,\n                        rcnn_test_cfg):\n        \"\"\"Test det bboxes with test time augmentation.\"\"\"\n        aug_bboxes = []\n        aug_scores = []\n        for x, img_meta in zip(feats, img_metas):\n            # only one image in the batch\n            img_shape = img_meta[0]['img_shape']\n            scale_factor = img_meta[0]['scale_factor']\n            flip = img_meta[0]['flip']\n            flip_direction = img_meta[0]['flip_direction']\n            # TODO more flexible\n            proposals = bbox_mapping(rpn_results_list[0][:, :4], img_shape,\n                                     scale_factor, flip, flip_direction)\n            rois = bbox2roi([proposals])\n            bbox_results = self.bbox_forward(x, rois)\n            bboxes, scores = self.bbox_head.get_bboxes(\n                rois,\n                bbox_results['cls_score'],\n                bbox_results['bbox_pred'],\n                img_shape,\n                scale_factor,\n                rescale=False,\n                cfg=None)\n            aug_bboxes.append(bboxes)\n            aug_scores.append(scores)\n        # after merging, bboxes will be rescaled to the original image size\n        merged_bboxes, merged_scores = merge_aug_bboxes(\n            aug_bboxes, aug_scores, img_metas, rcnn_test_cfg)\n        if merged_bboxes.shape[0] == 0:\n            # There is no proposal in the single image\n            det_bboxes = merged_bboxes.new_zeros(0, 5)\n            det_labels = merged_bboxes.new_zeros((0, ), dtype=torch.long)\n        else:\n            det_bboxes, det_labels = multiclass_nms(merged_bboxes,\n                                                    merged_scores,\n                                                    rcnn_test_cfg.score_thr,\n                                                    rcnn_test_cfg.nms,\n                                                    rcnn_test_cfg.max_per_img)\n        return det_bboxes, det_labels\n\n\nclass MaskTestMixin:\n\n    if sys.version_info >= (3, 7):\n        # TODO: Currently not supported\n        async def async_test_mask(self,\n                                  x,\n                                  img_metas,\n                                  det_bboxes,\n                                  det_labels,\n                                  rescale=False,\n                                  mask_test_cfg=None):\n            \"\"\"Asynchronized test for mask head without augmentation.\"\"\"\n            # image shape of the first image in the batch (only one)\n            ori_shape = img_metas[0]['ori_shape']\n            scale_factor = img_metas[0]['scale_factor']\n            if det_bboxes.shape[0] == 0:\n                segm_result = [[] for _ in range(self.mask_head.num_classes)]\n            else:\n                if rescale and not isinstance(scale_factor,\n                                              (float, torch.Tensor)):\n                    scale_factor = det_bboxes.new_tensor(scale_factor)\n                _bboxes = (\n                    det_bboxes[:, :4] *\n                    scale_factor if rescale else det_bboxes)\n                mask_rois = bbox2roi([_bboxes])\n                mask_feats = self.mask_roi_extractor(\n                    x[:len(self.mask_roi_extractor.featmap_strides)],\n                    mask_rois)\n\n                if self.with_shared_head:\n                    mask_feats = self.shared_head(mask_feats)\n                if mask_test_cfg and \\\n                        mask_test_cfg.get('async_sleep_interval'):\n                    sleep_interval = mask_test_cfg['async_sleep_interval']\n                else:\n                    sleep_interval = 0.035\n                async with completed(\n                        __name__,\n                        'mask_head_forward',\n                        sleep_interval=sleep_interval):\n                    mask_pred = self.mask_head(mask_feats)\n                segm_result = self.mask_head.get_results(\n                    mask_pred, _bboxes, det_labels, self.test_cfg, ori_shape,\n                    scale_factor, rescale)\n            return segm_result\n\n    # TODO: Currently not supported\n    def aug_test_mask(self, feats, img_metas, det_bboxes, det_labels):\n        \"\"\"Test for mask head with test time augmentation.\"\"\"\n        if det_bboxes.shape[0] == 0:\n            segm_result = [[] for _ in range(self.mask_head.num_classes)]\n        else:\n            aug_masks = []\n            for x, img_meta in zip(feats, img_metas):\n                img_shape = img_meta[0]['img_shape']\n                scale_factor = img_meta[0]['scale_factor']\n                flip = img_meta[0]['flip']\n                flip_direction = img_meta[0]['flip_direction']\n                _bboxes = bbox_mapping(det_bboxes[:, :4], img_shape,\n                                       scale_factor, flip, flip_direction)\n                mask_rois = bbox2roi([_bboxes])\n                mask_results = self._mask_forward(x, mask_rois)\n                # convert to numpy array to save memory\n                aug_masks.append(\n                    mask_results['mask_pred'].sigmoid().cpu().numpy())\n            merged_masks = merge_aug_masks(aug_masks, img_metas, self.test_cfg)\n\n            ori_shape = img_metas[0][0]['ori_shape']\n            scale_factor = det_bboxes.new_ones(4)\n            segm_result = self.mask_head.get_results(\n                merged_masks,\n                det_bboxes,\n                det_labels,\n                self.test_cfg,\n                ori_shape,\n                scale_factor=scale_factor,\n                rescale=False)\n        return segm_result\n"
  },
  {
    "path": "mmdet/models/roi_heads/trident_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch\nfrom mmcv.ops import batched_nms\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import InstanceList\nfrom .standard_roi_head import StandardRoIHead\n\n\n@MODELS.register_module()\nclass TridentRoIHead(StandardRoIHead):\n    \"\"\"Trident roi head.\n\n    Args:\n        num_branch (int): Number of branches in TridentNet.\n        test_branch_idx (int): In inference, all 3 branches will be used\n            if `test_branch_idx==-1`, otherwise only branch with index\n            `test_branch_idx` will be used.\n    \"\"\"\n\n    def __init__(self, num_branch: int, test_branch_idx: int,\n                 **kwargs) -> None:\n        self.num_branch = num_branch\n        self.test_branch_idx = test_branch_idx\n        super().__init__(**kwargs)\n\n    def merge_trident_bboxes(self,\n                             trident_results: InstanceList) -> InstanceData:\n        \"\"\"Merge bbox predictions of each branch.\n\n        Args:\n            trident_results (List[:obj:`InstanceData`]): A list of InstanceData\n                predicted from every branch.\n\n        Returns:\n            :obj:`InstanceData`: merged InstanceData.\n        \"\"\"\n        bboxes = torch.cat([res.bboxes for res in trident_results])\n        scores = torch.cat([res.scores for res in trident_results])\n        labels = torch.cat([res.labels for res in trident_results])\n\n        nms_cfg = self.test_cfg['nms']\n        results = InstanceData()\n        if bboxes.numel() == 0:\n            results.bboxes = bboxes\n            results.scores = scores\n            results.labels = labels\n        else:\n            det_bboxes, keep = batched_nms(bboxes, scores, labels, nms_cfg)\n            results.bboxes = det_bboxes[:, :-1]\n            results.scores = det_bboxes[:, -1]\n            results.labels = labels[keep]\n\n        if self.test_cfg['max_per_img'] > 0:\n            results = results[:self.test_cfg['max_per_img']]\n        return results\n\n    def predict(self,\n                x: Tuple[Tensor],\n                rpn_results_list: InstanceList,\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the roi head and predict detection\n        results on the features of the upstream network.\n\n        - Compute prediction bbox and label per branch.\n        - Merge predictions of each branch according to scores of\n          bboxes, i.e., bboxes with higher score are kept to give\n          top-k prediction.\n\n        Args:\n            x (tuple[Tensor]): Features from upstream network. Each\n                has shape (N, C, H, W).\n            rpn_results_list (list[:obj:`InstanceData`]): list of region\n                proposals.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results to\n                the original image. Defaults to True.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        results_list = super().predict(\n            x=x,\n            rpn_results_list=rpn_results_list,\n            batch_data_samples=batch_data_samples,\n            rescale=rescale)\n\n        num_branch = self.num_branch \\\n            if self.training or self.test_branch_idx == -1 else 1\n\n        merged_results_list = []\n        for i in range(len(batch_data_samples) // num_branch):\n            merged_results_list.append(\n                self.merge_trident_bboxes(results_list[i * num_branch:(i + 1) *\n                                                       num_branch]))\n        return merged_results_list\n"
  },
  {
    "path": "mmdet/models/seg_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .panoptic_fpn_head import PanopticFPNHead  # noqa: F401,F403\nfrom .panoptic_fusion_heads import *  # noqa: F401,F403\n"
  },
  {
    "path": "mmdet/models/seg_heads/base_semantic_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import Dict, List, Tuple, Union\n\nimport torch.nn.functional as F\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass BaseSemanticHead(BaseModule, metaclass=ABCMeta):\n    \"\"\"Base module of Semantic Head.\n\n    Args:\n        num_classes (int): the number of classes.\n        seg_rescale_factor (float): the rescale factor for ``gt_sem_seg``,\n            which equals to ``1 / output_strides``. The output_strides is\n            for ``seg_preds``. Defaults to  1 / 4.\n        init_cfg (Optional[Union[:obj:`ConfigDict`, dict]]): the initialization\n            config.\n        loss_seg (Union[:obj:`ConfigDict`, dict]): the loss of the semantic\n            head.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 seg_rescale_factor: float = 1 / 4.,\n                 loss_seg: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     ignore_index=255,\n                     loss_weight=1.0),\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.loss_seg = MODELS.build(loss_seg)\n        self.num_classes = num_classes\n        self.seg_rescale_factor = seg_rescale_factor\n\n    @abstractmethod\n    def forward(self, x: Union[Tensor, Tuple[Tensor]]) -> Dict[str, Tensor]:\n        \"\"\"Placeholder of forward function.\n\n        Args:\n            x (Tensor): Feature maps.\n\n        Returns:\n            Dict[str, Tensor]: A dictionary, including features\n                and predicted scores. Required keys: 'seg_preds'\n                and 'feats'.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def loss(self, x: Union[Tensor, Tuple[Tensor]],\n             batch_data_samples: SampleList) -> Dict[str, Tensor]:\n        \"\"\"\n        Args:\n            x (Union[Tensor, Tuple[Tensor]]): Feature maps.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Args:\n            x (Tensor): Feature maps.\n\n        Returns:\n            Dict[str, Tensor]: The loss of semantic head.\n        \"\"\"\n        pass\n\n    def predict(self,\n                x: Union[Tensor, Tuple[Tensor]],\n                batch_img_metas: List[dict],\n                rescale: bool = False) -> List[Tensor]:\n        \"\"\"Test without Augmentation.\n\n        Args:\n            x (Union[Tensor, Tuple[Tensor]]): Feature maps.\n            batch_img_metas (List[dict]): List of image information.\n            rescale (bool): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[Tensor]: semantic segmentation logits.\n        \"\"\"\n        seg_preds = self.forward(x)['seg_preds']\n        seg_preds = F.interpolate(\n            seg_preds,\n            size=batch_img_metas[0]['batch_input_shape'],\n            mode='bilinear',\n            align_corners=False)\n        seg_preds = [seg_preds[i] for i in range(len(batch_img_metas))]\n\n        if rescale:\n            seg_pred_list = []\n            for i in range(len(batch_img_metas)):\n                h, w = batch_img_metas[i]['img_shape']\n                seg_pred = seg_preds[i][:, :h, :w]\n\n                h, w = batch_img_metas[i]['ori_shape']\n                seg_pred = F.interpolate(\n                    seg_pred[None],\n                    size=(h, w),\n                    mode='bilinear',\n                    align_corners=False)[0]\n                seg_pred_list.append(seg_pred)\n        else:\n            seg_pred_list = seg_preds\n\n        return seg_pred_list\n"
  },
  {
    "path": "mmdet/models/seg_heads/panoptic_fpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.model import ModuleList\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\nfrom ..layers import ConvUpsample\nfrom ..utils import interpolate_as\nfrom .base_semantic_head import BaseSemanticHead\n\n\n@MODELS.register_module()\nclass PanopticFPNHead(BaseSemanticHead):\n    \"\"\"PanopticFPNHead used in Panoptic FPN.\n\n    In this head, the number of output channels is ``num_stuff_classes\n    + 1``, including all stuff classes and one thing class. The stuff\n    classes will be reset from ``0`` to ``num_stuff_classes - 1``, the\n    thing classes will be merged to ``num_stuff_classes``-th channel.\n\n    Arg:\n        num_things_classes (int): Number of thing classes. Default: 80.\n        num_stuff_classes (int): Number of stuff classes. Default: 53.\n        in_channels (int): Number of channels in the input feature\n            map.\n        inner_channels (int): Number of channels in inner features.\n        start_level (int): The start level of the input features\n            used in PanopticFPN.\n        end_level (int): The end level of the used features, the\n            ``end_level``-th layer will not be used.\n        conv_cfg (Optional[Union[ConfigDict, dict]]): Dictionary to construct\n            and config conv layer.\n        norm_cfg (Union[ConfigDict, dict]): Dictionary to construct and config\n            norm layer. Use ``GN`` by default.\n        init_cfg (Optional[Union[ConfigDict, dict]]): Initialization config\n            dict.\n        loss_seg (Union[ConfigDict, dict]): the loss of the semantic head.\n    \"\"\"\n\n    def __init__(self,\n                 num_things_classes: int = 80,\n                 num_stuff_classes: int = 53,\n                 in_channels: int = 256,\n                 inner_channels: int = 128,\n                 start_level: int = 0,\n                 end_level: int = 4,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: ConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 loss_seg: ConfigType = dict(\n                     type='CrossEntropyLoss', ignore_index=-1,\n                     loss_weight=1.0),\n                 init_cfg: OptMultiConfig = None) -> None:\n        seg_rescale_factor = 1 / 2**(start_level + 2)\n        super().__init__(\n            num_classes=num_stuff_classes + 1,\n            seg_rescale_factor=seg_rescale_factor,\n            loss_seg=loss_seg,\n            init_cfg=init_cfg)\n        self.num_things_classes = num_things_classes\n        self.num_stuff_classes = num_stuff_classes\n        # Used feature layers are [start_level, end_level)\n        self.start_level = start_level\n        self.end_level = end_level\n        self.num_stages = end_level - start_level\n        self.inner_channels = inner_channels\n\n        self.conv_upsample_layers = ModuleList()\n        for i in range(start_level, end_level):\n            self.conv_upsample_layers.append(\n                ConvUpsample(\n                    in_channels,\n                    inner_channels,\n                    num_layers=i if i > 0 else 1,\n                    num_upsample=i if i > 0 else 0,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                ))\n        self.conv_logits = nn.Conv2d(inner_channels, self.num_classes, 1)\n\n    def _set_things_to_void(self, gt_semantic_seg: Tensor) -> Tensor:\n        \"\"\"Merge thing classes to one class.\n\n        In PanopticFPN, the background labels will be reset from `0` to\n        `self.num_stuff_classes-1`, the foreground labels will be merged to\n        `self.num_stuff_classes`-th channel.\n        \"\"\"\n        gt_semantic_seg = gt_semantic_seg.int()\n        fg_mask = gt_semantic_seg < self.num_things_classes\n        bg_mask = (gt_semantic_seg >= self.num_things_classes) * (\n            gt_semantic_seg < self.num_things_classes + self.num_stuff_classes)\n\n        new_gt_seg = torch.clone(gt_semantic_seg)\n        new_gt_seg = torch.where(bg_mask,\n                                 gt_semantic_seg - self.num_things_classes,\n                                 new_gt_seg)\n        new_gt_seg = torch.where(fg_mask,\n                                 fg_mask.int() * self.num_stuff_classes,\n                                 new_gt_seg)\n        return new_gt_seg\n\n    def loss(self, x: Union[Tensor, Tuple[Tensor]],\n             batch_data_samples: SampleList) -> Dict[str, Tensor]:\n        \"\"\"\n        Args:\n            x (Union[Tensor, Tuple[Tensor]]): Feature maps.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            Dict[str, Tensor]: The loss of semantic head.\n        \"\"\"\n        seg_preds = self(x)['seg_preds']\n        gt_semantic_segs = [\n            data_sample.gt_sem_seg.sem_seg\n            for data_sample in batch_data_samples\n        ]\n\n        gt_semantic_segs = torch.stack(gt_semantic_segs)\n        if self.seg_rescale_factor != 1.0:\n            gt_semantic_segs = F.interpolate(\n                gt_semantic_segs.float(),\n                scale_factor=self.seg_rescale_factor,\n                mode='nearest').squeeze(1)\n\n        # Things classes will be merged to one class in PanopticFPN.\n        gt_semantic_segs = self._set_things_to_void(gt_semantic_segs)\n\n        if seg_preds.shape[-2:] != gt_semantic_segs.shape[-2:]:\n            seg_preds = interpolate_as(seg_preds, gt_semantic_segs)\n        seg_preds = seg_preds.permute((0, 2, 3, 1))\n\n        loss_seg = self.loss_seg(\n            seg_preds.reshape(-1, self.num_classes),  # => [NxHxW, C]\n            gt_semantic_segs.reshape(-1).long())\n\n        return dict(loss_seg=loss_seg)\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights.\"\"\"\n        super().init_weights()\n        nn.init.normal_(self.conv_logits.weight.data, 0, 0.01)\n        self.conv_logits.bias.data.zero_()\n\n    def forward(self, x: Tuple[Tensor]) -> Dict[str, Tensor]:\n        \"\"\"Forward.\n\n        Args:\n            x (Tuple[Tensor]): Multi scale Feature maps.\n\n        Returns:\n            dict[str, Tensor]: semantic segmentation predictions and\n                feature maps.\n        \"\"\"\n        # the number of subnets must be not more than\n        # the length of features.\n        assert self.num_stages <= len(x)\n\n        feats = []\n        for i, layer in enumerate(self.conv_upsample_layers):\n            f = layer(x[self.start_level + i])\n            feats.append(f)\n\n        seg_feats = torch.sum(torch.stack(feats, dim=0), dim=0)\n        seg_preds = self.conv_logits(seg_feats)\n        out = dict(seg_preds=seg_preds, seg_feats=seg_feats)\n        return out\n"
  },
  {
    "path": "mmdet/models/seg_heads/panoptic_fusion_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_panoptic_fusion_head import \\\n    BasePanopticFusionHead  # noqa: F401,F403\nfrom .heuristic_fusion_head import HeuristicFusionHead  # noqa: F401,F403\nfrom .maskformer_fusion_head import MaskFormerFusionHead  # noqa: F401,F403\n"
  },
  {
    "path": "mmdet/models/seg_heads/panoptic_fusion_heads/base_panoptic_fusion_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\n\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass BasePanopticFusionHead(BaseModule, metaclass=ABCMeta):\n    \"\"\"Base class for panoptic heads.\"\"\"\n\n    def __init__(self,\n                 num_things_classes: int = 80,\n                 num_stuff_classes: int = 53,\n                 test_cfg: OptConfigType = None,\n                 loss_panoptic: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.num_things_classes = num_things_classes\n        self.num_stuff_classes = num_stuff_classes\n        self.num_classes = num_things_classes + num_stuff_classes\n        self.test_cfg = test_cfg\n\n        if loss_panoptic:\n            self.loss_panoptic = MODELS.build(loss_panoptic)\n        else:\n            self.loss_panoptic = None\n\n    @property\n    def with_loss(self) -> bool:\n        \"\"\"bool: whether the panoptic head contains loss function.\"\"\"\n        return self.loss_panoptic is not None\n\n    @abstractmethod\n    def loss(self, **kwargs):\n        \"\"\"Loss function.\"\"\"\n\n    @abstractmethod\n    def predict(self, **kwargs):\n        \"\"\"Predict function.\"\"\"\n"
  },
  {
    "path": "mmdet/models/seg_heads/panoptic_fusion_heads/heuristic_fusion_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nfrom mmengine.structures import InstanceData, PixelData\nfrom torch import Tensor\n\nfrom mmdet.evaluation.functional import INSTANCE_OFFSET\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList, OptConfigType, OptMultiConfig, PixelList\nfrom .base_panoptic_fusion_head import BasePanopticFusionHead\n\n\n@MODELS.register_module()\nclass HeuristicFusionHead(BasePanopticFusionHead):\n    \"\"\"Fusion Head with Heuristic method.\"\"\"\n\n    def __init__(self,\n                 num_things_classes: int = 80,\n                 num_stuff_classes: int = 53,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        super().__init__(\n            num_things_classes=num_things_classes,\n            num_stuff_classes=num_stuff_classes,\n            test_cfg=test_cfg,\n            loss_panoptic=None,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def loss(self, **kwargs) -> dict:\n        \"\"\"HeuristicFusionHead has no training loss.\"\"\"\n        return dict()\n\n    def _lay_masks(self,\n                   mask_results: InstanceData,\n                   overlap_thr: float = 0.5) -> Tensor:\n        \"\"\"Lay instance masks to a result map.\n\n        Args:\n            mask_results (:obj:`InstanceData`): Instance segmentation results,\n                each contains ``bboxes``, ``labels``, ``scores`` and ``masks``.\n            overlap_thr (float): Threshold to determine whether two masks\n                overlap. default: 0.5.\n\n        Returns:\n            Tensor: The result map, (H, W).\n        \"\"\"\n        bboxes = mask_results.bboxes\n        scores = mask_results.scores\n        labels = mask_results.labels\n        masks = mask_results.masks\n\n        num_insts = bboxes.shape[0]\n        id_map = torch.zeros(\n            masks.shape[-2:], device=bboxes.device, dtype=torch.long)\n        if num_insts == 0:\n            return id_map, labels\n\n        # Sort by score to use heuristic fusion\n        order = torch.argsort(-scores)\n        bboxes = bboxes[order]\n        labels = labels[order]\n        segm_masks = masks[order]\n\n        instance_id = 1\n        left_labels = []\n        for idx in range(bboxes.shape[0]):\n            _cls = labels[idx]\n            _mask = segm_masks[idx]\n            instance_id_map = torch.ones_like(\n                _mask, dtype=torch.long) * instance_id\n            area = _mask.sum()\n            if area == 0:\n                continue\n\n            pasted = id_map > 0\n            intersect = (_mask * pasted).sum()\n            if (intersect / (area + 1e-5)) > overlap_thr:\n                continue\n\n            _part = _mask * (~pasted)\n            id_map = torch.where(_part, instance_id_map, id_map)\n            left_labels.append(_cls)\n            instance_id += 1\n\n        if len(left_labels) > 0:\n            instance_labels = torch.stack(left_labels)\n        else:\n            instance_labels = bboxes.new_zeros((0, ), dtype=torch.long)\n        assert instance_id == (len(instance_labels) + 1)\n        return id_map, instance_labels\n\n    def _predict_single(self, mask_results: InstanceData, seg_preds: Tensor,\n                        **kwargs) -> PixelData:\n        \"\"\"Fuse the results of instance and semantic segmentations.\n\n        Args:\n            mask_results (:obj:`InstanceData`): Instance segmentation results,\n                each contains ``bboxes``, ``labels``, ``scores`` and ``masks``.\n            seg_preds (Tensor): The semantic segmentation results,\n                (num_stuff + 1, H, W).\n\n        Returns:\n            Tensor: The panoptic segmentation result, (H, W).\n        \"\"\"\n        id_map, labels = self._lay_masks(mask_results,\n                                         self.test_cfg.mask_overlap)\n\n        seg_results = seg_preds.argmax(dim=0)\n        seg_results = seg_results + self.num_things_classes\n\n        pan_results = seg_results\n        instance_id = 1\n        for idx in range(len(mask_results)):\n            _mask = id_map == (idx + 1)\n            if _mask.sum() == 0:\n                continue\n            _cls = labels[idx]\n            # simply trust detection\n            segment_id = _cls + instance_id * INSTANCE_OFFSET\n            pan_results[_mask] = segment_id\n            instance_id += 1\n\n        ids, counts = torch.unique(\n            pan_results % INSTANCE_OFFSET, return_counts=True)\n        stuff_ids = ids[ids >= self.num_things_classes]\n        stuff_counts = counts[ids >= self.num_things_classes]\n        ignore_stuff_ids = stuff_ids[\n            stuff_counts < self.test_cfg.stuff_area_limit]\n\n        assert pan_results.ndim == 2\n        pan_results[(pan_results.unsqueeze(2) == ignore_stuff_ids.reshape(\n            1, 1, -1)).any(dim=2)] = self.num_classes\n\n        pan_results = PixelData(sem_seg=pan_results[None].int())\n        return pan_results\n\n    def predict(self, mask_results_list: InstanceList,\n                seg_preds_list: List[Tensor], **kwargs) -> PixelList:\n        \"\"\"Predict results by fusing the results of instance and semantic\n        segmentations.\n\n        Args:\n            mask_results_list (list[:obj:`InstanceData`]): Instance\n                segmentation results, each contains ``bboxes``, ``labels``,\n                ``scores`` and ``masks``.\n            seg_preds_list (Tensor): List of semantic segmentation results.\n\n        Returns:\n            List[PixelData]: Panoptic segmentation result.\n        \"\"\"\n        results_list = [\n            self._predict_single(mask_results_list[i], seg_preds_list[i])\n            for i in range(len(mask_results_list))\n        ]\n\n        return results_list\n"
  },
  {
    "path": "mmdet/models/seg_heads/panoptic_fusion_heads/maskformer_fusion_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData, PixelData\nfrom torch import Tensor\n\nfrom mmdet.evaluation.functional import INSTANCE_OFFSET\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.mask import mask2bbox\nfrom mmdet.utils import OptConfigType, OptMultiConfig\nfrom .base_panoptic_fusion_head import BasePanopticFusionHead\n\n\n@MODELS.register_module()\nclass MaskFormerFusionHead(BasePanopticFusionHead):\n    \"\"\"MaskFormer fusion head which postprocesses results for panoptic\n    segmentation, instance segmentation and semantic segmentation.\"\"\"\n\n    def __init__(self,\n                 num_things_classes: int = 80,\n                 num_stuff_classes: int = 53,\n                 test_cfg: OptConfigType = None,\n                 loss_panoptic: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs):\n        super().__init__(\n            num_things_classes=num_things_classes,\n            num_stuff_classes=num_stuff_classes,\n            test_cfg=test_cfg,\n            loss_panoptic=loss_panoptic,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def loss(self, **kwargs):\n        \"\"\"MaskFormerFusionHead has no training loss.\"\"\"\n        return dict()\n\n    def panoptic_postprocess(self, mask_cls: Tensor,\n                             mask_pred: Tensor) -> PixelData:\n        \"\"\"Panoptic segmengation inference.\n\n        Args:\n            mask_cls (Tensor): Classfication outputs of shape\n                (num_queries, cls_out_channels) for a image.\n                Note `cls_out_channels` should includes\n                background.\n            mask_pred (Tensor): Mask outputs of shape\n                (num_queries, h, w) for a image.\n\n        Returns:\n            :obj:`PixelData`: Panoptic segment result of shape \\\n                (h, w), each element in Tensor means: \\\n                ``segment_id = _cls + instance_id * INSTANCE_OFFSET``.\n        \"\"\"\n        object_mask_thr = self.test_cfg.get('object_mask_thr', 0.8)\n        iou_thr = self.test_cfg.get('iou_thr', 0.8)\n        filter_low_score = self.test_cfg.get('filter_low_score', False)\n\n        scores, labels = F.softmax(mask_cls, dim=-1).max(-1)\n        mask_pred = mask_pred.sigmoid()\n\n        keep = labels.ne(self.num_classes) & (scores > object_mask_thr)\n        cur_scores = scores[keep]\n        cur_classes = labels[keep]\n        cur_masks = mask_pred[keep]\n\n        cur_prob_masks = cur_scores.view(-1, 1, 1) * cur_masks\n\n        h, w = cur_masks.shape[-2:]\n        panoptic_seg = torch.full((h, w),\n                                  self.num_classes,\n                                  dtype=torch.int32,\n                                  device=cur_masks.device)\n        if cur_masks.shape[0] == 0:\n            # We didn't detect any mask :(\n            pass\n        else:\n            cur_mask_ids = cur_prob_masks.argmax(0)\n            instance_id = 1\n            for k in range(cur_classes.shape[0]):\n                pred_class = int(cur_classes[k].item())\n                isthing = pred_class < self.num_things_classes\n                mask = cur_mask_ids == k\n                mask_area = mask.sum().item()\n                original_area = (cur_masks[k] >= 0.5).sum().item()\n\n                if filter_low_score:\n                    mask = mask & (cur_masks[k] >= 0.5)\n\n                if mask_area > 0 and original_area > 0:\n                    if mask_area / original_area < iou_thr:\n                        continue\n\n                    if not isthing:\n                        # different stuff regions of same class will be\n                        # merged here, and stuff share the instance_id 0.\n                        panoptic_seg[mask] = pred_class\n                    else:\n                        panoptic_seg[mask] = (\n                            pred_class + instance_id * INSTANCE_OFFSET)\n                        instance_id += 1\n\n        return PixelData(sem_seg=panoptic_seg[None])\n\n    def semantic_postprocess(self, mask_cls: Tensor,\n                             mask_pred: Tensor) -> PixelData:\n        \"\"\"Semantic segmengation postprocess.\n\n        Args:\n            mask_cls (Tensor): Classfication outputs of shape\n                (num_queries, cls_out_channels) for a image.\n                Note `cls_out_channels` should includes\n                background.\n            mask_pred (Tensor): Mask outputs of shape\n                (num_queries, h, w) for a image.\n\n        Returns:\n            :obj:`PixelData`: Semantic segment result.\n        \"\"\"\n        # TODO add semantic segmentation result\n        raise NotImplementedError\n\n    def instance_postprocess(self, mask_cls: Tensor,\n                             mask_pred: Tensor) -> InstanceData:\n        \"\"\"Instance segmengation postprocess.\n\n        Args:\n            mask_cls (Tensor): Classfication outputs of shape\n                (num_queries, cls_out_channels) for a image.\n                Note `cls_out_channels` should includes\n                background.\n            mask_pred (Tensor): Mask outputs of shape\n                (num_queries, h, w) for a image.\n\n        Returns:\n            :obj:`InstanceData`: Instance segmentation results.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        max_per_image = self.test_cfg.get('max_per_image', 100)\n        num_queries = mask_cls.shape[0]\n        # shape (num_queries, num_class)\n        scores = F.softmax(mask_cls, dim=-1)[:, :-1]\n        # shape (num_queries * num_class, )\n        labels = torch.arange(self.num_classes, device=mask_cls.device).\\\n            unsqueeze(0).repeat(num_queries, 1).flatten(0, 1)\n        scores_per_image, top_indices = scores.flatten(0, 1).topk(\n            max_per_image, sorted=False)\n        labels_per_image = labels[top_indices]\n\n        query_indices = top_indices // self.num_classes\n        mask_pred = mask_pred[query_indices]\n\n        # extract things\n        is_thing = labels_per_image < self.num_things_classes\n        scores_per_image = scores_per_image[is_thing]\n        labels_per_image = labels_per_image[is_thing]\n        mask_pred = mask_pred[is_thing]\n\n        mask_pred_binary = (mask_pred > 0).float()\n        mask_scores_per_image = (mask_pred.sigmoid() *\n                                 mask_pred_binary).flatten(1).sum(1) / (\n                                     mask_pred_binary.flatten(1).sum(1) + 1e-6)\n        det_scores = scores_per_image * mask_scores_per_image\n        mask_pred_binary = mask_pred_binary.bool()\n        bboxes = mask2bbox(mask_pred_binary)\n\n        results = InstanceData()\n        results.bboxes = bboxes\n        results.labels = labels_per_image\n        results.scores = det_scores\n        results.masks = mask_pred_binary\n        return results\n\n    def predict(self,\n                mask_cls_results: Tensor,\n                mask_pred_results: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = False,\n                **kwargs) -> List[dict]:\n        \"\"\"Test segment without test-time aumengtation.\n\n        Only the output of last decoder layers was used.\n\n        Args:\n            mask_cls_results (Tensor): Mask classification logits,\n                shape (batch_size, num_queries, cls_out_channels).\n                Note `cls_out_channels` should includes background.\n            mask_pred_results (Tensor): Mask logits, shape\n                (batch_size, num_queries, h, w).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): If True, return boxes in\n                original image space. Default False.\n\n        Returns:\n            list[dict]: Instance segmentation \\\n                results and panoptic segmentation results for each \\\n                image.\n\n            .. code-block:: none\n\n                [\n                    {\n                        'pan_results': PixelData,\n                        'ins_results': InstanceData,\n                        # semantic segmentation results are not supported yet\n                        'sem_results': PixelData\n                    },\n                    ...\n                ]\n        \"\"\"\n        batch_img_metas = [\n            data_sample.metainfo for data_sample in batch_data_samples\n        ]\n        panoptic_on = self.test_cfg.get('panoptic_on', True)\n        semantic_on = self.test_cfg.get('semantic_on', False)\n        instance_on = self.test_cfg.get('instance_on', False)\n        assert not semantic_on, 'segmantic segmentation '\\\n            'results are not supported yet.'\n\n        results = []\n        for mask_cls_result, mask_pred_result, meta in zip(\n                mask_cls_results, mask_pred_results, batch_img_metas):\n            # remove padding\n            img_height, img_width = meta['img_shape'][:2]\n            mask_pred_result = mask_pred_result[:, :img_height, :img_width]\n\n            if rescale:\n                # return result in original resolution\n                ori_height, ori_width = meta['ori_shape'][:2]\n                mask_pred_result = F.interpolate(\n                    mask_pred_result[:, None],\n                    size=(ori_height, ori_width),\n                    mode='bilinear',\n                    align_corners=False)[:, 0]\n\n            result = dict()\n            if panoptic_on:\n                pan_results = self.panoptic_postprocess(\n                    mask_cls_result, mask_pred_result)\n                result['pan_results'] = pan_results\n\n            if instance_on:\n                ins_results = self.instance_postprocess(\n                    mask_cls_result, mask_pred_result)\n                result['ins_results'] = ins_results\n\n            if semantic_on:\n                sem_results = self.semantic_postprocess(\n                    mask_cls_result, mask_pred_result)\n                result['sem_results'] = sem_results\n\n            results.append(result)\n\n        return results\n"
  },
  {
    "path": "mmdet/models/task_modules/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .assigners import *  # noqa: F401,F403\nfrom .builder import (ANCHOR_GENERATORS, BBOX_ASSIGNERS, BBOX_CODERS,\n                      BBOX_SAMPLERS, IOU_CALCULATORS, MATCH_COSTS,\n                      PRIOR_GENERATORS, build_anchor_generator, build_assigner,\n                      build_bbox_coder, build_iou_calculator, build_match_cost,\n                      build_prior_generator, build_sampler)\nfrom .coders import *  # noqa: F401,F403\nfrom .prior_generators import *  # noqa: F401,F403\nfrom .samplers import *  # noqa: F401,F403\nfrom .tracking import *  # noqa: F401,F403\n\n__all__ = [\n    'ANCHOR_GENERATORS', 'PRIOR_GENERATORS', 'BBOX_ASSIGNERS', 'BBOX_SAMPLERS',\n    'MATCH_COSTS', 'BBOX_CODERS', 'IOU_CALCULATORS', 'build_anchor_generator',\n    'build_prior_generator', 'build_assigner', 'build_sampler',\n    'build_iou_calculator', 'build_match_cost', 'build_bbox_coder'\n]\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .approx_max_iou_assigner import ApproxMaxIoUAssigner\nfrom .assign_result import AssignResult\nfrom .atss_assigner import ATSSAssigner\nfrom .base_assigner import BaseAssigner\nfrom .center_region_assigner import CenterRegionAssigner\nfrom .dynamic_soft_label_assigner import DynamicSoftLabelAssigner\nfrom .grid_assigner import GridAssigner\nfrom .hungarian_assigner import HungarianAssigner\nfrom .iou2d_calculator import BboxOverlaps2D, BboxOverlaps2D_GLIP\nfrom .match_cost import (BBoxL1Cost, BinaryFocalLossCost, ClassificationCost,\n                         CrossEntropyLossCost, DiceCost, FocalLossCost,\n                         IoUCost)\nfrom .max_iou_assigner import MaxIoUAssigner\nfrom .multi_instance_assigner import MultiInstanceAssigner\nfrom .point_assigner import PointAssigner\nfrom .region_assigner import RegionAssigner\nfrom .sim_ota_assigner import SimOTAAssigner\nfrom .task_aligned_assigner import TaskAlignedAssigner\nfrom .topk_hungarian_assigner import TopkHungarianAssigner\nfrom .uniform_assigner import UniformAssigner\n\n__all__ = [\n    'BaseAssigner', 'BinaryFocalLossCost', 'MaxIoUAssigner',\n    'ApproxMaxIoUAssigner', 'AssignResult', 'PointAssigner', 'ATSSAssigner',\n    'CenterRegionAssigner', 'GridAssigner', 'HungarianAssigner',\n    'RegionAssigner', 'UniformAssigner', 'SimOTAAssigner',\n    'TaskAlignedAssigner', 'TopkHungarianAssigner', 'BBoxL1Cost',\n    'ClassificationCost', 'CrossEntropyLossCost', 'DiceCost', 'FocalLossCost',\n    'IoUCost', 'BboxOverlaps2D', 'DynamicSoftLabelAssigner',\n    'MultiInstanceAssigner', 'BboxOverlaps2D_GLIP'\n]\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/approx_max_iou_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Union\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom .assign_result import AssignResult\nfrom .max_iou_assigner import MaxIoUAssigner\n\n\n@TASK_UTILS.register_module()\nclass ApproxMaxIoUAssigner(MaxIoUAssigner):\n    \"\"\"Assign a corresponding gt bbox or background to each bbox.\n\n    Each proposals will be assigned with an integer indicating the ground truth\n     index. (semi-positive index: gt label (0-based), -1: background)\n\n    - -1: negative sample, no assigned gt\n    - semi-positive integer: positive sample, index (0-based) of assigned gt\n\n    Args:\n        pos_iou_thr (float): IoU threshold for positive bboxes.\n        neg_iou_thr (float or tuple): IoU threshold for negative bboxes.\n        min_pos_iou (float): Minimum iou for a bbox to be considered as a\n            positive bbox. Positive samples can have smaller IoU than\n            pos_iou_thr due to the 4th step (assign max IoU sample to each gt).\n        gt_max_assign_all (bool): Whether to assign all bboxes with the same\n            highest overlap with some gt to that gt.\n        ignore_iof_thr (float): IoF threshold for ignoring bboxes (if\n            `gt_bboxes_ignore` is specified). Negative values mean not\n            ignoring any bboxes.\n        ignore_wrt_candidates (bool): Whether to compute the iof between\n            `bboxes` and `gt_bboxes_ignore`, or the contrary.\n        match_low_quality (bool): Whether to allow quality matches. This is\n            usually allowed for RPN and single stage detectors, but not allowed\n            in the second stage.\n        gpu_assign_thr (int): The upper bound of the number of GT for GPU\n            assign. When the number of gt is above this threshold, will assign\n            on CPU device. Negative values mean not assign on CPU.\n        iou_calculator (:obj:`ConfigDict` or dict): Config of overlaps\n            Calculator.\n    \"\"\"\n\n    def __init__(\n        self,\n        pos_iou_thr: float,\n        neg_iou_thr: Union[float, tuple],\n        min_pos_iou: float = .0,\n        gt_max_assign_all: bool = True,\n        ignore_iof_thr: float = -1,\n        ignore_wrt_candidates: bool = True,\n        match_low_quality: bool = True,\n        gpu_assign_thr: int = -1,\n        iou_calculator: Union[ConfigDict, dict] = dict(type='BboxOverlaps2D')\n    ) -> None:\n        self.pos_iou_thr = pos_iou_thr\n        self.neg_iou_thr = neg_iou_thr\n        self.min_pos_iou = min_pos_iou\n        self.gt_max_assign_all = gt_max_assign_all\n        self.ignore_iof_thr = ignore_iof_thr\n        self.ignore_wrt_candidates = ignore_wrt_candidates\n        self.gpu_assign_thr = gpu_assign_thr\n        self.match_low_quality = match_low_quality\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to approxs.\n\n        This method assign a gt bbox to each group of approxs (bboxes),\n        each group of approxs is represent by a base approx (bbox) and\n        will be assigned with -1, or a semi-positive number.\n        background_label (-1) means negative sample,\n        semi-positive number is the index (0-based) of assigned gt.\n        The assignment is done in following steps, the order matters.\n\n        1. assign every bbox to background_label (-1)\n        2. use the max IoU of each group of approxs to assign\n        2. assign proposals whose iou with all gts < neg_iou_thr to background\n        3. for each bbox, if the iou with its nearest gt >= pos_iou_thr,\n           assign it to that bbox\n        4. for each gt bbox, assign its nearest proposals (may be more than\n           one) to itself\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). ``approxs`` means the\n                group of approxs aligned with ``priors``, has shape\n                (n, num_approxs, 4).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n        squares = pred_instances.priors\n        approxs = pred_instances.approxs\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        gt_bboxes_ignore = None if gt_instances_ignore is None else \\\n            gt_instances_ignore.get('bboxes', None)\n        approxs_per_octave = approxs.size(1)\n\n        num_squares = squares.size(0)\n        num_gts = gt_bboxes.size(0)\n\n        if num_squares == 0 or num_gts == 0:\n            # No predictions and/or truth, return empty assignment\n            overlaps = approxs.new(num_gts, num_squares)\n            assign_result = self.assign_wrt_overlaps(overlaps, gt_labels)\n            return assign_result\n\n        # re-organize anchors by approxs_per_octave x num_squares\n        approxs = torch.transpose(approxs, 0, 1).contiguous().view(-1, 4)\n        assign_on_cpu = True if (self.gpu_assign_thr > 0) and (\n            num_gts > self.gpu_assign_thr) else False\n        # compute overlap and assign gt on CPU when number of GT is large\n        if assign_on_cpu:\n            device = approxs.device\n            approxs = approxs.cpu()\n            gt_bboxes = gt_bboxes.cpu()\n            if gt_bboxes_ignore is not None:\n                gt_bboxes_ignore = gt_bboxes_ignore.cpu()\n            if gt_labels is not None:\n                gt_labels = gt_labels.cpu()\n        all_overlaps = self.iou_calculator(approxs, gt_bboxes)\n\n        overlaps, _ = all_overlaps.view(approxs_per_octave, num_squares,\n                                        num_gts).max(dim=0)\n        overlaps = torch.transpose(overlaps, 0, 1)\n\n        if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None\n                and gt_bboxes_ignore.numel() > 0 and squares.numel() > 0):\n            if self.ignore_wrt_candidates:\n                ignore_overlaps = self.iou_calculator(\n                    squares, gt_bboxes_ignore, mode='iof')\n                ignore_max_overlaps, _ = ignore_overlaps.max(dim=1)\n            else:\n                ignore_overlaps = self.iou_calculator(\n                    gt_bboxes_ignore, squares, mode='iof')\n                ignore_max_overlaps, _ = ignore_overlaps.max(dim=0)\n            overlaps[:, ignore_max_overlaps > self.ignore_iof_thr] = -1\n\n        assign_result = self.assign_wrt_overlaps(overlaps, gt_labels)\n        if assign_on_cpu:\n            assign_result.gt_inds = assign_result.gt_inds.to(device)\n            assign_result.max_overlaps = assign_result.max_overlaps.to(device)\n            if assign_result.labels is not None:\n                assign_result.labels = assign_result.labels.to(device)\n        return assign_result\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/assign_result.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.utils import util_mixins\n\n\nclass AssignResult(util_mixins.NiceRepr):\n    \"\"\"Stores assignments between predicted and truth boxes.\n\n    Attributes:\n        num_gts (int): the number of truth boxes considered when computing this\n            assignment\n        gt_inds (Tensor): for each predicted box indicates the 1-based\n            index of the assigned truth box. 0 means unassigned and -1 means\n            ignore.\n        max_overlaps (Tensor): the iou between the predicted box and its\n            assigned truth box.\n        labels (Tensor): If specified, for each predicted box\n            indicates the category label of the assigned truth box.\n\n    Example:\n        >>> # An assign result between 4 predicted boxes and 9 true boxes\n        >>> # where only two boxes were assigned.\n        >>> num_gts = 9\n        >>> max_overlaps = torch.LongTensor([0, .5, .9, 0])\n        >>> gt_inds = torch.LongTensor([-1, 1, 2, 0])\n        >>> labels = torch.LongTensor([0, 3, 4, 0])\n        >>> self = AssignResult(num_gts, gt_inds, max_overlaps, labels)\n        >>> print(str(self))  # xdoctest: +IGNORE_WANT\n        <AssignResult(num_gts=9, gt_inds.shape=(4,), max_overlaps.shape=(4,),\n                      labels.shape=(4,))>\n        >>> # Force addition of gt labels (when adding gt as proposals)\n        >>> new_labels = torch.LongTensor([3, 4, 5])\n        >>> self.add_gt_(new_labels)\n        >>> print(str(self))  # xdoctest: +IGNORE_WANT\n        <AssignResult(num_gts=9, gt_inds.shape=(7,), max_overlaps.shape=(7,),\n                      labels.shape=(7,))>\n    \"\"\"\n\n    def __init__(self, num_gts: int, gt_inds: Tensor, max_overlaps: Tensor,\n                 labels: Tensor) -> None:\n        self.num_gts = num_gts\n        self.gt_inds = gt_inds\n        self.max_overlaps = max_overlaps\n        self.labels = labels\n        # Interface for possible user-defined properties\n        self._extra_properties = {}\n\n    @property\n    def num_preds(self):\n        \"\"\"int: the number of predictions in this assignment\"\"\"\n        return len(self.gt_inds)\n\n    def set_extra_property(self, key, value):\n        \"\"\"Set user-defined new property.\"\"\"\n        assert key not in self.info\n        self._extra_properties[key] = value\n\n    def get_extra_property(self, key):\n        \"\"\"Get user-defined property.\"\"\"\n        return self._extra_properties.get(key, None)\n\n    @property\n    def info(self):\n        \"\"\"dict: a dictionary of info about the object\"\"\"\n        basic_info = {\n            'num_gts': self.num_gts,\n            'num_preds': self.num_preds,\n            'gt_inds': self.gt_inds,\n            'max_overlaps': self.max_overlaps,\n            'labels': self.labels,\n        }\n        basic_info.update(self._extra_properties)\n        return basic_info\n\n    def __nice__(self):\n        \"\"\"str: a \"nice\" summary string describing this assign result\"\"\"\n        parts = []\n        parts.append(f'num_gts={self.num_gts!r}')\n        if self.gt_inds is None:\n            parts.append(f'gt_inds={self.gt_inds!r}')\n        else:\n            parts.append(f'gt_inds.shape={tuple(self.gt_inds.shape)!r}')\n        if self.max_overlaps is None:\n            parts.append(f'max_overlaps={self.max_overlaps!r}')\n        else:\n            parts.append('max_overlaps.shape='\n                         f'{tuple(self.max_overlaps.shape)!r}')\n        if self.labels is None:\n            parts.append(f'labels={self.labels!r}')\n        else:\n            parts.append(f'labels.shape={tuple(self.labels.shape)!r}')\n        return ', '.join(parts)\n\n    @classmethod\n    def random(cls, **kwargs):\n        \"\"\"Create random AssignResult for tests or debugging.\n\n        Args:\n            num_preds: number of predicted boxes\n            num_gts: number of true boxes\n            p_ignore (float): probability of a predicted box assigned to an\n                ignored truth\n            p_assigned (float): probability of a predicted box not being\n                assigned\n            p_use_label (float | bool): with labels or not\n            rng (None | int | numpy.random.RandomState): seed or state\n\n        Returns:\n            :obj:`AssignResult`: Randomly generated assign results.\n\n        Example:\n            >>> from mmdet.models.task_modules.assigners.assign_result import *  # NOQA\n            >>> self = AssignResult.random()\n            >>> print(self.info)\n        \"\"\"\n        from ..samplers.sampling_result import ensure_rng\n        rng = ensure_rng(kwargs.get('rng', None))\n\n        num_gts = kwargs.get('num_gts', None)\n        num_preds = kwargs.get('num_preds', None)\n        p_ignore = kwargs.get('p_ignore', 0.3)\n        p_assigned = kwargs.get('p_assigned', 0.7)\n        num_classes = kwargs.get('num_classes', 3)\n\n        if num_gts is None:\n            num_gts = rng.randint(0, 8)\n        if num_preds is None:\n            num_preds = rng.randint(0, 16)\n\n        if num_gts == 0:\n            max_overlaps = torch.zeros(num_preds, dtype=torch.float32)\n            gt_inds = torch.zeros(num_preds, dtype=torch.int64)\n            labels = torch.zeros(num_preds, dtype=torch.int64)\n\n        else:\n            import numpy as np\n\n            # Create an overlap for each predicted box\n            max_overlaps = torch.from_numpy(rng.rand(num_preds))\n\n            # Construct gt_inds for each predicted box\n            is_assigned = torch.from_numpy(rng.rand(num_preds) < p_assigned)\n            # maximum number of assignments constraints\n            n_assigned = min(num_preds, min(num_gts, is_assigned.sum()))\n\n            assigned_idxs = np.where(is_assigned)[0]\n            rng.shuffle(assigned_idxs)\n            assigned_idxs = assigned_idxs[0:n_assigned]\n            assigned_idxs.sort()\n\n            is_assigned[:] = 0\n            is_assigned[assigned_idxs] = True\n\n            is_ignore = torch.from_numpy(\n                rng.rand(num_preds) < p_ignore) & is_assigned\n\n            gt_inds = torch.zeros(num_preds, dtype=torch.int64)\n\n            true_idxs = np.arange(num_gts)\n            rng.shuffle(true_idxs)\n            true_idxs = torch.from_numpy(true_idxs)\n            gt_inds[is_assigned] = true_idxs[:n_assigned].long()\n\n            gt_inds = torch.from_numpy(\n                rng.randint(1, num_gts + 1, size=num_preds))\n            gt_inds[is_ignore] = -1\n            gt_inds[~is_assigned] = 0\n            max_overlaps[~is_assigned] = 0\n\n            if num_classes == 0:\n                labels = torch.zeros(num_preds, dtype=torch.int64)\n            else:\n                labels = torch.from_numpy(\n                    # remind that we set FG labels to [0, num_class-1]\n                    # since mmdet v2.0\n                    # BG cat_id: num_class\n                    rng.randint(0, num_classes, size=num_preds))\n                labels[~is_assigned] = 0\n\n        self = cls(num_gts, gt_inds, max_overlaps, labels)\n        return self\n\n    def add_gt_(self, gt_labels):\n        \"\"\"Add ground truth as assigned results.\n\n        Args:\n            gt_labels (torch.Tensor): Labels of gt boxes\n        \"\"\"\n        self_inds = torch.arange(\n            1, len(gt_labels) + 1, dtype=torch.long, device=gt_labels.device)\n        self.gt_inds = torch.cat([self_inds, self.gt_inds])\n\n        self.max_overlaps = torch.cat(\n            [self.max_overlaps.new_ones(len(gt_labels)), self.max_overlaps])\n\n        self.labels = torch.cat([gt_labels, self.labels])\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/atss_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import List, Optional\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.utils import ConfigType\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\ndef bbox_center_distance(bboxes: Tensor, priors: Tensor) -> Tensor:\n    \"\"\"Compute the center distance between bboxes and priors.\n\n    Args:\n        bboxes (Tensor): Shape (n, 4) for , \"xyxy\" format.\n        priors (Tensor): Shape (n, 4) for priors, \"xyxy\" format.\n\n    Returns:\n        Tensor: Center distances between bboxes and priors.\n    \"\"\"\n    bbox_cx = (bboxes[:, 0] + bboxes[:, 2]) / 2.0\n    bbox_cy = (bboxes[:, 1] + bboxes[:, 3]) / 2.0\n    bbox_points = torch.stack((bbox_cx, bbox_cy), dim=1)\n\n    priors_cx = (priors[:, 0] + priors[:, 2]) / 2.0\n    priors_cy = (priors[:, 1] + priors[:, 3]) / 2.0\n    priors_points = torch.stack((priors_cx, priors_cy), dim=1)\n\n    distances = (priors_points[:, None, :] -\n                 bbox_points[None, :, :]).pow(2).sum(-1).sqrt()\n\n    return distances\n\n\n@TASK_UTILS.register_module()\nclass ATSSAssigner(BaseAssigner):\n    \"\"\"Assign a corresponding gt bbox or background to each prior.\n\n    Each proposals will be assigned with `0` or a positive integer\n    indicating the ground truth index.\n\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n\n    If ``alpha`` is not None, it means that the dynamic cost\n    ATSSAssigner is adopted, which is currently only used in the DDOD.\n\n    Args:\n        topk (int): number of priors selected in each level\n        alpha (float, optional): param of cost rate for each proposal only\n            in DDOD. Defaults to None.\n        iou_calculator (:obj:`ConfigDict` or dict): Config dict for iou\n            calculator. Defaults to ``dict(type='BboxOverlaps2D')``\n        ignore_iof_thr (float): IoF threshold for ignoring bboxes (if\n            `gt_bboxes_ignore` is specified). Negative values mean not\n            ignoring any bboxes. Defaults to -1.\n    \"\"\"\n\n    def __init__(self,\n                 topk: int,\n                 alpha: Optional[float] = None,\n                 iou_calculator: ConfigType = dict(type='BboxOverlaps2D'),\n                 ignore_iof_thr: float = -1) -> None:\n        self.topk = topk\n        self.alpha = alpha\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n        self.ignore_iof_thr = ignore_iof_thr\n\n    # https://github.com/sfzhang15/ATSS/blob/master/atss_core/modeling/rpn/atss/loss.py\n    def assign(\n            self,\n            pred_instances: InstanceData,\n            num_level_priors: List[int],\n            gt_instances: InstanceData,\n            gt_instances_ignore: Optional[InstanceData] = None\n    ) -> AssignResult:\n        \"\"\"Assign gt to priors.\n\n        The assignment is done in following steps\n\n        1. compute iou between all prior (prior of all pyramid levels) and gt\n        2. compute center distance between all prior and gt\n        3. on each pyramid level, for each gt, select k prior whose center\n           are closest to the gt center, so we total select k*l prior as\n           candidates for each gt\n        4. get corresponding iou for the these candidates, and compute the\n           mean and std, set mean + std as the iou threshold\n        5. select these candidates whose iou are greater than or equal to\n           the threshold as positive\n        6. limit the positive sample's center in gt\n\n        If ``alpha`` is not None, and ``cls_scores`` and `bbox_preds`\n        are not None, the overlaps calculation in the first step\n        will also include dynamic cost, which is currently only used in\n        the DDOD.\n\n        Args:\n            pred_instances (:obj:`InstaceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors, points, or bboxes predicted by the model,\n                shape(n, 4).\n            num_level_priors (List): Number of bboxes in each level\n            gt_instances (:obj:`InstaceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            gt_instances_ignore (:obj:`InstaceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n        gt_labels = gt_instances.labels\n        if gt_instances_ignore is not None:\n            gt_bboxes_ignore = gt_instances_ignore.bboxes\n        else:\n            gt_bboxes_ignore = None\n\n        INF = 100000000\n        priors = priors[:, :4]\n        num_gt, num_priors = gt_bboxes.size(0), priors.size(0)\n\n        message = 'Invalid alpha parameter because cls_scores or ' \\\n                  'bbox_preds are None. If you want to use the ' \\\n                  'cost-based ATSSAssigner,  please set cls_scores, ' \\\n                  'bbox_preds and self.alpha at the same time. '\n\n        # compute iou between all bbox and gt\n        if self.alpha is None:\n            # ATSSAssigner\n            overlaps = self.iou_calculator(priors, gt_bboxes)\n            if ('scores' in pred_instances or 'bboxes' in pred_instances):\n                warnings.warn(message)\n\n        else:\n            # Dynamic cost ATSSAssigner in DDOD\n            assert ('scores' in pred_instances\n                    and 'bboxes' in pred_instances), message\n            cls_scores = pred_instances.scores\n            bbox_preds = pred_instances.bboxes\n\n            # compute cls cost for bbox and GT\n            cls_cost = torch.sigmoid(cls_scores[:, gt_labels])\n\n            # compute iou between all bbox and gt\n            overlaps = self.iou_calculator(bbox_preds, gt_bboxes)\n\n            # make sure that we are in element-wise multiplication\n            assert cls_cost.shape == overlaps.shape\n\n            # overlaps is actually a cost matrix\n            overlaps = cls_cost**(1 - self.alpha) * overlaps**self.alpha\n\n        # assign 0 by default\n        assigned_gt_inds = overlaps.new_full((num_priors, ),\n                                             0,\n                                             dtype=torch.long)\n\n        if num_gt == 0 or num_priors == 0:\n            # No ground truth or boxes, return empty assignment\n            max_overlaps = overlaps.new_zeros((num_priors, ))\n            if num_gt == 0:\n                # No truth, assign everything to background\n                assigned_gt_inds[:] = 0\n            assigned_labels = overlaps.new_full((num_priors, ),\n                                                -1,\n                                                dtype=torch.long)\n            return AssignResult(\n                num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n\n        # compute center distance between all bbox and gt\n        distances = bbox_center_distance(gt_bboxes, priors)\n\n        if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None\n                and gt_bboxes_ignore.numel() > 0 and priors.numel() > 0):\n            ignore_overlaps = self.iou_calculator(\n                priors, gt_bboxes_ignore, mode='iof')\n            ignore_max_overlaps, _ = ignore_overlaps.max(dim=1)\n            ignore_idxs = ignore_max_overlaps > self.ignore_iof_thr\n            distances[ignore_idxs, :] = INF\n            assigned_gt_inds[ignore_idxs] = -1\n\n        # Selecting candidates based on the center distance\n        candidate_idxs = []\n        start_idx = 0\n        for level, priors_per_level in enumerate(num_level_priors):\n            # on each pyramid level, for each gt,\n            # select k bbox whose center are closest to the gt center\n            end_idx = start_idx + priors_per_level\n            distances_per_level = distances[start_idx:end_idx, :]\n            selectable_k = min(self.topk, priors_per_level)\n            _, topk_idxs_per_level = distances_per_level.topk(\n                selectable_k, dim=0, largest=False)\n            candidate_idxs.append(topk_idxs_per_level + start_idx)\n            start_idx = end_idx\n        candidate_idxs = torch.cat(candidate_idxs, dim=0)\n\n        # get corresponding iou for the these candidates, and compute the\n        # mean and std, set mean + std as the iou threshold\n        candidate_overlaps = overlaps[candidate_idxs, torch.arange(num_gt)]\n        overlaps_mean_per_gt = candidate_overlaps.mean(0)\n        overlaps_std_per_gt = candidate_overlaps.std(0)\n        overlaps_thr_per_gt = overlaps_mean_per_gt + overlaps_std_per_gt\n\n        is_pos = candidate_overlaps >= overlaps_thr_per_gt[None, :]\n\n        # limit the positive sample's center in gt\n        for gt_idx in range(num_gt):\n            candidate_idxs[:, gt_idx] += gt_idx * num_priors\n        priors_cx = (priors[:, 0] + priors[:, 2]) / 2.0\n        priors_cy = (priors[:, 1] + priors[:, 3]) / 2.0\n        ep_priors_cx = priors_cx.view(1, -1).expand(\n            num_gt, num_priors).contiguous().view(-1)\n        ep_priors_cy = priors_cy.view(1, -1).expand(\n            num_gt, num_priors).contiguous().view(-1)\n        candidate_idxs = candidate_idxs.view(-1)\n\n        # calculate the left, top, right, bottom distance between positive\n        # prior center and gt side\n        l_ = ep_priors_cx[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 0]\n        t_ = ep_priors_cy[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 1]\n        r_ = gt_bboxes[:, 2] - ep_priors_cx[candidate_idxs].view(-1, num_gt)\n        b_ = gt_bboxes[:, 3] - ep_priors_cy[candidate_idxs].view(-1, num_gt)\n        is_in_gts = torch.stack([l_, t_, r_, b_], dim=1).min(dim=1)[0] > 0.01\n\n        is_pos = is_pos & is_in_gts\n\n        # if an anchor box is assigned to multiple gts,\n        # the one with the highest IoU will be selected.\n        overlaps_inf = torch.full_like(overlaps,\n                                       -INF).t().contiguous().view(-1)\n        index = candidate_idxs.view(-1)[is_pos.view(-1)]\n        overlaps_inf[index] = overlaps.t().contiguous().view(-1)[index]\n        overlaps_inf = overlaps_inf.view(num_gt, -1).t()\n\n        max_overlaps, argmax_overlaps = overlaps_inf.max(dim=1)\n        assigned_gt_inds[\n            max_overlaps != -INF] = argmax_overlaps[max_overlaps != -INF] + 1\n\n        assigned_labels = assigned_gt_inds.new_full((num_priors, ), -1)\n        pos_inds = torch.nonzero(\n            assigned_gt_inds > 0, as_tuple=False).squeeze()\n        if pos_inds.numel() > 0:\n            assigned_labels[pos_inds] = gt_labels[assigned_gt_inds[pos_inds] -\n                                                  1]\n        return AssignResult(\n            num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/base_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import Optional\n\nfrom mmengine.structures import InstanceData\n\n\nclass BaseAssigner(metaclass=ABCMeta):\n    \"\"\"Base assigner that assigns boxes to ground truth boxes.\"\"\"\n\n    @abstractmethod\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs):\n        \"\"\"Assign boxes to either a ground truth boxes or a negative boxes.\"\"\"\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/center_region_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.utils import ConfigType\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\ndef scale_boxes(bboxes: Tensor, scale: float) -> Tensor:\n    \"\"\"Expand an array of boxes by a given scale.\n\n    Args:\n        bboxes (Tensor): Shape (m, 4)\n        scale (float): The scale factor of bboxes\n\n    Returns:\n        Tensor: Shape (m, 4). Scaled bboxes\n    \"\"\"\n    assert bboxes.size(1) == 4\n    w_half = (bboxes[:, 2] - bboxes[:, 0]) * .5\n    h_half = (bboxes[:, 3] - bboxes[:, 1]) * .5\n    x_c = (bboxes[:, 2] + bboxes[:, 0]) * .5\n    y_c = (bboxes[:, 3] + bboxes[:, 1]) * .5\n\n    w_half *= scale\n    h_half *= scale\n\n    boxes_scaled = torch.zeros_like(bboxes)\n    boxes_scaled[:, 0] = x_c - w_half\n    boxes_scaled[:, 2] = x_c + w_half\n    boxes_scaled[:, 1] = y_c - h_half\n    boxes_scaled[:, 3] = y_c + h_half\n    return boxes_scaled\n\n\ndef is_located_in(points: Tensor, bboxes: Tensor) -> Tensor:\n    \"\"\"Are points located in bboxes.\n\n    Args:\n        points (Tensor): Points, shape: (m, 2).\n        bboxes (Tensor): Bounding boxes, shape: (n, 4).\n\n    Return:\n        Tensor: Flags indicating if points are located in bboxes,\n        shape: (m, n).\n    \"\"\"\n    assert points.size(1) == 2\n    assert bboxes.size(1) == 4\n    return (points[:, 0].unsqueeze(1) > bboxes[:, 0].unsqueeze(0)) & \\\n           (points[:, 0].unsqueeze(1) < bboxes[:, 2].unsqueeze(0)) & \\\n           (points[:, 1].unsqueeze(1) > bboxes[:, 1].unsqueeze(0)) & \\\n           (points[:, 1].unsqueeze(1) < bboxes[:, 3].unsqueeze(0))\n\n\ndef bboxes_area(bboxes: Tensor) -> Tensor:\n    \"\"\"Compute the area of an array of bboxes.\n\n    Args:\n        bboxes (Tensor): The coordinates ox bboxes. Shape: (m, 4)\n\n    Returns:\n        Tensor: Area of the bboxes. Shape: (m, )\n    \"\"\"\n    assert bboxes.size(1) == 4\n    w = (bboxes[:, 2] - bboxes[:, 0])\n    h = (bboxes[:, 3] - bboxes[:, 1])\n    areas = w * h\n    return areas\n\n\n@TASK_UTILS.register_module()\nclass CenterRegionAssigner(BaseAssigner):\n    \"\"\"Assign pixels at the center region of a bbox as positive.\n\n    Each proposals will be assigned with `-1`, `0`, or a positive integer\n    indicating the ground truth index.\n    - -1: negative samples\n    - semi-positive numbers: positive sample, index (0-based) of assigned gt\n\n    Args:\n        pos_scale (float): Threshold within which pixels are\n            labelled as positive.\n        neg_scale (float): Threshold above which pixels are\n            labelled as positive.\n        min_pos_iof (float): Minimum iof of a pixel with a gt to be\n            labelled as positive. Default: 1e-2\n        ignore_gt_scale (float): Threshold within which the pixels\n            are ignored when the gt is labelled as shadowed. Default: 0.5\n        foreground_dominate (bool): If True, the bbox will be assigned as\n            positive when a gt's kernel region overlaps with another's shadowed\n            (ignored) region, otherwise it is set as ignored. Default to False.\n        iou_calculator (:obj:`ConfigDict` or dict): Config of overlaps\n            Calculator.\n    \"\"\"\n\n    def __init__(\n        self,\n        pos_scale: float,\n        neg_scale: float,\n        min_pos_iof: float = 1e-2,\n        ignore_gt_scale: float = 0.5,\n        foreground_dominate: bool = False,\n        iou_calculator: ConfigType = dict(type='BboxOverlaps2D')\n    ) -> None:\n        self.pos_scale = pos_scale\n        self.neg_scale = neg_scale\n        self.min_pos_iof = min_pos_iof\n        self.ignore_gt_scale = ignore_gt_scale\n        self.foreground_dominate = foreground_dominate\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def get_gt_priorities(self, gt_bboxes: Tensor) -> Tensor:\n        \"\"\"Get gt priorities according to their areas.\n\n        Smaller gt has higher priority.\n\n        Args:\n            gt_bboxes (Tensor): Ground truth boxes, shape (k, 4).\n\n        Returns:\n            Tensor: The priority of gts so that gts with larger priority is\n            more likely to be assigned. Shape (k, )\n        \"\"\"\n        gt_areas = bboxes_area(gt_bboxes)\n        # Rank all gt bbox areas. Smaller objects has larger priority\n        _, sort_idx = gt_areas.sort(descending=True)\n        sort_idx = sort_idx.argsort()\n        return sort_idx\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to bboxes.\n\n        This method assigns gts to every prior (proposal/anchor), each prior\n        will be assigned with -1, or a semi-positive number. -1 means\n        negative sample, semi-positive number is the index (0-based) of\n        assigned gt.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assigned result. Note that shadowed_labels\n            of shape (N, 2) is also added as an `assign_result` attribute.\n            `shadowed_labels` is a tensor composed of N pairs of anchor_ind,\n            class_label], where N is the number of anchors that lie in the\n            outer region of a gt, anchor_ind is the shadowed anchor index\n            and class_label is the shadowed class label.\n\n        Example:\n            >>> from mmengine.structures import InstanceData\n            >>> self = CenterRegionAssigner(0.2, 0.2)\n            >>> pred_instances.priors = torch.Tensor([[0, 0, 10, 10],\n            ...                                      [10, 10, 20, 20]])\n            >>> gt_instances = InstanceData()\n            >>> gt_instances.bboxes = torch.Tensor([[0, 0, 10, 10]])\n            >>> gt_instances.labels = torch.Tensor([0])\n            >>> assign_result = self.assign(pred_instances, gt_instances)\n            >>> expected_gt_inds = torch.LongTensor([1, 0])\n            >>> assert torch.all(assign_result.gt_inds == expected_gt_inds)\n        \"\"\"\n        # There are in total 5 steps in the pixel assignment\n        # 1. Find core (the center region, say inner 0.2)\n        #     and shadow (the relatively ourter part, say inner 0.2-0.5)\n        #     regions of every gt.\n        # 2. Find all prior bboxes that lie in gt_core and gt_shadow regions\n        # 3. Assign prior bboxes in gt_core with a one-hot id of the gt in\n        #      the image.\n        #    3.1. For overlapping objects, the prior bboxes in gt_core is\n        #           assigned with the object with smallest area\n        # 4. Assign prior bboxes with class label according to its gt id.\n        #    4.1. Assign -1 to prior bboxes lying in shadowed gts\n        #    4.2. Assign positive prior boxes with the corresponding label\n        # 5. Find pixels lying in the shadow of an object and assign them with\n        #      background label, but set the loss weight of its corresponding\n        #      gt to zero.\n\n        # TODO not extract bboxes in assign.\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n        gt_labels = gt_instances.labels\n\n        assert priors.size(1) == 4, 'priors must have size of 4'\n        # 1. Find core positive and shadow region of every gt\n        gt_core = scale_boxes(gt_bboxes, self.pos_scale)\n        gt_shadow = scale_boxes(gt_bboxes, self.neg_scale)\n\n        # 2. Find prior bboxes that lie in gt_core and gt_shadow regions\n        prior_centers = (priors[:, 2:4] + priors[:, 0:2]) / 2\n        # The center points lie within the gt boxes\n        is_prior_in_gt = is_located_in(prior_centers, gt_bboxes)\n        # Only calculate prior and gt_core IoF. This enables small prior bboxes\n        #   to match large gts\n        prior_and_gt_core_overlaps = self.iou_calculator(\n            priors, gt_core, mode='iof')\n        # The center point of effective priors should be within the gt box\n        is_prior_in_gt_core = is_prior_in_gt & (\n            prior_and_gt_core_overlaps > self.min_pos_iof)  # shape (n, k)\n\n        is_prior_in_gt_shadow = (\n            self.iou_calculator(priors, gt_shadow, mode='iof') >\n            self.min_pos_iof)\n        # Rule out center effective positive pixels\n        is_prior_in_gt_shadow &= (~is_prior_in_gt_core)\n\n        num_gts, num_priors = gt_bboxes.size(0), priors.size(0)\n        if num_gts == 0 or num_priors == 0:\n            # If no gts exist, assign all pixels to negative\n            assigned_gt_ids = \\\n                is_prior_in_gt_core.new_zeros((num_priors,),\n                                              dtype=torch.long)\n            pixels_in_gt_shadow = assigned_gt_ids.new_empty((0, 2))\n        else:\n            # Step 3: assign a one-hot gt id to each pixel, and smaller objects\n            #    have high priority to assign the pixel.\n            sort_idx = self.get_gt_priorities(gt_bboxes)\n            assigned_gt_ids, pixels_in_gt_shadow = \\\n                self.assign_one_hot_gt_indices(is_prior_in_gt_core,\n                                               is_prior_in_gt_shadow,\n                                               gt_priority=sort_idx)\n\n        if (gt_instances_ignore is not None\n                and gt_instances_ignore.bboxes.numel() > 0):\n            # No ground truth or boxes, return empty assignment\n            gt_bboxes_ignore = gt_instances_ignore.bboxes\n            gt_bboxes_ignore = scale_boxes(\n                gt_bboxes_ignore, scale=self.ignore_gt_scale)\n            is_prior_in_ignored_gts = is_located_in(prior_centers,\n                                                    gt_bboxes_ignore)\n            is_prior_in_ignored_gts = is_prior_in_ignored_gts.any(dim=1)\n            assigned_gt_ids[is_prior_in_ignored_gts] = -1\n\n        # 4. Assign prior bboxes with class label according to its gt id.\n        # Default assigned label is the background (-1)\n        assigned_labels = assigned_gt_ids.new_full((num_priors, ), -1)\n        pos_inds = torch.nonzero(assigned_gt_ids > 0, as_tuple=False).squeeze()\n        if pos_inds.numel() > 0:\n            assigned_labels[pos_inds] = gt_labels[assigned_gt_ids[pos_inds] -\n                                                  1]\n        # 5. Find pixels lying in the shadow of an object\n        shadowed_pixel_labels = pixels_in_gt_shadow.clone()\n        if pixels_in_gt_shadow.numel() > 0:\n            pixel_idx, gt_idx =\\\n                pixels_in_gt_shadow[:, 0], pixels_in_gt_shadow[:, 1]\n            assert (assigned_gt_ids[pixel_idx] != gt_idx).all(), \\\n                'Some pixels are dually assigned to ignore and gt!'\n            shadowed_pixel_labels[:, 1] = gt_labels[gt_idx - 1]\n            override = (\n                assigned_labels[pixel_idx] == shadowed_pixel_labels[:, 1])\n            if self.foreground_dominate:\n                # When a pixel is both positive and shadowed, set it as pos\n                shadowed_pixel_labels = shadowed_pixel_labels[~override]\n            else:\n                # When a pixel is both pos and shadowed, set it as shadowed\n                assigned_labels[pixel_idx[override]] = -1\n                assigned_gt_ids[pixel_idx[override]] = 0\n\n        assign_result = AssignResult(\n            num_gts, assigned_gt_ids, None, labels=assigned_labels)\n        # Add shadowed_labels as assign_result property. Shape: (num_shadow, 2)\n        assign_result.set_extra_property('shadowed_labels',\n                                         shadowed_pixel_labels)\n        return assign_result\n\n    def assign_one_hot_gt_indices(\n            self,\n            is_prior_in_gt_core: Tensor,\n            is_prior_in_gt_shadow: Tensor,\n            gt_priority: Optional[Tensor] = None) -> Tuple[Tensor, Tensor]:\n        \"\"\"Assign only one gt index to each prior box.\n\n        Gts with large gt_priority are more likely to be assigned.\n\n        Args:\n            is_prior_in_gt_core (Tensor): Bool tensor indicating the prior\n                center is in the core area of a gt (e.g. 0-0.2).\n                Shape: (num_prior, num_gt).\n            is_prior_in_gt_shadow (Tensor): Bool tensor indicating the prior\n                center is in the shadowed area of a gt (e.g. 0.2-0.5).\n                Shape: (num_prior, num_gt).\n            gt_priority (Tensor): Priorities of gts. The gt with a higher\n                priority is more likely to be assigned to the bbox when the\n                bbox match with multiple gts. Shape: (num_gt, ).\n\n        Returns:\n            tuple: Returns (assigned_gt_inds, shadowed_gt_inds).\n\n            - assigned_gt_inds: The assigned gt index of each prior bbox \\\n            (i.e. index from 1 to num_gts). Shape: (num_prior, ).\n            - shadowed_gt_inds: shadowed gt indices. It is a tensor of \\\n            shape (num_ignore, 2) with first column being the shadowed prior \\\n            bbox indices and the second column the shadowed gt \\\n            indices (1-based).\n        \"\"\"\n        num_bboxes, num_gts = is_prior_in_gt_core.shape\n\n        if gt_priority is None:\n            gt_priority = torch.arange(\n                num_gts, device=is_prior_in_gt_core.device)\n        assert gt_priority.size(0) == num_gts\n        # The bigger gt_priority, the more preferable to be assigned\n        # The assigned inds are by default 0 (background)\n        assigned_gt_inds = is_prior_in_gt_core.new_zeros((num_bboxes, ),\n                                                         dtype=torch.long)\n        # Shadowed bboxes are assigned to be background. But the corresponding\n        #   label is ignored during loss calculation, which is done through\n        #   shadowed_gt_inds\n        shadowed_gt_inds = torch.nonzero(is_prior_in_gt_shadow, as_tuple=False)\n        if is_prior_in_gt_core.sum() == 0:  # No gt match\n            shadowed_gt_inds[:, 1] += 1  # 1-based. For consistency issue\n            return assigned_gt_inds, shadowed_gt_inds\n\n        # The priority of each prior box and gt pair. If one prior box is\n        #  matched bo multiple gts. Only the pair with the highest priority\n        #  is saved\n        pair_priority = is_prior_in_gt_core.new_full((num_bboxes, num_gts),\n                                                     -1,\n                                                     dtype=torch.long)\n\n        # Each bbox could match with multiple gts.\n        # The following codes deal with this situation\n        # Matched  bboxes (to any gt). Shape: (num_pos_anchor, )\n        inds_of_match = torch.any(is_prior_in_gt_core, dim=1)\n        # The matched gt index of each positive bbox. Length >= num_pos_anchor\n        #   , since one bbox could match multiple gts\n        matched_bbox_gt_inds = torch.nonzero(\n            is_prior_in_gt_core, as_tuple=False)[:, 1]\n        # Assign priority to each bbox-gt pair.\n        pair_priority[is_prior_in_gt_core] = gt_priority[matched_bbox_gt_inds]\n        _, argmax_priority = pair_priority[inds_of_match].max(dim=1)\n        assigned_gt_inds[inds_of_match] = argmax_priority + 1  # 1-based\n        # Zero-out the assigned anchor box to filter the shadowed gt indices\n        is_prior_in_gt_core[inds_of_match, argmax_priority] = 0\n        # Concat the shadowed indices due to overlapping with that out side of\n        #   effective scale. shape: (total_num_ignore, 2)\n        shadowed_gt_inds = torch.cat(\n            (shadowed_gt_inds,\n             torch.nonzero(is_prior_in_gt_core, as_tuple=False)),\n            dim=0)\n        # Change `is_prior_in_gt_core` back to keep arguments intact.\n        is_prior_in_gt_core[inds_of_match, argmax_priority] = 1\n        # 1-based shadowed gt indices, to be consistent with `assigned_gt_inds`\n        if shadowed_gt_inds.numel() > 0:\n            shadowed_gt_inds[:, 1] += 1\n        return assigned_gt_inds, shadowed_gt_inds\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/dynamic_soft_label_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import BaseBoxes\nfrom mmdet.utils import ConfigType\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\nINF = 100000000\nEPS = 1.0e-7\n\n\ndef center_of_mass(masks: Tensor, eps: float = 1e-7) -> Tensor:\n    \"\"\"Compute the masks center of mass.\n\n    Args:\n        masks: Mask tensor, has shape (num_masks, H, W).\n        eps: a small number to avoid normalizer to be zero.\n            Defaults to 1e-7.\n    Returns:\n        Tensor: The masks center of mass. Has shape (num_masks, 2).\n    \"\"\"\n    n, h, w = masks.shape\n    grid_h = torch.arange(h, device=masks.device)[:, None]\n    grid_w = torch.arange(w, device=masks.device)\n    normalizer = masks.sum(dim=(1, 2)).float().clamp(min=eps)\n    center_y = (masks * grid_h).sum(dim=(1, 2)) / normalizer\n    center_x = (masks * grid_w).sum(dim=(1, 2)) / normalizer\n    center = torch.cat([center_x[:, None], center_y[:, None]], dim=1)\n    return center\n\n\n@TASK_UTILS.register_module()\nclass DynamicSoftLabelAssigner(BaseAssigner):\n    \"\"\"Computes matching between predictions and ground truth with dynamic soft\n    label assignment.\n\n    Args:\n        soft_center_radius (float): Radius of the soft center prior.\n            Defaults to 3.0.\n        topk (int): Select top-k predictions to calculate dynamic k\n            best matches for each gt. Defaults to 13.\n        iou_weight (float): The scale factor of iou cost. Defaults to 3.0.\n        iou_calculator (ConfigType): Config of overlaps Calculator.\n            Defaults to dict(type='BboxOverlaps2D').\n    \"\"\"\n\n    def __init__(\n        self,\n        soft_center_radius: float = 3.0,\n        topk: int = 13,\n        iou_weight: float = 3.0,\n        iou_calculator: ConfigType = dict(type='BboxOverlaps2D')\n    ) -> None:\n        self.soft_center_radius = soft_center_radius\n        self.topk = topk\n        self.iou_weight = iou_weight\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to priors.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n        Returns:\n            obj:`AssignResult`: The assigned result.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        num_gt = gt_bboxes.size(0)\n\n        decoded_bboxes = pred_instances.bboxes\n        pred_scores = pred_instances.scores\n        priors = pred_instances.priors\n        num_bboxes = decoded_bboxes.size(0)\n\n        # assign 0 by default\n        assigned_gt_inds = decoded_bboxes.new_full((num_bboxes, ),\n                                                   0,\n                                                   dtype=torch.long)\n        if num_gt == 0 or num_bboxes == 0:\n            # No ground truth or boxes, return empty assignment\n            max_overlaps = decoded_bboxes.new_zeros((num_bboxes, ))\n            if num_gt == 0:\n                # No truth, assign everything to background\n                assigned_gt_inds[:] = 0\n            assigned_labels = decoded_bboxes.new_full((num_bboxes, ),\n                                                      -1,\n                                                      dtype=torch.long)\n            return AssignResult(\n                num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n\n        prior_center = priors[:, :2]\n        if isinstance(gt_bboxes, BaseBoxes):\n            is_in_gts = gt_bboxes.find_inside_points(prior_center)\n        else:\n            # Tensor boxes will be treated as horizontal boxes by defaults\n            lt_ = prior_center[:, None] - gt_bboxes[:, :2]\n            rb_ = gt_bboxes[:, 2:] - prior_center[:, None]\n\n            deltas = torch.cat([lt_, rb_], dim=-1)\n            is_in_gts = deltas.min(dim=-1).values > 0\n\n        valid_mask = is_in_gts.sum(dim=1) > 0\n\n        valid_decoded_bbox = decoded_bboxes[valid_mask]\n        valid_pred_scores = pred_scores[valid_mask]\n        num_valid = valid_decoded_bbox.size(0)\n\n        if num_valid == 0:\n            # No ground truth or boxes, return empty assignment\n            max_overlaps = decoded_bboxes.new_zeros((num_bboxes, ))\n            assigned_labels = decoded_bboxes.new_full((num_bboxes, ),\n                                                      -1,\n                                                      dtype=torch.long)\n            return AssignResult(\n                num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n        if hasattr(gt_instances, 'masks'):\n            gt_center = center_of_mass(gt_instances.masks, eps=EPS)\n        elif isinstance(gt_bboxes, BaseBoxes):\n            gt_center = gt_bboxes.centers\n        else:\n            # Tensor boxes will be treated as horizontal boxes by defaults\n            gt_center = (gt_bboxes[:, :2] + gt_bboxes[:, 2:]) / 2.0\n        valid_prior = priors[valid_mask]\n        strides = valid_prior[:, 2]\n        distance = (valid_prior[:, None, :2] - gt_center[None, :, :]\n                    ).pow(2).sum(-1).sqrt() / strides[:, None]\n        soft_center_prior = torch.pow(10, distance - self.soft_center_radius)\n\n        pairwise_ious = self.iou_calculator(valid_decoded_bbox, gt_bboxes)\n        iou_cost = -torch.log(pairwise_ious + EPS) * self.iou_weight\n\n        gt_onehot_label = (\n            F.one_hot(gt_labels.to(torch.int64),\n                      pred_scores.shape[-1]).float().unsqueeze(0).repeat(\n                          num_valid, 1, 1))\n        valid_pred_scores = valid_pred_scores.unsqueeze(1).repeat(1, num_gt, 1)\n\n        soft_label = gt_onehot_label * pairwise_ious[..., None]\n        scale_factor = soft_label - valid_pred_scores.sigmoid()\n        soft_cls_cost = F.binary_cross_entropy_with_logits(\n            valid_pred_scores, soft_label,\n            reduction='none') * scale_factor.abs().pow(2.0)\n        soft_cls_cost = soft_cls_cost.sum(dim=-1)\n\n        cost_matrix = soft_cls_cost + iou_cost + soft_center_prior\n\n        matched_pred_ious, matched_gt_inds = self.dynamic_k_matching(\n            cost_matrix, pairwise_ious, num_gt, valid_mask)\n\n        # convert to AssignResult format\n        assigned_gt_inds[valid_mask] = matched_gt_inds + 1\n        assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1)\n        assigned_labels[valid_mask] = gt_labels[matched_gt_inds].long()\n        max_overlaps = assigned_gt_inds.new_full((num_bboxes, ),\n                                                 -INF,\n                                                 dtype=torch.float32)\n        max_overlaps[valid_mask] = matched_pred_ious\n        return AssignResult(\n            num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n\n    def dynamic_k_matching(self, cost: Tensor, pairwise_ious: Tensor,\n                           num_gt: int,\n                           valid_mask: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Use IoU and matching cost to calculate the dynamic top-k positive\n        targets. Same as SimOTA.\n\n        Args:\n            cost (Tensor): Cost matrix.\n            pairwise_ious (Tensor): Pairwise iou matrix.\n            num_gt (int): Number of gt.\n            valid_mask (Tensor): Mask for valid bboxes.\n\n        Returns:\n            tuple: matched ious and gt indexes.\n        \"\"\"\n        matching_matrix = torch.zeros_like(cost, dtype=torch.uint8)\n        # select candidate topk ious for dynamic-k calculation\n        candidate_topk = min(self.topk, pairwise_ious.size(0))\n        topk_ious, _ = torch.topk(pairwise_ious, candidate_topk, dim=0)\n        # calculate dynamic k for each gt\n        dynamic_ks = torch.clamp(topk_ious.sum(0).int(), min=1)\n        for gt_idx in range(num_gt):\n            _, pos_idx = torch.topk(\n                cost[:, gt_idx], k=dynamic_ks[gt_idx], largest=False)\n            matching_matrix[:, gt_idx][pos_idx] = 1\n\n        del topk_ious, dynamic_ks, pos_idx\n\n        prior_match_gt_mask = matching_matrix.sum(1) > 1\n        if prior_match_gt_mask.sum() > 0:\n            cost_min, cost_argmin = torch.min(\n                cost[prior_match_gt_mask, :], dim=1)\n            matching_matrix[prior_match_gt_mask, :] *= 0\n            matching_matrix[prior_match_gt_mask, cost_argmin] = 1\n        # get foreground mask inside box and center prior\n        fg_mask_inboxes = matching_matrix.sum(1) > 0\n        valid_mask[valid_mask.clone()] = fg_mask_inboxes\n\n        matched_gt_inds = matching_matrix[fg_mask_inboxes, :].argmax(1)\n        matched_pred_ious = (matching_matrix *\n                             pairwise_ious).sum(1)[fg_mask_inboxes]\n        return matched_pred_ious, matched_gt_inds\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/grid_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.utils import ConfigType\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\n@TASK_UTILS.register_module()\nclass GridAssigner(BaseAssigner):\n    \"\"\"Assign a corresponding gt bbox or background to each bbox.\n\n    Each proposals will be assigned with `-1`, `0`, or a positive integer\n    indicating the ground truth index.\n\n    - -1: don't care\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n\n    Args:\n        pos_iou_thr (float): IoU threshold for positive bboxes.\n        neg_iou_thr (float or tuple[float, float]): IoU threshold for negative\n        bboxes.\n        min_pos_iou (float): Minimum iou for a bbox to be considered as a\n            positive bbox. Positive samples can have smaller IoU than\n            pos_iou_thr due to the 4th step (assign max IoU sample to each gt).\n            Defaults to 0.\n        gt_max_assign_all (bool): Whether to assign all bboxes with the same\n            highest overlap with some gt to that gt.\n        iou_calculator (:obj:`ConfigDict` or dict): Config of overlaps\n            Calculator.\n    \"\"\"\n\n    def __init__(\n        self,\n        pos_iou_thr: float,\n        neg_iou_thr: Union[float, Tuple[float, float]],\n        min_pos_iou: float = .0,\n        gt_max_assign_all: bool = True,\n        iou_calculator: ConfigType = dict(type='BboxOverlaps2D')\n    ) -> None:\n        self.pos_iou_thr = pos_iou_thr\n        self.neg_iou_thr = neg_iou_thr\n        self.min_pos_iou = min_pos_iou\n        self.gt_max_assign_all = gt_max_assign_all\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to bboxes. The process is very much like the max iou\n        assigner, except that positive samples are constrained within the cell\n        that the gt boxes fell in.\n\n        This method assign a gt bbox to every bbox (proposal/anchor), each bbox\n        will be assigned with -1, 0, or a positive number. -1 means don't care,\n        0 means negative sample, positive number is the index (1-based) of\n        assigned gt.\n        The assignment is done in following steps, the order matters.\n\n        1. assign every bbox to -1\n        2. assign proposals whose iou with all gts <= neg_iou_thr to 0\n        3. for each bbox within a cell, if the iou with its nearest gt >\n            pos_iou_thr and the center of that gt falls inside the cell,\n            assign it to that bbox\n        4. for each gt bbox, assign its nearest proposals within the cell the\n            gt bbox falls in to itself.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n\n        priors = pred_instances.priors\n        responsible_flags = pred_instances.responsible_flags\n\n        num_gts, num_priors = gt_bboxes.size(0), priors.size(0)\n\n        # compute iou between all gt and priors\n        overlaps = self.iou_calculator(gt_bboxes, priors)\n\n        # 1. assign -1 by default\n        assigned_gt_inds = overlaps.new_full((num_priors, ),\n                                             -1,\n                                             dtype=torch.long)\n\n        if num_gts == 0 or num_priors == 0:\n            # No ground truth or priors, return empty assignment\n            max_overlaps = overlaps.new_zeros((num_priors, ))\n            if num_gts == 0:\n                # No truth, assign everything to background\n                assigned_gt_inds[:] = 0\n            assigned_labels = overlaps.new_full((num_priors, ),\n                                                -1,\n                                                dtype=torch.long)\n            return AssignResult(\n                num_gts,\n                assigned_gt_inds,\n                max_overlaps,\n                labels=assigned_labels)\n\n        # 2. assign negative: below\n        # for each anchor, which gt best overlaps with it\n        # for each anchor, the max iou of all gts\n        # shape of max_overlaps == argmax_overlaps == num_priors\n        max_overlaps, argmax_overlaps = overlaps.max(dim=0)\n\n        if isinstance(self.neg_iou_thr, float):\n            assigned_gt_inds[(max_overlaps >= 0)\n                             & (max_overlaps <= self.neg_iou_thr)] = 0\n        elif isinstance(self.neg_iou_thr, (tuple, list)):\n            assert len(self.neg_iou_thr) == 2\n            assigned_gt_inds[(max_overlaps > self.neg_iou_thr[0])\n                             & (max_overlaps <= self.neg_iou_thr[1])] = 0\n\n        # 3. assign positive: falls into responsible cell and above\n        # positive IOU threshold, the order matters.\n        # the prior condition of comparison is to filter out all\n        # unrelated anchors, i.e. not responsible_flags\n        overlaps[:, ~responsible_flags.type(torch.bool)] = -1.\n\n        # calculate max_overlaps again, but this time we only consider IOUs\n        # for anchors responsible for prediction\n        max_overlaps, argmax_overlaps = overlaps.max(dim=0)\n\n        # for each gt, which anchor best overlaps with it\n        # for each gt, the max iou of all proposals\n        # shape of gt_max_overlaps == gt_argmax_overlaps == num_gts\n        gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1)\n\n        pos_inds = (max_overlaps > self.pos_iou_thr) & responsible_flags.type(\n            torch.bool)\n        assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1\n\n        # 4. assign positive to max overlapped anchors within responsible cell\n        for i in range(num_gts):\n            if gt_max_overlaps[i] > self.min_pos_iou:\n                if self.gt_max_assign_all:\n                    max_iou_inds = (overlaps[i, :] == gt_max_overlaps[i]) & \\\n                         responsible_flags.type(torch.bool)\n                    assigned_gt_inds[max_iou_inds] = i + 1\n                elif responsible_flags[gt_argmax_overlaps[i]]:\n                    assigned_gt_inds[gt_argmax_overlaps[i]] = i + 1\n\n        # assign labels of positive anchors\n        assigned_labels = assigned_gt_inds.new_full((num_priors, ), -1)\n        pos_inds = torch.nonzero(\n            assigned_gt_inds > 0, as_tuple=False).squeeze()\n        if pos_inds.numel() > 0:\n            assigned_labels[pos_inds] = gt_labels[assigned_gt_inds[pos_inds] -\n                                                  1]\n\n        return AssignResult(\n            num_gts, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/hungarian_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Union\n\nimport torch\nfrom mmengine import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom scipy.optimize import linear_sum_assignment\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\n@TASK_UTILS.register_module()\nclass HungarianAssigner(BaseAssigner):\n    \"\"\"Computes one-to-one matching between predictions and ground truth.\n\n    This class computes an assignment between the targets and the predictions\n    based on the costs. The costs are weighted sum of some components.\n    For DETR the costs are weighted sum of classification cost, regression L1\n    cost and regression iou cost. The targets don't include the no_object, so\n    generally there are more predictions than targets. After the one-to-one\n    matching, the un-matched are treated as backgrounds. Thus each query\n    prediction will be assigned with `0` or a positive integer indicating the\n    ground truth index:\n\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n\n    Args:\n        match_costs (:obj:`ConfigDict` or dict or \\\n            List[Union[:obj:`ConfigDict`, dict]]): Match cost configs.\n    \"\"\"\n\n    def __init__(\n        self, match_costs: Union[List[Union[dict, ConfigDict]], dict,\n                                 ConfigDict]\n    ) -> None:\n\n        if isinstance(match_costs, dict):\n            match_costs = [match_costs]\n        elif isinstance(match_costs, list):\n            assert len(match_costs) > 0, \\\n                'match_costs must not be a empty list.'\n\n        self.match_costs = [\n            TASK_UTILS.build(match_cost) for match_cost in match_costs\n        ]\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               img_meta: Optional[dict] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Computes one-to-one matching based on the weighted costs.\n\n        This method assign each query prediction to a ground truth or\n        background. The `assigned_gt_inds` with -1 means don't care,\n        0 means negative sample, and positive number is the index (1-based)\n        of assigned gt.\n        The assignment is done in the following steps, the order matters.\n\n        1. assign every prediction to -1\n        2. compute the weighted costs\n        3. do Hungarian matching on CPU based on the costs\n        4. assign all to 0 (background) first, then for each matched pair\n           between predictions and gts, treat this prediction as foreground\n           and assign the corresponding gt index (plus 1) to it.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places. It may includes ``masks``, with shape\n                (n, h, w) or (n, l).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                ``labels``, with shape (k, ) and ``masks``, with shape\n                (k, h, w) or (k, l).\n            img_meta (dict): Image information.\n\n        Returns:\n            :obj:`AssignResult`: The assigned result.\n        \"\"\"\n        assert isinstance(gt_instances.labels, Tensor)\n        num_gts, num_preds = len(gt_instances), len(pred_instances)\n        gt_labels = gt_instances.labels\n        device = gt_labels.device\n\n        # 1. assign -1 by default\n        assigned_gt_inds = torch.full((num_preds, ),\n                                      -1,\n                                      dtype=torch.long,\n                                      device=device)\n        assigned_labels = torch.full((num_preds, ),\n                                     -1,\n                                     dtype=torch.long,\n                                     device=device)\n\n        if num_gts == 0 or num_preds == 0:\n            # No ground truth or boxes, return empty assignment\n            if num_gts == 0:\n                # No ground truth, assign all to background\n                assigned_gt_inds[:] = 0\n            return AssignResult(\n                num_gts=num_gts,\n                gt_inds=assigned_gt_inds,\n                max_overlaps=None,\n                labels=assigned_labels)\n\n        # 2. compute weighted cost\n        cost_list = []\n        for match_cost in self.match_costs:\n            cost = match_cost(\n                pred_instances=pred_instances,\n                gt_instances=gt_instances,\n                img_meta=img_meta)\n            cost_list.append(cost)\n        cost = torch.stack(cost_list).sum(dim=0)\n\n        # 3. do Hungarian matching on CPU using linear_sum_assignment\n        cost = cost.detach().cpu()\n        if linear_sum_assignment is None:\n            raise ImportError('Please run \"pip install scipy\" '\n                              'to install scipy first.')\n\n        matched_row_inds, matched_col_inds = linear_sum_assignment(cost)\n        matched_row_inds = torch.from_numpy(matched_row_inds).to(device)\n        matched_col_inds = torch.from_numpy(matched_col_inds).to(device)\n\n        # 4. assign backgrounds and foregrounds\n        # assign all indices to backgrounds first\n        assigned_gt_inds[:] = 0\n        # assign foregrounds based on matching results\n        assigned_gt_inds[matched_row_inds] = matched_col_inds + 1\n        assigned_labels[matched_row_inds] = gt_labels[matched_col_inds]\n        return AssignResult(\n            num_gts=num_gts,\n            gt_inds=assigned_gt_inds,\n            max_overlaps=None,\n            labels=assigned_labels)\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/iou2d_calculator.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import bbox_overlaps, get_box_tensor\n\n\ndef cast_tensor_type(x, scale=1., dtype=None):\n    if dtype == 'fp16':\n        # scale is for preventing overflows\n        x = (x / scale).half()\n    return x\n\n\n@TASK_UTILS.register_module()\nclass BboxOverlaps2D:\n    \"\"\"2D Overlaps (e.g. IoUs, GIoUs) Calculator.\"\"\"\n\n    def __init__(self, scale=1., dtype=None):\n        self.scale = scale\n        self.dtype = dtype\n\n    def __call__(self, bboxes1, bboxes2, mode='iou', is_aligned=False):\n        \"\"\"Calculate IoU between 2D bboxes.\n\n        Args:\n            bboxes1 (Tensor or :obj:`BaseBoxes`): bboxes have shape (m, 4)\n                in <x1, y1, x2, y2> format, or shape (m, 5) in <x1, y1, x2,\n                y2, score> format.\n            bboxes2 (Tensor or :obj:`BaseBoxes`): bboxes have shape (m, 4)\n                in <x1, y1, x2, y2> format, shape (m, 5) in <x1, y1, x2, y2,\n                score> format, or be empty. If ``is_aligned `` is ``True``,\n                then m and n must be equal.\n            mode (str): \"iou\" (intersection over union), \"iof\" (intersection\n                over foreground), or \"giou\" (generalized intersection over\n                union).\n            is_aligned (bool, optional): If True, then m and n must be equal.\n                Default False.\n\n        Returns:\n            Tensor: shape (m, n) if ``is_aligned `` is False else shape (m,)\n        \"\"\"\n        bboxes1 = get_box_tensor(bboxes1)\n        bboxes2 = get_box_tensor(bboxes2)\n        assert bboxes1.size(-1) in [0, 4, 5]\n        assert bboxes2.size(-1) in [0, 4, 5]\n        if bboxes2.size(-1) == 5:\n            bboxes2 = bboxes2[..., :4]\n        if bboxes1.size(-1) == 5:\n            bboxes1 = bboxes1[..., :4]\n\n        if self.dtype == 'fp16':\n            # change tensor type to save cpu and cuda memory and keep speed\n            bboxes1 = cast_tensor_type(bboxes1, self.scale, self.dtype)\n            bboxes2 = cast_tensor_type(bboxes2, self.scale, self.dtype)\n            overlaps = bbox_overlaps(bboxes1, bboxes2, mode, is_aligned)\n            if not overlaps.is_cuda and overlaps.dtype == torch.float16:\n                # resume cpu float32\n                overlaps = overlaps.float()\n            return overlaps\n\n        return bbox_overlaps(bboxes1, bboxes2, mode, is_aligned)\n\n    def __repr__(self):\n        \"\"\"str: a string describing the module\"\"\"\n        repr_str = self.__class__.__name__ + f'(' \\\n            f'scale={self.scale}, dtype={self.dtype})'\n        return repr_str\n\n\n@TASK_UTILS.register_module()\nclass BboxOverlaps2D_GLIP(BboxOverlaps2D):\n\n    def __call__(self, bboxes1, bboxes2, mode='iou', is_aligned=False):\n        TO_REMOVE = 1\n        area1 = (bboxes1[:, 2] - bboxes1[:, 0] + TO_REMOVE) * (\n            bboxes1[:, 3] - bboxes1[:, 1] + TO_REMOVE)\n        area2 = (bboxes2[:, 2] - bboxes2[:, 0] + TO_REMOVE) * (\n            bboxes2[:, 3] - bboxes2[:, 1] + TO_REMOVE)\n\n        lt = torch.max(bboxes1[:, None, :2], bboxes2[:, :2])  # [N,M,2]\n        rb = torch.min(bboxes1[:, None, 2:], bboxes2[:, 2:])  # [N,M,2]\n\n        wh = (rb - lt + TO_REMOVE).clamp(min=0)  # [N,M,2]\n        inter = wh[:, :, 0] * wh[:, :, 1]  # [N,M]\n\n        iou = inter / (area1[:, None] + area2 - inter)\n        return iou\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/match_cost.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import abstractmethod\nfrom typing import Optional, Union\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import bbox_overlaps, bbox_xyxy_to_cxcywh\n\n\nclass BaseMatchCost:\n    \"\"\"Base match cost class.\n\n    Args:\n        weight (Union[float, int]): Cost weight. Defaults to 1.\n    \"\"\"\n\n    def __init__(self, weight: Union[float, int] = 1.) -> None:\n        self.weight = weight\n\n    @abstractmethod\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs) -> Tensor:\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            img_meta (dict, optional): Image information.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        pass\n\n\n@TASK_UTILS.register_module()\nclass BBoxL1Cost(BaseMatchCost):\n    \"\"\"BBoxL1Cost.\n\n    Note: ``bboxes`` in ``InstanceData`` passed in is of format 'xyxy'\n    and its coordinates are unnormalized.\n\n    Args:\n        box_format (str, optional): 'xyxy' for DETR, 'xywh' for Sparse_RCNN.\n            Defaults to 'xyxy'.\n        weight (Union[float, int]): Cost weight. Defaults to 1.\n\n    Examples:\n        >>> from mmdet.models.task_modules.assigners.\n        ... match_costs.match_cost import BBoxL1Cost\n        >>> import torch\n        >>> self = BBoxL1Cost()\n        >>> bbox_pred = torch.rand(1, 4)\n        >>> gt_bboxes= torch.FloatTensor([[0, 0, 2, 4], [1, 2, 3, 4]])\n        >>> factor = torch.tensor([10, 8, 10, 8])\n        >>> self(bbox_pred, gt_bboxes, factor)\n        tensor([[1.6172, 1.6422]])\n    \"\"\"\n\n    def __init__(self,\n                 box_format: str = 'xyxy',\n                 weight: Union[float, int] = 1.) -> None:\n        super().__init__(weight=weight)\n        assert box_format in ['xyxy', 'xywh']\n        self.box_format = box_format\n\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs) -> Tensor:\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): ``bboxes`` inside is\n                predicted boxes with unnormalized coordinate\n                (x, y, x, y).\n            gt_instances (:obj:`InstanceData`): ``bboxes`` inside is gt\n                bboxes with unnormalized coordinate (x, y, x, y).\n            img_meta (Optional[dict]): Image information. Defaults to None.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        pred_bboxes = pred_instances.bboxes\n        gt_bboxes = gt_instances.bboxes\n\n        # convert box format\n        if self.box_format == 'xywh':\n            gt_bboxes = bbox_xyxy_to_cxcywh(gt_bboxes)\n            pred_bboxes = bbox_xyxy_to_cxcywh(pred_bboxes)\n\n        # normalized\n        img_h, img_w = img_meta['img_shape']\n        factor = gt_bboxes.new_tensor([img_w, img_h, img_w,\n                                       img_h]).unsqueeze(0)\n        gt_bboxes = gt_bboxes / factor\n        pred_bboxes = pred_bboxes / factor\n\n        bbox_cost = torch.cdist(pred_bboxes, gt_bboxes, p=1)\n        return bbox_cost * self.weight\n\n\n@TASK_UTILS.register_module()\nclass IoUCost(BaseMatchCost):\n    \"\"\"IoUCost.\n\n    Note: ``bboxes`` in ``InstanceData`` passed in is of format 'xyxy'\n    and its coordinates are unnormalized.\n\n    Args:\n        iou_mode (str): iou mode such as 'iou', 'giou'. Defaults to 'giou'.\n        weight (Union[float, int]): Cost weight. Defaults to 1.\n\n    Examples:\n        >>> from mmdet.models.task_modules.assigners.\n        ... match_costs.match_cost import IoUCost\n        >>> import torch\n        >>> self = IoUCost()\n        >>> bboxes = torch.FloatTensor([[1,1, 2, 2], [2, 2, 3, 4]])\n        >>> gt_bboxes = torch.FloatTensor([[0, 0, 2, 4], [1, 2, 3, 4]])\n        >>> self(bboxes, gt_bboxes)\n        tensor([[-0.1250,  0.1667],\n            [ 0.1667, -0.5000]])\n    \"\"\"\n\n    def __init__(self, iou_mode: str = 'giou', weight: Union[float, int] = 1.):\n        super().__init__(weight=weight)\n        self.iou_mode = iou_mode\n\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs):\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): ``bboxes`` inside is\n                predicted boxes with unnormalized coordinate\n                (x, y, x, y).\n            gt_instances (:obj:`InstanceData`): ``bboxes`` inside is gt\n                bboxes with unnormalized coordinate (x, y, x, y).\n            img_meta (Optional[dict]): Image information. Defaults to None.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        pred_bboxes = pred_instances.bboxes\n        gt_bboxes = gt_instances.bboxes\n\n        # avoid fp16 overflow\n        if pred_bboxes.dtype == torch.float16:\n            fp16 = True\n            pred_bboxes = pred_bboxes.to(torch.float32)\n        else:\n            fp16 = False\n\n        overlaps = bbox_overlaps(\n            pred_bboxes, gt_bboxes, mode=self.iou_mode, is_aligned=False)\n\n        if fp16:\n            overlaps = overlaps.to(torch.float16)\n\n        # The 1 is a constant that doesn't change the matching, so omitted.\n        iou_cost = -overlaps\n        return iou_cost * self.weight\n\n\n@TASK_UTILS.register_module()\nclass ClassificationCost(BaseMatchCost):\n    \"\"\"ClsSoftmaxCost.\n\n    Args:\n        weight (Union[float, int]): Cost weight. Defaults to 1.\n\n    Examples:\n        >>> from mmdet.models.task_modules.assigners.\n        ...  match_costs.match_cost import ClassificationCost\n        >>> import torch\n        >>> self = ClassificationCost()\n        >>> cls_pred = torch.rand(4, 3)\n        >>> gt_labels = torch.tensor([0, 1, 2])\n        >>> factor = torch.tensor([10, 8, 10, 8])\n        >>> self(cls_pred, gt_labels)\n        tensor([[-0.3430, -0.3525, -0.3045],\n            [-0.3077, -0.2931, -0.3992],\n            [-0.3664, -0.3455, -0.2881],\n            [-0.3343, -0.2701, -0.3956]])\n    \"\"\"\n\n    def __init__(self, weight: Union[float, int] = 1) -> None:\n        super().__init__(weight=weight)\n\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs) -> Tensor:\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): ``scores`` inside is\n                predicted classification logits, of shape\n                (num_queries, num_class).\n            gt_instances (:obj:`InstanceData`): ``labels`` inside should have\n                shape (num_gt, ).\n            img_meta (Optional[dict]): _description_. Defaults to None.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        pred_scores = pred_instances.scores\n        gt_labels = gt_instances.labels\n\n        pred_scores = pred_scores.softmax(-1)\n        cls_cost = -pred_scores[:, gt_labels]\n\n        return cls_cost * self.weight\n\n\n@TASK_UTILS.register_module()\nclass FocalLossCost(BaseMatchCost):\n    \"\"\"FocalLossCost.\n\n    Args:\n        alpha (Union[float, int]): focal_loss alpha. Defaults to 0.25.\n        gamma (Union[float, int]): focal_loss gamma. Defaults to 2.\n        eps (float): Defaults to 1e-12.\n        binary_input (bool): Whether the input is binary. Currently,\n            binary_input = True is for masks input, binary_input = False\n            is for label input. Defaults to False.\n        weight (Union[float, int]): Cost weight. Defaults to 1.\n    \"\"\"\n\n    def __init__(self,\n                 alpha: Union[float, int] = 0.25,\n                 gamma: Union[float, int] = 2,\n                 eps: float = 1e-12,\n                 binary_input: bool = False,\n                 weight: Union[float, int] = 1.) -> None:\n        super().__init__(weight=weight)\n        self.alpha = alpha\n        self.gamma = gamma\n        self.eps = eps\n        self.binary_input = binary_input\n\n    def _focal_loss_cost(self, cls_pred: Tensor, gt_labels: Tensor) -> Tensor:\n        \"\"\"\n        Args:\n            cls_pred (Tensor): Predicted classification logits, shape\n                (num_queries, num_class).\n            gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,).\n\n        Returns:\n            torch.Tensor: cls_cost value with weight\n        \"\"\"\n        cls_pred = cls_pred.sigmoid()\n        neg_cost = -(1 - cls_pred + self.eps).log() * (\n            1 - self.alpha) * cls_pred.pow(self.gamma)\n        pos_cost = -(cls_pred + self.eps).log() * self.alpha * (\n            1 - cls_pred).pow(self.gamma)\n\n        cls_cost = pos_cost[:, gt_labels] - neg_cost[:, gt_labels]\n        return cls_cost * self.weight\n\n    def _mask_focal_loss_cost(self, cls_pred, gt_labels) -> Tensor:\n        \"\"\"\n        Args:\n            cls_pred (Tensor): Predicted classification logits.\n                in shape (num_queries, d1, ..., dn), dtype=torch.float32.\n            gt_labels (Tensor): Ground truth in shape (num_gt, d1, ..., dn),\n                dtype=torch.long. Labels should be binary.\n\n        Returns:\n            Tensor: Focal cost matrix with weight in shape\\\n                (num_queries, num_gt).\n        \"\"\"\n        cls_pred = cls_pred.flatten(1)\n        gt_labels = gt_labels.flatten(1).float()\n        n = cls_pred.shape[1]\n        cls_pred = cls_pred.sigmoid()\n        neg_cost = -(1 - cls_pred + self.eps).log() * (\n            1 - self.alpha) * cls_pred.pow(self.gamma)\n        pos_cost = -(cls_pred + self.eps).log() * self.alpha * (\n            1 - cls_pred).pow(self.gamma)\n\n        cls_cost = torch.einsum('nc,mc->nm', pos_cost, gt_labels) + \\\n            torch.einsum('nc,mc->nm', neg_cost, (1 - gt_labels))\n        return cls_cost / n * self.weight\n\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs) -> Tensor:\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Predicted instances which\n                must contain ``scores`` or ``masks``.\n            gt_instances (:obj:`InstanceData`): Ground truth which must contain\n                ``labels`` or ``mask``.\n            img_meta (Optional[dict]): Image information. Defaults to None.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        if self.binary_input:\n            pred_masks = pred_instances.masks\n            gt_masks = gt_instances.masks\n            return self._mask_focal_loss_cost(pred_masks, gt_masks)\n        else:\n            pred_scores = pred_instances.scores\n            gt_labels = gt_instances.labels\n            return self._focal_loss_cost(pred_scores, gt_labels)\n\n\n@TASK_UTILS.register_module()\nclass BinaryFocalLossCost(FocalLossCost):\n\n    def _focal_loss_cost(self, cls_pred: Tensor, gt_labels: Tensor) -> Tensor:\n        \"\"\"\n        Args:\n            cls_pred (Tensor): Predicted classification logits, shape\n                (num_queries, num_class).\n            gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,).\n\n        Returns:\n            torch.Tensor: cls_cost value with weight\n        \"\"\"\n        cls_pred = cls_pred.flatten(1)\n        gt_labels = gt_labels.flatten(1).float()\n        cls_pred = cls_pred.sigmoid()\n        neg_cost = -(1 - cls_pred + self.eps).log() * (\n            1 - self.alpha) * cls_pred.pow(self.gamma)\n        pos_cost = -(cls_pred + self.eps).log() * self.alpha * (\n            1 - cls_pred).pow(self.gamma)\n\n        cls_cost = torch.einsum('nc,mc->nm', pos_cost, gt_labels) + \\\n            torch.einsum('nc,mc->nm', neg_cost, (1 - gt_labels))\n        return cls_cost * self.weight\n\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs) -> Tensor:\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Predicted instances which\n                must contain ``scores`` or ``masks``.\n            gt_instances (:obj:`InstanceData`): Ground truth which must contain\n                ``labels`` or ``mask``.\n            img_meta (Optional[dict]): Image information. Defaults to None.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        # gt_instances.text_token_mask is a repeated tensor of the same length\n        # of instances. Only gt_instances.text_token_mask[0] is useful\n        text_token_mask = torch.nonzero(\n            gt_instances.text_token_mask[0]).squeeze(-1)\n        pred_scores = pred_instances.scores[:, text_token_mask]\n        gt_labels = gt_instances.positive_maps[:, text_token_mask]\n        return self._focal_loss_cost(pred_scores, gt_labels)\n\n\n@TASK_UTILS.register_module()\nclass DiceCost(BaseMatchCost):\n    \"\"\"Cost of mask assignments based on dice losses.\n\n    Args:\n        pred_act (bool): Whether to apply sigmoid to mask_pred.\n            Defaults to False.\n        eps (float): Defaults to 1e-3.\n        naive_dice (bool): If True, use the naive dice loss\n            in which the power of the number in the denominator is\n            the first power. If False, use the second power that\n            is adopted by K-Net and SOLO. Defaults to True.\n        weight (Union[float, int]): Cost weight. Defaults to 1.\n    \"\"\"\n\n    def __init__(self,\n                 pred_act: bool = False,\n                 eps: float = 1e-3,\n                 naive_dice: bool = True,\n                 weight: Union[float, int] = 1.) -> None:\n        super().__init__(weight=weight)\n        self.pred_act = pred_act\n        self.eps = eps\n        self.naive_dice = naive_dice\n\n    def _binary_mask_dice_loss(self, mask_preds: Tensor,\n                               gt_masks: Tensor) -> Tensor:\n        \"\"\"\n        Args:\n            mask_preds (Tensor): Mask prediction in shape (num_queries, *).\n            gt_masks (Tensor): Ground truth in shape (num_gt, *)\n                store 0 or 1, 0 for negative class and 1 for\n                positive class.\n\n        Returns:\n            Tensor: Dice cost matrix in shape (num_queries, num_gt).\n        \"\"\"\n        mask_preds = mask_preds.flatten(1)\n        gt_masks = gt_masks.flatten(1).float()\n        numerator = 2 * torch.einsum('nc,mc->nm', mask_preds, gt_masks)\n        if self.naive_dice:\n            denominator = mask_preds.sum(-1)[:, None] + \\\n                          gt_masks.sum(-1)[None, :]\n        else:\n            denominator = mask_preds.pow(2).sum(1)[:, None] + \\\n                          gt_masks.pow(2).sum(1)[None, :]\n        loss = 1 - (numerator + self.eps) / (denominator + self.eps)\n        return loss\n\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs) -> Tensor:\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Predicted instances which\n                must contain ``masks``.\n            gt_instances (:obj:`InstanceData`): Ground truth which must contain\n                ``mask``.\n            img_meta (Optional[dict]): Image information. Defaults to None.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        pred_masks = pred_instances.masks\n        gt_masks = gt_instances.masks\n\n        if self.pred_act:\n            pred_masks = pred_masks.sigmoid()\n        dice_cost = self._binary_mask_dice_loss(pred_masks, gt_masks)\n        return dice_cost * self.weight\n\n\n@TASK_UTILS.register_module()\nclass CrossEntropyLossCost(BaseMatchCost):\n    \"\"\"CrossEntropyLossCost.\n\n    Args:\n        use_sigmoid (bool): Whether the prediction uses sigmoid\n                of softmax. Defaults to True.\n        weight (Union[float, int]): Cost weight. Defaults to 1.\n    \"\"\"\n\n    def __init__(self,\n                 use_sigmoid: bool = True,\n                 weight: Union[float, int] = 1.) -> None:\n        super().__init__(weight=weight)\n        self.use_sigmoid = use_sigmoid\n\n    def _binary_cross_entropy(self, cls_pred: Tensor,\n                              gt_labels: Tensor) -> Tensor:\n        \"\"\"\n        Args:\n            cls_pred (Tensor): The prediction with shape (num_queries, 1, *) or\n                (num_queries, *).\n            gt_labels (Tensor): The learning label of prediction with\n                shape (num_gt, *).\n\n        Returns:\n            Tensor: Cross entropy cost matrix in shape (num_queries, num_gt).\n        \"\"\"\n        cls_pred = cls_pred.flatten(1).float()\n        gt_labels = gt_labels.flatten(1).float()\n        n = cls_pred.shape[1]\n        pos = F.binary_cross_entropy_with_logits(\n            cls_pred, torch.ones_like(cls_pred), reduction='none')\n        neg = F.binary_cross_entropy_with_logits(\n            cls_pred, torch.zeros_like(cls_pred), reduction='none')\n        cls_cost = torch.einsum('nc,mc->nm', pos, gt_labels) + \\\n            torch.einsum('nc,mc->nm', neg, 1 - gt_labels)\n        cls_cost = cls_cost / n\n\n        return cls_cost\n\n    def __call__(self,\n                 pred_instances: InstanceData,\n                 gt_instances: InstanceData,\n                 img_meta: Optional[dict] = None,\n                 **kwargs) -> Tensor:\n        \"\"\"Compute match cost.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Predicted instances which\n                must contain ``scores`` or ``masks``.\n            gt_instances (:obj:`InstanceData`): Ground truth which must contain\n                ``labels`` or ``masks``.\n            img_meta (Optional[dict]): Image information. Defaults to None.\n\n        Returns:\n            Tensor: Match Cost matrix of shape (num_preds, num_gts).\n        \"\"\"\n        pred_masks = pred_instances.masks\n        gt_masks = gt_instances.masks\n        if self.use_sigmoid:\n            cls_cost = self._binary_cross_entropy(pred_masks, gt_masks)\n        else:\n            raise NotImplementedError\n\n        return cls_cost * self.weight\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/max_iou_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Optional, Union\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\ndef _perm_box(bboxes,\n              iou_calculator,\n              iou_thr=0.97,\n              perm_range=0.01,\n              counter=0,\n              max_iter=5):\n    \"\"\"Compute the permuted bboxes.\n\n    Args:\n        bboxes (Tensor): Shape (n, 4) for , \"xyxy\" format.\n        iou_calculator (obj): Overlaps Calculator.\n        iou_thr (float): The permuted bboxes should have IoU > iou_thr.\n        perm_range (float): The scale of permutation.\n        counter (int): Counter of permutation iteration.\n        max_iter (int): The max iterations of permutation.\n    Returns:\n        Tensor: The permuted bboxes.\n    \"\"\"\n    ori_bboxes = copy.deepcopy(bboxes)\n    is_valid = True\n    N = bboxes.size(0)\n    perm_factor = bboxes.new_empty(N, 4).uniform_(1 - perm_range,\n                                                  1 + perm_range)\n    bboxes *= perm_factor\n    new_wh = bboxes[:, 2:] - bboxes[:, :2]\n    if (new_wh <= 0).any():\n        is_valid = False\n    iou = iou_calculator(ori_bboxes.unique(dim=0), bboxes)\n    if (iou < iou_thr).any():\n        is_valid = False\n    if not is_valid and counter < max_iter:\n        return _perm_box(\n            ori_bboxes,\n            iou_calculator,\n            perm_range=max(perm_range - counter * 0.001, 1e-3),\n            counter=counter + 1)\n    return bboxes\n\n\ndef perm_repeat_bboxes(bboxes, iou_calculator=None, perm_repeat_cfg=None):\n    \"\"\"Permute the repeated bboxes.\n\n    Args:\n        bboxes (Tensor): Shape (n, 4) for , \"xyxy\" format.\n        iou_calculator (obj): Overlaps Calculator.\n        perm_repeat_cfg (Dict): Config of permutation.\n    Returns:\n        Tensor: Bboxes after permuted repeated bboxes.\n    \"\"\"\n    assert isinstance(bboxes, torch.Tensor)\n    if iou_calculator is None:\n        import torchvision\n        iou_calculator = torchvision.ops.box_iou\n    bboxes = copy.deepcopy(bboxes)\n    unique_bboxes = bboxes.unique(dim=0)\n    iou_thr = perm_repeat_cfg.get('iou_thr', 0.97)\n    perm_range = perm_repeat_cfg.get('perm_range', 0.01)\n    for box in unique_bboxes:\n        inds = (bboxes == box).sum(-1).float() == 4\n        if inds.float().sum().item() == 1:\n            continue\n        bboxes[inds] = _perm_box(\n            bboxes[inds],\n            iou_calculator,\n            iou_thr=iou_thr,\n            perm_range=perm_range,\n            counter=0)\n    return bboxes\n\n\n@TASK_UTILS.register_module()\nclass MaxIoUAssigner(BaseAssigner):\n    \"\"\"Assign a corresponding gt bbox or background to each bbox.\n\n    Each proposals will be assigned with `-1`, or a semi-positive integer\n    indicating the ground truth index.\n\n    - -1: negative sample, no assigned gt\n    - semi-positive integer: positive sample, index (0-based) of assigned gt\n\n    Args:\n        pos_iou_thr (float): IoU threshold for positive bboxes.\n        neg_iou_thr (float or tuple): IoU threshold for negative bboxes.\n        min_pos_iou (float): Minimum iou for a bbox to be considered as a\n            positive bbox. Positive samples can have smaller IoU than\n            pos_iou_thr due to the 4th step (assign max IoU sample to each gt).\n            `min_pos_iou` is set to avoid assigning bboxes that have extremely\n            small iou with GT as positive samples. It brings about 0.3 mAP\n            improvements in 1x schedule but does not affect the performance of\n            3x schedule. More comparisons can be found in\n            `PR #7464 <https://github.com/open-mmlab/mmdetection/pull/7464>`_.\n        gt_max_assign_all (bool): Whether to assign all bboxes with the same\n            highest overlap with some gt to that gt.\n        ignore_iof_thr (float): IoF threshold for ignoring bboxes (if\n            `gt_bboxes_ignore` is specified). Negative values mean not\n            ignoring any bboxes.\n        ignore_wrt_candidates (bool): Whether to compute the iof between\n            `bboxes` and `gt_bboxes_ignore`, or the contrary.\n        match_low_quality (bool): Whether to allow low quality matches. This is\n            usually allowed for RPN and single stage detectors, but not allowed\n            in the second stage. Details are demonstrated in Step 4.\n        gpu_assign_thr (int): The upper bound of the number of GT for GPU\n            assign. When the number of gt is above this threshold, will assign\n            on CPU device. Negative values mean not assign on CPU.\n        iou_calculator (dict): Config of overlaps Calculator.\n        perm_repeat_gt_cfg (dict): Config of permute repeated gt bboxes.\n    \"\"\"\n\n    def __init__(self,\n                 pos_iou_thr: float,\n                 neg_iou_thr: Union[float, tuple],\n                 min_pos_iou: float = .0,\n                 gt_max_assign_all: bool = True,\n                 ignore_iof_thr: float = -1,\n                 ignore_wrt_candidates: bool = True,\n                 match_low_quality: bool = True,\n                 gpu_assign_thr: float = -1,\n                 iou_calculator: dict = dict(type='BboxOverlaps2D'),\n                 perm_repeat_gt_cfg=None):\n        self.pos_iou_thr = pos_iou_thr\n        self.neg_iou_thr = neg_iou_thr\n        self.min_pos_iou = min_pos_iou\n        self.gt_max_assign_all = gt_max_assign_all\n        self.ignore_iof_thr = ignore_iof_thr\n        self.ignore_wrt_candidates = ignore_wrt_candidates\n        self.gpu_assign_thr = gpu_assign_thr\n        self.match_low_quality = match_low_quality\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n        self.perm_repeat_gt_cfg = perm_repeat_gt_cfg\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to bboxes.\n\n        This method assign a gt bbox to every bbox (proposal/anchor), each bbox\n        will be assigned with -1, or a semi-positive number. -1 means negative\n        sample, semi-positive number is the index (0-based) of assigned gt.\n        The assignment is done in following steps, the order matters.\n\n        1. assign every bbox to the background\n        2. assign proposals whose iou with all gts < neg_iou_thr to 0\n        3. for each bbox, if the iou with its nearest gt >= pos_iou_thr,\n           assign it to that bbox\n        4. for each gt bbox, assign its nearest proposals (may be more than\n           one) to itself\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n\n        Example:\n            >>> from mmengine.structures import InstanceData\n            >>> self = MaxIoUAssigner(0.5, 0.5)\n            >>> pred_instances = InstanceData()\n            >>> pred_instances.priors = torch.Tensor([[0, 0, 10, 10],\n            ...                                      [10, 10, 20, 20]])\n            >>> gt_instances = InstanceData()\n            >>> gt_instances.bboxes = torch.Tensor([[0, 0, 10, 9]])\n            >>> gt_instances.labels = torch.Tensor([0])\n            >>> assign_result = self.assign(pred_instances, gt_instances)\n            >>> expected_gt_inds = torch.LongTensor([1, 0])\n            >>> assert torch.all(assign_result.gt_inds == expected_gt_inds)\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n        gt_labels = gt_instances.labels\n        if gt_instances_ignore is not None:\n            gt_bboxes_ignore = gt_instances_ignore.bboxes\n        else:\n            gt_bboxes_ignore = None\n\n        assign_on_cpu = True if (self.gpu_assign_thr > 0) and (\n            gt_bboxes.shape[0] > self.gpu_assign_thr) else False\n        # compute overlap and assign gt on CPU when number of GT is large\n        if assign_on_cpu:\n            device = priors.device\n            priors = priors.cpu()\n            gt_bboxes = gt_bboxes.cpu()\n            gt_labels = gt_labels.cpu()\n            if gt_bboxes_ignore is not None:\n                gt_bboxes_ignore = gt_bboxes_ignore.cpu()\n\n        if self.perm_repeat_gt_cfg is not None and priors.numel() > 0:\n            gt_bboxes_unique = perm_repeat_bboxes(gt_bboxes,\n                                                  self.iou_calculator,\n                                                  self.perm_repeat_gt_cfg)\n        else:\n            gt_bboxes_unique = gt_bboxes\n        overlaps = self.iou_calculator(gt_bboxes_unique, priors)\n\n        if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None\n                and gt_bboxes_ignore.numel() > 0 and priors.numel() > 0):\n            if self.ignore_wrt_candidates:\n                ignore_overlaps = self.iou_calculator(\n                    priors, gt_bboxes_ignore, mode='iof')\n                ignore_max_overlaps, _ = ignore_overlaps.max(dim=1)\n            else:\n                ignore_overlaps = self.iou_calculator(\n                    gt_bboxes_ignore, priors, mode='iof')\n                ignore_max_overlaps, _ = ignore_overlaps.max(dim=0)\n            overlaps[:, ignore_max_overlaps > self.ignore_iof_thr] = -1\n\n        assign_result = self.assign_wrt_overlaps(overlaps, gt_labels)\n        if assign_on_cpu:\n            assign_result.gt_inds = assign_result.gt_inds.to(device)\n            assign_result.max_overlaps = assign_result.max_overlaps.to(device)\n            if assign_result.labels is not None:\n                assign_result.labels = assign_result.labels.to(device)\n        return assign_result\n\n    def assign_wrt_overlaps(self, overlaps: Tensor,\n                            gt_labels: Tensor) -> AssignResult:\n        \"\"\"Assign w.r.t. the overlaps of priors with gts.\n\n        Args:\n            overlaps (Tensor): Overlaps between k gt_bboxes and n bboxes,\n                shape(k, n).\n            gt_labels (Tensor): Labels of k gt_bboxes, shape (k, ).\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n        num_gts, num_bboxes = overlaps.size(0), overlaps.size(1)\n\n        # 1. assign -1 by default\n        assigned_gt_inds = overlaps.new_full((num_bboxes, ),\n                                             -1,\n                                             dtype=torch.long)\n\n        if num_gts == 0 or num_bboxes == 0:\n            # No ground truth or boxes, return empty assignment\n            max_overlaps = overlaps.new_zeros((num_bboxes, ))\n            assigned_labels = overlaps.new_full((num_bboxes, ),\n                                                -1,\n                                                dtype=torch.long)\n            if num_gts == 0:\n                # No truth, assign everything to background\n                assigned_gt_inds[:] = 0\n            return AssignResult(\n                num_gts=num_gts,\n                gt_inds=assigned_gt_inds,\n                max_overlaps=max_overlaps,\n                labels=assigned_labels)\n\n        # for each anchor, which gt best overlaps with it\n        # for each anchor, the max iou of all gts\n        max_overlaps, argmax_overlaps = overlaps.max(dim=0)\n        # for each gt, which anchor best overlaps with it\n        # for each gt, the max iou of all proposals\n        gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1)\n\n        # 2. assign negative: below\n        # the negative inds are set to be 0\n        if isinstance(self.neg_iou_thr, float):\n            assigned_gt_inds[(max_overlaps >= 0)\n                             & (max_overlaps < self.neg_iou_thr)] = 0\n        elif isinstance(self.neg_iou_thr, tuple):\n            assert len(self.neg_iou_thr) == 2\n            assigned_gt_inds[(max_overlaps >= self.neg_iou_thr[0])\n                             & (max_overlaps < self.neg_iou_thr[1])] = 0\n\n        # 3. assign positive: above positive IoU threshold\n        pos_inds = max_overlaps >= self.pos_iou_thr\n        assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1\n\n        if self.match_low_quality:\n            # Low-quality matching will overwrite the assigned_gt_inds assigned\n            # in Step 3. Thus, the assigned gt might not be the best one for\n            # prediction.\n            # For example, if bbox A has 0.9 and 0.8 iou with GT bbox 1 & 2,\n            # bbox 1 will be assigned as the best target for bbox A in step 3.\n            # However, if GT bbox 2's gt_argmax_overlaps = A, bbox A's\n            # assigned_gt_inds will be overwritten to be bbox 2.\n            # This might be the reason that it is not used in ROI Heads.\n            for i in range(num_gts):\n                if gt_max_overlaps[i] >= self.min_pos_iou:\n                    if self.gt_max_assign_all:\n                        max_iou_inds = overlaps[i, :] == gt_max_overlaps[i]\n                        assigned_gt_inds[max_iou_inds] = i + 1\n                    else:\n                        assigned_gt_inds[gt_argmax_overlaps[i]] = i + 1\n\n        assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1)\n        pos_inds = torch.nonzero(\n            assigned_gt_inds > 0, as_tuple=False).squeeze()\n        if pos_inds.numel() > 0:\n            assigned_labels[pos_inds] = gt_labels[assigned_gt_inds[pos_inds] -\n                                                  1]\n\n        return AssignResult(\n            num_gts=num_gts,\n            gt_inds=assigned_gt_inds,\n            max_overlaps=max_overlaps,\n            labels=assigned_labels)\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/multi_instance_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom .assign_result import AssignResult\nfrom .max_iou_assigner import MaxIoUAssigner\n\n\n@TASK_UTILS.register_module()\nclass MultiInstanceAssigner(MaxIoUAssigner):\n    \"\"\"Assign a corresponding gt bbox or background to each proposal bbox. If\n    we need to use a proposal box to generate multiple predict boxes,\n    `MultiInstanceAssigner` can assign multiple gt to each proposal box.\n\n    Args:\n        num_instance (int): How many bboxes are predicted by each proposal box.\n    \"\"\"\n\n    def __init__(self, num_instance: int = 2, **kwargs):\n        super().__init__(**kwargs)\n        self.num_instance = num_instance\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to bboxes.\n\n        This method assign gt bboxes to every bbox (proposal/anchor), each bbox\n        is assigned a set of gts, and the number of gts in this set is defined\n        by `self.num_instance`.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n        # Set the FG label to 1 and add ignored annotations\n        gt_labels = gt_instances.labels + 1\n        if gt_instances_ignore is not None:\n            gt_bboxes_ignore = gt_instances_ignore.bboxes\n            if hasattr(gt_instances_ignore, 'labels'):\n                gt_labels_ignore = gt_instances_ignore.labels\n            else:\n                gt_labels_ignore = torch.ones_like(gt_bboxes_ignore)[:, 0] * -1\n        else:\n            gt_bboxes_ignore = None\n            gt_labels_ignore = None\n\n        assign_on_cpu = True if (self.gpu_assign_thr > 0) and (\n            gt_bboxes.shape[0] > self.gpu_assign_thr) else False\n        # compute overlap and assign gt on CPU when number of GT is large\n        if assign_on_cpu:\n            device = priors.device\n            priors = priors.cpu()\n            gt_bboxes = gt_bboxes.cpu()\n            gt_labels = gt_labels.cpu()\n            if gt_bboxes_ignore is not None:\n                gt_bboxes_ignore = gt_bboxes_ignore.cpu()\n                gt_labels_ignore = gt_labels_ignore.cpu()\n\n        if gt_bboxes_ignore is not None:\n            all_bboxes = torch.cat([gt_bboxes, gt_bboxes_ignore], dim=0)\n            all_labels = torch.cat([gt_labels, gt_labels_ignore], dim=0)\n        else:\n            all_bboxes = gt_bboxes\n            all_labels = gt_labels\n        all_priors = torch.cat([priors, all_bboxes], dim=0)\n\n        overlaps_normal = self.iou_calculator(\n            all_priors, all_bboxes, mode='iou')\n        overlaps_ignore = self.iou_calculator(\n            all_priors, all_bboxes, mode='iof')\n        gt_ignore_mask = all_labels.eq(-1).repeat(all_priors.shape[0], 1)\n        overlaps_normal = overlaps_normal * ~gt_ignore_mask\n        overlaps_ignore = overlaps_ignore * gt_ignore_mask\n\n        overlaps_normal, overlaps_normal_indices = overlaps_normal.sort(\n            descending=True, dim=1)\n        overlaps_ignore, overlaps_ignore_indices = overlaps_ignore.sort(\n            descending=True, dim=1)\n\n        # select the roi with the higher score\n        max_overlaps_normal = overlaps_normal[:, :self.num_instance].flatten()\n        gt_assignment_normal = overlaps_normal_indices[:, :self.\n                                                       num_instance].flatten()\n        max_overlaps_ignore = overlaps_ignore[:, :self.num_instance].flatten()\n        gt_assignment_ignore = overlaps_ignore_indices[:, :self.\n                                                       num_instance].flatten()\n\n        # ignore or not\n        ignore_assign_mask = (max_overlaps_normal < self.pos_iou_thr) * (\n            max_overlaps_ignore > max_overlaps_normal)\n        overlaps = (max_overlaps_normal * ~ignore_assign_mask) + (\n            max_overlaps_ignore * ignore_assign_mask)\n        gt_assignment = (gt_assignment_normal * ~ignore_assign_mask) + (\n            gt_assignment_ignore * ignore_assign_mask)\n\n        assigned_labels = all_labels[gt_assignment]\n        fg_mask = (overlaps >= self.pos_iou_thr) * (assigned_labels != -1)\n        bg_mask = (overlaps < self.neg_iou_thr) * (overlaps >= 0)\n        assigned_labels[fg_mask] = 1\n        assigned_labels[bg_mask] = 0\n\n        overlaps = overlaps.reshape(-1, self.num_instance)\n        gt_assignment = gt_assignment.reshape(-1, self.num_instance)\n        assigned_labels = assigned_labels.reshape(-1, self.num_instance)\n\n        assign_result = AssignResult(\n            num_gts=all_bboxes.size(0),\n            gt_inds=gt_assignment,\n            max_overlaps=overlaps,\n            labels=assigned_labels)\n\n        if assign_on_cpu:\n            assign_result.gt_inds = assign_result.gt_inds.to(device)\n            assign_result.max_overlaps = assign_result.max_overlaps.to(device)\n            if assign_result.labels is not None:\n                assign_result.labels = assign_result.labels.to(device)\n        return assign_result\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/point_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\n@TASK_UTILS.register_module()\nclass PointAssigner(BaseAssigner):\n    \"\"\"Assign a corresponding gt bbox or background to each point.\n\n    Each proposals will be assigned with `0`, or a positive integer\n    indicating the ground truth index.\n\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n    \"\"\"\n\n    def __init__(self, scale: int = 4, pos_num: int = 3) -> None:\n        self.scale = scale\n        self.pos_num = pos_num\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to points.\n\n        This method assign a gt bbox to every points set, each points set\n        will be assigned with  the background_label (-1), or a label number.\n        -1 is background, and semi-positive number is the index (0-based) of\n        assigned gt.\n        The assignment is done in following steps, the order matters.\n\n        1. assign every points to the background_label (-1)\n        2. A point is assigned to some gt bbox if\n            (i) the point is within the k closest points to the gt bbox\n            (ii) the distance between this point and the gt is smaller than\n                other gt bboxes\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n\n\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        # points to be assigned, shape(n, 3) while last\n        # dimension stands for (x, y, stride).\n        points = pred_instances.priors\n\n        num_points = points.shape[0]\n        num_gts = gt_bboxes.shape[0]\n\n        if num_gts == 0 or num_points == 0:\n            # If no truth assign everything to the background\n            assigned_gt_inds = points.new_full((num_points, ),\n                                               0,\n                                               dtype=torch.long)\n            assigned_labels = points.new_full((num_points, ),\n                                              -1,\n                                              dtype=torch.long)\n            return AssignResult(\n                num_gts=num_gts,\n                gt_inds=assigned_gt_inds,\n                max_overlaps=None,\n                labels=assigned_labels)\n\n        points_xy = points[:, :2]\n        points_stride = points[:, 2]\n        points_lvl = torch.log2(\n            points_stride).int()  # [3...,4...,5...,6...,7...]\n        lvl_min, lvl_max = points_lvl.min(), points_lvl.max()\n\n        # assign gt box\n        gt_bboxes_xy = (gt_bboxes[:, :2] + gt_bboxes[:, 2:]) / 2\n        gt_bboxes_wh = (gt_bboxes[:, 2:] - gt_bboxes[:, :2]).clamp(min=1e-6)\n        scale = self.scale\n        gt_bboxes_lvl = ((torch.log2(gt_bboxes_wh[:, 0] / scale) +\n                          torch.log2(gt_bboxes_wh[:, 1] / scale)) / 2).int()\n        gt_bboxes_lvl = torch.clamp(gt_bboxes_lvl, min=lvl_min, max=lvl_max)\n\n        # stores the assigned gt index of each point\n        assigned_gt_inds = points.new_zeros((num_points, ), dtype=torch.long)\n        # stores the assigned gt dist (to this point) of each point\n        assigned_gt_dist = points.new_full((num_points, ), float('inf'))\n        points_range = torch.arange(points.shape[0])\n\n        for idx in range(num_gts):\n            gt_lvl = gt_bboxes_lvl[idx]\n            # get the index of points in this level\n            lvl_idx = gt_lvl == points_lvl\n            points_index = points_range[lvl_idx]\n            # get the points in this level\n            lvl_points = points_xy[lvl_idx, :]\n            # get the center point of gt\n            gt_point = gt_bboxes_xy[[idx], :]\n            # get width and height of gt\n            gt_wh = gt_bboxes_wh[[idx], :]\n            # compute the distance between gt center and\n            #   all points in this level\n            points_gt_dist = ((lvl_points - gt_point) / gt_wh).norm(dim=1)\n            # find the nearest k points to gt center in this level\n            min_dist, min_dist_index = torch.topk(\n                points_gt_dist, self.pos_num, largest=False)\n            # the index of nearest k points to gt center in this level\n            min_dist_points_index = points_index[min_dist_index]\n            # The less_than_recorded_index stores the index\n            #   of min_dist that is less then the assigned_gt_dist. Where\n            #   assigned_gt_dist stores the dist from previous assigned gt\n            #   (if exist) to each point.\n            less_than_recorded_index = min_dist < assigned_gt_dist[\n                min_dist_points_index]\n            # The min_dist_points_index stores the index of points satisfy:\n            #   (1) it is k nearest to current gt center in this level.\n            #   (2) it is closer to current gt center than other gt center.\n            min_dist_points_index = min_dist_points_index[\n                less_than_recorded_index]\n            # assign the result\n            assigned_gt_inds[min_dist_points_index] = idx + 1\n            assigned_gt_dist[min_dist_points_index] = min_dist[\n                less_than_recorded_index]\n\n        assigned_labels = assigned_gt_inds.new_full((num_points, ), -1)\n        pos_inds = torch.nonzero(\n            assigned_gt_inds > 0, as_tuple=False).squeeze()\n        if pos_inds.numel() > 0:\n            assigned_labels[pos_inds] = gt_labels[assigned_gt_inds[pos_inds] -\n                                                  1]\n\n        return AssignResult(\n            num_gts=num_gts,\n            gt_inds=assigned_gt_inds,\n            max_overlaps=None,\n            labels=assigned_labels)\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/region_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom ..prior_generators import anchor_inside_flags\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\ndef calc_region(\n        bbox: Tensor,\n        ratio: float,\n        stride: int,\n        featmap_size: Optional[Tuple[int, int]] = None) -> Tuple[Tensor]:\n    \"\"\"Calculate region of the box defined by the ratio, the ratio is from the\n    center of the box to every edge.\"\"\"\n    # project bbox on the feature\n    f_bbox = bbox / stride\n    x1 = torch.round((1 - ratio) * f_bbox[0] + ratio * f_bbox[2])\n    y1 = torch.round((1 - ratio) * f_bbox[1] + ratio * f_bbox[3])\n    x2 = torch.round(ratio * f_bbox[0] + (1 - ratio) * f_bbox[2])\n    y2 = torch.round(ratio * f_bbox[1] + (1 - ratio) * f_bbox[3])\n    if featmap_size is not None:\n        x1 = x1.clamp(min=0, max=featmap_size[1])\n        y1 = y1.clamp(min=0, max=featmap_size[0])\n        x2 = x2.clamp(min=0, max=featmap_size[1])\n        y2 = y2.clamp(min=0, max=featmap_size[0])\n    return (x1, y1, x2, y2)\n\n\ndef anchor_ctr_inside_region_flags(anchors: Tensor, stride: int,\n                                   region: Tuple[Tensor]) -> Tensor:\n    \"\"\"Get the flag indicate whether anchor centers are inside regions.\"\"\"\n    x1, y1, x2, y2 = region\n    f_anchors = anchors / stride\n    x = (f_anchors[:, 0] + f_anchors[:, 2]) * 0.5\n    y = (f_anchors[:, 1] + f_anchors[:, 3]) * 0.5\n    flags = (x >= x1) & (x <= x2) & (y >= y1) & (y <= y2)\n    return flags\n\n\n@TASK_UTILS.register_module()\nclass RegionAssigner(BaseAssigner):\n    \"\"\"Assign a corresponding gt bbox or background to each bbox.\n\n    Each proposals will be assigned with `-1`, `0`, or a positive integer\n    indicating the ground truth index.\n\n    - -1: don't care\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n\n    Args:\n        center_ratio (float): ratio of the region in the center of the bbox to\n            define positive sample.\n        ignore_ratio (float): ratio of the region to define ignore samples.\n    \"\"\"\n\n    def __init__(self,\n                 center_ratio: float = 0.2,\n                 ignore_ratio: float = 0.5) -> None:\n        self.center_ratio = center_ratio\n        self.ignore_ratio = ignore_ratio\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               img_meta: dict,\n               featmap_sizes: List[Tuple[int, int]],\n               num_level_anchors: List[int],\n               anchor_scale: int,\n               anchor_strides: List[int],\n               gt_instances_ignore: Optional[InstanceData] = None,\n               allowed_border: int = 0) -> AssignResult:\n        \"\"\"Assign gt to anchors.\n\n        This method assign a gt bbox to every bbox (proposal/anchor), each bbox\n        will be assigned with -1, 0, or a positive number. -1 means don't care,\n        0 means negative sample, positive number is the index (1-based) of\n        assigned gt.\n\n        The assignment is done in following steps, and the order matters.\n\n        1. Assign every anchor to 0 (negative)\n        2. (For each gt_bboxes) Compute ignore flags based on ignore_region\n           then assign -1 to anchors w.r.t. ignore flags\n        3. (For each gt_bboxes) Compute pos flags based on center_region then\n           assign gt_bboxes to anchors w.r.t. pos flags\n        4. (For each gt_bboxes) Compute ignore flags based on adjacent anchor\n           level then assign -1 to anchors w.r.t. ignore flags\n        5. Assign anchor outside of image to -1\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            img_meta (dict): Meta info of image.\n            featmap_sizes (list[tuple[int, int]]): Feature map size each level.\n            num_level_anchors (list[int]): The number of anchors in each level.\n            anchor_scale (int): Scale of the anchor.\n            anchor_strides (list[int]): Stride of the anchor.\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n            allowed_border (int, optional): The border to allow the valid\n                anchor. Defaults to 0.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n        if gt_instances_ignore is not None:\n            raise NotImplementedError\n\n        num_gts = len(gt_instances)\n        num_bboxes = len(pred_instances)\n\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        flat_anchors = pred_instances.priors\n        flat_valid_flags = pred_instances.valid_flags\n        mlvl_anchors = torch.split(flat_anchors, num_level_anchors)\n\n        if num_gts == 0 or num_bboxes == 0:\n            # No ground truth or boxes, return empty assignment\n            max_overlaps = gt_bboxes.new_zeros((num_bboxes, ))\n            assigned_gt_inds = gt_bboxes.new_zeros((num_bboxes, ),\n                                                   dtype=torch.long)\n            assigned_labels = gt_bboxes.new_full((num_bboxes, ),\n                                                 -1,\n                                                 dtype=torch.long)\n            return AssignResult(\n                num_gts=num_gts,\n                gt_inds=assigned_gt_inds,\n                max_overlaps=max_overlaps,\n                labels=assigned_labels)\n\n        num_lvls = len(mlvl_anchors)\n        r1 = (1 - self.center_ratio) / 2\n        r2 = (1 - self.ignore_ratio) / 2\n\n        scale = torch.sqrt((gt_bboxes[:, 2] - gt_bboxes[:, 0]) *\n                           (gt_bboxes[:, 3] - gt_bboxes[:, 1]))\n        min_anchor_size = scale.new_full(\n            (1, ), float(anchor_scale * anchor_strides[0]))\n        target_lvls = torch.floor(\n            torch.log2(scale) - torch.log2(min_anchor_size) + 0.5)\n        target_lvls = target_lvls.clamp(min=0, max=num_lvls - 1).long()\n\n        # 1. assign 0 (negative) by default\n        mlvl_assigned_gt_inds = []\n        mlvl_ignore_flags = []\n        for lvl in range(num_lvls):\n            assigned_gt_inds = gt_bboxes.new_full((num_level_anchors[lvl], ),\n                                                  0,\n                                                  dtype=torch.long)\n            ignore_flags = torch.zeros_like(assigned_gt_inds)\n            mlvl_assigned_gt_inds.append(assigned_gt_inds)\n            mlvl_ignore_flags.append(ignore_flags)\n\n        for gt_id in range(num_gts):\n            lvl = target_lvls[gt_id].item()\n            featmap_size = featmap_sizes[lvl]\n            stride = anchor_strides[lvl]\n            anchors = mlvl_anchors[lvl]\n            gt_bbox = gt_bboxes[gt_id, :4]\n\n            # Compute regions\n            ignore_region = calc_region(gt_bbox, r2, stride, featmap_size)\n            ctr_region = calc_region(gt_bbox, r1, stride, featmap_size)\n\n            # 2. Assign -1 to ignore flags\n            ignore_flags = anchor_ctr_inside_region_flags(\n                anchors, stride, ignore_region)\n            mlvl_assigned_gt_inds[lvl][ignore_flags] = -1\n\n            # 3. Assign gt_bboxes to pos flags\n            pos_flags = anchor_ctr_inside_region_flags(anchors, stride,\n                                                       ctr_region)\n            mlvl_assigned_gt_inds[lvl][pos_flags] = gt_id + 1\n\n            # 4. Assign -1 to ignore adjacent lvl\n            if lvl > 0:\n                d_lvl = lvl - 1\n                d_anchors = mlvl_anchors[d_lvl]\n                d_featmap_size = featmap_sizes[d_lvl]\n                d_stride = anchor_strides[d_lvl]\n                d_ignore_region = calc_region(gt_bbox, r2, d_stride,\n                                              d_featmap_size)\n                ignore_flags = anchor_ctr_inside_region_flags(\n                    d_anchors, d_stride, d_ignore_region)\n                mlvl_ignore_flags[d_lvl][ignore_flags] = 1\n            if lvl < num_lvls - 1:\n                u_lvl = lvl + 1\n                u_anchors = mlvl_anchors[u_lvl]\n                u_featmap_size = featmap_sizes[u_lvl]\n                u_stride = anchor_strides[u_lvl]\n                u_ignore_region = calc_region(gt_bbox, r2, u_stride,\n                                              u_featmap_size)\n                ignore_flags = anchor_ctr_inside_region_flags(\n                    u_anchors, u_stride, u_ignore_region)\n                mlvl_ignore_flags[u_lvl][ignore_flags] = 1\n\n        # 4. (cont.) Assign -1 to ignore adjacent lvl\n        for lvl in range(num_lvls):\n            ignore_flags = mlvl_ignore_flags[lvl]\n            mlvl_assigned_gt_inds[lvl][ignore_flags == 1] = -1\n\n        # 5. Assign -1 to anchor outside of image\n        flat_assigned_gt_inds = torch.cat(mlvl_assigned_gt_inds)\n        assert (flat_assigned_gt_inds.shape[0] == flat_anchors.shape[0] ==\n                flat_valid_flags.shape[0])\n        inside_flags = anchor_inside_flags(flat_anchors, flat_valid_flags,\n                                           img_meta['img_shape'],\n                                           allowed_border)\n        outside_flags = ~inside_flags\n        flat_assigned_gt_inds[outside_flags] = -1\n\n        assigned_labels = torch.zeros_like(flat_assigned_gt_inds)\n        pos_flags = flat_assigned_gt_inds > 0\n        assigned_labels[pos_flags] = gt_labels[flat_assigned_gt_inds[pos_flags]\n                                               - 1]\n\n        return AssignResult(\n            num_gts=num_gts,\n            gt_inds=flat_assigned_gt_inds,\n            max_overlaps=None,\n            labels=assigned_labels)\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/sim_ota_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.utils import ConfigType\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\nINF = 100000.0\nEPS = 1.0e-7\n\n\n@TASK_UTILS.register_module()\nclass SimOTAAssigner(BaseAssigner):\n    \"\"\"Computes matching between predictions and ground truth.\n\n    Args:\n        center_radius (float): Ground truth center size\n            to judge whether a prior is in center. Defaults to 2.5.\n        candidate_topk (int): The candidate top-k which used to\n            get top-k ious to calculate dynamic-k. Defaults to 10.\n        iou_weight (float): The scale factor for regression\n            iou cost. Defaults to 3.0.\n        cls_weight (float): The scale factor for classification\n            cost. Defaults to 1.0.\n        iou_calculator (ConfigType): Config of overlaps Calculator.\n            Defaults to dict(type='BboxOverlaps2D').\n    \"\"\"\n\n    def __init__(self,\n                 center_radius: float = 2.5,\n                 candidate_topk: int = 10,\n                 iou_weight: float = 3.0,\n                 cls_weight: float = 1.0,\n                 iou_calculator: ConfigType = dict(type='BboxOverlaps2D')):\n        self.center_radius = center_radius\n        self.candidate_topk = candidate_topk\n        self.iou_weight = iou_weight\n        self.cls_weight = cls_weight\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to priors using SimOTA.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n        Returns:\n            obj:`AssignResult`: The assigned result.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        num_gt = gt_bboxes.size(0)\n\n        decoded_bboxes = pred_instances.bboxes\n        pred_scores = pred_instances.scores\n        priors = pred_instances.priors\n        num_bboxes = decoded_bboxes.size(0)\n\n        # assign 0 by default\n        assigned_gt_inds = decoded_bboxes.new_full((num_bboxes, ),\n                                                   0,\n                                                   dtype=torch.long)\n        if num_gt == 0 or num_bboxes == 0:\n            # No ground truth or boxes, return empty assignment\n            max_overlaps = decoded_bboxes.new_zeros((num_bboxes, ))\n            assigned_labels = decoded_bboxes.new_full((num_bboxes, ),\n                                                      -1,\n                                                      dtype=torch.long)\n            return AssignResult(\n                num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n\n        valid_mask, is_in_boxes_and_center = self.get_in_gt_and_in_center_info(\n            priors, gt_bboxes)\n        valid_decoded_bbox = decoded_bboxes[valid_mask]\n        valid_pred_scores = pred_scores[valid_mask]\n        num_valid = valid_decoded_bbox.size(0)\n        if num_valid == 0:\n            # No valid bboxes, return empty assignment\n            max_overlaps = decoded_bboxes.new_zeros((num_bboxes, ))\n            assigned_labels = decoded_bboxes.new_full((num_bboxes, ),\n                                                      -1,\n                                                      dtype=torch.long)\n            return AssignResult(\n                num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n\n        pairwise_ious = self.iou_calculator(valid_decoded_bbox, gt_bboxes)\n        iou_cost = -torch.log(pairwise_ious + EPS)\n\n        gt_onehot_label = (\n            F.one_hot(gt_labels.to(torch.int64),\n                      pred_scores.shape[-1]).float().unsqueeze(0).repeat(\n                          num_valid, 1, 1))\n\n        valid_pred_scores = valid_pred_scores.unsqueeze(1).repeat(1, num_gt, 1)\n        # disable AMP autocast and calculate BCE with FP32 to avoid overflow\n        with torch.cuda.amp.autocast(enabled=False):\n            cls_cost = (\n                F.binary_cross_entropy(\n                    valid_pred_scores.to(dtype=torch.float32),\n                    gt_onehot_label,\n                    reduction='none',\n                ).sum(-1).to(dtype=valid_pred_scores.dtype))\n\n        cost_matrix = (\n            cls_cost * self.cls_weight + iou_cost * self.iou_weight +\n            (~is_in_boxes_and_center) * INF)\n\n        matched_pred_ious, matched_gt_inds = \\\n            self.dynamic_k_matching(\n                cost_matrix, pairwise_ious, num_gt, valid_mask)\n\n        # convert to AssignResult format\n        assigned_gt_inds[valid_mask] = matched_gt_inds + 1\n        assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1)\n        assigned_labels[valid_mask] = gt_labels[matched_gt_inds].long()\n        max_overlaps = assigned_gt_inds.new_full((num_bboxes, ),\n                                                 -INF,\n                                                 dtype=torch.float32)\n        max_overlaps[valid_mask] = matched_pred_ious\n        return AssignResult(\n            num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n\n    def get_in_gt_and_in_center_info(\n            self, priors: Tensor, gt_bboxes: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Get the information of which prior is in gt bboxes and gt center\n        priors.\"\"\"\n        num_gt = gt_bboxes.size(0)\n\n        repeated_x = priors[:, 0].unsqueeze(1).repeat(1, num_gt)\n        repeated_y = priors[:, 1].unsqueeze(1).repeat(1, num_gt)\n        repeated_stride_x = priors[:, 2].unsqueeze(1).repeat(1, num_gt)\n        repeated_stride_y = priors[:, 3].unsqueeze(1).repeat(1, num_gt)\n\n        # is prior centers in gt bboxes, shape: [n_prior, n_gt]\n        l_ = repeated_x - gt_bboxes[:, 0]\n        t_ = repeated_y - gt_bboxes[:, 1]\n        r_ = gt_bboxes[:, 2] - repeated_x\n        b_ = gt_bboxes[:, 3] - repeated_y\n\n        deltas = torch.stack([l_, t_, r_, b_], dim=1)\n        is_in_gts = deltas.min(dim=1).values > 0\n        is_in_gts_all = is_in_gts.sum(dim=1) > 0\n\n        # is prior centers in gt centers\n        gt_cxs = (gt_bboxes[:, 0] + gt_bboxes[:, 2]) / 2.0\n        gt_cys = (gt_bboxes[:, 1] + gt_bboxes[:, 3]) / 2.0\n        ct_box_l = gt_cxs - self.center_radius * repeated_stride_x\n        ct_box_t = gt_cys - self.center_radius * repeated_stride_y\n        ct_box_r = gt_cxs + self.center_radius * repeated_stride_x\n        ct_box_b = gt_cys + self.center_radius * repeated_stride_y\n\n        cl_ = repeated_x - ct_box_l\n        ct_ = repeated_y - ct_box_t\n        cr_ = ct_box_r - repeated_x\n        cb_ = ct_box_b - repeated_y\n\n        ct_deltas = torch.stack([cl_, ct_, cr_, cb_], dim=1)\n        is_in_cts = ct_deltas.min(dim=1).values > 0\n        is_in_cts_all = is_in_cts.sum(dim=1) > 0\n\n        # in boxes or in centers, shape: [num_priors]\n        is_in_gts_or_centers = is_in_gts_all | is_in_cts_all\n\n        # both in boxes and centers, shape: [num_fg, num_gt]\n        is_in_boxes_and_centers = (\n            is_in_gts[is_in_gts_or_centers, :]\n            & is_in_cts[is_in_gts_or_centers, :])\n        return is_in_gts_or_centers, is_in_boxes_and_centers\n\n    def dynamic_k_matching(self, cost: Tensor, pairwise_ious: Tensor,\n                           num_gt: int,\n                           valid_mask: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Use IoU and matching cost to calculate the dynamic top-k positive\n        targets.\"\"\"\n        matching_matrix = torch.zeros_like(cost, dtype=torch.uint8)\n        # select candidate topk ious for dynamic-k calculation\n        candidate_topk = min(self.candidate_topk, pairwise_ious.size(0))\n        topk_ious, _ = torch.topk(pairwise_ious, candidate_topk, dim=0)\n        # calculate dynamic k for each gt\n        dynamic_ks = torch.clamp(topk_ious.sum(0).int(), min=1)\n        for gt_idx in range(num_gt):\n            _, pos_idx = torch.topk(\n                cost[:, gt_idx], k=dynamic_ks[gt_idx], largest=False)\n            matching_matrix[:, gt_idx][pos_idx] = 1\n\n        del topk_ious, dynamic_ks, pos_idx\n\n        prior_match_gt_mask = matching_matrix.sum(1) > 1\n        if prior_match_gt_mask.sum() > 0:\n            cost_min, cost_argmin = torch.min(\n                cost[prior_match_gt_mask, :], dim=1)\n            matching_matrix[prior_match_gt_mask, :] *= 0\n            matching_matrix[prior_match_gt_mask, cost_argmin] = 1\n        # get foreground mask inside box and center prior\n        fg_mask_inboxes = matching_matrix.sum(1) > 0\n        valid_mask[valid_mask.clone()] = fg_mask_inboxes\n\n        matched_gt_inds = matching_matrix[fg_mask_inboxes, :].argmax(1)\n        matched_pred_ious = (matching_matrix *\n                             pairwise_ious).sum(1)[fg_mask_inboxes]\n        return matched_pred_ious, matched_gt_inds\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/task_aligned_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.utils import ConfigType\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\nINF = 100000000\n\n\n@TASK_UTILS.register_module()\nclass TaskAlignedAssigner(BaseAssigner):\n    \"\"\"Task aligned assigner used in the paper:\n    `TOOD: Task-aligned One-stage Object Detection.\n    <https://arxiv.org/abs/2108.07755>`_.\n\n    Assign a corresponding gt bbox or background to each predicted bbox.\n    Each bbox will be assigned with `0` or a positive integer\n    indicating the ground truth index.\n\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n\n    Args:\n        topk (int): number of bbox selected in each level\n        iou_calculator (:obj:`ConfigDict` or dict): Config dict for iou\n            calculator. Defaults to ``dict(type='BboxOverlaps2D')``\n    \"\"\"\n\n    def __init__(self,\n                 topk: int,\n                 iou_calculator: ConfigType = dict(type='BboxOverlaps2D')):\n        assert topk >= 1\n        self.topk = topk\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               alpha: int = 1,\n               beta: int = 6) -> AssignResult:\n        \"\"\"Assign gt to bboxes.\n\n        The assignment is done in following steps\n\n        1. compute alignment metric between all bbox (bbox of all pyramid\n           levels) and gt\n        2. select top-k bbox as candidates for each gt\n        3. limit the positive sample's center in gt (because the anchor-free\n           detector only can predict positive distance)\n\n\n        Args:\n            pred_instances (:obj:`InstaceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors, points, or bboxes predicted by the model,\n                shape(n, 4).\n            gt_instances (:obj:`InstaceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            gt_instances_ignore (:obj:`InstaceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n            alpha (int): Hyper-parameters related to alignment_metrics.\n                Defaults to 1.\n            beta (int): Hyper-parameters related to alignment_metrics.\n                Defaults to 6.\n\n        Returns:\n            :obj:`TaskAlignedAssignResult`: The assign result.\n        \"\"\"\n        priors = pred_instances.priors\n        decode_bboxes = pred_instances.bboxes\n        pred_scores = pred_instances.scores\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n\n        priors = priors[:, :4]\n        num_gt, num_bboxes = gt_bboxes.size(0), priors.size(0)\n        # compute alignment metric between all bbox and gt\n        overlaps = self.iou_calculator(decode_bboxes, gt_bboxes).detach()\n        bbox_scores = pred_scores[:, gt_labels].detach()\n        # assign 0 by default\n        assigned_gt_inds = priors.new_full((num_bboxes, ), 0, dtype=torch.long)\n        assign_metrics = priors.new_zeros((num_bboxes, ))\n\n        if num_gt == 0 or num_bboxes == 0:\n            # No ground truth or boxes, return empty assignment\n            max_overlaps = priors.new_zeros((num_bboxes, ))\n            if num_gt == 0:\n                # No gt boxes, assign everything to background\n                assigned_gt_inds[:] = 0\n            assigned_labels = priors.new_full((num_bboxes, ),\n                                              -1,\n                                              dtype=torch.long)\n            assign_result = AssignResult(\n                num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n            assign_result.assign_metrics = assign_metrics\n            return assign_result\n\n        # select top-k bboxes as candidates for each gt\n        alignment_metrics = bbox_scores**alpha * overlaps**beta\n        topk = min(self.topk, alignment_metrics.size(0))\n        _, candidate_idxs = alignment_metrics.topk(topk, dim=0, largest=True)\n        candidate_metrics = alignment_metrics[candidate_idxs,\n                                              torch.arange(num_gt)]\n        is_pos = candidate_metrics > 0\n\n        # limit the positive sample's center in gt\n        priors_cx = (priors[:, 0] + priors[:, 2]) / 2.0\n        priors_cy = (priors[:, 1] + priors[:, 3]) / 2.0\n        for gt_idx in range(num_gt):\n            candidate_idxs[:, gt_idx] += gt_idx * num_bboxes\n        ep_priors_cx = priors_cx.view(1, -1).expand(\n            num_gt, num_bboxes).contiguous().view(-1)\n        ep_priors_cy = priors_cy.view(1, -1).expand(\n            num_gt, num_bboxes).contiguous().view(-1)\n        candidate_idxs = candidate_idxs.view(-1)\n\n        # calculate the left, top, right, bottom distance between positive\n        # bbox center and gt side\n        l_ = ep_priors_cx[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 0]\n        t_ = ep_priors_cy[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 1]\n        r_ = gt_bboxes[:, 2] - ep_priors_cx[candidate_idxs].view(-1, num_gt)\n        b_ = gt_bboxes[:, 3] - ep_priors_cy[candidate_idxs].view(-1, num_gt)\n        is_in_gts = torch.stack([l_, t_, r_, b_], dim=1).min(dim=1)[0] > 0.01\n        is_pos = is_pos & is_in_gts\n\n        # if an anchor box is assigned to multiple gts,\n        # the one with the highest iou will be selected.\n        overlaps_inf = torch.full_like(overlaps,\n                                       -INF).t().contiguous().view(-1)\n        index = candidate_idxs.view(-1)[is_pos.view(-1)]\n        overlaps_inf[index] = overlaps.t().contiguous().view(-1)[index]\n        overlaps_inf = overlaps_inf.view(num_gt, -1).t()\n\n        max_overlaps, argmax_overlaps = overlaps_inf.max(dim=1)\n        assigned_gt_inds[\n            max_overlaps != -INF] = argmax_overlaps[max_overlaps != -INF] + 1\n        assign_metrics[max_overlaps != -INF] = alignment_metrics[\n            max_overlaps != -INF, argmax_overlaps[max_overlaps != -INF]]\n\n        assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1)\n        pos_inds = torch.nonzero(\n            assigned_gt_inds > 0, as_tuple=False).squeeze()\n        if pos_inds.numel() > 0:\n            assigned_labels[pos_inds] = gt_labels[assigned_gt_inds[pos_inds] -\n                                                  1]\n        assign_result = AssignResult(\n            num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels)\n        assign_result.assign_metrics = assign_metrics\n        return assign_result\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/topk_hungarian_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom mmengine.structures import BaseDataElement\nfrom scipy.optimize import linear_sum_assignment\n\nfrom mmdet.registry import TASK_UTILS\nfrom .assign_result import AssignResult\nfrom .task_aligned_assigner import TaskAlignedAssigner\n\n\n@TASK_UTILS.register_module()\nclass TopkHungarianAssigner(TaskAlignedAssigner):\n    \"\"\"Computes 1-to-k matching between ground truth and predictions.\n\n    This class computes an assignment between the targets and the predictions\n    based on the costs. The costs are weighted sum of some components.\n    For DETR the costs are weighted sum of classification cost, regression L1\n    cost and regression iou cost. The targets don't include the no_object, so\n    generally there are more predictions than targets. After the 1-to-k\n    gt-pred matching, the un-matched are treated as backgrounds. Thus each\n    query prediction will be assigned with `0` or a positive integer\n    indicating the ground truth index:\n\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n\n    Args:\n        cls_cost (dict): Classification cost configuration.\n        reg_cost (dict): Regression L1  cost configuration.\n        iou_cost (dict): Regression iou cost configuration.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 cls_cost=dict(type='FocalLossCost', weight=2.0),\n                 reg_cost=dict(type='BBoxL1Cost', weight=5.0),\n                 iou_cost=dict(type='IoUCost', iou_mode='giou', weight=2.0),\n                 **kwargs):\n        super(TopkHungarianAssigner, self).__init__(*args, **kwargs)\n\n        self.cls_cost = TASK_UTILS.build(cls_cost)\n        self.reg_cost = TASK_UTILS.build(reg_cost)\n        self.iou_cost = TASK_UTILS.build(iou_cost)\n\n    def assign(self,\n               pred_scores,\n               decode_bboxes,\n               gt_bboxes,\n               gt_labels,\n               img_meta,\n               alpha=1,\n               beta=6,\n               **kwargs):\n        \"\"\"Computes 1-to-k gt-pred matching based on the weighted costs.\n\n        This method assign each query prediction to a ground truth or\n        background. The `assigned_gt_inds` with -1 means don't care,\n        0 means negative sample, and positive number is the index (1-based)\n        of assigned gt.\n        The assignment is done in the following steps, the order matters.\n\n        1. Assign every prediction to -1.\n        2. Compute the weighted costs, each cost has shape (num_pred, num_gt).\n        3. Update topk to be min(topk, int(num_pred / num_gt)), then repeat\n            costs topk times to shape: (num_pred, num_gt * topk), so that each\n            gt will match topk predictions.\n        3. Do Hungarian matching on CPU based on the costs.\n        4. Assign all to 0 (background) first, then for each matched pair\n           between predictions and gts, treat this prediction as foreground\n           and assign the corresponding gt index (plus 1) to it.\n        5. Calculate alignment metrics and overlaps of each matched pred-gt\n            pair.\n\n        Args:\n            pred_scores (Tensor): Predicted normalized classification\n                scores for one image, has shape (num_dense_queries,\n                cls_out_channels).\n            decode_bboxes (Tensor): Predicted unnormalized bbox coordinates\n                for one image, has shape (num_dense_queries, 4) with the\n                last dimension arranged as (x1, y1, x2, y2).\n            gt_bboxes (Tensor): Unnormalized ground truth\n                bboxes for one image, has shape (num_gt, 4) with the\n                last dimension arranged as (x1, y1, x2, y2).\n                NOTE: num_gt is dynamic for each image.\n            gt_labels (Tensor): Ground truth classification\n                    index for the image, has shape (num_gt,).\n                    NOTE: num_gt is dynamic for each image.\n            img_meta (dict): Meta information for one image.\n            alpha (int): Hyper-parameters related to alignment_metrics.\n                Defaults to 1.\n            beta (int): Hyper-parameters related to alignment_metrics.\n                Defaults to 6.\n\n        Returns:\n            :obj:`AssignResult`: The assigned result.\n        \"\"\"\n        pred_scores = pred_scores.detach()\n        decode_bboxes = decode_bboxes.detach()\n        temp_overlaps = self.iou_calculator(decode_bboxes, gt_bboxes).detach()\n        bbox_scores = pred_scores[:, gt_labels].detach()\n        alignment_metrics = bbox_scores**alpha * temp_overlaps**beta\n\n        pred_instances = BaseDataElement()\n        gt_instances = BaseDataElement()\n\n        pred_instances.bboxes = decode_bboxes\n        gt_instances.bboxes = gt_bboxes\n\n        pred_instances.scores = pred_scores\n        gt_instances.labels = gt_labels\n\n        reg_cost = self.reg_cost(pred_instances, gt_instances, img_meta)\n        iou_cost = self.iou_cost(pred_instances, gt_instances, img_meta)\n        cls_cost = self.cls_cost(pred_instances, gt_instances, img_meta)\n        all_cost = cls_cost + reg_cost + iou_cost\n\n        num_gt, num_bboxes = gt_bboxes.size(0), pred_scores.size(0)\n        if num_gt > 0:\n            # assign 0 by default\n            assigned_gt_inds = pred_scores.new_full((num_bboxes, ),\n                                                    0,\n                                                    dtype=torch.long)\n            select_cost = all_cost\n\n            topk = min(self.topk, int(len(select_cost) / num_gt))\n\n            # Repeat the ground truth `topk` times to perform 1-to-k gt-pred\n            #   matching. For example, if `num_pred` = 900, `num_gt` = 3, then\n            #   there are only 3 gt-pred pairs in sum for 1-1 matching.\n            #   However, for 1-k gt-pred matching, if `topk` = 4, then each\n            #   gt is assigned 4 unique predictions, so there would be 12\n            #   gt-pred pairs in sum.\n            repeat_select_cost = select_cost[...,\n                                             None].repeat(1, 1, topk).view(\n                                                 select_cost.size(0), -1)\n            # anchor index and gt index\n            matched_row_inds, matched_col_inds = linear_sum_assignment(\n                repeat_select_cost.detach().cpu().numpy())\n            matched_row_inds = torch.from_numpy(matched_row_inds).to(\n                pred_scores.device)\n            matched_col_inds = torch.from_numpy(matched_col_inds).to(\n                pred_scores.device)\n\n            match_gt_ids = matched_col_inds // topk\n            candidate_idxs = matched_row_inds\n\n            assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1)\n\n            if candidate_idxs.numel() > 0:\n                assigned_labels[candidate_idxs] = gt_labels[match_gt_ids]\n            else:\n                assigned_labels = None\n\n            assigned_gt_inds[candidate_idxs] = match_gt_ids + 1\n\n            overlaps = self.iou_calculator(\n                decode_bboxes[candidate_idxs],\n                gt_bboxes[match_gt_ids],\n                is_aligned=True).detach()\n\n            temp_pos_alignment_metrics = alignment_metrics[candidate_idxs]\n            pos_alignment_metrics = torch.gather(temp_pos_alignment_metrics, 1,\n                                                 match_gt_ids[:,\n                                                              None]).view(-1)\n            assign_result = AssignResult(\n                num_gt, assigned_gt_inds, overlaps, labels=assigned_labels)\n\n            assign_result.assign_metrics = pos_alignment_metrics\n            return assign_result\n        else:\n\n            assigned_gt_inds = pred_scores.new_full((num_bboxes, ),\n                                                    -1,\n                                                    dtype=torch.long)\n\n            assigned_labels = pred_scores.new_full((num_bboxes, ),\n                                                   -1,\n                                                   dtype=torch.long)\n\n            assigned_gt_inds[:] = 0\n            return AssignResult(\n                0, assigned_gt_inds, None, labels=assigned_labels)\n"
  },
  {
    "path": "mmdet/models/task_modules/assigners/uniform_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import bbox_xyxy_to_cxcywh\nfrom mmdet.utils import ConfigType\nfrom .assign_result import AssignResult\nfrom .base_assigner import BaseAssigner\n\n\n@TASK_UTILS.register_module()\nclass UniformAssigner(BaseAssigner):\n    \"\"\"Uniform Matching between the priors and gt boxes, which can achieve\n    balance in positive priors, and gt_bboxes_ignore was not considered for\n    now.\n\n    Args:\n        pos_ignore_thr (float): the threshold to ignore positive priors\n        neg_ignore_thr (float): the threshold to ignore negative priors\n        match_times(int): Number of positive priors for each gt box.\n           Defaults to 4.\n        iou_calculator (:obj:`ConfigDict` or dict): Config dict for iou\n            calculator. Defaults to ``dict(type='BboxOverlaps2D')``\n    \"\"\"\n\n    def __init__(self,\n                 pos_ignore_thr: float,\n                 neg_ignore_thr: float,\n                 match_times: int = 4,\n                 iou_calculator: ConfigType = dict(type='BboxOverlaps2D')):\n        self.match_times = match_times\n        self.pos_ignore_thr = pos_ignore_thr\n        self.neg_ignore_thr = neg_ignore_thr\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def assign(\n            self,\n            pred_instances: InstanceData,\n            gt_instances: InstanceData,\n            gt_instances_ignore: Optional[InstanceData] = None\n    ) -> AssignResult:\n        \"\"\"Assign gt to priors.\n\n        The assignment is done in following steps\n\n        1. assign -1 by default\n        2. compute the L1 cost between boxes. Note that we use priors and\n           predict boxes both\n        3. compute the ignore indexes use gt_bboxes and predict boxes\n        4. compute the ignore indexes of positive sample use priors and\n           predict boxes\n\n\n        Args:\n            pred_instances (:obj:`InstaceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be priors, points, or bboxes predicted by the model,\n                shape(n, 4).\n            gt_instances (:obj:`InstaceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            gt_instances_ignore (:obj:`InstaceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n        \"\"\"\n\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        priors = pred_instances.priors\n        bbox_pred = pred_instances.decoder_priors\n\n        num_gts, num_bboxes = gt_bboxes.size(0), bbox_pred.size(0)\n\n        # 1. assign -1 by default\n        assigned_gt_inds = bbox_pred.new_full((num_bboxes, ),\n                                              0,\n                                              dtype=torch.long)\n        assigned_labels = bbox_pred.new_full((num_bboxes, ),\n                                             -1,\n                                             dtype=torch.long)\n        if num_gts == 0 or num_bboxes == 0:\n            # No ground truth or boxes, return empty assignment\n            if num_gts == 0:\n                # No ground truth, assign all to background\n                assigned_gt_inds[:] = 0\n            assign_result = AssignResult(\n                num_gts, assigned_gt_inds, None, labels=assigned_labels)\n            assign_result.set_extra_property(\n                'pos_idx', bbox_pred.new_empty(0, dtype=torch.bool))\n            assign_result.set_extra_property('pos_predicted_boxes',\n                                             bbox_pred.new_empty((0, 4)))\n            assign_result.set_extra_property('target_boxes',\n                                             bbox_pred.new_empty((0, 4)))\n            return assign_result\n\n        # 2. Compute the L1 cost between boxes\n        # Note that we use priors and predict boxes both\n        cost_bbox = torch.cdist(\n            bbox_xyxy_to_cxcywh(bbox_pred),\n            bbox_xyxy_to_cxcywh(gt_bboxes),\n            p=1)\n        cost_bbox_priors = torch.cdist(\n            bbox_xyxy_to_cxcywh(priors), bbox_xyxy_to_cxcywh(gt_bboxes), p=1)\n\n        # We found that topk function has different results in cpu and\n        # cuda mode. In order to ensure consistency with the source code,\n        # we also use cpu mode.\n        # TODO: Check whether the performance of cpu and cuda are the same.\n        C = cost_bbox.cpu()\n        C1 = cost_bbox_priors.cpu()\n\n        # self.match_times x n\n        index = torch.topk(\n            C,  # c=b,n,x c[i]=n,x\n            k=self.match_times,\n            dim=0,\n            largest=False)[1]\n\n        # self.match_times x n\n        index1 = torch.topk(C1, k=self.match_times, dim=0, largest=False)[1]\n        # (self.match_times*2) x n\n        indexes = torch.cat((index, index1),\n                            dim=1).reshape(-1).to(bbox_pred.device)\n\n        pred_overlaps = self.iou_calculator(bbox_pred, gt_bboxes)\n        anchor_overlaps = self.iou_calculator(priors, gt_bboxes)\n        pred_max_overlaps, _ = pred_overlaps.max(dim=1)\n        anchor_max_overlaps, _ = anchor_overlaps.max(dim=0)\n\n        # 3. Compute the ignore indexes use gt_bboxes and predict boxes\n        ignore_idx = pred_max_overlaps > self.neg_ignore_thr\n        assigned_gt_inds[ignore_idx] = -1\n\n        # 4. Compute the ignore indexes of positive sample use priors\n        # and predict boxes\n        pos_gt_index = torch.arange(\n            0, C1.size(1),\n            device=bbox_pred.device).repeat(self.match_times * 2)\n        pos_ious = anchor_overlaps[indexes, pos_gt_index]\n        pos_ignore_idx = pos_ious < self.pos_ignore_thr\n\n        pos_gt_index_with_ignore = pos_gt_index + 1\n        pos_gt_index_with_ignore[pos_ignore_idx] = -1\n        assigned_gt_inds[indexes] = pos_gt_index_with_ignore\n\n        if gt_labels is not None:\n            assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1)\n            pos_inds = torch.nonzero(\n                assigned_gt_inds > 0, as_tuple=False).squeeze()\n            if pos_inds.numel() > 0:\n                assigned_labels[pos_inds] = gt_labels[\n                    assigned_gt_inds[pos_inds] - 1]\n        else:\n            assigned_labels = None\n\n        assign_result = AssignResult(\n            num_gts,\n            assigned_gt_inds,\n            anchor_max_overlaps,\n            labels=assigned_labels)\n        assign_result.set_extra_property('pos_idx', ~pos_ignore_idx)\n        assign_result.set_extra_property('pos_predicted_boxes',\n                                         bbox_pred[indexes])\n        assign_result.set_extra_property('target_boxes',\n                                         gt_bboxes[pos_gt_index])\n        return assign_result\n"
  },
  {
    "path": "mmdet/models/task_modules/builder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nfrom mmdet.registry import TASK_UTILS\n\nPRIOR_GENERATORS = TASK_UTILS\nANCHOR_GENERATORS = TASK_UTILS\nBBOX_ASSIGNERS = TASK_UTILS\nBBOX_SAMPLERS = TASK_UTILS\nBBOX_CODERS = TASK_UTILS\nMATCH_COSTS = TASK_UTILS\nIOU_CALCULATORS = TASK_UTILS\n\n\ndef build_bbox_coder(cfg, **default_args):\n    \"\"\"Builder of box coder.\"\"\"\n    warnings.warn('``build_sampler`` would be deprecated soon, please use '\n                  '``mmdet.registry.TASK_UTILS.build()`` ')\n    return TASK_UTILS.build(cfg, default_args=default_args)\n\n\ndef build_iou_calculator(cfg, default_args=None):\n    \"\"\"Builder of IoU calculator.\"\"\"\n    warnings.warn(\n        '``build_iou_calculator`` would be deprecated soon, please use '\n        '``mmdet.registry.TASK_UTILS.build()`` ')\n    return TASK_UTILS.build(cfg, default_args=default_args)\n\n\ndef build_match_cost(cfg, default_args=None):\n    \"\"\"Builder of IoU calculator.\"\"\"\n    warnings.warn('``build_match_cost`` would be deprecated soon, please use '\n                  '``mmdet.registry.TASK_UTILS.build()`` ')\n    return TASK_UTILS.build(cfg, default_args=default_args)\n\n\ndef build_assigner(cfg, **default_args):\n    \"\"\"Builder of box assigner.\"\"\"\n    warnings.warn('``build_assigner`` would be deprecated soon, please use '\n                  '``mmdet.registry.TASK_UTILS.build()`` ')\n    return TASK_UTILS.build(cfg, default_args=default_args)\n\n\ndef build_sampler(cfg, **default_args):\n    \"\"\"Builder of box sampler.\"\"\"\n    warnings.warn('``build_sampler`` would be deprecated soon, please use '\n                  '``mmdet.registry.TASK_UTILS.build()`` ')\n    return TASK_UTILS.build(cfg, default_args=default_args)\n\n\ndef build_prior_generator(cfg, default_args=None):\n    warnings.warn(\n        '``build_prior_generator`` would be deprecated soon, please use '\n        '``mmdet.registry.TASK_UTILS.build()`` ')\n    return TASK_UTILS.build(cfg, default_args=default_args)\n\n\ndef build_anchor_generator(cfg, default_args=None):\n    warnings.warn(\n        '``build_anchor_generator`` would be deprecated soon, please use '\n        '``mmdet.registry.TASK_UTILS.build()`` ')\n    return TASK_UTILS.build(cfg, default_args=default_args)\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_bbox_coder import BaseBBoxCoder\nfrom .bucketing_bbox_coder import BucketingBBoxCoder\nfrom .delta_xywh_bbox_coder import (DeltaXYWHBBoxCoder,\n                                    DeltaXYWHBBoxCoderForGLIP)\nfrom .distance_point_bbox_coder import DistancePointBBoxCoder\nfrom .legacy_delta_xywh_bbox_coder import LegacyDeltaXYWHBBoxCoder\nfrom .pseudo_bbox_coder import PseudoBBoxCoder\nfrom .tblr_bbox_coder import TBLRBBoxCoder\nfrom .yolo_bbox_coder import YOLOBBoxCoder\n\n__all__ = [\n    'BaseBBoxCoder', 'PseudoBBoxCoder', 'DeltaXYWHBBoxCoder',\n    'LegacyDeltaXYWHBBoxCoder', 'TBLRBBoxCoder', 'YOLOBBoxCoder',\n    'BucketingBBoxCoder', 'DistancePointBBoxCoder', 'DeltaXYWHBBoxCoderForGLIP'\n]\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/base_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\n\n\nclass BaseBBoxCoder(metaclass=ABCMeta):\n    \"\"\"Base bounding box coder.\n\n    Args:\n        use_box_type (bool): Whether to warp decoded boxes with the\n            box type data structure. Defaults to False.\n    \"\"\"\n\n    # The size of the last of dimension of the encoded tensor.\n    encode_size = 4\n\n    def __init__(self, use_box_type: bool = False, **kwargs):\n        self.use_box_type = use_box_type\n\n    @abstractmethod\n    def encode(self, bboxes, gt_bboxes):\n        \"\"\"Encode deltas between bboxes and ground truth boxes.\"\"\"\n\n    @abstractmethod\n    def decode(self, bboxes, bboxes_pred):\n        \"\"\"Decode the predicted bboxes according to prediction and base\n        boxes.\"\"\"\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/bucketing_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Sequence, Tuple, Union\n\nimport numpy as np\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import (BaseBoxes, HorizontalBoxes, bbox_rescale,\n                                   get_box_tensor)\nfrom .base_bbox_coder import BaseBBoxCoder\n\n\n@TASK_UTILS.register_module()\nclass BucketingBBoxCoder(BaseBBoxCoder):\n    \"\"\"Bucketing BBox Coder for Side-Aware Boundary Localization (SABL).\n\n    Boundary Localization with Bucketing and Bucketing Guided Rescoring\n    are implemented here.\n\n    Please refer to https://arxiv.org/abs/1912.04260 for more details.\n\n    Args:\n        num_buckets (int): Number of buckets.\n        scale_factor (int): Scale factor of proposals to generate buckets.\n        offset_topk (int): Topk buckets are used to generate\n             bucket fine regression targets. Defaults to 2.\n        offset_upperbound (float): Offset upperbound to generate\n             bucket fine regression targets.\n             To avoid too large offset displacements. Defaults to 1.0.\n        cls_ignore_neighbor (bool): Ignore second nearest bucket or Not.\n             Defaults to True.\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 num_buckets: int,\n                 scale_factor: int,\n                 offset_topk: int = 2,\n                 offset_upperbound: float = 1.0,\n                 cls_ignore_neighbor: bool = True,\n                 clip_border: bool = True,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.num_buckets = num_buckets\n        self.scale_factor = scale_factor\n        self.offset_topk = offset_topk\n        self.offset_upperbound = offset_upperbound\n        self.cls_ignore_neighbor = cls_ignore_neighbor\n        self.clip_border = clip_border\n\n    def encode(self, bboxes: Union[Tensor, BaseBoxes],\n               gt_bboxes: Union[Tensor, BaseBoxes]) -> Tuple[Tensor]:\n        \"\"\"Get bucketing estimation and fine regression targets during\n        training.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): source boxes,\n                e.g., object proposals.\n            gt_bboxes (torch.Tensor or :obj:`BaseBoxes`): target of the\n                transformation, e.g., ground truth boxes.\n\n        Returns:\n           encoded_bboxes(tuple[Tensor]): bucketing estimation\n            and fine regression targets and weights\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert bboxes.size(0) == gt_bboxes.size(0)\n        assert bboxes.size(-1) == gt_bboxes.size(-1) == 4\n        encoded_bboxes = bbox2bucket(bboxes, gt_bboxes, self.num_buckets,\n                                     self.scale_factor, self.offset_topk,\n                                     self.offset_upperbound,\n                                     self.cls_ignore_neighbor)\n        return encoded_bboxes\n\n    def decode(\n        self,\n        bboxes: Union[Tensor, BaseBoxes],\n        pred_bboxes: Tensor,\n        max_shape: Optional[Tuple[int]] = None\n    ) -> Tuple[Union[Tensor, BaseBoxes], Tensor]:\n        \"\"\"Apply transformation `pred_bboxes` to `boxes`.\n        Args:\n            boxes (torch.Tensor or :obj:`BaseBoxes`): Basic boxes.\n            pred_bboxes (torch.Tensor): Predictions for bucketing estimation\n                and fine regression\n            max_shape (tuple[int], optional): Maximum shape of boxes.\n                Defaults to None.\n\n        Returns:\n            Union[torch.Tensor, :obj:`BaseBoxes`]: Decoded boxes.\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        assert len(pred_bboxes) == 2\n        cls_preds, offset_preds = pred_bboxes\n        assert cls_preds.size(0) == bboxes.size(0) and offset_preds.size(\n            0) == bboxes.size(0)\n        bboxes, loc_confidence = bucket2bbox(bboxes, cls_preds, offset_preds,\n                                             self.num_buckets,\n                                             self.scale_factor, max_shape,\n                                             self.clip_border)\n        if self.use_box_type:\n            bboxes = HorizontalBoxes(bboxes, clone=False)\n        return bboxes, loc_confidence\n\n\ndef generat_buckets(proposals: Tensor,\n                    num_buckets: int,\n                    scale_factor: float = 1.0) -> Tuple[Tensor]:\n    \"\"\"Generate buckets w.r.t bucket number and scale factor of proposals.\n\n    Args:\n        proposals (Tensor): Shape (n, 4)\n        num_buckets (int): Number of buckets.\n        scale_factor (float): Scale factor to rescale proposals.\n\n    Returns:\n        tuple[Tensor]: (bucket_w, bucket_h, l_buckets, r_buckets,\n         t_buckets, d_buckets)\n\n            - bucket_w: Width of buckets on x-axis. Shape (n, ).\n            - bucket_h: Height of buckets on y-axis. Shape (n, ).\n            - l_buckets: Left buckets. Shape (n, ceil(side_num/2)).\n            - r_buckets: Right buckets. Shape (n, ceil(side_num/2)).\n            - t_buckets: Top buckets. Shape (n, ceil(side_num/2)).\n            - d_buckets: Down buckets. Shape (n, ceil(side_num/2)).\n    \"\"\"\n    proposals = bbox_rescale(proposals, scale_factor)\n\n    # number of buckets in each side\n    side_num = int(np.ceil(num_buckets / 2.0))\n    pw = proposals[..., 2] - proposals[..., 0]\n    ph = proposals[..., 3] - proposals[..., 1]\n    px1 = proposals[..., 0]\n    py1 = proposals[..., 1]\n    px2 = proposals[..., 2]\n    py2 = proposals[..., 3]\n\n    bucket_w = pw / num_buckets\n    bucket_h = ph / num_buckets\n\n    # left buckets\n    l_buckets = px1[:, None] + (0.5 + torch.arange(\n        0, side_num).to(proposals).float())[None, :] * bucket_w[:, None]\n    # right buckets\n    r_buckets = px2[:, None] - (0.5 + torch.arange(\n        0, side_num).to(proposals).float())[None, :] * bucket_w[:, None]\n    # top buckets\n    t_buckets = py1[:, None] + (0.5 + torch.arange(\n        0, side_num).to(proposals).float())[None, :] * bucket_h[:, None]\n    # down buckets\n    d_buckets = py2[:, None] - (0.5 + torch.arange(\n        0, side_num).to(proposals).float())[None, :] * bucket_h[:, None]\n    return bucket_w, bucket_h, l_buckets, r_buckets, t_buckets, d_buckets\n\n\ndef bbox2bucket(proposals: Tensor,\n                gt: Tensor,\n                num_buckets: int,\n                scale_factor: float,\n                offset_topk: int = 2,\n                offset_upperbound: float = 1.0,\n                cls_ignore_neighbor: bool = True) -> Tuple[Tensor]:\n    \"\"\"Generate buckets estimation and fine regression targets.\n\n    Args:\n        proposals (Tensor): Shape (n, 4)\n        gt (Tensor): Shape (n, 4)\n        num_buckets (int): Number of buckets.\n        scale_factor (float): Scale factor to rescale proposals.\n        offset_topk (int): Topk buckets are used to generate\n             bucket fine regression targets. Defaults to 2.\n        offset_upperbound (float): Offset allowance to generate\n             bucket fine regression targets.\n             To avoid too large offset displacements. Defaults to 1.0.\n        cls_ignore_neighbor (bool): Ignore second nearest bucket or Not.\n             Defaults to True.\n\n    Returns:\n        tuple[Tensor]: (offsets, offsets_weights, bucket_labels, cls_weights).\n\n            - offsets: Fine regression targets. \\\n                Shape (n, num_buckets*2).\n            - offsets_weights: Fine regression weights. \\\n                Shape (n, num_buckets*2).\n            - bucket_labels: Bucketing estimation labels. \\\n                Shape (n, num_buckets*2).\n            - cls_weights: Bucketing estimation weights. \\\n                Shape (n, num_buckets*2).\n    \"\"\"\n    assert proposals.size() == gt.size()\n\n    # generate buckets\n    proposals = proposals.float()\n    gt = gt.float()\n    (bucket_w, bucket_h, l_buckets, r_buckets, t_buckets,\n     d_buckets) = generat_buckets(proposals, num_buckets, scale_factor)\n\n    gx1 = gt[..., 0]\n    gy1 = gt[..., 1]\n    gx2 = gt[..., 2]\n    gy2 = gt[..., 3]\n\n    # generate offset targets and weights\n    # offsets from buckets to gts\n    l_offsets = (l_buckets - gx1[:, None]) / bucket_w[:, None]\n    r_offsets = (r_buckets - gx2[:, None]) / bucket_w[:, None]\n    t_offsets = (t_buckets - gy1[:, None]) / bucket_h[:, None]\n    d_offsets = (d_buckets - gy2[:, None]) / bucket_h[:, None]\n\n    # select top-k nearest buckets\n    l_topk, l_label = l_offsets.abs().topk(\n        offset_topk, dim=1, largest=False, sorted=True)\n    r_topk, r_label = r_offsets.abs().topk(\n        offset_topk, dim=1, largest=False, sorted=True)\n    t_topk, t_label = t_offsets.abs().topk(\n        offset_topk, dim=1, largest=False, sorted=True)\n    d_topk, d_label = d_offsets.abs().topk(\n        offset_topk, dim=1, largest=False, sorted=True)\n\n    offset_l_weights = l_offsets.new_zeros(l_offsets.size())\n    offset_r_weights = r_offsets.new_zeros(r_offsets.size())\n    offset_t_weights = t_offsets.new_zeros(t_offsets.size())\n    offset_d_weights = d_offsets.new_zeros(d_offsets.size())\n    inds = torch.arange(0, proposals.size(0)).to(proposals).long()\n\n    # generate offset weights of top-k nearest buckets\n    for k in range(offset_topk):\n        if k >= 1:\n            offset_l_weights[inds, l_label[:,\n                                           k]] = (l_topk[:, k] <\n                                                  offset_upperbound).float()\n            offset_r_weights[inds, r_label[:,\n                                           k]] = (r_topk[:, k] <\n                                                  offset_upperbound).float()\n            offset_t_weights[inds, t_label[:,\n                                           k]] = (t_topk[:, k] <\n                                                  offset_upperbound).float()\n            offset_d_weights[inds, d_label[:,\n                                           k]] = (d_topk[:, k] <\n                                                  offset_upperbound).float()\n        else:\n            offset_l_weights[inds, l_label[:, k]] = 1.0\n            offset_r_weights[inds, r_label[:, k]] = 1.0\n            offset_t_weights[inds, t_label[:, k]] = 1.0\n            offset_d_weights[inds, d_label[:, k]] = 1.0\n\n    offsets = torch.cat([l_offsets, r_offsets, t_offsets, d_offsets], dim=-1)\n    offsets_weights = torch.cat([\n        offset_l_weights, offset_r_weights, offset_t_weights, offset_d_weights\n    ],\n                                dim=-1)\n\n    # generate bucket labels and weight\n    side_num = int(np.ceil(num_buckets / 2.0))\n    labels = torch.stack(\n        [l_label[:, 0], r_label[:, 0], t_label[:, 0], d_label[:, 0]], dim=-1)\n\n    batch_size = labels.size(0)\n    bucket_labels = F.one_hot(labels.view(-1), side_num).view(batch_size,\n                                                              -1).float()\n    bucket_cls_l_weights = (l_offsets.abs() < 1).float()\n    bucket_cls_r_weights = (r_offsets.abs() < 1).float()\n    bucket_cls_t_weights = (t_offsets.abs() < 1).float()\n    bucket_cls_d_weights = (d_offsets.abs() < 1).float()\n    bucket_cls_weights = torch.cat([\n        bucket_cls_l_weights, bucket_cls_r_weights, bucket_cls_t_weights,\n        bucket_cls_d_weights\n    ],\n                                   dim=-1)\n    # ignore second nearest buckets for cls if necessary\n    if cls_ignore_neighbor:\n        bucket_cls_weights = (~((bucket_cls_weights == 1) &\n                                (bucket_labels == 0))).float()\n    else:\n        bucket_cls_weights[:] = 1.0\n    return offsets, offsets_weights, bucket_labels, bucket_cls_weights\n\n\ndef bucket2bbox(proposals: Tensor,\n                cls_preds: Tensor,\n                offset_preds: Tensor,\n                num_buckets: int,\n                scale_factor: float = 1.0,\n                max_shape: Optional[Union[Sequence[int], Tensor,\n                                          Sequence[Sequence[int]]]] = None,\n                clip_border: bool = True) -> Tuple[Tensor]:\n    \"\"\"Apply bucketing estimation (cls preds) and fine regression (offset\n    preds) to generate det bboxes.\n\n    Args:\n        proposals (Tensor): Boxes to be transformed. Shape (n, 4)\n        cls_preds (Tensor): bucketing estimation. Shape (n, num_buckets*2).\n        offset_preds (Tensor): fine regression. Shape (n, num_buckets*2).\n        num_buckets (int): Number of buckets.\n        scale_factor (float): Scale factor to rescale proposals.\n        max_shape (tuple[int, int]): Maximum bounds for boxes. specifies (H, W)\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Defaults to True.\n\n    Returns:\n        tuple[Tensor]: (bboxes, loc_confidence).\n\n            - bboxes: predicted bboxes. Shape (n, 4)\n            - loc_confidence: localization confidence of predicted bboxes.\n                Shape (n,).\n    \"\"\"\n\n    side_num = int(np.ceil(num_buckets / 2.0))\n    cls_preds = cls_preds.view(-1, side_num)\n    offset_preds = offset_preds.view(-1, side_num)\n\n    scores = F.softmax(cls_preds, dim=1)\n    score_topk, score_label = scores.topk(2, dim=1, largest=True, sorted=True)\n\n    rescaled_proposals = bbox_rescale(proposals, scale_factor)\n\n    pw = rescaled_proposals[..., 2] - rescaled_proposals[..., 0]\n    ph = rescaled_proposals[..., 3] - rescaled_proposals[..., 1]\n    px1 = rescaled_proposals[..., 0]\n    py1 = rescaled_proposals[..., 1]\n    px2 = rescaled_proposals[..., 2]\n    py2 = rescaled_proposals[..., 3]\n\n    bucket_w = pw / num_buckets\n    bucket_h = ph / num_buckets\n\n    score_inds_l = score_label[0::4, 0]\n    score_inds_r = score_label[1::4, 0]\n    score_inds_t = score_label[2::4, 0]\n    score_inds_d = score_label[3::4, 0]\n    l_buckets = px1 + (0.5 + score_inds_l.float()) * bucket_w\n    r_buckets = px2 - (0.5 + score_inds_r.float()) * bucket_w\n    t_buckets = py1 + (0.5 + score_inds_t.float()) * bucket_h\n    d_buckets = py2 - (0.5 + score_inds_d.float()) * bucket_h\n\n    offsets = offset_preds.view(-1, 4, side_num)\n    inds = torch.arange(proposals.size(0)).to(proposals).long()\n    l_offsets = offsets[:, 0, :][inds, score_inds_l]\n    r_offsets = offsets[:, 1, :][inds, score_inds_r]\n    t_offsets = offsets[:, 2, :][inds, score_inds_t]\n    d_offsets = offsets[:, 3, :][inds, score_inds_d]\n\n    x1 = l_buckets - l_offsets * bucket_w\n    x2 = r_buckets - r_offsets * bucket_w\n    y1 = t_buckets - t_offsets * bucket_h\n    y2 = d_buckets - d_offsets * bucket_h\n\n    if clip_border and max_shape is not None:\n        x1 = x1.clamp(min=0, max=max_shape[1] - 1)\n        y1 = y1.clamp(min=0, max=max_shape[0] - 1)\n        x2 = x2.clamp(min=0, max=max_shape[1] - 1)\n        y2 = y2.clamp(min=0, max=max_shape[0] - 1)\n    bboxes = torch.cat([x1[:, None], y1[:, None], x2[:, None], y2[:, None]],\n                       dim=-1)\n\n    # bucketing guided rescoring\n    loc_confidence = score_topk[:, 0]\n    top2_neighbor_inds = (score_label[:, 0] - score_label[:, 1]).abs() == 1\n    loc_confidence += score_topk[:, 1] * top2_neighbor_inds.float()\n    loc_confidence = loc_confidence.view(-1, 4).mean(dim=1)\n\n    return bboxes, loc_confidence\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/delta_xywh_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import BaseBoxes, HorizontalBoxes, get_box_tensor\nfrom .base_bbox_coder import BaseBBoxCoder\n\n\n@TASK_UTILS.register_module()\nclass DeltaXYWHBBoxCoder(BaseBBoxCoder):\n    \"\"\"Delta XYWH BBox coder.\n\n    Following the practice in `R-CNN <https://arxiv.org/abs/1311.2524>`_,\n    this coder encodes bbox (x1, y1, x2, y2) into delta (dx, dy, dw, dh) and\n    decodes delta (dx, dy, dw, dh) back to original bbox (x1, y1, x2, y2).\n\n    Args:\n        target_means (Sequence[float]): Denormalizing means of target for\n            delta coordinates\n        target_stds (Sequence[float]): Denormalizing standard deviation of\n            target for delta coordinates\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Defaults to True.\n        add_ctr_clamp (bool): Whether to add center clamp, when added, the\n            predicted box is clamped is its center is too far away from\n            the original anchor's center. Only used by YOLOF. Default False.\n        ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF.\n            Default 32.\n    \"\"\"\n\n    def __init__(self,\n                 target_means: Sequence[float] = (0., 0., 0., 0.),\n                 target_stds: Sequence[float] = (1., 1., 1., 1.),\n                 clip_border: bool = True,\n                 add_ctr_clamp: bool = False,\n                 ctr_clamp: int = 32,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.means = target_means\n        self.stds = target_stds\n        self.clip_border = clip_border\n        self.add_ctr_clamp = add_ctr_clamp\n        self.ctr_clamp = ctr_clamp\n\n    def encode(self, bboxes: Union[Tensor, BaseBoxes],\n               gt_bboxes: Union[Tensor, BaseBoxes]) -> Tensor:\n        \"\"\"Get box regression transformation deltas that can be used to\n        transform the ``bboxes`` into the ``gt_bboxes``.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Source boxes,\n                e.g., object proposals.\n            gt_bboxes (torch.Tensor or :obj:`BaseBoxes`): Target of the\n                transformation, e.g., ground-truth boxes.\n\n        Returns:\n            torch.Tensor: Box transformation deltas\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert bboxes.size(0) == gt_bboxes.size(0)\n        assert bboxes.size(-1) == gt_bboxes.size(-1) == 4\n        encoded_bboxes = bbox2delta(bboxes, gt_bboxes, self.means, self.stds)\n        return encoded_bboxes\n\n    def decode(\n        self,\n        bboxes: Union[Tensor, BaseBoxes],\n        pred_bboxes: Tensor,\n        max_shape: Optional[Union[Sequence[int], Tensor,\n                                  Sequence[Sequence[int]]]] = None,\n        wh_ratio_clip: Optional[float] = 16 / 1000\n    ) -> Union[Tensor, BaseBoxes]:\n        \"\"\"Apply transformation `pred_bboxes` to `boxes`.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Basic boxes. Shape\n                (B, N, 4) or (N, 4)\n            pred_bboxes (Tensor): Encoded offsets with respect to each roi.\n               Has shape (B, N, num_classes * 4) or (B, N, 4) or\n               (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H\n               when rois is a grid of anchors.Offset encoding follows [1]_.\n            max_shape (Sequence[int] or torch.Tensor or Sequence[\n               Sequence[int]],optional): Maximum bounds for boxes, specifies\n               (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then\n               the max_shape should be a Sequence[Sequence[int]]\n               and the length of max_shape should also be B.\n            wh_ratio_clip (float, optional): The allowed ratio between\n                width and height.\n\n        Returns:\n            Union[torch.Tensor, :obj:`BaseBoxes`]: Decoded boxes.\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        assert pred_bboxes.size(0) == bboxes.size(0)\n        if pred_bboxes.ndim == 3:\n            assert pred_bboxes.size(1) == bboxes.size(1)\n\n        if pred_bboxes.ndim == 2 and not torch.onnx.is_in_onnx_export():\n            # single image decode\n            decoded_bboxes = delta2bbox(bboxes, pred_bboxes, self.means,\n                                        self.stds, max_shape, wh_ratio_clip,\n                                        self.clip_border, self.add_ctr_clamp,\n                                        self.ctr_clamp)\n        else:\n            if pred_bboxes.ndim == 3 and not torch.onnx.is_in_onnx_export():\n                warnings.warn(\n                    'DeprecationWarning: onnx_delta2bbox is deprecated '\n                    'in the case of batch decoding and non-ONNX, '\n                    'please use “delta2bbox” instead. In order to improve '\n                    'the decoding speed, the batch function will no '\n                    'longer be supported. ')\n            decoded_bboxes = onnx_delta2bbox(bboxes, pred_bboxes, self.means,\n                                             self.stds, max_shape,\n                                             wh_ratio_clip, self.clip_border,\n                                             self.add_ctr_clamp,\n                                             self.ctr_clamp)\n\n        if self.use_box_type:\n            assert decoded_bboxes.size(-1) == 4, \\\n                ('Cannot warp decoded boxes with box type when decoded boxes'\n                 'have shape of (N, num_classes * 4)')\n            decoded_bboxes = HorizontalBoxes(decoded_bboxes)\n        return decoded_bboxes\n\n\n@TASK_UTILS.register_module()\nclass DeltaXYWHBBoxCoderForGLIP(DeltaXYWHBBoxCoder):\n    \"\"\"This is designed specifically for the GLIP algorithm.\n\n    In order to completely match the official performance, we need to perform\n    special calculations in the encoding and decoding processes, such as\n    additional +1 and -1 calculations. However, this is not a user-friendly\n    design.\n    \"\"\"\n\n    def encode(self, bboxes: Union[Tensor, BaseBoxes],\n               gt_bboxes: Union[Tensor, BaseBoxes]) -> Tensor:\n        \"\"\"Get box regression transformation deltas that can be used to\n        transform the ``bboxes`` into the ``gt_bboxes``.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Source boxes,\n                e.g., object proposals.\n            gt_bboxes (torch.Tensor or :obj:`BaseBoxes`): Target of the\n                transformation, e.g., ground-truth boxes.\n\n        Returns:\n            torch.Tensor: Box transformation deltas\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert bboxes.size(0) == gt_bboxes.size(0)\n        assert bboxes.size(-1) == gt_bboxes.size(-1) == 4\n        encoded_bboxes = bbox2delta(bboxes, gt_bboxes, self.means, self.stds)\n        return encoded_bboxes\n\n    def decode(\n        self,\n        bboxes: Union[Tensor, BaseBoxes],\n        pred_bboxes: Tensor,\n        max_shape: Optional[Union[Sequence[int], Tensor,\n                                  Sequence[Sequence[int]]]] = None,\n        wh_ratio_clip: Optional[float] = 16 / 1000\n    ) -> Union[Tensor, BaseBoxes]:\n        \"\"\"Apply transformation `pred_bboxes` to `boxes`.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Basic boxes. Shape\n                (B, N, 4) or (N, 4)\n            pred_bboxes (Tensor): Encoded offsets with respect to each roi.\n               Has shape (B, N, num_classes * 4) or (B, N, 4) or\n               (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H\n               when rois is a grid of anchors.Offset encoding follows [1]_.\n            max_shape (Sequence[int] or torch.Tensor or Sequence[\n               Sequence[int]],optional): Maximum bounds for boxes, specifies\n               (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then\n               the max_shape should be a Sequence[Sequence[int]]\n               and the length of max_shape should also be B.\n            wh_ratio_clip (float, optional): The allowed ratio between\n                width and height.\n\n        Returns:\n            Union[torch.Tensor, :obj:`BaseBoxes`]: Decoded boxes.\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        assert pred_bboxes.size(0) == bboxes.size(0)\n        if pred_bboxes.ndim == 3:\n            assert pred_bboxes.size(1) == bboxes.size(1)\n\n        if pred_bboxes.ndim == 2 and not torch.onnx.is_in_onnx_export():\n            # single image decode\n            decoded_bboxes = delta2bbox_glip(bboxes, pred_bboxes, self.means,\n                                             self.stds, max_shape,\n                                             wh_ratio_clip, self.clip_border,\n                                             self.add_ctr_clamp,\n                                             self.ctr_clamp)\n        else:\n            raise NotImplementedError()\n\n        if self.use_box_type:\n            assert decoded_bboxes.size(-1) == 4, \\\n                ('Cannot warp decoded boxes with box type when decoded boxes'\n                 'have shape of (N, num_classes * 4)')\n            decoded_bboxes = HorizontalBoxes(decoded_bboxes)\n        return decoded_bboxes\n\n\ndef bbox2delta(\n    proposals: Tensor,\n    gt: Tensor,\n    means: Sequence[float] = (0., 0., 0., 0.),\n    stds: Sequence[float] = (1., 1., 1., 1.)\n) -> Tensor:\n    \"\"\"Compute deltas of proposals w.r.t. gt.\n\n    We usually compute the deltas of x, y, w, h of proposals w.r.t ground\n    truth bboxes to get regression target.\n    This is the inverse function of :func:`delta2bbox`.\n\n    Args:\n        proposals (Tensor): Boxes to be transformed, shape (N, ..., 4)\n        gt (Tensor): Gt bboxes to be used as base, shape (N, ..., 4)\n        means (Sequence[float]): Denormalizing means for delta coordinates\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates\n\n    Returns:\n        Tensor: deltas with shape (N, 4), where columns represent dx, dy,\n            dw, dh.\n    \"\"\"\n    assert proposals.size() == gt.size()\n\n    proposals = proposals.float()\n    gt = gt.float()\n    px = (proposals[..., 0] + proposals[..., 2]) * 0.5\n    py = (proposals[..., 1] + proposals[..., 3]) * 0.5\n    pw = proposals[..., 2] - proposals[..., 0]\n    ph = proposals[..., 3] - proposals[..., 1]\n\n    gx = (gt[..., 0] + gt[..., 2]) * 0.5\n    gy = (gt[..., 1] + gt[..., 3]) * 0.5\n    gw = gt[..., 2] - gt[..., 0]\n    gh = gt[..., 3] - gt[..., 1]\n\n    dx = (gx - px) / pw\n    dy = (gy - py) / ph\n    dw = torch.log(gw / pw)\n    dh = torch.log(gh / ph)\n    deltas = torch.stack([dx, dy, dw, dh], dim=-1)\n\n    means = deltas.new_tensor(means).unsqueeze(0)\n    stds = deltas.new_tensor(stds).unsqueeze(0)\n    deltas = deltas.sub_(means).div_(stds)\n\n    return deltas\n\n\ndef delta2bbox(rois: Tensor,\n               deltas: Tensor,\n               means: Sequence[float] = (0., 0., 0., 0.),\n               stds: Sequence[float] = (1., 1., 1., 1.),\n               max_shape: Optional[Union[Sequence[int], Tensor,\n                                         Sequence[Sequence[int]]]] = None,\n               wh_ratio_clip: float = 16 / 1000,\n               clip_border: bool = True,\n               add_ctr_clamp: bool = False,\n               ctr_clamp: int = 32) -> Tensor:\n    \"\"\"Apply deltas to shift/scale base boxes.\n\n    Typically the rois are anchor or proposed bounding boxes and the deltas are\n    network outputs used to shift/scale those boxes.\n    This is the inverse function of :func:`bbox2delta`.\n\n    Args:\n        rois (Tensor): Boxes to be transformed. Has shape (N, 4).\n        deltas (Tensor): Encoded offsets relative to each roi.\n            Has shape (N, num_classes * 4) or (N, 4). Note\n            N = num_base_anchors * W * H, when rois is a grid of\n            anchors. Offset encoding follows [1]_.\n        means (Sequence[float]): Denormalizing means for delta coordinates.\n            Default (0., 0., 0., 0.).\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates. Default (1., 1., 1., 1.).\n        max_shape (tuple[int, int]): Maximum bounds for boxes, specifies\n           (H, W). Default None.\n        wh_ratio_clip (float): Maximum aspect ratio for boxes. Default\n            16 / 1000.\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Default True.\n        add_ctr_clamp (bool): Whether to add center clamp. When set to True,\n            the center of the prediction bounding box will be clamped to\n            avoid being too far away from the center of the anchor.\n            Only used by YOLOF. Default False.\n        ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF.\n            Default 32.\n\n    Returns:\n        Tensor: Boxes with shape (N, num_classes * 4) or (N, 4), where 4\n           represent tl_x, tl_y, br_x, br_y.\n\n    References:\n        .. [1] https://arxiv.org/abs/1311.2524\n\n    Example:\n        >>> rois = torch.Tensor([[ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 5.,  5.,  5.,  5.]])\n        >>> deltas = torch.Tensor([[  0.,   0.,   0.,   0.],\n        >>>                        [  1.,   1.,   1.,   1.],\n        >>>                        [  0.,   0.,   2.,  -1.],\n        >>>                        [ 0.7, -1.9, -0.5,  0.3]])\n        >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3))\n        tensor([[0.0000, 0.0000, 1.0000, 1.0000],\n                [0.1409, 0.1409, 2.8591, 2.8591],\n                [0.0000, 0.3161, 4.1945, 0.6839],\n                [5.0000, 5.0000, 5.0000, 5.0000]])\n    \"\"\"\n    num_bboxes, num_classes = deltas.size(0), deltas.size(1) // 4\n    if num_bboxes == 0:\n        return deltas\n\n    deltas = deltas.reshape(-1, 4)\n\n    means = deltas.new_tensor(means).view(1, -1)\n    stds = deltas.new_tensor(stds).view(1, -1)\n    denorm_deltas = deltas * stds + means\n\n    dxy = denorm_deltas[:, :2]\n    dwh = denorm_deltas[:, 2:]\n\n    # Compute width/height of each roi\n    rois_ = rois.repeat(1, num_classes).reshape(-1, 4)\n    pxy = ((rois_[:, :2] + rois_[:, 2:]) * 0.5)\n    pwh = (rois_[:, 2:] - rois_[:, :2])\n\n    dxy_wh = pwh * dxy\n\n    max_ratio = np.abs(np.log(wh_ratio_clip))\n    if add_ctr_clamp:\n        dxy_wh = torch.clamp(dxy_wh, max=ctr_clamp, min=-ctr_clamp)\n        dwh = torch.clamp(dwh, max=max_ratio)\n    else:\n        dwh = dwh.clamp(min=-max_ratio, max=max_ratio)\n\n    gxy = pxy + dxy_wh\n    gwh = pwh * dwh.exp()\n    x1y1 = gxy - (gwh * 0.5)\n    x2y2 = gxy + (gwh * 0.5)\n    bboxes = torch.cat([x1y1, x2y2], dim=-1)\n    if clip_border and max_shape is not None:\n        bboxes[..., 0::2].clamp_(min=0, max=max_shape[1])\n        bboxes[..., 1::2].clamp_(min=0, max=max_shape[0])\n    bboxes = bboxes.reshape(num_bboxes, -1)\n    return bboxes\n\n\ndef onnx_delta2bbox(rois: Tensor,\n                    deltas: Tensor,\n                    means: Sequence[float] = (0., 0., 0., 0.),\n                    stds: Sequence[float] = (1., 1., 1., 1.),\n                    max_shape: Optional[Union[Sequence[int], Tensor,\n                                              Sequence[Sequence[int]]]] = None,\n                    wh_ratio_clip: float = 16 / 1000,\n                    clip_border: Optional[bool] = True,\n                    add_ctr_clamp: bool = False,\n                    ctr_clamp: int = 32) -> Tensor:\n    \"\"\"Apply deltas to shift/scale base boxes.\n\n    Typically the rois are anchor or proposed bounding boxes and the deltas are\n    network outputs used to shift/scale those boxes.\n    This is the inverse function of :func:`bbox2delta`.\n\n    Args:\n        rois (Tensor): Boxes to be transformed. Has shape (N, 4) or (B, N, 4)\n        deltas (Tensor): Encoded offsets with respect to each roi.\n            Has shape (B, N, num_classes * 4) or (B, N, 4) or\n            (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H\n            when rois is a grid of anchors.Offset encoding follows [1]_.\n        means (Sequence[float]): Denormalizing means for delta coordinates.\n            Default (0., 0., 0., 0.).\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates. Default (1., 1., 1., 1.).\n        max_shape (Sequence[int] or torch.Tensor or Sequence[\n            Sequence[int]],optional): Maximum bounds for boxes, specifies\n            (H, W, C) or (H, W). If rois shape is (B, N, 4), then\n            the max_shape should be a Sequence[Sequence[int]]\n            and the length of max_shape should also be B. Default None.\n        wh_ratio_clip (float): Maximum aspect ratio for boxes.\n            Default 16 / 1000.\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Default True.\n        add_ctr_clamp (bool): Whether to add center clamp, when added, the\n            predicted box is clamped is its center is too far away from\n            the original anchor's center. Only used by YOLOF. Default False.\n        ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF.\n            Default 32.\n\n    Returns:\n        Tensor: Boxes with shape (B, N, num_classes * 4) or (B, N, 4) or\n           (N, num_classes * 4) or (N, 4), where 4 represent\n           tl_x, tl_y, br_x, br_y.\n\n    References:\n        .. [1] https://arxiv.org/abs/1311.2524\n\n    Example:\n        >>> rois = torch.Tensor([[ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 5.,  5.,  5.,  5.]])\n        >>> deltas = torch.Tensor([[  0.,   0.,   0.,   0.],\n        >>>                        [  1.,   1.,   1.,   1.],\n        >>>                        [  0.,   0.,   2.,  -1.],\n        >>>                        [ 0.7, -1.9, -0.5,  0.3]])\n        >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3))\n        tensor([[0.0000, 0.0000, 1.0000, 1.0000],\n                [0.1409, 0.1409, 2.8591, 2.8591],\n                [0.0000, 0.3161, 4.1945, 0.6839],\n                [5.0000, 5.0000, 5.0000, 5.0000]])\n    \"\"\"\n    means = deltas.new_tensor(means).view(1,\n                                          -1).repeat(1,\n                                                     deltas.size(-1) // 4)\n    stds = deltas.new_tensor(stds).view(1, -1).repeat(1, deltas.size(-1) // 4)\n    denorm_deltas = deltas * stds + means\n    dx = denorm_deltas[..., 0::4]\n    dy = denorm_deltas[..., 1::4]\n    dw = denorm_deltas[..., 2::4]\n    dh = denorm_deltas[..., 3::4]\n\n    x1, y1 = rois[..., 0], rois[..., 1]\n    x2, y2 = rois[..., 2], rois[..., 3]\n    # Compute center of each roi\n    px = ((x1 + x2) * 0.5).unsqueeze(-1).expand_as(dx)\n    py = ((y1 + y2) * 0.5).unsqueeze(-1).expand_as(dy)\n    # Compute width/height of each roi\n    pw = (x2 - x1).unsqueeze(-1).expand_as(dw)\n    ph = (y2 - y1).unsqueeze(-1).expand_as(dh)\n\n    dx_width = pw * dx\n    dy_height = ph * dy\n\n    max_ratio = np.abs(np.log(wh_ratio_clip))\n    if add_ctr_clamp:\n        dx_width = torch.clamp(dx_width, max=ctr_clamp, min=-ctr_clamp)\n        dy_height = torch.clamp(dy_height, max=ctr_clamp, min=-ctr_clamp)\n        dw = torch.clamp(dw, max=max_ratio)\n        dh = torch.clamp(dh, max=max_ratio)\n    else:\n        dw = dw.clamp(min=-max_ratio, max=max_ratio)\n        dh = dh.clamp(min=-max_ratio, max=max_ratio)\n    # Use exp(network energy) to enlarge/shrink each roi\n    gw = pw * dw.exp()\n    gh = ph * dh.exp()\n    # Use network energy to shift the center of each roi\n    gx = px + dx_width\n    gy = py + dy_height\n    # Convert center-xy/width/height to top-left, bottom-right\n    x1 = gx - gw * 0.5\n    y1 = gy - gh * 0.5\n    x2 = gx + gw * 0.5\n    y2 = gy + gh * 0.5\n\n    bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size())\n\n    if clip_border and max_shape is not None:\n        # clip bboxes with dynamic `min` and `max` for onnx\n        if torch.onnx.is_in_onnx_export():\n            from mmdet.core.export import dynamic_clip_for_onnx\n            x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape)\n            bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size())\n            return bboxes\n        if not isinstance(max_shape, torch.Tensor):\n            max_shape = x1.new_tensor(max_shape)\n        max_shape = max_shape[..., :2].type_as(x1)\n        if max_shape.ndim == 2:\n            assert bboxes.ndim == 3\n            assert max_shape.size(0) == bboxes.size(0)\n\n        min_xy = x1.new_tensor(0)\n        max_xy = torch.cat(\n            [max_shape] * (deltas.size(-1) // 2),\n            dim=-1).flip(-1).unsqueeze(-2)\n        bboxes = torch.where(bboxes < min_xy, min_xy, bboxes)\n        bboxes = torch.where(bboxes > max_xy, max_xy, bboxes)\n\n    return bboxes\n\n\ndef delta2bbox_glip(rois: Tensor,\n                    deltas: Tensor,\n                    means: Sequence[float] = (0., 0., 0., 0.),\n                    stds: Sequence[float] = (1., 1., 1., 1.),\n                    max_shape: Optional[Union[Sequence[int], Tensor,\n                                              Sequence[Sequence[int]]]] = None,\n                    wh_ratio_clip: float = 16 / 1000,\n                    clip_border: bool = True,\n                    add_ctr_clamp: bool = False,\n                    ctr_clamp: int = 32) -> Tensor:\n    \"\"\"Apply deltas to shift/scale base boxes.\n\n    Typically the rois are anchor or proposed bounding boxes and the deltas are\n    network outputs used to shift/scale those boxes.\n    This is the inverse function of :func:`bbox2delta`.\n\n    Args:\n        rois (Tensor): Boxes to be transformed. Has shape (N, 4).\n        deltas (Tensor): Encoded offsets relative to each roi.\n            Has shape (N, num_classes * 4) or (N, 4). Note\n            N = num_base_anchors * W * H, when rois is a grid of\n            anchors. Offset encoding follows [1]_.\n        means (Sequence[float]): Denormalizing means for delta coordinates.\n            Default (0., 0., 0., 0.).\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates. Default (1., 1., 1., 1.).\n        max_shape (tuple[int, int]): Maximum bounds for boxes, specifies\n           (H, W). Default None.\n        wh_ratio_clip (float): Maximum aspect ratio for boxes. Default\n            16 / 1000.\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Default True.\n        add_ctr_clamp (bool): Whether to add center clamp. When set to True,\n            the center of the prediction bounding box will be clamped to\n            avoid being too far away from the center of the anchor.\n            Only used by YOLOF. Default False.\n        ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF.\n            Default 32.\n\n    Returns:\n        Tensor: Boxes with shape (N, num_classes * 4) or (N, 4), where 4\n           represent tl_x, tl_y, br_x, br_y.\n    \"\"\"\n    num_bboxes, num_classes = deltas.size(0), deltas.size(1) // 4\n    if num_bboxes == 0:\n        return deltas\n\n    deltas = deltas.reshape(-1, 4)\n\n    means = deltas.new_tensor(means).view(1, -1)\n    stds = deltas.new_tensor(stds).view(1, -1)\n    denorm_deltas = deltas * stds + means\n\n    dxy = denorm_deltas[:, :2]\n    dwh = denorm_deltas[:, 2:]\n\n    # Compute width/height of each roi\n    rois_ = rois.repeat(1, num_classes).reshape(-1, 4)\n    pxy = ((rois_[:, :2] + rois_[:, 2:] - 1) * 0.5)  # note\n    pwh = (rois_[:, 2:] - rois_[:, :2])\n\n    dxy_wh = pwh * dxy\n\n    max_ratio = np.abs(np.log(wh_ratio_clip))\n    if add_ctr_clamp:\n        dxy_wh = torch.clamp(dxy_wh, max=ctr_clamp, min=-ctr_clamp)\n        dwh = torch.clamp(dwh, max=max_ratio)\n    else:\n        dwh = dwh.clamp(min=-max_ratio, max=max_ratio)\n\n    gxy = pxy + dxy_wh\n    gwh = pwh * dwh.exp()\n\n    x1y1 = gxy - (gwh - 1) * 0.5  # Note\n    x2y2 = gxy + (gwh - 1) * 0.5  # Note\n\n    bboxes = torch.cat([x1y1, x2y2], dim=-1)\n\n    if clip_border and max_shape is not None:\n        bboxes[..., 0::2].clamp_(min=0, max=max_shape[1] - 1)  # Note\n        bboxes[..., 1::2].clamp_(min=0, max=max_shape[0] - 1)  # Note\n    bboxes = bboxes.reshape(num_bboxes, -1)\n    return bboxes\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/distance_point_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Sequence, Union\n\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import (BaseBoxes, HorizontalBoxes, bbox2distance,\n                                   distance2bbox, get_box_tensor)\nfrom .base_bbox_coder import BaseBBoxCoder\n\n\n@TASK_UTILS.register_module()\nclass DistancePointBBoxCoder(BaseBBoxCoder):\n    \"\"\"Distance Point BBox coder.\n\n    This coder encodes gt bboxes (x1, y1, x2, y2) into (top, bottom, left,\n    right) and decode it back to the original.\n\n    Args:\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Defaults to True.\n    \"\"\"\n\n    def __init__(self, clip_border: Optional[bool] = True, **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.clip_border = clip_border\n\n    def encode(self,\n               points: Tensor,\n               gt_bboxes: Union[Tensor, BaseBoxes],\n               max_dis: Optional[float] = None,\n               eps: float = 0.1) -> Tensor:\n        \"\"\"Encode bounding box to distances.\n\n        Args:\n            points (Tensor): Shape (N, 2), The format is [x, y].\n            gt_bboxes (Tensor or :obj:`BaseBoxes`): Shape (N, 4), The format\n                is \"xyxy\"\n            max_dis (float): Upper bound of the distance. Default None.\n            eps (float): a small value to ensure target < max_dis, instead <=.\n                Default 0.1.\n\n        Returns:\n            Tensor: Box transformation deltas. The shape is (N, 4).\n        \"\"\"\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert points.size(0) == gt_bboxes.size(0)\n        assert points.size(-1) == 2\n        assert gt_bboxes.size(-1) == 4\n        return bbox2distance(points, gt_bboxes, max_dis, eps)\n\n    def decode(\n        self,\n        points: Tensor,\n        pred_bboxes: Tensor,\n        max_shape: Optional[Union[Sequence[int], Tensor,\n                                  Sequence[Sequence[int]]]] = None\n    ) -> Union[Tensor, BaseBoxes]:\n        \"\"\"Decode distance prediction to bounding box.\n\n        Args:\n            points (Tensor): Shape (B, N, 2) or (N, 2).\n            pred_bboxes (Tensor): Distance from the given point to 4\n                boundaries (left, top, right, bottom). Shape (B, N, 4)\n                or (N, 4)\n            max_shape (Sequence[int] or torch.Tensor or Sequence[\n                Sequence[int]],optional): Maximum bounds for boxes, specifies\n                (H, W, C) or (H, W). If priors shape is (B, N, 4), then\n                the max_shape should be a Sequence[Sequence[int]],\n                and the length of max_shape should also be B.\n                Default None.\n        Returns:\n            Union[Tensor, :obj:`BaseBoxes`]: Boxes with shape (N, 4) or\n            (B, N, 4)\n        \"\"\"\n        assert points.size(0) == pred_bboxes.size(0)\n        assert points.size(-1) == 2\n        assert pred_bboxes.size(-1) == 4\n        if self.clip_border is False:\n            max_shape = None\n        bboxes = distance2bbox(points, pred_bboxes, max_shape)\n\n        if self.use_box_type:\n            bboxes = HorizontalBoxes(bboxes)\n        return bboxes\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/legacy_delta_xywh_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Sequence, Union\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import BaseBoxes, HorizontalBoxes, get_box_tensor\nfrom .base_bbox_coder import BaseBBoxCoder\n\n\n@TASK_UTILS.register_module()\nclass LegacyDeltaXYWHBBoxCoder(BaseBBoxCoder):\n    \"\"\"Legacy Delta XYWH BBox coder used in MMDet V1.x.\n\n    Following the practice in R-CNN [1]_, this coder encodes bbox (x1, y1, x2,\n    y2) into delta (dx, dy, dw, dh) and decodes delta (dx, dy, dw, dh)\n    back to original bbox (x1, y1, x2, y2).\n\n    Note:\n        The main difference between :class`LegacyDeltaXYWHBBoxCoder` and\n        :class:`DeltaXYWHBBoxCoder` is whether ``+ 1`` is used during width and\n        height calculation. We suggest to only use this coder when testing with\n        MMDet V1.x models.\n\n    References:\n        .. [1] https://arxiv.org/abs/1311.2524\n\n    Args:\n        target_means (Sequence[float]): denormalizing means of target for\n            delta coordinates\n        target_stds (Sequence[float]): denormalizing standard deviation of\n            target for delta coordinates\n    \"\"\"\n\n    def __init__(self,\n                 target_means: Sequence[float] = (0., 0., 0., 0.),\n                 target_stds: Sequence[float] = (1., 1., 1., 1.),\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.means = target_means\n        self.stds = target_stds\n\n    def encode(self, bboxes: Union[Tensor, BaseBoxes],\n               gt_bboxes: Union[Tensor, BaseBoxes]) -> Tensor:\n        \"\"\"Get box regression transformation deltas that can be used to\n        transform the ``bboxes`` into the ``gt_bboxes``.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): source boxes,\n                e.g., object proposals.\n            gt_bboxes (torch.Tensor or :obj:`BaseBoxes`): target of the\n                transformation, e.g., ground-truth boxes.\n\n        Returns:\n            torch.Tensor: Box transformation deltas\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert bboxes.size(0) == gt_bboxes.size(0)\n        assert bboxes.size(-1) == gt_bboxes.size(-1) == 4\n        encoded_bboxes = legacy_bbox2delta(bboxes, gt_bboxes, self.means,\n                                           self.stds)\n        return encoded_bboxes\n\n    def decode(\n        self,\n        bboxes: Union[Tensor, BaseBoxes],\n        pred_bboxes: Tensor,\n        max_shape: Optional[Union[Sequence[int], Tensor,\n                                  Sequence[Sequence[int]]]] = None,\n        wh_ratio_clip: Optional[float] = 16 / 1000\n    ) -> Union[Tensor, BaseBoxes]:\n        \"\"\"Apply transformation `pred_bboxes` to `boxes`.\n\n        Args:\n            boxes (torch.Tensor or :obj:`BaseBoxes`): Basic boxes.\n            pred_bboxes (torch.Tensor): Encoded boxes with shape\n            max_shape (tuple[int], optional): Maximum shape of boxes.\n                Defaults to None.\n            wh_ratio_clip (float, optional): The allowed ratio between\n                width and height.\n\n        Returns:\n            Union[torch.Tensor, :obj:`BaseBoxes`]: Decoded boxes.\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        assert pred_bboxes.size(0) == bboxes.size(0)\n        decoded_bboxes = legacy_delta2bbox(bboxes, pred_bboxes, self.means,\n                                           self.stds, max_shape, wh_ratio_clip)\n\n        if self.use_box_type:\n            assert decoded_bboxes.size(-1) == 4, \\\n                ('Cannot warp decoded boxes with box type when decoded boxes'\n                 'have shape of (N, num_classes * 4)')\n            decoded_bboxes = HorizontalBoxes(decoded_bboxes)\n        return decoded_bboxes\n\n\ndef legacy_bbox2delta(\n    proposals: Tensor,\n    gt: Tensor,\n    means: Sequence[float] = (0., 0., 0., 0.),\n    stds: Sequence[float] = (1., 1., 1., 1.)\n) -> Tensor:\n    \"\"\"Compute deltas of proposals w.r.t. gt in the MMDet V1.x manner.\n\n    We usually compute the deltas of x, y, w, h of proposals w.r.t ground\n    truth bboxes to get regression target.\n    This is the inverse function of `delta2bbox()`\n\n    Args:\n        proposals (Tensor): Boxes to be transformed, shape (N, ..., 4)\n        gt (Tensor): Gt bboxes to be used as base, shape (N, ..., 4)\n        means (Sequence[float]): Denormalizing means for delta coordinates\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates\n\n    Returns:\n        Tensor: deltas with shape (N, 4), where columns represent dx, dy,\n            dw, dh.\n    \"\"\"\n    assert proposals.size() == gt.size()\n\n    proposals = proposals.float()\n    gt = gt.float()\n    px = (proposals[..., 0] + proposals[..., 2]) * 0.5\n    py = (proposals[..., 1] + proposals[..., 3]) * 0.5\n    pw = proposals[..., 2] - proposals[..., 0] + 1.0\n    ph = proposals[..., 3] - proposals[..., 1] + 1.0\n\n    gx = (gt[..., 0] + gt[..., 2]) * 0.5\n    gy = (gt[..., 1] + gt[..., 3]) * 0.5\n    gw = gt[..., 2] - gt[..., 0] + 1.0\n    gh = gt[..., 3] - gt[..., 1] + 1.0\n\n    dx = (gx - px) / pw\n    dy = (gy - py) / ph\n    dw = torch.log(gw / pw)\n    dh = torch.log(gh / ph)\n    deltas = torch.stack([dx, dy, dw, dh], dim=-1)\n\n    means = deltas.new_tensor(means).unsqueeze(0)\n    stds = deltas.new_tensor(stds).unsqueeze(0)\n    deltas = deltas.sub_(means).div_(stds)\n\n    return deltas\n\n\ndef legacy_delta2bbox(rois: Tensor,\n                      deltas: Tensor,\n                      means: Sequence[float] = (0., 0., 0., 0.),\n                      stds: Sequence[float] = (1., 1., 1., 1.),\n                      max_shape: Optional[\n                          Union[Sequence[int], Tensor,\n                                Sequence[Sequence[int]]]] = None,\n                      wh_ratio_clip: float = 16 / 1000) -> Tensor:\n    \"\"\"Apply deltas to shift/scale base boxes in the MMDet V1.x manner.\n\n    Typically the rois are anchor or proposed bounding boxes and the deltas are\n    network outputs used to shift/scale those boxes.\n    This is the inverse function of `bbox2delta()`\n\n    Args:\n        rois (Tensor): Boxes to be transformed. Has shape (N, 4)\n        deltas (Tensor): Encoded offsets with respect to each roi.\n            Has shape (N, 4 * num_classes). Note N = num_anchors * W * H when\n            rois is a grid of anchors. Offset encoding follows [1]_.\n        means (Sequence[float]): Denormalizing means for delta coordinates\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates\n        max_shape (tuple[int, int]): Maximum bounds for boxes. specifies (H, W)\n        wh_ratio_clip (float): Maximum aspect ratio for boxes.\n\n    Returns:\n        Tensor: Boxes with shape (N, 4), where columns represent\n            tl_x, tl_y, br_x, br_y.\n\n    References:\n        .. [1] https://arxiv.org/abs/1311.2524\n\n    Example:\n        >>> rois = torch.Tensor([[ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 5.,  5.,  5.,  5.]])\n        >>> deltas = torch.Tensor([[  0.,   0.,   0.,   0.],\n        >>>                        [  1.,   1.,   1.,   1.],\n        >>>                        [  0.,   0.,   2.,  -1.],\n        >>>                        [ 0.7, -1.9, -0.5,  0.3]])\n        >>> legacy_delta2bbox(rois, deltas, max_shape=(32, 32))\n        tensor([[0.0000, 0.0000, 1.5000, 1.5000],\n                [0.0000, 0.0000, 5.2183, 5.2183],\n                [0.0000, 0.1321, 7.8891, 0.8679],\n                [5.3967, 2.4251, 6.0033, 3.7749]])\n    \"\"\"\n    means = deltas.new_tensor(means).repeat(1, deltas.size(1) // 4)\n    stds = deltas.new_tensor(stds).repeat(1, deltas.size(1) // 4)\n    denorm_deltas = deltas * stds + means\n    dx = denorm_deltas[:, 0::4]\n    dy = denorm_deltas[:, 1::4]\n    dw = denorm_deltas[:, 2::4]\n    dh = denorm_deltas[:, 3::4]\n    max_ratio = np.abs(np.log(wh_ratio_clip))\n    dw = dw.clamp(min=-max_ratio, max=max_ratio)\n    dh = dh.clamp(min=-max_ratio, max=max_ratio)\n    # Compute center of each roi\n    px = ((rois[:, 0] + rois[:, 2]) * 0.5).unsqueeze(1).expand_as(dx)\n    py = ((rois[:, 1] + rois[:, 3]) * 0.5).unsqueeze(1).expand_as(dy)\n    # Compute width/height of each roi\n    pw = (rois[:, 2] - rois[:, 0] + 1.0).unsqueeze(1).expand_as(dw)\n    ph = (rois[:, 3] - rois[:, 1] + 1.0).unsqueeze(1).expand_as(dh)\n    # Use exp(network energy) to enlarge/shrink each roi\n    gw = pw * dw.exp()\n    gh = ph * dh.exp()\n    # Use network energy to shift the center of each roi\n    gx = px + pw * dx\n    gy = py + ph * dy\n    # Convert center-xy/width/height to top-left, bottom-right\n\n    # The true legacy box coder should +- 0.5 here.\n    # However, current implementation improves the performance when testing\n    # the models trained in MMDetection 1.X (~0.5 bbox AP, 0.2 mask AP)\n    x1 = gx - gw * 0.5\n    y1 = gy - gh * 0.5\n    x2 = gx + gw * 0.5\n    y2 = gy + gh * 0.5\n    if max_shape is not None:\n        x1 = x1.clamp(min=0, max=max_shape[1] - 1)\n        y1 = y1.clamp(min=0, max=max_shape[0] - 1)\n        x2 = x2.clamp(min=0, max=max_shape[1] - 1)\n        y2 = y2.clamp(min=0, max=max_shape[0] - 1)\n    bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view_as(deltas)\n    return bboxes\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/pseudo_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import BaseBoxes, HorizontalBoxes, get_box_tensor\nfrom .base_bbox_coder import BaseBBoxCoder\n\n\n@TASK_UTILS.register_module()\nclass PseudoBBoxCoder(BaseBBoxCoder):\n    \"\"\"Pseudo bounding box coder.\"\"\"\n\n    def __init__(self, **kwargs):\n        super().__init__(**kwargs)\n\n    def encode(self, bboxes: Tensor, gt_bboxes: Union[Tensor,\n                                                      BaseBoxes]) -> Tensor:\n        \"\"\"torch.Tensor: return the given ``bboxes``\"\"\"\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        return gt_bboxes\n\n    def decode(self, bboxes: Tensor, pred_bboxes: Union[Tensor,\n                                                        BaseBoxes]) -> Tensor:\n        \"\"\"torch.Tensor: return the given ``pred_bboxes``\"\"\"\n        if self.use_box_type:\n            pred_bboxes = HorizontalBoxes(pred_bboxes)\n        return pred_bboxes\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/tblr_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Sequence, Union\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import BaseBoxes, HorizontalBoxes, get_box_tensor\nfrom .base_bbox_coder import BaseBBoxCoder\n\n\n@TASK_UTILS.register_module()\nclass TBLRBBoxCoder(BaseBBoxCoder):\n    \"\"\"TBLR BBox coder.\n\n    Following the practice in `FSAF <https://arxiv.org/abs/1903.00621>`_,\n    this coder encodes gt bboxes (x1, y1, x2, y2) into (top, bottom, left,\n    right) and decode it back to the original.\n\n    Args:\n        normalizer (list | float): Normalization factor to be\n          divided with when coding the coordinates. If it is a list, it should\n          have length of 4 indicating normalization factor in tblr dims.\n          Otherwise it is a unified float factor for all dims. Default: 4.0\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 normalizer: Union[Sequence[float], float] = 4.0,\n                 clip_border: bool = True,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n        self.normalizer = normalizer\n        self.clip_border = clip_border\n\n    def encode(self, bboxes: Union[Tensor, BaseBoxes],\n               gt_bboxes: Union[Tensor, BaseBoxes]) -> Tensor:\n        \"\"\"Get box regression transformation deltas that can be used to\n        transform the ``bboxes`` into the ``gt_bboxes`` in the (top, left,\n        bottom, right) order.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): source boxes,\n                e.g., object proposals.\n            gt_bboxes (torch.Tensor or :obj:`BaseBoxes`): target of the\n                transformation, e.g., ground truth boxes.\n\n        Returns:\n            torch.Tensor: Box transformation deltas\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert bboxes.size(0) == gt_bboxes.size(0)\n        assert bboxes.size(-1) == gt_bboxes.size(-1) == 4\n        encoded_bboxes = bboxes2tblr(\n            bboxes, gt_bboxes, normalizer=self.normalizer)\n        return encoded_bboxes\n\n    def decode(\n        self,\n        bboxes: Union[Tensor, BaseBoxes],\n        pred_bboxes: Tensor,\n        max_shape: Optional[Union[Sequence[int], Tensor,\n                                  Sequence[Sequence[int]]]] = None\n    ) -> Union[Tensor, BaseBoxes]:\n        \"\"\"Apply transformation `pred_bboxes` to `boxes`.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Basic boxes.Shape\n                (B, N, 4) or (N, 4)\n            pred_bboxes (torch.Tensor): Encoded boxes with shape\n               (B, N, 4) or (N, 4)\n            max_shape (Sequence[int] or torch.Tensor or Sequence[\n               Sequence[int]],optional): Maximum bounds for boxes, specifies\n               (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then\n               the max_shape should be a Sequence[Sequence[int]]\n               and the length of max_shape should also be B.\n\n        Returns:\n            Union[torch.Tensor, :obj:`BaseBoxes`]: Decoded boxes.\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        decoded_bboxes = tblr2bboxes(\n            bboxes,\n            pred_bboxes,\n            normalizer=self.normalizer,\n            max_shape=max_shape,\n            clip_border=self.clip_border)\n\n        if self.use_box_type:\n            decoded_bboxes = HorizontalBoxes(decoded_bboxes)\n        return decoded_bboxes\n\n\ndef bboxes2tblr(priors: Tensor,\n                gts: Tensor,\n                normalizer: Union[Sequence[float], float] = 4.0,\n                normalize_by_wh: bool = True) -> Tensor:\n    \"\"\"Encode ground truth boxes to tblr coordinate.\n\n    It first convert the gt coordinate to tblr format,\n     (top, bottom, left, right), relative to prior box centers.\n     The tblr coordinate may be normalized by the side length of prior bboxes\n     if `normalize_by_wh` is specified as True, and it is then normalized by\n     the `normalizer` factor.\n\n    Args:\n        priors (Tensor): Prior boxes in point form\n            Shape: (num_proposals,4).\n        gts (Tensor): Coords of ground truth for each prior in point-form\n            Shape: (num_proposals, 4).\n        normalizer (Sequence[float] | float): normalization parameter of\n            encoded boxes. If it is a list, it has to have length = 4.\n            Default: 4.0\n        normalize_by_wh (bool): Whether to normalize tblr coordinate by the\n            side length (wh) of prior bboxes.\n\n    Return:\n        encoded boxes (Tensor), Shape: (num_proposals, 4)\n    \"\"\"\n\n    # dist b/t match center and prior's center\n    if not isinstance(normalizer, float):\n        normalizer = torch.tensor(normalizer, device=priors.device)\n        assert len(normalizer) == 4, 'Normalizer must have length = 4'\n    assert priors.size(0) == gts.size(0)\n    prior_centers = (priors[:, 0:2] + priors[:, 2:4]) / 2\n    xmin, ymin, xmax, ymax = gts.split(1, dim=1)\n    top = prior_centers[:, 1].unsqueeze(1) - ymin\n    bottom = ymax - prior_centers[:, 1].unsqueeze(1)\n    left = prior_centers[:, 0].unsqueeze(1) - xmin\n    right = xmax - prior_centers[:, 0].unsqueeze(1)\n    loc = torch.cat((top, bottom, left, right), dim=1)\n    if normalize_by_wh:\n        # Normalize tblr by anchor width and height\n        wh = priors[:, 2:4] - priors[:, 0:2]\n        w, h = torch.split(wh, 1, dim=1)\n        loc[:, :2] /= h  # tb is normalized by h\n        loc[:, 2:] /= w  # lr is normalized by w\n    # Normalize tblr by the given normalization factor\n    return loc / normalizer\n\n\ndef tblr2bboxes(priors: Tensor,\n                tblr: Tensor,\n                normalizer: Union[Sequence[float], float] = 4.0,\n                normalize_by_wh: bool = True,\n                max_shape: Optional[Union[Sequence[int], Tensor,\n                                          Sequence[Sequence[int]]]] = None,\n                clip_border: bool = True) -> Tensor:\n    \"\"\"Decode tblr outputs to prediction boxes.\n\n    The process includes 3 steps: 1) De-normalize tblr coordinates by\n    multiplying it with `normalizer`; 2) De-normalize tblr coordinates by the\n    prior bbox width and height if `normalize_by_wh` is `True`; 3) Convert\n    tblr (top, bottom, left, right) pair relative to the center of priors back\n    to (xmin, ymin, xmax, ymax) coordinate.\n\n    Args:\n        priors (Tensor): Prior boxes in point form (x0, y0, x1, y1)\n          Shape: (N,4) or (B, N, 4).\n        tblr (Tensor): Coords of network output in tblr form\n          Shape: (N, 4) or (B, N, 4).\n        normalizer (Sequence[float] | float): Normalization parameter of\n          encoded boxes. By list, it represents the normalization factors at\n          tblr dims. By float, it is the unified normalization factor at all\n          dims. Default: 4.0\n        normalize_by_wh (bool): Whether the tblr coordinates have been\n          normalized by the side length (wh) of prior bboxes.\n        max_shape (Sequence[int] or torch.Tensor or Sequence[\n            Sequence[int]],optional): Maximum bounds for boxes, specifies\n            (H, W, C) or (H, W). If priors shape is (B, N, 4), then\n            the max_shape should be a Sequence[Sequence[int]]\n            and the length of max_shape should also be B.\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Defaults to True.\n\n    Return:\n        encoded boxes (Tensor): Boxes with shape (N, 4) or (B, N, 4)\n    \"\"\"\n    if not isinstance(normalizer, float):\n        normalizer = torch.tensor(normalizer, device=priors.device)\n        assert len(normalizer) == 4, 'Normalizer must have length = 4'\n    assert priors.size(0) == tblr.size(0)\n    if priors.ndim == 3:\n        assert priors.size(1) == tblr.size(1)\n\n    loc_decode = tblr * normalizer\n    prior_centers = (priors[..., 0:2] + priors[..., 2:4]) / 2\n    if normalize_by_wh:\n        wh = priors[..., 2:4] - priors[..., 0:2]\n        w, h = torch.split(wh, 1, dim=-1)\n        # Inplace operation with slice would failed for exporting to ONNX\n        th = h * loc_decode[..., :2]  # tb\n        tw = w * loc_decode[..., 2:]  # lr\n        loc_decode = torch.cat([th, tw], dim=-1)\n    # Cannot be exported using onnx when loc_decode.split(1, dim=-1)\n    top, bottom, left, right = loc_decode.split((1, 1, 1, 1), dim=-1)\n    xmin = prior_centers[..., 0].unsqueeze(-1) - left\n    xmax = prior_centers[..., 0].unsqueeze(-1) + right\n    ymin = prior_centers[..., 1].unsqueeze(-1) - top\n    ymax = prior_centers[..., 1].unsqueeze(-1) + bottom\n\n    bboxes = torch.cat((xmin, ymin, xmax, ymax), dim=-1)\n\n    if clip_border and max_shape is not None:\n        # clip bboxes with dynamic `min` and `max` for onnx\n        if torch.onnx.is_in_onnx_export():\n            from mmdet.core.export import dynamic_clip_for_onnx\n            xmin, ymin, xmax, ymax = dynamic_clip_for_onnx(\n                xmin, ymin, xmax, ymax, max_shape)\n            bboxes = torch.cat([xmin, ymin, xmax, ymax], dim=-1)\n            return bboxes\n        if not isinstance(max_shape, torch.Tensor):\n            max_shape = priors.new_tensor(max_shape)\n        max_shape = max_shape[..., :2].type_as(priors)\n        if max_shape.ndim == 2:\n            assert bboxes.ndim == 3\n            assert max_shape.size(0) == bboxes.size(0)\n\n        min_xy = priors.new_tensor(0)\n        max_xy = torch.cat([max_shape, max_shape],\n                           dim=-1).flip(-1).unsqueeze(-2)\n        bboxes = torch.where(bboxes < min_xy, min_xy, bboxes)\n        bboxes = torch.where(bboxes > max_xy, max_xy, bboxes)\n\n    return bboxes\n"
  },
  {
    "path": "mmdet/models/task_modules/coders/yolo_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import BaseBoxes, HorizontalBoxes, get_box_tensor\nfrom .base_bbox_coder import BaseBBoxCoder\n\n\n@TASK_UTILS.register_module()\nclass YOLOBBoxCoder(BaseBBoxCoder):\n    \"\"\"YOLO BBox coder.\n\n    Following `YOLO <https://arxiv.org/abs/1506.02640>`_, this coder divide\n    image into grids, and encode bbox (x1, y1, x2, y2) into (cx, cy, dw, dh).\n    cx, cy in [0., 1.], denotes relative center position w.r.t the center of\n    bboxes. dw, dh are the same as :obj:`DeltaXYWHBBoxCoder`.\n\n    Args:\n        eps (float): Min value of cx, cy when encoding.\n    \"\"\"\n\n    def __init__(self, eps: float = 1e-6, **kwargs):\n        super().__init__(**kwargs)\n        self.eps = eps\n\n    def encode(self, bboxes: Union[Tensor, BaseBoxes],\n               gt_bboxes: Union[Tensor, BaseBoxes],\n               stride: Union[Tensor, int]) -> Tensor:\n        \"\"\"Get box regression transformation deltas that can be used to\n        transform the ``bboxes`` into the ``gt_bboxes``.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Source boxes,\n                e.g., anchors.\n            gt_bboxes (torch.Tensor or :obj:`BaseBoxes`): Target of the\n                transformation, e.g., ground-truth boxes.\n            stride (torch.Tensor | int): Stride of bboxes.\n\n        Returns:\n            torch.Tensor: Box transformation deltas\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert bboxes.size(0) == gt_bboxes.size(0)\n        assert bboxes.size(-1) == gt_bboxes.size(-1) == 4\n        x_center_gt = (gt_bboxes[..., 0] + gt_bboxes[..., 2]) * 0.5\n        y_center_gt = (gt_bboxes[..., 1] + gt_bboxes[..., 3]) * 0.5\n        w_gt = gt_bboxes[..., 2] - gt_bboxes[..., 0]\n        h_gt = gt_bboxes[..., 3] - gt_bboxes[..., 1]\n        x_center = (bboxes[..., 0] + bboxes[..., 2]) * 0.5\n        y_center = (bboxes[..., 1] + bboxes[..., 3]) * 0.5\n        w = bboxes[..., 2] - bboxes[..., 0]\n        h = bboxes[..., 3] - bboxes[..., 1]\n        w_target = torch.log((w_gt / w).clamp(min=self.eps))\n        h_target = torch.log((h_gt / h).clamp(min=self.eps))\n        x_center_target = ((x_center_gt - x_center) / stride + 0.5).clamp(\n            self.eps, 1 - self.eps)\n        y_center_target = ((y_center_gt - y_center) / stride + 0.5).clamp(\n            self.eps, 1 - self.eps)\n        encoded_bboxes = torch.stack(\n            [x_center_target, y_center_target, w_target, h_target], dim=-1)\n        return encoded_bboxes\n\n    def decode(self, bboxes: Union[Tensor, BaseBoxes], pred_bboxes: Tensor,\n               stride: Union[Tensor, int]) -> Union[Tensor, BaseBoxes]:\n        \"\"\"Apply transformation `pred_bboxes` to `boxes`.\n\n        Args:\n            boxes (torch.Tensor or :obj:`BaseBoxes`): Basic boxes,\n                e.g. anchors.\n            pred_bboxes (torch.Tensor): Encoded boxes with shape\n            stride (torch.Tensor | int): Strides of bboxes.\n\n        Returns:\n            Union[torch.Tensor, :obj:`BaseBoxes`]: Decoded boxes.\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        assert pred_bboxes.size(-1) == bboxes.size(-1) == 4\n        xy_centers = (bboxes[..., :2] + bboxes[..., 2:]) * 0.5 + (\n            pred_bboxes[..., :2] - 0.5) * stride\n        whs = (bboxes[..., 2:] -\n               bboxes[..., :2]) * 0.5 * pred_bboxes[..., 2:].exp()\n        decoded_bboxes = torch.stack(\n            (xy_centers[..., 0] - whs[..., 0], xy_centers[..., 1] -\n             whs[..., 1], xy_centers[..., 0] + whs[..., 0],\n             xy_centers[..., 1] + whs[..., 1]),\n            dim=-1)\n\n        if self.use_box_type:\n            decoded_bboxes = HorizontalBoxes(decoded_bboxes)\n        return decoded_bboxes\n"
  },
  {
    "path": "mmdet/models/task_modules/prior_generators/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .anchor_generator import (AnchorGenerator, LegacyAnchorGenerator,\n                               SSDAnchorGenerator, YOLOAnchorGenerator)\nfrom .point_generator import MlvlPointGenerator, PointGenerator\nfrom .utils import anchor_inside_flags, calc_region\n\n__all__ = [\n    'AnchorGenerator', 'LegacyAnchorGenerator', 'anchor_inside_flags',\n    'PointGenerator', 'calc_region', 'YOLOAnchorGenerator',\n    'MlvlPointGenerator', 'SSDAnchorGenerator'\n]\n"
  },
  {
    "path": "mmdet/models/task_modules/prior_generators/anchor_generator.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom typing import List, Optional, Tuple, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.utils import is_tuple_of\nfrom torch import Tensor\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import HorizontalBoxes\n\nDeviceType = Union[str, torch.device]\n\n\n@TASK_UTILS.register_module()\nclass AnchorGenerator:\n    \"\"\"Standard anchor generator for 2D anchor-based detectors.\n\n    Args:\n        strides (list[int] | list[tuple[int, int]]): Strides of anchors\n            in multiple feature levels in order (w, h).\n        ratios (list[float]): The list of ratios between the height and width\n            of anchors in a single level.\n        scales (list[int], Optional): Anchor scales for anchors\n            in a single level. It cannot be set at the same time\n            if `octave_base_scale` and `scales_per_octave` are set.\n        base_sizes (list[int], Optional): The basic sizes\n            of anchors in multiple levels.\n            If None is given, strides will be used as base_sizes.\n            (If strides are non square, the shortest stride is taken.)\n        scale_major (bool): Whether to multiply scales first when generating\n            base anchors. If true, the anchors in the same row will have the\n            same scales. By default it is True in V2.0\n        octave_base_scale (int, Optional): The base scale of octave.\n        scales_per_octave (int, Optional): Number of scales for each octave.\n            `octave_base_scale` and `scales_per_octave` are usually used in\n            retinanet and the `scales` should be None when they are set.\n        centers (list[tuple[float]], Optional): The centers of the anchor\n            relative to the feature grid center in multiple feature levels.\n            By default it is set to be None and not used. If a list of tuple of\n            float is given, they will be used to shift the centers of anchors.\n        center_offset (float): The offset of center in proportion to anchors'\n            width and height. By default it is 0 in V2.0.\n        use_box_type (bool): Whether to warp anchors with the box type data\n            structure. Defaults to False.\n\n    Examples:\n        >>> from mmdet.models.task_modules.\n        ... prior_generators import AnchorGenerator\n        >>> self = AnchorGenerator([16], [1.], [1.], [9])\n        >>> all_anchors = self.grid_priors([(2, 2)], device='cpu')\n        >>> print(all_anchors)\n        [tensor([[-4.5000, -4.5000,  4.5000,  4.5000],\n                [11.5000, -4.5000, 20.5000,  4.5000],\n                [-4.5000, 11.5000,  4.5000, 20.5000],\n                [11.5000, 11.5000, 20.5000, 20.5000]])]\n        >>> self = AnchorGenerator([16, 32], [1.], [1.], [9, 18])\n        >>> all_anchors = self.grid_priors([(2, 2), (1, 1)], device='cpu')\n        >>> print(all_anchors)\n        [tensor([[-4.5000, -4.5000,  4.5000,  4.5000],\n                [11.5000, -4.5000, 20.5000,  4.5000],\n                [-4.5000, 11.5000,  4.5000, 20.5000],\n                [11.5000, 11.5000, 20.5000, 20.5000]]), \\\n        tensor([[-9., -9., 9., 9.]])]\n    \"\"\"\n\n    def __init__(self,\n                 strides: Union[List[int], List[Tuple[int, int]]],\n                 ratios: List[float],\n                 scales: Optional[List[int]] = None,\n                 base_sizes: Optional[List[int]] = None,\n                 scale_major: bool = True,\n                 octave_base_scale: Optional[int] = None,\n                 scales_per_octave: Optional[int] = None,\n                 centers: Optional[List[Tuple[float, float]]] = None,\n                 center_offset: float = 0.,\n                 use_box_type: bool = False) -> None:\n        # check center and center_offset\n        if center_offset != 0:\n            assert centers is None, 'center cannot be set when center_offset' \\\n                                    f'!=0, {centers} is given.'\n        if not (0 <= center_offset <= 1):\n            raise ValueError('center_offset should be in range [0, 1], '\n                             f'{center_offset} is given.')\n        if centers is not None:\n            assert len(centers) == len(strides), \\\n                'The number of strides should be the same as centers, got ' \\\n                f'{strides} and {centers}'\n\n        # calculate base sizes of anchors\n        self.strides = [_pair(stride) for stride in strides]\n        self.base_sizes = [min(stride) for stride in self.strides\n                           ] if base_sizes is None else base_sizes\n        assert len(self.base_sizes) == len(self.strides), \\\n            'The number of strides should be the same as base sizes, got ' \\\n            f'{self.strides} and {self.base_sizes}'\n\n        # calculate scales of anchors\n        assert ((octave_base_scale is not None\n                 and scales_per_octave is not None) ^ (scales is not None)), \\\n            'scales and octave_base_scale with scales_per_octave cannot' \\\n            ' be set at the same time'\n        if scales is not None:\n            self.scales = torch.Tensor(scales)\n        elif octave_base_scale is not None and scales_per_octave is not None:\n            octave_scales = np.array(\n                [2**(i / scales_per_octave) for i in range(scales_per_octave)])\n            scales = octave_scales * octave_base_scale\n            self.scales = torch.Tensor(scales)\n        else:\n            raise ValueError('Either scales or octave_base_scale with '\n                             'scales_per_octave should be set')\n\n        self.octave_base_scale = octave_base_scale\n        self.scales_per_octave = scales_per_octave\n        self.ratios = torch.Tensor(ratios)\n        self.scale_major = scale_major\n        self.centers = centers\n        self.center_offset = center_offset\n        self.base_anchors = self.gen_base_anchors()\n        self.use_box_type = use_box_type\n\n    @property\n    def num_base_anchors(self) -> List[int]:\n        \"\"\"list[int]: total number of base anchors in a feature grid\"\"\"\n        return self.num_base_priors\n\n    @property\n    def num_base_priors(self) -> List[int]:\n        \"\"\"list[int]: The number of priors (anchors) at a point\n        on the feature grid\"\"\"\n        return [base_anchors.size(0) for base_anchors in self.base_anchors]\n\n    @property\n    def num_levels(self) -> int:\n        \"\"\"int: number of feature levels that the generator will be applied\"\"\"\n        return len(self.strides)\n\n    def gen_base_anchors(self) -> List[Tensor]:\n        \"\"\"Generate base anchors.\n\n        Returns:\n            list(torch.Tensor): Base anchors of a feature grid in multiple \\\n                feature levels.\n        \"\"\"\n        multi_level_base_anchors = []\n        for i, base_size in enumerate(self.base_sizes):\n            center = None\n            if self.centers is not None:\n                center = self.centers[i]\n            multi_level_base_anchors.append(\n                self.gen_single_level_base_anchors(\n                    base_size,\n                    scales=self.scales,\n                    ratios=self.ratios,\n                    center=center))\n        return multi_level_base_anchors\n\n    def gen_single_level_base_anchors(self,\n                                      base_size: Union[int, float],\n                                      scales: Tensor,\n                                      ratios: Tensor,\n                                      center: Optional[Tuple[float]] = None) \\\n            -> Tensor:\n        \"\"\"Generate base anchors of a single level.\n\n        Args:\n            base_size (int | float): Basic size of an anchor.\n            scales (torch.Tensor): Scales of the anchor.\n            ratios (torch.Tensor): The ratio between the height\n                and width of anchors in a single level.\n            center (tuple[float], optional): The center of the base anchor\n                related to a single feature grid. Defaults to None.\n\n        Returns:\n            torch.Tensor: Anchors in a single-level feature maps.\n        \"\"\"\n        w = base_size\n        h = base_size\n        if center is None:\n            x_center = self.center_offset * w\n            y_center = self.center_offset * h\n        else:\n            x_center, y_center = center\n\n        h_ratios = torch.sqrt(ratios)\n        w_ratios = 1 / h_ratios\n        if self.scale_major:\n            ws = (w * w_ratios[:, None] * scales[None, :]).view(-1)\n            hs = (h * h_ratios[:, None] * scales[None, :]).view(-1)\n        else:\n            ws = (w * scales[:, None] * w_ratios[None, :]).view(-1)\n            hs = (h * scales[:, None] * h_ratios[None, :]).view(-1)\n\n        # use float anchor and the anchor's center is aligned with the\n        # pixel center\n        base_anchors = [\n            x_center - 0.5 * ws, y_center - 0.5 * hs, x_center + 0.5 * ws,\n            y_center + 0.5 * hs\n        ]\n        base_anchors = torch.stack(base_anchors, dim=-1)\n\n        return base_anchors\n\n    def _meshgrid(self,\n                  x: Tensor,\n                  y: Tensor,\n                  row_major: bool = True) -> Tuple[Tensor]:\n        \"\"\"Generate mesh grid of x and y.\n\n        Args:\n            x (torch.Tensor): Grids of x dimension.\n            y (torch.Tensor): Grids of y dimension.\n            row_major (bool): Whether to return y grids first.\n                Defaults to True.\n\n        Returns:\n            tuple[torch.Tensor]: The mesh grids of x and y.\n        \"\"\"\n        # use shape instead of len to keep tracing while exporting to onnx\n        xx = x.repeat(y.shape[0])\n        yy = y.view(-1, 1).repeat(1, x.shape[0]).view(-1)\n        if row_major:\n            return xx, yy\n        else:\n            return yy, xx\n\n    def grid_priors(self,\n                    featmap_sizes: List[Tuple],\n                    dtype: torch.dtype = torch.float32,\n                    device: DeviceType = 'cuda') -> List[Tensor]:\n        \"\"\"Generate grid anchors in multiple feature levels.\n\n        Args:\n            featmap_sizes (list[tuple]): List of feature map sizes in\n                multiple feature levels.\n            dtype (:obj:`torch.dtype`): Dtype of priors.\n                Defaults to torch.float32.\n            device (str | torch.device): The device where the anchors\n                will be put on.\n\n        Return:\n            list[torch.Tensor]: Anchors in multiple feature levels. \\\n                The sizes of each tensor should be [N, 4], where \\\n                N = width * height * num_base_anchors, width and height \\\n                are the sizes of the corresponding feature level, \\\n                num_base_anchors is the number of anchors for that level.\n        \"\"\"\n        assert self.num_levels == len(featmap_sizes)\n        multi_level_anchors = []\n        for i in range(self.num_levels):\n            anchors = self.single_level_grid_priors(\n                featmap_sizes[i], level_idx=i, dtype=dtype, device=device)\n            multi_level_anchors.append(anchors)\n        return multi_level_anchors\n\n    def single_level_grid_priors(self,\n                                 featmap_size: Tuple[int, int],\n                                 level_idx: int,\n                                 dtype: torch.dtype = torch.float32,\n                                 device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate grid anchors of a single level.\n\n        Note:\n            This function is usually called by method ``self.grid_priors``.\n\n        Args:\n            featmap_size (tuple[int, int]): Size of the feature maps.\n            level_idx (int): The index of corresponding feature map level.\n            dtype (obj:`torch.dtype`): Date type of points.Defaults to\n                ``torch.float32``.\n            device (str | torch.device): The device the tensor will be put on.\n                Defaults to 'cuda'.\n\n        Returns:\n            torch.Tensor: Anchors in the overall feature maps.\n        \"\"\"\n\n        base_anchors = self.base_anchors[level_idx].to(device).to(dtype)\n        feat_h, feat_w = featmap_size\n        stride_w, stride_h = self.strides[level_idx]\n        # First create Range with the default dtype, than convert to\n        # target `dtype` for onnx exporting.\n        shift_x = torch.arange(0, feat_w, device=device).to(dtype) * stride_w\n        shift_y = torch.arange(0, feat_h, device=device).to(dtype) * stride_h\n\n        shift_xx, shift_yy = self._meshgrid(shift_x, shift_y)\n        shifts = torch.stack([shift_xx, shift_yy, shift_xx, shift_yy], dim=-1)\n        # first feat_w elements correspond to the first row of shifts\n        # add A anchors (1, A, 4) to K shifts (K, 1, 4) to get\n        # shifted anchors (K, A, 4), reshape to (K*A, 4)\n\n        all_anchors = base_anchors[None, :, :] + shifts[:, None, :]\n        all_anchors = all_anchors.view(-1, 4)\n        # first A rows correspond to A anchors of (0, 0) in feature map,\n        # then (0, 1), (0, 2), ...\n        if self.use_box_type:\n            all_anchors = HorizontalBoxes(all_anchors)\n        return all_anchors\n\n    def sparse_priors(self,\n                      prior_idxs: Tensor,\n                      featmap_size: Tuple[int, int],\n                      level_idx: int,\n                      dtype: torch.dtype = torch.float32,\n                      device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate sparse anchors according to the ``prior_idxs``.\n\n        Args:\n            prior_idxs (Tensor): The index of corresponding anchors\n                in the feature map.\n            featmap_size (tuple[int, int]): feature map size arrange as (h, w).\n            level_idx (int): The level index of corresponding feature\n                map.\n            dtype (obj:`torch.dtype`): Date type of points.Defaults to\n                ``torch.float32``.\n            device (str | torch.device): The device where the points is\n                located.\n        Returns:\n            Tensor: Anchor with shape (N, 4), N should be equal to\n                the length of ``prior_idxs``.\n        \"\"\"\n\n        height, width = featmap_size\n        num_base_anchors = self.num_base_anchors[level_idx]\n        base_anchor_id = prior_idxs % num_base_anchors\n        x = (prior_idxs //\n             num_base_anchors) % width * self.strides[level_idx][0]\n        y = (prior_idxs // width //\n             num_base_anchors) % height * self.strides[level_idx][1]\n        priors = torch.stack([x, y, x, y], 1).to(dtype).to(device) + \\\n            self.base_anchors[level_idx][base_anchor_id, :].to(device)\n\n        return priors\n\n    def grid_anchors(self,\n                     featmap_sizes: List[Tuple],\n                     device: DeviceType = 'cuda') -> List[Tensor]:\n        \"\"\"Generate grid anchors in multiple feature levels.\n\n        Args:\n            featmap_sizes (list[tuple]): List of feature map sizes in\n                multiple feature levels.\n            device (str | torch.device): Device where the anchors will be\n                put on.\n\n        Return:\n            list[torch.Tensor]: Anchors in multiple feature levels. \\\n                The sizes of each tensor should be [N, 4], where \\\n                N = width * height * num_base_anchors, width and height \\\n                are the sizes of the corresponding feature level, \\\n                num_base_anchors is the number of anchors for that level.\n        \"\"\"\n        warnings.warn('``grid_anchors`` would be deprecated soon. '\n                      'Please use ``grid_priors`` ')\n\n        assert self.num_levels == len(featmap_sizes)\n        multi_level_anchors = []\n        for i in range(self.num_levels):\n            anchors = self.single_level_grid_anchors(\n                self.base_anchors[i].to(device),\n                featmap_sizes[i],\n                self.strides[i],\n                device=device)\n            multi_level_anchors.append(anchors)\n        return multi_level_anchors\n\n    def single_level_grid_anchors(self,\n                                  base_anchors: Tensor,\n                                  featmap_size: Tuple[int, int],\n                                  stride: Tuple[int, int] = (16, 16),\n                                  device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate grid anchors of a single level.\n\n        Note:\n            This function is usually called by method ``self.grid_anchors``.\n\n        Args:\n            base_anchors (torch.Tensor): The base anchors of a feature grid.\n            featmap_size (tuple[int]): Size of the feature maps.\n            stride (tuple[int, int]): Stride of the feature map in order\n                (w, h). Defaults to (16, 16).\n            device (str | torch.device): Device the tensor will be put on.\n                Defaults to 'cuda'.\n\n        Returns:\n            torch.Tensor: Anchors in the overall feature maps.\n        \"\"\"\n\n        warnings.warn(\n            '``single_level_grid_anchors`` would be deprecated soon. '\n            'Please use ``single_level_grid_priors`` ')\n\n        # keep featmap_size as Tensor instead of int, so that we\n        # can convert to ONNX correctly\n        feat_h, feat_w = featmap_size\n        shift_x = torch.arange(0, feat_w, device=device) * stride[0]\n        shift_y = torch.arange(0, feat_h, device=device) * stride[1]\n\n        shift_xx, shift_yy = self._meshgrid(shift_x, shift_y)\n        shifts = torch.stack([shift_xx, shift_yy, shift_xx, shift_yy], dim=-1)\n        shifts = shifts.type_as(base_anchors)\n        # first feat_w elements correspond to the first row of shifts\n        # add A anchors (1, A, 4) to K shifts (K, 1, 4) to get\n        # shifted anchors (K, A, 4), reshape to (K*A, 4)\n\n        all_anchors = base_anchors[None, :, :] + shifts[:, None, :]\n        all_anchors = all_anchors.view(-1, 4)\n        # first A rows correspond to A anchors of (0, 0) in feature map,\n        # then (0, 1), (0, 2), ...\n        return all_anchors\n\n    def valid_flags(self,\n                    featmap_sizes: List[Tuple[int, int]],\n                    pad_shape: Tuple,\n                    device: DeviceType = 'cuda') -> List[Tensor]:\n        \"\"\"Generate valid flags of anchors in multiple feature levels.\n\n        Args:\n            featmap_sizes (list(tuple[int, int])): List of feature map sizes in\n                multiple feature levels.\n            pad_shape (tuple): The padded shape of the image.\n            device (str | torch.device): Device where the anchors will be\n                put on.\n\n        Return:\n            list(torch.Tensor): Valid flags of anchors in multiple levels.\n        \"\"\"\n        assert self.num_levels == len(featmap_sizes)\n        multi_level_flags = []\n        for i in range(self.num_levels):\n            anchor_stride = self.strides[i]\n            feat_h, feat_w = featmap_sizes[i]\n            h, w = pad_shape[:2]\n            valid_feat_h = min(int(np.ceil(h / anchor_stride[1])), feat_h)\n            valid_feat_w = min(int(np.ceil(w / anchor_stride[0])), feat_w)\n            flags = self.single_level_valid_flags((feat_h, feat_w),\n                                                  (valid_feat_h, valid_feat_w),\n                                                  self.num_base_anchors[i],\n                                                  device=device)\n            multi_level_flags.append(flags)\n        return multi_level_flags\n\n    def single_level_valid_flags(self,\n                                 featmap_size: Tuple[int, int],\n                                 valid_size: Tuple[int, int],\n                                 num_base_anchors: int,\n                                 device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate the valid flags of anchor in a single feature map.\n\n        Args:\n            featmap_size (tuple[int]): The size of feature maps, arrange\n                as (h, w).\n            valid_size (tuple[int]): The valid size of the feature maps.\n            num_base_anchors (int): The number of base anchors.\n            device (str | torch.device): Device where the flags will be put on.\n                Defaults to 'cuda'.\n\n        Returns:\n            torch.Tensor: The valid flags of each anchor in a single level \\\n                feature map.\n        \"\"\"\n        feat_h, feat_w = featmap_size\n        valid_h, valid_w = valid_size\n        assert valid_h <= feat_h and valid_w <= feat_w\n        valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device)\n        valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device)\n        valid_x[:valid_w] = 1\n        valid_y[:valid_h] = 1\n        valid_xx, valid_yy = self._meshgrid(valid_x, valid_y)\n        valid = valid_xx & valid_yy\n        valid = valid[:, None].expand(valid.size(0),\n                                      num_base_anchors).contiguous().view(-1)\n        return valid\n\n    def __repr__(self) -> str:\n        \"\"\"str: a string that describes the module\"\"\"\n        indent_str = '    '\n        repr_str = self.__class__.__name__ + '(\\n'\n        repr_str += f'{indent_str}strides={self.strides},\\n'\n        repr_str += f'{indent_str}ratios={self.ratios},\\n'\n        repr_str += f'{indent_str}scales={self.scales},\\n'\n        repr_str += f'{indent_str}base_sizes={self.base_sizes},\\n'\n        repr_str += f'{indent_str}scale_major={self.scale_major},\\n'\n        repr_str += f'{indent_str}octave_base_scale='\n        repr_str += f'{self.octave_base_scale},\\n'\n        repr_str += f'{indent_str}scales_per_octave='\n        repr_str += f'{self.scales_per_octave},\\n'\n        repr_str += f'{indent_str}num_levels={self.num_levels}\\n'\n        repr_str += f'{indent_str}centers={self.centers},\\n'\n        repr_str += f'{indent_str}center_offset={self.center_offset})'\n        return repr_str\n\n\n@TASK_UTILS.register_module()\nclass SSDAnchorGenerator(AnchorGenerator):\n    \"\"\"Anchor generator for SSD.\n\n    Args:\n        strides (list[int]  | list[tuple[int, int]]): Strides of anchors\n            in multiple feature levels.\n        ratios (list[float]): The list of ratios between the height and width\n            of anchors in a single level.\n        min_sizes (list[float]): The list of minimum anchor sizes on each\n            level.\n        max_sizes (list[float]): The list of maximum anchor sizes on each\n            level.\n        basesize_ratio_range (tuple(float)): Ratio range of anchors. Being\n            used when not setting min_sizes and max_sizes.\n        input_size (int): Size of feature map, 300 for SSD300, 512 for\n            SSD512. Being used when not setting min_sizes and max_sizes.\n        scale_major (bool): Whether to multiply scales first when generating\n            base anchors. If true, the anchors in the same row will have the\n            same scales. It is always set to be False in SSD.\n        use_box_type (bool): Whether to warp anchors with the box type data\n            structure. Defaults to False.\n    \"\"\"\n\n    def __init__(self,\n                 strides: Union[List[int], List[Tuple[int, int]]],\n                 ratios: List[float],\n                 min_sizes: Optional[List[float]] = None,\n                 max_sizes: Optional[List[float]] = None,\n                 basesize_ratio_range: Tuple[float] = (0.15, 0.9),\n                 input_size: int = 300,\n                 scale_major: bool = True,\n                 use_box_type: bool = False) -> None:\n        assert len(strides) == len(ratios)\n        assert not (min_sizes is None) ^ (max_sizes is None)\n        self.strides = [_pair(stride) for stride in strides]\n        self.centers = [(stride[0] / 2., stride[1] / 2.)\n                        for stride in self.strides]\n\n        if min_sizes is None and max_sizes is None:\n            # use hard code to generate SSD anchors\n            self.input_size = input_size\n            assert is_tuple_of(basesize_ratio_range, float)\n            self.basesize_ratio_range = basesize_ratio_range\n            # calculate anchor ratios and sizes\n            min_ratio, max_ratio = basesize_ratio_range\n            min_ratio = int(min_ratio * 100)\n            max_ratio = int(max_ratio * 100)\n            step = int(np.floor(max_ratio - min_ratio) / (self.num_levels - 2))\n            min_sizes = []\n            max_sizes = []\n            for ratio in range(int(min_ratio), int(max_ratio) + 1, step):\n                min_sizes.append(int(self.input_size * ratio / 100))\n                max_sizes.append(int(self.input_size * (ratio + step) / 100))\n            if self.input_size == 300:\n                if basesize_ratio_range[0] == 0.15:  # SSD300 COCO\n                    min_sizes.insert(0, int(self.input_size * 7 / 100))\n                    max_sizes.insert(0, int(self.input_size * 15 / 100))\n                elif basesize_ratio_range[0] == 0.2:  # SSD300 VOC\n                    min_sizes.insert(0, int(self.input_size * 10 / 100))\n                    max_sizes.insert(0, int(self.input_size * 20 / 100))\n                else:\n                    raise ValueError(\n                        'basesize_ratio_range[0] should be either 0.15'\n                        'or 0.2 when input_size is 300, got '\n                        f'{basesize_ratio_range[0]}.')\n            elif self.input_size == 512:\n                if basesize_ratio_range[0] == 0.1:  # SSD512 COCO\n                    min_sizes.insert(0, int(self.input_size * 4 / 100))\n                    max_sizes.insert(0, int(self.input_size * 10 / 100))\n                elif basesize_ratio_range[0] == 0.15:  # SSD512 VOC\n                    min_sizes.insert(0, int(self.input_size * 7 / 100))\n                    max_sizes.insert(0, int(self.input_size * 15 / 100))\n                else:\n                    raise ValueError(\n                        'When not setting min_sizes and max_sizes,'\n                        'basesize_ratio_range[0] should be either 0.1'\n                        'or 0.15 when input_size is 512, got'\n                        f' {basesize_ratio_range[0]}.')\n            else:\n                raise ValueError(\n                    'Only support 300 or 512 in SSDAnchorGenerator when '\n                    'not setting min_sizes and max_sizes, '\n                    f'got {self.input_size}.')\n\n        assert len(min_sizes) == len(max_sizes) == len(strides)\n\n        anchor_ratios = []\n        anchor_scales = []\n        for k in range(len(self.strides)):\n            scales = [1., np.sqrt(max_sizes[k] / min_sizes[k])]\n            anchor_ratio = [1.]\n            for r in ratios[k]:\n                anchor_ratio += [1 / r, r]  # 4 or 6 ratio\n            anchor_ratios.append(torch.Tensor(anchor_ratio))\n            anchor_scales.append(torch.Tensor(scales))\n\n        self.base_sizes = min_sizes\n        self.scales = anchor_scales\n        self.ratios = anchor_ratios\n        self.scale_major = scale_major\n        self.center_offset = 0\n        self.base_anchors = self.gen_base_anchors()\n        self.use_box_type = use_box_type\n\n    def gen_base_anchors(self) -> List[Tensor]:\n        \"\"\"Generate base anchors.\n\n        Returns:\n            list(torch.Tensor): Base anchors of a feature grid in multiple \\\n                feature levels.\n        \"\"\"\n        multi_level_base_anchors = []\n        for i, base_size in enumerate(self.base_sizes):\n            base_anchors = self.gen_single_level_base_anchors(\n                base_size,\n                scales=self.scales[i],\n                ratios=self.ratios[i],\n                center=self.centers[i])\n            indices = list(range(len(self.ratios[i])))\n            indices.insert(1, len(indices))\n            base_anchors = torch.index_select(base_anchors, 0,\n                                              torch.LongTensor(indices))\n            multi_level_base_anchors.append(base_anchors)\n        return multi_level_base_anchors\n\n    def __repr__(self) -> str:\n        \"\"\"str: a string that describes the module\"\"\"\n        indent_str = '    '\n        repr_str = self.__class__.__name__ + '(\\n'\n        repr_str += f'{indent_str}strides={self.strides},\\n'\n        repr_str += f'{indent_str}scales={self.scales},\\n'\n        repr_str += f'{indent_str}scale_major={self.scale_major},\\n'\n        repr_str += f'{indent_str}input_size={self.input_size},\\n'\n        repr_str += f'{indent_str}scales={self.scales},\\n'\n        repr_str += f'{indent_str}ratios={self.ratios},\\n'\n        repr_str += f'{indent_str}num_levels={self.num_levels},\\n'\n        repr_str += f'{indent_str}base_sizes={self.base_sizes},\\n'\n        repr_str += f'{indent_str}basesize_ratio_range='\n        repr_str += f'{self.basesize_ratio_range})'\n        return repr_str\n\n\n@TASK_UTILS.register_module()\nclass LegacyAnchorGenerator(AnchorGenerator):\n    \"\"\"Legacy anchor generator used in MMDetection V1.x.\n\n    Note:\n        Difference to the V2.0 anchor generator:\n\n        1. The center offset of V1.x anchors are set to be 0.5 rather than 0.\n        2. The width/height are minused by 1 when calculating the anchors' \\\n            centers and corners to meet the V1.x coordinate system.\n        3. The anchors' corners are quantized.\n\n    Args:\n        strides (list[int] | list[tuple[int]]): Strides of anchors\n            in multiple feature levels.\n        ratios (list[float]): The list of ratios between the height and width\n            of anchors in a single level.\n        scales (list[int] | None): Anchor scales for anchors in a single level.\n            It cannot be set at the same time if `octave_base_scale` and\n            `scales_per_octave` are set.\n        base_sizes (list[int]): The basic sizes of anchors in multiple levels.\n            If None is given, strides will be used to generate base_sizes.\n        scale_major (bool): Whether to multiply scales first when generating\n            base anchors. If true, the anchors in the same row will have the\n            same scales. By default it is True in V2.0\n        octave_base_scale (int): The base scale of octave.\n        scales_per_octave (int): Number of scales for each octave.\n            `octave_base_scale` and `scales_per_octave` are usually used in\n            retinanet and the `scales` should be None when they are set.\n        centers (list[tuple[float, float]] | None): The centers of the anchor\n            relative to the feature grid center in multiple feature levels.\n            By default it is set to be None and not used. It a list of float\n            is given, this list will be used to shift the centers of anchors.\n        center_offset (float): The offset of center in proportion to anchors'\n            width and height. By default it is 0.5 in V2.0 but it should be 0.5\n            in v1.x models.\n        use_box_type (bool): Whether to warp anchors with the box type data\n            structure. Defaults to False.\n\n    Examples:\n        >>> from mmdet.models.task_modules.\n        ... prior_generators import LegacyAnchorGenerator\n        >>> self = LegacyAnchorGenerator(\n        >>>     [16], [1.], [1.], [9], center_offset=0.5)\n        >>> all_anchors = self.grid_anchors(((2, 2),), device='cpu')\n        >>> print(all_anchors)\n        [tensor([[ 0.,  0.,  8.,  8.],\n                [16.,  0., 24.,  8.],\n                [ 0., 16.,  8., 24.],\n                [16., 16., 24., 24.]])]\n    \"\"\"\n\n    def gen_single_level_base_anchors(self,\n                                      base_size: Union[int, float],\n                                      scales: Tensor,\n                                      ratios: Tensor,\n                                      center: Optional[Tuple[float]] = None) \\\n            -> Tensor:\n        \"\"\"Generate base anchors of a single level.\n\n        Note:\n            The width/height of anchors are minused by 1 when calculating \\\n                the centers and corners to meet the V1.x coordinate system.\n\n        Args:\n            base_size (int | float): Basic size of an anchor.\n            scales (torch.Tensor): Scales of the anchor.\n            ratios (torch.Tensor): The ratio between the height.\n                and width of anchors in a single level.\n            center (tuple[float], optional): The center of the base anchor\n                related to a single feature grid. Defaults to None.\n\n        Returns:\n            torch.Tensor: Anchors in a single-level feature map.\n        \"\"\"\n        w = base_size\n        h = base_size\n        if center is None:\n            x_center = self.center_offset * (w - 1)\n            y_center = self.center_offset * (h - 1)\n        else:\n            x_center, y_center = center\n\n        h_ratios = torch.sqrt(ratios)\n        w_ratios = 1 / h_ratios\n        if self.scale_major:\n            ws = (w * w_ratios[:, None] * scales[None, :]).view(-1)\n            hs = (h * h_ratios[:, None] * scales[None, :]).view(-1)\n        else:\n            ws = (w * scales[:, None] * w_ratios[None, :]).view(-1)\n            hs = (h * scales[:, None] * h_ratios[None, :]).view(-1)\n\n        # use float anchor and the anchor's center is aligned with the\n        # pixel center\n        base_anchors = [\n            x_center - 0.5 * (ws - 1), y_center - 0.5 * (hs - 1),\n            x_center + 0.5 * (ws - 1), y_center + 0.5 * (hs - 1)\n        ]\n        base_anchors = torch.stack(base_anchors, dim=-1).round()\n\n        return base_anchors\n\n\n@TASK_UTILS.register_module()\nclass LegacySSDAnchorGenerator(SSDAnchorGenerator, LegacyAnchorGenerator):\n    \"\"\"Legacy anchor generator used in MMDetection V1.x.\n\n    The difference between `LegacySSDAnchorGenerator` and `SSDAnchorGenerator`\n    can be found in `LegacyAnchorGenerator`.\n    \"\"\"\n\n    def __init__(self,\n                 strides: Union[List[int], List[Tuple[int, int]]],\n                 ratios: List[float],\n                 basesize_ratio_range: Tuple[float],\n                 input_size: int = 300,\n                 scale_major: bool = True,\n                 use_box_type: bool = False) -> None:\n        super(LegacySSDAnchorGenerator, self).__init__(\n            strides=strides,\n            ratios=ratios,\n            basesize_ratio_range=basesize_ratio_range,\n            input_size=input_size,\n            scale_major=scale_major,\n            use_box_type=use_box_type)\n        self.centers = [((stride - 1) / 2., (stride - 1) / 2.)\n                        for stride in strides]\n        self.base_anchors = self.gen_base_anchors()\n\n\n@TASK_UTILS.register_module()\nclass YOLOAnchorGenerator(AnchorGenerator):\n    \"\"\"Anchor generator for YOLO.\n\n    Args:\n        strides (list[int] | list[tuple[int, int]]): Strides of anchors\n            in multiple feature levels.\n        base_sizes (list[list[tuple[int, int]]]): The basic sizes\n            of anchors in multiple levels.\n    \"\"\"\n\n    def __init__(self,\n                 strides: Union[List[int], List[Tuple[int, int]]],\n                 base_sizes: List[List[Tuple[int, int]]],\n                 use_box_type: bool = False) -> None:\n        self.strides = [_pair(stride) for stride in strides]\n        self.centers = [(stride[0] / 2., stride[1] / 2.)\n                        for stride in self.strides]\n        self.base_sizes = []\n        num_anchor_per_level = len(base_sizes[0])\n        for base_sizes_per_level in base_sizes:\n            assert num_anchor_per_level == len(base_sizes_per_level)\n            self.base_sizes.append(\n                [_pair(base_size) for base_size in base_sizes_per_level])\n        self.base_anchors = self.gen_base_anchors()\n        self.use_box_type = use_box_type\n\n    @property\n    def num_levels(self) -> int:\n        \"\"\"int: number of feature levels that the generator will be applied\"\"\"\n        return len(self.base_sizes)\n\n    def gen_base_anchors(self) -> List[Tensor]:\n        \"\"\"Generate base anchors.\n\n        Returns:\n            list(torch.Tensor): Base anchors of a feature grid in multiple \\\n                feature levels.\n        \"\"\"\n        multi_level_base_anchors = []\n        for i, base_sizes_per_level in enumerate(self.base_sizes):\n            center = None\n            if self.centers is not None:\n                center = self.centers[i]\n            multi_level_base_anchors.append(\n                self.gen_single_level_base_anchors(base_sizes_per_level,\n                                                   center))\n        return multi_level_base_anchors\n\n    def gen_single_level_base_anchors(self,\n                                      base_sizes_per_level: List[Tuple[int]],\n                                      center: Optional[Tuple[float]] = None) \\\n            -> Tensor:\n        \"\"\"Generate base anchors of a single level.\n\n        Args:\n            base_sizes_per_level (list[tuple[int]]): Basic sizes of\n                anchors.\n            center (tuple[float], optional): The center of the base anchor\n                related to a single feature grid. Defaults to None.\n\n        Returns:\n            torch.Tensor: Anchors in a single-level feature maps.\n        \"\"\"\n        x_center, y_center = center\n        base_anchors = []\n        for base_size in base_sizes_per_level:\n            w, h = base_size\n\n            # use float anchor and the anchor's center is aligned with the\n            # pixel center\n            base_anchor = torch.Tensor([\n                x_center - 0.5 * w, y_center - 0.5 * h, x_center + 0.5 * w,\n                y_center + 0.5 * h\n            ])\n            base_anchors.append(base_anchor)\n        base_anchors = torch.stack(base_anchors, dim=0)\n\n        return base_anchors\n"
  },
  {
    "path": "mmdet/models/task_modules/prior_generators/point_generator.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.registry import TASK_UTILS\n\nDeviceType = Union[str, torch.device]\n\n\n@TASK_UTILS.register_module()\nclass PointGenerator:\n\n    def _meshgrid(self,\n                  x: Tensor,\n                  y: Tensor,\n                  row_major: bool = True) -> Tuple[Tensor, Tensor]:\n        \"\"\"Generate mesh grid of x and y.\n\n        Args:\n            x (torch.Tensor): Grids of x dimension.\n            y (torch.Tensor): Grids of y dimension.\n            row_major (bool): Whether to return y grids first.\n                Defaults to True.\n\n        Returns:\n            tuple[torch.Tensor]: The mesh grids of x and y.\n        \"\"\"\n        xx = x.repeat(len(y))\n        yy = y.view(-1, 1).repeat(1, len(x)).view(-1)\n        if row_major:\n            return xx, yy\n        else:\n            return yy, xx\n\n    def grid_points(self,\n                    featmap_size: Tuple[int, int],\n                    stride=16,\n                    device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate grid points of a single level.\n\n        Args:\n            featmap_size (tuple[int, int]): Size of the feature maps.\n            stride (int): The stride of corresponding feature map.\n            device (str | torch.device): The device the tensor will be put on.\n                Defaults to 'cuda'.\n\n        Returns:\n            torch.Tensor: grid point in a feature map.\n        \"\"\"\n        feat_h, feat_w = featmap_size\n        shift_x = torch.arange(0., feat_w, device=device) * stride\n        shift_y = torch.arange(0., feat_h, device=device) * stride\n        shift_xx, shift_yy = self._meshgrid(shift_x, shift_y)\n        stride = shift_x.new_full((shift_xx.shape[0], ), stride)\n        shifts = torch.stack([shift_xx, shift_yy, stride], dim=-1)\n        all_points = shifts.to(device)\n        return all_points\n\n    def valid_flags(self,\n                    featmap_size: Tuple[int, int],\n                    valid_size: Tuple[int, int],\n                    device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate valid flags of anchors in a feature map.\n\n        Args:\n            featmap_sizes (list(tuple[int, int])): List of feature map sizes in\n                multiple feature levels.\n            valid_shape (tuple[int, int]): The valid shape of the image.\n            device (str | torch.device): Device where the anchors will be\n                put on.\n\n        Return:\n            torch.Tensor: Valid flags of anchors in a level.\n        \"\"\"\n        feat_h, feat_w = featmap_size\n        valid_h, valid_w = valid_size\n        assert valid_h <= feat_h and valid_w <= feat_w\n        valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device)\n        valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device)\n        valid_x[:valid_w] = 1\n        valid_y[:valid_h] = 1\n        valid_xx, valid_yy = self._meshgrid(valid_x, valid_y)\n        valid = valid_xx & valid_yy\n        return valid\n\n\n@TASK_UTILS.register_module()\nclass MlvlPointGenerator:\n    \"\"\"Standard points generator for multi-level (Mlvl) feature maps in 2D\n    points-based detectors.\n\n    Args:\n        strides (list[int] | list[tuple[int, int]]): Strides of anchors\n            in multiple feature levels in order (w, h).\n        offset (float): The offset of points, the value is normalized with\n            corresponding stride. Defaults to 0.5.\n    \"\"\"\n\n    def __init__(self,\n                 strides: Union[List[int], List[Tuple[int, int]]],\n                 offset: float = 0.5) -> None:\n        self.strides = [_pair(stride) for stride in strides]\n        self.offset = offset\n\n    @property\n    def num_levels(self) -> int:\n        \"\"\"int: number of feature levels that the generator will be applied\"\"\"\n        return len(self.strides)\n\n    @property\n    def num_base_priors(self) -> List[int]:\n        \"\"\"list[int]: The number of priors (points) at a point\n        on the feature grid\"\"\"\n        return [1 for _ in range(len(self.strides))]\n\n    def _meshgrid(self,\n                  x: Tensor,\n                  y: Tensor,\n                  row_major: bool = True) -> Tuple[Tensor, Tensor]:\n        yy, xx = torch.meshgrid(y, x)\n        if row_major:\n            # warning .flatten() would cause error in ONNX exporting\n            # have to use reshape here\n            return xx.reshape(-1), yy.reshape(-1)\n\n        else:\n            return yy.reshape(-1), xx.reshape(-1)\n\n    def grid_priors(self,\n                    featmap_sizes: List[Tuple],\n                    dtype: torch.dtype = torch.float32,\n                    device: DeviceType = 'cuda',\n                    with_stride: bool = False) -> List[Tensor]:\n        \"\"\"Generate grid points of multiple feature levels.\n\n        Args:\n            featmap_sizes (list[tuple]): List of feature map sizes in\n                multiple feature levels, each size arrange as\n                as (h, w).\n            dtype (:obj:`dtype`): Dtype of priors. Defaults to torch.float32.\n            device (str | torch.device): The device where the anchors will be\n                put on.\n            with_stride (bool): Whether to concatenate the stride to\n                the last dimension of points.\n\n        Return:\n            list[torch.Tensor]: Points of  multiple feature levels.\n            The sizes of each tensor should be (N, 2) when with stride is\n            ``False``, where N = width * height, width and height\n            are the sizes of the corresponding feature level,\n            and the last dimension 2 represent (coord_x, coord_y),\n            otherwise the shape should be (N, 4),\n            and the last dimension 4 represent\n            (coord_x, coord_y, stride_w, stride_h).\n        \"\"\"\n\n        assert self.num_levels == len(featmap_sizes)\n        multi_level_priors = []\n        for i in range(self.num_levels):\n            priors = self.single_level_grid_priors(\n                featmap_sizes[i],\n                level_idx=i,\n                dtype=dtype,\n                device=device,\n                with_stride=with_stride)\n            multi_level_priors.append(priors)\n        return multi_level_priors\n\n    def single_level_grid_priors(self,\n                                 featmap_size: Tuple[int],\n                                 level_idx: int,\n                                 dtype: torch.dtype = torch.float32,\n                                 device: DeviceType = 'cuda',\n                                 with_stride: bool = False) -> Tensor:\n        \"\"\"Generate grid Points of a single level.\n\n        Note:\n            This function is usually called by method ``self.grid_priors``.\n\n        Args:\n            featmap_size (tuple[int]): Size of the feature maps, arrange as\n                (h, w).\n            level_idx (int): The index of corresponding feature map level.\n            dtype (:obj:`dtype`): Dtype of priors. Defaults to torch.float32.\n            device (str | torch.device): The device the tensor will be put on.\n                Defaults to 'cuda'.\n            with_stride (bool): Concatenate the stride to the last dimension\n                of points.\n\n        Return:\n            Tensor: Points of single feature levels.\n            The shape of tensor should be (N, 2) when with stride is\n            ``False``, where N = width * height, width and height\n            are the sizes of the corresponding feature level,\n            and the last dimension 2 represent (coord_x, coord_y),\n            otherwise the shape should be (N, 4),\n            and the last dimension 4 represent\n            (coord_x, coord_y, stride_w, stride_h).\n        \"\"\"\n        feat_h, feat_w = featmap_size\n        stride_w, stride_h = self.strides[level_idx]\n        shift_x = (torch.arange(0, feat_w, device=device) +\n                   self.offset) * stride_w\n        # keep featmap_size as Tensor instead of int, so that we\n        # can convert to ONNX correctly\n        shift_x = shift_x.to(dtype)\n\n        shift_y = (torch.arange(0, feat_h, device=device) +\n                   self.offset) * stride_h\n        # keep featmap_size as Tensor instead of int, so that we\n        # can convert to ONNX correctly\n        shift_y = shift_y.to(dtype)\n        shift_xx, shift_yy = self._meshgrid(shift_x, shift_y)\n        if not with_stride:\n            shifts = torch.stack([shift_xx, shift_yy], dim=-1)\n        else:\n            # use `shape[0]` instead of `len(shift_xx)` for ONNX export\n            stride_w = shift_xx.new_full((shift_xx.shape[0], ),\n                                         stride_w).to(dtype)\n            stride_h = shift_xx.new_full((shift_yy.shape[0], ),\n                                         stride_h).to(dtype)\n            shifts = torch.stack([shift_xx, shift_yy, stride_w, stride_h],\n                                 dim=-1)\n        all_points = shifts.to(device)\n        return all_points\n\n    def valid_flags(self,\n                    featmap_sizes: List[Tuple[int, int]],\n                    pad_shape: Tuple[int],\n                    device: DeviceType = 'cuda') -> List[Tensor]:\n        \"\"\"Generate valid flags of points of multiple feature levels.\n\n        Args:\n            featmap_sizes (list(tuple)): List of feature map sizes in\n                multiple feature levels, each size arrange as\n                as (h, w).\n            pad_shape (tuple(int)): The padded shape of the image,\n                arrange as (h, w).\n            device (str | torch.device): The device where the anchors will be\n                put on.\n\n        Return:\n            list(torch.Tensor): Valid flags of points of multiple levels.\n        \"\"\"\n        assert self.num_levels == len(featmap_sizes)\n        multi_level_flags = []\n        for i in range(self.num_levels):\n            point_stride = self.strides[i]\n            feat_h, feat_w = featmap_sizes[i]\n            h, w = pad_shape[:2]\n            valid_feat_h = min(int(np.ceil(h / point_stride[1])), feat_h)\n            valid_feat_w = min(int(np.ceil(w / point_stride[0])), feat_w)\n            flags = self.single_level_valid_flags((feat_h, feat_w),\n                                                  (valid_feat_h, valid_feat_w),\n                                                  device=device)\n            multi_level_flags.append(flags)\n        return multi_level_flags\n\n    def single_level_valid_flags(self,\n                                 featmap_size: Tuple[int, int],\n                                 valid_size: Tuple[int, int],\n                                 device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate the valid flags of points of a single feature map.\n\n        Args:\n            featmap_size (tuple[int]): The size of feature maps, arrange as\n                as (h, w).\n            valid_size (tuple[int]): The valid size of the feature maps.\n                The size arrange as as (h, w).\n            device (str | torch.device): The device where the flags will be\n            put on. Defaults to 'cuda'.\n\n        Returns:\n            torch.Tensor: The valid flags of each points in a single level \\\n                feature map.\n        \"\"\"\n        feat_h, feat_w = featmap_size\n        valid_h, valid_w = valid_size\n        assert valid_h <= feat_h and valid_w <= feat_w\n        valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device)\n        valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device)\n        valid_x[:valid_w] = 1\n        valid_y[:valid_h] = 1\n        valid_xx, valid_yy = self._meshgrid(valid_x, valid_y)\n        valid = valid_xx & valid_yy\n        return valid\n\n    def sparse_priors(self,\n                      prior_idxs: Tensor,\n                      featmap_size: Tuple[int],\n                      level_idx: int,\n                      dtype: torch.dtype = torch.float32,\n                      device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate sparse points according to the ``prior_idxs``.\n\n        Args:\n            prior_idxs (Tensor): The index of corresponding anchors\n                in the feature map.\n            featmap_size (tuple[int]): feature map size arrange as (w, h).\n            level_idx (int): The level index of corresponding feature\n                map.\n            dtype (obj:`torch.dtype`): Date type of points. Defaults to\n                ``torch.float32``.\n            device (str | torch.device): The device where the points is\n                located.\n        Returns:\n            Tensor: Anchor with shape (N, 2), N should be equal to\n            the length of ``prior_idxs``. And last dimension\n            2 represent (coord_x, coord_y).\n        \"\"\"\n        height, width = featmap_size\n        x = (prior_idxs % width + self.offset) * self.strides[level_idx][0]\n        y = ((prior_idxs // width) % height +\n             self.offset) * self.strides[level_idx][1]\n        prioris = torch.stack([x, y], 1).to(dtype)\n        prioris = prioris.to(device)\n        return prioris\n"
  },
  {
    "path": "mmdet/models/task_modules/prior_generators/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.structures.bbox import BaseBoxes\n\n\ndef anchor_inside_flags(flat_anchors: Tensor,\n                        valid_flags: Tensor,\n                        img_shape: Tuple[int],\n                        allowed_border: int = 0) -> Tensor:\n    \"\"\"Check whether the anchors are inside the border.\n\n    Args:\n        flat_anchors (torch.Tensor): Flatten anchors, shape (n, 4).\n        valid_flags (torch.Tensor): An existing valid flags of anchors.\n        img_shape (tuple(int)): Shape of current image.\n        allowed_border (int): The border to allow the valid anchor.\n            Defaults to 0.\n\n    Returns:\n        torch.Tensor: Flags indicating whether the anchors are inside a \\\n            valid range.\n    \"\"\"\n    img_h, img_w = img_shape[:2]\n    if allowed_border >= 0:\n        if isinstance(flat_anchors, BaseBoxes):\n            inside_flags = valid_flags & \\\n                flat_anchors.is_inside([img_h, img_w],\n                                       all_inside=True,\n                                       allowed_border=allowed_border)\n        else:\n            inside_flags = valid_flags & \\\n                (flat_anchors[:, 0] >= -allowed_border) & \\\n                (flat_anchors[:, 1] >= -allowed_border) & \\\n                (flat_anchors[:, 2] < img_w + allowed_border) & \\\n                (flat_anchors[:, 3] < img_h + allowed_border)\n    else:\n        inside_flags = valid_flags\n    return inside_flags\n\n\ndef calc_region(bbox: Tensor,\n                ratio: float,\n                featmap_size: Optional[Tuple] = None) -> Tuple[int]:\n    \"\"\"Calculate a proportional bbox region.\n\n    The bbox center are fixed and the new h' and w' is h * ratio and w * ratio.\n\n    Args:\n        bbox (Tensor): Bboxes to calculate regions, shape (n, 4).\n        ratio (float): Ratio of the output region.\n        featmap_size (tuple, Optional): Feature map size in (height, width)\n            order used for clipping the boundary. Defaults to None.\n\n    Returns:\n        tuple: x1, y1, x2, y2\n    \"\"\"\n    x1 = torch.round((1 - ratio) * bbox[0] + ratio * bbox[2]).long()\n    y1 = torch.round((1 - ratio) * bbox[1] + ratio * bbox[3]).long()\n    x2 = torch.round(ratio * bbox[0] + (1 - ratio) * bbox[2]).long()\n    y2 = torch.round(ratio * bbox[1] + (1 - ratio) * bbox[3]).long()\n    if featmap_size is not None:\n        x1 = x1.clamp(min=0, max=featmap_size[1])\n        y1 = y1.clamp(min=0, max=featmap_size[0])\n        x2 = x2.clamp(min=0, max=featmap_size[1])\n        y2 = y2.clamp(min=0, max=featmap_size[0])\n    return (x1, y1, x2, y2)\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_sampler import BaseSampler\nfrom .combined_sampler import CombinedSampler\nfrom .instance_balanced_pos_sampler import InstanceBalancedPosSampler\nfrom .iou_balanced_neg_sampler import IoUBalancedNegSampler\nfrom .mask_pseudo_sampler import MaskPseudoSampler\nfrom .mask_sampling_result import MaskSamplingResult\nfrom .multi_instance_random_sampler import MultiInsRandomSampler\nfrom .multi_instance_sampling_result import MultiInstanceSamplingResult\nfrom .ohem_sampler import OHEMSampler\nfrom .pseudo_sampler import PseudoSampler\nfrom .random_sampler import RandomSampler\nfrom .sampling_result import SamplingResult\nfrom .score_hlr_sampler import ScoreHLRSampler\n\n__all__ = [\n    'BaseSampler', 'PseudoSampler', 'RandomSampler',\n    'InstanceBalancedPosSampler', 'IoUBalancedNegSampler', 'CombinedSampler',\n    'OHEMSampler', 'SamplingResult', 'ScoreHLRSampler', 'MaskPseudoSampler',\n    'MaskSamplingResult', 'MultiInstanceSamplingResult',\n    'MultiInsRandomSampler'\n]\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/base_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.structures.bbox import BaseBoxes, cat_boxes\nfrom ..assigners import AssignResult\nfrom .sampling_result import SamplingResult\n\n\nclass BaseSampler(metaclass=ABCMeta):\n    \"\"\"Base class of samplers.\n\n    Args:\n        num (int): Number of samples\n        pos_fraction (float): Fraction of positive samples\n        neg_pos_up (int): Upper bound number of negative and\n            positive samples. Defaults to -1.\n        add_gt_as_proposals (bool): Whether to add ground truth\n            boxes as proposals. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 num: int,\n                 pos_fraction: float,\n                 neg_pos_ub: int = -1,\n                 add_gt_as_proposals: bool = True,\n                 **kwargs) -> None:\n        self.num = num\n        self.pos_fraction = pos_fraction\n        self.neg_pos_ub = neg_pos_ub\n        self.add_gt_as_proposals = add_gt_as_proposals\n        self.pos_sampler = self\n        self.neg_sampler = self\n\n    @abstractmethod\n    def _sample_pos(self, assign_result: AssignResult, num_expected: int,\n                    **kwargs):\n        \"\"\"Sample positive samples.\"\"\"\n        pass\n\n    @abstractmethod\n    def _sample_neg(self, assign_result: AssignResult, num_expected: int,\n                    **kwargs):\n        \"\"\"Sample negative samples.\"\"\"\n        pass\n\n    def sample(self, assign_result: AssignResult, pred_instances: InstanceData,\n               gt_instances: InstanceData, **kwargs) -> SamplingResult:\n        \"\"\"Sample positive and negative bboxes.\n\n        This is a simple implementation of bbox sampling given candidates,\n        assigning results and ground truth bboxes.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Assigning results.\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n\n        Returns:\n            :obj:`SamplingResult`: Sampling result.\n\n        Example:\n            >>> from mmengine.structures import InstanceData\n            >>> from mmdet.models.task_modules.samplers import RandomSampler,\n            >>> from mmdet.models.task_modules.assigners import AssignResult\n            >>> from mmdet.models.task_modules.samplers.\n            ... sampling_result import ensure_rng, random_boxes\n            >>> rng = ensure_rng(None)\n            >>> assign_result = AssignResult.random(rng=rng)\n            >>> pred_instances = InstanceData()\n            >>> pred_instances.priors = random_boxes(assign_result.num_preds,\n            ...                                      rng=rng)\n            >>> gt_instances = InstanceData()\n            >>> gt_instances.bboxes = random_boxes(assign_result.num_gts,\n            ...                                    rng=rng)\n            >>> gt_instances.labels = torch.randint(\n            ...     0, 5, (assign_result.num_gts,), dtype=torch.long)\n            >>> self = RandomSampler(num=32, pos_fraction=0.5, neg_pos_ub=-1,\n            >>>                      add_gt_as_proposals=False)\n            >>> self = self.sample(assign_result, pred_instances, gt_instances)\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n        gt_labels = gt_instances.labels\n        if len(priors.shape) < 2:\n            priors = priors[None, :]\n\n        gt_flags = priors.new_zeros((priors.shape[0], ), dtype=torch.uint8)\n        if self.add_gt_as_proposals and len(gt_bboxes) > 0:\n            # When `gt_bboxes` and `priors` are all box type, convert\n            # `gt_bboxes` type to `priors` type.\n            if (isinstance(gt_bboxes, BaseBoxes)\n                    and isinstance(priors, BaseBoxes)):\n                gt_bboxes_ = gt_bboxes.convert_to(type(priors))\n            else:\n                gt_bboxes_ = gt_bboxes\n            priors = cat_boxes([gt_bboxes_, priors], dim=0)\n            assign_result.add_gt_(gt_labels)\n            gt_ones = priors.new_ones(gt_bboxes_.shape[0], dtype=torch.uint8)\n            gt_flags = torch.cat([gt_ones, gt_flags])\n\n        num_expected_pos = int(self.num * self.pos_fraction)\n        pos_inds = self.pos_sampler._sample_pos(\n            assign_result, num_expected_pos, bboxes=priors, **kwargs)\n        # We found that sampled indices have duplicated items occasionally.\n        # (may be a bug of PyTorch)\n        pos_inds = pos_inds.unique()\n        num_sampled_pos = pos_inds.numel()\n        num_expected_neg = self.num - num_sampled_pos\n        if self.neg_pos_ub >= 0:\n            _pos = max(1, num_sampled_pos)\n            neg_upper_bound = int(self.neg_pos_ub * _pos)\n            if num_expected_neg > neg_upper_bound:\n                num_expected_neg = neg_upper_bound\n        neg_inds = self.neg_sampler._sample_neg(\n            assign_result, num_expected_neg, bboxes=priors, **kwargs)\n        neg_inds = neg_inds.unique()\n\n        sampling_result = SamplingResult(\n            pos_inds=pos_inds,\n            neg_inds=neg_inds,\n            priors=priors,\n            gt_bboxes=gt_bboxes,\n            assign_result=assign_result,\n            gt_flags=gt_flags)\n        return sampling_result\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/combined_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.registry import TASK_UTILS\nfrom .base_sampler import BaseSampler\n\n\n@TASK_UTILS.register_module()\nclass CombinedSampler(BaseSampler):\n    \"\"\"A sampler that combines positive sampler and negative sampler.\"\"\"\n\n    def __init__(self, pos_sampler, neg_sampler, **kwargs):\n        super(CombinedSampler, self).__init__(**kwargs)\n        self.pos_sampler = TASK_UTILS.build(pos_sampler, default_args=kwargs)\n        self.neg_sampler = TASK_UTILS.build(neg_sampler, default_args=kwargs)\n\n    def _sample_pos(self, **kwargs):\n        \"\"\"Sample positive samples.\"\"\"\n        raise NotImplementedError\n\n    def _sample_neg(self, **kwargs):\n        \"\"\"Sample negative samples.\"\"\"\n        raise NotImplementedError\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/instance_balanced_pos_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport torch\n\nfrom mmdet.registry import TASK_UTILS\nfrom .random_sampler import RandomSampler\n\n\n@TASK_UTILS.register_module()\nclass InstanceBalancedPosSampler(RandomSampler):\n    \"\"\"Instance balanced sampler that samples equal number of positive samples\n    for each instance.\"\"\"\n\n    def _sample_pos(self, assign_result, num_expected, **kwargs):\n        \"\"\"Sample positive boxes.\n\n        Args:\n            assign_result (:obj:`AssignResult`): The assigned results of boxes.\n            num_expected (int): The number of expected positive samples\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False)\n        if pos_inds.numel() != 0:\n            pos_inds = pos_inds.squeeze(1)\n        if pos_inds.numel() <= num_expected:\n            return pos_inds\n        else:\n            unique_gt_inds = assign_result.gt_inds[pos_inds].unique()\n            num_gts = len(unique_gt_inds)\n            num_per_gt = int(round(num_expected / float(num_gts)) + 1)\n            sampled_inds = []\n            for i in unique_gt_inds:\n                inds = torch.nonzero(\n                    assign_result.gt_inds == i.item(), as_tuple=False)\n                if inds.numel() != 0:\n                    inds = inds.squeeze(1)\n                else:\n                    continue\n                if len(inds) > num_per_gt:\n                    inds = self.random_choice(inds, num_per_gt)\n                sampled_inds.append(inds)\n            sampled_inds = torch.cat(sampled_inds)\n            if len(sampled_inds) < num_expected:\n                num_extra = num_expected - len(sampled_inds)\n                extra_inds = np.array(\n                    list(set(pos_inds.cpu()) - set(sampled_inds.cpu())))\n                if len(extra_inds) > num_extra:\n                    extra_inds = self.random_choice(extra_inds, num_extra)\n                extra_inds = torch.from_numpy(extra_inds).to(\n                    assign_result.gt_inds.device).long()\n                sampled_inds = torch.cat([sampled_inds, extra_inds])\n            elif len(sampled_inds) > num_expected:\n                sampled_inds = self.random_choice(sampled_inds, num_expected)\n            return sampled_inds\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/iou_balanced_neg_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport torch\n\nfrom mmdet.registry import TASK_UTILS\nfrom .random_sampler import RandomSampler\n\n\n@TASK_UTILS.register_module()\nclass IoUBalancedNegSampler(RandomSampler):\n    \"\"\"IoU Balanced Sampling.\n\n    arXiv: https://arxiv.org/pdf/1904.02701.pdf (CVPR 2019)\n\n    Sampling proposals according to their IoU. `floor_fraction` of needed RoIs\n    are sampled from proposals whose IoU are lower than `floor_thr` randomly.\n    The others are sampled from proposals whose IoU are higher than\n    `floor_thr`. These proposals are sampled from some bins evenly, which are\n    split by `num_bins` via IoU evenly.\n\n    Args:\n        num (int): number of proposals.\n        pos_fraction (float): fraction of positive proposals.\n        floor_thr (float): threshold (minimum) IoU for IoU balanced sampling,\n            set to -1 if all using IoU balanced sampling.\n        floor_fraction (float): sampling fraction of proposals under floor_thr.\n        num_bins (int): number of bins in IoU balanced sampling.\n    \"\"\"\n\n    def __init__(self,\n                 num,\n                 pos_fraction,\n                 floor_thr=-1,\n                 floor_fraction=0,\n                 num_bins=3,\n                 **kwargs):\n        super(IoUBalancedNegSampler, self).__init__(num, pos_fraction,\n                                                    **kwargs)\n        assert floor_thr >= 0 or floor_thr == -1\n        assert 0 <= floor_fraction <= 1\n        assert num_bins >= 1\n\n        self.floor_thr = floor_thr\n        self.floor_fraction = floor_fraction\n        self.num_bins = num_bins\n\n    def sample_via_interval(self, max_overlaps, full_set, num_expected):\n        \"\"\"Sample according to the iou interval.\n\n        Args:\n            max_overlaps (torch.Tensor): IoU between bounding boxes and ground\n                truth boxes.\n            full_set (set(int)): A full set of indices of boxes。\n            num_expected (int): Number of expected samples。\n\n        Returns:\n            np.ndarray: Indices  of samples\n        \"\"\"\n        max_iou = max_overlaps.max()\n        iou_interval = (max_iou - self.floor_thr) / self.num_bins\n        per_num_expected = int(num_expected / self.num_bins)\n\n        sampled_inds = []\n        for i in range(self.num_bins):\n            start_iou = self.floor_thr + i * iou_interval\n            end_iou = self.floor_thr + (i + 1) * iou_interval\n            tmp_set = set(\n                np.where(\n                    np.logical_and(max_overlaps >= start_iou,\n                                   max_overlaps < end_iou))[0])\n            tmp_inds = list(tmp_set & full_set)\n            if len(tmp_inds) > per_num_expected:\n                tmp_sampled_set = self.random_choice(tmp_inds,\n                                                     per_num_expected)\n            else:\n                tmp_sampled_set = np.array(tmp_inds, dtype=np.int64)\n            sampled_inds.append(tmp_sampled_set)\n\n        sampled_inds = np.concatenate(sampled_inds)\n        if len(sampled_inds) < num_expected:\n            num_extra = num_expected - len(sampled_inds)\n            extra_inds = np.array(list(full_set - set(sampled_inds)))\n            if len(extra_inds) > num_extra:\n                extra_inds = self.random_choice(extra_inds, num_extra)\n            sampled_inds = np.concatenate([sampled_inds, extra_inds])\n\n        return sampled_inds\n\n    def _sample_neg(self, assign_result, num_expected, **kwargs):\n        \"\"\"Sample negative boxes.\n\n        Args:\n            assign_result (:obj:`AssignResult`): The assigned results of boxes.\n            num_expected (int): The number of expected negative samples\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False)\n        if neg_inds.numel() != 0:\n            neg_inds = neg_inds.squeeze(1)\n        if len(neg_inds) <= num_expected:\n            return neg_inds\n        else:\n            max_overlaps = assign_result.max_overlaps.cpu().numpy()\n            # balance sampling for negative samples\n            neg_set = set(neg_inds.cpu().numpy())\n\n            if self.floor_thr > 0:\n                floor_set = set(\n                    np.where(\n                        np.logical_and(max_overlaps >= 0,\n                                       max_overlaps < self.floor_thr))[0])\n                iou_sampling_set = set(\n                    np.where(max_overlaps >= self.floor_thr)[0])\n            elif self.floor_thr == 0:\n                floor_set = set(np.where(max_overlaps == 0)[0])\n                iou_sampling_set = set(\n                    np.where(max_overlaps > self.floor_thr)[0])\n            else:\n                floor_set = set()\n                iou_sampling_set = set(\n                    np.where(max_overlaps > self.floor_thr)[0])\n                # for sampling interval calculation\n                self.floor_thr = 0\n\n            floor_neg_inds = list(floor_set & neg_set)\n            iou_sampling_neg_inds = list(iou_sampling_set & neg_set)\n            num_expected_iou_sampling = int(num_expected *\n                                            (1 - self.floor_fraction))\n            if len(iou_sampling_neg_inds) > num_expected_iou_sampling:\n                if self.num_bins >= 2:\n                    iou_sampled_inds = self.sample_via_interval(\n                        max_overlaps, set(iou_sampling_neg_inds),\n                        num_expected_iou_sampling)\n                else:\n                    iou_sampled_inds = self.random_choice(\n                        iou_sampling_neg_inds, num_expected_iou_sampling)\n            else:\n                iou_sampled_inds = np.array(\n                    iou_sampling_neg_inds, dtype=np.int64)\n            num_expected_floor = num_expected - len(iou_sampled_inds)\n            if len(floor_neg_inds) > num_expected_floor:\n                sampled_floor_inds = self.random_choice(\n                    floor_neg_inds, num_expected_floor)\n            else:\n                sampled_floor_inds = np.array(floor_neg_inds, dtype=np.int64)\n            sampled_inds = np.concatenate(\n                (sampled_floor_inds, iou_sampled_inds))\n            if len(sampled_inds) < num_expected:\n                num_extra = num_expected - len(sampled_inds)\n                extra_inds = np.array(list(neg_set - set(sampled_inds)))\n                if len(extra_inds) > num_extra:\n                    extra_inds = self.random_choice(extra_inds, num_extra)\n                sampled_inds = np.concatenate((sampled_inds, extra_inds))\n            sampled_inds = torch.from_numpy(sampled_inds).long().to(\n                assign_result.gt_inds.device)\n            return sampled_inds\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/mask_pseudo_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"copy from\nhttps://github.com/ZwwWayne/K-Net/blob/main/knet/det/mask_pseudo_sampler.py.\"\"\"\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom ..assigners import AssignResult\nfrom .base_sampler import BaseSampler\nfrom .mask_sampling_result import MaskSamplingResult\n\n\n@TASK_UTILS.register_module()\nclass MaskPseudoSampler(BaseSampler):\n    \"\"\"A pseudo sampler that does not do sampling actually.\"\"\"\n\n    def __init__(self, **kwargs):\n        pass\n\n    def _sample_pos(self, **kwargs):\n        \"\"\"Sample positive samples.\"\"\"\n        raise NotImplementedError\n\n    def _sample_neg(self, **kwargs):\n        \"\"\"Sample negative samples.\"\"\"\n        raise NotImplementedError\n\n    def sample(self, assign_result: AssignResult, pred_instances: InstanceData,\n               gt_instances: InstanceData, *args, **kwargs):\n        \"\"\"Directly returns the positive and negative indices  of samples.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Mask assigning results.\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``scores`` and ``masks`` predicted\n                by the model.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``labels`` and ``masks``\n                attributes.\n\n        Returns:\n            :obj:`SamplingResult`: sampler results\n        \"\"\"\n        pred_masks = pred_instances.masks\n        gt_masks = gt_instances.masks\n        pos_inds = torch.nonzero(\n            assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique()\n        neg_inds = torch.nonzero(\n            assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique()\n        gt_flags = pred_masks.new_zeros(pred_masks.shape[0], dtype=torch.uint8)\n        sampling_result = MaskSamplingResult(\n            pos_inds=pos_inds,\n            neg_inds=neg_inds,\n            masks=pred_masks,\n            gt_masks=gt_masks,\n            assign_result=assign_result,\n            gt_flags=gt_flags,\n            avg_factor_with_neg=False)\n        return sampling_result\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/mask_sampling_result.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"copy from\nhttps://github.com/ZwwWayne/K-Net/blob/main/knet/det/mask_pseudo_sampler.py.\"\"\"\n\nimport torch\nfrom torch import Tensor\n\nfrom ..assigners import AssignResult\nfrom .sampling_result import SamplingResult\n\n\nclass MaskSamplingResult(SamplingResult):\n    \"\"\"Mask sampling result.\"\"\"\n\n    def __init__(self,\n                 pos_inds: Tensor,\n                 neg_inds: Tensor,\n                 masks: Tensor,\n                 gt_masks: Tensor,\n                 assign_result: AssignResult,\n                 gt_flags: Tensor,\n                 avg_factor_with_neg: bool = True) -> None:\n        self.pos_inds = pos_inds\n        self.neg_inds = neg_inds\n        self.num_pos = max(pos_inds.numel(), 1)\n        self.num_neg = max(neg_inds.numel(), 1)\n        self.avg_factor = self.num_pos + self.num_neg \\\n            if avg_factor_with_neg else self.num_pos\n\n        self.pos_masks = masks[pos_inds]\n        self.neg_masks = masks[neg_inds]\n        self.pos_is_gt = gt_flags[pos_inds]\n\n        self.num_gts = gt_masks.shape[0]\n        self.pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1\n\n        if gt_masks.numel() == 0:\n            # hack for index error case\n            assert self.pos_assigned_gt_inds.numel() == 0\n            self.pos_gt_masks = torch.empty_like(gt_masks)\n        else:\n            self.pos_gt_masks = gt_masks[self.pos_assigned_gt_inds, :]\n\n    @property\n    def masks(self) -> Tensor:\n        \"\"\"torch.Tensor: concatenated positive and negative masks.\"\"\"\n        return torch.cat([self.pos_masks, self.neg_masks])\n\n    def __nice__(self) -> str:\n        data = self.info.copy()\n        data['pos_masks'] = data.pop('pos_masks').shape\n        data['neg_masks'] = data.pop('neg_masks').shape\n        parts = [f\"'{k}': {v!r}\" for k, v in sorted(data.items())]\n        body = '    ' + ',\\n    '.join(parts)\n        return '{\\n' + body + '\\n}'\n\n    @property\n    def info(self) -> dict:\n        \"\"\"Returns a dictionary of info about the object.\"\"\"\n        return {\n            'pos_inds': self.pos_inds,\n            'neg_inds': self.neg_inds,\n            'pos_masks': self.pos_masks,\n            'neg_masks': self.neg_masks,\n            'pos_is_gt': self.pos_is_gt,\n            'num_gts': self.num_gts,\n            'pos_assigned_gt_inds': self.pos_assigned_gt_inds,\n        }\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/multi_instance_random_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom numpy import ndarray\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom ..assigners import AssignResult\nfrom .multi_instance_sampling_result import MultiInstanceSamplingResult\nfrom .random_sampler import RandomSampler\n\n\n@TASK_UTILS.register_module()\nclass MultiInsRandomSampler(RandomSampler):\n    \"\"\"Random sampler for multi instance.\n\n    Note:\n        Multi-instance means to predict multiple detection boxes with\n        one proposal box. `AssignResult` may assign multiple gt boxes\n        to each proposal box, in this case `RandomSampler` should be\n        replaced by `MultiInsRandomSampler`\n    \"\"\"\n\n    def _sample_pos(self, assign_result: AssignResult, num_expected: int,\n                    **kwargs) -> Union[Tensor, ndarray]:\n        \"\"\"Randomly sample some positive samples.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Bbox assigning results.\n            num_expected (int): The number of expected positive samples\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        pos_inds = torch.nonzero(\n            assign_result.labels[:, 0] > 0, as_tuple=False)\n        if pos_inds.numel() != 0:\n            pos_inds = pos_inds.squeeze(1)\n        if pos_inds.numel() <= num_expected:\n            return pos_inds\n        else:\n            return self.random_choice(pos_inds, num_expected)\n\n    def _sample_neg(self, assign_result: AssignResult, num_expected: int,\n                    **kwargs) -> Union[Tensor, ndarray]:\n        \"\"\"Randomly sample some negative samples.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Bbox assigning results.\n            num_expected (int): The number of expected positive samples\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        neg_inds = torch.nonzero(\n            assign_result.labels[:, 0] == 0, as_tuple=False)\n        if neg_inds.numel() != 0:\n            neg_inds = neg_inds.squeeze(1)\n        if len(neg_inds) <= num_expected:\n            return neg_inds\n        else:\n            return self.random_choice(neg_inds, num_expected)\n\n    def sample(self, assign_result: AssignResult, pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               **kwargs) -> MultiInstanceSamplingResult:\n        \"\"\"Sample positive and negative bboxes.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Assigning results from\n                MultiInstanceAssigner.\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n\n        Returns:\n            :obj:`MultiInstanceSamplingResult`: Sampling result.\n        \"\"\"\n\n        assert 'batch_gt_instances_ignore' in kwargs, \\\n            'batch_gt_instances_ignore is necessary for MultiInsRandomSampler'\n\n        gt_bboxes = gt_instances.bboxes\n        ignore_bboxes = kwargs['batch_gt_instances_ignore'].bboxes\n        gt_and_ignore_bboxes = torch.cat([gt_bboxes, ignore_bboxes], dim=0)\n        priors = pred_instances.priors\n        if len(priors.shape) < 2:\n            priors = priors[None, :]\n        priors = priors[:, :4]\n\n        gt_flags = priors.new_zeros((priors.shape[0], ), dtype=torch.uint8)\n        priors = torch.cat([priors, gt_and_ignore_bboxes], dim=0)\n        gt_ones = priors.new_ones(\n            gt_and_ignore_bboxes.shape[0], dtype=torch.uint8)\n        gt_flags = torch.cat([gt_flags, gt_ones])\n\n        num_expected_pos = int(self.num * self.pos_fraction)\n        pos_inds = self.pos_sampler._sample_pos(assign_result,\n                                                num_expected_pos)\n        # We found that sampled indices have duplicated items occasionally.\n        # (may be a bug of PyTorch)\n        pos_inds = pos_inds.unique()\n        num_sampled_pos = pos_inds.numel()\n        num_expected_neg = self.num - num_sampled_pos\n        if self.neg_pos_ub >= 0:\n            _pos = max(1, num_sampled_pos)\n            neg_upper_bound = int(self.neg_pos_ub * _pos)\n            if num_expected_neg > neg_upper_bound:\n                num_expected_neg = neg_upper_bound\n        neg_inds = self.neg_sampler._sample_neg(assign_result,\n                                                num_expected_neg)\n        neg_inds = neg_inds.unique()\n\n        sampling_result = MultiInstanceSamplingResult(\n            pos_inds=pos_inds,\n            neg_inds=neg_inds,\n            priors=priors,\n            gt_and_ignore_bboxes=gt_and_ignore_bboxes,\n            assign_result=assign_result,\n            gt_flags=gt_flags)\n        return sampling_result\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/multi_instance_sampling_result.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom torch import Tensor\n\nfrom ..assigners import AssignResult\nfrom .sampling_result import SamplingResult\n\n\nclass MultiInstanceSamplingResult(SamplingResult):\n    \"\"\"Bbox sampling result. Further encapsulation of SamplingResult. Three\n    attributes neg_assigned_gt_inds, neg_gt_labels, and neg_gt_bboxes have been\n    added for SamplingResult.\n\n    Args:\n        pos_inds (Tensor): Indices of positive samples.\n        neg_inds (Tensor): Indices of negative samples.\n        priors (Tensor): The priors can be anchors or points,\n            or the bboxes predicted by the previous stage.\n        gt_and_ignore_bboxes (Tensor): Ground truth and ignore bboxes.\n        assign_result (:obj:`AssignResult`): Assigning results.\n        gt_flags (Tensor): The Ground truth flags.\n        avg_factor_with_neg (bool):  If True, ``avg_factor`` equal to\n            the number of total priors; Otherwise, it is the number of\n            positive priors. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 pos_inds: Tensor,\n                 neg_inds: Tensor,\n                 priors: Tensor,\n                 gt_and_ignore_bboxes: Tensor,\n                 assign_result: AssignResult,\n                 gt_flags: Tensor,\n                 avg_factor_with_neg: bool = True) -> None:\n        self.neg_assigned_gt_inds = assign_result.gt_inds[neg_inds]\n        self.neg_gt_labels = assign_result.labels[neg_inds]\n\n        if gt_and_ignore_bboxes.numel() == 0:\n            self.neg_gt_bboxes = torch.empty_like(gt_and_ignore_bboxes).view(\n                -1, 4)\n        else:\n            if len(gt_and_ignore_bboxes.shape) < 2:\n                gt_and_ignore_bboxes = gt_and_ignore_bboxes.view(-1, 4)\n            self.neg_gt_bboxes = gt_and_ignore_bboxes[\n                self.neg_assigned_gt_inds.long(), :]\n\n        # To resist the minus 1 operation in `SamplingResult.init()`.\n        assign_result.gt_inds += 1\n        super().__init__(\n            pos_inds=pos_inds,\n            neg_inds=neg_inds,\n            priors=priors,\n            gt_bboxes=gt_and_ignore_bboxes,\n            assign_result=assign_result,\n            gt_flags=gt_flags,\n            avg_factor_with_neg=avg_factor_with_neg)\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/ohem_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import bbox2roi\nfrom .base_sampler import BaseSampler\n\n\n@TASK_UTILS.register_module()\nclass OHEMSampler(BaseSampler):\n    r\"\"\"Online Hard Example Mining Sampler described in `Training Region-based\n    Object Detectors with Online Hard Example Mining\n    <https://arxiv.org/abs/1604.03540>`_.\n    \"\"\"\n\n    def __init__(self,\n                 num,\n                 pos_fraction,\n                 context,\n                 neg_pos_ub=-1,\n                 add_gt_as_proposals=True,\n                 loss_key='loss_cls',\n                 **kwargs):\n        super(OHEMSampler, self).__init__(num, pos_fraction, neg_pos_ub,\n                                          add_gt_as_proposals)\n        self.context = context\n        if not hasattr(self.context, 'num_stages'):\n            self.bbox_head = self.context.bbox_head\n        else:\n            self.bbox_head = self.context.bbox_head[self.context.current_stage]\n\n        self.loss_key = loss_key\n\n    def hard_mining(self, inds, num_expected, bboxes, labels, feats):\n        with torch.no_grad():\n            rois = bbox2roi([bboxes])\n            if not hasattr(self.context, 'num_stages'):\n                bbox_results = self.context._bbox_forward(feats, rois)\n            else:\n                bbox_results = self.context._bbox_forward(\n                    self.context.current_stage, feats, rois)\n            cls_score = bbox_results['cls_score']\n            loss = self.bbox_head.loss(\n                cls_score=cls_score,\n                bbox_pred=None,\n                rois=rois,\n                labels=labels,\n                label_weights=cls_score.new_ones(cls_score.size(0)),\n                bbox_targets=None,\n                bbox_weights=None,\n                reduction_override='none')[self.loss_key]\n            _, topk_loss_inds = loss.topk(num_expected)\n        return inds[topk_loss_inds]\n\n    def _sample_pos(self,\n                    assign_result,\n                    num_expected,\n                    bboxes=None,\n                    feats=None,\n                    **kwargs):\n        \"\"\"Sample positive boxes.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Assigned results\n            num_expected (int): Number of expected positive samples\n            bboxes (torch.Tensor, optional): Boxes. Defaults to None.\n            feats (list[torch.Tensor], optional): Multi-level features.\n                Defaults to None.\n\n        Returns:\n            torch.Tensor: Indices  of positive samples\n        \"\"\"\n        # Sample some hard positive samples\n        pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False)\n        if pos_inds.numel() != 0:\n            pos_inds = pos_inds.squeeze(1)\n        if pos_inds.numel() <= num_expected:\n            return pos_inds\n        else:\n            return self.hard_mining(pos_inds, num_expected, bboxes[pos_inds],\n                                    assign_result.labels[pos_inds], feats)\n\n    def _sample_neg(self,\n                    assign_result,\n                    num_expected,\n                    bboxes=None,\n                    feats=None,\n                    **kwargs):\n        \"\"\"Sample negative boxes.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Assigned results\n            num_expected (int): Number of expected negative samples\n            bboxes (torch.Tensor, optional): Boxes. Defaults to None.\n            feats (list[torch.Tensor], optional): Multi-level features.\n                Defaults to None.\n\n        Returns:\n            torch.Tensor: Indices  of negative samples\n        \"\"\"\n        # Sample some hard negative samples\n        neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False)\n        if neg_inds.numel() != 0:\n            neg_inds = neg_inds.squeeze(1)\n        if len(neg_inds) <= num_expected:\n            return neg_inds\n        else:\n            neg_labels = assign_result.labels.new_empty(\n                neg_inds.size(0)).fill_(self.bbox_head.num_classes)\n            return self.hard_mining(neg_inds, num_expected, bboxes[neg_inds],\n                                    neg_labels, feats)\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/pseudo_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import TASK_UTILS\nfrom ..assigners import AssignResult\nfrom .base_sampler import BaseSampler\nfrom .sampling_result import SamplingResult\n\n\n@TASK_UTILS.register_module()\nclass PseudoSampler(BaseSampler):\n    \"\"\"A pseudo sampler that does not do sampling actually.\"\"\"\n\n    def __init__(self, **kwargs):\n        pass\n\n    def _sample_pos(self, **kwargs):\n        \"\"\"Sample positive samples.\"\"\"\n        raise NotImplementedError\n\n    def _sample_neg(self, **kwargs):\n        \"\"\"Sample negative samples.\"\"\"\n        raise NotImplementedError\n\n    def sample(self, assign_result: AssignResult, pred_instances: InstanceData,\n               gt_instances: InstanceData, *args, **kwargs):\n        \"\"\"Directly returns the positive and negative indices  of samples.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Bbox assigning results.\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors, points, or bboxes predicted by the model,\n                shape(n, 4).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes`` and ``labels``\n                attributes.\n\n        Returns:\n            :obj:`SamplingResult`: sampler results\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n\n        pos_inds = torch.nonzero(\n            assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique()\n        neg_inds = torch.nonzero(\n            assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique()\n\n        gt_flags = priors.new_zeros(priors.shape[0], dtype=torch.uint8)\n        sampling_result = SamplingResult(\n            pos_inds=pos_inds,\n            neg_inds=neg_inds,\n            priors=priors,\n            gt_bboxes=gt_bboxes,\n            assign_result=assign_result,\n            gt_flags=gt_flags,\n            avg_factor_with_neg=False)\n        return sampling_result\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/random_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nimport torch\nfrom numpy import ndarray\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom ..assigners import AssignResult\nfrom .base_sampler import BaseSampler\n\n\n@TASK_UTILS.register_module()\nclass RandomSampler(BaseSampler):\n    \"\"\"Random sampler.\n\n    Args:\n        num (int): Number of samples\n        pos_fraction (float): Fraction of positive samples\n        neg_pos_up (int): Upper bound number of negative and\n            positive samples. Defaults to -1.\n        add_gt_as_proposals (bool): Whether to add ground truth\n            boxes as proposals. Defaults to True.\n    \"\"\"\n\n    def __init__(self,\n                 num: int,\n                 pos_fraction: float,\n                 neg_pos_ub: int = -1,\n                 add_gt_as_proposals: bool = True,\n                 **kwargs):\n        from .sampling_result import ensure_rng\n        super().__init__(\n            num=num,\n            pos_fraction=pos_fraction,\n            neg_pos_ub=neg_pos_ub,\n            add_gt_as_proposals=add_gt_as_proposals)\n        self.rng = ensure_rng(kwargs.get('rng', None))\n\n    def random_choice(self, gallery: Union[Tensor, ndarray, list],\n                      num: int) -> Union[Tensor, ndarray]:\n        \"\"\"Random select some elements from the gallery.\n\n        If `gallery` is a Tensor, the returned indices will be a Tensor;\n        If `gallery` is a ndarray or list, the returned indices will be a\n        ndarray.\n\n        Args:\n            gallery (Tensor | ndarray | list): indices pool.\n            num (int): expected sample num.\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        assert len(gallery) >= num\n\n        is_tensor = isinstance(gallery, torch.Tensor)\n        if not is_tensor:\n            if torch.cuda.is_available():\n                device = torch.cuda.current_device()\n            else:\n                device = 'cpu'\n            gallery = torch.tensor(gallery, dtype=torch.long, device=device)\n        # This is a temporary fix. We can revert the following code\n        # when PyTorch fixes the abnormal return of torch.randperm.\n        # See: https://github.com/open-mmlab/mmdetection/pull/5014\n        perm = torch.randperm(gallery.numel())[:num].to(device=gallery.device)\n        rand_inds = gallery[perm]\n        if not is_tensor:\n            rand_inds = rand_inds.cpu().numpy()\n        return rand_inds\n\n    def _sample_pos(self, assign_result: AssignResult, num_expected: int,\n                    **kwargs) -> Union[Tensor, ndarray]:\n        \"\"\"Randomly sample some positive samples.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Bbox assigning results.\n            num_expected (int): The number of expected positive samples\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False)\n        if pos_inds.numel() != 0:\n            pos_inds = pos_inds.squeeze(1)\n        if pos_inds.numel() <= num_expected:\n            return pos_inds\n        else:\n            return self.random_choice(pos_inds, num_expected)\n\n    def _sample_neg(self, assign_result: AssignResult, num_expected: int,\n                    **kwargs) -> Union[Tensor, ndarray]:\n        \"\"\"Randomly sample some negative samples.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Bbox assigning results.\n            num_expected (int): The number of expected positive samples\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False)\n        if neg_inds.numel() != 0:\n            neg_inds = neg_inds.squeeze(1)\n        if len(neg_inds) <= num_expected:\n            return neg_inds\n        else:\n            return self.random_choice(neg_inds, num_expected)\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/sampling_result.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.structures.bbox import BaseBoxes, cat_boxes\nfrom mmdet.utils import util_mixins\nfrom mmdet.utils.util_random import ensure_rng\nfrom ..assigners import AssignResult\n\n\ndef random_boxes(num=1, scale=1, rng=None):\n    \"\"\"Simple version of ``kwimage.Boxes.random``\n\n    Returns:\n        Tensor: shape (n, 4) in x1, y1, x2, y2 format.\n\n    References:\n        https://gitlab.kitware.com/computer-vision/kwimage/blob/master/kwimage/structs/boxes.py#L1390\n\n    Example:\n        >>> num = 3\n        >>> scale = 512\n        >>> rng = 0\n        >>> boxes = random_boxes(num, scale, rng)\n        >>> print(boxes)\n        tensor([[280.9925, 278.9802, 308.6148, 366.1769],\n                [216.9113, 330.6978, 224.0446, 456.5878],\n                [405.3632, 196.3221, 493.3953, 270.7942]])\n    \"\"\"\n    rng = ensure_rng(rng)\n\n    tlbr = rng.rand(num, 4).astype(np.float32)\n\n    tl_x = np.minimum(tlbr[:, 0], tlbr[:, 2])\n    tl_y = np.minimum(tlbr[:, 1], tlbr[:, 3])\n    br_x = np.maximum(tlbr[:, 0], tlbr[:, 2])\n    br_y = np.maximum(tlbr[:, 1], tlbr[:, 3])\n\n    tlbr[:, 0] = tl_x * scale\n    tlbr[:, 1] = tl_y * scale\n    tlbr[:, 2] = br_x * scale\n    tlbr[:, 3] = br_y * scale\n\n    boxes = torch.from_numpy(tlbr)\n    return boxes\n\n\nclass SamplingResult(util_mixins.NiceRepr):\n    \"\"\"Bbox sampling result.\n\n    Args:\n        pos_inds (Tensor): Indices of positive samples.\n        neg_inds (Tensor): Indices of negative samples.\n        priors (Tensor): The priors can be anchors or points,\n            or the bboxes predicted by the previous stage.\n        gt_bboxes (Tensor): Ground truth of bboxes.\n        assign_result (:obj:`AssignResult`): Assigning results.\n        gt_flags (Tensor): The Ground truth flags.\n        avg_factor_with_neg (bool):  If True, ``avg_factor`` equal to\n            the number of total priors; Otherwise, it is the number of\n            positive priors. Defaults to True.\n\n    Example:\n        >>> # xdoctest: +IGNORE_WANT\n        >>> from mmdet.models.task_modules.samplers.sampling_result import *  # NOQA\n        >>> self = SamplingResult.random(rng=10)\n        >>> print(f'self = {self}')\n        self = <SamplingResult({\n            'neg_inds': tensor([1,  2,  3,  5,  6,  7,  8,\n                                9, 10, 11, 12, 13]),\n            'neg_priors': torch.Size([12, 4]),\n            'num_gts': 1,\n            'num_neg': 12,\n            'num_pos': 1,\n            'avg_factor': 13,\n            'pos_assigned_gt_inds': tensor([0]),\n            'pos_inds': tensor([0]),\n            'pos_is_gt': tensor([1], dtype=torch.uint8),\n            'pos_priors': torch.Size([1, 4])\n        })>\n    \"\"\"\n\n    def __init__(self,\n                 pos_inds: Tensor,\n                 neg_inds: Tensor,\n                 priors: Tensor,\n                 gt_bboxes: Tensor,\n                 assign_result: AssignResult,\n                 gt_flags: Tensor,\n                 avg_factor_with_neg: bool = True) -> None:\n        self.pos_inds = pos_inds\n        self.neg_inds = neg_inds\n        self.num_pos = max(pos_inds.numel(), 1)\n        self.num_neg = max(neg_inds.numel(), 1)\n        self.avg_factor_with_neg = avg_factor_with_neg\n        self.avg_factor = self.num_pos + self.num_neg \\\n            if avg_factor_with_neg else self.num_pos\n        self.pos_priors = priors[pos_inds]\n        self.neg_priors = priors[neg_inds]\n        self.pos_is_gt = gt_flags[pos_inds]\n\n        self.num_gts = gt_bboxes.shape[0]\n        self.pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1\n        self.pos_gt_labels = assign_result.labels[pos_inds]\n        box_dim = gt_bboxes.box_dim if isinstance(gt_bboxes, BaseBoxes) else 4\n        if gt_bboxes.numel() == 0:\n            # hack for index error case\n            assert self.pos_assigned_gt_inds.numel() == 0\n            self.pos_gt_bboxes = gt_bboxes.view(-1, box_dim)\n        else:\n            if len(gt_bboxes.shape) < 2:\n                gt_bboxes = gt_bboxes.view(-1, box_dim)\n            self.pos_gt_bboxes = gt_bboxes[self.pos_assigned_gt_inds.long()]\n\n    @property\n    def priors(self):\n        \"\"\"torch.Tensor: concatenated positive and negative priors\"\"\"\n        return cat_boxes([self.pos_priors, self.neg_priors])\n\n    @property\n    def bboxes(self):\n        \"\"\"torch.Tensor: concatenated positive and negative boxes\"\"\"\n        warnings.warn('DeprecationWarning: bboxes is deprecated, '\n                      'please use \"priors\" instead')\n        return self.priors\n\n    @property\n    def pos_bboxes(self):\n        warnings.warn('DeprecationWarning: pos_bboxes is deprecated, '\n                      'please use \"pos_priors\" instead')\n        return self.pos_priors\n\n    @property\n    def neg_bboxes(self):\n        warnings.warn('DeprecationWarning: neg_bboxes is deprecated, '\n                      'please use \"neg_priors\" instead')\n        return self.neg_priors\n\n    def to(self, device):\n        \"\"\"Change the device of the data inplace.\n\n        Example:\n            >>> self = SamplingResult.random()\n            >>> print(f'self = {self.to(None)}')\n            >>> # xdoctest: +REQUIRES(--gpu)\n            >>> print(f'self = {self.to(0)}')\n        \"\"\"\n        _dict = self.__dict__\n        for key, value in _dict.items():\n            if isinstance(value, (torch.Tensor, BaseBoxes)):\n                _dict[key] = value.to(device)\n        return self\n\n    def __nice__(self):\n        data = self.info.copy()\n        data['pos_priors'] = data.pop('pos_priors').shape\n        data['neg_priors'] = data.pop('neg_priors').shape\n        parts = [f\"'{k}': {v!r}\" for k, v in sorted(data.items())]\n        body = '    ' + ',\\n    '.join(parts)\n        return '{\\n' + body + '\\n}'\n\n    @property\n    def info(self):\n        \"\"\"Returns a dictionary of info about the object.\"\"\"\n        return {\n            'pos_inds': self.pos_inds,\n            'neg_inds': self.neg_inds,\n            'pos_priors': self.pos_priors,\n            'neg_priors': self.neg_priors,\n            'pos_is_gt': self.pos_is_gt,\n            'num_gts': self.num_gts,\n            'pos_assigned_gt_inds': self.pos_assigned_gt_inds,\n            'num_pos': self.num_pos,\n            'num_neg': self.num_neg,\n            'avg_factor': self.avg_factor\n        }\n\n    @classmethod\n    def random(cls, rng=None, **kwargs):\n        \"\"\"\n        Args:\n            rng (None | int | numpy.random.RandomState): seed or state.\n            kwargs (keyword arguments):\n                - num_preds: Number of predicted boxes.\n                - num_gts: Number of true boxes.\n                - p_ignore (float): Probability of a predicted box assigned to\n                    an ignored truth.\n                - p_assigned (float): probability of a predicted box not being\n                    assigned.\n\n        Returns:\n            :obj:`SamplingResult`: Randomly generated sampling result.\n\n        Example:\n            >>> from mmdet.models.task_modules.samplers.sampling_result import *  # NOQA\n            >>> self = SamplingResult.random()\n            >>> print(self.__dict__)\n        \"\"\"\n        from mmengine.structures import InstanceData\n\n        from mmdet.models.task_modules.assigners import AssignResult\n        from mmdet.models.task_modules.samplers import RandomSampler\n        rng = ensure_rng(rng)\n\n        # make probabilistic?\n        num = 32\n        pos_fraction = 0.5\n        neg_pos_ub = -1\n\n        assign_result = AssignResult.random(rng=rng, **kwargs)\n\n        # Note we could just compute an assignment\n        priors = random_boxes(assign_result.num_preds, rng=rng)\n        gt_bboxes = random_boxes(assign_result.num_gts, rng=rng)\n        gt_labels = torch.randint(\n            0, 5, (assign_result.num_gts, ), dtype=torch.long)\n\n        pred_instances = InstanceData()\n        pred_instances.priors = priors\n\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n\n        add_gt_as_proposals = True\n\n        sampler = RandomSampler(\n            num,\n            pos_fraction,\n            neg_pos_ub=neg_pos_ub,\n            add_gt_as_proposals=add_gt_as_proposals,\n            rng=rng)\n        self = sampler.sample(\n            assign_result=assign_result,\n            pred_instances=pred_instances,\n            gt_instances=gt_instances)\n        return self\n"
  },
  {
    "path": "mmdet/models/task_modules/samplers/score_hlr_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nimport torch\nfrom mmcv.ops import nms_match\nfrom mmengine.structures import InstanceData\nfrom numpy import ndarray\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import bbox2roi\nfrom ..assigners import AssignResult\nfrom .base_sampler import BaseSampler\nfrom .sampling_result import SamplingResult\n\n\n@TASK_UTILS.register_module()\nclass ScoreHLRSampler(BaseSampler):\n    r\"\"\"Importance-based Sample Reweighting (ISR_N), described in `Prime Sample\n    Attention in Object Detection <https://arxiv.org/abs/1904.04821>`_.\n\n    Score hierarchical local rank (HLR) differentiates with RandomSampler in\n    negative part. It firstly computes Score-HLR in a two-step way,\n    then linearly maps score hlr to the loss weights.\n\n    Args:\n        num (int): Total number of sampled RoIs.\n        pos_fraction (float): Fraction of positive samples.\n        context (:obj:`BaseRoIHead`): RoI head that the sampler belongs to.\n        neg_pos_ub (int): Upper bound of the ratio of num negative to num\n            positive, -1 means no upper bound. Defaults to -1.\n        add_gt_as_proposals (bool): Whether to add ground truth as proposals.\n            Defaults to True.\n        k (float): Power of the non-linear mapping. Defaults to 0.5\n        bias (float): Shift of the non-linear mapping. Defaults to 0.\n        score_thr (float): Minimum score that a negative sample is to be\n            considered as valid bbox. Defaults to 0.05.\n        iou_thr (float): IoU threshold for NMS match. Defaults to 0.5.\n    \"\"\"\n\n    def __init__(self,\n                 num: int,\n                 pos_fraction: float,\n                 context,\n                 neg_pos_ub: int = -1,\n                 add_gt_as_proposals: bool = True,\n                 k: float = 0.5,\n                 bias: float = 0,\n                 score_thr: float = 0.05,\n                 iou_thr: float = 0.5,\n                 **kwargs) -> None:\n        super().__init__(\n            num=num,\n            pos_fraction=pos_fraction,\n            neg_pos_ub=neg_pos_ub,\n            add_gt_as_proposals=add_gt_as_proposals)\n        self.k = k\n        self.bias = bias\n        self.score_thr = score_thr\n        self.iou_thr = iou_thr\n        self.context = context\n        # context of cascade detectors is a list, so distinguish them here.\n        if not hasattr(context, 'num_stages'):\n            self.bbox_roi_extractor = context.bbox_roi_extractor\n            self.bbox_head = context.bbox_head\n            self.with_shared_head = context.with_shared_head\n            if self.with_shared_head:\n                self.shared_head = context.shared_head\n        else:\n            self.bbox_roi_extractor = context.bbox_roi_extractor[\n                context.current_stage]\n            self.bbox_head = context.bbox_head[context.current_stage]\n\n    @staticmethod\n    def random_choice(gallery: Union[Tensor, ndarray, list],\n                      num: int) -> Union[Tensor, ndarray]:\n        \"\"\"Randomly select some elements from the gallery.\n\n        If `gallery` is a Tensor, the returned indices will be a Tensor;\n        If `gallery` is a ndarray or list, the returned indices will be a\n        ndarray.\n\n        Args:\n            gallery (Tensor or ndarray or list): indices pool.\n            num (int): expected sample num.\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        assert len(gallery) >= num\n\n        is_tensor = isinstance(gallery, torch.Tensor)\n        if not is_tensor:\n            if torch.cuda.is_available():\n                device = torch.cuda.current_device()\n            else:\n                device = 'cpu'\n            gallery = torch.tensor(gallery, dtype=torch.long, device=device)\n        perm = torch.randperm(gallery.numel(), device=gallery.device)[:num]\n        rand_inds = gallery[perm]\n        if not is_tensor:\n            rand_inds = rand_inds.cpu().numpy()\n        return rand_inds\n\n    def _sample_pos(self, assign_result: AssignResult, num_expected: int,\n                    **kwargs) -> Union[Tensor, ndarray]:\n        \"\"\"Randomly sample some positive samples.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Bbox assigning results.\n            num_expected (int): The number of expected positive samples\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        pos_inds = torch.nonzero(assign_result.gt_inds > 0).flatten()\n        if pos_inds.numel() <= num_expected:\n            return pos_inds\n        else:\n            return self.random_choice(pos_inds, num_expected)\n\n    def _sample_neg(self, assign_result: AssignResult, num_expected: int,\n                    bboxes: Tensor, feats: Tensor,\n                    **kwargs) -> Union[Tensor, ndarray]:\n        \"\"\"Sample negative samples.\n\n        Score-HLR sampler is done in the following steps:\n        1. Take the maximum positive score prediction of each negative samples\n            as s_i.\n        2. Filter out negative samples whose s_i <= score_thr, the left samples\n            are called valid samples.\n        3. Use NMS-Match to divide valid samples into different groups,\n            samples in the same group will greatly overlap with each other\n        4. Rank the matched samples in two-steps to get Score-HLR.\n            (1) In the same group, rank samples with their scores.\n            (2) In the same score rank across different groups,\n                rank samples with their scores again.\n        5. Linearly map Score-HLR to the final label weights.\n\n        Args:\n            assign_result (:obj:`AssignResult`): result of assigner.\n            num_expected (int): Expected number of samples.\n            bboxes (Tensor): bbox to be sampled.\n            feats (Tensor): Features come from FPN.\n\n        Returns:\n            Tensor or ndarray: sampled indices.\n        \"\"\"\n        neg_inds = torch.nonzero(assign_result.gt_inds == 0).flatten()\n        num_neg = neg_inds.size(0)\n        if num_neg == 0:\n            return neg_inds, None\n        with torch.no_grad():\n            neg_bboxes = bboxes[neg_inds]\n            neg_rois = bbox2roi([neg_bboxes])\n            bbox_result = self.context._bbox_forward(feats, neg_rois)\n            cls_score, bbox_pred = bbox_result['cls_score'], bbox_result[\n                'bbox_pred']\n\n            ori_loss = self.bbox_head.loss(\n                cls_score=cls_score,\n                bbox_pred=None,\n                rois=None,\n                labels=neg_inds.new_full((num_neg, ),\n                                         self.bbox_head.num_classes),\n                label_weights=cls_score.new_ones(num_neg),\n                bbox_targets=None,\n                bbox_weights=None,\n                reduction_override='none')['loss_cls']\n\n            # filter out samples with the max score lower than score_thr\n            max_score, argmax_score = cls_score.softmax(-1)[:, :-1].max(-1)\n            valid_inds = (max_score > self.score_thr).nonzero().view(-1)\n            invalid_inds = (max_score <= self.score_thr).nonzero().view(-1)\n            num_valid = valid_inds.size(0)\n            num_invalid = invalid_inds.size(0)\n\n            num_expected = min(num_neg, num_expected)\n            num_hlr = min(num_valid, num_expected)\n            num_rand = num_expected - num_hlr\n            if num_valid > 0:\n                valid_rois = neg_rois[valid_inds]\n                valid_max_score = max_score[valid_inds]\n                valid_argmax_score = argmax_score[valid_inds]\n                valid_bbox_pred = bbox_pred[valid_inds]\n\n                # valid_bbox_pred shape: [num_valid, #num_classes, 4]\n                valid_bbox_pred = valid_bbox_pred.view(\n                    valid_bbox_pred.size(0), -1, 4)\n                selected_bbox_pred = valid_bbox_pred[range(num_valid),\n                                                     valid_argmax_score]\n                pred_bboxes = self.bbox_head.bbox_coder.decode(\n                    valid_rois[:, 1:], selected_bbox_pred)\n                pred_bboxes_with_score = torch.cat(\n                    [pred_bboxes, valid_max_score[:, None]], -1)\n                group = nms_match(pred_bboxes_with_score, self.iou_thr)\n\n                # imp: importance\n                imp = cls_score.new_zeros(num_valid)\n                for g in group:\n                    g_score = valid_max_score[g]\n                    # g_score has already sorted\n                    rank = g_score.new_tensor(range(g_score.size(0)))\n                    imp[g] = num_valid - rank + g_score\n                _, imp_rank_inds = imp.sort(descending=True)\n                _, imp_rank = imp_rank_inds.sort()\n                hlr_inds = imp_rank_inds[:num_expected]\n\n                if num_rand > 0:\n                    rand_inds = torch.randperm(num_invalid)[:num_rand]\n                    select_inds = torch.cat(\n                        [valid_inds[hlr_inds], invalid_inds[rand_inds]])\n                else:\n                    select_inds = valid_inds[hlr_inds]\n\n                neg_label_weights = cls_score.new_ones(num_expected)\n\n                up_bound = max(num_expected, num_valid)\n                imp_weights = (up_bound -\n                               imp_rank[hlr_inds].float()) / up_bound\n                neg_label_weights[:num_hlr] = imp_weights\n                neg_label_weights[num_hlr:] = imp_weights.min()\n                neg_label_weights = (self.bias +\n                                     (1 - self.bias) * neg_label_weights).pow(\n                                         self.k)\n                ori_selected_loss = ori_loss[select_inds]\n                new_loss = ori_selected_loss * neg_label_weights\n                norm_ratio = ori_selected_loss.sum() / new_loss.sum()\n                neg_label_weights *= norm_ratio\n            else:\n                neg_label_weights = cls_score.new_ones(num_expected)\n                select_inds = torch.randperm(num_neg)[:num_expected]\n\n            return neg_inds[select_inds], neg_label_weights\n\n    def sample(self, assign_result: AssignResult, pred_instances: InstanceData,\n               gt_instances: InstanceData, **kwargs) -> SamplingResult:\n        \"\"\"Sample positive and negative bboxes.\n\n        This is a simple implementation of bbox sampling given candidates,\n        assigning results and ground truth bboxes.\n\n        Args:\n            assign_result (:obj:`AssignResult`): Assigning results.\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n\n        Returns:\n            :obj:`SamplingResult`: Sampling result.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n        gt_labels = gt_instances.labels\n\n        gt_flags = priors.new_zeros((priors.shape[0], ), dtype=torch.uint8)\n        if self.add_gt_as_proposals and len(gt_bboxes) > 0:\n            priors = torch.cat([gt_bboxes, priors], dim=0)\n            assign_result.add_gt_(gt_labels)\n            gt_ones = priors.new_ones(gt_bboxes.shape[0], dtype=torch.uint8)\n            gt_flags = torch.cat([gt_ones, gt_flags])\n\n        num_expected_pos = int(self.num * self.pos_fraction)\n        pos_inds = self.pos_sampler._sample_pos(\n            assign_result, num_expected_pos, bboxes=priors, **kwargs)\n        num_sampled_pos = pos_inds.numel()\n        num_expected_neg = self.num - num_sampled_pos\n        if self.neg_pos_ub >= 0:\n            _pos = max(1, num_sampled_pos)\n            neg_upper_bound = int(self.neg_pos_ub * _pos)\n            if num_expected_neg > neg_upper_bound:\n                num_expected_neg = neg_upper_bound\n        neg_inds, neg_label_weights = self.neg_sampler._sample_neg(\n            assign_result, num_expected_neg, bboxes=priors, **kwargs)\n\n        sampling_result = SamplingResult(\n            pos_inds=pos_inds,\n            neg_inds=neg_inds,\n            priors=priors,\n            gt_bboxes=gt_bboxes,\n            assign_result=assign_result,\n            gt_flags=gt_flags)\n        return sampling_result, neg_label_weights\n"
  },
  {
    "path": "mmdet/models/task_modules/tracking/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .aflink import AppearanceFreeLink\nfrom .camera_motion_compensation import CameraMotionCompensation\nfrom .interpolation import InterpolateTracklets\nfrom .kalman_filter import KalmanFilter\nfrom .similarity import embed_similarity\n\n__all__ = [\n    'KalmanFilter', 'InterpolateTracklets', 'embed_similarity',\n    'AppearanceFreeLink', 'CameraMotionCompensation'\n]\n"
  },
  {
    "path": "mmdet/models/task_modules/tracking/aflink.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom collections import defaultdict\nfrom typing import Tuple\n\nimport numpy as np\nimport torch\nfrom mmengine.model import BaseModule\nfrom mmengine.runner.checkpoint import load_checkpoint\nfrom scipy.optimize import linear_sum_assignment\nfrom torch import Tensor, nn\n\nfrom mmdet.registry import TASK_UTILS\n\nINFINITY = 1e5\n\n\nclass TemporalBlock(BaseModule):\n    \"\"\"The temporal block of AFLink model.\n\n    Args:\n        in_channel (int): the dimension of the input channels.\n        out_channel (int): the dimension of the output channels.\n    \"\"\"\n\n    def __init__(self,\n                 in_channel: int,\n                 out_channel: int,\n                 kernel_size: tuple = (7, 1)):\n        super(TemporalBlock, self).__init__()\n        self.conv = nn.Conv2d(in_channel, out_channel, kernel_size, bias=False)\n        self.relu = nn.ReLU(inplace=True)\n        self.bnf = nn.BatchNorm1d(out_channel)\n        self.bnx = nn.BatchNorm1d(out_channel)\n        self.bny = nn.BatchNorm1d(out_channel)\n\n    def bn(self, x: Tensor) -> Tensor:\n        x[:, :, :, 0] = self.bnf(x[:, :, :, 0])\n        x[:, :, :, 1] = self.bnx(x[:, :, :, 1])\n        x[:, :, :, 2] = self.bny(x[:, :, :, 2])\n        return x\n\n    def forward(self, x: Tensor) -> Tensor:\n        x = self.conv(x)\n        x = self.bn(x)\n        x = self.relu(x)\n        return x\n\n\nclass FusionBlock(BaseModule):\n    \"\"\"The fusion block of AFLink model.\n\n    Args:\n        in_channel (int): the dimension of the input channels.\n        out_channel (int): the dimension of the output channels.\n    \"\"\"\n\n    def __init__(self, in_channel: int, out_channel: int):\n        super(FusionBlock, self).__init__()\n        self.conv = nn.Conv2d(in_channel, out_channel, (1, 3), bias=False)\n        self.bn = nn.BatchNorm2d(out_channel)\n        self.relu = nn.ReLU(inplace=True)\n\n    def forward(self, x: Tensor) -> Tensor:\n        x = self.conv(x)\n        x = self.bn(x)\n        x = self.relu(x)\n        return x\n\n\nclass Classifier(BaseModule):\n    \"\"\"The classifier of AFLink model.\n\n    Args:\n        in_channel (int): the dimension of the input channels.\n    \"\"\"\n\n    def __init__(self, in_channel: int, out_channel: int):\n        super(Classifier, self).__init__()\n        self.fc1 = nn.Linear(in_channel * 2, in_channel // 2)\n        self.relu = nn.ReLU(inplace=True)\n        self.fc2 = nn.Linear(in_channel // 2, out_channel)\n\n    def forward(self, x1: Tensor, x2: Tensor) -> Tensor:\n        x = torch.cat((x1, x2), dim=1)\n        x = self.fc1(x)\n        x = self.relu(x)\n        x = self.fc2(x)\n        return x\n\n\nclass AFLinkModel(BaseModule):\n    \"\"\"Appearance-Free Link Model.\"\"\"\n\n    def __init__(self,\n                 temporal_module_channels: list = [1, 32, 64, 128, 256],\n                 fusion_module_channels: list = [256, 256],\n                 classifier_channels: list = [256, 2]):\n        super(AFLinkModel, self).__init__()\n        self.TemporalModule_1 = nn.Sequential(*[\n            TemporalBlock(temporal_module_channels[i],\n                          temporal_module_channels[i + 1])\n            for i in range(len(temporal_module_channels) - 1)\n        ])\n\n        self.TemporalModule_2 = nn.Sequential(*[\n            TemporalBlock(temporal_module_channels[i],\n                          temporal_module_channels[i + 1])\n            for i in range(len(temporal_module_channels) - 1)\n        ])\n\n        self.FusionBlock_1 = FusionBlock(*fusion_module_channels)\n        self.FusionBlock_2 = FusionBlock(*fusion_module_channels)\n\n        self.pooling = nn.AdaptiveAvgPool2d((1, 1))\n        self.classifier = Classifier(*classifier_channels)\n\n    def forward(self, x1: Tensor, x2: Tensor) -> Tensor:\n        assert not self.training, 'Only testing is supported for AFLink.'\n        x1 = x1[:, :, :, :3]\n        x2 = x2[:, :, :, :3]\n        x1 = self.TemporalModule_1(x1)  # [B,1,30,3] -> [B,256,6,3]\n        x2 = self.TemporalModule_2(x2)\n        x1 = self.FusionBlock_1(x1)\n        x2 = self.FusionBlock_2(x2)\n        x1 = self.pooling(x1).squeeze(-1).squeeze(-1)\n        x2 = self.pooling(x2).squeeze(-1).squeeze(-1)\n        y = self.classifier(x1, x2)\n        y = torch.softmax(y, dim=1)[0, 1]\n        return y\n\n\n@TASK_UTILS.register_module()\nclass AppearanceFreeLink(BaseModule):\n    \"\"\"Appearance-Free Link method.\n\n    This method is proposed in\n    \"StrongSORT: Make DeepSORT Great Again\"\n    `StrongSORT<https://arxiv.org/abs/2202.13514>`_.\n\n    Args:\n        checkpoint (str): Checkpoint path.\n        temporal_threshold (tuple, optional): The temporal constraint\n            for tracklets association. Defaults to (0, 30).\n        spatial_threshold (int, optional): The spatial constraint for\n            tracklets association. Defaults to 75.\n        confidence_threshold (float, optional): The minimum confidence\n            threshold for tracklets association. Defaults to 0.95.\n    \"\"\"\n\n    def __init__(self,\n                 checkpoint: str,\n                 temporal_threshold: tuple = (0, 30),\n                 spatial_threshold: int = 75,\n                 confidence_threshold: float = 0.95):\n        super(AppearanceFreeLink, self).__init__()\n        self.temporal_threshold = temporal_threshold\n        self.spatial_threshold = spatial_threshold\n        self.confidence_threshold = confidence_threshold\n\n        self.model = AFLinkModel()\n        if checkpoint:\n            load_checkpoint(self.model, checkpoint)\n        if torch.cuda.is_available():\n            self.model.cuda()\n        self.model.eval()\n\n        self.device = next(self.model.parameters()).device\n        self.fn_l2 = lambda x, y: np.sqrt(x**2 + y**2)\n\n    def data_transform(self,\n                       track1: np.ndarray,\n                       track2: np.ndarray,\n                       length: int = 30) -> Tuple[np.ndarray]:\n        \"\"\"Data Transformation. This is used to standardize the length of\n        tracks to a unified length. Then perform min-max normalization to the\n        motion embeddings.\n\n        Args:\n            track1 (ndarray): the first track with shape (N,C).\n            track2 (ndarray): the second track with shape (M,C).\n            length (int): the unified length of tracks. Defaults to 30.\n\n        Returns:\n            Tuple[ndarray]: the transformed track1 and track2.\n        \"\"\"\n        # fill or cut track1\n        length_1 = track1.shape[0]\n        track1 = track1[-length:] if length_1 >= length else \\\n            np.pad(track1, ((length - length_1, 0), (0, 0)))\n\n        # fill or cut track1\n        length_2 = track2.shape[0]\n        track2 = track2[:length] if length_2 >= length else \\\n            np.pad(track2, ((0, length - length_2), (0, 0)))\n\n        # min-max normalization\n        min_ = np.concatenate((track1, track2), axis=0).min(axis=0)\n        max_ = np.concatenate((track1, track2), axis=0).max(axis=0)\n        subtractor = (max_ + min_) / 2\n        divisor = (max_ - min_) / 2 + 1e-5\n        track1 = (track1 - subtractor) / divisor\n        track2 = (track2 - subtractor) / divisor\n\n        return track1, track2\n\n    def forward(self, pred_tracks: np.ndarray) -> np.ndarray:\n        \"\"\"Forward function.\n\n        pred_tracks (ndarray): With shape (N, 7). Each row denotes\n            (frame_id, track_id, x1, y1, x2, y2, score).\n\n        Returns:\n            ndarray: The linked tracks with shape (N, 7). Each row denotes\n                (frame_id, track_id, x1, y1, x2, y2, score)\n        \"\"\"\n        # sort tracks by the frame id\n        pred_tracks = pred_tracks[np.argsort(pred_tracks[:, 0])]\n\n        # gather tracks information\n        id2info = defaultdict(list)\n        for row in pred_tracks:\n            frame_id, track_id, x1, y1, x2, y2 = row[:6]\n            id2info[track_id].append([frame_id, x1, y1, x2 - x1, y2 - y1])\n        id2info = {k: np.array(v) for k, v in id2info.items()}\n        num_track = len(id2info)\n        track_ids = np.array(list(id2info))\n        cost_matrix = np.full((num_track, num_track), INFINITY)\n\n        # compute the cost matrix\n        for i, id_i in enumerate(track_ids):\n            for j, id_j in enumerate(track_ids):\n                if id_i == id_j:\n                    continue\n                info_i, info_j = id2info[id_i], id2info[id_j]\n                frame_i, box_i = info_i[-1][0], info_i[-1][1:3]\n                frame_j, box_j = info_j[0][0], info_j[0][1:3]\n                # temporal constraint\n                if not self.temporal_threshold[0] <= \\\n                        frame_j - frame_i <= self.temporal_threshold[1]:\n                    continue\n                # spatial constraint\n                if self.fn_l2(box_i[0] - box_j[0], box_i[1] - box_j[1]) \\\n                        > self.spatial_threshold:\n                    continue\n                # confidence constraint\n                track_i, track_j = self.data_transform(info_i, info_j)\n\n                # numpy to torch\n                track_i = torch.tensor(\n                    track_i, dtype=torch.float).to(self.device)\n                track_j = torch.tensor(\n                    track_j, dtype=torch.float).to(self.device)\n                track_i = track_i.unsqueeze(0).unsqueeze(0)\n                track_j = track_j.unsqueeze(0).unsqueeze(0)\n\n                confidence = self.model(track_i,\n                                        track_j).detach().cpu().numpy()\n                if confidence >= self.confidence_threshold:\n                    cost_matrix[i, j] = 1 - confidence\n\n        # linear assignment\n        indices = linear_sum_assignment(cost_matrix)\n        _id2id = dict()  # the temporary assignment results\n        id2id = dict()  # the final assignment results\n        for i, j in zip(indices[0], indices[1]):\n            if cost_matrix[i, j] < INFINITY:\n                _id2id[i] = j\n        for k, v in _id2id.items():\n            if k in id2id:\n                id2id[v] = id2id[k]\n            else:\n                id2id[v] = k\n\n        # link\n        for k, v in id2id.items():\n            pred_tracks[pred_tracks[:, 1] == k, 1] = v\n\n        # deduplicate\n        _, index = np.unique(pred_tracks[:, :2], return_index=True, axis=0)\n\n        return pred_tracks[index]\n"
  },
  {
    "path": "mmdet/models/task_modules/tracking/camera_motion_compensation.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport cv2\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import bbox_cxcyah_to_xyxy, bbox_xyxy_to_cxcyah\n\n\n@TASK_UTILS.register_module()\nclass CameraMotionCompensation:\n    \"\"\"Camera motion compensation.\n\n    Args:\n        warp_mode (str): Warp mode in opencv.\n            Defaults to 'cv2.MOTION_EUCLIDEAN'.\n        num_iters (int): Number of the iterations. Defaults to 50.\n        stop_eps (float): Terminate threshold. Defaults to 0.001.\n    \"\"\"\n\n    def __init__(self,\n                 warp_mode: str = 'cv2.MOTION_EUCLIDEAN',\n                 num_iters: int = 50,\n                 stop_eps: float = 0.001):\n        self.warp_mode = eval(warp_mode)\n        self.num_iters = num_iters\n        self.stop_eps = stop_eps\n\n    def get_warp_matrix(self, img: np.ndarray, ref_img: np.ndarray) -> Tensor:\n        \"\"\"Calculate warping matrix between two images.\"\"\"\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n        ref_img = cv2.cvtColor(ref_img, cv2.COLOR_BGR2GRAY)\n\n        warp_matrix = np.eye(2, 3, dtype=np.float32)\n        criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,\n                    self.num_iters, self.stop_eps)\n        cc, warp_matrix = cv2.findTransformECC(img, ref_img, warp_matrix,\n                                               self.warp_mode, criteria, None,\n                                               1)\n        warp_matrix = torch.from_numpy(warp_matrix)\n        return warp_matrix\n\n    def warp_bboxes(self, bboxes: Tensor, warp_matrix: Tensor) -> Tensor:\n        \"\"\"Warp bounding boxes according to the warping matrix.\"\"\"\n        tl, br = bboxes[:, :2], bboxes[:, 2:]\n        tl = torch.cat((tl, torch.ones(tl.shape[0], 1).to(bboxes.device)),\n                       dim=1)\n        br = torch.cat((br, torch.ones(tl.shape[0], 1).to(bboxes.device)),\n                       dim=1)\n        trans_tl = torch.mm(warp_matrix, tl.t()).t()\n        trans_br = torch.mm(warp_matrix, br.t()).t()\n        trans_bboxes = torch.cat((trans_tl, trans_br), dim=1)\n        return trans_bboxes.to(bboxes.device)\n\n    def warp_means(self, means: np.ndarray, warp_matrix: Tensor) -> np.ndarray:\n        \"\"\"Warp track.mean according to the warping matrix.\"\"\"\n        cxcyah = torch.from_numpy(means[:, :4]).float()\n        xyxy = bbox_cxcyah_to_xyxy(cxcyah)\n        warped_xyxy = self.warp_bboxes(xyxy, warp_matrix)\n        warped_cxcyah = bbox_xyxy_to_cxcyah(warped_xyxy).numpy()\n        means[:, :4] = warped_cxcyah\n        return means\n\n    def track(self, img: Tensor, ref_img: Tensor, tracks: dict,\n              num_samples: int, frame_id: int, metainfo: dict) -> dict:\n        \"\"\"Tracking forward.\"\"\"\n        img = img.squeeze(0).cpu().numpy().transpose((1, 2, 0))\n        ref_img = ref_img.squeeze(0).cpu().numpy().transpose((1, 2, 0))\n        warp_matrix = self.get_warp_matrix(img, ref_img)\n\n        # rescale the warp_matrix due to the `resize` in pipeline\n        scale_factor_h, scale_factor_w = metainfo['scale_factor']\n        warp_matrix[0, 2] = warp_matrix[0, 2] / scale_factor_w\n        warp_matrix[1, 2] = warp_matrix[1, 2] / scale_factor_h\n\n        bboxes = []\n        num_bboxes = []\n        means = []\n        for k, v in tracks.items():\n            if int(v['frame_ids'][-1]) < frame_id - 1:\n                _num = 1\n            else:\n                _num = min(num_samples, len(v.bboxes))\n            num_bboxes.append(_num)\n            bboxes.extend(v.bboxes[-_num:])\n            if len(v.mean) > 0:\n                means.append(v.mean)\n        bboxes = torch.cat(bboxes, dim=0)\n        warped_bboxes = self.warp_bboxes(bboxes, warp_matrix.to(bboxes.device))\n\n        warped_bboxes = torch.split(warped_bboxes, num_bboxes)\n        for b, (k, v) in zip(warped_bboxes, tracks.items()):\n            _num = b.shape[0]\n            b = torch.split(b, [1] * _num)\n            tracks[k].bboxes[-_num:] = b\n\n        if means:\n            means = np.asarray(means)\n            warped_means = self.warp_means(means, warp_matrix)\n            for m, (k, v) in zip(warped_means, tracks.items()):\n                tracks[k].mean = m\n\n        return tracks\n"
  },
  {
    "path": "mmdet/models/task_modules/tracking/interpolation.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\n\ntry:\n    from sklearn.gaussian_process import GaussianProcessRegressor as GPR\n    from sklearn.gaussian_process.kernels import RBF\n    HAS_SKIKIT_LEARN = True\nexcept ImportError:\n    HAS_SKIKIT_LEARN = False\n\nfrom mmdet.registry import TASK_UTILS\n\n\n@TASK_UTILS.register_module()\nclass InterpolateTracklets:\n    \"\"\"Interpolate tracks to make tracks more complete.\n\n    Args:\n        min_num_frames (int, optional): The minimum length of a track that will\n            be interpolated. Defaults to 5.\n        max_num_frames (int, optional): The maximum disconnected length in\n            a track. Defaults to 20.\n        use_gsi (bool, optional): Whether to use the GSI (Gaussian-smoothed\n            interpolation) method. Defaults to False.\n        smooth_tau (int, optional): smoothing parameter in GSI. Defaults to 10.\n    \"\"\"\n\n    def __init__(self,\n                 min_num_frames: int = 5,\n                 max_num_frames: int = 20,\n                 use_gsi: bool = False,\n                 smooth_tau: int = 10):\n        if not HAS_SKIKIT_LEARN:\n            raise RuntimeError('sscikit-learn is not installed,\\\n                 please install it by: pip install scikit-learn')\n        self.min_num_frames = min_num_frames\n        self.max_num_frames = max_num_frames\n        self.use_gsi = use_gsi\n        self.smooth_tau = smooth_tau\n\n    def _interpolate_track(self,\n                           track: np.ndarray,\n                           track_id: int,\n                           max_num_frames: int = 20) -> np.ndarray:\n        \"\"\"Interpolate a track linearly to make the track more complete.\n\n        This function is proposed in\n        \"ByteTrack: Multi-Object Tracking by Associating Every Detection Box.\"\n        `ByteTrack<https://arxiv.org/abs/2110.06864>`_.\n\n        Args:\n            track (ndarray): With shape (N, 7). Each row denotes\n                (frame_id, track_id, x1, y1, x2, y2, score).\n            max_num_frames (int, optional): The maximum disconnected length in\n                the track. Defaults to 20.\n\n        Returns:\n            ndarray: The interpolated track with shape (N, 7). Each row denotes\n                (frame_id, track_id, x1, y1, x2, y2, score)\n        \"\"\"\n        assert (track[:, 1] == track_id).all(), \\\n            'The track id should not changed when interpolate a track.'\n\n        frame_ids = track[:, 0]\n        interpolated_track = np.zeros((0, 7))\n        # perform interpolation for the disconnected frames in the track.\n        for i in np.where(np.diff(frame_ids) > 1)[0]:\n            left_frame_id = frame_ids[i]\n            right_frame_id = frame_ids[i + 1]\n            num_disconnected_frames = int(right_frame_id - left_frame_id)\n\n            if 1 < num_disconnected_frames < max_num_frames:\n                left_bbox = track[i, 2:6]\n                right_bbox = track[i + 1, 2:6]\n\n                # perform interpolation for two adjacent tracklets.\n                for j in range(1, num_disconnected_frames):\n                    cur_bbox = j / (num_disconnected_frames) * (\n                        right_bbox - left_bbox) + left_bbox\n                    cur_result = np.ones((7, ))\n                    cur_result[0] = j + left_frame_id\n                    cur_result[1] = track_id\n                    cur_result[2:6] = cur_bbox\n\n                    interpolated_track = np.concatenate(\n                        (interpolated_track, cur_result[None]), axis=0)\n\n        interpolated_track = np.concatenate((track, interpolated_track),\n                                            axis=0)\n        return interpolated_track\n\n    def gaussian_smoothed_interpolation(self,\n                                        track: np.ndarray,\n                                        smooth_tau: int = 10) -> np.ndarray:\n        \"\"\"Gaussian-Smoothed Interpolation.\n\n        This function is proposed in\n        \"StrongSORT: Make DeepSORT Great Again\"\n        `StrongSORT<https://arxiv.org/abs/2202.13514>`_.\n\n        Args:\n            track (ndarray): With shape (N, 7). Each row denotes\n                (frame_id, track_id, x1, y1, x2, y2, score).\n            smooth_tau (int, optional): smoothing parameter in GSI.\n                Defaults to 10.\n\n        Returns:\n            ndarray: The interpolated tracks with shape (N, 7). Each row\n                denotes (frame_id, track_id, x1, y1, x2, y2, score)\n        \"\"\"\n        len_scale = np.clip(smooth_tau * np.log(smooth_tau**3 / len(track)),\n                            smooth_tau**-1, smooth_tau**2)\n        gpr = GPR(RBF(len_scale, 'fixed'))\n        t = track[:, 0].reshape(-1, 1)\n        x1 = track[:, 2].reshape(-1, 1)\n        y1 = track[:, 3].reshape(-1, 1)\n        x2 = track[:, 4].reshape(-1, 1)\n        y2 = track[:, 5].reshape(-1, 1)\n        gpr.fit(t, x1)\n        x1_gpr = gpr.predict(t)\n        gpr.fit(t, y1)\n        y1_gpr = gpr.predict(t)\n        gpr.fit(t, x2)\n        x2_gpr = gpr.predict(t)\n        gpr.fit(t, y2)\n        y2_gpr = gpr.predict(t)\n        gsi_track = [[\n            t[i, 0], track[i, 1], x1_gpr[i], y1_gpr[i], x2_gpr[i], y2_gpr[i],\n            track[i, 6]\n        ] for i in range(len(t))]\n        return np.array(gsi_track)\n\n    def forward(self, pred_tracks: np.ndarray) -> np.ndarray:\n        \"\"\"Forward function.\n\n        pred_tracks (ndarray): With shape (N, 7). Each row denotes\n            (frame_id, track_id, x1, y1, x2, y2, score).\n\n        Returns:\n            ndarray: The interpolated tracks with shape (N, 7). Each row\n            denotes (frame_id, track_id, x1, y1, x2, y2, score).\n        \"\"\"\n        max_track_id = int(np.max(pred_tracks[:, 1]))\n        min_track_id = int(np.min(pred_tracks[:, 1]))\n\n        # perform interpolation for each track\n        interpolated_tracks = []\n        for track_id in range(min_track_id, max_track_id + 1):\n            inds = pred_tracks[:, 1] == track_id\n            track = pred_tracks[inds]\n            num_frames = len(track)\n            if num_frames <= 2:\n                continue\n\n            if num_frames > self.min_num_frames:\n                interpolated_track = self._interpolate_track(\n                    track, track_id, self.max_num_frames)\n            else:\n                interpolated_track = track\n\n            if self.use_gsi:\n                interpolated_track = self.gaussian_smoothed_interpolation(\n                    interpolated_track, self.smooth_tau)\n\n            interpolated_tracks.append(interpolated_track)\n\n        interpolated_tracks = np.concatenate(interpolated_tracks)\n        return interpolated_tracks[interpolated_tracks[:, 0].argsort()]\n"
  },
  {
    "path": "mmdet/models/task_modules/tracking/kalman_filter.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport numpy as np\nimport torch\n\ntry:\n    import scipy.linalg\n    HAS_SCIPY = True\nexcept ImportError:\n    HAS_SCIPY = False\n\nfrom mmdet.registry import TASK_UTILS\n\n\n@TASK_UTILS.register_module()\nclass KalmanFilter:\n    \"\"\"A simple Kalman filter for tracking bounding boxes in image space.\n\n    The implementation is referred to https://github.com/nwojke/deep_sort.\n\n    Args:\n        center_only (bool): If True, distance computation is done with\n            respect to the bounding box center position only.\n            Defaults to False.\n        use_nsa (bool): Whether to use the NSA (Noise Scale Adaptive) Kalman\n            Filter, which adaptively modulates the noise scale according to\n            the quality of detections. More details in\n            https://arxiv.org/abs/2202.11983. Defaults to False.\n    \"\"\"\n    chi2inv95 = {\n        1: 3.8415,\n        2: 5.9915,\n        3: 7.8147,\n        4: 9.4877,\n        5: 11.070,\n        6: 12.592,\n        7: 14.067,\n        8: 15.507,\n        9: 16.919\n    }\n\n    def __init__(self, center_only: bool = False, use_nsa: bool = False):\n        if not HAS_SCIPY:\n            raise RuntimeError('sscikit-learn is not installed,\\\n                 please install it by: pip install scikit-learn')\n        self.center_only = center_only\n        if self.center_only:\n            self.gating_threshold = self.chi2inv95[2]\n        else:\n            self.gating_threshold = self.chi2inv95[4]\n\n        self.use_nsa = use_nsa\n        ndim, dt = 4, 1.\n\n        # Create Kalman filter model matrices.\n        self._motion_mat = np.eye(2 * ndim, 2 * ndim)\n        for i in range(ndim):\n            self._motion_mat[i, ndim + i] = dt\n        self._update_mat = np.eye(ndim, 2 * ndim)\n\n        # Motion and observation uncertainty are chosen relative to the current\n        # state estimate. These weights control the amount of uncertainty in\n        # the model. This is a bit hacky.\n        self._std_weight_position = 1. / 20\n        self._std_weight_velocity = 1. / 160\n\n    def initiate(self, measurement: np.array) -> Tuple[np.array, np.array]:\n        \"\"\"Create track from unassociated measurement.\n\n        Args:\n            measurement (ndarray):  Bounding box coordinates (x, y, a, h) with\n            center position (x, y), aspect ratio a, and height h.\n\n        Returns:\n             (ndarray, ndarray): Returns the mean vector (8 dimensional) and\n                covariance matrix (8x8 dimensional) of the new track.\n                Unobserved velocities are initialized to 0 mean.\n        \"\"\"\n        mean_pos = measurement\n        mean_vel = np.zeros_like(mean_pos)\n        mean = np.r_[mean_pos, mean_vel]\n\n        std = [\n            2 * self._std_weight_position * measurement[3],\n            2 * self._std_weight_position * measurement[3], 1e-2,\n            2 * self._std_weight_position * measurement[3],\n            10 * self._std_weight_velocity * measurement[3],\n            10 * self._std_weight_velocity * measurement[3], 1e-5,\n            10 * self._std_weight_velocity * measurement[3]\n        ]\n        covariance = np.diag(np.square(std))\n        return mean, covariance\n\n    def predict(self, mean: np.array,\n                covariance: np.array) -> Tuple[np.array, np.array]:\n        \"\"\"Run Kalman filter prediction step.\n\n        Args:\n            mean (ndarray): The 8 dimensional mean vector of the object\n                state at the previous time step.\n\n            covariance (ndarray): The 8x8 dimensional covariance matrix\n                of the object state at the previous time step.\n\n        Returns:\n            (ndarray, ndarray): Returns the mean vector and covariance\n                matrix of the predicted state. Unobserved velocities are\n                initialized to 0 mean.\n        \"\"\"\n        std_pos = [\n            self._std_weight_position * mean[3],\n            self._std_weight_position * mean[3], 1e-2,\n            self._std_weight_position * mean[3]\n        ]\n        std_vel = [\n            self._std_weight_velocity * mean[3],\n            self._std_weight_velocity * mean[3], 1e-5,\n            self._std_weight_velocity * mean[3]\n        ]\n        motion_cov = np.diag(np.square(np.r_[std_pos, std_vel]))\n\n        mean = np.dot(self._motion_mat, mean)\n        covariance = np.linalg.multi_dot(\n            (self._motion_mat, covariance, self._motion_mat.T)) + motion_cov\n\n        return mean, covariance\n\n    def project(self,\n                mean: np.array,\n                covariance: np.array,\n                bbox_score: float = 0.) -> Tuple[np.array, np.array]:\n        \"\"\"Project state distribution to measurement space.\n\n        Args:\n            mean (ndarray): The state's mean vector (8 dimensional array).\n            covariance (ndarray): The state's covariance matrix (8x8\n                dimensional).\n            bbox_score (float): The confidence score of the bbox.\n                Defaults to 0.\n\n        Returns:\n            (ndarray, ndarray):  Returns the projected mean and covariance\n            matrix of the given state estimate.\n        \"\"\"\n        std = [\n            self._std_weight_position * mean[3],\n            self._std_weight_position * mean[3], 1e-1,\n            self._std_weight_position * mean[3]\n        ]\n\n        if self.use_nsa:\n            std = [(1 - bbox_score) * x for x in std]\n\n        innovation_cov = np.diag(np.square(std))\n\n        mean = np.dot(self._update_mat, mean)\n        covariance = np.linalg.multi_dot(\n            (self._update_mat, covariance, self._update_mat.T))\n        return mean, covariance + innovation_cov\n\n    def update(self,\n               mean: np.array,\n               covariance: np.array,\n               measurement: np.array,\n               bbox_score: float = 0.) -> Tuple[np.array, np.array]:\n        \"\"\"Run Kalman filter correction step.\n\n        Args:\n            mean (ndarray): The predicted state's mean vector (8 dimensional).\n            covariance (ndarray): The state's covariance matrix (8x8\n                dimensional).\n            measurement (ndarray): The 4 dimensional measurement vector\n                (x, y, a, h), where (x, y) is the center position, a the\n                aspect ratio, and h the height of the bounding box.\n            bbox_score (float): The confidence score of the bbox.\n                Defaults to 0.\n\n        Returns:\n             (ndarray, ndarray): Returns the measurement-corrected state\n             distribution.\n        \"\"\"\n        projected_mean, projected_cov = \\\n            self.project(mean, covariance, bbox_score)\n\n        chol_factor, lower = scipy.linalg.cho_factor(\n            projected_cov, lower=True, check_finite=False)\n        kalman_gain = scipy.linalg.cho_solve((chol_factor, lower),\n                                             np.dot(covariance,\n                                                    self._update_mat.T).T,\n                                             check_finite=False).T\n        innovation = measurement - projected_mean\n\n        new_mean = mean + np.dot(innovation, kalman_gain.T)\n        new_covariance = covariance - np.linalg.multi_dot(\n            (kalman_gain, projected_cov, kalman_gain.T))\n        return new_mean, new_covariance\n\n    def gating_distance(self,\n                        mean: np.array,\n                        covariance: np.array,\n                        measurements: np.array,\n                        only_position: bool = False) -> np.array:\n        \"\"\"Compute gating distance between state distribution and measurements.\n\n        A suitable distance threshold can be obtained from `chi2inv95`. If\n        `only_position` is False, the chi-square distribution has 4 degrees of\n        freedom, otherwise 2.\n\n        Args:\n            mean (ndarray): Mean vector over the state distribution (8\n                dimensional).\n            covariance (ndarray): Covariance of the state distribution (8x8\n                dimensional).\n            measurements (ndarray): An Nx4 dimensional matrix of N\n                measurements, each in format (x, y, a, h) where (x, y) is the\n                bounding box center position, a the aspect ratio, and h the\n                height.\n            only_position (bool, optional): If True, distance computation is\n                done with respect to the bounding box center position only.\n                Defaults to False.\n\n        Returns:\n            ndarray: Returns an array of length N, where the i-th element\n            contains the squared Mahalanobis distance between\n            (mean, covariance) and `measurements[i]`.\n        \"\"\"\n        mean, covariance = self.project(mean, covariance)\n        if only_position:\n            mean, covariance = mean[:2], covariance[:2, :2]\n            measurements = measurements[:, :2]\n\n        cholesky_factor = np.linalg.cholesky(covariance)\n        d = measurements - mean\n        z = scipy.linalg.solve_triangular(\n            cholesky_factor,\n            d.T,\n            lower=True,\n            check_finite=False,\n            overwrite_b=True)\n        squared_maha = np.sum(z * z, axis=0)\n        return squared_maha\n\n    def track(self, tracks: dict,\n              bboxes: torch.Tensor) -> Tuple[dict, np.array]:\n        \"\"\"Track forward.\n\n        Args:\n            tracks (dict[int:dict]): Track buffer.\n            bboxes (Tensor): Detected bounding boxes.\n\n        Returns:\n            (dict[int:dict], ndarray): Updated tracks and bboxes.\n        \"\"\"\n        costs = []\n        for id, track in tracks.items():\n            track.mean, track.covariance = self.predict(\n                track.mean, track.covariance)\n            gating_distance = self.gating_distance(track.mean,\n                                                   track.covariance,\n                                                   bboxes.cpu().numpy(),\n                                                   self.center_only)\n            costs.append(gating_distance)\n\n        costs = np.stack(costs, 0)\n        costs[costs > self.gating_threshold] = np.nan\n        return tracks, costs\n"
  },
  {
    "path": "mmdet/models/task_modules/tracking/similarity.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor\n\n\ndef embed_similarity(key_embeds: Tensor,\n                     ref_embeds: Tensor,\n                     method: str = 'dot_product',\n                     temperature: int = -1) -> Tensor:\n    \"\"\"Calculate feature similarity from embeddings.\n\n    Args:\n        key_embeds (Tensor): Shape (N1, C).\n        ref_embeds (Tensor): Shape (N2, C).\n        method (str, optional): Method to calculate the similarity,\n            options are 'dot_product' and 'cosine'. Defaults to\n            'dot_product'.\n        temperature (int, optional): Softmax temperature. Defaults to -1.\n\n    Returns:\n        Tensor: Similarity matrix of shape (N1, N2).\n    \"\"\"\n    assert method in ['dot_product', 'cosine']\n\n    if method == 'cosine':\n        key_embeds = F.normalize(key_embeds, p=2, dim=1)\n        ref_embeds = F.normalize(ref_embeds, p=2, dim=1)\n\n    similarity = torch.mm(key_embeds, ref_embeds.T)\n\n    if temperature > 0:\n        similarity /= float(temperature)\n    return similarity\n"
  },
  {
    "path": "mmdet/models/test_time_augs/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .det_tta import DetTTAModel\nfrom .merge_augs import (merge_aug_bboxes, merge_aug_masks,\n                         merge_aug_proposals, merge_aug_results,\n                         merge_aug_scores)\n\n__all__ = [\n    'merge_aug_bboxes', 'merge_aug_masks', 'merge_aug_proposals',\n    'merge_aug_scores', 'merge_aug_results', 'DetTTAModel'\n]\n"
  },
  {
    "path": "mmdet/models/test_time_augs/det_tta.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch\nfrom mmcv.ops import batched_nms\nfrom mmengine.model import BaseTTAModel\nfrom mmengine.registry import MODELS\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox_flip\n\n\n@MODELS.register_module()\nclass DetTTAModel(BaseTTAModel):\n    \"\"\"Merge augmented detection results, only bboxes corresponding score under\n    flipping and multi-scale resizing can be processed now.\n\n    Examples:\n        >>> tta_model = dict(\n        >>>     type='DetTTAModel',\n        >>>     tta_cfg=dict(nms=dict(\n        >>>                     type='nms',\n        >>>                     iou_threshold=0.5),\n        >>>                     max_per_img=100))\n        >>>\n        >>> tta_pipeline = [\n        >>>     dict(type='LoadImageFromFile',\n        >>>          backend_args=None),\n        >>>     dict(\n        >>>         type='TestTimeAug',\n        >>>         transforms=[[\n        >>>             dict(type='Resize',\n        >>>                  scale=(1333, 800),\n        >>>                  keep_ratio=True),\n        >>>         ], [\n        >>>             dict(type='RandomFlip', prob=1.),\n        >>>             dict(type='RandomFlip', prob=0.)\n        >>>         ], [\n        >>>             dict(\n        >>>                 type='PackDetInputs',\n        >>>                 meta_keys=('img_id', 'img_path', 'ori_shape',\n        >>>                         'img_shape', 'scale_factor', 'flip',\n        >>>                         'flip_direction'))\n        >>>         ]])]\n    \"\"\"\n\n    def __init__(self, tta_cfg=None, **kwargs):\n        super().__init__(**kwargs)\n        self.tta_cfg = tta_cfg\n\n    def merge_aug_bboxes(self, aug_bboxes: List[Tensor],\n                         aug_scores: List[Tensor],\n                         img_metas: List[str]) -> Tuple[Tensor, Tensor]:\n        \"\"\"Merge augmented detection bboxes and scores.\n\n        Args:\n            aug_bboxes (list[Tensor]): shape (n, 4*#class)\n            aug_scores (list[Tensor] or None): shape (n, #class)\n        Returns:\n            tuple[Tensor]: ``bboxes`` with shape (n,4), where\n            4 represent (tl_x, tl_y, br_x, br_y)\n            and ``scores`` with shape (n,).\n        \"\"\"\n        recovered_bboxes = []\n        for bboxes, img_info in zip(aug_bboxes, img_metas):\n            ori_shape = img_info['ori_shape']\n            flip = img_info['flip']\n            flip_direction = img_info['flip_direction']\n            if flip:\n                bboxes = bbox_flip(\n                    bboxes=bboxes,\n                    img_shape=ori_shape,\n                    direction=flip_direction)\n            recovered_bboxes.append(bboxes)\n        bboxes = torch.cat(recovered_bboxes, dim=0)\n        if aug_scores is None:\n            return bboxes\n        else:\n            scores = torch.cat(aug_scores, dim=0)\n            return bboxes, scores\n\n    def merge_preds(self, data_samples_list: List[List[DetDataSample]]):\n        \"\"\"Merge batch predictions of enhanced data.\n\n        Args:\n            data_samples_list (List[List[DetDataSample]]): List of predictions\n                of all enhanced data. The outer list indicates images, and the\n                inner list corresponds to the different views of one image.\n                Each element of the inner list is a ``DetDataSample``.\n        Returns:\n            List[DetDataSample]: Merged batch prediction.\n        \"\"\"\n        merged_data_samples = []\n        for data_samples in data_samples_list:\n            merged_data_samples.append(self._merge_single_sample(data_samples))\n        return merged_data_samples\n\n    def _merge_single_sample(\n            self, data_samples: List[DetDataSample]) -> DetDataSample:\n        \"\"\"Merge predictions which come form the different views of one image\n        to one prediction.\n\n        Args:\n            data_samples (List[DetDataSample]): List of predictions\n            of enhanced data which come form one image.\n        Returns:\n            List[DetDataSample]: Merged prediction.\n        \"\"\"\n        aug_bboxes = []\n        aug_scores = []\n        aug_labels = []\n        img_metas = []\n        # TODO: support instance segmentation TTA\n        assert data_samples[0].pred_instances.get('masks', None) is None, \\\n            'TTA of instance segmentation does not support now.'\n        for data_sample in data_samples:\n            aug_bboxes.append(data_sample.pred_instances.bboxes)\n            aug_scores.append(data_sample.pred_instances.scores)\n            aug_labels.append(data_sample.pred_instances.labels)\n            img_metas.append(data_sample.metainfo)\n\n        merged_bboxes, merged_scores = self.merge_aug_bboxes(\n            aug_bboxes, aug_scores, img_metas)\n        merged_labels = torch.cat(aug_labels, dim=0)\n\n        if merged_bboxes.numel() == 0:\n            return data_samples[0]\n\n        det_bboxes, keep_idxs = batched_nms(merged_bboxes, merged_scores,\n                                            merged_labels, self.tta_cfg.nms)\n\n        det_bboxes = det_bboxes[:self.tta_cfg.max_per_img]\n        det_labels = merged_labels[keep_idxs][:self.tta_cfg.max_per_img]\n\n        results = InstanceData()\n        _det_bboxes = det_bboxes.clone()\n        results.bboxes = _det_bboxes[:, :-1]\n        results.scores = _det_bboxes[:, -1]\n        results.labels = det_labels\n        det_results = data_samples[0]\n        det_results.pred_instances = results\n        return det_results\n"
  },
  {
    "path": "mmdet/models/test_time_augs/merge_augs.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport warnings\nfrom typing import List, Optional, Union\n\nimport numpy as np\nimport torch\nfrom mmcv.ops import nms\nfrom mmengine.config import ConfigDict\nfrom torch import Tensor\n\nfrom mmdet.structures.bbox import bbox_mapping_back\n\n\n# TODO remove this, never be used in mmdet\ndef merge_aug_proposals(aug_proposals, img_metas, cfg):\n    \"\"\"Merge augmented proposals (multiscale, flip, etc.)\n\n    Args:\n        aug_proposals (list[Tensor]): proposals from different testing\n            schemes, shape (n, 5). Note that they are not rescaled to the\n            original image size.\n\n        img_metas (list[dict]): list of image info dict where each dict has:\n            'img_shape', 'scale_factor', 'flip', and may also contain\n            'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.\n            For details on the values of these keys see\n            `mmdet/datasets/pipelines/formatting.py:Collect`.\n\n        cfg (dict): rpn test config.\n\n    Returns:\n        Tensor: shape (n, 4), proposals corresponding to original image scale.\n    \"\"\"\n\n    cfg = copy.deepcopy(cfg)\n\n    # deprecate arguments warning\n    if 'nms' not in cfg or 'max_num' in cfg or 'nms_thr' in cfg:\n        warnings.warn(\n            'In rpn_proposal or test_cfg, '\n            'nms_thr has been moved to a dict named nms as '\n            'iou_threshold, max_num has been renamed as max_per_img, '\n            'name of original arguments and the way to specify '\n            'iou_threshold of NMS will be deprecated.')\n    if 'nms' not in cfg:\n        cfg.nms = ConfigDict(dict(type='nms', iou_threshold=cfg.nms_thr))\n    if 'max_num' in cfg:\n        if 'max_per_img' in cfg:\n            assert cfg.max_num == cfg.max_per_img, f'You set max_num and ' \\\n                f'max_per_img at the same time, but get {cfg.max_num} ' \\\n                f'and {cfg.max_per_img} respectively' \\\n                f'Please delete max_num which will be deprecated.'\n        else:\n            cfg.max_per_img = cfg.max_num\n    if 'nms_thr' in cfg:\n        assert cfg.nms.iou_threshold == cfg.nms_thr, f'You set ' \\\n            f'iou_threshold in nms and ' \\\n            f'nms_thr at the same time, but get ' \\\n            f'{cfg.nms.iou_threshold} and {cfg.nms_thr}' \\\n            f' respectively. Please delete the nms_thr ' \\\n            f'which will be deprecated.'\n\n    recovered_proposals = []\n    for proposals, img_info in zip(aug_proposals, img_metas):\n        img_shape = img_info['img_shape']\n        scale_factor = img_info['scale_factor']\n        flip = img_info['flip']\n        flip_direction = img_info['flip_direction']\n        _proposals = proposals.clone()\n        _proposals[:, :4] = bbox_mapping_back(_proposals[:, :4], img_shape,\n                                              scale_factor, flip,\n                                              flip_direction)\n        recovered_proposals.append(_proposals)\n    aug_proposals = torch.cat(recovered_proposals, dim=0)\n    merged_proposals, _ = nms(aug_proposals[:, :4].contiguous(),\n                              aug_proposals[:, -1].contiguous(),\n                              cfg.nms.iou_threshold)\n    scores = merged_proposals[:, 4]\n    _, order = scores.sort(0, descending=True)\n    num = min(cfg.max_per_img, merged_proposals.shape[0])\n    order = order[:num]\n    merged_proposals = merged_proposals[order, :]\n    return merged_proposals\n\n\n# TODO remove this, never be used in mmdet\ndef merge_aug_bboxes(aug_bboxes, aug_scores, img_metas, rcnn_test_cfg):\n    \"\"\"Merge augmented detection bboxes and scores.\n\n    Args:\n        aug_bboxes (list[Tensor]): shape (n, 4*#class)\n        aug_scores (list[Tensor] or None): shape (n, #class)\n        img_shapes (list[Tensor]): shape (3, ).\n        rcnn_test_cfg (dict): rcnn test config.\n\n    Returns:\n        tuple: (bboxes, scores)\n    \"\"\"\n    recovered_bboxes = []\n    for bboxes, img_info in zip(aug_bboxes, img_metas):\n        img_shape = img_info[0]['img_shape']\n        scale_factor = img_info[0]['scale_factor']\n        flip = img_info[0]['flip']\n        flip_direction = img_info[0]['flip_direction']\n        bboxes = bbox_mapping_back(bboxes, img_shape, scale_factor, flip,\n                                   flip_direction)\n        recovered_bboxes.append(bboxes)\n    bboxes = torch.stack(recovered_bboxes).mean(dim=0)\n    if aug_scores is None:\n        return bboxes\n    else:\n        scores = torch.stack(aug_scores).mean(dim=0)\n        return bboxes, scores\n\n\ndef merge_aug_results(aug_batch_results, aug_batch_img_metas):\n    \"\"\"Merge augmented detection results, only bboxes corresponding score under\n    flipping and multi-scale resizing can be processed now.\n\n    Args:\n        aug_batch_results (list[list[[obj:`InstanceData`]]):\n            Detection results of multiple images with\n            different augmentations.\n            The outer list indicate the augmentation . The inter\n            list indicate the batch dimension.\n            Each item usually contains the following keys.\n\n            - scores (Tensor): Classification scores, in shape\n              (num_instance,)\n            - labels (Tensor): Labels of bboxes, in shape\n              (num_instances,).\n            - bboxes (Tensor): In shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        aug_batch_img_metas (list[list[dict]]): The outer list\n            indicates test-time augs (multiscale, flip, etc.)\n            and the inner list indicates\n            images in a batch. Each dict in the list contains\n            information of an image in the batch.\n\n    Returns:\n        batch_results (list[obj:`InstanceData`]): Same with\n        the input `aug_results` except that all bboxes have\n        been mapped to the original scale.\n    \"\"\"\n    num_augs = len(aug_batch_results)\n    num_imgs = len(aug_batch_results[0])\n\n    batch_results = []\n    aug_batch_results = copy.deepcopy(aug_batch_results)\n    for img_id in range(num_imgs):\n        aug_results = []\n        for aug_id in range(num_augs):\n            img_metas = aug_batch_img_metas[aug_id][img_id]\n            results = aug_batch_results[aug_id][img_id]\n\n            img_shape = img_metas['img_shape']\n            scale_factor = img_metas['scale_factor']\n            flip = img_metas['flip']\n            flip_direction = img_metas['flip_direction']\n            bboxes = bbox_mapping_back(results.bboxes, img_shape, scale_factor,\n                                       flip, flip_direction)\n            results.bboxes = bboxes\n            aug_results.append(results)\n        merged_aug_results = results.cat(aug_results)\n        batch_results.append(merged_aug_results)\n\n    return batch_results\n\n\ndef merge_aug_scores(aug_scores):\n    \"\"\"Merge augmented bbox scores.\"\"\"\n    if isinstance(aug_scores[0], torch.Tensor):\n        return torch.mean(torch.stack(aug_scores), dim=0)\n    else:\n        return np.mean(aug_scores, axis=0)\n\n\ndef merge_aug_masks(aug_masks: List[Tensor],\n                    img_metas: dict,\n                    weights: Optional[Union[list, Tensor]] = None) -> Tensor:\n    \"\"\"Merge augmented mask prediction.\n\n    Args:\n        aug_masks (list[Tensor]): each has shape\n            (n, c, h, w).\n        img_metas (dict): Image information.\n        weights (list or Tensor): Weight of each aug_masks,\n            the length should be n.\n\n    Returns:\n        Tensor: has shape (n, c, h, w)\n    \"\"\"\n    recovered_masks = []\n    for i, mask in enumerate(aug_masks):\n        if weights is not None:\n            assert len(weights) == len(aug_masks)\n            weight = weights[i]\n        else:\n            weight = 1\n        flip = img_metas.get('flip', False)\n        if flip:\n            flip_direction = img_metas['flip_direction']\n            if flip_direction == 'horizontal':\n                mask = mask[:, :, :, ::-1]\n            elif flip_direction == 'vertical':\n                mask = mask[:, :, ::-1, :]\n            elif flip_direction == 'diagonal':\n                mask = mask[:, :, :, ::-1]\n                mask = mask[:, :, ::-1, :]\n            else:\n                raise ValueError(\n                    f\"Invalid flipping direction '{flip_direction}'\")\n        recovered_masks.append(mask[None, :] * weight)\n\n    merged_masks = torch.cat(recovered_masks, 0).mean(dim=0)\n    if weights is not None:\n        merged_masks = merged_masks * len(weights) / sum(weights)\n    return merged_masks\n"
  },
  {
    "path": "mmdet/models/trackers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_tracker import BaseTracker\nfrom .byte_tracker import ByteTracker\nfrom .masktrack_rcnn_tracker import MaskTrackRCNNTracker\nfrom .ocsort_tracker import OCSORTTracker\nfrom .quasi_dense_tracker import QuasiDenseTracker\nfrom .sort_tracker import SORTTracker\nfrom .strongsort_tracker import StrongSORTTracker\n\n__all__ = [\n    'BaseTracker', 'ByteTracker', 'QuasiDenseTracker', 'SORTTracker',\n    'StrongSORTTracker', 'OCSORTTracker', 'MaskTrackRCNNTracker'\n]\n"
  },
  {
    "path": "mmdet/models/trackers/base_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom addict import Dict\n\n\nclass BaseTracker(metaclass=ABCMeta):\n    \"\"\"Base tracker model.\n\n    Args:\n        momentums (dict[str:float], optional): Momentums to update the buffers.\n            The `str` indicates the name of the buffer while the `float`\n            indicates the momentum. Defaults to None.\n        num_frames_retain (int, optional). If a track is disappeared more than\n            `num_frames_retain` frames, it will be deleted in the memo.\n             Defaults to 10.\n    \"\"\"\n\n    def __init__(self,\n                 momentums: Optional[dict] = None,\n                 num_frames_retain: int = 10) -> None:\n        super().__init__()\n        if momentums is not None:\n            assert isinstance(momentums, dict), 'momentums must be a dict'\n        self.momentums = momentums\n        self.num_frames_retain = num_frames_retain\n\n        self.reset()\n\n    def reset(self) -> None:\n        \"\"\"Reset the buffer of the tracker.\"\"\"\n        self.num_tracks = 0\n        self.tracks = dict()\n\n    @property\n    def empty(self) -> bool:\n        \"\"\"Whether the buffer is empty or not.\"\"\"\n        return False if self.tracks else True\n\n    @property\n    def ids(self) -> List[dict]:\n        \"\"\"All ids in the tracker.\"\"\"\n        return list(self.tracks.keys())\n\n    @property\n    def with_reid(self) -> bool:\n        \"\"\"bool: whether the framework has a reid model\"\"\"\n        return hasattr(self, 'reid') and self.reid is not None\n\n    def update(self, **kwargs) -> None:\n        \"\"\"Update the tracker.\n\n        Args:\n            kwargs (dict[str: Tensor | int]): The `str` indicates the\n                name of the input variable. `ids` and `frame_ids` are\n                obligatory in the keys.\n        \"\"\"\n        memo_items = [k for k, v in kwargs.items() if v is not None]\n        rm_items = [k for k in kwargs.keys() if k not in memo_items]\n        for item in rm_items:\n            kwargs.pop(item)\n        if not hasattr(self, 'memo_items'):\n            self.memo_items = memo_items\n        else:\n            assert memo_items == self.memo_items\n\n        assert 'ids' in memo_items\n        num_objs = len(kwargs['ids'])\n        id_indice = memo_items.index('ids')\n        assert 'frame_ids' in memo_items\n        frame_id = int(kwargs['frame_ids'])\n        if isinstance(kwargs['frame_ids'], int):\n            kwargs['frame_ids'] = torch.tensor([kwargs['frame_ids']] *\n                                               num_objs)\n        # cur_frame_id = int(kwargs['frame_ids'][0])\n        for k, v in kwargs.items():\n            if len(v) != num_objs:\n                raise ValueError('kwargs value must both equal')\n\n        for obj in zip(*kwargs.values()):\n            id = int(obj[id_indice])\n            if id in self.tracks:\n                self.update_track(id, obj)\n            else:\n                self.init_track(id, obj)\n\n        self.pop_invalid_tracks(frame_id)\n\n    def pop_invalid_tracks(self, frame_id: int) -> None:\n        \"\"\"Pop out invalid tracks.\"\"\"\n        invalid_ids = []\n        for k, v in self.tracks.items():\n            if frame_id - v['frame_ids'][-1] >= self.num_frames_retain:\n                invalid_ids.append(k)\n        for invalid_id in invalid_ids:\n            self.tracks.pop(invalid_id)\n\n    def update_track(self, id: int, obj: Tuple[torch.Tensor]):\n        \"\"\"Update a track.\"\"\"\n        for k, v in zip(self.memo_items, obj):\n            v = v[None]\n            if self.momentums is not None and k in self.momentums:\n                m = self.momentums[k]\n                self.tracks[id][k] = (1 - m) * self.tracks[id][k] + m * v\n            else:\n                self.tracks[id][k].append(v)\n\n    def init_track(self, id: int, obj: Tuple[torch.Tensor]):\n        \"\"\"Initialize a track.\"\"\"\n        self.tracks[id] = Dict()\n        for k, v in zip(self.memo_items, obj):\n            v = v[None]\n            if self.momentums is not None and k in self.momentums:\n                self.tracks[id][k] = v\n            else:\n                self.tracks[id][k] = [v]\n\n    @property\n    def memo(self) -> dict:\n        \"\"\"Return all buffers in the tracker.\"\"\"\n        outs = Dict()\n        for k in self.memo_items:\n            outs[k] = []\n\n        for id, objs in self.tracks.items():\n            for k, v in objs.items():\n                if k not in outs:\n                    continue\n                if self.momentums is not None and k in self.momentums:\n                    v = v\n                else:\n                    v = v[-1]\n                outs[k].append(v)\n\n        for k, v in outs.items():\n            outs[k] = torch.cat(v, dim=0)\n        return outs\n\n    def get(self,\n            item: str,\n            ids: Optional[list] = None,\n            num_samples: Optional[int] = None,\n            behavior: Optional[str] = None) -> torch.Tensor:\n        \"\"\"Get the buffer of a specific item.\n\n        Args:\n            item (str): The demanded item.\n            ids (list[int], optional): The demanded ids. Defaults to None.\n            num_samples (int, optional): Number of samples to calculate the\n                results. Defaults to None.\n            behavior (str, optional): Behavior to calculate the results.\n                Options are `mean` | None. Defaults to None.\n\n        Returns:\n            Tensor: The results of the demanded item.\n        \"\"\"\n        if ids is None:\n            ids = self.ids\n\n        outs = []\n        for id in ids:\n            out = self.tracks[id][item]\n            if isinstance(out, list):\n                if num_samples is not None:\n                    out = out[-num_samples:]\n                    out = torch.cat(out, dim=0)\n                    if behavior == 'mean':\n                        out = out.mean(dim=0, keepdim=True)\n                    elif behavior is None:\n                        out = out[None]\n                    else:\n                        raise NotImplementedError()\n                else:\n                    out = out[-1]\n            outs.append(out)\n        return torch.cat(outs, dim=0)\n\n    @abstractmethod\n    def track(self, *args, **kwargs):\n        \"\"\"Tracking forward function.\"\"\"\n        pass\n\n    def crop_imgs(self,\n                  img: torch.Tensor,\n                  meta_info: dict,\n                  bboxes: torch.Tensor,\n                  rescale: bool = False) -> torch.Tensor:\n        \"\"\"Crop the images according to some bounding boxes. Typically for re-\n        identification sub-module.\n\n        Args:\n            img (Tensor): of shape (T, C, H, W) encoding input image.\n                Typically these should be mean centered and std scaled.\n            meta_info (dict): image information dict where each dict\n                has: 'img_shape', 'scale_factor', 'flip', and may also contain\n                'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.\n            bboxes (Tensor): of shape (N, 4) or (N, 5).\n            rescale (bool, optional): If True, the bounding boxes should be\n                rescaled to fit the scale of the image. Defaults to False.\n\n        Returns:\n            Tensor: Image tensor of shape (T, C, H, W).\n        \"\"\"\n        h, w = meta_info['img_shape']\n        img = img[:, :, :h, :w]\n        if rescale:\n            factor_x, factor_y = meta_info['scale_factor']\n            bboxes[:, :4] *= torch.tensor(\n                [factor_x, factor_y, factor_x, factor_y]).to(bboxes.device)\n        bboxes[:, 0] = torch.clamp(bboxes[:, 0], min=0, max=w - 1)\n        bboxes[:, 1] = torch.clamp(bboxes[:, 1], min=0, max=h - 1)\n        bboxes[:, 2] = torch.clamp(bboxes[:, 2], min=1, max=w)\n        bboxes[:, 3] = torch.clamp(bboxes[:, 3], min=1, max=h)\n\n        crop_imgs = []\n        for bbox in bboxes:\n            x1, y1, x2, y2 = map(int, bbox)\n            if x2 <= x1:\n                x2 = x1 + 1\n            if y2 <= y1:\n                y2 = y1 + 1\n            crop_img = img[:, :, y1:y2, x1:x2]\n            if self.reid.get('img_scale', False):\n                crop_img = F.interpolate(\n                    crop_img,\n                    size=self.reid['img_scale'],\n                    mode='bilinear',\n                    align_corners=False)\n            crop_imgs.append(crop_img)\n\n        if len(crop_imgs) > 0:\n            return torch.cat(crop_imgs, dim=0)\n        elif self.reid.get('img_scale', False):\n            _h, _w = self.reid['img_scale']\n            return img.new_zeros((0, 3, _h, _w))\n        else:\n            return img.new_zeros((0, 3, h, w))\n"
  },
  {
    "path": "mmdet/models/trackers/byte_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\ntry:\n    import lap\nexcept ImportError:\n    lap = None\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import (bbox_cxcyah_to_xyxy, bbox_overlaps,\n                                   bbox_xyxy_to_cxcyah)\nfrom .base_tracker import BaseTracker\n\n\n@MODELS.register_module()\nclass ByteTracker(BaseTracker):\n    \"\"\"Tracker for ByteTrack.\n\n    Args:\n        motion (dict): Configuration of motion. Defaults to None.\n        obj_score_thrs (dict): Detection score threshold for matching objects.\n            - high (float): Threshold of the first matching. Defaults to 0.6.\n            - low (float): Threshold of the second matching. Defaults to 0.1.\n        init_track_thr (float): Detection score threshold for initializing a\n            new tracklet. Defaults to 0.7.\n        weight_iou_with_det_scores (bool): Whether using detection scores to\n            weight IOU which is used for matching. Defaults to True.\n        match_iou_thrs (dict): IOU distance threshold for matching between two\n            frames.\n            - high (float): Threshold of the first matching. Defaults to 0.1.\n            - low (float): Threshold of the second matching. Defaults to 0.5.\n            - tentative (float): Threshold of the matching for tentative\n                tracklets. Defaults to 0.3.\n        num_tentatives (int, optional): Number of continuous frames to confirm\n            a track. Defaults to 3.\n    \"\"\"\n\n    def __init__(self,\n                 motion: Optional[dict] = None,\n                 obj_score_thrs: dict = dict(high=0.6, low=0.1),\n                 init_track_thr: float = 0.7,\n                 weight_iou_with_det_scores: bool = True,\n                 match_iou_thrs: dict = dict(high=0.1, low=0.5, tentative=0.3),\n                 num_tentatives: int = 3,\n                 **kwargs):\n        super().__init__(**kwargs)\n\n        if lap is None:\n            raise RuntimeError('lap is not installed,\\\n                 please install it by: pip install lap')\n        if motion is not None:\n            self.motion = TASK_UTILS.build(motion)\n\n        self.obj_score_thrs = obj_score_thrs\n        self.init_track_thr = init_track_thr\n\n        self.weight_iou_with_det_scores = weight_iou_with_det_scores\n        self.match_iou_thrs = match_iou_thrs\n\n        self.num_tentatives = num_tentatives\n\n    @property\n    def confirmed_ids(self) -> List:\n        \"\"\"Confirmed ids in the tracker.\"\"\"\n        ids = [id for id, track in self.tracks.items() if not track.tentative]\n        return ids\n\n    @property\n    def unconfirmed_ids(self) -> List:\n        \"\"\"Unconfirmed ids in the tracker.\"\"\"\n        ids = [id for id, track in self.tracks.items() if track.tentative]\n        return ids\n\n    def init_track(self, id: int, obj: Tuple[torch.Tensor]) -> None:\n        \"\"\"Initialize a track.\"\"\"\n        super().init_track(id, obj)\n        if self.tracks[id].frame_ids[-1] == 0:\n            self.tracks[id].tentative = False\n        else:\n            self.tracks[id].tentative = True\n        bbox = bbox_xyxy_to_cxcyah(self.tracks[id].bboxes[-1])  # size = (1, 4)\n        assert bbox.ndim == 2 and bbox.shape[0] == 1\n        bbox = bbox.squeeze(0).cpu().numpy()\n        self.tracks[id].mean, self.tracks[id].covariance = self.kf.initiate(\n            bbox)\n\n    def update_track(self, id: int, obj: Tuple[torch.Tensor]) -> None:\n        \"\"\"Update a track.\"\"\"\n        super().update_track(id, obj)\n        if self.tracks[id].tentative:\n            if len(self.tracks[id]['bboxes']) >= self.num_tentatives:\n                self.tracks[id].tentative = False\n        bbox = bbox_xyxy_to_cxcyah(self.tracks[id].bboxes[-1])  # size = (1, 4)\n        assert bbox.ndim == 2 and bbox.shape[0] == 1\n        bbox = bbox.squeeze(0).cpu().numpy()\n        track_label = self.tracks[id]['labels'][-1]\n        label_idx = self.memo_items.index('labels')\n        obj_label = obj[label_idx]\n        assert obj_label == track_label\n        self.tracks[id].mean, self.tracks[id].covariance = self.kf.update(\n            self.tracks[id].mean, self.tracks[id].covariance, bbox)\n\n    def pop_invalid_tracks(self, frame_id: int) -> None:\n        \"\"\"Pop out invalid tracks.\"\"\"\n        invalid_ids = []\n        for k, v in self.tracks.items():\n            # case1: disappeared frames >= self.num_frames_retrain\n            case1 = frame_id - v['frame_ids'][-1] >= self.num_frames_retain\n            # case2: tentative tracks but not matched in this frame\n            case2 = v.tentative and v['frame_ids'][-1] != frame_id\n            if case1 or case2:\n                invalid_ids.append(k)\n        for invalid_id in invalid_ids:\n            self.tracks.pop(invalid_id)\n\n    def assign_ids(\n            self,\n            ids: List[int],\n            det_bboxes: torch.Tensor,\n            det_labels: torch.Tensor,\n            det_scores: torch.Tensor,\n            weight_iou_with_det_scores: Optional[bool] = False,\n            match_iou_thr: Optional[float] = 0.5\n    ) -> Tuple[np.ndarray, np.ndarray]:\n        \"\"\"Assign ids.\n\n        Args:\n            ids (list[int]): Tracking ids.\n            det_bboxes (Tensor): of shape (N, 4)\n            det_labels (Tensor): of shape (N,)\n            det_scores (Tensor): of shape (N,)\n            weight_iou_with_det_scores (bool, optional): Whether using\n                detection scores to weight IOU which is used for matching.\n                Defaults to False.\n            match_iou_thr (float, optional): Matching threshold.\n                Defaults to 0.5.\n\n        Returns:\n            tuple(np.ndarray, np.ndarray): The assigning ids.\n        \"\"\"\n        # get track_bboxes\n        track_bboxes = np.zeros((0, 4))\n        for id in ids:\n            track_bboxes = np.concatenate(\n                (track_bboxes, self.tracks[id].mean[:4][None]), axis=0)\n        track_bboxes = torch.from_numpy(track_bboxes).to(det_bboxes)\n        track_bboxes = bbox_cxcyah_to_xyxy(track_bboxes)\n\n        # compute distance\n        ious = bbox_overlaps(track_bboxes, det_bboxes)\n        if weight_iou_with_det_scores:\n            ious *= det_scores\n        # support multi-class association\n        track_labels = torch.tensor([\n            self.tracks[id]['labels'][-1] for id in ids\n        ]).to(det_bboxes.device)\n\n        cate_match = det_labels[None, :] == track_labels[:, None]\n        # to avoid det and track of different categories are matched\n        cate_cost = (1 - cate_match.int()) * 1e6\n\n        dists = (1 - ious + cate_cost).cpu().numpy()\n\n        # bipartite match\n        if dists.size > 0:\n            cost, row, col = lap.lapjv(\n                dists, extend_cost=True, cost_limit=1 - match_iou_thr)\n        else:\n            row = np.zeros(len(ids)).astype(np.int32) - 1\n            col = np.zeros(len(det_bboxes)).astype(np.int32) - 1\n        return row, col\n\n    def track(self, data_sample: DetDataSample, **kwargs) -> InstanceData:\n        \"\"\"Tracking forward function.\n\n        Args:\n            data_sample (:obj:`DetDataSample`): The data sample.\n                It includes information such as `pred_instances`.\n\n        Returns:\n            :obj:`InstanceData`: Tracking results of the input images.\n            Each InstanceData usually contains ``bboxes``, ``labels``,\n            ``scores`` and ``instances_id``.\n        \"\"\"\n        metainfo = data_sample.metainfo\n        bboxes = data_sample.pred_instances.bboxes\n        labels = data_sample.pred_instances.labels\n        scores = data_sample.pred_instances.scores\n\n        frame_id = metainfo.get('frame_id', -1)\n        if frame_id == 0:\n            self.reset()\n        if not hasattr(self, 'kf'):\n            self.kf = self.motion\n\n        if self.empty or bboxes.size(0) == 0:\n            valid_inds = scores > self.init_track_thr\n            scores = scores[valid_inds]\n            bboxes = bboxes[valid_inds]\n            labels = labels[valid_inds]\n            num_new_tracks = bboxes.size(0)\n            ids = torch.arange(self.num_tracks,\n                               self.num_tracks + num_new_tracks).to(labels)\n            self.num_tracks += num_new_tracks\n\n        else:\n            # 0. init\n            ids = torch.full((bboxes.size(0), ),\n                             -1,\n                             dtype=labels.dtype,\n                             device=labels.device)\n\n            # get the detection bboxes for the first association\n            first_det_inds = scores > self.obj_score_thrs['high']\n            first_det_bboxes = bboxes[first_det_inds]\n            first_det_labels = labels[first_det_inds]\n            first_det_scores = scores[first_det_inds]\n            first_det_ids = ids[first_det_inds]\n\n            # get the detection bboxes for the second association\n            second_det_inds = (~first_det_inds) & (\n                scores > self.obj_score_thrs['low'])\n            second_det_bboxes = bboxes[second_det_inds]\n            second_det_labels = labels[second_det_inds]\n            second_det_scores = scores[second_det_inds]\n            second_det_ids = ids[second_det_inds]\n\n            # 1. use Kalman Filter to predict current location\n            for id in self.confirmed_ids:\n                # track is lost in previous frame\n                if self.tracks[id].frame_ids[-1] != frame_id - 1:\n                    self.tracks[id].mean[7] = 0\n                (self.tracks[id].mean,\n                 self.tracks[id].covariance) = self.kf.predict(\n                     self.tracks[id].mean, self.tracks[id].covariance)\n\n            # 2. first match\n            first_match_track_inds, first_match_det_inds = self.assign_ids(\n                self.confirmed_ids, first_det_bboxes, first_det_labels,\n                first_det_scores, self.weight_iou_with_det_scores,\n                self.match_iou_thrs['high'])\n            # '-1' mean a detection box is not matched with tracklets in\n            # previous frame\n            valid = first_match_det_inds > -1\n            first_det_ids[valid] = torch.tensor(\n                self.confirmed_ids)[first_match_det_inds[valid]].to(labels)\n\n            first_match_det_bboxes = first_det_bboxes[valid]\n            first_match_det_labels = first_det_labels[valid]\n            first_match_det_scores = first_det_scores[valid]\n            first_match_det_ids = first_det_ids[valid]\n            assert (first_match_det_ids > -1).all()\n\n            first_unmatch_det_bboxes = first_det_bboxes[~valid]\n            first_unmatch_det_labels = first_det_labels[~valid]\n            first_unmatch_det_scores = first_det_scores[~valid]\n            first_unmatch_det_ids = first_det_ids[~valid]\n            assert (first_unmatch_det_ids == -1).all()\n\n            # 3. use unmatched detection bboxes from the first match to match\n            # the unconfirmed tracks\n            (tentative_match_track_inds,\n             tentative_match_det_inds) = self.assign_ids(\n                 self.unconfirmed_ids, first_unmatch_det_bboxes,\n                 first_unmatch_det_labels, first_unmatch_det_scores,\n                 self.weight_iou_with_det_scores,\n                 self.match_iou_thrs['tentative'])\n            valid = tentative_match_det_inds > -1\n            first_unmatch_det_ids[valid] = torch.tensor(self.unconfirmed_ids)[\n                tentative_match_det_inds[valid]].to(labels)\n\n            # 4. second match for unmatched tracks from the first match\n            first_unmatch_track_ids = []\n            for i, id in enumerate(self.confirmed_ids):\n                # tracklet is not matched in the first match\n                case_1 = first_match_track_inds[i] == -1\n                # tracklet is not lost in the previous frame\n                case_2 = self.tracks[id].frame_ids[-1] == frame_id - 1\n                if case_1 and case_2:\n                    first_unmatch_track_ids.append(id)\n\n            second_match_track_inds, second_match_det_inds = self.assign_ids(\n                first_unmatch_track_ids, second_det_bboxes, second_det_labels,\n                second_det_scores, False, self.match_iou_thrs['low'])\n            valid = second_match_det_inds > -1\n            second_det_ids[valid] = torch.tensor(first_unmatch_track_ids)[\n                second_match_det_inds[valid]].to(ids)\n\n            # 5. gather all matched detection bboxes from step 2-4\n            # we only keep matched detection bboxes in second match, which\n            # means the id != -1\n            valid = second_det_ids > -1\n            bboxes = torch.cat(\n                (first_match_det_bboxes, first_unmatch_det_bboxes), dim=0)\n            bboxes = torch.cat((bboxes, second_det_bboxes[valid]), dim=0)\n\n            labels = torch.cat(\n                (first_match_det_labels, first_unmatch_det_labels), dim=0)\n            labels = torch.cat((labels, second_det_labels[valid]), dim=0)\n\n            scores = torch.cat(\n                (first_match_det_scores, first_unmatch_det_scores), dim=0)\n            scores = torch.cat((scores, second_det_scores[valid]), dim=0)\n\n            ids = torch.cat((first_match_det_ids, first_unmatch_det_ids),\n                            dim=0)\n            ids = torch.cat((ids, second_det_ids[valid]), dim=0)\n\n            # 6. assign new ids\n            new_track_inds = ids == -1\n            ids[new_track_inds] = torch.arange(\n                self.num_tracks,\n                self.num_tracks + new_track_inds.sum()).to(labels)\n            self.num_tracks += new_track_inds.sum()\n\n        self.update(\n            ids=ids,\n            bboxes=bboxes,\n            scores=scores,\n            labels=labels,\n            frame_ids=frame_id)\n\n        # update pred_track_instances\n        pred_track_instances = InstanceData()\n        pred_track_instances.bboxes = bboxes\n        pred_track_instances.labels = labels\n        pred_track_instances.scores = scores\n        pred_track_instances.instances_id = ids\n\n        return pred_track_instances\n"
  },
  {
    "path": "mmdet/models/trackers/masktrack_rcnn_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom .base_tracker import BaseTracker\n\n\n@MODELS.register_module()\nclass MaskTrackRCNNTracker(BaseTracker):\n    \"\"\"Tracker for MaskTrack R-CNN.\n\n    Args:\n        match_weights (dict[str : float]): The Weighting factor when computing\n        the match score. It contains keys as follows:\n\n            - det_score (float): The coefficient of `det_score` when computing\n                match score.\n            - iou (float): The coefficient of `ious` when computing match\n                score.\n            - det_label (float): The coefficient of `label_deltas` when\n                computing match score.\n    \"\"\"\n\n    def __init__(self,\n                 match_weights: dict = dict(\n                     det_score=1.0, iou=2.0, det_label=10.0),\n                 **kwargs):\n        super().__init__(**kwargs)\n        self.match_weights = match_weights\n\n    def get_match_score(self, bboxes: Tensor, labels: Tensor, scores: Tensor,\n                        prev_bboxes: Tensor, prev_labels: Tensor,\n                        similarity_logits: Tensor) -> Tensor:\n        \"\"\"Get the match score.\n\n        Args:\n            bboxes (torch.Tensor): of shape (num_current_bboxes, 4) in\n                [tl_x, tl_y, br_x, br_y] format. Denoting the detection\n                bboxes of current frame.\n            labels (torch.Tensor): of shape (num_current_bboxes, )\n            scores (torch.Tensor): of shape (num_current_bboxes, )\n            prev_bboxes (torch.Tensor): of shape (num_previous_bboxes, 4) in\n                [tl_x, tl_y, br_x, br_y] format.  Denoting the detection bboxes\n                of previous frame.\n            prev_labels (torch.Tensor): of shape (num_previous_bboxes, )\n            similarity_logits (torch.Tensor): of shape (num_current_bboxes,\n                num_previous_bboxes + 1). Denoting the similarity logits from\n                track head.\n\n        Returns:\n            torch.Tensor: The matching score of shape (num_current_bboxes,\n            num_previous_bboxes + 1)\n        \"\"\"\n        similarity_scores = similarity_logits.softmax(dim=1)\n\n        ious = bbox_overlaps(bboxes, prev_bboxes)\n        iou_dummy = ious.new_zeros(ious.shape[0], 1)\n        ious = torch.cat((iou_dummy, ious), dim=1)\n\n        label_deltas = (labels.view(-1, 1) == prev_labels).float()\n        label_deltas_dummy = label_deltas.new_ones(label_deltas.shape[0], 1)\n        label_deltas = torch.cat((label_deltas_dummy, label_deltas), dim=1)\n\n        match_score = similarity_scores.log()\n        match_score += self.match_weights['det_score'] * \\\n            scores.view(-1, 1).log()\n        match_score += self.match_weights['iou'] * ious\n        match_score += self.match_weights['det_label'] * label_deltas\n\n        return match_score\n\n    def assign_ids(self, match_scores: Tensor):\n        num_prev_bboxes = match_scores.shape[1] - 1\n        _, match_ids = match_scores.max(dim=1)\n\n        ids = match_ids.new_zeros(match_ids.shape[0]) - 1\n        best_match_scores = match_scores.new_zeros(num_prev_bboxes) - 1e6\n        for idx, match_id in enumerate(match_ids):\n            if match_id == 0:\n                ids[idx] = self.num_tracks\n                self.num_tracks += 1\n            else:\n                match_score = match_scores[idx, match_id]\n                # TODO: fix the bug where multiple candidate might match\n                # with the same previous object.\n                if match_score > best_match_scores[match_id - 1]:\n                    ids[idx] = self.ids[match_id - 1]\n                    best_match_scores[match_id - 1] = match_score\n        return ids, best_match_scores\n\n    def track(self,\n              model: torch.nn.Module,\n              feats: List[torch.Tensor],\n              data_sample: DetDataSample,\n              rescale=True,\n              **kwargs) -> InstanceData:\n        \"\"\"Tracking forward function.\n\n        Args:\n            model (nn.Module): VIS model.\n            img (Tensor): of shape (T, C, H, W) encoding input image.\n                Typically these should be mean centered and std scaled.\n                The T denotes the number of key images and usually is 1 in\n                MaskTrackRCNN method.\n            feats (list[Tensor]): Multi level feature maps of `img`.\n            data_sample (:obj:`TrackDataSample`): The data sample.\n                It includes information such as `pred_det_instances`.\n            rescale (bool, optional): If True, the bounding boxes should be\n                rescaled to fit the original scale of the image. Defaults to\n                True.\n\n        Returns:\n            :obj:`InstanceData`: Tracking results of the input images.\n            Each InstanceData usually contains ``bboxes``, ``labels``,\n            ``scores`` and ``instances_id``.\n        \"\"\"\n        metainfo = data_sample.metainfo\n        bboxes = data_sample.pred_instances.bboxes\n        masks = data_sample.pred_instances.masks\n        labels = data_sample.pred_instances.labels\n        scores = data_sample.pred_instances.scores\n\n        frame_id = metainfo.get('frame_id', -1)\n        # create pred_track_instances\n        pred_track_instances = InstanceData()\n\n        if bboxes.shape[0] == 0:\n            ids = torch.zeros_like(labels)\n            pred_track_instances = data_sample.pred_instances.clone()\n            pred_track_instances.instances_id = ids\n            return pred_track_instances\n\n        rescaled_bboxes = bboxes.clone()\n        if rescale:\n            scale_factor = rescaled_bboxes.new_tensor(\n                metainfo['scale_factor']).repeat((1, 2))\n            rescaled_bboxes = rescaled_bboxes * scale_factor\n        roi_feats, _ = model.track_head.extract_roi_feats(\n            feats, [rescaled_bboxes])\n\n        if self.empty:\n            num_new_tracks = bboxes.size(0)\n            ids = torch.arange(\n                self.num_tracks,\n                self.num_tracks + num_new_tracks,\n                dtype=torch.long)\n            self.num_tracks += num_new_tracks\n        else:\n            prev_bboxes = self.get('bboxes')\n            prev_labels = self.get('labels')\n            prev_roi_feats = self.get('roi_feats')\n\n            similarity_logits = model.track_head.predict(\n                roi_feats, prev_roi_feats)\n            match_scores = self.get_match_score(bboxes, labels, scores,\n                                                prev_bboxes, prev_labels,\n                                                similarity_logits)\n            ids, _ = self.assign_ids(match_scores)\n\n        valid_inds = ids > -1\n        ids = ids[valid_inds]\n        bboxes = bboxes[valid_inds]\n        labels = labels[valid_inds]\n        scores = scores[valid_inds]\n        masks = masks[valid_inds]\n        roi_feats = roi_feats[valid_inds]\n\n        self.update(\n            ids=ids,\n            bboxes=bboxes,\n            labels=labels,\n            scores=scores,\n            masks=masks,\n            roi_feats=roi_feats,\n            frame_ids=frame_id)\n        # update pred_track_instances\n        pred_track_instances.bboxes = bboxes\n        pred_track_instances.masks = masks\n        pred_track_instances.labels = labels\n        pred_track_instances.scores = scores\n        pred_track_instances.instances_id = ids\n\n        return pred_track_instances\n"
  },
  {
    "path": "mmdet/models/trackers/ocsort_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\ntry:\n    import lap\nexcept ImportError:\n    lap = None\nimport numpy as np\nimport torch\nfrom addict import Dict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import (bbox_cxcyah_to_xyxy, bbox_overlaps,\n                                   bbox_xyxy_to_cxcyah)\nfrom .sort_tracker import SORTTracker\n\n\n@MODELS.register_module()\nclass OCSORTTracker(SORTTracker):\n    \"\"\"Tracker for OC-SORT.\n\n    Args:\n        motion (dict): Configuration of motion. Defaults to None.\n        obj_score_thrs (float): Detection score threshold for matching objects.\n            Defaults to 0.3.\n        init_track_thr (float): Detection score threshold for initializing a\n            new tracklet. Defaults to 0.7.\n        weight_iou_with_det_scores (bool): Whether using detection scores to\n            weight IOU which is used for matching. Defaults to True.\n        match_iou_thr (float): IOU distance threshold for matching between two\n            frames. Defaults to 0.3.\n        num_tentatives (int, optional): Number of continuous frames to confirm\n            a track. Defaults to 3.\n        vel_consist_weight (float): Weight of the velocity consistency term in\n            association (OCM term in the paper).\n        vel_delta_t (int): The difference of time step for calculating of the\n            velocity direction of tracklets.\n        init_cfg (dict or list[dict], optional): Initialization config dict.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 motion: Optional[dict] = None,\n                 obj_score_thr: float = 0.3,\n                 init_track_thr: float = 0.7,\n                 weight_iou_with_det_scores: bool = True,\n                 match_iou_thr: float = 0.3,\n                 num_tentatives: int = 3,\n                 vel_consist_weight: float = 0.2,\n                 vel_delta_t: int = 3,\n                 **kwargs):\n        if lap is None:\n            raise RuntimeError('lap is not installed,\\\n                 please install it by: pip install lap')\n        super().__init__(motion=motion, **kwargs)\n        self.obj_score_thr = obj_score_thr\n        self.init_track_thr = init_track_thr\n\n        self.weight_iou_with_det_scores = weight_iou_with_det_scores\n        self.match_iou_thr = match_iou_thr\n        self.vel_consist_weight = vel_consist_weight\n        self.vel_delta_t = vel_delta_t\n\n        self.num_tentatives = num_tentatives\n\n    @property\n    def unconfirmed_ids(self):\n        \"\"\"Unconfirmed ids in the tracker.\"\"\"\n        ids = [id for id, track in self.tracks.items() if track.tentative]\n        return ids\n\n    def init_track(self, id: int, obj: Tuple[torch.Tensor]):\n        \"\"\"Initialize a track.\"\"\"\n        super().init_track(id, obj)\n        if self.tracks[id].frame_ids[-1] == 0:\n            self.tracks[id].tentative = False\n        else:\n            self.tracks[id].tentative = True\n        bbox = bbox_xyxy_to_cxcyah(self.tracks[id].bboxes[-1])  # size = (1, 4)\n        assert bbox.ndim == 2 and bbox.shape[0] == 1\n        bbox = bbox.squeeze(0).cpu().numpy()\n        self.tracks[id].mean, self.tracks[id].covariance = self.kf.initiate(\n            bbox)\n        # track.obs maintains the history associated detections to this track\n        self.tracks[id].obs = []\n        bbox_id = self.memo_items.index('bboxes')\n        self.tracks[id].obs.append(obj[bbox_id])\n        # a placefolder to save mean/covariance before losing tracking it\n        # parameters to save: mean, covariance, measurement\n        self.tracks[id].tracked = True\n        self.tracks[id].saved_attr = Dict()\n        self.tracks[id].velocity = torch.tensor(\n            (-1, -1)).to(obj[bbox_id].device)  # placeholder\n\n    def update_track(self, id: int, obj: Tuple[torch.Tensor]):\n        \"\"\"Update a track.\"\"\"\n        super().update_track(id, obj)\n        if self.tracks[id].tentative:\n            if len(self.tracks[id]['bboxes']) >= self.num_tentatives:\n                self.tracks[id].tentative = False\n        bbox = bbox_xyxy_to_cxcyah(self.tracks[id].bboxes[-1])  # size = (1, 4)\n        assert bbox.ndim == 2 and bbox.shape[0] == 1\n        bbox = bbox.squeeze(0).cpu().numpy()\n        self.tracks[id].mean, self.tracks[id].covariance = self.kf.update(\n            self.tracks[id].mean, self.tracks[id].covariance, bbox)\n        self.tracks[id].tracked = True\n        bbox_id = self.memo_items.index('bboxes')\n        self.tracks[id].obs.append(obj[bbox_id])\n\n        bbox1 = self.k_step_observation(self.tracks[id])\n        bbox2 = obj[bbox_id]\n        self.tracks[id].velocity = self.vel_direction(bbox1, bbox2).to(\n            obj[bbox_id].device)\n\n    def vel_direction(self, bbox1: torch.Tensor, bbox2: torch.Tensor):\n        \"\"\"Estimate the direction vector between two boxes.\"\"\"\n        if bbox1.sum() < 0 or bbox2.sum() < 0:\n            return torch.tensor((-1, -1))\n        cx1, cy1 = (bbox1[0] + bbox1[2]) / 2.0, (bbox1[1] + bbox1[3]) / 2.0\n        cx2, cy2 = (bbox2[0] + bbox2[2]) / 2.0, (bbox2[1] + bbox2[3]) / 2.0\n        speed = torch.tensor([cy2 - cy1, cx2 - cx1])\n        norm = torch.sqrt((speed[0])**2 + (speed[1])**2) + 1e-6\n        return speed / norm\n\n    def vel_direction_batch(self, bboxes1: torch.Tensor,\n                            bboxes2: torch.Tensor):\n        \"\"\"Estimate the direction vector given two batches of boxes.\"\"\"\n        cx1, cy1 = (bboxes1[:, 0] + bboxes1[:, 2]) / 2.0, (bboxes1[:, 1] +\n                                                           bboxes1[:, 3]) / 2.0\n        cx2, cy2 = (bboxes2[:, 0] + bboxes2[:, 2]) / 2.0, (bboxes2[:, 1] +\n                                                           bboxes2[:, 3]) / 2.0\n        speed_diff_y = cy2[None, :] - cy1[:, None]\n        speed_diff_x = cx2[None, :] - cx1[:, None]\n        speed = torch.cat((speed_diff_y[..., None], speed_diff_x[..., None]),\n                          dim=-1)\n        norm = torch.sqrt((speed[:, :, 0])**2 + (speed[:, :, 1])**2) + 1e-6\n        speed[:, :, 0] /= norm\n        speed[:, :, 1] /= norm\n        return speed\n\n    def k_step_observation(self, track: Dict):\n        \"\"\"return the observation k step away before.\"\"\"\n        obs_seqs = track.obs\n        num_obs = len(obs_seqs)\n        if num_obs == 0:\n            return torch.tensor((-1, -1, -1, -1)).to(track.obs[0].device)\n        elif num_obs > self.vel_delta_t:\n            if obs_seqs[num_obs - 1 - self.vel_delta_t] is not None:\n                return obs_seqs[num_obs - 1 - self.vel_delta_t]\n            else:\n                return self.last_obs(track)\n        else:\n            return self.last_obs(track)\n\n    def ocm_assign_ids(self,\n                       ids: List[int],\n                       det_bboxes: torch.Tensor,\n                       det_labels: torch.Tensor,\n                       det_scores: torch.Tensor,\n                       weight_iou_with_det_scores: Optional[bool] = False,\n                       match_iou_thr: Optional[float] = 0.5):\n        \"\"\"Apply Observation-Centric Momentum (OCM) to assign ids.\n\n        OCM adds movement direction consistency into the association cost\n        matrix. This term requires no additional assumption but from the\n        same linear motion assumption as the canonical Kalman Filter in SORT.\n\n        Args:\n            ids (list[int]): Tracking ids.\n            det_bboxes (Tensor): of shape (N, 4)\n            det_labels (Tensor): of shape (N,)\n            det_scores (Tensor): of shape (N,)\n            weight_iou_with_det_scores (bool, optional): Whether using\n                detection scores to weight IOU which is used for matching.\n                Defaults to False.\n            match_iou_thr (float, optional): Matching threshold.\n                Defaults to 0.5.\n\n        Returns:\n            tuple(int): The assigning ids.\n\n        OC-SORT uses velocity consistency besides IoU for association\n        \"\"\"\n        # get track_bboxes\n        track_bboxes = np.zeros((0, 4))\n        for id in ids:\n            track_bboxes = np.concatenate(\n                (track_bboxes, self.tracks[id].mean[:4][None]), axis=0)\n        track_bboxes = torch.from_numpy(track_bboxes).to(det_bboxes)\n        track_bboxes = bbox_cxcyah_to_xyxy(track_bboxes)\n\n        # compute distance\n        ious = bbox_overlaps(track_bboxes, det_bboxes)\n        if weight_iou_with_det_scores:\n            ious *= det_scores\n\n        # support multi-class association\n        track_labels = torch.tensor([\n            self.tracks[id]['labels'][-1] for id in ids\n        ]).to(det_bboxes.device)\n        cate_match = det_labels[None, :] == track_labels[:, None]\n        # to avoid det and track of different categories are matched\n        cate_cost = (1 - cate_match.int()) * 1e6\n\n        dists = (1 - ious + cate_cost).cpu().numpy()\n\n        if len(ids) > 0 and len(det_bboxes) > 0:\n            track_velocities = torch.stack(\n                [self.tracks[id].velocity for id in ids]).to(det_bboxes.device)\n            k_step_observations = torch.stack([\n                self.k_step_observation(self.tracks[id]) for id in ids\n            ]).to(det_bboxes.device)\n            # valid1: if the track has previous observations to estimate speed\n            # valid2: if the associated observation k steps ago is a detection\n            valid1 = track_velocities.sum(dim=1) != -2\n            valid2 = k_step_observations.sum(dim=1) != -4\n            valid = valid1 & valid2\n\n            vel_to_match = self.vel_direction_batch(k_step_observations,\n                                                    det_bboxes)\n            track_velocities = track_velocities[:, None, :].repeat(\n                1, det_bboxes.shape[0], 1)\n\n            angle_cos = (vel_to_match * track_velocities).sum(dim=-1)\n            angle_cos = torch.clamp(angle_cos, min=-1, max=1)\n            angle = torch.acos(angle_cos)  # [0, pi]\n            norm_angle = (angle - np.pi / 2.) / np.pi  # [-0.5, 0.5]\n            valid_matrix = valid[:, None].int().repeat(1, det_bboxes.shape[0])\n            # set non-valid entries 0\n            valid_norm_angle = norm_angle * valid_matrix\n\n            dists += valid_norm_angle.cpu().numpy() * self.vel_consist_weight\n\n        # bipartite match\n        if dists.size > 0:\n            cost, row, col = lap.lapjv(\n                dists, extend_cost=True, cost_limit=1 - match_iou_thr)\n        else:\n            row = np.zeros(len(ids)).astype(np.int32) - 1\n            col = np.zeros(len(det_bboxes)).astype(np.int32) - 1\n        return row, col\n\n    def last_obs(self, track: Dict):\n        \"\"\"extract the last associated observation.\"\"\"\n        for bbox in track.obs[::-1]:\n            if bbox is not None:\n                return bbox\n\n    def ocr_assign_ids(self,\n                       track_obs: torch.Tensor,\n                       last_track_labels: torch.Tensor,\n                       det_bboxes: torch.Tensor,\n                       det_labels: torch.Tensor,\n                       det_scores: torch.Tensor,\n                       weight_iou_with_det_scores: Optional[bool] = False,\n                       match_iou_thr: Optional[float] = 0.5):\n        \"\"\"association for Observation-Centric Recovery.\n\n        As try to recover tracks from being lost whose estimated velocity is\n        out- to-date, we use IoU-only matching strategy.\n\n        Args:\n            track_obs (Tensor): the list of historical associated\n                detections of tracks\n            det_bboxes (Tensor): of shape (N, 5), unmatched detections\n            det_labels (Tensor): of shape (N,)\n            det_scores (Tensor): of shape (N,)\n            weight_iou_with_det_scores (bool, optional): Whether using\n                detection scores to weight IOU which is used for matching.\n                Defaults to False.\n            match_iou_thr (float, optional): Matching threshold.\n                Defaults to 0.5.\n\n        Returns:\n            tuple(int): The assigning ids.\n        \"\"\"\n        # compute distance\n        ious = bbox_overlaps(track_obs, det_bboxes)\n        if weight_iou_with_det_scores:\n            ious *= det_scores\n\n        # support multi-class association\n        cate_match = det_labels[None, :] == last_track_labels[:, None]\n        # to avoid det and track of different categories are matched\n        cate_cost = (1 - cate_match.int()) * 1e6\n\n        dists = (1 - ious + cate_cost).cpu().numpy()\n\n        # bipartite match\n        if dists.size > 0:\n            cost, row, col = lap.lapjv(\n                dists, extend_cost=True, cost_limit=1 - match_iou_thr)\n        else:\n            row = np.zeros(len(track_obs)).astype(np.int32) - 1\n            col = np.zeros(len(det_bboxes)).astype(np.int32) - 1\n        return row, col\n\n    def online_smooth(self, track: Dict, obj: torch.Tensor):\n        \"\"\"Once a track is recovered from being lost, online smooth its\n        parameters to fix the error accumulated during being lost.\n\n        NOTE: you can use different virtual trajectory generation\n        strategies, we adopt the naive linear interpolation as default\n        \"\"\"\n        last_match_bbox = self.last_obs(track)\n        new_match_bbox = obj\n        unmatch_len = 0\n        for bbox in track.obs[::-1]:\n            if bbox is None:\n                unmatch_len += 1\n            else:\n                break\n        bbox_shift_per_step = (new_match_bbox - last_match_bbox) / (\n            unmatch_len + 1)\n        track.mean = track.saved_attr.mean\n        track.covariance = track.saved_attr.covariance\n        for i in range(unmatch_len):\n            virtual_bbox = last_match_bbox + (i + 1) * bbox_shift_per_step\n            virtual_bbox = bbox_xyxy_to_cxcyah(virtual_bbox[None, :])\n            virtual_bbox = virtual_bbox.squeeze(0).cpu().numpy()\n            track.mean, track.covariance = self.kf.update(\n                track.mean, track.covariance, virtual_bbox)\n\n    def track(self, data_sample: DetDataSample, **kwargs) -> InstanceData:\n        \"\"\"Tracking forward function.\n        NOTE: this implementation is slightly different from the original\n        OC-SORT implementation (https://github.com/noahcao/OC_SORT)that we\n        do association between detections and tentative/non-tentative tracks\n        independently while the original implementation combines them together.\n\n        Args:\n            data_sample (:obj:`DetDataSample`): The data sample.\n                It includes information such as `pred_instances`.\n\n        Returns:\n            :obj:`InstanceData`: Tracking results of the input images.\n            Each InstanceData usually contains ``bboxes``, ``labels``,\n            ``scores`` and ``instances_id``.\n        \"\"\"\n        metainfo = data_sample.metainfo\n        bboxes = data_sample.pred_instances.bboxes\n        labels = data_sample.pred_instances.labels\n        scores = data_sample.pred_instances.scores\n        frame_id = metainfo.get('frame_id', -1)\n        if frame_id == 0:\n            self.reset()\n        if not hasattr(self, 'kf'):\n            self.kf = self.motion\n\n        if self.empty or bboxes.size(0) == 0:\n            valid_inds = scores > self.init_track_thr\n            scores = scores[valid_inds]\n            bboxes = bboxes[valid_inds]\n            labels = labels[valid_inds]\n            num_new_tracks = bboxes.size(0)\n            ids = torch.arange(self.num_tracks,\n                               self.num_tracks + num_new_tracks).to(labels)\n            self.num_tracks += num_new_tracks\n        else:\n            # 0. init\n            ids = torch.full((bboxes.size(0), ),\n                             -1,\n                             dtype=labels.dtype,\n                             device=labels.device)\n\n            # get the detection bboxes for the first association\n            det_inds = scores > self.obj_score_thr\n            det_bboxes = bboxes[det_inds]\n            det_labels = labels[det_inds]\n            det_scores = scores[det_inds]\n            det_ids = ids[det_inds]\n\n            # 1. predict by Kalman Filter\n            for id in self.confirmed_ids:\n                # track is lost in previous frame\n                if self.tracks[id].frame_ids[-1] != frame_id - 1:\n                    self.tracks[id].mean[7] = 0\n                if self.tracks[id].tracked:\n                    self.tracks[id].saved_attr.mean = self.tracks[id].mean\n                    self.tracks[id].saved_attr.covariance = self.tracks[\n                        id].covariance\n                (self.tracks[id].mean,\n                 self.tracks[id].covariance) = self.kf.predict(\n                     self.tracks[id].mean, self.tracks[id].covariance)\n\n            # 2. match detections and tracks' predicted locations\n            match_track_inds, raw_match_det_inds = self.ocm_assign_ids(\n                self.confirmed_ids, det_bboxes, det_labels, det_scores,\n                self.weight_iou_with_det_scores, self.match_iou_thr)\n            # '-1' mean a detection box is not matched with tracklets in\n            # previous frame\n            valid = raw_match_det_inds > -1\n            det_ids[valid] = torch.tensor(\n                self.confirmed_ids)[raw_match_det_inds[valid]].to(labels)\n\n            match_det_bboxes = det_bboxes[valid]\n            match_det_labels = det_labels[valid]\n            match_det_scores = det_scores[valid]\n            match_det_ids = det_ids[valid]\n            assert (match_det_ids > -1).all()\n\n            # unmatched tracks and detections\n            unmatch_det_bboxes = det_bboxes[~valid]\n            unmatch_det_labels = det_labels[~valid]\n            unmatch_det_scores = det_scores[~valid]\n            unmatch_det_ids = det_ids[~valid]\n            assert (unmatch_det_ids == -1).all()\n\n            # 3. use unmatched detection bboxes from the first match to match\n            # the unconfirmed tracks\n            (tentative_match_track_inds,\n             tentative_match_det_inds) = self.ocm_assign_ids(\n                 self.unconfirmed_ids, unmatch_det_bboxes, unmatch_det_labels,\n                 unmatch_det_scores, self.weight_iou_with_det_scores,\n                 self.match_iou_thr)\n            valid = tentative_match_det_inds > -1\n            unmatch_det_ids[valid] = torch.tensor(self.unconfirmed_ids)[\n                tentative_match_det_inds[valid]].to(labels)\n\n            match_det_bboxes = torch.cat(\n                (match_det_bboxes, unmatch_det_bboxes[valid]), dim=0)\n            match_det_labels = torch.cat(\n                (match_det_labels, unmatch_det_labels[valid]), dim=0)\n            match_det_scores = torch.cat(\n                (match_det_scores, unmatch_det_scores[valid]), dim=0)\n            match_det_ids = torch.cat((match_det_ids, unmatch_det_ids[valid]),\n                                      dim=0)\n            assert (match_det_ids > -1).all()\n\n            unmatch_det_bboxes = unmatch_det_bboxes[~valid]\n            unmatch_det_labels = unmatch_det_labels[~valid]\n            unmatch_det_scores = unmatch_det_scores[~valid]\n            unmatch_det_ids = unmatch_det_ids[~valid]\n            assert (unmatch_det_ids == -1).all()\n\n            all_track_ids = [id for id, _ in self.tracks.items()]\n            unmatched_track_inds = torch.tensor(\n                [ind for ind in all_track_ids if ind not in match_det_ids])\n\n            if len(unmatched_track_inds) > 0:\n                # 4. still some tracks not associated yet, perform OCR\n                last_observations = []\n                for id in unmatched_track_inds:\n                    last_box = self.last_obs(self.tracks[id.item()])\n                    last_observations.append(last_box)\n                last_observations = torch.stack(last_observations)\n                last_track_labels = torch.tensor([\n                    self.tracks[id.item()]['labels'][-1]\n                    for id in unmatched_track_inds\n                ]).to(det_bboxes.device)\n\n                remain_det_ids = torch.full((unmatch_det_bboxes.size(0), ),\n                                            -1,\n                                            dtype=labels.dtype,\n                                            device=labels.device)\n\n                _, ocr_match_det_inds = self.ocr_assign_ids(\n                    last_observations, last_track_labels, unmatch_det_bboxes,\n                    unmatch_det_labels, unmatch_det_scores,\n                    self.weight_iou_with_det_scores, self.match_iou_thr)\n\n                valid = ocr_match_det_inds > -1\n                remain_det_ids[valid] = unmatched_track_inds.clone()[\n                    ocr_match_det_inds[valid]].to(labels)\n\n                ocr_match_det_bboxes = unmatch_det_bboxes[valid]\n                ocr_match_det_labels = unmatch_det_labels[valid]\n                ocr_match_det_scores = unmatch_det_scores[valid]\n                ocr_match_det_ids = remain_det_ids[valid]\n                assert (ocr_match_det_ids > -1).all()\n\n                ocr_unmatch_det_bboxes = unmatch_det_bboxes[~valid]\n                ocr_unmatch_det_labels = unmatch_det_labels[~valid]\n                ocr_unmatch_det_scores = unmatch_det_scores[~valid]\n                ocr_unmatch_det_ids = remain_det_ids[~valid]\n                assert (ocr_unmatch_det_ids == -1).all()\n\n                unmatch_det_bboxes = ocr_unmatch_det_bboxes\n                unmatch_det_labels = ocr_unmatch_det_labels\n                unmatch_det_scores = ocr_unmatch_det_scores\n                unmatch_det_ids = ocr_unmatch_det_ids\n                match_det_bboxes = torch.cat(\n                    (match_det_bboxes, ocr_match_det_bboxes), dim=0)\n                match_det_labels = torch.cat(\n                    (match_det_labels, ocr_match_det_labels), dim=0)\n                match_det_scores = torch.cat(\n                    (match_det_scores, ocr_match_det_scores), dim=0)\n                match_det_ids = torch.cat((match_det_ids, ocr_match_det_ids),\n                                          dim=0)\n\n            # 5. summarize the track results\n            for i in range(len(match_det_ids)):\n                det_bbox = match_det_bboxes[i]\n                track_id = match_det_ids[i].item()\n                if not self.tracks[track_id].tracked:\n                    # the track is lost before this step\n                    self.online_smooth(self.tracks[track_id], det_bbox)\n\n            for track_id in all_track_ids:\n                if track_id not in match_det_ids:\n                    self.tracks[track_id].tracked = False\n                    self.tracks[track_id].obs.append(None)\n\n            bboxes = torch.cat((match_det_bboxes, unmatch_det_bboxes), dim=0)\n            labels = torch.cat((match_det_labels, unmatch_det_labels), dim=0)\n            scores = torch.cat((match_det_scores, unmatch_det_scores), dim=0)\n            ids = torch.cat((match_det_ids, unmatch_det_ids), dim=0)\n            # 6. assign new ids\n            new_track_inds = ids == -1\n\n            ids[new_track_inds] = torch.arange(\n                self.num_tracks,\n                self.num_tracks + new_track_inds.sum()).to(labels)\n            self.num_tracks += new_track_inds.sum()\n\n        self.update(\n            ids=ids,\n            bboxes=bboxes,\n            labels=labels,\n            scores=scores,\n            frame_ids=frame_id)\n\n        # update pred_track_instances\n        pred_track_instances = InstanceData()\n        pred_track_instances.bboxes = bboxes\n        pred_track_instances.labels = labels\n        pred_track_instances.scores = scores\n        pred_track_instances.instances_id = ids\n        return pred_track_instances\n"
  },
  {
    "path": "mmdet/models/trackers/quasi_dense_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackDataSample\nfrom mmdet.structures.bbox import bbox_overlaps\nfrom .base_tracker import BaseTracker\n\n\n@MODELS.register_module()\nclass QuasiDenseTracker(BaseTracker):\n    \"\"\"Tracker for Quasi-Dense Tracking.\n\n    Args:\n        init_score_thr (float): The cls_score threshold to\n            initialize a new tracklet. Defaults to 0.8.\n        obj_score_thr (float): The cls_score threshold to\n            update a tracked tracklet. Defaults to 0.5.\n        match_score_thr (float): The match threshold. Defaults to 0.5.\n        memo_tracklet_frames (int): The most frames in a tracklet memory.\n            Defaults to 10.\n        memo_backdrop_frames (int): The most frames in the backdrops.\n            Defaults to 1.\n        memo_momentum (float): The momentum value for embeds updating.\n            Defaults to 0.8.\n        nms_conf_thr (float): The nms threshold for confidence.\n            Defaults to 0.5.\n        nms_backdrop_iou_thr (float): The nms threshold for backdrop IoU.\n            Defaults to 0.3.\n        nms_class_iou_thr (float): The nms threshold for class IoU.\n            Defaults to 0.7.\n        with_cats (bool): Whether to track with the same category.\n            Defaults to True.\n        match_metric (str): The match metric. Defaults to 'bisoftmax'.\n    \"\"\"\n\n    def __init__(self,\n                 init_score_thr: float = 0.8,\n                 obj_score_thr: float = 0.5,\n                 match_score_thr: float = 0.5,\n                 memo_tracklet_frames: int = 10,\n                 memo_backdrop_frames: int = 1,\n                 memo_momentum: float = 0.8,\n                 nms_conf_thr: float = 0.5,\n                 nms_backdrop_iou_thr: float = 0.3,\n                 nms_class_iou_thr: float = 0.7,\n                 with_cats: bool = True,\n                 match_metric: str = 'bisoftmax',\n                 **kwargs):\n        super().__init__(**kwargs)\n        assert 0 <= memo_momentum <= 1.0\n        assert memo_tracklet_frames >= 0\n        assert memo_backdrop_frames >= 0\n        self.init_score_thr = init_score_thr\n        self.obj_score_thr = obj_score_thr\n        self.match_score_thr = match_score_thr\n        self.memo_tracklet_frames = memo_tracklet_frames\n        self.memo_backdrop_frames = memo_backdrop_frames\n        self.memo_momentum = memo_momentum\n        self.nms_conf_thr = nms_conf_thr\n        self.nms_backdrop_iou_thr = nms_backdrop_iou_thr\n        self.nms_class_iou_thr = nms_class_iou_thr\n        self.with_cats = with_cats\n        assert match_metric in ['bisoftmax', 'softmax', 'cosine']\n        self.match_metric = match_metric\n\n        self.num_tracks = 0\n        self.tracks = dict()\n        self.backdrops = []\n\n    def reset(self):\n        \"\"\"Reset the buffer of the tracker.\"\"\"\n        self.num_tracks = 0\n        self.tracks = dict()\n        self.backdrops = []\n\n    def update(self, ids: Tensor, bboxes: Tensor, embeds: Tensor,\n               labels: Tensor, scores: Tensor, frame_id: int) -> None:\n        \"\"\"Tracking forward function.\n\n        Args:\n            ids (Tensor): of shape(N, ).\n            bboxes (Tensor): of shape (N, 5).\n            embeds (Tensor): of shape (N, 256).\n            labels (Tensor): of shape (N, ).\n            scores (Tensor): of shape (N, ).\n            frame_id (int): The id of current frame, 0-index.\n        \"\"\"\n        tracklet_inds = ids > -1\n\n        for id, bbox, embed, label, score in zip(ids[tracklet_inds],\n                                                 bboxes[tracklet_inds],\n                                                 embeds[tracklet_inds],\n                                                 labels[tracklet_inds],\n                                                 scores[tracklet_inds]):\n            id = int(id)\n            # update the tracked ones and initialize new tracks\n            if id in self.tracks.keys():\n                velocity = (bbox - self.tracks[id]['bbox']) / (\n                    frame_id - self.tracks[id]['last_frame'])\n                self.tracks[id]['bbox'] = bbox\n                self.tracks[id]['embed'] = (\n                    1 - self.memo_momentum\n                ) * self.tracks[id]['embed'] + self.memo_momentum * embed\n                self.tracks[id]['last_frame'] = frame_id\n                self.tracks[id]['label'] = label\n                self.tracks[id]['score'] = score\n                self.tracks[id]['velocity'] = (\n                    self.tracks[id]['velocity'] * self.tracks[id]['acc_frame']\n                    + velocity) / (\n                        self.tracks[id]['acc_frame'] + 1)\n                self.tracks[id]['acc_frame'] += 1\n            else:\n                self.tracks[id] = dict(\n                    bbox=bbox,\n                    embed=embed,\n                    label=label,\n                    score=score,\n                    last_frame=frame_id,\n                    velocity=torch.zeros_like(bbox),\n                    acc_frame=0)\n        # backdrop update according to IoU\n        backdrop_inds = torch.nonzero(ids == -1, as_tuple=False).squeeze(1)\n        ious = bbox_overlaps(bboxes[backdrop_inds], bboxes)\n        for i, ind in enumerate(backdrop_inds):\n            if (ious[i, :ind] > self.nms_backdrop_iou_thr).any():\n                backdrop_inds[i] = -1\n        backdrop_inds = backdrop_inds[backdrop_inds > -1]\n        # old backdrops would be removed at first\n        self.backdrops.insert(\n            0,\n            dict(\n                bboxes=bboxes[backdrop_inds],\n                embeds=embeds[backdrop_inds],\n                labels=labels[backdrop_inds]))\n\n        # pop memo\n        invalid_ids = []\n        for k, v in self.tracks.items():\n            if frame_id - v['last_frame'] >= self.memo_tracklet_frames:\n                invalid_ids.append(k)\n        for invalid_id in invalid_ids:\n            self.tracks.pop(invalid_id)\n\n        if len(self.backdrops) > self.memo_backdrop_frames:\n            self.backdrops.pop()\n\n    @property\n    def memo(self) -> Tuple[Tensor, ...]:\n        \"\"\"Get tracks memory.\"\"\"\n        memo_embeds = []\n        memo_ids = []\n        memo_bboxes = []\n        memo_labels = []\n        # velocity of tracks\n        memo_vs = []\n        # get tracks\n        for k, v in self.tracks.items():\n            memo_bboxes.append(v['bbox'][None, :])\n            memo_embeds.append(v['embed'][None, :])\n            memo_ids.append(k)\n            memo_labels.append(v['label'].view(1, 1))\n            memo_vs.append(v['velocity'][None, :])\n        memo_ids = torch.tensor(memo_ids, dtype=torch.long).view(1, -1)\n        # get backdrops\n        for backdrop in self.backdrops:\n            backdrop_ids = torch.full((1, backdrop['embeds'].size(0)),\n                                      -1,\n                                      dtype=torch.long)\n            backdrop_vs = torch.zeros_like(backdrop['bboxes'])\n            memo_bboxes.append(backdrop['bboxes'])\n            memo_embeds.append(backdrop['embeds'])\n            memo_ids = torch.cat([memo_ids, backdrop_ids], dim=1)\n            memo_labels.append(backdrop['labels'][:, None])\n            memo_vs.append(backdrop_vs)\n\n        memo_bboxes = torch.cat(memo_bboxes, dim=0)\n        memo_embeds = torch.cat(memo_embeds, dim=0)\n        memo_labels = torch.cat(memo_labels, dim=0).squeeze(1)\n        memo_vs = torch.cat(memo_vs, dim=0)\n        return memo_bboxes, memo_labels, memo_embeds, memo_ids.squeeze(\n            0), memo_vs\n\n    def track(self,\n              model: torch.nn.Module,\n              img: torch.Tensor,\n              feats: List[torch.Tensor],\n              data_sample: TrackDataSample,\n              rescale=True,\n              **kwargs) -> InstanceData:\n        \"\"\"Tracking forward function.\n\n        Args:\n            model (nn.Module): MOT model.\n            img (Tensor): of shape (T, C, H, W) encoding input image.\n                Typically these should be mean centered and std scaled.\n                The T denotes the number of key images and usually is 1 in\n                QDTrack method.\n            feats (list[Tensor]): Multi level feature maps of `img`.\n            data_sample (:obj:`TrackDataSample`): The data sample.\n                It includes information such as `pred_instances`.\n            rescale (bool, optional): If True, the bounding boxes should be\n                rescaled to fit the original scale of the image. Defaults to\n                True.\n\n        Returns:\n            :obj:`InstanceData`: Tracking results of the input images.\n            Each InstanceData usually contains ``bboxes``, ``labels``,\n            ``scores`` and ``instances_id``.\n        \"\"\"\n        metainfo = data_sample.metainfo\n        bboxes = data_sample.pred_instances.bboxes\n        labels = data_sample.pred_instances.labels\n        scores = data_sample.pred_instances.scores\n\n        frame_id = metainfo.get('frame_id', -1)\n        # create pred_track_instances\n        pred_track_instances = InstanceData()\n\n        # return zero bboxes if there is no track targets\n        if bboxes.shape[0] == 0:\n            ids = torch.zeros_like(labels)\n            pred_track_instances = data_sample.pred_instances.clone()\n            pred_track_instances.instances_id = ids\n            return pred_track_instances\n\n        # get track feats\n        rescaled_bboxes = bboxes.clone()\n        if rescale:\n            scale_factor = rescaled_bboxes.new_tensor(\n                metainfo['scale_factor']).repeat((1, 2))\n            rescaled_bboxes = rescaled_bboxes * scale_factor\n        track_feats = model.track_head.predict(feats, [rescaled_bboxes])\n        # sort according to the object_score\n        _, inds = scores.sort(descending=True)\n        bboxes = bboxes[inds]\n        scores = scores[inds]\n        labels = labels[inds]\n        embeds = track_feats[inds, :]\n\n        # duplicate removal for potential backdrops and cross classes\n        valids = bboxes.new_ones((bboxes.size(0)))\n        ious = bbox_overlaps(bboxes, bboxes)\n        for i in range(1, bboxes.size(0)):\n            thr = self.nms_backdrop_iou_thr if scores[\n                i] < self.obj_score_thr else self.nms_class_iou_thr\n            if (ious[i, :i] > thr).any():\n                valids[i] = 0\n        valids = valids == 1\n        bboxes = bboxes[valids]\n        scores = scores[valids]\n        labels = labels[valids]\n        embeds = embeds[valids, :]\n\n        # init ids container\n        ids = torch.full((bboxes.size(0), ), -1, dtype=torch.long)\n\n        # match if buffer is not empty\n        if bboxes.size(0) > 0 and not self.empty:\n            (memo_bboxes, memo_labels, memo_embeds, memo_ids,\n             memo_vs) = self.memo\n\n            if self.match_metric == 'bisoftmax':\n                feats = torch.mm(embeds, memo_embeds.t())\n                d2t_scores = feats.softmax(dim=1)\n                t2d_scores = feats.softmax(dim=0)\n                match_scores = (d2t_scores + t2d_scores) / 2\n            elif self.match_metric == 'softmax':\n                feats = torch.mm(embeds, memo_embeds.t())\n                match_scores = feats.softmax(dim=1)\n            elif self.match_metric == 'cosine':\n                match_scores = torch.mm(\n                    F.normalize(embeds, p=2, dim=1),\n                    F.normalize(memo_embeds, p=2, dim=1).t())\n            else:\n                raise NotImplementedError\n            # track with the same category\n            if self.with_cats:\n                cat_same = labels.view(-1, 1) == memo_labels.view(1, -1)\n                match_scores *= cat_same.float().to(match_scores.device)\n            # track according to match_scores\n            for i in range(bboxes.size(0)):\n                conf, memo_ind = torch.max(match_scores[i, :], dim=0)\n                id = memo_ids[memo_ind]\n                if conf > self.match_score_thr:\n                    if id > -1:\n                        # keep bboxes with high object score\n                        # and remove background bboxes\n                        if scores[i] > self.obj_score_thr:\n                            ids[i] = id\n                            match_scores[:i, memo_ind] = 0\n                            match_scores[i + 1:, memo_ind] = 0\n                        else:\n                            if conf > self.nms_conf_thr:\n                                ids[i] = -2\n        # initialize new tracks\n        new_inds = (ids == -1) & (scores > self.init_score_thr).cpu()\n        num_news = new_inds.sum()\n        ids[new_inds] = torch.arange(\n            self.num_tracks, self.num_tracks + num_news, dtype=torch.long)\n        self.num_tracks += num_news\n\n        self.update(ids, bboxes, embeds, labels, scores, frame_id)\n        tracklet_inds = ids > -1\n        # update pred_track_instances\n        pred_track_instances.bboxes = bboxes[tracklet_inds]\n        pred_track_instances.labels = labels[tracklet_inds]\n        pred_track_instances.scores = scores[tracklet_inds]\n        pred_track_instances.instances_id = ids[tracklet_inds]\n\n        return pred_track_instances\n"
  },
  {
    "path": "mmdet/models/trackers/sort_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData\n\ntry:\n    import motmetrics\n    from motmetrics.lap import linear_sum_assignment\nexcept ImportError:\n    motmetrics = None\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox_overlaps, bbox_xyxy_to_cxcyah\nfrom mmdet.utils import OptConfigType\nfrom ..utils import imrenormalize\nfrom .base_tracker import BaseTracker\n\n\n@MODELS.register_module()\nclass SORTTracker(BaseTracker):\n    \"\"\"Tracker for SORT/DeepSORT.\n\n    Args:\n        obj_score_thr (float, optional): Threshold to filter the objects.\n            Defaults to 0.3.\n        motion (dict): Configuration of motion. Defaults to None.\n        reid (dict, optional): Configuration for the ReID model.\n            - num_samples (int, optional): Number of samples to calculate the\n                feature embeddings of a track. Default to 10.\n            - image_scale (tuple, optional): Input scale of the ReID model.\n                Default to (256, 128).\n            - img_norm_cfg (dict, optional): Configuration to normalize the\n                input. Default to None.\n            - match_score_thr (float, optional): Similarity threshold for the\n                matching process. Default to 2.0.\n        match_iou_thr (float, optional): Threshold of the IoU matching process.\n            Defaults to 0.7.\n        num_tentatives (int, optional): Number of continuous frames to confirm\n            a track. Defaults to 3.\n    \"\"\"\n\n    def __init__(self,\n                 motion: Optional[dict] = None,\n                 obj_score_thr: float = 0.3,\n                 reid: dict = dict(\n                     num_samples=10,\n                     img_scale=(256, 128),\n                     img_norm_cfg=None,\n                     match_score_thr=2.0),\n                 match_iou_thr: float = 0.7,\n                 num_tentatives: int = 3,\n                 **kwargs):\n        if motmetrics is None:\n            raise RuntimeError('motmetrics is not installed,\\\n                 please install it by: pip install motmetrics')\n        super().__init__(**kwargs)\n        if motion is not None:\n            self.motion = TASK_UTILS.build(motion)\n            assert self.motion is not None, 'SORT/Deep SORT need KalmanFilter'\n        self.obj_score_thr = obj_score_thr\n        self.reid = reid\n        self.match_iou_thr = match_iou_thr\n        self.num_tentatives = num_tentatives\n\n    @property\n    def confirmed_ids(self) -> List:\n        \"\"\"Confirmed ids in the tracker.\"\"\"\n        ids = [id for id, track in self.tracks.items() if not track.tentative]\n        return ids\n\n    def init_track(self, id: int, obj: Tuple[Tensor]) -> None:\n        \"\"\"Initialize a track.\"\"\"\n        super().init_track(id, obj)\n        self.tracks[id].tentative = True\n        bbox = bbox_xyxy_to_cxcyah(self.tracks[id].bboxes[-1])  # size = (1, 4)\n        assert bbox.ndim == 2 and bbox.shape[0] == 1\n        bbox = bbox.squeeze(0).cpu().numpy()\n        self.tracks[id].mean, self.tracks[id].covariance = self.kf.initiate(\n            bbox)\n\n    def update_track(self, id: int, obj: Tuple[Tensor]) -> None:\n        \"\"\"Update a track.\"\"\"\n        super().update_track(id, obj)\n        if self.tracks[id].tentative:\n            if len(self.tracks[id]['bboxes']) >= self.num_tentatives:\n                self.tracks[id].tentative = False\n        bbox = bbox_xyxy_to_cxcyah(self.tracks[id].bboxes[-1])  # size = (1, 4)\n        assert bbox.ndim == 2 and bbox.shape[0] == 1\n        bbox = bbox.squeeze(0).cpu().numpy()\n        self.tracks[id].mean, self.tracks[id].covariance = self.kf.update(\n            self.tracks[id].mean, self.tracks[id].covariance, bbox)\n\n    def pop_invalid_tracks(self, frame_id: int) -> None:\n        \"\"\"Pop out invalid tracks.\"\"\"\n        invalid_ids = []\n        for k, v in self.tracks.items():\n            # case1: disappeared frames >= self.num_frames_retrain\n            case1 = frame_id - v['frame_ids'][-1] >= self.num_frames_retain\n            # case2: tentative tracks but not matched in this frame\n            case2 = v.tentative and v['frame_ids'][-1] != frame_id\n            if case1 or case2:\n                invalid_ids.append(k)\n        for invalid_id in invalid_ids:\n            self.tracks.pop(invalid_id)\n\n    def track(self,\n              model: torch.nn.Module,\n              img: Tensor,\n              data_sample: DetDataSample,\n              data_preprocessor: OptConfigType = None,\n              rescale: bool = False,\n              **kwargs) -> InstanceData:\n        \"\"\"Tracking forward function.\n\n        Args:\n            model (nn.Module): MOT model.\n            img (Tensor): of shape (T, C, H, W) encoding input image.\n                Typically these should be mean centered and std scaled.\n                The T denotes the number of key images and usually is 1 in\n                SORT method.\n            data_sample (:obj:`TrackDataSample`): The data sample.\n                It includes information such as `pred_det_instances`.\n            data_preprocessor (dict or ConfigDict, optional): The pre-process\n               config of :class:`TrackDataPreprocessor`.  it usually includes,\n                ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n            rescale (bool, optional): If True, the bounding boxes should be\n                rescaled to fit the original scale of the image. Defaults to\n                False.\n\n        Returns:\n            :obj:`InstanceData`: Tracking results of the input images.\n            Each InstanceData usually contains ``bboxes``, ``labels``,\n            ``scores`` and ``instances_id``.\n        \"\"\"\n        metainfo = data_sample.metainfo\n        bboxes = data_sample.pred_instances.bboxes\n        labels = data_sample.pred_instances.labels\n        scores = data_sample.pred_instances.scores\n\n        frame_id = metainfo.get('frame_id', -1)\n        if frame_id == 0:\n            self.reset()\n        if not hasattr(self, 'kf'):\n            self.kf = self.motion\n\n        if self.with_reid:\n            if self.reid.get('img_norm_cfg', False):\n                img_norm_cfg = dict(\n                    mean=data_preprocessor['mean'],\n                    std=data_preprocessor['std'],\n                    to_bgr=data_preprocessor['rgb_to_bgr'])\n                reid_img = imrenormalize(img, img_norm_cfg,\n                                         self.reid['img_norm_cfg'])\n            else:\n                reid_img = img.clone()\n\n        valid_inds = scores > self.obj_score_thr\n        bboxes = bboxes[valid_inds]\n        labels = labels[valid_inds]\n        scores = scores[valid_inds]\n\n        if self.empty or bboxes.size(0) == 0:\n            num_new_tracks = bboxes.size(0)\n            ids = torch.arange(\n                self.num_tracks,\n                self.num_tracks + num_new_tracks,\n                dtype=torch.long).to(bboxes.device)\n            self.num_tracks += num_new_tracks\n            if self.with_reid:\n                crops = self.crop_imgs(reid_img, metainfo, bboxes.clone(),\n                                       rescale)\n                if crops.size(0) > 0:\n                    embeds = model.reid(crops, mode='tensor')\n                else:\n                    embeds = crops.new_zeros((0, model.reid.head.out_channels))\n        else:\n            ids = torch.full((bboxes.size(0), ), -1,\n                             dtype=torch.long).to(bboxes.device)\n\n            # motion\n            self.tracks, costs = self.motion.track(self.tracks,\n                                                   bbox_xyxy_to_cxcyah(bboxes))\n\n            active_ids = self.confirmed_ids\n            if self.with_reid:\n                crops = self.crop_imgs(reid_img, metainfo, bboxes.clone(),\n                                       rescale)\n                embeds = model.reid(crops, mode='tensor')\n\n                # reid\n                if len(active_ids) > 0:\n                    track_embeds = self.get(\n                        'embeds',\n                        active_ids,\n                        self.reid.get('num_samples', None),\n                        behavior='mean')\n                    reid_dists = torch.cdist(track_embeds, embeds)\n\n                    # support multi-class association\n                    track_labels = torch.tensor([\n                        self.tracks[id]['labels'][-1] for id in active_ids\n                    ]).to(bboxes.device)\n                    cate_match = labels[None, :] == track_labels[:, None]\n                    cate_cost = (1 - cate_match.int()) * 1e6\n                    reid_dists = (reid_dists + cate_cost).cpu().numpy()\n\n                    valid_inds = [list(self.ids).index(_) for _ in active_ids]\n                    reid_dists[~np.isfinite(costs[valid_inds, :])] = np.nan\n\n                    row, col = linear_sum_assignment(reid_dists)\n                    for r, c in zip(row, col):\n                        dist = reid_dists[r, c]\n                        if not np.isfinite(dist):\n                            continue\n                        if dist <= self.reid['match_score_thr']:\n                            ids[c] = active_ids[r]\n\n            active_ids = [\n                id for id in self.ids if id not in ids\n                and self.tracks[id].frame_ids[-1] == frame_id - 1\n            ]\n            if len(active_ids) > 0:\n                active_dets = torch.nonzero(ids == -1).squeeze(1)\n                track_bboxes = self.get('bboxes', active_ids)\n                ious = bbox_overlaps(track_bboxes, bboxes[active_dets])\n\n                # support multi-class association\n                track_labels = torch.tensor([\n                    self.tracks[id]['labels'][-1] for id in active_ids\n                ]).to(bboxes.device)\n                cate_match = labels[None, active_dets] == track_labels[:, None]\n                cate_cost = (1 - cate_match.int()) * 1e6\n\n                dists = (1 - ious + cate_cost).cpu().numpy()\n\n                row, col = linear_sum_assignment(dists)\n                for r, c in zip(row, col):\n                    dist = dists[r, c]\n                    if dist < 1 - self.match_iou_thr:\n                        ids[active_dets[c]] = active_ids[r]\n\n            new_track_inds = ids == -1\n            ids[new_track_inds] = torch.arange(\n                self.num_tracks,\n                self.num_tracks + new_track_inds.sum(),\n                dtype=torch.long).to(bboxes.device)\n            self.num_tracks += new_track_inds.sum()\n\n        self.update(\n            ids=ids,\n            bboxes=bboxes,\n            scores=scores,\n            labels=labels,\n            embeds=embeds if self.with_reid else None,\n            frame_ids=frame_id)\n\n        # update pred_track_instances\n        pred_track_instances = InstanceData()\n        pred_track_instances.bboxes = bboxes\n        pred_track_instances.labels = labels\n        pred_track_instances.scores = scores\n        pred_track_instances.instances_id = ids\n\n        return pred_track_instances\n"
  },
  {
    "path": "mmdet/models/trackers/strongsort_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData\n\ntry:\n    import motmetrics\n    from motmetrics.lap import linear_sum_assignment\nexcept ImportError:\n    motmetrics = None\nfrom torch import Tensor\n\nfrom mmdet.models.utils import imrenormalize\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackDataSample\nfrom mmdet.structures.bbox import bbox_overlaps, bbox_xyxy_to_cxcyah\nfrom mmdet.utils import OptConfigType\nfrom .sort_tracker import SORTTracker\n\n\ndef cosine_distance(x: Tensor, y: Tensor) -> np.ndarray:\n    \"\"\"compute the cosine distance.\n\n    Args:\n        x (Tensor): embeddings with shape (N,C).\n        y (Tensor): embeddings with shape (M,C).\n\n    Returns:\n        ndarray: cosine distance with shape (N,M).\n    \"\"\"\n    x = x.cpu().numpy()\n    y = y.cpu().numpy()\n    x = x / np.linalg.norm(x, axis=1, keepdims=True)\n    y = y / np.linalg.norm(y, axis=1, keepdims=True)\n    dists = 1. - np.dot(x, y.T)\n    return dists\n\n\n@MODELS.register_module()\nclass StrongSORTTracker(SORTTracker):\n    \"\"\"Tracker for StrongSORT.\n\n    Args:\n        obj_score_thr (float, optional): Threshold to filter the objects.\n            Defaults to 0.6.\n        motion (dict): Configuration of motion. Defaults to None.\n        reid (dict, optional): Configuration for the ReID model.\n            - num_samples (int, optional): Number of samples to calculate the\n                feature embeddings of a track. Default to None.\n            - image_scale (tuple, optional): Input scale of the ReID model.\n                Default to (256, 128).\n            - img_norm_cfg (dict, optional): Configuration to normalize the\n                input. Default to None.\n            - match_score_thr (float, optional): Similarity threshold for the\n                matching process. Default to 0.3.\n            - motion_weight (float, optional): the weight of the motion cost.\n                Defaults to 0.02.\n        match_iou_thr (float, optional): Threshold of the IoU matching process.\n            Defaults to 0.7.\n        num_tentatives (int, optional): Number of continuous frames to confirm\n            a track. Defaults to 2.\n    \"\"\"\n\n    def __init__(self,\n                 motion: Optional[dict] = None,\n                 obj_score_thr: float = 0.6,\n                 reid: dict = dict(\n                     num_samples=None,\n                     img_scale=(256, 128),\n                     img_norm_cfg=None,\n                     match_score_thr=0.3,\n                     motion_weight=0.02),\n                 match_iou_thr: float = 0.7,\n                 num_tentatives: int = 2,\n                 **kwargs):\n        if motmetrics is None:\n            raise RuntimeError('motmetrics is not installed,\\\n                 please install it by: pip install motmetrics')\n        super().__init__(motion, obj_score_thr, reid, match_iou_thr,\n                         num_tentatives, **kwargs)\n\n    def update_track(self, id: int, obj: Tuple[Tensor]) -> None:\n        \"\"\"Update a track.\"\"\"\n        for k, v in zip(self.memo_items, obj):\n            v = v[None]\n            if self.momentums is not None and k in self.momentums:\n                m = self.momentums[k]\n                self.tracks[id][k] = (1 - m) * self.tracks[id][k] + m * v\n            else:\n                self.tracks[id][k].append(v)\n\n        if self.tracks[id].tentative:\n            if len(self.tracks[id]['bboxes']) >= self.num_tentatives:\n                self.tracks[id].tentative = False\n        bbox = bbox_xyxy_to_cxcyah(self.tracks[id].bboxes[-1])  # size = (1, 4)\n        assert bbox.ndim == 2 and bbox.shape[0] == 1\n        bbox = bbox.squeeze(0).cpu().numpy()\n        score = float(self.tracks[id].scores[-1].cpu())\n        self.tracks[id].mean, self.tracks[id].covariance = self.kf.update(\n            self.tracks[id].mean, self.tracks[id].covariance, bbox, score)\n\n    def track(self,\n              model: torch.nn.Module,\n              img: Tensor,\n              data_sample: TrackDataSample,\n              data_preprocessor: OptConfigType = None,\n              rescale: bool = False,\n              **kwargs) -> InstanceData:\n        \"\"\"Tracking forward function.\n\n        Args:\n            model (nn.Module): MOT model.\n            img (Tensor): of shape (T, C, H, W) encoding input image.\n                Typically these should be mean centered and std scaled.\n                The T denotes the number of key images and usually is 1 in\n                SORT method.\n            feats (list[Tensor]): Multi level feature maps of `img`.\n            data_sample (:obj:`TrackDataSample`): The data sample.\n                It includes information such as `pred_det_instances`.\n            data_preprocessor (dict or ConfigDict, optional): The pre-process\n               config of :class:`TrackDataPreprocessor`.  it usually includes,\n                ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n            rescale (bool, optional): If True, the bounding boxes should be\n                rescaled to fit the original scale of the image. Defaults to\n                False.\n\n        Returns:\n            :obj:`InstanceData`: Tracking results of the input images.\n            Each InstanceData usually contains ``bboxes``, ``labels``,\n            ``scores`` and ``instances_id``.\n        \"\"\"\n        metainfo = data_sample.metainfo\n        bboxes = data_sample.pred_instances.bboxes\n        labels = data_sample.pred_instances.labels\n        scores = data_sample.pred_instances.scores\n\n        frame_id = metainfo.get('frame_id', -1)\n        if frame_id == 0:\n            self.reset()\n        if not hasattr(self, 'kf'):\n            self.kf = self.motion\n\n        if self.with_reid:\n            if self.reid.get('img_norm_cfg', False):\n                img_norm_cfg = dict(\n                    mean=data_preprocessor.get('mean', [0, 0, 0]),\n                    std=data_preprocessor.get('std', [1, 1, 1]),\n                    to_bgr=data_preprocessor.get('rgb_to_bgr', False))\n                reid_img = imrenormalize(img, img_norm_cfg,\n                                         self.reid['img_norm_cfg'])\n            else:\n                reid_img = img.clone()\n\n        valid_inds = scores > self.obj_score_thr\n        bboxes = bboxes[valid_inds]\n        labels = labels[valid_inds]\n        scores = scores[valid_inds]\n\n        if self.empty or bboxes.size(0) == 0:\n            num_new_tracks = bboxes.size(0)\n            ids = torch.arange(\n                self.num_tracks,\n                self.num_tracks + num_new_tracks,\n                dtype=torch.long).to(bboxes.device)\n            self.num_tracks += num_new_tracks\n            if self.with_reid:\n                crops = self.crop_imgs(reid_img, metainfo, bboxes.clone(),\n                                       rescale)\n                if crops.size(0) > 0:\n                    embeds = model.reid(crops, mode='tensor')\n                else:\n                    embeds = crops.new_zeros((0, model.reid.head.out_channels))\n        else:\n            ids = torch.full((bboxes.size(0), ), -1,\n                             dtype=torch.long).to(bboxes.device)\n\n            # motion\n            if model.with_cmc:\n                num_samples = 1\n                self.tracks = model.cmc.track(self.last_img, img, self.tracks,\n                                              num_samples, frame_id, metainfo)\n\n            self.tracks, motion_dists = self.motion.track(\n                self.tracks, bbox_xyxy_to_cxcyah(bboxes))\n\n            active_ids = self.confirmed_ids\n            if self.with_reid:\n                crops = self.crop_imgs(reid_img, metainfo, bboxes.clone(),\n                                       rescale)\n                embeds = model.reid(crops, mode='tensor')\n\n                # reid\n                if len(active_ids) > 0:\n                    track_embeds = self.get(\n                        'embeds',\n                        active_ids,\n                        self.reid.get('num_samples', None),\n                        behavior='mean')\n                    reid_dists = cosine_distance(track_embeds, embeds)\n                    valid_inds = [list(self.ids).index(_) for _ in active_ids]\n                    reid_dists[~np.isfinite(motion_dists[\n                        valid_inds, :])] = np.nan\n\n                    weight_motion = self.reid.get('motion_weight')\n                    match_dists = (1 - weight_motion) * reid_dists + \\\n                        weight_motion * motion_dists[valid_inds]\n\n                    # support multi-class association\n                    track_labels = torch.tensor([\n                        self.tracks[id]['labels'][-1] for id in active_ids\n                    ]).to(bboxes.device)\n                    cate_match = labels[None, :] == track_labels[:, None]\n                    cate_cost = ((1 - cate_match.int()) * 1e6).cpu().numpy()\n                    match_dists = match_dists + cate_cost\n\n                    row, col = linear_sum_assignment(match_dists)\n                    for r, c in zip(row, col):\n                        dist = match_dists[r, c]\n                        if not np.isfinite(dist):\n                            continue\n                        if dist <= self.reid['match_score_thr']:\n                            ids[c] = active_ids[r]\n\n            active_ids = [\n                id for id in self.ids if id not in ids\n                and self.tracks[id].frame_ids[-1] == frame_id - 1\n            ]\n            if len(active_ids) > 0:\n                active_dets = torch.nonzero(ids == -1).squeeze(1)\n                track_bboxes = self.get('bboxes', active_ids)\n                ious = bbox_overlaps(track_bboxes, bboxes[active_dets])\n\n                # support multi-class association\n                track_labels = torch.tensor([\n                    self.tracks[id]['labels'][-1] for id in active_ids\n                ]).to(bboxes.device)\n                cate_match = labels[None, active_dets] == track_labels[:, None]\n                cate_cost = (1 - cate_match.int()) * 1e6\n\n                dists = (1 - ious + cate_cost).cpu().numpy()\n\n                row, col = linear_sum_assignment(dists)\n                for r, c in zip(row, col):\n                    dist = dists[r, c]\n                    if dist < 1 - self.match_iou_thr:\n                        ids[active_dets[c]] = active_ids[r]\n\n            new_track_inds = ids == -1\n            ids[new_track_inds] = torch.arange(\n                self.num_tracks,\n                self.num_tracks + new_track_inds.sum(),\n                dtype=torch.long).to(bboxes.device)\n            self.num_tracks += new_track_inds.sum()\n\n        self.update(\n            ids=ids,\n            bboxes=bboxes,\n            scores=scores,\n            labels=labels,\n            embeds=embeds if self.with_reid else None,\n            frame_ids=frame_id)\n        self.last_img = img\n\n        # update pred_track_instances\n        pred_track_instances = InstanceData()\n        pred_track_instances.bboxes = bboxes\n        pred_track_instances.labels = labels\n        pred_track_instances.scores = scores\n        pred_track_instances.instances_id = ids\n\n        return pred_track_instances\n"
  },
  {
    "path": "mmdet/models/tracking_heads/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .mask2former_track_head import Mask2FormerTrackHead\nfrom .quasi_dense_embed_head import QuasiDenseEmbedHead\nfrom .quasi_dense_track_head import QuasiDenseTrackHead\nfrom .roi_embed_head import RoIEmbedHead\nfrom .roi_track_head import RoITrackHead\n\n__all__ = [\n    'QuasiDenseEmbedHead', 'QuasiDenseTrackHead', 'Mask2FormerTrackHead',\n    'RoIEmbedHead', 'RoITrackHead'\n]\n"
  },
  {
    "path": "mmdet/models/tracking_heads/mask2former_track_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom collections import defaultdict\nfrom typing import Dict, List, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Conv2d\nfrom mmcv.ops import point_sample\nfrom mmengine.model import ModuleList\nfrom mmengine.model.weight_init import caffe2_xavier_init\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.dense_heads import AnchorFreeHead, MaskFormerHead\nfrom mmdet.models.utils import get_uncertain_point_coords_with_randomness\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import TrackDataSample, TrackSampleList\nfrom mmdet.structures.mask import mask2bbox\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptMultiConfig, reduce_mean)\nfrom ..layers import Mask2FormerTransformerDecoder\n\n\n@MODELS.register_module()\nclass Mask2FormerTrackHead(MaskFormerHead):\n    \"\"\"Implements the Mask2Former head.\n\n    See `Masked-attention Mask Transformer for Universal Image\n    Segmentation <https://arxiv.org/pdf/2112.01527>`_ for details.\n\n    Args:\n        in_channels (list[int]): Number of channels in the input feature map.\n        feat_channels (int): Number of channels for features.\n        out_channels (int): Number of channels for output.\n        num_classes (int): Number of VIS classes.\n        num_queries (int): Number of query in Transformer decoder.\n            Defaults to 100.\n        num_transformer_feat_level (int): Number of feats levels.\n            Defaults to 3.\n        pixel_decoder (:obj:`ConfigDict` or dict): Config for pixel\n            decoder.\n        enforce_decoder_input_project (bool, optional): Whether to add\n            a layer to change the embed_dim of transformer encoder in\n            pixel decoder to the embed_dim of transformer decoder.\n            Defaults to False.\n        transformer_decoder (:obj:`ConfigDict` or dict): Config for\n            transformer decoder.\n        positional_encoding (:obj:`ConfigDict` or dict): Config for\n            transformer decoder position encoding.\n            Defaults to `SinePositionalEncoding3D`.\n        loss_cls (:obj:`ConfigDict` or dict): Config of the classification\n            loss. Defaults to `CrossEntropyLoss`.\n        loss_mask (:obj:`ConfigDict` or dict): Config of the mask loss.\n            Defaults to 'CrossEntropyLoss'.\n        loss_dice (:obj:`ConfigDict` or dict): Config of the dice loss.\n            Defaults to 'DiceLoss'.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config of\n            Mask2Former head. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config of\n            Mask2Former head. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict], optional): Initialization config dict. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: List[int],\n                 feat_channels: int,\n                 out_channels: int,\n                 num_classes: int,\n                 num_frames: int = 2,\n                 num_queries: int = 100,\n                 num_transformer_feat_level: int = 3,\n                 pixel_decoder: ConfigType = ...,\n                 enforce_decoder_input_project: bool = False,\n                 transformer_decoder: ConfigType = ...,\n                 positional_encoding: ConfigType = dict(\n                     num_feats=128, normalize=True),\n                 loss_cls: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=False,\n                     loss_weight=2.0,\n                     reduction='mean',\n                     class_weight=[1.0] * 133 + [0.1]),\n                 loss_mask: ConfigType = dict(\n                     type='CrossEntropyLoss',\n                     use_sigmoid=True,\n                     reduction='mean',\n                     loss_weight=5.0),\n                 loss_dice: ConfigType = dict(\n                     type='DiceLoss',\n                     use_sigmoid=True,\n                     activate=True,\n                     reduction='mean',\n                     naive_dice=True,\n                     eps=1.0,\n                     loss_weight=5.0),\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        super(AnchorFreeHead, self).__init__(init_cfg=init_cfg)\n        self.num_classes = num_classes\n        self.num_frames = num_frames\n        self.num_queries = num_queries\n        self.num_transformer_feat_level = num_transformer_feat_level\n        self.num_transformer_feat_level = num_transformer_feat_level\n        self.num_heads = transformer_decoder.layer_cfg.cross_attn_cfg.num_heads\n        self.num_transformer_decoder_layers = transformer_decoder.num_layers\n        assert pixel_decoder.encoder.layer_cfg. \\\n            self_attn_cfg.num_levels == num_transformer_feat_level\n        pixel_decoder_ = copy.deepcopy(pixel_decoder)\n        pixel_decoder_.update(\n            in_channels=in_channels,\n            feat_channels=feat_channels,\n            out_channels=out_channels)\n        self.pixel_decoder = MODELS.build(pixel_decoder_)\n        self.transformer_decoder = Mask2FormerTransformerDecoder(\n            **transformer_decoder)\n        self.decoder_embed_dims = self.transformer_decoder.embed_dims\n\n        self.decoder_input_projs = ModuleList()\n        # from low resolution to high resolution\n        for _ in range(num_transformer_feat_level):\n            if (self.decoder_embed_dims != feat_channels\n                    or enforce_decoder_input_project):\n                self.decoder_input_projs.append(\n                    Conv2d(\n                        feat_channels, self.decoder_embed_dims, kernel_size=1))\n            else:\n                self.decoder_input_projs.append(nn.Identity())\n        self.decoder_positional_encoding = MODELS.build(positional_encoding)\n        self.query_embed = nn.Embedding(self.num_queries, feat_channels)\n        self.query_feat = nn.Embedding(self.num_queries, feat_channels)\n        # from low resolution to high resolution\n        self.level_embed = nn.Embedding(self.num_transformer_feat_level,\n                                        feat_channels)\n\n        self.cls_embed = nn.Linear(feat_channels, self.num_classes + 1)\n        self.mask_embed = nn.Sequential(\n            nn.Linear(feat_channels, feat_channels), nn.ReLU(inplace=True),\n            nn.Linear(feat_channels, feat_channels), nn.ReLU(inplace=True),\n            nn.Linear(feat_channels, out_channels))\n\n        self.test_cfg = test_cfg\n        self.train_cfg = train_cfg\n        if train_cfg:\n            self.assigner = TASK_UTILS.build(self.train_cfg.assigner)\n            self.sampler = TASK_UTILS.build(\n                # self.train_cfg.sampler, default_args=dict(context=self))\n                self.train_cfg['sampler'],\n                default_args=dict(context=self))\n            self.num_points = self.train_cfg.get('num_points', 12544)\n            self.oversample_ratio = self.train_cfg.get('oversample_ratio', 3.0)\n            self.importance_sample_ratio = self.train_cfg.get(\n                'importance_sample_ratio', 0.75)\n\n        self.class_weight = loss_cls.class_weight\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_mask = MODELS.build(loss_mask)\n        self.loss_dice = MODELS.build(loss_dice)\n\n    def init_weights(self) -> None:\n        for m in self.decoder_input_projs:\n            if isinstance(m, Conv2d):\n                caffe2_xavier_init(m, bias=0)\n\n        self.pixel_decoder.init_weights()\n\n        for p in self.transformer_decoder.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_normal_(p)\n\n    def preprocess_gt(self, batch_gt_instances: InstanceList) -> InstanceList:\n        \"\"\"Preprocess the ground truth for all images.\n\n        It aims to reorganize the `gt`. For example, in the\n        `batch_data_sample.gt_instances.mask`, its shape is\n        `(all_num_gts, h, w)`, but we don't know each gt belongs to which `img`\n        (assume `num_frames` is 2). So, this func used to reshape the `gt_mask`\n        to `(num_gts_per_img, num_frames, h, w)`. In addition, we can't\n        guarantee that the number of instances in these two images is equal,\n        so `-1` refers to nonexistent instances.\n\n        Args:\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``labels``, each is\n                ground truth labels of each bbox, with shape (num_gts, )\n                and ``masks``, each is ground truth masks of each instances\n                of an image, shape (num_gts, h, w).\n\n        Returns:\n            list[obj:`InstanceData`]: each contains the following keys\n\n                - labels (Tensor): Ground truth class indices\\\n                    for an image, with shape (n, ), n is the sum of\\\n                    number of stuff type and number of instance in an image.\n                - masks (Tensor): Ground truth mask for a\\\n                    image, with shape (n, t, h, w).\n        \"\"\"\n        final_batch_gt_instances = []\n        batch_size = len(batch_gt_instances) // self.num_frames\n        for batch_idx in range(batch_size):\n            pair_gt_insatences = batch_gt_instances[batch_idx *\n                                                    self.num_frames:batch_idx *\n                                                    self.num_frames +\n                                                    self.num_frames]\n\n            assert len(\n                pair_gt_insatences\n            ) > 1, f'mask2former for vis need multi frames to train, \\\n                but you only use {len(pair_gt_insatences)} frames'\n\n            _device = pair_gt_insatences[0].labels.device\n\n            for gt_instances in pair_gt_insatences:\n                gt_instances.masks = gt_instances.masks.to_tensor(\n                    dtype=torch.bool, device=_device)\n            all_ins_id = torch.cat([\n                gt_instances.instances_ids\n                for gt_instances in pair_gt_insatences\n            ])\n            all_ins_id = all_ins_id.unique().tolist()\n            map_ins_id = dict()\n            for i, ins_id in enumerate(all_ins_id):\n                map_ins_id[ins_id] = i\n\n            num_instances = len(all_ins_id)\n            mask_shape = [\n                num_instances, self.num_frames,\n                pair_gt_insatences[0].masks.shape[1],\n                pair_gt_insatences[0].masks.shape[2]\n            ]\n            gt_masks_per_video = torch.zeros(\n                mask_shape, dtype=torch.bool, device=_device)\n            gt_ids_per_video = torch.full((num_instances, self.num_frames),\n                                          -1,\n                                          dtype=torch.long,\n                                          device=_device)\n            gt_labels_per_video = torch.full((num_instances, ),\n                                             -1,\n                                             dtype=torch.long,\n                                             device=_device)\n\n            for frame_id in range(self.num_frames):\n                cur_frame_gts = pair_gt_insatences[frame_id]\n                ins_ids = cur_frame_gts.instances_ids.tolist()\n                for i, id in enumerate(ins_ids):\n                    gt_masks_per_video[map_ins_id[id],\n                                       frame_id, :, :] = cur_frame_gts.masks[i]\n                    gt_ids_per_video[map_ins_id[id],\n                                     frame_id] = cur_frame_gts.instances_ids[i]\n                    gt_labels_per_video[\n                        map_ins_id[id]] = cur_frame_gts.labels[i]\n\n            tmp_instances = InstanceData(\n                labels=gt_labels_per_video,\n                masks=gt_masks_per_video.long(),\n                instances_id=gt_ids_per_video)\n            final_batch_gt_instances.append(tmp_instances)\n\n        return final_batch_gt_instances\n\n    def _get_targets_single(self, cls_score: Tensor, mask_pred: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict) -> Tuple[Tensor]:\n        \"\"\"Compute classification and mask targets for one image.\n\n        Args:\n            cls_score (Tensor): Mask score logits from a single decoder layer\n                for one image. Shape (num_queries, cls_out_channels).\n            mask_pred (Tensor): Mask logits for a single decoder layer for one\n                image. Shape (num_queries, num_frames, h, w).\n            gt_instances (:obj:`InstanceData`): It contains ``labels`` and\n                ``masks``.\n            img_meta (dict): Image informtation.\n\n        Returns:\n            tuple[Tensor]: A tuple containing the following for one image.\n\n                - labels (Tensor): Labels of each image. \\\n                    shape (num_queries, ).\n                - label_weights (Tensor): Label weights of each image. \\\n                    shape (num_queries, ).\n                - mask_targets (Tensor): Mask targets of each image. \\\n                    shape (num_queries, num_frames, h, w).\n                - mask_weights (Tensor): Mask weights of each image. \\\n                    shape (num_queries, ).\n                - pos_inds (Tensor): Sampled positive indices for each \\\n                    image.\n                - neg_inds (Tensor): Sampled negative indices for each \\\n                    image.\n                - sampling_result (:obj:`SamplingResult`): Sampling results.\n        \"\"\"\n        # (num_gts, )\n        gt_labels = gt_instances.labels\n        # (num_gts, num_frames, h, w)\n        gt_masks = gt_instances.masks\n        # sample points\n        num_queries = cls_score.shape[0]\n        num_gts = gt_labels.shape[0]\n\n        point_coords = torch.rand((1, self.num_points, 2),\n                                  device=cls_score.device)\n\n        # shape (num_queries, num_points)\n        mask_points_pred = point_sample(mask_pred,\n                                        point_coords.repeat(num_queries, 1,\n                                                            1)).flatten(1)\n        # shape (num_gts, num_points)\n        gt_points_masks = point_sample(gt_masks.float(),\n                                       point_coords.repeat(num_gts, 1,\n                                                           1)).flatten(1)\n\n        sampled_gt_instances = InstanceData(\n            labels=gt_labels, masks=gt_points_masks)\n        sampled_pred_instances = InstanceData(\n            scores=cls_score, masks=mask_points_pred)\n        # assign and sample\n        assign_result = self.assigner.assign(\n            pred_instances=sampled_pred_instances,\n            gt_instances=sampled_gt_instances,\n            img_meta=img_meta)\n        pred_instances = InstanceData(scores=cls_score, masks=mask_pred)\n        sampling_result = self.sampler.sample(\n            assign_result=assign_result,\n            pred_instances=pred_instances,\n            gt_instances=gt_instances)\n        pos_inds = sampling_result.pos_inds\n        neg_inds = sampling_result.neg_inds\n\n        # label target\n        labels = gt_labels.new_full((self.num_queries, ),\n                                    self.num_classes,\n                                    dtype=torch.long)\n        labels[pos_inds] = gt_labels[sampling_result.pos_assigned_gt_inds]\n        label_weights = gt_labels.new_ones((self.num_queries, ))\n\n        # mask target\n        mask_targets = gt_masks[sampling_result.pos_assigned_gt_inds]\n        mask_weights = mask_pred.new_zeros((self.num_queries, ))\n        mask_weights[pos_inds] = 1.0\n\n        return (labels, label_weights, mask_targets, mask_weights, pos_inds,\n                neg_inds, sampling_result)\n\n    def _loss_by_feat_single(self, cls_scores: Tensor, mask_preds: Tensor,\n                             batch_gt_instances: List[InstanceData],\n                             batch_img_metas: List[dict]) -> Tuple[Tensor]:\n        \"\"\"Loss function for outputs from a single decoder layer.\n\n        Args:\n            cls_scores (Tensor): Mask score logits from a single decoder layer\n                for all images. Shape (batch_size, num_queries,\n                cls_out_channels). Note `cls_out_channels` should include\n                background.\n            mask_preds (Tensor): Mask logits for a pixel decoder for all\n                images. Shape (batch_size, num_queries, num_frames,h, w).\n            batch_gt_instances (list[obj:`InstanceData`]): each contains\n                ``labels`` and ``masks``.\n            batch_img_metas (list[dict]): List of image meta information.\n\n        Returns:\n            tuple[Tensor]: Loss components for outputs from a single \\\n                decoder layer.\n        \"\"\"\n        num_imgs = cls_scores.size(0)\n        cls_scores_list = [cls_scores[i] for i in range(num_imgs)]\n        mask_preds_list = [mask_preds[i] for i in range(num_imgs)]\n        (labels_list, label_weights_list, mask_targets_list, mask_weights_list,\n         avg_factor) = self.get_targets(cls_scores_list, mask_preds_list,\n                                        batch_gt_instances, batch_img_metas)\n        # shape (batch_size, num_queries)\n        labels = torch.stack(labels_list, dim=0)\n        # shape (batch_size, num_queries)\n        label_weights = torch.stack(label_weights_list, dim=0)\n        # shape (num_total_gts, num_frames, h, w)\n        mask_targets = torch.cat(mask_targets_list, dim=0)\n        # shape (batch_size, num_queries)\n        mask_weights = torch.stack(mask_weights_list, dim=0)\n\n        # classfication loss\n        # shape (batch_size * num_queries, )\n        cls_scores = cls_scores.flatten(0, 1)\n        labels = labels.flatten(0, 1)\n        label_weights = label_weights.flatten(0, 1)\n\n        class_weight = cls_scores.new_tensor(self.class_weight)\n        loss_cls = self.loss_cls(\n            cls_scores,\n            labels,\n            label_weights,\n            avg_factor=class_weight[labels].sum())\n\n        num_total_masks = reduce_mean(cls_scores.new_tensor([avg_factor]))\n        num_total_masks = max(num_total_masks, 1)\n\n        # extract positive ones\n        # shape (batch_size, num_queries, num_frames, h, w)\n        # -> (num_total_gts, num_frames, h, w)\n        mask_preds = mask_preds[mask_weights > 0]\n\n        if mask_targets.shape[0] == 0:\n            # zero match\n            loss_dice = mask_preds.sum()\n            loss_mask = mask_preds.sum()\n            return loss_cls, loss_mask, loss_dice\n\n        with torch.no_grad():\n            points_coords = get_uncertain_point_coords_with_randomness(\n                mask_preds.flatten(0, 1).unsqueeze(1), None, self.num_points,\n                self.oversample_ratio, self.importance_sample_ratio)\n            # shape (num_total_gts * num_frames, h, w) ->\n            # (num_total_gts, num_points)\n            mask_point_targets = point_sample(\n                mask_targets.flatten(0, 1).unsqueeze(1).float(),\n                points_coords).squeeze(1)\n        # shape (num_total_gts * num_frames, num_points)\n        mask_point_preds = point_sample(\n            mask_preds.flatten(0, 1).unsqueeze(1), points_coords).squeeze(1)\n\n        # dice loss\n        loss_dice = self.loss_dice(\n            mask_point_preds, mask_point_targets, avg_factor=num_total_masks)\n\n        # mask loss\n        # shape (num_total_gts * num_frames, num_points) ->\n        # (num_total_gts * num_frames * num_points, )\n        mask_point_preds = mask_point_preds.reshape(-1)\n        # shape (num_total_gts, num_points) -> (num_total_gts * num_points, )\n        mask_point_targets = mask_point_targets.reshape(-1)\n        loss_mask = self.loss_mask(\n            mask_point_preds,\n            mask_point_targets,\n            avg_factor=num_total_masks * self.num_points / self.num_frames)\n\n        return loss_cls, loss_mask, loss_dice\n\n    def _forward_head(\n        self, decoder_out: Tensor, mask_feature: Tensor,\n        attn_mask_target_size: Tuple[int,\n                                     int]) -> Tuple[Tensor, Tensor, Tensor]:\n        \"\"\"Forward for head part which is called after every decoder layer.\n\n        Args:\n            decoder_out (Tensor): in shape (num_queries, batch_size, c).\n            mask_feature (Tensor): in shape (batch_size, t, c, h, w).\n            attn_mask_target_size (tuple[int, int]): target attention\n                mask size.\n\n        Returns:\n            tuple: A tuple contain three elements.\n\n                - cls_pred (Tensor): Classification scores in shape \\\n                    (batch_size, num_queries, cls_out_channels). \\\n                    Note `cls_out_channels` should include background.\n                - mask_pred (Tensor): Mask scores in shape \\\n                    (batch_size, num_queries,h, w).\n                - attn_mask (Tensor): Attention mask in shape \\\n                    (batch_size * num_heads, num_queries, h, w).\n        \"\"\"\n        decoder_out = self.transformer_decoder.post_norm(decoder_out)\n        cls_pred = self.cls_embed(decoder_out)\n        mask_embed = self.mask_embed(decoder_out)\n\n        # shape (batch_size, num_queries, t, h, w)\n        mask_pred = torch.einsum('bqc,btchw->bqthw', mask_embed, mask_feature)\n        b, q, t, _, _ = mask_pred.shape\n\n        attn_mask = F.interpolate(\n            mask_pred.flatten(0, 1),\n            attn_mask_target_size,\n            mode='bilinear',\n            align_corners=False).view(b, q, t, attn_mask_target_size[0],\n                                      attn_mask_target_size[1])\n\n        # shape (batch_size, num_queries, t, h, w) ->\n        # (batch_size, num_queries, t*h*w) ->\n        # (batch_size, num_head, num_queries, t*h*w) ->\n        # (batch_size*num_head, num_queries, t*h*w)\n        attn_mask = attn_mask.flatten(2).unsqueeze(1).repeat(\n            (1, self.num_heads, 1, 1)).flatten(0, 1)\n        attn_mask = attn_mask.sigmoid() < 0.5\n        attn_mask = attn_mask.detach()\n\n        return cls_pred, mask_pred, attn_mask\n\n    def forward(\n            self, x: List[Tensor], data_samples: TrackDataSample\n    ) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Forward function.\n\n        Args:\n            x (list[Tensor]): Multi scale Features from the\n                upstream network, each is a 4D-tensor.\n            data_samples (List[:obj:`TrackDataSample`]): The Data\n                Samples. It usually includes information such as `gt_instance`.\n\n        Returns:\n            tuple[list[Tensor]]: A tuple contains two elements.\n\n                - cls_pred_list (list[Tensor)]: Classification logits \\\n                    for each decoder layer. Each is a 3D-tensor with shape \\\n                    (batch_size, num_queries, cls_out_channels). \\\n                    Note `cls_out_channels` should include background.\n                - mask_pred_list (list[Tensor]): Mask logits for each \\\n                    decoder layer. Each with shape (batch_size, num_queries, \\\n                    h, w).\n        \"\"\"\n        mask_features, multi_scale_memorys = self.pixel_decoder(x)\n        bt, c_m, h_m, w_m = mask_features.shape\n        batch_size = bt // self.num_frames if self.training else 1\n        t = bt // batch_size\n        mask_features = mask_features.view(batch_size, t, c_m, h_m, w_m)\n        # multi_scale_memorys (from low resolution to high resolution)\n        decoder_inputs = []\n        decoder_positional_encodings = []\n        for i in range(self.num_transformer_feat_level):\n            decoder_input = self.decoder_input_projs[i](multi_scale_memorys[i])\n            decoder_input = decoder_input.flatten(2)\n            level_embed = self.level_embed.weight[i][None, :, None]\n            decoder_input = decoder_input + level_embed\n            _, c, hw = decoder_input.shape\n            # shape (batch_size*t, c, h, w) ->\n            # (batch_size, t, c, hw) ->\n            # (batch_size, t*h*w, c)\n            decoder_input = decoder_input.view(batch_size, t, c,\n                                               hw).permute(0, 1, 3,\n                                                           2).flatten(1, 2)\n            # shape (batch_size, c, h, w) -> (h*w, batch_size, c)\n            mask = decoder_input.new_zeros(\n                (batch_size, t) + multi_scale_memorys[i].shape[-2:],\n                dtype=torch.bool)\n            decoder_positional_encoding = self.decoder_positional_encoding(\n                mask)\n            decoder_positional_encoding = decoder_positional_encoding.flatten(\n                3).permute(0, 1, 3, 2).flatten(1, 2)\n            decoder_inputs.append(decoder_input)\n            decoder_positional_encodings.append(decoder_positional_encoding)\n        # shape (num_queries, c) -> (batch_size, num_queries, c)\n        query_feat = self.query_feat.weight.unsqueeze(0).repeat(\n            (batch_size, 1, 1))\n        query_embed = self.query_embed.weight.unsqueeze(0).repeat(\n            (batch_size, 1, 1))\n\n        cls_pred_list = []\n        mask_pred_list = []\n        cls_pred, mask_pred, attn_mask = self._forward_head(\n            query_feat, mask_features, multi_scale_memorys[0].shape[-2:])\n        cls_pred_list.append(cls_pred)\n        mask_pred_list.append(mask_pred)\n\n        for i in range(self.num_transformer_decoder_layers):\n            level_idx = i % self.num_transformer_feat_level\n            # if a mask is all True(all background), then set it all False.\n            attn_mask[torch.where(\n                attn_mask.sum(-1) == attn_mask.shape[-1])] = False\n\n            # cross_attn + self_attn\n            layer = self.transformer_decoder.layers[i]\n            query_feat = layer(\n                query=query_feat,\n                key=decoder_inputs[level_idx],\n                value=decoder_inputs[level_idx],\n                query_pos=query_embed,\n                key_pos=decoder_positional_encodings[level_idx],\n                cross_attn_mask=attn_mask,\n                query_key_padding_mask=None,\n                # here we do not apply masking on padded region\n                key_padding_mask=None)\n            cls_pred, mask_pred, attn_mask = self._forward_head(\n                query_feat, mask_features, multi_scale_memorys[\n                    (i + 1) % self.num_transformer_feat_level].shape[-2:])\n\n            cls_pred_list.append(cls_pred)\n            mask_pred_list.append(mask_pred)\n\n        return cls_pred_list, mask_pred_list\n\n    def loss(\n        self,\n        x: Tuple[Tensor],\n        data_samples: TrackSampleList,\n    ) -> Dict[str, Tensor]:\n        \"\"\"Perform forward propagation and loss calculation of the track head\n        on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the upstream\n                network, each is a 4D-tensor.\n            data_samples (List[:obj:`TrackDataSample`]): The Data\n                Samples. It usually includes information such as `gt_instance`.\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components\n        \"\"\"\n        batch_img_metas = []\n        batch_gt_instances = []\n\n        for data_sample in data_samples:\n            video_img_metas = defaultdict(list)\n            for image_idx in range(len(data_sample)):\n                batch_gt_instances.append(data_sample[image_idx].gt_instances)\n                for key, value in data_sample[image_idx].metainfo.items():\n                    video_img_metas[key].append(value)\n            batch_img_metas.append(video_img_metas)\n\n        # forward\n        all_cls_scores, all_mask_preds = self(x, data_samples)\n\n        # preprocess ground truth\n        batch_gt_instances = self.preprocess_gt(batch_gt_instances)\n        # loss\n        losses = self.loss_by_feat(all_cls_scores, all_mask_preds,\n                                   batch_gt_instances, batch_img_metas)\n\n        return losses\n\n    def predict(self,\n                x: Tuple[Tensor],\n                data_samples: TrackDataSample,\n                rescale: bool = True) -> InstanceList:\n        \"\"\"Test without augmentation.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            data_samples (List[:obj:`TrackDataSample`]): The Data\n                Samples. It usually includes information such as `gt_instance`.\n            rescale (bool, Optional): If False, then returned bboxes and masks\n                will fit the scale of img, otherwise, returned bboxes and masks\n                will fit the scale of original image shape. Defaults to True.\n\n        Returns:\n            list[obj:`InstanceData`]: each contains the following keys\n                - labels (Tensor): Prediction class indices\\\n                    for an image, with shape (n, ), n is the sum of\\\n                    number of stuff type and number of instance in an image.\n                - masks (Tensor): Prediction mask for a\\\n                    image, with shape (n, t, h, w).\n        \"\"\"\n\n        batch_img_metas = [\n            data_samples[img_idx].metainfo\n            for img_idx in range(len(data_samples))\n        ]\n        all_cls_scores, all_mask_preds = self(x, data_samples)\n        mask_cls_results = all_cls_scores[-1]\n        mask_pred_results = all_mask_preds[-1]\n\n        mask_cls_results = mask_cls_results[0]\n        # upsample masks\n        img_shape = batch_img_metas[0]['batch_input_shape']\n        mask_pred_results = F.interpolate(\n            mask_pred_results[0],\n            size=(img_shape[0], img_shape[1]),\n            mode='bilinear',\n            align_corners=False)\n\n        results = self.predict_by_feat(mask_cls_results, mask_pred_results,\n                                       batch_img_metas)\n        return results\n\n    def predict_by_feat(self,\n                        mask_cls_results: List[Tensor],\n                        mask_pred_results: List[Tensor],\n                        batch_img_metas: List[dict],\n                        rescale: bool = True) -> InstanceList:\n        \"\"\"Get top-10 predictions.\n\n        Args:\n            mask_cls_results (Tensor): Mask classification logits,\\\n                shape (batch_size, num_queries, cls_out_channels).\n                Note `cls_out_channels` should include background.\n            mask_pred_results (Tensor): Mask logits, shape \\\n                (batch_size, num_queries, h, w).\n            batch_img_metas (list[dict]): List of image meta information.\n            rescale (bool, Optional): If False, then returned bboxes and masks\n                will fit the scale of img, otherwise, returned bboxes and masks\n                will fit the scale of original image shape. Defaults to True.\n\n        Returns:\n            list[obj:`InstanceData`]: each contains the following keys\n                - labels (Tensor): Prediction class indices\\\n                    for an image, with shape (n, ), n is the sum of\\\n                    number of stuff type and number of instance in an image.\n                - masks (Tensor): Prediction mask for a\\\n                    image, with shape (n, t, h, w).\n        \"\"\"\n        results = []\n        if len(mask_cls_results) > 0:\n            scores = F.softmax(mask_cls_results, dim=-1)[:, :-1]\n            labels = torch.arange(self.num_classes).unsqueeze(0).repeat(\n                self.num_queries, 1).flatten(0, 1).to(scores.device)\n            # keep top-10 predictions\n            scores_per_image, topk_indices = scores.flatten(0, 1).topk(\n                10, sorted=False)\n            labels_per_image = labels[topk_indices]\n            topk_indices = topk_indices // self.num_classes\n            mask_pred_results = mask_pred_results[topk_indices]\n\n            img_shape = batch_img_metas[0]['img_shape']\n            mask_pred_results = \\\n                mask_pred_results[:, :, :img_shape[0], :img_shape[1]]\n            if rescale:\n                # return result in original resolution\n                ori_height, ori_width = batch_img_metas[0]['ori_shape'][:2]\n                mask_pred_results = F.interpolate(\n                    mask_pred_results,\n                    size=(ori_height, ori_width),\n                    mode='bilinear',\n                    align_corners=False)\n\n            masks = mask_pred_results > 0.\n\n            # format top-10 predictions\n            for img_idx in range(len(batch_img_metas)):\n                pred_track_instances = InstanceData()\n\n                pred_track_instances.masks = masks[:, img_idx]\n                pred_track_instances.bboxes = mask2bbox(masks[:, img_idx])\n                pred_track_instances.labels = labels_per_image\n                pred_track_instances.scores = scores_per_image\n                pred_track_instances.instances_id = torch.arange(10)\n\n                results.append(pred_track_instances)\n\n            return results\n"
  },
  {
    "path": "mmdet/models/tracking_heads/quasi_dense_embed_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.models.task_modules import SamplingResult\nfrom mmdet.registry import MODELS\nfrom ..task_modules.tracking import embed_similarity\n\n\n@MODELS.register_module()\nclass QuasiDenseEmbedHead(BaseModule):\n    \"\"\"The quasi-dense roi embed head.\n\n    Args:\n        embed_channels (int): The input channel of embed features.\n            Defaults to 256.\n        softmax_temp (int): Softmax temperature. Defaults to -1.\n        loss_track (dict): The loss function for tracking. Defaults to\n            MultiPosCrossEntropyLoss.\n        loss_track_aux (dict): The auxiliary loss function for tracking.\n            Defaults to MarginL2Loss.\n        init_cfg (:obj:`ConfigDict` or dict or list[:obj:`ConfigDict` or \\\n            dict]): Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 num_convs: int = 0,\n                 num_fcs: int = 0,\n                 roi_feat_size: int = 7,\n                 in_channels: int = 256,\n                 conv_out_channels: int = 256,\n                 with_avg_pool: bool = False,\n                 fc_out_channels: int = 1024,\n                 conv_cfg: Optional[dict] = None,\n                 norm_cfg: Optional[dict] = None,\n                 embed_channels: int = 256,\n                 softmax_temp: int = -1,\n                 loss_track: Optional[dict] = None,\n                 loss_track_aux: dict = dict(\n                     type='MarginL2Loss',\n                     sample_ratio=3,\n                     margin=0.3,\n                     loss_weight=1.0,\n                     hard_mining=True),\n                 init_cfg: dict = dict(\n                     type='Xavier',\n                     layer='Linear',\n                     distribution='uniform',\n                     bias=0,\n                     override=dict(\n                         type='Normal',\n                         name='fc_embed',\n                         mean=0,\n                         std=0.01,\n                         bias=0))):\n        super(QuasiDenseEmbedHead, self).__init__(init_cfg=init_cfg)\n        self.num_convs = num_convs\n        self.num_fcs = num_fcs\n        self.roi_feat_size = _pair(roi_feat_size)\n        self.roi_feat_area = self.roi_feat_size[0] * self.roi_feat_size[1]\n        self.in_channels = in_channels\n        self.conv_out_channels = conv_out_channels\n        self.with_avg_pool = with_avg_pool\n        self.fc_out_channels = fc_out_channels\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n\n        if self.with_avg_pool:\n            self.avg_pool = nn.AvgPool2d(self.roi_feat_size)\n        # add convs and fcs\n        self.convs, self.fcs, self.last_layer_dim = self._add_conv_fc_branch(\n            self.num_convs, self.num_fcs, self.in_channels)\n        self.relu = nn.ReLU(inplace=True)\n\n        if loss_track is None:\n            loss_track = dict(\n                type='MultiPosCrossEntropyLoss', loss_weight=0.25)\n\n        self.fc_embed = nn.Linear(self.last_layer_dim, embed_channels)\n        self.softmax_temp = softmax_temp\n        self.loss_track = MODELS.build(loss_track)\n        if loss_track_aux is not None:\n            self.loss_track_aux = MODELS.build(loss_track_aux)\n        else:\n            self.loss_track_aux = None\n\n    def _add_conv_fc_branch(\n            self, num_branch_convs: int, num_branch_fcs: int,\n            in_channels: int) -> Tuple[nn.ModuleList, nn.ModuleList, int]:\n        \"\"\"Add shared or separable branch. convs -> avg pool (optional) -> fcs.\n\n        Args:\n            num_branch_convs (int): The number of convoluational layers.\n            num_branch_fcs (int): The number of fully connection layers.\n            in_channels (int): The input channel of roi features.\n\n        Returns:\n            Tuple[nn.ModuleList, nn.ModuleList, int]: The convs, fcs and the\n                last layer dimension.\n        \"\"\"\n        last_layer_dim = in_channels\n        # add branch specific conv layers\n        branch_convs = nn.ModuleList()\n        if num_branch_convs > 0:\n            for i in range(num_branch_convs):\n                conv_in_channels = (\n                    last_layer_dim if i == 0 else self.conv_out_channels)\n                branch_convs.append(\n                    ConvModule(\n                        conv_in_channels,\n                        self.conv_out_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg))\n            last_layer_dim = self.conv_out_channels\n\n        # add branch specific fc layers\n        branch_fcs = nn.ModuleList()\n        if num_branch_fcs > 0:\n            if not self.with_avg_pool:\n                last_layer_dim *= self.roi_feat_area\n            for i in range(num_branch_fcs):\n                fc_in_channels = (\n                    last_layer_dim if i == 0 else self.fc_out_channels)\n                branch_fcs.append(\n                    nn.Linear(fc_in_channels, self.fc_out_channels))\n            last_layer_dim = self.fc_out_channels\n\n        return branch_convs, branch_fcs, last_layer_dim\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            x (Tensor): The input features from ROI head.\n\n        Returns:\n            Tensor: The embedding feature map.\n        \"\"\"\n\n        if self.num_convs > 0:\n            for conv in self.convs:\n                x = conv(x)\n        x = x.flatten(1)\n        if self.num_fcs > 0:\n            for fc in self.fcs:\n                x = self.relu(fc(x))\n        x = self.fc_embed(x)\n        return x\n\n    def get_targets(\n            self, gt_match_indices: List[Tensor],\n            key_sampling_results: List[SamplingResult],\n            ref_sampling_results: List[SamplingResult]) -> Tuple[List, List]:\n        \"\"\"Calculate the track targets and track weights for all samples in a\n        batch according to the sampling_results.\n\n        Args:\n            gt_match_indices (list(Tensor)): Mapping from gt_instance_ids to\n                ref_gt_instance_ids of the same tracklet in a pair of images.\n            key_sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            ref_sampling_results (List[obj:SamplingResult]): Assign results of\n                all reference images in a batch after sampling.\n\n        Returns:\n            Tuple[list[Tensor]]: Association results.\n            Containing the following list of Tensors:\n\n                - track_targets (list[Tensor]): The mapping instance ids from\n                    all positive proposals in the key image to all proposals\n                    in the reference image, each tensor in list has\n                    shape (len(key_pos_bboxes), len(ref_bboxes)).\n                - track_weights (list[Tensor]): Loss weights for all positive\n                    proposals in a batch, each tensor in list has\n                    shape (len(key_pos_bboxes),).\n        \"\"\"\n\n        track_targets = []\n        track_weights = []\n        for _gt_match_indices, key_res, ref_res in zip(gt_match_indices,\n                                                       key_sampling_results,\n                                                       ref_sampling_results):\n            targets = _gt_match_indices.new_zeros(\n                (key_res.pos_bboxes.size(0), ref_res.bboxes.size(0)),\n                dtype=torch.int)\n            _match_indices = _gt_match_indices[key_res.pos_assigned_gt_inds]\n            pos2pos = (_match_indices.view(\n                -1, 1) == ref_res.pos_assigned_gt_inds.view(1, -1)).int()\n            targets[:, :pos2pos.size(1)] = pos2pos\n            weights = (targets.sum(dim=1) > 0).float()\n            track_targets.append(targets)\n            track_weights.append(weights)\n        return track_targets, track_weights\n\n    def match(\n        self, key_embeds: Tensor, ref_embeds: Tensor,\n        key_sampling_results: List[SamplingResult],\n        ref_sampling_results: List[SamplingResult]\n    ) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Calculate the dist matrixes for loss measurement.\n\n        Args:\n            key_embeds (Tensor): Embeds of positive bboxes in sampling results\n                of key image.\n            ref_embeds (Tensor): Embeds of all bboxes in sampling results\n                of the reference image.\n            key_sampling_results (List[obj:SamplingResults]): Assign results of\n                all images in a batch after sampling.\n            ref_sampling_results (List[obj:SamplingResults]): Assign results of\n                all reference images in a batch after sampling.\n\n        Returns:\n            Tuple[list[Tensor]]: Calculation results.\n            Containing the following list of Tensors:\n\n                - dists (list[Tensor]): Dot-product dists between\n                    key_embeds and ref_embeds, each tensor in list has\n                    shape (len(key_pos_bboxes), len(ref_bboxes)).\n                - cos_dists (list[Tensor]): Cosine dists between\n                    key_embeds and ref_embeds, each tensor in list has\n                    shape (len(key_pos_bboxes), len(ref_bboxes)).\n        \"\"\"\n\n        num_key_rois = [res.pos_bboxes.size(0) for res in key_sampling_results]\n        key_embeds = torch.split(key_embeds, num_key_rois)\n        num_ref_rois = [res.bboxes.size(0) for res in ref_sampling_results]\n        ref_embeds = torch.split(ref_embeds, num_ref_rois)\n\n        dists, cos_dists = [], []\n        for key_embed, ref_embed in zip(key_embeds, ref_embeds):\n            dist = embed_similarity(\n                key_embed,\n                ref_embed,\n                method='dot_product',\n                temperature=self.softmax_temp)\n            dists.append(dist)\n            if self.loss_track_aux is not None:\n                cos_dist = embed_similarity(\n                    key_embed, ref_embed, method='cosine')\n                cos_dists.append(cos_dist)\n            else:\n                cos_dists.append(None)\n        return dists, cos_dists\n\n    def loss(self, key_roi_feats: Tensor, ref_roi_feats: Tensor,\n             key_sampling_results: List[SamplingResult],\n             ref_sampling_results: List[SamplingResult],\n             gt_match_indices_list: List[Tensor]) -> dict:\n        \"\"\"Calculate the track loss and the auxiliary track loss.\n\n        Args:\n            key_roi_feats (Tensor): Embeds of positive bboxes in sampling\n                results of key image.\n            ref_roi_feats (Tensor): Embeds of all bboxes in sampling results\n                of the reference image.\n            key_sampling_results (List[obj:SamplingResults]): Assign results of\n                all images in a batch after sampling.\n            ref_sampling_results (List[obj:SamplingResults]): Assign results of\n                all reference images in a batch after sampling.\n            gt_match_indices_list (list(Tensor)): Mapping from gt_instances_ids\n                to ref_gt_instances_ids of the same tracklet in a pair of\n                images.\n\n        Returns:\n            Dict [str: Tensor]: Calculation results.\n            Containing the following list of Tensors:\n\n                - loss_track (Tensor): Results of loss_track function.\n                - loss_track_aux (Tensor): Results of loss_track_aux function.\n        \"\"\"\n        key_track_feats = self(key_roi_feats)\n        ref_track_feats = self(ref_roi_feats)\n\n        losses = self.loss_by_feat(key_track_feats, ref_track_feats,\n                                   key_sampling_results, ref_sampling_results,\n                                   gt_match_indices_list)\n        return losses\n\n    def loss_by_feat(self, key_track_feats: Tensor, ref_track_feats: Tensor,\n                     key_sampling_results: List[SamplingResult],\n                     ref_sampling_results: List[SamplingResult],\n                     gt_match_indices_list: List[Tensor]) -> dict:\n        \"\"\"Calculate the track loss and the auxiliary track loss.\n\n        Args:\n            key_track_feats (Tensor): Embeds of positive bboxes in sampling\n                results of key image.\n            ref_track_feats (Tensor): Embeds of all bboxes in sampling results\n                of the reference image.\n            key_sampling_results (List[obj:SamplingResults]): Assign results of\n                all images in a batch after sampling.\n            ref_sampling_results (List[obj:SamplingResults]): Assign results of\n                all reference images in a batch after sampling.\n            gt_match_indices_list (list(Tensor)): Mapping from instances_ids\n                from key image to reference image of the same tracklet in a\n                pair of images.\n\n        Returns:\n            Dict [str: Tensor]: Calculation results.\n            Containing the following list of Tensors:\n\n                - loss_track (Tensor): Results of loss_track function.\n                - loss_track_aux (Tensor): Results of loss_track_aux function.\n        \"\"\"\n        dists, cos_dists = self.match(key_track_feats, ref_track_feats,\n                                      key_sampling_results,\n                                      ref_sampling_results)\n        targets, weights = self.get_targets(gt_match_indices_list,\n                                            key_sampling_results,\n                                            ref_sampling_results)\n        losses = dict()\n\n        loss_track = 0.\n        loss_track_aux = 0.\n        for _dists, _cos_dists, _targets, _weights in zip(\n                dists, cos_dists, targets, weights):\n            loss_track += self.loss_track(\n                _dists, _targets, _weights, avg_factor=_weights.sum())\n            if self.loss_track_aux is not None:\n                loss_track_aux += self.loss_track_aux(_cos_dists, _targets)\n        losses['loss_track'] = loss_track / len(dists)\n\n        if self.loss_track_aux is not None:\n            losses['loss_track_aux'] = loss_track_aux / len(dists)\n\n        return losses\n\n    def predict(self, bbox_feats: Tensor) -> Tensor:\n        \"\"\"Perform forward propagation of the tracking head and predict\n        tracking results on the features of the upstream network.\n\n        Args:\n            bbox_feats: The extracted roi features.\n\n        Returns:\n            Tensor: The extracted track features.\n        \"\"\"\n        track_feats = self(bbox_feats)\n        return track_feats\n"
  },
  {
    "path": "mmdet/models/tracking_heads/quasi_dense_track_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional\n\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import TrackSampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import InstanceList\n\n\n@MODELS.register_module()\nclass QuasiDenseTrackHead(BaseModule):\n    \"\"\"The quasi-dense track head.\"\"\"\n\n    def __init__(self,\n                 roi_extractor: Optional[dict] = None,\n                 embed_head: Optional[dict] = None,\n                 regress_head: Optional[dict] = None,\n                 train_cfg: Optional[dict] = None,\n                 test_cfg: Optional[dict] = None,\n                 init_cfg: Optional[dict] = None,\n                 **kwargs):\n        super().__init__(init_cfg=init_cfg)\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n        if embed_head is not None:\n            self.init_embed_head(roi_extractor, embed_head)\n\n        if regress_head is not None:\n            raise NotImplementedError('Regression head is not supported yet.')\n\n        self.init_assigner_sampler()\n\n    def init_embed_head(self, roi_extractor, embed_head) -> None:\n        \"\"\"Initialize ``embed_head``\n\n        Args:\n            roi_extractor (dict, optional): Configuration of roi extractor.\n                Defaults to None.\n            embed_head (dict, optional): Configuration of embed head. Defaults\n                to None.\n        \"\"\"\n        self.roi_extractor = MODELS.build(roi_extractor)\n        self.embed_head = MODELS.build(embed_head)\n\n    def init_assigner_sampler(self) -> None:\n        \"\"\"Initialize assigner and sampler.\"\"\"\n        self.bbox_assigner = None\n        self.bbox_sampler = None\n        if self.train_cfg:\n            self.bbox_assigner = TASK_UTILS.build(self.train_cfg.assigner)\n            self.bbox_sampler = TASK_UTILS.build(\n                self.train_cfg.sampler, default_args=dict(context=self))\n\n    @property\n    def with_track(self) -> bool:\n        \"\"\"bool: whether the multi-object tracker has an embed head\"\"\"\n        return hasattr(self, 'embed_head') and self.embed_head is not None\n\n    def extract_roi_feats(self, feats: List[Tensor],\n                          bboxes: List[Tensor]) -> Tensor:\n        \"\"\"Extract roi features.\n\n        Args:\n            feats (list[Tensor]): list of multi-level image features.\n            bboxes (list[Tensor]): list of bboxes in sampling result.\n\n        Returns:\n            Tensor: The extracted roi features.\n        \"\"\"\n        rois = bbox2roi(bboxes)\n        bbox_feats = self.roi_extractor(feats[:self.roi_extractor.num_inputs],\n                                        rois)\n        return bbox_feats\n\n    def loss(self, key_feats: List[Tensor], ref_feats: List[Tensor],\n             rpn_results_list: InstanceList,\n             ref_rpn_results_list: InstanceList, data_samples: TrackSampleList,\n             **kwargs) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            key_feats (list[Tensor]): list of multi-level image features.\n            ref_feats (list[Tensor]): list of multi-level ref_img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals of key img.\n            ref_rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals of ref img.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        assert self.with_track\n        num_imgs = len(data_samples)\n        batch_gt_instances = []\n        ref_batch_gt_instances = []\n        batch_gt_instances_ignore = []\n        gt_match_indices_list = []\n        for track_data_sample in data_samples:\n            key_data_sample = track_data_sample.get_key_frames()[0]\n            ref_data_sample = track_data_sample.get_ref_frames()[0]\n            batch_gt_instances.append(key_data_sample.gt_instances)\n            ref_batch_gt_instances.append(ref_data_sample.gt_instances)\n            if 'ignored_instances' in key_data_sample:\n                batch_gt_instances_ignore.append(\n                    key_data_sample.ignored_instances)\n            else:\n                batch_gt_instances_ignore.append(None)\n            # get gt_match_indices\n            ins_ids = key_data_sample.gt_instances.instances_ids.tolist()\n            ref_ins_ids = ref_data_sample.gt_instances.instances_ids.tolist()\n            match_indices = Tensor([\n                ref_ins_ids.index(i) if (i in ref_ins_ids and i > 0) else -1\n                for i in ins_ids\n            ]).to(key_feats[0].device)\n            gt_match_indices_list.append(match_indices)\n\n        key_sampling_results, ref_sampling_results = [], []\n        for i in range(num_imgs):\n            rpn_results = rpn_results_list[i]\n            ref_rpn_results = ref_rpn_results_list[i]\n            # rename ref_rpn_results.bboxes to ref_rpn_results.priors\n            ref_rpn_results.priors = ref_rpn_results.pop('bboxes')\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in key_feats])\n            key_sampling_results.append(sampling_result)\n\n            ref_assign_result = self.bbox_assigner.assign(\n                ref_rpn_results, ref_batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            ref_sampling_result = self.bbox_sampler.sample(\n                ref_assign_result,\n                ref_rpn_results,\n                ref_batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in ref_feats])\n            ref_sampling_results.append(ref_sampling_result)\n\n        key_bboxes = [res.pos_bboxes for res in key_sampling_results]\n        key_roi_feats = self.extract_roi_feats(key_feats, key_bboxes)\n        ref_bboxes = [res.bboxes for res in ref_sampling_results]\n        ref_roi_feats = self.extract_roi_feats(ref_feats, ref_bboxes)\n\n        loss_track = self.embed_head.loss(key_roi_feats, ref_roi_feats,\n                                          key_sampling_results,\n                                          ref_sampling_results,\n                                          gt_match_indices_list)\n\n        return loss_track\n\n    def predict(self, feats: List[Tensor],\n                rescaled_bboxes: List[Tensor]) -> Tensor:\n        \"\"\"Perform forward propagation of the tracking head and predict\n        tracking results on the features of the upstream network.\n\n        Args:\n            feats (list[Tensor]): Multi level feature maps of `img`.\n            rescaled_bboxes (list[Tensor]): list of rescaled bboxes in sampling\n                result.\n\n        Returns:\n            Tensor: The extracted track features.\n        \"\"\"\n        bbox_feats = self.extract_roi_feats(feats, rescaled_bboxes)\n        track_feats = self.embed_head.predict(bbox_feats)\n        return track_feats\n"
  },
  {
    "path": "mmdet/models/tracking_heads/roi_embed_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom collections import defaultdict\nfrom typing import List, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\nfrom torch.nn.modules.utils import _pair\n\nfrom mmdet.models.losses import accuracy\nfrom mmdet.models.task_modules import SamplingResult\nfrom mmdet.models.task_modules.tracking import embed_similarity\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass RoIEmbedHead(BaseModule):\n    \"\"\"The roi embed head.\n\n    This module is used in multi-object tracking methods, such as MaskTrack\n    R-CNN.\n\n    Args:\n        num_convs (int): The number of convoluational layers to embed roi\n            features. Defaults to 0.\n        num_fcs (int): The number of fully connection layers to embed roi\n            features. Defaults to 0.\n        roi_feat_size (int|tuple(int)): The spatial size of roi features.\n            Defaults to 7.\n        in_channels (int): The input channel of roi features. Defaults to 256.\n        conv_out_channels (int): The output channel of roi features after\n            forwarding convoluational layers. Defaults to 256.\n        with_avg_pool (bool): Whether use average pooling before passing roi\n            features into fully connection layers. Defaults to False.\n        fc_out_channels (int): The output channel of roi features after\n            forwarding fully connection layers. Defaults to 1024.\n        conv_cfg (dict): Config dict for convolution layer. Defaults to None,\n            which means using conv2d.\n        norm_cfg (dict): Config dict for normalization layer. Defaults to None.\n        loss_match (dict): The loss function. Defaults to\n            dict(type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)\n        init_cfg (dict): Configuration of initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 num_convs: int = 0,\n                 num_fcs: int = 0,\n                 roi_feat_size: int = 7,\n                 in_channels: int = 256,\n                 conv_out_channels: int = 256,\n                 with_avg_pool: bool = False,\n                 fc_out_channels: int = 1024,\n                 conv_cfg: Optional[dict] = None,\n                 norm_cfg: Optional[dict] = None,\n                 loss_match: dict = dict(\n                     type='mmdet.CrossEntropyLoss',\n                     use_sigmoid=False,\n                     loss_weight=1.0),\n                 init_cfg: Optional[dict] = None,\n                 **kwargs):\n        super(RoIEmbedHead, self).__init__(init_cfg=init_cfg)\n        self.num_convs = num_convs\n        self.num_fcs = num_fcs\n        self.roi_feat_size = _pair(roi_feat_size)\n        self.roi_feat_area = self.roi_feat_size[0] * self.roi_feat_size[1]\n        self.in_channels = in_channels\n        self.conv_out_channels = conv_out_channels\n        self.with_avg_pool = with_avg_pool\n        self.fc_out_channels = fc_out_channels\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.loss_match = MODELS.build(loss_match)\n        self.fp16_enabled = False\n\n        if self.with_avg_pool:\n            self.avg_pool = nn.AvgPool2d(self.roi_feat_size)\n        # add convs and fcs\n        self.convs, self.fcs, self.last_layer_dim = self._add_conv_fc_branch(\n            self.num_convs, self.num_fcs, self.in_channels)\n        self.relu = nn.ReLU(inplace=True)\n\n    def _add_conv_fc_branch(\n            self, num_branch_convs: int, num_branch_fcs: int,\n            in_channels: int) -> Tuple[nn.ModuleList, nn.ModuleList, int]:\n        \"\"\"Add shared or separable branch.\n\n        convs -> avg pool (optional) -> fcs\n        \"\"\"\n        last_layer_dim = in_channels\n        # add branch specific conv layers\n        branch_convs = nn.ModuleList()\n        if num_branch_convs > 0:\n            for i in range(num_branch_convs):\n                conv_in_channels = (\n                    last_layer_dim if i == 0 else self.conv_out_channels)\n                branch_convs.append(\n                    ConvModule(\n                        conv_in_channels,\n                        self.conv_out_channels,\n                        3,\n                        padding=1,\n                        conv_cfg=self.conv_cfg,\n                        norm_cfg=self.norm_cfg))\n            last_layer_dim = self.conv_out_channels\n\n        # add branch specific fc layers\n        branch_fcs = nn.ModuleList()\n        if num_branch_fcs > 0:\n            if not self.with_avg_pool:\n                last_layer_dim *= self.roi_feat_area\n            for i in range(num_branch_fcs):\n                fc_in_channels = (\n                    last_layer_dim if i == 0 else self.fc_out_channels)\n                branch_fcs.append(\n                    nn.Linear(fc_in_channels, self.fc_out_channels))\n            last_layer_dim = self.fc_out_channels\n\n        return branch_convs, branch_fcs, last_layer_dim\n\n    @property\n    def custom_activation(self):\n        return getattr(self.loss_match, 'custom_activation', False)\n\n    def extract_feat(self, x: Tensor,\n                     num_x_per_img: List[int]) -> Tuple[Tensor]:\n        \"\"\"Extract feature from the input `x`, and split the output to a list.\n\n        Args:\n            x (Tensor): of shape [N, C, H, W]. N is the number of proposals.\n            num_x_per_img (list[int]): The `x` contains proposals of\n                multi-images. `num_x_per_img` denotes the number of proposals\n                for each image.\n\n        Returns:\n            list[Tensor]: Each Tensor denotes the embed features belonging to\n            an image in a batch.\n        \"\"\"\n        if self.num_convs > 0:\n            for conv in self.convs:\n                x = conv(x)\n\n        if self.num_fcs > 0:\n            if self.with_avg_pool:\n                x = self.avg_pool(x)\n            x = x.flatten(1)\n            for fc in self.fcs:\n                x = self.relu(fc(x))\n        else:\n            x = x.flatten(1)\n\n        x_split = torch.split(x, num_x_per_img, dim=0)\n        return x_split\n\n    def forward(\n            self, x: Tensor, ref_x: Tensor, num_x_per_img: List[int],\n            num_x_per_ref_img: List[int]\n    ) -> Tuple[Tuple[Tensor], Tuple[Tensor]]:\n        \"\"\"Computing the similarity scores between `x` and `ref_x`.\n\n        Args:\n            x (Tensor): of shape [N, C, H, W]. N is the number of key frame\n                proposals.\n            ref_x (Tensor): of shape [M, C, H, W]. M is the number of reference\n                frame proposals.\n            num_x_per_img (list[int]): The `x` contains proposals of\n                multi-images. `num_x_per_img` denotes the number of proposals\n                for each key image.\n            num_x_per_ref_img (list[int]): The `ref_x` contains proposals of\n                multi-images. `num_x_per_ref_img` denotes the number of\n                proposals for each reference image.\n\n        Returns:\n            tuple[tuple[Tensor], tuple[Tensor]]: Each tuple of tensor denotes\n            the embed features belonging to an image in a batch.\n        \"\"\"\n        x_split = self.extract_feat(x, num_x_per_img)\n        ref_x_split = self.extract_feat(ref_x, num_x_per_ref_img)\n\n        return x_split, ref_x_split\n\n    def get_targets(self, sampling_results: List[SamplingResult],\n                    gt_instance_ids: List[Tensor],\n                    ref_gt_instance_ids: List[Tensor]) -> Tuple[List, List]:\n        \"\"\"Calculate the ground truth for all samples in a batch according to\n        the sampling_results.\n\n        Args:\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            gt_instance_ids (list[Tensor]): The instance ids of gt_bboxes of\n                all images in a batch, each tensor has shape (num_gt, ).\n            ref_gt_instance_ids (list[Tensor]): The instance ids of gt_bboxes\n                of all reference images in a batch, each tensor has shape\n                (num_gt, ).\n\n        Returns:\n            Tuple[list[Tensor]]: Ground truth for proposals in a batch.\n            Containing the following list of Tensors:\n\n                - track_id_targets (list[Tensor]): The instance ids of\n                  Gt_labels for all proposals in a batch, each tensor in list\n                  has shape (num_proposals,).\n                - track_id_weights (list[Tensor]): Labels_weights for\n                  all proposals in a batch, each tensor in list has\n                  shape (num_proposals,).\n        \"\"\"\n        track_id_targets = []\n        track_id_weights = []\n\n        for res, gt_instance_id, ref_gt_instance_id in zip(\n                sampling_results, gt_instance_ids, ref_gt_instance_ids):\n            pos_instance_ids = gt_instance_id[res.pos_assigned_gt_inds]\n            pos_match_id = gt_instance_id.new_zeros(len(pos_instance_ids))\n            for i, id in enumerate(pos_instance_ids):\n                if id in ref_gt_instance_id:\n                    pos_match_id[i] = ref_gt_instance_id.tolist().index(id) + 1\n\n            track_id_target = gt_instance_id.new_zeros(\n                len(res.bboxes), dtype=torch.int64)\n            track_id_target[:len(res.pos_bboxes)] = pos_match_id\n            track_id_weight = res.bboxes.new_zeros(len(res.bboxes))\n            track_id_weight[:len(res.pos_bboxes)] = 1.0\n\n            track_id_targets.append(track_id_target)\n            track_id_weights.append(track_id_weight)\n\n        return track_id_targets, track_id_weights\n\n    def loss(\n        self,\n        bbox_feats: Tensor,\n        ref_bbox_feats: Tensor,\n        num_bbox_per_img: int,\n        num_bbox_per_ref_img: int,\n        sampling_results: List[SamplingResult],\n        gt_instance_ids: List[Tensor],\n        ref_gt_instance_ids: List[Tensor],\n        reduction_override: Optional[str] = None,\n    ) -> dict:\n        \"\"\"Calculate the loss in a batch.\n\n        Args:\n            bbox_feats (Tensor): of shape [N, C, H, W]. N is the number of\n                bboxes.\n            ref_bbox_feats (Tensor): of shape [M, C, H, W]. M is the number of\n                reference bboxes.\n            num_bbox_per_img (list[int]): The `bbox_feats` contains proposals\n                of multi-images. `num_bbox_per_img` denotes the number of\n                proposals for each key image.\n            num_bbox_per_ref_img (list[int]): The `ref_bbox_feats` contains\n                proposals of multi-images. `num_bbox_per_ref_img` denotes the\n                number of proposals for each reference image.\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            gt_instance_ids (list[Tensor]): The instance ids of gt_bboxes of\n                all images in a batch, each tensor has shape (num_gt, ).\n            ref_gt_instance_ids (list[Tensor]): The instance ids of gt_bboxes\n                of all reference images in a batch, each tensor has shape\n                (num_gt, ).\n            reduction_override (str, optional): The method used to reduce the\n                loss. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components.\n        \"\"\"\n        x_split, ref_x_split = self(bbox_feats, ref_bbox_feats,\n                                    num_bbox_per_img, num_bbox_per_ref_img)\n\n        losses = self.loss_by_feat(x_split, ref_x_split, sampling_results,\n                                   gt_instance_ids, ref_gt_instance_ids,\n                                   reduction_override)\n        return losses\n\n    def loss_by_feat(self,\n                     x_split: Tuple[Tensor],\n                     ref_x_split: Tuple[Tensor],\n                     sampling_results: List[SamplingResult],\n                     gt_instance_ids: List[Tensor],\n                     ref_gt_instance_ids: List[Tensor],\n                     reduction_override: Optional[str] = None) -> dict:\n        \"\"\"Calculate losses.\n\n        Args:\n            x_split (Tensor): The embed features belonging to key image.\n            ref_x_split (Tensor): The embed features belonging to ref image.\n            sampling_results (List[obj:SamplingResult]): Assign results of\n                all images in a batch after sampling.\n            gt_instance_ids (list[Tensor]): The instance ids of gt_bboxes of\n                all images in a batch, each tensor has shape (num_gt, ).\n            ref_gt_instance_ids (list[Tensor]): The instance ids of gt_bboxes\n                of all reference images in a batch, each tensor has shape\n                (num_gt, ).\n            reduction_override (str, optional): The method used to reduce the\n                loss. Options are \"none\", \"mean\" and \"sum\".\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components.\n        \"\"\"\n        track_id_targets, track_id_weights = self.get_targets(\n            sampling_results, gt_instance_ids, ref_gt_instance_ids)\n        assert isinstance(track_id_targets, list)\n        assert isinstance(track_id_weights, list)\n        assert len(track_id_weights) == len(track_id_targets)\n\n        losses = defaultdict(list)\n        similarity_logits = []\n        for one_x, one_ref_x in zip(x_split, ref_x_split):\n            similarity_logit = embed_similarity(\n                one_x, one_ref_x, method='dot_product')\n            dummy = similarity_logit.new_zeros(one_x.shape[0], 1)\n            similarity_logit = torch.cat((dummy, similarity_logit), dim=1)\n            similarity_logits.append(similarity_logit)\n        assert isinstance(similarity_logits, list)\n        assert len(similarity_logits) == len(track_id_targets)\n\n        for similarity_logit, track_id_target, track_id_weight in zip(\n                similarity_logits, track_id_targets, track_id_weights):\n            avg_factor = max(torch.sum(track_id_target > 0).float().item(), 1.)\n            if similarity_logit.numel() > 0:\n                loss_match = self.loss_match(\n                    similarity_logit,\n                    track_id_target,\n                    track_id_weight,\n                    avg_factor=avg_factor,\n                    reduction_override=reduction_override)\n                if isinstance(loss_match, dict):\n                    for key, value in loss_match.items():\n                        losses[key].append(value)\n                else:\n                    losses['loss_match'].append(loss_match)\n\n                valid_index = track_id_weight > 0\n                valid_similarity_logit = similarity_logit[valid_index]\n                valid_track_id_target = track_id_target[valid_index]\n                if self.custom_activation:\n                    match_accuracy = self.loss_match.get_accuracy(\n                        valid_similarity_logit, valid_track_id_target)\n                    for key, value in match_accuracy.items():\n                        losses[key].append(value)\n                else:\n                    losses['match_accuracy'].append(\n                        accuracy(valid_similarity_logit,\n                                 valid_track_id_target))\n\n        for key, value in losses.items():\n            losses[key] = sum(losses[key]) / len(similarity_logits)\n        return losses\n\n    def predict(self, roi_feats: Tensor,\n                prev_roi_feats: Tensor) -> List[Tensor]:\n        \"\"\"Perform forward propagation of the tracking head and predict\n        tracking results on the features of the upstream network.\n\n        Args:\n            roi_feats (Tensor): Feature map of current images rois.\n            prev_roi_feats (Tensor): Feature map of previous images rois.\n\n        Returns:\n            list[Tensor]: The predicted similarity_logits of each pair of key\n            image and reference image.\n        \"\"\"\n        x_split, ref_x_split = self(roi_feats, prev_roi_feats,\n                                    [roi_feats.shape[0]],\n                                    [prev_roi_feats.shape[0]])\n\n        similarity_logits = self.predict_by_feat(x_split, ref_x_split)\n\n        return similarity_logits\n\n    def predict_by_feat(self, x_split: Tuple[Tensor],\n                        ref_x_split: Tuple[Tensor]) -> List[Tensor]:\n        \"\"\"Get similarity_logits.\n\n        Args:\n            x_split (Tensor): The embed features belonging to key image.\n            ref_x_split (Tensor): The embed features belonging to ref image.\n\n        Returns:\n            list[Tensor]: The predicted similarity_logits of each pair of key\n            image and reference image.\n        \"\"\"\n        similarity_logits = []\n        for one_x, one_ref_x in zip(x_split, ref_x_split):\n            similarity_logit = embed_similarity(\n                one_x, one_ref_x, method='dot_product')\n            dummy = similarity_logit.new_zeros(one_x.shape[0], 1)\n            similarity_logit = torch.cat((dummy, similarity_logit), dim=1)\n            similarity_logits.append(similarity_logit)\n        return similarity_logits\n"
  },
  {
    "path": "mmdet/models/tracking_heads/roi_track_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta\nfrom typing import List, Optional, Tuple\n\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import TrackSampleList\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import InstanceList\n\n\n@MODELS.register_module()\nclass RoITrackHead(BaseModule, metaclass=ABCMeta):\n    \"\"\"The roi track head.\n\n    This module is used in multi-object tracking methods, such as MaskTrack\n    R-CNN.\n\n    Args:\n        roi_extractor (dict): Configuration of roi extractor. Defaults to None.\n        embed_head (dict): Configuration of embed head. Defaults to None.\n        train_cfg (dict): Configuration when training. Defaults to None.\n        test_cfg (dict): Configuration when testing. Defaults to None.\n        init_cfg (dict): Configuration of initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 roi_extractor: Optional[dict] = None,\n                 embed_head: Optional[dict] = None,\n                 regress_head: Optional[dict] = None,\n                 train_cfg: Optional[dict] = None,\n                 test_cfg: Optional[dict] = None,\n                 init_cfg: Optional[dict] = None,\n                 *args,\n                 **kwargs):\n        super().__init__(init_cfg=init_cfg)\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n        if embed_head is not None:\n            self.init_embed_head(roi_extractor, embed_head)\n\n        if regress_head is not None:\n            raise NotImplementedError('Regression head is not supported yet.')\n\n        self.init_assigner_sampler()\n\n    def init_embed_head(self, roi_extractor, embed_head) -> None:\n        \"\"\"Initialize ``embed_head``\"\"\"\n        self.roi_extractor = MODELS.build(roi_extractor)\n        self.embed_head = MODELS.build(embed_head)\n\n    def init_assigner_sampler(self) -> None:\n        \"\"\"Initialize assigner and sampler.\"\"\"\n        self.bbox_assigner = None\n        self.bbox_sampler = None\n        if self.train_cfg:\n            self.bbox_assigner = TASK_UTILS.build(self.train_cfg.assigner)\n            self.bbox_sampler = TASK_UTILS.build(\n                self.train_cfg.sampler, default_args=dict(context=self))\n\n    @property\n    def with_track(self) -> bool:\n        \"\"\"bool: whether the multi-object tracker has an embed head\"\"\"\n        return hasattr(self, 'embed_head') and self.embed_head is not None\n\n    def extract_roi_feats(\n            self, feats: List[Tensor],\n            bboxes: List[Tensor]) -> Tuple[Tuple[Tensor], List[int]]:\n        \"\"\"Extract roi features.\n\n        Args:\n            feats (list[Tensor]): list of multi-level image features.\n            bboxes (list[Tensor]): list of bboxes in sampling result.\n\n        Returns:\n            tuple[tuple[Tensor], list[int]]: The extracted roi features and\n            the number of bboxes in each image.\n        \"\"\"\n        rois = bbox2roi(bboxes)\n        bbox_feats = self.roi_extractor(feats[:self.roi_extractor.num_inputs],\n                                        rois)\n        num_bbox_per_img = [len(bbox) for bbox in bboxes]\n        return bbox_feats, num_bbox_per_img\n\n    def loss(self, key_feats: List[Tensor], ref_feats: List[Tensor],\n             rpn_results_list: InstanceList, data_samples: TrackSampleList,\n             **kwargs) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            key_feats (list[Tensor]): list of multi-level image features.\n            ref_feats (list[Tensor]): list of multi-level ref_img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        assert self.with_track\n        batch_gt_instances = []\n        ref_batch_gt_instances = []\n        batch_gt_instances_ignore = []\n        gt_instance_ids = []\n        ref_gt_instance_ids = []\n        for track_data_sample in data_samples:\n            key_data_sample = track_data_sample.get_key_frames()[0]\n            ref_data_sample = track_data_sample.get_ref_frames()[0]\n            batch_gt_instances.append(key_data_sample.gt_instances)\n            ref_batch_gt_instances.append(ref_data_sample.gt_instances)\n            if 'ignored_instances' in key_data_sample:\n                batch_gt_instances_ignore.append(\n                    key_data_sample.ignored_instances)\n            else:\n                batch_gt_instances_ignore.append(None)\n\n            gt_instance_ids.append(key_data_sample.gt_instances.instances_ids)\n            ref_gt_instance_ids.append(\n                ref_data_sample.gt_instances.instances_ids)\n\n        losses = dict()\n        num_imgs = len(data_samples)\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        sampling_results = []\n        for i in range(num_imgs):\n            rpn_results = rpn_results_list[i]\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in key_feats])\n            sampling_results.append(sampling_result)\n\n        bboxes = [res.bboxes for res in sampling_results]\n        bbox_feats, num_bbox_per_img = self.extract_roi_feats(\n            key_feats, bboxes)\n\n        # batch_size is 1\n        ref_gt_bboxes = [\n            ref_batch_gt_instance.bboxes\n            for ref_batch_gt_instance in ref_batch_gt_instances\n        ]\n        ref_bbox_feats, num_bbox_per_ref_img = self.extract_roi_feats(\n            ref_feats, ref_gt_bboxes)\n\n        loss_track = self.embed_head.loss(bbox_feats, ref_bbox_feats,\n                                          num_bbox_per_img,\n                                          num_bbox_per_ref_img,\n                                          sampling_results, gt_instance_ids,\n                                          ref_gt_instance_ids)\n        losses.update(loss_track)\n\n        return losses\n\n    def predict(self, roi_feats: Tensor,\n                prev_roi_feats: Tensor) -> List[Tensor]:\n        \"\"\"Perform forward propagation of the tracking head and predict\n        tracking results on the features of the upstream network.\n\n        Args:\n            roi_feats (Tensor): Feature map of current images rois.\n            prev_roi_feats (Tensor): Feature map of previous images rois.\n\n        Returns:\n            list[Tensor]: The predicted similarity_logits of each pair of key\n            image and reference image.\n        \"\"\"\n        return self.embed_head.predict(roi_feats, prev_roi_feats)[0]\n"
  },
  {
    "path": "mmdet/models/utils/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .gaussian_target import (gather_feat, gaussian_radius,\n                              gen_gaussian_target, get_local_maximum,\n                              get_topk_from_heatmap, transpose_and_gather_feat)\nfrom .image import imrenormalize\nfrom .make_divisible import make_divisible\n# Disable yapf because it conflicts with isort.\n# yapf: disable\nfrom .misc import (align_tensor, aligned_bilinear, center_of_mass,\n                   empty_instances, filter_gt_instances,\n                   filter_scores_and_topk, flip_tensor, generate_coordinate,\n                   images_to_levels, interpolate_as, levels_to_images,\n                   mask2ndarray, multi_apply, relative_coordinate_maps,\n                   rename_loss_dict, reweight_loss_dict,\n                   samplelist_boxtype2tensor, select_single_mlvl,\n                   sigmoid_geometric_mean, unfold_wo_center, unmap,\n                   unpack_gt_instances)\nfrom .panoptic_gt_processing import preprocess_panoptic_gt\nfrom .point_sample import (get_uncertain_point_coords_with_randomness,\n                           get_uncertainty)\nfrom .vlfuse_helper import BertEncoderLayer, VLFuse, permute_and_flatten\nfrom .wbf import weighted_boxes_fusion\n\n__all__ = [\n    'gaussian_radius', 'gen_gaussian_target', 'make_divisible',\n    'get_local_maximum', 'get_topk_from_heatmap', 'transpose_and_gather_feat',\n    'interpolate_as', 'sigmoid_geometric_mean', 'gather_feat',\n    'preprocess_panoptic_gt', 'get_uncertain_point_coords_with_randomness',\n    'get_uncertainty', 'unpack_gt_instances', 'empty_instances',\n    'center_of_mass', 'filter_scores_and_topk', 'flip_tensor',\n    'generate_coordinate', 'levels_to_images', 'mask2ndarray', 'multi_apply',\n    'select_single_mlvl', 'unmap', 'images_to_levels',\n    'samplelist_boxtype2tensor', 'filter_gt_instances', 'rename_loss_dict',\n    'reweight_loss_dict', 'relative_coordinate_maps', 'aligned_bilinear',\n    'unfold_wo_center', 'imrenormalize', 'VLFuse', 'permute_and_flatten',\n    'BertEncoderLayer', 'align_tensor', 'weighted_boxes_fusion'\n]\n"
  },
  {
    "path": "mmdet/models/utils/gaussian_target.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom math import sqrt\n\nimport torch\nimport torch.nn.functional as F\n\n\ndef gaussian2D(radius, sigma=1, dtype=torch.float32, device='cpu'):\n    \"\"\"Generate 2D gaussian kernel.\n\n    Args:\n        radius (int): Radius of gaussian kernel.\n        sigma (int): Sigma of gaussian function. Default: 1.\n        dtype (torch.dtype): Dtype of gaussian tensor. Default: torch.float32.\n        device (str): Device of gaussian tensor. Default: 'cpu'.\n\n    Returns:\n        h (Tensor): Gaussian kernel with a\n            ``(2 * radius + 1) * (2 * radius + 1)`` shape.\n    \"\"\"\n    x = torch.arange(\n        -radius, radius + 1, dtype=dtype, device=device).view(1, -1)\n    y = torch.arange(\n        -radius, radius + 1, dtype=dtype, device=device).view(-1, 1)\n\n    h = (-(x * x + y * y) / (2 * sigma * sigma)).exp()\n\n    h[h < torch.finfo(h.dtype).eps * h.max()] = 0\n    return h\n\n\ndef gen_gaussian_target(heatmap, center, radius, k=1):\n    \"\"\"Generate 2D gaussian heatmap.\n\n    Args:\n        heatmap (Tensor): Input heatmap, the gaussian kernel will cover on\n            it and maintain the max value.\n        center (list[int]): Coord of gaussian kernel's center.\n        radius (int): Radius of gaussian kernel.\n        k (int): Coefficient of gaussian kernel. Default: 1.\n\n    Returns:\n        out_heatmap (Tensor): Updated heatmap covered by gaussian kernel.\n    \"\"\"\n    diameter = 2 * radius + 1\n    gaussian_kernel = gaussian2D(\n        radius, sigma=diameter / 6, dtype=heatmap.dtype, device=heatmap.device)\n\n    x, y = center\n\n    height, width = heatmap.shape[:2]\n\n    left, right = min(x, radius), min(width - x, radius + 1)\n    top, bottom = min(y, radius), min(height - y, radius + 1)\n\n    masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right]\n    masked_gaussian = gaussian_kernel[radius - top:radius + bottom,\n                                      radius - left:radius + right]\n    out_heatmap = heatmap\n    torch.max(\n        masked_heatmap,\n        masked_gaussian * k,\n        out=out_heatmap[y - top:y + bottom, x - left:x + right])\n\n    return out_heatmap\n\n\ndef gaussian_radius(det_size, min_overlap):\n    r\"\"\"Generate 2D gaussian radius.\n\n    This function is modified from the `official github repo\n    <https://github.com/princeton-vl/CornerNet-Lite/blob/master/core/sample/\n    utils.py#L65>`_.\n\n    Given ``min_overlap``, radius could computed by a quadratic equation\n    according to Vieta's formulas.\n\n    There are 3 cases for computing gaussian radius, details are following:\n\n    - Explanation of figure: ``lt`` and ``br`` indicates the left-top and\n      bottom-right corner of ground truth box. ``x`` indicates the\n      generated corner at the limited position when ``radius=r``.\n\n    - Case1: one corner is inside the gt box and the other is outside.\n\n    .. code:: text\n\n        |<   width   >|\n\n        lt-+----------+         -\n        |  |          |         ^\n        +--x----------+--+\n        |  |          |  |\n        |  |          |  |    height\n        |  | overlap  |  |\n        |  |          |  |\n        |  |          |  |      v\n        +--+---------br--+      -\n           |          |  |\n           +----------+--x\n\n    To ensure IoU of generated box and gt box is larger than ``min_overlap``:\n\n    .. math::\n        \\cfrac{(w-r)*(h-r)}{w*h+(w+h)r-r^2} \\ge {iou} \\quad\\Rightarrow\\quad\n        {r^2-(w+h)r+\\cfrac{1-iou}{1+iou}*w*h} \\ge 0 \\\\\n        {a} = 1,\\quad{b} = {-(w+h)},\\quad{c} = {\\cfrac{1-iou}{1+iou}*w*h}\n        {r} \\le \\cfrac{-b-\\sqrt{b^2-4*a*c}}{2*a}\n\n    - Case2: both two corners are inside the gt box.\n\n    .. code:: text\n\n        |<   width   >|\n\n        lt-+----------+         -\n        |  |          |         ^\n        +--x-------+  |\n        |  |       |  |\n        |  |overlap|  |       height\n        |  |       |  |\n        |  +-------x--+\n        |          |  |         v\n        +----------+-br         -\n\n    To ensure IoU of generated box and gt box is larger than ``min_overlap``:\n\n    .. math::\n        \\cfrac{(w-2*r)*(h-2*r)}{w*h} \\ge {iou} \\quad\\Rightarrow\\quad\n        {4r^2-2(w+h)r+(1-iou)*w*h} \\ge 0 \\\\\n        {a} = 4,\\quad {b} = {-2(w+h)},\\quad {c} = {(1-iou)*w*h}\n        {r} \\le \\cfrac{-b-\\sqrt{b^2-4*a*c}}{2*a}\n\n    - Case3: both two corners are outside the gt box.\n\n    .. code:: text\n\n           |<   width   >|\n\n        x--+----------------+\n        |  |                |\n        +-lt-------------+  |   -\n        |  |             |  |   ^\n        |  |             |  |\n        |  |   overlap   |  | height\n        |  |             |  |\n        |  |             |  |   v\n        |  +------------br--+   -\n        |                |  |\n        +----------------+--x\n\n    To ensure IoU of generated box and gt box is larger than ``min_overlap``:\n\n    .. math::\n        \\cfrac{w*h}{(w+2*r)*(h+2*r)} \\ge {iou} \\quad\\Rightarrow\\quad\n        {4*iou*r^2+2*iou*(w+h)r+(iou-1)*w*h} \\le 0 \\\\\n        {a} = {4*iou},\\quad {b} = {2*iou*(w+h)},\\quad {c} = {(iou-1)*w*h} \\\\\n        {r} \\le \\cfrac{-b+\\sqrt{b^2-4*a*c}}{2*a}\n\n    Args:\n        det_size (list[int]): Shape of object.\n        min_overlap (float): Min IoU with ground truth for boxes generated by\n            keypoints inside the gaussian kernel.\n\n    Returns:\n        radius (int): Radius of gaussian kernel.\n    \"\"\"\n    height, width = det_size\n\n    a1 = 1\n    b1 = (height + width)\n    c1 = width * height * (1 - min_overlap) / (1 + min_overlap)\n    sq1 = sqrt(b1**2 - 4 * a1 * c1)\n    r1 = (b1 - sq1) / (2 * a1)\n\n    a2 = 4\n    b2 = 2 * (height + width)\n    c2 = (1 - min_overlap) * width * height\n    sq2 = sqrt(b2**2 - 4 * a2 * c2)\n    r2 = (b2 - sq2) / (2 * a2)\n\n    a3 = 4 * min_overlap\n    b3 = -2 * min_overlap * (height + width)\n    c3 = (min_overlap - 1) * width * height\n    sq3 = sqrt(b3**2 - 4 * a3 * c3)\n    r3 = (b3 + sq3) / (2 * a3)\n    return min(r1, r2, r3)\n\n\ndef get_local_maximum(heat, kernel=3):\n    \"\"\"Extract local maximum pixel with given kernel.\n\n    Args:\n        heat (Tensor): Target heatmap.\n        kernel (int): Kernel size of max pooling. Default: 3.\n\n    Returns:\n        heat (Tensor): A heatmap where local maximum pixels maintain its\n            own value and other positions are 0.\n    \"\"\"\n    pad = (kernel - 1) // 2\n    hmax = F.max_pool2d(heat, kernel, stride=1, padding=pad)\n    keep = (hmax == heat).float()\n    return heat * keep\n\n\ndef get_topk_from_heatmap(scores, k=20):\n    \"\"\"Get top k positions from heatmap.\n\n    Args:\n        scores (Tensor): Target heatmap with shape\n            [batch, num_classes, height, width].\n        k (int): Target number. Default: 20.\n\n    Returns:\n        tuple[torch.Tensor]: Scores, indexes, categories and coords of\n            topk keypoint. Containing following Tensors:\n\n        - topk_scores (Tensor): Max scores of each topk keypoint.\n        - topk_inds (Tensor): Indexes of each topk keypoint.\n        - topk_clses (Tensor): Categories of each topk keypoint.\n        - topk_ys (Tensor): Y-coord of each topk keypoint.\n        - topk_xs (Tensor): X-coord of each topk keypoint.\n    \"\"\"\n    batch, _, height, width = scores.size()\n    topk_scores, topk_inds = torch.topk(scores.view(batch, -1), k)\n    topk_clses = topk_inds // (height * width)\n    topk_inds = topk_inds % (height * width)\n    topk_ys = topk_inds // width\n    topk_xs = (topk_inds % width).int().float()\n    return topk_scores, topk_inds, topk_clses, topk_ys, topk_xs\n\n\ndef gather_feat(feat, ind, mask=None):\n    \"\"\"Gather feature according to index.\n\n    Args:\n        feat (Tensor): Target feature map.\n        ind (Tensor): Target coord index.\n        mask (Tensor | None): Mask of feature map. Default: None.\n\n    Returns:\n        feat (Tensor): Gathered feature.\n    \"\"\"\n    dim = feat.size(2)\n    ind = ind.unsqueeze(2).repeat(1, 1, dim)\n    feat = feat.gather(1, ind)\n    if mask is not None:\n        mask = mask.unsqueeze(2).expand_as(feat)\n        feat = feat[mask]\n        feat = feat.view(-1, dim)\n    return feat\n\n\ndef transpose_and_gather_feat(feat, ind):\n    \"\"\"Transpose and gather feature according to index.\n\n    Args:\n        feat (Tensor): Target feature map.\n        ind (Tensor): Target coord index.\n\n    Returns:\n        feat (Tensor): Transposed and gathered feature.\n    \"\"\"\n    feat = feat.permute(0, 2, 3, 1).contiguous()\n    feat = feat.view(feat.size(0), -1, feat.size(3))\n    feat = gather_feat(feat, ind)\n    return feat\n"
  },
  {
    "path": "mmdet/models/utils/image.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Union\n\nimport mmcv\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\n\ndef imrenormalize(img: Union[Tensor, np.ndarray], img_norm_cfg: dict,\n                  new_img_norm_cfg: dict) -> Union[Tensor, np.ndarray]:\n    \"\"\"Re-normalize the image.\n\n    Args:\n        img (Tensor | ndarray): Input image. If the input is a Tensor, the\n            shape is (1, C, H, W). If the input is a ndarray, the shape\n            is (H, W, C).\n        img_norm_cfg (dict): Original configuration for the normalization.\n        new_img_norm_cfg (dict): New configuration for the normalization.\n\n    Returns:\n        Tensor | ndarray: Output image with the same type and shape of\n        the input.\n    \"\"\"\n    if isinstance(img, torch.Tensor):\n        assert img.ndim == 4 and img.shape[0] == 1\n        new_img = img.squeeze(0).cpu().numpy().transpose(1, 2, 0)\n        new_img = _imrenormalize(new_img, img_norm_cfg, new_img_norm_cfg)\n        new_img = new_img.transpose(2, 0, 1)[None]\n        return torch.from_numpy(new_img).to(img)\n    else:\n        return _imrenormalize(img, img_norm_cfg, new_img_norm_cfg)\n\n\ndef _imrenormalize(img: Union[Tensor, np.ndarray], img_norm_cfg: dict,\n                   new_img_norm_cfg: dict) -> Union[Tensor, np.ndarray]:\n    \"\"\"Re-normalize the image.\"\"\"\n    img_norm_cfg = img_norm_cfg.copy()\n    new_img_norm_cfg = new_img_norm_cfg.copy()\n    for k, v in img_norm_cfg.items():\n        if (k == 'mean' or k == 'std') and not isinstance(v, np.ndarray):\n            img_norm_cfg[k] = np.array(v, dtype=img.dtype)\n    # reverse cfg\n    if 'bgr_to_rgb' in img_norm_cfg:\n        img_norm_cfg['rgb_to_bgr'] = img_norm_cfg['bgr_to_rgb']\n        img_norm_cfg.pop('bgr_to_rgb')\n    for k, v in new_img_norm_cfg.items():\n        if (k == 'mean' or k == 'std') and not isinstance(v, np.ndarray):\n            new_img_norm_cfg[k] = np.array(v, dtype=img.dtype)\n    img = mmcv.imdenormalize(img, **img_norm_cfg)\n    img = mmcv.imnormalize(img, **new_img_norm_cfg)\n    return img\n"
  },
  {
    "path": "mmdet/models/utils/make_divisible.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\ndef make_divisible(value, divisor, min_value=None, min_ratio=0.9):\n    \"\"\"Make divisible function.\n\n    This function rounds the channel number to the nearest value that can be\n    divisible by the divisor. It is taken from the original tf repo. It ensures\n    that all layers have a channel number that is divisible by divisor. It can\n    be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py  # noqa\n\n    Args:\n        value (int): The original channel number.\n        divisor (int): The divisor to fully divide the channel number.\n        min_value (int): The minimum value of the output channel.\n            Default: None, means that the minimum value equal to the divisor.\n        min_ratio (float): The minimum ratio of the rounded channel number to\n            the original channel number. Default: 0.9.\n\n    Returns:\n        int: The modified output channel number.\n    \"\"\"\n\n    if min_value is None:\n        min_value = divisor\n    new_value = max(min_value, int(value + divisor / 2) // divisor * divisor)\n    # Make sure that round down does not go down by more than (1-min_ratio).\n    if new_value < min_ratio * value:\n        new_value += divisor\n    return new_value\n"
  },
  {
    "path": "mmdet/models/utils/misc.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom functools import partial\nfrom typing import List, Optional, Sequence, Tuple, Union\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData\nfrom mmengine.utils import digit_version\nfrom six.moves import map, zip\nfrom torch import Tensor\nfrom torch.autograd import Function\nfrom torch.nn import functional as F\n\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import BaseBoxes, get_box_type, stack_boxes\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\nfrom mmdet.utils import OptInstanceList\n\n\nclass SigmoidGeometricMean(Function):\n    \"\"\"Forward and backward function of geometric mean of two sigmoid\n    functions.\n\n    This implementation with analytical gradient function substitutes\n    the autograd function of (x.sigmoid() * y.sigmoid()).sqrt(). The\n    original implementation incurs none during gradient backprapagation\n    if both x and y are very small values.\n    \"\"\"\n\n    @staticmethod\n    def forward(ctx, x, y):\n        x_sigmoid = x.sigmoid()\n        y_sigmoid = y.sigmoid()\n        z = (x_sigmoid * y_sigmoid).sqrt()\n        ctx.save_for_backward(x_sigmoid, y_sigmoid, z)\n        return z\n\n    @staticmethod\n    def backward(ctx, grad_output):\n        x_sigmoid, y_sigmoid, z = ctx.saved_tensors\n        grad_x = grad_output * z * (1 - x_sigmoid) / 2\n        grad_y = grad_output * z * (1 - y_sigmoid) / 2\n        return grad_x, grad_y\n\n\nsigmoid_geometric_mean = SigmoidGeometricMean.apply\n\n\ndef interpolate_as(source, target, mode='bilinear', align_corners=False):\n    \"\"\"Interpolate the `source` to the shape of the `target`.\n\n    The `source` must be a Tensor, but the `target` can be a Tensor or a\n    np.ndarray with the shape (..., target_h, target_w).\n\n    Args:\n        source (Tensor): A 3D/4D Tensor with the shape (N, H, W) or\n            (N, C, H, W).\n        target (Tensor | np.ndarray): The interpolation target with the shape\n            (..., target_h, target_w).\n        mode (str): Algorithm used for interpolation. The options are the\n            same as those in F.interpolate(). Default: ``'bilinear'``.\n        align_corners (bool): The same as the argument in F.interpolate().\n\n    Returns:\n        Tensor: The interpolated source Tensor.\n    \"\"\"\n    assert len(target.shape) >= 2\n\n    def _interpolate_as(source, target, mode='bilinear', align_corners=False):\n        \"\"\"Interpolate the `source` (4D) to the shape of the `target`.\"\"\"\n        target_h, target_w = target.shape[-2:]\n        source_h, source_w = source.shape[-2:]\n        if target_h != source_h or target_w != source_w:\n            source = F.interpolate(\n                source,\n                size=(target_h, target_w),\n                mode=mode,\n                align_corners=align_corners)\n        return source\n\n    if len(source.shape) == 3:\n        source = source[:, None, :, :]\n        source = _interpolate_as(source, target, mode, align_corners)\n        return source[:, 0, :, :]\n    else:\n        return _interpolate_as(source, target, mode, align_corners)\n\n\ndef unpack_gt_instances(batch_data_samples: SampleList) -> tuple:\n    \"\"\"Unpack ``gt_instances``, ``gt_instances_ignore`` and ``img_metas`` based\n    on ``batch_data_samples``\n\n    Args:\n        batch_data_samples (List[:obj:`DetDataSample`]): The Data\n            Samples. It usually includes information such as\n            `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n    Returns:\n        tuple:\n\n            - batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            - batch_gt_instances_ignore (list[:obj:`InstanceData`]):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n            - batch_img_metas (list[dict]): Meta information of each image,\n                e.g., image size, scaling factor, etc.\n    \"\"\"\n    batch_gt_instances = []\n    batch_gt_instances_ignore = []\n    batch_img_metas = []\n    for data_sample in batch_data_samples:\n        batch_img_metas.append(data_sample.metainfo)\n        batch_gt_instances.append(data_sample.gt_instances)\n        if 'ignored_instances' in data_sample:\n            batch_gt_instances_ignore.append(data_sample.ignored_instances)\n        else:\n            batch_gt_instances_ignore.append(None)\n\n    return batch_gt_instances, batch_gt_instances_ignore, batch_img_metas\n\n\ndef empty_instances(batch_img_metas: List[dict],\n                    device: torch.device,\n                    task_type: str,\n                    instance_results: OptInstanceList = None,\n                    mask_thr_binary: Union[int, float] = 0,\n                    box_type: Union[str, type] = 'hbox',\n                    use_box_type: bool = False,\n                    num_classes: int = 80,\n                    score_per_cls: bool = False) -> List[InstanceData]:\n    \"\"\"Handle predicted instances when RoI is empty.\n\n    Note: If ``instance_results`` is not None, it will be modified\n    in place internally, and then return ``instance_results``\n\n    Args:\n        batch_img_metas (list[dict]): List of image information.\n        device (torch.device): Device of tensor.\n        task_type (str): Expected returned task type. it currently\n            supports bbox and mask.\n        instance_results (list[:obj:`InstanceData`]): List of instance\n            results.\n        mask_thr_binary (int, float): mask binarization threshold.\n            Defaults to 0.\n        box_type (str or type): The empty box type. Defaults to `hbox`.\n        use_box_type (bool): Whether to warp boxes with the box type.\n            Defaults to False.\n        num_classes (int): num_classes of bbox_head. Defaults to 80.\n        score_per_cls (bool):  Whether to generate classwise score for\n            the empty instance. ``score_per_cls`` will be True when the model\n            needs to produce raw results without nms. Defaults to False.\n\n    Returns:\n        list[:obj:`InstanceData`]: Detection results of each image\n    \"\"\"\n    assert task_type in ('bbox', 'mask'), 'Only support bbox and mask,' \\\n                                          f' but got {task_type}'\n\n    if instance_results is not None:\n        assert len(instance_results) == len(batch_img_metas)\n\n    results_list = []\n    for img_id in range(len(batch_img_metas)):\n        if instance_results is not None:\n            results = instance_results[img_id]\n            assert isinstance(results, InstanceData)\n        else:\n            results = InstanceData()\n\n        if task_type == 'bbox':\n            _, box_type = get_box_type(box_type)\n            bboxes = torch.zeros(0, box_type.box_dim, device=device)\n            if use_box_type:\n                bboxes = box_type(bboxes, clone=False)\n            results.bboxes = bboxes\n            score_shape = (0, num_classes + 1) if score_per_cls else (0, )\n            results.scores = torch.zeros(score_shape, device=device)\n            results.labels = torch.zeros((0, ),\n                                         device=device,\n                                         dtype=torch.long)\n        else:\n            # TODO: Handle the case where rescale is false\n            img_h, img_w = batch_img_metas[img_id]['ori_shape'][:2]\n            # the type of `im_mask` will be torch.bool or torch.uint8,\n            # where uint8 if for visualization and debugging.\n            im_mask = torch.zeros(\n                0,\n                img_h,\n                img_w,\n                device=device,\n                dtype=torch.bool if mask_thr_binary >= 0 else torch.uint8)\n            results.masks = im_mask\n        results_list.append(results)\n    return results_list\n\n\ndef multi_apply(func, *args, **kwargs):\n    \"\"\"Apply function to a list of arguments.\n\n    Note:\n        This function applies the ``func`` to multiple inputs and\n        map the multiple outputs of the ``func`` into different\n        list. Each list contains the same type of outputs corresponding\n        to different inputs.\n\n    Args:\n        func (Function): A function that will be applied to a list of\n            arguments\n\n    Returns:\n        tuple(list): A tuple containing multiple list, each list contains \\\n            a kind of returned results by the function\n    \"\"\"\n    pfunc = partial(func, **kwargs) if kwargs else func\n    map_results = map(pfunc, *args)\n    return tuple(map(list, zip(*map_results)))\n\n\ndef unmap(data, count, inds, fill=0):\n    \"\"\"Unmap a subset of item (data) back to the original set of items (of size\n    count)\"\"\"\n    if data.dim() == 1:\n        ret = data.new_full((count, ), fill)\n        ret[inds.type(torch.bool)] = data\n    else:\n        new_size = (count, ) + data.size()[1:]\n        ret = data.new_full(new_size, fill)\n        ret[inds.type(torch.bool), :] = data\n    return ret\n\n\ndef mask2ndarray(mask):\n    \"\"\"Convert Mask to ndarray..\n\n    Args:\n        mask (:obj:`BitmapMasks` or :obj:`PolygonMasks` or\n        torch.Tensor or np.ndarray): The mask to be converted.\n\n    Returns:\n        np.ndarray: Ndarray mask of shape (n, h, w) that has been converted\n    \"\"\"\n    if isinstance(mask, (BitmapMasks, PolygonMasks)):\n        mask = mask.to_ndarray()\n    elif isinstance(mask, torch.Tensor):\n        mask = mask.detach().cpu().numpy()\n    elif not isinstance(mask, np.ndarray):\n        raise TypeError(f'Unsupported {type(mask)} data type')\n    return mask\n\n\ndef flip_tensor(src_tensor, flip_direction):\n    \"\"\"flip tensor base on flip_direction.\n\n    Args:\n        src_tensor (Tensor): input feature map, shape (B, C, H, W).\n        flip_direction (str): The flipping direction. Options are\n          'horizontal', 'vertical', 'diagonal'.\n\n    Returns:\n        out_tensor (Tensor): Flipped tensor.\n    \"\"\"\n    assert src_tensor.ndim == 4\n    valid_directions = ['horizontal', 'vertical', 'diagonal']\n    assert flip_direction in valid_directions\n    if flip_direction == 'horizontal':\n        out_tensor = torch.flip(src_tensor, [3])\n    elif flip_direction == 'vertical':\n        out_tensor = torch.flip(src_tensor, [2])\n    else:\n        out_tensor = torch.flip(src_tensor, [2, 3])\n    return out_tensor\n\n\ndef select_single_mlvl(mlvl_tensors, batch_id, detach=True):\n    \"\"\"Extract a multi-scale single image tensor from a multi-scale batch\n    tensor based on batch index.\n\n    Note: The default value of detach is True, because the proposal gradient\n    needs to be detached during the training of the two-stage model. E.g\n    Cascade Mask R-CNN.\n\n    Args:\n        mlvl_tensors (list[Tensor]): Batch tensor for all scale levels,\n           each is a 4D-tensor.\n        batch_id (int): Batch index.\n        detach (bool): Whether detach gradient. Default True.\n\n    Returns:\n        list[Tensor]: Multi-scale single image tensor.\n    \"\"\"\n    assert isinstance(mlvl_tensors, (list, tuple))\n    num_levels = len(mlvl_tensors)\n\n    if detach:\n        mlvl_tensor_list = [\n            mlvl_tensors[i][batch_id].detach() for i in range(num_levels)\n        ]\n    else:\n        mlvl_tensor_list = [\n            mlvl_tensors[i][batch_id] for i in range(num_levels)\n        ]\n    return mlvl_tensor_list\n\n\ndef filter_scores_and_topk(scores, score_thr, topk, results=None):\n    \"\"\"Filter results using score threshold and topk candidates.\n\n    Args:\n        scores (Tensor): The scores, shape (num_bboxes, K).\n        score_thr (float): The score filter threshold.\n        topk (int): The number of topk candidates.\n        results (dict or list or Tensor, Optional): The results to\n           which the filtering rule is to be applied. The shape\n           of each item is (num_bboxes, N).\n\n    Returns:\n        tuple: Filtered results\n\n            - scores (Tensor): The scores after being filtered, \\\n                shape (num_bboxes_filtered, ).\n            - labels (Tensor): The class labels, shape \\\n                (num_bboxes_filtered, ).\n            - anchor_idxs (Tensor): The anchor indexes, shape \\\n                (num_bboxes_filtered, ).\n            - filtered_results (dict or list or Tensor, Optional): \\\n                The filtered results. The shape of each item is \\\n                (num_bboxes_filtered, N).\n    \"\"\"\n    valid_mask = scores > score_thr\n    scores = scores[valid_mask]\n    valid_idxs = torch.nonzero(valid_mask)\n\n    num_topk = min(topk, valid_idxs.size(0))\n    # torch.sort is actually faster than .topk (at least on GPUs)\n    scores, idxs = scores.sort(descending=True)\n    scores = scores[:num_topk]\n    topk_idxs = valid_idxs[idxs[:num_topk]]\n    keep_idxs, labels = topk_idxs.unbind(dim=1)\n\n    filtered_results = None\n    if results is not None:\n        if isinstance(results, dict):\n            filtered_results = {k: v[keep_idxs] for k, v in results.items()}\n        elif isinstance(results, list):\n            filtered_results = [result[keep_idxs] for result in results]\n        elif isinstance(results, torch.Tensor):\n            filtered_results = results[keep_idxs]\n        else:\n            raise NotImplementedError(f'Only supports dict or list or Tensor, '\n                                      f'but get {type(results)}.')\n    return scores, labels, keep_idxs, filtered_results\n\n\ndef center_of_mass(mask, esp=1e-6):\n    \"\"\"Calculate the centroid coordinates of the mask.\n\n    Args:\n        mask (Tensor): The mask to be calculated, shape (h, w).\n        esp (float): Avoid dividing by zero. Default: 1e-6.\n\n    Returns:\n        tuple[Tensor]: the coordinates of the center point of the mask.\n\n            - center_h (Tensor): the center point of the height.\n            - center_w (Tensor): the center point of the width.\n    \"\"\"\n    h, w = mask.shape\n    grid_h = torch.arange(h, device=mask.device)[:, None]\n    grid_w = torch.arange(w, device=mask.device)\n    normalizer = mask.sum().float().clamp(min=esp)\n    center_h = (mask * grid_h).sum() / normalizer\n    center_w = (mask * grid_w).sum() / normalizer\n    return center_h, center_w\n\n\ndef generate_coordinate(featmap_sizes, device='cuda'):\n    \"\"\"Generate the coordinate.\n\n    Args:\n        featmap_sizes (tuple): The feature to be calculated,\n            of shape (N, C, W, H).\n        device (str): The device where the feature will be put on.\n    Returns:\n        coord_feat (Tensor): The coordinate feature, of shape (N, 2, W, H).\n    \"\"\"\n\n    x_range = torch.linspace(-1, 1, featmap_sizes[-1], device=device)\n    y_range = torch.linspace(-1, 1, featmap_sizes[-2], device=device)\n    y, x = torch.meshgrid(y_range, x_range)\n    y = y.expand([featmap_sizes[0], 1, -1, -1])\n    x = x.expand([featmap_sizes[0], 1, -1, -1])\n    coord_feat = torch.cat([x, y], 1)\n\n    return coord_feat\n\n\ndef levels_to_images(mlvl_tensor: List[torch.Tensor]) -> List[torch.Tensor]:\n    \"\"\"Concat multi-level feature maps by image.\n\n    [feature_level0, feature_level1...] -> [feature_image0, feature_image1...]\n    Convert the shape of each element in mlvl_tensor from (N, C, H, W) to\n    (N, H*W , C), then split the element to N elements with shape (H*W, C), and\n    concat elements in same image of all level along first dimension.\n\n    Args:\n        mlvl_tensor (list[Tensor]): list of Tensor which collect from\n            corresponding level. Each element is of shape (N, C, H, W)\n\n    Returns:\n        list[Tensor]: A list that contains N tensors and each tensor is\n            of shape (num_elements, C)\n    \"\"\"\n    batch_size = mlvl_tensor[0].size(0)\n    batch_list = [[] for _ in range(batch_size)]\n    channels = mlvl_tensor[0].size(1)\n    for t in mlvl_tensor:\n        t = t.permute(0, 2, 3, 1)\n        t = t.view(batch_size, -1, channels).contiguous()\n        for img in range(batch_size):\n            batch_list[img].append(t[img])\n    return [torch.cat(item, 0) for item in batch_list]\n\n\ndef images_to_levels(target, num_levels):\n    \"\"\"Convert targets by image to targets by feature level.\n\n    [target_img0, target_img1] -> [target_level0, target_level1, ...]\n    \"\"\"\n    target = stack_boxes(target, 0)\n    level_targets = []\n    start = 0\n    for n in num_levels:\n        end = start + n\n        # level_targets.append(target[:, start:end].squeeze(0))\n        level_targets.append(target[:, start:end])\n        start = end\n    return level_targets\n\n\ndef samplelist_boxtype2tensor(batch_data_samples: SampleList) -> SampleList:\n    for data_samples in batch_data_samples:\n        if 'gt_instances' in data_samples:\n            bboxes = data_samples.gt_instances.get('bboxes', None)\n            if isinstance(bboxes, BaseBoxes):\n                data_samples.gt_instances.bboxes = bboxes.tensor\n        if 'pred_instances' in data_samples:\n            bboxes = data_samples.pred_instances.get('bboxes', None)\n            if isinstance(bboxes, BaseBoxes):\n                data_samples.pred_instances.bboxes = bboxes.tensor\n        if 'ignored_instances' in data_samples:\n            bboxes = data_samples.ignored_instances.get('bboxes', None)\n            if isinstance(bboxes, BaseBoxes):\n                data_samples.ignored_instances.bboxes = bboxes.tensor\n\n\n_torch_version_div_indexing = (\n    'parrots' not in torch.__version__\n    and digit_version(torch.__version__) >= digit_version('1.8'))\n\n\ndef floordiv(dividend, divisor, rounding_mode='trunc'):\n    if _torch_version_div_indexing:\n        return torch.div(dividend, divisor, rounding_mode=rounding_mode)\n    else:\n        return dividend // divisor\n\n\ndef _filter_gt_instances_by_score(batch_data_samples: SampleList,\n                                  score_thr: float) -> SampleList:\n    \"\"\"Filter ground truth (GT) instances by score.\n\n    Args:\n        batch_data_samples (SampleList): The Data\n            Samples. It usually includes information such as\n            `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n        score_thr (float): The score filter threshold.\n\n    Returns:\n        SampleList: The Data Samples filtered by score.\n    \"\"\"\n    for data_samples in batch_data_samples:\n        assert 'scores' in data_samples.gt_instances, \\\n            'there does not exit scores in instances'\n        if data_samples.gt_instances.bboxes.shape[0] > 0:\n            data_samples.gt_instances = data_samples.gt_instances[\n                data_samples.gt_instances.scores > score_thr]\n    return batch_data_samples\n\n\ndef _filter_gt_instances_by_size(batch_data_samples: SampleList,\n                                 wh_thr: tuple) -> SampleList:\n    \"\"\"Filter ground truth (GT) instances by size.\n\n    Args:\n        batch_data_samples (SampleList): The Data\n            Samples. It usually includes information such as\n            `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n        wh_thr (tuple):  Minimum width and height of bbox.\n\n    Returns:\n        SampleList: The Data Samples filtered by score.\n    \"\"\"\n    for data_samples in batch_data_samples:\n        bboxes = data_samples.gt_instances.bboxes\n        if bboxes.shape[0] > 0:\n            w = bboxes[:, 2] - bboxes[:, 0]\n            h = bboxes[:, 3] - bboxes[:, 1]\n            data_samples.gt_instances = data_samples.gt_instances[\n                (w > wh_thr[0]) & (h > wh_thr[1])]\n    return batch_data_samples\n\n\ndef filter_gt_instances(batch_data_samples: SampleList,\n                        score_thr: float = None,\n                        wh_thr: tuple = None):\n    \"\"\"Filter ground truth (GT) instances by score and/or size.\n\n    Args:\n        batch_data_samples (SampleList): The Data\n            Samples. It usually includes information such as\n            `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n        score_thr (float): The score filter threshold.\n        wh_thr (tuple):  Minimum width and height of bbox.\n\n    Returns:\n        SampleList: The Data Samples filtered by score and/or size.\n    \"\"\"\n\n    if score_thr is not None:\n        batch_data_samples = _filter_gt_instances_by_score(\n            batch_data_samples, score_thr)\n    if wh_thr is not None:\n        batch_data_samples = _filter_gt_instances_by_size(\n            batch_data_samples, wh_thr)\n    return batch_data_samples\n\n\ndef rename_loss_dict(prefix: str, losses: dict) -> dict:\n    \"\"\"Rename the key names in loss dict by adding a prefix.\n\n    Args:\n        prefix (str): The prefix for loss components.\n        losses (dict):  A dictionary of loss components.\n\n    Returns:\n            dict: A dictionary of loss components with prefix.\n    \"\"\"\n    return {prefix + k: v for k, v in losses.items()}\n\n\ndef reweight_loss_dict(losses: dict, weight: float) -> dict:\n    \"\"\"Reweight losses in the dict by weight.\n\n    Args:\n        losses (dict):  A dictionary of loss components.\n        weight (float): Weight for loss components.\n\n    Returns:\n            dict: A dictionary of weighted loss components.\n    \"\"\"\n    for name, loss in losses.items():\n        if 'loss' in name:\n            if isinstance(loss, Sequence):\n                losses[name] = [item * weight for item in loss]\n            else:\n                losses[name] = loss * weight\n    return losses\n\n\ndef relative_coordinate_maps(\n    locations: Tensor,\n    centers: Tensor,\n    strides: Tensor,\n    size_of_interest: int,\n    feat_sizes: Tuple[int],\n) -> Tensor:\n    \"\"\"Generate the relative coordinate maps with feat_stride.\n\n    Args:\n        locations (Tensor): The prior location of mask feature map.\n            It has shape (num_priors, 2).\n        centers (Tensor): The prior points of a object in\n            all feature pyramid. It has shape (num_pos, 2)\n        strides (Tensor): The prior strides of a object in\n            all feature pyramid. It has shape (num_pos, 1)\n        size_of_interest (int): The size of the region used in rel coord.\n        feat_sizes (Tuple[int]): The feature size H and W, which has 2 dims.\n    Returns:\n        rel_coord_feat (Tensor): The coordinate feature\n            of shape (num_pos, 2, H, W).\n    \"\"\"\n\n    H, W = feat_sizes\n    rel_coordinates = centers.reshape(-1, 1, 2) - locations.reshape(1, -1, 2)\n    rel_coordinates = rel_coordinates.permute(0, 2, 1).float()\n    rel_coordinates = rel_coordinates / (\n        strides[:, None, None] * size_of_interest)\n    return rel_coordinates.reshape(-1, 2, H, W)\n\n\ndef aligned_bilinear(tensor: Tensor, factor: int) -> Tensor:\n    \"\"\"aligned bilinear, used in original implement in CondInst:\n\n    https://github.com/aim-uofa/AdelaiDet/blob/\\\n    c0b2092ce72442b0f40972f7c6dda8bb52c46d16/adet/utils/comm.py#L23\n    \"\"\"\n\n    assert tensor.dim() == 4\n    assert factor >= 1\n    assert int(factor) == factor\n\n    if factor == 1:\n        return tensor\n\n    h, w = tensor.size()[2:]\n    tensor = F.pad(tensor, pad=(0, 1, 0, 1), mode='replicate')\n    oh = factor * h + 1\n    ow = factor * w + 1\n    tensor = F.interpolate(\n        tensor, size=(oh, ow), mode='bilinear', align_corners=True)\n    tensor = F.pad(\n        tensor, pad=(factor // 2, 0, factor // 2, 0), mode='replicate')\n\n    return tensor[:, :, :oh - 1, :ow - 1]\n\n\ndef unfold_wo_center(x, kernel_size: int, dilation: int) -> Tensor:\n    \"\"\"unfold_wo_center, used in original implement in BoxInst:\n\n    https://github.com/aim-uofa/AdelaiDet/blob/\\\n    4a3a1f7372c35b48ebf5f6adc59f135a0fa28d60/\\\n    adet/modeling/condinst/condinst.py#L53\n    \"\"\"\n    assert x.dim() == 4\n    assert kernel_size % 2 == 1\n\n    # using SAME padding\n    padding = (kernel_size + (dilation - 1) * (kernel_size - 1)) // 2\n    unfolded_x = F.unfold(\n        x, kernel_size=kernel_size, padding=padding, dilation=dilation)\n    unfolded_x = unfolded_x.reshape(\n        x.size(0), x.size(1), -1, x.size(2), x.size(3))\n    # remove the center pixels\n    size = kernel_size**2\n    unfolded_x = torch.cat(\n        (unfolded_x[:, :, :size // 2], unfolded_x[:, :, size // 2 + 1:]),\n        dim=2)\n\n    return unfolded_x\n\n\ndef padding_to(input_tensor: Tensor, max_len: int = 300) -> Tensor:\n    \"\"\"Pad the first dimension of `input_tensor` to `max_len`.\n\n    Args:\n        input_tensor (Tensor): The tensor to be padded,\n        max_len (int): Padding target size in the first dimension.\n            Default: 300\n    https://github.com/jshilong/DDQ/blob/ddq_detr/projects/models/utils.py#L19\n    Returns:\n        Tensor: The tensor padded with the first dimension size `max_len`.\n    \"\"\"\n    if max_len is None:\n        return input_tensor\n    num_padding = max_len - len(input_tensor)\n    if input_tensor.dim() > 1:\n        padding = input_tensor.new_zeros(\n            num_padding, *input_tensor.size()[1:], dtype=input_tensor.dtype)\n    else:\n        padding = input_tensor.new_zeros(num_padding, dtype=input_tensor.dtype)\n    output_tensor = torch.cat([input_tensor, padding], dim=0)\n    return output_tensor\n\n\ndef align_tensor(inputs: List[Tensor],\n                 max_len: Optional[int] = None) -> Tensor:\n    \"\"\"Pad each input to `max_len`, then stack them. If `max_len` is None, then\n    it is the max size of the first dimension of each input.\n\n        https://github.com/jshilong/DDQ/blob/ddq_detr/projects/models/\\\n        utils.py#L12\n\n    Args:\n        inputs (list[Tensor]): The tensors to be padded,\n            Each input should have the same shape except the first dimension.\n        max_len (int): Padding target size in the first dimension.\n            Default: None\n    Returns:\n        Tensor: Stacked inputs after padding in the first dimension.\n    \"\"\"\n    if max_len is None:\n        max_len = max([len(item) for item in inputs])\n\n    return torch.stack([padding_to(item, max_len) for item in inputs])\n"
  },
  {
    "path": "mmdet/models/utils/panoptic_gt_processing.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch\nfrom torch import Tensor\n\n\ndef preprocess_panoptic_gt(gt_labels: Tensor, gt_masks: Tensor,\n                           gt_semantic_seg: Tensor, num_things: int,\n                           num_stuff: int) -> Tuple[Tensor, Tensor]:\n    \"\"\"Preprocess the ground truth for a image.\n\n    Args:\n        gt_labels (Tensor): Ground truth labels of each bbox,\n            with shape (num_gts, ).\n        gt_masks (BitmapMasks): Ground truth masks of each instances\n            of a image, shape (num_gts, h, w).\n        gt_semantic_seg (Tensor | None): Ground truth of semantic\n            segmentation with the shape (1, h, w).\n            [0, num_thing_class - 1] means things,\n            [num_thing_class, num_class-1] means stuff,\n            255 means VOID. It's None when training instance segmentation.\n\n    Returns:\n        tuple[Tensor, Tensor]: a tuple containing the following targets.\n\n            - labels (Tensor): Ground truth class indices for a\n                image, with shape (n, ), n is the sum of number\n                of stuff type and number of instance in a image.\n            - masks (Tensor): Ground truth mask for a image, with\n                shape (n, h, w). Contains stuff and things when training\n                panoptic segmentation, and things only when training\n                instance segmentation.\n    \"\"\"\n    num_classes = num_things + num_stuff\n    things_masks = gt_masks.to_tensor(\n        dtype=torch.bool, device=gt_labels.device)\n\n    if gt_semantic_seg is None:\n        masks = things_masks.long()\n        return gt_labels, masks\n\n    things_labels = gt_labels\n    gt_semantic_seg = gt_semantic_seg.squeeze(0)\n\n    semantic_labels = torch.unique(\n        gt_semantic_seg,\n        sorted=False,\n        return_inverse=False,\n        return_counts=False)\n    stuff_masks_list = []\n    stuff_labels_list = []\n    for label in semantic_labels:\n        if label < num_things or label >= num_classes:\n            continue\n        stuff_mask = gt_semantic_seg == label\n        stuff_masks_list.append(stuff_mask)\n        stuff_labels_list.append(label)\n\n    if len(stuff_masks_list) > 0:\n        stuff_masks = torch.stack(stuff_masks_list, dim=0)\n        stuff_labels = torch.stack(stuff_labels_list, dim=0)\n        labels = torch.cat([things_labels, stuff_labels], dim=0)\n        masks = torch.cat([things_masks, stuff_masks], dim=0)\n    else:\n        labels = things_labels\n        masks = things_masks\n\n    masks = masks.long()\n    return labels, masks\n"
  },
  {
    "path": "mmdet/models/utils/point_sample.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nfrom mmcv.ops import point_sample\nfrom torch import Tensor\n\n\ndef get_uncertainty(mask_preds: Tensor, labels: Tensor) -> Tensor:\n    \"\"\"Estimate uncertainty based on pred logits.\n\n    We estimate uncertainty as L1 distance between 0.0 and the logits\n    prediction in 'mask_preds' for the foreground class in `classes`.\n\n    Args:\n        mask_preds (Tensor): mask predication logits, shape (num_rois,\n            num_classes, mask_height, mask_width).\n\n        labels (Tensor): Either predicted or ground truth label for\n            each predicted mask, of length num_rois.\n\n    Returns:\n        scores (Tensor): Uncertainty scores with the most uncertain\n            locations having the highest uncertainty score,\n            shape (num_rois, 1, mask_height, mask_width)\n    \"\"\"\n    if mask_preds.shape[1] == 1:\n        gt_class_logits = mask_preds.clone()\n    else:\n        inds = torch.arange(mask_preds.shape[0], device=mask_preds.device)\n        gt_class_logits = mask_preds[inds, labels].unsqueeze(1)\n    return -torch.abs(gt_class_logits)\n\n\ndef get_uncertain_point_coords_with_randomness(\n        mask_preds: Tensor, labels: Tensor, num_points: int,\n        oversample_ratio: float, importance_sample_ratio: float) -> Tensor:\n    \"\"\"Get ``num_points`` most uncertain points with random points during\n    train.\n\n    Sample points in [0, 1] x [0, 1] coordinate space based on their\n    uncertainty. The uncertainties are calculated for each point using\n    'get_uncertainty()' function that takes point's logit prediction as\n    input.\n\n    Args:\n        mask_preds (Tensor): A tensor of shape (num_rois, num_classes,\n            mask_height, mask_width) for class-specific or class-agnostic\n            prediction.\n        labels (Tensor): The ground truth class for each instance.\n        num_points (int): The number of points to sample.\n        oversample_ratio (float): Oversampling parameter.\n        importance_sample_ratio (float): Ratio of points that are sampled\n            via importnace sampling.\n\n    Returns:\n        point_coords (Tensor): A tensor of shape (num_rois, num_points, 2)\n            that contains the coordinates sampled points.\n    \"\"\"\n    assert oversample_ratio >= 1\n    assert 0 <= importance_sample_ratio <= 1\n    batch_size = mask_preds.shape[0]\n    num_sampled = int(num_points * oversample_ratio)\n    point_coords = torch.rand(\n        batch_size, num_sampled, 2, device=mask_preds.device)\n    point_logits = point_sample(mask_preds, point_coords)\n    # It is crucial to calculate uncertainty based on the sampled\n    # prediction value for the points. Calculating uncertainties of the\n    # coarse predictions first and sampling them for points leads to\n    # incorrect results.  To illustrate this: assume uncertainty func(\n    # logits)=-abs(logits), a sampled point between two coarse\n    # predictions with -1 and 1 logits has 0 logits, and therefore 0\n    # uncertainty value. However, if we calculate uncertainties for the\n    # coarse predictions first, both will have -1 uncertainty,\n    # and sampled point will get -1 uncertainty.\n    point_uncertainties = get_uncertainty(point_logits, labels)\n    num_uncertain_points = int(importance_sample_ratio * num_points)\n    num_random_points = num_points - num_uncertain_points\n    idx = torch.topk(\n        point_uncertainties[:, 0, :], k=num_uncertain_points, dim=1)[1]\n    shift = num_sampled * torch.arange(\n        batch_size, dtype=torch.long, device=mask_preds.device)\n    idx += shift[:, None]\n    point_coords = point_coords.view(-1, 2)[idx.view(-1), :].view(\n        batch_size, num_uncertain_points, 2)\n    if num_random_points > 0:\n        rand_roi_coords = torch.rand(\n            batch_size, num_random_points, 2, device=mask_preds.device)\n        point_coords = torch.cat((point_coords, rand_roi_coords), dim=1)\n    return point_coords\n"
  },
  {
    "path": "mmdet/models/utils/vlfuse_helper.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Modified from https://github.com/microsoft/GLIP/blob/main/maskrcnn_benchmark/utils/fuse_helper.py  # noqa\n# and https://github.com/microsoft/GLIP/blob/main/maskrcnn_benchmark/modeling/rpn/modeling_bert.py  # noqa\nimport math\nfrom typing import Dict, Optional, Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.checkpoint as checkpoint\nfrom mmcv.cnn.bricks import DropPath\nfrom torch import Tensor\n\ntry:\n    from transformers import BertConfig, BertPreTrainedModel\n    from transformers.modeling_utils import apply_chunking_to_forward\n    from transformers.models.bert.modeling_bert import \\\n        BertAttention as HFBertAttention\n    from transformers.models.bert.modeling_bert import \\\n        BertIntermediate as HFBertIntermediate\n    from transformers.models.bert.modeling_bert import \\\n        BertOutput as HFBertOutput\nexcept ImportError:\n    BertConfig = None\n    BertPreTrainedModel = object\n    apply_chunking_to_forward = None\n    HFBertAttention = object\n    HFBertIntermediate = object\n    HFBertOutput = object\n\nMAX_CLAMP_VALUE = 50000\n\n\ndef permute_and_flatten(layer: Tensor, N: int, A: int, C: int, H: int,\n                        W: int) -> Tensor:\n    \"\"\"Permute and then flatten a tensor,\n\n       from size (N, A, C, H, W) to (N, H * W * A, C).\n\n    Args:\n        layer (Tensor): Tensor of shape (N, C, H, W).\n        N (int): Batch size.\n        A (int): Number of attention heads.\n        C (int): Number of channels.\n        H (int): Height of feature map.\n        W (int): Width of feature map.\n\n    Returns:\n        Tensor: A Tensor of shape (N, H * W * A, C).\n    \"\"\"\n    layer = layer.view(N, A, C, H, W)\n    layer = layer.permute(0, 3, 4, 1, 2)\n    layer = layer.reshape(N, -1, C)\n    return layer\n\n\ndef clamp_values(vector: Tensor) -> Tensor:\n    \"\"\"Clamp the values of a vector to the range [-MAX_CLAMP_VALUE,\n    MAX_CLAMP_VALUE].\n\n    Args:\n        vector (Tensor): Tensor of shape (N, C, H, W).\n\n    Returns:\n        Tensor: A Tensor of shape (N, C, H, W) with clamped values.\n    \"\"\"\n    vector = torch.clamp(vector, min=-MAX_CLAMP_VALUE, max=MAX_CLAMP_VALUE)\n    return vector\n\n\nclass BiMultiHeadAttention(nn.Module):\n    \"\"\"Bidirectional fusion Multi-Head Attention layer.\n\n    Args:\n        v_dim (int): The dimension of the vision input.\n        l_dim (int): The dimension of the language input.\n        embed_dim (int): The embedding dimension for the attention operation.\n        num_heads (int): The number of attention heads.\n        dropout (float, optional): The dropout probability. Defaults to 0.1.\n    \"\"\"\n\n    def __init__(self,\n                 v_dim: int,\n                 l_dim: int,\n                 embed_dim: int,\n                 num_heads: int,\n                 dropout: float = 0.1):\n        super(BiMultiHeadAttention, self).__init__()\n\n        self.embed_dim = embed_dim\n        self.num_heads = num_heads\n        self.head_dim = embed_dim // num_heads\n        self.v_dim = v_dim\n        self.l_dim = l_dim\n\n        assert (\n            self.head_dim * self.num_heads == self.embed_dim\n        ), 'embed_dim must be divisible by num_heads ' \\\n           f'(got `embed_dim`: {self.embed_dim} ' \\\n           f'and `num_heads`: {self.num_heads}).'\n        self.scale = self.head_dim**(-0.5)\n        self.dropout = dropout\n\n        self.v_proj = nn.Linear(self.v_dim, self.embed_dim)\n        self.l_proj = nn.Linear(self.l_dim, self.embed_dim)\n        self.values_v_proj = nn.Linear(self.v_dim, self.embed_dim)\n        self.values_l_proj = nn.Linear(self.l_dim, self.embed_dim)\n\n        self.out_v_proj = nn.Linear(self.embed_dim, self.v_dim)\n        self.out_l_proj = nn.Linear(self.embed_dim, self.l_dim)\n\n        self.stable_softmax_2d = False\n        self.clamp_min_for_underflow = True\n        self.clamp_max_for_overflow = True\n\n        self._reset_parameters()\n\n    def _shape(self, tensor: Tensor, seq_len: int, bsz: int):\n        return tensor.view(bsz, seq_len, self.num_heads,\n                           self.head_dim).transpose(1, 2).contiguous()\n\n    def _reset_parameters(self):\n        nn.init.xavier_uniform_(self.v_proj.weight)\n        self.v_proj.bias.data.fill_(0)\n        nn.init.xavier_uniform_(self.l_proj.weight)\n        self.l_proj.bias.data.fill_(0)\n        nn.init.xavier_uniform_(self.values_v_proj.weight)\n        self.values_v_proj.bias.data.fill_(0)\n        nn.init.xavier_uniform_(self.values_l_proj.weight)\n        self.values_l_proj.bias.data.fill_(0)\n        nn.init.xavier_uniform_(self.out_v_proj.weight)\n        self.out_v_proj.bias.data.fill_(0)\n        nn.init.xavier_uniform_(self.out_l_proj.weight)\n        self.out_l_proj.bias.data.fill_(0)\n\n    def forward(\n        self,\n        vision: Tensor,\n        lang: Tensor,\n        attention_mask_v: Optional[Tensor] = None,\n        attention_mask_l: Optional[Tensor] = None,\n    ) -> Tuple[Tensor, Tensor]:\n        bsz, tgt_len, _ = vision.size()\n\n        query_states = self.v_proj(vision) * self.scale\n        key_states = self._shape(self.l_proj(lang), -1, bsz)\n        value_v_states = self._shape(self.values_v_proj(vision), -1, bsz)\n        value_l_states = self._shape(self.values_l_proj(lang), -1, bsz)\n\n        proj_shape = (bsz * self.num_heads, -1, self.head_dim)\n        query_states = self._shape(query_states, tgt_len,\n                                   bsz).view(*proj_shape)\n        key_states = key_states.view(*proj_shape)\n        value_v_states = value_v_states.view(*proj_shape)\n        value_l_states = value_l_states.view(*proj_shape)\n\n        src_len = key_states.size(1)\n        attn_weights = torch.bmm(query_states, key_states.transpose(1, 2))\n\n        if attn_weights.size() != (bsz * self.num_heads, tgt_len, src_len):\n            raise ValueError(\n                f'Attention weights should be of '\n                f'size {(bsz * self.num_heads, tgt_len, src_len)}, '\n                f'but is {attn_weights.size()}')\n\n        if self.stable_softmax_2d:\n            attn_weights = attn_weights - attn_weights.max()\n\n        if self.clamp_min_for_underflow:\n            # Do not increase -50000, data type half has quite limited range\n            attn_weights = torch.clamp(attn_weights, min=-MAX_CLAMP_VALUE)\n        if self.clamp_max_for_overflow:\n            # Do not increase 50000, data type half has quite limited range\n            attn_weights = torch.clamp(attn_weights, max=MAX_CLAMP_VALUE)\n\n        attn_weights_T = attn_weights.transpose(1, 2)\n        attn_weights_l = (\n            attn_weights_T -\n            torch.max(attn_weights_T, dim=-1, keepdim=True)[0])\n        if self.clamp_min_for_underflow:\n            # Do not increase -50000, data type half has quite limited range\n            attn_weights_l = torch.clamp(attn_weights_l, min=-MAX_CLAMP_VALUE)\n        if self.clamp_max_for_overflow:\n            # Do not increase 50000, data type half has quite limited range\n            attn_weights_l = torch.clamp(attn_weights_l, max=MAX_CLAMP_VALUE)\n\n        if attention_mask_v is not None:\n            attention_mask_v = (\n                attention_mask_v[:, None,\n                                 None, :].repeat(1, self.num_heads, 1,\n                                                 1).flatten(0, 1))\n            attn_weights_l.masked_fill_(attention_mask_v, float('-inf'))\n\n        attn_weights_l = attn_weights_l.softmax(dim=-1)\n\n        if attention_mask_l is not None:\n            assert (attention_mask_l.dim() == 2)\n            attention_mask = attention_mask_l.unsqueeze(1).unsqueeze(1)\n            attention_mask = attention_mask.expand(bsz, 1, tgt_len, src_len)\n            attention_mask = attention_mask.masked_fill(\n                attention_mask == 0, -9e15)\n\n            if attention_mask.size() != (bsz, 1, tgt_len, src_len):\n                raise ValueError('Attention mask should be of '\n                                 f'size {(bsz, 1, tgt_len, src_len)}')\n            attn_weights = attn_weights.view(bsz, self.num_heads, tgt_len,\n                                             src_len) + attention_mask\n            attn_weights = attn_weights.view(bsz * self.num_heads, tgt_len,\n                                             src_len)\n\n        attn_weights_v = nn.functional.softmax(attn_weights, dim=-1)\n\n        attn_probs_v = F.dropout(\n            attn_weights_v, p=self.dropout, training=self.training)\n        attn_probs_l = F.dropout(\n            attn_weights_l, p=self.dropout, training=self.training)\n\n        attn_output_v = torch.bmm(attn_probs_v, value_l_states)\n        attn_output_l = torch.bmm(attn_probs_l, value_v_states)\n\n        if attn_output_v.size() != (bsz * self.num_heads, tgt_len,\n                                    self.head_dim):\n            raise ValueError(\n                '`attn_output_v` should be of '\n                f'size {(bsz, self.num_heads, tgt_len, self.head_dim)}, '\n                f'but is {attn_output_v.size()}')\n\n        if attn_output_l.size() != (bsz * self.num_heads, src_len,\n                                    self.head_dim):\n            raise ValueError(\n                '`attn_output_l` should be of size '\n                f'{(bsz, self.num_heads, src_len, self.head_dim)}, '\n                f'but is {attn_output_l.size()}')\n\n        attn_output_v = attn_output_v.view(bsz, self.num_heads, tgt_len,\n                                           self.head_dim)\n        attn_output_v = attn_output_v.transpose(1, 2)\n        attn_output_v = attn_output_v.reshape(bsz, tgt_len, self.embed_dim)\n\n        attn_output_l = attn_output_l.view(bsz, self.num_heads, src_len,\n                                           self.head_dim)\n        attn_output_l = attn_output_l.transpose(1, 2)\n        attn_output_l = attn_output_l.reshape(bsz, src_len, self.embed_dim)\n\n        attn_output_v = self.out_v_proj(attn_output_v)\n        attn_output_l = self.out_l_proj(attn_output_l)\n\n        return attn_output_v, attn_output_l\n\n\nclass BiAttentionBlock(nn.Module):\n    \"\"\"BiAttentionBlock Module:\n\n    First, multi-level visual features are concat; Then the concat visual\n    feature and lang feature are fused by attention; Finally the newly visual\n    feature are split into multi levels.\n\n    Args:\n        v_dim (int): The dimension of the visual features.\n        l_dim (int): The dimension of the language feature.\n        embed_dim (int): The embedding dimension for the attention operation.\n        num_heads (int): The number of attention heads.\n        dropout (float, optional): The dropout probability. Defaults to 0.1.\n        drop_path (float, optional): The drop path probability.\n            Defaults to 0.0.\n        init_values (float, optional):\n            The initial value for the scaling parameter.\n            Defaults to 1e-4.\n    \"\"\"\n\n    def __init__(self,\n                 v_dim: int,\n                 l_dim: int,\n                 embed_dim: int,\n                 num_heads: int,\n                 dropout: float = 0.1,\n                 drop_path: float = .0,\n                 init_values: float = 1e-4):\n        super().__init__()\n\n        # pre layer norm\n        self.layer_norm_v = nn.LayerNorm(v_dim)\n        self.layer_norm_l = nn.LayerNorm(l_dim)\n        self.attn = BiMultiHeadAttention(\n            v_dim=v_dim,\n            l_dim=l_dim,\n            embed_dim=embed_dim,\n            num_heads=num_heads,\n            dropout=dropout)\n\n        # add layer scale for training stability\n        self.drop_path = DropPath(\n            drop_path) if drop_path > 0. else nn.Identity()\n        self.gamma_v = nn.Parameter(\n            init_values * torch.ones(v_dim), requires_grad=True)\n        self.gamma_l = nn.Parameter(\n            init_values * torch.ones(l_dim), requires_grad=True)\n\n    def forward(self,\n                vf0: Tensor,\n                vf1: Tensor,\n                vf2: Tensor,\n                vf3: Tensor,\n                vf4: Tensor,\n                lang_feature: Tensor,\n                attention_mask_l=None):\n        visual_features = [vf0, vf1, vf2, vf3, vf4]\n        size_per_level, visual_features_flatten = [], []\n        for i, feat_per_level in enumerate(visual_features):\n            bs, c, h, w = feat_per_level.shape\n            size_per_level.append([h, w])\n            feat = permute_and_flatten(feat_per_level, bs, -1, c, h, w)\n            visual_features_flatten.append(feat)\n        visual_features_flatten = torch.cat(visual_features_flatten, dim=1)\n        new_v, new_lang_feature = self.single_attention_call(\n            visual_features_flatten,\n            lang_feature,\n            attention_mask_l=attention_mask_l)\n        # [bs, N, C] -> [bs, C, N]\n        new_v = new_v.transpose(1, 2).contiguous()\n\n        start = 0\n        # fvfs is mean fusion_visual_features\n        fvfs = []\n        for (h, w) in size_per_level:\n            new_v_per_level = new_v[:, :,\n                                    start:start + h * w].view(bs, -1, h,\n                                                              w).contiguous()\n            fvfs.append(new_v_per_level)\n            start += h * w\n\n        return fvfs[0], fvfs[1], fvfs[2], fvfs[3], fvfs[4], new_lang_feature\n\n    def single_attention_call(\n        self,\n        visual: Tensor,\n        lang: Tensor,\n        attention_mask_v: Optional[Tensor] = None,\n        attention_mask_l: Optional[Tensor] = None,\n    ) -> Tuple[Tensor, Tensor]:\n        \"\"\"Perform a single attention call between the visual and language\n        inputs.\n\n        Args:\n        visual (Tensor): The visual input tensor.\n        lang (Tensor): The language input tensor.\n        attention_mask_v (Optional[Tensor]):\n            An optional attention mask tensor for the visual input.\n        attention_mask_l (Optional[Tensor]):\n            An optional attention mask tensor for the language input.\n\n        Returns:\n            Tuple[Tensor, Tensor]: A tuple containing the updated\n                visual and language tensors after the attention call.\n        \"\"\"\n        visual = self.layer_norm_v(visual)\n        lang = self.layer_norm_l(lang)\n        delta_v, delta_l = self.attn(\n            visual,\n            lang,\n            attention_mask_v=attention_mask_v,\n            attention_mask_l=attention_mask_l)\n        # visual, lang = visual + delta_v, l + delta_l\n        visual = visual + self.drop_path(self.gamma_v * delta_v)\n        lang = lang + self.drop_path(self.gamma_l * delta_l)\n        return visual, lang\n\n\nclass SingleScaleBiAttentionBlock(BiAttentionBlock):\n    \"\"\"This is a single-scale implementation of `BiAttentionBlock`.\n\n    The only differenece between it and `BiAttentionBlock` is that the\n    `forward` function of `SingleScaleBiAttentionBlock` only accepts a single\n    flatten visual feature map, while the `forward` function in\n    `BiAttentionBlock` accepts multiple visual feature maps.\n    \"\"\"\n\n    def forward(self,\n                visual_feature: Tensor,\n                lang_feature: Tensor,\n                attention_mask_v=None,\n                attention_mask_l=None):\n        \"\"\"Single-scale forward pass.\n\n        Args:\n            visual_feature (Tensor): The visual input tensor. Tensor of\n                shape (bs, patch_len, ch).\n            lang_feature (Tensor): The language input tensor. Tensor of\n                shape (bs, text_len, ch).\n            attention_mask_v (_type_, optional): Visual feature attention\n                mask. Defaults to None.\n            attention_mask_l (_type_, optional): Language feature attention\n                mask.Defaults to None.\n        \"\"\"\n        new_v, new_lang_feature = self.single_attention_call(\n            visual_feature,\n            lang_feature,\n            attention_mask_v=attention_mask_v,\n            attention_mask_l=attention_mask_l)\n        return new_v, new_lang_feature\n\n\nclass VLFuse(nn.Module):\n    \"\"\"Early Fusion Module.\n\n    Args:\n        v_dim (int): Dimension of visual features.\n        l_dim (int): Dimension of language features.\n        embed_dim (int): The embedding dimension for the attention operation.\n        num_heads (int): Number of attention heads.\n        dropout (float): Dropout probability.\n        drop_path (float): Drop path probability.\n        use_checkpoint (bool): Whether to use PyTorch's checkpoint function.\n    \"\"\"\n\n    def __init__(self,\n                 v_dim: int = 256,\n                 l_dim: int = 768,\n                 embed_dim: int = 2048,\n                 num_heads: int = 8,\n                 dropout: float = 0.1,\n                 drop_path: float = 0.0,\n                 use_checkpoint: bool = False):\n        super().__init__()\n        self.use_checkpoint = use_checkpoint\n        self.b_attn = BiAttentionBlock(\n            v_dim=v_dim,\n            l_dim=l_dim,\n            embed_dim=embed_dim,\n            num_heads=num_heads,\n            dropout=dropout,\n            drop_path=drop_path,\n            init_values=1.0 / 6.0)\n\n    def forward(self, x: dict) -> dict:\n        \"\"\"Forward pass of the VLFuse module.\"\"\"\n        visual_features = x['visual']\n        language_dict_features = x['lang']\n\n        if self.use_checkpoint:\n            # vf is mean visual_features\n            # checkpoint does not allow complex data structures as input,\n            # such as list, so we must split them.\n            vf0, vf1, vf2, vf3, vf4, language_features = checkpoint.checkpoint(\n                self.b_attn, *visual_features,\n                language_dict_features['hidden'],\n                language_dict_features['masks'])\n        else:\n            vf0, vf1, vf2, vf3, vf4, language_features = self.b_attn(\n                *visual_features, language_dict_features['hidden'],\n                language_dict_features['masks'])\n\n        language_dict_features['hidden'] = language_features\n        fused_language_dict_features = language_dict_features\n\n        features_dict = {\n            'visual': [vf0, vf1, vf2, vf3, vf4],\n            'lang': fused_language_dict_features\n        }\n\n        return features_dict\n\n\nclass BertEncoderLayer(BertPreTrainedModel):\n    \"\"\"A modified version of the `BertLayer` class from the\n    `transformers.models.bert.modeling_bert` module.\n\n    Args:\n        config (:class:`~transformers.BertConfig`):\n            The configuration object that\n            contains various parameters for the model.\n        clamp_min_for_underflow (bool, optional):\n            Whether to clamp the minimum value of the hidden states\n             to prevent underflow. Defaults to `False`.\n        clamp_max_for_overflow (bool, optional):\n            Whether to clamp the maximum value of the hidden states\n            to prevent overflow. Defaults to `False`.\n    \"\"\"\n\n    def __init__(self,\n                 config: BertConfig,\n                 clamp_min_for_underflow: bool = False,\n                 clamp_max_for_overflow: bool = False):\n        super().__init__(config)\n        self.config = config\n        self.chunk_size_feed_forward = config.chunk_size_feed_forward\n        self.seq_len_dim = 1\n\n        self.attention = BertAttention(config, clamp_min_for_underflow,\n                                       clamp_max_for_overflow)\n        self.intermediate = BertIntermediate(config)\n        self.output = BertOutput(config)\n\n    def forward(\n        self, inputs: Dict[str, Dict[str, torch.Tensor]]\n    ) -> Dict[str, Dict[str, torch.Tensor]]:\n        \"\"\"Applies the BertEncoderLayer to the input features.\"\"\"\n        language_dict_features = inputs['lang']\n        hidden_states = language_dict_features['hidden']\n        attention_mask = language_dict_features['masks']\n\n        device = hidden_states.device\n        input_shape = hidden_states.size()[:-1]\n        extended_attention_mask = self.get_extended_attention_mask(\n            attention_mask, input_shape, device)\n\n        self_attention_outputs = self.attention(\n            hidden_states,\n            extended_attention_mask,\n            None,\n            output_attentions=False,\n            past_key_value=None)\n        attention_output = self_attention_outputs[0]\n        outputs = self_attention_outputs[1:]\n        layer_output = apply_chunking_to_forward(self.feed_forward_chunk,\n                                                 self.chunk_size_feed_forward,\n                                                 self.seq_len_dim,\n                                                 attention_output)\n        outputs = (layer_output, ) + outputs\n        hidden_states = outputs[0]\n\n        language_dict_features['hidden'] = hidden_states\n\n        features_dict = {\n            'visual': inputs['visual'],\n            'lang': language_dict_features\n        }\n\n        return features_dict\n\n    def feed_forward_chunk(self, attention_output: Tensor) -> Tensor:\n        \"\"\"Applies the intermediate and output layers of the BertEncoderLayer\n        to a chunk of the input sequence.\"\"\"\n        intermediate_output = self.intermediate(attention_output)\n        layer_output = self.output(intermediate_output, attention_output)\n        return layer_output\n\n\n# The following code is the same as the Huggingface code,\n# with the only difference being the additional clamp operation.\nclass BertSelfAttention(nn.Module):\n    \"\"\"BERT self-attention layer from Huggingface transformers.\n\n    Compared to the BertSelfAttention of Huggingface, only add the clamp.\n\n    Args:\n        config (:class:`~transformers.BertConfig`):\n            The configuration object that\n            contains various parameters for the model.\n        clamp_min_for_underflow (bool, optional):\n            Whether to clamp the minimum value of the hidden states\n             to prevent underflow. Defaults to `False`.\n        clamp_max_for_overflow (bool, optional):\n            Whether to clamp the maximum value of the hidden states\n            to prevent overflow. Defaults to `False`.\n    \"\"\"\n\n    def __init__(self,\n                 config: BertConfig,\n                 clamp_min_for_underflow: bool = False,\n                 clamp_max_for_overflow: bool = False):\n        super().__init__()\n        if config.hidden_size % config.num_attention_heads != 0 and \\\n                not hasattr(config, 'embedding_size'):\n            raise ValueError(f'The hidden size ({config.hidden_size}) is '\n                             'not a multiple of the number of attention '\n                             f'heads ({config.num_attention_heads})')\n\n        self.num_attention_heads = config.num_attention_heads\n        self.attention_head_size = int(config.hidden_size /\n                                       config.num_attention_heads)\n        self.all_head_size = self.num_attention_heads * \\\n            self.attention_head_size\n\n        self.query = nn.Linear(config.hidden_size, self.all_head_size)\n        self.key = nn.Linear(config.hidden_size, self.all_head_size)\n        self.value = nn.Linear(config.hidden_size, self.all_head_size)\n\n        self.dropout = nn.Dropout(config.attention_probs_dropout_prob)\n        self.position_embedding_type = getattr(config,\n                                               'position_embedding_type',\n                                               'absolute')\n        if self.position_embedding_type == 'relative_key' or \\\n                self.position_embedding_type == 'relative_key_query':\n            self.max_position_embeddings = config.max_position_embeddings\n            self.distance_embedding = nn.Embedding(\n                2 * config.max_position_embeddings - 1,\n                self.attention_head_size)\n        self.clamp_min_for_underflow = clamp_min_for_underflow\n        self.clamp_max_for_overflow = clamp_max_for_overflow\n\n        self.is_decoder = config.is_decoder\n\n    def transpose_for_scores(self, x: Tensor) -> Tensor:\n        \"\"\"Transpose the dimensions of `x`.\"\"\"\n        new_x_shape = x.size()[:-1] + (self.num_attention_heads,\n                                       self.attention_head_size)\n        x = x.view(*new_x_shape)\n        return x.permute(0, 2, 1, 3)\n\n    def forward(\n        self,\n        hidden_states: Tensor,\n        attention_mask: Optional[Tensor] = None,\n        head_mask: Optional[Tensor] = None,\n        encoder_hidden_states: Optional[Tensor] = None,\n        encoder_attention_mask: Optional[Tensor] = None,\n        past_key_value: Optional[Tuple[Tensor, Tensor]] = None,\n        output_attentions: bool = False,\n    ) -> Tuple[Tensor, ...]:\n        \"\"\"Perform a forward pass through the BERT self-attention layer.\"\"\"\n\n        mixed_query_layer = self.query(hidden_states)\n\n        # If this is instantiated as a cross-attention module, the keys\n        # and values come from an encoder; the attention mask needs to be\n        # such that the encoder's padding tokens are not attended to.\n        is_cross_attention = encoder_hidden_states is not None\n\n        if is_cross_attention and past_key_value is not None:\n            # reuse k,v, cross_attentions\n            key_layer = past_key_value[0]\n            value_layer = past_key_value[1]\n            attention_mask = encoder_attention_mask\n        elif is_cross_attention:\n            key_layer = self.transpose_for_scores(\n                self.key(encoder_hidden_states))\n            value_layer = self.transpose_for_scores(\n                self.value(encoder_hidden_states))\n            attention_mask = encoder_attention_mask\n        elif past_key_value is not None:\n            key_layer = self.transpose_for_scores(self.key(hidden_states))\n            value_layer = self.transpose_for_scores(self.value(hidden_states))\n            key_layer = torch.cat([past_key_value[0], key_layer], dim=2)\n            value_layer = torch.cat([past_key_value[1], value_layer], dim=2)\n        else:\n            key_layer = self.transpose_for_scores(self.key(hidden_states))\n            value_layer = self.transpose_for_scores(self.value(hidden_states))\n\n        query_layer = self.transpose_for_scores(mixed_query_layer)\n\n        if self.is_decoder:\n            past_key_value = (key_layer, value_layer)\n\n        # Take the dot product between \"query\" and \"key\"\n        # to get the raw attention scores.\n        attention_scores = torch.matmul(query_layer,\n                                        key_layer.transpose(-1, -2))\n\n        if self.position_embedding_type == 'relative_key' or \\\n                self.position_embedding_type == 'relative_key_query':\n            seq_length = hidden_states.size()[1]\n            position_ids_l = torch.arange(\n                seq_length, dtype=torch.long,\n                device=hidden_states.device).view(-1, 1)\n            position_ids_r = torch.arange(\n                seq_length, dtype=torch.long,\n                device=hidden_states.device).view(1, -1)\n            distance = position_ids_l - position_ids_r\n            positional_embedding = self.distance_embedding(\n                distance + self.max_position_embeddings - 1)\n            positional_embedding = positional_embedding.to(\n                dtype=query_layer.dtype)  # fp16 compatibility\n\n            if self.position_embedding_type == 'relative_key':\n                relative_position_scores = torch.einsum(\n                    'bhld,lrd->bhlr', query_layer, positional_embedding)\n                attention_scores = attention_scores + relative_position_scores\n            elif self.position_embedding_type == 'relative_key_query':\n                relative_position_scores_query = torch.einsum(\n                    'bhld,lrd->bhlr', query_layer, positional_embedding)\n                relative_position_scores_key = torch.einsum(\n                    'bhrd,lrd->bhlr', key_layer, positional_embedding)\n                attention_scores = attention_scores + \\\n                    relative_position_scores_query + \\\n                    relative_position_scores_key\n\n        attention_scores = attention_scores / math.sqrt(\n            self.attention_head_size)\n\n        if self.clamp_min_for_underflow:\n            attention_scores = torch.clamp(\n                attention_scores, min=-MAX_CLAMP_VALUE\n            )  # Do not increase -50000, data type half has quite limited range\n        if self.clamp_max_for_overflow:\n            attention_scores = torch.clamp(\n                attention_scores, max=MAX_CLAMP_VALUE\n            )  # Do not increase 50000, data type half has quite limited range\n\n        if attention_mask is not None:\n            # Apply the attention mask is\n            # (precomputed for all layers in BertModel forward() function)\n            attention_scores = attention_scores + attention_mask\n\n        # Normalize the attention scores to probabilities.\n        attention_probs = nn.Softmax(dim=-1)(attention_scores)\n\n        # This is actually dropping out entire tokens to attend to, which might\n        # seem a bit unusual, but is taken from the original Transformer paper.\n        attention_probs = self.dropout(attention_probs)\n\n        # Mask heads if we want to\n        if head_mask is not None:\n            attention_probs = attention_probs * head_mask\n\n        context_layer = torch.matmul(attention_probs, value_layer)\n\n        context_layer = context_layer.permute(0, 2, 1, 3).contiguous()\n        new_context_layer_shape = context_layer.size()[:-2] + (\n            self.all_head_size, )\n        context_layer = context_layer.view(*new_context_layer_shape)\n\n        outputs = (context_layer,\n                   attention_probs) if output_attentions else (context_layer, )\n\n        if self.is_decoder:\n            outputs = outputs + (past_key_value, )\n        return outputs\n\n\nclass BertAttention(HFBertAttention):\n    \"\"\"BertAttention is made up of self-attention and intermediate+output.\n\n    Compared to the BertAttention of Huggingface, only add the clamp.\n\n    Args:\n        config (:class:`~transformers.BertConfig`):\n            The configuration object that\n            contains various parameters for the model.\n        clamp_min_for_underflow (bool, optional):\n            Whether to clamp the minimum value of the hidden states\n             to prevent underflow. Defaults to `False`.\n        clamp_max_for_overflow (bool, optional):\n            Whether to clamp the maximum value of the hidden states\n            to prevent overflow. Defaults to `False`.\n    \"\"\"\n\n    def __init__(self,\n                 config: BertConfig,\n                 clamp_min_for_underflow: bool = False,\n                 clamp_max_for_overflow: bool = False):\n        super().__init__(config)\n        self.self = BertSelfAttention(config, clamp_min_for_underflow,\n                                      clamp_max_for_overflow)\n\n\nclass BertIntermediate(HFBertIntermediate):\n    \"\"\"Modified from transformers.models.bert.modeling_bert.BertIntermediate.\n\n    Compared to the BertIntermediate of Huggingface, only add the clamp.\n    \"\"\"\n\n    def forward(self, hidden_states: Tensor) -> Tensor:\n        hidden_states = self.dense(hidden_states)\n        hidden_states = clamp_values(hidden_states)\n        hidden_states = self.intermediate_act_fn(hidden_states)\n        hidden_states = clamp_values(hidden_states)\n        return hidden_states\n\n\nclass BertOutput(HFBertOutput):\n    \"\"\"Modified from transformers.models.bert.modeling_bert.BertOutput.\n\n    Compared to the BertOutput of Huggingface, only add the clamp.\n    \"\"\"\n\n    def forward(self, hidden_states: Tensor, input_tensor: Tensor) -> Tensor:\n        hidden_states = self.dense(hidden_states)\n        hidden_states = self.dropout(hidden_states)\n        hidden_states = clamp_values(hidden_states)\n        hidden_states = self.LayerNorm(hidden_states + input_tensor)\n        hidden_states = clamp_values(hidden_states)\n        return hidden_states\n"
  },
  {
    "path": "mmdet/models/utils/wbf.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nimport warnings\nfrom typing import Tuple\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\n\n# References: https://github.com/ZFTurbo/Weighted-Boxes-Fusion\ndef weighted_boxes_fusion(\n        bboxes_list: list,\n        scores_list: list,\n        labels_list: list,\n        weights: list = None,\n        iou_thr: float = 0.55,\n        skip_box_thr: float = 0.0,\n        conf_type: str = 'avg',\n        allows_overflow: bool = False) -> Tuple[Tensor, Tensor, Tensor]:\n    \"\"\"weighted boxes fusion <https://arxiv.org/abs/1910.13302> is a method for\n    fusing predictions from different object detection models, which utilizes\n    confidence scores of all proposed bounding boxes to construct averaged\n    boxes.\n\n    Args:\n        bboxes_list(list): list of boxes predictions from each model,\n                                    each box is 4 numbers.\n        scores_list(list): list of scores for each model\n        labels_list(list): list of labels for each model\n        weights: list of weights for each model.\n                Default: None, which means weight == 1 for each model\n        iou_thr: IoU value for boxes to be a match\n        skip_box_thr: exclude boxes with score lower than this variable.\n        conf_type: how to calculate confidence in weighted boxes.\n            'avg': average value,\n            'max': maximum value,\n            'box_and_model_avg': box and model wise hybrid weighted average,\n            'absent_model_aware_avg': weighted average that takes into\n                            account the absent model.\n        allows_overflow: false if we want confidence score not exceed 1.0.\n\n    Returns:\n        bboxes(Tensor): boxes coordinates (Order of boxes: x1, y1, x2, y2).\n        scores(Tensor): confidence scores\n        labels(Tensor): boxes labels\n    \"\"\"\n\n    if weights is None:\n        weights = np.ones(len(bboxes_list))\n    if len(weights) != len(bboxes_list):\n        print('Warning: incorrect number of weights {}. Must be: '\n              '{}. Set weights equal to 1.'.format(\n                  len(weights), len(bboxes_list)))\n        weights = np.ones(len(bboxes_list))\n    weights = np.array(weights)\n\n    if conf_type not in [\n            'avg', 'max', 'box_and_model_avg', 'absent_model_aware_avg'\n    ]:\n        print('Unknown conf_type: {}. Must be \"avg\", '\n              '\"max\" or \"box_and_model_avg\", '\n              'or \"absent_model_aware_avg\"'.format(conf_type))\n        exit()\n\n    filtered_boxes = prefilter_boxes(bboxes_list, scores_list, labels_list,\n                                     weights, skip_box_thr)\n    if len(filtered_boxes) == 0:\n        return torch.Tensor(), torch.Tensor(), torch.Tensor()\n\n    overall_boxes = []\n\n    for label in filtered_boxes:\n        boxes = filtered_boxes[label]\n        new_boxes = []\n        weighted_boxes = np.empty((0, 8))\n\n        # Clusterize boxes\n        for j in range(0, len(boxes)):\n            index, best_iou = find_matching_box_fast(weighted_boxes, boxes[j],\n                                                     iou_thr)\n\n            if index != -1:\n                new_boxes[index].append(boxes[j])\n                weighted_boxes[index] = get_weighted_box(\n                    new_boxes[index], conf_type)\n            else:\n                new_boxes.append([boxes[j].copy()])\n                weighted_boxes = np.vstack((weighted_boxes, boxes[j].copy()))\n\n        # Rescale confidence based on number of models and boxes\n        for i in range(len(new_boxes)):\n            clustered_boxes = new_boxes[i]\n            if conf_type == 'box_and_model_avg':\n                clustered_boxes = np.array(clustered_boxes)\n                # weighted average for boxes\n                weighted_boxes[i, 1] = weighted_boxes[i, 1] * len(\n                    clustered_boxes) / weighted_boxes[i, 2]\n                # identify unique model index by model index column\n                _, idx = np.unique(clustered_boxes[:, 3], return_index=True)\n                # rescale by unique model weights\n                weighted_boxes[i, 1] = weighted_boxes[i, 1] * clustered_boxes[\n                    idx, 2].sum() / weights.sum()\n            elif conf_type == 'absent_model_aware_avg':\n                clustered_boxes = np.array(clustered_boxes)\n                # get unique model index in the cluster\n                models = np.unique(clustered_boxes[:, 3]).astype(int)\n                # create a mask to get unused model weights\n                mask = np.ones(len(weights), dtype=bool)\n                mask[models] = False\n                # absent model aware weighted average\n                weighted_boxes[\n                    i, 1] = weighted_boxes[i, 1] * len(clustered_boxes) / (\n                        weighted_boxes[i, 2] + weights[mask].sum())\n            elif conf_type == 'max':\n                weighted_boxes[i, 1] = weighted_boxes[i, 1] / weights.max()\n            elif not allows_overflow:\n                weighted_boxes[i, 1] = weighted_boxes[i, 1] * min(\n                    len(weights), len(clustered_boxes)) / weights.sum()\n            else:\n                weighted_boxes[i, 1] = weighted_boxes[i, 1] * len(\n                    clustered_boxes) / weights.sum()\n        overall_boxes.append(weighted_boxes)\n    overall_boxes = np.concatenate(overall_boxes, axis=0)\n    overall_boxes = overall_boxes[overall_boxes[:, 1].argsort()[::-1]]\n\n    bboxes = torch.Tensor(overall_boxes[:, 4:])\n    scores = torch.Tensor(overall_boxes[:, 1])\n    labels = torch.Tensor(overall_boxes[:, 0]).int()\n\n    return bboxes, scores, labels\n\n\ndef prefilter_boxes(boxes, scores, labels, weights, thr):\n\n    new_boxes = dict()\n\n    for t in range(len(boxes)):\n\n        if len(boxes[t]) != len(scores[t]):\n            print('Error. Length of boxes arrays not equal to '\n                  'length of scores array: {} != {}'.format(\n                      len(boxes[t]), len(scores[t])))\n            exit()\n\n        if len(boxes[t]) != len(labels[t]):\n            print('Error. Length of boxes arrays not equal to '\n                  'length of labels array: {} != {}'.format(\n                      len(boxes[t]), len(labels[t])))\n            exit()\n\n        for j in range(len(boxes[t])):\n            score = scores[t][j]\n            if score < thr:\n                continue\n            label = int(labels[t][j])\n            box_part = boxes[t][j]\n            x1 = float(box_part[0])\n            y1 = float(box_part[1])\n            x2 = float(box_part[2])\n            y2 = float(box_part[3])\n\n            # Box data checks\n            if x2 < x1:\n                warnings.warn('X2 < X1 value in box. Swap them.')\n                x1, x2 = x2, x1\n            if y2 < y1:\n                warnings.warn('Y2 < Y1 value in box. Swap them.')\n                y1, y2 = y2, y1\n            if (x2 - x1) * (y2 - y1) == 0.0:\n                warnings.warn('Zero area box skipped: {}.'.format(box_part))\n                continue\n\n            # [label, score, weight, model index, x1, y1, x2, y2]\n            b = [\n                int(label),\n                float(score) * weights[t], weights[t], t, x1, y1, x2, y2\n            ]\n\n            if label not in new_boxes:\n                new_boxes[label] = []\n            new_boxes[label].append(b)\n\n    # Sort each list in dict by score and transform it to numpy array\n    for k in new_boxes:\n        current_boxes = np.array(new_boxes[k])\n        new_boxes[k] = current_boxes[current_boxes[:, 1].argsort()[::-1]]\n\n    return new_boxes\n\n\ndef get_weighted_box(boxes, conf_type='avg'):\n\n    box = np.zeros(8, dtype=np.float32)\n    conf = 0\n    conf_list = []\n    w = 0\n    for b in boxes:\n        box[4:] += (b[1] * b[4:])\n        conf += b[1]\n        conf_list.append(b[1])\n        w += b[2]\n    box[0] = boxes[0][0]\n    if conf_type in ('avg', 'box_and_model_avg', 'absent_model_aware_avg'):\n        box[1] = conf / len(boxes)\n    elif conf_type == 'max':\n        box[1] = np.array(conf_list).max()\n    box[2] = w\n    box[3] = -1\n    box[4:] /= conf\n\n    return box\n\n\ndef find_matching_box_fast(boxes_list, new_box, match_iou):\n\n    def bb_iou_array(boxes, new_box):\n        # bb intersection over union\n        xA = np.maximum(boxes[:, 0], new_box[0])\n        yA = np.maximum(boxes[:, 1], new_box[1])\n        xB = np.minimum(boxes[:, 2], new_box[2])\n        yB = np.minimum(boxes[:, 3], new_box[3])\n\n        interArea = np.maximum(xB - xA, 0) * np.maximum(yB - yA, 0)\n\n        # compute the area of both the prediction and ground-truth rectangles\n        boxAArea = (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])\n        boxBArea = (new_box[2] - new_box[0]) * (new_box[3] - new_box[1])\n\n        iou = interArea / (boxAArea + boxBArea - interArea)\n\n        return iou\n\n    if boxes_list.shape[0] == 0:\n        return -1, match_iou\n\n    boxes = boxes_list\n\n    ious = bb_iou_array(boxes[:, 4:], new_box[4:])\n\n    ious[boxes[:, 0] != new_box[0]] = -1\n\n    best_idx = np.argmax(ious)\n    best_iou = ious[best_idx]\n\n    if best_iou <= match_iou:\n        best_iou = match_iou\n        best_idx = -1\n\n    return best_idx, best_iou\n"
  },
  {
    "path": "mmdet/models/vis/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .mask2former_vis import Mask2FormerVideo\nfrom .masktrack_rcnn import MaskTrackRCNN\n\n__all__ = ['Mask2FormerVideo', 'MaskTrackRCNN']\n"
  },
  {
    "path": "mmdet/models/vis/mask2former_vis.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Union\n\nfrom torch import Tensor\n\nfrom mmdet.models.mot import BaseMOTModel\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackDataSample, TrackSampleList\nfrom mmdet.utils import OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass Mask2FormerVideo(BaseMOTModel):\n    r\"\"\"Implementation of `Masked-attention Mask\n    Transformer for Universal Image Segmentation\n    <https://arxiv.org/pdf/2112.01527>`_.\n\n    Args:\n        backbone (dict): Configuration of backbone. Defaults to None.\n        track_head (dict): Configuration of track head. Defaults to None.\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`TrackDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n            Defaults to None.\n        init_cfg (dict or list[dict]): Configuration of initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 backbone: Optional[dict] = None,\n                 track_head: Optional[dict] = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super(BaseMOTModel, self).__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n\n        if backbone is not None:\n            self.backbone = MODELS.build(backbone)\n\n        if track_head is not None:\n            self.track_head = MODELS.build(track_head)\n\n        self.num_classes = self.track_head.num_classes\n\n    def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict,\n                              missing_keys, unexpected_keys, error_msgs):\n        \"\"\"Overload in order to load mmdet pretrained ckpt.\"\"\"\n        for key in list(state_dict):\n            if key.startswith('panoptic_head'):\n                state_dict[key.replace('panoptic',\n                                       'track')] = state_dict.pop(key)\n\n        super()._load_from_state_dict(state_dict, prefix, local_metadata,\n                                      strict, missing_keys, unexpected_keys,\n                                      error_msgs)\n\n    def loss(self, inputs: Tensor, data_samples: TrackSampleList,\n             **kwargs) -> Union[dict, tuple]:\n        \"\"\"\n        Args:\n            inputs (Tensor): Input images of shape (N, T, C, H, W).\n                These should usually be mean centered and std scaled.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance`.\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        # shape (N * T, C, H, W)\n        img = inputs.flatten(0, 1)\n\n        x = self.backbone(img)\n        losses = self.track_head.loss(x, data_samples)\n\n        return losses\n\n    def predict(self,\n                inputs: Tensor,\n                data_samples: TrackSampleList,\n                rescale: bool = True) -> TrackSampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with\n        postprocessing.\n\n        Args:\n            inputs (Tensor): of shape (N, T, C, H, W) encoding\n                input images. The N denotes batch size.\n                The T denotes the number of frames in a video.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `video_data_samples`.\n            rescale (bool, Optional): If False, then returned bboxes and masks\n                will fit the scale of img, otherwise, returned bboxes and masks\n                will fit the scale of original image shape. Defaults to True.\n\n        Returns:\n            TrackSampleList: Tracking results of the inputs.\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n\n        assert len(data_samples) == 1, \\\n            'Mask2former only support 1 batch size per gpu for now.'\n\n        # [T, C, H, W]\n        img = inputs[0]\n        track_data_sample = data_samples[0]\n        feats = self.backbone(img)\n        pred_track_ins_list = self.track_head.predict(feats, track_data_sample,\n                                                      rescale)\n\n        det_data_samples_list = []\n        for idx, pred_track_ins in enumerate(pred_track_ins_list):\n            img_data_sample = track_data_sample[idx]\n            img_data_sample.pred_track_instances = pred_track_ins\n            det_data_samples_list.append(img_data_sample)\n\n        results = TrackDataSample()\n        results.video_data_samples = det_data_samples_list\n        return [results]\n"
  },
  {
    "path": "mmdet/models/vis/masktrack_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.models.mot import BaseMOTModel\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import TrackSampleList\nfrom mmdet.utils import OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass MaskTrackRCNN(BaseMOTModel):\n    \"\"\"Video Instance Segmentation.\n\n    This video instance segmentor is the implementation of`MaskTrack R-CNN\n    <https://arxiv.org/abs/1905.04804>`_.\n\n    Args:\n        detector (dict): Configuration of detector. Defaults to None.\n        track_head (dict): Configuration of track head. Defaults to None.\n        tracker (dict): Configuration of tracker. Defaults to None.\n        data_preprocessor (dict or ConfigDict, optional): The pre-process\n           config of :class:`TrackDataPreprocessor`.  it usually includes,\n            ``pad_size_divisor``, ``pad_value``, ``mean`` and ``std``.\n        init_cfg (dict or list[dict]): Configuration of initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 detector: Optional[dict] = None,\n                 track_head: Optional[dict] = None,\n                 tracker: Optional[dict] = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super().__init__(data_preprocessor, init_cfg)\n\n        if detector is not None:\n            self.detector = MODELS.build(detector)\n        assert hasattr(self.detector, 'roi_head'), \\\n            'MaskTrack R-CNN only supports two stage detectors.'\n\n        if track_head is not None:\n            self.track_head = MODELS.build(track_head)\n        if tracker is not None:\n            self.tracker = MODELS.build(tracker)\n\n    def loss(self, inputs: Tensor, data_samples: TrackSampleList,\n             **kwargs) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            inputs (Dict[str, Tensor]): of shape (N, T, C, H, W) encoding\n                input images. Typically these should be mean centered and std\n                scaled. The N denotes batch size. The T denotes the number of\n                frames.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n        assert inputs.size(1) == 2, \\\n            'MaskTrackRCNN can only have 1 key frame and 1 reference frame.'\n\n        # split the data_samples into two aspects: key frames and reference\n        # frames\n        ref_data_samples, key_data_samples = [], []\n        key_frame_inds, ref_frame_inds = [], []\n\n        # set cat_id of gt_labels to 0 in RPN\n        for track_data_sample in data_samples:\n            key_data_sample = track_data_sample.get_key_frames()[0]\n            key_data_samples.append(key_data_sample)\n            ref_data_sample = track_data_sample.get_ref_frames()[0]\n            ref_data_samples.append(ref_data_sample)\n            key_frame_inds.append(track_data_sample.key_frames_inds[0])\n            ref_frame_inds.append(track_data_sample.ref_frames_inds[0])\n\n        key_frame_inds = torch.tensor(key_frame_inds, dtype=torch.int64)\n        ref_frame_inds = torch.tensor(ref_frame_inds, dtype=torch.int64)\n        batch_inds = torch.arange(len(inputs))\n        key_imgs = inputs[batch_inds, key_frame_inds].contiguous()\n        ref_imgs = inputs[batch_inds, ref_frame_inds].contiguous()\n\n        x = self.detector.extract_feat(key_imgs)\n        ref_x = self.detector.extract_feat(ref_imgs)\n\n        losses = dict()\n\n        # RPN forward and loss\n        if self.detector.with_rpn:\n            proposal_cfg = self.detector.train_cfg.get(\n                'rpn_proposal', self.detector.test_cfg.rpn)\n\n            rpn_losses, rpn_results_list = self.detector.rpn_head. \\\n                loss_and_predict(x,\n                                 key_data_samples,\n                                 proposal_cfg=proposal_cfg,\n                                 **kwargs)\n\n            # avoid get same name with roi_head loss\n            keys = rpn_losses.keys()\n            for key in keys:\n                if 'loss' in key and 'rpn' not in key:\n                    rpn_losses[f'rpn_{key}'] = rpn_losses.pop(key)\n            losses.update(rpn_losses)\n        else:\n            # TODO: Not support currently, should have a check at Fast R-CNN\n            assert key_data_samples[0].get('proposals', None) is not None\n            # use pre-defined proposals in InstanceData for the second stage\n            # to extract ROI features.\n            rpn_results_list = [\n                key_data_sample.proposals\n                for key_data_sample in key_data_samples\n            ]\n\n        losses_detect = self.detector.roi_head.loss(x, rpn_results_list,\n                                                    key_data_samples, **kwargs)\n        losses.update(losses_detect)\n\n        losses_track = self.track_head.loss(x, ref_x, rpn_results_list,\n                                            data_samples, **kwargs)\n        losses.update(losses_track)\n\n        return losses\n\n    def predict(self,\n                inputs: Tensor,\n                data_samples: TrackSampleList,\n                rescale: bool = True,\n                **kwargs) -> TrackSampleList:\n        \"\"\"Test without augmentation.\n\n        Args:\n            inputs (Tensor): of shape (N, T, C, H, W) encoding\n                input images. The N denotes batch size.\n                The T denotes the number of frames in a video.\n            data_samples (list[:obj:`TrackDataSample`]): The batch\n                data samples. It usually includes information such\n                as `video_data_samples`.\n            rescale (bool, Optional): If False, then returned bboxes and masks\n                will fit the scale of img, otherwise, returned bboxes and masks\n                will fit the scale of original image shape. Defaults to True.\n\n        Returns:\n            TrackSampleList: Tracking results of the inputs.\n        \"\"\"\n        assert inputs.dim() == 5, 'The img must be 5D Tensor (N, T, C, H, W).'\n\n        assert len(data_samples) == 1, \\\n            'MaskTrackRCNN only support 1 batch size per gpu for now.'\n\n        track_data_sample = data_samples[0]\n        video_len = len(track_data_sample)\n        if track_data_sample[0].frame_id == 0:\n            self.tracker.reset()\n\n        for frame_id in range(video_len):\n            img_data_sample = track_data_sample[frame_id]\n            single_img = inputs[:, frame_id].contiguous()\n            x = self.detector.extract_feat(single_img)\n\n            rpn_results_list = self.detector.rpn_head.predict(\n                x, [img_data_sample])\n            # det_results List[InstanceData]\n            det_results = self.detector.roi_head.predict(\n                x, rpn_results_list, [img_data_sample], rescale=rescale)\n            assert len(det_results) == 1, 'Batch inference is not supported.'\n            assert 'masks' in det_results[0], 'There are no mask results.'\n\n            img_data_sample.pred_instances = det_results[0]\n            frame_pred_track_instances = self.tracker.track(\n                model=self, feats=x, data_sample=img_data_sample, **kwargs)\n            img_data_sample.pred_track_instances = frame_pred_track_instances\n\n        return [track_data_sample]\n"
  },
  {
    "path": "mmdet/registry.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"MMDetection provides 17 registry nodes to support using modules across\nprojects. Each node is a child of the root registry in MMEngine.\n\nMore details can be found at\nhttps://mmengine.readthedocs.io/en/latest/tutorials/registry.html.\n\"\"\"\n\nfrom mmengine.registry import DATA_SAMPLERS as MMENGINE_DATA_SAMPLERS\nfrom mmengine.registry import DATASETS as MMENGINE_DATASETS\nfrom mmengine.registry import EVALUATOR as MMENGINE_EVALUATOR\nfrom mmengine.registry import HOOKS as MMENGINE_HOOKS\nfrom mmengine.registry import LOG_PROCESSORS as MMENGINE_LOG_PROCESSORS\nfrom mmengine.registry import LOOPS as MMENGINE_LOOPS\nfrom mmengine.registry import METRICS as MMENGINE_METRICS\nfrom mmengine.registry import MODEL_WRAPPERS as MMENGINE_MODEL_WRAPPERS\nfrom mmengine.registry import MODELS as MMENGINE_MODELS\nfrom mmengine.registry import \\\n    OPTIM_WRAPPER_CONSTRUCTORS as MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS\nfrom mmengine.registry import OPTIM_WRAPPERS as MMENGINE_OPTIM_WRAPPERS\nfrom mmengine.registry import OPTIMIZERS as MMENGINE_OPTIMIZERS\nfrom mmengine.registry import PARAM_SCHEDULERS as MMENGINE_PARAM_SCHEDULERS\nfrom mmengine.registry import \\\n    RUNNER_CONSTRUCTORS as MMENGINE_RUNNER_CONSTRUCTORS\nfrom mmengine.registry import RUNNERS as MMENGINE_RUNNERS\nfrom mmengine.registry import TASK_UTILS as MMENGINE_TASK_UTILS\nfrom mmengine.registry import TRANSFORMS as MMENGINE_TRANSFORMS\nfrom mmengine.registry import VISBACKENDS as MMENGINE_VISBACKENDS\nfrom mmengine.registry import VISUALIZERS as MMENGINE_VISUALIZERS\nfrom mmengine.registry import \\\n    WEIGHT_INITIALIZERS as MMENGINE_WEIGHT_INITIALIZERS\nfrom mmengine.registry import Registry\n\n# manage all kinds of runners like `EpochBasedRunner` and `IterBasedRunner`\nRUNNERS = Registry(\n    'runner', parent=MMENGINE_RUNNERS, locations=['mmdet.engine.runner'])\n# manage runner constructors that define how to initialize runners\nRUNNER_CONSTRUCTORS = Registry(\n    'runner constructor',\n    parent=MMENGINE_RUNNER_CONSTRUCTORS,\n    locations=['mmdet.engine.runner'])\n# manage all kinds of loops like `EpochBasedTrainLoop`\nLOOPS = Registry(\n    'loop', parent=MMENGINE_LOOPS, locations=['mmdet.engine.runner'])\n# manage all kinds of hooks like `CheckpointHook`\nHOOKS = Registry(\n    'hook', parent=MMENGINE_HOOKS, locations=['mmdet.engine.hooks'])\n\n# manage data-related modules\nDATASETS = Registry(\n    'dataset', parent=MMENGINE_DATASETS, locations=['mmdet.datasets'])\nDATA_SAMPLERS = Registry(\n    'data sampler',\n    parent=MMENGINE_DATA_SAMPLERS,\n    locations=['mmdet.datasets.samplers'])\nTRANSFORMS = Registry(\n    'transform',\n    parent=MMENGINE_TRANSFORMS,\n    locations=['mmdet.datasets.transforms'])\n\n# manage all kinds of modules inheriting `nn.Module`\nMODELS = Registry('model', parent=MMENGINE_MODELS, locations=['mmdet.models'])\n# manage all kinds of model wrappers like 'MMDistributedDataParallel'\nMODEL_WRAPPERS = Registry(\n    'model_wrapper',\n    parent=MMENGINE_MODEL_WRAPPERS,\n    locations=['mmdet.models'])\n# manage all kinds of weight initialization modules like `Uniform`\nWEIGHT_INITIALIZERS = Registry(\n    'weight initializer',\n    parent=MMENGINE_WEIGHT_INITIALIZERS,\n    locations=['mmdet.models'])\n\n# manage all kinds of optimizers like `SGD` and `Adam`\nOPTIMIZERS = Registry(\n    'optimizer',\n    parent=MMENGINE_OPTIMIZERS,\n    locations=['mmdet.engine.optimizers'])\n# manage optimizer wrapper\nOPTIM_WRAPPERS = Registry(\n    'optim_wrapper',\n    parent=MMENGINE_OPTIM_WRAPPERS,\n    locations=['mmdet.engine.optimizers'])\n# manage constructors that customize the optimization hyperparameters.\nOPTIM_WRAPPER_CONSTRUCTORS = Registry(\n    'optimizer constructor',\n    parent=MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS,\n    locations=['mmdet.engine.optimizers'])\n# manage all kinds of parameter schedulers like `MultiStepLR`\nPARAM_SCHEDULERS = Registry(\n    'parameter scheduler',\n    parent=MMENGINE_PARAM_SCHEDULERS,\n    locations=['mmdet.engine.schedulers'])\n# manage all kinds of metrics\nMETRICS = Registry(\n    'metric', parent=MMENGINE_METRICS, locations=['mmdet.evaluation'])\n# manage evaluator\nEVALUATOR = Registry(\n    'evaluator', parent=MMENGINE_EVALUATOR, locations=['mmdet.evaluation'])\n\n# manage task-specific modules like anchor generators and box coders\nTASK_UTILS = Registry(\n    'task util', parent=MMENGINE_TASK_UTILS, locations=['mmdet.models'])\n\n# manage visualizer\nVISUALIZERS = Registry(\n    'visualizer',\n    parent=MMENGINE_VISUALIZERS,\n    locations=['mmdet.visualization'])\n# manage visualizer backend\nVISBACKENDS = Registry(\n    'vis_backend',\n    parent=MMENGINE_VISBACKENDS,\n    locations=['mmdet.visualization'])\n\n# manage logprocessor\nLOG_PROCESSORS = Registry(\n    'log_processor',\n    parent=MMENGINE_LOG_PROCESSORS,\n    # TODO: update the location when mmdet has its own log processor\n    locations=['mmdet.engine'])\n"
  },
  {
    "path": "mmdet/structures/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .det_data_sample import DetDataSample, OptSampleList, SampleList\nfrom .reid_data_sample import ReIDDataSample\nfrom .track_data_sample import (OptTrackSampleList, TrackDataSample,\n                                TrackSampleList)\n\n__all__ = [\n    'DetDataSample', 'SampleList', 'OptSampleList', 'TrackDataSample',\n    'TrackSampleList', 'OptTrackSampleList', 'ReIDDataSample'\n]\n"
  },
  {
    "path": "mmdet/structures/bbox/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .base_boxes import BaseBoxes\nfrom .bbox_overlaps import bbox_overlaps\nfrom .box_type import (autocast_box_type, convert_box_type, get_box_type,\n                       register_box, register_box_converter)\nfrom .horizontal_boxes import HorizontalBoxes\nfrom .transforms import bbox_cxcyah_to_xyxy  # noqa: E501\nfrom .transforms import (bbox2corner, bbox2distance, bbox2result, bbox2roi,\n                         bbox_cxcywh_to_xyxy, bbox_flip, bbox_mapping,\n                         bbox_mapping_back, bbox_project, bbox_rescale,\n                         bbox_xyxy_to_cxcyah, bbox_xyxy_to_cxcywh, cat_boxes,\n                         corner2bbox, distance2bbox, empty_box_as,\n                         find_inside_bboxes, get_box_tensor, get_box_wh,\n                         roi2bbox, scale_boxes, stack_boxes)\n\n__all__ = [\n    'bbox_overlaps', 'bbox_flip', 'bbox_mapping', 'bbox_mapping_back',\n    'bbox2roi', 'roi2bbox', 'bbox2result', 'distance2bbox', 'bbox2distance',\n    'bbox_rescale', 'bbox_cxcywh_to_xyxy', 'bbox_xyxy_to_cxcywh',\n    'find_inside_bboxes', 'bbox2corner', 'corner2bbox', 'bbox_project',\n    'BaseBoxes', 'convert_box_type', 'get_box_type', 'register_box',\n    'register_box_converter', 'HorizontalBoxes', 'autocast_box_type',\n    'cat_boxes', 'stack_boxes', 'scale_boxes', 'get_box_wh', 'get_box_tensor',\n    'empty_box_as', 'bbox_xyxy_to_cxcyah', 'bbox_cxcyah_to_xyxy'\n]\n"
  },
  {
    "path": "mmdet/structures/bbox/base_boxes.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom abc import ABCMeta, abstractmethod, abstractproperty, abstractstaticmethod\nfrom typing import List, Optional, Sequence, Tuple, Type, TypeVar, Union\n\nimport numpy as np\nimport torch\nfrom torch import BoolTensor, Tensor\n\nfrom mmdet.structures.mask.structures import BitmapMasks, PolygonMasks\n\nT = TypeVar('T')\nDeviceType = Union[str, torch.device]\nIndexType = Union[slice, int, list, torch.LongTensor, torch.cuda.LongTensor,\n                  torch.BoolTensor, torch.cuda.BoolTensor, np.ndarray]\nMaskType = Union[BitmapMasks, PolygonMasks]\n\n\nclass BaseBoxes(metaclass=ABCMeta):\n    \"\"\"The base class for 2D box types.\n\n    The functions of ``BaseBoxes`` lie in three fields:\n\n    - Verify the boxes shape.\n    - Support tensor-like operations.\n    - Define abstract functions for 2D boxes.\n\n    In ``__init__`` , ``BaseBoxes`` verifies the validity of the data shape\n    w.r.t ``box_dim``. The tensor with the dimension >= 2 and the length\n    of the last dimension being ``box_dim`` will be regarded as valid.\n    ``BaseBoxes`` will restore them at the field ``tensor``. It's necessary\n    to override ``box_dim`` in subclass to guarantee the data shape is\n    correct.\n\n    There are many basic tensor-like functions implemented in ``BaseBoxes``.\n    In most cases, users can operate ``BaseBoxes`` instance like a normal\n    tensor. To protect the validity of data shape, All tensor-like functions\n    cannot modify the last dimension of ``self.tensor``.\n\n    When creating a new box type, users need to inherit from ``BaseBoxes``\n    and override abstract methods and specify the ``box_dim``. Then, register\n    the new box type by using the decorator ``register_box_type``.\n\n    Args:\n        data (Tensor or np.ndarray or Sequence): The box data with shape\n            (..., box_dim).\n        dtype (torch.dtype, Optional): data type of boxes. Defaults to None.\n        device (str or torch.device, Optional): device of boxes.\n            Default to None.\n        clone (bool): Whether clone ``boxes`` or not. Defaults to True.\n    \"\"\"\n\n    # Used to verify the last dimension length\n    # Should override it in subclass.\n    box_dim: int = 0\n\n    def __init__(self,\n                 data: Union[Tensor, np.ndarray, Sequence],\n                 dtype: Optional[torch.dtype] = None,\n                 device: Optional[DeviceType] = None,\n                 clone: bool = True) -> None:\n        if isinstance(data, (np.ndarray, Tensor, Sequence)):\n            data = torch.as_tensor(data)\n        else:\n            raise TypeError('boxes should be Tensor, ndarray, or Sequence, ',\n                            f'but got {type(data)}')\n\n        if device is not None or dtype is not None:\n            data = data.to(dtype=dtype, device=device)\n        # Clone the data to avoid potential bugs\n        if clone:\n            data = data.clone()\n        # handle the empty input like []\n        if data.numel() == 0:\n            data = data.reshape((-1, self.box_dim))\n\n        assert data.dim() >= 2 and data.size(-1) == self.box_dim, \\\n            ('The boxes dimension must >= 2 and the length of the last '\n             f'dimension must be {self.box_dim}, but got boxes with '\n             f'shape {data.shape}.')\n        self.tensor = data\n\n    def convert_to(self, dst_type: Union[str, type]) -> 'BaseBoxes':\n        \"\"\"Convert self to another box type.\n\n        Args:\n            dst_type (str or type): destination box type.\n\n        Returns:\n            :obj:`BaseBoxes`: destination box type object .\n        \"\"\"\n        from .box_type import convert_box_type\n        return convert_box_type(self, dst_type=dst_type)\n\n    def empty_boxes(self: T,\n                    dtype: Optional[torch.dtype] = None,\n                    device: Optional[DeviceType] = None) -> T:\n        \"\"\"Create empty box.\n\n        Args:\n            dtype (torch.dtype, Optional): data type of boxes.\n            device (str or torch.device, Optional): device of boxes.\n\n        Returns:\n            T: empty boxes with shape of (0, box_dim).\n        \"\"\"\n        empty_box = self.tensor.new_zeros(\n            0, self.box_dim, dtype=dtype, device=device)\n        return type(self)(empty_box, clone=False)\n\n    def fake_boxes(self: T,\n                   sizes: Tuple[int],\n                   fill: float = 0,\n                   dtype: Optional[torch.dtype] = None,\n                   device: Optional[DeviceType] = None) -> T:\n        \"\"\"Create fake boxes with specific sizes and fill values.\n\n        Args:\n            sizes (Tuple[int]): The size of fake boxes. The last value must\n                be equal with ``self.box_dim``.\n            fill (float): filling value. Defaults to 0.\n            dtype (torch.dtype, Optional): data type of boxes.\n            device (str or torch.device, Optional): device of boxes.\n\n        Returns:\n            T: Fake boxes with shape of ``sizes``.\n        \"\"\"\n        fake_boxes = self.tensor.new_full(\n            sizes, fill, dtype=dtype, device=device)\n        return type(self)(fake_boxes, clone=False)\n\n    def __getitem__(self: T, index: IndexType) -> T:\n        \"\"\"Rewrite getitem to protect the last dimension shape.\"\"\"\n        boxes = self.tensor\n        if isinstance(index, np.ndarray):\n            index = torch.as_tensor(index, device=self.device)\n        if isinstance(index, Tensor) and index.dtype == torch.bool:\n            assert index.dim() < boxes.dim()\n        elif isinstance(index, tuple):\n            assert len(index) < boxes.dim()\n            # `Ellipsis`(...) is commonly used in index like [None, ...].\n            # When `Ellipsis` is in index, it must be the last item.\n            if Ellipsis in index:\n                assert index[-1] is Ellipsis\n\n        boxes = boxes[index]\n        if boxes.dim() == 1:\n            boxes = boxes.reshape(1, -1)\n        return type(self)(boxes, clone=False)\n\n    def __setitem__(self: T, index: IndexType, values: Union[Tensor, T]) -> T:\n        \"\"\"Rewrite setitem to protect the last dimension shape.\"\"\"\n        assert type(values) is type(self), \\\n            'The value to be set must be the same box type as self'\n        values = values.tensor\n\n        if isinstance(index, np.ndarray):\n            index = torch.as_tensor(index, device=self.device)\n        if isinstance(index, Tensor) and index.dtype == torch.bool:\n            assert index.dim() < self.tensor.dim()\n        elif isinstance(index, tuple):\n            assert len(index) < self.tensor.dim()\n            # `Ellipsis`(...) is commonly used in index like [None, ...].\n            # When `Ellipsis` is in index, it must be the last item.\n            if Ellipsis in index:\n                assert index[-1] is Ellipsis\n\n        self.tensor[index] = values\n\n    def __len__(self) -> int:\n        \"\"\"Return the length of self.tensor first dimension.\"\"\"\n        return self.tensor.size(0)\n\n    def __deepcopy__(self, memo):\n        \"\"\"Only clone the ``self.tensor`` when applying deepcopy.\"\"\"\n        cls = self.__class__\n        other = cls.__new__(cls)\n        memo[id(self)] = other\n        other.tensor = self.tensor.clone()\n        return other\n\n    def __repr__(self) -> str:\n        \"\"\"Return a strings that describes the object.\"\"\"\n        return self.__class__.__name__ + '(\\n' + str(self.tensor) + ')'\n\n    def new_tensor(self, *args, **kwargs) -> Tensor:\n        \"\"\"Reload ``new_tensor`` from self.tensor.\"\"\"\n        return self.tensor.new_tensor(*args, **kwargs)\n\n    def new_full(self, *args, **kwargs) -> Tensor:\n        \"\"\"Reload ``new_full`` from self.tensor.\"\"\"\n        return self.tensor.new_full(*args, **kwargs)\n\n    def new_empty(self, *args, **kwargs) -> Tensor:\n        \"\"\"Reload ``new_empty`` from self.tensor.\"\"\"\n        return self.tensor.new_empty(*args, **kwargs)\n\n    def new_ones(self, *args, **kwargs) -> Tensor:\n        \"\"\"Reload ``new_ones`` from self.tensor.\"\"\"\n        return self.tensor.new_ones(*args, **kwargs)\n\n    def new_zeros(self, *args, **kwargs) -> Tensor:\n        \"\"\"Reload ``new_zeros`` from self.tensor.\"\"\"\n        return self.tensor.new_zeros(*args, **kwargs)\n\n    def size(self, dim: Optional[int] = None) -> Union[int, torch.Size]:\n        \"\"\"Reload new_zeros from self.tensor.\"\"\"\n        # self.tensor.size(dim) cannot work when dim=None.\n        return self.tensor.size() if dim is None else self.tensor.size(dim)\n\n    def dim(self) -> int:\n        \"\"\"Reload ``dim`` from self.tensor.\"\"\"\n        return self.tensor.dim()\n\n    @property\n    def device(self) -> torch.device:\n        \"\"\"Reload ``device`` from self.tensor.\"\"\"\n        return self.tensor.device\n\n    @property\n    def dtype(self) -> torch.dtype:\n        \"\"\"Reload ``dtype`` from self.tensor.\"\"\"\n        return self.tensor.dtype\n\n    @property\n    def shape(self) -> torch.Size:\n        return self.tensor.shape\n\n    def numel(self) -> int:\n        \"\"\"Reload ``numel`` from self.tensor.\"\"\"\n        return self.tensor.numel()\n\n    def numpy(self) -> np.ndarray:\n        \"\"\"Reload ``numpy`` from self.tensor.\"\"\"\n        return self.tensor.numpy()\n\n    def to(self: T, *args, **kwargs) -> T:\n        \"\"\"Reload ``to`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.to(*args, **kwargs), clone=False)\n\n    def cpu(self: T) -> T:\n        \"\"\"Reload ``cpu`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.cpu(), clone=False)\n\n    def cuda(self: T, *args, **kwargs) -> T:\n        \"\"\"Reload ``cuda`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.cuda(*args, **kwargs), clone=False)\n\n    def clone(self: T) -> T:\n        \"\"\"Reload ``clone`` from self.tensor.\"\"\"\n        return type(self)(self.tensor)\n\n    def detach(self: T) -> T:\n        \"\"\"Reload ``detach`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.detach(), clone=False)\n\n    def view(self: T, *shape: Tuple[int]) -> T:\n        \"\"\"Reload ``view`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.view(shape), clone=False)\n\n    def reshape(self: T, *shape: Tuple[int]) -> T:\n        \"\"\"Reload ``reshape`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.reshape(shape), clone=False)\n\n    def expand(self: T, *sizes: Tuple[int]) -> T:\n        \"\"\"Reload ``expand`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.expand(sizes), clone=False)\n\n    def repeat(self: T, *sizes: Tuple[int]) -> T:\n        \"\"\"Reload ``repeat`` from self.tensor.\"\"\"\n        return type(self)(self.tensor.repeat(sizes), clone=False)\n\n    def transpose(self: T, dim0: int, dim1: int) -> T:\n        \"\"\"Reload ``transpose`` from self.tensor.\"\"\"\n        ndim = self.tensor.dim()\n        assert dim0 != -1 and dim0 != ndim - 1\n        assert dim1 != -1 and dim1 != ndim - 1\n        return type(self)(self.tensor.transpose(dim0, dim1), clone=False)\n\n    def permute(self: T, *dims: Tuple[int]) -> T:\n        \"\"\"Reload ``permute`` from self.tensor.\"\"\"\n        assert dims[-1] == -1 or dims[-1] == self.tensor.dim() - 1\n        return type(self)(self.tensor.permute(dims), clone=False)\n\n    def split(self: T,\n              split_size_or_sections: Union[int, Sequence[int]],\n              dim: int = 0) -> List[T]:\n        \"\"\"Reload ``split`` from self.tensor.\"\"\"\n        assert dim != -1 and dim != self.tensor.dim() - 1\n        boxes_list = self.tensor.split(split_size_or_sections, dim=dim)\n        return [type(self)(boxes, clone=False) for boxes in boxes_list]\n\n    def chunk(self: T, chunks: int, dim: int = 0) -> List[T]:\n        \"\"\"Reload ``chunk`` from self.tensor.\"\"\"\n        assert dim != -1 and dim != self.tensor.dim() - 1\n        boxes_list = self.tensor.chunk(chunks, dim=dim)\n        return [type(self)(boxes, clone=False) for boxes in boxes_list]\n\n    def unbind(self: T, dim: int = 0) -> T:\n        \"\"\"Reload ``unbind`` from self.tensor.\"\"\"\n        assert dim != -1 and dim != self.tensor.dim() - 1\n        boxes_list = self.tensor.unbind(dim=dim)\n        return [type(self)(boxes, clone=False) for boxes in boxes_list]\n\n    def flatten(self: T, start_dim: int = 0, end_dim: int = -2) -> T:\n        \"\"\"Reload ``flatten`` from self.tensor.\"\"\"\n        assert end_dim != -1 and end_dim != self.tensor.dim() - 1\n        return type(self)(self.tensor.flatten(start_dim, end_dim), clone=False)\n\n    def squeeze(self: T, dim: Optional[int] = None) -> T:\n        \"\"\"Reload ``squeeze`` from self.tensor.\"\"\"\n        boxes = self.tensor.squeeze() if dim is None else \\\n            self.tensor.squeeze(dim)\n        return type(self)(boxes, clone=False)\n\n    def unsqueeze(self: T, dim: int) -> T:\n        \"\"\"Reload ``unsqueeze`` from self.tensor.\"\"\"\n        assert dim != -1 and dim != self.tensor.dim()\n        return type(self)(self.tensor.unsqueeze(dim), clone=False)\n\n    @classmethod\n    def cat(cls: Type[T], box_list: Sequence[T], dim: int = 0) -> T:\n        \"\"\"Cancatenates a box instance list into one single box instance.\n        Similar to ``torch.cat``.\n\n        Args:\n            box_list (Sequence[T]): A sequence of box instances.\n            dim (int): The dimension over which the box are concatenated.\n                Defaults to 0.\n\n        Returns:\n            T: Concatenated box instance.\n        \"\"\"\n        assert isinstance(box_list, Sequence)\n        if len(box_list) == 0:\n            raise ValueError('box_list should not be a empty list.')\n\n        assert dim != -1 and dim != box_list[0].dim() - 1\n        assert all(isinstance(boxes, cls) for boxes in box_list)\n\n        th_box_list = [boxes.tensor for boxes in box_list]\n        return cls(torch.cat(th_box_list, dim=dim), clone=False)\n\n    @classmethod\n    def stack(cls: Type[T], box_list: Sequence[T], dim: int = 0) -> T:\n        \"\"\"Concatenates a sequence of tensors along a new dimension. Similar to\n        ``torch.stack``.\n\n        Args:\n            box_list (Sequence[T]): A sequence of box instances.\n            dim (int): Dimension to insert. Defaults to 0.\n\n        Returns:\n            T: Concatenated box instance.\n        \"\"\"\n        assert isinstance(box_list, Sequence)\n        if len(box_list) == 0:\n            raise ValueError('box_list should not be a empty list.')\n\n        assert dim != -1 and dim != box_list[0].dim()\n        assert all(isinstance(boxes, cls) for boxes in box_list)\n\n        th_box_list = [boxes.tensor for boxes in box_list]\n        return cls(torch.stack(th_box_list, dim=dim), clone=False)\n\n    @abstractproperty\n    def centers(self) -> Tensor:\n        \"\"\"Return a tensor representing the centers of boxes.\"\"\"\n        pass\n\n    @abstractproperty\n    def areas(self) -> Tensor:\n        \"\"\"Return a tensor representing the areas of boxes.\"\"\"\n        pass\n\n    @abstractproperty\n    def widths(self) -> Tensor:\n        \"\"\"Return a tensor representing the widths of boxes.\"\"\"\n        pass\n\n    @abstractproperty\n    def heights(self) -> Tensor:\n        \"\"\"Return a tensor representing the heights of boxes.\"\"\"\n        pass\n\n    @abstractmethod\n    def flip_(self,\n              img_shape: Tuple[int, int],\n              direction: str = 'horizontal') -> None:\n        \"\"\"Flip boxes horizontally or vertically in-place.\n\n        Args:\n            img_shape (Tuple[int, int]): A tuple of image height and width.\n            direction (str): Flip direction, options are \"horizontal\",\n                \"vertical\" and \"diagonal\". Defaults to \"horizontal\"\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def translate_(self, distances: Tuple[float, float]) -> None:\n        \"\"\"Translate boxes in-place.\n\n        Args:\n            distances (Tuple[float, float]): translate distances. The first\n                is horizontal distance and the second is vertical distance.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def clip_(self, img_shape: Tuple[int, int]) -> None:\n        \"\"\"Clip boxes according to the image shape in-place.\n\n        Args:\n            img_shape (Tuple[int, int]): A tuple of image height and width.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def rotate_(self, center: Tuple[float, float], angle: float) -> None:\n        \"\"\"Rotate all boxes in-place.\n\n        Args:\n            center (Tuple[float, float]): Rotation origin.\n            angle (float): Rotation angle represented in degrees. Positive\n                values mean clockwise rotation.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def project_(self, homography_matrix: Union[Tensor, np.ndarray]) -> None:\n        \"\"\"Geometric transformat boxes in-place.\n\n        Args:\n            homography_matrix (Tensor or np.ndarray]):\n                Shape (3, 3) for geometric transformation.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def rescale_(self, scale_factor: Tuple[float, float]) -> None:\n        \"\"\"Rescale boxes w.r.t. rescale_factor in-place.\n\n        Note:\n            Both ``rescale_`` and ``resize_`` will enlarge or shrink boxes\n            w.r.t ``scale_facotr``. The difference is that ``resize_`` only\n            changes the width and the height of boxes, but ``rescale_`` also\n            rescales the box centers simultaneously.\n\n        Args:\n            scale_factor (Tuple[float, float]): factors for scaling boxes.\n                The length should be 2.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def resize_(self, scale_factor: Tuple[float, float]) -> None:\n        \"\"\"Resize the box width and height w.r.t scale_factor in-place.\n\n        Note:\n            Both ``rescale_`` and ``resize_`` will enlarge or shrink boxes\n            w.r.t ``scale_facotr``. The difference is that ``resize_`` only\n            changes the width and the height of boxes, but ``rescale_`` also\n            rescales the box centers simultaneously.\n\n        Args:\n            scale_factor (Tuple[float, float]): factors for scaling box\n                shapes. The length should be 2.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def is_inside(self,\n                  img_shape: Tuple[int, int],\n                  all_inside: bool = False,\n                  allowed_border: int = 0) -> BoolTensor:\n        \"\"\"Find boxes inside the image.\n\n        Args:\n            img_shape (Tuple[int, int]): A tuple of image height and width.\n            all_inside (bool): Whether the boxes are all inside the image or\n                part inside the image. Defaults to False.\n            allowed_border (int): Boxes that extend beyond the image shape\n                boundary by more than ``allowed_border`` are considered\n                \"outside\" Defaults to 0.\n        Returns:\n            BoolTensor: A BoolTensor indicating whether the box is inside\n            the image. Assuming the original boxes have shape (m, n, box_dim),\n            the output has shape (m, n).\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def find_inside_points(self,\n                           points: Tensor,\n                           is_aligned: bool = False) -> BoolTensor:\n        \"\"\"Find inside box points. Boxes dimension must be 2.\n\n        Args:\n            points (Tensor): Points coordinates. Has shape of (m, 2).\n            is_aligned (bool): Whether ``points`` has been aligned with boxes\n                or not. If True, the length of boxes and ``points`` should be\n                the same. Defaults to False.\n\n        Returns:\n            BoolTensor: A BoolTensor indicating whether a point is inside\n            boxes. Assuming the boxes has shape of (n, box_dim), if\n            ``is_aligned`` is False. The index has shape of (m, n). If\n            ``is_aligned`` is True, m should be equal to n and the index has\n            shape of (m, ).\n        \"\"\"\n        pass\n\n    @abstractstaticmethod\n    def overlaps(boxes1: 'BaseBoxes',\n                 boxes2: 'BaseBoxes',\n                 mode: str = 'iou',\n                 is_aligned: bool = False,\n                 eps: float = 1e-6) -> Tensor:\n        \"\"\"Calculate overlap between two set of boxes with their types\n        converted to the present box type.\n\n        Args:\n            boxes1 (:obj:`BaseBoxes`): BaseBoxes with shape of (m, box_dim)\n                or empty.\n            boxes2 (:obj:`BaseBoxes`): BaseBoxes with shape of (n, box_dim)\n                or empty.\n            mode (str): \"iou\" (intersection over union), \"iof\" (intersection\n                over foreground). Defaults to \"iou\".\n            is_aligned (bool): If True, then m and n must be equal. Defaults\n                to False.\n            eps (float): A value added to the denominator for numerical\n                stability. Defaults to 1e-6.\n\n        Returns:\n            Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,)\n        \"\"\"\n        pass\n\n    @abstractstaticmethod\n    def from_instance_masks(masks: MaskType) -> 'BaseBoxes':\n        \"\"\"Create boxes from instance masks.\n\n        Args:\n            masks (:obj:`BitmapMasks` or :obj:`PolygonMasks`): BitmapMasks or\n                PolygonMasks instance with length of n.\n\n        Returns:\n            :obj:`BaseBoxes`: Converted boxes with shape of (n, box_dim).\n        \"\"\"\n        pass\n"
  },
  {
    "path": "mmdet/structures/bbox/bbox_overlaps.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\n\n\ndef fp16_clamp(x, min=None, max=None):\n    if not x.is_cuda and x.dtype == torch.float16:\n        # clamp for cpu float16, tensor fp16 has no clamp implementation\n        return x.float().clamp(min, max).half()\n\n    return x.clamp(min, max)\n\n\ndef bbox_overlaps(bboxes1, bboxes2, mode='iou', is_aligned=False, eps=1e-6):\n    \"\"\"Calculate overlap between two set of bboxes.\n\n    FP16 Contributed by https://github.com/open-mmlab/mmdetection/pull/4889\n    Note:\n        Assume bboxes1 is M x 4, bboxes2 is N x 4, when mode is 'iou',\n        there are some new generated variable when calculating IOU\n        using bbox_overlaps function:\n\n        1) is_aligned is False\n            area1: M x 1\n            area2: N x 1\n            lt: M x N x 2\n            rb: M x N x 2\n            wh: M x N x 2\n            overlap: M x N x 1\n            union: M x N x 1\n            ious: M x N x 1\n\n            Total memory:\n                S = (9 x N x M + N + M) * 4 Byte,\n\n            When using FP16, we can reduce:\n                R = (9 x N x M + N + M) * 4 / 2 Byte\n                R large than (N + M) * 4 * 2 is always true when N and M >= 1.\n                Obviously, N + M <= N * M < 3 * N * M, when N >=2 and M >=2,\n                           N + 1 < 3 * N, when N or M is 1.\n\n            Given M = 40 (ground truth), N = 400000 (three anchor boxes\n            in per grid, FPN, R-CNNs),\n                R = 275 MB (one times)\n\n            A special case (dense detection), M = 512 (ground truth),\n                R = 3516 MB = 3.43 GB\n\n            When the batch size is B, reduce:\n                B x R\n\n            Therefore, CUDA memory runs out frequently.\n\n            Experiments on GeForce RTX 2080Ti (11019 MiB):\n\n            |   dtype   |   M   |   N   |   Use    |   Real   |   Ideal   |\n            |:----:|:----:|:----:|:----:|:----:|:----:|\n            |   FP32   |   512 | 400000 | 8020 MiB |   --   |   --   |\n            |   FP16   |   512 | 400000 |   4504 MiB | 3516 MiB | 3516 MiB |\n            |   FP32   |   40 | 400000 |   1540 MiB |   --   |   --   |\n            |   FP16   |   40 | 400000 |   1264 MiB |   276MiB   | 275 MiB |\n\n        2) is_aligned is True\n            area1: N x 1\n            area2: N x 1\n            lt: N x 2\n            rb: N x 2\n            wh: N x 2\n            overlap: N x 1\n            union: N x 1\n            ious: N x 1\n\n            Total memory:\n                S = 11 x N * 4 Byte\n\n            When using FP16, we can reduce:\n                R = 11 x N * 4 / 2 Byte\n\n        So do the 'giou' (large than 'iou').\n\n        Time-wise, FP16 is generally faster than FP32.\n\n        When gpu_assign_thr is not -1, it takes more time on cpu\n        but not reduce memory.\n        There, we can reduce half the memory and keep the speed.\n\n    If ``is_aligned`` is ``False``, then calculate the overlaps between each\n    bbox of bboxes1 and bboxes2, otherwise the overlaps between each aligned\n    pair of bboxes1 and bboxes2.\n\n    Args:\n        bboxes1 (Tensor): shape (B, m, 4) in <x1, y1, x2, y2> format or empty.\n        bboxes2 (Tensor): shape (B, n, 4) in <x1, y1, x2, y2> format or empty.\n            B indicates the batch dim, in shape (B1, B2, ..., Bn).\n            If ``is_aligned`` is ``True``, then m and n must be equal.\n        mode (str): \"iou\" (intersection over union), \"iof\" (intersection over\n            foreground) or \"giou\" (generalized intersection over union).\n            Default \"iou\".\n        is_aligned (bool, optional): If True, then m and n must be equal.\n            Default False.\n        eps (float, optional): A value added to the denominator for numerical\n            stability. Default 1e-6.\n\n    Returns:\n        Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,)\n\n    Example:\n        >>> bboxes1 = torch.FloatTensor([\n        >>>     [0, 0, 10, 10],\n        >>>     [10, 10, 20, 20],\n        >>>     [32, 32, 38, 42],\n        >>> ])\n        >>> bboxes2 = torch.FloatTensor([\n        >>>     [0, 0, 10, 20],\n        >>>     [0, 10, 10, 19],\n        >>>     [10, 10, 20, 20],\n        >>> ])\n        >>> overlaps = bbox_overlaps(bboxes1, bboxes2)\n        >>> assert overlaps.shape == (3, 3)\n        >>> overlaps = bbox_overlaps(bboxes1, bboxes2, is_aligned=True)\n        >>> assert overlaps.shape == (3, )\n\n    Example:\n        >>> empty = torch.empty(0, 4)\n        >>> nonempty = torch.FloatTensor([[0, 0, 10, 9]])\n        >>> assert tuple(bbox_overlaps(empty, nonempty).shape) == (0, 1)\n        >>> assert tuple(bbox_overlaps(nonempty, empty).shape) == (1, 0)\n        >>> assert tuple(bbox_overlaps(empty, empty).shape) == (0, 0)\n    \"\"\"\n\n    assert mode in ['iou', 'iof', 'giou'], f'Unsupported mode {mode}'\n    # Either the boxes are empty or the length of boxes' last dimension is 4\n    assert (bboxes1.size(-1) == 4 or bboxes1.size(0) == 0)\n    assert (bboxes2.size(-1) == 4 or bboxes2.size(0) == 0)\n\n    # Batch dim must be the same\n    # Batch dim: (B1, B2, ... Bn)\n    assert bboxes1.shape[:-2] == bboxes2.shape[:-2]\n    batch_shape = bboxes1.shape[:-2]\n\n    rows = bboxes1.size(-2)\n    cols = bboxes2.size(-2)\n    if is_aligned:\n        assert rows == cols\n\n    if rows * cols == 0:\n        if is_aligned:\n            return bboxes1.new(batch_shape + (rows, ))\n        else:\n            return bboxes1.new(batch_shape + (rows, cols))\n\n    area1 = (bboxes1[..., 2] - bboxes1[..., 0]) * (\n        bboxes1[..., 3] - bboxes1[..., 1])\n    area2 = (bboxes2[..., 2] - bboxes2[..., 0]) * (\n        bboxes2[..., 3] - bboxes2[..., 1])\n\n    if is_aligned:\n        lt = torch.max(bboxes1[..., :2], bboxes2[..., :2])  # [B, rows, 2]\n        rb = torch.min(bboxes1[..., 2:], bboxes2[..., 2:])  # [B, rows, 2]\n\n        wh = fp16_clamp(rb - lt, min=0)\n        overlap = wh[..., 0] * wh[..., 1]\n\n        if mode in ['iou', 'giou']:\n            union = area1 + area2 - overlap\n        else:\n            union = area1\n        if mode == 'giou':\n            enclosed_lt = torch.min(bboxes1[..., :2], bboxes2[..., :2])\n            enclosed_rb = torch.max(bboxes1[..., 2:], bboxes2[..., 2:])\n    else:\n        lt = torch.max(bboxes1[..., :, None, :2],\n                       bboxes2[..., None, :, :2])  # [B, rows, cols, 2]\n        rb = torch.min(bboxes1[..., :, None, 2:],\n                       bboxes2[..., None, :, 2:])  # [B, rows, cols, 2]\n\n        wh = fp16_clamp(rb - lt, min=0)\n        overlap = wh[..., 0] * wh[..., 1]\n\n        if mode in ['iou', 'giou']:\n            union = area1[..., None] + area2[..., None, :] - overlap\n        else:\n            union = area1[..., None]\n        if mode == 'giou':\n            enclosed_lt = torch.min(bboxes1[..., :, None, :2],\n                                    bboxes2[..., None, :, :2])\n            enclosed_rb = torch.max(bboxes1[..., :, None, 2:],\n                                    bboxes2[..., None, :, 2:])\n\n    eps = union.new_tensor([eps])\n    union = torch.max(union, eps)\n    ious = overlap / union\n    if mode in ['iou', 'iof']:\n        return ious\n    # calculate gious\n    enclose_wh = fp16_clamp(enclosed_rb - enclosed_lt, min=0)\n    enclose_area = enclose_wh[..., 0] * enclose_wh[..., 1]\n    enclose_area = torch.max(enclose_area, eps)\n    gious = ious - (enclose_area - union) / enclose_area\n    return gious\n"
  },
  {
    "path": "mmdet/structures/bbox/box_type.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Callable, Optional, Tuple, Type, Union\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom .base_boxes import BaseBoxes\n\nBoxType = Union[np.ndarray, Tensor, BaseBoxes]\n\nbox_types: dict = {}\n_box_type_to_name: dict = {}\nbox_converters: dict = {}\n\n\ndef _register_box(name: str, box_type: Type, force: bool = False) -> None:\n    \"\"\"Register a box type.\n\n    Args:\n        name (str): The name of box type.\n        box_type (type): Box mode class to be registered.\n        force (bool): Whether to override an existing class with the same\n            name. Defaults to False.\n    \"\"\"\n    assert issubclass(box_type, BaseBoxes)\n    name = name.lower()\n\n    if not force and (name in box_types or box_type in _box_type_to_name):\n        raise KeyError(f'box type {name} has been registered')\n    elif name in box_types:\n        _box_type = box_types.pop(name)\n        _box_type_to_name.pop(_box_type)\n    elif box_type in _box_type_to_name:\n        _name = _box_type_to_name.pop(box_type)\n        box_types.pop(_name)\n\n    box_types[name] = box_type\n    _box_type_to_name[box_type] = name\n\n\ndef register_box(name: str,\n                 box_type: Type = None,\n                 force: bool = False) -> Union[Type, Callable]:\n    \"\"\"Register a box type.\n\n    A record will be added to ``bbox_types``, whose key is the box type name\n    and value is the box type itself. Simultaneously, a reverse dictionary\n    ``_box_type_to_name`` will be updated. It can be used as a decorator or\n    a normal function.\n\n    Args:\n        name (str): The name of box type.\n        bbox_type (type, Optional): Box type class to be registered.\n            Defaults to None.\n        force (bool): Whether to override the existing box type with the same\n            name. Defaults to False.\n\n    Examples:\n        >>> from mmdet.structures.bbox import register_box\n        >>> from mmdet.structures.bbox import BaseBoxes\n\n        >>> # as a decorator\n        >>> @register_box('hbox')\n        >>> class HorizontalBoxes(BaseBoxes):\n        >>>     pass\n\n        >>> # as a normal function\n        >>> class RotatedBoxes(BaseBoxes):\n        >>>     pass\n        >>> register_box('rbox', RotatedBoxes)\n    \"\"\"\n    if not isinstance(force, bool):\n        raise TypeError(f'force must be a boolean, but got {type(force)}')\n\n    # use it as a normal method: register_box(name, box_type=BoxCls)\n    if box_type is not None:\n        _register_box(name=name, box_type=box_type, force=force)\n        return box_type\n\n    # use it as a decorator: @register_box(name)\n    def _register(cls):\n        _register_box(name=name, box_type=cls, force=force)\n        return cls\n\n    return _register\n\n\ndef _register_box_converter(src_type: Union[str, type],\n                            dst_type: Union[str, type],\n                            converter: Callable,\n                            force: bool = False) -> None:\n    \"\"\"Register a box converter.\n\n    Args:\n        src_type (str or type): source box type name or class.\n        dst_type (str or type): destination box type name or class.\n        converter (Callable): Convert function.\n        force (bool): Whether to override the existing box type with the same\n            name. Defaults to False.\n    \"\"\"\n    assert callable(converter)\n    src_type_name, _ = get_box_type(src_type)\n    dst_type_name, _ = get_box_type(dst_type)\n\n    converter_name = src_type_name + '2' + dst_type_name\n    if not force and converter_name in box_converters:\n        raise KeyError(f'The box converter from {src_type_name} to '\n                       f'{dst_type_name} has been registered.')\n\n    box_converters[converter_name] = converter\n\n\ndef register_box_converter(src_type: Union[str, type],\n                           dst_type: Union[str, type],\n                           converter: Optional[Callable] = None,\n                           force: bool = False) -> Callable:\n    \"\"\"Register a box converter.\n\n    A record will be added to ``box_converter``, whose key is\n    '{src_type_name}2{dst_type_name}' and value is the convert function.\n    It can be used as a decorator or a normal function.\n\n    Args:\n        src_type (str or type): source box type name or class.\n        dst_type (str or type): destination box type name or class.\n        converter (Callable): Convert function. Defaults to None.\n        force (bool): Whether to override the existing box type with the same\n            name. Defaults to False.\n\n    Examples:\n        >>> from mmdet.structures.bbox import register_box_converter\n        >>> # as a decorator\n        >>> @register_box_converter('hbox', 'rbox')\n        >>> def converter_A(boxes):\n        >>>     pass\n\n        >>> # as a normal function\n        >>> def converter_B(boxes):\n        >>>     pass\n        >>> register_box_converter('rbox', 'hbox', converter_B)\n    \"\"\"\n    if not isinstance(force, bool):\n        raise TypeError(f'force must be a boolean, but got {type(force)}')\n\n    # use it as a normal method:\n    # register_box_converter(src_type, dst_type, converter=Func)\n    if converter is not None:\n        _register_box_converter(\n            src_type=src_type,\n            dst_type=dst_type,\n            converter=converter,\n            force=force)\n        return converter\n\n    # use it as a decorator: @register_box_converter(name)\n    def _register(func):\n        _register_box_converter(\n            src_type=src_type, dst_type=dst_type, converter=func, force=force)\n        return func\n\n    return _register\n\n\ndef get_box_type(box_type: Union[str, type]) -> Tuple[str, type]:\n    \"\"\"get both box type name and class.\n\n    Args:\n        box_type (str or type): Single box type name or class.\n\n    Returns:\n        Tuple[str, type]: A tuple of box type name and class.\n    \"\"\"\n    if isinstance(box_type, str):\n        type_name = box_type.lower()\n        assert type_name in box_types, \\\n            f\"Box type {type_name} hasn't been registered in box_types.\"\n        type_cls = box_types[type_name]\n    elif issubclass(box_type, BaseBoxes):\n        assert box_type in _box_type_to_name, \\\n            f\"Box type {box_type} hasn't been registered in box_types.\"\n        type_name = _box_type_to_name[box_type]\n        type_cls = box_type\n    else:\n        raise KeyError('box_type must be a str or class inheriting from '\n                       f'BaseBoxes, but got {type(box_type)}.')\n    return type_name, type_cls\n\n\ndef convert_box_type(boxes: BoxType,\n                     *,\n                     src_type: Union[str, type] = None,\n                     dst_type: Union[str, type] = None) -> BoxType:\n    \"\"\"Convert boxes from source type to destination type.\n\n    If ``boxes`` is a instance of BaseBoxes, the ``src_type`` will be set\n    as the type of ``boxes``.\n\n    Args:\n        boxes (np.ndarray or Tensor or :obj:`BaseBoxes`): boxes need to\n            convert.\n        src_type (str or type, Optional): source box type. Defaults to None.\n        dst_type (str or type, Optional): destination box type. Defaults to\n            None.\n\n    Returns:\n        Union[np.ndarray, Tensor, :obj:`BaseBoxes`]: Converted boxes. It's type\n        is consistent with the input's type.\n    \"\"\"\n    assert dst_type is not None\n    dst_type_name, dst_type_cls = get_box_type(dst_type)\n\n    is_box_cls = False\n    is_numpy = False\n    if isinstance(boxes, BaseBoxes):\n        src_type_name, _ = get_box_type(type(boxes))\n        is_box_cls = True\n    elif isinstance(boxes, (Tensor, np.ndarray)):\n        assert src_type is not None\n        src_type_name, _ = get_box_type(src_type)\n        if isinstance(boxes, np.ndarray):\n            is_numpy = True\n    else:\n        raise TypeError('boxes must be a instance of BaseBoxes, Tensor or '\n                        f'ndarray, but get {type(boxes)}.')\n\n    if src_type_name == dst_type_name:\n        return boxes\n\n    converter_name = src_type_name + '2' + dst_type_name\n    assert converter_name in box_converters, \\\n        \"Convert function hasn't been registered in box_converters.\"\n    converter = box_converters[converter_name]\n\n    if is_box_cls:\n        boxes = converter(boxes.tensor)\n        return dst_type_cls(boxes)\n    elif is_numpy:\n        boxes = converter(torch.from_numpy(boxes))\n        return boxes.numpy()\n    else:\n        return converter(boxes)\n\n\ndef autocast_box_type(dst_box_type='hbox') -> Callable:\n    \"\"\"A decorator which automatically casts results['gt_bboxes'] to the\n    destination box type.\n\n    It commenly used in mmdet.datasets.transforms to make the transforms up-\n    compatible with the np.ndarray type of results['gt_bboxes'].\n\n    The speed of processing of np.ndarray and BaseBoxes data are the same:\n\n    - np.ndarray: 0.0509 img/s\n    - BaseBoxes: 0.0551 img/s\n\n    Args:\n        dst_box_type (str): Destination box type.\n    \"\"\"\n    _, box_type_cls = get_box_type(dst_box_type)\n\n    def decorator(func: Callable) -> Callable:\n\n        def wrapper(self, results: dict, *args, **kwargs) -> dict:\n            if ('gt_bboxes' not in results\n                    or isinstance(results['gt_bboxes'], BaseBoxes)):\n                return func(self, results)\n            elif isinstance(results['gt_bboxes'], np.ndarray):\n                results['gt_bboxes'] = box_type_cls(\n                    results['gt_bboxes'], clone=False)\n                if 'mix_results' in results:\n                    for res in results['mix_results']:\n                        if isinstance(res['gt_bboxes'], np.ndarray):\n                            res['gt_bboxes'] = box_type_cls(\n                                res['gt_bboxes'], clone=False)\n\n                _results = func(self, results, *args, **kwargs)\n\n                # In some cases, the function will process gt_bboxes in-place\n                # Simultaneously convert inputting and outputting gt_bboxes\n                # back to np.ndarray\n                if isinstance(_results, dict) and 'gt_bboxes' in _results:\n                    if isinstance(_results['gt_bboxes'], BaseBoxes):\n                        _results['gt_bboxes'] = _results['gt_bboxes'].numpy()\n                if isinstance(results['gt_bboxes'], BaseBoxes):\n                    results['gt_bboxes'] = results['gt_bboxes'].numpy()\n                return _results\n            else:\n                raise TypeError(\n                    \"auto_box_type requires results['gt_bboxes'] to \"\n                    'be BaseBoxes or np.ndarray, but got '\n                    f\"{type(results['gt_bboxes'])}\")\n\n        return wrapper\n\n    return decorator\n"
  },
  {
    "path": "mmdet/structures/bbox/horizontal_boxes.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, TypeVar, Union\n\nimport cv2\nimport numpy as np\nimport torch\nfrom torch import BoolTensor, Tensor\n\nfrom mmdet.structures.mask.structures import BitmapMasks, PolygonMasks\nfrom .base_boxes import BaseBoxes\nfrom .bbox_overlaps import bbox_overlaps\nfrom .box_type import register_box\n\nT = TypeVar('T')\nDeviceType = Union[str, torch.device]\nMaskType = Union[BitmapMasks, PolygonMasks]\n\n\n@register_box(name='hbox')\nclass HorizontalBoxes(BaseBoxes):\n    \"\"\"The horizontal box class used in MMDetection by default.\n\n    The ``box_dim`` of ``HorizontalBoxes`` is 4, which means the length of\n    the last dimension of the data should be 4. Two modes of box data are\n    supported in ``HorizontalBoxes``:\n\n    - 'xyxy': Each row of data indicates (x1, y1, x2, y2), which are the\n      coordinates of the left-top and right-bottom points.\n    - 'cxcywh': Each row of data indicates (x, y, w, h), where (x, y) are the\n      coordinates of the box centers and (w, h) are the width and height.\n\n    ``HorizontalBoxes`` only restores 'xyxy' mode of data. If the the data is\n    in 'cxcywh' mode, users need to input ``in_mode='cxcywh'`` and The code\n    will convert the 'cxcywh' data to 'xyxy' automatically.\n\n    Args:\n        data (Tensor or np.ndarray or Sequence): The box data with shape of\n            (..., 4).\n        dtype (torch.dtype, Optional): data type of boxes. Defaults to None.\n        device (str or torch.device, Optional): device of boxes.\n            Default to None.\n        clone (bool): Whether clone ``boxes`` or not. Defaults to True.\n        mode (str, Optional): the mode of boxes. If it is 'cxcywh', the\n            `data` will be converted to 'xyxy' mode. Defaults to None.\n    \"\"\"\n\n    box_dim: int = 4\n\n    def __init__(self,\n                 data: Union[Tensor, np.ndarray],\n                 dtype: torch.dtype = None,\n                 device: DeviceType = None,\n                 clone: bool = True,\n                 in_mode: Optional[str] = None) -> None:\n        super().__init__(data=data, dtype=dtype, device=device, clone=clone)\n        if isinstance(in_mode, str):\n            if in_mode not in ('xyxy', 'cxcywh'):\n                raise ValueError(f'Get invalid mode {in_mode}.')\n            if in_mode == 'cxcywh':\n                self.tensor = self.cxcywh_to_xyxy(self.tensor)\n\n    @staticmethod\n    def cxcywh_to_xyxy(boxes: Tensor) -> Tensor:\n        \"\"\"Convert box coordinates from (cx, cy, w, h) to (x1, y1, x2, y2).\n\n        Args:\n            boxes (Tensor): cxcywh boxes tensor with shape of (..., 4).\n\n        Returns:\n            Tensor: xyxy boxes tensor with shape of (..., 4).\n        \"\"\"\n        ctr, wh = boxes.split((2, 2), dim=-1)\n        return torch.cat([(ctr - wh / 2), (ctr + wh / 2)], dim=-1)\n\n    @staticmethod\n    def xyxy_to_cxcywh(boxes: Tensor) -> Tensor:\n        \"\"\"Convert box coordinates from (x1, y1, x2, y2) to (cx, cy, w, h).\n\n        Args:\n            boxes (Tensor): xyxy boxes tensor with shape of (..., 4).\n\n        Returns:\n            Tensor: cxcywh boxes tensor with shape of (..., 4).\n        \"\"\"\n        xy1, xy2 = boxes.split((2, 2), dim=-1)\n        return torch.cat([(xy2 + xy1) / 2, (xy2 - xy1)], dim=-1)\n\n    @property\n    def cxcywh(self) -> Tensor:\n        \"\"\"Return a tensor representing the cxcywh boxes.\"\"\"\n        return self.xyxy_to_cxcywh(self.tensor)\n\n    @property\n    def centers(self) -> Tensor:\n        \"\"\"Return a tensor representing the centers of boxes.\"\"\"\n        boxes = self.tensor\n        return (boxes[..., :2] + boxes[..., 2:]) / 2\n\n    @property\n    def areas(self) -> Tensor:\n        \"\"\"Return a tensor representing the areas of boxes.\"\"\"\n        boxes = self.tensor\n        return (boxes[..., 2] - boxes[..., 0]) * (\n            boxes[..., 3] - boxes[..., 1])\n\n    @property\n    def widths(self) -> Tensor:\n        \"\"\"Return a tensor representing the widths of boxes.\"\"\"\n        boxes = self.tensor\n        return boxes[..., 2] - boxes[..., 0]\n\n    @property\n    def heights(self) -> Tensor:\n        \"\"\"Return a tensor representing the heights of boxes.\"\"\"\n        boxes = self.tensor\n        return boxes[..., 3] - boxes[..., 1]\n\n    def flip_(self,\n              img_shape: Tuple[int, int],\n              direction: str = 'horizontal') -> None:\n        \"\"\"Flip boxes horizontally or vertically in-place.\n\n        Args:\n            img_shape (Tuple[int, int]): A tuple of image height and width.\n            direction (str): Flip direction, options are \"horizontal\",\n                \"vertical\" and \"diagonal\". Defaults to \"horizontal\"\n        \"\"\"\n        assert direction in ['horizontal', 'vertical', 'diagonal']\n        flipped = self.tensor\n        boxes = flipped.clone()\n        if direction == 'horizontal':\n            flipped[..., 0] = img_shape[1] - boxes[..., 2]\n            flipped[..., 2] = img_shape[1] - boxes[..., 0]\n        elif direction == 'vertical':\n            flipped[..., 1] = img_shape[0] - boxes[..., 3]\n            flipped[..., 3] = img_shape[0] - boxes[..., 1]\n        else:\n            flipped[..., 0] = img_shape[1] - boxes[..., 2]\n            flipped[..., 1] = img_shape[0] - boxes[..., 3]\n            flipped[..., 2] = img_shape[1] - boxes[..., 0]\n            flipped[..., 3] = img_shape[0] - boxes[..., 1]\n\n    def translate_(self, distances: Tuple[float, float]) -> None:\n        \"\"\"Translate boxes in-place.\n\n        Args:\n            distances (Tuple[float, float]): translate distances. The first\n                is horizontal distance and the second is vertical distance.\n        \"\"\"\n        boxes = self.tensor\n        assert len(distances) == 2\n        self.tensor = boxes + boxes.new_tensor(distances).repeat(2)\n\n    def clip_(self, img_shape: Tuple[int, int]) -> None:\n        \"\"\"Clip boxes according to the image shape in-place.\n\n        Args:\n            img_shape (Tuple[int, int]): A tuple of image height and width.\n        \"\"\"\n        boxes = self.tensor\n        boxes[..., 0::2] = boxes[..., 0::2].clamp(0, img_shape[1])\n        boxes[..., 1::2] = boxes[..., 1::2].clamp(0, img_shape[0])\n\n    def rotate_(self, center: Tuple[float, float], angle: float) -> None:\n        \"\"\"Rotate all boxes in-place.\n\n        Args:\n            center (Tuple[float, float]): Rotation origin.\n            angle (float): Rotation angle represented in degrees. Positive\n                values mean clockwise rotation.\n        \"\"\"\n        boxes = self.tensor\n        rotation_matrix = boxes.new_tensor(\n            cv2.getRotationMatrix2D(center, -angle, 1))\n\n        corners = self.hbox2corner(boxes)\n        corners = torch.cat(\n            [corners, corners.new_ones(*corners.shape[:-1], 1)], dim=-1)\n        corners_T = torch.transpose(corners, -1, -2)\n        corners_T = torch.matmul(rotation_matrix, corners_T)\n        corners = torch.transpose(corners_T, -1, -2)\n        self.tensor = self.corner2hbox(corners)\n\n    def project_(self, homography_matrix: Union[Tensor, np.ndarray]) -> None:\n        \"\"\"Geometric transformat boxes in-place.\n\n        Args:\n            homography_matrix (Tensor or np.ndarray]):\n                Shape (3, 3) for geometric transformation.\n        \"\"\"\n        boxes = self.tensor\n        if isinstance(homography_matrix, np.ndarray):\n            homography_matrix = boxes.new_tensor(homography_matrix)\n        corners = self.hbox2corner(boxes)\n        corners = torch.cat(\n            [corners, corners.new_ones(*corners.shape[:-1], 1)], dim=-1)\n        corners_T = torch.transpose(corners, -1, -2)\n        corners_T = torch.matmul(homography_matrix, corners_T)\n        corners = torch.transpose(corners_T, -1, -2)\n        # Convert to homogeneous coordinates by normalization\n        corners = corners[..., :2] / corners[..., 2:3]\n        self.tensor = self.corner2hbox(corners)\n\n    @staticmethod\n    def hbox2corner(boxes: Tensor) -> Tensor:\n        \"\"\"Convert box coordinates from (x1, y1, x2, y2) to corners ((x1, y1),\n        (x2, y1), (x1, y2), (x2, y2)).\n\n        Args:\n            boxes (Tensor): Horizontal box tensor with shape of (..., 4).\n\n        Returns:\n            Tensor: Corner tensor with shape of (..., 4, 2).\n        \"\"\"\n        x1, y1, x2, y2 = torch.split(boxes, 1, dim=-1)\n        corners = torch.cat([x1, y1, x2, y1, x1, y2, x2, y2], dim=-1)\n        return corners.reshape(*corners.shape[:-1], 4, 2)\n\n    @staticmethod\n    def corner2hbox(corners: Tensor) -> Tensor:\n        \"\"\"Convert box coordinates from corners ((x1, y1), (x2, y1), (x1, y2),\n        (x2, y2)) to (x1, y1, x2, y2).\n\n        Args:\n            corners (Tensor): Corner tensor with shape of (..., 4, 2).\n\n        Returns:\n            Tensor: Horizontal box tensor with shape of (..., 4).\n        \"\"\"\n        if corners.numel() == 0:\n            return corners.new_zeros((0, 4))\n        min_xy = corners.min(dim=-2)[0]\n        max_xy = corners.max(dim=-2)[0]\n        return torch.cat([min_xy, max_xy], dim=-1)\n\n    def rescale_(self, scale_factor: Tuple[float, float]) -> None:\n        \"\"\"Rescale boxes w.r.t. rescale_factor in-place.\n\n        Note:\n            Both ``rescale_`` and ``resize_`` will enlarge or shrink boxes\n            w.r.t ``scale_facotr``. The difference is that ``resize_`` only\n            changes the width and the height of boxes, but ``rescale_`` also\n            rescales the box centers simultaneously.\n\n        Args:\n            scale_factor (Tuple[float, float]): factors for scaling boxes.\n                The length should be 2.\n        \"\"\"\n        boxes = self.tensor\n        assert len(scale_factor) == 2\n        scale_factor = boxes.new_tensor(scale_factor).repeat(2)\n        self.tensor = boxes * scale_factor\n\n    def resize_(self, scale_factor: Tuple[float, float]) -> None:\n        \"\"\"Resize the box width and height w.r.t scale_factor in-place.\n\n        Note:\n            Both ``rescale_`` and ``resize_`` will enlarge or shrink boxes\n            w.r.t ``scale_facotr``. The difference is that ``resize_`` only\n            changes the width and the height of boxes, but ``rescale_`` also\n            rescales the box centers simultaneously.\n\n        Args:\n            scale_factor (Tuple[float, float]): factors for scaling box\n                shapes. The length should be 2.\n        \"\"\"\n        boxes = self.tensor\n        assert len(scale_factor) == 2\n        ctrs = (boxes[..., 2:] + boxes[..., :2]) / 2\n        wh = boxes[..., 2:] - boxes[..., :2]\n        scale_factor = boxes.new_tensor(scale_factor)\n        wh = wh * scale_factor\n        xy1 = ctrs - 0.5 * wh\n        xy2 = ctrs + 0.5 * wh\n        self.tensor = torch.cat([xy1, xy2], dim=-1)\n\n    def is_inside(self,\n                  img_shape: Tuple[int, int],\n                  all_inside: bool = False,\n                  allowed_border: int = 0) -> BoolTensor:\n        \"\"\"Find boxes inside the image.\n\n        Args:\n            img_shape (Tuple[int, int]): A tuple of image height and width.\n            all_inside (bool): Whether the boxes are all inside the image or\n                part inside the image. Defaults to False.\n            allowed_border (int): Boxes that extend beyond the image shape\n                boundary by more than ``allowed_border`` are considered\n                \"outside\" Defaults to 0.\n        Returns:\n            BoolTensor: A BoolTensor indicating whether the box is inside\n            the image. Assuming the original boxes have shape (m, n, 4),\n            the output has shape (m, n).\n        \"\"\"\n        img_h, img_w = img_shape\n        boxes = self.tensor\n        if all_inside:\n            return (boxes[:, 0] >= -allowed_border) & \\\n                (boxes[:, 1] >= -allowed_border) & \\\n                (boxes[:, 2] < img_w + allowed_border) & \\\n                (boxes[:, 3] < img_h + allowed_border)\n        else:\n            return (boxes[..., 0] < img_w + allowed_border) & \\\n                (boxes[..., 1] < img_h + allowed_border) & \\\n                (boxes[..., 2] > -allowed_border) & \\\n                (boxes[..., 3] > -allowed_border)\n\n    def find_inside_points(self,\n                           points: Tensor,\n                           is_aligned: bool = False) -> BoolTensor:\n        \"\"\"Find inside box points. Boxes dimension must be 2.\n\n        Args:\n            points (Tensor): Points coordinates. Has shape of (m, 2).\n            is_aligned (bool): Whether ``points`` has been aligned with boxes\n                or not. If True, the length of boxes and ``points`` should be\n                the same. Defaults to False.\n\n        Returns:\n            BoolTensor: A BoolTensor indicating whether a point is inside\n            boxes. Assuming the boxes has shape of (n, 4), if ``is_aligned``\n            is False. The index has shape of (m, n). If ``is_aligned`` is\n            True, m should be equal to n and the index has shape of (m, ).\n        \"\"\"\n        boxes = self.tensor\n        assert boxes.dim() == 2, 'boxes dimension must be 2.'\n\n        if not is_aligned:\n            boxes = boxes[None, :, :]\n            points = points[:, None, :]\n        else:\n            assert boxes.size(0) == points.size(0)\n\n        x_min, y_min, x_max, y_max = boxes.unbind(dim=-1)\n        return (points[..., 0] >= x_min) & (points[..., 0] <= x_max) & \\\n            (points[..., 1] >= y_min) & (points[..., 1] <= y_max)\n\n    def create_masks(self, img_shape: Tuple[int, int]) -> BitmapMasks:\n        \"\"\"\n        Args:\n            img_shape (Tuple[int, int]): A tuple of image height and width.\n\n        Returns:\n            :obj:`BitmapMasks`: Converted masks\n        \"\"\"\n        img_h, img_w = img_shape\n        boxes = self.tensor\n\n        xmin, ymin = boxes[:, 0:1], boxes[:, 1:2]\n        xmax, ymax = boxes[:, 2:3], boxes[:, 3:4]\n        gt_masks = np.zeros((len(boxes), img_h, img_w), dtype=np.uint8)\n        for i in range(len(boxes)):\n            gt_masks[i,\n                     int(ymin[i]):int(ymax[i]),\n                     int(xmin[i]):int(xmax[i])] = 1\n        return BitmapMasks(gt_masks, img_h, img_w)\n\n    @staticmethod\n    def overlaps(boxes1: BaseBoxes,\n                 boxes2: BaseBoxes,\n                 mode: str = 'iou',\n                 is_aligned: bool = False,\n                 eps: float = 1e-6) -> Tensor:\n        \"\"\"Calculate overlap between two set of boxes with their types\n        converted to ``HorizontalBoxes``.\n\n        Args:\n            boxes1 (:obj:`BaseBoxes`): BaseBoxes with shape of (m, box_dim)\n                or empty.\n            boxes2 (:obj:`BaseBoxes`): BaseBoxes with shape of (n, box_dim)\n                or empty.\n            mode (str): \"iou\" (intersection over union), \"iof\" (intersection\n                over foreground). Defaults to \"iou\".\n            is_aligned (bool): If True, then m and n must be equal. Defaults\n                to False.\n            eps (float): A value added to the denominator for numerical\n                stability. Defaults to 1e-6.\n\n        Returns:\n            Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,)\n        \"\"\"\n        boxes1 = boxes1.convert_to('hbox')\n        boxes2 = boxes2.convert_to('hbox')\n        return bbox_overlaps(\n            boxes1.tensor,\n            boxes2.tensor,\n            mode=mode,\n            is_aligned=is_aligned,\n            eps=eps)\n\n    @staticmethod\n    def from_instance_masks(masks: MaskType) -> 'HorizontalBoxes':\n        \"\"\"Create horizontal boxes from instance masks.\n\n        Args:\n            masks (:obj:`BitmapMasks` or :obj:`PolygonMasks`): BitmapMasks or\n                PolygonMasks instance with length of n.\n\n        Returns:\n            :obj:`HorizontalBoxes`: Converted boxes with shape of (n, 4).\n        \"\"\"\n        num_masks = len(masks)\n        boxes = np.zeros((num_masks, 4), dtype=np.float32)\n        if isinstance(masks, BitmapMasks):\n            x_any = masks.masks.any(axis=1)\n            y_any = masks.masks.any(axis=2)\n            for idx in range(num_masks):\n                x = np.where(x_any[idx, :])[0]\n                y = np.where(y_any[idx, :])[0]\n                if len(x) > 0 and len(y) > 0:\n                    # use +1 for x_max and y_max so that the right and bottom\n                    # boundary of instance masks are fully included by the box\n                    boxes[idx, :] = np.array(\n                        [x[0], y[0], x[-1] + 1, y[-1] + 1], dtype=np.float32)\n        elif isinstance(masks, PolygonMasks):\n            for idx, poly_per_obj in enumerate(masks.masks):\n                # simply use a number that is big enough for comparison with\n                # coordinates\n                xy_min = np.array([masks.width * 2, masks.height * 2],\n                                  dtype=np.float32)\n                xy_max = np.zeros(2, dtype=np.float32)\n                for p in poly_per_obj:\n                    xy = np.array(p).reshape(-1, 2).astype(np.float32)\n                    xy_min = np.minimum(xy_min, np.min(xy, axis=0))\n                    xy_max = np.maximum(xy_max, np.max(xy, axis=0))\n                boxes[idx, :2] = xy_min\n                boxes[idx, 2:] = xy_max\n        else:\n            raise TypeError(\n                '`masks` must be `BitmapMasks`  or `PolygonMasks`, '\n                f'but got {type(masks)}.')\n        return HorizontalBoxes(boxes)\n"
  },
  {
    "path": "mmdet/structures/bbox/transforms.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Sequence, Tuple, Union\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.structures.bbox import BaseBoxes\n\n\ndef find_inside_bboxes(bboxes: Tensor, img_h: int, img_w: int) -> Tensor:\n    \"\"\"Find bboxes as long as a part of bboxes is inside the image.\n\n    Args:\n        bboxes (Tensor): Shape (N, 4).\n        img_h (int): Image height.\n        img_w (int): Image width.\n\n    Returns:\n        Tensor: Index of the remaining bboxes.\n    \"\"\"\n    inside_inds = (bboxes[:, 0] < img_w) & (bboxes[:, 2] > 0) \\\n        & (bboxes[:, 1] < img_h) & (bboxes[:, 3] > 0)\n    return inside_inds\n\n\ndef bbox_flip(bboxes: Tensor,\n              img_shape: Tuple[int],\n              direction: str = 'horizontal') -> Tensor:\n    \"\"\"Flip bboxes horizontally or vertically.\n\n    Args:\n        bboxes (Tensor): Shape (..., 4*k)\n        img_shape (Tuple[int]): Image shape.\n        direction (str): Flip direction, options are \"horizontal\", \"vertical\",\n            \"diagonal\". Default: \"horizontal\"\n\n    Returns:\n        Tensor: Flipped bboxes.\n    \"\"\"\n    assert bboxes.shape[-1] % 4 == 0\n    assert direction in ['horizontal', 'vertical', 'diagonal']\n    flipped = bboxes.clone()\n    if direction == 'horizontal':\n        flipped[..., 0::4] = img_shape[1] - bboxes[..., 2::4]\n        flipped[..., 2::4] = img_shape[1] - bboxes[..., 0::4]\n    elif direction == 'vertical':\n        flipped[..., 1::4] = img_shape[0] - bboxes[..., 3::4]\n        flipped[..., 3::4] = img_shape[0] - bboxes[..., 1::4]\n    else:\n        flipped[..., 0::4] = img_shape[1] - bboxes[..., 2::4]\n        flipped[..., 1::4] = img_shape[0] - bboxes[..., 3::4]\n        flipped[..., 2::4] = img_shape[1] - bboxes[..., 0::4]\n        flipped[..., 3::4] = img_shape[0] - bboxes[..., 1::4]\n    return flipped\n\n\ndef bbox_mapping(bboxes: Tensor,\n                 img_shape: Tuple[int],\n                 scale_factor: Union[float, Tuple[float]],\n                 flip: bool,\n                 flip_direction: str = 'horizontal') -> Tensor:\n    \"\"\"Map bboxes from the original image scale to testing scale.\"\"\"\n    new_bboxes = bboxes * bboxes.new_tensor(scale_factor)\n    if flip:\n        new_bboxes = bbox_flip(new_bboxes, img_shape, flip_direction)\n    return new_bboxes\n\n\ndef bbox_mapping_back(bboxes: Tensor,\n                      img_shape: Tuple[int],\n                      scale_factor: Union[float, Tuple[float]],\n                      flip: bool,\n                      flip_direction: str = 'horizontal') -> Tensor:\n    \"\"\"Map bboxes from testing scale to original image scale.\"\"\"\n    new_bboxes = bbox_flip(bboxes, img_shape,\n                           flip_direction) if flip else bboxes\n    new_bboxes = new_bboxes.view(-1, 4) / new_bboxes.new_tensor(scale_factor)\n    return new_bboxes.view(bboxes.shape)\n\n\ndef bbox2roi(bbox_list: List[Union[Tensor, BaseBoxes]]) -> Tensor:\n    \"\"\"Convert a list of bboxes to roi format.\n\n    Args:\n        bbox_list (List[Union[Tensor, :obj:`BaseBoxes`]): a list of bboxes\n            corresponding to a batch of images.\n\n    Returns:\n        Tensor: shape (n, box_dim + 1), where ``box_dim`` depends on the\n        different box types. For example, If the box type in ``bbox_list``\n        is HorizontalBoxes, the output shape is (n, 5). Each row of data\n        indicates [batch_ind, x1, y1, x2, y2].\n    \"\"\"\n    rois_list = []\n    for img_id, bboxes in enumerate(bbox_list):\n        bboxes = get_box_tensor(bboxes)\n        img_inds = bboxes.new_full((bboxes.size(0), 1), img_id)\n        rois = torch.cat([img_inds, bboxes], dim=-1)\n        rois_list.append(rois)\n    rois = torch.cat(rois_list, 0)\n    return rois\n\n\ndef roi2bbox(rois: Tensor) -> List[Tensor]:\n    \"\"\"Convert rois to bounding box format.\n\n    Args:\n        rois (Tensor): RoIs with the shape (n, 5) where the first\n            column indicates batch id of each RoI.\n\n    Returns:\n        List[Tensor]: Converted boxes of corresponding rois.\n    \"\"\"\n    bbox_list = []\n    img_ids = torch.unique(rois[:, 0].cpu(), sorted=True)\n    for img_id in img_ids:\n        inds = (rois[:, 0] == img_id.item())\n        bbox = rois[inds, 1:]\n        bbox_list.append(bbox)\n    return bbox_list\n\n\n# TODO remove later\ndef bbox2result(bboxes: Union[Tensor, np.ndarray], labels: Union[Tensor,\n                                                                 np.ndarray],\n                num_classes: int) -> List[np.ndarray]:\n    \"\"\"Convert detection results to a list of numpy arrays.\n\n    Args:\n        bboxes (Tensor | np.ndarray): shape (n, 5)\n        labels (Tensor | np.ndarray): shape (n, )\n        num_classes (int): class number, including background class\n\n    Returns:\n        List(np.ndarray]): bbox results of each class\n    \"\"\"\n    if bboxes.shape[0] == 0:\n        return [np.zeros((0, 5), dtype=np.float32) for i in range(num_classes)]\n    else:\n        if isinstance(bboxes, torch.Tensor):\n            bboxes = bboxes.detach().cpu().numpy()\n            labels = labels.detach().cpu().numpy()\n        return [bboxes[labels == i, :] for i in range(num_classes)]\n\n\ndef distance2bbox(\n    points: Tensor,\n    distance: Tensor,\n    max_shape: Optional[Union[Sequence[int], Tensor,\n                              Sequence[Sequence[int]]]] = None\n) -> Tensor:\n    \"\"\"Decode distance prediction to bounding box.\n\n    Args:\n        points (Tensor): Shape (B, N, 2) or (N, 2).\n        distance (Tensor): Distance from the given point to 4\n            boundaries (left, top, right, bottom). Shape (B, N, 4) or (N, 4)\n        max_shape (Union[Sequence[int], Tensor, Sequence[Sequence[int]]],\n            optional): Maximum bounds for boxes, specifies\n            (H, W, C) or (H, W). If priors shape is (B, N, 4), then\n            the max_shape should be a Sequence[Sequence[int]]\n            and the length of max_shape should also be B.\n\n    Returns:\n        Tensor: Boxes with shape (N, 4) or (B, N, 4)\n    \"\"\"\n\n    x1 = points[..., 0] - distance[..., 0]\n    y1 = points[..., 1] - distance[..., 1]\n    x2 = points[..., 0] + distance[..., 2]\n    y2 = points[..., 1] + distance[..., 3]\n\n    bboxes = torch.stack([x1, y1, x2, y2], -1)\n\n    if max_shape is not None:\n        if bboxes.dim() == 2 and not torch.onnx.is_in_onnx_export():\n            # speed up\n            bboxes[:, 0::2].clamp_(min=0, max=max_shape[1])\n            bboxes[:, 1::2].clamp_(min=0, max=max_shape[0])\n            return bboxes\n\n        # clip bboxes with dynamic `min` and `max` for onnx\n        if torch.onnx.is_in_onnx_export():\n            # TODO: delete\n            from mmdet.core.export import dynamic_clip_for_onnx\n            x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape)\n            bboxes = torch.stack([x1, y1, x2, y2], dim=-1)\n            return bboxes\n        if not isinstance(max_shape, torch.Tensor):\n            max_shape = x1.new_tensor(max_shape)\n        max_shape = max_shape[..., :2].type_as(x1)\n        if max_shape.ndim == 2:\n            assert bboxes.ndim == 3\n            assert max_shape.size(0) == bboxes.size(0)\n\n        min_xy = x1.new_tensor(0)\n        max_xy = torch.cat([max_shape, max_shape],\n                           dim=-1).flip(-1).unsqueeze(-2)\n        bboxes = torch.where(bboxes < min_xy, min_xy, bboxes)\n        bboxes = torch.where(bboxes > max_xy, max_xy, bboxes)\n\n    return bboxes\n\n\ndef bbox2distance(points: Tensor,\n                  bbox: Tensor,\n                  max_dis: Optional[float] = None,\n                  eps: float = 0.1) -> Tensor:\n    \"\"\"Decode bounding box based on distances.\n\n    Args:\n        points (Tensor): Shape (n, 2) or (b, n, 2), [x, y].\n        bbox (Tensor): Shape (n, 4) or (b, n, 4), \"xyxy\" format\n        max_dis (float, optional): Upper bound of the distance.\n        eps (float): a small value to ensure target < max_dis, instead <=\n\n    Returns:\n        Tensor: Decoded distances.\n    \"\"\"\n    left = points[..., 0] - bbox[..., 0]\n    top = points[..., 1] - bbox[..., 1]\n    right = bbox[..., 2] - points[..., 0]\n    bottom = bbox[..., 3] - points[..., 1]\n    if max_dis is not None:\n        left = left.clamp(min=0, max=max_dis - eps)\n        top = top.clamp(min=0, max=max_dis - eps)\n        right = right.clamp(min=0, max=max_dis - eps)\n        bottom = bottom.clamp(min=0, max=max_dis - eps)\n    return torch.stack([left, top, right, bottom], -1)\n\n\ndef bbox_rescale(bboxes: Tensor, scale_factor: float = 1.0) -> Tensor:\n    \"\"\"Rescale bounding box w.r.t. scale_factor.\n\n    Args:\n        bboxes (Tensor): Shape (n, 4) for bboxes or (n, 5) for rois\n        scale_factor (float): rescale factor\n\n    Returns:\n        Tensor: Rescaled bboxes.\n    \"\"\"\n    if bboxes.size(1) == 5:\n        bboxes_ = bboxes[:, 1:]\n        inds_ = bboxes[:, 0]\n    else:\n        bboxes_ = bboxes\n    cx = (bboxes_[:, 0] + bboxes_[:, 2]) * 0.5\n    cy = (bboxes_[:, 1] + bboxes_[:, 3]) * 0.5\n    w = bboxes_[:, 2] - bboxes_[:, 0]\n    h = bboxes_[:, 3] - bboxes_[:, 1]\n    w = w * scale_factor\n    h = h * scale_factor\n    x1 = cx - 0.5 * w\n    x2 = cx + 0.5 * w\n    y1 = cy - 0.5 * h\n    y2 = cy + 0.5 * h\n    if bboxes.size(1) == 5:\n        rescaled_bboxes = torch.stack([inds_, x1, y1, x2, y2], dim=-1)\n    else:\n        rescaled_bboxes = torch.stack([x1, y1, x2, y2], dim=-1)\n    return rescaled_bboxes\n\n\ndef bbox_cxcywh_to_xyxy(bbox: Tensor) -> Tensor:\n    \"\"\"Convert bbox coordinates from (cx, cy, w, h) to (x1, y1, x2, y2).\n\n    Args:\n        bbox (Tensor): Shape (n, 4) for bboxes.\n\n    Returns:\n        Tensor: Converted bboxes.\n    \"\"\"\n    cx, cy, w, h = bbox.split((1, 1, 1, 1), dim=-1)\n    bbox_new = [(cx - 0.5 * w), (cy - 0.5 * h), (cx + 0.5 * w), (cy + 0.5 * h)]\n    return torch.cat(bbox_new, dim=-1)\n\n\ndef bbox_xyxy_to_cxcywh(bbox: Tensor) -> Tensor:\n    \"\"\"Convert bbox coordinates from (x1, y1, x2, y2) to (cx, cy, w, h).\n\n    Args:\n        bbox (Tensor): Shape (n, 4) for bboxes.\n\n    Returns:\n        Tensor: Converted bboxes.\n    \"\"\"\n    x1, y1, x2, y2 = bbox.split((1, 1, 1, 1), dim=-1)\n    bbox_new = [(x1 + x2) / 2, (y1 + y2) / 2, (x2 - x1), (y2 - y1)]\n    return torch.cat(bbox_new, dim=-1)\n\n\ndef bbox2corner(bboxes: torch.Tensor) -> torch.Tensor:\n    \"\"\"Convert bbox coordinates from (x1, y1, x2, y2) to corners ((x1, y1),\n    (x2, y1), (x1, y2), (x2, y2)).\n\n    Args:\n        bboxes (Tensor): Shape (n, 4) for bboxes.\n    Returns:\n        Tensor: Shape (n*4, 2) for corners.\n    \"\"\"\n    x1, y1, x2, y2 = torch.split(bboxes, 1, dim=1)\n    return torch.cat([x1, y1, x2, y1, x1, y2, x2, y2], dim=1).reshape(-1, 2)\n\n\ndef corner2bbox(corners: torch.Tensor) -> torch.Tensor:\n    \"\"\"Convert bbox coordinates from corners ((x1, y1), (x2, y1), (x1, y2),\n    (x2, y2)) to (x1, y1, x2, y2).\n\n    Args:\n        corners (Tensor): Shape (n*4, 2) for corners.\n    Returns:\n        Tensor: Shape (n, 4) for bboxes.\n    \"\"\"\n    corners = corners.reshape(-1, 4, 2)\n    min_xy = corners.min(dim=1)[0]\n    max_xy = corners.max(dim=1)[0]\n    return torch.cat([min_xy, max_xy], dim=1)\n\n\ndef bbox_project(\n    bboxes: Union[torch.Tensor, np.ndarray],\n    homography_matrix: Union[torch.Tensor, np.ndarray],\n    img_shape: Optional[Tuple[int, int]] = None\n) -> Union[torch.Tensor, np.ndarray]:\n    \"\"\"Geometric transformation for bbox.\n\n    Args:\n        bboxes (Union[torch.Tensor, np.ndarray]): Shape (n, 4) for bboxes.\n        homography_matrix (Union[torch.Tensor, np.ndarray]):\n            Shape (3, 3) for geometric transformation.\n        img_shape (Tuple[int, int], optional): Image shape. Defaults to None.\n    Returns:\n        Union[torch.Tensor, np.ndarray]: Converted bboxes.\n    \"\"\"\n    bboxes_type = type(bboxes)\n    if bboxes_type is np.ndarray:\n        bboxes = torch.from_numpy(bboxes)\n    if isinstance(homography_matrix, np.ndarray):\n        homography_matrix = torch.from_numpy(homography_matrix)\n    corners = bbox2corner(bboxes)\n    corners = torch.cat(\n        [corners, corners.new_ones(corners.shape[0], 1)], dim=1)\n    corners = torch.matmul(homography_matrix, corners.t()).t()\n    # Convert to homogeneous coordinates by normalization\n    corners = corners[:, :2] / corners[:, 2:3]\n    bboxes = corner2bbox(corners)\n    if img_shape is not None:\n        bboxes[:, 0::2] = bboxes[:, 0::2].clamp(0, img_shape[1])\n        bboxes[:, 1::2] = bboxes[:, 1::2].clamp(0, img_shape[0])\n    if bboxes_type is np.ndarray:\n        bboxes = bboxes.numpy()\n    return bboxes\n\n\ndef cat_boxes(data_list: List[Union[Tensor, BaseBoxes]],\n              dim: int = 0) -> Union[Tensor, BaseBoxes]:\n    \"\"\"Concatenate boxes with type of tensor or box type.\n\n    Args:\n        data_list (List[Union[Tensor, :obj:`BaseBoxes`]]): A list of tensors\n            or box types need to be concatenated.\n            dim (int): The dimension over which the box are concatenated.\n                Defaults to 0.\n\n    Returns:\n        Union[Tensor, :obj`BaseBoxes`]: Concatenated results.\n    \"\"\"\n    if data_list and isinstance(data_list[0], BaseBoxes):\n        return data_list[0].cat(data_list, dim=dim)\n    else:\n        return torch.cat(data_list, dim=dim)\n\n\ndef stack_boxes(data_list: List[Union[Tensor, BaseBoxes]],\n                dim: int = 0) -> Union[Tensor, BaseBoxes]:\n    \"\"\"Stack boxes with type of tensor or box type.\n\n    Args:\n        data_list (List[Union[Tensor, :obj:`BaseBoxes`]]): A list of tensors\n            or box types need to be stacked.\n            dim (int): The dimension over which the box are stacked.\n                Defaults to 0.\n\n    Returns:\n        Union[Tensor, :obj`BaseBoxes`]: Stacked results.\n    \"\"\"\n    if data_list and isinstance(data_list[0], BaseBoxes):\n        return data_list[0].stack(data_list, dim=dim)\n    else:\n        return torch.stack(data_list, dim=dim)\n\n\ndef scale_boxes(boxes: Union[Tensor, BaseBoxes],\n                scale_factor: Tuple[float, float]) -> Union[Tensor, BaseBoxes]:\n    \"\"\"Scale boxes with type of tensor or box type.\n\n    Args:\n        boxes (Tensor or :obj:`BaseBoxes`): boxes need to be scaled. Its type\n            can be a tensor or a box type.\n        scale_factor (Tuple[float, float]): factors for scaling boxes.\n            The length should be 2.\n\n    Returns:\n        Union[Tensor, :obj:`BaseBoxes`]: Scaled boxes.\n    \"\"\"\n    if isinstance(boxes, BaseBoxes):\n        boxes.rescale_(scale_factor)\n        return boxes\n    else:\n        # Tensor boxes will be treated as horizontal boxes\n        repeat_num = int(boxes.size(-1) / 2)\n        scale_factor = boxes.new_tensor(scale_factor).repeat((1, repeat_num))\n        return boxes * scale_factor\n\n\ndef get_box_wh(boxes: Union[Tensor, BaseBoxes]) -> Tuple[Tensor, Tensor]:\n    \"\"\"Get the width and height of boxes with type of tensor or box type.\n\n    Args:\n        boxes (Tensor or :obj:`BaseBoxes`): boxes with type of tensor\n            or box type.\n\n    Returns:\n        Tuple[Tensor, Tensor]: the width and height of boxes.\n    \"\"\"\n    if isinstance(boxes, BaseBoxes):\n        w = boxes.widths\n        h = boxes.heights\n    else:\n        # Tensor boxes will be treated as horizontal boxes by defaults\n        w = boxes[:, 2] - boxes[:, 0]\n        h = boxes[:, 3] - boxes[:, 1]\n    return w, h\n\n\ndef get_box_tensor(boxes: Union[Tensor, BaseBoxes]) -> Tensor:\n    \"\"\"Get tensor data from box type boxes.\n\n    Args:\n        boxes (Tensor or BaseBoxes): boxes with type of tensor or box type.\n            If its type is a tensor, the boxes will be directly returned.\n            If its type is a box type, the `boxes.tensor` will be returned.\n\n    Returns:\n        Tensor: boxes tensor.\n    \"\"\"\n    if isinstance(boxes, BaseBoxes):\n        boxes = boxes.tensor\n    return boxes\n\n\ndef empty_box_as(boxes: Union[Tensor, BaseBoxes]) -> Union[Tensor, BaseBoxes]:\n    \"\"\"Generate empty box according to input ``boxes` type and device.\n\n    Args:\n        boxes (Tensor or :obj:`BaseBoxes`): boxes with type of tensor\n            or box type.\n\n    Returns:\n        Union[Tensor, BaseBoxes]: Generated empty box.\n    \"\"\"\n    if isinstance(boxes, BaseBoxes):\n        return boxes.empty_boxes()\n    else:\n        # Tensor boxes will be treated as horizontal boxes by defaults\n        return boxes.new_zeros(0, 4)\n\n\ndef bbox_xyxy_to_cxcyah(bboxes: torch.Tensor) -> torch.Tensor:\n    \"\"\"Convert bbox coordinates from (x1, y1, x2, y2) to (cx, cy, ratio, h).\n\n    Args:\n        bbox (Tensor): Shape (n, 4) for bboxes.\n\n    Returns:\n        Tensor: Converted bboxes.\n    \"\"\"\n    cx = (bboxes[:, 2] + bboxes[:, 0]) / 2\n    cy = (bboxes[:, 3] + bboxes[:, 1]) / 2\n    w = bboxes[:, 2] - bboxes[:, 0]\n    h = bboxes[:, 3] - bboxes[:, 1]\n    xyah = torch.stack([cx, cy, w / h, h], -1)\n    return xyah\n\n\ndef bbox_cxcyah_to_xyxy(bboxes: torch.Tensor) -> torch.Tensor:\n    \"\"\"Convert bbox coordinates from (cx, cy, ratio, h) to (x1, y1, x2, y2).\n\n    Args:\n        bbox (Tensor): Shape (n, 4) for bboxes.\n    Returns:\n        Tensor: Converted bboxes.\n    \"\"\"\n    cx, cy, ratio, h = bboxes.split((1, 1, 1, 1), dim=-1)\n    w = ratio * h\n    x1y1x2y2 = [cx - w / 2.0, cy - h / 2.0, cx + w / 2.0, cy + h / 2.0]\n    return torch.cat(x1y1x2y2, dim=-1)\n"
  },
  {
    "path": "mmdet/structures/det_data_sample.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional\n\nfrom mmengine.structures import BaseDataElement, InstanceData, PixelData\n\n\nclass DetDataSample(BaseDataElement):\n    \"\"\"A data structure interface of MMDetection. They are used as interfaces\n    between different components.\n\n    The attributes in ``DetDataSample`` are divided into several parts:\n\n        - ``proposals``(InstanceData): Region proposals used in two-stage\n            detectors.\n        - ``gt_instances``(InstanceData): Ground truth of instance annotations.\n        - ``pred_instances``(InstanceData): Instances of detection predictions.\n        - ``pred_track_instances``(InstanceData): Instances of tracking\n            predictions.\n        - ``ignored_instances``(InstanceData): Instances to be ignored during\n            training/testing.\n        - ``gt_panoptic_seg``(PixelData): Ground truth of panoptic\n            segmentation.\n        - ``pred_panoptic_seg``(PixelData): Prediction of panoptic\n           segmentation.\n        - ``gt_sem_seg``(PixelData): Ground truth of semantic segmentation.\n        - ``pred_sem_seg``(PixelData): Prediction of semantic segmentation.\n\n    Examples:\n         >>> import torch\n         >>> import numpy as np\n         >>> from mmengine.structures import InstanceData\n         >>> from mmdet.structures import DetDataSample\n\n         >>> data_sample = DetDataSample()\n         >>> img_meta = dict(img_shape=(800, 1196),\n         ...                 pad_shape=(800, 1216))\n         >>> gt_instances = InstanceData(metainfo=img_meta)\n         >>> gt_instances.bboxes = torch.rand((5, 4))\n         >>> gt_instances.labels = torch.rand((5,))\n         >>> data_sample.gt_instances = gt_instances\n         >>> assert 'img_shape' in data_sample.gt_instances.metainfo_keys()\n         >>> len(data_sample.gt_instances)\n         5\n         >>> print(data_sample)\n        <DetDataSample(\n\n            META INFORMATION\n\n            DATA FIELDS\n            gt_instances: <InstanceData(\n\n                    META INFORMATION\n                    pad_shape: (800, 1216)\n                    img_shape: (800, 1196)\n\n                    DATA FIELDS\n                    labels: tensor([0.8533, 0.1550, 0.5433, 0.7294, 0.5098])\n                    bboxes:\n                    tensor([[9.7725e-01, 5.8417e-01, 1.7269e-01, 6.5694e-01],\n                            [1.7894e-01, 5.1780e-01, 7.0590e-01, 4.8589e-01],\n                            [7.0392e-01, 6.6770e-01, 1.7520e-01, 1.4267e-01],\n                            [2.2411e-01, 5.1962e-01, 9.6953e-01, 6.6994e-01],\n                            [4.1338e-01, 2.1165e-01, 2.7239e-04, 6.8477e-01]])\n                ) at 0x7f21fb1b9190>\n        ) at 0x7f21fb1b9880>\n         >>> pred_instances = InstanceData(metainfo=img_meta)\n         >>> pred_instances.bboxes = torch.rand((5, 4))\n         >>> pred_instances.scores = torch.rand((5,))\n         >>> data_sample = DetDataSample(pred_instances=pred_instances)\n         >>> assert 'pred_instances' in data_sample\n\n         >>> pred_track_instances = InstanceData(metainfo=img_meta)\n         >>> pred_track_instances.bboxes = torch.rand((5, 4))\n         >>> pred_track_instances.scores = torch.rand((5,))\n         >>> data_sample = DetDataSample(\n         ...    pred_track_instances=pred_track_instances)\n         >>> assert 'pred_track_instances' in data_sample\n\n         >>> data_sample = DetDataSample()\n         >>> gt_instances_data = dict(\n         ...                        bboxes=torch.rand(2, 4),\n         ...                        labels=torch.rand(2),\n         ...                        masks=np.random.rand(2, 2, 2))\n         >>> gt_instances = InstanceData(**gt_instances_data)\n         >>> data_sample.gt_instances = gt_instances\n         >>> assert 'gt_instances' in data_sample\n         >>> assert 'masks' in data_sample.gt_instances\n\n         >>> data_sample = DetDataSample()\n         >>> gt_panoptic_seg_data = dict(panoptic_seg=torch.rand(2, 4))\n         >>> gt_panoptic_seg = PixelData(**gt_panoptic_seg_data)\n         >>> data_sample.gt_panoptic_seg = gt_panoptic_seg\n         >>> print(data_sample)\n        <DetDataSample(\n\n            META INFORMATION\n\n            DATA FIELDS\n            _gt_panoptic_seg: <BaseDataElement(\n\n                    META INFORMATION\n\n                    DATA FIELDS\n                    panoptic_seg: tensor([[0.7586, 0.1262, 0.2892, 0.9341],\n                                [0.3200, 0.7448, 0.1052, 0.5371]])\n                ) at 0x7f66c2bb7730>\n            gt_panoptic_seg: <BaseDataElement(\n\n                    META INFORMATION\n\n                    DATA FIELDS\n                    panoptic_seg: tensor([[0.7586, 0.1262, 0.2892, 0.9341],\n                                [0.3200, 0.7448, 0.1052, 0.5371]])\n                ) at 0x7f66c2bb7730>\n        ) at 0x7f66c2bb7280>\n        >>> data_sample = DetDataSample()\n        >>> gt_segm_seg_data = dict(segm_seg=torch.rand(2, 2, 2))\n        >>> gt_segm_seg = PixelData(**gt_segm_seg_data)\n        >>> data_sample.gt_segm_seg = gt_segm_seg\n        >>> assert 'gt_segm_seg' in data_sample\n        >>> assert 'segm_seg' in data_sample.gt_segm_seg\n    \"\"\"\n\n    @property\n    def proposals(self) -> InstanceData:\n        return self._proposals\n\n    @proposals.setter\n    def proposals(self, value: InstanceData):\n        self.set_field(value, '_proposals', dtype=InstanceData)\n\n    @proposals.deleter\n    def proposals(self):\n        del self._proposals\n\n    @property\n    def gt_instances(self) -> InstanceData:\n        return self._gt_instances\n\n    @gt_instances.setter\n    def gt_instances(self, value: InstanceData):\n        self.set_field(value, '_gt_instances', dtype=InstanceData)\n\n    @gt_instances.deleter\n    def gt_instances(self):\n        del self._gt_instances\n\n    @property\n    def pred_instances(self) -> InstanceData:\n        return self._pred_instances\n\n    @pred_instances.setter\n    def pred_instances(self, value: InstanceData):\n        self.set_field(value, '_pred_instances', dtype=InstanceData)\n\n    @pred_instances.deleter\n    def pred_instances(self):\n        del self._pred_instances\n\n    # directly add ``pred_track_instances`` in ``DetDataSample``\n    # so that the ``TrackDataSample`` does not bother to access the\n    # instance-level information.\n    @property\n    def pred_track_instances(self) -> InstanceData:\n        return self._pred_track_instances\n\n    @pred_track_instances.setter\n    def pred_track_instances(self, value: InstanceData):\n        self.set_field(value, '_pred_track_instances', dtype=InstanceData)\n\n    @pred_track_instances.deleter\n    def pred_track_instances(self):\n        del self._pred_track_instances\n\n    @property\n    def ignored_instances(self) -> InstanceData:\n        return self._ignored_instances\n\n    @ignored_instances.setter\n    def ignored_instances(self, value: InstanceData):\n        self.set_field(value, '_ignored_instances', dtype=InstanceData)\n\n    @ignored_instances.deleter\n    def ignored_instances(self):\n        del self._ignored_instances\n\n    @property\n    def gt_panoptic_seg(self) -> PixelData:\n        return self._gt_panoptic_seg\n\n    @gt_panoptic_seg.setter\n    def gt_panoptic_seg(self, value: PixelData):\n        self.set_field(value, '_gt_panoptic_seg', dtype=PixelData)\n\n    @gt_panoptic_seg.deleter\n    def gt_panoptic_seg(self):\n        del self._gt_panoptic_seg\n\n    @property\n    def pred_panoptic_seg(self) -> PixelData:\n        return self._pred_panoptic_seg\n\n    @pred_panoptic_seg.setter\n    def pred_panoptic_seg(self, value: PixelData):\n        self.set_field(value, '_pred_panoptic_seg', dtype=PixelData)\n\n    @pred_panoptic_seg.deleter\n    def pred_panoptic_seg(self):\n        del self._pred_panoptic_seg\n\n    @property\n    def gt_sem_seg(self) -> PixelData:\n        return self._gt_sem_seg\n\n    @gt_sem_seg.setter\n    def gt_sem_seg(self, value: PixelData):\n        self.set_field(value, '_gt_sem_seg', dtype=PixelData)\n\n    @gt_sem_seg.deleter\n    def gt_sem_seg(self):\n        del self._gt_sem_seg\n\n    @property\n    def pred_sem_seg(self) -> PixelData:\n        return self._pred_sem_seg\n\n    @pred_sem_seg.setter\n    def pred_sem_seg(self, value: PixelData):\n        self.set_field(value, '_pred_sem_seg', dtype=PixelData)\n\n    @pred_sem_seg.deleter\n    def pred_sem_seg(self):\n        del self._pred_sem_seg\n\n\nSampleList = List[DetDataSample]\nOptSampleList = Optional[SampleList]\n"
  },
  {
    "path": "mmdet/structures/mask/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .mask_target import mask_target\nfrom .structures import (BaseInstanceMasks, BitmapMasks, PolygonMasks,\n                         bitmap_to_polygon, polygon_to_bitmap)\nfrom .utils import encode_mask_results, mask2bbox, split_combined_polys\n\n__all__ = [\n    'split_combined_polys', 'mask_target', 'BaseInstanceMasks', 'BitmapMasks',\n    'PolygonMasks', 'encode_mask_results', 'mask2bbox', 'polygon_to_bitmap',\n    'bitmap_to_polygon'\n]\n"
  },
  {
    "path": "mmdet/structures/mask/mask_target.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport torch\nfrom torch.nn.modules.utils import _pair\n\n\ndef mask_target(pos_proposals_list, pos_assigned_gt_inds_list, gt_masks_list,\n                cfg):\n    \"\"\"Compute mask target for positive proposals in multiple images.\n\n    Args:\n        pos_proposals_list (list[Tensor]): Positive proposals in multiple\n            images, each has shape (num_pos, 4).\n        pos_assigned_gt_inds_list (list[Tensor]): Assigned GT indices for each\n            positive proposals, each has shape (num_pos,).\n        gt_masks_list (list[:obj:`BaseInstanceMasks`]): Ground truth masks of\n            each image.\n        cfg (dict): Config dict that specifies the mask size.\n\n    Returns:\n        Tensor: Mask target of each image, has shape (num_pos, w, h).\n\n    Example:\n        >>> from mmengine.config import Config\n        >>> import mmdet\n        >>> from mmdet.data_elements.mask import BitmapMasks\n        >>> from mmdet.data_elements.mask.mask_target import *\n        >>> H, W = 17, 18\n        >>> cfg = Config({'mask_size': (13, 14)})\n        >>> rng = np.random.RandomState(0)\n        >>> # Positive proposals (tl_x, tl_y, br_x, br_y) for each image\n        >>> pos_proposals_list = [\n        >>>     torch.Tensor([\n        >>>         [ 7.2425,  5.5929, 13.9414, 14.9541],\n        >>>         [ 7.3241,  3.6170, 16.3850, 15.3102],\n        >>>     ]),\n        >>>     torch.Tensor([\n        >>>         [ 4.8448, 6.4010, 7.0314, 9.7681],\n        >>>         [ 5.9790, 2.6989, 7.4416, 4.8580],\n        >>>         [ 0.0000, 0.0000, 0.1398, 9.8232],\n        >>>     ]),\n        >>> ]\n        >>> # Corresponding class index for each proposal for each image\n        >>> pos_assigned_gt_inds_list = [\n        >>>     torch.LongTensor([7, 0]),\n        >>>     torch.LongTensor([5, 4, 1]),\n        >>> ]\n        >>> # Ground truth mask for each true object for each image\n        >>> gt_masks_list = [\n        >>>     BitmapMasks(rng.rand(8, H, W), height=H, width=W),\n        >>>     BitmapMasks(rng.rand(6, H, W), height=H, width=W),\n        >>> ]\n        >>> mask_targets = mask_target(\n        >>>     pos_proposals_list, pos_assigned_gt_inds_list,\n        >>>     gt_masks_list, cfg)\n        >>> assert mask_targets.shape == (5,) + cfg['mask_size']\n    \"\"\"\n    cfg_list = [cfg for _ in range(len(pos_proposals_list))]\n    mask_targets = map(mask_target_single, pos_proposals_list,\n                       pos_assigned_gt_inds_list, gt_masks_list, cfg_list)\n    mask_targets = list(mask_targets)\n    if len(mask_targets) > 0:\n        mask_targets = torch.cat(mask_targets)\n    return mask_targets\n\n\ndef mask_target_single(pos_proposals, pos_assigned_gt_inds, gt_masks, cfg):\n    \"\"\"Compute mask target for each positive proposal in the image.\n\n    Args:\n        pos_proposals (Tensor): Positive proposals.\n        pos_assigned_gt_inds (Tensor): Assigned GT inds of positive proposals.\n        gt_masks (:obj:`BaseInstanceMasks`): GT masks in the format of Bitmap\n            or Polygon.\n        cfg (dict): Config dict that indicate the mask size.\n\n    Returns:\n        Tensor: Mask target of each positive proposals in the image.\n\n    Example:\n        >>> from mmengine.config import Config\n        >>> import mmdet\n        >>> from mmdet.data_elements.mask import BitmapMasks\n        >>> from mmdet.data_elements.mask.mask_target import *  # NOQA\n        >>> H, W = 32, 32\n        >>> cfg = Config({'mask_size': (7, 11)})\n        >>> rng = np.random.RandomState(0)\n        >>> # Masks for each ground truth box (relative to the image)\n        >>> gt_masks_data = rng.rand(3, H, W)\n        >>> gt_masks = BitmapMasks(gt_masks_data, height=H, width=W)\n        >>> # Predicted positive boxes in one image\n        >>> pos_proposals = torch.FloatTensor([\n        >>>     [ 16.2,   5.5, 19.9, 20.9],\n        >>>     [ 17.3,  13.6, 19.3, 19.3],\n        >>>     [ 14.8,  16.4, 17.0, 23.7],\n        >>>     [  0.0,   0.0, 16.0, 16.0],\n        >>>     [  4.0,   0.0, 20.0, 16.0],\n        >>> ])\n        >>> # For each predicted proposal, its assignment to a gt mask\n        >>> pos_assigned_gt_inds = torch.LongTensor([0, 1, 2, 1, 1])\n        >>> mask_targets = mask_target_single(\n        >>>     pos_proposals, pos_assigned_gt_inds, gt_masks, cfg)\n        >>> assert mask_targets.shape == (5,) + cfg['mask_size']\n    \"\"\"\n    device = pos_proposals.device\n    mask_size = _pair(cfg.mask_size)\n    binarize = not cfg.get('soft_mask_target', False)\n    num_pos = pos_proposals.size(0)\n    if num_pos > 0:\n        proposals_np = pos_proposals.cpu().numpy()\n        maxh, maxw = gt_masks.height, gt_masks.width\n        proposals_np[:, [0, 2]] = np.clip(proposals_np[:, [0, 2]], 0, maxw)\n        proposals_np[:, [1, 3]] = np.clip(proposals_np[:, [1, 3]], 0, maxh)\n        pos_assigned_gt_inds = pos_assigned_gt_inds.cpu().numpy()\n\n        mask_targets = gt_masks.crop_and_resize(\n            proposals_np,\n            mask_size,\n            device=device,\n            inds=pos_assigned_gt_inds,\n            binarize=binarize).to_ndarray()\n\n        mask_targets = torch.from_numpy(mask_targets).float().to(device)\n    else:\n        mask_targets = pos_proposals.new_zeros((0, ) + mask_size)\n\n    return mask_targets\n"
  },
  {
    "path": "mmdet/structures/mask/structures.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport itertools\nfrom abc import ABCMeta, abstractmethod\nfrom typing import Sequence, Type, TypeVar\n\nimport cv2\nimport mmcv\nimport numpy as np\nimport pycocotools.mask as maskUtils\nimport shapely.geometry as geometry\nimport torch\nfrom mmcv.ops.roi_align import roi_align\n\nT = TypeVar('T')\n\n\nclass BaseInstanceMasks(metaclass=ABCMeta):\n    \"\"\"Base class for instance masks.\"\"\"\n\n    @abstractmethod\n    def rescale(self, scale, interpolation='nearest'):\n        \"\"\"Rescale masks as large as possible while keeping the aspect ratio.\n        For details can refer to `mmcv.imrescale`.\n\n        Args:\n            scale (tuple[int]): The maximum size (h, w) of rescaled mask.\n            interpolation (str): Same as :func:`mmcv.imrescale`.\n\n        Returns:\n            BaseInstanceMasks: The rescaled masks.\n        \"\"\"\n\n    @abstractmethod\n    def resize(self, out_shape, interpolation='nearest'):\n        \"\"\"Resize masks to the given out_shape.\n\n        Args:\n            out_shape: Target (h, w) of resized mask.\n            interpolation (str): See :func:`mmcv.imresize`.\n\n        Returns:\n            BaseInstanceMasks: The resized masks.\n        \"\"\"\n\n    @abstractmethod\n    def flip(self, flip_direction='horizontal'):\n        \"\"\"Flip masks alone the given direction.\n\n        Args:\n            flip_direction (str): Either 'horizontal' or 'vertical'.\n\n        Returns:\n            BaseInstanceMasks: The flipped masks.\n        \"\"\"\n\n    @abstractmethod\n    def pad(self, out_shape, pad_val):\n        \"\"\"Pad masks to the given size of (h, w).\n\n        Args:\n            out_shape (tuple[int]): Target (h, w) of padded mask.\n            pad_val (int): The padded value.\n\n        Returns:\n            BaseInstanceMasks: The padded masks.\n        \"\"\"\n\n    @abstractmethod\n    def crop(self, bbox):\n        \"\"\"Crop each mask by the given bbox.\n\n        Args:\n            bbox (ndarray): Bbox in format [x1, y1, x2, y2], shape (4, ).\n\n        Return:\n            BaseInstanceMasks: The cropped masks.\n        \"\"\"\n\n    @abstractmethod\n    def crop_and_resize(self,\n                        bboxes,\n                        out_shape,\n                        inds,\n                        device,\n                        interpolation='bilinear',\n                        binarize=True):\n        \"\"\"Crop and resize masks by the given bboxes.\n\n        This function is mainly used in mask targets computation.\n        It firstly align mask to bboxes by assigned_inds, then crop mask by the\n        assigned bbox and resize to the size of (mask_h, mask_w)\n\n        Args:\n            bboxes (Tensor): Bboxes in format [x1, y1, x2, y2], shape (N, 4)\n            out_shape (tuple[int]): Target (h, w) of resized mask\n            inds (ndarray): Indexes to assign masks to each bbox,\n                shape (N,) and values should be between [0, num_masks - 1].\n            device (str): Device of bboxes\n            interpolation (str): See `mmcv.imresize`\n            binarize (bool): if True fractional values are rounded to 0 or 1\n                after the resize operation. if False and unsupported an error\n                will be raised. Defaults to True.\n\n        Return:\n            BaseInstanceMasks: the cropped and resized masks.\n        \"\"\"\n\n    @abstractmethod\n    def expand(self, expanded_h, expanded_w, top, left):\n        \"\"\"see :class:`Expand`.\"\"\"\n\n    @property\n    @abstractmethod\n    def areas(self):\n        \"\"\"ndarray: areas of each instance.\"\"\"\n\n    @abstractmethod\n    def to_ndarray(self):\n        \"\"\"Convert masks to the format of ndarray.\n\n        Return:\n            ndarray: Converted masks in the format of ndarray.\n        \"\"\"\n\n    @abstractmethod\n    def to_tensor(self, dtype, device):\n        \"\"\"Convert masks to the format of Tensor.\n\n        Args:\n            dtype (str): Dtype of converted mask.\n            device (torch.device): Device of converted masks.\n\n        Returns:\n            Tensor: Converted masks in the format of Tensor.\n        \"\"\"\n\n    @abstractmethod\n    def translate(self,\n                  out_shape,\n                  offset,\n                  direction='horizontal',\n                  border_value=0,\n                  interpolation='bilinear'):\n        \"\"\"Translate the masks.\n\n        Args:\n            out_shape (tuple[int]): Shape for output mask, format (h, w).\n            offset (int | float): The offset for translate.\n            direction (str): The translate direction, either \"horizontal\"\n                or \"vertical\".\n            border_value (int | float): Border value. Default 0.\n            interpolation (str): Same as :func:`mmcv.imtranslate`.\n\n        Returns:\n            Translated masks.\n        \"\"\"\n\n    def shear(self,\n              out_shape,\n              magnitude,\n              direction='horizontal',\n              border_value=0,\n              interpolation='bilinear'):\n        \"\"\"Shear the masks.\n\n        Args:\n            out_shape (tuple[int]): Shape for output mask, format (h, w).\n            magnitude (int | float): The magnitude used for shear.\n            direction (str): The shear direction, either \"horizontal\"\n                or \"vertical\".\n            border_value (int | tuple[int]): Value used in case of a\n                constant border. Default 0.\n            interpolation (str): Same as in :func:`mmcv.imshear`.\n\n        Returns:\n            ndarray: Sheared masks.\n        \"\"\"\n\n    @abstractmethod\n    def rotate(self, out_shape, angle, center=None, scale=1.0, border_value=0):\n        \"\"\"Rotate the masks.\n\n        Args:\n            out_shape (tuple[int]): Shape for output mask, format (h, w).\n            angle (int | float): Rotation angle in degrees. Positive values\n                mean counter-clockwise rotation.\n            center (tuple[float], optional): Center point (w, h) of the\n                rotation in source image. If not specified, the center of\n                the image will be used.\n            scale (int | float): Isotropic scale factor.\n            border_value (int | float): Border value. Default 0 for masks.\n\n        Returns:\n            Rotated masks.\n        \"\"\"\n\n    def get_bboxes(self, dst_type='hbb'):\n        \"\"\"Get the certain type boxes from masks.\n\n        Please refer to ``mmdet.structures.bbox.box_type`` for more details of\n        the box type.\n\n        Args:\n            dst_type: Destination box type.\n\n        Returns:\n            :obj:`BaseBoxes`: Certain type boxes.\n        \"\"\"\n        from ..bbox import get_box_type\n        _, box_type_cls = get_box_type(dst_type)\n        return box_type_cls.from_instance_masks(self)\n\n    @classmethod\n    @abstractmethod\n    def cat(cls: Type[T], masks: Sequence[T]) -> T:\n        \"\"\"Concatenate a sequence of masks into one single mask instance.\n\n        Args:\n            masks (Sequence[T]): A sequence of mask instances.\n\n        Returns:\n            T: Concatenated mask instance.\n        \"\"\"\n\n\nclass BitmapMasks(BaseInstanceMasks):\n    \"\"\"This class represents masks in the form of bitmaps.\n\n    Args:\n        masks (ndarray): ndarray of masks in shape (N, H, W), where N is\n            the number of objects.\n        height (int): height of masks\n        width (int): width of masks\n\n    Example:\n        >>> from mmdet.data_elements.mask.structures import *  # NOQA\n        >>> num_masks, H, W = 3, 32, 32\n        >>> rng = np.random.RandomState(0)\n        >>> masks = (rng.rand(num_masks, H, W) > 0.1).astype(np.int64)\n        >>> self = BitmapMasks(masks, height=H, width=W)\n\n        >>> # demo crop_and_resize\n        >>> num_boxes = 5\n        >>> bboxes = np.array([[0, 0, 30, 10.0]] * num_boxes)\n        >>> out_shape = (14, 14)\n        >>> inds = torch.randint(0, len(self), size=(num_boxes,))\n        >>> device = 'cpu'\n        >>> interpolation = 'bilinear'\n        >>> new = self.crop_and_resize(\n        ...     bboxes, out_shape, inds, device, interpolation)\n        >>> assert len(new) == num_boxes\n        >>> assert new.height, new.width == out_shape\n    \"\"\"\n\n    def __init__(self, masks, height, width):\n        self.height = height\n        self.width = width\n        if len(masks) == 0:\n            self.masks = np.empty((0, self.height, self.width), dtype=np.uint8)\n        else:\n            assert isinstance(masks, (list, np.ndarray))\n            if isinstance(masks, list):\n                assert isinstance(masks[0], np.ndarray)\n                assert masks[0].ndim == 2  # (H, W)\n            else:\n                assert masks.ndim == 3  # (N, H, W)\n\n            self.masks = np.stack(masks).reshape(-1, height, width)\n            assert self.masks.shape[1] == self.height\n            assert self.masks.shape[2] == self.width\n\n    def __getitem__(self, index):\n        \"\"\"Index the BitmapMask.\n\n        Args:\n            index (int | ndarray): Indices in the format of integer or ndarray.\n\n        Returns:\n            :obj:`BitmapMasks`: Indexed bitmap masks.\n        \"\"\"\n        masks = self.masks[index].reshape(-1, self.height, self.width)\n        return BitmapMasks(masks, self.height, self.width)\n\n    def __iter__(self):\n        return iter(self.masks)\n\n    def __repr__(self):\n        s = self.__class__.__name__ + '('\n        s += f'num_masks={len(self.masks)}, '\n        s += f'height={self.height}, '\n        s += f'width={self.width})'\n        return s\n\n    def __len__(self):\n        \"\"\"Number of masks.\"\"\"\n        return len(self.masks)\n\n    def rescale(self, scale, interpolation='nearest'):\n        \"\"\"See :func:`BaseInstanceMasks.rescale`.\"\"\"\n        if len(self.masks) == 0:\n            new_w, new_h = mmcv.rescale_size((self.width, self.height), scale)\n            rescaled_masks = np.empty((0, new_h, new_w), dtype=np.uint8)\n        else:\n            rescaled_masks = np.stack([\n                mmcv.imrescale(mask, scale, interpolation=interpolation)\n                for mask in self.masks\n            ])\n        height, width = rescaled_masks.shape[1:]\n        return BitmapMasks(rescaled_masks, height, width)\n\n    def resize(self, out_shape, interpolation='nearest'):\n        \"\"\"See :func:`BaseInstanceMasks.resize`.\"\"\"\n        if len(self.masks) == 0:\n            resized_masks = np.empty((0, *out_shape), dtype=np.uint8)\n        else:\n            resized_masks = np.stack([\n                mmcv.imresize(\n                    mask, out_shape[::-1], interpolation=interpolation)\n                for mask in self.masks\n            ])\n        return BitmapMasks(resized_masks, *out_shape)\n\n    def flip(self, flip_direction='horizontal'):\n        \"\"\"See :func:`BaseInstanceMasks.flip`.\"\"\"\n        assert flip_direction in ('horizontal', 'vertical', 'diagonal')\n\n        if len(self.masks) == 0:\n            flipped_masks = self.masks\n        else:\n            flipped_masks = np.stack([\n                mmcv.imflip(mask, direction=flip_direction)\n                for mask in self.masks\n            ])\n        return BitmapMasks(flipped_masks, self.height, self.width)\n\n    def pad(self, out_shape, pad_val=0):\n        \"\"\"See :func:`BaseInstanceMasks.pad`.\"\"\"\n        if len(self.masks) == 0:\n            padded_masks = np.empty((0, *out_shape), dtype=np.uint8)\n        else:\n            padded_masks = np.stack([\n                mmcv.impad(mask, shape=out_shape, pad_val=pad_val)\n                for mask in self.masks\n            ])\n        return BitmapMasks(padded_masks, *out_shape)\n\n    def crop(self, bbox):\n        \"\"\"See :func:`BaseInstanceMasks.crop`.\"\"\"\n        assert isinstance(bbox, np.ndarray)\n        assert bbox.ndim == 1\n\n        # clip the boundary\n        bbox = bbox.copy()\n        bbox[0::2] = np.clip(bbox[0::2], 0, self.width)\n        bbox[1::2] = np.clip(bbox[1::2], 0, self.height)\n        x1, y1, x2, y2 = bbox\n        w = np.maximum(x2 - x1, 1)\n        h = np.maximum(y2 - y1, 1)\n\n        if len(self.masks) == 0:\n            cropped_masks = np.empty((0, h, w), dtype=np.uint8)\n        else:\n            cropped_masks = self.masks[:, y1:y1 + h, x1:x1 + w]\n        return BitmapMasks(cropped_masks, h, w)\n\n    def crop_and_resize(self,\n                        bboxes,\n                        out_shape,\n                        inds,\n                        device='cpu',\n                        interpolation='bilinear',\n                        binarize=True):\n        \"\"\"See :func:`BaseInstanceMasks.crop_and_resize`.\"\"\"\n        if len(self.masks) == 0:\n            empty_masks = np.empty((0, *out_shape), dtype=np.uint8)\n            return BitmapMasks(empty_masks, *out_shape)\n\n        # convert bboxes to tensor\n        if isinstance(bboxes, np.ndarray):\n            bboxes = torch.from_numpy(bboxes).to(device=device)\n        if isinstance(inds, np.ndarray):\n            inds = torch.from_numpy(inds).to(device=device)\n\n        num_bbox = bboxes.shape[0]\n        fake_inds = torch.arange(\n            num_bbox, device=device).to(dtype=bboxes.dtype)[:, None]\n        rois = torch.cat([fake_inds, bboxes], dim=1)  # Nx5\n        rois = rois.to(device=device)\n        if num_bbox > 0:\n            gt_masks_th = torch.from_numpy(self.masks).to(device).index_select(\n                0, inds).to(dtype=rois.dtype)\n            targets = roi_align(gt_masks_th[:, None, :, :], rois, out_shape,\n                                1.0, 0, 'avg', True).squeeze(1)\n            if binarize:\n                resized_masks = (targets >= 0.5).cpu().numpy()\n            else:\n                resized_masks = targets.cpu().numpy()\n        else:\n            resized_masks = []\n        return BitmapMasks(resized_masks, *out_shape)\n\n    def expand(self, expanded_h, expanded_w, top, left):\n        \"\"\"See :func:`BaseInstanceMasks.expand`.\"\"\"\n        if len(self.masks) == 0:\n            expanded_mask = np.empty((0, expanded_h, expanded_w),\n                                     dtype=np.uint8)\n        else:\n            expanded_mask = np.zeros((len(self), expanded_h, expanded_w),\n                                     dtype=np.uint8)\n            expanded_mask[:, top:top + self.height,\n                          left:left + self.width] = self.masks\n        return BitmapMasks(expanded_mask, expanded_h, expanded_w)\n\n    def translate(self,\n                  out_shape,\n                  offset,\n                  direction='horizontal',\n                  border_value=0,\n                  interpolation='bilinear'):\n        \"\"\"Translate the BitmapMasks.\n\n        Args:\n            out_shape (tuple[int]): Shape for output mask, format (h, w).\n            offset (int | float): The offset for translate.\n            direction (str): The translate direction, either \"horizontal\"\n                or \"vertical\".\n            border_value (int | float): Border value. Default 0 for masks.\n            interpolation (str): Same as :func:`mmcv.imtranslate`.\n\n        Returns:\n            BitmapMasks: Translated BitmapMasks.\n\n        Example:\n            >>> from mmdet.data_elements.mask.structures import BitmapMasks\n            >>> self = BitmapMasks.random(dtype=np.uint8)\n            >>> out_shape = (32, 32)\n            >>> offset = 4\n            >>> direction = 'horizontal'\n            >>> border_value = 0\n            >>> interpolation = 'bilinear'\n            >>> # Note, There seem to be issues when:\n            >>> # * the mask dtype is not supported by cv2.AffineWarp\n            >>> new = self.translate(out_shape, offset, direction,\n            >>>                      border_value, interpolation)\n            >>> assert len(new) == len(self)\n            >>> assert new.height, new.width == out_shape\n        \"\"\"\n        if len(self.masks) == 0:\n            translated_masks = np.empty((0, *out_shape), dtype=np.uint8)\n        else:\n            masks = self.masks\n            if masks.shape[-2:] != out_shape:\n                empty_masks = np.zeros((masks.shape[0], *out_shape),\n                                       dtype=masks.dtype)\n                min_h = min(out_shape[0], masks.shape[1])\n                min_w = min(out_shape[1], masks.shape[2])\n                empty_masks[:, :min_h, :min_w] = masks[:, :min_h, :min_w]\n                masks = empty_masks\n            translated_masks = mmcv.imtranslate(\n                masks.transpose((1, 2, 0)),\n                offset,\n                direction,\n                border_value=border_value,\n                interpolation=interpolation)\n            if translated_masks.ndim == 2:\n                translated_masks = translated_masks[:, :, None]\n            translated_masks = translated_masks.transpose(\n                (2, 0, 1)).astype(self.masks.dtype)\n        return BitmapMasks(translated_masks, *out_shape)\n\n    def shear(self,\n              out_shape,\n              magnitude,\n              direction='horizontal',\n              border_value=0,\n              interpolation='bilinear'):\n        \"\"\"Shear the BitmapMasks.\n\n        Args:\n            out_shape (tuple[int]): Shape for output mask, format (h, w).\n            magnitude (int | float): The magnitude used for shear.\n            direction (str): The shear direction, either \"horizontal\"\n                or \"vertical\".\n            border_value (int | tuple[int]): Value used in case of a\n                constant border.\n            interpolation (str): Same as in :func:`mmcv.imshear`.\n\n        Returns:\n            BitmapMasks: The sheared masks.\n        \"\"\"\n        if len(self.masks) == 0:\n            sheared_masks = np.empty((0, *out_shape), dtype=np.uint8)\n        else:\n            sheared_masks = mmcv.imshear(\n                self.masks.transpose((1, 2, 0)),\n                magnitude,\n                direction,\n                border_value=border_value,\n                interpolation=interpolation)\n            if sheared_masks.ndim == 2:\n                sheared_masks = sheared_masks[:, :, None]\n            sheared_masks = sheared_masks.transpose(\n                (2, 0, 1)).astype(self.masks.dtype)\n        return BitmapMasks(sheared_masks, *out_shape)\n\n    def rotate(self,\n               out_shape,\n               angle,\n               center=None,\n               scale=1.0,\n               border_value=0,\n               interpolation='bilinear'):\n        \"\"\"Rotate the BitmapMasks.\n\n        Args:\n            out_shape (tuple[int]): Shape for output mask, format (h, w).\n            angle (int | float): Rotation angle in degrees. Positive values\n                mean counter-clockwise rotation.\n            center (tuple[float], optional): Center point (w, h) of the\n                rotation in source image. If not specified, the center of\n                the image will be used.\n            scale (int | float): Isotropic scale factor.\n            border_value (int | float): Border value. Default 0 for masks.\n            interpolation (str): Same as in :func:`mmcv.imrotate`.\n\n        Returns:\n            BitmapMasks: Rotated BitmapMasks.\n        \"\"\"\n        if len(self.masks) == 0:\n            rotated_masks = np.empty((0, *out_shape), dtype=self.masks.dtype)\n        else:\n            rotated_masks = mmcv.imrotate(\n                self.masks.transpose((1, 2, 0)),\n                angle,\n                center=center,\n                scale=scale,\n                border_value=border_value,\n                interpolation=interpolation)\n            if rotated_masks.ndim == 2:\n                # case when only one mask, (h, w)\n                rotated_masks = rotated_masks[:, :, None]  # (h, w, 1)\n            rotated_masks = rotated_masks.transpose(\n                (2, 0, 1)).astype(self.masks.dtype)\n        return BitmapMasks(rotated_masks, *out_shape)\n\n    @property\n    def areas(self):\n        \"\"\"See :py:attr:`BaseInstanceMasks.areas`.\"\"\"\n        return self.masks.sum((1, 2))\n\n    def to_ndarray(self):\n        \"\"\"See :func:`BaseInstanceMasks.to_ndarray`.\"\"\"\n        return self.masks\n\n    def to_tensor(self, dtype, device):\n        \"\"\"See :func:`BaseInstanceMasks.to_tensor`.\"\"\"\n        return torch.tensor(self.masks, dtype=dtype, device=device)\n\n    @classmethod\n    def random(cls,\n               num_masks=3,\n               height=32,\n               width=32,\n               dtype=np.uint8,\n               rng=None):\n        \"\"\"Generate random bitmap masks for demo / testing purposes.\n\n        Example:\n            >>> from mmdet.data_elements.mask.structures import BitmapMasks\n            >>> self = BitmapMasks.random()\n            >>> print('self = {}'.format(self))\n            self = BitmapMasks(num_masks=3, height=32, width=32)\n        \"\"\"\n        from mmdet.utils.util_random import ensure_rng\n        rng = ensure_rng(rng)\n        masks = (rng.rand(num_masks, height, width) > 0.1).astype(dtype)\n        self = cls(masks, height=height, width=width)\n        return self\n\n    @classmethod\n    def cat(cls: Type[T], masks: Sequence[T]) -> T:\n        \"\"\"Concatenate a sequence of masks into one single mask instance.\n\n        Args:\n            masks (Sequence[BitmapMasks]): A sequence of mask instances.\n\n        Returns:\n            BitmapMasks: Concatenated mask instance.\n        \"\"\"\n        assert isinstance(masks, Sequence)\n        if len(masks) == 0:\n            raise ValueError('masks should not be an empty list.')\n        assert all(isinstance(m, cls) for m in masks)\n\n        mask_array = np.concatenate([m.masks for m in masks], axis=0)\n        return cls(mask_array, *mask_array.shape[1:])\n\n\nclass PolygonMasks(BaseInstanceMasks):\n    \"\"\"This class represents masks in the form of polygons.\n\n    Polygons is a list of three levels. The first level of the list\n    corresponds to objects, the second level to the polys that compose the\n    object, the third level to the poly coordinates\n\n    Args:\n        masks (list[list[ndarray]]): The first level of the list\n            corresponds to objects, the second level to the polys that\n            compose the object, the third level to the poly coordinates\n        height (int): height of masks\n        width (int): width of masks\n\n    Example:\n        >>> from mmdet.data_elements.mask.structures import *  # NOQA\n        >>> masks = [\n        >>>     [ np.array([0, 0, 10, 0, 10, 10., 0, 10, 0, 0]) ]\n        >>> ]\n        >>> height, width = 16, 16\n        >>> self = PolygonMasks(masks, height, width)\n\n        >>> # demo translate\n        >>> new = self.translate((16, 16), 4., direction='horizontal')\n        >>> assert np.all(new.masks[0][0][1::2] == masks[0][0][1::2])\n        >>> assert np.all(new.masks[0][0][0::2] == masks[0][0][0::2] + 4)\n\n        >>> # demo crop_and_resize\n        >>> num_boxes = 3\n        >>> bboxes = np.array([[0, 0, 30, 10.0]] * num_boxes)\n        >>> out_shape = (16, 16)\n        >>> inds = torch.randint(0, len(self), size=(num_boxes,))\n        >>> device = 'cpu'\n        >>> interpolation = 'bilinear'\n        >>> new = self.crop_and_resize(\n        ...     bboxes, out_shape, inds, device, interpolation)\n        >>> assert len(new) == num_boxes\n        >>> assert new.height, new.width == out_shape\n    \"\"\"\n\n    def __init__(self, masks, height, width):\n        assert isinstance(masks, list)\n        if len(masks) > 0:\n            assert isinstance(masks[0], list)\n            assert isinstance(masks[0][0], np.ndarray)\n\n        self.height = height\n        self.width = width\n        self.masks = masks\n\n    def __getitem__(self, index):\n        \"\"\"Index the polygon masks.\n\n        Args:\n            index (ndarray | List): The indices.\n\n        Returns:\n            :obj:`PolygonMasks`: The indexed polygon masks.\n        \"\"\"\n        if isinstance(index, np.ndarray):\n            if index.dtype == bool:\n                index = np.where(index)[0].tolist()\n            else:\n                index = index.tolist()\n        if isinstance(index, list):\n            masks = [self.masks[i] for i in index]\n        else:\n            try:\n                masks = self.masks[index]\n            except Exception:\n                raise ValueError(\n                    f'Unsupported input of type {type(index)} for indexing!')\n        if len(masks) and isinstance(masks[0], np.ndarray):\n            masks = [masks]  # ensure a list of three levels\n        return PolygonMasks(masks, self.height, self.width)\n\n    def __iter__(self):\n        return iter(self.masks)\n\n    def __repr__(self):\n        s = self.__class__.__name__ + '('\n        s += f'num_masks={len(self.masks)}, '\n        s += f'height={self.height}, '\n        s += f'width={self.width})'\n        return s\n\n    def __len__(self):\n        \"\"\"Number of masks.\"\"\"\n        return len(self.masks)\n\n    def rescale(self, scale, interpolation=None):\n        \"\"\"see :func:`BaseInstanceMasks.rescale`\"\"\"\n        new_w, new_h = mmcv.rescale_size((self.width, self.height), scale)\n        if len(self.masks) == 0:\n            rescaled_masks = PolygonMasks([], new_h, new_w)\n        else:\n            rescaled_masks = self.resize((new_h, new_w))\n        return rescaled_masks\n\n    def resize(self, out_shape, interpolation=None):\n        \"\"\"see :func:`BaseInstanceMasks.resize`\"\"\"\n        if len(self.masks) == 0:\n            resized_masks = PolygonMasks([], *out_shape)\n        else:\n            h_scale = out_shape[0] / self.height\n            w_scale = out_shape[1] / self.width\n            resized_masks = []\n            for poly_per_obj in self.masks:\n                resized_poly = []\n                for p in poly_per_obj:\n                    p = p.copy()\n                    p[0::2] = p[0::2] * w_scale\n                    p[1::2] = p[1::2] * h_scale\n                    resized_poly.append(p)\n                resized_masks.append(resized_poly)\n            resized_masks = PolygonMasks(resized_masks, *out_shape)\n        return resized_masks\n\n    def flip(self, flip_direction='horizontal'):\n        \"\"\"see :func:`BaseInstanceMasks.flip`\"\"\"\n        assert flip_direction in ('horizontal', 'vertical', 'diagonal')\n        if len(self.masks) == 0:\n            flipped_masks = PolygonMasks([], self.height, self.width)\n        else:\n            flipped_masks = []\n            for poly_per_obj in self.masks:\n                flipped_poly_per_obj = []\n                for p in poly_per_obj:\n                    p = p.copy()\n                    if flip_direction == 'horizontal':\n                        p[0::2] = self.width - p[0::2]\n                    elif flip_direction == 'vertical':\n                        p[1::2] = self.height - p[1::2]\n                    else:\n                        p[0::2] = self.width - p[0::2]\n                        p[1::2] = self.height - p[1::2]\n                    flipped_poly_per_obj.append(p)\n                flipped_masks.append(flipped_poly_per_obj)\n            flipped_masks = PolygonMasks(flipped_masks, self.height,\n                                         self.width)\n        return flipped_masks\n\n    def crop(self, bbox):\n        \"\"\"see :func:`BaseInstanceMasks.crop`\"\"\"\n        assert isinstance(bbox, np.ndarray)\n        assert bbox.ndim == 1\n\n        # clip the boundary\n        bbox = bbox.copy()\n        bbox[0::2] = np.clip(bbox[0::2], 0, self.width)\n        bbox[1::2] = np.clip(bbox[1::2], 0, self.height)\n        x1, y1, x2, y2 = bbox\n        w = np.maximum(x2 - x1, 1)\n        h = np.maximum(y2 - y1, 1)\n\n        if len(self.masks) == 0:\n            cropped_masks = PolygonMasks([], h, w)\n        else:\n            # reference: https://github.com/facebookresearch/fvcore/blob/main/fvcore/transforms/transform.py  # noqa\n            crop_box = geometry.box(x1, y1, x2, y2).buffer(0.0)\n            cropped_masks = []\n            # suppress shapely warnings util it incorporates GEOS>=3.11.2\n            # reference: https://github.com/shapely/shapely/issues/1345\n            initial_settings = np.seterr()\n            np.seterr(invalid='ignore')\n            for poly_per_obj in self.masks:\n                cropped_poly_per_obj = []\n                for p in poly_per_obj:\n                    p = p.copy()\n                    p = geometry.Polygon(p.reshape(-1, 2)).buffer(0.0)\n                    # polygon must be valid to perform intersection.\n                    if not p.is_valid:\n                        continue\n                    cropped = p.intersection(crop_box)\n                    if cropped.is_empty:\n                        continue\n                    if isinstance(cropped,\n                                  geometry.collection.BaseMultipartGeometry):\n                        cropped = cropped.geoms\n                    else:\n                        cropped = [cropped]\n                    # one polygon may be cropped to multiple ones\n                    for poly in cropped:\n                        # ignore lines or points\n                        if not isinstance(\n                                poly, geometry.Polygon) or not poly.is_valid:\n                            continue\n                        coords = np.asarray(poly.exterior.coords)\n                        # remove an extra identical vertex at the end\n                        coords = coords[:-1]\n                        coords[:, 0] -= x1\n                        coords[:, 1] -= y1\n                        cropped_poly_per_obj.append(coords.reshape(-1))\n                # a dummy polygon to avoid misalignment between masks and boxes\n                if len(cropped_poly_per_obj) == 0:\n                    cropped_poly_per_obj = [np.array([0, 0, 0, 0, 0, 0])]\n                cropped_masks.append(cropped_poly_per_obj)\n            np.seterr(**initial_settings)\n            cropped_masks = PolygonMasks(cropped_masks, h, w)\n        return cropped_masks\n\n    def pad(self, out_shape, pad_val=0):\n        \"\"\"padding has no effect on polygons`\"\"\"\n        return PolygonMasks(self.masks, *out_shape)\n\n    def expand(self, *args, **kwargs):\n        \"\"\"TODO: Add expand for polygon\"\"\"\n        raise NotImplementedError\n\n    def crop_and_resize(self,\n                        bboxes,\n                        out_shape,\n                        inds,\n                        device='cpu',\n                        interpolation='bilinear',\n                        binarize=True):\n        \"\"\"see :func:`BaseInstanceMasks.crop_and_resize`\"\"\"\n        out_h, out_w = out_shape\n        if len(self.masks) == 0:\n            return PolygonMasks([], out_h, out_w)\n\n        if not binarize:\n            raise ValueError('Polygons are always binary, '\n                             'setting binarize=False is unsupported')\n\n        resized_masks = []\n        for i in range(len(bboxes)):\n            mask = self.masks[inds[i]]\n            bbox = bboxes[i, :]\n            x1, y1, x2, y2 = bbox\n            w = np.maximum(x2 - x1, 1)\n            h = np.maximum(y2 - y1, 1)\n            h_scale = out_h / max(h, 0.1)  # avoid too large scale\n            w_scale = out_w / max(w, 0.1)\n\n            resized_mask = []\n            for p in mask:\n                p = p.copy()\n                # crop\n                # pycocotools will clip the boundary\n                p[0::2] = p[0::2] - bbox[0]\n                p[1::2] = p[1::2] - bbox[1]\n\n                # resize\n                p[0::2] = p[0::2] * w_scale\n                p[1::2] = p[1::2] * h_scale\n                resized_mask.append(p)\n            resized_masks.append(resized_mask)\n        return PolygonMasks(resized_masks, *out_shape)\n\n    def translate(self,\n                  out_shape,\n                  offset,\n                  direction='horizontal',\n                  border_value=None,\n                  interpolation=None):\n        \"\"\"Translate the PolygonMasks.\n\n        Example:\n            >>> self = PolygonMasks.random(dtype=np.int64)\n            >>> out_shape = (self.height, self.width)\n            >>> new = self.translate(out_shape, 4., direction='horizontal')\n            >>> assert np.all(new.masks[0][0][1::2] == self.masks[0][0][1::2])\n            >>> assert np.all(new.masks[0][0][0::2] == self.masks[0][0][0::2] + 4)  # noqa: E501\n        \"\"\"\n        assert border_value is None or border_value == 0, \\\n            'Here border_value is not '\\\n            f'used, and defaultly should be None or 0. got {border_value}.'\n        if len(self.masks) == 0:\n            translated_masks = PolygonMasks([], *out_shape)\n        else:\n            translated_masks = []\n            for poly_per_obj in self.masks:\n                translated_poly_per_obj = []\n                for p in poly_per_obj:\n                    p = p.copy()\n                    if direction == 'horizontal':\n                        p[0::2] = np.clip(p[0::2] + offset, 0, out_shape[1])\n                    elif direction == 'vertical':\n                        p[1::2] = np.clip(p[1::2] + offset, 0, out_shape[0])\n                    translated_poly_per_obj.append(p)\n                translated_masks.append(translated_poly_per_obj)\n            translated_masks = PolygonMasks(translated_masks, *out_shape)\n        return translated_masks\n\n    def shear(self,\n              out_shape,\n              magnitude,\n              direction='horizontal',\n              border_value=0,\n              interpolation='bilinear'):\n        \"\"\"See :func:`BaseInstanceMasks.shear`.\"\"\"\n        if len(self.masks) == 0:\n            sheared_masks = PolygonMasks([], *out_shape)\n        else:\n            sheared_masks = []\n            if direction == 'horizontal':\n                shear_matrix = np.stack([[1, magnitude],\n                                         [0, 1]]).astype(np.float32)\n            elif direction == 'vertical':\n                shear_matrix = np.stack([[1, 0], [magnitude,\n                                                  1]]).astype(np.float32)\n            for poly_per_obj in self.masks:\n                sheared_poly = []\n                for p in poly_per_obj:\n                    p = np.stack([p[0::2], p[1::2]], axis=0)  # [2, n]\n                    new_coords = np.matmul(shear_matrix, p)  # [2, n]\n                    new_coords[0, :] = np.clip(new_coords[0, :], 0,\n                                               out_shape[1])\n                    new_coords[1, :] = np.clip(new_coords[1, :], 0,\n                                               out_shape[0])\n                    sheared_poly.append(\n                        new_coords.transpose((1, 0)).reshape(-1))\n                sheared_masks.append(sheared_poly)\n            sheared_masks = PolygonMasks(sheared_masks, *out_shape)\n        return sheared_masks\n\n    def rotate(self,\n               out_shape,\n               angle,\n               center=None,\n               scale=1.0,\n               border_value=0,\n               interpolation='bilinear'):\n        \"\"\"See :func:`BaseInstanceMasks.rotate`.\"\"\"\n        if len(self.masks) == 0:\n            rotated_masks = PolygonMasks([], *out_shape)\n        else:\n            rotated_masks = []\n            rotate_matrix = cv2.getRotationMatrix2D(center, -angle, scale)\n            for poly_per_obj in self.masks:\n                rotated_poly = []\n                for p in poly_per_obj:\n                    p = p.copy()\n                    coords = np.stack([p[0::2], p[1::2]], axis=1)  # [n, 2]\n                    # pad 1 to convert from format [x, y] to homogeneous\n                    # coordinates format [x, y, 1]\n                    coords = np.concatenate(\n                        (coords, np.ones((coords.shape[0], 1), coords.dtype)),\n                        axis=1)  # [n, 3]\n                    rotated_coords = np.matmul(\n                        rotate_matrix[None, :, :],\n                        coords[:, :, None])[..., 0]  # [n, 2, 1] -> [n, 2]\n                    rotated_coords[:, 0] = np.clip(rotated_coords[:, 0], 0,\n                                                   out_shape[1])\n                    rotated_coords[:, 1] = np.clip(rotated_coords[:, 1], 0,\n                                                   out_shape[0])\n                    rotated_poly.append(rotated_coords.reshape(-1))\n                rotated_masks.append(rotated_poly)\n            rotated_masks = PolygonMasks(rotated_masks, *out_shape)\n        return rotated_masks\n\n    def to_bitmap(self):\n        \"\"\"convert polygon masks to bitmap masks.\"\"\"\n        bitmap_masks = self.to_ndarray()\n        return BitmapMasks(bitmap_masks, self.height, self.width)\n\n    @property\n    def areas(self):\n        \"\"\"Compute areas of masks.\n\n        This func is modified from `detectron2\n        <https://github.com/facebookresearch/detectron2/blob/ffff8acc35ea88ad1cb1806ab0f00b4c1c5dbfd9/detectron2/structures/masks.py#L387>`_.\n        The function only works with Polygons using the shoelace formula.\n\n        Return:\n            ndarray: areas of each instance\n        \"\"\"  # noqa: W501\n        area = []\n        for polygons_per_obj in self.masks:\n            area_per_obj = 0\n            for p in polygons_per_obj:\n                area_per_obj += self._polygon_area(p[0::2], p[1::2])\n            area.append(area_per_obj)\n        return np.asarray(area)\n\n    def _polygon_area(self, x, y):\n        \"\"\"Compute the area of a component of a polygon.\n\n        Using the shoelace formula:\n        https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates\n\n        Args:\n            x (ndarray): x coordinates of the component\n            y (ndarray): y coordinates of the component\n\n        Return:\n            float: the are of the component\n        \"\"\"  # noqa: 501\n        return 0.5 * np.abs(\n            np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))\n\n    def to_ndarray(self):\n        \"\"\"Convert masks to the format of ndarray.\"\"\"\n        if len(self.masks) == 0:\n            return np.empty((0, self.height, self.width), dtype=np.uint8)\n        bitmap_masks = []\n        for poly_per_obj in self.masks:\n            bitmap_masks.append(\n                polygon_to_bitmap(poly_per_obj, self.height, self.width))\n        return np.stack(bitmap_masks)\n\n    def to_tensor(self, dtype, device):\n        \"\"\"See :func:`BaseInstanceMasks.to_tensor`.\"\"\"\n        if len(self.masks) == 0:\n            return torch.empty((0, self.height, self.width),\n                               dtype=dtype,\n                               device=device)\n        ndarray_masks = self.to_ndarray()\n        return torch.tensor(ndarray_masks, dtype=dtype, device=device)\n\n    @classmethod\n    def random(cls,\n               num_masks=3,\n               height=32,\n               width=32,\n               n_verts=5,\n               dtype=np.float32,\n               rng=None):\n        \"\"\"Generate random polygon masks for demo / testing purposes.\n\n        Adapted from [1]_\n\n        References:\n            .. [1] https://gitlab.kitware.com/computer-vision/kwimage/-/blob/928cae35ca8/kwimage/structs/polygon.py#L379  # noqa: E501\n\n        Example:\n            >>> from mmdet.data_elements.mask.structures import PolygonMasks\n            >>> self = PolygonMasks.random()\n            >>> print('self = {}'.format(self))\n        \"\"\"\n        from mmdet.utils.util_random import ensure_rng\n        rng = ensure_rng(rng)\n\n        def _gen_polygon(n, irregularity, spikeyness):\n            \"\"\"Creates the polygon by sampling points on a circle around the\n            centre.  Random noise is added by varying the angular spacing\n            between sequential points, and by varying the radial distance of\n            each point from the centre.\n\n            Based on original code by Mike Ounsworth\n\n            Args:\n                n (int): number of vertices\n                irregularity (float): [0,1] indicating how much variance there\n                    is in the angular spacing of vertices. [0,1] will map to\n                    [0, 2pi/numberOfVerts]\n                spikeyness (float): [0,1] indicating how much variance there is\n                    in each vertex from the circle of radius aveRadius. [0,1]\n                    will map to [0, aveRadius]\n\n            Returns:\n                a list of vertices, in CCW order.\n            \"\"\"\n            from scipy.stats import truncnorm\n\n            # Generate around the unit circle\n            cx, cy = (0.0, 0.0)\n            radius = 1\n\n            tau = np.pi * 2\n\n            irregularity = np.clip(irregularity, 0, 1) * 2 * np.pi / n\n            spikeyness = np.clip(spikeyness, 1e-9, 1)\n\n            # generate n angle steps\n            lower = (tau / n) - irregularity\n            upper = (tau / n) + irregularity\n            angle_steps = rng.uniform(lower, upper, n)\n\n            # normalize the steps so that point 0 and point n+1 are the same\n            k = angle_steps.sum() / (2 * np.pi)\n            angles = (angle_steps / k).cumsum() + rng.uniform(0, tau)\n\n            # Convert high and low values to be wrt the standard normal range\n            # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.truncnorm.html\n            low = 0\n            high = 2 * radius\n            mean = radius\n            std = spikeyness\n            a = (low - mean) / std\n            b = (high - mean) / std\n            tnorm = truncnorm(a=a, b=b, loc=mean, scale=std)\n\n            # now generate the points\n            radii = tnorm.rvs(n, random_state=rng)\n            x_pts = cx + radii * np.cos(angles)\n            y_pts = cy + radii * np.sin(angles)\n\n            points = np.hstack([x_pts[:, None], y_pts[:, None]])\n\n            # Scale to 0-1 space\n            points = points - points.min(axis=0)\n            points = points / points.max(axis=0)\n\n            # Randomly place within 0-1 space\n            points = points * (rng.rand() * .8 + .2)\n            min_pt = points.min(axis=0)\n            max_pt = points.max(axis=0)\n\n            high = (1 - max_pt)\n            low = (0 - min_pt)\n            offset = (rng.rand(2) * (high - low)) + low\n            points = points + offset\n            return points\n\n        def _order_vertices(verts):\n            \"\"\"\n            References:\n                https://stackoverflow.com/questions/1709283/how-can-i-sort-a-coordinate-list-for-a-rectangle-counterclockwise\n            \"\"\"\n            mlat = verts.T[0].sum() / len(verts)\n            mlng = verts.T[1].sum() / len(verts)\n\n            tau = np.pi * 2\n            angle = (np.arctan2(mlat - verts.T[0], verts.T[1] - mlng) +\n                     tau) % tau\n            sortx = angle.argsort()\n            verts = verts.take(sortx, axis=0)\n            return verts\n\n        # Generate a random exterior for each requested mask\n        masks = []\n        for _ in range(num_masks):\n            exterior = _order_vertices(_gen_polygon(n_verts, 0.9, 0.9))\n            exterior = (exterior * [(width, height)]).astype(dtype)\n            masks.append([exterior.ravel()])\n\n        self = cls(masks, height, width)\n        return self\n\n    @classmethod\n    def cat(cls: Type[T], masks: Sequence[T]) -> T:\n        \"\"\"Concatenate a sequence of masks into one single mask instance.\n\n        Args:\n            masks (Sequence[PolygonMasks]): A sequence of mask instances.\n\n        Returns:\n            PolygonMasks: Concatenated mask instance.\n        \"\"\"\n        assert isinstance(masks, Sequence)\n        if len(masks) == 0:\n            raise ValueError('masks should not be an empty list.')\n        assert all(isinstance(m, cls) for m in masks)\n\n        mask_list = list(itertools.chain(*[m.masks for m in masks]))\n        return cls(mask_list, masks[0].height, masks[0].width)\n\n\ndef polygon_to_bitmap(polygons, height, width):\n    \"\"\"Convert masks from the form of polygons to bitmaps.\n\n    Args:\n        polygons (list[ndarray]): masks in polygon representation\n        height (int): mask height\n        width (int): mask width\n\n    Return:\n        ndarray: the converted masks in bitmap representation\n    \"\"\"\n    rles = maskUtils.frPyObjects(polygons, height, width)\n    rle = maskUtils.merge(rles)\n    bitmap_mask = maskUtils.decode(rle).astype(bool)\n    return bitmap_mask\n\n\ndef bitmap_to_polygon(bitmap):\n    \"\"\"Convert masks from the form of bitmaps to polygons.\n\n    Args:\n        bitmap (ndarray): masks in bitmap representation.\n\n    Return:\n        list[ndarray]: the converted mask in polygon representation.\n        bool: whether the mask has holes.\n    \"\"\"\n    bitmap = np.ascontiguousarray(bitmap).astype(np.uint8)\n    # cv2.RETR_CCOMP: retrieves all of the contours and organizes them\n    #   into a two-level hierarchy. At the top level, there are external\n    #   boundaries of the components. At the second level, there are\n    #   boundaries of the holes. If there is another contour inside a hole\n    #   of a connected component, it is still put at the top level.\n    # cv2.CHAIN_APPROX_NONE: stores absolutely all the contour points.\n    outs = cv2.findContours(bitmap, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)\n    contours = outs[-2]\n    hierarchy = outs[-1]\n    if hierarchy is None:\n        return [], False\n    # hierarchy[i]: 4 elements, for the indexes of next, previous,\n    # parent, or nested contours. If there is no corresponding contour,\n    # it will be -1.\n    with_hole = (hierarchy.reshape(-1, 4)[:, 3] >= 0).any()\n    contours = [c.reshape(-1, 2) for c in contours]\n    return contours, with_hole\n"
  },
  {
    "path": "mmdet/structures/mask/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport pycocotools.mask as mask_util\nimport torch\nfrom mmengine.utils import slice_list\n\n\ndef split_combined_polys(polys, poly_lens, polys_per_mask):\n    \"\"\"Split the combined 1-D polys into masks.\n\n    A mask is represented as a list of polys, and a poly is represented as\n    a 1-D array. In dataset, all masks are concatenated into a single 1-D\n    tensor. Here we need to split the tensor into original representations.\n\n    Args:\n        polys (list): a list (length = image num) of 1-D tensors\n        poly_lens (list): a list (length = image num) of poly length\n        polys_per_mask (list): a list (length = image num) of poly number\n            of each mask\n\n    Returns:\n        list: a list (length = image num) of list (length = mask num) of \\\n            list (length = poly num) of numpy array.\n    \"\"\"\n    mask_polys_list = []\n    for img_id in range(len(polys)):\n        polys_single = polys[img_id]\n        polys_lens_single = poly_lens[img_id].tolist()\n        polys_per_mask_single = polys_per_mask[img_id].tolist()\n\n        split_polys = slice_list(polys_single, polys_lens_single)\n        mask_polys = slice_list(split_polys, polys_per_mask_single)\n        mask_polys_list.append(mask_polys)\n    return mask_polys_list\n\n\n# TODO: move this function to more proper place\ndef encode_mask_results(mask_results):\n    \"\"\"Encode bitmap mask to RLE code.\n\n    Args:\n        mask_results (list): bitmap mask results.\n\n    Returns:\n        list | tuple: RLE encoded mask.\n    \"\"\"\n    encoded_mask_results = []\n    for mask in mask_results:\n        encoded_mask_results.append(\n            mask_util.encode(\n                np.array(mask[:, :, np.newaxis], order='F',\n                         dtype='uint8'))[0])  # encoded with RLE\n    return encoded_mask_results\n\n\ndef mask2bbox(masks):\n    \"\"\"Obtain tight bounding boxes of binary masks.\n\n    Args:\n        masks (Tensor): Binary mask of shape (n, h, w).\n\n    Returns:\n        Tensor: Bboxe with shape (n, 4) of \\\n            positive region in binary mask.\n    \"\"\"\n    N = masks.shape[0]\n    bboxes = masks.new_zeros((N, 4), dtype=torch.float32)\n    x_any = torch.any(masks, dim=1)\n    y_any = torch.any(masks, dim=2)\n    for i in range(N):\n        x = torch.where(x_any[i, :])[0]\n        y = torch.where(y_any[i, :])[0]\n        if len(x) > 0 and len(y) > 0:\n            bboxes[i, :] = bboxes.new_tensor(\n                [x[0], y[0], x[-1] + 1, y[-1] + 1])\n\n    return bboxes\n"
  },
  {
    "path": "mmdet/structures/reid_data_sample.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom numbers import Number\nfrom typing import Sequence, Union\n\nimport mmengine\nimport numpy as np\nimport torch\nfrom mmengine.structures import BaseDataElement, LabelData\n\n\ndef format_label(value: Union[torch.Tensor, np.ndarray, Sequence, int],\n                 num_classes: int = None) -> LabelData:\n    \"\"\"Convert label of various python types to :obj:`mmengine.LabelData`.\n\n    Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`,\n    :class:`Sequence`, :class:`int`.\n\n    Args:\n        value (torch.Tensor | numpy.ndarray | Sequence | int): Label value.\n        num_classes (int, optional): The number of classes. If not None, set\n            it to the metainfo. Defaults to None.\n\n    Returns:\n        :obj:`mmengine.LabelData`: The foramtted label data.\n    \"\"\"\n\n    # Handle single number\n    if isinstance(value, (torch.Tensor, np.ndarray)) and value.ndim == 0:\n        value = int(value.item())\n\n    if isinstance(value, np.ndarray):\n        value = torch.from_numpy(value)\n    elif isinstance(value, Sequence) and not mmengine.utils.is_str(value):\n        value = torch.tensor(value)\n    elif isinstance(value, int):\n        value = torch.LongTensor([value])\n    elif not isinstance(value, torch.Tensor):\n        raise TypeError(f'Type {type(value)} is not an available label type.')\n\n    metainfo = {}\n    if num_classes is not None:\n        metainfo['num_classes'] = num_classes\n        if value.max() >= num_classes:\n            raise ValueError(f'The label data ({value}) should not '\n                             f'exceed num_classes ({num_classes}).')\n    label = LabelData(label=value, metainfo=metainfo)\n    return label\n\n\nclass ReIDDataSample(BaseDataElement):\n    \"\"\"A data structure interface of ReID task.\n\n    It's used as interfaces between different components.\n\n    Meta field:\n        img_shape (Tuple): The shape of the corresponding input image.\n            Used for visualization.\n        ori_shape (Tuple): The original shape of the corresponding image.\n            Used for visualization.\n        num_classes (int): The number of all categories.\n            Used for label format conversion.\n\n    Data field:\n        gt_label (LabelData): The ground truth label.\n        pred_label (LabelData): The predicted label.\n        scores (torch.Tensor): The outputs of model.\n    \"\"\"\n\n    @property\n    def gt_label(self):\n        return self._gt_label\n\n    @gt_label.setter\n    def gt_label(self, value: LabelData):\n        self.set_field(value, '_gt_label', dtype=LabelData)\n\n    @gt_label.deleter\n    def gt_label(self):\n        del self._gt_label\n\n    def set_gt_label(\n        self, value: Union[np.ndarray, torch.Tensor, Sequence[Number], Number]\n    ) -> 'ReIDDataSample':\n        \"\"\"Set label of ``gt_label``.\"\"\"\n        label = format_label(value, self.get('num_classes'))\n        if 'gt_label' in self:  # setting for the second time\n            self.gt_label.label = label.label\n        else:  # setting for the first time\n            self.gt_label = label\n        return self\n\n    def set_gt_score(self, value: torch.Tensor) -> 'ReIDDataSample':\n        \"\"\"Set score of ``gt_label``.\"\"\"\n        assert isinstance(value, torch.Tensor), \\\n            f'The value should be a torch.Tensor but got {type(value)}.'\n        assert value.ndim == 1, \\\n            f'The dims of value should be 1, but got {value.ndim}.'\n\n        if 'num_classes' in self:\n            assert value.size(0) == self.num_classes, \\\n                f\"The length of value ({value.size(0)}) doesn't \"\\\n                f'match the num_classes ({self.num_classes}).'\n            metainfo = {'num_classes': self.num_classes}\n        else:\n            metainfo = {'num_classes': value.size(0)}\n\n        if 'gt_label' in self:  # setting for the second time\n            self.gt_label.score = value\n        else:  # setting for the first time\n            self.gt_label = LabelData(score=value, metainfo=metainfo)\n        return self\n\n    @property\n    def pred_feature(self):\n        return self._pred_feature\n\n    @pred_feature.setter\n    def pred_feature(self, value: torch.Tensor):\n        self.set_field(value, '_pred_feature', dtype=torch.Tensor)\n\n    @pred_feature.deleter\n    def pred_feature(self):\n        del self._pred_feature\n"
  },
  {
    "path": "mmdet/structures/track_data_sample.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Sequence\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import BaseDataElement\n\nfrom .det_data_sample import DetDataSample\n\n\nclass TrackDataSample(BaseDataElement):\n    \"\"\"A data structure interface of tracking task in MMDetection. It is used\n    as interfaces between different components.\n\n    This data structure can be viewd as a wrapper of multiple DetDataSample to\n    some extent. Specifically, it only contains a property:\n    ``video_data_samples`` which is a list of DetDataSample, each of which\n    corresponds to a single frame. If you want to get the property of a single\n    frame, you must first get the corresponding ``DetDataSample`` by indexing\n    and then get the property of the frame, such as ``gt_instances``,\n    ``pred_instances`` and so on. As for metainfo, it differs from\n    ``DetDataSample`` in that each value corresponds to the metainfo key is a\n    list where each element corresponds to information of a single frame.\n\n    Examples:\n        >>> import torch\n        >>> from mmengine.structures import InstanceData\n        >>> from mmdet.structures import DetDataSample, TrackDataSample\n        >>> track_data_sample = TrackDataSample()\n        >>> # set the 1st frame\n        >>> frame1_data_sample = DetDataSample(metainfo=dict(\n        ...         img_shape=(100, 100), frame_id=0))\n        >>> frame1_gt_instances = InstanceData()\n        >>> frame1_gt_instances.bbox = torch.zeros([2, 4])\n        >>> frame1_data_sample.gt_instances = frame1_gt_instances\n        >>> # set the 2nd frame\n        >>> frame2_data_sample = DetDataSample(metainfo=dict(\n        ...         img_shape=(100, 100), frame_id=1))\n        >>> frame2_gt_instances = InstanceData()\n        >>> frame2_gt_instances.bbox = torch.ones([3, 4])\n        >>> frame2_data_sample.gt_instances = frame2_gt_instances\n        >>> track_data_sample.video_data_samples = [frame1_data_sample,\n        ...                                         frame2_data_sample]\n        >>> # set metainfo for track_data_sample\n        >>> track_data_sample.set_metainfo(dict(key_frames_inds=[0]))\n        >>> track_data_sample.set_metainfo(dict(ref_frames_inds=[1]))\n        >>> print(track_data_sample)\n        <TrackDataSample(\n\n            META INFORMATION\n            key_frames_inds: [0]\n            ref_frames_inds: [1]\n\n            DATA FIELDS\n            video_data_samples: [<DetDataSample(\n\n                    META INFORMATION\n                    img_shape: (100, 100)\n\n                    DATA FIELDS\n                    gt_instances: <InstanceData(\n\n                            META INFORMATION\n\n                            DATA FIELDS\n                            bbox: tensor([[0., 0., 0., 0.],\n                                        [0., 0., 0., 0.]])\n                        ) at 0x7f639320dcd0>\n                ) at 0x7f64bd223340>, <DetDataSample(\n\n                    META INFORMATION\n                    img_shape: (100, 100)\n\n                    DATA FIELDS\n                    gt_instances: <InstanceData(\n\n                            META INFORMATION\n\n                            DATA FIELDS\n                            bbox: tensor([[1., 1., 1., 1.],\n                                        [1., 1., 1., 1.],\n                                        [1., 1., 1., 1.]])\n                        ) at 0x7f64bd128b20>\n                ) at 0x7f64bd1346d0>]\n        ) at 0x7f64bd2237f0>\n        >>> print(len(track_data_sample))\n        2\n        >>> key_data_sample = track_data_sample.get_key_frames()\n        >>> print(key_data_sample[0].frame_id)\n        0\n        >>> ref_data_sample = track_data_sample.get_ref_frames()\n        >>> print(ref_data_sample[0].frame_id)\n        1\n        >>> frame1_data_sample = track_data_sample[0]\n        >>> print(frame1_data_sample.gt_instances.bbox)\n        tensor([[0., 0., 0., 0.],\n                [0., 0., 0., 0.]])\n        >>> # Tensor-like methods\n        >>> cuda_track_data_sample = track_data_sample.to('cuda')\n        >>> cuda_track_data_sample = track_data_sample.cuda()\n        >>> cpu_track_data_sample = track_data_sample.cpu()\n        >>> cpu_track_data_sample = track_data_sample.to('cpu')\n        >>> fp16_instances = cuda_track_data_sample.to(\n        ...     device=None, dtype=torch.float16, non_blocking=False,\n        ...     copy=False, memory_format=torch.preserve_format)\n    \"\"\"\n\n    @property\n    def video_data_samples(self) -> List[DetDataSample]:\n        return self._video_data_samples\n\n    @video_data_samples.setter\n    def video_data_samples(self, value: List[DetDataSample]):\n        if isinstance(value, DetDataSample):\n            value = [value]\n        assert isinstance(value, list), 'video_data_samples must be a list'\n        assert isinstance(\n            value[0], DetDataSample\n        ), 'video_data_samples must be a list of DetDataSample, but got '\n        f'{value[0]}'\n        self.set_field(value, '_video_data_samples', dtype=list)\n\n    @video_data_samples.deleter\n    def video_data_samples(self):\n        del self._video_data_samples\n\n    def __getitem__(self, index):\n        assert hasattr(self,\n                       '_video_data_samples'), 'video_data_samples not set'\n        return self._video_data_samples[index]\n\n    def get_key_frames(self):\n        assert hasattr(self, 'key_frames_inds'), \\\n            'key_frames_inds not set'\n        assert isinstance(self.key_frames_inds, Sequence)\n        key_frames_info = []\n        for index in self.key_frames_inds:\n            key_frames_info.append(self[index])\n        return key_frames_info\n\n    def get_ref_frames(self):\n        assert hasattr(self, 'ref_frames_inds'), \\\n            'ref_frames_inds not set'\n        ref_frames_info = []\n        assert isinstance(self.ref_frames_inds, Sequence)\n        for index in self.ref_frames_inds:\n            ref_frames_info.append(self[index])\n        return ref_frames_info\n\n    def __len__(self):\n        return len(self._video_data_samples) if hasattr(\n            self, '_video_data_samples') else 0\n\n    # TODO: add UT for this Tensor-like method\n    # Tensor-like methods\n    def to(self, *args, **kwargs) -> 'BaseDataElement':\n        \"\"\"Apply same name function to all tensors in data_fields.\"\"\"\n        new_data = self.new()\n        for k, v_list in self.items():\n            data_list = []\n            for v in v_list:\n                if hasattr(v, 'to'):\n                    v = v.to(*args, **kwargs)\n                    data_list.append(v)\n            if len(data_list) > 0:\n                new_data.set_data({f'{k}': data_list})\n        return new_data\n\n    # Tensor-like methods\n    def cpu(self) -> 'BaseDataElement':\n        \"\"\"Convert all tensors to CPU in data.\"\"\"\n        new_data = self.new()\n        for k, v_list in self.items():\n            data_list = []\n            for v in v_list:\n                if isinstance(v, (torch.Tensor, BaseDataElement)):\n                    v = v.cpu()\n                    data_list.append(v)\n            if len(data_list) > 0:\n                new_data.set_data({f'{k}': data_list})\n        return new_data\n\n    # Tensor-like methods\n    def cuda(self) -> 'BaseDataElement':\n        \"\"\"Convert all tensors to GPU in data.\"\"\"\n        new_data = self.new()\n        for k, v_list in self.items():\n            data_list = []\n            for v in v_list:\n                if isinstance(v, (torch.Tensor, BaseDataElement)):\n                    v = v.cuda()\n                    data_list.append(v)\n            if len(data_list) > 0:\n                new_data.set_data({f'{k}': data_list})\n        return new_data\n\n    # Tensor-like methods\n    def npu(self) -> 'BaseDataElement':\n        \"\"\"Convert all tensors to NPU in data.\"\"\"\n        new_data = self.new()\n        for k, v_list in self.items():\n            data_list = []\n            for v in v_list:\n                if isinstance(v, (torch.Tensor, BaseDataElement)):\n                    v = v.npu()\n                    data_list.append(v)\n            if len(data_list) > 0:\n                new_data.set_data({f'{k}': data_list})\n        return new_data\n\n    # Tensor-like methods\n    def detach(self) -> 'BaseDataElement':\n        \"\"\"Detach all tensors in data.\"\"\"\n        new_data = self.new()\n        for k, v_list in self.items():\n            data_list = []\n            for v in v_list:\n                if isinstance(v, (torch.Tensor, BaseDataElement)):\n                    v = v.detach()\n                    data_list.append(v)\n            if len(data_list) > 0:\n                new_data.set_data({f'{k}': data_list})\n        return new_data\n\n    # Tensor-like methods\n    def numpy(self) -> 'BaseDataElement':\n        \"\"\"Convert all tensors to np.ndarray in data.\"\"\"\n        new_data = self.new()\n        for k, v_list in self.items():\n            data_list = []\n            for v in v_list:\n                if isinstance(v, (torch.Tensor, BaseDataElement)):\n                    v = v.detach().cpu().numpy()\n                    data_list.append(v)\n            if len(data_list) > 0:\n                new_data.set_data({f'{k}': data_list})\n        return new_data\n\n    def to_tensor(self) -> 'BaseDataElement':\n        \"\"\"Convert all np.ndarray to tensor in data.\"\"\"\n        new_data = self.new()\n        for k, v_list in self.items():\n            data_list = []\n            for v in v_list:\n                if isinstance(v, np.ndarray):\n                    v = torch.from_numpy(v)\n                elif isinstance(v, BaseDataElement):\n                    v = v.to_tensor()\n                data_list.append(v)\n            if len(data_list) > 0:\n                new_data.set_data({f'{k}': data_list})\n        return new_data\n\n    # Tensor-like methods\n    def clone(self) -> 'BaseDataElement':\n        \"\"\"Deep copy the current data element.\n\n        Returns:\n            BaseDataElement: The copy of current data element.\n        \"\"\"\n        clone_data = self.__class__()\n        clone_data.set_metainfo(dict(self.metainfo_items()))\n\n        for k, v_list in self.items():\n            clone_item_list = []\n            for v in v_list:\n                clone_item_list.append(v.clone())\n            clone_data.set_data({k: clone_item_list})\n        return clone_data\n\n\nTrackSampleList = List[TrackDataSample]\nOptTrackSampleList = Optional[TrackSampleList]\n"
  },
  {
    "path": "mmdet/testing/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom ._fast_stop_training_hook import FastStopTrainingHook  # noqa: F401,F403\nfrom ._utils import (demo_mm_inputs, demo_mm_proposals,\n                     demo_mm_sampling_results, demo_track_inputs,\n                     get_detector_cfg, get_roi_head_cfg, random_boxes,\n                     replace_to_ceph)\n\n__all__ = [\n    'demo_mm_inputs', 'get_detector_cfg', 'get_roi_head_cfg',\n    'demo_mm_proposals', 'demo_mm_sampling_results', 'replace_to_ceph',\n    'demo_track_inputs', 'VideoDataSampleFeeder', 'random_boxes'\n]\n"
  },
  {
    "path": "mmdet/testing/_fast_stop_training_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.hooks import Hook\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass FastStopTrainingHook(Hook):\n    \"\"\"Set runner's epoch information to the model.\"\"\"\n\n    def __init__(self, by_epoch, save_ckpt=False, stop_iter_or_epoch=5):\n        self.by_epoch = by_epoch\n        self.save_ckpt = save_ckpt\n        self.stop_iter_or_epoch = stop_iter_or_epoch\n\n    def after_train_iter(self, runner, batch_idx: int, data_batch: None,\n                         outputs: None) -> None:\n        if self.save_ckpt and self.by_epoch:\n            # If it is epoch-based and want to save weights,\n            # we must run at least 1 epoch.\n            return\n        if runner.iter >= self.stop_iter_or_epoch:\n            raise RuntimeError('quick exit')\n\n    def after_train_epoch(self, runner) -> None:\n        if runner.epoch >= self.stop_iter_or_epoch - 1:\n            raise RuntimeError('quick exit')\n"
  },
  {
    "path": "mmdet/testing/_utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom os.path import dirname, exists, join\n\nimport numpy as np\nimport torch\nfrom mmengine.config import Config\nfrom mmengine.dataset import pseudo_collate\nfrom mmengine.structures import InstanceData, PixelData\n\nfrom mmdet.utils.util_random import ensure_rng\nfrom ..registry import TASK_UTILS\nfrom ..structures import DetDataSample, TrackDataSample\nfrom ..structures.bbox import HorizontalBoxes\n\n\ndef _get_config_directory():\n    \"\"\"Find the predefined detector config directory.\"\"\"\n    try:\n        # Assume we are running in the source mmdetection repo\n        repo_dpath = dirname(dirname(dirname(__file__)))\n    except NameError:\n        # For IPython development when this __file__ is not defined\n        import mmdet\n        repo_dpath = dirname(dirname(mmdet.__file__))\n    config_dpath = join(repo_dpath, 'configs')\n    if not exists(config_dpath):\n        raise Exception('Cannot find config path')\n    return config_dpath\n\n\ndef _get_config_module(fname):\n    \"\"\"Load a configuration as a python module.\"\"\"\n    config_dpath = _get_config_directory()\n    config_fpath = join(config_dpath, fname)\n    config_mod = Config.fromfile(config_fpath)\n    return config_mod\n\n\ndef get_detector_cfg(fname):\n    \"\"\"Grab configs necessary to create a detector.\n\n    These are deep copied to allow for safe modification of parameters without\n    influencing other tests.\n    \"\"\"\n    config = _get_config_module(fname)\n    model = copy.deepcopy(config.model)\n    return model\n\n\ndef get_roi_head_cfg(fname):\n    \"\"\"Grab configs necessary to create a roi_head.\n\n    These are deep copied to allow for safe modification of parameters without\n    influencing other tests.\n    \"\"\"\n    config = _get_config_module(fname)\n    model = copy.deepcopy(config.model)\n\n    roi_head = model.roi_head\n    train_cfg = None if model.train_cfg is None else model.train_cfg.rcnn\n    test_cfg = None if model.test_cfg is None else model.test_cfg.rcnn\n    roi_head.update(dict(train_cfg=train_cfg, test_cfg=test_cfg))\n    return roi_head\n\n\ndef _rand_bboxes(rng, num_boxes, w, h):\n    cx, cy, bw, bh = rng.rand(num_boxes, 4).T\n\n    tl_x = ((cx * w) - (w * bw / 2)).clip(0, w)\n    tl_y = ((cy * h) - (h * bh / 2)).clip(0, h)\n    br_x = ((cx * w) + (w * bw / 2)).clip(0, w)\n    br_y = ((cy * h) + (h * bh / 2)).clip(0, h)\n\n    bboxes = np.vstack([tl_x, tl_y, br_x, br_y]).T\n    return bboxes\n\n\ndef _rand_masks(rng, num_boxes, bboxes, img_w, img_h):\n    from mmdet.structures.mask import BitmapMasks\n    masks = np.zeros((num_boxes, img_h, img_w))\n    for i, bbox in enumerate(bboxes):\n        bbox = bbox.astype(np.int32)\n        mask = (rng.rand(1, bbox[3] - bbox[1], bbox[2] - bbox[0]) >\n                0.3).astype(np.int64)\n        masks[i:i + 1, bbox[1]:bbox[3], bbox[0]:bbox[2]] = mask\n    return BitmapMasks(masks, height=img_h, width=img_w)\n\n\ndef demo_mm_inputs(batch_size=2,\n                   image_shapes=(3, 128, 128),\n                   num_items=None,\n                   num_classes=10,\n                   sem_seg_output_strides=1,\n                   with_mask=False,\n                   with_semantic=False,\n                   use_box_type=False,\n                   device='cpu',\n                   texts=None,\n                   custom_entities=False):\n    \"\"\"Create a superset of inputs needed to run test or train batches.\n\n    Args:\n        batch_size (int): batch size. Defaults to 2.\n        image_shapes (List[tuple], Optional): image shape.\n            Defaults to (3, 128, 128)\n        num_items (None | List[int]): specifies the number\n            of boxes in each batch item. Default to None.\n        num_classes (int): number of different labels a\n            box might have. Defaults to 10.\n        with_mask (bool): Whether to return mask annotation.\n            Defaults to False.\n        with_semantic (bool): whether to return semantic.\n            Defaults to False.\n        device (str): Destination device type. Defaults to cpu.\n    \"\"\"\n    rng = np.random.RandomState(0)\n\n    if isinstance(image_shapes, list):\n        assert len(image_shapes) == batch_size\n    else:\n        image_shapes = [image_shapes] * batch_size\n\n    if isinstance(num_items, list):\n        assert len(num_items) == batch_size\n\n    if texts is not None:\n        assert batch_size == len(texts)\n\n    packed_inputs = []\n    for idx in range(batch_size):\n        image_shape = image_shapes[idx]\n        c, h, w = image_shape\n\n        image = rng.randint(0, 255, size=image_shape, dtype=np.uint8)\n\n        mm_inputs = dict()\n        mm_inputs['inputs'] = torch.from_numpy(image).to(device)\n\n        img_meta = {\n            'img_id': idx,\n            'img_shape': image_shape[1:],\n            'ori_shape': image_shape[1:],\n            'filename': '<demo>.png',\n            'scale_factor': np.array([1.1, 1.2]),\n            'flip': False,\n            'flip_direction': None,\n            'border': [1, 1, 1, 1]  # Only used by CenterNet\n        }\n\n        if texts:\n            img_meta['text'] = texts[idx]\n            img_meta['custom_entities'] = custom_entities\n\n        data_sample = DetDataSample()\n        data_sample.set_metainfo(img_meta)\n\n        # gt_instances\n        gt_instances = InstanceData()\n        if num_items is None:\n            num_boxes = rng.randint(1, 10)\n        else:\n            num_boxes = num_items[idx]\n\n        bboxes = _rand_bboxes(rng, num_boxes, w, h)\n        labels = rng.randint(1, num_classes, size=num_boxes)\n        # TODO: remove this part when all model adapted with BaseBoxes\n        if use_box_type:\n            gt_instances.bboxes = HorizontalBoxes(bboxes, dtype=torch.float32)\n        else:\n            gt_instances.bboxes = torch.FloatTensor(bboxes)\n        gt_instances.labels = torch.LongTensor(labels)\n\n        if with_mask:\n            masks = _rand_masks(rng, num_boxes, bboxes, w, h)\n            gt_instances.masks = masks\n\n        # TODO: waiting for ci to be fixed\n        # masks = np.random.randint(0, 2, (len(bboxes), h, w), dtype=np.uint8)\n        # gt_instances.mask = BitmapMasks(masks, h, w)\n\n        data_sample.gt_instances = gt_instances\n\n        # ignore_instances\n        ignore_instances = InstanceData()\n        bboxes = _rand_bboxes(rng, num_boxes, w, h)\n        if use_box_type:\n            ignore_instances.bboxes = HorizontalBoxes(\n                bboxes, dtype=torch.float32)\n        else:\n            ignore_instances.bboxes = torch.FloatTensor(bboxes)\n        data_sample.ignored_instances = ignore_instances\n\n        # gt_sem_seg\n        if with_semantic:\n            # assume gt_semantic_seg using scale 1/8 of the img\n            gt_semantic_seg = torch.from_numpy(\n                np.random.randint(\n                    0,\n                    num_classes, (1, h // sem_seg_output_strides,\n                                  w // sem_seg_output_strides),\n                    dtype=np.uint8))\n            gt_sem_seg_data = dict(sem_seg=gt_semantic_seg)\n            data_sample.gt_sem_seg = PixelData(**gt_sem_seg_data)\n\n        mm_inputs['data_samples'] = data_sample.to(device)\n\n        # TODO: gt_ignore\n\n        packed_inputs.append(mm_inputs)\n    data = pseudo_collate(packed_inputs)\n    return data\n\n\ndef demo_mm_proposals(image_shapes, num_proposals, device='cpu'):\n    \"\"\"Create a list of fake porposals.\n\n    Args:\n        image_shapes (list[tuple[int]]): Batch image shapes.\n        num_proposals (int): The number of fake proposals.\n    \"\"\"\n    rng = np.random.RandomState(0)\n\n    results = []\n    for img_shape in image_shapes:\n        result = InstanceData()\n        w, h = img_shape[1:]\n        proposals = _rand_bboxes(rng, num_proposals, w, h)\n        result.bboxes = torch.from_numpy(proposals).float()\n        result.scores = torch.from_numpy(rng.rand(num_proposals)).float()\n        result.labels = torch.zeros(num_proposals).long()\n        results.append(result.to(device))\n    return results\n\n\ndef demo_mm_sampling_results(proposals_list,\n                             batch_gt_instances,\n                             batch_gt_instances_ignore=None,\n                             assigner_cfg=None,\n                             sampler_cfg=None,\n                             feats=None):\n    \"\"\"Create sample results that can be passed to BBoxHead.get_targets.\"\"\"\n    assert len(proposals_list) == len(batch_gt_instances)\n    if batch_gt_instances_ignore is None:\n        batch_gt_instances_ignore = [None for _ in batch_gt_instances]\n    else:\n        assert len(batch_gt_instances_ignore) == len(batch_gt_instances)\n\n    default_assigner_cfg = dict(\n        type='MaxIoUAssigner',\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n        min_pos_iou=0.5,\n        ignore_iof_thr=-1)\n    assigner_cfg = assigner_cfg if assigner_cfg is not None \\\n        else default_assigner_cfg\n    default_sampler_cfg = dict(\n        type='RandomSampler',\n        num=512,\n        pos_fraction=0.25,\n        neg_pos_ub=-1,\n        add_gt_as_proposals=True)\n    sampler_cfg = sampler_cfg if sampler_cfg is not None \\\n        else default_sampler_cfg\n    bbox_assigner = TASK_UTILS.build(assigner_cfg)\n    bbox_sampler = TASK_UTILS.build(sampler_cfg)\n\n    sampling_results = []\n    for i in range(len(batch_gt_instances)):\n        if feats is not None:\n            feats = [lvl_feat[i][None] for lvl_feat in feats]\n        # rename proposals.bboxes to proposals.priors\n        proposals = proposals_list[i]\n        proposals.priors = proposals.pop('bboxes')\n\n        assign_result = bbox_assigner.assign(proposals, batch_gt_instances[i],\n                                             batch_gt_instances_ignore[i])\n        sampling_result = bbox_sampler.sample(\n            assign_result, proposals, batch_gt_instances[i], feats=feats)\n        sampling_results.append(sampling_result)\n\n    return sampling_results\n\n\ndef demo_track_inputs(batch_size=1,\n                      num_frames=2,\n                      key_frames_inds=None,\n                      image_shapes=(3, 128, 128),\n                      num_items=None,\n                      num_classes=1,\n                      with_mask=False,\n                      with_semantic=False):\n    \"\"\"Create a superset of inputs needed to run test or train batches.\n\n    Args:\n        batch_size (int): batch size. Default to 1.\n        num_frames (int): The number of frames.\n        key_frames_inds (List): The indices of key frames.\n        image_shapes (List[tuple], Optional): image shape.\n            Default to (3, 128, 128)\n        num_items (None | List[int]): specifies the number\n            of boxes in each batch item. Default to None.\n        num_classes (int): number of different labels a\n            box might have. Default to 1.\n        with_mask (bool): Whether to return mask annotation.\n            Defaults to False.\n        with_semantic (bool): whether to return semantic.\n            Default to False.\n    \"\"\"\n    rng = np.random.RandomState(0)\n\n    # Make sure the length of image_shapes is equal to ``batch_size``\n    if isinstance(image_shapes, list):\n        assert len(image_shapes) == batch_size\n    else:\n        image_shapes = [image_shapes] * batch_size\n\n    packed_inputs = []\n    for idx in range(batch_size):\n        mm_inputs = dict(inputs=dict())\n        _, h, w = image_shapes[idx]\n\n        imgs = rng.randint(\n            0, 255, size=(num_frames, *image_shapes[idx]), dtype=np.uint8)\n        mm_inputs['inputs'] = torch.from_numpy(imgs)\n\n        img_meta = {\n            'img_id': idx,\n            'img_shape': image_shapes[idx][-2:],\n            'ori_shape': image_shapes[idx][-2:],\n            'filename': '<demo>.png',\n            'scale_factor': np.array([1.1, 1.2]),\n            'flip': False,\n            'flip_direction': None,\n            'is_video_data': True,\n        }\n\n        video_data_samples = []\n        for i in range(num_frames):\n            data_sample = DetDataSample()\n            img_meta['frame_id'] = i\n            data_sample.set_metainfo(img_meta)\n\n            # gt_instances\n            gt_instances = InstanceData()\n            if num_items is None:\n                num_boxes = rng.randint(1, 10)\n            else:\n                num_boxes = num_items[idx]\n\n            bboxes = _rand_bboxes(rng, num_boxes, w, h)\n            labels = rng.randint(0, num_classes, size=num_boxes)\n            instances_id = rng.randint(100, num_classes + 100, size=num_boxes)\n            gt_instances.bboxes = torch.FloatTensor(bboxes)\n            gt_instances.labels = torch.LongTensor(labels)\n            gt_instances.instances_ids = torch.LongTensor(instances_id)\n\n            if with_mask:\n                masks = _rand_masks(rng, num_boxes, bboxes, w, h)\n                gt_instances.masks = masks\n\n            data_sample.gt_instances = gt_instances\n            # ignore_instances\n            ignore_instances = InstanceData()\n            bboxes = _rand_bboxes(rng, num_boxes, w, h)\n            ignore_instances.bboxes = bboxes\n            data_sample.ignored_instances = ignore_instances\n\n            video_data_samples.append(data_sample)\n\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = video_data_samples\n        if key_frames_inds is not None:\n            assert isinstance(\n                key_frames_inds,\n                list) and len(key_frames_inds) < num_frames and max(\n                    key_frames_inds) < num_frames\n            ref_frames_inds = [\n                i for i in range(num_frames) if i not in key_frames_inds\n            ]\n            track_data_sample.set_metainfo(\n                dict(key_frames_inds=key_frames_inds))\n            track_data_sample.set_metainfo(\n                dict(ref_frames_inds=ref_frames_inds))\n        mm_inputs['data_samples'] = track_data_sample\n\n        # TODO: gt_ignore\n        packed_inputs.append(mm_inputs)\n    data = pseudo_collate(packed_inputs)\n    return data\n\n\ndef random_boxes(num=1, scale=1, rng=None):\n    \"\"\"Simple version of ``kwimage.Boxes.random``\n    Returns:\n        Tensor: shape (n, 4) in x1, y1, x2, y2 format.\n    References:\n        https://gitlab.kitware.com/computer-vision/kwimage/blob/master/kwimage/structs/boxes.py#L1390 # noqa: E501\n    Example:\n        >>> num = 3\n        >>> scale = 512\n        >>> rng = 0\n        >>> boxes = random_boxes(num, scale, rng)\n        >>> print(boxes)\n        tensor([[280.9925, 278.9802, 308.6148, 366.1769],\n                [216.9113, 330.6978, 224.0446, 456.5878],\n                [405.3632, 196.3221, 493.3953, 270.7942]])\n    \"\"\"\n    rng = ensure_rng(rng)\n\n    tlbr = rng.rand(num, 4).astype(np.float32)\n\n    tl_x = np.minimum(tlbr[:, 0], tlbr[:, 2])\n    tl_y = np.minimum(tlbr[:, 1], tlbr[:, 3])\n    br_x = np.maximum(tlbr[:, 0], tlbr[:, 2])\n    br_y = np.maximum(tlbr[:, 1], tlbr[:, 3])\n\n    tlbr[:, 0] = tl_x * scale\n    tlbr[:, 1] = tl_y * scale\n    tlbr[:, 2] = br_x * scale\n    tlbr[:, 3] = br_y * scale\n\n    boxes = torch.from_numpy(tlbr)\n    return boxes\n\n\n# TODO: Support full ceph\ndef replace_to_ceph(cfg):\n    backend_args = dict(\n        backend='petrel',\n        path_mapping=dict({\n            './data/': 's3://openmmlab/datasets/detection/',\n            'data/': 's3://openmmlab/datasets/detection/'\n        }))\n\n    # TODO: name is a reserved interface, which will be used later.\n    def _process_pipeline(dataset, name):\n\n        def replace_img(pipeline):\n            if pipeline['type'] == 'LoadImageFromFile':\n                pipeline['backend_args'] = backend_args\n\n        def replace_ann(pipeline):\n            if pipeline['type'] == 'LoadAnnotations' or pipeline[\n                    'type'] == 'LoadPanopticAnnotations':\n                pipeline['backend_args'] = backend_args\n\n        if 'pipeline' in dataset:\n            replace_img(dataset.pipeline[0])\n            replace_ann(dataset.pipeline[1])\n            if 'dataset' in dataset:\n                # dataset wrapper\n                replace_img(dataset.dataset.pipeline[0])\n                replace_ann(dataset.dataset.pipeline[1])\n        else:\n            # dataset wrapper\n            replace_img(dataset.dataset.pipeline[0])\n            replace_ann(dataset.dataset.pipeline[1])\n\n    def _process_evaluator(evaluator, name):\n        if evaluator['type'] == 'CocoPanopticMetric':\n            evaluator['backend_args'] = backend_args\n\n    # half ceph\n    _process_pipeline(cfg.train_dataloader.dataset, cfg.filename)\n    _process_pipeline(cfg.val_dataloader.dataset, cfg.filename)\n    _process_pipeline(cfg.test_dataloader.dataset, cfg.filename)\n    _process_evaluator(cfg.val_evaluator, cfg.filename)\n    _process_evaluator(cfg.test_evaluator, cfg.filename)\n"
  },
  {
    "path": "mmdet/utils/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .collect_env import collect_env\nfrom .compat_config import compat_cfg\nfrom .dist_utils import (all_reduce_dict, allreduce_grads, reduce_mean,\n                         sync_random_seed)\nfrom .logger import get_caller_name, log_img_scale\nfrom .memory import AvoidCUDAOOM, AvoidOOM\nfrom .misc import (find_latest_checkpoint, get_test_pipeline_cfg,\n                   update_data_root)\nfrom .mot_error_visualize import imshow_mot_errors\nfrom .replace_cfg_vals import replace_cfg_vals\nfrom .setup_env import (register_all_modules, setup_cache_size_limit_of_dynamo,\n                        setup_multi_processes)\nfrom .split_batch import split_batch\nfrom .typing_utils import (ConfigType, InstanceList, MultiConfig,\n                           OptConfigType, OptInstanceList, OptMultiConfig,\n                           OptPixelList, PixelList, RangeType)\n\n__all__ = [\n    'collect_env', 'find_latest_checkpoint', 'update_data_root',\n    'setup_multi_processes', 'get_caller_name', 'log_img_scale', 'compat_cfg',\n    'split_batch', 'register_all_modules', 'replace_cfg_vals', 'AvoidOOM',\n    'AvoidCUDAOOM', 'all_reduce_dict', 'allreduce_grads', 'reduce_mean',\n    'sync_random_seed', 'ConfigType', 'InstanceList', 'MultiConfig',\n    'OptConfigType', 'OptInstanceList', 'OptMultiConfig', 'OptPixelList',\n    'PixelList', 'RangeType', 'get_test_pipeline_cfg',\n    'setup_cache_size_limit_of_dynamo', 'imshow_mot_errors'\n]\n"
  },
  {
    "path": "mmdet/utils/benchmark.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport time\nfrom functools import partial\nfrom typing import List, Optional, Union\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import fuse_conv_bn\n# TODO need update\n# from mmcv.runner import wrap_fp16_model\nfrom mmengine import MMLogger\nfrom mmengine.config import Config\nfrom mmengine.device import get_max_cuda_memory\nfrom mmengine.dist import get_world_size\nfrom mmengine.runner import Runner, load_checkpoint\nfrom mmengine.utils.dl_utils import set_multi_processing\nfrom torch.nn.parallel import DistributedDataParallel\n\nfrom mmdet.registry import DATASETS, MODELS\n\ntry:\n    import psutil\nexcept ImportError:\n    psutil = None\n\n\ndef custom_round(value: Union[int, float],\n                 factor: Union[int, float],\n                 precision: int = 2) -> float:\n    \"\"\"Custom round function.\"\"\"\n    return round(value / factor, precision)\n\n\ngb_round = partial(custom_round, factor=1024**3)\n\n\ndef print_log(msg: str, logger: Optional[MMLogger] = None) -> None:\n    \"\"\"Print a log message.\"\"\"\n    if logger is None:\n        print(msg, flush=True)\n    else:\n        logger.info(msg)\n\n\ndef print_process_memory(p: psutil.Process,\n                         logger: Optional[MMLogger] = None) -> None:\n    \"\"\"print process memory info.\"\"\"\n    mem_used = gb_round(psutil.virtual_memory().used)\n    memory_full_info = p.memory_full_info()\n    uss_mem = gb_round(memory_full_info.uss)\n    if hasattr(memory_full_info, 'pss'):\n        pss_mem = gb_round(memory_full_info.pss)\n\n    for children in p.children():\n        child_mem_info = children.memory_full_info()\n        uss_mem += gb_round(child_mem_info.uss)\n        if hasattr(child_mem_info, 'pss'):\n            pss_mem += gb_round(child_mem_info.pss)\n\n    process_count = 1 + len(p.children())\n\n    log_msg = f'(GB) mem_used: {mem_used:.2f} | uss: {uss_mem:.2f} | '\n    if hasattr(memory_full_info, 'pss'):\n        log_msg += f'pss: {pss_mem:.2f} | '\n    log_msg += f'total_proc: {process_count}'\n    print_log(log_msg, logger)\n\n\nclass BaseBenchmark:\n    \"\"\"The benchmark base class.\n\n    The ``run`` method is an external calling interface, and it will\n    call the ``run_once`` method ``repeat_num`` times for benchmarking.\n    Finally, call the ``average_multiple_runs`` method to further process\n    the results of multiple runs.\n\n    Args:\n        max_iter (int): maximum iterations of benchmark.\n        log_interval (int): interval of logging.\n        num_warmup (int): Number of Warmup.\n        logger (MMLogger, optional): Formatted logger used to record messages.\n    \"\"\"\n\n    def __init__(self,\n                 max_iter: int,\n                 log_interval: int,\n                 num_warmup: int,\n                 logger: Optional[MMLogger] = None):\n        self.max_iter = max_iter\n        self.log_interval = log_interval\n        self.num_warmup = num_warmup\n        self.logger = logger\n\n    def run(self, repeat_num: int = 1) -> dict:\n        \"\"\"benchmark entry method.\n\n        Args:\n            repeat_num (int): Number of repeat benchmark.\n                Defaults to 1.\n        \"\"\"\n        assert repeat_num >= 1\n\n        results = []\n        for _ in range(repeat_num):\n            results.append(self.run_once())\n\n        results = self.average_multiple_runs(results)\n        return results\n\n    def run_once(self) -> dict:\n        \"\"\"Executes the benchmark once.\"\"\"\n        raise NotImplementedError()\n\n    def average_multiple_runs(self, results: List[dict]) -> dict:\n        \"\"\"Average the results of multiple runs.\"\"\"\n        raise NotImplementedError()\n\n\nclass InferenceBenchmark(BaseBenchmark):\n    \"\"\"The inference benchmark class. It will be statistical inference FPS,\n    CUDA memory and CPU memory information.\n\n    Args:\n        cfg (mmengine.Config): config.\n        checkpoint (str): Accept local filepath, URL, ``torchvision://xxx``,\n            ``open-mmlab://xxx``.\n        distributed (bool): distributed testing flag.\n        is_fuse_conv_bn (bool): Whether to fuse conv and bn, this will\n            slightly increase the inference speed.\n        max_iter (int): maximum iterations of benchmark. Defaults to 2000.\n        log_interval (int): interval of logging. Defaults to 50.\n        num_warmup (int): Number of Warmup. Defaults to 5.\n        logger (MMLogger, optional): Formatted logger used to record messages.\n    \"\"\"\n\n    def __init__(self,\n                 cfg: Config,\n                 checkpoint: str,\n                 distributed: bool,\n                 is_fuse_conv_bn: bool,\n                 max_iter: int = 2000,\n                 log_interval: int = 50,\n                 num_warmup: int = 5,\n                 logger: Optional[MMLogger] = None):\n        super().__init__(max_iter, log_interval, num_warmup, logger)\n\n        assert get_world_size(\n        ) == 1, 'Inference benchmark does not allow distributed multi-GPU'\n\n        self.cfg = copy.deepcopy(cfg)\n        self.distributed = distributed\n\n        if psutil is None:\n            raise ImportError('psutil is not installed, please install it by: '\n                              'pip install psutil')\n\n        self._process = psutil.Process()\n        env_cfg = self.cfg.get('env_cfg')\n        if env_cfg.get('cudnn_benchmark'):\n            torch.backends.cudnn.benchmark = True\n\n        mp_cfg: dict = env_cfg.get('mp_cfg', {})\n        set_multi_processing(**mp_cfg, distributed=self.distributed)\n\n        print_log('before build: ', self.logger)\n        print_process_memory(self._process, self.logger)\n\n        self.model = self._init_model(checkpoint, is_fuse_conv_bn)\n\n        # Because multiple processes will occupy additional CPU resources,\n        # FPS statistics will be more unstable when num_workers is not 0.\n        # It is reasonable to set num_workers to 0.\n        dataloader_cfg = cfg.test_dataloader\n        dataloader_cfg['num_workers'] = 0\n        dataloader_cfg['batch_size'] = 1\n        dataloader_cfg['persistent_workers'] = False\n        self.data_loader = Runner.build_dataloader(dataloader_cfg)\n\n        print_log('after build: ', self.logger)\n        print_process_memory(self._process, self.logger)\n\n    def _init_model(self, checkpoint: str, is_fuse_conv_bn: bool) -> nn.Module:\n        \"\"\"Initialize the model.\"\"\"\n        model = MODELS.build(self.cfg.model)\n        # TODO need update\n        # fp16_cfg = self.cfg.get('fp16', None)\n        # if fp16_cfg is not None:\n        #     wrap_fp16_model(model)\n\n        load_checkpoint(model, checkpoint, map_location='cpu')\n        if is_fuse_conv_bn:\n            model = fuse_conv_bn(model)\n\n        model = model.cuda()\n\n        if self.distributed:\n            model = DistributedDataParallel(\n                model,\n                device_ids=[torch.cuda.current_device()],\n                broadcast_buffers=False,\n                find_unused_parameters=False)\n\n        model.eval()\n        return model\n\n    def run_once(self) -> dict:\n        \"\"\"Executes the benchmark once.\"\"\"\n        pure_inf_time = 0\n        fps = 0\n\n        for i, data in enumerate(self.data_loader):\n\n            if (i + 1) % self.log_interval == 0:\n                print_log('==================================', self.logger)\n\n            torch.cuda.synchronize()\n            start_time = time.perf_counter()\n\n            with torch.no_grad():\n                self.model.test_step(data)\n\n            torch.cuda.synchronize()\n            elapsed = time.perf_counter() - start_time\n\n            if i >= self.num_warmup:\n                pure_inf_time += elapsed\n                if (i + 1) % self.log_interval == 0:\n                    fps = (i + 1 - self.num_warmup) / pure_inf_time\n                    cuda_memory = get_max_cuda_memory()\n\n                    print_log(\n                        f'Done image [{i + 1:<3}/{self.max_iter}], '\n                        f'fps: {fps:.1f} img/s, '\n                        f'times per image: {1000 / fps:.1f} ms/img, '\n                        f'cuda memory: {cuda_memory} MB', self.logger)\n                    print_process_memory(self._process, self.logger)\n\n            if (i + 1) == self.max_iter:\n                fps = (i + 1 - self.num_warmup) / pure_inf_time\n                break\n\n        return {'fps': fps}\n\n    def average_multiple_runs(self, results: List[dict]) -> dict:\n        \"\"\"Average the results of multiple runs.\"\"\"\n        print_log('============== Done ==================', self.logger)\n\n        fps_list_ = [round(result['fps'], 1) for result in results]\n        avg_fps_ = sum(fps_list_) / len(fps_list_)\n        outputs = {'avg_fps': avg_fps_, 'fps_list': fps_list_}\n\n        if len(fps_list_) > 1:\n            times_pre_image_list_ = [\n                round(1000 / result['fps'], 1) for result in results\n            ]\n            avg_times_pre_image_ = sum(times_pre_image_list_) / len(\n                times_pre_image_list_)\n\n            print_log(\n                f'Overall fps: {fps_list_}[{avg_fps_:.1f}] img/s, '\n                'times per image: '\n                f'{times_pre_image_list_}[{avg_times_pre_image_:.1f}] '\n                'ms/img', self.logger)\n        else:\n            print_log(\n                f'Overall fps: {fps_list_[0]:.1f} img/s, '\n                f'times per image: {1000 / fps_list_[0]:.1f} ms/img',\n                self.logger)\n\n        print_log(f'cuda memory: {get_max_cuda_memory()} MB', self.logger)\n        print_process_memory(self._process, self.logger)\n\n        return outputs\n\n\nclass DataLoaderBenchmark(BaseBenchmark):\n    \"\"\"The dataloader benchmark class. It will be statistical inference FPS and\n    CPU memory information.\n\n    Args:\n        cfg (mmengine.Config): config.\n        distributed (bool): distributed testing flag.\n        dataset_type (str): benchmark data type, only supports ``train``,\n            ``val`` and ``test``.\n        max_iter (int): maximum iterations of benchmark. Defaults to 2000.\n        log_interval (int): interval of logging. Defaults to 50.\n        num_warmup (int): Number of Warmup. Defaults to 5.\n        logger (MMLogger, optional): Formatted logger used to record messages.\n    \"\"\"\n\n    def __init__(self,\n                 cfg: Config,\n                 distributed: bool,\n                 dataset_type: str,\n                 max_iter: int = 2000,\n                 log_interval: int = 50,\n                 num_warmup: int = 5,\n                 logger: Optional[MMLogger] = None):\n        super().__init__(max_iter, log_interval, num_warmup, logger)\n\n        assert dataset_type in ['train', 'val', 'test'], \\\n            'dataset_type only supports train,' \\\n            f' val and test, but got {dataset_type}'\n        assert get_world_size(\n        ) == 1, 'Dataloader benchmark does not allow distributed multi-GPU'\n\n        self.cfg = copy.deepcopy(cfg)\n        self.distributed = distributed\n\n        if psutil is None:\n            raise ImportError('psutil is not installed, please install it by: '\n                              'pip install psutil')\n        self._process = psutil.Process()\n\n        mp_cfg = self.cfg.get('env_cfg', {}).get('mp_cfg')\n        if mp_cfg is not None:\n            set_multi_processing(distributed=self.distributed, **mp_cfg)\n        else:\n            set_multi_processing(distributed=self.distributed)\n\n        print_log('before build: ', self.logger)\n        print_process_memory(self._process, self.logger)\n\n        if dataset_type == 'train':\n            self.data_loader = Runner.build_dataloader(cfg.train_dataloader)\n        elif dataset_type == 'test':\n            self.data_loader = Runner.build_dataloader(cfg.test_dataloader)\n        else:\n            self.data_loader = Runner.build_dataloader(cfg.val_dataloader)\n\n        self.batch_size = self.data_loader.batch_size\n        self.num_workers = self.data_loader.num_workers\n\n        print_log('after build: ', self.logger)\n        print_process_memory(self._process, self.logger)\n\n    def run_once(self) -> dict:\n        \"\"\"Executes the benchmark once.\"\"\"\n        pure_inf_time = 0\n        fps = 0\n\n        # benchmark with 2000 image and take the average\n        start_time = time.perf_counter()\n        for i, data in enumerate(self.data_loader):\n            elapsed = time.perf_counter() - start_time\n\n            if (i + 1) % self.log_interval == 0:\n                print_log('==================================', self.logger)\n\n            if i >= self.num_warmup:\n                pure_inf_time += elapsed\n                if (i + 1) % self.log_interval == 0:\n                    fps = (i + 1 - self.num_warmup) / pure_inf_time\n\n                    print_log(\n                        f'Done batch [{i + 1:<3}/{self.max_iter}], '\n                        f'fps: {fps:.1f} batch/s, '\n                        f'times per batch: {1000 / fps:.1f} ms/batch, '\n                        f'batch size: {self.batch_size}, num_workers: '\n                        f'{self.num_workers}', self.logger)\n                    print_process_memory(self._process, self.logger)\n\n            if (i + 1) == self.max_iter:\n                fps = (i + 1 - self.num_warmup) / pure_inf_time\n                break\n\n            start_time = time.perf_counter()\n\n        return {'fps': fps}\n\n    def average_multiple_runs(self, results: List[dict]) -> dict:\n        \"\"\"Average the results of multiple runs.\"\"\"\n        print_log('============== Done ==================', self.logger)\n\n        fps_list_ = [round(result['fps'], 1) for result in results]\n        avg_fps_ = sum(fps_list_) / len(fps_list_)\n        outputs = {'avg_fps': avg_fps_, 'fps_list': fps_list_}\n\n        if len(fps_list_) > 1:\n            times_pre_image_list_ = [\n                round(1000 / result['fps'], 1) for result in results\n            ]\n            avg_times_pre_image_ = sum(times_pre_image_list_) / len(\n                times_pre_image_list_)\n\n            print_log(\n                f'Overall fps: {fps_list_}[{avg_fps_:.1f}] img/s, '\n                'times per batch: '\n                f'{times_pre_image_list_}[{avg_times_pre_image_:.1f}] '\n                f'ms/batch, batch size: {self.batch_size}, num_workers: '\n                f'{self.num_workers}', self.logger)\n        else:\n            print_log(\n                f'Overall fps: {fps_list_[0]:.1f} batch/s, '\n                f'times per batch: {1000 / fps_list_[0]:.1f} ms/batch, '\n                f'batch size: {self.batch_size}, num_workers: '\n                f'{self.num_workers}', self.logger)\n\n        print_process_memory(self._process, self.logger)\n\n        return outputs\n\n\nclass DatasetBenchmark(BaseBenchmark):\n    \"\"\"The dataset benchmark class. It will be statistical inference FPS, FPS\n    pre transform and CPU memory information.\n\n    Args:\n        cfg (mmengine.Config): config.\n        dataset_type (str): benchmark data type, only supports ``train``,\n            ``val`` and ``test``.\n        max_iter (int): maximum iterations of benchmark. Defaults to 2000.\n        log_interval (int): interval of logging. Defaults to 50.\n        num_warmup (int): Number of Warmup. Defaults to 5.\n        logger (MMLogger, optional): Formatted logger used to record messages.\n    \"\"\"\n\n    def __init__(self,\n                 cfg: Config,\n                 dataset_type: str,\n                 max_iter: int = 2000,\n                 log_interval: int = 50,\n                 num_warmup: int = 5,\n                 logger: Optional[MMLogger] = None):\n        super().__init__(max_iter, log_interval, num_warmup, logger)\n        assert dataset_type in ['train', 'val', 'test'], \\\n            'dataset_type only supports train,' \\\n            f' val and test, but got {dataset_type}'\n        assert get_world_size(\n        ) == 1, 'Dataset benchmark does not allow distributed multi-GPU'\n        self.cfg = copy.deepcopy(cfg)\n\n        if dataset_type == 'train':\n            dataloader_cfg = copy.deepcopy(cfg.train_dataloader)\n        elif dataset_type == 'test':\n            dataloader_cfg = copy.deepcopy(cfg.test_dataloader)\n        else:\n            dataloader_cfg = copy.deepcopy(cfg.val_dataloader)\n\n        dataset_cfg = dataloader_cfg.pop('dataset')\n        dataset = DATASETS.build(dataset_cfg)\n        if hasattr(dataset, 'full_init'):\n            dataset.full_init()\n        self.dataset = dataset\n\n    def run_once(self) -> dict:\n        \"\"\"Executes the benchmark once.\"\"\"\n        pure_inf_time = 0\n        fps = 0\n\n        total_index = list(range(len(self.dataset)))\n        np.random.shuffle(total_index)\n\n        start_time = time.perf_counter()\n        for i, idx in enumerate(total_index):\n            if (i + 1) % self.log_interval == 0:\n                print_log('==================================', self.logger)\n\n            get_data_info_start_time = time.perf_counter()\n            data_info = self.dataset.get_data_info(idx)\n            get_data_info_elapsed = time.perf_counter(\n            ) - get_data_info_start_time\n\n            if (i + 1) % self.log_interval == 0:\n                print_log(f'get_data_info - {get_data_info_elapsed * 1000} ms',\n                          self.logger)\n\n            for t in self.dataset.pipeline.transforms:\n                transform_start_time = time.perf_counter()\n                data_info = t(data_info)\n                transform_elapsed = time.perf_counter() - transform_start_time\n\n                if (i + 1) % self.log_interval == 0:\n                    print_log(\n                        f'{t.__class__.__name__} - '\n                        f'{transform_elapsed * 1000} ms', self.logger)\n\n                if data_info is None:\n                    break\n\n            elapsed = time.perf_counter() - start_time\n\n            if i >= self.num_warmup:\n                pure_inf_time += elapsed\n                if (i + 1) % self.log_interval == 0:\n                    fps = (i + 1 - self.num_warmup) / pure_inf_time\n\n                    print_log(\n                        f'Done img [{i + 1:<3}/{self.max_iter}], '\n                        f'fps: {fps:.1f} img/s, '\n                        f'times per img: {1000 / fps:.1f} ms/img', self.logger)\n\n            if (i + 1) == self.max_iter:\n                fps = (i + 1 - self.num_warmup) / pure_inf_time\n                break\n\n            start_time = time.perf_counter()\n\n        return {'fps': fps}\n\n    def average_multiple_runs(self, results: List[dict]) -> dict:\n        \"\"\"Average the results of multiple runs.\"\"\"\n        print_log('============== Done ==================', self.logger)\n\n        fps_list_ = [round(result['fps'], 1) for result in results]\n        avg_fps_ = sum(fps_list_) / len(fps_list_)\n        outputs = {'avg_fps': avg_fps_, 'fps_list': fps_list_}\n\n        if len(fps_list_) > 1:\n            times_pre_image_list_ = [\n                round(1000 / result['fps'], 1) for result in results\n            ]\n            avg_times_pre_image_ = sum(times_pre_image_list_) / len(\n                times_pre_image_list_)\n\n            print_log(\n                f'Overall fps: {fps_list_}[{avg_fps_:.1f}] img/s, '\n                'times per img: '\n                f'{times_pre_image_list_}[{avg_times_pre_image_:.1f}] '\n                'ms/img', self.logger)\n        else:\n            print_log(\n                f'Overall fps: {fps_list_[0]:.1f} img/s, '\n                f'times per img: {1000 / fps_list_[0]:.1f} ms/img',\n                self.logger)\n\n        return outputs\n"
  },
  {
    "path": "mmdet/utils/collect_env.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.utils import get_git_hash\nfrom mmengine.utils.dl_utils import collect_env as collect_base_env\n\nimport mmdet\n\n\ndef collect_env():\n    \"\"\"Collect the information of the running environments.\"\"\"\n    env_info = collect_base_env()\n    env_info['MMDetection'] = mmdet.__version__ + '+' + get_git_hash()[:7]\n    return env_info\n\n\nif __name__ == '__main__':\n    for name, val in collect_env().items():\n        print(f'{name}: {val}')\n"
  },
  {
    "path": "mmdet/utils/compat_config.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport warnings\n\nfrom mmengine.config import ConfigDict\n\n\ndef compat_cfg(cfg):\n    \"\"\"This function would modify some filed to keep the compatibility of\n    config.\n\n    For example, it will move some args which will be deprecated to the correct\n    fields.\n    \"\"\"\n    cfg = copy.deepcopy(cfg)\n    cfg = compat_imgs_per_gpu(cfg)\n    cfg = compat_loader_args(cfg)\n    cfg = compat_runner_args(cfg)\n    return cfg\n\n\ndef compat_runner_args(cfg):\n    if 'runner' not in cfg:\n        cfg.runner = ConfigDict({\n            'type': 'EpochBasedRunner',\n            'max_epochs': cfg.total_epochs\n        })\n        warnings.warn(\n            'config is now expected to have a `runner` section, '\n            'please set `runner` in your config.', UserWarning)\n    else:\n        if 'total_epochs' in cfg:\n            assert cfg.total_epochs == cfg.runner.max_epochs\n    return cfg\n\n\ndef compat_imgs_per_gpu(cfg):\n    cfg = copy.deepcopy(cfg)\n    if 'imgs_per_gpu' in cfg.data:\n        warnings.warn('\"imgs_per_gpu\" is deprecated in MMDet V2.0. '\n                      'Please use \"samples_per_gpu\" instead')\n        if 'samples_per_gpu' in cfg.data:\n            warnings.warn(\n                f'Got \"imgs_per_gpu\"={cfg.data.imgs_per_gpu} and '\n                f'\"samples_per_gpu\"={cfg.data.samples_per_gpu}, \"imgs_per_gpu\"'\n                f'={cfg.data.imgs_per_gpu} is used in this experiments')\n        else:\n            warnings.warn('Automatically set \"samples_per_gpu\"=\"imgs_per_gpu\"='\n                          f'{cfg.data.imgs_per_gpu} in this experiments')\n        cfg.data.samples_per_gpu = cfg.data.imgs_per_gpu\n    return cfg\n\n\ndef compat_loader_args(cfg):\n    \"\"\"Deprecated sample_per_gpu in cfg.data.\"\"\"\n\n    cfg = copy.deepcopy(cfg)\n    if 'train_dataloader' not in cfg.data:\n        cfg.data['train_dataloader'] = ConfigDict()\n    if 'val_dataloader' not in cfg.data:\n        cfg.data['val_dataloader'] = ConfigDict()\n    if 'test_dataloader' not in cfg.data:\n        cfg.data['test_dataloader'] = ConfigDict()\n\n    # special process for train_dataloader\n    if 'samples_per_gpu' in cfg.data:\n\n        samples_per_gpu = cfg.data.pop('samples_per_gpu')\n        assert 'samples_per_gpu' not in \\\n               cfg.data.train_dataloader, ('`samples_per_gpu` are set '\n                                           'in `data` field and ` '\n                                           'data.train_dataloader` '\n                                           'at the same time. '\n                                           'Please only set it in '\n                                           '`data.train_dataloader`. ')\n        cfg.data.train_dataloader['samples_per_gpu'] = samples_per_gpu\n\n    if 'persistent_workers' in cfg.data:\n\n        persistent_workers = cfg.data.pop('persistent_workers')\n        assert 'persistent_workers' not in \\\n               cfg.data.train_dataloader, ('`persistent_workers` are set '\n                                           'in `data` field and ` '\n                                           'data.train_dataloader` '\n                                           'at the same time. '\n                                           'Please only set it in '\n                                           '`data.train_dataloader`. ')\n        cfg.data.train_dataloader['persistent_workers'] = persistent_workers\n\n    if 'workers_per_gpu' in cfg.data:\n\n        workers_per_gpu = cfg.data.pop('workers_per_gpu')\n        cfg.data.train_dataloader['workers_per_gpu'] = workers_per_gpu\n        cfg.data.val_dataloader['workers_per_gpu'] = workers_per_gpu\n        cfg.data.test_dataloader['workers_per_gpu'] = workers_per_gpu\n\n    # special process for val_dataloader\n    if 'samples_per_gpu' in cfg.data.val:\n        # keep default value of `sample_per_gpu` is 1\n        assert 'samples_per_gpu' not in \\\n               cfg.data.val_dataloader, ('`samples_per_gpu` are set '\n                                         'in `data.val` field and ` '\n                                         'data.val_dataloader` at '\n                                         'the same time. '\n                                         'Please only set it in '\n                                         '`data.val_dataloader`. ')\n        cfg.data.val_dataloader['samples_per_gpu'] = \\\n            cfg.data.val.pop('samples_per_gpu')\n    # special process for val_dataloader\n\n    # in case the test dataset is concatenated\n    if isinstance(cfg.data.test, dict):\n        if 'samples_per_gpu' in cfg.data.test:\n            assert 'samples_per_gpu' not in \\\n                   cfg.data.test_dataloader, ('`samples_per_gpu` are set '\n                                              'in `data.test` field and ` '\n                                              'data.test_dataloader` '\n                                              'at the same time. '\n                                              'Please only set it in '\n                                              '`data.test_dataloader`. ')\n\n            cfg.data.test_dataloader['samples_per_gpu'] = \\\n                cfg.data.test.pop('samples_per_gpu')\n\n    elif isinstance(cfg.data.test, list):\n        for ds_cfg in cfg.data.test:\n            if 'samples_per_gpu' in ds_cfg:\n                assert 'samples_per_gpu' not in \\\n                       cfg.data.test_dataloader, ('`samples_per_gpu` are set '\n                                                  'in `data.test` field and ` '\n                                                  'data.test_dataloader` at'\n                                                  ' the same time. '\n                                                  'Please only set it in '\n                                                  '`data.test_dataloader`. ')\n        samples_per_gpu = max(\n            [ds_cfg.pop('samples_per_gpu', 1) for ds_cfg in cfg.data.test])\n        cfg.data.test_dataloader['samples_per_gpu'] = samples_per_gpu\n\n    return cfg\n"
  },
  {
    "path": "mmdet/utils/contextmanagers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport asyncio\nimport contextlib\nimport logging\nimport os\nimport time\nfrom typing import List\n\nimport torch\n\nlogger = logging.getLogger(__name__)\n\nDEBUG_COMPLETED_TIME = bool(os.environ.get('DEBUG_COMPLETED_TIME', False))\n\n\n@contextlib.asynccontextmanager\nasync def completed(trace_name='',\n                    name='',\n                    sleep_interval=0.05,\n                    streams: List[torch.cuda.Stream] = None):\n    \"\"\"Async context manager that waits for work to complete on given CUDA\n    streams.\"\"\"\n    if not torch.cuda.is_available():\n        yield\n        return\n\n    stream_before_context_switch = torch.cuda.current_stream()\n    if not streams:\n        streams = [stream_before_context_switch]\n    else:\n        streams = [s if s else stream_before_context_switch for s in streams]\n\n    end_events = [\n        torch.cuda.Event(enable_timing=DEBUG_COMPLETED_TIME) for _ in streams\n    ]\n\n    if DEBUG_COMPLETED_TIME:\n        start = torch.cuda.Event(enable_timing=True)\n        stream_before_context_switch.record_event(start)\n\n        cpu_start = time.monotonic()\n    logger.debug('%s %s starting, streams: %s', trace_name, name, streams)\n    grad_enabled_before = torch.is_grad_enabled()\n    try:\n        yield\n    finally:\n        current_stream = torch.cuda.current_stream()\n        assert current_stream == stream_before_context_switch\n\n        if DEBUG_COMPLETED_TIME:\n            cpu_end = time.monotonic()\n        for i, stream in enumerate(streams):\n            event = end_events[i]\n            stream.record_event(event)\n\n        grad_enabled_after = torch.is_grad_enabled()\n\n        # observed change of torch.is_grad_enabled() during concurrent run of\n        # async_test_bboxes code\n        assert (grad_enabled_before == grad_enabled_after\n                ), 'Unexpected is_grad_enabled() value change'\n\n        are_done = [e.query() for e in end_events]\n        logger.debug('%s %s completed: %s streams: %s', trace_name, name,\n                     are_done, streams)\n        with torch.cuda.stream(stream_before_context_switch):\n            while not all(are_done):\n                await asyncio.sleep(sleep_interval)\n                are_done = [e.query() for e in end_events]\n                logger.debug(\n                    '%s %s completed: %s streams: %s',\n                    trace_name,\n                    name,\n                    are_done,\n                    streams,\n                )\n\n        current_stream = torch.cuda.current_stream()\n        assert current_stream == stream_before_context_switch\n\n        if DEBUG_COMPLETED_TIME:\n            cpu_time = (cpu_end - cpu_start) * 1000\n            stream_times_ms = ''\n            for i, stream in enumerate(streams):\n                elapsed_time = start.elapsed_time(end_events[i])\n                stream_times_ms += f' {stream} {elapsed_time:.2f} ms'\n            logger.info('%s %s %.2f ms %s', trace_name, name, cpu_time,\n                        stream_times_ms)\n\n\n@contextlib.asynccontextmanager\nasync def concurrent(streamqueue: asyncio.Queue,\n                     trace_name='concurrent',\n                     name='stream'):\n    \"\"\"Run code concurrently in different streams.\n\n    :param streamqueue: asyncio.Queue instance.\n\n    Queue tasks define the pool of streams used for concurrent execution.\n    \"\"\"\n    if not torch.cuda.is_available():\n        yield\n        return\n\n    initial_stream = torch.cuda.current_stream()\n\n    with torch.cuda.stream(initial_stream):\n        stream = await streamqueue.get()\n        assert isinstance(stream, torch.cuda.Stream)\n\n        try:\n            with torch.cuda.stream(stream):\n                logger.debug('%s %s is starting, stream: %s', trace_name, name,\n                             stream)\n                yield\n                current = torch.cuda.current_stream()\n                assert current == stream\n                logger.debug('%s %s has finished, stream: %s', trace_name,\n                             name, stream)\n        finally:\n            streamqueue.task_done()\n            streamqueue.put_nowait(stream)\n"
  },
  {
    "path": "mmdet/utils/dist_utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport functools\nimport pickle\nimport warnings\nfrom collections import OrderedDict\n\nimport numpy as np\nimport torch\nimport torch.distributed as dist\nfrom mmengine.dist import get_dist_info\nfrom torch._utils import (_flatten_dense_tensors, _take_tensors,\n                          _unflatten_dense_tensors)\n\n\ndef _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1):\n    if bucket_size_mb > 0:\n        bucket_size_bytes = bucket_size_mb * 1024 * 1024\n        buckets = _take_tensors(tensors, bucket_size_bytes)\n    else:\n        buckets = OrderedDict()\n        for tensor in tensors:\n            tp = tensor.type()\n            if tp not in buckets:\n                buckets[tp] = []\n            buckets[tp].append(tensor)\n        buckets = buckets.values()\n\n    for bucket in buckets:\n        flat_tensors = _flatten_dense_tensors(bucket)\n        dist.all_reduce(flat_tensors)\n        flat_tensors.div_(world_size)\n        for tensor, synced in zip(\n                bucket, _unflatten_dense_tensors(flat_tensors, bucket)):\n            tensor.copy_(synced)\n\n\ndef allreduce_grads(params, coalesce=True, bucket_size_mb=-1):\n    \"\"\"Allreduce gradients.\n\n    Args:\n        params (list[torch.Parameters]): List of parameters of a model\n        coalesce (bool, optional): Whether allreduce parameters as a whole.\n            Defaults to True.\n        bucket_size_mb (int, optional): Size of bucket, the unit is MB.\n            Defaults to -1.\n    \"\"\"\n    grads = [\n        param.grad.data for param in params\n        if param.requires_grad and param.grad is not None\n    ]\n    world_size = dist.get_world_size()\n    if coalesce:\n        _allreduce_coalesced(grads, world_size, bucket_size_mb)\n    else:\n        for tensor in grads:\n            dist.all_reduce(tensor.div_(world_size))\n\n\ndef reduce_mean(tensor):\n    \"\"\"\"Obtain the mean of tensor on different GPUs.\"\"\"\n    if not (dist.is_available() and dist.is_initialized()):\n        return tensor\n    tensor = tensor.clone()\n    dist.all_reduce(tensor.div_(dist.get_world_size()), op=dist.ReduceOp.SUM)\n    return tensor\n\n\ndef obj2tensor(pyobj, device='cuda'):\n    \"\"\"Serialize picklable python object to tensor.\"\"\"\n    storage = torch.ByteStorage.from_buffer(pickle.dumps(pyobj))\n    return torch.ByteTensor(storage).to(device=device)\n\n\ndef tensor2obj(tensor):\n    \"\"\"Deserialize tensor to picklable python object.\"\"\"\n    return pickle.loads(tensor.cpu().numpy().tobytes())\n\n\n@functools.lru_cache()\ndef _get_global_gloo_group():\n    \"\"\"Return a process group based on gloo backend, containing all the ranks\n    The result is cached.\"\"\"\n    if dist.get_backend() == 'nccl':\n        return dist.new_group(backend='gloo')\n    else:\n        return dist.group.WORLD\n\n\ndef all_reduce_dict(py_dict, op='sum', group=None, to_float=True):\n    \"\"\"Apply all reduce function for python dict object.\n\n    The code is modified from https://github.com/Megvii-\n    BaseDetection/YOLOX/blob/main/yolox/utils/allreduce_norm.py.\n\n    NOTE: make sure that py_dict in different ranks has the same keys and\n    the values should be in the same shape. Currently only supports\n    nccl backend.\n\n    Args:\n        py_dict (dict): Dict to be applied all reduce op.\n        op (str): Operator, could be 'sum' or 'mean'. Default: 'sum'\n        group (:obj:`torch.distributed.group`, optional): Distributed group,\n            Default: None.\n        to_float (bool): Whether to convert all values of dict to float.\n            Default: True.\n\n    Returns:\n        OrderedDict: reduced python dict object.\n    \"\"\"\n    warnings.warn(\n        'group` is deprecated. Currently only supports NCCL backend.')\n    _, world_size = get_dist_info()\n    if world_size == 1:\n        return py_dict\n\n    # all reduce logic across different devices.\n    py_key = list(py_dict.keys())\n    if not isinstance(py_dict, OrderedDict):\n        py_key_tensor = obj2tensor(py_key)\n        dist.broadcast(py_key_tensor, src=0)\n        py_key = tensor2obj(py_key_tensor)\n\n    tensor_shapes = [py_dict[k].shape for k in py_key]\n    tensor_numels = [py_dict[k].numel() for k in py_key]\n\n    if to_float:\n        warnings.warn('Note: the \"to_float\" is True, you need to '\n                      'ensure that the behavior is reasonable.')\n        flatten_tensor = torch.cat(\n            [py_dict[k].flatten().float() for k in py_key])\n    else:\n        flatten_tensor = torch.cat([py_dict[k].flatten() for k in py_key])\n\n    dist.all_reduce(flatten_tensor, op=dist.ReduceOp.SUM)\n    if op == 'mean':\n        flatten_tensor /= world_size\n\n    split_tensors = [\n        x.reshape(shape) for x, shape in zip(\n            torch.split(flatten_tensor, tensor_numels), tensor_shapes)\n    ]\n    out_dict = {k: v for k, v in zip(py_key, split_tensors)}\n    if isinstance(py_dict, OrderedDict):\n        out_dict = OrderedDict(out_dict)\n    return out_dict\n\n\ndef sync_random_seed(seed=None, device='cuda'):\n    \"\"\"Make sure different ranks share the same seed.\n\n    All workers must call this function, otherwise it will deadlock.\n    This method is generally used in `DistributedSampler`,\n    because the seed should be identical across all processes\n    in the distributed group.\n\n    In distributed sampling, different ranks should sample non-overlapped\n    data in the dataset. Therefore, this function is used to make sure that\n    each rank shuffles the data indices in the same order based\n    on the same seed. Then different ranks could use different indices\n    to select non-overlapped data from the same data list.\n\n    Args:\n        seed (int, Optional): The seed. Default to None.\n        device (str): The device where the seed will be put on.\n            Default to 'cuda'.\n\n    Returns:\n        int: Seed to be used.\n    \"\"\"\n    if seed is None:\n        seed = np.random.randint(2**31)\n    assert isinstance(seed, int)\n\n    rank, world_size = get_dist_info()\n\n    if world_size == 1:\n        return seed\n\n    if rank == 0:\n        random_num = torch.tensor(seed, dtype=torch.int32, device=device)\n    else:\n        random_num = torch.tensor(0, dtype=torch.int32, device=device)\n    dist.broadcast(random_num, src=0)\n    return random_num.item()\n"
  },
  {
    "path": "mmdet/utils/large_image.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Sequence, Tuple\n\nimport torch\nfrom mmcv.ops import batched_nms\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.structures import DetDataSample, SampleList\n\n\ndef shift_rbboxes(bboxes: torch.Tensor, offset: Sequence[int]):\n    \"\"\"Shift rotated bboxes with offset.\n\n    Args:\n        bboxes (Tensor): The rotated bboxes need to be translated.\n            With shape (n, 5), which means (x, y, w, h, a).\n        offset (Sequence[int]): The translation offsets with shape of (2, ).\n    Returns:\n        Tensor: Shifted rotated bboxes.\n    \"\"\"\n    offset_tensor = bboxes.new_tensor(offset)\n    shifted_bboxes = bboxes.clone()\n    shifted_bboxes[:, 0:2] = shifted_bboxes[:, 0:2] + offset_tensor\n    return shifted_bboxes\n\n\ndef shift_predictions(det_data_samples: SampleList,\n                      offsets: Sequence[Tuple[int, int]],\n                      src_image_shape: Tuple[int, int]) -> SampleList:\n    \"\"\"Shift predictions to the original image.\n\n    Args:\n        det_data_samples (List[:obj:`DetDataSample`]): A list of patch results.\n        offsets (Sequence[Tuple[int, int]]): Positions of the left top points\n            of patches.\n        src_image_shape (Tuple[int, int]): A (height, width) tuple of the large\n            image's width and height.\n    Returns:\n        (List[:obj:`DetDataSample`]): shifted results.\n    \"\"\"\n    try:\n        from sahi.slicing import shift_bboxes, shift_masks\n    except ImportError:\n        raise ImportError('Please run \"pip install -U sahi\" '\n                          'to install sahi first for large image inference.')\n\n    assert len(det_data_samples) == len(\n        offsets), 'The `results` should has the ' 'same length with `offsets`.'\n    shifted_predictions = []\n    for det_data_sample, offset in zip(det_data_samples, offsets):\n        pred_inst = det_data_sample.pred_instances.clone()\n\n        # Check bbox type\n        if pred_inst.bboxes.size(-1) == 4:\n            # Horizontal bboxes\n            shifted_bboxes = shift_bboxes(pred_inst.bboxes, offset)\n        elif pred_inst.bboxes.size(-1) == 5:\n            # Rotated bboxes\n            shifted_bboxes = shift_rbboxes(pred_inst.bboxes, offset)\n        else:\n            raise NotImplementedError\n\n        # shift bboxes and masks\n        pred_inst.bboxes = shifted_bboxes\n        if 'masks' in det_data_sample:\n            pred_inst.masks = shift_masks(pred_inst.masks, offset,\n                                          src_image_shape)\n\n        shifted_predictions.append(pred_inst.clone())\n\n    shifted_predictions = InstanceData.cat(shifted_predictions)\n\n    return shifted_predictions\n\n\ndef merge_results_by_nms(results: SampleList, offsets: Sequence[Tuple[int,\n                                                                      int]],\n                         src_image_shape: Tuple[int, int],\n                         nms_cfg: dict) -> DetDataSample:\n    \"\"\"Merge patch results by nms.\n\n    Args:\n        results (List[:obj:`DetDataSample`]): A list of patch results.\n        offsets (Sequence[Tuple[int, int]]): Positions of the left top points\n            of patches.\n        src_image_shape (Tuple[int, int]): A (height, width) tuple of the large\n            image's width and height.\n        nms_cfg (dict): it should specify nms type and other parameters\n            like `iou_threshold`.\n    Returns:\n        :obj:`DetDataSample`: merged results.\n    \"\"\"\n    shifted_instances = shift_predictions(results, offsets, src_image_shape)\n\n    _, keeps = batched_nms(\n        boxes=shifted_instances.bboxes,\n        scores=shifted_instances.scores,\n        idxs=shifted_instances.labels,\n        nms_cfg=nms_cfg)\n    merged_instances = shifted_instances[keeps]\n\n    merged_result = results[0].clone()\n    merged_result.pred_instances = merged_instances\n    return merged_result\n"
  },
  {
    "path": "mmdet/utils/logger.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport inspect\n\nfrom mmengine.logging import print_log\n\n\ndef get_caller_name():\n    \"\"\"Get name of caller method.\"\"\"\n    # this_func_frame = inspect.stack()[0][0]  # i.e., get_caller_name\n    # callee_frame = inspect.stack()[1][0]  # e.g., log_img_scale\n    caller_frame = inspect.stack()[2][0]  # e.g., caller of log_img_scale\n    caller_method = caller_frame.f_code.co_name\n    try:\n        caller_class = caller_frame.f_locals['self'].__class__.__name__\n        return f'{caller_class}.{caller_method}'\n    except KeyError:  # caller is a function\n        return caller_method\n\n\ndef log_img_scale(img_scale, shape_order='hw', skip_square=False):\n    \"\"\"Log image size.\n\n    Args:\n        img_scale (tuple): Image size to be logged.\n        shape_order (str, optional): The order of image shape.\n            'hw' for (height, width) and 'wh' for (width, height).\n            Defaults to 'hw'.\n        skip_square (bool, optional): Whether to skip logging for square\n            img_scale. Defaults to False.\n\n    Returns:\n        bool: Whether to have done logging.\n    \"\"\"\n    if shape_order == 'hw':\n        height, width = img_scale\n    elif shape_order == 'wh':\n        width, height = img_scale\n    else:\n        raise ValueError(f'Invalid shape_order {shape_order}.')\n\n    if skip_square and (height == width):\n        return False\n\n    caller = get_caller_name()\n    print_log(\n        f'image shape: height={height}, width={width} in {caller}',\n        logger='current')\n\n    return True\n"
  },
  {
    "path": "mmdet/utils/memory.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\nfrom collections import abc\nfrom contextlib import contextmanager\nfrom functools import wraps\n\nimport torch\nfrom mmengine.logging import MMLogger\n\n\ndef cast_tensor_type(inputs, src_type=None, dst_type=None):\n    \"\"\"Recursively convert Tensor in inputs from ``src_type`` to ``dst_type``.\n\n    Args:\n        inputs: Inputs that to be casted.\n        src_type (torch.dtype | torch.device): Source type.\n        src_type (torch.dtype | torch.device): Destination type.\n\n    Returns:\n        The same type with inputs, but all contained Tensors have been cast.\n    \"\"\"\n    assert dst_type is not None\n    if isinstance(inputs, torch.Tensor):\n        if isinstance(dst_type, torch.device):\n            # convert Tensor to dst_device\n            if hasattr(inputs, 'to') and \\\n                    hasattr(inputs, 'device') and \\\n                    (inputs.device == src_type or src_type is None):\n                return inputs.to(dst_type)\n            else:\n                return inputs\n        else:\n            # convert Tensor to dst_dtype\n            if hasattr(inputs, 'to') and \\\n                    hasattr(inputs, 'dtype') and \\\n                    (inputs.dtype == src_type or src_type is None):\n                return inputs.to(dst_type)\n            else:\n                return inputs\n        # we need to ensure that the type of inputs to be casted are the same\n        # as the argument `src_type`.\n    elif isinstance(inputs, abc.Mapping):\n        return type(inputs)({\n            k: cast_tensor_type(v, src_type=src_type, dst_type=dst_type)\n            for k, v in inputs.items()\n        })\n    elif isinstance(inputs, abc.Iterable):\n        return type(inputs)(\n            cast_tensor_type(item, src_type=src_type, dst_type=dst_type)\n            for item in inputs)\n    # TODO: Currently not supported\n    # elif isinstance(inputs, InstanceData):\n    #     for key, value in inputs.items():\n    #         inputs[key] = cast_tensor_type(\n    #             value, src_type=src_type, dst_type=dst_type)\n    #     return inputs\n    else:\n        return inputs\n\n\n@contextmanager\ndef _ignore_torch_cuda_oom():\n    \"\"\"A context which ignores CUDA OOM exception from pytorch.\n\n    Code is modified from\n    <https://github.com/facebookresearch/detectron2/blob/main/detectron2/utils/memory.py>  # noqa: E501\n    \"\"\"\n    try:\n        yield\n    except RuntimeError as e:\n        # NOTE: the string may change?\n        if 'CUDA out of memory. ' in str(e):\n            pass\n        else:\n            raise\n\n\nclass AvoidOOM:\n    \"\"\"Try to convert inputs to FP16 and CPU if got a PyTorch's CUDA Out of\n    Memory error. It will do the following steps:\n\n        1. First retry after calling `torch.cuda.empty_cache()`.\n        2. If that still fails, it will then retry by converting inputs\n          to FP16.\n        3. If that still fails trying to convert inputs to CPUs.\n          In this case, it expects the function to dispatch to\n          CPU implementation.\n\n    Args:\n        to_cpu (bool): Whether to convert outputs to CPU if get an OOM\n            error. This will slow down the code significantly.\n            Defaults to True.\n        test (bool): Skip `_ignore_torch_cuda_oom` operate that can use\n            lightweight data in unit test, only used in\n            test unit. Defaults to False.\n\n    Examples:\n        >>> from mmdet.utils.memory import AvoidOOM\n        >>> AvoidCUDAOOM = AvoidOOM()\n        >>> output = AvoidOOM.retry_if_cuda_oom(\n        >>>     some_torch_function)(input1, input2)\n        >>> # To use as a decorator\n        >>> # from mmdet.utils import AvoidCUDAOOM\n        >>> @AvoidCUDAOOM.retry_if_cuda_oom\n        >>> def function(*args, **kwargs):\n        >>>     return None\n    ```\n\n    Note:\n        1. The output may be on CPU even if inputs are on GPU. Processing\n            on CPU will slow down the code significantly.\n        2. When converting inputs to CPU, it will only look at each argument\n            and check if it has `.device` and `.to` for conversion. Nested\n            structures of tensors are not supported.\n        3. Since the function might be called more than once, it has to be\n            stateless.\n    \"\"\"\n\n    def __init__(self, to_cpu=True, test=False):\n        self.to_cpu = to_cpu\n        self.test = test\n\n    def retry_if_cuda_oom(self, func):\n        \"\"\"Makes a function retry itself after encountering pytorch's CUDA OOM\n        error.\n\n        The implementation logic is referred to\n        https://github.com/facebookresearch/detectron2/blob/main/detectron2/utils/memory.py\n\n        Args:\n            func: a stateless callable that takes tensor-like objects\n                as arguments.\n        Returns:\n            func: a callable which retries `func` if OOM is encountered.\n        \"\"\"  # noqa: W605\n\n        @wraps(func)\n        def wrapped(*args, **kwargs):\n\n            # raw function\n            if not self.test:\n                with _ignore_torch_cuda_oom():\n                    return func(*args, **kwargs)\n\n                # Clear cache and retry\n                torch.cuda.empty_cache()\n                with _ignore_torch_cuda_oom():\n                    return func(*args, **kwargs)\n\n            # get the type and device of first tensor\n            dtype, device = None, None\n            values = args + tuple(kwargs.values())\n            for value in values:\n                if isinstance(value, torch.Tensor):\n                    dtype = value.dtype\n                    device = value.device\n                    break\n            if dtype is None or device is None:\n                raise ValueError('There is no tensor in the inputs, '\n                                 'cannot get dtype and device.')\n\n            # Convert to FP16\n            fp16_args = cast_tensor_type(args, dst_type=torch.half)\n            fp16_kwargs = cast_tensor_type(kwargs, dst_type=torch.half)\n            logger = MMLogger.get_current_instance()\n            logger.warning(f'Attempting to copy inputs of {str(func)} '\n                           'to FP16 due to CUDA OOM')\n\n            # get input tensor type, the output type will same as\n            # the first parameter type.\n            with _ignore_torch_cuda_oom():\n                output = func(*fp16_args, **fp16_kwargs)\n                output = cast_tensor_type(\n                    output, src_type=torch.half, dst_type=dtype)\n                if not self.test:\n                    return output\n            logger.warning('Using FP16 still meet CUDA OOM')\n\n            # Try on CPU. This will slow down the code significantly,\n            # therefore print a notice.\n            if self.to_cpu:\n                logger.warning(f'Attempting to copy inputs of {str(func)} '\n                               'to CPU due to CUDA OOM')\n                cpu_device = torch.empty(0).device\n                cpu_args = cast_tensor_type(args, dst_type=cpu_device)\n                cpu_kwargs = cast_tensor_type(kwargs, dst_type=cpu_device)\n\n                # convert outputs to GPU\n                with _ignore_torch_cuda_oom():\n                    logger.warning(f'Convert outputs to GPU (device={device})')\n                    output = func(*cpu_args, **cpu_kwargs)\n                    output = cast_tensor_type(\n                        output, src_type=cpu_device, dst_type=device)\n                    return output\n\n                warnings.warn('Cannot convert output to GPU due to CUDA OOM, '\n                              'the output is now on CPU, which might cause '\n                              'errors if the output need to interact with GPU '\n                              'data in subsequent operations')\n                logger.warning('Cannot convert output to GPU due to '\n                               'CUDA OOM, the output is on CPU now.')\n\n                return func(*cpu_args, **cpu_kwargs)\n            else:\n                # may still get CUDA OOM error\n                return func(*args, **kwargs)\n\n        return wrapped\n\n\n# To use AvoidOOM as a decorator\nAvoidCUDAOOM = AvoidOOM()\n"
  },
  {
    "path": "mmdet/utils/misc.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport glob\nimport os\nimport os.path as osp\nimport urllib\nimport warnings\nfrom typing import Union\n\nimport torch\nfrom mmengine.config import Config, ConfigDict\nfrom mmengine.logging import print_log\nfrom mmengine.utils import scandir\n\nIMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif',\n                  '.tiff', '.webp')\n\n\ndef find_latest_checkpoint(path, suffix='pth'):\n    \"\"\"Find the latest checkpoint from the working directory.\n\n    Args:\n        path(str): The path to find checkpoints.\n        suffix(str): File extension.\n            Defaults to pth.\n\n    Returns:\n        latest_path(str | None): File path of the latest checkpoint.\n    References:\n        .. [1] https://github.com/microsoft/SoftTeacher\n                  /blob/main/ssod/utils/patch.py\n    \"\"\"\n    if not osp.exists(path):\n        warnings.warn('The path of checkpoints does not exist.')\n        return None\n    if osp.exists(osp.join(path, f'latest.{suffix}')):\n        return osp.join(path, f'latest.{suffix}')\n\n    checkpoints = glob.glob(osp.join(path, f'*.{suffix}'))\n    if len(checkpoints) == 0:\n        warnings.warn('There are no checkpoints in the path.')\n        return None\n    latest = -1\n    latest_path = None\n    for checkpoint in checkpoints:\n        count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0])\n        if count > latest:\n            latest = count\n            latest_path = checkpoint\n    return latest_path\n\n\ndef update_data_root(cfg, logger=None):\n    \"\"\"Update data root according to env MMDET_DATASETS.\n\n    If set env MMDET_DATASETS, update cfg.data_root according to\n    MMDET_DATASETS. Otherwise, using cfg.data_root as default.\n\n    Args:\n        cfg (:obj:`Config`): The model config need to modify\n        logger (logging.Logger | str | None): the way to print msg\n    \"\"\"\n    assert isinstance(cfg, Config), \\\n        f'cfg got wrong type: {type(cfg)}, expected mmengine.Config'\n\n    if 'MMDET_DATASETS' in os.environ:\n        dst_root = os.environ['MMDET_DATASETS']\n        print_log(f'MMDET_DATASETS has been set to be {dst_root}.'\n                  f'Using {dst_root} as data root.')\n    else:\n        return\n\n    assert isinstance(cfg, Config), \\\n        f'cfg got wrong type: {type(cfg)}, expected mmengine.Config'\n\n    def update(cfg, src_str, dst_str):\n        for k, v in cfg.items():\n            if isinstance(v, ConfigDict):\n                update(cfg[k], src_str, dst_str)\n            if isinstance(v, str) and src_str in v:\n                cfg[k] = v.replace(src_str, dst_str)\n\n    update(cfg.data, cfg.data_root, dst_root)\n    cfg.data_root = dst_root\n\n\ndef get_test_pipeline_cfg(cfg: Union[str, ConfigDict]) -> ConfigDict:\n    \"\"\"Get the test dataset pipeline from entire config.\n\n    Args:\n        cfg (str or :obj:`ConfigDict`): the entire config. Can be a config\n            file or a ``ConfigDict``.\n\n    Returns:\n        :obj:`ConfigDict`: the config of test dataset.\n    \"\"\"\n    if isinstance(cfg, str):\n        cfg = Config.fromfile(cfg)\n\n    def _get_test_pipeline_cfg(dataset_cfg):\n        if 'pipeline' in dataset_cfg:\n            return dataset_cfg.pipeline\n        # handle dataset wrapper\n        elif 'dataset' in dataset_cfg:\n            return _get_test_pipeline_cfg(dataset_cfg.dataset)\n        # handle dataset wrappers like ConcatDataset\n        elif 'datasets' in dataset_cfg:\n            return _get_test_pipeline_cfg(dataset_cfg.datasets[0])\n\n        raise RuntimeError('Cannot find `pipeline` in `test_dataloader`')\n\n    return _get_test_pipeline_cfg(cfg.test_dataloader.dataset)\n\n\ndef get_file_list(source_root: str) -> [list, dict]:\n    \"\"\"Get file list.\n\n    Args:\n        source_root (str): image or video source path\n\n    Return:\n        source_file_path_list (list): A list for all source file.\n        source_type (dict): Source type: file or url or dir.\n    \"\"\"\n    is_dir = os.path.isdir(source_root)\n    is_url = source_root.startswith(('http:/', 'https:/'))\n    is_file = os.path.splitext(source_root)[-1].lower() in IMG_EXTENSIONS\n\n    source_file_path_list = []\n    if is_dir:\n        # when input source is dir\n        for file in scandir(source_root, IMG_EXTENSIONS, recursive=True):\n            source_file_path_list.append(os.path.join(source_root, file))\n    elif is_url:\n        # when input source is url\n        filename = os.path.basename(\n            urllib.parse.unquote(source_root).split('?')[0])\n        file_save_path = os.path.join(os.getcwd(), filename)\n        print(f'Downloading source file to {file_save_path}')\n        torch.hub.download_url_to_file(source_root, file_save_path)\n        source_file_path_list = [file_save_path]\n    elif is_file:\n        # when input source is single image\n        source_file_path_list = [source_root]\n    else:\n        print('Cannot find image file.')\n\n    source_type = dict(is_dir=is_dir, is_url=is_url, is_file=is_file)\n\n    return source_file_path_list, source_type\n"
  },
  {
    "path": "mmdet/utils/mot_error_visualize.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom typing import Union\n\ntry:\n    import seaborn as sns\nexcept ImportError:\n    sns = None\nimport cv2\nimport matplotlib.pyplot as plt\nimport mmcv\nimport numpy as np\nfrom matplotlib.patches import Rectangle\nfrom mmengine.utils import mkdir_or_exist\n\n\ndef imshow_mot_errors(*args, backend: str = 'cv2', **kwargs):\n    \"\"\"Show the wrong tracks on the input image.\n\n    Args:\n        backend (str, optional): Backend of visualization.\n            Defaults to 'cv2'.\n    \"\"\"\n    if backend == 'cv2':\n        return _cv2_show_wrong_tracks(*args, **kwargs)\n    elif backend == 'plt':\n        return _plt_show_wrong_tracks(*args, **kwargs)\n    else:\n        raise NotImplementedError()\n\n\ndef _cv2_show_wrong_tracks(img: Union[str, np.ndarray],\n                           bboxes: np.ndarray,\n                           ids: np.ndarray,\n                           error_types: np.ndarray,\n                           thickness: int = 2,\n                           font_scale: float = 0.4,\n                           text_width: int = 10,\n                           text_height: int = 15,\n                           show: bool = False,\n                           wait_time: int = 100,\n                           out_file: str = None) -> np.ndarray:\n    \"\"\"Show the wrong tracks with opencv.\n\n    Args:\n        img (str or ndarray): The image to be displayed.\n        bboxes (ndarray): A ndarray of shape (k, 5).\n        ids (ndarray): A ndarray of shape (k, ).\n        error_types (ndarray): A ndarray of shape (k, ), where 0 denotes\n            false positives, 1 denotes false negative and 2 denotes ID switch.\n        thickness (int, optional): Thickness of lines.\n            Defaults to 2.\n        font_scale (float, optional): Font scale to draw id and score.\n            Defaults to 0.4.\n        text_width (int, optional): Width to draw id and score.\n            Defaults to 10.\n        text_height (int, optional): Height to draw id and score.\n            Defaults to 15.\n        show (bool, optional): Whether to show the image on the fly.\n            Defaults to False.\n        wait_time (int, optional): Value of waitKey param.\n            Defaults to 100.\n        out_file (str, optional): The filename to write the image.\n            Defaults to None.\n\n    Returns:\n        ndarray: Visualized image.\n    \"\"\"\n    if sns is None:\n        raise ImportError('please run pip install seaborn')\n    assert bboxes.ndim == 2, \\\n        f' bboxes ndim should be 2, but its ndim is {bboxes.ndim}.'\n    assert ids.ndim == 1, \\\n        f' ids ndim should be 1, but its ndim is {ids.ndim}.'\n    assert error_types.ndim == 1, \\\n        f' error_types ndim should be 1, but its ndim is {error_types.ndim}.'\n    assert bboxes.shape[0] == ids.shape[0], \\\n        'bboxes.shape[0] and ids.shape[0] should have the same length.'\n    assert bboxes.shape[1] == 5, \\\n        f' bboxes.shape[1] should be 5, but its {bboxes.shape[1]}.'\n\n    bbox_colors = sns.color_palette()\n    # red, yellow, blue\n    bbox_colors = [bbox_colors[3], bbox_colors[1], bbox_colors[0]]\n    bbox_colors = [[int(255 * _c) for _c in bbox_color][::-1]\n                   for bbox_color in bbox_colors]\n\n    if isinstance(img, str):\n        img = mmcv.imread(img)\n    else:\n        assert img.ndim == 3\n\n    img_shape = img.shape\n    bboxes[:, 0::2] = np.clip(bboxes[:, 0::2], 0, img_shape[1])\n    bboxes[:, 1::2] = np.clip(bboxes[:, 1::2], 0, img_shape[0])\n\n    for bbox, error_type, id in zip(bboxes, error_types, ids):\n        x1, y1, x2, y2 = bbox[:4].astype(np.int32)\n        score = float(bbox[-1])\n\n        # bbox\n        bbox_color = bbox_colors[error_type]\n        cv2.rectangle(img, (x1, y1), (x2, y2), bbox_color, thickness=thickness)\n\n        # FN does not have id and score\n        if error_type == 1:\n            continue\n\n        # score\n        text = '{:.02f}'.format(score)\n        width = (len(text) - 1) * text_width\n        img[y1:y1 + text_height, x1:x1 + width, :] = bbox_color\n        cv2.putText(\n            img,\n            text, (x1, y1 + text_height - 2),\n            cv2.FONT_HERSHEY_COMPLEX,\n            font_scale,\n            color=(0, 0, 0))\n\n        # id\n        text = str(id)\n        width = len(text) * text_width\n        img[y1 + text_height:y1 + text_height * 2,\n            x1:x1 + width, :] = bbox_color\n        cv2.putText(\n            img,\n            str(id), (x1, y1 + text_height * 2 - 2),\n            cv2.FONT_HERSHEY_COMPLEX,\n            font_scale,\n            color=(0, 0, 0))\n\n    if show:\n        mmcv.imshow(img, wait_time=wait_time)\n    if out_file is not None:\n        mmcv.imwrite(img, out_file)\n\n    return img\n\n\ndef _plt_show_wrong_tracks(img: Union[str, np.ndarray],\n                           bboxes: np.ndarray,\n                           ids: np.ndarray,\n                           error_types: np.ndarray,\n                           thickness: float = 0.1,\n                           font_scale: float = 3.0,\n                           text_width: int = 8,\n                           text_height: int = 13,\n                           show: bool = False,\n                           wait_time: int = 100,\n                           out_file: str = None) -> np.ndarray:\n    \"\"\"Show the wrong tracks with matplotlib.\n\n    Args:\n        img (str or ndarray): The image to be displayed.\n        bboxes (ndarray): A ndarray of shape (k, 5).\n        ids (ndarray): A ndarray of shape (k, ).\n        error_types (ndarray): A ndarray of shape (k, ), where 0 denotes\n            false positives, 1 denotes false negative and 2 denotes ID switch.\n        thickness (float, optional): Thickness of lines.\n            Defaults to 0.1.\n        font_scale (float, optional): Font scale to draw id and score.\n            Defaults to 3.0.\n        text_width (int, optional): Width to draw id and score.\n            Defaults to 8.\n        text_height (int, optional): Height to draw id and score.\n            Defaults to 13.\n        show (bool, optional): Whether to show the image on the fly.\n            Defaults to False.\n        wait_time (int, optional): Value of waitKey param.\n            Defaults to 100.\n        out_file (str, optional): The filename to write the image.\n            Defaults to None.\n\n    Returns:\n        ndarray: Original image.\n    \"\"\"\n    assert bboxes.ndim == 2, \\\n        f' bboxes ndim should be 2, but its ndim is {bboxes.ndim}.'\n    assert ids.ndim == 1, \\\n        f' ids ndim should be 1, but its ndim is {ids.ndim}.'\n    assert error_types.ndim == 1, \\\n        f' error_types ndim should be 1, but its ndim is {error_types.ndim}.'\n    assert bboxes.shape[0] == ids.shape[0], \\\n        'bboxes.shape[0] and ids.shape[0] should have the same length.'\n    assert bboxes.shape[1] == 5, \\\n        f' bboxes.shape[1] should be 5, but its {bboxes.shape[1]}.'\n\n    bbox_colors = sns.color_palette()\n    # red, yellow, blue\n    bbox_colors = [bbox_colors[3], bbox_colors[1], bbox_colors[0]]\n\n    if isinstance(img, str):\n        img = plt.imread(img)\n    else:\n        assert img.ndim == 3\n        img = mmcv.bgr2rgb(img)\n\n    img_shape = img.shape\n    bboxes[:, 0::2] = np.clip(bboxes[:, 0::2], 0, img_shape[1])\n    bboxes[:, 1::2] = np.clip(bboxes[:, 1::2], 0, img_shape[0])\n\n    plt.imshow(img)\n    plt.gca().set_axis_off()\n    plt.autoscale(False)\n    plt.subplots_adjust(\n        top=1, bottom=0, right=1, left=0, hspace=None, wspace=None)\n    plt.margins(0, 0)\n    plt.gca().xaxis.set_major_locator(plt.NullLocator())\n    plt.gca().yaxis.set_major_locator(plt.NullLocator())\n    plt.rcParams['figure.figsize'] = img_shape[1], img_shape[0]\n\n    for bbox, error_type, id in zip(bboxes, error_types, ids):\n        x1, y1, x2, y2, score = bbox\n        w, h = int(x2 - x1), int(y2 - y1)\n        left_top = (int(x1), int(y1))\n\n        # bbox\n        plt.gca().add_patch(\n            Rectangle(\n                left_top,\n                w,\n                h,\n                thickness,\n                edgecolor=bbox_colors[error_type],\n                facecolor='none'))\n\n        # FN does not have id and score\n        if error_type == 1:\n            continue\n\n        # score\n        text = '{:.02f}'.format(score)\n        width = len(text) * text_width\n        plt.gca().add_patch(\n            Rectangle((left_top[0], left_top[1]),\n                      width,\n                      text_height,\n                      thickness,\n                      edgecolor=bbox_colors[error_type],\n                      facecolor=bbox_colors[error_type]))\n\n        plt.text(\n            left_top[0],\n            left_top[1] + text_height + 2,\n            text,\n            fontsize=font_scale)\n\n        # id\n        text = str(id)\n        width = len(text) * text_width\n        plt.gca().add_patch(\n            Rectangle((left_top[0], left_top[1] + text_height + 1),\n                      width,\n                      text_height,\n                      thickness,\n                      edgecolor=bbox_colors[error_type],\n                      facecolor=bbox_colors[error_type]))\n        plt.text(\n            left_top[0],\n            left_top[1] + 2 * (text_height + 1),\n            text,\n            fontsize=font_scale)\n\n    if out_file is not None:\n        mkdir_or_exist(osp.abspath(osp.dirname(out_file)))\n        plt.savefig(out_file, dpi=300, bbox_inches='tight', pad_inches=0.0)\n\n    if show:\n        plt.draw()\n        plt.pause(wait_time / 1000.)\n\n    plt.clf()\n    return img\n"
  },
  {
    "path": "mmdet/utils/profiling.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport contextlib\nimport sys\nimport time\n\nimport torch\n\nif sys.version_info >= (3, 7):\n\n    @contextlib.contextmanager\n    def profile_time(trace_name,\n                     name,\n                     enabled=True,\n                     stream=None,\n                     end_stream=None):\n        \"\"\"Print time spent by CPU and GPU.\n\n        Useful as a temporary context manager to find sweet spots of code\n        suitable for async implementation.\n        \"\"\"\n        if (not enabled) or not torch.cuda.is_available():\n            yield\n            return\n        stream = stream if stream else torch.cuda.current_stream()\n        end_stream = end_stream if end_stream else stream\n        start = torch.cuda.Event(enable_timing=True)\n        end = torch.cuda.Event(enable_timing=True)\n        stream.record_event(start)\n        try:\n            cpu_start = time.monotonic()\n            yield\n        finally:\n            cpu_end = time.monotonic()\n            end_stream.record_event(end)\n            end.synchronize()\n            cpu_time = (cpu_end - cpu_start) * 1000\n            gpu_time = start.elapsed_time(end)\n            msg = f'{trace_name} {name} cpu_time {cpu_time:.2f} ms '\n            msg += f'gpu_time {gpu_time:.2f} ms stream {stream}'\n            print(msg, end_stream)\n"
  },
  {
    "path": "mmdet/utils/replace_cfg_vals.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport re\n\nfrom mmengine.config import Config\n\n\ndef replace_cfg_vals(ori_cfg):\n    \"\"\"Replace the string \"${key}\" with the corresponding value.\n\n    Replace the \"${key}\" with the value of ori_cfg.key in the config. And\n    support replacing the chained ${key}. Such as, replace \"${key0.key1}\"\n    with the value of cfg.key0.key1. Code is modified from `vars.py\n    < https://github.com/microsoft/SoftTeacher/blob/main/ssod/utils/vars.py>`_  # noqa: E501\n\n    Args:\n        ori_cfg (mmengine.config.Config):\n            The origin config with \"${key}\" generated from a file.\n\n    Returns:\n        updated_cfg [mmengine.config.Config]:\n            The config with \"${key}\" replaced by the corresponding value.\n    \"\"\"\n\n    def get_value(cfg, key):\n        for k in key.split('.'):\n            cfg = cfg[k]\n        return cfg\n\n    def replace_value(cfg):\n        if isinstance(cfg, dict):\n            return {key: replace_value(value) for key, value in cfg.items()}\n        elif isinstance(cfg, list):\n            return [replace_value(item) for item in cfg]\n        elif isinstance(cfg, tuple):\n            return tuple([replace_value(item) for item in cfg])\n        elif isinstance(cfg, str):\n            # the format of string cfg may be:\n            # 1) \"${key}\", which will be replaced with cfg.key directly\n            # 2) \"xxx${key}xxx\" or \"xxx${key1}xxx${key2}xxx\",\n            # which will be replaced with the string of the cfg.key\n            keys = pattern_key.findall(cfg)\n            values = [get_value(ori_cfg, key[2:-1]) for key in keys]\n            if len(keys) == 1 and keys[0] == cfg:\n                # the format of string cfg is \"${key}\"\n                cfg = values[0]\n            else:\n                for key, value in zip(keys, values):\n                    # the format of string cfg is\n                    # \"xxx${key}xxx\" or \"xxx${key1}xxx${key2}xxx\"\n                    assert not isinstance(value, (dict, list, tuple)), \\\n                        f'for the format of string cfg is ' \\\n                        f\"'xxxxx${key}xxxxx' or 'xxx${key}xxx${key}xxx', \" \\\n                        f\"the type of the value of '${key}' \" \\\n                        f'can not be dict, list, or tuple' \\\n                        f'but you input {type(value)} in {cfg}'\n                    cfg = cfg.replace(key, str(value))\n            return cfg\n        else:\n            return cfg\n\n    # the pattern of string \"${key}\"\n    pattern_key = re.compile(r'\\$\\{[a-zA-Z\\d_.]*\\}')\n    # the type of ori_cfg._cfg_dict is mmengine.config.ConfigDict\n    updated_cfg = Config(\n        replace_value(ori_cfg._cfg_dict), filename=ori_cfg.filename)\n    # replace the model with model_wrapper\n    if updated_cfg.get('model_wrapper', None) is not None:\n        updated_cfg.model = updated_cfg.model_wrapper\n        updated_cfg.pop('model_wrapper')\n    return updated_cfg\n"
  },
  {
    "path": "mmdet/utils/setup_env.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport datetime\nimport logging\nimport os\nimport platform\nimport warnings\n\nimport cv2\nimport torch.multiprocessing as mp\nfrom mmengine import DefaultScope\nfrom mmengine.logging import print_log\nfrom mmengine.utils import digit_version\n\n\ndef setup_cache_size_limit_of_dynamo():\n    \"\"\"Setup cache size limit of dynamo.\n\n    Note: Due to the dynamic shape of the loss calculation and\n    post-processing parts in the object detection algorithm, these\n    functions must be compiled every time they are run.\n    Setting a large value for torch._dynamo.config.cache_size_limit\n    may result in repeated compilation, which can slow down training\n    and testing speed. Therefore, we need to set the default value of\n    cache_size_limit smaller. An empirical value is 4.\n    \"\"\"\n\n    import torch\n    if digit_version(torch.__version__) >= digit_version('2.0.0'):\n        if 'DYNAMO_CACHE_SIZE_LIMIT' in os.environ:\n            import torch._dynamo\n            cache_size_limit = int(os.environ['DYNAMO_CACHE_SIZE_LIMIT'])\n            torch._dynamo.config.cache_size_limit = cache_size_limit\n            print_log(\n                f'torch._dynamo.config.cache_size_limit is force '\n                f'set to {cache_size_limit}.',\n                logger='current',\n                level=logging.WARNING)\n\n\ndef setup_multi_processes(cfg):\n    \"\"\"Setup multi-processing environment variables.\"\"\"\n    # set multi-process start method as `fork` to speed up the training\n    if platform.system() != 'Windows':\n        mp_start_method = cfg.get('mp_start_method', 'fork')\n        current_method = mp.get_start_method(allow_none=True)\n        if current_method is not None and current_method != mp_start_method:\n            warnings.warn(\n                f'Multi-processing start method `{mp_start_method}` is '\n                f'different from the previous setting `{current_method}`.'\n                f'It will be force set to `{mp_start_method}`. You can change '\n                f'this behavior by changing `mp_start_method` in your config.')\n        mp.set_start_method(mp_start_method, force=True)\n\n    # disable opencv multithreading to avoid system being overloaded\n    opencv_num_threads = cfg.get('opencv_num_threads', 0)\n    cv2.setNumThreads(opencv_num_threads)\n\n    # setup OMP threads\n    # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py  # noqa\n    workers_per_gpu = cfg.data.get('workers_per_gpu', 1)\n    if 'train_dataloader' in cfg.data:\n        workers_per_gpu = \\\n            max(cfg.data.train_dataloader.get('workers_per_gpu', 1),\n                workers_per_gpu)\n\n    if 'OMP_NUM_THREADS' not in os.environ and workers_per_gpu > 1:\n        omp_num_threads = 1\n        warnings.warn(\n            f'Setting OMP_NUM_THREADS environment variable for each process '\n            f'to be {omp_num_threads} in default, to avoid your system being '\n            f'overloaded, please further tune the variable for optimal '\n            f'performance in your application as needed.')\n        os.environ['OMP_NUM_THREADS'] = str(omp_num_threads)\n\n    # setup MKL threads\n    if 'MKL_NUM_THREADS' not in os.environ and workers_per_gpu > 1:\n        mkl_num_threads = 1\n        warnings.warn(\n            f'Setting MKL_NUM_THREADS environment variable for each process '\n            f'to be {mkl_num_threads} in default, to avoid your system being '\n            f'overloaded, please further tune the variable for optimal '\n            f'performance in your application as needed.')\n        os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads)\n\n\ndef register_all_modules(init_default_scope: bool = True) -> None:\n    \"\"\"Register all modules in mmdet into the registries.\n\n    Args:\n        init_default_scope (bool): Whether initialize the mmdet default scope.\n            When `init_default_scope=True`, the global default scope will be\n            set to `mmdet`, and all registries will build modules from mmdet's\n            registry node. To understand more about the registry, please refer\n            to https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/registry.md\n            Defaults to True.\n    \"\"\"  # noqa\n    import mmdet.datasets  # noqa: F401,F403\n    import mmdet.engine  # noqa: F401,F403\n    import mmdet.evaluation  # noqa: F401,F403\n    import mmdet.models  # noqa: F401,F403\n    import mmdet.visualization  # noqa: F401,F403\n\n    if init_default_scope:\n        never_created = DefaultScope.get_current_instance() is None \\\n                        or not DefaultScope.check_instance_created('mmdet')\n        if never_created:\n            DefaultScope.get_instance('mmdet', scope_name='mmdet')\n            return\n        current_scope = DefaultScope.get_current_instance()\n        if current_scope.scope_name != 'mmdet':\n            warnings.warn('The current default scope '\n                          f'\"{current_scope.scope_name}\" is not \"mmdet\", '\n                          '`register_all_modules` will force the current'\n                          'default scope to be \"mmdet\". If this is not '\n                          'expected, please set `init_default_scope=False`.')\n            # avoid name conflict\n            new_instance_name = f'mmdet-{datetime.datetime.now()}'\n            DefaultScope.get_instance(new_instance_name, scope_name='mmdet')\n"
  },
  {
    "path": "mmdet/utils/split_batch.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\n\n\ndef split_batch(img, img_metas, kwargs):\n    \"\"\"Split data_batch by tags.\n\n    Code is modified from\n    <https://github.com/microsoft/SoftTeacher/blob/main/ssod/utils/structure_utils.py> # noqa: E501\n\n    Args:\n        img (Tensor): of shape (N, C, H, W) encoding input images.\n            Typically these should be mean centered and std scaled.\n        img_metas (list[dict]): List of image info dict where each dict\n            has: 'img_shape', 'scale_factor', 'flip', and may also contain\n            'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.\n            For details on the values of these keys, see\n            :class:`mmdet.datasets.pipelines.Collect`.\n        kwargs (dict): Specific to concrete implementation.\n\n    Returns:\n        data_groups (dict): a dict that data_batch splited by tags,\n            such as 'sup', 'unsup_teacher', and 'unsup_student'.\n    \"\"\"\n\n    # only stack img in the batch\n    def fuse_list(obj_list, obj):\n        return torch.stack(obj_list) if isinstance(obj,\n                                                   torch.Tensor) else obj_list\n\n    # select data with tag from data_batch\n    def select_group(data_batch, current_tag):\n        group_flag = [tag == current_tag for tag in data_batch['tag']]\n        return {\n            k: fuse_list([vv for vv, gf in zip(v, group_flag) if gf], v)\n            for k, v in data_batch.items()\n        }\n\n    kwargs.update({'img': img, 'img_metas': img_metas})\n    kwargs.update({'tag': [meta['tag'] for meta in img_metas]})\n    tags = list(set(kwargs['tag']))\n    data_groups = {tag: select_group(kwargs, tag) for tag in tags}\n    for tag, group in data_groups.items():\n        group.pop('tag')\n    return data_groups\n"
  },
  {
    "path": "mmdet/utils/typing_utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Collecting some commonly used type hint in mmdetection.\"\"\"\nfrom typing import List, Optional, Sequence, Tuple, Union\n\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData, PixelData\n\n# TODO: Need to avoid circular import with assigner and sampler\n# Type hint of config data\nConfigType = Union[ConfigDict, dict]\nOptConfigType = Optional[ConfigType]\n# Type hint of one or more config data\nMultiConfig = Union[ConfigType, List[ConfigType]]\nOptMultiConfig = Optional[MultiConfig]\n\nInstanceList = List[InstanceData]\nOptInstanceList = Optional[InstanceList]\n\nPixelList = List[PixelData]\nOptPixelList = Optional[PixelList]\n\nRangeType = Sequence[Tuple[int, int]]\n"
  },
  {
    "path": "mmdet/utils/util_mixins.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"This module defines the :class:`NiceRepr` mixin class, which defines a\n``__repr__`` and ``__str__`` method that only depend on a custom ``__nice__``\nmethod, which you must define. This means you only have to overload one\nfunction instead of two.  Furthermore, if the object defines a ``__len__``\nmethod, then the ``__nice__`` method defaults to something sensible, otherwise\nit is treated as abstract and raises ``NotImplementedError``.\n\nTo use simply have your object inherit from :class:`NiceRepr`\n(multi-inheritance should be ok).\n\nThis code was copied from the ubelt library: https://github.com/Erotemic/ubelt\n\nExample:\n    >>> # Objects that define __nice__ have a default __str__ and __repr__\n    >>> class Student(NiceRepr):\n    ...    def __init__(self, name):\n    ...        self.name = name\n    ...    def __nice__(self):\n    ...        return self.name\n    >>> s1 = Student('Alice')\n    >>> s2 = Student('Bob')\n    >>> print(f's1 = {s1}')\n    >>> print(f's2 = {s2}')\n    s1 = <Student(Alice)>\n    s2 = <Student(Bob)>\n\nExample:\n    >>> # Objects that define __len__ have a default __nice__\n    >>> class Group(NiceRepr):\n    ...    def __init__(self, data):\n    ...        self.data = data\n    ...    def __len__(self):\n    ...        return len(self.data)\n    >>> g = Group([1, 2, 3])\n    >>> print(f'g = {g}')\n    g = <Group(3)>\n\"\"\"\nimport warnings\n\n\nclass NiceRepr:\n    \"\"\"Inherit from this class and define ``__nice__`` to \"nicely\" print your\n    objects.\n\n    Defines ``__str__`` and ``__repr__`` in terms of ``__nice__`` function\n    Classes that inherit from :class:`NiceRepr` should redefine ``__nice__``.\n    If the inheriting class has a ``__len__``, method then the default\n    ``__nice__`` method will return its length.\n\n    Example:\n        >>> class Foo(NiceRepr):\n        ...    def __nice__(self):\n        ...        return 'info'\n        >>> foo = Foo()\n        >>> assert str(foo) == '<Foo(info)>'\n        >>> assert repr(foo).startswith('<Foo(info) at ')\n\n    Example:\n        >>> class Bar(NiceRepr):\n        ...    pass\n        >>> bar = Bar()\n        >>> import pytest\n        >>> with pytest.warns(None) as record:\n        >>>     assert 'object at' in str(bar)\n        >>>     assert 'object at' in repr(bar)\n\n    Example:\n        >>> class Baz(NiceRepr):\n        ...    def __len__(self):\n        ...        return 5\n        >>> baz = Baz()\n        >>> assert str(baz) == '<Baz(5)>'\n    \"\"\"\n\n    def __nice__(self):\n        \"\"\"str: a \"nice\" summary string describing this module\"\"\"\n        if hasattr(self, '__len__'):\n            # It is a common pattern for objects to use __len__ in __nice__\n            # As a convenience we define a default __nice__ for these objects\n            return str(len(self))\n        else:\n            # In all other cases force the subclass to overload __nice__\n            raise NotImplementedError(\n                f'Define the __nice__ method for {self.__class__!r}')\n\n    def __repr__(self):\n        \"\"\"str: the string of the module\"\"\"\n        try:\n            nice = self.__nice__()\n            classname = self.__class__.__name__\n            return f'<{classname}({nice}) at {hex(id(self))}>'\n        except NotImplementedError as ex:\n            warnings.warn(str(ex), category=RuntimeWarning)\n            return object.__repr__(self)\n\n    def __str__(self):\n        \"\"\"str: the string of the module\"\"\"\n        try:\n            classname = self.__class__.__name__\n            nice = self.__nice__()\n            return f'<{classname}({nice})>'\n        except NotImplementedError as ex:\n            warnings.warn(str(ex), category=RuntimeWarning)\n            return object.__repr__(self)\n"
  },
  {
    "path": "mmdet/utils/util_random.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Helpers for random number generators.\"\"\"\nimport numpy as np\n\n\ndef ensure_rng(rng=None):\n    \"\"\"Coerces input into a random number generator.\n\n    If the input is None, then a global random state is returned.\n\n    If the input is a numeric value, then that is used as a seed to construct a\n    random state. Otherwise the input is returned as-is.\n\n    Adapted from [1]_.\n\n    Args:\n        rng (int | numpy.random.RandomState | None):\n            if None, then defaults to the global rng. Otherwise this can be an\n            integer or a RandomState class\n    Returns:\n        (numpy.random.RandomState) : rng -\n            a numpy random number generator\n\n    References:\n        .. [1] https://gitlab.kitware.com/computer-vision/kwarray/blob/master/kwarray/util_random.py#L270  # noqa: E501\n    \"\"\"\n\n    if rng is None:\n        rng = np.random.mtrand._rand\n    elif isinstance(rng, int):\n        rng = np.random.RandomState(rng)\n    else:\n        rng = rng\n    return rng\n"
  },
  {
    "path": "mmdet/version.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n__version__ = '3.3.0'\nshort_version = __version__\n\n\ndef parse_version_info(version_str):\n    \"\"\"Parse a version string into a tuple.\n\n    Args:\n        version_str (str): The version string.\n    Returns:\n        tuple[int | str]: The version info, e.g., \"1.3.0\" is parsed into\n            (1, 3, 0), and \"2.0.0rc1\" is parsed into (2, 0, 0, 'rc1').\n    \"\"\"\n    version_info = []\n    for x in version_str.split('.'):\n        if x.isdigit():\n            version_info.append(int(x))\n        elif x.find('rc') != -1:\n            patch_version = x.split('rc')\n            version_info.append(int(patch_version[0]))\n            version_info.append(f'rc{patch_version[1]}')\n    return tuple(version_info)\n\n\nversion_info = parse_version_info(__version__)\n"
  },
  {
    "path": "mmdet/visualization/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .local_visualizer import DetLocalVisualizer, TrackLocalVisualizer\nfrom .palette import get_palette, jitter_color, palette_val\n\n__all__ = [\n    'palette_val', 'get_palette', 'DetLocalVisualizer', 'jitter_color',\n    'TrackLocalVisualizer'\n]\n"
  },
  {
    "path": "mmdet/visualization/local_visualizer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List, Optional, Tuple, Union\n\nimport cv2\nimport mmcv\nimport numpy as np\n\ntry:\n    import seaborn as sns\nexcept ImportError:\n    sns = None\nimport torch\nfrom mmengine.dist import master_only\nfrom mmengine.structures import InstanceData, PixelData\nfrom mmengine.visualization import Visualizer\n\nfrom ..evaluation import INSTANCE_OFFSET\nfrom ..registry import VISUALIZERS\nfrom ..structures import DetDataSample\nfrom ..structures.mask import BitmapMasks, PolygonMasks, bitmap_to_polygon\nfrom .palette import _get_adaptive_scales, get_palette, jitter_color\n\n\n@VISUALIZERS.register_module()\nclass DetLocalVisualizer(Visualizer):\n    \"\"\"MMDetection Local Visualizer.\n\n    Args:\n        name (str): Name of the instance. Defaults to 'visualizer'.\n        image (np.ndarray, optional): the origin image to draw. The format\n            should be RGB. Defaults to None.\n        vis_backends (list, optional): Visual backend config list.\n            Defaults to None.\n        save_dir (str, optional): Save file dir for all storage backends.\n            If it is None, the backend storage will not save any data.\n        bbox_color (str, tuple(int), optional): Color of bbox lines.\n            The tuple of color should be in BGR order. Defaults to None.\n        text_color (str, tuple(int), optional): Color of texts.\n            The tuple of color should be in BGR order.\n            Defaults to (200, 200, 200).\n        mask_color (str, tuple(int), optional): Color of masks.\n            The tuple of color should be in BGR order.\n            Defaults to None.\n        line_width (int, float): The linewidth of lines.\n            Defaults to 3.\n        alpha (int, float): The transparency of bboxes or mask.\n            Defaults to 0.8.\n\n    Examples:\n        >>> import numpy as np\n        >>> import torch\n        >>> from mmengine.structures import InstanceData\n        >>> from mmdet.structures import DetDataSample\n        >>> from mmdet.visualization import DetLocalVisualizer\n\n        >>> det_local_visualizer = DetLocalVisualizer()\n        >>> image = np.random.randint(0, 256,\n        ...                     size=(10, 12, 3)).astype('uint8')\n        >>> gt_instances = InstanceData()\n        >>> gt_instances.bboxes = torch.Tensor([[1, 2, 2, 5]])\n        >>> gt_instances.labels = torch.randint(0, 2, (1,))\n        >>> gt_det_data_sample = DetDataSample()\n        >>> gt_det_data_sample.gt_instances = gt_instances\n        >>> det_local_visualizer.add_datasample('image', image,\n        ...                         gt_det_data_sample)\n        >>> det_local_visualizer.add_datasample(\n        ...                       'image', image, gt_det_data_sample,\n        ...                        out_file='out_file.jpg')\n        >>> det_local_visualizer.add_datasample(\n        ...                        'image', image, gt_det_data_sample,\n        ...                         show=True)\n        >>> pred_instances = InstanceData()\n        >>> pred_instances.bboxes = torch.Tensor([[2, 4, 4, 8]])\n        >>> pred_instances.labels = torch.randint(0, 2, (1,))\n        >>> pred_det_data_sample = DetDataSample()\n        >>> pred_det_data_sample.pred_instances = pred_instances\n        >>> det_local_visualizer.add_datasample('image', image,\n        ...                         gt_det_data_sample,\n        ...                         pred_det_data_sample)\n    \"\"\"\n\n    def __init__(self,\n                 name: str = 'visualizer',\n                 image: Optional[np.ndarray] = None,\n                 vis_backends: Optional[Dict] = None,\n                 save_dir: Optional[str] = None,\n                 bbox_color: Optional[Union[str, Tuple[int]]] = None,\n                 text_color: Optional[Union[str,\n                                            Tuple[int]]] = (200, 200, 200),\n                 mask_color: Optional[Union[str, Tuple[int]]] = None,\n                 line_width: Union[int, float] = 3,\n                 alpha: float = 0.8) -> None:\n        super().__init__(\n            name=name,\n            image=image,\n            vis_backends=vis_backends,\n            save_dir=save_dir)\n        self.bbox_color = bbox_color\n        self.text_color = text_color\n        self.mask_color = mask_color\n        self.line_width = line_width\n        self.alpha = alpha\n        # Set default value. When calling\n        # `DetLocalVisualizer().dataset_meta=xxx`,\n        # it will override the default value.\n        self.dataset_meta = {}\n\n    def _draw_instances(self, image: np.ndarray, instances: ['InstanceData'],\n                        classes: Optional[List[str]],\n                        palette: Optional[List[tuple]]) -> np.ndarray:\n        \"\"\"Draw instances of GT or prediction.\n\n        Args:\n            image (np.ndarray): The image to draw.\n            instances (:obj:`InstanceData`): Data structure for\n                instance-level annotations or predictions.\n            classes (List[str], optional): Category information.\n            palette (List[tuple], optional): Palette information\n                corresponding to the category.\n\n        Returns:\n            np.ndarray: the drawn image which channel is RGB.\n        \"\"\"\n        self.set_image(image)\n\n        if 'bboxes' in instances and instances.bboxes.sum() > 0:\n            bboxes = instances.bboxes\n            labels = instances.labels\n\n            max_label = int(max(labels) if len(labels) > 0 else 0)\n            text_palette = get_palette(self.text_color, max_label + 1)\n            text_colors = [text_palette[label] for label in labels]\n\n            bbox_color = palette if self.bbox_color is None \\\n                else self.bbox_color\n            bbox_palette = get_palette(bbox_color, max_label + 1)\n            colors = [bbox_palette[label] for label in labels]\n            self.draw_bboxes(\n                bboxes,\n                edge_colors=colors,\n                alpha=self.alpha,\n                line_widths=self.line_width)\n\n            positions = bboxes[:, :2] + self.line_width\n            areas = (bboxes[:, 3] - bboxes[:, 1]) * (\n                bboxes[:, 2] - bboxes[:, 0])\n            scales = _get_adaptive_scales(areas)\n\n            for i, (pos, label) in enumerate(zip(positions, labels)):\n                if 'label_names' in instances:\n                    label_text = instances.label_names[i]\n                else:\n                    label_text = classes[\n                        label] if classes is not None else f'class {label}'\n                if 'scores' in instances:\n                    score = round(float(instances.scores[i]) * 100, 1)\n                    label_text += f': {score}'\n\n                self.draw_texts(\n                    label_text,\n                    pos,\n                    colors=text_colors[i],\n                    font_sizes=int(13 * scales[i]),\n                    bboxes=[{\n                        'facecolor': 'black',\n                        'alpha': 0.8,\n                        'pad': 0.7,\n                        'edgecolor': 'none'\n                    }])\n\n        if 'masks' in instances:\n            labels = instances.labels\n            masks = instances.masks\n            if isinstance(masks, torch.Tensor):\n                masks = masks.numpy()\n            elif isinstance(masks, (PolygonMasks, BitmapMasks)):\n                masks = masks.to_ndarray()\n\n            masks = masks.astype(bool)\n\n            max_label = int(max(labels) if len(labels) > 0 else 0)\n            mask_color = palette if self.mask_color is None \\\n                else self.mask_color\n            mask_palette = get_palette(mask_color, max_label + 1)\n            colors = [jitter_color(mask_palette[label]) for label in labels]\n            text_palette = get_palette(self.text_color, max_label + 1)\n            text_colors = [text_palette[label] for label in labels]\n\n            polygons = []\n            for i, mask in enumerate(masks):\n                contours, _ = bitmap_to_polygon(mask)\n                polygons.extend(contours)\n            self.draw_polygons(polygons, edge_colors='w', alpha=self.alpha)\n            self.draw_binary_masks(masks, colors=colors, alphas=self.alpha)\n\n            if len(labels) > 0 and \\\n                    ('bboxes' not in instances or\n                     instances.bboxes.sum() == 0):\n                # instances.bboxes.sum()==0 represent dummy bboxes.\n                # A typical example of SOLO does not exist bbox branch.\n                areas = []\n                positions = []\n                for mask in masks:\n                    _, _, stats, centroids = cv2.connectedComponentsWithStats(\n                        mask.astype(np.uint8), connectivity=8)\n                    if stats.shape[0] > 1:\n                        largest_id = np.argmax(stats[1:, -1]) + 1\n                        positions.append(centroids[largest_id])\n                        areas.append(stats[largest_id, -1])\n                areas = np.stack(areas, axis=0)\n                scales = _get_adaptive_scales(areas)\n\n                for i, (pos, label) in enumerate(zip(positions, labels)):\n                    if 'label_names' in instances:\n                        label_text = instances.label_names[i]\n                    else:\n                        label_text = classes[\n                            label] if classes is not None else f'class {label}'\n                    if 'scores' in instances:\n                        score = round(float(instances.scores[i]) * 100, 1)\n                        label_text += f': {score}'\n\n                    self.draw_texts(\n                        label_text,\n                        pos,\n                        colors=text_colors[i],\n                        font_sizes=int(13 * scales[i]),\n                        horizontal_alignments='center',\n                        bboxes=[{\n                            'facecolor': 'black',\n                            'alpha': 0.8,\n                            'pad': 0.7,\n                            'edgecolor': 'none'\n                        }])\n        return self.get_image()\n\n    def _draw_panoptic_seg(self, image: np.ndarray,\n                           panoptic_seg: ['PixelData'],\n                           classes: Optional[List[str]],\n                           palette: Optional[List]) -> np.ndarray:\n        \"\"\"Draw panoptic seg of GT or prediction.\n\n        Args:\n            image (np.ndarray): The image to draw.\n            panoptic_seg (:obj:`PixelData`): Data structure for\n                pixel-level annotations or predictions.\n            classes (List[str], optional): Category information.\n\n        Returns:\n            np.ndarray: the drawn image which channel is RGB.\n        \"\"\"\n        # TODO: Is there a way to bypass？\n        num_classes = len(classes)\n\n        panoptic_seg_data = panoptic_seg.sem_seg[0]\n\n        ids = np.unique(panoptic_seg_data)[::-1]\n\n        if 'label_names' in panoptic_seg:\n            # open set panoptic segmentation\n            classes = panoptic_seg.metainfo['label_names']\n            ignore_index = panoptic_seg.metainfo.get('ignore_index',\n                                                     len(classes))\n            ids = ids[ids != ignore_index]\n        else:\n            # for VOID label\n            ids = ids[ids != num_classes]\n\n        labels = np.array([id % INSTANCE_OFFSET for id in ids], dtype=np.int64)\n        segms = (panoptic_seg_data[None] == ids[:, None, None])\n\n        max_label = int(max(labels) if len(labels) > 0 else 0)\n\n        mask_color = palette if self.mask_color is None \\\n            else self.mask_color\n        mask_palette = get_palette(mask_color, max_label + 1)\n        colors = [mask_palette[label] for label in labels]\n\n        self.set_image(image)\n\n        # draw segm\n        polygons = []\n        for i, mask in enumerate(segms):\n            contours, _ = bitmap_to_polygon(mask)\n            polygons.extend(contours)\n        self.draw_polygons(polygons, edge_colors='w', alpha=self.alpha)\n        self.draw_binary_masks(segms, colors=colors, alphas=self.alpha)\n\n        # draw label\n        areas = []\n        positions = []\n        for mask in segms:\n            _, _, stats, centroids = cv2.connectedComponentsWithStats(\n                mask.astype(np.uint8), connectivity=8)\n            max_id = np.argmax(stats[1:, -1]) + 1\n            positions.append(centroids[max_id])\n            areas.append(stats[max_id, -1])\n        areas = np.stack(areas, axis=0)\n        scales = _get_adaptive_scales(areas)\n\n        text_palette = get_palette(self.text_color, max_label + 1)\n        text_colors = [text_palette[label] for label in labels]\n\n        for i, (pos, label) in enumerate(zip(positions, labels)):\n            label_text = classes[label]\n\n            self.draw_texts(\n                label_text,\n                pos,\n                colors=text_colors[i],\n                font_sizes=int(13 * scales[i]),\n                bboxes=[{\n                    'facecolor': 'black',\n                    'alpha': 0.8,\n                    'pad': 0.7,\n                    'edgecolor': 'none'\n                }],\n                horizontal_alignments='center')\n        return self.get_image()\n\n    def _draw_sem_seg(self, image: np.ndarray, sem_seg: PixelData,\n                      classes: Optional[List],\n                      palette: Optional[List]) -> np.ndarray:\n        \"\"\"Draw semantic seg of GT or prediction.\n\n        Args:\n            image (np.ndarray): The image to draw.\n            sem_seg (:obj:`PixelData`): Data structure for pixel-level\n                annotations or predictions.\n            classes (list, optional): Input classes for result rendering, as\n                the prediction of segmentation model is a segment map with\n                label indices, `classes` is a list which includes items\n                responding to the label indices. If classes is not defined,\n                visualizer will take `cityscapes` classes by default.\n                Defaults to None.\n            palette (list, optional): Input palette for result rendering, which\n                is a list of color palette responding to the classes.\n                Defaults to None.\n\n        Returns:\n            np.ndarray: the drawn image which channel is RGB.\n        \"\"\"\n        sem_seg_data = sem_seg.sem_seg\n        if isinstance(sem_seg_data, torch.Tensor):\n            sem_seg_data = sem_seg_data.numpy()\n\n        # 0 ~ num_class, the value 0 means background\n        ids = np.unique(sem_seg_data)\n        ignore_index = sem_seg.metainfo.get('ignore_index', 255)\n        ids = ids[ids != ignore_index]\n\n        if 'label_names' in sem_seg:\n            # open set semseg\n            label_names = sem_seg.metainfo['label_names']\n        else:\n            label_names = classes\n\n        labels = np.array(ids, dtype=np.int64)\n        colors = [palette[label] for label in labels]\n\n        self.set_image(image)\n\n        # draw semantic masks\n        for i, (label, color) in enumerate(zip(labels, colors)):\n            masks = sem_seg_data == label\n            self.draw_binary_masks(masks, colors=[color], alphas=self.alpha)\n            label_text = label_names[label]\n            _, _, stats, centroids = cv2.connectedComponentsWithStats(\n                masks[0].astype(np.uint8), connectivity=8)\n            if stats.shape[0] > 1:\n                largest_id = np.argmax(stats[1:, -1]) + 1\n                centroids = centroids[largest_id]\n\n                areas = stats[largest_id, -1]\n                scales = _get_adaptive_scales(areas)\n\n                self.draw_texts(\n                    label_text,\n                    centroids,\n                    colors=(255, 255, 255),\n                    font_sizes=int(13 * scales),\n                    horizontal_alignments='center',\n                    bboxes=[{\n                        'facecolor': 'black',\n                        'alpha': 0.8,\n                        'pad': 0.7,\n                        'edgecolor': 'none'\n                    }])\n\n        return self.get_image()\n\n    @master_only\n    def add_datasample(\n            self,\n            name: str,\n            image: np.ndarray,\n            data_sample: Optional['DetDataSample'] = None,\n            draw_gt: bool = True,\n            draw_pred: bool = True,\n            show: bool = False,\n            wait_time: float = 0,\n            # TODO: Supported in mmengine's Viusalizer.\n            out_file: Optional[str] = None,\n            pred_score_thr: float = 0.3,\n            step: int = 0) -> None:\n        \"\"\"Draw datasample and save to all backends.\n\n        - If GT and prediction are plotted at the same time, they are\n        displayed in a stitched image where the left image is the\n        ground truth and the right image is the prediction.\n        - If ``show`` is True, all storage backends are ignored, and\n        the images will be displayed in a local window.\n        - If ``out_file`` is specified, the drawn image will be\n        saved to ``out_file``. t is usually used when the display\n        is not available.\n\n        Args:\n            name (str): The image identifier.\n            image (np.ndarray): The image to draw.\n            data_sample (:obj:`DetDataSample`, optional): A data\n                sample that contain annotations and predictions.\n                Defaults to None.\n            draw_gt (bool): Whether to draw GT DetDataSample. Default to True.\n            draw_pred (bool): Whether to draw Prediction DetDataSample.\n                Defaults to True.\n            show (bool): Whether to display the drawn image. Default to False.\n            wait_time (float): The interval of show (s). Defaults to 0.\n            out_file (str): Path to output file. Defaults to None.\n            pred_score_thr (float): The threshold to visualize the bboxes\n                and masks. Defaults to 0.3.\n            step (int): Global step value to record. Defaults to 0.\n        \"\"\"\n        image = image.clip(0, 255).astype(np.uint8)\n        classes = self.dataset_meta.get('classes', None)\n        palette = self.dataset_meta.get('palette', None)\n\n        gt_img_data = None\n        pred_img_data = None\n\n        if data_sample is not None:\n            data_sample = data_sample.cpu()\n\n        if draw_gt and data_sample is not None:\n            gt_img_data = image\n            if 'gt_instances' in data_sample:\n                gt_img_data = self._draw_instances(image,\n                                                   data_sample.gt_instances,\n                                                   classes, palette)\n            if 'gt_sem_seg' in data_sample:\n                gt_img_data = self._draw_sem_seg(gt_img_data,\n                                                 data_sample.gt_sem_seg,\n                                                 classes, palette)\n\n            if 'gt_panoptic_seg' in data_sample:\n                assert classes is not None, 'class information is ' \\\n                                            'not provided when ' \\\n                                            'visualizing panoptic ' \\\n                                            'segmentation results.'\n                gt_img_data = self._draw_panoptic_seg(\n                    gt_img_data, data_sample.gt_panoptic_seg, classes, palette)\n\n        if draw_pred and data_sample is not None:\n            pred_img_data = image\n            if 'pred_instances' in data_sample:\n                pred_instances = data_sample.pred_instances\n                pred_instances = pred_instances[\n                    pred_instances.scores > pred_score_thr]\n                pred_img_data = self._draw_instances(image, pred_instances,\n                                                     classes, palette)\n\n            if 'pred_sem_seg' in data_sample:\n                pred_img_data = self._draw_sem_seg(pred_img_data,\n                                                   data_sample.pred_sem_seg,\n                                                   classes, palette)\n\n            if 'pred_panoptic_seg' in data_sample:\n                assert classes is not None, 'class information is ' \\\n                                            'not provided when ' \\\n                                            'visualizing panoptic ' \\\n                                            'segmentation results.'\n                pred_img_data = self._draw_panoptic_seg(\n                    pred_img_data, data_sample.pred_panoptic_seg.numpy(),\n                    classes, palette)\n\n        if gt_img_data is not None and pred_img_data is not None:\n            drawn_img = np.concatenate((gt_img_data, pred_img_data), axis=1)\n        elif gt_img_data is not None:\n            drawn_img = gt_img_data\n        elif pred_img_data is not None:\n            drawn_img = pred_img_data\n        else:\n            # Display the original image directly if nothing is drawn.\n            drawn_img = image\n\n        # It is convenient for users to obtain the drawn image.\n        # For example, the user wants to obtain the drawn image and\n        # save it as a video during video inference.\n        self.set_image(drawn_img)\n\n        if show:\n            self.show(drawn_img, win_name=name, wait_time=wait_time)\n\n        if out_file is not None:\n            mmcv.imwrite(drawn_img[..., ::-1], out_file)\n        else:\n            self.add_image(name, drawn_img, step)\n\n\ndef random_color(seed):\n    \"\"\"Random a color according to the input seed.\"\"\"\n    if sns is None:\n        raise RuntimeError('motmetrics is not installed,\\\n                 please install it by: pip install seaborn')\n    np.random.seed(seed)\n    colors = sns.color_palette()\n    color = colors[np.random.choice(range(len(colors)))]\n    color = tuple([int(255 * c) for c in color])\n    return color\n\n\n@VISUALIZERS.register_module()\nclass TrackLocalVisualizer(Visualizer):\n    \"\"\"Tracking Local Visualizer for the MOT, VIS tasks.\n\n    Args:\n        name (str): Name of the instance. Defaults to 'visualizer'.\n        image (np.ndarray, optional): the origin image to draw. The format\n            should be RGB. Defaults to None.\n        vis_backends (list, optional): Visual backend config list.\n            Defaults to None.\n        save_dir (str, optional): Save file dir for all storage backends.\n            If it is None, the backend storage will not save any data.\n        line_width (int, float): The linewidth of lines.\n            Defaults to 3.\n        alpha (int, float): The transparency of bboxes or mask.\n                Defaults to 0.8.\n    \"\"\"\n\n    def __init__(self,\n                 name: str = 'visualizer',\n                 image: Optional[np.ndarray] = None,\n                 vis_backends: Optional[Dict] = None,\n                 save_dir: Optional[str] = None,\n                 line_width: Union[int, float] = 3,\n                 alpha: float = 0.8) -> None:\n        super().__init__(name, image, vis_backends, save_dir)\n        self.line_width = line_width\n        self.alpha = alpha\n        # Set default value. When calling\n        # `TrackLocalVisualizer().dataset_meta=xxx`,\n        # it will override the default value.\n        self.dataset_meta = {}\n\n    def _draw_instances(self, image: np.ndarray,\n                        instances: InstanceData) -> np.ndarray:\n        \"\"\"Draw instances of GT or prediction.\n\n        Args:\n            image (np.ndarray): The image to draw.\n            instances (:obj:`InstanceData`): Data structure for\n                instance-level annotations or predictions.\n        Returns:\n            np.ndarray: the drawn image which channel is RGB.\n        \"\"\"\n        self.set_image(image)\n        classes = self.dataset_meta.get('classes', None)\n\n        # get colors and texts\n        # for the MOT and VIS tasks\n        colors = [random_color(_id) for _id in instances.instances_id]\n        categories = [\n            classes[label] if classes is not None else f'cls{label}'\n            for label in instances.labels\n        ]\n        if 'scores' in instances:\n            texts = [\n                f'{category_name}\\n{instance_id} | {score:.2f}'\n                for category_name, instance_id, score in zip(\n                    categories, instances.instances_id, instances.scores)\n            ]\n        else:\n            texts = [\n                f'{category_name}\\n{instance_id}' for category_name,\n                instance_id in zip(categories, instances.instances_id)\n            ]\n\n        # draw bboxes and texts\n        if 'bboxes' in instances:\n            # draw bboxes\n            bboxes = instances.bboxes.clone()\n            self.draw_bboxes(\n                bboxes,\n                edge_colors=colors,\n                alpha=self.alpha,\n                line_widths=self.line_width)\n            # draw texts\n            if texts is not None:\n                positions = bboxes[:, :2] + self.line_width\n                areas = (bboxes[:, 3] - bboxes[:, 1]) * (\n                    bboxes[:, 2] - bboxes[:, 0])\n                scales = _get_adaptive_scales(areas.cpu().numpy())\n                for i, pos in enumerate(positions):\n                    self.draw_texts(\n                        texts[i],\n                        pos,\n                        colors='black',\n                        font_sizes=int(13 * scales[i]),\n                        bboxes=[{\n                            'facecolor': [c / 255 for c in colors[i]],\n                            'alpha': 0.8,\n                            'pad': 0.7,\n                            'edgecolor': 'none'\n                        }])\n\n        # draw masks\n        if 'masks' in instances:\n            masks = instances.masks\n            polygons = []\n            for i, mask in enumerate(masks):\n                contours, _ = bitmap_to_polygon(mask)\n                polygons.extend(contours)\n            self.draw_polygons(polygons, edge_colors='w', alpha=self.alpha)\n            self.draw_binary_masks(masks, colors=colors, alphas=self.alpha)\n\n        return self.get_image()\n\n    @master_only\n    def add_datasample(\n            self,\n            name: str,\n            image: np.ndarray,\n            data_sample: DetDataSample = None,\n            draw_gt: bool = True,\n            draw_pred: bool = True,\n            show: bool = False,\n            wait_time: int = 0,\n            # TODO: Supported in mmengine's Viusalizer.\n            out_file: Optional[str] = None,\n            pred_score_thr: float = 0.3,\n            step: int = 0) -> None:\n        \"\"\"Draw datasample and save to all backends.\n\n        - If GT and prediction are plotted at the same time, they are\n        displayed in a stitched image where the left image is the\n        ground truth and the right image is the prediction.\n        - If ``show`` is True, all storage backends are ignored, and\n        the images will be displayed in a local window.\n        - If ``out_file`` is specified, the drawn image will be\n        saved to ``out_file``. t is usually used when the display\n        is not available.\n        Args:\n            name (str): The image identifier.\n            image (np.ndarray): The image to draw.\n            data_sample (OptTrackSampleList): A data\n                sample that contain annotations and predictions.\n                Defaults to None.\n            draw_gt (bool): Whether to draw GT TrackDataSample.\n                Default to True.\n            draw_pred (bool): Whether to draw Prediction TrackDataSample.\n                Defaults to True.\n            show (bool): Whether to display the drawn image. Default to False.\n            wait_time (int): The interval of show (s). Defaults to 0.\n            out_file (str): Path to output file. Defaults to None.\n            pred_score_thr (float): The threshold to visualize the bboxes\n                and masks. Defaults to 0.3.\n            step (int): Global step value to record. Defaults to 0.\n        \"\"\"\n        gt_img_data = None\n        pred_img_data = None\n\n        if data_sample is not None:\n            data_sample = data_sample.cpu()\n\n        if draw_gt and data_sample is not None:\n            assert 'gt_instances' in data_sample\n            gt_img_data = self._draw_instances(image, data_sample.gt_instances)\n\n        if draw_pred and data_sample is not None:\n            assert 'pred_track_instances' in data_sample\n            pred_instances = data_sample.pred_track_instances\n            if 'scores' in pred_instances:\n                pred_instances = pred_instances[\n                    pred_instances.scores > pred_score_thr].cpu()\n            pred_img_data = self._draw_instances(image, pred_instances)\n\n        if gt_img_data is not None and pred_img_data is not None:\n            drawn_img = np.concatenate((gt_img_data, pred_img_data), axis=1)\n        elif gt_img_data is not None:\n            drawn_img = gt_img_data\n        else:\n            drawn_img = pred_img_data\n\n        if show:\n            self.show(drawn_img, win_name=name, wait_time=wait_time)\n\n        if out_file is not None:\n            mmcv.imwrite(drawn_img[..., ::-1], out_file)\n        else:\n            self.add_image(name, drawn_img, step)\n"
  },
  {
    "path": "mmdet/visualization/palette.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple, Union\n\nimport mmcv\nimport numpy as np\nfrom mmengine.utils import is_str\n\n\ndef palette_val(palette: List[tuple]) -> List[tuple]:\n    \"\"\"Convert palette to matplotlib palette.\n\n    Args:\n        palette (List[tuple]): A list of color tuples.\n\n    Returns:\n        List[tuple[float]]: A list of RGB matplotlib color tuples.\n    \"\"\"\n    new_palette = []\n    for color in palette:\n        color = [c / 255 for c in color]\n        new_palette.append(tuple(color))\n    return new_palette\n\n\ndef get_palette(palette: Union[List[tuple], str, tuple],\n                num_classes: int) -> List[Tuple[int]]:\n    \"\"\"Get palette from various inputs.\n\n    Args:\n        palette (list[tuple] | str | tuple): palette inputs.\n        num_classes (int): the number of classes.\n\n    Returns:\n        list[tuple[int]]: A list of color tuples.\n    \"\"\"\n    assert isinstance(num_classes, int)\n\n    if isinstance(palette, list):\n        dataset_palette = palette\n    elif isinstance(palette, tuple):\n        dataset_palette = [palette] * num_classes\n    elif palette == 'random' or palette is None:\n        state = np.random.get_state()\n        # random color\n        np.random.seed(42)\n        palette = np.random.randint(0, 256, size=(num_classes, 3))\n        np.random.set_state(state)\n        dataset_palette = [tuple(c) for c in palette]\n    elif palette == 'coco':\n        from mmdet.datasets import CocoDataset, CocoPanopticDataset\n        dataset_palette = CocoDataset.METAINFO['palette']\n        if len(dataset_palette) < num_classes:\n            dataset_palette = CocoPanopticDataset.METAINFO['palette']\n    elif palette == 'citys':\n        from mmdet.datasets import CityscapesDataset\n        dataset_palette = CityscapesDataset.METAINFO['palette']\n    elif palette == 'voc':\n        from mmdet.datasets import VOCDataset\n        dataset_palette = VOCDataset.METAINFO['palette']\n    elif is_str(palette):\n        dataset_palette = [mmcv.color_val(palette)[::-1]] * num_classes\n    else:\n        raise TypeError(f'Invalid type for palette: {type(palette)}')\n\n    assert len(dataset_palette) >= num_classes, \\\n        'The length of palette should not be less than `num_classes`.'\n    return dataset_palette\n\n\ndef _get_adaptive_scales(areas: np.ndarray,\n                         min_area: int = 800,\n                         max_area: int = 30000) -> np.ndarray:\n    \"\"\"Get adaptive scales according to areas.\n\n    The scale range is [0.5, 1.0]. When the area is less than\n    ``min_area``, the scale is 0.5 while the area is larger than\n    ``max_area``, the scale is 1.0.\n\n    Args:\n        areas (ndarray): The areas of bboxes or masks with the\n            shape of (n, ).\n        min_area (int): Lower bound areas for adaptive scales.\n            Defaults to 800.\n        max_area (int): Upper bound areas for adaptive scales.\n            Defaults to 30000.\n\n    Returns:\n        ndarray: The adaotive scales with the shape of (n, ).\n    \"\"\"\n    scales = 0.5 + (areas - min_area) // (max_area - min_area)\n    scales = np.clip(scales, 0.5, 1.0)\n    return scales\n\n\ndef jitter_color(color: tuple) -> tuple:\n    \"\"\"Randomly jitter the given color in order to better distinguish instances\n    with the same class.\n\n    Args:\n        color (tuple): The RGB color tuple. Each value is between [0, 255].\n\n    Returns:\n        tuple: The jittered color tuple.\n    \"\"\"\n    jitter = np.random.rand(3)\n    jitter = (jitter / np.linalg.norm(jitter) - 0.5) * 0.5 * 255\n    color = np.clip(jitter + color, 0, 255).astype(np.uint8)\n    return tuple(color)\n"
  },
  {
    "path": "model-index.yml",
    "content": "Import:\n  - configs/albu_example/metafile.yml\n  - configs/atss/metafile.yml\n  - configs/autoassign/metafile.yml\n  - configs/boxinst/metafile.yml\n  - configs/carafe/metafile.yml\n  - configs/cascade_rcnn/metafile.yml\n  - configs/cascade_rpn/metafile.yml\n  - configs/centernet/metafile.yml\n  - configs/centripetalnet/metafile.yml\n  - configs/condinst/metafile.yml\n  - configs/conditional_detr/metafile.yml\n  - configs/cornernet/metafile.yml\n  - configs/convnext/metafile.yml\n  - configs/crowddet/metafile.yml\n  - configs/dab_detr/metafile.yml\n  - configs/dcn/metafile.yml\n  - configs/dcnv2/metafile.yml\n  - configs/ddod/metafile.yml\n  - configs/deformable_detr/metafile.yml\n  - configs/detectors/metafile.yml\n  - configs/detr/metafile.yml\n  - configs/dino/metafile.yml\n  - configs/double_heads/metafile.yml\n  - configs/dyhead/metafile.yml\n  - configs/dynamic_rcnn/metafile.yml\n  - configs/efficientnet/metafile.yml\n  - configs/empirical_attention/metafile.yml\n  - configs/faster_rcnn/metafile.yml\n  - configs/fcos/metafile.yml\n  - configs/foveabox/metafile.yml\n  - configs/fpg/metafile.yml\n  - configs/free_anchor/metafile.yml\n  - configs/fsaf/metafile.yml\n  - configs/gcnet/metafile.yml\n  - configs/gfl/metafile.yml\n  - configs/ghm/metafile.yml\n  - configs/gn/metafile.yml\n  - configs/gn+ws/metafile.yml\n  - configs/grid_rcnn/metafile.yml\n  - configs/groie/metafile.yml\n  - configs/guided_anchoring/metafile.yml\n  - configs/hrnet/metafile.yml\n  - configs/htc/metafile.yml\n  - configs/instaboost/metafile.yml\n  - configs/lad/metafile.yml\n  - configs/ld/metafile.yml\n  - configs/libra_rcnn/metafile.yml\n  - configs/lvis/metafile.yml\n  - configs/mask2former/metafile.yml\n  - configs/mask_rcnn/metafile.yml\n  - configs/maskformer/metafile.yml\n  - configs/ms_rcnn/metafile.yml\n  - configs/nas_fcos/metafile.yml\n  - configs/nas_fpn/metafile.yml\n  - configs/openimages/metafile.yml\n  - configs/paa/metafile.yml\n  - configs/pafpn/metafile.yml\n  - configs/panoptic_fpn/metafile.yml\n  - configs/pvt/metafile.yml\n  - configs/pisa/metafile.yml\n  - configs/point_rend/metafile.yml\n  - configs/queryinst/metafile.yml\n  - configs/regnet/metafile.yml\n  - configs/reppoints/metafile.yml\n  - configs/res2net/metafile.yml\n  - configs/resnest/metafile.yml\n  - configs/resnet_strikes_back/metafile.yml\n  - configs/retinanet/metafile.yml\n  - configs/rpn/metafile.yml\n  - configs/rtmdet/metafile.yml\n  - configs/sabl/metafile.yml\n  - configs/scnet/metafile.yml\n  - configs/scratch/metafile.yml\n  - configs/seesaw_loss/metafile.yml\n  - configs/simple_copy_paste/metafile.yml\n  - configs/soft_teacher/metafile.yml\n  - configs/sparse_rcnn/metafile.yml\n  - configs/solo/metafile.yml\n  - configs/solov2/metafile.yml\n  - configs/ssd/metafile.yml\n  - configs/strong_baselines/metafile.yml\n  - configs/swin/metafile.yml\n  - configs/tridentnet/metafile.yml\n  - configs/tood/metafile.yml\n  - configs/vfnet/metafile.yml\n  - configs/yolact/metafile.yml\n  - configs/yolo/metafile.yml\n  - configs/yolof/metafile.yml\n  - configs/yolox/metafile.yml\n  - configs/bytetrack/metafile.yml\n  - configs/strongsort/metafile.yml\n  - configs/ocsort/metafile.yml\n  - configs/sort/metafile.yml\n  - configs/deepsort/metafile.yml\n  - configs/qdtrack/metafile.yml\n  - configs/mask2former_vis/metafile.yml\n  - configs/masktrack_rcnn/metafile.yml\n  - configs/glip/metafile.yml\n  - configs/ddq/metafile.yml\n  - configs/grounding_dino/metafile.yml\n  - configs/mm_grounding_dino/metafile.yml\n"
  },
  {
    "path": "projects/AlignDETR/README.md",
    "content": "# AlignDETR\n\n> [Align-DETR: Improving DETR with Simple IoU-aware BCE loss](https://arxiv.org/abs/2304.07527)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nDETR has set up a simple end-to-end pipeline for object detection by formulating this task as a set prediction problem, showing promising potential. However, despite the significant progress in improving DETR, this paper identifies a problem of misalignment in the output distribution, which prevents the best-regressed samples from being assigned with high confidence, hindering the model's accuracy. We propose a metric, recall of best-regressed samples, to quantitively evaluate the misalignment problem. Observing its importance, we propose a novel Align-DETR that incorporates a localization precision-aware classification loss in optimization. The proposed loss, IA-BCE, guides the training of DETR to build a strong correlation between classification score and localization precision. We also adopt the mixed-matching strategy, to facilitate DETR-based detectors with faster training convergence while keeping an end-to-end scheme. Moreover, to overcome the dramatic decrease in sample quality induced by the sparsity of queries, we introduce a prime sample weighting mechanism to suppress the interference of unimportant samples. Extensive experiments are conducted with very competitive results reported. In particular, it delivers a 46 (+3.8)% AP on the DAB-DETR baseline with the ResNet-50 backbone and reaches a new SOTA performance of 50.2% AP in the 1x setting on the COCO validation set when employing the strong baseline DINO.\n\n![image](https://github.com/open-mmlab/mmdetection/assets/33146359/5a4fa664-b4c6-487d-b6d8-22be9d59a2bc)\n\n## Results and Models\n\n| Backbone |    Model    | Lr schd | box AP |                       Config                       |                                                                                                                                                                    Download                                                                                                                                                                    |\n| :------: | :---------: | :-----: | :----: | :------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | DINO-4scale |   12e   |  50.5  | [config](./align_detr-4scale_r50_8xb2-12e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/align_detr/align_detr-4scale_r50_8xb2-12e_coco/align_detr-4scale_r50_8xb2-12e_coco_20230914_095734-61f921af.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/align_detr/align_detr-4scale_r50_8xb2-12e_coco/align_detr-4scale_r50_8xb2-12e_coco_20230914_095734.log.json) |\n|   R-50   | DINO-4scale |   24e   |  51.4  | [config](./align_detr-4scale_r50_8xb2-24e_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/align_detr/align_detr-4scale_r50_8xb2-24e_coco/align_detr-4scale_r50_8xb2-24e_coco_20230919_152414-f4b6cf76.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/align_detr/align_detr-4scale_r50_8xb2-24e_coco/align_detr-4scale_r50_8xb2-24e_coco_20230919_152414.log.json) |\n\n## Citation\n\nWe provide the config files for AlignDETR: [Align-DETR: Improving DETR with Simple IoU-aware BCE loss](https://arxiv.org/abs/2304.07527).\n\n```latex\n@misc{cai2023aligndetr,\n      title={Align-DETR: Improving DETR with Simple IoU-aware BCE loss},\n      author={Zhi Cai and Songtao Liu and Guodong Wang and Zheng Ge and Xiangyu Zhang and Di Huang},\n      year={2023},\n      eprint={2304.07527},\n      archivePrefix={arXiv},\n      primaryClass={cs.CV}\n}\n```\n"
  },
  {
    "path": "projects/AlignDETR/align_detr/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .align_detr_head import AlignDETRHead\nfrom .mixed_hungarian_assigner import MixedHungarianAssigner\n\n__all__ = ['AlignDETRHead', 'MixedHungarianAssigner']\n"
  },
  {
    "path": "projects/AlignDETR/align_detr/align_detr_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Any, Dict, List, Tuple, Union\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.dense_heads import DINOHead\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import (bbox_cxcywh_to_xyxy, bbox_overlaps,\n                                   bbox_xyxy_to_cxcywh)\nfrom mmdet.utils import InstanceList\nfrom .utils import KeysRecorder\n\n\n@MODELS.register_module()\nclass AlignDETRHead(DINOHead):\n    r\"\"\"Head of the Align-DETR: Improving DETR with Simple IoU-aware BCE loss\n\n    Code is modified from the `official github repo\n    <https://github.com/FelixCaae/AlignDETR>`_.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/2304.07527>`_ .\n\n    Args:\n        all_layers_num_gt_repeat List[int]: Number to repeat gt for 1-to-k\n            matching between ground truth and predictions of each decoder\n            layer. Only used for matching queries, not for denoising queries.\n            Element count is `num_pred_layer`. If `as_two_stage` is True, then\n            the last element is for encoder output, and the others for\n            decoder layers. Otherwise, all elements are for decoder layers.\n            Defaults to a list of `1` for the last decoder layer and `2` for\n            the others.\n        alpha (float): Hyper-parameter of classification loss that controls\n            the proportion of each item to calculate `t`, the weighted\n            geometric average of the confident score and the IoU score, to\n            align classification and regression scores. Defaults to `0.25`.\n        gamma (float): Hyper-parameter of classification loss to do the hard\n            negative mining. Defaults to `2.0`.\n        tau (float): Hyper-parameter of classification and regression losses,\n            it is the temperature controlling the sharpness of the function\n            to calculate positive sample weight. Defaults to `1.5`.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 all_layers_num_gt_repeat: List[int] = None,\n                 alpha: float = 0.25,\n                 gamma: float = 2.0,\n                 tau: float = 1.5,\n                 **kwargs) -> None:\n        self.all_layers_num_gt_repeat = all_layers_num_gt_repeat\n        self.alpha = alpha\n        self.gamma = gamma\n        self.tau = tau\n        self.weight_table = torch.zeros(\n            len(all_layers_num_gt_repeat), max(all_layers_num_gt_repeat))\n        for layer_index, num_gt_repeat in enumerate(all_layers_num_gt_repeat):\n            self.weight_table[layer_index][:num_gt_repeat] = torch.exp(\n                -torch.arange(num_gt_repeat) / tau)\n\n        super().__init__(*args, **kwargs)\n        assert len(self.all_layers_num_gt_repeat) == self.num_pred_layer\n\n    def loss_by_feat(self, all_layers_cls_scores: Tensor, *args,\n                     **kwargs) -> Any:\n        \"\"\"Loss function.\n            AlignDETR: This method is based on `DINOHead.loss_by_feat`.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs,\n                num_queries_total, cls_out_channels), where\n                `num_queries_total` is the sum of `num_denoising_queries`\n                and `num_matching_queries`.\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        # Wrap `all_layers_cls_scores` with KeysRecorder to record its\n        #   `__getitem__` keys and get decoder layer index.\n        all_layers_cls_scores = KeysRecorder(all_layers_cls_scores)\n        result = super(AlignDETRHead,\n                       self).loss_by_feat(all_layers_cls_scores, *args,\n                                          **kwargs)\n        return result\n\n    def loss_by_feat_single(self, cls_scores: Union[KeysRecorder, Tensor],\n                            bbox_preds: Tensor,\n                            batch_gt_instances: InstanceList,\n                            batch_img_metas: List[dict]) -> Tuple[Tensor]:\n        \"\"\"Loss function for outputs from a single decoder layer of a single\n        feature level.\n            AlignDETR: This method is based on `DINOHead.loss_by_feat_single`.\n\n        Args:\n            cls_scores (Union[KeysRecorder, Tensor]): Box score logits from a\n                single decoder layer for all images, has shape (bs,\n                num_queries, cls_out_channels).\n            bbox_preds (Tensor): Sigmoid outputs from a single decoder layer\n                for all images, with normalized coordinate (cx, cy, w, h) and\n                shape (bs, num_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n\n        Returns:\n            Tuple[Tensor]: A tuple including `loss_cls`, `loss_box` and\n            `loss_iou`.\n        \"\"\"\n        # AlignDETR: Get layer_index.\n        if isinstance(cls_scores, KeysRecorder):\n            # Outputs are from decoder layer. Get layer_index from\n            #   `__getitem__` keys history.\n            keys = [key for key in cls_scores.keys if isinstance(key, int)]\n            assert len(keys) == 1, \\\n                'Failed to extract key from cls_scores.keys: {}'.format(keys)\n            layer_index = keys[0]\n            # Get dn_cls_scores tensor.\n            cls_scores = cls_scores.obj\n        else:\n            # Outputs are from encoder layer.\n            layer_index = self.num_pred_layer - 1\n\n        for img_meta in batch_img_metas:\n            img_meta['layer_index'] = layer_index\n\n        results = super(AlignDETRHead, self).loss_by_feat_single(\n            cls_scores,\n            bbox_preds,\n            batch_gt_instances=batch_gt_instances,\n            batch_img_metas=batch_img_metas)\n        return results\n\n    def get_targets(self, cls_scores_list: List[Tensor],\n                    bbox_preds_list: List[Tensor],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict]) -> tuple:\n        \"\"\"Compute regression and classification targets for a batch image.\n\n        Outputs from a single decoder layer of a single feature level are used.\n        AlignDETR: This method is based on `DETRHead.get_targets`.\n\n        Args:\n            cls_scores_list (list[Tensor]): Box score logits from a single\n                decoder layer for each image, has shape [num_queries,\n                cls_out_channels].\n            bbox_preds_list (list[Tensor]): Sigmoid outputs from a single\n                decoder layer for each image, with normalized coordinate\n                (cx, cy, w, h) and shape [num_queries, 4].\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n\n        Returns:\n            tuple: a tuple containing the following targets.\n\n            - labels_list (list[Tensor]): Labels for all images.\n            - label_weights_list (list[Tensor]): Label weights for all images.\n            - bbox_targets_list (list[Tensor]): BBox targets for all images.\n            - bbox_weights_list (list[Tensor]): BBox weights for all images.\n            - num_total_pos (int): Number of positive samples in all images.\n            - num_total_neg (int): Number of negative samples in all images.\n        \"\"\"\n        results = super(AlignDETRHead,\n                        self).get_targets(cls_scores_list, bbox_preds_list,\n                                          batch_gt_instances, batch_img_metas)\n\n        # AlignDETR: `num_total_pos` for matching queries is the number of\n        #   unique gt bboxes in the batch. Refer to AlignDETR official code:\n        #   https://github.com/FelixCaae/AlignDETR/blob/8c2b1806026e1b33fe1c282577de1647e352d7f0/aligndetr/criterions/base_criterion.py#L195C15-L195C15  # noqa: E501\n        num_total_pos = sum(\n            len(gt_instances) for gt_instances in batch_gt_instances)\n\n        results = list(results)\n        results[-2] = num_total_pos\n        return tuple(results)\n\n    def _get_targets_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            gt_instances: InstanceData,\n                            img_meta: dict) -> tuple:\n        \"\"\"Compute regression and classification targets for one image.\n\n        Outputs from a single decoder layer of a single feature level are used.\n        AlignDETR: This method is based on `DETRHead._get_targets_single`.\n\n        Args:\n            cls_score (Tensor): Box score logits from a single decoder layer\n                for one image. Shape [num_queries, cls_out_channels].\n            bbox_pred (Tensor): Sigmoid outputs from a single decoder layer\n                for one image, with normalized coordinate (cx, cy, w, h) and\n                shape [num_queries, 4].\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for one image.\n            layer_index (int): Decoder layer index for the outputs. Defaults\n                to `-1`.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - labels (Tensor): Labels of each image.\n            - label_weights (Tensor]): Label weights of each image.\n            - bbox_targets (Tensor): BBox targets of each image.\n            - bbox_weights (Tensor): BBox weights of each image.\n            - pos_inds (Tensor): Sampled positive indices for each image.\n            - neg_inds (Tensor): Sampled negative indices for each image.\n        \"\"\"\n        img_h, img_w = img_meta['img_shape']\n        factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                       img_h]).unsqueeze(0)\n        # convert bbox_pred from xywh, normalized to xyxy, unnormalized\n        bbox_pred = bbox_cxcywh_to_xyxy(bbox_pred)\n        bbox_pred = bbox_pred * factor\n\n        pred_instances = InstanceData(scores=cls_score, bboxes=bbox_pred)\n\n        # assigner and sampler\n        # AlignDETR: Get `k` of current layer.\n        layer_index = img_meta['layer_index']\n        num_gt_repeat = self.all_layers_num_gt_repeat[layer_index]\n        assign_result = self.assigner.assign(\n            pred_instances=pred_instances,\n            gt_instances=gt_instances,\n            img_meta=img_meta,\n            k=num_gt_repeat)\n\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        pos_inds = torch.nonzero(\n            assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique()\n        neg_inds = torch.nonzero(\n            assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique()\n        pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1\n        pos_gt_bboxes = gt_bboxes[pos_assigned_gt_inds.long(), :]\n\n        # AlignDETR: Get label targets, label weights, and bbox weights.\n        target_results = self._get_align_detr_targets_single(\n            cls_score,\n            bbox_pred,\n            gt_labels,\n            pos_gt_bboxes,\n            pos_inds,\n            pos_assigned_gt_inds,\n            layer_index,\n            is_matching_queries=True)\n\n        label_targets, label_weights, bbox_weights = target_results\n\n        # bbox targets\n        bbox_targets = torch.zeros_like(bbox_pred, dtype=gt_bboxes.dtype)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image.\n        # Thus the learning target should be normalized by the image size, also\n        # the box format should be converted from defaultly x1y1x2y2 to cxcywh.\n        pos_gt_bboxes_normalized = pos_gt_bboxes / factor\n        pos_gt_bboxes_targets = bbox_xyxy_to_cxcywh(pos_gt_bboxes_normalized)\n        bbox_targets[pos_inds] = pos_gt_bboxes_targets\n        return (label_targets, label_weights, bbox_targets, bbox_weights,\n                pos_inds, neg_inds)\n\n    def _loss_dn_single(self, dn_cls_scores: KeysRecorder,\n                        dn_bbox_preds: Tensor,\n                        batch_gt_instances: InstanceList,\n                        batch_img_metas: List[dict],\n                        dn_meta: Dict[str, int]) -> Tuple[Tensor]:\n        \"\"\"Denoising loss for outputs from a single decoder layer.\n            AlignDETR: This method is based on `DINOHead._loss_dn_single`.\n\n        Args:\n            dn_cls_scores (KeysRecorder): Classification scores of a single\n                decoder layer in denoising part, has shape (bs,\n                num_denoising_queries, cls_out_channels).\n            dn_bbox_preds (Tensor): Regression outputs of a single decoder\n                layer in denoising part. Each is a 4D-tensor with normalized\n                coordinate format (cx, cy, w, h) and has shape\n                (bs, num_denoising_queries, 4).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            Tuple[Tensor]: A tuple including `loss_cls`, `loss_box` and\n            `loss_iou`.\n        \"\"\"\n        # AlignDETR: Get dn_cls_scores tensor.\n        dn_cls_scores = dn_cls_scores.obj\n\n        # AlignDETR: Add layer outputs to meta info because they are not\n        #   variables of method `_get_dn_targets_single`.\n        for image_index, img_meta in enumerate(batch_img_metas):\n            img_meta['dn_cls_score'] = dn_cls_scores[image_index]\n            img_meta['dn_bbox_pred'] = dn_bbox_preds[image_index]\n\n        results = super()._loss_dn_single(dn_cls_scores, dn_bbox_preds,\n                                          batch_gt_instances, batch_img_metas,\n                                          dn_meta)\n        return results\n\n    def _get_dn_targets_single(self, gt_instances: InstanceData,\n                               img_meta: dict, dn_meta: Dict[str,\n                                                             int]) -> tuple:\n        \"\"\"Get targets in denoising part for one image.\n            AlignDETR: This method is based on\n            `DINOHead._get_dn_targets_single`.\n            and 1) Added passing `dn_cls_score`, `dn_bbox_pred` to this\n            method; 2) Modified the way to get targets.\n        Args:\n            dn_cls_score (Tensor): Box score logits from a single decoder\n            layer in denoising part for one image, has shape\n                [num_denoising_queries, cls_out_channels].\n            dn_bbox_pred (Tensor): Sigmoid outputs from a single decoder\n            layer in denoising part for one image, with\n                normalized coordinate (cx, cy, w, h) and shape\n                [num_denoising_queries, 4].\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It should includes ``bboxes`` and ``labels``\n                attributes.\n            img_meta (dict): Meta information for one image.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n              group collation, including 'num_denoising_queries' and\n              'num_denoising_groups'. It will be used for split outputs of\n              denoising and matching parts and loss calculation.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - labels (Tensor): Labels of each image.\n            - label_weights (Tensor]): Label weights of each image.\n            - bbox_targets (Tensor): BBox targets of each image.\n            - bbox_weights (Tensor): BBox weights of each image.\n            - pos_inds (Tensor): Sampled positive indices for each image.\n            - neg_inds (Tensor): Sampled negative indices for each image.\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        gt_labels = gt_instances.labels\n        num_groups = dn_meta['num_denoising_groups']\n        num_denoising_queries = dn_meta['num_denoising_queries']\n        num_queries_each_group = int(num_denoising_queries / num_groups)\n        device = gt_bboxes.device\n\n        if len(gt_labels) > 0:\n            t = torch.arange(len(gt_labels), dtype=torch.long, device=device)\n            t = t.unsqueeze(0).repeat(num_groups, 1)\n            pos_assigned_gt_inds = t.flatten()\n            pos_inds = torch.arange(\n                num_groups, dtype=torch.long, device=device)\n            pos_inds = pos_inds.unsqueeze(1) * num_queries_each_group + t\n            pos_inds = pos_inds.flatten()\n        else:\n            pos_inds = pos_assigned_gt_inds = \\\n                gt_bboxes.new_tensor([], dtype=torch.long)\n\n        neg_inds = pos_inds + num_queries_each_group // 2\n\n        # AlignDETR: Get meta info and layer outputs.\n        img_h, img_w = img_meta['img_shape']\n        dn_cls_score = img_meta['dn_cls_score']\n        dn_bbox_pred = img_meta['dn_bbox_pred']\n        factor = dn_bbox_pred.new_tensor([img_w, img_h, img_w,\n                                          img_h]).unsqueeze(0)\n\n        # AlignDETR: Convert dn_bbox_pred from xywh, normalized to xyxy,\n        #   unnormalized.\n        dn_bbox_pred = bbox_cxcywh_to_xyxy(dn_bbox_pred)\n        dn_bbox_pred = dn_bbox_pred * factor\n\n        # AlignDETR: Get label targets, label weights, and bbox weights.\n        target_results = self._get_align_detr_targets_single(\n            dn_cls_score, dn_bbox_pred, gt_labels,\n            gt_bboxes.repeat([num_groups, 1]), pos_inds, pos_assigned_gt_inds)\n\n        label_targets, label_weights, bbox_weights = target_results\n\n        # bbox targets\n        bbox_targets = torch.zeros(num_denoising_queries, 4, device=device)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image.\n        # Thus the learning target should be normalized by the image size, also\n        # the box format should be converted from defaultly x1y1x2y2 to cxcywh.\n        gt_bboxes_normalized = gt_bboxes / factor\n        gt_bboxes_targets = bbox_xyxy_to_cxcywh(gt_bboxes_normalized)\n        bbox_targets[pos_inds] = gt_bboxes_targets.repeat([num_groups, 1])\n\n        return (label_targets, label_weights, bbox_targets, bbox_weights,\n                pos_inds, neg_inds)\n\n    def _get_align_detr_targets_single(self,\n                                       cls_score: Tensor,\n                                       bbox_pred: Tensor,\n                                       gt_labels: Tensor,\n                                       pos_gt_bboxes: Tensor,\n                                       pos_inds: Tensor,\n                                       pos_assigned_gt_inds: Tensor,\n                                       layer_index: int = -1,\n                                       is_matching_queries: bool = False):\n        '''AlignDETR: Get label targets, label weights, and bbox weights based\n            on `t`, the weighted geometric average of the confident score and\n            the IoU score, to align classification and regression scores.\n\n        Args:\n            cls_score (Tensor): Box score logits from the last encoder layer\n                or a single decoder layer for one image. Shape\n                [num_queries or num_denoising_queries, cls_out_channels].\n            bbox_pred (Tensor): Sigmoid outputs from the last encoder layer\n                or a single decoder layer for one image, with unnormalized\n                coordinate (x, y, x, y) and shape\n                [num_queries or num_denoising_queries, 4].\n            gt_labels (Tensor): Ground truth classification labels for one\n                image, has shape [num_gt].\n            pos_gt_bboxes (Tensor): Positive ground truth bboxes for one\n                image, with unnormalized coordinate (x, y, x, y) and shape\n                [num_positive, 4].\n            pos_inds (Tensor): Positive prediction box indices, has shape\n                [num_positive].\n            pos_assigned_gt_inds Tensor: Positive ground truth box indices,\n                has shape [num_positive].\n            layer_index (int): decoder layer index for the outputs. Defaults\n                to `-1`.\n            is_matching_queries (bool): The outputs are from matching\n                queries or denoising queries. Defaults to `False`.\n\n        Returns:\n            tuple[Tensor]: a tuple containing the following for one image.\n\n            - label_targets (Tensor): Labels of one image. Shape\n                [num_queries or num_denoising_queries, cls_out_channels].\n            - label_weights (Tensor): Label weights of one image. Shape\n                [num_queries or num_denoising_queries, cls_out_channels].\n            - bbox_weights (Tensor): BBox weights of one image. Shape\n                [num_queries or num_denoising_queries, 4].\n        '''\n\n        # Classification loss\n        # =           1 * BCE(prob, t * rank_weights) for positive sample;\n        # = prob**gamma * BCE(prob,                0) for negative sample.\n        # That is,\n        # label_targets = 0                for negative sample;\n        #               = t * rank_weights for positive sample.\n        # label_weights = pred**gamma for negative sample;\n        #               = 1           for positive sample.\n        cls_prob = cls_score.sigmoid()\n        label_targets = torch.zeros_like(\n            cls_score, device=pos_gt_bboxes.device)\n        label_weights = cls_prob**self.gamma\n\n        bbox_weights = torch.zeros_like(bbox_pred, dtype=pos_gt_bboxes.dtype)\n\n        if len(pos_inds) == 0:\n            return label_targets, label_weights, bbox_weights\n\n        pos_cls_score_inds = (pos_inds, gt_labels[pos_assigned_gt_inds])\n        iou_scores = bbox_overlaps(\n            bbox_pred[pos_inds], pos_gt_bboxes, is_aligned=True)\n\n        # t (Tensor): The weighted geometric average of the confident score\n        #   and the IoU score, to align classification and regression scores.\n        #   Shape [num_positive].\n        t = (\n            cls_prob[pos_cls_score_inds]**self.alpha *\n            iou_scores**(1 - self.alpha))\n        t = torch.clamp(t, 0.01).detach()\n\n        # Calculate rank_weights for matching queries.\n        if is_matching_queries:\n            # rank_weights (Tensor): Weights of each group of predictions\n            #   assigned to the same positive gt bbox. Shape [num_positive].\n            rank_weights = torch.zeros_like(t, dtype=self.weight_table.dtype)\n\n            assert 0 <= layer_index < len(self.weight_table), layer_index\n            rank_to_weight = self.weight_table[layer_index].to(\n                rank_weights.device)\n            unique_gt_inds = torch.unique(pos_assigned_gt_inds)\n\n            # For each positive gt bbox, get all predictions assigned to it,\n            #   then calculate rank weights for this group of predictions.\n            for gt_index in unique_gt_inds:\n                pred_group_cond = pos_assigned_gt_inds == gt_index\n                # Weights are based on their rank sorted by t in the group.\n                pred_group = t[pred_group_cond]\n                indices = pred_group.sort(descending=True)[1]\n                group_weights = torch.zeros_like(\n                    indices, dtype=self.weight_table.dtype)\n                group_weights[indices] = rank_to_weight[:len(indices)]\n                rank_weights[pred_group_cond] = group_weights\n\n            t = t * rank_weights\n            pos_bbox_weights = rank_weights.unsqueeze(-1).repeat(\n                1, bbox_pred.size(-1))\n            bbox_weights[pos_inds] = pos_bbox_weights\n        else:\n            bbox_weights[pos_inds] = 1.0\n\n        label_targets[pos_cls_score_inds] = t\n        label_weights[pos_cls_score_inds] = 1.0\n\n        return label_targets, label_weights, bbox_weights\n"
  },
  {
    "path": "projects/AlignDETR/align_detr/mixed_hungarian_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Optional, Union\n\nimport torch\nfrom mmengine import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom scipy.optimize import linear_sum_assignment\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules import AssignResult, BaseAssigner\nfrom mmdet.registry import TASK_UTILS\n\n\n@TASK_UTILS.register_module()\nclass MixedHungarianAssigner(BaseAssigner):\n    \"\"\"Computes 1-to-k matching between ground truth and predictions.\n\n    This class computes an assignment between the targets and the predictions\n    based on the costs. The costs are weighted sum of some components.\n    For DETR the costs are weighted sum of classification cost, regression L1\n    cost and regression iou cost. The targets don't include the no_object, so\n    generally there are more predictions than targets. After the 1-to-k\n    gt-pred matching, the un-matched are treated as backgrounds. Thus\n    each query prediction will be assigned with `0` or a positive integer\n    indicating the ground truth index:\n\n    - 0: negative sample, no assigned gt\n    - positive integer: positive sample, index (1-based) of assigned gt\n\n    Args:\n        match_costs (:obj:`ConfigDict` or dict or \\\n            List[Union[:obj:`ConfigDict`, dict]]): Match cost configs.\n    \"\"\"\n\n    def __init__(\n        self, match_costs: Union[List[Union[dict, ConfigDict]], dict,\n                                 ConfigDict]\n    ) -> None:\n\n        if isinstance(match_costs, dict):\n            match_costs = [match_costs]\n        elif isinstance(match_costs, list):\n            assert len(match_costs) > 0, \\\n                'match_costs must not be a empty list.'\n\n        self.match_costs = [\n            TASK_UTILS.build(match_cost) for match_cost in match_costs\n        ]\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               img_meta: Optional[dict] = None,\n               k: int = 1,\n               **kwargs) -> AssignResult:\n        \"\"\"Computes 1-to-k gt-pred matching based on the weighted costs.\n\n        This method assign each query prediction to a ground truth or\n        background. The `assigned_gt_inds` with -1 means don't care,\n        0 means negative sample, and positive number is the index (1-based)\n        of assigned gt.\n        The assignment is done in the following steps, the order matters.\n\n        1. Assign every prediction to -1.\n        2. Compute the weighted costs, each cost has shape\n            (num_preds, num_gts).\n        3. Update k according to num_preds and num_gts, then repeat\n            costs k times to shape: (num_preds, k * num_gts), so that each\n            gt will match k predictions.\n        4. Do Hungarian matching on CPU based on the costs.\n        5. Assign all to 0 (background) first, then for each matched pair\n           between predictions and gts, treat this prediction as foreground\n           and assign the corresponding gt index (plus 1) to it.\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places. It may includes ``masks``, with shape\n                (n, h, w) or (n, l).\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                ``labels``, with shape (k, ) and ``masks``, with shape\n                (k, h, w) or (k, l).\n            img_meta (dict): Image information for one image.\n\n        Returns:\n            :obj:`AssignResult`: The assigned result.\n        \"\"\"\n        assert isinstance(gt_instances.labels, Tensor)\n        num_gts, num_preds = len(gt_instances), len(pred_instances)\n        gt_labels = gt_instances.labels\n        device = gt_labels.device\n\n        # 1. Assign -1 by default.\n        assigned_gt_inds = torch.full((num_preds, ),\n                                      -1,\n                                      dtype=torch.long,\n                                      device=device)\n        assigned_labels = torch.full((num_preds, ),\n                                     -1,\n                                     dtype=torch.long,\n                                     device=device)\n\n        if num_gts == 0 or num_preds == 0:\n            # No ground truth or boxes, return empty assignment.\n            if num_gts == 0:\n                # No ground truth, assign all to background.\n                assigned_gt_inds[:] = 0\n            return AssignResult(\n                num_gts=num_gts,\n                gt_inds=assigned_gt_inds,\n                max_overlaps=None,\n                labels=assigned_labels)\n\n        # 2. Compute weighted costs.\n        cost_list = []\n        for match_cost in self.match_costs:\n            cost = match_cost(\n                pred_instances=pred_instances,\n                gt_instances=gt_instances,\n                img_meta=img_meta)\n            cost_list.append(cost)\n        cost = torch.stack(cost_list).sum(dim=0)\n\n        # 3. Update k according to num_preds and num_gts,  then\n        #   repeat the ground truth k times to perform 1-to-k gt-pred\n        #   matching. For example, if num_preds = 900, num_gts = 3, then\n        #   there are only 3 gt-pred pairs in sum for 1-1 matching.\n        #   However, for 1-k gt-pred matching, if k = 4, then each\n        #   gt is assigned 4 unique predictions, so there would be 12\n        #   gt-pred pairs in sum.\n        k = max(1, min(k, num_preds // num_gts))\n        cost = cost.repeat(1, k)\n\n        # 4. Do Hungarian matching on CPU using linear_sum_assignment.\n        cost = cost.detach().cpu()\n        if linear_sum_assignment is None:\n            raise ImportError('Please run \"pip install scipy\" '\n                              'to install scipy first.')\n\n        matched_row_inds, matched_col_inds = linear_sum_assignment(cost)\n        matched_row_inds = torch.from_numpy(matched_row_inds).to(device)\n        matched_col_inds = torch.from_numpy(matched_col_inds).to(device)\n\n        matched_col_inds = matched_col_inds % num_gts\n        # 5. Assign backgrounds and foregrounds.\n        # Assign all indices to backgrounds first.\n        assigned_gt_inds[:] = 0\n        # Assign foregrounds based on matching results.\n        assigned_gt_inds[matched_row_inds] = matched_col_inds + 1\n        assigned_labels[matched_row_inds] = gt_labels[matched_col_inds]\n        assign_result = AssignResult(\n            num_gts=k * num_gts,\n            gt_inds=assigned_gt_inds,\n            max_overlaps=None,\n            labels=assigned_labels)\n\n        return assign_result\n"
  },
  {
    "path": "projects/AlignDETR/align_detr/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Any, List, Optional\n\n\nclass KeysRecorder:\n    \"\"\"Wrap object to record its `__getitem__` keys in the history.\n\n    Args:\n        obj (object): Any object that supports `__getitem__`.\n        keys (List): List of keys already recorded. Default to None.\n    \"\"\"\n\n    def __init__(self, obj: Any, keys: Optional[List[Any]] = None) -> None:\n        self.obj = obj\n\n        if keys is None:\n            keys = []\n        self.keys = keys\n\n    def __getitem__(self, key: Any) -> 'KeysRecorder':\n        \"\"\"Wrap method `__getitem__`  to record its keys.\n\n        Args:\n            key: Key that is passed to the object.\n\n        Returns:\n            result (KeysRecorder): KeysRecorder instance that wraps sub_obj.\n        \"\"\"\n        sub_obj = self.obj.__getitem__(key)\n        keys = self.keys.copy()\n        keys.append(key)\n        # Create a KeysRecorder instance from the sub_obj.\n        result = KeysRecorder(sub_obj, keys)\n        return result\n"
  },
  {
    "path": "projects/AlignDETR/configs/align_detr-4scale_r50_8xb2-12e_coco.py",
    "content": "_base_ = [\n    '../../../configs/_base_/datasets/coco_detection.py',\n    '../../../configs/_base_/default_runtime.py'\n]\ncustom_imports = dict(\n    imports=['projects.AlignDETR.align_detr'], allow_failed_imports=False)\n\nmodel = dict(\n    type='DINO',\n    num_queries=900,  # num_matching_queries\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        # AlignDETR: Only freeze stem.\n        frozen_stages=0,\n        norm_cfg=dict(type='FrozenBN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        # AlignDETR: Add conv bias.\n        bias=True,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0))),  # 0.1 for DeformDETR\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=4,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128,\n        normalize=True,\n        # AlignDETR: Set offset and temperature the same as DeformDETR.\n        offset=-0.5,  # -0.5 for DeformDETR\n        temperature=10000),  # 10000 for DeformDETR\n    bbox_head=dict(\n        type='AlignDETRHead',\n        # AlignDETR: First 6 elements of `all_layers_num_gt_repeat` are for\n        #   decoder layers' outputs. The last element is for encoder layer.\n        all_layers_num_gt_repeat=[2, 2, 2, 2, 2, 1, 2],\n        alpha=0.25,\n        gamma=2.0,\n        tau=1.5,\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(  # TODO: Move to model.train_cfg ?\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None,\n                       num_dn_queries=100)),  # TODO: half num_dn_queries\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MixedHungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))  # 100 for DeformDETR\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        # AlignDETR: Filter empty gt.\n        filter_cfg=dict(filter_empty_gt=True),\n        pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,  # 0.0002 for DeformDETR\n        weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(\n        custom_keys={'backbone': dict(lr_mult=0.1)},\n        # AlignDETR: No norm decay.\n        norm_decay_mult=0.0)\n)  # custom_keys contains sampling_offsets and reference_points in DeformDETR  # noqa\n\n# learning policy\nmax_epochs = 12\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=2000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "projects/AlignDETR/configs/align_detr-4scale_r50_8xb2-24e_coco.py",
    "content": "_base_ = './align_detr-4scale_r50_8xb2-12e_coco.py'\nmax_epochs = 24\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=2000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[20],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "projects/CO-DETR/README.md",
    "content": "# CO-DETR\n\n> [DETRs with Collaborative Hybrid Assignments Training](https://arxiv.org/abs/2211.12860)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nIn this paper, we provide the observation that too few queries assigned as positive samples in DETR with one-to-one set matching leads to sparse supervision on the encoder's output which considerably hurt the discriminative feature learning of the encoder and vice visa for attention learning in the decoder. To alleviate this, we present a novel collaborative hybrid assignments training scheme, namely Co-DETR, to learn more efficient and effective DETR-based detectors from versatile label assignment manners. This new training scheme can easily enhance the encoder's learning ability in end-to-end detectors by training the multiple parallel auxiliary heads supervised by one-to-many label assignments such as ATSS and Faster RCNN. In addition, we conduct extra customized positive queries by extracting the positive coordinates from these auxiliary heads to improve the training efficiency of positive samples in the decoder. In inference, these auxiliary heads are discarded and thus our method introduces no additional parameters and computational cost to the original detector while requiring no hand-crafted non-maximum suppression (NMS). We conduct extensive experiments to evaluate the effectiveness of the proposed approach on DETR variants, including DAB-DETR, Deformable-DETR, and DINO-Deformable-DETR. The state-of-the-art DINO-Deformable-DETR with Swin-L can be improved from 58.5% to 59.5% AP on COCO val. Surprisingly, incorporated with ViT-L backbone, we achieve 66.0% AP on COCO test-dev and 67.9% AP on LVIS val, outperforming previous methods by clear margins with much fewer model sizes.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/dceaf7ee-cd6c-4be0-b7b1-5b01a7f11724\"/>\n</div>\n\n## Results and Models\n\n|   Model   | Backbone | Epochs | Aug  |            Dataset            | box AP |                                 Config                                 |                                                                                                                                                     Download                                                                                                                                                      |\n| :-------: | :------: | :----: | :--: | :---------------------------: | :----: | :--------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  Co-DINO  |   R50    |   12   | LSJ  |             COCO              |  52.0  |    [config](configs/codino/co_dino_5scale_r50_lsj_8xb2_1x_coco.py)     | [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_r50_lsj_8xb2_1x_coco/co_dino_5scale_r50_lsj_8xb2_1x_coco-69a72d67.pth)\\\\ [log](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_r50_lsj_8xb2_1x_coco/co_dino_5scale_r50_lsj_8xb2_1x_coco_20230818_150457.json) |\n| Co-DINO\\* |   R50    |   12   | DETR |             COCO              |  52.1  |      [config](configs/codino/co_dino_5scale_r50_8xb2_1x_coco.py)       |                                                                                                      [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_r50_1x_coco-7481f903.pth)                                                                                                      |\n| Co-DINO\\* |   R50    |   36   | LSJ  |             COCO              |  54.8  |    [config](configs/codino/co_dino_5scale_r50_lsj_8xb2_3x_coco.py)     |                                                                                                    [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_lsj_r50_3x_coco-fe5a6829.pth)                                                                                                    |\n| Co-DINO\\* |  Swin-L  |   12   | DETR |             COCO              |  58.9  |    [config](configs/codino/co_dino_5scale_swin_l_16xb1_1x_coco.py)     |                                                                                                  [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_swin_large_1x_coco-27c13da4.pth)                                                                                                   |\n| Co-DINO\\* |  Swin-L  |   12   | LSJ  |             COCO              |  59.3  |  [config](configs/codino/co_dino_5scale_swin_l_lsj_16xb1_1x_coco.py)   |                                                                                                [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_lsj_swin_large_1x_coco-3af73af2.pth)                                                                                                 |\n| Co-DINO\\* |  Swin-L  |   36   | DETR |             COCO              |  60.0  |    [config](configs/codino/co_dino_5scale_swin_l_16xb1_3x_coco.py)     |                                                                                                  [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_swin_large_3x_coco-d7a6d8af.pth)                                                                                                   |\n| Co-DINO\\* |  Swin-L  |   36   | LSJ  |             COCO              |  60.7  |  [config](configs/codino/co_dino_5scale_swin_l_lsj_16xb1_3x_coco.py)   |                                                                                                [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_lsj_swin_large_1x_coco-3af73af2.pth)                                                                                                 |\n| Co-DINO\\* |  Swin-L  |   16   | DETR | Objects365 pre-trained + COCO |  64.1  | [config](configs/codino/co_dino_5scale_swin_l_16xb1_16e_o365tococo.py) |                                                                                               [model](https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_swin_large_16e_o365tococo-614254c9.pth)                                                                                               |\n\nNote\n\n- Models labeled * are not trained by us, but from [CO-DETR](https://github.com/Sense-X/Co-DETR) official website.\n- We find that the performance is unstable and may fluctuate by about 0.3 mAP.\n- If you want to save GPU memory by enabling checkpointing, please use the `pip install fairscale` command.\n"
  },
  {
    "path": "projects/CO-DETR/codetr/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .co_atss_head import CoATSSHead\nfrom .co_dino_head import CoDINOHead\nfrom .co_roi_head import CoStandardRoIHead\nfrom .codetr import CoDETR\nfrom .transformer import (CoDinoTransformer, DetrTransformerDecoderLayer,\n                          DetrTransformerEncoder, DinoTransformerDecoder)\n\n__all__ = [\n    'CoDETR', 'CoDinoTransformer', 'DinoTransformerDecoder', 'CoDINOHead',\n    'CoATSSHead', 'CoStandardRoIHead', 'DetrTransformerEncoder',\n    'DetrTransformerDecoderLayer'\n]\n"
  },
  {
    "path": "projects/CO-DETR/codetr/co_atss_head.py",
    "content": "from typing import List\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.models.dense_heads import ATSSHead\nfrom mmdet.models.utils import images_to_levels, multi_apply\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList, OptInstanceList, reduce_mean\n\n\n@MODELS.register_module()\nclass CoATSSHead(ATSSHead):\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            centernesses: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W)\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W)\n            centernesses (list[Tensor]): Centerness for each scale\n                level with shape (N, num_anchors * 1, H, W)\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], Optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        (anchor_list, labels_list, label_weights_list, bbox_targets_list,\n         bbox_weights_list, avg_factor, ori_anchors, ori_labels,\n         ori_bbox_targets) = cls_reg_targets\n\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n\n        losses_cls, losses_bbox, loss_centerness, \\\n            bbox_avg_factor = multi_apply(\n                self.loss_by_feat_single,\n                anchor_list,\n                cls_scores,\n                bbox_preds,\n                centernesses,\n                labels_list,\n                label_weights_list,\n                bbox_targets_list,\n                avg_factor=avg_factor)\n\n        bbox_avg_factor = sum(bbox_avg_factor)\n        bbox_avg_factor = reduce_mean(bbox_avg_factor).clamp_(min=1).item()\n        losses_bbox = list(map(lambda x: x / bbox_avg_factor, losses_bbox))\n\n        # diff\n        pos_coords = (ori_anchors, ori_labels, ori_bbox_targets, 'atss')\n        return dict(\n            loss_cls=losses_cls,\n            loss_bbox=losses_bbox,\n            loss_centerness=loss_centerness,\n            pos_coords=pos_coords)\n\n    def get_targets(self,\n                    anchor_list: List[List[Tensor]],\n                    valid_flag_list: List[List[Tensor]],\n                    batch_gt_instances: InstanceList,\n                    batch_img_metas: List[dict],\n                    batch_gt_instances_ignore: OptInstanceList = None,\n                    unmap_outputs: bool = True) -> tuple:\n        \"\"\"Get targets for ATSS head.\n\n        This method is almost the same as `AnchorHead.get_targets()`. Besides\n        returning the targets as the parent method does, it also returns the\n        anchors as the first element of the returned tuple.\n        \"\"\"\n        num_imgs = len(batch_img_metas)\n        assert len(anchor_list) == len(valid_flag_list) == num_imgs\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        num_level_anchors_list = [num_level_anchors] * num_imgs\n\n        # concat all level anchors and flags to a single tensor\n        for i in range(num_imgs):\n            assert len(anchor_list[i]) == len(valid_flag_list[i])\n            anchor_list[i] = torch.cat(anchor_list[i])\n            valid_flag_list[i] = torch.cat(valid_flag_list[i])\n\n        # compute targets for each image\n        if batch_gt_instances_ignore is None:\n            batch_gt_instances_ignore = [None] * num_imgs\n        (all_anchors, all_labels, all_label_weights, all_bbox_targets,\n         all_bbox_weights, pos_inds_list, neg_inds_list,\n         sampling_results_list) = multi_apply(\n             self._get_targets_single,\n             anchor_list,\n             valid_flag_list,\n             num_level_anchors_list,\n             batch_gt_instances,\n             batch_img_metas,\n             batch_gt_instances_ignore,\n             unmap_outputs=unmap_outputs)\n        # Get `avg_factor` of all images, which calculate in `SamplingResult`.\n        # When using sampling method, avg_factor is usually the sum of\n        # positive and negative priors. When using `PseudoSampler`,\n        # `avg_factor` is usually equal to the number of positive priors.\n        avg_factor = sum(\n            [results.avg_factor for results in sampling_results_list])\n        # split targets to a list w.r.t. multiple levels\n        anchors_list = images_to_levels(all_anchors, num_level_anchors)\n        labels_list = images_to_levels(all_labels, num_level_anchors)\n        label_weights_list = images_to_levels(all_label_weights,\n                                              num_level_anchors)\n        bbox_targets_list = images_to_levels(all_bbox_targets,\n                                             num_level_anchors)\n        bbox_weights_list = images_to_levels(all_bbox_weights,\n                                             num_level_anchors)\n\n        # diff\n        ori_anchors = all_anchors\n        ori_labels = all_labels\n        ori_bbox_targets = all_bbox_targets\n        return (anchors_list, labels_list, label_weights_list,\n                bbox_targets_list, bbox_weights_list, avg_factor, ori_anchors,\n                ori_labels, ori_bbox_targets)\n"
  },
  {
    "path": "projects/CO-DETR/codetr/co_dino_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import Linear\nfrom mmcv.ops import batched_nms\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models import DINOHead\nfrom mmdet.models.layers import CdnQueryGenerator\nfrom mmdet.models.layers.transformer import inverse_sigmoid\nfrom mmdet.models.utils import multi_apply\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import (bbox_cxcywh_to_xyxy, bbox_overlaps,\n                                   bbox_xyxy_to_cxcywh)\nfrom mmdet.utils import InstanceList, reduce_mean\n\n\n@MODELS.register_module()\nclass CoDINOHead(DINOHead):\n\n    def __init__(self,\n                 *args,\n                 num_query=900,\n                 transformer=None,\n                 in_channels=2048,\n                 max_pos_coords=300,\n                 dn_cfg=None,\n                 use_zero_padding=False,\n                 positional_encoding=dict(\n                     type='SinePositionalEncoding',\n                     num_feats=128,\n                     normalize=True),\n                 **kwargs):\n        self.with_box_refine = True\n        self.mixed_selection = True\n        self.in_channels = in_channels\n        self.max_pos_coords = max_pos_coords\n        self.positional_encoding = positional_encoding\n        self.num_query = num_query\n        self.use_zero_padding = use_zero_padding\n\n        if 'two_stage_num_proposals' in transformer:\n            assert transformer['two_stage_num_proposals'] == num_query, \\\n                'two_stage_num_proposals must be equal to num_query for DINO'\n        else:\n            transformer['two_stage_num_proposals'] = num_query\n        transformer['as_two_stage'] = True\n        if self.mixed_selection:\n            transformer['mixed_selection'] = self.mixed_selection\n        self.transformer = transformer\n        self.act_cfg = transformer.get('act_cfg',\n                                       dict(type='ReLU', inplace=True))\n\n        super().__init__(*args, **kwargs)\n\n        self.activate = MODELS.build(self.act_cfg)\n        self.positional_encoding = MODELS.build(self.positional_encoding)\n        self.init_denoising(dn_cfg)\n\n    def _init_layers(self):\n        self.transformer = MODELS.build(self.transformer)\n        self.embed_dims = self.transformer.embed_dims\n        assert hasattr(self.positional_encoding, 'num_feats')\n        num_feats = self.positional_encoding.num_feats\n        assert num_feats * 2 == self.embed_dims, 'embed_dims should' \\\n            f' be exactly 2 times of num_feats. Found {self.embed_dims}' \\\n            f' and {num_feats}.'\n        \"\"\"Initialize classification branch and regression branch of head.\"\"\"\n        fc_cls = Linear(self.embed_dims, self.cls_out_channels)\n        reg_branch = []\n        for _ in range(self.num_reg_fcs):\n            reg_branch.append(Linear(self.embed_dims, self.embed_dims))\n            reg_branch.append(nn.ReLU())\n        reg_branch.append(Linear(self.embed_dims, 4))\n        reg_branch = nn.Sequential(*reg_branch)\n\n        def _get_clones(module, N):\n            return nn.ModuleList([copy.deepcopy(module) for i in range(N)])\n\n        # last reg_branch is used to generate proposal from\n        # encode feature map when as_two_stage is True.\n        num_pred = (self.transformer.decoder.num_layers + 1) if \\\n            self.as_two_stage else self.transformer.decoder.num_layers\n\n        self.cls_branches = _get_clones(fc_cls, num_pred)\n        self.reg_branches = _get_clones(reg_branch, num_pred)\n\n        self.downsample = nn.Sequential(\n            nn.Conv2d(\n                self.embed_dims,\n                self.embed_dims,\n                kernel_size=3,\n                stride=2,\n                padding=1), nn.GroupNorm(32, self.embed_dims))\n\n    def init_denoising(self, dn_cfg):\n        if dn_cfg is not None:\n            dn_cfg['num_classes'] = self.num_classes\n            dn_cfg['num_matching_queries'] = self.num_query\n            dn_cfg['embed_dims'] = self.embed_dims\n        self.dn_generator = CdnQueryGenerator(**dn_cfg)\n\n    def forward(self,\n                mlvl_feats,\n                img_metas,\n                dn_label_query=None,\n                dn_bbox_query=None,\n                attn_mask=None):\n        batch_size = mlvl_feats[0].size(0)\n        input_img_h, input_img_w = img_metas[0]['batch_input_shape']\n        img_masks = mlvl_feats[0].new_ones(\n            (batch_size, input_img_h, input_img_w))\n        for img_id in range(batch_size):\n            img_h, img_w = img_metas[img_id]['img_shape']\n            img_masks[img_id, :img_h, :img_w] = 0\n\n        mlvl_masks = []\n        mlvl_positional_encodings = []\n        for feat in mlvl_feats:\n            mlvl_masks.append(\n                F.interpolate(img_masks[None],\n                              size=feat.shape[-2:]).to(torch.bool).squeeze(0))\n            mlvl_positional_encodings.append(\n                self.positional_encoding(mlvl_masks[-1]))\n\n        query_embeds = None\n        hs, inter_references, topk_score, topk_anchor, enc_outputs = \\\n            self.transformer(\n                mlvl_feats,\n                mlvl_masks,\n                query_embeds,\n                mlvl_positional_encodings,\n                dn_label_query,\n                dn_bbox_query,\n                attn_mask,\n                reg_branches=self.reg_branches if self.with_box_refine else None,  # noqa:E501\n                cls_branches=self.cls_branches if self.as_two_stage else None  # noqa:E501\n            )\n        outs = []\n        num_level = len(mlvl_feats)\n        start = 0\n        for lvl in range(num_level):\n            bs, c, h, w = mlvl_feats[lvl].shape\n            end = start + h * w\n            feat = enc_outputs[start:end].permute(1, 2, 0).contiguous()\n            start = end\n            outs.append(feat.reshape(bs, c, h, w))\n        outs.append(self.downsample(outs[-1]))\n\n        hs = hs.permute(0, 2, 1, 3)\n\n        if dn_label_query is not None and dn_label_query.size(1) == 0:\n            # NOTE: If there is no target in the image, the parameters of\n            # label_embedding won't be used in producing loss, which raises\n            # RuntimeError when using distributed mode.\n            hs[0] += self.dn_generator.label_embedding.weight[0, 0] * 0.0\n\n        outputs_classes = []\n        outputs_coords = []\n\n        for lvl in range(hs.shape[0]):\n            reference = inter_references[lvl]\n            reference = inverse_sigmoid(reference, eps=1e-3)\n            outputs_class = self.cls_branches[lvl](hs[lvl])\n            tmp = self.reg_branches[lvl](hs[lvl])\n            if reference.shape[-1] == 4:\n                tmp += reference\n            else:\n                assert reference.shape[-1] == 2\n                tmp[..., :2] += reference\n            outputs_coord = tmp.sigmoid()\n            outputs_classes.append(outputs_class)\n            outputs_coords.append(outputs_coord)\n\n        outputs_classes = torch.stack(outputs_classes)\n        outputs_coords = torch.stack(outputs_coords)\n\n        return outputs_classes, outputs_coords, topk_score, topk_anchor, outs\n\n    def predict(self,\n                feats: List[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> InstanceList:\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n        outs = self.forward(feats, batch_img_metas)\n\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, rescale=rescale)\n\n        return predictions\n\n    def predict_by_feat(self,\n                        all_cls_scores,\n                        all_bbox_preds,\n                        enc_cls_scores,\n                        enc_bbox_preds,\n                        enc_outputs,\n                        batch_img_metas,\n                        rescale=True):\n\n        cls_scores = all_cls_scores[-1]\n        bbox_preds = all_bbox_preds[-1]\n\n        result_list = []\n        for img_id in range(len(batch_img_metas)):\n            cls_score = cls_scores[img_id]\n            bbox_pred = bbox_preds[img_id]\n            img_meta = batch_img_metas[img_id]\n            results = self._predict_by_feat_single(cls_score, bbox_pred,\n                                                   img_meta, rescale)\n            result_list.append(results)\n        return result_list\n\n    def _predict_by_feat_single(self,\n                                cls_score: Tensor,\n                                bbox_pred: Tensor,\n                                img_meta: dict,\n                                rescale: bool = True) -> InstanceData:\n        \"\"\"Transform outputs from the last decoder layer into bbox predictions\n        for each image.\n\n        Args:\n            cls_score (Tensor): Box score logits from the last decoder layer\n                for each image. Shape [num_queries, cls_out_channels].\n            bbox_pred (Tensor): Sigmoid outputs from the last decoder layer\n                for each image, with coordinate format (cx, cy, w, h) and\n                shape [num_queries, 4].\n            img_meta (dict): Image meta info.\n            rescale (bool): If True, return boxes in original image\n                space. Default True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert len(cls_score) == len(bbox_pred)  # num_queries\n        max_per_img = self.test_cfg.get('max_per_img', self.num_query)\n        score_thr = self.test_cfg.get('score_thr', 0)\n        with_nms = self.test_cfg.get('nms', None)\n\n        img_shape = img_meta['img_shape']\n        # exclude background\n        if self.loss_cls.use_sigmoid:\n            cls_score = cls_score.sigmoid()\n            scores, indexes = cls_score.view(-1).topk(max_per_img)\n            det_labels = indexes % self.num_classes\n            bbox_index = indexes // self.num_classes\n            bbox_pred = bbox_pred[bbox_index]\n        else:\n            scores, det_labels = F.softmax(cls_score, dim=-1)[..., :-1].max(-1)\n            scores, bbox_index = scores.topk(max_per_img)\n            bbox_pred = bbox_pred[bbox_index]\n            det_labels = det_labels[bbox_index]\n\n        if score_thr > 0:\n            valid_mask = scores > score_thr\n            scores = scores[valid_mask]\n            bbox_pred = bbox_pred[valid_mask]\n            det_labels = det_labels[valid_mask]\n\n        det_bboxes = bbox_cxcywh_to_xyxy(bbox_pred)\n        det_bboxes[:, 0::2] = det_bboxes[:, 0::2] * img_shape[1]\n        det_bboxes[:, 1::2] = det_bboxes[:, 1::2] * img_shape[0]\n        det_bboxes[:, 0::2].clamp_(min=0, max=img_shape[1])\n        det_bboxes[:, 1::2].clamp_(min=0, max=img_shape[0])\n        if rescale:\n            assert img_meta.get('scale_factor') is not None\n            det_bboxes /= det_bboxes.new_tensor(\n                img_meta['scale_factor']).repeat((1, 2))\n\n        results = InstanceData()\n        results.bboxes = det_bboxes\n        results.scores = scores\n        results.labels = det_labels\n\n        if with_nms and results.bboxes.numel() > 0:\n            det_bboxes, keep_idxs = batched_nms(results.bboxes, results.scores,\n                                                results.labels,\n                                                self.test_cfg.nms)\n            results = results[keep_idxs]\n            results.scores = det_bboxes[:, -1]\n            results = results[:max_per_img]\n\n        return results\n\n    def loss(self, x, batch_data_samples):\n        assert self.dn_generator is not None, '\"dn_cfg\" must be set'\n\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        dn_label_query, dn_bbox_query, attn_mask, dn_meta = \\\n            self.dn_generator(batch_data_samples)\n\n        outs = self(x, batch_img_metas, dn_label_query, dn_bbox_query,\n                    attn_mask)\n\n        loss_inputs = outs[:-1] + (batch_gt_instances, batch_img_metas,\n                                   dn_meta)\n        losses = self.loss_by_feat(*loss_inputs)\n        enc_outputs = outs[-1]\n        return losses, enc_outputs\n\n    def forward_aux(self, mlvl_feats, img_metas, aux_targets, head_idx):\n        \"\"\"Forward function.\n\n        Args:\n            mlvl_feats (tuple[Tensor]): Features from the upstream\n                network, each is a 4D-tensor with shape\n                (N, C, H, W).\n            img_metas (list[dict]): List of image information.\n\n        Returns:\n            all_cls_scores (Tensor): Outputs from the classification head, \\\n                shape [nb_dec, bs, num_query, cls_out_channels]. Note \\\n                cls_out_channels should includes background.\n            all_bbox_preds (Tensor): Sigmoid outputs from the regression \\\n                head with normalized coordinate format (cx, cy, w, h). \\\n                Shape [nb_dec, bs, num_query, 4].\n            enc_outputs_class (Tensor): The score of each point on encode \\\n                feature map, has shape (N, h*w, num_class). Only when \\\n                as_two_stage is True it would be returned, otherwise \\\n                `None` would be returned.\n            enc_outputs_coord (Tensor): The proposal generate from the \\\n                encode feature map, has shape (N, h*w, 4). Only when \\\n                as_two_stage is True it would be returned, otherwise \\\n                `None` would be returned.\n        \"\"\"\n        aux_coords, aux_labels, aux_targets, aux_label_weights, \\\n            aux_bbox_weights, aux_feats, attn_masks = aux_targets\n        batch_size = mlvl_feats[0].size(0)\n        input_img_h, input_img_w = img_metas[0]['batch_input_shape']\n        img_masks = mlvl_feats[0].new_ones(\n            (batch_size, input_img_h, input_img_w))\n        for img_id in range(batch_size):\n            img_h, img_w = img_metas[img_id]['img_shape']\n            img_masks[img_id, :img_h, :img_w] = 0\n\n        mlvl_masks = []\n        mlvl_positional_encodings = []\n        for feat in mlvl_feats:\n            mlvl_masks.append(\n                F.interpolate(img_masks[None],\n                              size=feat.shape[-2:]).to(torch.bool).squeeze(0))\n            mlvl_positional_encodings.append(\n                self.positional_encoding(mlvl_masks[-1]))\n\n        query_embeds = None\n        hs, inter_references = self.transformer.forward_aux(\n            mlvl_feats,\n            mlvl_masks,\n            query_embeds,\n            mlvl_positional_encodings,\n            aux_coords,\n            pos_feats=aux_feats,\n            reg_branches=self.reg_branches if self.with_box_refine else None,\n            cls_branches=self.cls_branches if self.as_two_stage else None,\n            return_encoder_output=True,\n            attn_masks=attn_masks,\n            head_idx=head_idx)\n\n        hs = hs.permute(0, 2, 1, 3)\n        outputs_classes = []\n        outputs_coords = []\n\n        for lvl in range(hs.shape[0]):\n            reference = inter_references[lvl]\n            reference = inverse_sigmoid(reference, eps=1e-3)\n            outputs_class = self.cls_branches[lvl](hs[lvl])\n            tmp = self.reg_branches[lvl](hs[lvl])\n            if reference.shape[-1] == 4:\n                tmp += reference\n            else:\n                assert reference.shape[-1] == 2\n                tmp[..., :2] += reference\n            outputs_coord = tmp.sigmoid()\n            outputs_classes.append(outputs_class)\n            outputs_coords.append(outputs_coord)\n\n        outputs_classes = torch.stack(outputs_classes)\n        outputs_coords = torch.stack(outputs_coords)\n\n        return outputs_classes, outputs_coords, None, None\n\n    def loss_aux(self,\n                 x,\n                 pos_coords=None,\n                 head_idx=0,\n                 batch_data_samples=None):\n        batch_gt_instances = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        gt_bboxes = [b.bboxes for b in batch_gt_instances]\n        gt_labels = [b.labels for b in batch_gt_instances]\n\n        aux_targets = self.get_aux_targets(pos_coords, batch_img_metas, x,\n                                           head_idx)\n        outs = self.forward_aux(x[:-1], batch_img_metas, aux_targets, head_idx)\n        outs = outs + aux_targets\n        if gt_labels is None:\n            loss_inputs = outs + (gt_bboxes, batch_img_metas)\n        else:\n            loss_inputs = outs + (gt_bboxes, gt_labels, batch_img_metas)\n        losses = self.loss_aux_by_feat(*loss_inputs)\n        return losses\n\n    def get_aux_targets(self, pos_coords, img_metas, mlvl_feats, head_idx):\n        coords, labels, targets = pos_coords[:3]\n        head_name = pos_coords[-1]\n        bs, c = len(coords), mlvl_feats[0].shape[1]\n        max_num_coords = 0\n        all_feats = []\n        for i in range(bs):\n            label = labels[i]\n            feats = [\n                feat[i].reshape(c, -1).transpose(1, 0) for feat in mlvl_feats\n            ]\n            feats = torch.cat(feats, dim=0)\n            bg_class_ind = self.num_classes\n            pos_inds = ((label >= 0)\n                        & (label < bg_class_ind)).nonzero().squeeze(1)\n            max_num_coords = max(max_num_coords, len(pos_inds))\n            all_feats.append(feats)\n        max_num_coords = min(self.max_pos_coords, max_num_coords)\n        max_num_coords = max(9, max_num_coords)\n\n        if self.use_zero_padding:\n            attn_masks = []\n            label_weights = coords[0].new_zeros([bs, max_num_coords])\n        else:\n            attn_masks = None\n            label_weights = coords[0].new_ones([bs, max_num_coords])\n        bbox_weights = coords[0].new_zeros([bs, max_num_coords, 4])\n\n        aux_coords, aux_labels, aux_targets, aux_feats = [], [], [], []\n\n        for i in range(bs):\n            coord, label, target = coords[i], labels[i], targets[i]\n            feats = all_feats[i]\n            if 'rcnn' in head_name:\n                feats = pos_coords[-2][i]\n                num_coords_per_point = 1\n            else:\n                num_coords_per_point = coord.shape[0] // feats.shape[0]\n            feats = feats.unsqueeze(1).repeat(1, num_coords_per_point, 1)\n            feats = feats.reshape(feats.shape[0] * num_coords_per_point,\n                                  feats.shape[-1])\n            img_meta = img_metas[i]\n            img_h, img_w = img_meta['img_shape']\n            factor = coord.new_tensor([img_w, img_h, img_w,\n                                       img_h]).unsqueeze(0)\n            bg_class_ind = self.num_classes\n            pos_inds = ((label >= 0)\n                        & (label < bg_class_ind)).nonzero().squeeze(1)\n            neg_inds = (label == bg_class_ind).nonzero().squeeze(1)\n            if pos_inds.shape[0] > max_num_coords:\n                indices = torch.randperm(\n                    pos_inds.shape[0])[:max_num_coords].cuda()\n                pos_inds = pos_inds[indices]\n\n            coord = bbox_xyxy_to_cxcywh(coord[pos_inds] / factor)\n            label = label[pos_inds]\n            target = bbox_xyxy_to_cxcywh(target[pos_inds] / factor)\n            feat = feats[pos_inds]\n\n            if self.use_zero_padding:\n                label_weights[i][:len(label)] = 1\n                bbox_weights[i][:len(label)] = 1\n                attn_mask = torch.zeros([\n                    max_num_coords,\n                    max_num_coords,\n                ]).bool().to(coord.device)\n            else:\n                bbox_weights[i][:len(label)] = 1\n\n            if coord.shape[0] < max_num_coords:\n                padding_shape = max_num_coords - coord.shape[0]\n                if self.use_zero_padding:\n                    padding_coord = coord.new_zeros([padding_shape, 4])\n                    padding_label = label.new_ones([padding_shape\n                                                    ]) * self.num_classes\n                    padding_target = target.new_zeros([padding_shape, 4])\n                    padding_feat = feat.new_zeros([padding_shape, c])\n                    attn_mask[coord.shape[0]:, 0:coord.shape[0], ] = True\n                    attn_mask[:, coord.shape[0]:, ] = True\n                else:\n                    indices = torch.randperm(\n                        neg_inds.shape[0])[:padding_shape].cuda()\n                    neg_inds = neg_inds[indices]\n                    padding_coord = bbox_xyxy_to_cxcywh(coords[i][neg_inds] /\n                                                        factor)\n                    padding_label = labels[i][neg_inds]\n                    padding_target = bbox_xyxy_to_cxcywh(targets[i][neg_inds] /\n                                                         factor)\n                    padding_feat = feats[neg_inds]\n                coord = torch.cat((coord, padding_coord), dim=0)\n                label = torch.cat((label, padding_label), dim=0)\n                target = torch.cat((target, padding_target), dim=0)\n                feat = torch.cat((feat, padding_feat), dim=0)\n            if self.use_zero_padding:\n                attn_masks.append(attn_mask.unsqueeze(0))\n            aux_coords.append(coord.unsqueeze(0))\n            aux_labels.append(label.unsqueeze(0))\n            aux_targets.append(target.unsqueeze(0))\n            aux_feats.append(feat.unsqueeze(0))\n\n        if self.use_zero_padding:\n            attn_masks = torch.cat(\n                attn_masks, dim=0).unsqueeze(1).repeat(1, 8, 1, 1)\n            attn_masks = attn_masks.reshape(bs * 8, max_num_coords,\n                                            max_num_coords)\n        else:\n            attn_masks = None\n\n        aux_coords = torch.cat(aux_coords, dim=0)\n        aux_labels = torch.cat(aux_labels, dim=0)\n        aux_targets = torch.cat(aux_targets, dim=0)\n        aux_feats = torch.cat(aux_feats, dim=0)\n        aux_label_weights = label_weights\n        aux_bbox_weights = bbox_weights\n        return (aux_coords, aux_labels, aux_targets, aux_label_weights,\n                aux_bbox_weights, aux_feats, attn_masks)\n\n    def loss_aux_by_feat(self,\n                         all_cls_scores,\n                         all_bbox_preds,\n                         enc_cls_scores,\n                         enc_bbox_preds,\n                         aux_coords,\n                         aux_labels,\n                         aux_targets,\n                         aux_label_weights,\n                         aux_bbox_weights,\n                         aux_feats,\n                         attn_masks,\n                         gt_bboxes_list,\n                         gt_labels_list,\n                         img_metas,\n                         gt_bboxes_ignore=None):\n        num_dec_layers = len(all_cls_scores)\n        all_labels = [aux_labels for _ in range(num_dec_layers)]\n        all_label_weights = [aux_label_weights for _ in range(num_dec_layers)]\n        all_bbox_targets = [aux_targets for _ in range(num_dec_layers)]\n        all_bbox_weights = [aux_bbox_weights for _ in range(num_dec_layers)]\n        img_metas_list = [img_metas for _ in range(num_dec_layers)]\n        all_gt_bboxes_ignore_list = [\n            gt_bboxes_ignore for _ in range(num_dec_layers)\n        ]\n\n        losses_cls, losses_bbox, losses_iou = multi_apply(\n            self._loss_aux_by_feat_single, all_cls_scores, all_bbox_preds,\n            all_labels, all_label_weights, all_bbox_targets, all_bbox_weights,\n            img_metas_list, all_gt_bboxes_ignore_list)\n\n        loss_dict = dict()\n        # loss of proposal generated from encode feature map.\n\n        # loss from the last decoder layer\n        loss_dict['loss_cls_aux'] = losses_cls[-1]\n        loss_dict['loss_bbox_aux'] = losses_bbox[-1]\n        loss_dict['loss_iou_aux'] = losses_iou[-1]\n        # loss from other decoder layers\n        num_dec_layer = 0\n        for loss_cls_i, loss_bbox_i, loss_iou_i in zip(losses_cls[:-1],\n                                                       losses_bbox[:-1],\n                                                       losses_iou[:-1]):\n            loss_dict[f'd{num_dec_layer}.loss_cls_aux'] = loss_cls_i\n            loss_dict[f'd{num_dec_layer}.loss_bbox_aux'] = loss_bbox_i\n            loss_dict[f'd{num_dec_layer}.loss_iou_aux'] = loss_iou_i\n            num_dec_layer += 1\n        return loss_dict\n\n    def _loss_aux_by_feat_single(self,\n                                 cls_scores,\n                                 bbox_preds,\n                                 labels,\n                                 label_weights,\n                                 bbox_targets,\n                                 bbox_weights,\n                                 img_metas,\n                                 gt_bboxes_ignore_list=None):\n        num_imgs = cls_scores.size(0)\n        num_q = cls_scores.size(1)\n\n        try:\n            labels = labels.reshape(num_imgs * num_q)\n            label_weights = label_weights.reshape(num_imgs * num_q)\n            bbox_targets = bbox_targets.reshape(num_imgs * num_q, 4)\n            bbox_weights = bbox_weights.reshape(num_imgs * num_q, 4)\n        except Exception:\n            return cls_scores.mean() * 0, cls_scores.mean(\n            ) * 0, cls_scores.mean() * 0\n\n        bg_class_ind = self.num_classes\n        num_total_pos = len(\n            ((labels >= 0) & (labels < bg_class_ind)).nonzero().squeeze(1))\n        num_total_neg = num_imgs * num_q - num_total_pos\n\n        # classification loss\n        cls_scores = cls_scores.reshape(-1, self.cls_out_channels)\n        # construct weighted avg_factor to match with the official DETR repo\n        cls_avg_factor = num_total_pos * 1.0 + \\\n            num_total_neg * self.bg_cls_weight\n        if self.sync_cls_avg_factor:\n            cls_avg_factor = reduce_mean(\n                cls_scores.new_tensor([cls_avg_factor]))\n        cls_avg_factor = max(cls_avg_factor, 1)\n\n        bg_class_ind = self.num_classes\n        pos_inds = ((labels >= 0)\n                    & (labels < bg_class_ind)).nonzero().squeeze(1)\n        scores = label_weights.new_zeros(labels.shape)\n        pos_bbox_targets = bbox_targets[pos_inds]\n        pos_decode_bbox_targets = bbox_cxcywh_to_xyxy(pos_bbox_targets)\n        pos_bbox_pred = bbox_preds.reshape(-1, 4)[pos_inds]\n        pos_decode_bbox_pred = bbox_cxcywh_to_xyxy(pos_bbox_pred)\n        scores[pos_inds] = bbox_overlaps(\n            pos_decode_bbox_pred.detach(),\n            pos_decode_bbox_targets,\n            is_aligned=True)\n        loss_cls = self.loss_cls(\n            cls_scores, (labels, scores),\n            weight=label_weights,\n            avg_factor=cls_avg_factor)\n\n        # Compute the average number of gt boxes across all gpus, for\n        # normalization purposes\n        num_total_pos = loss_cls.new_tensor([num_total_pos])\n        num_total_pos = torch.clamp(reduce_mean(num_total_pos), min=1).item()\n\n        # construct factors used for rescale bboxes\n        factors = []\n        for img_meta, bbox_pred in zip(img_metas, bbox_preds):\n            img_h, img_w = img_meta['img_shape']\n            factor = bbox_pred.new_tensor([img_w, img_h, img_w,\n                                           img_h]).unsqueeze(0).repeat(\n                                               bbox_pred.size(0), 1)\n            factors.append(factor)\n        factors = torch.cat(factors, 0)\n\n        # DETR regress the relative position of boxes (cxcywh) in the image,\n        # thus the learning target is normalized by the image size. So here\n        # we need to re-scale them for calculating IoU loss\n        bbox_preds = bbox_preds.reshape(-1, 4)\n        bboxes = bbox_cxcywh_to_xyxy(bbox_preds) * factors\n        bboxes_gt = bbox_cxcywh_to_xyxy(bbox_targets) * factors\n\n        # regression IoU loss, defaultly GIoU loss\n        loss_iou = self.loss_iou(\n            bboxes, bboxes_gt, bbox_weights, avg_factor=num_total_pos)\n\n        # regression L1 loss\n        loss_bbox = self.loss_bbox(\n            bbox_preds, bbox_targets, bbox_weights, avg_factor=num_total_pos)\n        return loss_cls, loss_bbox, loss_iou\n"
  },
  {
    "path": "projects/CO-DETR/codetr/co_roi_head.py",
    "content": "from typing import List, Tuple\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads import StandardRoIHead\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.utils import unpack_gt_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.bbox import bbox2roi\nfrom mmdet.utils import InstanceList\n\n\n@MODELS.register_module()\nclass CoStandardRoIHead(StandardRoIHead):\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: List[DetDataSample]) -> dict:\n        max_proposal = 2000\n\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, _ = outputs\n\n        # assign gts and sample proposals\n        num_imgs = len(batch_data_samples)\n        sampling_results = []\n        for i in range(num_imgs):\n            # rename rpn_results.bboxes to rpn_results.priors\n            rpn_results = rpn_results_list[i]\n            rpn_results.priors = rpn_results.pop('bboxes')\n\n            assign_result = self.bbox_assigner.assign(\n                rpn_results, batch_gt_instances[i],\n                batch_gt_instances_ignore[i])\n            sampling_result = self.bbox_sampler.sample(\n                assign_result,\n                rpn_results,\n                batch_gt_instances[i],\n                feats=[lvl_feat[i][None] for lvl_feat in x])\n            sampling_results.append(sampling_result)\n\n        losses = dict()\n        # bbox head forward and loss\n        if self.with_bbox:\n            bbox_results = self.bbox_loss(x, sampling_results)\n            losses.update(bbox_results['loss_bbox'])\n\n            bbox_targets = bbox_results['bbox_targets']\n            for res in sampling_results:\n                max_proposal = min(max_proposal, res.bboxes.shape[0])\n            ori_coords = bbox2roi([res.bboxes for res in sampling_results])\n            ori_proposals, ori_labels, \\\n                ori_bbox_targets, ori_bbox_feats = [], [], [], []\n            for i in range(num_imgs):\n                idx = (ori_coords[:, 0] == i).nonzero().squeeze(1)\n                idx = idx[:max_proposal]\n                ori_proposal = ori_coords[idx][:, 1:].unsqueeze(0)\n                ori_label = bbox_targets[0][idx].unsqueeze(0)\n                ori_bbox_target = bbox_targets[2][idx].unsqueeze(0)\n                ori_bbox_feat = bbox_results['bbox_feats'].mean(-1).mean(-1)\n                ori_bbox_feat = ori_bbox_feat[idx].unsqueeze(0)\n                ori_proposals.append(ori_proposal)\n                ori_labels.append(ori_label)\n                ori_bbox_targets.append(ori_bbox_target)\n                ori_bbox_feats.append(ori_bbox_feat)\n            ori_coords = torch.cat(ori_proposals, dim=0)\n            ori_labels = torch.cat(ori_labels, dim=0)\n            ori_bbox_targets = torch.cat(ori_bbox_targets, dim=0)\n            ori_bbox_feats = torch.cat(ori_bbox_feats, dim=0)\n            pos_coords = (ori_coords, ori_labels, ori_bbox_targets,\n                          ori_bbox_feats, 'rcnn')\n            losses.update(pos_coords=pos_coords)\n\n        return losses\n\n    def bbox_loss(self, x: Tuple[Tensor],\n                  sampling_results: List[SamplingResult]) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the bbox head on\n        the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n        \"\"\"\n        rois = bbox2roi([res.priors for res in sampling_results])\n        bbox_results = self._bbox_forward(x, rois)\n\n        bbox_loss_and_target = self.bbox_head.loss_and_target(\n            cls_score=bbox_results['cls_score'],\n            bbox_pred=bbox_results['bbox_pred'],\n            rois=rois,\n            sampling_results=sampling_results,\n            rcnn_train_cfg=self.train_cfg)\n\n        bbox_results.update(loss_bbox=bbox_loss_and_target['loss_bbox'])\n        # diff\n        bbox_results.update(bbox_targets=bbox_loss_and_target['bbox_targets'])\n        return bbox_results\n"
  },
  {
    "path": "projects/CO-DETR/codetr/codetr.py",
    "content": "import copy\nfrom typing import Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.models.detectors.base import BaseDetector\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList, SampleList\nfrom mmdet.utils import InstanceList, OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass CoDETR(BaseDetector):\n\n    def __init__(\n            self,\n            backbone,\n            neck=None,\n            query_head=None,  # detr head\n            rpn_head=None,  # two-stage rpn\n            roi_head=[None],  # two-stage\n            bbox_head=[None],  # one-stage\n            train_cfg=[None, None],\n            test_cfg=[None, None],\n            # Control whether to consider positive samples\n            # from the auxiliary head as additional positive queries.\n            with_pos_coord=True,\n            use_lsj=True,\n            eval_module='detr',\n            # Evaluate the Nth head.\n            eval_index=0,\n            data_preprocessor: OptConfigType = None,\n            init_cfg: OptMultiConfig = None):\n        super(CoDETR, self).__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.with_pos_coord = with_pos_coord\n        self.use_lsj = use_lsj\n\n        assert eval_module in ['detr', 'one-stage', 'two-stage']\n        self.eval_module = eval_module\n\n        self.backbone = MODELS.build(backbone)\n        if neck is not None:\n            self.neck = MODELS.build(neck)\n        # Module index for evaluation\n        self.eval_index = eval_index\n        head_idx = 0\n        if query_head is not None:\n            query_head.update(train_cfg=train_cfg[head_idx] if (\n                train_cfg is not None and train_cfg[head_idx] is not None\n            ) else None)\n            query_head.update(test_cfg=test_cfg[head_idx])\n            self.query_head = MODELS.build(query_head)\n            self.query_head.init_weights()\n            head_idx += 1\n\n        if rpn_head is not None:\n            rpn_train_cfg = train_cfg[head_idx].rpn if (\n                train_cfg is not None\n                and train_cfg[head_idx] is not None) else None\n            rpn_head_ = rpn_head.copy()\n            rpn_head_.update(\n                train_cfg=rpn_train_cfg, test_cfg=test_cfg[head_idx].rpn)\n            self.rpn_head = MODELS.build(rpn_head_)\n            self.rpn_head.init_weights()\n\n        self.roi_head = nn.ModuleList()\n        for i in range(len(roi_head)):\n            if roi_head[i]:\n                rcnn_train_cfg = train_cfg[i + head_idx].rcnn if (\n                    train_cfg\n                    and train_cfg[i + head_idx] is not None) else None\n                roi_head[i].update(train_cfg=rcnn_train_cfg)\n                roi_head[i].update(test_cfg=test_cfg[i + head_idx].rcnn)\n                self.roi_head.append(MODELS.build(roi_head[i]))\n                self.roi_head[-1].init_weights()\n\n        self.bbox_head = nn.ModuleList()\n        for i in range(len(bbox_head)):\n            if bbox_head[i]:\n                bbox_head[i].update(\n                    train_cfg=train_cfg[i + head_idx + len(self.roi_head)] if (\n                        train_cfg and train_cfg[i + head_idx +\n                                                len(self.roi_head)] is not None\n                    ) else None)\n                bbox_head[i].update(test_cfg=test_cfg[i + head_idx +\n                                                      len(self.roi_head)])\n                self.bbox_head.append(MODELS.build(bbox_head[i]))\n                self.bbox_head[-1].init_weights()\n\n        self.head_idx = head_idx\n        self.train_cfg = train_cfg\n        self.test_cfg = test_cfg\n\n    @property\n    def with_rpn(self):\n        \"\"\"bool: whether the detector has RPN\"\"\"\n        return hasattr(self, 'rpn_head') and self.rpn_head is not None\n\n    @property\n    def with_query_head(self):\n        \"\"\"bool: whether the detector has a RoI head\"\"\"\n        return hasattr(self, 'query_head') and self.query_head is not None\n\n    @property\n    def with_roi_head(self):\n        \"\"\"bool: whether the detector has a RoI head\"\"\"\n        return hasattr(self, 'roi_head') and self.roi_head is not None and len(\n            self.roi_head) > 0\n\n    @property\n    def with_shared_head(self):\n        \"\"\"bool: whether the detector has a shared head in the RoI Head\"\"\"\n        return hasattr(self, 'roi_head') and self.roi_head[0].with_shared_head\n\n    @property\n    def with_bbox(self):\n        \"\"\"bool: whether the detector has a bbox head\"\"\"\n        return ((hasattr(self, 'roi_head') and self.roi_head is not None\n                 and len(self.roi_head) > 0)\n                or (hasattr(self, 'bbox_head') and self.bbox_head is not None\n                    and len(self.bbox_head) > 0))\n\n    def extract_feat(self, batch_inputs: Tensor) -> Tuple[Tensor]:\n        \"\"\"Extract features.\n\n        Args:\n            batch_inputs (Tensor): Image tensor, has shape (bs, dim, H, W).\n\n        Returns:\n            tuple[Tensor]: Tuple of feature maps from neck. Each feature map\n            has shape (bs, dim, H, W).\n        \"\"\"\n        x = self.backbone(batch_inputs)\n        if self.with_neck:\n            x = self.neck(x)\n        return x\n\n    def _forward(self,\n                 batch_inputs: Tensor,\n                 batch_data_samples: OptSampleList = None):\n        pass\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, list]:\n        batch_input_shape = batch_data_samples[0].batch_input_shape\n        if self.use_lsj:\n            for data_samples in batch_data_samples:\n                img_metas = data_samples.metainfo\n                input_img_h, input_img_w = batch_input_shape\n                img_metas['img_shape'] = [input_img_h, input_img_w]\n\n        x = self.extract_feat(batch_inputs)\n\n        losses = dict()\n\n        def upd_loss(losses, idx, weight=1):\n            new_losses = dict()\n            for k, v in losses.items():\n                new_k = '{}{}'.format(k, idx)\n                if isinstance(v, list) or isinstance(v, tuple):\n                    new_losses[new_k] = [i * weight for i in v]\n                else:\n                    new_losses[new_k] = v * weight\n            return new_losses\n\n        # DETR encoder and decoder forward\n        if self.with_query_head:\n            bbox_losses, x = self.query_head.loss(x, batch_data_samples)\n            losses.update(bbox_losses)\n\n        # RPN forward and loss\n        if self.with_rpn:\n            proposal_cfg = self.train_cfg[self.head_idx].get(\n                'rpn_proposal', self.test_cfg[self.head_idx].rpn)\n\n            rpn_data_samples = copy.deepcopy(batch_data_samples)\n            # set cat_id of gt_labels to 0 in RPN\n            for data_sample in rpn_data_samples:\n                data_sample.gt_instances.labels = \\\n                    torch.zeros_like(data_sample.gt_instances.labels)\n\n            rpn_losses, proposal_list = self.rpn_head.loss_and_predict(\n                x, rpn_data_samples, proposal_cfg=proposal_cfg)\n\n            # avoid get same name with roi_head loss\n            keys = rpn_losses.keys()\n            for key in list(keys):\n                if 'loss' in key and 'rpn' not in key:\n                    rpn_losses[f'rpn_{key}'] = rpn_losses.pop(key)\n\n            losses.update(rpn_losses)\n        else:\n            assert batch_data_samples[0].get('proposals', None) is not None\n            # use pre-defined proposals in InstanceData for the second stage\n            # to extract ROI features.\n            proposal_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        positive_coords = []\n        for i in range(len(self.roi_head)):\n            roi_losses = self.roi_head[i].loss(x, proposal_list,\n                                               batch_data_samples)\n            if self.with_pos_coord:\n                positive_coords.append(roi_losses.pop('pos_coords'))\n            else:\n                if 'pos_coords' in roi_losses.keys():\n                    roi_losses.pop('pos_coords')\n            roi_losses = upd_loss(roi_losses, idx=i)\n            losses.update(roi_losses)\n\n        for i in range(len(self.bbox_head)):\n            bbox_losses = self.bbox_head[i].loss(x, batch_data_samples)\n            if self.with_pos_coord:\n                pos_coords = bbox_losses.pop('pos_coords')\n                positive_coords.append(pos_coords)\n            else:\n                if 'pos_coords' in bbox_losses.keys():\n                    bbox_losses.pop('pos_coords')\n            bbox_losses = upd_loss(bbox_losses, idx=i + len(self.roi_head))\n            losses.update(bbox_losses)\n\n        if self.with_pos_coord and len(positive_coords) > 0:\n            for i in range(len(positive_coords)):\n                bbox_losses = self.query_head.loss_aux(x, positive_coords[i],\n                                                       i, batch_data_samples)\n                bbox_losses = upd_loss(bbox_losses, idx=i)\n                losses.update(bbox_losses)\n\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs, has shape (bs, dim, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the input images.\n            Each DetDataSample usually contain 'pred_instances'. And the\n            `pred_instances` usually contains following keys.\n\n            - scores (Tensor): Classification scores, has a shape\n              (num_instance, )\n            - labels (Tensor): Labels of bboxes, has a shape\n              (num_instances, ).\n            - bboxes (Tensor): Has a shape (num_instances, 4),\n              the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        assert self.eval_module in ['detr', 'one-stage', 'two-stage']\n\n        if self.use_lsj:\n            for data_samples in batch_data_samples:\n                img_metas = data_samples.metainfo\n                input_img_h, input_img_w = img_metas['batch_input_shape']\n                img_metas['img_shape'] = [input_img_h, input_img_w]\n\n        img_feats = self.extract_feat(batch_inputs)\n        if self.with_bbox and self.eval_module == 'one-stage':\n            results_list = self.predict_bbox_head(\n                img_feats, batch_data_samples, rescale=rescale)\n        elif self.with_roi_head and self.eval_module == 'two-stage':\n            results_list = self.predict_roi_head(\n                img_feats, batch_data_samples, rescale=rescale)\n        else:\n            results_list = self.predict_query_head(\n                img_feats, batch_data_samples, rescale=rescale)\n\n        batch_data_samples = self.add_pred_to_datasample(\n            batch_data_samples, results_list)\n        return batch_data_samples\n\n    def predict_query_head(self,\n                           mlvl_feats: Tuple[Tensor],\n                           batch_data_samples: SampleList,\n                           rescale: bool = True) -> InstanceList:\n        return self.query_head.predict(\n            mlvl_feats, batch_data_samples=batch_data_samples, rescale=rescale)\n\n    def predict_roi_head(self,\n                         mlvl_feats: Tuple[Tensor],\n                         batch_data_samples: SampleList,\n                         rescale: bool = True) -> InstanceList:\n        assert self.with_bbox, 'Bbox head must be implemented.'\n        if self.with_query_head:\n            batch_img_metas = [\n                data_samples.metainfo for data_samples in batch_data_samples\n            ]\n            results = self.query_head.forward(mlvl_feats, batch_img_metas)\n            mlvl_feats = results[-1]\n        rpn_results_list = self.rpn_head.predict(\n            mlvl_feats, batch_data_samples, rescale=False)\n        return self.roi_head[self.eval_index].predict(\n            mlvl_feats, rpn_results_list, batch_data_samples, rescale=rescale)\n\n    def predict_bbox_head(self,\n                          mlvl_feats: Tuple[Tensor],\n                          batch_data_samples: SampleList,\n                          rescale: bool = True) -> InstanceList:\n        assert self.with_bbox, 'Bbox head must be implemented.'\n        if self.with_query_head:\n            batch_img_metas = [\n                data_samples.metainfo for data_samples in batch_data_samples\n            ]\n            results = self.query_head.forward(mlvl_feats, batch_img_metas)\n            mlvl_feats = results[-1]\n        return self.bbox_head[self.eval_index].predict(\n            mlvl_feats, batch_data_samples, rescale=rescale)\n"
  },
  {
    "path": "projects/CO-DETR/codetr/transformer.py",
    "content": "import math\nimport warnings\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import build_norm_layer\nfrom mmcv.cnn.bricks.transformer import (BaseTransformerLayer,\n                                         TransformerLayerSequence,\n                                         build_transformer_layer_sequence)\nfrom mmcv.ops import MultiScaleDeformableAttention\nfrom mmengine.model import BaseModule\nfrom mmengine.model.weight_init import xavier_init\nfrom torch.nn.init import normal_\n\nfrom mmdet.models.layers.transformer import inverse_sigmoid\nfrom mmdet.registry import MODELS\n\ntry:\n    from fairscale.nn.checkpoint import checkpoint_wrapper\nexcept Exception:\n    checkpoint_wrapper = None\n\n# In order to save the cost and effort of reproduction,\n# I did not refactor it into the style of mmdet 3.x DETR.\n\n\nclass Transformer(BaseModule):\n    \"\"\"Implements the DETR transformer.\n\n    Following the official DETR implementation, this module copy-paste\n    from torch.nn.Transformer with modifications:\n\n        * positional encodings are passed in MultiheadAttention\n        * extra LN at the end of encoder is removed\n        * decoder returns a stack of activations from all decoding layers\n\n    See `paper: End-to-End Object Detection with Transformers\n    <https://arxiv.org/pdf/2005.12872>`_ for details.\n\n    Args:\n        encoder (`mmcv.ConfigDict` | Dict): Config of\n            TransformerEncoder. Defaults to None.\n        decoder ((`mmcv.ConfigDict` | Dict)): Config of\n            TransformerDecoder. Defaults to None\n        init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self, encoder=None, decoder=None, init_cfg=None):\n        super(Transformer, self).__init__(init_cfg=init_cfg)\n        self.encoder = build_transformer_layer_sequence(encoder)\n        self.decoder = build_transformer_layer_sequence(decoder)\n        self.embed_dims = self.encoder.embed_dims\n\n    def init_weights(self):\n        # follow the official DETR to init parameters\n        for m in self.modules():\n            if hasattr(m, 'weight') and m.weight.dim() > 1:\n                xavier_init(m, distribution='uniform')\n        self._is_init = True\n\n    def forward(self, x, mask, query_embed, pos_embed):\n        \"\"\"Forward function for `Transformer`.\n\n        Args:\n            x (Tensor): Input query with shape [bs, c, h, w] where\n                c = embed_dims.\n            mask (Tensor): The key_padding_mask used for encoder and decoder,\n                with shape [bs, h, w].\n            query_embed (Tensor): The query embedding for decoder, with shape\n                [num_query, c].\n            pos_embed (Tensor): The positional encoding for encoder and\n                decoder, with the same shape as `x`.\n\n        Returns:\n            tuple[Tensor]: results of decoder containing the following tensor.\n\n                - out_dec: Output from decoder. If return_intermediate_dec \\\n                      is True output has shape [num_dec_layers, bs,\n                      num_query, embed_dims], else has shape [1, bs, \\\n                      num_query, embed_dims].\n                - memory: Output results from encoder, with shape \\\n                      [bs, embed_dims, h, w].\n        \"\"\"\n        bs, c, h, w = x.shape\n        # use `view` instead of `flatten` for dynamically exporting to ONNX\n        x = x.view(bs, c, -1).permute(2, 0, 1)  # [bs, c, h, w] -> [h*w, bs, c]\n        pos_embed = pos_embed.view(bs, c, -1).permute(2, 0, 1)\n        query_embed = query_embed.unsqueeze(1).repeat(\n            1, bs, 1)  # [num_query, dim] -> [num_query, bs, dim]\n        mask = mask.view(bs, -1)  # [bs, h, w] -> [bs, h*w]\n        memory = self.encoder(\n            query=x,\n            key=None,\n            value=None,\n            query_pos=pos_embed,\n            query_key_padding_mask=mask)\n        target = torch.zeros_like(query_embed)\n        # out_dec: [num_layers, num_query, bs, dim]\n        out_dec = self.decoder(\n            query=target,\n            key=memory,\n            value=memory,\n            key_pos=pos_embed,\n            query_pos=query_embed,\n            key_padding_mask=mask)\n        out_dec = out_dec.transpose(1, 2)\n        memory = memory.permute(1, 2, 0).reshape(bs, c, h, w)\n        return out_dec, memory\n\n\n@MODELS.register_module(force=True)\nclass DeformableDetrTransformerDecoder(TransformerLayerSequence):\n    \"\"\"Implements the decoder in DETR transformer.\n\n    Args:\n        return_intermediate (bool): Whether to return intermediate outputs.\n        coder_norm_cfg (dict): Config of last normalization layer. Default：\n            `LN`.\n    \"\"\"\n\n    def __init__(self, *args, return_intermediate=False, **kwargs):\n\n        super(DeformableDetrTransformerDecoder, self).__init__(*args, **kwargs)\n        self.return_intermediate = return_intermediate\n\n    def forward(self,\n                query,\n                *args,\n                reference_points=None,\n                valid_ratios=None,\n                reg_branches=None,\n                **kwargs):\n        \"\"\"Forward function for `TransformerDecoder`.\n\n        Args:\n            query (Tensor): Input query with shape\n                `(num_query, bs, embed_dims)`.\n            reference_points (Tensor): The reference\n                points of offset. has shape\n                (bs, num_query, 4) when as_two_stage,\n                otherwise has shape ((bs, num_query, 2).\n            valid_ratios (Tensor): The radios of valid\n                points on the feature map, has shape\n                (bs, num_levels, 2)\n            reg_branch: (obj:`nn.ModuleList`): Used for\n                refining the regression results. Only would\n                be passed when with_box_refine is True,\n                otherwise would be passed a `None`.\n\n        Returns:\n            Tensor: Results with shape [1, num_query, bs, embed_dims] when\n                return_intermediate is `False`, otherwise it has shape\n                [num_layers, num_query, bs, embed_dims].\n        \"\"\"\n        output = query\n        intermediate = []\n        intermediate_reference_points = []\n        for lid, layer in enumerate(self.layers):\n            if reference_points.shape[-1] == 4:\n                reference_points_input = reference_points[:, :, None] * \\\n                    torch.cat([valid_ratios, valid_ratios], -1)[:, None]\n            else:\n                assert reference_points.shape[-1] == 2\n                reference_points_input = reference_points[:, :, None] * \\\n                    valid_ratios[:, None]\n            output = layer(\n                output,\n                *args,\n                reference_points=reference_points_input,\n                **kwargs)\n            output = output.permute(1, 0, 2)\n\n            if reg_branches is not None:\n                tmp = reg_branches[lid](output)\n                if reference_points.shape[-1] == 4:\n                    new_reference_points = tmp + inverse_sigmoid(\n                        reference_points)\n                    new_reference_points = new_reference_points.sigmoid()\n                else:\n                    assert reference_points.shape[-1] == 2\n                    new_reference_points = tmp\n                    new_reference_points[..., :2] = tmp[\n                        ..., :2] + inverse_sigmoid(reference_points)\n                    new_reference_points = new_reference_points.sigmoid()\n                reference_points = new_reference_points.detach()\n\n            output = output.permute(1, 0, 2)\n            if self.return_intermediate:\n                intermediate.append(output)\n                intermediate_reference_points.append(reference_points)\n\n        if self.return_intermediate:\n            return torch.stack(intermediate), torch.stack(\n                intermediate_reference_points)\n\n        return output, reference_points\n\n\n@MODELS.register_module(force=True)\nclass DeformableDetrTransformer(Transformer):\n    \"\"\"Implements the DeformableDETR transformer.\n\n    Args:\n        as_two_stage (bool): Generate query from encoder features.\n            Default: False.\n        num_feature_levels (int): Number of feature maps from FPN:\n            Default: 4.\n        two_stage_num_proposals (int): Number of proposals when set\n            `as_two_stage` as True. Default: 300.\n    \"\"\"\n\n    def __init__(self,\n                 as_two_stage=False,\n                 num_feature_levels=4,\n                 two_stage_num_proposals=300,\n                 **kwargs):\n        super(DeformableDetrTransformer, self).__init__(**kwargs)\n        self.as_two_stage = as_two_stage\n        self.num_feature_levels = num_feature_levels\n        self.two_stage_num_proposals = two_stage_num_proposals\n        self.embed_dims = self.encoder.embed_dims\n        self.init_layers()\n\n    def init_layers(self):\n        \"\"\"Initialize layers of the DeformableDetrTransformer.\"\"\"\n        self.level_embeds = nn.Parameter(\n            torch.Tensor(self.num_feature_levels, self.embed_dims))\n\n        if self.as_two_stage:\n            self.enc_output = nn.Linear(self.embed_dims, self.embed_dims)\n            self.enc_output_norm = nn.LayerNorm(self.embed_dims)\n            self.pos_trans = nn.Linear(self.embed_dims * 2,\n                                       self.embed_dims * 2)\n            self.pos_trans_norm = nn.LayerNorm(self.embed_dims * 2)\n        else:\n            self.reference_points = nn.Linear(self.embed_dims, 2)\n\n    def init_weights(self):\n        \"\"\"Initialize the transformer weights.\"\"\"\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n        for m in self.modules():\n            if isinstance(m, MultiScaleDeformableAttention):\n                m.init_weights()\n        if not self.as_two_stage:\n            xavier_init(self.reference_points, distribution='uniform', bias=0.)\n        normal_(self.level_embeds)\n\n    def gen_encoder_output_proposals(self, memory, memory_padding_mask,\n                                     spatial_shapes):\n        \"\"\"Generate proposals from encoded memory.\n\n        Args:\n            memory (Tensor) : The output of encoder,\n                has shape (bs, num_key, embed_dim).  num_key is\n                equal the number of points on feature map from\n                all level.\n            memory_padding_mask (Tensor): Padding mask for memory.\n                has shape (bs, num_key).\n            spatial_shapes (Tensor): The shape of all feature maps.\n                has shape (num_level, 2).\n\n        Returns:\n            tuple: A tuple of feature map and bbox prediction.\n\n                - output_memory (Tensor): The input of decoder,  \\\n                    has shape (bs, num_key, embed_dim).  num_key is \\\n                    equal the number of points on feature map from \\\n                    all levels.\n                - output_proposals (Tensor): The normalized proposal \\\n                    after a inverse sigmoid, has shape \\\n                    (bs, num_keys, 4).\n        \"\"\"\n\n        N, S, C = memory.shape\n        proposals = []\n        _cur = 0\n        for lvl, (H, W) in enumerate(spatial_shapes):\n            mask_flatten_ = memory_padding_mask[:, _cur:(_cur + H * W)].view(\n                N, H, W, 1)\n            valid_H = torch.sum(~mask_flatten_[:, :, 0, 0], 1)\n            valid_W = torch.sum(~mask_flatten_[:, 0, :, 0], 1)\n\n            grid_y, grid_x = torch.meshgrid(\n                torch.linspace(\n                    0, H - 1, H, dtype=torch.float32, device=memory.device),\n                torch.linspace(\n                    0, W - 1, W, dtype=torch.float32, device=memory.device))\n            grid = torch.cat([grid_x.unsqueeze(-1), grid_y.unsqueeze(-1)], -1)\n\n            scale = torch.cat([valid_W.unsqueeze(-1),\n                               valid_H.unsqueeze(-1)], 1).view(N, 1, 1, 2)\n            grid = (grid.unsqueeze(0).expand(N, -1, -1, -1) + 0.5) / scale\n            wh = torch.ones_like(grid) * 0.05 * (2.0**lvl)\n            proposal = torch.cat((grid, wh), -1).view(N, -1, 4)\n            proposals.append(proposal)\n            _cur += (H * W)\n        output_proposals = torch.cat(proposals, 1)\n        output_proposals_valid = ((output_proposals > 0.01) &\n                                  (output_proposals < 0.99)).all(\n                                      -1, keepdim=True)\n        output_proposals = torch.log(output_proposals / (1 - output_proposals))\n        output_proposals = output_proposals.masked_fill(\n            memory_padding_mask.unsqueeze(-1), float('inf'))\n        output_proposals = output_proposals.masked_fill(\n            ~output_proposals_valid, float('inf'))\n\n        output_memory = memory\n        output_memory = output_memory.masked_fill(\n            memory_padding_mask.unsqueeze(-1), float(0))\n        output_memory = output_memory.masked_fill(~output_proposals_valid,\n                                                  float(0))\n        output_memory = self.enc_output_norm(self.enc_output(output_memory))\n        return output_memory, output_proposals\n\n    @staticmethod\n    def get_reference_points(spatial_shapes, valid_ratios, device):\n        \"\"\"Get the reference points used in decoder.\n\n        Args:\n            spatial_shapes (Tensor): The shape of all\n                feature maps, has shape (num_level, 2).\n            valid_ratios (Tensor): The radios of valid\n                points on the feature map, has shape\n                (bs, num_levels, 2)\n            device (obj:`device`): The device where\n                reference_points should be.\n\n        Returns:\n            Tensor: reference points used in decoder, has \\\n                shape (bs, num_keys, num_levels, 2).\n        \"\"\"\n        reference_points_list = []\n        for lvl, (H, W) in enumerate(spatial_shapes):\n            ref_y, ref_x = torch.meshgrid(\n                torch.linspace(\n                    0.5, H - 0.5, H, dtype=torch.float32, device=device),\n                torch.linspace(\n                    0.5, W - 0.5, W, dtype=torch.float32, device=device))\n            ref_y = ref_y.reshape(-1)[None] / (\n                valid_ratios[:, None, lvl, 1] * H)\n            ref_x = ref_x.reshape(-1)[None] / (\n                valid_ratios[:, None, lvl, 0] * W)\n            ref = torch.stack((ref_x, ref_y), -1)\n            reference_points_list.append(ref)\n        reference_points = torch.cat(reference_points_list, 1)\n        reference_points = reference_points[:, :, None] * valid_ratios[:, None]\n        return reference_points\n\n    def get_valid_ratio(self, mask):\n        \"\"\"Get the valid radios of feature maps of all  level.\"\"\"\n        _, H, W = mask.shape\n        valid_H = torch.sum(~mask[:, :, 0], 1)\n        valid_W = torch.sum(~mask[:, 0, :], 1)\n        valid_ratio_h = valid_H.float() / H\n        valid_ratio_w = valid_W.float() / W\n        valid_ratio = torch.stack([valid_ratio_w, valid_ratio_h], -1)\n        return valid_ratio\n\n    def get_proposal_pos_embed(self,\n                               proposals,\n                               num_pos_feats=128,\n                               temperature=10000):\n        \"\"\"Get the position embedding of proposal.\"\"\"\n        scale = 2 * math.pi\n        dim_t = torch.arange(\n            num_pos_feats, dtype=torch.float32, device=proposals.device)\n        dim_t = temperature**(2 * (dim_t // 2) / num_pos_feats)\n        # N, L, 4\n        proposals = proposals.sigmoid() * scale\n        # N, L, 4, 128\n        pos = proposals[:, :, :, None] / dim_t\n        # N, L, 4, 64, 2\n        pos = torch.stack((pos[:, :, :, 0::2].sin(), pos[:, :, :, 1::2].cos()),\n                          dim=4).flatten(2)\n        return pos\n\n    def forward(self,\n                mlvl_feats,\n                mlvl_masks,\n                query_embed,\n                mlvl_pos_embeds,\n                reg_branches=None,\n                cls_branches=None,\n                **kwargs):\n        \"\"\"Forward function for `Transformer`.\n\n        Args:\n            mlvl_feats (list(Tensor)): Input queries from\n                different level. Each element has shape\n                [bs, embed_dims, h, w].\n            mlvl_masks (list(Tensor)): The key_padding_mask from\n                different level used for encoder and decoder,\n                each element has shape  [bs, h, w].\n            query_embed (Tensor): The query embedding for decoder,\n                with shape [num_query, c].\n            mlvl_pos_embeds (list(Tensor)): The positional encoding\n                of feats from different level, has the shape\n                 [bs, embed_dims, h, w].\n            reg_branches (obj:`nn.ModuleList`): Regression heads for\n                feature maps from each decoder layer. Only would\n                be passed when\n                `with_box_refine` is True. Default to None.\n            cls_branches (obj:`nn.ModuleList`): Classification heads\n                for feature maps from each decoder layer. Only would\n                 be passed when `as_two_stage`\n                 is True. Default to None.\n\n\n        Returns:\n            tuple[Tensor]: results of decoder containing the following tensor.\n\n                - inter_states: Outputs from decoder. If\n                    return_intermediate_dec is True output has shape \\\n                      (num_dec_layers, bs, num_query, embed_dims), else has \\\n                      shape (1, bs, num_query, embed_dims).\n                - init_reference_out: The initial value of reference \\\n                    points, has shape (bs, num_queries, 4).\n                - inter_references_out: The internal value of reference \\\n                    points in decoder, has shape \\\n                    (num_dec_layers, bs,num_query, embed_dims)\n                - enc_outputs_class: The classification score of \\\n                    proposals generated from \\\n                    encoder's feature maps, has shape \\\n                    (batch, h*w, num_classes). \\\n                    Only would be returned when `as_two_stage` is True, \\\n                    otherwise None.\n                - enc_outputs_coord_unact: The regression results \\\n                    generated from encoder's feature maps., has shape \\\n                    (batch, h*w, 4). Only would \\\n                    be returned when `as_two_stage` is True, \\\n                    otherwise None.\n        \"\"\"\n        assert self.as_two_stage or query_embed is not None\n\n        feat_flatten = []\n        mask_flatten = []\n        lvl_pos_embed_flatten = []\n        spatial_shapes = []\n        for lvl, (feat, mask, pos_embed) in enumerate(\n                zip(mlvl_feats, mlvl_masks, mlvl_pos_embeds)):\n            bs, c, h, w = feat.shape\n            spatial_shape = (h, w)\n            spatial_shapes.append(spatial_shape)\n            feat = feat.flatten(2).transpose(1, 2)\n            mask = mask.flatten(1)\n            pos_embed = pos_embed.flatten(2).transpose(1, 2)\n            lvl_pos_embed = pos_embed + self.level_embeds[lvl].view(1, 1, -1)\n            lvl_pos_embed_flatten.append(lvl_pos_embed)\n            feat_flatten.append(feat)\n            mask_flatten.append(mask)\n        feat_flatten = torch.cat(feat_flatten, 1)\n        mask_flatten = torch.cat(mask_flatten, 1)\n        lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)\n        spatial_shapes = torch.as_tensor(\n            spatial_shapes, dtype=torch.long, device=feat_flatten.device)\n        level_start_index = torch.cat((spatial_shapes.new_zeros(\n            (1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))\n        valid_ratios = torch.stack(\n            [self.get_valid_ratio(m) for m in mlvl_masks], 1)\n\n        reference_points = \\\n            self.get_reference_points(spatial_shapes,\n                                      valid_ratios,\n                                      device=feat.device)\n\n        feat_flatten = feat_flatten.permute(1, 0, 2)  # (H*W, bs, embed_dims)\n        lvl_pos_embed_flatten = lvl_pos_embed_flatten.permute(\n            1, 0, 2)  # (H*W, bs, embed_dims)\n        memory = self.encoder(\n            query=feat_flatten,\n            key=None,\n            value=None,\n            query_pos=lvl_pos_embed_flatten,\n            query_key_padding_mask=mask_flatten,\n            spatial_shapes=spatial_shapes,\n            reference_points=reference_points,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            **kwargs)\n\n        memory = memory.permute(1, 0, 2)\n        bs, _, c = memory.shape\n        if self.as_two_stage:\n            output_memory, output_proposals = \\\n                self.gen_encoder_output_proposals(\n                    memory, mask_flatten, spatial_shapes)\n            enc_outputs_class = cls_branches[self.decoder.num_layers](\n                output_memory)\n            enc_outputs_coord_unact = \\\n                reg_branches[\n                    self.decoder.num_layers](output_memory) + output_proposals\n\n            topk = self.two_stage_num_proposals\n            # We only use the first channel in enc_outputs_class as foreground,\n            # the other (num_classes - 1) channels are actually not used.\n            # Its targets are set to be 0s, which indicates the first\n            # class (foreground) because we use [0, num_classes - 1] to\n            # indicate class labels, background class is indicated by\n            # num_classes (similar convention in RPN).\n            # See https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/dense_heads/deformable_detr_head.py#L241 # noqa\n            # This follows the official implementation of Deformable DETR.\n            topk_proposals = torch.topk(\n                enc_outputs_class[..., 0], topk, dim=1)[1]\n            topk_coords_unact = torch.gather(\n                enc_outputs_coord_unact, 1,\n                topk_proposals.unsqueeze(-1).repeat(1, 1, 4))\n            topk_coords_unact = topk_coords_unact.detach()\n            reference_points = topk_coords_unact.sigmoid()\n            init_reference_out = reference_points\n            pos_trans_out = self.pos_trans_norm(\n                self.pos_trans(self.get_proposal_pos_embed(topk_coords_unact)))\n            query_pos, query = torch.split(pos_trans_out, c, dim=2)\n        else:\n            query_pos, query = torch.split(query_embed, c, dim=1)\n            query_pos = query_pos.unsqueeze(0).expand(bs, -1, -1)\n            query = query.unsqueeze(0).expand(bs, -1, -1)\n            reference_points = self.reference_points(query_pos).sigmoid()\n            init_reference_out = reference_points\n\n        # decoder\n        query = query.permute(1, 0, 2)\n        memory = memory.permute(1, 0, 2)\n        query_pos = query_pos.permute(1, 0, 2)\n        inter_states, inter_references = self.decoder(\n            query=query,\n            key=None,\n            value=memory,\n            query_pos=query_pos,\n            key_padding_mask=mask_flatten,\n            reference_points=reference_points,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            reg_branches=reg_branches,\n            **kwargs)\n\n        inter_references_out = inter_references\n        if self.as_two_stage:\n            return inter_states, init_reference_out,\\\n                inter_references_out, enc_outputs_class,\\\n                enc_outputs_coord_unact\n        return inter_states, init_reference_out, \\\n            inter_references_out, None, None\n\n\n@MODELS.register_module()\nclass CoDeformableDetrTransformerDecoder(TransformerLayerSequence):\n    \"\"\"Implements the decoder in DETR transformer.\n\n    Args:\n        return_intermediate (bool): Whether to return intermediate outputs.\n        coder_norm_cfg (dict): Config of last normalization layer. Default：\n            `LN`.\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 return_intermediate=False,\n                 look_forward_twice=False,\n                 **kwargs):\n\n        super(CoDeformableDetrTransformerDecoder,\n              self).__init__(*args, **kwargs)\n        self.return_intermediate = return_intermediate\n        self.look_forward_twice = look_forward_twice\n\n    def forward(self,\n                query,\n                *args,\n                reference_points=None,\n                valid_ratios=None,\n                reg_branches=None,\n                **kwargs):\n        \"\"\"Forward function for `TransformerDecoder`.\n\n        Args:\n            query (Tensor): Input query with shape\n                `(num_query, bs, embed_dims)`.\n            reference_points (Tensor): The reference\n                points of offset. has shape\n                (bs, num_query, 4) when as_two_stage,\n                otherwise has shape ((bs, num_query, 2).\n            valid_ratios (Tensor): The radios of valid\n                points on the feature map, has shape\n                (bs, num_levels, 2)\n            reg_branch: (obj:`nn.ModuleList`): Used for\n                refining the regression results. Only would\n                be passed when with_box_refine is True,\n                otherwise would be passed a `None`.\n\n        Returns:\n            Tensor: Results with shape [1, num_query, bs, embed_dims] when\n                return_intermediate is `False`, otherwise it has shape\n                [num_layers, num_query, bs, embed_dims].\n        \"\"\"\n        output = query\n        intermediate = []\n        intermediate_reference_points = []\n        for lid, layer in enumerate(self.layers):\n            if reference_points.shape[-1] == 4:\n                reference_points_input = reference_points[:, :, None] * \\\n                    torch.cat([valid_ratios, valid_ratios], -1)[:, None]\n            else:\n                assert reference_points.shape[-1] == 2\n                reference_points_input = reference_points[:, :, None] * \\\n                    valid_ratios[:, None]\n            output = layer(\n                output,\n                *args,\n                reference_points=reference_points_input,\n                **kwargs)\n            output = output.permute(1, 0, 2)\n\n            if reg_branches is not None:\n                tmp = reg_branches[lid](output)\n                if reference_points.shape[-1] == 4:\n                    new_reference_points = tmp + inverse_sigmoid(\n                        reference_points)\n                    new_reference_points = new_reference_points.sigmoid()\n                else:\n                    assert reference_points.shape[-1] == 2\n                    new_reference_points = tmp\n                    new_reference_points[..., :2] = tmp[\n                        ..., :2] + inverse_sigmoid(reference_points)\n                    new_reference_points = new_reference_points.sigmoid()\n                reference_points = new_reference_points.detach()\n\n            output = output.permute(1, 0, 2)\n            if self.return_intermediate:\n                intermediate.append(output)\n                intermediate_reference_points.append(\n                    new_reference_points if self.\n                    look_forward_twice else reference_points)\n        if self.return_intermediate:\n            return torch.stack(intermediate), torch.stack(\n                intermediate_reference_points)\n\n        return output, reference_points\n\n\n@MODELS.register_module()\nclass CoDeformableDetrTransformer(DeformableDetrTransformer):\n\n    def __init__(self,\n                 mixed_selection=True,\n                 with_pos_coord=True,\n                 with_coord_feat=True,\n                 num_co_heads=1,\n                 **kwargs):\n        self.mixed_selection = mixed_selection\n        self.with_pos_coord = with_pos_coord\n        self.with_coord_feat = with_coord_feat\n        self.num_co_heads = num_co_heads\n        super(CoDeformableDetrTransformer, self).__init__(**kwargs)\n        self._init_layers()\n\n    def _init_layers(self):\n        \"\"\"Initialize layers of the CoDeformableDetrTransformer.\"\"\"\n        if self.with_pos_coord:\n            if self.num_co_heads > 0:\n                # bug: this code should be 'self.head_pos_embed =\n                # nn.Embedding(self.num_co_heads, self.embed_dims)',\n                # we keep this bug for reproducing our results with ResNet-50.\n                # You can fix this bug when reproducing results with\n                # swin transformer.\n                self.head_pos_embed = nn.Embedding(self.num_co_heads, 1, 1,\n                                                   self.embed_dims)\n                self.aux_pos_trans = nn.ModuleList()\n                self.aux_pos_trans_norm = nn.ModuleList()\n                self.pos_feats_trans = nn.ModuleList()\n                self.pos_feats_norm = nn.ModuleList()\n                for i in range(self.num_co_heads):\n                    self.aux_pos_trans.append(\n                        nn.Linear(self.embed_dims * 2, self.embed_dims * 2))\n                    self.aux_pos_trans_norm.append(\n                        nn.LayerNorm(self.embed_dims * 2))\n                    if self.with_coord_feat:\n                        self.pos_feats_trans.append(\n                            nn.Linear(self.embed_dims, self.embed_dims))\n                        self.pos_feats_norm.append(\n                            nn.LayerNorm(self.embed_dims))\n\n    def get_proposal_pos_embed(self,\n                               proposals,\n                               num_pos_feats=128,\n                               temperature=10000):\n        \"\"\"Get the position embedding of proposal.\"\"\"\n        num_pos_feats = self.embed_dims // 2\n        scale = 2 * math.pi\n        dim_t = torch.arange(\n            num_pos_feats, dtype=torch.float32, device=proposals.device)\n        dim_t = temperature**(2 * (dim_t // 2) / num_pos_feats)\n        # N, L, 4\n        proposals = proposals.sigmoid() * scale\n        # N, L, 4, 128\n        pos = proposals[:, :, :, None] / dim_t\n        # N, L, 4, 64, 2\n        pos = torch.stack((pos[:, :, :, 0::2].sin(), pos[:, :, :, 1::2].cos()),\n                          dim=4).flatten(2)\n        return pos\n\n    def forward(self,\n                mlvl_feats,\n                mlvl_masks,\n                query_embed,\n                mlvl_pos_embeds,\n                reg_branches=None,\n                cls_branches=None,\n                return_encoder_output=False,\n                attn_masks=None,\n                **kwargs):\n        \"\"\"Forward function for `Transformer`.\n\n        Args:\n            mlvl_feats (list(Tensor)): Input queries from\n                different level. Each element has shape\n                [bs, embed_dims, h, w].\n            mlvl_masks (list(Tensor)): The key_padding_mask from\n                different level used for encoder and decoder,\n                each element has shape  [bs, h, w].\n            query_embed (Tensor): The query embedding for decoder,\n                with shape [num_query, c].\n            mlvl_pos_embeds (list(Tensor)): The positional encoding\n                of feats from different level, has the shape\n                 [bs, embed_dims, h, w].\n            reg_branches (obj:`nn.ModuleList`): Regression heads for\n                feature maps from each decoder layer. Only would\n                be passed when\n                `with_box_refine` is True. Default to None.\n            cls_branches (obj:`nn.ModuleList`): Classification heads\n                for feature maps from each decoder layer. Only would\n                 be passed when `as_two_stage`\n                 is True. Default to None.\n\n\n        Returns:\n            tuple[Tensor]: results of decoder containing the following tensor.\n\n                - inter_states: Outputs from decoder. If\n                    return_intermediate_dec is True output has shape \\\n                      (num_dec_layers, bs, num_query, embed_dims), else has \\\n                      shape (1, bs, num_query, embed_dims).\n                - init_reference_out: The initial value of reference \\\n                    points, has shape (bs, num_queries, 4).\n                - inter_references_out: The internal value of reference \\\n                    points in decoder, has shape \\\n                    (num_dec_layers, bs,num_query, embed_dims)\n                - enc_outputs_class: The classification score of \\\n                    proposals generated from \\\n                    encoder's feature maps, has shape \\\n                    (batch, h*w, num_classes). \\\n                    Only would be returned when `as_two_stage` is True, \\\n                    otherwise None.\n                - enc_outputs_coord_unact: The regression results \\\n                    generated from encoder's feature maps., has shape \\\n                    (batch, h*w, 4). Only would \\\n                    be returned when `as_two_stage` is True, \\\n                    otherwise None.\n        \"\"\"\n        assert self.as_two_stage or query_embed is not None\n\n        feat_flatten = []\n        mask_flatten = []\n        lvl_pos_embed_flatten = []\n        spatial_shapes = []\n        for lvl, (feat, mask, pos_embed) in enumerate(\n                zip(mlvl_feats, mlvl_masks, mlvl_pos_embeds)):\n            bs, c, h, w = feat.shape\n            spatial_shape = (h, w)\n            spatial_shapes.append(spatial_shape)\n            feat = feat.flatten(2).transpose(1, 2)\n            mask = mask.flatten(1)\n            pos_embed = pos_embed.flatten(2).transpose(1, 2)\n            lvl_pos_embed = pos_embed + self.level_embeds[lvl].view(1, 1, -1)\n            lvl_pos_embed_flatten.append(lvl_pos_embed)\n            feat_flatten.append(feat)\n            mask_flatten.append(mask)\n        feat_flatten = torch.cat(feat_flatten, 1)\n        mask_flatten = torch.cat(mask_flatten, 1)\n        lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)\n        spatial_shapes = torch.as_tensor(\n            spatial_shapes, dtype=torch.long, device=feat_flatten.device)\n        level_start_index = torch.cat((spatial_shapes.new_zeros(\n            (1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))\n        valid_ratios = torch.stack(\n            [self.get_valid_ratio(m) for m in mlvl_masks], 1)\n\n        reference_points = \\\n            self.get_reference_points(spatial_shapes,\n                                      valid_ratios,\n                                      device=feat.device)\n\n        feat_flatten = feat_flatten.permute(1, 0, 2)  # (H*W, bs, embed_dims)\n        lvl_pos_embed_flatten = lvl_pos_embed_flatten.permute(\n            1, 0, 2)  # (H*W, bs, embed_dims)\n        memory = self.encoder(\n            query=feat_flatten,\n            key=None,\n            value=None,\n            query_pos=lvl_pos_embed_flatten,\n            query_key_padding_mask=mask_flatten,\n            spatial_shapes=spatial_shapes,\n            reference_points=reference_points,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            **kwargs)\n\n        memory = memory.permute(1, 0, 2)\n        bs, _, c = memory.shape\n        if self.as_two_stage:\n            output_memory, output_proposals = \\\n                self.gen_encoder_output_proposals(\n                    memory, mask_flatten, spatial_shapes)\n            enc_outputs_class = cls_branches[self.decoder.num_layers](\n                output_memory)\n            enc_outputs_coord_unact = \\\n                reg_branches[\n                    self.decoder.num_layers](output_memory) + output_proposals\n\n            topk = self.two_stage_num_proposals\n            topk = query_embed.shape[0]\n            topk_proposals = torch.topk(\n                enc_outputs_class[..., 0], topk, dim=1)[1]\n            topk_coords_unact = torch.gather(\n                enc_outputs_coord_unact, 1,\n                topk_proposals.unsqueeze(-1).repeat(1, 1, 4))\n            topk_coords_unact = topk_coords_unact.detach()\n            reference_points = topk_coords_unact.sigmoid()\n            init_reference_out = reference_points\n            pos_trans_out = self.pos_trans_norm(\n                self.pos_trans(self.get_proposal_pos_embed(topk_coords_unact)))\n\n            if not self.mixed_selection:\n                query_pos, query = torch.split(pos_trans_out, c, dim=2)\n            else:\n                # query_embed here is the content embed for deformable DETR\n                query = query_embed.unsqueeze(0).expand(bs, -1, -1)\n                query_pos, _ = torch.split(pos_trans_out, c, dim=2)\n        else:\n            query_pos, query = torch.split(query_embed, c, dim=1)\n            query_pos = query_pos.unsqueeze(0).expand(bs, -1, -1)\n            query = query.unsqueeze(0).expand(bs, -1, -1)\n            reference_points = self.reference_points(query_pos).sigmoid()\n            init_reference_out = reference_points\n\n        # decoder\n        query = query.permute(1, 0, 2)\n        memory = memory.permute(1, 0, 2)\n        query_pos = query_pos.permute(1, 0, 2)\n        inter_states, inter_references = self.decoder(\n            query=query,\n            key=None,\n            value=memory,\n            query_pos=query_pos,\n            key_padding_mask=mask_flatten,\n            reference_points=reference_points,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            reg_branches=reg_branches,\n            attn_masks=attn_masks,\n            **kwargs)\n\n        inter_references_out = inter_references\n        if self.as_two_stage:\n            if return_encoder_output:\n                return inter_states, init_reference_out,\\\n                    inter_references_out, enc_outputs_class,\\\n                    enc_outputs_coord_unact, memory\n            return inter_states, init_reference_out,\\\n                inter_references_out, enc_outputs_class,\\\n                enc_outputs_coord_unact\n        if return_encoder_output:\n            return inter_states, init_reference_out, \\\n                inter_references_out, None, None, memory\n        return inter_states, init_reference_out, \\\n            inter_references_out, None, None\n\n    def forward_aux(self,\n                    mlvl_feats,\n                    mlvl_masks,\n                    query_embed,\n                    mlvl_pos_embeds,\n                    pos_anchors,\n                    pos_feats=None,\n                    reg_branches=None,\n                    cls_branches=None,\n                    return_encoder_output=False,\n                    attn_masks=None,\n                    head_idx=0,\n                    **kwargs):\n        feat_flatten = []\n        mask_flatten = []\n        spatial_shapes = []\n        for lvl, (feat, mask, pos_embed) in enumerate(\n                zip(mlvl_feats, mlvl_masks, mlvl_pos_embeds)):\n            bs, c, h, w = feat.shape\n            spatial_shape = (h, w)\n            spatial_shapes.append(spatial_shape)\n            feat = feat.flatten(2).transpose(1, 2)\n            mask = mask.flatten(1)\n            feat_flatten.append(feat)\n            mask_flatten.append(mask)\n        feat_flatten = torch.cat(feat_flatten, 1)\n        mask_flatten = torch.cat(mask_flatten, 1)\n        spatial_shapes = torch.as_tensor(\n            spatial_shapes, dtype=torch.long, device=feat_flatten.device)\n        level_start_index = torch.cat((spatial_shapes.new_zeros(\n            (1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))\n        valid_ratios = torch.stack(\n            [self.get_valid_ratio(m) for m in mlvl_masks], 1)\n\n        feat_flatten = feat_flatten.permute(1, 0, 2)  # (H*W, bs, embed_dims)\n\n        memory = feat_flatten\n        memory = memory.permute(1, 0, 2)\n        bs, _, c = memory.shape\n\n        topk_coords_unact = inverse_sigmoid(pos_anchors)\n        reference_points = pos_anchors\n        init_reference_out = reference_points\n        if self.num_co_heads > 0:\n            pos_trans_out = self.aux_pos_trans_norm[head_idx](\n                self.aux_pos_trans[head_idx](\n                    self.get_proposal_pos_embed(topk_coords_unact)))\n            query_pos, query = torch.split(pos_trans_out, c, dim=2)\n            if self.with_coord_feat:\n                query = query + self.pos_feats_norm[head_idx](\n                    self.pos_feats_trans[head_idx](pos_feats))\n                query_pos = query_pos + self.head_pos_embed.weight[head_idx]\n\n        # decoder\n        query = query.permute(1, 0, 2)\n        memory = memory.permute(1, 0, 2)\n        query_pos = query_pos.permute(1, 0, 2)\n        inter_states, inter_references = self.decoder(\n            query=query,\n            key=None,\n            value=memory,\n            query_pos=query_pos,\n            key_padding_mask=mask_flatten,\n            reference_points=reference_points,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            reg_branches=reg_branches,\n            attn_masks=attn_masks,\n            **kwargs)\n\n        inter_references_out = inter_references\n        return inter_states, init_reference_out, \\\n            inter_references_out\n\n\ndef build_MLP(input_dim, hidden_dim, output_dim, num_layers):\n    assert num_layers > 1, \\\n        f'num_layers should be greater than 1 but got {num_layers}'\n    h = [hidden_dim] * (num_layers - 1)\n    layers = list()\n    for n, k in zip([input_dim] + h[:-1], h):\n        layers.extend((nn.Linear(n, k), nn.ReLU()))\n    # Note that the relu func of MLP in original DETR repo is set\n    # 'inplace=False', however the ReLU cfg of FFN in mmdet is set\n    # 'inplace=True' by default.\n    layers.append(nn.Linear(hidden_dim, output_dim))\n    return nn.Sequential(*layers)\n\n\n@MODELS.register_module()\nclass DinoTransformerDecoder(DeformableDetrTransformerDecoder):\n\n    def __init__(self, *args, **kwargs):\n        super(DinoTransformerDecoder, self).__init__(*args, **kwargs)\n        self._init_layers()\n\n    def _init_layers(self):\n        self.ref_point_head = build_MLP(self.embed_dims * 2, self.embed_dims,\n                                        self.embed_dims, 2)\n        self.norm = nn.LayerNorm(self.embed_dims)\n\n    @staticmethod\n    def gen_sineembed_for_position(pos_tensor, pos_feat):\n        # n_query, bs, _ = pos_tensor.size()\n        # sineembed_tensor = torch.zeros(n_query, bs, 256)\n        scale = 2 * math.pi\n        dim_t = torch.arange(\n            pos_feat, dtype=torch.float32, device=pos_tensor.device)\n        dim_t = 10000**(2 * (dim_t // 2) / pos_feat)\n        x_embed = pos_tensor[:, :, 0] * scale\n        y_embed = pos_tensor[:, :, 1] * scale\n        pos_x = x_embed[:, :, None] / dim_t\n        pos_y = y_embed[:, :, None] / dim_t\n        pos_x = torch.stack((pos_x[:, :, 0::2].sin(), pos_x[:, :, 1::2].cos()),\n                            dim=3).flatten(2)\n        pos_y = torch.stack((pos_y[:, :, 0::2].sin(), pos_y[:, :, 1::2].cos()),\n                            dim=3).flatten(2)\n        if pos_tensor.size(-1) == 2:\n            pos = torch.cat((pos_y, pos_x), dim=2)\n        elif pos_tensor.size(-1) == 4:\n            w_embed = pos_tensor[:, :, 2] * scale\n            pos_w = w_embed[:, :, None] / dim_t\n            pos_w = torch.stack(\n                (pos_w[:, :, 0::2].sin(), pos_w[:, :, 1::2].cos()),\n                dim=3).flatten(2)\n\n            h_embed = pos_tensor[:, :, 3] * scale\n            pos_h = h_embed[:, :, None] / dim_t\n            pos_h = torch.stack(\n                (pos_h[:, :, 0::2].sin(), pos_h[:, :, 1::2].cos()),\n                dim=3).flatten(2)\n\n            pos = torch.cat((pos_y, pos_x, pos_w, pos_h), dim=2)\n        else:\n            raise ValueError('Unknown pos_tensor shape(-1):{}'.format(\n                pos_tensor.size(-1)))\n        return pos\n\n    def forward(self,\n                query,\n                *args,\n                reference_points=None,\n                valid_ratios=None,\n                reg_branches=None,\n                **kwargs):\n        output = query\n        intermediate = []\n        intermediate_reference_points = [reference_points]\n        for lid, layer in enumerate(self.layers):\n            if reference_points.shape[-1] == 4:\n                reference_points_input = \\\n                    reference_points[:, :, None] * torch.cat(\n                        [valid_ratios, valid_ratios], -1)[:, None]\n            else:\n                assert reference_points.shape[-1] == 2\n                reference_points_input = \\\n                    reference_points[:, :, None] * valid_ratios[:, None]\n\n            query_sine_embed = self.gen_sineembed_for_position(\n                reference_points_input[:, :, 0, :], self.embed_dims // 2)\n            query_pos = self.ref_point_head(query_sine_embed)\n\n            query_pos = query_pos.permute(1, 0, 2)\n            output = layer(\n                output,\n                *args,\n                query_pos=query_pos,\n                reference_points=reference_points_input,\n                **kwargs)\n            output = output.permute(1, 0, 2)\n\n            if reg_branches is not None:\n                tmp = reg_branches[lid](output)\n                assert reference_points.shape[-1] == 4\n                new_reference_points = tmp + inverse_sigmoid(\n                    reference_points, eps=1e-3)\n                new_reference_points = new_reference_points.sigmoid()\n                reference_points = new_reference_points.detach()\n\n            output = output.permute(1, 0, 2)\n            if self.return_intermediate:\n                intermediate.append(self.norm(output))\n                intermediate_reference_points.append(new_reference_points)\n                # NOTE this is for the \"Look Forward Twice\" module,\n                # in the DeformDETR, reference_points was appended.\n\n        if self.return_intermediate:\n            return torch.stack(intermediate), torch.stack(\n                intermediate_reference_points)\n\n        return output, reference_points\n\n\n@MODELS.register_module()\nclass CoDinoTransformer(CoDeformableDetrTransformer):\n\n    def __init__(self, *args, **kwargs):\n        super(CoDinoTransformer, self).__init__(*args, **kwargs)\n\n    def init_layers(self):\n        \"\"\"Initialize layers of the DinoTransformer.\"\"\"\n        self.level_embeds = nn.Parameter(\n            torch.Tensor(self.num_feature_levels, self.embed_dims))\n        self.enc_output = nn.Linear(self.embed_dims, self.embed_dims)\n        self.enc_output_norm = nn.LayerNorm(self.embed_dims)\n        self.query_embed = nn.Embedding(self.two_stage_num_proposals,\n                                        self.embed_dims)\n\n    def _init_layers(self):\n        if self.with_pos_coord:\n            if self.num_co_heads > 0:\n                self.aux_pos_trans = nn.ModuleList()\n                self.aux_pos_trans_norm = nn.ModuleList()\n                self.pos_feats_trans = nn.ModuleList()\n                self.pos_feats_norm = nn.ModuleList()\n                for i in range(self.num_co_heads):\n                    self.aux_pos_trans.append(\n                        nn.Linear(self.embed_dims * 2, self.embed_dims))\n                    self.aux_pos_trans_norm.append(\n                        nn.LayerNorm(self.embed_dims))\n                    if self.with_coord_feat:\n                        self.pos_feats_trans.append(\n                            nn.Linear(self.embed_dims, self.embed_dims))\n                        self.pos_feats_norm.append(\n                            nn.LayerNorm(self.embed_dims))\n\n    def init_weights(self):\n        super().init_weights()\n        nn.init.normal_(self.query_embed.weight.data)\n\n    def forward(self,\n                mlvl_feats,\n                mlvl_masks,\n                query_embed,\n                mlvl_pos_embeds,\n                dn_label_query,\n                dn_bbox_query,\n                attn_mask,\n                reg_branches=None,\n                cls_branches=None,\n                **kwargs):\n        assert self.as_two_stage and query_embed is None, \\\n            'as_two_stage must be True for DINO'\n\n        feat_flatten = []\n        mask_flatten = []\n        lvl_pos_embed_flatten = []\n        spatial_shapes = []\n        for lvl, (feat, mask, pos_embed) in enumerate(\n                zip(mlvl_feats, mlvl_masks, mlvl_pos_embeds)):\n            bs, c, h, w = feat.shape\n            spatial_shape = (h, w)\n            spatial_shapes.append(spatial_shape)\n            feat = feat.flatten(2).transpose(1, 2)\n            mask = mask.flatten(1)\n            pos_embed = pos_embed.flatten(2).transpose(1, 2)\n            lvl_pos_embed = pos_embed + self.level_embeds[lvl].view(1, 1, -1)\n            lvl_pos_embed_flatten.append(lvl_pos_embed)\n            feat_flatten.append(feat)\n            mask_flatten.append(mask)\n        feat_flatten = torch.cat(feat_flatten, 1)\n        mask_flatten = torch.cat(mask_flatten, 1)\n        lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)\n        spatial_shapes = torch.as_tensor(\n            spatial_shapes, dtype=torch.long, device=feat_flatten.device)\n        level_start_index = torch.cat((spatial_shapes.new_zeros(\n            (1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))\n        valid_ratios = torch.stack(\n            [self.get_valid_ratio(m) for m in mlvl_masks], 1)\n\n        reference_points = self.get_reference_points(\n            spatial_shapes, valid_ratios, device=feat.device)\n\n        feat_flatten = feat_flatten.permute(1, 0, 2)  # (H*W, bs, embed_dims)\n        lvl_pos_embed_flatten = lvl_pos_embed_flatten.permute(\n            1, 0, 2)  # (H*W, bs, embed_dims)\n        memory = self.encoder(\n            query=feat_flatten,\n            key=None,\n            value=None,\n            query_pos=lvl_pos_embed_flatten,\n            query_key_padding_mask=mask_flatten,\n            spatial_shapes=spatial_shapes,\n            reference_points=reference_points,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            **kwargs)\n        memory = memory.permute(1, 0, 2)\n        bs, _, c = memory.shape\n\n        output_memory, output_proposals = self.gen_encoder_output_proposals(\n            memory, mask_flatten, spatial_shapes)\n        enc_outputs_class = cls_branches[self.decoder.num_layers](\n            output_memory)\n        enc_outputs_coord_unact = reg_branches[self.decoder.num_layers](\n            output_memory) + output_proposals\n        cls_out_features = cls_branches[self.decoder.num_layers].out_features\n        topk = self.two_stage_num_proposals\n        # NOTE In DeformDETR, enc_outputs_class[..., 0] is used for topk\n        topk_indices = torch.topk(enc_outputs_class.max(-1)[0], topk, dim=1)[1]\n\n        topk_score = torch.gather(\n            enc_outputs_class, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, cls_out_features))\n        topk_coords_unact = torch.gather(\n            enc_outputs_coord_unact, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, 4))\n        topk_anchor = topk_coords_unact.sigmoid()\n        topk_coords_unact = topk_coords_unact.detach()\n\n        query = self.query_embed.weight[:, None, :].repeat(1, bs,\n                                                           1).transpose(0, 1)\n        # NOTE the query_embed here is not spatial query as in DETR.\n        # It is actually content query, which is named tgt in other\n        # DETR-like models\n        if dn_label_query is not None:\n            query = torch.cat([dn_label_query, query], dim=1)\n        if dn_bbox_query is not None:\n            reference_points = torch.cat([dn_bbox_query, topk_coords_unact],\n                                         dim=1)\n        else:\n            reference_points = topk_coords_unact\n        reference_points = reference_points.sigmoid()\n        # decoder\n        query = query.permute(1, 0, 2)\n        memory = memory.permute(1, 0, 2)\n        inter_states, inter_references = self.decoder(\n            query=query,\n            key=None,\n            value=memory,\n            attn_masks=attn_mask,\n            key_padding_mask=mask_flatten,\n            reference_points=reference_points,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            reg_branches=reg_branches,\n            **kwargs)\n\n        inter_references_out = inter_references\n\n        return inter_states, inter_references_out, \\\n            topk_score, topk_anchor, memory\n\n    def forward_aux(self,\n                    mlvl_feats,\n                    mlvl_masks,\n                    query_embed,\n                    mlvl_pos_embeds,\n                    pos_anchors,\n                    pos_feats=None,\n                    reg_branches=None,\n                    cls_branches=None,\n                    return_encoder_output=False,\n                    attn_masks=None,\n                    head_idx=0,\n                    **kwargs):\n        feat_flatten = []\n        mask_flatten = []\n        spatial_shapes = []\n        for lvl, (feat, mask, pos_embed) in enumerate(\n                zip(mlvl_feats, mlvl_masks, mlvl_pos_embeds)):\n            bs, c, h, w = feat.shape\n            spatial_shape = (h, w)\n            spatial_shapes.append(spatial_shape)\n            feat = feat.flatten(2).transpose(1, 2)\n            mask = mask.flatten(1)\n            feat_flatten.append(feat)\n            mask_flatten.append(mask)\n        feat_flatten = torch.cat(feat_flatten, 1)\n        mask_flatten = torch.cat(mask_flatten, 1)\n        spatial_shapes = torch.as_tensor(\n            spatial_shapes, dtype=torch.long, device=feat_flatten.device)\n        level_start_index = torch.cat((spatial_shapes.new_zeros(\n            (1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))\n        valid_ratios = torch.stack(\n            [self.get_valid_ratio(m) for m in mlvl_masks], 1)\n\n        feat_flatten = feat_flatten.permute(1, 0, 2)  # (H*W, bs, embed_dims)\n\n        memory = feat_flatten\n        memory = memory.permute(1, 0, 2)\n        bs, _, c = memory.shape\n\n        topk_coords_unact = inverse_sigmoid(pos_anchors)\n        reference_points = pos_anchors\n        if self.num_co_heads > 0:\n            pos_trans_out = self.aux_pos_trans_norm[head_idx](\n                self.aux_pos_trans[head_idx](\n                    self.get_proposal_pos_embed(topk_coords_unact)))\n            query = pos_trans_out\n            if self.with_coord_feat:\n                query = query + self.pos_feats_norm[head_idx](\n                    self.pos_feats_trans[head_idx](pos_feats))\n\n        # decoder\n        query = query.permute(1, 0, 2)\n        memory = memory.permute(1, 0, 2)\n        inter_states, inter_references = self.decoder(\n            query=query,\n            key=None,\n            value=memory,\n            attn_masks=None,\n            key_padding_mask=mask_flatten,\n            reference_points=reference_points,\n            spatial_shapes=spatial_shapes,\n            level_start_index=level_start_index,\n            valid_ratios=valid_ratios,\n            reg_branches=reg_branches,\n            **kwargs)\n\n        inter_references_out = inter_references\n\n        return inter_states, inter_references_out\n\n\n@MODELS.register_module()\nclass DetrTransformerEncoder(TransformerLayerSequence):\n    \"\"\"TransformerEncoder of DETR.\n\n    Args:\n        post_norm_cfg (dict): Config of last normalization layer. Default：\n            `LN`. Only used when `self.pre_norm` is `True`\n    \"\"\"\n\n    def __init__(self,\n                 *args,\n                 post_norm_cfg=dict(type='LN'),\n                 with_cp=-1,\n                 **kwargs):\n        super(DetrTransformerEncoder, self).__init__(*args, **kwargs)\n        if post_norm_cfg is not None:\n            self.post_norm = build_norm_layer(\n                post_norm_cfg, self.embed_dims)[1] if self.pre_norm else None\n        else:\n            assert not self.pre_norm, f'Use prenorm in ' \\\n                                      f'{self.__class__.__name__},' \\\n                                      f'Please specify post_norm_cfg'\n            self.post_norm = None\n        self.with_cp = with_cp\n        if self.with_cp > 0:\n            if checkpoint_wrapper is None:\n                warnings.warn('If you want to reduce GPU memory usage, \\\n                              please install fairscale by executing the \\\n                              following command: pip install fairscale.')\n                return\n            for i in range(self.with_cp):\n                self.layers[i] = checkpoint_wrapper(self.layers[i])\n\n\n@MODELS.register_module()\nclass DetrTransformerDecoderLayer(BaseTransformerLayer):\n    \"\"\"Implements decoder layer in DETR transformer.\n\n    Args:\n        attn_cfgs (list[`mmcv.ConfigDict`] | list[dict] | dict )):\n            Configs for self_attention or cross_attention, the order\n            should be consistent with it in `operation_order`. If it is\n            a dict, it would be expand to the number of attention in\n            `operation_order`.\n        feedforward_channels (int): The hidden dimension for FFNs.\n        ffn_dropout (float): Probability of an element to be zeroed\n            in ffn. Default 0.0.\n        operation_order (tuple[str]): The execution order of operation\n            in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm').\n            Default：None\n        act_cfg (dict): The activation config for FFNs. Default: `LN`\n        norm_cfg (dict): Config dict for normalization layer.\n            Default: `LN`.\n        ffn_num_fcs (int): The number of fully-connected layers in FFNs.\n            Default：2.\n    \"\"\"\n\n    def __init__(self,\n                 attn_cfgs,\n                 feedforward_channels,\n                 ffn_dropout=0.0,\n                 operation_order=None,\n                 act_cfg=dict(type='ReLU', inplace=True),\n                 norm_cfg=dict(type='LN'),\n                 ffn_num_fcs=2,\n                 **kwargs):\n        super(DetrTransformerDecoderLayer, self).__init__(\n            attn_cfgs=attn_cfgs,\n            feedforward_channels=feedforward_channels,\n            ffn_dropout=ffn_dropout,\n            operation_order=operation_order,\n            act_cfg=act_cfg,\n            norm_cfg=norm_cfg,\n            ffn_num_fcs=ffn_num_fcs,\n            **kwargs)\n        assert len(operation_order) == 6\n        assert set(operation_order) == set(\n            ['self_attn', 'norm', 'cross_attn', 'ffn'])\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_r50_8xb2_1x_coco.py",
    "content": "_base_ = './co_dino_5scale_r50_lsj_8xb2_1x_coco.py'\n\nmodel = dict(\n    use_lsj=False, data_preprocessor=dict(pad_mask=False, batch_augments=None))\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        _delete_=True,\n        type=_base_.dataset_type,\n        data_root=_base_.data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=False, min_size=32),\n        pipeline=train_pipeline,\n        backend_args=_base_.backend_args))\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_r50_lsj_8xb2_1x_coco.py",
    "content": "_base_ = 'mmdet::common/ssj_scp_270k_coco-instance.py'\n\ncustom_imports = dict(\n    imports=['projects.CO-DETR.codetr'], allow_failed_imports=False)\n\n# model settings\nnum_dec_layer = 6\nloss_lambda = 2.0\nnum_classes = 80\n\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\nmodel = dict(\n    type='CoDETR',\n    # If using the lsj augmentation,\n    # it is recommended to set it to True.\n    use_lsj=True,\n    # detr: 52.1\n    # one-stage: 49.4\n    # two-stage: 47.9\n    eval_module='detr',  # in ['detr', 'one-stage', 'two-stage']\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[256, 512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=5),\n    query_head=dict(\n        type='CoDINOHead',\n        num_query=900,\n        num_classes=num_classes,\n        in_channels=2048,\n        as_two_stage=True,\n        dn_cfg=dict(\n            label_noise_scale=0.5,\n            box_noise_scale=1.0,\n            group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),\n        transformer=dict(\n            type='CoDinoTransformer',\n            with_coord_feat=False,\n            num_co_heads=2,  # ATSS Aux Head + Faster RCNN Aux Head\n            num_feature_levels=5,\n            encoder=dict(\n                type='DetrTransformerEncoder',\n                num_layers=6,\n                # number of layers that use checkpoint.\n                # The maximum value for the setting is num_layers.\n                # FairScale must be installed for it to work.\n                with_cp=4,\n                transformerlayers=dict(\n                    type='BaseTransformerLayer',\n                    attn_cfgs=dict(\n                        type='MultiScaleDeformableAttention',\n                        embed_dims=256,\n                        num_levels=5,\n                        dropout=0.0),\n                    feedforward_channels=2048,\n                    ffn_dropout=0.0,\n                    operation_order=('self_attn', 'norm', 'ffn', 'norm'))),\n            decoder=dict(\n                type='DinoTransformerDecoder',\n                num_layers=6,\n                return_intermediate=True,\n                transformerlayers=dict(\n                    type='DetrTransformerDecoderLayer',\n                    attn_cfgs=[\n                        dict(\n                            type='MultiheadAttention',\n                            embed_dims=256,\n                            num_heads=8,\n                            dropout=0.0),\n                        dict(\n                            type='MultiScaleDeformableAttention',\n                            embed_dims=256,\n                            num_levels=5,\n                            dropout=0.0),\n                    ],\n                    feedforward_channels=2048,\n                    ffn_dropout=0.0,\n                    operation_order=('self_attn', 'norm', 'cross_attn', 'norm',\n                                     'ffn', 'norm')))),\n        positional_encoding=dict(\n            type='SinePositionalEncoding',\n            num_feats=128,\n            temperature=20,\n            normalize=True),\n        loss_cls=dict(  # Different from the DINO\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            beta=2.0,\n            loss_weight=1.0),\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    rpn_head=dict(\n        type='RPNHead',\n        in_channels=256,\n        feat_channels=256,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            loss_weight=1.0 * num_dec_layer * loss_lambda),\n        loss_bbox=dict(\n            type='L1Loss', loss_weight=1.0 * num_dec_layer * loss_lambda)),\n    roi_head=[\n        dict(\n            type='CoStandardRoIHead',\n            bbox_roi_extractor=dict(\n                type='SingleRoIExtractor',\n                roi_layer=dict(\n                    type='RoIAlign', output_size=7, sampling_ratio=0),\n                out_channels=256,\n                featmap_strides=[4, 8, 16, 32, 64],\n                finest_scale=56),\n            bbox_head=dict(\n                type='Shared2FCBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=num_classes,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=False,\n                reg_decoded_bbox=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=1.0 * num_dec_layer * loss_lambda),\n                loss_bbox=dict(\n                    type='GIoULoss',\n                    loss_weight=10.0 * num_dec_layer * loss_lambda)))\n    ],\n    bbox_head=[\n        dict(\n            type='CoATSSHead',\n            num_classes=num_classes,\n            in_channels=256,\n            stacked_convs=1,\n            feat_channels=256,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[4, 8, 16, 32, 64, 128]),\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[.0, .0, .0, .0],\n                target_stds=[0.1, 0.1, 0.2, 0.2]),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0 * num_dec_layer * loss_lambda),\n            loss_bbox=dict(\n                type='GIoULoss',\n                loss_weight=2.0 * num_dec_layer * loss_lambda),\n            loss_centerness=dict(\n                type='CrossEntropyLoss',\n                use_sigmoid=True,\n                loss_weight=1.0 * num_dec_layer * loss_lambda)),\n    ],\n    # model training and testing settings\n    train_cfg=[\n        dict(\n            assigner=dict(\n                type='HungarianAssigner',\n                match_costs=[\n                    dict(type='FocalLossCost', weight=2.0),\n                    dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                    dict(type='IoUCost', iou_mode='giou', weight=2.0)\n                ])),\n        dict(\n            rpn=dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.3,\n                    min_pos_iou=0.3,\n                    match_low_quality=True,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=256,\n                    pos_fraction=0.5,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False),\n            rpn_proposal=dict(\n                nms_pre=4000,\n                max_per_img=1000,\n                nms=dict(type='nms', iou_threshold=0.7),\n                min_bbox_size=0),\n            rcnn=dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.5,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                pos_weight=-1,\n                debug=False)),\n        dict(\n            assigner=dict(type='ATSSAssigner', topk=9),\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False)\n    ],\n    test_cfg=[\n        # Deferent from the DINO, we use the NMS.\n        dict(\n            max_per_img=300,\n            # NMS can improve the mAP by 0.2.\n            nms=dict(type='soft_nms', iou_threshold=0.8)),\n        dict(\n            rpn=dict(\n                nms_pre=1000,\n                max_per_img=1000,\n                nms=dict(type='nms', iou_threshold=0.7),\n                min_bbox_size=0),\n            rcnn=dict(\n                score_thr=0.0,\n                nms=dict(type='nms', iou_threshold=0.5),\n                max_per_img=100)),\n        dict(\n            # atss bbox head:\n            nms_pre=1000,\n            min_bbox_size=0,\n            score_thr=0.0,\n            nms=dict(type='nms', iou_threshold=0.6),\n            max_per_img=100),\n        # soft-nms is also supported for rcnn testing\n        # e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05)\n    ])\n\n# LSJ + CopyPaste\nload_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=image_size, pad_val=dict(img=(114, 114, 114))),\n]\n\ntrain_pipeline = [\n    dict(type='CopyPaste', max_num_pasted=100),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        pipeline=train_pipeline,\n        dataset=dict(\n            filter_cfg=dict(filter_empty_gt=False), pipeline=load_pipeline)))\n\n# follow ViTDet\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=image_size, keep_ratio=True),  # diff\n    dict(type='Pad', size=image_size, pad_val=dict(img=(114, 114, 114))),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\noptim_wrapper = dict(\n    _delete_=True,\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=2e-4, weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.1)}))\n\nval_evaluator = dict(metric='bbox')\ntest_evaluator = val_evaluator\n\nmax_epochs = 12\ntrain_cfg = dict(\n    _delete_=True,\n    type='EpochBasedTrainLoop',\n    max_epochs=max_epochs,\n    val_interval=1)\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=True, interval=1, max_keep_ckpts=3))\nlog_processor = dict(by_epoch=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_r50_lsj_8xb2_3x_coco.py",
    "content": "_base_ = ['co_dino_5scale_r50_lsj_8xb2_1x_coco.py']\n\nparam_scheduler = [dict(milestones=[30])]\ntrain_cfg = dict(max_epochs=36)\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_swin_l_16xb1_16e_o365tococo.py",
    "content": "_base_ = ['co_dino_5scale_r50_8xb2_1x_coco.py']\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/codetr/co_dino_5scale_swin_large_16e_o365tococo-614254c9.pth'  # noqa\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=True,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[192, 384, 768, 1536]),\n    query_head=dict(\n        dn_cfg=dict(box_noise_scale=0.4, group_cfg=dict(num_dn_queries=500)),\n        transformer=dict(encoder=dict(with_cp=6))))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 2048), (512, 2048), (544, 2048), (576, 2048),\n                            (608, 2048), (640, 2048), (672, 2048), (704, 2048),\n                            (736, 2048), (768, 2048), (800, 2048), (832, 2048),\n                            (864, 2048), (896, 2048), (928, 2048), (960, 2048),\n                            (992, 2048), (1024, 2048), (1056, 2048),\n                            (1088, 2048), (1120, 2048), (1152, 2048),\n                            (1184, 2048), (1216, 2048), (1248, 2048),\n                            (1280, 2048), (1312, 2048), (1344, 2048),\n                            (1376, 2048), (1408, 2048), (1440, 2048),\n                            (1472, 2048), (1504, 2048), (1536, 2048)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 2048), (512, 2048), (544, 2048), (576, 2048),\n                            (608, 2048), (640, 2048), (672, 2048), (704, 2048),\n                            (736, 2048), (768, 2048), (800, 2048), (832, 2048),\n                            (864, 2048), (896, 2048), (928, 2048), (960, 2048),\n                            (992, 2048), (1024, 2048), (1056, 2048),\n                            (1088, 2048), (1120, 2048), (1152, 2048),\n                            (1184, 2048), (1216, 2048), (1248, 2048),\n                            (1280, 2048), (1312, 2048), (1344, 2048),\n                            (1376, 2048), (1408, 2048), (1440, 2048),\n                            (1472, 2048), (1504, 2048), (1536, 2048)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    batch_size=1, num_workers=1, dataset=dict(pipeline=train_pipeline))\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=(2048, 1280), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\noptim_wrapper = dict(optimizer=dict(lr=1e-4))\n\nmax_epochs = 16\ntrain_cfg = dict(max_epochs=max_epochs)\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[8],\n        gamma=0.1)\n]\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_swin_l_16xb1_1x_coco.py",
    "content": "_base_ = ['co_dino_5scale_r50_8xb2_1x_coco.py']\n\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\n\n# model settings\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[192, 384, 768, 1536]),\n    query_head=dict(transformer=dict(encoder=dict(with_cp=6))))\n\ntrain_dataloader = dict(batch_size=1, num_workers=1)\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_swin_l_16xb1_3x_coco.py",
    "content": "_base_ = ['co_dino_5scale_swin_l_16xb1_1x_coco.py']\n# model settings\nmodel = dict(backbone=dict(drop_path_rate=0.6))\n\nparam_scheduler = [dict(milestones=[30])]\ntrain_cfg = dict(max_epochs=36)\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_swin_l_lsj_16xb1_1x_coco.py",
    "content": "_base_ = ['co_dino_5scale_r50_lsj_8xb2_1x_coco.py']\n\nimage_size = (1280, 1280)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\npretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth'  # noqa\n\n# model settings\nmodel = dict(\n    data_preprocessor=dict(batch_augments=batch_augments),\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        pretrain_img_size=384,\n        embed_dims=192,\n        depths=[2, 2, 18, 2],\n        num_heads=[6, 12, 24, 48],\n        window_size=12,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(0, 1, 2, 3),\n        # Please only add indices that would be used\n        # in FPN, otherwise some parameter will not be used\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),\n    neck=dict(in_channels=[192, 384, 768, 1536]),\n    query_head=dict(transformer=dict(encoder=dict(with_cp=6))))\n\nload_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='Pad', size=image_size, pad_val=dict(img=(114, 114, 114))),\n]\n\ntrain_dataloader = dict(\n    batch_size=1,\n    num_workers=1,\n    dataset=dict(dataset=dict(pipeline=load_pipeline)))\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=image_size, keep_ratio=True),\n    dict(type='Pad', size=image_size, pad_val=dict(img=(114, 114, 114))),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "projects/CO-DETR/configs/codino/co_dino_5scale_swin_l_lsj_16xb1_3x_coco.py",
    "content": "_base_ = ['co_dino_5scale_swin_l_lsj_16xb1_1x_coco.py']\n\nmodel = dict(backbone=dict(drop_path_rate=0.5))\n\nparam_scheduler = [dict(type='MultiStepLR', milestones=[30])]\n\ntrain_cfg = dict(max_epochs=36)\n"
  },
  {
    "path": "projects/ConvNeXt-V2/README.md",
    "content": "# ConvNeXt-V2\n\n> [ConvNeXt V2: Co-designing and Scaling ConvNets with Masked Autoencoders](http://arxiv.org/abs/2301.00808)\n\n## Abstract\n\nDriven by improved architectures and better representation learning frameworks, the field of visual recognition has enjoyed rapid modernization and performance boost in the early 2020s. For example, modern ConvNets, represented by ConvNeXt \\[52\\], have demonstrated strong performance in various scenarios. While these models were originally designed for supervised learning with ImageNet labels, they can also potentially benefit from self-supervised learning techniques such as masked autoencoders (MAE) . However, we found that simply combining these two approaches leads to subpar performance. In this paper, we propose a fully convolutional masked autoencoder framework and a new Global Response Normalization (GRN) layer that can be added to the ConvNeXt architecture to enhance inter-channel feature competition. This co-design of self-supervised learning techniques and architectural improvement results in a new model family called ConvNeXt V2, which significantly improves the performance of pure ConvNets on various recognition benchmarks, including ImageNet classification, COCO detection, and ADE20K segmentation. We also provide pre-trained ConvNeXt V2 models of various sizes, ranging from an efficient 3.7Mparameter Atto model with 76.7% top-1 accuracy on Im-ageNet, to a 650M Huge model that achieves a state-of-theart 88.9% accuracy using only public training data.\n\n<div align=center>\n<img src=\"https://user-images.githubusercontent.com/12907710/212588579-02d621d8-5796-4f0d-b4d2-758fe9c2f395.png\" width=\"50%\"/>\n</div>\n\n## Results and models\n\n|   Method   |   Backbone    | Pretrain | Lr schd | Augmentation | Mem (GB) | box AP | mask AP |                            Config                            |                                                                                                                                                                                        Download                                                                                                                                                                                         |\n| :--------: | :-----------: | :------: | :-----: | :----------: | :------: | :----: | :-----: | :----------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| Mask R-CNN | ConvNeXt-V2-B |  FCMAE   |   3x    |     LSJ      |   22.5   |  52.9  |  46.4   | [config](./mask-rcnn_convnext-v2-b_fpn_lsj-3x-fcmae_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/convnextv2/mask-rcnn_convnext-v2-b_fpn_lsj-3x-fcmae_coco/mask-rcnn_convnext-v2-b_fpn_lsj-3x-fcmae_coco_20230113_110947-757ee2dd.pth)  \\| [log](https://download.openmmlab.com/mmdetection/v3.0/convnextv2/mask-rcnn_convnext-v2-b_fpn_lsj-3x-fcmae_coco/mask-rcnn_convnext-v2-b_fpn_lsj-3x-fcmae_coco_20230113_110947.log.json) |\n\n**Note**:\n\n- This is a pre-release version of ConvNeXt-V2 object detection. The official finetuning setting of ConvNeXt-V2 has not been released yet.\n- ConvNeXt backbone needs to install [MMPretrain](https://github.com/open-mmlab/mmpretrain/) first, which has abundant backbones for downstream tasks.\n\n```shell\npip install mmpretrain\n```\n\n## Citation\n\n```bibtex\n@article{Woo2023ConvNeXtV2,\n  title={ConvNeXt V2: Co-designing and Scaling ConvNets with Masked Autoencoders},\n  author={Sanghyun Woo, Shoubhik Debnath, Ronghang Hu, Xinlei Chen, Zhuang Liu, In So Kweon and Saining Xie},\n  year={2023},\n  journal={arXiv preprint arXiv:2301.00808},\n}\n```\n"
  },
  {
    "path": "projects/ConvNeXt-V2/configs/mask-rcnn_convnext-v2-b_fpn_lsj-3x-fcmae_coco.py",
    "content": "_base_ = [\n    'mmdet::_base_/models/mask-rcnn_r50_fpn.py',\n    'mmdet::_base_/datasets/coco_instance.py',\n    'mmdet::_base_/schedules/schedule_1x.py',\n    'mmdet::_base_/default_runtime.py'\n]\n\n# please install the mmpretrain\n# import mmpretrain.models to trigger register_module in mmpretrain\ncustom_imports = dict(\n    imports=['mmpretrain.models'], allow_failed_imports=False)\ncheckpoint_file = 'https://download.openmmlab.com/mmclassification/v0/convnext-v2/convnext-v2-base_3rdparty-fcmae_in1k_20230104-8a798eaf.pth'  # noqa\nimage_size = (1024, 1024)\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='mmpretrain.ConvNeXt',\n        arch='base',\n        out_indices=[0, 1, 2, 3],\n        # TODO: verify stochastic depth rate {0.1, 0.2, 0.3, 0.4}\n        drop_path_rate=0.4,\n        layer_scale_init_value=0.,  # disable layer scale when using GRN\n        gap_before_final_norm=False,\n        use_grn=True,  # V2 uses GRN\n        init_cfg=dict(\n            type='Pretrained', checkpoint=checkpoint_file,\n            prefix='backbone.')),\n    neck=dict(in_channels=[128, 256, 512, 1024]),\n    test_cfg=dict(\n        rpn=dict(nms=dict(type='nms')),  # TODO: does RPN use soft_nms?\n        rcnn=dict(nms=dict(type='soft_nms'))))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    batch_size=4,  # total_batch_size 32 = 8 GPUS x 4 images\n    num_workers=8,\n    dataset=dict(pipeline=train_pipeline))\n\nmax_epochs = 36\ntrain_cfg = dict(max_epochs=max_epochs)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[27, 33],\n        gamma=0.1)\n]\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    constructor='LearningRateDecayOptimizerConstructor',\n    paramwise_cfg={\n        'decay_rate': 0.95,\n        'decay_type': 'layer_wise',  # TODO: sweep layer-wise lr decay?\n        'num_layers': 12\n    },\n    optimizer=dict(\n        _delete_=True,\n        type='AdamW',\n        lr=0.0001,\n        betas=(0.9, 0.999),\n        weight_decay=0.05,\n    ))\n\ndefault_hooks = dict(checkpoint=dict(max_keep_ckpts=1))\n"
  },
  {
    "path": "projects/Detic/README.md",
    "content": "# Note: This project has been deprecated, please use [Detic_new](../Detic_new).\n\n# Detecting Twenty-thousand Classes using Image-level Supervision\n\n## Description\n\n**Detic**: A **Det**ector with **i**mage **c**lasses that can use image-level labels to easily train detectors.\n\n<p align=\"center\"> <img src='https://github.com/facebookresearch/Detic/blob/main/docs/teaser.jpeg?raw=true' align=\"center\" height=\"300px\"> </p>\n\n> [**Detecting Twenty-thousand Classes using Image-level Supervision**](http://arxiv.org/abs/2201.02605),\n> Xingyi Zhou, Rohit Girdhar, Armand Joulin, Philipp Krähenbühl, Ishan Misra,\n> *ECCV 2022 ([arXiv 2201.02605](http://arxiv.org/abs/2201.02605))*\n\n## Usage\n\n<!-- For a typical model, this section should contain the commands for training and testing. You are also suggested to dump your environment specification to env.yml by `conda env export > env.yml`. -->\n\n## Installation\n\nDetic requires to install CLIP.\n\n```shell\npip install git+https://github.com/openai/CLIP.git\n```\n\n### Demo\n\n#### Inference with existing dataset vocabulary embeddings\n\nFirst, go to the Detic project folder.\n\n```shell\ncd projects/Detic\n```\n\nThen, download the pre-computed CLIP embeddings from [dataset metainfo](https://github.com/facebookresearch/Detic/tree/main/datasets/metadata) to the `datasets/metadata` folder.\nThe CLIP embeddings will be loaded to the zero-shot classifier during inference.\nFor example, you can download LVIS's class name embeddings with the following command:\n\n```shell\nwget -P datasets/metadata https://raw.githubusercontent.com/facebookresearch/Detic/main/datasets/metadata/lvis_v1_clip_a%2Bcname.npy\n```\n\nYou can run demo like this:\n\n```shell\npython demo.py \\\n  ${IMAGE_PATH} \\\n  ${CONFIG_PATH} \\\n  ${MODEL_PATH} \\\n  --show \\\n  --score-thr 0.5 \\\n  --dataset lvis\n```\n\n![image](https://user-images.githubusercontent.com/12907710/213624759-f0a2ba0c-0f5c-4424-a350-5ba5349e5842.png)\n\n### Inference with custom vocabularies\n\n- Detic can detects any class given class names by using CLIP.\n\nYou can detect custom classes with `--class-name` command:\n\n```\npython demo.py \\\n  ${IMAGE_PATH} \\\n  ${CONFIG_PATH} \\\n  ${MODEL_PATH} \\\n  --show \\\n  --score-thr 0.3 \\\n  --class-name headphone webcam paper coffe\n```\n\n![image](https://user-images.githubusercontent.com/12907710/213624637-e9e8a313-9821-4782-a18a-4408c876852b.png)\n\nNote that `headphone`, `paper` and `coffe` (typo intended) are not LVIS classes. Despite the misspelled class name, Detic can produce a reasonable detection for `coffe`.\n\n## Results\n\nHere we only provide the Detic Swin-B model for the open vocabulary demo. Multi-dataset training and open-vocabulary testing will be supported in the future.\n\nTo find more variants, please visit the [official model zoo](https://github.com/facebookresearch/Detic/blob/main/docs/MODEL_ZOO.md).\n\n| Backbone |       Training data        |                                Config                                 |                                                                                      Download                                                                                      |\n| :------: | :------------------------: | :-------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  Swin-B  | ImageNet-21K & LVIS & COCO | [config](./configs/detic_centernet2_swin-b_fpn_4x_lvis-coco-in21k.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis-coco-in21k/detic_centernet2_swin-b_fpn_4x_lvis-coco-in21k_20230120-0d301978.pth) |\n\n## Citation\n\nIf you find Detic is useful in your research or applications, please consider giving a star 🌟 to the [official repository](https://github.com/facebookresearch/Detic) and citing Detic by the following BibTeX entry.\n\n```BibTeX\n@inproceedings{zhou2022detecting,\n  title={Detecting Twenty-thousand Classes using Image-level Supervision},\n  author={Zhou, Xingyi and Girdhar, Rohit and Joulin, Armand and Kr{\\\"a}henb{\\\"u}hl, Philipp and Misra, Ishan},\n  booktitle={ECCV},\n  year={2022}\n}\n\n```\n\n## Checklist\n\n<!-- Here is a checklist illustrating a usual development workflow of a successful project, and also serves as an overview of this project's progress. The PIC (person in charge) or contributors of this project should check all the items that they believe have been finished, which will further be verified by codebase maintainers via a PR.\nOpenMMLab's maintainer will review the code to ensure the project's quality. Reaching the first milestone means that this project suffices the minimum requirement of being merged into 'projects/'. But this project is only eligible to become a part of the core package upon attaining the last milestone.\nNote that keeping this section up-to-date is crucial not only for this project's developers but the entire community, since there might be some other contributors joining this project and deciding their starting point from this list. It also helps maintainers accurately estimate time and effort on further code polishing, if needed.\nA project does not necessarily have to be finished in a single PR, but it's essential for the project to at least reach the first milestone in its very first PR. -->\n\n- [x] Milestone 1: PR-ready, and acceptable to be one of the `projects/`.\n\n  - [x] Finish the code\n\n    <!-- The code's design shall follow existing interfaces and convention. For example, each model component should be registered into `mmdet.registry.MODELS` and configurable via a config file. -->\n\n  - [x] Basic docstrings & proper citation\n\n    <!-- Each major object should contain a docstring, describing its functionality and arguments. If you have adapted the code from other open-source projects, don't forget to cite the source project in docstring and make sure your behavior is not against its license. Typically, we do not accept any code snippet under GPL license. [A Short Guide to Open Source Licenses](https://medium.com/nationwide-technology/a-short-guide-to-open-source-licenses-cf5b1c329edd) -->\n\n  - [x] Test-time correctness\n\n    <!-- If you are reproducing the result from a paper, make sure your model's inference-time performance matches that in the original paper. The weights usually could be obtained by simply renaming the keys in the official pre-trained weights. This test could be skipped though, if you are able to prove the training-time correctness and check the second milestone. -->\n\n  - [x] A full README\n\n    <!-- As this template does. -->\n\n- [ ] Milestone 2: Indicates a successful model implementation.\n\n  - [ ] Training-time correctness\n\n    <!-- If you are reproducing the result from a paper, checking this item means that you should have trained your model from scratch based on the original paper's specification and verified that the final result matches the report within a minor error range. -->\n\n- [ ] Milestone 3: Good to be a part of our core package!\n\n  - [ ] Type hints and docstrings\n\n    <!-- Ideally *all* the methods should have [type hints](https://www.pythontutorial.net/python-basics/python-type-hints/) and [docstrings](https://google.github.io/styleguide/pyguide.html#381-docstrings). [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/mmdet/datasets/transforms/transforms.py#L41-L169) -->\n\n  - [ ] Unit tests\n\n    <!-- Unit tests for each module are required. [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/tests/test_datasets/test_transforms/test_transforms.py#L35-L88) -->\n\n  - [ ] Code polishing\n\n    <!-- Refactor your code according to reviewer's comment. -->\n\n  - [ ] Metafile.yml\n\n    <!-- It will be parsed by MIM and Inferencer. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/metafile.yml) -->\n\n- [ ] Move your modules into the core package following the codebase's file hierarchy structure.\n\n  <!-- In particular, you may have to refactor this README into a standard one. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/README.md) -->\n\n- [ ] Refactor your modules into the core package following the codebase's file hierarchy structure.\n"
  },
  {
    "path": "projects/Detic/configs/detic_centernet2_swin-b_fpn_4x_lvis-coco-in21k.py",
    "content": "_base_ = 'mmdet::common/lsj-200e_coco-detection.py'\n\ncustom_imports = dict(\n    imports=['projects.Detic.detic'], allow_failed_imports=False)\n\nimage_size = (1024, 1024)\nbatch_augments = [dict(type='BatchFixedSizePad', size=image_size)]\n\ncls_layer = dict(\n    type='ZeroShotClassifier',\n    zs_weight_path='rand',\n    zs_weight_dim=512,\n    use_bias=0.0,\n    norm_weight=True,\n    norm_temperature=50.0)\nreg_layer = [\n    dict(type='Linear', in_features=1024, out_features=1024),\n    dict(type='ReLU', inplace=True),\n    dict(type='Linear', in_features=1024, out_features=4)\n]\n\nnum_classes = 22047\n\nmodel = dict(\n    type='CascadeRCNN',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024],\n        out_channels=256,\n        start_level=0,\n        add_extra_convs='on_output',\n        num_outs=5,\n        init_cfg=dict(type='Caffe2Xavier', layer='Conv2d'),\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        type='CenterNetRPNHead',\n        num_classes=1,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        conv_bias=True,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True),\n        loss_cls=dict(\n            type='GaussianFocalLoss',\n            pos_weight=0.25,\n            neg_weight=0.75,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n    ),\n    roi_head=dict(\n        type='DeticRoIHead',\n        num_stages=3,\n        stage_loss_weights=[1, 0.5, 0.25],\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=7,\n                sampling_ratio=0,\n                use_torchvision=True),\n            out_channels=256,\n            featmap_strides=[8, 16, 32],\n            # approximately equal to\n            # canonical_box_size=224, canonical_level=4 in D2\n            finest_scale=112),\n        bbox_head=[\n            dict(\n                type='DeticBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=num_classes,\n                cls_predictor_cfg=cls_layer,\n                reg_predictor_cfg=reg_layer,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss', use_sigmoid=True,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='DeticBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=num_classes,\n                cls_predictor_cfg=cls_layer,\n                reg_predictor_cfg=reg_layer,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss', use_sigmoid=True,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0,\n                               loss_weight=1.0)),\n            dict(\n                type='DeticBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=num_classes,\n                cls_predictor_cfg=cls_layer,\n                reg_predictor_cfg=reg_layer,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss', use_sigmoid=True,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))\n        ],\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[8, 16, 32],\n            # approximately equal to\n            # canonical_box_size=224, canonical_level=4 in D2\n            finest_scale=112),\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            class_agnostic=True,\n            num_classes=num_classes,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            nms_pre=2000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.8,\n                    neg_iou_thr=0.8,\n                    min_pos_iou=0.8,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rpn=dict(\n            score_thr=0.0001,\n            nms_pre=1000,\n            max_per_img=256,\n            nms=dict(type='nms', iou_threshold=0.9),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.02,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=300,\n            mask_thr_binary=0.5)))\n\nbackend = 'pillow'\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=_base_.backend_args,\n        imdecode_backend=backend),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True, backend=backend),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(batch_size=8, num_workers=4)\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    optimizer=dict(\n        type='SGD', lr=0.01 * 4, momentum=0.9, weight_decay=0.00004),\n    paramwise_cfg=dict(norm_decay_mult=0.))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.00025,\n        by_epoch=False,\n        begin=0,\n        end=4000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=25,\n        by_epoch=True,\n        milestones=[22, 24],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "projects/Detic/demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport urllib\nfrom argparse import ArgumentParser\n\nimport mmcv\nimport torch\nfrom mmengine.logging import print_log\nfrom mmengine.utils import ProgressBar, scandir\n\nfrom mmdet.apis import inference_detector, init_detector\nfrom mmdet.registry import VISUALIZERS\nfrom mmdet.utils import register_all_modules\n\nIMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif',\n                  '.tiff', '.webp')\n\n\ndef get_file_list(source_root: str) -> [list, dict]:\n    \"\"\"Get file list.\n\n    Args:\n        source_root (str): image or video source path\n\n    Return:\n        source_file_path_list (list): A list for all source file.\n        source_type (dict): Source type: file or url or dir.\n    \"\"\"\n    is_dir = os.path.isdir(source_root)\n    is_url = source_root.startswith(('http:/', 'https:/'))\n    is_file = os.path.splitext(source_root)[-1].lower() in IMG_EXTENSIONS\n\n    source_file_path_list = []\n    if is_dir:\n        # when input source is dir\n        for file in scandir(source_root, IMG_EXTENSIONS, recursive=True):\n            source_file_path_list.append(os.path.join(source_root, file))\n    elif is_url:\n        # when input source is url\n        filename = os.path.basename(\n            urllib.parse.unquote(source_root).split('?')[0])\n        file_save_path = os.path.join(os.getcwd(), filename)\n        print(f'Downloading source file to {file_save_path}')\n        torch.hub.download_url_to_file(source_root, file_save_path)\n        source_file_path_list = [file_save_path]\n    elif is_file:\n        # when input source is single image\n        source_file_path_list = [source_root]\n    else:\n        print('Cannot find image file.')\n\n    source_type = dict(is_dir=is_dir, is_url=is_url, is_file=is_file)\n\n    return source_file_path_list, source_type\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument(\n        'img', help='Image path, include image file, dir and URL.')\n    parser.add_argument('config', help='Config file')\n    parser.add_argument('checkpoint', help='Checkpoint file')\n    parser.add_argument(\n        '--out-dir', default='./output', help='Path to output file')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--show', action='store_true', help='Show the detection results')\n    parser.add_argument(\n        '--score-thr', type=float, default=0.3, help='Bbox score threshold')\n    parser.add_argument(\n        '--dataset', type=str, help='dataset name to load the text embedding')\n    parser.add_argument(\n        '--class-name', nargs='+', type=str, help='custom class names')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    # register all modules in mmdet into the registries\n    register_all_modules()\n\n    # build the model from a config file and a checkpoint file\n    model = init_detector(args.config, args.checkpoint, device=args.device)\n\n    if not os.path.exists(args.out_dir) and not args.show:\n        os.mkdir(args.out_dir)\n\n    # init visualizer\n    visualizer = VISUALIZERS.build(model.cfg.visualizer)\n    visualizer.dataset_meta = model.dataset_meta\n\n    # get file list\n    files, source_type = get_file_list(args.img)\n    from detic.utils import (get_class_names, get_text_embeddings,\n                             reset_cls_layer_weight)\n\n    # class name embeddings\n    if args.class_name:\n        dataset_classes = args.class_name\n    elif args.dataset:\n        dataset_classes = get_class_names(args.dataset)\n    embedding = get_text_embeddings(\n        dataset=args.dataset, custom_vocabulary=args.class_name)\n    visualizer.dataset_meta['classes'] = dataset_classes\n    reset_cls_layer_weight(model, embedding)\n\n    # start detector inference\n    progress_bar = ProgressBar(len(files))\n    for file in files:\n        result = inference_detector(model, file)\n\n        img = mmcv.imread(file)\n        img = mmcv.imconvert(img, 'bgr', 'rgb')\n\n        if source_type['is_dir']:\n            filename = os.path.relpath(file, args.img).replace('/', '_')\n        else:\n            filename = os.path.basename(file)\n        out_file = None if args.show else os.path.join(args.out_dir, filename)\n\n        progress_bar.update()\n\n        visualizer.add_datasample(\n            filename,\n            img,\n            data_sample=result,\n            draw_gt=False,\n            show=args.show,\n            wait_time=0,\n            out_file=out_file,\n            pred_score_thr=args.score_thr)\n\n    if not args.show:\n        print_log(\n            f'\\nResults have been saved at {os.path.abspath(args.out_dir)}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/Detic/detic/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .centernet_rpn_head import CenterNetRPNHead\nfrom .detic_bbox_head import DeticBBoxHead\nfrom .detic_roi_head import DeticRoIHead\nfrom .zero_shot_classifier import ZeroShotClassifier\n\n__all__ = [\n    'CenterNetRPNHead', 'DeticBBoxHead', 'DeticRoIHead', 'ZeroShotClassifier'\n]\n"
  },
  {
    "path": "projects/Detic/detic/centernet_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import Scale\nfrom mmengine import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.dense_heads import CenterNetUpdateHead\nfrom mmdet.models.utils import multi_apply\nfrom mmdet.registry import MODELS\n\nINF = 1000000000\nRangeType = Sequence[Tuple[int, int]]\n\n\n@MODELS.register_module(force=True)  # avoid bug\nclass CenterNetRPNHead(CenterNetUpdateHead):\n    \"\"\"CenterNetUpdateHead is an improved version of CenterNet in CenterNet2.\n\n    Paper link `<https://arxiv.org/abs/2103.07461>`_.\n    \"\"\"\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self._init_reg_convs()\n        self._init_predictor()\n\n    def _init_predictor(self) -> None:\n        \"\"\"Initialize predictor layers of the head.\"\"\"\n        self.conv_cls = nn.Conv2d(\n            self.feat_channels, self.num_classes, 3, padding=1)\n        self.conv_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1)\n\n    def forward(self, x: Tuple[Tensor]) -> Tuple[List[Tensor], List[Tensor]]:\n        \"\"\"Forward features from the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n\n        Returns:\n            tuple: A tuple of each level outputs.\n\n            - cls_scores (list[Tensor]): Box scores for each scale level, \\\n            each is a 4D-tensor, the channel number is num_classes.\n            - bbox_preds (list[Tensor]): Box energies / deltas for each \\\n            scale level, each is a 4D-tensor, the channel number is 4.\n        \"\"\"\n        res = multi_apply(self.forward_single, x, self.scales, self.strides)\n        return res\n\n    def forward_single(self, x: Tensor, scale: Scale,\n                       stride: int) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n            scale (:obj:`mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n            stride (int): The corresponding stride for feature maps.\n\n        Returns:\n            tuple: scores for each class, bbox predictions of\n            input feature maps.\n        \"\"\"\n        for m in self.reg_convs:\n            x = m(x)\n        cls_score = self.conv_cls(x)\n        bbox_pred = self.conv_reg(x)\n        # scale the bbox_pred of different level\n        # float to avoid overflow when enabling FP16\n        bbox_pred = scale(bbox_pred).float()\n        # bbox_pred needed for gradient computation has been modified\n        # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace\n        # F.relu(bbox_pred) with bbox_pred.clamp(min=0)\n        bbox_pred = bbox_pred.clamp(min=0)\n        if not self.training:\n            bbox_pred *= stride\n        return cls_score, bbox_pred  # score aligned, box larger\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid. In all\n                anchor-based methods, it has shape (num_priors, 4). In\n                all anchor-free methods, it has shape (num_priors, 2)\n                when `with_stride=True`, otherwise it still has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bbox_preds = []\n        mlvl_valid_priors = []\n        mlvl_scores = []\n        mlvl_labels = []\n\n        for level_idx, (cls_score, bbox_pred, score_factor, priors) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list,\n                              score_factor_list, mlvl_priors)):\n\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            dim = self.bbox_coder.encode_size\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, dim)\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            heatmap = cls_score.sigmoid()\n            score_thr = cfg.get('score_thr', 0)\n\n            candidate_inds = heatmap > score_thr  # 0.05\n            pre_nms_top_n = candidate_inds.sum()  # N\n            pre_nms_top_n = pre_nms_top_n.clamp(max=nms_pre)  # N\n\n            heatmap = heatmap[candidate_inds]  # n\n\n            candidate_nonzeros = candidate_inds.nonzero()  # n\n            box_loc = candidate_nonzeros[:, 0]  # n\n            labels = candidate_nonzeros[:, 1]  # n\n\n            bbox_pred = bbox_pred[box_loc]  # n x 4\n            per_grids = priors[box_loc]  # n x 2\n\n            if candidate_inds.sum().item() > pre_nms_top_n.item():\n                heatmap, top_k_indices = \\\n                    heatmap.topk(pre_nms_top_n, sorted=False)\n                labels = labels[top_k_indices]\n                bbox_pred = bbox_pred[top_k_indices]\n                per_grids = per_grids[top_k_indices]\n\n            bboxes = self.bbox_coder.decode(per_grids, bbox_pred)\n            # avoid invalid boxes in RoI heads\n            bboxes[:, 2] = torch.max(bboxes[:, 2], bboxes[:, 0] + 0.01)\n            bboxes[:, 3] = torch.max(bboxes[:, 3], bboxes[:, 1] + 0.01)\n\n            mlvl_bbox_preds.append(bboxes)\n            mlvl_valid_priors.append(priors)\n            mlvl_scores.append(torch.sqrt(heatmap))\n            mlvl_labels.append(labels)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bbox_preds)\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n"
  },
  {
    "path": "projects/Detic/detic/detic_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Union\n\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.layers import multiclass_nms\nfrom mmdet.models.roi_heads.bbox_heads import Shared2FCBBoxHead\nfrom mmdet.models.utils import empty_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import get_box_tensor, scale_boxes\n\n\n@MODELS.register_module(force=True)  # avoid bug\nclass DeticBBoxHead(Shared2FCBBoxHead):\n\n    def __init__(self,\n                 *args,\n                 init_cfg: Optional[Union[dict, ConfigDict]] = None,\n                 **kwargs) -> None:\n        super().__init__(*args, init_cfg=init_cfg, **kwargs)\n        # reconstruct fc_cls and fc_reg since input channels are changed\n        assert self.with_cls\n        cls_channels = self.num_classes\n        cls_predictor_cfg_ = self.cls_predictor_cfg.copy()\n        cls_predictor_cfg_.update(\n            in_features=self.cls_last_dim, out_features=cls_channels)\n        self.fc_cls = MODELS.build(cls_predictor_cfg_)\n\n    def _predict_by_feat_single(\n            self,\n            roi: Tensor,\n            cls_score: Tensor,\n            bbox_pred: Tensor,\n            img_meta: dict,\n            rescale: bool = False,\n            rcnn_test_cfg: Optional[ConfigDict] = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            roi (Tensor): Boxes to be transformed. Has shape (num_boxes, 5).\n                last dimension 5 arrange as (batch_index, x1, y1, x2, y2).\n            cls_score (Tensor): Box scores, has shape\n                (num_boxes, num_classes + 1).\n            bbox_pred (Tensor): Box energies / deltas.\n                has shape (num_boxes, num_classes * 4).\n            img_meta (dict): image information.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of Bbox Head.\n                Defaults to None\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\\\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        results = InstanceData()\n        if roi.shape[0] == 0:\n            return empty_instances([img_meta],\n                                   roi.device,\n                                   task_type='bbox',\n                                   instance_results=[results],\n                                   box_type=self.predict_box_type,\n                                   use_box_type=False,\n                                   num_classes=self.num_classes,\n                                   score_per_cls=rcnn_test_cfg is None)[0]\n        scores = cls_score\n        img_shape = img_meta['img_shape']\n        num_rois = roi.size(0)\n\n        num_classes = 1 if self.reg_class_agnostic else self.num_classes\n        roi = roi.repeat_interleave(num_classes, dim=0)\n        bbox_pred = bbox_pred.view(-1, self.bbox_coder.encode_size)\n        bboxes = self.bbox_coder.decode(\n            roi[..., 1:], bbox_pred, max_shape=img_shape)\n\n        if rescale and bboxes.size(0) > 0:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = [1 / s for s in img_meta['scale_factor']]\n            bboxes = scale_boxes(bboxes, scale_factor)\n\n        # Get the inside tensor when `bboxes` is a box type\n        bboxes = get_box_tensor(bboxes)\n        box_dim = bboxes.size(-1)\n        bboxes = bboxes.view(num_rois, -1)\n\n        if rcnn_test_cfg is None:\n            # This means that it is aug test.\n            # It needs to return the raw results without nms.\n            results.bboxes = bboxes\n            results.scores = scores\n        else:\n            det_bboxes, det_labels = multiclass_nms(\n                bboxes,\n                scores,\n                rcnn_test_cfg.score_thr,\n                rcnn_test_cfg.nms,\n                rcnn_test_cfg.max_per_img,\n                box_dim=box_dim)\n            results.bboxes = det_bboxes[:, :-1]\n            results.scores = det_bboxes[:, -1]\n            results.labels = det_labels\n        return results\n"
  },
  {
    "path": "projects/Detic/detic/detic_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Sequence, Tuple\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads import CascadeRoIHead\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.test_time_augs import merge_aug_masks\nfrom mmdet.models.utils.misc import empty_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi, get_box_tensor\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig\n\n\n@MODELS.register_module(force=True)  # avoid bug\nclass DeticRoIHead(CascadeRoIHead):\n\n    def init_mask_head(self, mask_roi_extractor: MultiConfig,\n                       mask_head: MultiConfig) -> None:\n        \"\"\"Initialize mask head and mask roi extractor.\n\n        Args:\n            mask_head (dict): Config of mask in mask head.\n            mask_roi_extractor (:obj:`ConfigDict`, dict or list):\n                Config of mask roi extractor.\n        \"\"\"\n        self.mask_head = MODELS.build(mask_head)\n\n        if mask_roi_extractor is not None:\n            self.share_roi_extractor = False\n            self.mask_roi_extractor = MODELS.build(mask_roi_extractor)\n        else:\n            self.share_roi_extractor = True\n            self.mask_roi_extractor = self.bbox_roi_extractor\n\n    def _refine_roi(self, x: Tuple[Tensor], rois: Tensor,\n                    batch_img_metas: List[dict],\n                    num_proposals_per_img: Sequence[int], **kwargs) -> tuple:\n        \"\"\"Multi-stage refinement of RoI.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): shape (n, 5), [batch_ind, x1, y1, x2, y2]\n            batch_img_metas (list[dict]): List of image information.\n            num_proposals_per_img (sequence[int]): number of proposals\n                in each image.\n\n        Returns:\n            tuple:\n\n               - rois (Tensor): Refined RoI.\n               - cls_scores (list[Tensor]): Average predicted\n                   cls score per image.\n               - bbox_preds (list[Tensor]): Bbox branch predictions\n                   for the last stage of per image.\n        \"\"\"\n        # \"ms\" in variable names means multi-stage\n        ms_scores = []\n        for stage in range(self.num_stages):\n            bbox_results = self._bbox_forward(\n                stage=stage, x=x, rois=rois, **kwargs)\n\n            # split batch bbox prediction back to each image\n            cls_scores = bbox_results['cls_score'].sigmoid()\n            bbox_preds = bbox_results['bbox_pred']\n\n            rois = rois.split(num_proposals_per_img, 0)\n            cls_scores = cls_scores.split(num_proposals_per_img, 0)\n            ms_scores.append(cls_scores)\n            bbox_preds = bbox_preds.split(num_proposals_per_img, 0)\n\n            if stage < self.num_stages - 1:\n                bbox_head = self.bbox_head[stage]\n                refine_rois_list = []\n                for i in range(len(batch_img_metas)):\n                    if rois[i].shape[0] > 0:\n                        bbox_label = cls_scores[i][:, :-1].argmax(dim=1)\n                        # Refactor `bbox_head.regress_by_class` to only accept\n                        # box tensor without img_idx concatenated.\n                        refined_bboxes = bbox_head.regress_by_class(\n                            rois[i][:, 1:], bbox_label, bbox_preds[i],\n                            batch_img_metas[i])\n                        refined_bboxes = get_box_tensor(refined_bboxes)\n                        refined_rois = torch.cat(\n                            [rois[i][:, [0]], refined_bboxes], dim=1)\n                        refine_rois_list.append(refined_rois)\n                rois = torch.cat(refine_rois_list)\n        # ms_scores aligned\n        # average scores of each image by stages\n        cls_scores = [\n            sum([score[i] for score in ms_scores]) / float(len(ms_scores))\n            for i in range(len(batch_img_metas))\n        ]  # aligned\n        return rois, cls_scores, bbox_preds\n\n    def _bbox_forward(self, stage: int, x: Tuple[Tensor],\n                      rois: Tensor) -> dict:\n        \"\"\"Box head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n             dict[str, Tensor]: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n        \"\"\"\n        bbox_roi_extractor = self.bbox_roi_extractor[stage]\n        bbox_head = self.bbox_head[stage]\n        bbox_feats = bbox_roi_extractor(x[:bbox_roi_extractor.num_inputs],\n                                        rois)\n        # do not support caffe_c4 model anymore\n        cls_score, bbox_pred = bbox_head(bbox_feats)\n\n        bbox_results = dict(\n            cls_score=cls_score, bbox_pred=bbox_pred, bbox_feats=bbox_feats)\n        return bbox_results\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False,\n                     **kwargs) -> InstanceList:\n        \"\"\"Perform forward propagation of the bbox head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of R-CNN.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        proposals = [res.bboxes for res in rpn_results_list]\n        proposal_scores = [res.scores for res in rpn_results_list]\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = bbox2roi(proposals)\n\n        if rois.shape[0] == 0:\n            return empty_instances(\n                batch_img_metas,\n                rois.device,\n                task_type='bbox',\n                box_type=self.bbox_head[-1].predict_box_type,\n                num_classes=self.bbox_head[-1].num_classes,\n                score_per_cls=rcnn_test_cfg is None)\n        # rois aligned\n        rois, cls_scores, bbox_preds = self._refine_roi(\n            x=x,\n            rois=rois,\n            batch_img_metas=batch_img_metas,\n            num_proposals_per_img=num_proposals_per_img,\n            **kwargs)\n\n        # score reweighting in centernet2\n        cls_scores = [(s * ps[:, None])**0.5\n                      for s, ps in zip(cls_scores, proposal_scores)]\n        cls_scores = [\n            s * (s == s[:, :-1].max(dim=1)[0][:, None]).float()\n            for s in cls_scores\n        ]\n\n        # fast_rcnn_inference\n        results_list = self.bbox_head[-1].predict_by_feat(\n            rois=rois,\n            cls_scores=cls_scores,\n            bbox_preds=bbox_preds,\n            batch_img_metas=batch_img_metas,\n            rescale=rescale,\n            rcnn_test_cfg=rcnn_test_cfg)\n        return results_list\n\n    def _mask_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n        \"\"\"Mask head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n        \"\"\"\n        mask_feats = self.mask_roi_extractor(\n            x[:self.mask_roi_extractor.num_inputs], rois)\n        # do not support caffe_c4 model anymore\n        mask_preds = self.mask_head(mask_feats)\n\n        mask_results = dict(mask_preds=mask_preds)\n        return mask_results\n\n    def mask_loss(self, x, sampling_results: List[SamplingResult],\n                  batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Run forward function and calculate loss for mask head in training.\n\n        Args:\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `loss_mask` (dict): A dictionary of mask loss components.\n        \"\"\"\n        pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n        mask_results = self._mask_forward(x, pos_rois)\n\n        mask_loss_and_target = self.mask_head.loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=self.train_cfg[-1])\n        mask_results.update(mask_loss_and_target)\n\n        return mask_results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        raise NotImplementedError\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: List[InstanceData],\n                     rescale: bool = False) -> List[InstanceData]:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas,\n                mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        num_mask_rois_per_img = [len(res) for res in results_list]\n        aug_masks = []\n        mask_results = self._mask_forward(x, mask_rois)\n        mask_preds = mask_results['mask_preds']\n        # split batch mask prediction back to each image\n        mask_preds = mask_preds.split(num_mask_rois_per_img, 0)\n        aug_masks.append([m.sigmoid().detach() for m in mask_preds])\n\n        merged_masks = []\n        for i in range(len(batch_img_metas)):\n            aug_mask = [mask[i] for mask in aug_masks]\n            merged_mask = merge_aug_masks(aug_mask, batch_img_metas[i])\n            merged_masks.append(merged_mask)\n        results_list = self.mask_head.predict_by_feat(\n            mask_preds=merged_masks,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale,\n            activate_map=True)\n        return results_list\n"
  },
  {
    "path": "projects/Detic/detic/text_encoder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Union\n\nimport torch\nimport torch.nn as nn\n\n\nclass CLIPTextEncoder(nn.Module):\n\n    def __init__(self, model_name='ViT-B/32'):\n        super().__init__()\n        import clip\n        from clip.simple_tokenizer import SimpleTokenizer\n        self.tokenizer = SimpleTokenizer()\n        pretrained_model, _ = clip.load(model_name, device='cpu')\n        self.clip = pretrained_model\n\n    @property\n    def device(self):\n        return self.clip.device\n\n    @property\n    def dtype(self):\n        return self.clip.dtype\n\n    def tokenize(self,\n                 texts: Union[str, List[str]],\n                 context_length: int = 77) -> torch.LongTensor:\n        if isinstance(texts, str):\n            texts = [texts]\n\n        sot_token = self.tokenizer.encoder['<|startoftext|>']\n        eot_token = self.tokenizer.encoder['<|endoftext|>']\n        all_tokens = [[sot_token] + self.tokenizer.encode(text) + [eot_token]\n                      for text in texts]\n        result = torch.zeros(len(all_tokens), context_length, dtype=torch.long)\n\n        for i, tokens in enumerate(all_tokens):\n            if len(tokens) > context_length:\n                st = torch.randint(len(tokens) - context_length + 1,\n                                   (1, ))[0].item()\n                tokens = tokens[st:st + context_length]\n            result[i, :len(tokens)] = torch.tensor(tokens)\n\n        return result\n\n    def forward(self, text):\n        text = self.tokenize(text)\n        text_features = self.clip.encode_text(text)\n        return text_features\n"
  },
  {
    "path": "projects/Detic/detic/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.logging import print_log\n\nfrom .text_encoder import CLIPTextEncoder\n\n# download from\n# https://github.com/facebookresearch/Detic/tree/main/datasets/metadata\nDATASET_EMBEDDINGS = {\n    'lvis': 'datasets/metadata/lvis_v1_clip_a+cname.npy',\n    'objects365': 'datasets/metadata/o365_clip_a+cnamefix.npy',\n    'openimages': 'datasets/metadata/oid_clip_a+cname.npy',\n    'coco': 'datasets/metadata/coco_clip_a+cname.npy',\n}\n\n\ndef get_text_embeddings(dataset=None,\n                        custom_vocabulary=None,\n                        prompt_prefix='a '):\n    assert (dataset is None) ^ (custom_vocabulary is None), \\\n        'Either `dataset` or `custom_vocabulary` should be specified.'\n    if dataset:\n        if dataset in DATASET_EMBEDDINGS:\n            return DATASET_EMBEDDINGS[dataset]\n        else:\n            custom_vocabulary = get_class_names(dataset)\n\n    text_encoder = CLIPTextEncoder()\n    text_encoder.eval()\n    texts = [prompt_prefix + x for x in custom_vocabulary]\n    print_log(\n        f'Computing text embeddings for {len(custom_vocabulary)} classes.')\n    embeddings = text_encoder(texts).detach().permute(1, 0).contiguous().cpu()\n    return embeddings\n\n\ndef get_class_names(dataset):\n    if dataset == 'coco':\n        from mmdet.datasets import CocoDataset\n        class_names = CocoDataset.METAINFO['classes']\n    elif dataset == 'cityscapes':\n        from mmdet.datasets import CityscapesDataset\n        class_names = CityscapesDataset.METAINFO['classes']\n    elif dataset == 'voc':\n        from mmdet.datasets import VOCDataset\n        class_names = VOCDataset.METAINFO['classes']\n    elif dataset == 'openimages':\n        from mmdet.datasets import OpenImagesDataset\n        class_names = OpenImagesDataset.METAINFO['classes']\n    elif dataset == 'lvis':\n        from mmdet.datasets import LVISV1Dataset\n        class_names = LVISV1Dataset.METAINFO['classes']\n    else:\n        raise TypeError(f'Invalid type for dataset name: {type(dataset)}')\n    return class_names\n\n\ndef reset_cls_layer_weight(model, weight):\n    if type(weight) == str:\n        print_log(f'Resetting cls_layer_weight from file: {weight}')\n        zs_weight = torch.tensor(\n            np.load(weight),\n            dtype=torch.float32).permute(1, 0).contiguous()  # D x C\n    else:\n        zs_weight = weight\n    zs_weight = torch.cat(\n        [zs_weight, zs_weight.new_zeros(\n            (zs_weight.shape[0], 1))], dim=1)  # D x (C + 1)\n    zs_weight = F.normalize(zs_weight, p=2, dim=0)\n    zs_weight = zs_weight.to('cuda')\n    num_classes = zs_weight.shape[-1]\n\n    for bbox_head in model.roi_head.bbox_head:\n        bbox_head.num_classes = num_classes\n        del bbox_head.fc_cls.zs_weight\n        bbox_head.fc_cls.zs_weight = zs_weight\n"
  },
  {
    "path": "projects/Detic/detic/zero_shot_classifier.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\nimport numpy as np\nimport torch\nfrom torch import nn\nfrom torch.nn import functional as F\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module(force=True)  # avoid bug\nclass ZeroShotClassifier(nn.Module):\n\n    def __init__(\n        self,\n        in_features: int,\n        out_features: int,  # num_classes\n        zs_weight_path: str,\n        zs_weight_dim: int = 512,\n        use_bias: float = 0.0,\n        norm_weight: bool = True,\n        norm_temperature: float = 50.0,\n    ):\n        super().__init__()\n        num_classes = out_features\n        self.norm_weight = norm_weight\n        self.norm_temperature = norm_temperature\n\n        self.use_bias = use_bias < 0\n        if self.use_bias:\n            self.cls_bias = nn.Parameter(torch.ones(1) * use_bias)\n\n        self.linear = nn.Linear(in_features, zs_weight_dim)\n\n        if zs_weight_path == 'rand':\n            zs_weight = torch.randn((zs_weight_dim, num_classes))\n            nn.init.normal_(zs_weight, std=0.01)\n        else:\n            zs_weight = torch.tensor(\n                np.load(zs_weight_path),\n                dtype=torch.float32).permute(1, 0).contiguous()  # D x C\n        zs_weight = torch.cat(\n            [zs_weight, zs_weight.new_zeros(\n                (zs_weight_dim, 1))], dim=1)  # D x (C + 1)\n\n        if self.norm_weight:\n            zs_weight = F.normalize(zs_weight, p=2, dim=0)\n\n        if zs_weight_path == 'rand':\n            self.zs_weight = nn.Parameter(zs_weight)\n        else:\n            self.register_buffer('zs_weight', zs_weight)\n\n        assert self.zs_weight.shape[1] == num_classes + 1, self.zs_weight.shape\n\n    def forward(self, x, classifier=None):\n        '''\n        Inputs:\n            x: B x D'\n            classifier_info: (C', C' x D)\n        '''\n        x = self.linear(x)\n        if classifier is not None:\n            zs_weight = classifier.permute(1, 0).contiguous()  # D x C'\n            zs_weight = F.normalize(zs_weight, p=2, dim=0) \\\n                if self.norm_weight else zs_weight\n        else:\n            zs_weight = self.zs_weight\n        if self.norm_weight:\n            x = self.norm_temperature * F.normalize(x, p=2, dim=1)\n        x = torch.mm(x, zs_weight)\n        if self.use_bias:\n            x = x + self.cls_bias\n        return x\n"
  },
  {
    "path": "projects/Detic_new/README.md",
    "content": "# Detecting Twenty-thousand Classes using Image-level Supervision\n\n## Description\n\n**Detic**: A **Det**ector with **i**mage **c**lasses that can use image-level labels to easily train detectors.\n\n<p align=\"center\"> <img src='https://github.com/facebookresearch/Detic/blob/main/docs/teaser.jpeg?raw=true' align=\"center\" height=\"300px\"> </p>\n\n> [**Detecting Twenty-thousand Classes using Image-level Supervision**](http://arxiv.org/abs/2201.02605),\n> Xingyi Zhou, Rohit Girdhar, Armand Joulin, Philipp Krähenbühl, Ishan Misra,\n> *ECCV 2022 ([arXiv 2201.02605](http://arxiv.org/abs/2201.02605))*\n\n## Usage\n\n<!-- For a typical model, this section should contain the commands for training and testing. You are also suggested to dump your environment specification to env.yml by `conda env export > env.yml`. -->\n\n## Installation\n\nDetic requires to install CLIP.\n\n```shell\npip install git+https://github.com/openai/CLIP.git\n```\n\n## Prepare Datasets\n\nIt is recommended to download and extract the dataset somewhere outside the project directory and symlink the dataset root to `$MMDETECTION/data` as below. If your folder structure is different, you may need to change the corresponding paths in config files.\n\n### LVIS\n\nLVIS dataset is adopted as box-labeled data,  [LVIS](https://www.lvisdataset.org/) is available from official website or mirror.  You need to generate `lvis_v1_train_norare.json` according to the [official prepare datasets](https://github.com/facebookresearch/Detic/blob/main/datasets/README.md#coco-and-lvis) for open-vocabulary LVIS, which removes the labels of 337 rare-class from training. You can also download [lvis_v1_train_norare.json](https://download.openmmlab.com/mmdetection/v3.0/detic/data/lvis/annotations/lvis_v1_train_norare.json) from our backup. The directory should be like this.\n\n```shell\nmmdetection\n├── data\n│   ├── lvis\n│   │   ├── annotations\n│   │   |\t├── lvis_v1_train.json\n│   │   |\t├── lvis_v1_val.json\n│   │   |\t├── lvis_v1_train_norare.json\n│   │   ├── train2017\n│   │   ├── val2017\n```\n\n### ImageNet-LVIS\n\nImageNet-LVIS is adopted as image-labeled data. You can download [ImageNet-21K](https://www.image-net.org/download.php) dataset from the official website.  Then you need to unzip the overlapping classes of LVIS and convert them into LVIS annotation format according to the [official prepare datasets](https://github.com/facebookresearch/Detic/blob/main/datasets/README.md#imagenet-21k). The directory should be like this.\n\n```shell\nmmdetection\n├── data\n│   ├── imagenet\n│   │   ├── annotations\n│   │   |\t├── imagenet_lvis_image_info.json\n│   │   ├── ImageNet-21K\n│   │   |\t├── n00007846\n│   │   |\t├── n01318894\n│   │   |\t├── ...\n```\n\n### Metadata\n\n`data/metadata/` is the preprocessed meta-data (included in the repo). Please follow the [official instruction](https://github.com/facebookresearch/Detic/blob/main/datasets/README.md#metadata) to pre-process the  LVIS dataset. You will generate `lvis_v1_train_cat_info.json` for Federated loss, which contains the frequency of each category of training set of LVIS. In addition, `lvis_v1_clip_a+cname.npy` is the pre-computed CLIP embeddings for each category of LVIS. You can also choose to directly download [lvis_v1_train_cat_info](https://download.openmmlab.com/mmdetection/v3.0/detic/data/metadata/lvis_v1_train_cat_info.json) and [lvis_v1_clip_a+cname.npy](https://download.openmmlab.com/mmdetection/v3.0/detic/data/metadata/lvis_v1_clip_a%2Bcname.npy) form our backup. The directory should be like this.\n\n```shell\nmmdetection\n├── data\n│   ├── metadata\n│   │   ├── lvis_v1_train_cat_info.json\n│   │   ├── lvis_v1_clip_a+cname.npy\n```\n\n## Demo\n\nHere we provide the Detic model for the open vocabulary demo.  This model is trained on combined LVIS-COCO and ImageNet-21K for better demo purposes. LVIS models do not detect persons well due to its federated annotation protocol. LVIS+COCO models give better visual results.\n\n| Backbone |         Training data          |                                Config                                 |                                                                                      Download                                                                                      |\n| :------: | :----------------------------: | :-------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|  Swin-B  | LVIS  &  COCO  &  ImageNet-21K | [config](./configs/detic_centernet2_swin-b_fpn_4x_lvis_coco_in21k.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis-coco-in21k/detic_centernet2_swin-b_fpn_4x_lvis-coco-in21k_20230120-0d301978.pth) |\n\nYou can also download other models from  [official model zoo](https://github.com/facebookresearch/Detic/blob/main/docs/MODEL_ZOO.md), and convert the format by run\n\n```shell\npython tools/model_converters/detic_to_mmdet.py --src /path/to/detic_weight.pth --dst /path/to/mmdet_weight.pth\n```\n\n### Inference with existing dataset vocabulary\n\nYou can detect classes of existing dataset  with `--texts` command:\n\n```shell\npython demo/image_demo.py \\\n  ${IMAGE_PATH} \\\n  ${CONFIG_PATH} \\\n  ${MODEL_PATH} \\\n  --texts lvis \\\n  --pred-score-thr 0.5 \\\n  --palette 'random'\n```\n\n![image](https://user-images.githubusercontent.com/12907710/213624759-f0a2ba0c-0f5c-4424-a350-5ba5349e5842.png)\n\n### Inference with custom vocabularies\n\nDetic can detects any class given class names by using CLIP. You can detect customized classes with `--texts` command:\n\n```shell\npython demo/image_demo.py \\\n  ${IMAGE_PATH} \\\n  ${CONFIG_PATH} \\\n  ${MODEL_PATH} \\\n  --texts 'headphone . webcam . paper . coffe.' \\\n  --pred-score-thr 0.3 \\\n  --palette 'random'\n```\n\n![image](https://user-images.githubusercontent.com/12907710/213624637-e9e8a313-9821-4782-a18a-4408c876852b.png)\n\nNote that `headphone`, `paper` and `coffe` (typo intended) are not LVIS classes. Despite the misspelled class name, Detic can produce a reasonable detection for `coffe`.\n\n## Models and Results\n\n### Training\n\nThere are two stages in the whole training process. The first stage is to train a model using images with box labels as the baseline. The second stage is to finetune from the baseline model and leverage image-labeled data.\n\n#### First stage\n\nTo train the baseline with box-supervised, run\n\n```shell\nbash ./tools/dist_train.sh projects/Detic_new/detic_centernet2_r50_fpn_4x_lvis_boxsup.py 8\n```\n\n|                                         Model (Config)                                          | mask mAP | mask mAP(official) | mask mAP_rare | mask mAP_rare(officical) |\n| :---------------------------------------------------------------------------------------------: | :------: | :----------------: | :-----------: | :----------------------: |\n| [detic_centernet2_r50_fpn_4x_lvis_boxsup](./configs/detic_centernet2_r50_fpn_4x_lvis_boxsup.py) |   31.6   |        31.5        |     26.6      |           25.6           |\n\n#### Second stage\n\nThe second stage uses  both object detection and image classification datasets.\n\n##### Multi-Datasets Config\n\nWe provide improved dataset_wrapper `ConcatDataset` to concatenate multiple datasets, all datasets could have different annotation types and different pipelines (e.g., image_size). You can also obtain the index of `dataset_source` for each sample through ` get_dataset_source` . We provide sampler `MultiDataSampler` to custom the ratios of different datasets. Beside, we provide batch_sampler `MultiDataAspectRatioBatchSampler` to enable different datasets to have different batchsizes.  The config of multiple datasets is as follows:\n\n```python\ndataset_det = dict(\n    type='ClassBalancedDataset',\n    oversample_thr=1e-3,\n    dataset=dict(\n        type='LVISV1Dataset',\n        data_root='data/lvis/',\n        ann_file='annotations/lvis_v1_train.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline_det,\n        backend_args=backend_args))\n\ndataset_cls = dict(\n    type='ImageNetLVISV1Dataset',\n    data_root='data/imagenet',\n    ann_file='annotations/imagenet_lvis_image_info.json',\n    data_prefix=dict(img='ImageNet-LVIS/'),\n    pipeline=train_pipeline_cls,\n    backend_args=backend_args)\n\ntrain_dataloader = dict(\n    batch_size=[8, 32],\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(\n        type='MultiDataSampler',\n        dataset_ratio=[1, 4]),\n    batch_sampler=dict(\n        type='MultiDataAspectRatioBatchSampler',\n        num_datasets=2),\n    dataset=dict(\n        type='ConcatDataset',\n        datasets=[dataset_det, dataset_cls]))\n```\n\n###### Note:\n\n- If the one of the  multiple datasets is `ConcatDataset` , it is still considered as a dataset for `num_datasets` in `MultiDataAspectRatioBatchSampler`.\n\nTo finetune the baseline model with image-labeled data， run:\n\n```shell\nbash ./tools/dist_train.sh projects/Detic_new/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis.py 8\n```\n\n|                                             Model (Config)                                              | mask mAP | mask mAP(official) | mask mAP_rare | mask mAP_rare(officical) |\n| :-----------------------------------------------------------------------------------------------------: | :------: | :----------------: | :-----------: | :----------------------: |\n| [detic_centernet2_r50_fpn_4x_lvis_in21k-lvis](./configs/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis.py) |   32.9   |        33.2        |     30.9      |           29.7           |\n\n#### Standard LVIS Results\n\n|                                                Model (Config)                                                 | mask mAP | mask mAP(official) | mask mAP_rare | mask mAP_rare(officical) |                                                                                                                                                                                     Download                                                                                                                                                                                     |\n| :-----------------------------------------------------------------------------------------------------------: | :------: | :----------------: | :-----------: | :----------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|        [detic_centernet2_r50_fpn_4x_lvis_boxsup](./configs/detic_centernet2_r50_fpn_4x_lvis_boxsup.py)        |   31.6   |        31.5        |     26.6      |           25.6           |               [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis_boxsup/detic_centernet2_r50_fpn_4x_lvis_boxsup_20230911_233514-54116677.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis_boxsup/detic_centernet2_r50_fpn_4x_lvis_boxsup_20230911_233514.log.json)               |\n|    [detic_centernet2_r50_fpn_4x_lvis_in21k-lvis](./configs/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis.py)    |   32.9   |        33.2        |     30.9      |           29.7           |       [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis_20230912_040619-9e7a3258.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis_20230912_040619.log.json)       |\n|     [detic_centernet2_swin-b_fpn_4x_lvis_boxsup](./configs/detic_centernet2_swin-b_fpn_4x_lvis_boxsup.py)     |   40.7   |        40.7        |     38.0      |           35.9           |         [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis_boxsup/detic_centernet2_swin-b_fpn_4x_lvis_boxsup_20230825_061737-328e85f9.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis_boxsup/detic_centernet2_swin-b_fpn_4x_lvis_boxsup_20230825_061737.log.json)         |\n| [detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis](./configs/detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis.py) |   41.7   |        41.7        |     41.7      |           41.7           | [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis/detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis_20230926_235410-0c152391.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis/detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis_20230926_235410.log.json) |\n\n#### Open-vocabulary LVIS Results\n\n|                                                  Model (Config)                                                   | mask mAP | mask mAP(official) | mask mAP_rare | mask mAP_rare(officical) |                                                                                                                                                                                         Download                                                                                                                                                                                         |\n| :---------------------------------------------------------------------------------------------------------------: | :------: | :----------------: | :-----------: | :----------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     [detic_centernet2_r50_fpn_4x_lvis-base_boxsup](./configs/detic_centernet2_r50_fpn_4x_lvis-base_boxsup.py)     |   30.4   |        30.2        |     16.2      |           16.4           |         [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis-base_boxsup/detic_centernet2_r50_fpn_4x_lvis-base_boxsup_20230921_180638-c1685ee2.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis-base_boxsup/detic_centernet2_r50_fpn_4x_lvis-base_boxsup_20230921_180638.log.json)         |\n| [detic_centernet2_r50_fpn_4x_lvis-base_in21k-lvis](./configs/detic_centernet2_r50_fpn_4x_lvis-base_in21k-lvis.py) |   32.6   |        32.4        |     27.4      |           24.9           | [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis-base_in21k-lvis/detic_centernet2_r50_fpn_4x_lvis-base_in21k-lvis_20230925_014315-2d2cc8b7.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_r50_fpn_4x_lvis-base_in21k-lvis/detic_centernet2_r50_fpn_4x_lvis-base_in21k-lvis_20230925_014315.log.json) |\n\n### Testing\n\n#### Test Command\n\nTo evaluate a model with a trained model, run\n\n```shell\npython ./tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE}\n```\n\n#### Open-vocabulary LVIS Results\n\nThe models are converted from the official model zoo.\n\n|                                                     Model (Config)                                                      | mask mAP | mask mAP_novel |                                                                                      Download                                                                                       |\n| :---------------------------------------------------------------------------------------------------------------------: | :------: | :------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|     [detic_centernet2_swin-b_fpn_4x_lvis-base_boxsup](./configs/detic_centernet2_swin-b_fpn_4x_lvis-base_boxsup.py)     |   38.4   |      21.9      |     [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis-base_boxsup/detic_centernet2_swin-b_fpn_4x_lvis-base_boxsup-481281c8.pth)     |\n| [detic_centernet2_swin-b_fpn_4x_lvis-base_in21k-lvis](./configs/detic_centernet2_swin-b_fpn_4x_lvis-base_in21k-lvis.py) |   40.7   |      34.0      | [model](https://download.openmmlab.com/mmdetection/v3.0/detic/detic_centernet2_swin-b_fpn_4x_lvis-base_in21k-lvis/detic_centernet2_swin-b_fpn_4x_lvis-base_in21k-lvis-ec91245d.pth) |\n\n###### Note:\n\n- The open-vocabulary LVIS setup is LVIS without rare class annotations in training, termed `lvisbase`. We evaluate rare classes as novel classes in testing.\n- ` in21k-lvis` denotes that the model use the overlap classes between ImageNet-21K and LVIS as image-labeled data.\n\n## Citation\n\nIf you find Detic is useful in your research or applications, please consider giving a star 🌟 to the [official repository](https://github.com/facebookresearch/Detic) and citing Detic by the following BibTeX entry.\n\n```BibTeX\n@inproceedings{zhou2022detecting,\n  title={Detecting Twenty-thousand Classes using Image-level Supervision},\n  author={Zhou, Xingyi and Girdhar, Rohit and Joulin, Armand and Kr{\\\"a}henb{\\\"u}hl, Philipp and Misra, Ishan},\n  booktitle={ECCV},\n  year={2022}\n}\n```\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_r50_fpn_4x_lvis-base_boxsup.py",
    "content": "_base_ = './detic_centernet2_r50_fpn_4x_lvis_boxsup.py'\n\n# 'lvis_v1_train_norare.json' is the annotations of lvis_v1\n# removing the labels of 337 rare-class\ntrain_dataloader = dict(\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(ann_file='annotations/lvis_v1_train_norare.json')))\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_r50_fpn_4x_lvis-base_in21k-lvis.py",
    "content": "_base_ = './detic_centernet2_r50_fpn_4x_lvis_boxsup.py'\ndataset_type = ['LVISV1Dataset', 'ImageNetLVISV1Dataset']\nimage_size_det = (640, 640)\nimage_size_cls = (320, 320)\n\n# backend = 'pillow'\nbackend_args = None\n\ntrain_pipeline_det = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_det,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_det,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_cls = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=False, with_label=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_cls,\n        ratio_range=(0.5, 1.5),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_cls,\n        recompute_bbox=False,\n        bbox_clip_border=False,\n        allow_negative_crop=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\n# 'lvis_v1_train_norare.json' is the annotations of lvis_v1\n# removing the labels of 337 rare-class\ndataset_det = dict(\n    type='ClassBalancedDataset',\n    oversample_thr=1e-3,\n    dataset=dict(\n        type='LVISV1Dataset',\n        data_root='data/lvis/',\n        ann_file='annotations/lvis_v1_train_norare.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline_det,\n        backend_args=backend_args))\n\ndataset_cls = dict(\n    type='ImageNetLVISV1Dataset',\n    data_root='data/imagenet',\n    ann_file='annotations/imagenet_lvis_image_info.json',\n    data_prefix=dict(img='ImageNet-LVIS/'),\n    pipeline=train_pipeline_cls,\n    backend_args=backend_args)\n\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=[8, 32],\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='MultiDataSampler', dataset_ratio=[1, 4]),\n    batch_sampler=dict(\n        type='MultiDataAspectRatioBatchSampler', num_datasets=2),\n    dataset=dict(type='ConcatDataset', datasets=[dataset_det, dataset_cls]))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='CosineAnnealingLR',\n        begin=0,\n        by_epoch=False,\n        T_max=90000,\n    )\n]\n\nload_from = './first_stage/detic_centernet2_r50_fpn_4x_lvis-base_boxsup.pth'\n\nfind_unused_parameters = True\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_r50_fpn_4x_lvis_boxsup.py",
    "content": "_base_ = 'mmdet::_base_/default_runtime.py'\ndataset_type = 'LVISV1Dataset'\ncustom_imports = dict(\n    imports=['projects.Detic_new.detic'], allow_failed_imports=False)\n\nnum_classes = 1203\nlvis_cat_frequency_info = 'data/metadata/lvis_v1_train_cat_info.json'\n\n# 'data/metadata/lvis_v1_clip_a+cname.npy' is pre-computed\n# CLIP embeddings for each category\ncls_layer = dict(\n    type='ZeroShotClassifier',\n    zs_weight_path='data/metadata/lvis_v1_clip_a+cname.npy',\n    zs_weight_dim=512,\n    use_bias=0.0,\n    norm_weight=True,\n    norm_temperature=50.0)\nreg_layer = [\n    dict(type='Linear', in_features=1024, out_features=1024),\n    dict(type='ReLU', inplace=True),\n    dict(type='Linear', in_features=1024, out_features=4)\n]\n\nmodel = dict(\n    type='Detic',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='https://miil-public-eu.oss-eu-central-1.aliyuncs.com/'\n            'model-zoo/ImageNet_21K_P/models/resnet50_miil_21k.pth')),\n    neck=dict(\n        type='FPN',\n        in_channels=[512, 1024, 2048],\n        out_channels=256,\n        start_level=0,\n        add_extra_convs='on_output',\n        num_outs=5,\n        init_cfg=dict(type='Caffe2Xavier', layer='Conv2d'),\n        relu_before_extra_convs=True),\n    rpn_head=dict(\n        type='CenterNetRPNHead',\n        num_classes=1,\n        in_channels=256,\n        stacked_convs=4,\n        feat_channels=256,\n        strides=[8, 16, 32, 64, 128],\n        conv_bias=True,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True),\n        loss_cls=dict(\n            type='HeatmapFocalLoss',\n            alpha=0.25,\n            beta=4.0,\n            gamma=2.0,\n            pos_weight=0.5,\n            neg_weight=0.5,\n            loss_weight=1.0,\n            ignore_high_fp=0.85,\n        ),\n        loss_bbox=dict(type='GIoULoss', eps=1e-6, loss_weight=1.0),\n    ),\n    roi_head=dict(\n        type='DeticRoIHead',\n        num_stages=3,\n        stage_loss_weights=[1.0, 1.0, 1.0],\n        bbox_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(\n                type='RoIAlign',\n                output_size=7,\n                sampling_ratio=0,\n                use_torchvision=True),\n            out_channels=256,\n            featmap_strides=[8, 16, 32],\n            # approximately equal to\n            # canonical_box_size=224, canonical_level=4 in D2\n            finest_scale=112),\n        bbox_head=[\n            dict(\n                type='DeticBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=num_classes,\n                cls_predictor_cfg=cls_layer,\n                reg_predictor_cfg=reg_layer,\n                use_fed_loss=True,\n                cat_freq_path=lvis_cat_frequency_info,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss', use_sigmoid=True,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0)),\n            dict(\n                type='DeticBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=num_classes,\n                cls_predictor_cfg=cls_layer,\n                reg_predictor_cfg=reg_layer,\n                use_fed_loss=True,\n                cat_freq_path=lvis_cat_frequency_info,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.05, 0.05, 0.1, 0.1]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss', use_sigmoid=True,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=0.1,\n                               loss_weight=1.0)),\n            dict(\n                type='DeticBBoxHead',\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=num_classes,\n                cls_predictor_cfg=cls_layer,\n                reg_predictor_cfg=reg_layer,\n                use_fed_loss=True,\n                cat_freq_path=lvis_cat_frequency_info,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.033, 0.033, 0.067, 0.067]),\n                reg_class_agnostic=True,\n                loss_cls=dict(\n                    type='CrossEntropyLoss', use_sigmoid=True,\n                    loss_weight=1.0),\n                loss_bbox=dict(type='SmoothL1Loss', beta=0.1, loss_weight=1.0))\n        ],\n        mask_roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),\n            out_channels=256,\n            featmap_strides=[8, 16, 32],\n            # approximately equal to\n            # canonical_box_size=224, canonical_level=4 in D2\n            finest_scale=112),\n        mask_head=dict(\n            type='FCNMaskHead',\n            num_convs=4,\n            in_channels=256,\n            conv_out_channels=256,\n            class_agnostic=True,\n            num_classes=num_classes,\n            loss_mask=dict(\n                type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),\n    # model training and testing settings\n    train_cfg=dict(\n        rpn=dict(\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=0,\n            pos_weight=-1,\n            debug=False),\n        rpn_proposal=dict(\n            score_thr=0.0001,\n            nms_pre=4000,\n            max_per_img=2000,\n            nms=dict(type='nms', iou_threshold=0.9),\n            min_bbox_size=0),\n        rcnn=[\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.6,\n                    neg_iou_thr=0.6,\n                    min_pos_iou=0.6,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=True),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.7,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.8,\n                    neg_iou_thr=0.8,\n                    min_pos_iou=0.8,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=512,\n                    pos_fraction=0.25,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                mask_size=28,\n                pos_weight=-1,\n                debug=False)\n        ]),\n    test_cfg=dict(\n        rpn=dict(\n            score_thr=0.0001,\n            nms_pre=1000,\n            max_per_img=256,\n            nms=dict(type='nms', iou_threshold=0.9),\n            min_bbox_size=0),\n        rcnn=dict(\n            score_thr=0.02,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=300,\n            mask_thr_binary=0.5)))\n\n# backend = 'pillow'\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=(640, 640),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=(640, 640),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=backend_args,\n        imdecode_backend=backend_args),\n    dict(\n        type='Resize',\n        scale=(1333, 800),\n        keep_ratio=True,\n        backend=backend_args),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'custom_entities'))\n]\n\nval_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=backend_args,\n        imdecode_backend=backend_args),\n    dict(\n        type='Resize',\n        scale=(1333, 800),\n        keep_ratio=True,\n        backend=backend_args),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    batch_sampler=dict(type='AspectRatioBatchSampler'),\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(\n            type='LVISV1Dataset',\n            data_root='data/lvis/',\n            ann_file='annotations/lvis_v1_train_norare.json',\n            data_prefix=dict(img=''),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=train_pipeline,\n            backend_args=backend_args)))\n\nval_dataloader = dict(\n    batch_size=8,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    pin_memory=True,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type='LVISV1Dataset',\n        data_root='data/lvis/',\n        ann_file='annotations/lvis_v1_val.json',\n        data_prefix=dict(img=''),\n        pipeline=val_pipeline,\n        return_classes=False))\n\ntest_dataloader = dict(\n    batch_size=8,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    pin_memory=True,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type='LVISV1Dataset',\n        data_root='data/lvis/',\n        ann_file='annotations/lvis_v1_val.json',\n        data_prefix=dict(img=''),\n        pipeline=test_pipeline,\n        return_classes=True))\n\nval_evaluator = dict(\n    type='LVISMetric',\n    ann_file='data/lvis/annotations/lvis_v1_val.json',\n    metric=['bbox', 'segm'])\ntest_evaluator = val_evaluator\n\n# training schedule for 90k with batch_size of 64\n# with total batch_size of 16, 90k iters is equivalent to '1x' (12 epochs)\n# with total batch_size of 64, 90k iters is equivalent to '4x'\nmax_iter = 90000\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iter, val_interval=90000)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0002, weight_decay=0.0001),\n    paramwise_cfg=dict(norm_decay_mult=0.),\n    clip_grad=dict(max_norm=1.0, norm_type=2))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=10000),\n    dict(\n        type='CosineAnnealingLR',\n        begin=0,\n        by_epoch=False,\n        T_max=max_iter,\n    )\n]\n\n# only keep latest 5 checkpoints\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=False, interval=30000, max_keep_ckpts=5),\n    logger=dict(type='LoggerHook', interval=50))\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_r50_fpn_4x_lvis_in21k-lvis.py",
    "content": "_base_ = './detic_centernet2_r50_fpn_4x_lvis_boxsup.py'\ndataset_type = ['LVISV1Dataset', 'ImageNetLVISV1Dataset']\nimage_size_det = (640, 640)\nimage_size_cls = (320, 320)\n\n# backend = 'pillow'\nbackend_args = None\n\ntrain_pipeline_det = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_det,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_det,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_cls = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=False, with_label=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_cls,\n        ratio_range=(0.5, 1.5),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_cls,\n        recompute_bbox=False,\n        bbox_clip_border=False,\n        allow_negative_crop=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ndataset_det = dict(\n    type='ClassBalancedDataset',\n    oversample_thr=1e-3,\n    dataset=dict(\n        type='LVISV1Dataset',\n        data_root='data/lvis/',\n        ann_file='annotations/lvis_v1_train.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline_det,\n        backend_args=backend_args))\n\ndataset_cls = dict(\n    type='ImageNetLVISV1Dataset',\n    data_root='data/imagenet',\n    ann_file='annotations/imagenet_lvis_image_info.json',\n    data_prefix=dict(img='ImageNet-LVIS/'),\n    pipeline=train_pipeline_cls,\n    backend_args=backend_args)\n\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=[8, 32],\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='MultiDataSampler', dataset_ratio=[1, 4]),\n    batch_sampler=dict(\n        type='MultiDataAspectRatioBatchSampler', num_datasets=2),\n    dataset=dict(type='ConcatDataset', datasets=[dataset_det, dataset_cls]))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='CosineAnnealingLR',\n        begin=0,\n        by_epoch=False,\n        T_max=90000,\n    )\n]\n\nload_from = './first_stage/detic_centernet2_r50_fpn_4x_lvis_boxsup.pth'\n\nfind_unused_parameters = True\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_swin-b_fpn_4x_lvis-base_boxsup.py",
    "content": "_base_ = './detic_centernet2_swin-b_fpn_4x_lvis_boxsup.py'\n\n# 'lvis_v1_train_norare.json' is the annotations of lvis_v1\n# removing the labels of 337 rare-class\ntrain_dataloader = dict(\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(ann_file='annotations/lvis_v1_train_norare.json')))\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_swin-b_fpn_4x_lvis-base_in21k-lvis.py",
    "content": "_base_ = './detic_centernet2_r50_fpn_4x_lvis_in21k-lvis.py'\n\nimage_size_det = (896, 896)\nimage_size_cls = (448, 448)\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False),\n    neck=dict(in_channels=[256, 512, 1024]))\n\nbackend_args = None\ntrain_pipeline_det = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_det,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_det,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_cls = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=False, with_label=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_cls,\n        ratio_range=(0.5, 1.5),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_cls,\n        recompute_bbox=False,\n        bbox_clip_border=False,\n        allow_negative_crop=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\n# 'lvis_v1_train_norare.json' is the annotations of lvis_v1\n# removing the labels of 337 rare-class\ndataset_det = dict(\n    type='ClassBalancedDataset',\n    oversample_thr=1e-3,\n    dataset=dict(\n        type='LVISV1Dataset',\n        data_root='data/lvis/',\n        ann_file='annotations/lvis_v1_train_norare.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline_det,\n        backend_args=backend_args))\n\ndataset_cls = dict(\n    type='ImageNetLVISV1Dataset',\n    data_root='data/imagenet',\n    ann_file='annotations/imagenet_lvis_image_info.json',\n    data_prefix=dict(img='ImageNet-LVIS/'),\n    pipeline=train_pipeline_cls,\n    backend_args=backend_args)\n\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=[4, 16],\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='MultiDataSampler', dataset_ratio=[1, 4]),\n    batch_sampler=dict(\n        type='MultiDataAspectRatioBatchSampler', num_datasets=2),\n    dataset=dict(type='ConcatDataset', datasets=[dataset_det, dataset_cls]))\n\n# training schedule for 180k\nmax_iter = 180000\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iter, val_interval=180000)\n\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='CosineAnnealingLR',\n        begin=0,\n        by_epoch=False,\n        T_max=max_iter,\n    )\n]\n\nload_from = './first_stage/detic_centernet2_swin-b_fpn_4x_lvis-base_boxsup.pth'\nfind_unused_parameters = True\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_swin-b_fpn_4x_lvis_boxsup.py",
    "content": "_base_ = './detic_centernet2_r50_fpn_4x_lvis_boxsup.py'\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False,\n        convert_weights=True,\n        init_cfg=dict(\n            type='Pretrained',\n            checkpoint='https://github.com/SwinTransformer/storage/releases/'\n            'download/v1.0.0/swin_base_patch4_window7_224_22k.pth')),\n    neck=dict(in_channels=[256, 512, 1024]))\n\n# backend = 'pillow'\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=(896, 896),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=(896, 896),\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_dataloader = dict(\n    dataset=dict(\n        type='ClassBalancedDataset',\n        oversample_thr=1e-3,\n        dataset=dict(pipeline=train_pipeline)))\n\n# training schedule for 180k\nmax_iter = 180000\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iter, val_interval=180000)\n\n# Enable automatic-mixed-precision training with AmpOptimWrapper.\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR',\n        start_factor=0.0001,\n        by_epoch=False,\n        begin=0,\n        end=10000),\n    dict(\n        type='CosineAnnealingLR',\n        begin=0,\n        by_epoch=False,\n        T_max=max_iter,\n    )\n]\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_swin-b_fpn_4x_lvis_coco_in21k.py",
    "content": "# not support training, only for testing\n_base_ = './detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis.py'\n"
  },
  {
    "path": "projects/Detic_new/configs/detic_centernet2_swin-b_fpn_4x_lvis_in21k-lvis.py",
    "content": "_base_ = './detic_centernet2_r50_fpn_4x_lvis_in21k-lvis.py'\n\nimage_size_det = (896, 896)\nimage_size_cls = (448, 448)\n\nmodel = dict(\n    backbone=dict(\n        _delete_=True,\n        type='SwinTransformer',\n        embed_dims=128,\n        depths=[2, 2, 18, 2],\n        num_heads=[4, 8, 16, 32],\n        window_size=7,\n        mlp_ratio=4,\n        qkv_bias=True,\n        qk_scale=None,\n        drop_rate=0.,\n        attn_drop_rate=0.,\n        drop_path_rate=0.3,\n        patch_norm=True,\n        out_indices=(1, 2, 3),\n        with_cp=False),\n    neck=dict(in_channels=[256, 512, 1024]))\n\nbackend_args = None\ntrain_pipeline_det = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_det,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_det,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntrain_pipeline_cls = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=False, with_label=True),\n    dict(\n        type='RandomResize',\n        scale=image_size_cls,\n        ratio_range=(0.5, 1.5),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size_cls,\n        recompute_bbox=False,\n        bbox_clip_border=False,\n        allow_negative_crop=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ndataset_det = dict(\n    type='ClassBalancedDataset',\n    oversample_thr=1e-3,\n    dataset=dict(\n        type='LVISV1Dataset',\n        data_root='data/lvis/',\n        ann_file='annotations/lvis_v1_train.json',\n        data_prefix=dict(img=''),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline_det,\n        backend_args=backend_args))\n\ndataset_cls = dict(\n    type='ImageNetLVISV1Dataset',\n    data_root='data/imagenet',\n    ann_file='annotations/imagenet_lvis_image_info.json',\n    data_prefix=dict(img='ImageNet-LVIS/'),\n    pipeline=train_pipeline_cls,\n    backend_args=backend_args)\n\ntrain_dataloader = dict(\n    _delete_=True,\n    batch_size=[4, 16],\n    num_workers=2,\n    persistent_workers=True,\n    sampler=dict(type='MultiDataSampler', dataset_ratio=[1, 4]),\n    batch_sampler=dict(\n        type='MultiDataAspectRatioBatchSampler', num_datasets=2),\n    dataset=dict(type='ConcatDataset', datasets=[dataset_det, dataset_cls]))\n\n# training schedule for 180k\nmax_iter = 180000\ntrain_cfg = dict(\n    type='IterBasedTrainLoop', max_iters=max_iter, val_interval=180000)\n\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0001))\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0,\n        end=1000),\n    dict(\n        type='CosineAnnealingLR',\n        begin=0,\n        by_epoch=False,\n        T_max=max_iter,\n    )\n]\n\nload_from = './first_stage/detic_centernet2_swin-b_fpn_4x_lvis_boxsup.pth'\nfind_unused_parameters = True\n"
  },
  {
    "path": "projects/Detic_new/detic/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .centernet_rpn_head import CenterNetRPNHead\nfrom .detic import Detic\nfrom .detic_bbox_head import DeticBBoxHead\nfrom .detic_roi_head import DeticRoIHead\nfrom .heatmap_focal_loss import HeatmapFocalLoss\nfrom .imagenet_lvis import ImageNetLVISV1Dataset\nfrom .zero_shot_classifier import ZeroShotClassifier\n\n__all__ = [\n    'CenterNetRPNHead', 'Detic', 'DeticBBoxHead', 'DeticRoIHead',\n    'ZeroShotClassifier', 'HeatmapFocalLoss', 'ImageNetLVISV1Dataset'\n]\n"
  },
  {
    "path": "projects/Detic_new/detic/centernet_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import Dict, List, Optional, Sequence, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import Scale\nfrom mmengine import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.dense_heads import CenterNetUpdateHead\nfrom mmdet.models.utils import unpack_gt_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2distance\nfrom mmdet.utils import (ConfigType, InstanceList, OptConfigType,\n                         OptInstanceList, reduce_mean)\nfrom .iou_loss import IOULoss\n\n# from .heatmap_focal_loss import binary_heatmap_focal_loss_jit\nINF = 1000000000\nRangeType = Sequence[Tuple[int, int]]\n\n\n@MODELS.register_module()\nclass CenterNetRPNHead(CenterNetUpdateHead):\n    \"\"\"CenterNetUpdateHead is an improved version of CenterNet in CenterNet2.\n\n    Paper link `<https://arxiv.org/abs/2103.07461>`_.\n    Args:\n        num_classes (int): Number of categories excluding the background\n            category.\n        in_channels (int): Number of channel in the input feature map.\n        regress_ranges (Sequence[Tuple[int, int]]): Regress range of multiple\n            level points.\n        hm_min_radius (int): Heatmap target minimum radius of cls branch.\n            Defaults to 4.\n        hm_min_overlap (float): Heatmap target minimum overlap of cls branch.\n            Defaults to 0.8.\n        more_pos_thresh (float): The filtering threshold when the cls branch\n            adds more positive samples. Defaults to 0.2.\n        more_pos_topk (int): The maximum number of additional positive samples\n            added to each gt. Defaults to 9.\n        soft_weight_on_reg (bool): Whether to use the soft target of the\n            cls branch as the soft weight of the bbox branch.\n            Defaults to False.\n        loss_cls (:obj:`ConfigDict` or dict): Config of cls loss. Defaults to\n            dict(type='GaussianFocalLoss', loss_weight=1.0)\n        loss_bbox (:obj:`ConfigDict` or dict): Config of bbox loss. Defaults to\n             dict(type='GIoULoss', loss_weight=2.0).\n        norm_cfg (:obj:`ConfigDict` or dict, optional): dictionary to construct\n            and config norm layer.  Defaults to\n            ``norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)``.\n        train_cfg (:obj:`ConfigDict` or dict, optional): Training config.\n            Unused in CenterNet. Reserved for compatibility with\n            SingleStageDetector.\n        test_cfg (:obj:`ConfigDict` or dict, optional): Testing config\n            of CenterNet.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 in_channels: int,\n                 regress_ranges: RangeType = ((0, 80), (64, 160), (128, 320),\n                                              (256, 640), (512, INF)),\n                 hm_min_radius: int = 4,\n                 hm_min_overlap: float = 0.8,\n                 more_pos: bool = False,\n                 more_pos_thresh: float = 0.2,\n                 more_pos_topk: int = 9,\n                 soft_weight_on_reg: bool = False,\n                 not_clamp_box: bool = False,\n                 loss_cls: ConfigType = dict(\n                     type='HeatmapFocalLoss',\n                     alpha=0.25,\n                     beta=4.0,\n                     gamma=2.0,\n                     pos_weight=1.0,\n                     neg_weight=1.0,\n                     sigmoid_clamp=1e-4,\n                     ignore_high_fp=-1.0,\n                     loss_weight=1.0,\n                 ),\n                 loss_bbox: ConfigType = dict(\n                     type='GIoULoss', loss_weight=2.0),\n                 norm_cfg: OptConfigType = dict(\n                     type='GN', num_groups=32, requires_grad=True),\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 **kwargs) -> None:\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            # loss_bbox=loss_bbox,\n            loss_cls=loss_cls,\n            norm_cfg=norm_cfg,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            **kwargs)\n        self.soft_weight_on_reg = soft_weight_on_reg\n        self.hm_min_radius = hm_min_radius\n        self.more_pos_thresh = more_pos_thresh\n        self.more_pos_topk = more_pos_topk\n        self.more_pos = more_pos\n        self.not_clamp_box = not_clamp_box\n        self.delta = (1 - hm_min_overlap) / (1 + hm_min_overlap)\n        self.loss_bbox = IOULoss('giou')\n\n        # GaussianFocalLoss must be sigmoid mode\n        self.use_sigmoid_cls = True\n        self.cls_out_channels = num_classes\n\n        self.regress_ranges = regress_ranges\n        self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides])\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self._init_reg_convs()\n        self._init_predictor()\n\n    def forward_single(self, x: Tensor, scale: Scale,\n                       stride: int) -> Tuple[Tensor, Tensor]:\n        \"\"\"Forward features of a single scale level.\n\n        Args:\n            x (Tensor): FPN feature maps of the specified stride.\n            scale (:obj:`mmcv.cnn.Scale`): Learnable scale module to resize\n                the bbox prediction.\n            stride (int): The corresponding stride for feature maps.\n\n        Returns:\n            tuple: scores for each class, bbox predictions of\n            input feature maps.\n        \"\"\"\n        for m in self.reg_convs:\n            x = m(x)\n        cls_score = self.conv_cls(x)\n        bbox_pred = self.conv_reg(x)\n        # scale the bbox_pred of different level\n        # float to avoid overflow when enabling FP16\n        bbox_pred = scale(bbox_pred).float()\n        # bbox_pred needed for gradient computation has been modified\n        # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace\n        # F.relu(bbox_pred) with bbox_pred.clamp(min=0)\n        bbox_pred = bbox_pred.clamp(min=0)\n        return cls_score, bbox_pred  # score aligned, box larger\n\n    def loss_by_feat(\n        self,\n        cls_scores: List[Tensor],\n        bbox_preds: List[Tensor],\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level,\n                each is a 4D-tensor, the channel number is num_classes.\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level, each is a 4D-tensor, the channel number is 4.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance.  It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n\n        num_imgs = cls_scores[0].size(0)\n        assert len(cls_scores) == len(bbox_preds)\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        all_level_points = self.prior_generator.grid_priors(\n            featmap_sizes,\n            dtype=bbox_preds[0].dtype,\n            device=bbox_preds[0].device)\n\n        # 1 flatten outputs\n        flatten_cls_scores = [\n            cls_score.permute(0, 2, 3, 1).reshape(-1, self.cls_out_channels)\n            for cls_score in cls_scores\n        ]\n        flatten_bbox_preds = [\n            bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)\n            for bbox_pred in bbox_preds\n        ]\n        flatten_cls_scores = torch.cat(flatten_cls_scores)\n        flatten_bbox_preds = torch.cat(flatten_bbox_preds)\n\n        # repeat points to align with bbox_preds\n        flatten_points = torch.cat(\n            [points.repeat(num_imgs, 1) for points in all_level_points])\n\n        assert (torch.isfinite(flatten_bbox_preds).all().item())\n\n        # 2 calc reg and cls branch targets\n        cls_targets, bbox_targets = self.get_targets(all_level_points,\n                                                     batch_gt_instances)\n\n        # 3 pos index for cls branch\n        featmap_sizes = flatten_points.new_tensor(featmap_sizes)\n\n        if self.more_pos:\n            pos_inds, cls_labels = self.add_cls_pos_inds(\n                flatten_points, flatten_bbox_preds, featmap_sizes,\n                batch_gt_instances)\n        else:\n            pos_inds = self._get_label_inds(batch_gt_instances,\n                                            batch_img_metas, featmap_sizes)\n\n        # 4 calc cls loss\n        if pos_inds is None:\n            # num_gts=0\n            num_pos_cls = bbox_preds[0].new_tensor(0, dtype=torch.float)\n        else:\n            num_pos_cls = bbox_preds[0].new_tensor(\n                len(pos_inds), dtype=torch.float)\n        num_pos_cls = max(reduce_mean(num_pos_cls), 1.0)\n\n        cat_agn_cls_targets = cls_targets.max(dim=1)[0]  # M\n\n        cls_pos_loss, cls_neg_loss = self.loss_cls(\n            flatten_cls_scores.squeeze(1), cat_agn_cls_targets, pos_inds,\n            num_pos_cls)\n\n        # 5 calc reg loss\n        pos_bbox_inds = torch.nonzero(\n            bbox_targets.max(dim=1)[0] >= 0).squeeze(1)\n        pos_bbox_preds = flatten_bbox_preds[pos_bbox_inds]\n        pos_bbox_targets = bbox_targets[pos_bbox_inds]\n\n        bbox_weight_map = cls_targets.max(dim=1)[0]\n        bbox_weight_map = bbox_weight_map[pos_bbox_inds]\n        bbox_weight_map = bbox_weight_map if self.soft_weight_on_reg \\\n            else torch.ones_like(bbox_weight_map)\n\n        num_pos_bbox = max(reduce_mean(bbox_weight_map.sum()), 1.0)\n\n        if len(pos_bbox_inds) > 0:\n            bbox_loss = self.loss_bbox(\n                pos_bbox_preds,\n                pos_bbox_targets,\n                bbox_weight_map,\n                reduction='sum') / num_pos_bbox\n        else:\n            bbox_loss = flatten_bbox_preds.sum() * 0\n\n        return dict(\n            loss_bbox=bbox_loss,\n            loss_cls_pos=cls_pos_loss,\n            loss_cls_neg=cls_neg_loss)\n\n    def loss_and_predict(\n        self,\n        x: Tuple[Tensor],\n        batch_data_samples: SampleList,\n        proposal_cfg: Optional[ConfigDict] = None\n    ) -> Tuple[dict, InstanceList]:\n        \"\"\"Perform forward propagation of the head, then calculate loss and\n        predictions from the features and data samples.\n\n        Args:\n            x (tuple[Tensor]): Features from FPN.\n            batch_data_samples (list[:obj:`DetDataSample`]): Each item contains\n                the meta information of each image and corresponding\n                annotations.\n            proposal_cfg (ConfigDict, optional): Test / postprocessing\n                configuration, if None, test_cfg would be used.\n                Defaults to None.\n\n        Returns:\n            tuple: the return value is a tuple contains:\n\n                - losses: (dict[str, Tensor]): A dictionary of loss components.\n                - predictions (list[:obj:`InstanceData`]): Detection\n                  results of each image after the post process.\n        \"\"\"\n        outputs = unpack_gt_instances(batch_data_samples)\n        (batch_gt_instances, batch_gt_instances_ignore,\n         batch_img_metas) = outputs\n\n        outs = self(x)\n\n        loss_inputs = outs + (batch_gt_instances, batch_img_metas,\n                              batch_gt_instances_ignore)\n        losses = self.loss_by_feat(*loss_inputs)\n        predictions = self.predict_by_feat(\n            *outs, batch_img_metas=batch_img_metas, cfg=proposal_cfg)\n        return losses, predictions\n\n    def _predict_by_feat_single(self,\n                                cls_score_list: List[Tensor],\n                                bbox_pred_list: List[Tensor],\n                                score_factor_list: List[Tensor],\n                                mlvl_priors: List[Tensor],\n                                img_meta: dict,\n                                cfg: ConfigDict,\n                                rescale: bool = False,\n                                with_nms: bool = True) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            cls_score_list (list[Tensor]): Box scores from all scale\n                levels of a single image, each item has shape\n                (num_priors * num_classes, H, W).\n            bbox_pred_list (list[Tensor]): Box energies / deltas from\n                all scale levels of a single image, each item has shape\n                (num_priors * 4, H, W).\n            score_factor_list (list[Tensor]): Score factor from all scale\n                levels of a single image, each item has shape\n                (num_priors * 1, H, W).\n            mlvl_priors (list[Tensor]): Each element in the list is\n                the priors of a single level in feature pyramid. In all\n                anchor-based methods, it has shape (num_priors, 4). In\n                all anchor-free methods, it has shape (num_priors, 2)\n                when `with_stride=True`, otherwise it still has shape\n                (num_priors, 4).\n            img_meta (dict): Image meta info.\n            cfg (mmengine.Config): Test / postprocessing configuration,\n                if None, test_cfg would be used.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            with_nms (bool): If True, do nms before return boxes.\n                Defaults to True.\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n        nms_pre = cfg.get('nms_pre', -1)\n\n        mlvl_bbox_preds = []\n        mlvl_valid_priors = []\n        mlvl_scores = []\n        mlvl_labels = []\n\n        for level_idx, (cls_score, bbox_pred, score_factor, priors) in \\\n                enumerate(zip(cls_score_list, bbox_pred_list,\n                              score_factor_list, mlvl_priors)):\n\n            assert cls_score.size()[-2:] == bbox_pred.size()[-2:]\n\n            bbox_pred = bbox_pred * self.strides[level_idx]\n\n            dim = self.bbox_coder.encode_size\n            bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, dim)\n            cls_score = cls_score.permute(1, 2,\n                                          0).reshape(-1, self.cls_out_channels)\n            heatmap = cls_score.sigmoid()\n            score_thr = cfg.get('score_thr', 0)\n\n            candidate_inds = heatmap > score_thr  # 0.05\n            pre_nms_top_n = candidate_inds.sum()  # N\n            pre_nms_top_n = pre_nms_top_n.clamp(max=nms_pre)  # N\n\n            heatmap = heatmap[candidate_inds]  # n\n\n            candidate_nonzeros = candidate_inds.nonzero()  # n\n            box_loc = candidate_nonzeros[:, 0]  # n\n            labels = candidate_nonzeros[:, 1]  # n\n\n            bbox_pred = bbox_pred[box_loc]  # n x 4\n            per_grids = priors[box_loc]  # n x 2\n\n            if candidate_inds.sum().item() > pre_nms_top_n.item():\n                heatmap, top_k_indices = \\\n                    heatmap.topk(pre_nms_top_n, sorted=False)\n                labels = labels[top_k_indices]\n                bbox_pred = bbox_pred[top_k_indices]\n                per_grids = per_grids[top_k_indices]\n\n            bboxes = torch.stack([\n                per_grids[:, 0] - bbox_pred[:, 0],\n                per_grids[:, 1] - bbox_pred[:, 1],\n                per_grids[:, 0] + bbox_pred[:, 2],\n                per_grids[:, 1] + bbox_pred[:, 3],\n            ],\n                                 dim=1)  # n x 4\n\n            # avoid invalid boxes in RoI heads\n            bboxes[:, 2] = torch.max(bboxes[:, 2], bboxes[:, 0] + 0.01)\n            bboxes[:, 3] = torch.max(bboxes[:, 3], bboxes[:, 1] + 0.01)\n\n            # bboxes = self.bbox_coder.decode(per_grids, bbox_pred)\n            # # avoid invalid boxes in RoI heads\n            # bboxes[:, 2] = torch.max(bboxes[:, 2], bboxes[:, 0] + 0.01)\n            # bboxes[:, 3] = torch.max(bboxes[:, 3], bboxes[:, 1] + 0.01)\n\n            mlvl_bbox_preds.append(bboxes)\n            mlvl_valid_priors.append(priors)\n            mlvl_scores.append(torch.sqrt(heatmap))\n            mlvl_labels.append(labels)\n\n        results = InstanceData()\n        results.bboxes = torch.cat(mlvl_bbox_preds)\n        results.scores = torch.cat(mlvl_scores)\n        results.labels = torch.cat(mlvl_labels)\n\n        return self._bbox_post_process(\n            results=results,\n            cfg=cfg,\n            rescale=rescale,\n            with_nms=with_nms,\n            img_meta=img_meta)\n\n    def _get_label_inds(self, batch_gt_instances, batch_img_metas,\n                        shapes_per_level):\n        '''\n        Inputs:\n            batch_gt_instances: [n_i], sum n_i = N\n            shapes_per_level: L x 2 [(h_l, w_l)]_L\n        Returns:\n            pos_inds: N'\n            labels: N'\n        '''\n        pos_inds = []\n        L = len(self.strides)\n        B = len(batch_gt_instances)\n        shapes_per_level = shapes_per_level.long()\n        loc_per_level = (shapes_per_level[:, 0] *\n                         shapes_per_level[:, 1]).long()  # L\n        level_bases = []\n        s = 0\n        for i in range(L):\n            level_bases.append(s)\n            s = s + B * loc_per_level[i]\n        level_bases = shapes_per_level.new_tensor(level_bases).long()  # L\n        strides_default = shapes_per_level.new_tensor(\n            self.strides).float()  # L\n        for im_i in range(B):\n            targets_per_im = batch_gt_instances[im_i]\n            if hasattr(targets_per_im, 'bboxes'):\n                bboxes = targets_per_im.bboxes  # n x 4\n            else:\n                bboxes = targets_per_im.labels.new_tensor(\n                    [], dtype=torch.float).reshape(-1, 4)\n            n = bboxes.shape[0]\n            centers = ((bboxes[:, [0, 1]] + bboxes[:, [2, 3]]) / 2)  # n x 2\n            centers = centers.view(n, 1, 2).expand(n, L, 2).contiguous()\n            if self.not_clamp_box:\n                h, w = batch_img_metas[im_i]._image_size\n                centers[:, :, 0].clamp_(min=0).clamp_(max=w - 1)\n                centers[:, :, 1].clamp_(min=0).clamp_(max=h - 1)\n            strides = strides_default.view(1, L, 1).expand(n, L, 2)\n            centers_inds = (centers / strides).long()  # n x L x 2\n            Ws = shapes_per_level[:, 1].view(1, L).expand(n, L)\n            pos_ind = level_bases.view(1, L).expand(n, L) \\\n                + im_i * loc_per_level.view(1, L).expand(n, L) \\\n                + centers_inds[:, :, 1] * Ws + centers_inds[:, :, 0]  # n x L\n            is_cared_in_the_level = self.assign_fpn_level(bboxes)\n            pos_ind = pos_ind[is_cared_in_the_level].view(-1)\n\n            pos_inds.append(pos_ind)  # n'\n        pos_inds = torch.cat(pos_inds, dim=0).long()\n        return pos_inds  # N, N\n\n    def assign_fpn_level(self, boxes):\n        '''\n        Inputs:\n            boxes: n x 4\n            size_ranges: L x 2\n        Return:\n            is_cared_in_the_level: n x L\n        '''\n        size_ranges = boxes.new_tensor(self.regress_ranges).view(\n            len(self.regress_ranges), 2)  # L x 2\n        crit = ((boxes[:, 2:] - boxes[:, :2])**2).sum(dim=1)**0.5 / 2  # n\n        n, L = crit.shape[0], size_ranges.shape[0]\n        crit = crit.view(n, 1).expand(n, L)\n        size_ranges_expand = size_ranges.view(1, L, 2).expand(n, L, 2)\n        is_cared_in_the_level = (crit >= size_ranges_expand[:, :, 0]) & \\\n            (crit <= size_ranges_expand[:, :, 1])\n        return is_cared_in_the_level\n\n    def _get_targets_single(self, gt_instances: InstanceData, points: Tensor,\n                            regress_ranges: Tensor,\n                            strides: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Compute classification and bbox targets for a single image.\"\"\"\n        num_points = points.size(0)\n        num_gts = len(gt_instances)\n        gt_labels = gt_instances.labels\n\n        if not hasattr(gt_instances, 'bboxes'):\n            gt_bboxes = gt_labels.new_tensor([], dtype=torch.float)\n        else:\n            gt_bboxes = gt_instances.bboxes\n\n        if not hasattr(gt_instances, 'bboxes') or num_gts == 0:\n            return gt_labels.new_full((num_points,\n                                       self.num_classes),\n                                      self.num_classes,\n                                      dtype=torch.float), \\\n                   gt_bboxes.new_full((num_points, 4), -1)\n\n        # Calculate the regression tblr target corresponding to all points\n        points = points[:, None].expand(num_points, num_gts, 2)\n        gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 4)\n        strides = strides[:, None, None].expand(num_points, num_gts, 2)\n\n        bbox_target = bbox2distance(points, gt_bboxes)  # M x N x 4\n\n        # condition1: inside a gt bbox\n        inside_gt_bbox_mask = bbox_target.min(dim=2)[0] > 0  # M x N\n\n        # condition2: Calculate the nearest points from\n        # the upper, lower, left and right ranges from\n        # the center of the gt bbox\n        centers = ((gt_bboxes[..., [0, 1]] + gt_bboxes[..., [2, 3]]) / 2)\n        centers_discret = ((centers / strides).int() * strides).float() + \\\n            strides / 2\n\n        centers_discret_dist = points - centers_discret\n        dist_x = centers_discret_dist[..., 0].abs()\n        dist_y = centers_discret_dist[..., 1].abs()\n        inside_gt_center3x3_mask = (dist_x <= strides[..., 0]) & \\\n                                   (dist_y <= strides[..., 0])\n\n        # condition3： limit the regression range for each location\n        bbox_target_wh = bbox_target[..., :2] + bbox_target[..., 2:]\n        crit = (bbox_target_wh**2).sum(dim=2)**0.5 / 2\n        inside_fpn_level_mask = (crit >= regress_ranges[:, [0]]) & \\\n                                (crit <= regress_ranges[:, [1]])\n        bbox_target_mask = inside_gt_bbox_mask & \\\n            inside_gt_center3x3_mask & \\\n            inside_fpn_level_mask\n\n        # Calculate the distance weight map\n        gt_center_peak_mask = ((centers_discret_dist**2).sum(dim=2) == 0)\n        weighted_dist = ((points - centers)**2).sum(dim=2)  # M x N\n        weighted_dist[gt_center_peak_mask] = 0\n\n        areas = (gt_bboxes[..., 2] - gt_bboxes[..., 0]) * (\n            gt_bboxes[..., 3] - gt_bboxes[..., 1])\n        radius = self.delta**2 * 2 * areas\n        radius = torch.clamp(radius, min=self.hm_min_radius**2)\n        weighted_dist = weighted_dist / radius\n\n        # Calculate bbox_target\n        bbox_weighted_dist = weighted_dist.clone()\n        bbox_weighted_dist[bbox_target_mask == 0] = INF * 1.0\n        min_dist, min_inds = bbox_weighted_dist.min(dim=1)\n        bbox_target = bbox_target[range(len(bbox_target)),\n                                  min_inds]  # M x N x 4 --> M x 4\n        bbox_target[min_dist == INF] = -INF\n\n        # Convert to feature map scale\n        bbox_target /= strides[:, 0, :].repeat(1, 2)\n\n        # Calculate cls_target\n        cls_target = self._create_heatmaps_from_dist(weighted_dist, gt_labels)\n\n        return cls_target, bbox_target\n"
  },
  {
    "path": "projects/Detic_new/detic/detic.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nfrom typing import List, Union\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.logging import print_log\nfrom torch import Tensor\n\nfrom mmdet.datasets import LVISV1Dataset\nfrom mmdet.models.detectors.cascade_rcnn import CascadeRCNN\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\n\n\nclass CLIPTextEncoder(nn.Module):\n\n    def __init__(self, model_name='ViT-B/32'):\n        super().__init__()\n        import clip\n        from clip.simple_tokenizer import SimpleTokenizer\n        self.tokenizer = SimpleTokenizer()\n        pretrained_model, _ = clip.load(model_name, device='cpu')\n        self.clip = pretrained_model\n\n    @property\n    def device(self):\n        return self.clip.device\n\n    @property\n    def dtype(self):\n        return self.clip.dtype\n\n    def tokenize(self,\n                 texts: Union[str, List[str]],\n                 context_length: int = 77) -> torch.LongTensor:\n        if isinstance(texts, str):\n            texts = [texts]\n\n        sot_token = self.tokenizer.encoder['<|startoftext|>']\n        eot_token = self.tokenizer.encoder['<|endoftext|>']\n        all_tokens = [[sot_token] + self.tokenizer.encode(text) + [eot_token]\n                      for text in texts]\n        result = torch.zeros(len(all_tokens), context_length, dtype=torch.long)\n\n        for i, tokens in enumerate(all_tokens):\n            if len(tokens) > context_length:\n                st = torch.randint(len(tokens) - context_length + 1,\n                                   (1, ))[0].item()\n                tokens = tokens[st:st + context_length]\n            result[i, :len(tokens)] = torch.tensor(tokens)\n\n        return result\n\n    def forward(self, text):\n        text = self.tokenize(text)\n        text_features = self.clip.encode_text(text)\n        return text_features\n\n\ndef get_class_weight(original_caption, prompt_prefix='a '):\n    if isinstance(original_caption, str):\n        if original_caption == 'coco':\n            from mmdet.datasets import CocoDataset\n            class_names = CocoDataset.METAINFO['classes']\n        elif original_caption == 'cityscapes':\n            from mmdet.datasets import CityscapesDataset\n            class_names = CityscapesDataset.METAINFO['classes']\n        elif original_caption == 'voc':\n            from mmdet.datasets import VOCDataset\n            class_names = VOCDataset.METAINFO['classes']\n        elif original_caption == 'openimages':\n            from mmdet.datasets import OpenImagesDataset\n            class_names = OpenImagesDataset.METAINFO['classes']\n        elif original_caption == 'lvis':\n            from mmdet.datasets import LVISV1Dataset\n            class_names = LVISV1Dataset.METAINFO['classes']\n        else:\n            if not original_caption.endswith('.'):\n                original_caption = original_caption + ' . '\n            original_caption = original_caption.split(' . ')\n            class_names = list(filter(lambda x: len(x) > 0, original_caption))\n\n    # for test.py\n    else:\n        class_names = list(original_caption)\n\n    text_encoder = CLIPTextEncoder()\n    text_encoder.eval()\n    texts = [prompt_prefix + x for x in class_names]\n    print_log(f'Computing text embeddings for {len(class_names)} classes.')\n    embeddings = text_encoder(texts).detach().permute(1, 0).contiguous().cpu()\n    return class_names, embeddings\n\n\ndef reset_cls_layer_weight(roi_head, weight):\n    if type(weight) == str:\n        print_log(f'Resetting cls_layer_weight from file: {weight}')\n        zs_weight = torch.tensor(\n            np.load(weight),\n            dtype=torch.float32).permute(1, 0).contiguous()  # D x C\n    else:\n        zs_weight = weight\n    zs_weight = torch.cat(\n        [zs_weight, zs_weight.new_zeros(\n            (zs_weight.shape[0], 1))], dim=1)  # D x (C + 1)\n    zs_weight = F.normalize(zs_weight, p=2, dim=0)\n    zs_weight = zs_weight.to('cuda')\n    num_classes = zs_weight.shape[-1]\n\n    for bbox_head in roi_head.bbox_head:\n        bbox_head.num_classes = num_classes\n        del bbox_head.fc_cls.zs_weight\n        bbox_head.fc_cls.zs_weight = zs_weight\n\n\n@MODELS.register_module()\nclass Detic(CascadeRCNN):\n\n    def __init__(self,\n                 with_image_labels: bool = False,\n                 sync_caption_batch: bool = False,\n                 fp16: bool = False,\n                 roi_head_name: str = '',\n                 cap_batch_ratio: int = 4,\n                 with_caption: bool = False,\n                 dynamic_classifier: bool = False,\n                 **kwargs) -> None:\n        super().__init__(**kwargs)\n\n        self._entities = LVISV1Dataset.METAINFO['classes']\n        self._text_prompts = None\n        # Turn on co-training with classification data\n        self.with_image_labels = with_image_labels\n        # Caption losses\n        self.with_caption = with_caption\n        # synchronize across GPUs to enlarge # \"classes\"\n        self.sync_caption_batch = sync_caption_batch\n        # Ratio between detection data and caption data\n        self.cap_batch_ratio = cap_batch_ratio\n        self.fp16 = fp16\n        self.roi_head_name = roi_head_name\n        # dynamic class sampling when training with 21K classes,\n        # Federated loss is enabled when DYNAMIC_CLASSIFIER is on\n        self.dynamic_classifier = dynamic_classifier\n        self.return_proposal = False\n        if self.dynamic_classifier:\n            self.freq_weight = kwargs.pop('freq_weight')\n            self.num_classes = kwargs.pop('num_classes')\n            self.num_sample_cats = kwargs.pop('num_sample_cats')\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (List[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components\n        \"\"\"\n\n        x = self.extract_feat(batch_inputs)\n        losses = dict()\n\n        # RPN forward and loss\n        if self.with_rpn:\n            proposal_cfg = self.train_cfg.get('rpn_proposal',\n                                              self.test_cfg.rpn)\n            rpn_data_samples = copy.deepcopy(batch_data_samples)\n            # set cat_id of gt_labels to 0 in RPN\n            for data_sample in rpn_data_samples:\n                data_sample.gt_instances.labels = \\\n                    torch.zeros_like(data_sample.gt_instances.labels)\n\n            rpn_losses, rpn_results_list = self.rpn_head.loss_and_predict(\n                x, rpn_data_samples, proposal_cfg=proposal_cfg)\n\n            # avoid get same name with roi_head loss\n            keys = rpn_losses.keys()\n            for key in list(keys):\n                if 'loss' in key and 'rpn' not in key:\n                    rpn_losses[f'rpn_{key}'] = rpn_losses.pop(key)\n            losses.update(rpn_losses)\n            # if not hasattr(batch_data_samples[0].gt_instances, 'bboxes'):\n            #     losses.update({k: v * 0 for k, v in rpn_losses.items()})\n            # else:\n            #     losses.update(rpn_losses)\n        else:\n            assert batch_data_samples[0].get('proposals', None) is not None\n            # use pre-defined proposals in InstanceData for the second stage\n            # to extract ROI features.\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        roi_losses = self.roi_head.loss(x, rpn_results_list,\n                                        batch_data_samples)\n\n        losses.update(roi_losses)\n\n        return losses\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Return the detection results of the\n            input images. The returns value is DetDataSample,\n            which usually contain 'pred_instances'. And the\n            ``pred_instances`` usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        # For single image inference\n        if 'custom_entities' in batch_data_samples[0]:\n            text_prompts = batch_data_samples[0].text\n            if text_prompts != self._text_prompts:\n                self._text_prompts = text_prompts\n                class_names, zs_weight = get_class_weight(text_prompts)\n                self._entities = class_names\n                reset_cls_layer_weight(self.roi_head, zs_weight)\n\n        assert self.with_bbox, 'Bbox head must be implemented.'\n\n        x = self.extract_feat(batch_inputs)\n\n        # If there are no pre-defined proposals, use RPN to get proposals\n        if batch_data_samples[0].get('proposals', None) is None:\n            rpn_results_list = self.rpn_head.predict(\n                x, batch_data_samples, rescale=False)\n        else:\n            rpn_results_list = [\n                data_sample.proposals for data_sample in batch_data_samples\n            ]\n\n        results_list = self.roi_head.predict(\n            x, rpn_results_list, batch_data_samples, rescale=rescale)\n\n        for data_sample, pred_instances in zip(batch_data_samples,\n                                               results_list):\n            if len(pred_instances) > 0:\n                label_names = []\n                for labels in pred_instances.labels:\n                    label_names.append(self._entities[labels])\n                # for visualization\n                pred_instances.label_names = label_names\n            data_sample.pred_instances = pred_instances\n\n        return batch_data_samples\n"
  },
  {
    "path": "projects/Detic_new/detic/detic_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nfrom typing import List, Optional\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\nfrom torch.nn import functional as F\n\nfrom mmdet.models.layers import multiclass_nms\nfrom mmdet.models.losses import accuracy\nfrom mmdet.models.roi_heads.bbox_heads import Shared2FCBBoxHead\nfrom mmdet.models.utils import empty_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import get_box_tensor, scale_boxes\nfrom mmdet.utils import ConfigType, InstanceList\n\n\ndef load_class_freq(path='datasets/metadata/lvis_v1_train_cat_info.json',\n                    freq_weight=0.5):\n    cat_info = json.load(open(path, 'r'))\n    cat_info = torch.tensor(\n        [c['image_count'] for c in sorted(cat_info, key=lambda x: x['id'])])\n    freq_weight = cat_info.float()**freq_weight\n    return freq_weight\n\n\ndef get_fed_loss_inds(labels, num_sample_cats, C, weight=None):\n\n    appeared = torch.unique(labels)  # C'\n    prob = appeared.new_ones(C + 1).float()\n    prob[-1] = 0\n    if len(appeared) < num_sample_cats:\n        if weight is not None:\n            prob[:C] = weight.float().clone()\n        prob[appeared] = 0\n        more_appeared = torch.multinomial(\n            prob, num_sample_cats - len(appeared), replacement=False)\n        appeared = torch.cat([appeared, more_appeared])\n    return appeared\n\n\n@MODELS.register_module()\nclass DeticBBoxHead(Shared2FCBBoxHead):\n\n    def __init__(self,\n                 image_loss_weight: float = 0.1,\n                 use_fed_loss: bool = False,\n                 cat_freq_path: str = '',\n                 fed_loss_freq_weight: float = 0.5,\n                 fed_loss_num_cat: int = 50,\n                 cls_predictor_cfg: ConfigType = dict(\n                     type='ZeroShotClassifier'),\n                 *args,\n                 **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n        # reconstruct fc_cls and fc_reg since input channels are changed\n        assert self.with_cls\n\n        self.cls_predictor_cfg = cls_predictor_cfg\n        cls_channels = self.num_classes\n        self.cls_predictor_cfg.update(\n            in_features=self.cls_last_dim, out_features=cls_channels)\n        self.fc_cls = MODELS.build(self.cls_predictor_cfg)\n\n        self.init_cfg += [\n            dict(type='Caffe2Xavier', override=dict(name='reg_fcs'))\n        ]\n\n        self.image_loss_weight = image_loss_weight\n        self.use_fed_loss = use_fed_loss\n        self.cat_freq_path = cat_freq_path\n        self.fed_loss_freq_weight = fed_loss_freq_weight\n        self.fed_loss_num_cat = fed_loss_num_cat\n\n        if self.use_fed_loss:\n            freq_weight = load_class_freq(cat_freq_path, fed_loss_freq_weight)\n            self.register_buffer('freq_weight', freq_weight)\n        else:\n            self.freq_weight = None\n\n    def _predict_by_feat_single(\n            self,\n            roi: Tensor,\n            cls_score: Tensor,\n            bbox_pred: Tensor,\n            img_meta: dict,\n            rescale: bool = False,\n            rcnn_test_cfg: Optional[ConfigDict] = None) -> InstanceData:\n        \"\"\"Transform a single image's features extracted from the head into\n        bbox results.\n\n        Args:\n            roi (Tensor): Boxes to be transformed. Has shape (num_boxes, 5).\n                last dimension 5 arrange as (batch_index, x1, y1, x2, y2).\n            cls_score (Tensor): Box scores, has shape\n                (num_boxes, num_classes + 1).\n            bbox_pred (Tensor): Box energies / deltas.\n                has shape (num_boxes, num_classes * 4).\n            img_meta (dict): image information.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of Bbox Head.\n                Defaults to None\n\n        Returns:\n            :obj:`InstanceData`: Detection results of each image\\\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        results = InstanceData()\n        if roi.shape[0] == 0:\n            return empty_instances([img_meta],\n                                   roi.device,\n                                   task_type='bbox',\n                                   instance_results=[results],\n                                   box_type=self.predict_box_type,\n                                   use_box_type=False,\n                                   num_classes=self.num_classes,\n                                   score_per_cls=rcnn_test_cfg is None)[0]\n        scores = cls_score\n        img_shape = img_meta['img_shape']\n        num_rois = roi.size(0)\n\n        num_classes = 1 if self.reg_class_agnostic else self.num_classes\n        roi = roi.repeat_interleave(num_classes, dim=0)\n        bbox_pred = bbox_pred.view(-1, self.bbox_coder.encode_size)\n        bboxes = self.bbox_coder.decode(\n            roi[..., 1:], bbox_pred, max_shape=img_shape)\n\n        if rescale and bboxes.size(0) > 0:\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = [1 / s for s in img_meta['scale_factor']]\n            bboxes = scale_boxes(bboxes, scale_factor)\n\n        # Get the inside tensor when `bboxes` is a box type\n        bboxes = get_box_tensor(bboxes)\n        box_dim = bboxes.size(-1)\n        bboxes = bboxes.view(num_rois, -1)\n\n        if rcnn_test_cfg is None:\n            # This means that it is aug test.\n            # It needs to return the raw results without nms.\n            results.bboxes = bboxes\n            results.scores = scores\n        else:\n            det_bboxes, det_labels = multiclass_nms(\n                bboxes,\n                scores,\n                rcnn_test_cfg.score_thr,\n                rcnn_test_cfg.nms,\n                rcnn_test_cfg.max_per_img,\n                box_dim=box_dim)\n            results.bboxes = det_bboxes[:, :-1]\n            results.scores = det_bboxes[:, -1]\n            results.labels = det_labels\n        return results\n\n    def loss(self,\n             cls_score: Tensor,\n             bbox_pred: Tensor,\n             rois: Tensor,\n             labels: Tensor,\n             label_weights: Tensor,\n             bbox_targets: Tensor,\n             bbox_weights: Tensor,\n             reduction_override: Optional[str] = None) -> dict:\n        \"\"\"Calculate the loss based on the network predictions and targets.\n\n        Args:\n            cls_score (Tensor): Classification prediction\n                results of all class, has shape\n                (batch_size * num_proposals_single_image, num_classes)\n            bbox_pred (Tensor): Regression prediction results,\n                has shape\n                (batch_size * num_proposals_single_image, 4), the last\n                dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            rois (Tensor): RoIs with the shape\n                (batch_size * num_proposals_single_image, 5) where the first\n                column indicates batch id of each RoI.\n            labels (Tensor): Gt_labels for all proposals in a batch, has\n                shape (batch_size * num_proposals_single_image, ).\n            label_weights (Tensor): Labels_weights for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, ).\n            bbox_targets (Tensor): Regression target for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, 4),\n                the last dimension 4 represents [tl_x, tl_y, br_x, br_y].\n            bbox_weights (Tensor): Regression weights for all proposals in a\n                batch, has shape (batch_size * num_proposals_single_image, 4).\n            reduction_override (str, optional): The reduction\n                method used to override the original reduction\n                method of the loss. Options are \"none\",\n                \"mean\" and \"sum\". Defaults to None,\n\n        Returns:\n            dict: A dictionary of loss.\n        \"\"\"\n\n        losses = dict()\n\n        if cls_score is not None:\n\n            if cls_score.numel() > 0:\n                loss_cls_ = self.sigmoid_cross_entropy_loss(cls_score, labels)\n                if isinstance(loss_cls_, dict):\n                    losses.update(loss_cls_)\n                else:\n                    losses['loss_cls'] = loss_cls_\n                if self.custom_activation:\n                    acc_ = self.loss_cls.get_accuracy(cls_score, labels)\n                    losses.update(acc_)\n                else:\n                    losses['acc'] = accuracy(cls_score, labels)\n        if bbox_pred is not None:\n            bg_class_ind = self.num_classes\n            # 0~self.num_classes-1 are FG, self.num_classes is BG\n            pos_inds = (labels >= 0) & (labels < bg_class_ind)\n            # do not perform bounding box regression for BG anymore.\n            if pos_inds.any():\n                if self.reg_decoded_bbox:\n                    # When the regression loss (e.g. `IouLoss`,\n                    # `GIouLoss`, `DIouLoss`) is applied directly on\n                    # the decoded bounding boxes, it decodes the\n                    # already encoded coordinates to absolute format.\n                    bbox_pred = self.bbox_coder.decode(rois[:, 1:], bbox_pred)\n                    bbox_pred = get_box_tensor(bbox_pred)\n                if self.reg_class_agnostic:\n                    pos_bbox_pred = bbox_pred.view(\n                        bbox_pred.size(0), -1)[pos_inds.type(torch.bool)]\n                else:\n                    pos_bbox_pred = bbox_pred.view(\n                        bbox_pred.size(0), self.num_classes,\n                        -1)[pos_inds.type(torch.bool),\n                            labels[pos_inds.type(torch.bool)]]\n\n                losses['loss_bbox'] = self.loss_bbox(\n                    pos_bbox_pred,\n                    bbox_targets[pos_inds.type(torch.bool)],\n                    bbox_weights[pos_inds.type(torch.bool)],\n                    avg_factor=bbox_targets.size(0),\n                    reduction_override=reduction_override)\n            else:\n                losses['loss_bbox'] = bbox_pred[pos_inds].sum()\n        return losses\n\n    def sigmoid_cross_entropy_loss(self, cls_score, labels):\n        if cls_score.numel() == 0:\n            return cls_score.new_zeros(\n                [1])[0]  # This is more robust than .sum() * 0.\n        B = cls_score.shape[0]\n        C = cls_score.shape[1] - 1\n\n        target = cls_score.new_zeros(B, C + 1)\n        target[range(len(labels)), labels] = 1  # B x (C + 1)\n        target = target[:, :C]  # B x C\n\n        weight = 1\n        if self.use_fed_loss and (self.freq_weight is not None):  # fedloss\n            appeared = get_fed_loss_inds(\n                labels,\n                num_sample_cats=self.fed_loss_num_cat,\n                C=C,\n                weight=self.freq_weight)\n            appeared_mask = appeared.new_zeros(C + 1)\n            appeared_mask[appeared] = 1  # C + 1\n            appeared_mask = appeared_mask[:C]\n            fed_w = appeared_mask.view(1, C).expand(B, C)\n            weight = weight * fed_w.float()\n        # if self.ignore_zero_cats and (self.freq_weight is not None):\n        #     w = (self.freq_weight.view(-1) > 1e-4).float()\n        #     weight = weight * w.view(1, C).expand(B, C)\n        #     # import pdb; pdb.set_trace()\n\n        cls_loss = F.binary_cross_entropy_with_logits(\n            cls_score[:, :-1], target, reduction='none')  # B x C\n        loss = torch.sum(cls_loss * weight) / B\n        return loss\n\n    def image_label_losses(self, cls_score, sampling_results, image_labels):\n        '''\n        Inputs:\n            cls_score: N x (C + 1)\n            image_labels B x 1\n        '''\n        num_inst_per_image = [\n            len(pred_instances) for pred_instances in sampling_results\n        ]\n        cls_score = cls_score.split(\n            num_inst_per_image, dim=0)  # B x n x (C + 1)\n        B = len(cls_score)\n        loss = cls_score[0].new_zeros([1])[0]\n        for (score, labels, pred_instances) in zip(cls_score, image_labels,\n                                                   sampling_results):\n            if score.shape[0] == 0:\n                loss += score.new_zeros([1])[0]\n                continue\n            # find out max-size idx\n            bboxes = pred_instances.bboxes\n            areas = (bboxes[:, 2] - bboxes[:, 0]) * (\n                bboxes[:, 3] - bboxes[:, 1])\n            idx = areas[:-1].argmax().item() if len(areas) > 1 else 0\n\n            for label in labels:\n                target = score.new_zeros(score.shape[1])\n                target[label] = 1\n                loss_i = F.binary_cross_entropy_with_logits(\n                    score[idx], target, reduction='sum')\n                loss += loss_i / len(labels)\n        loss = loss / B\n\n        return loss * self.image_loss_weight\n\n    def refine_bboxes(self, bbox_results: dict,\n                      batch_img_metas: List[dict]) -> InstanceList:\n        \"\"\"Refine bboxes during training.\n\n        Args:\n            bbox_results (dict): Usually is a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `rois` (Tensor): RoIs with the shape (n, 5) where the first\n                  column indicates batch id of each RoI.\n                - `bbox_targets` (tuple):  Ground truth for proposals in a\n                  single image. Containing the following list of Tensors:\n                  (labels, label_weights, bbox_targets, bbox_weights)\n            batch_img_metas (List[dict]): List of image information.\n\n        Returns:\n            list[:obj:`InstanceData`]: Refined bboxes of each image.\n\n        Example:\n            >>> # xdoctest: +REQUIRES(module:kwarray)\n            >>> import numpy as np\n            >>> from mmdet.models.task_modules.samplers.\n            ... sampling_result import random_boxes\n            >>> from mmdet.models.task_modules.samplers import SamplingResult\n            >>> self = BBoxHead(reg_class_agnostic=True)\n            >>> n_roi = 2\n            >>> n_img = 4\n            >>> scale = 512\n            >>> rng = np.random.RandomState(0)\n            ... batch_img_metas = [{'img_shape': (scale, scale)}\n            >>>                     for _ in range(n_img)]\n            >>> sampling_results = [SamplingResult.random(rng=10)\n            ...                     for _ in range(n_img)]\n            >>> # Create rois in the expected format\n            >>> roi_boxes = random_boxes(n_roi, scale=scale, rng=rng)\n            >>> img_ids = torch.randint(0, n_img, (n_roi,))\n            >>> img_ids = img_ids.float()\n            >>> rois = torch.cat([img_ids[:, None], roi_boxes], dim=1)\n            >>> # Create other args\n            >>> labels = torch.randint(0, 81, (scale,)).long()\n            >>> bbox_preds = random_boxes(n_roi, scale=scale, rng=rng)\n            >>> cls_score = torch.randn((scale, 81))\n            ... # For each image, pretend random positive boxes are gts\n            >>> bbox_targets = (labels, None, None, None)\n            ... bbox_results = dict(rois=rois, bbox_pred=bbox_preds,\n            ...                     cls_score=cls_score,\n            ...                     bbox_targets=bbox_targets)\n            >>> bboxes_list = self.refine_bboxes(sampling_results,\n            ...                                  bbox_results,\n            ...                                  batch_img_metas)\n            >>> print(bboxes_list)\n        \"\"\"\n        # bbox_targets is a tuple\n        cls_scores = bbox_results['cls_score']\n        rois = bbox_results['rois']\n        bbox_preds = bbox_results['bbox_pred']\n        if self.custom_activation:\n            # TODO: Create a SeasawBBoxHead to simplified logic in BBoxHead\n            cls_scores = self.loss_cls.get_activation(cls_scores)\n        if cls_scores.numel() == 0:\n            return None\n        if cls_scores.shape[-1] == self.num_classes + 1:\n            # remove background class\n            cls_scores = cls_scores[:, :-1]\n        elif cls_scores.shape[-1] != self.num_classes:\n            raise ValueError('The last dim of `cls_scores` should equal to '\n                             '`num_classes` or `num_classes + 1`,'\n                             f'but got {cls_scores.shape[-1]}.')\n\n        img_ids = rois[:, 0].long().unique(sorted=True)\n        assert img_ids.numel() <= len(batch_img_metas)\n\n        results_list = []\n        for i in range(len(batch_img_metas)):\n            inds = torch.nonzero(\n                rois[:, 0] == i, as_tuple=False).squeeze(dim=1)\n\n            bboxes_ = rois[inds, 1:]\n            bbox_pred_ = bbox_preds[inds]\n            img_meta_ = batch_img_metas[i]\n\n            bboxes = self.regress(bboxes_, bbox_pred_, img_meta_)\n\n            # don't filter gt bboxes like D2\n            results = InstanceData(bboxes=bboxes)\n            results_list.append(results)\n\n        return results_list\n\n    def regress(self, priors: Tensor, bbox_pred: Tensor,\n                img_meta: dict) -> Tensor:\n        \"\"\"Regress the bbox for the predicted class. Used in Cascade R-CNN.\n\n        Args:\n            priors (Tensor): Priors from `rpn_head` or last stage\n                `bbox_head`, has shape (num_proposals, 4).\n            label (Tensor): Only used when `self.reg_class_agnostic`\n                is False, has shape (num_proposals, ).\n            bbox_pred (Tensor): Regression prediction of\n                current stage `bbox_head`. When `self.reg_class_agnostic`\n                is False, it has shape (n, num_classes * 4), otherwise\n                it has shape (n, 4).\n            img_meta (dict): Image meta info.\n\n        Returns:\n            Tensor: Regressed bboxes, the same shape as input rois.\n        \"\"\"\n        reg_dim = self.bbox_coder.encode_size\n        assert bbox_pred.size()[1] == reg_dim\n\n        max_shape = img_meta['img_shape']\n        regressed_bboxes = self.bbox_coder.decode(\n            priors, bbox_pred, max_shape=max_shape)\n        return regressed_bboxes\n"
  },
  {
    "path": "projects/Detic_new/detic/detic_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Sequence, Tuple\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads import CascadeRoIHead\nfrom mmdet.models.task_modules.samplers import SamplingResult\nfrom mmdet.models.test_time_augs import merge_aug_masks\nfrom mmdet.models.utils import empty_instances, unpack_gt_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import bbox2roi, get_box_tensor\nfrom mmdet.utils import ConfigType, InstanceList, MultiConfig\n\n\n@MODELS.register_module()\nclass DeticRoIHead(CascadeRoIHead):\n\n    def __init__(\n        self,\n        *,\n        mult_proposal_score: bool = False,\n        with_image_labels: bool = False,\n        add_image_box: bool = False,\n        image_box_size: float = 1.0,\n        ws_num_props: int = 128,\n        add_feature_to_prop: bool = False,\n        mask_weight: float = 1.0,\n        one_class_per_proposal: bool = False,\n        **kwargs,\n    ):\n        super().__init__(**kwargs)\n        self.mult_proposal_score = mult_proposal_score\n        self.with_image_labels = with_image_labels\n        self.add_image_box = add_image_box\n        self.image_box_size = image_box_size\n        self.ws_num_props = ws_num_props\n        self.add_feature_to_prop = add_feature_to_prop\n        self.mask_weight = mask_weight\n        self.one_class_per_proposal = one_class_per_proposal\n\n    def init_mask_head(self, mask_roi_extractor: MultiConfig,\n                       mask_head: MultiConfig) -> None:\n        \"\"\"Initialize mask head and mask roi extractor.\n\n        Args:\n            mask_head (dict): Config of mask in mask head.\n            mask_roi_extractor (:obj:`ConfigDict`, dict or list):\n                Config of mask roi extractor.\n        \"\"\"\n        self.mask_head = MODELS.build(mask_head)\n\n        if mask_roi_extractor is not None:\n            self.share_roi_extractor = False\n            self.mask_roi_extractor = MODELS.build(mask_roi_extractor)\n        else:\n            self.share_roi_extractor = True\n            self.mask_roi_extractor = self.bbox_roi_extractor\n\n    def _refine_roi(self, x: Tuple[Tensor], rois: Tensor,\n                    batch_img_metas: List[dict],\n                    num_proposals_per_img: Sequence[int], **kwargs) -> tuple:\n        \"\"\"Multi-stage refinement of RoI.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rois (Tensor): shape (n, 5), [batch_ind, x1, y1, x2, y2]\n            batch_img_metas (list[dict]): List of image information.\n            num_proposals_per_img (sequence[int]): number of proposals\n                in each image.\n\n        Returns:\n            tuple:\n\n               - rois (Tensor): Refined RoI.\n               - cls_scores (list[Tensor]): Average predicted\n                   cls score per image.\n               - bbox_preds (list[Tensor]): Bbox branch predictions\n                   for the last stage of per image.\n        \"\"\"\n        # \"ms\" in variable names means multi-stage\n        ms_scores = []\n        for stage in range(self.num_stages):\n            bbox_results = self._bbox_forward(\n                stage=stage, x=x, rois=rois, **kwargs)\n\n            # split batch bbox prediction back to each image\n            cls_scores = bbox_results['cls_score'].sigmoid()\n            bbox_preds = bbox_results['bbox_pred']\n\n            rois = rois.split(num_proposals_per_img, 0)\n            cls_scores = cls_scores.split(num_proposals_per_img, 0)\n            ms_scores.append(cls_scores)\n            bbox_preds = bbox_preds.split(num_proposals_per_img, 0)\n\n            if stage < self.num_stages - 1:\n                bbox_head = self.bbox_head[stage]\n                refine_rois_list = []\n                for i in range(len(batch_img_metas)):\n                    if rois[i].shape[0] > 0:\n                        bbox_label = cls_scores[i][:, :-1].argmax(dim=1)\n                        # Refactor `bbox_head.regress_by_class` to only accept\n                        # box tensor without img_idx concatenated.\n                        refined_bboxes = bbox_head.regress_by_class(\n                            rois[i][:, 1:], bbox_label, bbox_preds[i],\n                            batch_img_metas[i])\n                        refined_bboxes = get_box_tensor(refined_bboxes)\n                        refined_rois = torch.cat(\n                            [rois[i][:, [0]], refined_bboxes], dim=1)\n                        refine_rois_list.append(refined_rois)\n                rois = torch.cat(refine_rois_list)\n        # ms_scores aligned\n        # average scores of each image by stages\n        cls_scores = [\n            sum([score[i] for score in ms_scores]) / float(len(ms_scores))\n            for i in range(len(batch_img_metas))\n        ]  # aligned\n        return rois, cls_scores, bbox_preds\n\n    def predict_bbox(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     rpn_results_list: InstanceList,\n                     rcnn_test_cfg: ConfigType,\n                     rescale: bool = False,\n                     **kwargs) -> InstanceList:\n        \"\"\"Perform forward propagation of the bbox head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            rcnn_test_cfg (obj:`ConfigDict`): `test_cfg` of R-CNN.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        proposals = [res.bboxes for res in rpn_results_list]\n        proposal_scores = [res.scores for res in rpn_results_list]\n        num_proposals_per_img = tuple(len(p) for p in proposals)\n        rois = bbox2roi(proposals)\n\n        if rois.shape[0] == 0:\n            return empty_instances(\n                batch_img_metas,\n                rois.device,\n                task_type='bbox',\n                box_type=self.bbox_head[-1].predict_box_type,\n                num_classes=self.bbox_head[-1].num_classes,\n                score_per_cls=rcnn_test_cfg is None)\n        # rois aligned\n        rois, cls_scores, bbox_preds = self._refine_roi(\n            x=x,\n            rois=rois,\n            batch_img_metas=batch_img_metas,\n            num_proposals_per_img=num_proposals_per_img,\n            **kwargs)\n\n        # score reweighting in centernet2\n        cls_scores = [(s * ps[:, None])**0.5\n                      for s, ps in zip(cls_scores, proposal_scores)]\n        # # for demo\n        # cls_scores = [\n        #     s * (s == s[:, :-1].max(dim=1)[0][:, None]).float()\n        #     for s in cls_scores\n        # ]\n\n        # fast_rcnn_inference\n        results_list = self.bbox_head[-1].predict_by_feat(\n            rois=rois,\n            cls_scores=cls_scores,\n            bbox_preds=bbox_preds,\n            batch_img_metas=batch_img_metas,\n            rescale=rescale,\n            rcnn_test_cfg=rcnn_test_cfg)\n        return results_list\n\n    def _mask_forward(self, x: Tuple[Tensor], rois: Tensor) -> dict:\n        \"\"\"Mask head forward function used in both training and testing.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            rois (Tensor): RoIs with the shape (n, 5) where the first\n                column indicates batch id of each RoI.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n        \"\"\"\n        mask_feats = self.mask_roi_extractor(\n            x[:self.mask_roi_extractor.num_inputs], rois)\n        # do not support caffe_c4 model anymore\n        mask_preds = self.mask_head(mask_feats)\n\n        mask_results = dict(mask_preds=mask_preds)\n        return mask_results\n\n    def mask_loss(self, x, sampling_results: List[SamplingResult],\n                  batch_gt_instances: InstanceList) -> dict:\n        \"\"\"Run forward function and calculate loss for mask head in training.\n\n        Args:\n            x (tuple[Tensor]): Tuple of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes``, ``labels``, and\n                ``masks`` attributes.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `mask_preds` (Tensor): Mask prediction.\n                - `loss_mask` (dict): A dictionary of mask loss components.\n        \"\"\"\n        pos_rois = bbox2roi([res.pos_priors for res in sampling_results])\n        mask_results = self._mask_forward(x, pos_rois)\n\n        mask_loss_and_target = self.mask_head.loss_and_target(\n            mask_preds=mask_results['mask_preds'],\n            sampling_results=sampling_results,\n            batch_gt_instances=batch_gt_instances,\n            rcnn_train_cfg=self.train_cfg[-1])\n        mask_results.update(mask_loss_and_target)\n\n        return mask_results\n\n    def loss(self, x: Tuple[Tensor], rpn_results_list: InstanceList,\n             batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        roi on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): List of multi-level img features.\n            rpn_results_list (list[:obj:`InstanceData`]): List of region\n                proposals.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components\n        \"\"\"\n        assert len(rpn_results_list) == len(batch_data_samples)\n        outputs = unpack_gt_instances(batch_data_samples)\n        batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n            = outputs\n\n        num_imgs = len(batch_data_samples)\n        image_labels = [x.gt_instances.labels for x in batch_data_samples]\n        losses = dict()\n        results_list = rpn_results_list\n\n        for stage in range(self.num_stages):\n            self.current_stage = stage\n            stage_loss_weight = self.stage_loss_weights[stage]\n            if hasattr(batch_gt_instances[0], 'bboxes'):\n                # assign gts and sample proposals\n                sampling_results = []\n                if self.with_bbox or self.with_mask:\n                    bbox_assigner = self.bbox_assigner[stage]\n                    bbox_sampler = self.bbox_sampler[stage]\n\n                    for i in range(num_imgs):\n                        results = results_list[i]\n                        # rename rpn_results.bboxes to rpn_results.priors\n                        results.priors = results.pop('bboxes')\n\n                        assign_result = bbox_assigner.assign(\n                            results, batch_gt_instances[i],\n                            batch_gt_instances_ignore[i])\n\n                        sampling_result = bbox_sampler.sample(\n                            assign_result,\n                            results,\n                            batch_gt_instances[i],\n                            feats=[lvl_feat[i][None] for lvl_feat in x])\n\n                        sampling_results.append(sampling_result)\n\n                # bbox head forward and loss\n                bbox_results = self.bbox_loss(stage, x, sampling_results)\n\n                for name, value in bbox_results['loss_bbox'].items():\n                    losses[f's{stage}.{name}'] = (\n                        value * stage_loss_weight if 'loss' in name else value)\n                losses[f's{stage}.image_loss'] = x[0].new_zeros([1])[0]\n\n                # mask head forward and loss\n                # D2 only forward stage.0\n                if self.with_mask and stage == 0:\n                    mask_results = self.mask_loss(x, sampling_results,\n                                                  batch_gt_instances)\n                    for name, value in mask_results['loss_mask'].items():\n                        losses[name] = (\n                            value *\n                            stage_loss_weight if 'loss' in name else value)\n\n            else:\n                # get ws_num_props pred_instances for each image\n                sampling_results = [\n                    pred_instances[:self.ws_num_props]\n                    for pred_instances in results_list\n                ]\n                for i, pred_instances in enumerate(sampling_results):\n                    pred_instances.bboxes = pred_instances.bboxes.detach()\n                bbox_results = self.image_loss(stage, x, sampling_results,\n                                               image_labels)\n                losses[f's{stage}.image_loss'] = bbox_results['image_loss']\n\n                for name in ['loss_cls', 'loss_bbox']:\n                    losses[f's{stage}.{name}'] = x[0].new_zeros([1])[0]\n                if stage == 0:\n                    losses['loss_mask'] = x[0].new_zeros([1])[0]\n\n            # refine bboxes\n            if stage < self.num_stages - 1:\n                bbox_head = self.bbox_head[stage]\n                with torch.no_grad():\n                    results_list = bbox_head.refine_bboxes(\n                        bbox_results, batch_img_metas)\n                    # Empty proposal\n                    if results_list is None:\n                        break\n\n        return losses\n\n    def image_loss(self, stage: int, x: Tuple[Tensor],\n                   sampling_results: List[SamplingResult],\n                   image_labels) -> dict:\n        \"\"\"Run forward function and calculate loss for box head in training.\n\n        Args:\n            stage (int): The current stage in Cascade RoI Head.\n            x (tuple[Tensor]): List of multi-level img features.\n            sampling_results (list[\"obj:`SamplingResult`]): Sampling results.\n\n        Returns:\n            dict: Usually returns a dictionary with keys:\n\n                - `cls_score` (Tensor): Classification scores.\n                - `bbox_pred` (Tensor): Box energies / deltas.\n                - `bbox_feats` (Tensor): Extract bbox RoI features.\n                - `loss_bbox` (dict): A dictionary of bbox loss components.\n                - `rois` (Tensor): RoIs with the shape (n, 5) where the first\n                  column indicates batch id of each RoI.\n                - `bbox_targets` (tuple):  Ground truth for proposals in a\n                  single image. Containing the following list of Tensors:\n                  (labels, label_weights, bbox_targets, bbox_weights)\n        \"\"\"\n        bbox_head = self.bbox_head[stage]\n        rois = bbox2roi([res.bboxes for res in sampling_results])\n        bbox_results = self._bbox_forward(stage, x, rois)\n        bbox_results.update(rois=rois)\n\n        image_loss = bbox_head.image_label_losses(\n            cls_score=bbox_results['cls_score'],\n            sampling_results=sampling_results,\n            image_labels=image_labels)\n        bbox_results.update(dict(image_loss=image_loss))\n\n        return bbox_results\n\n    def predict_mask(self,\n                     x: Tuple[Tensor],\n                     batch_img_metas: List[dict],\n                     results_list: List[InstanceData],\n                     rescale: bool = False) -> List[InstanceData]:\n        \"\"\"Perform forward propagation of the mask head and predict detection\n        results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Feature maps of all scale level.\n            batch_img_metas (list[dict]): List of image information.\n            results_list (list[:obj:`InstanceData`]): Detection results of\n                each image.\n            rescale (bool): If True, return boxes in original image space.\n                Defaults to False.\n\n        Returns:\n            list[:obj:`InstanceData`]: Detection results of each image\n            after the post process.\n            Each item usually contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                  (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                  (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                  the last dimension 4 arrange as (x1, y1, x2, y2).\n                - masks (Tensor): Has a shape (num_instances, H, W).\n        \"\"\"\n        bboxes = [res.bboxes for res in results_list]\n        mask_rois = bbox2roi(bboxes)\n        if mask_rois.shape[0] == 0:\n            results_list = empty_instances(\n                batch_img_metas,\n                mask_rois.device,\n                task_type='mask',\n                instance_results=results_list,\n                mask_thr_binary=self.test_cfg.mask_thr_binary)\n            return results_list\n\n        num_mask_rois_per_img = [len(res) for res in results_list]\n        aug_masks = []\n        mask_results = self._mask_forward(x, mask_rois)\n        mask_preds = mask_results['mask_preds']\n        # split batch mask prediction back to each image\n        mask_preds = mask_preds.split(num_mask_rois_per_img, 0)\n        aug_masks.append([m.sigmoid().detach() for m in mask_preds])\n\n        merged_masks = []\n        for i in range(len(batch_img_metas)):\n            aug_mask = [mask[i] for mask in aug_masks]\n            merged_mask = merge_aug_masks(aug_mask, batch_img_metas[i])\n            merged_masks.append(merged_mask)\n        results_list = self.mask_head.predict_by_feat(\n            mask_preds=merged_masks,\n            results_list=results_list,\n            batch_img_metas=batch_img_metas,\n            rcnn_test_cfg=self.test_cfg,\n            rescale=rescale,\n            activate_map=True)\n        return results_list\n"
  },
  {
    "path": "projects/Detic_new/detic/heatmap_focal_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Union\n\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\n\n\n# support class-agnostic heatmap_focal_loss\ndef heatmap_focal_loss_with_pos_inds(\n        pred: Tensor,\n        targets: Tensor,\n        pos_inds: Tensor,\n        alpha: float = 2.0,\n        beta: float = 4.0,\n        gamma: float = 4.0,\n        sigmoid_clamp: float = 1e-4,\n        ignore_high_fp: float = -1.0,\n        pos_weight: float = 1.0,\n        neg_weight: float = 1.0,\n        avg_factor: Optional[Union[int, float]] = None) -> Tensor:\n\n    pred = torch.clamp(\n        pred.sigmoid_(), min=sigmoid_clamp, max=1 - sigmoid_clamp)\n\n    neg_weights = torch.pow(1 - targets, beta)\n\n    pos_pred = pred[pos_inds]\n    pos_loss = torch.log(pos_pred) * torch.pow(1 - pos_pred, gamma)\n    neg_loss = torch.log(1 - pred) * torch.pow(pred, gamma) * neg_weights\n    if ignore_high_fp > 0:\n        not_high_fp = (pred < ignore_high_fp).float()\n        neg_loss = not_high_fp * neg_loss\n\n    pos_loss = -pos_loss.sum()\n    neg_loss = -neg_loss.sum()\n    if alpha >= 0:\n        pos_loss = alpha * pos_loss\n        neg_loss = (1 - alpha) * neg_loss\n\n    pos_loss = pos_weight * pos_loss / avg_factor\n    neg_loss = neg_weight * neg_loss / avg_factor\n\n    return pos_loss, neg_loss\n\n\n@MODELS.register_module()\nclass HeatmapFocalLoss(nn.Module):\n    \"\"\"GaussianFocalLoss is a variant of focal loss.\n\n    More details can be found in the `paper\n    <https://arxiv.org/abs/1808.01244>`_\n    Code is modified from `kp_utils.py\n    <https://github.com/princeton-vl/CornerNet/blob/master/models/py_utils/kp_utils.py#L152>`_  # noqa: E501\n    Please notice that the target in GaussianFocalLoss is a gaussian heatmap,\n    not 0/1 binary target.\n\n    Args:\n        alpha (float): Power of prediction.\n        gamma (float): Power of target for negative samples.\n        reduction (str): Options are \"none\", \"mean\" and \"sum\".\n        loss_weight (float): Loss weight of current loss.\n        pos_weight(float): Positive sample loss weight. Defaults to 1.0.\n        neg_weight(float): Negative sample loss weight. Defaults to 1.0.\n    \"\"\"\n\n    def __init__(\n        self,\n        alpha: float = 2.0,\n        beta: float = 4.0,\n        gamma: float = 4.0,\n        sigmoid_clamp: float = 1e-4,\n        ignore_high_fp: float = -1.0,\n        loss_weight: float = 1.0,\n        pos_weight: float = 1.0,\n        neg_weight: float = 1.0,\n    ) -> None:\n        super().__init__()\n        self.alpha = alpha\n        self.beta = beta\n        self.gamma = gamma\n        self.sigmoid_clamp = sigmoid_clamp\n        self.ignore_high_fp = ignore_high_fp\n        self.loss_weight = loss_weight\n        self.pos_weight = pos_weight\n        self.neg_weight = neg_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                pos_inds: Optional[Tensor] = None,\n                avg_factor: Optional[Union[int, float]] = None) -> Tensor:\n        \"\"\"Forward function.\n\n        If you want to manually determine which positions are\n        positive samples, you can set the pos_index and pos_label\n        parameter. Currently, only the CenterNet update version uses\n        the parameter.\n\n        Args:\n            pred (torch.Tensor): The prediction. The shape is (N, num_classes).\n            target (torch.Tensor): The learning target of the prediction\n                in gaussian distribution. The shape is (N, num_classes).\n            pos_inds (torch.Tensor): The positive sample index.\n                Defaults to None.\n            pos_labels (torch.Tensor): The label corresponding to the positive\n                sample index. Defaults to None.\n            weight (torch.Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, float, optional): Average factor that is used to\n                average the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n        \"\"\"\n\n        pos_loss, neg_loss = heatmap_focal_loss_with_pos_inds(\n            pred,\n            target,\n            pos_inds,\n            alpha=self.alpha,\n            beta=self.beta,\n            gamma=self.gamma,\n            sigmoid_clamp=self.sigmoid_clamp,\n            ignore_high_fp=self.ignore_high_fp,\n            pos_weight=self.pos_weight,\n            neg_weight=self.neg_weight,\n            avg_factor=avg_factor)\n        return pos_loss, neg_loss\n"
  },
  {
    "path": "projects/Detic_new/detic/imagenet_lvis.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.METAINFO\nimport copy\nimport os.path as osp\nimport pickle\nimport warnings\nfrom typing import List, Union\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.datasets import LVISV1Dataset\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass ImageNetLVISV1Dataset(LVISV1Dataset):\n    \"\"\"LVIS v1 dataset for detection.\"\"\"\n\n    METAINFO = {\n        'classes':\n        ('aerosol_can', 'air_conditioner', 'airplane', 'alarm_clock',\n         'alcohol', 'alligator', 'almond', 'ambulance', 'amplifier', 'anklet',\n         'antenna', 'apple', 'applesauce', 'apricot', 'apron', 'aquarium',\n         'arctic_(type_of_shoe)', 'armband', 'armchair', 'armoire', 'armor',\n         'artichoke', 'trash_can', 'ashtray', 'asparagus', 'atomizer',\n         'avocado', 'award', 'awning', 'ax', 'baboon', 'baby_buggy',\n         'basketball_backboard', 'backpack', 'handbag', 'suitcase', 'bagel',\n         'bagpipe', 'baguet', 'bait', 'ball', 'ballet_skirt', 'balloon',\n         'bamboo', 'banana', 'Band_Aid', 'bandage', 'bandanna', 'banjo',\n         'banner', 'barbell', 'barge', 'barrel', 'barrette', 'barrow',\n         'baseball_base', 'baseball', 'baseball_bat', 'baseball_cap',\n         'baseball_glove', 'basket', 'basketball', 'bass_horn', 'bat_(animal)',\n         'bath_mat', 'bath_towel', 'bathrobe', 'bathtub', 'batter_(food)',\n         'battery', 'beachball', 'bead', 'bean_curd', 'beanbag', 'beanie',\n         'bear', 'bed', 'bedpan', 'bedspread', 'cow', 'beef_(food)', 'beeper',\n         'beer_bottle', 'beer_can', 'beetle', 'bell', 'bell_pepper', 'belt',\n         'belt_buckle', 'bench', 'beret', 'bib', 'Bible', 'bicycle', 'visor',\n         'billboard', 'binder', 'binoculars', 'bird', 'birdfeeder', 'birdbath',\n         'birdcage', 'birdhouse', 'birthday_cake', 'birthday_card',\n         'pirate_flag', 'black_sheep', 'blackberry', 'blackboard', 'blanket',\n         'blazer', 'blender', 'blimp', 'blinker', 'blouse', 'blueberry',\n         'gameboard', 'boat', 'bob', 'bobbin', 'bobby_pin', 'boiled_egg',\n         'bolo_tie', 'deadbolt', 'bolt', 'bonnet', 'book', 'bookcase',\n         'booklet', 'bookmark', 'boom_microphone', 'boot', 'bottle',\n         'bottle_opener', 'bouquet', 'bow_(weapon)',\n         'bow_(decorative_ribbons)', 'bow-tie', 'bowl', 'pipe_bowl',\n         'bowler_hat', 'bowling_ball', 'box', 'boxing_glove', 'suspenders',\n         'bracelet', 'brass_plaque', 'brassiere', 'bread-bin', 'bread',\n         'breechcloth', 'bridal_gown', 'briefcase', 'broccoli', 'broach',\n         'broom', 'brownie', 'brussels_sprouts', 'bubble_gum', 'bucket',\n         'horse_buggy', 'bull', 'bulldog', 'bulldozer', 'bullet_train',\n         'bulletin_board', 'bulletproof_vest', 'bullhorn', 'bun', 'bunk_bed',\n         'buoy', 'burrito', 'bus_(vehicle)', 'business_card', 'butter',\n         'butterfly', 'button', 'cab_(taxi)', 'cabana', 'cabin_car', 'cabinet',\n         'locker', 'cake', 'calculator', 'calendar', 'calf', 'camcorder',\n         'camel', 'camera', 'camera_lens', 'camper_(vehicle)', 'can',\n         'can_opener', 'candle', 'candle_holder', 'candy_bar', 'candy_cane',\n         'walking_cane', 'canister', 'canoe', 'cantaloup', 'canteen',\n         'cap_(headwear)', 'bottle_cap', 'cape', 'cappuccino',\n         'car_(automobile)', 'railcar_(part_of_a_train)', 'elevator_car',\n         'car_battery', 'identity_card', 'card', 'cardigan', 'cargo_ship',\n         'carnation', 'horse_carriage', 'carrot', 'tote_bag', 'cart', 'carton',\n         'cash_register', 'casserole', 'cassette', 'cast', 'cat',\n         'cauliflower', 'cayenne_(spice)', 'CD_player', 'celery',\n         'cellular_telephone', 'chain_mail', 'chair', 'chaise_longue',\n         'chalice', 'chandelier', 'chap', 'checkbook', 'checkerboard',\n         'cherry', 'chessboard', 'chicken_(animal)', 'chickpea',\n         'chili_(vegetable)', 'chime', 'chinaware', 'crisp_(potato_chip)',\n         'poker_chip', 'chocolate_bar', 'chocolate_cake', 'chocolate_milk',\n         'chocolate_mousse', 'choker', 'chopping_board', 'chopstick',\n         'Christmas_tree', 'slide', 'cider', 'cigar_box', 'cigarette',\n         'cigarette_case', 'cistern', 'clarinet', 'clasp', 'cleansing_agent',\n         'cleat_(for_securing_rope)', 'clementine', 'clip', 'clipboard',\n         'clippers_(for_plants)', 'cloak', 'clock', 'clock_tower',\n         'clothes_hamper', 'clothespin', 'clutch_bag', 'coaster', 'coat',\n         'coat_hanger', 'coatrack', 'cock', 'cockroach', 'cocoa_(beverage)',\n         'coconut', 'coffee_maker', 'coffee_table', 'coffeepot', 'coil',\n         'coin', 'colander', 'coleslaw', 'coloring_material',\n         'combination_lock', 'pacifier', 'comic_book', 'compass',\n         'computer_keyboard', 'condiment', 'cone', 'control',\n         'convertible_(automobile)', 'sofa_bed', 'cooker', 'cookie',\n         'cooking_utensil', 'cooler_(for_food)', 'cork_(bottle_plug)',\n         'corkboard', 'corkscrew', 'edible_corn', 'cornbread', 'cornet',\n         'cornice', 'cornmeal', 'corset', 'costume', 'cougar', 'coverall',\n         'cowbell', 'cowboy_hat', 'crab_(animal)', 'crabmeat', 'cracker',\n         'crape', 'crate', 'crayon', 'cream_pitcher', 'crescent_roll', 'crib',\n         'crock_pot', 'crossbar', 'crouton', 'crow', 'crowbar', 'crown',\n         'crucifix', 'cruise_ship', 'police_cruiser', 'crumb', 'crutch',\n         'cub_(animal)', 'cube', 'cucumber', 'cufflink', 'cup', 'trophy_cup',\n         'cupboard', 'cupcake', 'hair_curler', 'curling_iron', 'curtain',\n         'cushion', 'cylinder', 'cymbal', 'dagger', 'dalmatian', 'dartboard',\n         'date_(fruit)', 'deck_chair', 'deer', 'dental_floss', 'desk',\n         'detergent', 'diaper', 'diary', 'die', 'dinghy', 'dining_table',\n         'tux', 'dish', 'dish_antenna', 'dishrag', 'dishtowel', 'dishwasher',\n         'dishwasher_detergent', 'dispenser', 'diving_board', 'Dixie_cup',\n         'dog', 'dog_collar', 'doll', 'dollar', 'dollhouse', 'dolphin',\n         'domestic_ass', 'doorknob', 'doormat', 'doughnut', 'dove',\n         'dragonfly', 'drawer', 'underdrawers', 'dress', 'dress_hat',\n         'dress_suit', 'dresser', 'drill', 'drone', 'dropper',\n         'drum_(musical_instrument)', 'drumstick', 'duck', 'duckling',\n         'duct_tape', 'duffel_bag', 'dumbbell', 'dumpster', 'dustpan', 'eagle',\n         'earphone', 'earplug', 'earring', 'easel', 'eclair', 'eel', 'egg',\n         'egg_roll', 'egg_yolk', 'eggbeater', 'eggplant', 'electric_chair',\n         'refrigerator', 'elephant', 'elk', 'envelope', 'eraser', 'escargot',\n         'eyepatch', 'falcon', 'fan', 'faucet', 'fedora', 'ferret',\n         'Ferris_wheel', 'ferry', 'fig_(fruit)', 'fighter_jet', 'figurine',\n         'file_cabinet', 'file_(tool)', 'fire_alarm', 'fire_engine',\n         'fire_extinguisher', 'fire_hose', 'fireplace', 'fireplug',\n         'first-aid_kit', 'fish', 'fish_(food)', 'fishbowl', 'fishing_rod',\n         'flag', 'flagpole', 'flamingo', 'flannel', 'flap', 'flash',\n         'flashlight', 'fleece', 'flip-flop_(sandal)', 'flipper_(footwear)',\n         'flower_arrangement', 'flute_glass', 'foal', 'folding_chair',\n         'food_processor', 'football_(American)', 'football_helmet',\n         'footstool', 'fork', 'forklift', 'freight_car', 'French_toast',\n         'freshener', 'frisbee', 'frog', 'fruit_juice', 'frying_pan', 'fudge',\n         'funnel', 'futon', 'gag', 'garbage', 'garbage_truck', 'garden_hose',\n         'gargle', 'gargoyle', 'garlic', 'gasmask', 'gazelle', 'gelatin',\n         'gemstone', 'generator', 'giant_panda', 'gift_wrap', 'ginger',\n         'giraffe', 'cincture', 'glass_(drink_container)', 'globe', 'glove',\n         'goat', 'goggles', 'goldfish', 'golf_club', 'golfcart',\n         'gondola_(boat)', 'goose', 'gorilla', 'gourd', 'grape', 'grater',\n         'gravestone', 'gravy_boat', 'green_bean', 'green_onion', 'griddle',\n         'grill', 'grits', 'grizzly', 'grocery_bag', 'guitar', 'gull', 'gun',\n         'hairbrush', 'hairnet', 'hairpin', 'halter_top', 'ham', 'hamburger',\n         'hammer', 'hammock', 'hamper', 'hamster', 'hair_dryer', 'hand_glass',\n         'hand_towel', 'handcart', 'handcuff', 'handkerchief', 'handle',\n         'handsaw', 'hardback_book', 'harmonium', 'hat', 'hatbox', 'veil',\n         'headband', 'headboard', 'headlight', 'headscarf', 'headset',\n         'headstall_(for_horses)', 'heart', 'heater', 'helicopter', 'helmet',\n         'heron', 'highchair', 'hinge', 'hippopotamus', 'hockey_stick', 'hog',\n         'home_plate_(baseball)', 'honey', 'fume_hood', 'hook', 'hookah',\n         'hornet', 'horse', 'hose', 'hot-air_balloon', 'hotplate', 'hot_sauce',\n         'hourglass', 'houseboat', 'hummingbird', 'hummus', 'polar_bear',\n         'icecream', 'popsicle', 'ice_maker', 'ice_pack', 'ice_skate',\n         'igniter', 'inhaler', 'iPod', 'iron_(for_clothing)', 'ironing_board',\n         'jacket', 'jam', 'jar', 'jean', 'jeep', 'jelly_bean', 'jersey',\n         'jet_plane', 'jewel', 'jewelry', 'joystick', 'jumpsuit', 'kayak',\n         'keg', 'kennel', 'kettle', 'key', 'keycard', 'kilt', 'kimono',\n         'kitchen_sink', 'kitchen_table', 'kite', 'kitten', 'kiwi_fruit',\n         'knee_pad', 'knife', 'knitting_needle', 'knob', 'knocker_(on_a_door)',\n         'koala', 'lab_coat', 'ladder', 'ladle', 'ladybug', 'lamb_(animal)',\n         'lamb-chop', 'lamp', 'lamppost', 'lampshade', 'lantern', 'lanyard',\n         'laptop_computer', 'lasagna', 'latch', 'lawn_mower', 'leather',\n         'legging_(clothing)', 'Lego', 'legume', 'lemon', 'lemonade',\n         'lettuce', 'license_plate', 'life_buoy', 'life_jacket', 'lightbulb',\n         'lightning_rod', 'lime', 'limousine', 'lion', 'lip_balm', 'liquor',\n         'lizard', 'log', 'lollipop', 'speaker_(stereo_equipment)', 'loveseat',\n         'machine_gun', 'magazine', 'magnet', 'mail_slot', 'mailbox_(at_home)',\n         'mallard', 'mallet', 'mammoth', 'manatee', 'mandarin_orange',\n         'manger', 'manhole', 'map', 'marker', 'martini', 'mascot',\n         'mashed_potato', 'masher', 'mask', 'mast', 'mat_(gym_equipment)',\n         'matchbox', 'mattress', 'measuring_cup', 'measuring_stick',\n         'meatball', 'medicine', 'melon', 'microphone', 'microscope',\n         'microwave_oven', 'milestone', 'milk', 'milk_can', 'milkshake',\n         'minivan', 'mint_candy', 'mirror', 'mitten', 'mixer_(kitchen_tool)',\n         'money', 'monitor_(computer_equipment) computer_monitor', 'monkey',\n         'motor', 'motor_scooter', 'motor_vehicle', 'motorcycle',\n         'mound_(baseball)', 'mouse_(computer_equipment)', 'mousepad',\n         'muffin', 'mug', 'mushroom', 'music_stool', 'musical_instrument',\n         'nailfile', 'napkin', 'neckerchief', 'necklace', 'necktie', 'needle',\n         'nest', 'newspaper', 'newsstand', 'nightshirt',\n         'nosebag_(for_animals)', 'noseband_(for_animals)', 'notebook',\n         'notepad', 'nut', 'nutcracker', 'oar', 'octopus_(food)',\n         'octopus_(animal)', 'oil_lamp', 'olive_oil', 'omelet', 'onion',\n         'orange_(fruit)', 'orange_juice', 'ostrich', 'ottoman', 'oven',\n         'overalls_(clothing)', 'owl', 'packet', 'inkpad', 'pad', 'paddle',\n         'padlock', 'paintbrush', 'painting', 'pajamas', 'palette',\n         'pan_(for_cooking)', 'pan_(metal_container)', 'pancake', 'pantyhose',\n         'papaya', 'paper_plate', 'paper_towel', 'paperback_book',\n         'paperweight', 'parachute', 'parakeet', 'parasail_(sports)',\n         'parasol', 'parchment', 'parka', 'parking_meter', 'parrot',\n         'passenger_car_(part_of_a_train)', 'passenger_ship', 'passport',\n         'pastry', 'patty_(food)', 'pea_(food)', 'peach', 'peanut_butter',\n         'pear', 'peeler_(tool_for_fruit_and_vegetables)', 'wooden_leg',\n         'pegboard', 'pelican', 'pen', 'pencil', 'pencil_box',\n         'pencil_sharpener', 'pendulum', 'penguin', 'pennant', 'penny_(coin)',\n         'pepper', 'pepper_mill', 'perfume', 'persimmon', 'person', 'pet',\n         'pew_(church_bench)', 'phonebook', 'phonograph_record', 'piano',\n         'pickle', 'pickup_truck', 'pie', 'pigeon', 'piggy_bank', 'pillow',\n         'pin_(non_jewelry)', 'pineapple', 'pinecone', 'ping-pong_ball',\n         'pinwheel', 'tobacco_pipe', 'pipe', 'pistol', 'pita_(bread)',\n         'pitcher_(vessel_for_liquid)', 'pitchfork', 'pizza', 'place_mat',\n         'plate', 'platter', 'playpen', 'pliers', 'plow_(farm_equipment)',\n         'plume', 'pocket_watch', 'pocketknife', 'poker_(fire_stirring_tool)',\n         'pole', 'polo_shirt', 'poncho', 'pony', 'pool_table', 'pop_(soda)',\n         'postbox_(public)', 'postcard', 'poster', 'pot', 'flowerpot',\n         'potato', 'potholder', 'pottery', 'pouch', 'power_shovel', 'prawn',\n         'pretzel', 'printer', 'projectile_(weapon)', 'projector', 'propeller',\n         'prune', 'pudding', 'puffer_(fish)', 'puffin', 'pug-dog', 'pumpkin',\n         'puncher', 'puppet', 'puppy', 'quesadilla', 'quiche', 'quilt',\n         'rabbit', 'race_car', 'racket', 'radar', 'radiator', 'radio_receiver',\n         'radish', 'raft', 'rag_doll', 'raincoat', 'ram_(animal)', 'raspberry',\n         'rat', 'razorblade', 'reamer_(juicer)', 'rearview_mirror', 'receipt',\n         'recliner', 'record_player', 'reflector', 'remote_control',\n         'rhinoceros', 'rib_(food)', 'rifle', 'ring', 'river_boat', 'road_map',\n         'robe', 'rocking_chair', 'rodent', 'roller_skate', 'Rollerblade',\n         'rolling_pin', 'root_beer', 'router_(computer_equipment)',\n         'rubber_band', 'runner_(carpet)', 'plastic_bag',\n         'saddle_(on_an_animal)', 'saddle_blanket', 'saddlebag', 'safety_pin',\n         'sail', 'salad', 'salad_plate', 'salami', 'salmon_(fish)',\n         'salmon_(food)', 'salsa', 'saltshaker', 'sandal_(type_of_shoe)',\n         'sandwich', 'satchel', 'saucepan', 'saucer', 'sausage', 'sawhorse',\n         'saxophone', 'scale_(measuring_instrument)', 'scarecrow', 'scarf',\n         'school_bus', 'scissors', 'scoreboard', 'scraper', 'screwdriver',\n         'scrubbing_brush', 'sculpture', 'seabird', 'seahorse', 'seaplane',\n         'seashell', 'sewing_machine', 'shaker', 'shampoo', 'shark',\n         'sharpener', 'Sharpie', 'shaver_(electric)', 'shaving_cream', 'shawl',\n         'shears', 'sheep', 'shepherd_dog', 'sherbert', 'shield', 'shirt',\n         'shoe', 'shopping_bag', 'shopping_cart', 'short_pants', 'shot_glass',\n         'shoulder_bag', 'shovel', 'shower_head', 'shower_cap',\n         'shower_curtain', 'shredder_(for_paper)', 'signboard', 'silo', 'sink',\n         'skateboard', 'skewer', 'ski', 'ski_boot', 'ski_parka', 'ski_pole',\n         'skirt', 'skullcap', 'sled', 'sleeping_bag', 'sling_(bandage)',\n         'slipper_(footwear)', 'smoothie', 'snake', 'snowboard', 'snowman',\n         'snowmobile', 'soap', 'soccer_ball', 'sock', 'sofa', 'softball',\n         'solar_array', 'sombrero', 'soup', 'soup_bowl', 'soupspoon',\n         'sour_cream', 'soya_milk', 'space_shuttle', 'sparkler_(fireworks)',\n         'spatula', 'spear', 'spectacles', 'spice_rack', 'spider', 'crawfish',\n         'sponge', 'spoon', 'sportswear', 'spotlight', 'squid_(food)',\n         'squirrel', 'stagecoach', 'stapler_(stapling_machine)', 'starfish',\n         'statue_(sculpture)', 'steak_(food)', 'steak_knife', 'steering_wheel',\n         'stepladder', 'step_stool', 'stereo_(sound_system)', 'stew',\n         'stirrer', 'stirrup', 'stool', 'stop_sign', 'brake_light', 'stove',\n         'strainer', 'strap', 'straw_(for_drinking)', 'strawberry',\n         'street_sign', 'streetlight', 'string_cheese', 'stylus', 'subwoofer',\n         'sugar_bowl', 'sugarcane_(plant)', 'suit_(clothing)', 'sunflower',\n         'sunglasses', 'sunhat', 'surfboard', 'sushi', 'mop', 'sweat_pants',\n         'sweatband', 'sweater', 'sweatshirt', 'sweet_potato', 'swimsuit',\n         'sword', 'syringe', 'Tabasco_sauce', 'table-tennis_table', 'table',\n         'table_lamp', 'tablecloth', 'tachometer', 'taco', 'tag', 'taillight',\n         'tambourine', 'army_tank', 'tank_(storage_vessel)',\n         'tank_top_(clothing)', 'tape_(sticky_cloth_or_paper)', 'tape_measure',\n         'tapestry', 'tarp', 'tartan', 'tassel', 'tea_bag', 'teacup',\n         'teakettle', 'teapot', 'teddy_bear', 'telephone', 'telephone_booth',\n         'telephone_pole', 'telephoto_lens', 'television_camera',\n         'television_set', 'tennis_ball', 'tennis_racket', 'tequila',\n         'thermometer', 'thermos_bottle', 'thermostat', 'thimble', 'thread',\n         'thumbtack', 'tiara', 'tiger', 'tights_(clothing)', 'timer',\n         'tinfoil', 'tinsel', 'tissue_paper', 'toast_(food)', 'toaster',\n         'toaster_oven', 'toilet', 'toilet_tissue', 'tomato', 'tongs',\n         'toolbox', 'toothbrush', 'toothpaste', 'toothpick', 'cover',\n         'tortilla', 'tow_truck', 'towel', 'towel_rack', 'toy',\n         'tractor_(farm_equipment)', 'traffic_light', 'dirt_bike',\n         'trailer_truck', 'train_(railroad_vehicle)', 'trampoline', 'tray',\n         'trench_coat', 'triangle_(musical_instrument)', 'tricycle', 'tripod',\n         'trousers', 'truck', 'truffle_(chocolate)', 'trunk', 'vat', 'turban',\n         'turkey_(food)', 'turnip', 'turtle', 'turtleneck_(clothing)',\n         'typewriter', 'umbrella', 'underwear', 'unicycle', 'urinal', 'urn',\n         'vacuum_cleaner', 'vase', 'vending_machine', 'vent', 'vest',\n         'videotape', 'vinegar', 'violin', 'vodka', 'volleyball', 'vulture',\n         'waffle', 'waffle_iron', 'wagon', 'wagon_wheel', 'walking_stick',\n         'wall_clock', 'wall_socket', 'wallet', 'walrus', 'wardrobe',\n         'washbasin', 'automatic_washer', 'watch', 'water_bottle',\n         'water_cooler', 'water_faucet', 'water_heater', 'water_jug',\n         'water_gun', 'water_scooter', 'water_ski', 'water_tower',\n         'watering_can', 'watermelon', 'weathervane', 'webcam', 'wedding_cake',\n         'wedding_ring', 'wet_suit', 'wheel', 'wheelchair', 'whipped_cream',\n         'whistle', 'wig', 'wind_chime', 'windmill', 'window_box_(for_plants)',\n         'windshield_wiper', 'windsock', 'wine_bottle', 'wine_bucket',\n         'wineglass', 'blinder_(for_horses)', 'wok', 'wolf', 'wooden_spoon',\n         'wreath', 'wrench', 'wristband', 'wristlet', 'yacht', 'yogurt',\n         'yoke_(animal_equipment)', 'zebra', 'zucchini'),\n        'palette':\n        None\n    }\n\n    def get_data_info(self, idx: int) -> dict:\n        \"\"\"Get annotation by index and automatically call ``full_init`` if the\n        dataset has not been fully initialized.\n\n        Args:\n            idx (int): The index of data.\n\n        Returns:\n            dict: The idx-th annotation of the dataset.\n        \"\"\"\n        if self.serialize_data:\n            start_addr = 0 if idx == 0 else self.data_address[idx - 1].item()\n            end_addr = self.data_address[idx].item()\n            bytes = memoryview(\n                self.data_bytes[start_addr:end_addr])  # type: ignore\n            data_info = pickle.loads(bytes)  # type: ignore\n        else:\n            data_info = copy.deepcopy(self.data_list[idx])\n\n        # Some codebase needs `sample_idx` of data information. Here we convert\n        # the idx to a positive number and save it in data information.\n        if idx >= 0:\n            data_info['sample_idx'] = idx\n        else:\n            data_info['sample_idx'] = len(self) + idx\n\n        return data_info\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        try:\n            import lvis\n            if getattr(lvis, '__version__', '0') >= '10.5.3':\n                warnings.warn(\n                    'mmlvis is deprecated, please install official lvis-api by \"pip install git+https://github.com/lvis-dataset/lvis-api.git\"',  # noqa: E501\n                    UserWarning)\n            from lvis import LVIS\n        except ImportError:\n            raise ImportError(\n                'Package lvis is not installed. Please run \"pip install git+https://github.com/lvis-dataset/lvis-api.git\".'  # noqa: E501\n            )\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.lvis = LVIS(local_path)\n        self.cat_ids = self.lvis.get_cat_ids()\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.lvis.cat_img_map)\n        img_ids = self.lvis.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.lvis.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n\n            ann_ids = self.lvis.get_ann_ids(img_ids=[img_id])\n            total_ann_ids.extend(ann_ids)\n            parsed_data_info = self.parse_data_info(\n                {'raw_img_info': raw_img_info})\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.lvis\n        # print(data_list)\n        return data_list\n\n    def parse_data_info(self, raw_data_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            raw_data_info (dict): Raw data information load from ``ann_file``\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        img_info = raw_data_info['raw_img_info']\n\n        data_info = {}\n\n        # TODO: need to change data_prefix['img'] to data_prefix['img_path']\n        img_path = osp.join(self.data_prefix['img'], img_info['file_name'])\n        if self.data_prefix.get('seg', None):\n            seg_map_path = osp.join(\n                self.data_prefix['seg'],\n                img_info['file_name'].rsplit('.', 1)[0] + self.seg_map_suffix)\n        else:\n            seg_map_path = None\n        data_info['img_path'] = img_path\n        data_info['img_id'] = img_info['img_id']\n        data_info['seg_map_path'] = seg_map_path\n        data_info['height'] = img_info['height']\n        data_info['width'] = img_info['width']\n\n        if self.return_classes:\n            data_info['text'] = self.metainfo['classes']\n            data_info['custom_entities'] = True\n\n        instances = []\n        image_labels = [\n            self.cat2label[x] for x in img_info['pos_category_ids']\n        ]\n        for image_label in image_labels:\n            instance = {}\n            instance['bbox_label'] = image_label\n            instances.append(instance)\n        data_info['instances'] = instances\n\n        return data_info\n\n    def get_cat_ids(self, idx: int) -> List[int]:\n        \"\"\"Get COCO category ids by index.\n\n        Args:\n            idx (int): Index of data.\n\n        Returns:\n            List[int]: All categories in the image of specified index.\n        \"\"\"\n        data_info = self.get_data_info(idx)\n        image_labels = []\n        for instance in data_info['instances']:\n            image_labels.append(instance['bbox_label'])\n\n        return image_labels\n"
  },
  {
    "path": "projects/Detic_new/detic/iou_loss.py",
    "content": "import torch\nfrom torch import nn\n\n\n# support calculate  IOULoss with box_pred\nclass IOULoss(nn.Module):\n\n    def __init__(self, loc_loss_type='iou'):\n        super(IOULoss, self).__init__()\n        self.loc_loss_type = loc_loss_type\n\n    def forward(self, pred, target, weight=None, reduction='sum'):\n        pred_left = pred[:, 0]\n        pred_top = pred[:, 1]\n        pred_right = pred[:, 2]\n        pred_bottom = pred[:, 3]\n\n        target_left = target[:, 0]\n        target_top = target[:, 1]\n        target_right = target[:, 2]\n        target_bottom = target[:, 3]\n\n        target_aera = (target_left + target_right) * (\n            target_top + target_bottom)\n        pred_aera = (pred_left + pred_right) * (pred_top + pred_bottom)\n\n        w_intersect = torch.min(pred_left, target_left) + torch.min(\n            pred_right, target_right)\n        h_intersect = torch.min(pred_bottom, target_bottom) + torch.min(\n            pred_top, target_top)\n\n        g_w_intersect = torch.max(pred_left, target_left) + torch.max(\n            pred_right, target_right)\n        g_h_intersect = torch.max(pred_bottom, target_bottom) + torch.max(\n            pred_top, target_top)\n        ac_uion = g_w_intersect * g_h_intersect\n\n        area_intersect = w_intersect * h_intersect\n        area_union = target_aera + pred_aera - area_intersect\n\n        ious = (area_intersect + 1.0) / (area_union + 1.0)\n        gious = ious - (ac_uion - area_union) / ac_uion\n        if self.loc_loss_type == 'iou':\n            losses = -torch.log(ious)\n        elif self.loc_loss_type == 'linear_iou':\n            losses = 1 - ious\n        elif self.loc_loss_type == 'giou':\n            losses = 1 - gious\n        else:\n            raise NotImplementedError\n\n        if weight is not None:\n            losses = losses * weight\n        else:\n            losses = losses\n\n        if reduction == 'sum':\n            return losses.sum()\n        elif reduction == 'batch':\n            return losses.sum(dim=[1])\n        elif reduction == 'none':\n            return losses\n        else:\n            raise NotImplementedError\n\n\ndef giou_loss(\n    boxes1: torch.Tensor,\n    boxes2: torch.Tensor,\n    reduction: str = 'none',\n    eps: float = 1e-7,\n) -> torch.Tensor:\n    \"\"\"Generalized Intersection over Union Loss (Hamid Rezatofighi et.\n\n    al)\n    https://arxiv.org/abs/1902.09630\n    Gradient-friendly IoU loss with an additional penalty that is\n    non-zero when the boxes do not overlap and scales with the size\n    of their smallest enclosing box. This loss is symmetric, so the\n    boxes1 and boxes2 arguments are interchangeable.\n    Args:\n        boxes1, boxes2 (Tensor): box locations in XYXY format, shape\n        (N, 4) or (4,).\n        reduction: 'none' | 'mean' | 'sum'\n                 'none': No reduction will be applied to the output.\n                 'mean': The output will be averaged.\n                 'sum': The output will be summed.\n        eps (float): small number to prevent division by zero\n    \"\"\"\n\n    x1, y1, x2, y2 = boxes1.unbind(dim=-1)\n    x1g, y1g, x2g, y2g = boxes2.unbind(dim=-1)\n\n    assert (x2 >= x1).all(), 'bad box: x1 larger than x2'\n    assert (y2 >= y1).all(), 'bad box: y1 larger than y2'\n\n    # Intersection keypoints\n    xkis1 = torch.max(x1, x1g)\n    ykis1 = torch.max(y1, y1g)\n    xkis2 = torch.min(x2, x2g)\n    ykis2 = torch.min(y2, y2g)\n\n    intsctk = torch.zeros_like(x1)\n    mask = (ykis2 > ykis1) & (xkis2 > xkis1)\n    intsctk[mask] = (xkis2[mask] - xkis1[mask]) * (ykis2[mask] - ykis1[mask])\n    unionk = (x2 - x1) * (y2 - y1) + (x2g - x1g) * (y2g - y1g) - intsctk\n    iouk = intsctk / (unionk + eps)\n\n    # smallest enclosing box\n    xc1 = torch.min(x1, x1g)\n    yc1 = torch.min(y1, y1g)\n    xc2 = torch.max(x2, x2g)\n    yc2 = torch.max(y2, y2g)\n\n    area_c = (xc2 - xc1) * (yc2 - yc1)\n    miouk = iouk - ((area_c - unionk) / (area_c + eps))\n\n    loss = 1 - miouk\n\n    if reduction == 'mean':\n        loss = loss.mean()\n    elif reduction == 'sum':\n        loss = loss.sum()\n\n    return loss\n"
  },
  {
    "path": "projects/Detic_new/detic/zero_shot_classifier.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\nimport numpy as np\nimport torch\nfrom torch import nn\nfrom torch.nn import functional as F\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass ZeroShotClassifier(nn.Module):\n\n    def __init__(\n        self,\n        in_features: int,\n        out_features: int,  # num_classes\n        zs_weight_path: str,\n        zs_weight_dim: int = 512,\n        use_bias: float = 0.0,\n        norm_weight: bool = True,\n        norm_temperature: float = 50.0,\n    ):\n        super().__init__()\n        num_classes = out_features\n        self.norm_weight = norm_weight\n        self.norm_temperature = norm_temperature\n\n        self.use_bias = use_bias < 0\n        if self.use_bias:\n            self.cls_bias = nn.Parameter(torch.ones(1) * use_bias)\n\n        self.linear = nn.Linear(in_features, zs_weight_dim)\n\n        if zs_weight_path == 'rand':\n            zs_weight = torch.randn((zs_weight_dim, num_classes))\n            nn.init.normal_(zs_weight, std=0.01)\n        else:\n            zs_weight = torch.tensor(\n                np.load(zs_weight_path),\n                dtype=torch.float32).permute(1, 0).contiguous()  # D x C\n        zs_weight = torch.cat(\n            [zs_weight, zs_weight.new_zeros(\n                (zs_weight_dim, 1))], dim=1)  # D x (C + 1)\n\n        if self.norm_weight:\n            zs_weight = F.normalize(zs_weight, p=2, dim=0)\n\n        if zs_weight_path == 'rand':\n            self.zs_weight = nn.Parameter(zs_weight)\n        else:\n            self.register_buffer('zs_weight', zs_weight)\n\n        assert self.zs_weight.shape[1] == num_classes + 1, self.zs_weight.shape\n\n    def forward(self, x, classifier=None):\n        '''\n        Inputs:\n            x: B x D'\n            classifier_info: (C', C' x D)\n        '''\n        x = self.linear(x)\n        if classifier is not None:\n            zs_weight = classifier.permute(1, 0).contiguous()  # D x C'\n            zs_weight = F.normalize(zs_weight, p=2, dim=0) \\\n                if self.norm_weight else zs_weight\n        else:\n            zs_weight = self.zs_weight\n        if self.norm_weight:\n            x = self.norm_temperature * F.normalize(x, p=2, dim=1)\n        x = torch.mm(x, zs_weight)\n        if self.use_bias:\n            x = x + self.cls_bias\n        return x\n"
  },
  {
    "path": "projects/DiffusionDet/README.md",
    "content": "## Description\n\nThis is an implementation of [DiffusionDet](https://github.com/ShoufaChen/DiffusionDet) based on [MMDetection](https://github.com/open-mmlab/mmdetection/tree/main), [MMCV](https://github.com/open-mmlab/mmcv), and [MMEngine](https://github.com/open-mmlab/mmengine).\n\n<center>\n<img src=\"https://user-images.githubusercontent.com/48282753/211472911-c84d658a-952b-4608-8b91-9ac932cbf2e2.png\">\n</center>\n\n## Usage\n\n<!-- For a typical model, this section should contain the commands for training and testing. You are also suggested to dump your environment specification to env.yml by `conda env export > env.yml`. -->\n\n### Comparison of results\n\n1. Download the [DiffusionDet released model](https://github.com/ShoufaChen/DiffusionDet#models).\n\n2. Convert model from DiffusionDet version to MMDetection version. We give a [sample script](model_converters/diffusiondet_resnet_to_mmdet.py)\n   to convert `DiffusionDet-resnet50` model. Users can download the corresponding models from [here](https://github.com/ShoufaChen/DiffusionDet/releases/download/v0.1/diffdet_coco_res50.pth).\n\n   ```shell\n   python projects/DiffusionDet/model_converters/diffusiondet_resnet_to_mmdet.py ${DiffusionDet ckpt path} ${MMDetectron ckpt path}\n   ```\n\n3. Testing the model in MMDetection.\n\n   ```shell\n   python tools/test.py projects/DiffusionDet/configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py ${CHECKPOINT_PATH}\n   ```\n\n**Note:** During inference time, DiffusionDet will randomly generate noisy boxes,\nwhich may affect the AP results. If users want to get the same result every inference time, setting seed is a good way.\nWe give a table to compare the inference results on `ResNet50-500-proposals` between DiffusionDet and MMDetection.\n\n|                                                         Config                                                          | Step |    AP     |\n| :---------------------------------------------------------------------------------------------------------------------: | :--: | :-------: |\n| [DiffusionDet](https://github.com/ShoufaChen/DiffusionDet/blob/main/configs/diffdet.coco.res50.yaml) (released results) |  1   |   45.5    |\n|      [DiffusionDet](https://github.com/ShoufaChen/DiffusionDet/blob/main/configs/diffdet.coco.res50.yaml) (seed=0)      |  1   |   45.66   |\n|         [MMDetection](configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py) (seed=0)          |  1   |   45.7    |\n|       [MMDetection](configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py) (random seed)       |  1   | 45.6~45.8 |\n| [DiffusionDet](https://github.com/ShoufaChen/DiffusionDet/blob/main/configs/diffdet.coco.res50.yaml) (released results) |  4   |   46.1    |\n|      [DiffusionDet](https://github.com/ShoufaChen/DiffusionDet/blob/main/configs/diffdet.coco.res50.yaml) (seed=0)      |  4   |   46.38   |\n|         [MMDetection](configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py) (seed=0)          |  4   |   46.4    |\n|       [MMDetection](configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py) (random seed)       |  4   | 46.2~46.4 |\n\n- `seed=0` means hard set seed before generating random boxes.\n  ```python\n  # hard set seed=0 before generating random boxes\n  seed = 0\n  random.seed(seed)\n  torch.manual_seed(seed)\n  # torch.cuda.manual_seed(seed)\n  torch.cuda.manual_seed_all(seed)\n  ...\n  noise_bboxes_raw = torch.randn(\n      (self.num_proposals, 4),\n      device=device)\n  ...\n  ```\n- `random seed` means do not hard set seed before generating random boxes.\n\n### Training commands\n\nIn MMDetection's root directory, run the following command to train the model:\n\n```bash\npython tools/train.py projects/DiffusionDet/configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py\n```\n\nFor multi-gpu training, run:\n\n```bash\npython -m torch.distributed.launch --nnodes=1 --node_rank=0 --nproc_per_node=${NUM_GPUS} --master_port=29506 --master_addr=\"127.0.0.1\" tools/train.py projects/DiffusionDet/configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py\n```\n\n### Testing commands\n\nIn MMDetection's root directory, run the following command to test the model:\n\n```bash\n# for 1 step inference\n# test command\npython tools/test.py projects/DiffusionDet/configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py ${CHECKPOINT_PATH}\n\n# for 4 steps inference\n\n# test command\npython tools/test.py projects/DiffusionDet/configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py ${CHECKPOINT_PATH} --cfg-options model.bbox_head.sampling_timesteps=4\n```\n\n**Note:** There is no difference between 1 step or 4 steps (or other multi-step) during training. Users can set different steps during inference through `--cfg-options model.bbox_head.sampling_timesteps=${STEPS}`, but larger `sampling_timesteps` will affect the inference time.\n\n## Results\n\nHere we provide the baseline version of DiffusionDet with ResNet50 backbone.\n\nTo find more variants, please visit the [official model zoo](https://github.com/ShoufaChen/DiffusionDet#models).\n\n| Backbone |  Style  | Lr schd | AP (Step=1) | AP (Step=4) |                                           Config                                           |                                                                                                                                                                                                                                      Download                                                                                                                                                                                                                                      |\n| :------: | :-----: | :-----: | :---------: | :---------: | :----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | PyTorch |  450k   |    44.5     |    46.2     | [config](./configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/diffusiondet/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco_20230215_090925-7d6ed504.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/diffusiondet/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco_20230215_090925.log.json) |\n\n## License\n\nDiffusionDet is under the [CC-BY-NC 4.0 license](https://github.com/ShoufaChen/DiffusionDet/blob/main/LICENSE). Users should be careful about adopting these features in any commercial matters.\n\n## Citation\n\nIf you find DiffusionDet is useful in your research or applications, please consider giving a star 🌟 to the [official repository](https://github.com/ShoufaChen/DiffusionDet) and citing DiffusionDet by the following BibTeX entry.\n\n```BibTeX\n@article{chen2022diffusiondet,\n      title={DiffusionDet: Diffusion Model for Object Detection},\n      author={Chen, Shoufa and Sun, Peize and Song, Yibing and Luo, Ping},\n      journal={arXiv preprint arXiv:2211.09788},\n      year={2022}\n}\n```\n\n## Checklist\n\n<!-- Here is a checklist illustrating a usual development workflow of a successful project, and also serves as an overview of this project's progress. The PIC (person in charge) or contributors of this project should check all the items that they believe have been finished, which will further be verified by codebase maintainers via a PR.\nOpenMMLab's maintainer will review the code to ensure the project's quality. Reaching the first milestone means that this project suffices the minimum requirement of being merged into 'projects/'. But this project is only eligible to become a part of the core package upon attaining the last milestone.\nNote that keeping this section up-to-date is crucial not only for this project's developers but the entire community, since there might be some other contributors joining this project and deciding their starting point from this list. It also helps maintainers accurately estimate time and effort on further code polishing, if needed.\nA project does not necessarily have to be finished in a single PR, but it's essential for the project to at least reach the first milestone in its very first PR. -->\n\n- [x] Milestone 1: PR-ready, and acceptable to be one of the `projects/`.\n\n  - [x] Finish the code\n\n    <!-- The code's design shall follow existing interfaces and convention. For example, each model component should be registered into `mmdet.registry.MODELS` and configurable via a config file. -->\n\n  - [x] Basic docstrings & proper citation\n\n    <!-- Each major object should contain a docstring, describing its functionality and arguments. If you have adapted the code from other open-source projects, don't forget to cite the source project in docstring and make sure your behavior is not against its license. Typically, we do not accept any code snippet under GPL license. [A Short Guide to Open Source Licenses](https://medium.com/nationwide-technology/a-short-guide-to-open-source-licenses-cf5b1c329edd) -->\n\n  - [x] Test-time correctness\n\n    <!-- If you are reproducing the result from a paper, make sure your model's inference-time performance matches that in the original paper. The weights usually could be obtained by simply renaming the keys in the official pre-trained weights. This test could be skipped though, if you are able to prove the training-time correctness and check the second milestone. -->\n\n  - [x] A full README\n\n    <!-- As this template does. -->\n\n- [x] Milestone 2: Indicates a successful model implementation.\n\n  - [x] Training-time correctness\n\n    <!-- If you are reproducing the result from a paper, checking this item means that you should have trained your model from scratch based on the original paper's specification and verified that the final result matches the report within a minor error range. -->\n\n- [ ] Milestone 3: Good to be a part of our core package!\n\n  - [ ] Type hints and docstrings\n\n    <!-- Ideally *all* the methods should have [type hints](https://www.pythontutorial.net/python-basics/python-type-hints/) and [docstrings](https://google.github.io/styleguide/pyguide.html#381-docstrings). [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/mmdet/datasets/transforms/transforms.py#L41-L169) -->\n\n  - [ ] Unit tests\n\n    <!-- Unit tests for each module are required. [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/tests/test_datasets/test_transforms/test_transforms.py#L35-L88) -->\n\n  - [ ] Code polishing\n\n    <!-- Refactor your code according to reviewer's comment. -->\n\n  - [ ] Metafile.yml\n\n    <!-- It will be parsed by MIM and Inferencer. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/metafile.yml) -->\n\n- [ ] Move your modules into the core package following the codebase's file hierarchy structure.\n\n  <!-- In particular, you may have to refactor this README into a standard one. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/README.md) -->\n\n- [ ] Refactor your modules into the core package following the codebase's file hierarchy structure.\n"
  },
  {
    "path": "projects/DiffusionDet/configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py",
    "content": "_base_ = [\n    'mmdet::_base_/datasets/coco_detection.py',\n    'mmdet::_base_/schedules/schedule_1x.py',\n    'mmdet::_base_/default_runtime.py'\n]\n\ncustom_imports = dict(\n    imports=['projects.DiffusionDet.diffusiondet'], allow_failed_imports=False)\n\n# model settings\nmodel = dict(\n    type='DiffusionDet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='FPN',\n        in_channels=[256, 512, 1024, 2048],\n        out_channels=256,\n        num_outs=4),\n    bbox_head=dict(\n        type='DynamicDiffusionDetHead',\n        num_classes=80,\n        feat_channels=256,\n        num_proposals=500,\n        num_heads=6,\n        deep_supervision=True,\n        prior_prob=0.01,\n        snr_scale=2.0,\n        sampling_timesteps=1,\n        ddim_sampling_eta=1.0,\n        single_head=dict(\n            type='SingleDiffusionDetHead',\n            num_cls_convs=1,\n            num_reg_convs=3,\n            dim_feedforward=2048,\n            num_heads=8,\n            dropout=0.0,\n            act_cfg=dict(type='ReLU', inplace=True),\n            dynamic_conv=dict(dynamic_dim=64, dynamic_num=2)),\n        roi_extractor=dict(\n            type='SingleRoIExtractor',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256,\n            featmap_strides=[4, 8, 16, 32]),\n        # criterion\n        criterion=dict(\n            type='DiffusionDetCriterion',\n            num_classes=80,\n            assigner=dict(\n                type='DiffusionDetMatcher',\n                match_costs=[\n                    dict(\n                        type='FocalLossCost',\n                        alpha=0.25,\n                        gamma=2.0,\n                        weight=2.0,\n                        eps=1e-8),\n                    dict(type='BBoxL1Cost', weight=5.0, box_format='xyxy'),\n                    dict(type='IoUCost', iou_mode='giou', weight=2.0)\n                ],\n                center_radius=2.5,\n                candidate_topk=5),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                alpha=0.25,\n                gamma=2.0,\n                reduction='sum',\n                loss_weight=2.0),\n            loss_bbox=dict(type='L1Loss', reduction='sum', loss_weight=5.0),\n            loss_giou=dict(type='GIoULoss', reduction='sum',\n                           loss_weight=2.0))),\n    test_cfg=dict(\n        use_nms=True,\n        score_thr=0.5,\n        min_bbox_size=0,\n        nms=dict(type='nms', iou_threshold=0.5),\n    ))\n\nbackend = 'pillow'\ntrain_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=_base_.backend_args,\n        imdecode_backend=backend),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[[\n            dict(\n                type='RandomChoiceResize',\n                scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                        (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                        (736, 1333), (768, 1333), (800, 1333)],\n                keep_ratio=True,\n                backend=backend),\n        ],\n                    [\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(400, 1333), (500, 1333), (600, 1333)],\n                            keep_ratio=True,\n                            backend=backend),\n                        dict(\n                            type='RandomCrop',\n                            crop_type='absolute_range',\n                            crop_size=(384, 600),\n                            allow_negative_crop=True),\n                        dict(\n                            type='RandomChoiceResize',\n                            scales=[(480, 1333), (512, 1333), (544, 1333),\n                                    (576, 1333), (608, 1333), (640, 1333),\n                                    (672, 1333), (704, 1333), (736, 1333),\n                                    (768, 1333), (800, 1333)],\n                            keep_ratio=True,\n                            backend=backend)\n                    ]]),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args=_base_.backend_args,\n        imdecode_backend=backend),\n    dict(type='Resize', scale=(1333, 800), keep_ratio=True, backend=backend),\n    # If you don't have a gt annotation, delete the pipeline\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\ntrain_dataloader = dict(\n    sampler=dict(type='InfiniteSampler'),\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False, min_size=1e-5),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        _delete_=True, type='AdamW', lr=0.000025, weight_decay=0.0001),\n    clip_grad=dict(max_norm=1.0, norm_type=2))\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=450000,\n    val_interval=75000)\n\n# learning rate\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.01, by_epoch=False, begin=0, end=1000),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=450000,\n        by_epoch=False,\n        milestones=[350000, 420000],\n        gamma=0.1)\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=False, interval=75000, max_keep_ckpts=3))\nlog_processor = dict(by_epoch=False)\n"
  },
  {
    "path": "projects/DiffusionDet/diffusiondet/__init__.py",
    "content": "from .diffusiondet import DiffusionDet\nfrom .head import (DynamicConv, DynamicDiffusionDetHead,\n                   SingleDiffusionDetHead, SinusoidalPositionEmbeddings)\nfrom .loss import DiffusionDetCriterion, DiffusionDetMatcher\n\n__all__ = [\n    'DiffusionDet', 'DynamicDiffusionDetHead', 'SingleDiffusionDetHead',\n    'SinusoidalPositionEmbeddings', 'DynamicConv', 'DiffusionDetCriterion',\n    'DiffusionDetMatcher'\n]\n"
  },
  {
    "path": "projects/DiffusionDet/diffusiondet/diffusiondet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.models import SingleStageDetector\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass DiffusionDet(SingleStageDetector):\n    \"\"\"Implementation of `DiffusionDet <>`_\"\"\"\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "projects/DiffusionDet/diffusiondet/head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n# Modified from https://github.com/ShoufaChen/DiffusionDet/blob/main/diffusiondet/detector.py   # noqa\n# Modified from https://github.com/ShoufaChen/DiffusionDet/blob/main/diffusiondet/head.py   # noqa\n\n# This work is licensed under the CC-BY-NC 4.0 License.\n# Users should be careful about adopting these features in any commercial matters.    # noqa\n# For more details, please refer to https://github.com/ShoufaChen/DiffusionDet/blob/main/LICENSE    # noqa\n\nimport copy\nimport math\nimport random\nimport warnings\nfrom typing import Tuple\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import build_activation_layer\nfrom mmcv.ops import batched_nms\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures import SampleList\nfrom mmdet.structures.bbox import (bbox2roi, bbox_cxcywh_to_xyxy,\n                                   bbox_xyxy_to_cxcywh, get_box_wh,\n                                   scale_boxes)\nfrom mmdet.utils import InstanceList\n\n_DEFAULT_SCALE_CLAMP = math.log(100000.0 / 16)\n\n\ndef cosine_beta_schedule(timesteps, s=0.008):\n    \"\"\"Cosine schedule as proposed in\n    https://openreview.net/forum?id=-NEXDKk8gZ.\"\"\"\n    steps = timesteps + 1\n    x = torch.linspace(0, timesteps, steps, dtype=torch.float64)\n    alphas_cumprod = torch.cos(\n        ((x / timesteps) + s) / (1 + s) * math.pi * 0.5)**2\n    alphas_cumprod = alphas_cumprod / alphas_cumprod[0]\n    betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1])\n    return torch.clip(betas, 0, 0.999)\n\n\ndef extract(a, t, x_shape):\n    \"\"\"extract the appropriate t index for a batch of indices.\"\"\"\n    batch_size = t.shape[0]\n    out = a.gather(-1, t)\n    return out.reshape(batch_size, *((1, ) * (len(x_shape) - 1)))\n\n\nclass SinusoidalPositionEmbeddings(nn.Module):\n\n    def __init__(self, dim):\n        super().__init__()\n        self.dim = dim\n\n    def forward(self, time):\n        device = time.device\n        half_dim = self.dim // 2\n        embeddings = math.log(10000) / (half_dim - 1)\n        embeddings = torch.exp(\n            torch.arange(half_dim, device=device) * -embeddings)\n        embeddings = time[:, None] * embeddings[None, :]\n        embeddings = torch.cat((embeddings.sin(), embeddings.cos()), dim=-1)\n        return embeddings\n\n\n@MODELS.register_module()\nclass DynamicDiffusionDetHead(nn.Module):\n\n    def __init__(self,\n                 num_classes=80,\n                 feat_channels=256,\n                 num_proposals=500,\n                 num_heads=6,\n                 prior_prob=0.01,\n                 snr_scale=2.0,\n                 timesteps=1000,\n                 sampling_timesteps=1,\n                 self_condition=False,\n                 box_renewal=True,\n                 use_ensemble=True,\n                 deep_supervision=True,\n                 ddim_sampling_eta=1.0,\n                 criterion=dict(\n                     type='DiffusionDetCriterion',\n                     num_classes=80,\n                     assigner=dict(\n                         type='DiffusionDetMatcher',\n                         match_costs=[\n                             dict(\n                                 type='FocalLossCost',\n                                 alpha=2.0,\n                                 gamma=0.25,\n                                 weight=2.0),\n                             dict(\n                                 type='BBoxL1Cost',\n                                 weight=5.0,\n                                 box_format='xyxy'),\n                             dict(type='IoUCost', iou_mode='giou', weight=2.0)\n                         ],\n                         center_radius=2.5,\n                         candidate_topk=5),\n                 ),\n                 single_head=dict(\n                     type='DiffusionDetHead',\n                     num_cls_convs=1,\n                     num_reg_convs=3,\n                     dim_feedforward=2048,\n                     num_heads=8,\n                     dropout=0.0,\n                     act_cfg=dict(type='ReLU'),\n                     dynamic_conv=dict(dynamic_dim=64, dynamic_num=2)),\n                 roi_extractor=dict(\n                     type='SingleRoIExtractor',\n                     roi_layer=dict(\n                         type='RoIAlign', output_size=7, sampling_ratio=2),\n                     out_channels=256,\n                     featmap_strides=[4, 8, 16, 32]),\n                 test_cfg=None,\n                 **kwargs) -> None:\n        super().__init__()\n        self.roi_extractor = MODELS.build(roi_extractor)\n\n        self.num_classes = num_classes\n        self.num_classes = num_classes\n        self.feat_channels = feat_channels\n        self.num_proposals = num_proposals\n        self.num_heads = num_heads\n        # Build Diffusion\n        assert isinstance(timesteps, int), 'The type of `timesteps` should ' \\\n                                           f'be int but got {type(timesteps)}'\n        assert sampling_timesteps <= timesteps\n        self.timesteps = timesteps\n        self.sampling_timesteps = sampling_timesteps\n        self.snr_scale = snr_scale\n\n        self.ddim_sampling = self.sampling_timesteps < self.timesteps\n        self.ddim_sampling_eta = ddim_sampling_eta\n        self.self_condition = self_condition\n        self.box_renewal = box_renewal\n        self.use_ensemble = use_ensemble\n\n        self._build_diffusion()\n\n        # Build assigner\n        assert criterion.get('assigner', None) is not None\n        assigner = TASK_UTILS.build(criterion.get('assigner'))\n        # Init parameters.\n        self.use_focal_loss = assigner.use_focal_loss\n        self.use_fed_loss = assigner.use_fed_loss\n\n        # build criterion\n        criterion.update(deep_supervision=deep_supervision)\n        self.criterion = TASK_UTILS.build(criterion)\n\n        # Build Dynamic Head.\n        single_head_ = single_head.copy()\n        single_head_num_classes = single_head_.get('num_classes', None)\n        if single_head_num_classes is None:\n            single_head_.update(num_classes=num_classes)\n        else:\n            if single_head_num_classes != num_classes:\n                warnings.warn(\n                    'The `num_classes` of `DynamicDiffusionDetHead` and '\n                    '`SingleDiffusionDetHead` should be same, changing '\n                    f'`single_head.num_classes` to {num_classes}')\n                single_head_.update(num_classes=num_classes)\n\n        single_head_feat_channels = single_head_.get('feat_channels', None)\n        if single_head_feat_channels is None:\n            single_head_.update(feat_channels=feat_channels)\n        else:\n            if single_head_feat_channels != feat_channels:\n                warnings.warn(\n                    'The `feat_channels` of `DynamicDiffusionDetHead` and '\n                    '`SingleDiffusionDetHead` should be same, changing '\n                    f'`single_head.feat_channels` to {feat_channels}')\n                single_head_.update(feat_channels=feat_channels)\n\n        default_pooler_resolution = roi_extractor['roi_layer'].get(\n            'output_size')\n        assert default_pooler_resolution is not None\n        single_head_pooler_resolution = single_head_.get('pooler_resolution')\n        if single_head_pooler_resolution is None:\n            single_head_.update(pooler_resolution=default_pooler_resolution)\n        else:\n            if single_head_pooler_resolution != default_pooler_resolution:\n                warnings.warn(\n                    'The `pooler_resolution` of `DynamicDiffusionDetHead` '\n                    'and `SingleDiffusionDetHead` should be same, changing '\n                    f'`single_head.pooler_resolution` to {num_classes}')\n                single_head_.update(\n                    pooler_resolution=default_pooler_resolution)\n\n        single_head_.update(\n            use_focal_loss=self.use_focal_loss, use_fed_loss=self.use_fed_loss)\n        single_head_module = MODELS.build(single_head_)\n\n        self.num_heads = num_heads\n        self.head_series = nn.ModuleList(\n            [copy.deepcopy(single_head_module) for _ in range(num_heads)])\n\n        self.deep_supervision = deep_supervision\n\n        # Gaussian random feature embedding layer for time\n        time_dim = feat_channels * 4\n        self.time_mlp = nn.Sequential(\n            SinusoidalPositionEmbeddings(feat_channels),\n            nn.Linear(feat_channels, time_dim), nn.GELU(),\n            nn.Linear(time_dim, time_dim))\n\n        self.prior_prob = prior_prob\n        self.test_cfg = test_cfg\n        self.use_nms = self.test_cfg.get('use_nms', True)\n        self._init_weights()\n\n    def _init_weights(self):\n        # init all parameters.\n        bias_value = -math.log((1 - self.prior_prob) / self.prior_prob)\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n            # initialize the bias for focal loss and fed loss.\n            if self.use_focal_loss or self.use_fed_loss:\n                if p.shape[-1] == self.num_classes or \\\n                        p.shape[-1] == self.num_classes + 1:\n                    nn.init.constant_(p, bias_value)\n\n    def _build_diffusion(self):\n        betas = cosine_beta_schedule(self.timesteps)\n        alphas = 1. - betas\n        alphas_cumprod = torch.cumprod(alphas, dim=0)\n        alphas_cumprod_prev = F.pad(alphas_cumprod[:-1], (1, 0), value=1.)\n\n        self.register_buffer('betas', betas)\n        self.register_buffer('alphas_cumprod', alphas_cumprod)\n        self.register_buffer('alphas_cumprod_prev', alphas_cumprod_prev)\n\n        # calculations for diffusion q(x_t | x_{t-1}) and others\n        self.register_buffer('sqrt_alphas_cumprod', torch.sqrt(alphas_cumprod))\n        self.register_buffer('sqrt_one_minus_alphas_cumprod',\n                             torch.sqrt(1. - alphas_cumprod))\n        self.register_buffer('log_one_minus_alphas_cumprod',\n                             torch.log(1. - alphas_cumprod))\n        self.register_buffer('sqrt_recip_alphas_cumprod',\n                             torch.sqrt(1. / alphas_cumprod))\n        self.register_buffer('sqrt_recipm1_alphas_cumprod',\n                             torch.sqrt(1. / alphas_cumprod - 1))\n\n        # calculations for posterior q(x_{t-1} | x_t, x_0)\n        # equal to 1. / (1. / (1. - alpha_cumprod_tm1) + alpha_t / beta_t)\n        posterior_variance = betas * (1. - alphas_cumprod_prev) / (\n            1. - alphas_cumprod)\n        self.register_buffer('posterior_variance', posterior_variance)\n\n        # log calculation clipped because the posterior variance is 0 at\n        # the beginning of the diffusion chain\n        self.register_buffer('posterior_log_variance_clipped',\n                             torch.log(posterior_variance.clamp(min=1e-20)))\n        self.register_buffer(\n            'posterior_mean_coef1',\n            betas * torch.sqrt(alphas_cumprod_prev) / (1. - alphas_cumprod))\n        self.register_buffer('posterior_mean_coef2',\n                             (1. - alphas_cumprod_prev) * torch.sqrt(alphas) /\n                             (1. - alphas_cumprod))\n\n    def forward(self, features, init_bboxes, init_t, init_features=None):\n        time = self.time_mlp(init_t, )\n\n        inter_class_logits = []\n        inter_pred_bboxes = []\n\n        bs = len(features[0])\n        bboxes = init_bboxes\n\n        if init_features is not None:\n            init_features = init_features[None].repeat(1, bs, 1)\n            proposal_features = init_features.clone()\n        else:\n            proposal_features = None\n\n        for head_idx, single_head in enumerate(self.head_series):\n            class_logits, pred_bboxes, proposal_features = single_head(\n                features, bboxes, proposal_features, self.roi_extractor, time)\n            if self.deep_supervision:\n                inter_class_logits.append(class_logits)\n                inter_pred_bboxes.append(pred_bboxes)\n            bboxes = pred_bboxes.detach()\n\n        if self.deep_supervision:\n            return torch.stack(inter_class_logits), torch.stack(\n                inter_pred_bboxes)\n        else:\n            return class_logits[None, ...], pred_bboxes[None, ...]\n\n    def loss(self, x: Tuple[Tensor], batch_data_samples: SampleList) -> dict:\n        \"\"\"Perform forward propagation and loss calculation of the detection\n        head on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Features from the upstream network, each is\n                a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        prepare_outputs = self.prepare_training_targets(batch_data_samples)\n        (batch_gt_instances, batch_pred_instances, batch_gt_instances_ignore,\n         batch_img_metas) = prepare_outputs\n\n        batch_diff_bboxes = torch.stack([\n            pred_instances.diff_bboxes_abs\n            for pred_instances in batch_pred_instances\n        ])\n        batch_time = torch.stack(\n            [pred_instances.time for pred_instances in batch_pred_instances])\n\n        pred_logits, pred_bboxes = self(x, batch_diff_bboxes, batch_time)\n\n        output = {\n            'pred_logits': pred_logits[-1],\n            'pred_boxes': pred_bboxes[-1]\n        }\n        if self.deep_supervision:\n            output['aux_outputs'] = [{\n                'pred_logits': a,\n                'pred_boxes': b\n            } for a, b in zip(pred_logits[:-1], pred_bboxes[:-1])]\n\n        losses = self.criterion(output, batch_gt_instances, batch_img_metas)\n        return losses\n\n    def prepare_training_targets(self, batch_data_samples):\n        # hard-setting seed to keep results same (if necessary)\n        # random.seed(0)\n        # torch.manual_seed(0)\n        # torch.cuda.manual_seed_all(0)\n        # torch.backends.cudnn.deterministic = True\n        # torch.backends.cudnn.benchmark = False\n\n        batch_gt_instances = []\n        batch_pred_instances = []\n        batch_gt_instances_ignore = []\n        batch_img_metas = []\n        for data_sample in batch_data_samples:\n            img_meta = data_sample.metainfo\n            gt_instances = data_sample.gt_instances\n\n            gt_bboxes = gt_instances.bboxes\n            h, w = img_meta['img_shape']\n            image_size = gt_bboxes.new_tensor([w, h, w, h])\n\n            norm_gt_bboxes = gt_bboxes / image_size\n            norm_gt_bboxes_cxcywh = bbox_xyxy_to_cxcywh(norm_gt_bboxes)\n            pred_instances = self.prepare_diffusion(norm_gt_bboxes_cxcywh,\n                                                    image_size)\n\n            gt_instances.set_metainfo(dict(image_size=image_size))\n            gt_instances.norm_bboxes_cxcywh = norm_gt_bboxes_cxcywh\n\n            batch_gt_instances.append(gt_instances)\n            batch_pred_instances.append(pred_instances)\n            batch_img_metas.append(data_sample.metainfo)\n            if 'ignored_instances' in data_sample:\n                batch_gt_instances_ignore.append(data_sample.ignored_instances)\n            else:\n                batch_gt_instances_ignore.append(None)\n        return (batch_gt_instances, batch_pred_instances,\n                batch_gt_instances_ignore, batch_img_metas)\n\n    def prepare_diffusion(self, gt_boxes, image_size):\n        device = gt_boxes.device\n        time = torch.randint(\n            0, self.timesteps, (1, ), dtype=torch.long, device=device)\n        noise = torch.randn(self.num_proposals, 4, device=device)\n\n        num_gt = gt_boxes.shape[0]\n        if num_gt < self.num_proposals:\n            # 3 * sigma = 1/2 --> sigma: 1/6\n            box_placeholder = torch.randn(\n                self.num_proposals - num_gt, 4, device=device) / 6. + 0.5\n            box_placeholder[:, 2:] = torch.clip(\n                box_placeholder[:, 2:], min=1e-4)\n            x_start = torch.cat((gt_boxes, box_placeholder), dim=0)\n        else:\n            select_mask = [True] * self.num_proposals + \\\n                          [False] * (num_gt - self.num_proposals)\n            random.shuffle(select_mask)\n            x_start = gt_boxes[select_mask]\n\n        x_start = (x_start * 2. - 1.) * self.snr_scale\n\n        # noise sample\n        x = self.q_sample(x_start=x_start, time=time, noise=noise)\n\n        x = torch.clamp(x, min=-1 * self.snr_scale, max=self.snr_scale)\n        x = ((x / self.snr_scale) + 1) / 2.\n\n        diff_bboxes = bbox_cxcywh_to_xyxy(x)\n        # convert to abs bboxes\n        diff_bboxes_abs = diff_bboxes * image_size\n\n        metainfo = dict(time=time.squeeze(-1))\n        pred_instances = InstanceData(metainfo=metainfo)\n        pred_instances.diff_bboxes = diff_bboxes\n        pred_instances.diff_bboxes_abs = diff_bboxes_abs\n        pred_instances.noise = noise\n        return pred_instances\n\n    # forward diffusion\n    def q_sample(self, x_start, time, noise=None):\n        if noise is None:\n            noise = torch.randn_like(x_start)\n\n        x_start_shape = x_start.shape\n\n        sqrt_alphas_cumprod_t = extract(self.sqrt_alphas_cumprod, time,\n                                        x_start_shape)\n        sqrt_one_minus_alphas_cumprod_t = extract(\n            self.sqrt_one_minus_alphas_cumprod, time, x_start_shape)\n\n        return sqrt_alphas_cumprod_t * x_start + \\\n            sqrt_one_minus_alphas_cumprod_t * noise\n\n    def predict(self,\n                x: Tuple[Tensor],\n                batch_data_samples: SampleList,\n                rescale: bool = False) -> InstanceList:\n        \"\"\"Perform forward propagation of the detection head and predict\n        detection results on the features of the upstream network.\n\n        Args:\n            x (tuple[Tensor]): Multi-level features from the\n                upstream network, each is a 4D-tensor.\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool, optional): Whether to rescale the results.\n                Defaults to False.\n\n        Returns:\n            list[obj:`InstanceData`]: Detection results of each image\n            after the post process.\n        \"\"\"\n        # hard-setting seed to keep results same (if necessary)\n        # seed = 0\n        # random.seed(seed)\n        # torch.manual_seed(seed)\n        # torch.cuda.manual_seed_all(seed)\n\n        device = x[-1].device\n\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n\n        (time_pairs, batch_noise_bboxes, batch_noise_bboxes_raw,\n         batch_image_size) = self.prepare_testing_targets(\n             batch_img_metas, device)\n\n        predictions = self.predict_by_feat(\n            x,\n            time_pairs=time_pairs,\n            batch_noise_bboxes=batch_noise_bboxes,\n            batch_noise_bboxes_raw=batch_noise_bboxes_raw,\n            batch_image_size=batch_image_size,\n            device=device,\n            batch_img_metas=batch_img_metas)\n        return predictions\n\n    def predict_by_feat(self,\n                        x,\n                        time_pairs,\n                        batch_noise_bboxes,\n                        batch_noise_bboxes_raw,\n                        batch_image_size,\n                        device,\n                        batch_img_metas=None,\n                        cfg=None,\n                        rescale=True):\n\n        batch_size = len(batch_img_metas)\n\n        cfg = self.test_cfg if cfg is None else cfg\n        cfg = copy.deepcopy(cfg)\n\n        ensemble_score, ensemble_label, ensemble_coord = [], [], []\n        for time, time_next in time_pairs:\n            batch_time = torch.full((batch_size, ),\n                                    time,\n                                    device=device,\n                                    dtype=torch.long)\n            # self_condition = x_start if self.self_condition else None\n            pred_logits, pred_bboxes = self(x, batch_noise_bboxes, batch_time)\n\n            x_start = pred_bboxes[-1]\n\n            x_start = x_start / batch_image_size[:, None, :]\n            x_start = bbox_xyxy_to_cxcywh(x_start)\n            x_start = (x_start * 2 - 1.) * self.snr_scale\n            x_start = torch.clamp(\n                x_start, min=-1 * self.snr_scale, max=self.snr_scale)\n            pred_noise = self.predict_noise_from_start(batch_noise_bboxes_raw,\n                                                       batch_time, x_start)\n            pred_noise_list, x_start_list = [], []\n            noise_bboxes_list, num_remain_list = [], []\n            if self.box_renewal:  # filter\n                score_thr = cfg.get('score_thr', 0)\n                for img_id in range(batch_size):\n                    score_per_image = pred_logits[-1][img_id]\n\n                    score_per_image = torch.sigmoid(score_per_image)\n                    value, _ = torch.max(score_per_image, -1, keepdim=False)\n                    keep_idx = value > score_thr\n\n                    num_remain_list.append(torch.sum(keep_idx))\n                    pred_noise_list.append(pred_noise[img_id, keep_idx, :])\n                    x_start_list.append(x_start[img_id, keep_idx, :])\n                    noise_bboxes_list.append(batch_noise_bboxes[img_id,\n                                                                keep_idx, :])\n            if time_next < 0:\n                # Not same as original DiffusionDet\n                if self.use_ensemble and self.sampling_timesteps > 1:\n                    box_pred_per_image, scores_per_image, labels_per_image = \\\n                        self.inference(\n                            box_cls=pred_logits[-1],\n                            box_pred=pred_bboxes[-1],\n                            cfg=cfg,\n                            device=device)\n                    ensemble_score.append(scores_per_image)\n                    ensemble_label.append(labels_per_image)\n                    ensemble_coord.append(box_pred_per_image)\n                continue\n\n            alpha = self.alphas_cumprod[time]\n            alpha_next = self.alphas_cumprod[time_next]\n\n            sigma = self.ddim_sampling_eta * ((1 - alpha / alpha_next) *\n                                              (1 - alpha_next) /\n                                              (1 - alpha)).sqrt()\n            c = (1 - alpha_next - sigma**2).sqrt()\n\n            batch_noise_bboxes_list = []\n            batch_noise_bboxes_raw_list = []\n            for idx in range(batch_size):\n                pred_noise = pred_noise_list[idx]\n                x_start = x_start_list[idx]\n                noise_bboxes = noise_bboxes_list[idx]\n                num_remain = num_remain_list[idx]\n                noise = torch.randn_like(noise_bboxes)\n\n                noise_bboxes = x_start * alpha_next.sqrt() + \\\n                    c * pred_noise + sigma * noise\n\n                if self.box_renewal:  # filter\n                    # replenish with randn boxes\n                    if num_remain < self.num_proposals:\n                        noise_bboxes = torch.cat(\n                            (noise_bboxes,\n                             torch.randn(\n                                 self.num_proposals - num_remain,\n                                 4,\n                                 device=device)),\n                            dim=0)\n                    else:\n                        select_mask = [True] * self.num_proposals + \\\n                                      [False] * (num_remain -\n                                                 self.num_proposals)\n                        random.shuffle(select_mask)\n                        noise_bboxes = noise_bboxes[select_mask]\n\n                    # raw noise boxes\n                    batch_noise_bboxes_raw_list.append(noise_bboxes)\n                    # resize to xyxy\n                    noise_bboxes = torch.clamp(\n                        noise_bboxes,\n                        min=-1 * self.snr_scale,\n                        max=self.snr_scale)\n                    noise_bboxes = ((noise_bboxes / self.snr_scale) + 1) / 2\n                    noise_bboxes = bbox_cxcywh_to_xyxy(noise_bboxes)\n                    noise_bboxes = noise_bboxes * batch_image_size[idx]\n\n                batch_noise_bboxes_list.append(noise_bboxes)\n            batch_noise_bboxes = torch.stack(batch_noise_bboxes_list)\n            batch_noise_bboxes_raw = torch.stack(batch_noise_bboxes_raw_list)\n            if self.use_ensemble and self.sampling_timesteps > 1:\n                box_pred_per_image, scores_per_image, labels_per_image = \\\n                    self.inference(\n                        box_cls=pred_logits[-1],\n                        box_pred=pred_bboxes[-1],\n                        cfg=cfg,\n                        device=device)\n                ensemble_score.append(scores_per_image)\n                ensemble_label.append(labels_per_image)\n                ensemble_coord.append(box_pred_per_image)\n        if self.use_ensemble and self.sampling_timesteps > 1:\n            steps = len(ensemble_score)\n            results_list = []\n            for idx in range(batch_size):\n                ensemble_score_per_img = [\n                    ensemble_score[i][idx] for i in range(steps)\n                ]\n                ensemble_label_per_img = [\n                    ensemble_label[i][idx] for i in range(steps)\n                ]\n                ensemble_coord_per_img = [\n                    ensemble_coord[i][idx] for i in range(steps)\n                ]\n\n                scores_per_image = torch.cat(ensemble_score_per_img, dim=0)\n                labels_per_image = torch.cat(ensemble_label_per_img, dim=0)\n                box_pred_per_image = torch.cat(ensemble_coord_per_img, dim=0)\n\n                if self.use_nms:\n                    det_bboxes, keep_idxs = batched_nms(\n                        box_pred_per_image, scores_per_image, labels_per_image,\n                        cfg.nms)\n                    box_pred_per_image = box_pred_per_image[keep_idxs]\n                    labels_per_image = labels_per_image[keep_idxs]\n                    scores_per_image = det_bboxes[:, -1]\n                results = InstanceData()\n                results.bboxes = box_pred_per_image\n                results.scores = scores_per_image\n                results.labels = labels_per_image\n            results_list.append(results)\n        else:\n            box_cls = pred_logits[-1]\n            box_pred = pred_bboxes[-1]\n            results_list = self.inference(box_cls, box_pred, cfg, device)\n        if rescale:\n            results_list = self.do_results_post_process(\n                results_list, cfg, batch_img_metas=batch_img_metas)\n        return results_list\n\n    @staticmethod\n    def do_results_post_process(results_list, cfg, batch_img_metas=None):\n        processed_results = []\n        for results, img_meta in zip(results_list, batch_img_metas):\n            assert img_meta.get('scale_factor') is not None\n            scale_factor = [1 / s for s in img_meta['scale_factor']]\n            results.bboxes = scale_boxes(results.bboxes, scale_factor)\n            # clip w, h\n            h, w = img_meta['ori_shape']\n            results.bboxes[:, 0::2] = results.bboxes[:, 0::2].clamp(\n                min=0, max=w)\n            results.bboxes[:, 1::2] = results.bboxes[:, 1::2].clamp(\n                min=0, max=h)\n\n            # filter small size bboxes\n            if cfg.get('min_bbox_size', 0) >= 0:\n                w, h = get_box_wh(results.bboxes)\n                valid_mask = (w > cfg.min_bbox_size) & (h > cfg.min_bbox_size)\n                if not valid_mask.all():\n                    results = results[valid_mask]\n            processed_results.append(results)\n\n        return processed_results\n\n    def prepare_testing_targets(self, batch_img_metas, device):\n        # [-1, 0, 1, 2, ..., T-1] when sampling_timesteps == timesteps\n        times = torch.linspace(\n            -1, self.timesteps - 1, steps=self.sampling_timesteps + 1)\n        times = list(reversed(times.int().tolist()))\n        # [(T-1, T-2), (T-2, T-3), ..., (1, 0), (0, -1)]\n        time_pairs = list(zip(times[:-1], times[1:]))\n\n        noise_bboxes_list = []\n        noise_bboxes_raw_list = []\n        image_size_list = []\n        for img_meta in batch_img_metas:\n            h, w = img_meta['img_shape']\n            image_size = torch.tensor([w, h, w, h],\n                                      dtype=torch.float32,\n                                      device=device)\n            noise_bboxes_raw = torch.randn((self.num_proposals, 4),\n                                           device=device)\n            noise_bboxes = torch.clamp(\n                noise_bboxes_raw, min=-1 * self.snr_scale, max=self.snr_scale)\n            noise_bboxes = ((noise_bboxes / self.snr_scale) + 1) / 2\n            noise_bboxes = bbox_cxcywh_to_xyxy(noise_bboxes)\n            noise_bboxes = noise_bboxes * image_size\n\n            noise_bboxes_raw_list.append(noise_bboxes_raw)\n            noise_bboxes_list.append(noise_bboxes)\n            image_size_list.append(image_size[None])\n        batch_noise_bboxes = torch.stack(noise_bboxes_list)\n        batch_image_size = torch.cat(image_size_list)\n        batch_noise_bboxes_raw = torch.stack(noise_bboxes_raw_list)\n        return (time_pairs, batch_noise_bboxes, batch_noise_bboxes_raw,\n                batch_image_size)\n\n    def predict_noise_from_start(self, x_t, t, x0):\n        results = (extract(\n            self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - x0) / \\\n                  extract(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape)\n        return results\n\n    def inference(self, box_cls, box_pred, cfg, device):\n        \"\"\"\n        Args:\n            box_cls (Tensor): tensor of shape (batch_size, num_proposals, K).\n                The tensor predicts the classification probability for\n                each proposal.\n            box_pred (Tensor): tensors of shape (batch_size, num_proposals, 4).\n                The tensor predicts 4-vector (x,y,w,h) box\n                regression values for every proposal\n\n        Returns:\n            results (List[Instances]): a list of #images elements.\n        \"\"\"\n        results = []\n\n        if self.use_focal_loss or self.use_fed_loss:\n            scores = torch.sigmoid(box_cls)\n            labels = torch.arange(\n                self.num_classes,\n                device=device).unsqueeze(0).repeat(self.num_proposals,\n                                                   1).flatten(0, 1)\n            box_pred_list = []\n            scores_list = []\n            labels_list = []\n            for i, (scores_per_image,\n                    box_pred_per_image) in enumerate(zip(scores, box_pred)):\n\n                scores_per_image, topk_indices = scores_per_image.flatten(\n                    0, 1).topk(\n                        self.num_proposals, sorted=False)\n                labels_per_image = labels[topk_indices]\n                box_pred_per_image = box_pred_per_image.view(-1, 1, 4).repeat(\n                    1, self.num_classes, 1).view(-1, 4)\n                box_pred_per_image = box_pred_per_image[topk_indices]\n\n                if self.use_ensemble and self.sampling_timesteps > 1:\n                    box_pred_list.append(box_pred_per_image)\n                    scores_list.append(scores_per_image)\n                    labels_list.append(labels_per_image)\n                    continue\n\n                if self.use_nms:\n                    det_bboxes, keep_idxs = batched_nms(\n                        box_pred_per_image, scores_per_image, labels_per_image,\n                        cfg.nms)\n                    box_pred_per_image = box_pred_per_image[keep_idxs]\n                    labels_per_image = labels_per_image[keep_idxs]\n                    # some nms would reweight the score, such as softnms\n                    scores_per_image = det_bboxes[:, -1]\n                result = InstanceData()\n                result.bboxes = box_pred_per_image\n                result.scores = scores_per_image\n                result.labels = labels_per_image\n                results.append(result)\n\n        else:\n            # For each box we assign the best class or the second\n            # best if the best on is `no_object`.\n            scores, labels = F.softmax(box_cls, dim=-1)[:, :, :-1].max(-1)\n\n            for i, (scores_per_image, labels_per_image,\n                    box_pred_per_image) in enumerate(\n                        zip(scores, labels, box_pred)):\n                if self.use_ensemble and self.sampling_timesteps > 1:\n                    return box_pred_per_image, scores_per_image, \\\n                           labels_per_image\n\n                if self.use_nms:\n                    det_bboxes, keep_idxs = batched_nms(\n                        box_pred_per_image, scores_per_image, labels_per_image,\n                        cfg.nms)\n                    box_pred_per_image = box_pred_per_image[keep_idxs]\n                    labels_per_image = labels_per_image[keep_idxs]\n                    # some nms would reweight the score, such as softnms\n                    scores_per_image = det_bboxes[:, -1]\n\n                result = InstanceData()\n                result.bboxes = box_pred_per_image\n                result.scores = scores_per_image\n                result.labels = labels_per_image\n                results.append(result)\n        if self.use_ensemble and self.sampling_timesteps > 1:\n            return box_pred_list, scores_list, labels_list\n        else:\n            return results\n\n\n@MODELS.register_module()\nclass SingleDiffusionDetHead(nn.Module):\n\n    def __init__(\n        self,\n        num_classes=80,\n        feat_channels=256,\n        dim_feedforward=2048,\n        num_cls_convs=1,\n        num_reg_convs=3,\n        num_heads=8,\n        dropout=0.0,\n        pooler_resolution=7,\n        scale_clamp=_DEFAULT_SCALE_CLAMP,\n        bbox_weights=(2.0, 2.0, 1.0, 1.0),\n        use_focal_loss=True,\n        use_fed_loss=False,\n        act_cfg=dict(type='ReLU', inplace=True),\n        dynamic_conv=dict(dynamic_dim=64, dynamic_num=2)\n    ) -> None:\n        super().__init__()\n        self.feat_channels = feat_channels\n\n        # Dynamic\n        self.self_attn = nn.MultiheadAttention(\n            feat_channels, num_heads, dropout=dropout)\n        self.inst_interact = DynamicConv(\n            feat_channels=feat_channels,\n            pooler_resolution=pooler_resolution,\n            dynamic_dim=dynamic_conv['dynamic_dim'],\n            dynamic_num=dynamic_conv['dynamic_num'])\n\n        self.linear1 = nn.Linear(feat_channels, dim_feedforward)\n        self.dropout = nn.Dropout(dropout)\n        self.linear2 = nn.Linear(dim_feedforward, feat_channels)\n\n        self.norm1 = nn.LayerNorm(feat_channels)\n        self.norm2 = nn.LayerNorm(feat_channels)\n        self.norm3 = nn.LayerNorm(feat_channels)\n        self.dropout1 = nn.Dropout(dropout)\n        self.dropout2 = nn.Dropout(dropout)\n        self.dropout3 = nn.Dropout(dropout)\n\n        self.activation = build_activation_layer(act_cfg)\n\n        # block time mlp\n        self.block_time_mlp = nn.Sequential(\n            nn.SiLU(), nn.Linear(feat_channels * 4, feat_channels * 2))\n\n        # cls.\n        cls_module = list()\n        for _ in range(num_cls_convs):\n            cls_module.append(nn.Linear(feat_channels, feat_channels, False))\n            cls_module.append(nn.LayerNorm(feat_channels))\n            cls_module.append(nn.ReLU(inplace=True))\n        self.cls_module = nn.ModuleList(cls_module)\n\n        # reg.\n        reg_module = list()\n        for _ in range(num_reg_convs):\n            reg_module.append(nn.Linear(feat_channels, feat_channels, False))\n            reg_module.append(nn.LayerNorm(feat_channels))\n            reg_module.append(nn.ReLU(inplace=True))\n        self.reg_module = nn.ModuleList(reg_module)\n\n        # pred.\n        self.use_focal_loss = use_focal_loss\n        self.use_fed_loss = use_fed_loss\n        if self.use_focal_loss or self.use_fed_loss:\n            self.class_logits = nn.Linear(feat_channels, num_classes)\n        else:\n            self.class_logits = nn.Linear(feat_channels, num_classes + 1)\n        self.bboxes_delta = nn.Linear(feat_channels, 4)\n        self.scale_clamp = scale_clamp\n        self.bbox_weights = bbox_weights\n\n    def forward(self, features, bboxes, pro_features, pooler, time_emb):\n        \"\"\"\n        :param bboxes: (N, num_boxes, 4)\n        :param pro_features: (N, num_boxes, feat_channels)\n        \"\"\"\n\n        N, num_boxes = bboxes.shape[:2]\n\n        # roi_feature.\n        proposal_boxes = list()\n        for b in range(N):\n            proposal_boxes.append(bboxes[b])\n        rois = bbox2roi(proposal_boxes)\n\n        roi_features = pooler(features, rois)\n\n        if pro_features is None:\n            pro_features = roi_features.view(N, num_boxes, self.feat_channels,\n                                             -1).mean(-1)\n\n        roi_features = roi_features.view(N * num_boxes, self.feat_channels,\n                                         -1).permute(2, 0, 1)\n\n        # self_att.\n        pro_features = pro_features.view(N, num_boxes,\n                                         self.feat_channels).permute(1, 0, 2)\n        pro_features2 = self.self_attn(\n            pro_features, pro_features, value=pro_features)[0]\n        pro_features = pro_features + self.dropout1(pro_features2)\n        pro_features = self.norm1(pro_features)\n\n        # inst_interact.\n        pro_features = pro_features.view(\n            num_boxes, N,\n            self.feat_channels).permute(1, 0,\n                                        2).reshape(1, N * num_boxes,\n                                                   self.feat_channels)\n        pro_features2 = self.inst_interact(pro_features, roi_features)\n        pro_features = pro_features + self.dropout2(pro_features2)\n        obj_features = self.norm2(pro_features)\n\n        # obj_feature.\n        obj_features2 = self.linear2(\n            self.dropout(self.activation(self.linear1(obj_features))))\n        obj_features = obj_features + self.dropout3(obj_features2)\n        obj_features = self.norm3(obj_features)\n\n        fc_feature = obj_features.transpose(0, 1).reshape(N * num_boxes, -1)\n\n        scale_shift = self.block_time_mlp(time_emb)\n        scale_shift = torch.repeat_interleave(scale_shift, num_boxes, dim=0)\n        scale, shift = scale_shift.chunk(2, dim=1)\n        fc_feature = fc_feature * (scale + 1) + shift\n\n        cls_feature = fc_feature.clone()\n        reg_feature = fc_feature.clone()\n        for cls_layer in self.cls_module:\n            cls_feature = cls_layer(cls_feature)\n        for reg_layer in self.reg_module:\n            reg_feature = reg_layer(reg_feature)\n        class_logits = self.class_logits(cls_feature)\n        bboxes_deltas = self.bboxes_delta(reg_feature)\n        pred_bboxes = self.apply_deltas(bboxes_deltas, bboxes.view(-1, 4))\n\n        return (class_logits.view(N, num_boxes,\n                                  -1), pred_bboxes.view(N, num_boxes,\n                                                        -1), obj_features)\n\n    def apply_deltas(self, deltas, boxes):\n        \"\"\"Apply transformation `deltas` (dx, dy, dw, dh) to `boxes`.\n\n        Args:\n            deltas (Tensor): transformation deltas of shape (N, k*4),\n                where k >= 1. deltas[i] represents k potentially\n                different class-specific box transformations for\n                the single box boxes[i].\n            boxes (Tensor): boxes to transform, of shape (N, 4)\n        \"\"\"\n        boxes = boxes.to(deltas.dtype)\n\n        widths = boxes[:, 2] - boxes[:, 0]\n        heights = boxes[:, 3] - boxes[:, 1]\n        ctr_x = boxes[:, 0] + 0.5 * widths\n        ctr_y = boxes[:, 1] + 0.5 * heights\n\n        wx, wy, ww, wh = self.bbox_weights\n        dx = deltas[:, 0::4] / wx\n        dy = deltas[:, 1::4] / wy\n        dw = deltas[:, 2::4] / ww\n        dh = deltas[:, 3::4] / wh\n\n        # Prevent sending too large values into torch.exp()\n        dw = torch.clamp(dw, max=self.scale_clamp)\n        dh = torch.clamp(dh, max=self.scale_clamp)\n\n        pred_ctr_x = dx * widths[:, None] + ctr_x[:, None]\n        pred_ctr_y = dy * heights[:, None] + ctr_y[:, None]\n        pred_w = torch.exp(dw) * widths[:, None]\n        pred_h = torch.exp(dh) * heights[:, None]\n\n        pred_boxes = torch.zeros_like(deltas)\n        pred_boxes[:, 0::4] = pred_ctr_x - 0.5 * pred_w  # x1\n        pred_boxes[:, 1::4] = pred_ctr_y - 0.5 * pred_h  # y1\n        pred_boxes[:, 2::4] = pred_ctr_x + 0.5 * pred_w  # x2\n        pred_boxes[:, 3::4] = pred_ctr_y + 0.5 * pred_h  # y2\n\n        return pred_boxes\n\n\nclass DynamicConv(nn.Module):\n\n    def __init__(self,\n                 feat_channels: int,\n                 dynamic_dim: int = 64,\n                 dynamic_num: int = 2,\n                 pooler_resolution: int = 7) -> None:\n        super().__init__()\n\n        self.feat_channels = feat_channels\n        self.dynamic_dim = dynamic_dim\n        self.dynamic_num = dynamic_num\n        self.num_params = self.feat_channels * self.dynamic_dim\n        self.dynamic_layer = nn.Linear(self.feat_channels,\n                                       self.dynamic_num * self.num_params)\n\n        self.norm1 = nn.LayerNorm(self.dynamic_dim)\n        self.norm2 = nn.LayerNorm(self.feat_channels)\n\n        self.activation = nn.ReLU(inplace=True)\n\n        num_output = self.feat_channels * pooler_resolution**2\n        self.out_layer = nn.Linear(num_output, self.feat_channels)\n        self.norm3 = nn.LayerNorm(self.feat_channels)\n\n    def forward(self, pro_features: Tensor, roi_features: Tensor) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pro_features: (1,  N * num_boxes, self.feat_channels)\n            roi_features: (49, N * num_boxes, self.feat_channels)\n\n        Returns:\n        \"\"\"\n        features = roi_features.permute(1, 0, 2)\n        parameters = self.dynamic_layer(pro_features).permute(1, 0, 2)\n\n        param1 = parameters[:, :, :self.num_params].view(\n            -1, self.feat_channels, self.dynamic_dim)\n        param2 = parameters[:, :,\n                            self.num_params:].view(-1, self.dynamic_dim,\n                                                   self.feat_channels)\n\n        features = torch.bmm(features, param1)\n        features = self.norm1(features)\n        features = self.activation(features)\n\n        features = torch.bmm(features, param2)\n        features = self.norm2(features)\n        features = self.activation(features)\n\n        features = features.flatten(1)\n        features = self.out_layer(features)\n        features = self.norm3(features)\n        features = self.activation(features)\n\n        return features\n"
  },
  {
    "path": "projects/DiffusionDet/diffusiondet/loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n# Modified from https://github.com/ShoufaChen/DiffusionDet/blob/main/diffusiondet/loss.py   # noqa\n\n# This work is licensed under the CC-BY-NC 4.0 License.\n# Users should be careful about adopting these features in any commercial matters.  # noqa\n# For more details, please refer to https://github.com/ShoufaChen/DiffusionDet/blob/main/LICENSE    # noqa\n\nfrom typing import List, Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.structures.bbox import bbox_cxcywh_to_xyxy, bbox_xyxy_to_cxcywh\nfrom mmdet.utils import ConfigType\n\n\n@TASK_UTILS.register_module()\nclass DiffusionDetCriterion(nn.Module):\n\n    def __init__(\n            self,\n            num_classes,\n            assigner: Union[ConfigDict, nn.Module],\n            deep_supervision=True,\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                alpha=0.25,\n                gamma=2.0,\n                reduction='sum',\n                loss_weight=2.0),\n            loss_bbox=dict(type='L1Loss', reduction='sum', loss_weight=5.0),\n            loss_giou=dict(type='GIoULoss', reduction='sum', loss_weight=2.0),\n    ):\n\n        super().__init__()\n        self.num_classes = num_classes\n\n        if isinstance(assigner, nn.Module):\n            self.assigner = assigner\n        else:\n            self.assigner = TASK_UTILS.build(assigner)\n\n        self.deep_supervision = deep_supervision\n\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_bbox = MODELS.build(loss_bbox)\n        self.loss_giou = MODELS.build(loss_giou)\n\n    def forward(self, outputs, batch_gt_instances, batch_img_metas):\n        batch_indices = self.assigner(outputs, batch_gt_instances,\n                                      batch_img_metas)\n        # Compute all the requested losses\n        loss_cls = self.loss_classification(outputs, batch_gt_instances,\n                                            batch_indices)\n        loss_bbox, loss_giou = self.loss_boxes(outputs, batch_gt_instances,\n                                               batch_indices)\n\n        losses = dict(\n            loss_cls=loss_cls, loss_bbox=loss_bbox, loss_giou=loss_giou)\n\n        if self.deep_supervision:\n            assert 'aux_outputs' in outputs\n            for i, aux_outputs in enumerate(outputs['aux_outputs']):\n                batch_indices = self.assigner(aux_outputs, batch_gt_instances,\n                                              batch_img_metas)\n                loss_cls = self.loss_classification(aux_outputs,\n                                                    batch_gt_instances,\n                                                    batch_indices)\n                loss_bbox, loss_giou = self.loss_boxes(aux_outputs,\n                                                       batch_gt_instances,\n                                                       batch_indices)\n                tmp_losses = dict(\n                    loss_cls=loss_cls,\n                    loss_bbox=loss_bbox,\n                    loss_giou=loss_giou)\n                for name, value in tmp_losses.items():\n                    losses[f's.{i}.{name}'] = value\n        return losses\n\n    def loss_classification(self, outputs, batch_gt_instances, indices):\n        assert 'pred_logits' in outputs\n        src_logits = outputs['pred_logits']\n        target_classes_list = [\n            gt.labels[J] for gt, (_, J) in zip(batch_gt_instances, indices)\n        ]\n        target_classes = torch.full(\n            src_logits.shape[:2],\n            self.num_classes,\n            dtype=torch.int64,\n            device=src_logits.device)\n        for idx in range(len(batch_gt_instances)):\n            target_classes[idx, indices[idx][0]] = target_classes_list[idx]\n\n        src_logits = src_logits.flatten(0, 1)\n        target_classes = target_classes.flatten(0, 1)\n        # comp focal loss.\n        num_instances = max(torch.cat(target_classes_list).shape[0], 1)\n        loss_cls = self.loss_cls(\n            src_logits,\n            target_classes,\n        ) / num_instances\n        return loss_cls\n\n    def loss_boxes(self, outputs, batch_gt_instances, indices):\n        assert 'pred_boxes' in outputs\n        pred_boxes = outputs['pred_boxes']\n\n        target_bboxes_norm_list = [\n            gt.norm_bboxes_cxcywh[J]\n            for gt, (_, J) in zip(batch_gt_instances, indices)\n        ]\n        target_bboxes_list = [\n            gt.bboxes[J] for gt, (_, J) in zip(batch_gt_instances, indices)\n        ]\n\n        pred_bboxes_list = []\n        pred_bboxes_norm_list = []\n        for idx in range(len(batch_gt_instances)):\n            pred_bboxes_list.append(pred_boxes[idx, indices[idx][0]])\n            image_size = batch_gt_instances[idx].image_size\n            pred_bboxes_norm_list.append(pred_boxes[idx, indices[idx][0]] /\n                                         image_size)\n\n        pred_boxes_cat = torch.cat(pred_bboxes_list)\n        pred_boxes_norm_cat = torch.cat(pred_bboxes_norm_list)\n        target_bboxes_cat = torch.cat(target_bboxes_list)\n        target_bboxes_norm_cat = torch.cat(target_bboxes_norm_list)\n\n        if len(pred_boxes_cat) > 0:\n            num_instances = pred_boxes_cat.shape[0]\n\n            loss_bbox = self.loss_bbox(\n                pred_boxes_norm_cat,\n                bbox_cxcywh_to_xyxy(target_bboxes_norm_cat)) / num_instances\n            loss_giou = self.loss_giou(pred_boxes_cat,\n                                       target_bboxes_cat) / num_instances\n        else:\n            loss_bbox = pred_boxes.sum() * 0\n            loss_giou = pred_boxes.sum() * 0\n        return loss_bbox, loss_giou\n\n\n@TASK_UTILS.register_module()\nclass DiffusionDetMatcher(nn.Module):\n    \"\"\"This class computes an assignment between the targets and the\n    predictions of the network For efficiency reasons, the targets don't\n    include the no_object.\n\n    Because of this, in general, there are more predictions than targets. In\n    this case, we do a 1-to-k (dynamic) matching of the best predictions, while\n    the others are un-matched (and thus treated as non-objects).\n    \"\"\"\n\n    def __init__(self,\n                 match_costs: Union[List[Union[dict, ConfigDict]], dict,\n                                    ConfigDict],\n                 center_radius: float = 2.5,\n                 candidate_topk: int = 5,\n                 iou_calculator: ConfigType = dict(type='BboxOverlaps2D'),\n                 **kwargs):\n        super().__init__()\n\n        self.center_radius = center_radius\n        self.candidate_topk = candidate_topk\n\n        if isinstance(match_costs, dict):\n            match_costs = [match_costs]\n        elif isinstance(match_costs, list):\n            assert len(match_costs) > 0, \\\n                'match_costs must not be a empty list.'\n        self.use_focal_loss = False\n        self.use_fed_loss = False\n        for _match_cost in match_costs:\n            if _match_cost.get('type') == 'FocalLossCost':\n                self.use_focal_loss = True\n            if _match_cost.get('type') == 'FedLoss':\n                self.use_fed_loss = True\n                raise NotImplementedError\n\n        self.match_costs = [\n            TASK_UTILS.build(match_cost) for match_cost in match_costs\n        ]\n        self.iou_calculator = TASK_UTILS.build(iou_calculator)\n\n    def forward(self, outputs, batch_gt_instances, batch_img_metas):\n        assert 'pred_logits' in outputs and 'pred_boxes' in outputs\n\n        pred_logits = outputs['pred_logits']\n        pred_bboxes = outputs['pred_boxes']\n        batch_size = len(batch_gt_instances)\n\n        assert batch_size == pred_logits.shape[0] == pred_bboxes.shape[0]\n        batch_indices = []\n        for i in range(batch_size):\n            pred_instances = InstanceData()\n            pred_instances.bboxes = pred_bboxes[i, ...]\n            pred_instances.scores = pred_logits[i, ...]\n            gt_instances = batch_gt_instances[i]\n            img_meta = batch_img_metas[i]\n            indices = self.single_assigner(pred_instances, gt_instances,\n                                           img_meta)\n            batch_indices.append(indices)\n        return batch_indices\n\n    def single_assigner(self, pred_instances, gt_instances, img_meta):\n        with torch.no_grad():\n            gt_bboxes = gt_instances.bboxes\n            pred_bboxes = pred_instances.bboxes\n            num_gt = gt_bboxes.size(0)\n\n            if num_gt == 0:  # empty object in key frame\n                valid_mask = pred_bboxes.new_zeros((pred_bboxes.shape[0], ),\n                                                   dtype=torch.bool)\n                matched_gt_inds = pred_bboxes.new_zeros((gt_bboxes.shape[0], ),\n                                                        dtype=torch.long)\n                return valid_mask, matched_gt_inds\n\n            valid_mask, is_in_boxes_and_center = \\\n                self.get_in_gt_and_in_center_info(\n                    bbox_xyxy_to_cxcywh(pred_bboxes),\n                    bbox_xyxy_to_cxcywh(gt_bboxes)\n                )\n\n            cost_list = []\n            for match_cost in self.match_costs:\n                cost = match_cost(\n                    pred_instances=pred_instances,\n                    gt_instances=gt_instances,\n                    img_meta=img_meta)\n                cost_list.append(cost)\n\n            pairwise_ious = self.iou_calculator(pred_bboxes, gt_bboxes)\n\n            cost_list.append((~is_in_boxes_and_center) * 100.0)\n            cost_matrix = torch.stack(cost_list).sum(0)\n            cost_matrix[~valid_mask] = cost_matrix[~valid_mask] + 10000.0\n\n            fg_mask_inboxes, matched_gt_inds = \\\n                self.dynamic_k_matching(\n                    cost_matrix, pairwise_ious, num_gt)\n        return fg_mask_inboxes, matched_gt_inds\n\n    def get_in_gt_and_in_center_info(\n            self, pred_bboxes: Tensor,\n            gt_bboxes: Tensor) -> Tuple[Tensor, Tensor]:\n        \"\"\"Get the information of which prior is in gt bboxes and gt center\n        priors.\"\"\"\n        xy_target_gts = bbox_cxcywh_to_xyxy(gt_bboxes)  # (x1, y1, x2, y2)\n\n        pred_bboxes_center_x = pred_bboxes[:, 0].unsqueeze(1)\n        pred_bboxes_center_y = pred_bboxes[:, 1].unsqueeze(1)\n\n        # whether the center of each anchor is inside a gt box\n        b_l = pred_bboxes_center_x > xy_target_gts[:, 0].unsqueeze(0)\n        b_r = pred_bboxes_center_x < xy_target_gts[:, 2].unsqueeze(0)\n        b_t = pred_bboxes_center_y > xy_target_gts[:, 1].unsqueeze(0)\n        b_b = pred_bboxes_center_y < xy_target_gts[:, 3].unsqueeze(0)\n        # (b_l.long()+b_r.long()+b_t.long()+b_b.long())==4 [300,num_gt] ,\n        is_in_boxes = ((b_l.long() + b_r.long() + b_t.long() +\n                        b_b.long()) == 4)\n        is_in_boxes_all = is_in_boxes.sum(1) > 0  # [num_query]\n        # in fixed center\n        center_radius = 2.5\n        # Modified to self-adapted sampling --- the center size depends\n        # on the size of the gt boxes\n        # https://github.com/dulucas/UVO_Challenge/blob/main/Track1/detection/mmdet/core/bbox/assigners/rpn_sim_ota_assigner.py#L212    # noqa\n        b_l = pred_bboxes_center_x > (\n            gt_bboxes[:, 0] -\n            (center_radius *\n             (xy_target_gts[:, 2] - xy_target_gts[:, 0]))).unsqueeze(0)\n        b_r = pred_bboxes_center_x < (\n            gt_bboxes[:, 0] +\n            (center_radius *\n             (xy_target_gts[:, 2] - xy_target_gts[:, 0]))).unsqueeze(0)\n        b_t = pred_bboxes_center_y > (\n            gt_bboxes[:, 1] -\n            (center_radius *\n             (xy_target_gts[:, 3] - xy_target_gts[:, 1]))).unsqueeze(0)\n        b_b = pred_bboxes_center_y < (\n            gt_bboxes[:, 1] +\n            (center_radius *\n             (xy_target_gts[:, 3] - xy_target_gts[:, 1]))).unsqueeze(0)\n\n        is_in_centers = ((b_l.long() + b_r.long() + b_t.long() +\n                          b_b.long()) == 4)\n        is_in_centers_all = is_in_centers.sum(1) > 0\n\n        is_in_boxes_anchor = is_in_boxes_all | is_in_centers_all\n        is_in_boxes_and_center = (is_in_boxes & is_in_centers)\n\n        return is_in_boxes_anchor, is_in_boxes_and_center\n\n    def dynamic_k_matching(self, cost: Tensor, pairwise_ious: Tensor,\n                           num_gt: int) -> Tuple[Tensor, Tensor]:\n        \"\"\"Use IoU and matching cost to calculate the dynamic top-k positive\n        targets.\"\"\"\n        matching_matrix = torch.zeros_like(cost)\n        # select candidate topk ious for dynamic-k calculation\n        candidate_topk = min(self.candidate_topk, pairwise_ious.size(0))\n        topk_ious, _ = torch.topk(pairwise_ious, candidate_topk, dim=0)\n        # calculate dynamic k for each gt\n        dynamic_ks = torch.clamp(topk_ious.sum(0).int(), min=1)\n        for gt_idx in range(num_gt):\n            _, pos_idx = torch.topk(\n                cost[:, gt_idx], k=dynamic_ks[gt_idx], largest=False)\n            matching_matrix[:, gt_idx][pos_idx] = 1\n\n        del topk_ious, dynamic_ks, pos_idx\n\n        prior_match_gt_mask = matching_matrix.sum(1) > 1\n        if prior_match_gt_mask.sum() > 0:\n            _, cost_argmin = torch.min(cost[prior_match_gt_mask, :], dim=1)\n            matching_matrix[prior_match_gt_mask, :] *= 0\n            matching_matrix[prior_match_gt_mask, cost_argmin] = 1\n\n        while (matching_matrix.sum(0) == 0).any():\n            matched_query_id = matching_matrix.sum(1) > 0\n            cost[matched_query_id] += 100000.0\n            unmatch_id = torch.nonzero(\n                matching_matrix.sum(0) == 0, as_tuple=False).squeeze(1)\n            for gt_idx in unmatch_id:\n                pos_idx = torch.argmin(cost[:, gt_idx])\n                matching_matrix[:, gt_idx][pos_idx] = 1.0\n            if (matching_matrix.sum(1) > 1).sum() > 0:\n                _, cost_argmin = torch.min(cost[prior_match_gt_mask], dim=1)\n                matching_matrix[prior_match_gt_mask] *= 0\n                matching_matrix[prior_match_gt_mask, cost_argmin, ] = 1\n\n        assert not (matching_matrix.sum(0) == 0).any()\n        # get foreground mask inside box and center prior\n        fg_mask_inboxes = matching_matrix.sum(1) > 0\n        matched_gt_inds = matching_matrix[fg_mask_inboxes, :].argmax(1)\n\n        return fg_mask_inboxes, matched_gt_inds\n"
  },
  {
    "path": "projects/DiffusionDet/model_converters/diffusiondet_resnet_to_mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nfrom collections import OrderedDict\n\nimport numpy as np\nimport torch\nfrom mmengine.fileio import load\n\n\ndef convert(src, dst):\n    if src.endswith('pth'):\n        src_model = torch.load(src)\n    else:\n        src_model = load(src)\n\n    dst_state_dict = OrderedDict()\n    for k, v in src_model['model'].items():\n        key_name_split = k.split('.')\n        if 'backbone.fpn_lateral' in k:\n            lateral_id = int(key_name_split[-2][-1])\n            name = f'neck.lateral_convs.{lateral_id - 2}.' \\\n                   f'conv.{key_name_split[-1]}'\n        elif 'backbone.fpn_output' in k:\n            lateral_id = int(key_name_split[-2][-1])\n            name = f'neck.fpn_convs.{lateral_id - 2}.conv.' \\\n                   f'{key_name_split[-1]}'\n        elif 'backbone.bottom_up.stem.conv1.norm.' in k:\n            name = f'backbone.bn1.{key_name_split[-1]}'\n        elif 'backbone.bottom_up.stem.conv1.' in k:\n            name = f'backbone.conv1.{key_name_split[-1]}'\n        elif 'backbone.bottom_up.res' in k:\n            # weight_type = key_name_split[-1]\n            res_id = int(key_name_split[2][-1]) - 1\n            # deal with short cut\n            if 'shortcut' in key_name_split[4]:\n                if 'shortcut' == key_name_split[-2]:\n                    name = f'backbone.layer{res_id}.' \\\n                           f'{key_name_split[3]}.downsample.0.' \\\n                           f'{key_name_split[-1]}'\n                elif 'shortcut' == key_name_split[-3]:\n                    name = f'backbone.layer{res_id}.' \\\n                           f'{key_name_split[3]}.downsample.1.' \\\n                           f'{key_name_split[-1]}'\n                else:\n                    print(f'Unvalid key {k}')\n            # deal with conv\n            elif 'conv' in key_name_split[-2]:\n                conv_id = int(key_name_split[-2][-1])\n                name = f'backbone.layer{res_id}.{key_name_split[3]}' \\\n                       f'.conv{conv_id}.{key_name_split[-1]}'\n            # deal with BN\n            elif key_name_split[-2] == 'norm':\n                conv_id = int(key_name_split[-3][-1])\n                name = f'backbone.layer{res_id}.{key_name_split[3]}.' \\\n                       f'bn{conv_id}.{key_name_split[-1]}'\n            else:\n                print(f'{k} is invalid')\n\n        elif key_name_split[0] == 'head':\n            # d2: head.xxx -> mmdet: bbox_head.xxx\n            name = f'bbox_{k}'\n        else:\n            # some base parameters such as beta will not convert\n            print(f'{k} is not converted!!')\n            continue\n\n        if not isinstance(v, np.ndarray) and not isinstance(v, torch.Tensor):\n            raise ValueError(\n                'Unsupported type found in checkpoint! {}: {}'.format(\n                    k, type(v)))\n        if not isinstance(v, torch.Tensor):\n            dst_state_dict[name] = torch.from_numpy(v)\n        else:\n            dst_state_dict[name] = v\n    mmdet_model = dict(state_dict=dst_state_dict, meta=dict())\n    torch.save(mmdet_model, dst)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description='Convert model keys')\n    parser.add_argument('src', help='src detectron model path')\n    parser.add_argument('dst', help='save path')\n    args = parser.parse_args()\n    convert(args.src, args.dst)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/EfficientDet/README.md",
    "content": "# EfficientDet\n\n> [**EfficientDet: Scalable and Efficient Object Detection**](https://arxiv.org/pdf/1911.09070.pdf),\n> Mingxing Tan, Ruoming Pang, Quoc V. Le,\n> *CVPR 2020*\n\n## Abstract\n\nThis is an implementation of [EfficientDet](https://github.com/google/automl) based on [MMDetection](https://github.com/open-mmlab/mmdetection/tree/main), [MMCV](https://github.com/open-mmlab/mmcv), and [MMEngine](https://github.com/open-mmlab/mmengine).\n<br>\nEfficientDet a new family of object detectors, which consistently achieve much better efficiency than prior art across a wide\nspectrum of resource constraints.\nIn particular, with single model and single-scale, EfficientDet-D7 achieves stateof-the-art 55.1 AP on COCO test-dev with 77M parameters and 410B FLOP.\n<br>\nBiFPN is a simple yet highly effective weighted bi-directional feature pyramid network, which introduces learnable weights to learn the importance of different input features, while repeatedly applying topdown and bottom-up multi-scale feature fusion.\n<br>\nIn contrast to other feature pyramid network, such as FPN, FPN + PAN, NAS-FPN, BiFPN achieves  the best accuracy with fewer parameters and FLOPs.\n\n<div align=\"center\">\n<img src=\"https://github.com/zwhus/pictures/raw/main/Screenshot%20from%202023-01-31%2010-38-51.png\">\n</div>\n\n## Usage\n\n## Official TensorFlow Model\n\nThis project also supports [official tensorflow model](https://github.com/google/automl), it uses 90 categories and yxyx box encoding in training. If you want to use the original model weight to get official results, please refer to the following steps.\n\n### Model conversion\n\nFirstly, download EfficientDet [weights](https://github.com/google/automl/tree/master/efficientdet) and unzip,  please use the following command\n\n```bash\ntar -xzvf {EFFICIENTDET_WEIGHT}\n```\n\nThen, install tensorflow, please use the following command\n\n```bash\npip install tensorflow-gpu==2.6.0\n```\n\nLastly, convert weights from tensorflow to pytorch, please use the following command\n\n```bash\npython projects/EfficientDet/convert_tf_to_pt.py --backbone {BACKBONE_NAME} --tensorflow_weight {TENSORFLOW_WEIGHT_PATH} --out_weight {OUT_PATH}\n```\n\n### Testing commands\n\nIn MMDetection's root directory, run the following command to test the model:\n\n```bash\npython tools/test.py projects/EfficientDet/configs/tensorflow/efficientdet_effb0_bifpn_8xb16-crop512-300e_coco_tf.py ${CHECKPOINT_PATH}\n```\n\n## Reproduce Model\n\nFor convenience, we recommend the current implementation version, it uses 80 categories and xyxy encoding in training. On this basis, a higher result was finally achieved.\n\n### Training commands\n\nIn MMDetection's root directory, run the following command to train the model:\n\n```bash\npython tools/train.py projects/EfficientDet/configs/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco.py\n```\n\n### Testing commands\n\nIn MMDetection's root directory, run the following command to test the model:\n\n```bash\npython tools/test.py projects/EfficientDet/configs/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco.py ${CHECKPOINT_PATH}\n```\n\n## Results\n\nBased on mmdetection, this project aligns the accuracy of the [official model](https://github.com/google/automl).\n\n|                                                        Method                                                        |    Backbone     | Pretrained Model |  Training set  |   Test set   | Epoch | Val Box AP | Official AP |                                                                                                                                                                                                Download                                                                                                                                                                                                |\n| :------------------------------------------------------------------------------------------------------------------: | :-------------: | :--------------: | :------------: | :----------: | :---: | :--------: | :---------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| [efficientdet-d0\\*](projects/EfficientDet/configs/tensorflow/efficientdet_effb0_bifpn_8xb16-crop512-300e_coco_tf.py) | efficientnet-b0 |     ImageNet     | COCO2017 Train | COCO2017 Val |  300  |    34.4    |    34.3     |                                                                                                                                                                                                                                                                                                                                                                                                        |\n|         [efficientdet-d3](projects/EfficientDet/configs/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco.py)         | efficientnet-b3 |     ImageNet     | COCO2017 Train | COCO2017 Val |  300  |    47.2    |    46.8     | [model](https://download.openmmlab.com/mmdetection/v3.0/efficientdet/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco_20230223_122457-e6f7a833.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/efficientdet/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco_20230223_122457.log.json) |\n\n**Note**:\n\\*means use [official tensorflow model](https://github.com/google/automl) weights to test.\n\n## Citation\n\n```BibTeX\n@inproceedings{tan2020efficientdet,\n  title={Efficientdet: Scalable and efficient object detection},\n  author={Tan, Mingxing and Pang, Ruoming and Le, Quoc V},\n  booktitle={Proceedings of the IEEE/CVF conference on computer vision and pattern recognition},\n  pages={10781--10790},\n  year={2020}\n}\n```\n\n## Checklist\n\n<!-- Here is a checklist illustrating a usual development workflow of a successful project, and also serves as an overview of this project's progress. The PIC (person in charge) or contributors of this project should check all the items that they believe have been finished, which will further be verified by codebase maintainers via a PR.\nOpenMMLab's maintainer will review the code to ensure the project's quality. Reaching the first milestone means that this project suffices the minimum requirement of being merged into 'projects/'. But this project is only eligible to become a part of the core package upon attaining the last milestone.\nNote that keeping this section up-to-date is crucial not only for this project's developers but the entire community, since there might be some other contributors joining this project and deciding their starting point from this list. It also helps maintainers accurately estimate time and effort on further code polishing, if needed.\nA project does not necessarily have to be finished in a single PR, but it's essential for the project to at least reach the first milestone in its very first PR. -->\n\n- [x] Milestone 1: PR-ready, and acceptable to be one of the `projects/`.\n\n  - [x] Finish the code\n\n    <!-- The code's design shall follow existing interfaces and convention. For example, each model component should be registered into `mmdet.registry.MODELS` and configurable via a config file. -->\n\n  - [x] Basic docstrings & proper citation\n\n    <!-- Each major object should contain a docstring, describing its functionality and arguments. If you have adapted the code from other open-source projects, don't forget to cite the source project in docstring and make sure your behavior is not against its license. Typically, we do not accept any code snippet under GPL license. [A Short Guide to Open Source Licenses](https://medium.com/nationwide-technology/a-short-guide-to-open-source-licenses-cf5b1c329edd) -->\n\n  - [x] Test-time correctness\n\n    <!-- If you are reproducing the result from a paper, make sure your model's inference-time performance matches that in the original paper. The weights usually could be obtained by simply renaming the keys in the official pre-trained weights. This test could be skipped though, if you are able to prove the training-time correctness and check the second milestone. -->\n\n  - [x] A full README\n\n    <!-- As this template does. -->\n\n- [x] Milestone 2: Indicates a successful model implementation.\n\n  - [x] Training-time correctness\n\n    <!-- If you are reproducing the result from a paper, checking this item means that you should have trained your model from scratch based on the original paper's specification and verified that the final result matches the report within a minor error range. -->\n\n- [ ] Milestone 3: Good to be a part of our core package!\n\n  - [ ] Type hints and docstrings\n\n    <!-- Ideally *all* the methods should have [type hints](https://www.pythontutorial.net/python-basics/python-type-hints/) and [docstrings](https://google.github.io/styleguide/pyguide.html#381-docstrings). [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/mmdet/datasets/transforms/transforms.py#L41-L169) -->\n\n  - [ ] Unit tests\n\n    <!-- Unit tests for each module are required. [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/tests/test_datasets/test_transforms/test_transforms.py#L35-L88) -->\n\n  - [ ] Code polishing\n\n    <!-- Refactor your code according to reviewer's comment. -->\n\n  - [ ] Metafile.yml\n\n    <!-- It will be parsed by MIM and Inferencer. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/metafile.yml) -->\n\n- [ ] Move your modules into the core package following the codebase's file hierarchy structure.\n\n  <!-- In particular, you may have to refactor this README into a standard one. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/README.md) -->\n\n- [ ] Refactor your modules into the core package following the codebase's file hierarchy structure.\n"
  },
  {
    "path": "projects/EfficientDet/configs/efficientdet_effb0_bifpn_8xb16-crop512-300e_coco.py",
    "content": "_base_ = [\n    'mmdet::_base_/datasets/coco_detection.py',\n    'mmdet::_base_/schedules/schedule_1x.py',\n    'mmdet::_base_/default_runtime.py'\n]\ncustom_imports = dict(\n    imports=['projects.EfficientDet.efficientdet'], allow_failed_imports=False)\n\nimage_size = 512\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=(image_size, image_size))\n]\ndataset_type = 'CocoDataset'\nevalute_type = 'CocoMetric'\nnorm_cfg = dict(type='SyncBN', requires_grad=True, eps=1e-3, momentum=0.01)\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/efficientnet/efficientnet-b0_3rdparty_8xb32-aa-advprop_in1k_20220119-26434485.pth'  # noqa\nmodel = dict(\n    type='EfficientDet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=image_size,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='EfficientNet',\n        arch='b0',\n        drop_path_rate=0.2,\n        out_indices=(3, 4, 5),\n        frozen_stages=0,\n        conv_cfg=dict(type='Conv2dSamePadding'),\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone', checkpoint=checkpoint)),\n    neck=dict(\n        type='BiFPN',\n        num_stages=3,\n        in_channels=[40, 112, 320],\n        out_channels=64,\n        start_level=0,\n        norm_cfg=norm_cfg),\n    bbox_head=dict(\n        type='EfficientDetSepBNHead',\n        num_classes=80,\n        num_ins=5,\n        in_channels=64,\n        feat_channels=64,\n        stacked_convs=3,\n        norm_cfg=norm_cfg,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[1.0, 0.5, 2.0],\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=1.5,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='HuberLoss', beta=0.1, loss_weight=50)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        sampler=dict(\n            type='PseudoSampler'),  # Focal loss should use PseudoSampler\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(\n            type='soft_nms',\n            iou_threshold=0.3,\n            sigma=0.5,\n            min_score=1e-3,\n            method='gaussian'),\n        max_per_img=100))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(image_size, image_size),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(image_size, image_size)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(image_size, image_size), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=16,\n    num_workers=8,\n    dataset=dict(type=dataset_type, pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(type=dataset_type, pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type=evalute_type)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.16, weight_decay=4e-5),\n    paramwise_cfg=dict(\n        norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True),\n    clip_grad=dict(max_norm=10, norm_type=2))\n\n# learning policy\nmax_epochs = 300\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=917),\n    dict(\n        type='CosineAnnealingLR',\n        eta_min=0.0,\n        begin=1,\n        T_max=299,\n        end=300,\n        by_epoch=True,\n        convert_to_iter_based=True)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\nvis_backends = [\n    dict(type='LocalVisBackend'),\n    dict(type='TensorboardVisBackend')\n]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\ndefault_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=15))\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49)\n]\n# cudnn_benchmark=True can accelerate fix-size training\nenv_cfg = dict(cudnn_benchmark=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (16 samples per GPU)\nauto_scale_lr = dict(base_batch_size=128)\n"
  },
  {
    "path": "projects/EfficientDet/configs/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco-90cls.py",
    "content": "_base_ = [\n    'mmdet::_base_/datasets/coco_detection.py',\n    'mmdet::_base_/schedules/schedule_1x.py',\n    'mmdet::_base_/default_runtime.py'\n]\ncustom_imports = dict(\n    imports=['projects.EfficientDet.efficientdet'], allow_failed_imports=False)\n\nimage_size = 896\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=(image_size, image_size))\n]\ndataset_type = 'Coco90Dataset'\nevalute_type = 'Coco90Metric'\nnorm_cfg = dict(type='SyncBN', requires_grad=True, eps=1e-3, momentum=0.01)\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/efficientnet/efficientnet-b3_3rdparty_8xb32-aa-advprop_in1k_20220119-53b41118.pth'  # noqa\nmodel = dict(\n    type='EfficientDet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=image_size,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='EfficientNet',\n        arch='b3',\n        drop_path_rate=0.3,\n        out_indices=(3, 4, 5),\n        frozen_stages=0,\n        conv_cfg=dict(type='Conv2dSamePadding'),\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone', checkpoint=checkpoint)),\n    neck=dict(\n        type='BiFPN',\n        num_stages=6,\n        in_channels=[48, 136, 384],\n        out_channels=160,\n        start_level=0,\n        norm_cfg=norm_cfg),\n    bbox_head=dict(\n        type='EfficientDetSepBNHead',\n        num_classes=90,\n        num_ins=5,\n        in_channels=160,\n        feat_channels=160,\n        stacked_convs=4,\n        norm_cfg=norm_cfg,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[1.0, 0.5, 2.0],\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=1.5,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='HuberLoss', beta=0.1, loss_weight=50)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        sampler=dict(\n            type='PseudoSampler'),  # Focal loss should use PseudoSampler\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(\n            type='soft_nms',\n            iou_threshold=0.3,\n            sigma=0.5,\n            min_score=1e-3,\n            method='gaussian'),\n        max_per_img=100))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(image_size, image_size),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(image_size, image_size)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(image_size, image_size), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=16,\n    num_workers=8,\n    dataset=dict(type=dataset_type, pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(type=dataset_type, pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type=evalute_type)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.16, weight_decay=4e-5),\n    paramwise_cfg=dict(\n        norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True),\n    clip_grad=dict(max_norm=10, norm_type=2))\n\n# learning policy\nmax_epochs = 300\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=917),\n    dict(\n        type='CosineAnnealingLR',\n        eta_min=0.0,\n        begin=1,\n        T_max=299,\n        end=300,\n        by_epoch=True,\n        convert_to_iter_based=True)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\nvis_backends = [\n    dict(type='LocalVisBackend'),\n    dict(type='TensorboardVisBackend')\n]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\ndefault_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=15))\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49)\n]\n# cudnn_benchmark=True can accelerate fix-size training\nenv_cfg = dict(cudnn_benchmark=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (16 samples per GPU)\nauto_scale_lr = dict(base_batch_size=128)\n"
  },
  {
    "path": "projects/EfficientDet/configs/efficientdet_effb3_bifpn_8xb16-crop896-300e_coco.py",
    "content": "_base_ = [\n    'mmdet::_base_/datasets/coco_detection.py',\n    'mmdet::_base_/schedules/schedule_1x.py',\n    'mmdet::_base_/default_runtime.py'\n]\ncustom_imports = dict(\n    imports=['projects.EfficientDet.efficientdet'], allow_failed_imports=False)\n\nimage_size = 896\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=(image_size, image_size))\n]\ndataset_type = 'CocoDataset'\nevalute_type = 'CocoMetric'\nnorm_cfg = dict(type='SyncBN', requires_grad=True, eps=1e-3, momentum=0.01)\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/efficientnet/efficientnet-b3_3rdparty_8xb32-aa-advprop_in1k_20220119-53b41118.pth'  # noqa\nmodel = dict(\n    type='EfficientDet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=image_size,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='EfficientNet',\n        arch='b3',\n        drop_path_rate=0.3,\n        out_indices=(3, 4, 5),\n        frozen_stages=0,\n        conv_cfg=dict(type='Conv2dSamePadding'),\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone', checkpoint=checkpoint)),\n    neck=dict(\n        type='BiFPN',\n        num_stages=6,\n        in_channels=[48, 136, 384],\n        out_channels=160,\n        start_level=0,\n        norm_cfg=norm_cfg),\n    bbox_head=dict(\n        type='EfficientDetSepBNHead',\n        num_classes=80,\n        num_ins=5,\n        in_channels=160,\n        feat_channels=160,\n        stacked_convs=4,\n        norm_cfg=norm_cfg,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[1.0, 0.5, 2.0],\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=1.5,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='HuberLoss', beta=0.1, loss_weight=50)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='MaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        sampler=dict(\n            type='PseudoSampler'),  # Focal loss should use PseudoSampler\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(\n            type='soft_nms',\n            iou_threshold=0.3,\n            sigma=0.5,\n            min_score=1e-3,\n            method='gaussian'),\n        max_per_img=100))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(image_size, image_size),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(image_size, image_size)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(image_size, image_size), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=16,\n    num_workers=8,\n    dataset=dict(type=dataset_type, pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(type=dataset_type, pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type=evalute_type)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.16, weight_decay=4e-5),\n    paramwise_cfg=dict(\n        norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True),\n    clip_grad=dict(max_norm=10, norm_type=2))\n\n# learning policy\nmax_epochs = 300\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=917),\n    dict(\n        type='CosineAnnealingLR',\n        eta_min=0.0,\n        begin=1,\n        T_max=299,\n        end=300,\n        by_epoch=True,\n        convert_to_iter_based=True)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\nvis_backends = [\n    dict(type='LocalVisBackend'),\n    dict(type='TensorboardVisBackend')\n]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\ndefault_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=15))\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49)\n]\n# cudnn_benchmark=True can accelerate fix-size training\nenv_cfg = dict(cudnn_benchmark=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (16 samples per GPU)\nauto_scale_lr = dict(base_batch_size=128)\n"
  },
  {
    "path": "projects/EfficientDet/configs/tensorflow/efficientdet_effb0_bifpn_8xb16-crop512-300e_coco_tf.py",
    "content": "_base_ = [\n    'mmdet::_base_/datasets/coco_detection.py',\n    'mmdet::_base_/schedules/schedule_1x.py',\n    'mmdet::_base_/default_runtime.py'\n]\ncustom_imports = dict(\n    imports=['projects.EfficientDet.efficientdet'], allow_failed_imports=False)\n\nimage_size = 512\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=(image_size, image_size))\n]\ndataset_type = 'Coco90Dataset'\nevalute_type = 'Coco90Metric'\nnorm_cfg = dict(type='SyncBN', requires_grad=True, eps=1e-3, momentum=0.01)\ncheckpoint = 'https://download.openmmlab.com/mmclassification/v0/efficientnet/efficientnet-b0_3rdparty_8xb32-aa-advprop_in1k_20220119-26434485.pth'  # noqa\nmodel = dict(\n    type='EfficientDet',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=image_size,\n        batch_augments=batch_augments),\n    backbone=dict(\n        type='EfficientNet',\n        arch='b0',\n        drop_path_rate=0.2,\n        out_indices=(3, 4, 5),\n        frozen_stages=0,\n        conv_cfg=dict(type='Conv2dSamePadding'),\n        norm_cfg=norm_cfg,\n        norm_eval=False,\n        init_cfg=dict(\n            type='Pretrained', prefix='backbone', checkpoint=checkpoint)),\n    neck=dict(\n        type='BiFPN',\n        num_stages=3,\n        in_channels=[40, 112, 320],\n        out_channels=64,\n        start_level=0,\n        norm_cfg=norm_cfg),\n    bbox_head=dict(\n        type='EfficientDetSepBNHead',\n        num_classes=90,\n        num_ins=5,\n        in_channels=64,\n        feat_channels=64,\n        stacked_convs=3,\n        norm_cfg=norm_cfg,\n        anchor_generator=dict(\n            type='YXYXAnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[1.0, 0.5, 2.0],\n            strides=[8, 16, 32, 64, 128],\n            center_offset=0.5),\n        bbox_coder=dict(\n            type='YXYXDeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=1.5,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='HuberLoss', beta=0.1, loss_weight=50)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='TransMaxIoUAssigner',\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n            min_pos_iou=0,\n            ignore_iof_thr=-1),\n        sampler=dict(\n            type='PseudoSampler'),  # Focal loss should use PseudoSampler\n        allowed_border=-1,\n        pos_weight=-1,\n        debug=False),\n    test_cfg=dict(\n        nms_pre=1000,\n        min_bbox_size=0,\n        score_thr=0.05,\n        nms=dict(\n            type='soft_nms',\n            iou_threshold=0.3,\n            sigma=0.5,\n            min_score=1e-3,\n            method='gaussian'),\n        max_per_img=100))\n\n# dataset settings\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=(image_size, image_size),\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=(image_size, image_size)),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),\n    dict(type='Resize', scale=(image_size, image_size), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=16,\n    num_workers=8,\n    dataset=dict(type=dataset_type, pipeline=train_pipeline))\nval_dataloader = dict(dataset=dict(type=dataset_type, pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type=evalute_type)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    optimizer=dict(lr=0.16, weight_decay=4e-5),\n    paramwise_cfg=dict(\n        norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True),\n    clip_grad=dict(max_norm=10, norm_type=2))\n\n# learning policy\nmax_epochs = 300\nparam_scheduler = [\n    dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=917),\n    dict(\n        type='CosineAnnealingLR',\n        eta_min=0.0,\n        begin=1,\n        T_max=299,\n        end=300,\n        by_epoch=True,\n        convert_to_iter_based=True)\n]\ntrain_cfg = dict(max_epochs=max_epochs, val_interval=1)\n\nvis_backends = [\n    dict(type='LocalVisBackend'),\n    dict(type='TensorboardVisBackend')\n]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\n\ndefault_hooks = dict(checkpoint=dict(type='CheckpointHook', interval=15))\ncustom_hooks = [\n    dict(\n        type='EMAHook',\n        ema_type='ExpMomentumEMA',\n        momentum=0.0002,\n        update_buffers=True,\n        priority=49)\n]\n# cudnn_benchmark=True can accelerate fix-size training\nenv_cfg = dict(cudnn_benchmark=True)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (16 samples per GPU)\nauto_scale_lr = dict(base_batch_size=128)\n"
  },
  {
    "path": "projects/EfficientDet/convert_tf_to_pt.py",
    "content": "import argparse\n\nimport numpy as np\nimport torch\nfrom tensorflow.python.training import py_checkpoint_reader\n\ntorch.set_printoptions(precision=20)\n\n\ndef tf2pth(v):\n    if v.ndim == 4:\n        return np.ascontiguousarray(v.transpose(3, 2, 0, 1))\n    elif v.ndim == 2:\n        return np.ascontiguousarray(v.transpose())\n    return v\n\n\ndef convert_key(model_name, bifpn_repeats, weights):\n\n    p6_w1 = [\n        torch.tensor([-1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    p5_w1 = [\n        torch.tensor([-1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    p4_w1 = [\n        torch.tensor([-1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    p3_w1 = [\n        torch.tensor([-1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    p4_w2 = [\n        torch.tensor([-1e4, -1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    p5_w2 = [\n        torch.tensor([-1e4, -1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    p6_w2 = [\n        torch.tensor([-1e4, -1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    p7_w2 = [\n        torch.tensor([-1e4, -1e4], dtype=torch.float64)\n        for _ in range(bifpn_repeats)\n    ]\n    idx2key = {\n        0: '1.0',\n        1: '2.0',\n        2: '2.1',\n        3: '3.0',\n        4: '3.1',\n        5: '4.0',\n        6: '4.1',\n        7: '4.2',\n        8: '4.3',\n        9: '4.4',\n        10: '4.5',\n        11: '5.0',\n        12: '5.1',\n        13: '5.2',\n        14: '5.3',\n        15: '5.4'\n    }\n    m = dict()\n    for k, v in weights.items():\n\n        if 'Exponential' in k or 'global_step' in k:\n            continue\n\n        seg = k.split('/')\n        if len(seg) == 1:\n            continue\n        if seg[2] == 'depthwise_conv2d':\n            v = v.transpose(1, 0)\n\n        if seg[0] == model_name:\n            if seg[1] == 'stem':\n                prefix = 'backbone.layers.0'\n                mapping = {\n                    'conv2d/kernel': 'conv.weight',\n                    'tpu_batch_normalization/beta': 'bn.bias',\n                    'tpu_batch_normalization/gamma': 'bn.weight',\n                    'tpu_batch_normalization/moving_mean': 'bn.running_mean',\n                    'tpu_batch_normalization/moving_variance':\n                    'bn.running_var',\n                }\n                suffix = mapping['/'.join(seg[2:])]\n                m[prefix + '.' + suffix] = v\n\n            elif seg[1].startswith('blocks_'):\n                idx = int(seg[1][7:])\n                prefix = '.'.join(['backbone', 'layers', idx2key[idx]])\n                base_mapping = {\n                    'depthwise_conv2d/depthwise_kernel':\n                    'depthwise_conv.conv.weight',\n                    'se/conv2d/kernel': 'se.conv1.conv.weight',\n                    'se/conv2d/bias': 'se.conv1.conv.bias',\n                    'se/conv2d_1/kernel': 'se.conv2.conv.weight',\n                    'se/conv2d_1/bias': 'se.conv2.conv.bias'\n                }\n                if idx == 0:\n                    mapping = {\n                        'conv2d/kernel':\n                        'linear_conv.conv.weight',\n                        'tpu_batch_normalization/beta':\n                        'depthwise_conv.bn.bias',\n                        'tpu_batch_normalization/gamma':\n                        'depthwise_conv.bn.weight',\n                        'tpu_batch_normalization/moving_mean':\n                        'depthwise_conv.bn.running_mean',\n                        'tpu_batch_normalization/moving_variance':\n                        'depthwise_conv.bn.running_var',\n                        'tpu_batch_normalization_1/beta':\n                        'linear_conv.bn.bias',\n                        'tpu_batch_normalization_1/gamma':\n                        'linear_conv.bn.weight',\n                        'tpu_batch_normalization_1/moving_mean':\n                        'linear_conv.bn.running_mean',\n                        'tpu_batch_normalization_1/moving_variance':\n                        'linear_conv.bn.running_var',\n                    }\n                else:\n                    mapping = {\n                        'depthwise_conv2d/depthwise_kernel':\n                        'depthwise_conv.conv.weight',\n                        'conv2d/kernel':\n                        'expand_conv.conv.weight',\n                        'conv2d_1/kernel':\n                        'linear_conv.conv.weight',\n                        'tpu_batch_normalization/beta':\n                        'expand_conv.bn.bias',\n                        'tpu_batch_normalization/gamma':\n                        'expand_conv.bn.weight',\n                        'tpu_batch_normalization/moving_mean':\n                        'expand_conv.bn.running_mean',\n                        'tpu_batch_normalization/moving_variance':\n                        'expand_conv.bn.running_var',\n                        'tpu_batch_normalization_1/beta':\n                        'depthwise_conv.bn.bias',\n                        'tpu_batch_normalization_1/gamma':\n                        'depthwise_conv.bn.weight',\n                        'tpu_batch_normalization_1/moving_mean':\n                        'depthwise_conv.bn.running_mean',\n                        'tpu_batch_normalization_1/moving_variance':\n                        'depthwise_conv.bn.running_var',\n                        'tpu_batch_normalization_2/beta':\n                        'linear_conv.bn.bias',\n                        'tpu_batch_normalization_2/gamma':\n                        'linear_conv.bn.weight',\n                        'tpu_batch_normalization_2/moving_mean':\n                        'linear_conv.bn.running_mean',\n                        'tpu_batch_normalization_2/moving_variance':\n                        'linear_conv.bn.running_var',\n                    }\n                mapping.update(base_mapping)\n                suffix = mapping['/'.join(seg[2:])]\n                m[prefix + '.' + suffix] = v\n        elif seg[0] == 'resample_p6':\n            prefix = 'neck.bifpn.0.p5_to_p6.0'\n            mapping = {\n                'conv2d/kernel': 'down_conv.weight',\n                'conv2d/bias': 'down_conv.bias',\n                'bn/beta': 'bn.bias',\n                'bn/gamma': 'bn.weight',\n                'bn/moving_mean': 'bn.running_mean',\n                'bn/moving_variance': 'bn.running_var',\n            }\n            suffix = mapping['/'.join(seg[1:])]\n            m[prefix + '.' + suffix] = v\n        elif seg[0] == 'fpn_cells':\n            fpn_idx = int(seg[1][5:])\n            prefix = '.'.join(['neck', 'bifpn', str(fpn_idx)])\n            fnode_id = int(seg[2][5])\n            if fnode_id == 0:\n                mapping = {\n                    'op_after_combine5/conv/depthwise_kernel':\n                    'conv6_up.depthwise_conv.weight',\n                    'op_after_combine5/conv/pointwise_kernel':\n                    'conv6_up.pointwise_conv.weight',\n                    'op_after_combine5/conv/bias':\n                    'conv6_up.pointwise_conv.bias',\n                    'op_after_combine5/bn/beta':\n                    'conv6_up.bn.bias',\n                    'op_after_combine5/bn/gamma':\n                    'conv6_up.bn.weight',\n                    'op_after_combine5/bn/moving_mean':\n                    'conv6_up.bn.running_mean',\n                    'op_after_combine5/bn/moving_variance':\n                    'conv6_up.bn.running_var',\n                }\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1':\n                    suffix = mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p6_w1[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p6_w1[fpn_idx][1] = v\n                if torch.min(p6_w1[fpn_idx]) > -1e4:\n                    m[prefix + '.p6_w1'] = p6_w1[fpn_idx]\n            elif fnode_id == 1:\n                base_mapping = {\n                    'op_after_combine6/conv/depthwise_kernel':\n                    'conv5_up.depthwise_conv.weight',\n                    'op_after_combine6/conv/pointwise_kernel':\n                    'conv5_up.pointwise_conv.weight',\n                    'op_after_combine6/conv/bias':\n                    'conv5_up.pointwise_conv.bias',\n                    'op_after_combine6/bn/beta':\n                    'conv5_up.bn.bias',\n                    'op_after_combine6/bn/gamma':\n                    'conv5_up.bn.weight',\n                    'op_after_combine6/bn/moving_mean':\n                    'conv5_up.bn.running_mean',\n                    'op_after_combine6/bn/moving_variance':\n                    'conv5_up.bn.running_var',\n                }\n                if fpn_idx == 0:\n                    mapping = {\n                        'resample_0_2_6/conv2d/kernel':\n                        'p5_down_channel.down_conv.weight',\n                        'resample_0_2_6/conv2d/bias':\n                        'p5_down_channel.down_conv.bias',\n                        'resample_0_2_6/bn/beta':\n                        'p5_down_channel.bn.bias',\n                        'resample_0_2_6/bn/gamma':\n                        'p5_down_channel.bn.weight',\n                        'resample_0_2_6/bn/moving_mean':\n                        'p5_down_channel.bn.running_mean',\n                        'resample_0_2_6/bn/moving_variance':\n                        'p5_down_channel.bn.running_var',\n                    }\n                    base_mapping.update(mapping)\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1':\n                    suffix = base_mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p5_w1[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p5_w1[fpn_idx][1] = v\n                if torch.min(p5_w1[fpn_idx]) > -1e4:\n                    m[prefix + '.p5_w1'] = p5_w1[fpn_idx]\n            elif fnode_id == 2:\n                base_mapping = {\n                    'op_after_combine7/conv/depthwise_kernel':\n                    'conv4_up.depthwise_conv.weight',\n                    'op_after_combine7/conv/pointwise_kernel':\n                    'conv4_up.pointwise_conv.weight',\n                    'op_after_combine7/conv/bias':\n                    'conv4_up.pointwise_conv.bias',\n                    'op_after_combine7/bn/beta':\n                    'conv4_up.bn.bias',\n                    'op_after_combine7/bn/gamma':\n                    'conv4_up.bn.weight',\n                    'op_after_combine7/bn/moving_mean':\n                    'conv4_up.bn.running_mean',\n                    'op_after_combine7/bn/moving_variance':\n                    'conv4_up.bn.running_var',\n                }\n                if fpn_idx == 0:\n                    mapping = {\n                        'resample_0_1_7/conv2d/kernel':\n                        'p4_down_channel.down_conv.weight',\n                        'resample_0_1_7/conv2d/bias':\n                        'p4_down_channel.down_conv.bias',\n                        'resample_0_1_7/bn/beta':\n                        'p4_down_channel.bn.bias',\n                        'resample_0_1_7/bn/gamma':\n                        'p4_down_channel.bn.weight',\n                        'resample_0_1_7/bn/moving_mean':\n                        'p4_down_channel.bn.running_mean',\n                        'resample_0_1_7/bn/moving_variance':\n                        'p4_down_channel.bn.running_var',\n                    }\n                    base_mapping.update(mapping)\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1':\n                    suffix = base_mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p4_w1[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p4_w1[fpn_idx][1] = v\n                if torch.min(p4_w1[fpn_idx]) > -1e4:\n                    m[prefix + '.p4_w1'] = p4_w1[fpn_idx]\n            elif fnode_id == 3:\n\n                base_mapping = {\n                    'op_after_combine8/conv/depthwise_kernel':\n                    'conv3_up.depthwise_conv.weight',\n                    'op_after_combine8/conv/pointwise_kernel':\n                    'conv3_up.pointwise_conv.weight',\n                    'op_after_combine8/conv/bias':\n                    'conv3_up.pointwise_conv.bias',\n                    'op_after_combine8/bn/beta':\n                    'conv3_up.bn.bias',\n                    'op_after_combine8/bn/gamma':\n                    'conv3_up.bn.weight',\n                    'op_after_combine8/bn/moving_mean':\n                    'conv3_up.bn.running_mean',\n                    'op_after_combine8/bn/moving_variance':\n                    'conv3_up.bn.running_var',\n                }\n                if fpn_idx == 0:\n                    mapping = {\n                        'resample_0_0_8/conv2d/kernel':\n                        'p3_down_channel.down_conv.weight',\n                        'resample_0_0_8/conv2d/bias':\n                        'p3_down_channel.down_conv.bias',\n                        'resample_0_0_8/bn/beta':\n                        'p3_down_channel.bn.bias',\n                        'resample_0_0_8/bn/gamma':\n                        'p3_down_channel.bn.weight',\n                        'resample_0_0_8/bn/moving_mean':\n                        'p3_down_channel.bn.running_mean',\n                        'resample_0_0_8/bn/moving_variance':\n                        'p3_down_channel.bn.running_var',\n                    }\n                    base_mapping.update(mapping)\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1':\n                    suffix = base_mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p3_w1[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p3_w1[fpn_idx][1] = v\n                if torch.min(p3_w1[fpn_idx]) > -1e4:\n                    m[prefix + '.p3_w1'] = p3_w1[fpn_idx]\n            elif fnode_id == 4:\n                base_mapping = {\n                    'op_after_combine9/conv/depthwise_kernel':\n                    'conv4_down.depthwise_conv.weight',\n                    'op_after_combine9/conv/pointwise_kernel':\n                    'conv4_down.pointwise_conv.weight',\n                    'op_after_combine9/conv/bias':\n                    'conv4_down.pointwise_conv.bias',\n                    'op_after_combine9/bn/beta':\n                    'conv4_down.bn.bias',\n                    'op_after_combine9/bn/gamma':\n                    'conv4_down.bn.weight',\n                    'op_after_combine9/bn/moving_mean':\n                    'conv4_down.bn.running_mean',\n                    'op_after_combine9/bn/moving_variance':\n                    'conv4_down.bn.running_var',\n                }\n                if fpn_idx == 0:\n                    mapping = {\n                        'resample_0_1_9/conv2d/kernel':\n                        'p4_level_connection.down_conv.weight',\n                        'resample_0_1_9/conv2d/bias':\n                        'p4_level_connection.down_conv.bias',\n                        'resample_0_1_9/bn/beta':\n                        'p4_level_connection.bn.bias',\n                        'resample_0_1_9/bn/gamma':\n                        'p4_level_connection.bn.weight',\n                        'resample_0_1_9/bn/moving_mean':\n                        'p4_level_connection.bn.running_mean',\n                        'resample_0_1_9/bn/moving_variance':\n                        'p4_level_connection.bn.running_var',\n                    }\n                    base_mapping.update(mapping)\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1' and seg[3] != 'WSM_2':\n                    suffix = base_mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p4_w2[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p4_w2[fpn_idx][1] = v\n                elif seg[3] == 'WSM_2':\n                    p4_w2[fpn_idx][2] = v\n                if torch.min(p4_w2[fpn_idx]) > -1e4:\n                    m[prefix + '.p4_w2'] = p4_w2[fpn_idx]\n            elif fnode_id == 5:\n                base_mapping = {\n                    'op_after_combine10/conv/depthwise_kernel':\n                    'conv5_down.depthwise_conv.weight',\n                    'op_after_combine10/conv/pointwise_kernel':\n                    'conv5_down.pointwise_conv.weight',\n                    'op_after_combine10/conv/bias':\n                    'conv5_down.pointwise_conv.bias',\n                    'op_after_combine10/bn/beta':\n                    'conv5_down.bn.bias',\n                    'op_after_combine10/bn/gamma':\n                    'conv5_down.bn.weight',\n                    'op_after_combine10/bn/moving_mean':\n                    'conv5_down.bn.running_mean',\n                    'op_after_combine10/bn/moving_variance':\n                    'conv5_down.bn.running_var',\n                }\n                if fpn_idx == 0:\n                    mapping = {\n                        'resample_0_2_10/conv2d/kernel':\n                        'p5_level_connection.down_conv.weight',\n                        'resample_0_2_10/conv2d/bias':\n                        'p5_level_connection.down_conv.bias',\n                        'resample_0_2_10/bn/beta':\n                        'p5_level_connection.bn.bias',\n                        'resample_0_2_10/bn/gamma':\n                        'p5_level_connection.bn.weight',\n                        'resample_0_2_10/bn/moving_mean':\n                        'p5_level_connection.bn.running_mean',\n                        'resample_0_2_10/bn/moving_variance':\n                        'p5_level_connection.bn.running_var',\n                    }\n                    base_mapping.update(mapping)\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1' and seg[3] != 'WSM_2':\n                    suffix = base_mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p5_w2[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p5_w2[fpn_idx][1] = v\n                elif seg[3] == 'WSM_2':\n                    p5_w2[fpn_idx][2] = v\n                if torch.min(p5_w2[fpn_idx]) > -1e4:\n                    m[prefix + '.p5_w2'] = p5_w2[fpn_idx]\n            elif fnode_id == 6:\n                base_mapping = {\n                    'op_after_combine11/conv/depthwise_kernel':\n                    'conv6_down.depthwise_conv.weight',\n                    'op_after_combine11/conv/pointwise_kernel':\n                    'conv6_down.pointwise_conv.weight',\n                    'op_after_combine11/conv/bias':\n                    'conv6_down.pointwise_conv.bias',\n                    'op_after_combine11/bn/beta':\n                    'conv6_down.bn.bias',\n                    'op_after_combine11/bn/gamma':\n                    'conv6_down.bn.weight',\n                    'op_after_combine11/bn/moving_mean':\n                    'conv6_down.bn.running_mean',\n                    'op_after_combine11/bn/moving_variance':\n                    'conv6_down.bn.running_var',\n                }\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1' and seg[3] != 'WSM_2':\n                    suffix = base_mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p6_w2[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p6_w2[fpn_idx][1] = v\n                elif seg[3] == 'WSM_2':\n                    p6_w2[fpn_idx][2] = v\n                if torch.min(p6_w2[fpn_idx]) > -1e4:\n                    m[prefix + '.p6_w2'] = p6_w2[fpn_idx]\n            elif fnode_id == 7:\n                base_mapping = {\n                    'op_after_combine12/conv/depthwise_kernel':\n                    'conv7_down.depthwise_conv.weight',\n                    'op_after_combine12/conv/pointwise_kernel':\n                    'conv7_down.pointwise_conv.weight',\n                    'op_after_combine12/conv/bias':\n                    'conv7_down.pointwise_conv.bias',\n                    'op_after_combine12/bn/beta':\n                    'conv7_down.bn.bias',\n                    'op_after_combine12/bn/gamma':\n                    'conv7_down.bn.weight',\n                    'op_after_combine12/bn/moving_mean':\n                    'conv7_down.bn.running_mean',\n                    'op_after_combine12/bn/moving_variance':\n                    'conv7_down.bn.running_var',\n                }\n                if seg[3] != 'WSM' and seg[3] != 'WSM_1' and seg[3] != 'WSM_2':\n                    suffix = base_mapping['/'.join(seg[3:])]\n                    if 'depthwise_conv' in suffix:\n                        v = v.transpose(1, 0)\n                    m[prefix + '.' + suffix] = v\n                elif seg[3] == 'WSM':\n                    p7_w2[fpn_idx][0] = v\n                elif seg[3] == 'WSM_1':\n                    p7_w2[fpn_idx][1] = v\n                if torch.min(p7_w2[fpn_idx]) > -1e4:\n                    m[prefix + '.p7_w2'] = p7_w2[fpn_idx]\n        elif seg[0] == 'box_net':\n            if 'box-predict' in seg[1]:\n                prefix = '.'.join(['bbox_head', 'reg_header'])\n                base_mapping = {\n                    'depthwise_kernel': 'depthwise_conv.weight',\n                    'pointwise_kernel': 'pointwise_conv.weight',\n                    'bias': 'pointwise_conv.bias'\n                }\n                suffix = base_mapping['/'.join(seg[2:])]\n                if 'depthwise_conv' in suffix:\n                    v = v.transpose(1, 0)\n                m[prefix + '.' + suffix] = v\n            elif 'bn' in seg[1]:\n                bbox_conv_idx = int(seg[1][4])\n                bbox_bn_idx = int(seg[1][9]) - 3\n                prefix = '.'.join([\n                    'bbox_head', 'reg_bn_list',\n                    str(bbox_conv_idx),\n                    str(bbox_bn_idx)\n                ])\n                base_mapping = {\n                    'beta': 'bias',\n                    'gamma': 'weight',\n                    'moving_mean': 'running_mean',\n                    'moving_variance': 'running_var'\n                }\n                suffix = base_mapping['/'.join(seg[2:])]\n                m[prefix + '.' + suffix] = v\n            else:\n                bbox_conv_idx = int(seg[1][4])\n                prefix = '.'.join(\n                    ['bbox_head', 'reg_conv_list',\n                     str(bbox_conv_idx)])\n                base_mapping = {\n                    'depthwise_kernel': 'depthwise_conv.weight',\n                    'pointwise_kernel': 'pointwise_conv.weight',\n                    'bias': 'pointwise_conv.bias'\n                }\n                suffix = base_mapping['/'.join(seg[2:])]\n                if 'depthwise_conv' in suffix:\n                    v = v.transpose(1, 0)\n                m[prefix + '.' + suffix] = v\n        elif seg[0] == 'class_net':\n            if 'class-predict' in seg[1]:\n                prefix = '.'.join(['bbox_head', 'cls_header'])\n                base_mapping = {\n                    'depthwise_kernel': 'depthwise_conv.weight',\n                    'pointwise_kernel': 'pointwise_conv.weight',\n                    'bias': 'pointwise_conv.bias'\n                }\n                suffix = base_mapping['/'.join(seg[2:])]\n                if 'depthwise_conv' in suffix:\n                    v = v.transpose(1, 0)\n                m[prefix + '.' + suffix] = v\n            elif 'bn' in seg[1]:\n                cls_conv_idx = int(seg[1][6])\n                cls_bn_idx = int(seg[1][11]) - 3\n                prefix = '.'.join([\n                    'bbox_head', 'cls_bn_list',\n                    str(cls_conv_idx),\n                    str(cls_bn_idx)\n                ])\n                base_mapping = {\n                    'beta': 'bias',\n                    'gamma': 'weight',\n                    'moving_mean': 'running_mean',\n                    'moving_variance': 'running_var'\n                }\n                suffix = base_mapping['/'.join(seg[2:])]\n                m[prefix + '.' + suffix] = v\n            else:\n                cls_conv_idx = int(seg[1][6])\n                prefix = '.'.join(\n                    ['bbox_head', 'cls_conv_list',\n                     str(cls_conv_idx)])\n                base_mapping = {\n                    'depthwise_kernel': 'depthwise_conv.weight',\n                    'pointwise_kernel': 'pointwise_conv.weight',\n                    'bias': 'pointwise_conv.bias'\n                }\n                suffix = base_mapping['/'.join(seg[2:])]\n                if 'depthwise_conv' in suffix:\n                    v = v.transpose(1, 0)\n                m[prefix + '.' + suffix] = v\n    return m\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='convert efficientdet weight from tensorflow to pytorch')\n    parser.add_argument(\n        '--backbone',\n        type=str,\n        help='efficientnet model name, like efficientnet-b0')\n    parser.add_argument(\n        '--tensorflow_weight',\n        type=str,\n        help='efficientdet tensorflow weight name, like efficientdet-d0/model')\n    parser.add_argument(\n        '--out_weight',\n        type=str,\n        help='efficientdet pytorch weight name like demo.pth')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    model_name = args.backbone\n    ori_weight_name = args.tensorflow_weight\n    out_name = args.out_weight\n\n    repeat_map = {\n        0: 3,\n        1: 4,\n        2: 5,\n        3: 6,\n        4: 7,\n        5: 7,\n        6: 8,\n        7: 8,\n    }\n\n    reader = py_checkpoint_reader.NewCheckpointReader(ori_weight_name)\n    weights = {\n        n: torch.as_tensor(tf2pth(reader.get_tensor(n)))\n        for (n, _) in reader.get_variable_to_shape_map().items()\n    }\n    bifpn_repeats = repeat_map[int(model_name[14])]\n    out = convert_key(model_name, bifpn_repeats, weights)\n    result = {'state_dict': out}\n    torch.save(result, out_name)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/__init__.py",
    "content": "from .bifpn import BiFPN\nfrom .efficientdet import EfficientDet\nfrom .efficientdet_head import EfficientDetSepBNHead\nfrom .huber_loss import HuberLoss\nfrom .tensorflow.anchor_generator import YXYXAnchorGenerator\nfrom .tensorflow.coco_90class import Coco90Dataset\nfrom .tensorflow.coco_90metric import Coco90Metric\nfrom .tensorflow.trans_max_iou_assigner import TransMaxIoUAssigner\nfrom .tensorflow.yxyx_bbox_coder import YXYXDeltaXYWHBBoxCoder\nfrom .utils import Conv2dSamePadding\n\n__all__ = [\n    'EfficientDet', 'BiFPN', 'HuberLoss', 'EfficientDetSepBNHead',\n    'Conv2dSamePadding', 'Coco90Dataset', 'Coco90Metric',\n    'YXYXAnchorGenerator', 'TransMaxIoUAssigner', 'YXYXDeltaXYWHBBoxCoder'\n]\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/bifpn.py",
    "content": "from typing import List\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn.bricks import Swish\nfrom mmengine.model import BaseModule\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig, OptConfigType\nfrom .utils import DepthWiseConvBlock, DownChannelBlock, MaxPool2dSamePadding\n\n\nclass BiFPNStage(nn.Module):\n    \"\"\"\n        in_channels: List[int], input dim for P3, P4, P5\n        out_channels: int, output dim for P2 - P7\n        first_time: int, whether is the first bifpnstage\n        conv_bn_act_pattern: bool, whether use conv_bn_act_pattern\n        norm_cfg: (:obj:`ConfigDict` or dict, optional): Config dict for\n            normalization layer.\n        epsilon: float, hyperparameter in fusion features\n    \"\"\"\n\n    def __init__(self,\n                 in_channels: List[int],\n                 out_channels: int,\n                 first_time: bool = False,\n                 apply_bn_for_resampling: bool = True,\n                 conv_bn_act_pattern: bool = False,\n                 norm_cfg: OptConfigType = dict(\n                     type='BN', momentum=1e-2, eps=1e-3),\n                 epsilon: float = 1e-4) -> None:\n        super().__init__()\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.first_time = first_time\n        self.apply_bn_for_resampling = apply_bn_for_resampling\n        self.conv_bn_act_pattern = conv_bn_act_pattern\n        self.norm_cfg = norm_cfg\n        self.epsilon = epsilon\n\n        if self.first_time:\n            self.p5_down_channel = DownChannelBlock(\n                self.in_channels[-1],\n                self.out_channels,\n                apply_norm=self.apply_bn_for_resampling,\n                conv_bn_act_pattern=self.conv_bn_act_pattern,\n                norm_cfg=norm_cfg)\n            self.p4_down_channel = DownChannelBlock(\n                self.in_channels[-2],\n                self.out_channels,\n                apply_norm=self.apply_bn_for_resampling,\n                conv_bn_act_pattern=self.conv_bn_act_pattern,\n                norm_cfg=norm_cfg)\n            self.p3_down_channel = DownChannelBlock(\n                self.in_channels[-3],\n                self.out_channels,\n                apply_norm=self.apply_bn_for_resampling,\n                conv_bn_act_pattern=self.conv_bn_act_pattern,\n                norm_cfg=norm_cfg)\n            self.p5_to_p6 = nn.Sequential(\n                DownChannelBlock(\n                    self.in_channels[-1],\n                    self.out_channels,\n                    apply_norm=self.apply_bn_for_resampling,\n                    conv_bn_act_pattern=self.conv_bn_act_pattern,\n                    norm_cfg=norm_cfg), MaxPool2dSamePadding(3, 2))\n            self.p6_to_p7 = MaxPool2dSamePadding(3, 2)\n            self.p4_level_connection = DownChannelBlock(\n                self.in_channels[-2],\n                self.out_channels,\n                apply_norm=self.apply_bn_for_resampling,\n                conv_bn_act_pattern=self.conv_bn_act_pattern,\n                norm_cfg=norm_cfg)\n            self.p5_level_connection = DownChannelBlock(\n                self.in_channels[-1],\n                self.out_channels,\n                apply_norm=self.apply_bn_for_resampling,\n                conv_bn_act_pattern=self.conv_bn_act_pattern,\n                norm_cfg=norm_cfg)\n\n        self.p6_upsample = nn.Upsample(scale_factor=2, mode='nearest')\n        self.p5_upsample = nn.Upsample(scale_factor=2, mode='nearest')\n        self.p4_upsample = nn.Upsample(scale_factor=2, mode='nearest')\n        self.p3_upsample = nn.Upsample(scale_factor=2, mode='nearest')\n\n        # bottom to up: feature map down_sample module\n        self.p4_down_sample = MaxPool2dSamePadding(3, 2)\n        self.p5_down_sample = MaxPool2dSamePadding(3, 2)\n        self.p6_down_sample = MaxPool2dSamePadding(3, 2)\n        self.p7_down_sample = MaxPool2dSamePadding(3, 2)\n\n        # Fuse Conv Layers\n        self.conv6_up = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        self.conv5_up = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        self.conv4_up = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        self.conv3_up = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        self.conv4_down = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        self.conv5_down = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        self.conv6_down = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        self.conv7_down = DepthWiseConvBlock(\n            out_channels,\n            out_channels,\n            apply_norm=self.apply_bn_for_resampling,\n            conv_bn_act_pattern=self.conv_bn_act_pattern,\n            norm_cfg=norm_cfg)\n        # weights\n        self.p6_w1 = nn.Parameter(\n            torch.ones(2, dtype=torch.float32), requires_grad=True)\n        self.p6_w1_relu = nn.ReLU()\n        self.p5_w1 = nn.Parameter(\n            torch.ones(2, dtype=torch.float32), requires_grad=True)\n        self.p5_w1_relu = nn.ReLU()\n        self.p4_w1 = nn.Parameter(\n            torch.ones(2, dtype=torch.float32), requires_grad=True)\n        self.p4_w1_relu = nn.ReLU()\n        self.p3_w1 = nn.Parameter(\n            torch.ones(2, dtype=torch.float32), requires_grad=True)\n        self.p3_w1_relu = nn.ReLU()\n\n        self.p4_w2 = nn.Parameter(\n            torch.ones(3, dtype=torch.float32), requires_grad=True)\n        self.p4_w2_relu = nn.ReLU()\n        self.p5_w2 = nn.Parameter(\n            torch.ones(3, dtype=torch.float32), requires_grad=True)\n        self.p5_w2_relu = nn.ReLU()\n        self.p6_w2 = nn.Parameter(\n            torch.ones(3, dtype=torch.float32), requires_grad=True)\n        self.p6_w2_relu = nn.ReLU()\n        self.p7_w2 = nn.Parameter(\n            torch.ones(2, dtype=torch.float32), requires_grad=True)\n        self.p7_w2_relu = nn.ReLU()\n\n        self.swish = Swish()\n\n    def combine(self, x):\n        if not self.conv_bn_act_pattern:\n            x = self.swish(x)\n\n        return x\n\n    def forward(self, x):\n        if self.first_time:\n            p3, p4, p5 = x\n            # build feature map P6\n            p6_in = self.p5_to_p6(p5)\n            # build feature map P7\n            p7_in = self.p6_to_p7(p6_in)\n\n            p3_in = self.p3_down_channel(p3)\n            p4_in = self.p4_down_channel(p4)\n            p5_in = self.p5_down_channel(p5)\n\n        else:\n            p3_in, p4_in, p5_in, p6_in, p7_in = x\n\n        # Weights for P6_0 and P7_0 to P6_1\n        p6_w1 = self.p6_w1_relu(self.p6_w1)\n        weight = p6_w1 / (torch.sum(p6_w1, dim=0) + self.epsilon)\n        # Connections for P6_0 and P7_0 to P6_1 respectively\n        p6_up = self.conv6_up(\n            self.combine(weight[0] * p6_in +\n                         weight[1] * self.p6_upsample(p7_in)))\n\n        # Weights for P5_0 and P6_1 to P5_1\n        p5_w1 = self.p5_w1_relu(self.p5_w1)\n        weight = p5_w1 / (torch.sum(p5_w1, dim=0) + self.epsilon)\n        # Connections for P5_0 and P6_1 to P5_1 respectively\n        p5_up = self.conv5_up(\n            self.combine(weight[0] * p5_in +\n                         weight[1] * self.p5_upsample(p6_up)))\n\n        # Weights for P4_0 and P5_1 to P4_1\n        p4_w1 = self.p4_w1_relu(self.p4_w1)\n        weight = p4_w1 / (torch.sum(p4_w1, dim=0) + self.epsilon)\n        # Connections for P4_0 and P5_1 to P4_1 respectively\n        p4_up = self.conv4_up(\n            self.combine(weight[0] * p4_in +\n                         weight[1] * self.p4_upsample(p5_up)))\n\n        # Weights for P3_0 and P4_1 to P3_2\n        p3_w1 = self.p3_w1_relu(self.p3_w1)\n        weight = p3_w1 / (torch.sum(p3_w1, dim=0) + self.epsilon)\n        # Connections for P3_0 and P4_1 to P3_2 respectively\n        p3_out = self.conv3_up(\n            self.combine(weight[0] * p3_in +\n                         weight[1] * self.p3_upsample(p4_up)))\n\n        if self.first_time:\n            p4_in = self.p4_level_connection(p4)\n            p5_in = self.p5_level_connection(p5)\n\n        # Weights for P4_0, P4_1 and P3_2 to P4_2\n        p4_w2 = self.p4_w2_relu(self.p4_w2)\n        weight = p4_w2 / (torch.sum(p4_w2, dim=0) + self.epsilon)\n        # Connections for P4_0, P4_1 and P3_2 to P4_2 respectively\n        p4_out = self.conv4_down(\n            self.combine(weight[0] * p4_in + weight[1] * p4_up +\n                         weight[2] * self.p4_down_sample(p3_out)))\n\n        # Weights for P5_0, P5_1 and P4_2 to P5_2\n        p5_w2 = self.p5_w2_relu(self.p5_w2)\n        weight = p5_w2 / (torch.sum(p5_w2, dim=0) + self.epsilon)\n        # Connections for P5_0, P5_1 and P4_2 to P5_2 respectively\n        p5_out = self.conv5_down(\n            self.combine(weight[0] * p5_in + weight[1] * p5_up +\n                         weight[2] * self.p5_down_sample(p4_out)))\n\n        # Weights for P6_0, P6_1 and P5_2 to P6_2\n        p6_w2 = self.p6_w2_relu(self.p6_w2)\n        weight = p6_w2 / (torch.sum(p6_w2, dim=0) + self.epsilon)\n        # Connections for P6_0, P6_1 and P5_2 to P6_2 respectively\n        p6_out = self.conv6_down(\n            self.combine(weight[0] * p6_in + weight[1] * p6_up +\n                         weight[2] * self.p6_down_sample(p5_out)))\n\n        # Weights for P7_0 and P6_2 to P7_2\n        p7_w2 = self.p7_w2_relu(self.p7_w2)\n        weight = p7_w2 / (torch.sum(p7_w2, dim=0) + self.epsilon)\n        # Connections for P7_0 and P6_2 to P7_2\n        p7_out = self.conv7_down(\n            self.combine(weight[0] * p7_in +\n                         weight[1] * self.p7_down_sample(p6_out)))\n        return p3_out, p4_out, p5_out, p6_out, p7_out\n\n\n@MODELS.register_module()\nclass BiFPN(BaseModule):\n    \"\"\"\n        num_stages: int, bifpn number of repeats\n        in_channels: List[int], input dim for P3, P4, P5\n        out_channels: int, output dim for P2 - P7\n        start_level: int, Index of input features in backbone\n        epsilon: float, hyperparameter in fusion features\n        apply_bn_for_resampling: bool, whether use bn after resampling\n        conv_bn_act_pattern: bool, whether use conv_bn_act_pattern\n        norm_cfg: (:obj:`ConfigDict` or dict, optional): Config dict for\n            normalization layer.\n        init_cfg: MultiConfig: init method\n    \"\"\"\n\n    def __init__(self,\n                 num_stages: int,\n                 in_channels: List[int],\n                 out_channels: int,\n                 start_level: int = 0,\n                 epsilon: float = 1e-4,\n                 apply_bn_for_resampling: bool = True,\n                 conv_bn_act_pattern: bool = False,\n                 norm_cfg: OptConfigType = dict(\n                     type='BN', momentum=1e-2, eps=1e-3),\n                 init_cfg: MultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        self.start_level = start_level\n        self.bifpn = nn.Sequential(*[\n            BiFPNStage(\n                in_channels=in_channels,\n                out_channels=out_channels,\n                first_time=True if _ == 0 else False,\n                apply_bn_for_resampling=apply_bn_for_resampling,\n                conv_bn_act_pattern=conv_bn_act_pattern,\n                norm_cfg=norm_cfg,\n                epsilon=epsilon) for _ in range(num_stages)\n        ])\n\n    def forward(self, x):\n        x = x[self.start_level:]\n        x = self.bifpn(x)\n\n        return x\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/efficientdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.models.detectors.single_stage import SingleStageDetector\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass EfficientDet(SingleStageDetector):\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: ConfigType,\n                 bbox_head: ConfigType,\n                 train_cfg: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None) -> None:\n        super().__init__(\n            backbone=backbone,\n            neck=neck,\n            bbox_head=bbox_head,\n            train_cfg=train_cfg,\n            test_cfg=test_cfg,\n            data_preprocessor=data_preprocessor,\n            init_cfg=init_cfg)\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/efficientdet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List, Tuple\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn.bricks import Swish, build_norm_layer\nfrom mmengine.model import bias_init_with_prob\nfrom torch import Tensor\n\nfrom mmdet.models.dense_heads.anchor_head import AnchorHead\nfrom mmdet.models.utils import images_to_levels, multi_apply\nfrom mmdet.registry import MODELS\nfrom mmdet.structures.bbox import cat_boxes, get_box_tensor\nfrom mmdet.utils import (InstanceList, OptConfigType, OptInstanceList,\n                         OptMultiConfig, reduce_mean)\nfrom .utils import DepthWiseConvBlock\n\n\n@MODELS.register_module()\nclass EfficientDetSepBNHead(AnchorHead):\n    \"\"\"EfficientDetHead with separate BN.\n\n    num_classes (int): Number of categories num_ins (int):  Number of the input\n    feature map. in_channels (int): Number of channels in the input feature\n    map. feat_channels (int): Number of hidden channels. stacked_convs (int):\n    Number of repetitions of conv norm_cfg (dict): Config dict for\n    normalization layer. anchor_generator (dict): Config dict for anchor\n    generator bbox_coder (dict): Config of bounding box coder. loss_cls (dict):\n    Config of classification loss. loss_bbox (dict): Config of localization\n    loss. train_cfg (dict): Training config of anchor head. test_cfg (dict):\n    Testing config of anchor head. init_cfg (dict or list[dict], optional):\n    Initialization config dict.\n    \"\"\"\n\n    def __init__(self,\n                 num_classes: int,\n                 num_ins: int,\n                 in_channels: int,\n                 feat_channels: int,\n                 stacked_convs: int = 3,\n                 norm_cfg: OptConfigType = dict(\n                     type='BN', momentum=1e-2, eps=1e-3),\n                 init_cfg: OptMultiConfig = None,\n                 **kwargs) -> None:\n        self.num_ins = num_ins\n        self.stacked_convs = stacked_convs\n        self.norm_cfg = norm_cfg\n        super().__init__(\n            num_classes=num_classes,\n            in_channels=in_channels,\n            feat_channels=feat_channels,\n            init_cfg=init_cfg,\n            **kwargs)\n\n    def _init_layers(self) -> None:\n        \"\"\"Initialize layers of the head.\"\"\"\n        self.reg_conv_list = nn.ModuleList()\n        self.cls_conv_list = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            channels = self.in_channels if i == 0 else self.feat_channels\n            self.reg_conv_list.append(\n                DepthWiseConvBlock(\n                    channels, self.feat_channels, apply_norm=False))\n            self.cls_conv_list.append(\n                DepthWiseConvBlock(\n                    channels, self.feat_channels, apply_norm=False))\n\n        self.reg_bn_list = nn.ModuleList([\n            nn.ModuleList([\n                build_norm_layer(\n                    self.norm_cfg, num_features=self.feat_channels)[1]\n                for j in range(self.num_ins)\n            ]) for i in range(self.stacked_convs)\n        ])\n\n        self.cls_bn_list = nn.ModuleList([\n            nn.ModuleList([\n                build_norm_layer(\n                    self.norm_cfg, num_features=self.feat_channels)[1]\n                for j in range(self.num_ins)\n            ]) for i in range(self.stacked_convs)\n        ])\n\n        self.cls_header = DepthWiseConvBlock(\n            self.in_channels,\n            self.num_base_priors * self.cls_out_channels,\n            apply_norm=False)\n        self.reg_header = DepthWiseConvBlock(\n            self.in_channels, self.num_base_priors * 4, apply_norm=False)\n        self.swish = Swish()\n\n    def init_weights(self) -> None:\n        \"\"\"Initialize weights of the head.\"\"\"\n        for m in self.reg_conv_list:\n            nn.init.constant_(m.pointwise_conv.bias, 0.0)\n        for m in self.cls_conv_list:\n            nn.init.constant_(m.pointwise_conv.bias, 0.0)\n        bias_cls = bias_init_with_prob(0.01)\n        nn.init.constant_(self.cls_header.pointwise_conv.bias, bias_cls)\n        nn.init.constant_(self.reg_header.pointwise_conv.bias, 0.0)\n\n    def forward_single_bbox(self, feat: Tensor, level_id: int,\n                            i: int) -> Tensor:\n        conv_op = self.reg_conv_list[i]\n        bn = self.reg_bn_list[i][level_id]\n\n        feat = conv_op(feat)\n        feat = bn(feat)\n        feat = self.swish(feat)\n\n        return feat\n\n    def forward_single_cls(self, feat: Tensor, level_id: int,\n                           i: int) -> Tensor:\n        conv_op = self.cls_conv_list[i]\n        bn = self.cls_bn_list[i][level_id]\n\n        feat = conv_op(feat)\n        feat = bn(feat)\n        feat = self.swish(feat)\n\n        return feat\n\n    def forward(self, feats: Tuple[Tensor]) -> tuple:\n        cls_scores = []\n        bbox_preds = []\n        for level_id in range(self.num_ins):\n            feat = feats[level_id]\n            for i in range(self.stacked_convs):\n                feat = self.forward_single_bbox(feat, level_id, i)\n            bbox_pred = self.reg_header(feat)\n            bbox_preds.append(bbox_pred)\n        for level_id in range(self.num_ins):\n            feat = feats[level_id]\n            for i in range(self.stacked_convs):\n                feat = self.forward_single_cls(feat, level_id, i)\n            cls_score = self.cls_header(feat)\n            cls_scores.append(cls_score)\n\n        return cls_scores, bbox_preds\n\n    def loss_by_feat(\n            self,\n            cls_scores: List[Tensor],\n            bbox_preds: List[Tensor],\n            batch_gt_instances: InstanceList,\n            batch_img_metas: List[dict],\n            batch_gt_instances_ignore: OptInstanceList = None) -> dict:\n        \"\"\"Calculate the loss based on the features extracted by the detection\n        head.\n\n        Args:\n            cls_scores (list[Tensor]): Box scores for each scale level\n                has shape (N, num_anchors * num_classes, H, W).\n            bbox_preds (list[Tensor]): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]\n        assert len(featmap_sizes) == self.prior_generator.num_levels\n\n        device = cls_scores[0].device\n\n        anchor_list, valid_flag_list = self.get_anchors(\n            featmap_sizes, batch_img_metas, device=device)\n        cls_reg_targets = self.get_targets(\n            anchor_list,\n            valid_flag_list,\n            batch_gt_instances,\n            batch_img_metas,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n        (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,\n         avg_factor) = cls_reg_targets\n\n        # anchor number of multi levels\n        num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]]\n        # concat all level anchors and flags to a single tensor\n        concat_anchor_list = []\n        for i in range(len(anchor_list)):\n            concat_anchor_list.append(cat_boxes(anchor_list[i]))\n        all_anchor_list = images_to_levels(concat_anchor_list,\n                                           num_level_anchors)\n\n        avg_factor = reduce_mean(\n            torch.tensor(avg_factor, dtype=torch.float, device=device)).item()\n        avg_factor = max(avg_factor, 1.0)\n        losses_cls, losses_bbox = multi_apply(\n            self.loss_by_feat_single,\n            cls_scores,\n            bbox_preds,\n            all_anchor_list,\n            labels_list,\n            label_weights_list,\n            bbox_targets_list,\n            bbox_weights_list,\n            avg_factor=avg_factor)\n        return dict(loss_cls=losses_cls, loss_bbox=losses_bbox)\n\n    def loss_by_feat_single(self, cls_score: Tensor, bbox_pred: Tensor,\n                            anchors: Tensor, labels: Tensor,\n                            label_weights: Tensor, bbox_targets: Tensor,\n                            bbox_weights: Tensor, avg_factor: int) -> tuple:\n        \"\"\"Calculate the loss of a single scale level based on the features\n        extracted by the detection head.\n\n        Args:\n            cls_score (Tensor): Box scores for each scale level\n                Has shape (N, num_anchors * num_classes, H, W).\n            bbox_pred (Tensor): Box energies / deltas for each scale\n                level with shape (N, num_anchors * 4, H, W).\n            anchors (Tensor): Box reference for each scale level with shape\n                (N, num_total_anchors, 4).\n            labels (Tensor): Labels of each anchors with shape\n                (N, num_total_anchors).\n            label_weights (Tensor): Label weights of each anchor with shape\n                (N, num_total_anchors)\n            bbox_targets (Tensor): BBox regression targets of each anchor\n                weight shape (N, num_total_anchors, 4).\n            bbox_weights (Tensor): BBox regression loss weights of each anchor\n                with shape (N, num_total_anchors, 4).\n            avg_factor (int): Average factor that is used to average the loss.\n\n        Returns:\n            tuple: loss components.\n        \"\"\"\n\n        # classification loss\n        labels = labels.reshape(-1)\n        label_weights = label_weights.reshape(-1)\n        cls_score = cls_score.permute(0, 2, 3,\n                                      1).reshape(-1, self.cls_out_channels)\n        loss_cls = self.loss_cls(\n            cls_score, labels, label_weights, avg_factor=avg_factor)\n        # regression loss\n        target_dim = bbox_targets.size(-1)\n        bbox_targets = bbox_targets.reshape(-1, target_dim)\n        bbox_weights = bbox_weights.reshape(-1, target_dim)\n        bbox_pred = bbox_pred.permute(0, 2, 3,\n                                      1).reshape(-1,\n                                                 self.bbox_coder.encode_size)\n        if self.reg_decoded_bbox:\n            # When the regression loss (e.g. `IouLoss`, `GIouLoss`)\n            # is applied directly on the decoded bounding boxes, it\n            # decodes the already encoded coordinates to absolute format.\n            anchors = anchors.reshape(-1, anchors.size(-1))\n            bbox_pred = self.bbox_coder.decode(anchors, bbox_pred)\n            bbox_pred = get_box_tensor(bbox_pred)\n        loss_bbox = self.loss_bbox(\n            bbox_pred, bbox_targets, bbox_weights, avg_factor=avg_factor * 4)\n        return loss_cls, loss_bbox\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/huber_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\nfrom torch import Tensor\n\nfrom mmdet.models.losses.utils import weighted_loss\nfrom mmdet.registry import MODELS\n\n\n@weighted_loss\ndef huber_loss(pred: Tensor, target: Tensor, beta: float = 1.0) -> Tensor:\n    \"\"\"Huber loss.\n\n    Args:\n        pred (Tensor): The prediction.\n        target (Tensor): The learning target of the prediction.\n        beta (float, optional): The threshold in the piecewise function.\n            Defaults to 1.0.\n\n    Returns:\n        Tensor: Calculated loss\n    \"\"\"\n    assert beta > 0\n    if target.numel() == 0:\n        return pred.sum() * 0\n\n    assert pred.size() == target.size()\n    diff = torch.abs(pred - target)\n    loss = torch.where(diff < beta, 0.5 * diff * diff,\n                       beta * diff - 0.5 * beta * beta)\n    return loss\n\n\n@MODELS.register_module()\nclass HuberLoss(nn.Module):\n    \"\"\"Huber loss.\n\n    Args:\n        beta (float, optional): The threshold in the piecewise function.\n            Defaults to 1.0.\n        reduction (str, optional): The method to reduce the loss.\n            Options are \"none\", \"mean\" and \"sum\". Defaults to \"mean\".\n        loss_weight (float, optional): The weight of loss.\n    \"\"\"\n\n    def __init__(self,\n                 beta: float = 1.0,\n                 reduction: str = 'mean',\n                 loss_weight: float = 1.0) -> None:\n        super().__init__()\n        self.beta = beta\n        self.reduction = reduction\n        self.loss_weight = loss_weight\n\n    def forward(self,\n                pred: Tensor,\n                target: Tensor,\n                weight: Optional[Tensor] = None,\n                avg_factor: Optional[int] = None,\n                reduction_override: Optional[str] = None,\n                **kwargs) -> Tensor:\n        \"\"\"Forward function.\n\n        Args:\n            pred (Tensor): The prediction.\n            target (Tensor): The learning target of the prediction.\n            weight (Tensor, optional): The weight of loss for each\n                prediction. Defaults to None.\n            avg_factor (int, optional): Average factor that is used to average\n                the loss. Defaults to None.\n            reduction_override (str, optional): The reduction method used to\n                override the original reduction method of the loss.\n                Defaults to None.\n\n        Returns:\n            Tensor: Calculated loss\n        \"\"\"\n        assert reduction_override in (None, 'none', 'mean', 'sum')\n        reduction = (\n            reduction_override if reduction_override else self.reduction)\n        loss_bbox = self.loss_weight * huber_loss(\n            pred,\n            target,\n            weight,\n            beta=self.beta,\n            reduction=reduction,\n            avg_factor=avg_factor,\n            **kwargs)\n        return loss_bbox\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/tensorflow/anchor_generator.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional, Tuple, Union\n\nimport torch\nfrom torch import Tensor\n\nfrom mmdet.models.task_modules.prior_generators.anchor_generator import \\\n    AnchorGenerator\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import HorizontalBoxes\n\nDeviceType = Union[str, torch.device]\n\n\n@TASK_UTILS.register_module()\nclass YXYXAnchorGenerator(AnchorGenerator):\n\n    def gen_single_level_base_anchors(self,\n                                      base_size: Union[int, float],\n                                      scales: Tensor,\n                                      ratios: Tensor,\n                                      center: Optional[Tuple[float]] = None) \\\n            -> Tensor:\n        \"\"\"Generate base anchors of a single level.\n\n        Args:\n            base_size (int | float): Basic size of an anchor.\n            scales (torch.Tensor): Scales of the anchor.\n            ratios (torch.Tensor): The ratio between the height\n                and width of anchors in a single level.\n            center (tuple[float], optional): The center of the base anchor\n                related to a single feature grid. Defaults to None.\n\n        Returns:\n            torch.Tensor: Anchors in a single-level feature maps.\n        \"\"\"\n\n        w = base_size\n        h = base_size\n        if center is None:\n            x_center = self.center_offset * w\n            y_center = self.center_offset * h\n        else:\n            x_center, y_center = center\n\n        h_ratios = torch.sqrt(ratios)\n        w_ratios = 1 / h_ratios\n        if self.scale_major:\n            ws = (w * scales[:, None] * w_ratios[None, :]).view(-1)\n            hs = (h * scales[:, None] * h_ratios[None, :]).view(-1)\n        else:\n            ws = (w * scales[:, None] * w_ratios[None, :]).view(-1)\n            hs = (h * scales[:, None] * h_ratios[None, :]).view(-1)\n\n        # use float anchor and the anchor's center is aligned with the\n        # pixel center\n        base_anchors = [\n            y_center - 0.5 * hs,\n            x_center - 0.5 * ws,\n            y_center + 0.5 * hs,\n            x_center + 0.5 * ws,\n        ]\n        base_anchors = torch.stack(base_anchors, dim=-1)\n\n        return base_anchors\n\n    def single_level_grid_priors(self,\n                                 featmap_size: Tuple[int, int],\n                                 level_idx: int,\n                                 dtype: torch.dtype = torch.float32,\n                                 device: DeviceType = 'cuda') -> Tensor:\n        \"\"\"Generate grid anchors of a single level.\n\n        Note:\n            This function is usually called by method ``self.grid_priors``.\n\n        Args:\n            featmap_size (tuple[int, int]): Size of the feature maps.\n            level_idx (int): The index of corresponding feature map level.\n            dtype (obj:`torch.dtype`): Date type of points.Defaults to\n                ``torch.float32``.\n            device (str | torch.device): The device the tensor will be put on.\n                Defaults to 'cuda'.\n\n        Returns:\n            torch.Tensor: Anchors in the overall feature maps.\n        \"\"\"\n        base_anchors = self.base_anchors[level_idx].to(device).to(dtype)\n        feat_h, feat_w = featmap_size\n        stride_w, stride_h = self.strides[level_idx]\n        # First create Range with the default dtype, than convert to\n        # target `dtype` for onnx exporting.\n        shift_x = torch.arange(0, feat_w, device=device).to(dtype) * stride_w\n        shift_y = torch.arange(0, feat_h, device=device).to(dtype) * stride_h\n\n        shift_xx, shift_yy = self._meshgrid(shift_x, shift_y)\n        shifts = torch.stack([shift_yy, shift_xx, shift_yy, shift_xx], dim=-1)\n        # first feat_w elements correspond to the first row of shifts\n        # add A anchors (1, A, 4) to K shifts (K, 1, 4) to get\n        # shifted anchors (K, A, 4), reshape to (K*A, 4)\n\n        all_anchors = base_anchors[None, :, :] + shifts[:, None, :]\n        all_anchors = all_anchors.view(-1, 4)\n        # first A rows correspond to A anchors of (0, 0) in feature map,\n        # then (0, 1), (0, 2), ...\n        if self.use_box_type:\n            all_anchors = HorizontalBoxes(all_anchors)\n\n        return all_anchors\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/tensorflow/api_wrappers/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .coco_api import COCO, COCOeval, COCOPanoptic\n\n__all__ = ['COCO', 'COCOeval', 'COCOPanoptic']\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/tensorflow/api_wrappers/coco_api.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# This file add snake case alias for coco api\n\nimport warnings\nfrom collections import defaultdict\nfrom typing import List, Optional, Union\n\nimport pycocotools\nfrom pycocotools.coco import COCO as _COCO\nfrom pycocotools.cocoeval import COCOeval as _COCOeval\n\n\nclass COCO(_COCO):\n    \"\"\"This class is almost the same as official pycocotools package.\n\n    It implements some snake case function aliases. So that the COCO class has\n    the same interface as LVIS class.\n    \"\"\"\n\n    def __init__(self, annotation_file=None):\n        if getattr(pycocotools, '__version__', '0') >= '12.0.2':\n            warnings.warn(\n                'mmpycocotools is deprecated. Please install official pycocotools by \"pip install pycocotools\"',  # noqa: E501\n                UserWarning)\n        super().__init__(annotation_file=annotation_file)\n        self.img_ann_map = self.imgToAnns\n        self.cat_img_map = self.catToImgs\n\n    def get_ann_ids(self, img_ids=[], cat_ids=[], area_rng=[], iscrowd=None):\n        return self.getAnnIds(img_ids, cat_ids, area_rng, iscrowd)\n\n    def get_cat_ids(self, cat_names=[], sup_names=[], cat_ids=[]):\n        cat_ids_coco = self.getCatIds(cat_names, sup_names, cat_ids)\n        if None in cat_names:\n            index = [i for i, v in enumerate(cat_names) if v is not None]\n            cat_ids = list(range(len(cat_names)))\n            for i in range(len(index)):\n                cat_ids[index[i]] = cat_ids_coco[i]\n            return cat_ids\n        else:\n            return cat_ids_coco\n\n    def get_img_ids(self, img_ids=[], cat_ids=[]):\n        return self.getImgIds(img_ids, cat_ids)\n\n    def load_anns(self, ids):\n        return self.loadAnns(ids)\n\n    def load_cats(self, ids):\n        return self.loadCats(ids)\n\n    def load_imgs(self, ids):\n        return self.loadImgs(ids)\n\n\n# just for the ease of import\nCOCOeval = _COCOeval\n\n\nclass COCOPanoptic(COCO):\n    \"\"\"This wrapper is for loading the panoptic style annotation file.\n\n    The format is shown in the CocoPanopticDataset class.\n\n    Args:\n        annotation_file (str, optional): Path of annotation file.\n            Defaults to None.\n    \"\"\"\n\n    def __init__(self, annotation_file: Optional[str] = None) -> None:\n        super(COCOPanoptic, self).__init__(annotation_file)\n\n    def createIndex(self) -> None:\n        \"\"\"Create index.\"\"\"\n        # create index\n        print('creating index...')\n        # anns stores 'segment_id -> annotation'\n        anns, cats, imgs = {}, {}, {}\n        img_to_anns, cat_to_imgs = defaultdict(list), defaultdict(list)\n        if 'annotations' in self.dataset:\n            for ann in self.dataset['annotations']:\n                for seg_ann in ann['segments_info']:\n                    # to match with instance.json\n                    seg_ann['image_id'] = ann['image_id']\n                    img_to_anns[ann['image_id']].append(seg_ann)\n                    # segment_id is not unique in coco dataset orz...\n                    # annotations from different images but\n                    # may have same segment_id\n                    if seg_ann['id'] in anns.keys():\n                        anns[seg_ann['id']].append(seg_ann)\n                    else:\n                        anns[seg_ann['id']] = [seg_ann]\n\n            # filter out annotations from other images\n            img_to_anns_ = defaultdict(list)\n            for k, v in img_to_anns.items():\n                img_to_anns_[k] = [x for x in v if x['image_id'] == k]\n            img_to_anns = img_to_anns_\n\n        if 'images' in self.dataset:\n            for img_info in self.dataset['images']:\n                img_info['segm_file'] = img_info['file_name'].replace(\n                    'jpg', 'png')\n                imgs[img_info['id']] = img_info\n\n        if 'categories' in self.dataset:\n            for cat in self.dataset['categories']:\n                cats[cat['id']] = cat\n\n        if 'annotations' in self.dataset and 'categories' in self.dataset:\n            for ann in self.dataset['annotations']:\n                for seg_ann in ann['segments_info']:\n                    cat_to_imgs[seg_ann['category_id']].append(ann['image_id'])\n\n        print('index created!')\n\n        self.anns = anns\n        self.imgToAnns = img_to_anns\n        self.catToImgs = cat_to_imgs\n        self.imgs = imgs\n        self.cats = cats\n\n    def load_anns(self,\n                  ids: Union[List[int], int] = []) -> Optional[List[dict]]:\n        \"\"\"Load anns with the specified ids.\n\n        ``self.anns`` is a list of annotation lists instead of a\n        list of annotations.\n\n        Args:\n            ids (Union[List[int], int]): Integer ids specifying anns.\n\n        Returns:\n            anns (List[dict], optional): Loaded ann objects.\n        \"\"\"\n        anns = []\n\n        if hasattr(ids, '__iter__') and hasattr(ids, '__len__'):\n            # self.anns is a list of annotation lists instead of\n            # a list of annotations\n            for id in ids:\n                anns += self.anns[id]\n            return anns\n        elif type(ids) == int:\n            return self.anns[ids]\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/tensorflow/coco_90class.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nfrom typing import List, Union\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.datasets.base_det_dataset import BaseDetDataset\nfrom mmdet.registry import DATASETS\nfrom .api_wrappers import COCO\n\n\n@DATASETS.register_module()\nclass Coco90Dataset(BaseDetDataset):\n    \"\"\"Dataset for COCO.\"\"\"\n\n    METAINFO = {\n        'classes':\n        ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n         'truck', 'boat', 'traffic light', 'fire hydrant', None, 'stop sign',\n         'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',\n         'cow', 'elephant', 'bear', 'zebra', 'giraffe', None, 'backpack',\n         'umbrella', None, None, 'handbag', 'tie', 'suitcase', 'frisbee',\n         'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat',\n         'baseball glove', 'skateboard', 'surfboard', 'tennis racket',\n         'bottle', None, 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl',\n         'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot',\n         'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant',\n         'bed', None, 'dining table', None, None, 'toilet', None, 'tv',\n         'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',\n         'oven', 'toaster', 'sink', 'refrigerator', None, 'book', 'clock',\n         'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'),\n        # palette is a list of color tuples, which is used for visualization.\n        'palette':\n        [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),\n         (0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),\n         (100, 170, 30), None, (220, 220, 0), (175, 116, 175), (250, 0, 30),\n         (165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),\n         (0, 82, 0), (120, 166, 157), (110, 76, 0), (174, 57, 255),\n         (199, 100, 0), (72, 0, 118), None,\n         (255, 179, 240), (0, 125, 92), None, None, (209, 0, 151),\n         (188, 208, 182), (0, 220, 176), (255, 99, 164), (92, 0, 73),\n         (133, 129, 255), (78, 180, 255), (0, 228, 0), (174, 255, 243),\n         (45, 89, 255), (134, 134, 103), (145, 148, 174), (255, 208, 186),\n         (197, 226, 255), None, (171, 134, 1), (109, 63, 54), (207, 138, 255),\n         (151, 0, 95), (9, 80, 61), (84, 105, 51), (74, 65, 105),\n         (166, 196, 102), (208, 195, 210), (255, 109, 65), (0, 143, 149),\n         (179, 0, 194), (209, 99, 106), (5, 121, 0), (227, 255, 205),\n         (147, 186, 208), (153, 69, 1), (3, 95, 161), (163, 255, 0),\n         (119, 0, 170), None, (0, 182, 199), None, None, (0, 165, 120), None,\n         (183, 130, 88), (95, 32, 0), (130, 114, 135), (110, 129, 133),\n         (166, 74, 118), (219, 142, 185), (79, 210, 114), (178, 90, 62),\n         (65, 70, 15), (127, 167, 115), (59, 105, 106), None, (142, 108, 45),\n         (196, 172, 0), (95, 54, 80), (128, 76, 255), (201, 57, 1),\n         (246, 0, 122), (191, 162, 208)]\n    }\n    COCOAPI = COCO\n    # ann_id is unique in coco dataset.\n    ANN_ID_UNIQUE = True\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.coco = self.COCOAPI(local_path)\n        # The order of returned `cat_ids` will not\n        # change with the order of the `classes`\n        self.cat_ids = self.coco.get_cat_ids(\n            cat_names=self.metainfo['classes'])\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.coco.cat_img_map)\n\n        img_ids = self.coco.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.coco.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n\n            ann_ids = self.coco.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = self.coco.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n\n            parsed_data_info = self.parse_data_info({\n                'raw_ann_info':\n                raw_ann_info,\n                'raw_img_info':\n                raw_img_info\n            })\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.coco\n\n        return data_list\n\n    def parse_data_info(self, raw_data_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            raw_data_info (dict): Raw data information load from ``ann_file``\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        img_info = raw_data_info['raw_img_info']\n        ann_info = raw_data_info['raw_ann_info']\n\n        data_info = {}\n\n        # TODO: need to change data_prefix['img'] to data_prefix['img_path']\n        img_path = osp.join(self.data_prefix['img'], img_info['file_name'])\n        if self.data_prefix.get('seg', None):\n            seg_map_path = osp.join(\n                self.data_prefix['seg'],\n                img_info['file_name'].rsplit('.', 1)[0] + self.seg_map_suffix)\n        else:\n            seg_map_path = None\n        data_info['img_path'] = img_path\n        data_info['img_id'] = img_info['img_id']\n        data_info['seg_map_path'] = seg_map_path\n        data_info['height'] = img_info['height']\n        data_info['width'] = img_info['width']\n\n        instances = []\n        for i, ann in enumerate(ann_info):\n            instance = {}\n\n            if ann.get('ignore', False):\n                continue\n            x1, y1, w, h = ann['bbox']\n            inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                continue\n            if ann['category_id'] not in self.cat_ids:\n                continue\n            bbox = [x1, y1, x1 + w, y1 + h]\n\n            if ann.get('iscrowd', False):\n                instance['ignore_flag'] = 1\n            else:\n                instance['ignore_flag'] = 0\n            instance['bbox'] = bbox\n            instance['bbox_label'] = self.cat2label[ann['category_id']]\n\n            if ann.get('segmentation', None):\n                instance['mask'] = ann['segmentation']\n\n            instances.append(instance)\n        data_info['instances'] = instances\n        return data_info\n\n    def filter_data(self) -> List[dict]:\n        \"\"\"Filter annotations according to filter_cfg.\n\n        Returns:\n            List[dict]: Filtered results.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        if self.filter_cfg is None:\n            return self.data_list\n\n        filter_empty_gt = self.filter_cfg.get('filter_empty_gt', False)\n        min_size = self.filter_cfg.get('min_size', 0)\n\n        # obtain images that contain annotation\n        ids_with_ann = set(data_info['img_id'] for data_info in self.data_list)\n        # obtain images that contain annotations of the required categories\n        ids_in_cat = set()\n        for i, class_id in enumerate(self.cat_ids):\n            ids_in_cat |= set(self.cat_img_map[class_id])\n        # merge the image id sets of the two conditions and use the merged set\n        # to filter out images if self.filter_empty_gt=True\n        ids_in_cat &= ids_with_ann\n\n        valid_data_infos = []\n        for i, data_info in enumerate(self.data_list):\n            img_id = data_info['img_id']\n            width = data_info['width']\n            height = data_info['height']\n            if filter_empty_gt and img_id not in ids_in_cat:\n                continue\n            if min(width, height) >= min_size:\n                valid_data_infos.append(data_info)\n\n        return valid_data_infos\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/tensorflow/coco_90metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport datetime\nimport itertools\nimport os.path as osp\nimport tempfile\nfrom collections import OrderedDict\nfrom typing import Dict, List, Optional, Sequence, Union\n\nimport numpy as np\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.fileio import dump, get_local_path, load\nfrom mmengine.logging import MMLogger\nfrom terminaltables import AsciiTable\n\nfrom mmdet.evaluation.functional import eval_recalls\nfrom mmdet.registry import METRICS\nfrom mmdet.structures.mask import encode_mask_results\nfrom .api_wrappers import COCO, COCOeval\n\n\n@METRICS.register_module()\nclass Coco90Metric(BaseMetric):\n    \"\"\"COCO evaluation metric.\n\n    Evaluate AR, AP, and mAP for detection tasks including proposal/box\n    detection and instance segmentation. Please refer to\n    https://cocodataset.org/#detection-eval for more details.\n\n    Args:\n        ann_file (str, optional): Path to the coco format annotation file.\n            If not specified, ground truth annotations from the dataset will\n            be converted to coco format. Defaults to None.\n        metric (str | List[str]): Metrics to be evaluated. Valid metrics\n            include 'bbox', 'segm', 'proposal', and 'proposal_fast'.\n            Defaults to 'bbox'.\n        classwise (bool): Whether to evaluate the metric class-wise.\n            Defaults to False.\n        proposal_nums (Sequence[int]): Numbers of proposals to be evaluated.\n            Defaults to (100, 300, 1000).\n        iou_thrs (float | List[float], optional): IoU threshold to compute AP\n            and AR. If not specified, IoUs from 0.5 to 0.95 will be used.\n            Defaults to None.\n        metric_items (List[str], optional): Metric result names to be\n            recorded in the evaluation result. Defaults to None.\n        format_only (bool): Format the output results without perform\n            evaluation. It is useful when you want to format the result\n            to a specific format and submit it to the test server.\n            Defaults to False.\n        outfile_prefix (str, optional): The prefix of json files. It includes\n            the file path and the prefix of filename, e.g., \"a/b/prefix\".\n            If not specified, a temp file will be created. Defaults to None.\n        backend_args (dict, optional): Arguments to instantiate the\n            corresponding backend. Defaults to None.\n        collect_device (str): Device name used for collecting results from\n            different ranks during distributed training. Must be 'cpu' or\n            'gpu'. Defaults to 'cpu'.\n        prefix (str, optional): The prefix that will be added in the metric\n            names to disambiguate homonymous metrics of different evaluators.\n            If prefix is not provided in the argument, self.default_prefix\n            will be used instead. Defaults to None.\n    \"\"\"\n    default_prefix: Optional[str] = 'coco'\n\n    def __init__(self,\n                 ann_file: Optional[str] = None,\n                 metric: Union[str, List[str]] = 'bbox',\n                 classwise: bool = False,\n                 proposal_nums: Sequence[int] = (100, 300, 1000),\n                 iou_thrs: Optional[Union[float, Sequence[float]]] = None,\n                 metric_items: Optional[Sequence[str]] = None,\n                 format_only: bool = False,\n                 outfile_prefix: Optional[str] = None,\n                 backend_args: dict = None,\n                 collect_device: str = 'cpu',\n                 prefix: Optional[str] = None) -> None:\n        super().__init__(collect_device=collect_device, prefix=prefix)\n        # coco evaluation metrics\n        self.metrics = metric if isinstance(metric, list) else [metric]\n        allowed_metrics = ['bbox', 'segm', 'proposal', 'proposal_fast']\n        for metric in self.metrics:\n            if metric not in allowed_metrics:\n                raise KeyError(\n                    \"metric should be one of 'bbox', 'segm', 'proposal', \"\n                    f\"'proposal_fast', but got {metric}.\")\n\n        # do class wise evaluation, default False\n        self.classwise = classwise\n\n        # proposal_nums used to compute recall or precision.\n        self.proposal_nums = list(proposal_nums)\n\n        # iou_thrs used to compute recall or precision.\n        if iou_thrs is None:\n            iou_thrs = np.linspace(\n                .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)\n        self.iou_thrs = iou_thrs\n        self.metric_items = metric_items\n        self.format_only = format_only\n        if self.format_only:\n            assert outfile_prefix is not None, 'outfile_prefix must be not'\n            'None when format_only is True, otherwise the result files will'\n            'be saved to a temp directory which will be cleaned up at the end.'\n\n        self.outfile_prefix = outfile_prefix\n\n        self.backend_args = backend_args\n\n        # if ann_file is not specified,\n        # initialize coco api with the converted dataset\n        if ann_file is not None:\n            with get_local_path(\n                    ann_file, backend_args=self.backend_args) as local_path:\n                self._coco_api = COCO(local_path)\n        else:\n            self._coco_api = None\n\n        # handle dataset lazy init\n        self.cat_ids = None\n        self.img_ids = None\n\n    def fast_eval_recall(self,\n                         results: List[dict],\n                         proposal_nums: Sequence[int],\n                         iou_thrs: Sequence[float],\n                         logger: Optional[MMLogger] = None) -> np.ndarray:\n        \"\"\"Evaluate proposal recall with COCO's fast_eval_recall.\n\n        Args:\n            results (List[dict]): Results of the dataset.\n            proposal_nums (Sequence[int]): Proposal numbers used for\n                evaluation.\n            iou_thrs (Sequence[float]): IoU thresholds used for evaluation.\n            logger (MMLogger, optional): Logger used for logging the recall\n                summary.\n        Returns:\n            np.ndarray: Averaged recall results.\n        \"\"\"\n        gt_bboxes = []\n        pred_bboxes = [result['bboxes'] for result in results]\n        for i in range(len(self.img_ids)):\n            ann_ids = self._coco_api.get_ann_ids(img_ids=self.img_ids[i])\n            ann_info = self._coco_api.load_anns(ann_ids)\n            if len(ann_info) == 0:\n                gt_bboxes.append(np.zeros((0, 4)))\n                continue\n            bboxes = []\n            for ann in ann_info:\n                if ann.get('ignore', False) or ann['iscrowd']:\n                    continue\n                x1, y1, w, h = ann['bbox']\n                bboxes.append([x1, y1, x1 + w, y1 + h])\n            bboxes = np.array(bboxes, dtype=np.float32)\n            if bboxes.shape[0] == 0:\n                bboxes = np.zeros((0, 4))\n            gt_bboxes.append(bboxes)\n\n        recalls = eval_recalls(\n            gt_bboxes, pred_bboxes, proposal_nums, iou_thrs, logger=logger)\n        ar = recalls.mean(axis=1)\n        return ar\n\n    def xyxy2xywh(self, bbox: np.ndarray) -> list:\n        \"\"\"Convert ``xyxy`` style bounding boxes to ``xywh`` style for COCO\n        evaluation.\n\n        Args:\n            bbox (numpy.ndarray): The bounding boxes, shape (4, ), in\n                ``xyxy`` order.\n\n        Returns:\n            list[float]: The converted bounding boxes, in ``xywh`` order.\n        \"\"\"\n\n        _bbox: List = bbox.tolist()\n        return [\n            _bbox[0],\n            _bbox[1],\n            _bbox[2] - _bbox[0],\n            _bbox[3] - _bbox[1],\n        ]\n\n    def results2json(self, results: Sequence[dict],\n                     outfile_prefix: str) -> dict:\n        \"\"\"Dump the detection results to a COCO style json file.\n\n        There are 3 types of results: proposals, bbox predictions, mask\n        predictions, and they have different data types. This method will\n        automatically recognize the type, and dump them to json files.\n\n        Args:\n            results (Sequence[dict]): Testing results of the\n                dataset.\n            outfile_prefix (str): The filename prefix of the json files. If the\n                prefix is \"somepath/xxx\", the json files will be named\n                \"somepath/xxx.bbox.json\", \"somepath/xxx.segm.json\",\n                \"somepath/xxx.proposal.json\".\n\n        Returns:\n            dict: Possible keys are \"bbox\", \"segm\", \"proposal\", and\n            values are corresponding filenames.\n        \"\"\"\n        bbox_json_results = []\n        segm_json_results = [] if 'masks' in results[0] else None\n        for idx, result in enumerate(results):\n            image_id = result.get('img_id', idx)\n            labels = result['labels']\n            bboxes = result['bboxes']\n            scores = result['scores']\n            # bbox results\n            for i, label in enumerate(labels):\n                data = dict()\n                data['image_id'] = image_id\n                data['bbox'] = self.xyxy2xywh(bboxes[i])\n                data['score'] = float(scores[i])\n                data['category_id'] = self.cat_ids[label]\n                bbox_json_results.append(data)\n\n            if segm_json_results is None:\n                continue\n\n            # segm results\n            masks = result['masks']\n            mask_scores = result.get('mask_scores', scores)\n            for i, label in enumerate(labels):\n                data = dict()\n                data['image_id'] = image_id\n                data['bbox'] = self.xyxy2xywh(bboxes[i])\n                data['score'] = float(mask_scores[i])\n                data['category_id'] = self.cat_ids[label]\n                if isinstance(masks[i]['counts'], bytes):\n                    masks[i]['counts'] = masks[i]['counts'].decode()\n                data['segmentation'] = masks[i]\n                segm_json_results.append(data)\n\n        result_files = dict()\n        result_files['bbox'] = f'{outfile_prefix}.bbox.json'\n        result_files['proposal'] = f'{outfile_prefix}.bbox.json'\n        dump(bbox_json_results, result_files['bbox'])\n\n        if segm_json_results is not None:\n            result_files['segm'] = f'{outfile_prefix}.segm.json'\n            dump(segm_json_results, result_files['segm'])\n\n        return result_files\n\n    def gt_to_coco_json(self, gt_dicts: Sequence[dict],\n                        outfile_prefix: str) -> str:\n        \"\"\"Convert ground truth to coco format json file.\n\n        Args:\n            gt_dicts (Sequence[dict]): Ground truth of the dataset.\n            outfile_prefix (str): The filename prefix of the json files. If the\n                prefix is \"somepath/xxx\", the json file will be named\n                \"somepath/xxx.gt.json\".\n        Returns:\n            str: The filename of the json file.\n        \"\"\"\n        categories = [\n            dict(id=id, name=name)\n            for id, name in enumerate(self.dataset_meta['classes'])\n        ]\n        image_infos = []\n        annotations = []\n\n        for idx, gt_dict in enumerate(gt_dicts):\n            img_id = gt_dict.get('img_id', idx)\n            image_info = dict(\n                id=img_id,\n                width=gt_dict['width'],\n                height=gt_dict['height'],\n                file_name='')\n            image_infos.append(image_info)\n            for ann in gt_dict['anns']:\n                label = ann['bbox_label']\n                bbox = ann['bbox']\n                coco_bbox = [\n                    bbox[0],\n                    bbox[1],\n                    bbox[2] - bbox[0],\n                    bbox[3] - bbox[1],\n                ]\n\n                annotation = dict(\n                    id=len(annotations) +\n                    1,  # coco api requires id starts with 1\n                    image_id=img_id,\n                    bbox=coco_bbox,\n                    iscrowd=ann.get('ignore_flag', 0),\n                    category_id=int(label),\n                    area=coco_bbox[2] * coco_bbox[3])\n                if ann.get('mask', None):\n                    mask = ann['mask']\n                    # area = mask_util.area(mask)\n                    if isinstance(mask, dict) and isinstance(\n                            mask['counts'], bytes):\n                        mask['counts'] = mask['counts'].decode()\n                    annotation['segmentation'] = mask\n                    # annotation['area'] = float(area)\n                annotations.append(annotation)\n\n        info = dict(\n            date_created=str(datetime.datetime.now()),\n            description='Coco json file converted by mmdet CocoMetric.')\n        coco_json = dict(\n            info=info,\n            images=image_infos,\n            categories=categories,\n            licenses=None,\n        )\n        if len(annotations) > 0:\n            coco_json['annotations'] = annotations\n        converted_json_path = f'{outfile_prefix}.gt.json'\n        dump(coco_json, converted_json_path)\n        return converted_json_path\n\n    # TODO: data_batch is no longer needed, consider adjusting the\n    #  parameter position\n    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:\n        \"\"\"Process one batch of data samples and predictions. The processed\n        results should be stored in ``self.results``, which will be used to\n        compute the metrics when all batches have been processed.\n\n        Args:\n            data_batch (dict): A batch of data from the dataloader.\n            data_samples (Sequence[dict]): A batch of data samples that\n                contain annotations and predictions.\n        \"\"\"\n        for data_sample in data_samples:\n            result = dict()\n            pred = data_sample['pred_instances']\n            result['img_id'] = data_sample['img_id']\n            result['bboxes'] = pred['bboxes'].cpu().numpy()\n            result['scores'] = pred['scores'].cpu().numpy()\n            result['labels'] = pred['labels'].cpu().numpy()\n            # encode mask to RLE\n            if 'masks' in pred:\n                result['masks'] = encode_mask_results(\n                    pred['masks'].detach().cpu().numpy())\n            # some detectors use different scores for bbox and mask\n            if 'mask_scores' in pred:\n                result['mask_scores'] = pred['mask_scores'].cpu().numpy()\n\n            # parse gt\n            gt = dict()\n            gt['width'] = data_sample['ori_shape'][1]\n            gt['height'] = data_sample['ori_shape'][0]\n            gt['img_id'] = data_sample['img_id']\n            if self._coco_api is None:\n                # TODO: Need to refactor to support LoadAnnotations\n                assert 'instances' in data_sample, \\\n                    'ground truth is required for evaluation when ' \\\n                    '`ann_file` is not provided'\n                gt['anns'] = data_sample['instances']\n            # add converted result to the results list\n            self.results.append((gt, result))\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        # split gt and prediction list\n        gts, preds = zip(*results)\n\n        tmp_dir = None\n        if self.outfile_prefix is None:\n            tmp_dir = tempfile.TemporaryDirectory()\n            outfile_prefix = osp.join(tmp_dir.name, 'results')\n        else:\n            outfile_prefix = self.outfile_prefix\n\n        if self._coco_api is None:\n            # use converted gt json file to initialize coco api\n            logger.info('Converting ground truth to coco format...')\n            coco_json_path = self.gt_to_coco_json(\n                gt_dicts=gts, outfile_prefix=outfile_prefix)\n            self._coco_api = COCO(coco_json_path)\n\n            # handle lazy init\n        if self.cat_ids is None:\n            self.cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['classes'])\n        if self.img_ids is None:\n            self.img_ids = self._coco_api.get_img_ids()\n\n        # convert predictions to coco format and dump to json file\n        result_files = self.results2json(preds, outfile_prefix)\n\n        eval_results = OrderedDict()\n        if self.format_only:\n            logger.info('results are saved in '\n                        f'{osp.dirname(outfile_prefix)}')\n            return eval_results\n\n        for metric in self.metrics:\n            logger.info(f'Evaluating {metric}...')\n\n            # TODO: May refactor fast_eval_recall to an independent metric?\n            # fast eval recall\n            if metric == 'proposal_fast':\n                ar = self.fast_eval_recall(\n                    preds, self.proposal_nums, self.iou_thrs, logger=logger)\n                log_msg = []\n                for i, num in enumerate(self.proposal_nums):\n                    eval_results[f'AR@{num}'] = ar[i]\n                    log_msg.append(f'\\nAR@{num}\\t{ar[i]:.4f}')\n                log_msg = ''.join(log_msg)\n                logger.info(log_msg)\n                continue\n\n            # evaluate proposal, bbox and segm\n            iou_type = 'bbox' if metric == 'proposal' else metric\n            if metric not in result_files:\n                raise KeyError(f'{metric} is not in results')\n            try:\n                predictions = load(result_files[metric])\n                if iou_type == 'segm':\n                    # Refer to https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/coco.py#L331  # noqa\n                    # When evaluating mask AP, if the results contain bbox,\n                    # cocoapi will use the box area instead of the mask area\n                    # for calculating the instance area. Though the overall AP\n                    # is not affected, this leads to different\n                    # small/medium/large mask AP results.\n                    for x in predictions:\n                        x.pop('bbox')\n                coco_dt = self._coco_api.loadRes(predictions)\n\n            except IndexError:\n                logger.error(\n                    'The testing results of the whole dataset is empty.')\n                break\n\n            coco_eval = COCOeval(self._coco_api, coco_dt, iou_type)\n\n            coco_eval.params.catIds = self.cat_ids\n            coco_eval.params.imgIds = self.img_ids\n            coco_eval.params.maxDets = list(self.proposal_nums)\n            coco_eval.params.iouThrs = self.iou_thrs\n\n            # mapping of cocoEval.stats\n            coco_metric_names = {\n                'mAP': 0,\n                'mAP_50': 1,\n                'mAP_75': 2,\n                'mAP_s': 3,\n                'mAP_m': 4,\n                'mAP_l': 5,\n                'AR@100': 6,\n                'AR@300': 7,\n                'AR@1000': 8,\n                'AR_s@1000': 9,\n                'AR_m@1000': 10,\n                'AR_l@1000': 11\n            }\n            metric_items = self.metric_items\n            if metric_items is not None:\n                for metric_item in metric_items:\n                    if metric_item not in coco_metric_names:\n                        raise KeyError(\n                            f'metric item \"{metric_item}\" is not supported')\n\n            if metric == 'proposal':\n                coco_eval.params.useCats = 0\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if metric_items is None:\n                    metric_items = [\n                        'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000',\n                        'AR_m@1000', 'AR_l@1000'\n                    ]\n\n                for item in metric_items:\n                    val = float(\n                        f'{coco_eval.stats[coco_metric_names[item]]:.3f}')\n                    eval_results[item] = val\n            else:\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if self.classwise:  # Compute per-category AP\n                    # Compute per-category AP\n                    # from https://github.com/facebookresearch/detectron2/\n                    precisions = coco_eval.eval['precision']\n                    # precision: (iou, recall, cls, area range, max dets)\n                    assert len(self.cat_ids) == precisions.shape[2]\n\n                    results_per_category = []\n                    for idx, cat_id in enumerate(self.cat_ids):\n                        # area range index 0: all area ranges\n                        # max dets index -1: typically 100 per image\n                        nm = self._coco_api.loadCats(cat_id)[0]\n                        precision = precisions[:, :, idx, 0, -1]\n                        precision = precision[precision > -1]\n                        if precision.size:\n                            ap = np.mean(precision)\n                        else:\n                            ap = float('nan')\n                        results_per_category.append(\n                            (f'{nm[\"name\"]}', f'{round(ap, 3)}'))\n                        eval_results[f'{nm[\"name\"]}_precision'] = round(ap, 3)\n\n                    num_columns = min(6, len(results_per_category) * 2)\n                    results_flatten = list(\n                        itertools.chain(*results_per_category))\n                    headers = ['category', 'AP'] * (num_columns // 2)\n                    results_2d = itertools.zip_longest(*[\n                        results_flatten[i::num_columns]\n                        for i in range(num_columns)\n                    ])\n                    table_data = [headers]\n                    table_data += [result for result in results_2d]\n                    table = AsciiTable(table_data)\n                    logger.info('\\n' + table.table)\n\n                if metric_items is None:\n                    metric_items = [\n                        'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l'\n                    ]\n\n                for metric_item in metric_items:\n                    key = f'{metric}_{metric_item}'\n                    val = coco_eval.stats[coco_metric_names[metric_item]]\n                    eval_results[key] = float(f'{round(val, 3)}')\n\n                ap = coco_eval.stats[:6]\n                logger.info(f'{metric}_mAP_copypaste: {ap[0]:.3f} '\n                            f'{ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} '\n                            f'{ap[4]:.3f} {ap[5]:.3f}')\n\n        if tmp_dir is not None:\n            tmp_dir.cleanup()\n        return eval_results\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/tensorflow/trans_max_iou_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Optional\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners.assign_result import AssignResult\nfrom mmdet.models.task_modules.assigners.max_iou_assigner import MaxIoUAssigner\nfrom mmdet.registry import TASK_UTILS\n\n\n@TASK_UTILS.register_module()\nclass TransMaxIoUAssigner(MaxIoUAssigner):\n\n    def assign(self,\n               pred_instances: InstanceData,\n               gt_instances: InstanceData,\n               gt_instances_ignore: Optional[InstanceData] = None,\n               **kwargs) -> AssignResult:\n        \"\"\"Assign gt to bboxes.\n\n        This method assign a gt bbox to every bbox (proposal/anchor), each bbox\n        will be assigned with -1, or a semi-positive number. -1 means negative\n        sample, semi-positive number is the index (0-based) of assigned gt.\n        The assignment is done in following steps, the order matters.\n\n        1. assign every bbox to the background\n        2. assign proposals whose iou with all gts < neg_iou_thr to 0\n        3. for each bbox, if the iou with its nearest gt >= pos_iou_thr,\n           assign it to that bbox\n        4. for each gt bbox, assign its nearest proposals (may be more than\n           one) to itself\n\n        Args:\n            pred_instances (:obj:`InstanceData`): Instances of model\n                predictions. It includes ``priors``, and the priors can\n                be anchors or points, or the bboxes predicted by the\n                previous stage, has shape (n, 4). The bboxes predicted by\n                the current model or stage will be named ``bboxes``,\n                ``labels``, and ``scores``, the same as the ``InstanceData``\n                in other places.\n            gt_instances (:obj:`InstanceData`): Ground truth of instance\n                annotations. It usually includes ``bboxes``, with shape (k, 4),\n                and ``labels``, with shape (k, ).\n            gt_instances_ignore (:obj:`InstanceData`, optional): Instances\n                to be ignored during training. It includes ``bboxes``\n                attribute data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            :obj:`AssignResult`: The assign result.\n\n        Example:\n            >>> from mmengine.structures import InstanceData\n            >>> self = MaxIoUAssigner(0.5, 0.5)\n            >>> pred_instances = InstanceData()\n            >>> pred_instances.priors = torch.Tensor([[0, 0, 10, 10],\n            ...                                      [10, 10, 20, 20]])\n            >>> gt_instances = InstanceData()\n            >>> gt_instances.bboxes = torch.Tensor([[0, 0, 10, 9]])\n            >>> gt_instances.labels = torch.Tensor([0])\n            >>> assign_result = self.assign(pred_instances, gt_instances)\n            >>> expected_gt_inds = torch.LongTensor([1, 0])\n            >>> assert torch.all(assign_result.gt_inds == expected_gt_inds)\n        \"\"\"\n        gt_bboxes = gt_instances.bboxes\n        priors = pred_instances.priors\n        gt_labels = gt_instances.labels\n        if gt_instances_ignore is not None:\n            gt_bboxes_ignore = gt_instances_ignore.bboxes\n        else:\n            gt_bboxes_ignore = None\n\n        assign_on_cpu = True if (self.gpu_assign_thr > 0) and (\n            gt_bboxes.shape[0] > self.gpu_assign_thr) else False\n        # compute overlap and assign gt on CPU when number of GT is large\n        if assign_on_cpu:\n            device = priors.device\n            priors = priors.cpu()\n            gt_bboxes = gt_bboxes.cpu()\n            gt_labels = gt_labels.cpu()\n            if gt_bboxes_ignore is not None:\n                gt_bboxes_ignore = gt_bboxes_ignore.cpu()\n\n        trans_priors = torch.cat([\n            priors[..., 1].view(-1, 1), priors[..., 0].view(-1, 1),\n            priors[..., 3].view(-1, 1), priors[..., 2].view(-1, 1)\n        ],\n                                 dim=-1)\n        overlaps = self.iou_calculator(gt_bboxes, trans_priors)\n\n        if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None\n                and gt_bboxes_ignore.numel() > 0 and trans_priors.numel() > 0):\n            if self.ignore_wrt_candidates:\n                ignore_overlaps = self.iou_calculator(\n                    trans_priors, gt_bboxes_ignore, mode='iof')\n                ignore_max_overlaps, _ = ignore_overlaps.max(dim=1)\n            else:\n                ignore_overlaps = self.iou_calculator(\n                    gt_bboxes_ignore, trans_priors, mode='iof')\n                ignore_max_overlaps, _ = ignore_overlaps.max(dim=0)\n            overlaps[:, ignore_max_overlaps > self.ignore_iof_thr] = -1\n\n        assign_result = self.assign_wrt_overlaps(overlaps, gt_labels)\n        if assign_on_cpu:\n            assign_result.gt_inds = assign_result.gt_inds.to(device)\n            assign_result.max_overlaps = assign_result.max_overlaps.to(device)\n            if assign_result.labels is not None:\n                assign_result.labels = assign_result.labels.to(device)\n        return assign_result\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/tensorflow/yxyx_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport warnings\n\nimport numpy as np\nimport torch\n\nfrom mmdet.models.task_modules.coders.delta_xywh_bbox_coder import \\\n    DeltaXYWHBBoxCoder\nfrom mmdet.registry import TASK_UTILS\nfrom mmdet.structures.bbox import HorizontalBoxes, get_box_tensor\n\n\n@TASK_UTILS.register_module()\nclass YXYXDeltaXYWHBBoxCoder(DeltaXYWHBBoxCoder):\n\n    def encode(self, bboxes, gt_bboxes):\n        \"\"\"Get box regression transformation deltas that can be used to\n        transform the ``bboxes`` into the ``gt_bboxes``.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Source boxes,\n                e.g., object proposals.\n            gt_bboxes (torch.Tensor or :obj:`BaseBoxes`): Target of the\n                transformation, e.g., ground-truth boxes.\n\n        Returns:\n            torch.Tensor: Box transformation deltas\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        gt_bboxes = get_box_tensor(gt_bboxes)\n        assert bboxes.size(0) == gt_bboxes.size(0)\n        assert bboxes.size(-1) == gt_bboxes.size(-1) == 4\n        encoded_bboxes = YXbbox2delta(bboxes, gt_bboxes, self.means, self.stds)\n        return encoded_bboxes\n\n    def decode(self,\n               bboxes,\n               pred_bboxes,\n               max_shape=None,\n               wh_ratio_clip=16 / 1000):\n        \"\"\"Apply transformation `pred_bboxes` to `boxes`.\n\n        Args:\n            bboxes (torch.Tensor or :obj:`BaseBoxes`): Basic boxes. Shape\n                (B, N, 4) or (N, 4)\n            pred_bboxes (Tensor): Encoded offsets with respect to each roi.\n               Has shape (B, N, num_classes * 4) or (B, N, 4) or\n               (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H\n               when rois is a grid of anchors.Offset encoding follows [1]_.\n            max_shape (Sequence[int] or torch.Tensor or Sequence[\n               Sequence[int]],optional): Maximum bounds for boxes, specifies\n               (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then\n               the max_shape should be a Sequence[Sequence[int]]\n               and the length of max_shape should also be B.\n            wh_ratio_clip (float, optional): The allowed ratio between\n                width and height.\n\n        Returns:\n            Union[torch.Tensor, :obj:`BaseBoxes`]: Decoded boxes.\n        \"\"\"\n        bboxes = get_box_tensor(bboxes)\n        assert pred_bboxes.size(0) == bboxes.size(0)\n        if pred_bboxes.ndim == 3:\n            assert pred_bboxes.size(1) == bboxes.size(1)\n\n        if pred_bboxes.ndim == 2 and not torch.onnx.is_in_onnx_export():\n            # single image decode\n            decoded_bboxes = YXdelta2bbox(bboxes, pred_bboxes, self.means,\n                                          self.stds, max_shape, wh_ratio_clip,\n                                          self.clip_border, self.add_ctr_clamp,\n                                          self.ctr_clamp)\n        else:\n            if pred_bboxes.ndim == 3 and not torch.onnx.is_in_onnx_export():\n                warnings.warn(\n                    'DeprecationWarning: onnx_delta2bbox is deprecated '\n                    'in the case of batch decoding and non-ONNX, '\n                    'please use “delta2bbox” instead. In order to improve '\n                    'the decoding speed, the batch function will no '\n                    'longer be supported. ')\n            decoded_bboxes = YXonnx_delta2bbox(bboxes, pred_bboxes, self.means,\n                                               self.stds, max_shape,\n                                               wh_ratio_clip, self.clip_border,\n                                               self.add_ctr_clamp,\n                                               self.ctr_clamp)\n\n        if self.use_box_type:\n            assert decoded_bboxes.size(-1) == 4, \\\n                ('Cannot warp decoded boxes with box type when decoded boxes'\n                 'have shape of (N, num_classes * 4)')\n            decoded_bboxes = HorizontalBoxes(decoded_bboxes)\n        return decoded_bboxes\n\n\ndef YXdelta2bbox(rois,\n                 deltas,\n                 means=(0., 0., 0., 0.),\n                 stds=(1., 1., 1., 1.),\n                 max_shape=None,\n                 hw_ratio_clip=1000 / 16,\n                 clip_border=True,\n                 add_ctr_clamp=False,\n                 ctr_clamp=32):\n    \"\"\"Apply deltas to shift/scale base boxes.\n\n    Typically the rois are anchor or proposed bounding boxes and the deltas are\n    network outputs used to shift/scale those boxes.\n    This is the inverse function of :func:`bbox2delta`.\n\n    Args:\n        rois (Tensor): Boxes to be transformed. Has shape (N, 4).\n        deltas (Tensor): Encoded offsets relative to each roi.\n            Has shape (N, num_classes * 4) or (N, 4). Note\n            N = num_base_anchors * W * H, when rois is a grid of\n            anchors. Offset encoding follows [1]_.\n        means (Sequence[float]): Denormalizing means for delta coordinates.\n            Default (0., 0., 0., 0.).\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates. Default (1., 1., 1., 1.).\n        max_shape (tuple[int, int]): Maximum bounds for boxes, specifies\n           (H, W). Default None.\n        wh_ratio_clip (float): Maximum aspect ratio for boxes. Default\n            16 / 1000.\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Default True.\n        add_ctr_clamp (bool): Whether to add center clamp. When set to True,\n            the center of the prediction bounding box will be clamped to\n            avoid being too far away from the center of the anchor.\n            Only used by YOLOF. Default False.\n        ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF.\n            Default 32.\n\n    Returns:\n        Tensor: Boxes with shape (N, num_classes * 4) or (N, 4), where 4\n           represent tl_x, tl_y, br_x, br_y.\n\n    References:\n        .. [1] https://arxiv.org/abs/1311.2524\n\n    Example:\n        >>> rois = torch.Tensor([[ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 5.,  5.,  5.,  5.]])\n        >>> deltas = torch.Tensor([[  0.,   0.,   0.,   0.],\n        >>>                        [  1.,   1.,   1.,   1.],\n        >>>                        [  0.,   0.,   2.,  -1.],\n        >>>                        [ 0.7, -1.9, -0.5,  0.3]])\n        >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3))\n        tensor([[0.0000, 0.0000, 1.0000, 1.0000],\n                [0.1409, 0.1409, 2.8591, 2.8591],\n                [0.0000, 0.3161, 4.1945, 0.6839],\n                [5.0000, 5.0000, 5.0000, 5.0000]])\n    \"\"\"\n    num_bboxes, num_classes = deltas.size(0), deltas.size(1) // 4\n    if num_bboxes == 0:\n        return deltas\n\n    deltas = deltas.reshape(-1, 4)\n\n    means = deltas.new_tensor(means).view(1, -1)\n    stds = deltas.new_tensor(stds).view(1, -1)\n    denorm_deltas = deltas * stds + means\n\n    dyx = denorm_deltas[:, :2]\n    dhw = denorm_deltas[:, 2:]\n\n    # Compute width/height of each roi\n    rois_ = rois.repeat(1, num_classes).reshape(-1, 4)\n    pyx = ((rois_[:, :2] + rois_[:, 2:]) * 0.5)\n    phw = (rois_[:, 2:] - rois_[:, :2])\n\n    dyx_hw = phw * dyx\n\n    max_ratio = np.abs(np.log(hw_ratio_clip))\n    if add_ctr_clamp:\n        dyx_hw = torch.clamp(dyx_hw, max=ctr_clamp, min=-ctr_clamp)\n        dhw = torch.clamp(dhw, max=max_ratio)\n    else:\n        dhw = dhw.clamp(min=-max_ratio, max=max_ratio)\n\n    gyx = pyx + dyx_hw\n    ghw = phw * dhw.exp()\n    y1x1 = gyx - (ghw * 0.5)\n    y2x2 = gyx + (ghw * 0.5)\n    ymin, xmin = y1x1[:, 0].reshape(-1, 1), y1x1[:, 1].reshape(-1, 1)\n    ymax, xmax = y2x2[:, 0].reshape(-1, 1), y2x2[:, 1].reshape(-1, 1)\n    bboxes = torch.cat([xmin, ymin, xmax, ymax], dim=-1)\n    if clip_border and max_shape is not None:\n        bboxes[..., 0::2].clamp_(min=0, max=max_shape[1])\n        bboxes[..., 1::2].clamp_(min=0, max=max_shape[0])\n    bboxes = bboxes.reshape(num_bboxes, -1)\n    return bboxes\n\n\ndef YXbbox2delta(proposals, gt, means=(0., 0., 0., 0.), stds=(1., 1., 1., 1.)):\n    \"\"\"Compute deltas of proposals w.r.t. gt.\n\n    We usually compute the deltas of x, y, w, h of proposals w.r.t ground\n    truth bboxes to get regression target.\n    This is the inverse function of :func:`delta2bbox`.\n\n    Args:\n        proposals (Tensor): Boxes to be transformed, shape (N, ..., 4)\n        gt (Tensor): Gt bboxes to be used as base, shape (N, ..., 4)\n        means (Sequence[float]): Denormalizing means for delta coordinates\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates\n\n    Returns:\n        Tensor: deltas with shape (N, 4), where columns represent dx, dy,\n            dw, dh.\n    \"\"\"\n    assert proposals.size() == gt.size()\n\n    proposals = proposals.float()\n    gt = gt.float()\n    py = (proposals[..., 0] + proposals[..., 2]) * 0.5\n    px = (proposals[..., 1] + proposals[..., 3]) * 0.5\n    ph = proposals[..., 2] - proposals[..., 0]\n    pw = proposals[..., 3] - proposals[..., 1]\n\n    gx = (gt[..., 0] + gt[..., 2]) * 0.5\n    gy = (gt[..., 1] + gt[..., 3]) * 0.5\n    gw = gt[..., 2] - gt[..., 0]\n    gh = gt[..., 3] - gt[..., 1]\n\n    dx = (gx - px) / pw\n    dy = (gy - py) / ph\n    dw = torch.log(gw / pw)\n    dh = torch.log(gh / ph)\n    deltas = torch.stack([dy, dx, dh, dw], dim=-1)\n\n    means = deltas.new_tensor(means).unsqueeze(0)\n    stds = deltas.new_tensor(stds).unsqueeze(0)\n    deltas = deltas.sub_(means).div_(stds)\n\n    return deltas\n\n\ndef YXonnx_delta2bbox(rois,\n                      deltas,\n                      means=(0., 0., 0., 0.),\n                      stds=(1., 1., 1., 1.),\n                      max_shape=None,\n                      wh_ratio_clip=16 / 1000,\n                      clip_border=True,\n                      add_ctr_clamp=False,\n                      ctr_clamp=32):\n    \"\"\"Apply deltas to shift/scale base boxes.\n\n    Typically the rois are anchor or proposed bounding boxes and the deltas are\n    network outputs used to shift/scale those boxes.\n    This is the inverse function of :func:`bbox2delta`.\n\n    Args:\n        rois (Tensor): Boxes to be transformed. Has shape (N, 4) or (B, N, 4)\n        deltas (Tensor): Encoded offsets with respect to each roi.\n            Has shape (B, N, num_classes * 4) or (B, N, 4) or\n            (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H\n            when rois is a grid of anchors.Offset encoding follows [1]_.\n        means (Sequence[float]): Denormalizing means for delta coordinates.\n            Default (0., 0., 0., 0.).\n        stds (Sequence[float]): Denormalizing standard deviation for delta\n            coordinates. Default (1., 1., 1., 1.).\n        max_shape (Sequence[int] or torch.Tensor or Sequence[\n            Sequence[int]],optional): Maximum bounds for boxes, specifies\n            (H, W, C) or (H, W). If rois shape is (B, N, 4), then\n            the max_shape should be a Sequence[Sequence[int]]\n            and the length of max_shape should also be B. Default None.\n        wh_ratio_clip (float): Maximum aspect ratio for boxes.\n            Default 16 / 1000.\n        clip_border (bool, optional): Whether clip the objects outside the\n            border of the image. Default True.\n        add_ctr_clamp (bool): Whether to add center clamp, when added, the\n            predicted box is clamped is its center is too far away from\n            the original anchor's center. Only used by YOLOF. Default False.\n        ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF.\n            Default 32.\n\n    Returns:\n        Tensor: Boxes with shape (B, N, num_classes * 4) or (B, N, 4) or\n           (N, num_classes * 4) or (N, 4), where 4 represent\n           tl_x, tl_y, br_x, br_y.\n\n    References:\n        .. [1] https://arxiv.org/abs/1311.2524\n\n    Example:\n        >>> rois = torch.Tensor([[ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 0.,  0.,  1.,  1.],\n        >>>                      [ 5.,  5.,  5.,  5.]])\n        >>> deltas = torch.Tensor([[  0.,   0.,   0.,   0.],\n        >>>                        [  1.,   1.,   1.,   1.],\n        >>>                        [  0.,   0.,   2.,  -1.],\n        >>>                        [ 0.7, -1.9, -0.5,  0.3]])\n        >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3))\n        tensor([[0.0000, 0.0000, 1.0000, 1.0000],\n                [0.1409, 0.1409, 2.8591, 2.8591],\n                [0.0000, 0.3161, 4.1945, 0.6839],\n                [5.0000, 5.0000, 5.0000, 5.0000]])\n    \"\"\"\n    means = deltas.new_tensor(means).view(1,\n                                          -1).repeat(1,\n                                                     deltas.size(-1) // 4)\n    stds = deltas.new_tensor(stds).view(1, -1).repeat(1, deltas.size(-1) // 4)\n    denorm_deltas = deltas * stds + means\n    dy = denorm_deltas[..., 0::4]\n    dx = denorm_deltas[..., 1::4]\n    dh = denorm_deltas[..., 2::4]\n    dw = denorm_deltas[..., 3::4]\n\n    y1, x1 = rois[..., 0], rois[..., 1]\n    y2, x2 = rois[..., 2], rois[..., 3]\n    # Compute center of each roi\n    px = ((x1 + x2) * 0.5).unsqueeze(-1).expand_as(dx)\n    py = ((y1 + y2) * 0.5).unsqueeze(-1).expand_as(dy)\n    # Compute width/height of each roi\n    pw = (x2 - x1).unsqueeze(-1).expand_as(dw)\n    ph = (y2 - y1).unsqueeze(-1).expand_as(dh)\n\n    dx_width = pw * dx\n    dy_height = ph * dy\n\n    max_ratio = np.abs(np.log(wh_ratio_clip))\n    if add_ctr_clamp:\n        dx_width = torch.clamp(dx_width, max=ctr_clamp, min=-ctr_clamp)\n        dy_height = torch.clamp(dy_height, max=ctr_clamp, min=-ctr_clamp)\n        dw = torch.clamp(dw, max=max_ratio)\n        dh = torch.clamp(dh, max=max_ratio)\n    else:\n        dw = dw.clamp(min=-max_ratio, max=max_ratio)\n        dh = dh.clamp(min=-max_ratio, max=max_ratio)\n    # Use exp(network energy) to enlarge/shrink each roi\n    gw = pw * dw.exp()\n    gh = ph * dh.exp()\n    # Use network energy to shift the center of each roi\n    gx = px + dx_width\n    gy = py + dy_height\n    # Convert center-xy/width/height to top-left, bottom-right\n    x1 = gx - gw * 0.5\n    y1 = gy - gh * 0.5\n    x2 = gx + gw * 0.5\n    y2 = gy + gh * 0.5\n\n    bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size())\n\n    if clip_border and max_shape is not None:\n        # clip bboxes with dynamic `min` and `max` for onnx\n        if torch.onnx.is_in_onnx_export():\n            from mmdet.core.export import dynamic_clip_for_onnx\n            x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape)\n            bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size())\n            return bboxes\n        if not isinstance(max_shape, torch.Tensor):\n            max_shape = x1.new_tensor(max_shape)\n        max_shape = max_shape[..., :2].type_as(x1)\n        if max_shape.ndim == 2:\n            assert bboxes.ndim == 3\n            assert max_shape.size(0) == bboxes.size(0)\n\n        min_xy = x1.new_tensor(0)\n        max_xy = torch.cat(\n            [max_shape] * (deltas.size(-1) // 2),\n            dim=-1).flip(-1).unsqueeze(-2)\n        bboxes = torch.where(bboxes < min_xy, min_xy, bboxes)\n        bboxes = torch.where(bboxes > max_xy, max_xy, bboxes)\n\n    return bboxes\n"
  },
  {
    "path": "projects/EfficientDet/efficientdet/utils.py",
    "content": "import math\nfrom typing import Tuple, Union\n\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn.bricks import Swish, build_norm_layer\nfrom torch.nn import functional as F\nfrom torch.nn.init import _calculate_fan_in_and_fan_out, trunc_normal_\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import OptConfigType\n\n\ndef variance_scaling_trunc(tensor, gain=1.):\n    fan_in, _ = _calculate_fan_in_and_fan_out(tensor)\n    gain /= max(1.0, fan_in)\n    std = math.sqrt(gain) / .87962566103423978\n    return trunc_normal_(tensor, 0., std)\n\n\n@MODELS.register_module()\nclass Conv2dSamePadding(nn.Conv2d):\n\n    def __init__(self,\n                 in_channels: int,\n                 out_channels: int,\n                 kernel_size: Union[int, Tuple[int, int]],\n                 stride: Union[int, Tuple[int, int]] = 1,\n                 padding: Union[int, Tuple[int, int]] = 0,\n                 dilation: Union[int, Tuple[int, int]] = 1,\n                 groups: int = 1,\n                 bias: bool = True):\n        super().__init__(in_channels, out_channels, kernel_size, stride, 0,\n                         dilation, groups, bias)\n\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n        img_h, img_w = x.size()[-2:]\n        kernel_h, kernel_w = self.weight.size()[-2:]\n        extra_w = (math.ceil(img_w / self.stride[1]) -\n                   1) * self.stride[1] - img_w + kernel_w\n        extra_h = (math.ceil(img_h / self.stride[0]) -\n                   1) * self.stride[0] - img_h + kernel_h\n\n        left = extra_w // 2\n        right = extra_w - left\n        top = extra_h // 2\n        bottom = extra_h - top\n        x = F.pad(x, [left, right, top, bottom])\n        return F.conv2d(x, self.weight, self.bias, self.stride, self.padding,\n                        self.dilation, self.groups)\n\n\nclass MaxPool2dSamePadding(nn.Module):\n\n    def __init__(self,\n                 kernel_size: Union[int, Tuple[int, int]] = 3,\n                 stride: Union[int, Tuple[int, int]] = 2,\n                 **kwargs):\n        super().__init__()\n        self.pool = nn.MaxPool2d(kernel_size, stride, **kwargs)\n        self.stride = self.pool.stride\n        self.kernel_size = self.pool.kernel_size\n\n        if isinstance(self.stride, int):\n            self.stride = [self.stride] * 2\n        if isinstance(self.kernel_size, int):\n            self.kernel_size = [self.kernel_size] * 2\n\n    def forward(self, x):\n        h, w = x.shape[-2:]\n\n        extra_h = (math.ceil(w / self.stride[1]) -\n                   1) * self.stride[1] - w + self.kernel_size[1]\n        extra_v = (math.ceil(h / self.stride[0]) -\n                   1) * self.stride[0] - h + self.kernel_size[0]\n\n        left = extra_h // 2\n        right = extra_h - left\n        top = extra_v // 2\n        bottom = extra_v - top\n\n        x = F.pad(x, [left, right, top, bottom])\n        x = self.pool(x)\n\n        return x\n\n\nclass DepthWiseConvBlock(nn.Module):\n\n    def __init__(\n        self,\n        in_channels: int,\n        out_channels: int,\n        apply_norm: bool = True,\n        conv_bn_act_pattern: bool = False,\n        norm_cfg: OptConfigType = dict(type='BN', momentum=1e-2, eps=1e-3)\n    ) -> None:\n        super(DepthWiseConvBlock, self).__init__()\n        self.depthwise_conv = Conv2dSamePadding(\n            in_channels,\n            in_channels,\n            kernel_size=3,\n            stride=1,\n            groups=in_channels,\n            bias=False)\n        self.pointwise_conv = Conv2dSamePadding(\n            in_channels, out_channels, kernel_size=1, stride=1)\n\n        self.apply_norm = apply_norm\n        if self.apply_norm:\n            self.bn = build_norm_layer(norm_cfg, num_features=out_channels)[1]\n\n        self.apply_activation = conv_bn_act_pattern\n        if self.apply_activation:\n            self.swish = Swish()\n\n    def forward(self, x):\n        x = self.depthwise_conv(x)\n        x = self.pointwise_conv(x)\n        if self.apply_norm:\n            x = self.bn(x)\n        if self.apply_activation:\n            x = self.swish(x)\n\n        return x\n\n\nclass DownChannelBlock(nn.Module):\n\n    def __init__(\n        self,\n        in_channels: int,\n        out_channels: int,\n        apply_norm: bool = True,\n        conv_bn_act_pattern: bool = False,\n        norm_cfg: OptConfigType = dict(type='BN', momentum=1e-2, eps=1e-3)\n    ) -> None:\n        super(DownChannelBlock, self).__init__()\n        self.down_conv = Conv2dSamePadding(in_channels, out_channels, 1)\n        self.apply_norm = apply_norm\n        if self.apply_norm:\n            self.bn = build_norm_layer(norm_cfg, num_features=out_channels)[1]\n        self.apply_activation = conv_bn_act_pattern\n        if self.apply_activation:\n            self.swish = Swish()\n\n    def forward(self, x):\n        x = self.down_conv(x)\n        if self.apply_norm:\n            x = self.bn(x)\n        if self.apply_activation:\n            x = self.swish(x)\n\n        return x\n"
  },
  {
    "path": "projects/HDINO/README.md",
    "content": "# H-DETR\n\n> [DETRs with Hybrid Matching](https://arxiv.org/abs/2207.13080)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nOne-to-one set matching is a key design for DETR to establish its end-to-end capability, so that object detection does not require a hand-crafted NMS (non-maximum suppression) to remove duplicate detections. This end-to-end signature is important for the versatility of DETR, and it has been generalized to broader vision tasks. However, we note that there are few queries assigned as positive samples and the one-to-one set matching significantly reduces the training efficacy of positive samples. We propose a simple yet effective method based on a hybrid matching scheme that combines the original one-to-one matching branch with an auxiliary one-to-many matching branch during training. Our hybrid strategy has been shown to significantly improve accuracy. In inference, only the original one-to-one match branch is used, thus maintaining the end-to-end merit and the same inference efficiency of DETR. The method is named H-DETR, and it shows that a wide range of representative DETR methods can be consistently improved across a wide range of visual tasks, including DeformableDETR, PETRv2, PETR, and TransTrack, among others.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/254f3037-1ca8-4d0c-8f3e-45d8ec3f9abc\"/>\n</div>\n\n## Results and Models\n\n| Backbone |     Model     | Lr schd | box AP |                     Config                     | Download |\n| :------: | :-----------: | :-----: | :----: | :--------------------------------------------: | :------: |\n|   R-50   | H-DINO-4scale |   12e   |  48.0  | [config](./h-dino-4scale_r50_8xb2-12e_coco.py) |          |\n\n### NOTE\n\n1. We are based on `DINO` rather than `Deformable DETR` to support the `Hybrid Matching` algorithm.\n2. We found that directly applying Hybrid Matching to the DINO algorithm results in a significant decrease in performance. If you have any other insights or suggestions, please feel free to comment or submit a pull request (PR).\n\n## Citation\n\n```latex\n@article{jia2022detrs,\n  title={DETRs with Hybrid Matching},\n  author={Jia, Ding and Yuan, Yuhui and He, Haodi and Wu, Xiaopei and Yu, Haojun and Lin, Weihong and Sun, Lei and Zhang, Chao and Hu, Han},\n  journal={arXiv preprint arXiv:2207.13080},\n  year={2022}\n}\n```\n"
  },
  {
    "path": "projects/HDINO/__init__.py",
    "content": "from .h_dino import HDINO\nfrom .h_dino_head import HybridDINOHead\n\n__all__ = ['HDINO', 'HybridDINOHead']\n"
  },
  {
    "path": "projects/HDINO/h-dino-4scale_r50_8xb2-12e_coco.py",
    "content": "_base_ = [\n    '../../configs/_base_/datasets/coco_detection.py',\n    '../../configs/_base_/default_runtime.py'\n]\n\ncustom_imports = dict(imports=['projects.HDINO'], allow_failed_imports=False)\n\nmodel = dict(\n    type='HDINO',\n    num_queries=1800,  # num_total_queries: 900+900\n    with_box_refine=True,\n    as_two_stage=True,\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=1),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    neck=dict(\n        type='ChannelMapper',\n        in_channels=[512, 1024, 2048],\n        kernel_size=1,\n        out_channels=256,\n        act_cfg=None,\n        norm_cfg=dict(type='GN', num_groups=32),\n        num_outs=4),\n    encoder=dict(\n        num_layers=6,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_levels=4,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0))),  # 0.1 for DeformDETR\n    decoder=dict(\n        num_layers=6,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=4,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None),\n    positional_encoding=dict(\n        num_feats=128,\n        normalize=True,\n        offset=0.0,  # -0.5 for DeformDETR\n        temperature=20),  # 10000 for DeformDETR\n    bbox_head=dict(\n        type='HybridDINOHead',\n        num_classes=80,\n        sync_cls_avg_factor=True,\n        num_query_one2one=900,\n        k_one2many=2,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),  # 2.0 in DeformDETR\n        loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n        loss_iou=dict(type='GIoULoss', loss_weight=2.0)),\n    dn_cfg=dict(\n        label_noise_scale=0.5,\n        box_noise_scale=1.0,  # 0.4 for DN-DETR\n        group_cfg=dict(dynamic=True, num_groups=None, num_dn_queries=100)),\n    # training and testing settings\n    train_cfg=dict(\n        assigner=dict(\n            type='HungarianAssigner',\n            match_costs=[\n                dict(type='FocalLossCost', weight=2.0),\n                dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                dict(type='IoUCost', iou_mode='giou', weight=2.0)\n            ])),\n    test_cfg=dict(max_per_img=300))  # 100 for DeformDETR\n\n# train_pipeline, NOTE the img_scale and the Pad's size_divisor is different\n# from the default setting in mmdet.\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomChoice',\n        transforms=[\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ],\n            [\n                dict(\n                    type='RandomChoiceResize',\n                    # The radio of all image in train dataset < 7\n                    # follow the original implement\n                    scales=[(400, 4200), (500, 4200), (600, 4200)],\n                    keep_ratio=True),\n                dict(\n                    type='RandomCrop',\n                    crop_type='absolute_range',\n                    crop_size=(384, 600),\n                    allow_negative_crop=True),\n                dict(\n                    type='RandomChoiceResize',\n                    scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),\n                            (608, 1333), (640, 1333), (672, 1333), (704, 1333),\n                            (736, 1333), (768, 1333), (800, 1333)],\n                    keep_ratio=True)\n            ]\n        ]),\n    dict(type='PackDetInputs')\n]\ntrain_dataloader = dict(\n    dataset=dict(\n        filter_cfg=dict(filter_empty_gt=False), pipeline=train_pipeline))\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,  # 0.0002 for DeformDETR\n        weight_decay=0.0001),\n    clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.1)})\n)  # custom_keys contains sampling_offsets and reference_points in DeformDETR  # noqa\n\n# learning policy\nmax_epochs = 12\ntrain_cfg = dict(\n    type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[11],\n        gamma=0.1)\n]\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (2 samples per GPU)\nauto_scale_lr = dict(base_batch_size=16)\n"
  },
  {
    "path": "projects/HDINO/h_dino.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Tuple\n\nimport torch\nfrom torch import Tensor, nn\nfrom torch.nn.init import normal_\n\nfrom mmdet.models.detectors import DINO, DeformableDETR\nfrom mmdet.models.detectors.deformable_detr import \\\n    MultiScaleDeformableAttention\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList\nfrom mmdet.utils import OptConfigType\n\n\n@MODELS.register_module()\nclass HDINO(DINO):\n\n    def __init__(self,\n                 *args,\n                 bbox_head: OptConfigType = None,\n                 **kwargs) -> None:\n        self.method = 0\n        self.num_query_one2one = bbox_head['num_query_one2one']\n        super(HDINO, self).__init__(*args, bbox_head=bbox_head, **kwargs)\n\n    def _init_layers(self) -> None:\n        super(HDINO, self)._init_layers()\n        self.query_embedding = None\n        if self.method == 1:\n            self.query_map = nn.Linear(self.embed_dims, self.embed_dims)\n        else:\n            self.pos_trans_fc = nn.Linear(self.embed_dims * 2, self.embed_dims)\n            self.pos_trans_norm = nn.LayerNorm(self.embed_dims)\n\n    def init_weights(self) -> None:\n        super(DeformableDETR, self).init_weights()\n        \"\"\"Initialize weights for Transformer and other components.\"\"\"\n        for coder in self.encoder, self.decoder:\n            for p in coder.parameters():\n                if p.dim() > 1:\n                    nn.init.xavier_uniform_(p)\n        for m in self.modules():\n            if isinstance(m, MultiScaleDeformableAttention):\n                m.init_weights()\n        nn.init.xavier_uniform_(self.memory_trans_fc.weight)\n        normal_(self.level_embed)\n\n        if self.method == 1:\n            nn.init.xavier_uniform_(self.query_map.weight)\n        else:\n            nn.init.xavier_uniform_(self.pos_trans_fc.weight)\n\n    def pre_decoder(\n        self,\n        memory: Tensor,\n        memory_mask: Tensor,\n        spatial_shapes: Tensor,\n        batch_data_samples: OptSampleList = None,\n    ) -> Tuple[dict, dict]:\n\n        bs, _, c = memory.shape\n        cls_out_features = self.bbox_head.cls_branches[\n            self.decoder.num_layers].out_features\n\n        output_memory, output_proposals = self.gen_encoder_output_proposals(\n            memory, memory_mask, spatial_shapes)\n        enc_outputs_class = self.bbox_head.cls_branches[\n            self.decoder.num_layers](\n                output_memory)\n        enc_outputs_coord_unact = self.bbox_head.reg_branches[\n            self.decoder.num_layers](output_memory) + output_proposals\n\n        # NOTE The DINO selects top-k proposals according to scores of\n        # multi-class classification, while DeformDETR, where the input\n        # is `enc_outputs_class[..., 0]` selects according to scores of\n        # binary classification.\n        topk_indices = torch.topk(\n            enc_outputs_class.max(-1)[0], k=self.num_queries, dim=1)[1]\n        topk_score = torch.gather(\n            enc_outputs_class, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, cls_out_features))\n        topk_coords_unact = torch.gather(\n            enc_outputs_coord_unact, 1,\n            topk_indices.unsqueeze(-1).repeat(1, 1, 4))\n        topk_coords = topk_coords_unact.sigmoid()\n        topk_coords_unact = topk_coords_unact.detach()\n\n        # We only made changes here.\n        # -------------------------------------\n        if self.method == 1:\n            map_memory = self.query_map(memory.detach())\n            query = torch.gather(\n                map_memory, 1,\n                topk_indices.unsqueeze(-1).repeat(1, 1, self.embed_dims))\n        else:\n            pos_trans_out = self.pos_trans_fc(\n                self.get_proposal_pos_embed(topk_coords_unact))\n            query = self.pos_trans_norm(pos_trans_out)\n        # -------------------------------------\n\n        if self.training:\n            dn_label_query, dn_bbox_query, dn_mask, dn_meta = \\\n                self.dn_query_generator(batch_data_samples)\n            query = torch.cat([dn_label_query, query], dim=1)\n            reference_points = torch.cat([dn_bbox_query, topk_coords_unact],\n                                         dim=1)\n        else:\n            reference_points = topk_coords_unact\n            dn_mask, dn_meta = None, None\n        reference_points = reference_points.sigmoid()\n\n        decoder_inputs_dict = dict(\n            query=query,\n            memory=memory,\n            reference_points=reference_points,\n            dn_mask=dn_mask)\n        # NOTE DINO calculates encoder losses on scores and coordinates\n        # of selected top-k encoder queries, while DeformDETR is of all\n        # encoder queries.\n        head_inputs_dict = dict(\n            enc_outputs_class=topk_score,\n            enc_outputs_coord=topk_coords,\n            dn_meta=dn_meta) if self.training else dict()\n\n        # We only made changes here.\n        # -------------------------------------\n        if self.training:\n            # train: num_denoising_queries + num_query_one2one\n            # + num_query_one2many\n            dn_mask = decoder_inputs_dict['dn_mask']\n            num_denoising_queries = head_inputs_dict['dn_meta'][\n                'num_denoising_queries']\n            num_query_one2one = num_denoising_queries + self.num_query_one2one\n            # dn_mask[num_query_one2one:, :num_query_one2one] = True\n            dn_mask[num_denoising_queries:num_query_one2one,\n                    num_query_one2one:] = True\n            decoder_inputs_dict['dn_mask'] = dn_mask\n        else:\n            # test: num_query_one2one\n            # + num_query_one2many\n            query = decoder_inputs_dict['query']\n            reference_points = decoder_inputs_dict['reference_points']\n            num_query_one2many = self.num_queries - self.num_query_one2one\n            decoder_inputs_dict['query'] = query[:num_query_one2many]\n            decoder_inputs_dict[\n                'reference_points'] = reference_points[:num_query_one2many]\n        # -------------------------------------\n        return decoder_inputs_dict, head_inputs_dict\n"
  },
  {
    "path": "projects/HDINO/h_dino_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import Dict, List\n\nfrom torch import Tensor\n\nfrom mmdet.models.dense_heads.dino_head import DINOHead\nfrom mmdet.models.utils import multi_apply\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import InstanceList, OptInstanceList\n\n\n@MODELS.register_module()\nclass HybridDINOHead(DINOHead):\n    \"\"\"Head of the Hybrid Matching.\"\"\"\n\n    def __init__(self,\n                 *args,\n                 num_query_one2one: int = 900,\n                 k_one2many: int = 2,\n                 **kwargs) -> None:\n        self.num_query_one2one = num_query_one2one\n        self.k_one2many = k_one2many\n        super().__init__(*args, **kwargs)\n\n    def loss_by_feat(\n        self,\n        all_layers_cls_scores: Tensor,\n        all_layers_bbox_preds: Tensor,\n        enc_cls_scores: Tensor,\n        enc_bbox_preds: Tensor,\n        batch_gt_instances: InstanceList,\n        batch_img_metas: List[dict],\n        dn_meta: Dict[str, int],\n        batch_gt_instances_ignore: OptInstanceList = None\n    ) -> Dict[str, Tensor]:\n        \"\"\"Loss function.\n\n        Args:\n            all_layers_cls_scores (Tensor): Classification scores of all\n                decoder layers, has shape (num_decoder_layers, bs,\n                num_queries_total, cls_out_channels), where\n                `num_queries_total` is the sum of `num_denoising_queries`\n                and `num_matching_queries`.\n            all_layers_bbox_preds (Tensor): Regression outputs of all decoder\n                layers. Each is a 4D-tensor with normalized coordinate format\n                (cx, cy, w, h) and has shape (num_decoder_layers, bs,\n                num_queries_total, 4).\n            enc_cls_scores (Tensor): The score of each point on encode\n                feature map, has shape (bs, num_feat_points, cls_out_channels).\n            enc_bbox_preds (Tensor): The proposal generate from the encode\n                feature map, has shape (bs, num_feat_points, 4) with the last\n                dimension arranged as (cx, cy, w, h).\n            batch_gt_instances (list[:obj:`InstanceData`]): Batch of\n                gt_instance. It usually includes ``bboxes`` and ``labels``\n                attributes.\n            batch_img_metas (list[dict]): Meta information of each image, e.g.,\n                image size, scaling factor, etc.\n            dn_meta (Dict[str, int]): The dictionary saves information about\n                group collation, including 'num_denoising_queries' and\n                'num_denoising_groups'. It will be used for split outputs of\n                denoising and matching parts and loss calculation.\n            batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):\n                Batch of gt_instances_ignore. It includes ``bboxes`` attribute\n                data that is ignored during training and testing.\n                Defaults to None.\n\n        Returns:\n            dict[str, Tensor]: A dictionary of loss components.\n        \"\"\"\n        # train: num_denoising_queries + num_query_one2one\n        # + num_query_one2many\n        num_query_one2one = dn_meta[\n            'num_denoising_queries'] + self.num_query_one2one\n        outputs_classes_one2one = \\\n            all_layers_cls_scores[:, :, 0: num_query_one2one, :]\n        outputs_coords_one2one = \\\n            all_layers_bbox_preds[:, :, 0: num_query_one2one, :]\n        # hybrid-matching part\n        outputs_classes_one2many = \\\n            all_layers_cls_scores[:, :, num_query_one2one:, :]\n        outputs_coords_one2many = \\\n            all_layers_bbox_preds[:, :, num_query_one2one:, :]\n\n        loss_dict = super(HybridDINOHead, self).loss_by_feat(\n            outputs_classes_one2one, outputs_coords_one2one, enc_cls_scores,\n            enc_bbox_preds, batch_gt_instances, batch_img_metas, dn_meta,\n            batch_gt_instances_ignore)\n\n        o2m_batch_gt_instances = []\n        for gt_instance in batch_gt_instances:\n            bboxes = gt_instance.bboxes.repeat(self.k_one2many, 1)\n            labels = gt_instance.labels.repeat(self.k_one2many)\n            new_gt_instance = gt_instance.new(bboxes=bboxes, labels=labels)\n            o2m_batch_gt_instances.append(new_gt_instance)\n\n        losses_cls_o2m, losses_bbox_o2m, losses_iou_o2m = multi_apply(\n            self.loss_by_feat_single,\n            outputs_classes_one2many,\n            outputs_coords_one2many,\n            batch_gt_instances=o2m_batch_gt_instances,\n            batch_img_metas=batch_img_metas)\n\n        loss_dict['loss_cls_o2m'] = losses_cls_o2m[-1]\n        loss_dict['loss_bbox_o2m'] = losses_bbox_o2m[-1]\n        loss_dict['loss_iou_o2m'] = losses_iou_o2m[-1]\n        for num_dec_layer, (loss_cls_i, loss_bbox_i, loss_iou_i) in \\\n                enumerate(zip(losses_cls_o2m[:-1], losses_bbox_o2m[:-1],\n                              losses_iou_o2m[:-1])):\n            loss_dict[f'd{num_dec_layer}.loss_cls_o2m'] = loss_cls_i\n            loss_dict[f'd{num_dec_layer}.loss_bbox_o2m'] = loss_bbox_i\n            loss_dict[f'd{num_dec_layer}.loss_iou_o2m'] = loss_iou_i\n        return loss_dict\n"
  },
  {
    "path": "projects/LabelStudio/backend_template/_wsgi.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport json\nimport logging\nimport logging.config\nimport os\n\nlogging.config.dictConfig({\n    'version': 1,\n    'formatters': {\n        'standard': {\n            'format':\n            '[%(asctime)s] [%(levelname)s] [%(name)s::%(funcName)s::%(lineno)d] %(message)s'  # noqa E501\n        }\n    },\n    'handlers': {\n        'console': {\n            'class': 'logging.StreamHandler',\n            'level': 'DEBUG',\n            'stream': 'ext://sys.stdout',\n            'formatter': 'standard'\n        }\n    },\n    'root': {\n        'level': 'ERROR',\n        'handlers': ['console'],\n        'propagate': True\n    }\n})\n\n_DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'config.json')\n\n\ndef get_kwargs_from_config(config_path=_DEFAULT_CONFIG_PATH):\n    if not os.path.exists(config_path):\n        return dict()\n    with open(config_path) as f:\n        config = json.load(f)\n    assert isinstance(config, dict)\n    return config\n\n\nif __name__ == '__main__':\n\n    from label_studio_ml.api import init_app\n\n    from projects.LabelStudio.backend_template.mmdetection import MMDetection\n\n    parser = argparse.ArgumentParser(description='Label studio')\n    parser.add_argument(\n        '-p',\n        '--port',\n        dest='port',\n        type=int,\n        default=9090,\n        help='Server port')\n    parser.add_argument(\n        '--host', dest='host', type=str, default='0.0.0.0', help='Server host')\n    parser.add_argument(\n        '--kwargs',\n        '--with',\n        dest='kwargs',\n        metavar='KEY=VAL',\n        nargs='+',\n        type=lambda kv: kv.split('='),\n        help='Additional LabelStudioMLBase model initialization kwargs')\n    parser.add_argument(\n        '-d',\n        '--debug',\n        dest='debug',\n        action='store_true',\n        help='Switch debug mode')\n    parser.add_argument(\n        '--log-level',\n        dest='log_level',\n        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],\n        default=None,\n        help='Logging level')\n    parser.add_argument(\n        '--model-dir',\n        dest='model_dir',\n        default=os.path.dirname(__file__),\n        help='Directory models are store',\n    )\n    parser.add_argument(\n        '--check',\n        dest='check',\n        action='store_true',\n        help='Validate model instance before launching server')\n\n    args = parser.parse_args()\n\n    # setup logging level\n    if args.log_level:\n        logging.root.setLevel(args.log_level)\n\n    def isfloat(value):\n        try:\n            float(value)\n            return True\n        except ValueError:\n            return False\n\n    def parse_kwargs():\n        param = dict()\n        for k, v in args.kwargs:\n            if v.isdigit():\n                param[k] = int(v)\n            elif v == 'True' or v == 'true':\n                param[k] = True\n            elif v == 'False' or v == 'False':\n                param[k] = False\n            elif isfloat(v):\n                param[k] = float(v)\n            else:\n                param[k] = v\n        return param\n\n    kwargs = get_kwargs_from_config()\n\n    if args.kwargs:\n        kwargs.update(parse_kwargs())\n\n    if args.check:\n        print('Check \"' + MMDetection.__name__ + '\" instance creation..')\n        model = MMDetection(**kwargs)\n\n    app = init_app(\n        model_class=MMDetection,\n        model_dir=os.environ.get('MODEL_DIR', args.model_dir),\n        redis_queue=os.environ.get('RQ_QUEUE_NAME', 'default'),\n        redis_host=os.environ.get('REDIS_HOST', 'localhost'),\n        redis_port=os.environ.get('REDIS_PORT', 6379),\n        **kwargs)\n\n    app.run(host=args.host, port=args.port, debug=args.debug)\n\nelse:\n    # for uWSGI use\n    app = init_app(\n        model_class=MMDetection,\n        model_dir=os.environ.get('MODEL_DIR', os.path.dirname(__file__)),\n        redis_queue=os.environ.get('RQ_QUEUE_NAME', 'default'),\n        redis_host=os.environ.get('REDIS_HOST', 'localhost'),\n        redis_port=os.environ.get('REDIS_PORT', 6379))\n"
  },
  {
    "path": "projects/LabelStudio/backend_template/mmdetection.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport io\nimport json\nimport logging\nimport os\nfrom urllib.parse import urlparse\n\nimport boto3\nfrom botocore.exceptions import ClientError\nfrom label_studio_ml.model import LabelStudioMLBase\nfrom label_studio_ml.utils import (DATA_UNDEFINED_NAME, get_image_size,\n                                   get_single_tag_keys)\nfrom label_studio_tools.core.utils.io import get_data_dir\n\nfrom mmdet.apis import inference_detector, init_detector\n\nlogger = logging.getLogger(__name__)\n\n\nclass MMDetection(LabelStudioMLBase):\n    \"\"\"Object detector based on https://github.com/open-mmlab/mmdetection.\"\"\"\n\n    def __init__(self,\n                 config_file=None,\n                 checkpoint_file=None,\n                 image_dir=None,\n                 labels_file=None,\n                 score_threshold=0.5,\n                 device='cpu',\n                 **kwargs):\n\n        super(MMDetection, self).__init__(**kwargs)\n        config_file = config_file or os.environ['config_file']\n        checkpoint_file = checkpoint_file or os.environ['checkpoint_file']\n        self.config_file = config_file\n        self.checkpoint_file = checkpoint_file\n        self.labels_file = labels_file\n        # default Label Studio image upload folder\n        upload_dir = os.path.join(get_data_dir(), 'media', 'upload')\n        self.image_dir = image_dir or upload_dir\n        logger.debug(\n            f'{self.__class__.__name__} reads images from {self.image_dir}')\n        if self.labels_file and os.path.exists(self.labels_file):\n            self.label_map = json_load(self.labels_file)\n        else:\n            self.label_map = {}\n\n        self.from_name, self.to_name, self.value, self.labels_in_config = get_single_tag_keys(  # noqa E501\n            self.parsed_label_config, 'RectangleLabels', 'Image')\n        schema = list(self.parsed_label_config.values())[0]\n        self.labels_in_config = set(self.labels_in_config)\n\n        # Collect label maps from `predicted_values=\"airplane,car\"` attribute in <Label> tag # noqa E501\n        self.labels_attrs = schema.get('labels_attrs')\n        if self.labels_attrs:\n            for label_name, label_attrs in self.labels_attrs.items():\n                for predicted_value in label_attrs.get('predicted_values',\n                                                       '').split(','):\n                    self.label_map[predicted_value] = label_name\n\n        print('Load new model from: ', config_file, checkpoint_file)\n        self.model = init_detector(config_file, checkpoint_file, device=device)\n        self.score_thresh = score_threshold\n\n    def _get_image_url(self, task):\n        image_url = task['data'].get(\n            self.value) or task['data'].get(DATA_UNDEFINED_NAME)\n        if image_url.startswith('s3://'):\n            # presign s3 url\n            r = urlparse(image_url, allow_fragments=False)\n            bucket_name = r.netloc\n            key = r.path.lstrip('/')\n            client = boto3.client('s3')\n            try:\n                image_url = client.generate_presigned_url(\n                    ClientMethod='get_object',\n                    Params={\n                        'Bucket': bucket_name,\n                        'Key': key\n                    })\n            except ClientError as exc:\n                logger.warning(\n                    f'Can\\'t generate presigned URL for {image_url}. Reason: {exc}'  # noqa E501\n                )\n        return image_url\n\n    def predict(self, tasks, **kwargs):\n        assert len(tasks) == 1\n        task = tasks[0]\n        image_url = self._get_image_url(task)\n        image_path = self.get_local_path(image_url)\n        model_results = inference_detector(self.model,\n                                           image_path).pred_instances\n        results = []\n        all_scores = []\n        img_width, img_height = get_image_size(image_path)\n        print(f'>>> model_results: {model_results}')\n        print(f'>>> label_map {self.label_map}')\n        print(f'>>> self.model.dataset_meta: {self.model.dataset_meta}')\n        classes = self.model.dataset_meta.get('classes')\n        print(f'Classes >>> {classes}')\n        for item in model_results:\n            print(f'item >>>>> {item}')\n            bboxes, label, scores = item['bboxes'], item['labels'], item[\n                'scores']\n            score = float(scores[-1])\n            if score < self.score_thresh:\n                continue\n            print(f'bboxes >>>>> {bboxes}')\n            print(f'label >>>>> {label}')\n            output_label = classes[list(self.label_map.get(label, label))[0]]\n            print(f'>>> output_label: {output_label}')\n            if output_label not in self.labels_in_config:\n                print(output_label + ' label not found in project config.')\n                continue\n\n            for bbox in bboxes:\n                bbox = list(bbox)\n                if not bbox:\n                    continue\n\n                x, y, xmax, ymax = bbox[:4]\n                results.append({\n                    'from_name': self.from_name,\n                    'to_name': self.to_name,\n                    'type': 'rectanglelabels',\n                    'value': {\n                        'rectanglelabels': [output_label],\n                        'x': float(x) / img_width * 100,\n                        'y': float(y) / img_height * 100,\n                        'width': (float(xmax) - float(x)) / img_width * 100,\n                        'height': (float(ymax) - float(y)) / img_height * 100\n                    },\n                    'score': score\n                })\n                all_scores.append(score)\n        avg_score = sum(all_scores) / max(len(all_scores), 1)\n        print(f'>>> RESULTS: {results}')\n        return [{'result': results, 'score': avg_score}]\n\n\ndef json_load(file, int_keys=False):\n    with io.open(file, encoding='utf8') as f:\n        data = json.load(f)\n        if int_keys:\n            return {int(k): v for k, v in data.items()}\n        else:\n            return data\n"
  },
  {
    "path": "projects/LabelStudio/readme.md",
    "content": "# Semi-automatic Object Detection Annotation with MMDetection and Label-Studio\n\nPlease read the [Docs](../../docs/en/user_guides/label_studio.md) for more details.\n"
  },
  {
    "path": "projects/RF100-Benchmark/README.md",
    "content": "# Roboflow 100 Benchmark\n\n> [Roboflow 100: A Rich, Multi-Domain Object Detection Benchmark](https://arxiv.org/abs/2211.13523v3)\n\n<!-- [Dataset] -->\n\n## Abstract\n\nThe evaluation of object detection models is usually performed by optimizing a single metric, e.g. mAP, on a fixed set of datasets, e.g. Microsoft COCO and Pascal VOC. Due to image retrieval and annotation costs, these datasets consist largely of images found on the web and do not represent many real-life domains that are being modelled in practice, e.g. satellite, microscopic and gaming, making it difficult to assert the degree of generalization learned by the model. We introduce the Roboflow-100 (RF100) consisting of 100 datasets, 7 imagery domains, 224,714 images, and 805 class labels with over 11,170 labelling hours. We derived RF100 from over 90,000 public datasets, 60 million public images that are actively being assembled and labelled by computer vision practitioners in the open on the web application Roboflow Universe. By releasing RF100, we aim to provide a semantically diverse, multi-domain benchmark of datasets to help researchers test their model's generalizability with real-life data. RF100 download and benchmark replication are available on GitHub.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/71b0eb6f-d710-4100-9fb1-9d5485e07fdb\"/>\n</div>\n\n## Code Structure\n\n```text\n# current path is projects/RF100-Benchmark/\n├── configs\n│         ├── dino_r50_fpn_ms_8xb8_tweeter-profile.py\n│         ├── faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py\n│         └── tood_r50_fpn_ms_8xb8_tweeter-profile.py\n├── README.md\n├── README_zh-CN.md\n├── rf100\n└── scripts\n    ├── create_new_config.py # Based on the provided configuration, generate the training configuration of the remaining 99 datasets\n    ├── datasets_links_640.txt # Dataset download link, from the official repo\n    ├── download_dataset.py # Dataset download code, from the official repo\n    ├── download_datasets.sh # Dataset download script, from the official repo\n    ├── labels_names.json # Dataset information, from the official repo, but there are some errors so we modified it\n    ├── parse_dataset_link.py # from the official repo\n    ├── log_extract.py # Results collection and collation of training\n    └── dist_train.sh # Training and evaluation startup script\n    └── slurm_train.sh # Slurm Training and evaluation startup script\n```\n\n## Dataset Preparation\n\nRoboflow 100 dataset is hosted by Roboflow platform, and detailed download scripts are provided in the [roboflow-100-benchmark](https://github.com/roboflow/roboflow-100-benchmark) repository. For simplicity, we use the official download script directly.\n\nBefore downloading the data, you need to register an account on the Roboflow platform to get the API key.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/6126e69e-85ce-4dec-8e7b-936c4fae29a6\"/>\n</div>\n\n```shell\nexport ROBOFLOW_API_KEY = Your Private API Key\n```\n\nAt the same time, you should also install the Roboflow package.\n\n```shell\npip install roboflow\n```\n\nFinally, use the following command to download the dataset.\n\n```shell\ncd projects/RF100-Benchmark/\nbash scripts/download_datasets.sh\n```\n\nDownload the dataset, and a `rf100` folder will be generated in the current directory `projects/RF100-Benchmark/`, which contains all the datasets. The structure is as follows:\n\n```text\n# current path is projects/RF100-Benchmark/\n├── README.md\n├── README_zh-CN.md\n└── scripts\n    ├── datasets_links_640.txt\n├── rf100\n│    └── tweeter-profile\n│    │    ├── train\n|    |    |    ├── 0b3la49zec231_jpg.rf.8913f1b7db315c31d09b1d2f583fb521.jpg\n|    |    |    ├──_annotations.coco.json\n│    │    ├── valid\n|    |    |    ├── 0fcjw3hbfdy41_jpg.rf.d61585a742f6e9d1a46645389b0073ff.jpg\n|    |    |    ├──_annotations.coco.json\n│    │    ├── test\n|    |    |    ├── 0dh0to01eum41_jpg.rf.dcca24808bb396cdc07eda27a2cea2d4.jpg\n|    |    |    ├──_annotations.coco.json\n│    │    ├── README.dataset.txt\n│    │    ├── README.roboflow.txt\n│    └── 4-fold-defect\n...\n```\n\nThe dataset takes up a total of 12.3G of storage space. If you don't want to train and evaluate all models at once, you can modify the `scripts/datasets_links_640.txt` file and delete the links to the datasets you don't want to use.\n\nRoboflow 100 dataset features are shown in the following figure\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/e2693662-3d16-49a4-af0b-2a03be7e16b6\"/>\n</div>\n\nIf you want to have a clear understanding of the dataset, you can check the [roboflow-100-benchmark](https://github.com/roboflow/roboflow-100-benchmark) repository, which provides many dataset analysis scripts.\n\n## Model Training and Evaluation\n\nIf you want to train and evaluate all models at once, you can use the following command.\n\n1. Single GPU Training\n\n```shell\n# current path is projects/RF100-Benchmark/\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 1\n# Specify the save path\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 1 my_work_dirs\n```\n\n2. Distributed Multi-GPU Training\n\n```shell\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8\n# Specify the save path\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8 my_work_dirs\n```\n\n3. Slurm Training\n\n```shell\nbash scripts/slurm_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8\n# Specify the save path\nbash scripts/slurm_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8 my_work_dirs\n```\n\nAfter training, a `work_dirs` folder will be generated in the current directory, which contains the trained model weights and logs.\n\n1. For the convenience of users to debug or only want to train specific datasets, we provide the `DEBUG` variable in `scripts/*_train.sh`, you only need to set it to 1, and specify the datasets you want to train in the `datasets_list` variable.\n2. Considering that for various reasons, users may encounter training failures for certain datasets during the training process, we provide the `RETRY_PATH` variable, you only need to pass in the txt dataset list file, and the program will read the dataset in the file, and then only train specific datasets. If not provided, it is training the full dataset.\n\n```shell\nRETRY_PATH=failed_dataset_list.txt bash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8 my_work_dirs\n```\n\nThe txt represents a dataset name on each line, as shown below (the blank line in the 4th line is indispensable):\n\n```text\nacl-x-ray\ntweeter-profile\nabdomen-mri\n\n```\n\nThe txt file can also be generated using the `log_extract.py` script introduced later, without manually creating it.\n\n## Model Summary\n\nIf you want to collect the results after the model is trained or during the training, you can execute the `log_extract.py` script, which will collect the information under `work_dirs` and output it in csv and xlsx format.\n\nBefore running the script, please make sure that `pandas` and `openpyxl` are installed\n\n```shell\npython scripts/log_extract.py faster_rcnn --epoch 25 --work-dirs my_work_dirs\n```\n\n- The first input parameter is used to generate the csv title, so you can enter any string, but it is recommended to enter the model name for easy viewing later.\n- `--epoch` parameter refers to the number of model training epochs, which is used to parse the log. By default, we train 100 epochs for each dataset, but RepeatDataset is used in the configuration, so the actual training epoch is 25.\n- `--work-dirs` is the working path where you save the trained model. The default is the `work_dirs` folder under the current path.\n\nAfter running, the following three new files will be generated in `my_work_dirs`\n\n```text\ntimestamp_detail.xlsx # Detailed information on the sorting of 100 datasets.\ntimestamp_sum.xlsx # Summary information of 100 datasets.\ntimestamp_eval.csv # Evaluation results of 100 datasets in the order of training.\nfailed_dataset_list.txt\n```\n\nCurrently, we provide the evaluation results of the Faster RCNN, TOOD and DINO algorithms (no careful parameter tuning). You can also quickly evaluate your own model according to the above process.\n\n## Result Analysis\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/86420903-36a8-410d-9251-4304b9704f7d\"/>\n</div>\n\n💎 The detailed table can be accessed directly [here](https://aicarrier.feishu.cn/drive/folder/QJ4rfqLzylIVTjdjYo3cunbinMh) 💎\n\nTo ensure a fair comparison and no special parameter tuning, the `Faster RCNN, TOOD and DINO` algorithms use the same epoch and data augmentation strategy, and all load the COCO pre-training weights, and save the best model performance on the validation set during training. Other instructions are as follows:\n\n- To speed up the training speed, all models are trained on 8-card GPUs. Except that the DINO algorithm trains OOM on some datasets, all other models and datasets are trained on 8 3090s\n- Because the GT boxes of the single image of the 5 datasets 'bacteria-ptywi', 'circuit-elements', 'marbles', 'printed-circuit-board', 'solar-panels-taxvb' are very large, which makes DINO unable to train on 3090, so we train these 5 datasets on A100\n\nFrom the above figure, the performance of the `DINO` algorithm is better than that of traditional CNN detection algorithms such as `Faster RCNN and TOOD`, which shows that the Transformer algorithm is also better than traditional CNN detection algorithms in different fields or different data volumes. However, if a certain field is analyzed separately, it may not be the case.\n\nRoboflow 100 datasets also have defects:\n\n- Some datasets have very few training images. If you want to benchmark with the same hyperparameters, it may cause poor performance\n- Some datasets in some fields have very small and many objects. `Faster RCNN, TOOD and DINO` have very poor results without specific parameter tuning. For this situation, users can ignore the results of these datasets\n- Some datasets have too casual annotations, which may result in poor performance if you want to apply them to image-text detection models\n\nFinally, it needs to be explained:\n\n1. Since there are a lot of 100 datasets, we cannot check each dataset, so if there is anything unreasonable, please feedback, we will fix it as soon as possible.\n2. We also provide various scale summary results such as mAP_s, but because some data does not exist this scale bounding box, we ignore these datasets when summarizing.\n\n## Custom Algorithm Benchmark\n\nIf users want to benchmark different algorithms for Roboflow 100, you only need to add algorithm configurations in the `projects/RF100-Benchmark/configs` folder.\n\nNote: Since the internal running process is to replace the string in the user-provided configuration with the function of custom dataset, the configuration provided by the user must be the `tweeter-profile` dataset and must include the `data_root` and `class_name` variables, otherwise the program will report an error.\n\n## Citation\n\n```BibTeX\n@misc{2211.13523,\nAuthor = {Floriana Ciaglia and Francesco Saverio Zuppichini and Paul Guerrie and Mark McQuade and Jacob Solawetz},\nTitle = {Roboflow 100: A Rich, Multi-Domain Object Detection Benchmark},\nYear = {2022},\nEprint = {arXiv:2211.13523},\n}\n```\n"
  },
  {
    "path": "projects/RF100-Benchmark/README_zh-CN.md",
    "content": "# Roboflow 100 Benchmark\n\n> [Roboflow 100: A Rich, Multi-Domain Object Detection Benchmark](https://arxiv.org/abs/2211.13523v3)\n\n<!-- [Dataset] -->\n\n## 摘要\n\n目标检测模型的评估通常通过在一组固定的数据集上优化单一指标（例如 mAP），例如 Microsoft COCO 和 Pascal VOC。由于图像检索和注释成本高昂，这些数据集主要由在网络上找到的图像组成，并不能代表实际建模的许多现实领域，例如卫星、显微和游戏等，这使得很难确定模型学到的泛化程度。我们介绍了 Roboflow-100（RF100），它包括 100 个数据集、7 个图像领域、224,714 张图像和 805 个类别标签，超过 11,170 个标注小时。我们从超过 90,000 个公共数据集、6000 万个公共图像中提取了 RF100，这些数据集正在由计算机视觉从业者在网络应用程序 Roboflow Universe 上积极组装和标注。通过发布 RF100，我们旨在提供一个语义多样、多领域的数据集基准，帮助研究人员用真实数据测试模型的泛化能力。\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/71b0eb6f-d710-4100-9fb1-9d5485e07fdb\"/>\n</div>\n\n## 代码结构说明\n\n```text\n# 当前文件路径为 projects/RF100-Benchmark/\n├── configs # 配置文件\n│         ├── dino_r50_fpn_ms_8xb8_tweeter-profile.py\n│         ├── faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py\n│         └── tood_r50_fpn_ms_8xb8_tweeter-profile.py\n├── README.md\n├── README_zh-CN.md\n├── rf100\n└── scripts\n    ├── create_new_config.py # 基于上述提供的配置生成其余 99 个数据集训练配置\n    ├── datasets_links_640.txt # 数据集下载链接，来自官方 repo\n    ├── download_dataset.py # 数据集下载代码，来自官方 repo\n    ├── download_datasets.sh # 数据集下载脚本，来自官方 repo\n    ├── labels_names.json # 数据集信息，来自官方 repo,不过由于有一些错误因此我们进行了修改\n    ├── parse_dataset_link.py # 下载数据集需要，来自官方 repo\n    ├── log_extract.py # 对训练的结果进行收集和整理\n    └── dist_train.sh # 训练和评估启动脚本\n    └── slurm_train.sh # slurm 训练和评估启动脚本\n```\n\n## 数据集准备\n\nRoboflow 100 数据集是由 Roboflow 平台托管，并且在 [roboflow-100-benchmark](https://github.com/roboflow/roboflow-100-benchmark) 仓库中提供了详细的下载脚本。为了简单，我们直接使用官方提供的下载脚本。\n\n在下载数据前，你首先需要在 Roboflow 平台注册账号，获取 API key。\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/6126e69e-85ce-4dec-8e7b-936c4fae29a6\"/>\n</div>\n\n```shell\nexport ROBOFLOW_API_KEY = 你的 Private API Key\n```\n\n同时你也应该安装 Roboflow 包。\n\n```shell\npip install roboflow\n```\n\n最后使用如下命令下载数据集即可。\n\n```shell\ncd projects/RF100-Benchmark/\nbash scripts/download_datasets.sh\n```\n\n下载完成后，会在当前目录下 `projects/RF100-Benchmark/` 生成 `rf100` 文件夹，其中包含了所有的数据集。其结构如下所示：\n\n```text\n# 当前文件路径为 projects/RF100-Benchmark/\n├── README.md\n├── README_zh-CN.md\n└── scripts\n    ├── datasets_links_640.txt\n├── rf100\n│    └── tweeter-profile\n│    │    ├── train\n|    |    |    ├── 0b3la49zec231_jpg.rf.8913f1b7db315c31d09b1d2f583fb521.jpg\n|    |    |    ├──_annotations.coco.json\n│    │    ├── valid\n|    |    |    ├── 0fcjw3hbfdy41_jpg.rf.d61585a742f6e9d1a46645389b0073ff.jpg\n|    |    |    ├──_annotations.coco.json\n│    │    ├── test\n|    |    |    ├── 0dh0to01eum41_jpg.rf.dcca24808bb396cdc07eda27a2cea2d4.jpg\n|    |    |    ├──_annotations.coco.json\n│    │    ├── README.dataset.txt\n│    │    ├── README.roboflow.txt\n│    └── 4-fold-defect\n...\n```\n\n整个数据集一共需要 12.3G 存储空间。如果你不想一次性训练和评估所有模型，你可以修改 `scripts/datasets_links_640.txt` 文件，将你不想使用的数据集链接删掉即可。\n\nRoboflow 100 数据集的特点如下图所示\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/e2693662-3d16-49a4-af0b-2a03be7e16b6\"/>\n</div>\n\n如果想对数据集有个清晰的认识，可以查看 [roboflow-100-benchmark](https://github.com/roboflow/roboflow-100-benchmark) 仓库，其提供了诸多数据集分析脚本。\n\n## 模型训练和评估\n\n在准备好数据集后，可以一键开启单卡或者多卡训练。以 `faster-rcnn_r50_fpn` 算法为例\n\n1. 单卡训练\n\n```shell\n# 当前位于 projects/RF100-Benchmark/\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 1\n# 如果想指定保存路径\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 1 my_work_dirs\n```\n\n2. 分布式多卡训练\n\n```shell\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8\n# 如果想指定保存路径\nbash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8 my_work_dirs\n```\n\n3. Slurm 训练\n\n```shell\nbash scripts/slurm_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8\n# 如果想指定保存路径\nbash scripts/slurm_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8 my_work_dirs\n```\n\n训练完成后会在当前路径下生成 `work_dirs` 文件夹，其中包含了训练好的模型权重和日志。\n\n1. 为了方便用户调试或者只想训练特定的数据集，在 `scripts/*_train.sh` 中我们提供了 `DEBUG` 变量，你只需要设置为 1，并且在 `datasets_list` 变量中指定你想训练的数据集即可。\n2. 考虑到由于各种原因，用户训练过程中可能出现某些数据集训练失败，因此我们提供了 `RETRY_PATH` 变量，你只需要传入 txt 数据集列表文件即可，程序会读取该文件中的数据集，然后只训练特定数据集。如果不提供则为全量数据集训练。\n\n```shell\nRETRY_PATH=failed_dataset_list.txt bash scripts/dist_train.sh configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py 8 my_work_dirs\n```\n\ntxt 文件中每一行代表一个数据集名称，示例如下(第 4 行的空行不可少)：\n\n```text\nacl-x-ray\ntweeter-profile\nabdomen-mri\n\n```\n\n上述 txt 文件你也可以采用后续介绍的 `log_extract.py` 脚本生成，而无需手动创建。\n\n## 模型汇总\n\n在模型训练好或者在训练中途你想对结果进行收集，你可以执行 `log_extract.py` 脚本，该脚本会将 `work_dirs` 下的信息收集并输出为 csv 和 xlsx 格式。\n\n在运行脚本前，请确保安装了 `pandas` 和 `openpyxl`\n\n```shell\npython scripts/log_extract.py faster_rcnn --epoch 25 --work-dirs my_work_dirs\n```\n\n- 第一个输入参数是用于生成 csv 标题，因此你可以输入任意字符串，但是建议输入模型名称，方便后续查看。\n- `--epoch` 参数是指模型训练 epoch 数，用于解析 log，默认我们是对每个数据集训练 100 epoch，但是配置中采用了 `RepeatDataset`，因此实际训练 epoch 是 25\n- `--work-dirs` 是你训练模型保存的工作路径，默认是当前路径下的 `work_dirs` 文件夹\n\n运行后会在 `my_work_dirs` 里面生成如下三个新文件\n\n```text\n时间戳_detail.xlsx # 100 个数据集的排序后详细信息\n时间戳_sum.xlsx # 100 个数据集的汇总信息\n时间戳_eval.csv # 100 个数据集的按照训练顺序评估结果\nfailed_dataset_list.txt # 失败数据集列表\n```\n\n目前我们提供了 `Faster RCNN、TOOD 和 DINO` 算法的评估结果(并没有进行精心的调参)。你也可以按照上述流程对自己的模型进行快速评估。\n\n## 结果分析\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/86420903-36a8-410d-9251-4304b9704f7d\"/>\n</div>\n\n💎 详情表，请直接访问 [结果](https://aicarrier.feishu.cn/drive/folder/QJ4rfqLzylIVTjdjYo3cunbinMh) 💎\n\n为了确保对比公平且不存在特别的调参，`Faster RCNN、TOOD 和 DINO` 算法采用了相同的 epoch 和数据增强策略，并且都加载了 COCO 预训练权重，同时在训练中保存了验证集上性能最好的模型。其他说明如下所示：\n\n- 为了加快训练速度，所有模型都是在 8 卡 GPU 上面训练。除了 DINO 算法在部分数据集上训练 OOM 外，其余所有模型和数据集都是在 8 张 3090 上训练\n- 由于 'bacteria-ptywi', 'circuit-elements', 'marbles', 'printed-circuit-board', 'solar-panels-taxvb' 这 5 个数据集单张图片的 GT 框非常多导致 DINO 在 3090 上无法训练，因此这 5 个数据集我们在 A100 上进行训练\n\n从上图来看，`DINO` 算法性能好于 `Faster RCNN 和 TOOD` 等传统 CNN 检测算法，说明 Transformer 算法在不同的领域或者不同数据量的情况下效果也是好于传统 CNN 类检测算法的，不过如果单独分析某些领域则不一定。\n\nRoboflow 100 数据集本身也存在缺陷：\n\n- 有些数据集训练图片数非常少，如果要统一超参进行 benchmark，可能会导致其性能很差\n- 有些领域的部分数据集物体非常小且多，`Faster RCNN、TOOD 和 DINO` 在不进行特定调参情况下效果都非常差。针对这种情况，用户可以忽略这些数据集的结果\n- 有些数据集标注的类别过于随意，如果想应用于图文检测类模型，则可能会存在性能低下的现象\n\n最后需要说明：\n\n1. 由于 100 个数据集比较多，我们无法对每个数据集进行检查，因此如果有不合理的地方，欢迎反馈，我们将尽快修复\n2. 我们也提供了 `mAP_s` 等各种尺度的汇总结果，但是由于部分数据不存在这个尺度边界框，因为汇总时候我们忽略这些数据集\n\n## 自定义算法进行 benchmark\n\n如果用户想针对不同算法进行 Roboflow 100 Benchmark，你只需要在 `projects/RF100-Benchmark/configs` 文件夹新增算法配置即可。\n\n注意：由于内部运行过程是通过将用户提供的配置中是以字符串替换的方式实现自定义数据集的功能，因此用户提供的配置必须是 `tweeter-profile` 数据集且必须包括 `data_root` 和 `class_name` 变量，否则程序会报错。\n\n## 引用\n\n```BibTeX\n@misc{2211.13523,\nAuthor = {Floriana Ciaglia and Francesco Saverio Zuppichini and Paul Guerrie and Mark McQuade and Jacob Solawetz},\nTitle = {Roboflow 100: A Rich, Multi-Domain Object Detection Benchmark},\nYear = {2022},\nEprint = {arXiv:2211.13523},\n}\n```\n"
  },
  {
    "path": "projects/RF100-Benchmark/__init__.py",
    "content": "from .coco import RF100CocoDataset\nfrom .coco_metric import RF100CocoMetric\n\n__all__ = ['RF100CocoDataset', 'RF100CocoMetric']\n"
  },
  {
    "path": "projects/RF100-Benchmark/coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nfrom typing import List, Union\n\nfrom mmengine.fileio import get_local_path\n\nfrom mmdet.datasets.api_wrappers import COCO\nfrom mmdet.datasets.coco import CocoDataset\nfrom mmdet.registry import DATASETS\n\n\n@DATASETS.register_module()\nclass RF100CocoDataset(CocoDataset):\n    \"\"\"Dataset for COCO.\n\n    In the RF100 dataset, there are cases where the classes and sup_names are\n    the same, which is incorrect, such as \"bees-jt5in\". Therefore, we need to\n    handle this situation.\n    \"\"\"\n\n    METAINFO = {\n        'classes':\n        ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n         'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',\n         'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',\n         'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',\n         'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',\n         'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard',\n         'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork',\n         'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',\n         'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair',\n         'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv',\n         'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',\n         'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',\n         'scissors', 'teddy bear', 'hair drier', 'toothbrush'),\n        # palette is a list of color tuples, which is used for visualization.\n        'palette':\n        [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),\n         (0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),\n         (100, 170, 30), (220, 220, 0), (175, 116, 175), (250, 0, 30),\n         (165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),\n         (0, 82, 0), (120, 166, 157), (110, 76, 0), (174, 57, 255),\n         (199, 100, 0), (72, 0, 118), (255, 179, 240), (0, 125, 92),\n         (209, 0, 151), (188, 208, 182), (0, 220, 176), (255, 99, 164),\n         (92, 0, 73), (133, 129, 255), (78, 180, 255), (0, 228, 0),\n         (174, 255, 243), (45, 89, 255), (134, 134, 103), (145, 148, 174),\n         (255, 208, 186), (197, 226, 255), (171, 134, 1), (109, 63, 54),\n         (207, 138, 255), (151, 0, 95), (9, 80, 61), (84, 105, 51),\n         (74, 65, 105), (166, 196, 102), (208, 195, 210), (255, 109, 65),\n         (0, 143, 149), (179, 0, 194), (209, 99, 106), (5, 121, 0),\n         (227, 255, 205), (147, 186, 208), (153, 69, 1), (3, 95, 161),\n         (163, 255, 0), (119, 0, 170), (0, 182, 199), (0, 165, 120),\n         (183, 130, 88), (95, 32, 0), (130, 114, 135), (110, 129, 133),\n         (166, 74, 118), (219, 142, 185), (79, 210, 114), (178, 90, 62),\n         (65, 70, 15), (127, 167, 115), (59, 105, 106), (142, 108, 45),\n         (196, 172, 0), (95, 54, 80), (128, 76, 255), (201, 57, 1),\n         (246, 0, 122), (191, 162, 208)]\n    }\n    COCOAPI = COCO\n    # ann_id is unique in coco dataset.\n    ANN_ID_UNIQUE = True\n\n    def load_data_list(self) -> List[dict]:\n        \"\"\"Load annotations from an annotation file named as ``self.ann_file``\n\n        Returns:\n            List[dict]: A list of annotation.\n        \"\"\"  # noqa: E501\n        with get_local_path(\n                self.ann_file, backend_args=self.backend_args) as local_path:\n            self.coco = self.COCOAPI(local_path)\n        # The order of returned `cat_ids` will not\n        # change with the order of the `classes`\n        self.cat_ids = self.coco.get_cat_ids(\n            cat_names=self.metainfo['classes'])\n\n        # ----------------------------------\n        # We only change this\n        if len(self.cat_ids) != len(self.metainfo['classes']):\n            sup_id = self.coco.get_cat_ids(sup_names=['none'])\n            self.cat_ids = [x for x in self.cat_ids if x not in sup_id]\n        # ----------------------------------\n\n        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}\n        self.cat_img_map = copy.deepcopy(self.coco.cat_img_map)\n\n        img_ids = self.coco.get_img_ids()\n        data_list = []\n        total_ann_ids = []\n        for img_id in img_ids:\n            raw_img_info = self.coco.load_imgs([img_id])[0]\n            raw_img_info['img_id'] = img_id\n\n            ann_ids = self.coco.get_ann_ids(img_ids=[img_id])\n            raw_ann_info = self.coco.load_anns(ann_ids)\n            total_ann_ids.extend(ann_ids)\n\n            parsed_data_info = self.parse_data_info({\n                'raw_ann_info':\n                raw_ann_info,\n                'raw_img_info':\n                raw_img_info\n            })\n            data_list.append(parsed_data_info)\n        if self.ANN_ID_UNIQUE:\n            assert len(set(total_ann_ids)) == len(\n                total_ann_ids\n            ), f\"Annotation ids in '{self.ann_file}' are not unique!\"\n\n        del self.coco\n\n        return data_list\n\n    def parse_data_info(self, raw_data_info: dict) -> Union[dict, List[dict]]:\n        \"\"\"Parse raw annotation to target format.\n\n        Args:\n            raw_data_info (dict): Raw data information load from ``ann_file``\n\n        Returns:\n            Union[dict, List[dict]]: Parsed annotation.\n        \"\"\"\n        img_info = raw_data_info['raw_img_info']\n        ann_info = raw_data_info['raw_ann_info']\n\n        data_info = {}\n\n        # TODO: need to change data_prefix['img'] to data_prefix['img_path']\n        img_path = osp.join(self.data_prefix['img'], img_info['file_name'])\n        if self.data_prefix.get('seg', None):\n            seg_map_path = osp.join(\n                self.data_prefix['seg'],\n                img_info['file_name'].rsplit('.', 1)[0] + self.seg_map_suffix)\n        else:\n            seg_map_path = None\n        data_info['img_path'] = img_path\n        data_info['img_id'] = img_info['img_id']\n        data_info['seg_map_path'] = seg_map_path\n        data_info['height'] = img_info['height']\n        data_info['width'] = img_info['width']\n\n        if self.return_classes:\n            data_info['text'] = self.metainfo['classes']\n            data_info['custom_entities'] = True\n\n        instances = []\n        for i, ann in enumerate(ann_info):\n            instance = {}\n\n            if ann.get('ignore', False):\n                continue\n            x1, y1, w, h = ann['bbox']\n            inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                continue\n            if ann['category_id'] not in self.cat_ids:\n                continue\n            bbox = [x1, y1, x1 + w, y1 + h]\n\n            if ann.get('iscrowd', False):\n                instance['ignore_flag'] = 1\n            else:\n                instance['ignore_flag'] = 0\n            instance['bbox'] = bbox\n            instance['bbox_label'] = self.cat2label[ann['category_id']]\n\n            if ann.get('segmentation', None):\n                instance['mask'] = ann['segmentation']\n\n            instances.append(instance)\n        data_info['instances'] = instances\n        return data_info\n\n    def filter_data(self) -> List[dict]:\n        \"\"\"Filter annotations according to filter_cfg.\n\n        Returns:\n            List[dict]: Filtered results.\n        \"\"\"\n        if self.test_mode:\n            return self.data_list\n\n        if self.filter_cfg is None:\n            return self.data_list\n\n        filter_empty_gt = self.filter_cfg.get('filter_empty_gt', False)\n        min_size = self.filter_cfg.get('min_size', 0)\n\n        # obtain images that contain annotation\n        ids_with_ann = set(data_info['img_id'] for data_info in self.data_list)\n        # obtain images that contain annotations of the required categories\n        ids_in_cat = set()\n        for i, class_id in enumerate(self.cat_ids):\n            ids_in_cat |= set(self.cat_img_map[class_id])\n        # merge the image id sets of the two conditions and use the merged set\n        # to filter out images if self.filter_empty_gt=True\n        ids_in_cat &= ids_with_ann\n\n        valid_data_infos = []\n        for i, data_info in enumerate(self.data_list):\n            img_id = data_info['img_id']\n            width = data_info['width']\n            height = data_info['height']\n            if filter_empty_gt and img_id not in ids_in_cat:\n                continue\n            if min(width, height) >= min_size:\n                valid_data_infos.append(data_info)\n\n        return valid_data_infos\n"
  },
  {
    "path": "projects/RF100-Benchmark/coco_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport itertools\nimport os.path as osp\nimport tempfile\nfrom collections import OrderedDict\nfrom typing import Dict\n\nimport numpy as np\nfrom mmengine.fileio import load\nfrom mmengine.logging import MMLogger\nfrom terminaltables import AsciiTable\n\nfrom mmdet.datasets.api_wrappers import COCO, COCOeval\nfrom mmdet.evaluation.metrics import CocoMetric\nfrom mmdet.registry import METRICS\n\n\n@METRICS.register_module()\nclass RF100CocoMetric(CocoMetric):\n    \"\"\"COCO evaluation metric.\n\n    In the RF100 dataset, there are cases where the classes and sup_names are\n    the same, which is incorrect, such as \"bees-jt5in\". Therefore, we need to\n    handle this situation.\n    \"\"\"\n\n    def compute_metrics(self, results: list) -> Dict[str, float]:\n        \"\"\"Compute the metrics from processed results.\n\n        Args:\n            results (list): The processed results of each batch.\n\n        Returns:\n            Dict[str, float]: The computed metrics. The keys are the names of\n            the metrics, and the values are corresponding results.\n        \"\"\"\n        logger: MMLogger = MMLogger.get_current_instance()\n\n        # split gt and prediction list\n        gts, preds = zip(*results)\n\n        tmp_dir = None\n        if self.outfile_prefix is None:\n            tmp_dir = tempfile.TemporaryDirectory()\n            outfile_prefix = osp.join(tmp_dir.name, 'results')\n        else:\n            outfile_prefix = self.outfile_prefix\n\n        if self._coco_api is None:\n            # use converted gt json file to initialize coco api\n            logger.info('Converting ground truth to coco format...')\n            coco_json_path = self.gt_to_coco_json(\n                gt_dicts=gts, outfile_prefix=outfile_prefix)\n            self._coco_api = COCO(coco_json_path)\n\n        # handle lazy init\n        if self.cat_ids is None:\n            self.cat_ids = self._coco_api.get_cat_ids(\n                cat_names=self.dataset_meta['classes'])\n\n            # ----------------------------------\n            # We only change this\n            if len(self.cat_ids) != len(self.dataset_meta['classes']):\n                sup_id = self._coco_api.get_cat_ids(sup_names=['none'])\n                self.cat_ids = [x for x in self.cat_ids if x not in sup_id]\n            # ----------------------------------\n\n        if self.img_ids is None:\n            self.img_ids = self._coco_api.get_img_ids()\n\n        # convert predictions to coco format and dump to json file\n        result_files = self.results2json(preds, outfile_prefix)\n\n        eval_results = OrderedDict()\n        if self.format_only:\n            logger.info('results are saved in '\n                        f'{osp.dirname(outfile_prefix)}')\n            return eval_results\n\n        for metric in self.metrics:\n            logger.info(f'Evaluating {metric}...')\n\n            # TODO: May refactor fast_eval_recall to an independent metric?\n            # fast eval recall\n            if metric == 'proposal_fast':\n                ar = self.fast_eval_recall(\n                    preds, self.proposal_nums, self.iou_thrs, logger=logger)\n                log_msg = []\n                for i, num in enumerate(self.proposal_nums):\n                    eval_results[f'AR@{num}'] = ar[i]\n                    log_msg.append(f'\\nAR@{num}\\t{ar[i]:.4f}')\n                log_msg = ''.join(log_msg)\n                logger.info(log_msg)\n                continue\n\n            # evaluate proposal, bbox and segm\n            iou_type = 'bbox' if metric == 'proposal' else metric\n            if metric not in result_files:\n                raise KeyError(f'{metric} is not in results')\n            try:\n                predictions = load(result_files[metric])\n                if iou_type == 'segm':\n                    # Refer to https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/coco.py#L331  # noqa\n                    # When evaluating mask AP, if the results contain bbox,\n                    # cocoapi will use the box area instead of the mask area\n                    # for calculating the instance area. Though the overall AP\n                    # is not affected, this leads to different\n                    # small/medium/large mask AP results.\n                    for x in predictions:\n                        x.pop('bbox')\n                coco_dt = self._coco_api.loadRes(predictions)\n\n            except IndexError:\n                logger.error(\n                    'The testing results of the whole dataset is empty.')\n                break\n\n            coco_eval = COCOeval(self._coco_api, coco_dt, iou_type)\n\n            coco_eval.params.catIds = self.cat_ids\n            coco_eval.params.imgIds = self.img_ids\n            coco_eval.params.maxDets = list(self.proposal_nums)\n            coco_eval.params.iouThrs = self.iou_thrs\n\n            # mapping of cocoEval.stats\n            coco_metric_names = {\n                'mAP': 0,\n                'mAP_50': 1,\n                'mAP_75': 2,\n                'mAP_s': 3,\n                'mAP_m': 4,\n                'mAP_l': 5,\n                'AR@100': 6,\n                'AR@300': 7,\n                'AR@1000': 8,\n                'AR_s@1000': 9,\n                'AR_m@1000': 10,\n                'AR_l@1000': 11\n            }\n            metric_items = self.metric_items\n            if metric_items is not None:\n                for metric_item in metric_items:\n                    if metric_item not in coco_metric_names:\n                        raise KeyError(\n                            f'metric item \"{metric_item}\" is not supported')\n\n            if metric == 'proposal':\n                coco_eval.params.useCats = 0\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if metric_items is None:\n                    metric_items = [\n                        'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000',\n                        'AR_m@1000', 'AR_l@1000'\n                    ]\n\n                for item in metric_items:\n                    val = float(\n                        f'{coco_eval.stats[coco_metric_names[item]]:.3f}')\n                    eval_results[item] = val\n            else:\n                coco_eval.evaluate()\n                coco_eval.accumulate()\n                coco_eval.summarize()\n                if self.classwise:  # Compute per-category AP\n                    # Compute per-category AP\n                    # from https://github.com/facebookresearch/detectron2/\n                    precisions = coco_eval.eval['precision']\n                    # precision: (iou, recall, cls, area range, max dets)\n                    assert len(self.cat_ids) == precisions.shape[2]\n\n                    results_per_category = []\n                    for idx, cat_id in enumerate(self.cat_ids):\n                        t = []\n                        # area range index 0: all area ranges\n                        # max dets index -1: typically 100 per image\n                        nm = self._coco_api.loadCats(cat_id)[0]\n                        precision = precisions[:, :, idx, 0, -1]\n                        precision = precision[precision > -1]\n                        if precision.size:\n                            ap = np.mean(precision)\n                        else:\n                            ap = float('nan')\n                        t.append(f'{nm[\"name\"]}')\n                        t.append(f'{round(ap, 3)}')\n                        eval_results[f'{nm[\"name\"]}_precision'] = round(ap, 3)\n\n                        # indexes of IoU  @50 and @75\n                        for iou in [0, 5]:\n                            precision = precisions[iou, :, idx, 0, -1]\n                            precision = precision[precision > -1]\n                            if precision.size:\n                                ap = np.mean(precision)\n                            else:\n                                ap = float('nan')\n                            t.append(f'{round(ap, 3)}')\n\n                        # indexes of area of small, median and large\n                        for area in [1, 2, 3]:\n                            precision = precisions[:, :, idx, area, -1]\n                            precision = precision[precision > -1]\n                            if precision.size:\n                                ap = np.mean(precision)\n                            else:\n                                ap = float('nan')\n                            t.append(f'{round(ap, 3)}')\n                        results_per_category.append(tuple(t))\n\n                    num_columns = len(results_per_category[0])\n                    results_flatten = list(\n                        itertools.chain(*results_per_category))\n                    headers = [\n                        'category', 'mAP', 'mAP_50', 'mAP_75', 'mAP_s',\n                        'mAP_m', 'mAP_l'\n                    ]\n                    results_2d = itertools.zip_longest(*[\n                        results_flatten[i::num_columns]\n                        for i in range(num_columns)\n                    ])\n                    table_data = [headers]\n                    table_data += [result for result in results_2d]\n                    table = AsciiTable(table_data)\n                    logger.info('\\n' + table.table)\n\n                if metric_items is None:\n                    metric_items = [\n                        'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l'\n                    ]\n\n                for metric_item in metric_items:\n                    key = f'{metric}_{metric_item}'\n                    val = coco_eval.stats[coco_metric_names[metric_item]]\n                    eval_results[key] = float(f'{round(val, 3)}')\n\n                ap = coco_eval.stats[:6]\n                logger.info(f'{metric}_mAP_copypaste: {ap[0]:.3f} '\n                            f'{ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} '\n                            f'{ap[4]:.3f} {ap[5]:.3f}')\n\n        if tmp_dir is not None:\n            tmp_dir.cleanup()\n        return eval_results\n"
  },
  {
    "path": "projects/RF100-Benchmark/configs/dino_r50_fpn_ms_8xb8_tweeter-profile.py",
    "content": "_base_ = '../../../configs/dino/dino-4scale_r50_8xb2-12e_coco.py'\n\ncustom_imports = dict(\n    imports=['projects.RF100-Benchmark'], allow_failed_imports=False)\n\ndata_root = 'rf100/tweeter-profile/'\nclass_name = ('profile_info', )\nnum_classes = len(class_name)\nmetainfo = dict(classes=class_name)\nimage_scale = (640, 640)\n\nmodel = dict(\n    backbone=dict(\n        norm_eval=False, norm_cfg=dict(requires_grad=True), frozen_stages=-1),\n    bbox_head=dict(num_classes=int(num_classes)))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=image_scale,\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=image_scale),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=image_scale, keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=4,\n    batch_sampler=None,\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=4,\n        dataset=dict(\n            type='RF100CocoDataset',\n            metainfo=metainfo,\n            data_root=data_root,\n            ann_file='train/_annotations.coco.json',\n            data_prefix=dict(img='train/'),\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            pipeline=train_pipeline)))\n\nval_dataloader = dict(\n    dataset=dict(\n        type='RF100CocoDataset',\n        metainfo=metainfo,\n        data_root=data_root,\n        ann_file='valid/_annotations.coco.json',\n        data_prefix=dict(img='valid/'),\n        pipeline=test_pipeline,\n    ))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='RF100CocoMetric',\n    ann_file=data_root + 'valid/_annotations.coco.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\nmax_epochs = 25\ntrain_cfg = dict(max_epochs=max_epochs)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=200),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[18, 22],\n        gamma=0.1)\n]\n\nload_from = 'https://download.openmmlab.com/mmdetection/v3.0/dino/dino-4scale_r50_8xb2-12e_coco/dino-4scale_r50_8xb2-12e_coco_20221202_182705-55b2bba2.pth'  # noqa\n\n# We only save the best checkpoint by validation mAP.\ndefault_hooks = dict(\n    checkpoint=dict(save_best='auto', max_keep_ckpts=-1, interval=-1))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=64)\n\nbroadcast_buffers = True\n"
  },
  {
    "path": "projects/RF100-Benchmark/configs/faster-rcnn_r50_fpn_ms_8xb8_tweeter-profile.py",
    "content": "_base_ = '../../../configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py'\n\ncustom_imports = dict(\n    imports=['projects.RF100-Benchmark'], allow_failed_imports=False)\n\ndata_root = 'rf100/tweeter-profile/'\nclass_name = ('profile_info', )\nnum_classes = len(class_name)\nmetainfo = dict(classes=class_name)\nimage_scale = (640, 640)\n\nmodel = dict(\n    backbone=dict(norm_eval=False, frozen_stages=-1),\n    roi_head=dict(bbox_head=dict(num_classes=int(num_classes))))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=image_scale,\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=image_scale),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=image_scale, keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=4,\n    batch_sampler=None,\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=4,\n        dataset=dict(\n            type='RF100CocoDataset',\n            metainfo=metainfo,\n            data_root=data_root,\n            ann_file='train/_annotations.coco.json',\n            data_prefix=dict(img='train/'),\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            pipeline=train_pipeline)))\n\nval_dataloader = dict(\n    dataset=dict(\n        type='RF100CocoDataset',\n        metainfo=metainfo,\n        data_root=data_root,\n        ann_file='valid/_annotations.coco.json',\n        data_prefix=dict(img='valid/'),\n        pipeline=test_pipeline,\n    ))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='RF100CocoMetric',\n    ann_file=data_root + 'valid/_annotations.coco.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\nmax_epochs = 25\ntrain_cfg = dict(max_epochs=max_epochs)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=200),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[18, 22],\n        gamma=0.1)\n]\n\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_mstrain_3x_coco/faster_rcnn_r50_fpn_mstrain_3x_coco_20210524_110822-e10bd31c.pth'  # noqa\n\n# We only save the best checkpoint by validation mAP.\ndefault_hooks = dict(\n    checkpoint=dict(save_best='auto', max_keep_ckpts=-1, interval=-1))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=64)\n\nbroadcast_buffers = True\n"
  },
  {
    "path": "projects/RF100-Benchmark/configs/tood_r50_fpn_ms_8xb8_tweeter-profile.py",
    "content": "_base_ = '../../../configs/tood/tood_r50_fpn_1x_coco.py'\n\ncustom_imports = dict(\n    imports=['projects.RF100-Benchmark'], allow_failed_imports=False)\n\ndata_root = 'rf100/tweeter-profile/'\nclass_name = ('profile_info', )\nnum_classes = len(class_name)\nmetainfo = dict(classes=class_name)\nimage_scale = (640, 640)\n\nmodel = dict(\n    backbone=dict(norm_eval=False, frozen_stages=-1),\n    bbox_head=dict(num_classes=int(num_classes)))\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='RandomResize',\n        scale=image_scale,\n        ratio_range=(0.8, 1.2),\n        keep_ratio=True),\n    dict(type='RandomCrop', crop_size=image_scale),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(type='Resize', scale=image_scale, keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=4,\n    batch_sampler=None,\n    dataset=dict(\n        _delete_=True,\n        type='RepeatDataset',\n        times=4,\n        dataset=dict(\n            type='RF100CocoDataset',\n            metainfo=metainfo,\n            data_root=data_root,\n            ann_file='train/_annotations.coco.json',\n            data_prefix=dict(img='train/'),\n            filter_cfg=dict(filter_empty_gt=False, min_size=32),\n            pipeline=train_pipeline)))\n\nval_dataloader = dict(\n    dataset=dict(\n        type='RF100CocoDataset',\n        metainfo=metainfo,\n        data_root=data_root,\n        ann_file='valid/_annotations.coco.json',\n        data_prefix=dict(img='valid/'),\n        pipeline=test_pipeline,\n    ))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='RF100CocoMetric',\n    ann_file=data_root + 'valid/_annotations.coco.json',\n    metric='bbox',\n    format_only=False)\ntest_evaluator = val_evaluator\n\nmax_epochs = 25\ntrain_cfg = dict(max_epochs=max_epochs)\n\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=200),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_epochs,\n        by_epoch=True,\n        milestones=[18, 22],\n        gamma=0.1)\n]\n\nload_from = 'https://download.openmmlab.com/mmdetection/v2.0/tood/tood_r50_fpn_1x_coco/tood_r50_fpn_1x_coco_20211210_103425-20e20746.pth'  # noqa\n\n# We only save the best checkpoint by validation mAP.\ndefault_hooks = dict(\n    checkpoint=dict(save_best='auto', max_keep_ckpts=-1, interval=-1))\n\n# Default setting for scaling LR automatically\n#   - `enable` means enable scaling LR automatically\n#       or not by default.\n#   - `base_batch_size` = (8 GPUs) x (2 samples per GPU).\nauto_scale_lr = dict(enable=False, base_batch_size=64)\n\nbroadcast_buffers = True\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/create_new_config.py",
    "content": "from argparse import ArgumentParser\n\nfrom mmengine.fileio import load\nfrom mmengine.utils import mkdir_or_exist\n\n\ndef parse_args():\n    parser = ArgumentParser(description='create new config')\n    parser.add_argument('config')\n    parser.add_argument('dataset')\n    parser.add_argument('--save-dir', default='temp_configs')\n    parser.add_argument('--name-json', default='scripts/labels_names.json')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    config = args.config\n    labels_names_json = args.name_json\n\n    mkdir_or_exist(args.save_dir)\n\n    json_data = load(labels_names_json)\n    dataset_name = [j['name'] for j in json_data]\n    classes_name = [tuple(j['classes'].keys()) for j in json_data]\n    if args.dataset in dataset_name:\n        classes_name = classes_name[dataset_name.index(args.dataset)]\n        with open(config, 'r') as file:\n            content = file.read()\n        new_content = content.replace(\"('profile_info', )\", str(classes_name))\n        new_content = new_content.replace('tweeter-profile', args.dataset)\n\n        with open(f'{args.save_dir}/{args.dataset}.py', 'w') as file:\n            file.write(new_content)\n    else:\n        raise ValueError('dataset name not found in labels_names.json')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/datasets_links_640.txt",
    "content": "https://app.roboflow.com/roboflow-100/tweeter-profile/1\nhttps://app.roboflow.com/roboflow-100/gauge-u2lwv/4\nhttps://app.roboflow.com/roboflow-100/road-traffic/3\nhttps://app.roboflow.com/roboflow-100/wall-damage/1\nhttps://app.roboflow.com/roboflow-100/fish-market-ggjso/5\nhttps://app.roboflow.com/roboflow-100/soda-bottles/3\nhttps://app.roboflow.com/roboflow-100/flir-camera-objects/1\nhttps://app.roboflow.com/roboflow-100/stomata-cells/1\nhttps://app.roboflow.com/roboflow-100/leaf-disease-nsdsr/1\nhttps://app.roboflow.com/roboflow-100/bees-jt5in/1\nhttps://app.roboflow.com/roboflow-100/team-fight-tactics/1\nhttps://app.roboflow.com/roboflow-100/phages/1\nhttps://app.roboflow.com/roboflow-100/robomasters-285km/1\nhttps://app.roboflow.com/roboflow-100/lettuce-pallets/1\nhttps://app.roboflow.com/roboflow-100/trail-camera/1\nhttps://app.roboflow.com/roboflow-100/sedimentary-features-9eosf/4\nhttps://app.roboflow.com/roboflow-100/liver-disease/1\nhttps://app.roboflow.com/roboflow-100/cell-towers/1\nhttps://app.roboflow.com/roboflow-100/shark-teeth-5atku/1\nhttps://app.roboflow.com/roboflow-100/currency-v4f8j/1\nhttps://app.roboflow.com/roboflow-100/asbestos/1\nhttps://app.roboflow.com/roboflow-100/insects-mytwu/1\nhttps://app.roboflow.com/roboflow-100/cotton-20xz5/1\nhttps://app.roboflow.com/roboflow-100/uno-deck/1\nhttps://app.roboflow.com/roboflow-100/grass-weeds/1\nhttps://app.roboflow.com/roboflow-100/circuit-voltages/1\nhttps://app.roboflow.com/roboflow-100/people-in-paintings/1\nhttps://app.roboflow.com/roboflow-100/apples-fvpl5/1\nhttps://app.roboflow.com/roboflow-100/number-ops/1\nhttps://app.roboflow.com/roboflow-100/cable-damage/1\nhttps://app.roboflow.com/roboflow-100/furniture-ngpea/1\nhttps://app.roboflow.com/roboflow-100/poker-cards-cxcvz/1\nhttps://app.roboflow.com/roboflow-100/pills-sxdht/1\nhttps://app.roboflow.com/roboflow-100/bone-fracture-7fylg/1\nhttps://app.roboflow.com/roboflow-100/marbles/1\nhttps://app.roboflow.com/roboflow-100/cavity-rs0uf/1\nhttps://app.roboflow.com/roboflow-100/pests-2xlvx/1\nhttps://app.roboflow.com/roboflow-100/printed-circuit-board/3\nhttps://app.roboflow.com/roboflow-100/peanuts-sd4kf/1\nhttps://app.roboflow.com/roboflow-100/vehicles-q0x2v/1\nhttps://app.roboflow.com/roboflow-100/digits-t2eg6/1\nhttps://app.roboflow.com/roboflow-100/wine-labels/1\nhttps://app.roboflow.com/roboflow-100/truck-movement/3\nhttps://app.roboflow.com/roboflow-100/coral-lwptl/1\nhttps://app.roboflow.com/roboflow-100/brain-tumor-m2pbp/1\nhttps://app.roboflow.com/roboflow-100/cotton-plant-disease/1\nhttps://app.roboflow.com/roboflow-100/bacteria-ptywi/1\nhttps://app.roboflow.com/roboflow-100/4-fold-defect/1\nhttps://app.roboflow.com/roboflow-100/cells-uyemf/1\nhttps://app.roboflow.com/roboflow-100/gynecology-mri/1\nhttps://app.roboflow.com/roboflow-100/axial-mri/1\nhttps://app.roboflow.com/roboflow-100/abdomen-mri/1\nhttps://app.roboflow.com/roboflow-100/acl-x-ray/1\nhttps://app.roboflow.com/roboflow-100/radio-signal/1\nhttps://app.roboflow.com/roboflow-100/x-ray-rheumatology/1\nhttps://app.roboflow.com/roboflow-100/parasites-1s07h/1\nhttps://app.roboflow.com/roboflow-100/aerial-cows/1\nhttps://app.roboflow.com/roboflow-100/aerial-spheres/1\nhttps://app.roboflow.com/roboflow-100/secondary-chains/1\nhttps://app.roboflow.com/roboflow-100/aerial-pool/3\nhttps://app.roboflow.com/roboflow-100/underwater-objects-5v7p8/1\nhttps://app.roboflow.com/roboflow-100/peixos-fish/3\nhttps://app.roboflow.com/roboflow-100/underwater-pipes-4ng4t/1\nhttps://app.roboflow.com/roboflow-100/signatures-xc8up/1\nhttps://app.roboflow.com/roboflow-100/activity-diagrams-qdobr/1\nhttps://app.roboflow.com/roboflow-100/document-parts/1\nhttps://app.roboflow.com/roboflow-100/tweeter-posts/1\nhttps://app.roboflow.com/roboflow-100/avatar-recognition-nuexe/1\nhttps://app.roboflow.com/roboflow-100/csgo-videogame/1\nhttps://app.roboflow.com/roboflow-100/farcry6-videogame/1\nhttps://app.roboflow.com/roboflow-100/apex-videogame/1\nhttps://app.roboflow.com/roboflow-100/cables-nl42k/1\nhttps://app.roboflow.com/roboflow-100/circuit-elements/3\nhttps://app.roboflow.com/roboflow-100/washroom-rf1fa/1\nhttps://app.roboflow.com/roboflow-100/construction-safety-gsnvb/1\nhttps://app.roboflow.com/roboflow-100/street-work/3\nhttps://app.roboflow.com/roboflow-100/excavators-czvg9/1\nhttps://app.roboflow.com/roboflow-100/corrosion-bi3q3/1\nhttps://app.roboflow.com/roboflow-100/solar-panels-taxvb/1\nhttps://app.roboflow.com/roboflow-100/animals-ij5d2/1\nhttps://app.roboflow.com/roboflow-100/valentines-chocolate/3\nhttps://app.roboflow.com/roboflow-100/sign-language-sokdr/1\nhttps://app.roboflow.com/roboflow-100/halo-infinite-angel-videogame/1\nhttps://app.roboflow.com/roboflow-100/aquarium-qlnqy/1\nhttps://app.roboflow.com/roboflow-100/thermal-cheetah-my4dp/1\nhttps://app.roboflow.com/roboflow-100/chess-pieces-mjzgj/1\nhttps://app.roboflow.com/roboflow-100/bccd-ouzjz/1\nhttps://app.roboflow.com/roboflow-100/mask-wearing-608pr/1\nhttps://app.roboflow.com/roboflow-100/thermal-dogs-and-people-x6ejw/1\nhttps://app.roboflow.com/roboflow-100/weed-crop-aerial/1\nhttps://app.roboflow.com/roboflow-100/mitosis-gjs3g/1\nhttps://app.roboflow.com/roboflow-100/smoke-uvylj/1\nhttps://app.roboflow.com/roboflow-100/road-signs-6ih4y/1\nhttps://app.roboflow.com/roboflow-100/soccer-players-5fuqs/1\nhttps://app.roboflow.com/roboflow-100/hand-gestures-jps7z/1\nhttps://app.roboflow.com/roboflow-100/paper-parts/3\nhttps://app.roboflow.com/roboflow-100/cloud-types/1\nhttps://app.roboflow.com/roboflow-100/tabular-data-wf9uh/1\nhttps://app.roboflow.com/roboflow-100/paragraphs-co84b/1\nhttps://app.roboflow.com/roboflow-100/coins-1apki/1\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/dist_train.sh",
    "content": "#!/usr/bin/env bash\n\nCONFIG=$1\nGPUS=$2\nWORK_DIRS=${3:-'work_dirs'}\nRETRY_PATH=${RETRY_PATH:-''}\nNNODES=${NNODES:-1}\nNODE_RANK=${NODE_RANK:-0}\nPORT=${PORT:-29500}\nMASTER_ADDR=${MASTER_ADDR:-\"127.0.0.1\"}\n\ndatasets=$(pwd)/rf100\nexport PYTHONPATH=\"../..\":$PYTHONPATH\n\nDEBUG=0\n# example\ndatasets_list=(\"acl-x-ray\", \"tweeter-profile\")\n\nif [ -n \"$RETRY_PATH\" ]; then\n    DEBUG=1\n    datasets_list=()\n    while IFS= read -r line; do\n      if [ -n \"$line\" ]; then\n          datasets_list+=(\"$line\")\n      fi\n    done < \"$RETRY_PATH\"\nfi\n\nif [ \"$DEBUG\" == 1 ]; then\n  echo \"current training dataset list is: ${datasets_list[@]}\"\nelse\n  echo \"Currently training with the full dataset.\"\nfi\necho \"==============================================\"\n\nfor dataset in $(ls $datasets)\n  do\n    # You can customize string_list to train only specific datasets.\n    if [ \"$DEBUG\" == 1 ]; then\n      if [[ ! \" ${datasets_list[@]} \" =~ \"$dataset\" ]]; then\n        continue\n      fi\n    fi\n\n    echo \"Training on $dataset\"\n    python $(pwd)/scripts/create_new_config.py $CONFIG $dataset\n    if [ \"$GPUS\" == 1 ]; then\n       python ../../tools/train.py \"temp_configs/$dataset.py\" --work-dir \"$WORK_DIRS/$dataset\" ${@:4}\n    else\n        python -m torch.distributed.launch \\\n            --nnodes=$NNODES \\\n            --node_rank=$NODE_RANK \\\n            --master_addr=$MASTER_ADDR \\\n            --nproc_per_node=$GPUS \\\n            --master_port=$PORT \\\n            ../../tools/train.py \\\n            \"temp_configs/$dataset.py\" \\\n            --launcher pytorch --work-dir \"$WORK_DIRS/$dataset\" ${@:4}\n    fi\n    echo \"==============================================\"\n  done\n\n#rm -rf temp_configs\necho \"Done training all the datasets\"\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/download_dataset.py",
    "content": "from argparse import ArgumentParser\nfrom os import environ\nfrom pathlib import Path\n\nfrom roboflow import Roboflow\n\n\ndef main():\n    # construct the argument parser and parse the arguments\n    parser = ArgumentParser()\n\n    parser.add_argument(\n        '-p',\n        '--project',\n        required=True,\n        type=str,\n        help='The project ID of the dataset found in the dataset URL.',\n    )\n    parser.add_argument(\n        '-v',\n        '--version',\n        required=True,\n        type=int,\n        help='The version the dataset you want to use',\n    )\n    parser.add_argument(\n        '-f',\n        '--model_format',\n        required=False,\n        type=str,\n        default='coco',\n        help='The format of the export you want to use (i.e. coco or yolov5)',\n    )\n\n    parser.add_argument(\n        '-l',\n        '--location',\n        required=False,\n        type=str,\n        default='./rf100',\n        help='Where to store the dataset',\n    )\n    # parses command line arguments\n    args = vars(parser.parse_args())\n\n    try:\n        api_key = environ['ROBOFLOW_API_KEY']\n    except KeyError:\n        raise KeyError('You must export your Roboflow api key, '\n                       'to obtain one see https://docs.roboflow.com/rest-api.')\n    # create location if it doesn't exist\n    out_dir = Path(args['location']) / args['project']\n    out_dir.mkdir(parents=True, exist_ok=True)\n    print(\n        f'Storing {args[\"project\"] } in {out_dir} for {args[\"model_format\"]}')\n    # get and download the dataset\n    rf = Roboflow(api_key=api_key)\n    project = rf.workspace('roboflow-100').project(args['project'])\n    project.version(args['version']).download(\n        args['model_format'], location=str(out_dir))\n    print('Done!')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/download_datasets.sh",
    "content": "#!/bin/bash\n#set -euo pipefail\ninput=\"$(pwd)/scripts/datasets_links_640.txt\"\n\nwhile getopts f:l: flag\ndo\n    case \"${flag}\" in\n        f) format=${OPTARG};;\n        l) location=${OPTARG};;\n    esac\ndone\n# default values\nformat=${format:-coco}\nlocation=${location:-$(pwd)/rf100}\n\necho \"Starting downloading RF100...\"\n\nfor link in $(cat $input)\ndo\n    attributes=$(python3 $(pwd)/scripts/parse_dataset_link.py  -l $link)\n\n    project=$(echo $attributes | cut -d' ' -f 3)\n    version=$(echo $attributes | cut -d' ' -f 4)\n    if [ ! -d  \"$location/$project\" ] ;\n    then\n        python3 $(pwd)/scripts/download_dataset.py -p $project -v $version -l $location -f $format\n    fi\ndone\n\necho \"Done!\"\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/labels_names.json",
    "content": "[{\"category\": \"real world\",\n  \"classes\": {\"0\": 34,\n              \"1\": 70,\n              \"10\": 43,\n              \"11\": 55,\n              \"12\": 45,\n              \"13\": 143,\n              \"2\": 47,\n              \"3\": 64,\n              \"4\": 47,\n              \"5\": 66,\n              \"6\": 118,\n              \"7\": 35,\n              \"8\": 54,\n              \"9\": 92},\n  \"name\": \"hand-gestures-jps7z\"},\n {\"category\": \"real world\", \"classes\": {\"smoke\": 821}, \"name\": \"smoke-uvylj\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Minorrotation\": 85,\n              \"Moderaterotation\": 273,\n              \"Severerotation\": 103},\n  \"name\": \"wall-damage\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Slippage\": 646, \"corrosion\": 1657, \"crack\": 2513},\n  \"name\": \"corrosion-bi3q3\"},\n {\"category\": \"real world\",\n  \"classes\": {\"EXCAVATORS\": 1530, \"dump truck\": 1274, \"wheel loader\": 1080},\n  \"name\": \"excavators-czvg9\"},\n {\"category\": \"real world\",\n  \"classes\": {\"bishop\": 1,\n              \"black-bishop\": 140,\n              \"black-king\": 147,\n              \"black-knight\": 196,\n              \"black-pawn\": 659,\n              \"black-queen\": 87,\n              \"black-rook\": 201,\n              \"white-bishop\": 172,\n              \"white-king\": 149,\n              \"white-knight\": 184,\n              \"white-pawn\": 639,\n              \"white-queen\": 111,\n              \"white-rook\": 184},\n  \"name\": \"chess-pieces-mjzgj\"},\n {\"category\": \"real world\",\n  \"classes\": {\"bus_stop\": 105,\n              \"do_not_enter\": 100,\n              \"do_not_stop\": 100,\n              \"do_not_turn_l\": 113,\n              \"do_not_turn_r\": 101,\n              \"do_not_u_turn\": 100,\n              \"enter_left_lane\": 102,\n              \"green_light\": 167,\n              \"left_right_lane\": 101,\n              \"no_parking\": 104,\n              \"parking\": 101,\n              \"ped_crossing\": 100,\n              \"ped_zebra_cross\": 117,\n              \"railway_crossing\": 102,\n              \"red_light\": 149,\n              \"stop\": 108,\n              \"t_intersection_l\": 102,\n              \"traffic_light\": 100,\n              \"u_turn\": 100,\n              \"warning\": 100,\n              \"yellow_light\": 94},\n  \"name\": \"road-signs-6ih4y\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Cone\": 945,\n              \"Face_Shield\": 177,\n              \"Gloves\": 579,\n              \"Goggles\": 242,\n              \"Head\": 432,\n              \"Helmet\": 1018,\n              \"No glasses\": 218,\n              \"No gloves\": 382},\n  \"name\": \"street-work\"},\n {\"category\": \"real world\",\n  \"classes\": {\"helmet\": 2543,\n              \"no-helmet\": 129,\n              \"no-vest\": 892,\n              \"person\": 2817,\n              \"vest\": 1343},\n  \"name\": \"construction-safety-gsnvb\"},\n {\"category\": \"real world\",\n  \"classes\": {\"bicycles\": 82,\n              \"buses\": 111,\n              \"crosswalks\": 284,\n              \"fire hydrants\": 94,\n              \"motorcycles\": 240,\n              \"traffic lights\": 952,\n              \"vehicles\": 1272},\n  \"name\": \"road-traffic\"},\n {\"category\": \"real world\",\n  \"classes\": {\"bathtub\": 723,\n              \"c\": 1,\n              \"geyser\": 351,\n              \"mirror\": 1566,\n              \"showerhead\": 937,\n              \"sink\": 16,\n              \"toilet\": 18,\n              \"towel\": 1742,\n              \"washbasin\": 1977,\n              \"wc\": 2126},\n  \"name\": \"washroom-rf1fa\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Button\": 364,\n              \"Buzzer\": 68,\n              \"Capacitor\": 20800,\n              \"Capacitor Jumper\": 51117,\n              \"Capacitor Network\": 8048,\n              \"Clock\": 188,\n              \"Connector\": 8683,\n              \"Diode\": 8025,\n              \"EM\": 204,\n              \"Electrolytic Capacitor\": 2565,\n              \"Electrolytic capacitor\": 34,\n              \"Ferrite Bead\": 124,\n              \"Flex Cable\": 92,\n              \"Fuse\": 1508,\n              \"IC\": 9976,\n              \"Inductor\": 6450,\n              \"Jumper\": 709,\n              \"Led\": 974,\n              \"Pads\": 1495,\n              \"Pins\": 1527,\n              \"Potentiometer\": 28,\n              \"RP\": 62,\n              \"Resistor\": 27388,\n              \"Resistor Jumper\": 54428,\n              \"Resistor Network\": 8805,\n              \"Switch\": 341,\n              \"Test Point\": 2222,\n              \"Transducer\": 70,\n              \"Transformer\": 440,\n              \"Transistor\": 6862,\n              \"Unknown Unlabeled\": 1080},\n  \"name\": \"circuit-elements\"},\n {\"category\": \"real world\",\n  \"classes\": {\"mask\": 806, \"no-mask\": 148},\n  \"name\": \"mask-wearing-608pr\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Antenne\": 2550,\n              \"BBS\": 649,\n              \"BFU\": 338,\n              \"Batterie\": 4694,\n              \"DDF\": 353,\n              \"PCF\": 348,\n              \"PCU AC\": 383,\n              \"PCU DC\": 263,\n              \"PDU\": 356,\n              \"PSU\": 915,\n              \"RBS\": 1382},\n  \"name\": \"cables-nl42k\"},\n {\"category\": \"real world\",\n  \"classes\": {\"coca-cola\": 18514,\n              \"fanta\": 17426,\n              \"sprite\": 18747},\n  \"name\": \"soda-bottles\"},\n {\"category\": \"real world\",\n  \"classes\": {\"otr_chassis_loaded\": 492,\n              \"otr_chassis_unloaded\": 1844,\n              \"otr_chassis_working\": 994,\n              \"person\": 1164,\n              \"stacker\": 81},\n  \"name\": \"truck-movement\"},\n {\"category\": \"real world\",\n  \"classes\": {\"AlcoholPercentage\": 1464,\n              \"Appellation AOC DOC AVARegion\": 3458,\n              \"Appellation QualityLevel\": 960,\n              \"CountryCountry\": 2082,\n              \"Distinct Logo\": 4698,\n              \"Established YearYear\": 746,\n              \"Maker-Name\": 5897,\n              \"Organic\": 49,\n              \"Sustainable\": 78,\n              \"Sweetness-Brut-SecSweetness-Brut-Sec\": 244,\n              \"TypeWine Type\": 3595,\n              \"VintageYear\": 1763},\n  \"name\": \"wine-labels\"},\n {\"category\": \"real world\",\n  \"classes\": {\"0\": 1540,\n              \"1\": 2310,\n              \"2\": 1730,\n              \"3\": 1509,\n              \"4\": 1402,\n              \"5\": 1465,\n              \"6\": 1529,\n              \"7\": 1554,\n              \"8\": 1287,\n              \"9\": 1240},\n  \"name\": \"digits-t2eg6\"},\n {\"category\": \"real world\",\n  \"classes\": {\"big bus\": 816,\n              \"big truck\": 3632,\n              \"bus-l-\": 398,\n              \"bus-s-\": 148,\n              \"car\": 31641,\n              \"mid truck\": 703,\n              \"small bus\": 263,\n              \"small truck\": 5842,\n              \"truck-l-\": 2278,\n              \"truck-m-\": 3672,\n              \"truck-s-\": 1363,\n              \"truck-xl-\": 821},\n  \"name\": \"vehicles-q0x2v\"},\n {\"category\": \"real world\",\n  \"classes\": {\"with mold\": 14000, \"without mold\": 5350},\n  \"name\": \"peanuts-sd4kf\"},\n {\"category\": \"real world\",\n  \"classes\": {\"-\": 8,\n              \"Battery\": 4,\n              \"Button\": 277,\n              \"Buzzer\": 4,\n              \"Capacitor\": 19034,\n              \"Capacitor Jumper\": 32564,\n              \"Clock\": 146,\n              \"Connector\": 4558,\n              \"Diode\": 284,\n              \"Display\": 18,\n              \"EM\": 162,\n              \"Electrolytic Capacitor\": 856,\n              \"Ferrite Bead\": 108,\n              \"Fuse\": 28,\n              \"Heatsink\": 14,\n              \"IC\": 7481,\n              \"Inductor\": 235,\n              \"Jumper\": 324,\n              \"Led\": 784,\n              \"PS\": 4,\n              \"Pads\": 1176,\n              \"Pins\": 1212,\n              \"Potentiometer\": 26,\n              \"Resistor\": 16034,\n              \"Resistor Jumper\": 37329,\n              \"Resistor Network\": 4627,\n              \"SK\": 8,\n              \"Switch\": 189,\n              \"Test Point\": 1289,\n              \"Transformer\": 2,\n              \"Transistor\": 4273,\n              \"Unknown Unlabeled\": 982,\n              \"Zener Diode\": 15,\n              \"iC\": 123},\n  \"name\": \"printed-circuit-board\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Agrotis\": 44,\n              \"Athetis lepigone\": 66,\n              \"Athetis lineosa\": 43,\n              \"Chilo suppressalis\": 110,\n              \"Cnaphalocrocis medinalis Guenee\": 74,\n              \"Creatonotus transiens\": 181,\n              \"Diaphania indica\": 71,\n              \"Endotricha consocia\": 36,\n              \"Euproctis sparsa\": 72,\n              \"Gryllidae\": 138,\n              \"Gryllotalpidae\": 51,\n              \"Helicoverpa armigera\": 65,\n              \"Holotrichia oblita Faldermann\": 92,\n              \"Loxostege sticticalis\": 77,\n              \"Mamestra brassicae\": 111,\n              \"Maruca testulalis Geyer\": 42,\n              \"Mythimna separata\": 11,\n              \"Naranga aenescens Moore\": 39,\n              \"Nilaparvata\": 74,\n              \"Paracymoriza taiwanalis\": 42,\n              \"Sesamia inferens\": 52,\n              \"Sirthenea flavipes\": 87,\n              \"Sogatella furcifera\": 41,\n              \"Spodoptera exigua\": 59,\n              \"Spoladea recurvalis\": 103,\n              \"Staurophora celsia\": 95,\n              \"Timandra Recompta\": 67,\n              \"Trichoptera\": 35},\n  \"name\": \"pests-2xlvx\"},\n {\"category\": \"real world\",\n  \"classes\": {\"cavity\": 2039, \"normal\": 2982},\n  \"name\": \"cavity-rs0uf\"},\n {\"category\": \"real world\",\n  \"classes\": {\"mildew\": 5347, \"rose_P01\": 2054, \"rose_R02\": 3366},\n  \"name\": \"leaf-disease-nsdsr\"},\n {\"category\": \"real world\",\n  \"classes\": {\"red\": 3092, \"white\": 3127},\n  \"name\": \"marbles\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Cipro 500\": 113,\n              \"Ibuphil 600 mg\": 2,\n              \"Ibuphil Cold 400-60\": 72,\n              \"Xyzall 5mg\": 72,\n              \"blue\": 59,\n              \"pink\": 58,\n              \"red\": 60,\n              \"white\": 60},\n  \"name\": \"pills-sxdht\"},\n {\"category\": \"real world\",\n  \"classes\": {\"10 Diamonds\": 111,\n              \"10 Hearts\": 100,\n              \"10 Spades\": 120,\n              \"10 Trefoils\": 104,\n              \"2 Diamonds\": 113,\n              \"2 Hearts\": 100,\n              \"2 Spades\": 120,\n              \"2 Trefoils\": 104,\n              \"3 Diamonds\": 113,\n              \"3 Hearts\": 100,\n              \"3 Spades\": 120,\n              \"3 Trefoils\": 104,\n              \"4 Diamonds\": 113,\n              \"4 Hearts\": 100,\n              \"4 Spades\": 120,\n              \"4 Trefoils\": 100,\n              \"5 Diamonds\": 105,\n              \"5 Hearts\": 100,\n              \"5 Spades\": 108,\n              \"5 Trefoils\": 1,\n              \"59\": 104,\n              \"6 Diamonds\": 100,\n              \"6 Hearts\": 105,\n              \"6 Spades\": 100,\n              \"6 Trefoils\": 108,\n              \"7 Diamonds\": 100,\n              \"7 Hearts\": 105,\n              \"7 Spades\": 100,\n              \"7 Trefoils\": 108,\n              \"8 Diamonds\": 100,\n              \"8 Hearts\": 105,\n              \"8 Spades\": 100,\n              \"8 Trefoils\": 108,\n              \"9 Diamonds\": 104,\n              \"9 Hearts\": 111,\n              \"9 Spades\": 100,\n              \"9 Trefoils\": 120,\n              \"A Diamonds\": 104,\n              \"A Hearts\": 113,\n              \"A Spades\": 100,\n              \"A Trefoils\": 120,\n              \"J Diamonds\": 104,\n              \"J Hearts\": 110,\n              \"J Spades\": 100,\n              \"J Trefoils\": 120,\n              \"K Diamonds\": 104,\n              \"K Hearts\": 111,\n              \"K Spades\": 100,\n              \"K Trefoils\": 120,\n              \"Q Diamonds\": 104,\n              \"Q Hearts\": 111,\n              \"Q Spades\": 100,\n              \"Q Trefoils\": 119},\n  \"name\": \"poker-cards-cxcvz\"},\n {\"category\": \"real world\",\n  \"classes\": {\"0\": 560,\n              \"1\": 525,\n              \"2\": 409,\n              \"3\": 477,\n              \"4\": 485,\n              \"5\": 398,\n              \"6\": 551,\n              \"7\": 502,\n              \"8\": 519,\n              \"9\": 506,\n              \"div\": 398,\n              \"eqv\": 608,\n              \"minus\": 400,\n              \"mult\": 393,\n              \"plus\": 397},\n  \"name\": \"number-ops\"},\n {\"category\": \"real world\",\n  \"classes\": {\"army worm\": 100,\n              \"legume blister beetle\": 100,\n              \"red spider\": 100,\n              \"rice gall midge\": 101,\n              \"rice leaf roller\": 96,\n              \"rice leafhopper\": 109,\n              \"rice water weevil\": 104,\n              \"wheat phloeothrips\": 100,\n              \"white backed plant hopper\": 105,\n              \"yellow rice borer\": 96},\n  \"name\": \"insects-mytwu\"},\n {\"category\": \"real world\",\n  \"classes\": {\"G-arboreum\": 294,\n              \"G-barbadense\": 212,\n              \"G-herbaceum\": 239,\n              \"G-hirsitum\": 189},\n  \"name\": \"cotton-20xz5\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Chair\": 249, \"Sofa\": 240, \"Table\": 200},\n  \"name\": \"furniture-ngpea\"},\n {\"category\": \"real world\",\n  \"classes\": {\"break\": 822, \"thunderbolt\": 690},\n  \"name\": \"cable-damage\"},\n {\"category\": \"real world\",\n  \"classes\": {\"cat\": 110,\n              \"chicken\": 214,\n              \"cow\": 207,\n              \"dog\": 144,\n              \"fox\": 109,\n              \"goat\": 175,\n              \"horse\": 170,\n              \"person\": 232,\n              \"racoon\": 126,\n              \"skunk\": 102},\n  \"name\": \"animals-ij5d2\"},\n {\"category\": \"real world\",\n  \"classes\": {\"coin\": 13720, \"nail\": 2191, \"nut\": 1364, \"screw\": 995},\n  \"name\": \"coins-1apki\"},\n {\"category\": \"real world\",\n  \"classes\": {\"apple\": 2183, \"damaged_apple\": 710},\n  \"name\": \"apples-fvpl5\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Human\": 5151},\n  \"name\": \"people-in-paintings\"},\n {\"category\": \"real world\",\n  \"classes\": {\"GND\": 40,\n              \"IDC\": 76,\n              \"IDC_I\": 12,\n              \"R\": 418,\n              \"VDC\": 137,\n              \"VDC_I\": 15},\n  \"name\": \"circuit-voltages\"},\n {\"category\": \"real world\",\n  \"classes\": {\"0\": 1800,\n              \"1\": 1754,\n              \"10\": 1788,\n              \"11\": 1824,\n              \"12\": 1815,\n              \"13\": 1763,\n              \"14\": 1855,\n              \"2\": 1824,\n              \"3\": 1872,\n              \"4\": 1760,\n              \"5\": 1835,\n              \"6\": 1768,\n              \"7\": 1782,\n              \"8\": 1753,\n              \"9\": 1783},\n  \"name\": \"uno-deck\"},\n {\"category\": \"real world\",\n  \"classes\": {\"0 ridderzuring\": 6814},\n  \"name\": \"grass-weeds\"},\n {\"category\": \"real world\",\n  \"classes\": {\"gauges\": 499, \"numbers\": 1296},\n  \"name\": \"gauge-u2lwv\"},\n {\"category\": \"real world\",\n  \"classes\": {\"A\": 29,\n              \"B\": 25,\n              \"C\": 25,\n              \"D\": 28,\n              \"E\": 25,\n              \"F\": 30,\n              \"G\": 30,\n              \"H\": 29,\n              \"I\": 30,\n              \"J\": 38,\n              \"K\": 27,\n              \"L\": 28,\n              \"M\": 28,\n              \"N\": 27,\n              \"O\": 28,\n              \"P\": 25,\n              \"Q\": 26,\n              \"R\": 25,\n              \"S\": 30,\n              \"T\": 25,\n              \"U\": 25,\n              \"V\": 28,\n              \"W\": 27,\n              \"X\": 26,\n              \"Y\": 26,\n              \"Z\": 30},\n  \"name\": \"sign-language-sokdr\"},\n {\"category\": \"real world\",\n  \"classes\": {\"sees-dark-almond-nougat\": 71,\n              \"sees-dark-almonds\": 73,\n              \"sees-dark-bordeaux\": 75,\n              \"sees-dark-caramel-patties\": 73,\n              \"sees-dark-chocolate-buttercream\": 80,\n              \"sees-dark-marzipan\": 77,\n              \"sees-dark-normandie\": 77,\n              \"sees-dark-scotchmallow\": 72,\n              \"sees-dark-walnut-square\": 69,\n              \"sees-milk-almond-caramel\": 73,\n              \"sees-milk-almonds\": 99,\n              \"sees-milk-beverly\": 73,\n              \"sees-milk-bordeaux\": 100,\n              \"sees-milk-butterscotch-square\": 74,\n              \"sees-milk-california-brittle\": 77,\n              \"sees-milk-chelsea\": 76,\n              \"sees-milk-chocolate-buttercream\": 75,\n              \"sees-milk-coconut-cream\": 72,\n              \"sees-milk-mayfair\": 73,\n              \"sees-milk-mocha\": 83,\n              \"sees-milk-molasses-chips\": 75,\n              \"sees-milk-rum-nougat\": 80},\n  \"name\": \"valentines-chocolate\"},\n {\"category\": \"real world\",\n  \"classes\": {\"aair\": 1443,\n              \"boal\": 1283,\n              \"chapila\": 342,\n              \"deshi puti\": 346,\n              \"foli\": 505,\n              \"ilish\": 806,\n              \"kal baush\": 772,\n              \"katla\": 1355,\n              \"koi\": 651,\n              \"magur\": 462,\n              \"mrigel\": 1429,\n              \"pabda\": 1379,\n              \"pangas\": 749,\n              \"puti\": 1257,\n              \"rui\": 2102,\n              \"shol\": 1113,\n              \"taki\": 1755,\n              \"tara baim\": 860,\n              \"telapiya\": 209},\n  \"name\": \"fish-market-ggjso\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Ready\": 2009,\n              \"empty_pod\": 1593,\n              \"germination\": 6604,\n              \"pod\": 3594,\n              \"young\": 5922},\n  \"name\": \"lettuce-pallets\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Lower\": 18,\n              \"Sand Tiger Shark\": 194,\n              \"Snaggletooth Shark\": 22,\n              \"Upper\": 46},\n  \"name\": \"shark-teeth-5atku\"},\n {\"category\": \"real world\", \"classes\": {\"bees\": 9756}, \"name\": \"bees-jt5in\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Cross bedding\": 332,\n              \"Low angle\": 583,\n              \"Massive\": 2011,\n              \"Parallel lamination\": 671,\n              \"mud drape\": 885},\n  \"name\": \"sedimentary-features-9eosf\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Dime\": 1201,\n              \"Nickel\": 1478,\n              \"Penny\": 2249,\n              \"Quarter\": 870,\n              \"fifty\": 83,\n              \"five\": 92,\n              \"hundred\": 46,\n              \"one\": 80,\n              \"ten\": 88,\n              \"twenty\": 94},\n  \"name\": \"currency-v4f8j\"},\n {\"category\": \"real world\",\n  \"classes\": {\"Deer\": 888, \"Hog\": 1398},\n  \"name\": \"trail-camera\"},\n {\"category\": \"real world\",\n  \"classes\": {\"joint\": 4147, \"side\": 417},\n  \"name\": \"cell-towers\"},\n {\"category\": \"videogames\",\n  \"classes\": {\"avatar\": 3782, \"object\": 602},\n  \"name\": \"apex-videogame\"},\n {\"category\": \"videogames\",\n  \"classes\": {\"assassin\": 100,\n              \"atv\": 1,\n              \"car\": 2,\n              \"gun\": 30,\n              \"gun menu\": 1,\n              \"healthbar\": 6,\n              \"horse\": 6,\n              \"hud\": 9,\n              \"map\": 29,\n              \"person\": 44,\n              \"surroundings\": 13},\n  \"name\": \"farcry6-videogame\"},\n {\"category\": \"videogames\",\n  \"classes\": {\"CT\": 1351, \"T\": 1663},\n  \"name\": \"csgo-videogame\"},\n {\"category\": \"videogames\",\n  \"classes\": {\"Character\": 903},\n  \"name\": \"avatar-recognition-nuexe\"},\n {\"category\": \"videogames\",\n  \"classes\": {\"enemy\": 556,\n              \"enemy-head\": 484,\n              \"friendly\": 135,\n              \"friendly-head\": 57},\n  \"name\": \"halo-infinite-angel-videogame\"},\n {\"category\": \"videogames\",\n  \"classes\": {\"Akali\": 35,\n              \"Blitzcrank\": 64,\n              \"Braum\": 53,\n              \"Caitlyn\": 50,\n              \"Camille\": 50,\n              \"Cho-Gath\": 55,\n              \"Darius\": 56,\n              \"Dr- Mundo\": 47,\n              \"Ekko\": 40,\n              \"Ezreal\": 76,\n              \"Fiora\": 38,\n              \"Galio\": 43,\n              \"Gankplank\": 67,\n              \"Garen\": 44,\n              \"Graves\": 41,\n              \"Heimerdinger\": 62,\n              \"Illaoi\": 50,\n              \"Janna\": 76,\n              \"Jayce\": 58,\n              \"Jhin\": 38,\n              \"Jinx\": 37,\n              \"Kai-Sa\": 49,\n              \"Kassadin\": 52,\n              \"Katarina\": 54,\n              \"Kog-Maw\": 49,\n              \"Leona\": 48,\n              \"Lissandra\": 46,\n              \"Lulu\": 52,\n              \"Lux\": 45,\n              \"Malzahar\": 51,\n              \"Miss Fortune\": 49,\n              \"Orianna\": 44,\n              \"Poppy\": 57,\n              \"Quinn\": 46,\n              \"Samira\": 45,\n              \"Seraphine\": 39,\n              \"Shaco\": 48,\n              \"Singed\": 82,\n              \"Sion\": 39,\n              \"Swain\": 62,\n              \"Tahm Kench\": 43,\n              \"Talon\": 41,\n              \"Taric\": 38,\n              \"Tristana\": 68,\n              \"Trundle\": 67,\n              \"Twisted Fate\": 53,\n              \"Twitch\": 56,\n              \"Urgot\": 48,\n              \"Veigar\": 50,\n              \"Vex\": 60,\n              \"Vi\": 75,\n              \"Viktor\": 37,\n              \"Warwick\": 61,\n              \"Yone\": 40,\n              \"Yuumi\": 48,\n              \"Zac\": 57,\n              \"Ziggs\": 59,\n              \"Zilean\": 59,\n              \"Zyra\": 41},\n  \"name\": \"team-fight-tactics\"},\n {\"category\": \"videogames\",\n  \"classes\": {\"armor\": 4598,\n              \"base\": 1622,\n              \"car\": 2538,\n              \"rune\": 3,\n              \"rune-blue\": 263,\n              \"rune-gray\": 19,\n              \"rune-grey\": 16,\n              \"rune-red\": 449,\n              \"watcher\": 1300},\n  \"name\": \"robomasters-285km\"},\n {\"category\": \"documents\",\n  \"classes\": {\"caption\": 136, \"tweet\": 139},\n  \"name\": \"tweeter-posts\"},\n {\"category\": \"documents\",\n  \"classes\": {\"profile_info\": 738},\n  \"name\": \"tweeter-profile\"},\n {\"category\": \"documents\",\n  \"classes\": {\"table\": 1152, \"title\": 564},\n  \"name\": \"document-parts\"},\n {\"category\": \"documents\",\n  \"classes\": {\"action\": 903,\n              \"activity\": 1286,\n              \"commeent\": 67,\n              \"control_flow\": 4177,\n              \"control_flowcontrol_flow\": 12,\n              \"decision_node\": 449,\n              \"exit_node\": 7,\n              \"final_flow_node\": 16,\n              \"final_node\": 364,\n              \"fork\": 160,\n              \"merge\": 157,\n              \"merge_noode\": 124,\n              \"null\": 3,\n              \"object\": 39,\n              \"object_flow\": 14,\n              \"signal_recept\": 26,\n              \"signal_send\": 17,\n              \"start_node\": 360,\n              \"text\": 462},\n  \"name\": \"activity-diagrams-qdobr\"},\n {\"category\": \"documents\",\n  \"classes\": {\"signature\": 443},\n  \"name\": \"signatures-xc8up\"},\n {\"category\": \"documents\",\n  \"classes\": {\"author\": 162,\n              \"chapter\": 642,\n              \"equation\": 3136,\n              \"equation number\": 1696,\n              \"figure\": 3106,\n              \"figure caption\": 2820,\n              \"footnote\": 781,\n              \"list of content heading\": 165,\n              \"list of content text\": 325,\n              \"page number\": 11833,\n              \"paragraph\": 15070,\n              \"reference text\": 994,\n              \"section\": 1716,\n              \"subsection\": 1977,\n              \"subsubsection\": 1013,\n              \"table\": 1185,\n              \"table caption\": 953,\n              \"table of contents text\": 227,\n              \"title\": 175},\n  \"name\": \"paper-parts\"},\n {\"category\": \"documents\",\n  \"classes\": {\"-\": 2,\n              \"bold_parent_row\": 2707,\n              \"bold_row\": 1011,\n              \"closure_row\": 6964,\n              \"column\": 15757,\n              \"direct_children\": 6632,\n              \"non_bold_parent_row\": 2834,\n              \"non_bold_row\": 15099,\n              \"parent_column\": 999,\n              \"prime_parent\": 972,\n              \"sub_row\": 1581,\n              \"table\": 4076},\n  \"name\": \"tabular-data-wf9uh\"},\n {\"category\": \"documents\",\n  \"classes\": {\"-\": 2, \"g\": 2091, \"g1\": 1, \"g3\": 3, \"h\": 1, \"m\": 45252, \"n\": 50},\n  \"name\": \"paragraphs-co84b\"},\n {\"category\": \"underwater\",\n  \"classes\": {\"pipe\": 12238},\n  \"name\": \"underwater-pipes-4ng4t\"},\n {\"category\": \"underwater\",\n  \"classes\": {\"fish\": 2673,\n              \"jellyfish\": 694,\n              \"penguin\": 516,\n              \"puffin\": 284,\n              \"shark\": 354,\n              \"starfish\": 116,\n              \"stingray\": 184},\n  \"name\": \"aquarium-qlnqy\"},\n {\"category\": \"underwater\",\n  \"classes\": {\"peix\": 12376,\n              \"taca\": 728},\n  \"name\": \"peixos-fish\"},\n {\"category\": \"underwater\",\n  \"classes\": {\"echinus\": 25299,\n              \"holothurian\": 6584,\n              \"scallop\": 10485,\n              \"starfish\": 10270,\n              \"waterweeds\": 46},\n  \"name\": \"underwater-objects-5v7p8\"},\n {\"category\": \"underwater\",\n  \"classes\": {\"Arborescent\": 687,\n              \"Caespitose-a\": 367,\n              \"Caespitose-b\": 305,\n              \"Columnar\": 1,\n              \"Corymbose\": 381,\n              \"Digitate\": 392,\n              \"Encrusting\": 379,\n              \"Foliose\": 562,\n              \"Massive-Faviidae\": 702,\n              \"Massive-Merulinidae\": 192,\n              \"Massive-Mussidae\": 319,\n              \"Massive-Poritidae\": 1016,\n              \"Solitary\": 462,\n              \"Tabular\": 718},\n  \"name\": \"coral-lwptl\"},\n {\"category\": \"aerial\",\n  \"classes\": {\"black-hat\": 2538,\n              \"bodysurface\": 6599,\n              \"bodyunder\": 1333,\n              \"umpire\": 548,\n              \"white-hat\": 2202},\n  \"name\": \"aerial-pool\"},\n {\"category\": \"aerial\", \"classes\": {\"chain\": 1427}, \"name\": \"secondary-chains\"},\n {\"category\": \"aerial\",\n  \"classes\": {\"green_sphero\": 262,\n              \"orange-sphero\": 1,\n              \"orange_sphero\": 458,\n              \"purple_sphero\": 263,\n              \"red_sphero\": 416,\n              \"yellow_sphero\": 463},\n  \"name\": \"aerial-spheres\"},\n {\"category\": \"aerial\",\n  \"classes\": {\"football\": 1766, \"player\": 96, \"referee\": 141},\n  \"name\": \"soccer-players-5fuqs\"},\n {\"category\": \"aerial\",\n  \"classes\": {\"crop\": 411, \"weed\": 7442},\n  \"name\": \"weed-crop-aerial\"},\n {\"category\": \"aerial\", \"classes\": {\"cow\": 15860}, \"name\": \"aerial-cows\"},\n {\"category\": \"aerial\",\n  \"classes\": {\"Fish\": 2528, \"Flower\": 2141, \"Gravel\": 2674, \"Sugar\": 3408},\n  \"name\": \"cloud-types\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"close\": 3629, \"open\": 9883},\n  \"name\": \"stomata-cells\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"Platelets\": 361, \"RBC\": 4155, \"WBC\": 372},\n  \"name\": \"bccd-ouzjz\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"Ancylostoma Spp\": 680,\n              \"Ascaris Lumbricoides\": 676,\n              \"Enterobius Vermicularis\": 912,\n              \"Fasciola Hepatica\": 492,\n              \"Hymenolepis\": 472,\n              \"Schistosoma\": 569,\n              \"Taenia Sp\": 654,\n              \"Trichuris Trichiura\": 636},\n  \"name\": \"parasites-1s07h\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"celula\": 2258},\n  \"name\": \"cells-uyemf\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"4-fold defect\": 12025},\n  \"name\": \"4-fold-defect\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"Str_pne\": 2943},\n  \"name\": \"bacteria-ptywi\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"dc\": 6312},\n  \"name\": \"cotton-plant-disease\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"Mitosis\": 436},\n  \"name\": \"mitosis-gjs3g\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"activated\": 906, \"non-activated\": 31359},\n  \"name\": \"phages\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"ballooning\": 2187,\n              \"fibrosis\": 1804,\n              \"inflammation\": 2734,\n              \"steatosis\": 3052},\n  \"name\": \"liver-disease\"},\n {\"category\": \"microscopic\",\n  \"classes\": {\"thick-dark-mark\": 1752,\n              \"thick-light-mark\": 5911,\n              \"thin-dark-mark\": 971,\n              \"thin-light-mark\": 817},\n  \"name\": \"asbestos\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"dog\": 117, \"person\": 140},\n  \"name\": \"thermal-dogs-and-people-x6ejw\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"Cell\": 513,\n              \"Cell-Multi\": 282,\n              \"No-Anomaly\": 6865,\n              \"Shadowing\": 1054,\n              \"Unclassified\": 333},\n  \"name\": \"solar-panels-taxvb\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"stray\": 1775, \"target\": 1348},\n  \"name\": \"radio-signal\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"cheetah\": 186, \"human\": 45},\n  \"name\": \"thermal-cheetah-my4dp\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"artefact\": 6,\n              \"distal phalanges\": 974,\n              \"fifth metacarpal bone\": 193,\n              \"first metacarpal bone\": 189,\n              \"fourth metacarpal bone\": 192,\n              \"intermediate phalanges\": 773,\n              \"proximal phalanges\": 968,\n              \"radius\": 185,\n              \"second metacarpal bone\": 199,\n              \"soft tissue calcination\": 98,\n              \"third metacarpal bone\": 194,\n              \"ulna\": 184},\n  \"name\": \"x-ray-rheumatology\"},\n {\"category\": \"electromagnetic\", \"classes\": {\"acl\": 3059}, \"name\": \"acl-x-ray\"},\n {\"category\": \"electromagnetic\", \"classes\": {\"0\": 2645}, \"name\": \"abdomen-mri\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"negative\": 204, \"positive\": 186},\n  \"name\": \"axial-mri\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"6W\": 6, \"7W\": 1, \"EH\": 2895},\n  \"name\": \"gynecology-mri\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"label0\": 6214, \"label1\": 9778, \"label2\": 5896},\n  \"name\": \"brain-tumor-m2pbp\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"angle\": 41, \"fracture\": 326, \"line\": 164, \"messed_up_angle\": 70},\n  \"name\": \"bone-fracture-7fylg\"},\n {\"category\": \"electromagnetic\",\n  \"classes\": {\"bicycle\": 4458, \"car\": 47501, \"dog\": 240, \"person\": 31872},\n  \"name\": \"flir-camera-objects\"}]\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/log_extract.py",
    "content": "import argparse\nimport csv\nimport json\nimport os\nimport re\n\nimport numpy as np\nimport pandas as pd\nfrom openpyxl import load_workbook\nfrom openpyxl.styles import Alignment\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='log_name')\n    parser.add_argument(\n        'method', type=str, help='method name, used in csv/xlsx header')\n    parser.add_argument(\n        '--epoch',\n        type=int,\n        default=25,\n        required=False,\n        help='train_epoch, used for checking whether training completed')\n    parser.add_argument(\n        '--work-dirs',\n        type=str,\n        default='work_dirs/',\n        required=False,\n        help='directory for saving results')\n    parser.add_argument(\n        '--origin',\n        type=str,\n        default=False,\n        required=False,\n        help='excel with datasets in the order of execution ')\n    args = parser.parse_args()\n\n    return args\n\n\ndef write_csv(datas, args):\n    num = 0\n    fail_num = 0\n    none_exist_num = 0\n    fail = []\n    none_exist = []\n    latest_time = 0\n    with open('scripts/labels_names.json') as f:\n        label = json.load(f)\n    for dataset in sorted(os.listdir(datas)):\n        print(f'\\ndataset={dataset}, index={num}')\n        num += 1\n        with open(\n                os.path.join(datas, dataset, 'train/_annotations.coco.json'),\n                'r') as f:\n            image = json.load(f)\n            num_train = len(image['images'])  # get number of train images\n        with open(\n                os.path.join(datas, dataset, 'valid/_annotations.coco.json'),\n                'r') as f:\n            image = json.load(f)\n            num_valid = len(image['images'])  # get number of valid images\n        for index in label:\n            if index['name'] == dataset:\n                category = index['category']  # get category of dataset\n                class_num = len(index['classes'].keys())\n\n        # determine whether the dataset directory exists\n        try:\n            dirs = [\n                os.path.join(args.work_dirs, dataset, d)\n                for d in os.listdir(os.path.join(args.work_dirs, dataset))\n                if os.path.isdir(os.path.join(args.work_dirs, dataset, d))\n            ]\n            dirs.sort(key=os.path.getmtime)\n\n            latest_dir = dirs[-1]\n            latest_log_name = latest_dir.split('/')[-1]\n            if int(latest_log_name) > int(latest_time):\n                latest_time = latest_log_name\n            print('time=' + latest_log_name)\n            latest_log = latest_dir + f'/{latest_log_name}.log'\n            with open(latest_log, 'r') as f:\n                log = f.read()\n            print(latest_log)\n\n            complete_flag = re.findall(\n                r'Epoch\\(val\\) \\[{}\\]\\[\\d+/\\d+\\]'.format(args.epoch),\n                log)  # find log of args.epoch's validating process\n\n            # Check whether the training is complete\n            if not complete_flag:\n                fail_num += 1\n                fail.append(dataset)\n                print('-------------------------------------')\n                print(f'{dataset} train failed!')\n                print(f'{fail_num} dataset failed!')\n                print('-------------------------------------')\n                key_value = [\n                    dataset, category, class_num, num_train, num_valid, '', '',\n                    '', '', ''\n                ]\n            else:\n                \"\"\"match result.\"\"\"\n                match_all = re.findall(\n                    r'The best checkpoint with ([\\d.]+) '\n                    r'coco/bbox_mAP at ([\\d.]+) epoch', log)\n                if match_all:\n                    match = match_all[-1]\n                    best_epoch = match[-1]\n                    print(f'best_epoch={best_epoch}')\n                    # find best result\n                    match_AP = re.findall(\n                        r'\\[{}\\]\\[\\d+/\\d+\\]    coco/bbox_mAP: (-?\\d+\\.?\\d*)  coco/bbox_mAP_50: (-?\\d+\\.?\\d*)  coco/bbox_mAP_75: -?\\d+\\.?\\d*  coco/bbox_mAP_s: (-?\\d+\\.?\\d*)  coco/bbox_mAP_m: (-?\\d+\\.?\\d*)  coco/bbox_mAP_l: (-?\\d+\\.?\\d*)'  # noqa\n                        .format(best_epoch),\n                        log)\n                    print(f'match_AP={match_AP}')\n\n                    key_value = [\n                        dataset, category, class_num, num_train, num_valid\n                    ]\n                    key_value.extend(match_AP[0])\n                else:\n                    print('----------------- --------------------------')\n                    print('log has no result!')\n                    print('----------------------------------------------')\n                    key_value = [\n                        dataset, category, class_num, num_train, num_valid, '',\n                        '', '', '', ''\n                    ]\n        except RuntimeError:\n            print(f\"{dataset} directory doesn't exist!\")\n            none_exist_num += 1\n            none_exist.append(dataset)\n            key_value = [\n                dataset, category, num_train, num_valid, '', '', '', '', ''\n            ]\n\n        if num == 1:  # generate headers\n            result_csv = os.path.join(args.work_dirs,\n                                      f'{latest_log_name}_eval.csv')\n            print(result_csv)\n            with open(result_csv, mode='w') as f:\n                writer = csv.writer(f)\n                header1 = [\n                    'Dataset', 'Category', 'Classes', 'Images', 'Images',\n                    args.method, args.method, args.method, args.method,\n                    args.method\n                ]\n                writer.writerow(header1)\n            with open(result_csv, mode='a') as f:\n                writer = csv.writer(f)\n                header2 = [\n                    'Dataset', 'Category', 'Classes', 'train', 'valid', 'mAP',\n                    'mAP50', 'mAP_s', 'mAP_m', 'mAP_l'\n                ]\n                writer.writerow(header2)\n                writer.writerow(key_value)\n\n        else:\n            with open(result_csv, mode='a') as f:\n                writer = csv.writer(f)\n                writer.writerow(key_value)\n\n    return result_csv, fail, fail_num, \\\n        none_exist, none_exist_num, os.path.join(\n         args.work_dirs, latest_time[4:])\n\n\ndef wb_align(file, pair_ls):\n    # adjust format of .xlsx file\n    wb = load_workbook(file)\n    ws = wb.active\n    for pair in pair_ls:\n        ws.merge_cells(f'{pair[0]}:{pair[1]}')\n        ws[f'{pair[0]}'].alignment = Alignment(\n            horizontal='center', vertical='center')\n    wb.save(file)\n\n\ndef sort_excel(in_csv, out_xlsx):\n    # read csv with two headers then convert it to xlsx,\n    # sort it by category name & dataset name\n    df = pd.read_csv(in_csv)\n    df_sorted = df.iloc[1:].sort_values(by=['Category', 'Dataset'])\n    df_sort = pd.concat([df.iloc[:1], df_sorted])\n    df_sort.to_excel(out_xlsx, index=False)\n\n\ndef sum_excel(in_csv, out_xlsx):\n    # read csv with two headers then convert it to xlsx,\n    # get total number of train&valid images and mean of results\n    df = pd.read_csv(in_csv)\n    df.insert(2, 'dataset', pd.Series([]))\n    df = df.iloc[:, 1:]\n    average = df.iloc[1:].groupby('Category')  # group by category name\n    df_new = df.iloc[0:1, :]\n    num = 0\n    for key, value in average:\n        num += 1\n        df_cate = [key]\n        for i in range(1, 10):\n            if i == 1:\n                df_cate.append(len(value))\n            elif i != 1 and i < 5:\n                df_cate.append(value.iloc[:, i].astype(float).sum())\n            else:\n                # import pdb; pdb.set_trace()\n                df_cate.append(\n                    format(\n                        value.iloc[:, i].astype(float).replace(\n                            '', np.nan).replace(-1.0000, np.nan).mean(),\n                        '.4f'))\n\n        # import pdb;pdb.set_trace()\n        df_new.loc[len(df_new)] = df_cate\n\n    df_cate = ['total']  # final row = 'total'\n    for i in range(1, 10):\n        if i < 5:\n            df_cate.append(df_new.iloc[1:, i].astype(float).sum())\n        else:\n            df_cate.append(\n                format(\n                    df_new.iloc[1:, i].astype(float).replace('',\n                                                             np.nan).mean(),\n                    '.4f'))\n    df_new.loc[len(df_new) + 1] = df_cate\n    df_new.to_excel(out_xlsx, float_format='%.4f', index=False)\n\n\ndef main():\n    args = parse_args()\n\n    result_csv, fail, fail_num, none_exist, \\\n        none_exist_num, latest_time = write_csv('rf100/', args)\n\n    os.rename(result_csv, latest_time + '_eval.csv')\n    result_csv = latest_time + '_eval.csv'\n\n    # write excel in the order of execution\n    if args.origin:\n        df = pd.read_csv(result_csv)\n        result_xlsx_detail = '{}_origin.xlsx'.format(latest_time)\n        if os.path.exists(result_xlsx_detail):\n            os.remove(result_xlsx_detail)\n        print(f'\\n{result_xlsx_detail} created!\\n')\n        df.to_excel(result_xlsx_detail)\n        wb_align(result_xlsx_detail, [['E1', 'F1'], ['G1', 'K1']])\n\n    # write excel in the order of category&dataset name\n    result_xlsx_sort = '{}_detail.xlsx'.format(latest_time)\n    result_xlsx_sum = '{}_sum.xlsx'.format(latest_time)\n    if os.path.exists(result_xlsx_sum):\n        os.remove(result_xlsx_sum)\n\n    # sortec by category name\n    sort_excel(result_csv, result_xlsx_sort)\n    wb_align(result_xlsx_sort, [['D1', 'E1'], ['F1', 'J1']])\n\n    # sum of each category\n    sum_excel(result_csv, result_xlsx_sum)\n    wb_align(\n        result_xlsx_sum,\n        [['A1', 'A2'], ['B1', 'B2'], ['C1', 'C2'], ['D1', 'E1'], ['F1', 'J1']])\n\n    # save fail\n    print(f'sum_file = {result_xlsx_sum}')\n    ''' generate .txt file '''\n    print(f'{none_exist_num} datasets were not trained:\\n{none_exist}\\n')\n    print(f'{fail_num} training failed:\\n{fail}\\n')\n\n    fail_txt = os.path.join(args.work_dirs, 'failed_dataset_list.txt')\n    with open(fail_txt, 'w') as f:\n        pass\n    with open(fail_txt, 'a') as f:\n        for item in none_exist:\n            f.write(f'{item}\\n')\n        for item in fail:\n            f.write(f'{item}\\n')\n\n    print(f'all {fail_num + none_exist_num} untrained datasets '\n          f'have been logged in {fail_txt}!')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/parse_dataset_link.py",
    "content": "import re\nfrom argparse import ArgumentParser\n\n\ndef main():\n    parser = ArgumentParser(\n        description='A handy script that will decompose and print from '\n        \"a roboflow dataset link it's workspace, project and version\")\n    parser.add_argument(\n        '-l', '--link', required=True, help='A link to a roboflow dataset')\n    args = vars(parser.parse_args())\n    # first one gonna be protocol, e.g. http\n    _, url, workspace, project, version = re.split('/+', args['link'])\n    print(url, workspace, project, version)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/RF100-Benchmark/scripts/slurm_train.sh",
    "content": "#!/usr/bin/env bash\n\nCONFIG=$1\nGPUS=$2\nWORK_DIRS=${3:-'work_dirs'}\nRETRY_PATH=${RETRY_PATH:-''}\nNNODES=${NNODES:-1}\nNODE_RANK=${NODE_RANK:-0}\nPORT=${PORT:-29500}\nMASTER_ADDR=${MASTER_ADDR:-\"127.0.0.1\"}\nPARTITION=${PARTITION:-'mm_dev'}\nJOB_NAME=${JOB_NAME:-'benchmark'}\nGPUS_PER_NODE=${GPUS_PER_NODE:-8}\nCPUS_PER_TASK=${CPUS_PER_TASK:-5}\nSRUN_ARGS=${SRUN_ARGS:-\"\"}\n\ndatasets=$(pwd)/rf100\nexport PYTHONPATH=\"../..\":$PYTHONPATH\n\nDEBUG=0\n# example\ndatasets_list=('bacteria-ptywi', 'circuit-elements', 'marbles', 'printed-circuit-board', 'solar-panels-taxvb')\n\nif [ -n \"$RETRY_PATH\" ]; then\n    DEBUG=1\n    datasets_list=()\n    while IFS= read -r line; do\n      if [ -n \"$line\" ]; then\n          datasets_list+=(\"$line\")\n      fi\n    done < \"$RETRY_PATH\"\nfi\n\nif [ \"$DEBUG\" == 1 ]; then\n  echo \"current training dataset list is: ${datasets_list[@]}\"\nelse\n  echo \"Currently training with the full dataset.\"\nfi\necho \"==============================================\"\n\nfor dataset in $(ls $datasets)\n  do\n    # You can customize string_list to train only specific datasets.\n    if [ \"$DEBUG\" == 1 ]; then\n      if [[ ! \" ${datasets_list[@]} \" =~ \"$dataset\" ]]; then\n        continue\n      fi\n    fi\n\n    echo \"Training on $dataset\"\n    python $(pwd)/scripts/create_new_config.py $CONFIG $dataset\n\n    srun -p ${PARTITION} \\\n    --job-name=${JOB_NAME} \\\n    --gres=gpu:${GPUS_PER_NODE} \\\n    --ntasks=${GPUS} \\\n    --ntasks-per-node=${GPUS_PER_NODE} \\\n    --cpus-per-task=${CPUS_PER_TASK} \\\n    --kill-on-bad-exit=1 \\\n    ${SRUN_ARGS} \\\n    python -u  ../../tools/train.py \"temp_configs/$dataset.py\"  --work-dir=\"$WORK_DIRS/$dataset\" --launcher=\"slurm\" ${@:4}\n\n    echo \"==============================================\"\n  done\n\n#rm -rf temp_configs\necho \"Done training all the datasets\"\n"
  },
  {
    "path": "projects/SparseInst/README.md",
    "content": "<div align=\"center\">\n<img src=\"https://github.com/hustvl/SparseInst/raw/main/assets/banner.gif\">\n<br>\n<br>\nTianheng Cheng, <a href=\"https://xinggangw.info/\">Xinggang Wang</a><sup><span>&#8224;</span></sup>, Shaoyu Chen, Wenqiang Zhang, <a href=\"https://scholar.google.com/citations?user=pCY-bikAAAAJ&hl=zh-CN\">Qian Zhang</a>, <a href=\"https://scholar.google.com/citations?user=IyyEKyIAAAAJ&hl=zh-CN\">Chang Huang</a>, <a href=\"https://zhaoxiangzhang.net/\">Zhaoxiang Zhang</a>, <a href=\"http://eic.hust.edu.cn/professor/liuwenyu/\"> Wenyu Liu</a>\n</br>\n(<span>&#8224;</span>: corresponding author)\n<div>\n<a href=\"https://arxiv.org/abs/2203.12827\">[arXiv paper]</a>\n<a href=\"https://openaccess.thecvf.com/content/CVPR2022/papers/Cheng_Sparse_Instance_Activation_for_Real-Time_Instance_Segmentation_CVPR_2022_paper.pdf\">[CVPR paper]</a>\n<a href=\"https://drive.google.com/file/d/1xhqQvQ0YVCHd8XQxnCVqef75Hey7kI-d/view?usp=sharing\">[slides]</a>\n</div>\n</div>\n\n## Description\n\nThis is an implementation of [SparseInst](https://github.com/hustvl/SparseInst) based on [MMDetection](https://github.com/open-mmlab/mmdetection/tree/main), [MMCV](https://github.com/open-mmlab/mmcv), and [MMEngine](https://github.com/open-mmlab/mmengine).\n\n**SparseInst** is a conceptually novel, efficient, and fully convolutional framework for real-time instance segmentation.\nIn contrast to region boxes or anchors (centers), SparseInst adopts a sparse set of **instance activation maps** as object representation, to highlight informative regions for each foreground objects.\nThen it obtains the instance-level features by aggregating features according to the highlighted regions for recognition and segmentation.\nThe bipartite matching compels the instance activation maps to predict objects in a one-to-one style, thus avoiding non-maximum suppression (NMS) in post-processing. Owing to the simple yet effective designs with instance activation maps, SparseInst has extremely fast inference speed and achieves **40 FPS** and **37.9 AP** on COCO (NVIDIA 2080Ti), significantly outperforms the counter parts in terms of speed and accuracy.\n\n<center>\n<img src=\"https://github.com/hustvl/SparseInst/raw/main/assets/sparseinst.png\">\n</center>\n\n## Usage\n\n<!-- For a typical model, this section should contain the commands for training and testing. You are also suggested to dump your environment specification to env.yml by `conda env export > env.yml`. -->\n\n### Training commands\n\nIn MMDetection's root directory, run the following command to train the model:\n\n```bash\npython tools/train.py projects/SparseInst/configs/sparseinst_r50_iam_8xb8-ms-270k_coco.py\n```\n\nFor multi-gpu training, run:\n\n```bash\npython -m torch.distributed.launch --nnodes=1 --node_rank=0 --nproc_per_node=${NUM_GPUS} --master_port=29506 --master_addr=\"127.0.0.1\" tools/train.py projects/SparseInst/configs/sparseinst_r50_iam_8xb8-ms-270k_coco.py\n```\n\n### Testing commands\n\nIn MMDetection's root directory, run the following command to test the model:\n\n```bash\npython tools/test.py projects/SparseInst/configs/sparseinst_r50_iam_8xb8-ms-270k_coco.py ${CHECKPOINT_PATH}\n```\n\n## Results\n\nHere we provide the baseline version of SparseInst with ResNet50 backbone.\n\nTo find more variants, please visit the [official model zoo](https://github.com/hustvl/SparseInst#models).\n\n| Backbone |  Style  | Lr schd | Mem (GB) | FPS  | mask AP val2017 |                           Config                            |                                                                                                                                                                    Download                                                                                                                                                                    |\n| :------: | :-----: | :-----: | :------: | :--: | :-------------: | :---------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n|   R-50   | PyTorch |  270k   |   8.7    | 44.3 |      32.9       | [config](./configs/sparseinst_r50_iam_8xb8-ms-270k_coco.py) | [model](https://download.openmmlab.com/mmdetection/v3.0/sparseinst/sparseinst_r50_iam_8xb8-ms-270k_coco/sparseinst_r50_iam_8xb8-ms-270k_coco_20221111_181051-72c711cd.pth) \\| [log](https://download.openmmlab.com/mmdetection/v3.0/sparseinst/sparseinst_r50_iam_8xb8-ms-270k_coco/sparseinst_r50_iam_8xb8-ms-270k_coco_20221111_181051.json) |\n\n## Citation\n\nIf you find SparseInst is useful in your research or applications, please consider giving a star 🌟 to the [official repository](https://github.com/hustvl/SparseInst) and citing SparseInst by the following BibTeX entry.\n\n```BibTeX\n@inproceedings{Cheng2022SparseInst,\n  title     =   {Sparse Instance Activation for Real-Time Instance Segmentation},\n  author    =   {Cheng, Tianheng and Wang, Xinggang and Chen, Shaoyu and Zhang, Wenqiang and Zhang, Qian and Huang, Chang and Zhang, Zhaoxiang and Liu, Wenyu},\n  booktitle =   {Proc. IEEE Conf. Computer Vision and Pattern Recognition (CVPR)},\n  year      =   {2022}\n}\n\n```\n\n## Checklist\n\n<!-- Here is a checklist illustrating a usual development workflow of a successful project, and also serves as an overview of this project's progress. The PIC (person in charge) or contributors of this project should check all the items that they believe have been finished, which will further be verified by codebase maintainers via a PR.\nOpenMMLab's maintainer will review the code to ensure the project's quality. Reaching the first milestone means that this project suffices the minimum requirement of being merged into 'projects/'. But this project is only eligible to become a part of the core package upon attaining the last milestone.\nNote that keeping this section up-to-date is crucial not only for this project's developers but the entire community, since there might be some other contributors joining this project and deciding their starting point from this list. It also helps maintainers accurately estimate time and effort on further code polishing, if needed.\nA project does not necessarily have to be finished in a single PR, but it's essential for the project to at least reach the first milestone in its very first PR. -->\n\n- [x] Milestone 1: PR-ready, and acceptable to be one of the `projects/`.\n\n  - [x] Finish the code\n\n    <!-- The code's design shall follow existing interfaces and convention. For example, each model component should be registered into `mmdet.registry.MODELS` and configurable via a config file. -->\n\n  - [x] Basic docstrings & proper citation\n\n    <!-- Each major object should contain a docstring, describing its functionality and arguments. If you have adapted the code from other open-source projects, don't forget to cite the source project in docstring and make sure your behavior is not against its license. Typically, we do not accept any code snippet under GPL license. [A Short Guide to Open Source Licenses](https://medium.com/nationwide-technology/a-short-guide-to-open-source-licenses-cf5b1c329edd) -->\n\n  - [x] Test-time correctness\n\n    <!-- If you are reproducing the result from a paper, make sure your model's inference-time performance matches that in the original paper. The weights usually could be obtained by simply renaming the keys in the official pre-trained weights. This test could be skipped though, if you are able to prove the training-time correctness and check the second milestone. -->\n\n  - [x] A full README\n\n    <!-- As this template does. -->\n\n- [x] Milestone 2: Indicates a successful model implementation.\n\n  - [x] Training-time correctness\n\n    <!-- If you are reproducing the result from a paper, checking this item means that you should have trained your model from scratch based on the original paper's specification and verified that the final result matches the report within a minor error range. -->\n\n- [ ] Milestone 3: Good to be a part of our core package!\n\n  - [ ] Type hints and docstrings\n\n    <!-- Ideally *all* the methods should have [type hints](https://www.pythontutorial.net/python-basics/python-type-hints/) and [docstrings](https://google.github.io/styleguide/pyguide.html#381-docstrings). [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/mmdet/datasets/transforms/transforms.py#L41-L169) -->\n\n  - [ ] Unit tests\n\n    <!-- Unit tests for each module are required. [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/tests/test_datasets/test_transforms/test_transforms.py#L35-L88) -->\n\n  - [ ] Code polishing\n\n    <!-- Refactor your code according to reviewer's comment. -->\n\n  - [ ] Metafile.yml\n\n    <!-- It will be parsed by MIM and Inferencer. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/metafile.yml) -->\n\n- [ ] Move your modules into the core package following the codebase's file hierarchy structure.\n\n  <!-- In particular, you may have to refactor this README into a standard one. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/README.md) -->\n\n- [ ] Refactor your modules into the core package following the codebase's file hierarchy structure.\n"
  },
  {
    "path": "projects/SparseInst/configs/sparseinst_r50_iam_8xb8-ms-270k_coco.py",
    "content": "_base_ = [\n    'mmdet::_base_/datasets/coco_instance.py',\n    'mmdet::_base_/schedules/schedule_1x.py',\n    'mmdet::_base_/default_runtime.py'\n]\n\ncustom_imports = dict(\n    imports=['projects.SparseInst.sparseinst'], allow_failed_imports=False)\n\nmodel = dict(\n    type='SparseInst',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_mask=True,\n        pad_size_divisor=32),\n    backbone=dict(\n        type='ResNet',\n        depth=50,\n        num_stages=4,\n        out_indices=(1, 2, 3),\n        frozen_stages=0,\n        norm_cfg=dict(type='BN', requires_grad=False),\n        norm_eval=True,\n        style='pytorch',\n        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),\n    encoder=dict(\n        type='InstanceContextEncoder',\n        in_channels=[512, 1024, 2048],\n        out_channels=256),\n    decoder=dict(\n        type='BaseIAMDecoder',\n        in_channels=256 + 2,\n        num_classes=80,\n        ins_dim=256,\n        ins_conv=4,\n        mask_dim=256,\n        mask_conv=4,\n        kernel_dim=128,\n        scale_factor=2.0,\n        output_iam=False,\n        num_masks=100),\n    criterion=dict(\n        type='SparseInstCriterion',\n        num_classes=80,\n        assigner=dict(type='SparseInstMatcher', alpha=0.8, beta=0.2),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            alpha=0.25,\n            gamma=2.0,\n            reduction='sum',\n            loss_weight=2.0),\n        loss_obj=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='mean',\n            loss_weight=1.0),\n        loss_mask=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='mean',\n            loss_weight=5.0),\n        loss_dice=dict(\n            type='DiceLoss',\n            use_sigmoid=True,\n            reduction='sum',\n            eps=5e-5,\n            loss_weight=2.0),\n    ),\n    test_cfg=dict(score_thr=0.005, mask_thr_binary=0.45))\n\nbackend = 'pillow'\ntrain_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args={{_base_.backend_args}},\n        imdecode_backend=backend),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=True,\n        with_mask=True,\n        poly2mask=False),\n    dict(\n        type='RandomChoiceResize',\n        scales=[(416, 853), (448, 853), (480, 853), (512, 853), (544, 853),\n                (576, 853), (608, 853), (640, 853)],\n        keep_ratio=True,\n        backend=backend),\n    dict(type='RandomFlip', prob=0.5),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        backend_args={{_base_.backend_args}},\n        imdecode_backend=backend),\n    dict(type='Resize', scale=(640, 853), keep_ratio=True, backend=backend),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=8,\n    num_workers=8,\n    sampler=dict(type='InfiniteSampler'),\n    dataset=dict(pipeline=train_pipeline))\ntest_dataloader = dict(dataset=dict(pipeline=test_pipeline))\nval_dataloader = test_dataloader\n\nval_evaluator = dict(metric='segm')\ntest_evaluator = val_evaluator\n\n# optimizer\noptim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(_delete_=True, type='AdamW', lr=0.00005, weight_decay=0.05))\ntrain_cfg = dict(\n    _delete_=True,\n    type='IterBasedTrainLoop',\n    max_iters=270000,\n    val_interval=10000)\n# learning rate\nparam_scheduler = [\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=270000,\n        by_epoch=False,\n        milestones=[210000, 250000],\n        gamma=0.1)\n]\n\ndefault_hooks = dict(\n    checkpoint=dict(by_epoch=False, interval=10000, max_keep_ckpts=3))\nlog_processor = dict(by_epoch=False)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\nauto_scale_lr = dict(base_batch_size=64, enable=True)\n"
  },
  {
    "path": "projects/SparseInst/sparseinst/__init__.py",
    "content": "from .decoder import BaseIAMDecoder, GroupIAMDecoder, GroupIAMSoftDecoder\nfrom .encoder import PyramidPoolingModule\nfrom .loss import SparseInstCriterion, SparseInstMatcher\nfrom .sparseinst import SparseInst\n\n__all__ = [\n    'BaseIAMDecoder', 'GroupIAMDecoder', 'GroupIAMSoftDecoder',\n    'PyramidPoolingModule', 'SparseInstCriterion', 'SparseInstMatcher',\n    'SparseInst'\n]\n"
  },
  {
    "path": "projects/SparseInst/sparseinst/decoder.py",
    "content": "# Copyright (c) Tianheng Cheng and its affiliates. All Rights Reserved\n\nimport math\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.model.weight_init import caffe2_xavier_init, kaiming_init\nfrom torch.nn import init\n\nfrom mmdet.registry import MODELS\n\n\ndef _make_stack_3x3_convs(num_convs,\n                          in_channels,\n                          out_channels,\n                          act_cfg=dict(type='ReLU', inplace=True)):\n    convs = []\n    for _ in range(num_convs):\n        convs.append(nn.Conv2d(in_channels, out_channels, 3, padding=1))\n        convs.append(MODELS.build(act_cfg))\n        in_channels = out_channels\n    return nn.Sequential(*convs)\n\n\nclass InstanceBranch(nn.Module):\n\n    def __init__(self,\n                 in_channels,\n                 dim=256,\n                 num_convs=4,\n                 num_masks=100,\n                 num_classes=80,\n                 kernel_dim=128,\n                 act_cfg=dict(type='ReLU', inplace=True)):\n        super().__init__()\n        num_masks = num_masks\n        self.num_classes = num_classes\n\n        self.inst_convs = _make_stack_3x3_convs(num_convs, in_channels, dim,\n                                                act_cfg)\n        # iam prediction, a simple conv\n        self.iam_conv = nn.Conv2d(dim, num_masks, 3, padding=1)\n\n        # outputs\n        self.cls_score = nn.Linear(dim, self.num_classes)\n        self.mask_kernel = nn.Linear(dim, kernel_dim)\n        self.objectness = nn.Linear(dim, 1)\n\n        self.prior_prob = 0.01\n        self._init_weights()\n\n    def _init_weights(self):\n        for m in self.inst_convs.modules():\n            if isinstance(m, nn.Conv2d):\n                kaiming_init(m)\n        bias_value = -math.log((1 - self.prior_prob) / self.prior_prob)\n        for module in [self.iam_conv, self.cls_score]:\n            init.constant_(module.bias, bias_value)\n        init.normal_(self.iam_conv.weight, std=0.01)\n        init.normal_(self.cls_score.weight, std=0.01)\n\n        init.normal_(self.mask_kernel.weight, std=0.01)\n        init.constant_(self.mask_kernel.bias, 0.0)\n\n    def forward(self, features):\n        # instance features (x4 convs)\n        features = self.inst_convs(features)\n        # predict instance activation maps\n        iam = self.iam_conv(features)\n        iam_prob = iam.sigmoid()\n\n        B, N = iam_prob.shape[:2]\n        C = features.size(1)\n        # BxNxHxW -> BxNx(HW)\n        iam_prob = iam_prob.view(B, N, -1)\n        normalizer = iam_prob.sum(-1).clamp(min=1e-6)\n        iam_prob = iam_prob / normalizer[:, :, None]\n        # aggregate features: BxCxHxW -> Bx(HW)xC\n        inst_features = torch.bmm(iam_prob,\n                                  features.view(B, C, -1).permute(0, 2, 1))\n        # predict classification & segmentation kernel & objectness\n        pred_logits = self.cls_score(inst_features)\n        pred_kernel = self.mask_kernel(inst_features)\n        pred_scores = self.objectness(inst_features)\n        return pred_logits, pred_kernel, pred_scores, iam\n\n\nclass MaskBranch(nn.Module):\n\n    def __init__(self,\n                 in_channels,\n                 dim=256,\n                 num_convs=4,\n                 kernel_dim=128,\n                 act_cfg=dict(type='ReLU', inplace=True)):\n        super().__init__()\n        self.mask_convs = _make_stack_3x3_convs(num_convs, in_channels, dim,\n                                                act_cfg)\n        self.projection = nn.Conv2d(dim, kernel_dim, kernel_size=1)\n        self._init_weights()\n\n    def _init_weights(self):\n        for m in self.mask_convs.modules():\n            if isinstance(m, nn.Conv2d):\n                kaiming_init(m)\n        kaiming_init(self.projection)\n\n    def forward(self, features):\n        # mask features (x4 convs)\n        features = self.mask_convs(features)\n        return self.projection(features)\n\n\n@MODELS.register_module()\nclass BaseIAMDecoder(nn.Module):\n\n    def __init__(self,\n                 in_channels,\n                 num_classes,\n                 ins_dim=256,\n                 ins_conv=4,\n                 mask_dim=256,\n                 mask_conv=4,\n                 kernel_dim=128,\n                 scale_factor=2.0,\n                 output_iam=False,\n                 num_masks=100,\n                 act_cfg=dict(type='ReLU', inplace=True)):\n        super().__init__()\n        # add 2 for coordinates\n        in_channels = in_channels  # ENCODER.NUM_CHANNELS + 2\n\n        self.scale_factor = scale_factor\n        self.output_iam = output_iam\n\n        self.inst_branch = InstanceBranch(\n            in_channels,\n            dim=ins_dim,\n            num_convs=ins_conv,\n            num_masks=num_masks,\n            num_classes=num_classes,\n            kernel_dim=kernel_dim,\n            act_cfg=act_cfg)\n        self.mask_branch = MaskBranch(\n            in_channels,\n            dim=mask_dim,\n            num_convs=mask_conv,\n            kernel_dim=kernel_dim,\n            act_cfg=act_cfg)\n\n    @torch.no_grad()\n    def compute_coordinates_linspace(self, x):\n        # linspace is not supported in ONNX\n        h, w = x.size(2), x.size(3)\n        y_loc = torch.linspace(-1, 1, h, device=x.device)\n        x_loc = torch.linspace(-1, 1, w, device=x.device)\n        y_loc, x_loc = torch.meshgrid(y_loc, x_loc)\n        y_loc = y_loc.expand([x.shape[0], 1, -1, -1])\n        x_loc = x_loc.expand([x.shape[0], 1, -1, -1])\n        locations = torch.cat([x_loc, y_loc], 1)\n        return locations.to(x)\n\n    @torch.no_grad()\n    def compute_coordinates(self, x):\n        h, w = x.size(2), x.size(3)\n        y_loc = -1.0 + 2.0 * torch.arange(h, device=x.device) / (h - 1)\n        x_loc = -1.0 + 2.0 * torch.arange(w, device=x.device) / (w - 1)\n        y_loc, x_loc = torch.meshgrid(y_loc, x_loc)\n        y_loc = y_loc.expand([x.shape[0], 1, -1, -1])\n        x_loc = x_loc.expand([x.shape[0], 1, -1, -1])\n        locations = torch.cat([x_loc, y_loc], 1)\n        return locations.to(x)\n\n    def forward(self, features):\n        coord_features = self.compute_coordinates(features)\n        features = torch.cat([coord_features, features], dim=1)\n        pred_logits, pred_kernel, pred_scores, iam = self.inst_branch(features)\n        mask_features = self.mask_branch(features)\n\n        N = pred_kernel.shape[1]\n        # mask_features: BxCxHxW\n        B, C, H, W = mask_features.shape\n        pred_masks = torch.bmm(pred_kernel,\n                               mask_features.view(B, C,\n                                                  H * W)).view(B, N, H, W)\n\n        pred_masks = F.interpolate(\n            pred_masks,\n            scale_factor=self.scale_factor,\n            mode='bilinear',\n            align_corners=False)\n\n        output = {\n            'pred_logits': pred_logits,\n            'pred_masks': pred_masks,\n            'pred_scores': pred_scores,\n        }\n\n        if self.output_iam:\n            iam = F.interpolate(\n                iam,\n                scale_factor=self.scale_factor,\n                mode='bilinear',\n                align_corners=False)\n            output['pred_iam'] = iam\n\n        return output\n\n\nclass GroupInstanceBranch(nn.Module):\n\n    def __init__(self,\n                 in_channels,\n                 num_groups=4,\n                 dim=256,\n                 num_convs=4,\n                 num_masks=100,\n                 num_classes=80,\n                 kernel_dim=128,\n                 act_cfg=dict(type='ReLU', inplace=True)):\n        super().__init__()\n        self.num_groups = num_groups\n        self.num_classes = num_classes\n\n        self.inst_convs = _make_stack_3x3_convs(\n            num_convs, in_channels, dim, act_cfg=act_cfg)\n        # iam prediction, a group conv\n        expand_dim = dim * self.num_groups\n        self.iam_conv = nn.Conv2d(\n            dim,\n            num_masks * self.num_groups,\n            3,\n            padding=1,\n            groups=self.num_groups)\n        # outputs\n        self.fc = nn.Linear(expand_dim, expand_dim)\n\n        self.cls_score = nn.Linear(expand_dim, self.num_classes)\n        self.mask_kernel = nn.Linear(expand_dim, kernel_dim)\n        self.objectness = nn.Linear(expand_dim, 1)\n\n        self.prior_prob = 0.01\n        self._init_weights()\n\n    def _init_weights(self):\n        for m in self.inst_convs.modules():\n            if isinstance(m, nn.Conv2d):\n                kaiming_init(m)\n        bias_value = -math.log((1 - self.prior_prob) / self.prior_prob)\n        for module in [self.iam_conv, self.cls_score]:\n            init.constant_(module.bias, bias_value)\n        init.normal_(self.iam_conv.weight, std=0.01)\n        init.normal_(self.cls_score.weight, std=0.01)\n\n        init.normal_(self.mask_kernel.weight, std=0.01)\n        init.constant_(self.mask_kernel.bias, 0.0)\n        caffe2_xavier_init(self.fc)\n\n    def forward(self, features):\n        # instance features (x4 convs)\n        features = self.inst_convs(features)\n        # predict instance activation maps\n        iam = self.iam_conv(features)\n        iam_prob = iam.sigmoid()\n\n        B, N = iam_prob.shape[:2]\n        C = features.size(1)\n        # BxNxHxW -> BxNx(HW)\n        iam_prob = iam_prob.view(B, N, -1)\n        normalizer = iam_prob.sum(-1).clamp(min=1e-6)\n        iam_prob = iam_prob / normalizer[:, :, None]\n\n        # aggregate features: BxCxHxW -> Bx(HW)xC\n        inst_features = torch.bmm(iam_prob,\n                                  features.view(B, C, -1).permute(0, 2, 1))\n\n        inst_features = inst_features.reshape(B, 4, N // self.num_groups,\n                                              -1).transpose(1, 2).reshape(\n                                                  B, N // self.num_groups, -1)\n\n        inst_features = F.relu_(self.fc(inst_features))\n        # predict classification & segmentation kernel & objectness\n        pred_logits = self.cls_score(inst_features)\n        pred_kernel = self.mask_kernel(inst_features)\n        pred_scores = self.objectness(inst_features)\n        return pred_logits, pred_kernel, pred_scores, iam\n\n\n@MODELS.register_module()\nclass GroupIAMDecoder(BaseIAMDecoder):\n\n    def __init__(self,\n                 in_channels,\n                 num_classes,\n                 num_groups=4,\n                 ins_dim=256,\n                 ins_conv=4,\n                 mask_dim=256,\n                 mask_conv=4,\n                 kernel_dim=128,\n                 scale_factor=2.0,\n                 output_iam=False,\n                 num_masks=100,\n                 act_cfg=dict(type='ReLU', inplace=True)):\n        super().__init__(\n            in_channels=in_channels,\n            num_classes=num_classes,\n            ins_dim=ins_dim,\n            ins_conv=ins_conv,\n            mask_dim=mask_dim,\n            mask_conv=mask_conv,\n            kernel_dim=kernel_dim,\n            scale_factor=scale_factor,\n            output_iam=output_iam,\n            num_masks=num_masks,\n            act_cfg=act_cfg)\n        self.inst_branch = GroupInstanceBranch(\n            in_channels,\n            num_groups=num_groups,\n            dim=ins_dim,\n            num_convs=ins_conv,\n            num_masks=num_masks,\n            num_classes=num_classes,\n            kernel_dim=kernel_dim,\n            act_cfg=act_cfg)\n\n\nclass GroupInstanceSoftBranch(GroupInstanceBranch):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.softmax_bias = nn.Parameter(torch.ones([\n            1,\n        ]))\n\n    def forward(self, features):\n        # instance features (x4 convs)\n        features = self.inst_convs(features)\n        # predict instance activation maps\n        iam = self.iam_conv(features)\n\n        B, N = iam.shape[:2]\n        C = features.size(1)\n        # BxNxHxW -> BxNx(HW)\n        iam_prob = F.softmax(iam.view(B, N, -1) + self.softmax_bias, dim=-1)\n        # aggregate features: BxCxHxW -> Bx(HW)xC\n        inst_features = torch.bmm(iam_prob,\n                                  features.view(B, C, -1).permute(0, 2, 1))\n\n        inst_features = inst_features.reshape(B, self.num_groups,\n                                              N // self.num_groups,\n                                              -1).transpose(1, 2).reshape(\n                                                  B, N // self.num_groups, -1)\n\n        inst_features = F.relu_(self.fc(inst_features))\n        # predict classification & segmentation kernel & objectness\n        pred_logits = self.cls_score(inst_features)\n        pred_kernel = self.mask_kernel(inst_features)\n        pred_scores = self.objectness(inst_features)\n        return pred_logits, pred_kernel, pred_scores, iam\n\n\n@MODELS.register_module()\nclass GroupIAMSoftDecoder(BaseIAMDecoder):\n\n    def __init__(self,\n                 in_channels,\n                 num_classes,\n                 num_groups=4,\n                 ins_dim=256,\n                 ins_conv=4,\n                 mask_dim=256,\n                 mask_conv=4,\n                 kernel_dim=128,\n                 scale_factor=2.0,\n                 output_iam=False,\n                 num_masks=100,\n                 act_cfg=dict(type='ReLU', inplace=True)):\n        super().__init__(\n            in_channels=in_channels,\n            num_classes=num_classes,\n            ins_dim=ins_dim,\n            ins_conv=ins_conv,\n            mask_dim=mask_dim,\n            mask_conv=mask_conv,\n            kernel_dim=kernel_dim,\n            scale_factor=scale_factor,\n            output_iam=output_iam,\n            num_masks=num_masks,\n            act_cfg=act_cfg)\n        self.inst_branch = GroupInstanceSoftBranch(\n            in_channels,\n            num_groups=num_groups,\n            dim=ins_dim,\n            num_convs=ins_conv,\n            num_masks=num_masks,\n            num_classes=num_classes,\n            kernel_dim=kernel_dim,\n            act_cfg=act_cfg)\n"
  },
  {
    "path": "projects/SparseInst/sparseinst/encoder.py",
    "content": "# Copyright (c) Tianheng Cheng and its affiliates. All Rights Reserved\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmengine.model.weight_init import caffe2_xavier_init, kaiming_init\n\nfrom mmdet.registry import MODELS\n\n\nclass PyramidPoolingModule(nn.Module):\n\n    def __init__(self,\n                 in_channels,\n                 channels=512,\n                 sizes=(1, 2, 3, 6),\n                 act_cfg=dict(type='ReLU')):\n        super().__init__()\n        self.stages = []\n        self.stages = nn.ModuleList(\n            [self._make_stage(in_channels, channels, size) for size in sizes])\n        self.bottleneck = nn.Conv2d(in_channels + len(sizes) * channels,\n                                    in_channels, 1)\n        self.act = MODELS.build(act_cfg)\n\n    def _make_stage(self, features, out_features, size):\n        prior = nn.AdaptiveAvgPool2d(output_size=(size, size))\n        conv = nn.Conv2d(features, out_features, 1)\n        return nn.Sequential(prior, conv)\n\n    def forward(self, feats):\n        h, w = feats.size(2), feats.size(3)\n        priors = [\n            F.interpolate(\n                input=self.act(stage(feats)),\n                size=(h, w),\n                mode='bilinear',\n                align_corners=False) for stage in self.stages\n        ] + [feats]\n        out = self.act(self.bottleneck(torch.cat(priors, 1)))\n        return out\n\n\n@MODELS.register_module()\nclass InstanceContextEncoder(nn.Module):\n    \"\"\"\n    Instance Context Encoder\n    1. construct feature pyramids from ResNet\n    2. enlarge receptive fields (ppm)\n    3. multi-scale fusion\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels=256,\n                 with_ppm=True,\n                 act_cfg=dict(type='ReLU')):\n        super().__init__()\n        self.num_channels = out_channels\n        self.in_channels = in_channels\n        self.with_ppm = with_ppm\n        fpn_laterals = []\n        fpn_outputs = []\n        for in_channel in reversed(self.in_channels):\n            lateral_conv = nn.Conv2d(in_channel, self.num_channels, 1)\n            output_conv = nn.Conv2d(\n                self.num_channels, self.num_channels, 3, padding=1)\n            caffe2_xavier_init(lateral_conv)\n            caffe2_xavier_init(output_conv)\n            fpn_laterals.append(lateral_conv)\n            fpn_outputs.append(output_conv)\n        self.fpn_laterals = nn.ModuleList(fpn_laterals)\n        self.fpn_outputs = nn.ModuleList(fpn_outputs)\n        # ppm\n        if self.with_ppm:\n            self.ppm = PyramidPoolingModule(\n                self.num_channels, self.num_channels // 4, act_cfg=act_cfg)\n        # final fusion\n        self.fusion = nn.Conv2d(self.num_channels * 3, self.num_channels, 1)\n        kaiming_init(self.fusion)\n\n    def forward(self, features):\n        features = features[::-1]\n        prev_features = self.fpn_laterals[0](features[0])\n        if self.with_ppm:\n            prev_features = self.ppm(prev_features)\n        outputs = [self.fpn_outputs[0](prev_features)]\n        for feature, lat_conv, output_conv in zip(features[1:],\n                                                  self.fpn_laterals[1:],\n                                                  self.fpn_outputs[1:]):\n            lat_features = lat_conv(feature)\n            top_down_features = F.interpolate(\n                prev_features, scale_factor=2.0, mode='nearest')\n            prev_features = lat_features + top_down_features\n            outputs.insert(0, output_conv(prev_features))\n        size = outputs[0].shape[2:]\n        features = [outputs[0]] + [\n            F.interpolate(x, size, mode='bilinear', align_corners=False)\n            for x in outputs[1:]\n        ]\n        features = self.fusion(torch.cat(features, dim=1))\n        return features\n"
  },
  {
    "path": "projects/SparseInst/sparseinst/loss.py",
    "content": "# Copyright (c) Tianheng Cheng and its affiliates. All Rights Reserved\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom scipy.optimize import linear_sum_assignment\nfrom torch.cuda.amp import autocast\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.utils import reduce_mean\n\n\ndef compute_mask_iou(inputs, targets):\n    inputs = inputs.sigmoid()\n    # thresholding\n    binarized_inputs = (inputs >= 0.4).float()\n    targets = (targets > 0.5).float()\n    intersection = (binarized_inputs * targets).sum(-1)\n    union = targets.sum(-1) + binarized_inputs.sum(-1) - intersection\n    score = intersection / (union + 1e-6)\n    return score\n\n\ndef dice_score(inputs, targets):\n    inputs = inputs.sigmoid()\n    numerator = 2 * torch.matmul(inputs, targets.t())\n    denominator = (inputs * inputs).sum(-1)[:,\n                                            None] + (targets * targets).sum(-1)\n    score = numerator / (denominator + 1e-4)\n    return score\n\n\n@MODELS.register_module()\nclass SparseInstCriterion(nn.Module):\n    \"\"\"This part is partially derivated from:\n\n    https://github.com/facebookresearch/detr/blob/main/models/detr.py.\n    \"\"\"\n\n    def __init__(\n        self,\n        num_classes,\n        assigner,\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            alpha=0.25,\n            gamma=2.0,\n            reduction='sum',\n            loss_weight=2.0),\n        loss_obj=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='mean',\n            loss_weight=1.0),\n        loss_mask=dict(\n            type='CrossEntropyLoss',\n            use_sigmoid=True,\n            reduction='mean',\n            loss_weight=5.0),\n        loss_dice=dict(\n            type='DiceLoss',\n            use_sigmoid=True,\n            reduction='sum',\n            eps=5e-5,\n            loss_weight=2.0),\n    ):\n        super().__init__()\n        self.matcher = TASK_UTILS.build(assigner)\n        self.num_classes = num_classes\n        self.loss_cls = MODELS.build(loss_cls)\n        self.loss_obj = MODELS.build(loss_obj)\n        self.loss_mask = MODELS.build(loss_mask)\n        self.loss_dice = MODELS.build(loss_dice)\n\n    def _get_src_permutation_idx(self, indices):\n        # permute predictions following indices\n        batch_idx = torch.cat(\n            [torch.full_like(src, i) for i, (src, _) in enumerate(indices)])\n        src_idx = torch.cat([src for (src, _) in indices])\n        return batch_idx, src_idx\n\n    def _get_tgt_permutation_idx(self, indices):\n        # permute targets following indices\n        batch_idx = torch.cat(\n            [torch.full_like(tgt, i) for i, (_, tgt) in enumerate(indices)])\n        tgt_idx = torch.cat([tgt for (_, tgt) in indices])\n        return batch_idx, tgt_idx\n\n    def loss_classification(self, outputs, batch_gt_instances, indices,\n                            num_instances):\n        assert 'pred_logits' in outputs\n        src_logits = outputs['pred_logits']\n        idx = self._get_src_permutation_idx(indices)\n        target_classes_o = torch.cat(\n            [gt.labels[J] for gt, (_, J) in zip(batch_gt_instances, indices)])\n        target_classes = torch.full(\n            src_logits.shape[:2],\n            self.num_classes,\n            dtype=torch.int64,\n            device=src_logits.device)\n        target_classes[idx] = target_classes_o\n\n        src_logits = src_logits.flatten(0, 1)\n        target_classes = target_classes.flatten(0, 1)\n        # comp focal loss.\n        class_loss = self.loss_cls(\n            src_logits,\n            target_classes,\n        ) / num_instances\n        return class_loss\n\n    def loss_masks_with_iou_objectness(self, outputs, batch_gt_instances,\n                                       indices, num_instances):\n        src_idx = self._get_src_permutation_idx(indices)\n        tgt_idx = self._get_tgt_permutation_idx(indices)\n        # Bx100xHxW\n        assert 'pred_masks' in outputs\n        assert 'pred_scores' in outputs\n        src_iou_scores = outputs['pred_scores']\n        src_masks = outputs['pred_masks']\n        with torch.no_grad():\n            target_masks = torch.cat([\n                gt.masks.to_tensor(\n                    dtype=src_masks.dtype, device=src_masks.device)\n                for gt in batch_gt_instances\n            ])\n        num_masks = [len(gt.masks) for gt in batch_gt_instances]\n        target_masks = target_masks.to(src_masks)\n        if len(target_masks) == 0:\n\n            loss_dice = src_masks.sum() * 0.0\n            loss_mask = src_masks.sum() * 0.0\n            loss_objectness = src_iou_scores.sum() * 0.0\n\n            return loss_objectness, loss_dice, loss_mask\n\n        src_masks = src_masks[src_idx]\n        target_masks = F.interpolate(\n            target_masks[:, None],\n            size=src_masks.shape[-2:],\n            mode='bilinear',\n            align_corners=False).squeeze(1)\n\n        src_masks = src_masks.flatten(1)\n        # FIXME: tgt_idx\n        mix_tgt_idx = torch.zeros_like(tgt_idx[1])\n        cum_sum = 0\n        for num_mask in num_masks:\n            mix_tgt_idx[cum_sum:cum_sum + num_mask] = cum_sum\n            cum_sum += num_mask\n        mix_tgt_idx += tgt_idx[1]\n\n        target_masks = target_masks[mix_tgt_idx].flatten(1)\n\n        with torch.no_grad():\n            ious = compute_mask_iou(src_masks, target_masks)\n\n        tgt_iou_scores = ious\n        src_iou_scores = src_iou_scores[src_idx]\n        tgt_iou_scores = tgt_iou_scores.flatten(0)\n        src_iou_scores = src_iou_scores.flatten(0)\n\n        loss_objectness = self.loss_obj(src_iou_scores, tgt_iou_scores)\n        loss_dice = self.loss_dice(src_masks, target_masks) / num_instances\n        loss_mask = self.loss_mask(src_masks, target_masks)\n\n        return loss_objectness, loss_dice, loss_mask\n\n    def forward(self, outputs, batch_gt_instances, batch_img_metas,\n                batch_gt_instances_ignore):\n        # Retrieve the matching between the outputs of\n        # the last layer and the targets\n        indices = self.matcher(outputs, batch_gt_instances)\n        # Compute the average number of target boxes\n        # across all nodes, for normalization purposes\n        num_instances = sum(gt.labels.shape[0] for gt in batch_gt_instances)\n        num_instances = torch.as_tensor([num_instances],\n                                        dtype=torch.float,\n                                        device=next(iter(\n                                            outputs.values())).device)\n        num_instances = reduce_mean(num_instances).clamp_(min=1).item()\n        # Compute all the requested losses\n        loss_cls = self.loss_classification(outputs, batch_gt_instances,\n                                            indices, num_instances)\n        loss_obj, loss_dice, loss_mask = self.loss_masks_with_iou_objectness(\n            outputs, batch_gt_instances, indices, num_instances)\n\n        return dict(\n            loss_cls=loss_cls,\n            loss_obj=loss_obj,\n            loss_dice=loss_dice,\n            loss_mask=loss_mask)\n\n\n@TASK_UTILS.register_module()\nclass SparseInstMatcher(nn.Module):\n\n    def __init__(self, alpha=0.8, beta=0.2):\n        super().__init__()\n        self.alpha = alpha\n        self.beta = beta\n        self.mask_score = dice_score\n\n    def forward(self, outputs, batch_gt_instances):\n        with torch.no_grad():\n            B, N, H, W = outputs['pred_masks'].shape\n            pred_masks = outputs['pred_masks']\n            pred_logits = outputs['pred_logits'].sigmoid()\n            device = pred_masks.device\n\n            tgt_ids = torch.cat([gt.labels for gt in batch_gt_instances])\n\n            if tgt_ids.shape[0] == 0:\n                return [(torch.as_tensor([]).to(pred_logits),\n                         torch.as_tensor([]).to(pred_logits))] * B\n            tgt_masks = torch.cat([\n                gt.masks.to_tensor(dtype=pred_masks.dtype, device=device)\n                for gt in batch_gt_instances\n            ])\n\n            tgt_masks = F.interpolate(\n                tgt_masks[:, None],\n                size=pred_masks.shape[-2:],\n                mode='bilinear',\n                align_corners=False).squeeze(1)\n\n            pred_masks = pred_masks.view(B * N, -1)\n            tgt_masks = tgt_masks.flatten(1)\n            with autocast(enabled=False):\n                pred_masks = pred_masks.float()\n                tgt_masks = tgt_masks.float()\n                pred_logits = pred_logits.float()\n                mask_score = self.mask_score(pred_masks, tgt_masks)\n                # Nx(Number of gts)\n                matching_prob = pred_logits.view(B * N, -1)[:, tgt_ids]\n                C = (mask_score**self.alpha) * (matching_prob**self.beta)\n\n            C = C.view(B, N, -1).cpu()\n            # hungarian matching\n            sizes = [len(gt.masks) for gt in batch_gt_instances]\n            indices = [\n                linear_sum_assignment(c[i], maximize=True)\n                for i, c in enumerate(C.split(sizes, -1))\n            ]\n            indices = [(torch.as_tensor(i, dtype=torch.int64),\n                        torch.as_tensor(j, dtype=torch.int64))\n                       for i, j in indices]\n            return indices\n"
  },
  {
    "path": "projects/SparseInst/sparseinst/sparseinst.py",
    "content": "# Copyright (c) Tianheng Cheng and its affiliates. All Rights Reserved\nfrom typing import List, Tuple, Union\n\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.structures import InstanceData\nfrom torch import Tensor\n\nfrom mmdet.models import BaseDetector\nfrom mmdet.models.utils import unpack_gt_instances\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import OptSampleList, SampleList\nfrom mmdet.utils import ConfigType, OptConfigType\n\n\n@torch.jit.script\ndef rescoring_mask(scores, mask_pred, masks):\n    mask_pred_ = mask_pred.float()\n    return scores * ((masks * mask_pred_).sum([1, 2]) /\n                     (mask_pred_.sum([1, 2]) + 1e-6))\n\n\n@MODELS.register_module()\nclass SparseInst(BaseDetector):\n    \"\"\"Implementation of `SparseInst <https://arxiv.org/abs/1912.02424>`_\n\n    Args:\n        data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of\n            :class:`DetDataPreprocessor` to process the input data.\n            Defaults to None.\n        backbone (:obj:`ConfigDict` or dict): The backbone module.\n        encoder (:obj:`ConfigDict` or dict): The encoder module.\n        decoder (:obj:`ConfigDict` or dict): The decoder module.\n        criterion (:obj:`ConfigDict` or dict, optional): The training matcher\n            and losses. Defaults to None.\n        test_cfg (:obj:`ConfigDict` or dict, optional): The testing config\n            of SparseInst. Defaults to None.\n        init_cfg (:obj:`ConfigDict` or dict, optional): the config to control\n            the initialization. Defaults to None.\n    \"\"\"\n\n    def __init__(self,\n                 data_preprocessor: ConfigType,\n                 backbone: ConfigType,\n                 encoder: ConfigType,\n                 decoder: ConfigType,\n                 criterion: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 init_cfg: OptConfigType = None):\n        super().__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n\n        # backbone\n        self.backbone = MODELS.build(backbone)\n        # encoder & decoder\n        self.encoder = MODELS.build(encoder)\n        self.decoder = MODELS.build(decoder)\n\n        # matcher & loss (matcher is built in loss)\n        self.criterion = MODELS.build(criterion)\n\n        # inference\n        self.cls_threshold = test_cfg.score_thr\n        self.mask_threshold = test_cfg.mask_thr_binary\n\n    def _forward(\n            self,\n            batch_inputs: Tensor,\n            batch_data_samples: OptSampleList = None) -> Tuple[List[Tensor]]:\n        \"\"\"Network forward process. Usually includes backbone, neck and head\n        forward without any post-processing.\n\n         Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n\n        Returns:\n            tuple[list]: A tuple of features from ``bbox_head`` forward.\n        \"\"\"\n        x = self.backbone(batch_inputs)\n        x = self.encoder(x)\n        results = self.decoder(x)\n        return results\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        \"\"\"Predict results from a batch of inputs and data samples with post-\n        processing.\n\n        Args:\n            batch_inputs (Tensor): Inputs with shape (N, C, H, W).\n            batch_data_samples (List[:obj:`DetDataSample`]): The Data\n                Samples. It usually includes information such as\n                `gt_instance`, `gt_panoptic_seg` and `gt_sem_seg`.\n            rescale (bool): Whether to rescale the results.\n                Defaults to True.\n\n        Returns:\n            list[:obj:`DetDataSample`]: Detection results of the\n            input images. Each DetDataSample usually contain\n            'pred_instances'. And the ``pred_instances`` usually\n            contains following keys.\n\n                - scores (Tensor): Classification scores, has a shape\n                    (num_instance, )\n                - labels (Tensor): Labels of bboxes, has a shape\n                    (num_instances, ).\n                - bboxes (Tensor): Has a shape (num_instances, 4),\n                    the last dimension 4 arrange as (x1, y1, x2, y2).\n        \"\"\"\n        max_shape = batch_inputs.shape[-2:]\n        output = self._forward(batch_inputs)\n\n        pred_scores = output['pred_logits'].sigmoid()\n        pred_masks = output['pred_masks'].sigmoid()\n        pred_objectness = output['pred_scores'].sigmoid()\n        pred_scores = torch.sqrt(pred_scores * pred_objectness)\n\n        results_list = []\n        for batch_idx, (scores_per_image, mask_pred_per_image,\n                        datasample) in enumerate(\n                            zip(pred_scores, pred_masks, batch_data_samples)):\n            result = InstanceData()\n            # max/argmax\n            scores, labels = scores_per_image.max(dim=-1)\n            # cls threshold\n            keep = scores > self.cls_threshold\n            scores = scores[keep]\n            labels = labels[keep]\n            mask_pred_per_image = mask_pred_per_image[keep]\n\n            if scores.size(0) == 0:\n                result.scores = scores\n                result.labels = labels\n                results_list.append(result)\n                continue\n\n            img_meta = datasample.metainfo\n            # rescoring mask using maskness\n            scores = rescoring_mask(scores,\n                                    mask_pred_per_image > self.mask_threshold,\n                                    mask_pred_per_image)\n            h, w = img_meta['img_shape'][:2]\n            mask_pred_per_image = F.interpolate(\n                mask_pred_per_image.unsqueeze(1),\n                size=max_shape,\n                mode='bilinear',\n                align_corners=False)[:, :, :h, :w]\n\n            if rescale:\n                ori_h, ori_w = img_meta['ori_shape'][:2]\n                mask_pred_per_image = F.interpolate(\n                    mask_pred_per_image,\n                    size=(ori_h, ori_w),\n                    mode='bilinear',\n                    align_corners=False).squeeze(1)\n\n            mask_pred = mask_pred_per_image > self.mask_threshold\n            result.masks = mask_pred\n            result.scores = scores\n            result.labels = labels\n            # create an empty bbox in InstanceData to avoid bugs when\n            # calculating metrics.\n            result.bboxes = result.scores.new_zeros(len(scores), 4)\n            results_list.append(result)\n\n        batch_data_samples = self.add_pred_to_datasample(\n            batch_data_samples, results_list)\n        return batch_data_samples\n\n    def loss(self, batch_inputs: Tensor,\n             batch_data_samples: SampleList) -> Union[dict, list]:\n        \"\"\"Calculate losses from a batch of inputs and data samples.\n\n        Args:\n            batch_inputs (Tensor): Input images of shape (N, C, H, W).\n                These should usually be mean centered and std scaled.\n            batch_data_samples (list[:obj:`DetDataSample`]): The batch\n                data samples. It usually includes information such\n                as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`.\n\n        Returns:\n            dict: A dictionary of loss components.\n        \"\"\"\n        outs = self._forward(batch_inputs)\n        (batch_gt_instances, batch_gt_instances_ignore,\n         batch_img_metas) = unpack_gt_instances(batch_data_samples)\n\n        losses = self.criterion(outs, batch_gt_instances, batch_img_metas,\n                                batch_gt_instances_ignore)\n        return losses\n\n    def extract_feat(self, batch_inputs: Tensor) -> Tuple[Tensor]:\n        \"\"\"Extract features.\n\n        Args:\n            batch_inputs (Tensor): Image tensor with shape (N, C, H ,W).\n\n        Returns:\n            tuple[Tensor]: Multi-level features that may have\n            different resolutions.\n        \"\"\"\n        x = self.backbone(batch_inputs)\n        x = self.encoder(x)\n        return x\n"
  },
  {
    "path": "projects/VISION-Datasets/README.md",
    "content": "# VISION-Datasets\n\n> VISION Datasets: A Benchmark for Vision-based InduStrial InspectiON\n\n## Introduction\n\nDespite progress in vision-based inspection algorithms, real-world industrial challenges – specifically in data availability, quality, and complex production requirements – often remain under-addressed. We introduce the VISION Datasets, a diverse collection of 14 industrial inspection datasets, uniquely poised to meet these challenges. Unlike previous datasets, VISION brings versatility to defect detection, offering annotation masks across all splits and catering to various detection methodologies. Our datasets also feature instance-segmentation annotation, enabling precise defect identification. With a total of 18k images encompassing 44 defect types, VISION strives to mirror a wide range of real-world production scenarios. By supporting two ongoing challenge competitions on the VISION Datasets, we hope to foster further advancements in vision-based industrial inspection. The datasets are available at https://huggingface.co/datasets/VISION-Workshop/VISION-Datasets.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/1d8e81d1-0023-49ce-a855-a09149509fe7\" width=\"70%\"/>\n</div>\n\n## Dataset Preparation\n\nAt first, you should download the dataset from https://huggingface.co/datasets/VISION-Workshop/VISION-Datasets and organize it as follows:\n\n```text\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n├── │── VISION-Datasets\n├── │   ├── Cable.tar.gz\n├── │   ├── Capacitor.tar.gz\n├── │   ├── Casting.tar.gz\n├── |   ├── Console.tar.gz\n├── │   ├── Cylinder.tar.gz\n├── │   ├── Electronics.tar.gz\n├── │   ├── Groove.tar.gz\n├── │   ├── Hemisphere.tar.gz\n├── │   ├── Lens.tar.gz\n├── │   ├── PCB_1.tar.gz\n├── │   ├── PCB_2.tar.gz\n├── |   ├── README.md\n├── │   ├── Ring.tar.gz\n├── │   ├── Screw.tar.gz\n├── │   └── Wood.tar.gz\n```\n\nThen you can use the following command to save the following command as the `vision_unzip.sh` file and place it in the `mmdetection` root directory, and then run the script `bash vision_unzip.sh` to unzip it.\n\n```shell\n#!/usr/bin/env bash\n\nfor file in data/VISION-Datasets/*.tar.gz; do\n    tar -xzvzf \"$file\" -C data/VISION-Datasets/\ndone\n```\n\nFinally, the file organization format is as follows:\n\n```text\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n|   │── VISION-Datasets\n|   │   ├── Cable.tar.gz\n|   │   ├── Capacitor.tar.gz\n|   │   ├── Casting.tar.gz\n|   |   ├── Console.tar.gz\n|   │   ├── Cylinder.tar.gz\n|   │   ├── Electronics.tar.gz\n|   │   ├── Groove.tar.gz\n|   │   ├── Hemisphere.tar.gz\n|   │   ├── Lens.tar.gz\n|   │   ├── PCB_1.tar.gz\n|   │   ├── PCB_2.tar.gz\n|   |   ├── README.md\n|   │   ├── Ring.tar.gz\n|   │   ├── Screw.tar.gz\n|   │   └── Wood.tar.gz\n|   │   ├── Cable\n|   │   |    |── train\n|   │   |    |     |──  _annotations.coco.json # COCO format annotation\n|   │   |    |     |──  000001.png # Images\n|   │   |    |     |──  000002.png\n|   │   |    |     |──  xxxxxx.png\n|   │   |    |── val\n|   │   |    |     |──  _annotations.coco.json # COCO format annotation\n|   │   |    |     |──  xxxxxx.png # Images\n|   │   |    |── inference\n|   │   |    |     |──  _annotations.coco.json # COCO format annotation with unlabeled image list only\n|   │   |    |     |──  xxxxxx.png # Images\n...\n```\n\n## Models and Results\n\nTODO\n\n## Citation\n\n```latex\n@article{vision-datasets,\n  title         = {VISION Datasets: A Benchmark for Vision-based InduStrial InspectiON},\n  author        = {Haoping Bai, Shancong Mou, Tatiana Likhomanenko, Ramazan Gokberk Cinbis, Oncel Tuzel, Ping Huang, Jiulong Shan, Jianjun Shi, Meng Cao},\n  journal       = {arXiv preprint arXiv:2306.07890},\n  year          = {2023},\n}\n```\n"
  },
  {
    "path": "projects/VISION-Datasets/README_zh-CN.md",
    "content": "# VISION-Datasets\n\n> VISION Datasets: A Benchmark for Vision-based InduStrial InspectiON\n\n## Introduction\n\nDespite progress in vision-based inspection algorithms, real-world industrial challenges – specifically in data availability, quality, and complex production requirements – often remain under-addressed. We introduce the VISION Datasets, a diverse collection of 14 industrial inspection datasets, uniquely poised to meet these challenges. Unlike previous datasets, VISION brings versatility to defect detection, offering annotation masks across all splits and catering to various detection methodologies. Our datasets also feature instance-segmentation annotation, enabling precise defect identification. With a total of 18k images encompassing 44 defect types, VISION strives to mirror a wide range of real-world production scenarios. By supporting two ongoing challenge competitions on the VISION Datasets, we hope to foster further advancements in vision-based industrial inspection. The datasets are available at https://huggingface.co/datasets/VISION-Workshop/VISION-Datasets.\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/1d8e81d1-0023-49ce-a855-a09149509fe7\" width=\"70%\"/>\n</div>\n\n## Dataset Preparation\n\n首先你应该从 https://huggingface.co/datasets/VISION-Workshop/VISION-Datasets 下载数据集，并将其组织为如下格式：\n\n```text\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n├── │── VISION-Datasets\n├── │   ├── Cable.tar.gz\n├── │   ├── Capacitor.tar.gz\n├── │   ├── Casting.tar.gz\n├── |   ├── Console.tar.gz\n├── │   ├── Cylinder.tar.gz\n├── │   ├── Electronics.tar.gz\n├── │   ├── Groove.tar.gz\n├── │   ├── Hemisphere.tar.gz\n├── │   ├── Lens.tar.gz\n├── │   ├── PCB_1.tar.gz\n├── │   ├── PCB_2.tar.gz\n├── |   ├── README.md\n├── │   ├── Ring.tar.gz\n├── │   ├── Screw.tar.gz\n├── │   └── Wood.tar.gz\n```\n\n然后你可以使用将以下命令保存为 `vision_unzip.sh` 文件，并将其放置于 `mmdetection` 根目录下，然后 `bash vision_unzip.sh` 运行脚本进行解压处理\n\n```shell\n#!/usr/bin/env bash\n\nfor file in data/VISION-Datasets/*.tar.gz; do\n    tar -xzvzf \"$file\" -C data/VISION-Datasets/\ndone\n```\n\n最终的文件组织格式如下所示：\n\n```text\nmmdetection\n├── mmdet\n├── tools\n├── configs\n├── data\n|   │── VISION-Datasets\n|   │   ├── Cable.tar.gz\n|   │   ├── Capacitor.tar.gz\n|   │   ├── Casting.tar.gz\n|   |   ├── Console.tar.gz\n|   │   ├── Cylinder.tar.gz\n|   │   ├── Electronics.tar.gz\n|   │   ├── Groove.tar.gz\n|   │   ├── Hemisphere.tar.gz\n|   │   ├── Lens.tar.gz\n|   │   ├── PCB_1.tar.gz\n|   │   ├── PCB_2.tar.gz\n|   |   ├── README.md\n|   │   ├── Ring.tar.gz\n|   │   ├── Screw.tar.gz\n|   │   └── Wood.tar.gz\n|   │   ├── Cable\n|   │   |    |── train\n|   │   |    |     |──  _annotations.coco.json # COCO format annotation\n|   │   |    |     |──  000001.png # Images\n|   │   |    |     |──  000002.png\n|   │   |    |     |──  xxxxxx.png\n|   │   |    |── val\n|   │   |    |     |──  _annotations.coco.json # COCO format annotation\n|   │   |    |     |──  xxxxxx.png # Images\n|   │   |    |── inference\n|   │   |    |     |──  _annotations.coco.json # COCO format annotation with unlabeled image list only\n|   │   |    |     |──  xxxxxx.png # Images\n...\n```\n\n## Models and Results\n\nTODO\n\n## Citation\n\n```latex\n@article{vision-datasets,\n  title         = {VISION Datasets: A Benchmark for Vision-based InduStrial InspectiON},\n  author        = {Haoping Bai, Shancong Mou, Tatiana Likhomanenko, Ramazan Gokberk Cinbis, Oncel Tuzel, Ping Huang, Jiulong Shan, Jianjun Shi, Meng Cao},\n  journal       = {arXiv preprint arXiv:2306.07890},\n  year          = {2023},\n}\n```\n"
  },
  {
    "path": "projects/ViTDet/README.md",
    "content": "# ViTDet\n\n## Description\n\nThis is an implementation of [ViTDet](https://github.com/facebookresearch/detectron2/tree/main/projects/ViTDet) based on [MMDetection](https://github.com/facebookresearch/detectron2/tree/main/projects/ViTDet), [MMCV](https://github.com/open-mmlab/mmcv), and [MMEngine](https://github.com/open-mmlab/mmengine).\n\n## Usage\n\n### Training commands\n\nFollow original [setting](https://github.com/facebookresearch/detectron2/tree/main/projects/ViTDet), this project is trained with total batch size of 64 (16 GPU with 4 images per GPU).\n\nIn MMDetection's root directory, run the following command to train the model:\n\n```bash\nGPUS=${GPUS} ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR}\n```\n\nBelow is an example of using 16 GPUs to train VitDet on a Slurm partition named _dev_, and set the work-dir to some shared file systems.\n\n```shell\nGPUS=16 ./tools/slurm_train.sh dev vitdet_mask_b projects/ViTDet/configs/vitdet_mask-rcnn_vit-b-mae_lsj-100e.py /nfs/xxxx/vitdet_mask-rcnn_vit-b-mae_lsj-100e\n```\n\n### Testing commands\n\nIn MMDetection's root directory, run the following command to test the model:\n\n```bash\npython tools/test.py projects/ViTDet/configs/vitdet_mask-rcnn_vit-b-mae_lsj-100e.py ${CHECKPOINT_PATH}\n```\n\n## Results\n\nBased on mmdetection, this project almost aligns the test and train accuracy of the [ViTDet](https://github.com/facebookresearch/detectron2/tree/main/projects/ViTDet).\n\n|                           Method                           | Backbone | Pretrained Model |  Training set  |   Test set   | Epoch | Val Box AP | Val Mask  AP |                                                                                                                                                               Download                                                                                                                                                                |\n| :--------------------------------------------------------: | :------: | :--------------: | :------------: | :----------: | :---: | :--------: | :----------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |\n| [ViTDet](./configs/vitdet_mask-rcnn_vit-b-mae_lsj-100e.py) |  ViT-B   |       MAE        | COCO2017 Train | COCO2017 Val |  100  |    51.6    |     45.7     | [model](https://download.openmmlab.com/mmdetection/v3.0/vitdet/vitdet_mask-rcnn_vit-b-mae_lsj-100e/vitdet_mask-rcnn_vit-b-mae_lsj-100e_20230328_153519-e15fe294.pth) / [log](https://download.openmmlab.com/mmdetection/v3.0/vitdet/vitdet_mask-rcnn_vit-b-mae_lsj-100e/vitdet_mask-rcnn_vit-b-mae_lsj-100e_20230328_153519.log.json) |\n\n**Note**:\n\n1. The mask AP is lower than official [repo](https://github.com/facebookresearch/detectron2/tree/main/projects/ViTDet) slightly\n2. other model vision will release code and weights in the future\n\n## Citation\n\n```latex\n@article{li2022exploring,\n  title={Exploring plain vision transformer backbones for object detection},\n  author={Li, Yanghao and Mao, Hanzi and Girshick, Ross and He, Kaiming},\n  journal={arXiv preprint arXiv:2203.16527},\n  year={2022}\n}\n```\n\n## Checklist\n\n<!-- Here is a checklist illustrating a usual development workflow of a successful project, and also serves as an overview of this project's progress. The PIC (person in charge) or contributors of this project should check all the items that they believe have been finished, which will further be verified by codebase maintainers via a PR.\nOpenMMLab's maintainer will review the code to ensure the project's quality. Reaching the first milestone means that this project suffices the minimum requirement of being merged into 'projects/'. But this project is only eligible to become a part of the core package upon attaining the last milestone.\nNote that keeping this section up-to-date is crucial not only for this project's developers but the entire community, since there might be some other contributors joining this project and deciding their starting point from this list. It also helps maintainers accurately estimate time and effort on further code polishing, if needed.\nA project does not necessarily have to be finished in a single PR, but it's essential for the project to at least reach the first milestone in its very first PR. -->\n\n- [x] Milestone 1: PR-ready, and acceptable to be one of the `projects/`.\n\n  - [x] Finish the code\n\n    <!-- The code's design shall follow existing interfaces and convention. For example, each model component should be registered into `mmdet.registry.MODELS` and configurable via a config file. -->\n\n  - [x] Basic docstrings & proper citation\n\n    <!-- Each major object should contain a docstring, describing its functionality and arguments. If you have adapted the code from other open-source projects, don't forget to cite the source project in docstring and make sure your behavior is not against its license. Typically, we do not accept any code snippet under GPL license. [A Short Guide to Open Source Licenses](https://medium.com/nationwide-technology/a-short-guide-to-open-source-licenses-cf5b1c329edd) -->\n\n  - [x] Test-time correctness\n\n    <!-- If you are reproducing the result from a paper, make sure your model's inference-time performance matches that in the original paper. The weights usually could be obtained by simply renaming the keys in the official pre-trained weights. This test could be skipped though, if you are able to prove the training-time correctness and check the second milestone. -->\n\n  - [x] A full README\n\n    <!-- As this template does. -->\n\n- [x] Milestone 2: Indicates a successful model implementation.\n\n  - [x] Training-time correctness\n\n    <!-- If you are reproducing the result from a paper, checking this item means that you should have trained your model from scratch based on the original paper's specification and verified that the final result matches the report within a minor error range. -->\n\n- [ ] Milestone 3: Good to be a part of our core package!\n\n  - [ ] Type hints and docstrings\n\n    <!-- Ideally *all* the methods should have [type hints](https://www.pythontutorial.net/python-basics/python-type-hints/) and [docstrings](https://google.github.io/styleguide/pyguide.html#381-docstrings). [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/mmdet/datasets/transforms/transforms.py#L41-L169) -->\n\n  - [ ] Unit tests\n\n    <!-- Unit tests for each module are required. [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/tests/test_datasets/test_transforms/test_transforms.py#L35-L88) -->\n\n  - [ ] Code polishing\n\n    <!-- Refactor your code according to reviewer's comment. -->\n\n  - [ ] Metafile.yml\n\n    <!-- It will be parsed by MIM and Inferencer. [Example](https://github.com/open-mmlab/mmdetection/blob/3.x/configs/faster_rcnn/metafile.yml) -->\n\n- [ ] Move your modules into the core package following the codebase's file hierarchy structure.\n\n  <!-- In particular, you may have to refactor this README into a standard one. [Example](https://github.com/open-mmlab/mmdetection/blob/3.x/configs/faster_rcnn/README.md) -->\n\n- [ ] Refactor your modules into the core package following the codebase's file hierarchy structure.\n"
  },
  {
    "path": "projects/ViTDet/configs/lsj-100e_coco-instance.py",
    "content": "_base_ = [\n    '../../../configs/_base_/default_runtime.py',\n]\n\n# dataset settings\ndataset_type = 'CocoDataset'\ndata_root = 'data/coco/'\nimage_size = (1024, 1024)\n\nbackend_args = None\n\ntrain_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(type='RandomFlip', prob=0.5),\n    dict(\n        type='RandomResize',\n        scale=image_size,\n        ratio_range=(0.1, 2.0),\n        keep_ratio=True),\n    dict(\n        type='RandomCrop',\n        crop_type='absolute_range',\n        crop_size=image_size,\n        recompute_bbox=True,\n        allow_negative_crop=True),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)),\n    dict(type='Pad', size=image_size, pad_val=dict(img=(114, 114, 114))),\n    dict(type='PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=backend_args),\n    dict(type='Resize', scale=image_size, keep_ratio=True),\n    dict(type='Pad', size=image_size, pad_val=dict(img=(114, 114, 114))),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\ntrain_dataloader = dict(\n    batch_size=4,\n    num_workers=8,\n    persistent_workers=True,\n    sampler=dict(type='DefaultSampler', shuffle=True),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_train2017.json',\n        data_prefix=dict(img='train2017/'),\n        filter_cfg=dict(filter_empty_gt=True, min_size=32),\n        pipeline=train_pipeline))\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        ann_file='annotations/instances_val2017.json',\n        data_prefix=dict(img='val2017/'),\n        test_mode=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(\n    type='CocoMetric',\n    ann_file=data_root + 'annotations/instances_val2017.json',\n    metric=['bbox', 'segm'],\n    format_only=False)\ntest_evaluator = val_evaluator\n\noptim_wrapper = dict(\n    type='AmpOptimWrapper',\n    constructor='LayerDecayOptimizerConstructor',\n    paramwise_cfg={\n        'decay_rate': 0.7,\n        'decay_type': 'layer_wise',\n        'num_layers': 12,\n    },\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,\n        betas=(0.9, 0.999),\n        weight_decay=0.1,\n    ))\n\n# 100 ep = 184375 iters * 64 images/iter / 118000 images/ep\nmax_iters = 184375\ninterval = 5000\ndynamic_intervals = [(max_iters // interval * interval + 1, max_iters)]\nparam_scheduler = [\n    dict(\n        type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=250),\n    dict(\n        type='MultiStepLR',\n        begin=0,\n        end=max_iters,\n        by_epoch=False,\n        # 88 ep = [163889 iters * 64 images/iter / 118000 images/ep\n        # 96 ep = [177546 iters * 64 images/iter / 118000 images/ep\n        milestones=[163889, 177546],\n        gamma=0.1)\n]\n\ntrain_cfg = dict(\n    type='IterBasedTrainLoop',\n    max_iters=max_iters,\n    val_interval=interval,\n    dynamic_intervals=dynamic_intervals)\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n\ndefault_hooks = dict(\n    logger=dict(type='LoggerHook', interval=50),\n    checkpoint=dict(\n        type='CheckpointHook',\n        by_epoch=False,\n        save_last=True,\n        interval=interval,\n        max_keep_ckpts=5))\nvis_backends = [\n    dict(type='LocalVisBackend'),\n    dict(type='TensorboardVisBackend')\n]\nvisualizer = dict(\n    type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer')\nlog_processor = dict(type='LogProcessor', window_size=50, by_epoch=False)\n\nauto_scale_lr = dict(base_batch_size=64)\n"
  },
  {
    "path": "projects/ViTDet/configs/vitdet_mask-rcnn_vit-b-mae_lsj-100e.py",
    "content": "_base_ = [\n    '../../../configs/_base_/models/mask-rcnn_r50_fpn.py',\n    './lsj-100e_coco-instance.py',\n]\n\ncustom_imports = dict(imports=['projects.ViTDet.vitdet'])\n\nbackbone_norm_cfg = dict(type='LN', requires_grad=True)\nnorm_cfg = dict(type='LN2d', requires_grad=True)\nimage_size = (1024, 1024)\nbatch_augments = [\n    dict(type='BatchFixedSizePad', size=image_size, pad_mask=True)\n]\n\n# model settings\nmodel = dict(\n    data_preprocessor=dict(pad_size_divisor=32, batch_augments=batch_augments),\n    backbone=dict(\n        _delete_=True,\n        type='ViT',\n        img_size=1024,\n        patch_size=16,\n        embed_dim=768,\n        depth=12,\n        num_heads=12,\n        drop_path_rate=0.1,\n        window_size=14,\n        mlp_ratio=4,\n        qkv_bias=True,\n        norm_cfg=backbone_norm_cfg,\n        window_block_indexes=[\n            0,\n            1,\n            3,\n            4,\n            6,\n            7,\n            9,\n            10,\n        ],\n        use_rel_pos=True,\n        init_cfg=dict(\n            type='Pretrained', checkpoint='mae_pretrain_vit_base.pth')),\n    neck=dict(\n        _delete_=True,\n        type='SimpleFPN',\n        backbone_channel=768,\n        in_channels=[192, 384, 768, 768],\n        out_channels=256,\n        num_outs=5,\n        norm_cfg=norm_cfg),\n    rpn_head=dict(num_convs=2),\n    roi_head=dict(\n        bbox_head=dict(\n            type='Shared4Conv1FCBBoxHead',\n            conv_out_channels=256,\n            norm_cfg=norm_cfg),\n        mask_head=dict(norm_cfg=norm_cfg)))\n\ncustom_hooks = [dict(type='Fp16CompresssionHook')]\n"
  },
  {
    "path": "projects/ViTDet/vitdet/__init__.py",
    "content": "from .fp16_compression_hook import Fp16CompresssionHook\nfrom .layer_decay_optimizer_constructor import LayerDecayOptimizerConstructor\nfrom .simple_fpn import SimpleFPN\nfrom .vit import LN2d, ViT\n\n__all__ = [\n    'LayerDecayOptimizerConstructor', 'ViT', 'SimpleFPN', 'LN2d',\n    'Fp16CompresssionHook'\n]\n"
  },
  {
    "path": "projects/ViTDet/vitdet/fp16_compression_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmengine.hooks import Hook\n\nfrom mmdet.registry import HOOKS\n\n\n@HOOKS.register_module()\nclass Fp16CompresssionHook(Hook):\n    \"\"\"Support fp16 compression in DDP mode.\n\n    In detectron2, vitdet use Fp16CompresssionHook in training process\n    Fp16CompresssionHook can reduce training time and improve bbox mAP when you\n    use Fp16CompresssionHook, training time reduce form 3 days to 2 days and\n    box mAP from 51.4 to 51.6\n    \"\"\"\n\n    def before_train(self, runner):\n\n        if runner.distributed:\n            if runner.cfg.get('model_wrapper_cfg') is None:\n                from torch.distributed.algorithms.ddp_comm_hooks import \\\n                    default as comm_hooks\n                runner.model.register_comm_hook(\n                    state=None, hook=comm_hooks.fp16_compress_hook)\n                runner.logger.info('use fp16 compression in DDP mode')\n"
  },
  {
    "path": "projects/ViTDet/vitdet/layer_decay_optimizer_constructor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nfrom typing import List\n\nimport torch.nn as nn\nfrom mmengine.dist import get_dist_info\nfrom mmengine.logging import MMLogger\nfrom mmengine.optim import DefaultOptimWrapperConstructor\n\nfrom mmdet.registry import OPTIM_WRAPPER_CONSTRUCTORS\n\n\ndef get_layer_id_for_vit(var_name, max_layer_id):\n    \"\"\"Get the layer id to set the different learning rates in ``layer_wise``\n    decay_type.\n\n    Args:\n        var_name (str): The key of the model.\n        max_layer_id (int): Maximum layer id.\n    Returns:\n        int: The id number corresponding to different learning rate in\n        ``LayerDecayOptimizerConstructor``.\n    \"\"\"\n    if var_name.startswith('backbone'):\n        if 'patch_embed' in var_name or 'pos_embed' in var_name:\n            return 0\n        elif '.blocks.' in var_name:\n            layer_id = int(var_name.split('.')[2]) + 1\n            return layer_id\n        else:\n            return max_layer_id + 1\n    else:\n        return max_layer_id + 1\n\n\n@OPTIM_WRAPPER_CONSTRUCTORS.register_module()\nclass LayerDecayOptimizerConstructor(DefaultOptimWrapperConstructor):\n    # Different learning rates are set for different layers of backbone.\n    # Note: Currently, this optimizer constructor is built for ViT.\n\n    def add_params(self, params: List[dict], module: nn.Module,\n                   **kwargs) -> None:\n        \"\"\"Add all parameters of module to the params list.\n\n        The parameters of the given module will be added to the list of param\n        groups, with specific rules defined by paramwise_cfg.\n        Args:\n            params (list[dict]): A list of param groups, it will be modified\n                in place.\n            module (nn.Module): The module to be added.\n        \"\"\"\n        logger = MMLogger.get_current_instance()\n\n        parameter_groups = {}\n        logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}')\n        num_layers = self.paramwise_cfg.get('num_layers') + 2\n        decay_rate = self.paramwise_cfg.get('decay_rate')\n        decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise')\n        logger.info('Build LayerDecayOptimizerConstructor  '\n                    f'{decay_type} {decay_rate} - {num_layers}')\n        weight_decay = self.base_wd\n\n        for name, param in module.named_parameters():\n            if not param.requires_grad:\n                continue  # frozen weights\n            if name.startswith('backbone.blocks') and 'norm' in name:\n                group_name = 'no_decay'\n                this_weight_decay = 0.\n            elif 'pos_embed' in name:\n                group_name = 'no_decay_pos_embed'\n                this_weight_decay = 0\n            else:\n                group_name = 'decay'\n                this_weight_decay = weight_decay\n\n            layer_id = get_layer_id_for_vit(\n                name, self.paramwise_cfg.get('num_layers'))\n            logger.info(f'set param {name} as id {layer_id}')\n\n            group_name = f'layer_{layer_id}_{group_name}'\n            this_lr_multi = 1.\n\n            if group_name not in parameter_groups:\n                scale = decay_rate**(num_layers - 1 - layer_id)\n\n                parameter_groups[group_name] = {\n                    'weight_decay': this_weight_decay,\n                    'params': [],\n                    'param_names': [],\n                    'lr_scale': scale,\n                    'group_name': group_name,\n                    'lr': scale * self.base_lr * this_lr_multi,\n                }\n\n            parameter_groups[group_name]['params'].append(param)\n            parameter_groups[group_name]['param_names'].append(name)\n\n        rank, _ = get_dist_info()\n        if rank == 0:\n            to_display = {}\n            for key in parameter_groups:\n                to_display[key] = {\n                    'param_names': parameter_groups[key]['param_names'],\n                    'lr_scale': parameter_groups[key]['lr_scale'],\n                    'lr': parameter_groups[key]['lr'],\n                    'weight_decay': parameter_groups[key]['weight_decay'],\n                }\n            logger.info(f'Param groups = {json.dumps(to_display, indent=2)}')\n        params.extend(parameter_groups.values())\n"
  },
  {
    "path": "projects/ViTDet/vitdet/simple_fpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom typing import List\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import ConvModule, build_norm_layer\nfrom mmengine.model import BaseModule\nfrom torch import Tensor\n\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import MultiConfig, OptConfigType\n\n\n@MODELS.register_module()\nclass SimpleFPN(BaseModule):\n    \"\"\"Simple Feature Pyramid Network for ViTDet.\"\"\"\n\n    def __init__(self,\n                 backbone_channel: int,\n                 in_channels: List[int],\n                 out_channels: int,\n                 num_outs: int,\n                 conv_cfg: OptConfigType = None,\n                 norm_cfg: OptConfigType = None,\n                 act_cfg: OptConfigType = None,\n                 init_cfg: MultiConfig = None) -> None:\n        super().__init__(init_cfg=init_cfg)\n        assert isinstance(in_channels, list)\n        self.backbone_channel = backbone_channel\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)\n        self.num_outs = num_outs\n\n        self.fpn1 = nn.Sequential(\n            nn.ConvTranspose2d(self.backbone_channel,\n                               self.backbone_channel // 2, 2, 2),\n            build_norm_layer(norm_cfg, self.backbone_channel // 2)[1],\n            nn.GELU(),\n            nn.ConvTranspose2d(self.backbone_channel // 2,\n                               self.backbone_channel // 4, 2, 2))\n        self.fpn2 = nn.Sequential(\n            nn.ConvTranspose2d(self.backbone_channel,\n                               self.backbone_channel // 2, 2, 2))\n        self.fpn3 = nn.Sequential(nn.Identity())\n        self.fpn4 = nn.Sequential(nn.MaxPool2d(kernel_size=2, stride=2))\n\n        self.lateral_convs = nn.ModuleList()\n        self.fpn_convs = nn.ModuleList()\n\n        for i in range(self.num_ins):\n            l_conv = ConvModule(\n                in_channels[i],\n                out_channels,\n                1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg,\n                inplace=False)\n            fpn_conv = ConvModule(\n                out_channels,\n                out_channels,\n                3,\n                padding=1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg,\n                inplace=False)\n\n            self.lateral_convs.append(l_conv)\n            self.fpn_convs.append(fpn_conv)\n\n    def forward(self, input: Tensor) -> tuple:\n        \"\"\"Forward function.\n\n        Args:\n            inputs (Tensor): Features from the upstream network, 4D-tensor\n        Returns:\n            tuple: Feature maps, each is a 4D-tensor.\n        \"\"\"\n        # build FPN\n        inputs = []\n        inputs.append(self.fpn1(input))\n        inputs.append(self.fpn2(input))\n        inputs.append(self.fpn3(input))\n        inputs.append(self.fpn4(input))\n\n        # build laterals\n        laterals = [\n            lateral_conv(inputs[i])\n            for i, lateral_conv in enumerate(self.lateral_convs)\n        ]\n\n        # build outputs\n        # part 1: from original levels\n        outs = [self.fpn_convs[i](laterals[i]) for i in range(self.num_ins)]\n\n        # part 2: add extra levels\n        if self.num_outs > len(outs):\n            for i in range(self.num_outs - self.num_ins):\n                outs.append(F.max_pool2d(outs[-1], 1, stride=2))\n        return tuple(outs)\n"
  },
  {
    "path": "projects/ViTDet/vitdet/vit.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport math\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom mmcv.cnn import build_activation_layer, build_norm_layer\nfrom mmcv.cnn.bricks import DropPath\nfrom mmengine.logging import MMLogger\nfrom mmengine.model import BaseModule\nfrom mmengine.runner.checkpoint import CheckpointLoader\n\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass LN2d(nn.Module):\n    \"\"\"A LayerNorm variant, popularized by Transformers, that performs\n    pointwise mean and variance normalization over the channel dimension for\n    inputs that have shape (batch_size, channels, height, width).\"\"\"\n\n    def __init__(self, normalized_shape, eps=1e-6):\n        super().__init__()\n        self.weight = nn.Parameter(torch.ones(normalized_shape))\n        self.bias = nn.Parameter(torch.zeros(normalized_shape))\n        self.eps = eps\n        self.normalized_shape = (normalized_shape, )\n\n    def forward(self, x):\n        u = x.mean(1, keepdim=True)\n        s = (x - u).pow(2).mean(1, keepdim=True)\n        x = (x - u) / torch.sqrt(s + self.eps)\n        x = self.weight[:, None, None] * x + self.bias[:, None, None]\n        return x\n\n\ndef get_abs_pos(abs_pos, has_cls_token, hw):\n    h, w = hw\n    if has_cls_token:\n        abs_pos = abs_pos[:, 1:]\n    xy_num = abs_pos.shape[1]\n    size = int(math.sqrt(xy_num))\n    assert size * size == xy_num\n\n    if size != h or size != w:\n        new_abs_pos = F.interpolate(\n            abs_pos.reshape(1, size, size, -1).permute(0, 3, 1, 2),\n            size=(h, w),\n            mode='bicubic',\n            align_corners=False,\n        )\n\n        return new_abs_pos.permute(0, 2, 3, 1)\n    else:\n        return abs_pos.reshape(1, h, w, -1)\n\n\ndef get_rel_pos(q_size, k_size, rel_pos):\n    \"\"\"\n    Get relative positional embeddings according to the relative positions\n    of query and key sizes.\n    Args:\n        q_size (int): size of query q.\n        k_size (int): size of key k.\n        rel_pos (Tensor): relative position embeddings (L, C).\n\n    Returns:\n        Extracted positional embeddings according to relative positions.\n    \"\"\"\n    max_rel_dist = int(2 * max(q_size, k_size) - 1)\n    # Interpolate rel pos if needed.\n    if rel_pos.shape[0] != max_rel_dist:\n        # Interpolate rel pos.\n        rel_pos_resized = F.interpolate(\n            rel_pos.reshape(1, rel_pos.shape[0], -1).permute(0, 2, 1),\n            size=max_rel_dist,\n            mode='linear',\n        )\n        rel_pos_resized = rel_pos_resized.reshape(-1,\n                                                  max_rel_dist).permute(1, 0)\n    else:\n        rel_pos_resized = rel_pos\n\n    # Scale the coords with short length if shapes for q and k are different.\n    q_coords = torch.arange(q_size)[:, None] * max(k_size / q_size, 1.0)\n    k_coords = torch.arange(k_size)[None, :] * max(q_size / k_size, 1.0)\n    relative_coords = (q_coords -\n                       k_coords) + (k_size - 1) * max(q_size / k_size, 1.0)\n\n    return rel_pos_resized[relative_coords.long()]\n\n\ndef add_decomposed_rel_pos(attn, q, rel_pos_h, rel_pos_w, q_size, k_size):\n    \"\"\"\n    Args:\n        attn (Tensor): attention map.\n        q (Tensor):\n            query q in the attention layer with shape (B, q_h * q_w, C).\n        rel_pos_h (Tensor):\n            relative position embeddings (Lh, C) for height axis.\n        rel_pos_w (Tensor):\n            relative position embeddings (Lw, C) for width axis.\n        q_size (Tuple):\n            spatial sequence size of query q with (q_h, q_w).\n        k_size (Tuple):\n            spatial sequence size of key k with (k_h, k_w).\n\n    Returns:\n        attn (Tensor): attention map with added relative positional embeddings.\n    \"\"\"\n    q_h, q_w = q_size\n    k_h, k_w = k_size\n    Rh = get_rel_pos(q_h, k_h, rel_pos_h)\n    Rw = get_rel_pos(q_w, k_w, rel_pos_w)\n\n    B, _, dim = q.shape\n    r_q = q.reshape(B, q_h, q_w, dim)\n    rel_h = torch.einsum('bhwc,hkc->bhwk', r_q, Rh)\n    rel_w = torch.einsum('bhwc,wkc->bhwk', r_q, Rw)\n\n    attn = (attn.view(B, q_h, q_w, k_h, k_w) + rel_h[:, :, :, :, None] +\n            rel_w[:, :, :, None, :]).view(B, q_h * q_w, k_h * k_w)\n\n    return attn\n\n\ndef window_partition(x, window_size):\n    \"\"\"\n    Args:\n        x: (B, H, W, C)\n        window_size (int): window size\n    Returns:\n        windows: (num_windows*B, window_size, window_size, C)\n    \"\"\"\n    B, H, W, C = x.shape\n\n    pad_h = (window_size - H % window_size) % window_size\n    pad_w = (window_size - W % window_size) % window_size\n    if pad_h > 0 or pad_w > 0:\n        x = F.pad(x, (0, 0, 0, pad_w, 0, pad_h))\n    Hp, Wp = H + pad_h, W + pad_w\n\n    x = x.view(B, Hp // window_size, window_size, Wp // window_size,\n               window_size, C)\n    windows = x.permute(0, 1, 3, 2, 4,\n                        5).contiguous().view(-1, window_size, window_size, C)\n    return windows, (Hp, Wp)\n\n\ndef window_unpartition(windows, window_size, pad_hw, hw):\n    \"\"\"\n    Args:\n        windows: (num_windows*B, window_size, window_size, C)\n        window_size (int): Window size\n        H (int): Height of image\n        W (int): Width of image\n    Returns:\n        x: (B, H, W, C)\n    \"\"\"\n    Hp, Wp = pad_hw\n    H, W = hw\n    B = windows.shape[0] // (Hp * Wp // window_size // window_size)\n    x = windows.view(B, Hp // window_size, Wp // window_size, window_size,\n                     window_size, -1)\n    x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, Hp, Wp, -1)\n\n    if Hp > H or Wp > W:\n        x = x[:, :H, :W, :].contiguous()\n    return x\n\n\nclass Attention(nn.Module):\n\n    def __init__(self,\n                 dim,\n                 num_heads=8,\n                 qkv_bias=True,\n                 use_rel_pos=False,\n                 rel_pos_zero_init=True,\n                 input_size=None):\n        super().__init__()\n        self.num_heads = num_heads\n        head_dim = dim // num_heads\n        self.scale = head_dim**-0.5\n\n        self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)\n        self.proj = nn.Linear(dim, dim)\n\n        self.use_rel_pos = use_rel_pos\n        if self.use_rel_pos:\n            # initialize relative positional embeddings\n            self.rel_pos_h = nn.Parameter(\n                torch.zeros(2 * input_size[0] - 1, head_dim))\n            self.rel_pos_w = nn.Parameter(\n                torch.zeros(2 * input_size[1] - 1, head_dim))\n\n            if not rel_pos_zero_init:\n                nn.init.trunc_normal_(self.rel_pos_h, std=0.02)\n                nn.init.trunc_normal_(self.rel_pos_w, std=0.02)\n\n    def forward(self, x):\n        B, H, W, _ = x.shape\n        # qkv with shape (3, B, nHead, H * W, C)\n        qkv = self.qkv(x).reshape(B, H * W, 3, self.num_heads,\n                                  -1).permute(2, 0, 3, 1, 4)\n        # q, k, v with shape (B * nHead, H * W, C)\n        q, k, v = qkv.reshape(3, B * self.num_heads, H * W, -1).unbind(0)\n\n        attn = (q * self.scale) @ k.transpose(-2, -1)\n\n        if self.use_rel_pos:\n            attn = add_decomposed_rel_pos(attn, q, self.rel_pos_h,\n                                          self.rel_pos_w, (H, W), (H, W))\n\n        attn = attn.softmax(dim=-1)\n        x = (attn @ v).view(B, self.num_heads, H, W,\n                            -1).permute(0, 2, 3, 1, 4).reshape(B, H, W, -1)\n        x = self.proj(x)\n\n        return x\n\n\nclass Mlp(nn.Module):\n    \"\"\"MLP as used in Vision Transformer, MLP-Mixer and related networks.\"\"\"\n\n    def __init__(\n            self,\n            in_features,\n            hidden_features=None,\n            out_features=None,\n            act_cfg=dict(type='GELU'),\n            bias=True,\n            drop=0.,\n    ):\n        super().__init__()\n        out_features = out_features or in_features\n        hidden_features = hidden_features or in_features\n\n        self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)\n        self.act = build_activation_layer(act_cfg)\n        self.drop1 = nn.Dropout(drop)\n        self.fc2 = nn.Linear(hidden_features, out_features, bias=bias)\n        self.drop2 = nn.Dropout(drop)\n\n    def forward(self, x):\n        x = self.fc1(x)\n        x = self.act(x)\n        x = self.drop1(x)\n        x = self.fc2(x)\n        x = self.drop2(x)\n        return x\n\n\nclass Block(nn.Module):\n\n    def __init__(\n        self,\n        dim,\n        num_heads,\n        mlp_ratio=4.0,\n        qkv_bias=True,\n        drop_path=0.0,\n        norm_cfg=dict(type='LN', eps=1e-6),\n        act_cfg=dict(type='GELU'),\n        use_rel_pos=False,\n        rel_pos_zero_init=True,\n        window_size=0,\n        input_size=None,\n    ):\n        super().__init__()\n        self.norm1 = build_norm_layer(norm_cfg, dim)[1]\n        self.attn = Attention(\n            dim,\n            num_heads=num_heads,\n            qkv_bias=qkv_bias,\n            use_rel_pos=use_rel_pos,\n            rel_pos_zero_init=rel_pos_zero_init,\n            input_size=input_size if window_size == 0 else\n            (window_size, window_size),\n        )\n\n        self.drop_path = DropPath(\n            drop_path) if drop_path > 0. else nn.Identity()\n        self.norm2 = build_norm_layer(norm_cfg, dim)[1]\n        self.mlp = Mlp(\n            in_features=dim,\n            hidden_features=int(dim * mlp_ratio),\n            act_cfg=act_cfg)\n\n        self.window_size = window_size\n\n    def forward(self, x):\n        shortcut = x\n        x = self.norm1(x)\n        # Window partition\n        if self.window_size > 0:\n            H, W = x.shape[1], x.shape[2]\n            x, pad_hw = window_partition(x, self.window_size)\n\n        x = self.attn(x)\n        # Reverse window partition\n        if self.window_size > 0:\n            x = window_unpartition(x, self.window_size, pad_hw, (H, W))\n\n        x = shortcut + self.drop_path(x)\n        x = x + self.drop_path(self.mlp(self.norm2(x)))\n\n        return x\n\n\nclass PatchEmbed(nn.Module):\n    \"\"\"Image to Patch Embedding.\"\"\"\n\n    def __init__(self,\n                 kernel_size=(16, 16),\n                 stride=(16, 16),\n                 padding=(0, 0),\n                 in_chans=3,\n                 embed_dim=768):\n        \"\"\"\n        Args:\n            kernel_size (Tuple): kernel size of the projection layer.\n            stride (Tuple): stride of the projection layer.\n            padding (Tuple): padding size of the projection layer.\n            in_chans (int): Number of input image channels.\n            embed_dim (int):  embed_dim (int): Patch embedding dimension.\n        \"\"\"\n        super().__init__()\n\n        self.proj = nn.Conv2d(\n            in_chans,\n            embed_dim,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding)\n\n    def forward(self, x):\n        x = self.proj(x)\n        # B C H W -> B H W C\n        x = x.permute(0, 2, 3, 1)\n        return x\n\n\n@MODELS.register_module()\nclass ViT(BaseModule):\n    \"\"\"Vision Transformer with support for patch or hybrid CNN input stage.\"\"\"\n\n    def __init__(self,\n                 img_size=1024,\n                 patch_size=16,\n                 in_chans=3,\n                 embed_dim=768,\n                 depth=12,\n                 num_heads=12,\n                 mlp_ratio=4.0,\n                 qkv_bias=True,\n                 drop_path_rate=0.0,\n                 norm_cfg=dict(type='LN', eps=1e-6),\n                 act_cfg=dict(type='GELU'),\n                 use_abs_pos=True,\n                 use_rel_pos=False,\n                 rel_pos_zero_init=True,\n                 window_size=0,\n                 window_block_indexes=(0, 1, 3, 4, 6, 7, 9, 10),\n                 pretrain_img_size=224,\n                 pretrain_use_cls_token=True,\n                 init_cfg=None):\n\n        super().__init__()\n        self.pretrain_use_cls_token = pretrain_use_cls_token\n        self.init_cfg = init_cfg\n\n        self.patch_embed = PatchEmbed(\n            kernel_size=(patch_size, patch_size),\n            stride=(patch_size, patch_size),\n            in_chans=in_chans,\n            embed_dim=embed_dim)\n\n        if use_abs_pos:\n            num_patches = (pretrain_img_size // patch_size) * (\n                pretrain_img_size // patch_size)\n            num_positions = (num_patches +\n                             1) if pretrain_use_cls_token else num_patches\n            self.pos_embed = nn.Parameter(\n                torch.zeros(1, num_positions, embed_dim))\n        else:\n            self.pos_embed = None\n\n        dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)]\n\n        self.blocks = nn.ModuleList([\n            Block(\n                dim=embed_dim,\n                num_heads=num_heads,\n                mlp_ratio=mlp_ratio,\n                qkv_bias=qkv_bias,\n                drop_path=dpr[i],\n                norm_cfg=norm_cfg,\n                act_cfg=act_cfg,\n                use_rel_pos=use_rel_pos,\n                rel_pos_zero_init=rel_pos_zero_init,\n                window_size=window_size if i in window_block_indexes else 0,\n                input_size=(img_size // patch_size, img_size // patch_size))\n            for i in range(depth)\n        ])\n\n        if self.pos_embed is not None:\n            nn.init.trunc_normal_(self.pos_embed, std=0.02)\n\n    def _init_weights(self, m):\n        if isinstance(m, nn.Linear):\n            nn.init.trunc_normal_(m.weight, std=0.02)\n            if isinstance(m, nn.Linear) and m.bias is not None:\n                nn.init.constant_(m.bias, 0)\n        elif isinstance(m, nn.LayerNorm):\n            nn.init.constant_(m.bias, 0)\n            nn.init.constant_(m.weight, 1.0)\n\n    def init_weights(self):\n        logger = MMLogger.get_current_instance()\n        if self.init_cfg is None:\n            logger.warn(f'No pre-trained weights for '\n                        f'{self.__class__.__name__}, '\n                        f'training start from scratch')\n            self.apply(self._init_weights)\n        else:\n            assert 'checkpoint' in self.init_cfg, f'Only support ' \\\n                                                  f'specify `Pretrained` in ' \\\n                                                  f'`init_cfg` in ' \\\n                                                  f'{self.__class__.__name__} '\n            ckpt = CheckpointLoader.load_checkpoint(\n                self.init_cfg.checkpoint, logger=logger, map_location='cpu')\n            if 'model' in ckpt:\n                _state_dict = ckpt['model']\n            self.load_state_dict(_state_dict, False)\n\n    def forward(self, x):\n        x = self.patch_embed(x)\n        if self.pos_embed is not None:\n            x = x + get_abs_pos(self.pos_embed, self.pretrain_use_cls_token,\n                                (x.shape[1], x.shape[2]))\n\n        for blk in self.blocks:\n            x = blk(x)\n\n        x = x.permute(0, 3, 1, 2)\n\n        return x\n"
  },
  {
    "path": "projects/XDecoder/README.md",
    "content": "# X-Decoder\n\n> [X-Decoder: Generalized Decoding for Pixel, Image, and Language](https://arxiv.org/pdf/2212.11270.pdf)\n\n<!-- [ALGORITHM] -->\n\n## Abstract\n\nWe present X-Decoder, a generalized decoding model that can predict pixel-level segmentation and language tokens seamlessly. X-Decodert takes as input two types of queries: (i) generic non-semantic queries and (ii) semantic queries induced from text inputs, to decode different pixel-level and token-level outputs in the same semantic space. With such a novel design, X-Decoder is the first work that provides a unified way to support all types of image segmentation and a variety of vision-language (VL) tasks. Further, our design enables seamless interactions across tasks at different granularities and brings mutual benefits by learning a common and rich pixel-level visual-semantic understanding space, without any pseudo-labeling. After pretraining on a mixed set of a limited amount of segmentation data and millions of image-text pairs, X-Decoder exhibits strong transferability to a wide range of downstream tasks in both zero-shot and finetuning settings. Notably, it achieves (1) state-of-the-art results on open-vocabulary segmentation and referring segmentation on eight datasets; (2) better or competitive finetuned performance to other generalist and specialist models on segmentation and VL tasks; and (3) flexibility for efficient finetuning and novel task composition (e.g., referring captioning and image editing).\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/cb126615-9402-4c19-8ea9-133722d7519c\" width=\"70%\"/>\n</div>\n\n## Installation\n\n```shell\n# if source\npip install -r requirements/multimodal.txt\n\n# if wheel\nmim install mmdet[multimodal]\n```\n\n## How to use it?\n\nFor convenience, you can download the weights to the `mmdetection` root dir\n\n```shell\nwget https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt\nwget https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_best_openseg.pt\n```\n\nThe above two weights are directly copied from the official website without any modification. The specific source is https://github.com/microsoft/X-Decoder\n\nFor convenience of demonstration, please download [the folder](https://github.com/microsoft/X-Decoder/tree/main/inference_demo/images) and place it in the root directory of mmdetection.\n\n**(1) Open Vocabulary Semantic Segmentation**\n\n```shell\ncd projects/XDecoder\npython demo.py ../../images/animals.png configs/xdecoder-tiny_zeroshot_open-vocab-semseg_coco.py --weights ../../xdecoder_focalt_last_novg.pt --texts zebra.giraffe\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/c397c0ed-859a-4004-8725-78a591742bc8\" width=\"70%\"/>\n</div>\n\n**(2) Open Vocabulary Instance Segmentation**\n\n```shell\ncd projects/XDecoder\npython demo.py ../../images/owls.jpeg configs/xdecoder-tiny_zeroshot_open-vocab-instance_coco.py --weights ../../xdecoder_focalt_last_novg.pt --texts owl\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/494b0b1c-4a42-4019-97ae-d33ee68af3d2\" width=\"70%\"/>\n</div>\n\n**(3) Open Vocabulary Panoptic Segmentation**\n\n```shell\ncd projects/XDecoder\npython demo.py ../../images/street.jpg configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_coco.py --weights ../../xdecoder_focalt_last_novg.pt  --text car.person --stuff-text tree.sky\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/9ad1e0f4-75ce-4e37-a5cc-83e0e8a722ed\" width=\"70%\"/>\n</div>\n\n**(4) Referring Expression Segmentation**\n\n```shell\ncd projects/XDecoder\npython demo.py ../../images/fruit.jpg configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcocog.py --weights ../../xdecoder_focalt_last_novg.pt  --text \"The larger watermelon. The front white flower. White tea pot.\"\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/f3ecdb50-20f0-4dc4-aa9c-90995ae04893\" width=\"70%\"/>\n</div>\n\n**(5) Image Caption**\n\n```shell\ncd projects/XDecoder\npython demo.py ../../images/penguin.jpeg configs/xdecoder-tiny_zeroshot_caption_coco2014.py --weights ../../xdecoder_focalt_last_novg.pt\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/7690ab79-791e-4011-ab0c-01f46c4a3d80\" width=\"70%\"/>\n</div>\n\n**(6) Referring Expression Image Caption**\n\n```shell\ncd projects/XDecoder\npython demo.py ../../images/fruit.jpg configs/xdecoder-tiny_zeroshot_ref-caption.py --weights ../../xdecoder_focalt_last_novg.pt --text 'White tea pot'\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/bae2fdba-0172-4fc8-8ad1-73b54c64ec30\" width=\"70%\"/>\n</div>\n\n**(7) Text Image Region Retrieval**\n\n```shell\ncd projects/XDecoder\npython demo.py ../../images/coco configs/xdecoder-tiny_zeroshot_text-image-retrieval.py --weights ../../xdecoder_focalt_last_novg.pt --text 'pizza on the plate'\n```\n\n```text\nThe image that best matches the given text is ../../images/coco/000.jpg and probability is 0.998\n```\n\n<div align=center>\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/479de6b2-88e7-41f0-8228-4b9a48f52954\" width=\"70%\"/>\n</div>\n\nWe have also prepared a gradio program in the `projects/gradio_demo` directory, which you can run interactively all the inference supported by mmdetection in your browser.\n\n## Models and results\n\n### Semantic segmentation on ADE20K\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#ade20k-2016-dataset-preparation).\n\n**Test Command**\n\nSince semantic segmentation is a pixel-level task, we don't need to use a threshold to filter out low-confidence predictions. So we set `model.test_cfg.use_thr_for_mc=False` in the test command.\n\n```shell\n./tools/dist_test.sh projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-semseg_ade20k.py xdecoder_focalt_best_openseg.pt 8 --cfg-options model.test_cfg.use_thr_for_mc=False\n```\n\n| Model                             | mIoU  | mIOU(official) |                                Config                                |\n| :-------------------------------- | :---: | :------------: | :------------------------------------------------------------------: |\n| `xdecoder_focalt_best_openseg.pt` | 25.24 |     25.13      | [config](configs/xdecoder-tiny_zeroshot_open-vocab-semseg_ade20k.py) |\n\n### Instance segmentation on ADE20K\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#ade20k-2016-dataset-preparation).\n\n```shell\n./tools/dist_test.sh projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-instance_ade20k.py xdecoder_focalt_best_openseg.pt 8\n```\n\n| Model                             | mIoU | mIOU(official) |                                 Config                                 |\n| :-------------------------------- | :--: | :------------: | :--------------------------------------------------------------------: |\n| `xdecoder_focalt_best_openseg.pt` | 10.1 |      10.1      | [config](configs/xdecoder-tiny_zeroshot_open-vocab-instance_ade20k.py) |\n\n### Panoptic segmentation on ADE20K\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#ade20k-2016-dataset-preparation).\n\n```shell\n./tools/dist_test.sh projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_ade20k.py xdecoder_focalt_best_openseg.pt 8\n```\n\n| Model                             | mIoU  | mIOU(official) |                                 Config                                 |\n| :-------------------------------- | :---: | :------------: | :--------------------------------------------------------------------: |\n| `xdecoder_focalt_best_openseg.pt` | 19.11 |     18.97      | [config](configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_ade20k.py) |\n\n### Semantic segmentation on COCO2017\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#coco-semantic-dataset-preparation) of `(2) use panoptic dataset` part.\n\n```shell\n./tools/dist_test.sh projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-semseg_coco.py xdecoder_focalt_last_novg.pt 8 --cfg-options model.test_cfg.use_thr_for_mc=False\n```\n\n| Model                                           | mIOU | mIOU(official) |                               Config                               |\n| :---------------------------------------------- | :--: | :------------: | :----------------------------------------------------------------: |\n| `xdecoder-tiny_zeroshot_open-vocab-semseg_coco` | 62.1 |      62.1      | [config](configs/xdecoder-tiny_zeroshot_open-vocab-semseg_coco.py) |\n\n### Instance segmentation on COCO2017\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#basic-detection-dataset-preparation).\n\n```shell\n./tools/dist_test.sh projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-instance_coco.py xdecoder_focalt_last_novg.pt 8\n```\n\n| Model                                             | Mask mAP | Mask mAP(official) |                                Config                                |\n| :------------------------------------------------ | :------: | :----------------: | :------------------------------------------------------------------: |\n| `xdecoder-tiny_zeroshot_open-vocab-instance_coco` |   39.8   |        39.7        | [config](configs/xdecoder-tiny_zeroshot_open-vocab-instance_coco.py) |\n\n### Panoptic segmentation on COCO2017\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#basic-detection-dataset-preparation).\n\n```shell\n./tools/dist_test.sh projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_coco.py xdecoder_focalt_last_novg.pt 8\n```\n\n| Model                                             |  PQ   | PQ(official) |                                Config                                |\n| :------------------------------------------------ | :---: | :----------: | :------------------------------------------------------------------: |\n| `xdecoder-tiny_zeroshot_open-vocab-panoptic_coco` | 51.42 |    51.16     | [config](configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_coco.py) |\n\n### Referring segmentation on RefCOCO\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#refcoco-dataset-preparation).\n\n```shell\n./tools/dist_test.sh  projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcocog.py xdecoder_focalt_last_novg.pt 8  --cfg-options test_dataloader.dataset.split='val'\n```\n\n| Model                          |  text mode   |  cIoU   | cIOU(official) |                                 Config                                  |\n| :----------------------------- | :----------: | :-----: | :------------: | :---------------------------------------------------------------------: |\n| `xdecoder_focalt_last_novg.pt` | select first | 58.8415 |     57.85      | [config](configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcocog.py) |\n| `xdecoder_focalt_last_novg.pt` |   original   | 60.0321 |       -        | [config](configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcocog.py) |\n| `xdecoder_focalt_last_novg.pt` |    concat    | 60.3551 |       -        | [config](configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcocog.py) |\n\n**Note:**\n\n1. If you set the scale of `Resize` to (1024, 512), the result will be `57.69`.\n2. `text mode` is the `RefCoCoDataset` parameter in MMDetection, it determines the texts loaded to the data list. It can be set to `select_first`, `original`, `concat` and `random`.\n   - `select_first`: select the first text in the text list as the description to an instance.\n   - `original`: use all texts in the text list as the description to an instance.\n   - `concat`: concatenate all texts in the text list as the description to an instance.\n   - `random`: randomly select one text in the text list as the description to an instance, usually used for training.\n\n### Image Caption on COCO2014\n\nPrepare your dataset according to the [docs](../../docs/en/user_guides/dataset_prepare.md#coco-caption-dataset-preparation).\n\nBefore testing, you need to install jdk 1.8, otherwise it will prompt that java does not exist during the evaluation process\n\n```\n./tools/dist_test.sh projects/XDecoder/configs/xdecoder-tiny_zeroshot_caption_coco2014.py xdecoder_focalt_last_novg.pt 8\n```\n\n| Model                                     | BLEU-4 | CIDER  |                            Config                            |\n| :---------------------------------------- | :----: | :----: | :----------------------------------------------------------: |\n| `xdecoder-tiny_zeroshot_caption_coco2014` | 35.26  | 116.81 | [config](configs/xdecoder-tiny_zeroshot_caption_coco2014.py) |\n\n## Citation\n\n```latex\n@article{zou2022xdecoder,\n  author      = {Zou*, Xueyan and Dou*, Zi-Yi and Yang*, Jianwei and Gan, Zhe and Li, Linjie and Li, Chunyuan and Dai, Xiyang and Wang, Jianfeng and Yuan, Lu and Peng, Nanyun and Wang, Lijuan and Lee*, Yong Jae and Gao*, Jianfeng},\n  title       = {Generalized Decoding for Pixel, Image and Language},\n  publisher   = {arXiv},\n  year        = {2022},\n}\n```\n"
  },
  {
    "path": "projects/XDecoder/configs/_base_/xdecoder-tiny_caption.py",
    "content": "_base_ = 'xdecoder-tiny_open-vocab-semseg.py'\n\nmodel = dict(head=dict(task='caption'))\n"
  },
  {
    "path": "projects/XDecoder/configs/_base_/xdecoder-tiny_open-vocab-instance.py",
    "content": "_base_ = 'xdecoder-tiny_open-vocab-semseg.py'\n\nmodel = dict(head=dict(task='instance'), test_cfg=dict(max_per_img=100))\n"
  },
  {
    "path": "projects/XDecoder/configs/_base_/xdecoder-tiny_open-vocab-panoptic.py",
    "content": "_base_ = 'xdecoder-tiny_open-vocab-semseg.py'\n\nmodel = dict(\n    head=dict(task='panoptic'), test_cfg=dict(mask_thr=0.8, overlap_thr=0.8))\n"
  },
  {
    "path": "projects/XDecoder/configs/_base_/xdecoder-tiny_open-vocab-semseg.py",
    "content": "_base_ = 'mmdet::_base_/default_runtime.py'\n\ncustom_imports = dict(\n    imports=['projects.XDecoder.xdecoder'], allow_failed_imports=False)\n\nmodel = dict(\n    type='XDecoder',\n    data_preprocessor=dict(\n        type='DetDataPreprocessor',\n        mean=[123.675, 116.28, 103.53],\n        std=[58.395, 57.12, 57.375],\n        bgr_to_rgb=True,\n        pad_size_divisor=32),\n    backbone=dict(type='FocalNet'),\n    head=dict(\n        type='XDecoderUnifiedhead',\n        in_channels=(96, 192, 384, 768),\n        pixel_decoder=dict(type='XTransformerEncoderPixelDecoder'),\n        transformer_decoder=dict(type='XDecoderTransformerDecoder'),\n        task='semseg',\n    ),\n    # use_thr_for_mc=True means use threshold for multi-class\n    # This parameter is only used in semantic segmentation task and\n    # referring semantic segmentation task.\n    test_cfg=dict(mask_thr=0.5, use_thr_for_mc=True, ignore_index=255),\n)\n\nval_cfg = dict(type='ValLoop')\ntest_cfg = dict(type='TestLoop')\n"
  },
  {
    "path": "projects/XDecoder/configs/_base_/xdecoder-tiny_ref-seg.py",
    "content": "_base_ = 'xdecoder-tiny_open-vocab-semseg.py'\n\nmodel = dict(head=dict(task='ref-seg'))\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_caption_coco2014.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_caption.py', 'mmdet::_base_/datasets/coco_caption.py'\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        imdecode_backend='pillow',\n        backend_args=_base_.backend_args),\n    dict(type='ResizeShortestEdge', scale=224, backend='pillow'),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor'))\n]\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-instance_ade20k.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_open-vocab-instance.py',\n    'mmdet::_base_/datasets/ade20k_instance.py'\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='Resize', scale=(2560, 640), keep_ratio=True),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text'))\n]\n\nval_dataloader = dict(\n    dataset=dict(return_classes=True, pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n\ntest_evaluator = dict(metric=['segm'])\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-instance_coco.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_open-vocab-instance.py',\n    'mmdet::_base_/datasets/coco_instance.py'\n]\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        imdecode_backend='pillow',\n        backend_args=_base_.backend_args),\n    dict(\n        type='ResizeShortestEdge', scale=800, max_size=1333, backend='pillow'),\n    dict(type='LoadAnnotations', with_bbox=True, with_mask=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text'))\n]\n\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(metric='segm')\ntest_evaluator = val_evaluator\n\ntrain_dataloader = None\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_ade20k.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_open-vocab-panoptic.py',\n    'mmdet::_base_/datasets/ade20k_panoptic.py'\n]\n\nmodel = dict(test_cfg=dict(mask_thr=0.4))\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='Resize', scale=(2560, 640), keep_ratio=True),\n    dict(type='LoadPanopticAnnotations', backend_args=_base_.backend_args),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'stuff_text'))\n]\n\nx_decoder_ade20k_thing_classes = (\n    'bed', 'window', 'cabinet', 'person', 'door', 'table', 'curtain', 'chair',\n    'car', 'painting', 'sofa', 'shelf', 'mirror', 'armchair', 'seat', 'fence',\n    'desk', 'wardrobe', 'lamp', 'tub', 'rail', 'cushion', 'box', 'column',\n    'signboard', 'chest of drawers', 'counter', 'sink', 'fireplace',\n    'refrigerator', 'stairs', 'case', 'pool table', 'pillow', 'screen door',\n    'bookcase', 'coffee table', 'toilet', 'flower', 'book', 'bench',\n    'countertop', 'stove', 'palm', 'kitchen island', 'computer',\n    'swivel chair', 'boat', 'arcade machine', 'bus', 'towel', 'light', 'truck',\n    'chandelier', 'awning', 'street lamp', 'booth', 'tv', 'airplane',\n    'clothes', 'pole', 'bannister', 'ottoman', 'bottle', 'van', 'ship',\n    'fountain', 'washer', 'plaything', 'stool', 'barrel', 'basket', 'bag',\n    'minibike', 'oven', 'ball', 'food', 'step', 'trade name', 'microwave',\n    'pot', 'animal', 'bicycle', 'dishwasher', 'screen', 'sculpture', 'hood',\n    'sconce', 'vase', 'traffic light', 'tray', 'trash can', 'fan', 'plate',\n    'monitor', 'bulletin board', 'radiator', 'glass', 'clock', 'flag')\n\nx_decoder_ade20k_stuff_classes = (\n    'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'grass',\n    'sidewalk', 'earth', 'mountain', 'plant', 'water', 'house', 'sea', 'rug',\n    'field', 'rock', 'base', 'sand', 'skyscraper', 'grandstand', 'path',\n    'runway', 'stairway', 'river', 'bridge', 'blind', 'hill', 'bar', 'hovel',\n    'tower', 'dirt track', 'land', 'escalator', 'buffet', 'poster', 'stage',\n    'conveyer belt', 'canopy', 'pool', 'falls', 'tent', 'cradle', 'tank',\n    'lake', 'blanket', 'pier', 'crt screen', 'shower')\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(\n            thing_classes=x_decoder_ade20k_thing_classes,\n            stuff_classes=x_decoder_ade20k_stuff_classes),\n        return_classes=True,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_coco.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_open-vocab-panoptic.py',\n    'mmdet::_base_/datasets/coco_panoptic.py'\n]\n\nmodel = dict(test_cfg=dict(mask_thr=0.4))\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        imdecode_backend='pillow',\n        backend_args=_base_.backend_args),\n    dict(\n        type='ResizeShortestEdge', scale=800, max_size=1333, backend='pillow'),\n    dict(type='LoadPanopticAnnotations', backend_args=_base_.backend_args),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text', 'stuff_text'))\n]\n\nval_dataloader = dict(\n    dataset=dict(pipeline=test_pipeline, return_classes=True))\n\ntest_dataloader = val_dataloader\n\ntrain_dataloader = None\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcoco+.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_ref-seg.py', 'mmdet::_base_/datasets/refcoco+.py'\n]\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcoco.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_ref-seg.py', 'mmdet::_base_/datasets/refcoco.py'\n]\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcocog.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_ref-seg.py', 'mmdet::_base_/datasets/refcocog.py'\n]\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-semseg_ade20k.py",
    "content": "_base_ = [\n    '_base_/xdecoder-tiny_open-vocab-semseg.py',\n    'mmdet::_base_/datasets/ade20k_semantic.py'\n]\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', backend_args=_base_.backend_args),\n    dict(type='Resize', scale=(2560, 640), keep_ratio=True),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=False,\n        with_mask=False,\n        with_seg=True,\n        reduce_zero_label=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_path', 'ori_shape', 'img_shape', 'text'))\n]\n\nx_decoder_ade20k_classes = (\n    'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed',\n    'window', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', 'door',\n    'table', 'mountain', 'plant', 'curtain', 'chair', 'car', 'water',\n    'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', 'field',\n    'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', 'lamp', 'tub',\n    'rail', 'cushion', 'base', 'box', 'column', 'signboard',\n    'chest of drawers', 'counter', 'sand', 'sink', 'skyscraper', 'fireplace',\n    'refrigerator', 'grandstand', 'path', 'stairs', 'runway', 'case',\n    'pool table', 'pillow', 'screen door', 'stairway', 'river', 'bridge',\n    'bookcase', 'blind', 'coffee table', 'toilet', 'flower', 'book', 'hill',\n    'bench', 'countertop', 'stove', 'palm', 'kitchen island', 'computer',\n    'swivel chair', 'boat', 'bar', 'arcade machine', 'hovel', 'bus', 'towel',\n    'light', 'truck', 'tower', 'chandelier', 'awning', 'street lamp', 'booth',\n    'tv', 'airplane', 'dirt track', 'clothes', 'pole', 'land', 'bannister',\n    'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van',\n    'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything',\n    'pool', 'stool', 'barrel', 'basket', 'falls', 'tent', 'bag', 'minibike',\n    'cradle', 'oven', 'ball', 'food', 'step', 'tank', 'trade name',\n    'microwave', 'pot', 'animal', 'bicycle', 'lake', 'dishwasher', 'screen',\n    'blanket', 'sculpture', 'hood', 'sconce', 'vase', 'traffic light', 'tray',\n    'trash can', 'fan', 'pier', 'crt screen', 'plate', 'monitor',\n    'bulletin board', 'shower', 'radiator', 'glass', 'clock', 'flag')\n\nval_dataloader = dict(\n    dataset=dict(\n        metainfo=dict(classes=x_decoder_ade20k_classes),\n        return_classes=True,\n        use_label_map=False,\n        pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-semseg_coco.py",
    "content": "_base_ = '_base_/xdecoder-tiny_open-vocab-semseg.py'\n\ndataset_type = 'CocoSegDataset'\ndata_root = 'data/coco/'\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile', imdecode_backend='pillow',\n        backend_args=None),\n    dict(\n        type='ResizeShortestEdge', scale=800, max_size=1333, backend='pillow'),\n    dict(\n        type='LoadAnnotations',\n        with_bbox=False,\n        with_label=False,\n        with_seg=True),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_path', 'ori_shape', 'img_shape', 'scale_factor',\n                   'text'))\n]\n\nx_decoder_coco2017_semseg_classes = (\n    'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',\n    'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',\n    'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',\n    'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag',\n    'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite',\n    'baseball bat', 'baseball glove', 'skateboard', 'surfboard',\n    'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon',\n    'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot',\n    'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant',\n    'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote',\n    'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink',\n    'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',\n    'hair drier', 'toothbrush', 'banner', 'blanket', 'bridge', 'cardboard',\n    'counter', 'curtain', 'door-stuff', 'floor-wood', 'flower', 'fruit',\n    'gravel', 'house', 'light', 'mirror-stuff', 'net', 'pillow', 'platform',\n    'playingfield', 'railroad', 'river', 'road', 'roof', 'sand', 'sea',\n    'shelf', 'snow', 'stairs', 'tent', 'towel', 'wall-brick', 'wall-stone',\n    'wall-tile', 'wall-wood', 'water-other', 'window-blind', 'window-other',\n    'tree-merged', 'fence-merged', 'ceiling-merged', 'sky-other-merged',\n    'cabinet-merged', 'table-merged', 'floor-other-merged', 'pavement-merged',\n    'mountain-merged', 'grass-merged', 'dirt-merged', 'paper-merged',\n    'food-other-merged', 'building-other-merged', 'rock-merged',\n    'wall-other-merged', 'rug-merged')\n\nval_dataloader = dict(\n    batch_size=1,\n    num_workers=2,\n    persistent_workers=True,\n    drop_last=False,\n    sampler=dict(type='DefaultSampler', shuffle=False),\n    dataset=dict(\n        type=dataset_type,\n        data_root=data_root,\n        metainfo=dict(classes=x_decoder_coco2017_semseg_classes),\n        use_label_map=False,\n        data_prefix=dict(\n            img_path='val2017/',\n            seg_map_path='annotations/panoptic_semseg_val2017/'),\n        pipeline=test_pipeline,\n        return_classes=True))\n\ntest_dataloader = val_dataloader\n\nval_evaluator = dict(type='SemSegMetric', iou_metrics=['mIoU'])\ntest_evaluator = val_evaluator\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_ref-caption.py",
    "content": "_base_ = 'xdecoder-tiny_zeroshot_caption_coco2014.py'\n\nmodel = dict(head=dict(task='ref-caption'))\n\ngrounding_scale = 512\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile', imdecode_backend='pillow'),\n    dict(type='ResizeShortestEdge', scale=224, backend='pillow'),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text'))\n]\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "projects/XDecoder/configs/xdecoder-tiny_zeroshot_text-image-retrieval.py",
    "content": "_base_ = 'xdecoder-tiny_zeroshot_caption_coco2014.py'\n\nmodel = dict(head=dict(task='retrieval'))\n\ngrounding_scale = 512\n\ntest_pipeline = [\n    dict(\n        type='LoadImageFromFile',\n        imdecode_backend='pillow',\n        backend_args=_base_.backend_args),\n    dict(\n        type='ResizeShortestEdge',\n        scale=224,\n        backend='pillow',\n        interpolation='bicubic'),\n    dict(\n        type='PackDetInputs',\n        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape',\n                   'scale_factor', 'text'))\n]\n\nval_dataloader = dict(dataset=dict(pipeline=test_pipeline))\ntest_dataloader = val_dataloader\n"
  },
  {
    "path": "projects/XDecoder/demo.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom argparse import ArgumentParser\n\nfrom mmengine.config import Config\nfrom mmengine.logging import print_log\n\nfrom mmdet.apis import DetInferencer\nfrom projects.XDecoder.xdecoder.inference import (\n    ImageCaptionInferencer, RefImageCaptionInferencer,\n    TextToImageRegionRetrievalInferencer)\n\nTASKINFOS = {\n    'semseg': DetInferencer,\n    'ref-seg': DetInferencer,\n    'instance': DetInferencer,\n    'panoptic': DetInferencer,\n    'caption': ImageCaptionInferencer,\n    'ref-caption': RefImageCaptionInferencer,\n    'retrieval': TextToImageRegionRetrievalInferencer,\n}\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument(\n        'inputs', type=str, help='Input image file or folder path.')\n    parser.add_argument('model', type=str, help='Config file name')\n    parser.add_argument('--weights', help='Checkpoint file')\n    parser.add_argument('--texts', help='text prompt')\n    parser.add_argument(\n        '--out-dir',\n        type=str,\n        default='outputs',\n        help='Output directory of images or prediction results.')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--show',\n        action='store_true',\n        help='Display the image in a popup window.')\n    parser.add_argument(\n        '--no-save-vis',\n        action='store_true',\n        help='Do not save detection vis results')\n    parser.add_argument(\n        '--palette',\n        default='none',\n        choices=['ade20k', 'coco', 'voc', 'citys', 'random', 'none'],\n        help='Color palette used for visualization')\n\n    # only for instance segmentation\n    parser.add_argument(\n        '--pred-score-thr',\n        type=float,\n        default=0.5,\n        help='bbox score threshold')\n    # only for panoptic segmentation\n    parser.add_argument(\n        '--stuff-texts',\n        help='text prompt for stuff name in panoptic segmentation')\n\n    call_args = vars(parser.parse_args())\n    if call_args['no_save_vis']:\n        call_args['out_dir'] = ''\n\n    init_kws = ['model', 'weights', 'device', 'palette']\n    init_args = {}\n    for init_kw in init_kws:\n        init_args[init_kw] = call_args.pop(init_kw)\n\n    return init_args, call_args\n\n\ndef main():\n    init_args, call_args = parse_args()\n\n    cfg = Config.fromfile(init_args['model'])\n    task = cfg.model.head.task\n    assert task in TASKINFOS\n\n    inferencer = TASKINFOS[task](**init_args)\n\n    if task != 'caption':\n        assert call_args[\n            'texts'] is not None, f'text prompts is required for {task}'\n        if task != 'panoptic':\n            call_args.pop('stuff_texts')\n    else:\n        call_args.pop('texts')\n        call_args.pop('stuff_texts')\n\n    inferencer(**call_args)\n\n    if call_args['out_dir'] != '' and not call_args['no_save_vis']:\n        print_log(f'results have been saved at {call_args[\"out_dir\"]}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/__init__.py",
    "content": "from .focalnet import FocalNet\nfrom .pixel_decoder import XTransformerEncoderPixelDecoder\nfrom .transformer_decoder import XDecoderTransformerDecoder\nfrom .unified_head import XDecoderUnifiedhead\nfrom .xdecoder import XDecoder\n\n__all__ = [\n    'XDecoder', 'FocalNet', 'XDecoderUnifiedhead',\n    'XTransformerEncoderPixelDecoder', 'XDecoderTransformerDecoder'\n]\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/focalnet.py",
    "content": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.checkpoint as checkpoint\nfrom mmcv.cnn.bricks import DropPath\n\nfrom mmdet.registry import MODELS\n\n# modified from https://github.com/microsoft/X-Decoder/blob/main/xdecoder/backbone/focal_dw.py # noqa\n\n\n@MODELS.register_module()\nclass FocalNet(nn.Module):\n\n    def __init__(\n        self,\n        patch_size=4,\n        in_chans=3,\n        embed_dim=96,\n        depths=[2, 2, 6, 2],\n        mlp_ratio=4.,\n        drop_rate=0.,\n        drop_path_rate=0.3,\n        norm_layer=nn.LayerNorm,\n        patch_norm=True,\n        out_indices=[0, 1, 2, 3],\n        frozen_stages=-1,\n        focal_levels=[3, 3, 3, 3],\n        focal_windows=[3, 3, 3, 3],\n        use_pre_norms=[False, False, False, False],\n        use_conv_embed=True,\n        use_postln=True,\n        use_postln_in_modulation=False,\n        scaling_modulator=True,\n        use_layerscale=True,\n        use_checkpoint=False,\n    ):\n        super().__init__()\n\n        self.num_layers = len(depths)\n        self.embed_dim = embed_dim\n        self.patch_norm = patch_norm\n        self.out_indices = out_indices\n        self.frozen_stages = frozen_stages\n\n        # split image into non-overlapping patches\n        self.patch_embed = PatchEmbed(\n            patch_size=patch_size,\n            in_chans=in_chans,\n            embed_dim=embed_dim,\n            norm_layer=norm_layer if self.patch_norm else None,\n            use_conv_embed=use_conv_embed,\n            is_stem=True,\n            use_pre_norm=False)\n\n        self.pos_drop = nn.Dropout(p=drop_rate)\n\n        dpr = [\n            x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))\n        ]\n\n        self.layers = nn.ModuleList()\n        for i_layer in range(self.num_layers):\n            layer = BasicLayer(\n                dim=int(embed_dim * 2**i_layer),\n                depth=depths[i_layer],\n                mlp_ratio=mlp_ratio,\n                drop=drop_rate,\n                drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])],\n                norm_layer=norm_layer,\n                downsample=PatchEmbed if\n                (i_layer < self.num_layers - 1) else None,\n                focal_window=focal_windows[i_layer],\n                focal_level=focal_levels[i_layer],\n                use_pre_norm=use_pre_norms[i_layer],\n                use_conv_embed=use_conv_embed,\n                use_postln=use_postln,\n                use_postln_in_modulation=use_postln_in_modulation,\n                scaling_modulator=scaling_modulator,\n                use_layerscale=use_layerscale,\n                use_checkpoint=use_checkpoint)\n            self.layers.append(layer)\n\n        num_features = [int(embed_dim * 2**i) for i in range(self.num_layers)]\n        self.num_features = num_features\n\n        # add a norm layer for each output\n        for i_layer in self.out_indices:\n            layer = norm_layer(num_features[i_layer])\n            layer_name = f'norm{i_layer}'\n            self.add_module(layer_name, layer)\n\n    def forward(self, x):\n        x = self.patch_embed(x)\n        Wh, Ww = x.size(2), x.size(3)\n\n        x = x.flatten(2).transpose(1, 2)\n        x = self.pos_drop(x)\n\n        outs = {}\n        for i in range(self.num_layers):\n            layer = self.layers[i]\n            x_out, H, W, x, Wh, Ww = layer(x, Wh, Ww)\n            if i in self.out_indices:\n                norm_layer = getattr(self, f'norm{i}')\n                x_out = norm_layer(x_out)\n\n                out = x_out.view(-1, H, W,\n                                 self.num_features[i]).permute(0, 3, 1,\n                                                               2).contiguous()\n                outs['res{}'.format(i + 2)] = out\n        return outs\n\n\nclass Mlp(nn.Module):\n    \"\"\"Multilayer perceptron.\"\"\"\n\n    def __init__(self,\n                 in_features,\n                 hidden_features=None,\n                 out_features=None,\n                 act_layer=nn.GELU,\n                 drop=0.):\n        super().__init__()\n        out_features = out_features or in_features\n        hidden_features = hidden_features or in_features\n        self.fc1 = nn.Linear(in_features, hidden_features)\n        self.act = act_layer()\n        self.fc2 = nn.Linear(hidden_features, out_features)\n        self.drop = nn.Dropout(drop)\n\n    def forward(self, x):\n        x = self.fc1(x)\n        x = self.act(x)\n        x = self.drop(x)\n        x = self.fc2(x)\n        x = self.drop(x)\n        return x\n\n\nclass FocalModulation(nn.Module):\n    \"\"\"Focal Modulation.\n\n    Args:\n        dim (int): Number of input channels.\n        proj_drop (float, optional): Dropout ratio of output. Default: 0.0\n        focal_level (int): Number of focal levels\n        focal_window (int): Focal window size at focal level 1\n        focal_factor (int, default=2): Step to increase the focal window\n    \"\"\"\n\n    def __init__(self,\n                 dim,\n                 proj_drop=0.,\n                 focal_level=2,\n                 focal_window=7,\n                 focal_factor=2,\n                 use_postln_in_modulation=False,\n                 scaling_modulator=False):\n\n        super().__init__()\n        self.dim = dim\n\n        self.focal_level = focal_level\n        self.focal_window = focal_window\n        self.focal_factor = focal_factor\n        self.use_postln_in_modulation = use_postln_in_modulation\n        self.scaling_modulator = scaling_modulator\n\n        self.f = nn.Linear(dim, 2 * dim + (self.focal_level + 1), bias=True)\n        self.h = nn.Conv2d(\n            dim, dim, kernel_size=1, stride=1, padding=0, groups=1, bias=True)\n\n        self.act = nn.GELU()\n        self.proj = nn.Linear(dim, dim)\n        self.proj_drop = nn.Dropout(proj_drop)\n        self.focal_layers = nn.ModuleList()\n\n        if self.use_postln_in_modulation:\n            self.ln = nn.LayerNorm(dim)\n\n        for k in range(self.focal_level):\n            kernel_size = self.focal_factor * k + self.focal_window\n            self.focal_layers.append(\n                nn.Sequential(\n                    nn.Conv2d(\n                        dim,\n                        dim,\n                        kernel_size=kernel_size,\n                        stride=1,\n                        groups=dim,\n                        padding=kernel_size // 2,\n                        bias=False),\n                    nn.GELU(),\n                ))\n\n    def forward(self, x):\n        \"\"\"Forward function.\n\n        Args:\n            x: input features with shape of (B, H, W, C)\n        \"\"\"\n        B, nH, nW, C = x.shape\n        x = self.f(x)\n        x = x.permute(0, 3, 1, 2).contiguous()\n        q, ctx, gates = torch.split(x, (C, C, self.focal_level + 1), 1)\n\n        ctx_all = 0\n        for level in range(self.focal_level):\n            ctx = self.focal_layers[level](ctx)\n            ctx_all = ctx_all + ctx * gates[:, level:level + 1]\n        ctx_global = self.act(ctx.mean(2, keepdim=True).mean(3, keepdim=True))\n        ctx_all = ctx_all + ctx_global * gates[:, self.focal_level:]\n\n        if self.scaling_modulator:\n            ctx_all = ctx_all / (self.focal_level + 1)\n\n        x_out = q * self.h(ctx_all)\n        x_out = x_out.permute(0, 2, 3, 1).contiguous()\n        if self.use_postln_in_modulation:\n            x_out = self.ln(x_out)\n        x_out = self.proj(x_out)\n        x_out = self.proj_drop(x_out)\n        return x_out\n\n\nclass FocalModulationBlock(nn.Module):\n    \"\"\"Focal Modulation Block.\n\n    Args:\n        dim (int): Number of input channels.\n        mlp_ratio (float): Ratio of mlp hidden dim to embedding dim.\n        drop (float, optional): Dropout rate. Default: 0.0\n        drop_path (float, optional): Stochastic depth rate. Default: 0.0\n        act_layer (nn.Module, optional): Activation layer. Default: nn.GELU\n        norm_layer (nn.Module, optional): Normalization layer.\n            Default: nn.LayerNorm\n        focal_level (int): number of focal levels\n        focal_window (int): focal kernel size at level 1\n    \"\"\"\n\n    def __init__(self,\n                 dim,\n                 mlp_ratio=4.,\n                 drop=0.,\n                 drop_path=0.,\n                 act_layer=nn.GELU,\n                 norm_layer=nn.LayerNorm,\n                 focal_level=2,\n                 focal_window=9,\n                 use_postln=False,\n                 use_postln_in_modulation=False,\n                 scaling_modulator=False,\n                 use_layerscale=False,\n                 layerscale_value=1e-4):\n        super().__init__()\n        self.dim = dim\n        self.mlp_ratio = mlp_ratio\n        self.focal_window = focal_window\n        self.focal_level = focal_level\n        self.use_postln = use_postln\n        self.use_layerscale = use_layerscale\n\n        self.dw1 = nn.Conv2d(\n            dim, dim, kernel_size=3, stride=1, padding=1, groups=dim)\n        self.norm1 = norm_layer(dim)\n        self.modulation = FocalModulation(\n            dim,\n            focal_window=self.focal_window,\n            focal_level=self.focal_level,\n            proj_drop=drop,\n            use_postln_in_modulation=use_postln_in_modulation,\n            scaling_modulator=scaling_modulator)\n\n        self.dw2 = nn.Conv2d(\n            dim, dim, kernel_size=3, stride=1, padding=1, groups=dim)\n        self.drop_path = DropPath(\n            drop_path) if drop_path > 0. else nn.Identity()\n        self.norm2 = norm_layer(dim)\n        mlp_hidden_dim = int(dim * mlp_ratio)\n        self.mlp = Mlp(\n            in_features=dim,\n            hidden_features=mlp_hidden_dim,\n            act_layer=act_layer,\n            drop=drop)\n\n        self.H = None\n        self.W = None\n\n        self.gamma_1 = 1.0\n        self.gamma_2 = 1.0\n        if self.use_layerscale:\n            self.gamma_1 = nn.Parameter(\n                layerscale_value * torch.ones(dim), requires_grad=True)\n            self.gamma_2 = nn.Parameter(\n                layerscale_value * torch.ones(dim), requires_grad=True)\n\n    def forward(self, x):\n        \"\"\"Forward function.\n\n        Args:\n            x: Input feature, tensor size (B, H*W, C).\n            H, W: Spatial resolution of the input feature.\n        \"\"\"\n        B, L, C = x.shape\n        H, W = self.H, self.W\n        assert L == H * W, 'input feature has wrong size'\n\n        x = x.view(B, H, W, C).permute(0, 3, 1, 2).contiguous()\n        x = x + self.dw1(x)\n        x = x.permute(0, 2, 3, 1).contiguous().view(B, L, C)\n\n        shortcut = x\n        if not self.use_postln:\n            x = self.norm1(x)\n        x = x.view(B, H, W, C)\n\n        # FM\n        x = self.modulation(x).view(B, H * W, C)\n        x = shortcut + self.drop_path(self.gamma_1 * x)\n        if self.use_postln:\n            x = self.norm1(x)\n\n        x = x.view(B, H, W, C).permute(0, 3, 1, 2).contiguous()\n        x = x + self.dw2(x)\n        x = x.permute(0, 2, 3, 1).contiguous().view(B, L, C)\n\n        if not self.use_postln:\n            x = x + self.drop_path(self.gamma_2 * self.mlp(self.norm2(x)))\n        else:\n            x = x + self.drop_path(self.gamma_2 * self.mlp(x))\n            x = self.norm2(x)\n\n        return x\n\n\nclass BasicLayer(nn.Module):\n    \"\"\"A basic focal modulation layer for one stage.\n\n    Args:\n        dim (int): Number of feature channels\n        depth (int): Depths of this stage.\n        mlp_ratio (float): Ratio of mlp hidden dim to embedding dim.\n            Default: 4.\n        drop (float, optional): Dropout rate. Default: 0.0\n        drop_path (float | tuple[float], optional): Stochastic depth rate.\n            Default: 0.0\n        norm_layer (nn.Module, optional): Normalization layer.\n            Default: nn.LayerNorm\n        downsample (nn.Module | None, optional): Downsample layer at the\n            end of the layer. Default: None\n        focal_level (int): Number of focal levels\n        focal_window (int): Focal window size at focal level 1\n        use_conv_embed (bool): Use overlapped convolution for patch\n            embedding or now. Default: False\n        use_checkpoint (bool): Whether to use checkpointing to save memory.\n            Default: False\n    \"\"\"\n\n    def __init__(\n        self,\n        dim,\n        depth,\n        mlp_ratio=4.,\n        drop=0.,\n        drop_path=0.,\n        norm_layer=nn.LayerNorm,\n        downsample=None,\n        focal_window=9,\n        focal_level=2,\n        use_conv_embed=False,\n        use_postln=False,\n        use_postln_in_modulation=False,\n        scaling_modulator=False,\n        use_layerscale=False,\n        use_checkpoint=False,\n        use_pre_norm=False,\n    ):\n        super().__init__()\n        self.depth = depth\n        self.use_checkpoint = use_checkpoint\n\n        # build blocks\n        self.blocks = nn.ModuleList([\n            FocalModulationBlock(\n                dim=dim,\n                mlp_ratio=mlp_ratio,\n                drop=drop,\n                drop_path=drop_path[i]\n                if isinstance(drop_path, list) else drop_path,\n                focal_window=focal_window,\n                focal_level=focal_level,\n                use_postln=use_postln,\n                use_postln_in_modulation=use_postln_in_modulation,\n                scaling_modulator=scaling_modulator,\n                use_layerscale=use_layerscale,\n                norm_layer=norm_layer) for i in range(depth)\n        ])\n\n        # patch merging layer\n        if downsample is not None:\n            self.downsample = downsample(\n                patch_size=2,\n                in_chans=dim,\n                embed_dim=2 * dim,\n                use_conv_embed=use_conv_embed,\n                norm_layer=norm_layer,\n                is_stem=False,\n                use_pre_norm=use_pre_norm)\n\n        else:\n            self.downsample = None\n\n    def forward(self, x, H, W):\n        \"\"\"Forward function.\n\n        Args:\n            x: Input feature, tensor size (B, H*W, C).\n            H, W: Spatial resolution of the input feature.\n        \"\"\"\n        for blk in self.blocks:\n            blk.H, blk.W = H, W\n            if self.use_checkpoint:\n                x = checkpoint.checkpoint(blk, x)\n            else:\n                x = blk(x)\n        if self.downsample is not None:\n            x_reshaped = x.transpose(1, 2).view(x.shape[0], x.shape[-1], H, W)\n            x_down = self.downsample(x_reshaped)\n            x_down = x_down.flatten(2).transpose(1, 2)\n            Wh, Ww = (H + 1) // 2, (W + 1) // 2\n            return x, H, W, x_down, Wh, Ww\n        else:\n            return x, H, W, x, H, W\n\n\nclass PatchEmbed(nn.Module):\n    \"\"\"Image to Patch Embedding.\n\n    Args:\n        patch_size (int): Patch token size. Default: 4.\n        in_chans (int): Number of input image channels. Default: 3.\n        embed_dim (int): Number of linear projection output channels.\n            Default: 96.\n        norm_layer (nn.Module, optional): Normalization layer.\n            Default: None\n        use_conv_embed (bool): Whether use overlapped convolution for\n            patch embedding. Default: False\n        is_stem (bool): Is the stem block or not.\n    \"\"\"\n\n    def __init__(self,\n                 patch_size=4,\n                 in_chans=3,\n                 embed_dim=96,\n                 norm_layer=None,\n                 use_conv_embed=False,\n                 is_stem=False,\n                 use_pre_norm=False):\n        super().__init__()\n        patch_size = (patch_size, patch_size)\n        self.patch_size = patch_size\n\n        self.in_chans = in_chans\n        self.embed_dim = embed_dim\n        self.use_pre_norm = use_pre_norm\n\n        if use_conv_embed:\n            # if we choose to use conv embedding,\n            # then we treat the stem and non-stem differently\n            if is_stem:\n                kernel_size = 7\n                padding = 3\n                stride = 4\n            else:\n                kernel_size = 3\n                padding = 1\n                stride = 2\n            self.proj = nn.Conv2d(\n                in_chans,\n                embed_dim,\n                kernel_size=kernel_size,\n                stride=stride,\n                padding=padding)\n        else:\n            self.proj = nn.Conv2d(\n                in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)\n\n        if self.use_pre_norm:\n            if norm_layer is not None:\n                self.norm = norm_layer(in_chans)\n            else:\n                self.norm = None\n        else:\n            if norm_layer is not None:\n                self.norm = norm_layer(embed_dim)\n            else:\n                self.norm = None\n\n    def forward(self, x):\n        \"\"\"Forward function.\"\"\"\n        B, C, H, W = x.size()\n        if W % self.patch_size[1] != 0:\n            x = F.pad(x, (0, self.patch_size[1] - W % self.patch_size[1]))\n        if H % self.patch_size[0] != 0:\n            x = F.pad(x,\n                      (0, 0, 0, self.patch_size[0] - H % self.patch_size[0]))\n\n        if self.use_pre_norm:\n            if self.norm is not None:\n                x = x.flatten(2).transpose(1, 2)  # B Ph*Pw C\n                x = self.norm(x).transpose(1, 2).view(B, C, H, W)\n            x = self.proj(x)\n        else:\n            x = self.proj(x)  # B C Wh Ww\n            if self.norm is not None:\n                Wh, Ww = x.size(2), x.size(3)\n                x = x.flatten(2).transpose(1, 2)\n                x = self.norm(x)\n                x = x.transpose(1, 2).view(-1, self.embed_dim, Wh, Ww)\n\n        return x\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/inference/__init__.py",
    "content": "from .image_caption import ImageCaptionInferencer, RefImageCaptionInferencer\nfrom .texttoimage_regionretrieval_inferencer import \\\n    TextToImageRegionRetrievalInferencer\n\n__all__ = [\n    'ImageCaptionInferencer', 'RefImageCaptionInferencer',\n    'TextToImageRegionRetrievalInferencer'\n]\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/inference/image_caption.py",
    "content": "import copy\nimport os.path as osp\nfrom typing import Iterable, List, Optional, Tuple, Union\n\nimport mmcv\nimport mmengine\nimport numpy as np\nimport torch\nfrom mmengine.dataset import Compose\nfrom rich.progress import track\n\nfrom mmdet.apis.det_inferencer import DetInferencer, InputsType, PredType\nfrom mmdet.utils import ConfigType\n\n\ndef get_adaptive_scale(img_shape: Tuple[int, int],\n                       min_scale: float = 0.3,\n                       max_scale: float = 3.0) -> float:\n    \"\"\"Get adaptive scale according to image shape.\n\n    The target scale depends on the the short edge length of the image. If the\n    short edge length equals 224, the output is 1.0. And output linear scales\n    according the short edge length.\n\n    You can also specify the minimum scale and the maximum scale to limit the\n    linear scale.\n\n    Args:\n        img_shape (Tuple[int, int]): The shape of the canvas image.\n        min_scale (float): The minimum scale. Defaults to 0.3.\n        max_scale (float): The maximum scale. Defaults to 3.0.\n\n    Returns:\n        int: The adaptive scale.\n    \"\"\"\n    short_edge_length = min(img_shape)\n    scale = short_edge_length / 224.\n    return min(max(scale, min_scale), max_scale)\n\n\nclass ImageCaptionInferencer(DetInferencer):\n    DEFAULT_TEXT_CFG = {\n        'font_families': 'monospace',\n        'colors': 'white',\n        'bboxes': dict(facecolor='black', alpha=0.5, boxstyle='Round'),\n        'vertical_alignments': 'top',\n        'horizontal_alignments': 'left',\n    }\n\n    def visualize(self,\n                  inputs: InputsType,\n                  preds: PredType,\n                  return_vis: bool = False,\n                  show: bool = False,\n                  wait_time: int = 0,\n                  draw_pred: bool = True,\n                  pred_score_thr: float = 0.3,\n                  no_save_vis: bool = False,\n                  img_out_dir: str = '',\n                  **kwargs) -> Union[List[np.ndarray], None]:\n\n        if no_save_vis is True:\n            img_out_dir = ''\n\n        if not show and img_out_dir == '' and not return_vis:\n            return None\n\n        if self.visualizer is None:\n            raise ValueError('Visualization needs the \"visualizer\" term'\n                             'defined in the config, but got None.')\n\n        results = []\n\n        text_cfg = self.DEFAULT_TEXT_CFG\n\n        for single_input, pred in zip(inputs, preds):\n            if isinstance(single_input, str):\n                img_bytes = mmengine.fileio.get(single_input)\n                img = mmcv.imfrombytes(img_bytes)\n                img = img[:, :, ::-1]\n                img_name = osp.basename(single_input)\n            elif isinstance(single_input, np.ndarray):\n                img = single_input.copy()\n                img_num = str(self.num_visualized_imgs).zfill(8)\n                img_name = f'{img_num}.jpg'\n            else:\n                raise ValueError('Unsupported input type: '\n                                 f'{type(single_input)}')\n\n            out_file = osp.join(img_out_dir, 'vis',\n                                img_name) if img_out_dir != '' else None\n\n            self.visualizer.set_image(img)\n\n            img_scale = get_adaptive_scale(img.shape[:2])\n            text_cfg['font_sizes'] = int(img_scale * 7)\n\n            self.visualizer.draw_texts(\n                pred.pred_caption, torch.tensor([img_scale * 5,\n                                                 img_scale * 5]), **text_cfg)\n            drawn_img = self.visualizer.get_image()\n\n            self.visualizer.add_datasample(\n                img_name,\n                drawn_img,\n                pred,\n                show=show,\n                wait_time=wait_time,\n                draw_gt=False,\n                draw_pred=draw_pred,\n                pred_score_thr=pred_score_thr,\n                out_file=out_file,\n            )\n            results.append(self.visualizer.get_image())\n            self.num_visualized_imgs += 1\n\n        return results\n\n\nclass RefImageCaptionInferencer(ImageCaptionInferencer):\n\n    def _init_pipeline(self, cfg: ConfigType) -> Compose:\n        \"\"\"Initialize the test pipeline.\"\"\"\n        pipeline_cfg = cfg.test_dataloader.dataset.pipeline\n\n        # For inference, the key of ``img_id`` is not used.\n        if 'meta_keys' in pipeline_cfg[-1]:\n            pipeline_cfg[-1]['meta_keys'] = tuple(\n                meta_key for meta_key in pipeline_cfg[-1]['meta_keys']\n                if meta_key != 'img_id')\n\n        load_img_idx = self._get_transform_idx(pipeline_cfg,\n                                               'LoadImageFromFile')\n        if load_img_idx == -1:\n            raise ValueError(\n                'LoadImageFromFile is not found in the test pipeline')\n        pipeline_cfg[load_img_idx]['type'] = 'mmdet.InferencerLoader'\n\n        caption_pipeline = Compose(pipeline_cfg)\n\n        grounding_pipeline_cp = copy.deepcopy(pipeline_cfg)\n        grounding_pipeline_cp[1].scale = cfg.grounding_scale\n        grounding_pipeline = Compose(grounding_pipeline_cp)\n\n        return {\n            'grounding_pipeline': grounding_pipeline,\n            'caption_pipeline': caption_pipeline\n        }\n\n    def _get_chunk_data(self, inputs: Iterable, chunk_size: int):\n        \"\"\"Get batch data from inputs.\n\n        Args:\n            inputs (Iterable): An iterable dataset.\n            chunk_size (int): Equivalent to batch size.\n\n        Yields:\n            list: batch data.\n        \"\"\"\n        inputs_iter = iter(inputs)\n        while True:\n            try:\n                chunk_data = []\n                for _ in range(chunk_size):\n                    inputs_ = next(inputs_iter)\n                    if 'img' in inputs_:\n                        ori_inputs_ = inputs_['img']\n                    else:\n                        ori_inputs_ = inputs_['img_path']\n                    chunk_data.append(\n                        (ori_inputs_, self.pipeline['grounding_pipeline'](\n                            copy.deepcopy(inputs_)),\n                         self.pipeline['caption_pipeline'](\n                             copy.deepcopy(inputs_))))\n                yield chunk_data\n            except StopIteration:\n                if chunk_data:\n                    yield chunk_data\n                break\n\n    def __call__(\n            self,\n            inputs: InputsType,\n            batch_size: int = 1,\n            return_vis: bool = False,\n            show: bool = False,\n            wait_time: int = 0,\n            no_save_vis: bool = False,\n            draw_pred: bool = True,\n            pred_score_thr: float = 0.3,\n            return_datasamples: bool = False,\n            print_result: bool = False,\n            no_save_pred: bool = True,\n            out_dir: str = '',\n            texts: Optional[Union[str, list]] = None,\n            # by open panoptic task\n            stuff_texts: Optional[Union[str, list]] = None,\n            custom_entities: bool = False,  # by GLIP\n            **kwargs) -> dict:\n        \"\"\"Call the inferencer.\n\n        Args:\n            inputs (InputsType): Inputs for the inferencer.\n            batch_size (int): Inference batch size. Defaults to 1.\n            show (bool): Whether to display the visualization results in a\n                popup window. Defaults to False.\n            wait_time (float): The interval of show (s). Defaults to 0.\n            no_save_vis (bool): Whether to force not to save prediction\n                vis results. Defaults to False.\n            draw_pred (bool): Whether to draw predicted bounding boxes.\n                Defaults to True.\n            pred_score_thr (float): Minimum score of bboxes to draw.\n                Defaults to 0.3.\n            return_datasamples (bool): Whether to return results as\n                :obj:`DetDataSample`. Defaults to False.\n            print_result (bool): Whether to print the inference result w/o\n                visualization to the console. Defaults to False.\n            no_save_pred (bool): Whether to force not to save prediction\n                results. Defaults to True.\n            out_file: Dir to save the inference results or\n                visualization. If left as empty, no file will be saved.\n                Defaults to ''.\n\n            **kwargs: Other keyword arguments passed to :meth:`preprocess`,\n                :meth:`forward`, :meth:`visualize` and :meth:`postprocess`.\n                Each key in kwargs should be in the corresponding set of\n                ``preprocess_kwargs``, ``forward_kwargs``, ``visualize_kwargs``\n                and ``postprocess_kwargs``.\n\n        Returns:\n            dict: Inference and visualization results.\n        \"\"\"\n        assert batch_size == 1\n        (\n            preprocess_kwargs,\n            forward_kwargs,\n            visualize_kwargs,\n            postprocess_kwargs,\n        ) = self._dispatch_kwargs(**kwargs)\n\n        ori_inputs = self._inputs_to_list(inputs)\n\n        if isinstance(texts, str):\n            texts = [texts] * len(ori_inputs)\n\n        for i in range(len(texts)):\n            if isinstance(ori_inputs[i], str):\n                ori_inputs[i] = {\n                    'text': texts[i],\n                    'img_path': ori_inputs[i],\n                    'custom_entities': custom_entities\n                }\n            else:\n                ori_inputs[i] = {\n                    'text': texts[i],\n                    'img': ori_inputs[i],\n                    'custom_entities': custom_entities\n                }\n        inputs = self.preprocess(\n            ori_inputs, batch_size=batch_size, **preprocess_kwargs)\n\n        results_dict = {'predictions': [], 'visualization': []}\n        for ori_inputs, grounding_data, caption_data in track(\n                inputs, description='Inference'):\n\n            self.model.sem_seg_head.task = 'ref-seg'\n            self.model.sem_seg_head.predictor.task = 'ref-seg'\n            preds = self.forward(grounding_data, **forward_kwargs)\n\n            for data_sample, pred_datasmaple in zip(\n                    caption_data['data_samples'], preds):\n                data_sample.pred_instances = pred_datasmaple.pred_instances\n                data_sample.set_metainfo({\n                    'grounding_img_shape':\n                    pred_datasmaple.metainfo['img_shape']\n                })\n\n            self.model.sem_seg_head.task = 'caption'\n            self.model.sem_seg_head.predictor.task = 'caption'\n\n            preds = self.forward(caption_data, **forward_kwargs)\n\n            if isinstance(ori_inputs, dict):\n                ori_inputs = ori_inputs['img_path']\n\n            visualization = self.visualize(\n                ori_inputs,\n                preds,\n                return_vis=return_vis,\n                show=show,\n                wait_time=wait_time,\n                draw_pred=draw_pred,\n                pred_score_thr=pred_score_thr,\n                no_save_vis=no_save_vis,\n                img_out_dir=out_dir,\n                **visualize_kwargs)\n            results = self.postprocess(\n                preds,\n                visualization,\n                return_datasamples=return_datasamples,\n                print_result=print_result,\n                no_save_pred=no_save_pred,\n                pred_out_dir=out_dir,\n                **postprocess_kwargs)\n            results_dict['predictions'].extend(results['predictions'])\n            if results['visualization'] is not None:\n                results_dict['visualization'].extend(results['visualization'])\n        return results_dict\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/inference/texttoimage_regionretrieval_inferencer.py",
    "content": "import copy\nfrom typing import Iterable, Optional, Union\n\nimport torch\nfrom mmengine.dataset import Compose\nfrom rich.progress import track\n\nfrom mmdet.apis.det_inferencer import DetInferencer, InputsType\nfrom mmdet.utils import ConfigType\n\n\nclass TextToImageRegionRetrievalInferencer(DetInferencer):\n\n    def _init_pipeline(self, cfg: ConfigType) -> Compose:\n        \"\"\"Initialize the test pipeline.\"\"\"\n        pipeline_cfg = cfg.test_dataloader.dataset.pipeline\n\n        # For inference, the key of ``img_id`` is not used.\n        if 'meta_keys' in pipeline_cfg[-1]:\n            pipeline_cfg[-1]['meta_keys'] = tuple(\n                meta_key for meta_key in pipeline_cfg[-1]['meta_keys']\n                if meta_key != 'img_id')\n\n        load_img_idx = self._get_transform_idx(pipeline_cfg,\n                                               'LoadImageFromFile')\n        if load_img_idx == -1:\n            raise ValueError(\n                'LoadImageFromFile is not found in the test pipeline')\n        pipeline_cfg[load_img_idx]['type'] = 'mmdet.InferencerLoader'\n\n        retrieval_pipeline = Compose(pipeline_cfg)\n\n        grounding_pipeline_cp = copy.deepcopy(pipeline_cfg)\n        grounding_pipeline_cp[1].scale = cfg.grounding_scale\n        grounding_pipeline = Compose(grounding_pipeline_cp)\n\n        return {\n            'grounding_pipeline': grounding_pipeline,\n            'retrieval_pipeline': retrieval_pipeline\n        }\n\n    def _get_chunk_data(self, inputs: Iterable, pipeline, chunk_size: int):\n        \"\"\"Get batch data from inputs.\n\n        Args:\n            inputs (Iterable): An iterable dataset.\n            chunk_size (int): Equivalent to batch size.\n\n        Yields:\n            list: batch data.\n        \"\"\"\n        inputs_iter = iter(inputs)\n        while True:\n            try:\n                chunk_data = []\n                for _ in range(chunk_size):\n                    inputs_ = next(inputs_iter)\n                    chunk_data.append(\n                        (inputs_, pipeline(copy.deepcopy(inputs_))))\n                yield chunk_data\n            except StopIteration:\n                if chunk_data:\n                    yield chunk_data\n                break\n\n    def preprocess(self,\n                   inputs: InputsType,\n                   pipeline,\n                   batch_size: int = 1,\n                   **kwargs):\n        \"\"\"Process the inputs into a model-feedable format.\n\n        Customize your preprocess by overriding this method. Preprocess should\n        return an iterable object, of which each item will be used as the\n        input of ``model.test_step``.\n\n        ``BaseInferencer.preprocess`` will return an iterable chunked data,\n        which will be used in __call__ like this:\n\n        .. code-block:: python\n\n            def __call__(self, inputs, batch_size=1, **kwargs):\n                chunked_data = self.preprocess(inputs, batch_size, **kwargs)\n                for batch in chunked_data:\n                    preds = self.forward(batch, **kwargs)\n\n        Args:\n            inputs (InputsType): Inputs given by user.\n            batch_size (int): batch size. Defaults to 1.\n\n        Yields:\n            Any: Data processed by the ``pipeline`` and ``collate_fn``.\n        \"\"\"\n        chunked_data = self._get_chunk_data(inputs, pipeline, batch_size)\n        yield from map(self.collate_fn, chunked_data)\n\n    def __call__(\n            self,\n            inputs: InputsType,\n            batch_size: int = 1,\n            return_vis: bool = False,\n            show: bool = False,\n            wait_time: int = 0,\n            no_save_vis: bool = False,\n            draw_pred: bool = True,\n            pred_score_thr: float = 0.3,\n            return_datasamples: bool = False,\n            print_result: bool = False,\n            no_save_pred: bool = True,\n            out_dir: str = '',\n            texts: Optional[Union[str, list]] = None,\n            # by open panoptic task\n            stuff_texts: Optional[Union[str, list]] = None,\n            custom_entities: bool = False,  # by GLIP\n            **kwargs) -> dict:\n        \"\"\"Call the inferencer.\n\n        Args:\n            inputs (InputsType): Inputs for the inferencer.\n            batch_size (int): Inference batch size. Defaults to 1.\n            show (bool): Whether to display the visualization results in a\n                popup window. Defaults to False.\n            wait_time (float): The interval of show (s). Defaults to 0.\n            no_save_vis (bool): Whether to force not to save prediction\n                vis results. Defaults to False.\n            draw_pred (bool): Whether to draw predicted bounding boxes.\n                Defaults to True.\n            pred_score_thr (float): Minimum score of bboxes to draw.\n                Defaults to 0.3.\n            return_datasamples (bool): Whether to return results as\n                :obj:`DetDataSample`. Defaults to False.\n            print_result (bool): Whether to print the inference result w/o\n                visualization to the console. Defaults to False.\n            no_save_pred (bool): Whether to force not to save prediction\n                results. Defaults to True.\n            out_file: Dir to save the inference results or\n                visualization. If left as empty, no file will be saved.\n                Defaults to ''.\n\n            **kwargs: Other keyword arguments passed to :meth:`preprocess`,\n                :meth:`forward`, :meth:`visualize` and :meth:`postprocess`.\n                Each key in kwargs should be in the corresponding set of\n                ``preprocess_kwargs``, ``forward_kwargs``, ``visualize_kwargs``\n                and ``postprocess_kwargs``.\n\n        Returns:\n            dict: Inference and visualization results.\n        \"\"\"\n        (\n            preprocess_kwargs,\n            forward_kwargs,\n            visualize_kwargs,\n            postprocess_kwargs,\n        ) = self._dispatch_kwargs(**kwargs)\n\n        ori_inputs = self._inputs_to_list(inputs)\n\n        if isinstance(texts, str):\n            texts = [texts] * len(ori_inputs)\n\n        for i in range(len(texts)):\n            ori_inputs[i] = {\n                'img_path': ori_inputs[i],\n                'text': texts[i],\n                'custom_entities': False\n            }\n        inputs = self.preprocess(\n            ori_inputs,\n            pipeline=self.pipeline['retrieval_pipeline'],\n            batch_size=batch_size,\n            **preprocess_kwargs)\n\n        self.model.sem_seg_head._force_not_use_cache = True\n\n        pred_scores = []\n        for _, retrieval_data in track(inputs, description='Inference'):\n            preds = self.forward(retrieval_data, **forward_kwargs)\n            pred_scores.append(preds[0].pred_score)\n\n        pred_score = torch.cat(pred_scores)\n        pred_score = torch.softmax(pred_score, dim=0)\n        max_id = torch.argmax(pred_score)\n        retrieval_ori_input = ori_inputs[max_id.item()]\n        max_prob = round(pred_score[max_id].item(), 3)\n        print(\n            'The image that best matches the given text is '\n            f\"{retrieval_ori_input['img_path']} and probability is {max_prob}\")\n\n        inputs = self.preprocess([retrieval_ori_input],\n                                 pipeline=self.pipeline['grounding_pipeline'],\n                                 batch_size=1,\n                                 **preprocess_kwargs)\n\n        self.model.task = 'ref-seg'\n        self.model.sem_seg_head.task = 'ref-seg'\n        self.model.sem_seg_head.predictor.task = 'ref-seg'\n\n        ori_inputs, grounding_data = next(inputs)\n\n        if isinstance(ori_inputs, dict):\n            ori_inputs = ori_inputs['img_path']\n\n        preds = self.forward(grounding_data, **forward_kwargs)\n\n        visualization = self.visualize(\n            ori_inputs,\n            preds,\n            return_vis=return_vis,\n            show=show,\n            wait_time=wait_time,\n            draw_pred=draw_pred,\n            pred_score_thr=pred_score_thr,\n            no_save_vis=no_save_vis,\n            img_out_dir=out_dir,\n            **visualize_kwargs)\n        results = self.postprocess(\n            preds,\n            visualization,\n            return_datasamples=return_datasamples,\n            print_result=print_result,\n            no_save_pred=no_save_pred,\n            pred_out_dir=out_dir,\n            **postprocess_kwargs)\n        if results['visualization'] is not None:\n            results['visualization'] = results['visualization']\n        return results\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/language_model.py",
    "content": "import os\nfrom collections import OrderedDict\n\nimport torch\nfrom mmcv.cnn.bricks import DropPath\nfrom torch import nn\nfrom transformers import CLIPTokenizer\n\nfrom .utils import get_prompt_templates\n\n# modified from https://github.com/microsoft/X-Decoder/blob/main/xdecoder/language/vlpencoder.py # noqa\n\n\nclass LanguageEncoder(nn.Module):\n\n    def __init__(\n        self,\n        tokenizer='openai/clip-vit-base-patch32',\n        dim_lang=512,\n        dim_projection=512,\n    ):\n        super().__init__()\n\n        os.environ['TOKENIZERS_PARALLELISM'] = 'true'\n        self.tokenizer = CLIPTokenizer.from_pretrained(tokenizer)\n        self.tokenizer.add_special_tokens(\n            {'cls_token': self.tokenizer.eos_token})\n\n        max_token_num = self.tokenizer.model_max_length\n        self.lang_encoder = Transformer(max_token_num,\n                                        self.tokenizer.vocab_size, dim_lang)\n\n        self.lang_proj = nn.Parameter(torch.empty(dim_lang, dim_projection))\n        self.max_token_num = max_token_num\n        self.logit_scale = nn.Parameter(torch.ones([]))\n\n    @torch.no_grad()\n    def get_mean_embeds(self, class_names, name='default'):\n\n        def extract_mean_emb(txts):\n            tokens = self.tokenizer(\n                txts,\n                padding='max_length',\n                truncation=True,\n                max_length=self.max_token_num,\n                return_tensors='pt')\n            clss_embedding, _ = self.forward_language(\n                (tokens['input_ids'].cuda(), tokens['attention_mask'].cuda()),\n                norm=True,\n                with_token_embed=False)\n            clss_embedding = clss_embedding.mean(dim=0)\n            clss_embedding /= clss_embedding.norm()\n            return clss_embedding\n\n        templates = get_prompt_templates()\n\n        clss_embeddings = []\n        for clss in class_names:\n            txts = [\n                template.format(\n                    clss.replace('-other',\n                                 '').replace('-merged',\n                                             '').replace('-stuff', ''))\n                for template in templates\n            ]\n            clss_embeddings.append(extract_mean_emb(txts))\n\n        text_emb = torch.stack(clss_embeddings, dim=0)\n        setattr(self, '{}_text_embeddings'.format(name), text_emb)\n\n    def get_text_embeds(self, txts, name='grounding', norm=False):\n        tokens = self.tokenizer(\n            txts,\n            padding='max_length',\n            truncation=True,\n            max_length=self.max_token_num,\n            return_tensors='pt')\n        tokens = {key: value.cuda() for key, value in tokens.items()}\n        class_emb, token_emb = self.forward_language(\n            (tokens['input_ids'], tokens['attention_mask']), norm=norm)\n        ret = {\n            'tokens': tokens,\n            'token_emb': token_emb,\n            'class_emb': class_emb,\n        }\n        setattr(self, '{}_token_embeddings'.format(name), ret)\n        return ret\n\n    def get_sot_token(self, device):\n        # 49406: CLIP SOT token <|startoftext|>\n        # 77: CLIP context_length\n        return torch.tensor([[49406] * 77], device=device)\n\n    def compute_similarity(self, v_emb, name='default'):\n        v_emb = v_emb / (v_emb.norm(dim=-1, keepdim=True) + 1e-7)\n        t_emb = getattr(self, '{}_text_embeddings'.format(name))\n        output = self.logit_scale.exp() * v_emb @ t_emb.unsqueeze(0).transpose(\n            1, 2)\n        return output\n\n    def forward_language(self,\n                         texts,\n                         norm=False,\n                         with_token_embed=True,\n                         with_cls_embed=True):\n        x = self.lang_encoder(*texts)\n        hidden_x = x['last_hidden_state']\n\n        class_embed = None\n        if with_cls_embed:\n            class_embed = hidden_x[torch.arange(hidden_x.size(0)),\n                                   texts[0].argmax(dim=-1)]\n\n            class_embed = class_embed @ self.lang_proj\n            if norm:\n                class_embed = class_embed / (\n                    class_embed.norm(dim=-1, keepdim=True) + 1e-7)\n\n        hidden_embed = None\n        if with_token_embed:\n            hidden_embed = hidden_x @ self.lang_proj\n            if norm:\n                hidden_embed = hidden_embed / (\n                    hidden_embed.norm(dim=-1, keepdim=True) + 1e-7)\n\n        return class_embed, hidden_embed\n\n\nclass Transformer(nn.Module):\n\n    def __init__(self,\n                 context_length,\n                 vocab_size,\n                 width,\n                 layers: int = 12,\n                 heads: int = 8,\n                 drop_path: float = 0.0,\n                 autogressive: bool = True):\n        super().__init__()\n\n        self.token_embedding = nn.Embedding(vocab_size, width)\n\n        self.context_length = context_length\n        self.positional_embedding = nn.Parameter(\n            torch.empty(self.context_length, width))\n\n        self.width = width\n        self.layers = layers\n        self.autogressive = autogressive\n        attn_mask = self.build_attention_mask() if autogressive else None\n        dpr = [x.item() for x in torch.linspace(0, drop_path, layers)\n               ]  # stochastic depth decay rule\n        self.resblocks = nn.ModuleList([\n            ResidualAttentionBlock(width, heads, attn_mask, dpr[i])\n            for i in range(layers)\n        ])\n\n        self.ln_final = LayerNorm(width)\n\n    @property\n    def dim_out(self):\n        return self.width\n\n    def build_attention_mask(self):\n        # lazily create causal attention mask,\n        # with full attention between the vision tokens\n        # pytorch uses additive attention mask; fill with -inf\n        mask = torch.empty(self.context_length, self.context_length)\n        mask.fill_(float('-inf'))\n        mask.triu_(1)  # zero out the lower diagonal\n        return mask\n\n    def forward(self, input_ids, attention_mask=None):\n        key_padding_mask = (attention_mask == 0) if (\n            not self.autogressive and attention_mask is not None) else None\n        x = self.token_embedding(input_ids)  # [batch_size, n_ctx, d_model]\n        x = x + self.positional_embedding\n        x = x.permute(1, 0, 2)  # NLD -> LND\n        for block in self.resblocks:\n            x = block(x, key_padding_mask)\n        x = x.permute(1, 0, 2)  # LND -> NLD\n\n        x = self.ln_final(x)\n\n        return {'last_hidden_state': x}\n\n\nclass LayerNorm(nn.Module):\n\n    def __init__(self, hidden_size, eps=1e-12):\n        \"\"\"Construct a layernorm module in the TF style (epsilon inside the\n        square root).\"\"\"\n        super(LayerNorm, self).__init__()\n        self.weight = nn.Parameter(torch.ones(hidden_size))\n        self.bias = nn.Parameter(torch.zeros(hidden_size))\n        self.variance_epsilon = eps\n\n    def forward(self, x):\n        pdtype = x.dtype\n        x = x.float()\n        u = x.mean(-1, keepdim=True)\n        s = (x - u).pow(2).mean(-1, keepdim=True)\n        x = (x - u) / torch.sqrt(s + self.variance_epsilon)\n        return self.weight * x.to(pdtype) + self.bias\n\n\nclass QuickGELU(nn.Module):\n\n    def forward(self, x: torch.Tensor):\n        return x * torch.sigmoid(1.702 * x)\n\n\nclass ResidualAttentionBlock(nn.Module):\n\n    def __init__(self,\n                 d_model: int,\n                 n_head: int,\n                 attn_mask: torch.Tensor = None,\n                 drop_path: float = 0.0):\n        super().__init__()\n\n        self.attn = nn.MultiheadAttention(d_model, n_head)\n        self.ln_1 = LayerNorm(d_model)\n        self.mlp = nn.Sequential(\n            OrderedDict([('c_fc', nn.Linear(d_model, d_model * 4)),\n                         ('gelu', QuickGELU()),\n                         ('c_proj', nn.Linear(d_model * 4, d_model))]))\n        self.ln_2 = LayerNorm(d_model)\n        self.attn_mask = attn_mask\n        self.drop_path = DropPath(\n            drop_path) if drop_path > 0. else nn.Identity()\n\n    def attention(self,\n                  x: torch.Tensor,\n                  key_padding_mask: torch.Tensor = None):\n        self.attn_mask = self.attn_mask.to(dtype=x.dtype, device=x.device) \\\n            if self.attn_mask is not None else None\n\n        return self.attn(\n            x,\n            x,\n            x,\n            key_padding_mask=key_padding_mask,\n            need_weights=False,\n            attn_mask=self.attn_mask)[0]\n\n    def forward(self, x: torch.Tensor, key_padding_mask: torch.Tensor = None):\n        x = x + self.drop_path(\n            self.attention(self.ln_1(x), key_padding_mask=key_padding_mask))\n        x = x + self.drop_path(self.mlp(self.ln_2(x)))\n        return x\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/pixel_decoder.py",
    "content": "from typing import Callable, Optional, Union\n\nfrom torch import nn\nfrom torch.nn import functional as F\n\nfrom mmdet.registry import MODELS\nfrom .transformer_blocks import (Conv2d, PositionEmbeddingSine,\n                                 TransformerEncoder, TransformerEncoderLayer,\n                                 get_norm)\n\n# modified from https://github.com/microsoft/X-Decoder/blob/main/xdecoder/body/encoder/transformer_encoder_fpn.py # noqa\n\n\nclass TransformerEncoderOnly(nn.Module):\n\n    def __init__(self,\n                 d_model=512,\n                 nhead=8,\n                 num_encoder_layers=6,\n                 dim_feedforward=2048,\n                 dropout=0.1,\n                 activation='relu',\n                 normalize_before=False):\n        super().__init__()\n\n        encoder_layer = TransformerEncoderLayer(d_model, nhead,\n                                                dim_feedforward, dropout,\n                                                activation, normalize_before)\n        encoder_norm = nn.LayerNorm(d_model) if normalize_before else None\n        self.encoder = TransformerEncoder(encoder_layer, num_encoder_layers,\n                                          encoder_norm)\n\n        self._reset_parameters()\n\n        self.d_model = d_model\n        self.nhead = nhead\n\n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def forward(self, src, mask, pos_embed):\n        # flatten NxCxHxW to HWxNxC\n        bs, c, h, w = src.shape\n        src = src.flatten(2).permute(2, 0, 1)\n        pos_embed = pos_embed.flatten(2).permute(2, 0, 1)\n        if mask is not None:\n            mask = mask.flatten(1)\n\n        memory = self.encoder(src, src_key_padding_mask=mask, pos=pos_embed)\n        return memory.permute(1, 2, 0).view(bs, c, h, w)\n\n\nclass BasePixelDecoder(nn.Module):\n\n    def __init__(\n        self,\n        in_channels,\n        conv_dim: int,\n        mask_dim: int,\n        mask_on: bool,\n        norm: Optional[Union[str, Callable]] = None,\n    ):\n        super().__init__()\n\n        lateral_convs = []\n        output_convs = []\n\n        use_bias = norm == ''\n        for idx, in_channel in enumerate(in_channels):\n            if idx == len(in_channels) - 1:\n                output_norm = get_norm(norm, conv_dim)\n                output_conv = Conv2d(\n                    in_channel,\n                    conv_dim,\n                    kernel_size=3,\n                    stride=1,\n                    padding=1,\n                    bias=use_bias,\n                    norm=output_norm,\n                    activation=F.relu,\n                )\n                self.add_module('layer_{}'.format(idx + 1), output_conv)\n\n                lateral_convs.append(None)\n                output_convs.append(output_conv)\n            else:\n                lateral_norm = get_norm(norm, conv_dim)\n                output_norm = get_norm(norm, conv_dim)\n\n                lateral_conv = Conv2d(\n                    in_channel,\n                    conv_dim,\n                    kernel_size=1,\n                    bias=use_bias,\n                    norm=lateral_norm)\n                output_conv = Conv2d(\n                    conv_dim,\n                    conv_dim,\n                    kernel_size=3,\n                    stride=1,\n                    padding=1,\n                    bias=use_bias,\n                    norm=output_norm,\n                    activation=F.relu,\n                )\n                self.add_module('adapter_{}'.format(idx + 1), lateral_conv)\n                self.add_module('layer_{}'.format(idx + 1), output_conv)\n\n                lateral_convs.append(lateral_conv)\n                output_convs.append(output_conv)\n        # Place convs into top-down order (from low to high resolution)\n        # to make the top-down computation in forward clearer.\n        self.lateral_convs = lateral_convs[::-1]\n        self.output_convs = output_convs[::-1]\n\n        self.mask_on = mask_on\n        if self.mask_on:\n            self.mask_dim = mask_dim\n            self.mask_features = Conv2d(\n                conv_dim,\n                mask_dim,\n                kernel_size=3,\n                stride=1,\n                padding=1,\n            )\n        self.maskformer_num_feature_levels = 3\n\n\n# To prevent conflicts with TransformerEncoderPixelDecoder in mask2former,\n# we change the name to XTransformerEncoderPixelDecoder\n@MODELS.register_module()\nclass XTransformerEncoderPixelDecoder(BasePixelDecoder):\n\n    def __init__(\n        self,\n        in_channels,\n        transformer_dropout: float = 0.0,\n        transformer_nheads: int = 8,\n        transformer_dim_feedforward: int = 2048,\n        transformer_enc_layers: int = 6,\n        transformer_pre_norm: bool = False,\n        conv_dim: int = 512,\n        mask_dim: int = 512,\n        norm: Optional[Union[str, Callable]] = 'GN',\n    ):\n\n        super().__init__(\n            in_channels,\n            conv_dim=conv_dim,\n            mask_dim=mask_dim,\n            norm=norm,\n            mask_on=True)\n\n        self.in_features = ['res2', 'res3', 'res4', 'res5']\n        feature_channels = in_channels\n\n        in_channels = feature_channels[len(in_channels) - 1]\n        self.input_proj = Conv2d(in_channels, conv_dim, kernel_size=1)\n        self.transformer = TransformerEncoderOnly(\n            d_model=conv_dim,\n            dropout=transformer_dropout,\n            nhead=transformer_nheads,\n            dim_feedforward=transformer_dim_feedforward,\n            num_encoder_layers=transformer_enc_layers,\n            normalize_before=transformer_pre_norm,\n        )\n        self.pe_layer = PositionEmbeddingSine(conv_dim // 2, normalize=True)\n\n        # update layer\n        use_bias = norm == ''\n        output_norm = get_norm(norm, conv_dim)\n        output_conv = Conv2d(\n            conv_dim,\n            conv_dim,\n            kernel_size=3,\n            stride=1,\n            padding=1,\n            bias=use_bias,\n            norm=output_norm,\n            activation=F.relu,\n        )\n        delattr(self, 'layer_{}'.format(len(self.in_features)))\n        self.add_module('layer_{}'.format(len(self.in_features)), output_conv)\n        self.output_convs[0] = output_conv\n\n    def forward(self, features):\n        multi_scale_features = []\n        num_cur_levels = 0\n\n        # Reverse feature maps into top-down order\n        # (from low to high resolution)\n        for idx, f in enumerate(self.in_features[::-1]):\n            x = features[f]\n            lateral_conv = self.lateral_convs[idx]\n            output_conv = self.output_convs[idx]\n            if lateral_conv is None:\n                transformer = self.input_proj(x)\n                pos = self.pe_layer(x)\n                transformer = self.transformer(transformer, None, pos)\n                y = output_conv(transformer)\n            else:\n                cur_fpn = lateral_conv(x)\n                # Following FPN implementation, we use nearest upsampling here\n                y = cur_fpn + F.interpolate(\n                    y, size=cur_fpn.shape[-2:], mode='nearest')\n                y = output_conv(y)\n            if num_cur_levels < self.maskformer_num_feature_levels:\n                multi_scale_features.append(y)\n                num_cur_levels += 1\n\n        mask_features = self.mask_features(y)\n        return mask_features, multi_scale_features\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/transformer_blocks.py",
    "content": "import copy\nimport math\nfrom typing import Optional\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import Tensor, nn\n\n# modified from https://github.com/microsoft/X-Decoder/blob/main/xdecoder/body/transformer_blocks.py # noqa\n\"\"\"Transformer class.\n\nCopy-paste from torch.nn.Transformer with modifications:\n    * positional encodings are passed in MHattention\n    * extra LN at the end of encoder is removed\n    * decoder returns a stack of activations from all decoding layers\n\"\"\"\n\n\nclass Conv2d(torch.nn.Conv2d):\n    \"\"\"A wrapper around :class:`torch.nn.Conv2d` to support empty inputs and\n    more features.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"Extra keyword arguments supported in addition to those in\n        `torch.nn.Conv2d`:\n\n        Args:\n            norm (nn.Module, optional): a normalization layer\n            activation (callable(Tensor) -> Tensor): a callable\n                activation function\n\n        It assumes that norm layer is used before activation.\n        \"\"\"\n        norm = kwargs.pop('norm', None)\n        activation = kwargs.pop('activation', None)\n        super().__init__(*args, **kwargs)\n\n        self.norm = norm\n        self.activation = activation\n\n    def forward(self, x):\n        x = F.conv2d(x, self.weight, self.bias, self.stride, self.padding,\n                     self.dilation, self.groups)\n        if self.norm is not None:\n            x = self.norm(x)\n        if self.activation is not None:\n            x = self.activation(x)\n        return x\n\n\nclass PositionEmbeddingSine(nn.Module):\n    \"\"\"This is a more standard version of the position embedding, very similar\n    to the one used by the Attention is all you need paper, generalized to work\n    on images.\"\"\"\n\n    def __init__(self,\n                 num_pos_feats=64,\n                 temperature=10000,\n                 normalize=False,\n                 scale=None):\n        super().__init__()\n        self.num_pos_feats = num_pos_feats\n        self.temperature = temperature\n        self.normalize = normalize\n        if scale is not None and normalize is False:\n            raise ValueError('normalize should be True if scale is passed')\n        if scale is None:\n            scale = 2 * math.pi\n        self.scale = scale\n\n    def forward(self, x, mask=None):\n        if mask is None:\n            mask = torch.zeros((x.size(0), x.size(2), x.size(3)),\n                               device=x.device,\n                               dtype=torch.bool)\n        not_mask = ~mask\n        y_embed = not_mask.cumsum(1, dtype=x.dtype)\n        x_embed = not_mask.cumsum(2, dtype=x.dtype)\n        if self.normalize:\n            eps = 1e-6\n            y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale\n            x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale\n\n        dim_t = torch.arange(\n            self.num_pos_feats, dtype=x.dtype, device=x.device)\n        dim_t = self.temperature**(2 * (dim_t // 2) / self.num_pos_feats)\n\n        pos_x = x_embed[:, :, :, None] / dim_t\n        pos_y = y_embed[:, :, :, None] / dim_t\n        pos_x = torch.stack(\n            (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()),\n            dim=4).flatten(3)\n        pos_y = torch.stack(\n            (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()),\n            dim=4).flatten(3)\n        pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)\n        return pos\n\n    def __repr__(self, _repr_indent=4):\n        head = 'Positional encoding ' + self.__class__.__name__\n        body = [\n            'num_pos_feats: {}'.format(self.num_pos_feats),\n            'temperature: {}'.format(self.temperature),\n            'normalize: {}'.format(self.normalize),\n            'scale: {}'.format(self.scale),\n        ]\n        # _repr_indent = 4\n        lines = [head] + [' ' * _repr_indent + line for line in body]\n        return '\\n'.join(lines)\n\n\nclass TransformerEncoder(nn.Module):\n\n    def __init__(self, encoder_layer, num_layers, norm=None):\n        super().__init__()\n        self.layers = _get_clones(encoder_layer, num_layers)\n        self.num_layers = num_layers\n        self.norm = norm\n\n    def forward(\n        self,\n        src,\n        mask: Optional[Tensor] = None,\n        src_key_padding_mask: Optional[Tensor] = None,\n        pos: Optional[Tensor] = None,\n    ):\n        output = src\n\n        for layer in self.layers:\n            output = layer(\n                output,\n                src_mask=mask,\n                src_key_padding_mask=src_key_padding_mask,\n                pos=pos)\n\n        if self.norm is not None:\n            output = self.norm(output)\n\n        return output\n\n\nclass TransformerEncoderLayer(nn.Module):\n\n    def __init__(\n        self,\n        d_model,\n        nhead,\n        dim_feedforward=2048,\n        dropout=0.1,\n        activation='relu',\n        normalize_before=False,\n    ):\n        super().__init__()\n        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)\n        # Implementation of Feedforward model\n        self.linear1 = nn.Linear(d_model, dim_feedforward)\n        self.dropout = nn.Dropout(dropout)\n        self.linear2 = nn.Linear(dim_feedforward, d_model)\n\n        self.norm1 = nn.LayerNorm(d_model)\n        self.norm2 = nn.LayerNorm(d_model)\n        self.dropout1 = nn.Dropout(dropout)\n        self.dropout2 = nn.Dropout(dropout)\n\n        self.activation = _get_activation_fn(activation)\n        self.normalize_before = normalize_before\n\n    def with_pos_embed(self, tensor, pos: Optional[Tensor]):\n        return tensor if pos is None else tensor + pos\n\n    def forward_post(\n        self,\n        src,\n        src_mask: Optional[Tensor] = None,\n        src_key_padding_mask: Optional[Tensor] = None,\n        pos: Optional[Tensor] = None,\n    ):\n        q = k = self.with_pos_embed(src, pos)\n\n        src2 = self.self_attn(\n            q,\n            k,\n            value=src,\n            attn_mask=src_mask,\n            key_padding_mask=src_key_padding_mask)[0]\n        src = src + self.dropout1(src2)\n        src = self.norm1(src)\n        src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))\n        src = src + self.dropout2(src2)\n        src = self.norm2(src)\n        return src\n\n    def forward_pre(\n        self,\n        src,\n        src_mask: Optional[Tensor] = None,\n        src_key_padding_mask: Optional[Tensor] = None,\n        pos: Optional[Tensor] = None,\n    ):\n        src2 = self.norm1(src)\n        q = k = self.with_pos_embed(src2, pos)\n        src2 = self.self_attn(\n            q,\n            k,\n            value=src2,\n            attn_mask=src_mask,\n            key_padding_mask=src_key_padding_mask)[0]\n        src = src + self.dropout1(src2)\n        src2 = self.norm2(src)\n        src2 = self.linear2(self.dropout(self.activation(self.linear1(src2))))\n        src = src + self.dropout2(src2)\n        return src\n\n    def forward(\n        self,\n        src,\n        src_mask: Optional[Tensor] = None,\n        src_key_padding_mask: Optional[Tensor] = None,\n        pos: Optional[Tensor] = None,\n    ):\n        if self.normalize_before:\n            return self.forward_pre(src, src_mask, src_key_padding_mask, pos)\n        return self.forward_post(src, src_mask, src_key_padding_mask, pos)\n\n\nclass SelfAttentionLayer(nn.Module):\n\n    def __init__(self,\n                 d_model,\n                 nhead,\n                 dropout=0.0,\n                 activation='relu',\n                 normalize_before=False):\n        super().__init__()\n        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)\n\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(dropout)\n\n        self.activation = _get_activation_fn(activation)\n        self.normalize_before = normalize_before\n\n        self._reset_parameters()\n\n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def with_pos_embed(self, tensor, pos: Optional[Tensor]):\n        return tensor if pos is None else tensor + pos\n\n    def forward_post(self,\n                     tgt,\n                     tgt_mask: Optional[Tensor] = None,\n                     tgt_key_padding_mask: Optional[Tensor] = None,\n                     query_pos: Optional[Tensor] = None):\n        q = k = self.with_pos_embed(tgt, query_pos)\n        tgt2 = self.self_attn(\n            q,\n            k,\n            value=tgt,\n            attn_mask=tgt_mask,\n            key_padding_mask=tgt_key_padding_mask)[0]\n        tgt = tgt + self.dropout(tgt2)\n        tgt = self.norm(tgt)\n\n        return tgt\n\n    def forward_pre(self,\n                    tgt,\n                    tgt_mask: Optional[Tensor] = None,\n                    tgt_key_padding_mask: Optional[Tensor] = None,\n                    query_pos: Optional[Tensor] = None):\n        tgt2 = self.norm(tgt)\n        q = k = self.with_pos_embed(tgt2, query_pos)\n        tgt2 = self.self_attn(\n            q,\n            k,\n            value=tgt2,\n            attn_mask=tgt_mask,\n            key_padding_mask=tgt_key_padding_mask)[0]\n        tgt = tgt + self.dropout(tgt2)\n\n        return tgt\n\n    def forward(self,\n                tgt,\n                tgt_mask: Optional[Tensor] = None,\n                tgt_key_padding_mask: Optional[Tensor] = None,\n                query_pos: Optional[Tensor] = None):\n        if self.normalize_before:\n            return self.forward_pre(tgt, tgt_mask, tgt_key_padding_mask,\n                                    query_pos)\n        return self.forward_post(tgt, tgt_mask, tgt_key_padding_mask,\n                                 query_pos)\n\n\nclass CrossAttentionLayer(nn.Module):\n\n    def __init__(self,\n                 d_model,\n                 nhead,\n                 dropout=0.0,\n                 activation='relu',\n                 normalize_before=False):\n        super().__init__()\n        self.multihead_attn = nn.MultiheadAttention(\n            d_model, nhead, dropout=dropout)\n\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(dropout)\n\n        self.activation = _get_activation_fn(activation)\n        self.normalize_before = normalize_before\n\n        self._reset_parameters()\n\n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def with_pos_embed(self, tensor, pos: Optional[Tensor]):\n        return tensor if pos is None else tensor + pos\n\n    def forward_post(self,\n                     tgt,\n                     memory,\n                     memory_mask: Optional[Tensor] = None,\n                     memory_key_padding_mask: Optional[Tensor] = None,\n                     pos: Optional[Tensor] = None,\n                     query_pos: Optional[Tensor] = None):\n        tgt2, avg_attn = self.multihead_attn(\n            query=self.with_pos_embed(tgt, query_pos),\n            key=self.with_pos_embed(memory, pos),\n            value=memory,\n            attn_mask=memory_mask,\n            key_padding_mask=memory_key_padding_mask)\n        tgt = tgt + self.dropout(tgt2)\n        tgt = self.norm(tgt)\n        return tgt, avg_attn\n\n    def forward_pre(self,\n                    tgt,\n                    memory,\n                    memory_mask: Optional[Tensor] = None,\n                    memory_key_padding_mask: Optional[Tensor] = None,\n                    pos: Optional[Tensor] = None,\n                    query_pos: Optional[Tensor] = None):\n        tgt2 = self.norm(tgt)\n        tgt2, avg_attn = self.multihead_attn(\n            query=self.with_pos_embed(tgt2, query_pos),\n            key=self.with_pos_embed(memory, pos),\n            value=memory,\n            attn_mask=memory_mask,\n            key_padding_mask=memory_key_padding_mask)\n        tgt = tgt + self.dropout(tgt2)\n\n        return tgt, avg_attn\n\n    def forward(self,\n                tgt,\n                memory,\n                memory_mask: Optional[Tensor] = None,\n                memory_key_padding_mask: Optional[Tensor] = None,\n                pos: Optional[Tensor] = None,\n                query_pos: Optional[Tensor] = None):\n        if self.normalize_before:\n            return self.forward_pre(tgt, memory, memory_mask,\n                                    memory_key_padding_mask, pos, query_pos)\n        return self.forward_post(tgt, memory, memory_mask,\n                                 memory_key_padding_mask, pos, query_pos)\n\n\nclass FFNLayer(nn.Module):\n\n    def __init__(self,\n                 d_model,\n                 dim_feedforward=2048,\n                 dropout=0.0,\n                 activation='relu',\n                 normalize_before=False):\n        super().__init__()\n        # Implementation of Feedforward model\n        self.linear1 = nn.Linear(d_model, dim_feedforward)\n        self.dropout = nn.Dropout(dropout)\n        self.linear2 = nn.Linear(dim_feedforward, d_model)\n\n        self.norm = nn.LayerNorm(d_model)\n\n        self.activation = _get_activation_fn(activation)\n        self.normalize_before = normalize_before\n\n        self._reset_parameters()\n\n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def with_pos_embed(self, tensor, pos: Optional[Tensor]):\n        return tensor if pos is None else tensor + pos\n\n    def forward_post(self, tgt):\n        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))\n        tgt = tgt + self.dropout(tgt2)\n        tgt = self.norm(tgt)\n        return tgt\n\n    def forward_pre(self, tgt):\n        tgt2 = self.norm(tgt)\n        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt2))))\n        tgt = tgt + self.dropout(tgt2)\n        return tgt\n\n    def forward(self, tgt):\n        if self.normalize_before:\n            return self.forward_pre(tgt)\n        return self.forward_post(tgt)\n\n\nclass MLP(nn.Module):\n    \"\"\"Very simple multi-layer perceptron (also called FFN)\"\"\"\n\n    def __init__(self, input_dim, hidden_dim, output_dim, num_layers):\n        super().__init__()\n        self.num_layers = num_layers\n        h = [hidden_dim] * (num_layers - 1)\n        self.layers = nn.ModuleList(\n            nn.Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]))\n\n    def forward(self, x):\n        for i, layer in enumerate(self.layers):\n            x = F.relu(layer(x)) if i < self.num_layers - 1 else layer(x)\n        return x\n\n\ndef get_norm(norm, out_channels):\n    \"\"\"\n    Args:\n        norm (str or callable): either one of BN, SyncBN, FrozenBN, GN;\n            or a callable that takes a channel number and returns\n            the normalization layer as a nn.Module.\n\n    Returns:\n        nn.Module or None: the normalization layer\n    \"\"\"\n    if norm is None:\n        return None\n    if isinstance(norm, str):\n        if len(norm) == 0:\n            return None\n        norm = {\n            'BN': nn.BatchNorm2d,\n            'GN': lambda channels: nn.GroupNorm(32, channels),\n        }[norm]\n    return norm(out_channels)\n\n\ndef _get_clones(module, N):\n    return nn.ModuleList([copy.deepcopy(module) for i in range(N)])\n\n\ndef _get_activation_fn(activation):\n    \"\"\"Return an activation function given a string.\"\"\"\n    if activation == 'relu':\n        return F.relu\n    if activation == 'gelu':\n        return F.gelu\n    if activation == 'glu':\n        return F.glu\n    raise RuntimeError(f'activation should be relu/gelu, not {activation}.')\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/transformer_decoder.py",
    "content": "import torch\nfrom torch import nn\nfrom torch.nn import functional as F\n\nfrom mmdet.registry import MODELS\nfrom .language_model import LanguageEncoder\nfrom .transformer_blocks import (MLP, Conv2d, CrossAttentionLayer, FFNLayer,\n                                 PositionEmbeddingSine, SelfAttentionLayer)\nfrom .utils import is_lower_torch_version\n\n\ndef vl_similarity(image_feat, text_feat, temperature=1):\n    logits = torch.matmul(image_feat, text_feat.t())\n    logits = temperature.exp().clamp(max=100) * logits\n    return logits\n\n\n@MODELS.register_module()\nclass XDecoderTransformerDecoder(nn.Module):\n\n    def __init__(\n        self,\n        in_channels=512,\n        hidden_dim: int = 512,\n        dim_proj: int = 512,\n        num_queries: int = 101,\n        max_token_num: int = 77,\n        nheads: int = 8,\n        dim_feedforward: int = 2048,\n        decoder_layers: int = 9,\n        pre_norm: bool = False,\n        mask_dim: int = 512,\n        task: str = 'semseg',\n        captioning_step: int = 50,\n    ):\n        super().__init__()\n\n        # positional encoding\n        self.pe_layer = PositionEmbeddingSine(hidden_dim // 2, normalize=True)\n\n        # define transformer decoder here\n        self.num_heads = nheads\n        self.num_layers = decoder_layers\n        self.max_token_num = max_token_num\n        self.transformer_self_attention_layers = nn.ModuleList()\n        self.transformer_cross_attention_layers = nn.ModuleList()\n        self.transformer_ffn_layers = nn.ModuleList()\n\n        for _ in range(self.num_layers):\n            self.transformer_self_attention_layers.append(\n                SelfAttentionLayer(\n                    d_model=hidden_dim,\n                    nhead=nheads,\n                    dropout=0.0,\n                    normalize_before=pre_norm,\n                ))\n\n            self.transformer_cross_attention_layers.append(\n                CrossAttentionLayer(\n                    d_model=hidden_dim,\n                    nhead=nheads,\n                    dropout=0.0,\n                    normalize_before=pre_norm,\n                ))\n\n            self.transformer_ffn_layers.append(\n                FFNLayer(\n                    d_model=hidden_dim,\n                    dim_feedforward=dim_feedforward,\n                    dropout=0.0,\n                    normalize_before=pre_norm,\n                ))\n\n        self.decoder_norm = nn.LayerNorm(hidden_dim)\n\n        self.num_queries = num_queries\n        # learnable query features\n        self.query_feat = nn.Embedding(num_queries, hidden_dim)\n        # learnable query p.e.\n        self.query_embed = nn.Embedding(num_queries, hidden_dim)\n\n        # level embedding (always use 3 scales)\n        self.num_feature_levels = 3\n        self.level_embed = nn.Embedding(self.num_feature_levels, hidden_dim)\n        self.input_proj = nn.ModuleList()\n\n        for _ in range(self.num_feature_levels):\n            if in_channels != hidden_dim:\n                self.input_proj.append(\n                    Conv2d(in_channels, hidden_dim, kernel_size=1))\n            else:\n                self.input_proj.append(nn.Sequential())\n\n        self.task = task\n\n        # output FFNs\n        self.lang_encoder = LanguageEncoder()\n\n        self.mask_embed = MLP(hidden_dim, hidden_dim, mask_dim, 3)\n        self.class_embed = nn.Parameter(torch.empty(hidden_dim, dim_proj))\n\n        # for caption and ref-caption\n        self.caping_embed = nn.Parameter(torch.empty(hidden_dim, dim_proj))\n        self.pos_embed_caping = nn.Embedding(max_token_num, hidden_dim)\n        self.captioning_step = captioning_step\n\n        # register self_attn_mask to avoid information leakage,\n        # it includes interaction between object query, class query and\n        # caption query\n        self_attn_mask = torch.zeros((1, num_queries + max_token_num,\n                                      num_queries + max_token_num)).bool()\n        # object+class query does not attend with caption query.\n        self_attn_mask[:, :num_queries, num_queries:] = True\n        # caption query only attend with previous token.\n        self_attn_mask[:, num_queries:, num_queries:] = torch.triu(\n            torch.ones((1, max_token_num, max_token_num)), diagonal=1).bool()\n        # object query does not attend with class query.\n        self_attn_mask[:, :num_queries - 1, num_queries - 1:num_queries] = True\n        # class query does not attend with object query.\n        self_attn_mask[:, num_queries - 1:num_queries, :num_queries - 1] = True\n        self.register_buffer('self_attn_mask', self_attn_mask)\n\n    def forward(self, x, mask_features, extra=None):\n        if self.task == 'caption':\n            return self.forward_caption(x, mask_features, extra)\n\n        assert len(x) == self.num_feature_levels\n        src = []\n        pos = []\n        size_list = []\n\n        for i in range(self.num_feature_levels):\n            size_list.append(x[i].shape[-2:])\n            pos.append(self.pe_layer(x[i], None).flatten(2))\n            src.append(self.input_proj[i](x[i]).flatten(2) +\n                       self.level_embed.weight[i][None, :, None])\n\n            # flatten NxCxHxW to HWxNxC\n            pos[-1] = pos[-1].permute(2, 0, 1)\n            src[-1] = src[-1].permute(2, 0, 1)\n\n        _, bs, _ = src[0].shape\n\n        query_embed = self.query_embed.weight.unsqueeze(1).repeat(1, bs, 1)\n        output = self.query_feat.weight.unsqueeze(1).repeat(1, bs, 1)\n\n        predictions_mask = []\n        predictions_class_embed = []\n\n        if self.task == 'ref-seg':\n            self_tgt_mask = self.self_attn_mask[:, :self.num_queries, :self.\n                                                num_queries].repeat(\n                                                    output.shape[1] *\n                                                    self.num_heads, 1, 1)\n            grounding_tokens = extra['grounding_tokens']\n            _grounding_tokens = grounding_tokens.detach().clone()\n            # initialize with negative attention at the beginning.\n            pad_tgt_mask = torch.ones(\n                (1, self.num_queries + (self.num_queries - 1) +\n                 len(grounding_tokens), self.num_queries +\n                 (self.num_queries - 1) + len(grounding_tokens)),\n                device=self_tgt_mask.device).bool().repeat(\n                    output.shape[1] * self.num_heads, 1, 1)\n            pad_tgt_mask[:, :self.num_queries, :self.\n                         num_queries] = self_tgt_mask\n            # grounding tokens could attend with eatch other\n            pad_tgt_mask[:, self.num_queries:, self.num_queries:] = False\n            self_tgt_mask = pad_tgt_mask\n            output = torch.cat((output, output[:-1]), dim=0)\n            # also pad language embdding to fix embedding\n            query_embed = torch.cat((query_embed, query_embed[:-1]), dim=0)\n        else:\n            self_tgt_mask = self.self_attn_mask[:, :self.num_queries, :self.\n                                                num_queries].repeat(\n                                                    output.shape[1] *\n                                                    self.num_heads, 1, 1)\n\n        results = self.forward_prediction_heads(\n            output, mask_features, attn_mask_target_size=size_list[0])\n        attn_mask = results['attn_mask']\n        predictions_class_embed.append(results['class_embed'])\n        predictions_mask.append(results['outputs_mask'])\n\n        for i in range(self.num_layers):\n            level_index = i % self.num_feature_levels\n            attn_mask[torch.where(\n                attn_mask.sum(-1) == attn_mask.shape[-1])] = False\n\n            # attention: cross-attention first\n            output, avg_attn = self.transformer_cross_attention_layers[i](\n                output,\n                src[level_index],\n                memory_mask=attn_mask,\n                # here we do not apply masking on padded region\n                memory_key_padding_mask=None,\n                pos=pos[level_index],\n                query_pos=query_embed)\n\n            if self.task == 'ref-seg':\n                output = torch.cat((output, _grounding_tokens), dim=0)\n                query_embed = torch.cat((query_embed, grounding_tokens), dim=0)\n\n            output = self.transformer_self_attention_layers[i](\n                output,\n                tgt_mask=self_tgt_mask,\n                tgt_key_padding_mask=None,\n                query_pos=query_embed)\n\n            output = self.transformer_ffn_layers[i](output)\n\n            if self.task == 'ref-seg':\n                _grounding_tokens = output[-len(_grounding_tokens):]\n                output = output[:-len(_grounding_tokens)]\n                query_embed = query_embed[:-len(_grounding_tokens)]\n\n            results = self.forward_prediction_heads(\n                output,\n                mask_features,\n                attn_mask_target_size=size_list[(i + 1) %\n                                                self.num_feature_levels])\n            attn_mask = results['attn_mask']\n            predictions_mask.append(results['outputs_mask'])\n            predictions_class_embed.append(results['class_embed'])\n\n        out = {\n            'pred_masks': predictions_mask[-1],\n            'pred_class_embed': predictions_class_embed[-1],\n        }\n\n        if self.task == 'ref-seg':\n            mask_pred_results = []\n            outputs_class = []\n            for idx in range(mask_features.shape[0]):  # batch size\n                pred_gmasks = out['pred_masks'][idx, self.num_queries:2 *\n                                                self.num_queries - 1]\n                v_emb = predictions_class_embed[-1][idx, self.num_queries:2 *\n                                                    self.num_queries - 1]\n                t_emb = extra['class_emb']\n\n                t_emb = t_emb / (t_emb.norm(dim=-1, keepdim=True) + 1e-7)\n                v_emb = v_emb / (v_emb.norm(dim=-1, keepdim=True) + 1e-7)\n\n                temperature = self.lang_encoder.logit_scale\n                out_prob = vl_similarity(v_emb, t_emb, temperature=temperature)\n\n                matched_id = out_prob.max(0)[1]\n                mask_pred_results += [pred_gmasks[matched_id, :, :]]\n                outputs_class += [out_prob[matched_id, :]]\n            out['pred_masks'] = mask_pred_results\n            out['pred_logits'] = outputs_class\n        elif self.task == 'retrieval':\n            t_emb = extra['class_emb']\n            temperature = self.lang_encoder.logit_scale\n            v_emb = out['pred_class_embed'][:, -1, :]\n            v_emb = v_emb / (v_emb.norm(dim=-1, keepdim=True) + 1e-7)\n            logits = vl_similarity(v_emb, t_emb, temperature)\n            out['pred_logits'] = logits\n        elif self.task in ['semseg', 'instance', 'panoptic']:\n            outputs_class = self.lang_encoder.compute_similarity(\n                out['pred_class_embed'])\n            out['pred_logits'] = outputs_class\n        return out\n\n    def forward_caption(self, x, mask_features, extra=None):\n        assert len(x) == self.num_feature_levels\n        src = []\n        pos = []\n        size_list = []\n\n        for i in range(self.num_feature_levels):\n            size_list.append(x[i].shape[-2:])\n            pos.append(self.pe_layer(x[i], None).flatten(2))\n            src.append(self.input_proj[i](x[i]).flatten(2) +\n                       self.level_embed.weight[i][None, :, None])\n\n            # flatten NxCxHxW to HWxNxC\n            pos[-1] = pos[-1].permute(2, 0, 1)\n            src[-1] = src[-1].permute(2, 0, 1)\n\n        _, bs, _ = src[0].shape\n\n        # QxNxC\n        query_embed_ = self.query_embed.weight.unsqueeze(1).repeat(1, bs, 1)\n        query_feat = self.query_feat.weight.unsqueeze(1).repeat(1, bs, 1)\n        lang_token = extra['start_token'].repeat(bs, 1)\n        pos_embed = self.pos_embed_caping.weight.unsqueeze(1).repeat(1, bs, 1)\n\n        # prepare token embedding for evaluation\n        token_embs = self.lang_encoder.lang_encoder.token_embedding.weight\n\n        for cap_idx in range(0, self.captioning_step):\n            lang_embed = self.lang_encoder.forward_language(\n                (lang_token, ), with_cls_embed=False)[1].transpose(0, 1)\n            # concat object query, class token and caption token.\n            output = torch.cat((query_feat, lang_embed), dim=0)\n            lang_embed += pos_embed\n            query_embed = torch.cat((query_embed_, lang_embed), dim=0)\n\n            # prediction heads on learnable query features\n            results = self.forward_prediction_heads(\n                output, mask_features, attn_mask_target_size=size_list[0])\n            attn_mask = results['attn_mask']\n\n            for i in range(self.num_layers):\n                level_index = i % self.num_feature_levels\n                attn_mask[torch.where(\n                    attn_mask.sum(-1) == attn_mask.shape[-1])] = False\n                attn_mask = torch.cat(\n                    (attn_mask,\n                     torch.zeros_like(attn_mask[:, :self.max_token_num, :])),\n                    dim=1)\n                self_tgt_mask = self.self_attn_mask.repeat(\n                    output.shape[1] * self.num_heads, 1, 1)\n\n                if 'grounding_mask' in extra:\n                    bs, nq, wh = attn_mask.shape\n                    assert bs == self.num_heads, 'Only support single ' \\\n                                                 'image referring captioning.'\n                    grounding_mask = extra['grounding_mask']\n                    attn_mask = attn_mask.reshape(bs, nq, size_list[i % 3][0],\n                                                  size_list[i % 3][1])\n                    grounding_mask = F.interpolate(\n                        grounding_mask.float(),\n                        size_list[i % 3],\n                        mode='nearest').bool()[0, 0]\n                    attn_mask[:, self.num_queries:, grounding_mask] = True\n                    attn_mask = attn_mask.reshape(bs, nq, wh)\n\n                # attention: cross-attention first\n                output, avg_attn = self.transformer_cross_attention_layers[i](\n                    output,\n                    src[level_index],\n                    memory_mask=attn_mask,\n                    # here we do not apply masking on padded region\n                    memory_key_padding_mask=None,\n                    pos=pos[level_index],\n                    query_pos=query_embed)\n\n                output = self.transformer_self_attention_layers[i](\n                    output,\n                    tgt_mask=self_tgt_mask,\n                    tgt_key_padding_mask=None,\n                    query_pos=query_embed)\n\n                output = self.transformer_ffn_layers[i](output)\n\n                results = self.forward_prediction_heads(\n                    output,\n                    mask_features,\n                    attn_mask_target_size=size_list[(i + 1) %\n                                                    self.num_feature_levels])\n                attn_mask = results['attn_mask']\n\n            pred_captions = results['outputs_caption']\n            pred_captions = pred_captions @ token_embs.t()\n            lang_token[:, cap_idx + 1] = pred_captions[:, cap_idx].max(-1)[1]\n\n        texts = self.lang_encoder.tokenizer.batch_decode(\n            lang_token, skip_special_tokens=False)\n        texts_new = []\n\n        for x in texts:\n            x = x.split('<|endoftext|>')[0]\n            x = x.replace('<|endoftext|>', '')\n            x = x.replace('<|startoftext|>', '')\n            x = x.strip()\n            texts_new.append(x)\n\n        out = {'pred_caption': texts_new}\n        return out\n\n    def forward_prediction_heads(self, output, mask_features,\n                                 attn_mask_target_size):\n        decoder_output = self.decoder_norm(output)\n        decoder_output = decoder_output.transpose(0, 1)\n\n        if self.task == 'caption':\n            outputs_caption = decoder_output[:, self.\n                                             num_queries:] @ self.caping_embed\n\n        # recompute class token output.\n        norm_decoder_output = decoder_output / (\n            decoder_output.norm(dim=-1, keepdim=True) + 1e-7)\n        obj_token = norm_decoder_output[:, :self.num_queries - 1]\n        cls_token = norm_decoder_output[:,\n                                        self.num_queries - 1:self.num_queries]\n\n        sim = (cls_token @ obj_token.transpose(1, 2)).softmax(-1)[:, 0, :,\n                                                                  None]\n        cls_token = (sim * decoder_output[:, :self.num_queries - 1]).sum(\n            dim=1, keepdim=True)\n\n        if self.task == 'ref-seg':\n            decoder_output = torch.cat(\n                (decoder_output[:, :self.num_queries - 1], cls_token,\n                 decoder_output[:, self.num_queries:2 * self.num_queries - 1]),\n                dim=1)\n        else:\n            decoder_output = torch.cat(\n                (decoder_output[:, :self.num_queries - 1], cls_token), dim=1)\n\n        mask_embed = self.mask_embed(decoder_output)\n        outputs_mask = torch.einsum('bqc,bchw->bqhw', mask_embed,\n                                    mask_features)\n\n        if is_lower_torch_version():\n            attn_mask = F.interpolate(\n                outputs_mask,\n                size=attn_mask_target_size,\n                mode='bicubic',\n                align_corners=False)\n        else:\n            attn_mask = F.interpolate(\n                outputs_mask,\n                size=attn_mask_target_size,\n                mode='bicubic',\n                align_corners=False,\n                antialias=True)\n\n        attn_mask = (attn_mask.sigmoid().flatten(2).unsqueeze(1).repeat(\n            1, self.num_heads, 1, 1).flatten(0, 1) < 0.5).bool()\n        attn_mask = attn_mask.detach()\n\n        attn_mask[:, self.num_queries:self.num_queries + 1].fill_(False)\n\n        if self.task == 'caption':\n            results = {\n                'attn_mask': attn_mask,\n                'outputs_caption': outputs_caption,\n            }\n            return results\n        else:\n            class_embed = decoder_output @ self.class_embed\n            results = {\n                'outputs_mask': outputs_mask,\n                'attn_mask': attn_mask,\n                'class_embed': class_embed,\n            }\n            return results\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/unified_head.py",
    "content": "import copy\nfrom typing import Sequence\n\nimport torch\nfrom mmengine.structures import InstanceData, PixelData\nfrom torch import nn\nfrom torch.nn import functional as F\n\nfrom mmdet.evaluation.functional import INSTANCE_OFFSET\nfrom mmdet.registry import MODELS\nfrom .utils import (is_lower_torch_version, retry_if_cuda_oom,\n                    sem_seg_postprocess)\n\n\n@MODELS.register_module()\nclass XDecoderUnifiedhead(nn.Module):\n\n    def __init__(self,\n                 in_channels: int,\n                 pixel_decoder: nn.Module,\n                 transformer_decoder: nn.Module,\n                 task: str = 'semseg',\n                 test_cfg=None):\n        super().__init__()\n        self.task = task\n        self.test_cfg = test_cfg\n\n        pixel_decoder_ = copy.deepcopy(pixel_decoder)\n        pixel_decoder_.update(in_channels=in_channels)\n        self.pixel_decoder = MODELS.build(pixel_decoder_)\n\n        transformer_decoder_ = copy.deepcopy(transformer_decoder)\n        transformer_decoder_.update(task=task)\n        self.predictor = MODELS.build(transformer_decoder_)\n\n        self.return_inter_mask = False\n        if self.task == 'ref-caption':\n            # ref-caption = ref-seg + caption,\n            # so we need to return the intermediate mask\n            self.return_inter_mask = True\n\n        self._all_text_prompts = None\n        self._extra = None\n        # TODO: Very trick, for retrieval task\n        self._force_not_use_cache = False\n\n    def pre_process(self, batch_data_samples, device):\n        extra = {}\n        if self.task != 'caption':\n            # have text\n            all_text_prompts = []\n            num_thing_class = 0\n            for data_samples in batch_data_samples:\n                if isinstance(data_samples.text, str):\n                    text = data_samples.text.split('.')\n                elif isinstance(data_samples.text, Sequence):\n                    text = data_samples.text\n                else:\n                    raise TypeError(\n                        'Type pf data_sample.text must be sequence or str')\n                text = list(filter(lambda x: len(x) > 0, text))\n                all_text_prompts.append(text)\n                num_thing_class = len(text)\n                # for panoptic\n                if 'stuff_text' in data_samples:\n                    if isinstance(data_samples.stuff_text, str):\n                        text = data_samples.stuff_text.split('.')\n                    elif isinstance(data_samples.stuff_text, Sequence):\n                        text = data_samples.stuff_text\n                    else:\n                        raise TypeError('Type pf data_sample.stuff_text '\n                                        'must be sequence or str')\n                    text = list(filter(lambda x: len(x) > 0, text))\n                    all_text_prompts[-1].extend(text)\n\n            # TODO: support batch\n            all_text_prompts = all_text_prompts[0]\n\n            if all_text_prompts != self._all_text_prompts \\\n                    or self._force_not_use_cache:\n                # avoid redundant computation\n                self._all_text_prompts = all_text_prompts\n                if self.task in ['semseg', 'instance', 'panoptic']:\n                    self.predictor.lang_encoder.get_mean_embeds(\n                        all_text_prompts + ['background'])\n                elif self.task == 'ref-seg':\n                    token_info = self.predictor.lang_encoder.get_text_embeds(\n                        all_text_prompts, norm=False)\n                    token_emb = token_info['token_emb']\n                    tokens = token_info['tokens']\n                    query_emb = token_emb[tokens['attention_mask'].bool()]\n                    extra['grounding_tokens'] = query_emb[:, None]\n                    extra['class_emb'] = token_info['class_emb']\n                elif self.task == 'retrieval':\n                    token_info = self.predictor.lang_encoder.get_text_embeds(\n                        all_text_prompts, norm=True)\n                    extra['class_emb'] = token_info['class_emb']\n                self._extra = extra\n                return extra, all_text_prompts, num_thing_class\n            else:\n                return self._extra, all_text_prompts, num_thing_class\n        else:\n            if not hasattr(self, 'start_token'):\n                self.start_token = self.predictor.lang_encoder. \\\n                    get_sot_token(device=device)\n            extra['start_token'] = self.start_token\n            return extra, None, None\n\n    def predict(self, features, batch_data_samples):\n        # multi scale feature\n        mask_features, multi_scale_features = self.pixel_decoder(features)\n\n        # pre process\n        extra, all_text_prompts, num_thing_class = self.pre_process(\n            batch_data_samples, mask_features.device)\n\n        # transformer decoder forward\n        predictions = self.predictor(\n            multi_scale_features, mask_features, extra=extra)\n\n        # post process\n        return self.post_process(predictions, batch_data_samples,\n                                 all_text_prompts, num_thing_class)\n\n    def post_process(self, predictions, batch_data_samples, all_text_prompts,\n                     num_thing_class):\n        batch_img_metas = [\n            data_samples.metainfo for data_samples in batch_data_samples\n        ]\n        batch_input_shape = batch_data_samples[0].metainfo['batch_input_shape']\n\n        if self.task == 'caption':\n            for text, data_samples in zip(predictions['pred_caption'],\n                                          batch_data_samples):\n                data_samples.pred_caption = text\n\n            if 'pred_instances' in batch_data_samples[0]:\n                for img_metas, data_samples in zip(batch_img_metas,\n                                                   batch_data_samples):\n                    original_caption = data_samples.text.split('.')\n                    text_prompts = list(\n                        filter(lambda x: len(x) > 0, original_caption))\n\n                    height = img_metas['ori_shape'][0]\n                    width = img_metas['ori_shape'][1]\n                    image_size = img_metas['grounding_img_shape'][:2]\n\n                    mask_pred_result = data_samples.pred_instances.masks.float(\n                    )\n                    mask_cls_result = data_samples.pred_instances.scores.float(\n                    )\n\n                    mask_pred_result = retry_if_cuda_oom(sem_seg_postprocess)(\n                        mask_pred_result, image_size, height, width)\n\n                    pred_instances = retry_if_cuda_oom(\n                        self._instance_inference)(mask_cls_result,\n                                                  mask_pred_result,\n                                                  text_prompts)\n                    data_samples.pred_instances = pred_instances\n\n        elif self.task in ['semseg', 'instance', 'panoptic']:\n            mask_pred_results = predictions['pred_masks']\n            mask_cls_results = predictions['pred_logits']\n            if is_lower_torch_version():\n                mask_pred_results = F.interpolate(\n                    mask_pred_results,\n                    size=(batch_input_shape[-2], batch_input_shape[-1]),\n                    mode='bicubic',\n                    align_corners=False)\n            else:\n                mask_pred_results = F.interpolate(\n                    mask_pred_results,\n                    size=(batch_input_shape[-2], batch_input_shape[-1]),\n                    mode='bicubic',\n                    align_corners=False,\n                    antialias=True)\n\n            # for batch\n            for mask_cls_result, \\\n                    mask_pred_result, \\\n                    img_metas, \\\n                    data_samples in zip(\n                                mask_cls_results,\n                                mask_pred_results,\n                                batch_img_metas,\n                                batch_data_samples):\n                height = img_metas['ori_shape'][0]\n                width = img_metas['ori_shape'][1]\n                image_size = img_metas['img_shape'][:2]\n                mask_pred_result = retry_if_cuda_oom(sem_seg_postprocess)(\n                    mask_pred_result, image_size, height, width)\n                mask_cls_result = mask_cls_result.to(mask_pred_result)\n\n                if self.task == 'semseg':\n                    pred_sem_seg = retry_if_cuda_oom(self._semantic_inference)(\n                        mask_cls_result, mask_pred_result, all_text_prompts)\n                    data_samples.pred_sem_seg = pred_sem_seg\n                elif self.task == 'instance':\n                    pred_instances = retry_if_cuda_oom(\n                        self._instance_inference)(mask_cls_result,\n                                                  mask_pred_result,\n                                                  all_text_prompts)\n                    data_samples.pred_instances = pred_instances\n                elif self.task == 'panoptic':\n                    pred_panoptic_seg = retry_if_cuda_oom(\n                        self._panoptic_inference)(mask_cls_result,\n                                                  mask_pred_result,\n                                                  all_text_prompts,\n                                                  num_thing_class)\n                    data_samples.pred_panoptic_seg = pred_panoptic_seg\n        elif self.task == 'ref-seg':\n            mask_pred_results = predictions['pred_masks']\n            mask_cls_results = predictions['pred_logits']\n            results_ = zip(mask_pred_results, mask_cls_results,\n                           batch_img_metas, batch_data_samples)\n            for mask_pred_result, mask_cls_result, \\\n                    img_metas, data_samples in results_:\n                if is_lower_torch_version():\n                    mask_pred_result = F.interpolate(\n                        mask_pred_result[None],\n                        size=(batch_input_shape[-2], batch_input_shape[-1]),\n                        mode='bicubic',\n                        align_corners=False)[0]\n                else:\n                    mask_pred_result = F.interpolate(\n                        mask_pred_result[None],\n                        size=(batch_input_shape[-2], batch_input_shape[-1]),\n                        mode='bicubic',\n                        align_corners=False,\n                        antialias=True)[0]\n\n                if self.return_inter_mask:\n                    mask = mask_pred_result > 0\n                    pred_instances = InstanceData()\n                    pred_instances.masks = mask\n                    pred_instances.scores = mask_cls_result\n                    data_samples.pred_instances = pred_instances\n                    continue\n\n                height = img_metas['ori_shape'][0]\n                width = img_metas['ori_shape'][1]\n                image_size = img_metas['img_shape'][:2]\n                mask_pred_result = retry_if_cuda_oom(sem_seg_postprocess)(\n                    mask_pred_result, image_size, height, width)\n\n                pred_instances = retry_if_cuda_oom(self._instance_inference)(\n                    mask_cls_result, mask_pred_result, all_text_prompts)\n                data_samples.pred_instances = pred_instances\n        elif self.task == 'retrieval':\n            batch_data_samples[0].pred_score = predictions['pred_logits']\n        return batch_data_samples\n\n    def _instance_inference(self, mask_cls, mask_pred, text_prompts):\n        num_class = len(text_prompts)\n\n        if self.task in ['ref-seg', 'caption']:\n            scores = F.softmax(mask_cls, dim=-1)\n            scores_per_image = scores.max(dim=-1)[0]\n            labels_per_image = torch.arange(num_class)\n        else:\n            scores = F.softmax(mask_cls, dim=-1)[:, :-1]\n\n            labels = torch.arange(\n                num_class,\n                device=scores.device).unsqueeze(0).repeat(scores.shape[0],\n                                                          1).flatten(0, 1)\n            scores_per_image, topk_indices = scores.flatten(0, 1).topk(\n                self.test_cfg.get('max_per_img', 100), sorted=False)\n\n            labels_per_image = labels[topk_indices]\n            topk_indices = (topk_indices // num_class)\n            mask_pred = mask_pred[topk_indices]\n\n        result = InstanceData()\n        mask_pred = mask_pred.sigmoid()\n        result.masks = (mask_pred > self.test_cfg.mask_thr).float()\n\n        # calculate average mask prob\n        mask_scores_per_image = (mask_pred.flatten(1) *\n                                 result.masks.flatten(1)).sum(1) / (\n                                     result.masks.flatten(1).sum(1) + 1e-6)\n        result.scores = scores_per_image * mask_scores_per_image\n        result.labels = labels_per_image\n        result.label_names = [\n            text_prompts[label] for label in labels_per_image\n        ]\n        result.bboxes = result.scores.new_zeros(len(result.scores), 4)\n        return result\n\n    def _semantic_inference(self, mask_cls, mask_pred, text_prompts):\n        mask_cls = F.softmax(mask_cls, dim=-1)[..., :-1]\n        mask_pred = mask_pred.sigmoid()\n        sem_seg = torch.einsum('qc,qhw->chw', mask_cls, mask_pred)\n\n        if sem_seg.shape[0] == 1:\n            # 0 is foreground, ignore_index is background\n            sem_seg = (sem_seg.squeeze(0) <= self.test_cfg.mask_thr).int()\n            sem_seg[sem_seg == 1] = self.test_cfg.get('ignore_index', 255)\n        else:\n            # 0 is foreground, ignore_index is background\n            if self.test_cfg.use_thr_for_mc:\n                foreground_flag = sem_seg > self.test_cfg.mask_thr\n                sem_seg = sem_seg.max(0)[1]\n                sem_seg[foreground_flag.sum(0) == 0] = self.test_cfg.get(\n                    'ignore_index', 255)\n            else:\n                sem_seg = sem_seg.max(0)[1]\n        pred_sem_seg = PixelData(\n            sem_seg=sem_seg[None],\n            metainfo={\n                'label_names': text_prompts,\n                'ignore_index': self.test_cfg.get('ignore_index', 255)\n            })\n        return pred_sem_seg\n\n    def _panoptic_inference(self, mask_cls, mask_pred, all_text_prompts,\n                            num_thing_class):\n        scores, labels = F.softmax(mask_cls, dim=-1).max(-1)\n        mask_pred = mask_pred.sigmoid()\n\n        keep = labels.ne(len(all_text_prompts)) & (\n            scores > self.test_cfg.mask_thr)\n        cur_scores = scores[keep]\n        cur_classes = labels[keep]\n        cur_masks = mask_pred[keep]\n        cur_prob_masks = cur_scores.view(-1, 1, 1) * cur_masks\n\n        h, w = cur_masks.shape[-2:]\n        panoptic_seg = torch.full((h, w),\n                                  self.test_cfg.get('ignore_index', 255),\n                                  dtype=torch.int32,\n                                  device=cur_masks.device)\n        instance_id = 1\n\n        if cur_masks.shape[0] > 0:\n            cur_mask_ids = cur_prob_masks.argmax(0)\n            for k in range(cur_classes.shape[0]):\n                pred_class = cur_classes[k].item()\n                isthing = int(pred_class) < num_thing_class\n                mask_area = (cur_mask_ids == k).sum().item()\n                original_area = (cur_masks[k] >= 0.5).sum().item()\n                mask = (cur_mask_ids == k) & (cur_masks[k] >= 0.5)\n\n                if mask_area > 0 and original_area > 0 and mask.sum().item(\n                ) > 0:\n                    if mask_area / original_area < self.test_cfg.overlap_thr:\n                        continue\n                    # merge stuff regions\n                    if not isthing:\n                        panoptic_seg[mask] = int(pred_class)\n                    else:\n                        panoptic_seg[mask] = int(\n                            pred_class) + instance_id * INSTANCE_OFFSET\n                        instance_id += 1\n\n        panoptic_seg = PixelData(\n            sem_seg=panoptic_seg[None],\n            metainfo={\n                'label_names': all_text_prompts,\n                'ignore_index': self.test_cfg.get('ignore_index', 255)\n            })\n        return panoptic_seg\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/utils.py",
    "content": "import logging\nfrom contextlib import contextmanager\nfrom functools import wraps\n\nimport torch\nfrom mmcv.cnn.bricks.wrappers import obsolete_torch_version\nfrom torch.nn import functional as F\n\nTORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2])\n\n\ndef is_lower_torch_version(version=(1, 10)):\n    \"\"\"Check if the pytorch version is lower than \"version.\"\"\"\n    return obsolete_torch_version(TORCH_VERSION, version)\n\n\n@contextmanager\ndef _ignore_torch_cuda_oom():\n    \"\"\"A context which ignores CUDA OOM exception from pytorch.\"\"\"\n    try:\n        yield\n    except RuntimeError as e:\n        if 'CUDA out of memory. ' in str(e):\n            pass\n        else:\n            raise\n\n\ndef retry_if_cuda_oom(func):\n    \"\"\"Makes a function retry itself after encountering pytorch's CUDA OOM\n    error. It will first retry after calling `torch.cuda.empty_cache()`.\n\n    If that still fails, it will then retry by trying to convert inputs\n    to CPUs. In this case, it expects the function to dispatch to CPU\n    implementation. The return values may become CPU tensors as well\n    and it's user's responsibility to convert it back to CUDA tensor\n    if needed.\n\n    Args:\n        func: a stateless callable that takes tensor-like objects as arguments\n\n    Returns:\n        a callable which retries `func` if OOM is encountered.\n\n    Examples:\n    ::\n        output = retry_if_cuda_oom(some_torch_function)(input1, input2)\n        # output may be on CPU even if inputs are on GPU\n\n    Note:\n        1. When converting inputs to CPU, it will only\n           look at each argument and check if it has `.device`\n           and `.to` for conversion. Nested structures of tensors\n           are not supported.\n\n        2. Since the function might be called more than once, it has to be\n           stateless.\n    \"\"\"\n\n    def maybe_to_cpu(x):\n        try:\n            like_gpu_tensor = x.device.type == 'cuda' and hasattr(x, 'to')\n        except AttributeError:\n            like_gpu_tensor = False\n        if like_gpu_tensor:\n            return x.to(device='cpu')\n        else:\n            return x\n\n    @wraps(func)\n    def wrapped(*args, **kwargs):\n        with _ignore_torch_cuda_oom():\n            return func(*args, **kwargs)\n\n        # Clear cache and retry\n        torch.cuda.empty_cache()\n        with _ignore_torch_cuda_oom():\n            return func(*args, **kwargs)\n\n        # Try on CPU. This slows down the code significantly,\n        # therefore print a notice.\n        logger = logging.getLogger(__name__)\n        logger.info(\n            'Attempting to copy inputs of {} to CPU due to CUDA OOM'.format(\n                str(func)[0:5]))\n        new_args = (maybe_to_cpu(x) for x in args)\n        new_kwargs = {k: maybe_to_cpu(v) for k, v in kwargs.items()}\n        return func(*new_args, **new_kwargs)\n\n    return wrapped\n\n\ndef sem_seg_postprocess(result, img_size, output_height, output_width):\n    \"\"\"Return semantic segmentation predictions in the original resolution.\n\n    The input images are often resized when entering semantic segmentor.\n    Moreover, in same cases, they also padded inside segmentor to be\n    divisible by maximum network stride. As a result, we often need\n    the predictions of the segmentor in a different resolution from\n    its inputs.\n\n    Args:\n        result (Tensor): semantic segmentation prediction logits.\n            A tensor of shape (C, H, W), where C is the number of classes,\n            and H, W are the height and width of the prediction.\n        img_size (tuple): image size that segmentor is taking as input.\n        output_height, output_width: the desired output resolution.\n\n    Returns:\n        semantic segmentation prediction (Tensor): A tensor of the shape\n            (C, output_height, output_width) that contains per-pixel\n            soft predictions.\n    \"\"\"\n    result = result[:, :img_size[0], :img_size[1]].expand(1, -1, -1, -1)\n    if is_lower_torch_version():\n        result = F.interpolate(\n            result,\n            size=(output_height, output_width),\n            mode='bicubic',\n            align_corners=False)[0]\n    else:\n        result = F.interpolate(\n            result,\n            size=(output_height, output_width),\n            mode='bicubic',\n            align_corners=False,\n            antialias=True)[0]\n    return result\n\n\ndef get_prompt_templates():\n    prompt_templates = [\n        '{}.',\n        'a photo of a {}.',\n        'a bad photo of a {}.',\n        'a photo of many {}.',\n        'a sculpture of a {}.',\n        'a photo of the hard to see {}.',\n        'a low resolution photo of the {}.',\n        'a rendering of a {}.',\n        'graffiti of a {}.',\n        'a bad photo of the {}.',\n        'a cropped photo of the {}.',\n        'a tattoo of a {}.',\n        'the embroidered {}.',\n        'a photo of a hard to see {}.',\n        'a bright photo of a {}.',\n        'a photo of a clean {}.',\n        'a photo of a dirty {}.',\n        'a dark photo of the {}.',\n        'a drawing of a {}.',\n        'a photo of my {}.',\n        'the plastic {}.',\n        'a photo of the cool {}.',\n        'a close-up photo of a {}.',\n        'a black and white photo of the {}.',\n        'a painting of the {}.',\n        'a painting of a {}.',\n        'a pixelated photo of the {}.',\n        'a sculpture of the {}.',\n        'a bright photo of the {}.',\n        'a cropped photo of a {}.',\n        'a plastic {}.',\n        'a photo of the dirty {}.',\n        'a jpeg corrupted photo of a {}.',\n        'a blurry photo of the {}.',\n        'a photo of the {}.',\n        'a good photo of the {}.',\n        'a rendering of the {}.',\n        'a {} in a video game.',\n        'a photo of one {}.',\n        'a doodle of a {}.',\n        'a close-up photo of the {}.',\n        'the origami {}.',\n        'the {} in a video game.',\n        'a sketch of a {}.',\n        'a doodle of the {}.',\n        'a origami {}.',\n        'a low resolution photo of a {}.',\n        'the toy {}.',\n        'a rendition of the {}.',\n        'a photo of the clean {}.',\n        'a photo of a large {}.',\n        'a rendition of a {}.',\n        'a photo of a nice {}.',\n        'a photo of a weird {}.',\n        'a blurry photo of a {}.',\n        'a cartoon {}.',\n        'art of a {}.',\n        'a sketch of the {}.',\n        'a embroidered {}.',\n        'a pixelated photo of a {}.',\n        'itap of the {}.',\n        'a jpeg corrupted photo of the {}.',\n        'a good photo of a {}.',\n        'a plushie {}.',\n        'a photo of the nice {}.',\n        'a photo of the small {}.',\n        'a photo of the weird {}.',\n        'the cartoon {}.',\n        'art of the {}.',\n        'a drawing of the {}.',\n        'a photo of the large {}.',\n        'a black and white photo of a {}.',\n        'the plushie {}.',\n        'a dark photo of a {}.',\n        'itap of a {}.',\n        'graffiti of the {}.',\n        'a toy {}.',\n        'itap of my {}.',\n        'a photo of a cool {}.',\n        'a photo of a small {}.',\n        'a tattoo of the {}.',\n    ]\n    return prompt_templates\n"
  },
  {
    "path": "projects/XDecoder/xdecoder/xdecoder.py",
    "content": "from torch import Tensor\n\nfrom mmdet.models.detectors.single_stage import SingleStageDetector\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import SampleList\nfrom mmdet.utils import ConfigType, OptConfigType, OptMultiConfig\n\n\n@MODELS.register_module()\nclass XDecoder(SingleStageDetector):\n\n    def __init__(self,\n                 backbone: ConfigType,\n                 neck: OptConfigType = None,\n                 head: OptConfigType = None,\n                 test_cfg: OptConfigType = None,\n                 data_preprocessor: OptConfigType = None,\n                 init_cfg: OptMultiConfig = None):\n        super(SingleStageDetector, self).__init__(\n            data_preprocessor=data_preprocessor, init_cfg=init_cfg)\n        self.backbone = MODELS.build(backbone)\n        if neck is not None:\n            self.neck = MODELS.build(neck)\n\n        head_ = head.deepcopy()\n        head_.update(test_cfg=test_cfg)\n        self.sem_seg_head = MODELS.build(head_)  # TODO: sem_seg_head -> head\n\n    def predict(self,\n                batch_inputs: Tensor,\n                batch_data_samples: SampleList,\n                rescale: bool = True) -> SampleList:\n        visual_features = self.extract_feat(batch_inputs)\n        outputs = self.sem_seg_head.predict(visual_features,\n                                            batch_data_samples)\n        return outputs\n"
  },
  {
    "path": "projects/example_largemodel/README.md",
    "content": "# Vision Large Model Example\n\nThe project is used to explore how to successfully train relatively large visual models on consumer-level graphics cards.\n\nAlthough the visual model does not have such an exaggerated number of parameters as LLM, even the commonly used models with Swin Large as the backbone need to be trained successfully on A100, which undoubtedly hinders users' exploration and experiments on visual large models. Therefore, this project will explore how to train visual large models on 3090 and even smaller graphics cards with 24G or less memory.\n\nThe project mainly involves training technologies such as `FSDP`, `DeepSpeed` and `ColossalAI` commonly used in large model training.\n\nThe project will be continuously updated and improved. If you have better exploration and suggestions, you are also welcome to submit a PR\n\n## requirements\n\n```text\nmmengine >=0.9.0 # Example 1\ndeepspeed # Example 2\nfairscale # Example 2\n```\n\n## Example 1: Train `dino-5scale_swin-l_fsdp_8xb2-12e_coco.py` with 8 24G 3090 GPUs and FSDP\n\n```bash\ncd mmdetection\n./tools/dist_train.sh projects/example_largemodel/dino-5scale_swin-l_fsdp_8xb2-12e_coco.py 8\n./tools/dist_train.sh projects/example_largemodel/dino-5scale_swin-l_fsdp_8xb2-12e_coco.py 8 --amp\n```\n\n| ID  | AMP | GC of Backbone | GC of Encoder | FSDP | Peak Mem (GB) | Iter Time (s) |\n| :-: | :-: | :------------: | :-----------: | :--: | :-----------: | :-----------: |\n|  1  |     |                |               |      |   49 (A100)   |      0.9      |\n|  2  |  √  |                |               |      |   39 (A100)   |      1.2      |\n|  3  |     |       √        |               |      |   33 (A100)   |      1.1      |\n|  4  |  √  |       √        |               |      |   25 (A100)   |      1.3      |\n|  5  |     |       √        |       √       |      |      18       |      2.2      |\n|  6  |  √  |       √        |       √       |      |      13       |      1.6      |\n|  7  |     |       √        |       √       |  √   |      14       |      2.9      |\n|  8  |  √  |       √        |       √       |  √   |      8.5      |      2.4      |\n\n- AMP: Automatic Mixed Precision\n- GC: Gradient/Activation checkpointing\n- FSDP: ZeRO-3 with Activation Checkpointing ZeRO-3\n- Iter Time: Total training time for one iteration\n\nFrom the above analysis, it can be seen that:\n\n1. By combining FSDP with AMP and GC techniques, the initial 49GB of GPU memory can be reduced to 8.5GB, but it comes at the cost of a 1.7x increase in training time.\n2. In object detection visual models, the largest memory consumption is due to activation values, rather than optimizer states, which is different from LLM. Therefore, users should prefer gradient checkpoints over FSDP.\n3. If gradient checkpoints are not enabled and only FSDP is used, out-of-memory (OOM) errors can still occur, even with more fine-grained parameter splitting strategies.\n4. While AMP can significantly reduce memory usage, some algorithms may experience a decrease in precision when using AMP, whereas FSDP does not exhibit this issue.\n\n## Example 2: Train `dino-5scale_swin-l_deepspeed_8xb2-12e_coco.py` with 8 24G 3090 GPUs and DeepSpeed\n\n```bash\ncd mmdetection\n./tools/dist_train.sh projects/example_largemodel/dino-5scale_swin-l_deepspeed_8xb2-12e_coco.py 8\n```\n\nIt is a pity that this is still a failed case so far, because the gradient will always overflow, resulting in very low accuracy.\n\n| ID  | AMP | GC of Backbone | GC of Encoder | DeepSpeed | Peak Mem (GB) | Iter Time (s) |\n| :-: | :-: | :------------: | :-----------: | :-------: | :-----------: | :-----------: |\n|  1  |     |                |               |           |   49 (A100)   |      0.9      |\n|  2  |  √  |                |               |           |   39 (A100)   |      1.2      |\n|  3  |  √  |       √        |               |           |   25 (A100)   |      1.3      |\n|  4  |  √  |       √        |               |     √     |     10.5      |      1.5      |\n|  5  |  √  |       √        |       √       |           |      13       |      1.6      |\n|  6  |  √  |       √        |       √       |     √     |      5.0      |      1.4      |\n\nFrom the above analysis, it can be seen that:\n\n1. DeepSpeed has greatly improved usability compared to FSDP. Gradient checkpointing can be done using the native torch functionality without the need for custom modifications, and there is no need for the `auto_wrap_policy` parameter that needs to be set by the user.\n2. The DeepSpeed ZeRO series requires the use of FP16 mode and utilizes NVIDIA's Apex package. It uses Apex's AMP O2 mode, which requires code modifications. However, the O2 mode uses a significant amount of FP16 computation, which prevents DINO algorithm from training properly. But this mode can significantly save GPU memory and provides more thorough type conversion compared to torch's official AMP.\n\nFrom the above analysis, it can be concluded that if DeepSpeed can successfully train the DINO model without reduce performance, it will have a significant advantage over FSDP. If you have a deep understanding of DeepSpeed and Apex and are interested in troubleshooting accuracy issues, your feedback or PR is welcome.\n\nAs mentioned earlier, due to the specific nature of Apex AMP O2, the current version of MMDetection cannot train the DINO model. Considering this as a failed case, the modified code has been placed in the [dino_deepspeed branch](https://github.com/hhaAndroid/mmdetection/tree/dino_deepspeed). The corresponding modifications can be seen in this [commit](https://github.com/hhaAndroid/mmdetection/commit/0c825ae38e2cee3d11a20c5c4adf24ee682d0a55). If you are interested, you can pull this branch and experiment with it.\n"
  },
  {
    "path": "projects/example_largemodel/README_zh-CN.md",
    "content": "# 视觉大模型实践案例\n\n本工程用于探索如何在消费级显卡上成功训练相对大的视觉模型。\n\n虽然视觉模型并没有像 LLM 那样有极其夸张的参数量，但是即使常用的以 Swin Large 为 backbone 的模型，都需要在 A100 上才能成功训练，这无疑阻碍了用户在视觉大模型上的探索和实验。因此本工程将探索在 3090 等 24G 甚至更小显存的消级显卡上如何训练视觉大模型。\n\n本工程主要涉及到的训练技术有 `FSDP`、`DeepSpeed` 和 `ColossalAI` 等常用大模型训练技术。\n\n本工程将不断更新完善，如果你有比较好的探索和意见，也非常欢迎提 PR\n\n## 依赖\n\n```text\nmmengine >=0.9.0 # 案例 1\ndeepspeed # 案例 2\nfairscale # 案例 2\n```\n\n## 案例 1： 采用 8 张 24G 3090 显卡结合 FSDP 训练 `dino-5scale_swin-l_fsdp_8xb2-12e_coco.py`\n\n```bash\ncd mmdetection\n./tools/dist_train.sh projects/example_largemodel/dino-5scale_swin-l_fsdp_8xb2-12e_coco.py 8\n./tools/dist_train.sh projects/example_largemodel/dino-5scale_swin-l_fsdp_8xb2-12e_coco.py 8 --amp\n```\n\n| ID  | AMP | GC of Backbone | GC of Encoder | FSDP | Peak Mem (GB) | Iter Time (s) |\n| :-: | :-: | :------------: | :-----------: | :--: | :-----------: | :-----------: |\n|  1  |     |                |               |      |   49 (A100)   |      0.9      |\n|  2  |  √  |                |               |      |   39 (A100)   |      1.2      |\n|  3  |     |       √        |               |      |   33 (A100)   |      1.1      |\n|  4  |  √  |       √        |               |      |   25 (A100)   |      1.3      |\n|  5  |     |       √        |       √       |      |      18       |      2.2      |\n|  6  |  √  |       √        |       √       |      |      13       |      1.6      |\n|  7  |     |       √        |       √       |  √   |      14       |      2.9      |\n|  8  |  √  |       √        |       √       |  √   |      8.5      |      2.4      |\n\n- AMP: 混合精度训练\n- GC: 梯度/激活值检查点\n- FSDP: ZeRO-3 结合梯度检查点\n- Iter Time: 一次迭代训练总时间\n\n从上表可以看出：\n\n1. 采用 FSDP 结合 AMP 和 GC 技术，可以将最初的 49G 显存降低为 8.5G，但是会增加 1.7 倍训练时间\n2. 在目标检测视觉模型中，占据最大显存的是激活值，而不是优化器状态，这和 LLM 不同，因此用户应该首选梯度检查点，而不是 FSDP\n3. 如果不开启梯度检查点，仅开启 FSDP 的话依然会 OOM，即使尝试了更加细致的参数切分策略\n4. 虽然 AMP 可以减少不少显存，但是有些算法使用 AMP 会导致精度下降而 FSDP 不会\n\n## 案例 2： 采用 8 张 24G 3090 显卡结合 DeepSpeed 训练 `dino-5scale_swin-l_deepspeed_8xb2-12e_coco.py`\n\n```bash\ncd mmdetection\n./tools/dist_train.sh projects/example_largemodel/dino-5scale_swin-l_deepspeed_8xb2-12e_coco.py 8\n```\n\n很遗憾，到目前为止这依然是一个失败的案例，因为梯度始终会溢出导致精度很低。\n\n| ID  | AMP | GC of Backbone | GC of Encoder | DeepSpeed | Peak Mem (GB) | Iter Time (s) |\n| :-: | :-: | :------------: | :-----------: | :-------: | :-----------: | :-----------: |\n|  1  |     |                |               |           |   49 (A100)   |      0.9      |\n|  2  |  √  |                |               |           |   39 (A100)   |      1.2      |\n|  3  |  √  |       √        |               |           |   25 (A100)   |      1.3      |\n|  4  |  √  |       √        |               |     √     |     10.5      |      1.5      |\n|  5  |  √  |       √        |       √       |           |      13       |      1.6      |\n|  6  |  √  |       √        |       √       |     √     |      5.0      |      1.4      |\n\n从上表可以看出：\n\n1. DeepSpeed 易用性上相比于 FSDP 有很大提升，因为梯度检查点可以用 torch 原生的而不需要修改特殊定制，同时也没有 `auto_wrap_policy` 这个需要用户自行设置的参数\n2. DeepSpeed ZeRO 系列必须要采用 FP16 模式，其底层是采用了 NVIDIA’s Apex package, 其使用 Apex 的 AMP O2 模式，这导致需要修改代码，并且 O2 模式采用大量 FP16 计算导致 DINO 算法无法正常训练，但是它的这种模式可以显著节省显存，相比于 torch 官方的 AMP，类型转换更加彻底\n\n从上述分析可知，如果 DeepSpeed 能够在不降低性能情况下成功训练 DINO 模型，那么其将比 FSDP 具备比较大的优势。如果您对 DeepSpeed 和 Apex 有比较深入的了解同时有兴趣排查精度问题，欢迎反馈或者提 PR\n\n前面说过由于 Apex AMP O2 的特殊性，目前的 MMDetection 无法训练 DINO 模型，考虑到这是一个失败的案例，因此将修改的代码放在了 https://github.com/hhaAndroid/mmdetection/tree/dino_deepspeed 分支，其对应修改见 [commit](https://github.com/hhaAndroid/mmdetection/commit/0c825ae38e2cee3d11a20c5c4adf24ee682d0a55)。如果您有兴趣尝试，可以拉取该分支进行试验。\n"
  },
  {
    "path": "projects/example_largemodel/__init__.py",
    "content": "from .fsdp_utils import checkpoint_check_fn, layer_auto_wrap_policy\n\n__all__ = ['checkpoint_check_fn', 'layer_auto_wrap_policy']\n"
  },
  {
    "path": "projects/example_largemodel/dino-5scale_swin-l_deepspeed_8xb2-12e_coco.py",
    "content": "from mmengine.config import read_base\n\nwith read_base():\n    from mmdet.configs.dino.dino_5scale_swin_l_8xb2_12e_coco import *  # noqa\n\nmodel.update(dict(encoder=dict(num_cp=6)))  # noqa\n\nrunner_type = 'FlexibleRunner'\nstrategy = dict(\n    type='DeepSpeedStrategy',\n    gradient_clipping=0.1,\n    fp16=dict(\n        enabled=True,\n        fp16_master_weights_and_grads=False,\n        loss_scale=0,\n        loss_scale_window=500,\n        hysteresis=2,\n        min_loss_scale=1,\n        initial_scale_power=15,\n    ),\n    inputs_to_half=['inputs'],\n    zero_optimization=dict(\n        stage=3,\n        allgather_partitions=True,\n        reduce_scatter=True,\n        allgather_bucket_size=50000000,\n        reduce_bucket_size=50000000,\n        overlap_comm=True,\n        contiguous_gradients=True,\n        cpu_offload=False),\n)\n\noptim_wrapper = dict(\n    type='DeepSpeedOptimWrapper',\n    optimizer=dict(\n        type='AdamW',\n        lr=0.0001,  # 0.0002 for DeformDETR\n        weight_decay=0.0001),\n    # clip_grad=dict(max_norm=0.1, norm_type=2),\n    paramwise_cfg=dict(custom_keys={'backbone': dict(lr_mult=0.1)}))\n\n# To debug\ndefault_hooks.update(dict(logger=dict(interval=1)))  # noqa\nlog_processor.update(dict(window_size=1))  # noqa\n"
  },
  {
    "path": "projects/example_largemodel/dino-5scale_swin-l_fsdp_8xb2-12e_coco.py",
    "content": "from mmengine.config import read_base\n\nwith read_base():\n    from mmdet.configs.dino.dino_5scale_swin_l_8xb2_12e_coco import *  # noqa\n\nfrom projects.example_largemodel import (checkpoint_check_fn,\n                                         layer_auto_wrap_policy)\n\n# The checkpoint needs to be controlled by the checkpoint_check_fn.\nmodel.update(dict(backbone=dict(with_cp=False)))  # noqa\n\n# TODO: The new version of configs does not support passing a module list,\n#  so for now, it can only be hard-coded. We will fix this issue in the future.\nrunner_type = 'FlexibleRunner'\nstrategy = dict(\n    type='FSDPStrategy',\n    activation_checkpointing=dict(check_fn=checkpoint_check_fn),\n    model_wrapper=dict(auto_wrap_policy=dict(type=layer_auto_wrap_policy)))\n"
  },
  {
    "path": "projects/example_largemodel/fsdp_utils.py",
    "content": "from typing import Sequence, Union\n\nimport torch.nn as nn\n\nfrom mmdet.models.backbones.swin import SwinBlock\nfrom mmdet.models.layers.transformer.deformable_detr_layers import \\\n    DeformableDetrTransformerEncoderLayer\n\n\n# TODO: The new version of configs does not support passing a module list,\n#  so for now, it can only be hard-coded. We will fix this issue in the future.\ndef layer_auto_wrap_policy(\n    module,\n    recurse: bool,\n    nonwrapped_numel: int,\n    layer_cls: Union[nn.Module, Sequence[nn.Module]] = (\n        SwinBlock, DeformableDetrTransformerEncoderLayer),\n) -> bool:\n    if recurse:\n        # always recurse\n        return True\n    else:\n        # if not recursing, decide whether we should wrap for\n        # the leaf node or reminder\n        return isinstance(module, tuple(layer_cls))\n\n\ndef checkpoint_check_fn(submodule,\n                        layer_cls: Union[nn.Module, Sequence[nn.Module]] = (\n                            SwinBlock, DeformableDetrTransformerEncoderLayer)):\n    return isinstance(submodule, tuple(layer_cls))\n\n\n# non_reentrant_wrapper = partial(\n#     checkpoint_wrapper,\n#     offload_to_cpu=False,\n#     checkpoint_impl=CheckpointImpl.NO_REENTRANT,\n# )\n"
  },
  {
    "path": "projects/example_project/README.md",
    "content": "# Dummy ResNet Wrapper\n\nThis is an example README for community `projects/`. We have provided detailed explanations for each field in the form of html comments, which are visible when you read the source of this README file. If you wish to submit your project to our main repository, then all the fields in this README are mandatory for others to understand what you have achieved in this implementation. For more details, read our [contribution guide](https://mmdetection.readthedocs.io/en/main/notes/contribution_guide.html) or approach us in [Discussions](https://github.com/open-mmlab/mmdetection/discussions).\n\n## Description\n\n<!-- Share any information you would like others to know. For example:\nAuthor: @xxx.\nThis is an implementation of \\[XXX\\]. -->\n\nThis project implements a dummy ResNet wrapper, which literally does nothing new but prints \"hello world\" during initialization.\n\n## Usage\n\n<!-- For a typical model, this section should contain the commands for training and testing. You are also suggested to dump your environment specification to env.yml by `conda env export > env.yml`. -->\n\n### Training commands\n\nIn MMDetection's root directory, run the following command to train the model:\n\n```bash\npython tools/train.py projects/example_project/configs/faster-rcnn_dummy-resnet_fpn_1x_coco.py\n```\n\nFor multi-gpu training, run:\n\n```bash\npython -m torch.distributed.launch --nnodes=1 --node_rank=0 --nproc_per_node=${NUM_GPUS} --master_port=29506 --master_addr=\"127.0.0.1\" tools/train.py projects/example_project/configs/faster-rcnn_dummy-resnet_fpn_1x_coco.py\n```\n\n### Testing commands\n\nIn MMDetection's root directory, run the following command to test the model:\n\n```bash\npython tools/test.py projects/example_project/configs/faster-rcnn_dummy-resnet_fpn_1x_coco.py ${CHECKPOINT_PATH}\n```\n\n## Results\n\n<!-- List the results as usually done in other model's README. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/README.md#results-and-models)\nYou should claim whether this is based on the pre-trained weights, which are converted from the official release; or it's a reproduced result obtained from retraining the model in this project. -->\n\n|                                Method                                 |  Backbone   | Pretrained Model |  Training set  |   Test set   | #epoch | box AP |         Download         |\n| :-------------------------------------------------------------------: | :---------: | :--------------: | :------------: | :----------: | :----: | :----: | :----------------------: |\n| [Faster R-CNN dummy](configs/faster-rcnn_dummy-resnet_fpn_1x_coco.py) | DummyResNet |        -         | COCO2017 Train | COCO2017 Val |   12   | 0.8853 | [model](<>) \\| [log](<>) |\n\n## Citation\n\n<!-- You may remove this section if not applicable. -->\n\n```latex\n@article{Ren_2017,\n   title={Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks},\n   journal={IEEE Transactions on Pattern Analysis and Machine Intelligence},\n   publisher={Institute of Electrical and Electronics Engineers (IEEE)},\n   author={Ren, Shaoqing and He, Kaiming and Girshick, Ross and Sun, Jian},\n   year={2017},\n   month={Jun},\n}\n```\n\n## Checklist\n\n<!-- Here is a checklist illustrating a usual development workflow of a successful project, and also serves as an overview of this project's progress. The PIC (person in charge) or contributors of this project should check all the items that they believe have been finished, which will further be verified by codebase maintainers via a PR.\nOpenMMLab's maintainer will review the code to ensure the project's quality. Reaching the first milestone means that this project suffices the minimum requirement of being merged into 'projects/'. But this project is only eligible to become a part of the core package upon attaining the last milestone.\nNote that keeping this section up-to-date is crucial not only for this project's developers but the entire community, since there might be some other contributors joining this project and deciding their starting point from this list. It also helps maintainers accurately estimate time and effort on further code polishing, if needed.\nA project does not necessarily have to be finished in a single PR, but it's essential for the project to at least reach the first milestone in its very first PR. -->\n\n- [ ] Milestone 1: PR-ready, and acceptable to be one of the `projects/`.\n\n  - [ ] Finish the code\n\n    <!-- The code's design shall follow existing interfaces and convention. For example, each model component should be registered into `mmdet.registry.MODELS` and configurable via a config file. -->\n\n  - [ ] Basic docstrings & proper citation\n\n    <!-- Each major object should contain a docstring, describing its functionality and arguments. If you have adapted the code from other open-source projects, don't forget to cite the source project in docstring and make sure your behavior is not against its license. Typically, we do not accept any code snippet under GPL license. [A Short Guide to Open Source Licenses](https://medium.com/nationwide-technology/a-short-guide-to-open-source-licenses-cf5b1c329edd) -->\n\n  - [ ] Test-time correctness\n\n    <!-- If you are reproducing the result from a paper, make sure your model's inference-time performance matches that in the original paper. The weights usually could be obtained by simply renaming the keys in the official pre-trained weights. This test could be skipped though, if you are able to prove the training-time correctness and check the second milestone. -->\n\n  - [ ] A full README\n\n    <!-- As this template does. -->\n\n- [ ] Milestone 2: Indicates a successful model implementation.\n\n  - [ ] Training-time correctness\n\n    <!-- If you are reproducing the result from a paper, checking this item means that you should have trained your model from scratch based on the original paper's specification and verified that the final result matches the report within a minor error range. -->\n\n- [ ] Milestone 3: Good to be a part of our core package!\n\n  - [ ] Type hints and docstrings\n\n    <!-- Ideally *all* the methods should have [type hints](https://www.pythontutorial.net/python-basics/python-type-hints/) and [docstrings](https://google.github.io/styleguide/pyguide.html#381-docstrings). [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/mmdet/datasets/transforms/transforms.py#L41-L169) -->\n\n  - [ ] Unit tests\n\n    <!-- Unit tests for each module are required. [Example](https://github.com/open-mmlab/mmdetection/blob/5b0d5b40d5c6cfda906db7464ca22cbd4396728a/tests/test_datasets/test_transforms/test_transforms.py#L35-L88) -->\n\n  - [ ] Code polishing\n\n    <!-- Refactor your code according to reviewer's comment. -->\n\n  - [ ] Metafile.yml\n\n    <!-- It will be parsed by MIM and Inferencer. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/metafile.yml) -->\n\n- [ ] Move your modules into the core package following the codebase's file hierarchy structure.\n\n  <!-- In particular, you may have to refactor this README into a standard one. [Example](https://github.com/open-mmlab/mmdetection/blob/main/configs/faster_rcnn/README.md) -->\n\n- [ ] Refactor your modules into the core package following the codebase's file hierarchy structure.\n"
  },
  {
    "path": "projects/example_project/configs/faster-rcnn_dummy-resnet_fpn_1x_coco.py",
    "content": "_base_ = ['../../../configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py']\n\ncustom_imports = dict(imports=['projects.example_project.dummy'])\n\n_base_.model.backbone.type = 'DummyResNet'\n"
  },
  {
    "path": "projects/example_project/dummy/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .dummy_resnet import DummyResNet\n\n__all__ = ['DummyResNet']\n"
  },
  {
    "path": "projects/example_project/dummy/dummy_resnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom mmdet.models.backbones import ResNet\nfrom mmdet.registry import MODELS\n\n\n@MODELS.register_module()\nclass DummyResNet(ResNet):\n    \"\"\"Implements a dummy ResNet wrapper for demonstration purpose.\n    Args:\n        **kwargs: All the arguments are passed to the parent class.\n    \"\"\"\n\n    def __init__(self, **kwargs) -> None:\n        print('Hello world!')\n        super().__init__(**kwargs)\n"
  },
  {
    "path": "projects/gradio_demo/README.md",
    "content": "# MMDetection Gradio Demo\n\nHere is a gradio demo for MMDetection supported inference tasks.\n\nCurrently supported tasks:\n\n- Object Detection\n- Instance Segmentation\n- Panoptic Segmentation\n- Grounding Object Detection\n- Open Vocabulary Object Detection\n- Open Vocabulary Instance Segmentation\n- Open Vocabulary Semantic Segmentation\n- Open Vocabulary Panoptic Segmentation\n- Referring Expression Segmentation\n- Image Caption\n- Referring Expression Image Caption\n- Text-To-Image Retrieval\n\n## Preview\n\n<img src=\"https://github.com/open-mmlab/mmdetection/assets/17425982/421adfab-98cb-4b65-ab99-15d154cba95f\" width=\"90%\"/>\n\n## Requirements\n\nTo run the demo, you need to install MMDetection at first. And please install with the extra multi-modality\ndependencies to enable multi-modality tasks.\n\n```shell\n# At the MMDetection root folder\npip install -e \".[multimodal]\"\n```\n\nAnd then install the latest gradio package.\n\n```shell\npip install \"gradio>=3.31.0\"\n```\n\n## Start\n\nThen, you can start the gradio server on the local machine by:\n\n```shell\ncd mmdetection\npython projects/gradio_demo/launch.py\n```\n\nThe demo will start a local server `http://127.0.0.1:7860` and you can browse it by your browser.\n"
  },
  {
    "path": "projects/gradio_demo/launch.py",
    "content": "# Modified from MMPretrain\nimport gradio as gr\nimport torch\nfrom mmengine.logging import MMLogger\n\nfrom mmdet.apis import DetInferencer\nfrom projects.XDecoder.xdecoder.inference import (\n    ImageCaptionInferencer, RefImageCaptionInferencer,\n    TextToImageRegionRetrievalInferencer)\n\nlogger = MMLogger('mmdetection', logger_name='mmdet')\nif torch.cuda.is_available():\n    gpus = [\n        torch.device(f'cuda:{i}') for i in range(torch.cuda.device_count())\n    ]\n    logger.info(f'Available GPUs: {len(gpus)}')\nelse:\n    gpus = None\n    logger.info('No available GPU.')\n\n\ndef get_free_device():\n    if gpus is None:\n        return torch.device('cpu')\n    if hasattr(torch.cuda, 'mem_get_info'):\n        free = [torch.cuda.mem_get_info(gpu)[0] for gpu in gpus]\n        select = max(zip(free, range(len(free))))[1]\n    else:\n        import random\n        select = random.randint(0, len(gpus) - 1)\n    return gpus[select]\n\n\nclass ObjectDetectionTab:\n    model_list = [\n        'retinanet_r50-caffe_fpn_1x_coco',\n        'faster-rcnn_r50-caffe_fpn_1x_coco',\n        'dino-5scale_swin-l_8xb2-12e_coco.py',\n    ]\n\n    def __init__(self) -> None:\n        self.create_ui()\n\n    def create_ui(self):\n        with gr.Row():\n            with gr.Column():\n                select_model = gr.Dropdown(\n                    label='Choose a model',\n                    elem_id='od_models',\n                    elem_classes='select_model',\n                    choices=self.model_list,\n                    value=self.model_list[0],\n                )\n            with gr.Column():\n                image_input = gr.Image(\n                    label='Image',\n                    source='upload',\n                    elem_classes='input_image',\n                    type='filepath',\n                    interactive=True,\n                    tool='editor',\n                )\n                output = gr.Image(\n                    label='Result',\n                    source='upload',\n                    interactive=False,\n                    elem_classes='result',\n                )\n                run_button = gr.Button(\n                    'Run',\n                    elem_classes='run_button',\n                )\n                run_button.click(\n                    self.inference,\n                    inputs=[select_model, image_input],\n                    outputs=output,\n                )\n\n        with gr.Row():\n            example_images = gr.Dataset(\n                components=[image_input], samples=[['demo/demo.jpg']])\n            example_images.click(\n                fn=lambda x: gr.Image.update(value=x[0]),\n                inputs=example_images,\n                outputs=image_input)\n\n    def inference(self, model, image):\n        det_inferencer = DetInferencer(\n            model, scope='mmdet', device=get_free_device())\n        results_dict = det_inferencer(image, return_vis=True, no_save_vis=True)\n        vis = results_dict['visualization'][0]\n        return vis\n\n\nclass InstanceSegTab(ObjectDetectionTab):\n    model_list = ['mask-rcnn_r50-caffe_fpn_1x_coco', 'solov2_r50_fpn_1x_coco']\n\n\nclass PanopticSegTab(ObjectDetectionTab):\n    model_list = [\n        'panoptic_fpn_r50_fpn_1x_coco',\n        'mask2former_swin-s-p4-w7-224_8xb2-lsj-50e_coco-panoptic'\n    ]\n\n\nclass OpenVocabObjectDetectionTab:\n    model_list = ['glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365']\n\n    def __init__(self) -> None:\n        self.create_ui()\n\n    def create_ui(self):\n        with gr.Row():\n            with gr.Column():\n                select_model = gr.Dropdown(\n                    label='Choose a model',\n                    elem_id='od_models',\n                    elem_classes='select_model',\n                    choices=self.model_list,\n                    value=self.model_list[0],\n                )\n            with gr.Column():\n                image_input = gr.Image(\n                    label='Image',\n                    source='upload',\n                    elem_classes='input_image',\n                    type='filepath',\n                    interactive=True,\n                    tool='editor',\n                )\n                text_input = gr.Textbox(\n                    label='text prompt',\n                    elem_classes='input_text',\n                    interactive=True,\n                )\n                output = gr.Image(\n                    label='Result',\n                    source='upload',\n                    interactive=False,\n                    elem_classes='result',\n                )\n                run_button = gr.Button(\n                    'Run',\n                    elem_classes='run_button',\n                )\n                run_button.click(\n                    self.inference,\n                    inputs=[select_model, image_input, text_input],\n                    outputs=output,\n                )\n\n        with gr.Row():\n            example_images = gr.Dataset(\n                components=[image_input, text_input],\n                samples=[['demo/demo.jpg', 'bench . car .']])\n            example_images.click(\n                fn=self.update,\n                inputs=example_images,\n                outputs=[image_input, text_input])\n\n    def update(self, example):\n        return gr.Image.update(value=example[0]), gr.Textbox.update(\n            value=example[1])\n\n    def inference(self, model, image, text):\n        det_inferencer = DetInferencer(\n            model, scope='mmdet', device=get_free_device())\n        results_dict = det_inferencer(\n            image,\n            texts=text,\n            custom_entities=True,\n            pred_score_thr=0.5,\n            return_vis=True,\n            no_save_vis=True)\n        vis = results_dict['visualization'][0]\n        return vis\n\n\nclass GroundingDetectionTab(OpenVocabObjectDetectionTab):\n    model_list = ['glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365']\n\n    def create_ui(self):\n        with gr.Row():\n            with gr.Column():\n                select_model = gr.Dropdown(\n                    label='Choose a model',\n                    elem_id='od_models',\n                    elem_classes='select_model',\n                    choices=self.model_list,\n                    value=self.model_list[0],\n                )\n            with gr.Column():\n                image_input = gr.Image(\n                    label='Image',\n                    source='upload',\n                    elem_classes='input_image',\n                    type='filepath',\n                    interactive=True,\n                    tool='editor',\n                )\n                text_input = gr.Textbox(\n                    label='text prompt',\n                    elem_classes='input_text',\n                    interactive=True,\n                )\n                output = gr.Image(\n                    label='Result',\n                    source='upload',\n                    interactive=False,\n                    elem_classes='result',\n                )\n                run_button = gr.Button(\n                    'Run',\n                    elem_classes='run_button',\n                )\n                run_button.click(\n                    self.inference,\n                    inputs=[select_model, image_input, text_input],\n                    outputs=output,\n                )\n\n        with gr.Row():\n            example_images = gr.Dataset(\n                components=[image_input, text_input],\n                samples=[['demo/demo.jpg', 'There are a lot of cars here.']])\n            example_images.click(\n                fn=self.update,\n                inputs=example_images,\n                outputs=[image_input, text_input])\n\n    def inference(self, model, image, text):\n        det_inferencer = DetInferencer(\n            model, scope='mmdet', device=get_free_device())\n        results_dict = det_inferencer(\n            image,\n            texts=text,\n            custom_entities=False,\n            pred_score_thr=0.5,\n            return_vis=True,\n            no_save_vis=True)\n        vis = results_dict['visualization'][0]\n        return vis\n\n\nclass OpenVocabInstanceSegTab(OpenVocabObjectDetectionTab):\n    model_list = ['xdecoder-tiny']\n\n    model_info = {\n        'xdecoder-tiny': {\n            'model':\n            'projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-instance_coco.py',  # noqa\n            'weights':\n            'https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt'  # noqa\n        }\n    }\n\n    def inference(self, model, image, text):\n        det_inferencer = DetInferencer(\n            **self.model_info[model], scope='mmdet', device=get_free_device())\n        results_dict = det_inferencer(\n            image, texts=text, return_vis=True, no_save_vis=True)\n        vis = results_dict['visualization'][0]\n        return vis\n\n\nclass OpenVocabPanopticSegTab(OpenVocabObjectDetectionTab):\n    model_list = ['xdecoder-tiny']\n\n    model_info = {\n        'xdecoder-tiny': {\n            'model':\n            'projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-panoptic_coco.py',  # noqa\n            'weights':\n            'https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt'  # noqa\n        }\n    }\n\n    def create_ui(self):\n        with gr.Row():\n            with gr.Column():\n                select_model = gr.Dropdown(\n                    label='Choose a model',\n                    elem_id='od_models',\n                    elem_classes='select_model',\n                    choices=self.model_list,\n                    value=self.model_list[0],\n                )\n            with gr.Column():\n                image_input = gr.Image(\n                    label='Image',\n                    source='upload',\n                    elem_classes='input_image',\n                    type='filepath',\n                    interactive=True,\n                    tool='editor',\n                )\n                text_input = gr.Textbox(\n                    label='thing text prompt',\n                    elem_classes='input_text_thing',\n                    interactive=True,\n                )\n                stuff_text_input = gr.Textbox(\n                    label='stuff text prompt',\n                    elem_classes='input_text_stuff',\n                    interactive=True,\n                )\n                output = gr.Image(\n                    label='Result',\n                    source='upload',\n                    interactive=False,\n                    elem_classes='result',\n                )\n                run_button = gr.Button(\n                    'Run',\n                    elem_classes='run_button',\n                )\n                run_button.click(\n                    self.inference,\n                    inputs=[\n                        select_model, image_input, text_input, stuff_text_input\n                    ],\n                    outputs=output,\n                )\n        with gr.Row():\n            example_images = gr.Dataset(\n                components=[image_input, text_input, stuff_text_input],\n                samples=[['demo/demo.jpg', 'bench.car', 'tree']])\n            example_images.click(\n                fn=self.update,\n                inputs=example_images,\n                outputs=[image_input, text_input, stuff_text_input])\n\n    def update(self, example):\n        return gr.Image.update(value=example[0]), \\\n            gr.Textbox.update(label='thing text prompt', value=example[1]), \\\n            gr.Textbox.update(label='stuff text prompt', value=example[2])\n\n    def inference(self, model, image, text, stuff_text):\n        det_inferencer = DetInferencer(\n            **self.model_info[model], scope='mmdet', device=get_free_device())\n        results_dict = det_inferencer(\n            image,\n            texts=text,\n            stuff_texts=stuff_text,\n            return_vis=True,\n            no_save_vis=True)\n        vis = results_dict['visualization'][0]\n        return vis\n\n\nclass OpenVocabSemSegTab(OpenVocabInstanceSegTab):\n    model_list = ['xdecoder-tiny']\n\n    model_info = {\n        'xdecoder-tiny': {\n            'model':\n            'projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-semseg_coco.py',  # noqa\n            'weights':\n            'https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt'  # noqa\n        }\n    }\n\n\nclass ReferSegTab(OpenVocabInstanceSegTab):\n    model_list = ['xdecoder-tiny']\n\n    model_info = {\n        'xdecoder-tiny': {\n            'model':\n            'projects/XDecoder/configs/xdecoder-tiny_zeroshot_open-vocab-ref-seg_refcocog.py',  # noqa\n            'weights':\n            'https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt'  # noqa\n        }\n    }\n\n\nclass ImageCaptionTab:\n    model_list = ['xdecoder-tiny']\n\n    model_info = {\n        'xdecoder-tiny': {\n            'model':\n            'projects/XDecoder/configs/xdecoder-tiny_zeroshot_caption_coco2014.py',  # noqa\n            'weights':\n            'https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt'  # noqa\n        }\n    }\n\n    def __init__(self) -> None:\n        self.create_ui()\n\n    def create_ui(self):\n        with gr.Row():\n            with gr.Column():\n                select_model = gr.Dropdown(\n                    label='Choose a model',\n                    elem_id='image_caption_models',\n                    elem_classes='select_model',\n                    choices=self.model_list,\n                    value=self.model_list[0],\n                )\n            with gr.Column():\n                image_input = gr.Image(\n                    label='Input',\n                    source='upload',\n                    elem_classes='input_image',\n                    interactive=True,\n                    tool='editor',\n                )\n                caption_output = gr.Textbox(\n                    label='Result',\n                    lines=2,\n                    elem_classes='caption_result',\n                    interactive=False,\n                )\n                run_button = gr.Button(\n                    'Run',\n                    elem_classes='run_button',\n                )\n                run_button.click(\n                    self.inference,\n                    inputs=[select_model, image_input],\n                    outputs=caption_output,\n                )\n\n        with gr.Row():\n            example_images = gr.Dataset(\n                components=[image_input], samples=[['demo/demo.jpg']])\n            example_images.click(\n                fn=lambda x: gr.Image.update(value=x[0]),\n                inputs=example_images,\n                outputs=image_input)\n\n    def inference(self, model, image):\n        ic_inferencer = ImageCaptionInferencer(\n            **self.model_info[model], scope='mmdet', device=get_free_device())\n        results_dict = ic_inferencer(\n            image, return_vis=False, no_save_vis=True, return_datasamples=True)\n        return results_dict['predictions'][0].pred_caption\n\n\nclass ReferImageCaptionTab(OpenVocabInstanceSegTab):\n    model_list = ['xdecoder-tiny']\n\n    model_info = {\n        'xdecoder-tiny': {\n            'model':\n            'projects/XDecoder/configs/xdecoder-tiny_zeroshot_ref-caption.py',  # noqa\n            'weights':\n            'https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt'  # noqa\n        }\n    }\n\n    def create_ui(self):\n        with gr.Row():\n            with gr.Column():\n                select_model = gr.Dropdown(\n                    label='Choose a model',\n                    elem_id='image_caption_models',\n                    elem_classes='select_model',\n                    choices=self.model_list,\n                    value=self.model_list[0],\n                )\n            with gr.Column():\n                image_input = gr.Image(\n                    label='Input',\n                    source='upload',\n                    elem_classes='input_image',\n                    type='filepath',\n                    interactive=True,\n                    tool='editor',\n                )\n                text_input = gr.Textbox(\n                    label='text prompt',\n                    elem_classes='input_text',\n                    interactive=True,\n                )\n                output = gr.Image(\n                    label='Result',\n                    source='upload',\n                    interactive=False,\n                    elem_classes='result',\n                )\n                run_button = gr.Button(\n                    'Run',\n                    elem_classes='run_button',\n                )\n                run_button.click(\n                    self.inference,\n                    inputs=[select_model, image_input, text_input],\n                    outputs=output,\n                )\n\n        with gr.Row():\n            example_images = gr.Dataset(\n                components=[image_input, text_input],\n                samples=[['demo/demo.jpg', 'tree']])\n            example_images.click(\n                fn=self.update,\n                inputs=example_images,\n                outputs=[image_input, text_input])\n\n    def update(self, example):\n        return gr.Image.update(value=example[0]), gr.Textbox.update(\n            value=example[1])\n\n    def inference(self, model, image, text):\n        ric_inferencer = RefImageCaptionInferencer(\n            **self.model_info[model], scope='mmdet', device=get_free_device())\n        results_dict = ric_inferencer(\n            image, texts=text, return_vis=True, no_save_vis=True)\n        vis = results_dict['visualization'][0]\n        return vis\n\n\nclass TextToImageRetrievalTab:\n    model_list = ['xdecoder-tiny']\n\n    model_info = {\n        'xdecoder-tiny': {\n            'model':\n            'projects/XDecoder/configs/xdecoder-tiny_zeroshot_text-image-retrieval.py',  # noqa\n            'weights':\n            'https://download.openmmlab.com/mmdetection/v3.0/xdecoder/xdecoder_focalt_last_novg.pt'  # noqa\n        }\n    }\n\n    def __init__(self) -> None:\n        self.create_ui()\n\n    def create_ui(self):\n        with gr.Row():\n            with gr.Column():\n                select_model = gr.Dropdown(\n                    label='Choose a model',\n                    elem_id='t2i_retri_models',\n                    elem_classes='select_model',\n                    choices=self.model_list,\n                    value=self.model_list[0],\n                )\n            with gr.Column():\n                prototype = gr.File(\n                    file_count='multiple', file_types=['image'])\n                text_input = gr.Textbox(\n                    label='Query',\n                    elem_classes='input_text',\n                    interactive=True,\n                )\n                retri_output = gr.Image(\n                    label='Result',\n                    source='upload',\n                    interactive=False,\n                    elem_classes='result',\n                )\n\n                run_button = gr.Button(\n                    'Run',\n                    elem_classes='run_button',\n                )\n                run_button.click(\n                    self.inference,\n                    inputs=[select_model, prototype, text_input],\n                    outputs=retri_output,\n                )\n\n    def inference(self, model, prototype, text):\n        inputs = [file.name for file in prototype]\n        retri_inferencer = TextToImageRegionRetrievalInferencer(\n            **self.model_info[model], scope='mmdet', device=get_free_device())\n        results_dict = retri_inferencer(\n            inputs, texts=text, return_vis=True, no_save_vis=True)\n        vis = results_dict['visualization'][0]\n        return vis\n\n\nif __name__ == '__main__':\n    title = 'MMDetection Inference Demo'\n\n    DESCRIPTION = '''# <div align=\"center\">MMDetection Inference Demo  </div>\n    <div align=\"center\">\n    <img src=\"https://user-images.githubusercontent.com/45811724/190993591-\n    bd3f1f11-1c30-4b93-b5f4-05c9ff64ff7f.gif\" width=\"50%\"/>\n    </div>\n\n    #### This is an official demo for MMDet. \\n\n\n    - The first time running requires downloading the weights,\n    please wait a moment. \\n\n    - OV is mean Open Vocabulary \\n\n    - Refer Seg is mean Referring Expression Segmentation \\n\n    - In Text-Image Region Retrieval, you need to provide n images and\n    a query text, and the model will predict the most matching image and\n    its corresponding grounding mask.\n    '''\n\n    with gr.Blocks(analytics_enabled=False, title=title) as demo:\n        gr.Markdown(DESCRIPTION)\n        with gr.Tabs():\n            with gr.TabItem('Detection'):\n                ObjectDetectionTab()\n            with gr.TabItem('Instance'):\n                InstanceSegTab()\n            with gr.TabItem('Panoptic'):\n                PanopticSegTab()\n            with gr.TabItem('Grounding Detection'):\n                GroundingDetectionTab()\n            with gr.TabItem('OV Detection'):\n                OpenVocabObjectDetectionTab()\n            with gr.TabItem('OV Instance'):\n                OpenVocabInstanceSegTab()\n            with gr.TabItem('OV Panoptic'):\n                OpenVocabPanopticSegTab()\n            with gr.TabItem('OV SemSeg'):\n                OpenVocabSemSegTab()\n            with gr.TabItem('Refer Seg'):\n                ReferSegTab()\n            with gr.TabItem('Image Caption'):\n                ImageCaptionTab()\n            with gr.TabItem('Refer Caption'):\n                ReferImageCaptionTab()\n            with gr.TabItem('Text-Image Region Retrieval'):\n                TextToImageRetrievalTab()\n    demo.queue().launch(share=True)\n"
  },
  {
    "path": "projects/iSAID/README.md",
    "content": "# iSAID Dataset\n\n> **iSAID**: A Large-scale Dataset for Instance Segmentation in Aerial Images\n\n## Introduction\n\nExisting Earth Vision datasets are either suitable for semantic segmentation or object detection. iSAID is the first benchmark dataset for instance segmentation in aerial images. This large-scale and densely annotated dataset contains 655,451 object instances for 15 categories across 2,806 high-resolution images. The distinctive characteristics of iSAID are the following: (a) large number of images with high spatial resolution, (b) fifteen important and commonly occurring categories, (c) large number of instances per category, (d) large count of labelled instances per image, which might help in learning contextual information, (e) huge object scale variation, containing small, medium and large objects, often within the same image, (f) Imbalanced and uneven distribution of objects with varying orientation within images, depicting real-life aerial conditions, (g) several small size objects, with ambiguous appearance, can only be resolved with contextual reasoning, (h) precise instance-level annotations carried out by professional annotators, cross-checked and validated by expert annotators complying with well-defined guidelines.\n\nFor more detail, please refer to our [paper](http://openaccess.thecvf.com/content_CVPRW_2019/papers/DOAI/Zamir_iSAID_A_Large-scale_Dataset_for_Instance_Segmentation_in_Aerial_Images_CVPRW_2019_paper.pdf) .\n\n## Prepare\n\niSAID download link：[Image](https://captain-whu.github.io/DOTA/dataset.html)、[Annotation](https://captain-whu.github.io/iSAID/dataset.html)\nPlease follow the steps as described in the [official repository](https://github.com/CAPTAIN-WHU/iSAID_Devkit) to preprocess the data (`patch_width`=800,`patch_height`=800,`overlap_area`=200). The final folder format should be as follows.\n\n```\niSAID_patches\n├── test\n│   └── images\n│       ├── P0006_0_0_800_800.png\n│       └── ...\n│       └── P0009_0_0_800_800.png\n├── train\n│   └── instance_only_filtered_train.json\n│   └── images\n│       ├── P0002_0_0_800_800_instance_color_RGB.png\n│       ├── P0002_0_0_800_800_instance_id_RGB.png\n│       ├── P0002_0_800_800.png\n│       ├── ...\n│       ├── P0010_0_0_800_800_instance_color_RGB.png\n│       ├── P0010_0_0_800_800_instance_id_RGB.png\n│       └── P0010_0_800_800.png\n└── val\n    └── instance_only_filtered_val.json\n    └── images\n        ├── P0003_0_0_800_800_instance_color_RGB.png\n        ├── P0003_0_0_800_800_instance_id_RGB.png\n        ├── P0003_0_0_800_800.png\n        ├── ...\n        ├── P0004_0_0_800_800_instance_color_RGB.png\n        ├── P0004_0_0_800_800_instance_id_RGB.png\n        └── P0004_0_0_800_800.png\n```\n\nAfter that, use the following command in the mmdetection directory to convert the json file format.\n\n```\npython projects/iSAID/isaid_json.py /path/to/iSAID\n```\n\n## Usage\n\n### Train\n\n```python\npython tools/train.py projects/iSAID/configs/mask_rcnn_r50_fpn_1x_isaid.py\n```\n\n### Test\n\n```python\npython tools/test.py projects/iSAID/configs/mask_rcnn_r50_fpn_1x_isaid.py ${CHECKPOINT_PATH}\n```\n\n## Citation\n\n```\n@inproceedings{waqas2019isaid,\ntitle={iSAID: A Large-scale Dataset for Instance Segmentation in Aerial Images},\nauthor={Waqas Zamir, Syed and Arora, Aditya and Gupta, Akshita and Khan, Salman and Sun, Guolei and Shahbaz Khan, Fahad and Zhu, Fan and Shao, Ling and Xia, Gui-Song and Bai, Xiang},\nbooktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition Workshops},\npages={28--37},\nyear={2019}\n}\n```\n\n```\n@InProceedings{Xia_2018_CVPR,\nauthor = {Xia, Gui-Song and Bai, Xiang and Ding, Jian and Zhu, Zhen and Belongie, Serge and Luo, Jiebo and Datcu, Mihai and Pelillo, Marcello and Zhang, Liangpei},\ntitle = {DOTA: A Large-Scale Dataset for Object Detection in Aerial Images},\nbooktitle = {The IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},\nmonth = {June},\nyear = {2018}\n}\n```\n"
  },
  {
    "path": "projects/iSAID/README_zh-CN.md",
    "content": "# iSAID数据集\n\n> **iSAID**: A Large-scale Dataset for Instance Segmentation in Aerial Images\n\n## 数据集介绍\n\nExisting Earth Vision datasets are either suitable for semantic segmentation or object detection. iSAID is the first benchmark dataset for instance segmentation in aerial images. This large-scale and densely annotated dataset contains 655,451 object instances for 15 categories across 2,806 high-resolution images. The distinctive characteristics of iSAID are the following: (a) large number of images with high spatial resolution, (b) fifteen important and commonly occurring categories, (c) large number of instances per category, (d) large count of labelled instances per image, which might help in learning contextual information, (e) huge object scale variation, containing small, medium and large objects, often within the same image, (f) Imbalanced and uneven distribution of objects with varying orientation within images, depicting real-life aerial conditions, (g) several small size objects, with ambiguous appearance, can only be resolved with contextual reasoning, (h) precise instance-level annotations carried out by professional annotators, cross-checked and validated by expert annotators complying with well-defined guidelines.\n\nFor more detail, please refer to our [paper](http://openaccess.thecvf.com/content_CVPRW_2019/papers/DOAI/Zamir_iSAID_A_Large-scale_Dataset_for_Instance_Segmentation_in_Aerial_Images_CVPRW_2019_paper.pdf) .\n\n## 数据集准备\n\niSAID数据集下载链接：[图像数据](https://captain-whu.github.io/DOTA/dataset.html)、[标注数据](https://captain-whu.github.io/iSAID/dataset.html)\n请按照[官方仓库](https://github.com/CAPTAIN-WHU/iSAID_Devkit)中所述步骤进行数据预处理（`patch_width`=800,`patch_height`=800,`overlap_area`=200），最终得到的文件夹格式为\n\n```\niSAID_patches\n├── test\n│   └── images\n│       ├── P0006_0_0_800_800.png\n│       └── ...\n│       └── P0009_0_0_800_800.png\n├── train\n│   └── instance_only_filtered_train.json\n│   └── images\n│       ├── P0002_0_0_800_800_instance_color_RGB.png\n│       ├── P0002_0_0_800_800_instance_id_RGB.png\n│       ├── P0002_0_800_800.png\n│       ├── ...\n│       ├── P0010_0_0_800_800_instance_color_RGB.png\n│       ├── P0010_0_0_800_800_instance_id_RGB.png\n│       └── P0010_0_800_800.png\n└── val\n    └── instance_only_filtered_val.json\n    └── images\n        ├── P0003_0_0_800_800_instance_color_RGB.png\n        ├── P0003_0_0_800_800_instance_id_RGB.png\n        ├── P0003_0_0_800_800.png\n        ├── ...\n        ├── P0004_0_0_800_800_instance_color_RGB.png\n        ├── P0004_0_0_800_800_instance_id_RGB.png\n        └── P0004_0_0_800_800.png\n```\n\n之后，在mmdetection目录下使用以下命令转换json文件格式\n\n```\npython projects/iSAID/isaid_json.py /path/to/iSAID\n```\n\n## 使用方法\n\n### 训练\n\n```python\npython tools/train.py projects/iSAID/configs/mask_rcnn_r50_fpn_1x_isaid.py\n```\n\n### 测试\n\n```python\npython tools/test.py projects/iSAID/configs/mask_rcnn_r50_fpn_1x_isaid.py ${CHECKPOINT_PATH}\n```\n\n## Citation\n\n```\n@inproceedings{waqas2019isaid,\ntitle={iSAID: A Large-scale Dataset for Instance Segmentation in Aerial Images},\nauthor={Waqas Zamir, Syed and Arora, Aditya and Gupta, Akshita and Khan, Salman and Sun, Guolei and Shahbaz Khan, Fahad and Zhu, Fan and Shao, Ling and Xia, Gui-Song and Bai, Xiang},\nbooktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition Workshops},\npages={28--37},\nyear={2019}\n}\n```\n\n```\n@InProceedings{Xia_2018_CVPR,\nauthor = {Xia, Gui-Song and Bai, Xiang and Ding, Jian and Zhu, Zhen and Belongie, Serge and Luo, Jiebo and Datcu, Mihai and Pelillo, Marcello and Zhang, Liangpei},\ntitle = {DOTA: A Large-Scale Dataset for Object Detection in Aerial Images},\nbooktitle = {The IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},\nmonth = {June},\nyear = {2018}\n}\n```\n"
  },
  {
    "path": "projects/iSAID/configs/mask_rcnn_r50_fpn_1x_isaid.py",
    "content": "_base_ = [\n    '../../../configs/_base_/models/mask-rcnn_r50_fpn.py',\n    '../../../configs/_base_/datasets/isaid_instance.py',\n    '../../../configs/_base_/schedules/schedule_1x.py',\n    '../../../configs/_base_/default_runtime.py'\n]\n"
  },
  {
    "path": "projects/iSAID/isaid_json.py",
    "content": "import argparse\nimport json\nimport os.path as osp\n\n\ndef json_convert(path):\n    with open(path, 'r+') as f:\n        coco_data = json.load(f)\n        coco_data['categories'].append({'id': 0, 'name': 'background'})\n        coco_data['categories'] = sorted(\n            coco_data['categories'], key=lambda x: x['id'])\n        f.seek(0)\n        json.dump(coco_data, f)\n        f.truncate()\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(\n        description='Convert iSAID dataset to mmdetection format')\n    parser.add_argument('dataset_path', help='iSAID folder path')\n\n    args = parser.parse_args()\n    dataset_path = args.dataset_path\n    json_list = ['train', 'val']\n    for dataset_mode in ['train', 'val']:\n        json_file = 'instancesonly_filtered_' + dataset_mode + '.json'\n        json_file_path = osp.join(dataset_path, dataset_mode, json_file)\n        assert osp.exists(json_file_path), f'train is not in {dataset_path}'\n        json_convert(json_file_path)\n"
  },
  {
    "path": "pytest.ini",
    "content": "[pytest]\naddopts = --xdoctest --xdoctest-style=auto\nnorecursedirs = .git ignore build __pycache__ data docker docs .eggs\n\nfilterwarnings= default\n                ignore:.*No cfgstr given in Cacher constructor or call.*:Warning\n                ignore:.*Define the __nice__ method for.*:Warning\n"
  },
  {
    "path": "requirements/albu.txt",
    "content": "albumentations>=0.3.2 --no-binary qudida,albumentations\n"
  },
  {
    "path": "requirements/build.txt",
    "content": "# These must be installed before building mmdetection\ncython\nnumpy\n"
  },
  {
    "path": "requirements/docs.txt",
    "content": "docutils==0.16.0\nmyst-parser\n-e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme\nsphinx==4.0.2\nsphinx-copybutton\nsphinx_markdown_tables\nsphinx_rtd_theme==0.5.2\nurllib3<2.0.0\n"
  },
  {
    "path": "requirements/mminstall.txt",
    "content": "mmcv>=2.0.0rc4,<2.2.0\nmmengine>=0.7.1,<1.0.0\n"
  },
  {
    "path": "requirements/multimodal.txt",
    "content": "fairscale\njsonlines\nnltk\npycocoevalcap\ntransformers\n"
  },
  {
    "path": "requirements/optional.txt",
    "content": "cityscapesscripts\nemoji\nfairscale\nimagecorruptions\nscikit-learn\n"
  },
  {
    "path": "requirements/readthedocs.txt",
    "content": "mmcv>=2.0.0rc4,<2.2.0\nmmengine>=0.7.1,<1.0.0\nscipy\ntorch\ntorchvision\nurllib3<2.0.0\n"
  },
  {
    "path": "requirements/runtime.txt",
    "content": "matplotlib\nnumpy\npycocotools\nscipy\nshapely\nsix\nterminaltables\ntqdm\n"
  },
  {
    "path": "requirements/tests.txt",
    "content": "asynctest\ncityscapesscripts\ncodecov\nflake8\nimagecorruptions\ninstaboostfast\ninterrogate\nisort==4.3.21\n# Note: used for kwarray.group_items, this may be ported to mmcv in the future.\nkwarray\nmemory_profiler\n-e git+https://github.com/open-mmlab/mmtracking@dev-1.x#egg=mmtrack\nnltk\nonnx==1.7.0\nonnxruntime>=1.8.0\nparameterized\nprettytable\nprotobuf<=3.20.1\npsutil\npytest\ntransformers\nubelt\nxdoctest>=0.10.0\nyapf\n"
  },
  {
    "path": "requirements/tracking.txt",
    "content": "mmpretrain\nmotmetrics\nnumpy<1.24.0\nscikit-learn\nseaborn\n"
  },
  {
    "path": "requirements.txt",
    "content": "-r requirements/build.txt\n-r requirements/optional.txt\n-r requirements/runtime.txt\n"
  },
  {
    "path": "setup.cfg",
    "content": "[isort]\nline_length = 79\nmulti_line_output = 0\nextra_standard_library = setuptools\nknown_first_party = mmdet\nknown_third_party = PIL,asynctest,cityscapesscripts,cv2,gather_models,matplotlib,mmcv,mmengine,numpy,onnx,onnxruntime,pycocotools,parameterized,pytest,pytorch_sphinx_theme,requests,scipy,seaborn,six,terminaltables,torch,ts,yaml\nno_lines_before = STDLIB,LOCALFOLDER\ndefault_section = THIRDPARTY\n\n[yapf]\nBASED_ON_STYLE = pep8\nBLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF = true\nSPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN = true\n\n# ignore-words-list needs to be lowercase format. For example, if we want to\n# ignore word \"BA\", then we need to append \"ba\" to ignore-words-list rather\n# than \"BA\"\n[codespell]\nskip = *.ipynb,configs/v3det/category_name_13204_v3det_2023_v1.txt\nquiet-level = 3\nignore-words-list = patten,nd,ty,mot,hist,formating,winn,gool,datas,wan,confids,TOOD,tood,ba,warmup,nam,DOTA,dota,conveyer,singed,comittee,extention,moniter,pres,\n\n[flake8]\nper-file-ignores = mmdet/configs/*: F401,F403,F405\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport os.path as osp\nimport platform\nimport shutil\nimport sys\nimport warnings\nfrom setuptools import find_packages, setup\n\nimport torch\nfrom torch.utils.cpp_extension import (BuildExtension, CppExtension,\n                                       CUDAExtension)\n\n\ndef readme():\n    with open('README.md', encoding='utf-8') as f:\n        content = f.read()\n    return content\n\n\nversion_file = 'mmdet/version.py'\n\n\ndef get_version():\n    with open(version_file, 'r') as f:\n        exec(compile(f.read(), version_file, 'exec'))\n    return locals()['__version__']\n\n\ndef make_cuda_ext(name, module, sources, sources_cuda=[]):\n\n    define_macros = []\n    extra_compile_args = {'cxx': []}\n\n    if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1':\n        define_macros += [('WITH_CUDA', None)]\n        extension = CUDAExtension\n        extra_compile_args['nvcc'] = [\n            '-D__CUDA_NO_HALF_OPERATORS__',\n            '-D__CUDA_NO_HALF_CONVERSIONS__',\n            '-D__CUDA_NO_HALF2_OPERATORS__',\n        ]\n        sources += sources_cuda\n    else:\n        print(f'Compiling {name} without CUDA')\n        extension = CppExtension\n\n    return extension(\n        name=f'{module}.{name}',\n        sources=[os.path.join(*module.split('.'), p) for p in sources],\n        define_macros=define_macros,\n        extra_compile_args=extra_compile_args)\n\n\ndef parse_requirements(fname='requirements.txt', with_version=True):\n    \"\"\"Parse the package dependencies listed in a requirements file but strips\n    specific versioning information.\n\n    Args:\n        fname (str): path to requirements file\n        with_version (bool, default=False): if True include version specs\n\n    Returns:\n        List[str]: list of requirements items\n\n    CommandLine:\n        python -c \"import setup; print(setup.parse_requirements())\"\n    \"\"\"\n    import re\n    import sys\n    from os.path import exists\n    require_fpath = fname\n\n    def parse_line(line):\n        \"\"\"Parse information from a line in a requirements text file.\"\"\"\n        if line.startswith('-r '):\n            # Allow specifying requirements in other files\n            target = line.split(' ')[1]\n            for info in parse_require_file(target):\n                yield info\n        else:\n            info = {'line': line}\n            if line.startswith('-e '):\n                info['package'] = line.split('#egg=')[1]\n            elif '@git+' in line:\n                info['package'] = line\n            else:\n                # Remove versioning from the package\n                pat = '(' + '|'.join(['>=', '==', '>']) + ')'\n                parts = re.split(pat, line, maxsplit=1)\n                parts = [p.strip() for p in parts]\n\n                info['package'] = parts[0]\n                if len(parts) > 1:\n                    op, rest = parts[1:]\n                    if ';' in rest:\n                        # Handle platform specific dependencies\n                        # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies\n                        version, platform_deps = map(str.strip,\n                                                     rest.split(';'))\n                        info['platform_deps'] = platform_deps\n                    else:\n                        version = rest  # NOQA\n                    info['version'] = (op, version)\n            yield info\n\n    def parse_require_file(fpath):\n        with open(fpath, 'r') as f:\n            for line in f.readlines():\n                line = line.strip()\n                if line and not line.startswith('#'):\n                    for info in parse_line(line):\n                        yield info\n\n    def gen_packages_items():\n        if exists(require_fpath):\n            for info in parse_require_file(require_fpath):\n                parts = [info['package']]\n                if with_version and 'version' in info:\n                    parts.extend(info['version'])\n                if not sys.version.startswith('3.4'):\n                    # apparently package_deps are broken in 3.4\n                    platform_deps = info.get('platform_deps')\n                    if platform_deps is not None:\n                        parts.append(';' + platform_deps)\n                item = ''.join(parts)\n                yield item\n\n    packages = list(gen_packages_items())\n    return packages\n\n\ndef add_mim_extension():\n    \"\"\"Add extra files that are required to support MIM into the package.\n\n    These files will be added by creating a symlink to the originals if the\n    package is installed in `editable` mode (e.g. pip install -e .), or by\n    copying from the originals otherwise.\n    \"\"\"\n\n    # parse installment mode\n    if 'develop' in sys.argv:\n        # installed by `pip install -e .`\n        if platform.system() == 'Windows':\n            # set `copy` mode here since symlink fails on Windows.\n            mode = 'copy'\n        else:\n            mode = 'symlink'\n    elif 'sdist' in sys.argv or 'bdist_wheel' in sys.argv:\n        # installed by `pip install .`\n        # or create source distribution by `python setup.py sdist`\n        mode = 'copy'\n    else:\n        return\n\n    filenames = [\n        'tools', 'configs', 'demo', 'model-index.yml', 'dataset-index.yml'\n    ]\n    repo_path = osp.dirname(__file__)\n    mim_path = osp.join(repo_path, 'mmdet', '.mim')\n    os.makedirs(mim_path, exist_ok=True)\n\n    for filename in filenames:\n        if osp.exists(filename):\n            src_path = osp.join(repo_path, filename)\n            tar_path = osp.join(mim_path, filename)\n\n            if osp.isfile(tar_path) or osp.islink(tar_path):\n                os.remove(tar_path)\n            elif osp.isdir(tar_path):\n                shutil.rmtree(tar_path)\n\n            if mode == 'symlink':\n                src_relpath = osp.relpath(src_path, osp.dirname(tar_path))\n                os.symlink(src_relpath, tar_path)\n            elif mode == 'copy':\n                if osp.isfile(src_path):\n                    shutil.copyfile(src_path, tar_path)\n                elif osp.isdir(src_path):\n                    shutil.copytree(src_path, tar_path)\n                else:\n                    warnings.warn(f'Cannot copy file {src_path}.')\n            else:\n                raise ValueError(f'Invalid mode {mode}')\n\n\nif __name__ == '__main__':\n    add_mim_extension()\n    setup(\n        name='mmdet',\n        version=get_version(),\n        description='OpenMMLab Detection Toolbox and Benchmark',\n        long_description=readme(),\n        long_description_content_type='text/markdown',\n        author='MMDetection Contributors',\n        author_email='openmmlab@gmail.com',\n        keywords='computer vision, object detection',\n        url='https://github.com/open-mmlab/mmdetection',\n        packages=find_packages(exclude=('configs', 'tools', 'demo')),\n        include_package_data=True,\n        classifiers=[\n            'Development Status :: 5 - Production/Stable',\n            'License :: OSI Approved :: Apache Software License',\n            'Operating System :: OS Independent',\n            'Programming Language :: Python :: 3',\n            'Programming Language :: Python :: 3.7',\n            'Programming Language :: Python :: 3.8',\n            'Programming Language :: Python :: 3.9',\n        ],\n        license='Apache License 2.0',\n        install_requires=parse_requirements('requirements/runtime.txt'),\n        extras_require={\n            'all': parse_requirements('requirements.txt'),\n            'tests': parse_requirements('requirements/tests.txt'),\n            'build': parse_requirements('requirements/build.txt'),\n            'optional': parse_requirements('requirements/optional.txt'),\n            'mim': parse_requirements('requirements/mminstall.txt'),\n            'tracking': parse_requirements('requirements/tracking.txt'),\n            'multimodal': parse_requirements('requirements/multimodal.txt'),\n        },\n        ext_modules=[],\n        cmdclass={'build_ext': BuildExtension},\n        zip_safe=False)\n"
  },
  {
    "path": "tests/test_apis/test_det_inferencer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport tempfile\nfrom unittest import TestCase, mock\nfrom unittest.mock import Mock, patch\n\nimport mmcv\nimport mmengine\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData\nfrom mmengine.utils import is_list_of\nfrom parameterized import parameterized\n\nfrom mmdet.apis import DetInferencer\nfrom mmdet.evaluation.functional import get_classes\nfrom mmdet.structures import DetDataSample\n\n\nclass TestDetInferencer(TestCase):\n\n    @mock.patch('mmengine.infer.infer._load_checkpoint', return_value=None)\n    def test_init(self, mock):\n        # init from metafile\n        DetInferencer('rtmdet-t')\n        # init from cfg\n        DetInferencer('configs/yolox/yolox_tiny_8xb8-300e_coco.py')\n\n    def assert_predictions_equal(self, preds1, preds2):\n        for pred1, pred2 in zip(preds1, preds2):\n            if 'bboxes' in pred1:\n                self.assertTrue(\n                    np.allclose(pred1['bboxes'], pred2['bboxes'], 0.1))\n            if 'scores' in pred1:\n                self.assertTrue(\n                    np.allclose(pred1['scores'], pred2['scores'], 0.1))\n            if 'labels' in pred1:\n                self.assertTrue(np.allclose(pred1['labels'], pred2['labels']))\n            if 'panoptic_seg_path' in pred1:\n                self.assertTrue(\n                    pred1['panoptic_seg_path'] == pred2['panoptic_seg_path'])\n\n    @parameterized.expand([\n        'rtmdet-t', 'mask-rcnn_r50_fpn_1x_coco', 'panoptic_fpn_r50_fpn_1x_coco'\n    ])\n    def test_call(self, model):\n        # single img\n        img_path = 'tests/data/color.jpg'\n\n        mock_load = Mock(return_value=None)\n        with patch('mmengine.infer.infer._load_checkpoint', mock_load):\n            inferencer = DetInferencer(model)\n\n        # In the case of not loading the pretrained weight, the category\n        # defaults to COCO 80, so it needs to be replaced.\n        if model == 'panoptic_fpn_r50_fpn_1x_coco':\n            inferencer.visualizer.dataset_meta = {\n                'classes': get_classes('coco_panoptic'),\n                'palette': 'random'\n            }\n\n        res_path = inferencer(img_path, return_vis=True)\n        # ndarray\n        img = mmcv.imread(img_path)\n        res_ndarray = inferencer(img, return_vis=True)\n        self.assert_predictions_equal(res_path['predictions'],\n                                      res_ndarray['predictions'])\n        self.assertIn('visualization', res_path)\n        self.assertIn('visualization', res_ndarray)\n\n        # multiple images\n        img_paths = ['tests/data/color.jpg', 'tests/data/gray.jpg']\n        res_path = inferencer(img_paths, return_vis=True)\n        # list of ndarray\n        imgs = [mmcv.imread(p) for p in img_paths]\n        res_ndarray = inferencer(imgs, return_vis=True)\n        self.assert_predictions_equal(res_path['predictions'],\n                                      res_ndarray['predictions'])\n        self.assertIn('visualization', res_path)\n        self.assertIn('visualization', res_ndarray)\n\n        # img dir, test different batch sizes\n        img_dir = 'tests/data/VOCdevkit/VOC2007/JPEGImages/'\n        res_bs1 = inferencer(img_dir, batch_size=1, return_vis=True)\n        res_bs3 = inferencer(img_dir, batch_size=3, return_vis=True)\n        self.assert_predictions_equal(res_bs1['predictions'],\n                                      res_bs3['predictions'])\n\n        # There is a jitter operation when the mask is drawn,\n        # so it cannot be asserted.\n        if model == 'rtmdet-t':\n            for res_bs1_vis, res_bs3_vis in zip(res_bs1['visualization'],\n                                                res_bs3['visualization']):\n                self.assertTrue(np.allclose(res_bs1_vis, res_bs3_vis))\n\n    @parameterized.expand([\n        'rtmdet-t', 'mask-rcnn_r50_fpn_1x_coco', 'panoptic_fpn_r50_fpn_1x_coco'\n    ])\n    def test_visualize(self, model):\n        img_paths = ['tests/data/color.jpg', 'tests/data/gray.jpg']\n\n        mock_load = Mock(return_value=None)\n        with patch('mmengine.infer.infer._load_checkpoint', mock_load):\n            inferencer = DetInferencer(model)\n\n        # In the case of not loading the pretrained weight, the category\n        # defaults to COCO 80, so it needs to be replaced.\n        if model == 'panoptic_fpn_r50_fpn_1x_coco':\n            inferencer.visualizer.dataset_meta = {\n                'classes': get_classes('coco_panoptic'),\n                'palette': 'random'\n            }\n\n        with tempfile.TemporaryDirectory() as tmp_dir:\n            inferencer(img_paths, out_dir=tmp_dir)\n            for img_dir in ['color.jpg', 'gray.jpg']:\n                self.assertTrue(osp.exists(osp.join(tmp_dir, 'vis', img_dir)))\n\n    @parameterized.expand([\n        'rtmdet-t', 'mask-rcnn_r50_fpn_1x_coco', 'panoptic_fpn_r50_fpn_1x_coco'\n    ])\n    def test_postprocess(self, model):\n        # return_datasamples\n        img_path = 'tests/data/color.jpg'\n\n        mock_load = Mock(return_value=None)\n        with patch('mmengine.infer.infer._load_checkpoint', mock_load):\n            inferencer = DetInferencer(model)\n\n        # In the case of not loading the pretrained weight, the category\n        # defaults to COCO 80, so it needs to be replaced.\n        if model == 'panoptic_fpn_r50_fpn_1x_coco':\n            inferencer.visualizer.dataset_meta = {\n                'classes': get_classes('coco_panoptic'),\n                'palette': 'random'\n            }\n\n        res = inferencer(img_path, return_datasamples=True)\n        self.assertTrue(is_list_of(res['predictions'], DetDataSample))\n\n        with tempfile.TemporaryDirectory() as tmp_dir:\n            res = inferencer(img_path, out_dir=tmp_dir, no_save_pred=False)\n            dumped_res = mmengine.load(\n                osp.join(tmp_dir, 'preds', 'color.json'))\n            self.assertEqual(res['predictions'][0], dumped_res)\n\n    @mock.patch('mmengine.infer.infer._load_checkpoint', return_value=None)\n    def test_pred2dict(self, mock):\n        data_sample = DetDataSample()\n        data_sample.pred_instances = InstanceData()\n\n        data_sample.pred_instances.bboxes = np.array([[0, 0, 1, 1]])\n        data_sample.pred_instances.labels = np.array([0])\n        data_sample.pred_instances.scores = torch.FloatTensor([0.9])\n        res = DetInferencer('rtmdet-t').pred2dict(data_sample)\n        self.assertListAlmostEqual(res['bboxes'], [[0, 0, 1, 1]])\n        self.assertListAlmostEqual(res['labels'], [0])\n        self.assertListAlmostEqual(res['scores'], [0.9])\n\n    def assertListAlmostEqual(self, list1, list2, places=7):\n        for i in range(len(list1)):\n            if isinstance(list1[i], list):\n                self.assertListAlmostEqual(list1[i], list2[i], places=places)\n            else:\n                self.assertAlmostEqual(list1[i], list2[i], places=places)\n"
  },
  {
    "path": "tests/test_apis/test_inference.py",
    "content": "import os\nfrom pathlib import Path\n\nimport numpy as np\nimport pytest\nimport torch\n\nfrom mmdet.apis import inference_detector, init_detector\nfrom mmdet.structures import DetDataSample\nfrom mmdet.utils import register_all_modules\n\n# TODO: Waiting to fix multiple call error bug\nregister_all_modules()\n\n\n@pytest.mark.parametrize('config,devices',\n                         [('configs/retinanet/retinanet_r18_fpn_1x_coco.py',\n                           ('cpu', 'cuda'))])\ndef test_init_detector(config, devices):\n    assert all([device in ['cpu', 'cuda'] for device in devices])\n\n    project_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))\n    project_dir = os.path.join(project_dir, '..')\n\n    config_file = os.path.join(project_dir, config)\n\n    # test init_detector with config_file: str and cfg_options\n    cfg_options = dict(\n        model=dict(\n            backbone=dict(\n                depth=18,\n                init_cfg=dict(\n                    type='Pretrained', checkpoint='torchvision://resnet18'))))\n\n    for device in devices:\n        if device == 'cuda' and not torch.cuda.is_available():\n            pytest.skip('test requires GPU and torch+cuda')\n\n        model = init_detector(\n            config_file, device=device, cfg_options=cfg_options)\n\n        # test init_detector with :obj:`Path`\n        config_path_object = Path(config_file)\n        model = init_detector(config_path_object, device=device)\n\n        # test init_detector with undesirable type\n        with pytest.raises(TypeError):\n            config_list = [config_file]\n            model = init_detector(config_list)  # noqa: F841\n\n\n@pytest.mark.parametrize('config,devices',\n                         [('configs/retinanet/retinanet_r18_fpn_1x_coco.py',\n                           ('cpu', 'cuda'))])\ndef test_inference_detector(config, devices):\n    assert all([device in ['cpu', 'cuda'] for device in devices])\n\n    project_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))\n    project_dir = os.path.join(project_dir, '..')\n\n    config_file = os.path.join(project_dir, config)\n\n    # test init_detector with config_file: str and cfg_options\n    rng = np.random.RandomState(0)\n    img1 = rng.randint(0, 255, (32, 32, 3), dtype=np.uint8)\n    img2 = rng.randint(0, 255, (32, 32, 3), dtype=np.uint8)\n\n    for device in devices:\n        if device == 'cuda' and not torch.cuda.is_available():\n            pytest.skip('test requires GPU and torch+cuda')\n\n        model = init_detector(config_file, device=device)\n        result = inference_detector(model, img1)\n        assert isinstance(result, DetDataSample)\n        result = inference_detector(model, [img1, img2])\n        assert isinstance(result, list) and len(result) == 2\n"
  },
  {
    "path": "tests/test_datasets/test_cityscapes.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport unittest\n\nfrom mmengine.fileio import dump\n\nfrom mmdet.datasets import CityscapesDataset\n\n\nclass TestCityscapesDataset(unittest.TestCase):\n\n    def setUp(self) -> None:\n        image1 = {\n            'file_name': 'munster/munster_000102_000019_leftImg8bit.png',\n            'height': 1024,\n            'width': 2048,\n            'segm_file': 'munster/munster_000102_000019_gtFine_labelIds.png',\n            'id': 0\n        }\n        image2 = {\n            'file_name': 'munster/munster_000157_000019_leftImg8bit.png',\n            'height': 1024,\n            'width': 2048,\n            'segm_file': 'munster/munster_000157_000019_gtFine_labelIds.png',\n            'id': 1\n        }\n        image3 = {\n            'file_name': 'munster/munster_000139_000019_leftImg8bit.png',\n            'height': 1024,\n            'width': 2048,\n            'segm_file': 'munster/munster_000139_000019_gtFine_labelIds.png',\n            'id': 2\n        }\n        image4 = {\n            'file_name': 'munster/munster_000034_000019_leftImg8bit.png',\n            'height': 31,\n            'width': 15,\n            'segm_file': 'munster/munster_000034_000019_gtFine_labelIds.png',\n            'id': 3\n        }\n\n        images = [image1, image2, image3, image4]\n\n        categories = [{\n            'id': 24,\n            'name': 'person'\n        }, {\n            'id': 25,\n            'name': 'rider'\n        }, {\n            'id': 26,\n            'name': 'car'\n        }]\n\n        annotations = [\n            {\n                'iscrowd': 0,\n                'category_id': 24,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': 2595,\n                'segmentation': {\n                    'size': [1024, 2048],\n                    'counts': 'xxx'\n                },\n                'image_id': 0,\n                'id': 0\n            },\n            {\n                'iscrowd': 0,\n                'category_id': 25,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': -1,\n                'segmentation': {\n                    'size': [1024, 2048],\n                    'counts': 'xxx'\n                },\n                'image_id': 0,\n                'id': 1\n            },\n            {\n                'iscrowd': 0,\n                'category_id': 26,\n                'bbox': [379.0, 435.0, -1, 124.0],\n                'area': 2,\n                'segmentation': {\n                    'size': [1024, 2048],\n                    'counts': 'xxx'\n                },\n                'image_id': 0,\n                'id': 2\n            },\n            {\n                'iscrowd': 0,\n                'category_id': 24,\n                'bbox': [379.0, 435.0, 52.0, -1],\n                'area': 2,\n                'segmentation': {\n                    'size': [1024, 2048],\n                    'counts': 'xxx'\n                },\n                'image_id': 0,\n                'id': 3\n            },\n            {\n                'iscrowd': 0,\n                'category_id': 1,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': 2595,\n                'segmentation': {\n                    'size': [1024, 2048],\n                    'counts': 'xxx'\n                },\n                'image_id': 0,\n                'id': 4\n            },\n            {\n                'iscrowd': 1,\n                'category_id': 26,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': 2595,\n                'segmentation': {\n                    'size': [1024, 2048],\n                    'counts': 'xxx'\n                },\n                'image_id': 1,\n                'id': 5\n            },\n            {\n                'iscrowd': 0,\n                'category_id': 26,\n                'bbox': [379.0, 435.0, 10, 2],\n                'area': 2595,\n                'segmentation': {\n                    'size': [1024, 2048],\n                    'counts': 'xxx'\n                },\n                'image_id': 3,\n                'id': 6\n            },\n        ]\n        fake_json = {\n            'images': images,\n            'annotations': annotations,\n            'categories': categories\n        }\n        self.json_name = 'cityscapes.json'\n        dump(fake_json, self.json_name)\n\n        self.metainfo = dict(classes=('person', 'rider', 'car'))\n\n    def tearDown(self):\n        os.remove(self.json_name)\n\n    def test_cityscapes_dataset(self):\n        dataset = CityscapesDataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 1)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n        dataset = CityscapesDataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            test_mode=True,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n    def test_cityscapes_dataset_without_filter_cfg(self):\n        dataset = CityscapesDataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            filter_cfg=None,\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n        dataset = CityscapesDataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            test_mode=True,\n            filter_cfg=None,\n            pipeline=[])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n"
  },
  {
    "path": "tests/test_datasets/test_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nfrom mmdet.datasets import CocoDataset\n\n\nclass TestCocoDataset(unittest.TestCase):\n\n    def test_coco_dataset(self):\n        # test CocoDataset\n        metainfo = dict(classes=('bus', 'car'), task_name='new_task')\n        dataset = CocoDataset(\n            data_prefix=dict(img='imgs'),\n            ann_file='tests/data/coco_sample.json',\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[],\n            serialize_data=False,\n            lazy_init=False)\n        self.assertEqual(dataset.metainfo['classes'], ('bus', 'car'))\n        self.assertEqual(dataset.metainfo['task_name'], 'new_task')\n        self.assertListEqual(dataset.get_cat_ids(0), [0, 1])\n\n    def test_coco_dataset_without_filter_cfg(self):\n        # test CocoDataset without filter_cfg\n        dataset = CocoDataset(\n            data_prefix=dict(img='imgs'),\n            ann_file='tests/data/coco_sample.json',\n            pipeline=[])\n        self.assertEqual(len(dataset), 4)\n\n        # test with test_mode = True\n        dataset = CocoDataset(\n            data_prefix=dict(img='imgs'),\n            ann_file='tests/data/coco_sample.json',\n            test_mode=True,\n            pipeline=[])\n        self.assertEqual(len(dataset), 4)\n\n    def test_coco_annotation_ids_unique(self):\n        # test annotation ids not unique error\n        metainfo = dict(classes=('car', ), task_name='new_task')\n        with self.assertRaisesRegex(AssertionError, 'are not unique!'):\n            CocoDataset(\n                data_prefix=dict(img='imgs'),\n                ann_file='tests/data/coco_wrong_format_sample.json',\n                metainfo=metainfo,\n                pipeline=[])\n"
  },
  {
    "path": "tests/test_datasets/test_coco_api_wrapper.py",
    "content": "import os.path as osp\nimport tempfile\nimport unittest\n\nfrom mmengine.fileio import dump\n\nfrom mmdet.datasets.api_wrappers import COCOPanoptic\n\n\nclass TestCOCOPanoptic(unittest.TestCase):\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    def test_create_index(self):\n        ann_json = {'test': ['test', 'createIndex']}\n        annotation_file = osp.join(self.tmp_dir.name, 'createIndex.json')\n        dump(ann_json, annotation_file)\n        COCOPanoptic(annotation_file)\n\n    def test_load_anns(self):\n        categories = [{\n            'id': 0,\n            'name': 'person',\n            'supercategory': 'person',\n            'isthing': 1\n        }]\n\n        images = [{\n            'id': 0,\n            'width': 80,\n            'height': 60,\n            'file_name': 'fake_name1.jpg',\n        }]\n\n        annotations = [{\n            'segments_info': [\n                {\n                    'id': 1,\n                    'category_id': 0,\n                    'area': 400,\n                    'bbox': [10, 10, 10, 40],\n                    'iscrowd': 0\n                },\n            ],\n            'file_name':\n            'fake_name1.png',\n            'image_id':\n            0\n        }]\n\n        ann_json = {\n            'images': images,\n            'annotations': annotations,\n            'categories': categories,\n        }\n\n        annotation_file = osp.join(self.tmp_dir.name, 'load_anns.json')\n        dump(ann_json, annotation_file)\n\n        api = COCOPanoptic(annotation_file)\n        api.load_anns(1)\n\n        self.assertIsNone(api.load_anns(0.1))\n"
  },
  {
    "path": "tests/test_datasets/test_coco_panoptic.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport unittest\n\nfrom mmengine.fileio import dump\n\nfrom mmdet.datasets import CocoPanopticDataset\n\n\nclass TestCocoPanopticDataset(unittest.TestCase):\n\n    def setUp(self):\n        image1 = {\n            'id': 0,\n            'width': 640,\n            'height': 640,\n            'file_name': 'fake_name1.jpg',\n        }\n\n        image2 = {\n            'id': 1,\n            'width': 640,\n            'height': 800,\n            'file_name': 'fake_name2.jpg',\n        }\n\n        image3 = {\n            'id': 2,\n            'width': 31,\n            'height': 40,\n            'file_name': 'fake_name3.jpg',\n        }\n\n        image4 = {\n            'id': 3,\n            'width': 400,\n            'height': 400,\n            'file_name': 'fake_name4.jpg',\n        }\n        images = [image1, image2, image3, image4]\n\n        annotations = [\n            {\n                'segments_info': [{\n                    'id': 1,\n                    'category_id': 0,\n                    'area': 400,\n                    'bbox': [50, 60, 20, 20],\n                    'iscrowd': 0\n                }, {\n                    'id': 2,\n                    'category_id': 1,\n                    'area': 900,\n                    'bbox': [100, 120, 30, 30],\n                    'iscrowd': 0\n                }, {\n                    'id': 3,\n                    'category_id': 2,\n                    'iscrowd': 0,\n                    'bbox': [1, 189, 612, 285],\n                    'area': 70036\n                }],\n                'file_name':\n                'fake_name1.jpg',\n                'image_id':\n                0\n            },\n            {\n                'segments_info': [\n                    {\n                        # Different to instance style json, there\n                        # are duplicate ids in panoptic style json\n                        'id': 1,\n                        'category_id': 0,\n                        'area': 400,\n                        'bbox': [50, 60, 20, 20],\n                        'iscrowd': 0\n                    },\n                    {\n                        'id': 4,\n                        'category_id': 1,\n                        'area': 900,\n                        'bbox': [100, 120, 30, 30],\n                        'iscrowd': 1\n                    },\n                    {\n                        'id': 5,\n                        'category_id': 2,\n                        'iscrowd': 0,\n                        'bbox': [100, 200, 200, 300],\n                        'area': 66666\n                    },\n                    {\n                        'id': 6,\n                        'category_id': 0,\n                        'iscrowd': 0,\n                        'bbox': [1, 189, -10, 285],\n                        'area': -2\n                    },\n                    {\n                        'id': 10,\n                        'category_id': 0,\n                        'iscrowd': 0,\n                        'bbox': [1, 189, 10, -285],\n                        'area': 100\n                    }\n                ],\n                'file_name':\n                'fake_name2.jpg',\n                'image_id':\n                1\n            },\n            {\n                'segments_info': [{\n                    'id': 7,\n                    'category_id': 0,\n                    'area': 25,\n                    'bbox': [0, 0, 5, 5],\n                    'iscrowd': 0\n                }],\n                'file_name':\n                'fake_name3.jpg',\n                'image_id':\n                2\n            },\n            {\n                'segments_info': [{\n                    'id': 8,\n                    'category_id': 0,\n                    'area': 25,\n                    'bbox': [0, 0, 400, 400],\n                    'iscrowd': 1\n                }],\n                'file_name':\n                'fake_name4.jpg',\n                'image_id':\n                3\n            }\n        ]\n\n        categories = [{\n            'id': 0,\n            'name': 'car',\n            'supercategory': 'car',\n            'isthing': 1\n        }, {\n            'id': 1,\n            'name': 'person',\n            'supercategory': 'person',\n            'isthing': 1\n        }, {\n            'id': 2,\n            'name': 'wall',\n            'supercategory': 'wall',\n            'isthing': 0\n        }]\n\n        fake_json = {\n            'images': images,\n            'annotations': annotations,\n            'categories': categories\n        }\n        self.json_name = 'coco_panoptic.json'\n        dump(fake_json, self.json_name)\n\n        self.metainfo = dict(\n            classes=('person', 'car', 'wall'),\n            thing_classes=('person', 'car'),\n            stuff_classes=('wall', ))\n\n    def tearDown(self):\n        os.remove(self.json_name)\n\n    def test_coco_panoptic_dataset(self):\n        dataset = CocoPanopticDataset(\n            data_root='./',\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs', seg='seg'),\n            metainfo=self.metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        self.assertEqual(dataset.metainfo['thing_classes'],\n                         self.metainfo['thing_classes'])\n        self.assertEqual(dataset.metainfo['stuff_classes'],\n                         self.metainfo['stuff_classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 2)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n        # test mode\n        dataset = CocoPanopticDataset(\n            data_root='./',\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs', seg='seg'),\n            metainfo=self.metainfo,\n            test_mode=True,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        self.assertEqual(dataset.metainfo['thing_classes'],\n                         self.metainfo['thing_classes'])\n        self.assertEqual(dataset.metainfo['stuff_classes'],\n                         self.metainfo['stuff_classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n    def test_coco_panoptic_dataset_without_filter_cfg(self):\n        dataset = CocoPanopticDataset(\n            data_root='./',\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs', seg='seg'),\n            metainfo=self.metainfo,\n            filter_cfg=None,\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        self.assertEqual(dataset.metainfo['thing_classes'],\n                         self.metainfo['thing_classes'])\n        self.assertEqual(dataset.metainfo['stuff_classes'],\n                         self.metainfo['stuff_classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n        # test mode\n        dataset = CocoPanopticDataset(\n            data_root='./',\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs', seg='seg'),\n            metainfo=self.metainfo,\n            filter_cfg=None,\n            test_mode=True,\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        self.assertEqual(dataset.metainfo['thing_classes'],\n                         self.metainfo['thing_classes'])\n        self.assertEqual(dataset.metainfo['stuff_classes'],\n                         self.metainfo['stuff_classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n"
  },
  {
    "path": "tests/test_datasets/test_crowdhuman.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nfrom mmdet.datasets import CrowdHumanDataset\n\n\nclass TestCrowdHumanDataset(unittest.TestCase):\n\n    def test_crowdhuman_init(self):\n        dataset = CrowdHumanDataset(\n            data_root='tests/data/crowdhuman_dataset/',\n            ann_file='test_annotation_train.odgt',\n            data_prefix=dict(img='Images/'),\n            pipeline=[])\n        self.assertEqual(len(dataset), 1)\n        self.assertEqual(dataset.metainfo['classes'], ('person', ))\n"
  },
  {
    "path": "tests/test_datasets/test_dsdldet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nfrom mmdet.datasets import DSDLDetDataset\n\ntry:\n    from dsdl.dataset import DSDLDataset\nexcept ImportError:\n    DSDLDataset = None\n\n\nclass TestDSDLDetDataset(unittest.TestCase):\n\n    def test_dsdldet_init(self):\n        if DSDLDataset is not None:\n            dataset = DSDLDetDataset(\n                data_root='tests/data/dsdl_det',\n                ann_file='set-train/train.yaml')\n            dataset.full_init()\n\n            self.assertEqual(len(dataset), 2)\n            self.assertEqual(len(dataset[0]['instances']), 4)\n            self.assertEqual(dataset.get_cat_ids(0), [3, 0, 0, 1])\n        else:\n            ImportWarning('Package `dsdl` is not installed.')\n"
  },
  {
    "path": "tests/test_datasets/test_lvis.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport unittest\n\nfrom mmengine.fileio import dump\n\nfrom mmdet.datasets import LVISV1Dataset, LVISV05Dataset\n\ntry:\n    import lvis\nexcept ImportError:\n    lvis = None\n\n\nclass TestLVISDataset(unittest.TestCase):\n\n    def setUp(self) -> None:\n\n        image1 = {\n            # ``coco_url`` for v1 only.\n            'coco_url': 'http://images.cocodataset.org/train2017/0.jpg',\n            # ``file_name`` for v0.5 only.\n            'file_name': '0.jpg',\n            'height': 1024,\n            'width': 2048,\n            'neg_category_ids': [],\n            'not_exhaustive_category_ids': [],\n            'id': 0\n        }\n        image2 = {\n            'coco_url': 'http://images.cocodataset.org/train2017/1.jpg',\n            'file_name': '1.jpg',\n            'height': 1024,\n            'width': 2048,\n            'neg_category_ids': [],\n            'not_exhaustive_category_ids': [],\n            'id': 1\n        }\n        image3 = {\n            'coco_url': 'http://images.cocodataset.org/train2017/2.jpg',\n            'file_name': '2.jpg',\n            'height': 1024,\n            'width': 2048,\n            'neg_category_ids': [],\n            'not_exhaustive_category_ids': [],\n            'id': 2\n        }\n        image4 = {\n            'coco_url': 'http://images.cocodataset.org/train2017/3.jpg',\n            'file_name': '3.jpg',\n            'height': 31,\n            'width': 15,\n            'neg_category_ids': [],\n            'not_exhaustive_category_ids': [],\n            'id': 3\n        }\n\n        images = [image1, image2, image3, image4]\n\n        categories = [{\n            'id': 1,\n            'name': 'aerosol_can',\n            'frequency': 'c',\n            'image_count': 64\n        }, {\n            'id': 2,\n            'name': 'air_conditioner',\n            'frequency': 'f',\n            'image_count': 364\n        }, {\n            'id': 3,\n            'name': 'airplane',\n            'frequency': 'f',\n            'image_count': 1911\n        }]\n\n        annotations = [\n            {\n                'category_id': 1,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': 2595,\n                'segmentation': [[0.0, 0.0]],\n                'image_id': 0,\n                'id': 0\n            },\n            {\n                'category_id': 2,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': -1,\n                'segmentation': [[0.0, 0.0]],\n                'image_id': 0,\n                'id': 1\n            },\n            {\n                'category_id': 3,\n                'bbox': [379.0, 435.0, -1, 124.0],\n                'area': 2,\n                'segmentation': [[0.0, 0.0]],\n                'image_id': 0,\n                'id': 2\n            },\n            {\n                'category_id': 1,\n                'bbox': [379.0, 435.0, 52.0, -1],\n                'area': 2,\n                'segmentation': [[0.0, 0.0]],\n                'image_id': 0,\n                'id': 3\n            },\n            {\n                'category_id': 1,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': 2595,\n                'segmentation': [[0.0, 0.0]],\n                'image_id': 0,\n                'id': 4\n            },\n            {\n                'category_id': 3,\n                'bbox': [379.0, 435.0, 52.0, 124.0],\n                'area': 2595,\n                'segmentation': [[0.0, 0.0]],\n                'image_id': 1,\n                'id': 5\n            },\n            {\n                'category_id': 3,\n                'bbox': [379.0, 435.0, 10, 2],\n                'area': 2595,\n                'segmentation': [[0.0, 0.0]],\n                'image_id': 3,\n                'id': 6\n            },\n        ]\n        fake_json = {\n            'images': images,\n            'annotations': annotations,\n            'categories': categories\n        }\n        self.json_name = 'lvis.json'\n        dump(fake_json, self.json_name)\n\n        self.metainfo = dict(\n            classes=('aerosol_can', 'air_conditioner', 'airplane'))\n\n    def tearDown(self):\n        os.remove(self.json_name)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_lvis05_dataset(self):\n        dataset = LVISV05Dataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 2)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n        dataset = LVISV05Dataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            test_mode=True,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_lvis1_dataset(self):\n        dataset = LVISV1Dataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 2)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n        dataset = LVISV1Dataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            test_mode=True,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_lvis1_dataset_without_filter_cfg(self):\n        dataset = LVISV1Dataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            filter_cfg=None,\n            pipeline=[])\n        self.assertEqual(dataset.metainfo['classes'], self.metainfo['classes'])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n\n        dataset = LVISV1Dataset(\n            ann_file=self.json_name,\n            data_prefix=dict(img='imgs'),\n            metainfo=self.metainfo,\n            test_mode=True,\n            filter_cfg=None,\n            pipeline=[])\n        dataset.full_init()\n        # filter images of small size and images\n        # with all illegal annotations\n        self.assertEqual(len(dataset), 4)\n        self.assertEqual(len(dataset.load_data_list()), 4)\n"
  },
  {
    "path": "tests/test_datasets/test_mot_challenge_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nfrom mmdet.datasets import MOTChallengeDataset\n\n\nclass TestMOTChallengeDataset(unittest.TestCase):\n\n    def test_mot_challenge_dataset(self):\n        # test CocoDataset\n        metainfo = dict(classes=('pedestrian'), task_name='new_task')\n        dataset = MOTChallengeDataset(\n            data_prefix=dict(img_path='imgs'),\n            ann_file='tests/data/mot_sample.json',\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[],\n            serialize_data=False,\n            lazy_init=False)\n        self.assertEqual(dataset.metainfo['classes'], ('pedestrian'))\n        self.assertEqual(dataset.metainfo['task_name'], 'new_task')\n        self.assertListEqual(dataset.get_cat_ids((0, 1)), [0, 0])\n        self.assertListEqual(dataset.get_cat_ids(0), [0, 0, 0, 0, 0, 0])\n        self.assertEqual(len(dataset), 2)\n        self.assertEqual(dataset.num_all_imgs, 5)\n        self.assertEqual(len(dataset[0]['images'][2]['instances']), 2)\n\n    def test_mot_challenge_dataset_with_visibility(self):\n        dataset = MOTChallengeDataset(\n            data_prefix=dict(img_path='imgs'),\n            ann_file='tests/data/mot_sample.json',\n            metainfo=dict(classes=('pedestrian')),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            visibility_thr=0.5,\n            pipeline=[])\n        self.assertEqual(dataset.num_all_imgs, 5)\n        self.assertEqual(len(dataset[0]['images'][2]['instances']), 1)\n"
  },
  {
    "path": "tests/test_datasets/test_objects365.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nfrom mmdet.datasets import Objects365V1Dataset, Objects365V2Dataset\n\n\nclass TestObjects365V1Dataset(unittest.TestCase):\n\n    def test_obj365v1_dataset(self):\n        # test Objects365V1Dataset\n        metainfo = dict(classes=('bus', 'car'), task_name='new_task')\n        dataset = Objects365V1Dataset(\n            data_prefix=dict(img='imgs'),\n            ann_file='tests/data/coco_sample.json',\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[],\n            serialize_data=False,\n            lazy_init=False)\n        self.assertEqual(dataset.metainfo['classes'], ('bus', 'car'))\n        self.assertEqual(dataset.metainfo['task_name'], 'new_task')\n        self.assertListEqual(dataset.get_cat_ids(0), [0, 1])\n        self.assertEqual(dataset.cat_ids, [1, 2])\n\n    def test_obj365v1_with_unsorted_annotation(self):\n        # test Objects365V1Dataset with unsorted annotations\n        metainfo = dict(classes=('bus', 'car'), task_name='new_task')\n        dataset = Objects365V1Dataset(\n            data_prefix=dict(img='imgs'),\n            ann_file='tests/data/Objects365/unsorted_obj365_sample.json',\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[],\n            serialize_data=False,\n            lazy_init=False)\n        self.assertEqual(dataset.metainfo['classes'], ('bus', 'car'))\n        self.assertEqual(dataset.metainfo['task_name'], 'new_task')\n        # sort the unsorted annotations\n        self.assertListEqual(dataset.get_cat_ids(0), [0, 1])\n        self.assertEqual(dataset.cat_ids, [1, 2])\n\n    def test_obj365v1_annotation_ids_unique(self):\n        # test annotation ids not unique error\n        metainfo = dict(classes=('car', ), task_name='new_task')\n        with self.assertRaisesRegex(AssertionError, 'are not unique!'):\n            Objects365V1Dataset(\n                data_prefix=dict(img='imgs'),\n                ann_file='tests/data/coco_wrong_format_sample.json',\n                metainfo=metainfo,\n                pipeline=[])\n\n\nclass TestObjects365V2Dataset(unittest.TestCase):\n\n    def test_obj365v2_dataset(self):\n        # test Objects365V2Dataset\n        metainfo = dict(classes=('bus', 'car'), task_name='new_task')\n        dataset = Objects365V2Dataset(\n            data_prefix=dict(img='imgs'),\n            ann_file='tests/data/coco_sample.json',\n            metainfo=metainfo,\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[],\n            serialize_data=False,\n            lazy_init=False)\n        self.assertEqual(dataset.metainfo['classes'], ('bus', 'car'))\n        self.assertEqual(dataset.metainfo['task_name'], 'new_task')\n        self.assertListEqual(dataset.get_cat_ids(0), [0, 1])\n        self.assertEqual(dataset.cat_ids, [1, 2])\n\n    def test_obj365v1_annotation_ids_unique(self):\n        # test annotation ids not unique error\n        metainfo = dict(classes=('car', ), task_name='new_task')\n        with self.assertRaisesRegex(AssertionError, 'are not unique!'):\n            Objects365V2Dataset(\n                data_prefix=dict(img='imgs'),\n                ann_file='tests/data/coco_wrong_format_sample.json',\n                metainfo=metainfo,\n                pipeline=[])\n"
  },
  {
    "path": "tests/test_datasets/test_openimages.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nfrom mmdet.datasets import OpenImagesChallengeDataset, OpenImagesDataset\n\n\nclass TestOpenImagesDataset(unittest.TestCase):\n\n    def test_init(self):\n        dataset = OpenImagesDataset(\n            data_root='tests/data/OpenImages/',\n            ann_file='annotations/oidv6-train-annotations-bbox.csv',\n            data_prefix=dict(img='OpenImages/train/'),\n            label_file='annotations/class-descriptions-boxable.csv',\n            hierarchy_file='annotations/bbox_labels_600_hierarchy.json',\n            meta_file='annotations/image-metas.pkl',\n            pipeline=[])\n        dataset.full_init()\n        self.assertEqual(len(dataset), 1)\n        self.assertEqual(dataset.metainfo['classes'], ['Airplane'])\n\n\nclass TestOpenImagesChallengeDataset(unittest.TestCase):\n\n    def test_init(self):\n        dataset = OpenImagesChallengeDataset(\n            data_root='tests/data/OpenImages/',\n            ann_file='challenge2019/challenge-2019-train-detection-bbox.txt',\n            data_prefix=dict(img='OpenImages/train/'),\n            label_file='challenge2019/cls-label-description.csv',\n            hierarchy_file='challenge2019/class_label_tree.np',\n            meta_file='annotations/image-metas.pkl',\n            pipeline=[])\n        dataset.full_init()\n        self.assertEqual(len(dataset), 1)\n        self.assertEqual(dataset.metainfo['classes'], ['Airplane'])\n"
  },
  {
    "path": "tests/test_datasets/test_pascal_voc.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nfrom mmdet.datasets import VOCDataset\n\n\nclass TestVOCDataset(unittest.TestCase):\n\n    def test_voc2007_init(self):\n        dataset = VOCDataset(\n            data_root='tests/data/VOCdevkit/',\n            ann_file='VOC2007/ImageSets/Main/trainval.txt',\n            data_prefix=dict(sub_data_root='VOC2007/'),\n            filter_cfg=dict(\n                filter_empty_gt=True, min_size=32, bbox_min_size=32),\n            pipeline=[])\n        dataset.full_init()\n        self.assertEqual(len(dataset), 1)\n\n        data_list = dataset.load_data_list()\n        self.assertEqual(len(data_list), 1)\n        self.assertEqual(len(data_list[0]['instances']), 2)\n        self.assertEqual(dataset.get_cat_ids(0), [11, 14])\n\n    def test_voc2012_init(self):\n        dataset = VOCDataset(\n            data_root='tests/data/VOCdevkit/',\n            ann_file='VOC2012/ImageSets/Main/trainval.txt',\n            data_prefix=dict(sub_data_root='VOC2012/'),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            pipeline=[])\n        dataset.full_init()\n        self.assertEqual(len(dataset), 1)\n\n        data_list = dataset.load_data_list()\n        self.assertEqual(len(data_list), 1)\n        self.assertEqual(len(data_list[0]['instances']), 1)\n        self.assertEqual(dataset.get_cat_ids(0), [18])\n"
  },
  {
    "path": "tests/test_datasets/test_reid_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom unittest import TestCase\n\nfrom mmdet.datasets import ReIDDataset\n\nPREFIX = osp.join(osp.dirname(__file__), '../data')\n# This is a demo annotation file for ReIDDataset.\nREID_ANN_FILE = f'{PREFIX}/demo_reid_data/mot17_reid/ann.txt'\n\n\nclass TestReIDDataset(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        cls.num_ids = 8\n        cls.ins_per_id = 4\n        cls.dataset = ReIDDataset(\n            pipeline=[], ann_file=REID_ANN_FILE, data_prefix=dict(img_path=''))\n        cls.dataset_triplet = ReIDDataset(\n            pipeline=[],\n            triplet_sampler=dict(\n                num_ids=cls.num_ids, ins_per_id=cls.ins_per_id),\n            ann_file=REID_ANN_FILE,\n            data_prefix=dict(img_path=''))\n\n    def test_get_data_info(self):\n        # id 0 has 21 objects\n        img_id = 0\n        data_list = [\n            self.dataset.get_data_info(i) for i in range(len(self.dataset))\n        ]\n        assert len([\n            data_info for data_info in data_list\n            if data_info['gt_label'] == img_id\n        ]) == 21\n        # id 11 doesn't have objects\n        img_id = 11\n        assert len([\n            data_info for data_info in data_list\n            if data_info['gt_label'] == img_id\n        ]) == 0\n\n    def test_len(self):\n        assert len(self.dataset) == 704\n        assert len(self.dataset_triplet) == 704\n\n    def test_getitem(self):\n        for i in range(len(self.dataset)):\n            results = self.dataset[i]\n            assert isinstance(results, dict)  # no triplet -> dict\n            assert 'img_path' in results\n            assert 'gt_label' in results\n        for i in range(len(self.dataset_triplet)):\n            num = self.num_ids * self.ins_per_id\n            results = self.dataset_triplet[i]\n            assert isinstance(results, dict)  # triplet -> dict\n            assert len(results['img_path']) == num\n            assert 'img_path' in results\n            assert 'gt_label' in results\n            for idx in range(num - 1):\n                if (idx + 1) % self.ins_per_id != 0:\n                    assert results['gt_label'][idx] == \\\n                           results['gt_label'][idx + 1]\n"
  },
  {
    "path": "tests/test_datasets/test_samplers/test_batch_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nfrom unittest import TestCase\nfrom unittest.mock import patch\n\nimport numpy as np\nfrom mmengine.dataset import DefaultSampler\nfrom torch.utils.data import Dataset\n\nfrom mmdet.datasets.samplers import AspectRatioBatchSampler\n\n\nclass DummyDataset(Dataset):\n\n    def __init__(self, length):\n        self.length = length\n        self.shapes = np.random.random((length, 2))\n\n    def __len__(self):\n        return self.length\n\n    def __getitem__(self, idx):\n        return self.shapes[idx]\n\n    def get_data_info(self, idx):\n        return dict(width=self.shapes[idx][0], height=self.shapes[idx][1])\n\n\nclass TestAspectRatioBatchSampler(TestCase):\n\n    @patch('mmengine.dist.get_dist_info', return_value=(0, 1))\n    def setUp(self, mock):\n        self.length = 100\n        self.dataset = DummyDataset(self.length)\n        self.sampler = DefaultSampler(self.dataset, shuffle=False)\n\n    def test_invalid_inputs(self):\n        with self.assertRaisesRegex(\n                ValueError, 'batch_size should be a positive integer value'):\n            AspectRatioBatchSampler(self.sampler, batch_size=-1)\n\n        with self.assertRaisesRegex(\n                TypeError, 'sampler should be an instance of ``Sampler``'):\n            AspectRatioBatchSampler(None, batch_size=1)\n\n    def test_divisible_batch(self):\n        batch_size = 5\n        batch_sampler = AspectRatioBatchSampler(\n            self.sampler, batch_size=batch_size, drop_last=True)\n        self.assertEqual(len(batch_sampler), self.length // batch_size)\n        for batch_idxs in batch_sampler:\n            self.assertEqual(len(batch_idxs), batch_size)\n            batch = [self.dataset[idx] for idx in batch_idxs]\n            flag = batch[0][0] < batch[0][1]\n            for i in range(1, batch_size):\n                self.assertEqual(batch[i][0] < batch[i][1], flag)\n\n    def test_indivisible_batch(self):\n        batch_size = 7\n        batch_sampler = AspectRatioBatchSampler(\n            self.sampler, batch_size=batch_size, drop_last=False)\n        all_batch_idxs = list(batch_sampler)\n        self.assertEqual(\n            len(batch_sampler), (self.length + batch_size - 1) // batch_size)\n        self.assertEqual(\n            len(all_batch_idxs), (self.length + batch_size - 1) // batch_size)\n\n        batch_sampler = AspectRatioBatchSampler(\n            self.sampler, batch_size=batch_size, drop_last=True)\n        all_batch_idxs = list(batch_sampler)\n        self.assertEqual(len(batch_sampler), self.length // batch_size)\n        self.assertEqual(len(all_batch_idxs), self.length // batch_size)\n\n        # the last batch may not have the same aspect ratio\n        for batch_idxs in all_batch_idxs[:-1]:\n            self.assertEqual(len(batch_idxs), batch_size)\n            batch = [self.dataset[idx] for idx in batch_idxs]\n            flag = batch[0][0] < batch[0][1]\n            for i in range(1, batch_size):\n                self.assertEqual(batch[i][0] < batch[i][1], flag)\n"
  },
  {
    "path": "tests/test_datasets/test_samplers/test_multi_source_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\nimport bisect\nfrom unittest import TestCase\nfrom unittest.mock import patch\n\nimport numpy as np\nfrom torch.utils.data import ConcatDataset, Dataset\n\nfrom mmdet.datasets.samplers import GroupMultiSourceSampler, MultiSourceSampler\n\n\nclass DummyDataset(Dataset):\n\n    def __init__(self, length, flag):\n        self.length = length\n        self.flag = flag\n        self.shapes = np.random.random((length, 2))\n\n    def __len__(self):\n        return self.length\n\n    def __getitem__(self, idx):\n        return self.shapes[idx]\n\n    def get_data_info(self, idx):\n        return dict(\n            width=self.shapes[idx][0],\n            height=self.shapes[idx][1],\n            flag=self.flag)\n\n\nclass DummyConcatDataset(ConcatDataset):\n\n    def _get_ori_dataset_idx(self, idx):\n        dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx)\n        sample_idx = idx if dataset_idx == 0 else idx - self.cumulative_sizes[\n            dataset_idx - 1]\n        return dataset_idx, sample_idx\n\n    def get_data_info(self, idx: int):\n        dataset_idx, sample_idx = self._get_ori_dataset_idx(idx)\n        return self.datasets[dataset_idx].get_data_info(sample_idx)\n\n\nclass TestMultiSourceSampler(TestCase):\n\n    @patch('mmengine.dist.get_dist_info', return_value=(7, 8))\n    def setUp(self, mock):\n        self.length_a = 100\n        self.dataset_a = DummyDataset(self.length_a, flag='a')\n        self.length_b = 1000\n        self.dataset_b = DummyDataset(self.length_b, flag='b')\n        self.dataset = DummyConcatDataset([self.dataset_a, self.dataset_b])\n\n    def test_multi_source_sampler(self):\n        # test dataset is not ConcatDataset\n        with self.assertRaises(AssertionError):\n            MultiSourceSampler(\n                self.dataset_a, batch_size=5, source_ratio=[1, 4])\n        # test invalid batch_size\n        with self.assertRaises(AssertionError):\n            MultiSourceSampler(\n                self.dataset_a, batch_size=-5, source_ratio=[1, 4])\n        # test source_ratio longer then dataset\n        with self.assertRaises(AssertionError):\n            MultiSourceSampler(\n                self.dataset, batch_size=5, source_ratio=[1, 2, 4])\n        sampler = MultiSourceSampler(\n            self.dataset, batch_size=5, source_ratio=[1, 4])\n        sampler = iter(sampler)\n        flags = []\n        for i in range(100):\n            idx = next(sampler)\n            flags.append(self.dataset.get_data_info(idx)['flag'])\n        flags_gt = ['a', 'b', 'b', 'b', 'b'] * 20\n        self.assertEqual(flags, flags_gt)\n\n\nclass TestGroupMultiSourceSampler(TestCase):\n\n    @patch('mmengine.dist.get_dist_info', return_value=(7, 8))\n    def setUp(self, mock):\n        self.length_a = 100\n        self.dataset_a = DummyDataset(self.length_a, flag='a')\n        self.length_b = 1000\n        self.dataset_b = DummyDataset(self.length_b, flag='b')\n        self.dataset = DummyConcatDataset([self.dataset_a, self.dataset_b])\n\n    def test_group_multi_source_sampler(self):\n        sampler = GroupMultiSourceSampler(\n            self.dataset, batch_size=5, source_ratio=[1, 4])\n        sampler = iter(sampler)\n        flags = []\n        groups = []\n        for i in range(100):\n            idx = next(sampler)\n            data_info = self.dataset.get_data_info(idx)\n            flags.append(data_info['flag'])\n            group = 0 if data_info['width'] < data_info['height'] else 1\n            groups.append(group)\n        flags_gt = ['a', 'b', 'b', 'b', 'b'] * 20\n        self.assertEqual(flags, flags_gt)\n        groups = set(\n            [sum(x) for x in (groups[k:k + 5] for k in range(0, 100, 5))])\n        groups_gt = set([0, 5])\n        self.assertEqual(groups, groups_gt)\n"
  },
  {
    "path": "tests/test_datasets/test_samplers/test_track_img_sampler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom collections.abc import Iterable\nfrom copy import deepcopy\nfrom unittest import TestCase\n\nfrom mmengine.dataset import ClassBalancedDataset, ConcatDataset\n\nfrom mmdet.datasets import MOTChallengeDataset, TrackImgSampler\n\n\nclass TestTrackImgSampler(TestCase):\n\n    def test_iter_base_video_dataset(self):\n        # train mode\n        dataset = MOTChallengeDataset(\n            data_prefix=dict(img_path='imgs'),\n            ann_file='tests/data/mot_sample.json',\n            metainfo=dict(classes=('pedestrian')),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            test_mode=False,\n            pipeline=[])\n        video_sampler = TrackImgSampler(dataset)\n        assert len(video_sampler) == 5\n        iterator = iter(video_sampler)\n        assert isinstance(iterator, Iterable)\n        for index in iterator:\n            assert isinstance(index, tuple)\n            video_index, frame_index = index\n            assert video_index < 2\n            if video_index == 0:\n                assert frame_index >= 0 and frame_index < 3\n            else:\n                assert frame_index >= 0 and frame_index < 2\n\n        # test mode\n        dataset = MOTChallengeDataset(\n            data_prefix=dict(img_path='imgs'),\n            ann_file='tests/data/mot_sample.json',\n            metainfo=dict(classes=('pedestrian')),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            test_mode=True,\n            pipeline=[])\n        video_sampler = TrackImgSampler(dataset)\n        assert len(video_sampler) == 5\n        assert len(video_sampler.indices) == 1\n\n    def test_iter_concat_dataset(self):\n        single_dataset = MOTChallengeDataset(\n            data_prefix=dict(img_path='imgs'),\n            ann_file='tests/data/mot_sample.json',\n            metainfo=dict(classes=('pedestrian')),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            test_mode=False,\n            pipeline=[])\n\n        dataset = ConcatDataset([single_dataset, deepcopy(single_dataset)])\n        video_sampler = TrackImgSampler(dataset)\n        assert len(video_sampler) == 10\n        iterator = iter(video_sampler)\n        assert isinstance(iterator, Iterable)\n        for index in iterator:\n            assert isinstance(index, tuple)\n            video_index, frame_index = index\n            assert video_index < 4\n            if video_index == 0:\n                assert frame_index >= 0 and frame_index < 3\n            elif video_index == 3:\n                assert frame_index >= 0 and frame_index < 2\n\n    def test_iter_class_balanced_dataset(self):\n        single_dataset = MOTChallengeDataset(\n            data_prefix=dict(img_path='imgs'),\n            ann_file='tests/data/mot_sample.json',\n            metainfo=dict(classes=('pedestrian', 'person_on_vehicle')),\n            filter_cfg=dict(filter_empty_gt=True, min_size=32),\n            visibility_thr=0.1,\n            test_mode=False,\n            pipeline=[])\n\n        dataset = ClassBalancedDataset(single_dataset, oversample_thr=0.6)\n        video_sampler = TrackImgSampler(dataset)\n        assert len(video_sampler) == 8\n        iterator = iter(video_sampler)\n        assert isinstance(iterator, Iterable)\n        for index in iterator:\n            assert isinstance(index, tuple)\n            video_index, frame_index = index\n            assert video_index < 3\n            if video_index == 0 or video_index == 2:\n                assert frame_index >= 0 and frame_index < 3\n            else:\n                assert frame_index >= 0 and frame_index < 2\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .utils import construct_toy_data, create_full_masks, create_random_bboxes\n\n__all__ = ['create_random_bboxes', 'create_full_masks', 'construct_toy_data']\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_augment_wrappers.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport unittest\n\nfrom mmdet.datasets.transforms import (AutoAugment, AutoContrast, Brightness,\n                                       Color, Contrast, Equalize, Invert,\n                                       Posterize, RandAugment, Rotate,\n                                       Sharpness, ShearX, ShearY, Solarize,\n                                       SolarizeAdd, TranslateX, TranslateY)\nfrom mmdet.utils import register_all_modules\nfrom .utils import check_result_same, construct_toy_data\n\nregister_all_modules()\n\n\nclass TestAutoAugment(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map',\n                           'homography_matrix')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.img_fill_val = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_autoaugment(self):\n        # test AutoAugment equipped with Shear\n        policies = [[\n            dict(type='ShearX', prob=1.0, level=3, reversal_prob=0.0),\n            dict(type='ShearY', prob=1.0, level=7, reversal_prob=1.0)\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_shearx = ShearX(prob=1.0, level=3, reversal_prob=0.0)\n        transform_sheary = ShearY(prob=1.0, level=7, reversal_prob=1.0)\n        results_sheared = transform_sheary(\n            transform_shearx(copy.deepcopy(self.results_mask)))\n        check_result_same(results_sheared, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Rotate\n        policies = [[\n            dict(type='Rotate', prob=1.0, level=10, reversal_prob=0.0),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_rotate = Rotate(prob=1.0, level=10, reversal_prob=0.0)\n        results_rotated = transform_rotate(copy.deepcopy(self.results_mask))\n        check_result_same(results_rotated, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Translate\n        policies = [[\n            dict(\n                type='TranslateX',\n                prob=1.0,\n                level=10,\n                max_mag=1.0,\n                reversal_prob=0.0),\n            dict(\n                type='TranslateY',\n                prob=1.0,\n                level=10,\n                max_mag=1.0,\n                reversal_prob=1.0)\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_translatex = TranslateX(\n            prob=1.0, level=10, max_mag=1.0, reversal_prob=0.0)\n        transform_translatey = TranslateY(\n            prob=1.0, level=10, max_mag=1.0, reversal_prob=1.0)\n        results_translated = transform_translatey(\n            transform_translatex(copy.deepcopy(self.results_mask)))\n        check_result_same(results_translated, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Brightness\n        policies = [[\n            dict(type='Brightness', prob=1.0, level=3),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_brightness = Brightness(prob=1.0, level=3)\n        results_brightness = transform_brightness(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_brightness, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Color\n        policies = [[\n            dict(type='Color', prob=1.0, level=3),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_color = Color(prob=1.0, level=3)\n        results_colored = transform_color(copy.deepcopy(self.results_mask))\n        check_result_same(results_colored, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Contrast\n        policies = [[\n            dict(type='Contrast', prob=1.0, level=3),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_contrast = Contrast(prob=1.0, level=3)\n        results_contrasted = transform_contrast(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_contrasted, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Sharpness\n        policies = [[\n            dict(type='Sharpness', prob=1.0, level=3),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_sharpness = Sharpness(prob=1.0, level=3)\n        results_sharpness = transform_sharpness(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_sharpness, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Solarize\n        policies = [[\n            dict(type='Solarize', prob=1.0, level=3),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_solarize = Solarize(prob=1.0, level=3)\n        results_solarized = transform_solarize(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_solarized, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with SolarizeAdd\n        policies = [[\n            dict(type='SolarizeAdd', prob=1.0, level=3),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_solarizeadd = SolarizeAdd(prob=1.0, level=3)\n        results_solarizeadded = transform_solarizeadd(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_solarizeadded, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Posterize\n        policies = [[\n            dict(type='Posterize', prob=1.0, level=3),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_posterize = Posterize(prob=1.0, level=3)\n        results_posterized = transform_posterize(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_posterized, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Equalize\n        policies = [[\n            dict(type='Equalize', prob=1.0),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_equalize = Equalize(prob=1.0)\n        results_equalized = transform_equalize(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_equalized, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with AutoContrast\n        policies = [[\n            dict(type='AutoContrast', prob=1.0),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_autocontrast = AutoContrast(prob=1.0)\n        results_autocontrast = transform_autocontrast(\n            copy.deepcopy(self.results_mask))\n        check_result_same(results_autocontrast, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with Invert\n        policies = [[\n            dict(type='Invert', prob=1.0),\n        ]]\n        transform_auto = AutoAugment(policies=policies)\n        results_auto = transform_auto(copy.deepcopy(self.results_mask))\n        transform_invert = Invert(prob=1.0)\n        results_inverted = transform_invert(copy.deepcopy(self.results_mask))\n        check_result_same(results_inverted, results_auto, self.check_keys)\n\n        # test AutoAugment equipped with default policies\n        transform_auto = AutoAugment()\n        transform_auto(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        policies = [[\n            dict(type='Rotate', prob=1.0, level=10, reversal_prob=0.0),\n            dict(type='Invert', prob=1.0),\n        ]]\n        transform = AutoAugment(policies=policies)\n        self.assertEqual(\n            repr(transform), ('AutoAugment('\n                              'policies=[['\n                              \"{'type': 'Rotate', 'prob': 1.0, \"\n                              \"'level': 10, 'reversal_prob': 0.0}, \"\n                              \"{'type': 'Invert', 'prob': 1.0}]], \"\n                              'prob=None)'))\n\n\nclass TestRandAugment(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map',\n                           'homography_matrix')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.img_fill_val = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_randaugment(self):\n        # test RandAugment equipped with Rotate\n        aug_space = [[\n            dict(type='Rotate', prob=1.0, level=10, reversal_prob=0.0)\n        ]]\n        transform_rand = RandAugment(aug_space=aug_space, aug_num=1)\n        results_rand = transform_rand(copy.deepcopy(self.results_mask))\n        transform_rotate = Rotate(prob=1.0, level=10, reversal_prob=0.0)\n        results_rotated = transform_rotate(copy.deepcopy(self.results_mask))\n        check_result_same(results_rotated, results_rand, self.check_keys)\n\n        # test RandAugment equipped with default augmentation space\n        transform_rand = RandAugment()\n        transform_rand(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        aug_space = [\n            [dict(type='Rotate')],\n            [dict(type='Invert')],\n        ]\n        transform = RandAugment(aug_space=aug_space)\n        self.assertEqual(\n            repr(transform), ('RandAugment('\n                              'aug_space=['\n                              \"[{'type': 'Rotate'}], \"\n                              \"[{'type': 'Invert'}]], \"\n                              'aug_num=2, '\n                              'prob=None)'))\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_colorspace.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport unittest\n\nfrom mmdet.datasets.transforms import (AutoContrast, Brightness, Color,\n                                       ColorTransform, Contrast, Equalize,\n                                       Invert, Posterize, Sharpness, Solarize,\n                                       SolarizeAdd)\nfrom .utils import check_result_same, construct_toy_data\n\n\nclass TestColorTransform(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_colortransform(self):\n        # test assertion for invalid value of level\n        with self.assertRaises(AssertionError):\n            transform = ColorTransform(level=-1)\n\n        # test assertion for invalid prob\n        with self.assertRaises(AssertionError):\n            transform = ColorTransform(level=1, prob=-0.5)\n\n        # test case when no translation is called (prob=0)\n        transform = ColorTransform(prob=0.0, level=10)\n        results_wo_color = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_color, self.check_keys)\n\n    def test_repr(self):\n        transform = ColorTransform(level=10, prob=1.)\n        self.assertEqual(\n            repr(transform), ('ColorTransform(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n\n\nclass TestColor(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_color(self):\n        # test case when level=5 (without color aug)\n        transform = Color(prob=1.0, level=5)\n        results_wo_color = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_color, self.check_keys)\n        # test case when level=0\n        transform = Color(prob=1.0, level=0)\n        transform(copy.deepcopy(self.results_mask))\n        # test case when level=10\n        transform = Color(prob=1.0, level=10)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Color(level=10, prob=1.)\n        self.assertEqual(\n            repr(transform), ('Color(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n\n\nclass TestBrightness(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_brightness(self):\n        # test case when level=5 (without Brightness aug)\n        transform = Brightness(level=5, prob=1.0)\n        results_wo_brightness = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_brightness,\n                          self.check_keys)\n        # test case when level=0\n        transform = Brightness(prob=1.0, level=0)\n        transform(copy.deepcopy(self.results_mask))\n        # test case when level=10\n        transform = Brightness(prob=1.0, level=10)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Brightness(prob=1.0, level=10)\n        self.assertEqual(\n            repr(transform), ('Brightness(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n\n\nclass TestContrast(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_contrast(self):\n        # test case when level=5 (without Contrast aug)\n        transform = Contrast(prob=1.0, level=5)\n        results_wo_contrast = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_contrast,\n                          self.check_keys)\n        # test case when level=0\n        transform = Contrast(prob=1.0, level=0)\n        transform(copy.deepcopy(self.results_mask))\n        # test case when level=10\n        transform = Contrast(prob=1.0, level=10)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Contrast(level=10, prob=1.)\n        self.assertEqual(\n            repr(transform), ('Contrast(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n\n\nclass TestSharpness(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_sharpness(self):\n        # test case when level=5 (without Sharpness aug)\n        transform = Sharpness(prob=1.0, level=5)\n        results_wo_sharpness = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_sharpness,\n                          self.check_keys)\n        # test case when level=0\n        transform = Sharpness(prob=1.0, level=0)\n        transform(copy.deepcopy(self.results_mask))\n        # test case when level=10\n        transform = Sharpness(prob=1.0, level=10)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Sharpness(level=10, prob=1.)\n        self.assertEqual(\n            repr(transform), ('Sharpness(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n\n\nclass TestSolarize(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_solarize(self):\n        # test case when level=10 (without Solarize aug)\n        transform = Solarize(prob=1.0, level=10)\n        results_wo_solarize = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_solarize,\n                          self.check_keys)\n        # test case when level=0\n        transform = Solarize(prob=1.0, level=0)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Solarize(level=10, prob=1.)\n        self.assertEqual(\n            repr(transform), ('Solarize(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.0, '\n                              'max_mag=256.0)'))\n\n\nclass TestSolarizeAdd(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_solarize(self):\n        # test case when level=0 (without Solarize aug)\n        transform = SolarizeAdd(prob=1.0, level=0)\n        results_wo_solarizeadd = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_solarizeadd,\n                          self.check_keys)\n        # test case when level=10\n        transform = SolarizeAdd(prob=1.0, level=10)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = SolarizeAdd(level=10, prob=1.)\n        self.assertEqual(\n            repr(transform), ('SolarizeAdd(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.0, '\n                              'max_mag=110.0)'))\n\n\nclass TestPosterize(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_posterize(self):\n        # test case when level=10 (without Posterize aug)\n        transform = Posterize(prob=1.0, level=10, max_mag=8.0)\n        results_wo_posterize = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_posterize,\n                          self.check_keys)\n        # test case when level=0\n        transform = Posterize(prob=1.0, level=0)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Posterize(level=10, prob=1.)\n        self.assertEqual(\n            repr(transform), ('Posterize(prob=1.0, '\n                              'level=10, '\n                              'min_mag=0.0, '\n                              'max_mag=4.0)'))\n\n\nclass TestEqualize(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_equalize(self):\n        # test case when no translation is called (prob=0)\n        transform = Equalize(prob=0.0)\n        results_wo_equalize = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_equalize,\n                          self.check_keys)\n        # test case when translation is called\n        transform = Equalize(prob=1.0)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Equalize(prob=1.0)\n        self.assertEqual(\n            repr(transform), ('Equalize(prob=1.0, '\n                              'level=None, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n\n\nclass TestAutoContrast(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_autocontrast(self):\n        # test case when no translation is called (prob=0)\n        transform = AutoContrast(prob=0.0)\n        results_wo_autocontrast = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_autocontrast,\n                          self.check_keys)\n        # test case when translation is called\n        transform = AutoContrast(prob=1.0)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = AutoContrast(prob=1.0)\n        self.assertEqual(\n            repr(transform), ('AutoContrast(prob=1.0, '\n                              'level=None, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n\n\nclass TestInvert(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n\n    def test_invert(self):\n        # test case when no translation is called (prob=0)\n        transform = Invert(prob=0.0)\n        results_wo_invert = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_invert,\n                          self.check_keys)\n        # test case when translation is called\n        transform = Invert(prob=1.0)\n        transform(copy.deepcopy(self.results_mask))\n\n    def test_repr(self):\n        transform = Invert(prob=1.0)\n        self.assertEqual(\n            repr(transform), ('Invert(prob=1.0, '\n                              'level=None, '\n                              'min_mag=0.1, '\n                              'max_mag=1.9)'))\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_formatting.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nimport unittest\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData, LabelData, PixelData\n\nfrom mmdet.datasets.transforms import (PackDetInputs, PackReIDInputs,\n                                       PackTrackInputs)\nfrom mmdet.structures import DetDataSample, ReIDDataSample\nfrom mmdet.structures.mask import BitmapMasks\n\n\nclass TestPackDetInputs(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        data_prefix = osp.join(osp.dirname(__file__), '../../data')\n        img_path = osp.join(data_prefix, 'color.jpg')\n        rng = np.random.RandomState(0)\n        self.results1 = {\n            'img_id': 1,\n            'img_path': img_path,\n            'ori_shape': (300, 400),\n            'img_shape': (600, 800),\n            'scale_factor': 2.0,\n            'flip': False,\n            'img': rng.rand(300, 400),\n            'gt_seg_map': rng.rand(300, 400),\n            'gt_masks':\n            BitmapMasks(rng.rand(3, 300, 400), height=300, width=400),\n            'gt_bboxes_labels': rng.rand(3, ),\n            'gt_ignore_flags': np.array([0, 0, 1], dtype=bool),\n            'proposals': rng.rand(2, 4),\n            'proposals_scores': rng.rand(2, )\n        }\n        self.results2 = {\n            'img_id': 1,\n            'img_path': img_path,\n            'ori_shape': (300, 400),\n            'img_shape': (600, 800),\n            'scale_factor': 2.0,\n            'flip': False,\n            'img': rng.rand(300, 400),\n            'gt_seg_map': rng.rand(300, 400),\n            'gt_masks':\n            BitmapMasks(rng.rand(3, 300, 400), height=300, width=400),\n            'gt_bboxes_labels': rng.rand(3, ),\n            'proposals': rng.rand(2, 4),\n            'proposals_scores': rng.rand(2, )\n        }\n        self.meta_keys = ('img_id', 'img_path', 'ori_shape', 'scale_factor',\n                          'flip')\n\n    def test_transform(self):\n        transform = PackDetInputs(meta_keys=self.meta_keys)\n        results = transform(copy.deepcopy(self.results1))\n        self.assertIn('data_samples', results)\n        self.assertIsInstance(results['data_samples'], DetDataSample)\n        self.assertIsInstance(results['data_samples'].gt_instances,\n                              InstanceData)\n        self.assertIsInstance(results['data_samples'].ignored_instances,\n                              InstanceData)\n        self.assertEqual(len(results['data_samples'].gt_instances), 2)\n        self.assertEqual(len(results['data_samples'].ignored_instances), 1)\n        self.assertIsInstance(results['data_samples'].gt_sem_seg, PixelData)\n        self.assertIsInstance(results['data_samples'].proposals, InstanceData)\n        self.assertEqual(len(results['data_samples'].proposals), 2)\n        self.assertIsInstance(results['data_samples'].proposals.bboxes,\n                              torch.Tensor)\n        self.assertIsInstance(results['data_samples'].proposals.scores,\n                              torch.Tensor)\n\n    def test_transform_without_ignore(self):\n        transform = PackDetInputs(meta_keys=self.meta_keys)\n        results = transform(copy.deepcopy(self.results2))\n        self.assertIn('data_samples', results)\n        self.assertIsInstance(results['data_samples'], DetDataSample)\n        self.assertIsInstance(results['data_samples'].gt_instances,\n                              InstanceData)\n        self.assertIsInstance(results['data_samples'].ignored_instances,\n                              InstanceData)\n        self.assertEqual(len(results['data_samples'].gt_instances), 3)\n        self.assertEqual(len(results['data_samples'].ignored_instances), 0)\n        self.assertIsInstance(results['data_samples'].gt_sem_seg, PixelData)\n        self.assertIsInstance(results['data_samples'].proposals, InstanceData)\n        self.assertEqual(len(results['data_samples'].proposals), 2)\n        self.assertIsInstance(results['data_samples'].proposals.bboxes,\n                              torch.Tensor)\n        self.assertIsInstance(results['data_samples'].proposals.scores,\n                              torch.Tensor)\n\n    def test_repr(self):\n        transform = PackDetInputs(meta_keys=self.meta_keys)\n        self.assertEqual(\n            repr(transform), f'PackDetInputs(meta_keys={self.meta_keys})')\n\n\nclass TestPackTrackInputs(unittest.TestCase):\n\n    def setUp(self):\n        self.H, self.W = 5, 10\n        self.img = np.zeros((self.H, self.W, 3))\n        self.gt_bboxes = np.zeros((2, 4))\n        self.gt_masks = BitmapMasks(\n            np.random.rand(2, self.H, self.W), height=self.H, width=self.W)\n        self.gt_bboxes_labels = [\n            np.zeros((2, )),\n            np.zeros((2, )) + 1,\n            np.zeros((2, )) - 1\n        ]\n        self.gt_instances_ids = [\n            np.ones((2, ), dtype=np.int32),\n            np.ones((2, ), dtype=np.int32) - 1,\n            np.ones((2, ), dtype=np.int32) + 1\n        ]\n        self.frame_id = [0, 1, 2]\n        self.scale_factor = [1.0, 1.5, 2.0]\n        self.flip = [False] * 3\n        self.ori_shape = [(self.H, self.W)] * 3\n        self.img_id = [0, 1, 2]\n        self.results_1 = dict(\n            img=[self.img.copy(),\n                 self.img.copy(),\n                 self.img.copy()],\n            gt_bboxes=[\n                self.gt_bboxes.copy(),\n                self.gt_bboxes.copy(),\n                self.gt_bboxes.copy()\n            ],\n            gt_bboxes_labels=copy.deepcopy(self.gt_bboxes_labels),\n            gt_instances_ids=copy.deepcopy(self.gt_instances_ids),\n            gt_masks=[\n                copy.deepcopy(self.gt_masks),\n                copy.deepcopy(self.gt_masks),\n                copy.deepcopy(self.gt_masks)\n            ],\n            frame_id=self.frame_id,\n            ori_shape=self.ori_shape,\n            scale_factor=self.scale_factor,\n            flip=self.flip,\n            img_id=self.img_id,\n            key_frame_flags=[False, True, False])\n\n        self.results_2 = copy.deepcopy(self.results_1)\n        self.gt_ignore_flags = [\n            np.array([0, 1], dtype=np.bool),\n            np.array([1, 0], dtype=np.bool),\n            np.array([0, 0], dtype=np.bool)\n        ]\n        self.results_2.update(\n            dict(gt_ignore_flags=copy.deepcopy(self.gt_ignore_flags)))\n\n        self.meta_keys = ('frame_id', 'ori_shape', 'scale_factor', 'flip')\n        self.pack_track_inputs = PackTrackInputs(meta_keys=self.meta_keys)\n\n    def test_transform_without_ignore(self):\n        track_results = self.pack_track_inputs(self.results_1)\n        assert isinstance(track_results, dict)\n\n        inputs = track_results['inputs']\n        assert isinstance(inputs, torch.Tensor)\n        assert inputs.shape == (3, 3, self.H, self.W)\n\n        track_data_sample = track_results['data_samples']\n        assert len(track_data_sample) == 3\n        assert 'key_frames_inds' in track_data_sample.metainfo and \\\n            track_data_sample.key_frames_inds == [1]\n        assert 'ref_frames_inds' in track_data_sample.metainfo and \\\n            track_data_sample.ref_frames_inds == [0, 2]\n        for i, data_sample in enumerate(track_data_sample):\n            assert data_sample.gt_instances.bboxes.shape == (2, 4)\n            assert len(data_sample.gt_instances.masks) == 2\n            assert (data_sample.gt_instances.labels.numpy() ==\n                    self.gt_bboxes_labels[i]).all()\n            assert (data_sample.gt_instances.instances_ids.numpy() ==\n                    self.gt_instances_ids[i]).all()\n            for key in self.meta_keys:\n                assert data_sample.metainfo[key] == getattr(self, key)[i]\n\n    def test_transform_with_ignore(self):\n        track_results = self.pack_track_inputs(self.results_2)\n        assert isinstance(track_results, dict)\n\n        inputs = track_results['inputs']\n        assert isinstance(inputs, torch.Tensor)\n        assert inputs.shape == (3, 3, self.H, self.W)\n\n        track_data_sample = track_results['data_samples']\n        assert len(track_data_sample) == 3\n        for i, data_sample in enumerate(track_data_sample):\n            valid_mask = ~self.gt_ignore_flags[i]\n            valid_len = valid_mask.sum().item()\n            assert data_sample.gt_instances.bboxes.shape == (valid_len, 4)\n            assert len(data_sample.gt_instances.masks) == valid_len\n            assert (data_sample.gt_instances.labels.numpy() ==\n                    self.gt_bboxes_labels[i][valid_mask]).all()\n            assert (data_sample.gt_instances.instances_ids.numpy() ==\n                    self.gt_instances_ids[i][valid_mask]).all()\n            for key in self.meta_keys:\n                assert data_sample.metainfo[key] == getattr(self, key)[i]\n\n\nclass TestPackReIDInputs(unittest.TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        cls.results = dict(\n            img=np.random.randn(256, 128, 3),\n            gt_label=0,\n            img_path='',\n            ori_shape=(128, 128),\n            img_shape=(256, 128),\n            scale=(128, 256),\n            scale_factor=(1., 2.),\n            flip=False,\n            flip_direction=None)\n        cls.pack_reid_inputs = PackReIDInputs(\n            meta_keys=('flip', 'flip_direction'))\n\n    def test_transform(self):\n        results = self.pack_reid_inputs(self.results)\n        self.assertIn('inputs', results)\n        self.assertIsInstance(results['inputs'], torch.Tensor)\n        self.assertIn('data_samples', results)\n        data_sample = results['data_samples']\n        self.assertIsInstance(data_sample, ReIDDataSample)\n        self.assertIsInstance(data_sample.gt_label, LabelData)\n        self.assertEqual(data_sample.img_path, '')\n        self.assertEqual(data_sample.ori_shape, (128, 128))\n        self.assertEqual(data_sample.img_shape, (256, 128))\n        self.assertEqual(data_sample.scale, (128, 256))\n        self.assertEqual(data_sample.scale_factor, (1., 2.))\n        self.assertEqual(data_sample.flip, False)\n        self.assertIsNone(data_sample.flip_direction)\n\n    def test_repr(self):\n        self.assertEqual(\n            repr(self.pack_reid_inputs),\n            f'PackReIDInputs(meta_keys={self.pack_reid_inputs.meta_keys})')\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_frame_sampling.py",
    "content": "import unittest\n\nimport numpy as np\n\nfrom mmdet.datasets.transforms import BaseFrameSample, UniformRefFrameSample\n\n\nclass TestFrameSample(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod()\n        -> tearDown() -> cleanUp()\n        \"\"\"\n        self.H, self.W = 5, 8\n        self.img = np.zeros((self.H, self.W, 3))\n        self.gt_bboxes = np.zeros((2, 4))\n        self.gt_bboxes_labels = [\n            np.zeros((2, )),\n            np.zeros((2, )) + 1,\n            np.zeros((2, )) - 1\n        ]\n        self.gt_instances_id = [\n            np.ones((2, ), dtype=np.int32),\n            np.ones((2, ), dtype=np.int32) - 1,\n            np.ones((2, ), dtype=np.int32) + 1\n        ]\n        self.frame_id = [0, 1, 2]\n        self.scale_factor = [1.0, 1.5, 2.0]\n        self.flip = [False] * 3\n        self.ori_shape = [(self.H, self.W)] * 3\n        self.img_id = [0, 1, 2]\n\n        self.video_infos = dict(video_id=0, video_length=10, key_frame_id=4)\n        self.video_infos['images'] = []\n        self.info_keys = [\n            'video_id', 'video_length', 'img', 'gt_bboxes', 'gt_bboxes_labels',\n            'gt_instances_id', 'img_id', 'frame_id'\n        ]\n        for i in range(10):\n            frame_info = dict(\n                img=np.zeros((self.H, self.W, 3)) + i,\n                gt_bboxes=np.zeros((2, 4)) + i,\n                gt_bboxes_labels=np.zeros((2, )) + i,\n                gt_instances_id=np.zeros((2, ), dtype=np.int32) + i,\n                ori_shape=(self.H + i, self.W + i),\n                frame_id=i,\n                img_id=i)\n            self.video_infos['images'].append(frame_info)\n\n    def test_base_frame_sample(self):\n        sampler = BaseFrameSample()\n        results = sampler(self.video_infos)\n        assert isinstance(results, dict)\n        for key in self.info_keys:\n            assert key in results\n            assert len(results[key]) == 1\n            if key == 'frame_id':\n                assert results[key] == [4]\n\n        key_frame_id = self.video_infos['key_frame_id']\n        assert (results['img'][0] == np.zeros(\n            (self.H, self.W, 3)) + key_frame_id).all()\n        assert (results['gt_bboxes'][0] == np.zeros(\n            (2, 4)) + key_frame_id).all()\n        assert (results['gt_bboxes_labels'][0] == np.zeros(\n            (2, )) + key_frame_id).all()\n        assert (results['gt_instances_id'][0] == np.zeros(\n            (2, )) + key_frame_id).all()\n        assert results['ori_shape'][0] == (self.H + key_frame_id,\n                                           self.W + key_frame_id)\n        assert results['img_id'][0] == key_frame_id\n\n    def test_uniform_ref_frame_sample(self):\n        sampler = UniformRefFrameSample(\n            num_ref_imgs=2, frame_range=[-1, 1], filter_key_img=True)\n        results = sampler(self.video_infos)\n        assert isinstance(results, dict)\n        for key in self.info_keys:\n            assert key in results\n            assert len(results[key]) == 3\n            if key == 'frame_id':\n                assert results[key] == [3, 4, 5]\n\n        key_frame_id = self.video_infos['key_frame_id']\n        assert (results['img'][1] == np.zeros(\n            (self.H, self.W, 3)) + key_frame_id).all()\n        assert (results['gt_bboxes'][1] == np.zeros(\n            (2, 4)) + key_frame_id).all()\n        assert (results['gt_bboxes_labels'][1] == np.zeros(\n            (2, )) + key_frame_id).all()\n        assert (results['gt_instances_id'][1] == np.zeros(\n            (2, )) + key_frame_id).all()\n        assert results['ori_shape'][1] == (self.H + key_frame_id,\n                                           self.W + key_frame_id)\n        assert results['img_id'][1] == key_frame_id\n\n        # test the filter_key_img and the correctness of returned frame index\n        sampler = UniformRefFrameSample(\n            num_ref_imgs=2, frame_range=[0, 1], filter_key_img=False)\n        results = sampler(self.video_infos)\n        assert 4 in results['img_id'] and results['img_id'].count(4) == 2\n        assert 5 in results['img_id'] and results['img_id'].count(5) == 1\n        assert results['key_frame_flags'] == [True, False, False]\n\n    def test_repr(self):\n        transform = BaseFrameSample()\n        self.assertEqual(\n            repr(transform),\n            \"BaseFrameSample(collect_video_keys=['video_id', 'video_length'])\")\n\n        transform = UniformRefFrameSample(\n            num_ref_imgs=2, frame_range=10, filter_key_img=True)\n        self.assertEqual(\n            repr(transform),\n            ('UniformRefFrameSample(num_ref_imgs=2, '\n             'frame_range=[-10, 10], filter_key_img=True, '\n             \"collect_video_keys=['video_id', 'video_length'])\"))\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_geometric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport unittest\n\nimport numpy as np\n\nfrom mmdet.datasets.transforms import (GeomTransform, Rotate, ShearX, ShearY,\n                                       TranslateX, TranslateY)\nfrom mmdet.structures.bbox import HorizontalBoxes\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\nfrom .utils import check_result_same, construct_toy_data\n\n\nclass TestGeomTransform(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.img_border_value = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_geomtransform(self):\n        # test assertion for invalid prob\n        with self.assertRaises(AssertionError):\n            transform = GeomTransform(\n                prob=-0.5, level=1, min_mag=0.0, max_mag=1.0)\n\n        # test assertion for invalid value of level\n        with self.assertRaises(AssertionError):\n            transform = GeomTransform(\n                prob=0.5, level=-1, min_mag=0.0, max_mag=1.0)\n\n        # test assertion for invalid value of min_mag and max_mag\n        with self.assertRaises(AssertionError):\n            transform = ShearX(prob=0.5, level=2, min_mag=1.0, max_mag=0.0)\n\n        # test assertion for the num of elements in tuple img_border_value\n        with self.assertRaises(AssertionError):\n            transform = GeomTransform(\n                prob=0.5,\n                level=1,\n                min_mag=0.0,\n                max_mag=1.0,\n                img_border_value=(128, 128, 128, 128))\n\n        # test ValueError for invalid type of img_border_value\n        with self.assertRaises(ValueError):\n            transform = GeomTransform(\n                prob=0.5,\n                level=1,\n                min_mag=0.0,\n                max_mag=1.0,\n                img_border_value=[128, 128, 128])\n\n        # test assertion for invalid value of img_border_value\n        with self.assertRaises(AssertionError):\n            transform = GeomTransform(\n                prob=0.5,\n                level=1,\n                min_mag=0.0,\n                max_mag=1.0,\n                img_border_value=(128, -1, 256))\n\n        # test case when no aug (prob=0)\n        transform = GeomTransform(\n            prob=0.,\n            level=10,\n            min_mag=0.0,\n            max_mag=1.0,\n            img_border_value=self.img_border_value)\n        results_wo_aug = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_aug, self.check_keys)\n\n    def test_repr(self):\n        transform = GeomTransform(\n            prob=0.5,\n            level=5,\n            min_mag=0.0,\n            max_mag=1.0,\n        )\n        self.assertEqual(\n            repr(transform), ('GeomTransform(prob=0.5, '\n                              'level=5, '\n                              'min_mag=0.0, '\n                              'max_mag=1.0, '\n                              'reversal_prob=0.5, '\n                              'img_border_value=(128.0, 128.0, 128.0), '\n                              'mask_border_value=0, '\n                              'seg_ignore_label=255, '\n                              'interpolation=bilinear)'))\n\n\nclass TestShearX(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.results_poly = construct_toy_data(poly2mask=False)\n        self.results_mask_boxtype = construct_toy_data(\n            poly2mask=True, use_box_type=True)\n        self.img_border_value = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_shearx(self):\n        # test assertion for invalid value of min_mag\n        with self.assertRaises(AssertionError):\n            transform = ShearX(prob=0.5, level=2, min_mag=-30.)\n        # test assertion for invalid value of max_mag\n        with self.assertRaises(AssertionError):\n            transform = ShearX(prob=0.5, level=2, max_mag=100.)\n\n        # test case when no shear horizontally (level=0)\n        transform = ShearX(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label,\n        )\n        results_wo_shearx = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_shearx,\n                          self.check_keys)\n\n        # test shear horizontally, magnitude=-1\n        transform = ShearX(\n            prob=1.0,\n            level=10,\n            max_mag=45.,\n            reversal_prob=1.0,\n            img_border_value=self.img_border_value)\n        results_sheared = transform(copy.deepcopy(self.results_mask))\n        results_gt = copy.deepcopy(self.results_mask)\n        img_gt = np.array([[1, 2, 3, 4], [0, 5, 6, 7], [0, 0, 9, 10]],\n                          dtype=np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        img_gt[1, 0, :] = np.array(self.img_border_value)\n        img_gt[2, 0, :] = np.array(self.img_border_value)\n        img_gt[2, 1, :] = np.array(self.img_border_value)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = np.array([[1, 0, 4, 2]], dtype=np.float32)\n        results_gt['gt_bboxes_labels'] = np.array([13], dtype=np.int64)\n        gt_masks = np.array([[0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 255, 255], [255, 255, 13, 13], [255, 255, 255, 13]],\n            dtype=self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_sheared, self.check_keys)\n\n        # test PolygonMasks with shear horizontally, magnitude=1\n        results_sheared = transform(copy.deepcopy(self.results_poly))\n        gt_masks = [[np.array([3, 2, 1, 0, 3, 1], dtype=np.float32)]]\n        results_gt['gt_masks'] = PolygonMasks(gt_masks, 3, 4)\n        check_result_same(results_gt, results_sheared, self.check_keys)\n\n    def test_shearx_use_box_type(self):\n        # test case when no shear horizontally (level=0)\n        transform = ShearX(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label,\n        )\n        results_wo_shearx = transform(copy.deepcopy(self.results_mask_boxtype))\n        check_result_same(self.results_mask_boxtype, results_wo_shearx,\n                          self.check_keys)\n\n        # test shear horizontally, magnitude=-1\n        transform = ShearX(\n            prob=1.0,\n            level=10,\n            max_mag=45.,\n            reversal_prob=1.0,\n            img_border_value=self.img_border_value)\n        results_sheared = transform(copy.deepcopy(self.results_mask_boxtype))\n        results_gt = copy.deepcopy(self.results_mask_boxtype)\n        img_gt = np.array([[1, 2, 3, 4], [0, 5, 6, 7], [0, 0, 9, 10]],\n                          dtype=np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        img_gt[1, 0, :] = np.array(self.img_border_value)\n        img_gt[2, 0, :] = np.array(self.img_border_value)\n        img_gt[2, 1, :] = np.array(self.img_border_value)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = HorizontalBoxes(\n            np.array([[1, 0, 4, 2]], dtype=np.float32))\n        results_gt['gt_bboxes_labels'] = np.array([13], dtype=np.int64)\n        gt_masks = np.array([[0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 255, 255], [255, 255, 13, 13], [255, 255, 255, 13]],\n            dtype=self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_sheared, self.check_keys)\n\n    def test_repr(self):\n        transform = ShearX(prob=0.5, level=10)\n        self.assertEqual(\n            repr(transform), ('ShearX(prob=0.5, '\n                              'level=10, '\n                              'min_mag=0.0, '\n                              'max_mag=30.0, '\n                              'reversal_prob=0.5, '\n                              'img_border_value=(128.0, 128.0, 128.0), '\n                              'mask_border_value=0, '\n                              'seg_ignore_label=255, '\n                              'interpolation=bilinear)'))\n\n\nclass TestShearY(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.results_poly = construct_toy_data(poly2mask=False)\n        self.results_mask_boxtype = construct_toy_data(\n            poly2mask=True, use_box_type=True)\n        self.img_border_value = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_sheary(self):\n        # test assertion for invalid value of min_mag\n        with self.assertRaises(AssertionError):\n            transform = ShearY(prob=0.5, level=2, min_mag=-30.)\n        # test assertion for invalid value of max_mag\n        with self.assertRaises(AssertionError):\n            transform = ShearY(prob=0.5, level=2, max_mag=100.)\n\n        # test case when no shear vertically (level=0)\n        transform = ShearY(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label,\n        )\n        results_wo_sheary = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_sheary,\n                          self.check_keys)\n\n        # test shear vertically, magnitude=1\n        transform = ShearY(prob=1., level=10, max_mag=45., reversal_prob=0.)\n        results_sheared = transform(copy.deepcopy(self.results_mask))\n        results_gt = copy.deepcopy(self.results_mask)\n        img_gt = np.array(\n            [[1, 6, 11, 128], [5, 10, 128, 128], [9, 128, 128, 128]],\n            dtype=np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = np.array([[1, 0, 2, 1]], dtype=np.float32)\n        results_gt['gt_bboxes_labels'] = np.array([13], dtype=np.int64)\n        gt_masks = np.array([[0, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 255, 255], [255, 13, 255, 255], [255, 255, 255, 255]],\n            dtype=self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_sheared, self.check_keys)\n\n        # test PolygonMasks with shear vertically, magnitude=-1\n        results_sheared = transform(copy.deepcopy(self.results_poly))\n        gt_masks = [[np.array([1, 1, 1, 0, 2, 0], dtype=np.float32)]]\n        results_gt['gt_masks'] = PolygonMasks(gt_masks, 3, 4)\n        check_result_same(results_gt, results_sheared, self.check_keys)\n\n    def test_sheary_use_box_type(self):\n        # test case when no shear vertically (level=0)\n        transform = ShearY(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label,\n        )\n        results_wo_sheary = transform(copy.deepcopy(self.results_mask_boxtype))\n        check_result_same(self.results_mask_boxtype, results_wo_sheary,\n                          self.check_keys)\n\n        # test shear vertically, magnitude=1\n        transform = ShearY(prob=1., level=10, max_mag=45., reversal_prob=0.)\n        results_sheared = transform(copy.deepcopy(self.results_mask_boxtype))\n        results_gt = copy.deepcopy(self.results_mask_boxtype)\n        img_gt = np.array(\n            [[1, 6, 11, 128], [5, 10, 128, 128], [9, 128, 128, 128]],\n            dtype=np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = HorizontalBoxes(\n            np.array([[1, 0, 2, 1]], dtype=np.float32))\n        results_gt['gt_bboxes_labels'] = np.array([13], dtype=np.int64)\n        gt_masks = np.array([[0, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 255, 255], [255, 13, 255, 255], [255, 255, 255, 255]],\n            dtype=self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_sheared, self.check_keys)\n\n    def test_repr(self):\n        transform = ShearY(prob=0.5, level=10)\n        self.assertEqual(\n            repr(transform), ('ShearY(prob=0.5, '\n                              'level=10, '\n                              'min_mag=0.0, '\n                              'max_mag=30.0, '\n                              'reversal_prob=0.5, '\n                              'img_border_value=(128.0, 128.0, 128.0), '\n                              'mask_border_value=0, '\n                              'seg_ignore_label=255, '\n                              'interpolation=bilinear)'))\n\n\nclass TestRotate(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.results_poly = construct_toy_data(poly2mask=False)\n        self.results_mask_boxtype = construct_toy_data(\n            poly2mask=True, use_box_type=True)\n        self.img_border_value = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_rotate(self):\n        # test assertion for invalid value of min_mag\n        with self.assertRaises(AssertionError):\n            transform = ShearY(prob=0.5, level=2, min_mag=-90.0)\n        # test assertion for invalid value of max_mag\n        with self.assertRaises(AssertionError):\n            transform = ShearY(prob=0.5, level=2, max_mag=270.0)\n\n        # test case when no rotate aug (level=0)\n        transform = Rotate(\n            prob=1.,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label,\n        )\n        results_wo_rotate = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_rotate,\n                          self.check_keys)\n\n        # test clockwise rotation with angle 90\n        transform = Rotate(\n            prob=1.,\n            level=10,\n            max_mag=90.0,\n            # set reversal_prob to 1 for clockwise rotation\n            reversal_prob=1.,\n        )\n        results_rotated = transform(copy.deepcopy(self.results_mask))\n        # The image, masks, and semantic segmentation map\n        # will be bilinearly interpolated.\n        img_gt = np.array([[69, 8, 4, 65], [69, 9, 5, 65],\n                           [70, 10, 6, 66]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt = copy.deepcopy(self.results_mask)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = np.array([[0.5, 0.5, 2.5, 1.5]],\n                                           dtype=np.float32)\n        gt_masks = np.array([[0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 13, 13], [255, 255, 13, 255],\n             [255, 255, 255,\n              255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_rotated, self.check_keys)\n\n        # test clockwise rotation with angle 90, PolygonMasks\n        results_rotated = transform(copy.deepcopy(self.results_poly))\n        gt_masks = [[np.array([0, 1, 0, 1, 0, 2], dtype=np.float)]]\n        results_gt['gt_masks'] = PolygonMasks(gt_masks, 3, 4)\n        check_result_same(results_gt, results_rotated, self.check_keys)\n\n        # test counter-clockwise rotation with angle 90\n        transform = Rotate(\n            prob=1.0,\n            level=10,\n            max_mag=90.0,\n            # set reversal_prob to 0 for counter-clockwise rotation\n            reversal_prob=0.0,\n        )\n        results_rotated = transform(copy.deepcopy(self.results_mask))\n        # The image, masks, and  semantic segmentation map\n        # will be bilinearly interpolated.\n        img_gt = np.array([[66, 6, 10, 70], [65, 5, 9, 69],\n                           [65, 4, 8, 69]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt = copy.deepcopy(self.results_mask)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = np.array([[0.5, 0.5, 2.5, 1.5]],\n                                           dtype=np.float32)\n        gt_masks = np.array([[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 255, 255, 255], [255, 13, 255, 255],\n             [13, 13, 13, 255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_rotated, self.check_keys)\n\n        # test counter-clockwise rotation with angle 90, PolygonMasks\n        results_rotated = transform(copy.deepcopy(self.results_poly))\n        gt_masks = [[np.array([2, 0, 0, 0, 1, 0], dtype=np.float)]]\n        results_gt['gt_masks'] = PolygonMasks(gt_masks, 3, 4)\n        check_result_same(results_gt, results_rotated, self.check_keys)\n\n    def test_rotate_use_box_type(self):\n        # test case when no rotate aug (level=0)\n        transform = Rotate(\n            prob=1.,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label,\n        )\n        results_wo_rotate = transform(copy.deepcopy(self.results_mask_boxtype))\n        check_result_same(self.results_mask_boxtype, results_wo_rotate,\n                          self.check_keys)\n\n        # test clockwise rotation with angle 90\n        transform = Rotate(\n            prob=1.,\n            level=10,\n            max_mag=90.0,\n            # set reversal_prob to 1 for clockwise rotation\n            reversal_prob=1.,\n        )\n        results_rotated = transform(copy.deepcopy(self.results_mask_boxtype))\n        # The image, masks, and semantic segmentation map\n        # will be bilinearly interpolated.\n        img_gt = np.array([[69, 8, 4, 65], [69, 9, 5, 65],\n                           [70, 10, 6, 66]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt = copy.deepcopy(self.results_mask_boxtype)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = HorizontalBoxes(\n            np.array([[0.5, 0.5, 2.5, 1.5]], dtype=np.float32))\n        gt_masks = np.array([[0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 13, 13], [255, 255, 13, 255],\n             [255, 255, 255,\n              255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_rotated, self.check_keys)\n\n        # test counter-clockwise rotation with angle 90\n        transform = Rotate(\n            prob=1.0,\n            level=10,\n            max_mag=90.0,\n            # set reversal_prob to 0 for counter-clockwise rotation\n            reversal_prob=0.0,\n        )\n        results_rotated = transform(copy.deepcopy(self.results_mask_boxtype))\n        # The image, masks, and  semantic segmentation map\n        # will be bilinearly interpolated.\n        img_gt = np.array([[66, 6, 10, 70], [65, 5, 9, 69],\n                           [65, 4, 8, 69]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt = copy.deepcopy(self.results_mask_boxtype)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = HorizontalBoxes(\n            np.array([[0.5, 0.5, 2.5, 1.5]], dtype=np.float32))\n        gt_masks = np.array([[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 255, 255, 255], [255, 13, 255, 255],\n             [13, 13, 13, 255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_rotated, self.check_keys)\n\n    def test_repr(self):\n        transform = Rotate(prob=0.5, level=5)\n        self.assertEqual(\n            repr(transform), ('Rotate(prob=0.5, '\n                              'level=5, '\n                              'min_mag=0.0, '\n                              'max_mag=30.0, '\n                              'reversal_prob=0.5, '\n                              'img_border_value=(128.0, 128.0, 128.0), '\n                              'mask_border_value=0, '\n                              'seg_ignore_label=255, '\n                              'interpolation=bilinear)'))\n\n\nclass TestTranslateX(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.results_poly = construct_toy_data(poly2mask=False)\n        self.results_mask_boxtype = construct_toy_data(\n            poly2mask=True, use_box_type=True)\n        self.img_border_value = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_translatex(self):\n        # test assertion for invalid value of min_mag\n        with self.assertRaises(AssertionError):\n            transform = TranslateX(prob=0.5, level=2, min_mag=-1.)\n        # test assertion for invalid value of max_mag\n        with self.assertRaises(AssertionError):\n            transform = TranslateX(prob=0.5, level=2, max_mag=1.1)\n\n        # test case when level=0 (without translate aug)\n        transform = TranslateX(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label)\n        results_wo_translatex = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_translatex,\n                          self.check_keys)\n\n        # test translate horizontally, magnitude=-1\n        transform = TranslateX(\n            prob=1.0,\n            level=10,\n            max_mag=0.3,\n            reversal_prob=0.0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label)\n        results_translated = transform(copy.deepcopy(self.results_mask))\n        img_gt = np.array([[2, 3, 4, 0], [6, 7, 8, 0], [10, 11, 12,\n                                                        0]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        img_gt[:, 3, :] = np.array(self.img_border_value)\n        results_gt = copy.deepcopy(self.results_mask)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = np.array([[0, 0, 1, 2]], dtype=np.float32)\n        gt_masks = np.array([[1, 0, 0, 0], [1, 1, 0, 0], [1, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[13, 255, 255, 255], [13, 13, 255, 255],\n             [13, 255, 255,\n              255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_translated, self.check_keys)\n\n        # test PolygonMasks with translate horizontally.\n        results_translated = transform(copy.deepcopy(self.results_poly))\n        gt_masks = [[np.array([0, 2, 0, 0, 1, 1], dtype=np.float32)]]\n        results_gt['gt_masks'] = PolygonMasks(gt_masks, 3, 4)\n        check_result_same(results_gt, results_translated, self.check_keys)\n\n    def test_translatex_use_box_type(self):\n        # test case when level=0 (without translate aug)\n        transform = TranslateX(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label)\n        results_wo_translatex = transform(\n            copy.deepcopy(self.results_mask_boxtype))\n        check_result_same(self.results_mask_boxtype, results_wo_translatex,\n                          self.check_keys)\n\n        # test translate horizontally, magnitude=-1\n        transform = TranslateX(\n            prob=1.0,\n            level=10,\n            max_mag=0.3,\n            reversal_prob=0.0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label)\n        results_translated = transform(\n            copy.deepcopy(self.results_mask_boxtype))\n        img_gt = np.array([[2, 3, 4, 0], [6, 7, 8, 0], [10, 11, 12,\n                                                        0]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        img_gt[:, 3, :] = np.array(self.img_border_value)\n        results_gt = copy.deepcopy(self.results_mask)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = HorizontalBoxes(\n            np.array([[0, 0, 1, 2]], dtype=np.float32))\n        gt_masks = np.array([[1, 0, 0, 0], [1, 1, 0, 0], [1, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[13, 255, 255, 255], [13, 13, 255, 255],\n             [13, 255, 255,\n              255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_translated, self.check_keys)\n\n    def test_repr(self):\n        transform = TranslateX(prob=0.5, level=5)\n        self.assertEqual(\n            repr(transform), ('TranslateX(prob=0.5, '\n                              'level=5, '\n                              'min_mag=0.0, '\n                              'max_mag=0.1, '\n                              'reversal_prob=0.5, '\n                              'img_border_value=(128.0, 128.0, 128.0), '\n                              'mask_border_value=0, '\n                              'seg_ignore_label=255, '\n                              'interpolation=bilinear)'))\n\n\nclass TestTranslateY(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.check_keys = ('img', 'gt_bboxes', 'gt_bboxes_labels', 'gt_masks',\n                           'gt_ignore_flags', 'gt_seg_map')\n        self.results_mask = construct_toy_data(poly2mask=True)\n        self.results_poly = construct_toy_data(poly2mask=False)\n        self.results_mask_boxtype = construct_toy_data(\n            poly2mask=True, use_box_type=True)\n        self.img_border_value = (104, 116, 124)\n        self.seg_ignore_label = 255\n\n    def test_translatey(self):\n        # test assertion for invalid value of min_mag\n        with self.assertRaises(AssertionError):\n            transform = TranslateY(prob=0.5, level=2, min_mag=-1.0)\n        # test assertion for invalid value of max_mag\n        with self.assertRaises(AssertionError):\n            transform = TranslateY(prob=0.5, level=2, max_mag=1.1)\n\n        # test case when level=0 (without translate aug)\n        transform = TranslateY(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label)\n        results_wo_translatey = transform(copy.deepcopy(self.results_mask))\n        check_result_same(self.results_mask, results_wo_translatey,\n                          self.check_keys)\n\n        # test translate vertically, magnitude=-1\n        transform = TranslateY(\n            prob=1.0,\n            level=10,\n            max_mag=0.4,\n            reversal_prob=0.0,\n            seg_ignore_label=self.seg_ignore_label)\n\n        results_translated = transform(copy.deepcopy(self.results_mask))\n        img_gt = np.array([[5, 6, 7, 8], [9, 10, 11, 12],\n                           [128, 128, 128, 128]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt = copy.deepcopy(self.results_mask)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = np.array([[1, 0, 2, 1]], dtype=np.float32)\n        gt_masks = np.array([[0, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 13, 255], [255, 13, 255, 255],\n             [255, 255, 255,\n              255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_translated, self.check_keys)\n\n        # test PolygonMasks with translate vertically.\n        results_translated = transform(copy.deepcopy(self.results_poly))\n        gt_masks = [[np.array([1, 1, 1, 0, 2, 0], dtype=np.float32)]]\n        results_gt['gt_masks'] = PolygonMasks(gt_masks, 3, 4)\n        check_result_same(results_gt, results_translated, self.check_keys)\n\n    def test_translatey_use_box_type(self):\n        # test case when level=0 (without translate aug)\n        transform = TranslateY(\n            prob=1.0,\n            level=0,\n            img_border_value=self.img_border_value,\n            seg_ignore_label=self.seg_ignore_label)\n        results_wo_translatey = transform(\n            copy.deepcopy(self.results_mask_boxtype))\n        check_result_same(self.results_mask_boxtype, results_wo_translatey,\n                          self.check_keys)\n\n        # test translate vertically, magnitude=-1\n        transform = TranslateY(\n            prob=1.0,\n            level=10,\n            max_mag=0.4,\n            reversal_prob=0.0,\n            seg_ignore_label=self.seg_ignore_label)\n\n        results_translated = transform(\n            copy.deepcopy(self.results_mask_boxtype))\n        img_gt = np.array([[5, 6, 7, 8], [9, 10, 11, 12],\n                           [128, 128, 128, 128]]).astype(np.uint8)\n        img_gt = np.stack([img_gt, img_gt, img_gt], axis=-1)\n        results_gt = copy.deepcopy(self.results_mask_boxtype)\n        results_gt['img'] = img_gt\n        results_gt['gt_bboxes'] = HorizontalBoxes(\n            np.array([[1, 0, 2, 1]], dtype=np.float32))\n        gt_masks = np.array([[0, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results_gt['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n        results_gt['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n        results_gt['gt_seg_map'] = np.array(\n            [[255, 13, 13, 255], [255, 13, 255, 255],\n             [255, 255, 255,\n              255]]).astype(self.results_mask['gt_seg_map'].dtype)\n        check_result_same(results_gt, results_translated, self.check_keys)\n\n    def test_repr(self):\n        transform = TranslateX(prob=0.5, level=5)\n        self.assertEqual(\n            repr(transform), ('TranslateX(prob=0.5, '\n                              'level=5, '\n                              'min_mag=0.0, '\n                              'max_mag=0.1, '\n                              'reversal_prob=0.5, '\n                              'img_border_value=(128.0, 128.0, 128.0), '\n                              'mask_border_value=0, '\n                              'seg_ignore_label=255, '\n                              'interpolation=bilinear)'))\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_instaboost.py",
    "content": "import os.path as osp\nimport unittest\n\nimport numpy as np\n\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n\nclass TestInstaboost(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        img_path = osp.join(osp.dirname(__file__), '../../data/gray.jpg')\n        self.results = {\n            'img_path':\n            img_path,\n            'img_shape': (300, 400),\n            'instances': [{\n                'bbox': [0, 0, 10, 20],\n                'bbox_label': 1,\n                'mask': [[0, 0, 0, 20, 10, 20, 10, 0]],\n                'ignore_flag': 0\n            }, {\n                'bbox': [10, 10, 110, 120],\n                'bbox_label': 2,\n                'mask': [[10, 10, 110, 10, 110, 120, 110, 10]],\n                'ignore_flag': 0\n            }, {\n                'bbox': [50, 50, 60, 80],\n                'bbox_label': 2,\n                'mask': [[50, 50, 60, 50, 60, 80, 50, 80]],\n                'ignore_flag': 1\n            }]\n        }\n\n    def test_transform(self):\n        load = TRANSFORMS.build(dict(type='LoadImageFromFile'))\n        instaboost_transform = TRANSFORMS.build(dict(type='InstaBoost'))\n\n        # Execute transforms\n        results = load(self.results)\n        results = instaboost_transform(results)\n\n        self.assertEqual(results['img'].dtype, np.uint8)\n        self.assertIn('instances', results)\n\n    def test_repr(self):\n        instaboost_transform = TRANSFORMS.build(dict(type='InstaBoost'))\n\n        self.assertEqual(\n            repr(instaboost_transform), 'InstaBoost(aug_ratio=0.5)')\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_loading.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os\nimport os.path as osp\nimport sys\nimport unittest\nfrom unittest.mock import MagicMock, Mock, patch\n\nimport mmcv\nimport numpy as np\n\nfrom mmdet.datasets.transforms import (FilterAnnotations, LoadAnnotations,\n                                       LoadEmptyAnnotations,\n                                       LoadImageFromNDArray,\n                                       LoadMultiChannelImageFromFiles,\n                                       LoadProposals, LoadTrackAnnotations)\nfrom mmdet.evaluation import INSTANCE_OFFSET\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\n\ntry:\n    import panopticapi\nexcept ImportError:\n    panopticapi = None\n\n\nclass TestLoadAnnotations(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        data_prefix = osp.join(osp.dirname(__file__), '../../data')\n        seg_map = osp.join(data_prefix, 'gray.jpg')\n        self.results = {\n            'ori_shape': (300, 400),\n            'seg_map_path':\n            seg_map,\n            'instances': [{\n                'bbox': [0, 0, 10, 20],\n                'bbox_label': 1,\n                'mask': [[0, 0, 0, 20, 10, 20, 10, 0]],\n                'ignore_flag': 0\n            }, {\n                'bbox': [10, 10, 110, 120],\n                'bbox_label': 2,\n                'mask': [[10, 10, 110, 10, 110, 120, 110, 10]],\n                'ignore_flag': 0\n            }, {\n                'bbox': [50, 50, 60, 80],\n                'bbox_label': 2,\n                'mask': [[50, 50, 60, 50, 60, 80, 50, 80]],\n                'ignore_flag': 1\n            }]\n        }\n\n    def test_load_bboxes(self):\n        transform = LoadAnnotations(\n            with_bbox=True,\n            with_label=False,\n            with_seg=False,\n            with_mask=False,\n            box_type=None)\n        results = transform(copy.deepcopy(self.results))\n        self.assertIn('gt_bboxes', results)\n        self.assertTrue((results['gt_bboxes'] == np.array([[0, 0, 10, 20],\n                                                           [10, 10, 110, 120],\n                                                           [50, 50, 60,\n                                                            80]])).all())\n        self.assertEqual(results['gt_bboxes'].dtype, np.float32)\n        self.assertTrue((results['gt_ignore_flags'] == np.array([0, 0,\n                                                                 1])).all())\n        self.assertEqual(results['gt_ignore_flags'].dtype, bool)\n\n    def test_load_labels(self):\n        transform = LoadAnnotations(\n            with_bbox=False,\n            with_label=True,\n            with_seg=False,\n            with_mask=False,\n        )\n        results = transform(copy.deepcopy(self.results))\n        self.assertIn('gt_bboxes_labels', results)\n        self.assertTrue((results['gt_bboxes_labels'] == np.array([1, 2,\n                                                                  2])).all())\n        self.assertEqual(results['gt_bboxes_labels'].dtype, np.int64)\n\n    def test_load_mask(self):\n        transform = LoadAnnotations(\n            with_bbox=False,\n            with_label=False,\n            with_seg=False,\n            with_mask=True,\n            poly2mask=False)\n        results = transform(copy.deepcopy(self.results))\n        self.assertIn('gt_masks', results)\n        self.assertEqual(len(results['gt_masks']), 3)\n        self.assertIsInstance(results['gt_masks'], PolygonMasks)\n\n    def test_load_mask_poly2mask(self):\n        transform = LoadAnnotations(\n            with_bbox=False,\n            with_label=False,\n            with_seg=False,\n            with_mask=True,\n            poly2mask=True)\n        results = transform(copy.deepcopy(self.results))\n        self.assertIn('gt_masks', results)\n        self.assertEqual(len(results['gt_masks']), 3)\n        self.assertIsInstance(results['gt_masks'], BitmapMasks)\n\n    def test_load_semseg(self):\n        transform = LoadAnnotations(\n            with_bbox=False, with_label=False, with_seg=True, with_mask=False)\n        results = transform(copy.deepcopy(self.results))\n        self.assertIn('gt_seg_map', results)\n        self.assertIn('ignore_index', results)\n        self.assertEqual(results['gt_seg_map'].shape, (288, 512))\n\n        # test reduce_zero_label and ignore_index\n        transform = LoadAnnotations(\n            with_bbox=False,\n            with_label=False,\n            with_seg=True,\n            with_mask=False,\n            reduce_zero_label=True,\n            ignore_index=10)\n        results = transform(copy.deepcopy(self.results))\n        self.assertIn('gt_seg_map', results)\n        self.assertIn('ignore_index', results)\n        self.assertEqual(results['ignore_index'], 10)\n        self.assertEqual(results['gt_seg_map'].shape, (288, 512))\n\n    def test_repr(self):\n        transform = LoadAnnotations(\n            with_bbox=True,\n            with_label=False,\n            with_seg=False,\n            with_mask=False,\n        )\n        self.assertEqual(\n            repr(transform), ('LoadAnnotations(with_bbox=True, '\n                              'with_label=False, with_mask=False, '\n                              'with_seg=False, poly2mask=True, '\n                              \"imdecode_backend='cv2', \"\n                              'backend_args=None)'))\n\n\nclass TestFilterAnnotations(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.results = {\n            'img':\n            np.random.random((224, 224, 3)),\n            'img_shape': (224, 224),\n            'gt_bboxes_labels':\n            np.array([1, 2, 3], dtype=np.int64),\n            'gt_bboxes':\n            np.array([[10, 10, 20, 20], [20, 20, 40, 40], [40, 40, 80, 80]]),\n            'gt_ignore_flags':\n            np.array([0, 0, 1], dtype=np.bool8),\n            'gt_masks':\n            BitmapMasks(rng.rand(3, 224, 224), height=224, width=224),\n        }\n\n    def test_transform(self):\n        # test keep_empty = True\n        transform = FilterAnnotations(\n            min_gt_bbox_wh=(50, 50),\n            keep_empty=True,\n        )\n        results = transform(copy.deepcopy(self.results))\n        self.assertIsNone(results)\n\n        # test keep_empty = False\n        transform = FilterAnnotations(\n            min_gt_bbox_wh=(50, 50),\n            keep_empty=False,\n        )\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(isinstance(results, dict))\n\n        # test filter annotations\n        transform = FilterAnnotations(min_gt_bbox_wh=(15, 15), )\n        results = transform(copy.deepcopy(self.results))\n\n        self.assertIsInstance(results, dict)\n        self.assertTrue((results['gt_bboxes_labels'] == np.array([2,\n                                                                  3])).all())\n        self.assertTrue((results['gt_bboxes'] == np.array([[20, 20, 40, 40],\n                                                           [40, 40, 80,\n                                                            80]])).all())\n        self.assertEqual(len(results['gt_masks']), 2)\n        self.assertEqual(len(results['gt_ignore_flags']), 2)\n\n    def test_repr(self):\n        transform = FilterAnnotations(\n            min_gt_bbox_wh=(1, 1),\n            keep_empty=False,\n        )\n        self.assertEqual(\n            repr(transform), ('FilterAnnotations(min_gt_bbox_wh=(1, 1), '\n                              'keep_empty=False)'))\n\n\nclass TestLoadPanopticAnnotations(unittest.TestCase):\n\n    def setUp(self):\n        seg_map = np.zeros((10, 10), dtype=np.int32)\n        seg_map[:5, :10] = 1 + 10 * INSTANCE_OFFSET\n        seg_map[5:10, :5] = 4 + 11 * INSTANCE_OFFSET\n        seg_map[5:10, 5:10] = 6 + 0 * INSTANCE_OFFSET\n        rgb_seg_map = np.zeros((10, 10, 3), dtype=np.uint8)\n        rgb_seg_map[:, :, 0] = seg_map / (256 * 256)\n        rgb_seg_map[:, :, 1] = seg_map % (256 * 256) / 256\n        rgb_seg_map[:, :, 2] = seg_map % 256\n        self.seg_map_path = './1.png'\n        mmcv.imwrite(rgb_seg_map, self.seg_map_path)\n\n        self.seg_map = seg_map\n        self.rgb_seg_map = rgb_seg_map\n        self.results = {\n            'ori_shape': (10, 10),\n            'instances': [{\n                'bbox': [0, 0, 10, 5],\n                'bbox_label': 0,\n                'ignore_flag': 0,\n            }, {\n                'bbox': [0, 5, 5, 10],\n                'bbox_label': 1,\n                'ignore_flag': 1,\n            }],\n            'segments_info': [\n                {\n                    'id': 1 + 10 * INSTANCE_OFFSET,\n                    'category': 0,\n                    'is_thing': True,\n                },\n                {\n                    'id': 4 + 11 * INSTANCE_OFFSET,\n                    'category': 1,\n                    'is_thing': True,\n                },\n                {\n                    'id': 6 + 0 * INSTANCE_OFFSET,\n                    'category': 2,\n                    'is_thing': False,\n                },\n            ],\n            'seg_map_path':\n            self.seg_map_path\n        }\n\n        self.gt_mask = BitmapMasks([\n            (seg_map == 1 + 10 * INSTANCE_OFFSET).astype(np.uint8),\n            (seg_map == 4 + 11 * INSTANCE_OFFSET).astype(np.uint8),\n        ], 10, 10)\n        self.gt_bboxes = np.array([[0, 0, 10, 5], [0, 5, 5, 10]],\n                                  dtype=np.float32)\n        self.gt_bboxes_labels = np.array([0, 1], dtype=np.int64)\n        self.gt_ignore_flags = np.array([0, 1], dtype=bool)\n        self.gt_seg_map = np.zeros((10, 10), dtype=np.int32)\n        self.gt_seg_map[:5, :10] = 0\n        self.gt_seg_map[5:10, :5] = 1\n        self.gt_seg_map[5:10, 5:10] = 2\n\n    def tearDown(self):\n        os.remove(self.seg_map_path)\n\n    @unittest.skipIf(panopticapi is not None, 'panopticapi is installed')\n    def test_init_without_panopticapi(self):\n        # test if panopticapi is not installed\n        from mmdet.datasets.transforms import LoadPanopticAnnotations\n        with self.assertRaisesRegex(\n                ImportError,\n                'panopticapi is not installed, please install it by'):\n            LoadPanopticAnnotations()\n\n    def test_transform(self):\n        sys.modules['panopticapi'] = MagicMock()\n        sys.modules['panopticapi.utils'] = MagicMock()\n        from mmdet.datasets.transforms import LoadPanopticAnnotations\n        mock_rgb2id = Mock(return_value=self.seg_map)\n        with patch('panopticapi.utils.rgb2id', mock_rgb2id):\n            # test with all False\n            transform = LoadPanopticAnnotations(\n                with_bbox=False,\n                with_label=False,\n                with_mask=False,\n                with_seg=False)\n            results = transform(copy.deepcopy(self.results))\n            self.assertDictEqual(results, self.results)\n            # test with with_mask=True\n            transform = LoadPanopticAnnotations(\n                with_bbox=False,\n                with_label=False,\n                with_mask=True,\n                with_seg=False)\n            results = transform(copy.deepcopy(self.results))\n            self.assertTrue(\n                (results['gt_masks'].masks == self.gt_mask.masks).all())\n\n            # test with with_seg=True\n            transform = LoadPanopticAnnotations(\n                with_bbox=False,\n                with_label=False,\n                with_mask=False,\n                with_seg=True)\n            results = transform(copy.deepcopy(self.results))\n            self.assertNotIn('gt_masks', results)\n            self.assertTrue((results['gt_seg_map'] == self.gt_seg_map).all())\n\n            # test with all True\n            transform = LoadPanopticAnnotations(\n                with_bbox=True,\n                with_label=True,\n                with_mask=True,\n                with_seg=True,\n                box_type=None)\n            results = transform(copy.deepcopy(self.results))\n            self.assertTrue(\n                (results['gt_masks'].masks == self.gt_mask.masks).all())\n            self.assertTrue((results['gt_bboxes'] == self.gt_bboxes).all())\n            self.assertTrue(\n                (results['gt_bboxes_labels'] == self.gt_bboxes_labels).all())\n            self.assertTrue(\n                (results['gt_ignore_flags'] == self.gt_ignore_flags).all())\n            self.assertTrue((results['gt_seg_map'] == self.gt_seg_map).all())\n\n\nclass TestLoadImageFromNDArray(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.results = {'img': np.zeros((256, 256, 3), dtype=np.uint8)}\n\n    def test_transform(self):\n        transform = LoadImageFromNDArray()\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img'].shape, (256, 256, 3))\n        self.assertEqual(results['img'].dtype, np.uint8)\n        self.assertEqual(results['img_shape'], (256, 256))\n        self.assertEqual(results['ori_shape'], (256, 256))\n\n        # to_float32\n        transform = LoadImageFromNDArray(to_float32=True)\n        results = transform(copy.deepcopy(results))\n        self.assertEqual(results['img'].dtype, np.float32)\n\n    def test_repr(self):\n        transform = LoadImageFromNDArray()\n        self.assertEqual(\n            repr(transform), ('LoadImageFromNDArray('\n                              'ignore_empty=False, '\n                              'to_float32=False, '\n                              \"color_type='color', \"\n                              \"imdecode_backend='cv2', \"\n                              'backend_args=None)'))\n\n\nclass TestLoadMultiChannelImageFromFiles(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.img_path = []\n        for i in range(4):\n            img_channel_path = f'./part_{i}.jpg'\n            img_channel = np.zeros((10, 10), dtype=np.uint8)\n            mmcv.imwrite(img_channel, img_channel_path)\n            self.img_path.append(img_channel_path)\n        self.results = {'img_path': self.img_path}\n\n    def tearDown(self):\n        for filename in self.img_path:\n            os.remove(filename)\n\n    def test_transform(self):\n        transform = LoadMultiChannelImageFromFiles()\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img'].shape, (10, 10, 4))\n        self.assertEqual(results['img'].dtype, np.uint8)\n        self.assertEqual(results['img_shape'], (10, 10))\n        self.assertEqual(results['ori_shape'], (10, 10))\n\n        # to_float32\n        transform = LoadMultiChannelImageFromFiles(to_float32=True)\n        results = transform(copy.deepcopy(results))\n        self.assertEqual(results['img'].dtype, np.float32)\n\n    def test_rper(self):\n        transform = LoadMultiChannelImageFromFiles()\n        self.assertEqual(\n            repr(transform), ('LoadMultiChannelImageFromFiles('\n                              'to_float32=False, '\n                              \"color_type='unchanged', \"\n                              \"imdecode_backend='cv2', \"\n                              'backend_args=None)'))\n\n\nclass TestLoadProposals(unittest.TestCase):\n\n    def test_transform(self):\n        transform = LoadProposals()\n        results = {\n            'proposals':\n            dict(\n                bboxes=np.zeros((5, 4), dtype=np.int64),\n                scores=np.zeros((5, ), dtype=np.int64))\n        }\n        results = transform(results)\n        self.assertEqual(results['proposals'].dtype, np.float32)\n        self.assertEqual(results['proposals'].shape[-1], 4)\n        self.assertEqual(results['proposals_scores'].dtype, np.float32)\n\n        #  bboxes.shape[1] should be 4\n        results = {'proposals': dict(bboxes=np.zeros((5, 5), dtype=np.int64))}\n        with self.assertRaises(AssertionError):\n            transform(results)\n\n        # bboxes.shape[0] should equal to scores.shape[0]\n        results = {\n            'proposals':\n            dict(\n                bboxes=np.zeros((5, 4), dtype=np.int64),\n                scores=np.zeros((3, ), dtype=np.int64))\n        }\n        with self.assertRaises(AssertionError):\n            transform(results)\n\n        # empty bboxes\n        results = {\n            'proposals': dict(bboxes=np.zeros((0, 4), dtype=np.float32))\n        }\n        results = transform(results)\n        excepted_proposals = np.zeros((0, 4), dtype=np.float32)\n        excepted_proposals_scores = np.zeros(0, dtype=np.float32)\n        self.assertTrue((results['proposals'] == excepted_proposals).all())\n        self.assertTrue(\n            (results['proposals_scores'] == excepted_proposals_scores).all())\n\n        transform = LoadProposals(num_max_proposals=2)\n        results = {\n            'proposals':\n            dict(\n                bboxes=np.zeros((5, 4), dtype=np.int64),\n                scores=np.zeros((5, ), dtype=np.int64))\n        }\n        results = transform(results)\n        self.assertEqual(results['proposals'].shape[0], 2)\n\n    def test_repr(self):\n        transform = LoadProposals()\n        self.assertEqual(\n            repr(transform), 'LoadProposals(num_max_proposals=None)')\n\n\nclass TestLoadEmptyAnnotations(unittest.TestCase):\n\n    def test_transform(self):\n        transform = LoadEmptyAnnotations(\n            with_bbox=True, with_label=True, with_mask=True, with_seg=True)\n        results = {'img_shape': (224, 224)}\n        results = transform(results)\n        self.assertEqual(results['gt_bboxes'].dtype, np.float32)\n        self.assertEqual(results['gt_bboxes'].shape[-1], 4)\n        self.assertEqual(results['gt_ignore_flags'].dtype, bool)\n        self.assertEqual(results['gt_bboxes_labels'].dtype, np.int64)\n        self.assertEqual(results['gt_masks'].masks.dtype, np.uint8)\n        self.assertEqual(results['gt_masks'].masks.shape[-2:],\n                         results['img_shape'])\n        self.assertEqual(results['gt_seg_map'].dtype, np.uint8)\n        self.assertEqual(results['gt_seg_map'].shape, results['img_shape'])\n\n    def test_repr(self):\n        transform = LoadEmptyAnnotations()\n        self.assertEqual(\n            repr(transform), 'LoadEmptyAnnotations(with_bbox=True, '\n            'with_label=True, '\n            'with_mask=False, '\n            'with_seg=False, '\n            'seg_ignore_label=255)')\n\n\nclass TestLoadTrackAnnotations(unittest.TestCase):\n\n    def setUp(self):\n        data_prefix = osp.join(osp.dirname(__file__), '../data')\n        seg_map = osp.join(data_prefix, 'grayscale.jpg')\n        self.results = {\n            'seg_map_path':\n            seg_map,\n            'instances': [{\n                'bbox': [0, 0, 10, 20],\n                'bbox_label': 1,\n                'instance_id': 100,\n                'keypoints': [1, 2, 3]\n            }, {\n                'bbox': [10, 10, 110, 120],\n                'bbox_label': 2,\n                'instance_id': 102,\n                'keypoints': [4, 5, 6]\n            }]\n        }\n\n    def test_load_instances_id(self):\n        transform = LoadTrackAnnotations(\n            with_bbox=False,\n            with_label=True,\n            with_seg=False,\n            with_keypoints=False,\n        )\n        results = transform(copy.deepcopy(self.results))\n        assert 'gt_instances_ids' in results\n        assert (results['gt_instances_ids'] == np.array([100, 102])).all()\n        assert results['gt_instances_ids'].dtype == np.int32\n\n    def test_repr(self):\n        transform = LoadTrackAnnotations(\n            with_bbox=True, with_label=False, with_seg=False, with_mask=False)\n        assert repr(transform) == ('LoadTrackAnnotations(with_bbox=True, '\n                                   'with_label=False, with_mask=False,'\n                                   ' with_seg=False, poly2mask=True,'\n                                   \" imdecode_backend='cv2', \"\n                                   'file_client_args=None)')\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_transforms.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os.path as osp\nimport unittest\n\nimport mmcv\nimport numpy as np\nimport torch\nfrom mmcv.transforms import LoadImageFromFile\n\n# yapf:disable\nfrom mmdet.datasets.transforms import (CopyPaste, CutOut, Expand,\n                                       FixScaleResize, FixShapeResize,\n                                       MinIoURandomCrop, MixUp, Mosaic, Pad,\n                                       PhotoMetricDistortion, RandomAffine,\n                                       RandomCenterCropPad, RandomCrop,\n                                       RandomErasing, RandomFlip, RandomShift,\n                                       Resize, ResizeShortestEdge, SegRescale,\n                                       YOLOXHSVRandomAug)\n# yapf:enable\nfrom mmdet.evaluation import bbox_overlaps\nfrom mmdet.registry import TRANSFORMS\nfrom mmdet.structures.bbox import HorizontalBoxes, bbox_project\nfrom mmdet.structures.mask import BitmapMasks\nfrom .utils import construct_toy_data, create_full_masks, create_random_bboxes\n\ntry:\n    import albumentations\n    from albumentations import Compose\nexcept ImportError:\n    albumentations = None\n    Compose = None\n# yapf:enable\n\n\nclass TestResize(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod()\n        -> tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.data_info1 = dict(\n            img=np.random.random((400, 500, 3)),\n            gt_seg_map=np.random.random((400, 500, 3)),\n            gt_bboxes=np.array([[0, 0, 112, 112]], dtype=np.float32),\n            gt_masks=BitmapMasks(rng.rand(1, 400, 500), height=400, width=500))\n        self.data_info2 = dict(\n            img=np.random.random((200, 100, 3)),\n            gt_bboxes=np.array([[20, 15, 60, 45]], dtype=np.float32),\n            dtype=np.float32)\n        self.data_info3 = dict(img=np.random.random((200, 100, 3)))\n\n    def test_resize(self):\n        # test keep_ratio is True\n        transform = Resize(scale=(100, 100), keep_ratio=True)\n        results = transform(copy.deepcopy(self.data_info1))\n        self.assertEqual(results['img_shape'], (80, 100))\n        self.assertEqual(results['scale_factor'], (80 / 400, 100 / 500))\n\n        # test resize_bboxes/seg/masks\n        transform = Resize(scale_factor=(1.5, 2))\n        results = transform(copy.deepcopy(self.data_info1))\n        self.assertTrue(\n            (results['gt_bboxes'] == np.array([[0., 0., 168., 224.]])).all())\n        self.assertEqual(results['gt_masks'].height, 800)\n        self.assertEqual(results['gt_masks'].width, 750)\n        self.assertEqual(results['gt_seg_map'].shape[:2], (800, 750))\n\n        # test clip_object_border = False\n        transform = Resize(scale=(200, 150), clip_object_border=False)\n        results = transform(self.data_info2)\n        self.assertTrue(\n            (results['gt_bboxes'] == np.array([40., 11.25, 120.,\n                                               33.75])).all())\n\n        # test only with image\n        transform = Resize(scale=(200, 150), clip_object_border=False)\n        results = transform(self.data_info3)\n        self.assertTupleEqual(results['img'].shape[:2], (150, 200))\n\n        # test geometric transformation with homography matrix\n        transform = Resize(scale_factor=(1.5, 2))\n        results = transform(copy.deepcopy(self.data_info1))\n        self.assertTrue((bbox_project(\n            copy.deepcopy(self.data_info1['gt_bboxes']),\n            results['homography_matrix']) == results['gt_bboxes']).all())\n\n    def test_resize_use_box_type(self):\n        data_info1 = copy.deepcopy(self.data_info1)\n        data_info1['gt_bboxes'] = HorizontalBoxes(data_info1['gt_bboxes'])\n        data_info2 = copy.deepcopy(self.data_info2)\n        data_info2['gt_bboxes'] = HorizontalBoxes(data_info2['gt_bboxes'])\n        # test keep_ratio is True\n        transform = Resize(scale=(100, 150), keep_ratio=True)\n        results = transform(copy.deepcopy(data_info1))\n        self.assertEqual(results['img_shape'], (100, 125))\n        self.assertEqual(results['scale_factor'], (100 / 400, 125 / 500))\n\n        # test resize_bboxes/seg/masks\n        transform = Resize(scale_factor=(1.5, 2))\n        results = transform(copy.deepcopy(data_info1))\n        self.assertTrue(\n            (results['gt_bboxes'].numpy() == np.array([[0, 0, 168,\n                                                        224]])).all())\n        self.assertEqual(results['gt_masks'].height, 800)\n        self.assertEqual(results['gt_masks'].width, 750)\n        self.assertEqual(results['gt_seg_map'].shape[:2], (800, 750))\n\n        # test clip_object_border = False\n        transform = Resize(scale=(200, 150), clip_object_border=False)\n        results = transform(data_info2)\n        self.assertTrue((results['gt_bboxes'].numpy() == np.array(\n            [40., 11.25, 120., 33.75])).all())\n\n        # test geometric transformation with homography matrix\n        transform = Resize(scale_factor=(1.5, 2))\n        results = transform(copy.deepcopy(data_info1))\n        self.assertTrue((bbox_project(\n            copy.deepcopy(data_info1['gt_bboxes'].numpy()),\n            results['homography_matrix']) == results['gt_bboxes'].numpy()\n                         ).all())\n\n    def test_repr(self):\n        transform = Resize(scale=(100, 100), keep_ratio=True)\n        self.assertEqual(\n            repr(transform), ('Resize(scale=(100, 100), '\n                              'scale_factor=None, keep_ratio=True, '\n                              'clip_object_border=True), backend=cv2), '\n                              'interpolation=bilinear)'))\n\n\nclass TestFixScaleResize(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod()\n        -> tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.data_info1 = dict(\n            img=np.random.random((200, 300, 3)),\n            gt_seg_map=np.random.random((200, 300, 3)),\n            gt_bboxes=np.array([[0, 0, 112, 112]], dtype=np.float32),\n            gt_masks=BitmapMasks(rng.rand(1, 200, 300), height=200, width=300))\n\n    def test_resize(self):\n        # test keep_ratio is True\n        transform = FixScaleResize(scale=(101, 201), keep_ratio=True)\n        results = transform(copy.deepcopy(self.data_info1))\n        self.assertEqual(results['img_shape'], (101, 151))\n        self.assertEqual(results['scale_factor'], (151 / 300, 101 / 200))\n\n\nclass TestFixShapeResize(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.data_info1 = dict(\n            img=np.random.random((200, 300, 3)),\n            gt_seg_map=np.random.random((200, 300, 3)),\n            gt_bboxes=np.array([[0, 0, 112, 133]], dtype=np.float32),\n            gt_masks=BitmapMasks(rng.rand(1, 200, 300), height=200, width=300))\n        self.data_info2 = dict(\n            img=np.random.random((300, 400, 3)),\n            gt_bboxes=np.array([[200, 150, 600, 450]], dtype=np.float32),\n            dtype=np.float32)\n        self.data_info3 = dict(img=np.random.random((300, 400, 3)))\n        self.data_info4 = dict(\n            img=np.random.random((400, 450, 3)),\n            gt_bboxes=np.array([[200, 150, 300, 400]], dtype=np.float32),\n            dtype=np.float32)\n\n    def test_resize(self):\n        # test keep_ratio is True\n        transform = FixShapeResize(width=100, height=50, keep_ratio=True)\n        results = transform(copy.deepcopy(self.data_info1))\n        self.assertEqual(results['img_shape'], (50, 100))\n        self.assertEqual(results['scale_factor'], (50 / 200, 50 / 200))\n        # test resize_bboxes/seg/masks\n        transform = FixShapeResize(width=120, height=100, keep_ratio=False)\n        results = transform(copy.deepcopy(self.data_info1))\n        self.assertEqual(results['gt_masks'].height, 100)\n        self.assertEqual(results['gt_masks'].width, 120)\n        self.assertEqual(results['gt_seg_map'].shape[:2], (100, 120))\n\n        # test clip_object_border = False\n        transform = FixShapeResize(\n            width=200, height=150, clip_object_border=False)\n        results = transform(copy.deepcopy(self.data_info2))\n        self.assertTrue((results['gt_bboxes'] == np.array([100, 75, 300,\n                                                           225])).all())\n\n        # test only with image\n        transform = FixShapeResize(\n            width=200, height=150, clip_object_border=False)\n        results = transform(self.data_info3)\n        self.assertTupleEqual(results['img'].shape[:2], (150, 200))\n\n        # test geometric transformation with homography matrix\n        transform = FixShapeResize(width=400, height=300)\n        results = transform(copy.deepcopy(self.data_info4))\n        self.assertTrue((bbox_project(\n            copy.deepcopy(self.data_info4['gt_bboxes']),\n            results['homography_matrix']) == results['gt_bboxes']).all())\n\n    def test_resize_with_boxlist(self):\n        data_info1 = copy.deepcopy(self.data_info1)\n        data_info1['gt_bboxes'] = HorizontalBoxes(data_info1['gt_bboxes'])\n        data_info2 = copy.deepcopy(self.data_info2)\n        data_info2['gt_bboxes'] = HorizontalBoxes(data_info2['gt_bboxes'])\n        data_info4 = copy.deepcopy(self.data_info4)\n        data_info4['gt_bboxes'] = HorizontalBoxes(data_info4['gt_bboxes'])\n        # test keep_ratio is True\n        transform = FixShapeResize(width=100, height=200, keep_ratio=True)\n        results = transform(copy.deepcopy(data_info1))\n        self.assertEqual(results['img_shape'], (200, 100))\n        self.assertEqual(results['scale_factor'], (100 / 300, 100 / 300))\n\n        # test resize_bboxes/seg/masks\n        transform = FixShapeResize(width=150, height=200, keep_ratio=False)\n        results = transform(copy.deepcopy(data_info1))\n        self.assertTrue(\n            (results['gt_bboxes'].numpy() == np.array([[0, 0, 56,\n                                                        133]])).all())\n        self.assertEqual(results['gt_masks'].height, 200)\n        self.assertEqual(results['gt_masks'].width, 150)\n        self.assertEqual(results['gt_seg_map'].shape[:2], (200, 150))\n\n        # test clip_object_border = False\n        transform = FixShapeResize(\n            width=200, height=150, clip_object_border=False)\n        results = transform(copy.deepcopy(data_info2))\n        self.assertTrue(\n            (results['gt_bboxes'].numpy() == np.array([100, 75, 300,\n                                                       225])).all())\n\n        # test only with image\n        transform = FixShapeResize(\n            width=200, height=150, clip_object_border=False)\n        results = transform(self.data_info3)\n        self.assertTupleEqual(results['img'].shape[:2], (150, 200))\n\n        # test geometric transformation with homography matrix\n        transform = FixShapeResize(width=400, height=300)\n        results = transform(copy.deepcopy(data_info4))\n        self.assertTrue((bbox_project(\n            copy.deepcopy(self.data_info4['gt_bboxes']),\n            results['homography_matrix']) == results['gt_bboxes'].numpy()\n                         ).all())\n\n    def test_repr(self):\n        transform = FixShapeResize(width=100, height=50, keep_ratio=True)\n        self.assertEqual(\n            repr(transform), ('FixShapeResize(width=100, height=50, '\n                              'keep_ratio=True, '\n                              'clip_object_border=True), backend=cv2), '\n                              'interpolation=bilinear)'))\n\n\nclass TestRandomFlip(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.results1 = {\n            'img': np.random.random((224, 224, 3)),\n            'gt_bboxes': np.array([[0, 1, 100, 101]], dtype=np.float32),\n            'gt_masks':\n            BitmapMasks(rng.rand(1, 224, 224), height=224, width=224),\n            'gt_seg_map': np.random.random((224, 224))\n        }\n\n        self.results2 = {'img': self.results1['img']}\n\n    def test_transform(self):\n        # test with image, gt_bboxes, gt_masks, gt_seg_map\n        transform = RandomFlip(1.0)\n        results_update = transform.transform(copy.deepcopy(self.results1))\n        self.assertTrue(\n            (results_update['gt_bboxes'] == np.array([[124, 1, 224,\n                                                       101]])).all())\n        # test only with image\n        transform = RandomFlip(1.0)\n        results_update = transform.transform(copy.deepcopy(self.results2))\n        self.assertTrue(\n            (results_update['img'] == self.results2['img'][:, ::-1]).all())\n\n        # test geometric transformation with homography matrix\n        # (1) Horizontal Flip\n        transform = RandomFlip(1.0)\n        results_update = transform.transform(copy.deepcopy(self.results1))\n        bboxes = copy.deepcopy(self.results1['gt_bboxes'])\n        self.assertTrue((bbox_project(\n            bboxes,\n            results_update['homography_matrix']) == results_update['gt_bboxes']\n                         ).all())\n        # (2) Vertical Flip\n        transform = RandomFlip(1.0, direction='vertical')\n        results_update = transform.transform(copy.deepcopy(self.results1))\n        bboxes = copy.deepcopy(self.results1['gt_bboxes'])\n        self.assertTrue((bbox_project(\n            bboxes,\n            results_update['homography_matrix']) == results_update['gt_bboxes']\n                         ).all())\n        # (3) Diagonal Flip\n        transform = RandomFlip(1.0, direction='diagonal')\n        results_update = transform.transform(copy.deepcopy(self.results1))\n        bboxes = copy.deepcopy(self.results1['gt_bboxes'])\n        self.assertTrue((bbox_project(\n            bboxes,\n            results_update['homography_matrix']) == results_update['gt_bboxes']\n                         ).all())\n\n    def test_transform_use_box_type(self):\n        results1 = copy.deepcopy(self.results1)\n        results1['gt_bboxes'] = HorizontalBoxes(results1['gt_bboxes'])\n        # test with image, gt_bboxes, gt_masks, gt_seg_map\n        transform = RandomFlip(1.0)\n        results_update = transform.transform(copy.deepcopy(results1))\n        self.assertTrue((results_update['gt_bboxes'].numpy() == np.array(\n            [[124, 1, 224, 101]])).all())\n\n        # test geometric transformation with homography matrix\n        # (1) Horizontal Flip\n        transform = RandomFlip(1.0)\n        results_update = transform.transform(copy.deepcopy(results1))\n        bboxes = copy.deepcopy(results1['gt_bboxes'].numpy())\n        self.assertTrue((bbox_project(bboxes,\n                                      results_update['homography_matrix']) ==\n                         results_update['gt_bboxes'].numpy()).all())\n        # (2) Vertical Flip\n        transform = RandomFlip(1.0, direction='vertical')\n        results_update = transform.transform(copy.deepcopy(results1))\n        bboxes = copy.deepcopy(results1['gt_bboxes'].numpy())\n        self.assertTrue((bbox_project(bboxes,\n                                      results_update['homography_matrix']) ==\n                         results_update['gt_bboxes'].numpy()).all())\n        # (3) Diagonal Flip\n        transform = RandomFlip(1.0, direction='diagonal')\n        results_update = transform.transform(copy.deepcopy(results1))\n        bboxes = copy.deepcopy(results1['gt_bboxes'].numpy())\n        self.assertTrue((bbox_project(bboxes,\n                                      results_update['homography_matrix']) ==\n                         results_update['gt_bboxes'].numpy()).all())\n\n    def test_repr(self):\n        transform = RandomFlip(0.1)\n        transform_str = str(transform)\n        self.assertIsInstance(transform_str, str)\n\n\nclass TestPad(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.results = {\n            'img': np.random.random((100, 80, 3)),\n            'gt_masks':\n            BitmapMasks(rng.rand(4, 100, 80), height=100, width=80)\n        }\n\n    def test_transform(self):\n        # test pad img/gt_masks with size\n        transform = Pad(size=(120, 110))\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img'].shape[:2], (110, 120))\n        self.assertEqual(results['gt_masks'].masks.shape[1:], (110, 120))\n\n        # test pad img/gt_masks with size_divisor\n        transform = Pad(size_divisor=11)\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img'].shape[:2], (110, 88))\n        self.assertEqual(results['gt_masks'].masks.shape[1:], (110, 88))\n\n        # test pad img/gt_masks with pad_to_square\n        transform = Pad(pad_to_square=True)\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img'].shape[:2], (100, 100))\n        self.assertEqual(results['gt_masks'].masks.shape[1:], (100, 100))\n\n        # test pad img/gt_masks with pad_to_square and size_divisor\n        transform = Pad(pad_to_square=True, size_divisor=11)\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img'].shape[:2], (110, 110))\n        self.assertEqual(results['gt_masks'].masks.shape[1:], (110, 110))\n\n        # test pad img/gt_masks with pad_to_square and size_divisor\n        transform = Pad(pad_to_square=True, size_divisor=11)\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img'].shape[:2], (110, 110))\n        self.assertEqual(results['gt_masks'].masks.shape[1:], (110, 110))\n\n    def test_repr(self):\n        transform = Pad(\n            pad_to_square=True, size_divisor=11, padding_mode='edge')\n        self.assertEqual(\n            repr(transform),\n            ('Pad(size=None, size_divisor=11, pad_to_square=True, '\n             \"pad_val={'img': 0, 'seg': 255}), padding_mode=edge)\"))\n\n\nclass TestMinIoURandomCrop(unittest.TestCase):\n\n    def test_transform(self):\n        results = dict()\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        results['img'] = img\n        results['img_shape'] = img.shape[:2]\n        gt_bboxes = create_random_bboxes(1, results['img_shape'][1],\n                                         results['img_shape'][0])\n        results['gt_labels'] = np.ones(gt_bboxes.shape[0], dtype=np.int64)\n        results['gt_bboxes'] = gt_bboxes\n        transform = MinIoURandomCrop()\n        results = transform.transform(copy.deepcopy(results))\n\n        self.assertEqual(results['gt_labels'].shape[0],\n                         results['gt_bboxes'].shape[0])\n        self.assertEqual(results['gt_labels'].dtype, np.int64)\n        self.assertEqual(results['gt_bboxes'].dtype, np.float32)\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n\n        patch = np.array(\n            [0, 0, results['img_shape'][1], results['img_shape'][0]])\n        ious = bbox_overlaps(patch.reshape(-1, 4),\n                             results['gt_bboxes']).reshape(-1)\n        mode = transform.mode\n        if mode == 1:\n            self.assertTrue(np.equal(results['gt_bboxes'], gt_bboxes).all())\n        else:\n            self.assertTrue((ious >= mode).all())\n\n    def test_transform_use_box_type(self):\n        results = dict()\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        results['img'] = img\n        results['img_shape'] = img.shape[:2]\n        gt_bboxes = create_random_bboxes(1, results['img_shape'][1],\n                                         results['img_shape'][0])\n        results['gt_labels'] = np.ones(gt_bboxes.shape[0], dtype=np.int64)\n        results['gt_bboxes'] = HorizontalBoxes(gt_bboxes)\n        transform = MinIoURandomCrop()\n        results = transform.transform(copy.deepcopy(results))\n\n        self.assertEqual(results['gt_labels'].shape[0],\n                         results['gt_bboxes'].shape[0])\n        self.assertEqual(results['gt_labels'].dtype, np.int64)\n        self.assertEqual(results['gt_bboxes'].dtype, torch.float32)\n\n        patch = np.array(\n            [0, 0, results['img_shape'][1], results['img_shape'][0]])\n        ious = bbox_overlaps(\n            patch.reshape(-1, 4), results['gt_bboxes'].numpy()).reshape(-1)\n        mode = transform.mode\n        if mode == 1:\n            self.assertTrue((results['gt_bboxes'].numpy() == gt_bboxes).all())\n        else:\n            self.assertTrue((ious >= mode).all())\n\n    def test_repr(self):\n        transform = MinIoURandomCrop()\n        self.assertEqual(\n            repr(transform), ('MinIoURandomCrop'\n                              '(min_ious=(0.1, 0.3, 0.5, 0.7, 0.9), '\n                              'min_crop_size=0.3, '\n                              'bbox_clip_border=True)'))\n\n\nclass TestPhotoMetricDistortion(unittest.TestCase):\n\n    def test_transform(self):\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        transform = PhotoMetricDistortion()\n\n        # test uint8 input\n        results = dict()\n        results['img'] = img\n        results = transform.transform(copy.deepcopy(results))\n        self.assertEqual(results['img'].dtype, np.float32)\n\n        # test float32 input\n        results = dict()\n        results['img'] = img.astype(np.float32)\n        results = transform.transform(copy.deepcopy(results))\n        self.assertEqual(results['img'].dtype, np.float32)\n\n    def test_repr(self):\n        transform = PhotoMetricDistortion()\n        self.assertEqual(\n            repr(transform), ('PhotoMetricDistortion'\n                              '(brightness_delta=32, '\n                              'contrast_range=(0.5, 1.5), '\n                              'saturation_range=(0.5, 1.5), '\n                              'hue_delta=18)'))\n\n\nclass TestExpand(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.results = {\n            'img': np.random.random((224, 224, 3)),\n            'img_shape': (224, 224),\n            'gt_bboxes': np.array([[0, 1, 100, 101]]),\n            'gt_masks':\n            BitmapMasks(rng.rand(1, 224, 224), height=224, width=224),\n            'gt_seg_map': np.random.random((224, 224))\n        }\n\n    def test_transform(self):\n        transform = Expand()\n        results = transform.transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n        self.assertEqual(\n            results['img_shape'],\n            (results['gt_masks'].height, results['gt_masks'].width))\n        self.assertEqual(results['img_shape'], results['gt_seg_map'].shape)\n\n    def test_transform_use_box_type(self):\n        results = copy.deepcopy(self.results)\n        results['gt_bboxes'] = HorizontalBoxes(results['gt_bboxes'])\n        transform = Expand()\n        results = transform.transform(results)\n        self.assertEqual(\n            results['img_shape'],\n            (results['gt_masks'].height, results['gt_masks'].width))\n        self.assertEqual(results['img_shape'], results['gt_seg_map'].shape)\n\n    def test_repr(self):\n        transform = Expand()\n        self.assertEqual(\n            repr(transform), ('Expand'\n                              '(mean=(0, 0, 0), to_rgb=True, '\n                              'ratio_range=(1, 4), '\n                              'seg_ignore_label=None, '\n                              'prob=0.5)'))\n\n\nclass TestSegRescale(unittest.TestCase):\n\n    def setUp(self) -> None:\n        seg_map = np.random.randint(0, 255, size=(32, 32), dtype=np.int32)\n        self.results = {'gt_seg_map': seg_map}\n\n    def test_transform(self):\n        # test scale_factor != 1\n        transform = SegRescale(scale_factor=2)\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['gt_seg_map'].shape[:2], (64, 64))\n        # test scale_factor = 1\n        transform = SegRescale(scale_factor=1)\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['gt_seg_map'].shape[:2], (32, 32))\n\n    def test_repr(self):\n        transform = SegRescale(scale_factor=2)\n        self.assertEqual(\n            repr(transform), ('SegRescale(scale_factor=2, backend=cv2)'))\n\n\nclass TestRandomCrop(unittest.TestCase):\n\n    def test_init(self):\n        # test invalid crop_type\n        with self.assertRaisesRegex(ValueError, 'Invalid crop_type'):\n            RandomCrop(crop_size=(10, 10), crop_type='unknown')\n\n        crop_type_list = ['absolute', 'absolute_range']\n        for crop_type in crop_type_list:\n            # test h > 0 and w > 0\n            for crop_size in [(0, 0), (0, 1), (1, 0)]:\n                with self.assertRaises(AssertionError):\n                    RandomCrop(crop_size=crop_size, crop_type=crop_type)\n            # test type(h) = int and type(w) = int\n            for crop_size in [(1.0, 1), (1, 1.0), (1.0, 1.0)]:\n                with self.assertRaises(AssertionError):\n                    RandomCrop(crop_size=crop_size, crop_type=crop_type)\n\n        # test crop_size[0] <= crop_size[1]\n        with self.assertRaises(AssertionError):\n            RandomCrop(crop_size=(10, 5), crop_type='absolute_range')\n\n        # test h in (0, 1] and w in (0, 1]\n        crop_type_list = ['relative_range', 'relative']\n        for crop_type in crop_type_list:\n            for crop_size in [(0, 1), (1, 0), (1.1, 0.5), (0.5, 1.1)]:\n                with self.assertRaises(AssertionError):\n                    RandomCrop(crop_size=crop_size, crop_type=crop_type)\n\n    def test_transform(self):\n        # test relative and absolute crop\n        src_results = {\n            'img': np.random.randint(0, 255, size=(24, 32), dtype=np.int32)\n        }\n        target_shape = (12, 16)\n        for crop_type, crop_size in zip(['relative', 'absolute'], [(0.5, 0.5),\n                                                                   (16, 12)]):\n            transform = RandomCrop(crop_size=crop_size, crop_type=crop_type)\n            results = transform(copy.deepcopy(src_results))\n            print(results['img'].shape[:2])\n            self.assertEqual(results['img'].shape[:2], target_shape)\n\n        # test absolute_range crop\n        transform = RandomCrop(crop_size=(10, 20), crop_type='absolute_range')\n        results = transform(copy.deepcopy(src_results))\n        h, w = results['img'].shape\n        self.assertTrue(10 <= w <= 20)\n        self.assertTrue(10 <= h <= 20)\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n        # test relative_range crop\n        transform = RandomCrop(\n            crop_size=(0.5, 0.5), crop_type='relative_range')\n        results = transform(copy.deepcopy(src_results))\n        h, w = results['img'].shape\n        self.assertTrue(16 <= w <= 32)\n        self.assertTrue(12 <= h <= 24)\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n\n        # test with gt_bboxes, gt_bboxes_labels, gt_ignore_flags,\n        # gt_masks, gt_seg_map, gt_instances_ids\n        img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_bboxes = np.array([[0, 0, 7, 7], [2, 3, 9, 9]], dtype=np.float32)\n        gt_bboxes_labels = np.array([0, 1], dtype=np.int64)\n        gt_ignore_flags = np.array([0, 1], dtype=bool)\n        gt_masks_ = np.zeros((2, 10, 10), np.uint8)\n        gt_masks_[0, 0:7, 0:7] = 1\n        gt_masks_[1, 2:7, 3:8] = 1\n        gt_masks = BitmapMasks(gt_masks_.copy(), height=10, width=10)\n        gt_seg_map = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_instances_ids = np.array([0, 1], dtype=np.int64)\n        src_results = {\n            'img': img,\n            'gt_bboxes': gt_bboxes,\n            'gt_bboxes_labels': gt_bboxes_labels,\n            'gt_ignore_flags': gt_ignore_flags,\n            'gt_masks': gt_masks,\n            'gt_seg_map': gt_seg_map,\n            'gt_instances_ids': gt_instances_ids\n        }\n        transform = RandomCrop(\n            crop_size=(7, 5),\n            allow_negative_crop=False,\n            recompute_bbox=False,\n            bbox_clip_border=True)\n        results = transform(copy.deepcopy(src_results))\n        h, w = results['img'].shape\n        self.assertEqual(h, 5)\n        self.assertEqual(w, 7)\n        self.assertEqual(results['gt_bboxes'].shape[0], 2)\n        self.assertEqual(results['gt_bboxes_labels'].shape[0], 2)\n        self.assertEqual(results['gt_ignore_flags'].shape[0], 2)\n        self.assertTupleEqual(results['gt_seg_map'].shape[:2], (5, 7))\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n        self.assertEqual(results['gt_instances_ids'].shape[0], 2)\n\n        # test geometric transformation with homography matrix\n        bboxes = copy.deepcopy(src_results['gt_bboxes'])\n        self.assertTrue((bbox_project(bboxes, results['homography_matrix'],\n                                      (5, 7)) == results['gt_bboxes']).all())\n\n        # test recompute_bbox = True\n        gt_masks_ = np.zeros((2, 10, 10), np.uint8)\n        gt_masks = BitmapMasks(gt_masks_.copy(), height=10, width=10)\n        gt_bboxes = np.array([[0.1, 0.1, 0.2, 0.2]])\n        src_results = {\n            'img': img,\n            'gt_bboxes': gt_bboxes,\n            'gt_masks': gt_masks\n        }\n        target_gt_bboxes = np.zeros((1, 4), dtype=np.float32)\n        transform = RandomCrop(\n            crop_size=(10, 11),\n            allow_negative_crop=False,\n            recompute_bbox=True,\n            bbox_clip_border=True)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue((results['gt_bboxes'] == target_gt_bboxes).all())\n\n        # test bbox_clip_border = False\n        src_results = {'img': img, 'gt_bboxes': gt_bboxes}\n        transform = RandomCrop(\n            crop_size=(10, 11),\n            allow_negative_crop=False,\n            recompute_bbox=True,\n            bbox_clip_border=False)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue(\n            (results['gt_bboxes'] == src_results['gt_bboxes']).all())\n\n        # test the crop does not contain any gt-bbox\n        # allow_negative_crop = False\n        img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_bboxes = np.zeros((0, 4), dtype=np.float32)\n        src_results = {'img': img, 'gt_bboxes': gt_bboxes}\n        transform = RandomCrop(crop_size=(5, 3), allow_negative_crop=False)\n        results = transform(copy.deepcopy(src_results))\n        self.assertIsNone(results)\n\n        # allow_negative_crop = True\n        img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_bboxes = np.zeros((0, 4), dtype=np.float32)\n        src_results = {'img': img, 'gt_bboxes': gt_bboxes}\n        transform = RandomCrop(crop_size=(5, 3), allow_negative_crop=True)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue(isinstance(results, dict))\n\n    def test_transform_use_box_type(self):\n        # test with gt_bboxes, gt_bboxes_labels, gt_ignore_flags,\n        # gt_masks, gt_seg_map\n        img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_bboxes = np.array([[0, 0, 7, 7], [2, 3, 9, 9]], dtype=np.float32)\n        gt_bboxes_labels = np.array([0, 1], dtype=np.int64)\n        gt_ignore_flags = np.array([0, 1], dtype=bool)\n        gt_masks_ = np.zeros((2, 10, 10), np.uint8)\n        gt_masks_[0, 0:7, 0:7] = 1\n        gt_masks_[1, 2:7, 3:8] = 1\n        gt_masks = BitmapMasks(gt_masks_.copy(), height=10, width=10)\n        gt_seg_map = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_instances_ids = np.array([0, 1], dtype=np.int64)\n        src_results = {\n            'img': img,\n            'gt_bboxes': HorizontalBoxes(gt_bboxes),\n            'gt_bboxes_labels': gt_bboxes_labels,\n            'gt_ignore_flags': gt_ignore_flags,\n            'gt_masks': gt_masks,\n            'gt_seg_map': gt_seg_map,\n            'gt_instances_ids': gt_instances_ids\n        }\n        transform = RandomCrop(\n            crop_size=(7, 5),\n            allow_negative_crop=False,\n            recompute_bbox=False,\n            bbox_clip_border=True)\n        results = transform(copy.deepcopy(src_results))\n        h, w = results['img'].shape\n        self.assertEqual(h, 5)\n        self.assertEqual(w, 7)\n        self.assertEqual(results['gt_bboxes'].shape[0], 2)\n        self.assertEqual(results['gt_bboxes_labels'].shape[0], 2)\n        self.assertEqual(results['gt_ignore_flags'].shape[0], 2)\n        self.assertTupleEqual(results['gt_seg_map'].shape[:2], (5, 7))\n        self.assertEqual(results['gt_instances_ids'].shape[0], 2)\n\n        # test geometric transformation with homography matrix\n        bboxes = copy.deepcopy(src_results['gt_bboxes'].numpy())\n        print(bboxes, results['gt_bboxes'])\n        self.assertTrue(\n            (bbox_project(bboxes, results['homography_matrix'],\n                          (5, 7)) == results['gt_bboxes'].numpy()).all())\n\n        # test recompute_bbox = True\n        gt_masks_ = np.zeros((2, 10, 10), np.uint8)\n        gt_masks = BitmapMasks(gt_masks_.copy(), height=10, width=10)\n        gt_bboxes = HorizontalBoxes(np.array([[0.1, 0.1, 0.2, 0.2]]))\n        src_results = {\n            'img': img,\n            'gt_bboxes': gt_bboxes,\n            'gt_masks': gt_masks\n        }\n        target_gt_bboxes = np.zeros((1, 4), dtype=np.float32)\n        transform = RandomCrop(\n            crop_size=(10, 11),\n            allow_negative_crop=False,\n            recompute_bbox=True,\n            bbox_clip_border=True)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue(\n            (results['gt_bboxes'].numpy() == target_gt_bboxes).all())\n\n        # test bbox_clip_border = False\n        src_results = {'img': img, 'gt_bboxes': gt_bboxes}\n        transform = RandomCrop(\n            crop_size=(10, 10),\n            allow_negative_crop=False,\n            recompute_bbox=True,\n            bbox_clip_border=False)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue(\n            (results['gt_bboxes'].numpy() == src_results['gt_bboxes'].numpy()\n             ).all())\n\n        # test the crop does not contain any gt-bbox\n        # allow_negative_crop = False\n        img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_bboxes = HorizontalBoxes(np.zeros((0, 4), dtype=np.float32))\n        src_results = {'img': img, 'gt_bboxes': gt_bboxes}\n        transform = RandomCrop(crop_size=(5, 2), allow_negative_crop=False)\n        results = transform(copy.deepcopy(src_results))\n        self.assertIsNone(results)\n\n        # allow_negative_crop = True\n        img = np.random.randint(0, 255, size=(10, 10), dtype=np.uint8)\n        gt_bboxes = HorizontalBoxes(np.zeros((0, 4), dtype=np.float32))\n        src_results = {'img': img, 'gt_bboxes': gt_bboxes}\n        transform = RandomCrop(crop_size=(5, 2), allow_negative_crop=True)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue(isinstance(results, dict))\n\n    def test_repr(self):\n        crop_type = 'absolute'\n        crop_size = (10, 5)\n        allow_negative_crop = False\n        recompute_bbox = True\n        bbox_clip_border = False\n        transform = RandomCrop(\n            crop_size=crop_size,\n            crop_type=crop_type,\n            allow_negative_crop=allow_negative_crop,\n            recompute_bbox=recompute_bbox,\n            bbox_clip_border=bbox_clip_border)\n        self.assertEqual(\n            repr(transform),\n            f'RandomCrop(crop_size={crop_size}, crop_type={crop_type}, '\n            f'allow_negative_crop={allow_negative_crop}, '\n            f'recompute_bbox={recompute_bbox}, '\n            f'bbox_clip_border={bbox_clip_border})')\n\n\nclass TestCutOut(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        self.results = {'img': img}\n\n    def test_transform(self):\n        # test n_holes\n        with self.assertRaises(AssertionError):\n            transform = CutOut(n_holes=(5, 3), cutout_shape=(8, 8))\n        with self.assertRaises(AssertionError):\n            transform = CutOut(n_holes=(3, 4, 5), cutout_shape=(8, 8))\n\n        # test cutout_shape and cutout_ratio\n        with self.assertRaises(AssertionError):\n            transform = CutOut(n_holes=1, cutout_shape=8)\n        with self.assertRaises(AssertionError):\n            transform = CutOut(n_holes=1, cutout_ratio=0.2)\n\n        # either of cutout_shape and cutout_ratio should be given\n        with self.assertRaises(AssertionError):\n            transform = CutOut(n_holes=1)\n        with self.assertRaises(AssertionError):\n            transform = CutOut(\n                n_holes=1, cutout_shape=(2, 2), cutout_ratio=(0.4, 0.4))\n\n        transform = CutOut(n_holes=1, cutout_shape=(10, 10))\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].sum() < self.results['img'].sum())\n\n        transform = CutOut(\n            n_holes=(2, 4),\n            cutout_shape=[(10, 10), (15, 15)],\n            fill_in=(255, 255, 255))\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].sum() > self.results['img'].sum())\n\n        transform = CutOut(\n            n_holes=1, cutout_ratio=(0.8, 0.8), fill_in=(255, 255, 255))\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].sum() > self.results['img'].sum())\n\n    def test_repr(self):\n        transform = CutOut(n_holes=1, cutout_shape=(10, 10))\n        self.assertEqual(\n            repr(transform), ('CutOut(n_holes=(1, 1), '\n                              'cutout_shape=[(10, 10)], '\n                              'fill_in=(0, 0, 0))'))\n        transform = CutOut(\n            n_holes=1, cutout_ratio=(0.8, 0.8), fill_in=(255, 255, 255))\n        self.assertEqual(\n            repr(transform), ('CutOut(n_holes=(1, 1), '\n                              'cutout_ratio=[(0.8, 0.8)], '\n                              'fill_in=(255, 255, 255))'))\n\n\nclass TestMosaic(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.results = {\n            'img':\n            np.random.random((224, 224, 3)),\n            'img_shape': (224, 224),\n            'gt_bboxes_labels':\n            np.array([1, 2, 3], dtype=np.int64),\n            'gt_bboxes':\n            np.array([[10, 10, 20, 20], [20, 20, 40, 40], [40, 40, 80, 80]],\n                     dtype=np.float32),\n            'gt_ignore_flags':\n            np.array([0, 0, 1], dtype=bool),\n            'gt_masks':\n            BitmapMasks(rng.rand(3, 224, 224), height=224, width=224),\n        }\n\n    def test_transform(self):\n        # test assertion for invalid img_scale\n        with self.assertRaises(AssertionError):\n            transform = Mosaic(img_scale=640)\n\n        # test assertion for invalid probability\n        with self.assertRaises(AssertionError):\n            transform = Mosaic(prob=1.5)\n\n        transform = Mosaic(img_scale=(12, 10))\n        # test assertion for invalid mix_results\n        with self.assertRaises(AssertionError):\n            results = transform(copy.deepcopy(self.results))\n\n        self.results['mix_results'] = [copy.deepcopy(self.results)] * 3\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].shape[:2] == (20, 24))\n        self.assertTrue(results['gt_bboxes_labels'].shape[0] ==\n                        results['gt_bboxes'].shape[0])\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == np.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n\n    def test_transform_with_no_gt(self):\n        self.results['gt_bboxes'] = np.empty((0, 4), dtype=np.float32)\n        self.results['gt_bboxes_labels'] = np.empty((0, ), dtype=np.int64)\n        self.results['gt_ignore_flags'] = np.empty((0, ), dtype=bool)\n        transform = Mosaic(img_scale=(12, 10))\n        self.results['mix_results'] = [copy.deepcopy(self.results)] * 3\n        results = transform(copy.deepcopy(self.results))\n        self.assertIsInstance(results, dict)\n        self.assertTrue(results['img'].shape[:2] == (20, 24))\n        self.assertTrue(\n            results['gt_bboxes_labels'].shape[0] == results['gt_bboxes'].\n            shape[0] == results['gt_ignore_flags'].shape[0] == 0)\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == np.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n\n    def test_transform_use_box_type(self):\n        transform = Mosaic(img_scale=(12, 10))\n        results = copy.deepcopy(self.results)\n        results['gt_bboxes'] = HorizontalBoxes(results['gt_bboxes'])\n        results['mix_results'] = [results] * 3\n        results = transform(results)\n        self.assertTrue(results['img'].shape[:2] == (20, 24))\n        self.assertTrue(results['gt_bboxes_labels'].shape[0] ==\n                        results['gt_bboxes'].shape[0])\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == torch.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n\n    def test_repr(self):\n        transform = Mosaic(img_scale=(640, 640), )\n        self.assertEqual(\n            repr(transform), ('Mosaic(img_scale=(640, 640), '\n                              'center_ratio_range=(0.5, 1.5), '\n                              'pad_val=114.0, '\n                              'prob=1.0)'))\n\n\nclass TestMixUp(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.results = {\n            'img':\n            np.random.random((224, 224, 3)),\n            'img_shape': (224, 224),\n            'gt_bboxes_labels':\n            np.array([1, 2, 3], dtype=np.int64),\n            'gt_bboxes':\n            np.array([[10, 10, 20, 20], [20, 20, 40, 40], [40, 40, 80, 80]],\n                     dtype=np.float32),\n            'gt_ignore_flags':\n            np.array([0, 0, 1], dtype=bool),\n            'gt_masks':\n            BitmapMasks(rng.rand(3, 224, 224), height=224, width=224),\n        }\n\n    def test_transform(self):\n        # test assertion for invalid img_scale\n        with self.assertRaises(AssertionError):\n            transform = MixUp(img_scale=640)\n\n        transform = MixUp(img_scale=(12, 10))\n        # test assertion for invalid mix_results\n        with self.assertRaises(AssertionError):\n            results = transform(copy.deepcopy(self.results))\n\n        with self.assertRaises(AssertionError):\n            self.results['mix_results'] = [copy.deepcopy(self.results)] * 2\n            results = transform(copy.deepcopy(self.results))\n\n        self.results['mix_results'] = [copy.deepcopy(self.results)]\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].shape[:2] == (224, 224))\n        self.assertTrue(results['gt_bboxes_labels'].shape[0] ==\n                        results['gt_bboxes'].shape[0])\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == np.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n\n    def test_transform_use_box_type(self):\n        results = copy.deepcopy(self.results)\n        results['gt_bboxes'] = HorizontalBoxes(results['gt_bboxes'])\n\n        transform = MixUp(img_scale=(12, 10))\n        results['mix_results'] = [results]\n        results = transform(results)\n        self.assertTrue(results['img'].shape[:2] == (224, 224))\n        self.assertTrue(results['gt_bboxes_labels'].shape[0] ==\n                        results['gt_bboxes'].shape[0])\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == torch.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n\n    def test_repr(self):\n        transform = MixUp(\n            img_scale=(640, 640),\n            ratio_range=(0.8, 1.6),\n            pad_val=114.0,\n        )\n        self.assertEqual(\n            repr(transform), ('MixUp(dynamic_scale=(640, 640), '\n                              'ratio_range=(0.8, 1.6), '\n                              'flip_ratio=0.5, '\n                              'pad_val=114.0, '\n                              'max_iters=15, '\n                              'bbox_clip_border=True)'))\n\n\nclass TestRandomAffine(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.results = {\n            'img':\n            np.random.random((224, 224, 3)),\n            'img_shape': (224, 224),\n            'gt_bboxes_labels':\n            np.array([1, 2, 3], dtype=np.int64),\n            'gt_bboxes':\n            np.array([[10, 10, 20, 20], [20, 20, 40, 40], [40, 40, 80, 80]],\n                     dtype=np.float32),\n            'gt_ignore_flags':\n            np.array([0, 0, 1], dtype=bool),\n        }\n\n    def test_transform(self):\n        # test assertion for invalid translate_ratio\n        with self.assertRaises(AssertionError):\n            transform = RandomAffine(max_translate_ratio=1.5)\n\n        # test assertion for invalid scaling_ratio_range\n        with self.assertRaises(AssertionError):\n            transform = RandomAffine(scaling_ratio_range=(1.5, 0.5))\n\n        with self.assertRaises(AssertionError):\n            transform = RandomAffine(scaling_ratio_range=(0, 0.5))\n\n        transform = RandomAffine()\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].shape[:2] == (224, 224))\n        self.assertTrue(results['gt_bboxes_labels'].shape[0] ==\n                        results['gt_bboxes'].shape[0])\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == np.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n\n    def test_transform_use_box_type(self):\n        results = copy.deepcopy(self.results)\n        results['gt_bboxes'] = HorizontalBoxes(results['gt_bboxes'])\n\n        transform = RandomAffine()\n        results = transform(copy.deepcopy(results))\n        self.assertTrue(results['img'].shape[:2] == (224, 224))\n        self.assertTrue(results['gt_bboxes_labels'].shape[0] ==\n                        results['gt_bboxes'].shape[0])\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == torch.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n\n    def test_repr(self):\n        transform = RandomAffine(\n            scaling_ratio_range=(0.1, 2),\n            border=(-320, -320),\n        )\n        self.assertEqual(\n            repr(transform), ('RandomAffine(max_rotate_degree=10.0, '\n                              'max_translate_ratio=0.1, '\n                              'scaling_ratio_range=(0.1, 2), '\n                              'max_shear_degree=2.0, '\n                              'border=(-320, -320), '\n                              'border_val=(114, 114, 114), '\n                              'bbox_clip_border=True)'))\n\n\nclass TestYOLOXHSVRandomAug(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        self.results = {\n            'img':\n            img,\n            'img_shape': (224, 224),\n            'gt_bboxes_labels':\n            np.array([1, 2, 3], dtype=np.int64),\n            'gt_bboxes':\n            np.array([[10, 10, 20, 20], [20, 20, 40, 40], [40, 40, 80, 80]],\n                     dtype=np.float32),\n            'gt_ignore_flags':\n            np.array([0, 0, 1], dtype=bool),\n        }\n\n    def test_transform(self):\n        transform = YOLOXHSVRandomAug()\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(\n            results['img'].shape[:2] == self.results['img'].shape[:2])\n        self.assertTrue(results['gt_bboxes_labels'].shape[0] ==\n                        results['gt_bboxes'].shape[0])\n        self.assertTrue(results['gt_bboxes_labels'].dtype == np.int64)\n        self.assertTrue(results['gt_bboxes'].dtype == np.float32)\n        self.assertTrue(results['gt_ignore_flags'].dtype == bool)\n\n    def test_repr(self):\n        transform = YOLOXHSVRandomAug()\n        self.assertEqual(\n            repr(transform), ('YOLOXHSVRandomAug(hue_delta=5, '\n                              'saturation_delta=30, '\n                              'value_delta=30)'))\n\n\nclass TestRandomCenterCropPad(unittest.TestCase):\n\n    def test_init(self):\n        # test assertion for invalid crop_size while test_mode=False\n        with self.assertRaises(AssertionError):\n            RandomCenterCropPad(\n                crop_size=(-1, 0), test_mode=False, test_pad_mode=None)\n\n        # test assertion for invalid ratios while test_mode=False\n        with self.assertRaises(AssertionError):\n            RandomCenterCropPad(\n                crop_size=(511, 511),\n                ratios=(1.0, 1.0),\n                test_mode=False,\n                test_pad_mode=None)\n\n        # test assertion for invalid mean, std and to_rgb\n        with self.assertRaises(AssertionError):\n            RandomCenterCropPad(\n                crop_size=(511, 511),\n                mean=None,\n                std=None,\n                to_rgb=None,\n                test_mode=False,\n                test_pad_mode=None)\n\n        # test assertion for invalid crop_size while test_mode=True\n        with self.assertRaises(AssertionError):\n            RandomCenterCropPad(\n                crop_size=(511, 511),\n                ratios=None,\n                border=None,\n                mean=[123.675, 116.28, 103.53],\n                std=[58.395, 57.12, 57.375],\n                to_rgb=True,\n                test_mode=True,\n                test_pad_mode=('logical_or', 127))\n\n        # test assertion for invalid ratios while test_mode=True\n        with self.assertRaises(AssertionError):\n            RandomCenterCropPad(\n                crop_size=None,\n                ratios=(0.9, 1.0, 1.1),\n                border=None,\n                mean=[123.675, 116.28, 103.53],\n                std=[58.395, 57.12, 57.375],\n                to_rgb=True,\n                test_mode=True,\n                test_pad_mode=('logical_or', 127))\n\n        # test assertion for invalid border while test_mode=True\n        with self.assertRaises(AssertionError):\n            RandomCenterCropPad(\n                crop_size=None,\n                ratios=None,\n                border=128,\n                mean=[123.675, 116.28, 103.53],\n                std=[58.395, 57.12, 57.375],\n                to_rgb=True,\n                test_mode=True,\n                test_pad_mode=('logical_or', 127))\n\n        # test assertion for invalid test_pad_mode while test_mode=True\n        with self.assertRaises(AssertionError):\n            RandomCenterCropPad(\n                crop_size=None,\n                ratios=None,\n                border=None,\n                mean=[123.675, 116.28, 103.53],\n                std=[58.395, 57.12, 57.375],\n                to_rgb=True,\n                test_mode=True,\n                test_pad_mode=('do_nothing', 100))\n\n    def test_transform(self):\n        results = dict(\n            img_path=osp.join(osp.dirname(__file__), '../../data/color.jpg'))\n\n        load = LoadImageFromFile(to_float32=True)\n        results = load(results)\n        test_results = copy.deepcopy(results)\n\n        h, w = results['img_shape']\n        gt_bboxes = create_random_bboxes(4, w, h)\n        gt_bboxes_labels = np.array([1, 2, 3, 1], dtype=np.int64)\n        gt_ignore_flags = np.array([0, 0, 1, 1], dtype=bool)\n        results['gt_bboxes'] = gt_bboxes\n        results['gt_bboxes_labels'] = gt_bboxes_labels\n        results['gt_ignore_flags'] = gt_ignore_flags\n        crop_module = RandomCenterCropPad(\n            crop_size=(w - 20, h - 20),\n            ratios=(1.0, ),\n            border=128,\n            mean=[123.675, 116.28, 103.53],\n            std=[58.395, 57.12, 57.375],\n            to_rgb=True,\n            test_mode=False,\n            test_pad_mode=None)\n        train_results = crop_module(results)\n        assert train_results['img'].shape[:2] == (h - 20, w - 20)\n        # All bboxes should be reserved after crop\n        assert train_results['img_shape'][:2] == (h - 20, w - 20)\n        assert train_results['gt_bboxes'].shape[0] == 4\n        assert train_results['gt_bboxes'].dtype == np.float32\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n\n        crop_module = RandomCenterCropPad(\n            crop_size=None,\n            ratios=None,\n            border=None,\n            mean=[123.675, 116.28, 103.53],\n            std=[58.395, 57.12, 57.375],\n            to_rgb=True,\n            test_mode=True,\n            test_pad_mode=('logical_or', 127))\n        test_results = crop_module(test_results)\n        assert test_results['img'].shape[:2] == (h | 127, w | 127)\n        assert test_results['img_shape'][:2] == (h | 127, w | 127)\n        assert 'border' in test_results\n\n    def test_transform_use_box_type(self):\n        results = dict(\n            img_path=osp.join(osp.dirname(__file__), '../../data/color.jpg'))\n\n        load = LoadImageFromFile(to_float32=True)\n        results = load(results)\n        test_results = copy.deepcopy(results)\n\n        h, w = results['img_shape']\n        gt_bboxes = create_random_bboxes(4, w, h)\n        gt_bboxes_labels = np.array([1, 2, 3, 1], dtype=np.int64)\n        gt_ignore_flags = np.array([0, 0, 1, 1], dtype=bool)\n        results['gt_bboxes'] = HorizontalBoxes(gt_bboxes)\n        results['gt_bboxes_labels'] = gt_bboxes_labels\n        results['gt_ignore_flags'] = gt_ignore_flags\n        crop_module = RandomCenterCropPad(\n            crop_size=(w - 20, h - 20),\n            ratios=(1.0, ),\n            border=128,\n            mean=[123.675, 116.28, 103.53],\n            std=[58.395, 57.12, 57.375],\n            to_rgb=True,\n            test_mode=False,\n            test_pad_mode=None)\n        train_results = crop_module(results)\n        assert train_results['img'].shape[:2] == (h - 20, w - 20)\n        # All bboxes should be reserved after crop\n        assert train_results['img_shape'][:2] == (h - 20, w - 20)\n        assert train_results['gt_bboxes'].shape[0] == 4\n        assert train_results['gt_bboxes'].dtype == torch.float32\n\n        crop_module = RandomCenterCropPad(\n            crop_size=None,\n            ratios=None,\n            border=None,\n            mean=[123.675, 116.28, 103.53],\n            std=[58.395, 57.12, 57.375],\n            to_rgb=True,\n            test_mode=True,\n            test_pad_mode=('logical_or', 127))\n        test_results = crop_module(test_results)\n        assert test_results['img'].shape[:2] == (h | 127, w | 127)\n        assert test_results['img_shape'][:2] == (h | 127, w | 127)\n        assert 'border' in test_results\n\n\nclass TestCopyPaste(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        h, w, _ = img.shape\n        dst_bboxes = np.array([[0.2 * w, 0.2 * h, 0.4 * w, 0.4 * h],\n                               [0.5 * w, 0.5 * h, 0.6 * w, 0.6 * h]],\n                              dtype=np.float32)\n        src_bboxes = np.array([[0.1 * w, 0.1 * h, 0.3 * w, 0.5 * h],\n                               [0.4 * w, 0.4 * h, 0.7 * w, 0.7 * h],\n                               [0.8 * w, 0.8 * h, 0.9 * w, 0.9 * h]],\n                              dtype=np.float32)\n\n        self.dst_results = {\n            'img': img.copy(),\n            'gt_bboxes': dst_bboxes,\n            'gt_bboxes_labels': np.ones(dst_bboxes.shape[0], dtype=np.int64),\n            'gt_masks': create_full_masks(dst_bboxes, w, h),\n            'gt_ignore_flags': np.array([0, 1], dtype=bool),\n        }\n        self.src_results = {\n            'img': img.copy(),\n            'gt_bboxes': src_bboxes,\n            'gt_bboxes_labels':\n            np.ones(src_bboxes.shape[0], dtype=np.int64) * 2,\n            'gt_masks': create_full_masks(src_bboxes, w, h),\n            'gt_ignore_flags': np.array([0, 0, 1], dtype=bool),\n        }\n\n    def test_transform(self):\n        transform = CopyPaste(selected=False)\n        # test assertion for invalid mix_results\n        with self.assertRaises(AssertionError):\n            results = transform(copy.deepcopy(self.dst_results))\n\n        results = copy.deepcopy(self.dst_results)\n        results['mix_results'] = [copy.deepcopy(self.src_results)]\n        results = transform(results)\n\n        self.assertEqual(results['img'].shape[:2],\n                         self.dst_results['img'].shape[:2])\n\n        # one object of destination image is totally occluded\n        self.assertEqual(\n            results['gt_bboxes'].shape[0],\n            self.dst_results['gt_bboxes'].shape[0] +\n            self.src_results['gt_bboxes'].shape[0] - 1)\n        self.assertEqual(\n            results['gt_bboxes_labels'].shape[0],\n            self.dst_results['gt_bboxes_labels'].shape[0] +\n            self.src_results['gt_bboxes_labels'].shape[0] - 1)\n        self.assertEqual(\n            results['gt_masks'].masks.shape[0],\n            self.dst_results['gt_masks'].masks.shape[0] +\n            self.src_results['gt_masks'].masks.shape[0] - 1)\n        self.assertEqual(\n            results['gt_ignore_flags'].shape[0],\n            self.dst_results['gt_ignore_flags'].shape[0] +\n            self.src_results['gt_ignore_flags'].shape[0] - 1)\n\n        # the object of destination image is partially occluded\n        ori_bbox = self.dst_results['gt_bboxes'][0]\n        occ_bbox = results['gt_bboxes'][0]\n        ori_mask = self.dst_results['gt_masks'].masks[0]\n        occ_mask = results['gt_masks'].masks[0]\n        self.assertTrue(ori_mask.sum() > occ_mask.sum())\n        self.assertTrue(\n            np.all(np.abs(occ_bbox - ori_bbox) <= transform.bbox_occluded_thr)\n            or occ_mask.sum() > transform.mask_occluded_thr)\n\n        # test copypaste with selected objects\n        transform = CopyPaste()\n        results = copy.deepcopy(self.dst_results)\n        results['mix_results'] = [copy.deepcopy(self.src_results)]\n        results = transform(results)\n\n        # test copypaste with an empty source image\n        results = copy.deepcopy(self.dst_results)\n        valid_inds = [False] * self.src_results['gt_bboxes'].shape[0]\n        results['mix_results'] = [{\n            'img':\n            self.src_results['img'].copy(),\n            'gt_bboxes':\n            self.src_results['gt_bboxes'][valid_inds],\n            'gt_bboxes_labels':\n            self.src_results['gt_bboxes_labels'][valid_inds],\n            'gt_masks':\n            self.src_results['gt_masks'][valid_inds],\n            'gt_ignore_flags':\n            self.src_results['gt_ignore_flags'][valid_inds],\n        }]\n        results = transform(results)\n\n        # test copypaste with an empty mask results\n        transform = CopyPaste()\n        results = copy.deepcopy(self.dst_results)\n        results = {k: v for k, v in results.items() if 'mask' not in k}\n        results['mix_results'] = [copy.deepcopy(self.src_results)]\n        with self.assertRaises(RuntimeError):\n            results = transform(results)\n\n        # test copypaste with boxes as masks\n        transform = CopyPaste(paste_by_box=True)\n        results = copy.deepcopy(self.dst_results)\n        results = {k: v for k, v in results.items() if 'mask' not in k}\n        src_results = copy.deepcopy(self.src_results)\n        src_results = {k: v for k, v in src_results.items() if 'mask' not in k}\n        results['mix_results'] = [src_results]\n        results = transform(results)\n\n        self.assertEqual(results['img'].shape[:2],\n                         self.dst_results['img'].shape[:2])\n\n    def test_transform_use_box_type(self):\n        src_results = copy.deepcopy(self.src_results)\n        src_results['gt_bboxes'] = HorizontalBoxes(src_results['gt_bboxes'])\n        dst_results = copy.deepcopy(self.dst_results)\n        dst_results['gt_bboxes'] = HorizontalBoxes(dst_results['gt_bboxes'])\n        transform = CopyPaste(selected=False)\n\n        results = copy.deepcopy(dst_results)\n        results['mix_results'] = [copy.deepcopy(src_results)]\n        results = transform(results)\n\n        self.assertEqual(results['img'].shape[:2],\n                         self.dst_results['img'].shape[:2])\n\n        # one object of destination image is totally occluded\n        self.assertEqual(\n            results['gt_bboxes'].shape[0],\n            self.dst_results['gt_bboxes'].shape[0] +\n            self.src_results['gt_bboxes'].shape[0] - 1)\n        self.assertEqual(\n            results['gt_bboxes_labels'].shape[0],\n            self.dst_results['gt_bboxes_labels'].shape[0] +\n            self.src_results['gt_bboxes_labels'].shape[0] - 1)\n        self.assertEqual(\n            results['gt_masks'].masks.shape[0],\n            self.dst_results['gt_masks'].masks.shape[0] +\n            self.src_results['gt_masks'].masks.shape[0] - 1)\n        self.assertEqual(\n            results['gt_ignore_flags'].shape[0],\n            self.dst_results['gt_ignore_flags'].shape[0] +\n            self.src_results['gt_ignore_flags'].shape[0] - 1)\n\n        # the object of destination image is partially occluded\n        ori_bbox = dst_results['gt_bboxes'][0].numpy()\n        occ_bbox = results['gt_bboxes'][0].numpy()\n        ori_mask = dst_results['gt_masks'].masks[0]\n        occ_mask = results['gt_masks'].masks[0]\n        self.assertTrue(ori_mask.sum() > occ_mask.sum())\n        self.assertTrue(\n            np.all(np.abs(occ_bbox - ori_bbox) <= transform.bbox_occluded_thr)\n            or occ_mask.sum() > transform.mask_occluded_thr)\n\n        # test copypaste with selected objects\n        transform = CopyPaste()\n        results = copy.deepcopy(dst_results)\n        results['mix_results'] = [copy.deepcopy(src_results)]\n        results = transform(results)\n\n        # test copypaste with an empty source image\n        results = copy.deepcopy(dst_results)\n        valid_inds = [False] * self.src_results['gt_bboxes'].shape[0]\n        results['mix_results'] = [{\n            'img':\n            src_results['img'].copy(),\n            'gt_bboxes':\n            src_results['gt_bboxes'][valid_inds],\n            'gt_bboxes_labels':\n            src_results['gt_bboxes_labels'][valid_inds],\n            'gt_masks':\n            src_results['gt_masks'][valid_inds],\n            'gt_ignore_flags':\n            src_results['gt_ignore_flags'][valid_inds],\n        }]\n        results = transform(results)\n\n    def test_repr(self):\n        transform = CopyPaste()\n        self.assertEqual(\n            repr(transform), ('CopyPaste(max_num_pasted=100, '\n                              'bbox_occluded_thr=10, '\n                              'mask_occluded_thr=300, '\n                              'selected=True), '\n                              'paste_by_box=False)'))\n\n\nclass TestAlbu(unittest.TestCase):\n\n    @unittest.skipIf(albumentations is None, 'albumentations is not installed')\n    def test_transform(self):\n        results = dict(\n            img_path=osp.join(osp.dirname(__file__), '../../data/color.jpg'))\n\n        # Define simple pipeline\n        load = dict(type='LoadImageFromFile')\n        load = TRANSFORMS.build(load)\n\n        albu_transform = dict(\n            type='Albu', transforms=[dict(type='ChannelShuffle', p=1)])\n        albu_transform = TRANSFORMS.build(albu_transform)\n\n        # Execute transforms\n        results = load(results)\n        results = albu_transform(results)\n\n        self.assertEqual(results['img'].dtype, np.uint8)\n\n        # test bbox\n        albu_transform = dict(\n            type='Albu',\n            transforms=[dict(type='ChannelShuffle', p=1)],\n            bbox_params=dict(\n                type='BboxParams',\n                format='pascal_voc',\n                label_fields=['gt_bboxes_labels', 'gt_ignore_flags']),\n            keymap={\n                'img': 'image',\n                'gt_bboxes': 'bboxes'\n            })\n        albu_transform = TRANSFORMS.build(albu_transform)\n        results = {\n            'img':\n            np.random.random((224, 224, 3)),\n            'img_shape': (224, 224),\n            'gt_bboxes_labels':\n            np.array([1, 2, 3], dtype=np.int64),\n            'gt_bboxes':\n            np.array([[10, 10, 20, 20], [20, 20, 40, 40], [40, 40, 80, 80]],\n                     dtype=np.float32),\n            'gt_ignore_flags':\n            np.array([0, 0, 1], dtype=bool),\n        }\n        results = albu_transform(results)\n        self.assertEqual(results['img'].dtype, np.float64)\n        self.assertEqual(results['gt_bboxes'].dtype, np.float32)\n        self.assertEqual(results['gt_ignore_flags'].dtype, bool)\n        self.assertEqual(results['gt_bboxes_labels'].dtype, np.int64)\n        self.assertEqual(results['img_shape'], results['img'].shape[:2])\n\n    @unittest.skipIf(albumentations is None, 'albumentations is not installed')\n    def test_repr(self):\n        albu_transform = dict(\n            type='Albu', transforms=[dict(type='ChannelShuffle', p=1)])\n        albu_transform = TRANSFORMS.build(albu_transform)\n\n        self.assertEqual(\n            repr(albu_transform), 'Albu(transforms=['\n            '{\\'type\\': \\'ChannelShuffle\\', '\n            '\\'p\\': 1}])')\n\n\nclass TestCorrupt(unittest.TestCase):\n\n    def test_transform(self):\n        results = dict(\n            img_path=osp.join(osp.dirname(__file__), '../../data/color.jpg'))\n\n        # Define simple pipeline\n        load = dict(type='LoadImageFromFile')\n        load = TRANSFORMS.build(load)\n\n        corrupt_transform = dict(type='Corrupt', corruption='gaussian_blur')\n        corrupt_transform = TRANSFORMS.build(corrupt_transform)\n\n        # Execute transforms\n        results = load(results)\n        results = corrupt_transform(results)\n\n        self.assertEqual(results['img'].dtype, np.uint8)\n\n    def test_repr(self):\n        corrupt_transform = dict(type='Corrupt', corruption='gaussian_blur')\n        corrupt_transform = TRANSFORMS.build(corrupt_transform)\n\n        self.assertEqual(\n            repr(corrupt_transform), 'Corrupt(corruption=gaussian_blur, '\n            'severity=1)')\n\n\nclass TestRandomShift(unittest.TestCase):\n\n    def test_init(self):\n        # test assertion for invalid shift_ratio\n        with self.assertRaises(AssertionError):\n            RandomShift(prob=1.5)\n\n        # test assertion for invalid max_shift_px\n        with self.assertRaises(AssertionError):\n            RandomShift(max_shift_px=-1)\n\n    def test_transform(self):\n\n        results = dict()\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        results['img'] = img\n        h, w, _ = img.shape\n        gt_bboxes = create_random_bboxes(8, w, h)\n        results['gt_bboxes_labels'] = np.ones(\n            gt_bboxes.shape[0], dtype=np.int64)\n        results['gt_bboxes'] = gt_bboxes\n        transform = RandomShift(prob=1.0)\n        results = transform(results)\n\n        self.assertEqual(results['img'].shape[:2], (h, w))\n        self.assertEqual(results['gt_bboxes_labels'].shape[0],\n                         results['gt_bboxes'].shape[0])\n        self.assertEqual(results['gt_bboxes_labels'].dtype, np.int64)\n        self.assertEqual(results['gt_bboxes'].dtype, np.float32)\n\n    def test_transform_use_box_type(self):\n\n        results = dict()\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../../data/color.jpg'), 'color')\n        results['img'] = img\n        h, w, _ = img.shape\n        gt_bboxes = create_random_bboxes(8, w, h)\n        results['gt_bboxes_labels'] = np.ones(\n            gt_bboxes.shape[0], dtype=np.int64)\n        results['gt_bboxes'] = HorizontalBoxes(gt_bboxes)\n        transform = RandomShift(prob=1.0)\n        results = transform(results)\n\n        self.assertEqual(results['img'].shape[:2], (h, w))\n        self.assertEqual(results['gt_bboxes_labels'].shape[0],\n                         results['gt_bboxes'].shape[0])\n        self.assertEqual(results['gt_bboxes_labels'].dtype, np.int64)\n        self.assertEqual(results['gt_bboxes'].dtype, torch.float32)\n\n    def test_repr(self):\n        transform = RandomShift()\n        self.assertEqual(\n            repr(transform), ('RandomShift(prob=0.5, '\n                              'max_shift_px=32, '\n                              'filter_thr_px=1)'))\n\n\nclass TestRandomErasing(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.results = construct_toy_data(poly2mask=True)\n\n    def test_transform(self):\n        transform = RandomErasing(\n            n_patches=(1, 5), ratio=(0.4, 0.8), img_border_value=0)\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].sum() < self.results['img'].sum())\n\n        transform = RandomErasing(\n            n_patches=1, ratio=0.999, img_border_value=255)\n        results = transform(copy.deepcopy(self.results))\n        self.assertTrue(results['img'].sum() > self.results['img'].sum())\n        # test empty results\n        empty_results = copy.deepcopy(self.results)\n        empty_results['gt_bboxes'] = np.zeros((0, 4), dtype=np.float32)\n        empty_results['gt_bboxes_labels'] = np.zeros((0, ), dtype=np.int64)\n        empty_results['gt_masks'] = empty_results['gt_masks'][False]\n        empty_results['gt_ignore_flags'] = np.zeros((0, ), dtype=bool)\n        empty_results['gt_seg_map'] = np.ones_like(\n            empty_results['gt_seg_map']) * 255\n        results = transform(copy.deepcopy(empty_results))\n        self.assertTrue(results['img'].sum() > self.results['img'].sum())\n\n    def test_transform_use_box_type(self):\n        src_results = copy.deepcopy(self.results)\n        src_results['gt_bboxes'] = HorizontalBoxes(src_results['gt_bboxes'])\n\n        transform = RandomErasing(\n            n_patches=(1, 5), ratio=(0.4, 0.8), img_border_value=0)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue(results['img'].sum() < src_results['img'].sum())\n\n        transform = RandomErasing(\n            n_patches=1, ratio=0.999, img_border_value=255)\n        results = transform(copy.deepcopy(src_results))\n        self.assertTrue(results['img'].sum() > src_results['img'].sum())\n        # test empty results\n        empty_results = copy.deepcopy(src_results)\n        empty_results['gt_bboxes'] = HorizontalBoxes([], dtype=torch.float32)\n        empty_results['gt_bboxes_labels'] = np.zeros((0, ), dtype=np.int64)\n        empty_results['gt_masks'] = empty_results['gt_masks'][False]\n        empty_results['gt_ignore_flags'] = np.zeros((0, ), dtype=bool)\n        empty_results['gt_seg_map'] = np.ones_like(\n            empty_results['gt_seg_map']) * 255\n        results = transform(copy.deepcopy(empty_results))\n        self.assertTrue(results['img'].sum() > src_results['img'].sum())\n\n    def test_repr(self):\n        transform = RandomErasing(n_patches=(1, 5), ratio=(0, 0.2))\n        self.assertEqual(\n            repr(transform), ('RandomErasing(n_patches=(1, 5), '\n                              'ratio=(0, 0.2), '\n                              'squared=True, '\n                              'bbox_erased_thr=0.9, '\n                              'img_border_value=128, '\n                              'mask_border_value=0, '\n                              'seg_ignore_label=255)'))\n\n\nclass TestResizeShortestEdge(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod()\n        -> tearDown() -> cleanUp()\n        \"\"\"\n        rng = np.random.RandomState(0)\n        self.data_info = dict(\n            img=np.random.random((220, 100, 3)),\n            gt_seg_map=np.random.random((220, 100, 3)),\n            gt_bboxes=np.array([[0, 0, 112, 12]], dtype=np.float32),\n            gt_masks=BitmapMasks(rng.rand(1, 220, 100), height=220, width=100))\n\n    def test_resize(self):\n        transform = ResizeShortestEdge(scale=200)\n        results = transform(copy.deepcopy(self.data_info))\n        self.assertEqual(results['img_shape'], (440, 200))\n        self.assertEqual(results['scale_factor'], (200 / 100, 440 / 220))\n\n        transform = ResizeShortestEdge(scale=200, max_size=301)\n        results = transform(copy.deepcopy(self.data_info))\n        self.assertEqual(results['img_shape'], (301, 137))\n        self.assertEqual(results['scale_factor'], (137 / 100, 301 / 220))\n\n        transform = ResizeShortestEdge(scale=201, keep_ratio=True)\n        results = transform(copy.deepcopy(self.data_info))\n        self.assertEqual(results['img_shape'], (442, 201))\n        self.assertEqual(results['scale_factor'], (201 / 100, 442 / 220))\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/test_wrappers.py",
    "content": "import copy\nimport os.path as osp\nimport unittest\n\nfrom mmcv.transforms import Compose\n\nfrom mmdet.datasets.transforms import MultiBranch, RandomOrder\nfrom mmdet.utils import register_all_modules\nfrom .utils import construct_toy_data\n\nregister_all_modules()\n\n\nclass TestMultiBranch(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        data_prefix = osp.join(osp.dirname(__file__), '../../data')\n        img_path = osp.join(data_prefix, 'color.jpg')\n        seg_map = osp.join(data_prefix, 'gray.jpg')\n        self.meta_keys = ('img_id', 'img_path', 'ori_shape', 'img_shape',\n                          'scale_factor', 'flip', 'flip_direction',\n                          'homography_matrix')\n        self.results = {\n            'img_path':\n            img_path,\n            'img_id':\n            12345,\n            'img_shape': (300, 400),\n            'seg_map_path':\n            seg_map,\n            'instances': [{\n                'bbox': [0, 0, 10, 20],\n                'bbox_label': 1,\n                'mask': [[0, 0, 0, 20, 10, 20, 10, 0]],\n                'ignore_flag': 0\n            }, {\n                'bbox': [10, 10, 110, 120],\n                'bbox_label': 2,\n                'mask': [[10, 10, 110, 10, 110, 120, 110, 10]],\n                'ignore_flag': 0\n            }, {\n                'bbox': [50, 50, 60, 80],\n                'bbox_label': 2,\n                'mask': [[50, 50, 60, 50, 60, 80, 50, 80]],\n                'ignore_flag': 1\n            }]\n        }\n        self.branch_field = ['sup', 'sup_teacher', 'sup_student']\n        self.weak_pipeline = [\n            dict(type='ShearX'),\n            dict(type='PackDetInputs', meta_keys=self.meta_keys)\n        ]\n        self.strong_pipeline = [\n            dict(type='ShearX'),\n            dict(type='ShearY'),\n            dict(type='PackDetInputs', meta_keys=self.meta_keys)\n        ]\n        self.labeled_pipeline = [\n            dict(type='LoadImageFromFile'),\n            dict(\n                type='LoadAnnotations',\n                with_bbox=True,\n                with_mask=True,\n                with_seg=True),\n            dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n            dict(type='RandomFlip', prob=0.5),\n            dict(\n                type='MultiBranch',\n                branch_field=self.branch_field,\n                sup_teacher=self.weak_pipeline,\n                sup_student=self.strong_pipeline),\n        ]\n        self.unlabeled_pipeline = [\n            dict(type='LoadImageFromFile'),\n            dict(type='Resize', scale=(1333, 800), keep_ratio=True),\n            dict(type='RandomFlip', prob=0.5),\n            dict(\n                type='MultiBranch',\n                branch_field=self.branch_field,\n                unsup_teacher=self.weak_pipeline,\n                unsup_student=self.strong_pipeline),\n        ]\n\n    def test_transform(self):\n        labeled_pipeline = Compose(self.labeled_pipeline)\n        labeled_results = labeled_pipeline(copy.deepcopy(self.results))\n        unlabeled_pipeline = Compose(self.unlabeled_pipeline)\n        unlabeled_results = unlabeled_pipeline(copy.deepcopy(self.results))\n\n        # test branch sup_teacher and sup_student\n        sup_branches = ['sup_teacher', 'sup_student']\n        for branch in sup_branches:\n            self.assertIn(branch, labeled_results['data_samples'])\n            self.assertIn('homography_matrix',\n                          labeled_results['data_samples'][branch])\n            self.assertIn('labels',\n                          labeled_results['data_samples'][branch].gt_instances)\n            self.assertIn('bboxes',\n                          labeled_results['data_samples'][branch].gt_instances)\n            self.assertIn('masks',\n                          labeled_results['data_samples'][branch].gt_instances)\n            self.assertIn('gt_sem_seg',\n                          labeled_results['data_samples'][branch])\n        # test branch unsup_teacher and unsup_student\n        unsup_branches = ['unsup_teacher', 'unsup_student']\n        for branch in unsup_branches:\n            self.assertIn(branch, unlabeled_results['data_samples'])\n            self.assertIn('homography_matrix',\n                          unlabeled_results['data_samples'][branch])\n            self.assertNotIn(\n                'labels',\n                unlabeled_results['data_samples'][branch].gt_instances)\n            self.assertNotIn(\n                'bboxes',\n                unlabeled_results['data_samples'][branch].gt_instances)\n            self.assertNotIn(\n                'masks',\n                unlabeled_results['data_samples'][branch].gt_instances)\n            self.assertNotIn('gt_sem_seg',\n                             unlabeled_results['data_samples'][branch])\n\n    def test_repr(self):\n        pipeline = [dict(type='PackDetInputs', meta_keys=())]\n        transform = MultiBranch(\n            branch_field=self.branch_field, sup=pipeline, unsup=pipeline)\n        self.assertEqual(\n            repr(transform),\n            (\"MultiBranch(branch_pipelines=['sup', 'unsup'])\"))\n\n\nclass TestRandomOrder(unittest.TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.results = construct_toy_data(poly2mask=True)\n        self.pipeline = [\n            dict(type='Sharpness'),\n            dict(type='Contrast'),\n            dict(type='Brightness'),\n            dict(type='Rotate'),\n            dict(type='ShearX'),\n            dict(type='TranslateY')\n        ]\n\n    def test_transform(self):\n        transform = RandomOrder(self.pipeline)\n        results = transform(copy.deepcopy(self.results))\n        self.assertEqual(results['img_shape'], self.results['img_shape'])\n        self.assertEqual(results['gt_bboxes'].shape,\n                         self.results['gt_bboxes'].shape)\n        self.assertEqual(results['gt_bboxes_labels'],\n                         self.results['gt_bboxes_labels'])\n        self.assertEqual(results['gt_ignore_flags'],\n                         self.results['gt_ignore_flags'])\n        self.assertEqual(results['gt_masks'].masks.shape,\n                         self.results['gt_masks'].masks.shape)\n        self.assertEqual(results['gt_seg_map'].shape,\n                         self.results['gt_seg_map'].shape)\n\n    def test_repr(self):\n        transform = RandomOrder(self.pipeline)\n        self.assertEqual(\n            repr(transform), ('RandomOrder(Sharpness, Contrast, '\n                              'Brightness, Rotate, ShearX, TranslateY, )'))\n"
  },
  {
    "path": "tests/test_datasets/test_transforms/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.structures.bbox import BaseBoxes, HorizontalBoxes\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\n\n\ndef create_random_bboxes(num_bboxes, img_w, img_h):\n    bboxes_left_top = np.random.uniform(0, 0.5, size=(num_bboxes, 2))\n    bboxes_right_bottom = np.random.uniform(0.5, 1, size=(num_bboxes, 2))\n    bboxes = np.concatenate((bboxes_left_top, bboxes_right_bottom), 1)\n    bboxes = (bboxes * np.array([img_w, img_h, img_w, img_h])).astype(\n        np.float32)\n    return bboxes\n\n\ndef create_full_masks(gt_bboxes, img_w, img_h):\n    xmin, ymin = gt_bboxes[:, 0:1], gt_bboxes[:, 1:2]\n    xmax, ymax = gt_bboxes[:, 2:3], gt_bboxes[:, 3:4]\n    gt_masks = np.zeros((len(gt_bboxes), img_h, img_w), dtype=np.uint8)\n    for i in range(len(gt_bboxes)):\n        gt_masks[i, int(ymin[i]):int(ymax[i]), int(xmin[i]):int(xmax[i])] = 1\n    gt_masks = BitmapMasks(gt_masks, img_h, img_w)\n    return gt_masks\n\n\ndef construct_toy_data(poly2mask, use_box_type=False):\n    img = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],\n                   dtype=np.uint8)\n    img = np.stack([img, img, img], axis=-1)\n    results = dict()\n    results['img'] = img\n    results['img_shape'] = img.shape[:2]\n    if use_box_type:\n        results['gt_bboxes'] = HorizontalBoxes(\n            np.array([[1, 0, 2, 2]], dtype=np.float32))\n    else:\n        results['gt_bboxes'] = np.array([[1, 0, 2, 2]], dtype=np.float32)\n    results['gt_bboxes_labels'] = np.array([13], dtype=np.int64)\n    if poly2mask:\n        gt_masks = np.array([[0, 1, 0, 0], [0, 1, 1, 0], [0, 1, 0, 0]],\n                            dtype=np.uint8)[None, :, :]\n        results['gt_masks'] = BitmapMasks(gt_masks, 3, 4)\n    else:\n        raw_masks = [[np.array([1, 2, 1, 0, 2, 1], dtype=np.float32)]]\n        results['gt_masks'] = PolygonMasks(raw_masks, 3, 4)\n    results['gt_ignore_flags'] = np.array(np.array([1], dtype=bool))\n    results['gt_seg_map'] = np.array(\n        [[255, 13, 255, 255], [255, 13, 13, 255], [255, 13, 255, 255]],\n        dtype=np.uint8)\n    return results\n\n\ndef check_result_same(results, pipeline_results, check_keys):\n    \"\"\"Check whether the ``pipeline_results`` is the same with the predefined\n    ``results``.\n\n    Args:\n        results (dict): Predefined results which should be the standard\n            output of the transform pipeline.\n        pipeline_results (dict): Results processed by the transform\n            pipeline.\n        check_keys (tuple): Keys that need to be checked between\n            results and pipeline_results.\n    \"\"\"\n    for key in check_keys:\n        if results.get(key, None) is None:\n            continue\n        if isinstance(results[key], (BitmapMasks, PolygonMasks)):\n            assert_allclose(pipeline_results[key].to_ndarray(),\n                            results[key].to_ndarray())\n        elif isinstance(results[key], BaseBoxes):\n            assert_allclose(pipeline_results[key].tensor, results[key].tensor)\n        else:\n            assert_allclose(pipeline_results[key], results[key])\n"
  },
  {
    "path": "tests/test_datasets/test_tta.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom unittest import TestCase\n\nimport mmcv\nimport pytest\n\nfrom mmdet.datasets.transforms import *  # noqa\nfrom mmdet.registry import TRANSFORMS\n\n\nclass TestMuitiScaleFlipAug(TestCase):\n\n    def test_exception(self):\n        with pytest.raises(TypeError):\n            tta_transform = dict(\n                type='TestTimeAug',\n                transforms=[dict(type='Resize', keep_ratio=False)],\n            )\n            TRANSFORMS.build(tta_transform)\n\n    def test_multi_scale_flip_aug(self):\n        tta_transform = dict(\n            type='TestTimeAug',\n            transforms=[[\n                dict(type='Resize', scale=scale, keep_ratio=False)\n                for scale in [(256, 256), (512, 512), (1024, 1024)]\n            ],\n                        [\n                            dict(\n                                type='mmdet.PackDetInputs',\n                                meta_keys=('img_id', 'img_path', 'ori_shape',\n                                           'img_shape', 'scale_factor'))\n                        ]])\n        tta_module = TRANSFORMS.build(tta_transform)\n\n        results = dict()\n        img = mmcv.imread(\n            osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')\n        results['img_id'] = '1'\n        results['img_path'] = 'data/color.jpg'\n        results['img'] = img\n        results['ori_shape'] = img.shape\n        results['ori_height'] = img.shape[0]\n        results['ori_width'] = img.shape[1]\n        # Set initial values for default meta_keys\n        results['pad_shape'] = img.shape\n        results['scale_factor'] = 1.0\n\n        tta_results = tta_module(results.copy())\n        assert [img.shape\n                for img in tta_results['inputs']] == [(3, 256, 256),\n                                                      (3, 512, 512),\n                                                      (3, 1024, 1024)]\n\n        tta_transform = dict(\n            type='TestTimeAug',\n            transforms=[\n                [\n                    dict(type='Resize', scale=scale, keep_ratio=False)\n                    for scale in [(256, 256), (512, 512), (1024, 1024)]\n                ],\n                [\n                    dict(type='RandomFlip', prob=0., direction='horizontal'),\n                    dict(type='RandomFlip', prob=1., direction='horizontal')\n                ],\n                [\n                    dict(\n                        type='mmdet.PackDetInputs',\n                        meta_keys=('img_id', 'img_path', 'ori_shape',\n                                   'img_shape', 'scale_factor', 'flip',\n                                   'flip_direction'))\n                ]\n            ])\n        tta_module = TRANSFORMS.build(tta_transform)\n        tta_results: dict = tta_module(results.copy())\n        assert [img.shape\n                for img in tta_results['inputs']] == [(3, 256, 256),\n                                                      (3, 256, 256),\n                                                      (3, 512, 512),\n                                                      (3, 512, 512),\n                                                      (3, 1024, 1024),\n                                                      (3, 1024, 1024)]\n        assert [\n            data_sample.metainfo['flip']\n            for data_sample in tta_results['data_samples']\n        ] == [False, True, False, True, False, True]\n\n        tta_transform = dict(\n            type='TestTimeAug',\n            transforms=[[\n                dict(type='Resize', scale=(512, 512), keep_ratio=False)\n            ],\n                        [\n                            dict(\n                                type='mmdet.PackDetInputs',\n                                meta_keys=('img_id', 'img_path', 'ori_shape',\n                                           'img_shape', 'scale_factor'))\n                        ]])\n        tta_module = TRANSFORMS.build(tta_transform)\n        tta_results = tta_module(results.copy())\n        assert [tta_results['inputs'][0].shape] == [(3, 512, 512)]\n\n        tta_transform = dict(\n            type='TestTimeAug',\n            transforms=[\n                [dict(type='Resize', scale=(512, 512), keep_ratio=False)],\n                [\n                    dict(type='RandomFlip', prob=0., direction='horizontal'),\n                    dict(type='RandomFlip', prob=1., direction='horizontal')\n                ],\n                [\n                    dict(\n                        type='mmdet.PackDetInputs',\n                        meta_keys=('img_id', 'img_path', 'ori_shape',\n                                   'img_shape', 'scale_factor', 'flip',\n                                   'flip_direction'))\n                ]\n            ])\n        tta_module = TRANSFORMS.build(tta_transform)\n        tta_results = tta_module(results.copy())\n        assert [img.shape for img in tta_results['inputs']] == [(3, 512, 512),\n                                                                (3, 512, 512)]\n        assert [\n            data_sample.metainfo['flip']\n            for data_sample in tta_results['data_samples']\n        ] == [False, True]\n\n        tta_transform = dict(\n            type='TestTimeAug',\n            transforms=[[\n                dict(type='Resize', scale_factor=r, keep_ratio=False)\n                for r in [0.5, 1.0, 2.0]\n            ],\n                        [\n                            dict(\n                                type='mmdet.PackDetInputs',\n                                meta_keys=('img_id', 'img_path', 'ori_shape',\n                                           'img_shape', 'scale_factor'))\n                        ]])\n        tta_module = TRANSFORMS.build(tta_transform)\n        tta_results = tta_module(results.copy())\n        assert [img.shape for img in tta_results['inputs']] == [(3, 144, 256),\n                                                                (3, 288, 512),\n                                                                (3, 576, 1024)]\n\n        tta_transform = dict(\n            type='TestTimeAug',\n            transforms=[\n                [\n                    dict(type='Resize', scale_factor=r, keep_ratio=True)\n                    for r in [0.5, 1.0, 2.0]\n                ],\n                [\n                    dict(type='RandomFlip', prob=0., direction='horizontal'),\n                    dict(type='RandomFlip', prob=1., direction='horizontal')\n                ],\n                [\n                    dict(\n                        type='mmdet.PackDetInputs',\n                        meta_keys=('img_id', 'img_path', 'ori_shape',\n                                   'img_shape', 'scale_factor', 'flip',\n                                   'flip_direction'))\n                ]\n            ])\n        tta_module = TRANSFORMS.build(tta_transform)\n        tta_results = tta_module(results.copy())\n        assert [img.shape for img in tta_results['inputs']] == [(3, 144, 256),\n                                                                (3, 144, 256),\n                                                                (3, 288, 512),\n                                                                (3, 288, 512),\n                                                                (3, 576, 1024),\n                                                                (3, 576, 1024)]\n        assert [\n            data_sample.metainfo['flip']\n            for data_sample in tta_results['data_samples']\n        ] == [False, True, False, True, False, True]\n"
  },
  {
    "path": "tests/test_datasets/test_wider_face.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport cv2\nimport numpy as np\n\nfrom mmdet.datasets import WIDERFaceDataset\n\n\nclass TestWIDERFaceDataset(unittest.TestCase):\n\n    def setUp(self) -> None:\n        img_path = 'tests/data/WIDERFace/WIDER_train/0--Parade/0_Parade_marchingband_1_5.jpg'  # noqa: E501\n        dummy_img = np.zeros((683, 1024, 3), dtype=np.uint8)\n        cv2.imwrite(img_path, dummy_img)\n\n    def test_wider_face_dataset(self):\n        dataset = WIDERFaceDataset(\n            data_root='tests/data/WIDERFace',\n            ann_file='train.txt',\n            data_prefix=dict(img='WIDER_train'),\n            pipeline=[])\n        dataset.full_init()\n        self.assertEqual(len(dataset), 1)\n\n        data_list = dataset.load_data_list()\n        self.assertEqual(len(data_list), 1)\n        self.assertEqual(len(data_list[0]['instances']), 10)\n"
  },
  {
    "path": "tests/test_datasets/test_youtube_vis_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nfrom mmdet.datasets import YouTubeVISDataset\n\n\nclass TestYouTubeVISDataset(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n\n        cls.dataset = YouTubeVISDataset(\n            ann_file='tests/data/vis_sample.json', dataset_version='2019')\n\n    def test_set_dataset_classes(self):\n        assert isinstance(self.dataset.metainfo, dict)\n        assert len(self.dataset.metainfo['classes']) == 40\n"
  },
  {
    "path": "tests/test_engine/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_engine/test_hooks/test_checkloss_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\nfrom unittest.mock import Mock\n\nimport torch\n\nfrom mmdet.engine.hooks import CheckInvalidLossHook\n\n\nclass TestCheckInvalidLossHook(TestCase):\n\n    def test_after_train_iter(self):\n        n = 50\n        hook = CheckInvalidLossHook(n)\n        runner = Mock()\n        runner.logger = Mock()\n        runner.logger.info = Mock()\n\n        # Test `after_train_iter` function within the n iteration.\n        runner.iter = 10\n        outputs = dict(loss=torch.LongTensor([2]))\n        hook.after_train_iter(runner, 10, outputs=outputs)\n        outputs = dict(loss=torch.tensor(float('nan')))\n        hook.after_train_iter(runner, 10, outputs=outputs)\n        outputs = dict(loss=torch.tensor(float('inf')))\n        hook.after_train_iter(runner, 10, outputs=outputs)\n\n        # Test `after_train_iter` at the n iteration.\n        runner.iter = n - 1\n        outputs = dict(loss=torch.LongTensor([2]))\n        hook.after_train_iter(runner, n - 1, outputs=outputs)\n        outputs = dict(loss=torch.tensor(float('nan')))\n        with self.assertRaises(AssertionError):\n            hook.after_train_iter(runner, n - 1, outputs=outputs)\n        outputs = dict(loss=torch.tensor(float('inf')))\n        with self.assertRaises(AssertionError):\n            hook.after_train_iter(runner, n - 1, outputs=outputs)\n"
  },
  {
    "path": "tests/test_engine/test_hooks/test_mean_teacher_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport tempfile\nfrom unittest import TestCase\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.evaluator import BaseMetric\nfrom mmengine.model import BaseModel\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.registry import MODEL_WRAPPERS\nfrom mmengine.runner import Runner\nfrom torch.utils.data import Dataset\n\nfrom mmdet.registry import DATASETS\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n\nclass ToyModel(nn.Module):\n\n    def __init__(self):\n        super().__init__()\n        self.linear = nn.Linear(2, 1)\n\n    def forward(self, inputs, data_samples, mode='tensor'):\n        labels = torch.stack(data_samples)\n        inputs = torch.stack(inputs)\n        outputs = self.linear(inputs)\n        if mode == 'tensor':\n            return outputs\n        elif mode == 'loss':\n            loss = (labels - outputs).sum()\n            outputs = dict(loss=loss)\n            return outputs\n        else:\n            return outputs\n\n\nclass ToyModel1(BaseModel, ToyModel):\n\n    def __init__(self):\n        super().__init__()\n\n    def forward(self, *args, **kwargs):\n        return super(BaseModel, self).forward(*args, **kwargs)\n\n\nclass ToyModel2(BaseModel):\n\n    def __init__(self):\n        super().__init__()\n        self.teacher = ToyModel1()\n        self.student = ToyModel1()\n\n    def forward(self, *args, **kwargs):\n        return self.student(*args, **kwargs)\n\n\n@DATASETS.register_module(force=True)\nclass DummyDataset(Dataset):\n    METAINFO = dict()  # type: ignore\n    data = torch.randn(12, 2)\n    label = torch.ones(12)\n\n    @property\n    def metainfo(self):\n        return self.METAINFO\n\n    def __len__(self):\n        return self.data.size(0)\n\n    def __getitem__(self, index):\n        return dict(inputs=self.data[index], data_samples=self.label[index])\n\n\nclass ToyMetric1(BaseMetric):\n\n    def __init__(self, collect_device='cpu', dummy_metrics=None):\n        super().__init__(collect_device=collect_device)\n        self.dummy_metrics = dummy_metrics\n\n    def process(self, data_batch, predictions):\n        result = {'acc': 1}\n        self.results.append(result)\n\n    def compute_metrics(self, results):\n        return dict(acc=1)\n\n\nclass TestMeanTeacherHook(TestCase):\n\n    def setUp(self):\n        self.temp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.temp_dir.cleanup()\n\n    def test_mean_teacher_hook(self):\n        device = 'cuda:0' if torch.cuda.is_available() else 'cpu'\n        model = ToyModel2().to(device)\n        runner = Runner(\n            model=model,\n            train_dataloader=dict(\n                dataset=DummyDataset(),\n                sampler=dict(type='DefaultSampler', shuffle=True),\n                batch_size=3,\n                num_workers=0),\n            val_dataloader=dict(\n                dataset=DummyDataset(),\n                sampler=dict(type='DefaultSampler', shuffle=False),\n                batch_size=3,\n                num_workers=0),\n            val_evaluator=[ToyMetric1()],\n            work_dir=self.temp_dir.name,\n            default_scope='mmdet',\n            optim_wrapper=OptimWrapper(\n                torch.optim.Adam(ToyModel().parameters())),\n            train_cfg=dict(by_epoch=True, max_epochs=2, val_interval=1),\n            val_cfg=dict(),\n            default_hooks=dict(logger=None),\n            custom_hooks=[dict(type='MeanTeacherHook')],\n            experiment_name='test1')\n        runner.train()\n\n        self.assertTrue(\n            osp.exists(osp.join(self.temp_dir.name, 'epoch_2.pth')))\n        # checkpoint = torch.load(osp.join(self.temp_dir.name, 'epoch_2.pth'))\n\n        # load and testing\n        runner = Runner(\n            model=model,\n            test_dataloader=dict(\n                dataset=DummyDataset(),\n                sampler=dict(type='DefaultSampler', shuffle=True),\n                batch_size=3,\n                num_workers=0),\n            test_evaluator=[ToyMetric1()],\n            test_cfg=dict(),\n            work_dir=self.temp_dir.name,\n            default_scope='mmdet',\n            load_from=osp.join(self.temp_dir.name, 'epoch_2.pth'),\n            default_hooks=dict(logger=None),\n            custom_hooks=[dict(type='MeanTeacherHook')],\n            experiment_name='test2')\n        runner.test()\n\n        @MODEL_WRAPPERS.register_module()\n        class DummyWrapper(BaseModel):\n\n            def __init__(self, model):\n                super().__init__()\n                self.module = model\n\n            def forward(self, *args, **kwargs):\n                return self.module(*args, **kwargs)\n\n        # with model wrapper\n        runner = Runner(\n            model=DummyWrapper(ToyModel2()),\n            test_dataloader=dict(\n                dataset=DummyDataset(),\n                sampler=dict(type='DefaultSampler', shuffle=True),\n                batch_size=3,\n                num_workers=0),\n            test_evaluator=[ToyMetric1()],\n            test_cfg=dict(),\n            work_dir=self.temp_dir.name,\n            default_scope='mmdet',\n            load_from=osp.join(self.temp_dir.name, 'epoch_2.pth'),\n            default_hooks=dict(logger=None),\n            custom_hooks=[dict(type='MeanTeacherHook')],\n            experiment_name='test3')\n        runner.test()\n"
  },
  {
    "path": "tests/test_engine/test_hooks/test_memory_profiler_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\nfrom unittest.mock import Mock\n\nfrom mmdet.engine.hooks import MemoryProfilerHook\n\n\nclass TestMemoryProfilerHook(TestCase):\n\n    def test_after_train_iter(self):\n        hook = MemoryProfilerHook(2)\n        runner = Mock()\n        runner.logger = Mock()\n        runner.logger.info = Mock()\n        hook.after_train_iter(runner, 0)\n        runner.logger.info.assert_not_called()\n        hook.after_train_iter(runner, 1)\n        runner.logger.info.assert_called_once()\n\n    def test_after_val_iter(self):\n        hook = MemoryProfilerHook(2)\n        runner = Mock()\n        runner.logger = Mock()\n        runner.logger.info = Mock()\n        hook.after_val_iter(runner, 0)\n        runner.logger.info.assert_not_called()\n        hook.after_val_iter(runner, 1)\n        runner.logger.info.assert_called_once()\n\n    def test_after_test_iter(self):\n        hook = MemoryProfilerHook(2)\n        runner = Mock()\n        runner.logger = Mock()\n        runner.logger.info = Mock()\n        hook.after_test_iter(runner, 0)\n        runner.logger.info.assert_not_called()\n        hook.after_test_iter(runner, 1)\n        runner.logger.info.assert_called_once()\n"
  },
  {
    "path": "tests/test_engine/test_hooks/test_num_class_check_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom copy import deepcopy\nfrom unittest import TestCase\nfrom unittest.mock import Mock\n\nfrom mmcv.cnn import VGG\nfrom mmengine.dataset import BaseDataset\nfrom torch import nn\n\nfrom mmdet.engine.hooks import NumClassCheckHook\nfrom mmdet.models.roi_heads.mask_heads import FusedSemanticHead\n\n\nclass TestNumClassCheckHook(TestCase):\n\n    def setUp(self):\n        # Setup NumClassCheckHook\n        hook = NumClassCheckHook()\n        self.hook = hook\n\n        # Setup runner mock\n        runner = Mock()\n        runner.model = Mock()\n        runner.logger = Mock()\n        runner.logger.warning = Mock()\n        runner.train_dataloader = Mock()\n        runner.val_dataloader = Mock()\n        self.runner = runner\n\n        # Setup dataset\n        metainfo = dict(classes=None)\n        self.none_classmeta_dataset = BaseDataset(\n            metainfo=metainfo, lazy_init=True)\n        metainfo = dict(classes='class_name')\n        self.str_classmeta_dataset = BaseDataset(\n            metainfo=metainfo, lazy_init=True)\n        metainfo = dict(classes=('bus', 'car'))\n        self.normal_classmeta_dataset = BaseDataset(\n            metainfo=metainfo, lazy_init=True)\n\n        # Setup valid model\n        valid_model = nn.Module()\n        valid_model.add_module('backbone', VGG(depth=11))\n        fused_semantic_head = FusedSemanticHead(\n            num_ins=1,\n            fusion_level=0,\n            num_convs=1,\n            in_channels=1,\n            conv_out_channels=1)\n        valid_model.add_module('semantic_head', fused_semantic_head)\n        rpn_head = nn.Module()\n        rpn_head.num_classes = 1\n        valid_model.add_module('rpn_head', rpn_head)\n        bbox_head = nn.Module()\n        bbox_head.num_classes = 2\n        valid_model.add_module('bbox_head', bbox_head)\n        self.valid_model = valid_model\n\n        # Setup invalid model\n        invalid_model = nn.Module()\n        bbox_head = nn.Module()\n        bbox_head.num_classes = 4\n        invalid_model.add_module('bbox_head', bbox_head)\n        self.invalid_model = invalid_model\n\n    def test_before_train_epch(self):\n        runner = deepcopy(self.runner)\n\n        # Test when dataset.metainfo['classes'] is None\n        runner.train_dataloader.dataset = self.none_classmeta_dataset\n        self.hook.before_train_epoch(runner)\n        runner.logger.warning.assert_called_once()\n        # Test when dataset.metainfo['classes'] is a str\n        runner.train_dataloader.dataset = self.str_classmeta_dataset\n        with self.assertRaises(AssertionError):\n            self.hook.before_train_epoch(runner)\n\n        runner.train_dataloader.dataset = self.normal_classmeta_dataset\n        # Test `num_classes` of model is compatible with dataset\n        runner.model = self.valid_model\n        self.hook.before_train_epoch(runner)\n        # Test `num_classes` of model is not compatible with dataset\n        runner.model = self.invalid_model\n        with self.assertRaises(AssertionError):\n            self.hook.before_train_epoch(runner)\n\n    def test_before_val_epoch(self):\n        runner = deepcopy(self.runner)\n\n        # Test when dataset.metainfo['classes'] is None\n        runner.val_dataloader.dataset = self.none_classmeta_dataset\n        self.hook.before_val_epoch(runner)\n        runner.logger.warning.assert_called_once()\n        # Test when dataset.metainfo['classes'] is a str\n        runner.val_dataloader.dataset = self.str_classmeta_dataset\n        with self.assertRaises(AssertionError):\n            self.hook.before_val_epoch(runner)\n\n        runner.val_dataloader.dataset = self.normal_classmeta_dataset\n        # Test `num_classes` of model is compatible with dataset\n        runner.model = self.valid_model\n        self.hook.before_val_epoch(runner)\n        # Test `num_classes` of model is not compatible with dataset\n        runner.model = self.invalid_model\n        with self.assertRaises(AssertionError):\n            self.hook.before_val_epoch(runner)\n"
  },
  {
    "path": "tests/test_engine/test_hooks/test_pipeline_switch_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\nfrom unittest.mock import Mock\n\nfrom mmdet.engine.hooks import PipelineSwitchHook\n\n\nclass TestPipelineSwitchHook(TestCase):\n\n    def test_persistent_workers_on(self):\n        runner = Mock()\n        runner.model = Mock()\n        runner.model.module = Mock()\n        runner.train_dataloader = Mock()\n        runner.train_dataloader.persistent_workers = True\n        runner.train_dataloader._DataLoader__initialized = True\n\n        stage2 = [dict(type='RandomResize', scale=(1280, 1280))]\n\n        runner.epoch = 284  # epoch < switch_epoch\n        hook = PipelineSwitchHook(switch_epoch=285, switch_pipeline=stage2)\n        hook.before_train_epoch(runner)\n        self.assertFalse(hook._restart_dataloader)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n\n        runner.epoch = 285  # epoch == switch_epoch\n        hook.before_train_epoch(runner)\n        self.assertTrue(hook._restart_dataloader)\n        self.assertFalse(runner.train_dataloader._DataLoader__initialized)\n        self.assertTrue(\n            len(runner.train_dataloader.dataset.pipeline.transforms) == 1)\n\n        runner.epoch = 286  # epoch > switch_epoch\n        hook.before_train_epoch(runner)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n        self.assertTrue(\n            len(runner.train_dataloader.dataset.pipeline.transforms) == 1)\n\n    def test_persistent_workers_off(self):\n        runner = Mock()\n        runner.model = Mock()\n        runner.train_dataloader = Mock()\n        runner.train_dataloader.persistent_workers = False\n        runner.train_dataloader._DataLoader__initialized = True\n\n        stage2 = [dict(type='RandomResize', scale=(1280, 1280))]\n\n        runner.epoch = 284  # epoch < switch_epoch\n        hook = PipelineSwitchHook(switch_epoch=285, switch_pipeline=stage2)\n        hook.before_train_epoch(runner)\n        self.assertFalse(hook._restart_dataloader)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n\n        runner.epoch = 285  # epoch == switch_epoch\n        hook.before_train_epoch(runner)\n        self.assertFalse(hook._restart_dataloader)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n        self.assertTrue(\n            len(runner.train_dataloader.dataset.pipeline.transforms) == 1)\n\n        runner.epoch = 286  # epoch > switch_epoch\n        hook.before_train_epoch(runner)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n        self.assertTrue(\n            len(runner.train_dataloader.dataset.pipeline.transforms) == 1)\n\n    def test_initialize_after_switching(self):\n        # This simulates the resumption after the switching.\n        runner = Mock()\n        runner.model = Mock()\n        runner.model.module = Mock()\n        runner.train_dataloader = Mock()\n        runner.train_dataloader.persistent_workers = True\n        runner.train_dataloader._DataLoader__initialized = True\n\n        stage2 = [dict(type='RandomResize', scale=(1280, 1280))]\n\n        runner.epoch = 286  # epoch > switch_epoch\n        hook = PipelineSwitchHook(switch_epoch=285, switch_pipeline=stage2)\n        hook.before_train_epoch(runner)\n        self.assertTrue(hook._restart_dataloader)\n        self.assertFalse(runner.train_dataloader._DataLoader__initialized)\n        self.assertTrue(\n            len(runner.train_dataloader.dataset.pipeline.transforms) == 1)\n"
  },
  {
    "path": "tests/test_engine/test_hooks/test_sync_norm_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\nfrom unittest.mock import Mock, patch\n\nimport torch.nn as nn\n\nfrom mmdet.engine.hooks import SyncNormHook\n\n\nclass TestSyncNormHook(TestCase):\n\n    @patch(\n        'mmdet.engine.hooks.sync_norm_hook.get_dist_info', return_value=(0, 1))\n    def test_before_val_epoch_non_dist(self, mock):\n        model = nn.Sequential(\n            nn.Conv2d(1, 5, kernel_size=3), nn.BatchNorm2d(5, momentum=0.3),\n            nn.Linear(5, 10))\n        runner = Mock()\n        runner.model = model\n        hook = SyncNormHook()\n        hook.before_val_epoch(runner)\n\n    @patch(\n        'mmdet.engine.hooks.sync_norm_hook.get_dist_info', return_value=(0, 2))\n    def test_before_val_epoch_dist(self, mock):\n        model = nn.Sequential(\n            nn.Conv2d(1, 5, kernel_size=3), nn.BatchNorm2d(5, momentum=0.3),\n            nn.Linear(5, 10))\n        runner = Mock()\n        runner.model = model\n        hook = SyncNormHook()\n        hook.before_val_epoch(runner)\n\n    @patch(\n        'mmdet.engine.hooks.sync_norm_hook.get_dist_info', return_value=(0, 2))\n    def test_before_val_epoch_dist_no_norm(self, mock):\n        model = nn.Sequential(nn.Conv2d(1, 5, kernel_size=3), nn.Linear(5, 10))\n        runner = Mock()\n        runner.model = model\n        hook = SyncNormHook()\n        hook.before_val_epoch(runner)\n"
  },
  {
    "path": "tests/test_engine/test_hooks/test_visualization_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport shutil\nimport time\nfrom unittest import TestCase\nfrom unittest.mock import Mock\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.engine.hooks import DetVisualizationHook, TrackVisualizationHook\nfrom mmdet.structures import DetDataSample, TrackDataSample\nfrom mmdet.visualization import DetLocalVisualizer, TrackLocalVisualizer\n\n\ndef _rand_bboxes(num_boxes, h, w):\n    cx, cy, bw, bh = torch.rand(num_boxes, 4).T\n\n    tl_x = ((cx * w) - (w * bw / 2)).clamp(0, w)\n    tl_y = ((cy * h) - (h * bh / 2)).clamp(0, h)\n    br_x = ((cx * w) + (w * bw / 2)).clamp(0, w)\n    br_y = ((cy * h) + (h * bh / 2)).clamp(0, h)\n\n    bboxes = torch.stack([tl_x, tl_y, br_x, br_y], dim=0).T\n    return bboxes\n\n\nclass TestVisualizationHook(TestCase):\n\n    def setUp(self) -> None:\n        DetLocalVisualizer.get_instance('current_visualizer')\n\n        pred_instances = InstanceData()\n        pred_instances.bboxes = _rand_bboxes(5, 10, 12)\n        pred_instances.labels = torch.randint(0, 2, (5, ))\n        pred_instances.scores = torch.rand((5, ))\n        pred_det_data_sample = DetDataSample()\n        pred_det_data_sample.set_metainfo({\n            'img_path':\n            osp.join(osp.dirname(__file__), '../../data/color.jpg')\n        })\n        pred_det_data_sample.pred_instances = pred_instances\n        self.outputs = [pred_det_data_sample] * 2\n\n    def test_after_val_iter(self):\n        runner = Mock()\n        runner.iter = 1\n        hook = DetVisualizationHook()\n        hook.after_val_iter(runner, 1, {}, self.outputs)\n\n    def test_after_test_iter(self):\n        runner = Mock()\n        runner.iter = 1\n        hook = DetVisualizationHook(draw=True)\n        hook.after_test_iter(runner, 1, {}, self.outputs)\n        self.assertEqual(hook._test_index, 2)\n\n        # test\n        timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())\n        test_out_dir = timestamp + '1'\n        runner.work_dir = timestamp\n        runner.timestamp = '1'\n        hook = DetVisualizationHook(draw=False, test_out_dir=test_out_dir)\n        hook.after_test_iter(runner, 1, {}, self.outputs)\n        self.assertTrue(not osp.exists(f'{timestamp}/1/{test_out_dir}'))\n\n        hook = DetVisualizationHook(draw=True, test_out_dir=test_out_dir)\n        hook.after_test_iter(runner, 1, {}, self.outputs)\n        self.assertTrue(osp.exists(f'{timestamp}/1/{test_out_dir}'))\n        shutil.rmtree(f'{timestamp}')\n\n\nclass TestTrackVisualizationHook(TestCase):\n\n    def setUp(self) -> None:\n        TrackLocalVisualizer.get_instance('visualizer')\n        # pseudo data_batch\n        self.data_batch = dict(data_samples=None, inputs=None)\n\n        pred_instances_data = dict(\n            bboxes=torch.tensor([[100, 100, 200, 200], [150, 150, 400, 200]]),\n            instances_id=torch.tensor([1, 2]),\n            labels=torch.tensor([0, 1]),\n            scores=torch.tensor([0.955, 0.876]))\n        pred_instances = InstanceData(**pred_instances_data)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_track_instances = pred_instances\n        img_data_sample.gt_instances = pred_instances\n        img_data_sample.set_metainfo(\n            dict(\n                img_path=osp.join(\n                    osp.dirname(__file__), '../../data/color.jpg'),\n                scale_factor=(1.0, 1.0)))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        track_data_sample.set_metainfo(dict(ori_length=1))\n        self.outputs = [track_data_sample]\n\n    def test_after_val_iter_image(self):\n        runner = Mock()\n        runner.iter = 1\n        hook = TrackVisualizationHook(frame_interval=10, draw=True)\n        hook.after_val_iter(runner, 9, self.data_batch, self.outputs)\n\n    def test_after_test_iter(self):\n        runner = Mock()\n        runner.iter = 1\n        hook = TrackVisualizationHook(frame_interval=10, draw=True)\n        hook.after_val_iter(runner, 9, self.data_batch, self.outputs)\n\n        # test test_out_dir\n        timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())\n        test_out_dir = timestamp + '1'\n        runner.work_dir = timestamp\n        runner.timestamp = '1'\n        hook = TrackVisualizationHook(\n            frame_interval=10, draw=True, test_out_dir=test_out_dir)\n        hook.after_test_iter(runner, 9, self.data_batch, self.outputs)\n        self.assertTrue(osp.exists(f'{timestamp}/1/{test_out_dir}'))\n        shutil.rmtree(f'{timestamp}')\n"
  },
  {
    "path": "tests/test_engine/test_hooks/test_yolox_mode_switch_hook.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\nfrom unittest.mock import Mock, patch\n\nfrom mmdet.engine.hooks import YOLOXModeSwitchHook\n\n\nclass TestYOLOXModeSwitchHook(TestCase):\n\n    @patch('mmdet.engine.hooks.yolox_mode_switch_hook.is_model_wrapper')\n    def test_is_model_wrapper_and_persistent_workers_on(\n            self, mock_is_model_wrapper):\n        mock_is_model_wrapper.return_value = True\n        runner = Mock()\n        runner.model = Mock()\n        runner.model.module = Mock()\n        runner.model.module.detector.bbox_head.use_l1 = False\n        runner.train_dataloader = Mock()\n        runner.train_dataloader.persistent_workers = True\n        runner.train_dataloader._DataLoader__initialized = True\n        runner.epoch = 284\n        runner.max_epochs = 300\n\n        hook = YOLOXModeSwitchHook(num_last_epochs=15)\n        hook.before_train_epoch(runner)\n        self.assertTrue(hook._restart_dataloader)\n        self.assertTrue(runner.model.module.detector.bbox_head.use_l1)\n        self.assertFalse(runner.train_dataloader._DataLoader__initialized)\n\n        runner.epoch = 285\n        hook.before_train_epoch(runner)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n\n    def test_not_model_wrapper_and_persistent_workers_off(self):\n        runner = Mock()\n        runner.model = Mock()\n        runner.model.detector.bbox_head.use_l1 = False\n        runner.train_dataloader = Mock()\n        runner.train_dataloader.persistent_workers = False\n        runner.train_dataloader._DataLoader__initialized = True\n        runner.epoch = 284\n        runner.max_epochs = 300\n\n        hook = YOLOXModeSwitchHook(num_last_epochs=15)\n        hook.before_train_epoch(runner)\n        self.assertFalse(hook._restart_dataloader)\n        self.assertTrue(runner.model.detector.bbox_head.use_l1)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n\n        runner.epoch = 285\n        hook.before_train_epoch(runner)\n        self.assertFalse(hook._restart_dataloader)\n        self.assertTrue(runner.train_dataloader._DataLoader__initialized)\n\n    @patch('mmdet.engine.hooks.yolox_mode_switch_hook.is_model_wrapper')\n    def test_initialize_after_switching(self, mock_is_model_wrapper):\n        # This simulates the resumption after the switching.\n        mock_is_model_wrapper.return_value = True\n        runner = Mock()\n        runner.model = Mock()\n        runner.model.module = Mock()\n        runner.model.module.bbox_head.use_l1 = False\n        runner.train_dataloader = Mock()\n        runner.train_dataloader.persistent_workers = True\n        runner.train_dataloader._DataLoader__initialized = True\n        runner.epoch = 285\n        runner.max_epochs = 300\n\n        # epoch + 1 > max_epochs - num_last_epochs .\n        hook = YOLOXModeSwitchHook(num_last_epochs=15)\n        hook.before_train_epoch(runner)\n        self.assertTrue(hook._restart_dataloader)\n        self.assertTrue(runner.model.module.detector.bbox_head.use_l1)\n        self.assertFalse(runner.train_dataloader._DataLoader__initialized)\n"
  },
  {
    "path": "tests/test_engine/test_optimizers/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_engine/test_optimizers/test_layer_decay_optimizer_constructor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\nimport torch.nn as nn\nfrom mmcv.cnn import ConvModule\n\nfrom mmdet.engine import LearningRateDecayOptimizerConstructor\n\nbase_lr = 1\ndecay_rate = 2\nbase_wd = 0.05\nweight_decay = 0.05\n\nexpected_stage_wise_lr_wd_convnext = [{\n    'weight_decay': 0.0,\n    'lr_scale': 128\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 1\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 64\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 64\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 32\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 32\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 16\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 16\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 8\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 8\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 128\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 1\n}]\n\nexpected_layer_wise_lr_wd_convnext = [{\n    'weight_decay': 0.0,\n    'lr_scale': 128\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 1\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 64\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 64\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 32\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 32\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 16\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 16\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 2\n}, {\n    'weight_decay': 0.0,\n    'lr_scale': 2\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 128\n}, {\n    'weight_decay': 0.05,\n    'lr_scale': 1\n}]\n\n\nclass ToyConvNeXt(nn.Module):\n\n    def __init__(self):\n        super().__init__()\n        self.stages = nn.ModuleList()\n        for i in range(4):\n            stage = nn.Sequential(ConvModule(3, 4, kernel_size=1, bias=True))\n            self.stages.append(stage)\n        self.norm0 = nn.BatchNorm2d(2)\n\n        # add some variables to meet unit test coverate rate\n        self.cls_token = nn.Parameter(torch.ones(1))\n        self.mask_token = nn.Parameter(torch.ones(1))\n        self.pos_embed = nn.Parameter(torch.ones(1))\n        self.stem_norm = nn.Parameter(torch.ones(1))\n        self.downsample_norm0 = nn.BatchNorm2d(2)\n        self.downsample_norm1 = nn.BatchNorm2d(2)\n        self.downsample_norm2 = nn.BatchNorm2d(2)\n        self.lin = nn.Parameter(torch.ones(1))\n        self.lin.requires_grad = False\n        self.downsample_layers = nn.ModuleList()\n        for _ in range(4):\n            stage = nn.Sequential(nn.Conv2d(3, 4, kernel_size=1, bias=True))\n            self.downsample_layers.append(stage)\n\n\nclass ToyDetector(nn.Module):\n\n    def __init__(self, backbone):\n        super().__init__()\n        self.backbone = backbone\n        self.head = nn.Conv2d(2, 2, kernel_size=1, groups=2)\n\n\nclass PseudoDataParallel(nn.Module):\n\n    def __init__(self, model):\n        super().__init__()\n        self.module = model\n\n\ndef check_optimizer_lr_wd(optimizer, gt_lr_wd):\n    assert isinstance(optimizer, torch.optim.AdamW)\n    assert optimizer.defaults['lr'] == base_lr\n    assert optimizer.defaults['weight_decay'] == base_wd\n    param_groups = optimizer.param_groups\n    print(param_groups)\n    assert len(param_groups) == len(gt_lr_wd)\n    for i, param_dict in enumerate(param_groups):\n        assert param_dict['weight_decay'] == gt_lr_wd[i]['weight_decay']\n        assert param_dict['lr_scale'] == gt_lr_wd[i]['lr_scale']\n        assert param_dict['lr_scale'] == param_dict['lr']\n\n\ndef test_learning_rate_decay_optimizer_constructor():\n\n    # Test lr wd for ConvNeXT\n    backbone = ToyConvNeXt()\n    model = PseudoDataParallel(ToyDetector(backbone))\n    optim_wrapper_cfg = dict(\n        type='OptimWrapper',\n        optimizer=dict(\n            type='AdamW', lr=base_lr, betas=(0.9, 0.999), weight_decay=0.05))\n    # stagewise decay\n    stagewise_paramwise_cfg = dict(\n        decay_rate=decay_rate, decay_type='stage_wise', num_layers=6)\n    optim_constructor = LearningRateDecayOptimizerConstructor(\n        optim_wrapper_cfg, stagewise_paramwise_cfg)\n    optim_wrapper = optim_constructor(model)\n    check_optimizer_lr_wd(optim_wrapper.optimizer,\n                          expected_stage_wise_lr_wd_convnext)\n    # layerwise decay\n    layerwise_paramwise_cfg = dict(\n        decay_rate=decay_rate, decay_type='layer_wise', num_layers=6)\n    optim_constructor = LearningRateDecayOptimizerConstructor(\n        optim_wrapper_cfg, layerwise_paramwise_cfg)\n    optim_wrapper = optim_constructor(model)\n    check_optimizer_lr_wd(optim_wrapper.optimizer,\n                          expected_layer_wise_lr_wd_convnext)\n"
  },
  {
    "path": "tests/test_engine/test_runner/test_loops.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport tempfile\nfrom unittest import TestCase\nfrom unittest.mock import Mock\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.evaluator import Evaluator\nfrom mmengine.model import BaseModel\nfrom mmengine.optim import OptimWrapper\nfrom mmengine.runner import Runner\nfrom torch.utils.data import Dataset\n\nfrom mmdet.registry import DATASETS\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n\nclass ToyModel(nn.Module):\n\n    def __init__(self):\n        super().__init__()\n        self.linear = nn.Linear(2, 1)\n\n    def forward(self, inputs, data_samples, mode='tensor'):\n        labels = torch.stack(data_samples)\n        inputs = torch.stack(inputs)\n        outputs = self.linear(inputs)\n        if mode == 'tensor':\n            return outputs\n        elif mode == 'loss':\n            loss = (labels - outputs).sum()\n            outputs = dict(loss=loss)\n            return outputs\n        else:\n            return outputs\n\n\nclass ToyModel1(BaseModel, ToyModel):\n\n    def __init__(self):\n        super().__init__()\n\n    def forward(self, *args, **kwargs):\n        return super(BaseModel, self).forward(*args, **kwargs)\n\n\nclass ToyModel2(BaseModel):\n\n    def __init__(self):\n        super().__init__()\n        self.teacher = ToyModel1()\n        self.student = ToyModel1()\n        self.semi_test_cfg = dict(predict_on='teacher')\n\n    def forward(self, *args, **kwargs):\n        return self.student(*args, **kwargs)\n\n\n@DATASETS.register_module(force=True)\nclass DummyDataset(Dataset):\n    METAINFO = dict()  # type: ignore\n    data = torch.randn(12, 2)\n    label = torch.ones(12)\n\n    @property\n    def metainfo(self):\n        return self.METAINFO\n\n    def __len__(self):\n        return self.data.size(0)\n\n    def __getitem__(self, index):\n        return dict(inputs=self.data[index], data_samples=self.label[index])\n\n\nclass TestTeacherStudentValLoop(TestCase):\n\n    def setUp(self):\n        self.temp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.temp_dir.cleanup()\n\n    def test_teacher_student_val_loop(self):\n        device = 'cuda:0' if torch.cuda.is_available() else 'cpu'\n        model = ToyModel2().to(device)\n        evaluator = Mock()\n        evaluator.evaluate = Mock(return_value=dict(acc=0.5))\n        evaluator.__class__ = Evaluator\n        runner = Runner(\n            model=model,\n            train_dataloader=dict(\n                dataset=dict(type='DummyDataset'),\n                sampler=dict(type='DefaultSampler', shuffle=True),\n                batch_size=3,\n                num_workers=0),\n            val_dataloader=dict(\n                dataset=dict(type='DummyDataset'),\n                sampler=dict(type='DefaultSampler', shuffle=False),\n                batch_size=3,\n                num_workers=0),\n            val_evaluator=evaluator,\n            work_dir=self.temp_dir.name,\n            default_scope='mmdet',\n            optim_wrapper=OptimWrapper(\n                torch.optim.Adam(ToyModel().parameters())),\n            train_cfg=dict(by_epoch=True, max_epochs=2, val_interval=1),\n            val_cfg=dict(type='TeacherStudentValLoop'),\n            default_hooks=dict(logger=dict(type='LoggerHook', interval=1)),\n            experiment_name='test1')\n        runner.train()\n"
  },
  {
    "path": "tests/test_engine/test_schedulers/test_quadratic_warmup.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom mmengine.optim.scheduler import _ParamScheduler\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.engine.schedulers import (QuadraticWarmupLR,\n                                     QuadraticWarmupMomentum,\n                                     QuadraticWarmupParamScheduler)\n\n\nclass ToyModel(torch.nn.Module):\n\n    def __init__(self):\n        super().__init__()\n        self.conv1 = torch.nn.Conv2d(1, 1, 1)\n        self.conv2 = torch.nn.Conv2d(1, 1, 1)\n\n    def forward(self, x):\n        return self.conv2(F.relu(self.conv1(x)))\n\n\nclass TestQuadraticWarmupScheduler(TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.model = ToyModel()\n        self.optimizer = optim.SGD(\n            self.model.parameters(), lr=0.05, momentum=0.01, weight_decay=5e-4)\n\n    def _test_scheduler_value(self,\n                              schedulers,\n                              targets,\n                              epochs=10,\n                              param_name='lr'):\n        if isinstance(schedulers, _ParamScheduler):\n            schedulers = [schedulers]\n        for epoch in range(epochs):\n            for param_group, target in zip(self.optimizer.param_groups,\n                                           targets):\n                print(param_group[param_name])\n                assert_allclose(\n                    target[epoch],\n                    param_group[param_name],\n                    msg='{} is wrong in epoch {}: expected {}, got {}'.format(\n                        param_name, epoch, target[epoch],\n                        param_group[param_name]),\n                    atol=1e-5,\n                    rtol=0)\n            [scheduler.step() for scheduler in schedulers]\n\n    def test_quadratic_warmup_scheduler(self):\n        with self.assertRaises(ValueError):\n            QuadraticWarmupParamScheduler(self.optimizer, param_name='lr')\n        epochs = 10\n        iters = 5\n        warmup_factor = [pow((i + 1) / float(iters), 2) for i in range(iters)]\n        single_targets = [x * 0.05 for x in warmup_factor] + [0.05] * (\n            epochs - iters)\n        targets = [single_targets, [x * epochs for x in single_targets]]\n        scheduler = QuadraticWarmupParamScheduler(\n            self.optimizer, param_name='lr', end=iters)\n        self._test_scheduler_value(scheduler, targets, epochs)\n\n    def test_quadratic_warmup_scheduler_convert_iterbased(self):\n        epochs = 10\n        end = 5\n        epoch_length = 11\n\n        iters = end * epoch_length\n        warmup_factor = [pow((i + 1) / float(iters), 2) for i in range(iters)]\n        single_targets = [x * 0.05 for x in warmup_factor] + [0.05] * (\n            epochs * epoch_length - iters)\n        targets = [single_targets, [x * epochs for x in single_targets]]\n        scheduler = QuadraticWarmupParamScheduler.build_iter_from_epoch(\n            self.optimizer,\n            param_name='lr',\n            end=end,\n            epoch_length=epoch_length)\n        self._test_scheduler_value(scheduler, targets, epochs * epoch_length)\n\n    def test_quadratic_warmup_lr(self):\n        epochs = 10\n        iters = 5\n        warmup_factor = [pow((i + 1) / float(iters), 2) for i in range(iters)]\n        single_targets = [x * 0.05 for x in warmup_factor] + [0.05] * (\n            epochs - iters)\n        targets = [single_targets, [x * epochs for x in single_targets]]\n        scheduler = QuadraticWarmupLR(self.optimizer, end=iters)\n        self._test_scheduler_value(scheduler, targets, epochs)\n\n    def test_quadratic_warmup_momentum(self):\n        epochs = 10\n        iters = 5\n        warmup_factor = [pow((i + 1) / float(iters), 2) for i in range(iters)]\n        single_targets = [x * 0.01 for x in warmup_factor] + [0.01] * (\n            epochs - iters)\n        targets = [single_targets, [x * epochs for x in single_targets]]\n        scheduler = QuadraticWarmupMomentum(self.optimizer, end=iters)\n        self._test_scheduler_value(\n            scheduler, targets, epochs, param_name='momentum')\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_cityscapes_metric.py",
    "content": "import os\nimport os.path as osp\nimport tempfile\nimport unittest\n\nimport numpy as np\nimport torch\nfrom PIL import Image\n\nfrom mmdet.evaluation import CityScapesMetric\n\ntry:\n    import cityscapesscripts\nexcept ImportError:\n    cityscapesscripts = None\n\n\nclass TestCityScapesMetric(unittest.TestCase):\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    @unittest.skipIf(cityscapesscripts is None,\n                     'cityscapesscripts is not installed.')\n    def test_init(self):\n        # test with outfile_prefix = None\n        with self.assertRaises(AssertionError):\n            CityScapesMetric(outfile_prefix=None)\n\n    @unittest.skipIf(cityscapesscripts is None,\n                     'cityscapesscripts is not installed.')\n    def test_evaluate(self):\n        dummy_mask1 = np.zeros((1, 20, 20), dtype=np.uint8)\n        dummy_mask1[:, :10, :10] = 1\n        dummy_mask2 = np.zeros((1, 20, 20), dtype=np.uint8)\n        dummy_mask2[:, :10, :10] = 1\n\n        self.outfile_prefix = osp.join(self.tmp_dir.name, 'test')\n        self.seg_prefix = osp.join(self.tmp_dir.name, 'cityscapes/gtFine/val')\n        city = 'lindau'\n        sequenceNb = '000000'\n        frameNb = '000019'\n        img_name1 = f'{city}_{sequenceNb}_{frameNb}_gtFine_instanceIds.png'\n        img_path1 = osp.join(self.seg_prefix, city, img_name1)\n\n        frameNb = '000020'\n        img_name2 = f'{city}_{sequenceNb}_{frameNb}_gtFine_instanceIds.png'\n        img_path2 = osp.join(self.seg_prefix, city, img_name2)\n        os.makedirs(osp.join(self.seg_prefix, city))\n\n        masks1 = np.zeros((20, 20), dtype=np.int32)\n        masks1[:10, :10] = 24 * 1000\n        Image.fromarray(masks1).save(img_path1)\n\n        masks2 = np.zeros((20, 20), dtype=np.int32)\n        masks2[:10, :10] = 24 * 1000 + 1\n        Image.fromarray(masks2).save(img_path2)\n\n        data_samples = [{\n            'img_path': img_path1,\n            'pred_instances': {\n                'scores': torch.from_numpy(np.array([1.0])),\n                'labels': torch.from_numpy(np.array([0])),\n                'masks': torch.from_numpy(dummy_mask1)\n            }\n        }, {\n            'img_path': img_path2,\n            'pred_instances': {\n                'scores': torch.from_numpy(np.array([0.98])),\n                'labels': torch.from_numpy(np.array([1])),\n                'masks': torch.from_numpy(dummy_mask2)\n            }\n        }]\n\n        target = {'cityscapes/mAP': 0.5, 'cityscapes/AP@50': 0.5}\n        metric = CityScapesMetric(\n            seg_prefix=self.seg_prefix,\n            format_only=False,\n            outfile_prefix=self.outfile_prefix)\n        metric.dataset_meta = dict(\n            classes=('person', 'rider', 'car', 'truck', 'bus', 'train',\n                     'motorcycle', 'bicycle'))\n        metric.process({}, data_samples)\n        results = metric.evaluate(size=2)\n        self.assertDictEqual(results, target)\n        del metric\n        self.assertTrue(not osp.exists('{self.outfile_prefix}.results'))\n\n        # test format_only\n        metric = CityScapesMetric(\n            seg_prefix=self.seg_prefix,\n            format_only=True,\n            outfile_prefix=self.outfile_prefix)\n        metric.dataset_meta = dict(\n            classes=('person', 'rider', 'car', 'truck', 'bus', 'train',\n                     'motorcycle', 'bicycle'))\n        metric.process({}, data_samples)\n        results = metric.evaluate(size=2)\n        self.assertDictEqual(results, dict())\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_coco_metric.py",
    "content": "import os.path as osp\nimport tempfile\nfrom unittest import TestCase\n\nimport numpy as np\nimport pycocotools.mask as mask_util\nimport torch\nfrom mmengine.fileio import dump\n\nfrom mmdet.evaluation import CocoMetric\n\n\nclass TestCocoMetric(TestCase):\n\n    def _create_dummy_coco_json(self, json_name):\n        dummy_mask = np.zeros((10, 10), order='F', dtype=np.uint8)\n        dummy_mask[:5, :5] = 1\n        rle_mask = mask_util.encode(dummy_mask)\n        rle_mask['counts'] = rle_mask['counts'].decode('utf-8')\n        image = {\n            'id': 0,\n            'width': 640,\n            'height': 640,\n            'file_name': 'fake_name.jpg',\n        }\n\n        annotation_1 = {\n            'id': 1,\n            'image_id': 0,\n            'category_id': 0,\n            'area': 400,\n            'bbox': [50, 60, 20, 20],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        annotation_2 = {\n            'id': 2,\n            'image_id': 0,\n            'category_id': 0,\n            'area': 900,\n            'bbox': [100, 120, 30, 30],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        annotation_3 = {\n            'id': 3,\n            'image_id': 0,\n            'category_id': 1,\n            'area': 1600,\n            'bbox': [150, 160, 40, 40],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        annotation_4 = {\n            'id': 4,\n            'image_id': 0,\n            'category_id': 0,\n            'area': 10000,\n            'bbox': [250, 260, 100, 100],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        categories = [\n            {\n                'id': 0,\n                'name': 'car',\n                'supercategory': 'car',\n            },\n            {\n                'id': 1,\n                'name': 'bicycle',\n                'supercategory': 'bicycle',\n            },\n        ]\n\n        fake_json = {\n            'images': [image],\n            'annotations':\n            [annotation_1, annotation_2, annotation_3, annotation_4],\n            'categories': categories\n        }\n\n        dump(fake_json, json_name)\n\n    def _create_dummy_results(self):\n        bboxes = np.array([[50, 60, 70, 80], [100, 120, 130, 150],\n                           [150, 160, 190, 200], [250, 260, 350, 360]])\n        scores = np.array([1.0, 0.98, 0.96, 0.95])\n        labels = np.array([0, 0, 1, 0])\n        dummy_mask = np.zeros((4, 10, 10), dtype=np.uint8)\n        dummy_mask[:, :5, :5] = 1\n        return dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels),\n            masks=torch.from_numpy(dummy_mask))\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    def test_init(self):\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        with self.assertRaisesRegex(KeyError, 'metric should be one of'):\n            CocoMetric(ann_file=fake_json_file, metric='unknown')\n\n    def test_evaluate(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test single coco dataset evaluation\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file,\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n\n        # test box and segm coco dataset evaluation\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file,\n            metric=['bbox', 'segm'],\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n            'coco/segm_mAP': 1.0,\n            'coco/segm_mAP_50': 1.0,\n            'coco/segm_mAP_75': 1.0,\n            'coco/segm_mAP_s': 1.0,\n            'coco/segm_mAP_m': 1.0,\n            'coco/segm_mAP_l': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.segm.json')))\n\n        # test invalid custom metric_items\n        with self.assertRaisesRegex(KeyError,\n                                    'metric item \"invalid\" is not supported'):\n            coco_metric = CocoMetric(\n                ann_file=fake_json_file, metric_items=['invalid'])\n            coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n            coco_metric.process({}, [\n                dict(\n                    pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))\n            ])\n            coco_metric.evaluate(size=1)\n\n        # test custom metric_items\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file, metric_items=['mAP_m'])\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        target = {\n            'coco/bbox_mAP_m': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n\n    def test_classwise_evaluate(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test single coco dataset evaluation\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file, metric='bbox', classwise=True)\n        # coco_metric1 = CocoMetric(\n        #     ann_file=fake_json_file, metric='bbox', classwise=True)\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n            'coco/car_precision': 1.0,\n            'coco/bicycle_precision': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n\n    def test_manually_set_iou_thrs(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n\n        # test single coco dataset evaluation\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file, metric='bbox', iou_thrs=[0.3, 0.6])\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        self.assertEqual(coco_metric.iou_thrs, [0.3, 0.6])\n\n    def test_fast_eval_recall(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test default proposal nums\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file, metric='proposal_fast')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        target = {'coco/AR@100': 1.0, 'coco/AR@300': 1.0, 'coco/AR@1000': 1.0}\n        self.assertDictEqual(eval_results, target)\n\n        # test manually set proposal nums\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file,\n            metric='proposal_fast',\n            proposal_nums=(2, 4))\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        target = {'coco/AR@2': 0.5, 'coco/AR@4': 1.0}\n        self.assertDictEqual(eval_results, target)\n\n    def test_evaluate_proposal(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        coco_metric = CocoMetric(ann_file=fake_json_file, metric='proposal')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        print(eval_results)\n        target = {\n            'coco/AR@100': 1,\n            'coco/AR@300': 1.0,\n            'coco/AR@1000': 1.0,\n            'coco/AR_s@1000': 1.0,\n            'coco/AR_m@1000': 1.0,\n            'coco/AR_l@1000': 1.0\n        }\n        self.assertDictEqual(eval_results, target)\n\n    def test_empty_results(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        coco_metric = CocoMetric(ann_file=fake_json_file, metric='bbox')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        bboxes = np.zeros((0, 4))\n        labels = np.array([])\n        scores = np.array([])\n        dummy_mask = np.zeros((0, 10, 10), dtype=np.uint8)\n        empty_pred = dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels),\n            masks=torch.from_numpy(dummy_mask))\n        coco_metric.process(\n            {},\n            [dict(pred_instances=empty_pred, img_id=0, ori_shape=(640, 640))])\n        # coco api Index error will be caught\n        coco_metric.evaluate(size=1)\n\n    def test_evaluate_without_json(self):\n        dummy_pred = self._create_dummy_results()\n\n        dummy_mask = np.zeros((10, 10), order='F', dtype=np.uint8)\n        dummy_mask[:5, :5] = 1\n        rle_mask = mask_util.encode(dummy_mask)\n        rle_mask['counts'] = rle_mask['counts'].decode('utf-8')\n        instances = [{\n            'bbox_label': 0,\n            'bbox': [50, 60, 70, 80],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }, {\n            'bbox_label': 0,\n            'bbox': [100, 120, 130, 150],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }, {\n            'bbox_label': 1,\n            'bbox': [150, 160, 190, 200],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }, {\n            'bbox_label': 0,\n            'bbox': [250, 260, 350, 360],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }]\n        coco_metric = CocoMetric(\n            ann_file=None,\n            metric=['bbox', 'segm'],\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process({}, [\n            dict(\n                pred_instances=dummy_pred,\n                img_id=0,\n                ori_shape=(640, 640),\n                instances=instances)\n        ])\n        eval_results = coco_metric.evaluate(size=1)\n        print(eval_results)\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n            'coco/segm_mAP': 1.0,\n            'coco/segm_mAP_50': 1.0,\n            'coco/segm_mAP_75': 1.0,\n            'coco/segm_mAP_s': 1.0,\n            'coco/segm_mAP_m': 1.0,\n            'coco/segm_mAP_l': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.segm.json')))\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.gt.json')))\n\n    def test_format_only(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        with self.assertRaises(AssertionError):\n            CocoMetric(\n                ann_file=fake_json_file,\n                classwise=False,\n                format_only=True,\n                outfile_prefix=None)\n\n        coco_metric = CocoMetric(\n            ann_file=fake_json_file,\n            metric='bbox',\n            classwise=False,\n            format_only=True,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = coco_metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, dict())\n        self.assertTrue(osp.exists(f'{self.tmp_dir.name}/test.bbox.json'))\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_coco_occluded_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom tempfile import TemporaryDirectory\n\nimport mmengine\nimport numpy as np\n\nfrom mmdet.datasets import CocoDataset\nfrom mmdet.evaluation import CocoOccludedSeparatedMetric\n\n\ndef test_coco_occluded_separated_metric():\n    ann = [[\n        'fake1.jpg', 'person', 8, [219.9, 176.12, 11.14, 34.23], {\n            'size': [480, 640],\n            'counts': b'nYW31n>2N2FNbA48Kf=?XBDe=m0OM3M4YOPB8_>L4JXao5'\n        }\n    ]] * 3\n    dummy_mask = np.zeros((10, 10), dtype=np.uint8)\n    dummy_mask[:5, :5] = 1\n    rle = {\n        'size': [480, 640],\n        'counts': b'nYW31n>2N2FNbA48Kf=?XBDe=m0OM3M4YOPB8_>L4JXao5'\n    }\n    res = [(None,\n            dict(\n                img_id=0,\n                bboxes=np.array([[50, 60, 70, 80]] * 2),\n                masks=[rle] * 2,\n                labels=np.array([0, 1], dtype=np.int64),\n                scores=np.array([0.77, 0.77])))] * 3\n\n    tempdir = TemporaryDirectory()\n    ann_path = osp.join(tempdir.name, 'coco_occluded.pkl')\n    mmengine.dump(ann, ann_path)\n\n    metric = CocoOccludedSeparatedMetric(\n        ann_file='tests/data/coco_sample.json',\n        occluded_ann=ann_path,\n        separated_ann=ann_path,\n        metric=[])\n    metric.dataset_meta = CocoDataset.METAINFO\n    eval_res = metric.compute_metrics(res)\n    assert isinstance(eval_res, dict)\n    assert eval_res['occluded_recall'] == 100\n    assert eval_res['separated_recall'] == 100\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_coco_panoptic_metric.py",
    "content": "import os\nimport os.path as osp\nimport tempfile\nimport unittest\nfrom copy import deepcopy\n\nimport mmcv\nimport numpy as np\nimport torch\nfrom mmengine.fileio import dump\n\nfrom mmdet.evaluation import INSTANCE_OFFSET, CocoPanopticMetric\n\ntry:\n    import panopticapi\nexcept ImportError:\n    panopticapi = None\n\n\nclass TestCocoPanopticMetric(unittest.TestCase):\n\n    def _create_panoptic_gt_annotations(self, ann_file, seg_map_dir):\n        categories = [{\n            'id': 0,\n            'name': 'person',\n            'supercategory': 'person',\n            'isthing': 1\n        }, {\n            'id': 1,\n            'name': 'cat',\n            'supercategory': 'cat',\n            'isthing': 1\n        }, {\n            'id': 2,\n            'name': 'dog',\n            'supercategory': 'dog',\n            'isthing': 1\n        }, {\n            'id': 3,\n            'name': 'wall',\n            'supercategory': 'wall',\n            'isthing': 0\n        }]\n\n        images = [{\n            'id': 0,\n            'width': 80,\n            'height': 60,\n            'file_name': 'fake_name1.jpg',\n        }]\n\n        annotations = [{\n            'segments_info': [{\n                'id': 1,\n                'category_id': 0,\n                'area': 400,\n                'bbox': [10, 10, 10, 40],\n                'iscrowd': 0\n            }, {\n                'id': 2,\n                'category_id': 0,\n                'area': 400,\n                'bbox': [30, 10, 10, 40],\n                'iscrowd': 0\n            }, {\n                'id': 3,\n                'category_id': 2,\n                'iscrowd': 0,\n                'bbox': [50, 10, 10, 5],\n                'area': 50\n            }, {\n                'id': 4,\n                'category_id': 3,\n                'iscrowd': 0,\n                'bbox': [0, 0, 80, 60],\n                'area': 3950\n            }],\n            'file_name':\n            'fake_name1.png',\n            'image_id':\n            0\n        }]\n\n        gt_json = {\n            'images': images,\n            'annotations': annotations,\n            'categories': categories\n        }\n\n        # 4 is the id of the background class annotation.\n        gt = np.zeros((60, 80), dtype=np.int64) + 4\n        gt_bboxes = np.array(\n            [[10, 10, 10, 40], [30, 10, 10, 40], [50, 10, 10, 5]],\n            dtype=np.int64)\n        for i in range(3):\n            x, y, w, h = gt_bboxes[i]\n            gt[y:y + h, x:x + w] = i + 1  # id starts from 1\n\n        rgb_gt_seg_map = np.zeros(gt.shape + (3, ), dtype=np.uint8)\n        rgb_gt_seg_map[:, :, 2] = gt // (256 * 256)\n        rgb_gt_seg_map[:, :, 1] = gt % (256 * 256) // 256\n        rgb_gt_seg_map[:, :, 0] = gt % 256\n\n        img_path = osp.join(seg_map_dir, 'fake_name1.png')\n        mmcv.imwrite(rgb_gt_seg_map[:, :, ::-1], img_path)\n        dump(gt_json, ann_file)\n\n        return gt_json\n\n    def _create_panoptic_data_samples(self):\n        # predictions\n        # TP for background class, IoU=3576/4324=0.827\n        # 2 the category id of the background class\n        pred = np.zeros((60, 80), dtype=np.int64) + 2\n        pred_bboxes = np.array(\n            [\n                [11, 11, 10, 40],  # TP IoU=351/449=0.78\n                [38, 10, 10, 40],  # FP\n                [51, 10, 10, 5]  # TP IoU=45/55=0.818\n            ],\n            dtype=np.int64)\n        pred_labels = np.array([0, 0, 1], dtype=np.int64)\n        for i in range(3):\n            x, y, w, h = pred_bboxes[i]\n            pred[y:y + h, x:x + w] = (i + 1) * INSTANCE_OFFSET + pred_labels[i]\n\n        data_samples = [{\n            'img_id':\n            0,\n            'ori_shape': (60, 80),\n            'img_path':\n            'xxx/fake_name1.jpg',\n            'segments_info': [{\n                'id': 1,\n                'category': 0,\n                'is_thing': 1\n            }, {\n                'id': 2,\n                'category': 0,\n                'is_thing': 1\n            }, {\n                'id': 3,\n                'category': 1,\n                'is_thing': 1\n            }, {\n                'id': 4,\n                'category': 2,\n                'is_thing': 0\n            }],\n            'seg_map_path':\n            osp.join(self.gt_seg_dir, 'fake_name1.png'),\n            'pred_panoptic_seg': {\n                'sem_seg': torch.from_numpy(pred).unsqueeze(0)\n            },\n        }]\n\n        return data_samples\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n        self.gt_json_path = osp.join(self.tmp_dir.name, 'gt.json')\n        self.gt_seg_dir = osp.join(self.tmp_dir.name, 'gt_seg')\n        os.mkdir(self.gt_seg_dir)\n        self._create_panoptic_gt_annotations(self.gt_json_path,\n                                             self.gt_seg_dir)\n        self.dataset_meta = {\n            'classes': ('person', 'dog', 'wall'),\n            'thing_classes': ('person', 'dog'),\n            'stuff_classes': ('wall', )\n        }\n        self.target = {\n            'coco_panoptic/PQ': 67.86874803219071,\n            'coco_panoptic/SQ': 80.89770126158936,\n            'coco_panoptic/RQ': 83.33333333333334,\n            'coco_panoptic/PQ_th': 60.45252075318891,\n            'coco_panoptic/SQ_th': 79.9959505972869,\n            'coco_panoptic/RQ_th': 75.0,\n            'coco_panoptic/PQ_st': 82.70120259019427,\n            'coco_panoptic/SQ_st': 82.70120259019427,\n            'coco_panoptic/RQ_st': 100.0\n        }\n        self.data_samples = self._create_panoptic_data_samples()\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    @unittest.skipIf(panopticapi is not None, 'panopticapi is installed')\n    def test_init(self):\n        with self.assertRaises(RuntimeError):\n            CocoPanopticMetric()\n\n    @unittest.skipIf(panopticapi is None, 'panopticapi is not installed')\n    def test_evaluate_without_json(self):\n        # with tmpfile, without json\n        metric = CocoPanopticMetric(\n            ann_file=None,\n            seg_prefix=self.gt_seg_dir,\n            classwise=False,\n            nproc=1,\n            outfile_prefix=None)\n\n        metric.dataset_meta = self.dataset_meta\n        metric.process({}, deepcopy(self.data_samples))\n        eval_results = metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, self.target)\n\n        # without tmpfile and json\n        outfile_prefix = f'{self.tmp_dir.name}/test'\n        metric = CocoPanopticMetric(\n            ann_file=None,\n            seg_prefix=self.gt_seg_dir,\n            classwise=False,\n            nproc=1,\n            outfile_prefix=outfile_prefix)\n\n        metric.dataset_meta = self.dataset_meta\n        metric.process({}, deepcopy(self.data_samples))\n        eval_results = metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, self.target)\n\n    @unittest.skipIf(panopticapi is None, 'panopticapi is not installed')\n    def test_evaluate_with_json(self):\n        # with tmpfile and json\n        metric = CocoPanopticMetric(\n            ann_file=self.gt_json_path,\n            seg_prefix=self.gt_seg_dir,\n            classwise=False,\n            nproc=1,\n            outfile_prefix=None)\n\n        metric.dataset_meta = self.dataset_meta\n        metric.process({}, deepcopy(self.data_samples))\n        eval_results = metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, self.target)\n\n        # classwise\n        metric = CocoPanopticMetric(\n            ann_file=self.gt_json_path,\n            seg_prefix=self.gt_seg_dir,\n            classwise=True,\n            nproc=1,\n            outfile_prefix=None)\n        metric.dataset_meta = self.dataset_meta\n        metric.process({}, deepcopy(self.data_samples))\n        eval_results = metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, self.target)\n\n        # without tmpfile, with json\n        outfile_prefix = f'{self.tmp_dir.name}/test1'\n        metric = CocoPanopticMetric(\n            ann_file=self.gt_json_path,\n            seg_prefix=self.gt_seg_dir,\n            classwise=False,\n            nproc=1,\n            outfile_prefix=outfile_prefix)\n        metric.dataset_meta = self.dataset_meta\n        metric.process({}, deepcopy(self.data_samples))\n        eval_results = metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, self.target)\n\n    @unittest.skipIf(panopticapi is None, 'panopticapi is not installed')\n    def test_format_only(self):\n        with self.assertRaises(AssertionError):\n            metric = CocoPanopticMetric(\n                ann_file=self.gt_json_path,\n                seg_prefix=self.gt_seg_dir,\n                classwise=False,\n                nproc=1,\n                format_only=True,\n                outfile_prefix=None)\n\n        outfile_prefix = f'{self.tmp_dir.name}/test'\n        metric = CocoPanopticMetric(\n            ann_file=self.gt_json_path,\n            seg_prefix=self.gt_seg_dir,\n            classwise=False,\n            nproc=1,\n            format_only=True,\n            outfile_prefix=outfile_prefix)\n        metric.dataset_meta = self.dataset_meta\n        metric.process({}, deepcopy(self.data_samples))\n        eval_results = metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, dict())\n        self.assertTrue(osp.exists(f'{self.tmp_dir.name}/test.panoptic'))\n        self.assertTrue(osp.exists(f'{self.tmp_dir.name}/test.panoptic.json'))\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_coco_video_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport tempfile\nfrom unittest import TestCase\n\nimport numpy as np\nimport pycocotools.mask as mask_util\nimport torch\nfrom mmengine.fileio import dump\nfrom mmengine.structures import BaseDataElement, InstanceData\n\nfrom mmdet.evaluation import CocoVideoMetric\nfrom mmdet.structures import DetDataSample, TrackDataSample\n\n\nclass TestCocoVideoMetric(TestCase):\n\n    def _create_dummy_coco_json(self, json_name):\n        dummy_mask = np.zeros((10, 10), order='F', dtype=np.uint8)\n        dummy_mask[:5, :5] = 1\n        rle_mask = mask_util.encode(dummy_mask)\n        rle_mask['counts'] = rle_mask['counts'].decode('utf-8')\n        image = {\n            'id': 0,\n            'width': 640,\n            'height': 640,\n            'file_name': 'fake_name.jpg',\n        }\n\n        annotation_1 = {\n            'id': 1,\n            'image_id': 0,\n            'category_id': 0,\n            'area': 400,\n            'bbox': [50, 60, 20, 20],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        annotation_2 = {\n            'id': 2,\n            'image_id': 0,\n            'category_id': 0,\n            'area': 900,\n            'bbox': [100, 120, 30, 30],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        annotation_3 = {\n            'id': 3,\n            'image_id': 0,\n            'category_id': 1,\n            'area': 1600,\n            'bbox': [150, 160, 40, 40],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        annotation_4 = {\n            'id': 4,\n            'image_id': 0,\n            'category_id': 0,\n            'area': 10000,\n            'bbox': [250, 260, 100, 100],\n            'iscrowd': 0,\n            'segmentation': rle_mask,\n        }\n\n        categories = [\n            {\n                'id': 0,\n                'name': 'car',\n                'supercategory': 'car',\n            },\n            {\n                'id': 1,\n                'name': 'bicycle',\n                'supercategory': 'bicycle',\n            },\n        ]\n\n        fake_json = {\n            'images': [image],\n            'annotations':\n            [annotation_1, annotation_2, annotation_3, annotation_4],\n            'categories': categories\n        }\n\n        dump(fake_json, json_name)\n\n    def _create_dummy_results(self):\n        bboxes = np.array([[50, 60, 70, 80], [100, 120, 130, 150],\n                           [150, 160, 190, 200], [250, 260, 350, 360]])\n        scores = np.array([1.0, 0.98, 0.96, 0.95])\n        labels = np.array([0, 0, 1, 0])\n        dummy_mask = np.zeros((4, 10, 10), dtype=np.uint8)\n        dummy_mask[:, :5, :5] = 1\n        return dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels),\n            masks=torch.from_numpy(dummy_mask))\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    def test_init(self):\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        with self.assertRaisesRegex(KeyError, 'metric should be one of'):\n            CocoVideoMetric(ann_file=fake_json_file, metric='unknown')\n\n    def test_evaluate(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test single coco dataset evaluation\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file,\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        pred_det_instances = InstanceData(**dummy_pred)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_instances = pred_det_instances\n        img_data_sample.set_metainfo(\n            dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n\n        # test box and segm coco dataset evaluation\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file,\n            metric=['bbox', 'segm'],\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n            'coco/segm_mAP': 1.0,\n            'coco/segm_mAP_50': 1.0,\n            'coco/segm_mAP_75': 1.0,\n            'coco/segm_mAP_s': 1.0,\n            'coco/segm_mAP_m': 1.0,\n            'coco/segm_mAP_l': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.segm.json')))\n\n        # test invalid custom metric_items\n        with self.assertRaisesRegex(KeyError,\n                                    'metric item \"invalid\" is not supported'):\n            coco_metric = CocoVideoMetric(\n                ann_file=fake_json_file, metric_items=['invalid'])\n            coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n            coco_metric.process(\n                dict(inputs=None, data_samples=None), predictions)\n            coco_metric.evaluate()\n\n        # test custom metric_items\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file, metric_items=['mAP_m'])\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        target = {\n            'coco/bbox_mAP_m': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n\n    def test_classwise_evaluate(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test single coco dataset evaluation\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file, metric='bbox', classwise=True)\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        pred_det_instances = InstanceData(**dummy_pred)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_instances = pred_det_instances\n        img_data_sample.set_metainfo(\n            dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n            'coco/car_precision': 1.0,\n            'coco/bicycle_precision': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n\n    def test_manually_set_iou_thrs(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n\n        # test single coco dataset evaluation\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file, metric='bbox', iou_thrs=[0.3, 0.6])\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        self.assertEqual(coco_metric.iou_thrs, [0.3, 0.6])\n\n    def test_fast_eval_recall(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test default proposal nums\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file, metric='proposal_fast')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        pred_det_instances = InstanceData(**dummy_pred)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_instances = pred_det_instances\n        img_data_sample.set_metainfo(\n            dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        target = {'coco/AR@100': 1.0, 'coco/AR@300': 1.0, 'coco/AR@1000': 1.0}\n        self.assertDictEqual(eval_results, target)\n\n        # test manually set proposal nums\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file,\n            metric='proposal_fast',\n            proposal_nums=(2, 4))\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        target = {'coco/AR@2': 0.5, 'coco/AR@4': 1.0}\n        self.assertDictEqual(eval_results, target)\n\n    def test_evaluate_proposal(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        coco_metric = CocoVideoMetric(\n            ann_file=fake_json_file, metric='proposal')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        pred_det_instances = InstanceData(**dummy_pred)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_instances = pred_det_instances\n        img_data_sample.set_metainfo(\n            dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        print(eval_results)\n        target = {\n            'coco/AR@100': 1,\n            'coco/AR@300': 1.0,\n            'coco/AR@1000': 1.0,\n            'coco/AR_s@1000': 1.0,\n            'coco/AR_m@1000': 1.0,\n            'coco/AR_l@1000': 1.0\n        }\n        self.assertDictEqual(eval_results, target)\n\n    def test_empty_results(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_coco_json(fake_json_file)\n        coco_metric = CocoVideoMetric(ann_file=fake_json_file, metric='bbox')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        bboxes = np.zeros((0, 4))\n        labels = np.array([])\n        scores = np.array([])\n        dummy_mask = np.zeros((0, 10, 10), dtype=np.uint8)\n        empty_pred = dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels),\n            masks=torch.from_numpy(dummy_mask))\n        pred_det_instances = InstanceData(**empty_pred)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_instances = pred_det_instances\n        img_data_sample.set_metainfo(\n            dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        # coco api Index error will be caught\n        coco_metric.evaluate()\n\n    def test_evaluate_without_json(self):\n        dummy_pred = self._create_dummy_results()\n\n        dummy_mask = np.zeros((10, 10), order='F', dtype=np.uint8)\n        dummy_mask[:5, :5] = 1\n        rle_mask = mask_util.encode(dummy_mask)\n        rle_mask['counts'] = rle_mask['counts'].decode('utf-8')\n        instances = [{\n            'bbox_label': 0,\n            'bbox': [50, 60, 70, 80],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }, {\n            'bbox_label': 0,\n            'bbox': [100, 120, 130, 150],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }, {\n            'bbox_label': 1,\n            'bbox': [150, 160, 190, 200],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }, {\n            'bbox_label': 0,\n            'bbox': [250, 260, 350, 360],\n            'ignore_flag': 0,\n            'mask': rle_mask,\n        }]\n        coco_metric = CocoVideoMetric(\n            ann_file=None,\n            metric=['bbox', 'segm'],\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])\n        pred_det_instances = InstanceData(**dummy_pred)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_instances = pred_det_instances\n        img_data_sample.instances = instances\n        img_data_sample.set_metainfo(\n            dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        coco_metric.process(dict(inputs=None, data_samples=None), predictions)\n        eval_results = coco_metric.evaluate()\n        print(eval_results)\n        target = {\n            'coco/bbox_mAP': 1.0,\n            'coco/bbox_mAP_50': 1.0,\n            'coco/bbox_mAP_75': 1.0,\n            'coco/bbox_mAP_s': 1.0,\n            'coco/bbox_mAP_m': 1.0,\n            'coco/bbox_mAP_l': 1.0,\n            'coco/segm_mAP': 1.0,\n            'coco/segm_mAP_50': 1.0,\n            'coco/segm_mAP_75': 1.0,\n            'coco/segm_mAP_s': 1.0,\n            'coco/segm_mAP_m': 1.0,\n            'coco/segm_mAP_l': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.segm.json')))\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.gt.json')))\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_crowdhuman_metric.py",
    "content": "import os.path as osp\nimport tempfile\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\n\nfrom mmdet.evaluation import CrowdHumanMetric\n\n\nclass TestCrowdHumanMetric(TestCase):\n\n    def _create_dummy_results(self):\n        bboxes = np.array([[1330, 317, 418, 1338], [792, 24, 723, 2017],\n                           [693, 291, 307, 894], [522, 290, 285, 826],\n                           [728, 336, 175, 602], [92, 337, 267, 681]])\n        bboxes[:, 2:4] += bboxes[:, 0:2]\n        scores = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0])\n        return dict(\n            bboxes=torch.from_numpy(bboxes), scores=torch.from_numpy(scores))\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n        self.ann_file_path = \\\n            'tests/data/crowdhuman_dataset/test_annotation_train.odgt',\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    def test_init(self):\n        with self.assertRaisesRegex(KeyError, 'metric should be one of'):\n            CrowdHumanMetric(ann_file=self.ann_file_path[0], metric='unknown')\n\n    def test_evaluate(self):\n        # create dummy data\n        dummy_pred = self._create_dummy_results()\n\n        crowdhuman_metric = CrowdHumanMetric(\n            ann_file=self.ann_file_path[0],\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        crowdhuman_metric.process({}, [\n            dict(\n                pred_instances=dummy_pred,\n                img_id='283554,35288000868e92d4',\n                ori_shape=(1640, 1640))\n        ])\n        eval_results = crowdhuman_metric.evaluate(size=1)\n        target = {\n            'crowd_human/mAP': 0.8333,\n            'crowd_human/mMR': 0.0,\n            'crowd_human/JI': 1.0\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(osp.isfile(osp.join(self.tmp_dir.name, 'test.json')))\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_dump_det_results.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nimport tempfile\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.fileio import load\nfrom torch import Tensor\n\nfrom mmdet.evaluation import DumpDetResults\nfrom mmdet.structures.mask import encode_mask_results\n\n\nclass TestDumpResults(TestCase):\n\n    def test_init(self):\n        with self.assertRaisesRegex(ValueError,\n                                    'The output file must be a pkl file.'):\n            DumpDetResults(out_file_path='./results.json')\n\n    def test_process(self):\n        metric = DumpDetResults(out_file_path='./results.pkl')\n        data_samples = [dict(data=(Tensor([1, 2, 3]), Tensor([4, 5, 6])))]\n        metric.process(None, data_samples)\n        self.assertEqual(len(metric.results), 1)\n        self.assertEqual(metric.results[0]['data'][0].device,\n                         torch.device('cpu'))\n\n        metric = DumpDetResults(out_file_path='./results.pkl')\n        masks = torch.zeros(10, 10, 4)\n        data_samples = [\n            dict(pred_instances=dict(masks=masks), gt_instances=[])\n        ]\n        metric.process(None, data_samples)\n        self.assertEqual(len(metric.results), 1)\n        self.assertEqual(metric.results[0]['pred_instances']['masks'],\n                         encode_mask_results(masks.numpy()))\n        self.assertNotIn('gt_instances', metric.results[0])\n\n    def test_compute_metrics(self):\n        temp_dir = tempfile.TemporaryDirectory()\n        path = osp.join(temp_dir.name, 'results.pkl')\n        metric = DumpDetResults(out_file_path=path)\n        data_samples = [dict(data=(Tensor([1, 2, 3]), Tensor([4, 5, 6])))]\n        metric.process(None, data_samples)\n        metric.compute_metrics(metric.results)\n        self.assertTrue(osp.isfile(path))\n\n        results = load(path)\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0]['data'][0].device, torch.device('cpu'))\n\n        temp_dir.cleanup()\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_lvis_metric.py",
    "content": "import os.path as osp\nimport tempfile\nimport unittest\n\nimport numpy as np\nimport pycocotools.mask as mask_util\nimport torch\n\nfrom mmdet.evaluation.metrics import LVISMetric\n\ntry:\n    import lvis\nexcept ImportError:\n    lvis = None\n\nfrom mmengine.fileio import dump\n\n\nclass TestLVISMetric(unittest.TestCase):\n\n    def _create_dummy_lvis_json(self, json_name):\n        dummy_mask = np.zeros((10, 10), order='F', dtype=np.uint8)\n        dummy_mask[:5, :5] = 1\n        rle_mask = mask_util.encode(dummy_mask)\n        rle_mask['counts'] = rle_mask['counts'].decode('utf-8')\n        image = {\n            'id': 0,\n            'width': 640,\n            'height': 640,\n            'neg_category_ids': [],\n            'not_exhaustive_category_ids': [],\n            'coco_url': 'http://images.cocodataset.org/val2017/0.jpg',\n        }\n\n        annotation_1 = {\n            'id': 1,\n            'image_id': 0,\n            'category_id': 1,\n            'area': 400,\n            'bbox': [50, 60, 20, 20],\n            'segmentation': rle_mask,\n        }\n\n        annotation_2 = {\n            'id': 2,\n            'image_id': 0,\n            'category_id': 1,\n            'area': 900,\n            'bbox': [100, 120, 30, 30],\n            'segmentation': rle_mask,\n        }\n\n        annotation_3 = {\n            'id': 3,\n            'image_id': 0,\n            'category_id': 2,\n            'area': 1600,\n            'bbox': [150, 160, 40, 40],\n            'segmentation': rle_mask,\n        }\n\n        annotation_4 = {\n            'id': 4,\n            'image_id': 0,\n            'category_id': 1,\n            'area': 10000,\n            'bbox': [250, 260, 100, 100],\n            'segmentation': rle_mask,\n        }\n\n        categories = [\n            {\n                'id': 1,\n                'name': 'aerosol_can',\n                'frequency': 'c',\n                'image_count': 64\n            },\n            {\n                'id': 2,\n                'name': 'air_conditioner',\n                'frequency': 'f',\n                'image_count': 364\n            },\n        ]\n\n        fake_json = {\n            'images': [image],\n            'annotations':\n            [annotation_1, annotation_2, annotation_3, annotation_4],\n            'categories': categories\n        }\n\n        dump(fake_json, json_name)\n\n    def _create_dummy_results(self):\n        bboxes = np.array([[50, 60, 70, 80], [100, 120, 130, 150],\n                           [150, 160, 190, 200], [250, 260, 350, 360]])\n        scores = np.array([1.0, 0.98, 0.96, 0.95])\n        labels = np.array([0, 0, 1, 0])\n        dummy_mask = np.zeros((4, 10, 10), dtype=np.uint8)\n        dummy_mask[:, :5, :5] = 1\n        return dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels),\n            masks=torch.from_numpy(dummy_mask))\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_init(self):\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n        with self.assertRaisesRegex(KeyError, 'metric should be one of'):\n            LVISMetric(ann_file=fake_json_file, metric='unknown')\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_evaluate(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test single lvis dataset evaluation\n        lvis_metric = LVISMetric(\n            ann_file=fake_json_file,\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        target = {\n            'lvis/bbox_AP': 1.0,\n            'lvis/bbox_AP50': 1.0,\n            'lvis/bbox_AP75': 1.0,\n            'lvis/bbox_APs': 1.0,\n            'lvis/bbox_APm': 1.0,\n            'lvis/bbox_APl': 1.0,\n            'lvis/bbox_APr': -1.0,\n            'lvis/bbox_APc': 1.0,\n            'lvis/bbox_APf': 1.0\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n\n        # test box and segm lvis dataset evaluation\n        lvis_metric = LVISMetric(\n            ann_file=fake_json_file,\n            metric=['bbox', 'segm'],\n            classwise=False,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        target = {\n            'lvis/bbox_AP': 1.0,\n            'lvis/bbox_AP50': 1.0,\n            'lvis/bbox_AP75': 1.0,\n            'lvis/bbox_APs': 1.0,\n            'lvis/bbox_APm': 1.0,\n            'lvis/bbox_APl': 1.0,\n            'lvis/bbox_APr': -1.0,\n            'lvis/bbox_APc': 1.0,\n            'lvis/bbox_APf': 1.0,\n            'lvis/segm_AP': 1.0,\n            'lvis/segm_AP50': 1.0,\n            'lvis/segm_AP75': 1.0,\n            'lvis/segm_APs': 1.0,\n            'lvis/segm_APm': 1.0,\n            'lvis/segm_APl': 1.0,\n            'lvis/segm_APr': -1.0,\n            'lvis/segm_APc': 1.0,\n            'lvis/segm_APf': 1.0\n        }\n        self.assertDictEqual(eval_results, target)\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))\n        self.assertTrue(\n            osp.isfile(osp.join(self.tmp_dir.name, 'test.segm.json')))\n\n        # test invalid custom metric_items\n        with self.assertRaisesRegex(\n                KeyError,\n                \"metric should be one of 'bbox', 'segm', 'proposal', \"\n                \"'proposal_fast', but got invalid.\"):\n            lvis_metric = LVISMetric(\n                ann_file=fake_json_file, metric=['invalid'])\n            lvis_metric.evaluate(size=1)\n\n        # test custom metric_items\n        lvis_metric = LVISMetric(ann_file=fake_json_file, metric_items=['APm'])\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        target = {\n            'lvis/bbox_APm': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_classwise_evaluate(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test single lvis dataset evaluation\n        lvis_metric = LVISMetric(\n            ann_file=fake_json_file, metric='bbox', classwise=True)\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        target = {\n            'lvis/bbox_AP': 1.0,\n            'lvis/bbox_AP50': 1.0,\n            'lvis/bbox_AP75': 1.0,\n            'lvis/bbox_APs': 1.0,\n            'lvis/bbox_APm': 1.0,\n            'lvis/bbox_APl': 1.0,\n            'lvis/bbox_APr': -1.0,\n            'lvis/bbox_APc': 1.0,\n            'lvis/bbox_APf': 1.0,\n            'lvis/aerosol_can_precision': 1.0,\n            'lvis/air_conditioner_precision': 1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_manually_set_iou_thrs(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n\n        # test single lvis dataset evaluation\n        lvis_metric = LVISMetric(\n            ann_file=fake_json_file, metric='bbox', iou_thrs=[0.3, 0.6])\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        self.assertEqual(lvis_metric.iou_thrs, [0.3, 0.6])\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_fast_eval_recall(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        # test default proposal nums\n        lvis_metric = LVISMetric(\n            ann_file=fake_json_file, metric='proposal_fast')\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        target = {'lvis/AR@100': 1.0, 'lvis/AR@300': 1.0, 'lvis/AR@1000': 1.0}\n        self.assertDictEqual(eval_results, target)\n\n        # test manually set proposal nums\n        lvis_metric = LVISMetric(\n            ann_file=fake_json_file,\n            metric='proposal_fast',\n            proposal_nums=(2, 4))\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        target = {'lvis/AR@2': 0.5, 'lvis/AR@4': 1.0}\n        self.assertDictEqual(eval_results, target)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_evaluate_proposal(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        lvis_metric = LVISMetric(ann_file=fake_json_file, metric='proposal')\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        target = {\n            'lvis/AR@300': 1.0,\n            'lvis/ARs@300': 1.0,\n            'lvis/ARm@300': 1.0,\n            'lvis/ARl@300': 1.0\n        }\n        self.assertDictEqual(eval_results, target)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_empty_results(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n        lvis_metric = LVISMetric(ann_file=fake_json_file, metric='bbox')\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        bboxes = np.zeros((0, 4))\n        labels = np.array([])\n        scores = np.array([])\n        dummy_mask = np.zeros((0, 10, 10), dtype=np.uint8)\n        empty_pred = dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels),\n            masks=torch.from_numpy(dummy_mask))\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=empty_pred, img_id=0, ori_shape=(640, 640))])\n        # lvis api Index error will be caught\n        lvis_metric.evaluate(size=1)\n\n    @unittest.skipIf(lvis is None, 'lvis is not installed.')\n    def test_format_only(self):\n        # create dummy data\n        fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')\n        self._create_dummy_lvis_json(fake_json_file)\n        dummy_pred = self._create_dummy_results()\n\n        with self.assertRaises(AssertionError):\n            LVISMetric(\n                ann_file=fake_json_file,\n                classwise=False,\n                format_only=True,\n                outfile_prefix=None)\n\n        lvis_metric = LVISMetric(\n            ann_file=fake_json_file,\n            metric='bbox',\n            classwise=False,\n            format_only=True,\n            outfile_prefix=f'{self.tmp_dir.name}/test')\n        lvis_metric.dataset_meta = dict(\n            classes=['aerosol_can', 'air_conditioner'])\n        lvis_metric.process(\n            {},\n            [dict(pred_instances=dummy_pred, img_id=0, ori_shape=(640, 640))])\n        eval_results = lvis_metric.evaluate(size=1)\n        self.assertDictEqual(eval_results, dict())\n        self.assertTrue(osp.exists(f'{self.tmp_dir.name}/test.bbox.json'))\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_mot_challenge_metrics.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os\nimport tempfile\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import BaseDataElement, InstanceData\n\nfrom mmdet.evaluation import MOTChallengeMetric\nfrom mmdet.structures import DetDataSample, TrackDataSample\n\n\nclass TestMOTChallengeMetric(TestCase):\n\n    def test_init(self):\n        with self.assertRaisesRegex(KeyError, 'metric unknown is not'):\n            MOTChallengeMetric(metric='unknown')\n        with self.assertRaises(AssertionError):\n            MOTChallengeMetric(benchmark='MOT21')\n\n    def __del__(self):\n        self.tmp_dir.cleanup()\n\n    @staticmethod\n    def _get_predictions_demo():\n        instances = [{\n            'bbox_label': 0,\n            'bbox': [0, 0, 100, 100],\n            'ignore_flag': 0,\n            'instance_id': 1,\n            'mot_conf': 1.0,\n            'category_id': 1,\n            'visibility': 1.0\n        }, {\n            'bbox_label': 0,\n            'bbox': [0, 0, 100, 100],\n            'ignore_flag': 0,\n            'instance_id': 2,\n            'mot_conf': 1.0,\n            'category_id': 1,\n            'visibility': 1.0\n        }]\n        instances_2 = copy.deepcopy(instances)\n        sep = os.sep\n        pred_instances_data = dict(\n            bboxes=torch.tensor([\n                [0, 0, 100, 100],\n                [0, 0, 100, 40],\n            ]),\n            instances_id=torch.tensor([1, 2]),\n            scores=torch.tensor([1.0, 1.0]))\n        pred_instances_data_2 = copy.deepcopy(pred_instances_data)\n        pred_instances = InstanceData(**pred_instances_data)\n        pred_instances_2 = InstanceData(**pred_instances_data_2)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_track_instances = pred_instances\n        img_data_sample.instances = instances\n        img_data_sample.set_metainfo(\n            dict(\n                frame_id=0,\n                ori_video_length=2,\n                video_length=2,\n                img_id=1,\n                img_path=f'xxx{sep}MOT17-09-DPM{sep}img1{sep}000001.jpg',\n            ))\n        img_data_sample_2 = DetDataSample()\n        img_data_sample_2.pred_track_instances = pred_instances_2\n        img_data_sample_2.instances = instances_2\n        img_data_sample_2.set_metainfo(\n            dict(\n                frame_id=1,\n                ori_video_length=2,\n                video_length=2,\n                img_id=2,\n                img_path=f'xxx{sep}MOT17-09-DPM{sep}img1{sep}000002.jpg',\n            ))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [\n            img_data_sample, img_data_sample_2\n        ]\n        # [TrackDataSample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        return predictions\n\n    def _test_evaluate(self, format_only, outfile_predix=None):\n        \"\"\"Test using the metric in the same way as Evaluator.\"\"\"\n        metric = MOTChallengeMetric(\n            metric=['HOTA', 'CLEAR', 'Identity'],\n            format_only=format_only,\n            outfile_prefix=outfile_predix)\n        metric.dataset_meta = {'classes': ('pedestrian', )}\n        data_batch = dict(input=None, data_samples=None)\n        predictions = self._get_predictions_demo()\n        metric.process(data_batch, predictions)\n        eval_results = metric.evaluate()\n        return eval_results\n\n    def test_evaluate(self):\n        eval_results = self._test_evaluate(False)\n        target = {\n            'motchallenge-metric/IDF1': 0.5,\n            'motchallenge-metric/MOTA': 0,\n            'motchallenge-metric/HOTA': 0.755,\n            'motchallenge-metric/IDSW': 0,\n        }\n        for key in target:\n            assert eval_results[key] - target[key] < 1e-3\n\n    def test_evaluate_format_only(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n        eval_results = self._test_evaluate(\n            True, outfile_predix=self.tmp_dir.name)\n        assert eval_results == dict()\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_openimages_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport numpy as np\nimport torch\n\nfrom mmdet.datasets import OpenImagesDataset\nfrom mmdet.evaluation import OpenImagesMetric\nfrom mmdet.utils import register_all_modules\n\n\nclass TestOpenImagesMetric(unittest.TestCase):\n\n    def _create_dummy_results(self):\n        bboxes = np.array([[23.2172, 31.7541, 987.3413, 357.8443],\n                           [100, 120, 130, 150], [150, 160, 190, 200],\n                           [250, 260, 350, 360]])\n        scores = np.array([1.0, 0.98, 0.96, 0.95])\n        labels = np.array([0, 0, 0, 0])\n        return dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels))\n\n    def test_init(self):\n        # test invalid iou_thrs\n        with self.assertRaises(AssertionError):\n            OpenImagesMetric(iou_thrs={'a', 0.5}, ioa_thrs={'b', 0.5})\n        # test ioa and iou_thrs length not equal\n        with self.assertRaises(AssertionError):\n            OpenImagesMetric(iou_thrs=[0.5, 0.75], ioa_thrs=[0.5])\n\n        metric = OpenImagesMetric(iou_thrs=0.6)\n        self.assertEqual(metric.iou_thrs, [0.6])\n\n    def test_eval(self):\n        register_all_modules()\n        dataset = OpenImagesDataset(\n            data_root='tests/data/OpenImages/',\n            ann_file='annotations/oidv6-train-annotations-bbox.csv',\n            data_prefix=dict(img='OpenImages/train/'),\n            label_file='annotations/class-descriptions-boxable.csv',\n            hierarchy_file='annotations/bbox_labels_600_hierarchy.json',\n            meta_file='annotations/image-metas.pkl',\n            pipeline=[\n                dict(type='LoadAnnotations', with_bbox=True),\n                dict(\n                    type='PackDetInputs',\n                    meta_keys=('img_id', 'img_path', 'instances'))\n            ])\n        dataset.full_init()\n        data_sample = dataset[0]['data_samples'].to_dict()\n        data_sample['pred_instances'] = self._create_dummy_results()\n\n        metric = OpenImagesMetric()\n        metric.dataset_meta = dataset.metainfo\n        metric.process({}, [data_sample])\n        results = metric.evaluate(size=len(dataset))\n        targets = {'openimages/AP50': 1.0, 'openimages/mAP': 1.0}\n        self.assertDictEqual(results, targets)\n\n        # test multi-threshold\n        metric = OpenImagesMetric(iou_thrs=[0.1, 0.5], ioa_thrs=[0.1, 0.5])\n        metric.dataset_meta = dataset.metainfo\n        metric.process({}, [data_sample])\n        results = metric.evaluate(size=len(dataset))\n        targets = {\n            'openimages/AP10': 1.0,\n            'openimages/AP50': 1.0,\n            'openimages/mAP': 1.0\n        }\n        self.assertDictEqual(results, targets)\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_reid_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.registry import init_default_scope\n\nfrom mmdet.registry import METRICS\nfrom mmdet.structures import ReIDDataSample\n\n\nclass TestReIDMetrics(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    def test_evaluate(self):\n        \"\"\"Test using the metric in the same way as Evaluator.\"\"\"\n        data_samples = [\n            ReIDDataSample().set_gt_label(i).to_dict()\n            for i in [0, 0, 1, 1, 1, 1]\n        ]\n        pred_batch = [\n            dict(pred_feature=torch.tensor(\n                [1., .0, .1])),  # [x,√,x,x,x],R1=0,R5=1,AP=0.50\n            dict(pred_feature=torch.tensor(\n                [.8, .0, .0])),  # [x,√,x,x,x],R1=0,R5=1,AP=0.50\n            dict(pred_feature=torch.tensor(\n                [.1, 1., .1])),  # [√,√,x,√,x],R1=1,R5=1,AP≈0.92\n            dict(pred_feature=torch.tensor(\n                [.0, .9, .1])),  # [√,√,√,x,x],R1=1,R5=1,AP=1.00\n            dict(pred_feature=torch.tensor(\n                [.9, .1, .0])),  # [x,x,√,√,√],R1=0,R5=1,AP≈0.48\n            dict(pred_feature=torch.tensor(\n                [.0, .1, 1.])),  # [√,√,x,√,x],R1=1,R5=1,AP≈0.92\n        ]\n        # get union\n        for idx in range(len(data_samples)):\n            data_samples[idx] = {**data_samples[idx], **pred_batch[idx]}\n\n        metric = METRICS.build(\n            dict(\n                type='ReIDMetrics',\n                metric=['mAP', 'CMC'],\n                metric_options=dict(rank_list=[1, 5], max_rank=5),\n            ))\n\n        prefix = 'reid-metric'\n        data_batch = dict(input=None, data_samples=None)\n        metric.process(data_batch, data_samples)\n        results = metric.evaluate(6)\n        self.assertIsInstance(results, dict)\n        self.assertEqual(results[f'{prefix}/mAP'], 0.719)\n        self.assertEqual(results[f'{prefix}/R1'], 0.5)\n        self.assertEqual(results[f'{prefix}/R5'], 1.0)\n"
  },
  {
    "path": "tests/test_evaluation/test_metrics/test_youtube_vis_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os\nimport tempfile\nfrom unittest import TestCase\n\nimport numpy as np\nimport pycocotools.mask as mask_util\nimport torch\nfrom mmengine.registry import init_default_scope\nfrom mmengine.structures import BaseDataElement, InstanceData\n\nfrom mmdet.registry import METRICS\nfrom mmdet.structures import DetDataSample, TrackDataSample\n\n\nclass TestYouTubeVISMetric(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    def setUp(self):\n        self.tmp_dir = tempfile.TemporaryDirectory()\n\n    def tearDown(self):\n        self.tmp_dir.cleanup()\n\n    def _create_dummy_results(self, track_id):\n        bboxes = np.array([[100, 100, 150, 150]])\n        scores = np.array([1.0])\n        labels = np.array([0])\n        instance_id = np.array([track_id])\n        dummy_mask = np.zeros((1, 720, 1280), dtype=np.uint8)\n        dummy_mask[:, 100:150, 100:150] = 1\n        return dict(\n            bboxes=torch.from_numpy(bboxes),\n            scores=torch.from_numpy(scores),\n            labels=torch.from_numpy(labels),\n            instances_id=torch.from_numpy(instance_id),\n            masks=torch.from_numpy(dummy_mask))\n\n    def test_format_only(self):\n        outfile_prefix = f'{self.tmp_dir.name}/result'\n        vis_metric = METRICS.build(\n            dict(\n                type='YouTubeVISMetric',\n                format_only=True,\n                outfile_prefix=outfile_prefix,\n            ))\n        dummy_pred = self._create_dummy_results(track_id=0)\n        dummy_mask = np.zeros((720, 1280), order='F', dtype=np.uint8)\n        dummy_mask[100:150, 100:150] = 1\n        rle_mask = mask_util.encode(dummy_mask)\n        rle_mask['counts'] = rle_mask['counts'].decode('utf-8')\n        instances = [{\n            'bbox_label': 0,\n            'bbox': [100, 100, 150, 150],\n            'ignore_flag': 0,\n            'instance_id': 1,\n            'mask': rle_mask,\n        }]\n        vis_metric.dataset_meta = dict(classes=['car', 'train'])\n        data_batch = dict(inputs=None, data_samples=None)\n        gt_insatnce = InstanceData(**dummy_pred)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_track_instances = gt_insatnce\n        img_data_sample.set_metainfo(\n            dict(\n                img_id=0,\n                video_id=1,\n                ori_video_length=1,\n                ori_shape=(720, 1280),\n                instances=instances))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        vis_metric.process(data_batch, predictions)\n        vis_metric.evaluate(size=1)\n        assert os.path.exists(f'{outfile_prefix}.json')\n        assert os.path.exists(f'{outfile_prefix}.submission_file.zip')\n\n    def test_evaluate(self):\n        \"\"\"Test using the metric in the same way as Evaluator.\"\"\"\n        dummy_pred_1 = self._create_dummy_results(track_id=1)\n        dummy_pred_2 = self._create_dummy_results(track_id=1)\n        dummy_pred_3 = self._create_dummy_results(track_id=2)\n\n        dummy_mask = np.zeros((720, 1280), order='F', dtype=np.uint8)\n        dummy_mask[100:150, 100:150] = 1\n        rle_mask = mask_util.encode(dummy_mask)\n        rle_mask['counts'] = rle_mask['counts'].decode('utf-8')\n        instances_1 = [{\n            'bbox_label': 0,\n            'bbox': [100, 100, 150, 150],\n            'ignore_flag': 0,\n            'instance_id': 1,\n            'mask': rle_mask,\n        }]\n        instances_2 = [{\n            'bbox_label': 0,\n            'bbox': [100, 100, 150, 150],\n            'ignore_flag': 0,\n            'instance_id': 2,\n            'mask': rle_mask,\n        }]\n        vis_metric = METRICS.build(\n            dict(\n                type='YouTubeVISMetric',\n                outfile_prefix=f'{self.tmp_dir.name}/test',\n            ))\n\n        vis_metric.dataset_meta = dict(classes=['car', 'train'])\n        data_batch = dict(inputs=None, data_samples=None)\n        gt_insatnce = InstanceData(**dummy_pred_1)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_track_instances = gt_insatnce\n        img_data_sample.set_metainfo(\n            dict(\n                img_id=1,\n                video_id=1,\n                ori_video_length=2,\n                ori_shape=(720, 1280),\n                instances=instances_1))\n        gt_insatnce_2 = InstanceData(**dummy_pred_2)\n        img_data_sample_2 = DetDataSample()\n        img_data_sample_2.pred_track_instances = gt_insatnce_2\n        img_data_sample_2.set_metainfo(\n            dict(\n                img_id=2,\n                video_id=1,\n                ori_video_length=2,\n                ori_shape=(720, 1280),\n                instances=instances_1))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [\n            img_data_sample, img_data_sample_2\n        ]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        vis_metric.process(data_batch, predictions)\n\n        gt_insatnce = InstanceData(**dummy_pred_3)\n        img_data_sample = DetDataSample()\n        img_data_sample.pred_track_instances = gt_insatnce\n        img_data_sample.set_metainfo(\n            dict(\n                img_id=3,\n                video_id=2,\n                ori_video_length=1,\n                ori_shape=(720, 1280),\n                instances=instances_2))\n        track_data_sample = TrackDataSample()\n        track_data_sample.video_data_samples = [img_data_sample]\n        predictions = []\n        if isinstance(track_data_sample, BaseDataElement):\n            predictions.append(track_data_sample.to_dict())\n        vis_metric.process(data_batch, predictions)\n\n        eval_results = vis_metric.evaluate(size=3)\n        target = {\n            'youtube_vis/segm_mAP': 1.0,\n            'youtube_vis/segm_mAP_50': 1.0,\n            'youtube_vis/segm_mAP_75': 1.0,\n            'youtube_vis/segm_mAP_s': 1.0,\n            'youtube_vis/segm_mAP_m': -1.0,\n            'youtube_vis/segm_mAP_l': -1.0,\n        }\n        self.assertDictEqual(eval_results, target)\n"
  },
  {
    "path": "tests/test_models/test_backbones/__init__.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom .utils import check_norm_state, is_block, is_norm\n\n__all__ = ['is_block', 'is_norm', 'check_norm_state']\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_csp_darknet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.models.backbones.csp_darknet import CSPDarknet\nfrom .utils import check_norm_state, is_norm\n\n\ndef test_csp_darknet_backbone():\n    with pytest.raises(ValueError):\n        # frozen_stages must in range(-1, len(arch_setting) + 1)\n        CSPDarknet(frozen_stages=6)\n\n    with pytest.raises(AssertionError):\n        # out_indices in range(len(arch_setting) + 1)\n        CSPDarknet(out_indices=[6])\n\n    # Test CSPDarknet with first stage frozen\n    frozen_stages = 1\n    model = CSPDarknet(frozen_stages=frozen_stages)\n    model.train()\n\n    for mod in model.stem.modules():\n        for param in mod.parameters():\n            assert param.requires_grad is False\n    for i in range(1, frozen_stages + 1):\n        layer = getattr(model, f'stage{i}')\n        for mod in layer.modules():\n            if isinstance(mod, _BatchNorm):\n                assert mod.training is False\n        for param in layer.parameters():\n            assert param.requires_grad is False\n\n    # Test CSPDarknet with norm_eval=True\n    model = CSPDarknet(norm_eval=True)\n    model.train()\n\n    assert check_norm_state(model.modules(), False)\n\n    # Test CSPDarknet-P5 forward with widen_factor=0.5\n    model = CSPDarknet(arch='P5', widen_factor=0.25, out_indices=range(0, 5))\n    model.train()\n\n    imgs = torch.randn(1, 3, 64, 64)\n    feat = model(imgs)\n    assert len(feat) == 5\n    assert feat[0].shape == torch.Size((1, 16, 32, 32))\n    assert feat[1].shape == torch.Size((1, 32, 16, 16))\n    assert feat[2].shape == torch.Size((1, 64, 8, 8))\n    assert feat[3].shape == torch.Size((1, 128, 4, 4))\n    assert feat[4].shape == torch.Size((1, 256, 2, 2))\n\n    # Test CSPDarknet-P6 forward with widen_factor=0.5\n    model = CSPDarknet(\n        arch='P6',\n        widen_factor=0.25,\n        out_indices=range(0, 6),\n        spp_kernal_sizes=(3, 5, 7))\n    model.train()\n\n    imgs = torch.randn(1, 3, 128, 128)\n    feat = model(imgs)\n    assert feat[0].shape == torch.Size((1, 16, 64, 64))\n    assert feat[1].shape == torch.Size((1, 32, 32, 32))\n    assert feat[2].shape == torch.Size((1, 64, 16, 16))\n    assert feat[3].shape == torch.Size((1, 128, 8, 8))\n    assert feat[4].shape == torch.Size((1, 192, 4, 4))\n    assert feat[5].shape == torch.Size((1, 256, 2, 2))\n\n    # Test CSPDarknet forward with dict(type='ReLU')\n    model = CSPDarknet(\n        widen_factor=0.125, act_cfg=dict(type='ReLU'), out_indices=range(0, 5))\n    model.train()\n\n    imgs = torch.randn(1, 3, 64, 64)\n    feat = model(imgs)\n    assert len(feat) == 5\n    assert feat[0].shape == torch.Size((1, 8, 32, 32))\n    assert feat[1].shape == torch.Size((1, 16, 16, 16))\n    assert feat[2].shape == torch.Size((1, 32, 8, 8))\n    assert feat[3].shape == torch.Size((1, 64, 4, 4))\n    assert feat[4].shape == torch.Size((1, 128, 2, 2))\n\n    # Test CSPDarknet with BatchNorm forward\n    model = CSPDarknet(widen_factor=0.125, out_indices=range(0, 5))\n    for m in model.modules():\n        if is_norm(m):\n            assert isinstance(m, _BatchNorm)\n    model.train()\n\n    imgs = torch.randn(1, 3, 64, 64)\n    feat = model(imgs)\n    assert len(feat) == 5\n    assert feat[0].shape == torch.Size((1, 8, 32, 32))\n    assert feat[1].shape == torch.Size((1, 16, 16, 16))\n    assert feat[2].shape == torch.Size((1, 32, 8, 8))\n    assert feat[3].shape == torch.Size((1, 64, 4, 4))\n    assert feat[4].shape == torch.Size((1, 128, 2, 2))\n\n    # Test CSPDarknet with custom arch forward\n    arch_ovewrite = [[32, 56, 3, True, False], [56, 224, 2, True, False],\n                     [224, 512, 1, True, False]]\n    model = CSPDarknet(\n        arch_ovewrite=arch_ovewrite,\n        widen_factor=0.25,\n        out_indices=(0, 1, 2, 3))\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size((1, 8, 16, 16))\n    assert feat[1].shape == torch.Size((1, 14, 8, 8))\n    assert feat[2].shape == torch.Size((1, 56, 4, 4))\n    assert feat[3].shape == torch.Size((1, 128, 2, 2))\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_detectors_resnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\n\nfrom mmdet.models.backbones import DetectoRS_ResNet\n\n\ndef test_detectorrs_resnet_backbone():\n    detectorrs_cfg = dict(\n        depth=50,\n        num_stages=4,\n        out_indices=(0, 1, 2, 3),\n        frozen_stages=1,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        norm_eval=True,\n        style='pytorch',\n        conv_cfg=dict(type='ConvAWS'),\n        sac=dict(type='SAC', use_deform=True),\n        stage_with_sac=(False, True, True, True),\n        output_img=True)\n    \"\"\"Test init_weights config\"\"\"\n    with pytest.raises(AssertionError):\n        # pretrained and init_cfg cannot be specified at the same time\n        DetectoRS_ResNet(\n            **detectorrs_cfg, pretrained='Pretrained', init_cfg='Pretrained')\n\n    with pytest.raises(AssertionError):\n        # init_cfg must be a dict\n        DetectoRS_ResNet(\n            **detectorrs_cfg, pretrained=None, init_cfg=['Pretrained'])\n\n    with pytest.raises(KeyError):\n        # init_cfg must contain the key `type`\n        DetectoRS_ResNet(\n            **detectorrs_cfg,\n            pretrained=None,\n            init_cfg=dict(checkpoint='Pretrained'))\n\n    with pytest.raises(AssertionError):\n        # init_cfg only support initialize pretrained model way\n        DetectoRS_ResNet(\n            **detectorrs_cfg, pretrained=None, init_cfg=dict(type='Trained'))\n\n    with pytest.raises(TypeError):\n        # pretrained mast be a str or None\n        model = DetectoRS_ResNet(\n            **detectorrs_cfg, pretrained=['Pretrained'], init_cfg=None)\n        model.init_weights()\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_efficientnet.py",
    "content": "import pytest\nimport torch\n\nfrom mmdet.models.backbones import EfficientNet\n\n\ndef test_efficientnet_backbone():\n    \"\"\"Test EfficientNet backbone.\"\"\"\n    with pytest.raises(AssertionError):\n        # EfficientNet arch should be a key in EfficientNet.arch_settings\n        EfficientNet(arch='c3')\n\n    model = EfficientNet(arch='b0', out_indices=(0, 1, 2, 3, 4, 5, 6))\n    model.train()\n\n    imgs = torch.randn(2, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 7\n    assert feat[0].shape == torch.Size([2, 32, 16, 16])\n    assert feat[1].shape == torch.Size([2, 16, 16, 16])\n    assert feat[2].shape == torch.Size([2, 24, 8, 8])\n    assert feat[3].shape == torch.Size([2, 40, 4, 4])\n    assert feat[4].shape == torch.Size([2, 112, 2, 2])\n    assert feat[5].shape == torch.Size([2, 320, 1, 1])\n    assert feat[6].shape == torch.Size([2, 1280, 1, 1])\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_hourglass.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.backbones.hourglass import HourglassNet\n\n\ndef test_hourglass_backbone():\n    with pytest.raises(AssertionError):\n        # HourglassNet's num_stacks should larger than 0\n        HourglassNet(num_stacks=0)\n\n    with pytest.raises(AssertionError):\n        # len(stage_channels) should equal len(stage_blocks)\n        HourglassNet(\n            stage_channels=[256, 256, 384, 384, 384],\n            stage_blocks=[2, 2, 2, 2, 2, 4])\n\n    with pytest.raises(AssertionError):\n        # len(stage_channels) should lagrer than downsample_times\n        HourglassNet(\n            downsample_times=5,\n            stage_channels=[256, 256, 384, 384, 384],\n            stage_blocks=[2, 2, 2, 2, 2])\n\n    # Test HourglassNet-52\n    model = HourglassNet(\n        num_stacks=1,\n        stage_channels=(64, 64, 96, 96, 96, 128),\n        feat_channel=64)\n    model.train()\n\n    imgs = torch.randn(1, 3, 256, 256)\n    feat = model(imgs)\n    assert len(feat) == 1\n    assert feat[0].shape == torch.Size([1, 64, 64, 64])\n\n    # Test HourglassNet-104\n    model = HourglassNet(\n        num_stacks=2,\n        stage_channels=(64, 64, 96, 96, 96, 128),\n        feat_channel=64)\n    model.train()\n\n    imgs = torch.randn(1, 3, 256, 256)\n    feat = model(imgs)\n    assert len(feat) == 2\n    assert feat[0].shape == torch.Size([1, 64, 64, 64])\n    assert feat[1].shape == torch.Size([1, 64, 64, 64])\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_hrnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.backbones.hrnet import HRModule, HRNet\nfrom mmdet.models.backbones.resnet import BasicBlock, Bottleneck\n\n\n@pytest.mark.parametrize('block', [BasicBlock, Bottleneck])\ndef test_hrmodule(block):\n    # Test multiscale forward\n    num_channles = (32, 64)\n    in_channels = [c * block.expansion for c in num_channles]\n    hrmodule = HRModule(\n        num_branches=2,\n        blocks=block,\n        in_channels=in_channels,\n        num_blocks=(4, 4),\n        num_channels=num_channles,\n    )\n\n    feats = [\n        torch.randn(1, in_channels[0], 64, 64),\n        torch.randn(1, in_channels[1], 32, 32)\n    ]\n    feats = hrmodule(feats)\n\n    assert len(feats) == 2\n    assert feats[0].shape == torch.Size([1, in_channels[0], 64, 64])\n    assert feats[1].shape == torch.Size([1, in_channels[1], 32, 32])\n\n    # Test single scale forward\n    num_channles = (32, 64)\n    in_channels = [c * block.expansion for c in num_channles]\n    hrmodule = HRModule(\n        num_branches=2,\n        blocks=block,\n        in_channels=in_channels,\n        num_blocks=(4, 4),\n        num_channels=num_channles,\n        multiscale_output=False,\n    )\n\n    feats = [\n        torch.randn(1, in_channels[0], 64, 64),\n        torch.randn(1, in_channels[1], 32, 32)\n    ]\n    feats = hrmodule(feats)\n\n    assert len(feats) == 1\n    assert feats[0].shape == torch.Size([1, in_channels[0], 64, 64])\n\n\ndef test_hrnet_backbone():\n    # only have 3 stages\n    extra = dict(\n        stage1=dict(\n            num_modules=1,\n            num_branches=1,\n            block='BOTTLENECK',\n            num_blocks=(4, ),\n            num_channels=(64, )),\n        stage2=dict(\n            num_modules=1,\n            num_branches=2,\n            block='BASIC',\n            num_blocks=(4, 4),\n            num_channels=(32, 64)),\n        stage3=dict(\n            num_modules=4,\n            num_branches=3,\n            block='BASIC',\n            num_blocks=(4, 4, 4),\n            num_channels=(32, 64, 128)))\n\n    with pytest.raises(AssertionError):\n        # HRNet now only support 4 stages\n        HRNet(extra=extra)\n    extra['stage4'] = dict(\n        num_modules=3,\n        num_branches=3,  # should be 4\n        block='BASIC',\n        num_blocks=(4, 4, 4, 4),\n        num_channels=(32, 64, 128, 256))\n\n    with pytest.raises(AssertionError):\n        # len(num_blocks) should equal num_branches\n        HRNet(extra=extra)\n\n    extra['stage4']['num_branches'] = 4\n\n    # Test hrnetv2p_w32\n    model = HRNet(extra=extra)\n    model.init_weights()\n    model.train()\n\n    imgs = torch.randn(1, 3, 256, 256)\n    feats = model(imgs)\n    assert len(feats) == 4\n    assert feats[0].shape == torch.Size([1, 32, 64, 64])\n    assert feats[3].shape == torch.Size([1, 256, 8, 8])\n\n    # Test single scale output\n    model = HRNet(extra=extra, multiscale_output=False)\n    model.init_weights()\n    model.train()\n\n    imgs = torch.randn(1, 3, 256, 256)\n    feats = model(imgs)\n    assert len(feats) == 1\n    assert feats[0].shape == torch.Size([1, 32, 64, 64])\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_mobilenet_v2.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\nfrom torch.nn.modules import GroupNorm\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.models.backbones.mobilenet_v2 import MobileNetV2\nfrom .utils import check_norm_state, is_block, is_norm\n\n\ndef test_mobilenetv2_backbone():\n    with pytest.raises(ValueError):\n        # frozen_stages must in range(-1, 8)\n        MobileNetV2(frozen_stages=8)\n\n    with pytest.raises(ValueError):\n        # out_indices in range(-1, 8)\n        MobileNetV2(out_indices=[8])\n\n    # Test MobileNetV2 with first stage frozen\n    frozen_stages = 1\n    model = MobileNetV2(frozen_stages=frozen_stages)\n    model.train()\n\n    for mod in model.conv1.modules():\n        for param in mod.parameters():\n            assert param.requires_grad is False\n    for i in range(1, frozen_stages + 1):\n        layer = getattr(model, f'layer{i}')\n        for mod in layer.modules():\n            if isinstance(mod, _BatchNorm):\n                assert mod.training is False\n        for param in layer.parameters():\n            assert param.requires_grad is False\n\n    # Test MobileNetV2 with norm_eval=True\n    model = MobileNetV2(norm_eval=True)\n    model.train()\n\n    assert check_norm_state(model.modules(), False)\n\n    # Test MobileNetV2 forward with widen_factor=1.0\n    model = MobileNetV2(widen_factor=1.0, out_indices=range(0, 8))\n    model.train()\n\n    assert check_norm_state(model.modules(), True)\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert len(feat) == 8\n    assert feat[0].shape == torch.Size((1, 16, 112, 112))\n    assert feat[1].shape == torch.Size((1, 24, 56, 56))\n    assert feat[2].shape == torch.Size((1, 32, 28, 28))\n    assert feat[3].shape == torch.Size((1, 64, 14, 14))\n    assert feat[4].shape == torch.Size((1, 96, 14, 14))\n    assert feat[5].shape == torch.Size((1, 160, 7, 7))\n    assert feat[6].shape == torch.Size((1, 320, 7, 7))\n    assert feat[7].shape == torch.Size((1, 1280, 7, 7))\n\n    # Test MobileNetV2 forward with widen_factor=0.5\n    model = MobileNetV2(widen_factor=0.5, out_indices=range(0, 7))\n    model.train()\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert len(feat) == 7\n    assert feat[0].shape == torch.Size((1, 8, 112, 112))\n    assert feat[1].shape == torch.Size((1, 16, 56, 56))\n    assert feat[2].shape == torch.Size((1, 16, 28, 28))\n    assert feat[3].shape == torch.Size((1, 32, 14, 14))\n    assert feat[4].shape == torch.Size((1, 48, 14, 14))\n    assert feat[5].shape == torch.Size((1, 80, 7, 7))\n    assert feat[6].shape == torch.Size((1, 160, 7, 7))\n\n    # Test MobileNetV2 forward with widen_factor=2.0\n    model = MobileNetV2(widen_factor=2.0, out_indices=range(0, 8))\n    model.train()\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert feat[0].shape == torch.Size((1, 32, 112, 112))\n    assert feat[1].shape == torch.Size((1, 48, 56, 56))\n    assert feat[2].shape == torch.Size((1, 64, 28, 28))\n    assert feat[3].shape == torch.Size((1, 128, 14, 14))\n    assert feat[4].shape == torch.Size((1, 192, 14, 14))\n    assert feat[5].shape == torch.Size((1, 320, 7, 7))\n    assert feat[6].shape == torch.Size((1, 640, 7, 7))\n    assert feat[7].shape == torch.Size((1, 2560, 7, 7))\n\n    # Test MobileNetV2 forward with dict(type='ReLU')\n    model = MobileNetV2(\n        widen_factor=1.0, act_cfg=dict(type='ReLU'), out_indices=range(0, 7))\n    model.train()\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert len(feat) == 7\n    assert feat[0].shape == torch.Size((1, 16, 112, 112))\n    assert feat[1].shape == torch.Size((1, 24, 56, 56))\n    assert feat[2].shape == torch.Size((1, 32, 28, 28))\n    assert feat[3].shape == torch.Size((1, 64, 14, 14))\n    assert feat[4].shape == torch.Size((1, 96, 14, 14))\n    assert feat[5].shape == torch.Size((1, 160, 7, 7))\n    assert feat[6].shape == torch.Size((1, 320, 7, 7))\n\n    # Test MobileNetV2 with BatchNorm forward\n    model = MobileNetV2(widen_factor=1.0, out_indices=range(0, 7))\n    for m in model.modules():\n        if is_norm(m):\n            assert isinstance(m, _BatchNorm)\n    model.train()\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert len(feat) == 7\n    assert feat[0].shape == torch.Size((1, 16, 112, 112))\n    assert feat[1].shape == torch.Size((1, 24, 56, 56))\n    assert feat[2].shape == torch.Size((1, 32, 28, 28))\n    assert feat[3].shape == torch.Size((1, 64, 14, 14))\n    assert feat[4].shape == torch.Size((1, 96, 14, 14))\n    assert feat[5].shape == torch.Size((1, 160, 7, 7))\n    assert feat[6].shape == torch.Size((1, 320, 7, 7))\n\n    # Test MobileNetV2 with GroupNorm forward\n    model = MobileNetV2(\n        widen_factor=1.0,\n        norm_cfg=dict(type='GN', num_groups=2, requires_grad=True),\n        out_indices=range(0, 7))\n    for m in model.modules():\n        if is_norm(m):\n            assert isinstance(m, GroupNorm)\n    model.train()\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert len(feat) == 7\n    assert feat[0].shape == torch.Size((1, 16, 112, 112))\n    assert feat[1].shape == torch.Size((1, 24, 56, 56))\n    assert feat[2].shape == torch.Size((1, 32, 28, 28))\n    assert feat[3].shape == torch.Size((1, 64, 14, 14))\n    assert feat[4].shape == torch.Size((1, 96, 14, 14))\n    assert feat[5].shape == torch.Size((1, 160, 7, 7))\n    assert feat[6].shape == torch.Size((1, 320, 7, 7))\n\n    # Test MobileNetV2 with layers 1, 3, 5 out forward\n    model = MobileNetV2(widen_factor=1.0, out_indices=(0, 2, 4))\n    model.train()\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert len(feat) == 3\n    assert feat[0].shape == torch.Size((1, 16, 112, 112))\n    assert feat[1].shape == torch.Size((1, 32, 28, 28))\n    assert feat[2].shape == torch.Size((1, 96, 14, 14))\n\n    # Test MobileNetV2 with checkpoint forward\n    model = MobileNetV2(\n        widen_factor=1.0, with_cp=True, out_indices=range(0, 7))\n    for m in model.modules():\n        if is_block(m):\n            assert m.with_cp\n    model.train()\n\n    imgs = torch.randn(1, 3, 224, 224)\n    feat = model(imgs)\n    assert len(feat) == 7\n    assert feat[0].shape == torch.Size((1, 16, 112, 112))\n    assert feat[1].shape == torch.Size((1, 24, 56, 56))\n    assert feat[2].shape == torch.Size((1, 32, 28, 28))\n    assert feat[3].shape == torch.Size((1, 64, 14, 14))\n    assert feat[4].shape == torch.Size((1, 96, 14, 14))\n    assert feat[5].shape == torch.Size((1, 160, 7, 7))\n    assert feat[6].shape == torch.Size((1, 320, 7, 7))\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_pvt.py",
    "content": "import pytest\nimport torch\n\nfrom mmdet.models.backbones.pvt import (PVTEncoderLayer,\n                                        PyramidVisionTransformer,\n                                        PyramidVisionTransformerV2)\n\n\ndef test_pvt_block():\n    # test PVT structure and forward\n    block = PVTEncoderLayer(\n        embed_dims=64, num_heads=4, feedforward_channels=256)\n    assert block.ffn.embed_dims == 64\n    assert block.attn.num_heads == 4\n    assert block.ffn.feedforward_channels == 256\n    x = torch.randn(1, 56 * 56, 64)\n    x_out = block(x, (56, 56))\n    assert x_out.shape == torch.Size([1, 56 * 56, 64])\n\n\ndef test_pvt():\n    \"\"\"Test PVT backbone.\"\"\"\n\n    with pytest.raises(TypeError):\n        # Pretrained arg must be str or None.\n        PyramidVisionTransformer(pretrained=123)\n\n    # test pretrained image size\n    with pytest.raises(AssertionError):\n        PyramidVisionTransformer(pretrain_img_size=(224, 224, 224))\n\n    # Test absolute position embedding\n    temp = torch.randn((1, 3, 224, 224))\n    model = PyramidVisionTransformer(\n        pretrain_img_size=224, use_abs_pos_embed=True)\n    model.init_weights()\n    model(temp)\n\n    # Test normal inference\n    temp = torch.randn((1, 3, 32, 32))\n    model = PyramidVisionTransformer()\n    outs = model(temp)\n    assert outs[0].shape == (1, 64, 8, 8)\n    assert outs[1].shape == (1, 128, 4, 4)\n    assert outs[2].shape == (1, 320, 2, 2)\n    assert outs[3].shape == (1, 512, 1, 1)\n\n    # Test abnormal inference size\n    temp = torch.randn((1, 3, 33, 33))\n    model = PyramidVisionTransformer()\n    outs = model(temp)\n    assert outs[0].shape == (1, 64, 8, 8)\n    assert outs[1].shape == (1, 128, 4, 4)\n    assert outs[2].shape == (1, 320, 2, 2)\n    assert outs[3].shape == (1, 512, 1, 1)\n\n    # Test abnormal inference size\n    temp = torch.randn((1, 3, 112, 137))\n    model = PyramidVisionTransformer()\n    outs = model(temp)\n    assert outs[0].shape == (1, 64, 28, 34)\n    assert outs[1].shape == (1, 128, 14, 17)\n    assert outs[2].shape == (1, 320, 7, 8)\n    assert outs[3].shape == (1, 512, 3, 4)\n\n\ndef test_pvtv2():\n    \"\"\"Test PVTv2 backbone.\"\"\"\n\n    with pytest.raises(TypeError):\n        # Pretrained arg must be str or None.\n        PyramidVisionTransformerV2(pretrained=123)\n\n    # test pretrained image size\n    with pytest.raises(AssertionError):\n        PyramidVisionTransformerV2(pretrain_img_size=(224, 224, 224))\n\n    # Test normal inference\n    temp = torch.randn((1, 3, 32, 32))\n    model = PyramidVisionTransformerV2()\n    outs = model(temp)\n    assert outs[0].shape == (1, 64, 8, 8)\n    assert outs[1].shape == (1, 128, 4, 4)\n    assert outs[2].shape == (1, 320, 2, 2)\n    assert outs[3].shape == (1, 512, 1, 1)\n\n    # Test abnormal inference size\n    temp = torch.randn((1, 3, 31, 31))\n    model = PyramidVisionTransformerV2()\n    outs = model(temp)\n    assert outs[0].shape == (1, 64, 8, 8)\n    assert outs[1].shape == (1, 128, 4, 4)\n    assert outs[2].shape == (1, 320, 2, 2)\n    assert outs[3].shape == (1, 512, 1, 1)\n\n    # Test abnormal inference size\n    temp = torch.randn((1, 3, 112, 137))\n    model = PyramidVisionTransformerV2()\n    outs = model(temp)\n    assert outs[0].shape == (1, 64, 28, 35)\n    assert outs[1].shape == (1, 128, 14, 18)\n    assert outs[2].shape == (1, 320, 7, 9)\n    assert outs[3].shape == (1, 512, 4, 5)\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_regnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.backbones import RegNet\n\nregnet_test_data = [\n    ('regnetx_400mf',\n     dict(w0=24, wa=24.48, wm=2.54, group_w=16, depth=22,\n          bot_mul=1.0), [32, 64, 160, 384]),\n    ('regnetx_800mf',\n     dict(w0=56, wa=35.73, wm=2.28, group_w=16, depth=16,\n          bot_mul=1.0), [64, 128, 288, 672]),\n    ('regnetx_1.6gf',\n     dict(w0=80, wa=34.01, wm=2.25, group_w=24, depth=18,\n          bot_mul=1.0), [72, 168, 408, 912]),\n    ('regnetx_3.2gf',\n     dict(w0=88, wa=26.31, wm=2.25, group_w=48, depth=25,\n          bot_mul=1.0), [96, 192, 432, 1008]),\n    ('regnetx_4.0gf',\n     dict(w0=96, wa=38.65, wm=2.43, group_w=40, depth=23,\n          bot_mul=1.0), [80, 240, 560, 1360]),\n    ('regnetx_6.4gf',\n     dict(w0=184, wa=60.83, wm=2.07, group_w=56, depth=17,\n          bot_mul=1.0), [168, 392, 784, 1624]),\n    ('regnetx_8.0gf',\n     dict(w0=80, wa=49.56, wm=2.88, group_w=120, depth=23,\n          bot_mul=1.0), [80, 240, 720, 1920]),\n    ('regnetx_12gf',\n     dict(w0=168, wa=73.36, wm=2.37, group_w=112, depth=19,\n          bot_mul=1.0), [224, 448, 896, 2240]),\n]\n\n\n@pytest.mark.parametrize('arch_name,arch,out_channels', regnet_test_data)\ndef test_regnet_backbone(arch_name, arch, out_channels):\n    with pytest.raises(AssertionError):\n        # ResNeXt depth should be in [50, 101, 152]\n        RegNet(arch_name + '233')\n\n    # Test RegNet with arch_name\n    model = RegNet(arch_name)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, out_channels[0], 8, 8])\n    assert feat[1].shape == torch.Size([1, out_channels[1], 4, 4])\n    assert feat[2].shape == torch.Size([1, out_channels[2], 2, 2])\n    assert feat[3].shape == torch.Size([1, out_channels[3], 1, 1])\n\n    # Test RegNet with arch\n    model = RegNet(arch)\n    assert feat[0].shape == torch.Size([1, out_channels[0], 8, 8])\n    assert feat[1].shape == torch.Size([1, out_channels[1], 4, 4])\n    assert feat[2].shape == torch.Size([1, out_channels[2], 2, 2])\n    assert feat[3].shape == torch.Size([1, out_channels[3], 1, 1])\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_renext.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.backbones import ResNeXt\nfrom mmdet.models.backbones.resnext import Bottleneck as BottleneckX\nfrom .utils import is_block\n\n\ndef test_renext_bottleneck():\n    with pytest.raises(AssertionError):\n        # Style must be in ['pytorch', 'caffe']\n        BottleneckX(64, 64, groups=32, base_width=4, style='tensorflow')\n\n    # Test ResNeXt Bottleneck structure\n    block = BottleneckX(\n        64, 64, groups=32, base_width=4, stride=2, style='pytorch')\n    assert block.conv2.stride == (2, 2)\n    assert block.conv2.groups == 32\n    assert block.conv2.out_channels == 128\n\n    # Test ResNeXt Bottleneck with DCN\n    dcn = dict(type='DCN', deform_groups=1, fallback_on_stride=False)\n    with pytest.raises(AssertionError):\n        # conv_cfg must be None if dcn is not None\n        BottleneckX(\n            64,\n            64,\n            groups=32,\n            base_width=4,\n            dcn=dcn,\n            conv_cfg=dict(type='Conv'))\n    BottleneckX(64, 64, dcn=dcn)\n\n    # Test ResNeXt Bottleneck forward\n    block = BottleneckX(64, 16, groups=32, base_width=4)\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test ResNeXt Bottleneck forward with plugins\n    plugins = [\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='0010',\n                kv_stride=2),\n            stages=(False, False, True, True),\n            position='after_conv2')\n    ]\n    block = BottleneckX(64, 16, groups=32, base_width=4, plugins=plugins)\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n\ndef test_resnext_backbone():\n    with pytest.raises(KeyError):\n        # ResNeXt depth should be in [50, 101, 152]\n        ResNeXt(depth=18)\n\n    # Test ResNeXt with group 32, base_width 4\n    model = ResNeXt(depth=50, groups=32, base_width=4)\n    for m in model.modules():\n        if is_block(m):\n            assert m.conv2.groups == 32\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 256, 8, 8])\n    assert feat[1].shape == torch.Size([1, 512, 4, 4])\n    assert feat[2].shape == torch.Size([1, 1024, 2, 2])\n    assert feat[3].shape == torch.Size([1, 2048, 1, 1])\n\n\nregnet_test_data = [\n    ('regnetx_400mf',\n     dict(w0=24, wa=24.48, wm=2.54, group_w=16, depth=22,\n          bot_mul=1.0), [32, 64, 160, 384]),\n    ('regnetx_800mf',\n     dict(w0=56, wa=35.73, wm=2.28, group_w=16, depth=16,\n          bot_mul=1.0), [64, 128, 288, 672]),\n    ('regnetx_1.6gf',\n     dict(w0=80, wa=34.01, wm=2.25, group_w=24, depth=18,\n          bot_mul=1.0), [72, 168, 408, 912]),\n    ('regnetx_3.2gf',\n     dict(w0=88, wa=26.31, wm=2.25, group_w=48, depth=25,\n          bot_mul=1.0), [96, 192, 432, 1008]),\n    ('regnetx_4.0gf',\n     dict(w0=96, wa=38.65, wm=2.43, group_w=40, depth=23,\n          bot_mul=1.0), [80, 240, 560, 1360]),\n    ('regnetx_6.4gf',\n     dict(w0=184, wa=60.83, wm=2.07, group_w=56, depth=17,\n          bot_mul=1.0), [168, 392, 784, 1624]),\n    ('regnetx_8.0gf',\n     dict(w0=80, wa=49.56, wm=2.88, group_w=120, depth=23,\n          bot_mul=1.0), [80, 240, 720, 1920]),\n    ('regnetx_12gf',\n     dict(w0=168, wa=73.36, wm=2.37, group_w=112, depth=19,\n          bot_mul=1.0), [224, 448, 896, 2240]),\n]\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_res2net.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.backbones import Res2Net\nfrom mmdet.models.backbones.res2net import Bottle2neck\nfrom .utils import is_block\n\n\ndef test_res2net_bottle2neck():\n    with pytest.raises(AssertionError):\n        # Style must be in ['pytorch', 'caffe']\n        Bottle2neck(64, 64, base_width=26, scales=4, style='tensorflow')\n\n    with pytest.raises(AssertionError):\n        # Scale must be larger than 1\n        Bottle2neck(64, 64, base_width=26, scales=1, style='pytorch')\n\n    # Test Res2Net Bottle2neck structure\n    block = Bottle2neck(\n        64, 64, base_width=26, stride=2, scales=4, style='pytorch')\n    assert block.scales == 4\n\n    # Test Res2Net Bottle2neck with DCN\n    dcn = dict(type='DCN', deform_groups=1, fallback_on_stride=False)\n    with pytest.raises(AssertionError):\n        # conv_cfg must be None if dcn is not None\n        Bottle2neck(\n            64,\n            64,\n            base_width=26,\n            scales=4,\n            dcn=dcn,\n            conv_cfg=dict(type='Conv'))\n    Bottle2neck(64, 64, dcn=dcn)\n\n    # Test Res2Net Bottle2neck forward\n    block = Bottle2neck(64, 16, base_width=26, scales=4)\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n\ndef test_res2net_backbone():\n    with pytest.raises(KeyError):\n        # Res2Net depth should be in [50, 101, 152]\n        Res2Net(depth=18)\n\n    # Test Res2Net with scales 4, base_width 26\n    model = Res2Net(depth=50, scales=4, base_width=26)\n    for m in model.modules():\n        if is_block(m):\n            assert m.scales == 4\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 256, 8, 8])\n    assert feat[1].shape == torch.Size([1, 512, 4, 4])\n    assert feat[2].shape == torch.Size([1, 1024, 2, 2])\n    assert feat[3].shape == torch.Size([1, 2048, 1, 1])\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_resnest.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.backbones import ResNeSt\nfrom mmdet.models.backbones.resnest import Bottleneck as BottleneckS\n\n\ndef test_resnest_bottleneck():\n    with pytest.raises(AssertionError):\n        # Style must be in ['pytorch', 'caffe']\n        BottleneckS(64, 64, radix=2, reduction_factor=4, style='tensorflow')\n\n    # Test ResNeSt Bottleneck structure\n    block = BottleneckS(\n        2, 4, radix=2, reduction_factor=4, stride=2, style='pytorch')\n    assert block.avd_layer.stride == 2\n    assert block.conv2.channels == 4\n\n    # Test ResNeSt Bottleneck forward\n    block = BottleneckS(16, 4, radix=2, reduction_factor=4)\n    x = torch.randn(2, 16, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([2, 16, 56, 56])\n\n\ndef test_resnest_backbone():\n    with pytest.raises(KeyError):\n        # ResNeSt depth should be in [50, 101, 152, 200]\n        ResNeSt(depth=18)\n\n    # Test ResNeSt with radix 2, reduction_factor 4\n    model = ResNeSt(\n        depth=50,\n        base_channels=4,\n        radix=2,\n        reduction_factor=4,\n        out_indices=(0, 1, 2, 3))\n    model.train()\n\n    imgs = torch.randn(2, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([2, 16, 8, 8])\n    assert feat[1].shape == torch.Size([2, 32, 4, 4])\n    assert feat[2].shape == torch.Size([2, 64, 2, 2])\n    assert feat[3].shape == torch.Size([2, 128, 1, 1])\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_resnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\nfrom mmcv.ops import DeformConv2dPack\nfrom torch.nn.modules import AvgPool2d, GroupNorm\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.models.backbones import ResNet, ResNetV1d\nfrom mmdet.models.backbones.resnet import BasicBlock, Bottleneck\nfrom mmdet.models.layers import ResLayer, SimplifiedBasicBlock\nfrom .utils import check_norm_state, is_block, is_norm\n\n\ndef assert_params_all_zeros(module) -> bool:\n    \"\"\"Check if the parameters of the module is all zeros.\n\n    Args:\n        module (nn.Module): The module to be checked.\n\n    Returns:\n        bool: Whether the parameters of the module is all zeros.\n    \"\"\"\n    weight_data = module.weight.data\n    is_weight_zero = weight_data.allclose(\n        weight_data.new_zeros(weight_data.size()))\n\n    if hasattr(module, 'bias') and module.bias is not None:\n        bias_data = module.bias.data\n        is_bias_zero = bias_data.allclose(\n            bias_data.new_zeros(bias_data.size()))\n    else:\n        is_bias_zero = True\n\n    return is_weight_zero and is_bias_zero\n\n\ndef test_resnet_basic_block():\n    with pytest.raises(AssertionError):\n        # Not implemented yet.\n        dcn = dict(type='DCN', deform_groups=1, fallback_on_stride=False)\n        BasicBlock(64, 64, dcn=dcn)\n\n    with pytest.raises(AssertionError):\n        # Not implemented yet.\n        plugins = [\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv3')\n        ]\n        BasicBlock(64, 64, plugins=plugins)\n\n    with pytest.raises(AssertionError):\n        # Not implemented yet\n        plugins = [\n            dict(\n                cfg=dict(\n                    type='GeneralizedAttention',\n                    spatial_range=-1,\n                    num_heads=8,\n                    attention_type='0010',\n                    kv_stride=2),\n                position='after_conv2')\n        ]\n        BasicBlock(64, 64, plugins=plugins)\n\n    # test BasicBlock structure and forward\n    block = BasicBlock(64, 64)\n    assert block.conv1.in_channels == 64\n    assert block.conv1.out_channels == 64\n    assert block.conv1.kernel_size == (3, 3)\n    assert block.conv2.in_channels == 64\n    assert block.conv2.out_channels == 64\n    assert block.conv2.kernel_size == (3, 3)\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test BasicBlock with checkpoint forward\n    block = BasicBlock(64, 64, with_cp=True)\n    assert block.with_cp\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n\ndef test_resnet_bottleneck():\n    with pytest.raises(AssertionError):\n        # Style must be in ['pytorch', 'caffe']\n        Bottleneck(64, 64, style='tensorflow')\n\n    with pytest.raises(AssertionError):\n        # Allowed positions are 'after_conv1', 'after_conv2', 'after_conv3'\n        plugins = [\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv4')\n        ]\n        Bottleneck(64, 16, plugins=plugins)\n\n    with pytest.raises(AssertionError):\n        # Need to specify different postfix to avoid duplicate plugin name\n        plugins = [\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv3'),\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv3')\n        ]\n        Bottleneck(64, 16, plugins=plugins)\n\n    with pytest.raises(KeyError):\n        # Plugin type is not supported\n        plugins = [dict(cfg=dict(type='WrongPlugin'), position='after_conv3')]\n        Bottleneck(64, 16, plugins=plugins)\n\n    # Test Bottleneck with checkpoint forward\n    block = Bottleneck(64, 16, with_cp=True)\n    assert block.with_cp\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test Bottleneck style\n    block = Bottleneck(64, 64, stride=2, style='pytorch')\n    assert block.conv1.stride == (1, 1)\n    assert block.conv2.stride == (2, 2)\n    block = Bottleneck(64, 64, stride=2, style='caffe')\n    assert block.conv1.stride == (2, 2)\n    assert block.conv2.stride == (1, 1)\n\n    # Test Bottleneck DCN\n    dcn = dict(type='DCN', deform_groups=1, fallback_on_stride=False)\n    with pytest.raises(AssertionError):\n        Bottleneck(64, 64, dcn=dcn, conv_cfg=dict(type='Conv'))\n    block = Bottleneck(64, 64, dcn=dcn)\n    assert isinstance(block.conv2, DeformConv2dPack)\n\n    # Test Bottleneck forward\n    block = Bottleneck(64, 16)\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test Bottleneck with 1 ContextBlock after conv3\n    plugins = [\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16),\n            position='after_conv3')\n    ]\n    block = Bottleneck(64, 16, plugins=plugins)\n    assert block.context_block.in_channels == 64\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test Bottleneck with 1 GeneralizedAttention after conv2\n    plugins = [\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='0010',\n                kv_stride=2),\n            position='after_conv2')\n    ]\n    block = Bottleneck(64, 16, plugins=plugins)\n    assert block.gen_attention_block.in_channels == 16\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test Bottleneck with 1 GeneralizedAttention after conv2, 1 NonLocal2D\n    # after conv2, 1 ContextBlock after conv3\n    plugins = [\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='0010',\n                kv_stride=2),\n            position='after_conv2'),\n        dict(cfg=dict(type='NonLocal2d'), position='after_conv2'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16),\n            position='after_conv3')\n    ]\n    block = Bottleneck(64, 16, plugins=plugins)\n    assert block.gen_attention_block.in_channels == 16\n    assert block.nonlocal_block.in_channels == 16\n    assert block.context_block.in_channels == 64\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test Bottleneck with 1 ContextBlock after conv2, 2 ContextBlock after\n    # conv3\n    plugins = [\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=1),\n            position='after_conv2'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=2),\n            position='after_conv3'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=3),\n            position='after_conv3')\n    ]\n    block = Bottleneck(64, 16, plugins=plugins)\n    assert block.context_block1.in_channels == 16\n    assert block.context_block2.in_channels == 64\n    assert block.context_block3.in_channels == 64\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n\ndef test_simplied_basic_block():\n    with pytest.raises(AssertionError):\n        # Not implemented yet.\n        dcn = dict(type='DCN', deform_groups=1, fallback_on_stride=False)\n        SimplifiedBasicBlock(64, 64, dcn=dcn)\n\n    with pytest.raises(AssertionError):\n        # Not implemented yet.\n        plugins = [\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv3')\n        ]\n        SimplifiedBasicBlock(64, 64, plugins=plugins)\n\n    with pytest.raises(AssertionError):\n        # Not implemented yet\n        plugins = [\n            dict(\n                cfg=dict(\n                    type='GeneralizedAttention',\n                    spatial_range=-1,\n                    num_heads=8,\n                    attention_type='0010',\n                    kv_stride=2),\n                position='after_conv2')\n        ]\n        SimplifiedBasicBlock(64, 64, plugins=plugins)\n\n    with pytest.raises(AssertionError):\n        # Not implemented yet\n        SimplifiedBasicBlock(64, 64, with_cp=True)\n\n    # test SimplifiedBasicBlock structure and forward\n    block = SimplifiedBasicBlock(64, 64)\n    assert block.conv1.in_channels == 64\n    assert block.conv1.out_channels == 64\n    assert block.conv1.kernel_size == (3, 3)\n    assert block.conv2.in_channels == 64\n    assert block.conv2.out_channels == 64\n    assert block.conv2.kernel_size == (3, 3)\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # test SimplifiedBasicBlock without norm\n    block = SimplifiedBasicBlock(64, 64, norm_cfg=None)\n    assert block.norm1 is None\n    assert block.norm2 is None\n    x_out = block(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n\ndef test_resnet_res_layer():\n    # Test ResLayer of 3 Bottleneck w\\o downsample\n    layer = ResLayer(Bottleneck, 64, 16, 3)\n    assert len(layer) == 3\n    assert layer[0].conv1.in_channels == 64\n    assert layer[0].conv1.out_channels == 16\n    for i in range(1, len(layer)):\n        assert layer[i].conv1.in_channels == 64\n        assert layer[i].conv1.out_channels == 16\n    for i in range(len(layer)):\n        assert layer[i].downsample is None\n    x = torch.randn(1, 64, 56, 56)\n    x_out = layer(x)\n    assert x_out.shape == torch.Size([1, 64, 56, 56])\n\n    # Test ResLayer of 3 Bottleneck with downsample\n    layer = ResLayer(Bottleneck, 64, 64, 3)\n    assert layer[0].downsample[0].out_channels == 256\n    for i in range(1, len(layer)):\n        assert layer[i].downsample is None\n    x = torch.randn(1, 64, 56, 56)\n    x_out = layer(x)\n    assert x_out.shape == torch.Size([1, 256, 56, 56])\n\n    # Test ResLayer of 3 Bottleneck with stride=2\n    layer = ResLayer(Bottleneck, 64, 64, 3, stride=2)\n    assert layer[0].downsample[0].out_channels == 256\n    assert layer[0].downsample[0].stride == (2, 2)\n    for i in range(1, len(layer)):\n        assert layer[i].downsample is None\n    x = torch.randn(1, 64, 56, 56)\n    x_out = layer(x)\n    assert x_out.shape == torch.Size([1, 256, 28, 28])\n\n    # Test ResLayer of 3 Bottleneck with stride=2 and average downsample\n    layer = ResLayer(Bottleneck, 64, 64, 3, stride=2, avg_down=True)\n    assert isinstance(layer[0].downsample[0], AvgPool2d)\n    assert layer[0].downsample[1].out_channels == 256\n    assert layer[0].downsample[1].stride == (1, 1)\n    for i in range(1, len(layer)):\n        assert layer[i].downsample is None\n    x = torch.randn(1, 64, 56, 56)\n    x_out = layer(x)\n    assert x_out.shape == torch.Size([1, 256, 28, 28])\n\n    # Test ResLayer of 3 BasicBlock with stride=2 and downsample_first=False\n    layer = ResLayer(BasicBlock, 64, 64, 3, stride=2, downsample_first=False)\n    assert layer[2].downsample[0].out_channels == 64\n    assert layer[2].downsample[0].stride == (2, 2)\n    for i in range(len(layer) - 1):\n        assert layer[i].downsample is None\n    x = torch.randn(1, 64, 56, 56)\n    x_out = layer(x)\n    assert x_out.shape == torch.Size([1, 64, 28, 28])\n\n\ndef test_resnest_stem():\n    # Test default stem_channels\n    model = ResNet(50)\n    assert model.stem_channels == 64\n    assert model.conv1.out_channels == 64\n    assert model.norm1.num_features == 64\n\n    # Test default stem_channels, with base_channels=3\n    model = ResNet(50, base_channels=3)\n    assert model.stem_channels == 3\n    assert model.conv1.out_channels == 3\n    assert model.norm1.num_features == 3\n    assert model.layer1[0].conv1.in_channels == 3\n\n    # Test stem_channels=3\n    model = ResNet(50, stem_channels=3)\n    assert model.stem_channels == 3\n    assert model.conv1.out_channels == 3\n    assert model.norm1.num_features == 3\n    assert model.layer1[0].conv1.in_channels == 3\n\n    # Test stem_channels=3, with base_channels=2\n    model = ResNet(50, stem_channels=3, base_channels=2)\n    assert model.stem_channels == 3\n    assert model.conv1.out_channels == 3\n    assert model.norm1.num_features == 3\n    assert model.layer1[0].conv1.in_channels == 3\n\n    # Test V1d stem_channels\n    model = ResNetV1d(depth=50, stem_channels=6)\n    model.train()\n    assert model.stem[0].out_channels == 3\n    assert model.stem[1].num_features == 3\n    assert model.stem[3].out_channels == 3\n    assert model.stem[4].num_features == 3\n    assert model.stem[6].out_channels == 6\n    assert model.stem[7].num_features == 6\n    assert model.layer1[0].conv1.in_channels == 6\n\n\ndef test_resnet_backbone():\n    \"\"\"Test resnet backbone.\"\"\"\n    with pytest.raises(KeyError):\n        # ResNet depth should be in [18, 34, 50, 101, 152]\n        ResNet(20)\n\n    with pytest.raises(AssertionError):\n        # In ResNet: 1 <= num_stages <= 4\n        ResNet(50, num_stages=0)\n\n    with pytest.raises(AssertionError):\n        # len(stage_with_dcn) == num_stages\n        dcn = dict(type='DCN', deform_groups=1, fallback_on_stride=False)\n        ResNet(50, dcn=dcn, stage_with_dcn=(True, ))\n\n    with pytest.raises(AssertionError):\n        # len(stage_with_plugin) == num_stages\n        plugins = [\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                stages=(False, True, True),\n                position='after_conv3')\n        ]\n        ResNet(50, plugins=plugins)\n\n    with pytest.raises(AssertionError):\n        # In ResNet: 1 <= num_stages <= 4\n        ResNet(50, num_stages=5)\n\n    with pytest.raises(AssertionError):\n        # len(strides) == len(dilations) == num_stages\n        ResNet(50, strides=(1, ), dilations=(1, 1), num_stages=3)\n\n    with pytest.raises(TypeError):\n        # pretrained must be a string path\n        model = ResNet(50, pretrained=0)\n\n    with pytest.raises(AssertionError):\n        # Style must be in ['pytorch', 'caffe']\n        ResNet(50, style='tensorflow')\n\n    # Test ResNet50 norm_eval=True\n    model = ResNet(50, norm_eval=True, base_channels=1)\n    model.train()\n    assert check_norm_state(model.modules(), False)\n\n    # Test ResNet50 with torchvision pretrained weight\n    model = ResNet(\n        depth=50, norm_eval=True, pretrained='torchvision://resnet50')\n    model.train()\n    assert check_norm_state(model.modules(), False)\n\n    # Test ResNet50 with first stage frozen\n    frozen_stages = 1\n    model = ResNet(50, frozen_stages=frozen_stages, base_channels=1)\n    model.train()\n    assert model.norm1.training is False\n    for layer in [model.conv1, model.norm1]:\n        for param in layer.parameters():\n            assert param.requires_grad is False\n    for i in range(1, frozen_stages + 1):\n        layer = getattr(model, f'layer{i}')\n        for mod in layer.modules():\n            if isinstance(mod, _BatchNorm):\n                assert mod.training is False\n        for param in layer.parameters():\n            assert param.requires_grad is False\n\n    # Test ResNet50V1d with first stage frozen\n    model = ResNetV1d(depth=50, frozen_stages=frozen_stages, base_channels=2)\n    assert len(model.stem) == 9\n    model.train()\n    assert check_norm_state(model.stem, False)\n    for param in model.stem.parameters():\n        assert param.requires_grad is False\n    for i in range(1, frozen_stages + 1):\n        layer = getattr(model, f'layer{i}')\n        for mod in layer.modules():\n            if isinstance(mod, _BatchNorm):\n                assert mod.training is False\n        for param in layer.parameters():\n            assert param.requires_grad is False\n\n    # Test ResNet18 forward\n    model = ResNet(18)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 64, 8, 8])\n    assert feat[1].shape == torch.Size([1, 128, 4, 4])\n    assert feat[2].shape == torch.Size([1, 256, 2, 2])\n    assert feat[3].shape == torch.Size([1, 512, 1, 1])\n\n    # Test ResNet18 with checkpoint forward\n    model = ResNet(18, with_cp=True)\n    for m in model.modules():\n        if is_block(m):\n            assert m.with_cp\n\n    # Test ResNet50 with BatchNorm forward\n    model = ResNet(50, base_channels=1)\n    for m in model.modules():\n        if is_norm(m):\n            assert isinstance(m, _BatchNorm)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 4, 8, 8])\n    assert feat[1].shape == torch.Size([1, 8, 4, 4])\n    assert feat[2].shape == torch.Size([1, 16, 2, 2])\n    assert feat[3].shape == torch.Size([1, 32, 1, 1])\n\n    # Test ResNet50 with layers 1, 2, 3 out forward\n    model = ResNet(50, out_indices=(0, 1, 2), base_channels=1)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 3\n    assert feat[0].shape == torch.Size([1, 4, 8, 8])\n    assert feat[1].shape == torch.Size([1, 8, 4, 4])\n    assert feat[2].shape == torch.Size([1, 16, 2, 2])\n\n    # Test ResNet50 with checkpoint forward\n    model = ResNet(50, with_cp=True, base_channels=1)\n    for m in model.modules():\n        if is_block(m):\n            assert m.with_cp\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 4, 8, 8])\n    assert feat[1].shape == torch.Size([1, 8, 4, 4])\n    assert feat[2].shape == torch.Size([1, 16, 2, 2])\n    assert feat[3].shape == torch.Size([1, 32, 1, 1])\n\n    # Test ResNet50 with GroupNorm forward\n    model = ResNet(\n        50,\n        base_channels=4,\n        norm_cfg=dict(type='GN', num_groups=2, requires_grad=True))\n    for m in model.modules():\n        if is_norm(m):\n            assert isinstance(m, GroupNorm)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 16, 8, 8])\n    assert feat[1].shape == torch.Size([1, 32, 4, 4])\n    assert feat[2].shape == torch.Size([1, 64, 2, 2])\n    assert feat[3].shape == torch.Size([1, 128, 1, 1])\n\n    # Test ResNet50 with 1 GeneralizedAttention after conv2, 1 NonLocal2D\n    # after conv2, 1 ContextBlock after conv3 in layers 2, 3, 4\n    plugins = [\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='0010',\n                kv_stride=2),\n            stages=(False, True, True, True),\n            position='after_conv2'),\n        dict(cfg=dict(type='NonLocal2d'), position='after_conv2'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16),\n            stages=(False, True, True, False),\n            position='after_conv3')\n    ]\n    model = ResNet(50, plugins=plugins, base_channels=8)\n    for m in model.layer1.modules():\n        if is_block(m):\n            assert not hasattr(m, 'context_block')\n            assert not hasattr(m, 'gen_attention_block')\n            assert m.nonlocal_block.in_channels == 8\n    for m in model.layer2.modules():\n        if is_block(m):\n            assert m.nonlocal_block.in_channels == 16\n            assert m.gen_attention_block.in_channels == 16\n            assert m.context_block.in_channels == 64\n\n    for m in model.layer3.modules():\n        if is_block(m):\n            assert m.nonlocal_block.in_channels == 32\n            assert m.gen_attention_block.in_channels == 32\n            assert m.context_block.in_channels == 128\n\n    for m in model.layer4.modules():\n        if is_block(m):\n            assert m.nonlocal_block.in_channels == 64\n            assert m.gen_attention_block.in_channels == 64\n            assert not hasattr(m, 'context_block')\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 32, 8, 8])\n    assert feat[1].shape == torch.Size([1, 64, 4, 4])\n    assert feat[2].shape == torch.Size([1, 128, 2, 2])\n    assert feat[3].shape == torch.Size([1, 256, 1, 1])\n\n    # Test ResNet50 with 1 ContextBlock after conv2, 1 ContextBlock after\n    # conv3 in layers 2, 3, 4\n    plugins = [\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=1),\n            stages=(False, True, True, False),\n            position='after_conv3'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=2),\n            stages=(False, True, True, False),\n            position='after_conv3')\n    ]\n\n    model = ResNet(50, plugins=plugins, base_channels=8)\n    for m in model.layer1.modules():\n        if is_block(m):\n            assert not hasattr(m, 'context_block')\n            assert not hasattr(m, 'context_block1')\n            assert not hasattr(m, 'context_block2')\n    for m in model.layer2.modules():\n        if is_block(m):\n            assert not hasattr(m, 'context_block')\n            assert m.context_block1.in_channels == 64\n            assert m.context_block2.in_channels == 64\n\n    for m in model.layer3.modules():\n        if is_block(m):\n            assert not hasattr(m, 'context_block')\n            assert m.context_block1.in_channels == 128\n            assert m.context_block2.in_channels == 128\n\n    for m in model.layer4.modules():\n        if is_block(m):\n            assert not hasattr(m, 'context_block')\n            assert not hasattr(m, 'context_block1')\n            assert not hasattr(m, 'context_block2')\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 32, 8, 8])\n    assert feat[1].shape == torch.Size([1, 64, 4, 4])\n    assert feat[2].shape == torch.Size([1, 128, 2, 2])\n    assert feat[3].shape == torch.Size([1, 256, 1, 1])\n\n    # Test ResNet50 zero initialization of residual\n    model = ResNet(50, zero_init_residual=True, base_channels=1)\n    model.init_weights()\n    for m in model.modules():\n        if isinstance(m, Bottleneck):\n            assert assert_params_all_zeros(m.norm3)\n        elif isinstance(m, BasicBlock):\n            assert assert_params_all_zeros(m.norm2)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 4, 8, 8])\n    assert feat[1].shape == torch.Size([1, 8, 4, 4])\n    assert feat[2].shape == torch.Size([1, 16, 2, 2])\n    assert feat[3].shape == torch.Size([1, 32, 1, 1])\n\n    # Test ResNetV1d forward\n    model = ResNetV1d(depth=50, base_channels=2)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 4\n    assert feat[0].shape == torch.Size([1, 8, 8, 8])\n    assert feat[1].shape == torch.Size([1, 16, 4, 4])\n    assert feat[2].shape == torch.Size([1, 32, 2, 2])\n    assert feat[3].shape == torch.Size([1, 64, 1, 1])\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_swin.py",
    "content": "import pytest\nimport torch\n\nfrom mmdet.models.backbones.swin import SwinBlock, SwinTransformer\n\n\ndef test_swin_block():\n    # test SwinBlock structure and forward\n    block = SwinBlock(embed_dims=64, num_heads=4, feedforward_channels=256)\n    assert block.ffn.embed_dims == 64\n    assert block.attn.w_msa.num_heads == 4\n    assert block.ffn.feedforward_channels == 256\n    x = torch.randn(1, 56 * 56, 64)\n    x_out = block(x, (56, 56))\n    assert x_out.shape == torch.Size([1, 56 * 56, 64])\n\n    # Test BasicBlock with checkpoint forward\n    block = SwinBlock(\n        embed_dims=64, num_heads=4, feedforward_channels=256, with_cp=True)\n    assert block.with_cp\n    x = torch.randn(1, 56 * 56, 64)\n    x_out = block(x, (56, 56))\n    assert x_out.shape == torch.Size([1, 56 * 56, 64])\n\n\ndef test_swin_transformer():\n    \"\"\"Test Swin Transformer backbone.\"\"\"\n\n    with pytest.raises(TypeError):\n        # Pretrained arg must be str or None.\n        SwinTransformer(pretrained=123)\n\n    with pytest.raises(AssertionError):\n        # Because swin uses non-overlapping patch embed, so the stride of patch\n        # embed must be equal to patch size.\n        SwinTransformer(strides=(2, 2, 2, 2), patch_size=4)\n\n    # test pretrained image size\n    with pytest.raises(AssertionError):\n        SwinTransformer(pretrain_img_size=(224, 224, 224))\n\n    # Test absolute position embedding\n    temp = torch.randn((1, 3, 224, 224))\n    model = SwinTransformer(pretrain_img_size=224, use_abs_pos_embed=True)\n    model.init_weights()\n    model(temp)\n\n    # Test patch norm\n    model = SwinTransformer(patch_norm=False)\n    model(temp)\n\n    # Test normal inference\n    temp = torch.randn((1, 3, 32, 32))\n    model = SwinTransformer()\n    outs = model(temp)\n    assert outs[0].shape == (1, 96, 8, 8)\n    assert outs[1].shape == (1, 192, 4, 4)\n    assert outs[2].shape == (1, 384, 2, 2)\n    assert outs[3].shape == (1, 768, 1, 1)\n\n    # Test abnormal inference size\n    temp = torch.randn((1, 3, 31, 31))\n    model = SwinTransformer()\n    outs = model(temp)\n    assert outs[0].shape == (1, 96, 8, 8)\n    assert outs[1].shape == (1, 192, 4, 4)\n    assert outs[2].shape == (1, 384, 2, 2)\n    assert outs[3].shape == (1, 768, 1, 1)\n\n    # Test abnormal inference size\n    temp = torch.randn((1, 3, 112, 137))\n    model = SwinTransformer()\n    outs = model(temp)\n    assert outs[0].shape == (1, 96, 28, 35)\n    assert outs[1].shape == (1, 192, 14, 18)\n    assert outs[2].shape == (1, 384, 7, 9)\n    assert outs[3].shape == (1, 768, 4, 5)\n\n    model = SwinTransformer(frozen_stages=4)\n    model.train()\n    for p in model.parameters():\n        assert not p.requires_grad\n"
  },
  {
    "path": "tests/test_models/test_backbones/test_trident_resnet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.backbones import TridentResNet\nfrom mmdet.models.backbones.trident_resnet import TridentBottleneck\n\n\ndef test_trident_resnet_bottleneck():\n    trident_dilations = (1, 2, 3)\n    test_branch_idx = 1\n    concat_output = True\n    trident_build_config = (trident_dilations, test_branch_idx, concat_output)\n\n    with pytest.raises(AssertionError):\n        # Style must be in ['pytorch', 'caffe']\n        TridentBottleneck(\n            *trident_build_config, inplanes=64, planes=64, style='tensorflow')\n\n    with pytest.raises(AssertionError):\n        # Allowed positions are 'after_conv1', 'after_conv2', 'after_conv3'\n        plugins = [\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv4')\n        ]\n        TridentBottleneck(\n            *trident_build_config, inplanes=64, planes=16, plugins=plugins)\n\n    with pytest.raises(AssertionError):\n        # Need to specify different postfix to avoid duplicate plugin name\n        plugins = [\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv3'),\n            dict(\n                cfg=dict(type='ContextBlock', ratio=1. / 16),\n                position='after_conv3')\n        ]\n        TridentBottleneck(\n            *trident_build_config, inplanes=64, planes=16, plugins=plugins)\n\n    with pytest.raises(KeyError):\n        # Plugin type is not supported\n        plugins = [dict(cfg=dict(type='WrongPlugin'), position='after_conv3')]\n        TridentBottleneck(\n            *trident_build_config, inplanes=64, planes=16, plugins=plugins)\n\n    # Test Bottleneck with checkpoint forward\n    block = TridentBottleneck(\n        *trident_build_config, inplanes=64, planes=16, with_cp=True)\n    assert block.with_cp\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([block.num_branch, 64, 56, 56])\n\n    # Test Bottleneck style\n    block = TridentBottleneck(\n        *trident_build_config,\n        inplanes=64,\n        planes=64,\n        stride=2,\n        style='pytorch')\n    assert block.conv1.stride == (1, 1)\n    assert block.conv2.stride == (2, 2)\n    block = TridentBottleneck(\n        *trident_build_config, inplanes=64, planes=64, stride=2, style='caffe')\n    assert block.conv1.stride == (2, 2)\n    assert block.conv2.stride == (1, 1)\n\n    # Test Bottleneck forward\n    block = TridentBottleneck(*trident_build_config, inplanes=64, planes=16)\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([block.num_branch, 64, 56, 56])\n\n    # Test Bottleneck with 1 ContextBlock after conv3\n    plugins = [\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16),\n            position='after_conv3')\n    ]\n    block = TridentBottleneck(\n        *trident_build_config, inplanes=64, planes=16, plugins=plugins)\n    assert block.context_block.in_channels == 64\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([block.num_branch, 64, 56, 56])\n\n    # Test Bottleneck with 1 GeneralizedAttention after conv2\n    plugins = [\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='0010',\n                kv_stride=2),\n            position='after_conv2')\n    ]\n    block = TridentBottleneck(\n        *trident_build_config, inplanes=64, planes=16, plugins=plugins)\n    assert block.gen_attention_block.in_channels == 16\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([block.num_branch, 64, 56, 56])\n\n    # Test Bottleneck with 1 GeneralizedAttention after conv2, 1 NonLocal2D\n    # after conv2, 1 ContextBlock after conv3\n    plugins = [\n        dict(\n            cfg=dict(\n                type='GeneralizedAttention',\n                spatial_range=-1,\n                num_heads=8,\n                attention_type='0010',\n                kv_stride=2),\n            position='after_conv2'),\n        dict(cfg=dict(type='NonLocal2d'), position='after_conv2'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16),\n            position='after_conv3')\n    ]\n    block = TridentBottleneck(\n        *trident_build_config, inplanes=64, planes=16, plugins=plugins)\n    assert block.gen_attention_block.in_channels == 16\n    assert block.nonlocal_block.in_channels == 16\n    assert block.context_block.in_channels == 64\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([block.num_branch, 64, 56, 56])\n\n    # Test Bottleneck with 1 ContextBlock after conv2, 2 ContextBlock after\n    # conv3\n    plugins = [\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=1),\n            position='after_conv2'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=2),\n            position='after_conv3'),\n        dict(\n            cfg=dict(type='ContextBlock', ratio=1. / 16, postfix=3),\n            position='after_conv3')\n    ]\n    block = TridentBottleneck(\n        *trident_build_config, inplanes=64, planes=16, plugins=plugins)\n    assert block.context_block1.in_channels == 16\n    assert block.context_block2.in_channels == 64\n    assert block.context_block3.in_channels == 64\n    x = torch.randn(1, 64, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size([block.num_branch, 64, 56, 56])\n\n\ndef test_trident_resnet_backbone():\n    tridentresnet_config = dict(\n        num_branch=3,\n        test_branch_idx=1,\n        strides=(1, 2, 2),\n        dilations=(1, 1, 1),\n        trident_dilations=(1, 2, 3),\n        out_indices=(2, ),\n    )\n    \"\"\"Test tridentresnet backbone.\"\"\"\n    with pytest.raises(AssertionError):\n        # TridentResNet depth should be in [50, 101, 152]\n        TridentResNet(18, **tridentresnet_config)\n\n    with pytest.raises(AssertionError):\n        # In TridentResNet: num_stages == 3\n        TridentResNet(50, num_stages=4, **tridentresnet_config)\n\n    model = TridentResNet(50, num_stages=3, **tridentresnet_config)\n    model.train()\n\n    imgs = torch.randn(1, 3, 32, 32)\n    feat = model(imgs)\n    assert len(feat) == 1\n    assert feat[0].shape == torch.Size([3, 1024, 2, 2])\n"
  },
  {
    "path": "tests/test_models/test_backbones/utils.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom torch.nn.modules import GroupNorm\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.models.backbones.res2net import Bottle2neck\nfrom mmdet.models.backbones.resnet import BasicBlock, Bottleneck\nfrom mmdet.models.backbones.resnext import Bottleneck as BottleneckX\nfrom mmdet.models.layers import SimplifiedBasicBlock\n\n\ndef is_block(modules):\n    \"\"\"Check if is ResNet building block.\"\"\"\n    if isinstance(modules, (BasicBlock, Bottleneck, BottleneckX, Bottle2neck,\n                            SimplifiedBasicBlock)):\n        return True\n    return False\n\n\ndef is_norm(modules):\n    \"\"\"Check if is one of the norms.\"\"\"\n    if isinstance(modules, (GroupNorm, _BatchNorm)):\n        return True\n    return False\n\n\ndef check_norm_state(modules, train_state):\n    \"\"\"Check if norm layer is in correct train state.\"\"\"\n    for mod in modules:\n        if isinstance(mod, _BatchNorm):\n            if mod.training != train_state:\n                return False\n    return True\n"
  },
  {
    "path": "tests/test_models/test_data_preprocessors/test_batch_resize.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nfrom mmdet.models.data_preprocessors import BatchResize, DetDataPreprocessor\nfrom mmdet.testing import demo_mm_inputs\n\n\nclass TestDetDataPreprocessor(TestCase):\n\n    def test_batch_resize(self):\n\n        processor = DetDataPreprocessor(\n            mean=[103.53, 116.28, 123.675],\n            std=[57.375, 57.12, 58.395],\n            bgr_to_rgb=False,\n            batch_augments=[\n                dict(type='BatchResize', scale=(32, 32), pad_size_divisor=32)\n            ])\n        self.assertTrue(isinstance(processor.batch_augments[0], BatchResize))\n\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 10, 11], [3, 9, 24]], use_box_type=True)\n        data = processor(packed_inputs, training=True)\n        batch_inputs, batch_data_samples = data['inputs'], data['data_samples']\n        self.assertEqual(batch_inputs.shape[-2:], (32, 32))\n        self.assertEqual(batch_data_samples[0].scale_factor,\n                         batch_data_samples[1].scale_factor)\n"
  },
  {
    "path": "tests/test_models/test_data_preprocessors/test_boxinst_preprocessor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.models.data_preprocessors import BoxInstDataPreprocessor\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs\n\n\nclass TestBoxInstDataPreprocessor(TestCase):\n\n    def test_forward(self):\n        processor = BoxInstDataPreprocessor(mean=[0, 0, 0], std=[1, 1, 1])\n\n        data = {\n            'inputs': [torch.randint(0, 256, (3, 256, 256))],\n            'data_samples': [DetDataSample()]\n        }\n\n        # Test evaluation mode\n        out_data = processor(data)\n        batch_inputs, batch_data_samples = out_data['inputs'], out_data[\n            'data_samples']\n\n        self.assertEqual(batch_inputs.shape, (1, 3, 256, 256))\n        self.assertEqual(len(batch_data_samples), 1)\n\n        # Test traning mode without gt bboxes\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 256, 256], [3, 128, 128]], num_items=[0, 0])\n        out_data = processor(packed_inputs, training=True)\n        batch_inputs, batch_data_samples = out_data['inputs'], out_data[\n            'data_samples']\n\n        self.assertEqual(batch_inputs.shape, (2, 3, 256, 256))\n        self.assertEqual(len(batch_data_samples), 2)\n        self.assertEqual(len(batch_data_samples[0].gt_instances.masks), 0)\n        self.assertEqual(\n            len(batch_data_samples[0].gt_instances.pairwise_masks), 0)\n        self.assertEqual(len(batch_data_samples[1].gt_instances.masks), 0)\n        self.assertEqual(\n            len(batch_data_samples[1].gt_instances.pairwise_masks), 0)\n\n        # Test traning mode with gt bboxes\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 256, 256], [3, 128, 128]], num_items=[2, 1])\n        out_data = processor(packed_inputs, training=True)\n        batch_inputs, batch_data_samples = out_data['inputs'], out_data[\n            'data_samples']\n\n        self.assertEqual(batch_inputs.shape, (2, 3, 256, 256))\n        self.assertEqual(len(batch_data_samples), 2)\n        self.assertEqual(len(batch_data_samples[0].gt_instances.masks), 2)\n        self.assertEqual(\n            len(batch_data_samples[0].gt_instances.pairwise_masks), 2)\n        self.assertEqual(len(batch_data_samples[1].gt_instances.masks), 1)\n        self.assertEqual(\n            len(batch_data_samples[1].gt_instances.pairwise_masks), 1)\n"
  },
  {
    "path": "tests/test_models/test_data_preprocessors/test_data_preprocessor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\n\nfrom mmdet.models.data_preprocessors import (BatchFixedSizePad,\n                                             BatchSyncRandomResize,\n                                             DetDataPreprocessor,\n                                             MultiBranchDataPreprocessor)\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs\n\n\nclass TestDetDataPreprocessor(TestCase):\n\n    def test_init(self):\n        # test mean is None\n        processor = DetDataPreprocessor()\n        self.assertTrue(not hasattr(processor, 'mean'))\n        self.assertTrue(processor._enable_normalize is False)\n\n        # test mean is not None\n        processor = DetDataPreprocessor(mean=[0, 0, 0], std=[1, 1, 1])\n        self.assertTrue(hasattr(processor, 'mean'))\n        self.assertTrue(hasattr(processor, 'std'))\n        self.assertTrue(processor._enable_normalize)\n\n        # please specify both mean and std\n        with self.assertRaises(AssertionError):\n            DetDataPreprocessor(mean=[0, 0, 0])\n\n        # bgr2rgb and rgb2bgr cannot be set to True at the same time\n        with self.assertRaises(AssertionError):\n            DetDataPreprocessor(bgr_to_rgb=True, rgb_to_bgr=True)\n\n    def test_pad_shape(self):\n        processor = DetDataPreprocessor()\n        data = dict(inputs=torch.randint(0, 256, (2, 3, 10, 15)))\n        batch_pad_shape = processor._get_pad_shape(data)\n        self.assertEqual(batch_pad_shape, [(10, 15), (10, 15)])\n\n        data = dict(inputs=[torch.randint(0, 256, (3, 11, 10))])\n        self.assertEqual(processor._get_pad_shape(data), [(11, 10)])\n\n        # batch with different image sizes\n        data = dict(inputs=[\n            torch.randint(0, 256, (3, 10, 16)),\n            torch.randint(0, 256, (3, 15, 20))\n        ])\n\n        self.assertEqual(processor._get_pad_shape(data), [(10, 16), (15, 20)])\n\n        # test with pad divisor\n        processor = DetDataPreprocessor(pad_size_divisor=10)\n        data = dict(inputs=torch.randint(0, 256, (2, 3, 52, 65)))\n        self.assertAlmostEqual(\n            processor._get_pad_shape(data), [(60, 70), (60, 70)])\n\n    def test_forward(self):\n        processor = DetDataPreprocessor(mean=[0, 0, 0], std=[1, 1, 1])\n\n        data = {\n            'inputs': [torch.randint(0, 256, (3, 11, 10))],\n            'data_samples': [DetDataSample()]\n        }\n        out_data = processor(data)\n        batch_inputs, batch_data_samples = out_data['inputs'], out_data[\n            'data_samples']\n\n        self.assertEqual(batch_inputs.shape, (1, 3, 11, 10))\n        self.assertEqual(len(batch_data_samples), 1)\n\n        # test channel_conversion\n        processor = DetDataPreprocessor(\n            mean=[0., 0., 0.], std=[1., 1., 1.], bgr_to_rgb=True)\n        out_data = processor(data)\n        batch_inputs, batch_data_samples = out_data['inputs'], out_data[\n            'data_samples']\n        self.assertEqual(batch_inputs.shape, (1, 3, 11, 10))\n        self.assertEqual(len(batch_data_samples), 1)\n\n        # test padding\n        data = {\n            'inputs': [\n                torch.randint(0, 256, (3, 10, 11)),\n                torch.randint(0, 256, (3, 9, 14))\n            ]\n        }\n        processor = DetDataPreprocessor(\n            mean=[0., 0., 0.], std=[1., 1., 1.], bgr_to_rgb=True)\n        out_data = processor(data)\n        batch_inputs, batch_data_samples = out_data['inputs'], out_data[\n            'data_samples']\n        self.assertEqual(batch_inputs.shape, (2, 3, 10, 14))\n        self.assertIsNone(batch_data_samples)\n\n        # test pad_size_divisor\n        data = {\n            'inputs': [\n                torch.randint(0, 256, (3, 10, 11)),\n                torch.randint(0, 256, (3, 9, 24))\n            ],\n            'data_samples': [DetDataSample()] * 2\n        }\n        processor = DetDataPreprocessor(\n            mean=[0., 0., 0.], std=[1., 1., 1.], pad_size_divisor=5)\n        out_data = processor(data)\n        batch_inputs, batch_data_samples = out_data['inputs'], out_data[\n            'data_samples']\n        self.assertEqual(batch_inputs.shape, (2, 3, 10, 25))\n        self.assertEqual(len(batch_data_samples), 2)\n        for data_samples, expected_shape in zip(batch_data_samples,\n                                                [(10, 15), (10, 25)]):\n            self.assertEqual(data_samples.pad_shape, expected_shape)\n\n        # test pad_mask=True and pad_seg=True\n        processor = DetDataPreprocessor(\n            pad_mask=True, mask_pad_value=0, pad_seg=True, seg_pad_value=0)\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 10, 11], [3, 9, 24]],\n            with_mask=True,\n            with_semantic=True,\n            use_box_type=True)\n        packed_inputs['data_samples'][0].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 10, 11))\n        packed_inputs['data_samples'][1].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 9, 24))\n        mask_pad_sums = [\n            x.gt_instances.masks.masks.sum()\n            for x in packed_inputs['data_samples']\n        ]\n        seg_pad_sums = [\n            x.gt_sem_seg.sem_seg.sum() for x in packed_inputs['data_samples']\n        ]\n        batch_data_samples = processor(\n            packed_inputs, training=True)['data_samples']\n        for data_samples, expected_shape, mask_pad_sum, seg_pad_sum in zip(\n                batch_data_samples, [(10, 24), (10, 24)], mask_pad_sums,\n                seg_pad_sums):\n            self.assertEqual(data_samples.gt_instances.masks.masks.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_instances.masks.masks.sum(),\n                             mask_pad_sum)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.sum(),\n                             seg_pad_sum)\n\n    def test_batch_sync_random_resize(self):\n        processor = DetDataPreprocessor(batch_augments=[\n            dict(\n                type='BatchSyncRandomResize',\n                random_size_range=(320, 320),\n                size_divisor=32,\n                interval=1)\n        ])\n        self.assertTrue(\n            isinstance(processor.batch_augments[0], BatchSyncRandomResize))\n        message_hub = MessageHub.get_instance('test_batch_sync_random_resize')\n        message_hub.update_info('iter', 0)\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 128, 128], [3, 128, 128]], use_box_type=True)\n        batch_inputs = processor(packed_inputs, training=True)['inputs']\n        self.assertEqual(batch_inputs.shape, (2, 3, 128, 128))\n\n        # resize after one iter\n        message_hub.update_info('iter', 1)\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 128, 128], [3, 128, 128]], use_box_type=True)\n        batch_inputs = processor(packed_inputs, training=True)['inputs']\n        self.assertEqual(batch_inputs.shape, (2, 3, 320, 320))\n\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 128, 128], [3, 128, 128]], use_box_type=True)\n        batch_inputs = processor(packed_inputs, training=False)['inputs']\n        self.assertEqual(batch_inputs.shape, (2, 3, 128, 128))\n\n    def test_batch_fixed_size_pad(self):\n        # test pad_mask=False and pad_seg=False\n        processor = DetDataPreprocessor(\n            pad_mask=False,\n            pad_seg=False,\n            batch_augments=[\n                dict(\n                    type='BatchFixedSizePad',\n                    size=(32, 32),\n                    img_pad_value=0,\n                    pad_mask=True,\n                    mask_pad_value=0,\n                    pad_seg=True,\n                    seg_pad_value=0)\n            ])\n        self.assertTrue(\n            isinstance(processor.batch_augments[0], BatchFixedSizePad))\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 10, 11], [3, 9, 24]],\n            with_mask=True,\n            with_semantic=True,\n            use_box_type=True)\n        packed_inputs['data_samples'][0].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 10, 11))\n        packed_inputs['data_samples'][1].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 9, 24))\n        mask_pad_sums = [\n            x.gt_instances.masks.masks.sum()\n            for x in packed_inputs['data_samples']\n        ]\n        seg_pad_sums = [\n            x.gt_sem_seg.sem_seg.sum() for x in packed_inputs['data_samples']\n        ]\n        data = processor(packed_inputs, training=True)\n        batch_inputs, batch_data_samples = data['inputs'], data['data_samples']\n        self.assertEqual(batch_inputs.shape[-2:], (32, 32))\n        for data_samples, expected_shape, mask_pad_sum, seg_pad_sum in zip(\n                batch_data_samples, [(32, 32), (32, 32)], mask_pad_sums,\n                seg_pad_sums):\n            self.assertEqual(data_samples.gt_instances.masks.masks.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_instances.masks.masks.sum(),\n                             mask_pad_sum)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.sum(),\n                             seg_pad_sum)\n\n        # test pad_mask=True and pad_seg=True\n        processor = DetDataPreprocessor(\n            pad_mask=True,\n            pad_seg=True,\n            seg_pad_value=0,\n            mask_pad_value=0,\n            batch_augments=[\n                dict(\n                    type='BatchFixedSizePad',\n                    size=(32, 32),\n                    img_pad_value=0,\n                    pad_mask=True,\n                    mask_pad_value=0,\n                    pad_seg=True,\n                    seg_pad_value=0)\n            ])\n        self.assertTrue(\n            isinstance(processor.batch_augments[0], BatchFixedSizePad))\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 10, 11], [3, 9, 24]],\n            with_mask=True,\n            with_semantic=True,\n            use_box_type=True)\n        packed_inputs['data_samples'][0].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 10, 11))\n        packed_inputs['data_samples'][1].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 9, 24))\n        mask_pad_sums = [\n            x.gt_instances.masks.masks.sum()\n            for x in packed_inputs['data_samples']\n        ]\n        seg_pad_sums = [\n            x.gt_sem_seg.sem_seg.sum() for x in packed_inputs['data_samples']\n        ]\n        data = processor(packed_inputs, training=True)\n        batch_inputs, batch_data_samples = data['inputs'], data['data_samples']\n        self.assertEqual(batch_inputs.shape[-2:], (32, 32))\n        for data_samples, expected_shape, mask_pad_sum, seg_pad_sum in zip(\n                batch_data_samples, [(32, 32), (32, 32)], mask_pad_sums,\n                seg_pad_sums):\n            self.assertEqual(data_samples.gt_instances.masks.masks.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_instances.masks.masks.sum(),\n                             mask_pad_sum)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.sum(),\n                             seg_pad_sum)\n\n        # test negative pad/no pad\n        processor = DetDataPreprocessor(\n            pad_mask=True,\n            pad_seg=True,\n            seg_pad_value=0,\n            mask_pad_value=0,\n            batch_augments=[\n                dict(\n                    type='BatchFixedSizePad',\n                    size=(5, 5),\n                    img_pad_value=0,\n                    pad_mask=True,\n                    mask_pad_value=1,\n                    pad_seg=True,\n                    seg_pad_value=1)\n            ])\n        self.assertTrue(\n            isinstance(processor.batch_augments[0], BatchFixedSizePad))\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 10, 11], [3, 9, 24]],\n            with_mask=True,\n            with_semantic=True,\n            use_box_type=True)\n        packed_inputs['data_samples'][0].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 10, 11))\n        packed_inputs['data_samples'][1].gt_sem_seg.sem_seg = torch.randint(\n            0, 256, (1, 9, 24))\n        mask_pad_sums = [\n            x.gt_instances.masks.masks.sum()\n            for x in packed_inputs['data_samples']\n        ]\n        seg_pad_sums = [\n            x.gt_sem_seg.sem_seg.sum() for x in packed_inputs['data_samples']\n        ]\n        data = processor(packed_inputs, training=True)\n        batch_inputs, batch_data_samples = data['inputs'], data['data_samples']\n        self.assertEqual(batch_inputs.shape[-2:], (10, 24))\n        for data_samples, expected_shape, mask_pad_sum, seg_pad_sum in zip(\n                batch_data_samples, [(10, 24), (10, 24)], mask_pad_sums,\n                seg_pad_sums):\n            self.assertEqual(data_samples.gt_instances.masks.masks.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.shape[-2:],\n                             expected_shape)\n            self.assertEqual(data_samples.gt_instances.masks.masks.sum(),\n                             mask_pad_sum)\n            self.assertEqual(data_samples.gt_sem_seg.sem_seg.sum(),\n                             seg_pad_sum)\n\n\nclass TestMultiBranchDataPreprocessor(TestCase):\n\n    def setUp(self):\n        \"\"\"Setup the model and optimizer which are used in every test method.\n\n        TestCase calls functions in this order: setUp() -> testMethod() ->\n        tearDown() -> cleanUp()\n        \"\"\"\n        self.data_preprocessor = dict(\n            type='DetDataPreprocessor',\n            mean=[123.675, 116.28, 103.53],\n            std=[58.395, 57.12, 57.375],\n            bgr_to_rgb=True,\n            pad_size_divisor=32)\n        self.multi_data = {\n            'inputs': {\n                'sup': [torch.randint(0, 256, (3, 224, 224))],\n                'unsup_teacher': [\n                    torch.randint(0, 256, (3, 400, 600)),\n                    torch.randint(0, 256, (3, 600, 400))\n                ],\n                'unsup_student': [\n                    torch.randint(0, 256, (3, 700, 500)),\n                    torch.randint(0, 256, (3, 500, 700))\n                ]\n            },\n            'data_samples': {\n                'sup': [DetDataSample()],\n                'unsup_teacher': [DetDataSample(),\n                                  DetDataSample()],\n                'unsup_student': [DetDataSample(),\n                                  DetDataSample()],\n            }\n        }\n        self.data = {\n            'inputs': [torch.randint(0, 256, (3, 224, 224))],\n            'data_samples': [DetDataSample()]\n        }\n\n    def test_multi_data_preprocessor(self):\n        processor = MultiBranchDataPreprocessor(self.data_preprocessor)\n        # test processing multi_data when training\n        multi_data = processor(self.multi_data, training=True)\n        self.assertEqual(multi_data['inputs']['sup'].shape, (1, 3, 224, 224))\n        self.assertEqual(multi_data['inputs']['unsup_teacher'].shape,\n                         (2, 3, 608, 608))\n        self.assertEqual(multi_data['inputs']['unsup_student'].shape,\n                         (2, 3, 704, 704))\n        self.assertEqual(len(multi_data['data_samples']['sup']), 1)\n        self.assertEqual(len(multi_data['data_samples']['unsup_teacher']), 2)\n        self.assertEqual(len(multi_data['data_samples']['unsup_student']), 2)\n        # test processing data when testing\n        data = processor(self.data)\n        self.assertEqual(data['inputs'].shape, (1, 3, 224, 224))\n        self.assertEqual(len(data['data_samples']), 1)\n"
  },
  {
    "path": "tests/test_models/test_data_preprocessors/test_track_data_preprocessor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nfrom mmdet.models.data_preprocessors import TrackDataPreprocessor\nfrom mmdet.testing import demo_track_inputs\n\n\nclass TestTrackDataPreprocessor(TestCase):\n\n    def test_init(self):\n        # test mean is None\n        processor = TrackDataPreprocessor()\n        self.assertTrue(not hasattr(processor, 'mean'))\n        self.assertTrue(processor._enable_normalize is False)\n\n        # test mean is not None\n        processor = TrackDataPreprocessor(mean=[0, 0, 0], std=[1, 1, 1])\n        self.assertTrue(hasattr(processor, 'mean'))\n        self.assertTrue(hasattr(processor, 'std'))\n        self.assertTrue(processor._enable_normalize)\n\n        # please specify both mean and std\n        with self.assertRaises(AssertionError):\n            TrackDataPreprocessor(mean=[0, 0, 0])\n\n        # bgr2rgb and rgb2bgr cannot be set to True at the same time\n        with self.assertRaises(AssertionError):\n            TrackDataPreprocessor(bgr_to_rgb=True, rgb_to_bgr=True)\n\n    def test_forward(self):\n        processor = TrackDataPreprocessor(mean=[0, 0, 0], std=[1, 1, 1])\n\n        data = demo_track_inputs(\n            batch_size=1,\n            num_frames=1,\n            image_shapes=(3, 11, 10),\n            num_items=[1])\n        out_data = processor(data)\n        inputs, data_samples = out_data['inputs'], out_data['data_samples']\n        self.assertEqual(inputs.shape, (1, 1, 3, 11, 10))\n        self.assertEqual(len(data_samples), 1)\n\n        # test channel_conversion\n        processor = TrackDataPreprocessor(\n            mean=[0., 0., 0.], std=[1., 1., 1.], bgr_to_rgb=True)\n        out_data = processor(data)\n        inputs, data_samples = out_data['inputs'], out_data['data_samples']\n        self.assertEqual(len(data_samples), 1)\n\n        # test padding\n        data = demo_track_inputs(\n            batch_size=2,\n            num_frames=2,\n            image_shapes=[(3, 10, 11), (3, 9, 14)],\n            num_items=[1, 1])\n        out_data = processor(data)\n        inputs, data_samples = out_data['inputs'], out_data['data_samples']\n        self.assertEqual(inputs.shape, (2, 2, 3, 10, 14))\n\n        # test pad_size_divisor\n        data = demo_track_inputs(\n            batch_size=2,\n            num_frames=2,\n            image_shapes=[(3, 10, 11), (3, 9, 24)],\n            num_items=[1, 1])\n        processor = TrackDataPreprocessor(\n            mean=[0., 0., 0.], std=[1., 1., 1.], pad_size_divisor=5)\n        out_data = processor(data)\n        inputs, data_samples = out_data['inputs'], out_data['data_samples']\n        self.assertEqual(inputs.shape, (2, 2, 3, 10, 25))\n        self.assertEqual(len(data_samples), 2)\n        for track_data_sample, expected_shape in zip(data_samples, [(10, 15),\n                                                                    (10, 25)]):\n            for det_data_sample in track_data_sample.video_data_samples:\n                self.assertEqual(det_data_sample.pad_shape, expected_shape)\n\n        # test pad_mask=True\n        data = demo_track_inputs(\n            batch_size=2,\n            num_frames=2,\n            image_shapes=[(3, 10, 11), (3, 9, 24)],\n            num_items=[1, 1],\n            with_mask=True)\n        processor = TrackDataPreprocessor(pad_mask=True, mask_pad_value=0)\n        mask_pad_sums = []\n        for track_data_sample in data['data_samples']:\n            pad_sum_per_sample = []\n            for x in track_data_sample.video_data_samples:\n                pad_sum_per_sample.append(x.gt_instances.masks.masks.sum())\n            mask_pad_sums.append(pad_sum_per_sample)\n        out_data = processor(data, training=True)\n        inputs, data_samples = out_data['inputs'], out_data['data_samples']\n        for track_data_sample, expected_shape, mask_pad_sum in zip(\n                data_samples, [(10, 24), (10, 24)], mask_pad_sums):\n            for i, det_data_sample in enumerate(\n                    track_data_sample.video_data_samples):\n                self.assertEqual(\n                    det_data_sample.gt_instances.masks.masks.shape[-2:],\n                    expected_shape)\n                self.assertEqual(\n                    det_data_sample.gt_instances.masks.masks.sum(),\n                    mask_pad_sum[i])\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_anchor_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import AnchorHead\n\n\nclass TestAnchorHead(TestCase):\n\n    def test_anchor_head_loss(self):\n        \"\"\"Tests anchor head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.3,\n                    min_pos_iou=0.3,\n                    match_low_quality=True,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=256,\n                    pos_fraction=0.5,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                allowed_border=0,\n                pos_weight=-1,\n                debug=False))\n        anchor_head = AnchorHead(num_classes=4, in_channels=1, train_cfg=cfg)\n\n        # Anchor head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // (2**(i + 2)), s // (2**(i + 2)))\n            for i in range(len(anchor_head.prior_generator.strides)))\n        cls_scores, bbox_preds = anchor_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = anchor_head.loss_by_feat(cls_scores, bbox_preds,\n                                                   [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box loss.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        assert empty_cls_loss.item() > 0, 'cls loss should be non-zero'\n        assert empty_box_loss.item() == 0, (\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = anchor_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        assert onegt_cls_loss.item() > 0, 'cls loss should be non-zero'\n        assert onegt_box_loss.item() > 0, 'box loss should be non-zero'\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_atss_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import ATSSHead\n\n\nclass TestATSSHead(TestCase):\n\n    def test_atss_head_loss(self):\n        \"\"\"Tests atss head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1\n        }]\n        cfg = Config(\n            dict(\n                assigner=dict(type='ATSSAssigner', topk=9),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        atss_head = ATSSHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            feat_channels=1,\n            norm_cfg=None,\n            train_cfg=cfg,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8, 16, 32, 64, 128]),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=2.0))\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [8, 16, 32, 64, 128]\n        ]\n        cls_scores, bbox_preds, centernesses = atss_head.forward(feat)\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = atss_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 centernesses, [gt_instances],\n                                                 img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        empty_centerness_loss = sum(empty_gt_losses['loss_centerness'])\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_centerness_loss.item(), 0,\n            'there should be no centerness loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = atss_head.loss_by_feat(cls_scores, bbox_preds,\n                                               centernesses, [gt_instances],\n                                               img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        onegt_centerness_loss = sum(one_gt_losses['loss_centerness'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_centerness_loss.item(), 0,\n                           'centerness loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_autoassign_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import AutoAssignHead\n\n\nclass TestAutoAssignHead(TestCase):\n\n    def test_autoassign_head_loss(self):\n        \"\"\"Tests autoassign head loss when truth is empty and non-empty.\"\"\"\n        s = 300\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        autoassign_head = AutoAssignHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            feat_channels=1,\n            strides=[8, 16, 32, 64, 128],\n            loss_bbox=dict(type='GIoULoss', loss_weight=5.0),\n            norm_cfg=None)\n\n        # Fcos head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in autoassign_head.prior_generator.strides)\n        cls_scores, bbox_preds, centernesses = autoassign_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = autoassign_head.loss_by_feat(cls_scores, bbox_preds,\n                                                       centernesses,\n                                                       [gt_instances],\n                                                       img_metas)\n        # When there is no truth, the neg loss should be nonzero but\n        # pos loss and center loss should be zero\n        empty_pos_loss = empty_gt_losses['loss_pos'].item()\n        empty_neg_loss = empty_gt_losses['loss_neg'].item()\n        empty_ctr_loss = empty_gt_losses['loss_center'].item()\n        self.assertGreater(empty_neg_loss, 0, 'neg loss should be non-zero')\n        self.assertEqual(\n            empty_pos_loss, 0,\n            'there should be no pos loss when there are no true boxes')\n        self.assertEqual(\n            empty_ctr_loss, 0,\n            'there should be no centerness loss when there are no true boxes')\n\n        # When truth is non-empty then all pos, neg loss and center loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = autoassign_head.loss_by_feat(cls_scores, bbox_preds,\n                                                     centernesses,\n                                                     [gt_instances], img_metas)\n        onegt_pos_loss = one_gt_losses['loss_pos'].item()\n        onegt_neg_loss = one_gt_losses['loss_neg'].item()\n        onegt_ctr_loss = one_gt_losses['loss_center'].item()\n        self.assertGreater(onegt_pos_loss, 0, 'pos loss should be non-zero')\n        self.assertGreater(onegt_neg_loss, 0, 'neg loss should be non-zero')\n        self.assertGreater(onegt_ctr_loss, 0, 'center loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_boxinst_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine import MessageHub\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import BoxInstBboxHead, BoxInstMaskHead\nfrom mmdet.structures.mask import BitmapMasks\n\n\ndef _rand_masks(num_items, bboxes, img_w, img_h):\n    rng = np.random.RandomState(0)\n    masks = np.zeros((num_items, img_h, img_w), dtype=np.float32)\n    for i, bbox in enumerate(bboxes):\n        bbox = bbox.astype(np.int32)\n        mask = (rng.rand(1, bbox[3] - bbox[1], bbox[2] - bbox[0]) >\n                0.3).astype(np.int64)\n        masks[i:i + 1, bbox[1]:bbox[3], bbox[0]:bbox[2]] = mask\n    return BitmapMasks(masks, height=img_h, width=img_w)\n\n\ndef _fake_mask_feature_head():\n    mask_feature_head = ConfigDict(\n        in_channels=1,\n        feat_channels=1,\n        start_level=0,\n        end_level=2,\n        out_channels=8,\n        mask_stride=8,\n        num_stacked_convs=4,\n        norm_cfg=dict(type='BN', requires_grad=True))\n    return mask_feature_head\n\n\nclass TestBoxInstHead(TestCase):\n\n    def test_boxinst_maskhead_loss(self):\n        \"\"\"Tests boxinst maskhead loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        boxinst_bboxhead = BoxInstBboxHead(\n            num_classes=4,\n            in_channels=1,\n            feat_channels=1,\n            stacked_convs=1,\n            norm_cfg=None)\n\n        mask_feature_head = _fake_mask_feature_head()\n        boxinst_maskhead = BoxInstMaskHead(\n            mask_feature_head=mask_feature_head,\n            loss_mask=dict(\n                type='DiceLoss',\n                use_sigmoid=True,\n                activate=True,\n                eps=5e-6,\n                loss_weight=1.0))\n\n        # Fcos head expects a multiple levels of features per image\n        feats = []\n        for i in range(len(boxinst_bboxhead.strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 3)), s // (2**(i + 3))))\n        feats = tuple(feats)\n        cls_scores, bbox_preds, centernesses, param_preds =\\\n            boxinst_bboxhead.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_instances.masks = _rand_masks(0, gt_instances.bboxes.numpy(), s, s)\n        gt_instances.pairwise_masks = _rand_masks(\n            0, gt_instances.bboxes.numpy(), s // 4, s // 4).to_tensor(\n                dtype=torch.float32,\n                device='cpu').unsqueeze(1).repeat(1, 8, 1, 1)\n        message_hub = MessageHub.get_instance('runtime_info')\n        message_hub.update_info('iter', 1)\n        _ = boxinst_bboxhead.loss_by_feat(cls_scores, bbox_preds, centernesses,\n                                          param_preds, [gt_instances],\n                                          img_metas)\n        # When truth is empty then all mask loss\n        # should be zero for random inputs\n        positive_infos = boxinst_bboxhead.get_positive_infos()\n        mask_outs = boxinst_maskhead.forward(feats, positive_infos)\n        empty_gt_mask_losses = boxinst_maskhead.loss_by_feat(\n            *mask_outs, [gt_instances], img_metas, positive_infos)\n        loss_mask_project = empty_gt_mask_losses['loss_mask_project']\n        loss_mask_pairwise = empty_gt_mask_losses['loss_mask_pairwise']\n        self.assertEqual(loss_mask_project, 0,\n                         'mask project loss should be zero')\n        self.assertEqual(loss_mask_pairwise, 0,\n                         'mask pairwise loss should be zero')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor([[0.111, 0.222, 25.6667, 29.8757]])\n        gt_instances.labels = torch.LongTensor([2])\n        gt_instances.masks = _rand_masks(1, gt_instances.bboxes.numpy(), s, s)\n        gt_instances.pairwise_masks = _rand_masks(\n            1, gt_instances.bboxes.numpy(), s // 4, s // 4).to_tensor(\n                dtype=torch.float32,\n                device='cpu').unsqueeze(1).repeat(1, 8, 1, 1)\n\n        _ = boxinst_bboxhead.loss_by_feat(cls_scores, bbox_preds, centernesses,\n                                          param_preds, [gt_instances],\n                                          img_metas)\n        positive_infos = boxinst_bboxhead.get_positive_infos()\n        mask_outs = boxinst_maskhead.forward(feats, positive_infos)\n        one_gt_mask_losses = boxinst_maskhead.loss_by_feat(\n            *mask_outs, [gt_instances], img_metas, positive_infos)\n        loss_mask_project = one_gt_mask_losses['loss_mask_project']\n        loss_mask_pairwise = one_gt_mask_losses['loss_mask_pairwise']\n        self.assertGreater(loss_mask_project, 0,\n                           'mask project loss should be nonzero')\n        self.assertGreater(loss_mask_pairwise, 0,\n                           'mask pairwise loss should be nonzero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_cascade_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import CascadeRPNHead\nfrom mmdet.structures import DetDataSample\n\nrpn_weight = 0.7\ncascade_rpn_config = ConfigDict(\n    dict(\n        num_stages=2,\n        num_classes=1,\n        stages=[\n            dict(\n                type='StageCascadeRPNHead',\n                in_channels=1,\n                feat_channels=1,\n                anchor_generator=dict(\n                    type='AnchorGenerator',\n                    scales=[8],\n                    ratios=[1.0],\n                    strides=[4, 8, 16, 32, 64]),\n                adapt_cfg=dict(type='dilation', dilation=3),\n                bridged_feature=True,\n                with_cls=False,\n                reg_decoded_bbox=True,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=(.0, .0, .0, .0),\n                    target_stds=(0.1, 0.1, 0.5, 0.5)),\n                loss_bbox=dict(\n                    type='IoULoss', linear=True,\n                    loss_weight=10.0 * rpn_weight)),\n            dict(\n                type='StageCascadeRPNHead',\n                in_channels=1,\n                feat_channels=1,\n                adapt_cfg=dict(type='offset'),\n                bridged_feature=False,\n                with_cls=True,\n                reg_decoded_bbox=True,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=(.0, .0, .0, .0),\n                    target_stds=(0.05, 0.05, 0.1, 0.1)),\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=True,\n                    loss_weight=1.0 * rpn_weight),\n                loss_bbox=dict(\n                    type='IoULoss', linear=True,\n                    loss_weight=10.0 * rpn_weight))\n        ],\n        train_cfg=[\n            dict(\n                assigner=dict(\n                    type='RegionAssigner', center_ratio=0.2, ignore_ratio=0.5),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False),\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.7,\n                    min_pos_iou=0.3,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=256,\n                    pos_fraction=0.5,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False)\n        ],\n        test_cfg=dict(max_per_img=300, nms=dict(iou_threshold=0.8))))\n\n\nclass TestStageCascadeRPNHead(TestCase):\n\n    def test_cascade_rpn_head_loss(self):\n        \"\"\"Tests cascade rpn head loss when truth is empty and non-empty.\"\"\"\n        cascade_rpn_head = CascadeRPNHead(**cascade_rpn_config)\n\n        s = 256\n        feats = [\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in cascade_rpn_head.stages[0].prior_generator.strides\n        ]\n        img_metas = {\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': 1,\n        }\n        sample = DetDataSample()\n        sample.set_metainfo(img_metas)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        sample.gt_instances = gt_instances\n\n        empty_gt_losses = cascade_rpn_head.loss(feats, [sample])\n        for key, loss in empty_gt_losses.items():\n            loss = sum(loss)\n            if 'cls' in key:\n                self.assertGreater(loss.item(), 0,\n                                   'cls loss should be non-zero')\n            elif 'reg' in key:\n                self.assertEqual(\n                    loss.item(), 0,\n                    'there should be no reg loss when no ground true boxes')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([0])\n        sample.gt_instances = gt_instances\n\n        one_gt_losses = cascade_rpn_head.loss(feats, [sample])\n        for loss in one_gt_losses.values():\n            loss = sum(loss)\n            self.assertGreater(\n                loss.item(), 0,\n                'cls loss, or box loss, or iou loss should be non-zero')\n\n    def test_cascade_rpn_head_loss_and_predict(self):\n        \"\"\"Tests cascade rpn head loss and predict function.\"\"\"\n        cascade_rpn_head = CascadeRPNHead(**cascade_rpn_config)\n\n        s = 256\n        feats = [\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in cascade_rpn_head.stages[0].prior_generator.strides\n        ]\n        img_metas = {\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': 1,\n        }\n        sample = DetDataSample()\n        sample.set_metainfo(img_metas)\n\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        sample.gt_instances = gt_instances\n        proposal_cfg = ConfigDict(\n            dict(max_per_img=300, nms=dict(iou_threshold=0.8)))\n\n        cascade_rpn_head.loss_and_predict(feats, [sample], proposal_cfg)\n\n    def test_cascade_rpn_head_predict(self):\n        \"\"\"Tests cascade rpn head predict function.\"\"\"\n        cascade_rpn_head = CascadeRPNHead(**cascade_rpn_config)\n\n        s = 256\n        feats = [\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in cascade_rpn_head.stages[0].prior_generator.strides\n        ]\n        img_metas = {\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': 1,\n        }\n        sample = DetDataSample()\n        sample.set_metainfo(img_metas)\n\n        cascade_rpn_head.predict(feats, [sample])\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_centernet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import CenterNetHead\n\n\nclass TestCenterNetHead(TestCase):\n\n    def test_center_head_loss(self):\n        \"\"\"Tests center head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{'batch_input_shape': (s, s, 3)}]\n        test_cfg = dict(topK=100, max_per_img=100)\n        centernet_head = CenterNetHead(\n            num_classes=4, in_channels=1, feat_channels=4, test_cfg=test_cfg)\n\n        feat = [torch.rand(1, 1, s, s)]\n        center_out, wh_out, offset_out = centernet_head.forward(feat)\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        empty_gt_losses = centernet_head.loss_by_feat(center_out, wh_out,\n                                                      offset_out,\n                                                      [gt_instances],\n                                                      img_metas)\n        loss_center = empty_gt_losses['loss_center_heatmap']\n        loss_wh = empty_gt_losses['loss_wh']\n        loss_offset = empty_gt_losses['loss_offset']\n        assert loss_center.item() > 0, 'loss_center should be non-zero'\n        assert loss_wh.item() == 0, (\n            'there should be no loss_wh when there are no true boxes')\n        assert loss_offset.item() == 0, (\n            'there should be no loss_offset when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = centernet_head.loss_by_feat(center_out, wh_out,\n                                                    offset_out, [gt_instances],\n                                                    img_metas)\n        loss_center = one_gt_losses['loss_center_heatmap']\n        loss_wh = one_gt_losses['loss_wh']\n        loss_offset = one_gt_losses['loss_offset']\n        assert loss_center.item() > 0, 'loss_center should be non-zero'\n        assert loss_wh.item() > 0, 'loss_wh should be non-zero'\n        assert loss_offset.item() > 0, 'loss_offset should be non-zero'\n\n    def test_centernet_head_get_targets(self):\n        \"\"\"Tests center head generating and decoding the heatmap.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'batch_input_shape': (s, s),\n        }]\n        test_cfg = ConfigDict(\n            dict(topk=100, local_maximum_kernel=3, max_per_img=100))\n        gt_bboxes = [\n            torch.Tensor([[10, 20, 200, 240], [40, 50, 100, 200],\n                          [10, 20, 100, 240]])\n        ]\n        gt_labels = [torch.LongTensor([1, 1, 2])]\n\n        centernet_head = CenterNetHead(\n            num_classes=4, in_channels=1, feat_channels=4, test_cfg=test_cfg)\n        self.feat_shape = (1, 1, s // 4, s // 4)\n        targets, _ = centernet_head.get_targets(gt_bboxes, gt_labels,\n                                                self.feat_shape,\n                                                img_metas[0]['img_shape'])\n        center_target = targets['center_heatmap_target']\n        wh_target = targets['wh_target']\n        offset_target = targets['offset_target']\n        # make sure assign target right\n        for i in range(len(gt_bboxes[0])):\n            bbox, label = gt_bboxes[0][i] / 4, gt_labels[0][i]\n            ctx, cty = sum(bbox[0::2]) / 2, sum(bbox[1::2]) / 2\n            int_ctx, int_cty = int(sum(bbox[0::2]) / 2), int(\n                sum(bbox[1::2]) / 2)\n            w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]\n            x_off = ctx - int(ctx)\n            y_off = cty - int(cty)\n            assert center_target[0, label, int_cty, int_ctx] == 1\n            assert wh_target[0, 0, int_cty, int_ctx] == w\n            assert wh_target[0, 1, int_cty, int_ctx] == h\n            assert offset_target[0, 0, int_cty, int_ctx] == x_off\n            assert offset_target[0, 1, int_cty, int_ctx] == y_off\n\n    def test_centernet_head_get_results(self):\n        \"\"\"Tests center head generating and decoding the heatmap.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'batch_input_shape': (s, s),\n            'border': (0, 0, 0, 0),\n        }]\n        test_cfg = ConfigDict(\n            dict(\n                topk=100,\n                local_maximum_kernel=3,\n                max_per_img=100,\n                nms=dict(type='nms', iou_threshold=0.5)))\n        gt_bboxes = [\n            torch.Tensor([[10, 20, 200, 240], [40, 50, 100, 200],\n                          [10, 20, 100, 240]])\n        ]\n        gt_labels = [torch.LongTensor([1, 1, 2])]\n\n        centernet_head = CenterNetHead(\n            num_classes=4, in_channels=1, feat_channels=4, test_cfg=test_cfg)\n        self.feat_shape = (1, 1, s // 4, s // 4)\n        targets, _ = centernet_head.get_targets(gt_bboxes, gt_labels,\n                                                self.feat_shape,\n                                                img_metas[0]['img_shape'])\n        center_target = targets['center_heatmap_target']\n        wh_target = targets['wh_target']\n        offset_target = targets['offset_target']\n        # make sure get_bboxes is right\n        detections = centernet_head.predict_by_feat([center_target],\n                                                    [wh_target],\n                                                    [offset_target],\n                                                    img_metas,\n                                                    rescale=True,\n                                                    with_nms=False)\n\n        pred_instances = detections[0]\n        out_bboxes = pred_instances.bboxes[:3]\n        out_clses = pred_instances.labels[:3]\n        for bbox, cls in zip(out_bboxes, out_clses):\n            flag = False\n            for gt_bbox, gt_cls in zip(gt_bboxes[0], gt_labels[0]):\n                if (bbox[:4] == gt_bbox[:4]).all():\n                    flag = True\n            assert flag, 'get_bboxes is wrong'\n\n        detections = centernet_head.predict_by_feat([center_target],\n                                                    [wh_target],\n                                                    [offset_target],\n                                                    img_metas,\n                                                    rescale=True,\n                                                    with_nms=True)\n\n        pred_instances = detections[0]\n        out_bboxes = pred_instances.bboxes[:3]\n        out_clses = pred_instances.labels[:3]\n        for bbox, cls in zip(out_bboxes, out_clses):\n            flag = False\n            for gt_bbox, gt_cls in zip(gt_bboxes[0], gt_labels[0]):\n                if (bbox[:4] == gt_bbox[:4]).all():\n                    flag = True\n        assert flag, 'get_bboxes is wrong'\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_centernet_update_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import CenterNetUpdateHead\n\n\nclass TestCenterNetUpdateHead(TestCase):\n\n    def test_centernet_update_head_loss(self):\n        \"\"\"Tests fcos head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        centernet_head = CenterNetUpdateHead(\n            num_classes=4,\n            in_channels=1,\n            feat_channels=1,\n            stacked_convs=1,\n            norm_cfg=None)\n\n        # Fcos head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in centernet_head.prior_generator.strides)\n        cls_scores, bbox_preds = centernet_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = centernet_head.loss_by_feat(cls_scores, bbox_preds,\n                                                      [gt_instances],\n                                                      img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # box loss and centerness loss should be zero\n        empty_cls_loss = empty_gt_losses['loss_cls'].item()\n        empty_box_loss = empty_gt_losses['loss_bbox'].item()\n        self.assertGreater(empty_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss, 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = centernet_head.loss_by_feat(cls_scores, bbox_preds,\n                                                    [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls'].item()\n        onegt_box_loss = one_gt_losses['loss_bbox'].item()\n        self.assertGreater(onegt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss, 0, 'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_centripetal_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import CentripetalHead\n\n\nclass TestCentripetalHead(TestCase):\n\n    def test_centripetal_head_loss(self):\n        \"\"\"Tests corner head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3)\n        }]\n\n        centripetal_head = CentripetalHead(\n            num_classes=4, in_channels=1, corner_emb_channels=0)\n\n        # Corner head expects a multiple levels of features per image\n        feat = [\n            torch.rand(1, 1, s // 4, s // 4)\n            for _ in range(centripetal_head.num_feat_levels)\n        ]\n        forward_outputs = centripetal_head.forward(feat)\n\n        # Test that empty ground truth encourages the network\n        # to predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_bboxes_ignore = None\n\n        empty_gt_losses = centripetal_head.loss_by_feat(\n            *forward_outputs, [gt_instances], img_metas, gt_bboxes_ignore)\n        empty_det_loss = sum(empty_gt_losses['det_loss'])\n        empty_guiding_loss = sum(empty_gt_losses['guiding_loss'])\n        empty_centripetal_loss = sum(empty_gt_losses['centripetal_loss'])\n        empty_off_loss = sum(empty_gt_losses['off_loss'])\n        self.assertTrue(empty_det_loss.item() > 0,\n                        'det loss should be non-zero')\n        self.assertTrue(\n            empty_guiding_loss.item() == 0,\n            'there should be no guiding loss when there are no true boxes')\n        self.assertTrue(\n            empty_centripetal_loss.item() == 0,\n            'there should be no centripetal loss when there are no true boxes')\n        self.assertTrue(\n            empty_off_loss.item() == 0,\n            'there should be no box loss when there are no true boxes')\n\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874],\n             [123.6667, 123.8757, 138.6326, 251.8874]])\n        gt_instances.labels = torch.LongTensor([2, 3])\n\n        two_gt_losses = centripetal_head.loss_by_feat(*forward_outputs,\n                                                      [gt_instances],\n                                                      img_metas,\n                                                      gt_bboxes_ignore)\n        twogt_det_loss = sum(two_gt_losses['det_loss'])\n        twogt_guiding_loss = sum(two_gt_losses['guiding_loss'])\n        twogt_centripetal_loss = sum(two_gt_losses['centripetal_loss'])\n        twogt_off_loss = sum(two_gt_losses['off_loss'])\n        assert twogt_det_loss.item() > 0, 'det loss should be non-zero'\n        assert twogt_guiding_loss.item() > 0, 'push loss should be non-zero'\n        assert twogt_centripetal_loss.item(\n        ) > 0, 'pull loss should be non-zero'\n        assert twogt_off_loss.item() > 0, 'off loss should be non-zero'\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_condinst_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import CondInstBboxHead, CondInstMaskHead\nfrom mmdet.structures.mask import BitmapMasks\n\n\ndef _rand_masks(num_items, bboxes, img_w, img_h):\n    rng = np.random.RandomState(0)\n    masks = np.zeros((num_items, img_h, img_w), dtype=np.float32)\n    for i, bbox in enumerate(bboxes):\n        bbox = bbox.astype(np.int32)\n        mask = (rng.rand(1, bbox[3] - bbox[1], bbox[2] - bbox[0]) >\n                0.3).astype(np.int64)\n        masks[i:i + 1, bbox[1]:bbox[3], bbox[0]:bbox[2]] = mask\n    return BitmapMasks(masks, height=img_h, width=img_w)\n\n\ndef _fake_mask_feature_head():\n    mask_feature_head = ConfigDict(\n        in_channels=1,\n        feat_channels=1,\n        start_level=0,\n        end_level=2,\n        out_channels=8,\n        mask_stride=8,\n        num_stacked_convs=4,\n        norm_cfg=dict(type='BN', requires_grad=True))\n    return mask_feature_head\n\n\nclass TestCondInstHead(TestCase):\n\n    def test_condinst_bboxhead_loss(self):\n        \"\"\"Tests condinst bboxhead loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        condinst_bboxhead = CondInstBboxHead(\n            num_classes=4,\n            in_channels=1,\n            feat_channels=1,\n            stacked_convs=1,\n            norm_cfg=None)\n\n        # Fcos head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in condinst_bboxhead.prior_generator.strides)\n        cls_scores, bbox_preds, centernesses, param_preds =\\\n            condinst_bboxhead.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_instances.masks = _rand_masks(0, gt_instances.bboxes.numpy(), s, s)\n\n        empty_gt_losses = condinst_bboxhead.loss_by_feat(\n            cls_scores, bbox_preds, centernesses, param_preds, [gt_instances],\n            img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # box loss and centerness loss should be zero\n        empty_cls_loss = empty_gt_losses['loss_cls'].item()\n        empty_box_loss = empty_gt_losses['loss_bbox'].item()\n        empty_ctr_loss = empty_gt_losses['loss_centerness'].item()\n        self.assertGreater(empty_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss, 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_ctr_loss, 0,\n            'there should be no centerness loss when there are no true boxes')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        gt_instances.masks = _rand_masks(1, gt_instances.bboxes.numpy(), s, s)\n\n        one_gt_losses = condinst_bboxhead.loss_by_feat(cls_scores, bbox_preds,\n                                                       centernesses,\n                                                       param_preds,\n                                                       [gt_instances],\n                                                       img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls'].item()\n        onegt_box_loss = one_gt_losses['loss_bbox'].item()\n        onegt_ctr_loss = one_gt_losses['loss_centerness'].item()\n        self.assertGreater(onegt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(onegt_ctr_loss, 0,\n                           'centerness loss should be non-zero')\n\n        # Test the `center_sampling` works fine.\n        condinst_bboxhead.center_sampling = True\n        ctrsamp_losses = condinst_bboxhead.loss_by_feat(\n            cls_scores, bbox_preds, centernesses, param_preds, [gt_instances],\n            img_metas)\n        ctrsamp_cls_loss = ctrsamp_losses['loss_cls'].item()\n        ctrsamp_box_loss = ctrsamp_losses['loss_bbox'].item()\n        ctrsamp_ctr_loss = ctrsamp_losses['loss_centerness'].item()\n        self.assertGreater(ctrsamp_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(ctrsamp_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(ctrsamp_ctr_loss, 0,\n                           'centerness loss should be non-zero')\n\n        # Test the `norm_on_bbox` works fine.\n        condinst_bboxhead.norm_on_bbox = True\n        normbox_losses = condinst_bboxhead.loss_by_feat(\n            cls_scores, bbox_preds, centernesses, param_preds, [gt_instances],\n            img_metas)\n        normbox_cls_loss = normbox_losses['loss_cls'].item()\n        normbox_box_loss = normbox_losses['loss_bbox'].item()\n        normbox_ctr_loss = normbox_losses['loss_centerness'].item()\n        self.assertGreater(normbox_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(normbox_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(normbox_ctr_loss, 0,\n                           'centerness loss should be non-zero')\n\n    def test_condinst_maskhead_loss(self):\n        \"\"\"Tests condinst maskhead loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        condinst_bboxhead = CondInstBboxHead(\n            num_classes=4,\n            in_channels=1,\n            feat_channels=1,\n            stacked_convs=1,\n            norm_cfg=None)\n\n        mask_feature_head = _fake_mask_feature_head()\n        condinst_maskhead = CondInstMaskHead(\n            mask_feature_head=mask_feature_head,\n            loss_mask=dict(\n                type='DiceLoss',\n                use_sigmoid=True,\n                activate=True,\n                eps=5e-6,\n                loss_weight=1.0))\n\n        # Fcos head expects a multiple levels of features per image\n        feats = []\n        for i in range(len(condinst_bboxhead.strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 3)), s // (2**(i + 3))))\n        feats = tuple(feats)\n        cls_scores, bbox_preds, centernesses, param_preds =\\\n            condinst_bboxhead.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_instances.masks = _rand_masks(0, gt_instances.bboxes.numpy(), s, s)\n\n        _ = condinst_bboxhead.loss_by_feat(cls_scores, bbox_preds,\n                                           centernesses, param_preds,\n                                           [gt_instances], img_metas)\n        # When truth is empty then all mask loss\n        # should be zero for random inputs\n        positive_infos = condinst_bboxhead.get_positive_infos()\n        mask_outs = condinst_maskhead.forward(feats, positive_infos)\n        empty_gt_mask_losses = condinst_maskhead.loss_by_feat(\n            *mask_outs, [gt_instances], img_metas, positive_infos)\n        loss_mask = empty_gt_mask_losses['loss_mask']\n        self.assertEqual(loss_mask, 0, 'mask loss should be zero')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        gt_instances.masks = _rand_masks(1, gt_instances.bboxes.numpy(), s, s)\n\n        _ = condinst_bboxhead.loss_by_feat(cls_scores, bbox_preds,\n                                           centernesses, param_preds,\n                                           [gt_instances], img_metas)\n        positive_infos = condinst_bboxhead.get_positive_infos()\n        mask_outs = condinst_maskhead.forward(feats, positive_infos)\n        one_gt_mask_losses = condinst_maskhead.loss_by_feat(\n            *mask_outs, [gt_instances], img_metas, positive_infos)\n        loss_mask = one_gt_mask_losses['loss_mask']\n        self.assertGreater(loss_mask, 0, 'mask loss should be nonzero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_corner_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.evaluation import bbox_overlaps\nfrom mmdet.models.dense_heads import CornerHead\n\n\nclass TestCornerHead(TestCase):\n\n    def test_corner_head_loss(self):\n        \"\"\"Tests corner head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3)\n        }]\n\n        corner_head = CornerHead(num_classes=4, in_channels=1)\n\n        # Corner head expects a multiple levels of features per image\n        feat = [\n            torch.rand(1, 1, s // 4, s // 4)\n            for _ in range(corner_head.num_feat_levels)\n        ]\n        forward_outputs = corner_head.forward(feat)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_bboxes_ignore = None\n\n        empty_gt_losses = corner_head.loss_by_feat(*forward_outputs,\n                                                   [gt_instances], img_metas,\n                                                   gt_bboxes_ignore)\n        empty_det_loss = sum(empty_gt_losses['det_loss'])\n        empty_push_loss = sum(empty_gt_losses['push_loss'])\n        empty_pull_loss = sum(empty_gt_losses['pull_loss'])\n        empty_off_loss = sum(empty_gt_losses['off_loss'])\n        self.assertTrue(empty_det_loss.item() > 0,\n                        'det loss should be non-zero')\n        self.assertTrue(\n            empty_push_loss.item() == 0,\n            'there should be no push loss when there are no true boxes')\n        self.assertTrue(\n            empty_pull_loss.item() == 0,\n            'there should be no pull loss when there are no true boxes')\n        self.assertTrue(\n            empty_off_loss.item() == 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = corner_head.loss_by_feat(*forward_outputs,\n                                                 [gt_instances], img_metas,\n                                                 gt_bboxes_ignore)\n        onegt_det_loss = sum(one_gt_losses['det_loss'])\n        onegt_push_loss = sum(one_gt_losses['push_loss'])\n        onegt_pull_loss = sum(one_gt_losses['pull_loss'])\n        onegt_off_loss = sum(one_gt_losses['off_loss'])\n        self.assertTrue(onegt_det_loss.item() > 0,\n                        'det loss should be non-zero')\n        self.assertTrue(\n            onegt_push_loss.item() == 0,\n            'there should be no push loss when there are only one true box')\n        self.assertTrue(onegt_pull_loss.item() > 0,\n                        'pull loss should be non-zero')\n        self.assertTrue(onegt_off_loss.item() > 0,\n                        'off loss should be non-zero')\n\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874],\n             [123.6667, 123.8757, 138.6326, 251.8874]])\n        gt_instances.labels = torch.LongTensor([2, 3])\n\n        two_gt_losses = corner_head.loss_by_feat(*forward_outputs,\n                                                 [gt_instances], img_metas,\n                                                 gt_bboxes_ignore)\n        twogt_det_loss = sum(two_gt_losses['det_loss'])\n        twogt_push_loss = sum(two_gt_losses['push_loss'])\n        twogt_pull_loss = sum(two_gt_losses['pull_loss'])\n        twogt_off_loss = sum(two_gt_losses['off_loss'])\n        self.assertTrue(twogt_det_loss.item() > 0,\n                        'det loss should be non-zero')\n        # F.relu limits push loss larger than or equal to 0.\n        self.assertTrue(twogt_push_loss.item() >= 0,\n                        'push loss should be non-zero')\n        self.assertTrue(twogt_pull_loss.item() > 0,\n                        'pull loss should be non-zero')\n        self.assertTrue(twogt_off_loss.item() > 0,\n                        'off loss should be non-zero')\n\n    def test_corner_head_encode_and_decode_heatmap(self):\n        \"\"\"Tests corner head generating and decoding the heatmap.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3),\n            'border': (0, 0, 0, 0)\n        }]\n\n        gt_bboxes = [\n            torch.Tensor([[10, 20, 200, 240], [40, 50, 100, 200],\n                          [10, 20, 200, 240]])\n        ]\n        gt_labels = [torch.LongTensor([1, 1, 2])]\n\n        corner_head = CornerHead(\n            num_classes=4, in_channels=1, corner_emb_channels=1)\n\n        feat = [\n            torch.rand(1, 1, s // 4, s // 4)\n            for _ in range(corner_head.num_feat_levels)\n        ]\n\n        targets = corner_head.get_targets(\n            gt_bboxes,\n            gt_labels,\n            feat[0].shape,\n            img_metas[0]['batch_input_shape'],\n            with_corner_emb=corner_head.with_corner_emb)\n\n        gt_tl_heatmap = targets['topleft_heatmap']\n        gt_br_heatmap = targets['bottomright_heatmap']\n        gt_tl_offset = targets['topleft_offset']\n        gt_br_offset = targets['bottomright_offset']\n        embedding = targets['corner_embedding']\n        [top, left], [bottom, right] = embedding[0][0]\n        gt_tl_embedding_heatmap = torch.zeros([1, 1, s // 4, s // 4])\n        gt_br_embedding_heatmap = torch.zeros([1, 1, s // 4, s // 4])\n        gt_tl_embedding_heatmap[0, 0, top, left] = 1\n        gt_br_embedding_heatmap[0, 0, bottom, right] = 1\n\n        batch_bboxes, batch_scores, batch_clses = corner_head._decode_heatmap(\n            tl_heat=gt_tl_heatmap,\n            br_heat=gt_br_heatmap,\n            tl_off=gt_tl_offset,\n            br_off=gt_br_offset,\n            tl_emb=gt_tl_embedding_heatmap,\n            br_emb=gt_br_embedding_heatmap,\n            img_meta=img_metas[0],\n            k=100,\n            kernel=3,\n            distance_threshold=0.5)\n\n        bboxes = batch_bboxes.view(-1, 4)\n        scores = batch_scores.view(-1, 1)\n        clses = batch_clses.view(-1, 1)\n\n        idx = scores.argsort(dim=0, descending=True)\n        bboxes = bboxes[idx].view(-1, 4)\n        scores = scores[idx].view(-1)\n        clses = clses[idx].view(-1)\n\n        valid_bboxes = bboxes[torch.where(scores > 0.05)]\n        valid_labels = clses[torch.where(scores > 0.05)]\n        max_coordinate = valid_bboxes.max()\n        offsets = valid_labels.to(valid_bboxes) * (max_coordinate + 1)\n        gt_offsets = gt_labels[0].to(gt_bboxes[0]) * (max_coordinate + 1)\n\n        offset_bboxes = valid_bboxes + offsets[:, None]\n        offset_gtbboxes = gt_bboxes[0] + gt_offsets[:, None]\n\n        iou_matrix = bbox_overlaps(offset_bboxes.numpy(),\n                                   offset_gtbboxes.numpy())\n        self.assertEqual((iou_matrix == 1).sum(), 3)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_ddod_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import DDODHead\n\n\nclass TestDDODHead(TestCase):\n\n    def test_ddod_head_loss(self):\n        \"\"\"Tests ddod head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1\n        }]\n        cfg = Config(\n            dict(\n                assigner=dict(type='ATSSAssigner', topk=9, alpha=0.8),\n                reg_assigner=dict(type='ATSSAssigner', topk=9, alpha=0.5),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        atss_head = DDODHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            feat_channels=1,\n            use_dcn=False,\n            norm_cfg=None,\n            train_cfg=cfg,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8, 16, 32, 64, 128]),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n            loss_iou=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0))\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [8, 16, 32, 64, 128]\n        ]\n        cls_scores, bbox_preds, centernesses = atss_head.forward(feat)\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = atss_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 centernesses, [gt_instances],\n                                                 img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        empty_centerness_loss = sum(empty_gt_losses['loss_iou'])\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_centerness_loss.item(), 0,\n            'there should be no centerness loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = atss_head.loss_by_feat(cls_scores, bbox_preds,\n                                               centernesses, [gt_instances],\n                                               img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        onegt_centerness_loss = sum(one_gt_losses['loss_iou'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_centerness_loss.item(), 0,\n                           'centerness loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_ddq_detr_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import DDQDETRHead\nfrom mmdet.structures import DetDataSample\n\n\nclass TestDDQDETRHead(TestCase):\n\n    def test_ddq_detr_head_loss(self):\n        \"\"\"Tests DDQDETRHead loss when truth is empty and non-empty.\"\"\"\n        num_classes = 2\n        num_dn_queries = 10\n        num_distinct_queries = 10\n        dense_topk_ratio = 1.0\n        num_decoder_layers = 2\n        embed_dims = 256\n        num_attention_heads = 8\n\n        batch_data_samples = self.get_batch_data_samples()\n        batch_size = len(batch_data_samples)\n\n        batch_gt_instances = []\n        batch_img_metas = []\n\n        for data_sample in batch_data_samples:\n            batch_img_metas.append(data_sample.metainfo)\n            batch_gt_instances.append(data_sample.gt_instances)\n\n        max_num_target = max(\n            len(data_sample.gt_instances)\n            for data_sample in batch_data_samples)\n\n        num_denoising_groups = max(num_dn_queries // max_num_target, 1)\n        num_denoising_queries = num_denoising_groups * 2 * max_num_target\n\n        num_dense_queries = int(num_distinct_queries * dense_topk_ratio)\n\n        num_total_queries = (\n            num_denoising_queries + num_distinct_queries + num_dense_queries)\n\n        dn_meta = {\n            'num_denoising_queries': num_denoising_queries,\n            'num_denoising_groups': num_denoising_groups,\n            'num_dense_queries': num_dense_queries\n        }\n\n        train_cfg = Config(\n            dict(\n                assigner=dict(\n                    type='HungarianAssigner',\n                    match_costs=[\n                        dict(type='FocalLossCost', weight=2.0),\n                        dict(type='BBoxL1Cost', weight=5.0, box_format='xywh'),\n                        dict(type='IoUCost', iou_mode='giou', weight=2.0)\n                    ])))\n\n        bbox_head = DDQDETRHead(\n            num_classes=num_classes,\n            sync_cls_avg_factor=True,\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0),\n            loss_bbox=dict(type='L1Loss', loss_weight=5.0),\n            loss_iou=dict(type='GIoULoss', loss_weight=2.0),\n            share_pred_layer=False,\n            num_pred_layer=1 + num_decoder_layers,\n            as_two_stage=True,\n            train_cfg=train_cfg,\n            test_cfg=dict(max_per_img=300))\n\n        bbox_head.cache_dict = dict(\n            distinct_query_mask=list(\n                torch.rand([\n                    num_decoder_layers - 1, batch_size * num_attention_heads,\n                    num_distinct_queries, num_distinct_queries\n                ]) < 0.5),\n            num_heads=num_attention_heads,\n            num_dense_queries=num_dense_queries)\n\n        # query\n        hidden_states = torch.randn(\n            [num_decoder_layers, batch_size, num_total_queries, embed_dims])\n        # normalized cx, cy, w, h\n        references = list(\n            torch.rand(\n                [1 + num_decoder_layers, batch_size, num_total_queries, 4]))\n        all_layers_outputs_classes, all_layers_outputs_coords = \\\n            bbox_head.forward(hidden_states, references)\n\n        # logits\n        enc_outputs_class = torch.randn(\n            [batch_size, num_distinct_queries, num_classes])\n\n        # normalized cx, cy, w, h\n        enc_outputs_coord = torch.rand([batch_size, num_distinct_queries, 4])\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        empty_batch_gt_instances = []\n\n        for _ in range(batch_size):\n            gt_instances = InstanceData()\n            gt_instances.labels = torch.LongTensor([])\n            gt_instances.bboxes = torch.empty((0, 4))\n            empty_batch_gt_instances.append(gt_instances)\n\n        empty_gt_losses = bbox_head.loss_by_feat(all_layers_outputs_classes,\n                                                 all_layers_outputs_coords,\n                                                 enc_outputs_class,\n                                                 enc_outputs_coord,\n                                                 empty_batch_gt_instances,\n                                                 batch_img_metas, dn_meta)\n\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        losses = bbox_head.loss_by_feat(all_layers_outputs_classes,\n                                        all_layers_outputs_coords,\n                                        enc_outputs_class, enc_outputs_coord,\n                                        batch_gt_instances, batch_img_metas,\n                                        dn_meta)\n\n        cls_loss = losses['loss_cls']\n        box_loss = losses['loss_bbox']\n        self.assertGreater(cls_loss.item(), 0, 'cls loss should be non-zero')\n        self.assertGreater(box_loss.item(), 0, 'box loss should be non-zero')\n\n    def get_batch_data_samples(self):\n        \"\"\"Generate batch data samples including model inputs and gt labels.\"\"\"\n        data_sample_file_path = 'tests/data/coco_batched_sample.json'\n\n        with open(data_sample_file_path, 'r') as file_stream:\n            data_sample_infos = json.load(file_stream)\n\n        batch_data_samples = []\n\n        for data_sample_info in data_sample_infos:\n            data_sample = DetDataSample()\n\n            metainfo = data_sample_info['metainfo']\n            labels = data_sample_info['labels']\n            bboxes = data_sample_info['bboxes']\n\n            data_sample.set_metainfo(metainfo)\n\n            gt_instances = InstanceData()\n            gt_instances.labels = torch.LongTensor(labels)\n            gt_instances.bboxes = torch.Tensor(bboxes)\n            data_sample.gt_instances = gt_instances\n\n            batch_data_samples.append(data_sample)\n\n        return batch_data_samples\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_embedding_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport pytest\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import EmbeddingRPNHead\nfrom mmdet.structures import DetDataSample\n\n\nclass TestEmbeddingRPNHead(TestCase):\n\n    def test_init(self):\n        \"\"\"Test init rpn head.\"\"\"\n        rpn_head = EmbeddingRPNHead(\n            num_proposals=100, proposal_feature_channel=256)\n        rpn_head.init_weights()\n        self.assertTrue(rpn_head.init_proposal_bboxes)\n        self.assertTrue(rpn_head.init_proposal_features)\n\n    def test_loss_and_predict(self):\n        s = 256\n        img_meta = {\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }\n        rpn_head = EmbeddingRPNHead(\n            num_proposals=100, proposal_feature_channel=256)\n\n        feats = [\n            torch.rand(2, 1, s // (2**(i + 2)), s // (2**(i + 2)))\n            for i in range(5)\n        ]\n\n        data_sample = DetDataSample()\n        data_sample.set_metainfo(img_meta)\n\n        # test predict\n        result_list = rpn_head.predict(feats, [data_sample])\n        self.assertTrue(isinstance(result_list, list))\n        self.assertTrue(isinstance(result_list[0], InstanceData))\n\n        # test loss_and_predict\n        result_list = rpn_head.loss_and_predict(feats, [data_sample])\n        self.assertTrue(isinstance(result_list, tuple))\n        self.assertTrue(isinstance(result_list[0], dict))\n        self.assertEqual(len(result_list[0]), 0)\n        self.assertTrue(isinstance(result_list[1], list))\n        self.assertTrue(isinstance(result_list[1][0], InstanceData))\n\n        # test loss\n        with pytest.raises(NotImplementedError):\n            rpn_head.loss(feats, [data_sample])\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_fcos_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import FCOSHead\n\n\nclass TestFCOSHead(TestCase):\n\n    def test_fcos_head_loss(self):\n        \"\"\"Tests fcos head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        fcos_head = FCOSHead(\n            num_classes=4,\n            in_channels=1,\n            feat_channels=1,\n            stacked_convs=1,\n            norm_cfg=None)\n\n        # Fcos head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in fcos_head.prior_generator.strides)\n        cls_scores, bbox_preds, centernesses = fcos_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = fcos_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 centernesses, [gt_instances],\n                                                 img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # box loss and centerness loss should be zero\n        empty_cls_loss = empty_gt_losses['loss_cls'].item()\n        empty_box_loss = empty_gt_losses['loss_bbox'].item()\n        empty_ctr_loss = empty_gt_losses['loss_centerness'].item()\n        self.assertGreater(empty_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss, 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_ctr_loss, 0,\n            'there should be no centerness loss when there are no true boxes')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = fcos_head.loss_by_feat(cls_scores, bbox_preds,\n                                               centernesses, [gt_instances],\n                                               img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls'].item()\n        onegt_box_loss = one_gt_losses['loss_bbox'].item()\n        onegt_ctr_loss = one_gt_losses['loss_centerness'].item()\n        self.assertGreater(onegt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(onegt_ctr_loss, 0,\n                           'centerness loss should be non-zero')\n\n        # Test the `center_sampling` works fine.\n        fcos_head.center_sampling = True\n        ctrsamp_losses = fcos_head.loss_by_feat(cls_scores, bbox_preds,\n                                                centernesses, [gt_instances],\n                                                img_metas)\n        ctrsamp_cls_loss = ctrsamp_losses['loss_cls'].item()\n        ctrsamp_box_loss = ctrsamp_losses['loss_bbox'].item()\n        ctrsamp_ctr_loss = ctrsamp_losses['loss_centerness'].item()\n        self.assertGreater(ctrsamp_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(ctrsamp_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(ctrsamp_ctr_loss, 0,\n                           'centerness loss should be non-zero')\n\n        # Test the `norm_on_bbox` works fine.\n        fcos_head.norm_on_bbox = True\n        normbox_losses = fcos_head.loss_by_feat(cls_scores, bbox_preds,\n                                                centernesses, [gt_instances],\n                                                img_metas)\n        normbox_cls_loss = normbox_losses['loss_cls'].item()\n        normbox_box_loss = normbox_losses['loss_bbox'].item()\n        normbox_ctr_loss = normbox_losses['loss_centerness'].item()\n        self.assertGreater(normbox_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(normbox_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(normbox_ctr_loss, 0,\n                           'centerness loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_fovea_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import FoveaHead\n\n\nclass TestFOVEAHead(TestCase):\n\n    def test_fovea_head_loss(self):\n        \"\"\"Tests anchor head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        fovea_head = FoveaHead(num_classes=4, in_channels=1)\n\n        # Anchor head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // (2**(i + 2)), s // (2**(i + 2)))\n            for i in range(len(fovea_head.prior_generator.strides)))\n        cls_scores, bbox_preds = fovea_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = fovea_head.loss_by_feat(cls_scores, bbox_preds,\n                                                  [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = fovea_head.loss_by_feat(cls_scores, bbox_preds,\n                                                [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_free_anchor_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import FreeAnchorRetinaHead\n\n\nclass TestFreeAnchorRetinaHead(TestCase):\n\n    def test_free_anchor_head_loss(self):\n        \"\"\"Tests rpn head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        anchor_head = FreeAnchorRetinaHead(num_classes=1, in_channels=1)\n\n        # Anchor head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // (2**(i + 2)), s // (2**(i + 2)))\n            for i in range(len(anchor_head.prior_generator.strides)))\n        cls_scores, bbox_preds = anchor_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = anchor_head.loss_by_feat(cls_scores, bbox_preds,\n                                                   [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box loss.\n        positive_bag_loss = empty_gt_losses['positive_bag_loss']\n        negative_bag_loss = empty_gt_losses['negative_bag_loss']\n        self.assertGreater(negative_bag_loss.item(), 0,\n                           'negative_bag loss should be non-zero')\n        self.assertEqual(\n            positive_bag_loss.item(), 0,\n            'there should be no positive_bag loss when there are no true boxes'\n        )\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([0])\n\n        one_gt_losses = anchor_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['positive_bag_loss']\n        onegt_box_loss = one_gt_losses['negative_bag_loss']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'positive bag loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'negative bag loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_fsaf_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom math import ceil\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import FSAFHead\n\n\nclass TestFSAFHead(TestCase):\n\n    def test_fsaf_head_loss(self):\n        \"\"\"Tests fsaf head loss when truth is empty and non-empty.\"\"\"\n        s = 300\n        img_metas = [{\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': 1,\n        }]\n        cfg = Config(\n            dict(\n                assigner=dict(\n                    type='CenterRegionAssigner',\n                    pos_scale=0.2,\n                    neg_scale=0.2,\n                    min_pos_iof=0.01),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        fsaf_head = FSAFHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            feat_channels=1,\n            reg_decoded_bbox=True,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                octave_base_scale=1,\n                scales_per_octave=1,\n                ratios=[1.0],\n                strides=[8, 16, 32, 64, 128]),\n            bbox_coder=dict(type='TBLRBBoxCoder', normalizer=4.0),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0,\n                reduction='none'),\n            loss_bbox=dict(\n                type='IoULoss', eps=1e-6, loss_weight=1.0, reduction='none'),\n            train_cfg=cfg)\n\n        # FSAF head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, ceil(s / stride[0]), ceil(s / stride[0]))\n            for stride in fsaf_head.prior_generator.strides)\n        cls_scores, bbox_preds = fsaf_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = fsaf_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # box loss should be zero\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        self.assertGreater(empty_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = fsaf_head.loss_by_feat(cls_scores, bbox_preds,\n                                               [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_ga_retina_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\n\nfrom mmdet.models.dense_heads import GARetinaHead\n\nga_retina_head_config = ConfigDict(\n    dict(\n        num_classes=4,\n        in_channels=4,\n        feat_channels=4,\n        stacked_convs=1,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.04, loss_weight=1.0),\n        train_cfg=dict(\n            ga_assigner=dict(\n                type='ApproxMaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.4,\n                min_pos_iou=0.4,\n                ignore_iof_thr=-1),\n            ga_sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.0,\n                ignore_iof_thr=-1),\n            allowed_border=-1,\n            pos_weight=-1,\n            center_ratio=0.2,\n            ignore_ratio=0.5,\n            debug=False),\n        test_cfg=dict(\n            nms_pre=1000,\n            min_bbox_size=0,\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)))\n\n\nclass TestGARetinaHead(TestCase):\n\n    def test_ga_retina_head_init_and_forward(self):\n        \"\"\"The GARetinaHead inherit loss and prediction function from\n        GuidedAchorHead.\n\n        Here, we only test GARetinaHet initialization and forward.\n        \"\"\"\n        # Test initializaion\n        ga_retina_head = GARetinaHead(**ga_retina_head_config)\n\n        # Test forward\n        s = 256\n        feats = (\n            torch.rand(1, 4, s // stride[1], s // stride[0])\n            for stride in ga_retina_head.square_anchor_generator.strides)\n        ga_retina_head(feats)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_ga_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import GARPNHead\n\nga_rpn_config = ConfigDict(\n    dict(\n        num_classes=1,\n        in_channels=4,\n        feat_channels=4,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=8,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[4, 8, 16, 32, 64]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[8],\n            strides=[4, 8, 16, 32, 64]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.14, 0.14]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.07, 0.07, 0.11, 0.11]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0),\n        train_cfg=dict(\n            ga_assigner=dict(\n                type='ApproxMaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                ignore_iof_thr=-1),\n            ga_sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.7,\n                neg_iou_thr=0.3,\n                min_pos_iou=0.3,\n                match_low_quality=True,\n                ignore_iof_thr=-1),\n            sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            allowed_border=-1,\n            center_ratio=0.2,\n            ignore_ratio=0.5,\n            pos_weight=-1,\n            debug=False),\n        test_cfg=dict(\n            nms_pre=1000,\n            ms_post=1000,\n            max_per_img=300,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)))\n\n\nclass TestGARPNHead(TestCase):\n\n    def test_ga_rpn_head_loss(self):\n        \"\"\"Tests ga rpn head loss.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': (1, 1)\n        }]\n        ga_rpn_head = GARPNHead(**ga_rpn_config)\n\n        feats = (\n            torch.rand(1, 4, s // stride[1], s // stride[0])\n            for stride in ga_rpn_head.square_anchor_generator.strides)\n        outs = ga_rpn_head(feats)\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([0])\n\n        one_gt_losses = ga_rpn_head.loss_by_feat(*outs, [gt_instances],\n                                                 img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_rpn_cls']).item()\n        onegt_box_loss = sum(one_gt_losses['loss_rpn_bbox']).item()\n        onegt_shape_loss = sum(one_gt_losses['loss_anchor_shape']).item()\n        onegt_loc_loss = sum(one_gt_losses['loss_anchor_loc']).item()\n        self.assertGreater(onegt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(onegt_shape_loss, 0,\n                           'shape loss should be non-zero')\n        self.assertGreater(onegt_loc_loss, 0,\n                           'location loss should be non-zero')\n\n    def test_ga_rpn_head_predict_by_feat(self):\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': (1, 1)\n        }]\n        ga_rpn_head = GARPNHead(**ga_rpn_config)\n\n        feats = (\n            torch.rand(1, 4, s // stride[1], s // stride[0])\n            for stride in ga_rpn_head.square_anchor_generator.strides)\n        outs = ga_rpn_head(feats)\n\n        cfg = ConfigDict(\n            dict(\n                nms_pre=2000,\n                nms_post=1000,\n                max_per_img=300,\n                nms=dict(type='nms', iou_threshold=0.7),\n                min_bbox_size=0))\n        ga_rpn_head.predict_by_feat(\n            *outs, batch_img_metas=img_metas, cfg=cfg, rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_gfl_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import GFLHead\n\n\nclass TestGFLHead(TestCase):\n\n    def test_gfl_head_loss(self):\n        \"\"\"Tests gfl head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(type='ATSSAssigner', topk=9),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        gfl_head = GFLHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            train_cfg=train_cfg,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8, 16, 32, 64, 128]),\n            loss_cls=dict(\n                type='QualityFocalLoss',\n                use_sigmoid=True,\n                beta=2.0,\n                loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=2.0))\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16, 32, 64]\n        ]\n        cls_scores, bbox_preds = gfl_head.forward(feat)\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = gfl_head.loss_by_feat(cls_scores, bbox_preds,\n                                                [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        empty_dfl_loss = sum(empty_gt_losses['loss_dfl'])\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_dfl_loss.item(), 0,\n            'there should be no dfl loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = gfl_head.loss_by_feat(cls_scores, bbox_preds,\n                                              [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        onegt_dfl_loss = sum(one_gt_losses['loss_dfl'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_dfl_loss.item(), 0,\n                           'dfl loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_guided_anchor_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import GuidedAnchorHead\n\nguided_anchor_head_config = ConfigDict(\n    dict(\n        num_classes=4,\n        in_channels=4,\n        feat_channels=4,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]),\n        anchor_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]),\n        loc_filter_thr=0.01,\n        loss_loc=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_shape=dict(type='BoundedIoULoss', beta=0.2, loss_weight=1.0),\n        loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_bbox=dict(type='SmoothL1Loss', beta=0.04, loss_weight=1.0),\n        train_cfg=dict(\n            ga_assigner=dict(\n                type='ApproxMaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.4,\n                min_pos_iou=0.4,\n                ignore_iof_thr=-1),\n            ga_sampler=dict(\n                type='RandomSampler',\n                num=256,\n                pos_fraction=0.5,\n                neg_pos_ub=-1,\n                add_gt_as_proposals=False),\n            assigner=dict(\n                type='MaxIoUAssigner',\n                pos_iou_thr=0.5,\n                neg_iou_thr=0.5,\n                min_pos_iou=0.0,\n                ignore_iof_thr=-1),\n            allowed_border=-1,\n            pos_weight=-1,\n            center_ratio=0.2,\n            ignore_ratio=0.5,\n            debug=False),\n        test_cfg=dict(\n            nms_pre=1000,\n            min_bbox_size=0,\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)))\n\n\nclass TestGuidedAnchorHead(TestCase):\n\n    def test_guided_anchor_head_loss(self):\n        \"\"\"Tests guided anchor loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': (1, 1)\n        }]\n        guided_anchor_head = GuidedAnchorHead(**guided_anchor_head_config)\n\n        feats = (\n            torch.rand(1, 4, s // stride[1], s // stride[0])\n            for stride in guided_anchor_head.square_anchor_generator.strides)\n        outs = guided_anchor_head(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = guided_anchor_head.loss_by_feat(\n            *outs, [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # box shape and location loss should be zero\n        empty_cls_loss = sum(empty_gt_losses['loss_cls']).item()\n        empty_box_loss = sum(empty_gt_losses['loss_bbox']).item()\n        empty_shape_loss = sum(empty_gt_losses['loss_shape']).item()\n        empty_loc_loss = sum(empty_gt_losses['loss_loc']).item()\n        self.assertGreater(empty_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(empty_loc_loss, 0,\n                           'location loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss, 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_shape_loss, 0,\n            'there should be no shape loss when there are no true boxes')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = guided_anchor_head.loss_by_feat(\n            *outs, [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls']).item()\n        onegt_box_loss = sum(one_gt_losses['loss_bbox']).item()\n        onegt_shape_loss = sum(one_gt_losses['loss_shape']).item()\n        onegt_loc_loss = sum(one_gt_losses['loss_loc']).item()\n        self.assertGreater(onegt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(onegt_shape_loss, 0,\n                           'shape loss should be non-zero')\n        self.assertGreater(onegt_loc_loss, 0,\n                           'location loss should be non-zero')\n\n    def test_guided_anchor_head_predict_by_feat(self):\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': (1, 1)\n        }]\n        guided_anchor_head = GuidedAnchorHead(**guided_anchor_head_config)\n\n        feats = (\n            torch.rand(1, 4, s // stride[1], s // stride[0])\n            for stride in guided_anchor_head.square_anchor_generator.strides)\n        outs = guided_anchor_head(feats)\n\n        guided_anchor_head.predict_by_feat(\n            *outs, batch_img_metas=img_metas, rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_lad_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import LADHead, lad_head\nfrom mmdet.models.dense_heads.lad_head import levels_to_images\n\n\nclass TestLADHead(TestCase):\n\n    def test_lad_head_loss(self):\n        \"\"\"Tests lad head loss when truth is empty and non-empty.\"\"\"\n\n        class mock_skm:\n\n            def GaussianMixture(self, *args, **kwargs):\n                return self\n\n            def fit(self, loss):\n                pass\n\n            def predict(self, loss):\n                components = np.zeros_like(loss, dtype=np.long)\n                return components.reshape(-1)\n\n            def score_samples(self, loss):\n                scores = np.random.random(len(loss))\n                return scores\n\n        lad_head.skm = mock_skm()\n\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.1,\n                    neg_iou_thr=0.1,\n                    min_pos_iou=0,\n                    ignore_iof_thr=-1),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        # since Focal Loss is not supported on CPU\n        # since Focal Loss is not supported on CPU\n        lad = LADHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n            loss_centerness=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))\n        teacher_model = LADHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n            loss_centerness=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16, 32, 64]\n        ]\n        lad.init_weights()\n        teacher_model.init_weights()\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        batch_gt_instances_ignore = None\n\n        outs_teacher = teacher_model(feat)\n        label_assignment_results = teacher_model.get_label_assignment(\n            *outs_teacher, [gt_instances], img_metas,\n            batch_gt_instances_ignore)\n\n        outs = teacher_model(feat)\n        empty_gt_losses = lad.loss_by_feat(*outs, [gt_instances], img_metas,\n                                           batch_gt_instances_ignore,\n                                           label_assignment_results)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        empty_iou_loss = empty_gt_losses['loss_iou']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_iou_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        batch_gt_instances_ignore = None\n\n        label_assignment_results = teacher_model.get_label_assignment(\n            *outs_teacher, [gt_instances], img_metas,\n            batch_gt_instances_ignore)\n        one_gt_losses = lad.loss_by_feat(*outs, [gt_instances], img_metas,\n                                         batch_gt_instances_ignore,\n                                         label_assignment_results)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        onegt_iou_loss = one_gt_losses['loss_iou']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_iou_loss.item(), 0,\n                           'box loss should be non-zero')\n        n, c, h, w = 10, 4, 20, 20\n        mlvl_tensor = [torch.ones(n, c, h, w) for i in range(5)]\n        results = levels_to_images(mlvl_tensor)\n        self.assertEqual(len(results), n)\n        self.assertEqual(results[0].size(), (h * w * 5, c))\n        self.assertTrue(lad.with_score_voting)\n\n        lad = LADHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8]),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n            loss_centerness=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))\n        cls_scores = [torch.ones(2, 4, 5, 5)]\n        bbox_preds = [torch.ones(2, 4, 5, 5)]\n        iou_preds = [torch.ones(2, 1, 5, 5)]\n        cfg = Config(\n            dict(\n                nms_pre=1000,\n                min_bbox_size=0,\n                score_thr=0.05,\n                nms=dict(type='nms', iou_threshold=0.6),\n                max_per_img=100))\n        rescale = False\n        lad.predict_by_feat(\n            cls_scores, bbox_preds, iou_preds, img_metas, cfg, rescale=rescale)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_ld_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import GFLHead, LDHead\n\n\nclass TestLDHead(TestCase):\n\n    def test_ld_head_loss(self):\n        \"\"\"Tests ld head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(type='ATSSAssigner', topk=9, ignore_iof_thr=0.1),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n\n        ld_head = LDHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            loss_ld=dict(\n                type='KnowledgeDistillationKLDivLoss', loss_weight=1.0),\n            loss_cls=dict(\n                type='QualityFocalLoss',\n                use_sigmoid=True,\n                beta=2.0,\n                loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8, 16, 32, 64, 128]))\n\n        teacher_model = GFLHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            loss_cls=dict(\n                type='QualityFocalLoss',\n                use_sigmoid=True,\n                beta=2.0,\n                loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8, 16, 32, 64, 128]))\n\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16, 32, 64]\n        ]\n        cls_scores, bbox_preds = ld_head.forward(feat)\n        rand_soft_target = teacher_model.forward(feat)[1]\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        batch_gt_instances_ignore = None\n\n        empty_gt_losses = ld_head.loss_by_feat(cls_scores, bbox_preds,\n                                               [gt_instances], img_metas,\n                                               rand_soft_target,\n                                               batch_gt_instances_ignore)\n\n        # When there is no truth, the cls loss should be nonzero, ld loss\n        # should be non-negative but there should be no box loss.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        empty_ld_loss = sum(empty_gt_losses['loss_ld'])\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertGreaterEqual(empty_ld_loss.item(), 0,\n                                'ld loss should be non-negative')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        batch_gt_instances_ignore = None\n\n        one_gt_losses = ld_head.loss_by_feat(cls_scores, bbox_preds,\n                                             [gt_instances], img_metas,\n                                             rand_soft_target,\n                                             batch_gt_instances_ignore)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n\n        batch_gt_instances_ignore = gt_instances\n\n        # When truth is non-empty but ignored then the cls loss should be\n        # nonzero, but there should be no box loss.\n        ignore_gt_losses = ld_head.loss_by_feat(cls_scores, bbox_preds,\n                                                [gt_instances], img_metas,\n                                                rand_soft_target,\n                                                batch_gt_instances_ignore)\n        ignore_cls_loss = sum(ignore_gt_losses['loss_cls'])\n        ignore_box_loss = sum(ignore_gt_losses['loss_bbox'])\n\n        self.assertGreater(ignore_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(ignore_box_loss.item(), 0,\n                         'gt bbox ignored loss should be zero')\n\n        # When truth is non-empty and not ignored then both cls and box loss\n        # should be nonzero for random inputs\n        batch_gt_instances_ignore = InstanceData()\n        batch_gt_instances_ignore.bboxes = torch.randn(1, 4)\n\n        not_ignore_gt_losses = ld_head.loss_by_feat(cls_scores, bbox_preds,\n                                                    [gt_instances], img_metas,\n                                                    rand_soft_target,\n                                                    batch_gt_instances_ignore)\n        not_ignore_cls_loss = sum(not_ignore_gt_losses['loss_cls'])\n        not_ignore_box_loss = sum(not_ignore_gt_losses['loss_bbox'])\n\n        self.assertGreater(not_ignore_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreaterEqual(not_ignore_box_loss.item(), 0,\n                                'gt bbox not ignored loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_nasfcos_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import NASFCOSHead\n\n\nclass TestNASFCOSHead(TestCase):\n\n    def test_nasfcos_head_loss(self):\n        \"\"\"Tests nasfcos head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        nasfcos_head = NASFCOSHead(\n            num_classes=4,\n            in_channels=2,  # the same as `deform_groups` in dconv3x3_config\n            feat_channels=2,\n            norm_cfg=None)\n\n        # Nasfcos head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 2, s // stride[1], s // stride[0]).float()\n            for stride in nasfcos_head.prior_generator.strides)\n        cls_scores, bbox_preds, centernesses = nasfcos_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = nasfcos_head.loss_by_feat(cls_scores, bbox_preds,\n                                                    centernesses,\n                                                    [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # box loss and centerness loss should be zero\n        empty_cls_loss = empty_gt_losses['loss_cls'].item()\n        empty_box_loss = empty_gt_losses['loss_bbox'].item()\n        empty_ctr_loss = empty_gt_losses['loss_centerness'].item()\n        self.assertGreater(empty_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss, 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_ctr_loss, 0,\n            'there should be no centerness loss when there are no true boxes')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = nasfcos_head.loss_by_feat(cls_scores, bbox_preds,\n                                                  centernesses, [gt_instances],\n                                                  img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls'].item()\n        onegt_box_loss = one_gt_losses['loss_bbox'].item()\n        onegt_ctr_loss = one_gt_losses['loss_centerness'].item()\n        self.assertGreater(onegt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss, 0, 'box loss should be non-zero')\n        self.assertGreater(onegt_ctr_loss, 0,\n                           'centerness loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_paa_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import PAAHead, paa_head\nfrom mmdet.models.utils import levels_to_images\n\n\nclass TestPAAHead(TestCase):\n\n    def test_paa_head_loss(self):\n        \"\"\"Tests paa head loss when truth is empty and non-empty.\"\"\"\n\n        class mock_skm:\n\n            def GaussianMixture(self, *args, **kwargs):\n                return self\n\n            def fit(self, loss):\n                pass\n\n            def predict(self, loss):\n                components = np.zeros_like(loss, dtype=np.long)\n                return components.reshape(-1)\n\n            def score_samples(self, loss):\n                scores = np.random.random(len(loss))\n                return scores\n\n        paa_head.skm = mock_skm()\n\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.1,\n                    neg_iou_thr=0.1,\n                    min_pos_iou=0,\n                    ignore_iof_thr=-1),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        # since Focal Loss is not supported on CPU\n        paa = PAAHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8, 16, 32, 64, 128]),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n            loss_centerness=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16, 32, 64]\n        ]\n        paa.init_weights()\n        cls_scores, bbox_preds, iou_preds = paa(feat)\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        empty_gt_losses = paa.loss_by_feat(cls_scores, bbox_preds, iou_preds,\n                                           [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        empty_iou_loss = empty_gt_losses['loss_iou']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_iou_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = paa.loss_by_feat(cls_scores, bbox_preds, iou_preds,\n                                         [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        onegt_iou_loss = one_gt_losses['loss_iou']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_iou_loss.item(), 0,\n                           'box loss should be non-zero')\n        n, c, h, w = 10, 4, 20, 20\n        mlvl_tensor = [torch.ones(n, c, h, w) for i in range(5)]\n        results = levels_to_images(mlvl_tensor)\n        self.assertEqual(len(results), n)\n        self.assertEqual(results[0].size(), (h * w * 5, c))\n        self.assertTrue(paa.with_score_voting)\n\n        paa = PAAHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                octave_base_scale=8,\n                scales_per_octave=1,\n                strides=[8]),\n            loss_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=1.3),\n            loss_centerness=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))\n        cls_scores = [torch.ones(2, 4, 5, 5)]\n        bbox_preds = [torch.ones(2, 4, 5, 5)]\n        iou_preds = [torch.ones(2, 1, 5, 5)]\n        cfg = Config(\n            dict(\n                nms_pre=1000,\n                min_bbox_size=0,\n                score_thr=0.05,\n                nms=dict(type='nms', iou_threshold=0.6),\n                max_per_img=100))\n        rescale = False\n        paa.predict_by_feat(\n            cls_scores, bbox_preds, iou_preds, img_metas, cfg, rescale=rescale)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_pisa_retinanet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom math import ceil\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import PISARetinaHead\n\n\nclass TestPISARetinaHead(TestCase):\n\n    def test_pisa_reitnanet_head_loss(self):\n        \"\"\"Tests pisa retinanet head loss when truth is empty and non-empty.\"\"\"\n        s = 300\n        img_metas = [{\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': 1,\n        }]\n        cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.4,\n                    min_pos_iou=0,\n                    ignore_iof_thr=-1),\n                isr=dict(k=2., bias=0.),\n                carl=dict(k=1., bias=0.2),\n                sampler=dict(type='PseudoSampler'),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        pisa_retinanet_head = PISARetinaHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            feat_channels=256,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                octave_base_scale=4,\n                scales_per_octave=3,\n                ratios=[0.5, 1.0, 2.0],\n                strides=[8, 16, 32, 64, 128]),\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[.0, .0, .0, .0],\n                target_stds=[1.0, 1.0, 1.0, 1.0]),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0),\n            loss_bbox=dict(type='SmoothL1Loss', beta=0.11, loss_weight=1.0),\n            train_cfg=cfg)\n\n        # pisa retina head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, ceil(s / stride[0]), ceil(s / stride[0]))\n            for stride in pisa_retinanet_head.prior_generator.strides)\n        cls_scores, bbox_preds = pisa_retinanet_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = pisa_retinanet_head.loss_by_feat(\n            cls_scores, bbox_preds, [gt_instances], img_metas)\n        # When there is no truth, cls_loss and box_loss should all be zero.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        empty_carl_loss = empty_gt_losses['loss_carl']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_carl_loss.item(), 0,\n            'there should be no carl loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = pisa_retinanet_head.loss_by_feat(\n            cls_scores, bbox_preds, [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        onegt_carl_loss = one_gt_losses['loss_carl']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_carl_loss.item(), 0,\n                           'carl loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_pisa_ssd_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom math import ceil\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import PISASSDHead\n\n\nclass TestPISASSDHead(TestCase):\n\n    def test_pisa_ssd_head_loss(self):\n        \"\"\"Tests pisa ssd head loss when truth is empty and non-empty.\"\"\"\n        s = 300\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.,\n                    ignore_iof_thr=-1,\n                    gt_max_assign_all=False),\n                sampler=dict(type='PseudoSampler'),\n                smoothl1_beta=1.,\n                allowed_border=-1,\n                pos_weight=-1,\n                neg_pos_ratio=3,\n                debug=False))\n        pisa_ssd_head = PISASSDHead(\n            num_classes=4,\n            in_channels=(1, 1, 1, 1, 1, 1),\n            anchor_generator=dict(\n                type='SSDAnchorGenerator',\n                scale_major=False,\n                input_size=s,\n                basesize_ratio_range=(0.15, 0.9),\n                strides=[8, 16, 32, 64, 100, 300],\n                ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]]),\n            train_cfg=cfg)\n\n        # PISA SSD head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, ceil(s / stride[0]), ceil(s / stride[0]))\n            for stride in pisa_ssd_head.prior_generator.strides)\n        cls_scores, bbox_preds = pisa_ssd_head.forward(feats)\n\n        # test without isr and carl\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = pisa_ssd_head.loss_by_feat(cls_scores, bbox_preds,\n                                                     [gt_instances], img_metas)\n        # When there is no truth, cls_loss and box_loss should all be zero.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        self.assertEqual(\n            empty_cls_loss.item(), 0,\n            'there should be no cls loss when there are no true boxes')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = pisa_ssd_head.loss_by_feat(cls_scores, bbox_preds,\n                                                   [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n\n        pisa_ssd_head.train_cfg.update(\n            dict(isr=dict(k=2., bias=0.), carl=dict(k=1., bias=0.2)))\n\n        # test with isr and carl\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = pisa_ssd_head.loss_by_feat(cls_scores, bbox_preds,\n                                                     [gt_instances], img_metas)\n        # When there is no truth, cls_loss and box_loss should all be zero.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        self.assertEqual(\n            empty_cls_loss.item(), 0,\n            'there should be no cls loss when there are no true boxes')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = pisa_ssd_head.loss_by_feat(cls_scores, bbox_preds,\n                                                   [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_reppoints_head.py",
    "content": "import unittest\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom parameterized import parameterized\n\nfrom mmdet.models.dense_heads import RepPointsHead\nfrom mmdet.structures import DetDataSample\n\n\nclass TestRepPointsHead(unittest.TestCase):\n\n    @parameterized.expand(['moment', 'minmax', 'partial_minmax'])\n    def test_head_loss(self, transform_method='moment'):\n        cfg = ConfigDict(\n            dict(\n                num_classes=2,\n                in_channels=32,\n                point_feat_channels=10,\n                num_points=9,\n                gradient_mul=0.1,\n                point_strides=[8, 16, 32, 64, 128],\n                point_base_scale=4,\n                loss_cls=dict(\n                    type='FocalLoss',\n                    use_sigmoid=True,\n                    gamma=2.0,\n                    alpha=0.25,\n                    loss_weight=1.0),\n                loss_bbox_init=dict(\n                    type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=0.5),\n                loss_bbox_refine=dict(\n                    type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0),\n                use_grid_points=False,\n                center_init=True,\n                transform_method=transform_method,\n                moment_mul=0.01,\n                init_cfg=dict(\n                    type='Normal',\n                    layer='Conv2d',\n                    std=0.01,\n                    override=dict(\n                        type='Normal',\n                        name='reppoints_cls_out',\n                        std=0.01,\n                        bias_prob=0.01)),\n                train_cfg=dict(\n                    init=dict(\n                        assigner=dict(\n                            type='PointAssigner', scale=4, pos_num=1),\n                        allowed_border=-1,\n                        pos_weight=-1,\n                        debug=False),\n                    refine=dict(\n                        assigner=dict(\n                            type='MaxIoUAssigner',\n                            pos_iou_thr=0.5,\n                            neg_iou_thr=0.4,\n                            min_pos_iou=0,\n                            ignore_iof_thr=-1),\n                        allowed_border=-1,\n                        pos_weight=-1,\n                        debug=False)),\n                test_cfg=dict(\n                    nms_pre=1000,\n                    min_bbox_size=0,\n                    score_thr=0.05,\n                    nms=dict(type='nms', iou_threshold=0.5),\n                    max_per_img=100)))\n        reppoints_head = RepPointsHead(**cfg)\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s),\n            'scale_factor': (1, 1),\n            'pad_shape': (s, s),\n            'batch_input_shape': (s, s)\n        }]\n        x = [\n            torch.rand(1, 32, s // 2**(i + 2), s // 2**(i + 2))\n            for i in range(5)\n        ]\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_bboxes_ignore = None\n\n        reppoints_head.train()\n        forward_outputs = reppoints_head.forward(x)\n        empty_gt_losses = reppoints_head.loss_by_feat(*forward_outputs,\n                                                      [gt_instances],\n                                                      img_metas,\n                                                      gt_bboxes_ignore)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no pts loss.\n        for key, losses in empty_gt_losses.items():\n            for loss in losses:\n                if 'cls' in key:\n                    self.assertGreater(loss.item(), 0,\n                                       'cls loss should be non-zero')\n                elif 'pts' in key:\n                    self.assertEqual(\n                        loss.item(), 0,\n                        'there should be no reg loss when no ground true boxes'\n                    )\n\n        # When truth is non-empty then both cls and pts loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = reppoints_head.loss_by_feat(*forward_outputs,\n                                                    [gt_instances], img_metas,\n                                                    gt_bboxes_ignore)\n        # loss_cls should all be non-zero\n        self.assertTrue(\n            all([loss.item() > 0 for loss in one_gt_losses['loss_cls']]))\n        # only one level loss_pts_init is non-zero\n        cnt_non_zero = 0\n        for loss in one_gt_losses['loss_pts_init']:\n            if loss.item() != 0:\n                cnt_non_zero += 1\n        self.assertEqual(cnt_non_zero, 1)\n\n        # only one level loss_pts_refine is non-zero\n        cnt_non_zero = 0\n        for loss in one_gt_losses['loss_pts_init']:\n            if loss.item() != 0:\n                cnt_non_zero += 1\n        self.assertEqual(cnt_non_zero, 1)\n\n        # test loss\n        samples = DetDataSample()\n        samples.set_metainfo(img_metas[0])\n        samples.gt_instances = gt_instances\n        reppoints_head.loss(x, [samples])\n        # test only predict\n        reppoints_head.eval()\n        reppoints_head.predict(x, [samples], rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_retina_sepBN_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import RetinaSepBNHead\n\n\nclass TestRetinaSepBNHead(TestCase):\n\n    def test_init(self):\n        \"\"\"Test init RetinaSepBN head.\"\"\"\n        anchor_head = RetinaSepBNHead(num_classes=1, num_ins=1, in_channels=1)\n        anchor_head.init_weights()\n        self.assertTrue(anchor_head.cls_convs)\n        self.assertTrue(anchor_head.reg_convs)\n        self.assertTrue(anchor_head.retina_cls)\n        self.assertTrue(anchor_head.retina_reg)\n\n    def test_retina_sepbn_head_loss(self):\n        \"\"\"Tests RetinaSepBN head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.4,\n                    min_pos_iou=0,\n                    ignore_iof_thr=-1),\n                sampler=dict(type='PseudoSampler'\n                             ),  # Focal loss should use PseudoSampler\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        anchor_head = RetinaSepBNHead(\n            num_classes=4, num_ins=5, in_channels=1, train_cfg=cfg)\n\n        # Anchor head expects a multiple levels of features per image\n        feats = []\n        for i in range(len(anchor_head.prior_generator.strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 2)), s // (2**(i + 2))))\n\n        cls_scores, bbox_preds = anchor_head.forward(tuple(feats))\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = anchor_head.loss_by_feat(cls_scores, bbox_preds,\n                                                   [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box loss.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = anchor_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_rpn_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport pytest\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import RPNHead\n\n\nclass TestRPNHead(TestCase):\n\n    def test_init(self):\n        \"\"\"Test init rpn head.\"\"\"\n        rpn_head = RPNHead(num_classes=1, in_channels=1)\n        self.assertTrue(rpn_head.rpn_conv)\n        self.assertTrue(rpn_head.rpn_cls)\n        self.assertTrue(rpn_head.rpn_reg)\n\n        # rpn_head.num_convs > 1\n        rpn_head = RPNHead(num_classes=1, in_channels=1, num_convs=2)\n        self.assertTrue(rpn_head.rpn_conv)\n        self.assertTrue(rpn_head.rpn_cls)\n        self.assertTrue(rpn_head.rpn_reg)\n\n    def test_rpn_head_loss(self):\n        \"\"\"Tests rpn head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.7,\n                    neg_iou_thr=0.3,\n                    min_pos_iou=0.3,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='RandomSampler',\n                    num=256,\n                    pos_fraction=0.5,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                allowed_border=0,\n                pos_weight=-1,\n                debug=False))\n        rpn_head = RPNHead(num_classes=1, in_channels=1, train_cfg=cfg)\n\n        # Anchor head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // (2**(i + 2)), s // (2**(i + 2)))\n            for i in range(len(rpn_head.prior_generator.strides)))\n        cls_scores, bbox_preds = rpn_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = rpn_head.loss_by_feat(cls_scores, bbox_preds,\n                                                [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box loss.\n        empty_cls_loss = sum(empty_gt_losses['loss_rpn_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_rpn_bbox'])\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'rpn cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([0])\n\n        one_gt_losses = rpn_head.loss_by_feat(cls_scores, bbox_preds,\n                                              [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_rpn_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_rpn_bbox'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'rpn cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'rpn box loss should be non-zero')\n\n        # When there is no valid anchor, the loss will be None,\n        # and this will raise a ValueError.\n        img_metas = [{\n            'img_shape': (8, 8, 3),\n            'pad_shape': (8, 8, 3),\n            'scale_factor': 1,\n        }]\n        with pytest.raises(ValueError):\n            rpn_head.loss_by_feat(cls_scores, bbox_preds, [gt_instances],\n                                  img_metas)\n\n    def test_bbox_post_process(self):\n        \"\"\"Test the length of detection instance results is 0.\"\"\"\n        from mmengine.config import ConfigDict\n        cfg = ConfigDict(\n            nms_pre=1000,\n            max_per_img=1000,\n            nms=dict(type='nms', iou_threshold=0.7),\n            min_bbox_size=0)\n\n        rpn_head = RPNHead(num_classes=1, in_channels=1)\n        results = InstanceData(metainfo=dict())\n        results.bboxes = torch.zeros((0, 4))\n        results.scores = torch.zeros(0)\n        results = rpn_head._bbox_post_process(results, cfg, img_meta=dict())\n        self.assertEqual(len(results), 0)\n        self.assertEqual(results.bboxes.size(), (0, 4))\n        self.assertEqual(results.scores.size(), (0, ))\n        self.assertEqual(results.labels.size(), (0, ))\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_sabl_retina_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import SABLRetinaHead\n\n\nclass TestSABLRetinaHead(TestCase):\n\n    def test_sabl_retina_head(self):\n        \"\"\"Tests sabl retina head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s),\n            'pad_shape': (s, s),\n            'scale_factor': [1, 1],\n        }]\n        train_cfg = ConfigDict(\n            dict(\n                assigner=dict(\n                    type='ApproxMaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.4,\n                    min_pos_iou=0.0,\n                    ignore_iof_thr=-1),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        sabl_retina_head = SABLRetinaHead(\n            num_classes=4,\n            in_channels=1,\n            feat_channels=1,\n            stacked_convs=1,\n            approx_anchor_generator=dict(\n                type='AnchorGenerator',\n                octave_base_scale=4,\n                scales_per_octave=3,\n                ratios=[0.5, 1.0, 2.0],\n                strides=[8, 16, 32, 64, 128]),\n            square_anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                scales=[4],\n                strides=[8, 16, 32, 64, 128]),\n            bbox_coder=dict(\n                type='BucketingBBoxCoder', num_buckets=14, scale_factor=3.0),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0),\n            loss_bbox_cls=dict(\n                type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.5),\n            loss_bbox_reg=dict(\n                type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.5),\n            train_cfg=train_cfg)\n\n        # Fcos head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in sabl_retina_head.square_anchor_generator.strides)\n        outs = sabl_retina_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = sabl_retina_head.loss_by_feat(\n            *outs, [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # box loss and centerness loss should be zero\n        empty_cls_loss = sum(empty_gt_losses['loss_cls']).item()\n        empty_box_cls_loss = sum(empty_gt_losses['loss_bbox_cls']).item()\n        empty_box_reg_loss = sum(empty_gt_losses['loss_bbox_reg']).item()\n        self.assertGreater(empty_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_cls_loss, 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_box_reg_loss, 0,\n            'there should be no centerness loss when there are no true boxes')\n\n        # When truth is non-empty then all cls, box loss and centerness loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = sabl_retina_head.loss_by_feat(*outs, [gt_instances],\n                                                      img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls']).item()\n        onegt_box_cls_loss = sum(one_gt_losses['loss_bbox_cls']).item()\n        onegt_box_reg_loss = sum(one_gt_losses['loss_bbox_reg']).item()\n        self.assertGreater(onegt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(onegt_box_cls_loss, 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_box_reg_loss, 0,\n                           'centerness loss should be non-zero')\n\n        test_cfg = ConfigDict(\n            dict(\n                nms_pre=1000,\n                min_bbox_size=0,\n                score_thr=0.05,\n                nms=dict(type='nms', iou_threshold=0.5),\n                max_per_img=100))\n        # test predict_by_feat\n        sabl_retina_head.predict_by_feat(\n            *outs, batch_img_metas=img_metas, cfg=test_cfg, rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_solo_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom parameterized import parameterized\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import (DecoupledSOLOHead,\n                                      DecoupledSOLOLightHead, SOLOHead)\nfrom mmdet.structures.mask import BitmapMasks\n\n\ndef _rand_masks(num_items, bboxes, img_w, img_h):\n    rng = np.random.RandomState(0)\n    masks = np.zeros((num_items, img_h, img_w))\n    for i, bbox in enumerate(bboxes):\n        bbox = bbox.astype(np.int32)\n        mask = (rng.rand(1, bbox[3] - bbox[1], bbox[2] - bbox[0]) >\n                0.3).astype(np.int64)\n        masks[i:i + 1, bbox[1]:bbox[3], bbox[0]:bbox[2]] = mask\n    return BitmapMasks(masks, height=img_h, width=img_w)\n\n\nclass TestSOLOHead(TestCase):\n\n    @parameterized.expand([(SOLOHead, ), (DecoupledSOLOHead, ),\n                           (DecoupledSOLOLightHead, )])\n    def test_mask_head_loss(self, MaskHead):\n        \"\"\"Tests mask head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'ori_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3)\n        }]\n\n        mask_head = MaskHead(num_classes=4, in_channels=1)\n\n        # SOLO head expects a multiple levels of features per image\n        feats = []\n        for i in range(len(mask_head.strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 2)), s // (2**(i + 2))))\n        feats = tuple(feats)\n\n        mask_outs = mask_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty(0, 4)\n        gt_instances.labels = torch.LongTensor([])\n        gt_instances.masks = _rand_masks(0, gt_instances.bboxes.numpy(), s, s)\n\n        empty_gt_losses = mask_head.loss_by_feat(\n            *mask_outs,\n            batch_gt_instances=[gt_instances],\n            batch_img_metas=img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_mask_loss = empty_gt_losses['loss_mask']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_mask_loss.item(), 0,\n            'there should be no mask loss when there are no true mask')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        gt_instances.masks = _rand_masks(1, gt_instances.bboxes.numpy(), s, s)\n\n        one_gt_losses = mask_head.loss_by_feat(\n            *mask_outs,\n            batch_gt_instances=[gt_instances],\n            batch_img_metas=img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_mask_loss = one_gt_losses['loss_mask']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_mask_loss.item(), 0,\n                           'mask loss should be non-zero')\n\n    def test_solo_head_empty_result(self):\n        s = 256\n        img_metas = {\n            'img_shape': (s, s, 3),\n            'ori_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3)\n        }\n\n        mask_head = SOLOHead(num_classes=4, in_channels=1)\n\n        cls_scores = torch.empty(0, 80)\n        mask_preds = torch.empty(0, 16, 16)\n        test_cfg = ConfigDict(\n            score_thr=0.1,\n            mask_thr=0.5,\n        )\n        results = mask_head._predict_by_feat_single(\n            cls_scores=cls_scores,\n            mask_preds=mask_preds,\n            img_meta=img_metas,\n            cfg=test_cfg)\n\n        self.assertIsInstance(results, InstanceData)\n        self.assertEqual(len(results), 0)\n\n    def test_decoupled_solo_head_empty_result(self):\n        s = 256\n        img_metas = {\n            'img_shape': (s, s, 3),\n            'ori_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3)\n        }\n\n        mask_head = DecoupledSOLOHead(num_classes=4, in_channels=1)\n\n        cls_scores = torch.empty(0, 80)\n        mask_preds_x = torch.empty(0, 16, 16)\n        mask_preds_y = torch.empty(0, 16, 16)\n        test_cfg = ConfigDict(\n            score_thr=0.1,\n            mask_thr=0.5,\n        )\n        results = mask_head._predict_by_feat_single(\n            cls_scores=cls_scores,\n            mask_preds_x=mask_preds_x,\n            mask_preds_y=mask_preds_y,\n            img_meta=img_metas,\n            cfg=test_cfg)\n\n        self.assertIsInstance(results, InstanceData)\n        self.assertEqual(len(results), 0)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_solov2_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import SOLOV2Head\nfrom mmdet.structures.mask import BitmapMasks\n\n\ndef _rand_masks(num_items, bboxes, img_w, img_h):\n    rng = np.random.RandomState(0)\n    masks = np.zeros((num_items, img_h, img_w))\n    for i, bbox in enumerate(bboxes):\n        bbox = bbox.astype(np.int32)\n        mask = (rng.rand(1, bbox[3] - bbox[1], bbox[2] - bbox[0]) >\n                0.3).astype(np.int64)\n        masks[i:i + 1, bbox[1]:bbox[3], bbox[0]:bbox[2]] = mask\n    return BitmapMasks(masks, height=img_h, width=img_w)\n\n\ndef _fake_mask_feature_head():\n    mask_feature_head = ConfigDict(\n        feat_channels=128,\n        start_level=0,\n        end_level=3,\n        out_channels=256,\n        mask_stride=4,\n        norm_cfg=dict(type='GN', num_groups=32, requires_grad=True))\n    return mask_feature_head\n\n\nclass TestSOLOv2Head(TestCase):\n\n    def test_solov2_head_loss(self):\n        \"\"\"Tests mask head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'ori_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3)\n        }]\n\n        mask_feature_head = _fake_mask_feature_head()\n\n        mask_head = SOLOV2Head(\n            num_classes=4, in_channels=1, mask_feature_head=mask_feature_head)\n\n        # SOLO head expects a multiple levels of features per image\n        feats = []\n        for i in range(len(mask_head.strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 2)), s // (2**(i + 2))))\n        feats = tuple(feats)\n\n        mask_outs = mask_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty(0, 4)\n        gt_instances.labels = torch.LongTensor([])\n        gt_instances.masks = _rand_masks(0, gt_instances.bboxes.numpy(), s, s)\n\n        empty_gt_losses = mask_head.loss_by_feat(\n            *mask_outs,\n            batch_gt_instances=[gt_instances],\n            batch_img_metas=img_metas)\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_mask_loss = empty_gt_losses['loss_mask']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_mask_loss.item(), 0,\n            'there should be no mask loss when there are no true mask')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        gt_instances.masks = _rand_masks(1, gt_instances.bboxes.numpy(), s, s)\n\n        one_gt_losses = mask_head.loss_by_feat(\n            *mask_outs,\n            batch_gt_instances=[gt_instances],\n            batch_img_metas=img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_mask_loss = one_gt_losses['loss_mask']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_mask_loss.item(), 0,\n                           'mask loss should be non-zero')\n\n    def test_solov2_head_empty_result(self):\n        s = 256\n        img_metas = {\n            'img_shape': (s, s, 3),\n            'ori_shape': (s, s, 3),\n            'scale_factor': 1,\n            'batch_input_shape': (s, s, 3)\n        }\n\n        mask_feature_head = _fake_mask_feature_head()\n        mask_head = SOLOV2Head(\n            num_classes=4, in_channels=1, mask_feature_head=mask_feature_head)\n\n        kernel_preds = torch.empty(0, 128)\n        cls_scores = torch.empty(0, 80)\n        mask_feats = torch.empty(0, 16, 16)\n        test_cfg = ConfigDict(\n            score_thr=0.1,\n            mask_thr=0.5,\n        )\n        results = mask_head._predict_by_feat_single(\n            kernel_preds=kernel_preds,\n            cls_scores=cls_scores,\n            mask_feats=mask_feats,\n            img_meta=img_metas,\n            cfg=test_cfg)\n\n        self.assertIsInstance(results, InstanceData)\n        self.assertEqual(len(results), 0)\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_ssd_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom math import ceil\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import SSDHead\n\n\nclass TestSSDHead(TestCase):\n\n    def test_ssd_head_loss(self):\n        \"\"\"Tests ssd head loss when truth is empty and non-empty.\"\"\"\n        s = 300\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        cfg = Config(\n            dict(\n                assigner=dict(\n                    type='MaxIoUAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.,\n                    ignore_iof_thr=-1,\n                    gt_max_assign_all=False),\n                sampler=dict(type='PseudoSampler'),\n                smoothl1_beta=1.,\n                allowed_border=-1,\n                pos_weight=-1,\n                neg_pos_ratio=3,\n                debug=False))\n        ssd_head = SSDHead(\n            num_classes=4,\n            in_channels=(1, 1, 1, 1, 1, 1),\n            stacked_convs=1,\n            feat_channels=1,\n            use_depthwise=True,\n            anchor_generator=dict(\n                type='SSDAnchorGenerator',\n                scale_major=False,\n                input_size=s,\n                basesize_ratio_range=(0.15, 0.9),\n                strides=[8, 16, 32, 64, 100, 300],\n                ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]]),\n            train_cfg=cfg)\n\n        # SSD head expects a multiple levels of features per image\n        feats = (\n            torch.rand(1, 1, ceil(s / stride[0]), ceil(s / stride[0]))\n            for stride in ssd_head.prior_generator.strides)\n        cls_scores, bbox_preds = ssd_head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = ssd_head.loss_by_feat(cls_scores, bbox_preds,\n                                                [gt_instances], img_metas)\n        # When there is no truth, cls_loss and box_loss should all be zero.\n        empty_cls_loss = sum(empty_gt_losses['loss_cls'])\n        empty_box_loss = sum(empty_gt_losses['loss_bbox'])\n        self.assertEqual(\n            empty_cls_loss.item(), 0,\n            'there should be no cls loss when there are no true boxes')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = ssd_head.loss_by_feat(cls_scores, bbox_preds,\n                                              [gt_instances], img_metas)\n        onegt_cls_loss = sum(one_gt_losses['loss_cls'])\n        onegt_box_loss = sum(one_gt_losses['loss_bbox'])\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_tood_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config, MessageHub\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import TOODHead\n\n\ndef _tood_head(anchor_type):\n    \"\"\"Set type of tood head.\"\"\"\n    train_cfg = Config(\n        dict(\n            initial_epoch=4,\n            initial_assigner=dict(type='ATSSAssigner', topk=9),\n            assigner=dict(type='TaskAlignedAssigner', topk=13),\n            alpha=1,\n            beta=6,\n            allowed_border=-1,\n            pos_weight=-1,\n            debug=False))\n    test_cfg = Config(\n        dict(\n            nms_pre=1000,\n            min_bbox_size=0,\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.6),\n            max_per_img=100))\n\n    tood_head = TOODHead(\n        num_classes=80,\n        in_channels=1,\n        stacked_convs=1,\n        feat_channels=8,  # the same as `la_down_rate` in TaskDecomposition\n        norm_cfg=None,\n        anchor_type=anchor_type,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            octave_base_scale=8,\n            scales_per_octave=1,\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[0.1, 0.1, 0.2, 0.2]),\n        initial_loss_cls=dict(\n            type='FocalLoss',\n            use_sigmoid=True,\n            activated=True,  # use probability instead of logit as input\n            gamma=2.0,\n            alpha=0.25,\n            loss_weight=1.0),\n        loss_cls=dict(\n            type='QualityFocalLoss',\n            use_sigmoid=True,\n            activated=True,  # use probability instead of logit as input\n            beta=2.0,\n            loss_weight=1.0),\n        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),\n        train_cfg=train_cfg,\n        test_cfg=test_cfg)\n    return tood_head\n\n\nclass TestTOODHead(TestCase):\n\n    def test_tood_head_anchor_free_loss(self):\n        \"\"\"Tests tood head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1\n        }]\n        tood_head = _tood_head('anchor_free')\n        tood_head.init_weights()\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [8, 16, 32, 64, 128]\n        ]\n        cls_scores, bbox_preds = tood_head(feat)\n\n        message_hub = MessageHub.get_instance('runtime_info')\n        message_hub.update_info('epoch', 0)\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_bboxes_ignore = None\n        empty_gt_losses = tood_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 [gt_instances], img_metas,\n                                                 gt_bboxes_ignore)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(\n            sum(empty_cls_loss).item(), 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            sum(empty_box_loss).item(), 0,\n            'there should be no box loss when there are no true boxes')\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        gt_bboxes_ignore = None\n        one_gt_losses = tood_head.loss_by_feat(cls_scores, bbox_preds,\n                                               [gt_instances], img_metas,\n                                               gt_bboxes_ignore)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        self.assertGreater(\n            sum(onegt_cls_loss).item(), 0, 'cls loss should be non-zero')\n        self.assertGreater(\n            sum(onegt_box_loss).item(), 0, 'box loss should be non-zero')\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_bboxes_ignore = None\n        empty_gt_losses = tood_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 [gt_instances], img_metas,\n                                                 gt_bboxes_ignore)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(\n            sum(empty_cls_loss).item(), 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            sum(empty_box_loss).item(), 0,\n            'there should be no box loss when there are no true boxes')\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        gt_bboxes_ignore = None\n        one_gt_losses = tood_head.loss_by_feat(cls_scores, bbox_preds,\n                                               [gt_instances], img_metas,\n                                               gt_bboxes_ignore)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        self.assertGreater(\n            sum(onegt_cls_loss).item(), 0, 'cls loss should be non-zero')\n        self.assertGreater(\n            sum(onegt_box_loss).item(), 0, 'box loss should be non-zero')\n\n    def test_tood_head_anchor_based_loss(self):\n        \"\"\"Tests tood head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'pad_shape': (s, s, 3),\n            'scale_factor': 1\n        }]\n        tood_head = _tood_head('anchor_based')\n        tood_head.init_weights()\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [8, 16, 32, 64, 128]\n        ]\n        cls_scores, bbox_preds = tood_head(feat)\n\n        message_hub = MessageHub.get_instance('runtime_info')\n        message_hub.update_info('epoch', 0)\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        gt_bboxes_ignore = None\n        empty_gt_losses = tood_head.loss_by_feat(cls_scores, bbox_preds,\n                                                 [gt_instances], img_metas,\n                                                 gt_bboxes_ignore)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(\n            sum(empty_cls_loss).item(), 0, 'cls loss should be non-zero')\n        self.assertEqual(\n            sum(empty_box_loss).item(), 0,\n            'there should be no box loss when there are no true boxes')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_vfnet_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import VFNetHead\n\n\nclass TestVFNetHead(TestCase):\n\n    def test_vfnet_head_loss(self):\n        \"\"\"Tests vfnet head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n            'pad_shape': (s, s, 3)\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(type='ATSSAssigner', topk=9),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        # since VarFocal Loss is not supported on CPU\n        vfnet_head = VFNetHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            loss_cls=dict(\n                type='VarifocalLoss', use_sigmoid=True, loss_weight=1.0))\n\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16, 32, 64]\n        ]\n        cls_scores, bbox_preds, bbox_preds_refine = vfnet_head.forward(feat)\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        empty_gt_losses = vfnet_head.loss_by_feat(cls_scores, bbox_preds,\n                                                  bbox_preds_refine,\n                                                  [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = vfnet_head.loss_by_feat(cls_scores, bbox_preds,\n                                                bbox_preds_refine,\n                                                [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n\n    def test_vfnet_head_loss_without_atss(self):\n        \"\"\"Tests vfnet head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n            'pad_shape': (s, s, 3)\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(type='ATSSAssigner', topk=9),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        # since VarFocal Loss is not supported on CPU\n        vfnet_head = VFNetHead(\n            num_classes=4,\n            in_channels=1,\n            train_cfg=train_cfg,\n            use_atss=False,\n            loss_cls=dict(\n                type='VarifocalLoss', use_sigmoid=True, loss_weight=1.0))\n\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16, 32, 64]\n        ]\n        cls_scores, bbox_preds, bbox_preds_refine = vfnet_head.forward(feat)\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        empty_gt_losses = vfnet_head.loss_by_feat(cls_scores, bbox_preds,\n                                                  bbox_preds_refine,\n                                                  [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = vfnet_head.loss_by_feat(cls_scores, bbox_preds,\n                                                bbox_preds_refine,\n                                                [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_yolo_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.dense_heads import YOLOV3Head\n\n\nclass TestYOLOV3Head(TestCase):\n\n    def test_yolo_head_loss(self):\n        \"\"\"Tests YOLO head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        head = YOLOV3Head(\n            num_classes=4,\n            in_channels=[1, 1, 1],\n            out_channels=[1, 1, 1],\n            train_cfg=Config(\n                dict(\n                    assigner=dict(\n                        type='GridAssigner',\n                        pos_iou_thr=0.5,\n                        neg_iou_thr=0.5,\n                        min_pos_iou=0))))\n        head.init_weights()\n\n        # YOLO head expects a multiple levels of features per image\n        feats = [\n            torch.rand(1, 1, s // stride[1], s // stride[0])\n            for stride in head.prior_generator.strides\n        ]\n        predmaps, = head.forward(feats)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n\n        empty_gt_losses = head.loss_by_feat(predmaps, [gt_instances],\n                                            img_metas)\n        # When there is no truth, the conf loss should be nonzero but\n        # cls loss and xy&wh loss should be zero\n        empty_cls_loss = sum(empty_gt_losses['loss_cls']).item()\n        empty_conf_loss = sum(empty_gt_losses['loss_conf']).item()\n        empty_xy_loss = sum(empty_gt_losses['loss_xy']).item()\n        empty_wh_loss = sum(empty_gt_losses['loss_wh']).item()\n        self.assertGreater(empty_conf_loss, 0, 'conf loss should be non-zero')\n        self.assertEqual(\n            empty_cls_loss, 0,\n            'there should be no cls loss when there are no true boxes')\n        self.assertEqual(\n            empty_xy_loss, 0,\n            'there should be no xy loss when there are no true boxes')\n        self.assertEqual(\n            empty_wh_loss, 0,\n            'there should be no wh loss when there are no true boxes')\n\n        # When truth is non-empty then all conf, cls loss and xywh loss\n        # should be nonzero for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n\n        one_gt_losses = head.loss_by_feat(predmaps, [gt_instances], img_metas)\n        one_gt_cls_loss = sum(one_gt_losses['loss_cls']).item()\n        one_gt_conf_loss = sum(one_gt_losses['loss_conf']).item()\n        one_gt_xy_loss = sum(one_gt_losses['loss_xy']).item()\n        one_gt_wh_loss = sum(one_gt_losses['loss_wh']).item()\n        self.assertGreater(one_gt_conf_loss, 0, 'conf loss should be non-zero')\n        self.assertGreater(one_gt_cls_loss, 0, 'cls loss should be non-zero')\n        self.assertGreater(one_gt_xy_loss, 0, 'xy loss should be non-zero')\n        self.assertGreater(one_gt_wh_loss, 0, 'wh loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_yolof_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet import *  # noqa\nfrom mmdet.models.dense_heads import YOLOFHead\n\n\nclass TestYOLOFHead(TestCase):\n\n    def test_yolof_head_loss(self):\n        \"\"\"Tests yolof head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n            'pad_shape': (s, s, 3)\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(\n                    type='UniformAssigner',\n                    pos_ignore_thr=0.15,\n                    neg_ignore_thr=0.7),\n                allowed_border=-1,\n                pos_weight=-1,\n                debug=False))\n        yolof_head = YOLOFHead(\n            num_classes=4,\n            in_channels=1,\n            feat_channels=1,\n            reg_decoded_bbox=True,\n            train_cfg=train_cfg,\n            anchor_generator=dict(\n                type='AnchorGenerator',\n                ratios=[1.0],\n                scales=[1, 2, 4, 8, 16],\n                strides=[32]),\n            bbox_coder=dict(\n                type='DeltaXYWHBBoxCoder',\n                target_means=[.0, .0, .0, .0],\n                target_stds=[1., 1., 1., 1.],\n                add_ctr_clamp=True,\n                ctr_clamp=32),\n            loss_cls=dict(\n                type='FocalLoss',\n                use_sigmoid=True,\n                gamma=2.0,\n                alpha=0.25,\n                loss_weight=1.0),\n            loss_bbox=dict(type='GIoULoss', loss_weight=1.0))\n        feat = [torch.rand(1, 1, s // 32, s // 32)]\n        cls_scores, bbox_preds = yolof_head.forward(feat)\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        empty_gt_losses = yolof_head.loss_by_feat(cls_scores, bbox_preds,\n                                                  [gt_instances], img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls']\n        empty_box_loss = empty_gt_losses['loss_bbox']\n        self.assertGreater(empty_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        one_gt_losses = yolof_head.loss_by_feat(cls_scores, bbox_preds,\n                                                [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls']\n        onegt_box_loss = one_gt_losses['loss_bbox']\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_dense_heads/test_yolox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmcv.cnn import ConvModule, DepthwiseSeparableConvModule\nfrom mmengine.config import Config\nfrom mmengine.model import bias_init_with_prob\nfrom mmengine.structures import InstanceData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.dense_heads import YOLOXHead\n\n\nclass TestYOLOXHead(TestCase):\n\n    def test_init_weights(self):\n        head = YOLOXHead(\n            num_classes=4, in_channels=1, stacked_convs=1, use_depthwise=False)\n        head.init_weights()\n        bias_init = bias_init_with_prob(0.01)\n        for conv_cls, conv_obj in zip(head.multi_level_conv_cls,\n                                      head.multi_level_conv_obj):\n            assert_allclose(conv_cls.bias.data,\n                            torch.ones_like(conv_cls.bias.data) * bias_init)\n            assert_allclose(conv_obj.bias.data,\n                            torch.ones_like(conv_obj.bias.data) * bias_init)\n\n    def test_predict_by_feat(self):\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': (1.0, 1.0),\n        }]\n        test_cfg = Config(\n            dict(score_thr=0.01, nms=dict(type='nms', iou_threshold=0.65)))\n        head = YOLOXHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            use_depthwise=False,\n            test_cfg=test_cfg)\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16]\n        ]\n        cls_scores, bbox_preds, objectnesses = head.forward(feat)\n        head.predict_by_feat(\n            cls_scores,\n            bbox_preds,\n            objectnesses,\n            img_metas,\n            cfg=test_cfg,\n            rescale=True,\n            with_nms=True)\n        head.predict_by_feat(\n            cls_scores,\n            bbox_preds,\n            objectnesses,\n            img_metas,\n            cfg=test_cfg,\n            rescale=False,\n            with_nms=False)\n\n    def test_loss_by_feat(self):\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        train_cfg = Config(\n            dict(\n                assigner=dict(\n                    type='SimOTAAssigner',\n                    center_radius=2.5,\n                    candidate_topk=10,\n                    iou_weight=3.0,\n                    cls_weight=1.0)))\n        head = YOLOXHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            use_depthwise=False,\n            train_cfg=train_cfg)\n        assert not head.use_l1\n        assert isinstance(head.multi_level_cls_convs[0][0], ConvModule)\n\n        feat = [\n            torch.rand(1, 1, s // feat_size, s // feat_size)\n            for feat_size in [4, 8, 16]\n        ]\n        cls_scores, bbox_preds, objectnesses = head.forward(feat)\n\n        # Test that empty ground truth encourages the network to predict\n        # background\n        gt_instances = InstanceData(\n            bboxes=torch.empty((0, 4)), labels=torch.LongTensor([]))\n\n        empty_gt_losses = head.loss_by_feat(cls_scores, bbox_preds,\n                                            objectnesses, [gt_instances],\n                                            img_metas)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        empty_cls_loss = empty_gt_losses['loss_cls'].sum()\n        empty_box_loss = empty_gt_losses['loss_bbox'].sum()\n        empty_obj_loss = empty_gt_losses['loss_obj'].sum()\n        self.assertEqual(\n            empty_cls_loss.item(), 0,\n            'there should be no cls loss when there are no true boxes')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertGreater(empty_obj_loss.item(), 0,\n                           'objectness loss should be non-zero')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        head = YOLOXHead(\n            num_classes=4,\n            in_channels=1,\n            stacked_convs=1,\n            use_depthwise=True,\n            train_cfg=train_cfg)\n        assert isinstance(head.multi_level_cls_convs[0][0],\n                          DepthwiseSeparableConvModule)\n        head.use_l1 = True\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[23.6667, 23.8757, 238.6326, 151.8874]]),\n            labels=torch.LongTensor([2]))\n\n        one_gt_losses = head.loss_by_feat(cls_scores, bbox_preds, objectnesses,\n                                          [gt_instances], img_metas)\n        onegt_cls_loss = one_gt_losses['loss_cls'].sum()\n        onegt_box_loss = one_gt_losses['loss_bbox'].sum()\n        onegt_obj_loss = one_gt_losses['loss_obj'].sum()\n        onegt_l1_loss = one_gt_losses['loss_l1'].sum()\n        self.assertGreater(onegt_cls_loss.item(), 0,\n                           'cls loss should be non-zero')\n        self.assertGreater(onegt_box_loss.item(), 0,\n                           'box loss should be non-zero')\n        self.assertGreater(onegt_obj_loss.item(), 0,\n                           'obj loss should be non-zero')\n        self.assertGreater(onegt_l1_loss.item(), 0,\n                           'l1 loss should be non-zero')\n\n        # Test groud truth out of bound\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[s * 4, s * 4, s * 4 + 10, s * 4 + 10]]),\n            labels=torch.LongTensor([2]))\n        empty_gt_losses = head.loss_by_feat(cls_scores, bbox_preds,\n                                            objectnesses, [gt_instances],\n                                            img_metas)\n        # When gt_bboxes out of bound, the assign results should be empty,\n        # so the cls and bbox loss should be zero.\n        empty_cls_loss = empty_gt_losses['loss_cls'].sum()\n        empty_box_loss = empty_gt_losses['loss_bbox'].sum()\n        empty_obj_loss = empty_gt_losses['loss_obj'].sum()\n        self.assertEqual(\n            empty_cls_loss.item(), 0,\n            'there should be no cls loss when gt_bboxes out of bound')\n        self.assertEqual(\n            empty_box_loss.item(), 0,\n            'there should be no box loss when gt_bboxes out of bound')\n        self.assertGreater(empty_obj_loss.item(), 0,\n                           'objectness loss should be non-zero')\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_conditional_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestConditionalDETR(TestCase):\n\n    def setUp(self) -> None:\n        register_all_modules()\n\n    def test_conditional_detr_head_loss(self):\n        \"\"\"Tests transformer head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        metainfo = {\n            'img_shape': (s, s),\n            'scale_factor': (1, 1),\n            'pad_shape': (s, s),\n            'batch_input_shape': (s, s)\n        }\n        img_metas = DetDataSample()\n        img_metas.set_metainfo(metainfo)\n        batch_data_samples = []\n        batch_data_samples.append(img_metas)\n\n        config = get_detector_cfg(\n            'conditional_detr/conditional-detr_r50_8xb2-50e_coco.py')\n\n        model = MODELS.build(config)\n        model.init_weights()\n        random_image = torch.rand(1, 3, s, s)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        img_metas.gt_instances = gt_instances\n        batch_data_samples1 = []\n        batch_data_samples1.append(img_metas)\n        empty_gt_losses = model.loss(\n            random_image, batch_data_samples=batch_data_samples1)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        for key, loss in empty_gt_losses.items():\n            if 'cls' in key:\n                self.assertGreater(loss.item(), 0,\n                                   'cls loss should be non-zero')\n            elif 'bbox' in key:\n                self.assertEqual(\n                    loss.item(), 0,\n                    'there should be no box loss when no ground true boxes')\n            elif 'iou' in key:\n                self.assertEqual(\n                    loss.item(), 0,\n                    'there should be no iou loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        img_metas.gt_instances = gt_instances\n        batch_data_samples2 = []\n        batch_data_samples2.append(img_metas)\n        one_gt_losses = model.loss(\n            random_image, batch_data_samples=batch_data_samples2)\n        for loss in one_gt_losses.values():\n            self.assertGreater(\n                loss.item(), 0,\n                'cls loss, or box loss, or iou loss should be non-zero')\n\n        model.eval()\n        # test _forward\n        model._forward(random_image, batch_data_samples=batch_data_samples2)\n        # test only predict\n        model.predict(\n            random_image, batch_data_samples=batch_data_samples2, rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_cornernet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestCornerNet(TestCase):\n\n    def setUp(self) -> None:\n        register_all_modules()\n        model_cfg = get_detector_cfg(\n            'cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py')\n\n        backbone = dict(\n            type='ResNet',\n            depth=18,\n            num_stages=4,\n            out_indices=(3, ),\n            norm_cfg=dict(type='BN', requires_grad=True),\n            norm_eval=True,\n            style='pytorch')\n\n        neck = dict(\n            type='FPN',\n            in_channels=[512],\n            out_channels=256,\n            start_level=0,\n            add_extra_convs='on_input',\n            num_outs=1)\n\n        model_cfg.backbone = ConfigDict(**backbone)\n        model_cfg.neck = ConfigDict(**neck)\n        model_cfg.bbox_head.num_feat_levels = 1\n        self.model_cfg = model_cfg\n\n    def test_init(self):\n        model = get_detector_cfg(\n            'cornernet/cornernet_hourglass104_8xb6-210e-mstest_coco.py')\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.bbox_head is not None)\n        self.assertTrue(detector.backbone is not None)\n        self.assertTrue(not hasattr(detector, 'neck'))\n\n    @unittest.skipIf(not torch.cuda.is_available(),\n                     'test requires GPU and torch+cuda')\n    def test_cornernet_forward_loss_mode(self):\n        from mmdet.registry import MODELS\n        detector = MODELS.build(self.model_cfg)\n        detector.init_weights()\n\n        packed_inputs = demo_mm_inputs(2, [[3, 511, 511], [3, 511, 511]])\n        data = detector.data_preprocessor(packed_inputs, True)\n        losses = detector.forward(**data, mode='loss')\n        assert isinstance(losses, dict)\n\n    @unittest.skipIf(not torch.cuda.is_available(),\n                     'test requires GPU and torch+cuda')\n    def test_cornernet_forward_predict_mode(self):\n        from mmdet.registry import MODELS\n        detector = MODELS.build(self.model_cfg)\n        detector.init_weights()\n\n        packed_inputs = demo_mm_inputs(2, [[3, 512, 512], [3, 512, 512]])\n        data = detector.data_preprocessor(packed_inputs, False)\n\n        # Test forward test\n        detector.eval()\n        with torch.no_grad():\n            batch_results = detector.forward(**data, mode='predict')\n            assert len(batch_results) == 2\n            assert isinstance(batch_results[0], DetDataSample)\n\n    @unittest.skipIf(not torch.cuda.is_available(),\n                     'test requires GPU and torch+cuda')\n    def test_cornernet_forward_tensor_mode(self):\n        from mmdet.registry import MODELS\n        detector = MODELS.build(self.model_cfg)\n        detector.init_weights()\n\n        packed_inputs = demo_mm_inputs(2, [[3, 512, 512], [3, 512, 512]])\n        data = detector.data_preprocessor(packed_inputs, False)\n        batch_results = detector.forward(**data, mode='tensor')\n        assert isinstance(batch_results, tuple)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_dab_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestDABDETR(TestCase):\n\n    def setUp(self) -> None:\n        register_all_modules()\n\n    def test_dab_detr_head_loss(self):\n        \"\"\"Tests transformer head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        metainfo = {\n            'img_shape': (s, s),\n            'scale_factor': (1, 1),\n            'pad_shape': (s, s),\n            'batch_input_shape': (s, s)\n        }\n        img_metas = DetDataSample()\n        img_metas.set_metainfo(metainfo)\n        batch_data_samples = []\n        batch_data_samples.append(img_metas)\n\n        config = get_detector_cfg('dab_detr/dab-detr_r50_8xb2-50e_coco.py')\n\n        model = MODELS.build(config)\n        model.init_weights()\n        random_image = torch.rand(1, 3, s, s)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        img_metas.gt_instances = gt_instances\n        batch_data_samples1 = []\n        batch_data_samples1.append(img_metas)\n        empty_gt_losses = model.loss(\n            random_image, batch_data_samples=batch_data_samples1)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        for key, loss in empty_gt_losses.items():\n            if 'cls' in key:\n                self.assertGreater(loss.item(), 0,\n                                   'cls loss should be non-zero')\n            elif 'bbox' in key:\n                self.assertEqual(\n                    loss.item(), 0,\n                    'there should be no box loss when no ground true boxes')\n            elif 'iou' in key:\n                self.assertEqual(\n                    loss.item(), 0,\n                    'there should be no iou loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        img_metas.gt_instances = gt_instances\n        batch_data_samples2 = []\n        batch_data_samples2.append(img_metas)\n        one_gt_losses = model.loss(\n            random_image, batch_data_samples=batch_data_samples2)\n        for loss in one_gt_losses.values():\n            self.assertGreater(\n                loss.item(), 0,\n                'cls loss, or box loss, or iou loss should be non-zero')\n\n        model.eval()\n        # test _forward\n        model._forward(random_image, batch_data_samples=batch_data_samples2)\n        # test only predict\n        model.predict(\n            random_image, batch_data_samples=batch_data_samples2, rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_ddq_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestDDQDETR(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    def test_ddq_detr_head_loss(self):\n        \"\"\"Tests DDQ DETR head loss when truth is empty and non-empty.\"\"\"\n        configs = [\n            get_detector_cfg('ddq/ddq-detr-4scale_r50_8xb2-12e_coco.py')\n        ]  # noqa E501\n\n        for config in configs:\n            config.backbone.depth = 18\n            config.backbone.init_cfg = None\n            config.neck.in_channels = [128, 256, 512]\n            config.encoder.num_layers = 1\n            # `num_layers` >= 2, because attention masks of the last\n            #   `num_layers` - 1 layers are used for distinct query selection\n            config.decoder.num_layers = 2\n            config.bbox_head.num_classes = 2\n            config.num_queries = 10\n            config.dense_topk_ratio = 1.0\n            config.dn_cfg.group_cfg.num_dn_queries = 10\n            config.test_cfg.max_per_img = 2\n            model = MODELS.build(config)\n            model.init_weights()\n\n            batch_data_samples = self.get_batch_data_samples()\n            batch_input_shape = batch_data_samples[0].metainfo[\n                'batch_input_shape']\n            random_images = torch.rand([1, 3, *batch_input_shape])\n\n            # Test that empty ground truth encourages the network to\n            # predict background\n            gt_instances = InstanceData()\n            gt_instances.bboxes = torch.empty((0, 4))\n            gt_instances.labels = torch.LongTensor([])\n            data_sample = DetDataSample()\n            data_sample.set_metainfo(batch_data_samples[0].metainfo)\n            data_sample.gt_instances = gt_instances\n\n            batch_data_samples_1 = [data_sample]\n            empty_gt_losses = model.loss(\n                random_images, batch_data_samples=batch_data_samples_1)\n            # When there is no truth, the cls loss should be nonzero but there\n            # should be no box or aux loss.\n            zero_loss_keywords = ['bbox', 'iou', 'dn']\n\n            for key, loss in empty_gt_losses.items():\n                if 'aux' in key:\n                    self.assertEqual(\n                        len(loss), len(batch_data_samples_1),\n                        (r'Actual aux loss count for {} is {},' +\n                         r'it should be {}').format(key, len(loss),\n                                                    len(batch_data_samples_1)))\n\n                    for image_loss in loss:\n                        _image_loss = image_loss.item()\n                        self.assertEqual(\n                            _image_loss, 0,\n                            f'there should be no {key}({_image_loss}) '\n                            f'when no ground true boxes')\n\n                    continue\n\n                _loss = loss.item()\n\n                if any(zero_loss_keyword in key\n                       for zero_loss_keyword in zero_loss_keywords):\n                    self.assertEqual(\n                        _loss, 0, f'there should be no {key}({_loss}) '\n                        f'when no ground true boxes')\n                elif 'cls' in key:\n                    self.assertGreater(_loss, 0,\n                                       f'{key}({_loss}) should be non-zero')\n\n            # When truth is non-empty then both cls and box loss should\n            # be nonzero for random inputs.\n            random_images = torch.rand(\n                [len(batch_data_samples), 3, *batch_input_shape])\n            batch_data_samples_2 = batch_data_samples\n            gt_losses = model.loss(\n                random_images, batch_data_samples=batch_data_samples_2)\n            for key, loss in gt_losses.items():\n                if 'aux' in key:\n                    self.assertEqual(\n                        len(loss), len(batch_data_samples_2),\n                        (r'Actual aux loss count for {} is {},' +\n                         r'it should be {}').format(key, len(loss),\n                                                    len(batch_data_samples_2)))\n\n                    for image_loss in loss:\n                        self.assertGreater(\n                            image_loss.item(), 0,\n                            'cls loss, or box loss, or iou loss should be' +\n                            ' non-zero')\n\n                    continue\n\n                self.assertGreater(\n                    loss.item(), 0,\n                    'cls loss, or box loss, or iou loss should be non-zero')\n\n            model.eval()\n            # test _forward\n            model._forward(\n                random_images, batch_data_samples=batch_data_samples_2)\n            # test only predict\n            model.predict(\n                random_images,\n                batch_data_samples=batch_data_samples_2,\n                rescale=True)\n\n    def get_batch_data_samples(self):\n        \"\"\"Generate batch data samples including model inputs and gt labels.\"\"\"\n        data_sample_file_path = 'tests/data/coco_batched_sample.json'\n\n        with open(data_sample_file_path, 'r') as file_stream:\n            data_sample_infos = json.load(file_stream)\n\n        batch_data_samples = []\n\n        for data_sample_info in data_sample_infos:\n            data_sample = DetDataSample()\n\n            metainfo = data_sample_info['metainfo']\n            labels = data_sample_info['labels']\n            bboxes = data_sample_info['bboxes']\n\n            data_sample.set_metainfo(metainfo)\n\n            gt_instances = InstanceData()\n            gt_instances.labels = torch.LongTensor(labels)\n            gt_instances.bboxes = torch.Tensor(bboxes)\n            data_sample.gt_instances = gt_instances\n\n            batch_data_samples.append(data_sample)\n\n        return batch_data_samples\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_deformable_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestDeformableDETR(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    def test_deformable_detr_head_loss(self):\n        \"\"\"Tests transformer head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        metainfo = {\n            'img_shape': (s, s),\n            'scale_factor': (1, 1),\n            'pad_shape': (s, s),\n            'batch_input_shape': (s, s)\n        }\n        img_metas = DetDataSample()\n        img_metas.set_metainfo(metainfo)\n        batch_data_samples = []\n        batch_data_samples.append(img_metas)\n\n        configs = [\n            get_detector_cfg(\n                'deformable_detr/deformable-detr_r50_16xb2-50e_coco.py'),\n            get_detector_cfg(\n                'deformable_detr/deformable-detr-refine_r50_16xb2-50e_coco.py'  # noqa\n            ),\n            get_detector_cfg(\n                'deformable_detr/deformable-detr-refine-twostage_r50_16xb2-50e_coco.py'  # noqa\n            )\n        ]\n\n        for config in configs:\n            model = MODELS.build(config)\n            model.init_weights()\n            random_image = torch.rand(1, 3, s, s)\n\n            # Test that empty ground truth encourages the network to\n            # predict background\n            gt_instances = InstanceData()\n            gt_instances.bboxes = torch.empty((0, 4))\n            gt_instances.labels = torch.LongTensor([])\n            img_metas.gt_instances = gt_instances\n            batch_data_samples1 = []\n            batch_data_samples1.append(img_metas)\n            empty_gt_losses = model.loss(\n                random_image, batch_data_samples=batch_data_samples1)\n            # When there is no truth, the cls loss should be nonzero but there\n            # should be no box loss.\n            for key, loss in empty_gt_losses.items():\n                if 'cls' in key:\n                    self.assertGreater(loss.item(), 0,\n                                       'cls loss should be non-zero')\n                elif 'bbox' in key:\n                    self.assertEqual(\n                        loss.item(), 0,\n                        'there should be no box loss when no ground true boxes'\n                    )\n                elif 'iou' in key:\n                    self.assertEqual(\n                        loss.item(), 0,\n                        'there should be no iou loss when no ground true boxes'\n                    )\n\n            # When truth is non-empty then both cls and box loss should\n            # be nonzero for random inputs\n            gt_instances = InstanceData()\n            gt_instances.bboxes = torch.Tensor(\n                [[23.6667, 23.8757, 238.6326, 151.8874]])\n            gt_instances.labels = torch.LongTensor([2])\n            img_metas.gt_instances = gt_instances\n            batch_data_samples2 = []\n            batch_data_samples2.append(img_metas)\n            one_gt_losses = model.loss(\n                random_image, batch_data_samples=batch_data_samples2)\n            for loss in one_gt_losses.values():\n                self.assertGreater(\n                    loss.item(), 0,\n                    'cls loss, or box loss, or iou loss should be non-zero')\n\n            model.eval()\n            # test _forward\n            model._forward(\n                random_image, batch_data_samples=batch_data_samples2)\n            # test only predict\n            model.predict(\n                random_image,\n                batch_data_samples=batch_data_samples2,\n                rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_detr.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestDETR(TestCase):\n\n    def setUp(self) -> None:\n        register_all_modules()\n\n    def test_detr_head_loss(self):\n        \"\"\"Tests transformer head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        metainfo = {\n            'img_shape': (s, s),\n            'scale_factor': (1, 1),\n            'pad_shape': (s, s),\n            'batch_input_shape': (s, s)\n        }\n        img_metas = DetDataSample()\n        img_metas.set_metainfo(metainfo)\n        batch_data_samples = []\n        batch_data_samples.append(img_metas)\n\n        config = get_detector_cfg('detr/detr_r50_8xb2-150e_coco.py')\n\n        model = MODELS.build(config)\n        model.init_weights()\n        random_image = torch.rand(1, 3, s, s)\n\n        # Test that empty ground truth encourages the network to\n        # predict background\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4))\n        gt_instances.labels = torch.LongTensor([])\n        img_metas.gt_instances = gt_instances\n        batch_data_samples1 = []\n        batch_data_samples1.append(img_metas)\n        empty_gt_losses = model.loss(\n            random_image, batch_data_samples=batch_data_samples1)\n        # When there is no truth, the cls loss should be nonzero but there\n        # should be no box loss.\n        for key, loss in empty_gt_losses.items():\n            if 'cls' in key:\n                self.assertGreater(loss.item(), 0,\n                                   'cls loss should be non-zero')\n            elif 'bbox' in key:\n                self.assertEqual(\n                    loss.item(), 0,\n                    'there should be no box loss when no ground true boxes')\n            elif 'iou' in key:\n                self.assertEqual(\n                    loss.item(), 0,\n                    'there should be no iou loss when there are no true boxes')\n\n        # When truth is non-empty then both cls and box loss should be nonzero\n        # for random inputs\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instances.labels = torch.LongTensor([2])\n        img_metas.gt_instances = gt_instances\n        batch_data_samples2 = []\n        batch_data_samples2.append(img_metas)\n        one_gt_losses = model.loss(\n            random_image, batch_data_samples=batch_data_samples2)\n        for loss in one_gt_losses.values():\n            self.assertGreater(\n                loss.item(), 0,\n                'cls loss, or box loss, or iou loss should be non-zero')\n\n        model.eval()\n        # test _forward\n        model._forward(random_image, batch_data_samples=batch_data_samples2)\n        # test only predict\n        model.predict(\n            random_image, batch_data_samples=batch_data_samples2, rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_dino.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestDINO(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    def test_dino_head_loss(self):\n        \"\"\"Tests transformer head loss when truth is empty and non-empty.\"\"\"\n        s = 256\n        metainfo = {\n            'img_shape': (s, s),\n            'scale_factor': (1, 1),\n            'pad_shape': (s, s),\n            'batch_input_shape': (s, s)\n        }\n        data_sample = DetDataSample()\n        data_sample.set_metainfo(metainfo)\n\n        configs = [get_detector_cfg('dino/dino-4scale_r50_8xb2-12e_coco.py')]\n\n        for config in configs:\n            model = MODELS.build(config)\n            model.init_weights()\n            random_image = torch.rand(1, 3, s, s)\n\n            # Test that empty ground truth encourages the network to\n            # predict background\n            gt_instances = InstanceData()\n            gt_instances.bboxes = torch.empty((0, 4))\n            gt_instances.labels = torch.LongTensor([])\n            data_sample.gt_instances = gt_instances\n            batch_data_samples_1 = [data_sample]\n            empty_gt_losses = model.loss(\n                random_image, batch_data_samples=batch_data_samples_1)\n            # When there is no truth, the cls loss should be nonzero but there\n            # should be no box loss.\n            for key, loss in empty_gt_losses.items():\n                _loss = loss.item()\n                if 'bbox' in key or 'iou' in key or 'dn' in key:\n                    self.assertEqual(\n                        _loss, 0, f'there should be no {key}({_loss}) '\n                        f'when no ground true boxes')\n                elif 'cls' in key:\n                    self.assertGreater(_loss, 0,\n                                       f'{key}({_loss}) should be non-zero')\n\n            # When truth is non-empty then both cls and box loss should\n            # be nonzero for random inputs\n            gt_instances = InstanceData()\n            gt_instances.bboxes = torch.Tensor(\n                [[23.6667, 23.8757, 238.6326, 151.8874]])\n            gt_instances.labels = torch.LongTensor([2])\n            data_sample.gt_instances = gt_instances\n            batch_data_samples_2 = [data_sample]\n            one_gt_losses = model.loss(\n                random_image, batch_data_samples=batch_data_samples_2)\n            for loss in one_gt_losses.values():\n                self.assertGreater(\n                    loss.item(), 0,\n                    'cls loss, or box loss, or iou loss should be non-zero')\n\n            model.eval()\n            # test _forward\n            model._forward(\n                random_image, batch_data_samples=batch_data_samples_2)\n            # test only predict\n            model.predict(\n                random_image,\n                batch_data_samples=batch_data_samples_2,\n                rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_glip.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestGLIP(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    @parameterized.expand(\n        ['glip/glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py'])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.backbone)\n        self.assertTrue(detector.language_model)\n        self.assertTrue(detector.neck)\n        self.assertTrue(detector.bbox_head)\n\n    @parameterized.expand([\n        ('glip/glip_atss_swin-t_a_fpn_dyhead_pretrain_obj365.py', ('cpu',\n                                                                   'cuda'))\n    ])\n    def test_glip_forward_predict_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            # test custom_entities is True\n            packed_inputs = demo_mm_inputs(\n                2, [[3, 128, 128], [3, 125, 130]],\n                texts=['a', 'b'],\n                custom_entities=True)\n            data = detector.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            detector.eval()\n            with torch.no_grad():\n                batch_results = detector.forward(**data, mode='predict')\n                self.assertEqual(len(batch_results), 2)\n                self.assertIsInstance(batch_results[0], DetDataSample)\n\n            # test custom_entities is False\n            # packed_inputs = demo_mm_inputs(\n            #     2, [[3, 128, 128], [3, 125, 130]],\n            #     texts=['a', 'b'],\n            #     custom_entities=False)\n            # data = detector.data_preprocessor(packed_inputs, False)\n            # # Test forward test\n            # detector.eval()\n            # with torch.no_grad():\n            #     batch_results = detector.forward(**data, mode='predict')\n            #     self.assertEqual(len(batch_results), 2)\n            #     self.assertIsInstance(batch_results[0], DetDataSample)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_kd_single_stage.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet import *  # noqa\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestKDSingleStageDetector(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    @parameterized.expand(['ld/ld_r18-gflv1-r101_fpn_1x_coco.py'])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.backbone)\n        self.assertTrue(detector.neck)\n        self.assertTrue(detector.bbox_head)\n\n    @parameterized.expand([('ld/ld_r18-gflv1-r101_fpn_1x_coco.py', ('cpu',\n                                                                    'cuda'))])\n    def test_single_stage_forward_train(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, True)\n            # Test forward train\n            losses = detector.forward(**data, mode='loss')\n            self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([('ld/ld_r18-gflv1-r101_fpn_1x_coco.py', ('cpu',\n                                                                    'cuda'))])\n    def test_single_stage_forward_test(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, False)\n\n            # Test forward test\n            detector.eval()\n            with torch.no_grad():\n                batch_results = detector.forward(**data, mode='predict')\n                self.assertEqual(len(batch_results), 2)\n                self.assertIsInstance(batch_results[0], DetDataSample)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_maskformer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing._utils import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestMaskFormer(unittest.TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    def _create_model_cfg(self):\n        cfg_path = 'maskformer/maskformer_r50_ms-16xb1-75e_coco.py'\n        model_cfg = get_detector_cfg(cfg_path)\n        base_channels = 32\n        model_cfg.backbone.depth = 18\n        model_cfg.backbone.init_cfg = None\n        model_cfg.backbone.base_channels = base_channels\n        model_cfg.panoptic_head.in_channels = [\n            base_channels * 2**i for i in range(4)\n        ]\n        model_cfg.panoptic_head.feat_channels = base_channels\n        model_cfg.panoptic_head.out_channels = base_channels\n        model_cfg.panoptic_head.pixel_decoder.encoder.\\\n            layer_cfg.self_attn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.pixel_decoder.encoder.\\\n            layer_cfg.ffn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.pixel_decoder.encoder.\\\n            layer_cfg.ffn_cfg.feedforward_channels = base_channels * 8\n        model_cfg.panoptic_head.pixel_decoder.\\\n            positional_encoding.num_feats = base_channels // 2\n        model_cfg.panoptic_head.positional_encoding.\\\n            num_feats = base_channels // 2\n        model_cfg.panoptic_head.transformer_decoder.\\\n            layer_cfg.self_attn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.transformer_decoder. \\\n            layer_cfg.cross_attn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.transformer_decoder.\\\n            layer_cfg.ffn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.transformer_decoder.\\\n            layer_cfg.ffn_cfg.feedforward_channels = base_channels * 8\n        return model_cfg\n\n    def test_init(self):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n        detector.init_weights()\n        assert detector.backbone\n        assert detector.panoptic_head\n\n    @parameterized.expand([('cpu', ), ('cuda', )])\n    def test_forward_loss_mode(self, device):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n\n        packed_inputs = demo_mm_inputs(\n            2,\n            image_shapes=[(3, 128, 127), (3, 91, 92)],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=True)\n        data = detector.data_preprocessor(packed_inputs, True)\n        # Test loss mode\n        losses = detector.forward(**data, mode='loss')\n        self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([('cpu', ), ('cuda', )])\n    def test_forward_predict_mode(self, device):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n        packed_inputs = demo_mm_inputs(\n            2,\n            image_shapes=[(3, 128, 127), (3, 91, 92)],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=True)\n        data = detector.data_preprocessor(packed_inputs, False)\n        # Test forward test\n        detector.eval()\n        with torch.no_grad():\n            batch_results = detector.forward(**data, mode='predict')\n            self.assertEqual(len(batch_results), 2)\n            self.assertIsInstance(batch_results[0], DetDataSample)\n\n    @parameterized.expand([('cpu', ), ('cuda', )])\n    def test_forward_tensor_mode(self, device):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 128, 128], [3, 125, 130]],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=True)\n        data = detector.data_preprocessor(packed_inputs, False)\n        out = detector.forward(**data, mode='tensor')\n        self.assertIsInstance(out, tuple)\n\n\nclass TestMask2Former(unittest.TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    def _create_model_cfg(self, cfg_path):\n        model_cfg = get_detector_cfg(cfg_path)\n        base_channels = 32\n        model_cfg.backbone.depth = 18\n        model_cfg.backbone.init_cfg = None\n        model_cfg.backbone.base_channels = base_channels\n        model_cfg.panoptic_head.in_channels = [\n            base_channels * 2**i for i in range(4)\n        ]\n        model_cfg.panoptic_head.feat_channels = base_channels\n        model_cfg.panoptic_head.out_channels = base_channels\n        model_cfg.panoptic_head.pixel_decoder.encoder.\\\n            layer_cfg.self_attn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.pixel_decoder.encoder.\\\n            layer_cfg.ffn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.pixel_decoder.encoder.\\\n            layer_cfg.ffn_cfg.feedforward_channels = base_channels * 4\n        model_cfg.panoptic_head.pixel_decoder.\\\n            positional_encoding.num_feats = base_channels // 2\n        model_cfg.panoptic_head.positional_encoding.\\\n            num_feats = base_channels // 2\n        model_cfg.panoptic_head.transformer_decoder.\\\n            layer_cfg.self_attn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.transformer_decoder. \\\n            layer_cfg.cross_attn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.transformer_decoder.\\\n            layer_cfg.ffn_cfg.embed_dims = base_channels\n        model_cfg.panoptic_head.transformer_decoder.\\\n            layer_cfg.ffn_cfg.feedforward_channels = base_channels * 8\n\n        return model_cfg\n\n    def test_init(self):\n        model_cfg = self._create_model_cfg(\n            'mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py')\n        detector = MODELS.build(model_cfg)\n        detector.init_weights()\n        assert detector.backbone\n        assert detector.panoptic_head\n\n    @parameterized.expand([\n        ('cpu', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py'),\n        ('cpu', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco.py'),\n        ('cuda', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py'),\n        ('cuda', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco.py')\n    ])\n    def test_forward_loss_mode(self, device, cfg_path):\n        print(device, cfg_path)\n        with_semantic = 'panoptic' in cfg_path\n        model_cfg = self._create_model_cfg(cfg_path)\n        detector = MODELS.build(model_cfg)\n\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n\n        packed_inputs = demo_mm_inputs(\n            2,\n            image_shapes=[(3, 128, 127), (3, 91, 92)],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=with_semantic)\n        data = detector.data_preprocessor(packed_inputs, True)\n        # Test loss mode\n        losses = detector.forward(**data, mode='loss')\n        self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([\n        ('cpu', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py'),\n        ('cpu', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco.py'),\n        ('cuda', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py'),\n        ('cuda', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco.py')\n    ])\n    def test_forward_predict_mode(self, device, cfg_path):\n        with_semantic = 'panoptic' in cfg_path\n        model_cfg = self._create_model_cfg(cfg_path)\n        detector = MODELS.build(model_cfg)\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n        packed_inputs = demo_mm_inputs(\n            2,\n            image_shapes=[(3, 128, 127), (3, 91, 92)],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=with_semantic)\n        data = detector.data_preprocessor(packed_inputs, False)\n        # Test forward test\n        detector.eval()\n        with torch.no_grad():\n            batch_results = detector.forward(**data, mode='predict')\n            self.assertEqual(len(batch_results), 2)\n            self.assertIsInstance(batch_results[0], DetDataSample)\n\n    @parameterized.expand([\n        ('cpu', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py'),\n        ('cpu', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco.py'),\n        ('cuda', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco-panoptic.py'),\n        ('cuda', 'mask2former/mask2former_r50_8xb2-lsj-50e_coco.py')\n    ])\n    def test_forward_tensor_mode(self, device, cfg_path):\n        with_semantic = 'panoptic' in cfg_path\n        model_cfg = self._create_model_cfg(cfg_path)\n        detector = MODELS.build(model_cfg)\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 128, 128], [3, 125, 130]],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=with_semantic)\n        data = detector.data_preprocessor(packed_inputs, False)\n        out = detector.forward(**data, mode='tensor')\n        self.assertIsInstance(out, tuple)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_panoptic_two_stage_segmentor.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing._utils import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestTwoStagePanopticSegmentor(unittest.TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    def _create_model_cfg(self):\n        cfg_file = 'panoptic_fpn/panoptic-fpn_r50_fpn_1x_coco.py'\n        model_cfg = get_detector_cfg(cfg_file)\n        model_cfg.backbone.depth = 18\n        model_cfg.neck.in_channels = [64, 128, 256, 512]\n        model_cfg.backbone.init_cfg = None\n        return model_cfg\n\n    def test_init(self):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n        assert detector.backbone\n        assert detector.neck\n        assert detector.rpn_head\n        assert detector.roi_head\n        assert detector.roi_head.mask_head\n        assert detector.with_semantic_head\n        assert detector.with_panoptic_fusion_head\n\n    @parameterized.expand([('cpu', ), ('cuda', )])\n    def test_forward_loss_mode(self, device):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n\n        packed_inputs = demo_mm_inputs(\n            2,\n            image_shapes=[(3, 128, 127), (3, 91, 92)],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=True)\n        data = detector.data_preprocessor(packed_inputs, True)\n        # Test loss mode\n        losses = detector.forward(**data, mode='loss')\n        self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([('cpu', ), ('cuda', )])\n    def test_forward_predict_mode(self, device):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n        packed_inputs = demo_mm_inputs(\n            2,\n            image_shapes=[(3, 128, 127), (3, 91, 92)],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=True)\n        data = detector.data_preprocessor(packed_inputs, False)\n        # Test forward test\n        detector.eval()\n        with torch.no_grad():\n            batch_results = detector.forward(**data, mode='predict')\n            self.assertEqual(len(batch_results), 2)\n            self.assertIsInstance(batch_results[0], DetDataSample)\n\n    @parameterized.expand([('cpu', ), ('cuda', )])\n    def test_forward_tensor_mode(self, device):\n        model_cfg = self._create_model_cfg()\n        detector = MODELS.build(model_cfg)\n        if device == 'cuda' and not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.to(device)\n\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 128, 128], [3, 125, 130]],\n            sem_seg_output_strides=1,\n            with_mask=True,\n            with_semantic=True)\n        data = detector.data_preprocessor(packed_inputs, False)\n        out = detector.forward(**data, mode='tensor')\n        self.assertIsInstance(out, tuple)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_rpn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestRPN(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    @parameterized.expand(['rpn/rpn_r50_fpn_1x_coco.py'])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.backbone)\n        self.assertTrue(detector.neck)\n        self.assertTrue(detector.bbox_head)\n\n        # if rpn.num_classes > 1, force set rpn.num_classes = 1\n        model.rpn_head.num_classes = 2\n        detector = MODELS.build(model)\n        self.assertEqual(detector.bbox_head.num_classes, 1)\n\n    @parameterized.expand([('rpn/rpn_r50_fpn_1x_coco.py', ('cpu', 'cuda'))])\n    def test_rpn_forward_loss_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, True)\n            # Test forward train\n            losses = detector.forward(**data, mode='loss')\n            self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([('rpn/rpn_r50_fpn_1x_coco.py', ('cpu', 'cuda'))])\n    def test_rpn_forward_predict_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            detector.eval()\n            with torch.no_grad():\n                batch_results = detector.forward(**data, mode='predict')\n                self.assertEqual(len(batch_results), 2)\n                self.assertIsInstance(batch_results[0], DetDataSample)\n\n    @parameterized.expand([('rpn/rpn_r50_fpn_1x_coco.py', ('cpu', 'cuda'))])\n    def test_rpn_forward_tensor_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, False)\n            batch_results = detector.forward(**data, mode='tensor')\n            self.assertIsInstance(batch_results, tuple)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_semi_base.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nfrom mmengine.registry import MODELS\nfrom parameterized import parameterized\n\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n\nclass TestSemiBase(TestCase):\n\n    @parameterized.expand([\n        'soft_teacher/'\n        'soft-teacher_faster-rcnn_r50-caffe_fpn_180k_semi-0.1-coco.py',\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.detector.backbone.depth = 18\n        model.detector.neck.in_channels = [64, 128, 256, 512]\n        model.detector.backbone.init_cfg = None\n\n        model = MODELS.build(model)\n        self.assertTrue(model.teacher.backbone)\n        self.assertTrue(model.teacher.neck)\n        self.assertTrue(model.teacher.rpn_head)\n        self.assertTrue(model.teacher.roi_head)\n        self.assertTrue(model.student.backbone)\n        self.assertTrue(model.student.neck)\n        self.assertTrue(model.student.rpn_head)\n        self.assertTrue(model.student.roi_head)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_single_stage.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom parameterized import parameterized\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestSingleStageDetector(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    @parameterized.expand([\n        'retinanet/retinanet_r18_fpn_1x_coco.py',\n        'centernet/centernet_r18_8xb16-crop512-140e_coco.py',\n        'fsaf/fsaf_r50_fpn_1x_coco.py',\n        'yolox/yolox_tiny_8xb8-300e_coco.py',\n        'yolo/yolov3_mobilenetv2_8xb24-320-300e_coco.py',\n        'reppoints/reppoints-minmax_r50_fpn-gn_head-gn_1x_coco.py',\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.backbone)\n        self.assertTrue(detector.neck)\n        self.assertTrue(detector.bbox_head)\n\n    @parameterized.expand([\n        ('retinanet/retinanet_r18_fpn_1x_coco.py', ('cpu', 'cuda')),\n        ('centernet/centernet_r18_8xb16-crop512-140e_coco.py', ('cpu',\n                                                                'cuda')),\n        ('yolox/yolox_tiny_8xb8-300e_coco.py', ('cpu', 'cuda')),\n        ('yolo/yolov3_mobilenetv2_8xb24-320-300e_coco.py', ('cpu', 'cuda')),\n    ])\n    def test_single_stage_forward_loss_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_single_stage_forward_loss_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n            detector.init_weights()\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, True)\n            losses = detector.forward(**data, mode='loss')\n            self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([\n        ('retinanet/retinanet_r18_fpn_1x_coco.py', ('cpu', 'cuda')),\n        ('centernet/centernet_r18_8xb16-crop512-140e_coco.py', ('cpu',\n                                                                'cuda')),\n        ('yolox/yolox_tiny_8xb8-300e_coco.py', ('cpu', 'cuda')),\n        ('yolo/yolov3_mobilenetv2_8xb24-320-300e_coco.py', ('cpu', 'cuda')),\n    ])\n    def test_single_stage_forward_predict_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            detector.eval()\n            with torch.no_grad():\n                batch_results = detector.forward(**data, mode='predict')\n                self.assertEqual(len(batch_results), 2)\n                self.assertIsInstance(batch_results[0], DetDataSample)\n\n    @parameterized.expand([\n        ('retinanet/retinanet_r18_fpn_1x_coco.py', ('cpu', 'cuda')),\n        ('centernet/centernet_r18_8xb16-crop512-140e_coco.py', ('cpu',\n                                                                'cuda')),\n        ('yolox/yolox_tiny_8xb8-300e_coco.py', ('cpu', 'cuda')),\n        ('yolo/yolov3_mobilenetv2_8xb24-320-300e_coco.py', ('cpu', 'cuda')),\n    ])\n    def test_single_stage_forward_tensor_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, False)\n            batch_results = detector.forward(**data, mode='tensor')\n            self.assertIsInstance(batch_results, tuple)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_single_stage_instance_seg.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestSingleStageInstanceSegmentor(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    @parameterized.expand([\n        'solo/solo_r50_fpn_1x_coco.py',\n        'solov2/solov2_r50_fpn_1x_coco.py',\n        'yolact/yolact_r50_1xb8-55e_coco.py',\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.backbone)\n        self.assertTrue(detector.neck)\n        self.assertTrue(detector.mask_head)\n        if detector.with_bbox:\n            self.assertTrue(detector.bbox_head)\n\n    @parameterized.expand([\n        ('solo/solo_r50_fpn_1x_coco.py', ('cpu', 'cuda')),\n        ('solov2/solov2-light_r18_fpn_ms-3x_coco.py', ('cpu', 'cuda')),\n        ('yolact/yolact_r50_1xb8-55e_coco.py', ('cpu', 'cuda')),\n    ])\n    def test_single_stage_forward_loss_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n            detector.init_weights()\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(\n                2, [[3, 128, 128], [3, 125, 130]], with_mask=True)\n            data = detector.data_preprocessor(packed_inputs, True)\n            losses = detector.forward(**data, mode='loss')\n            self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([\n        ('solo/decoupled-solo-light_r50_fpn_3x_coco.py', ('cpu', 'cuda')),\n        ('yolact/yolact_r50_1xb8-55e_coco.py', ('cpu', 'cuda')),\n    ])\n    def test_single_stage_forward_predict_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(\n                2, [[3, 128, 128], [3, 125, 130]], with_mask=True)\n            data = detector.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            detector.eval()\n            with torch.no_grad():\n                batch_results = detector.forward(**data, mode='predict')\n                self.assertEqual(len(batch_results), 2)\n                self.assertIsInstance(batch_results[0], DetDataSample)\n\n    @parameterized.expand([\n        ('solo/solo_r50_fpn_1x_coco.py', ('cpu', 'cuda')),\n        ('solov2/solov2_r50_fpn_1x_coco.py', ('cpu', 'cuda')),\n        ('yolact/yolact_r50_1xb8-55e_coco.py', ('cpu', 'cuda')),\n    ])\n    def test_single_stage_forward_tensor_mode(self, cfg_file, devices):\n        model = get_detector_cfg(cfg_file)\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            detector = MODELS.build(model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                detector = detector.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = detector.data_preprocessor(packed_inputs, False)\n            batch_results = detector.forward(**data, mode='tensor')\n            self.assertIsInstance(batch_results, tuple)\n"
  },
  {
    "path": "tests/test_models/test_detectors/test_two_stage.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import demo_mm_inputs, get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestTwoStageBBox(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    @parameterized.expand([\n        'faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py',\n        'cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py',\n        'sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py',\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.backbone)\n        self.assertTrue(detector.neck)\n        self.assertTrue(detector.rpn_head)\n        self.assertTrue(detector.roi_head)\n\n        # if rpn.num_classes > 1, force set rpn.num_classes = 1\n        if hasattr(model.rpn_head, 'num_classes'):\n            model.rpn_head.num_classes = 2\n            detector = MODELS.build(model)\n            self.assertEqual(detector.rpn_head.num_classes, 1)\n\n    @parameterized.expand([\n        'faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py',\n        'cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py',\n        'sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py',\n    ])\n    def test_two_stage_forward_loss_mode(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n\n        if not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.cuda()\n\n        packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n\n        data = detector.data_preprocessor(packed_inputs, True)\n        # Test loss mode\n        losses = detector.forward(**data, mode='loss')\n        self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([\n        'faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py',\n        'cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py',\n        'sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py',\n    ])\n    def test_two_stage_forward_predict_mode(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n\n        if not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.cuda()\n\n        packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n        data = detector.data_preprocessor(packed_inputs, False)\n        # Test forward test\n        detector.eval()\n        with torch.no_grad():\n            with torch.no_grad():\n                batch_results = detector.forward(**data, mode='predict')\n            self.assertEqual(len(batch_results), 2)\n            self.assertIsInstance(batch_results[0], DetDataSample)\n\n    # TODO: Awaiting refactoring\n    # @parameterized.expand([\n    #     'faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py',\n    #     'cascade_rcnn/cascade-rcnn_r50_fpn_1x_coco.py',\n    #     'sparse_rcnn/sparse-rcnn_r50_fpn_1x_coco.py',\n    # ])\n    # def test_two_stage_forward_tensor_mode(self, cfg_file):\n    #     model = get_detector_cfg(cfg_file)\n    #     # backbone convert to ResNet18\n    #     model.backbone.depth = 18\n    #     model.neck.in_channels = [64, 128, 256, 512]\n    #     model.backbone.init_cfg = None\n    #\n    #     from mmdet.models import build_detector\n    #     detector = build_detector(model)\n    #\n    #     if not torch.cuda.is_available():\n    #         return unittest.skip('test requires GPU and torch+cuda')\n    #     detector = detector.cuda()\n    #\n    #     packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n\n    # data = detector.data_preprocessor(packed_inputs, False)\n\n    # out = detector.forward(**data, mode='tensor')\n    # self.assertIsInstance(out, tuple)\n\n\nclass TestTwoStageMask(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    @parameterized.expand([\n        'mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py',\n        'cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py',\n        'queryinst/queryinst_r50_fpn_1x_coco.py'\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n        self.assertTrue(detector.backbone)\n        self.assertTrue(detector.neck)\n        self.assertTrue(detector.rpn_head)\n        self.assertTrue(detector.roi_head)\n        self.assertTrue(detector.roi_head.mask_head)\n\n        # if rpn.num_classes > 1, force set rpn.num_classes = 1\n        if hasattr(model.rpn_head, 'num_classes'):\n            model.rpn_head.num_classes = 2\n            detector = MODELS.build(model)\n            self.assertEqual(detector.rpn_head.num_classes, 1)\n\n    @parameterized.expand([\n        'mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py',\n        'cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py',\n        'queryinst/queryinst_r50_fpn_1x_coco.py'\n    ])\n    def test_two_stage_forward_loss_mode(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n\n        if not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.cuda()\n\n        packed_inputs = demo_mm_inputs(\n            2, [[3, 128, 128], [3, 125, 130]], with_mask=True)\n        data = detector.data_preprocessor(packed_inputs, True)\n        # Test loss mode\n        losses = detector.forward(**data, mode='loss')\n        self.assertIsInstance(losses, dict)\n\n    @parameterized.expand([\n        'mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py',\n        'cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py',\n        'queryinst/queryinst_r50_fpn_1x_coco.py'\n    ])\n    def test_two_stage_forward_predict_mode(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        # backbone convert to ResNet18\n        model.backbone.depth = 18\n        model.neck.in_channels = [64, 128, 256, 512]\n        model.backbone.init_cfg = None\n\n        from mmdet.registry import MODELS\n        detector = MODELS.build(model)\n\n        if not torch.cuda.is_available():\n            return unittest.skip('test requires GPU and torch+cuda')\n        detector = detector.cuda()\n\n        packed_inputs = demo_mm_inputs(2, [[3, 256, 256], [3, 255, 260]])\n        data = detector.data_preprocessor(packed_inputs, False)\n        # Test forward test\n        detector.eval()\n        with torch.no_grad():\n            batch_results = detector.forward(**data, mode='predict')\n            self.assertEqual(len(batch_results), 2)\n            self.assertIsInstance(batch_results[0], DetDataSample)\n\n    # TODO: Awaiting refactoring\n    # @parameterized.expand([\n    #     'mask_rcnn/mask-rcnn_r50_fpn_1x_coco.py',\n    #     'cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py',\n    #     'queryinst/queryinst_r50_fpn_1x_coco.py'\n    # ])\n    # def test_two_stage_forward_tensor_mode(self, cfg_file):\n    #     model = get_detector_cfg(cfg_file)\n    #     # backbone convert to ResNet18\n    #     model.backbone.depth = 18\n    #     model.neck.in_channels = [64, 128, 256, 512]\n    #     model.backbone.init_cfg = None\n    #\n    #     from mmdet.models import build_detector\n    #     detector = build_detector(model)\n    #\n    #     if not torch.cuda.is_available():\n    #         return unittest.skip('test requires GPU and torch+cuda')\n    #     detector = detector.cuda()\n    #\n    #     packed_inputs = demo_mm_inputs(\n    #         2, [[3, 128, 128], [3, 125, 130]], with_mask=True)\n    #     data = detector.data_preprocessor(packed_inputs, False)\n    #\n    #     # out = detector.forward(**data, mode='tensor')\n    #     # self.assertIsInstance(out, tuple)\n"
  },
  {
    "path": "tests/test_models/test_layers/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_models/test_layers/test_brick_wrappers.py",
    "content": "from unittest.mock import patch\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mmdet.models.layers import AdaptiveAvgPool2d, adaptive_avg_pool2d\n\nif torch.__version__ != 'parrots':\n    torch_version = '1.7'\nelse:\n    torch_version = 'parrots'\n\n\n@patch('torch.__version__', torch_version)\ndef test_adaptive_avg_pool2d():\n    # Test the empty batch dimension\n    # Test the two input conditions\n    x_empty = torch.randn(0, 3, 4, 5)\n    # 1. tuple[int, int]\n    wrapper_out = adaptive_avg_pool2d(x_empty, (2, 2))\n    assert wrapper_out.shape == (0, 3, 2, 2)\n    # 2. int\n    wrapper_out = adaptive_avg_pool2d(x_empty, 2)\n    assert wrapper_out.shape == (0, 3, 2, 2)\n\n    # wrapper op with 3-dim input\n    x_normal = torch.randn(3, 3, 4, 5)\n    wrapper_out = adaptive_avg_pool2d(x_normal, (2, 2))\n    ref_out = F.adaptive_avg_pool2d(x_normal, (2, 2))\n    assert wrapper_out.shape == (3, 3, 2, 2)\n    assert torch.equal(wrapper_out, ref_out)\n\n    wrapper_out = adaptive_avg_pool2d(x_normal, 2)\n    ref_out = F.adaptive_avg_pool2d(x_normal, 2)\n    assert wrapper_out.shape == (3, 3, 2, 2)\n    assert torch.equal(wrapper_out, ref_out)\n\n\n@patch('torch.__version__', torch_version)\ndef test_AdaptiveAvgPool2d():\n    # Test the empty batch dimension\n    x_empty = torch.randn(0, 3, 4, 5)\n    # Test the four input conditions\n    # 1. tuple[int, int]\n    wrapper = AdaptiveAvgPool2d((2, 2))\n    wrapper_out = wrapper(x_empty)\n    assert wrapper_out.shape == (0, 3, 2, 2)\n\n    # 2. int\n    wrapper = AdaptiveAvgPool2d(2)\n    wrapper_out = wrapper(x_empty)\n    assert wrapper_out.shape == (0, 3, 2, 2)\n\n    # 3. tuple[None, int]\n    wrapper = AdaptiveAvgPool2d((None, 2))\n    wrapper_out = wrapper(x_empty)\n    assert wrapper_out.shape == (0, 3, 4, 2)\n\n    # 3. tuple[int, None]\n    wrapper = AdaptiveAvgPool2d((2, None))\n    wrapper_out = wrapper(x_empty)\n    assert wrapper_out.shape == (0, 3, 2, 5)\n\n    # Test the normal batch dimension\n    x_normal = torch.randn(3, 3, 4, 5)\n    wrapper = AdaptiveAvgPool2d((2, 2))\n    ref = nn.AdaptiveAvgPool2d((2, 2))\n    wrapper_out = wrapper(x_normal)\n    ref_out = ref(x_normal)\n    assert wrapper_out.shape == (3, 3, 2, 2)\n    assert torch.equal(wrapper_out, ref_out)\n\n    wrapper = AdaptiveAvgPool2d(2)\n    ref = nn.AdaptiveAvgPool2d(2)\n    wrapper_out = wrapper(x_normal)\n    ref_out = ref(x_normal)\n    assert wrapper_out.shape == (3, 3, 2, 2)\n    assert torch.equal(wrapper_out, ref_out)\n\n    wrapper = AdaptiveAvgPool2d((None, 2))\n    ref = nn.AdaptiveAvgPool2d((None, 2))\n    wrapper_out = wrapper(x_normal)\n    ref_out = ref(x_normal)\n    assert wrapper_out.shape == (3, 3, 4, 2)\n    assert torch.equal(wrapper_out, ref_out)\n\n    wrapper = AdaptiveAvgPool2d((2, None))\n    ref = nn.AdaptiveAvgPool2d((2, None))\n    wrapper_out = wrapper(x_normal)\n    ref_out = ref(x_normal)\n    assert wrapper_out.shape == (3, 3, 2, 5)\n    assert torch.equal(wrapper_out, ref_out)\n"
  },
  {
    "path": "tests/test_models/test_layers/test_conv_upsample.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.layers import ConvUpsample\n\n\n@pytest.mark.parametrize('num_layers', [0, 1, 2])\ndef test_conv_upsample(num_layers):\n    num_upsample = num_layers if num_layers > 0 else 0\n    num_layers = num_layers if num_layers > 0 else 1\n    layer = ConvUpsample(\n        10,\n        5,\n        num_layers=num_layers,\n        num_upsample=num_upsample,\n        conv_cfg=None,\n        norm_cfg=None)\n\n    size = 5\n    x = torch.randn((1, 10, size, size))\n    size = size * pow(2, num_upsample)\n    x = layer(x)\n    assert x.shape[-2:] == (size, size)\n"
  },
  {
    "path": "tests/test_models/test_layers/test_ema.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport itertools\nimport math\nfrom unittest import TestCase\n\nimport torch\nimport torch.nn as nn\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.layers import ExpMomentumEMA\n\n\nclass TestEMA(TestCase):\n\n    def test_exp_momentum_ema(self):\n        model = nn.Sequential(nn.Conv2d(1, 5, kernel_size=3), nn.Linear(5, 10))\n        # Test invalid gamma\n        with self.assertRaisesRegex(AssertionError,\n                                    'gamma must be greater than 0'):\n            ExpMomentumEMA(model, gamma=-1)\n\n        # Test EMA\n        model = torch.nn.Sequential(\n            torch.nn.Conv2d(1, 5, kernel_size=3), torch.nn.Linear(5, 10))\n        momentum = 0.1\n        gamma = 4\n\n        ema_model = ExpMomentumEMA(model, momentum=momentum, gamma=gamma)\n        averaged_params = [\n            torch.zeros_like(param) for param in model.parameters()\n        ]\n        n_updates = 10\n        for i in range(n_updates):\n            updated_averaged_params = []\n            for p, p_avg in zip(model.parameters(), averaged_params):\n                p.detach().add_(torch.randn_like(p))\n                if i == 0:\n                    updated_averaged_params.append(p.clone())\n                else:\n                    m = (1 - momentum) * math.exp(-(1 + i) / gamma) + momentum\n                    updated_averaged_params.append(\n                        (p_avg * (1 - m) + p * m).clone())\n            ema_model.update_parameters(model)\n            averaged_params = updated_averaged_params\n\n        for p_target, p_ema in zip(averaged_params, ema_model.parameters()):\n            assert_allclose(p_target, p_ema)\n\n    def test_exp_momentum_ema_update_buffer(self):\n        model = nn.Sequential(\n            nn.Conv2d(1, 5, kernel_size=3), nn.BatchNorm2d(5, momentum=0.3),\n            nn.Linear(5, 10))\n        # Test invalid gamma\n        with self.assertRaisesRegex(AssertionError,\n                                    'gamma must be greater than 0'):\n            ExpMomentumEMA(model, gamma=-1)\n\n        # Test EMA with momentum annealing.\n        momentum = 0.1\n        gamma = 4\n\n        ema_model = ExpMomentumEMA(\n            model, gamma=gamma, momentum=momentum, update_buffers=True)\n        averaged_params = [\n            torch.zeros_like(param)\n            for param in itertools.chain(model.parameters(), model.buffers())\n            if param.size() != torch.Size([])\n        ]\n        n_updates = 10\n        for i in range(n_updates):\n            updated_averaged_params = []\n            params = [\n                param for param in itertools.chain(model.parameters(),\n                                                   model.buffers())\n                if param.size() != torch.Size([])\n            ]\n            for p, p_avg in zip(params, averaged_params):\n                p.detach().add_(torch.randn_like(p))\n                if i == 0:\n                    updated_averaged_params.append(p.clone())\n                else:\n                    m = (1 - momentum) * math.exp(-(1 + i) / gamma) + momentum\n                    updated_averaged_params.append(\n                        (p_avg * (1 - m) + p * m).clone())\n            ema_model.update_parameters(model)\n            averaged_params = updated_averaged_params\n\n        ema_params = [\n            param for param in itertools.chain(ema_model.module.parameters(),\n                                               ema_model.module.buffers())\n            if param.size() != torch.Size([])\n        ]\n        for p_target, p_ema in zip(averaged_params, ema_params):\n            assert_allclose(p_target, p_ema)\n"
  },
  {
    "path": "tests/test_models/test_layers/test_inverted_residual.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\nfrom mmcv.cnn import is_norm\nfrom torch.nn.modules import GroupNorm\n\nfrom mmdet.models.layers import InvertedResidual, SELayer\n\n\ndef test_inverted_residual():\n\n    with pytest.raises(AssertionError):\n        # stride must be in [1, 2]\n        InvertedResidual(16, 16, 32, stride=3)\n\n    with pytest.raises(AssertionError):\n        # se_cfg must be None or dict\n        InvertedResidual(16, 16, 32, se_cfg=list())\n\n    with pytest.raises(AssertionError):\n        # in_channeld and mid_channels must be the same if\n        # with_expand_conv is False\n        InvertedResidual(16, 16, 32, with_expand_conv=False)\n\n    # Test InvertedResidual forward, stride=1\n    block = InvertedResidual(16, 16, 32, stride=1)\n    x = torch.randn(1, 16, 56, 56)\n    x_out = block(x)\n    assert getattr(block, 'se', None) is None\n    assert block.with_res_shortcut\n    assert x_out.shape == torch.Size((1, 16, 56, 56))\n\n    # Test InvertedResidual forward, stride=2\n    block = InvertedResidual(16, 16, 32, stride=2)\n    x = torch.randn(1, 16, 56, 56)\n    x_out = block(x)\n    assert not block.with_res_shortcut\n    assert x_out.shape == torch.Size((1, 16, 28, 28))\n\n    # Test InvertedResidual forward with se layer\n    se_cfg = dict(channels=32)\n    block = InvertedResidual(16, 16, 32, stride=1, se_cfg=se_cfg)\n    x = torch.randn(1, 16, 56, 56)\n    x_out = block(x)\n    assert isinstance(block.se, SELayer)\n    assert x_out.shape == torch.Size((1, 16, 56, 56))\n\n    # Test InvertedResidual forward, with_expand_conv=False\n    block = InvertedResidual(32, 16, 32, with_expand_conv=False)\n    x = torch.randn(1, 32, 56, 56)\n    x_out = block(x)\n    assert getattr(block, 'expand_conv', None) is None\n    assert x_out.shape == torch.Size((1, 16, 56, 56))\n\n    # Test InvertedResidual forward with GroupNorm\n    block = InvertedResidual(\n        16, 16, 32, norm_cfg=dict(type='GN', num_groups=2))\n    x = torch.randn(1, 16, 56, 56)\n    x_out = block(x)\n    for m in block.modules():\n        if is_norm(m):\n            assert isinstance(m, GroupNorm)\n    assert x_out.shape == torch.Size((1, 16, 56, 56))\n\n    # Test InvertedResidual forward with HSigmoid\n    block = InvertedResidual(16, 16, 32, act_cfg=dict(type='HSigmoid'))\n    x = torch.randn(1, 16, 56, 56)\n    x_out = block(x)\n    assert x_out.shape == torch.Size((1, 16, 56, 56))\n\n    # Test InvertedResidual forward with checkpoint\n    block = InvertedResidual(16, 16, 32, with_cp=True)\n    x = torch.randn(1, 16, 56, 56)\n    x_out = block(x)\n    assert block.with_cp\n    assert x_out.shape == torch.Size((1, 16, 56, 56))\n"
  },
  {
    "path": "tests/test_models/test_layers/test_plugins.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport pytest\nimport torch\nfrom mmengine.config import ConfigDict\n\nfrom mmdet.models.layers import DropBlock\nfrom mmdet.registry import MODELS\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n\ndef test_dropblock():\n    feat = torch.rand(1, 1, 11, 11)\n    drop_prob = 1.0\n    dropblock = DropBlock(drop_prob, block_size=11, warmup_iters=0)\n    out_feat = dropblock(feat)\n    assert (out_feat == 0).all() and out_feat.shape == feat.shape\n    drop_prob = 0.5\n    dropblock = DropBlock(drop_prob, block_size=5, warmup_iters=0)\n    out_feat = dropblock(feat)\n    assert out_feat.shape == feat.shape\n\n    # drop_prob must be (0,1]\n    with pytest.raises(AssertionError):\n        DropBlock(1.5, 3)\n\n    # block_size cannot be an even number\n    with pytest.raises(AssertionError):\n        DropBlock(0.5, 2)\n\n    # warmup_iters cannot be less than 0\n    with pytest.raises(AssertionError):\n        DropBlock(0.5, 3, -1)\n\n\nclass TestPixelDecoder(unittest.TestCase):\n\n    def test_forward(self):\n        base_channels = 64\n        pixel_decoder_cfg = ConfigDict(\n            dict(\n                type='PixelDecoder',\n                in_channels=[base_channels * 2**i for i in range(4)],\n                feat_channels=base_channels,\n                out_channels=base_channels,\n                norm_cfg=dict(type='GN', num_groups=32),\n                act_cfg=dict(type='ReLU')))\n        self = MODELS.build(pixel_decoder_cfg)\n        self.init_weights()\n        img_metas = [{}, {}]\n        feats = [\n            torch.rand(\n                (2, base_channels * 2**i, 4 * 2**(3 - i), 5 * 2**(3 - i)))\n            for i in range(4)\n        ]\n        mask_feature, memory = self(feats, img_metas)\n\n        assert (memory == feats[-1]).all()\n        assert mask_feature.shape == feats[0].shape\n\n\nclass TestTransformerEncoderPixelDecoder(unittest.TestCase):\n\n    def test_forward(self):\n        base_channels = 64\n        pixel_decoder_cfg = ConfigDict(\n            dict(\n                type='TransformerEncoderPixelDecoder',\n                in_channels=[base_channels * 2**i for i in range(4)],\n                feat_channels=base_channels,\n                out_channels=base_channels,\n                norm_cfg=dict(type='GN', num_groups=32),\n                act_cfg=dict(type='ReLU'),\n                encoder=dict(  # DetrTransformerEncoder\n                    num_layers=6,\n                    layer_cfg=dict(  # DetrTransformerEncoderLayer\n                        self_attn_cfg=dict(  # MultiheadAttention\n                            embed_dims=base_channels,\n                            num_heads=8,\n                            attn_drop=0.1,\n                            proj_drop=0.1,\n                            dropout_layer=None,\n                            batch_first=True),\n                        ffn_cfg=dict(\n                            embed_dims=base_channels,\n                            feedforward_channels=base_channels * 8,\n                            num_fcs=2,\n                            act_cfg=dict(type='ReLU', inplace=True),\n                            ffn_drop=0.1,\n                            dropout_layer=None,\n                            add_identity=True),\n                        norm_cfg=dict(type='LN'),\n                        init_cfg=None),\n                    init_cfg=None),\n                positional_encoding=dict(\n                    num_feats=base_channels // 2, normalize=True)))\n        self = MODELS.build(pixel_decoder_cfg)\n        self.init_weights()\n        img_metas = [{\n            'batch_input_shape': (128, 160),\n            'img_shape': (120, 160),\n        }, {\n            'batch_input_shape': (128, 160),\n            'img_shape': (125, 160),\n        }]\n        feats = [\n            torch.rand(\n                (2, base_channels * 2**i, 4 * 2**(3 - i), 5 * 2**(3 - i)))\n            for i in range(4)\n        ]\n        mask_feature, memory = self(feats, img_metas)\n\n        assert memory.shape[-2:] == feats[-1].shape[-2:]\n        assert mask_feature.shape == feats[0].shape\n\n\nclass TestMSDeformAttnPixelDecoder(unittest.TestCase):\n\n    def test_forward(self):\n        base_channels = 64\n        pixel_decoder_cfg = ConfigDict(\n            dict(\n                type='MSDeformAttnPixelDecoder',\n                in_channels=[base_channels * 2**i for i in range(4)],\n                strides=[4, 8, 16, 32],\n                feat_channels=base_channels,\n                out_channels=base_channels,\n                num_outs=3,\n                norm_cfg=dict(type='GN', num_groups=32),\n                act_cfg=dict(type='ReLU'),\n                encoder=dict(  # DeformableDetrTransformerEncoder\n                    num_layers=6,\n                    layer_cfg=dict(  # DeformableDetrTransformerEncoderLayer\n                        self_attn_cfg=dict(  # MultiScaleDeformableAttention\n                            embed_dims=base_channels,\n                            num_heads=8,\n                            num_levels=3,\n                            num_points=4,\n                            im2col_step=64,\n                            dropout=0.0,\n                            batch_first=True,\n                            norm_cfg=None,\n                            init_cfg=None),\n                        ffn_cfg=dict(\n                            embed_dims=base_channels,\n                            feedforward_channels=base_channels * 4,\n                            num_fcs=2,\n                            ffn_drop=0.0,\n                            act_cfg=dict(type='ReLU', inplace=True))),\n                    init_cfg=None),\n                positional_encoding=dict(\n                    num_feats=base_channels // 2, normalize=True),\n                init_cfg=None))\n        self = MODELS.build(pixel_decoder_cfg)\n        self.init_weights()\n        feats = [\n            torch.rand(\n                (2, base_channels * 2**i, 4 * 2**(3 - i), 5 * 2**(3 - i)))\n            for i in range(4)\n        ]\n        mask_feature, multi_scale_features = self(feats)\n\n        assert mask_feature.shape == feats[0].shape\n        assert len(multi_scale_features) == 3\n        multi_scale_features = multi_scale_features[::-1]\n        for i in range(3):\n            assert multi_scale_features[i].shape[-2:] == feats[i +\n                                                               1].shape[-2:]\n"
  },
  {
    "path": "tests/test_models/test_layers/test_position_encoding.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.layers import (LearnedPositionalEncoding,\n                                 SinePositionalEncoding)\n\n\ndef test_sine_positional_encoding(num_feats=16, batch_size=2):\n    # test invalid type of scale\n    with pytest.raises(AssertionError):\n        module = SinePositionalEncoding(\n            num_feats, scale=(3., ), normalize=True)\n\n    module = SinePositionalEncoding(num_feats)\n    h, w = 10, 6\n    mask = (torch.rand(batch_size, h, w) > 0.5).to(torch.int)\n    assert not module.normalize\n    out = module(mask)\n    assert out.shape == (batch_size, num_feats * 2, h, w)\n\n    # set normalize\n    module = SinePositionalEncoding(num_feats, normalize=True)\n    assert module.normalize\n    out = module(mask)\n    assert out.shape == (batch_size, num_feats * 2, h, w)\n\n\ndef test_learned_positional_encoding(num_feats=16,\n                                     row_num_embed=10,\n                                     col_num_embed=10,\n                                     batch_size=2):\n    module = LearnedPositionalEncoding(num_feats, row_num_embed, col_num_embed)\n    assert module.row_embed.weight.shape == (row_num_embed, num_feats)\n    assert module.col_embed.weight.shape == (col_num_embed, num_feats)\n    h, w = 10, 6\n    mask = torch.rand(batch_size, h, w) > 0.5\n    out = module(mask)\n    assert out.shape == (batch_size, num_feats * 2, h, w)\n"
  },
  {
    "path": "tests/test_models/test_layers/test_se_layer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.model import constant_init\n\nfrom mmdet.models.layers import DyReLU, SELayer\n\n\ndef test_se_layer():\n    with pytest.raises(AssertionError):\n        # act_cfg sequence length must equal to 2\n        SELayer(channels=32, act_cfg=(dict(type='ReLU'), ))\n\n    with pytest.raises(AssertionError):\n        # act_cfg sequence must be a tuple of dict\n        SELayer(channels=32, act_cfg=[dict(type='ReLU'), dict(type='ReLU')])\n\n    # Test SELayer forward\n    layer = SELayer(channels=32)\n    layer.init_weights()\n    layer.train()\n\n    x = torch.randn((1, 32, 10, 10))\n    x_out = layer(x)\n    assert x_out.shape == torch.Size((1, 32, 10, 10))\n\n\ndef test_dyrelu():\n    with pytest.raises(AssertionError):\n        # act_cfg sequence length must equal to 2\n        DyReLU(channels=32, act_cfg=(dict(type='ReLU'), ))\n\n    with pytest.raises(AssertionError):\n        # act_cfg sequence must be a tuple of dict\n        DyReLU(channels=32, act_cfg=[dict(type='ReLU'), dict(type='ReLU')])\n\n    # Test DyReLU forward\n    layer = DyReLU(channels=32)\n    layer.init_weights()\n    layer.train()\n    x = torch.randn((1, 32, 10, 10))\n    x_out = layer(x)\n    assert x_out.shape == torch.Size((1, 32, 10, 10))\n\n    # DyReLU should act as standard (static) ReLU\n    # when eliminating the effect of SE-like module\n    layer = DyReLU(channels=32)\n    constant_init(layer.conv2.conv, 0)\n    layer.train()\n    x = torch.randn((1, 32, 10, 10))\n    x_out = layer(x)\n    relu_out = F.relu(x)\n    assert torch.equal(x_out, relu_out)\n"
  },
  {
    "path": "tests/test_models/test_layers/test_transformer.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\nfrom mmengine.config import ConfigDict\n\nfrom mmdet.models.layers.transformer import (AdaptivePadding,\n                                             DDQTransformerDecoder,\n                                             DetrTransformerDecoder,\n                                             DetrTransformerEncoder,\n                                             PatchEmbed, PatchMerging)\n\n\ndef test_adaptive_padding():\n\n    for padding in ('same', 'corner'):\n        kernel_size = 16\n        stride = 16\n        dilation = 1\n        input = torch.rand(1, 1, 15, 17)\n        pool = AdaptivePadding(\n            kernel_size=kernel_size,\n            stride=stride,\n            dilation=dilation,\n            padding=padding)\n        out = pool(input)\n        # padding to divisible by 16\n        assert (out.shape[2], out.shape[3]) == (16, 32)\n        input = torch.rand(1, 1, 16, 17)\n        out = pool(input)\n        # padding to divisible by 16\n        assert (out.shape[2], out.shape[3]) == (16, 32)\n\n        kernel_size = (2, 2)\n        stride = (2, 2)\n        dilation = (1, 1)\n\n        adap_pad = AdaptivePadding(\n            kernel_size=kernel_size,\n            stride=stride,\n            dilation=dilation,\n            padding=padding)\n        input = torch.rand(1, 1, 11, 13)\n        out = adap_pad(input)\n        # padding to divisible by 2\n        assert (out.shape[2], out.shape[3]) == (12, 14)\n\n        kernel_size = (2, 2)\n        stride = (10, 10)\n        dilation = (1, 1)\n\n        adap_pad = AdaptivePadding(\n            kernel_size=kernel_size,\n            stride=stride,\n            dilation=dilation,\n            padding=padding)\n        input = torch.rand(1, 1, 10, 13)\n        out = adap_pad(input)\n        #  no padding\n        assert (out.shape[2], out.shape[3]) == (10, 13)\n\n        kernel_size = (11, 11)\n        adap_pad = AdaptivePadding(\n            kernel_size=kernel_size,\n            stride=stride,\n            dilation=dilation,\n            padding=padding)\n        input = torch.rand(1, 1, 11, 13)\n        out = adap_pad(input)\n        #  all padding\n        assert (out.shape[2], out.shape[3]) == (21, 21)\n\n        # test padding as kernel is (7,9)\n        input = torch.rand(1, 1, 11, 13)\n        stride = (3, 4)\n        kernel_size = (4, 5)\n        dilation = (2, 2)\n        # actually (7, 9)\n        adap_pad = AdaptivePadding(\n            kernel_size=kernel_size,\n            stride=stride,\n            dilation=dilation,\n            padding=padding)\n        dilation_out = adap_pad(input)\n        assert (dilation_out.shape[2], dilation_out.shape[3]) == (16, 21)\n        kernel_size = (7, 9)\n        dilation = (1, 1)\n        adap_pad = AdaptivePadding(\n            kernel_size=kernel_size,\n            stride=stride,\n            dilation=dilation,\n            padding=padding)\n        kernel79_out = adap_pad(input)\n        assert (kernel79_out.shape[2], kernel79_out.shape[3]) == (16, 21)\n        assert kernel79_out.shape == dilation_out.shape\n\n    # assert only support \"same\" \"corner\"\n    with pytest.raises(AssertionError):\n        AdaptivePadding(\n            kernel_size=kernel_size,\n            stride=stride,\n            dilation=dilation,\n            padding=1)\n\n\ndef test_patch_embed():\n    B = 2\n    H = 3\n    W = 4\n    C = 3\n    embed_dims = 10\n    kernel_size = 3\n    stride = 1\n    dummy_input = torch.rand(B, C, H, W)\n    patch_merge_1 = PatchEmbed(\n        in_channels=C,\n        embed_dims=embed_dims,\n        kernel_size=kernel_size,\n        stride=stride,\n        padding=0,\n        dilation=1,\n        norm_cfg=None)\n\n    x1, shape = patch_merge_1(dummy_input)\n    # test out shape\n    assert x1.shape == (2, 2, 10)\n    # test outsize is correct\n    assert shape == (1, 2)\n    # test L = out_h * out_w\n    assert shape[0] * shape[1] == x1.shape[1]\n\n    B = 2\n    H = 10\n    W = 10\n    C = 3\n    embed_dims = 10\n    kernel_size = 5\n    stride = 2\n    dummy_input = torch.rand(B, C, H, W)\n    # test dilation\n    patch_merge_2 = PatchEmbed(\n        in_channels=C,\n        embed_dims=embed_dims,\n        kernel_size=kernel_size,\n        stride=stride,\n        padding=0,\n        dilation=2,\n        norm_cfg=None,\n    )\n\n    x2, shape = patch_merge_2(dummy_input)\n    # test out shape\n    assert x2.shape == (2, 1, 10)\n    # test outsize is correct\n    assert shape == (1, 1)\n    # test L = out_h * out_w\n    assert shape[0] * shape[1] == x2.shape[1]\n\n    stride = 2\n    input_size = (10, 10)\n\n    dummy_input = torch.rand(B, C, H, W)\n    # test stride and norm\n    patch_merge_3 = PatchEmbed(\n        in_channels=C,\n        embed_dims=embed_dims,\n        kernel_size=kernel_size,\n        stride=stride,\n        padding=0,\n        dilation=2,\n        norm_cfg=dict(type='LN'),\n        input_size=input_size)\n\n    x3, shape = patch_merge_3(dummy_input)\n    # test out shape\n    assert x3.shape == (2, 1, 10)\n    # test outsize is correct\n    assert shape == (1, 1)\n    # test L = out_h * out_w\n    assert shape[0] * shape[1] == x3.shape[1]\n\n    # test the init_out_size with nn.Unfold\n    assert patch_merge_3.init_out_size[1] == (input_size[0] - 2 * 4 -\n                                              1) // 2 + 1\n    assert patch_merge_3.init_out_size[0] == (input_size[0] - 2 * 4 -\n                                              1) // 2 + 1\n    H = 11\n    W = 12\n    input_size = (H, W)\n    dummy_input = torch.rand(B, C, H, W)\n    # test stride and norm\n    patch_merge_3 = PatchEmbed(\n        in_channels=C,\n        embed_dims=embed_dims,\n        kernel_size=kernel_size,\n        stride=stride,\n        padding=0,\n        dilation=2,\n        norm_cfg=dict(type='LN'),\n        input_size=input_size)\n\n    _, shape = patch_merge_3(dummy_input)\n    # when input_size equal to real input\n    # the out_size should be equal to `init_out_size`\n    assert shape == patch_merge_3.init_out_size\n\n    input_size = (H, W)\n    dummy_input = torch.rand(B, C, H, W)\n    # test stride and norm\n    patch_merge_3 = PatchEmbed(\n        in_channels=C,\n        embed_dims=embed_dims,\n        kernel_size=kernel_size,\n        stride=stride,\n        padding=0,\n        dilation=2,\n        norm_cfg=dict(type='LN'),\n        input_size=input_size)\n\n    _, shape = patch_merge_3(dummy_input)\n    # when input_size equal to real input\n    # the out_size should be equal to `init_out_size`\n    assert shape == patch_merge_3.init_out_size\n\n    # test adap padding\n    for padding in ('same', 'corner'):\n        in_c = 2\n        embed_dims = 3\n        B = 2\n\n        # test stride is 1\n        input_size = (5, 5)\n        kernel_size = (5, 5)\n        stride = (1, 1)\n        dilation = 1\n        bias = False\n\n        x = torch.rand(B, in_c, *input_size)\n        patch_embed = PatchEmbed(\n            in_channels=in_c,\n            embed_dims=embed_dims,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_embed(x)\n        assert x_out.size() == (B, 25, 3)\n        assert out_size == (5, 5)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n        # test kernel_size == stride\n        input_size = (5, 5)\n        kernel_size = (5, 5)\n        stride = (5, 5)\n        dilation = 1\n        bias = False\n\n        x = torch.rand(B, in_c, *input_size)\n        patch_embed = PatchEmbed(\n            in_channels=in_c,\n            embed_dims=embed_dims,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_embed(x)\n        assert x_out.size() == (B, 1, 3)\n        assert out_size == (1, 1)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n        # test kernel_size == stride\n        input_size = (6, 5)\n        kernel_size = (5, 5)\n        stride = (5, 5)\n        dilation = 1\n        bias = False\n\n        x = torch.rand(B, in_c, *input_size)\n        patch_embed = PatchEmbed(\n            in_channels=in_c,\n            embed_dims=embed_dims,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_embed(x)\n        assert x_out.size() == (B, 2, 3)\n        assert out_size == (2, 1)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n        # test different kernel_size with different stride\n        input_size = (6, 5)\n        kernel_size = (6, 2)\n        stride = (6, 2)\n        dilation = 1\n        bias = False\n\n        x = torch.rand(B, in_c, *input_size)\n        patch_embed = PatchEmbed(\n            in_channels=in_c,\n            embed_dims=embed_dims,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_embed(x)\n        assert x_out.size() == (B, 3, 3)\n        assert out_size == (1, 3)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n\ndef test_patch_merging():\n\n    # Test the model with int padding\n    in_c = 3\n    out_c = 4\n    kernel_size = 3\n    stride = 3\n    padding = 1\n    dilation = 1\n    bias = False\n    # test the case `pad_to_stride` is False\n    patch_merge = PatchMerging(\n        in_channels=in_c,\n        out_channels=out_c,\n        kernel_size=kernel_size,\n        stride=stride,\n        padding=padding,\n        dilation=dilation,\n        bias=bias)\n    B, L, C = 1, 100, 3\n    input_size = (10, 10)\n    x = torch.rand(B, L, C)\n    x_out, out_size = patch_merge(x, input_size)\n    assert x_out.size() == (1, 16, 4)\n    assert out_size == (4, 4)\n    # assert out size is consistent with real output\n    assert x_out.size(1) == out_size[0] * out_size[1]\n    in_c = 4\n    out_c = 5\n    kernel_size = 6\n    stride = 3\n    padding = 2\n    dilation = 2\n    bias = False\n    patch_merge = PatchMerging(\n        in_channels=in_c,\n        out_channels=out_c,\n        kernel_size=kernel_size,\n        stride=stride,\n        padding=padding,\n        dilation=dilation,\n        bias=bias)\n    B, L, C = 1, 100, 4\n    input_size = (10, 10)\n    x = torch.rand(B, L, C)\n    x_out, out_size = patch_merge(x, input_size)\n    assert x_out.size() == (1, 4, 5)\n    assert out_size == (2, 2)\n    # assert out size is consistent with real output\n    assert x_out.size(1) == out_size[0] * out_size[1]\n\n    # Test with adaptive padding\n    for padding in ('same', 'corner'):\n        in_c = 2\n        out_c = 3\n        B = 2\n\n        # test stride is 1\n        input_size = (5, 5)\n        kernel_size = (5, 5)\n        stride = (1, 1)\n        dilation = 1\n        bias = False\n        L = input_size[0] * input_size[1]\n\n        x = torch.rand(B, L, in_c)\n        patch_merge = PatchMerging(\n            in_channels=in_c,\n            out_channels=out_c,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_merge(x, input_size)\n        assert x_out.size() == (B, 25, 3)\n        assert out_size == (5, 5)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n        # test kernel_size == stride\n        input_size = (5, 5)\n        kernel_size = (5, 5)\n        stride = (5, 5)\n        dilation = 1\n        bias = False\n        L = input_size[0] * input_size[1]\n\n        x = torch.rand(B, L, in_c)\n        patch_merge = PatchMerging(\n            in_channels=in_c,\n            out_channels=out_c,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_merge(x, input_size)\n        assert x_out.size() == (B, 1, 3)\n        assert out_size == (1, 1)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n        # test kernel_size == stride\n        input_size = (6, 5)\n        kernel_size = (5, 5)\n        stride = (5, 5)\n        dilation = 1\n        bias = False\n        L = input_size[0] * input_size[1]\n\n        x = torch.rand(B, L, in_c)\n        patch_merge = PatchMerging(\n            in_channels=in_c,\n            out_channels=out_c,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_merge(x, input_size)\n        assert x_out.size() == (B, 2, 3)\n        assert out_size == (2, 1)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n        # test different kernel_size with different stride\n        input_size = (6, 5)\n        kernel_size = (6, 2)\n        stride = (6, 2)\n        dilation = 1\n        bias = False\n        L = input_size[0] * input_size[1]\n\n        x = torch.rand(B, L, in_c)\n        patch_merge = PatchMerging(\n            in_channels=in_c,\n            out_channels=out_c,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            bias=bias)\n\n        x_out, out_size = patch_merge(x, input_size)\n        assert x_out.size() == (B, 3, 3)\n        assert out_size == (1, 3)\n        assert x_out.size(1) == out_size[0] * out_size[1]\n\n\ndef test_detr_transformer_encoder_decoder():\n    config = ConfigDict(\n        num_layers=6,\n        layer_cfg=dict(  # DetrTransformerDecoderLayer\n            self_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1),\n            cross_attn_cfg=dict(  # MultiheadAttention\n                embed_dims=256,\n                num_heads=8,\n                dropout=0.1),\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,\n                num_fcs=2,\n                ffn_drop=0.1,\n                act_cfg=dict(type='ReLU', inplace=True))))\n    assert len(DetrTransformerDecoder(**config).layers) == 6\n    assert DetrTransformerDecoder(**config)\n\n    config = ConfigDict(\n        dict(\n            num_layers=6,\n            layer_cfg=dict(  # DetrTransformerEncoderLayer\n                self_attn_cfg=dict(  # MultiheadAttention\n                    embed_dims=256,\n                    num_heads=8,\n                    dropout=0.1),\n                ffn_cfg=dict(\n                    embed_dims=256,\n                    feedforward_channels=2048,\n                    num_fcs=2,\n                    ffn_drop=0.1,\n                    act_cfg=dict(type='ReLU', inplace=True)))))\n    assert len(DetrTransformerEncoder(**config).layers) == 6\n    assert DetrTransformerEncoder(**config)\n\n\ndef test_ddq_transformer_decoder():\n    num_layers = 2\n    config = ConfigDict(\n        num_layers=num_layers,\n        return_intermediate=True,\n        layer_cfg=dict(\n            self_attn_cfg=dict(embed_dims=256, num_heads=8,\n                               dropout=0.0),  # 0.1 for DeformDETR\n            cross_attn_cfg=dict(embed_dims=256, num_levels=4,\n                                dropout=0.0),  # 0.1 for DeformDETR\n            ffn_cfg=dict(\n                embed_dims=256,\n                feedforward_channels=2048,  # 1024 for DeformDETR\n                ffn_drop=0.0)),  # 0.1 for DeformDETR\n        post_norm_cfg=None)\n    assert len(DDQTransformerDecoder(**config).layers) == num_layers\n    assert DDQTransformerDecoder(**config)\n"
  },
  {
    "path": "tests/test_models/test_losses/test_gaussian_focal_loss.py",
    "content": "import unittest\n\nimport torch\n\nfrom mmdet.models.losses import GaussianFocalLoss\n\n\nclass TestGaussianFocalLoss(unittest.TestCase):\n\n    def test_forward(self):\n        pred = torch.rand((10, 4))\n        target = torch.rand((10, 4))\n        gaussian_focal_loss = GaussianFocalLoss()\n        loss1 = gaussian_focal_loss(pred, target)\n        self.assertIsInstance(loss1, torch.Tensor)\n\n        loss2 = gaussian_focal_loss(pred, target, avg_factor=0.5)\n        self.assertIsInstance(loss2, torch.Tensor)\n\n        # test reduction\n        gaussian_focal_loss = GaussianFocalLoss(reduction='none')\n        loss = gaussian_focal_loss(pred, target)\n        self.assertTrue(loss.shape == (10, 4))\n\n        # test reduction_override\n        loss = gaussian_focal_loss(pred, target, reduction_override='mean')\n        self.assertTrue(loss.ndim == 0)\n\n        # Only supports None, 'none', 'mean', 'sum'\n        with self.assertRaises(AssertionError):\n            gaussian_focal_loss(pred, target, reduction_override='max')\n\n        # test pos_inds\n        pos_inds = (torch.rand(5) * 8).long()\n        pos_labels = (torch.rand(5) * 2).long()\n        gaussian_focal_loss = GaussianFocalLoss()\n        loss = gaussian_focal_loss(pred, target, pos_inds, pos_labels)\n        self.assertIsInstance(loss, torch.Tensor)\n"
  },
  {
    "path": "tests/test_models/test_losses/test_l2_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.models import L2Loss\n\n\nclass TestL2Loss(TestCase):\n\n    def test_l2_loss(self):\n        pred = torch.Tensor([[1, 1, 0, 0, 0, 0, 1]])\n        target = torch.Tensor([[1, 1, 0, 0, 0, 0, 0]])\n\n        loss = L2Loss(\n            neg_pos_ub=2,\n            pos_margin=0,\n            neg_margin=0.1,\n            hard_mining=True,\n            loss_weight=1.0)\n        assert torch.allclose(loss(pred, target), torch.tensor(0.1350))\n"
  },
  {
    "path": "tests/test_models/test_losses/test_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport json\n\nimport pytest\nimport torch\nimport torch.nn.functional as F\nfrom mmengine.utils import digit_version\n\nfrom mmdet.models.losses import (BalancedL1Loss, CrossEntropyLoss, DDQAuxLoss,\n                                 DiceLoss, DistributionFocalLoss, EQLV2Loss,\n                                 FocalLoss, GaussianFocalLoss,\n                                 KnowledgeDistillationKLDivLoss, L1Loss,\n                                 MarginL2Loss, MSELoss, QualityFocalLoss,\n                                 SeesawLoss, SmoothL1Loss, VarifocalLoss)\nfrom mmdet.models.losses.ghm_loss import GHMC, GHMR\nfrom mmdet.models.losses.iou_loss import (BoundedIoULoss, CIoULoss, DIoULoss,\n                                          EIoULoss, GIoULoss, IoULoss,\n                                          SIoULoss)\n\n\n@pytest.mark.parametrize('loss_class', [\n    IoULoss, BoundedIoULoss, GIoULoss, DIoULoss, CIoULoss, EIoULoss, SIoULoss\n])\ndef test_iou_type_loss_zeros_weight(loss_class):\n    pred = torch.rand((10, 4))\n    target = torch.rand((10, 4))\n    weight = torch.zeros(10)\n\n    loss = loss_class()(pred, target, weight)\n    assert loss == 0.\n\n\n@pytest.mark.parametrize('loss_class', [\n    BalancedL1Loss, BoundedIoULoss, CIoULoss, CrossEntropyLoss, DIoULoss,\n    EIoULoss, SIoULoss, FocalLoss, DistributionFocalLoss, MSELoss, SeesawLoss,\n    GaussianFocalLoss, GIoULoss, QualityFocalLoss, IoULoss, L1Loss,\n    VarifocalLoss, GHMR, GHMC, SmoothL1Loss, KnowledgeDistillationKLDivLoss,\n    DiceLoss\n])\ndef test_loss_with_reduction_override(loss_class):\n    pred = torch.rand((10, 4))\n    target = torch.rand((10, 4)),\n    weight = None\n\n    with pytest.raises(AssertionError):\n        # only reduction_override from [None, 'none', 'mean', 'sum']\n        # is not allowed\n        reduction_override = True\n        loss_class()(\n            pred, target, weight, reduction_override=reduction_override)\n\n\n@pytest.mark.parametrize('loss_class', [QualityFocalLoss])\n@pytest.mark.parametrize('activated', [False, True])\ndef test_QualityFocalLoss_Loss(loss_class, activated):\n    input_shape = (4, 5)\n    pred = torch.rand(input_shape)\n    label = torch.Tensor([0, 1, 2, 0]).long()\n    quality_label = torch.rand(input_shape[0])\n\n    original_loss = loss_class(activated=activated)(pred,\n                                                    (label, quality_label))\n    assert isinstance(original_loss, torch.Tensor)\n\n    target = torch.nn.functional.one_hot(label, 5)\n    target = target * quality_label.reshape(input_shape[0], 1)\n\n    new_loss = loss_class(activated=activated)(pred, target)\n    assert isinstance(new_loss, torch.Tensor)\n    assert new_loss == original_loss\n\n\n@pytest.mark.parametrize('loss_class', [\n    IoULoss, BoundedIoULoss, GIoULoss, DIoULoss, CIoULoss, EIoULoss, SIoULoss,\n    MSELoss, L1Loss, SmoothL1Loss, BalancedL1Loss, MarginL2Loss\n])\n@pytest.mark.parametrize('input_shape', [(10, 4), (0, 4)])\ndef test_regression_losses(loss_class, input_shape):\n    pred = torch.rand(input_shape)\n    target = torch.rand(input_shape)\n    weight = torch.rand(input_shape)\n\n    # Test loss forward\n    loss = loss_class()(pred, target)\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with weight\n    loss = loss_class()(pred, target, weight)\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with reduction_override\n    loss = loss_class()(pred, target, reduction_override='mean')\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with avg_factor\n    loss = loss_class()(pred, target, avg_factor=10)\n    assert isinstance(loss, torch.Tensor)\n\n    with pytest.raises(ValueError):\n        # loss can evaluate with avg_factor only if\n        # reduction is None, 'none' or 'mean'.\n        reduction_override = 'sum'\n        loss_class()(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n\n    # Test loss forward with avg_factor and reduction\n    for reduction_override in [None, 'none', 'mean']:\n        loss_class()(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n        assert isinstance(loss, torch.Tensor)\n\n\n@pytest.mark.parametrize('loss_class', [CrossEntropyLoss])\n@pytest.mark.parametrize('input_shape', [(10, 5), (0, 5)])\ndef test_classification_losses(loss_class, input_shape):\n    if input_shape[0] == 0 and digit_version(\n            torch.__version__) < digit_version('1.5.0'):\n        pytest.skip(\n            f'CELoss in PyTorch {torch.__version__} does not support empty'\n            f'tensor.')\n\n    pred = torch.rand(input_shape)\n    target = torch.randint(0, 5, (input_shape[0], ))\n\n    # Test loss forward\n    loss = loss_class()(pred, target)\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with reduction_override\n    loss = loss_class()(pred, target, reduction_override='mean')\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with avg_factor\n    loss = loss_class()(pred, target, avg_factor=10)\n    assert isinstance(loss, torch.Tensor)\n\n    with pytest.raises(ValueError):\n        # loss can evaluate with avg_factor only if\n        # reduction is None, 'none' or 'mean'.\n        reduction_override = 'sum'\n        loss_class()(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n\n    # Test loss forward with avg_factor and reduction\n    for reduction_override in [None, 'none', 'mean']:\n        loss_class()(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n        assert isinstance(loss, torch.Tensor)\n\n\n@pytest.mark.parametrize('loss_class', [FocalLoss])\n@pytest.mark.parametrize('input_shape', [(10, 5), (3, 5, 40, 40)])\ndef test_FocalLoss_loss(loss_class, input_shape):\n    pred = torch.rand(input_shape)\n    target = torch.randint(0, 5, (input_shape[0], ))\n    if len(input_shape) == 4:\n        B, N, W, H = input_shape\n        target = F.one_hot(torch.randint(0, 5, (B * W * H, )),\n                           5).reshape(B, W, H, N).permute(0, 3, 1, 2)\n\n    # Test loss forward\n    loss = loss_class()(pred, target)\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with reduction_override\n    loss = loss_class()(pred, target, reduction_override='mean')\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with avg_factor\n    loss = loss_class()(pred, target, avg_factor=10)\n    assert isinstance(loss, torch.Tensor)\n\n    with pytest.raises(ValueError):\n        # loss can evaluate with avg_factor only if\n        # reduction is None, 'none' or 'mean'.\n        reduction_override = 'sum'\n        loss_class()(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n\n    # Test loss forward with avg_factor and reduction\n    for reduction_override in [None, 'none', 'mean']:\n        loss_class()(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n        assert isinstance(loss, torch.Tensor)\n\n\n@pytest.mark.parametrize('loss_class', [GHMR])\n@pytest.mark.parametrize('input_shape', [(10, 4), (0, 4)])\ndef test_GHMR_loss(loss_class, input_shape):\n    pred = torch.rand(input_shape)\n    target = torch.rand(input_shape)\n    weight = torch.rand(input_shape)\n\n    # Test loss forward\n    loss = loss_class()(pred, target, weight)\n    assert isinstance(loss, torch.Tensor)\n\n\n@pytest.mark.parametrize('use_sigmoid', [True, False])\n@pytest.mark.parametrize('reduction', ['sum', 'mean', None])\n@pytest.mark.parametrize('avg_non_ignore', [True, False])\ndef test_loss_with_ignore_index(use_sigmoid, reduction, avg_non_ignore):\n    # Test cross_entropy loss\n    loss_class = CrossEntropyLoss(\n        use_sigmoid=use_sigmoid,\n        use_mask=False,\n        ignore_index=255,\n        avg_non_ignore=avg_non_ignore)\n    pred = torch.rand((10, 5))\n    target = torch.randint(0, 5, (10, ))\n\n    ignored_indices = torch.randint(0, 10, (2, ), dtype=torch.long)\n    target[ignored_indices] = 255\n\n    # Test loss forward with default ignore\n    loss_with_ignore = loss_class(pred, target, reduction_override=reduction)\n    assert isinstance(loss_with_ignore, torch.Tensor)\n\n    # Test loss forward with forward ignore\n    target[ignored_indices] = 255\n    loss_with_forward_ignore = loss_class(\n        pred, target, ignore_index=255, reduction_override=reduction)\n    assert isinstance(loss_with_forward_ignore, torch.Tensor)\n\n    # Verify correctness\n    if avg_non_ignore:\n        # manually remove the ignored elements\n        not_ignored_indices = (target != 255)\n        pred = pred[not_ignored_indices]\n        target = target[not_ignored_indices]\n    loss = loss_class(pred, target, reduction_override=reduction)\n\n    assert torch.allclose(loss, loss_with_ignore)\n    assert torch.allclose(loss, loss_with_forward_ignore)\n\n    # test ignore all target\n    pred = torch.rand((10, 5))\n    target = torch.ones((10, ), dtype=torch.long) * 255\n    loss = loss_class(pred, target, reduction_override=reduction)\n    assert loss == 0\n\n\n@pytest.mark.parametrize('naive_dice', [True, False])\ndef test_dice_loss(naive_dice):\n    loss_class = DiceLoss\n    pred = torch.rand((10, 4, 4))\n    target = torch.rand((10, 4, 4))\n    weight = torch.rand((10))\n\n    # Test loss forward\n    loss = loss_class(naive_dice=naive_dice)(pred, target)\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with weight\n    loss = loss_class(naive_dice=naive_dice)(pred, target, weight)\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with reduction_override\n    loss = loss_class(naive_dice=naive_dice)(\n        pred, target, reduction_override='mean')\n    assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with avg_factor\n    loss = loss_class(naive_dice=naive_dice)(pred, target, avg_factor=10)\n    assert isinstance(loss, torch.Tensor)\n\n    with pytest.raises(ValueError):\n        # loss can evaluate with avg_factor only if\n        # reduction is None, 'none' or 'mean'.\n        reduction_override = 'sum'\n        loss_class(naive_dice=naive_dice)(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n\n    # Test loss forward with avg_factor and reduction\n    for reduction_override in [None, 'none', 'mean']:\n        loss_class(naive_dice=naive_dice)(\n            pred, target, avg_factor=10, reduction_override=reduction_override)\n        assert isinstance(loss, torch.Tensor)\n\n    # Test loss forward with has_acted=False and use_sigmoid=False\n    with pytest.raises(NotImplementedError):\n        loss_class(\n            use_sigmoid=False, activate=True, naive_dice=naive_dice)(pred,\n                                                                     target)\n\n    # Test loss forward with weight.ndim != loss.ndim\n    with pytest.raises(AssertionError):\n        weight = torch.rand((2, 8))\n        loss_class(naive_dice=naive_dice)(pred, target, weight)\n\n    # Test loss forward with len(weight) != len(pred)\n    with pytest.raises(AssertionError):\n        weight = torch.rand((8))\n        loss_class(naive_dice=naive_dice)(pred, target, weight)\n\n\n@pytest.mark.parametrize('loss_class', [EQLV2Loss])\n@pytest.mark.parametrize('reduction', ['mean'])\ndef test_eqlv2_loss(loss_class, reduction):\n    cls_score = torch.randn((1204, 1204))\n    label = torch.randint(0, 2, (1204, ))\n    weight = None\n\n    loss = loss_class()(cls_score, label, weight)\n    assert isinstance(loss, torch.Tensor)\n\n\n@pytest.mark.parametrize('loss_class', [DDQAuxLoss])\ndef test_ddq_aux_loss(loss_class):\n    data_sample_file_path = 'tests/data/coco_batched_sample.json'\n    num_classes = 80\n    num_pred = 1350\n\n    with open(data_sample_file_path, 'r') as file_stream:\n        data_sample_infos = json.load(file_stream)\n\n    gt_bboxes = []\n    img_metas = []\n    gt_labels = []\n\n    for data_sample_info in data_sample_infos:\n        metainfo = data_sample_info['metainfo']\n        labels = torch.LongTensor(data_sample_info['labels'])\n        bboxes = torch.Tensor(data_sample_info['bboxes'])\n\n        img_metas.append(metainfo)\n        gt_labels.append(labels)\n        gt_bboxes.append(bboxes)\n\n    batch_size = len(data_sample_infos)\n    pred_classes = torch.rand([batch_size, num_pred, num_classes])\n    pred_bboxes = torch.rand([batch_size, num_pred, 4])\n\n    aux_loss_for_dense = loss_class(\n        train_cfg=dict(assigner=dict(type='TopkHungarianAssigner', topk=4)))\n\n    aux_loss = aux_loss_for_dense.loss(pred_classes, pred_bboxes, gt_bboxes,\n                                       gt_labels, img_metas)\n\n    assert isinstance(aux_loss, dict)\n\n    for loss_name, batch_losses in aux_loss.items():\n        assert isinstance(batch_losses, list)\n\n        for loss in batch_losses:\n            assert isinstance(loss, torch.Tensor)\n"
  },
  {
    "path": "tests/test_models/test_losses/test_multi_pos_cross_entropy_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.models.losses import MultiPosCrossEntropyLoss\n\n\nclass TestMultiPosCrossEntropyLoss(TestCase):\n\n    def test_mpce_loss(self):\n        costs = torch.tensor([[1, 0], [0, 1]])\n        labels = torch.tensor([[1, 1], [0, 0]])\n\n        loss = MultiPosCrossEntropyLoss(reduction='mean', loss_weight=1.0)\n        assert torch.allclose(loss(costs, labels), torch.tensor(0.))\n\n        labels = torch.Tensor([[1, 0], [0, 1]])\n        loss(costs, labels)\n        assert torch.allclose(loss(costs, labels), torch.tensor(0.31326))\n"
  },
  {
    "path": "tests/test_models/test_losses/test_triplet_loss.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.models import TripletLoss\n\n\nclass TestTripletLoss(TestCase):\n\n    def test_triplet_loss(self):\n        feature = torch.Tensor([[1, 1], [1, 1], [0, 0], [0, 0]])\n        label = torch.Tensor([1, 1, 0, 0])\n\n        loss = TripletLoss(margin=0.3, loss_weight=1.0)\n        assert torch.allclose(loss(feature, label), torch.tensor(0.))\n\n        label = torch.Tensor([1, 0, 1, 0])\n        assert torch.allclose(loss(feature, label), torch.tensor(1.7142))\n"
  },
  {
    "path": "tests/test_models/test_mot/test_byte_track.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_track_inputs, get_detector_cfg\n\n\nclass TestByteTrack(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'bytetrack/bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain'\n        '_test-mot17halfval.py',\n    ])\n    def test_bytetrack_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model.detector.neck.out_channels = 1\n        model.detector.neck.num_csp_blocks = 1\n        model.detector.bbox_head.in_channels = 1\n        model.detector.bbox_head.feat_channels = 1\n        model = MODELS.build(model)\n        assert model.detector\n\n    @parameterized.expand([\n        ('bytetrack/bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_'\n         'test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_bytetrack_forward_loss_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_bytetrack_forward_loss_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            _model.detector.neck.out_channels = 1\n            _model.detector.neck.num_csp_blocks = 1\n            _model.detector.bbox_head.num_classes = 10\n            _model.detector.bbox_head.in_channels = 1\n            _model.detector.bbox_head.feat_channels = 1\n            # _scope_ will be popped after build\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = model.data_preprocessor(packed_inputs, True)\n            losses = model.forward(**data, mode='loss')\n            assert isinstance(losses, dict)\n\n    @parameterized.expand([\n        ('bytetrack/bytetrack_yolox_x_8xb4-80e_crowdhuman-mot17halftrain_'\n         'test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_bytetrack_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_bytetrack_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            _model.detector.neck.out_channels = 1\n            _model.detector.neck.num_csp_blocks = 1\n            _model.detector.bbox_head.in_channels = 1\n            _model.detector.bbox_head.feat_channels = 1\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                image_shapes=[(3, 256, 256)],\n                num_classes=1)\n            out_data = model.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_models/test_mot/test_deep_sort.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg\n\n\nclass TestDeepSORT(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e'\n        '_mot17halftrain_test-mot17halfval.py'\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model = MODELS.build(model)\n        assert model.detector\n        assert model.reid\n        assert model.tracker\n\n    @parameterized.expand([\n        ('deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e'\n         '_mot17halftrain_test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_deepsort_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_deepsort_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                image_shapes=[(3, 256, 256)],\n                num_classes=1)\n            out_data = model.data_preprocessor(packed_inputs, False)\n\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_models/test_mot/test_oc_sort.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_track_inputs, get_detector_cfg\n\n\nclass TestByteTrack(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'ocsort/ocsort_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain'\n        '_test-mot17halfval.py',\n    ])\n    def test_bytetrack_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model.detector.neck.out_channels = 1\n        model.detector.neck.num_csp_blocks = 1\n        model.detector.bbox_head.in_channels = 1\n        model.detector.bbox_head.feat_channels = 1\n        model = MODELS.build(model)\n        assert model.detector\n\n    @parameterized.expand([\n        ('ocsort/ocsort_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_'\n         'test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_bytetrack_forward_loss_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_bytetrack_forward_loss_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            _model.detector.neck.out_channels = 1\n            _model.detector.neck.num_csp_blocks = 1\n            _model.detector.bbox_head.num_classes = 10\n            _model.detector.bbox_head.in_channels = 1\n            _model.detector.bbox_head.feat_channels = 1\n            # _scope_ will be popped after build\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_mm_inputs(2, [[3, 128, 128], [3, 125, 130]])\n            data = model.data_preprocessor(packed_inputs, True)\n            losses = model.forward(**data, mode='loss')\n            assert isinstance(losses, dict)\n\n    @parameterized.expand([\n        ('ocsort/ocsort_yolox_x_8xb4-amp-80e_crowdhuman-mot17halftrain_'\n         'test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_bytetrack_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_bytetrack_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            _model.detector.neck.out_channels = 1\n            _model.detector.neck.num_csp_blocks = 1\n            _model.detector.bbox_head.in_channels = 1\n            _model.detector.bbox_head.feat_channels = 1\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                image_shapes=[(3, 256, 256)],\n                num_classes=1)\n            out_data = model.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_models/test_mot/test_qdtrack.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg\n\n\nclass TestQDTrack(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17halftrain_'\n        'test-mot17halfval.py',\n    ])\n    def test_qdtrack_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n\n        model = MODELS.build(model)\n        assert model.detector\n        assert model.track_head\n\n    @parameterized.expand([\n        ('qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17'\n         'halftrain_test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_qdtrack_forward_loss_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_qdtrack_forward_loss_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            # _scope_ will be popped after build\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                key_frames_inds=[0],\n                image_shapes=(3, 128, 128),\n                num_items=None)\n            out_data = model.data_preprocessor(packed_inputs, True)\n            inputs, data_samples = out_data['inputs'], out_data['data_samples']\n            # Test forward\n            losses = model.forward(inputs, data_samples, mode='loss')\n            assert isinstance(losses, dict)\n\n    @parameterized.expand([\n        ('qdtrack/qdtrack_faster-rcnn_r50_fpn_8xb2-4e_mot17'\n         'halftrain_test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_qdtrack_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_bytetrack_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1, num_frames=1, image_shapes=(3, 128, 128))\n            out_data = model.data_preprocessor(packed_inputs, False)\n\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_models/test_mot/test_sort.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg\n\n\nclass TestDeepSORT(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'sort/sort_faster-rcnn_r50_fpn_8xb2-4e'\n        '_mot17halftrain_test-mot17halfval.py'\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model = MODELS.build(model)\n        assert model.detector\n        assert model.tracker\n\n    @parameterized.expand([\n        ('sort/sort_faster-rcnn_r50_fpn_8xb2-4e'\n         '_mot17halftrain_test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_deepsort_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_deepsort_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                image_shapes=[(3, 256, 256)],\n                num_classes=1)\n            out_data = model.data_preprocessor(packed_inputs, False)\n\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_models/test_mot/test_strong_sort.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg\n\n\nclass TestDeepSORT(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman'\n        '-mot17halftrain_test-mot17halfval.py'\n    ])\n    def test_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n        model.detector.neck.out_channels = 1\n        model.detector.neck.num_csp_blocks = 1\n        model.detector.bbox_head.in_channels = 1\n        model.detector.bbox_head.feat_channels = 1\n        model.reid.backbone.depth = 18\n        model.reid.head.fc_channels = 1\n        model.reid.head.out_channels = 1\n        model.reid.head.num_classes = 2\n        model.cmc = None\n        model = MODELS.build(model)\n        assert model.detector\n        assert model.reid\n        assert model.tracker\n\n    @parameterized.expand([\n        ('strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman'\n         '-mot17halftrain_test-mot17halfval.py', ('cpu', 'cuda')),\n    ])\n    def test_strongsort_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_strongsort_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            _model.detector.neck.out_channels = 1\n            _model.detector.neck.num_csp_blocks = 1\n            _model.detector.bbox_head.in_channels = 1\n            _model.detector.bbox_head.feat_channels = 1\n            _model.reid.backbone.depth = 18\n            _model.reid.head.in_channels = 512\n            _model.reid.head.fc_channels = 1\n            _model.reid.head.out_channels = 1\n            _model.reid.head.num_classes = 2\n            _model.cmc = None\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                image_shapes=[(3, 256, 256)],\n                num_classes=1)\n            out_data = model.data_preprocessor(packed_inputs, False)\n\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_models/test_necks/test_ct_resnet_neck.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport torch\n\nfrom mmdet.models.necks import CTResNetNeck\n\n\nclass TestCTResNetNeck(unittest.TestCase):\n\n    def test_init(self):\n        # num_filters/num_kernels must be same length\n        with self.assertRaises(AssertionError):\n            CTResNetNeck(\n                in_channels=10,\n                num_deconv_filters=(10, 10),\n                num_deconv_kernels=(4, ))\n\n        ct_resnet_neck = CTResNetNeck(\n            in_channels=16,\n            num_deconv_filters=(8, 8),\n            num_deconv_kernels=(4, 4),\n            use_dcn=False)\n        ct_resnet_neck.init_weights()\n\n    def test_forward(self):\n        in_channels = 16\n        num_filters = (8, 8)\n        num_kernels = (4, 4)\n        feat = torch.rand(1, 16, 4, 4)\n        ct_resnet_neck = CTResNetNeck(\n            in_channels=in_channels,\n            num_deconv_filters=num_filters,\n            num_deconv_kernels=num_kernels,\n            use_dcn=False)\n\n        # feat must be list or tuple\n        with self.assertRaises(AssertionError):\n            ct_resnet_neck(feat)\n\n        out_feat = ct_resnet_neck([feat])[0]\n        self.assertEqual(out_feat.shape, (1, num_filters[-1], 16, 16))\n\n        if torch.cuda.is_available():\n            # test dcn\n            ct_resnet_neck = CTResNetNeck(\n                in_channels=in_channels,\n                num_deconv_filters=num_filters,\n                num_deconv_kernels=num_kernels)\n            ct_resnet_neck = ct_resnet_neck.cuda()\n            feat = feat.cuda()\n            out_feat = ct_resnet_neck([feat])[0]\n            self.assertEqual(out_feat.shape, (1, num_filters[-1], 16, 16))\n"
  },
  {
    "path": "tests/test_models/test_necks/test_necks.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\nfrom torch.nn.modules.batchnorm import _BatchNorm\n\nfrom mmdet.models.necks import (FPG, FPN, FPN_CARAFE, NASFCOS_FPN, NASFPN, SSH,\n                                YOLOXPAFPN, ChannelMapper, DilatedEncoder,\n                                DyHead, SSDNeck, YOLOV3Neck)\n\n\ndef test_fpn():\n    \"\"\"Tests fpn.\"\"\"\n    s = 64\n    in_channels = [8, 16, 32, 64]\n    feat_sizes = [s // 2**i for i in range(4)]  # [64, 32, 16, 8]\n    out_channels = 8\n\n    # end_level=-1 is equal to end_level=3\n    FPN(in_channels=in_channels,\n        out_channels=out_channels,\n        start_level=0,\n        end_level=-1,\n        num_outs=5)\n    FPN(in_channels=in_channels,\n        out_channels=out_channels,\n        start_level=0,\n        end_level=3,\n        num_outs=5)\n\n    # `num_outs` is not equal to end_level - start_level + 1\n    with pytest.raises(AssertionError):\n        FPN(in_channels=in_channels,\n            out_channels=out_channels,\n            start_level=1,\n            end_level=2,\n            num_outs=3)\n\n    # `num_outs` is not equal to len(in_channels) - start_level\n    with pytest.raises(AssertionError):\n        FPN(in_channels=in_channels,\n            out_channels=out_channels,\n            start_level=1,\n            num_outs=2)\n\n    # `end_level` is larger than len(in_channels) - 1\n    with pytest.raises(AssertionError):\n        FPN(in_channels=in_channels,\n            out_channels=out_channels,\n            start_level=1,\n            end_level=4,\n            num_outs=2)\n\n    # `num_outs` is not equal to end_level - start_level\n    with pytest.raises(AssertionError):\n        FPN(in_channels=in_channels,\n            out_channels=out_channels,\n            start_level=1,\n            end_level=3,\n            num_outs=1)\n\n    # Invalid `add_extra_convs` option\n    with pytest.raises(AssertionError):\n        FPN(in_channels=in_channels,\n            out_channels=out_channels,\n            start_level=1,\n            add_extra_convs='on_xxx',\n            num_outs=5)\n\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        start_level=1,\n        add_extra_convs=True,\n        num_outs=5)\n\n    # FPN expects a multiple levels of features per image\n    feats = [\n        torch.rand(1, in_channels[i], feat_sizes[i], feat_sizes[i])\n        for i in range(len(in_channels))\n    ]\n    outs = fpn_model(feats)\n    assert fpn_model.add_extra_convs == 'on_input'\n    assert len(outs) == fpn_model.num_outs\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    # Tests for fpn with no extra convs (pooling is used instead)\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        start_level=1,\n        add_extra_convs=False,\n        num_outs=5)\n    outs = fpn_model(feats)\n    assert len(outs) == fpn_model.num_outs\n    assert not fpn_model.add_extra_convs\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    # Tests for fpn with lateral bns\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        start_level=1,\n        add_extra_convs=True,\n        no_norm_on_lateral=False,\n        norm_cfg=dict(type='BN', requires_grad=True),\n        num_outs=5)\n    outs = fpn_model(feats)\n    assert len(outs) == fpn_model.num_outs\n    assert fpn_model.add_extra_convs == 'on_input'\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n    bn_exist = False\n    for m in fpn_model.modules():\n        if isinstance(m, _BatchNorm):\n            bn_exist = True\n    assert bn_exist\n\n    # Bilinear upsample\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        start_level=1,\n        add_extra_convs=True,\n        upsample_cfg=dict(mode='bilinear', align_corners=True),\n        num_outs=5)\n    fpn_model(feats)\n    outs = fpn_model(feats)\n    assert len(outs) == fpn_model.num_outs\n    assert fpn_model.add_extra_convs == 'on_input'\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    # Scale factor instead of fixed upsample size upsample\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        start_level=1,\n        add_extra_convs=True,\n        upsample_cfg=dict(scale_factor=2),\n        num_outs=5)\n    outs = fpn_model(feats)\n    assert len(outs) == fpn_model.num_outs\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    # Extra convs source is 'inputs'\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        add_extra_convs='on_input',\n        start_level=1,\n        num_outs=5)\n    assert fpn_model.add_extra_convs == 'on_input'\n    outs = fpn_model(feats)\n    assert len(outs) == fpn_model.num_outs\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    # Extra convs source is 'laterals'\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        add_extra_convs='on_lateral',\n        start_level=1,\n        num_outs=5)\n    assert fpn_model.add_extra_convs == 'on_lateral'\n    outs = fpn_model(feats)\n    assert len(outs) == fpn_model.num_outs\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    # Extra convs source is 'outputs'\n    fpn_model = FPN(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        add_extra_convs='on_output',\n        start_level=1,\n        num_outs=5)\n    assert fpn_model.add_extra_convs == 'on_output'\n    outs = fpn_model(feats)\n    assert len(outs) == fpn_model.num_outs\n    for i in range(fpn_model.num_outs):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n\ndef test_channel_mapper():\n    \"\"\"Tests ChannelMapper.\"\"\"\n    s = 64\n    in_channels = [8, 16, 32, 64]\n    feat_sizes = [s // 2**i for i in range(4)]  # [64, 32, 16, 8]\n    out_channels = 8\n    kernel_size = 3\n    feats = [\n        torch.rand(1, in_channels[i], feat_sizes[i], feat_sizes[i])\n        for i in range(len(in_channels))\n    ]\n\n    # in_channels must be a list\n    with pytest.raises(AssertionError):\n        channel_mapper = ChannelMapper(\n            in_channels=10, out_channels=out_channels, kernel_size=kernel_size)\n    # the length of channel_mapper's inputs must be equal to the length of\n    # in_channels\n    with pytest.raises(AssertionError):\n        channel_mapper = ChannelMapper(\n            in_channels=in_channels[:-1],\n            out_channels=out_channels,\n            kernel_size=kernel_size)\n        channel_mapper(feats)\n\n    channel_mapper = ChannelMapper(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        kernel_size=kernel_size)\n\n    outs = channel_mapper(feats)\n    assert len(outs) == len(feats)\n    for i in range(len(feats)):\n        outs[i].shape[1] == out_channels\n        outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n\ndef test_dilated_encoder():\n    in_channels = 16\n    out_channels = 32\n    out_shape = 34\n    dilated_encoder = DilatedEncoder(in_channels, out_channels, 16, 2,\n                                     [2, 4, 6, 8])\n    feat = [torch.rand(1, in_channels, 34, 34)]\n    out_feat = dilated_encoder(feat)[0]\n    assert out_feat.shape == (1, out_channels, out_shape, out_shape)\n\n\ndef test_yolov3_neck():\n    # num_scales, in_channels, out_channels must be same length\n    with pytest.raises(AssertionError):\n        YOLOV3Neck(num_scales=3, in_channels=[16, 8, 4], out_channels=[8, 4])\n\n    # len(feats) must equal to num_scales\n    with pytest.raises(AssertionError):\n        neck = YOLOV3Neck(\n            num_scales=3, in_channels=[16, 8, 4], out_channels=[8, 4, 2])\n        feats = (torch.rand(1, 4, 16, 16), torch.rand(1, 8, 16, 16))\n        neck(feats)\n\n    # test normal channels\n    s = 32\n    in_channels = [16, 8, 4]\n    out_channels = [8, 4, 2]\n    feat_sizes = [s // 2**i for i in range(len(in_channels) - 1, -1, -1)]\n    feats = [\n        torch.rand(1, in_channels[i], feat_sizes[i], feat_sizes[i])\n        for i in range(len(in_channels) - 1, -1, -1)\n    ]\n    neck = YOLOV3Neck(\n        num_scales=3, in_channels=in_channels, out_channels=out_channels)\n    outs = neck(feats)\n\n    assert len(outs) == len(feats)\n    for i in range(len(outs)):\n        assert outs[i].shape == \\\n               (1, out_channels[i], feat_sizes[i], feat_sizes[i])\n\n    # test more flexible setting\n    s = 32\n    in_channels = [32, 8, 16]\n    out_channels = [19, 21, 5]\n    feat_sizes = [s // 2**i for i in range(len(in_channels) - 1, -1, -1)]\n    feats = [\n        torch.rand(1, in_channels[i], feat_sizes[i], feat_sizes[i])\n        for i in range(len(in_channels) - 1, -1, -1)\n    ]\n    neck = YOLOV3Neck(\n        num_scales=3, in_channels=in_channels, out_channels=out_channels)\n    outs = neck(feats)\n\n    assert len(outs) == len(feats)\n    for i in range(len(outs)):\n        assert outs[i].shape == \\\n               (1, out_channels[i], feat_sizes[i], feat_sizes[i])\n\n\ndef test_ssd_neck():\n    # level_strides/level_paddings must be same length\n    with pytest.raises(AssertionError):\n        SSDNeck(\n            in_channels=[8, 16],\n            out_channels=[8, 16, 32],\n            level_strides=[2],\n            level_paddings=[2, 1])\n\n    # length of out_channels must larger than in_channels\n    with pytest.raises(AssertionError):\n        SSDNeck(\n            in_channels=[8, 16],\n            out_channels=[8],\n            level_strides=[2],\n            level_paddings=[2])\n\n    # len(out_channels) - len(in_channels) must equal to len(level_strides)\n    with pytest.raises(AssertionError):\n        SSDNeck(\n            in_channels=[8, 16],\n            out_channels=[4, 16, 64],\n            level_strides=[2, 2],\n            level_paddings=[2, 2])\n\n    # in_channels must be same with out_channels[:len(in_channels)]\n    with pytest.raises(AssertionError):\n        SSDNeck(\n            in_channels=[8, 16],\n            out_channels=[4, 16, 64],\n            level_strides=[2],\n            level_paddings=[2])\n\n    ssd_neck = SSDNeck(\n        in_channels=[4],\n        out_channels=[4, 8, 16],\n        level_strides=[2, 1],\n        level_paddings=[1, 0])\n    feats = (torch.rand(1, 4, 16, 16), )\n    outs = ssd_neck(feats)\n    assert outs[0].shape == (1, 4, 16, 16)\n    assert outs[1].shape == (1, 8, 8, 8)\n    assert outs[2].shape == (1, 16, 6, 6)\n\n    # test SSD-Lite Neck\n    ssd_neck = SSDNeck(\n        in_channels=[4, 8],\n        out_channels=[4, 8, 16],\n        level_strides=[1],\n        level_paddings=[1],\n        l2_norm_scale=None,\n        use_depthwise=True,\n        norm_cfg=dict(type='BN'),\n        act_cfg=dict(type='ReLU6'))\n    assert not hasattr(ssd_neck, 'l2_norm')\n\n    from mmcv.cnn.bricks import DepthwiseSeparableConvModule\n    assert isinstance(ssd_neck.extra_layers[0][-1],\n                      DepthwiseSeparableConvModule)\n\n    feats = (torch.rand(1, 4, 8, 8), torch.rand(1, 8, 8, 8))\n    outs = ssd_neck(feats)\n    assert outs[0].shape == (1, 4, 8, 8)\n    assert outs[1].shape == (1, 8, 8, 8)\n    assert outs[2].shape == (1, 16, 8, 8)\n\n\ndef test_yolox_pafpn():\n    s = 64\n    in_channels = [8, 16, 32, 64]\n    feat_sizes = [s // 2**i for i in range(4)]  # [64, 32, 16, 8]\n    out_channels = 24\n    feats = [\n        torch.rand(1, in_channels[i], feat_sizes[i], feat_sizes[i])\n        for i in range(len(in_channels))\n    ]\n    neck = YOLOXPAFPN(in_channels=in_channels, out_channels=out_channels)\n    outs = neck(feats)\n    assert len(outs) == len(feats)\n    for i in range(len(feats)):\n        assert outs[i].shape[1] == out_channels\n        assert outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    # test depth-wise\n    neck = YOLOXPAFPN(\n        in_channels=in_channels, out_channels=out_channels, use_depthwise=True)\n\n    from mmcv.cnn.bricks import DepthwiseSeparableConvModule\n    assert isinstance(neck.downsamples[0], DepthwiseSeparableConvModule)\n\n    outs = neck(feats)\n    assert len(outs) == len(feats)\n    for i in range(len(feats)):\n        assert outs[i].shape[1] == out_channels\n        assert outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n\ndef test_dyhead():\n    s = 64\n    in_channels = 8\n    out_channels = 16\n    feat_sizes = [s // 2**i for i in range(4)]  # [64, 32, 16, 8]\n    feats = [\n        torch.rand(1, in_channels, feat_sizes[i], feat_sizes[i])\n        for i in range(len(feat_sizes))\n    ]\n    neck = DyHead(\n        in_channels=in_channels, out_channels=out_channels, num_blocks=3)\n    outs = neck(feats)\n    assert len(outs) == len(feats)\n    for i in range(len(outs)):\n        assert outs[i].shape[1] == out_channels\n        assert outs[i].shape[2] == outs[i].shape[3] == s // (2**i)\n\n    feat = torch.rand(1, 8, 4, 4)\n    # input feat must be tuple or list\n    with pytest.raises(AssertionError):\n        neck(feat)\n\n\ndef test_fpg():\n    # end_level=-1 is equal to end_level=3\n    norm_cfg = dict(type='BN', requires_grad=True)\n    FPG(in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        inter_channels=8,\n        num_outs=5,\n        add_extra_convs=True,\n        start_level=1,\n        end_level=-1,\n        stack_times=9,\n        paths=['bu'] * 9,\n        same_down_trans=None,\n        same_up_trans=dict(\n            type='conv',\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_lateral_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_down_trans=dict(\n            type='interpolation_conv',\n            mode='nearest',\n            kernel_size=3,\n            norm_cfg=norm_cfg,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        across_up_trans=None,\n        across_skip_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        output_trans=dict(\n            type='last_conv',\n            kernel_size=3,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        norm_cfg=norm_cfg,\n        skip_inds=[(0, 1, 2, 3), (0, 1, 2), (0, 1), (0, ), ()])\n    FPG(in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        inter_channels=8,\n        num_outs=5,\n        add_extra_convs=True,\n        start_level=1,\n        end_level=3,\n        stack_times=9,\n        paths=['bu'] * 9,\n        same_down_trans=None,\n        same_up_trans=dict(\n            type='conv',\n            kernel_size=3,\n            stride=2,\n            padding=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_lateral_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        across_down_trans=dict(\n            type='interpolation_conv',\n            mode='nearest',\n            kernel_size=3,\n            norm_cfg=norm_cfg,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        across_up_trans=None,\n        across_skip_trans=dict(\n            type='conv',\n            kernel_size=1,\n            norm_cfg=norm_cfg,\n            inplace=False,\n            order=('act', 'conv', 'norm')),\n        output_trans=dict(\n            type='last_conv',\n            kernel_size=3,\n            order=('act', 'conv', 'norm'),\n            inplace=False),\n        norm_cfg=norm_cfg,\n        skip_inds=[(0, 1, 2, 3), (0, 1, 2), (0, 1), (0, ), ()])\n\n    # `end_level` is larger than len(in_channels) - 1\n    with pytest.raises(AssertionError):\n        FPG(in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            stack_times=9,\n            paths=['bu'] * 9,\n            start_level=1,\n            end_level=4,\n            num_outs=2,\n            skip_inds=[(0, 1, 2, 3), (0, 1, 2), (0, 1), (0, ), ()])\n\n    # `num_outs` is not equal to end_level - start_level + 1\n    with pytest.raises(AssertionError):\n        FPG(in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            stack_times=9,\n            paths=['bu'] * 9,\n            start_level=1,\n            end_level=2,\n            num_outs=3,\n            skip_inds=[(0, 1, 2, 3), (0, 1, 2), (0, 1), (0, ), ()])\n\n\ndef test_fpn_carafe():\n    # end_level=-1 is equal to end_level=3\n    FPN_CARAFE(\n        in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        start_level=0,\n        end_level=3,\n        num_outs=4)\n    FPN_CARAFE(\n        in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        start_level=0,\n        end_level=-1,\n        num_outs=4)\n    # `end_level` is larger than len(in_channels) - 1\n    with pytest.raises(AssertionError):\n        FPN_CARAFE(\n            in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            start_level=1,\n            end_level=4,\n            num_outs=2)\n\n    # `num_outs` is not equal to end_level - start_level + 1\n    with pytest.raises(AssertionError):\n        FPN_CARAFE(\n            in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            start_level=1,\n            end_level=2,\n            num_outs=3)\n\n\ndef test_nas_fpn():\n    # end_level=-1 is equal to end_level=3\n    NASFPN(\n        in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        stack_times=9,\n        start_level=0,\n        end_level=3,\n        num_outs=4)\n    NASFPN(\n        in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        stack_times=9,\n        start_level=0,\n        end_level=-1,\n        num_outs=4)\n    # `end_level` is larger than len(in_channels) - 1\n    with pytest.raises(AssertionError):\n        NASFPN(\n            in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            stack_times=9,\n            start_level=1,\n            end_level=4,\n            num_outs=2)\n\n    # `num_outs` is not equal to end_level - start_level + 1\n    with pytest.raises(AssertionError):\n        NASFPN(\n            in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            stack_times=9,\n            start_level=1,\n            end_level=2,\n            num_outs=3)\n\n\ndef test_nasfcos_fpn():\n    # end_level=-1 is equal to end_level=3\n    NASFCOS_FPN(\n        in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        start_level=0,\n        end_level=3,\n        num_outs=4)\n    NASFCOS_FPN(\n        in_channels=[8, 16, 32, 64],\n        out_channels=8,\n        start_level=0,\n        end_level=-1,\n        num_outs=4)\n\n    # `end_level` is larger than len(in_channels) - 1\n    with pytest.raises(AssertionError):\n        NASFCOS_FPN(\n            in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            start_level=1,\n            end_level=4,\n            num_outs=2)\n\n    # `num_outs` is not equal to end_level - start_level + 1\n    with pytest.raises(AssertionError):\n        NASFCOS_FPN(\n            in_channels=[8, 16, 32, 64],\n            out_channels=8,\n            start_level=1,\n            end_level=2,\n            num_outs=3)\n\n\ndef test_ssh_neck():\n    \"\"\"Tests ssh.\"\"\"\n    s = 64\n    in_channels = [8, 16, 32, 64]\n    feat_sizes = [s // 2**i for i in range(4)]  # [64, 32, 16, 8]\n    out_channels = [16, 32, 64, 128]\n    ssh_model = SSH(\n        num_scales=4, in_channels=in_channels, out_channels=out_channels)\n\n    feats = [\n        torch.rand(1, in_channels[i], feat_sizes[i], feat_sizes[i])\n        for i in range(len(in_channels))\n    ]\n    outs = ssh_model(feats)\n    assert len(outs) == len(feats)\n    for i in range(len(outs)):\n        assert outs[i].shape == \\\n            (1, out_channels[i], feat_sizes[i], feat_sizes[i])\n"
  },
  {
    "path": "tests/test_models/test_reid/test_base_reid.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import ReIDDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestBaseReID(TestCase):\n\n    @classmethod\n    def setUpClass(cls) -> None:\n        register_all_modules()\n\n    @parameterized.expand([\n        'reid/reid_r50_8xb32-6e_mot17train80_test-mot17val20.py',\n    ])\n    def test_forward(self, cfg_file):\n        model_cfg = get_detector_cfg(cfg_file)\n        model = MODELS.build(model_cfg)\n        inputs = torch.rand(1, 4, 3, 256, 128)\n        data_samples = [\n            ReIDDataSample().set_gt_label(label) for label in (0, 0, 1, 1)\n        ]\n\n        # test mode='tensor'\n        feats = model(inputs, mode='tensor')\n        assert feats.shape == (4, 128)\n\n        # test mode='loss'\n        losses = model(inputs, data_samples, mode='loss')\n        assert losses.keys() == {'triplet_loss', 'ce_loss', 'accuracy_top-1'}\n        assert losses['ce_loss'].item() > 0\n        assert losses['triplet_loss'].item() > 0\n\n        # test mode='predict'\n        predictions = model(inputs, data_samples, mode='predict')\n        for pred in predictions:\n            assert isinstance(pred, ReIDDataSample)\n            assert isinstance(pred.pred_feature, torch.Tensor)\n            assert isinstance(pred.gt_label.label, torch.Tensor)\n            assert pred.pred_feature.shape == (128, )\n"
  },
  {
    "path": "tests/test_models/test_reid/test_fc_module.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.models import FcModule\n\n\nclass TestFcModule(TestCase):\n\n    def test_forward(self):\n        inputs = torch.rand(32, 128)\n\n        # test\n        fc = FcModule(\n            in_channels=128,\n            out_channels=32,\n        )\n        fc.init_weights()\n        outputs = fc(inputs)\n        assert outputs.shape == (32, 32)\n\n        # test with norm\n        fc = FcModule(\n            in_channels=128,\n            out_channels=32,\n            norm_cfg=dict(type='BN1d'),\n        )\n        outputs = fc(inputs)\n        assert outputs.shape == (32, 32)\n\n        # test with norm and act\n        fc = FcModule(\n            in_channels=128,\n            out_channels=32,\n            norm_cfg=dict(type='BN1d'),\n            act_cfg=dict(type='ReLU'),\n        )\n        outputs = fc(inputs)\n        assert outputs.shape == (32, 32)\n"
  },
  {
    "path": "tests/test_models/test_reid/test_gap.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.models import GlobalAveragePooling\n\n\nclass TestGlobalAveragePooling(TestCase):\n\n    def test_forward(self):\n        inputs = torch.rand(32, 128, 14, 14)\n\n        # test AdaptiveAvgPool2d\n        neck = GlobalAveragePooling()\n        outputs = neck(inputs)\n        assert outputs.shape == (32, 128)\n\n        # test kernel_size\n        neck = GlobalAveragePooling(kernel_size=7)\n        outputs = neck(inputs)\n        assert outputs.shape == (32, 128 * 2 * 2)\n\n        # test kenel_size and stride\n        neck = GlobalAveragePooling(kernel_size=7, stride=2)\n        outputs = neck(inputs)\n        assert outputs.shape == (32, 128 * 4 * 4)\n"
  },
  {
    "path": "tests/test_models/test_reid/test_linear_reid_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import ReIDDataSample\nfrom mmdet.utils import register_all_modules\n\n\nclass TestLinearReIDHead(TestCase):\n\n    @classmethod\n    def setUpClass(cls) -> None:\n        register_all_modules()\n        head_cfg = dict(\n            type='LinearReIDHead',\n            num_fcs=1,\n            in_channels=128,\n            fc_channels=64,\n            out_channels=32,\n            num_classes=2,\n            loss_cls=dict(type='mmpretrain.CrossEntropyLoss', loss_weight=1.0),\n            loss_triplet=dict(type='TripletLoss', margin=0.3, loss_weight=1.0),\n            norm_cfg=dict(type='BN1d'),\n            act_cfg=dict(type='ReLU'))\n        cls.head = MODELS.build(head_cfg)\n        cls.inputs = (torch.rand(4, 128), torch.rand(4, 128))\n        cls.data_samples = [\n            ReIDDataSample().set_gt_label(label) for label in (0, 0, 1, 1)\n        ]\n\n    def test_forward(self):\n        outputs = self.head(self.inputs)\n        assert outputs.shape == (4, 32)\n\n    def test_loss(self):\n        losses = self.head.loss(self.inputs, self.data_samples)\n        assert losses.keys() == {'triplet_loss', 'ce_loss', 'accuracy_top-1'}\n        assert losses['ce_loss'].item() >= 0\n        assert losses['triplet_loss'].item() >= 0\n\n    def test_predict(self):\n        predictions = self.head.predict(self.inputs, self.data_samples)\n        for pred in predictions:\n            assert isinstance(pred, ReIDDataSample)\n            assert isinstance(pred.pred_feature, torch.Tensor)\n            assert isinstance(pred.gt_label.label, torch.Tensor)\n            assert pred.pred_feature.shape == (32, )\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_bbox_heads/test_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.roi_heads.bbox_heads import (BBoxHead, Shared2FCBBoxHead,\n                                               Shared4Conv1FCBBoxHead)\nfrom mmdet.models.task_modules.samplers import SamplingResult\n\n\nclass TestBboxHead(TestCase):\n\n    def test_init(self):\n        # Shared2FCBBoxHead\n        bbox_head = Shared2FCBBoxHead(\n            in_channels=1, fc_out_channels=1, num_classes=4)\n        self.assertTrue(bbox_head.fc_cls)\n        self.assertTrue(bbox_head.fc_reg)\n        self.assertEqual(len(bbox_head.shared_fcs), 2)\n\n        # Shared4Conv1FCBBoxHead\n        bbox_head = Shared4Conv1FCBBoxHead(\n            in_channels=1, fc_out_channels=1, num_classes=4)\n        self.assertTrue(bbox_head.fc_cls)\n        self.assertTrue(bbox_head.fc_reg)\n        self.assertEqual(len(bbox_head.shared_convs), 4)\n        self.assertEqual(len(bbox_head.shared_fcs), 1)\n\n    def test_bbox_head_get_results(self):\n        num_classes = 6\n        bbox_head = BBoxHead(reg_class_agnostic=True, num_classes=num_classes)\n        s = 128\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        num_samples = 2\n        rois = [torch.rand((num_samples, 5))]\n        cls_scores = [torch.rand((num_samples, num_classes + 1))]\n        bbox_preds = [torch.rand((num_samples, 4))]\n\n        # with nms\n        rcnn_test_cfg = ConfigDict(\n            score_thr=0.,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas,\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertLessEqual(len(result_list[0]), num_samples * num_classes)\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(result_list[0].bboxes.shape[1], 4)\n        self.assertEqual(len(result_list[0].scores.shape), 1)\n        self.assertEqual(len(result_list[0].labels.shape), 1)\n\n        # without nms\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), num_samples)\n        self.assertEqual(result_list[0].bboxes.shape, bbox_preds[0].shape)\n        self.assertEqual(result_list[0].scores.shape, cls_scores[0].shape)\n        self.assertIsNone(result_list[0].get('label', None))\n\n        # num_samples is 0\n        num_samples = 0\n        rois = [torch.rand((num_samples, 5))]\n        cls_scores = [torch.rand((num_samples, num_classes + 1))]\n        bbox_preds = [torch.rand((num_samples, 4))]\n\n        # with nms\n        rcnn_test_cfg = ConfigDict(\n            score_thr=0.,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas,\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), 0)\n        self.assertEqual(result_list[0].bboxes.shape[1], 4)\n\n        # without nms\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), 0)\n        self.assertEqual(result_list[0].bboxes.shape, bbox_preds[0].shape)\n        self.assertIsNone(result_list[0].get('label', None))\n\n    def test_bbox_head_refine_bboxes(self):\n        num_classes = 6\n        bbox_head = BBoxHead(reg_class_agnostic=True, num_classes=num_classes)\n        s = 128\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        sampling_results = [SamplingResult.random()]\n        num_samples = 20\n        rois = torch.rand((num_samples, 4))\n        roi_img_ids = torch.zeros(num_samples, 1)\n        rois = torch.cat((roi_img_ids, rois), dim=1)\n        cls_scores = torch.rand((num_samples, num_classes + 1))\n        bbox_preds = torch.rand((num_samples, 4))\n        labels = torch.randint(0, num_classes + 1, (num_samples, )).long()\n        bbox_targets = (labels, None, None, None)\n        bbox_results = dict(\n            rois=rois,\n            bbox_pred=bbox_preds,\n            cls_score=cls_scores,\n            bbox_targets=bbox_targets)\n\n        bbox_list = bbox_head.refine_bboxes(\n            sampling_results=sampling_results,\n            bbox_results=bbox_results,\n            batch_img_metas=img_metas)\n\n        self.assertGreaterEqual(num_samples, len(bbox_list[0]))\n        self.assertIsInstance(bbox_list[0], InstanceData)\n        self.assertEqual(bbox_list[0].bboxes.shape[1], 4)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_bbox_heads/test_double_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads.bbox_heads import DoubleConvFCBBoxHead\n\n\nclass TestDoubleBboxHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward_loss(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        double_bbox_head = DoubleConvFCBBoxHead(\n            num_convs=4,\n            num_fcs=2,\n            in_channels=1,\n            conv_out_channels=4,\n            fc_out_channels=4)\n        double_bbox_head = double_bbox_head.to(device=device)\n\n        num_samples = 4\n        feats = torch.rand((num_samples, 1, 7, 7)).to(device)\n        double_bbox_head(x_cls=feats, x_reg=feats)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_bbox_heads/test_multi_instance_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.roi_heads.bbox_heads import MultiInstanceBBoxHead\n\n\nclass TestMultiInstanceBBoxHead(TestCase):\n\n    def test_init(self):\n        bbox_head = MultiInstanceBBoxHead(\n            num_instance=2,\n            with_refine=True,\n            num_shared_fcs=2,\n            in_channels=1,\n            fc_out_channels=1,\n            num_classes=4)\n        self.assertTrue(bbox_head.shared_fcs_ref)\n        self.assertTrue(bbox_head.fc_reg)\n        self.assertTrue(bbox_head.fc_cls)\n        self.assertEqual(len(bbox_head.shared_fcs), 2)\n        self.assertEqual(len(bbox_head.fc_reg), 2)\n        self.assertEqual(len(bbox_head.fc_cls), 2)\n\n    def test_bbox_head_get_results(self):\n        num_classes = 1\n        num_instance = 2\n        bbox_head = MultiInstanceBBoxHead(\n            num_instance=num_instance,\n            num_shared_fcs=2,\n            reg_class_agnostic=True,\n            num_classes=num_classes)\n        s = 128\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        num_samples = 2\n        rois = [torch.rand((num_samples, 5))]\n        cls_scores = []\n        bbox_preds = []\n        for k in range(num_instance):\n            cls_scores.append(torch.rand((num_samples, num_classes + 1)))\n            bbox_preds.append(torch.rand((num_samples, 4)))\n        cls_scores = [torch.cat(cls_scores, dim=1)]\n        bbox_preds = [torch.cat(bbox_preds, dim=1)]\n\n        # with nms\n        rcnn_test_cfg = ConfigDict(\n            nms=dict(type='nms', iou_threshold=0.5),\n            score_thr=0.01,\n            max_per_img=500)\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas,\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertLessEqual(\n            len(result_list[0]), num_samples * num_instance * num_classes)\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(result_list[0].bboxes.shape[1], 4)\n        self.assertEqual(len(result_list[0].scores.shape), 1)\n        self.assertEqual(len(result_list[0].labels.shape), 1)\n\n        # without nms\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), num_samples * num_instance)\n        self.assertIsNone(result_list[0].get('label', None))\n\n        # num_samples is 0\n        num_samples = 0\n        rois = [torch.rand((num_samples, 5))]\n        cls_scores = []\n        bbox_preds = []\n        for k in range(num_instance):\n            cls_scores.append(torch.rand((num_samples, num_classes + 1)))\n            bbox_preds.append(torch.rand((num_samples, 4)))\n        cls_scores = [torch.cat(cls_scores, dim=1)]\n        bbox_preds = [torch.cat(bbox_preds, dim=1)]\n\n        # with nms\n        rcnn_test_cfg = ConfigDict(\n            score_thr=0.,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas,\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), 0)\n        self.assertEqual(result_list[0].bboxes.shape[1], 4)\n\n        # without nms\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), 0 * num_instance)\n        self.assertIsNone(result_list[0].get('label', None))\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_bbox_heads/test_sabl_bbox_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.roi_heads.bbox_heads import SABLHead\nfrom mmdet.models.task_modules.samplers import SamplingResult\n\n\nclass TestSABLBboxHead(TestCase):\n\n    def test_init(self):\n        bbox_head = SABLHead(\n            cls_in_channels=1,\n            cls_out_channels=1,\n            reg_in_channels=1,\n            reg_offset_out_channels=1,\n            reg_cls_out_channels=1,\n            num_classes=4)\n        self.assertTrue(bbox_head.fc_cls)\n        self.assertTrue(hasattr(bbox_head, 'reg_cls_fcs'))\n        self.assertTrue(hasattr(bbox_head, 'reg_offset_fcs'))\n        self.assertFalse(hasattr(bbox_head, 'fc_reg'))\n\n    def test_bbox_head_get_results(self):\n        num_classes = 6\n        bbox_head = SABLHead(reg_class_agnostic=True, num_classes=num_classes)\n        s = 128\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n\n        num_samples = 2\n        rois = [torch.rand((num_samples, 5))]\n        cls_scores = [torch.rand((num_samples, num_classes + 1))]\n        bbox_preds = [(torch.rand(\n            (num_samples, 28)), torch.rand((num_samples, 28)))]\n\n        # with nms\n        rcnn_test_cfg = ConfigDict(\n            score_thr=0.,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas,\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertLessEqual(len(result_list[0]), num_samples * num_classes)\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(result_list[0].bboxes.shape[1], 4)\n        self.assertEqual(len(result_list[0].scores.shape), 1)\n        self.assertEqual(len(result_list[0].labels.shape), 1)\n\n        # without nms\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), num_samples)\n        self.assertEqual(result_list[0].scores.shape, cls_scores[0].shape)\n        self.assertIsNone(result_list[0].get('label', None))\n\n        # num_samples is 0\n        num_samples = 0\n        rois = [torch.rand((num_samples, 5))]\n        cls_scores = [torch.rand((num_samples, num_classes + 1))]\n        bbox_preds = [(torch.rand(\n            (num_samples, 28)), torch.rand((num_samples, 28)))]\n\n        # with nms\n        rcnn_test_cfg = ConfigDict(\n            score_thr=0.,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100)\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas,\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), 0)\n        self.assertEqual(result_list[0].bboxes.shape[1], 4)\n\n        # without nms\n        result_list = bbox_head.predict_by_feat(\n            rois=tuple(rois),\n            cls_scores=tuple(cls_scores),\n            bbox_preds=tuple(bbox_preds),\n            batch_img_metas=img_metas)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), 0)\n        self.assertIsNone(result_list[0].get('label', None))\n\n    def test_bbox_head_refine_bboxes(self):\n        num_classes = 8\n        bbox_head = SABLHead(reg_class_agnostic=True, num_classes=num_classes)\n        s = 20\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        sampling_results = [SamplingResult.random()]\n        num_samples = 20\n        rois = torch.rand((num_samples, 4))\n        roi_img_ids = torch.zeros(num_samples, 1)\n        rois = torch.cat((roi_img_ids, rois), dim=1)\n        cls_scores = torch.rand((num_samples, num_classes + 1))\n        cls_preds = torch.rand((num_samples, 28))\n        offset_preds = torch.rand((num_samples, 28))\n        labels = torch.randint(0, num_classes + 1, (num_samples, )).long()\n        bbox_targets = (labels, None, None, None)\n        bbox_results = dict(\n            rois=rois,\n            bbox_pred=(cls_preds, offset_preds),\n            cls_score=cls_scores,\n            bbox_targets=bbox_targets)\n\n        bbox_list = bbox_head.refine_bboxes(\n            sampling_results=sampling_results,\n            bbox_results=bbox_results,\n            batch_img_metas=img_metas)\n\n        self.assertGreaterEqual(num_samples, len(bbox_list[0]))\n        self.assertIsInstance(bbox_list[0], InstanceData)\n        self.assertEqual(bbox_list[0].bboxes.shape[1], 4)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_bbox_heads/test_scnet_bbox_head.py",
    "content": "import unittest\n\nimport torch\n\nfrom mmdet.models.roi_heads.bbox_heads import SCNetBBoxHead\n\n\nclass TestSCNetBBoxHead(unittest.TestCase):\n\n    def test_forward(self):\n        x = torch.rand((2, 1, 16, 16))\n        bbox_head = SCNetBBoxHead(\n            num_shared_fcs=2,\n            in_channels=1,\n            roi_feat_size=16,\n            conv_out_channels=1,\n            fc_out_channels=256,\n        )\n        results = bbox_head(x, return_shared_feat=False)\n        self.assertEqual(len(results), 2)\n        results = bbox_head(x, return_shared_feat=True)\n        self.assertEqual(len(results), 3)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_cascade_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads import StandardRoIHead  # noqa\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\n\n\nclass TestCascadeRoIHead(TestCase):\n\n    @parameterized.expand(\n        ['cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py'])\n    def test_init(self, cfg_file):\n        \"\"\"Test init standard RoI head.\"\"\"\n        # Normal Cascade Mask R-CNN RoI head\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        assert roi_head.with_bbox\n        assert roi_head.with_mask\n\n    @parameterized.expand(\n        ['cascade_rcnn/cascade-mask-rcnn_r50_fpn_1x_coco.py'])\n    def test_cascade_roi_head_loss(self, cfg_file):\n        \"\"\"Tests standard roi head loss when truth is empty and non-empty.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        # When truth is non-empty then both cls, box, and mask loss\n        # should be nonzero for random inputs\n        img_shape_list = [(3, s, s) for _ in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box and mask loss.\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss_cls' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n            elif 'loss_bbox' in name or 'loss_mask' in name:\n                self.assertEqual(value.sum(), 0)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_dynamic_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestDynamicRoIHead(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n        self.roi_head_cfg = get_roi_head_cfg(\n            'dynamic_rcnn/dynamic-rcnn_r50_fpn_1x_coco.py')\n\n    def test_init(self):\n        roi_head = MODELS.build(self.roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_dynamic_roi_head_loss(self, device):\n        \"\"\"Tests trident roi head predict.\"\"\"\n        if not torch.cuda.is_available() and device == 'cuda':\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.to(device=device)\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device=device))\n\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        loss_cls = out['loss_cls']\n        loss_bbox = out['loss_bbox']\n        self.assertGreater(loss_cls.sum(), 0, 'cls loss should be non-zero')\n        self.assertGreater(loss_bbox.sum(), 0, 'box loss should be non-zero')\n\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        empty_cls_loss = out['loss_cls']\n        empty_bbox_loss = out['loss_bbox']\n        self.assertGreater(empty_cls_loss.sum(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_bbox_loss.sum(), 0,\n            'there should be no box loss when there are no true boxes')\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_grid_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestGridRoIHead(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n        self.roi_head_cfg = get_roi_head_cfg(\n            'grid_rcnn/grid-rcnn_r50_fpn_gn-head_2x_coco.py')\n\n    def test_init(self):\n        roi_head = MODELS.build(self.roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_grid_roi_head_loss(self, device):\n        \"\"\"Tests trident roi head predict.\"\"\"\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.to(device=device)\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device=device))\n\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        loss_cls = out['loss_cls']\n        loss_grid = out['loss_grid']\n        self.assertGreater(loss_cls.sum(), 0, 'cls loss should be non-zero')\n        self.assertGreater(loss_grid.sum(), 0, 'grid loss should be non-zero')\n\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        empty_cls_loss = out['loss_cls']\n        self.assertGreater(empty_cls_loss.sum(), 0,\n                           'cls loss should be non-zero')\n        self.assertNotIn(\n            'loss_grid', out,\n            'grid loss should be passed when there are no true boxes')\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_grid_roi_head_predict(self, device):\n        \"\"\"Tests trident roi head predict.\"\"\"\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.to(device=device)\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device=device))\n\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        roi_head.predict(feats, proposals_list, batch_data_samples)\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_grid_roi_head_forward(self, device):\n        \"\"\"Tests trident roi head forward.\"\"\"\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.to(device=device)\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device=device))\n\n        image_shapes = [(3, s, s)]\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        roi_head.forward(feats, proposals_list)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_htc_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads import HybridTaskCascadeRoIHead  # noqa\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\n\n\nclass TestHTCRoIHead(TestCase):\n\n    @parameterized.expand(['htc/htc_r50_fpn_1x_coco.py'])\n    def test_init(self, cfg_file):\n        \"\"\"Test init htc RoI head.\"\"\"\n        # Normal HTC RoI head\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        assert roi_head.with_bbox\n        assert roi_head.with_mask\n        assert roi_head.with_semantic\n\n    @parameterized.expand(['htc/htc_r50_fpn_1x_coco.py'])\n    def test_htc_roi_head_loss(self, cfg_file):\n        \"\"\"Tests htc roi head loss when truth is empty and non-empty.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        # When truth is non-empty then both cls, box, and mask loss\n        # should be nonzero for random inputs\n        img_shape_list = [(3, s, s) for _ in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            with_semantic=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box and mask loss.\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            with_semantic=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss_cls' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n            elif 'loss_bbox' in name or 'loss_mask' in name:\n                self.assertEqual(value.sum(), 0)\n\n    @parameterized.expand(['htc/htc_r50_fpn_1x_coco.py'])\n    def test_htc_roi_head_predict(self, cfg_file):\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        img_shape_list = [(3, s, s) for _ in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        results = roi_head.predict(\n            feats, proposal_list, batch_data_samples, rescale=True)\n        self.assertEqual(results[0].masks.shape[-2:], (s, s))\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_coarse_mask_head.py",
    "content": "import unittest\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads.mask_heads import CoarseMaskHead\n\n\nclass TestCoarseMaskHead(unittest.TestCase):\n\n    def test_init(self):\n        with self.assertRaises(AssertionError):\n            CoarseMaskHead(num_fcs=0)\n\n        with self.assertRaises(AssertionError):\n            CoarseMaskHead(downsample_factor=0.5)\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        x = torch.rand((1, 32, 7, 7)).to(device)\n        mask_head = CoarseMaskHead(\n            downsample_factor=2,\n            in_channels=32,\n            conv_out_channels=32,\n            roi_feat_size=7).to(device)\n        mask_head.init_weights()\n        res = mask_head(x)\n        self.assertEqual(res.shape[-2:], (3, 3))\n\n        mask_head = CoarseMaskHead(\n            downsample_factor=1,\n            in_channels=32,\n            conv_out_channels=32,\n            roi_feat_size=7).to(device)\n        mask_head.init_weights()\n        res = mask_head(x)\n        self.assertEqual(res.shape[-2:], (7, 7))\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_fcn_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads.mask_heads import FCNMaskHead\n\n\nclass TestFCNMaskHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_get_seg_masks(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n        num_classes = 6\n        mask_head = FCNMaskHead(\n            num_convs=1,\n            in_channels=1,\n            conv_out_channels=1,\n            num_classes=num_classes)\n        rcnn_test_cfg = ConfigDict(\n            score_thr=0.05,\n            nms=dict(type='nms', iou_threshold=0.5),\n            max_per_img=100,\n            mask_thr_binary=0.5)\n        s = 128\n        img_metas = {\n            'img_shape': (s, s, 3),\n            'scale_factor': (1, 1),\n            'ori_shape': (s, s, 3)\n        }\n        result = InstanceData(metainfo=img_metas)\n\n        num_samples = 2\n        mask_pred = [torch.rand((num_samples, num_classes, 14, 14)).to(device)]\n        result.bboxes = torch.rand((num_samples, 4)).to(device)\n        result.labels = torch.randint(\n            num_classes, (num_samples, ), dtype=torch.long).to(device)\n        mask_head.to(device=device)\n        result_list = mask_head.predict_by_feat(\n            mask_preds=tuple(mask_pred),\n            results_list=[result],\n            batch_img_metas=[img_metas],\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), num_samples)\n        self.assertEqual(result_list[0].masks.shape, (num_samples, s, s))\n\n        # test with activate_map, `mask_pred` has been activated before\n        num_samples = 2\n        mask_pred = [torch.rand((num_samples, num_classes, 14, 14)).to(device)]\n        mask_pred = [m.sigmoid().detach() for m in mask_pred]\n        result.bboxes = torch.rand((num_samples, 4)).to(device)\n        result.labels = torch.randint(\n            num_classes, (num_samples, ), dtype=torch.long).to(device)\n        mask_head.to(device=device)\n        result_list = mask_head.predict_by_feat(\n            mask_preds=tuple(mask_pred),\n            results_list=[result],\n            batch_img_metas=[img_metas],\n            rcnn_test_cfg=rcnn_test_cfg,\n            activate_map=True)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), num_samples)\n        self.assertEqual(result_list[0].masks.shape, (num_samples, s, s))\n\n        # num_samples is 0\n        num_samples = 0\n        result = InstanceData(metainfo=img_metas)\n        mask_pred = [torch.rand((num_samples, num_classes, 14, 14)).to(device)]\n        result.bboxes = torch.zeros((num_samples, 4)).to(device)\n        result.labels = torch.zeros((num_samples, )).to(device)\n        result_list = mask_head.predict_by_feat(\n            mask_preds=tuple(mask_pred),\n            results_list=[result],\n            batch_img_metas=[img_metas],\n            rcnn_test_cfg=rcnn_test_cfg)\n\n        self.assertIsInstance(result_list[0], InstanceData)\n        self.assertEqual(len(result_list[0]), num_samples)\n        self.assertEqual(result_list[0].masks.shape, (num_samples, s, s))\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_feature_relay_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads.mask_heads import FeatureRelayHead\n\n\nclass TestFeatureRelayHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n        mask_head = FeatureRelayHead(in_channels=10, out_conv_channels=10)\n\n        x = torch.rand((1, 10))\n        results = mask_head(x)\n        self.assertIsInstance(results, Tensor)\n        x = torch.empty((0, 10))\n        results = mask_head(x)\n        self.assertEqual(results, None)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_fused_semantic_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads.mask_heads import FusedSemanticHead\n\n\nclass TestFusedSemanticHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward_loss(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        semantic_head = FusedSemanticHead(\n            num_ins=5,\n            fusion_level=1,\n            in_channels=4,\n            conv_out_channels=4,\n            num_classes=6)\n        feats = [\n            torch.rand((1, 4, 32 // 2**(i + 1), 32 // 2**(i + 1)))\n            for i in range(5)\n        ]\n        mask_pred, x = semantic_head(feats)\n        labels = torch.randint(0, 6, (1, 1, 64, 64))\n        loss = semantic_head.loss(mask_pred, labels)\n        self.assertIsInstance(loss, Tensor)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_global_context_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads.mask_heads import GlobalContextHead\n\n\nclass TestGlobalContextHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward_loss(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        head = GlobalContextHead(\n            num_convs=1, in_channels=4, conv_out_channels=4, num_classes=10)\n        feats = [\n            torch.rand((1, 4, 64 // 2**(i + 1), 64 // 2**(i + 1)))\n            for i in range(5)\n        ]\n        mc_pred, x = head(feats)\n\n        labels = [torch.randint(0, 10, (10, ))]\n        loss = head.loss(mc_pred, labels)\n        self.assertIsInstance(loss, Tensor)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_grid_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads.mask_heads import GridHead\nfrom mmdet.models.utils import unpack_gt_instances\nfrom mmdet.testing import (demo_mm_inputs, demo_mm_proposals,\n                           demo_mm_sampling_results)\n\n\nclass TestGridHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_grid_head_loss(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        grid_head = GridHead()\n        grid_head.to(device=device)\n\n        s = 256\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n\n        train_cfg = ConfigDict(dict(pos_radius=1))\n\n        # prepare ground truth\n        (batch_gt_instances, batch_gt_instances_ignore,\n         _) = unpack_gt_instances(batch_data_samples)\n        sampling_results = demo_mm_sampling_results(\n            proposals_list=proposals_list,\n            batch_gt_instances=batch_gt_instances,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        # prepare grid feats\n        pos_bboxes = torch.cat([res.pos_bboxes for res in sampling_results])\n        grid_feats = torch.rand((pos_bboxes.size(0), 256, 14, 14)).to(device)\n        sample_idx = torch.arange(0, pos_bboxes.size(0))\n        grid_pred = grid_head(grid_feats)\n\n        grid_head.loss(grid_pred, sample_idx, sampling_results, train_cfg)\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_mask_iou_head_predict_by_feat(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        grid_head = GridHead()\n        grid_head.to(device=device)\n\n        s = 128\n        num_samples = 2\n        num_classes = 4\n        img_metas = {\n            'img_shape': (s, s, 3),\n            'scale_factor': (1, 1),\n            'ori_shape': (s, s, 3)\n        }\n        results = InstanceData(metainfo=img_metas)\n        results.bboxes = torch.rand((num_samples, 4)).to(device)\n        results.scores = torch.rand((num_samples, )).to(device)\n        results.labels = torch.randint(\n            num_classes, (num_samples, ), dtype=torch.long).to(device)\n\n        grid_feats = torch.rand((num_samples, 256, 14, 14)).to(device)\n        grid_preds = grid_head(grid_feats)\n        grid_head.predict_by_feat(\n            grid_preds=grid_preds,\n            results_list=[results],\n            batch_img_metas=[img_metas])\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_htc_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads.mask_heads import HTCMaskHead\n\n\nclass TestHTCMaskHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n        num_classes = 6\n        mask_head = HTCMaskHead(\n            with_conv_res=True,\n            num_convs=1,\n            in_channels=1,\n            conv_out_channels=1,\n            num_classes=num_classes)\n\n        x = torch.rand((1, 1, 10, 10))\n        res_feat = torch.rand((1, 1, 10, 10))\n\n        with self.assertRaises(AssertionError):\n            mask_head(x, return_logits=False, return_feat=False)\n\n        results = mask_head(x)\n        self.assertEqual(len(results), 2)\n        results = mask_head(x, res_feat=res_feat)\n        self.assertEqual(len(results), 2)\n\n        results = mask_head(x, return_logits=False)\n        self.assertIsInstance(results, Tensor)\n        results = mask_head(x, return_feat=False)\n        self.assertIsInstance(results, Tensor)\n\n        results = mask_head(x, res_feat=res_feat, return_logits=False)\n        self.assertIsInstance(results, Tensor)\n        results = mask_head(x, res_feat=res_feat, return_feat=False)\n        self.assertIsInstance(results, Tensor)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_maskiou_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads.mask_heads import MaskIoUHead\nfrom mmdet.models.utils import unpack_gt_instances\nfrom mmdet.structures.mask import mask_target\nfrom mmdet.testing import (demo_mm_inputs, demo_mm_proposals,\n                           demo_mm_sampling_results)\n\n\nclass TestMaskIoUHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_mask_iou_head_loss_and_target(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        mask_iou_head = MaskIoUHead(num_classes=4)\n        mask_iou_head.to(device=device)\n\n        s = 256\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        train_cfg = ConfigDict(dict(mask_size=28, mask_thr_binary=0.5))\n\n        # prepare ground truth\n        (batch_gt_instances, batch_gt_instances_ignore,\n         _) = unpack_gt_instances(batch_data_samples)\n        sampling_results = demo_mm_sampling_results(\n            proposals_list=proposals_list,\n            batch_gt_instances=batch_gt_instances,\n            batch_gt_instances_ignore=batch_gt_instances_ignore)\n\n        # prepare mask feats, pred and target\n        pos_proposals = [res.pos_priors for res in sampling_results]\n        pos_assigned_gt_inds = [\n            res.pos_assigned_gt_inds for res in sampling_results\n        ]\n        gt_masks = [res.masks for res in batch_gt_instances]\n        mask_targets = mask_target(pos_proposals, pos_assigned_gt_inds,\n                                   gt_masks, train_cfg)\n        mask_feats = torch.rand((mask_targets.size(0), 256, 14, 14)).to(device)\n        mask_preds = torch.rand((mask_targets.size(0), 4, 28, 28)).to(device)\n\n        pos_labels = torch.cat([res.pos_gt_labels for res in sampling_results])\n        pos_mask_pred = mask_preds[range(mask_preds.size(0)), pos_labels]\n        mask_iou_pred = mask_iou_head(mask_feats, pos_mask_pred)\n        pos_mask_iou_pred = mask_iou_pred[range(mask_iou_pred.size(0)),\n                                          pos_labels]\n\n        mask_iou_head.loss_and_target(pos_mask_iou_pred, pos_mask_pred,\n                                      mask_targets, sampling_results,\n                                      batch_gt_instances, train_cfg)\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_mask_iou_head_predict_by_feat(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        mask_iou_head = MaskIoUHead(num_classes=4)\n        mask_iou_head.to(device=device)\n\n        s = 128\n        num_samples = 2\n        num_classes = 4\n        img_metas = {\n            'img_shape': (s, s, 3),\n            'scale_factor': (1, 1),\n            'ori_shape': (s, s, 3)\n        }\n        results = InstanceData(metainfo=img_metas)\n        results.bboxes = torch.rand((num_samples, 4)).to(device)\n        results.scores = torch.rand((num_samples, )).to(device)\n        results.labels = torch.randint(\n            num_classes, (num_samples, ), dtype=torch.long).to(device)\n\n        mask_feats = torch.rand((num_samples, 256, 14, 14)).to(device)\n        mask_preds = torch.rand((num_samples, num_classes, 28, 28)).to(device)\n        mask_iou_preds = mask_iou_head(\n            mask_feats, mask_preds[range(results.labels.size(0)),\n                                   results.labels])\n\n        mask_iou_head.predict_by_feat(\n            mask_iou_preds=[mask_iou_preds], results_list=[results])\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_scnet_mask_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads.mask_heads import SCNetMaskHead\n\n\nclass TestSCNetMaskHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n        num_classes = 6\n        mask_head = SCNetMaskHead(\n            conv_to_res=True,\n            num_convs=1,\n            in_channels=1,\n            conv_out_channels=1,\n            num_classes=num_classes)\n\n        x = torch.rand((1, 1, 10, 10))\n        results = mask_head(x)\n        self.assertIsInstance(results, Tensor)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_heads/test_scnet_semantic_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\nfrom torch import Tensor\n\nfrom mmdet.models.roi_heads.mask_heads import SCNetSemanticHead\n\n\nclass TestSCNetSemanticHead(TestCase):\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_forward_loss(self, device):\n        if device == 'cuda':\n            if not torch.cuda.is_available():\n                return unittest.skip('test requires GPU and torch+cuda')\n\n        semantic_head = SCNetSemanticHead(\n            num_ins=5,\n            fusion_level=1,\n            in_channels=4,\n            conv_out_channels=4,\n            num_classes=6)\n        feats = [\n            torch.rand((1, 4, 32 // 2**(i + 1), 32 // 2**(i + 1)))\n            for i in range(5)\n        ]\n        mask_pred, x = semantic_head(feats)\n        labels = torch.randint(0, 6, (1, 1, 64, 64))\n        loss = semantic_head.loss(mask_pred, labels)\n        self.assertIsInstance(loss, Tensor)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_mask_scoring_roI_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestMaskScoringRoiHead(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n        self.roi_head_cfg = get_roi_head_cfg(\n            'ms_rcnn/ms-rcnn_r50_fpn_1x_coco.py')\n\n    def test_init(self):\n        roi_head = MODELS.build(self.roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n        self.assertTrue(roi_head.with_mask)\n        self.assertTrue(roi_head.mask_iou_head)\n\n    def test_mask_scoring_roi_head_loss(self):\n        \"\"\"Tests trident roi head predict.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.cuda()\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        loss_cls = out['loss_cls']\n        loss_bbox = out['loss_bbox']\n        loss_mask = out['loss_mask']\n        self.assertGreater(loss_cls.sum(), 0, 'cls loss should be non-zero')\n        self.assertGreater(loss_bbox.sum(), 0, 'box loss should be non-zero')\n        self.assertGreater(loss_mask.sum(), 0, 'mask loss should be non-zero')\n\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        empty_cls_loss = out['loss_cls']\n        empty_bbox_loss = out['loss_bbox']\n        empty_mask_loss = out['loss_mask']\n        self.assertGreater(empty_cls_loss.sum(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_bbox_loss.sum(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_mask_loss.sum(), 0,\n            'there should be no mask loss when there are no true boxes')\n\n    def test_mask_scoring_roi_head_predict(self):\n        \"\"\"Tests trident roi head predict.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.cuda()\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n        roi_head.predict(feats, proposals_list, batch_data_samples)\n\n    def test_mask_scoring_roi_head_forward(self):\n        \"\"\"Tests trident roi head forward.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.cuda()\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n\n        image_shapes = [(3, s, s)]\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n        roi_head.forward(feats, proposals_list)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_multi_instance_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import Config\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n\ndef _fake_roi_head():\n    \"\"\"Set a fake roi head config.\"\"\"\n\n    roi_head = Config(\n        dict(\n            type='MultiInstanceRoIHead',\n            bbox_roi_extractor=dict(\n                type='SingleRoIExtractor',\n                roi_layer=dict(\n                    type='RoIAlign',\n                    output_size=7,\n                    sampling_ratio=-1,\n                    aligned=True,\n                    use_torchvision=True),\n                out_channels=256,\n                featmap_strides=[4, 8, 16, 32]),\n            bbox_head=dict(\n                type='MultiInstanceBBoxHead',\n                with_refine=False,\n                num_shared_fcs=2,\n                in_channels=256,\n                fc_out_channels=1024,\n                roi_feat_size=7,\n                num_classes=1,\n                bbox_coder=dict(\n                    type='DeltaXYWHBBoxCoder',\n                    target_means=[0., 0., 0., 0.],\n                    target_stds=[0.1, 0.1, 0.2, 0.2]),\n                reg_class_agnostic=False,\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    loss_weight=1.0,\n                    use_sigmoid=False,\n                    reduction='none'),\n                loss_bbox=dict(\n                    type='SmoothL1Loss', loss_weight=1.0, reduction='none')),\n            train_cfg=dict(\n                assigner=dict(\n                    type='MultiInstanceAssigner',\n                    pos_iou_thr=0.5,\n                    neg_iou_thr=0.5,\n                    min_pos_iou=0.3,\n                    match_low_quality=False,\n                    ignore_iof_thr=-1),\n                sampler=dict(\n                    type='MultiInsRandomSampler',\n                    num=512,\n                    pos_fraction=0.5,\n                    neg_pos_ub=-1,\n                    add_gt_as_proposals=False),\n                pos_weight=-1,\n                debug=False),\n            test_cfg=dict(\n                nms=dict(iou_threshold=0.5), score_thr=0.01, max_per_img=500)))\n\n    return roi_head\n\n\nclass TestMultiInstanceRoIHead(TestCase):\n\n    def test_init(self):\n        \"\"\"Test init multi instance RoI head.\"\"\"\n        roi_head_cfg = _fake_roi_head()\n        roi_head = MODELS.build(roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n\n    def test_standard_roi_head_loss(self):\n        \"\"\"Tests multi instance roi head loss when truth is empty and non-\n        empty.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        roi_head_cfg = _fake_roi_head()\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        # When truth is non-empty then emd loss should be nonzero for\n        # random inputs\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=False,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        loss = out['loss_rcnn_emd']\n        self.assertGreater(loss.sum(), 0, 'loss should be non-zero')\n\n        # When there is no truth, the emd loss should be zero.\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        empty_loss = out['loss_rcnn_emd']\n        self.assertEqual(\n            empty_loss.sum(), 0,\n            'there should be no emd loss when there are no true boxes')\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_pisa_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestPISARoIHead(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n        self.roi_head_cfg = get_roi_head_cfg(\n            'pisa/faster-rcnn_r50_fpn_pisa_1x_coco.py')\n\n    def test_init(self):\n        roi_head = MODELS.build(self.roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n\n    @parameterized.expand(['cpu', 'cuda'])\n    def test_pisa_roi_head(self, device):\n        \"\"\"Tests trident roi head predict.\"\"\"\n        if not torch.cuda.is_available() and device == 'cuda':\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        roi_head = MODELS.build(self.roi_head_cfg)\n        roi_head = roi_head.to(device=device)\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device=device))\n\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        loss_cls = out['loss_cls']\n        loss_bbox = out['loss_bbox']\n        self.assertGreater(loss_cls.sum(), 0, 'cls loss should be non-zero')\n        self.assertGreater(loss_bbox.sum(), 0, 'box loss should be non-zero')\n\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device=device)['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device=device)\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        empty_cls_loss = out['loss_cls']\n        empty_bbox_loss = out['loss_bbox']\n        self.assertGreater(empty_cls_loss.sum(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_bbox_loss.sum(), 0,\n            'there should be no box loss when there are no true boxes')\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_point_rend_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads import PointRendRoIHead  # noqa\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\n\n\nclass TestHTCRoIHead(TestCase):\n\n    @parameterized.expand(\n        ['point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py'])\n    def test_init(self, cfg_file):\n        \"\"\"Test init Point rend RoI head.\"\"\"\n        # Normal HTC RoI head\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        assert roi_head.with_bbox\n        assert roi_head.with_mask\n\n    @parameterized.expand(\n        ['point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py'])\n    def test_point_rend_roi_head_loss(self, cfg_file):\n        \"\"\"Tests htc roi head loss when truth is empty and non-empty.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        # When truth is non-empty then both cls, box, and mask loss\n        # should be nonzero for random inputs\n        img_shape_list = [img_meta['img_shape'] for img_meta in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n\n        # Positive rois must not be empty\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        with self.assertRaises(AssertionError):\n            out = roi_head.loss(feats, proposal_list, batch_data_samples)\n\n    @parameterized.expand(\n        ['point_rend/point-rend_r50-caffe_fpn_ms-1x_coco.py'])\n    def test_point_rend_roi_head_predict(self, cfg_file):\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        img_shape_list = [img_meta['img_shape'] for img_meta in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        results = roi_head.predict(\n            feats, proposal_list, batch_data_samples, rescale=True)\n        self.assertEqual(results[0].masks.shape[-2:], (s, s))\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_roi_extractors/test_generic_roi_extractor.py",
    "content": "import unittest\n\nimport torch\n\nfrom mmdet.models.roi_heads.roi_extractors import GenericRoIExtractor\n\n\nclass TestGenericRoIExtractor(unittest.TestCase):\n\n    def test_init(self):\n        with self.assertRaises(AssertionError):\n            GenericRoIExtractor(\n                aggregation='other',\n                roi_layer=dict(\n                    type='RoIAlign', output_size=7, sampling_ratio=2),\n                out_channels=16,\n                featmap_strides=[4, 8, 16, 32])\n\n        roi_extractor = GenericRoIExtractor(\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=16,\n            featmap_strides=[4, 8, 16, 32])\n        self.assertFalse(roi_extractor.with_pre)\n        self.assertFalse(roi_extractor.with_post)\n\n    def test_forward(self):\n        # test with pre/post\n        cfg = dict(\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=16,\n            featmap_strides=[4, 8, 16, 32],\n            pre_cfg=dict(\n                type='ConvModule',\n                in_channels=16,\n                out_channels=16,\n                kernel_size=5,\n                padding=2,\n                inplace=False),\n            post_cfg=dict(\n                type='ConvModule',\n                in_channels=16,\n                out_channels=16,\n                kernel_size=5,\n                padding=2,\n                inplace=False))\n        roi_extractor = GenericRoIExtractor(**cfg)\n\n        # empty rois\n        feats = (\n            torch.rand((1, 16, 200, 336)),\n            torch.rand((1, 16, 100, 168)),\n        )\n        rois = torch.empty((0, 5), dtype=torch.float32)\n        res = roi_extractor(feats, rois)\n        self.assertEqual(len(res), 0)\n\n        # single scale feature\n        rois = torch.tensor([[0.0000, 587.8285, 52.1405, 886.2484, 341.5644]])\n        feats = (torch.rand((1, 16, 200, 336)), )\n        res = roi_extractor(feats, rois)\n        self.assertEqual(res.shape, (1, 16, 7, 7))\n\n        # multi-scale features\n        feats = (\n            torch.rand((1, 16, 200, 336)),\n            torch.rand((1, 16, 100, 168)),\n            torch.rand((1, 16, 50, 84)),\n            torch.rand((1, 16, 25, 42)),\n        )\n        res = roi_extractor(feats, rois)\n        self.assertEqual(res.shape, (1, 16, 7, 7))\n\n        # test w.o. pre/post concat\n        cfg = dict(\n            aggregation='concat',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=16 * 4,\n            featmap_strides=[4, 8, 16, 32])\n\n        roi_extractor = GenericRoIExtractor(**cfg)\n        res = roi_extractor(feats, rois)\n        self.assertEqual(res.shape, (1, 64, 7, 7))\n\n        # test concat channels number\n        cfg = dict(\n            aggregation='concat',\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=256 * 5,  # 256*5 != 256*4\n            featmap_strides=[4, 8, 16, 32])\n\n        roi_extractor = GenericRoIExtractor(**cfg)\n\n        feats = (\n            torch.rand((1, 256, 200, 336)),\n            torch.rand((1, 256, 100, 168)),\n            torch.rand((1, 256, 50, 84)),\n            torch.rand((1, 256, 25, 42)),\n        )\n        # out_channels does not sum of feat channels\n        with self.assertRaises(AssertionError):\n            roi_extractor(feats, rois)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_roi_extractors/test_single_level_roi_extractor.py",
    "content": "import unittest\n\nimport torch\n\nfrom mmdet.models.roi_heads.roi_extractors import SingleRoIExtractor\n\n\nclass TestSingleRoIExtractor(unittest.TestCase):\n\n    def test_forward(self):\n        cfg = dict(\n            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2),\n            out_channels=16,\n            featmap_strides=[4, 8, 16, 32])\n        roi_extractor = SingleRoIExtractor(**cfg)\n\n        # empty rois\n        feats = (torch.rand((1, 16, 200, 336)), )\n        rois = torch.empty((0, 5), dtype=torch.float32)\n        res = roi_extractor(feats, rois)\n        self.assertEqual(len(res), 0)\n\n        # single scale feature\n        rois = torch.tensor([[0.0000, 587.8285, 52.1405, 886.2484, 341.5644]])\n        res = roi_extractor(feats, rois)\n        self.assertEqual(res.shape, (1, 16, 7, 7))\n\n        # multi-scale features\n        feats = (\n            torch.rand((1, 16, 200, 336)),\n            torch.rand((1, 16, 100, 168)),\n            torch.rand((1, 16, 50, 84)),\n            torch.rand((1, 16, 25, 42)),\n        )\n        res = roi_extractor(feats, rois)\n        self.assertEqual(res.shape, (1, 16, 7, 7))\n\n        res = roi_extractor(feats, rois, roi_scale_factor=2.0)\n        self.assertEqual(res.shape, (1, 16, 7, 7))\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_scnet_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads import SCNetRoIHead  # noqa\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\n\n\nclass TestSCNetRoIHead(TestCase):\n\n    @parameterized.expand(['scnet/scnet_r50_fpn_1x_coco.py'])\n    def test_init(self, cfg_file):\n        \"\"\"Test init scnet RoI head.\"\"\"\n        # Normal Cascade Mask R-CNN RoI head\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        assert roi_head.with_bbox\n        assert roi_head.with_mask\n        assert roi_head.with_semantic\n        assert roi_head.with_feat_relay\n        assert roi_head.with_glbctx\n\n    @parameterized.expand(['scnet/scnet_r50_fpn_1x_coco.py'])\n    def test_scnet_roi_head_loss(self, cfg_file):\n        \"\"\"Tests htc roi head loss when truth is empty and non-empty.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        # When truth is non-empty then both cls, box, and mask loss\n        # should be nonzero for random inputs\n        img_shape_list = [(3, s, s) for _ in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            with_semantic=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box and mask loss.\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            with_semantic=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss_cls' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n            elif 'loss_bbox' in name or 'loss_mask' in name:\n                self.assertEqual(value.sum(), 0)\n\n    @parameterized.expand(['scnet/scnet_r50_fpn_1x_coco.py'])\n    def test_scnet_roi_head_predict(self, cfg_file):\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        img_shape_list = [(3, s, s) for _ in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        results = roi_head.predict(\n            feats, proposal_list, batch_data_samples, rescale=True)\n        self.assertEqual(results[0].masks.shape[-2:], (s, s))\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_sparse_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nimport torch.nn as nn\nfrom parameterized import parameterized\n\nfrom mmdet.models.roi_heads import StandardRoIHead  # noqa\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\n\n\nclass TestCascadeRoIHead(TestCase):\n\n    @parameterized.expand(['queryinst/queryinst_r50_fpn_1x_coco.py'])\n    def test_init(self, cfg_file):\n        \"\"\"Test init standard RoI head.\"\"\"\n        # Normal Cascade Mask R-CNN RoI head\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head.init_weights()\n        assert roi_head.with_bbox\n        assert roi_head.with_mask\n\n    @parameterized.expand(['queryinst/queryinst_r50_fpn_1x_coco.py'])\n    def test_cascade_roi_head_loss(self, cfg_file):\n        \"\"\"Tests standard roi head loss when truth is empty and non-empty.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        img_metas = [{\n            'img_shape': (s, s, 3),\n            'scale_factor': 1,\n        }]\n        roi_head_cfg = get_roi_head_cfg(cfg_file)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head_cfg.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 1, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        # When truth is non-empty then both cls, box, and mask loss\n        # should be nonzero for random inputs\n        img_shape_list = [(3, s, s) for _ in img_metas]\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        # add import elements into proposal\n        init_proposal_features = nn.Embedding(100, 256).cuda().weight.clone()\n        for proposal in proposal_list:\n            proposal.features = init_proposal_features\n            proposal.imgs_whwh = feats[0].new_tensor([[s, s, s,\n                                                       s]]).repeat(100, 1)\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box and mask loss.\n        proposal_list = demo_mm_proposals(img_shape_list, 100, device='cuda')\n        # add import elements into proposal\n        init_proposal_features = nn.Embedding(100, 256).cuda().weight.clone()\n        for proposal in proposal_list:\n            proposal.features = init_proposal_features\n            proposal.imgs_whwh = feats[0].new_tensor([[s, s, s,\n                                                       s]]).repeat(100, 1)\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=[(3, s, s)],\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        out = roi_head.loss(feats, proposal_list, batch_data_samples)\n        for name, value in out.items():\n            if 'loss_cls' in name:\n                self.assertGreaterEqual(\n                    value.sum(), 0, msg='loss should be non-zero')\n            elif 'loss_bbox' in name or 'loss_mask' in name:\n                self.assertEqual(value.sum(), 0)\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_standard_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import Config\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n\ndef _fake_roi_head(with_shared_head=False):\n    \"\"\"Set a fake roi head config.\"\"\"\n    if not with_shared_head:\n        roi_head = Config(\n            dict(\n                type='StandardRoIHead',\n                bbox_roi_extractor=dict(\n                    type='SingleRoIExtractor',\n                    roi_layer=dict(\n                        type='RoIAlign', output_size=7, sampling_ratio=0),\n                    out_channels=1,\n                    featmap_strides=[4, 8, 16, 32]),\n                bbox_head=dict(\n                    type='Shared2FCBBoxHead',\n                    in_channels=1,\n                    fc_out_channels=1,\n                    num_classes=4),\n                mask_roi_extractor=dict(\n                    type='SingleRoIExtractor',\n                    roi_layer=dict(\n                        type='RoIAlign', output_size=14, sampling_ratio=0),\n                    out_channels=1,\n                    featmap_strides=[4, 8, 16, 32]),\n                mask_head=dict(\n                    type='FCNMaskHead',\n                    num_convs=1,\n                    in_channels=1,\n                    conv_out_channels=1,\n                    num_classes=4),\n                train_cfg=dict(\n                    assigner=dict(\n                        type='MaxIoUAssigner',\n                        pos_iou_thr=0.5,\n                        neg_iou_thr=0.5,\n                        min_pos_iou=0.5,\n                        match_low_quality=True,\n                        ignore_iof_thr=-1),\n                    sampler=dict(\n                        type='RandomSampler',\n                        num=512,\n                        pos_fraction=0.25,\n                        neg_pos_ub=-1,\n                        add_gt_as_proposals=True),\n                    mask_size=28,\n                    pos_weight=-1,\n                    debug=False),\n                test_cfg=dict(\n                    score_thr=0.05,\n                    nms=dict(type='nms', iou_threshold=0.5),\n                    max_per_img=100,\n                    mask_thr_binary=0.5)))\n    else:\n        roi_head = Config(\n            dict(\n                type='StandardRoIHead',\n                shared_head=dict(\n                    type='ResLayer',\n                    depth=50,\n                    stage=3,\n                    stride=2,\n                    dilation=1,\n                    style='caffe',\n                    norm_cfg=dict(type='BN', requires_grad=False),\n                    norm_eval=True),\n                bbox_roi_extractor=dict(\n                    type='SingleRoIExtractor',\n                    roi_layer=dict(\n                        type='RoIAlign', output_size=14, sampling_ratio=0),\n                    out_channels=1,\n                    featmap_strides=[16]),\n                bbox_head=dict(\n                    type='BBoxHead',\n                    with_avg_pool=True,\n                    in_channels=2048,\n                    roi_feat_size=7,\n                    num_classes=4),\n                mask_roi_extractor=None,\n                mask_head=dict(\n                    type='FCNMaskHead',\n                    num_convs=0,\n                    in_channels=2048,\n                    conv_out_channels=1,\n                    num_classes=4),\n                train_cfg=dict(\n                    assigner=dict(\n                        type='MaxIoUAssigner',\n                        pos_iou_thr=0.5,\n                        neg_iou_thr=0.5,\n                        min_pos_iou=0.5,\n                        match_low_quality=False,\n                        ignore_iof_thr=-1),\n                    sampler=dict(\n                        type='RandomSampler',\n                        num=512,\n                        pos_fraction=0.25,\n                        neg_pos_ub=-1,\n                        add_gt_as_proposals=True),\n                    mask_size=14,\n                    pos_weight=-1,\n                    debug=False),\n                test_cfg=dict(\n                    score_thr=0.05,\n                    nms=dict(type='nms', iou_threshold=0.5),\n                    max_per_img=100,\n                    mask_thr_binary=0.5)))\n    return roi_head\n\n\nclass TestStandardRoIHead(TestCase):\n\n    def test_init(self):\n        \"\"\"Test init standard RoI head.\"\"\"\n        # Normal Mask R-CNN RoI head\n        roi_head_cfg = _fake_roi_head()\n        roi_head = MODELS.build(roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n        self.assertTrue(roi_head.with_mask)\n\n        # Mask R-CNN RoI head with shared_head\n        roi_head_cfg = _fake_roi_head(with_shared_head=True)\n        roi_head = MODELS.build(roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n        self.assertTrue(roi_head.with_mask)\n        self.assertTrue(roi_head.with_shared_head)\n\n    @parameterized.expand([(False, ), (True, )])\n    def test_standard_roi_head_loss(self, with_shared_head):\n        \"\"\"Tests standard roi head loss when truth is empty and non-empty.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n        s = 256\n        roi_head_cfg = _fake_roi_head(with_shared_head=with_shared_head)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            if not with_shared_head:\n                feats.append(\n                    torch.rand(1, 1, s // (2**(i + 2)),\n                               s // (2**(i + 2))).to(device='cuda'))\n            else:\n                feats.append(\n                    torch.rand(1, 1024, s // (2**(i + 2)),\n                               s // (2**(i + 2))).to(device='cuda'))\n        feats = tuple(feats)\n\n        # When truth is non-empty then both cls, box, and mask loss\n        # should be nonzero for random inputs\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[1],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        loss_cls = out['loss_cls']\n        loss_bbox = out['loss_bbox']\n        loss_mask = out['loss_mask']\n        self.assertGreater(loss_cls.sum(), 0, 'cls loss should be non-zero')\n        self.assertGreater(loss_bbox.sum(), 0, 'box loss should be non-zero')\n        self.assertGreater(loss_mask.sum(), 0, 'mask loss should be non-zero')\n\n        # When there is no truth, the cls loss should be nonzero but\n        # there should be no box and mask loss.\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n        out = roi_head.loss(feats, proposals_list, batch_data_samples)\n        empty_cls_loss = out['loss_cls']\n        empty_bbox_loss = out['loss_bbox']\n        empty_mask_loss = out['loss_mask']\n        self.assertGreater(empty_cls_loss.sum(), 0,\n                           'cls loss should be non-zero')\n        self.assertEqual(\n            empty_bbox_loss.sum(), 0,\n            'there should be no box loss when there are no true boxes')\n        self.assertEqual(\n            empty_mask_loss.sum(), 0,\n            'there should be no mask loss when there are no true boxes')\n"
  },
  {
    "path": "tests/test_models/test_roi_heads/test_trident_roi_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport unittest\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_mm_inputs, demo_mm_proposals, get_roi_head_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestTridentRoIHead(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n        self.roi_head_cfg = get_roi_head_cfg(\n            'tridentnet/tridentnet_r50-caffe_1x_coco.py')\n\n    def test_init(self):\n\n        roi_head = MODELS.build(self.roi_head_cfg)\n        self.assertTrue(roi_head.with_bbox)\n        self.assertTrue(roi_head.with_shared_head)\n\n    def test_trident_roi_head_predict(self):\n        \"\"\"Tests trident roi head predict.\"\"\"\n        if not torch.cuda.is_available():\n            # RoI pooling only support in GPU\n            return unittest.skip('test requires GPU and torch+cuda')\n\n        roi_head_cfg = copy.deepcopy(self.roi_head_cfg)\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        s = 256\n        feats = []\n        for i in range(len(roi_head.bbox_roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 1024, s // (2**(i + 2)),\n                           s // (2**(i + 2))).to(device='cuda'))\n\n        image_shapes = [(3, s, s)]\n        batch_data_samples = demo_mm_inputs(\n            batch_size=1,\n            image_shapes=image_shapes,\n            num_items=[0],\n            num_classes=4,\n            with_mask=True,\n            device='cuda')['data_samples']\n        proposals_list = demo_mm_proposals(\n            image_shapes=image_shapes, num_proposals=100, device='cuda')\n        # When `test_branch_idx == 1`\n        roi_head.predict(feats, proposals_list, batch_data_samples)\n        # When `test_branch_idx == -1`\n        roi_head_cfg.test_branch_idx = -1\n        roi_head = MODELS.build(roi_head_cfg)\n        roi_head = roi_head.cuda()\n        roi_head.predict(feats, proposals_list, batch_data_samples)\n"
  },
  {
    "path": "tests/test_models/test_seg_heads/test_heuristic_fusion_head.py",
    "content": "import unittest\n\nimport torch\nfrom mmengine.config import Config\nfrom mmengine.structures import InstanceData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.evaluation import INSTANCE_OFFSET\nfrom mmdet.models.seg_heads.panoptic_fusion_heads import HeuristicFusionHead\n\n\nclass TestHeuristicFusionHead(unittest.TestCase):\n\n    def test_loss(self):\n        head = HeuristicFusionHead(num_things_classes=2, num_stuff_classes=2)\n        result = head.loss()\n        self.assertTrue(not head.with_loss)\n        self.assertDictEqual(result, dict())\n\n    def test_predict(self):\n        test_cfg = Config(dict(mask_overlap=0.5, stuff_area_limit=1))\n        head = HeuristicFusionHead(\n            num_things_classes=2, num_stuff_classes=2, test_cfg=test_cfg)\n        mask_results = InstanceData()\n        mask_results.bboxes = torch.tensor([[0, 0, 1, 1], [1, 1, 2, 2]])\n        mask_results.labels = torch.tensor([0, 1])\n        mask_results.scores = torch.tensor([0.8, 0.7])\n        mask_results.masks = torch.tensor([[[1, 0], [0, 0]], [[0, 0],\n                                                              [0, 1]]]).bool()\n\n        seg_preds_list = [\n            torch.tensor([[[0.2, 0.7], [0.3, 0.1]], [[0.2, 0.2], [0.6, 0.1]],\n                          [[0.6, 0.1], [0.1, 0.8]]])\n        ]\n        target_list = [\n            torch.tensor([[0 + 1 * INSTANCE_OFFSET, 2],\n                          [3, 1 + 2 * INSTANCE_OFFSET]])\n        ]\n        results_list = head.predict([mask_results], seg_preds_list)\n        for target, result in zip(target_list, results_list):\n            assert_allclose(result.sem_seg[0], target)\n\n        # test with no thing\n        head = HeuristicFusionHead(\n            num_things_classes=2, num_stuff_classes=2, test_cfg=test_cfg)\n        mask_results = InstanceData()\n        mask_results.bboxes = torch.zeros((0, 4))\n        mask_results.labels = torch.zeros((0, )).long()\n        mask_results.scores = torch.zeros((0, ))\n        mask_results.masks = torch.zeros((0, 2, 2), dtype=torch.bool)\n        seg_preds_list = [\n            torch.tensor([[[0.2, 0.7], [0.3, 0.1]], [[0.2, 0.2], [0.6, 0.1]],\n                          [[0.6, 0.1], [0.1, 0.8]]])\n        ]\n        target_list = [torch.tensor([[4, 2], [3, 4]])]\n\n        results_list = head.predict([mask_results], seg_preds_list)\n        for target, result in zip(target_list, results_list):\n            assert_allclose(result.sem_seg[0], target)\n"
  },
  {
    "path": "tests/test_models/test_seg_heads/test_maskformer_fusion_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport torch\nfrom mmengine.config import Config\n\nfrom mmdet.models.seg_heads.panoptic_fusion_heads import MaskFormerFusionHead\nfrom mmdet.structures import DetDataSample\n\n\nclass TestMaskFormerFusionHead(unittest.TestCase):\n\n    def test_loss(self):\n        head = MaskFormerFusionHead(num_things_classes=2, num_stuff_classes=2)\n        result = head.loss()\n        self.assertTrue(not head.with_loss)\n        self.assertDictEqual(result, dict())\n\n    def test_predict(self):\n        mask_cls_results = torch.rand((2, 10, 5))\n        mask_pred_results = torch.rand((2, 10, 32, 32))\n        batch_data_samples = [\n            DetDataSample(\n                metainfo={\n                    'batch_input_shape': (32, 32),\n                    'img_shape': (32, 30),\n                    'ori_shape': (30, 30)\n                }),\n            DetDataSample(\n                metainfo={\n                    'batch_input_shape': (32, 32),\n                    'img_shape': (32, 30),\n                    'ori_shape': (29, 30)\n                })\n        ]\n\n        # get panoptic and instance segmentation results\n        test_cfg = Config(\n            dict(\n                panoptic_on=True,\n                semantic_on=False,\n                instance_on=True,\n                max_per_image=10,\n                object_mask_thr=0.3,\n                iou_thr=0.3,\n                filter_low_score=False))\n        head = MaskFormerFusionHead(\n            num_things_classes=2, num_stuff_classes=2, test_cfg=test_cfg)\n        results = head.predict(\n            mask_cls_results,\n            mask_pred_results,\n            batch_data_samples,\n            rescale=False)\n        for i in range(len(results)):\n            self.assertEqual(results[i]['pan_results'].sem_seg.shape[-2:],\n                             batch_data_samples[i].img_shape)\n            self.assertEqual(results[i]['ins_results'].masks.shape[-2:],\n                             batch_data_samples[i].img_shape)\n\n        results = head.predict(\n            mask_cls_results,\n            mask_pred_results,\n            batch_data_samples,\n            rescale=True)\n        for i in range(len(results)):\n            self.assertEqual(results[i]['pan_results'].sem_seg.shape[-2:],\n                             batch_data_samples[i].ori_shape)\n            self.assertEqual(results[i]['ins_results'].masks.shape[-2:],\n                             batch_data_samples[i].ori_shape)\n\n        # get empty results\n        test_cfg = Config(\n            dict(\n                panoptic_on=False,\n                semantic_on=False,\n                instance_on=False,\n                max_per_image=10,\n                object_mask_thr=0.3,\n                iou_thr=0.3,\n                filter_low_score=False))\n        head = MaskFormerFusionHead(\n            num_things_classes=2, num_stuff_classes=2, test_cfg=test_cfg)\n        results = head.predict(\n            mask_cls_results,\n            mask_pred_results,\n            batch_data_samples,\n            rescale=True)\n        for i in range(len(results)):\n            self.assertEqual(results[i], dict())\n\n        # semantic segmentation is not supported\n        test_cfg = Config(\n            dict(\n                panoptic_on=False,\n                semantic_on=True,\n                instance_on=False,\n                max_per_image=10,\n                object_mask_thr=0.3,\n                iou_thr=0.3,\n                filter_low_score=False))\n        head = MaskFormerFusionHead(\n            num_things_classes=2, num_stuff_classes=2, test_cfg=test_cfg)\n        with self.assertRaises(AssertionError):\n            results = head.predict(\n                mask_cls_results,\n                mask_pred_results,\n                batch_data_samples,\n                rescale=True)\n"
  },
  {
    "path": "tests/test_models/test_seg_heads/test_panoptic_fpn_head.py",
    "content": "import unittest\n\nimport torch\nfrom mmengine.structures import PixelData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.seg_heads import PanopticFPNHead\nfrom mmdet.structures import DetDataSample\n\n\nclass TestPanopticFPNHead(unittest.TestCase):\n\n    def test_init_weights(self):\n        head = PanopticFPNHead(\n            num_things_classes=2,\n            num_stuff_classes=2,\n            in_channels=32,\n            inner_channels=32)\n        head.init_weights()\n        assert_allclose(head.conv_logits.bias.data,\n                        torch.zeros_like(head.conv_logits.bias.data))\n\n    def test_loss(self):\n        head = PanopticFPNHead(\n            num_things_classes=2,\n            num_stuff_classes=2,\n            in_channels=32,\n            inner_channels=32,\n            start_level=0,\n            end_level=1)\n        x = [torch.rand((2, 32, 8, 8)), torch.rand((2, 32, 4, 4))]\n        data_sample1 = DetDataSample()\n        data_sample1.gt_sem_seg = PixelData(\n            sem_seg=torch.randint(0, 4, (1, 7, 8)))\n        data_sample2 = DetDataSample()\n        data_sample2.gt_sem_seg = PixelData(\n            sem_seg=torch.randint(0, 4, (1, 7, 8)))\n        batch_data_samples = [data_sample1, data_sample2]\n        results = head.loss(x, batch_data_samples)\n        self.assertIsInstance(results, dict)\n\n    def test_predict(self):\n        head = PanopticFPNHead(\n            num_things_classes=2,\n            num_stuff_classes=2,\n            in_channels=32,\n            inner_channels=32,\n            start_level=0,\n            end_level=1)\n        x = [torch.rand((2, 32, 8, 8)), torch.rand((2, 32, 4, 4))]\n        img_meta1 = {\n            'batch_input_shape': (16, 16),\n            'img_shape': (14, 14),\n            'ori_shape': (12, 12),\n        }\n        img_meta2 = {\n            'batch_input_shape': (16, 16),\n            'img_shape': (16, 16),\n            'ori_shape': (16, 16),\n        }\n        batch_img_metas = [img_meta1, img_meta2]\n        head.eval()\n        with torch.no_grad():\n            seg_preds = head.predict(x, batch_img_metas, rescale=False)\n            self.assertTupleEqual(seg_preds[0].shape[-2:], (16, 16))\n            self.assertTupleEqual(seg_preds[1].shape[-2:], (16, 16))\n\n            seg_preds = head.predict(x, batch_img_metas, rescale=True)\n            self.assertTupleEqual(seg_preds[0].shape[-2:], (12, 12))\n            self.assertTupleEqual(seg_preds[1].shape[-2:], (16, 16))\n"
  },
  {
    "path": "tests/test_models/test_task_modules/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_approx_max_iou_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners import ApproxMaxIoUAssigner\n\n\nclass TestApproxIoUAssigner(TestCase):\n\n    def test_approx_iou_assigner(self):\n        assigner = ApproxMaxIoUAssigner(\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n        )\n        bboxes = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData()\n        pred_instances.priors = bboxes\n        pred_instances.approxs = bboxes[:, None, :]\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n\n        expected_gt_inds = torch.LongTensor([1, 0, 2, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_approx_iou_assigner_with_empty_gt(self):\n        \"\"\"Test corner case where an image might have no true detections.\"\"\"\n        assigner = ApproxMaxIoUAssigner(\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n        )\n        bboxes = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([])\n        gt_labels = torch.LongTensor([])\n\n        pred_instances = InstanceData()\n        pred_instances.priors = bboxes\n        pred_instances.approxs = bboxes[:, None, :]\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n\n        expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_approx_iou_assigner_with_empty_boxes(self):\n        \"\"\"Test corner case where an network might predict no boxes.\"\"\"\n        assigner = ApproxMaxIoUAssigner(\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n        )\n        bboxes = torch.empty((0, 4))\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData()\n        pred_instances.priors = bboxes\n        pred_instances.approxs = bboxes[:, None, :]\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n\n        self.assertEqual(len(assign_result.gt_inds), 0)\n\n    def test_approx_iou_assigner_with_empty_boxes_and_gt(self):\n        \"\"\"Test corner case where an network might predict no boxes and no\n        gt.\"\"\"\n        assigner = ApproxMaxIoUAssigner(\n            pos_iou_thr=0.5,\n            neg_iou_thr=0.5,\n        )\n        bboxes = torch.empty((0, 4))\n        gt_bboxes = torch.empty((0, 4))\n        gt_labels = torch.LongTensor([])\n\n        pred_instances = InstanceData()\n        pred_instances.priors = bboxes\n        pred_instances.approxs = bboxes[:, None, :]\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n\n        self.assertEqual(len(assign_result.gt_inds), 0)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_atss_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners import ATSSAssigner\n\n\nclass TestATSSAssigner(TestCase):\n\n    def test_atss_assigner(self):\n        atss_assigner = ATSSAssigner(topk=9)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        num_level_bboxes = [4]\n\n        assign_result = atss_assigner.assign(pred_instances, num_level_bboxes,\n                                             gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 4)\n        self.assertEqual(len(assign_result.labels), 4)\n\n        expected_gt_inds = torch.LongTensor([1, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_atss_assigner_with_ignore(self):\n        atss_assigner = ATSSAssigner(topk=9)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [30, 32, 40, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n        gt_bboxes_ignore = torch.Tensor([\n            [30, 30, 40, 40],\n        ])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        gt_instances_ignore = InstanceData(bboxes=gt_bboxes_ignore)\n        num_level_bboxes = [4]\n        assign_result = atss_assigner.assign(\n            pred_instances,\n            num_level_bboxes,\n            gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n\n        expected_gt_inds = torch.LongTensor([1, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_atss_assigner_with_empty_gt(self):\n        \"\"\"Test corner case where an image might have no true detections.\"\"\"\n        atss_assigner = ATSSAssigner(topk=9)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.empty(0, 4)\n        gt_labels = torch.empty(0)\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        num_level_bboxes = [4]\n        assign_result = atss_assigner.assign(pred_instances, num_level_bboxes,\n                                             gt_instances)\n\n        expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_atss_assigner_with_empty_boxes(self):\n        \"\"\"Test corner case where a network might predict no boxes.\"\"\"\n        atss_assigner = ATSSAssigner(topk=9)\n        priors = torch.empty((0, 4))\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        num_level_bboxes = [0]\n        assign_result = atss_assigner.assign(pred_instances, num_level_bboxes,\n                                             gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n        self.assertTrue(tuple(assign_result.labels.shape) == (0, ))\n\n    def test_atss_assigner_with_empty_boxes_and_ignore(self):\n        \"\"\"Test corner case where a network might predict no boxes and\n        ignore_iof_thr is on.\"\"\"\n        atss_assigner = ATSSAssigner(topk=9)\n        priors = torch.empty((0, 4))\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_bboxes_ignore = torch.Tensor([\n            [30, 30, 40, 40],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        gt_instances_ignore = InstanceData(bboxes=gt_bboxes_ignore)\n        num_level_bboxes = [0]\n\n        assign_result = atss_assigner.assign(\n            pred_instances,\n            num_level_bboxes,\n            gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n        self.assertTrue(tuple(assign_result.labels.shape) == (0, ))\n\n    def test_atss_assigner_with_empty_boxes_and_gt(self):\n        \"\"\"Test corner case where a network might predict no boxes and no\n        gt.\"\"\"\n        atss_assigner = ATSSAssigner(topk=9)\n        priors = torch.empty((0, 4))\n        gt_bboxes = torch.empty((0, 4))\n        gt_labels = torch.empty(0)\n        num_level_bboxes = [0]\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        assign_result = atss_assigner.assign(pred_instances, num_level_bboxes,\n                                             gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_center_region_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners import CenterRegionAssigner\n\n\nclass TestCenterRegionAssigner(TestCase):\n\n    def test_center_region_assigner(self):\n        center_region_assigner = CenterRegionAssigner(\n            pos_scale=0.2, neg_scale=0.2, min_pos_iof=0.01)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n\n        assign_result = center_region_assigner.assign(pred_instances,\n                                                      gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 4)\n        self.assertEqual(len(assign_result.labels), 4)\n\n        expected_gt_inds = torch.LongTensor([1, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n        expected_shadowed_labels = torch.LongTensor([[2, 3]])\n        shadowed_labels = assign_result.get_extra_property('shadowed_labels')\n        self.assertTrue(torch.all(shadowed_labels == expected_shadowed_labels))\n\n    def test_center_region_assigner_with_ignore(self):\n        center_region_assigner = CenterRegionAssigner(\n            pos_scale=0.2, neg_scale=0.2, min_pos_iof=0.01)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [30, 32, 40, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n        gt_bboxes_ignore = torch.Tensor([\n            [30, 30, 40, 40],\n        ])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        gt_instances_ignore = InstanceData(bboxes=gt_bboxes_ignore)\n        assign_result = center_region_assigner.assign(\n            pred_instances,\n            gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n\n        expected_gt_inds = torch.LongTensor([1, 0, 0, -1])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_center_region_assigner_with_empty_gt(self):\n        \"\"\"Test corner case where an image might have no true detections.\"\"\"\n        center_region_assigner = CenterRegionAssigner(\n            pos_scale=0.2, neg_scale=0.2, min_pos_iof=0.01)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.empty(0, 4)\n        gt_labels = torch.empty(0)\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        assign_result = center_region_assigner.assign(pred_instances,\n                                                      gt_instances)\n\n        expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_center_region_assigner_with_empty_boxes(self):\n        \"\"\"Test corner case where a network might predict no boxes.\"\"\"\n        center_region_assigner = CenterRegionAssigner(\n            pos_scale=0.2, neg_scale=0.2, min_pos_iof=0.01)\n        priors = torch.empty((0, 4))\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        assign_result = center_region_assigner.assign(pred_instances,\n                                                      gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n        self.assertTrue(tuple(assign_result.labels.shape) == (0, ))\n\n    def test_center_region_assigner_with_empty_boxes_and_ignore(self):\n        \"\"\"Test corner case where a network might predict no boxes and\n        ignore_iof_thr is on.\"\"\"\n        center_region_assigner = CenterRegionAssigner(\n            pos_scale=0.2, neg_scale=0.2, min_pos_iof=0.01)\n        priors = torch.empty((0, 4))\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_bboxes_ignore = torch.Tensor([\n            [30, 30, 40, 40],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        gt_instances_ignore = InstanceData(bboxes=gt_bboxes_ignore)\n\n        assign_result = center_region_assigner.assign(\n            pred_instances,\n            gt_instances,\n            gt_instances_ignore=gt_instances_ignore)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n        self.assertTrue(tuple(assign_result.labels.shape) == (0, ))\n\n    def test_center_region_assigner_with_empty_boxes_and_gt(self):\n        \"\"\"Test corner case where a network might predict no boxes and no\n        gt.\"\"\"\n        center_region_assigner = CenterRegionAssigner(\n            pos_scale=0.2, neg_scale=0.2, min_pos_iof=0.01)\n        priors = torch.empty((0, 4))\n        gt_bboxes = torch.empty((0, 4))\n        gt_labels = torch.empty(0)\n\n        pred_instances = InstanceData(priors=priors)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        assign_result = center_region_assigner.assign(pred_instances,\n                                                      gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_dynamic_soft_label_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.task_modules.assigners import DynamicSoftLabelAssigner\nfrom mmdet.structures.bbox import HorizontalBoxes\n\n\nclass TestDynamicSoftLabelAssigner(TestCase):\n\n    def test_assign(self):\n        assigner = DynamicSoftLabelAssigner(\n            soft_center_radius=3.0, topk=1, iou_weight=3.0)\n        pred_instances = InstanceData(\n            bboxes=torch.Tensor([[23, 23, 43, 43], [4, 5, 6, 7]]),\n            scores=torch.FloatTensor([[0.2], [0.8]]),\n            priors=torch.Tensor([[30, 30, 8, 8], [4, 5, 6, 7]]))\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[23, 23, 43, 43]]),\n            labels=torch.LongTensor([0]))\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n\n        expected_gt_inds = torch.LongTensor([1, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_assign_with_no_valid_bboxes(self):\n        assigner = DynamicSoftLabelAssigner(\n            soft_center_radius=3.0, topk=1, iou_weight=3.0)\n        pred_instances = InstanceData(\n            bboxes=torch.Tensor([[123, 123, 143, 143], [114, 151, 161, 171]]),\n            scores=torch.FloatTensor([[0.2], [0.8]]),\n            priors=torch.Tensor([[30, 30, 8, 8], [55, 55, 8, 8]]))\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[0, 0, 1, 1]]), labels=torch.LongTensor([0]))\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n\n        expected_gt_inds = torch.LongTensor([0, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_assign_with_empty_gt(self):\n        assigner = DynamicSoftLabelAssigner(\n            soft_center_radius=3.0, topk=1, iou_weight=3.0)\n        pred_instances = InstanceData(\n            bboxes=torch.Tensor([[[30, 40, 50, 60]], [[4, 5, 6, 7]]]),\n            scores=torch.FloatTensor([[0.2], [0.8]]),\n            priors=torch.Tensor([[0, 12, 23, 34], [4, 5, 6, 7]]))\n        gt_instances = InstanceData(\n            bboxes=torch.empty(0, 4), labels=torch.empty(0))\n\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([0, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_box_type_input(self):\n        assigner = DynamicSoftLabelAssigner(\n            soft_center_radius=3.0, topk=1, iou_weight=3.0)\n        pred_instances = InstanceData(\n            bboxes=torch.Tensor([[23, 23, 43, 43], [4, 5, 6, 7]]),\n            scores=torch.FloatTensor([[0.2], [0.8]]),\n            priors=torch.Tensor([[30, 30, 8, 8], [4, 5, 6, 7]]))\n        gt_instances = InstanceData(\n            bboxes=HorizontalBoxes(torch.Tensor([[23, 23, 43, 43]])),\n            labels=torch.LongTensor([0]))\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n\n        expected_gt_inds = torch.LongTensor([1, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_grid_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.task_modules.assigners import GridAssigner\n\n\nclass TestGridAssigner(TestCase):\n\n    def test_assign(self):\n        assigner = GridAssigner(pos_iou_thr=0.5, neg_iou_thr=0.3)\n        pred_instances = InstanceData(\n            priors=torch.Tensor([[23, 23, 43, 43], [4, 5, 6, 7]]),\n            responsible_flags=torch.BoolTensor([1, 1]))\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[23, 23, 43, 43]]),\n            labels=torch.LongTensor([0]))\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([1, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n        # invalid neg_iou_thr\n        with self.assertRaises(AssertionError):\n            assigner = GridAssigner(\n                pos_iou_thr=0.5, neg_iou_thr=[0.3, 0.1, 0.4])\n            assigner.assign(\n                pred_instances=pred_instances, gt_instances=gt_instances)\n\n        # multi-neg_iou_thr\n        assigner = GridAssigner(pos_iou_thr=0.5, neg_iou_thr=(0.1, 0.3))\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([1, -1])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n        # gt_max_assign_all=False\n        assigner = GridAssigner(\n            pos_iou_thr=0.5, neg_iou_thr=0.3, gt_max_assign_all=False)\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([1, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n        # large min_pos_iou\n        assigner = GridAssigner(\n            pos_iou_thr=0.5, neg_iou_thr=0.3, min_pos_iou=1)\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([1, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_assign_with_empty_gt(self):\n        assigner = GridAssigner(pos_iou_thr=0.5, neg_iou_thr=0.3)\n        pred_instances = InstanceData(\n            priors=torch.Tensor([[0, 12, 23, 34], [4, 5, 6, 7]]),\n            responsible_flags=torch.BoolTensor([1, 1]))\n        gt_instances = InstanceData(\n            bboxes=torch.empty(0, 4), labels=torch.empty(0))\n\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([0, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_assign_with_empty_priors(self):\n        assigner = GridAssigner(pos_iou_thr=0.5, neg_iou_thr=0.3)\n        pred_instances = InstanceData(\n            priors=torch.Tensor(torch.empty(0, 4)),\n            responsible_flags=torch.empty(0))\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[23, 23, 43, 43]]),\n            labels=torch.LongTensor([0]))\n\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_hungarian_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners import HungarianAssigner\n\n\nclass TestHungarianAssigner(TestCase):\n\n    def test_init(self):\n        with self.assertRaises(AssertionError):\n            HungarianAssigner([])\n\n    def test_hungarian_match_assigner(self):\n        assigner = HungarianAssigner([\n            dict(type='ClassificationCost', weight=1.),\n            dict(type='BBoxL1Cost', weight=5.0),\n            dict(type='IoUCost', iou_mode='giou', weight=2.0)\n        ])\n\n        # test no gt bboxes\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.empty((0, 4)).float()\n        gt_instances.labels = torch.empty((0, )).long()\n        pred_instances = InstanceData()\n        pred_instances.scores = torch.rand((10, 81))\n        pred_instances.bboxes = torch.rand((10, 4))\n        img_meta = dict(img_shape=(10, 8))\n\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds == 0))\n        self.assertTrue(torch.all(assign_result.labels == -1))\n\n        # test with gt bboxes\n        gt_instances.bboxes = torch.FloatTensor([[0, 0, 5, 7], [3, 5, 7, 8]])\n        gt_instances.labels = torch.LongTensor([1, 20])\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.bboxes.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.bboxes.size(0))\n\n    def test_bbox_match_cost(self):\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.FloatTensor([[0, 0, 5, 7], [3, 5, 7, 8]])\n        gt_instances.labels = torch.LongTensor([1, 20])\n        pred_instances = InstanceData()\n        pred_instances.scores = torch.rand((10, 81))\n        pred_instances.bboxes = torch.rand((10, 4))\n        img_meta = dict(img_shape=(10, 8))\n\n        # test IoUCost\n        assigner = HungarianAssigner(\n            ConfigDict(dict(type='IoUCost', iou_mode='iou')))\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.bboxes.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.bboxes.size(0))\n\n        # test BBoxL1Cost\n        assigner = HungarianAssigner(ConfigDict(dict(type='BBoxL1Cost')))\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.bboxes.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.bboxes.size(0))\n\n    def test_cls_match_cost(self):\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.FloatTensor([[0, 0, 5, 7], [3, 5, 7, 8]])\n        gt_instances.labels = torch.LongTensor([1, 20])\n        pred_instances = InstanceData()\n        pred_instances.scores = torch.rand((10, 81))\n        pred_instances.bboxes = torch.rand((10, 4))\n        img_meta = dict(img_shape=(10, 8))\n\n        # test FocalLossCost\n        assigner = HungarianAssigner(dict(type='FocalLossCost'))\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.bboxes.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.bboxes.size(0))\n\n        # test ClassificationCost\n        assigner = HungarianAssigner(dict(type='ClassificationCost'))\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.bboxes.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.bboxes.size(0))\n\n    def test_mask_match_cost(self):\n        gt_instances = InstanceData()\n        gt_instances.masks = torch.randint(0, 2, (2, 10, 10)).long()\n        gt_instances.labels = torch.LongTensor([1, 20])\n\n        pred_instances = InstanceData()\n        pred_instances.masks = torch.rand((4, 10, 10))\n        pred_instances.scores = torch.rand((4, 25))\n        img_meta = dict(img_shape=(10, 10))\n\n        # test DiceCost\n        assigner = HungarianAssigner(dict(type='DiceCost'))\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.masks.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.masks.size(0))\n\n        # test CrossEntropyLossCost\n        assigner = HungarianAssigner(dict(type='CrossEntropyLossCost'))\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.masks.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.masks.size(0))\n\n        # test FocalLossCost\n        assigner = HungarianAssigner(\n            dict(type='FocalLossCost', binary_input=True))\n        assign_result = assigner.assign(\n            pred_instances, gt_instances, img_meta=img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_instances.masks.size(0))\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_instances.masks.size(0))\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_max_iou_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Tests the Assigner objects.\n\nCommandLine:\n    pytest  tests/test_core/test_bbox/test_assigners/test_max_iou_assigner.py\n    xdoctest  tests/test_core/test_bbox/test_assigners/test_max_iou_assigner.py zero\n\"\"\" # noqa\nimport pytest\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners import MaxIoUAssigner\n\n\n@pytest.mark.parametrize('neg_iou_thr', [0.5, (0, 0.5)])\ndef test_max_iou_assigner(neg_iou_thr):\n    self = MaxIoUAssigner(\n        pos_iou_thr=0.5,\n        neg_iou_thr=neg_iou_thr,\n    )\n    priors = torch.FloatTensor([\n        [0, 0, 10, 10],\n        [10, 10, 20, 20],\n        [5, 5, 15, 15],\n        [32, 32, 38, 42],\n    ])\n    gt_bboxes = torch.FloatTensor([\n        [0, 0, 10, 9],\n        [0, 10, 10, 19],\n    ])\n    gt_labels = torch.LongTensor([2, 3])\n\n    pred_instances = InstanceData(priors=priors)\n    gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n\n    assign_result = self.assign(pred_instances, gt_instances)\n    assert len(assign_result.gt_inds) == 4\n    assert len(assign_result.labels) == 4\n\n    expected_gt_inds = torch.LongTensor([1, 0, 2, 0])\n    assert torch.all(assign_result.gt_inds == expected_gt_inds)\n\n\ndef test_max_iou_assigner_with_ignore():\n    self = MaxIoUAssigner(\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n        ignore_iof_thr=0.5,\n        ignore_wrt_candidates=False,\n    )\n    priors = torch.FloatTensor([\n        [0, 0, 10, 10],\n        [10, 10, 20, 20],\n        [5, 5, 15, 15],\n        [30, 32, 40, 42],\n    ])\n    gt_bboxes = torch.FloatTensor([\n        [0, 0, 10, 9],\n        [0, 10, 10, 19],\n    ])\n    gt_labels = torch.LongTensor([2, 3])\n    gt_bboxes_ignore = torch.Tensor([\n        [30, 30, 40, 40],\n    ])\n\n    pred_instances = InstanceData(priors=priors)\n    gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n    gt_instances_ignore = InstanceData(bboxes=gt_bboxes_ignore)\n    assign_result = self.assign(\n        pred_instances, gt_instances, gt_instances_ignore=gt_instances_ignore)\n\n    expected_gt_inds = torch.LongTensor([1, 0, 2, -1])\n    assert torch.all(assign_result.gt_inds == expected_gt_inds)\n\n\ndef test_max_iou_assigner_with_empty_gt():\n    \"\"\"Test corner case where an image might have no true detections.\"\"\"\n    self = MaxIoUAssigner(\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n    )\n    priors = torch.FloatTensor([\n        [0, 0, 10, 10],\n        [10, 10, 20, 20],\n        [5, 5, 15, 15],\n        [32, 32, 38, 42],\n    ])\n    gt_bboxes = torch.empty(0, 4)\n    gt_labels = torch.empty(0)\n\n    pred_instances = InstanceData(priors=priors)\n    gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n    assign_result = self.assign(pred_instances, gt_instances)\n\n    expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n    assert torch.all(assign_result.gt_inds == expected_gt_inds)\n\n\ndef test_max_iou_assigner_with_empty_priors():\n    \"\"\"Test corner case where a network might predict no boxes.\"\"\"\n    self = MaxIoUAssigner(\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n    )\n    priors = torch.empty((0, 4))\n    gt_bboxes = torch.FloatTensor([\n        [0, 0, 10, 9],\n        [0, 10, 10, 19],\n    ])\n    gt_labels = torch.LongTensor([2, 3])\n\n    pred_instances = InstanceData(priors=priors)\n    gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n    # Test with gt_labels\n    assign_result = self.assign(pred_instances, gt_instances)\n    assert len(assign_result.gt_inds) == 0\n    assert tuple(assign_result.labels.shape) == (0, )\n\n\ndef test_max_iou_assigner_with_empty_boxes_and_ignore():\n    \"\"\"Test corner case where a network might predict no boxes and\n    ignore_iof_thr is on.\"\"\"\n    self = MaxIoUAssigner(\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n        ignore_iof_thr=0.5,\n    )\n    priors = torch.empty((0, 4))\n    gt_bboxes = torch.FloatTensor([\n        [0, 0, 10, 9],\n        [0, 10, 10, 19],\n    ])\n    gt_bboxes_ignore = torch.Tensor([\n        [30, 30, 40, 40],\n    ])\n    gt_labels = torch.LongTensor([2, 3])\n\n    pred_instances = InstanceData(priors=priors)\n    gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n    gt_instances_ignore = InstanceData(bboxes=gt_bboxes_ignore)\n\n    # Test with gt_labels\n    assign_result = self.assign(\n        pred_instances, gt_instances, gt_instances_ignore=gt_instances_ignore)\n    assert len(assign_result.gt_inds) == 0\n    assert tuple(assign_result.labels.shape) == (0, )\n\n\ndef test_max_iou_assigner_with_empty_priors_and_gt():\n    \"\"\"Test corner case where a network might predict no boxes and no gt.\"\"\"\n    self = MaxIoUAssigner(\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n    )\n    priors = torch.empty(0, 4)\n    gt_bboxes = torch.empty(0, 4)\n    gt_labels = torch.empty(0)\n\n    pred_instances = InstanceData(priors=priors)\n    gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n    assign_result = self.assign(pred_instances, gt_instances)\n    assert len(assign_result.gt_inds) == 0\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_point_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport unittest\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.task_modules.assigners import PointAssigner\n\n\nclass TestPointAssigner(unittest.TestCase):\n\n    def test_point_assigner(self):\n        assigner = PointAssigner()\n        pred_instances = InstanceData()\n        pred_instances.priors = torch.FloatTensor([\n            # [x, y, stride]\n            [0, 0, 1],\n            [10, 10, 1],\n            [5, 5, 1],\n            [32, 32, 1],\n        ])\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_instances.labels = torch.LongTensor([0, 1])\n        assign_result = assigner.assign(pred_instances, gt_instances)\n        expected_gt_inds = torch.LongTensor([1, 2, 1, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_point_assigner_with_empty_gt(self):\n        \"\"\"Test corner case where an image might have no true detections.\"\"\"\n        assigner = PointAssigner()\n        pred_instances = InstanceData()\n        pred_instances.priors = torch.FloatTensor([\n            # [x, y, stride]\n            [0, 0, 1],\n            [10, 10, 1],\n            [5, 5, 1],\n            [32, 32, 1],\n        ])\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.FloatTensor([])\n        gt_instances.labels = torch.LongTensor([])\n        assign_result = assigner.assign(pred_instances, gt_instances)\n\n        expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_point_assigner_with_empty_boxes_and_gt(self):\n        \"\"\"Test corner case where an image might predict no points and no\n        gt.\"\"\"\n        assigner = PointAssigner()\n        pred_instances = InstanceData()\n        pred_instances.priors = torch.FloatTensor([])\n        gt_instances = InstanceData()\n        gt_instances.bboxes = torch.FloatTensor([])\n        gt_instances.labels = torch.LongTensor([])\n        assign_result = assigner.assign(pred_instances, gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_region_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.config import ConfigDict\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners import RegionAssigner\n\n\nclass TestRegionAssigner(TestCase):\n\n    def setUp(self):\n        self.img_meta = ConfigDict(dict(img_shape=(256, 256)))\n        self.featmap_sizes = [(64, 64)]\n        self.anchor_scale = 10\n        self.anchor_strides = [1]\n\n    def test_region_assigner(self):\n        region_assigner = RegionAssigner(center_ratio=0.5, ignore_ratio=0.8)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        valid_flags = torch.BoolTensor([1, 1, 1, 1])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors, valid_flags=valid_flags)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        num_level_anchors = [4]\n\n        assign_result = region_assigner.assign(\n            pred_instances, gt_instances, self.img_meta, self.featmap_sizes,\n            num_level_anchors, self.anchor_scale, self.anchor_strides)\n        self.assertEqual(len(assign_result.gt_inds), 4)\n        self.assertEqual(len(assign_result.labels), 4)\n\n        expected_gt_inds = torch.LongTensor([1, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_region_assigner_with_ignore(self):\n        region_assigner = RegionAssigner(center_ratio=0.5)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [30, 32, 40, 42],\n        ])\n        valid_flags = torch.BoolTensor([1, 1, 1, 1])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n        gt_bboxes_ignore = torch.Tensor([\n            [30, 30, 40, 40],\n        ])\n\n        pred_instances = InstanceData(priors=priors, valid_flags=valid_flags)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        gt_instances_ignore = InstanceData(bboxes=gt_bboxes_ignore)\n        num_level_anchors = [4]\n        with self.assertRaises(NotImplementedError):\n            region_assigner.assign(\n                pred_instances,\n                gt_instances,\n                self.img_meta,\n                self.featmap_sizes,\n                num_level_anchors,\n                self.anchor_scale,\n                self.anchor_strides,\n                gt_instances_ignore=gt_instances_ignore)\n\n    def test_region_assigner_with_empty_gt(self):\n        \"\"\"Test corner case where an image might have no true detections.\"\"\"\n        region_assigner = RegionAssigner(center_ratio=0.5)\n        priors = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        valid_flags = torch.BoolTensor([1, 1, 1, 1])\n        gt_bboxes = torch.empty(0, 4)\n        gt_labels = torch.empty(0)\n\n        pred_instances = InstanceData(priors=priors, valid_flags=valid_flags)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        num_level_anchors = [4]\n        assign_result = region_assigner.assign(\n            pred_instances, gt_instances, self.img_meta, self.featmap_sizes,\n            num_level_anchors, self.anchor_scale, self.anchor_strides)\n\n        expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n\n    def test_atss_assigner_with_empty_boxes(self):\n        \"\"\"Test corner case where a network might predict no boxes.\"\"\"\n        region_assigner = RegionAssigner(center_ratio=0.5)\n        priors = torch.empty((0, 4))\n        valid_flags = torch.BoolTensor([])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n\n        pred_instances = InstanceData(priors=priors, valid_flags=valid_flags)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        num_level_anchors = [0]\n        assign_result = region_assigner.assign(\n            pred_instances, gt_instances, self.img_meta, self.featmap_sizes,\n            num_level_anchors, self.anchor_scale, self.anchor_strides)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n        self.assertTrue(tuple(assign_result.labels.shape) == (0, ))\n\n    def test_atss_assigner_with_empty_boxes_and_gt(self):\n        \"\"\"Test corner case where a network might predict no boxes and no\n        gt.\"\"\"\n        region_assigner = RegionAssigner(center_ratio=0.5)\n        priors = torch.empty((0, 4))\n        valid_flags = torch.BoolTensor([])\n        gt_bboxes = torch.empty((0, 4))\n        gt_labels = torch.empty(0)\n        num_level_anchors = [0]\n\n        pred_instances = InstanceData(priors=priors, valid_flags=valid_flags)\n        gt_instances = InstanceData(bboxes=gt_bboxes, labels=gt_labels)\n        assign_result = region_assigner.assign(\n            pred_instances, gt_instances, self.img_meta, self.featmap_sizes,\n            num_level_anchors, self.anchor_scale, self.anchor_strides)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_simota_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.task_modules.assigners import SimOTAAssigner\n\n\nclass TestSimOTAAssigner(TestCase):\n\n    def test_assign(self):\n        assigner = SimOTAAssigner(\n            center_radius=2.5,\n            candidate_topk=1,\n            iou_weight=3.0,\n            cls_weight=1.0)\n        pred_instances = InstanceData(\n            bboxes=torch.Tensor([[23, 23, 43, 43], [4, 5, 6, 7]]),\n            scores=torch.FloatTensor([[0.2], [0.8]]),\n            priors=torch.Tensor([[30, 30, 8, 8], [4, 5, 6, 7]]))\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[23, 23, 43, 43]]),\n            labels=torch.LongTensor([0]))\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n\n        expected_gt_inds = torch.LongTensor([1, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_assign_with_no_valid_bboxes(self):\n        assigner = SimOTAAssigner(\n            center_radius=2.5,\n            candidate_topk=1,\n            iou_weight=3.0,\n            cls_weight=1.0)\n        pred_instances = InstanceData(\n            bboxes=torch.Tensor([[123, 123, 143, 143], [114, 151, 161, 171]]),\n            scores=torch.FloatTensor([[0.2], [0.8]]),\n            priors=torch.Tensor([[30, 30, 8, 8], [55, 55, 8, 8]]))\n        gt_instances = InstanceData(\n            bboxes=torch.Tensor([[0, 0, 1, 1]]), labels=torch.LongTensor([0]))\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n\n        expected_gt_inds = torch.LongTensor([0, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_assign_with_empty_gt(self):\n        assigner = SimOTAAssigner(\n            center_radius=2.5,\n            candidate_topk=1,\n            iou_weight=3.0,\n            cls_weight=1.0)\n        pred_instances = InstanceData(\n            bboxes=torch.Tensor([[[30, 40, 50, 60]], [[4, 5, 6, 7]]]),\n            scores=torch.FloatTensor([[0.2], [0.8]]),\n            priors=torch.Tensor([[0, 12, 23, 34], [4, 5, 6, 7]]))\n        gt_instances = InstanceData(\n            bboxes=torch.empty(0, 4), labels=torch.empty(0))\n\n        assign_result = assigner.assign(\n            pred_instances=pred_instances, gt_instances=gt_instances)\n        expected_gt_inds = torch.LongTensor([0, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_task_aligned_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.task_modules.assigners import TaskAlignedAssigner\n\n\nclass TestTaskAlignedAssigner(TestCase):\n\n    def test_task_aligned_assigner(self):\n\n        with self.assertRaises(AssertionError):\n            TaskAlignedAssigner(topk=0)\n\n        assigner = TaskAlignedAssigner(topk=13)\n        pred_score = torch.FloatTensor([[0.1, 0.2], [0.2, 0.3], [0.3, 0.4],\n                                        [0.4, 0.5]])\n        pred_bbox = torch.FloatTensor([\n            [1, 1, 12, 8],\n            [4, 4, 20, 20],\n            [1, 5, 15, 15],\n            [30, 5, 32, 42],\n        ])\n        anchor = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([0, 1])\n        pred_instances = InstanceData()\n        pred_instances.priors = anchor\n        pred_instances.bboxes = pred_bbox\n        pred_instances.scores = pred_score\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n\n        assign_result = assigner.assign(pred_instances, gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 4)\n        self.assertEqual(len(assign_result.labels), 4)\n\n        # test empty gt\n        gt_bboxes = torch.empty(0, 4)\n        gt_labels = torch.empty(0, 2).long()\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n        expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n        self.assertTrue(torch.all(assign_result.gt_inds == expected_gt_inds))\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_task_uniform_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.structures import InstanceData\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.models.task_modules.assigners import UniformAssigner\n\n\nclass TestUniformAssigner(TestCase):\n\n    def test_uniform_assigner(self):\n        assigner = UniformAssigner(0.15, 0.7, 1)\n        pred_bbox = torch.FloatTensor([\n            [1, 1, 12, 8],\n            [4, 4, 20, 20],\n            [1, 5, 15, 15],\n            [30, 5, 32, 42],\n        ])\n        anchor = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n        pred_instances = InstanceData()\n        pred_instances.priors = anchor\n        pred_instances.decoder_priors = pred_bbox\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 4)\n        self.assertEqual(len(assign_result.labels), 4)\n\n        expected_gt_inds = torch.LongTensor([-1, 0, 2, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_uniform_assigner_with_empty_gt(self):\n        \"\"\"Test corner case where an image might have no true detections.\"\"\"\n        assigner = UniformAssigner(0.15, 0.7, 1)\n        pred_bbox = torch.FloatTensor([\n            [1, 1, 12, 8],\n            [4, 4, 20, 20],\n            [1, 5, 15, 15],\n            [30, 5, 32, 42],\n        ])\n        anchor = torch.FloatTensor([\n            [0, 0, 10, 10],\n            [10, 10, 20, 20],\n            [5, 5, 15, 15],\n            [32, 32, 38, 42],\n        ])\n        gt_bboxes = torch.empty(0, 4)\n        gt_labels = torch.empty(0)\n        pred_instances = InstanceData()\n        pred_instances.priors = anchor\n        pred_instances.decoder_priors = pred_bbox\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n\n        expected_gt_inds = torch.LongTensor([0, 0, 0, 0])\n        assert_allclose(assign_result.gt_inds, expected_gt_inds)\n\n    def test_uniform_assigner_with_empty_boxes(self):\n        \"\"\"Test corner case where a network might predict no boxes.\"\"\"\n        assigner = UniformAssigner(0.15, 0.7, 1)\n        pred_bbox = torch.empty((0, 4))\n        anchor = torch.empty((0, 4))\n        gt_bboxes = torch.FloatTensor([\n            [0, 0, 10, 9],\n            [0, 10, 10, 19],\n        ])\n        gt_labels = torch.LongTensor([2, 3])\n        pred_instances = InstanceData()\n        pred_instances.priors = anchor\n        pred_instances.decoder_priors = pred_bbox\n        gt_instances = InstanceData()\n        gt_instances.bboxes = gt_bboxes\n        gt_instances.labels = gt_labels\n\n        # Test with gt_labels\n        assign_result = assigner.assign(pred_instances, gt_instances)\n        self.assertEqual(len(assign_result.gt_inds), 0)\n        self.assertEqual(tuple(assign_result.labels.shape), (0, ))\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_assigners/test_topk_hungarian_assigner.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import ConfigDict\n\nfrom mmdet.models.task_modules.assigners import TopkHungarianAssigner\n\n\nclass TestTopkHungarianAssigner(TestCase):\n\n    def test_init(self):\n        with self.assertRaises(AssertionError):\n            TopkHungarianAssigner(topk=0)\n\n    def test_topk_hungarian_match_assigner(self):\n        num_classes = 80\n        topk = 4\n\n        pred_class_scores = torch.rand((10, num_classes))\n        pred_bboxes = torch.randn((10, 4))\n\n        assigner = TopkHungarianAssigner(topk=topk)\n\n        # test no gt bboxes\n        gt_bboxes = torch.empty((0, 4))\n        gt_labels = torch.LongTensor([])\n        img_meta = dict(img_shape=(10, 8))\n\n        assign_result = assigner.assign(pred_class_scores, pred_bboxes,\n                                        gt_bboxes, gt_labels, img_meta)\n\n        self.assertTrue(torch.all(assign_result.gt_inds == 0))\n        self.assertTrue(torch.all(assign_result.labels == -1))\n\n        # test with gt bboxes\n        gt_bboxes = torch.FloatTensor([[0, 0, 5, 7], [3, 5, 7, 8]])\n        gt_labels = torch.LongTensor([1, 20])\n        assign_result = assigner.assign(pred_class_scores, pred_bboxes,\n                                        gt_bboxes, gt_labels, img_meta)\n\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_bboxes.size(0) * topk)\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_bboxes.size(0) * topk)\n\n    def test_bbox_match_cost(self):\n        num_classes = 80\n        topk = 4\n\n        pred_class_scores = torch.rand((10, num_classes))\n        pred_bboxes = torch.randn((10, 4))\n\n        gt_bboxes = torch.FloatTensor([[0, 0, 5, 7], [3, 5, 7, 8]])\n        gt_labels = torch.LongTensor([1, 20])\n        img_meta = dict(img_shape=(10, 8))\n\n        # test IoUCost\n        assigner = TopkHungarianAssigner(\n            topk=topk,\n            iou_cost=ConfigDict(dict(type='IoUCost', iou_mode='iou')))\n        assign_result = assigner.assign(pred_class_scores, pred_bboxes,\n                                        gt_bboxes, gt_labels, img_meta)\n\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_bboxes.size(0) * topk)\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_bboxes.size(0) * topk)\n\n        # test BBoxL1Cost\n        assigner = TopkHungarianAssigner(\n            topk=4, reg_cost=ConfigDict(dict(type='BBoxL1Cost')))\n        assign_result = assigner.assign(pred_class_scores, pred_bboxes,\n                                        gt_bboxes, gt_labels, img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_bboxes.size(0) * topk)\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_bboxes.size(0) * topk)\n\n    def test_cls_match_cost(self):\n        num_classes = 80\n        topk = 4\n\n        pred_class_scores = torch.rand((10, num_classes))\n        pred_bboxes = torch.randn((10, 4))\n\n        gt_bboxes = torch.FloatTensor([[0, 0, 5, 7], [3, 5, 7, 8]])\n        gt_labels = torch.LongTensor([1, 20])\n        img_meta = dict(img_shape=(10, 8))\n\n        # test FocalLossCost\n        assigner = TopkHungarianAssigner(\n            topk=topk, cls_cost=dict(type='FocalLossCost'))\n        assign_result = assigner.assign(pred_class_scores, pred_bboxes,\n                                        gt_bboxes, gt_labels, img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_bboxes.size(0) * topk)\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_bboxes.size(0) * topk)\n\n        # test ClassificationCost\n        assigner = TopkHungarianAssigner(\n            topk=4, cls_cost=dict(type='ClassificationCost'))\n        assign_result = assigner.assign(pred_class_scores, pred_bboxes,\n                                        gt_bboxes, gt_labels, img_meta)\n        self.assertTrue(torch.all(assign_result.gt_inds > -1))\n        self.assertEqual((assign_result.gt_inds > 0).sum(),\n                         gt_bboxes.size(0) * topk)\n        self.assertEqual((assign_result.labels > -1).sum(),\n                         gt_bboxes.size(0) * topk)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_coder/test_delta_xywh_bbox_coder.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport pytest\nimport torch\n\nfrom mmdet.models.task_modules.coders import (DeltaXYWHBBoxCoder,\n                                              DeltaXYWHBBoxCoderForGLIP)\n\n\ndef test_delta_bbox_coder():\n    coder = DeltaXYWHBBoxCoder()\n\n    rois = torch.Tensor([[0., 0., 1., 1.], [0., 0., 1., 1.], [0., 0., 1., 1.],\n                         [5., 5., 5., 5.]])\n    deltas = torch.Tensor([[0., 0., 0., 0.], [1., 1., 1., 1.],\n                           [0., 0., 2., -1.], [0.7, -1.9, -0.5, 0.3]])\n    expected_decode_bboxes = torch.Tensor([[0.0000, 0.0000, 1.0000, 1.0000],\n                                           [0.1409, 0.1409, 2.8591, 2.8591],\n                                           [0.0000, 0.3161, 4.1945, 0.6839],\n                                           [5.0000, 5.0000, 5.0000, 5.0000]])\n\n    out = coder.decode(rois, deltas, max_shape=(32, 32))\n    assert expected_decode_bboxes.allclose(out, atol=1e-04)\n    out = coder.decode(rois, deltas, max_shape=torch.Tensor((32, 32)))\n    assert expected_decode_bboxes.allclose(out, atol=1e-04)\n\n    batch_rois = rois.unsqueeze(0).repeat(2, 1, 1)\n    batch_deltas = deltas.unsqueeze(0).repeat(2, 1, 1)\n    batch_out = coder.decode(batch_rois, batch_deltas, max_shape=(32, 32))[0]\n    assert out.allclose(batch_out)\n    batch_out = coder.decode(\n        batch_rois, batch_deltas, max_shape=[(32, 32), (32, 32)])[0]\n    assert out.allclose(batch_out)\n\n    # test max_shape is not equal to batch\n    with pytest.raises(AssertionError):\n        coder.decode(\n            batch_rois, batch_deltas, max_shape=[(32, 32), (32, 32), (32, 32)])\n\n    rois = torch.zeros((0, 4))\n    deltas = torch.zeros((0, 4))\n    out = coder.decode(rois, deltas, max_shape=(32, 32))\n    assert rois.shape == out.shape\n\n    # test add_ctr_clamp\n    coder = DeltaXYWHBBoxCoder(add_ctr_clamp=True, ctr_clamp=2)\n\n    rois = torch.Tensor([[0., 0., 6., 6.], [0., 0., 1., 1.], [0., 0., 1., 1.],\n                         [5., 5., 5., 5.]])\n    deltas = torch.Tensor([[1., 1., 2., 2.], [1., 1., 1., 1.],\n                           [0., 0., 2., -1.], [0.7, -1.9, -0.5, 0.3]])\n    expected_decode_bboxes = torch.Tensor([[0.0000, 0.0000, 27.1672, 27.1672],\n                                           [0.1409, 0.1409, 2.8591, 2.8591],\n                                           [0.0000, 0.3161, 4.1945, 0.6839],\n                                           [5.0000, 5.0000, 5.0000, 5.0000]])\n\n    out = coder.decode(rois, deltas, max_shape=(32, 32))\n    assert expected_decode_bboxes.allclose(out, atol=1e-04)\n\n    coder = DeltaXYWHBBoxCoderForGLIP()\n\n    rois = torch.Tensor([[0., 0., 1., 1.], [0., 0., 1., 1.], [0., 0., 1., 1.],\n                         [5., 5., 5., 5.]])\n    deltas = torch.Tensor([[0., 0., 0., 0.], [1., 1., 1., 1.],\n                           [0., 0., 2., -1.], [0.7, -1.9, -0.5, 0.3]])\n    expected_decode_bboxes = torch.Tensor([[0.0000, 0.0000, 0.0000, 0.0000],\n                                           [0.1409, 0.1409, 1.8591, 1.8591],\n                                           [0.0000, 0.3161, 3.1945, 0.0000],\n                                           [5.0000, 5.0000, 4.0000, 4.0000]])\n\n    out = coder.decode(rois, deltas, max_shape=(32, 32))\n    assert expected_decode_bboxes.allclose(out, atol=1e-04)\n    out = coder.decode(rois, deltas, max_shape=torch.Tensor((32, 32)))\n    assert expected_decode_bboxes.allclose(out, atol=1e-04)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_iou2d_calculator.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport pytest\nimport torch\n\nfrom mmdet.evaluation import bbox_overlaps as recall_overlaps\nfrom mmdet.models.task_modules import BboxOverlaps2D\nfrom mmdet.structures.bbox import bbox_overlaps\n\n\ndef test_bbox_overlaps_2d(eps=1e-7):\n\n    def _construct_bbox(num_bbox=None):\n        img_h = int(np.random.randint(3, 1000))\n        img_w = int(np.random.randint(3, 1000))\n        if num_bbox is None:\n            num_bbox = np.random.randint(1, 10)\n        x1y1 = torch.rand((num_bbox, 2))\n        x2y2 = torch.max(torch.rand((num_bbox, 2)), x1y1)\n        bboxes = torch.cat((x1y1, x2y2), -1)\n        bboxes[:, 0::2] *= img_w\n        bboxes[:, 1::2] *= img_h\n        return bboxes, num_bbox\n\n    # is_aligned is True, bboxes.size(-1) == 5 (include score)\n    self = BboxOverlaps2D()\n    bboxes1, num_bbox = _construct_bbox()\n    bboxes2, _ = _construct_bbox(num_bbox)\n    bboxes1 = torch.cat((bboxes1, torch.rand((num_bbox, 1))), 1)\n    bboxes2 = torch.cat((bboxes2, torch.rand((num_bbox, 1))), 1)\n    gious = self(bboxes1, bboxes2, 'giou', True)\n    assert gious.size() == (num_bbox, ), gious.size()\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n\n    # is_aligned is True, bboxes1.size(-2) == 0\n    bboxes1 = torch.empty((0, 4))\n    bboxes2 = torch.empty((0, 4))\n    gious = self(bboxes1, bboxes2, 'giou', True)\n    assert gious.size() == (0, ), gious.size()\n    assert torch.all(gious == torch.empty((0, )))\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n\n    # is_aligned is True, and bboxes.ndims > 2\n    bboxes1, num_bbox = _construct_bbox()\n    bboxes2, _ = _construct_bbox(num_bbox)\n    bboxes1 = bboxes1.unsqueeze(0).repeat(2, 1, 1)\n    # test assertion when batch dim is not the same\n    with pytest.raises(AssertionError):\n        self(bboxes1, bboxes2.unsqueeze(0).repeat(3, 1, 1), 'giou', True)\n    bboxes2 = bboxes2.unsqueeze(0).repeat(2, 1, 1)\n    gious = self(bboxes1, bboxes2, 'giou', True)\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n    assert gious.size() == (2, num_bbox)\n    bboxes1 = bboxes1.unsqueeze(0).repeat(2, 1, 1, 1)\n    bboxes2 = bboxes2.unsqueeze(0).repeat(2, 1, 1, 1)\n    gious = self(bboxes1, bboxes2, 'giou', True)\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n    assert gious.size() == (2, 2, num_bbox)\n\n    # is_aligned is False\n    bboxes1, num_bbox1 = _construct_bbox()\n    bboxes2, num_bbox2 = _construct_bbox()\n    gious = self(bboxes1, bboxes2, 'giou')\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n    assert gious.size() == (num_bbox1, num_bbox2)\n\n    # is_aligned is False, and bboxes.ndims > 2\n    bboxes1 = bboxes1.unsqueeze(0).repeat(2, 1, 1)\n    bboxes2 = bboxes2.unsqueeze(0).repeat(2, 1, 1)\n    gious = self(bboxes1, bboxes2, 'giou')\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n    assert gious.size() == (2, num_bbox1, num_bbox2)\n    bboxes1 = bboxes1.unsqueeze(0)\n    bboxes2 = bboxes2.unsqueeze(0)\n    gious = self(bboxes1, bboxes2, 'giou')\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n    assert gious.size() == (1, 2, num_bbox1, num_bbox2)\n\n    # is_aligned is False, bboxes1.size(-2) == 0\n    gious = self(torch.empty(1, 2, 0, 4), bboxes2, 'giou')\n    assert torch.all(gious == torch.empty(1, 2, 0, bboxes2.size(-2)))\n    assert torch.all(gious >= -1) and torch.all(gious <= 1)\n\n    # test allclose between bbox_overlaps and the original official\n    # implementation.\n    bboxes1 = torch.FloatTensor([\n        [0, 0, 10, 10],\n        [10, 10, 20, 20],\n        [32, 32, 38, 42],\n    ])\n    bboxes2 = torch.FloatTensor([\n        [0, 0, 10, 20],\n        [0, 10, 10, 19],\n        [10, 10, 20, 20],\n    ])\n    gious = bbox_overlaps(bboxes1, bboxes2, 'giou', is_aligned=True, eps=eps)\n    gious = gious.numpy().round(4)\n    # the gt is got with four decimal precision.\n    expected_gious = np.array([0.5000, -0.0500, -0.8214])\n    assert np.allclose(gious, expected_gious, rtol=0, atol=eps)\n\n    # test mode 'iof'\n    ious = bbox_overlaps(bboxes1, bboxes2, 'iof', is_aligned=True, eps=eps)\n    assert torch.all(ious >= -1) and torch.all(ious <= 1)\n    assert ious.size() == (bboxes1.size(0), )\n    ious = bbox_overlaps(bboxes1, bboxes2, 'iof', eps=eps)\n    assert torch.all(ious >= -1) and torch.all(ious <= 1)\n    assert ious.size() == (bboxes1.size(0), bboxes2.size(0))\n\n\ndef test_voc_recall_overlaps():\n\n    def _construct_bbox(num_bbox=None):\n        img_h = int(np.random.randint(3, 1000))\n        img_w = int(np.random.randint(3, 1000))\n        if num_bbox is None:\n            num_bbox = np.random.randint(1, 10)\n        x1y1 = torch.rand((num_bbox, 2))\n        x2y2 = torch.max(torch.rand((num_bbox, 2)), x1y1)\n        bboxes = torch.cat((x1y1, x2y2), -1)\n        bboxes[:, 0::2] *= img_w\n        bboxes[:, 1::2] *= img_h\n        return bboxes.numpy(), num_bbox\n\n    bboxes1, num_bbox = _construct_bbox()\n    bboxes2, _ = _construct_bbox(num_bbox)\n    ious = recall_overlaps(\n        bboxes1, bboxes2, 'iou', use_legacy_coordinate=False)\n    assert ious.shape == (num_bbox, num_bbox)\n    assert np.all(ious >= -1) and np.all(ious <= 1)\n\n    ious = recall_overlaps(bboxes1, bboxes2, 'iou', use_legacy_coordinate=True)\n    assert ious.shape == (num_bbox, num_bbox)\n    assert np.all(ious >= -1) and np.all(ious <= 1)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_prior_generators/test_anchor_generator.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"\nCommandLine:\n    pytest tests/test_utils/test_anchor.py\n    xdoctest tests/test_utils/test_anchor.py zero\n\n\"\"\"\nimport pytest\nimport torch\n\n\ndef test_standard_points_generator():\n    from mmdet.models.task_modules import build_prior_generator\n\n    # teat init\n    anchor_generator_cfg = dict(\n        type='MlvlPointGenerator', strides=[4, 8], offset=0)\n    anchor_generator = build_prior_generator(anchor_generator_cfg)\n    assert anchor_generator is not None\n    assert anchor_generator.num_base_priors == [1, 1]\n    # test_stride\n    from mmdet.models.task_modules.prior_generators import MlvlPointGenerator\n\n    # Square strides\n    mlvl_points = MlvlPointGenerator(strides=[4, 10], offset=0)\n    mlvl_points_half_stride_generator = MlvlPointGenerator(\n        strides=[4, 10], offset=0.5)\n    assert mlvl_points.num_levels == 2\n\n    # assert self.num_levels == len(featmap_sizes)\n    with pytest.raises(AssertionError):\n        mlvl_points.grid_priors(featmap_sizes=[(2, 2)], device='cpu')\n    priors = mlvl_points.grid_priors(\n        featmap_sizes=[(2, 2), (4, 8)], device='cpu')\n    priors_with_stride = mlvl_points.grid_priors(\n        featmap_sizes=[(2, 2), (4, 8)], with_stride=True, device='cpu')\n    assert len(priors) == 2\n\n    # assert last dimension is (coord_x, coord_y, stride_w, stride_h).\n    assert priors_with_stride[0].size(1) == 4\n    assert priors_with_stride[0][0][2] == 4\n    assert priors_with_stride[0][0][3] == 4\n    assert priors_with_stride[1][0][2] == 10\n    assert priors_with_stride[1][0][3] == 10\n\n    stride_4_feat_2_2 = priors[0]\n    assert (stride_4_feat_2_2[1] - stride_4_feat_2_2[0]).sum() == 4\n    assert stride_4_feat_2_2.size(0) == 4\n    assert stride_4_feat_2_2.size(1) == 2\n\n    stride_10_feat_4_8 = priors[1]\n    assert (stride_10_feat_4_8[1] - stride_10_feat_4_8[0]).sum() == 10\n    assert stride_10_feat_4_8.size(0) == 4 * 8\n    assert stride_10_feat_4_8.size(1) == 2\n\n    # assert the offset of 0.5 * stride\n    priors_half_offset = mlvl_points_half_stride_generator.grid_priors(\n        featmap_sizes=[(2, 2), (4, 8)], device='cpu')\n\n    assert (priors_half_offset[0][0] - priors[0][0]).sum() == 4 * 0.5 * 2\n    assert (priors_half_offset[1][0] - priors[1][0]).sum() == 10 * 0.5 * 2\n    if torch.cuda.is_available():\n        anchor_generator_cfg = dict(\n            type='MlvlPointGenerator', strides=[4, 8], offset=0)\n        anchor_generator = build_prior_generator(anchor_generator_cfg)\n        assert anchor_generator is not None\n        # Square strides\n        mlvl_points = MlvlPointGenerator(strides=[4, 10], offset=0)\n        mlvl_points_half_stride_generator = MlvlPointGenerator(\n            strides=[4, 10], offset=0.5)\n        assert mlvl_points.num_levels == 2\n\n        # assert self.num_levels == len(featmap_sizes)\n        with pytest.raises(AssertionError):\n            mlvl_points.grid_priors(featmap_sizes=[(2, 2)], device='cuda')\n        priors = mlvl_points.grid_priors(\n            featmap_sizes=[(2, 2), (4, 8)], device='cuda')\n        priors_with_stride = mlvl_points.grid_priors(\n            featmap_sizes=[(2, 2), (4, 8)], with_stride=True, device='cuda')\n        assert len(priors) == 2\n\n        # assert last dimension is (coord_x, coord_y, stride_w, stride_h).\n        assert priors_with_stride[0].size(1) == 4\n        assert priors_with_stride[0][0][2] == 4\n        assert priors_with_stride[0][0][3] == 4\n        assert priors_with_stride[1][0][2] == 10\n        assert priors_with_stride[1][0][3] == 10\n\n        stride_4_feat_2_2 = priors[0]\n        assert (stride_4_feat_2_2[1] - stride_4_feat_2_2[0]).sum() == 4\n        assert stride_4_feat_2_2.size(0) == 4\n        assert stride_4_feat_2_2.size(1) == 2\n\n        stride_10_feat_4_8 = priors[1]\n        assert (stride_10_feat_4_8[1] - stride_10_feat_4_8[0]).sum() == 10\n        assert stride_10_feat_4_8.size(0) == 4 * 8\n        assert stride_10_feat_4_8.size(1) == 2\n\n        # assert the offset of 0.5 * stride\n        priors_half_offset = mlvl_points_half_stride_generator.grid_priors(\n            featmap_sizes=[(2, 2), (4, 8)], device='cuda')\n\n        assert (priors_half_offset[0][0] - priors[0][0]).sum() == 4 * 0.5 * 2\n        assert (priors_half_offset[1][0] - priors[1][0]).sum() == 10 * 0.5 * 2\n\n\ndef test_sparse_prior():\n    from mmdet.models.task_modules.prior_generators import MlvlPointGenerator\n    mlvl_points = MlvlPointGenerator(strides=[4, 10], offset=0)\n    prior_indexs = torch.Tensor([0, 2, 4, 5, 6, 9]).long()\n\n    featmap_sizes = [(3, 5), (6, 4)]\n    grid_anchors = mlvl_points.grid_priors(\n        featmap_sizes=featmap_sizes, with_stride=False, device='cpu')\n    sparse_prior = mlvl_points.sparse_priors(\n        prior_idxs=prior_indexs,\n        featmap_size=featmap_sizes[0],\n        level_idx=0,\n        device='cpu')\n\n    assert not sparse_prior.is_cuda\n    assert (sparse_prior == grid_anchors[0][prior_indexs]).all()\n    sparse_prior = mlvl_points.sparse_priors(\n        prior_idxs=prior_indexs,\n        featmap_size=featmap_sizes[1],\n        level_idx=1,\n        device='cpu')\n    assert (sparse_prior == grid_anchors[1][prior_indexs]).all()\n\n    from mmdet.models.task_modules.prior_generators import AnchorGenerator\n    mlvl_anchors = AnchorGenerator(\n        strides=[16, 32], ratios=[1.], scales=[1.], base_sizes=[4, 8])\n    prior_indexs = torch.Tensor([0, 2, 4, 5, 6, 9]).long()\n\n    featmap_sizes = [(3, 5), (6, 4)]\n    grid_anchors = mlvl_anchors.grid_priors(\n        featmap_sizes=featmap_sizes, device='cpu')\n    sparse_prior = mlvl_anchors.sparse_priors(\n        prior_idxs=prior_indexs,\n        featmap_size=featmap_sizes[0],\n        level_idx=0,\n        device='cpu')\n    assert (sparse_prior == grid_anchors[0][prior_indexs]).all()\n    sparse_prior = mlvl_anchors.sparse_priors(\n        prior_idxs=prior_indexs,\n        featmap_size=featmap_sizes[1],\n        level_idx=1,\n        device='cpu')\n    assert (sparse_prior == grid_anchors[1][prior_indexs]).all()\n\n    # for ssd\n    from mmdet.models.task_modules.prior_generators import SSDAnchorGenerator\n    featmap_sizes = [(38, 38), (19, 19), (10, 10)]\n    anchor_generator = SSDAnchorGenerator(\n        scale_major=False,\n        input_size=300,\n        basesize_ratio_range=(0.15, 0.9),\n        strides=[8, 16, 32],\n        ratios=[[2], [2, 3], [2, 3]])\n    ssd_anchors = anchor_generator.grid_anchors(featmap_sizes, device='cpu')\n    for i in range(len(featmap_sizes)):\n        sparse_ssd_anchors = anchor_generator.sparse_priors(\n            prior_idxs=prior_indexs,\n            level_idx=i,\n            featmap_size=featmap_sizes[i],\n            device='cpu')\n        assert (sparse_ssd_anchors == ssd_anchors[i][prior_indexs]).all()\n\n    # for yolo\n    from mmdet.models.task_modules.prior_generators import YOLOAnchorGenerator\n    featmap_sizes = [(38, 38), (19, 19), (10, 10)]\n    anchor_generator = YOLOAnchorGenerator(\n        strides=[32, 16, 8],\n        base_sizes=[\n            [(116, 90), (156, 198), (373, 326)],\n            [(30, 61), (62, 45), (59, 119)],\n            [(10, 13), (16, 30), (33, 23)],\n        ])\n    yolo_anchors = anchor_generator.grid_anchors(featmap_sizes, device='cpu')\n    for i in range(len(featmap_sizes)):\n        sparse_yolo_anchors = anchor_generator.sparse_priors(\n            prior_idxs=prior_indexs,\n            level_idx=i,\n            featmap_size=featmap_sizes[i],\n            device='cpu')\n        assert (sparse_yolo_anchors == yolo_anchors[i][prior_indexs]).all()\n\n    if torch.cuda.is_available():\n        mlvl_points = MlvlPointGenerator(strides=[4, 10], offset=0)\n        prior_indexs = torch.Tensor([0, 3, 4, 5, 6, 7, 1, 2, 4, 5, 6,\n                                     9]).long().cuda()\n\n        featmap_sizes = [(6, 8), (6, 4)]\n        grid_anchors = mlvl_points.grid_priors(\n            featmap_sizes=featmap_sizes, with_stride=False, device='cuda')\n        sparse_prior = mlvl_points.sparse_priors(\n            prior_idxs=prior_indexs,\n            featmap_size=featmap_sizes[0],\n            level_idx=0,\n            device='cuda')\n        assert (sparse_prior == grid_anchors[0][prior_indexs]).all()\n        sparse_prior = mlvl_points.sparse_priors(\n            prior_idxs=prior_indexs,\n            featmap_size=featmap_sizes[1],\n            level_idx=1,\n            device='cuda')\n        assert (sparse_prior == grid_anchors[1][prior_indexs]).all()\n        assert sparse_prior.is_cuda\n        mlvl_anchors = AnchorGenerator(\n            strides=[16, 32],\n            ratios=[1., 2.5],\n            scales=[1., 5.],\n            base_sizes=[4, 8])\n        prior_indexs = torch.Tensor([4, 5, 6, 7, 0, 2, 50, 4, 5, 6,\n                                     9]).long().cuda()\n\n        featmap_sizes = [(13, 5), (16, 4)]\n        grid_anchors = mlvl_anchors.grid_priors(\n            featmap_sizes=featmap_sizes, device='cuda')\n        sparse_prior = mlvl_anchors.sparse_priors(\n            prior_idxs=prior_indexs,\n            featmap_size=featmap_sizes[0],\n            level_idx=0,\n            device='cuda')\n        assert (sparse_prior == grid_anchors[0][prior_indexs]).all()\n        sparse_prior = mlvl_anchors.sparse_priors(\n            prior_idxs=prior_indexs,\n            featmap_size=featmap_sizes[1],\n            level_idx=1,\n            device='cuda')\n        assert (sparse_prior == grid_anchors[1][prior_indexs]).all()\n\n        # for ssd\n        from mmdet.models.task_modules.prior_generators import \\\n            SSDAnchorGenerator\n        featmap_sizes = [(38, 38), (19, 19), (10, 10)]\n        anchor_generator = SSDAnchorGenerator(\n            scale_major=False,\n            input_size=300,\n            basesize_ratio_range=(0.15, 0.9),\n            strides=[8, 16, 32],\n            ratios=[[2], [2, 3], [2, 3]])\n        ssd_anchors = anchor_generator.grid_anchors(\n            featmap_sizes, device='cuda')\n        for i in range(len(featmap_sizes)):\n            sparse_ssd_anchors = anchor_generator.sparse_priors(\n                prior_idxs=prior_indexs,\n                level_idx=i,\n                featmap_size=featmap_sizes[i],\n                device='cuda')\n            assert (sparse_ssd_anchors == ssd_anchors[i][prior_indexs]).all()\n\n        # for yolo\n        from mmdet.models.task_modules.prior_generators import \\\n            YOLOAnchorGenerator\n        featmap_sizes = [(38, 38), (19, 19), (10, 10)]\n        anchor_generator = YOLOAnchorGenerator(\n            strides=[32, 16, 8],\n            base_sizes=[\n                [(116, 90), (156, 198), (373, 326)],\n                [(30, 61), (62, 45), (59, 119)],\n                [(10, 13), (16, 30), (33, 23)],\n            ])\n        yolo_anchors = anchor_generator.grid_anchors(\n            featmap_sizes, device='cuda')\n        for i in range(len(featmap_sizes)):\n            sparse_yolo_anchors = anchor_generator.sparse_priors(\n                prior_idxs=prior_indexs,\n                level_idx=i,\n                featmap_size=featmap_sizes[i],\n                device='cuda')\n            assert (sparse_yolo_anchors == yolo_anchors[i][prior_indexs]).all()\n\n\ndef test_standard_anchor_generator():\n    from mmdet.models.task_modules import build_anchor_generator\n    anchor_generator_cfg = dict(\n        type='AnchorGenerator',\n        scales=[8],\n        ratios=[0.5, 1.0, 2.0],\n        strides=[4, 8])\n\n    anchor_generator = build_anchor_generator(anchor_generator_cfg)\n    assert anchor_generator.num_base_priors == \\\n           anchor_generator.num_base_anchors\n    assert anchor_generator.num_base_priors == [3, 3]\n    assert anchor_generator is not None\n\n\ndef test_strides():\n    from mmdet.models.task_modules.prior_generators import AnchorGenerator\n\n    # Square strides\n    self = AnchorGenerator([10], [1.], [1.], [10])\n    anchors = self.grid_anchors([(2, 2)], device='cpu')\n\n    expected_anchors = torch.tensor([[-5., -5., 5., 5.], [5., -5., 15., 5.],\n                                     [-5., 5., 5., 15.], [5., 5., 15., 15.]])\n\n    assert torch.equal(anchors[0], expected_anchors)\n\n    # Different strides in x and y direction\n    self = AnchorGenerator([(10, 20)], [1.], [1.], [10])\n    anchors = self.grid_anchors([(2, 2)], device='cpu')\n\n    expected_anchors = torch.tensor([[-5., -5., 5., 5.], [5., -5., 15., 5.],\n                                     [-5., 15., 5., 25.], [5., 15., 15., 25.]])\n\n    assert torch.equal(anchors[0], expected_anchors)\n\n\ndef test_ssd_anchor_generator():\n    from mmdet.models.task_modules import build_anchor_generator\n    if torch.cuda.is_available():\n        device = 'cuda'\n    else:\n        device = 'cpu'\n\n    # min_sizes max_sizes must set at the same time\n    with pytest.raises(AssertionError):\n        anchor_generator_cfg = dict(\n            type='SSDAnchorGenerator',\n            scale_major=False,\n            min_sizes=[48, 100, 150, 202, 253, 300],\n            max_sizes=None,\n            strides=[8, 16, 32, 64, 100, 300],\n            ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]])\n        build_anchor_generator(anchor_generator_cfg)\n\n    # length of min_sizes max_sizes must be the same\n    with pytest.raises(AssertionError):\n        anchor_generator_cfg = dict(\n            type='SSDAnchorGenerator',\n            scale_major=False,\n            min_sizes=[48, 100, 150, 202, 253, 300],\n            max_sizes=[100, 150, 202, 253],\n            strides=[8, 16, 32, 64, 100, 300],\n            ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]])\n        build_anchor_generator(anchor_generator_cfg)\n\n    # test setting anchor size manually\n    anchor_generator_cfg = dict(\n        type='SSDAnchorGenerator',\n        scale_major=False,\n        min_sizes=[48, 100, 150, 202, 253, 304],\n        max_sizes=[100, 150, 202, 253, 304, 320],\n        strides=[16, 32, 64, 107, 160, 320],\n        ratios=[[2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]])\n\n    featmap_sizes = [(38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1)]\n    anchor_generator = build_anchor_generator(anchor_generator_cfg)\n\n    expected_base_anchors = [\n        torch.Tensor([[-16.0000, -16.0000, 32.0000, 32.0000],\n                      [-26.6410, -26.6410, 42.6410, 42.6410],\n                      [-25.9411, -8.9706, 41.9411, 24.9706],\n                      [-8.9706, -25.9411, 24.9706, 41.9411],\n                      [-33.5692, -5.8564, 49.5692, 21.8564],\n                      [-5.8564, -33.5692, 21.8564, 49.5692]]),\n        torch.Tensor([[-34.0000, -34.0000, 66.0000, 66.0000],\n                      [-45.2372, -45.2372, 77.2372, 77.2372],\n                      [-54.7107, -19.3553, 86.7107, 51.3553],\n                      [-19.3553, -54.7107, 51.3553, 86.7107],\n                      [-70.6025, -12.8675, 102.6025, 44.8675],\n                      [-12.8675, -70.6025, 44.8675, 102.6025]]),\n        torch.Tensor([[-43.0000, -43.0000, 107.0000, 107.0000],\n                      [-55.0345, -55.0345, 119.0345, 119.0345],\n                      [-74.0660, -21.0330, 138.0660, 85.0330],\n                      [-21.0330, -74.0660, 85.0330, 138.0660],\n                      [-97.9038, -11.3013, 161.9038, 75.3013],\n                      [-11.3013, -97.9038, 75.3013, 161.9038]]),\n        torch.Tensor([[-47.5000, -47.5000, 154.5000, 154.5000],\n                      [-59.5332, -59.5332, 166.5332, 166.5332],\n                      [-89.3356, -17.9178, 196.3356, 124.9178],\n                      [-17.9178, -89.3356, 124.9178, 196.3356],\n                      [-121.4371, -4.8124, 228.4371, 111.8124],\n                      [-4.8124, -121.4371, 111.8124, 228.4371]]),\n        torch.Tensor([[-46.5000, -46.5000, 206.5000, 206.5000],\n                      [-58.6651, -58.6651, 218.6651, 218.6651],\n                      [-98.8980, -9.4490, 258.8980, 169.4490],\n                      [-9.4490, -98.8980, 169.4490, 258.8980],\n                      [-139.1044, 6.9652, 299.1044, 153.0348],\n                      [6.9652, -139.1044, 153.0348, 299.1044]]),\n        torch.Tensor([[8.0000, 8.0000, 312.0000, 312.0000],\n                      [4.0513, 4.0513, 315.9487, 315.9487],\n                      [-54.9605, 52.5198, 374.9604, 267.4802],\n                      [52.5198, -54.9605, 267.4802, 374.9604],\n                      [-103.2717, 72.2428, 423.2717, 247.7572],\n                      [72.2428, -103.2717, 247.7572, 423.2717]])\n    ]\n\n    base_anchors = anchor_generator.base_anchors\n    for i, base_anchor in enumerate(base_anchors):\n        assert base_anchor.allclose(expected_base_anchors[i])\n\n    # check valid flags\n    expected_valid_pixels = [2400, 600, 150, 54, 24, 6]\n    multi_level_valid_flags = anchor_generator.valid_flags(\n        featmap_sizes, (320, 320), device)\n    for i, single_level_valid_flag in enumerate(multi_level_valid_flags):\n        assert single_level_valid_flag.sum() == expected_valid_pixels[i]\n\n    # check number of base anchors for each level\n    assert anchor_generator.num_base_anchors == [6, 6, 6, 6, 6, 6]\n\n    # check anchor generation\n    anchors = anchor_generator.grid_anchors(featmap_sizes, device)\n    assert len(anchors) == 6\n\n    # test vgg ssd anchor setting\n    anchor_generator_cfg = dict(\n        type='SSDAnchorGenerator',\n        scale_major=False,\n        input_size=300,\n        basesize_ratio_range=(0.15, 0.9),\n        strides=[8, 16, 32, 64, 100, 300],\n        ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]])\n\n    featmap_sizes = [(38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1)]\n    anchor_generator = build_anchor_generator(anchor_generator_cfg)\n\n    # check base anchors\n    expected_base_anchors = [\n        torch.Tensor([[-6.5000, -6.5000, 14.5000, 14.5000],\n                      [-11.3704, -11.3704, 19.3704, 19.3704],\n                      [-10.8492, -3.4246, 18.8492, 11.4246],\n                      [-3.4246, -10.8492, 11.4246, 18.8492]]),\n        torch.Tensor([[-14.5000, -14.5000, 30.5000, 30.5000],\n                      [-25.3729, -25.3729, 41.3729, 41.3729],\n                      [-23.8198, -7.9099, 39.8198, 23.9099],\n                      [-7.9099, -23.8198, 23.9099, 39.8198],\n                      [-30.9711, -4.9904, 46.9711, 20.9904],\n                      [-4.9904, -30.9711, 20.9904, 46.9711]]),\n        torch.Tensor([[-33.5000, -33.5000, 65.5000, 65.5000],\n                      [-45.5366, -45.5366, 77.5366, 77.5366],\n                      [-54.0036, -19.0018, 86.0036, 51.0018],\n                      [-19.0018, -54.0036, 51.0018, 86.0036],\n                      [-69.7365, -12.5788, 101.7365, 44.5788],\n                      [-12.5788, -69.7365, 44.5788, 101.7365]]),\n        torch.Tensor([[-44.5000, -44.5000, 108.5000, 108.5000],\n                      [-56.9817, -56.9817, 120.9817, 120.9817],\n                      [-76.1873, -22.0937, 140.1873, 86.0937],\n                      [-22.0937, -76.1873, 86.0937, 140.1873],\n                      [-100.5019, -12.1673, 164.5019, 76.1673],\n                      [-12.1673, -100.5019, 76.1673, 164.5019]]),\n        torch.Tensor([[-53.5000, -53.5000, 153.5000, 153.5000],\n                      [-66.2185, -66.2185, 166.2185, 166.2185],\n                      [-96.3711, -23.1855, 196.3711, 123.1855],\n                      [-23.1855, -96.3711, 123.1855, 196.3711]]),\n        torch.Tensor([[19.5000, 19.5000, 280.5000, 280.5000],\n                      [6.6342, 6.6342, 293.3658, 293.3658],\n                      [-34.5549, 57.7226, 334.5549, 242.2774],\n                      [57.7226, -34.5549, 242.2774, 334.5549]]),\n    ]\n    base_anchors = anchor_generator.base_anchors\n    for i, base_anchor in enumerate(base_anchors):\n        assert base_anchor.allclose(expected_base_anchors[i])\n\n    # check valid flags\n    expected_valid_pixels = [5776, 2166, 600, 150, 36, 4]\n    multi_level_valid_flags = anchor_generator.valid_flags(\n        featmap_sizes, (300, 300), device)\n    for i, single_level_valid_flag in enumerate(multi_level_valid_flags):\n        assert single_level_valid_flag.sum() == expected_valid_pixels[i]\n\n    # check number of base anchors for each level\n    assert anchor_generator.num_base_anchors == [4, 6, 6, 6, 4, 4]\n\n    # check anchor generation\n    anchors = anchor_generator.grid_anchors(featmap_sizes, device)\n    assert len(anchors) == 6\n\n\ndef test_anchor_generator_with_tuples():\n    from mmdet.models.task_modules import build_anchor_generator\n    if torch.cuda.is_available():\n        device = 'cuda'\n    else:\n        device = 'cpu'\n\n    anchor_generator_cfg = dict(\n        type='SSDAnchorGenerator',\n        scale_major=False,\n        input_size=300,\n        basesize_ratio_range=(0.15, 0.9),\n        strides=[8, 16, 32, 64, 100, 300],\n        ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]])\n\n    featmap_sizes = [(38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1)]\n    anchor_generator = build_anchor_generator(anchor_generator_cfg)\n    anchors = anchor_generator.grid_anchors(featmap_sizes, device)\n\n    anchor_generator_cfg_tuples = dict(\n        type='SSDAnchorGenerator',\n        scale_major=False,\n        input_size=300,\n        basesize_ratio_range=(0.15, 0.9),\n        strides=[(8, 8), (16, 16), (32, 32), (64, 64), (100, 100), (300, 300)],\n        ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]])\n\n    anchor_generator_tuples = build_anchor_generator(\n        anchor_generator_cfg_tuples)\n    anchors_tuples = anchor_generator_tuples.grid_anchors(\n        featmap_sizes, device)\n    for anchor, anchor_tuples in zip(anchors, anchors_tuples):\n        assert torch.equal(anchor, anchor_tuples)\n\n\ndef test_yolo_anchor_generator():\n    from mmdet.models.task_modules import build_anchor_generator\n    if torch.cuda.is_available():\n        device = 'cuda'\n    else:\n        device = 'cpu'\n\n    anchor_generator_cfg = dict(\n        type='YOLOAnchorGenerator',\n        strides=[32, 16, 8],\n        base_sizes=[\n            [(116, 90), (156, 198), (373, 326)],\n            [(30, 61), (62, 45), (59, 119)],\n            [(10, 13), (16, 30), (33, 23)],\n        ])\n\n    featmap_sizes = [(14, 18), (28, 36), (56, 72)]\n    anchor_generator = build_anchor_generator(anchor_generator_cfg)\n\n    # check base anchors\n    expected_base_anchors = [\n        torch.Tensor([[-42.0000, -29.0000, 74.0000, 61.0000],\n                      [-62.0000, -83.0000, 94.0000, 115.0000],\n                      [-170.5000, -147.0000, 202.5000, 179.0000]]),\n        torch.Tensor([[-7.0000, -22.5000, 23.0000, 38.5000],\n                      [-23.0000, -14.5000, 39.0000, 30.5000],\n                      [-21.5000, -51.5000, 37.5000, 67.5000]]),\n        torch.Tensor([[-1.0000, -2.5000, 9.0000, 10.5000],\n                      [-4.0000, -11.0000, 12.0000, 19.0000],\n                      [-12.5000, -7.5000, 20.5000, 15.5000]])\n    ]\n    base_anchors = anchor_generator.base_anchors\n    for i, base_anchor in enumerate(base_anchors):\n        assert base_anchor.allclose(expected_base_anchors[i])\n\n    # check number of base anchors for each level\n    assert anchor_generator.num_base_anchors == [3, 3, 3]\n\n    # check anchor generation\n    anchors = anchor_generator.grid_anchors(featmap_sizes, device)\n    assert len(anchors) == 3\n\n\ndef test_retina_anchor():\n    from mmdet.registry import MODELS\n    if torch.cuda.is_available():\n        device = 'cuda'\n    else:\n        device = 'cpu'\n\n    # head configs modified from\n    # configs/nas_fpn/retinanet_r50_fpn_crop640_50e.py\n    bbox_head = dict(\n        type='RetinaSepBNHead',\n        num_classes=4,\n        num_ins=5,\n        in_channels=4,\n        stacked_convs=1,\n        feat_channels=4,\n        anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        bbox_coder=dict(\n            type='DeltaXYWHBBoxCoder',\n            target_means=[.0, .0, .0, .0],\n            target_stds=[1.0, 1.0, 1.0, 1.0]))\n\n    retina_head = MODELS.build(bbox_head)\n    assert retina_head.anchor_generator is not None\n\n    # use the featmap sizes in NASFPN setting to test retina head\n    featmap_sizes = [(80, 80), (40, 40), (20, 20), (10, 10), (5, 5)]\n    # check base anchors\n    expected_base_anchors = [\n        torch.Tensor([[-22.6274, -11.3137, 22.6274, 11.3137],\n                      [-28.5088, -14.2544, 28.5088, 14.2544],\n                      [-35.9188, -17.9594, 35.9188, 17.9594],\n                      [-16.0000, -16.0000, 16.0000, 16.0000],\n                      [-20.1587, -20.1587, 20.1587, 20.1587],\n                      [-25.3984, -25.3984, 25.3984, 25.3984],\n                      [-11.3137, -22.6274, 11.3137, 22.6274],\n                      [-14.2544, -28.5088, 14.2544, 28.5088],\n                      [-17.9594, -35.9188, 17.9594, 35.9188]]),\n        torch.Tensor([[-45.2548, -22.6274, 45.2548, 22.6274],\n                      [-57.0175, -28.5088, 57.0175, 28.5088],\n                      [-71.8376, -35.9188, 71.8376, 35.9188],\n                      [-32.0000, -32.0000, 32.0000, 32.0000],\n                      [-40.3175, -40.3175, 40.3175, 40.3175],\n                      [-50.7968, -50.7968, 50.7968, 50.7968],\n                      [-22.6274, -45.2548, 22.6274, 45.2548],\n                      [-28.5088, -57.0175, 28.5088, 57.0175],\n                      [-35.9188, -71.8376, 35.9188, 71.8376]]),\n        torch.Tensor([[-90.5097, -45.2548, 90.5097, 45.2548],\n                      [-114.0350, -57.0175, 114.0350, 57.0175],\n                      [-143.6751, -71.8376, 143.6751, 71.8376],\n                      [-64.0000, -64.0000, 64.0000, 64.0000],\n                      [-80.6349, -80.6349, 80.6349, 80.6349],\n                      [-101.5937, -101.5937, 101.5937, 101.5937],\n                      [-45.2548, -90.5097, 45.2548, 90.5097],\n                      [-57.0175, -114.0350, 57.0175, 114.0350],\n                      [-71.8376, -143.6751, 71.8376, 143.6751]]),\n        torch.Tensor([[-181.0193, -90.5097, 181.0193, 90.5097],\n                      [-228.0701, -114.0350, 228.0701, 114.0350],\n                      [-287.3503, -143.6751, 287.3503, 143.6751],\n                      [-128.0000, -128.0000, 128.0000, 128.0000],\n                      [-161.2699, -161.2699, 161.2699, 161.2699],\n                      [-203.1873, -203.1873, 203.1873, 203.1873],\n                      [-90.5097, -181.0193, 90.5097, 181.0193],\n                      [-114.0350, -228.0701, 114.0350, 228.0701],\n                      [-143.6751, -287.3503, 143.6751, 287.3503]]),\n        torch.Tensor([[-362.0387, -181.0193, 362.0387, 181.0193],\n                      [-456.1401, -228.0701, 456.1401, 228.0701],\n                      [-574.7006, -287.3503, 574.7006, 287.3503],\n                      [-256.0000, -256.0000, 256.0000, 256.0000],\n                      [-322.5398, -322.5398, 322.5398, 322.5398],\n                      [-406.3747, -406.3747, 406.3747, 406.3747],\n                      [-181.0193, -362.0387, 181.0193, 362.0387],\n                      [-228.0701, -456.1401, 228.0701, 456.1401],\n                      [-287.3503, -574.7006, 287.3503, 574.7006]])\n    ]\n    base_anchors = retina_head.anchor_generator.base_anchors\n    for i, base_anchor in enumerate(base_anchors):\n        assert base_anchor.allclose(expected_base_anchors[i])\n\n    # check valid flags\n    expected_valid_pixels = [57600, 14400, 3600, 900, 225]\n    multi_level_valid_flags = retina_head.anchor_generator.valid_flags(\n        featmap_sizes, (640, 640), device)\n    for i, single_level_valid_flag in enumerate(multi_level_valid_flags):\n        assert single_level_valid_flag.sum() == expected_valid_pixels[i]\n\n    # check number of base anchors for each level\n    assert retina_head.anchor_generator.num_base_anchors == [9, 9, 9, 9, 9]\n\n    # check anchor generation\n    anchors = retina_head.anchor_generator.grid_anchors(featmap_sizes, device)\n    assert len(anchors) == 5\n\n\ndef test_guided_anchor():\n    from mmdet.registry import MODELS\n    if torch.cuda.is_available():\n        device = 'cuda'\n    else:\n        device = 'cpu'\n    # head configs modified from\n    # configs/guided_anchoring/ga-retinanet_r50_fpn_1x_coco.py\n    bbox_head = dict(\n        type='GARetinaHead',\n        num_classes=8,\n        in_channels=4,\n        stacked_convs=1,\n        feat_channels=4,\n        approx_anchor_generator=dict(\n            type='AnchorGenerator',\n            octave_base_scale=4,\n            scales_per_octave=3,\n            ratios=[0.5, 1.0, 2.0],\n            strides=[8, 16, 32, 64, 128]),\n        square_anchor_generator=dict(\n            type='AnchorGenerator',\n            ratios=[1.0],\n            scales=[4],\n            strides=[8, 16, 32, 64, 128]))\n\n    ga_retina_head = MODELS.build(bbox_head)\n    assert ga_retina_head.approx_anchor_generator is not None\n\n    # use the featmap sizes in NASFPN setting to test ga_retina_head\n    featmap_sizes = [(100, 152), (50, 76), (25, 38), (13, 19), (7, 10)]\n    # check base anchors\n    expected_approxs = [\n        torch.Tensor([[-22.6274, -11.3137, 22.6274, 11.3137],\n                      [-28.5088, -14.2544, 28.5088, 14.2544],\n                      [-35.9188, -17.9594, 35.9188, 17.9594],\n                      [-16.0000, -16.0000, 16.0000, 16.0000],\n                      [-20.1587, -20.1587, 20.1587, 20.1587],\n                      [-25.3984, -25.3984, 25.3984, 25.3984],\n                      [-11.3137, -22.6274, 11.3137, 22.6274],\n                      [-14.2544, -28.5088, 14.2544, 28.5088],\n                      [-17.9594, -35.9188, 17.9594, 35.9188]]),\n        torch.Tensor([[-45.2548, -22.6274, 45.2548, 22.6274],\n                      [-57.0175, -28.5088, 57.0175, 28.5088],\n                      [-71.8376, -35.9188, 71.8376, 35.9188],\n                      [-32.0000, -32.0000, 32.0000, 32.0000],\n                      [-40.3175, -40.3175, 40.3175, 40.3175],\n                      [-50.7968, -50.7968, 50.7968, 50.7968],\n                      [-22.6274, -45.2548, 22.6274, 45.2548],\n                      [-28.5088, -57.0175, 28.5088, 57.0175],\n                      [-35.9188, -71.8376, 35.9188, 71.8376]]),\n        torch.Tensor([[-90.5097, -45.2548, 90.5097, 45.2548],\n                      [-114.0350, -57.0175, 114.0350, 57.0175],\n                      [-143.6751, -71.8376, 143.6751, 71.8376],\n                      [-64.0000, -64.0000, 64.0000, 64.0000],\n                      [-80.6349, -80.6349, 80.6349, 80.6349],\n                      [-101.5937, -101.5937, 101.5937, 101.5937],\n                      [-45.2548, -90.5097, 45.2548, 90.5097],\n                      [-57.0175, -114.0350, 57.0175, 114.0350],\n                      [-71.8376, -143.6751, 71.8376, 143.6751]]),\n        torch.Tensor([[-181.0193, -90.5097, 181.0193, 90.5097],\n                      [-228.0701, -114.0350, 228.0701, 114.0350],\n                      [-287.3503, -143.6751, 287.3503, 143.6751],\n                      [-128.0000, -128.0000, 128.0000, 128.0000],\n                      [-161.2699, -161.2699, 161.2699, 161.2699],\n                      [-203.1873, -203.1873, 203.1873, 203.1873],\n                      [-90.5097, -181.0193, 90.5097, 181.0193],\n                      [-114.0350, -228.0701, 114.0350, 228.0701],\n                      [-143.6751, -287.3503, 143.6751, 287.3503]]),\n        torch.Tensor([[-362.0387, -181.0193, 362.0387, 181.0193],\n                      [-456.1401, -228.0701, 456.1401, 228.0701],\n                      [-574.7006, -287.3503, 574.7006, 287.3503],\n                      [-256.0000, -256.0000, 256.0000, 256.0000],\n                      [-322.5398, -322.5398, 322.5398, 322.5398],\n                      [-406.3747, -406.3747, 406.3747, 406.3747],\n                      [-181.0193, -362.0387, 181.0193, 362.0387],\n                      [-228.0701, -456.1401, 228.0701, 456.1401],\n                      [-287.3503, -574.7006, 287.3503, 574.7006]])\n    ]\n    approxs = ga_retina_head.approx_anchor_generator.base_anchors\n    for i, base_anchor in enumerate(approxs):\n        assert base_anchor.allclose(expected_approxs[i])\n\n    # check valid flags\n    expected_valid_pixels = [136800, 34200, 8550, 2223, 630]\n    multi_level_valid_flags = ga_retina_head.approx_anchor_generator \\\n        .valid_flags(featmap_sizes, (800, 1216), device)\n    for i, single_level_valid_flag in enumerate(multi_level_valid_flags):\n        assert single_level_valid_flag.sum() == expected_valid_pixels[i]\n\n    # check number of base anchors for each level\n    assert ga_retina_head.approx_anchor_generator.num_base_anchors == [\n        9, 9, 9, 9, 9\n    ]\n\n    # check approx generation\n    squares = ga_retina_head.square_anchor_generator.grid_anchors(\n        featmap_sizes, device)\n    assert len(squares) == 5\n\n    expected_squares = [\n        torch.Tensor([[-16., -16., 16., 16.]]),\n        torch.Tensor([[-32., -32., 32., 32]]),\n        torch.Tensor([[-64., -64., 64., 64.]]),\n        torch.Tensor([[-128., -128., 128., 128.]]),\n        torch.Tensor([[-256., -256., 256., 256.]])\n    ]\n    squares = ga_retina_head.square_anchor_generator.base_anchors\n    for i, base_anchor in enumerate(squares):\n        assert base_anchor.allclose(expected_squares[i])\n\n    # square_anchor_generator does not check valid flags\n    # check number of base anchors for each level\n    assert (ga_retina_head.square_anchor_generator.num_base_anchors == [\n        1, 1, 1, 1, 1\n    ])\n\n    # check square generation\n    anchors = ga_retina_head.square_anchor_generator.grid_anchors(\n        featmap_sizes, device)\n    assert len(anchors) == 5\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_samplers/test_pesudo_sampler.py",
    "content": "# TODO: follow up\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_track/test_aflink.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nfrom mmengine.registry import init_default_scope\nfrom torch import nn\n\nfrom mmdet.registry import TASK_UTILS\n\n\nclass TestAppearanceFreeLink(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n        cls.cfg = dict(\n            type='AppearanceFreeLink',\n            checkpoint='',\n            temporal_threshold=(0, 30),\n            spatial_threshold=75,\n            confidence_threshold=0.95,\n        )\n\n    def test_init(self):\n        aflink = TASK_UTILS.build(self.cfg)\n        assert aflink.temporal_threshold == (0, 30)\n        assert aflink.spatial_threshold == 75\n        assert aflink.confidence_threshold == 0.95\n        assert isinstance(aflink.model, nn.Module)\n\n    def test_forward(self):\n        pred_track = np.random.randn(10, 7)\n        aflink = TASK_UTILS.build(self.cfg)\n        linked_track = aflink.forward(pred_track)\n        assert isinstance(linked_track, np.ndarray)\n        assert linked_track.shape == (10, 7)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_track/test_interpolation.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nfrom mmengine.registry import init_default_scope\n\nfrom mmdet.registry import TASK_UTILS\n\n\nclass TestInterpolateTracklets(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n        cls.cfg = dict(\n            type='InterpolateTracklets',\n            min_num_frames=5,\n            max_num_frames=20,\n            use_gsi=True,\n            smooth_tau=10)\n\n    def test_init(self):\n        interpolation = TASK_UTILS.build(self.cfg)\n        assert interpolation.min_num_frames == 5\n        assert interpolation.max_num_frames == 20\n        assert interpolation.use_gsi\n        assert interpolation.smooth_tau == 10\n\n    def test_forward(self):\n        pred_track = np.random.randn(5, 7)\n\n        # set frame_id and target_id\n        pred_track[:, 0] = np.array([1, 2, 5, 6, 7])\n        pred_track[:, 1] = 1\n\n        interpolation = TASK_UTILS.build(self.cfg)\n        linked_track = interpolation.forward(pred_track)\n        assert isinstance(linked_track, np.ndarray)\n        assert linked_track.shape == (5, 7)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_track/test_kalman_filter.py",
    "content": "from unittest import TestCase\n\nimport numpy as np\nfrom mmengine.registry import init_default_scope\n\nfrom mmdet.registry import TASK_UTILS\n\n\nclass TestKalmanFilter(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n        motion = dict(type='KalmanFilter', )\n        cls.kf = TASK_UTILS.build(motion)\n\n    def test_init(self):\n        pred_det = np.random.randn(4)\n        mean, covariance = self.kf.initiate(pred_det)\n        assert len(mean) == 8\n        assert covariance.shape == (8, 8)\n\n    def test_predict(self):\n        mean = np.random.randn(8)\n        covariance = np.random.randn(8, 8)\n        mean, covariance = self.kf.predict(mean, covariance)\n        assert len(mean) == 8\n        assert covariance.shape == (8, 8)\n\n    def test_update(self):\n        mean = np.ones(8)\n        covariance = np.ones((8, 8))\n        measurement = np.ones(4)\n        score = 0.1\n        mean, covariance = self.kf.update(mean, covariance, measurement, score)\n        assert len(mean) == 8\n        assert covariance.shape == (8, 8)\n"
  },
  {
    "path": "tests/test_models/test_task_modules/test_track/test_similarity.py",
    "content": "import torch\n\nfrom mmdet.models.task_modules import embed_similarity\n\n\ndef test_embed_similarity():\n    \"\"\"Test embed similarity.\"\"\"\n    embeds = torch.rand(2, 3)\n    similarity = embed_similarity(embeds, embeds)\n    assert similarity.shape == (2, 2)\n"
  },
  {
    "path": "tests/test_models/test_trackers/test_byte_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.testing import demo_track_inputs, random_boxes\nfrom mmdet.utils import register_all_modules\n\n\nclass TestByteTracker(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        register_all_modules(init_default_scope=True)\n        cfg = dict(\n            type='ByteTracker',\n            motion=dict(type='KalmanFilter'),\n            obj_score_thrs=dict(high=0.6, low=0.1),\n            init_track_thr=0.7,\n            weight_iou_with_det_scores=True,\n            match_iou_thrs=dict(high=0.1, low=0.5, tentative=0.3),\n            num_tentatives=3,\n            num_frames_retain=30)\n        cls.tracker = MODELS.build(cfg)\n        cls.tracker.kf = TASK_UTILS.build(dict(type='KalmanFilter'))\n        cls.num_frames_retain = cfg['num_frames_retain']\n        cls.num_objs = 30\n\n    def test_init(self):\n        bboxes = random_boxes(self.num_objs, 512)\n        labels = torch.zeros(self.num_objs)\n        scores = torch.ones(self.num_objs)\n        ids = torch.arange(self.num_objs)\n        self.tracker.update(\n            ids=ids, bboxes=bboxes, scores=scores, labels=labels, frame_ids=0)\n\n        assert self.tracker.ids == list(ids)\n        assert self.tracker.memo_items == [\n            'ids', 'bboxes', 'scores', 'labels', 'frame_ids'\n        ]\n\n    def test_track(self):\n\n        with torch.no_grad():\n            packed_inputs = demo_track_inputs(batch_size=1, num_frames=2)\n            track_data_sample = packed_inputs['data_samples'][0]\n            video_len = len(track_data_sample)\n            for frame_id in range(video_len):\n                img_data_sample = track_data_sample[frame_id]\n                img_data_sample.pred_instances = \\\n                    img_data_sample.gt_instances.clone()\n                # add fake scores\n                scores = torch.ones(len(img_data_sample.gt_instances.bboxes))\n                img_data_sample.pred_instances.scores = torch.FloatTensor(\n                    scores)\n\n                pred_track_instances = self.tracker.track(\n                    data_sample=img_data_sample)\n\n                bboxes = pred_track_instances.bboxes\n                labels = pred_track_instances.labels\n\n                assert bboxes.shape[1] == 4\n                assert bboxes.shape[0] == labels.shape[0]\n"
  },
  {
    "path": "tests/test_models/test_trackers/test_masktrack_rcnn_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg, random_boxes\n\n\nclass TestMaskTrackRCNNTracker(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n        tracker_cfg = dict(\n            type='MaskTrackRCNNTracker',\n            match_weights=dict(det_score=1.0, iou=2.0, det_label=10.0),\n            num_frames_retain=20)\n        cls.tracker = MODELS.build(tracker_cfg)\n        cls.num_objs = 5\n\n    def test_get_match_score(self):\n        bboxes = random_boxes(self.num_objs, 64)\n        labels = torch.arange(self.num_objs)\n        scores = torch.arange(self.num_objs, dtype=torch.float32)\n        similarity_logits = torch.randn(self.num_objs, self.num_objs + 1)\n\n        match_score = self.tracker.get_match_score(bboxes, labels, scores,\n                                                   bboxes, labels,\n                                                   similarity_logits)\n        assert match_score.size() == similarity_logits.size()\n\n    @parameterized.expand([\n        'masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py'  # noqa: E501\n    ])\n    def test_track(self, cfg_file):\n        _model = get_detector_cfg(cfg_file)\n        # _scope_ will be popped after build\n        model = MODELS.build(_model)\n\n        packed_inputs = demo_track_inputs(\n            batch_size=1, num_frames=2, with_mask=True)\n        track_data_sample = packed_inputs['data_samples'][0]\n        imgs = packed_inputs['inputs'][0]\n        video_len = len(track_data_sample)\n        for frame_id in range(video_len):\n            img_data_sample = track_data_sample[frame_id]\n            single_image = imgs[frame_id]\n            img_data_sample.pred_instances = \\\n                img_data_sample.gt_instances.clone()\n            # add fake scores\n            scores = torch.ones(len(img_data_sample.pred_instances.bboxes))\n            img_data_sample.pred_instances.scores = torch.FloatTensor(scores)\n            feats = []\n            for i in range(\n                    len(model.track_head.roi_extractor.featmap_strides)):\n                feats.append(\n                    torch.rand(1, 256, 256 // (2**(i + 2)),\n                               256 // (2**(i + 2))).to(device='cpu'))\n            pred_track_instances = self.tracker.track(\n                model=model,\n                img=single_image,\n                feats=tuple(feats),\n                data_sample=img_data_sample)\n\n            bboxes = pred_track_instances.bboxes\n            labels = pred_track_instances.labels\n            ids = pred_track_instances.instances_id\n\n            assert bboxes.shape[1] == 4\n            assert bboxes.shape[0] == labels.shape[0]\n            assert bboxes.shape[0] == ids.shape[0]\n"
  },
  {
    "path": "tests/test_models/test_trackers/test_oc_sort_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.testing import demo_track_inputs\nfrom mmdet.utils import register_all_modules\n\n\nclass TestByteTracker(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        register_all_modules(init_default_scope=True)\n        cfg = dict(\n            type='OCSORTTracker',\n            motion=dict(type='KalmanFilter'),\n            obj_score_thr=0.3,\n            init_track_thr=0.7,\n            weight_iou_with_det_scores=True,\n            match_iou_thr=0.3,\n            num_tentatives=3,\n            vel_consist_weight=0.2,\n            vel_delta_t=3,\n            num_frames_retain=30)\n        cls.tracker = MODELS.build(cfg)\n        cls.tracker.kf = TASK_UTILS.build(dict(type='KalmanFilter'))\n        cls.num_frames_retain = cfg['num_frames_retain']\n        cls.num_objs = 30\n\n    def test_track(self):\n\n        with torch.no_grad():\n            packed_inputs = demo_track_inputs(batch_size=1, num_frames=2)\n            track_data_sample = packed_inputs['data_samples'][0]\n            video_len = len(track_data_sample)\n            for frame_id in range(video_len):\n                img_data_sample = track_data_sample[frame_id]\n                img_data_sample.pred_instances = \\\n                    img_data_sample.gt_instances.clone()\n                # add fake scores\n                scores = torch.ones(len(img_data_sample.gt_instances.bboxes))\n                img_data_sample.pred_instances.scores = torch.FloatTensor(\n                    scores)\n\n                pred_track_instances = self.tracker.track(\n                    data_sample=img_data_sample)\n\n                bboxes = pred_track_instances.bboxes\n                labels = pred_track_instances.labels\n\n                assert bboxes.shape[1] == 4\n                assert bboxes.shape[0] == labels.shape[0]\n"
  },
  {
    "path": "tests/test_models/test_trackers/test_sort_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\nfrom unittest.mock import MagicMock\n\nimport torch\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg, random_boxes\nfrom mmdet.utils import register_all_modules\n\n\nclass TestSORTTracker(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        register_all_modules(init_default_scope=True)\n        cls.num_objs = 30\n\n    @parameterized.expand([\n        'deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e'\n        '_mot17halftrain_test-mot17halfval.py'\n    ])\n    def test_init(self, cfg_file):\n        cfg = get_detector_cfg(cfg_file)\n        tracker = MODELS.build(cfg['tracker'])\n        tracker.kf = TASK_UTILS.build(cfg['tracker']['motion'])\n\n        bboxes = random_boxes(self.num_objs, 512)\n        labels = torch.zeros(self.num_objs)\n        scores = torch.ones(self.num_objs)\n        ids = torch.arange(self.num_objs)\n        tracker.update(\n            ids=ids, bboxes=bboxes, scores=scores, labels=labels, frame_ids=0)\n\n        assert tracker.ids == list(ids)\n        assert tracker.memo_items == [\n            'ids', 'bboxes', 'scores', 'labels', 'frame_ids'\n        ]\n\n    @parameterized.expand([\n        'deepsort/deepsort_faster-rcnn_r50_fpn_8xb2-4e'\n        '_mot17halftrain_test-mot17halfval.py'\n    ])\n    def test_track(self, cfg_file):\n        imgs = torch.rand((1, 2, 3, 128, 128))\n\n        cfg = get_detector_cfg(cfg_file)\n        tracker = MODELS.build(cfg['tracker'])\n        tracker.kf = TASK_UTILS.build(cfg['tracker']['motion'])\n\n        model = MagicMock()\n        model.reid = MODELS.build(cfg['reid'])\n\n        with torch.no_grad():\n            packed_inputs = demo_track_inputs(batch_size=1, num_frames=2)\n            track_data_sample = packed_inputs['data_samples'][0]\n            video_len = len(track_data_sample)\n            for frame_id in range(video_len):\n                img_data_sample = track_data_sample[frame_id]\n                single_img = imgs[:, frame_id]\n                img_data_sample.pred_instances = \\\n                    img_data_sample.gt_instances.clone()\n                # add fake scores\n                scores = torch.ones(len(img_data_sample.gt_instances.bboxes))\n                img_data_sample.pred_instances.scores = torch.FloatTensor(\n                    scores)\n\n                pred_track_instances = tracker.track(\n                    model=model,\n                    img=single_img,\n                    feats=None,\n                    data_sample=img_data_sample,\n                    data_preprocessor=cfg['data_preprocessor'])\n\n                bboxes = pred_track_instances.bboxes\n                labels = pred_track_instances.labels\n                ids = pred_track_instances.instances_id\n\n                assert bboxes.shape[1] == 4\n                assert bboxes.shape[0] == labels.shape[0]\n                assert bboxes.shape[0] == ids.shape[0]\n"
  },
  {
    "path": "tests/test_models/test_trackers/test_strong_sort_tracker.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\nfrom unittest.mock import MagicMock\n\nimport torch\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS, TASK_UTILS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg, random_boxes\n\n\nclass TestStrongSORTTracker(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n        cls.num_objs = 30\n\n    @parameterized.expand([\n        'strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain'\n        '_test-mot17halfval.py'\n    ])\n    def test_init(self, cfg_file):\n        cfg = get_detector_cfg(cfg_file)\n        tracker = MODELS.build(cfg['tracker'])\n        tracker.kf = TASK_UTILS.build(cfg['tracker']['motion'])\n        tracker.cmc = TASK_UTILS.build(cfg['cmc'])\n\n        bboxes = random_boxes(self.num_objs, 512)\n        labels = torch.zeros(self.num_objs)\n        scores = torch.ones(self.num_objs)\n        ids = torch.arange(self.num_objs)\n        tracker.update(\n            ids=ids, bboxes=bboxes, scores=scores, labels=labels, frame_ids=0)\n\n        assert tracker.ids == list(ids)\n        assert tracker.memo_items == [\n            'ids', 'bboxes', 'scores', 'labels', 'frame_ids'\n        ]\n\n    @parameterized.expand([\n        'strongsort/strongsort_yolox_x_8xb4-80e_crowdhuman-mot17halftrain'\n        '_test-mot17halfval.py'\n    ])\n    def test_track(self, cfg_file):\n        img = torch.rand((1, 3, 128, 128))\n\n        cfg = get_detector_cfg(cfg_file)\n        tracker = MODELS.build(cfg['tracker'])\n        tracker.kf = TASK_UTILS.build(cfg['tracker']['motion'])\n\n        model = MagicMock()\n        model.reid = MODELS.build(cfg['reid'])\n        model.cmc = TASK_UTILS.build(cfg['cmc'])\n\n        with torch.no_grad():\n            packed_inputs = demo_track_inputs(batch_size=1, num_frames=2)\n            track_data_sample = packed_inputs['data_samples'][0]\n            video_len = len(track_data_sample)\n            for frame_id in range(video_len):\n                img_data_sample = track_data_sample[frame_id]\n                img_data_sample.pred_instances = \\\n                    img_data_sample.gt_instances.clone()\n                # add fake scores\n                scores = torch.ones(len(img_data_sample.gt_instances.bboxes))\n                img_data_sample.pred_instances.scores = torch.FloatTensor(\n                    scores)\n\n                pred_track_instances = tracker.track(\n                    model=model,\n                    img=img,\n                    data_sample=img_data_sample,\n                    data_preprocessor=cfg['data_preprocessor'])\n\n                bboxes = pred_track_instances.bboxes\n                labels = pred_track_instances.labels\n\n                assert bboxes.shape[1] == 4\n                assert bboxes.shape[0] == labels.shape[0]\n"
  },
  {
    "path": "tests/test_models/test_tracking_heads/test_mask2former_track_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import Config\n\nfrom mmdet.models.tracking_heads import Mask2FormerTrackHead\nfrom mmdet.structures import DetDataSample, TrackDataSample\nfrom mmdet.testing import demo_track_inputs\n\n\nclass TestMask2FormerHead(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        cls.config = Config(\n            dict(\n                in_channels=[256, 512, 1024,\n                             2048],  # pass to pixel_decoder inside\n                strides=[4, 8, 16, 32],\n                feat_channels=256,\n                out_channels=256,\n                num_classes=40,\n                num_frames=2,\n                num_queries=100,\n                num_transformer_feat_level=3,\n                pixel_decoder=dict(\n                    type='MSDeformAttnPixelDecoder',\n                    num_outs=3,\n                    norm_cfg=dict(type='GN', num_groups=32),\n                    act_cfg=dict(type='ReLU'),\n                    encoder=dict(\n                        num_layers=6,\n                        layer_cfg=dict(\n                            self_attn_cfg=dict(\n                                embed_dims=256,\n                                num_heads=8,\n                                num_levels=3,\n                                num_points=4,\n                                im2col_step=128,\n                                dropout=0.0,\n                                batch_first=True),\n                            ffn_cfg=dict(\n                                embed_dims=256,\n                                feedforward_channels=1024,\n                                num_fcs=2,\n                                ffn_drop=0.0,\n                                act_cfg=dict(type='ReLU', inplace=True)))),\n                    positional_encoding=dict(num_feats=128, normalize=True)),\n                enforce_decoder_input_project=False,\n                positional_encoding=dict(\n                    type='SinePositionalEncoding3D',\n                    num_feats=128,\n                    normalize=True),\n                transformer_decoder=dict(  # Mask2FormerTransformerDecoder\n                    return_intermediate=True,\n                    num_layers=9,\n                    layer_cfg=dict(  # Mask2FormerTransformerDecoderLayer\n                        self_attn_cfg=dict(  # MultiheadAttention\n                            embed_dims=256,\n                            num_heads=8,\n                            dropout=0.0,\n                            batch_first=True),\n                        cross_attn_cfg=dict(  # MultiheadAttention\n                            embed_dims=256,\n                            num_heads=8,\n                            dropout=0.0,\n                            batch_first=True),\n                        ffn_cfg=dict(\n                            embed_dims=256,\n                            feedforward_channels=2048,\n                            num_fcs=2,\n                            ffn_drop=0.0,\n                            act_cfg=dict(type='ReLU', inplace=True))),\n                    init_cfg=None),\n                loss_cls=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=False,\n                    loss_weight=2.0,\n                    reduction='mean',\n                    class_weight=[1.0] * 40 + [0.1]),\n                loss_mask=dict(\n                    type='CrossEntropyLoss',\n                    use_sigmoid=True,\n                    reduction='mean',\n                    loss_weight=5.0),\n                loss_dice=dict(\n                    type='DiceLoss',\n                    use_sigmoid=True,\n                    activate=True,\n                    reduction='mean',\n                    naive_dice=True,\n                    eps=1.0,\n                    loss_weight=5.0),\n                train_cfg=dict(\n                    num_points=12544,\n                    oversample_ratio=3.0,\n                    importance_sample_ratio=0.75,\n                    assigner=dict(\n                        type='mmdet.HungarianAssigner',\n                        match_costs=[\n                            dict(type='mmdet.ClassificationCost', weight=2.0),\n                            dict(\n                                type='mmdet.CrossEntropyLossCost',\n                                weight=5.0,\n                                use_sigmoid=True),\n                            dict(\n                                type='mmdet.DiceCost',\n                                weight=5.0,\n                                pred_act=True,\n                                eps=1.0)\n                        ]),\n                    sampler=dict(type='mmdet.MaskPseudoSampler'))))\n\n    def test_mask2former_head_loss(self):\n        mask2former_head = Mask2FormerTrackHead(**self.config)\n        mask2former_head.init_weights()\n        s = 256\n        feats = [\n            torch.rand(2, 256 * (2**i), s // stride, s // stride)\n            for i, stride in enumerate([8, 16, 32, 64])\n        ]\n        packed_inputs = demo_track_inputs(\n            batch_size=1,\n            num_frames=2,\n            key_frames_inds=[0],\n            image_shapes=[(3, s, s)],\n            num_classes=2,\n            with_mask=True)\n        data_sample = packed_inputs['data_samples'][0]\n        loss = mask2former_head.loss(feats, [data_sample])\n        # loss_cls, loss_mask and loss_dice\n        assert len(loss) == 30\n\n    def test_mask2former_head_predict(self):\n        mask2former_head = Mask2FormerTrackHead(**self.config)\n        mask2former_head.training = False\n        mask2former_head.init_weights()\n        s = 256\n        # assume the video has 30 frames\n        feats = [\n            torch.rand(30, 256 * (2**i), s // stride, s // stride)\n            for i, stride in enumerate([8, 16, 32, 64])\n        ]\n\n        img_metas = dict(\n            img_shape=(s, s),\n            ori_shape=(s, s),\n            scale_factor=(1, 1),\n            pad_shape=(s, s),\n            batch_input_shape=(s, s))\n\n        img_data_samples = [\n            DetDataSample(metainfo=img_metas) for _ in range(30)\n        ]\n        data_sample = TrackDataSample()\n        data_sample.video_data_samples = img_data_samples\n        results = mask2former_head.predict(feats, data_sample)\n\n        assert len(results) == 30\n"
  },
  {
    "path": "tests/test_models/test_tracking_heads/test_quasi_dense_embed_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport mmengine\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.tracking_heads import QuasiDenseEmbedHead\nfrom mmdet.registry import TASK_UTILS\n\n\ndef _dummy_bbox_sampling(rpn_results_list, batch_gt_instances):\n    \"\"\"Create sample results that can be passed to Head.get_targets.\"\"\"\n    num_imgs = len(rpn_results_list)\n    feat = torch.rand(1, 1, 3, 3)\n    assign_config = dict(\n        type='MaxIoUAssigner',\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n        min_pos_iou=0.5,\n        ignore_iof_thr=-1)\n    sampler_config = dict(\n        type='CombinedSampler',\n        num=4,\n        pos_fraction=0.5,\n        neg_pos_ub=3,\n        add_gt_as_proposals=True,\n        pos_sampler=dict(type='InstanceBalancedPosSampler'),\n        neg_sampler=dict(type='RandomSampler'))\n    bbox_assigner = TASK_UTILS.build(assign_config)\n    bbox_sampler = TASK_UTILS.build(sampler_config)\n\n    sampling_results = []\n    for i in range(num_imgs):\n        assign_result = bbox_assigner.assign(rpn_results_list[i],\n                                             batch_gt_instances[i])\n        sampling_result = bbox_sampler.sample(\n            assign_result,\n            rpn_results_list[i],\n            batch_gt_instances[i],\n            feats=feat)\n        sampling_results.append(sampling_result)\n\n    return sampling_results\n\n\nclass TestQuasiDenseEmbedHead(TestCase):\n\n    def test_quasi_dense_embed_head_loss(self):\n        cfg = mmengine.Config(\n            dict(\n                num_convs=4,\n                num_fcs=1,\n                embed_channels=256,\n                norm_cfg=dict(type='GN', num_groups=32),\n                loss_track=dict(\n                    type='MultiPosCrossEntropyLoss', loss_weight=0.25),\n                loss_track_aux=dict(\n                    type='MarginL2Loss',\n                    neg_pos_ub=3,\n                    pos_margin=0,\n                    neg_margin=0.1,\n                    hard_mining=True,\n                    loss_weight=1.0)))\n\n        embed_head = QuasiDenseEmbedHead(**cfg)\n\n        key_feats = torch.rand(2, 256, 7, 7)\n        ref_feats = key_feats\n        rpn_results = InstanceData()\n        rpn_results.labels = torch.LongTensor([1, 2])\n        rpn_results.priors = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874],\n             [23.6667, 23.8757, 238.6326, 151.8874]])\n        rpn_results_list = [rpn_results]\n\n        gt_instance = InstanceData()\n        gt_instance.labels = torch.LongTensor([1, 2])\n        gt_instance.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874],\n             [23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instance.instances_id = torch.LongTensor([1, 2])\n        batch_gt_instances = [gt_instance]\n\n        sampling_results = _dummy_bbox_sampling(rpn_results_list,\n                                                batch_gt_instances)\n        gt_match_indices_list = [torch.Tensor([0, 1])]\n        loss_track = embed_head.loss(key_feats, ref_feats, sampling_results,\n                                     sampling_results, gt_match_indices_list)\n        assert loss_track['loss_track'] >= 0, 'track loss should be zero'\n        assert loss_track['loss_track_aux'] > 0, 'aux loss should be non-zero'\n\n    def test_quasi_dense_embed_head_predict(self):\n        cfg = mmengine.Config(\n            dict(\n                num_convs=4,\n                num_fcs=1,\n                embed_channels=256,\n                norm_cfg=dict(type='GN', num_groups=32),\n                loss_track=dict(\n                    type='MultiPosCrossEntropyLoss', loss_weight=0.25),\n                loss_track_aux=dict(\n                    type='MarginL2Loss',\n                    neg_pos_ub=3,\n                    pos_margin=0,\n                    neg_margin=0.1,\n                    hard_mining=True,\n                    loss_weight=1.0)))\n\n        embed_head = QuasiDenseEmbedHead(**cfg)\n\n        key_feats = torch.rand(2, 256, 7, 7)\n        track_feats = embed_head.predict(key_feats)\n\n        assert isinstance(track_feats, torch.Tensor)\n        assert track_feats.size() == (2, 256)\n"
  },
  {
    "path": "tests/test_models/test_tracking_heads/test_quasi_dense_track_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import init_default_scope\nfrom mmengine.config import Config\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, random_boxes\n\n\ndef _fake_proposals(img_metas, proposal_len):\n    \"\"\"Create a fake proposal list.\"\"\"\n    results = []\n    for i in range(len(img_metas)):\n        result = InstanceData(metainfo=img_metas[i])\n        proposal = random_boxes(proposal_len, 10).to(device='cpu')\n        result.bboxes = proposal\n        results.append(result)\n    return results\n\n\nclass TestQuasiDenseTrackHead(TestCase):\n\n    def setUp(self):\n        init_default_scope('mmdet')\n        cfg = Config(\n            dict(\n                type='QuasiDenseTrackHead',\n                roi_extractor=dict(\n                    type='SingleRoIExtractor',\n                    roi_layer=dict(\n                        type='RoIAlign', output_size=7, sampling_ratio=0),\n                    out_channels=256,\n                    featmap_strides=[4, 8, 16, 32]),\n                embed_head=dict(\n                    type='QuasiDenseEmbedHead',\n                    num_convs=4,\n                    num_fcs=1,\n                    embed_channels=256,\n                    norm_cfg=dict(type='GN', num_groups=32),\n                    loss_track=dict(\n                        type='MultiPosCrossEntropyLoss', loss_weight=0.25),\n                    loss_track_aux=dict(\n                        type='MarginL2Loss',\n                        neg_pos_ub=3,\n                        pos_margin=0,\n                        neg_margin=0.1,\n                        hard_mining=True,\n                        loss_weight=1.0)),\n                loss_bbox=dict(type='L1Loss', loss_weight=1.0),\n                train_cfg=dict(\n                    assigner=dict(\n                        type='MaxIoUAssigner',\n                        pos_iou_thr=0.7,\n                        neg_iou_thr=0.5,\n                        min_pos_iou=0.5,\n                        match_low_quality=False,\n                        ignore_iof_thr=-1),\n                    sampler=dict(\n                        type='CombinedSampler',\n                        num=256,\n                        pos_fraction=0.5,\n                        neg_pos_ub=3,\n                        add_gt_as_proposals=True,\n                        pos_sampler=dict(type='InstanceBalancedPosSampler'),\n                        neg_sampler=dict(type='RandomSampler')))))\n        self.track_head = MODELS.build(cfg)\n\n    def test_quasi_dense_track_head_loss(self):\n        packed_inputs = demo_track_inputs(\n            batch_size=1,\n            num_frames=2,\n            key_frames_inds=[0],\n            image_shapes=[(3, 256, 256)])\n        img_metas = [{\n            'img_shape': (256, 256, 3),\n            'scale_factor': 1,\n        }]\n        proposal_list = _fake_proposals(img_metas, 10)\n        feats = []\n        for i in range(len(self.track_head.roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, 256 // (2**(i + 2)),\n                           256 // (2**(i + 2))).to(device='cpu'))\n        key_feats = tuple(feats)\n        ref_feats = key_feats\n        loss_track = self.track_head.loss(key_feats, ref_feats, proposal_list,\n                                          proposal_list,\n                                          [packed_inputs['data_samples'][0]])\n        assert loss_track['loss_track'] >= 0, 'track loss should be zero'\n        assert loss_track['loss_track_aux'] > 0, 'aux loss should be non-zero'\n\n    def test_quasi_dense_track_head_predict(self):\n        feats = []\n        for i in range(len(self.track_head.roi_extractor.featmap_strides)):\n            feats.append(\n                torch.rand(1, 256, 256 // (2**(i + 2)),\n                           256 // (2**(i + 2))).to(device='cpu'))\n        feats = tuple(feats)\n        track_feat = self.track_head.predict(\n            feats, [torch.Tensor([[10, 10, 20, 20]])])\n        assert track_feat.size() == (1, 256)\n"
  },
  {
    "path": "tests/test_models/test_tracking_heads/test_roi_embed_head.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport mmengine\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.tracking_heads import RoIEmbedHead\nfrom mmdet.registry import TASK_UTILS\n\n\ndef _dummy_bbox_sampling(rpn_results_list, batch_gt_instances):\n    \"\"\"Create sample results that can be passed to Head.get_targets.\"\"\"\n    num_imgs = len(rpn_results_list)\n    feat = torch.rand(1, 1, 3, 3)\n    assign_config = dict(\n        type='MaxIoUAssigner',\n        pos_iou_thr=0.5,\n        neg_iou_thr=0.5,\n        min_pos_iou=0.5,\n        ignore_iof_thr=-1)\n    sampler_config = dict(\n        type='RandomSampler',\n        num=512,\n        pos_fraction=0.25,\n        neg_pos_ub=-1,\n        add_gt_as_proposals=False)\n    bbox_assigner = TASK_UTILS.build(assign_config)\n    bbox_sampler = TASK_UTILS.build(sampler_config)\n\n    sampling_results = []\n    for i in range(num_imgs):\n        assign_result = bbox_assigner.assign(rpn_results_list[i],\n                                             batch_gt_instances[i])\n        sampling_result = bbox_sampler.sample(\n            assign_result,\n            rpn_results_list[i],\n            batch_gt_instances[i],\n            feats=feat)\n        sampling_results.append(sampling_result)\n\n    return sampling_results\n\n\nclass TestRoIEmbedHead(TestCase):\n\n    def test_roi_embed_head_loss(self):\n        \"\"\"Test roi embed head loss when truth is non-empty.\"\"\"\n        cfg = mmengine.Config(\n            dict(\n                num_convs=2,\n                num_fcs=2,\n                roi_feat_size=7,\n                in_channels=16,\n                fc_out_channels=32))\n\n        embed_head = RoIEmbedHead(**cfg)\n\n        x = torch.rand(1, 16, 7, 7)\n        ref_x = torch.rand(1, 16, 7, 7)\n        num_x_per_img = [1]\n        num_x_per_ref_img = [1]\n        x_split, ref_x_split = embed_head.forward(x, ref_x, num_x_per_img,\n                                                  num_x_per_ref_img)\n\n        gt_instance_ids = [torch.LongTensor([2])]\n        ref_gt_instance_ids = [torch.LongTensor([2])]\n\n        rpn_results = InstanceData()\n        rpn_results.labels = torch.LongTensor([2])\n        rpn_results.priors = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        rpn_results_list = [rpn_results]\n\n        gt_instance = InstanceData()\n        gt_instance.labels = torch.LongTensor([2])\n        gt_instance.bboxes = torch.Tensor(\n            [[23.6667, 23.8757, 238.6326, 151.8874]])\n        gt_instance.instances_id = torch.LongTensor([2])\n        batch_gt_instances = [gt_instance]\n\n        sampling_results = _dummy_bbox_sampling(rpn_results_list,\n                                                batch_gt_instances)\n\n        gt_losses = embed_head.loss_by_feat(x_split, ref_x_split,\n                                            sampling_results, gt_instance_ids,\n                                            ref_gt_instance_ids)\n        assert gt_losses['loss_match'] > 0, 'match loss should be non-zero'\n        assert gt_losses[\n            'match_accuracy'] >= 0, 'match accuracy should be non-zero or zero'\n\n    def test_roi_embed_head_predict(self):\n        cfg = mmengine.Config(\n            dict(\n                num_convs=2,\n                num_fcs=2,\n                roi_feat_size=7,\n                in_channels=16,\n                fc_out_channels=32))\n\n        embed_head = RoIEmbedHead(**cfg)\n\n        x = torch.rand(1, 16, 7, 7)\n        ref_x = torch.rand(1, 16, 7, 7)\n        similarity_logits = embed_head.predict(x, ref_x)\n\n        assert isinstance(similarity_logits, list)\n        assert len(similarity_logits) == 1\n"
  },
  {
    "path": "tests/test_models/test_tta/test_det_tta.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine import ConfigDict\n\nfrom mmdet.models import DetTTAModel\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.testing import get_detector_cfg\nfrom mmdet.utils import register_all_modules\n\n\nclass TestDetTTAModel(TestCase):\n\n    def setUp(self):\n        register_all_modules()\n\n    def test_det_tta_model(self):\n\n        detector_cfg = get_detector_cfg(\n            'retinanet/retinanet_r18_fpn_1x_coco.py')\n        cfg = ConfigDict(\n            type='DetTTAModel',\n            module=detector_cfg,\n            tta_cfg=dict(\n                nms=dict(type='nms', iou_threshold=0.5), max_per_img=100))\n\n        model: DetTTAModel = MODELS.build(cfg)\n\n        imgs = []\n        data_samples = []\n        directions = ['horizontal', 'vertical']\n        for i in range(12):\n            flip_direction = directions[0] if i % 3 == 0 else directions[1]\n            imgs.append(torch.randn(1, 3, 100 + 10 * i, 100 + 10 * i))\n            data_samples.append([\n                DetDataSample(\n                    metainfo=dict(\n                        ori_shape=(100, 100),\n                        img_shape=(100 + 10 * i, 100 + 10 * i),\n                        scale_factor=((100 + 10 * i) / 100,\n                                      (100 + 10 * i) / 100),\n                        flip=(i % 2 == 0),\n                        flip_direction=flip_direction), )\n            ])\n\n        model.test_step(dict(inputs=imgs, data_samples=data_samples))\n"
  },
  {
    "path": "tests/test_models/test_utils/test_misc.py",
    "content": "import copy\n\nimport pytest\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.models.utils import (empty_instances, filter_gt_instances,\n                                rename_loss_dict, reweight_loss_dict,\n                                unpack_gt_instances)\nfrom mmdet.testing import demo_mm_inputs\n\n\ndef test_parse_gt_instance_info():\n    packed_inputs = demo_mm_inputs()['data_samples']\n    batch_gt_instances, batch_gt_instances_ignore, batch_img_metas \\\n        = unpack_gt_instances(packed_inputs)\n    assert len(batch_gt_instances) == len(packed_inputs)\n    assert len(batch_gt_instances_ignore) == len(packed_inputs)\n    assert len(batch_img_metas) == len(packed_inputs)\n\n\ndef test_process_empty_roi():\n    batch_size = 2\n    batch_img_metas = [{'ori_shape': (10, 12)}] * batch_size\n    device = torch.device('cpu')\n\n    results_list = empty_instances(batch_img_metas, device, task_type='bbox')\n    assert len(results_list) == batch_size\n    for results in results_list:\n        assert isinstance(results, InstanceData)\n        assert len(results) == 0\n        assert torch.allclose(results.bboxes, torch.zeros(0, 4, device=device))\n\n    results_list = empty_instances(\n        batch_img_metas,\n        device,\n        task_type='mask',\n        instance_results=results_list,\n        mask_thr_binary=0.5)\n    assert len(results_list) == batch_size\n    for results in results_list:\n        assert isinstance(results, InstanceData)\n        assert len(results) == 0\n        assert results.masks.shape == (0, 10, 12)\n\n    # batch_img_metas and instance_results length must be the same\n    with pytest.raises(AssertionError):\n        empty_instances(\n            batch_img_metas,\n            device,\n            task_type='mask',\n            instance_results=[results_list[0]] * 3)\n\n\ndef test_filter_gt_instances():\n    packed_inputs = demo_mm_inputs()['data_samples']\n    score_thr = 0.7\n    with pytest.raises(AssertionError):\n        filter_gt_instances(packed_inputs, score_thr=score_thr)\n\n    # filter no instances by score\n    for inputs in packed_inputs:\n        inputs.gt_instances.scores = torch.ones_like(\n            inputs.gt_instances.labels).float()\n    filtered_packed_inputs = filter_gt_instances(\n        copy.deepcopy(packed_inputs), score_thr=score_thr)\n    for filtered_inputs, inputs in zip(filtered_packed_inputs, packed_inputs):\n        assert len(filtered_inputs.gt_instances) == len(inputs.gt_instances)\n\n    # filter all instances\n    for inputs in packed_inputs:\n        inputs.gt_instances.scores = torch.zeros_like(\n            inputs.gt_instances.labels).float()\n    filtered_packed_inputs = filter_gt_instances(\n        copy.deepcopy(packed_inputs), score_thr=score_thr)\n    for filtered_inputs in filtered_packed_inputs:\n        assert len(filtered_inputs.gt_instances) == 0\n\n    packed_inputs = demo_mm_inputs()['data_samples']\n    # filter no instances by size\n    wh_thr = (0, 0)\n    filtered_packed_inputs = filter_gt_instances(\n        copy.deepcopy(packed_inputs), wh_thr=wh_thr)\n    for filtered_inputs, inputs in zip(filtered_packed_inputs, packed_inputs):\n        assert len(filtered_inputs.gt_instances) == len(inputs.gt_instances)\n\n    # filter all instances by size\n    for inputs in packed_inputs:\n        img_shape = inputs.img_shape\n        wh_thr = (max(wh_thr[0], img_shape[0]), max(wh_thr[1], img_shape[1]))\n    filtered_packed_inputs = filter_gt_instances(\n        copy.deepcopy(packed_inputs), wh_thr=wh_thr)\n    for filtered_inputs in filtered_packed_inputs:\n        assert len(filtered_inputs.gt_instances) == 0\n\n\ndef test_rename_loss_dict():\n    prefix = 'sup_'\n    losses = {'cls_loss': torch.tensor(2.), 'reg_loss': torch.tensor(1.)}\n    sup_losses = rename_loss_dict(prefix, losses)\n    for name in losses.keys():\n        assert sup_losses[prefix + name] == losses[name]\n\n\ndef test_reweight_loss_dict():\n    weight = 4\n    losses = {'cls_loss': torch.tensor(2.), 'reg_loss': torch.tensor(1.)}\n    weighted_losses = reweight_loss_dict(copy.deepcopy(losses), weight)\n    for name in losses.keys():\n        assert weighted_losses[name] == losses[name] * weight\n"
  },
  {
    "path": "tests/test_models/test_utils/test_model_misc.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\nimport torch\nfrom torch.autograd import gradcheck\n\nfrom mmdet.models.utils import interpolate_as, sigmoid_geometric_mean\n\n\ndef test_interpolate_as():\n    source = torch.rand((1, 5, 4, 4))\n    target = torch.rand((1, 1, 16, 16))\n\n    # Test 4D source and target\n    result = interpolate_as(source, target)\n    assert result.shape == torch.Size((1, 5, 16, 16))\n\n    # Test 3D target\n    result = interpolate_as(source, target.squeeze(0))\n    assert result.shape == torch.Size((1, 5, 16, 16))\n\n    # Test 3D source\n    result = interpolate_as(source.squeeze(0), target)\n    assert result.shape == torch.Size((5, 16, 16))\n\n    # Test type(target) == np.ndarray\n    target = np.random.rand(16, 16)\n    result = interpolate_as(source.squeeze(0), target)\n    assert result.shape == torch.Size((5, 16, 16))\n\n\ndef test_sigmoid_geometric_mean():\n    x = torch.randn(20, 20, dtype=torch.double, requires_grad=True)\n    y = torch.randn(20, 20, dtype=torch.double, requires_grad=True)\n    inputs = (x, y)\n    test = gradcheck(sigmoid_geometric_mean, inputs, eps=1e-6, atol=1e-4)\n    assert test\n"
  },
  {
    "path": "tests/test_models/test_vis/test_mask2former.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg\n\n\nclass TestMask2Former(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py',\n    ])\n    def test_mask2former_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n\n        model = MODELS.build(model)\n        assert model.backbone\n        assert model.track_head\n\n    @parameterized.expand([\n        ('mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py',\n         ('cpu', 'cuda')),\n    ])\n    def test_mask2former_forward_loss_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_mask2former_forward_loss_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            # _scope_ will be popped after build\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                key_frames_inds=[0],\n                image_shapes=(3, 128, 128),\n                num_classes=2,\n                with_mask=True)\n            out_data = model.data_preprocessor(packed_inputs, True)\n            inputs, data_samples = out_data['inputs'], out_data['data_samples']\n            losses = model.forward(inputs, data_samples, mode='loss')\n            assert isinstance(losses, dict)\n\n    @parameterized.expand([\n        ('mask2former_vis/mask2former_r50_8xb2-8e_youtubevis2021.py',\n         ('cpu', 'cuda')),\n    ])\n    def test_mask2former_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_mask2former_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=1,\n                image_shapes=(3, 128, 128),\n                num_classes=2,\n                with_mask=True)\n            out_data = model.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_models/test_vis/test_masktrack_rcnn.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport time\nimport unittest\nfrom unittest import TestCase\n\nimport torch\nfrom mmengine.logging import MessageHub\nfrom mmengine.registry import init_default_scope\nfrom parameterized import parameterized\n\nfrom mmdet.registry import MODELS\nfrom mmdet.testing import demo_track_inputs, get_detector_cfg\n\n\nclass TestMaskTrackRCNN(TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        init_default_scope('mmdet')\n\n    @parameterized.expand([\n        'masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py',  # noqa: E501\n    ])\n    def test_mask_track_rcnn_init(self, cfg_file):\n        model = get_detector_cfg(cfg_file)\n\n        model = MODELS.build(model)\n        assert model.detector\n        assert model.track_head\n        assert model.tracker\n\n    @parameterized.expand([\n        (\n            'masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py',  # noqa: E501\n            ('cpu', 'cuda')),\n    ])\n    def test_mask_track_rcnn_forward_loss_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_mask_track_rcnn_forward_loss_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            # _scope_ will be popped after build\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=2,\n                key_frames_inds=[0],\n                image_shapes=(3, 128, 128),\n                num_classes=2,\n                with_mask=True)\n            out_data = model.data_preprocessor(packed_inputs, True)\n            # Test forward\n            losses = model.forward(**out_data, mode='loss')\n            assert isinstance(losses, dict)\n\n    @parameterized.expand([\n        (\n            'masktrack_rcnn/masktrack-rcnn_mask-rcnn_r50_fpn_8xb1-12e_youtubevis2019.py',  # noqa: E501\n            ('cpu', 'cuda')),\n    ])\n    def test_mask_track_rcnn_forward_predict_mode(self, cfg_file, devices):\n        message_hub = MessageHub.get_instance(\n            f'test_mask_track_rcnn_forward_predict_mode-{time.time()}')\n        message_hub.update_info('iter', 0)\n        message_hub.update_info('epoch', 0)\n\n        assert all([device in ['cpu', 'cuda'] for device in devices])\n\n        for device in devices:\n            _model = get_detector_cfg(cfg_file)\n            model = MODELS.build(_model)\n\n            if device == 'cuda':\n                if not torch.cuda.is_available():\n                    return unittest.skip('test requires GPU and torch+cuda')\n                model = model.cuda()\n\n            packed_inputs = demo_track_inputs(\n                batch_size=1,\n                num_frames=1,\n                image_shapes=(3, 128, 128),\n                num_classes=2,\n                with_mask=True)\n            out_data = model.data_preprocessor(packed_inputs, False)\n            # Test forward test\n            model.eval()\n            with torch.no_grad():\n                batch_results = model.forward(**out_data, mode='predict')\n                assert len(batch_results) == 1\n"
  },
  {
    "path": "tests/test_structures/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_structures/test_bbox/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_structures/test_bbox/test_base_boxes.py",
    "content": "from unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine.testing import assert_allclose\n\nfrom .utils import ToyBaseBoxes\n\n\nclass TestBaseBoxes(TestCase):\n\n    def test_init(self):\n        box_tensor = torch.rand((3, 4, 4))\n        boxes = ToyBaseBoxes(box_tensor)\n\n        boxes = ToyBaseBoxes(box_tensor, dtype=torch.float64)\n        self.assertEqual(boxes.tensor.dtype, torch.float64)\n\n        if torch.cuda.is_available():\n            boxes = ToyBaseBoxes(box_tensor, device='cuda')\n            self.assertTrue(boxes.tensor.is_cuda)\n\n        with self.assertRaises(AssertionError):\n            box_tensor = torch.rand((4, ))\n            boxes = ToyBaseBoxes(box_tensor)\n\n        with self.assertRaises(AssertionError):\n            box_tensor = torch.rand((3, 4, 3))\n            boxes = ToyBaseBoxes(box_tensor)\n\n    def test_getitem(self):\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n\n        # test single dimension index\n        # int\n        new_boxes = boxes[0]\n        self.assertIsInstance(new_boxes, ToyBaseBoxes)\n        self.assertEqual(new_boxes.tensor.shape, (4, 4))\n        # list\n        new_boxes = boxes[[0, 2]]\n        self.assertIsInstance(new_boxes, ToyBaseBoxes)\n        self.assertEqual(new_boxes.tensor.shape, (2, 4, 4))\n        # slice\n        new_boxes = boxes[0:2]\n        self.assertIsInstance(new_boxes, ToyBaseBoxes)\n        self.assertEqual(new_boxes.tensor.shape, (2, 4, 4))\n        # torch.LongTensor\n        new_boxes = boxes[torch.LongTensor([0, 1])]\n        self.assertIsInstance(new_boxes, ToyBaseBoxes)\n        self.assertEqual(new_boxes.tensor.shape, (2, 4, 4))\n        # torch.BoolTensor\n        new_boxes = boxes[torch.BoolTensor([True, False, True])]\n        self.assertIsInstance(new_boxes, ToyBaseBoxes)\n        self.assertEqual(new_boxes.tensor.shape, (2, 4, 4))\n        with self.assertRaises(AssertionError):\n            index = torch.rand((2, 4, 4)) > 0\n            new_boxes = boxes[index]\n\n        # test multiple dimension index\n        # select single box\n        new_boxes = boxes[1, 2]\n        self.assertIsInstance(new_boxes, ToyBaseBoxes)\n        self.assertEqual(new_boxes.tensor.shape, (1, 4))\n        # select the last dimension\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes[1, 2, 1]\n        # has Ellipsis\n        new_boxes = boxes[None, ...]\n        self.assertIsInstance(new_boxes, ToyBaseBoxes)\n        self.assertEqual(new_boxes.tensor.shape, (1, 3, 4, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes[..., None]\n\n    def test_setitem(self):\n        values = ToyBaseBoxes(torch.rand(3, 4, 4))\n        tensor = torch.rand(3, 4, 4)\n\n        # only support BaseBoxes type\n        with self.assertRaises(AssertionError):\n            boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n            boxes[0:2] = tensor[0:2]\n\n        # test single dimension index\n        # int\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        boxes[1] = values[1]\n        assert_allclose(boxes.tensor[1], values.tensor[1])\n        # list\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        boxes[[1, 2]] = values[[1, 2]]\n        assert_allclose(boxes.tensor[[1, 2]], values.tensor[[1, 2]])\n        # slice\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        boxes[0:2] = values[0:2]\n        assert_allclose(boxes.tensor[0:2], values.tensor[0:2])\n        # torch.BoolTensor\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        index = torch.rand(3, 4) > 0.5\n        boxes[index] = values[index]\n        assert_allclose(boxes.tensor[index], values.tensor[index])\n\n        # multiple dimension index\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        boxes[0:2, 0:2] = values[0:2, 0:2]\n        assert_allclose(boxes.tensor[0:2, 0:2], values.tensor[0:2, 0:2])\n        # select single box\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        boxes[1, 1] = values[1, 1]\n        assert_allclose(boxes.tensor[1, 1], values.tensor[1, 1])\n        # select the last dimension\n        with self.assertRaises(AssertionError):\n            boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n            boxes[1, 1, 1] = values[1, 1, 1]\n        # has Ellipsis\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        boxes[0:2, ...] = values[0:2, ...]\n        assert_allclose(boxes.tensor[0:2, ...], values.tensor[0:2, ...])\n\n    def test_tensor_like_functions(self):\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        # new_tensor\n        boxes.new_tensor([1, 2, 3])\n        # new_full\n        boxes.new_full((3, 4), 0)\n        # new_empty\n        boxes.new_empty((3, 4))\n        # new_ones\n        boxes.new_ones((3, 4))\n        # new_zeros\n        boxes.new_zeros((3, 4))\n        # size\n        self.assertEqual(boxes.size(0), 3)\n        self.assertEqual(tuple(boxes.size()), (3, 4, 4))\n        # dim\n        self.assertEqual(boxes.dim(), 3)\n        # device\n        self.assertIsInstance(boxes.device, torch.device)\n        # dtype\n        self.assertIsInstance(boxes.dtype, torch.dtype)\n        # numpy\n        np_boxes = boxes.numpy()\n        self.assertIsInstance(np_boxes, np.ndarray)\n        self.assertTrue((np_boxes == np_boxes).all())\n        # to\n        new_boxes = boxes.to(torch.uint8)\n        self.assertEqual(new_boxes.tensor.dtype, torch.uint8)\n        if torch.cuda.is_available():\n            new_boxes = boxes.to(device='cuda')\n            self.assertTrue(new_boxes.tensor.is_cuda)\n        # cpu\n        if torch.cuda.is_available():\n            new_boxes = boxes.to(device='cuda')\n            new_boxes = new_boxes.cpu()\n            self.assertFalse(new_boxes.tensor.is_cuda)\n        # cuda\n        if torch.cuda.is_available():\n            new_boxes = boxes.cuda()\n            self.assertTrue(new_boxes.tensor.is_cuda)\n        # clone\n        boxes.clone()\n        # detach\n        boxes.detach()\n        # view\n        new_boxes = boxes.view(12, 4)\n        self.assertEqual(tuple(new_boxes.size()), (12, 4))\n        new_boxes = boxes.view(-1, 4)\n        self.assertEqual(tuple(new_boxes.size()), (12, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.view(-1)\n        # reshape\n        new_boxes = boxes.reshape(12, 4)\n        self.assertEqual(tuple(new_boxes.size()), (12, 4))\n        new_boxes = boxes.reshape(-1, 4)\n        self.assertEqual(tuple(new_boxes.size()), (12, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.reshape(-1)\n        # expand\n        new_boxes = boxes[None, ...].expand(4, -1, -1, -1)\n        self.assertEqual(tuple(new_boxes.size()), (4, 3, 4, 4))\n        # repeat\n        new_boxes = boxes.repeat(2, 2, 1)\n        self.assertEqual(tuple(new_boxes.size()), (6, 8, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.repeat(2, 2, 2)\n        # transpose\n        new_boxes = boxes.transpose(0, 1)\n        self.assertEqual(tuple(new_boxes.size()), (4, 3, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.transpose(1, 2)\n        # permute\n        new_boxes = boxes.permute(1, 0, 2)\n        self.assertEqual(tuple(new_boxes.size()), (4, 3, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.permute(2, 1, 0)\n        # split\n        boxes_list = boxes.split(1, dim=0)\n        for box in boxes_list:\n            self.assertIsInstance(box, ToyBaseBoxes)\n            self.assertEqual(tuple(box.size()), (1, 4, 4))\n        boxes_list = boxes.split([1, 2], dim=0)\n        with self.assertRaises(AssertionError):\n            boxes_list = boxes.split(1, dim=2)\n        # chunk\n        boxes_list = boxes.split(3, dim=1)\n        self.assertEqual(len(boxes_list), 2)\n        for box in boxes_list:\n            self.assertIsInstance(box, ToyBaseBoxes)\n        with self.assertRaises(AssertionError):\n            boxes_list = boxes.split(3, dim=2)\n        # unbind\n        boxes_list = boxes.unbind(dim=1)\n        self.assertEqual(len(boxes_list), 4)\n        for box in boxes_list:\n            self.assertIsInstance(box, ToyBaseBoxes)\n            self.assertEqual(tuple(box.size()), (3, 4))\n        with self.assertRaises(AssertionError):\n            boxes_list = boxes.unbind(dim=2)\n        # flatten\n        new_boxes = boxes.flatten()\n        self.assertEqual(tuple(new_boxes.size()), (12, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.flatten(end_dim=2)\n        # squeeze\n        boxes = ToyBaseBoxes(torch.rand(1, 3, 1, 4, 4))\n        new_boxes = boxes.squeeze()\n        self.assertEqual(tuple(new_boxes.size()), (3, 4, 4))\n        new_boxes = boxes.squeeze(dim=2)\n        self.assertEqual(tuple(new_boxes.size()), (1, 3, 4, 4))\n        # unsqueeze\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        new_boxes = boxes.unsqueeze(0)\n        self.assertEqual(tuple(new_boxes.size()), (1, 3, 4, 4))\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.unsqueeze(3)\n        # cat\n        with self.assertRaises(ValueError):\n            ToyBaseBoxes.cat([])\n        box_list = []\n        box_list.append(ToyBaseBoxes(torch.rand(3, 4, 4)))\n        box_list.append(ToyBaseBoxes(torch.rand(1, 4, 4)))\n        with self.assertRaises(AssertionError):\n            ToyBaseBoxes.cat(box_list, dim=2)\n        cat_boxes = ToyBaseBoxes.cat(box_list, dim=0)\n        self.assertIsInstance(cat_boxes, ToyBaseBoxes)\n        self.assertEqual((cat_boxes.size()), (4, 4, 4))\n        # stack\n        with self.assertRaises(ValueError):\n            ToyBaseBoxes.stack([])\n        box_list = []\n        box_list.append(ToyBaseBoxes(torch.rand(3, 4, 4)))\n        box_list.append(ToyBaseBoxes(torch.rand(3, 4, 4)))\n        with self.assertRaises(AssertionError):\n            ToyBaseBoxes.stack(box_list, dim=3)\n        stack_boxes = ToyBaseBoxes.stack(box_list, dim=1)\n        self.assertIsInstance(stack_boxes, ToyBaseBoxes)\n        self.assertEqual((stack_boxes.size()), (3, 2, 4, 4))\n\n    def test_misc(self):\n        boxes = ToyBaseBoxes(torch.rand(3, 4, 4))\n        # __len__\n        self.assertEqual(len(boxes), 3)\n        # __repr__\n        repr(boxes)\n        # fake_boxes\n        new_boxes = boxes.fake_boxes((3, 4, 4), 1)\n        self.assertEqual(tuple(new_boxes.size()), (3, 4, 4))\n        self.assertEqual(boxes.dtype, new_boxes.dtype)\n        self.assertEqual(boxes.device, new_boxes.device)\n        self.assertTrue((new_boxes.tensor == 1).all())\n        with self.assertRaises(AssertionError):\n            new_boxes = boxes.fake_boxes((3, 4, 1))\n        new_boxes = boxes.fake_boxes((3, 4, 4), dtype=torch.uint8)\n        self.assertEqual(new_boxes.dtype, torch.uint8)\n        if torch.cuda.is_available():\n            new_boxes = boxes.fake_boxes((3, 4, 4), device='cuda')\n            self.assertTrue(new_boxes.tensor.is_cuda)\n"
  },
  {
    "path": "tests/test_structures/test_bbox/test_box_type.py",
    "content": "from unittest import TestCase\nfrom unittest.mock import MagicMock\n\nimport torch\n\nfrom mmdet.structures.bbox.box_type import (_box_type_to_name, box_converters,\n                                            box_types, convert_box_type,\n                                            get_box_type, register_box,\n                                            register_box_converter)\nfrom .utils import ToyBaseBoxes\n\n\nclass TestBoxType(TestCase):\n\n    def setUp(self):\n        self.box_types = box_types.copy()\n        self.box_converters = box_converters.copy()\n        self._box_type_to_name = _box_type_to_name.copy()\n\n    def tearDown(self):\n        # Clear registered items\n        box_types.clear()\n        box_converters.clear()\n        _box_type_to_name.clear()\n        # Restore original items\n        box_types.update(self.box_types)\n        box_converters.update(self.box_converters)\n        _box_type_to_name.update(self._box_type_to_name)\n\n    def test_register_box(self):\n        # test usage of decorator\n        @register_box('A')\n        class A(ToyBaseBoxes):\n            pass\n\n        # test usage of normal function\n        class B(ToyBaseBoxes):\n            pass\n\n        register_box('B', B)\n\n        # register class without inheriting from BaseBoxes\n        with self.assertRaises(AssertionError):\n\n            @register_box('C')\n            class C:\n                pass\n\n        # test register registered class\n        with self.assertRaises(KeyError):\n\n            @register_box('A')\n            class AA(ToyBaseBoxes):\n                pass\n\n        with self.assertRaises(KeyError):\n            register_box('BB', B)\n\n        @register_box('A', force=True)\n        class AAA(ToyBaseBoxes):\n            pass\n\n        self.assertIs(box_types['a'], AAA)\n        self.assertEqual(_box_type_to_name[AAA], 'a')\n        register_box('BB', B, force=True)\n        self.assertIs(box_types['bb'], B)\n        self.assertEqual(_box_type_to_name[B], 'bb')\n        self.assertEqual(len(box_types), len(_box_type_to_name))\n\n    def test_register_box_converter(self):\n\n        @register_box('A')\n        class A(ToyBaseBoxes):\n            pass\n\n        @register_box('B')\n        class B(ToyBaseBoxes):\n            pass\n\n        @register_box('C')\n        class C(ToyBaseBoxes):\n            pass\n\n        # test usage of decorator\n        @register_box_converter('A', 'B')\n        def converter_A(bboxes):\n            return bboxes\n\n        # test usage of normal function\n        def converter_B(bboxes):\n            return bboxes\n\n        register_box_converter('B'\n                               'A', converter_B)\n\n        # register uncallable object\n        with self.assertRaises(AssertionError):\n            register_box_converter('A', 'C', 'uncallable str')\n\n        # test register unregistered bbox mode\n        with self.assertRaises(AssertionError):\n\n            @register_box_converter('A', 'D')\n            def converter_C(bboxes):\n                return bboxes\n\n        # test register registered converter\n        with self.assertRaises(KeyError):\n\n            @register_box_converter('A', 'B')\n            def converter_D(bboxes):\n                return bboxes\n\n        @register_box_converter('A', 'B', force=True)\n        def converter_E(bboxes):\n            return bboxes\n\n        self.assertIs(box_converters['a2b'], converter_E)\n\n    def test_get_box_type(self):\n\n        @register_box('A')\n        class A(ToyBaseBoxes):\n            pass\n\n        mode_name, mode_cls = get_box_type('A')\n        self.assertEqual(mode_name, 'a')\n        self.assertIs(mode_cls, A)\n        mode_name, mode_cls = get_box_type(A)\n        self.assertEqual(mode_name, 'a')\n        self.assertIs(mode_cls, A)\n\n        # get unregistered mode\n        class B(ToyBaseBoxes):\n            pass\n\n        with self.assertRaises(AssertionError):\n            mode_name, mode_cls = get_box_type('B')\n        with self.assertRaises(AssertionError):\n            mode_name, mode_cls = get_box_type(B)\n\n    def test_convert_box_type(self):\n\n        @register_box('A')\n        class A(ToyBaseBoxes):\n            pass\n\n        @register_box('B')\n        class B(ToyBaseBoxes):\n            pass\n\n        @register_box('C')\n        class C(ToyBaseBoxes):\n            pass\n\n        converter = MagicMock()\n        converter.return_value = torch.rand(3, 4, 4)\n        register_box_converter('A', 'B', converter)\n\n        bboxes_a = A(torch.rand(3, 4, 4))\n        th_bboxes_a = bboxes_a.tensor\n        np_bboxes_a = th_bboxes_a.numpy()\n\n        # test convert to mode\n        convert_box_type(bboxes_a, dst_type='B')\n        self.assertTrue(converter.called)\n        converted_bboxes = convert_box_type(bboxes_a, dst_type='A')\n        self.assertIs(converted_bboxes, bboxes_a)\n        # test convert to unregistered mode\n        with self.assertRaises(AssertionError):\n            convert_box_type(bboxes_a, dst_type='C')\n\n        # test convert tensor and ndarray\n        # without specific src_type\n        with self.assertRaises(AssertionError):\n            convert_box_type(th_bboxes_a, dst_type='B')\n        with self.assertRaises(AssertionError):\n            convert_box_type(np_bboxes_a, dst_type='B')\n        # test np.ndarray\n        convert_box_type(np_bboxes_a, src_type='A', dst_type='B')\n        converted_bboxes = convert_box_type(\n            np_bboxes_a, src_type='A', dst_type='A')\n        self.assertIs(converted_bboxes, np_bboxes_a)\n        # test tensor\n        convert_box_type(th_bboxes_a, src_type='A', dst_type='B')\n        converted_bboxes = convert_box_type(\n            th_bboxes_a, src_type='A', dst_type='A')\n        self.assertIs(converted_bboxes, th_bboxes_a)\n        # test other type\n        with self.assertRaises(TypeError):\n            convert_box_type([[1, 2, 3, 4]], src_type='A', dst_type='B')\n"
  },
  {
    "path": "tests/test_structures/test_bbox/test_horizontal_boxes.py",
    "content": "import random\nfrom math import sqrt\nfrom unittest import TestCase\n\nimport cv2\nimport numpy as np\nimport torch\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.structures.bbox import HorizontalBoxes\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\n\n\nclass TestHorizontalBoxes(TestCase):\n\n    def test_init(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        th_boxes_cxcywh = torch.Tensor([15, 15, 10, 10]).reshape(1, 1, 4)\n\n        boxes = HorizontalBoxes(th_boxes)\n        assert_allclose(boxes.tensor, th_boxes)\n        boxes = HorizontalBoxes(th_boxes, in_mode='xyxy')\n        assert_allclose(boxes.tensor, th_boxes)\n        boxes = HorizontalBoxes(th_boxes_cxcywh, in_mode='cxcywh')\n        assert_allclose(boxes.tensor, th_boxes)\n        with self.assertRaises(ValueError):\n            boxes = HorizontalBoxes(th_boxes, in_mode='invalid')\n\n    def test_cxcywh(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        th_boxes_cxcywh = torch.Tensor([15, 15, 10, 10]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n\n        assert_allclose(\n            HorizontalBoxes.xyxy_to_cxcywh(th_boxes), th_boxes_cxcywh)\n        assert_allclose(th_boxes,\n                        HorizontalBoxes.cxcywh_to_xyxy(th_boxes_cxcywh))\n        assert_allclose(boxes.cxcywh, th_boxes_cxcywh)\n\n    def test_propoerty(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n\n        # Centers\n        centers = torch.Tensor([15, 15]).reshape(1, 1, 2)\n        assert_allclose(boxes.centers, centers)\n        # Areas\n        areas = torch.Tensor([100]).reshape(1, 1)\n        assert_allclose(boxes.areas, areas)\n        # widths\n        widths = torch.Tensor([10]).reshape(1, 1)\n        assert_allclose(boxes.widths, widths)\n        # heights\n        heights = torch.Tensor([10]).reshape(1, 1)\n        assert_allclose(boxes.heights, heights)\n\n    def test_flip(self):\n        img_shape = [50, 85]\n        # horizontal flip\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        flipped_boxes_th = torch.Tensor([65, 10, 75, 20]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.flip_(img_shape, direction='horizontal')\n        assert_allclose(boxes.tensor, flipped_boxes_th)\n        # vertical flip\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        flipped_boxes_th = torch.Tensor([10, 30, 20, 40]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.flip_(img_shape, direction='vertical')\n        assert_allclose(boxes.tensor, flipped_boxes_th)\n        # diagonal flip\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        flipped_boxes_th = torch.Tensor([65, 30, 75, 40]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.flip_(img_shape, direction='diagonal')\n        assert_allclose(boxes.tensor, flipped_boxes_th)\n\n    def test_translate(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.translate_([23, 46])\n        translated_boxes_th = torch.Tensor([33, 56, 43, 66]).reshape(1, 1, 4)\n        assert_allclose(boxes.tensor, translated_boxes_th)\n\n    def test_clip(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        img_shape = [13, 14]\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.clip_(img_shape)\n        cliped_boxes_th = torch.Tensor([10, 10, 14, 13]).reshape(1, 1, 4)\n        assert_allclose(boxes.tensor, cliped_boxes_th)\n\n    def test_rotate(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        center = (15, 15)\n        angle = -45\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.rotate_(center, angle)\n        rotated_boxes_th = torch.Tensor([\n            15 - 5 * sqrt(2), 15 - 5 * sqrt(2), 15 + 5 * sqrt(2),\n            15 + 5 * sqrt(2)\n        ]).reshape(1, 1, 4)\n        assert_allclose(boxes.tensor, rotated_boxes_th)\n\n    def test_project(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        boxes1 = HorizontalBoxes(th_boxes)\n        boxes2 = boxes1.clone()\n\n        matrix = np.zeros((3, 3), dtype=np.float32)\n        center = [random.random() * 80, random.random() * 80]\n        angle = random.random() * 180\n        matrix[:2, :3] = cv2.getRotationMatrix2D(center, angle, 1)\n        x_translate = random.random() * 40\n        y_translate = random.random() * 40\n        matrix[0, 2] = matrix[0, 2] + x_translate\n        matrix[1, 2] = matrix[1, 2] + y_translate\n        scale_factor = random.random() * 2\n        matrix[2, 2] = 1 / scale_factor\n        boxes1.project_(matrix)\n\n        boxes2.rotate_(center, -angle)\n        boxes2.translate_([x_translate, y_translate])\n        boxes2.rescale_([scale_factor, scale_factor])\n        assert_allclose(boxes1.tensor, boxes2.tensor)\n        # test empty boxes\n        empty_boxes = HorizontalBoxes(torch.zeros((0, 4)))\n        empty_boxes.project_(matrix)\n\n    def test_rescale(self):\n        scale_factor = [0.4, 0.8]\n        # rescale\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.rescale_(scale_factor)\n        rescaled_boxes_th = torch.Tensor([4, 8, 8, 16]).reshape(1, 1, 4)\n        assert_allclose(boxes.tensor, rescaled_boxes_th)\n\n    def test_resize(self):\n        scale_factor = [0.4, 0.8]\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n        boxes.resize_(scale_factor)\n        resized_boxes_th = torch.Tensor([13, 11, 17, 19]).reshape(1, 1, 4)\n        assert_allclose(boxes.tensor, resized_boxes_th)\n\n    def test_is_inside(self):\n        th_boxes = torch.Tensor([[10, 10, 20, 20], [-5, -5, 15, 15],\n                                 [45, 45, 55, 55]]).reshape(1, 3, 4)\n        img_shape = [30, 30]\n        boxes = HorizontalBoxes(th_boxes)\n\n        index = boxes.is_inside(img_shape)\n        index_th = torch.BoolTensor([True, True, False]).reshape(1, 3)\n        assert_allclose(index, index_th)\n\n    def test_find_inside_points(self):\n        th_boxes = torch.Tensor([10, 10, 20, 20]).reshape(1, 4)\n        boxes = HorizontalBoxes(th_boxes)\n        points = torch.Tensor([[0, 0], [0, 15], [15, 0], [15, 15]])\n        index = boxes.find_inside_points(points)\n        index_th = torch.BoolTensor([False, False, False, True]).reshape(4, 1)\n        assert_allclose(index, index_th)\n        # is_aligned\n        boxes = boxes.expand(4, 4)\n        index = boxes.find_inside_points(points, is_aligned=True)\n        index_th = torch.BoolTensor([False, False, False, True])\n        assert_allclose(index, index_th)\n\n    def test_from_instance_masks(self):\n        bitmap_masks = BitmapMasks.random()\n        boxes = HorizontalBoxes.from_instance_masks(bitmap_masks)\n        self.assertIsInstance(boxes, HorizontalBoxes)\n        self.assertEqual(len(boxes), len(bitmap_masks))\n        polygon_masks = PolygonMasks.random()\n        boxes = HorizontalBoxes.from_instance_masks(polygon_masks)\n        self.assertIsInstance(boxes, HorizontalBoxes)\n        self.assertEqual(len(boxes), len(bitmap_masks))\n        # zero length masks\n        bitmap_masks = BitmapMasks.random(num_masks=0)\n        boxes = HorizontalBoxes.from_instance_masks(bitmap_masks)\n        self.assertIsInstance(boxes, HorizontalBoxes)\n        self.assertEqual(len(boxes), 0)\n        polygon_masks = PolygonMasks.random(num_masks=0)\n        boxes = HorizontalBoxes.from_instance_masks(polygon_masks)\n        self.assertIsInstance(boxes, HorizontalBoxes)\n        self.assertEqual(len(boxes), 0)\n"
  },
  {
    "path": "tests/test_structures/test_bbox/utils.py",
    "content": "from mmdet.structures.bbox import BaseBoxes\n\n\nclass ToyBaseBoxes(BaseBoxes):\n\n    box_dim = 4\n\n    @property\n    def centers(self):\n        pass\n\n    @property\n    def areas(self):\n        pass\n\n    @property\n    def widths(self):\n        pass\n\n    @property\n    def heights(self):\n        pass\n\n    def flip_(self, img_shape, direction='horizontal'):\n        pass\n\n    def translate_(self, distances):\n        pass\n\n    def clip_(self, img_shape):\n        pass\n\n    def rotate_(self, center, angle):\n        pass\n\n    def project_(self, homography_matrix):\n        pass\n\n    def rescale_(self, scale_factor):\n        pass\n\n    def resize_(self, scale_factor):\n        pass\n\n    def is_inside(self, img_shape):\n        pass\n\n    def find_inside_points(self, points, is_aligned=False):\n        pass\n\n    def overlaps(bboxes1, bboxes2, mode='iou', is_aligned=False, eps=1e-6):\n        pass\n\n    def from_instance_masks(masks):\n        pass\n"
  },
  {
    "path": "tests/test_structures/test_det_data_sample.py",
    "content": "from unittest import TestCase\n\nimport numpy as np\nimport pytest\nimport torch\nfrom mmengine.structures import InstanceData, PixelData\n\nfrom mmdet.structures import DetDataSample\n\n\ndef _equal(a, b):\n    if isinstance(a, (torch.Tensor, np.ndarray)):\n        return (a == b).all()\n    else:\n        return a == b\n\n\nclass TestDetDataSample(TestCase):\n\n    def test_init(self):\n        meta_info = dict(\n            img_size=[256, 256],\n            scale_factor=np.array([1.5, 1.5]),\n            img_shape=torch.rand(4))\n\n        det_data_sample = DetDataSample(metainfo=meta_info)\n        assert 'img_size' in det_data_sample\n        assert det_data_sample.img_size == [256, 256]\n        assert det_data_sample.get('img_size') == [256, 256]\n\n    def test_setter(self):\n        det_data_sample = DetDataSample()\n        # test gt_instances\n        gt_instances_data = dict(\n            bboxes=torch.rand(4, 4),\n            labels=torch.rand(4),\n            masks=np.random.rand(4, 2, 2))\n        gt_instances = InstanceData(**gt_instances_data)\n        det_data_sample.gt_instances = gt_instances\n        assert 'gt_instances' in det_data_sample\n        assert _equal(det_data_sample.gt_instances.bboxes,\n                      gt_instances_data['bboxes'])\n        assert _equal(det_data_sample.gt_instances.labels,\n                      gt_instances_data['labels'])\n        assert _equal(det_data_sample.gt_instances.masks,\n                      gt_instances_data['masks'])\n\n        # test pred_instances\n        pred_instances_data = dict(\n            bboxes=torch.rand(2, 4),\n            labels=torch.rand(2),\n            masks=np.random.rand(2, 2, 2))\n        pred_instances = InstanceData(**pred_instances_data)\n        det_data_sample.pred_instances = pred_instances\n        assert 'pred_instances' in det_data_sample\n        assert _equal(det_data_sample.pred_instances.bboxes,\n                      pred_instances_data['bboxes'])\n        assert _equal(det_data_sample.pred_instances.labels,\n                      pred_instances_data['labels'])\n        assert _equal(det_data_sample.pred_instances.masks,\n                      pred_instances_data['masks'])\n\n        # test pred_track_instances\n        pred_track_instances_data = dict(\n            bboxes=torch.rand(2, 4),\n            labels=torch.rand(2),\n            masks=np.random.rand(2, 2, 2))\n        pred_instances = InstanceData(**pred_track_instances_data)\n        det_data_sample.pred_instances = pred_instances\n        assert 'pred_instances' in det_data_sample\n        assert _equal(det_data_sample.pred_instances.bboxes,\n                      pred_track_instances_data['bboxes'])\n        assert _equal(det_data_sample.pred_instances.labels,\n                      pred_track_instances_data['labels'])\n        assert _equal(det_data_sample.pred_instances.masks,\n                      pred_track_instances_data['masks'])\n\n        # test proposals\n        proposals_data = dict(bboxes=torch.rand(4, 4), labels=torch.rand(4))\n        proposals = InstanceData(**proposals_data)\n        det_data_sample.proposals = proposals\n        assert 'proposals' in det_data_sample\n        assert _equal(det_data_sample.proposals.bboxes,\n                      proposals_data['bboxes'])\n        assert _equal(det_data_sample.proposals.labels,\n                      proposals_data['labels'])\n\n        # test ignored_instances\n        ignored_instances_data = dict(\n            bboxes=torch.rand(4, 4), labels=torch.rand(4))\n        ignored_instances = InstanceData(**ignored_instances_data)\n        det_data_sample.ignored_instances = ignored_instances\n        assert 'ignored_instances' in det_data_sample\n        assert _equal(det_data_sample.ignored_instances.bboxes,\n                      ignored_instances_data['bboxes'])\n        assert _equal(det_data_sample.ignored_instances.labels,\n                      ignored_instances_data['labels'])\n\n        # test gt_panoptic_seg\n        gt_panoptic_seg_data = dict(panoptic_seg=torch.rand(5, 4))\n        gt_panoptic_seg = PixelData(**gt_panoptic_seg_data)\n        det_data_sample.gt_panoptic_seg = gt_panoptic_seg\n        assert 'gt_panoptic_seg' in det_data_sample\n        assert _equal(det_data_sample.gt_panoptic_seg.panoptic_seg,\n                      gt_panoptic_seg_data['panoptic_seg'])\n\n        # test pred_panoptic_seg\n        pred_panoptic_seg_data = dict(panoptic_seg=torch.rand(5, 4))\n        pred_panoptic_seg = PixelData(**pred_panoptic_seg_data)\n        det_data_sample.pred_panoptic_seg = pred_panoptic_seg\n        assert 'pred_panoptic_seg' in det_data_sample\n        assert _equal(det_data_sample.pred_panoptic_seg.panoptic_seg,\n                      pred_panoptic_seg_data['panoptic_seg'])\n\n        # test gt_sem_seg\n        gt_segm_seg_data = dict(segm_seg=torch.rand(5, 4, 2))\n        gt_segm_seg = PixelData(**gt_segm_seg_data)\n        det_data_sample.gt_segm_seg = gt_segm_seg\n        assert 'gt_segm_seg' in det_data_sample\n        assert _equal(det_data_sample.gt_segm_seg.segm_seg,\n                      gt_segm_seg_data['segm_seg'])\n\n        # test pred_segm_seg\n        pred_segm_seg_data = dict(segm_seg=torch.rand(5, 4, 2))\n        pred_segm_seg = PixelData(**pred_segm_seg_data)\n        det_data_sample.pred_segm_seg = pred_segm_seg\n        assert 'pred_segm_seg' in det_data_sample\n        assert _equal(det_data_sample.pred_segm_seg.segm_seg,\n                      pred_segm_seg_data['segm_seg'])\n\n        # test type error\n        with pytest.raises(AssertionError):\n            det_data_sample.pred_instances = torch.rand(2, 4)\n\n        with pytest.raises(AssertionError):\n            det_data_sample.pred_panoptic_seg = torch.rand(2, 4)\n\n        with pytest.raises(AssertionError):\n            det_data_sample.pred_sem_seg = torch.rand(2, 4)\n\n    def test_deleter(self):\n        gt_instances_data = dict(\n            bboxes=torch.rand(4, 4),\n            labels=torch.rand(4),\n            masks=np.random.rand(4, 2, 2))\n\n        det_data_sample = DetDataSample()\n        gt_instances = InstanceData(data=gt_instances_data)\n        det_data_sample.gt_instances = gt_instances\n        assert 'gt_instances' in det_data_sample\n        del det_data_sample.gt_instances\n        assert 'gt_instances' not in det_data_sample\n\n        pred_panoptic_seg_data = torch.rand(5, 4)\n        pred_panoptic_seg = PixelData(data=pred_panoptic_seg_data)\n        det_data_sample.pred_panoptic_seg = pred_panoptic_seg\n        assert 'pred_panoptic_seg' in det_data_sample\n        del det_data_sample.pred_panoptic_seg\n        assert 'pred_panoptic_seg' not in det_data_sample\n\n        pred_segm_seg_data = dict(segm_seg=torch.rand(5, 4, 2))\n        pred_segm_seg = PixelData(**pred_segm_seg_data)\n        det_data_sample.pred_segm_seg = pred_segm_seg\n        assert 'pred_segm_seg' in det_data_sample\n        del det_data_sample.pred_segm_seg\n        assert 'pred_segm_seg' not in det_data_sample\n"
  },
  {
    "path": "tests/test_structures/test_mask/test_mask_structures.py",
    "content": "from unittest import TestCase\n\nimport numpy as np\nfrom mmengine.testing import assert_allclose\n\nfrom mmdet.structures.mask import BitmapMasks, PolygonMasks\n\n\nclass TestMaskStructures(TestCase):\n\n    def test_bitmap_translate_same_size(self):\n        mask_array = np.zeros((5, 10, 10), dtype=np.uint8)\n        mask_array[:, 0:5, 0:5] = 1\n        mask_target = np.zeros((5, 10, 10), dtype=np.uint8)\n        mask_target[:, 0:5, 5:10] = 1\n\n        mask = BitmapMasks(mask_array, 10, 10)\n        mask = mask.translate((10, 10), 5)\n        assert mask.masks.shape == (5, 10, 10)\n        assert_allclose(mask_target, mask.masks)\n\n    def test_bitmap_translate_diff_size(self):\n        # test out shape larger\n        mask_array = np.zeros((5, 10, 10), dtype=np.uint8)\n        mask_array[:, 0:5, 0:5] = 1\n\n        mask_target = np.zeros((5, 20, 20), dtype=np.uint8)\n        mask_target[:, 0:5, 5:10] = 1\n        mask = BitmapMasks(mask_array, 10, 10)\n        mask = mask.translate((20, 20), 5)\n        assert mask.masks.shape == (5, 20, 20)\n        assert_allclose(mask_target, mask.masks)\n\n        # test out shape smaller\n        mask_array = np.zeros((5, 10, 10), dtype=np.uint8)\n        mask_array[:, 0:5, 0:5] = 1\n\n        mask_target = np.zeros((5, 20, 8), dtype=np.uint8)\n        mask_target[:, 0:5, 5:] = 1\n        mask = BitmapMasks(mask_array, 10, 10)\n        mask = mask.translate((20, 8), 5)\n        assert mask.masks.shape == (5, 20, 8)\n        assert_allclose(mask_target, mask.masks)\n\n    def test_bitmap_cat(self):\n        # test invalid inputs\n        with self.assertRaises(AssertionError):\n            BitmapMasks.cat(BitmapMasks.random(4))\n        with self.assertRaises(ValueError):\n            BitmapMasks.cat([])\n        with self.assertRaises(AssertionError):\n            BitmapMasks.cat([BitmapMasks.random(2), PolygonMasks.random(3)])\n\n        masks = [BitmapMasks.random(num_masks=3) for _ in range(5)]\n        cat_mask = BitmapMasks.cat(masks)\n        assert len(cat_mask) == 3 * 5\n        for i, m in enumerate(masks):\n            assert_allclose(m.masks, cat_mask.masks[i * 3:(i + 1) * 3])\n\n    def test_polygon_cat(self):\n        # test invalid inputs\n        with self.assertRaises(AssertionError):\n            PolygonMasks.cat(PolygonMasks.random(4))\n        with self.assertRaises(ValueError):\n            PolygonMasks.cat([])\n        with self.assertRaises(AssertionError):\n            PolygonMasks.cat([BitmapMasks.random(2), PolygonMasks.random(3)])\n\n        masks = [PolygonMasks.random(num_masks=3) for _ in range(5)]\n        cat_mask = PolygonMasks.cat(masks)\n        assert len(cat_mask) == 3 * 5\n        for i, m in enumerate(masks):\n            assert_allclose(m.masks, cat_mask.masks[i * 3:(i + 1) * 3])\n"
  },
  {
    "path": "tests/test_structures/test_reid_data_sample.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom unittest import TestCase\n\nimport numpy as np\nimport torch\nfrom mmengine.structures import LabelData\n\nfrom mmdet.structures import ReIDDataSample\n\n\ndef _equal(a, b):\n    if isinstance(a, (torch.Tensor, np.ndarray)):\n        return (a == b).all()\n    else:\n        return a == b\n\n\nclass TestReIDDataSample(TestCase):\n\n    def test_init(self):\n        img_shape = (256, 128)\n        ori_shape = (64, 64)\n        num_classes = 5\n        meta_info = dict(\n            img_shape=img_shape, ori_shape=ori_shape, num_classes=num_classes)\n        data_sample = ReIDDataSample(metainfo=meta_info)\n        self.assertIn('img_shape', data_sample)\n        self.assertIn('ori_shape', data_sample)\n        self.assertIn('num_classes', data_sample)\n        self.assertTrue(_equal(data_sample.get('img_shape'), img_shape))\n        self.assertTrue(_equal(data_sample.get('ori_shape'), ori_shape))\n        self.assertTrue(_equal(data_sample.get('num_classes'), num_classes))\n\n    def test_set_gt_label(self):\n        data_sample = ReIDDataSample(metainfo=dict(num_classes=5))\n        method = getattr(data_sample, 'set_' + 'gt_label')\n\n        # Test number\n        method(1)\n        label = data_sample.get('gt_label')\n        self.assertIsInstance(label, LabelData)\n        self.assertIsInstance(label.label, torch.LongTensor)\n\n        # Test tensor with single number\n        method(torch.tensor(2))\n        label = data_sample.get('gt_label')\n        self.assertIsInstance(label, LabelData)\n        self.assertIsInstance(label.label, torch.LongTensor)\n\n        # Test array with single number\n        method(np.array(3))\n        label = data_sample.get('gt_label')\n        self.assertIsInstance(label, LabelData)\n        self.assertIsInstance(label.label, torch.LongTensor)\n\n        # Test tensor\n        _label = torch.tensor([1, 2, 3])\n        method(_label)\n        label = data_sample.get('gt_label')\n        self.assertIsInstance(label, LabelData)\n        self.assertIsInstance(label.label, torch.Tensor)\n        self.assertTrue(_equal(label.label, _label))\n\n        # Test array\n        _label = np.array([1, 2, 3])\n        method(_label)\n        label = data_sample.get('gt_label')\n        self.assertIsInstance(label, LabelData)\n        self.assertIsInstance(label.label, torch.Tensor)\n        self.assertTrue(_equal(label.label, torch.from_numpy(_label)))\n\n        # Test Sequence\n        _label = [1, 2, 3.]\n        method(_label)\n        label = data_sample.get('gt_label')\n        self.assertIsInstance(label, LabelData)\n        self.assertIsInstance(label.label, torch.Tensor)\n        self.assertTrue(_equal(label.label, torch.tensor(_label)))\n\n        # Test set num_classes\n        self.assertEqual(label.num_classes, 5)\n\n        # Test unavailable type\n        with self.assertRaisesRegex(TypeError, \"<class 'str'> is not\"):\n            method('hi')\n\n    def test_set_gt_score(self):\n        data_sample = ReIDDataSample(metainfo={'num_classes': 5})\n        method = getattr(data_sample, 'set_' + 'gt_score')\n\n        # Test set\n        score = [0.1, 0.1, 0.6, 0.1, 0.1]\n        method(torch.tensor(score))\n        sample_gt_label = getattr(data_sample, 'gt_label')\n        self.assertIn('score', sample_gt_label)\n        torch.testing.assert_allclose(sample_gt_label.score, score)\n        self.assertEqual(sample_gt_label.num_classes, 5)\n\n        # Test set again\n        score = [0.2, 0.1, 0.5, 0.1, 0.1]\n        method(torch.tensor(score))\n        torch.testing.assert_allclose(sample_gt_label.score, score)\n\n        # Test invalid type\n        with self.assertRaisesRegex(AssertionError, 'be a torch.Tensor'):\n            method(score)\n\n        # Test invalid dims\n        with self.assertRaisesRegex(AssertionError, 'but got 2'):\n            method(torch.tensor([score]))\n\n        # Test invalid num_classes\n        with self.assertRaisesRegex(AssertionError, r'length of value \\(6\\)'):\n            method(torch.tensor(score + [0.1]))\n\n        # Test auto inter num_classes\n        data_sample = ReIDDataSample()\n        method = getattr(data_sample, 'set_gt_score')\n        method(torch.tensor(score))\n        sample_gt_label = getattr(data_sample, 'gt_label')\n        self.assertEqual(sample_gt_label.num_classes, len(score))\n\n    def test_del_gt_label(self):\n        data_sample = ReIDDataSample()\n        self.assertNotIn('gt_label', data_sample)\n        data_sample.set_gt_label(1)\n        self.assertIn('gt_label', data_sample)\n        del data_sample.gt_label\n        self.assertNotIn('gt_label', data_sample)\n"
  },
  {
    "path": "tests/test_structures/test_track_data_sample.py",
    "content": "from unittest import TestCase\n\nimport pytest\n\nfrom mmdet.structures import DetDataSample, TrackDataSample\n\n\nclass TestDetDataSample(TestCase):\n\n    def test_init(self):\n        track_data_sample = TrackDataSample(\n            metainfo=dict(key_frames_inds=[0], ref_frames_inds=[1]))\n\n        assert 'key_frames_inds' in track_data_sample.metainfo and \\\n            'ref_frames_inds' in track_data_sample.metainfo\n        assert track_data_sample.key_frames_inds == [0]\n        assert track_data_sample.ref_frames_inds == [1]\n        with pytest.raises(AssertionError):\n            track_data_sample.get_key_frames()\n        with pytest.raises(AssertionError):\n            track_data_sample.get_ref_frames()\n\n    def test_setter(self):\n        det_data_sample_1 = DetDataSample(\n            metainfo=dict(scale_factor=(1.5, 1.5)))\n        det_data_sample_2 = DetDataSample(metainfo=dict(scale_factor=(2., 2.)))\n        track_data_sample = TrackDataSample(\n            metainfo=dict(key_frames_inds=[0], ref_frames_inds=[1]))\n        track_data_sample.video_data_samples = [\n            det_data_sample_1, det_data_sample_2\n        ]\n\n        assert track_data_sample.get_key_frames()[0].scale_factor == (1.5, 1.5)\n        assert track_data_sample.get_ref_frames()[0].scale_factor == (2., 2.)\n\n    def test_deleter(self):\n        det_data_sample_1 = DetDataSample(\n            metainfo=dict(scale_factor=(1.5, 1.5)))\n        det_data_sample_2 = DetDataSample(metainfo=dict(scale_factor=(2., 2.)))\n        track_data_sample = TrackDataSample(\n            metainfo=dict(key_frames_inds=[0], ref_frames_inds=[1]))\n        track_data_sample.video_data_samples = [\n            det_data_sample_1, det_data_sample_2\n        ]\n        assert 'video_data_samples' in track_data_sample\n        del track_data_sample.video_data_samples\n        assert 'video_data_samples' not in track_data_sample\n"
  },
  {
    "path": "tests/test_utils/test_benchmark.py",
    "content": "import copy\nimport os\nimport tempfile\nimport unittest\n\nimport torch\nfrom mmengine import Config, MMLogger\nfrom mmengine.dataset import Compose\nfrom mmengine.model import BaseModel\nfrom torch.utils.data import Dataset\n\nfrom mmdet.registry import DATASETS, MODELS\nfrom mmdet.utils import register_all_modules\nfrom mmdet.utils.benchmark import (DataLoaderBenchmark, DatasetBenchmark,\n                                   InferenceBenchmark)\n\n\n@MODELS.register_module()\nclass ToyDetector(BaseModel):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__()\n\n    def forward(self, *args, **kwargs):\n        pass\n\n\n@DATASETS.register_module()\nclass ToyDataset(Dataset):\n    METAINFO = dict()  # type: ignore\n    data = torch.randn(12, 2)\n    label = torch.ones(12)\n\n    def __init__(self):\n        self.pipeline = Compose([lambda x: x])\n\n    def __len__(self):\n        return self.data.size(0)\n\n    def get_data_info(self, index):\n        return dict(inputs=self.data[index], data_sample=self.label[index])\n\n    def __getitem__(self, index):\n        return dict(inputs=self.data[index], data_sample=self.label[index])\n\n\n@DATASETS.register_module()\nclass ToyFullInitDataset(Dataset):\n    METAINFO = dict()  # type: ignore\n    data = torch.randn(12, 2)\n    label = torch.ones(12)\n\n    def __init__(self):\n        self.pipeline = Compose([lambda x: x])\n\n    def __len__(self):\n        return self.data.size(0)\n\n    def get_data_info(self, index):\n        return dict(inputs=self.data[index], data_sample=self.label[index])\n\n    def full_init(self):\n        pass\n\n    def __getitem__(self, index):\n        return dict(inputs=self.data[index], data_sample=self.label[index])\n\n\nclass TestInferenceBenchmark(unittest.TestCase):\n\n    def setUp(self) -> None:\n        register_all_modules()\n\n        self.cfg = Config(\n            dict(\n                model=dict(type='ToyDetector'),\n                test_dataloader=dict(\n                    dataset=dict(type='ToyDataset'),\n                    sampler=dict(type='DefaultSampler', shuffle=False),\n                    batch_size=3,\n                    num_workers=1),\n                env_cfg=dict(dist_cfg=dict(backend='nccl'))))\n        self.max_iter = 10\n        self.log_interval = 5\n\n    @unittest.skipIf(not torch.cuda.is_available(),\n                     'test requires GPU and torch+cuda')\n    def test_init_and_run(self):\n        checkpoint_path = os.path.join(tempfile.gettempdir(), 'checkpoint.pth')\n        torch.save(ToyDetector().state_dict(), checkpoint_path)\n\n        cfg = copy.deepcopy(self.cfg)\n        inference_benchmark = InferenceBenchmark(cfg, checkpoint_path, False,\n                                                 False, self.max_iter,\n                                                 self.log_interval)\n        results = inference_benchmark.run()\n\n        self.assertTrue(isinstance(results, dict))\n        self.assertTrue('avg_fps' in results)\n        self.assertTrue('fps_list' in results)\n        self.assertEqual(len(results['fps_list']), 1)\n        self.assertTrue(inference_benchmark.data_loader.num_workers == 0)\n        self.assertTrue(inference_benchmark.data_loader.batch_size == 1)\n\n        results = inference_benchmark.run(1)\n        self.assertTrue('avg_fps' in results)\n        self.assertTrue('fps_list' in results)\n        self.assertEqual(len(results['fps_list']), 1)\n        self.assertTrue(inference_benchmark.data_loader.num_workers == 0)\n        self.assertTrue(inference_benchmark.data_loader.batch_size == 1)\n\n        # test repeat\n        results = inference_benchmark.run(3)\n        self.assertTrue('avg_fps' in results)\n        self.assertTrue('fps_list' in results)\n        self.assertEqual(len(results['fps_list']), 3)\n\n        # test cudnn_benchmark\n        cfg = copy.deepcopy(self.cfg)\n        cfg.env_cfg.cudnn_benchmark = True\n        inference_benchmark = InferenceBenchmark(cfg, checkpoint_path, False,\n                                                 False, self.max_iter,\n                                                 self.log_interval)\n        inference_benchmark.run(1)\n\n        # test mp_cfg\n        cfg = copy.deepcopy(self.cfg)\n        cfg.env_cfg.cudnn_benchmark = True\n        cfg.env_cfg.mp_cfg = {\n            'mp_start_method': 'fork',\n            'opencv_num_threads': 1\n        }\n        inference_benchmark = InferenceBenchmark(cfg, checkpoint_path, False,\n                                                 False, self.max_iter,\n                                                 self.log_interval)\n        inference_benchmark.run(1)\n\n        # test fp16\n        cfg = copy.deepcopy(self.cfg)\n        cfg.fp16 = True\n        inference_benchmark = InferenceBenchmark(cfg, checkpoint_path, False,\n                                                 False, self.max_iter,\n                                                 self.log_interval)\n        inference_benchmark.run(1)\n\n        # test logger\n        logger = MMLogger.get_instance(\n            'mmdet', log_file='temp.log', log_level='INFO')\n        inference_benchmark = InferenceBenchmark(\n            cfg,\n            checkpoint_path,\n            False,\n            False,\n            self.max_iter,\n            self.log_interval,\n            logger=logger)\n        inference_benchmark.run(1)\n        self.assertTrue(os.path.exists('temp.log'))\n\n        os.remove(checkpoint_path)\n        os.remove('temp.log')\n\n\nclass TestDataLoaderBenchmark(unittest.TestCase):\n\n    def setUp(self) -> None:\n        register_all_modules()\n\n        self.cfg = Config(\n            dict(\n                model=dict(type='ToyDetector'),\n                train_dataloader=dict(\n                    dataset=dict(type='ToyDataset'),\n                    sampler=dict(type='DefaultSampler', shuffle=True),\n                    batch_size=2,\n                    num_workers=1),\n                val_dataloader=dict(\n                    dataset=dict(type='ToyDataset'),\n                    sampler=dict(type='DefaultSampler', shuffle=False),\n                    batch_size=1,\n                    num_workers=2),\n                test_dataloader=dict(\n                    dataset=dict(type='ToyDataset'),\n                    sampler=dict(type='DefaultSampler', shuffle=False),\n                    batch_size=3,\n                    num_workers=1),\n                env_cfg=dict(dist_cfg=dict(backend='nccl'))))\n        self.max_iter = 5\n        self.log_interval = 1\n        self.num_warmup = 1\n\n    def test_init_and_run(self):\n        cfg = copy.deepcopy(self.cfg)\n        dataloader_benchmark = DataLoaderBenchmark(cfg, False, 'train',\n                                                   self.max_iter,\n                                                   self.log_interval,\n                                                   self.num_warmup)\n        results = dataloader_benchmark.run(1)\n        self.assertTrue('avg_fps' in results)\n        self.assertTrue('fps_list' in results)\n        self.assertEqual(len(results['fps_list']), 1)\n        self.assertTrue(dataloader_benchmark.data_loader.num_workers == 1)\n        self.assertTrue(dataloader_benchmark.data_loader.batch_size == 2)\n\n        # test repeat\n        results = dataloader_benchmark.run(3)\n        self.assertTrue('avg_fps' in results)\n        self.assertTrue('fps_list' in results)\n        self.assertEqual(len(results['fps_list']), 3)\n\n        # test dataset_type input parameters error\n        with self.assertRaises(AssertionError):\n            DataLoaderBenchmark(cfg, False, 'training', self.max_iter,\n                                self.log_interval, self.num_warmup)\n\n        dataloader_benchmark = DataLoaderBenchmark(cfg, False, 'val',\n                                                   self.max_iter,\n                                                   self.log_interval,\n                                                   self.num_warmup)\n        self.assertTrue(dataloader_benchmark.data_loader.num_workers == 2)\n        self.assertTrue(dataloader_benchmark.data_loader.batch_size == 1)\n\n        dataloader_benchmark = DataLoaderBenchmark(cfg, False, 'test',\n                                                   self.max_iter,\n                                                   self.log_interval,\n                                                   self.num_warmup)\n        self.assertTrue(dataloader_benchmark.data_loader.num_workers == 1)\n        self.assertTrue(dataloader_benchmark.data_loader.batch_size == 3)\n\n        # test mp_cfg\n        cfg = copy.deepcopy(self.cfg)\n        cfg.env_cfg.mp_cfg = {\n            'mp_start_method': 'fork',\n            'opencv_num_threads': 1\n        }\n        dataloader_benchmark = DataLoaderBenchmark(cfg, False, 'train',\n                                                   self.max_iter,\n                                                   self.log_interval,\n                                                   self.num_warmup)\n        dataloader_benchmark.run(1)\n\n\nclass TestDatasetBenchmark(unittest.TestCase):\n\n    def setUp(self) -> None:\n        register_all_modules()\n\n        self.cfg = Config(\n            dict(\n                model=dict(type='ToyDetector'),\n                train_dataloader=dict(\n                    dataset=dict(type='ToyDataset'),\n                    sampler=dict(type='DefaultSampler', shuffle=True),\n                    batch_size=2,\n                    num_workers=1),\n                val_dataloader=dict(\n                    dataset=dict(type='ToyDataset'),\n                    sampler=dict(type='DefaultSampler', shuffle=False),\n                    batch_size=1,\n                    num_workers=2),\n                test_dataloader=dict(\n                    dataset=dict(type='ToyDataset'),\n                    sampler=dict(type='DefaultSampler', shuffle=False),\n                    batch_size=3,\n                    num_workers=1)))\n        self.max_iter = 5\n        self.log_interval = 1\n        self.num_warmup = 1\n\n    def test_init_and_run(self):\n        cfg = copy.deepcopy(self.cfg)\n        dataset_benchmark = DatasetBenchmark(cfg, 'train', self.max_iter,\n                                             self.log_interval,\n                                             self.num_warmup)\n        results = dataset_benchmark.run(1)\n        self.assertTrue('avg_fps' in results)\n        self.assertTrue('fps_list' in results)\n        self.assertEqual(len(results['fps_list']), 1)\n\n        # test repeat\n        results = dataset_benchmark.run(3)\n        self.assertTrue('avg_fps' in results)\n        self.assertTrue('fps_list' in results)\n        self.assertEqual(len(results['fps_list']), 3)\n\n        # test test dataset\n        dataset_benchmark = DatasetBenchmark(cfg, 'test', self.max_iter,\n                                             self.log_interval,\n                                             self.num_warmup)\n        dataset_benchmark.run(1)\n\n        # test val dataset\n        dataset_benchmark = DatasetBenchmark(cfg, 'val', self.max_iter,\n                                             self.log_interval,\n                                             self.num_warmup)\n        dataset_benchmark.run(1)\n\n        # test dataset_type input parameters error\n        with self.assertRaises(AssertionError):\n            DatasetBenchmark(cfg, 'training', self.max_iter, self.log_interval,\n                             self.num_warmup)\n\n        # test full_init\n        cfg = copy.deepcopy(self.cfg)\n        cfg.test_dataloader.dataset = dict(type='ToyFullInitDataset')\n        dataset_benchmark = DatasetBenchmark(cfg, 'train', self.max_iter,\n                                             self.log_interval,\n                                             self.num_warmup)\n        dataset_benchmark.run(1)\n"
  },
  {
    "path": "tests/test_utils/test_memory.py",
    "content": "import numpy as np\nimport pytest\nimport torch\n\nfrom mmdet.utils import AvoidOOM\nfrom mmdet.utils.memory import cast_tensor_type\n\n\ndef test_avoidoom():\n    tensor = torch.from_numpy(np.random.random((20, 20)))\n    if torch.cuda.is_available():\n        tensor = tensor.cuda()\n        # get default result\n        default_result = torch.mm(tensor, tensor.transpose(1, 0))\n\n        # when not occurred OOM error\n        AvoidCudaOOM = AvoidOOM()\n        result = AvoidCudaOOM.retry_if_cuda_oom(torch.mm)(tensor,\n                                                          tensor.transpose(\n                                                              1, 0))\n        assert default_result.device == result.device and \\\n               default_result.dtype == result.dtype and \\\n               torch.equal(default_result, result)\n\n        # calculate with fp16 and convert back to source type\n        AvoidCudaOOM = AvoidOOM(test=True)\n        result = AvoidCudaOOM.retry_if_cuda_oom(torch.mm)(tensor,\n                                                          tensor.transpose(\n                                                              1, 0))\n        assert default_result.device == result.device and \\\n               default_result.dtype == result.dtype and \\\n               torch.allclose(default_result, result, 1e-3)\n\n        # calculate on cpu and convert back to source device\n        AvoidCudaOOM = AvoidOOM(test=True)\n        result = AvoidCudaOOM.retry_if_cuda_oom(torch.mm)(tensor,\n                                                          tensor.transpose(\n                                                              1, 0))\n        assert result.dtype == default_result.dtype and \\\n               result.device == default_result.device and \\\n               torch.allclose(default_result, result)\n\n        # do not calculate on cpu and the outputs will be same as input\n        AvoidCudaOOM = AvoidOOM(test=True, to_cpu=False)\n        result = AvoidCudaOOM.retry_if_cuda_oom(torch.mm)(tensor,\n                                                          tensor.transpose(\n                                                              1, 0))\n        assert result.dtype == default_result.dtype and \\\n               result.device == default_result.device\n\n    else:\n        default_result = torch.mm(tensor, tensor.transpose(1, 0))\n        AvoidCudaOOM = AvoidOOM()\n        result = AvoidCudaOOM.retry_if_cuda_oom(torch.mm)(tensor,\n                                                          tensor.transpose(\n                                                              1, 0))\n        assert default_result.device == result.device and \\\n               default_result.dtype == result.dtype and \\\n               torch.equal(default_result, result)\n\n\ndef test_cast_tensor_type():\n    inputs = torch.rand(10)\n    if torch.cuda.is_available():\n        inputs = inputs.cuda()\n    with pytest.raises(AssertionError):\n        cast_tensor_type(inputs, src_type=None, dst_type=None)\n    # input is a float\n    out = cast_tensor_type(10., dst_type=torch.half)\n    assert out == 10. and isinstance(out, float)\n    # convert Tensor to fp16 and re-convert to fp32\n    fp16_out = cast_tensor_type(inputs, dst_type=torch.half)\n    assert fp16_out.dtype == torch.half\n    fp32_out = cast_tensor_type(fp16_out, dst_type=torch.float32)\n    assert fp32_out.dtype == torch.float32\n\n    # input is a list\n    list_input = [inputs, inputs]\n    list_outs = cast_tensor_type(list_input, dst_type=torch.half)\n    assert len(list_outs) == len(list_input) and \\\n           isinstance(list_outs, list)\n    for out in list_outs:\n        assert out.dtype == torch.half\n    # input is a dict\n    dict_input = {'test1': inputs, 'test2': inputs}\n    dict_outs = cast_tensor_type(dict_input, dst_type=torch.half)\n    assert len(dict_outs) == len(dict_input) and \\\n           isinstance(dict_outs, dict)\n\n    # convert the input tensor to CPU and re-convert to GPU\n    if torch.cuda.is_available():\n        cpu_device = torch.empty(0).device\n        gpu_device = inputs.device\n        cpu_out = cast_tensor_type(inputs, dst_type=cpu_device)\n        assert cpu_out.device == cpu_device\n\n        gpu_out = cast_tensor_type(inputs, dst_type=gpu_device)\n        assert gpu_out.device == gpu_device\n"
  },
  {
    "path": "tests/test_utils/test_replace_cfg_vals.py",
    "content": "import os.path as osp\nimport tempfile\nfrom copy import deepcopy\n\nimport pytest\nfrom mmengine.config import Config\n\nfrom mmdet.utils import replace_cfg_vals\n\n\ndef test_replace_cfg_vals():\n    temp_file = tempfile.NamedTemporaryFile()\n    cfg_path = f'{temp_file.name}.py'\n    with open(cfg_path, 'w') as f:\n        f.write('configs')\n\n    ori_cfg_dict = dict()\n    ori_cfg_dict['cfg_name'] = osp.basename(temp_file.name)\n    ori_cfg_dict['work_dir'] = 'work_dirs/${cfg_name}/${percent}/${fold}'\n    ori_cfg_dict['percent'] = 5\n    ori_cfg_dict['fold'] = 1\n    ori_cfg_dict['model_wrapper'] = dict(\n        type='SoftTeacher', detector='${model}')\n    ori_cfg_dict['model'] = dict(\n        type='FasterRCNN',\n        backbone=dict(type='ResNet'),\n        neck=dict(type='FPN'),\n        rpn_head=dict(type='RPNHead'),\n        roi_head=dict(type='StandardRoIHead'),\n        train_cfg=dict(\n            rpn=dict(\n                assigner=dict(type='MaxIoUAssigner'),\n                sampler=dict(type='RandomSampler'),\n            ),\n            rpn_proposal=dict(nms=dict(type='nms', iou_threshold=0.7)),\n            rcnn=dict(\n                assigner=dict(type='MaxIoUAssigner'),\n                sampler=dict(type='RandomSampler'),\n            ),\n        ),\n        test_cfg=dict(\n            rpn=dict(nms=dict(type='nms', iou_threshold=0.7)),\n            rcnn=dict(nms=dict(type='nms', iou_threshold=0.5)),\n        ),\n    )\n    ori_cfg_dict['iou_threshold'] = dict(\n        rpn_proposal_nms='${model.train_cfg.rpn_proposal.nms.iou_threshold}',\n        test_rpn_nms='${model.test_cfg.rpn.nms.iou_threshold}',\n        test_rcnn_nms='${model.test_cfg.rcnn.nms.iou_threshold}',\n    )\n\n    ori_cfg_dict['str'] = 'Hello, world!'\n    ori_cfg_dict['dict'] = {'Hello': 'world!'}\n    ori_cfg_dict['list'] = [\n        'Hello, world!',\n    ]\n    ori_cfg_dict['tuple'] = ('Hello, world!', )\n    ori_cfg_dict['test_str'] = 'xxx${str}xxx'\n\n    ori_cfg = Config(ori_cfg_dict, filename=cfg_path)\n    updated_cfg = replace_cfg_vals(deepcopy(ori_cfg))\n\n    assert updated_cfg.work_dir \\\n        == f'work_dirs/{osp.basename(temp_file.name)}/5/1'\n    assert updated_cfg.model.detector == ori_cfg.model\n    assert updated_cfg.iou_threshold.rpn_proposal_nms \\\n        == ori_cfg.model.train_cfg.rpn_proposal.nms.iou_threshold\n    assert updated_cfg.test_str == 'xxxHello, world!xxx'\n    ori_cfg_dict['test_dict'] = 'xxx${dict}xxx'\n    ori_cfg_dict['test_list'] = 'xxx${list}xxx'\n    ori_cfg_dict['test_tuple'] = 'xxx${tuple}xxx'\n    with pytest.raises(AssertionError):\n        cfg = deepcopy(ori_cfg)\n        cfg['test_dict'] = 'xxx${dict}xxx'\n        updated_cfg = replace_cfg_vals(cfg)\n    with pytest.raises(AssertionError):\n        cfg = deepcopy(ori_cfg)\n        cfg['test_list'] = 'xxx${list}xxx'\n        updated_cfg = replace_cfg_vals(cfg)\n    with pytest.raises(AssertionError):\n        cfg = deepcopy(ori_cfg)\n        cfg['test_tuple'] = 'xxx${tuple}xxx'\n        updated_cfg = replace_cfg_vals(cfg)\n"
  },
  {
    "path": "tests/test_utils/test_setup_env.py",
    "content": "import datetime\nimport sys\nfrom unittest import TestCase\n\nfrom mmengine import DefaultScope\n\nfrom mmdet.utils import register_all_modules\n\n\nclass TestSetupEnv(TestCase):\n\n    def test_register_all_modules(self):\n        from mmdet.registry import DATASETS\n\n        # not init default scope\n        sys.modules.pop('mmdet.datasets', None)\n        sys.modules.pop('mmdet.datasets.coco', None)\n        DATASETS._module_dict.pop('CocoDataset', None)\n        self.assertFalse('CocoDataset' in DATASETS.module_dict)\n        register_all_modules(init_default_scope=False)\n        self.assertTrue('CocoDataset' in DATASETS.module_dict)\n\n        # init default scope\n        sys.modules.pop('mmdet.datasets')\n        sys.modules.pop('mmdet.datasets.coco')\n        DATASETS._module_dict.pop('CocoDataset', None)\n        self.assertFalse('CocoDataset' in DATASETS.module_dict)\n        register_all_modules(init_default_scope=True)\n        self.assertTrue('CocoDataset' in DATASETS.module_dict)\n        self.assertEqual(DefaultScope.get_current_instance().scope_name,\n                         'mmdet')\n\n        # init default scope when another scope is init\n        name = f'test-{datetime.datetime.now()}'\n        DefaultScope.get_instance(name, scope_name='test')\n        with self.assertWarnsRegex(\n                Warning, 'The current default scope \"test\" is not \"mmdet\"'):\n            register_all_modules(init_default_scope=True)\n"
  },
  {
    "path": "tests/test_visualization/test_local_visualizer.py",
    "content": "import os\nfrom unittest import TestCase\n\nimport cv2\nimport numpy as np\nimport torch\nfrom mmengine.structures import InstanceData, PixelData\n\nfrom mmdet.evaluation import INSTANCE_OFFSET\nfrom mmdet.structures import DetDataSample\nfrom mmdet.visualization import DetLocalVisualizer, TrackLocalVisualizer\n\n\ndef _rand_bboxes(num_boxes, h, w):\n    cx, cy, bw, bh = torch.rand(num_boxes, 4).T\n\n    tl_x = ((cx * w) - (w * bw / 2)).clamp(0, w)\n    tl_y = ((cy * h) - (h * bh / 2)).clamp(0, h)\n    br_x = ((cx * w) + (w * bw / 2)).clamp(0, w)\n    br_y = ((cy * h) + (h * bh / 2)).clamp(0, h)\n\n    bboxes = torch.stack([tl_x, tl_y, br_x, br_y], dim=0).T\n    return bboxes\n\n\ndef _create_panoptic_data(num_boxes, h, w):\n    sem_seg = np.zeros((h, w), dtype=np.int64) + 2\n    bboxes = _rand_bboxes(num_boxes, h, w).int()\n    labels = torch.randint(2, (num_boxes, ))\n    for i in range(num_boxes):\n        x, y, w, h = bboxes[i]\n        sem_seg[y:y + h, x:x + w] = (i + 1) * INSTANCE_OFFSET + labels[i]\n\n    return sem_seg[None]\n\n\nclass TestDetLocalVisualizer(TestCase):\n\n    def test_add_datasample(self):\n        h = 12\n        w = 10\n        num_class = 3\n        num_bboxes = 5\n        out_file = 'out_file.jpg'\n\n        image = np.random.randint(0, 256, size=(h, w, 3)).astype('uint8')\n\n        # test gt_instances\n        gt_instances = InstanceData()\n        gt_instances.bboxes = _rand_bboxes(num_bboxes, h, w)\n        gt_instances.labels = torch.randint(0, num_class, (num_bboxes, ))\n        det_data_sample = DetDataSample()\n        det_data_sample.gt_instances = gt_instances\n\n        det_local_visualizer = DetLocalVisualizer()\n        det_local_visualizer.add_datasample(\n            'image', image, det_data_sample, draw_pred=False)\n\n        # test out_file\n        det_local_visualizer.add_datasample(\n            'image',\n            image,\n            det_data_sample,\n            draw_pred=False,\n            out_file=out_file)\n        assert os.path.exists(out_file)\n        drawn_img = cv2.imread(out_file)\n        assert drawn_img.shape == (h, w, 3)\n        os.remove(out_file)\n\n        # test gt_instances and pred_instances\n        pred_instances = InstanceData()\n        pred_instances.bboxes = _rand_bboxes(num_bboxes, h, w)\n        pred_instances.labels = torch.randint(0, num_class, (num_bboxes, ))\n        pred_instances.scores = torch.rand((num_bboxes, ))\n        det_data_sample.pred_instances = pred_instances\n\n        det_local_visualizer.add_datasample(\n            'image', image, det_data_sample, out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, w * 2, 3))\n\n        det_local_visualizer.add_datasample(\n            'image', image, det_data_sample, draw_gt=False, out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, w, 3))\n\n        det_local_visualizer.add_datasample(\n            'image',\n            image,\n            det_data_sample,\n            draw_pred=False,\n            out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, w, 3))\n\n        # test gt_panoptic_seg and pred_panoptic_seg\n        det_local_visualizer.dataset_meta = dict(classes=('1', '2'))\n        gt_sem_seg = _create_panoptic_data(num_bboxes, h, w)\n        panoptic_seg = PixelData(sem_seg=gt_sem_seg)\n\n        det_data_sample = DetDataSample()\n        det_data_sample.gt_panoptic_seg = panoptic_seg\n\n        pred_sem_seg = _create_panoptic_data(num_bboxes, h, w)\n        panoptic_seg = PixelData(sem_seg=pred_sem_seg)\n        det_data_sample.pred_panoptic_seg = panoptic_seg\n\n        det_local_visualizer.add_datasample(\n            'image', image, det_data_sample, out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, w * 2, 3))\n\n        # class information must be provided\n        det_local_visualizer.dataset_meta = {}\n        with self.assertRaises(AssertionError):\n            det_local_visualizer.add_datasample(\n                'image', image, det_data_sample, out_file=out_file)\n\n    def _assert_image_and_shape(self, out_file, out_shape):\n        assert os.path.exists(out_file)\n        drawn_img = cv2.imread(out_file)\n        assert drawn_img.shape == out_shape\n        os.remove(out_file)\n\n\nclass TestTrackLocalVisualizer(TestCase):\n\n    @staticmethod\n    def _get_gt_instances():\n        bboxes = np.array([[912, 484, 1009, 593], [1338, 418, 1505, 797]])\n        masks = np.zeros((2, 1080, 1920), dtype=np.bool_)\n        for i, bbox in enumerate(bboxes):\n            masks[i, bbox[1]:bbox[3], bbox[0]:bbox[2]] = True\n        instances_data = dict(\n            bboxes=torch.tensor(bboxes),\n            masks=masks,\n            instances_id=torch.tensor([1, 2]),\n            labels=torch.tensor([0, 1]))\n        instances = InstanceData(**instances_data)\n        return instances\n\n    @staticmethod\n    def _get_pred_instances():\n        instances_data = dict(\n            bboxes=torch.tensor([[900, 500, 1000, 600], [1300, 400, 1500,\n                                                         800]]),\n            instances_id=torch.tensor([1, 2]),\n            labels=torch.tensor([0, 1]),\n            scores=torch.tensor([0.955, 0.876]))\n        instances = InstanceData(**instances_data)\n        return instances\n\n    @staticmethod\n    def _assert_image_and_shape(out_file, out_shape):\n        assert os.path.exists(out_file)\n        drawn_img = cv2.imread(out_file)\n        assert drawn_img.shape == out_shape\n        os.remove(out_file)\n\n    def test_add_datasample(self):\n        out_file = 'out_file.jpg'\n        h, w = 1080, 1920\n        image = np.random.randint(0, 256, size=(h, w, 3)).astype('uint8')\n        gt_instances = self._get_gt_instances()\n        pred_instances = self._get_pred_instances()\n        image_data_sample = DetDataSample()\n        image_data_sample.gt_instances = gt_instances\n        image_data_sample.pred_track_instances = pred_instances\n\n        track_local_visualizer = TrackLocalVisualizer(alpha=0.2)\n        track_local_visualizer.dataset_meta = dict(\n            classes=['pedestrian', 'vehicle'])\n\n        # test gt_instances\n        track_local_visualizer.add_datasample('image', image,\n                                              image_data_sample, None)\n\n        # test out_file\n        track_local_visualizer.add_datasample(\n            'image', image, image_data_sample, None, out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, w, 3))\n\n        # test gt_instances and pred_instances\n        track_local_visualizer.add_datasample(\n            'image', image, image_data_sample, out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, 2 * w, 3))\n\n        track_local_visualizer.add_datasample(\n            'image',\n            image,\n            image_data_sample,\n            draw_gt=False,\n            out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, w, 3))\n\n        track_local_visualizer.add_datasample(\n            'image',\n            image,\n            image_data_sample,\n            draw_pred=False,\n            out_file=out_file)\n        self._assert_image_and_shape(out_file, (h, w, 3))\n"
  },
  {
    "path": "tests/test_visualization/test_palette.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport numpy as np\n\nfrom mmdet.datasets import CocoDataset\nfrom mmdet.visualization import get_palette, jitter_color, palette_val\n\n\ndef test_palette():\n    assert palette_val([(1, 2, 3)])[0] == (1 / 255, 2 / 255, 3 / 255)\n\n    # test list\n    palette = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]\n    palette_ = get_palette(palette, 3)\n    for color, color_ in zip(palette, palette_):\n        assert color == color_\n\n    # test tuple\n    palette = get_palette((1, 2, 3), 3)\n    assert len(palette) == 3\n    for color in palette:\n        assert color == (1, 2, 3)\n\n    # test color str\n    palette = get_palette('red', 3)\n    assert len(palette) == 3\n    for color in palette:\n        assert color == (255, 0, 0)\n\n    # test dataset str\n    palette = get_palette('coco', len(CocoDataset.METAINFO['classes']))\n    assert len(palette) == len(CocoDataset.METAINFO['classes'])\n    assert palette[0] == (220, 20, 60)\n\n    # TODO: Awaiting refactoring\n    # palette = get_palette('coco', len(CocoPanopticDataset.METAINFO['CLASSES'])) # noqa\n    # assert len(palette) == len(CocoPanopticDataset.METAINFO['CLASSES'])\n    # assert palette[-1] == (250, 141, 255)\n    # palette = get_palette('voc', len(VOCDataset.METAINFO['CLASSES']))\n    # assert len(palette) == len(VOCDataset.METAINFO['CLASSES'])\n    # assert palette[0] == (106, 0, 228)\n    # palette = get_palette('citys', len(CityscapesDataset.METAINFO['CLASSES'])) # noqa\n    # assert len(palette) == len(CityscapesDataset.METAINFO['CLASSES'])\n    # assert palette[0] == (220, 20, 60)\n\n    # test random\n    palette1 = get_palette('random', 3)\n    palette2 = get_palette(None, 3)\n    for color1, color2 in zip(palette1, palette2):\n        assert isinstance(color1, tuple)\n        assert isinstance(color2, tuple)\n        assert color1 == color2\n\n\ndef test_jitter_color():\n    color = tuple(np.random.randint(0, 255, 3, np.uint8))\n    jittered_color = jitter_color(color)\n    for c in jittered_color:\n        assert 0 <= c <= 255\n"
  },
  {
    "path": "tools/analysis_tools/analyze_logs.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport json\nfrom collections import defaultdict\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\n\n\ndef cal_train_time(log_dicts, args):\n    for i, log_dict in enumerate(log_dicts):\n        print(f'{\"-\" * 5}Analyze train time of {args.json_logs[i]}{\"-\" * 5}')\n        all_times = []\n        for epoch in log_dict.keys():\n            if args.include_outliers:\n                all_times.append(log_dict[epoch]['time'])\n            else:\n                all_times.append(log_dict[epoch]['time'][1:])\n        if not all_times:\n            raise KeyError(\n                'Please reduce the log interval in the config so that'\n                'interval is less than iterations of one epoch.')\n        epoch_ave_time = np.array(list(map(lambda x: np.mean(x), all_times)))\n        slowest_epoch = epoch_ave_time.argmax()\n        fastest_epoch = epoch_ave_time.argmin()\n        std_over_epoch = epoch_ave_time.std()\n        print(f'slowest epoch {slowest_epoch + 1}, '\n              f'average time is {epoch_ave_time[slowest_epoch]:.4f} s/iter')\n        print(f'fastest epoch {fastest_epoch + 1}, '\n              f'average time is {epoch_ave_time[fastest_epoch]:.4f} s/iter')\n        print(f'time std over epochs is {std_over_epoch:.4f}')\n        print(f'average iter time: {np.mean(epoch_ave_time):.4f} s/iter\\n')\n\n\ndef plot_curve(log_dicts, args):\n    if args.backend is not None:\n        plt.switch_backend(args.backend)\n    sns.set_style(args.style)\n    # if legend is None, use {filename}_{key} as legend\n    legend = args.legend\n    if legend is None:\n        legend = []\n        for json_log in args.json_logs:\n            for metric in args.keys:\n                legend.append(f'{json_log}_{metric}')\n    assert len(legend) == (len(args.json_logs) * len(args.keys))\n    metrics = args.keys\n\n    # TODO: support dynamic eval interval(e.g. RTMDet) when plotting mAP.\n    num_metrics = len(metrics)\n    for i, log_dict in enumerate(log_dicts):\n        epochs = list(log_dict.keys())\n        for j, metric in enumerate(metrics):\n            print(f'plot curve of {args.json_logs[i]}, metric is {metric}')\n            if metric not in log_dict[epochs[int(args.eval_interval) - 1]]:\n                if 'mAP' in metric:\n                    raise KeyError(\n                        f'{args.json_logs[i]} does not contain metric '\n                        f'{metric}. Please check if \"--no-validate\" is '\n                        'specified when you trained the model. Or check '\n                        f'if the eval_interval {args.eval_interval} in args '\n                        'is equal to the eval_interval during training.')\n                raise KeyError(\n                    f'{args.json_logs[i]} does not contain metric {metric}. '\n                    'Please reduce the log interval in the config so that '\n                    'interval is less than iterations of one epoch.')\n\n            if 'mAP' in metric:\n                xs = []\n                ys = []\n                for epoch in epochs:\n                    ys += log_dict[epoch][metric]\n                    if log_dict[epoch][metric]:\n                        xs += [epoch]\n                plt.xlabel('epoch')\n                plt.plot(xs, ys, label=legend[i * num_metrics + j], marker='o')\n            else:\n                xs = []\n                ys = []\n                for epoch in epochs:\n                    iters = log_dict[epoch]['step']\n                    xs.append(np.array(iters))\n                    ys.append(np.array(log_dict[epoch][metric][:len(iters)]))\n                xs = np.concatenate(xs)\n                ys = np.concatenate(ys)\n                plt.xlabel('iter')\n                plt.plot(\n                    xs, ys, label=legend[i * num_metrics + j], linewidth=0.5)\n            plt.legend()\n        if args.title is not None:\n            plt.title(args.title)\n    if args.out is None:\n        plt.show()\n    else:\n        print(f'save curve to: {args.out}')\n        plt.savefig(args.out)\n        plt.cla()\n\n\ndef add_plot_parser(subparsers):\n    parser_plt = subparsers.add_parser(\n        'plot_curve', help='parser for plotting curves')\n    parser_plt.add_argument(\n        'json_logs',\n        type=str,\n        nargs='+',\n        help='path of train log in json format')\n    parser_plt.add_argument(\n        '--keys',\n        type=str,\n        nargs='+',\n        default=['bbox_mAP'],\n        help='the metric that you want to plot')\n    parser_plt.add_argument(\n        '--start-epoch',\n        type=str,\n        default='1',\n        help='the epoch that you want to start')\n    parser_plt.add_argument(\n        '--eval-interval',\n        type=str,\n        default='1',\n        help='the eval interval when training')\n    parser_plt.add_argument('--title', type=str, help='title of figure')\n    parser_plt.add_argument(\n        '--legend',\n        type=str,\n        nargs='+',\n        default=None,\n        help='legend of each plot')\n    parser_plt.add_argument(\n        '--backend', type=str, default=None, help='backend of plt')\n    parser_plt.add_argument(\n        '--style', type=str, default='dark', help='style of plt')\n    parser_plt.add_argument('--out', type=str, default=None)\n\n\ndef add_time_parser(subparsers):\n    parser_time = subparsers.add_parser(\n        'cal_train_time',\n        help='parser for computing the average time per training iteration')\n    parser_time.add_argument(\n        'json_logs',\n        type=str,\n        nargs='+',\n        help='path of train log in json format')\n    parser_time.add_argument(\n        '--include-outliers',\n        action='store_true',\n        help='include the first value of every epoch when computing '\n        'the average time')\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Analyze Json Log')\n    # currently only support plot curve and calculate average train time\n    subparsers = parser.add_subparsers(dest='task', help='task parser')\n    add_plot_parser(subparsers)\n    add_time_parser(subparsers)\n    args = parser.parse_args()\n    return args\n\n\ndef load_json_logs(json_logs):\n    # load and convert json_logs to log_dict, key is epoch, value is a sub dict\n    # keys of sub dict is different metrics, e.g. memory, bbox_mAP\n    # value of sub dict is a list of corresponding values of all iterations\n    log_dicts = [dict() for _ in json_logs]\n    for json_log, log_dict in zip(json_logs, log_dicts):\n        with open(json_log, 'r') as log_file:\n            epoch = 1\n            for i, line in enumerate(log_file):\n                log = json.loads(line.strip())\n                val_flag = False\n                # skip lines only contains one key\n                if not len(log) > 1:\n                    continue\n\n                if epoch not in log_dict:\n                    log_dict[epoch] = defaultdict(list)\n\n                for k, v in log.items():\n                    if '/' in k:\n                        log_dict[epoch][k.split('/')[-1]].append(v)\n                        val_flag = True\n                    elif val_flag:\n                        continue\n                    else:\n                        log_dict[epoch][k].append(v)\n\n                if 'epoch' in log.keys():\n                    epoch = log['epoch']\n\n    return log_dicts\n\n\ndef main():\n    args = parse_args()\n\n    json_logs = args.json_logs\n    for json_log in json_logs:\n        assert json_log.endswith('.json')\n\n    log_dicts = load_json_logs(json_logs)\n\n    eval(args.task)(log_dicts, args)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/analyze_results.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os.path as osp\nfrom multiprocessing import Pool\n\nimport mmcv\nimport numpy as np\nfrom mmengine.config import Config, DictAction\nfrom mmengine.fileio import load\nfrom mmengine.registry import init_default_scope\nfrom mmengine.runner import Runner\nfrom mmengine.structures import InstanceData, PixelData\nfrom mmengine.utils import ProgressBar, check_file_exist, mkdir_or_exist\n\nfrom mmdet.datasets import get_loading_pipeline\nfrom mmdet.evaluation import eval_map\nfrom mmdet.registry import DATASETS, RUNNERS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.utils import replace_cfg_vals, update_data_root\nfrom mmdet.visualization import DetLocalVisualizer\n\n\ndef bbox_map_eval(det_result, annotation, nproc=4):\n    \"\"\"Evaluate mAP of single image det result.\n\n    Args:\n        det_result (list[list]): [[cls1_det, cls2_det, ...], ...].\n            The outer list indicates images, and the inner list indicates\n            per-class detected bboxes.\n        annotation (dict): Ground truth annotations where keys of\n             annotations are:\n\n            - bboxes: numpy array of shape (n, 4)\n            - labels: numpy array of shape (n, )\n            - bboxes_ignore (optional): numpy array of shape (k, 4)\n            - labels_ignore (optional): numpy array of shape (k, )\n\n        nproc (int): Processes used for computing mAP.\n            Default: 4.\n\n    Returns:\n        float: mAP\n    \"\"\"\n\n    # use only bbox det result\n    if isinstance(det_result, tuple):\n        bbox_det_result = [det_result[0]]\n    else:\n        bbox_det_result = [det_result]\n    # mAP\n    iou_thrs = np.linspace(\n        .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True)\n\n    processes = []\n    workers = Pool(processes=nproc)\n    for thr in iou_thrs:\n        p = workers.apply_async(eval_map, (bbox_det_result, [annotation]), {\n            'iou_thr': thr,\n            'logger': 'silent',\n            'nproc': 1\n        })\n        processes.append(p)\n\n    workers.close()\n    workers.join()\n\n    mean_aps = []\n    for p in processes:\n        mean_aps.append(p.get()[0])\n\n    return sum(mean_aps) / len(mean_aps)\n\n\nclass ResultVisualizer:\n    \"\"\"Display and save evaluation results.\n\n    Args:\n        show (bool): Whether to show the image. Default: True.\n        wait_time (float): Value of waitKey param. Default: 0.\n        score_thr (float): Minimum score of bboxes to be shown.\n           Default: 0.\n        runner (:obj:`Runner`): The runner of the visualization process.\n    \"\"\"\n\n    def __init__(self, show=False, wait_time=0, score_thr=0, runner=None):\n        self.show = show\n        self.wait_time = wait_time\n        self.score_thr = score_thr\n        self.visualizer = DetLocalVisualizer()\n        self.runner = runner\n        self.evaluator = runner.test_evaluator\n\n    def _save_image_gts_results(self,\n                                dataset,\n                                results,\n                                performances,\n                                out_dir=None,\n                                task='det'):\n        \"\"\"Display or save image with groung truths and predictions from a\n        model.\n\n        Args:\n            dataset (Dataset): A PyTorch dataset.\n            results (list): Object detection or panoptic segmentation\n                results from test results pkl file.\n            performances (dict): A dict contains samples's indices\n                in dataset and model's performance on them.\n            out_dir (str, optional): The filename to write the image.\n                Defaults: None.\n            task (str): The task to be performed. Defaults: 'det'\n        \"\"\"\n        mkdir_or_exist(out_dir)\n\n        for performance_info in performances:\n            index, performance = performance_info\n            data_info = dataset[index]\n            data_info['gt_instances'] = data_info['instances']\n\n            # calc save file path\n            filename = data_info['img_path']\n            fname, name = osp.splitext(osp.basename(filename))\n            save_filename = fname + '_' + str(round(performance, 3)) + name\n            out_file = osp.join(out_dir, save_filename)\n\n            if task == 'det':\n                gt_instances = InstanceData()\n                gt_instances.bboxes = [\n                    d['bbox'] for d in data_info['gt_instances']\n                ]\n                gt_instances.labels = [\n                    d['bbox_label'] for d in data_info['gt_instances']\n                ]\n\n                pred_instances = InstanceData()\n                pred_instances.bboxes = results[index]['pred_instances'][\n                    'bboxes']\n                pred_instances.labels = results[index]['pred_instances'][\n                    'labels']\n                pred_instances.scores = results[index]['pred_instances'][\n                    'scores']\n\n                data_samples = DetDataSample()\n                data_samples.pred_instances = pred_instances\n                data_samples.gt_instances = gt_instances\n\n            elif task == 'seg':\n                gt_panoptic_seg = PixelData()\n                gt_panoptic_seg.sem_seg = [\n                    d['gt_seg_map'] for d in data_info['gt_instances']\n                ]\n\n                pred_panoptic_seg = PixelData()\n                pred_panoptic_seg.sem_seg = results[index][\n                    'pred_panoptic_seg']['sem_seg']\n\n                data_samples = DetDataSample()\n                data_samples.pred_panoptic_seg = pred_panoptic_seg\n                data_samples.gt_panoptic_seg = gt_panoptic_seg\n\n            img = mmcv.imread(filename, channel_order='rgb')\n            self.visualizer.add_datasample(\n                'image',\n                img,\n                data_samples,\n                show=self.show,\n                draw_gt=False,\n                pred_score_thr=self.score_thr,\n                out_file=out_file)\n\n    def evaluate_and_show(self,\n                          dataset,\n                          results,\n                          topk=20,\n                          show_dir='work_dir'):\n        \"\"\"Evaluate and show results.\n\n        Args:\n            dataset (Dataset): A PyTorch dataset.\n            results (list): Object detection or panoptic segmentation\n                results from test results pkl file.\n            topk (int): Number of the highest topk and\n                lowest topk after evaluation index sorting. Default: 20.\n            show_dir (str, optional): The filename to write the image.\n                Default: 'work_dir'\n        \"\"\"\n\n        self.visualizer.dataset_meta = dataset.metainfo\n\n        assert topk > 0\n        if (topk * 2) > len(dataset):\n            topk = len(dataset) // 2\n\n        good_dir = osp.abspath(osp.join(show_dir, 'good'))\n        bad_dir = osp.abspath(osp.join(show_dir, 'bad'))\n\n        if 'pred_panoptic_seg' in results[0].keys():\n            good_samples, bad_samples = self.panoptic_evaluate(\n                dataset, results, topk=topk)\n            self._save_image_gts_results(\n                dataset, results, good_samples, good_dir, task='seg')\n            self._save_image_gts_results(\n                dataset, results, bad_samples, bad_dir, task='seg')\n        elif 'pred_instances' in results[0].keys():\n            good_samples, bad_samples = self.detection_evaluate(\n                dataset, results, topk=topk)\n            self._save_image_gts_results(\n                dataset, results, good_samples, good_dir, task='det')\n            self._save_image_gts_results(\n                dataset, results, bad_samples, bad_dir, task='det')\n        else:\n            raise 'expect \\'pred_panoptic_seg\\' or \\'pred_instances\\' \\\n                in dict result'\n\n    def detection_evaluate(self, dataset, results, topk=20, eval_fn=None):\n        \"\"\"Evaluation for object detection.\n\n        Args:\n            dataset (Dataset): A PyTorch dataset.\n            results (list): Object detection results from test\n                results pkl file.\n            topk (int): Number of the highest topk and\n                lowest topk after evaluation index sorting. Default: 20.\n            eval_fn (callable, optional): Eval function, Default: None.\n\n        Returns:\n            tuple: A tuple contains good samples and bad samples.\n                good_mAPs (dict[int, float]): A dict contains good\n                    samples's indices in dataset and model's\n                    performance on them.\n                bad_mAPs (dict[int, float]): A dict contains bad\n                    samples's indices in dataset and model's\n                    performance on them.\n        \"\"\"\n\n        if eval_fn is None:\n            eval_fn = bbox_map_eval\n        else:\n            assert callable(eval_fn)\n\n        prog_bar = ProgressBar(len(results))\n        _mAPs = {}\n        data_info = {}\n        for i, (result, ) in enumerate(zip(results)):\n\n            # self.dataset[i] should not call directly\n            # because there is a risk of mismatch\n            data_info = dataset.prepare_data(i)\n            data_info['bboxes'] = data_info['gt_bboxes'].tensor\n            data_info['labels'] = data_info['gt_bboxes_labels']\n\n            pred = result['pred_instances']\n            pred_bboxes = pred['bboxes'].cpu().numpy()\n            pred_scores = pred['scores'].cpu().numpy()\n            pred_labels = pred['labels'].cpu().numpy()\n\n            dets = []\n            for label in range(len(dataset.metainfo['classes'])):\n                index = np.where(pred_labels == label)[0]\n                pred_bbox_scores = np.hstack(\n                    [pred_bboxes[index], pred_scores[index].reshape((-1, 1))])\n                dets.append(pred_bbox_scores)\n            mAP = eval_fn(dets, data_info)\n\n            _mAPs[i] = mAP\n            prog_bar.update()\n        # descending select topk image\n        _mAPs = list(sorted(_mAPs.items(), key=lambda kv: kv[1]))\n        good_mAPs = _mAPs[-topk:]\n        bad_mAPs = _mAPs[:topk]\n\n        return good_mAPs, bad_mAPs\n\n    def panoptic_evaluate(self, dataset, results, topk=20):\n        \"\"\"Evaluation for panoptic segmentation.\n\n        Args:\n            dataset (Dataset): A PyTorch dataset.\n            results (list): Panoptic segmentation results from test\n                results pkl file.\n            topk (int): Number of the highest topk and\n                lowest topk after evaluation index sorting. Default: 20.\n\n        Returns:\n            tuple: A tuple contains good samples and bad samples.\n                good_pqs (dict[int, float]): A dict contains good\n                    samples's indices in dataset and model's\n                    performance on them.\n                bad_pqs (dict[int, float]): A dict contains bad\n                    samples's indices in dataset and model's\n                    performance on them.\n        \"\"\"\n        pqs = {}\n        prog_bar = ProgressBar(len(results))\n\n        for i in range(len(results)):\n            data_sample = {}\n            for k in dataset[i].keys():\n                data_sample[k] = dataset[i][k]\n\n            for k in results[i].keys():\n                data_sample[k] = results[i][k]\n\n            self.evaluator.process([data_sample])\n            metrics = self.evaluator.evaluate(1)\n\n            pqs[i] = metrics['coco_panoptic/PQ']\n            prog_bar.update()\n\n        # descending select topk image\n        pqs = list(sorted(pqs.items(), key=lambda kv: kv[1]))\n        good_pqs = pqs[-topk:]\n        bad_pqs = pqs[:topk]\n\n        return good_pqs, bad_pqs\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMDet eval image prediction result for each')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument(\n        'prediction_path', help='prediction path where test pkl result')\n    parser.add_argument(\n        'show_dir', help='directory where painted images will be saved')\n    parser.add_argument('--show', action='store_true', help='show results')\n    parser.add_argument(\n        '--wait-time',\n        type=float,\n        default=0,\n        help='the interval of show (s), 0 is block')\n    parser.add_argument(\n        '--topk',\n        default=20,\n        type=int,\n        help='saved Number of the highest topk '\n        'and lowest topk after index sorting')\n    parser.add_argument(\n        '--show-score-thr',\n        type=float,\n        default=0,\n        help='score threshold (default: 0.)')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    check_file_exist(args.prediction_path)\n\n    cfg = Config.fromfile(args.config)\n\n    # replace the ${key} with the value of cfg.key\n    cfg = replace_cfg_vals(cfg)\n\n    # update data root according to MMDET_DATASETS\n    update_data_root(cfg)\n\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    cfg.test_dataloader.dataset.test_mode = True\n\n    cfg.test_dataloader.pop('batch_size', 0)\n    if cfg.train_dataloader.dataset.type in ('MultiImageMixDataset',\n                                             'ClassBalancedDataset',\n                                             'RepeatDataset'):\n        cfg.test_dataloader.dataset.pipeline = get_loading_pipeline(\n            cfg.train_dataloader.dataset.dataset.pipeline)\n    elif cfg.train_dataloader.dataset.type in ('ConcatDataset', ):\n        cfg.test_dataloader.dataset.pipeline = get_loading_pipeline(\n            cfg.train_dataloader.dataset.datasets[0].pipeline)\n    else:\n        cfg.test_dataloader.dataset.pipeline = get_loading_pipeline(\n            cfg.train_dataloader.dataset.pipeline)\n    dataset = DATASETS.build(cfg.test_dataloader.dataset)\n    outputs = load(args.prediction_path)\n\n    cfg.work_dir = args.show_dir\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    result_visualizer = ResultVisualizer(args.show, args.wait_time,\n                                         args.show_score_thr, runner)\n    result_visualizer.evaluate_and_show(\n        dataset, outputs, topk=args.topk, show_dir=args.show_dir)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/benchmark.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\n\nfrom mmengine import MMLogger\nfrom mmengine.config import Config, DictAction\nfrom mmengine.dist import init_dist\nfrom mmengine.registry import init_default_scope\nfrom mmengine.utils import mkdir_or_exist\n\nfrom mmdet.utils.benchmark import (DataLoaderBenchmark, DatasetBenchmark,\n                                   InferenceBenchmark)\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='MMDet benchmark')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('--checkpoint', help='checkpoint file')\n    parser.add_argument(\n        '--task',\n        choices=['inference', 'dataloader', 'dataset'],\n        default='dataloader',\n        help='Which task do you want to go to benchmark')\n    parser.add_argument(\n        '--repeat-num',\n        type=int,\n        default=1,\n        help='number of repeat times of measurement for averaging the results')\n    parser.add_argument(\n        '--max-iter', type=int, default=2000, help='num of max iter')\n    parser.add_argument(\n        '--log-interval', type=int, default=50, help='interval of logging')\n    parser.add_argument(\n        '--num-warmup', type=int, default=5, help='Number of warmup')\n    parser.add_argument(\n        '--fuse-conv-bn',\n        action='store_true',\n        help='Whether to fuse conv and bn, this will slightly increase'\n        'the inference speed')\n    parser.add_argument(\n        '--dataset-type',\n        choices=['train', 'val', 'test'],\n        default='test',\n        help='Benchmark dataset type. only supports train, val and test')\n    parser.add_argument(\n        '--work-dir',\n        help='the directory to save the file containing '\n        'benchmark metrics')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--local_rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    return args\n\n\ndef inference_benchmark(args, cfg, distributed, logger):\n    benchmark = InferenceBenchmark(\n        cfg,\n        args.checkpoint,\n        distributed,\n        args.fuse_conv_bn,\n        args.max_iter,\n        args.log_interval,\n        args.num_warmup,\n        logger=logger)\n    return benchmark\n\n\ndef dataloader_benchmark(args, cfg, distributed, logger):\n    benchmark = DataLoaderBenchmark(\n        cfg,\n        distributed,\n        args.dataset_type,\n        args.max_iter,\n        args.log_interval,\n        args.num_warmup,\n        logger=logger)\n    return benchmark\n\n\ndef dataset_benchmark(args, cfg, distributed, logger):\n    benchmark = DatasetBenchmark(\n        cfg,\n        args.dataset_type,\n        args.max_iter,\n        args.log_interval,\n        args.num_warmup,\n        logger=logger)\n    return benchmark\n\n\ndef main():\n    args = parse_args()\n    cfg = Config.fromfile(args.config)\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    distributed = False\n    if args.launcher != 'none':\n        init_dist(args.launcher, **cfg.get('env_cfg', {}).get('dist_cfg', {}))\n        distributed = True\n\n    log_file = None\n    if args.work_dir:\n        log_file = os.path.join(args.work_dir, 'benchmark.log')\n        mkdir_or_exist(args.work_dir)\n\n    logger = MMLogger.get_instance(\n        'mmdet', log_file=log_file, log_level='INFO')\n\n    benchmark = eval(f'{args.task}_benchmark')(args, cfg, distributed, logger)\n    benchmark.run(args.repeat_num)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/browse_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os.path as osp\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.registry import init_default_scope\nfrom mmengine.utils import ProgressBar\n\nfrom mmdet.models.utils import mask2ndarray\nfrom mmdet.registry import DATASETS, VISUALIZERS\nfrom mmdet.structures.bbox import BaseBoxes\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Browse a dataset')\n    parser.add_argument('config', help='train config file path')\n    parser.add_argument(\n        '--output-dir',\n        default=None,\n        type=str,\n        help='If there is no display interface, you can save it')\n    parser.add_argument('--not-show', default=False, action='store_true')\n    parser.add_argument(\n        '--show-interval',\n        type=float,\n        default=2,\n        help='the interval of show (s)')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    cfg = Config.fromfile(args.config)\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # register all modules in mmdet into the registries\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    dataset = DATASETS.build(cfg.train_dataloader.dataset)\n    visualizer = VISUALIZERS.build(cfg.visualizer)\n    visualizer.dataset_meta = dataset.metainfo\n\n    progress_bar = ProgressBar(len(dataset))\n    for item in dataset:\n        img = item['inputs'].permute(1, 2, 0).numpy()\n        data_sample = item['data_samples'].numpy()\n        gt_instances = data_sample.gt_instances\n        img_path = osp.basename(item['data_samples'].img_path)\n\n        out_file = osp.join(\n            args.output_dir,\n            osp.basename(img_path)) if args.output_dir is not None else None\n\n        img = img[..., [2, 1, 0]]  # bgr to rgb\n        gt_bboxes = gt_instances.get('bboxes', None)\n        if gt_bboxes is not None and isinstance(gt_bboxes, BaseBoxes):\n            gt_instances.bboxes = gt_bboxes.tensor\n        gt_masks = gt_instances.get('masks', None)\n        if gt_masks is not None:\n            masks = mask2ndarray(gt_masks)\n            gt_instances.masks = masks.astype(bool)\n        data_sample.gt_instances = gt_instances\n\n        visualizer.add_datasample(\n            osp.basename(img_path),\n            img,\n            data_sample,\n            draw_pred=False,\n            show=not args.not_show,\n            wait_time=args.show_interval,\n            out_file=out_file)\n\n        progress_bar.update()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/browse_grounding_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os.path as osp\n\nimport numpy as np\nfrom mmcv.image import imwrite\nfrom mmengine.config import Config, DictAction\nfrom mmengine.registry import init_default_scope\nfrom mmengine.utils import ProgressBar\n\nfrom mmdet.registry import DATASETS, VISUALIZERS\nfrom mmdet.structures.bbox import BaseBoxes\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Browse a dataset')\n    parser.add_argument('config', help='train config file path')\n    parser.add_argument(\n        '--output-dir',\n        '-o',\n        default=None,\n        type=str,\n        help='If there is no display interface, you can save it')\n    parser.add_argument('--not-show', default=False, action='store_true')\n    parser.add_argument('--show-num', '-n', type=int, default=30)\n    parser.add_argument('--shuffle', default=False, action='store_true')\n    parser.add_argument(\n        '--show-interval',\n        type=float,\n        default=0,\n        help='the interval of show (s)')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    return args\n\n\ndef draw_all_character(visualizer, characters, w):\n    start_index = 2\n    y_index = 5\n    for char in characters:\n        if isinstance(char, str):\n            visualizer.draw_texts(\n                str(char),\n                positions=np.array([start_index, y_index]),\n                colors=(0, 0, 0),\n                font_families='monospace')\n            start_index += len(char) * 8\n        else:\n            visualizer.draw_texts(\n                str(char[0]),\n                positions=np.array([start_index, y_index]),\n                colors=char[1],\n                font_families='monospace')\n            start_index += len(char[0]) * 8\n\n        if start_index > w - 10:\n            start_index = 2\n            y_index += 15\n\n    drawn_text = visualizer.get_image()\n    return drawn_text\n\n\ndef main():\n    args = parse_args()\n    cfg = Config.fromfile(args.config)\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    assert args.show_num > 0\n\n    # register all modules in mmdet into the registries\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    dataset = DATASETS.build(cfg.train_dataloader.dataset)\n    visualizer = VISUALIZERS.build(cfg.visualizer)\n    visualizer.dataset_meta = dataset.metainfo\n\n    dataset_index = list(range(len(dataset)))\n    if args.shuffle:\n        import random\n        random.shuffle(dataset_index)\n\n    progress_bar = ProgressBar(len(dataset))\n    for i in dataset_index[:args.show_num]:\n        item = dataset[i]\n        img = item['inputs'].permute(1, 2, 0).numpy()\n        data_sample = item['data_samples'].numpy()\n        gt_instances = data_sample.gt_instances\n        tokens_positive = data_sample.tokens_positive\n\n        gt_labels = gt_instances.labels\n\n        base_name = osp.basename(item['data_samples'].img_path)\n        name, extension = osp.splitext(base_name)\n\n        out_file = osp.join(args.output_dir, name + '_' + str(i) +\n                            extension) if args.output_dir is not None else None\n\n        img = img[..., [2, 1, 0]]  # bgr to rgb\n        gt_bboxes = gt_instances.get('bboxes', None)\n        if gt_bboxes is not None and isinstance(gt_bboxes, BaseBoxes):\n            gt_instances.bboxes = gt_bboxes.tensor\n\n        print(data_sample.text)\n\n        dataset_mode = data_sample.dataset_mode\n        if dataset_mode == 'VG':\n            max_label = int(max(gt_labels) if len(gt_labels) > 0 else 0)\n            palette = np.random.randint(0, 256, size=(max_label + 1, 3))\n            bbox_palette = [tuple(c) for c in palette]\n            # bbox_palette = get_palette('random', max_label + 1)\n            colors = [bbox_palette[label] for label in gt_labels]\n\n            visualizer.set_image(img)\n\n            for label, bbox, color in zip(gt_labels, gt_bboxes, colors):\n                visualizer.draw_bboxes(\n                    bbox, edge_colors=color, face_colors=color, alpha=0.3)\n                visualizer.draw_bboxes(bbox, edge_colors=color, alpha=1)\n\n            drawn_img = visualizer.get_image()\n\n            new_image = np.ones((100, img.shape[1], 3), dtype=np.uint8) * 255\n            visualizer.set_image(new_image)\n\n            gt_tokens_positive = [\n                tokens_positive[label] for label in gt_labels\n            ]\n            split_by_character = [char for char in data_sample.text]\n            characters = []\n            start_index = 0\n            end_index = 0\n            for w in split_by_character:\n                end_index += len(w)\n                is_find = False\n                for i, positive in enumerate(gt_tokens_positive):\n                    for p in positive:\n                        if start_index >= p[0] and end_index <= p[1]:\n                            characters.append([w, colors[i]])\n                            is_find = True\n                            break\n                    if is_find:\n                        break\n                if not is_find:\n                    characters.append([w, (0, 0, 0)])\n                start_index = end_index\n\n            drawn_text = draw_all_character(visualizer, characters,\n                                            img.shape[1])\n            drawn_img = np.concatenate((drawn_img, drawn_text), axis=0)\n        else:\n            gt_labels = gt_instances.labels\n            text = data_sample.text\n            label_names = []\n            for label in gt_labels:\n                label_names.append(text[\n                    tokens_positive[label][0][0]:tokens_positive[label][0][1]])\n            gt_instances.label_names = label_names\n            data_sample.gt_instances = gt_instances\n\n            visualizer.add_datasample(\n                base_name,\n                img,\n                data_sample,\n                draw_pred=False,\n                show=False,\n                wait_time=0,\n                out_file=None)\n            drawn_img = visualizer.get_image()\n\n            new_image = np.ones((100, img.shape[1], 3), dtype=np.uint8) * 255\n            visualizer.set_image(new_image)\n\n            characters = [char for char in text]\n            drawn_text = draw_all_character(visualizer, characters,\n                                            img.shape[1])\n            drawn_img = np.concatenate((drawn_img, drawn_text), axis=0)\n\n        if not args.not_show:\n            visualizer.show(\n                drawn_img, win_name=base_name, wait_time=args.show_interval)\n\n        if out_file is not None:\n            imwrite(drawn_img[..., ::-1], out_file)\n\n        progress_bar.update()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/browse_grounding_raw.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport json\nimport os.path as osp\n\nimport cv2\nimport numpy as np\nfrom mmcv.image import imfrombytes, imwrite\nfrom mmengine.fileio import get\nfrom mmengine.structures import InstanceData\nfrom mmengine.utils import mkdir_or_exist\n\nfrom mmdet.structures import DetDataSample\nfrom mmdet.visualization import DetLocalVisualizer\nfrom mmdet.visualization.palette import _get_adaptive_scales\n\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Browse a dataset')\n    parser.add_argument('data_root')\n    parser.add_argument('ann_file')\n    parser.add_argument('img_prefix')\n    parser.add_argument('--label-map-file', '-m', default=None)\n    parser.add_argument(\n        '--output-dir',\n        '-o',\n        default=None,\n        type=str,\n        help='If there is no display interface, you can save it')\n    parser.add_argument('--not-show', default=False, action='store_true')\n    parser.add_argument('--show-num', '-n', type=int, default=30)\n    parser.add_argument('--shuffle', default=False, action='store_true')\n    parser.add_argument(\n        '--show-interval',\n        type=float,\n        default=0,\n        help='the interval of show (s)')\n    args = parser.parse_args()\n    return args\n\n\ndef draw_all_character(visualizer, characters, w):\n    start_index = 2\n    y_index = 5\n    for char in characters:\n        if isinstance(char, str):\n            visualizer.draw_texts(\n                str(char),\n                positions=np.array([start_index, y_index]),\n                colors=(0, 0, 0),\n                font_families='monospace')\n            start_index += len(char) * 8\n        else:\n            visualizer.draw_texts(\n                str(char[0]),\n                positions=np.array([start_index, y_index]),\n                colors=char[1],\n                font_families='monospace')\n            start_index += len(char[0]) * 8\n\n        if start_index > w - 10:\n            start_index = 2\n            y_index += 15\n\n    drawn_text = visualizer.get_image()\n    return drawn_text\n\n\ndef main():\n    args = parse_args()\n    assert args.show_num > 0\n\n    local_path = osp.join(args.data_root, args.ann_file)\n    with open(local_path, 'r') as f:\n        data_list = [json.loads(line) for line in f]\n\n    dataset_index = list(range(len(data_list)))\n    if args.shuffle:\n        import random\n        random.shuffle(dataset_index)\n\n    if args.label_map_file is not None:\n        label_map_file = osp.join(args.data_root, args.label_map_file)\n        with open(label_map_file, 'r') as file:\n            label_map = json.load(file)\n\n    visualizer = DetLocalVisualizer()\n\n    for i in dataset_index[:args.show_num]:\n        item = data_list[i]\n\n        img_path = osp.join(args.data_root, args.img_prefix, item['filename'])\n        if backend_args is not None:\n            img_bytes = get(img_path, backend_args)\n            img = imfrombytes(img_bytes, flag='color')\n        else:\n            img = cv2.imread(img_path)\n        img = img[..., [2, 1, 0]]  # bgr to rgb\n\n        base_name, extension = osp.splitext(item['filename'])\n\n        out_file = osp.join(args.output_dir, base_name + '_' + str(i) +\n                            extension) if args.output_dir is not None else None\n\n        if args.output_dir is not None:\n            mkdir_or_exist(args.output_dir)\n\n        if 'detection' in item:\n            anno = item['detection']\n\n            instances = [obj for obj in anno['instances']]\n            bboxes = [obj['bbox'] for obj in instances]\n            bbox_labels = [int(obj['label']) for obj in instances]\n            label_names = [label_map[str(label)] for label in bbox_labels]\n\n            data_sample = DetDataSample()\n            gt_instances = InstanceData()\n            if len(instances) > 0 and 'score' in instances[0]:\n                score = [obj['score'] for obj in instances]\n                gt_instances['scores'] = np.array(score)\n\n            gt_instances['bboxes'] = np.array(bboxes).reshape(-1, 4)\n            gt_instances['labels'] = np.array(bbox_labels)\n            gt_instances['label_names'] = label_names\n            data_sample.gt_instances = gt_instances\n\n            visualizer.add_datasample(\n                osp.basename(img_path),\n                img,\n                data_sample,\n                draw_pred=False,\n                show=not args.not_show,\n                wait_time=args.show_interval,\n                out_file=out_file)\n        elif 'grounding' in item:\n            anno = item['grounding']\n            text = anno['caption']\n            regions = anno['regions']\n\n            max_label = len(regions) if len(regions) > 0 else 0\n            palette = np.random.randint(0, 256, size=(max_label + 1, 3))\n            bbox_palette = [tuple(c) for c in palette]\n            # bbox_palette = get_palette('random', max_label + 1)\n            colors = [bbox_palette[label] for label in range(max_label)]\n\n            visualizer.set_image(img)\n\n            gt_tokens_positive = []\n            for i, region in enumerate(regions):\n                bbox = region['bbox']\n                bbox = np.array(bbox).reshape(-1, 4)\n                tokens_positive = region['tokens_positive']\n                gt_tokens_positive.append(tokens_positive)\n                visualizer.draw_bboxes(\n                    bbox,\n                    edge_colors=colors[i],\n                    face_colors=colors[i],\n                    alpha=0.3)\n                visualizer.draw_bboxes(bbox, edge_colors=colors[i], alpha=1)\n\n                if 'score' in region:\n                    areas = (bbox[:, 3] - bbox[:, 1]) * (\n                        bbox[:, 2] - bbox[:, 0])\n                    scales = _get_adaptive_scales(areas)\n                    score = region['score'][0]\n                    score = [str(s) for s in score]\n                    font_sizes = [\n                        int(13 * scales[i]) for i in range(len(scales))\n                    ]\n                    visualizer.draw_texts(\n                        score,\n                        bbox[:, :2].astype(np.int32),\n                        colors=(255, 255, 255),\n                        font_sizes=font_sizes,\n                        bboxes=[{\n                            'facecolor': 'black',\n                            'alpha': 0.8,\n                            'pad': 0.7,\n                            'edgecolor': 'none'\n                        }] * len(bbox))\n\n            drawn_img = visualizer.get_image()\n            new_image = np.ones((100, img.shape[1], 3), dtype=np.uint8) * 255\n            visualizer.set_image(new_image)\n\n            split_by_character = [char for char in text]\n            characters = []\n            start_index = 0\n            end_index = 0\n            for w in split_by_character:\n                end_index += len(w)\n                is_find = False\n                for i, positive in enumerate(gt_tokens_positive):\n                    for p in positive:\n                        if start_index >= p[0] and end_index <= p[1]:\n                            characters.append([w, colors[i]])\n                            is_find = True\n                            break\n                    if is_find:\n                        break\n                if not is_find:\n                    characters.append([w, (0, 0, 0)])\n                start_index = end_index\n\n            drawn_text = draw_all_character(visualizer, characters,\n                                            img.shape[1])\n            drawn_img = np.concatenate((drawn_img, drawn_text), axis=0)\n\n            if not args.not_show:\n                visualizer.show(\n                    drawn_img,\n                    win_name=base_name,\n                    wait_time=args.show_interval)\n\n            if out_file is not None:\n                imwrite(drawn_img[..., ::-1], out_file)\n\n        elif 'referring' in item:\n            referring = item['referring']\n\n            max_label = len(referring) if len(referring) > 0 else 0\n            palette = np.random.randint(0, 256, size=(max_label + 1, 3))\n            bbox_palette = [tuple(c) for c in palette]\n            # bbox_palette = get_palette('random', max_label + 1)\n            colors = [bbox_palette[label] for label in range(max_label)]\n\n            visualizer.set_image(img)\n            phrases = []\n            for i, ref in enumerate(referring):\n                bbox = ref['bbox']\n                phrase = ref['phrase']\n                phrases.append(' // '.join(phrase))\n                bbox = np.array(bbox).reshape(-1, 4)\n\n                visualizer.draw_bboxes(\n                    bbox,\n                    edge_colors=colors[i],\n                    face_colors=colors[i],\n                    alpha=0.3)\n                visualizer.draw_bboxes(bbox, edge_colors=colors[i], alpha=1)\n            drawn_img = visualizer.get_image()\n\n            new_image = np.ones((100, img.shape[1], 3), dtype=np.uint8) * 255\n            visualizer.set_image(new_image)\n\n            start_index = 2\n            y_index = 5\n\n            chunk_size = max(min(img.shape[1] - 400, 70), 50)\n            for i, p in enumerate(phrases):\n                chunk_p = [\n                    p[i:i + chunk_size] for i in range(0, len(p), chunk_size)\n                ]\n                for cp in chunk_p:\n                    visualizer.draw_texts(\n                        cp,\n                        positions=np.array([start_index, y_index]),\n                        colors=colors[i],\n                        font_families='monospace')\n                    y_index += 15\n\n            drawn_text = visualizer.get_image()\n            drawn_img = np.concatenate((drawn_img, drawn_text), axis=0)\n\n            if not args.not_show:\n                visualizer.show(\n                    drawn_img,\n                    win_name=base_name,\n                    wait_time=args.show_interval)\n\n            if out_file is not None:\n                imwrite(drawn_img[..., ::-1], out_file)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/coco_error_analysis.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport os\nfrom argparse import ArgumentParser\nfrom multiprocessing import Pool\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom pycocotools.coco import COCO\nfrom pycocotools.cocoeval import COCOeval\n\n\ndef makeplot(rs, ps, outDir, class_name, iou_type):\n    cs = np.vstack([\n        np.ones((2, 3)),\n        np.array([0.31, 0.51, 0.74]),\n        np.array([0.75, 0.31, 0.30]),\n        np.array([0.36, 0.90, 0.38]),\n        np.array([0.50, 0.39, 0.64]),\n        np.array([1, 0.6, 0]),\n    ])\n    areaNames = ['allarea', 'small', 'medium', 'large']\n    types = ['C75', 'C50', 'Loc', 'Sim', 'Oth', 'BG', 'FN']\n    for i in range(len(areaNames)):\n        area_ps = ps[..., i, 0]\n        figure_title = iou_type + '-' + class_name + '-' + areaNames[i]\n        aps = [ps_.mean() for ps_ in area_ps]\n        ps_curve = [\n            ps_.mean(axis=1) if ps_.ndim > 1 else ps_ for ps_ in area_ps\n        ]\n        ps_curve.insert(0, np.zeros(ps_curve[0].shape))\n        fig = plt.figure()\n        ax = plt.subplot(111)\n        for k in range(len(types)):\n            ax.plot(rs, ps_curve[k + 1], color=[0, 0, 0], linewidth=0.5)\n            ax.fill_between(\n                rs,\n                ps_curve[k],\n                ps_curve[k + 1],\n                color=cs[k],\n                label=str(f'[{aps[k]:.3f}]' + types[k]),\n            )\n        plt.xlabel('recall')\n        plt.ylabel('precision')\n        plt.xlim(0, 1.0)\n        plt.ylim(0, 1.0)\n        plt.title(figure_title)\n        plt.legend()\n        # plt.show()\n        fig.savefig(outDir + f'/{figure_title}.png')\n        plt.close(fig)\n\n\ndef autolabel(ax, rects):\n    \"\"\"Attach a text label above each bar in *rects*, displaying its height.\"\"\"\n    for rect in rects:\n        height = rect.get_height()\n        if height > 0 and height <= 1:  # for percent values\n            text_label = '{:2.0f}'.format(height * 100)\n        else:\n            text_label = '{:2.0f}'.format(height)\n        ax.annotate(\n            text_label,\n            xy=(rect.get_x() + rect.get_width() / 2, height),\n            xytext=(0, 3),  # 3 points vertical offset\n            textcoords='offset points',\n            ha='center',\n            va='bottom',\n            fontsize='x-small',\n        )\n\n\ndef makebarplot(rs, ps, outDir, class_name, iou_type):\n    areaNames = ['allarea', 'small', 'medium', 'large']\n    types = ['C75', 'C50', 'Loc', 'Sim', 'Oth', 'BG', 'FN']\n    fig, ax = plt.subplots()\n    x = np.arange(len(areaNames))  # the areaNames locations\n    width = 0.60  # the width of the bars\n    rects_list = []\n    figure_title = iou_type + '-' + class_name + '-' + 'ap bar plot'\n    for i in range(len(types) - 1):\n        type_ps = ps[i, ..., 0]\n        aps = [ps_.mean() for ps_ in type_ps.T]\n        rects_list.append(\n            ax.bar(\n                x - width / 2 + (i + 1) * width / len(types),\n                aps,\n                width / len(types),\n                label=types[i],\n            ))\n\n    # Add some text for labels, title and custom x-axis tick labels, etc.\n    ax.set_ylabel('Mean Average Precision (mAP)')\n    ax.set_title(figure_title)\n    ax.set_xticks(x)\n    ax.set_xticklabels(areaNames)\n    ax.legend()\n\n    # Add score texts over bars\n    for rects in rects_list:\n        autolabel(ax, rects)\n\n    # Save plot\n    fig.savefig(outDir + f'/{figure_title}.png')\n    plt.close(fig)\n\n\ndef get_gt_area_group_numbers(cocoEval):\n    areaRng = cocoEval.params.areaRng\n    areaRngStr = [str(aRng) for aRng in areaRng]\n    areaRngLbl = cocoEval.params.areaRngLbl\n    areaRngStr2areaRngLbl = dict(zip(areaRngStr, areaRngLbl))\n    areaRngLbl2Number = dict.fromkeys(areaRngLbl, 0)\n    for evalImg in cocoEval.evalImgs:\n        if evalImg:\n            for gtIgnore in evalImg['gtIgnore']:\n                if not gtIgnore:\n                    aRngLbl = areaRngStr2areaRngLbl[str(evalImg['aRng'])]\n                    areaRngLbl2Number[aRngLbl] += 1\n    return areaRngLbl2Number\n\n\ndef make_gt_area_group_numbers_plot(cocoEval, outDir, verbose=True):\n    areaRngLbl2Number = get_gt_area_group_numbers(cocoEval)\n    areaRngLbl = areaRngLbl2Number.keys()\n    if verbose:\n        print('number of annotations per area group:', areaRngLbl2Number)\n\n    # Init figure\n    fig, ax = plt.subplots()\n    x = np.arange(len(areaRngLbl))  # the areaNames locations\n    width = 0.60  # the width of the bars\n    figure_title = 'number of annotations per area group'\n\n    rects = ax.bar(x, areaRngLbl2Number.values(), width)\n\n    # Add some text for labels, title and custom x-axis tick labels, etc.\n    ax.set_ylabel('Number of annotations')\n    ax.set_title(figure_title)\n    ax.set_xticks(x)\n    ax.set_xticklabels(areaRngLbl)\n\n    # Add score texts over bars\n    autolabel(ax, rects)\n\n    # Save plot\n    fig.tight_layout()\n    fig.savefig(outDir + f'/{figure_title}.png')\n    plt.close(fig)\n\n\ndef make_gt_area_histogram_plot(cocoEval, outDir):\n    n_bins = 100\n    areas = [ann['area'] for ann in cocoEval.cocoGt.anns.values()]\n\n    # init figure\n    figure_title = 'gt annotation areas histogram plot'\n    fig, ax = plt.subplots()\n\n    # Set the number of bins\n    ax.hist(np.sqrt(areas), bins=n_bins)\n\n    # Add some text for labels, title and custom x-axis tick labels, etc.\n    ax.set_xlabel('Squareroot Area')\n    ax.set_ylabel('Number of annotations')\n    ax.set_title(figure_title)\n\n    # Save plot\n    fig.tight_layout()\n    fig.savefig(outDir + f'/{figure_title}.png')\n    plt.close(fig)\n\n\ndef analyze_individual_category(k,\n                                cocoDt,\n                                cocoGt,\n                                catId,\n                                iou_type,\n                                areas=None):\n    nm = cocoGt.loadCats(catId)[0]\n    print(f'--------------analyzing {k + 1}-{nm[\"name\"]}---------------')\n    ps_ = {}\n    dt = copy.deepcopy(cocoDt)\n    nm = cocoGt.loadCats(catId)[0]\n    imgIds = cocoGt.getImgIds()\n    dt_anns = dt.dataset['annotations']\n    select_dt_anns = []\n    for ann in dt_anns:\n        if ann['category_id'] == catId:\n            select_dt_anns.append(ann)\n    dt.dataset['annotations'] = select_dt_anns\n    dt.createIndex()\n    # compute precision but ignore superclass confusion\n    gt = copy.deepcopy(cocoGt)\n    child_catIds = gt.getCatIds(supNms=[nm['supercategory']])\n    for idx, ann in enumerate(gt.dataset['annotations']):\n        if ann['category_id'] in child_catIds and ann['category_id'] != catId:\n            gt.dataset['annotations'][idx]['ignore'] = 1\n            gt.dataset['annotations'][idx]['iscrowd'] = 1\n            gt.dataset['annotations'][idx]['category_id'] = catId\n    cocoEval = COCOeval(gt, copy.deepcopy(dt), iou_type)\n    cocoEval.params.imgIds = imgIds\n    cocoEval.params.maxDets = [100]\n    cocoEval.params.iouThrs = [0.1]\n    cocoEval.params.useCats = 1\n    if areas:\n        cocoEval.params.areaRng = [\n            [0**2, areas[2]],\n            [0**2, areas[0]],\n            [areas[0], areas[1]],\n            [areas[1], areas[2]],\n        ]\n    cocoEval.evaluate()\n    cocoEval.accumulate()\n    ps_supercategory = cocoEval.eval['precision'][0, :, k, :, :]\n    ps_['ps_supercategory'] = ps_supercategory\n    # compute precision but ignore any class confusion\n    gt = copy.deepcopy(cocoGt)\n    for idx, ann in enumerate(gt.dataset['annotations']):\n        if ann['category_id'] != catId:\n            gt.dataset['annotations'][idx]['ignore'] = 1\n            gt.dataset['annotations'][idx]['iscrowd'] = 1\n            gt.dataset['annotations'][idx]['category_id'] = catId\n    cocoEval = COCOeval(gt, copy.deepcopy(dt), iou_type)\n    cocoEval.params.imgIds = imgIds\n    cocoEval.params.maxDets = [100]\n    cocoEval.params.iouThrs = [0.1]\n    cocoEval.params.useCats = 1\n    if areas:\n        cocoEval.params.areaRng = [\n            [0**2, areas[2]],\n            [0**2, areas[0]],\n            [areas[0], areas[1]],\n            [areas[1], areas[2]],\n        ]\n    cocoEval.evaluate()\n    cocoEval.accumulate()\n    ps_allcategory = cocoEval.eval['precision'][0, :, k, :, :]\n    ps_['ps_allcategory'] = ps_allcategory\n    return k, ps_\n\n\ndef analyze_results(res_file,\n                    ann_file,\n                    res_types,\n                    out_dir,\n                    extraplots=None,\n                    areas=None,\n                    score_thr=None):\n    for res_type in res_types:\n        assert res_type in ['bbox', 'segm']\n    if areas:\n        assert (len(areas) == 3), '3 integers should be specified as areas, \\\n            representing 3 area regions'\n\n    if score_thr:\n        assert score_thr >= 0, 'score_thr should be bigger than 0'\n\n    directory = os.path.dirname(out_dir + '/')\n    if not os.path.exists(directory):\n        print(f'-------------create {out_dir}-----------------')\n        os.makedirs(directory)\n\n    cocoGt = COCO(ann_file)\n    cocoDt = cocoGt.loadRes(res_file)\n    imgIds = cocoGt.getImgIds()\n\n    if score_thr:\n        cocoDt.dataset['annotations'] = list(\n            filter(lambda ann: ann['score'] >= score_thr,\n                   cocoDt.dataset['annotations']))\n        cocoDt.createIndex()\n\n    for res_type in res_types:\n        res_out_dir = out_dir + '/' + res_type + '/'\n        res_directory = os.path.dirname(res_out_dir)\n        if not os.path.exists(res_directory):\n            print(f'-------------create {res_out_dir}-----------------')\n            os.makedirs(res_directory)\n        iou_type = res_type\n        cocoEval = COCOeval(\n            copy.deepcopy(cocoGt), copy.deepcopy(cocoDt), iou_type)\n        cocoEval.params.imgIds = imgIds\n        cocoEval.params.iouThrs = [0.75, 0.5, 0.1]\n        cocoEval.params.maxDets = [100]\n        if areas:\n            cocoEval.params.areaRng = [\n                [0**2, areas[2]],\n                [0**2, areas[0]],\n                [areas[0], areas[1]],\n                [areas[1], areas[2]],\n            ]\n        cocoEval.evaluate()\n        cocoEval.accumulate()\n        ps = cocoEval.eval['precision']\n        ps = np.vstack([ps, np.zeros((4, *ps.shape[1:]))])\n        catIds = cocoGt.getCatIds()\n        recThrs = cocoEval.params.recThrs\n        with Pool(processes=48) as pool:\n            args = [(k, cocoDt, cocoGt, catId, iou_type, areas)\n                    for k, catId in enumerate(catIds)]\n            analyze_results = pool.starmap(analyze_individual_category, args)\n        for k, catId in enumerate(catIds):\n            nm = cocoGt.loadCats(catId)[0]\n            print(f'--------------saving {k + 1}-{nm[\"name\"]}---------------')\n            analyze_result = analyze_results[k]\n            assert k == analyze_result[0]\n            ps_supercategory = analyze_result[1]['ps_supercategory']\n            ps_allcategory = analyze_result[1]['ps_allcategory']\n            # compute precision but ignore superclass confusion\n            ps[3, :, k, :, :] = ps_supercategory\n            # compute precision but ignore any class confusion\n            ps[4, :, k, :, :] = ps_allcategory\n            # fill in background and false negative errors and plot\n            ps[ps == -1] = 0\n            ps[5, :, k, :, :] = ps[4, :, k, :, :] > 0\n            ps[6, :, k, :, :] = 1.0\n            makeplot(recThrs, ps[:, :, k], res_out_dir, nm['name'], iou_type)\n            if extraplots:\n                makebarplot(recThrs, ps[:, :, k], res_out_dir, nm['name'],\n                            iou_type)\n        makeplot(recThrs, ps, res_out_dir, 'allclass', iou_type)\n        if extraplots:\n            makebarplot(recThrs, ps, res_out_dir, 'allclass', iou_type)\n            make_gt_area_group_numbers_plot(\n                cocoEval=cocoEval, outDir=res_out_dir, verbose=True)\n            make_gt_area_histogram_plot(cocoEval=cocoEval, outDir=res_out_dir)\n\n\ndef main():\n    parser = ArgumentParser(description='COCO Error Analysis Tool')\n    parser.add_argument('result', help='result file (json format) path')\n    parser.add_argument('out_dir', help='dir to save analyze result images')\n    parser.add_argument(\n        '--ann',\n        default='data/coco/annotations/instances_val2017.json',\n        help='annotation file path',\n    )\n    parser.add_argument(\n        '--types', type=str, nargs='+', default=['bbox'], help='result types')\n    parser.add_argument(\n        '--extraplots',\n        action='store_true',\n        help='export extra bar/stat plots')\n    parser.add_argument(\n        '--score-thr',\n        type=float,\n        default=None,\n        help='score threshold to filter detection bboxes, only applied'\n        'when users want to change it.',\n    )\n    parser.add_argument(\n        '--areas',\n        type=int,\n        nargs='+',\n        default=[1024, 9216, 10000000000],\n        help='area regions',\n    )\n    args = parser.parse_args()\n    analyze_results(\n        args.result,\n        args.ann,\n        args.types,\n        out_dir=args.out_dir,\n        extraplots=args.extraplots,\n        areas=args.areas,\n        score_thr=args.score_thr,\n    )\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/coco_occluded_separated_recall.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom argparse import ArgumentParser\n\nimport mmengine\nfrom mmengine.logging import print_log\n\nfrom mmdet.datasets import CocoDataset\nfrom mmdet.evaluation import CocoOccludedSeparatedMetric\n\n\ndef main():\n    parser = ArgumentParser(\n        description='Compute recall of COCO occluded and separated masks '\n        'presented in paper https://arxiv.org/abs/2210.10046.')\n    parser.add_argument('result', help='result file (pkl format) path')\n    parser.add_argument('--out', help='file path to save evaluation results')\n    parser.add_argument(\n        '--score-thr',\n        type=float,\n        default=0.3,\n        help='Score threshold for the recall calculation. Defaults to 0.3')\n    parser.add_argument(\n        '--iou-thr',\n        type=float,\n        default=0.75,\n        help='IoU threshold for the recall calculation. Defaults to 0.75.')\n    parser.add_argument(\n        '--ann',\n        default='data/coco/annotations/instances_val2017.json',\n        help='coco annotation file path')\n    args = parser.parse_args()\n\n    results = mmengine.load(args.result)\n    assert 'masks' in results[0]['pred_instances'], \\\n        'The results must be predicted by instance segmentation model.'\n    metric = CocoOccludedSeparatedMetric(\n        ann_file=args.ann, iou_thr=args.iou_thr, score_thr=args.score_thr)\n    metric.dataset_meta = CocoDataset.METAINFO\n    for datasample in results:\n        metric.process(data_batch=None, data_samples=[datasample])\n    metric_res = metric.compute_metrics(metric.results)\n    if args.out is not None:\n        mmengine.dump(metric_res, args.out)\n        print_log(f'Evaluation results have been saved to {args.out}.')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/confusion_matrix.py",
    "content": "import argparse\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.ticker import MultipleLocator\nfrom mmcv.ops import nms\nfrom mmengine import Config, DictAction\nfrom mmengine.fileio import load\nfrom mmengine.registry import init_default_scope\nfrom mmengine.utils import ProgressBar\n\nfrom mmdet.evaluation import bbox_overlaps\nfrom mmdet.registry import DATASETS\nfrom mmdet.utils import replace_cfg_vals, update_data_root\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Generate confusion matrix from detection results')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument(\n        'prediction_path', help='prediction path where test .pkl result')\n    parser.add_argument(\n        'save_dir', help='directory where confusion matrix will be saved')\n    parser.add_argument(\n        '--show', action='store_true', help='show confusion matrix')\n    parser.add_argument(\n        '--color-theme',\n        default='plasma',\n        help='theme of the matrix color map')\n    parser.add_argument(\n        '--score-thr',\n        type=float,\n        default=0.3,\n        help='score threshold to filter detection bboxes')\n    parser.add_argument(\n        '--tp-iou-thr',\n        type=float,\n        default=0.5,\n        help='IoU threshold to be considered as matched')\n    parser.add_argument(\n        '--nms-iou-thr',\n        type=float,\n        default=None,\n        help='nms IoU threshold, only applied when users want to change the'\n        'nms IoU threshold.')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    return args\n\n\ndef calculate_confusion_matrix(dataset,\n                               results,\n                               score_thr=0,\n                               nms_iou_thr=None,\n                               tp_iou_thr=0.5):\n    \"\"\"Calculate the confusion matrix.\n\n    Args:\n        dataset (Dataset): Test or val dataset.\n        results (list[ndarray]): A list of detection results in each image.\n        score_thr (float|optional): Score threshold to filter bboxes.\n            Default: 0.\n        nms_iou_thr (float|optional): nms IoU threshold, the detection results\n            have done nms in the detector, only applied when users want to\n            change the nms IoU threshold. Default: None.\n        tp_iou_thr (float|optional): IoU threshold to be considered as matched.\n            Default: 0.5.\n    \"\"\"\n    num_classes = len(dataset.metainfo['classes'])\n    confusion_matrix = np.zeros(shape=[num_classes + 1, num_classes + 1])\n    assert len(dataset) == len(results)\n    prog_bar = ProgressBar(len(results))\n    for idx, per_img_res in enumerate(results):\n        res_bboxes = per_img_res['pred_instances']\n        gts = dataset.get_data_info(idx)['instances']\n        analyze_per_img_dets(confusion_matrix, gts, res_bboxes, score_thr,\n                             tp_iou_thr, nms_iou_thr)\n        prog_bar.update()\n    return confusion_matrix\n\n\ndef analyze_per_img_dets(confusion_matrix,\n                         gts,\n                         result,\n                         score_thr=0,\n                         tp_iou_thr=0.5,\n                         nms_iou_thr=None):\n    \"\"\"Analyze detection results on each image.\n\n    Args:\n        confusion_matrix (ndarray): The confusion matrix,\n            has shape (num_classes + 1, num_classes + 1).\n        gt_bboxes (ndarray): Ground truth bboxes, has shape (num_gt, 4).\n        gt_labels (ndarray): Ground truth labels, has shape (num_gt).\n        result (ndarray): Detection results, has shape\n            (num_classes, num_bboxes, 5).\n        score_thr (float): Score threshold to filter bboxes.\n            Default: 0.\n        tp_iou_thr (float): IoU threshold to be considered as matched.\n            Default: 0.5.\n        nms_iou_thr (float|optional): nms IoU threshold, the detection results\n            have done nms in the detector, only applied when users want to\n            change the nms IoU threshold. Default: None.\n    \"\"\"\n    true_positives = np.zeros(len(gts))\n    gt_bboxes = []\n    gt_labels = []\n    for gt in gts:\n        gt_bboxes.append(gt['bbox'])\n        gt_labels.append(gt['bbox_label'])\n\n    gt_bboxes = np.array(gt_bboxes)\n    gt_labels = np.array(gt_labels)\n\n    unique_label = np.unique(result['labels'].numpy())\n\n    for det_label in unique_label:\n        mask = (result['labels'] == det_label)\n        det_bboxes = result['bboxes'][mask].numpy()\n        det_scores = result['scores'][mask].numpy()\n\n        if nms_iou_thr:\n            det_bboxes, _ = nms(\n                det_bboxes, det_scores, nms_iou_thr, score_threshold=score_thr)\n        ious = bbox_overlaps(det_bboxes[:, :4], gt_bboxes)\n        for i, score in enumerate(det_scores):\n            det_match = 0\n            if score >= score_thr:\n                for j, gt_label in enumerate(gt_labels):\n                    if ious[i, j] >= tp_iou_thr:\n                        det_match += 1\n                        if gt_label == det_label:\n                            true_positives[j] += 1  # TP\n                        confusion_matrix[gt_label, det_label] += 1\n                if det_match == 0:  # BG FP\n                    confusion_matrix[-1, det_label] += 1\n    for num_tp, gt_label in zip(true_positives, gt_labels):\n        if num_tp == 0:  # FN\n            confusion_matrix[gt_label, -1] += 1\n\n\ndef plot_confusion_matrix(confusion_matrix,\n                          labels,\n                          save_dir=None,\n                          show=True,\n                          title='Normalized Confusion Matrix',\n                          color_theme='plasma'):\n    \"\"\"Draw confusion matrix with matplotlib.\n\n    Args:\n        confusion_matrix (ndarray): The confusion matrix.\n        labels (list[str]): List of class names.\n        save_dir (str|optional): If set, save the confusion matrix plot to the\n            given path. Default: None.\n        show (bool): Whether to show the plot. Default: True.\n        title (str): Title of the plot. Default: `Normalized Confusion Matrix`.\n        color_theme (str): Theme of the matrix color map. Default: `plasma`.\n    \"\"\"\n    # normalize the confusion matrix\n    per_label_sums = confusion_matrix.sum(axis=1)[:, np.newaxis]\n    confusion_matrix = \\\n        confusion_matrix.astype(np.float32) / per_label_sums * 100\n\n    num_classes = len(labels)\n    fig, ax = plt.subplots(\n        figsize=(0.5 * num_classes, 0.5 * num_classes * 0.8), dpi=180)\n    cmap = plt.get_cmap(color_theme)\n    im = ax.imshow(confusion_matrix, cmap=cmap)\n    plt.colorbar(mappable=im, ax=ax)\n\n    title_font = {'weight': 'bold', 'size': 12}\n    ax.set_title(title, fontdict=title_font)\n    label_font = {'size': 10}\n    plt.ylabel('Ground Truth Label', fontdict=label_font)\n    plt.xlabel('Prediction Label', fontdict=label_font)\n\n    # draw locator\n    xmajor_locator = MultipleLocator(1)\n    xminor_locator = MultipleLocator(0.5)\n    ax.xaxis.set_major_locator(xmajor_locator)\n    ax.xaxis.set_minor_locator(xminor_locator)\n    ymajor_locator = MultipleLocator(1)\n    yminor_locator = MultipleLocator(0.5)\n    ax.yaxis.set_major_locator(ymajor_locator)\n    ax.yaxis.set_minor_locator(yminor_locator)\n\n    # draw grid\n    ax.grid(True, which='minor', linestyle='-')\n\n    # draw label\n    ax.set_xticks(np.arange(num_classes))\n    ax.set_yticks(np.arange(num_classes))\n    ax.set_xticklabels(labels)\n    ax.set_yticklabels(labels)\n\n    ax.tick_params(\n        axis='x', bottom=False, top=True, labelbottom=False, labeltop=True)\n    plt.setp(\n        ax.get_xticklabels(), rotation=45, ha='left', rotation_mode='anchor')\n\n    # draw confution matrix value\n    for i in range(num_classes):\n        for j in range(num_classes):\n            ax.text(\n                j,\n                i,\n                '{}%'.format(\n                    int(confusion_matrix[\n                        i,\n                        j]) if not np.isnan(confusion_matrix[i, j]) else -1),\n                ha='center',\n                va='center',\n                color='w',\n                size=7)\n\n    ax.set_ylim(len(confusion_matrix) - 0.5, -0.5)  # matplotlib>3.1.1\n\n    fig.tight_layout()\n    if save_dir is not None:\n        plt.savefig(\n            os.path.join(save_dir, 'confusion_matrix.png'), format='png')\n    if show:\n        plt.show()\n\n\ndef main():\n    args = parse_args()\n\n    cfg = Config.fromfile(args.config)\n\n    # replace the ${key} with the value of cfg.key\n    cfg = replace_cfg_vals(cfg)\n\n    # update data root according to MMDET_DATASETS\n    update_data_root(cfg)\n\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    results = load(args.prediction_path)\n\n    if not os.path.exists(args.save_dir):\n        os.makedirs(args.save_dir)\n\n    dataset = DATASETS.build(cfg.test_dataloader.dataset)\n\n    confusion_matrix = calculate_confusion_matrix(dataset, results,\n                                                  args.score_thr,\n                                                  args.nms_iou_thr,\n                                                  args.tp_iou_thr)\n    plot_confusion_matrix(\n        confusion_matrix,\n        dataset.metainfo['classes'] + ('background', ),\n        save_dir=args.save_dir,\n        show=args.show,\n        color_theme=args.color_theme)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/eval_metric.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\n\nimport mmengine\nfrom mmengine import Config, DictAction\nfrom mmengine.evaluator import Evaluator\nfrom mmengine.registry import init_default_scope\n\nfrom mmdet.registry import DATASETS\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Evaluate metric of the '\n                                     'results saved in pkl format')\n    parser.add_argument('config', help='Config of the model')\n    parser.add_argument('pkl_results', help='Results in pickle format')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    cfg = Config.fromfile(args.config)\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    dataset = DATASETS.build(cfg.test_dataloader.dataset)\n    predictions = mmengine.load(args.pkl_results)\n\n    evaluator = Evaluator(cfg.val_evaluator)\n    evaluator.dataset_meta = dataset.metainfo\n    eval_results = evaluator.offline_evaluate(predictions)\n    print(eval_results)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/fuse_results.py",
    "content": "import argparse\n\nfrom mmengine.fileio import dump, load\nfrom mmengine.logging import print_log\nfrom mmengine.utils import ProgressBar\nfrom pycocotools.coco import COCO\nfrom pycocotools.cocoeval import COCOeval\n\nfrom mmdet.models.utils import weighted_boxes_fusion\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Fusion image \\\n        prediction results using Weighted \\\n        Boxes Fusion from multiple models.')\n    parser.add_argument(\n        'pred-results',\n        type=str,\n        nargs='+',\n        help='files of prediction results \\\n                    from multiple models, json format.')\n    parser.add_argument('--annotation', type=str, help='annotation file path')\n    parser.add_argument(\n        '--weights',\n        type=float,\n        nargs='*',\n        default=None,\n        help='weights for each model, '\n        'remember to correspond to the above prediction path.')\n    parser.add_argument(\n        '--fusion-iou-thr',\n        type=float,\n        default=0.55,\n        help='IoU value for boxes to be a match in wbf.')\n    parser.add_argument(\n        '--skip-box-thr',\n        type=float,\n        default=0.0,\n        help='exclude boxes with score lower than this variable in wbf.')\n    parser.add_argument(\n        '--conf-type',\n        type=str,\n        default='avg',\n        help='how to calculate confidence in weighted boxes in wbf.')\n    parser.add_argument(\n        '--eval-single',\n        action='store_true',\n        help='whether evaluate each single model result.')\n    parser.add_argument(\n        '--save-fusion-results',\n        action='store_true',\n        help='whether save fusion result')\n    parser.add_argument(\n        '--out-dir',\n        type=str,\n        default='outputs',\n        help='Output directory of images or prediction results.')\n\n    args = parser.parse_args()\n\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    assert len(args.models_name) == len(args.pred_results), \\\n        'the quantities of model names and prediction results are not equal'\n\n    cocoGT = COCO(args.annotation)\n\n    predicts_raw = []\n\n    models_name = ['model_' + str(i) for i in range(len(args.pred_results))]\n\n    for model_name, path in \\\n            zip(models_name, args.pred_results):\n        pred = load(path)\n        predicts_raw.append(pred)\n\n        if args.eval_single:\n            print_log(f'Evaluate {model_name}...')\n            cocoDt = cocoGT.loadRes(pred)\n            coco_eval = COCOeval(cocoGT, cocoDt, iouType='bbox')\n            coco_eval.evaluate()\n            coco_eval.accumulate()\n            coco_eval.summarize()\n\n    predict = {\n        str(image_id): {\n            'bboxes_list': [[] for _ in range(len(predicts_raw))],\n            'scores_list': [[] for _ in range(len(predicts_raw))],\n            'labels_list': [[] for _ in range(len(predicts_raw))]\n        }\n        for image_id in cocoGT.getImgIds()\n    }\n\n    for i, pred_single in enumerate(predicts_raw):\n        for pred in pred_single:\n            p = predict[str(pred['image_id'])]\n            p['bboxes_list'][i].append(pred['bbox'])\n            p['scores_list'][i].append(pred['score'])\n            p['labels_list'][i].append(pred['category_id'])\n\n    result = []\n    prog_bar = ProgressBar(len(predict))\n    for image_id, res in predict.items():\n        bboxes, scores, labels = weighted_boxes_fusion(\n            res['bboxes_list'],\n            res['scores_list'],\n            res['labels_list'],\n            weights=args.weights,\n            iou_thr=args.fusion_iou_thr,\n            skip_box_thr=args.skip_box_thr,\n            conf_type=args.conf_type)\n\n        for bbox, score, label in zip(bboxes, scores, labels):\n            result.append({\n                'bbox': bbox.numpy().tolist(),\n                'category_id': int(label),\n                'image_id': int(image_id),\n                'score': float(score)\n            })\n\n        prog_bar.update()\n\n    if args.save_fusion_results:\n        out_file = args.out_dir + '/fusion_results.json'\n        dump(result, file=out_file)\n        print_log(\n            f'Fusion results have been saved to {out_file}.', logger='current')\n\n    print_log('Evaluate fusion results using wbf...')\n    cocoDt = cocoGT.loadRes(result)\n    coco_eval = COCOeval(cocoGT, cocoDt, iouType='bbox')\n    coco_eval.evaluate()\n    coco_eval.accumulate()\n    coco_eval.summarize()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/get_flops.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport tempfile\nfrom functools import partial\nfrom pathlib import Path\n\nimport numpy as np\nimport torch\nfrom mmengine.config import Config, DictAction\nfrom mmengine.logging import MMLogger\nfrom mmengine.model import revert_sync_batchnorm\nfrom mmengine.registry import init_default_scope\nfrom mmengine.runner import Runner\nfrom mmengine.utils import digit_version\n\nfrom mmdet.registry import MODELS\n\ntry:\n    from mmengine.analysis import get_model_complexity_info\n    from mmengine.analysis.print_helper import _format_size\nexcept ImportError:\n    raise ImportError('Please upgrade mmengine >= 0.6.0')\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Get a detector flops')\n    parser.add_argument('config', help='train config file path')\n    parser.add_argument(\n        '--num-images',\n        type=int,\n        default=100,\n        help='num images of calculate model flops')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    return args\n\n\ndef inference(args, logger):\n    if digit_version(torch.__version__) < digit_version('1.12'):\n        logger.warning(\n            'Some config files, such as configs/yolact and configs/detectors,'\n            'may have compatibility issues with torch.jit when torch<1.12. '\n            'If you want to calculate flops for these models, '\n            'please make sure your pytorch version is >=1.12.')\n\n    config_name = Path(args.config)\n    if not config_name.exists():\n        logger.error(f'{config_name} not found.')\n\n    cfg = Config.fromfile(args.config)\n    cfg.val_dataloader.batch_size = 1\n    cfg.work_dir = tempfile.TemporaryDirectory().name\n\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    # TODO: The following usage is temporary and not safe\n    # use hard code to convert mmSyncBN to SyncBN. This is a known\n    # bug in mmengine, mmSyncBN requires a distributed environment，\n    # this question involves models like configs/strong_baselines\n    if hasattr(cfg, 'head_norm_cfg'):\n        cfg['head_norm_cfg'] = dict(type='SyncBN', requires_grad=True)\n        cfg['model']['roi_head']['bbox_head']['norm_cfg'] = dict(\n            type='SyncBN', requires_grad=True)\n        cfg['model']['roi_head']['mask_head']['norm_cfg'] = dict(\n            type='SyncBN', requires_grad=True)\n\n    result = {}\n    avg_flops = []\n    data_loader = Runner.build_dataloader(cfg.val_dataloader)\n    model = MODELS.build(cfg.model)\n    if torch.cuda.is_available():\n        model = model.cuda()\n    model = revert_sync_batchnorm(model)\n    model.eval()\n    _forward = model.forward\n\n    for idx, data_batch in enumerate(data_loader):\n        if idx == args.num_images:\n            break\n        data = model.data_preprocessor(data_batch)\n        result['ori_shape'] = data['data_samples'][0].ori_shape\n        result['pad_shape'] = data['data_samples'][0].pad_shape\n        if hasattr(data['data_samples'][0], 'batch_input_shape'):\n            result['pad_shape'] = data['data_samples'][0].batch_input_shape\n        model.forward = partial(_forward, data_samples=data['data_samples'])\n        outputs = get_model_complexity_info(\n            model,\n            None,\n            inputs=data['inputs'],\n            show_table=False,\n            show_arch=False)\n        avg_flops.append(outputs['flops'])\n        params = outputs['params']\n        result['compute_type'] = 'dataloader: load a picture from the dataset'\n    del data_loader\n\n    mean_flops = _format_size(int(np.average(avg_flops)))\n    params = _format_size(params)\n    result['flops'] = mean_flops\n    result['params'] = params\n\n    return result\n\n\ndef main():\n    args = parse_args()\n    logger = MMLogger.get_instance(name='MMLogger')\n    result = inference(args, logger)\n    split_line = '=' * 30\n    ori_shape = result['ori_shape']\n    pad_shape = result['pad_shape']\n    flops = result['flops']\n    params = result['params']\n    compute_type = result['compute_type']\n\n    if pad_shape != ori_shape:\n        print(f'{split_line}\\nUse size divisor set input shape '\n              f'from {ori_shape} to {pad_shape}')\n    print(f'{split_line}\\nCompute type: {compute_type}\\n'\n          f'Input shape: {pad_shape}\\nFlops: {flops}\\n'\n          f'Params: {params}\\n{split_line}')\n    print('!!!Please be cautious if you use the results in papers. '\n          'You may need to check if all ops are supported and verify '\n          'that the flops computation is correct.')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/mot/browse_dataset.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os.path as osp\n\nimport mmengine\nfrom mmengine import Config, DictAction\nfrom mmengine.registry import init_default_scope\n\nfrom mmdet.registry import DATASETS, VISUALIZERS\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Browse a dataset')\n    parser.add_argument('config', help='train config file path')\n    parser.add_argument(\n        '--output-dir',\n        default=None,\n        type=str,\n        help='If there is no display interface, you can save it')\n    parser.add_argument('--show', default=True, action='store_true')\n    parser.add_argument(\n        '--show-interval',\n        type=float,\n        default=2,\n        help='the interval of show (s)')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    cfg = Config.fromfile(args.config)\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    dataset = DATASETS.build(cfg.train_dataloader.dataset)\n\n    visualizer = VISUALIZERS.build(cfg.visualizer)\n    visualizer.dataset_meta = dataset.metainfo\n\n    progress_bar = mmengine.ProgressBar(len(dataset))\n    for idx, item in enumerate(dataset):  # inputs data_samples\n        data_sample = item['data_samples']\n        input = item['inputs']\n        for img_idx in range(len(data_sample)):\n            img_data_sample = data_sample[img_idx]\n            img_path = img_data_sample.img_path\n            img = input[img_idx].permute(1, 2, 0).numpy()\n            out_file = osp.join(\n                args.output_dir,\n                str(idx).zfill(6),\n                f'img_{img_idx}.jpg') if args.output_dir is not None else None\n            img = img[..., [2, 1, 0]]  # bgr to rgb\n            visualizer.add_datasample(\n                osp.basename(img_path),\n                img,\n                data_sample=img_data_sample,\n                draw_pred=False,\n                show=args.show,\n                wait_time=args.show_interval,\n                out_file=out_file)\n            # Record file path mapping.\n            if args.output_dir is not None:\n                with open(\n                        osp.join(args.output_dir,\n                                 str(idx).zfill(6), 'info.txt'), 'a') as f:\n                    f.write(f'The source filepath of img_{img_idx}.jpg'\n                            f'is `{img_path}`.\\n')\n        progress_bar.update()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/mot/dist_mot_search.sh",
    "content": "#!/usr/bin/env bash\n\nCONFIG=$1\nGPUS=$2\nPORT=${PORT:-29500}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\npython -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=$PORT \\\n    $(dirname \"$0\")/mot_param_search.py $CONFIG --launcher pytorch ${@:3}\n"
  },
  {
    "path": "tools/analysis_tools/mot/mot_error_visualize.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\nimport re\n\nimport mmcv\nimport motmetrics as mm\nimport numpy as np\nimport pandas as pd\nfrom mmengine import Config\nfrom mmengine.logging import print_log\nfrom mmengine.registry import init_default_scope\nfrom torch.utils.data import Dataset\n\nfrom mmdet.registry import DATASETS\nfrom mmdet.utils import imshow_mot_errors\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='visualize errors for multiple object tracking')\n    parser.add_argument('config', help='path of the config file')\n    parser.add_argument(\n        '--result-dir', help='directory of the inference result')\n    parser.add_argument(\n        '--output-dir',\n        help='directory where painted images or videos will be saved')\n    parser.add_argument(\n        '--show',\n        action='store_true',\n        help='whether to show the results on the fly')\n    parser.add_argument(\n        '--fps', type=int, default=3, help='FPS of the output video')\n    parser.add_argument(\n        '--backend',\n        type=str,\n        choices=['cv2', 'plt'],\n        default='cv2',\n        help='backend of visualization')\n    args = parser.parse_args()\n    return args\n\n\ndef compare_res_gts(results_dir: str, dataset: Dataset, video_name: str):\n    \"\"\"Evaluate the results of the video.\n\n    Args:\n        results_dir (str): the directory of the MOT results.\n        dataset (Dataset): MOT dataset of the video to be evaluated.\n        video_name (str): Name of the video to be evaluated.\n\n    Returns:\n        tuple: (acc, res, gt), acc contains the results of MOT metrics,\n        res is the results of inference and gt is the ground truth.\n    \"\"\"\n    if 'half-train' in dataset.ann_file:\n        gt_file = osp.join(dataset.data_prefix['img_path'],\n                           f'{video_name}/gt/gt_half-train.txt')\n        gt = mm.io.loadtxt(gt_file)\n        gt.index = gt.index.set_levels(\n            pd.factorize(gt.index.levels[0])[0] + 1, level=0)\n    elif 'half-val' in dataset.ann_file:\n        gt_file = osp.join(dataset.data_prefix['img_path'],\n                           f'{video_name}/gt/gt_half-val.txt')\n        gt = mm.io.loadtxt(gt_file)\n        gt.index = gt.index.set_levels(\n            pd.factorize(gt.index.levels[0])[0] + 1, level=0)\n    else:\n        gt_file = osp.join(dataset.data_prefix['img_path'],\n                           f'{video_name}/gt/gt.txt')\n        gt = mm.io.loadtxt(gt_file)\n        gt.index = gt.index.set_levels(\n            pd.factorize(gt.index.levels[0])[0] + 1, level=0)\n    res_file = osp.join(results_dir, f'{video_name}.txt')\n    res = mm.io.loadtxt(res_file)\n    ini_file = osp.join(dataset.data_prefix['img_path'],\n                        f'{video_name}/seqinfo.ini')\n    if osp.exists(ini_file):\n        acc, _ = mm.utils.CLEAR_MOT_M(gt, res, ini_file)\n    else:\n        acc = mm.utils.compare_to_groundtruth(gt, res)\n\n    return acc, res, gt\n\n\ndef main():\n    args = parse_args()\n\n    assert args.show or args.out_dir, \\\n        ('Please specify at least one operation (show the results '\n         '/ save the results) with the argument \"--show\" or \"--out-dir\"')\n\n    if args.out_dir is not None:\n        os.makedirs(args.out_dir, exist_ok=True)\n\n    print_log('This script visualizes the error for multiple object tracking. '\n              'By Default, the red bounding box denotes false positive, '\n              'the yellow bounding box denotes the false negative '\n              'and the blue bounding box denotes ID switch.')\n\n    cfg = Config.fromfile(args.config)\n\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n    dataset = DATASETS.build(cfg.val_dataloader.dataset)\n\n    # create index from frame_id to filename\n    filenames_dict = dict()\n    for i in range(len(dataset)):\n        video_info = dataset.get_data_info(i)\n        # the `data_info['file_name']` usually has the same format\n        # with \"MOT17-09-DPM/img1/000003.jpg\"\n        # split with both '\\' and '/' to be compatible with different OS.\n        for data_info in video_info['images']:\n            split_path = re.split(r'[\\\\/]', data_info['file_name'])\n            video_name = split_path[-3]\n            frame_id = int(data_info['frame_id'] + 1)\n            if video_name not in filenames_dict:\n                filenames_dict[video_name] = dict()\n        # the data_info['img_path'] usually has the same format\n        # with `img_path_prefix + \"MOT17-09-DPM/img1/000003.jpg\"`\n            filenames_dict[video_name][frame_id] = data_info['img_path']\n    video_names = tuple(filenames_dict.keys())\n\n    for video_name in video_names:\n        print_log(f'Start processing video {video_name}')\n\n        acc, res, gt = compare_res_gts(args.result_dir, dataset, video_name)\n\n        frames_id_list = sorted(\n            list(set(acc.mot_events.index.get_level_values(0))))\n        for frame_id in frames_id_list:\n            # events in the current frame\n            events = acc.mot_events.xs(frame_id)\n            cur_res = res.loc[frame_id] if frame_id in res.index else None\n            cur_gt = gt.loc[frame_id] if frame_id in gt.index else None\n            # path of image\n            img = filenames_dict[video_name][frame_id]\n            fps = events[events.Type == 'FP']\n            fns = events[events.Type == 'MISS']\n            idsws = events[events.Type == 'SWITCH']\n\n            bboxes, ids, error_types = [], [], []\n            for fp_index in fps.index:\n                hid = events.loc[fp_index].HId\n                bboxes.append([\n                    cur_res.loc[hid].X, cur_res.loc[hid].Y,\n                    cur_res.loc[hid].X + cur_res.loc[hid].Width,\n                    cur_res.loc[hid].Y + cur_res.loc[hid].Height,\n                    cur_res.loc[hid].Confidence\n                ])\n                ids.append(hid)\n                # error_type = 0 denotes false positive error\n                error_types.append(0)\n            for fn_index in fns.index:\n                oid = events.loc[fn_index].OId\n                bboxes.append([\n                    cur_gt.loc[oid].X, cur_gt.loc[oid].Y,\n                    cur_gt.loc[oid].X + cur_gt.loc[oid].Width,\n                    cur_gt.loc[oid].Y + cur_gt.loc[oid].Height,\n                    cur_gt.loc[oid].Confidence\n                ])\n                ids.append(-1)\n                # error_type = 1 denotes false negative error\n                error_types.append(1)\n            for idsw_index in idsws.index:\n                hid = events.loc[idsw_index].HId\n                bboxes.append([\n                    cur_res.loc[hid].X, cur_res.loc[hid].Y,\n                    cur_res.loc[hid].X + cur_res.loc[hid].Width,\n                    cur_res.loc[hid].Y + cur_res.loc[hid].Height,\n                    cur_res.loc[hid].Confidence\n                ])\n                ids.append(hid)\n                # error_type = 2 denotes id switch\n                error_types.append(2)\n            if len(bboxes) == 0:\n                bboxes = np.zeros((0, 5), dtype=np.float32)\n            else:\n                bboxes = np.asarray(bboxes, dtype=np.float32)\n            ids = np.asarray(ids, dtype=np.int32)\n            error_types = np.asarray(error_types, dtype=np.int32)\n            imshow_mot_errors(\n                img,\n                bboxes,\n                ids,\n                error_types,\n                show=args.show,\n                out_file=osp.join(args.out_dir,\n                                  f'{video_name}/{frame_id:06d}.jpg')\n                if args.out_dir else None,\n                backend=args.backend)\n\n        print_log(f'Done! Visualization images are saved in '\n                  f'\\'{args.out_dir}/{video_name}\\'')\n\n        mmcv.frames2video(\n            f'{args.out_dir}/{video_name}',\n            f'{args.out_dir}/{video_name}.mp4',\n            fps=args.fps,\n            fourcc='mp4v',\n            start=frames_id_list[0],\n            end=frames_id_list[-1],\n            show_progress=False)\n        print_log(\n            f'Done! Visualization video is saved as '\n            f'\\'{args.out_dir}/{video_name}.mp4\\' with a FPS of {args.fps}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/mot/mot_param_search.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\nfrom itertools import product\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.dist import get_dist_info\nfrom mmengine.logging import MMLogger, print_log\nfrom mmengine.model import is_model_wrapper\nfrom mmengine.registry import init_default_scope\nfrom mmengine.runner import Runner\nfrom mmengine.runner.checkpoint import load_checkpoint\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMDet tracking test (and eval) a model')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('--checkpoint', help='checkpoint file')\n    parser.add_argument('--detector', help='detection checkpoint file')\n    parser.add_argument('--reid', help='reid checkpoint file')\n    parser.add_argument(\n        '--work-dir',\n        help='the directory to save the file containing evaluation metrics')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--local_rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    return args\n\n\ndef get_search_params(cfg, search_params=None, prefix=None, logger=None):\n    if search_params is None:\n        search_params = dict()\n    for k, v in cfg.items():\n        if prefix is not None:\n            entire_k = prefix + '.' + k\n        else:\n            entire_k = k\n        if isinstance(v, list):\n            print_log(f'search `{entire_k}` in {v}.', logger)\n            search_params[entire_k] = v\n        if isinstance(v, dict):\n            search_params = get_search_params(v, search_params, entire_k,\n                                              logger)\n    return search_params\n\n\ndef main():\n\n    args = parse_args()\n\n    # do not init the default scope here because it will be init in the runner\n\n    # load config\n    cfg = Config.fromfile(args.config)\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = args.work_dir\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(args.config))[0])\n\n    cfg.load_from = args.checkpoint\n\n    logger = MMLogger.get_instance(name='ParamsSearcher', logger_name='Logger')\n    # get all cases\n    search_params = get_search_params(cfg.model.tracker, logger=logger)\n    search_params_names = tuple(search_params.keys())\n    all_search_cases = []\n    for values in product(*search_params.values()):\n        search = dict()\n        for k, v in zip(search_params_names, values):\n            search[k] = v\n        all_search_cases.append(search)\n\n    print_log(f'Totally {len(all_search_cases)} cases.', logger)\n\n    search_metrics = []\n    metrics_types = [cfg.test_evaluator.metric] if isinstance(\n        cfg.test_evaluator.metric, str) else cfg.test_evaluator.metric\n    if 'HOTA' in metrics_types:\n        search_metrics.extend(['HOTA', 'AssA', 'DetA'])\n    if 'CLEAR' in metrics_types:\n        search_metrics.extend(\n            ['MOTA', 'MOTP', 'IDSW', 'TP', 'FN', 'FP', 'Frag', 'MT', 'ML'])\n    if 'Identity' in metrics_types:\n        search_metrics.extend(['IDF1', 'IDTP', 'IDFN', 'IDFP', 'IDP', 'IDR'])\n    print_log(f'Record {search_metrics}.', logger)\n\n    runner = Runner.from_cfg(cfg)\n    if is_model_wrapper(runner.model):\n        model = runner.model.module\n    else:\n        model = runner.model\n\n    if args.detector:\n        assert not (args.checkpoint and args.detector), \\\n            'Error: checkpoint and detector checkpoint cannot both exist'\n        load_checkpoint(model.detector, args.detector)\n\n    if args.reid:\n        assert (args.checkpoint is not None) or (args.detector is not None), \\\n            'Error: checkpoint and detector checkpoint cannot both not exist'\n        assert not (args.checkpoint and args.reid), \\\n            'Error: checkpoint and reid checkpoint cannot both exist'\n        load_checkpoint(model.reid, args.reid)\n\n    for case in all_search_cases:\n        for name, value in case.items():\n            if hasattr(runner.model, 'module'):\n                setattr(runner.model.module.tracker, name, value)\n            else:\n                setattr(runner.model.tracker, name, value)\n        runner.test()\n        rank, _ = get_dist_info()\n        if rank == 0:\n            _records = []\n            for metric in search_metrics:\n                res = runner.message_hub.get_scalar(\n                    'test/motchallenge-metric/' + metric).current()\n                if isinstance(res, float):\n                    _records.append(f'{res:.3f}')\n                else:\n                    _records.append(f'{res}')\n            print_log(f'-------------- {case}: {_records} --------------',\n                      logger)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/mot/slurm_mot_search.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\n\nPARTITION=$1\nJOB_NAME=$2\nCONFIG=$3\nGPUS=$4\nGPUS_PER_NODE=${GPUS_PER_NODE:-8}\nCPUS_PER_TASK=${CPUS_PER_TASK:-2}\nPY_ARGS=${@:5}\nSRUN_ARGS=${SRUN_ARGS:-\"\"}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\nsrun -p ${PARTITION} \\\n    --job-name=${JOB_NAME} \\\n    --gres=gpu:${GPUS_PER_NODE} \\\n    --ntasks=${GPUS} \\\n    --ntasks-per-node=${GPUS_PER_NODE} \\\n    --cpus-per-task=${CPUS_PER_TASK} \\\n    --kill-on-bad-exit=1 \\\n    ${SRUN_ARGS} \\\n    python -u $(dirname \"$0\")/mot_param_search.py ${CONFIG} --launcher=\"slurm\" ${PY_ARGS}\n"
  },
  {
    "path": "tools/analysis_tools/optimize_anchors.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Optimize anchor settings on a specific dataset.\n\nThis script provides two method to optimize YOLO anchors including k-means\nanchor cluster and differential evolution. You can use ``--algorithm k-means``\nand ``--algorithm differential_evolution`` to switch two method.\n\nExample:\n    Use k-means anchor cluster::\n\n        python tools/analysis_tools/optimize_anchors.py ${CONFIG} \\\n        --algorithm k-means --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} \\\n        --output-dir ${OUTPUT_DIR}\n    Use differential evolution to optimize anchors::\n\n        python tools/analysis_tools/optimize_anchors.py ${CONFIG} \\\n        --algorithm differential_evolution \\\n        --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} \\\n        --output-dir ${OUTPUT_DIR}\n\"\"\"\nimport argparse\nimport os.path as osp\n\nimport numpy as np\nimport torch\nfrom mmengine.config import Config\nfrom mmengine.fileio import dump\nfrom mmengine.logging import MMLogger\nfrom mmengine.registry import init_default_scope\nfrom mmengine.utils import ProgressBar\nfrom scipy.optimize import differential_evolution\n\nfrom mmdet.registry import DATASETS\nfrom mmdet.structures.bbox import (bbox_cxcywh_to_xyxy, bbox_overlaps,\n                                   bbox_xyxy_to_cxcywh)\nfrom mmdet.utils import replace_cfg_vals, update_data_root\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Optimize anchor parameters.')\n    parser.add_argument('config', help='Train config file path.')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for calculating.')\n    parser.add_argument(\n        '--input-shape',\n        type=int,\n        nargs='+',\n        default=[608, 608],\n        help='input image size')\n    parser.add_argument(\n        '--algorithm',\n        default='differential_evolution',\n        help='Algorithm used for anchor optimizing.'\n        'Support k-means and differential_evolution for YOLO.')\n    parser.add_argument(\n        '--iters',\n        default=1000,\n        type=int,\n        help='Maximum iterations for optimizer.')\n    parser.add_argument(\n        '--output-dir',\n        default=None,\n        type=str,\n        help='Path to save anchor optimize result.')\n\n    args = parser.parse_args()\n    return args\n\n\nclass BaseAnchorOptimizer:\n    \"\"\"Base class for anchor optimizer.\n\n    Args:\n        dataset (obj:`Dataset`): Dataset object.\n        input_shape (list[int]): Input image shape of the model.\n            Format in [width, height].\n        logger (obj:`logging.Logger`): The logger for logging.\n        device (str, optional): Device used for calculating.\n            Default: 'cuda:0'\n        out_dir (str, optional): Path to save anchor optimize result.\n            Default: None\n    \"\"\"\n\n    def __init__(self,\n                 dataset,\n                 input_shape,\n                 logger,\n                 device='cuda:0',\n                 out_dir=None):\n        self.dataset = dataset\n        self.input_shape = input_shape\n        self.logger = logger\n        self.device = device\n        self.out_dir = out_dir\n        bbox_whs, img_shapes = self.get_whs_and_shapes()\n        ratios = img_shapes.max(1, keepdims=True) / np.array([input_shape])\n\n        # resize to input shape\n        self.bbox_whs = bbox_whs / ratios\n\n    def get_whs_and_shapes(self):\n        \"\"\"Get widths and heights of bboxes and shapes of images.\n\n        Returns:\n            tuple[np.ndarray]: Array of bbox shapes and array of image\n            shapes with shape (num_bboxes, 2) in [width, height] format.\n        \"\"\"\n        self.logger.info('Collecting bboxes from annotation...')\n        bbox_whs = []\n        img_shapes = []\n        prog_bar = ProgressBar(len(self.dataset))\n        for idx in range(len(self.dataset)):\n            data_info = self.dataset.get_data_info(idx)\n            img_shape = np.array([data_info['width'], data_info['height']])\n            gt_instances = data_info['instances']\n            for instance in gt_instances:\n                bbox = np.array(instance['bbox'])\n                wh = bbox[2:4] - bbox[0:2]\n                img_shapes.append(img_shape)\n                bbox_whs.append(wh)\n\n            prog_bar.update()\n        print('\\n')\n        bbox_whs = np.array(bbox_whs)\n        img_shapes = np.array(img_shapes)\n        self.logger.info(f'Collected {bbox_whs.shape[0]} bboxes.')\n        return bbox_whs, img_shapes\n\n    def get_zero_center_bbox_tensor(self):\n        \"\"\"Get a tensor of bboxes centered at (0, 0).\n\n        Returns:\n            Tensor: Tensor of bboxes with shape (num_bboxes, 4)\n            in [xmin, ymin, xmax, ymax] format.\n        \"\"\"\n        whs = torch.from_numpy(self.bbox_whs).to(\n            self.device, dtype=torch.float32)\n        bboxes = bbox_cxcywh_to_xyxy(\n            torch.cat([torch.zeros_like(whs), whs], dim=1))\n        return bboxes\n\n    def optimize(self):\n        raise NotImplementedError\n\n    def save_result(self, anchors, path=None):\n        anchor_results = []\n        for w, h in anchors:\n            anchor_results.append([round(w), round(h)])\n        self.logger.info(f'Anchor optimize result:{anchor_results}')\n        if path:\n            json_path = osp.join(path, 'anchor_optimize_result.json')\n            dump(anchor_results, json_path)\n            self.logger.info(f'Result saved in {json_path}')\n\n\nclass YOLOKMeansAnchorOptimizer(BaseAnchorOptimizer):\n    r\"\"\"YOLO anchor optimizer using k-means. Code refer to `AlexeyAB/darknet.\n    <https://github.com/AlexeyAB/darknet/blob/master/src/detector.c>`_.\n\n    Args:\n        num_anchors (int) : Number of anchors.\n        iters (int): Maximum iterations for k-means.\n    \"\"\"\n\n    def __init__(self, num_anchors, iters, **kwargs):\n\n        super(YOLOKMeansAnchorOptimizer, self).__init__(**kwargs)\n        self.num_anchors = num_anchors\n        self.iters = iters\n\n    def optimize(self):\n        anchors = self.kmeans_anchors()\n        self.save_result(anchors, self.out_dir)\n\n    def kmeans_anchors(self):\n        self.logger.info(\n            f'Start cluster {self.num_anchors} YOLO anchors with K-means...')\n        bboxes = self.get_zero_center_bbox_tensor()\n        cluster_center_idx = torch.randint(\n            0, bboxes.shape[0], (self.num_anchors, )).to(self.device)\n\n        assignments = torch.zeros((bboxes.shape[0], )).to(self.device)\n        cluster_centers = bboxes[cluster_center_idx]\n        if self.num_anchors == 1:\n            cluster_centers = self.kmeans_maximization(bboxes, assignments,\n                                                       cluster_centers)\n            anchors = bbox_xyxy_to_cxcywh(cluster_centers)[:, 2:].cpu().numpy()\n            anchors = sorted(anchors, key=lambda x: x[0] * x[1])\n            return anchors\n\n        prog_bar = ProgressBar(self.iters)\n        for i in range(self.iters):\n            converged, assignments = self.kmeans_expectation(\n                bboxes, assignments, cluster_centers)\n            if converged:\n                self.logger.info(f'K-means process has converged at iter {i}.')\n                break\n            cluster_centers = self.kmeans_maximization(bboxes, assignments,\n                                                       cluster_centers)\n            prog_bar.update()\n        print('\\n')\n        avg_iou = bbox_overlaps(bboxes,\n                                cluster_centers).max(1)[0].mean().item()\n\n        anchors = bbox_xyxy_to_cxcywh(cluster_centers)[:, 2:].cpu().numpy()\n        anchors = sorted(anchors, key=lambda x: x[0] * x[1])\n        self.logger.info(f'Anchor cluster finish. Average IOU: {avg_iou}')\n\n        return anchors\n\n    def kmeans_maximization(self, bboxes, assignments, centers):\n        \"\"\"Maximization part of EM algorithm(Expectation-Maximization)\"\"\"\n        new_centers = torch.zeros_like(centers)\n        for i in range(centers.shape[0]):\n            mask = (assignments == i)\n            if mask.sum():\n                new_centers[i, :] = bboxes[mask].mean(0)\n        return new_centers\n\n    def kmeans_expectation(self, bboxes, assignments, centers):\n        \"\"\"Expectation part of EM algorithm(Expectation-Maximization)\"\"\"\n        ious = bbox_overlaps(bboxes, centers)\n        closest = ious.argmax(1)\n        converged = (closest == assignments).all()\n        return converged, closest\n\n\nclass YOLODEAnchorOptimizer(BaseAnchorOptimizer):\n    \"\"\"YOLO anchor optimizer using differential evolution algorithm.\n\n    Args:\n        num_anchors (int) : Number of anchors.\n        iters (int): Maximum iterations for k-means.\n        strategy (str): The differential evolution strategy to use.\n            Should be one of:\n\n                - 'best1bin'\n                - 'best1exp'\n                - 'rand1exp'\n                - 'randtobest1exp'\n                - 'currenttobest1exp'\n                - 'best2exp'\n                - 'rand2exp'\n                - 'randtobest1bin'\n                - 'currenttobest1bin'\n                - 'best2bin'\n                - 'rand2bin'\n                - 'rand1bin'\n\n            Default: 'best1bin'.\n        population_size (int): Total population size of evolution algorithm.\n            Default: 15.\n        convergence_thr (float): Tolerance for convergence, the\n            optimizing stops when ``np.std(pop) <= abs(convergence_thr)\n            + convergence_thr * np.abs(np.mean(population_energies))``,\n            respectively. Default: 0.0001.\n        mutation (tuple[float]): Range of dithering randomly changes the\n            mutation constant. Default: (0.5, 1).\n        recombination (float): Recombination constant of crossover probability.\n            Default: 0.7.\n    \"\"\"\n\n    def __init__(self,\n                 num_anchors,\n                 iters,\n                 strategy='best1bin',\n                 population_size=15,\n                 convergence_thr=0.0001,\n                 mutation=(0.5, 1),\n                 recombination=0.7,\n                 **kwargs):\n\n        super(YOLODEAnchorOptimizer, self).__init__(**kwargs)\n\n        self.num_anchors = num_anchors\n        self.iters = iters\n        self.strategy = strategy\n        self.population_size = population_size\n        self.convergence_thr = convergence_thr\n        self.mutation = mutation\n        self.recombination = recombination\n\n    def optimize(self):\n        anchors = self.differential_evolution()\n        self.save_result(anchors, self.out_dir)\n\n    def differential_evolution(self):\n        bboxes = self.get_zero_center_bbox_tensor()\n\n        bounds = []\n        for i in range(self.num_anchors):\n            bounds.extend([(0, self.input_shape[0]), (0, self.input_shape[1])])\n\n        result = differential_evolution(\n            func=self.avg_iou_cost,\n            bounds=bounds,\n            args=(bboxes, ),\n            strategy=self.strategy,\n            maxiter=self.iters,\n            popsize=self.population_size,\n            tol=self.convergence_thr,\n            mutation=self.mutation,\n            recombination=self.recombination,\n            updating='immediate',\n            disp=True)\n        self.logger.info(\n            f'Anchor evolution finish. Average IOU: {1 - result.fun}')\n        anchors = [(w, h) for w, h in zip(result.x[::2], result.x[1::2])]\n        anchors = sorted(anchors, key=lambda x: x[0] * x[1])\n        return anchors\n\n    @staticmethod\n    def avg_iou_cost(anchor_params, bboxes):\n        assert len(anchor_params) % 2 == 0\n        anchor_whs = torch.tensor(\n            [[w, h]\n             for w, h in zip(anchor_params[::2], anchor_params[1::2])]).to(\n                 bboxes.device, dtype=bboxes.dtype)\n        anchor_boxes = bbox_cxcywh_to_xyxy(\n            torch.cat([torch.zeros_like(anchor_whs), anchor_whs], dim=1))\n        ious = bbox_overlaps(bboxes, anchor_boxes)\n        max_ious, _ = ious.max(1)\n        cost = 1 - max_ious.mean().item()\n        return cost\n\n\ndef main():\n    logger = MMLogger.get_current_instance()\n    args = parse_args()\n    cfg = args.config\n    cfg = Config.fromfile(cfg)\n    init_default_scope(cfg.get('default_scope', 'mmdet'))\n\n    # replace the ${key} with the value of cfg.key\n    cfg = replace_cfg_vals(cfg)\n\n    # update data root according to MMDET_DATASETS\n    update_data_root(cfg)\n\n    input_shape = args.input_shape\n    assert len(input_shape) == 2\n\n    anchor_type = cfg.model.bbox_head.anchor_generator.type\n    assert anchor_type == 'YOLOAnchorGenerator', \\\n        f'Only support optimize YOLOAnchor, but get {anchor_type}.'\n\n    base_sizes = cfg.model.bbox_head.anchor_generator.base_sizes\n    num_anchors = sum([len(sizes) for sizes in base_sizes])\n\n    train_data_cfg = cfg.train_dataloader\n    while 'dataset' in train_data_cfg:\n        train_data_cfg = train_data_cfg['dataset']\n    dataset = DATASETS.build(train_data_cfg)\n\n    if args.algorithm == 'k-means':\n        optimizer = YOLOKMeansAnchorOptimizer(\n            dataset=dataset,\n            input_shape=input_shape,\n            device=args.device,\n            num_anchors=num_anchors,\n            iters=args.iters,\n            logger=logger,\n            out_dir=args.output_dir)\n    elif args.algorithm == 'differential_evolution':\n        optimizer = YOLODEAnchorOptimizer(\n            dataset=dataset,\n            input_shape=input_shape,\n            device=args.device,\n            num_anchors=num_anchors,\n            iters=args.iters,\n            logger=logger,\n            out_dir=args.output_dir)\n    else:\n        raise NotImplementedError(\n            f'Only support k-means and differential_evolution, '\n            f'but get {args.algorithm}')\n\n    optimizer.optimize()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/robustness_eval.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport os.path as osp\nfrom argparse import ArgumentParser\n\nimport numpy as np\nfrom mmengine.fileio import load\n\n\ndef print_coco_results(results):\n\n    def _print(result, ap=1, iouThr=None, areaRng='all', maxDets=100):\n        titleStr = 'Average Precision' if ap == 1 else 'Average Recall'\n        typeStr = '(AP)' if ap == 1 else '(AR)'\n        iouStr = '0.50:0.95' \\\n            if iouThr is None else f'{iouThr:0.2f}'\n        iStr = f' {titleStr:<18} {typeStr} @[ IoU={iouStr:<9} | '\n        iStr += f'area={areaRng:>6s} | maxDets={maxDets:>3d} ] = {result:0.3f}'\n        print(iStr)\n\n    stats = np.zeros((12, ))\n    stats[0] = _print(results[0], 1)\n    stats[1] = _print(results[1], 1, iouThr=.5)\n    stats[2] = _print(results[2], 1, iouThr=.75)\n    stats[3] = _print(results[3], 1, areaRng='small')\n    stats[4] = _print(results[4], 1, areaRng='medium')\n    stats[5] = _print(results[5], 1, areaRng='large')\n    # TODO support recall metric\n    '''\n    stats[6] = _print(results[6], 0, maxDets=1)\n    stats[7] = _print(results[7], 0, maxDets=10)\n    stats[8] = _print(results[8], 0)\n    stats[9] = _print(results[9], 0, areaRng='small')\n    stats[10] = _print(results[10], 0, areaRng='medium')\n    stats[11] = _print(results[11], 0, areaRng='large')\n    '''\n\n\ndef get_coco_style_results(filename,\n                           task='bbox',\n                           metric=None,\n                           prints='mPC',\n                           aggregate='benchmark'):\n\n    assert aggregate in ['benchmark', 'all']\n\n    if prints == 'all':\n        prints = ['P', 'mPC', 'rPC']\n    elif isinstance(prints, str):\n        prints = [prints]\n    for p in prints:\n        assert p in ['P', 'mPC', 'rPC']\n\n    if metric is None:\n        metrics = [\n            'mAP',\n            'mAP_50',\n            'mAP_75',\n            'mAP_s',\n            'mAP_m',\n            'mAP_l',\n        ]\n    elif isinstance(metric, list):\n        metrics = metric\n    else:\n        metrics = [metric]\n\n    for metric_name in metrics:\n        assert metric_name in [\n            'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l'\n        ]\n\n    eval_output = load(filename)\n\n    num_distortions = len(list(eval_output.keys()))\n    results = np.zeros((num_distortions, 6, len(metrics)), dtype='float32')\n\n    for corr_i, distortion in enumerate(eval_output):\n        for severity in eval_output[distortion]:\n            for metric_j, metric_name in enumerate(metrics):\n                metric_dict = eval_output[distortion][severity]\n\n                new_metric_dict = {}\n                for k, v in metric_dict.items():\n                    if '/' in k:\n                        new_metric_dict[k.split('/')[-1]] = v\n                mAP = new_metric_dict['_'.join((task, metric_name))]\n                results[corr_i, severity, metric_j] = mAP\n\n    P = results[0, 0, :]\n    if aggregate == 'benchmark':\n        mPC = np.mean(results[:15, 1:, :], axis=(0, 1))\n    else:\n        mPC = np.mean(results[:, 1:, :], axis=(0, 1))\n    rPC = mPC / P\n\n    print(f'\\nmodel: {osp.basename(filename)}')\n    if metric is None:\n        if 'P' in prints:\n            print(f'Performance on Clean Data [P] ({task})')\n            print_coco_results(P)\n        if 'mPC' in prints:\n            print(f'Mean Performance under Corruption [mPC] ({task})')\n            print_coco_results(mPC)\n        if 'rPC' in prints:\n            print(f'Relative Performance under Corruption [rPC] ({task})')\n            print_coco_results(rPC)\n    else:\n        if 'P' in prints:\n            print(f'Performance on Clean Data [P] ({task})')\n            for metric_i, metric_name in enumerate(metrics):\n                print(f'{metric_name:5} =  {P[metric_i]:0.3f}')\n        if 'mPC' in prints:\n            print(f'Mean Performance under Corruption [mPC] ({task})')\n            for metric_i, metric_name in enumerate(metrics):\n                print(f'{metric_name:5} =  {mPC[metric_i]:0.3f}')\n        if 'rPC' in prints:\n            print(f'Relative Performance under Corruption [rPC] ({task})')\n            for metric_i, metric_name in enumerate(metrics):\n                print(f'{metric_name:5} => {rPC[metric_i] * 100:0.1f} %')\n\n    return results\n\n\ndef get_voc_style_results(filename, prints='mPC', aggregate='benchmark'):\n\n    assert aggregate in ['benchmark', 'all']\n\n    if prints == 'all':\n        prints = ['P', 'mPC', 'rPC']\n    elif isinstance(prints, str):\n        prints = [prints]\n    for p in prints:\n        assert p in ['P', 'mPC', 'rPC']\n\n    eval_output = load(filename)\n\n    num_distortions = len(list(eval_output.keys()))\n    results = np.zeros((num_distortions, 6, 20), dtype='float32')\n\n    for i, distortion in enumerate(eval_output):\n        for severity in eval_output[distortion]:\n            mAP = [\n                eval_output[distortion][severity][j]['ap']\n                for j in range(len(eval_output[distortion][severity]))\n            ]\n            results[i, severity, :] = mAP\n\n    P = results[0, 0, :]\n    if aggregate == 'benchmark':\n        mPC = np.mean(results[:15, 1:, :], axis=(0, 1))\n    else:\n        mPC = np.mean(results[:, 1:, :], axis=(0, 1))\n    rPC = mPC / P\n\n    print(f'\\nmodel: {osp.basename(filename)}')\n    if 'P' in prints:\n        print(f'Performance on Clean Data [P] in AP50 = {np.mean(P):0.3f}')\n    if 'mPC' in prints:\n        print('Mean Performance under Corruption [mPC] in AP50 = '\n              f'{np.mean(mPC):0.3f}')\n    if 'rPC' in prints:\n        print('Relative Performance under Corruption [rPC] in % = '\n              f'{np.mean(rPC) * 100:0.1f}')\n\n    return np.mean(results, axis=2, keepdims=True)\n\n\ndef get_results(filename,\n                dataset='coco',\n                task='bbox',\n                metric=None,\n                prints='mPC',\n                aggregate='benchmark'):\n    assert dataset in ['coco', 'voc', 'cityscapes']\n\n    if dataset in ['coco', 'cityscapes']:\n        results = get_coco_style_results(\n            filename,\n            task=task,\n            metric=metric,\n            prints=prints,\n            aggregate=aggregate)\n    elif dataset == 'voc':\n        if task != 'bbox':\n            print('Only bbox analysis is supported for Pascal VOC')\n            print('Will report bbox results\\n')\n        if metric not in [None, ['AP'], ['AP50']]:\n            print('Only the AP50 metric is supported for Pascal VOC')\n            print('Will report AP50 metric\\n')\n        results = get_voc_style_results(\n            filename, prints=prints, aggregate=aggregate)\n\n    return results\n\n\ndef get_distortions_from_file(filename):\n\n    eval_output = load(filename)\n\n    return get_distortions_from_results(eval_output)\n\n\ndef get_distortions_from_results(eval_output):\n    distortions = []\n    for i, distortion in enumerate(eval_output):\n        distortions.append(distortion.replace('_', ' '))\n    return distortions\n\n\ndef main():\n    parser = ArgumentParser(description='Corruption Result Analysis')\n    parser.add_argument('filename', help='result file path')\n    parser.add_argument(\n        '--dataset',\n        type=str,\n        choices=['coco', 'voc', 'cityscapes'],\n        default='coco',\n        help='dataset type')\n    parser.add_argument(\n        '--task',\n        type=str,\n        nargs='+',\n        choices=['bbox', 'segm'],\n        default=['bbox'],\n        help='task to report')\n    parser.add_argument(\n        '--metric',\n        nargs='+',\n        choices=[\n            None, 'AP', 'AP50', 'AP75', 'APs', 'APm', 'APl', 'AR1', 'AR10',\n            'AR100', 'ARs', 'ARm', 'ARl'\n        ],\n        default=None,\n        help='metric to report')\n    parser.add_argument(\n        '--prints',\n        type=str,\n        nargs='+',\n        choices=['P', 'mPC', 'rPC'],\n        default='mPC',\n        help='corruption benchmark metric to print')\n    parser.add_argument(\n        '--aggregate',\n        type=str,\n        choices=['all', 'benchmark'],\n        default='benchmark',\n        help='aggregate all results or only those \\\n        for benchmark corruptions')\n\n    args = parser.parse_args()\n\n    for task in args.task:\n        get_results(\n            args.filename,\n            dataset=args.dataset,\n            task=task,\n            metric=args.metric,\n            prints=args.prints,\n            aggregate=args.aggregate)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/analysis_tools/test_robustness.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport copy\nimport os\nimport os.path as osp\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.dist import get_dist_info\nfrom mmengine.evaluator import DumpResults\nfrom mmengine.fileio import dump\nfrom mmengine.runner import Runner\n\nfrom mmdet.engine.hooks.utils import trigger_visualization_hook\nfrom mmdet.registry import RUNNERS\nfrom tools.analysis_tools.robustness_eval import get_results\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='MMDet test detector')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('checkpoint', help='checkpoint file')\n    parser.add_argument(\n        '--out',\n        type=str,\n        help='dump predictions to a pickle file for offline evaluation')\n    parser.add_argument(\n        '--corruptions',\n        type=str,\n        nargs='+',\n        default='benchmark',\n        choices=[\n            'all', 'benchmark', 'noise', 'blur', 'weather', 'digital',\n            'holdout', 'None', 'gaussian_noise', 'shot_noise', 'impulse_noise',\n            'defocus_blur', 'glass_blur', 'motion_blur', 'zoom_blur', 'snow',\n            'frost', 'fog', 'brightness', 'contrast', 'elastic_transform',\n            'pixelate', 'jpeg_compression', 'speckle_noise', 'gaussian_blur',\n            'spatter', 'saturate'\n        ],\n        help='corruptions')\n    parser.add_argument(\n        '--work-dir',\n        help='the directory to save the file containing evaluation metrics')\n    parser.add_argument(\n        '--severities',\n        type=int,\n        nargs='+',\n        default=[0, 1, 2, 3, 4, 5],\n        help='corruption severity levels')\n    parser.add_argument(\n        '--summaries',\n        type=bool,\n        default=False,\n        help='Print summaries for every corruption and severity')\n    parser.add_argument('--show', action='store_true', help='show results')\n    parser.add_argument(\n        '--show-dir', help='directory where painted images will be saved')\n    parser.add_argument(\n        '--wait-time', type=float, default=2, help='the interval of show (s)')\n    parser.add_argument('--seed', type=int, default=None, help='random seed')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--local_rank', type=int, default=0)\n    parser.add_argument(\n        '--final-prints',\n        type=str,\n        nargs='+',\n        choices=['P', 'mPC', 'rPC'],\n        default='mPC',\n        help='corruption benchmark metric to print at the end')\n    parser.add_argument(\n        '--final-prints-aggregate',\n        type=str,\n        choices=['all', 'benchmark'],\n        default='benchmark',\n        help='aggregate all results or only those for benchmark corruptions')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    assert args.out or args.show or args.show_dir, \\\n        ('Please specify at least one operation (save or show the results) '\n         'with the argument \"--out\", \"--show\" or \"show-dir\"')\n\n    # load config\n    cfg = Config.fromfile(args.config)\n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = args.work_dir\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(args.config))[0])\n\n    cfg.model.backbone.init_cfg.type = None\n    cfg.test_dataloader.dataset.test_mode = True\n\n    cfg.load_from = args.checkpoint\n    if args.show or args.show_dir:\n        cfg = trigger_visualization_hook(cfg, args)\n\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    # add `DumpResults` dummy metric\n    if args.out is not None:\n        assert args.out.endswith(('.pkl', '.pickle')), \\\n            'The dump file must be a pkl file.'\n        runner.test_evaluator.metrics.append(\n            DumpResults(out_file_path=args.out))\n\n    if 'all' in args.corruptions:\n        corruptions = [\n            'gaussian_noise', 'shot_noise', 'impulse_noise', 'defocus_blur',\n            'glass_blur', 'motion_blur', 'zoom_blur', 'snow', 'frost', 'fog',\n            'brightness', 'contrast', 'elastic_transform', 'pixelate',\n            'jpeg_compression', 'speckle_noise', 'gaussian_blur', 'spatter',\n            'saturate'\n        ]\n    elif 'benchmark' in args.corruptions:\n        corruptions = [\n            'gaussian_noise', 'shot_noise', 'impulse_noise', 'defocus_blur',\n            'glass_blur', 'motion_blur', 'zoom_blur', 'snow', 'frost', 'fog',\n            'brightness', 'contrast', 'elastic_transform', 'pixelate',\n            'jpeg_compression'\n        ]\n    elif 'noise' in args.corruptions:\n        corruptions = ['gaussian_noise', 'shot_noise', 'impulse_noise']\n    elif 'blur' in args.corruptions:\n        corruptions = [\n            'defocus_blur', 'glass_blur', 'motion_blur', 'zoom_blur'\n        ]\n    elif 'weather' in args.corruptions:\n        corruptions = ['snow', 'frost', 'fog', 'brightness']\n    elif 'digital' in args.corruptions:\n        corruptions = [\n            'contrast', 'elastic_transform', 'pixelate', 'jpeg_compression'\n        ]\n    elif 'holdout' in args.corruptions:\n        corruptions = ['speckle_noise', 'gaussian_blur', 'spatter', 'saturate']\n    elif 'None' in args.corruptions:\n        corruptions = ['None']\n        args.severities = [0]\n    else:\n        corruptions = args.corruptions\n\n    aggregated_results = {}\n    for corr_i, corruption in enumerate(corruptions):\n        aggregated_results[corruption] = {}\n        for sev_i, corruption_severity in enumerate(args.severities):\n            # evaluate severity 0 (= no corruption) only once\n            if corr_i > 0 and corruption_severity == 0:\n                aggregated_results[corruption][0] = \\\n                    aggregated_results[corruptions[0]][0]\n                continue\n\n            test_loader_cfg = copy.deepcopy(cfg.test_dataloader)\n            # assign corruption and severity\n            if corruption_severity > 0:\n                corruption_trans = dict(\n                    type='Corrupt',\n                    corruption=corruption,\n                    severity=corruption_severity)\n                # TODO: hard coded \"1\", we assume that the first step is\n                # loading images, which needs to be fixed in the future\n                test_loader_cfg.dataset.pipeline.insert(1, corruption_trans)\n\n            test_loader = runner.build_dataloader(test_loader_cfg)\n\n            runner.test_loop.dataloader = test_loader\n            # set random seeds\n            if args.seed is not None:\n                runner.set_randomness(args.seed)\n\n            # print info\n            print(f'\\nTesting {corruption} at severity {corruption_severity}')\n\n            eval_results = runner.test()\n            if args.out:\n                eval_results_filename = (\n                    osp.splitext(args.out)[0] + '_results' +\n                    osp.splitext(args.out)[1])\n                aggregated_results[corruption][\n                    corruption_severity] = eval_results\n                dump(aggregated_results, eval_results_filename)\n\n    rank, _ = get_dist_info()\n    if rank == 0:\n        eval_results_filename = (\n            osp.splitext(args.out)[0] + '_results' + osp.splitext(args.out)[1])\n        # print final results\n        print('\\nAggregated results:')\n        prints = args.final_prints\n        aggregate = args.final_prints_aggregate\n\n        if cfg.dataset_type == 'VOCDataset':\n            get_results(\n                eval_results_filename,\n                dataset='voc',\n                prints=prints,\n                aggregate=aggregate)\n        else:\n            get_results(\n                eval_results_filename,\n                dataset='coco',\n                prints=prints,\n                aggregate=aggregate)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/ade20k2coco.py",
    "content": "import argparse\nimport json\nimport os\nfrom pathlib import Path\n\nimport numpy as np\nimport pycocotools.mask as mask_util\nfrom mmengine.utils import ProgressBar, mkdir_or_exist\nfrom panopticapi.utils import IdGenerator, save_json\nfrom PIL import Image\n\nfrom mmdet.datasets.ade20k import ADE20KPanopticDataset\n\nORIGINAL_CATEGORIES = [\n    'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road, route',\n    'bed', 'window', 'grass', 'cabinet', 'sidewalk, pavement', 'person',\n    'earth, ground', 'door', 'table', 'mountain, mount', 'plant', 'curtain',\n    'chair', 'car', 'water', 'painting, picture', 'sofa', 'shelf', 'house',\n    'sea', 'mirror', 'rug', 'field', 'armchair', 'seat', 'fence', 'desk',\n    'rock, stone', 'wardrobe, closet, press', 'lamp', 'tub', 'rail', 'cushion',\n    'base, pedestal, stand', 'box', 'column, pillar', 'signboard, sign',\n    'chest of drawers, chest, bureau, dresser', 'counter', 'sand', 'sink',\n    'skyscraper', 'fireplace', 'refrigerator, icebox',\n    'grandstand, covered stand', 'path', 'stairs', 'runway',\n    'case, display case, showcase, vitrine',\n    'pool table, billiard table, snooker table', 'pillow',\n    'screen door, screen', 'stairway, staircase', 'river', 'bridge, span',\n    'bookcase', 'blind, screen', 'coffee table',\n    'toilet, can, commode, crapper, pot, potty, stool, throne', 'flower',\n    'book', 'hill', 'bench', 'countertop', 'stove', 'palm, palm tree',\n    'kitchen island', 'computer', 'swivel chair', 'boat', 'bar',\n    'arcade machine', 'hovel, hut, hutch, shack, shanty', 'bus', 'towel',\n    'light', 'truck', 'tower', 'chandelier', 'awning, sunshade, sunblind',\n    'street lamp', 'booth', 'tv', 'airplane', 'dirt track', 'clothes', 'pole',\n    'land, ground, soil',\n    'bannister, banister, balustrade, balusters, handrail',\n    'escalator, moving staircase, moving stairway',\n    'ottoman, pouf, pouffe, puff, hassock', 'bottle',\n    'buffet, counter, sideboard',\n    'poster, posting, placard, notice, bill, card', 'stage', 'van', 'ship',\n    'fountain',\n    'conveyer belt, conveyor belt, conveyer, conveyor, transporter', 'canopy',\n    'washer, automatic washer, washing machine', 'plaything, toy', 'pool',\n    'stool', 'barrel, cask', 'basket, handbasket', 'falls', 'tent', 'bag',\n    'minibike, motorbike', 'cradle', 'oven', 'ball', 'food, solid food',\n    'step, stair', 'tank, storage tank', 'trade name', 'microwave', 'pot',\n    'animal', 'bicycle', 'lake', 'dishwasher', 'screen', 'blanket, cover',\n    'sculpture', 'hood, exhaust hood', 'sconce', 'vase', 'traffic light',\n    'tray', 'trash can', 'fan', 'pier', 'crt screen', 'plate', 'monitor',\n    'bulletin board', 'shower', 'radiator', 'glass, drinking glass', 'clock',\n    'flag'\n]\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert ADE20K annotations to COCO format')\n    parser.add_argument('src', help='ade20k data path')\n    parser.add_argument('--task', help='task name', default='panoptic')\n    args = parser.parse_args()\n    return args\n\n\ndef prepare_instance_annotations(dataset_dir: str):\n    dataset_dir = Path(dataset_dir)\n    for name, dirname in [('train', 'training'), ('val', 'validation')]:\n        image_dir = dataset_dir / 'images' / dirname\n        instance_dir = dataset_dir / 'annotations_instance' / dirname\n\n        ann_id = 0\n\n        # json\n        out_file = dataset_dir / f'ade20k_instance_{name}.json'\n\n        # json config\n        instance_config_file = dataset_dir / 'imgCatIds.json'\n        with open(instance_config_file, 'r') as f:\n            category_dict = json.load(f)['categories']\n\n        # catid mapping\n        mapping_file = dataset_dir / 'categoryMapping.txt'\n        with open(mapping_file, 'r') as f:\n            map_id = {}\n            for i, line in enumerate(f.readlines()):\n                if i == 0:\n                    continue\n                ins_id, sem_id, _ = line.strip().split()\n                map_id[int(ins_id)] = int(sem_id) - 1\n\n        for cat in category_dict:\n            cat['id'] = map_id[cat['id']]\n\n        filenames = sorted(list(image_dir.iterdir()))\n\n        ann_dict = {}\n        images = []\n        annotations = []\n\n        progressbar = ProgressBar(len(filenames))\n        for filename in filenames:\n            image = {}\n            image_id = filename.stem\n\n            image['id'] = image_id\n            image['file_name'] = filename.name\n\n            original_format = np.array(Image.open(filename))\n            image['height'] = original_format.shape[0]\n            image['width'] = original_format.shape[1]\n\n            images.append(image)\n\n            instance_file = instance_dir / f'{image_id}.png'\n            ins_seg = np.array(Image.open(instance_file))\n            assert ins_seg.dtype == np.uint8\n\n            instance_cat_ids = ins_seg[..., 0]\n            instance_ins_ids = ins_seg[..., 1]\n\n            for thing_id in np.unique(instance_ins_ids):\n                if thing_id == 0:\n                    continue\n                mask = instance_ins_ids == thing_id\n                instance_cat_id = np.unique(instance_cat_ids[mask])\n                assert len(instance_cat_id) == 1\n\n                anno = {}\n                anno['id'] = ann_id\n                ann_id += 1\n                anno['image_id'] = image['id']\n                anno['iscrowd'] = int(0)\n                anno['category_id'] = int(map_id[instance_cat_id[0]])\n\n                inds = np.nonzero(mask)\n                ymin, ymax = inds[0].min(), inds[0].max()\n                xmin, xmax = inds[1].min(), inds[1].max()\n                anno['bbox'] = [\n                    int(xmin),\n                    int(ymin),\n                    int(xmax - xmin + 1),\n                    int(ymax - ymin + 1)\n                ]\n\n                rle = mask_util.encode(\n                    np.array(mask[:, :, np.newaxis], order='F',\n                             dtype='uint8'))[0]\n                rle['counts'] = rle['counts'].decode('utf-8')\n                anno['segmentation'] = rle\n                anno['area'] = int(mask_util.area(rle))\n                annotations.append(anno)\n            progressbar.update()\n\n        ann_dict['images'] = images\n        ann_dict['categories'] = category_dict\n        ann_dict['annotations'] = annotations\n        save_json(ann_dict, out_file)\n\n\ndef prepare_panoptic_annotations(dataset_dir: str):\n    dataset_dir = Path(dataset_dir)\n\n    for name, dirname in [('train', 'training'), ('val', 'validation')]:\n        image_dir = dataset_dir / 'images' / dirname\n        semantic_dir = dataset_dir / 'annotations' / dirname\n        instance_dir = dataset_dir / 'annotations_instance' / dirname\n\n        # folder to store panoptic PNGs\n        out_folder = dataset_dir / f'ade20k_panoptic_{name}'\n        # json with segmentations information\n        out_file = dataset_dir / f'ade20k_panoptic_{name}.json'\n\n        mkdir_or_exist(out_folder)\n\n        # catid mapping\n        neworder_categories = []\n        all_classes = ORIGINAL_CATEGORIES\n        thing_classes = ADE20KPanopticDataset.METAINFO['thing_classes']\n        stuff_classes = ADE20KPanopticDataset.METAINFO['stuff_classes']\n        palette = ADE20KPanopticDataset.METAINFO['palette']\n\n        old_2_new_mapping = {}\n        new_2_old_mapping = {}\n        for i, t in enumerate(thing_classes):\n            j = list(all_classes).index(t)\n            old_2_new_mapping[j] = i\n            new_2_old_mapping[i] = j\n\n        for i, t in enumerate(stuff_classes):\n            j = list(all_classes).index(t)\n            old_2_new_mapping[j] = i + len(thing_classes)\n            new_2_old_mapping[i + len(thing_classes)] = j\n\n        for old, new in old_2_new_mapping.items():\n            neworder_categories.append({\n                'id': new,\n                'name': all_classes[old],\n                'isthing': int(new < len(thing_classes)),\n                'color': palette[new]\n            })\n        categories_dict = {cat['id']: cat for cat in neworder_categories}\n\n        panoptic_json_categories = neworder_categories[:]\n        panoptic_json_images = []\n        panoptic_json_annotations = []\n\n        filenames = sorted(list(image_dir.iterdir()))\n        progressbar = ProgressBar(len(filenames))\n        for filename in filenames:\n            panoptic_json_image = {}\n\n            image_id = filename.stem\n\n            panoptic_json_image['id'] = image_id\n            panoptic_json_image['file_name'] = filename.name\n\n            original_format = np.array(Image.open(filename))\n            panoptic_json_image['height'] = original_format.shape[0]\n            panoptic_json_image['width'] = original_format.shape[1]\n\n            pan_seg = np.zeros(\n                (original_format.shape[0], original_format.shape[1], 3),\n                dtype=np.uint8)\n            id_generator = IdGenerator(categories_dict)\n\n            filename_semantic = semantic_dir / f'{image_id}.png'\n            filename_instance = instance_dir / f'{image_id}.png'\n\n            sem_seg = np.array(Image.open(filename_semantic))\n            ins_seg = np.array(Image.open(filename_instance))\n\n            assert sem_seg.dtype == np.uint8\n            assert ins_seg.dtype == np.uint8\n\n            semantic_cat_ids = sem_seg - 1\n            instance_cat_ids = ins_seg[..., 0] - 1\n            # instance id starts from 1!\n            # because 0 is reserved as VOID label\n            instance_ins_ids = ins_seg[..., 1]\n\n            segm_info = []\n\n            # process stuffs\n            for semantic_cat_id in np.unique(semantic_cat_ids):\n                if semantic_cat_id == 255:\n                    continue\n                if categories_dict[old_2_new_mapping[int(\n                        semantic_cat_id)]]['isthing'] == 1:\n                    continue\n                mask = semantic_cat_ids == semantic_cat_id\n                # should not have any overlap\n                assert pan_seg[mask].sum() == 0\n\n                segment_id, color = id_generator.get_id_and_color(\n                    old_2_new_mapping[int(semantic_cat_id)])\n                pan_seg[mask] = color\n\n                area = np.sum(mask)\n                # bbox computation for a segment\n                hor = np.sum(mask, axis=0)\n                hor_idx = np.nonzero(hor)[0]\n                x = hor_idx[0]\n                width = hor_idx[-1] - x + 1\n                vert = np.sum(mask, axis=1)\n                vert_idx = np.nonzero(vert)[0]\n                y = vert_idx[0]\n                height = vert_idx[-1] - y + 1\n                bbox = [int(x), int(y), int(width), int(height)]\n\n                segm_info.append({\n                    'id':\n                    int(segment_id),\n                    'category_id':\n                    old_2_new_mapping[int(semantic_cat_id)],\n                    'area':\n                    int(area),\n                    'bbox':\n                    bbox,\n                    'iscrowd':\n                    0\n                })\n\n            # process things\n            for thing_id in np.unique(instance_ins_ids):\n                if thing_id == 0:\n                    continue\n                mask = instance_ins_ids == thing_id\n\n                instance_cat_id = np.unique(instance_cat_ids[mask])\n                assert len(instance_cat_id) == 1\n\n                segment_id, color = id_generator.get_id_and_color(\n                    instance_cat_id[0])\n                pan_seg[mask] = color\n\n                area = np.sum(mask)\n                # bbox computation for a segment\n                hor = np.sum(mask, axis=0)\n                hor_idx = np.nonzero(hor)[0]\n                x = hor_idx[-1] - x + 1\n                width = hor_idx[-1] - x + 1\n                vert = np.sum(mask, axis=1)\n                vert_idx = np.nonzero(vert)[0]\n                y = vert_idx[0]\n                height = vert_idx[-1] - y + 1\n                bbox = [int(x), int(y), int(width), int(height)]\n\n                segm_info.append({\n                    'id': int(segment_id),\n                    'category_id': int(instance_cat_id[0]),\n                    'area': int(area),\n                    'bbox': bbox,\n                    'iscrowd': 0\n                })\n\n            panoptic_json_annotation = {\n                'image_id': image_id,\n                'file_name': image_id + '.png',\n                'segments_info': segm_info\n            }\n\n            Image.fromarray(pan_seg).save(out_folder / f'{image_id}.png')\n\n            panoptic_json_images.append(panoptic_json_image)\n            panoptic_json_annotations.append(panoptic_json_annotation)\n\n            progressbar.update()\n\n        panoptic_json = {\n            'images': panoptic_json_images,\n            'annotations': panoptic_json_annotations,\n            'categories': panoptic_json_categories\n        }\n        save_json(panoptic_json, out_file)\n\n\ndef main():\n    args = parse_args()\n    assert args.task in ['panoptic', 'instance']\n    src = args.src\n    if args.task == 'panoptic':\n        annotation_train_path = f'{src}/ade20k_panoptic_train'\n        annotation_val_path = f'{src}/ade20k_panoptic_val'\n        print('Preparing ADE20K panoptic annotations ...')\n        print(\n            f'Creating panoptic annotations to {annotation_train_path} and {annotation_val_path} ...'  # noqa\n        )\n        if os.path.exists(annotation_train_path) or os.path.exists(\n                annotation_val_path):\n            raise RuntimeError('Panoptic annotations already exist.')\n        prepare_panoptic_annotations(src)\n        print('Done.')\n    else:\n        annotation_train_path = f'{src}/ade20k_instance_train'\n        annotation_val_path = f'{src}/ade20k_instance_val'\n        print('Preparing ADE20K instance annotations ...')\n        print(\n            f'Creating instance annotations to {annotation_train_path} and {annotation_val_path} ...'  # noqa\n        )\n        if os.path.exists(annotation_train_path) or os.path.exists(\n                annotation_val_path):\n            raise RuntimeError('Instance annotations already exist.')\n        prepare_instance_annotations(src)\n        print('Done.')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/cityscapes.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport glob\nimport os.path as osp\n\nimport cityscapesscripts.helpers.labels as CSLabels\nimport mmcv\nimport numpy as np\nimport pycocotools.mask as maskUtils\nfrom mmengine.fileio import dump\nfrom mmengine.utils import (Timer, mkdir_or_exist, track_parallel_progress,\n                            track_progress)\n\n\ndef collect_files(img_dir, gt_dir):\n    suffix = 'leftImg8bit.png'\n    files = []\n    for img_file in glob.glob(osp.join(img_dir, '**/*.png')):\n        assert img_file.endswith(suffix), img_file\n        inst_file = gt_dir + img_file[\n            len(img_dir):-len(suffix)] + 'gtFine_instanceIds.png'\n        # Note that labelIds are not converted to trainId for seg map\n        segm_file = gt_dir + img_file[\n            len(img_dir):-len(suffix)] + 'gtFine_labelIds.png'\n        files.append((img_file, inst_file, segm_file))\n    assert len(files), f'No images found in {img_dir}'\n    print(f'Loaded {len(files)} images from {img_dir}')\n\n    return files\n\n\ndef collect_annotations(files, nproc=1):\n    print('Loading annotation images')\n    if nproc > 1:\n        images = track_parallel_progress(load_img_info, files, nproc=nproc)\n    else:\n        images = track_progress(load_img_info, files)\n\n    return images\n\n\ndef load_img_info(files):\n    img_file, inst_file, segm_file = files\n    inst_img = mmcv.imread(inst_file, 'unchanged')\n    # ids < 24 are stuff labels (filtering them first is about 5% faster)\n    unique_inst_ids = np.unique(inst_img[inst_img >= 24])\n    anno_info = []\n    for inst_id in unique_inst_ids:\n        # For non-crowd annotations, inst_id // 1000 is the label_id\n        # Crowd annotations have <1000 instance ids\n        label_id = inst_id // 1000 if inst_id >= 1000 else inst_id\n        label = CSLabels.id2label[label_id]\n        if not label.hasInstances or label.ignoreInEval:\n            continue\n\n        category_id = label.id\n        iscrowd = int(inst_id < 1000)\n        mask = np.asarray(inst_img == inst_id, dtype=np.uint8, order='F')\n        mask_rle = maskUtils.encode(mask[:, :, None])[0]\n\n        area = maskUtils.area(mask_rle)\n        # convert to COCO style XYWH format\n        bbox = maskUtils.toBbox(mask_rle)\n\n        # for json encoding\n        mask_rle['counts'] = mask_rle['counts'].decode()\n\n        anno = dict(\n            iscrowd=iscrowd,\n            category_id=category_id,\n            bbox=bbox.tolist(),\n            area=area.tolist(),\n            segmentation=mask_rle)\n        anno_info.append(anno)\n    video_name = osp.basename(osp.dirname(img_file))\n    img_info = dict(\n        # remove img_prefix for filename\n        file_name=osp.join(video_name, osp.basename(img_file)),\n        height=inst_img.shape[0],\n        width=inst_img.shape[1],\n        anno_info=anno_info,\n        segm_file=osp.join(video_name, osp.basename(segm_file)))\n\n    return img_info\n\n\ndef cvt_annotations(image_infos, out_json_name):\n    out_json = dict()\n    img_id = 0\n    ann_id = 0\n    out_json['images'] = []\n    out_json['categories'] = []\n    out_json['annotations'] = []\n    for image_info in image_infos:\n        image_info['id'] = img_id\n        anno_infos = image_info.pop('anno_info')\n        out_json['images'].append(image_info)\n        for anno_info in anno_infos:\n            anno_info['image_id'] = img_id\n            anno_info['id'] = ann_id\n            out_json['annotations'].append(anno_info)\n            ann_id += 1\n        img_id += 1\n    for label in CSLabels.labels:\n        if label.hasInstances and not label.ignoreInEval:\n            cat = dict(id=label.id, name=label.name)\n            out_json['categories'].append(cat)\n\n    if len(out_json['annotations']) == 0:\n        out_json.pop('annotations')\n\n    dump(out_json, out_json_name)\n    return out_json\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert Cityscapes annotations to COCO format')\n    parser.add_argument('cityscapes_path', help='cityscapes data path')\n    parser.add_argument('--img-dir', default='leftImg8bit', type=str)\n    parser.add_argument('--gt-dir', default='gtFine', type=str)\n    parser.add_argument('-o', '--out-dir', help='output path')\n    parser.add_argument(\n        '--nproc', default=1, type=int, help='number of process')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    cityscapes_path = args.cityscapes_path\n    out_dir = args.out_dir if args.out_dir else cityscapes_path\n    mkdir_or_exist(out_dir)\n\n    img_dir = osp.join(cityscapes_path, args.img_dir)\n    gt_dir = osp.join(cityscapes_path, args.gt_dir)\n\n    set_name = dict(\n        train='instancesonly_filtered_gtFine_train.json',\n        val='instancesonly_filtered_gtFine_val.json',\n        test='instancesonly_filtered_gtFine_test.json')\n\n    for split, json_name in set_name.items():\n        print(f'Converting {split} into {json_name}')\n        with Timer(print_tmpl='It took {}s to convert Cityscapes annotation'):\n            files = collect_files(\n                osp.join(img_dir, split), osp.join(gt_dir, split))\n            image_infos = collect_annotations(files, nproc=args.nproc)\n            cvt_annotations(image_infos, osp.join(out_dir, json_name))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/coco2odvg.py",
    "content": "import argparse\nimport json\nimport os.path\n\nimport jsonlines\nfrom pycocotools.coco import COCO\nfrom tqdm import tqdm\n\nid_map = {\n    0: 1,\n    1: 2,\n    2: 3,\n    3: 4,\n    4: 5,\n    5: 6,\n    6: 7,\n    7: 8,\n    8: 9,\n    9: 10,\n    10: 11,\n    11: 13,\n    12: 14,\n    13: 15,\n    14: 16,\n    15: 17,\n    16: 18,\n    17: 19,\n    18: 20,\n    19: 21,\n    20: 22,\n    21: 23,\n    22: 24,\n    23: 25,\n    24: 27,\n    25: 28,\n    26: 31,\n    27: 32,\n    28: 33,\n    29: 34,\n    30: 35,\n    31: 36,\n    32: 37,\n    33: 38,\n    34: 39,\n    35: 40,\n    36: 41,\n    37: 42,\n    38: 43,\n    39: 44,\n    40: 46,\n    41: 47,\n    42: 48,\n    43: 49,\n    44: 50,\n    45: 51,\n    46: 52,\n    47: 53,\n    48: 54,\n    49: 55,\n    50: 56,\n    51: 57,\n    52: 58,\n    53: 59,\n    54: 60,\n    55: 61,\n    56: 62,\n    57: 63,\n    58: 64,\n    59: 65,\n    60: 67,\n    61: 70,\n    62: 72,\n    63: 73,\n    64: 74,\n    65: 75,\n    66: 76,\n    67: 77,\n    68: 78,\n    69: 79,\n    70: 80,\n    71: 81,\n    72: 82,\n    73: 84,\n    74: 85,\n    75: 86,\n    76: 87,\n    77: 88,\n    78: 89,\n    79: 90\n}\nkey_list_coco = list(id_map.keys())\nval_list_coco = list(id_map.values())\nkey_list_o365 = [i for i in range(365)]\nval_list_o365 = [i for i in range(1, 366)]\nkey_list_v3det = [i for i in range(13204)]\nval_list_v3det = [i for i in range(1, 13205)]\n\n\ndef dump_coco_label_map(args):\n    ori_map = {\n        '1': 'person',\n        '2': 'bicycle',\n        '3': 'car',\n        '4': 'motorcycle',\n        '5': 'airplane',\n        '6': 'bus',\n        '7': 'train',\n        '8': 'truck',\n        '9': 'boat',\n        '10': 'traffic light',\n        '11': 'fire hydrant',\n        '13': 'stop sign',\n        '14': 'parking meter',\n        '15': 'bench',\n        '16': 'bird',\n        '17': 'cat',\n        '18': 'dog',\n        '19': 'horse',\n        '20': 'sheep',\n        '21': 'cow',\n        '22': 'elephant',\n        '23': 'bear',\n        '24': 'zebra',\n        '25': 'giraffe',\n        '27': 'backpack',\n        '28': 'umbrella',\n        '31': 'handbag',\n        '32': 'tie',\n        '33': 'suitcase',\n        '34': 'frisbee',\n        '35': 'skis',\n        '36': 'snowboard',\n        '37': 'sports ball',\n        '38': 'kite',\n        '39': 'baseball bat',\n        '40': 'baseball glove',\n        '41': 'skateboard',\n        '42': 'surfboard',\n        '43': 'tennis racket',\n        '44': 'bottle',\n        '46': 'wine glass',\n        '47': 'cup',\n        '48': 'fork',\n        '49': 'knife',\n        '50': 'spoon',\n        '51': 'bowl',\n        '52': 'banana',\n        '53': 'apple',\n        '54': 'sandwich',\n        '55': 'orange',\n        '56': 'broccoli',\n        '57': 'carrot',\n        '58': 'hot dog',\n        '59': 'pizza',\n        '60': 'donut',\n        '61': 'cake',\n        '62': 'chair',\n        '63': 'couch',\n        '64': 'potted plant',\n        '65': 'bed',\n        '67': 'dining table',\n        '70': 'toilet',\n        '72': 'tv',\n        '73': 'laptop',\n        '74': 'mouse',\n        '75': 'remote',\n        '76': 'keyboard',\n        '77': 'cell phone',\n        '78': 'microwave',\n        '79': 'oven',\n        '80': 'toaster',\n        '81': 'sink',\n        '82': 'refrigerator',\n        '84': 'book',\n        '85': 'clock',\n        '86': 'vase',\n        '87': 'scissors',\n        '88': 'teddy bear',\n        '89': 'hair drier',\n        '90': 'toothbrush'\n    }\n    new_map = {}\n    for key, value in ori_map.items():\n        label = int(key)\n        ind = val_list_coco.index(label)\n        label_trans = key_list_coco[ind]\n        new_map[label_trans] = value\n    if args.output is None:\n        output = os.path.dirname(args.input) + '/coco2017_label_map.json'\n    else:\n        output = os.path.dirname(args.output) + '/coco2017_label_map.json'\n    with open(output, 'w') as f:\n        json.dump(new_map, f)\n\n\ndef dump_o365v1_label_map(args):\n    with open(args.input, 'r') as f:\n        j = json.load(f)\n    o_dict = {}\n    for category in j['categories']:\n        index = str(int(category['id']) - 1)\n        name = category['name']\n        o_dict[index] = name\n    if args.output is None:\n        output = os.path.dirname(args.input) + '/o365v1_label_map.json'\n    else:\n        output = os.path.dirname(args.output) + '/o365v1_label_map.json'\n    with open(output, 'w') as f:\n        json.dump(o_dict, f)\n\n\ndef dump_o365v2_label_map(args):\n    with open(args.input, 'r') as f:\n        j = json.load(f)\n    o_dict = {}\n    for category in j['categories']:\n        index = str(int(category['id']) - 1)\n        name = category['name']\n        o_dict[index] = name\n    if args.output is None:\n        output = os.path.dirname(args.input) + '/o365v2_label_map.json'\n    else:\n        output = os.path.dirname(args.output) + '/o365v2_label_map.json'\n    with open(output, 'w') as f:\n        json.dump(o_dict, f)\n\n\ndef dump_v3det_label_map(args):\n    with open(args.input, 'r') as f:\n        j = json.load(f)\n    o_dict = {}\n    for category in j['categories']:\n        index = str(int(category['id']) - 1)\n        name = category['name']\n        o_dict[index] = name\n    if args.output is None:\n        output = os.path.dirname(args.input) + '/v3det_2023_v1_label_map.json'\n    else:\n        output = os.path.dirname(args.output) + '/v3det_2023_v1_label_map.json'\n    with open(output, 'w') as f:\n        json.dump(o_dict, f)\n\n\ndef coco2odvg(args):\n    coco = COCO(args.input)\n    cats = coco.loadCats(coco.getCatIds())\n    nms = {cat['id']: cat['name'] for cat in cats}\n    metas = []\n    if args.output is None:\n        out_path = args.input[:-5] + '_od.json'\n    else:\n        out_path = args.output\n\n    if args.dataset == 'coco':\n        key_list = key_list_coco\n        val_list = val_list_coco\n        dump_coco_label_map(args)\n    elif args.dataset == 'o365v1':\n        key_list = key_list_o365\n        val_list = val_list_o365\n        dump_o365v1_label_map(args)\n    elif args.dataset == 'o365v2':\n        key_list = key_list_o365\n        val_list = val_list_o365\n        dump_o365v2_label_map(args)\n    elif args.dataset == 'v3det':\n        key_list = key_list_v3det\n        val_list = val_list_v3det\n        dump_v3det_label_map(args)\n\n    for img_id, img_info in tqdm(coco.imgs.items()):\n        # missing images\n        if args.dataset == 'o365v2' and img_id in [908726, 320532, 320534]:\n            print(img_info['file_name'])\n            continue\n        if args.dataset == 'o365v1' and img_id in [6, 19, 23]:\n            print(img_info['file_name'])\n            continue\n\n        if args.dataset == 'o365v2':\n            file_name = img_info['file_name']\n            if file_name.startswith('images/v2/'):\n                file_name = file_name.replace('images/v2/', '')\n            elif file_name.startswith('images/v1/'):\n                file_name = file_name.replace('images/v1/', '')\n            img_info['file_name'] = file_name\n\n        ann_ids = coco.getAnnIds(imgIds=img_id)\n        instance_list = []\n        for ann_id in ann_ids:\n            ann = coco.anns[ann_id]\n\n            if ann.get('ignore', False):\n                continue\n            x1, y1, w, h = ann['bbox']\n            inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                continue\n\n            if ann.get('iscrowd', False):\n                continue\n\n            bbox_xyxy = [x1, y1, x1 + w, y1 + h]\n            label = ann['category_id']\n            category = nms[label]\n            ind = val_list.index(label)\n            label_trans = key_list[ind]\n            instance_list.append({\n                'bbox': bbox_xyxy,\n                'label': label_trans,\n                'category': category\n            })\n        metas.append({\n            'filename': img_info['file_name'],\n            'height': img_info['height'],\n            'width': img_info['width'],\n            'detection': {\n                'instances': instance_list\n            }\n        })\n\n    with jsonlines.open(out_path, mode='w') as writer:\n        writer.write_all(metas)\n\n    print('save to {}'.format(out_path))\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser('coco to odvg format.', add_help=True)\n    parser.add_argument('input', type=str, help='input json file name')\n    parser.add_argument(\n        '--output', '-o', type=str, help='output json file name')\n    parser.add_argument(\n        '--dataset',\n        '-d',\n        required=True,\n        type=str,\n        choices=['coco', 'o365v1', 'o365v2', 'v3det'],\n    )\n    args = parser.parse_args()\n\n    coco2odvg(args)\n"
  },
  {
    "path": "tools/dataset_converters/coco2ovd.py",
    "content": "import argparse\nimport json\nimport os.path\n\nbase_classes = ('person', 'bicycle', 'car', 'motorcycle', 'train', 'truck',\n                'boat', 'bench', 'bird', 'horse', 'sheep', 'bear', 'zebra',\n                'giraffe', 'backpack', 'handbag', 'suitcase', 'frisbee',\n                'skis', 'kite', 'surfboard', 'bottle', 'fork', 'spoon', 'bowl',\n                'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot',\n                'pizza', 'donut', 'chair', 'bed', 'toilet', 'tv', 'laptop',\n                'mouse', 'remote', 'microwave', 'oven', 'toaster',\n                'refrigerator', 'book', 'clock', 'vase', 'toothbrush')\n\nnovel_classes = ('airplane', 'bus', 'cat', 'dog', 'cow', 'elephant',\n                 'umbrella', 'tie', 'snowboard', 'skateboard', 'cup', 'knife',\n                 'cake', 'couch', 'keyboard', 'sink', 'scissors')\n\n\ndef filter_annotation(anno_dict, split_name_list, class_id_to_split):\n    filtered_categories = []\n    for item in anno_dict['categories']:\n        if class_id_to_split.get(item['id']) in split_name_list:\n            item['split'] = class_id_to_split.get(item['id'])\n            filtered_categories.append(item)\n    anno_dict['categories'] = filtered_categories\n\n    filtered_images = []\n    filtered_annotations = []\n    useful_image_ids = set()\n    for item in anno_dict['annotations']:\n        if class_id_to_split.get(item['category_id']) in split_name_list:\n            filtered_annotations.append(item)\n            useful_image_ids.add(item['image_id'])\n    for item in anno_dict['images']:\n        if item['id'] in useful_image_ids:\n            filtered_images.append(item)\n    anno_dict['annotations'] = filtered_annotations\n    anno_dict['images'] = filtered_images\n\n\ndef coco2ovd(args):\n    ann_path = os.path.join(args.data_root, 'annotations/')\n    with open(ann_path + 'instances_train2017.json', 'r') as fin:\n        coco_train_anno_all = json.load(fin)\n\n    class_id_to_split = {}\n    for item in coco_train_anno_all['categories']:\n        if item['name'] in base_classes:\n            class_id_to_split[item['id']] = 'seen'\n        elif item['name'] in novel_classes:\n            class_id_to_split[item['id']] = 'unseen'\n\n    filter_annotation(coco_train_anno_all, ['seen'], class_id_to_split)\n    with open(ann_path + 'instances_train2017_seen_2.json', 'w') as fout:\n        json.dump(coco_train_anno_all, fout)\n\n    with open(ann_path + 'instances_val2017.json', 'r') as fin:\n        coco_val_anno_all = json.load(fin)\n\n    filter_annotation(coco_val_anno_all, ['seen', 'unseen'], class_id_to_split)\n    with open(ann_path + 'instances_val2017_all_2.json', 'w') as fout:\n        json.dump(coco_val_anno_all, fout)\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser('coco to ovd format.', add_help=True)\n    parser.add_argument('data_root', type=str, help='coco root path')\n    args = parser.parse_args()\n\n    coco2ovd(args)\n"
  },
  {
    "path": "tools/dataset_converters/coco_stuff164k.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os.path as osp\nfrom functools import partial\nfrom glob import glob\n\nimport numpy as np\nfrom mmengine.utils import (mkdir_or_exist, track_parallel_progress,\n                            track_progress)\nfrom PIL import Image\n\nCOCO_LEN = 123287\n\nclsID_to_trID = {\n    0: 0,\n    1: 1,\n    2: 2,\n    3: 3,\n    4: 4,\n    5: 5,\n    6: 6,\n    7: 7,\n    8: 8,\n    9: 9,\n    10: 10,\n    12: 11,\n    13: 12,\n    14: 13,\n    15: 14,\n    16: 15,\n    17: 16,\n    18: 17,\n    19: 18,\n    20: 19,\n    21: 20,\n    22: 21,\n    23: 22,\n    24: 23,\n    26: 24,\n    27: 25,\n    30: 26,\n    31: 27,\n    32: 28,\n    33: 29,\n    34: 30,\n    35: 31,\n    36: 32,\n    37: 33,\n    38: 34,\n    39: 35,\n    40: 36,\n    41: 37,\n    42: 38,\n    43: 39,\n    45: 40,\n    46: 41,\n    47: 42,\n    48: 43,\n    49: 44,\n    50: 45,\n    51: 46,\n    52: 47,\n    53: 48,\n    54: 49,\n    55: 50,\n    56: 51,\n    57: 52,\n    58: 53,\n    59: 54,\n    60: 55,\n    61: 56,\n    62: 57,\n    63: 58,\n    64: 59,\n    66: 60,\n    69: 61,\n    71: 62,\n    72: 63,\n    73: 64,\n    74: 65,\n    75: 66,\n    76: 67,\n    77: 68,\n    78: 69,\n    79: 70,\n    80: 71,\n    81: 72,\n    83: 73,\n    84: 74,\n    85: 75,\n    86: 76,\n    87: 77,\n    88: 78,\n    89: 79,\n    91: 80,\n    92: 81,\n    93: 82,\n    94: 83,\n    95: 84,\n    96: 85,\n    97: 86,\n    98: 87,\n    99: 88,\n    100: 89,\n    101: 90,\n    102: 91,\n    103: 92,\n    104: 93,\n    105: 94,\n    106: 95,\n    107: 96,\n    108: 97,\n    109: 98,\n    110: 99,\n    111: 100,\n    112: 101,\n    113: 102,\n    114: 103,\n    115: 104,\n    116: 105,\n    117: 106,\n    118: 107,\n    119: 108,\n    120: 109,\n    121: 110,\n    122: 111,\n    123: 112,\n    124: 113,\n    125: 114,\n    126: 115,\n    127: 116,\n    128: 117,\n    129: 118,\n    130: 119,\n    131: 120,\n    132: 121,\n    133: 122,\n    134: 123,\n    135: 124,\n    136: 125,\n    137: 126,\n    138: 127,\n    139: 128,\n    140: 129,\n    141: 130,\n    142: 131,\n    143: 132,\n    144: 133,\n    145: 134,\n    146: 135,\n    147: 136,\n    148: 137,\n    149: 138,\n    150: 139,\n    151: 140,\n    152: 141,\n    153: 142,\n    154: 143,\n    155: 144,\n    156: 145,\n    157: 146,\n    158: 147,\n    159: 148,\n    160: 149,\n    161: 150,\n    162: 151,\n    163: 152,\n    164: 153,\n    165: 154,\n    166: 155,\n    167: 156,\n    168: 157,\n    169: 158,\n    170: 159,\n    171: 160,\n    172: 161,\n    173: 162,\n    174: 163,\n    175: 164,\n    176: 165,\n    177: 166,\n    178: 167,\n    179: 168,\n    180: 169,\n    181: 170,\n    255: 255\n}\n\n\ndef convert_to_trainID(maskpath, out_mask_dir, is_train):\n    mask = np.array(Image.open(maskpath))\n    mask_copy = mask.copy()\n    for clsID, trID in clsID_to_trID.items():\n        mask_copy[mask == clsID] = trID\n    seg_filename = osp.join(out_mask_dir, 'train2017',\n                            osp.basename(maskpath)) if is_train else osp.join(\n                                out_mask_dir, 'val2017',\n                                osp.basename(maskpath))\n    Image.fromarray(mask_copy).save(seg_filename, 'PNG')\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description=\\\n        'Convert COCO Stuff 164k annotations to mmdet format')  # noqa\n    parser.add_argument('coco_path', help='coco stuff path')\n    parser.add_argument(\n        '--out-dir-name',\n        '-o',\n        default='stuffthingmaps_semseg',\n        help='output path')\n    parser.add_argument(\n        '--nproc', default=16, type=int, help='number of process')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    coco_path = args.coco_path\n    out_dir = osp.join(coco_path, args.out_dir_name)\n    nproc = args.nproc\n\n    mkdir_or_exist(osp.join(out_dir, 'train2017'))\n    mkdir_or_exist(osp.join(out_dir, 'val2017'))\n\n    train_list = glob(osp.join(coco_path, 'stuffthingmaps/train2017', '*.png'))\n    val_list = glob(osp.join(coco_path, 'stuffthingmaps/val2017', '*.png'))\n    assert (len(train_list) +\n            len(val_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format(\n                len(train_list), len(val_list))\n\n    if args.nproc > 1:\n        track_parallel_progress(\n            partial(convert_to_trainID, out_mask_dir=out_dir, is_train=True),\n            train_list,\n            nproc=nproc)\n        track_parallel_progress(\n            partial(convert_to_trainID, out_mask_dir=out_dir, is_train=False),\n            val_list,\n            nproc=nproc)\n    else:\n        track_progress(\n            partial(convert_to_trainID, out_mask_dir=out_dir, is_train=True),\n            train_list)\n        track_progress(\n            partial(convert_to_trainID, out_mask_dir=out_dir, is_train=False),\n            val_list)\n\n    print('Done!')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/crowdhuman2coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport json\nimport os\nimport os.path as osp\nfrom collections import defaultdict\n\nimport mmengine\nfrom PIL import Image\nfrom tqdm import tqdm\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='CrowdHuman to COCO Video format')\n    parser.add_argument(\n        '-i',\n        '--input',\n        help='root directory of CrowdHuman annotations',\n    )\n    parser.add_argument(\n        '-o',\n        '--output',\n        help='directory to save coco formatted label file',\n    )\n    return parser.parse_args()\n\n\ndef load_odgt(filename):\n    with open(filename, 'r') as f:\n        lines = f.readlines()\n    data_infos = [json.loads(line.strip('\\n')) for line in lines]\n    return data_infos\n\n\ndef convert_crowdhuman(ann_dir, save_dir, mode='train'):\n    \"\"\"Convert CrowdHuman dataset in COCO style.\n\n    Args:\n        ann_dir (str): The path of CrowdHuman dataset.\n        save_dir (str): The path to save annotation files.\n        mode (str): Convert train dataset or validation dataset. Options are\n            'train', 'val'. Default: 'train'.\n    \"\"\"\n    assert mode in ['train', 'val']\n\n    records = dict(img_id=1, ann_id=1)\n    outputs = defaultdict(list)\n    outputs['categories'] = [dict(id=1, name='pedestrian')]\n\n    data_infos = load_odgt(osp.join(ann_dir, f'annotation_{mode}.odgt'))\n    for data_info in tqdm(data_infos):\n        img_name = osp.join('Images', f\"{data_info['ID']}.jpg\")\n        img = Image.open(osp.join(ann_dir, mode, img_name))\n        width, height = img.size[:2]\n        image = dict(\n            file_name=img_name,\n            height=height,\n            width=width,\n            id=records['img_id'])\n        outputs['images'].append(image)\n\n        if mode != 'test':\n            for ann_info in data_info['gtboxes']:\n                bbox = ann_info['fbox']\n                if 'extra' in ann_info and 'ignore' in ann_info[\n                        'extra'] and ann_info['extra']['ignore'] == 1:\n                    iscrowd = True\n                else:\n                    iscrowd = False\n                ann = dict(\n                    id=records['ann_id'],\n                    image_id=records['img_id'],\n                    category_id=outputs['categories'][0]['id'],\n                    vis_bbox=ann_info['vbox'],\n                    bbox=bbox,\n                    area=bbox[2] * bbox[3],\n                    iscrowd=iscrowd)\n                outputs['annotations'].append(ann)\n                records['ann_id'] += 1\n        records['img_id'] += 1\n\n    if not osp.isdir(save_dir):\n        os.makedirs(save_dir)\n    mmengine.dump(outputs, osp.join(save_dir, f'crowdhuman_{mode}.json'))\n    print(f'-----CrowdHuman {mode} set------')\n    print(f'total {records[\"img_id\"] - 1} images')\n    if mode != 'test':\n        print(f'{records[\"ann_id\"] - 1} pedestrians are annotated.')\n    print('-----------------------')\n\n\ndef main():\n    args = parse_args()\n    convert_crowdhuman(args.input, args.output, mode='train')\n    convert_crowdhuman(args.input, args.output, mode='val')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/extract_coco_from_mixed.py",
    "content": "import argparse\nimport os.path as osp\n\nimport mmengine\nfrom pycocotools.coco import COCO\n\n\ndef extract_coco(args):\n    coco = COCO(args.mixed_ann)\n\n    json_data = mmengine.load(args.mixed_ann)\n    new_json_data = {\n        'info': json_data['info'],\n        'licenses': json_data['licenses'],\n        'categories': json_data['categories'],\n        'images': [],\n        'annotations': []\n    }\n    del json_data\n\n    img_ids = coco.getImgIds()\n    for img_id in img_ids:\n        img_info = coco.loadImgs([img_id])[0]\n        if img_info['data_source'] == 'coco':\n            new_json_data['images'].append(img_info)\n            ann_ids = coco.getAnnIds(imgIds=[img_id])\n            img_ann_info = coco.loadAnns(ann_ids)\n            new_json_data['annotations'].extend(img_ann_info)\n    if args.out_ann is None:\n        out_ann = osp.dirname(\n            args.mixed_ann) + '/final_mixed_train_only_coco.json'\n        mmengine.dump(new_json_data, out_ann)\n        print('save new json to {}'.format(out_ann))\n    else:\n        mmengine.dump(new_json_data, args.out_ann)\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(\n        'split mixed goldg to coco.', add_help=True)\n    parser.add_argument('mixed_ann', type=str)\n    parser.add_argument('--out-ann', '-o', type=str)\n    args = parser.parse_args()\n\n    extract_coco(args)\n"
  },
  {
    "path": "tools/dataset_converters/fix_o365_names.py",
    "content": "# Reference: https://github.com/shenyunhang/APE/blob/main/datasets/tools/objects3652coco/fix_o365_names.py # noqa\nimport argparse\nimport copy\nimport json\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        '--ann',\n        default='data/objects365v2/annotations/zhiyuan_objv2_train.json')\n    parser.add_argument(\n        '--fix_name_map',\n        default='tools/dataset_converters/zhiyuan_objv2_train_names_fix.csv')\n    args = parser.parse_args()\n\n    new_names = {}\n    old_names = {}\n    with open(args.fix_name_map, 'r') as f:\n        for line in f:\n            tmp = line.strip().split(',')\n            old_names[int(tmp[0])] = tmp[1]\n            new_names[int(tmp[0])] = tmp[2]\n    data = json.load(open(args.ann, 'r'))\n\n    cat_info = copy.deepcopy(data['categories'])\n\n    for x in cat_info:\n        if old_names[x['id']] != new_names[x['id']]:\n            print('Renaming', x['id'], x['name'], new_names[x['id']])\n            x['name'] = new_names[x['id']]\n\n    data['categories'] = cat_info\n    out_name = args.ann[:-5] + '_fixname.json'\n    print('Saving to', out_name)\n    json.dump(data, open(out_name, 'w'))\n"
  },
  {
    "path": "tools/dataset_converters/goldg2odvg.py",
    "content": "import argparse\n\nimport jsonlines\nfrom pycocotools.coco import COCO\nfrom tqdm import tqdm\n\n\ndef _has_only_empty_bbox(anno):\n    return all(any(o <= 1 for o in obj['bbox'][2:]) for obj in anno)\n\n\ndef has_valid_annotation(anno):\n    # if it's empty, there is no annotation\n    if len(anno) == 0:\n        return False\n    # if all boxes have close to zero area, there is no annotation\n    if _has_only_empty_bbox(anno):\n        return False\n    return True\n\n\ndef goldg2odvg(args):\n    coco = COCO(args.input)\n    ids = list(sorted(coco.imgs.keys()))\n\n    out_results = []\n    for img_id in tqdm(ids):\n        if isinstance(img_id, str):\n            ann_ids = coco.getAnnIds(imgIds=[img_id], iscrowd=0)\n        else:\n            ann_ids = coco.getAnnIds(imgIds=img_id, iscrowd=0)\n        annos = coco.loadAnns(ann_ids)\n        if not has_valid_annotation(annos):\n            continue\n\n        img_info = coco.loadImgs(img_id)[0]\n        file_name = img_info['file_name']\n        caption = img_info['caption']\n\n        regions = {}\n\n        for anno in annos:\n            box = anno['bbox']\n            tokens_positive = anno['tokens_positive']\n            x1, y1, w, h = box\n            inter_w = max(0, min(x1 + w, int(img_info['width'])) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, int(img_info['height'])) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if anno['area'] <= 0 or w < 1 or h < 1:\n                continue\n\n            if anno.get('iscrowd', False):\n                continue\n            bbox_xyxy = [\n                x1, y1,\n                min(x1 + w, int(img_info['width'])),\n                min(y1 + h, int(img_info['height']))\n            ]\n\n            tokens_positive = sorted(tokens_positive, key=lambda x: x[0])\n\n            phrase = []\n            pre_end_index = -10\n            for token in tokens_positive:\n                start_index = token[0]\n                end_index = token[1]\n                if pre_end_index + 1 == start_index:\n                    if caption[token[0] - 1] == ' ':\n                        phrase[\n                            -1] = phrase[-1] + ' ' + caption[token[0]:token[1]]\n                    else:\n                        phrase.append(caption[token[0]:token[1]])\n                else:\n                    phrase.append(caption[token[0]:token[1]])\n                pre_end_index = end_index\n\n            key = ' '.join(phrase)\n\n            if key not in regions:\n                regions[key] = {\n                    'bbox': bbox_xyxy,\n                    'phrase': phrase,\n                    'tokens_positive': tokens_positive\n                }\n            else:\n                old_box = regions[key]['bbox']\n                if isinstance(old_box[0], list):\n                    old_box.append(bbox_xyxy)\n                else:\n                    old_box = [old_box, bbox_xyxy]\n\n                regions[key]['bbox'] = old_box\n\n        out_dict = {\n            'filename': file_name,\n            'height': int(img_info['height']),\n            'width': int(img_info['width']),\n            'grounding': {\n                'caption': caption\n            }\n        }\n\n        region_list = []\n        for key, value in regions.items():\n            phrase = value['phrase']\n            if len(phrase) == 1:\n                phrase = phrase[0]\n            region_list.append({\n                'bbox': value['bbox'],\n                'phrase': phrase,\n                'tokens_positive': value['tokens_positive']\n            })\n        out_dict['grounding']['regions'] = region_list\n        out_results.append(out_dict)\n\n    if args.out_ann is None:\n        out_path = args.input[:-5] + '_vg.json'\n    else:\n        out_path = args.out_ann\n\n    with jsonlines.open(out_path, mode='w') as writer:\n        writer.write_all(out_results)\n    print(f'save to {out_path}')\n\n\n# goldg+: final_mixed_train_no_coco.json +\n# final_flickr_separateGT_train.json +\n# final_mixed_train_only_coco.json\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser('goldg to odvg format.', add_help=True)\n    parser.add_argument('input', type=str, help='input json file name')\n    parser.add_argument('--out-ann', '-o', type=str)\n    args = parser.parse_args()\n\n    goldg2odvg(args)\n"
  },
  {
    "path": "tools/dataset_converters/grit2odvg.py",
    "content": "import argparse\nimport json\nimport multiprocessing\nimport os\nimport os.path as osp\n\nimport emoji\nimport jsonlines\nfrom transformers import AutoTokenizer\n\ntokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')\nis_debug = False\n\n\ndef is_valid_caption(caption, rules={'↙️', '[CLS]', '[SEP]'}):\n    check_anno = caption.strip(\n    )[:-1]  # Remove the ending delimiter from the caption.\n    for ch in rules:\n        if ch in check_anno:\n            return False\n    return True\n\n\ndef process_one_file(anno_file, result_queue):\n    print('processing', anno_file)\n    with open(anno_file, 'r') as f:\n        metas = json.load(f)\n\n    results = []\n    for meta in metas:\n        # print('============================')\n        file_name = meta['key'][0:5] + '/' + meta['key'] + '.jpg'\n        file_name = osp.join('images', file_name)\n\n        h = meta['height']\n        w = meta['width']\n\n        caption = meta['caption']\n        # Weird captions are filtered out from the beginning.\n        if not is_valid_caption(caption):\n            if is_debug:\n                print('=====caption filtered====', caption)\n            continue\n\n        # Captions exceeding 240 tokens are filtered out,\n        # where 240 is an empirical value.\n        tokenized = tokenizer([caption], return_tensors='pt')\n        if tokenized.input_ids.shape[1] >= 240:\n            if is_debug:\n                print('=====token filtered====', caption)\n            continue\n\n        ref_exps = meta['ref_exps']\n        ref_captions = [i[0:2] for i in ref_exps]\n        ref_token_positives = [i[0:2] for i in ref_exps]\n        ref_captions = [caption[int(i[0]):int(i[1])] for i in ref_captions]\n        ref_boxes = [i[2:6] for i in ref_exps]\n\n        regions = {}\n        for bbox, ref_caption, tokens_positive in zip(ref_boxes, ref_captions,\n                                                      ref_token_positives):\n            #  If the current reference includes special delimiters,\n            #  it will be filtered out.\n            if not is_valid_caption(\n                    caption, rules={'.', '？', ' ', \"\\'\", \"\\\"\"}):\n                if is_debug:\n                    print('=====ref filtered====', caption)\n                continue\n            # If the current reference contains non-ASCII characters,\n            # it will be filtered out.\n            if not str.isascii(caption):\n                if is_debug:\n                    print('=====ref filtered====', caption)\n                continue\n            # If the current reference includes non-ASCII characters,\n            # it will be filtered out.\n            if emoji.emoji_count(caption):\n                if is_debug:\n                    print('=====ref filtered====', caption)\n                continue\n\n            box = [\n                round(bbox[0] * w, 3),\n                round(bbox[1] * h, 3),\n                round((bbox[2]) * w, 3),\n                round((bbox[3]) * h, 3)\n            ]\n            x1, y1, x2, y2 = box\n            inter_w = max(0, min(x1 + w, int(w)) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, int(h)) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                if is_debug:\n                    print('=====wh filtered====', box)\n                continue\n            if w <= 1 or h <= 1:\n                if is_debug:\n                    print('=====area filtered====', box)\n                continue\n\n            if ref_caption not in regions:\n                regions[ref_caption] = {\n                    'bbox':\n                    box,\n                    'phrase':\n                    ref_caption,\n                    'tokens_positive':\n                    [[int(tokens_positive[0]),\n                      int(tokens_positive[1])]],\n                }\n            else:\n                old_box = regions[ref_caption]['bbox']\n                if isinstance(old_box[0], list):\n                    old_box.append(box)\n                else:\n                    old_box = [old_box, box]\n                regions[ref_caption]['bbox'] = old_box\n\n        if len(regions) > 0:\n            print('caption: ', caption)\n            print('regions', regions)\n        else:\n            if is_debug:\n                print('caption: ', caption)\n                print('regions', regions)\n\n        if len(regions) == 0:\n            continue\n\n        out_dict = {\n            'filename': file_name,\n            'height': int(h),\n            'width': int(w),\n            'grounding': {\n                'caption': caption\n            }\n        }\n\n        region_list = []\n        for key, value in regions.items():\n            phrase = value['phrase']\n            if len(phrase) == 1:\n                phrase = phrase[0]\n            region_list.append({\n                'bbox': value['bbox'],\n                'phrase': phrase,\n                'tokens_positive': value['tokens_positive']\n            })\n        out_dict['grounding']['regions'] = region_list\n        print(out_dict)\n        results.append(out_dict)\n    result_queue.put(results)\n\n\ndef grit2odvg(args):\n    annotations_dir = osp.join(args.data_root, 'annotations')\n    annos_files = [\n        osp.join(annotations_dir, anno) for anno in os.listdir(annotations_dir)\n        if anno.endswith('.json') and not anno.endswith('vg.json')\n    ]\n\n    annos_files = annos_files[:2]\n\n    manager = multiprocessing.Manager()\n    result_queue = manager.Queue()\n    pool = multiprocessing.Pool(processes=min(len(annos_files), 16))\n\n    for anno_file in annos_files:\n        pool.apply_async(process_one_file, args=(anno_file, result_queue))\n\n    pool.close()\n    pool.join()\n\n    out_datas = []\n    while not result_queue.empty():\n        out_datas.extend(result_queue.get())\n\n    out_path = osp.join(args.data_root, 'grit20m_vg.json')\n    with jsonlines.open(out_path, mode='w') as writer:\n        writer.write_all(out_datas)\n    print('save to ', out_path)\n    print('total img: ', len(out_datas))\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser('grit to odvg format.', add_help=True)\n    parser.add_argument('data_root', type=str, help='input dir name')\n    args = parser.parse_args()\n\n    grit2odvg(args)\n"
  },
  {
    "path": "tools/dataset_converters/grit_processing.py",
    "content": "import argparse\nimport json\nimport logging\nimport os\nimport tarfile\nfrom functools import partial\nfrom multiprocessing import Pool\n\n\ndef create_logger(output_file):\n    logger = logging.getLogger('grit_logger')\n    logger.setLevel(logging.INFO)  # set logger output level\n    formatter = logging.Formatter('%(asctime)s - %(message)s')\n\n    fh = logging.FileHandler(output_file)\n    fh.setLevel(logging.INFO)\n    fh.setFormatter(formatter)\n\n    console = logging.StreamHandler()\n    console.setLevel(logging.INFO)\n\n    logger.addHandler(fh)\n    logger.addHandler(console)\n\n    return logger\n\n\ndef count_download_image(download_json_dir, logger):\n    parquet_files = [\n        f for f in os.listdir(download_json_dir) if f.endswith('.json')\n    ]\n    len = 0\n\n    for file in parquet_files:\n        with open(os.path.join(download_json_dir, file), 'r') as f:\n            data = json.load(f)\n            len = len + int(data['successes'])\n        logger.info(file + 'has ' + str(data['successes']) +\n                    ' successful images')\n\n    logger.info('all files finished.', str(len),\n                'images have been successfully downloaded.')\n\n\ndef tar_processing(tar_path, output_dir, logger):\n    filepath = untar(tar_path, logger)\n    json_files = [f for f in os.listdir(filepath) if f.endswith('.json')]\n    all_data = []\n    cnt = 0\n\n    for file in json_files:\n        with open(os.path.join(filepath, file), 'r') as f:\n            df = json.load(f)\n        cnt = cnt + 1\n        all_data.extend([df])\n    dir_name = os.path.basename(filepath)\n    # write all data to a json file\n    logger.info(f'{dir_name} has {cnt} jsons')\n    json_name = os.path.basename(filepath) + '.json'\n    if not os.path.exists(os.path.join(output_dir, 'annotations')):\n        os.mkdir(os.path.join(output_dir, 'annotations'))\n    with open(os.path.join(output_dir, 'annotations', json_name), 'w') as f:\n        json.dump(all_data, f)\n    logger.info(f'{dir_name} completed')\n    cp_rm(filepath, output_dir)\n    return os.path.basename(filepath)\n\n\ndef untar(filepath, logger):\n    if tarfile.is_tarfile(filepath):\n        new_folder = os.path.splitext(filepath)[0]\n        tar_name = os.path.basename(filepath)\n        with tarfile.open(filepath) as tar:\n            members = tar.getmembers()\n            if not os.path.exists(new_folder):\n                os.mkdir(new_folder)\n            else:\n                f = os.listdir(new_folder)\n                if len(members) == len(f):\n                    logger.info(f'{tar_name} already decompressed')\n                    return new_folder\n            logger.info(f'{tar_name} decompressing...')\n            os.system(f'tar -xf {filepath} -C {new_folder}')\n            logger.info(f'{tar_name} decompressed!')\n        return new_folder\n\n\ndef cp_rm(filepath, output_dir):\n    # delete txt/json\n    for file in os.listdir(filepath):\n        if file.endswith('.txt') or file.endswith('.json'):\n            os.remove(os.path.join(filepath, file))\n    # move images to output dir\n    target_dir = os.path.join(output_dir, 'images')\n    if not os.path.exists(os.path.join(output_dir, 'images')):\n        os.mkdir(os.path.join(output_dir, 'images'))\n    os.system('mv -f {} {}'.format(filepath, target_dir))\n\n\ndef main(args):\n    logger = create_logger(args.log_name)\n    all_file_name = [\n        os.path.join(args.image_dir, file)\n        for file in os.listdir(args.image_dir) if file.endswith('.tar')\n    ]\n    all_file_name.sort()\n    func = partial(tar_processing, output_dir=args.output_dir, logger=logger)\n    with Pool(processes=args.num_process) as pool:\n        result = pool.imap(func=func, iterable=all_file_name)  # noqa\n        # print(result)\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument('image_dir', type=str)  # grit raw directory\n    parser.add_argument('output_dir', type=str)\n    parser.add_argument('--num-process', default=10)\n    parser.add_argument('--log-name', type=str, default='grit_processing.log')\n    args = parser.parse_args()\n\n    main(args)\n"
  },
  {
    "path": "tools/dataset_converters/images2coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\n\nfrom mmengine.fileio import dump, list_from_file\nfrom mmengine.utils import mkdir_or_exist, scandir, track_iter_progress\nfrom PIL import Image\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert images to coco format without annotations')\n    parser.add_argument('img_path', help='The root path of images')\n    parser.add_argument(\n        'classes', type=str, help='The text file name of storage class list')\n    parser.add_argument(\n        'out',\n        type=str,\n        help='The output annotation json file name, The save dir is in the '\n        'same directory as img_path')\n    parser.add_argument(\n        '-e',\n        '--exclude-extensions',\n        type=str,\n        nargs='+',\n        help='The suffix of images to be excluded, such as \"png\" and \"bmp\"')\n    args = parser.parse_args()\n    return args\n\n\ndef collect_image_infos(path, exclude_extensions=None):\n    img_infos = []\n\n    images_generator = scandir(path, recursive=True)\n    for image_path in track_iter_progress(list(images_generator)):\n        if exclude_extensions is None or (\n                exclude_extensions is not None\n                and not image_path.lower().endswith(exclude_extensions)):\n            image_path = os.path.join(path, image_path)\n            img_pillow = Image.open(image_path)\n            img_info = {\n                'filename': image_path,\n                'width': img_pillow.width,\n                'height': img_pillow.height,\n            }\n            img_infos.append(img_info)\n    return img_infos\n\n\ndef cvt_to_coco_json(img_infos, classes):\n    image_id = 0\n    coco = dict()\n    coco['images'] = []\n    coco['type'] = 'instance'\n    coco['categories'] = []\n    coco['annotations'] = []\n    image_set = set()\n\n    for category_id, name in enumerate(classes):\n        category_item = dict()\n        category_item['supercategory'] = str('none')\n        category_item['id'] = int(category_id)\n        category_item['name'] = str(name)\n        coco['categories'].append(category_item)\n\n    for img_dict in img_infos:\n        file_name = img_dict['filename']\n        assert file_name not in image_set\n        image_item = dict()\n        image_item['id'] = int(image_id)\n        image_item['file_name'] = str(file_name)\n        image_item['height'] = int(img_dict['height'])\n        image_item['width'] = int(img_dict['width'])\n        coco['images'].append(image_item)\n        image_set.add(file_name)\n\n        image_id += 1\n    return coco\n\n\ndef main():\n    args = parse_args()\n    assert args.out.endswith(\n        'json'), 'The output file name must be json suffix'\n\n    # 1 load image list info\n    img_infos = collect_image_infos(args.img_path, args.exclude_extensions)\n\n    # 2 convert to coco format data\n    classes = list_from_file(args.classes)\n    coco_info = cvt_to_coco_json(img_infos, classes)\n\n    # 3 dump\n    save_dir = os.path.join(args.img_path, '..', 'annotations')\n    mkdir_or_exist(save_dir)\n    save_path = os.path.join(save_dir, args.out)\n    dump(coco_info, save_path)\n    print(f'save json file: {save_path}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/lvis2odvg.py",
    "content": "import argparse\nimport json\nimport os.path\n\nimport jsonlines\nfrom lvis import LVIS\nfrom tqdm import tqdm\n\nkey_list_lvis = [i for i in range(1203)]\nval_list_lvis = [i for i in range(1, 1204)]\n\n\ndef dump_lvis_label_map(args):\n    with open(args.input, 'r') as f:\n        j = json.load(f)\n    o_dict = {}\n    for category in j['categories']:\n        index = str(int(category['id']) - 1)\n        name = category['name']\n        o_dict[index] = name\n    if args.output is None:\n        output = os.path.dirname(args.input) + '/lvis_v1_label_map.json'\n    else:\n        output = os.path.dirname(args.output) + '/lvis_v1_label_map.json'\n    with open(output, 'w') as f:\n        json.dump(o_dict, f)\n\n\ndef lvis2odvg(args):\n    lvis = LVIS(args.input)\n    cats = lvis.load_cats(lvis.get_cat_ids())\n    nms = {cat['id']: cat['name'] for cat in cats}\n    metas = []\n    if args.output is None:\n        out_path = args.input[:-5] + '_od.json'\n    else:\n        out_path = args.output\n\n    key_list = key_list_lvis\n    val_list = val_list_lvis\n    dump_lvis_label_map(args)\n\n    for img_id, img_info in tqdm(lvis.imgs.items()):\n        file_name = img_info['coco_url'].replace(\n            'http://images.cocodataset.org/', '')\n        ann_ids = lvis.get_ann_ids(img_ids=[img_id])\n        raw_ann_info = lvis.load_anns(ann_ids)\n        instance_list = []\n        for ann in raw_ann_info:\n            if ann.get('ignore', False):\n                print(f'invalid ignore box of {ann}')\n                continue\n            x1, y1, w, h = ann['bbox']\n            inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                print(f'invalid wh box of {ann}')\n                continue\n            if ann['area'] <= 0 or w < 1 or h < 1:\n                print(f'invalid area box of {ann}, '\n                      f'w={img_info[\"width\"]}, h={img_info[\"height\"]}')\n                continue\n\n            if ann.get('iscrowd', False):\n                print(f'invalid iscrowd box of {ann}')\n                continue\n\n            bbox_xyxy = [x1, y1, x1 + w, y1 + h]\n            label = ann['category_id']\n            category = nms[label]\n            ind = val_list.index(label)\n            label_trans = key_list[ind]\n            instance_list.append({\n                'bbox': bbox_xyxy,\n                'label': label_trans,\n                'category': category\n            })\n        metas.append({\n            'filename': file_name,\n            'height': img_info['height'],\n            'width': img_info['width'],\n            'detection': {\n                'instances': instance_list\n            }\n        })\n\n    with jsonlines.open(out_path, mode='w') as writer:\n        writer.write_all(metas)\n\n    print('save to {}'.format(out_path))\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser('lvis to odvg format.', add_help=True)\n    parser.add_argument('input', type=str, help='input list name')\n    parser.add_argument('--output', '-o', type=str, help='input list name')\n    args = parser.parse_args()\n    lvis2odvg(args)\n"
  },
  {
    "path": "tools/dataset_converters/lvis2ovd.py",
    "content": "import argparse\nimport json\nimport os.path\n\nimport jsonlines\n\n\ndef lvis2ovd(args):\n    ann_path = os.path.join(args.data_root, 'annotations/')\n\n    lvis = json.load(open(ann_path + 'lvis_v1_val.json'))\n    base_class_ids = [\n        cat['id'] - 1 for cat in lvis['categories'] if cat['frequency'] != 'r'\n    ]\n\n    with open(ann_path + 'lvis_v1_train_od.json') as f:\n        data = [json.loads(d) for d in f]\n    for i in range(len(data)):\n        instance = [\n            inst for inst in data[i]['detection']['instances']\n            if inst['label'] in base_class_ids\n        ]\n        data[i]['detection']['instances'] = instance\n    with jsonlines.open(\n            ann_path + 'lvis_v1_train_od_norare.json', mode='w') as writer:\n        writer.write_all(data)\n\n    label_map = json.load(open(ann_path + 'lvis_v1_label_map.json'))\n    label_map = {\n        k: v\n        for k, v in label_map.items() if int(k) in base_class_ids\n    }\n    json.dump(label_map, open(ann_path + 'lvis_v1_label_map_norare.json', 'w'))\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser('lvis to ovd format.', add_help=True)\n    parser.add_argument('data_root', type=str, help='coco root path')\n    args = parser.parse_args()\n\n    lvis2ovd(args)\n"
  },
  {
    "path": "tools/dataset_converters/mot2coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# This script converts MOT labels into COCO style.\n# Official website of the MOT dataset: https://motchallenge.net/\n#\n# Label format of MOT dataset:\n#   GTs:\n#       <frame_id> # starts from 1 but COCO style starts from 0,\n#       <instance_id>, <x1>, <y1>, <w>, <h>,\n#       <conf> # conf is annotated as 0 if the object is ignored,\n#       <class_id>, <visibility>\n#\n#   DETs and Results:\n#       <frame_id>, <instance_id>, <x1>, <y1>, <w>, <h>, <conf>,\n#       <x>, <y>, <z> # for 3D objects\n\nimport argparse\nimport os\nimport os.path as osp\nfrom collections import defaultdict\n\nimport mmengine\nimport numpy as np\nfrom tqdm import tqdm\n\n# Classes in MOT:\nCLASSES = [\n    dict(id=1, name='pedestrian'),\n    dict(id=2, name='person_on_vehicle'),\n    dict(id=3, name='car'),\n    dict(id=4, name='bicycle'),\n    dict(id=5, name='motorbike'),\n    dict(id=6, name='non_mot_vehicle'),\n    dict(id=7, name='static_person'),\n    dict(id=8, name='distractor'),\n    dict(id=9, name='occluder'),\n    dict(id=10, name='occluder_on_ground'),\n    dict(id=11, name='occluder_full'),\n    dict(id=12, name='reflection'),\n    dict(id=13, name='crowd')\n]\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert MOT label and detections to COCO-VID format.')\n    parser.add_argument('-i', '--input', help='path of MOT data')\n    parser.add_argument(\n        '-o', '--output', help='path to save coco formatted label file')\n    parser.add_argument(\n        '--convert-det',\n        action='store_true',\n        help='convert official detection results.')\n    parser.add_argument(\n        '--split-train',\n        action='store_true',\n        help='split the train set into half-train and half-validate.')\n    return parser.parse_args()\n\n\ndef parse_gts(gts, is_mot15):\n    outputs = defaultdict(list)\n    for gt in gts:\n        gt = gt.strip().split(',')\n        frame_id, ins_id = map(int, gt[:2])\n        bbox = list(map(float, gt[2:6]))\n        if is_mot15:\n            conf = 1.\n            category_id = 1\n            visibility = 1.\n        else:\n            conf = float(gt[6])\n            category_id = int(gt[7])\n            visibility = float(gt[8])\n        anns = dict(\n            category_id=category_id,\n            bbox=bbox,\n            area=bbox[2] * bbox[3],\n            iscrowd=False,\n            visibility=visibility,\n            mot_instance_id=ins_id,\n            mot_conf=conf)\n        outputs[frame_id].append(anns)\n    return outputs\n\n\ndef parse_dets(dets):\n    outputs = defaultdict(list)\n    for det in dets:\n        det = det.strip().split(',')\n        frame_id, ins_id = map(int, det[:2])\n        assert ins_id == -1\n        bbox = list(map(float, det[2:7]))\n        # [x1, y1, x2, y2] to be consistent with mmdet\n        bbox = [\n            bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3], bbox[4]\n        ]\n        outputs[frame_id].append(bbox)\n\n    return outputs\n\n\ndef main():\n    args = parse_args()\n    if not osp.isdir(args.output):\n        os.makedirs(args.output)\n\n    sets = ['train', 'test']\n    if args.split_train:\n        sets += ['half-train', 'half-val']\n    vid_id, img_id, ann_id = 1, 1, 1\n\n    for subset in sets:\n        ins_id = 0\n        print(f'Converting {subset} set to COCO format')\n        if 'half' in subset:\n            in_folder = osp.join(args.input, 'train')\n        else:\n            in_folder = osp.join(args.input, subset)\n        out_file = osp.join(args.output, f'{subset}_cocoformat.json')\n        outputs = defaultdict(list)\n        outputs['categories'] = CLASSES\n        if args.convert_det:\n            det_file = osp.join(args.output, f'{subset}_detections.pkl')\n            detections = dict(det_bboxes=dict())\n        video_names = os.listdir(in_folder)\n        for video_name in tqdm(video_names):\n            # basic params\n            parse_gt = 'test' not in subset\n            ins_maps = dict()\n            # load video infos\n            video_folder = osp.join(in_folder, video_name)\n            infos = mmengine.list_from_file(f'{video_folder}/seqinfo.ini')\n            # video-level infos\n            assert video_name == infos[1].strip().split('=')[1]\n            img_folder = infos[2].strip().split('=')[1]\n            img_names = os.listdir(f'{video_folder}/{img_folder}')\n            img_names = sorted(img_names)\n            fps = int(infos[3].strip().split('=')[1])\n            num_imgs = int(infos[4].strip().split('=')[1])\n            assert num_imgs == len(img_names)\n            width = int(infos[5].strip().split('=')[1])\n            height = int(infos[6].strip().split('=')[1])\n            video = dict(\n                id=vid_id,\n                name=video_name,\n                fps=fps,\n                width=width,\n                height=height)\n            # parse annotations\n            if parse_gt:\n                gts = mmengine.list_from_file(f'{video_folder}/gt/gt.txt')\n                if 'MOT15' in video_folder:\n                    img2gts = parse_gts(gts, True)\n                else:\n                    img2gts = parse_gts(gts, False)\n            if args.convert_det:\n                dets = mmengine.list_from_file(f'{video_folder}/det/det.txt')\n                img2dets = parse_dets(dets)\n            # make half sets\n            if 'half' in subset:\n                split_frame = num_imgs // 2 + 1\n                if 'train' in subset:\n                    img_names = img_names[:split_frame]\n                elif 'val' in subset:\n                    img_names = img_names[split_frame:]\n                else:\n                    raise ValueError(\n                        'subset must be named with `train` or `val`')\n                mot_frame_ids = [str(int(_.split('.')[0])) for _ in img_names]\n                with open(f'{video_folder}/gt/gt_{subset}.txt', 'wt') as f:\n                    for gt in gts:\n                        if gt.split(',')[0] in mot_frame_ids:\n                            f.writelines(f'{gt}\\n')\n            # image and box level infos\n            for frame_id, name in enumerate(img_names):\n                img_name = osp.join(video_name, img_folder, name)\n                mot_frame_id = int(name.split('.')[0])\n                image = dict(\n                    id=img_id,\n                    video_id=vid_id,\n                    file_name=img_name,\n                    height=height,\n                    width=width,\n                    frame_id=frame_id,\n                    mot_frame_id=mot_frame_id)\n                if parse_gt:\n                    gts = img2gts[mot_frame_id]\n                    for gt in gts:\n                        gt.update(id=ann_id, image_id=img_id)\n                        mot_ins_id = gt['mot_instance_id']\n                        if mot_ins_id in ins_maps:\n                            gt['instance_id'] = ins_maps[mot_ins_id]\n                        else:\n                            gt['instance_id'] = ins_id\n                            ins_maps[mot_ins_id] = ins_id\n                            ins_id += 1\n                        outputs['annotations'].append(gt)\n                        ann_id += 1\n                if args.convert_det:\n                    dets = np.array(img2dets[mot_frame_id])\n                    if dets.ndim == 1:\n                        assert len(dets) == 0\n                        dets = np.zeros((0, 5))\n                    detections['det_bboxes'][img_name] = [dets]\n                outputs['images'].append(image)\n                img_id += 1\n            outputs['videos'].append(video)\n            vid_id += 1\n            outputs['num_instances'] = ins_id\n        print(f'{subset} has {ins_id} instances.')\n        mmengine.dump(outputs, out_file)\n        if args.convert_det:\n            mmengine.dump(detections, det_file)\n            print(f'Done! Saved as {out_file} and {det_file}')\n        else:\n            print(f'Done! Saved as {out_file}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/mot2reid.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# This script converts MOT dataset into ReID dataset.\n# Official website of the MOT dataset: https://motchallenge.net/\n#\n# Label format of MOT dataset:\n#   GTs:\n#       <frame_id> # starts from 1,\n#       <instance_id>, <x1>, <y1>, <w>, <h>,\n#       <conf> # conf is annotated as 0 if the object is ignored,\n#       <class_id>, <visibility>\n#\n#   DETs and Results:\n#       <frame_id>, <instance_id>, <x1>, <y1>, <w>, <h>, <conf>,\n#       <x>, <y>, <z> # for 3D objects\n#\n# Classes in MOT:\n#   1: 'pedestrian'\n#   2: 'person on vehicle'\n#   3: 'car'\n#   4: 'bicycle'\n#   5: 'motorbike'\n#   6: 'non motorized vehicle'\n#   7: 'static person'\n#   8: 'distractor'\n#   9: 'occluder'\n#   10: 'occluder on the ground',\n#   11: 'occluder full'\n#   12: 'reflection'\n#\n#   USELESS classes and IGNORES classes will not be selected\n#   into the dataset for reid model training.\nimport argparse\nimport os\nimport os.path as osp\nimport random\n\nimport mmcv\nimport numpy as np\nfrom mmengine.fileio import list_from_file\nfrom tqdm import tqdm\n\nUSELESS = [3, 4, 5, 6, 9, 10, 11]\nIGNORES = [2, 7, 8, 12, 13]\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert MOT dataset into ReID dataset.')\n    parser.add_argument('-i', '--input', help='path of MOT data')\n    parser.add_argument('-o', '--output', help='path to save ReID dataset')\n    parser.add_argument(\n        '--val-split',\n        type=float,\n        default=0.2,\n        help='proportion of the validation dataset to the whole ReID dataset')\n    parser.add_argument(\n        '--vis-threshold',\n        type=float,\n        default=0.3,\n        help='threshold of visibility for each person')\n    parser.add_argument(\n        '--min-per-person',\n        type=int,\n        default=8,\n        help='minimum number of images for each person')\n    parser.add_argument(\n        '--max-per-person',\n        type=int,\n        default=1000,\n        help='maxmum number of images for each person')\n    return parser.parse_args()\n\n\ndef main():\n    args = parse_args()\n    if not osp.isdir(args.output):\n        os.makedirs(args.output, exist_ok=True)\n\n    in_folder = osp.join(args.input, 'train')\n    video_names = os.listdir(in_folder)\n    if 'MOT17' in in_folder:\n        video_names = [\n            video_name for video_name in video_names if 'FRCNN' in video_name\n        ]\n    is_mot15 = True if 'MOT15' in in_folder else False\n    for video_name in tqdm(video_names):\n        # load video infos\n        video_folder = osp.join(in_folder, video_name)\n        infos = list_from_file(f'{video_folder}/seqinfo.ini')\n        # video-level infos\n        assert video_name == infos[1].strip().split('=')[1]\n        raw_img_folder = infos[2].strip().split('=')[1]\n        raw_img_names = os.listdir(f'{video_folder}/{raw_img_folder}')\n        raw_img_names = sorted(raw_img_names)\n        num_raw_imgs = int(infos[4].strip().split('=')[1])\n        assert num_raw_imgs == len(raw_img_names)\n\n        reid_train_folder = osp.join(args.output, 'imgs')\n        if not osp.exists(reid_train_folder):\n            os.makedirs(reid_train_folder)\n        gts = list_from_file(f'{video_folder}/gt/gt.txt')\n        last_frame_id = -1\n        for gt in gts:\n            gt = gt.strip().split(',')\n            frame_id, ins_id = map(int, gt[:2])\n            ltwh = list(map(float, gt[2:6]))\n            if is_mot15:\n                class_id = 1\n                visibility = 1.\n            else:\n                class_id = int(gt[7])\n                visibility = float(gt[8])\n            if class_id in USELESS:\n                continue\n            elif class_id in IGNORES:\n                continue\n            elif visibility < args.vis_threshold:\n                continue\n            reid_img_folder = osp.join(reid_train_folder,\n                                       f'{video_name}_{ins_id:06d}')\n            if not osp.exists(reid_img_folder):\n                os.makedirs(reid_img_folder)\n            idx = len(os.listdir(reid_img_folder))\n            reid_img_name = f'{idx:06d}.jpg'\n            if frame_id != last_frame_id:\n                raw_img_name = raw_img_names[frame_id - 1]\n                raw_img = mmcv.imread(\n                    f'{video_folder}/{raw_img_folder}/{raw_img_name}')\n                last_frame_id = frame_id\n            xyxy = np.asarray(\n                [ltwh[0], ltwh[1], ltwh[0] + ltwh[2], ltwh[1] + ltwh[3]])\n            reid_img = mmcv.imcrop(raw_img, xyxy)\n            mmcv.imwrite(reid_img, f'{reid_img_folder}/{reid_img_name}')\n\n    reid_meta_folder = osp.join(args.output, 'meta')\n    if not osp.exists(reid_meta_folder):\n        os.makedirs(reid_meta_folder)\n    reid_train_list = []\n    reid_val_list = []\n    reid_img_folder_names = sorted(os.listdir(reid_train_folder))\n    num_ids = len(reid_img_folder_names)\n    num_train_ids = int(num_ids * (1 - args.val_split))\n    train_label, val_label = 0, 0\n    random.seed(0)\n    for reid_img_folder_name in reid_img_folder_names[:num_train_ids]:\n        reid_img_names = os.listdir(\n            f'{reid_train_folder}/{reid_img_folder_name}')\n        # ignore ids whose number of image is less than min_per_person\n        if (len(reid_img_names) < args.min_per_person):\n            continue\n        # downsampling when there are too many images owned by one id\n        if (len(reid_img_names) > args.max_per_person):\n            reid_img_names = random.sample(reid_img_names, args.max_per_person)\n        # training set\n        for reid_img_name in reid_img_names:\n            reid_train_list.append(\n                f'{reid_img_folder_name}/{reid_img_name} {train_label}\\n')\n        train_label += 1\n    reid_entire_dataset_list = reid_train_list.copy()\n    for reid_img_folder_name in reid_img_folder_names[num_train_ids:]:\n        reid_img_names = os.listdir(\n            f'{reid_train_folder}/{reid_img_folder_name}')\n        # ignore ids whose number of image is less than min_per_person\n        if (len(reid_img_names) < args.min_per_person):\n            continue\n        # downsampling when there are too many images owned by one id\n        if (len(reid_img_names) > args.max_per_person):\n            reid_img_names = random.sample(reid_img_names, args.max_per_person)\n        for reid_img_name in reid_img_names:\n            # validation set\n            reid_val_list.append(\n                f'{reid_img_folder_name}/{reid_img_name} {val_label}\\n')\n            reid_entire_dataset_list.append(\n                f'{reid_img_folder_name}/{reid_img_name} '\n                f'{train_label + val_label}\\n')\n        val_label += 1\n    with open(\n            osp.join(reid_meta_folder,\n                     f'train_{int(100 * (1 - args.val_split))}.txt'),\n            'w') as f:\n        f.writelines(reid_train_list)\n    with open(\n            osp.join(reid_meta_folder, f'val_{int(100 * args.val_split)}.txt'),\n            'w') as f:\n        f.writelines(reid_val_list)\n    with open(osp.join(reid_meta_folder, 'train.txt'), 'w') as f:\n        f.writelines(reid_entire_dataset_list)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/openimages2odvg.py",
    "content": "import argparse\nimport copy\nimport csv\nimport json\nimport os.path as osp\n\nimport jsonlines\nfrom mmcv.image import imfrombytes\nfrom mmengine.fileio import get\n\n\ndef _parse_label_file(label_file):\n    index_list = []\n    classes_names = []\n    with open(label_file, 'r') as f:\n        reader = csv.reader(f)\n        for line in reader:\n            classes_names.append(line[1])\n            index_list.append(line[0])\n    index_mapping = {index: i for i, index in enumerate(index_list)}\n    return classes_names, index_mapping\n\n\n# backend_args = dict(\n#     backend='petrel',\n#     path_mapping=dict({\n#         './data/': 's3://openmmlab/datasets/detection/',\n#         'data/': 's3://openmmlab/datasets/detection/'\n#     }))\nbackend_args = None\n\n\ndef oi2odvg(args):\n    ann_file = osp.join(args.input_dir, 'oidv6-train-annotations-bbox.csv')\n    label_file = osp.join(args.input_dir, 'class-descriptions-boxable.csv')\n\n    classes_names, index_mapping = _parse_label_file(label_file)\n\n    label_map = {}\n    for class_name, idx in index_mapping.items():\n        class_name = classes_names[idx]\n        label_map[str(idx)] = class_name\n\n    if args.out_ann is None:\n        output = osp.join(args.input_dir, 'openimages_label_map.json')\n    else:\n        output = osp.join(\n            osp.dirname(args.out_ann), 'openimages_label_map.json')\n    with open(output, 'w') as f:\n        json.dump(label_map, f)\n\n    metas = []\n    skip_count = 0\n    with open(ann_file, 'r') as f:\n        reader = csv.reader(f)\n        last_img_id = None\n        _filename_shape = [0, 0]\n        instances = []\n        for i, line in enumerate(reader):\n            if i == 0:\n                continue\n            img_id = line[0]\n            if last_img_id is None:\n                last_img_id = img_id\n            label_id = line[2]\n\n            filename = f'{img_id}.jpg'\n            label = index_mapping[label_id]\n            category = label_map[str(label)]\n            bbox = [\n                float(line[4]),  # xmin\n                float(line[6]),  # ymin\n                float(line[5]),  # xmax\n                float(line[7])  # ymax\n            ]\n\n            # is_occluded = True if int(line[8]) == 1 else False\n            # is_truncated = True if int(line[9]) == 1 else False\n            is_group_of = True if int(line[10]) == 1 else False\n            # is_depiction = True if int(line[11]) == 1 else False\n            # is_inside = True if int(line[12]) == 1 else False\n\n            # if any([is_occluded, is_truncated, is_group_of,\n            # is_depiction, is_inside]):\n            if is_group_of:\n                print(f'skip {filename} of one instance')\n                skip_count += 1\n                continue\n\n            # denormalize\n            if filename != _filename_shape[0]:\n                if args.img_prefix is not None:\n                    _filename = osp.join(\n                        osp.dirname(args.input_dir), args.img_prefix, filename)\n                else:\n                    _filename = osp.join(osp.dirname(args.input_dir), filename)\n                img_bytes = get(_filename, backend_args)\n                img = imfrombytes(img_bytes, flag='color')\n                shape = img.shape\n                _filename_shape = [filename, shape]\n            else:\n                shape = _filename_shape[1]\n\n            h, w = shape[:2]\n            bbox = [\n                max(bbox[0] * w, 0),\n                max(bbox[1] * h, 0),\n                min(bbox[2] * w, w),\n                min(bbox[3] * h, h)\n            ]\n\n            x1, y1, x2, y2 = bbox\n            inter_w = max(0, min(x2, w) - max(x1, 0))\n            inter_h = max(0, min(y2, h) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if w < 1 or h < 1:\n                continue\n\n            instance = {\n                'filename': filename,\n                'height': h,\n                'width': w,\n                'bbox': bbox,\n                'label': label,\n                'category': category\n            }\n\n            if img_id != last_img_id:\n                copy_instances = copy.deepcopy(instances)\n                for copy_instance in copy_instances:\n                    _filename = copy_instance.pop('filename')\n                    _h = copy_instance.pop('height')\n                    _w = copy_instance.pop('width')\n\n                meta_ifo = {\n                    'filename': _filename,\n                    'height': _h,\n                    'width': _w,\n                    'detection': {\n                        'instances': copy_instances\n                    }\n                }\n                metas.append(meta_ifo)\n                instances = []\n            instances.append(instance)\n            last_img_id = img_id\n\n        for instance in instances:\n            _filename = instance.pop('filename')\n            _h = instance.pop('height')\n            _w = instance.pop('width')\n        meta_ifo = {\n            'filename': _filename,\n            'height': _h,\n            'width': _w,\n            'detection': {\n                'instances': instances\n            }\n        }\n        metas.append(meta_ifo)\n\n    if args.out_ann is None:\n        out_path = osp.join(args.input_dir, 'oidv6-train-annotations_od.json')\n    else:\n        out_path = args.out_ann\n\n    with jsonlines.open(out_path, mode='w') as writer:\n        writer.write_all(metas)\n\n    print('skip {} instances'.format(skip_count))\n    print('save to {}'.format(out_path))\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(\n        'openimages to odvg format.', add_help=True)\n    parser.add_argument(\n        '--input-dir',\n        default='data/OpenImages/annotations',\n        type=str,\n        help='input list name')\n    parser.add_argument('--img-prefix', default='OpenImages/train/')\n    parser.add_argument('--out-ann', '-o', type=str)\n    args = parser.parse_args()\n\n    oi2odvg(args)\n"
  },
  {
    "path": "tools/dataset_converters/pascal_voc.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os.path as osp\nimport xml.etree.ElementTree as ET\n\nimport numpy as np\nfrom mmengine.fileio import dump, list_from_file\nfrom mmengine.utils import mkdir_or_exist, track_progress\n\nfrom mmdet.evaluation import voc_classes\n\nlabel_ids = {name: i for i, name in enumerate(voc_classes())}\n\n\ndef parse_xml(args):\n    xml_path, img_path = args\n    tree = ET.parse(xml_path)\n    root = tree.getroot()\n    size = root.find('size')\n    w = int(size.find('width').text)\n    h = int(size.find('height').text)\n    bboxes = []\n    labels = []\n    bboxes_ignore = []\n    labels_ignore = []\n    for obj in root.findall('object'):\n        name = obj.find('name').text\n        label = label_ids[name]\n        difficult = int(obj.find('difficult').text)\n        bnd_box = obj.find('bndbox')\n        bbox = [\n            int(bnd_box.find('xmin').text),\n            int(bnd_box.find('ymin').text),\n            int(bnd_box.find('xmax').text),\n            int(bnd_box.find('ymax').text)\n        ]\n        if difficult:\n            bboxes_ignore.append(bbox)\n            labels_ignore.append(label)\n        else:\n            bboxes.append(bbox)\n            labels.append(label)\n    if not bboxes:\n        bboxes = np.zeros((0, 4))\n        labels = np.zeros((0, ))\n    else:\n        bboxes = np.array(bboxes, ndmin=2) - 1\n        labels = np.array(labels)\n    if not bboxes_ignore:\n        bboxes_ignore = np.zeros((0, 4))\n        labels_ignore = np.zeros((0, ))\n    else:\n        bboxes_ignore = np.array(bboxes_ignore, ndmin=2) - 1\n        labels_ignore = np.array(labels_ignore)\n    annotation = {\n        'filename': img_path,\n        'width': w,\n        'height': h,\n        'ann': {\n            'bboxes': bboxes.astype(np.float32),\n            'labels': labels.astype(np.int64),\n            'bboxes_ignore': bboxes_ignore.astype(np.float32),\n            'labels_ignore': labels_ignore.astype(np.int64)\n        }\n    }\n    return annotation\n\n\ndef cvt_annotations(devkit_path, years, split, out_file):\n    if not isinstance(years, list):\n        years = [years]\n    annotations = []\n    for year in years:\n        filelist = osp.join(devkit_path,\n                            f'VOC{year}/ImageSets/Main/{split}.txt')\n        if not osp.isfile(filelist):\n            print(f'filelist does not exist: {filelist}, '\n                  f'skip voc{year} {split}')\n            return\n        img_names = list_from_file(filelist)\n        xml_paths = [\n            osp.join(devkit_path, f'VOC{year}/Annotations/{img_name}.xml')\n            for img_name in img_names\n        ]\n        img_paths = [\n            f'VOC{year}/JPEGImages/{img_name}.jpg' for img_name in img_names\n        ]\n        part_annotations = track_progress(parse_xml,\n                                          list(zip(xml_paths, img_paths)))\n        annotations.extend(part_annotations)\n    if out_file.endswith('json'):\n        annotations = cvt_to_coco_json(annotations)\n    dump(annotations, out_file)\n    return annotations\n\n\ndef cvt_to_coco_json(annotations):\n    image_id = 0\n    annotation_id = 0\n    coco = dict()\n    coco['images'] = []\n    coco['type'] = 'instance'\n    coco['categories'] = []\n    coco['annotations'] = []\n    image_set = set()\n\n    def addAnnItem(annotation_id, image_id, category_id, bbox, difficult_flag):\n        annotation_item = dict()\n        annotation_item['segmentation'] = []\n\n        seg = []\n        # bbox[] is x1,y1,x2,y2\n        # left_top\n        seg.append(int(bbox[0]))\n        seg.append(int(bbox[1]))\n        # left_bottom\n        seg.append(int(bbox[0]))\n        seg.append(int(bbox[3]))\n        # right_bottom\n        seg.append(int(bbox[2]))\n        seg.append(int(bbox[3]))\n        # right_top\n        seg.append(int(bbox[2]))\n        seg.append(int(bbox[1]))\n\n        annotation_item['segmentation'].append(seg)\n\n        xywh = np.array(\n            [bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]])\n        annotation_item['area'] = int(xywh[2] * xywh[3])\n        if difficult_flag == 1:\n            annotation_item['ignore'] = 0\n            annotation_item['iscrowd'] = 1\n        else:\n            annotation_item['ignore'] = 0\n            annotation_item['iscrowd'] = 0\n        annotation_item['image_id'] = int(image_id)\n        annotation_item['bbox'] = xywh.astype(int).tolist()\n        annotation_item['category_id'] = int(category_id)\n        annotation_item['id'] = int(annotation_id)\n        coco['annotations'].append(annotation_item)\n        return annotation_id + 1\n\n    for category_id, name in enumerate(voc_classes()):\n        category_item = dict()\n        category_item['supercategory'] = str('none')\n        category_item['id'] = int(category_id)\n        category_item['name'] = str(name)\n        coco['categories'].append(category_item)\n\n    for ann_dict in annotations:\n        file_name = ann_dict['filename']\n        ann = ann_dict['ann']\n        assert file_name not in image_set\n        image_item = dict()\n        image_item['id'] = int(image_id)\n        image_item['file_name'] = str(file_name)\n        image_item['height'] = int(ann_dict['height'])\n        image_item['width'] = int(ann_dict['width'])\n        coco['images'].append(image_item)\n        image_set.add(file_name)\n\n        bboxes = ann['bboxes'][:, :4]\n        labels = ann['labels']\n        for bbox_id in range(len(bboxes)):\n            bbox = bboxes[bbox_id]\n            label = labels[bbox_id]\n            annotation_id = addAnnItem(\n                annotation_id, image_id, label, bbox, difficult_flag=0)\n\n        bboxes_ignore = ann['bboxes_ignore'][:, :4]\n        labels_ignore = ann['labels_ignore']\n        for bbox_id in range(len(bboxes_ignore)):\n            bbox = bboxes_ignore[bbox_id]\n            label = labels_ignore[bbox_id]\n            annotation_id = addAnnItem(\n                annotation_id, image_id, label, bbox, difficult_flag=1)\n\n        image_id += 1\n\n    return coco\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Convert PASCAL VOC annotations to mmdetection format')\n    parser.add_argument('devkit_path', help='pascal voc devkit path')\n    parser.add_argument('-o', '--out-dir', help='output path')\n    parser.add_argument(\n        '--out-format',\n        default='pkl',\n        choices=('pkl', 'coco'),\n        help='output format, \"coco\" indicates coco annotation format')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    devkit_path = args.devkit_path\n    out_dir = args.out_dir if args.out_dir else devkit_path\n    mkdir_or_exist(out_dir)\n\n    years = []\n    if osp.isdir(osp.join(devkit_path, 'VOC2007')):\n        years.append('2007')\n    if osp.isdir(osp.join(devkit_path, 'VOC2012')):\n        years.append('2012')\n    if '2007' in years and '2012' in years:\n        years.append(['2007', '2012'])\n    if not years:\n        raise IOError(f'The devkit path {devkit_path} contains neither '\n                      '\"VOC2007\" nor \"VOC2012\" subfolder')\n    out_fmt = f'.{args.out_format}'\n    if args.out_format == 'coco':\n        out_fmt = '.json'\n    for year in years:\n        if year == '2007':\n            prefix = 'voc07'\n        elif year == '2012':\n            prefix = 'voc12'\n        elif year == ['2007', '2012']:\n            prefix = 'voc0712'\n        for split in ['train', 'val', 'trainval']:\n            dataset_name = prefix + '_' + split\n            print(f'processing {dataset_name} ...')\n            cvt_annotations(devkit_path, year, split,\n                            osp.join(out_dir, dataset_name + out_fmt))\n        if not isinstance(year, list):\n            dataset_name = prefix + '_test'\n            print(f'processing {dataset_name} ...')\n            cvt_annotations(devkit_path, year, 'test',\n                            osp.join(out_dir, dataset_name + out_fmt))\n    print('Done!')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/prepare_coco_semantic_annos_from_panoptic_annos.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n# Modified from https://github.com/facebookresearch/Mask2Former/blob/main/datasets/prepare_coco_semantic_annos_from_panoptic_annos.py # noqa\n\nimport argparse\nimport functools\nimport json\nimport multiprocessing as mp\nimport os\nimport time\n\nimport numpy as np\nfrom panopticapi.utils import rgb2id\nfrom PIL import Image\n\nCOCO_CATEGORIES = [\n    {\n        'color': [220, 20, 60],\n        'isthing': 1,\n        'id': 1,\n        'name': 'person'\n    },\n    {\n        'color': [119, 11, 32],\n        'isthing': 1,\n        'id': 2,\n        'name': 'bicycle'\n    },\n    {\n        'color': [0, 0, 142],\n        'isthing': 1,\n        'id': 3,\n        'name': 'car'\n    },\n    {\n        'color': [0, 0, 230],\n        'isthing': 1,\n        'id': 4,\n        'name': 'motorcycle'\n    },\n    {\n        'color': [106, 0, 228],\n        'isthing': 1,\n        'id': 5,\n        'name': 'airplane'\n    },\n    {\n        'color': [0, 60, 100],\n        'isthing': 1,\n        'id': 6,\n        'name': 'bus'\n    },\n    {\n        'color': [0, 80, 100],\n        'isthing': 1,\n        'id': 7,\n        'name': 'train'\n    },\n    {\n        'color': [0, 0, 70],\n        'isthing': 1,\n        'id': 8,\n        'name': 'truck'\n    },\n    {\n        'color': [0, 0, 192],\n        'isthing': 1,\n        'id': 9,\n        'name': 'boat'\n    },\n    {\n        'color': [250, 170, 30],\n        'isthing': 1,\n        'id': 10,\n        'name': 'traffic light'\n    },\n    {\n        'color': [100, 170, 30],\n        'isthing': 1,\n        'id': 11,\n        'name': 'fire hydrant'\n    },\n    {\n        'color': [220, 220, 0],\n        'isthing': 1,\n        'id': 13,\n        'name': 'stop sign'\n    },\n    {\n        'color': [175, 116, 175],\n        'isthing': 1,\n        'id': 14,\n        'name': 'parking meter'\n    },\n    {\n        'color': [250, 0, 30],\n        'isthing': 1,\n        'id': 15,\n        'name': 'bench'\n    },\n    {\n        'color': [165, 42, 42],\n        'isthing': 1,\n        'id': 16,\n        'name': 'bird'\n    },\n    {\n        'color': [255, 77, 255],\n        'isthing': 1,\n        'id': 17,\n        'name': 'cat'\n    },\n    {\n        'color': [0, 226, 252],\n        'isthing': 1,\n        'id': 18,\n        'name': 'dog'\n    },\n    {\n        'color': [182, 182, 255],\n        'isthing': 1,\n        'id': 19,\n        'name': 'horse'\n    },\n    {\n        'color': [0, 82, 0],\n        'isthing': 1,\n        'id': 20,\n        'name': 'sheep'\n    },\n    {\n        'color': [120, 166, 157],\n        'isthing': 1,\n        'id': 21,\n        'name': 'cow'\n    },\n    {\n        'color': [110, 76, 0],\n        'isthing': 1,\n        'id': 22,\n        'name': 'elephant'\n    },\n    {\n        'color': [174, 57, 255],\n        'isthing': 1,\n        'id': 23,\n        'name': 'bear'\n    },\n    {\n        'color': [199, 100, 0],\n        'isthing': 1,\n        'id': 24,\n        'name': 'zebra'\n    },\n    {\n        'color': [72, 0, 118],\n        'isthing': 1,\n        'id': 25,\n        'name': 'giraffe'\n    },\n    {\n        'color': [255, 179, 240],\n        'isthing': 1,\n        'id': 27,\n        'name': 'backpack'\n    },\n    {\n        'color': [0, 125, 92],\n        'isthing': 1,\n        'id': 28,\n        'name': 'umbrella'\n    },\n    {\n        'color': [209, 0, 151],\n        'isthing': 1,\n        'id': 31,\n        'name': 'handbag'\n    },\n    {\n        'color': [188, 208, 182],\n        'isthing': 1,\n        'id': 32,\n        'name': 'tie'\n    },\n    {\n        'color': [0, 220, 176],\n        'isthing': 1,\n        'id': 33,\n        'name': 'suitcase'\n    },\n    {\n        'color': [255, 99, 164],\n        'isthing': 1,\n        'id': 34,\n        'name': 'frisbee'\n    },\n    {\n        'color': [92, 0, 73],\n        'isthing': 1,\n        'id': 35,\n        'name': 'skis'\n    },\n    {\n        'color': [133, 129, 255],\n        'isthing': 1,\n        'id': 36,\n        'name': 'snowboard'\n    },\n    {\n        'color': [78, 180, 255],\n        'isthing': 1,\n        'id': 37,\n        'name': 'sports ball'\n    },\n    {\n        'color': [0, 228, 0],\n        'isthing': 1,\n        'id': 38,\n        'name': 'kite'\n    },\n    {\n        'color': [174, 255, 243],\n        'isthing': 1,\n        'id': 39,\n        'name': 'baseball bat'\n    },\n    {\n        'color': [45, 89, 255],\n        'isthing': 1,\n        'id': 40,\n        'name': 'baseball glove'\n    },\n    {\n        'color': [134, 134, 103],\n        'isthing': 1,\n        'id': 41,\n        'name': 'skateboard'\n    },\n    {\n        'color': [145, 148, 174],\n        'isthing': 1,\n        'id': 42,\n        'name': 'surfboard'\n    },\n    {\n        'color': [255, 208, 186],\n        'isthing': 1,\n        'id': 43,\n        'name': 'tennis racket'\n    },\n    {\n        'color': [197, 226, 255],\n        'isthing': 1,\n        'id': 44,\n        'name': 'bottle'\n    },\n    {\n        'color': [171, 134, 1],\n        'isthing': 1,\n        'id': 46,\n        'name': 'wine glass'\n    },\n    {\n        'color': [109, 63, 54],\n        'isthing': 1,\n        'id': 47,\n        'name': 'cup'\n    },\n    {\n        'color': [207, 138, 255],\n        'isthing': 1,\n        'id': 48,\n        'name': 'fork'\n    },\n    {\n        'color': [151, 0, 95],\n        'isthing': 1,\n        'id': 49,\n        'name': 'knife'\n    },\n    {\n        'color': [9, 80, 61],\n        'isthing': 1,\n        'id': 50,\n        'name': 'spoon'\n    },\n    {\n        'color': [84, 105, 51],\n        'isthing': 1,\n        'id': 51,\n        'name': 'bowl'\n    },\n    {\n        'color': [74, 65, 105],\n        'isthing': 1,\n        'id': 52,\n        'name': 'banana'\n    },\n    {\n        'color': [166, 196, 102],\n        'isthing': 1,\n        'id': 53,\n        'name': 'apple'\n    },\n    {\n        'color': [208, 195, 210],\n        'isthing': 1,\n        'id': 54,\n        'name': 'sandwich'\n    },\n    {\n        'color': [255, 109, 65],\n        'isthing': 1,\n        'id': 55,\n        'name': 'orange'\n    },\n    {\n        'color': [0, 143, 149],\n        'isthing': 1,\n        'id': 56,\n        'name': 'broccoli'\n    },\n    {\n        'color': [179, 0, 194],\n        'isthing': 1,\n        'id': 57,\n        'name': 'carrot'\n    },\n    {\n        'color': [209, 99, 106],\n        'isthing': 1,\n        'id': 58,\n        'name': 'hot dog'\n    },\n    {\n        'color': [5, 121, 0],\n        'isthing': 1,\n        'id': 59,\n        'name': 'pizza'\n    },\n    {\n        'color': [227, 255, 205],\n        'isthing': 1,\n        'id': 60,\n        'name': 'donut'\n    },\n    {\n        'color': [147, 186, 208],\n        'isthing': 1,\n        'id': 61,\n        'name': 'cake'\n    },\n    {\n        'color': [153, 69, 1],\n        'isthing': 1,\n        'id': 62,\n        'name': 'chair'\n    },\n    {\n        'color': [3, 95, 161],\n        'isthing': 1,\n        'id': 63,\n        'name': 'couch'\n    },\n    {\n        'color': [163, 255, 0],\n        'isthing': 1,\n        'id': 64,\n        'name': 'potted plant'\n    },\n    {\n        'color': [119, 0, 170],\n        'isthing': 1,\n        'id': 65,\n        'name': 'bed'\n    },\n    {\n        'color': [0, 182, 199],\n        'isthing': 1,\n        'id': 67,\n        'name': 'dining table'\n    },\n    {\n        'color': [0, 165, 120],\n        'isthing': 1,\n        'id': 70,\n        'name': 'toilet'\n    },\n    {\n        'color': [183, 130, 88],\n        'isthing': 1,\n        'id': 72,\n        'name': 'tv'\n    },\n    {\n        'color': [95, 32, 0],\n        'isthing': 1,\n        'id': 73,\n        'name': 'laptop'\n    },\n    {\n        'color': [130, 114, 135],\n        'isthing': 1,\n        'id': 74,\n        'name': 'mouse'\n    },\n    {\n        'color': [110, 129, 133],\n        'isthing': 1,\n        'id': 75,\n        'name': 'remote'\n    },\n    {\n        'color': [166, 74, 118],\n        'isthing': 1,\n        'id': 76,\n        'name': 'keyboard'\n    },\n    {\n        'color': [219, 142, 185],\n        'isthing': 1,\n        'id': 77,\n        'name': 'cell phone'\n    },\n    {\n        'color': [79, 210, 114],\n        'isthing': 1,\n        'id': 78,\n        'name': 'microwave'\n    },\n    {\n        'color': [178, 90, 62],\n        'isthing': 1,\n        'id': 79,\n        'name': 'oven'\n    },\n    {\n        'color': [65, 70, 15],\n        'isthing': 1,\n        'id': 80,\n        'name': 'toaster'\n    },\n    {\n        'color': [127, 167, 115],\n        'isthing': 1,\n        'id': 81,\n        'name': 'sink'\n    },\n    {\n        'color': [59, 105, 106],\n        'isthing': 1,\n        'id': 82,\n        'name': 'refrigerator'\n    },\n    {\n        'color': [142, 108, 45],\n        'isthing': 1,\n        'id': 84,\n        'name': 'book'\n    },\n    {\n        'color': [196, 172, 0],\n        'isthing': 1,\n        'id': 85,\n        'name': 'clock'\n    },\n    {\n        'color': [95, 54, 80],\n        'isthing': 1,\n        'id': 86,\n        'name': 'vase'\n    },\n    {\n        'color': [128, 76, 255],\n        'isthing': 1,\n        'id': 87,\n        'name': 'scissors'\n    },\n    {\n        'color': [201, 57, 1],\n        'isthing': 1,\n        'id': 88,\n        'name': 'teddy bear'\n    },\n    {\n        'color': [246, 0, 122],\n        'isthing': 1,\n        'id': 89,\n        'name': 'hair drier'\n    },\n    {\n        'color': [191, 162, 208],\n        'isthing': 1,\n        'id': 90,\n        'name': 'toothbrush'\n    },\n    {\n        'color': [255, 255, 128],\n        'isthing': 0,\n        'id': 92,\n        'name': 'banner'\n    },\n    {\n        'color': [147, 211, 203],\n        'isthing': 0,\n        'id': 93,\n        'name': 'blanket'\n    },\n    {\n        'color': [150, 100, 100],\n        'isthing': 0,\n        'id': 95,\n        'name': 'bridge'\n    },\n    {\n        'color': [168, 171, 172],\n        'isthing': 0,\n        'id': 100,\n        'name': 'cardboard'\n    },\n    {\n        'color': [146, 112, 198],\n        'isthing': 0,\n        'id': 107,\n        'name': 'counter'\n    },\n    {\n        'color': [210, 170, 100],\n        'isthing': 0,\n        'id': 109,\n        'name': 'curtain'\n    },\n    {\n        'color': [92, 136, 89],\n        'isthing': 0,\n        'id': 112,\n        'name': 'door-stuff'\n    },\n    {\n        'color': [218, 88, 184],\n        'isthing': 0,\n        'id': 118,\n        'name': 'floor-wood'\n    },\n    {\n        'color': [241, 129, 0],\n        'isthing': 0,\n        'id': 119,\n        'name': 'flower'\n    },\n    {\n        'color': [217, 17, 255],\n        'isthing': 0,\n        'id': 122,\n        'name': 'fruit'\n    },\n    {\n        'color': [124, 74, 181],\n        'isthing': 0,\n        'id': 125,\n        'name': 'gravel'\n    },\n    {\n        'color': [70, 70, 70],\n        'isthing': 0,\n        'id': 128,\n        'name': 'house'\n    },\n    {\n        'color': [255, 228, 255],\n        'isthing': 0,\n        'id': 130,\n        'name': 'light'\n    },\n    {\n        'color': [154, 208, 0],\n        'isthing': 0,\n        'id': 133,\n        'name': 'mirror-stuff'\n    },\n    {\n        'color': [193, 0, 92],\n        'isthing': 0,\n        'id': 138,\n        'name': 'net'\n    },\n    {\n        'color': [76, 91, 113],\n        'isthing': 0,\n        'id': 141,\n        'name': 'pillow'\n    },\n    {\n        'color': [255, 180, 195],\n        'isthing': 0,\n        'id': 144,\n        'name': 'platform'\n    },\n    {\n        'color': [106, 154, 176],\n        'isthing': 0,\n        'id': 145,\n        'name': 'playingfield'\n    },\n    {\n        'color': [230, 150, 140],\n        'isthing': 0,\n        'id': 147,\n        'name': 'railroad'\n    },\n    {\n        'color': [60, 143, 255],\n        'isthing': 0,\n        'id': 148,\n        'name': 'river'\n    },\n    {\n        'color': [128, 64, 128],\n        'isthing': 0,\n        'id': 149,\n        'name': 'road'\n    },\n    {\n        'color': [92, 82, 55],\n        'isthing': 0,\n        'id': 151,\n        'name': 'roof'\n    },\n    {\n        'color': [254, 212, 124],\n        'isthing': 0,\n        'id': 154,\n        'name': 'sand'\n    },\n    {\n        'color': [73, 77, 174],\n        'isthing': 0,\n        'id': 155,\n        'name': 'sea'\n    },\n    {\n        'color': [255, 160, 98],\n        'isthing': 0,\n        'id': 156,\n        'name': 'shelf'\n    },\n    {\n        'color': [255, 255, 255],\n        'isthing': 0,\n        'id': 159,\n        'name': 'snow'\n    },\n    {\n        'color': [104, 84, 109],\n        'isthing': 0,\n        'id': 161,\n        'name': 'stairs'\n    },\n    {\n        'color': [169, 164, 131],\n        'isthing': 0,\n        'id': 166,\n        'name': 'tent'\n    },\n    {\n        'color': [225, 199, 255],\n        'isthing': 0,\n        'id': 168,\n        'name': 'towel'\n    },\n    {\n        'color': [137, 54, 74],\n        'isthing': 0,\n        'id': 171,\n        'name': 'wall-brick'\n    },\n    {\n        'color': [135, 158, 223],\n        'isthing': 0,\n        'id': 175,\n        'name': 'wall-stone'\n    },\n    {\n        'color': [7, 246, 231],\n        'isthing': 0,\n        'id': 176,\n        'name': 'wall-tile'\n    },\n    {\n        'color': [107, 255, 200],\n        'isthing': 0,\n        'id': 177,\n        'name': 'wall-wood'\n    },\n    {\n        'color': [58, 41, 149],\n        'isthing': 0,\n        'id': 178,\n        'name': 'water-other'\n    },\n    {\n        'color': [183, 121, 142],\n        'isthing': 0,\n        'id': 180,\n        'name': 'window-blind'\n    },\n    {\n        'color': [255, 73, 97],\n        'isthing': 0,\n        'id': 181,\n        'name': 'window-other'\n    },\n    {\n        'color': [107, 142, 35],\n        'isthing': 0,\n        'id': 184,\n        'name': 'tree-merged'\n    },\n    {\n        'color': [190, 153, 153],\n        'isthing': 0,\n        'id': 185,\n        'name': 'fence-merged'\n    },\n    {\n        'color': [146, 139, 141],\n        'isthing': 0,\n        'id': 186,\n        'name': 'ceiling-merged'\n    },\n    {\n        'color': [70, 130, 180],\n        'isthing': 0,\n        'id': 187,\n        'name': 'sky-other-merged'\n    },\n    {\n        'color': [134, 199, 156],\n        'isthing': 0,\n        'id': 188,\n        'name': 'cabinet-merged'\n    },\n    {\n        'color': [209, 226, 140],\n        'isthing': 0,\n        'id': 189,\n        'name': 'table-merged'\n    },\n    {\n        'color': [96, 36, 108],\n        'isthing': 0,\n        'id': 190,\n        'name': 'floor-other-merged'\n    },\n    {\n        'color': [96, 96, 96],\n        'isthing': 0,\n        'id': 191,\n        'name': 'pavement-merged'\n    },\n    {\n        'color': [64, 170, 64],\n        'isthing': 0,\n        'id': 192,\n        'name': 'mountain-merged'\n    },\n    {\n        'color': [152, 251, 152],\n        'isthing': 0,\n        'id': 193,\n        'name': 'grass-merged'\n    },\n    {\n        'color': [208, 229, 228],\n        'isthing': 0,\n        'id': 194,\n        'name': 'dirt-merged'\n    },\n    {\n        'color': [206, 186, 171],\n        'isthing': 0,\n        'id': 195,\n        'name': 'paper-merged'\n    },\n    {\n        'color': [152, 161, 64],\n        'isthing': 0,\n        'id': 196,\n        'name': 'food-other-merged'\n    },\n    {\n        'color': [116, 112, 0],\n        'isthing': 0,\n        'id': 197,\n        'name': 'building-other-merged'\n    },\n    {\n        'color': [0, 114, 143],\n        'isthing': 0,\n        'id': 198,\n        'name': 'rock-merged'\n    },\n    {\n        'color': [102, 102, 156],\n        'isthing': 0,\n        'id': 199,\n        'name': 'wall-other-merged'\n    },\n    {\n        'color': [250, 141, 255],\n        'isthing': 0,\n        'id': 200,\n        'name': 'rug-merged'\n    },\n]\n\n\ndef _process_panoptic_to_semantic(input_panoptic, output_semantic, segments,\n                                  id_map):\n    panoptic = np.asarray(Image.open(input_panoptic), dtype=np.uint32)\n    panoptic = rgb2id(panoptic)\n    output = np.zeros_like(panoptic, dtype=np.uint8) + 255\n    for seg in segments:\n        cat_id = seg['category_id']\n        new_cat_id = id_map[cat_id]\n        output[panoptic == seg['id']] = new_cat_id\n    Image.fromarray(output).save(output_semantic)\n\n\ndef separate_coco_semantic_from_panoptic(panoptic_json, panoptic_root,\n                                         sem_seg_root, categories):\n    \"\"\"Create semantic segmentation annotations from panoptic segmentation\n    annotations, to be used by PanopticFPN.\n\n    It maps all thing categories to class 0, and maps all\n    unlabeled pixels to class 255.\n    It maps all stuff categories to contiguous ids starting from 1.\n    Args:\n        panoptic_json (str): path to the panoptic json file, in COCO's format.\n        panoptic_root (str): a directory with panoptic annotation files, in\n            COCO's format.\n        sem_seg_root (str): a directory to output semantic annotation files\n        categories (list[dict]): category metadata. Each dict needs to have:\n            \"id\": corresponds to the \"category_id\" in the json annotations\n            \"isthing\": 0 or 1\n    \"\"\"\n    os.makedirs(sem_seg_root, exist_ok=True)\n\n    id_map = {}  # map from category id to id in the output semantic annotation\n    assert len(categories) <= 254\n    for i, k in enumerate(categories):\n        id_map[k['id']] = i\n    # what is id = 0?\n    # id_map[0] = 255\n    print(id_map)\n\n    with open(panoptic_json) as f:\n        obj = json.load(f)\n\n    pool = mp.Pool(processes=max(mp.cpu_count() // 2, 4))\n\n    def iter_annotations():\n        for anno in obj['annotations']:\n            file_name = anno['file_name']\n            segments = anno['segments_info']\n            input = os.path.join(panoptic_root, file_name)\n            output = os.path.join(sem_seg_root, file_name)\n            yield input, output, segments\n\n    print('Start writing to {} ...'.format(sem_seg_root))\n    start = time.time()\n    pool.starmap(\n        functools.partial(_process_panoptic_to_semantic, id_map=id_map),\n        iter_annotations(),\n        chunksize=100,\n    )\n    print('Finished. time: {:.2f}s'.format(time.time() - start))\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description=\\\n        'Convert COCO Stuff 164k annotations to mmdet format')  # noqa\n    parser.add_argument('coco_path', help='coco stuff path')\n    args = parser.parse_args()\n    return args\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    dataset_dir = args.coco_path\n    for s in ['val2017', 'train2017']:\n        separate_coco_semantic_from_panoptic(\n            os.path.join(dataset_dir,\n                         'annotations/panoptic_{}.json'.format(s)),\n            os.path.join(dataset_dir, 'annotations/panoptic_{}'.format(s)),\n            os.path.join(dataset_dir,\n                         'annotations/panoptic_semseg_{}'.format(s)),\n            COCO_CATEGORIES,\n        )\n"
  },
  {
    "path": "tools/dataset_converters/refcoco2odvg.py",
    "content": "import argparse\nimport os.path as osp\n\nimport jsonlines\nfrom pycocotools.coco import COCO\nfrom tqdm import tqdm\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='refcoco to odvg')\n    parser.add_argument('mdetr_anno_dir', type=str)\n    parser.add_argument('--out-dir', '-o', type=str)\n    args = parser.parse_args()\n    return args\n\n\ndef _has_only_empty_bbox(anno):\n    return all(any(o <= 1 for o in obj['bbox'][2:]) for obj in anno)\n\n\ndef has_valid_annotation(anno):\n    # if it's empty, there is no annotation\n    if len(anno) == 0:\n        return False\n    # if all boxes have close to zero area, there is no annotation\n    if _has_only_empty_bbox(anno):\n        return False\n    return True\n\n\ndef process_item(args, filename):\n    path = osp.join(args.mdetr_anno_dir, filename)\n    coco = COCO(path)\n\n    ids = list(sorted(coco.imgs.keys()))\n\n    out_results = []\n    for img_id in tqdm(ids):\n        if isinstance(img_id, str):\n            ann_ids = coco.getAnnIds(imgIds=[img_id], iscrowd=0)\n        else:\n            ann_ids = coco.getAnnIds(imgIds=img_id, iscrowd=0)\n        annos = coco.loadAnns(ann_ids)\n        if not has_valid_annotation(annos):\n            continue\n\n        img_info = coco.loadImgs(img_id)[0]\n        file_name = img_info['file_name']\n        caption = img_info['caption']\n\n        regions = {}\n\n        for anno in annos:\n            box = anno['bbox']\n            tokens_positive = anno['tokens_positive']\n            x1, y1, w, h = box\n            inter_w = max(0, min(x1 + w, int(img_info['width'])) - max(x1, 0))\n            inter_h = max(0, min(y1 + h, int(img_info['height'])) - max(y1, 0))\n            if inter_w * inter_h == 0:\n                continue\n            if anno['area'] <= 0 or w < 1 or h < 1:\n                continue\n\n            if anno.get('iscrowd', False):\n                continue\n            bbox_xyxy = [\n                x1, y1,\n                min(x1 + w, int(img_info['width'])),\n                min(y1 + h, int(img_info['height']))\n            ]\n\n            tokens_positive = sorted(tokens_positive, key=lambda x: x[0])\n\n            phrase = []\n            pre_end_index = -10\n            for token in tokens_positive:\n                start_index = token[0]\n                end_index = token[1]\n                if pre_end_index + 1 == start_index:\n                    if caption[token[0] - 1] == ' ':\n                        phrase[\n                            -1] = phrase[-1] + ' ' + caption[token[0]:token[1]]\n                    else:\n                        phrase.append(caption[token[0]:token[1]])\n                else:\n                    phrase.append(caption[token[0]:token[1]])\n                pre_end_index = end_index\n\n            key = ' '.join(phrase)\n\n            if key not in regions:\n                regions[key] = {\n                    'bbox': bbox_xyxy,\n                    'phrase': phrase,\n                    'tokens_positive': tokens_positive\n                }\n            else:\n                old_box = regions[key]['bbox']\n                if isinstance(old_box[0], list):\n                    old_box.append(bbox_xyxy)\n                else:\n                    old_box = [old_box, bbox_xyxy]\n\n                regions[key]['bbox'] = old_box\n\n        out_dict = {\n            'filename': file_name,\n            'height': int(img_info['height']),\n            'width': int(img_info['width']),\n            'grounding': {\n                'caption': caption\n            }\n        }\n\n        region_list = []\n        for key, value in regions.items():\n            phrase = value['phrase']\n            if len(phrase) == 1:\n                phrase = phrase[0]\n            region_list.append({\n                'bbox': value['bbox'],\n                'phrase': phrase,\n                'tokens_positive': value['tokens_positive']\n            })\n        out_dict['grounding']['regions'] = region_list\n        out_results.append(out_dict)\n\n    if args.out_dir is None:\n        out_path = osp.join(args.mdetr_anno_dir, filename[:-5] + '_vg.json')\n    else:\n        out_path = osp.join(args.out_dir, filename[:-5] + '_vg.json')\n\n    with jsonlines.open(out_path, mode='w') as writer:\n        writer.write_all(out_results)\n    print(f'save to {out_path}')\n\n\ndef main():\n    args = parse_args()\n    process_item(args, 'finetune_refcoco_train.json')\n    process_item(args, 'finetune_refcoco+_train.json')\n    process_item(args, 'finetune_refcocog_train.json')\n    process_item(args, 'finetune_grefcoco_train.json')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/remove_cocotrain2017_from_refcoco.py",
    "content": "import argparse\nimport json\nimport os.path as osp\n\nimport mmengine\nfrom pycocotools.coco import COCO\n\n\ndef diff_image_id(coco2017_train_ids, ref_ids):\n    set1 = set(coco2017_train_ids)\n    set2 = set(ref_ids)\n    intersection = set1.intersection(set2)\n    result = set1 - intersection\n    return result\n\n\ndef gen_new_json(coco2017_train_path, json_data, coco2017_train_ids):\n    coco = COCO(coco2017_train_path)\n    new_json_data = {\n        'info': json_data['info'],\n        'licenses': json_data['licenses'],\n        'categories': json_data['categories'],\n        'images': [],\n        'annotations': []\n    }\n\n    for id in coco2017_train_ids:\n        ann_ids = coco.getAnnIds(imgIds=[id])\n        img_ann_info = coco.loadAnns(ann_ids)\n        img_info = coco.loadImgs([id])[0]\n\n        new_json_data['images'].append(img_info)\n        new_json_data['annotations'].extend(img_ann_info)\n    return new_json_data\n\n\n# coco2017 val and final_mixed_train.json have no intersection,\n# so deduplication is not necessary.\n\n# coco2017 val and datasets like refcoco based on coco2014 train\n# have no intersection, so deduplication is not necessary.\n\n\n# coco2017 train and datasets like refcoco based on coco2014\n# train have overlapping annotations in the validation set,\n# so deduplication is required.\ndef exclude_coco(args):\n    with open(args.coco2017_train, 'r') as f:\n        coco2017_train = json.load(f)\n    coco2017_train_ids = [train['id'] for train in coco2017_train['images']]\n    orig_len = len(coco2017_train_ids)\n\n    with open(osp.join(args.mdetr_anno_dir, 'finetune_refcoco_val.json'),\n              'r') as f:\n        refcoco_ann = json.load(f)\n    refcoco_ids = [refcoco['original_id'] for refcoco in refcoco_ann['images']]\n    coco2017_train_ids = diff_image_id(coco2017_train_ids, refcoco_ids)\n\n    with open(\n            osp.join(args.mdetr_anno_dir, 'finetune_refcoco+_val.json'),\n            'r') as f:\n        refcoco_plus_ann = json.load(f)\n    refcoco_plus_ids = [\n        refcoco['original_id'] for refcoco in refcoco_plus_ann['images']\n    ]\n    coco2017_train_ids = diff_image_id(coco2017_train_ids, refcoco_plus_ids)\n\n    with open(\n            osp.join(args.mdetr_anno_dir, 'finetune_refcocog_val.json'),\n            'r') as f:\n        refcocog_ann = json.load(f)\n    refcocog_ids = [\n        refcoco['original_id'] for refcoco in refcocog_ann['images']\n    ]\n    coco2017_train_ids = diff_image_id(coco2017_train_ids, refcocog_ids)\n\n    with open(\n            osp.join(args.mdetr_anno_dir, 'finetune_grefcoco_val.json'),\n            'r') as f:\n        grefcoco_ann = json.load(f)\n    grefcoco_ids = [\n        refcoco['original_id'] for refcoco in grefcoco_ann['images']\n    ]\n    coco2017_train_ids = diff_image_id(coco2017_train_ids, grefcoco_ids)\n\n    coco2017_train_ids = list(coco2017_train_ids)\n    print(\n        'remove {} images from coco2017_train'.format(orig_len -\n                                                      len(coco2017_train_ids)))\n\n    new_json_data = gen_new_json(args.coco2017_train, coco2017_train,\n                                 coco2017_train_ids)\n    if args.out_ann is None:\n        out_ann = osp.dirname(\n            args.coco2017_train) + '/instances_train2017_norefval.json'\n        mmengine.dump(new_json_data, out_ann)\n        print('save new json to {}'.format(out_ann))\n    else:\n        mmengine.dump(new_json_data, args.out_ann)\n        print('save new json to {}'.format(args.out_ann))\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser('coco to odvg format.', add_help=True)\n    parser.add_argument('mdetr_anno_dir', type=str)\n    parser.add_argument('coco2017_train', type=str)\n    parser.add_argument('--out-ann', '-o', type=str)\n    args = parser.parse_args()\n\n    exclude_coco(args)\n"
  },
  {
    "path": "tools/dataset_converters/scripts/preprocess_coco2017.sh",
    "content": "#!/usr/bin/env bash\n\nDOWNLOAD_DIR=$1\nDATA_ROOT=$2\n\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Images/val2017.zip -d $DATA_ROOT\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Images/train2017.zip -d $DATA_ROOT\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Images/test2017.zip -d $DATA_ROOT/\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Images/unlabeled2017.zip -d $DATA_ROOT\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Annotations/stuff_annotations_trainval2017.zip -d $DATA_ROOT/\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Annotations/panoptic_annotations_trainval2017.zip -d $DATA_ROOT/\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Annotations/image_info_unlabeled2017.zip -d $DATA_ROOT/\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Annotations/image_info_test2017.zip -d $DATA_ROOT/\nunzip $DOWNLOAD_DIR/OpenDataLab___COCO_2017/raw/Annotations/annotations_trainval2017.zip -d $DATA_ROOT\nrm -rf $DOWNLOAD_DIR/OpenDataLab___COCO_2017\n"
  },
  {
    "path": "tools/dataset_converters/scripts/preprocess_voc2007.sh",
    "content": "#!/usr/bin/env bash\n\nDOWNLOAD_DIR=$1\nDATA_ROOT=$2\n\ntar -xvf $DOWNLOAD_DIR/OpenDataLab___PASCAL_VOC2007/raw/VOCtrainval_06-Nov-2007.tar -C $DATA_ROOT\ntar -xvf $DOWNLOAD_DIR/OpenDataLab___PASCAL_VOC2007/raw/VOCtestnoimgs_06-Nov-2007.tar -C $DATA_ROOT\nrm -rf $DOWNLOAD_DIR/OpenDataLab___PASCAL_VOC2007\n"
  },
  {
    "path": "tools/dataset_converters/scripts/preprocess_voc2012.sh",
    "content": "#!/usr/bin/env bash\n\nDOWNLOAD_DIR=$1\nDATA_ROOT=$2\n\ntar -xvf $DOWNLOAD_DIR/OpenDataLab___PASCAL_VOC2012/raw/VOCtrainval_11-May-2012.tar -C $DATA_ROOT\ntar -xvf $DOWNLOAD_DIR/OpenDataLab___PASCAL_VOC2012/raw/VOC2012test.tar -C $DATA_ROOT\nrm -rf $DOWNLOAD_DIR/OpenDataLab___PASCAL_VOC2012\n"
  },
  {
    "path": "tools/dataset_converters/youtubevis2coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport copy\nimport os\nimport os.path as osp\nfrom collections import defaultdict\n\nimport mmengine\nfrom tqdm import tqdm\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='YouTube-VIS to COCO Video format')\n    parser.add_argument(\n        '-i',\n        '--input',\n        help='root directory of YouTube-VIS annotations',\n    )\n    parser.add_argument(\n        '-o',\n        '--output',\n        help='directory to save coco formatted label file',\n    )\n    parser.add_argument(\n        '--version',\n        choices=['2019', '2021'],\n        help='The version of YouTube-VIS Dataset',\n    )\n    return parser.parse_args()\n\n\ndef convert_vis(ann_dir, save_dir, dataset_version, mode='train'):\n    \"\"\"Convert YouTube-VIS dataset in COCO style.\n\n    Args:\n        ann_dir (str): The path of YouTube-VIS dataset.\n        save_dir (str): The path to save `VIS`.\n        dataset_version (str): The version of dataset. Options are '2019',\n            '2021'.\n        mode (str): Convert train dataset or validation dataset or test\n            dataset. Options are 'train', 'valid', 'test'. Default: 'train'.\n    \"\"\"\n    assert dataset_version in ['2019', '2021']\n    assert mode in ['train', 'valid', 'test']\n    VIS = defaultdict(list)\n    records = dict(vid_id=1, img_id=1, ann_id=1, global_instance_id=1)\n    obj_num_classes = dict()\n\n    if dataset_version == '2019':\n        official_anns = mmengine.load(osp.join(ann_dir, f'{mode}.json'))\n    elif dataset_version == '2021':\n        official_anns = mmengine.load(\n            osp.join(ann_dir, mode, 'instances.json'))\n    VIS['categories'] = copy.deepcopy(official_anns['categories'])\n\n    has_annotations = mode == 'train'\n    if has_annotations:\n        vid_to_anns = defaultdict(list)\n        for ann_info in official_anns['annotations']:\n            vid_to_anns[ann_info['video_id']].append(ann_info)\n\n    video_infos = official_anns['videos']\n    for video_info in tqdm(video_infos):\n        video_name = video_info['file_names'][0].split(os.sep)[0]\n        video = dict(\n            id=video_info['id'],\n            name=video_name,\n            width=video_info['width'],\n            height=video_info['height'])\n        VIS['videos'].append(video)\n\n        num_frames = len(video_info['file_names'])\n        width = video_info['width']\n        height = video_info['height']\n        if has_annotations:\n            ann_infos_in_video = vid_to_anns[video_info['id']]\n            instance_id_maps = dict()\n\n        for frame_id in range(num_frames):\n            image = dict(\n                file_name=video_info['file_names'][frame_id],\n                height=height,\n                width=width,\n                id=records['img_id'],\n                frame_id=frame_id,\n                video_id=video_info['id'])\n            VIS['images'].append(image)\n\n            if has_annotations:\n                for ann_info in ann_infos_in_video:\n                    bbox = ann_info['bboxes'][frame_id]\n                    if bbox is None:\n                        continue\n\n                    category_id = ann_info['category_id']\n                    track_id = ann_info['id']\n                    segmentation = ann_info['segmentations'][frame_id]\n                    area = ann_info['areas'][frame_id]\n                    assert isinstance(category_id, int)\n                    assert isinstance(track_id, int)\n                    assert segmentation is not None\n                    assert area is not None\n\n                    if track_id in instance_id_maps:\n                        instance_id = instance_id_maps[track_id]\n                    else:\n                        instance_id = records['global_instance_id']\n                        records['global_instance_id'] += 1\n                        instance_id_maps[track_id] = instance_id\n\n                    ann = dict(\n                        id=records['ann_id'],\n                        video_id=video_info['id'],\n                        image_id=records['img_id'],\n                        category_id=category_id,\n                        instance_id=instance_id,\n                        bbox=bbox,\n                        segmentation=segmentation,\n                        area=area,\n                        iscrowd=ann_info['iscrowd'])\n\n                    if category_id not in obj_num_classes:\n                        obj_num_classes[category_id] = 1\n                    else:\n                        obj_num_classes[category_id] += 1\n\n                    VIS['annotations'].append(ann)\n                    records['ann_id'] += 1\n            records['img_id'] += 1\n        records['vid_id'] += 1\n\n    if not osp.isdir(save_dir):\n        os.makedirs(save_dir)\n    mmengine.dump(\n        VIS, osp.join(save_dir, f'youtube_vis_{dataset_version}_{mode}.json'))\n    print(f'-----YouTube VIS {dataset_version} {mode}------')\n    print(f'{records[\"vid_id\"]- 1} videos')\n    print(f'{records[\"img_id\"]- 1} images')\n    if has_annotations:\n        print(f'{records[\"ann_id\"] - 1} objects')\n        print(f'{records[\"global_instance_id\"] - 1} instances')\n    print('-----------------------')\n    if has_annotations:\n        for i in range(1, len(VIS['categories']) + 1):\n            class_name = VIS['categories'][i - 1]['name']\n            print(f'Class {i} {class_name} has {obj_num_classes[i]} objects.')\n\n\ndef main():\n    args = parse_args()\n    for sub_set in ['train', 'valid', 'test']:\n        convert_vis(args.input, args.output, args.version, sub_set)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/dataset_converters/zhiyuan_objv2_train_names_fix.csv",
    "content": "1,Person,Person\n2,Sneakers,Sneakers\n3,Chair,Chair\n4,Other Shoes,Other Shoes\n5,Hat,Hat\n6,Car,Car\n7,Lamp,Lamp\n8,Glasses,Glasses\n9,Bottle,Bottle\n10,Desk,Desk\n11,Cup,Cup\n12,Street Lights,Street Lights\n13,Cabinet/shelf,Cabinet/shelf\n14,Handbag/Satchel,Handbag/Satchel\n15,Bracelet,Bracelet\n16,Plate,Plate\n17,Picture/Frame,Picture/Frame\n18,Helmet,Helmet\n19,Book,Book\n20,Gloves,Gloves\n21,Storage box,Storage box\n22,Boat,Boat\n23,Leather Shoes,Leather Shoes\n24,Flower,Flower\n25,Bench,Bench\n26,Potted Plant,Potted Plant\n27,Bowl/Basin,Bowl/Basin\n28,Flag,Flag\n29,Pillow,Pillow\n30,Boots,Boots\n31,Vase,Vase\n32,Microphone,Microphone\n33,Necklace,Necklace\n34,Ring,Ring\n35,SUV,SUV\n36,Wine Glass,Wine Glass\n37,Belt,Belt\n38,Moniter/TV,Monitor/TV\n39,Backpack,Backpack\n40,Umbrella,Umbrella\n41,Traffic Light,Traffic Light\n42,Speaker,Speaker\n43,Watch,Watch\n44,Tie,Tie\n45,Trash bin Can,Trash bin Can\n46,Slippers,Slippers\n47,Bicycle,Bicycle\n48,Stool,Stool\n49,Barrel/bucket,Barrel/bucket\n50,Van,Van\n51,Couch,Couch\n52,Sandals,Sandals\n53,Bakset,Basket\n54,Drum,Drum\n55,Pen/Pencil,Pen/Pencil\n56,Bus,Bus\n57,Wild Bird,Wild Bird\n58,High Heels,High Heels\n59,Motorcycle,Motorcycle\n60,Guitar,Guitar\n61,Carpet,Carpet\n62,Cell Phone,Cell Phone\n63,Bread,Bread\n64,Camera,Camera\n65,Canned,Canned\n66,Truck,Truck\n67,Traffic cone,Traffic cone\n68,Cymbal,Cymbal\n69,Lifesaver,Lifesaver\n70,Towel,Towel\n71,Stuffed Toy,Stuffed Toy\n72,Candle,Candle\n73,Sailboat,Sailboat\n74,Laptop,Laptop\n75,Awning,Awning\n76,Bed,Bed\n77,Faucet,Faucet\n78,Tent,Tent\n79,Horse,Horse\n80,Mirror,Mirror\n81,Power outlet,Power outlet\n82,Sink,Sink\n83,Apple,Apple\n84,Air Conditioner,Air Conditioner\n85,Knife,Knife\n86,Hockey Stick,Hockey Stick\n87,Paddle,Paddle\n88,Pickup Truck,Pickup Truck\n89,Fork,Fork\n90,Traffic Sign,Traffic Sign\n91,Ballon,Ballon\n92,Tripod,Tripod\n93,Dog,Dog\n94,Spoon,Spoon\n95,Clock,Clock\n96,Pot,Pot\n97,Cow,Cow\n98,Cake,Cake\n99,Dinning Table,Dining Table\n100,Sheep,Sheep\n101,Hanger,Hanger\n102,Blackboard/Whiteboard,Blackboard/Whiteboard\n103,Napkin,Napkin\n104,Other Fish,Other Fish\n105,Orange/Tangerine,Orange/Tangerine\n106,Toiletry,Toiletry\n107,Keyboard,Keyboard\n108,Tomato,Tomato\n109,Lantern,Lantern\n110,Machinery Vehicle,Machinery Vehicle\n111,Fan,Fan\n112,Green Vegetables,Green Vegetables\n113,Banana,Banana\n114,Baseball Glove,Baseball Glove\n115,Airplane,Airplane\n116,Mouse,Mouse\n117,Train,Train\n118,Pumpkin,Pumpkin\n119,Soccer,Soccer\n120,Skiboard,Skiboard\n121,Luggage,Luggage\n122,Nightstand,Nightstand\n123,Tea pot,Teapot\n124,Telephone,Telephone\n125,Trolley,Trolley\n126,Head Phone,Head Phone\n127,Sports Car,Sports Car\n128,Stop Sign,Stop Sign\n129,Dessert,Dessert\n130,Scooter,Scooter\n131,Stroller,Stroller\n132,Crane,Crane\n133,Remote,Remote\n134,Refrigerator,Refrigerator\n135,Oven,Oven\n136,Lemon,Lemon\n137,Duck,Duck\n138,Baseball Bat,Baseball Bat\n139,Surveillance Camera,Surveillance Camera\n140,Cat,Cat\n141,Jug,Jug\n142,Broccoli,Broccoli\n143,Piano,Piano\n144,Pizza,Pizza\n145,Elephant,Elephant\n146,Skateboard,Skateboard\n147,Surfboard,Surfboard\n148,Gun,Gun\n149,Skating and Skiing shoes,Skating and Skiing shoes\n150,Gas stove,Gas stove\n151,Donut,Donut\n152,Bow Tie,Bow Tie\n153,Carrot,Carrot\n154,Toilet,Toilet\n155,Kite,Kite\n156,Strawberry,Strawberry\n157,Other Balls,Other Balls\n158,Shovel,Shovel\n159,Pepper,Pepper\n160,Computer Box,Computer Box\n161,Toilet Paper,Toilet Paper\n162,Cleaning Products,Cleaning Products\n163,Chopsticks,Chopsticks\n164,Microwave,Microwave\n165,Pigeon,Pigeon\n166,Baseball,Baseball\n167,Cutting/chopping Board,Cutting/chopping Board\n168,Coffee Table,Coffee Table\n169,Side Table,Side Table\n170,Scissors,Scissors\n171,Marker,Marker\n172,Pie,Pie\n173,Ladder,Ladder\n174,Snowboard,Snowboard\n175,Cookies,Cookies\n176,Radiator,Radiator\n177,Fire Hydrant,Fire Hydrant\n178,Basketball,Basketball\n179,Zebra,Zebra\n180,Grape,Grape\n181,Giraffe,Giraffe\n182,Potato,Potato\n183,Sausage,Sausage\n184,Tricycle,Tricycle\n185,Violin,Violin\n186,Egg,Egg\n187,Fire Extinguisher,Fire Extinguisher\n188,Candy,Candy\n189,Fire Truck,Fire Truck\n190,Billards,Billiards\n191,Converter,Converter\n192,Bathtub,Bathtub\n193,Wheelchair,Wheelchair\n194,Golf Club,Golf Club\n195,Briefcase,Briefcase\n196,Cucumber,Cucumber\n197,Cigar/Cigarette,Cigar/Cigarette\n198,Paint Brush,Paint Brush\n199,Pear,Pear\n200,Heavy Truck,Heavy Truck\n201,Hamburger,Hamburger\n202,Extractor,Extractor\n203,Extention Cord,Extension Cord\n204,Tong,Tong\n205,Tennis Racket,Tennis Racket\n206,Folder,Folder\n207,American Football,American Football\n208,earphone,earphone\n209,Mask,Mask\n210,Kettle,Kettle\n211,Tennis,Tennis\n212,Ship,Ship\n213,Swing,Swing\n214,Coffee Machine,Coffee Machine\n215,Slide,Slide\n216,Carriage,Carriage\n217,Onion,Onion\n218,Green beans,Green beans\n219,Projector,Projector\n220,Frisbee,Frisbee\n221,Washing Machine/Drying Machine,Washing Machine/Drying Machine\n222,Chicken,Chicken\n223,Printer,Printer\n224,Watermelon,Watermelon\n225,Saxophone,Saxophone\n226,Tissue,Tissue\n227,Toothbrush,Toothbrush\n228,Ice cream,Ice cream\n229,Hotair ballon,Hot air balloon\n230,Cello,Cello\n231,French Fries,French Fries\n232,Scale,Scale\n233,Trophy,Trophy\n234,Cabbage,Cabbage\n235,Hot dog,Hot dog\n236,Blender,Blender\n237,Peach,Peach\n238,Rice,Rice\n239,Wallet/Purse,Wallet/Purse\n240,Volleyball,Volleyball\n241,Deer,Deer\n242,Goose,Goose\n243,Tape,Tape\n244,Tablet,Tablet\n245,Cosmetics,Cosmetics\n246,Trumpet,Trumpet\n247,Pineapple,Pineapple\n248,Golf Ball,Golf Ball\n249,Ambulance,Ambulance\n250,Parking meter,Parking meter\n251,Mango,Mango\n252,Key,Key\n253,Hurdle,Hurdle\n254,Fishing Rod,Fishing Rod\n255,Medal,Medal\n256,Flute,Flute\n257,Brush,Brush\n258,Penguin,Penguin\n259,Megaphone,Megaphone\n260,Corn,Corn\n261,Lettuce,Lettuce\n262,Garlic,Garlic\n263,Swan,Swan\n264,Helicopter,Helicopter\n265,Green Onion,Green Onion\n266,Sandwich,Sandwich\n267,Nuts,Nuts\n268,Speed Limit Sign,Speed Limit Sign\n269,Induction Cooker,Induction Cooker\n270,Broom,Broom\n271,Trombone,Trombone\n272,Plum,Plum\n273,Rickshaw,Rickshaw\n274,Goldfish,Goldfish\n275,Kiwi fruit,Kiwi fruit\n276,Router/modem,Router/modem\n277,Poker Card,Poker Card\n278,Toaster,Toaster\n279,Shrimp,Shrimp\n280,Sushi,Sushi\n281,Cheese,Cheese\n282,Notepaper,Notepaper\n283,Cherry,Cherry\n284,Pliers,Pliers\n285,CD,CD\n286,Pasta,Pasta\n287,Hammer,Hammer\n288,Cue,Cue\n289,Avocado,Avocado\n290,Hamimelon,Hami melon\n291,Flask,Flask\n292,Mushroon,Mushroom\n293,Screwdriver,Screwdriver\n294,Soap,Soap\n295,Recorder,Recorder\n296,Bear,Bear\n297,Eggplant,Eggplant\n298,Board Eraser,Board Eraser\n299,Coconut,Coconut\n300,Tape Measur/ Ruler,Tape Measure/ Ruler\n301,Pig,Pig\n302,Showerhead,Showerhead\n303,Globe,Globe\n304,Chips,Chips\n305,Steak,Steak\n306,Crosswalk Sign,Crosswalk Sign\n307,Stapler,Stapler\n308,Campel,Camel\n309,Formula 1,Formula 1\n310,Pomegranate,Pomegranate\n311,Dishwasher,Dishwasher\n312,Crab,Crab\n313,Hoverboard,Hoverboard\n314,Meat ball,Meatball\n315,Rice Cooker,Rice Cooker\n316,Tuba,Tuba\n317,Calculator,Calculator\n318,Papaya,Papaya\n319,Antelope,Antelope\n320,Parrot,Parrot\n321,Seal,Seal\n322,Buttefly,Butterfly\n323,Dumbbell,Dumbbell\n324,Donkey,Donkey\n325,Lion,Lion\n326,Urinal,Urinal\n327,Dolphin,Dolphin\n328,Electric Drill,Electric Drill\n329,Hair Dryer,Hair Dryer\n330,Egg tart,Egg tart\n331,Jellyfish,Jellyfish\n332,Treadmill,Treadmill\n333,Lighter,Lighter\n334,Grapefruit,Grapefruit\n335,Game board,Game board\n336,Mop,Mop\n337,Radish,Radish\n338,Baozi,Baozi\n339,Target,Target\n340,French,French\n341,Spring Rolls,Spring Rolls\n342,Monkey,Monkey\n343,Rabbit,Rabbit\n344,Pencil Case,Pencil Case\n345,Yak,Yak\n346,Red Cabbage,Red Cabbage\n347,Binoculars,Binoculars\n348,Asparagus,Asparagus\n349,Barbell,Barbell\n350,Scallop,Scallop\n351,Noddles,Noddles\n352,Comb,Comb\n353,Dumpling,Dumpling\n354,Oyster,Oyster\n355,Table Teniis paddle,Table Tennis paddle\n356,Cosmetics Brush/Eyeliner Pencil,Cosmetics Brush/Eyeliner Pencil\n357,Chainsaw,Chainsaw\n358,Eraser,Eraser\n359,Lobster,Lobster\n360,Durian,Durian\n361,Okra,Okra\n362,Lipstick,Lipstick\n363,Cosmetics Mirror,Cosmetics Mirror\n364,Curling,Curling\n365,Table Tennis,Table Tennis\n"
  },
  {
    "path": "tools/deployment/mmdet2torchserve.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nfrom argparse import ArgumentParser, Namespace\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\n\nfrom mmengine.config import Config\nfrom mmengine.utils import mkdir_or_exist\n\ntry:\n    from model_archiver.model_packaging import package_model\n    from model_archiver.model_packaging_utils import ModelExportUtils\nexcept ImportError:\n    package_model = None\n\n\ndef mmdet2torchserve(\n    config_file: str,\n    checkpoint_file: str,\n    output_folder: str,\n    model_name: str,\n    model_version: str = '1.0',\n    force: bool = False,\n):\n    \"\"\"Converts MMDetection model (config + checkpoint) to TorchServe `.mar`.\n\n    Args:\n        config_file:\n            In MMDetection config format.\n            The contents vary for each task repository.\n        checkpoint_file:\n            In MMDetection checkpoint format.\n            The contents vary for each task repository.\n        output_folder:\n            Folder where `{model_name}.mar` will be created.\n            The file created will be in TorchServe archive format.\n        model_name:\n            If not None, used for naming the `{model_name}.mar` file\n            that will be created under `output_folder`.\n            If None, `{Path(checkpoint_file).stem}` will be used.\n        model_version:\n            Model's version.\n        force:\n            If True, if there is an existing `{model_name}.mar`\n            file under `output_folder` it will be overwritten.\n    \"\"\"\n    mkdir_or_exist(output_folder)\n\n    config = Config.fromfile(config_file)\n\n    with TemporaryDirectory() as tmpdir:\n        config.dump(f'{tmpdir}/config.py')\n\n        args = Namespace(\n            **{\n                'model_file': f'{tmpdir}/config.py',\n                'config_file': f'{tmpdir}/config.py',\n                'serialized_file': checkpoint_file,\n                'handler': f'{Path(__file__).parent}/mmdet_handler.py',\n                'model_name': model_name or Path(checkpoint_file).stem,\n                'version': model_version,\n                'export_path': output_folder,\n                'force': force,\n                'requirements_file': None,\n                'extra_files': None,\n                'runtime': 'python',\n                'archive_format': 'default'\n            })\n        manifest = ModelExportUtils.generate_manifest_json(args)\n        package_model(args, manifest)\n\n\ndef parse_args():\n    parser = ArgumentParser(\n        description='Convert MMDetection models to TorchServe `.mar` format.')\n    parser.add_argument('config', type=str, help='config file path')\n    parser.add_argument('checkpoint', type=str, help='checkpoint file path')\n    parser.add_argument(\n        '--output-folder',\n        type=str,\n        required=True,\n        help='Folder where `{model_name}.mar` will be created.')\n    parser.add_argument(\n        '--model-name',\n        type=str,\n        default=None,\n        help='If not None, used for naming the `{model_name}.mar`'\n        'file that will be created under `output_folder`.'\n        'If None, `{Path(checkpoint_file).stem}` will be used.')\n    parser.add_argument(\n        '--model-version',\n        type=str,\n        default='1.0',\n        help='Number used for versioning.')\n    parser.add_argument(\n        '-f',\n        '--force',\n        action='store_true',\n        help='overwrite the existing `{model_name}.mar`')\n    args = parser.parse_args()\n\n    return args\n\n\nif __name__ == '__main__':\n    args = parse_args()\n\n    if package_model is None:\n        raise ImportError('`torch-model-archiver` is required.'\n                          'Try: pip install torch-model-archiver')\n\n    mmdet2torchserve(args.config, args.checkpoint, args.output_folder,\n                     args.model_name, args.model_version, args.force)\n"
  },
  {
    "path": "tools/deployment/mmdet_handler.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport base64\nimport os\n\nimport mmcv\nimport numpy as np\nimport torch\nfrom ts.torch_handler.base_handler import BaseHandler\n\nfrom mmdet.apis import inference_detector, init_detector\n\n\nclass MMdetHandler(BaseHandler):\n    threshold = 0.5\n\n    def initialize(self, context):\n        properties = context.system_properties\n        self.map_location = 'cuda' if torch.cuda.is_available() else 'cpu'\n        self.device = torch.device(self.map_location + ':' +\n                                   str(properties.get('gpu_id')) if torch.cuda.\n                                   is_available() else self.map_location)\n        self.manifest = context.manifest\n\n        model_dir = properties.get('model_dir')\n        serialized_file = self.manifest['model']['serializedFile']\n        checkpoint = os.path.join(model_dir, serialized_file)\n        self.config_file = os.path.join(model_dir, 'config.py')\n\n        self.model = init_detector(self.config_file, checkpoint, self.device)\n        self.initialized = True\n\n    def preprocess(self, data):\n        images = []\n\n        for row in data:\n            image = row.get('data') or row.get('body')\n            if isinstance(image, str):\n                image = base64.b64decode(image)\n            image = mmcv.imfrombytes(image)\n            images.append(image)\n\n        return images\n\n    def inference(self, data, *args, **kwargs):\n        results = inference_detector(self.model, data)\n        return results\n\n    def postprocess(self, data):\n        # Format output following the example ObjectDetectionHandler format\n        output = []\n        for data_sample in data:\n            pred_instances = data_sample.pred_instances\n            bboxes = pred_instances.bboxes.cpu().numpy().astype(\n                np.float32).tolist()\n            labels = pred_instances.labels.cpu().numpy().astype(\n                np.int32).tolist()\n            scores = pred_instances.scores.cpu().numpy().astype(\n                np.float32).tolist()\n            preds = []\n            for idx in range(len(labels)):\n                cls_score, bbox, cls_label = scores[idx], bboxes[idx], labels[\n                    idx]\n                if cls_score >= self.threshold:\n                    class_name = self.model.dataset_meta['classes'][cls_label]\n                    result = dict(\n                        class_label=cls_label,\n                        class_name=class_name,\n                        bbox=bbox,\n                        score=cls_score)\n                    preds.append(result)\n            output.append(preds)\n        return output\n"
  },
  {
    "path": "tools/deployment/test_torchserver.py",
    "content": "import os\nfrom argparse import ArgumentParser\n\nimport mmcv\nimport requests\nimport torch\nfrom mmengine.structures import InstanceData\n\nfrom mmdet.apis import inference_detector, init_detector\nfrom mmdet.registry import VISUALIZERS\nfrom mmdet.structures import DetDataSample\n\n\ndef parse_args():\n    parser = ArgumentParser()\n    parser.add_argument('img', help='Image file')\n    parser.add_argument('config', help='Config file')\n    parser.add_argument('checkpoint', help='Checkpoint file')\n    parser.add_argument('model_name', help='The model name in the server')\n    parser.add_argument(\n        '--inference-addr',\n        default='127.0.0.1:8080',\n        help='Address and port of the inference server')\n    parser.add_argument(\n        '--device', default='cuda:0', help='Device used for inference')\n    parser.add_argument(\n        '--score-thr', type=float, default=0.5, help='bbox score threshold')\n    parser.add_argument(\n        '--work-dir',\n        type=str,\n        default=None,\n        help='output directory to save drawn results.')\n    args = parser.parse_args()\n    return args\n\n\ndef align_ts_output(inputs, metainfo, device):\n    bboxes = []\n    labels = []\n    scores = []\n    for i, pred in enumerate(inputs):\n        bboxes.append(pred['bbox'])\n        labels.append(pred['class_label'])\n        scores.append(pred['score'])\n    pred_instances = InstanceData(metainfo=metainfo)\n    pred_instances.bboxes = torch.tensor(\n        bboxes, dtype=torch.float32, device=device)\n    pred_instances.labels = torch.tensor(\n        labels, dtype=torch.int64, device=device)\n    pred_instances.scores = torch.tensor(\n        scores, dtype=torch.float32, device=device)\n    ts_data_sample = DetDataSample(pred_instances=pred_instances)\n    return ts_data_sample\n\n\ndef main(args):\n    # build the model from a config file and a checkpoint file\n    model = init_detector(args.config, args.checkpoint, device=args.device)\n    # test a single image\n    pytorch_results = inference_detector(model, args.img)\n    keep = pytorch_results.pred_instances.scores >= args.score_thr\n    pytorch_results.pred_instances = pytorch_results.pred_instances[keep]\n\n    # init visualizer\n    visualizer = VISUALIZERS.build(model.cfg.visualizer)\n    # the dataset_meta is loaded from the checkpoint and\n    # then pass to the model in init_detector\n    visualizer.dataset_meta = model.dataset_meta\n\n    # show the results\n    img = mmcv.imread(args.img)\n    img = mmcv.imconvert(img, 'bgr', 'rgb')\n    pt_out_file = None\n    ts_out_file = None\n    if args.work_dir is not None:\n        os.makedirs(args.work_dir, exist_ok=True)\n        pt_out_file = os.path.join(args.work_dir, 'pytorch_result.png')\n        ts_out_file = os.path.join(args.work_dir, 'torchserve_result.png')\n    visualizer.add_datasample(\n        'pytorch_result',\n        img.copy(),\n        data_sample=pytorch_results,\n        draw_gt=False,\n        out_file=pt_out_file,\n        show=True,\n        wait_time=0)\n\n    url = 'http://' + args.inference_addr + '/predictions/' + args.model_name\n    with open(args.img, 'rb') as image:\n        response = requests.post(url, image)\n    metainfo = pytorch_results.pred_instances.metainfo\n    ts_results = align_ts_output(response.json(), metainfo, args.device)\n\n    visualizer.add_datasample(\n        'torchserve_result',\n        img,\n        data_sample=ts_results,\n        draw_gt=False,\n        out_file=ts_out_file,\n        show=True,\n        wait_time=0)\n\n    assert torch.allclose(pytorch_results.pred_instances.bboxes,\n                          ts_results.pred_instances.bboxes)\n    assert torch.allclose(pytorch_results.pred_instances.labels,\n                          ts_results.pred_instances.labels)\n    assert torch.allclose(pytorch_results.pred_instances.scores,\n                          ts_results.pred_instances.scores)\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    main(args)\n"
  },
  {
    "path": "tools/dist_test.sh",
    "content": "#!/usr/bin/env bash\n\nCONFIG=$1\nCHECKPOINT=$2\nGPUS=$3\nNNODES=${NNODES:-1}\nNODE_RANK=${NODE_RANK:-0}\nPORT=${PORT:-29500}\nMASTER_ADDR=${MASTER_ADDR:-\"127.0.0.1\"}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\npython -m torch.distributed.launch \\\n    --nnodes=$NNODES \\\n    --node_rank=$NODE_RANK \\\n    --master_addr=$MASTER_ADDR \\\n    --nproc_per_node=$GPUS \\\n    --master_port=$PORT \\\n    $(dirname \"$0\")/test.py \\\n    $CONFIG \\\n    $CHECKPOINT \\\n    --launcher pytorch \\\n    ${@:4}\n"
  },
  {
    "path": "tools/dist_test_tracking.sh",
    "content": "#!/usr/bin/env bash\n\nCONFIG=$1\nGPUS=$2\nNNODES=${NNODES:-1}\nNODE_RANK=${NODE_RANK:-0}\nPORT=${PORT:-29500}\nMASTER_ADDR=${MASTER_ADDR:-\"127.0.0.1\"}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\npython -m torch.distributed.launch \\\n    --nnodes=$NNODES \\\n    --node_rank=$NODE_RANK \\\n    --master_addr=$MASTER_ADDR \\\n    --nproc_per_node=$GPUS \\\n    --master_port=$PORT \\\n    $(dirname \"$0\")/test_tracking.py \\\n    $CONFIG \\\n    --launcher pytorch \\\n    ${@:3}\n"
  },
  {
    "path": "tools/dist_train.sh",
    "content": "#!/usr/bin/env bash\n\nCONFIG=$1\nGPUS=$2\nNNODES=${NNODES:-1}\nNODE_RANK=${NODE_RANK:-0}\nPORT=${PORT:-29500}\nMASTER_ADDR=${MASTER_ADDR:-\"127.0.0.1\"}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\npython -m torch.distributed.launch \\\n    --nnodes=$NNODES \\\n    --node_rank=$NODE_RANK \\\n    --master_addr=$MASTER_ADDR \\\n    --nproc_per_node=$GPUS \\\n    --master_port=$PORT \\\n    $(dirname \"$0\")/train.py \\\n    $CONFIG \\\n    --launcher pytorch ${@:3}\n"
  },
  {
    "path": "tools/misc/download_dataset.py",
    "content": "import argparse\nimport tarfile\nfrom itertools import repeat\nfrom multiprocessing.pool import ThreadPool\nfrom pathlib import Path\nfrom tarfile import TarFile\nfrom zipfile import ZipFile\n\nimport torch\nfrom mmengine.utils.path import mkdir_or_exist\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Download datasets for training')\n    parser.add_argument(\n        '--dataset-name', type=str, help='dataset name', default='coco2017')\n    parser.add_argument(\n        '--save-dir',\n        type=str,\n        help='the dir to save dataset',\n        default='data/coco')\n    parser.add_argument(\n        '--unzip',\n        action='store_true',\n        help='whether unzip dataset or not, zipped files will be saved')\n    parser.add_argument(\n        '--delete',\n        action='store_true',\n        help='delete the download zipped files')\n    parser.add_argument(\n        '--threads', type=int, help='number of threading', default=4)\n    args = parser.parse_args()\n    return args\n\n\ndef download(url, dir, unzip=True, delete=False, threads=1):\n\n    def download_one(url, dir):\n        f = dir / Path(url).name\n        if Path(url).is_file():\n            Path(url).rename(f)\n        elif not f.exists():\n            print(f'Downloading {url} to {f}')\n            torch.hub.download_url_to_file(url, f, progress=True)\n        if unzip and f.suffix in ('.zip', '.tar'):\n            print(f'Unzipping {f.name}')\n            if f.suffix == '.zip':\n                ZipFile(f).extractall(path=dir)\n            elif f.suffix == '.tar':\n                TarFile(f).extractall(path=dir)\n            if delete:\n                f.unlink()\n                print(f'Delete {f}')\n\n    dir = Path(dir)\n    if threads > 1:\n        pool = ThreadPool(threads)\n        pool.imap(lambda x: download_one(*x), zip(url, repeat(dir)))\n        pool.close()\n        pool.join()\n    else:\n        for u in [url] if isinstance(url, (str, Path)) else url:\n            download_one(u, dir)\n\n\ndef download_objects365v2(url, dir, unzip=True, delete=False, threads=1):\n\n    def download_single(url, dir):\n\n        if 'train' in url:\n            saving_dir = dir / Path('train_zip')\n            mkdir_or_exist(saving_dir)\n            f = saving_dir / Path(url).name\n\n            unzip_dir = dir / Path('train')\n            mkdir_or_exist(unzip_dir)\n        elif 'val' in url:\n            saving_dir = dir / Path('val')\n            mkdir_or_exist(saving_dir)\n            f = saving_dir / Path(url).name\n\n            unzip_dir = dir / Path('val')\n            mkdir_or_exist(unzip_dir)\n        else:\n            raise NotImplementedError\n\n        if Path(url).is_file():\n            Path(url).rename(f)\n        elif not f.exists():\n            print(f'Downloading {url} to {f}')\n            torch.hub.download_url_to_file(url, f, progress=True)\n\n        if unzip and str(f).endswith('.tar.gz'):\n            print(f'Unzipping {f.name}')\n            tar = tarfile.open(f)\n            tar.extractall(path=unzip_dir)\n            if delete:\n                f.unlink()\n                print(f'Delete {f}')\n\n    # process annotations\n    full_url = []\n    for _url in url:\n        if 'zhiyuan_objv2_train.tar.gz' in _url or \\\n                'zhiyuan_objv2_val.json' in _url:\n            full_url.append(_url)\n        elif 'train' in _url:\n            for i in range(51):\n                full_url.append(f'{_url}patch{i}.tar.gz')\n        elif 'val/images/v1' in _url:\n            for i in range(16):\n                full_url.append(f'{_url}patch{i}.tar.gz')\n        elif 'val/images/v2' in _url:\n            for i in range(16, 44):\n                full_url.append(f'{_url}patch{i}.tar.gz')\n        else:\n            raise NotImplementedError\n\n    dir = Path(dir)\n    if threads > 1:\n        pool = ThreadPool(threads)\n        pool.imap(lambda x: download_single(*x), zip(full_url, repeat(dir)))\n        pool.close()\n        pool.join()\n    else:\n        for u in full_url:\n            download_single(u, dir)\n\n\ndef main():\n    args = parse_args()\n    path = Path(args.save_dir)\n    if not path.exists():\n        path.mkdir(parents=True, exist_ok=True)\n    data2url = dict(\n        # TODO: Support for downloading Panoptic Segmentation of COCO\n        coco2017=[\n            'http://images.cocodataset.org/zips/train2017.zip',\n            'http://images.cocodataset.org/zips/val2017.zip',\n            'http://images.cocodataset.org/zips/test2017.zip',\n            'http://images.cocodataset.org/zips/unlabeled2017.zip',\n            'http://images.cocodataset.org/annotations/annotations_trainval2017.zip',  # noqa\n            'http://images.cocodataset.org/annotations/stuff_annotations_trainval2017.zip',  # noqa\n            'http://images.cocodataset.org/annotations/panoptic_annotations_trainval2017.zip',  # noqa\n            'http://images.cocodataset.org/annotations/image_info_test2017.zip',  # noqa\n            'http://images.cocodataset.org/annotations/image_info_unlabeled2017.zip',  # noqa\n        ],\n        coco2014=[\n            'http://images.cocodataset.org/zips/train2014.zip',\n            'http://images.cocodataset.org/zips/val2014.zip',\n            'http://images.cocodataset.org/zips/test2014.zip',\n            'http://images.cocodataset.org/annotations/annotations_trainval2014.zip',  # noqa\n            'http://images.cocodataset.org/annotations/image_info_test2014.zip'  # noqa\n        ],\n        lvis=[\n            'https://s3-us-west-2.amazonaws.com/dl.fbaipublicfiles.com/LVIS/lvis_v1_train.json.zip',  # noqa\n            'https://s3-us-west-2.amazonaws.com/dl.fbaipublicfiles.com/LVIS/lvis_v1_train.json.zip',  # noqa\n        ],\n        voc2007=[\n            'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar',  # noqa\n            'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar',  # noqa\n            'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCdevkit_08-Jun-2007.tar',  # noqa\n        ],\n        voc2012=[\n            'http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar',  # noqa\n        ],\n        balloon=[\n            # src link: https://github.com/matterport/Mask_RCNN/releases/download/v2.1/balloon_dataset.zip # noqa\n            'https://download.openmmlab.com/mmyolo/data/balloon_dataset.zip'\n        ],\n        # Note: There is no download link for Objects365-V1 right now. If you\n        # would like to download Objects365-V1, please visit\n        # http://www.objects365.org/ to concat the author.\n        objects365v2=[\n            # training annotations\n            'https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/train/zhiyuan_objv2_train.tar.gz',  # noqa\n            # validation annotations\n            'https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/val/zhiyuan_objv2_val.json',  # noqa\n            # training url root\n            'https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/train/',  # noqa\n            # validation url root_1\n            'https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/val/images/v1/',  # noqa\n            # validation url root_2\n            'https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/val/images/v2/'  # noqa\n        ],\n        ade20k_2016=[\n            # training images and semantic segmentation annotations\n            'http://data.csail.mit.edu/places/ADEchallenge/ADEChallengeData2016.zip',  # noqa\n            # instance segmentation annotations\n            'http://sceneparsing.csail.mit.edu/data/ChallengeData2017/annotations_instance.tar',  # noqa\n            # img categories ids\n            'https://raw.githubusercontent.com/CSAILVision/placeschallenge/master/instancesegmentation/imgCatIds.json',  # noqa\n            # category mapping\n            'https://raw.githubusercontent.com/CSAILVision/placeschallenge/master/instancesegmentation/categoryMapping.txt'  # noqa\n        ],\n        refcoco=[\n            # images\n            'http://images.cocodataset.org/zips/train2014.zip',\n            # refcoco annotations\n            'https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcoco.zip',\n            # refcoco+ annotations\n            'https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcoco+.zip',\n            # refcocog annotations\n            'https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcocog.zip'\n        ])\n    url = data2url.get(args.dataset_name, None)\n    if url is None:\n        print('Only support ADE20K, COCO, RefCOCO, VOC, LVIS, '\n              'balloon, and Objects365v2 now!')\n        return\n    if args.dataset_name == 'objects365v2':\n        download_objects365v2(\n            url,\n            dir=path,\n            unzip=args.unzip,\n            delete=args.delete,\n            threads=args.threads)\n    else:\n        download(\n            url,\n            dir=path,\n            unzip=args.unzip,\n            delete=args.delete,\n            threads=args.threads)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/misc/gen_coco_panoptic_test_info.py",
    "content": "import argparse\nimport os.path as osp\n\nfrom mmengine.fileio import dump, load\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Generate COCO test image information '\n        'for COCO panoptic segmentation.')\n    parser.add_argument('data_root', help='Path to COCO annotation directory.')\n    args = parser.parse_args()\n\n    return args\n\n\ndef main():\n    args = parse_args()\n    data_root = args.data_root\n    val_info = load(osp.join(data_root, 'panoptic_val2017.json'))\n    test_old_info = load(osp.join(data_root, 'image_info_test-dev2017.json'))\n\n    # replace categories from image_info_test-dev2017.json\n    # with categories from panoptic_val2017.json which\n    # has attribute `isthing`.\n    test_info = test_old_info\n    test_info.update({'categories': val_info['categories']})\n    dump(test_info, osp.join(data_root,\n                             'panoptic_image_info_test-dev2017.json'))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/misc/get_crowdhuman_id_hw.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Get image shape on CrowdHuman dataset.\n\nHere is an example to run this script.\n\nExample:\n    python tools/misc/get_crowdhuman_id_hw.py ${CONFIG} \\\n    --dataset ${DATASET_TYPE}\n\"\"\"\nimport argparse\nimport json\nimport logging\nimport os.path as osp\nfrom multiprocessing import Pool\n\nimport mmcv\nfrom mmengine.config import Config\nfrom mmengine.fileio import dump, get, get_text\nfrom mmengine.logging import print_log\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Collect image metas')\n    parser.add_argument('config', help='Config file path')\n    parser.add_argument(\n        '--dataset',\n        choices=['train', 'val'],\n        help='Collect image metas from which dataset')\n    parser.add_argument(\n        '--nproc',\n        default=10,\n        type=int,\n        help='Processes used for get image metas')\n    args = parser.parse_args()\n    return args\n\n\ndef get_image_metas(anno_str, img_prefix):\n    id_hw = {}\n    anno_dict = json.loads(anno_str)\n    img_path = osp.join(img_prefix, f\"{anno_dict['ID']}.jpg\")\n    img_id = anno_dict['ID']\n    img_bytes = get(img_path)\n    img = mmcv.imfrombytes(img_bytes, backend='cv2')\n    id_hw[img_id] = img.shape[:2]\n    return id_hw\n\n\ndef main():\n    args = parse_args()\n\n    # get ann_file and img_prefix from config files\n    cfg = Config.fromfile(args.config)\n    dataset = args.dataset\n    dataloader_cfg = cfg.get(f'{dataset}_dataloader')\n    ann_file = osp.join(dataloader_cfg.dataset.data_root,\n                        dataloader_cfg.dataset.ann_file)\n    img_prefix = osp.join(dataloader_cfg.dataset.data_root,\n                          dataloader_cfg.dataset.data_prefix['img'])\n\n    # load image metas\n    print_log(\n        f'loading CrowdHuman {dataset} annotation...', level=logging.INFO)\n    anno_strs = get_text(ann_file).strip().split('\\n')\n    pool = Pool(args.nproc)\n    # get image metas with multiple processes\n    id_hw_temp = pool.starmap(\n        get_image_metas,\n        zip(anno_strs, [img_prefix for _ in range(len(anno_strs))]),\n    )\n    pool.close()\n\n    # save image metas\n    id_hw = {}\n    for sub_dict in id_hw_temp:\n        id_hw.update(sub_dict)\n\n    data_root = osp.dirname(ann_file)\n    save_path = osp.join(data_root, f'id_hw_{dataset}.json')\n    print_log(\n        f'\\nsaving \"id_hw_{dataset}.json\" in \"{data_root}\"',\n        level=logging.INFO)\n    dump(id_hw, save_path, file_format='json')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/misc/get_image_metas.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\n\"\"\"Get image metas on a specific dataset.\n\nHere is an example to run this script.\n\nExample:\n    python tools/misc/get_image_metas.py ${CONFIG} \\\n    --out ${OUTPUT FILE NAME}\n\"\"\"\nimport argparse\nimport csv\nimport os.path as osp\nfrom multiprocessing import Pool\n\nimport mmcv\nfrom mmengine.config import Config\nfrom mmengine.fileio import dump, get\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Collect image metas')\n    parser.add_argument('config', help='Config file path')\n    parser.add_argument(\n        '--dataset',\n        default='val',\n        choices=['train', 'val', 'test'],\n        help='Collect image metas from which dataset')\n    parser.add_argument(\n        '--out',\n        default='validation-image-metas.pkl',\n        help='The output image metas file name. The save dir is in the '\n        'same directory as `dataset.ann_file` path')\n    parser.add_argument(\n        '--nproc',\n        default=4,\n        type=int,\n        help='Processes used for get image metas')\n    args = parser.parse_args()\n    return args\n\n\ndef get_metas_from_csv_style_ann_file(ann_file):\n    data_infos = []\n    cp_filename = None\n    with open(ann_file, 'r') as f:\n        reader = csv.reader(f)\n        for i, line in enumerate(reader):\n            if i == 0:\n                continue\n            img_id = line[0]\n            filename = f'{img_id}.jpg'\n            if filename != cp_filename:\n                data_infos.append(dict(filename=filename))\n                cp_filename = filename\n    return data_infos\n\n\ndef get_metas_from_txt_style_ann_file(ann_file):\n    with open(ann_file) as f:\n        lines = f.readlines()\n    i = 0\n    data_infos = []\n    while i < len(lines):\n        filename = lines[i].rstrip()\n        data_infos.append(dict(filename=filename))\n        skip_lines = int(lines[i + 2]) + 3\n        i += skip_lines\n    return data_infos\n\n\ndef get_image_metas(data_info, img_prefix):\n    filename = data_info.get('filename', None)\n    if filename is not None:\n        if img_prefix is not None:\n            filename = osp.join(img_prefix, filename)\n        img_bytes = get(filename)\n        img = mmcv.imfrombytes(img_bytes, flag='color')\n        shape = img.shape\n        meta = dict(filename=filename, ori_shape=shape)\n    else:\n        raise NotImplementedError('Missing `filename` in data_info')\n    return meta\n\n\ndef main():\n    args = parse_args()\n    assert args.out.endswith('pkl'), 'The output file name must be pkl suffix'\n\n    # load config files\n    cfg = Config.fromfile(args.config)\n    dataloader_cfg = cfg.get(f'{args.dataset}_dataloader')\n    ann_file = osp.join(dataloader_cfg.dataset.data_root,\n                        dataloader_cfg.dataset.ann_file)\n    img_prefix = osp.join(dataloader_cfg.dataset.data_root,\n                          dataloader_cfg.dataset.data_prefix['img'])\n\n    print(f'{\"-\" * 5} Start Processing {\"-\" * 5}')\n    if ann_file.endswith('csv'):\n        data_infos = get_metas_from_csv_style_ann_file(ann_file)\n    elif ann_file.endswith('txt'):\n        data_infos = get_metas_from_txt_style_ann_file(ann_file)\n    else:\n        shuffix = ann_file.split('.')[-1]\n        raise NotImplementedError('File name must be csv or txt suffix but '\n                                  f'get {shuffix}')\n\n    print(f'Successfully load annotation file from {ann_file}')\n    print(f'Processing {len(data_infos)} images...')\n    pool = Pool(args.nproc)\n    # get image metas with multiple processes\n    image_metas = pool.starmap(\n        get_image_metas,\n        zip(data_infos, [img_prefix for _ in range(len(data_infos))]),\n    )\n    pool.close()\n\n    # save image metas\n    root_path = dataloader_cfg.dataset.ann_file.rsplit('/', 1)[0]\n    save_path = osp.join(root_path, args.out)\n    dump(image_metas, save_path, protocol=4)\n    print(f'Image meta file save to: {save_path}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/misc/print_config.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\n\nfrom mmengine import Config, DictAction\n\nfrom mmdet.utils import replace_cfg_vals, update_data_root\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Print the whole config')\n    parser.add_argument('config', help='config file path')\n    parser.add_argument(\n        '--save-path',\n        default=None,\n        help='save path of whole config, suffixed with .py, .json or .yml')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    args = parser.parse_args()\n\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    cfg = Config.fromfile(args.config)\n\n    # replace the ${key} with the value of cfg.key\n    cfg = replace_cfg_vals(cfg)\n\n    # update data root according to MMDET_DATASETS\n    update_data_root(cfg)\n\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n    print(f'Config:\\n{cfg.pretty_text}')\n\n    if args.save_path is not None:\n        save_path = args.save_path\n\n        suffix = os.path.splitext(save_path)[-1]\n        assert suffix in ['.py', '.json', '.yml']\n\n        if not os.path.exists(os.path.split(save_path)[0]):\n            os.makedirs(os.path.split(save_path)[0])\n        cfg.dump(save_path)\n        print(f'Config saving at {save_path}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/misc/split_coco.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os.path as osp\n\nimport numpy as np\nfrom mmengine.fileio import dump, load\nfrom mmengine.utils import mkdir_or_exist, track_parallel_progress\n\nprog_description = '''K-Fold coco split.\n\nTo split coco data for semi-supervised object detection:\n    python tools/misc/split_coco.py\n'''\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        '--data-root',\n        type=str,\n        help='The data root of coco dataset.',\n        default='./data/coco/')\n    parser.add_argument(\n        '--out-dir',\n        type=str,\n        help='The output directory of coco semi-supervised annotations.',\n        default='./data/coco/semi_anns/')\n    parser.add_argument(\n        '--labeled-percent',\n        type=float,\n        nargs='+',\n        help='The percentage of labeled data in the training set.',\n        default=[1, 2, 5, 10])\n    parser.add_argument(\n        '--fold',\n        type=int,\n        help='K-fold cross validation for semi-supervised object detection.',\n        default=5)\n    args = parser.parse_args()\n    return args\n\n\ndef split_coco(data_root, out_dir, percent, fold):\n    \"\"\"Split COCO data for Semi-supervised object detection.\n\n    Args:\n        data_root (str): The data root of coco dataset.\n        out_dir (str): The output directory of coco semi-supervised\n            annotations.\n        percent (float): The percentage of labeled data in the training set.\n        fold (int): The fold of dataset and set as random seed for data split.\n    \"\"\"\n\n    def save_anns(name, images, annotations):\n        sub_anns = dict()\n        sub_anns['images'] = images\n        sub_anns['annotations'] = annotations\n        sub_anns['licenses'] = anns['licenses']\n        sub_anns['categories'] = anns['categories']\n        sub_anns['info'] = anns['info']\n\n        mkdir_or_exist(out_dir)\n        dump(sub_anns, f'{out_dir}/{name}.json')\n\n    # set random seed with the fold\n    np.random.seed(fold)\n    ann_file = osp.join(data_root, 'annotations/instances_train2017.json')\n    anns = load(ann_file)\n\n    image_list = anns['images']\n    labeled_total = int(percent / 100. * len(image_list))\n    labeled_inds = set(\n        np.random.choice(range(len(image_list)), size=labeled_total))\n    labeled_ids, labeled_images, unlabeled_images = [], [], []\n\n    for i in range(len(image_list)):\n        if i in labeled_inds:\n            labeled_images.append(image_list[i])\n            labeled_ids.append(image_list[i]['id'])\n        else:\n            unlabeled_images.append(image_list[i])\n\n    # get all annotations of labeled images\n    labeled_ids = set(labeled_ids)\n    labeled_annotations, unlabeled_annotations = [], []\n\n    for ann in anns['annotations']:\n        if ann['image_id'] in labeled_ids:\n            labeled_annotations.append(ann)\n        else:\n            unlabeled_annotations.append(ann)\n\n    # save labeled and unlabeled\n    labeled_name = f'instances_train2017.{fold}@{percent}'\n    unlabeled_name = f'instances_train2017.{fold}@{percent}-unlabeled'\n\n    save_anns(labeled_name, labeled_images, labeled_annotations)\n    save_anns(unlabeled_name, unlabeled_images, unlabeled_annotations)\n\n\ndef multi_wrapper(args):\n    return split_coco(*args)\n\n\nif __name__ == '__main__':\n    args = parse_args()\n    arguments_list = [(args.data_root, args.out_dir, p, f)\n                      for f in range(1, args.fold + 1)\n                      for p in args.labeled_percent]\n    track_parallel_progress(multi_wrapper, arguments_list, args.fold)\n"
  },
  {
    "path": "tools/misc/split_odvg.py",
    "content": "import argparse\nimport json\nimport os\nimport shutil\n\nimport jsonlines\nimport numpy as np\nfrom mmengine.utils import ProgressBar, mkdir_or_exist\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('data_root', type=str, help='The data root.')\n    parser.add_argument('ann_file', type=str)\n    parser.add_argument('img_prefix', type=str)\n    parser.add_argument(\n        'out_dir',\n        type=str,\n        help='The output directory of coco semi-supervised annotations.')\n    parser.add_argument(\n        '--label-map-file', '-m', type=str, help='label map file')\n    parser.add_argument(\n        '--num-img',\n        '-n',\n        default=200,\n        type=int,\n        help='num of extract image, -1 means all images')\n    parser.add_argument('--seed', default=-1, type=int, help='seed')\n    args = parser.parse_args()\n    return args\n\n\ndef main():\n    args = parse_args()\n    assert args.out_dir != args.data_root, \\\n        'The file will be overwritten in place, ' \\\n        'so the same folder is not allowed !'\n\n    seed = int(args.seed)\n    if seed != -1:\n        print(f'Set the global seed: {seed}')\n        np.random.seed(int(args.seed))\n\n    ann_file = os.path.join(args.data_root, args.ann_file)\n    with open(ann_file, 'r') as f:\n        data_list = [json.loads(line) for line in f]\n\n    np.random.shuffle(data_list)\n\n    num_img = args.num_img\n\n    progress_bar = ProgressBar(num_img)\n    for i in range(num_img):\n        file_name = data_list[i]['filename']\n        image_path = os.path.join(args.data_root, args.img_prefix, file_name)\n        out_image_dir = os.path.join(args.out_dir, args.img_prefix)\n        mkdir_or_exist(out_image_dir)\n        out_image_path = os.path.join(out_image_dir, file_name)\n        shutil.copyfile(image_path, out_image_path)\n\n        progress_bar.update()\n\n    out_path = os.path.join(args.out_dir, args.ann_file)\n    out_dir = os.path.dirname(out_path)\n    mkdir_or_exist(out_dir)\n\n    with jsonlines.open(out_path, mode='w') as writer:\n        writer.write_all(data_list[:num_img])\n\n    if args.label_map_file is not None:\n        out_dir = os.path.dirname(\n            os.path.join(args.out_dir, args.label_map_file))\n        mkdir_or_exist(out_dir)\n        shutil.copyfile(\n            os.path.join(args.data_root, args.label_map_file),\n            os.path.join(args.out_dir, args.label_map_file))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/detectron2_to_mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine.fileio import load\nfrom mmengine.runner import save_checkpoint\n\n\ndef convert(src: str, dst: str, prefix: str = 'd2_model') -> None:\n    \"\"\"Convert Detectron2 checkpoint to MMDetection style.\n\n    Args:\n        src (str): The Detectron2 checkpoint path, should endswith `pkl`.\n        dst (str): The MMDetection checkpoint path.\n        prefix (str): The prefix of MMDetection model, defaults to 'd2_model'.\n    \"\"\"\n    # load arch_settings\n    assert src.endswith('pkl'), \\\n        'the source Detectron2 checkpoint should endswith `pkl`.'\n    d2_model = load(src, encoding='latin1').get('model')\n    assert d2_model is not None\n\n    # convert to mmdet style\n    dst_state_dict = OrderedDict()\n    for name, value in d2_model.items():\n        if not isinstance(value, torch.Tensor):\n            value = torch.from_numpy(value)\n        dst_state_dict[f'{prefix}.{name}'] = value\n\n    mmdet_model = dict(state_dict=dst_state_dict, meta=dict())\n    save_checkpoint(mmdet_model, dst)\n    print(f'Convert Detectron2 model {src} to MMDetection model {dst}')\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='Convert Detectron2 checkpoint to MMDetection style')\n    parser.add_argument('src', help='Detectron2 model path')\n    parser.add_argument('dst', help='MMDetectron model save path')\n    parser.add_argument(\n        '--prefix', default='d2_model', type=str, help='prefix of the model')\n    args = parser.parse_args()\n    convert(args.src, args.dst, args.prefix)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/detectron2pytorch.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine.fileio import load\n\narch_settings = {50: (3, 4, 6, 3), 101: (3, 4, 23, 3)}\n\n\ndef convert_bn(blobs, state_dict, caffe_name, torch_name, converted_names):\n    # detectron replace bn with affine channel layer\n    state_dict[torch_name + '.bias'] = torch.from_numpy(blobs[caffe_name +\n                                                              '_b'])\n    state_dict[torch_name + '.weight'] = torch.from_numpy(blobs[caffe_name +\n                                                                '_s'])\n    bn_size = state_dict[torch_name + '.weight'].size()\n    state_dict[torch_name + '.running_mean'] = torch.zeros(bn_size)\n    state_dict[torch_name + '.running_var'] = torch.ones(bn_size)\n    converted_names.add(caffe_name + '_b')\n    converted_names.add(caffe_name + '_s')\n\n\ndef convert_conv_fc(blobs, state_dict, caffe_name, torch_name,\n                    converted_names):\n    state_dict[torch_name + '.weight'] = torch.from_numpy(blobs[caffe_name +\n                                                                '_w'])\n    converted_names.add(caffe_name + '_w')\n    if caffe_name + '_b' in blobs:\n        state_dict[torch_name + '.bias'] = torch.from_numpy(blobs[caffe_name +\n                                                                  '_b'])\n        converted_names.add(caffe_name + '_b')\n\n\ndef convert(src, dst, depth):\n    \"\"\"Convert keys in detectron pretrained ResNet models to pytorch style.\"\"\"\n    # load arch_settings\n    if depth not in arch_settings:\n        raise ValueError('Only support ResNet-50 and ResNet-101 currently')\n    block_nums = arch_settings[depth]\n    # load caffe model\n    caffe_model = load(src, encoding='latin1')\n    blobs = caffe_model['blobs'] if 'blobs' in caffe_model else caffe_model\n    # convert to pytorch style\n    state_dict = OrderedDict()\n    converted_names = set()\n    convert_conv_fc(blobs, state_dict, 'conv1', 'conv1', converted_names)\n    convert_bn(blobs, state_dict, 'res_conv1_bn', 'bn1', converted_names)\n    for i in range(1, len(block_nums) + 1):\n        for j in range(block_nums[i - 1]):\n            if j == 0:\n                convert_conv_fc(blobs, state_dict, f'res{i + 1}_{j}_branch1',\n                                f'layer{i}.{j}.downsample.0', converted_names)\n                convert_bn(blobs, state_dict, f'res{i + 1}_{j}_branch1_bn',\n                           f'layer{i}.{j}.downsample.1', converted_names)\n            for k, letter in enumerate(['a', 'b', 'c']):\n                convert_conv_fc(blobs, state_dict,\n                                f'res{i + 1}_{j}_branch2{letter}',\n                                f'layer{i}.{j}.conv{k+1}', converted_names)\n                convert_bn(blobs, state_dict,\n                           f'res{i + 1}_{j}_branch2{letter}_bn',\n                           f'layer{i}.{j}.bn{k + 1}', converted_names)\n    # check if all layers are converted\n    for key in blobs:\n        if key not in converted_names:\n            print(f'Not Convert: {key}')\n    # save checkpoint\n    checkpoint = dict()\n    checkpoint['state_dict'] = state_dict\n    torch.save(checkpoint, dst)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description='Convert model keys')\n    parser.add_argument('src', help='src detectron model path')\n    parser.add_argument('dst', help='save path')\n    parser.add_argument('depth', type=int, help='ResNet model depth')\n    args = parser.parse_args()\n    convert(args.src, args.dst, args.depth)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/detic_to_mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport subprocess\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine.runner import CheckpointLoader\n\nconvert_dict_fpn = {\n    'backbone.fpn_lateral3': 'neck.lateral_convs.0.conv',\n    'backbone.fpn_lateral4': 'neck.lateral_convs.1.conv',\n    'backbone.fpn_lateral5': 'neck.lateral_convs.2.conv',\n    'backbone.fpn_output3': 'neck.fpn_convs.0.conv',\n    'backbone.fpn_output4': 'neck.fpn_convs.1.conv',\n    'backbone.fpn_output5': 'neck.fpn_convs.2.conv',\n    'backbone.top_block.p6': 'neck.fpn_convs.3.conv',\n    'backbone.top_block.p7': 'neck.fpn_convs.4.conv',\n}\n\nconvert_dict_rpn = {\n    'proposal_generator.centernet_head.bbox_tower.0':\n    'rpn_head.reg_convs.0.conv',\n    'proposal_generator.centernet_head.bbox_tower.1':\n    'rpn_head.reg_convs.0.gn',\n    'proposal_generator.centernet_head.bbox_tower.3':\n    'rpn_head.reg_convs.1.conv',\n    'proposal_generator.centernet_head.bbox_tower.4':\n    'rpn_head.reg_convs.1.gn',\n    'proposal_generator.centernet_head.bbox_tower.6':\n    'rpn_head.reg_convs.2.conv',\n    'proposal_generator.centernet_head.bbox_tower.7':\n    'rpn_head.reg_convs.2.gn',\n    'proposal_generator.centernet_head.bbox_tower.9':\n    'rpn_head.reg_convs.3.conv',\n    'proposal_generator.centernet_head.bbox_tower.10':\n    'rpn_head.reg_convs.3.gn',\n    'proposal_generator.centernet_head.bbox_pred': 'rpn_head.conv_reg',\n    'proposal_generator.centernet_head.scales.0.scale':\n    'rpn_head.scales.0.scale',\n    'proposal_generator.centernet_head.scales.1.scale':\n    'rpn_head.scales.1.scale',\n    'proposal_generator.centernet_head.scales.2.scale':\n    'rpn_head.scales.2.scale',\n    'proposal_generator.centernet_head.scales.3.scale':\n    'rpn_head.scales.3.scale',\n    'proposal_generator.centernet_head.scales.4.scale':\n    'rpn_head.scales.4.scale',\n    'proposal_generator.centernet_head.agn_hm': 'rpn_head.conv_cls',\n}\n\nconvert_dict_roi = {\n    'roi_heads.box_head.0.fc1': 'roi_head.bbox_head.0.shared_fcs.0',\n    'roi_heads.box_head.0.fc2': 'roi_head.bbox_head.0.shared_fcs.1',\n    'roi_heads.box_head.1.fc1': 'roi_head.bbox_head.1.shared_fcs.0',\n    'roi_heads.box_head.1.fc2': 'roi_head.bbox_head.1.shared_fcs.1',\n    'roi_heads.box_head.2.fc1': 'roi_head.bbox_head.2.shared_fcs.0',\n    'roi_heads.box_head.2.fc2': 'roi_head.bbox_head.2.shared_fcs.1',\n    'roi_heads.box_predictor.0.freq_weight':\n    'roi_head.bbox_head.0.freq_weight',\n    'roi_heads.box_predictor.0.cls_score.zs_weight':\n    'roi_head.bbox_head.0.fc_cls.zs_weight',\n    'roi_heads.box_predictor.0.cls_score.linear':\n    'roi_head.bbox_head.0.fc_cls.linear',\n    'roi_heads.box_predictor.0.bbox_pred.0': 'roi_head.bbox_head.0.fc_reg.0',\n    'roi_heads.box_predictor.0.bbox_pred.2': 'roi_head.bbox_head.0.fc_reg.2',\n    'roi_heads.box_predictor.1.freq_weight':\n    'roi_head.bbox_head.1.freq_weight',\n    'roi_heads.box_predictor.1.cls_score.zs_weight':\n    'roi_head.bbox_head.1.fc_cls.zs_weight',\n    'roi_heads.box_predictor.1.cls_score.linear':\n    'roi_head.bbox_head.1.fc_cls.linear',\n    'roi_heads.box_predictor.1.bbox_pred.0': 'roi_head.bbox_head.1.fc_reg.0',\n    'roi_heads.box_predictor.1.bbox_pred.2': 'roi_head.bbox_head.1.fc_reg.2',\n    'roi_heads.box_predictor.2.freq_weight':\n    'roi_head.bbox_head.2.freq_weight',\n    'roi_heads.box_predictor.2.cls_score.zs_weight':\n    'roi_head.bbox_head.2.fc_cls.zs_weight',\n    'roi_heads.box_predictor.2.cls_score.linear':\n    'roi_head.bbox_head.2.fc_cls.linear',\n    'roi_heads.box_predictor.2.bbox_pred.0': 'roi_head.bbox_head.2.fc_reg.0',\n    'roi_heads.box_predictor.2.bbox_pred.2': 'roi_head.bbox_head.2.fc_reg.2',\n    'roi_heads.mask_head.mask_fcn1': 'roi_head.mask_head.convs.0.conv',\n    'roi_heads.mask_head.mask_fcn2': 'roi_head.mask_head.convs.1.conv',\n    'roi_heads.mask_head.mask_fcn3': 'roi_head.mask_head.convs.2.conv',\n    'roi_heads.mask_head.mask_fcn4': 'roi_head.mask_head.convs.3.conv',\n    'roi_heads.mask_head.deconv': 'roi_head.mask_head.upsample',\n    'roi_heads.mask_head.predictor': 'roi_head.mask_head.conv_logits',\n}\n\n\ndef correct_unfold_reduction_order(x):\n    out_channel, in_channel = x.shape\n    x = x.reshape(out_channel, 4, in_channel // 4)\n    x = x[:, [0, 2, 1, 3], :].transpose(1, 2).reshape(out_channel, in_channel)\n    return x\n\n\ndef correct_unfold_norm_order(x):\n    in_channel = x.shape[0]\n    x = x.reshape(4, in_channel // 4)\n    x = x[[0, 2, 1, 3], :].transpose(0, 1).reshape(in_channel)\n    return x\n\n\ndef convert(ckpt):\n    new_ckpt = OrderedDict()\n\n    for k, v in list(ckpt.items()):\n        new_v = v\n        if 'backbone.bottom_up' in k:\n            new_k = k.replace('backbone.bottom_up', 'backbone')\n            # for Transformer backbone\n            if 'patch_embed.proj' in new_k:\n                new_k = new_k.replace('patch_embed.proj',\n                                      'patch_embed.projection')\n            elif 'pos_drop' in new_k:\n                new_k = new_k.replace('pos_drop', 'drop_after_pos')\n\n            if 'layers' in new_k:\n                new_k = new_k.replace('layers', 'stages')\n                if 'mlp.fc1' in new_k:\n                    new_k = new_k.replace('mlp.fc1', 'ffn.layers.0.0')\n                elif 'mlp.fc2' in new_k:\n                    new_k = new_k.replace('mlp.fc2', 'ffn.layers.1')\n                elif 'attn' in new_k:\n                    new_k = new_k.replace('attn', 'attn.w_msa')\n\n                if 'downsample' in k:\n                    if 'reduction.' in k:\n                        new_v = correct_unfold_reduction_order(v)\n                    elif 'norm.' in k:\n                        new_v = correct_unfold_norm_order(v)\n            # for resnet\n            if 'base.' in k:\n                new_k = new_k.replace('base.', '')\n\n        elif 'backbone.fpn' in k or 'backbone.top_block' in k:\n            old_k = k.replace('.weight', '')\n            old_k = old_k.replace('.bias', '')\n            new_k = k.replace(old_k, convert_dict_fpn[old_k])\n        elif 'proposal_generator' in k:\n            old_k = k.replace('.weight', '')\n            old_k = old_k.replace('.bias', '')\n            new_k = k.replace(old_k, convert_dict_rpn[old_k])\n        elif 'roi_heads' in k:\n            old_k = k.replace('.weight', '')\n            old_k = old_k.replace('.bias', '')\n            new_k = k.replace(old_k, convert_dict_roi[old_k])\n        else:\n            print('skip:', k)\n            continue\n\n        new_ckpt[new_k] = new_v\n    return new_ckpt\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='Convert keys in pretrained eva '\n        'models to mmpretrain style.')\n    parser.add_argument(\n        '--src',\n        default='Detic_LbaseI_CLIP_SwinB_896b32_4x_ft4x_max-size.pth',\n        help='src model path or url')\n    # The dst path must be a full path of the new checkpoint.\n    parser.add_argument(\n        '--dst',\n        default='detic_centernet2_swin-b_fpn_4x_lvis-base_in21k-lvis.pth',\n        help='save path')\n    args = parser.parse_args()\n\n    checkpoint = CheckpointLoader.load_checkpoint(args.src, map_location='cpu')\n\n    if 'model' in checkpoint:\n        state_dict = checkpoint['model']\n    else:\n        state_dict = checkpoint\n\n    weight = {}\n    new_state_dict = convert(state_dict)\n    if 'backbone.fc.weight' in new_state_dict.keys():\n        del [new_state_dict['backbone.fc.weight']]\n    if 'backbone.fc.bias' in new_state_dict.keys():\n        del [new_state_dict['backbone.fc.bias']]\n    weight['state_dict'] = new_state_dict\n    torch.save(weight, args.dst)\n\n    sha = subprocess.check_output(['sha256sum', args.dst]).decode()\n    final_file = args.dst.replace('.pth', '') + '-{}.pth'.format(sha[:8])\n    subprocess.Popen(['mv', args.dst, final_file])\n    print(f'Done!!, save to {final_file}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/glip_to_mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport subprocess\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine.runner import CheckpointLoader\n\nconvert_dict_fpn = {\n    'module.backbone.fpn.fpn_inner2': 'neck.lateral_convs.0.conv',\n    'module.backbone.fpn.fpn_inner3': 'neck.lateral_convs.1.conv',\n    'module.backbone.fpn.fpn_inner4': 'neck.lateral_convs.2.conv',\n    'module.backbone.fpn.fpn_layer2': 'neck.fpn_convs.0.conv',\n    'module.backbone.fpn.fpn_layer3': 'neck.fpn_convs.1.conv',\n    'module.backbone.fpn.fpn_layer4': 'neck.fpn_convs.2.conv',\n    'module.backbone.fpn.top_blocks.p6': 'neck.fpn_convs.3.conv',\n    'module.backbone.fpn.top_blocks.p7': 'neck.fpn_convs.4.conv',\n}\n\n\ndef correct_unfold_reduction_order(x):\n    out_channel, in_channel = x.shape\n    x = x.reshape(out_channel, 4, in_channel // 4)\n    x = x[:, [0, 2, 1, 3], :].transpose(1, 2).reshape(out_channel, in_channel)\n    return x\n\n\ndef correct_unfold_norm_order(x):\n    in_channel = x.shape[0]\n    x = x.reshape(4, in_channel // 4)\n    x = x[[0, 2, 1, 3], :].transpose(0, 1).reshape(in_channel)\n    return x\n\n\ndef convert(ckpt):\n    new_ckpt = OrderedDict()\n\n    for k, v in list(ckpt.items()):\n        if 'anchor_generator' in k or 'resizer' in k or 'cls_logits' in k:\n            continue\n\n        new_v = v\n        if 'module.backbone.body' in k:\n            new_k = k.replace('module.backbone.body', 'backbone')\n            if 'patch_embed.proj' in new_k:\n                new_k = new_k.replace('patch_embed.proj',\n                                      'patch_embed.projection')\n            elif 'pos_drop' in new_k:\n                new_k = new_k.replace('pos_drop', 'drop_after_pos')\n\n            if 'layers' in new_k:\n                new_k = new_k.replace('layers', 'stages')\n                if 'mlp.fc1' in new_k:\n                    new_k = new_k.replace('mlp.fc1', 'ffn.layers.0.0')\n                elif 'mlp.fc2' in new_k:\n                    new_k = new_k.replace('mlp.fc2', 'ffn.layers.1')\n                elif 'attn' in new_k:\n                    new_k = new_k.replace('attn', 'attn.w_msa')\n\n                if 'downsample' in k:\n                    if 'reduction.' in k:\n                        new_v = correct_unfold_reduction_order(v)\n                    elif 'norm.' in k:\n                        new_v = correct_unfold_norm_order(v)\n\n        elif 'module.backbone.fpn' in k:\n            old_k = k.replace('.weight', '')\n            old_k = old_k.replace('.bias', '')\n            new_k = k.replace(old_k, convert_dict_fpn[old_k])\n        elif 'module.language_backbone' in k:\n            new_k = k.replace('module.language_backbone',\n                              'language_model.language_backbone')\n            if 'pooler' in k:\n                continue\n        elif 'module.rpn' in k:\n            if 'module.rpn.head.scales' in k:\n                new_k = k.replace('module.rpn.head.scales',\n                                  'bbox_head.head.scales')\n            else:\n                new_k = k.replace('module.rpn', 'bbox_head')\n\n            if 'anchor_generator' in k and 'resizer' in k:\n                continue\n        else:\n            print('skip:', k)\n            continue\n\n        if 'DyConv' in new_k:\n            new_k = new_k.replace('DyConv', 'dyconvs')\n\n        if 'AttnConv' in new_k:\n            new_k = new_k.replace('AttnConv', 'attnconv')\n\n        new_ckpt[new_k] = new_v\n    return new_ckpt\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='Convert keys to mmdet style.')\n    parser.add_argument(\n        'src', default='glip_a_tiny_o365.pth', help='src model path or url')\n    # The dst path must be a full path of the new checkpoint.\n    parser.add_argument(\n        '--dst', default='glip_tiny_a_mmdet.pth', help='save path')\n    args = parser.parse_args()\n\n    checkpoint = CheckpointLoader.load_checkpoint(args.src, map_location='cpu')\n\n    if 'model' in checkpoint:\n        state_dict = checkpoint['model']\n    else:\n        state_dict = checkpoint\n\n    weight = convert(state_dict)\n    torch.save(weight, args.dst)\n\n    sha = subprocess.check_output(['sha256sum', args.dst]).decode()\n    final_file = args.dst.replace('.pth', '') + '-{}.pth'.format(sha[:8])\n    subprocess.Popen(['mv', args.dst, final_file])\n    print(f'Done!!, save to {final_file}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/groundingdino_to_mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport subprocess\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine.runner import CheckpointLoader\n\n\ndef correct_unfold_reduction_order(x):\n    out_channel, in_channel = x.shape\n    x = x.reshape(out_channel, 4, in_channel // 4)\n    x = x[:, [0, 2, 1, 3], :].transpose(1, 2).reshape(out_channel, in_channel)\n    return x\n\n\ndef correct_unfold_norm_order(x):\n    in_channel = x.shape[0]\n    x = x.reshape(4, in_channel // 4)\n    x = x[[0, 2, 1, 3], :].transpose(0, 1).reshape(in_channel)\n    return x\n\n\ndef convert(ckpt):\n    new_ckpt = OrderedDict()\n\n    for k, v in list(ckpt.items()):\n        new_v = v\n        #\n        if 'module' not in k:\n            # NOTE: swin-b has no module prefix and swin-t has module prefix\n            k = 'module.' + k\n        if 'module.bbox_embed' in k:\n            # NOTE: bbox_embed name is swin-b is different from swin-t\n            k = k.replace('module.bbox_embed',\n                          'module.transformer.decoder.bbox_embed')\n\n        if 'module.backbone.0' in k:\n            new_k = k.replace('module.backbone.0', 'backbone')\n            if 'patch_embed.proj' in new_k:\n                new_k = new_k.replace('patch_embed.proj',\n                                      'patch_embed.projection')\n            elif 'pos_drop' in new_k:\n                new_k = new_k.replace('pos_drop', 'drop_after_pos')\n\n            if 'layers' in new_k:\n                new_k = new_k.replace('layers', 'stages')\n                if 'mlp.fc1' in new_k:\n                    new_k = new_k.replace('mlp.fc1', 'ffn.layers.0.0')\n                elif 'mlp.fc2' in new_k:\n                    new_k = new_k.replace('mlp.fc2', 'ffn.layers.1')\n                elif 'attn' in new_k:\n                    new_k = new_k.replace('attn', 'attn.w_msa')\n\n                if 'downsample' in k:\n                    if 'reduction.' in k:\n                        new_v = correct_unfold_reduction_order(v)\n                    elif 'norm.' in k:\n                        new_v = correct_unfold_norm_order(v)\n\n        elif 'module.bert' in k:\n            new_k = k.replace('module.bert',\n                              'language_model.language_backbone.body.model')\n            # new_k = k.replace('module.bert', 'bert')\n\n        elif 'module.feat_map' in k:\n            new_k = k.replace('module.feat_map', 'text_feat_map')\n\n        elif 'module.input_proj' in k:\n            new_k = k.replace('module.input_proj', 'neck.convs')\n            if 'neck.convs.3' in new_k:\n                # extra convs for 4th scale\n                new_k = new_k.replace('neck.convs.3', 'neck.extra_convs.0')\n            if '0.weight' in new_k:\n                # 0.weight -> conv.weight\n                new_k = new_k.replace('0.weight', 'conv.weight')\n            if '0.bias' in new_k:\n                # 0.bias -> conv.bias\n                new_k = new_k.replace('0.bias', 'conv.bias')\n            if '1.weight' in new_k:\n                # 1.weight -> gn.weight\n                new_k = new_k.replace('1.weight', 'gn.weight')\n            if '1.bias' in new_k:\n                # 1.bias -> gn.bias\n                new_k = new_k.replace('1.bias', 'gn.bias')\n\n        elif 'module.transformer.level_embed' in k:\n            # module.transformer.level_embed -> level_embed\n            new_k = k.replace('module.transformer.level_embed', 'level_embed')\n\n        elif 'module.transformer.encoder' in k:\n            # if '.layers' in k:\n            new_k = k.replace('module.transformer.encoder', 'encoder')\n            if 'norm1' in new_k:\n                new_k = new_k.replace('norm1', 'norms.0')\n            if 'norm2' in new_k:\n                new_k = new_k.replace('norm2', 'norms.1')\n            if 'norm3' in new_k:\n                new_k = new_k.replace('norm3', 'norms.2')\n            if 'linear1' in new_k:\n                new_k = new_k.replace('linear1', 'ffn.layers.0.0')\n            if 'linear2' in new_k:\n                new_k = new_k.replace('linear2', 'ffn.layers.1')\n\n            if 'text_layers' in new_k and 'self_attn' in new_k:\n                new_k = new_k.replace('self_attn', 'self_attn.attn')\n\n        elif 'module.transformer.enc_output' in k:\n            if 'module.transformer.enc_output' in k and 'norm' not in k:\n                new_k = k.replace('module.transformer.enc_output',\n                                  'memory_trans_fc')\n            if 'module.transformer.enc_output_norm' in k:\n                new_k = k.replace('module.transformer.enc_output_norm',\n                                  'memory_trans_norm')\n\n        elif 'module.transformer.enc_out_bbox_embed.layers' in k:\n            # ugly version\n            if 'module.transformer.enc_out_bbox_embed.layers.0' in k:\n                new_k = k.replace(\n                    'module.transformer.enc_out_bbox_embed.layers.0',\n                    'bbox_head.reg_branches.6.0')\n            if 'module.transformer.enc_out_bbox_embed.layers.1' in k:\n                new_k = k.replace(\n                    'module.transformer.enc_out_bbox_embed.layers.1',\n                    'bbox_head.reg_branches.6.2')\n            if 'module.transformer.enc_out_bbox_embed.layers.2' in k:\n                new_k = k.replace(\n                    'module.transformer.enc_out_bbox_embed.layers.2',\n                    'bbox_head.reg_branches.6.4')\n\n        elif 'module.transformer.tgt_embed' in k:\n            new_k = k.replace('module.transformer.tgt_embed',\n                              'query_embedding')\n\n        elif 'module.transformer.decoder' in k:\n            new_k = k.replace('module.transformer.decoder', 'decoder')\n            if 'norm1' in new_k:\n                # norm1 in official GroundingDINO is the third norm in decoder\n                new_k = new_k.replace('norm1', 'norms.2')\n            if 'catext_norm' in new_k:\n                # catext_norm in official GroundingDINO is the\n                # second norm in decoder\n                new_k = new_k.replace('catext_norm', 'norms.1')\n            if 'norm2' in new_k:\n                # norm2 in official GroundingDINO is the first norm in decoder\n                new_k = new_k.replace('norm2', 'norms.0')\n            if 'norm3' in new_k:\n                new_k = new_k.replace('norm3', 'norms.3')\n            if 'ca_text' in new_k:\n                new_k = new_k.replace('ca_text', 'cross_attn_text')\n                if 'in_proj_weight' in new_k:\n                    new_k = new_k.replace('in_proj_weight',\n                                          'attn.in_proj_weight')\n                if 'in_proj_bias' in new_k:\n                    new_k = new_k.replace('in_proj_bias', 'attn.in_proj_bias')\n                if 'out_proj.weight' in new_k:\n                    new_k = new_k.replace('out_proj.weight',\n                                          'attn.out_proj.weight')\n                if 'out_proj.bias' in new_k:\n                    new_k = new_k.replace('out_proj.bias',\n                                          'attn.out_proj.bias')\n            if 'linear1' in new_k:\n                new_k = new_k.replace('linear1', 'ffn.layers.0.0')\n            if 'linear2' in new_k:\n                new_k = new_k.replace('linear2', 'ffn.layers.1')\n            if 'self_attn' in new_k:\n                new_k = new_k.replace('self_attn', 'self_attn.attn')\n            if 'bbox_embed' in new_k:\n                reg_layer_id = int(new_k.split('.')[2])\n                linear_id = int(new_k.split('.')[4])\n                weight_or_bias = new_k.split('.')[-1]\n                new_k = 'bbox_head.reg_branches.' + \\\n                    str(reg_layer_id)+'.'+str(2*linear_id)+'.'+weight_or_bias\n\n        else:\n            print('skip:', k)\n            continue\n\n        new_ckpt[new_k] = new_v\n    return new_ckpt\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='Convert keys to mmdet style.')\n    parser.add_argument(\n        'src',\n        default='groundingdino_swint_ogc.pth.pth',\n        help='src model path or url')\n    # The dst path must be a full path of the new checkpoint.\n    parser.add_argument(\n        'dst',\n        default='groundingdino_swint_ogc.pth_mmdet.pth',\n        help='save path')\n    args = parser.parse_args()\n\n    checkpoint = CheckpointLoader.load_checkpoint(args.src, map_location='cpu')\n\n    if 'model' in checkpoint:\n        state_dict = checkpoint['model']\n    else:\n        state_dict = checkpoint\n\n    weight = convert(state_dict)\n    torch.save(weight, args.dst)\n    sha = subprocess.check_output(['sha256sum', args.dst]).decode()\n    final_file = args.dst.replace('.pth', '') + '-{}.pth'.format(sha[:8])\n    subprocess.Popen(['mv', args.dst, final_file])\n    print(f'Done!!, save to {final_file}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/publish_model.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport subprocess\n\nimport torch\nfrom mmengine.logging import print_log\nfrom mmengine.utils import digit_version\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='Process a checkpoint to be published')\n    parser.add_argument('in_file', help='input checkpoint filename')\n    parser.add_argument('out_file', help='output checkpoint filename')\n    parser.add_argument(\n        '--save-keys',\n        nargs='+',\n        type=str,\n        default=['meta', 'state_dict'],\n        help='keys to save in the published checkpoint')\n    args = parser.parse_args()\n    return args\n\n\ndef process_checkpoint(in_file, out_file, save_keys=['meta', 'state_dict']):\n    checkpoint = torch.load(in_file, map_location='cpu')\n\n    # only keep `meta` and `state_dict` for smaller file size\n    ckpt_keys = list(checkpoint.keys())\n    for k in ckpt_keys:\n        if k not in save_keys:\n            print_log(\n                f'Key `{k}` will be removed because it is not in '\n                f'save_keys. If you want to keep it, '\n                f'please set --save-keys.',\n                logger='current')\n            checkpoint.pop(k, None)\n\n    # if it is necessary to remove some sensitive data in checkpoint['meta'],\n    # add the code here.\n    if digit_version(torch.__version__) >= digit_version('1.6'):\n        torch.save(checkpoint, out_file, _use_new_zipfile_serialization=False)\n    else:\n        torch.save(checkpoint, out_file)\n    sha = subprocess.check_output(['sha256sum', out_file]).decode()\n    if out_file.endswith('.pth'):\n        out_file_name = out_file[:-4]\n    else:\n        out_file_name = out_file\n    final_file = out_file_name + f'-{sha[:8]}.pth'\n    subprocess.Popen(['mv', out_file, final_file])\n    print_log(\n        f'The published model is saved at {final_file}.', logger='current')\n\n\ndef main():\n    args = parse_args()\n    process_checkpoint(args.in_file, args.out_file, args.save_keys)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/regnet2mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nfrom collections import OrderedDict\n\nimport torch\n\n\ndef convert_stem(model_key, model_weight, state_dict, converted_names):\n    new_key = model_key.replace('stem.conv', 'conv1')\n    new_key = new_key.replace('stem.bn', 'bn1')\n    state_dict[new_key] = model_weight\n    converted_names.add(model_key)\n    print(f'Convert {model_key} to {new_key}')\n\n\ndef convert_head(model_key, model_weight, state_dict, converted_names):\n    new_key = model_key.replace('head.fc', 'fc')\n    state_dict[new_key] = model_weight\n    converted_names.add(model_key)\n    print(f'Convert {model_key} to {new_key}')\n\n\ndef convert_reslayer(model_key, model_weight, state_dict, converted_names):\n    split_keys = model_key.split('.')\n    layer, block, module = split_keys[:3]\n    block_id = int(block[1:])\n    layer_name = f'layer{int(layer[1:])}'\n    block_name = f'{block_id - 1}'\n\n    if block_id == 1 and module == 'bn':\n        new_key = f'{layer_name}.{block_name}.downsample.1.{split_keys[-1]}'\n    elif block_id == 1 and module == 'proj':\n        new_key = f'{layer_name}.{block_name}.downsample.0.{split_keys[-1]}'\n    elif module == 'f':\n        if split_keys[3] == 'a_bn':\n            module_name = 'bn1'\n        elif split_keys[3] == 'b_bn':\n            module_name = 'bn2'\n        elif split_keys[3] == 'c_bn':\n            module_name = 'bn3'\n        elif split_keys[3] == 'a':\n            module_name = 'conv1'\n        elif split_keys[3] == 'b':\n            module_name = 'conv2'\n        elif split_keys[3] == 'c':\n            module_name = 'conv3'\n        new_key = f'{layer_name}.{block_name}.{module_name}.{split_keys[-1]}'\n    else:\n        raise ValueError(f'Unsupported conversion of key {model_key}')\n    print(f'Convert {model_key} to {new_key}')\n    state_dict[new_key] = model_weight\n    converted_names.add(model_key)\n\n\ndef convert(src, dst):\n    \"\"\"Convert keys in pycls pretrained RegNet models to mmdet style.\"\"\"\n    # load caffe model\n    regnet_model = torch.load(src)\n    blobs = regnet_model['model_state']\n    # convert to pytorch style\n    state_dict = OrderedDict()\n    converted_names = set()\n    for key, weight in blobs.items():\n        if 'stem' in key:\n            convert_stem(key, weight, state_dict, converted_names)\n        elif 'head' in key:\n            convert_head(key, weight, state_dict, converted_names)\n        elif key.startswith('s'):\n            convert_reslayer(key, weight, state_dict, converted_names)\n\n    # check if all layers are converted\n    for key in blobs:\n        if key not in converted_names:\n            print(f'not converted: {key}')\n    # save checkpoint\n    checkpoint = dict()\n    checkpoint['state_dict'] = state_dict\n    torch.save(checkpoint, dst)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description='Convert model keys')\n    parser.add_argument('src', help='src detectron model path')\n    parser.add_argument('dst', help='save path')\n    args = parser.parse_args()\n    convert(args.src, args.dst)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/selfsup2mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nfrom collections import OrderedDict\n\nimport torch\n\n\ndef moco_convert(src, dst):\n    \"\"\"Convert keys in pycls pretrained moco models to mmdet style.\"\"\"\n    # load caffe model\n    moco_model = torch.load(src)\n    blobs = moco_model['state_dict']\n    # convert to pytorch style\n    state_dict = OrderedDict()\n    for k, v in blobs.items():\n        if not k.startswith('module.encoder_q.'):\n            continue\n        old_k = k\n        k = k.replace('module.encoder_q.', '')\n        state_dict[k] = v\n        print(old_k, '->', k)\n    # save checkpoint\n    checkpoint = dict()\n    checkpoint['state_dict'] = state_dict\n    torch.save(checkpoint, dst)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description='Convert model keys')\n    parser.add_argument('src', help='src detectron model path')\n    parser.add_argument('dst', help='save path')\n    parser.add_argument(\n        '--selfsup', type=str, choices=['moco', 'swav'], help='save path')\n    args = parser.parse_args()\n    if args.selfsup == 'moco':\n        moco_convert(args.src, args.dst)\n    elif args.selfsup == 'swav':\n        print('SWAV does not need to convert the keys')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/swinv1_to_mmdet.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport subprocess\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine.runner import CheckpointLoader\n\n\ndef swin_converter(ckpt):\n\n    new_ckpt = OrderedDict()\n\n    def correct_unfold_reduction_order(x):\n        out_channel, in_channel = x.shape\n        x = x.reshape(out_channel, 4, in_channel // 4)\n        x = x[:, [0, 2, 1, 3], :].transpose(1,\n                                            2).reshape(out_channel, in_channel)\n        return x\n\n    def correct_unfold_norm_order(x):\n        in_channel = x.shape[0]\n        x = x.reshape(4, in_channel // 4)\n        x = x[[0, 2, 1, 3], :].transpose(0, 1).reshape(in_channel)\n        return x\n\n    for k, v in ckpt.items():\n        if k.startswith('backbone.layers'):\n            new_v = v\n            if 'attn.' in k:\n                new_k = k.replace('attn.', 'attn.w_msa.')\n            elif 'mlp.' in k:\n                if 'mlp.fc1.' in k:\n                    new_k = k.replace('mlp.fc1.', 'ffn.layers.0.0.')\n                elif 'mlp.fc2.' in k:\n                    new_k = k.replace('mlp.fc2.', 'ffn.layers.1.')\n                else:\n                    new_k = k.replace('mlp.', 'ffn.')\n            elif 'downsample' in k:\n                new_k = k\n                if 'reduction.' in k:\n                    new_v = correct_unfold_reduction_order(v)\n                elif 'norm.' in k:\n                    new_v = correct_unfold_norm_order(v)\n            else:\n                new_k = k\n            new_k = new_k.replace('layers', 'stages', 1)\n        elif k.startswith('backbone.patch_embed'):\n            new_v = v\n            if 'proj' in k:\n                new_k = k.replace('proj', 'projection')\n            else:\n                new_k = k\n        else:\n            new_v = v\n            new_k = k\n\n        new_ckpt[new_k] = new_v\n\n    return new_ckpt\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='Convert keys to mmdet style.')\n    parser.add_argument('src', help='src model path or url')\n    # The dst path must be a full path of the new checkpoint.\n    parser.add_argument('dst', help='save path')\n    args = parser.parse_args()\n\n    checkpoint = CheckpointLoader.load_checkpoint(args.src, map_location='cpu')\n\n    if 'state_dict' in checkpoint:\n        state_dict = checkpoint['state_dict']\n    else:\n        state_dict = checkpoint\n    torch.save(swin_converter(state_dict), args.dst)\n\n    sha = subprocess.check_output(['sha256sum', args.dst]).decode()\n    final_file = args.dst.replace('.pth', '') + '-{}.pth'.format(sha[:8])\n    subprocess.Popen(['mv', args.dst, final_file])\n    print(f'Done!!, save to {final_file}')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/upgrade_model_version.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport re\nimport tempfile\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine import Config\n\n\ndef is_head(key):\n    valid_head_list = [\n        'bbox_head', 'mask_head', 'semantic_head', 'grid_head', 'mask_iou_head'\n    ]\n\n    return any(key.startswith(h) for h in valid_head_list)\n\n\ndef parse_config(config_strings):\n    temp_file = tempfile.NamedTemporaryFile()\n    config_path = f'{temp_file.name}.py'\n    with open(config_path, 'w') as f:\n        f.write(config_strings)\n\n    config = Config.fromfile(config_path)\n    is_two_stage = True\n    is_ssd = False\n    is_retina = False\n    reg_cls_agnostic = False\n    if 'rpn_head' not in config.model:\n        is_two_stage = False\n        # check whether it is SSD\n        if config.model.bbox_head.type == 'SSDHead':\n            is_ssd = True\n        elif config.model.bbox_head.type == 'RetinaHead':\n            is_retina = True\n    elif isinstance(config.model['bbox_head'], list):\n        reg_cls_agnostic = True\n    elif 'reg_class_agnostic' in config.model.bbox_head:\n        reg_cls_agnostic = config.model.bbox_head \\\n            .reg_class_agnostic\n    temp_file.close()\n    return is_two_stage, is_ssd, is_retina, reg_cls_agnostic\n\n\ndef reorder_cls_channel(val, num_classes=81):\n    # bias\n    if val.dim() == 1:\n        new_val = torch.cat((val[1:], val[:1]), dim=0)\n    # weight\n    else:\n        out_channels, in_channels = val.shape[:2]\n        # conv_cls for softmax output\n        if out_channels != num_classes and out_channels % num_classes == 0:\n            new_val = val.reshape(-1, num_classes, in_channels, *val.shape[2:])\n            new_val = torch.cat((new_val[:, 1:], new_val[:, :1]), dim=1)\n            new_val = new_val.reshape(val.size())\n        # fc_cls\n        elif out_channels == num_classes:\n            new_val = torch.cat((val[1:], val[:1]), dim=0)\n        # agnostic | retina_cls | rpn_cls\n        else:\n            new_val = val\n\n    return new_val\n\n\ndef truncate_cls_channel(val, num_classes=81):\n\n    # bias\n    if val.dim() == 1:\n        if val.size(0) % num_classes == 0:\n            new_val = val[:num_classes - 1]\n        else:\n            new_val = val\n    # weight\n    else:\n        out_channels, in_channels = val.shape[:2]\n        # conv_logits\n        if out_channels % num_classes == 0:\n            new_val = val.reshape(num_classes, in_channels, *val.shape[2:])[1:]\n            new_val = new_val.reshape(-1, *val.shape[1:])\n        # agnostic\n        else:\n            new_val = val\n\n    return new_val\n\n\ndef truncate_reg_channel(val, num_classes=81):\n    # bias\n    if val.dim() == 1:\n        # fc_reg | rpn_reg\n        if val.size(0) % num_classes == 0:\n            new_val = val.reshape(num_classes, -1)[:num_classes - 1]\n            new_val = new_val.reshape(-1)\n        # agnostic\n        else:\n            new_val = val\n    # weight\n    else:\n        out_channels, in_channels = val.shape[:2]\n        # fc_reg | rpn_reg\n        if out_channels % num_classes == 0:\n            new_val = val.reshape(num_classes, -1, in_channels,\n                                  *val.shape[2:])[1:]\n            new_val = new_val.reshape(-1, *val.shape[1:])\n        # agnostic\n        else:\n            new_val = val\n\n    return new_val\n\n\ndef convert(in_file, out_file, num_classes):\n    \"\"\"Convert keys in checkpoints.\n\n    There can be some breaking changes during the development of mmdetection,\n    and this tool is used for upgrading checkpoints trained with old versions\n    to the latest one.\n    \"\"\"\n    checkpoint = torch.load(in_file)\n    in_state_dict = checkpoint.pop('state_dict')\n    out_state_dict = OrderedDict()\n    meta_info = checkpoint['meta']\n    is_two_stage, is_ssd, is_retina, reg_cls_agnostic = parse_config(\n        '#' + meta_info['config'])\n    if meta_info['mmdet_version'] <= '0.5.3' and is_retina:\n        upgrade_retina = True\n    else:\n        upgrade_retina = False\n\n    # MMDetection v2.5.0 unifies the class order in RPN\n    # if the model is trained in version<v2.5.0\n    # The RPN model should be upgraded to be used in version>=2.5.0\n    if meta_info['mmdet_version'] < '2.5.0':\n        upgrade_rpn = True\n    else:\n        upgrade_rpn = False\n\n    for key, val in in_state_dict.items():\n        new_key = key\n        new_val = val\n        if is_two_stage and is_head(key):\n            new_key = 'roi_head.{}'.format(key)\n\n        # classification\n        if upgrade_rpn:\n            m = re.search(\n                r'(conv_cls|retina_cls|rpn_cls|fc_cls|fcos_cls|'\n                r'fovea_cls).(weight|bias)', new_key)\n        else:\n            m = re.search(\n                r'(conv_cls|retina_cls|fc_cls|fcos_cls|'\n                r'fovea_cls).(weight|bias)', new_key)\n        if m is not None:\n            print(f'reorder cls channels of {new_key}')\n            new_val = reorder_cls_channel(val, num_classes)\n\n        # regression\n        if upgrade_rpn:\n            m = re.search(r'(fc_reg).(weight|bias)', new_key)\n        else:\n            m = re.search(r'(fc_reg|rpn_reg).(weight|bias)', new_key)\n        if m is not None and not reg_cls_agnostic:\n            print(f'truncate regression channels of {new_key}')\n            new_val = truncate_reg_channel(val, num_classes)\n\n        # mask head\n        m = re.search(r'(conv_logits).(weight|bias)', new_key)\n        if m is not None:\n            print(f'truncate mask prediction channels of {new_key}')\n            new_val = truncate_cls_channel(val, num_classes)\n\n        m = re.search(r'(cls_convs|reg_convs).\\d.(weight|bias)', key)\n        # Legacy issues in RetinaNet since V1.x\n        # Use ConvModule instead of nn.Conv2d in RetinaNet\n        # cls_convs.0.weight -> cls_convs.0.conv.weight\n        if m is not None and upgrade_retina:\n            param = m.groups()[1]\n            new_key = key.replace(param, f'conv.{param}')\n            out_state_dict[new_key] = val\n            print(f'rename the name of {key} to {new_key}')\n            continue\n\n        m = re.search(r'(cls_convs).\\d.(weight|bias)', key)\n        if m is not None and is_ssd:\n            print(f'reorder cls channels of {new_key}')\n            new_val = reorder_cls_channel(val, num_classes)\n\n        out_state_dict[new_key] = new_val\n    checkpoint['state_dict'] = out_state_dict\n    torch.save(checkpoint, out_file)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description='Upgrade model version')\n    parser.add_argument('in_file', help='input checkpoint file')\n    parser.add_argument('out_file', help='output checkpoint file')\n    parser.add_argument(\n        '--num-classes',\n        type=int,\n        default=81,\n        help='number of classes of the original model')\n    args = parser.parse_args()\n    convert(args.in_file, args.out_file, args.num_classes)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/model_converters/upgrade_ssd_version.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport tempfile\nfrom collections import OrderedDict\n\nimport torch\nfrom mmengine import Config\nfrom mmengine.utils import digit_version\n\n\ndef parse_config(config_strings):\n    temp_file = tempfile.NamedTemporaryFile()\n    config_path = f'{temp_file.name}.py'\n    with open(config_path, 'w') as f:\n        f.write(config_strings)\n\n    config = Config.fromfile(config_path)\n    # check whether it is SSD\n    if config.model.bbox_head.type != 'SSDHead':\n        raise AssertionError('This is not a SSD model.')\n\n\ndef convert(in_file, out_file):\n    checkpoint = torch.load(in_file)\n    in_state_dict = checkpoint.pop('state_dict')\n    out_state_dict = OrderedDict()\n    meta_info = checkpoint['meta']\n    parse_config('#' + meta_info['config'])\n    for key, value in in_state_dict.items():\n        if 'extra' in key:\n            layer_idx = int(key.split('.')[2])\n            new_key = 'neck.extra_layers.{}.{}.conv.'.format(\n                layer_idx // 2, layer_idx % 2) + key.split('.')[-1]\n        elif 'l2_norm' in key:\n            new_key = 'neck.l2_norm.weight'\n        elif 'bbox_head' in key:\n            new_key = key[:21] + '.0' + key[21:]\n        else:\n            new_key = key\n        out_state_dict[new_key] = value\n    checkpoint['state_dict'] = out_state_dict\n\n    if digit_version(torch.__version__) >= digit_version('1.6'):\n        torch.save(checkpoint, out_file, _use_new_zipfile_serialization=False)\n    else:\n        torch.save(checkpoint, out_file)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description='Upgrade SSD version')\n    parser.add_argument('in_file', help='input checkpoint file')\n    parser.add_argument('out_file', help='output checkpoint file')\n\n    args = parser.parse_args()\n    convert(args.in_file, args.out_file)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/slurm_test.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\n\nPARTITION=$1\nJOB_NAME=$2\nCONFIG=$3\nCHECKPOINT=$4\nGPUS=${GPUS:-8}\nGPUS_PER_NODE=${GPUS_PER_NODE:-8}\nCPUS_PER_TASK=${CPUS_PER_TASK:-5}\nPY_ARGS=${@:5}\nSRUN_ARGS=${SRUN_ARGS:-\"\"}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\nsrun -p ${PARTITION} \\\n    --job-name=${JOB_NAME} \\\n    --gres=gpu:${GPUS_PER_NODE} \\\n    --ntasks=${GPUS} \\\n    --ntasks-per-node=${GPUS_PER_NODE} \\\n    --cpus-per-task=${CPUS_PER_TASK} \\\n    --kill-on-bad-exit=1 \\\n    ${SRUN_ARGS} \\\n    python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher=\"slurm\" ${PY_ARGS}\n"
  },
  {
    "path": "tools/slurm_test_tracking.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\n\nPARTITION=$1\nJOB_NAME=$2\nCONFIG=$3\nGPUS=${GPUS:-8}\nGPUS_PER_NODE=${GPUS_PER_NODE:-8}\nCPUS_PER_TASK=${CPUS_PER_TASK:-5}\nPY_ARGS=${@:4}\nSRUN_ARGS=${SRUN_ARGS:-\"\"}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\nsrun -p ${PARTITION} \\\n    --job-name=${JOB_NAME} \\\n    --gres=gpu:${GPUS_PER_NODE} \\\n    --ntasks=${GPUS} \\\n    --ntasks-per-node=${GPUS_PER_NODE} \\\n    --cpus-per-task=${CPUS_PER_TASK} \\\n    --kill-on-bad-exit=1 \\\n    ${SRUN_ARGS} \\\n    python -u tools/test_tracking.py ${CONFIG} --launcher=\"slurm\" ${PY_ARGS}\n"
  },
  {
    "path": "tools/slurm_train.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\n\nPARTITION=$1\nJOB_NAME=$2\nCONFIG=$3\nWORK_DIR=$4\nGPUS=${GPUS:-8}\nGPUS_PER_NODE=${GPUS_PER_NODE:-8}\nCPUS_PER_TASK=${CPUS_PER_TASK:-5}\nSRUN_ARGS=${SRUN_ARGS:-\"\"}\nPY_ARGS=${@:5}\n\nPYTHONPATH=\"$(dirname $0)/..\":$PYTHONPATH \\\nsrun -p ${PARTITION} \\\n    --job-name=${JOB_NAME} \\\n    --gres=gpu:${GPUS_PER_NODE} \\\n    --ntasks=${GPUS} \\\n    --ntasks-per-node=${GPUS_PER_NODE} \\\n    --cpus-per-task=${CPUS_PER_TASK} \\\n    --kill-on-bad-exit=1 \\\n    ${SRUN_ARGS} \\\n    python -u tools/train.py ${CONFIG} --work-dir=${WORK_DIR} --launcher=\"slurm\" ${PY_ARGS}\n"
  },
  {
    "path": "tools/test.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\nimport warnings\nfrom copy import deepcopy\n\nfrom mmengine import ConfigDict\nfrom mmengine.config import Config, DictAction\nfrom mmengine.runner import Runner\n\nfrom mmdet.engine.hooks.utils import trigger_visualization_hook\nfrom mmdet.evaluation import DumpDetResults\nfrom mmdet.registry import RUNNERS\nfrom mmdet.utils import setup_cache_size_limit_of_dynamo\n\n\n# TODO: support fuse_conv_bn and format_only\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMDet test (and eval) a model')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('checkpoint', help='checkpoint file')\n    parser.add_argument(\n        '--work-dir',\n        help='the directory to save the file containing evaluation metrics')\n    parser.add_argument(\n        '--out',\n        type=str,\n        help='dump predictions to a pickle file for offline evaluation')\n    parser.add_argument(\n        '--show', action='store_true', help='show prediction results')\n    parser.add_argument(\n        '--show-dir',\n        help='directory where painted images will be saved. '\n        'If specified, it will be automatically saved '\n        'to the work_dir/timestamp/show_dir')\n    parser.add_argument(\n        '--wait-time', type=float, default=2, help='the interval of show (s)')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--tta', action='store_true')\n    # When using PyTorch version >= 2.0.0, the `torch.distributed.launch`\n    # will pass the `--local-rank` parameter to `tools/train.py` instead\n    # of `--local_rank`.\n    parser.add_argument('--local_rank', '--local-rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    # Reduce the number of repeated compilations and improve\n    # testing speed.\n    setup_cache_size_limit_of_dynamo()\n\n    # load config\n    cfg = Config.fromfile(args.config)\n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = args.work_dir\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(args.config))[0])\n\n    cfg.load_from = args.checkpoint\n\n    if args.show or args.show_dir:\n        cfg = trigger_visualization_hook(cfg, args)\n\n    if args.tta:\n\n        if 'tta_model' not in cfg:\n            warnings.warn('Cannot find ``tta_model`` in config, '\n                          'we will set it as default.')\n            cfg.tta_model = dict(\n                type='DetTTAModel',\n                tta_cfg=dict(\n                    nms=dict(type='nms', iou_threshold=0.5), max_per_img=100))\n        if 'tta_pipeline' not in cfg:\n            warnings.warn('Cannot find ``tta_pipeline`` in config, '\n                          'we will set it as default.')\n            test_data_cfg = cfg.test_dataloader.dataset\n            while 'dataset' in test_data_cfg:\n                test_data_cfg = test_data_cfg['dataset']\n            cfg.tta_pipeline = deepcopy(test_data_cfg.pipeline)\n            flip_tta = dict(\n                type='TestTimeAug',\n                transforms=[\n                    [\n                        dict(type='RandomFlip', prob=1.),\n                        dict(type='RandomFlip', prob=0.)\n                    ],\n                    [\n                        dict(\n                            type='PackDetInputs',\n                            meta_keys=('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'flip',\n                                       'flip_direction'))\n                    ],\n                ])\n            cfg.tta_pipeline[-1] = flip_tta\n        cfg.model = ConfigDict(**cfg.tta_model, module=cfg.model)\n        cfg.test_dataloader.dataset.pipeline = cfg.tta_pipeline\n\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    # add `DumpResults` dummy metric\n    if args.out is not None:\n        assert args.out.endswith(('.pkl', '.pickle')), \\\n            'The dump file must be a pkl file.'\n        runner.test_evaluator.metrics.append(\n            DumpDetResults(out_file_path=args.out))\n\n    # start testing\n    runner.test()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/test_tracking.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.model import is_model_wrapper\nfrom mmengine.registry import RUNNERS\nfrom mmengine.runner import Runner\nfrom mmengine.runner.checkpoint import load_checkpoint\n\nfrom mmdet.utils import register_all_modules\n\n\n# TODO: support fuse_conv_bn, visualization, and format_only\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMTrack test (and eval) a model')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('--checkpoint', help='checkpoint file')\n    parser.add_argument('--detector', help='detection checkpoint file')\n    parser.add_argument('--reid', help='reid checkpoint file')\n    parser.add_argument(\n        '--work-dir',\n        help='the directory to save the file containing evaluation metrics')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--local-rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    # register all modules in mmtrack into the registries\n    # do not init the default scope here because it will be init in the runner\n    register_all_modules(init_default_scope=False)\n\n    # load config\n    cfg = Config.fromfile(args.config)\n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = args.work_dir\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(args.config))[0])\n\n    cfg.load_from = args.checkpoint\n\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    if is_model_wrapper(runner.model):\n        model = runner.model.module\n    else:\n        model = runner.model\n\n    if args.detector:\n        assert not (args.checkpoint and args.detector), \\\n            'Error: checkpoint and detector checkpoint cannot both exist'\n        load_checkpoint(model.detector, args.detector)\n\n    if args.reid:\n        assert not (args.checkpoint and args.reid), \\\n             'Error: checkpoint and reid checkpoint cannot both exist'\n        load_checkpoint(model.reid, args.reid)\n\n    # start testing\n    runner.test()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/train.py",
    "content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\n\nfrom mmengine.config import Config, DictAction\nfrom mmengine.registry import RUNNERS\nfrom mmengine.runner import Runner\n\nfrom mmdet.utils import setup_cache_size_limit_of_dynamo\n\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Train a detector')\n    parser.add_argument('config', help='train config file path')\n    parser.add_argument('--work-dir', help='the dir to save logs and models')\n    parser.add_argument(\n        '--amp',\n        action='store_true',\n        default=False,\n        help='enable automatic-mixed-precision training')\n    parser.add_argument(\n        '--auto-scale-lr',\n        action='store_true',\n        help='enable automatically scaling LR.')\n    parser.add_argument(\n        '--resume',\n        nargs='?',\n        type=str,\n        const='auto',\n        help='If specify checkpoint path, resume from it, while if not '\n        'specify, try to auto resume from the latest checkpoint '\n        'in the work directory.')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    # When using PyTorch version >= 2.0.0, the `torch.distributed.launch`\n    # will pass the `--local-rank` parameter to `tools/train.py` instead\n    # of `--local_rank`.\n    parser.add_argument('--local_rank', '--local-rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    # Reduce the number of repeated compilations and improve\n    # training speed.\n    setup_cache_size_limit_of_dynamo()\n\n    # load config\n    cfg = Config.fromfile(args.config)\n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = args.work_dir\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(args.config))[0])\n\n    # enable automatic-mixed-precision training\n    if args.amp is True:\n        cfg.optim_wrapper.type = 'AmpOptimWrapper'\n        cfg.optim_wrapper.loss_scale = 'dynamic'\n\n    # enable automatically scaling LR\n    if args.auto_scale_lr:\n        if 'auto_scale_lr' in cfg and \\\n                'enable' in cfg.auto_scale_lr and \\\n                'base_batch_size' in cfg.auto_scale_lr:\n            cfg.auto_scale_lr.enable = True\n        else:\n            raise RuntimeError('Can not find \"auto_scale_lr\" or '\n                               '\"auto_scale_lr.enable\" or '\n                               '\"auto_scale_lr.base_batch_size\" in your'\n                               ' configuration file.')\n\n    # resume is determined in this priority: resume from > auto_resume\n    if args.resume == 'auto':\n        cfg.resume = True\n        cfg.load_from = None\n    elif args.resume is not None:\n        cfg.resume = True\n        cfg.load_from = args.resume\n\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    # start training\n    runner.train()\n\n\nif __name__ == '__main__':\n    main()\n"
  }
]